2021-11-22 02:27:23 +00:00
/* RetroArch - A frontend for libretro.
* Copyright ( C ) 2010 - 2014 - Hans - Kristian Arntzen
* Copyright ( C ) 2011 - 2021 - Daniel De Matteis
* Copyright ( C ) 2012 - 2015 - Michael Lelli
* Copyright ( C ) 2014 - 2017 - Jean - Andr <EFBFBD> Santoni
* Copyright ( C ) 2016 - 2019 - Brad Parker
* Copyright ( C ) 2016 - 2019 - Andr <EFBFBD> s Su <EFBFBD> rez ( input mapper code )
* Copyright ( C ) 2016 - 2017 - Gregor Richards ( network code )
*
* RetroArch is free software : you can redistribute it and / or modify it under the terms
* of the GNU General Public License as published by the Free Software Found -
* ation , either version 3 of the License , or ( at your option ) any later version .
*
* RetroArch is distributed in the hope that it will be useful , but WITHOUT ANY WARRANTY ;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE . See the GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License along with RetroArch .
* If not , see < http : //www.gnu.org/licenses/>.
*/
# ifdef _WIN32
# ifdef _XBOX
# include <xtl.h>
# else
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
# endif
# if defined(DEBUG) && defined(HAVE_DRMINGW)
# include "exchndl.h"
# endif
# endif
# if defined(DINGUX)
# include <sys/types.h>
# include <unistd.h>
# endif
# if (defined(__linux__) || defined(__unix__) || defined(DINGUX)) && !defined(EMSCRIPTEN)
# include <signal.h>
# endif
# if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX)
# ifndef LEGACY_WIN32
# define LEGACY_WIN32
# endif
# endif
# if defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)
# include <objbase.h>
# include <process.h>
# endif
# include <stdio.h>
# include <stdlib.h>
# include <stdarg.h>
# include <stdint.h>
# include <string.h>
# include <ctype.h>
# include <errno.h>
# include <math.h>
# include <locale.h>
# include <boolean.h>
# include <clamping.h>
# include <string/stdstring.h>
# include <dynamic/dylib.h>
# include <file/config_file.h>
# include <lists/string_list.h>
# include <memalign.h>
# include <retro_math.h>
# include <retro_timers.h>
# include <encodings/utf.h>
# include <time/rtime.h>
# include <libretro.h>
# define VFS_FRONTEND
# include <vfs/vfs_implementation.h>
# include <features/features_cpu.h>
# include <compat/strl.h>
# include <compat/strcasestr.h>
# include <compat/getopt.h>
# include <compat/posix_string.h>
# include <streams/file_stream.h>
# include <file/file_path.h>
# include <retro_assert.h>
# include <retro_miscellaneous.h>
# include <queues/message_queue.h>
# include <lists/dir_list.h>
# ifdef EMSCRIPTEN
# include <emscripten/emscripten.h>
# endif
# ifdef HAVE_LIBNX
# include <switch.h>
# endif
# if defined(HAVE_LAKKA) || defined(HAVE_LIBNX)
# include "switch_performance_profiles.h"
# endif
# if defined(ANDROID)
# include "play_feature_delivery/play_feature_delivery.h"
# endif
# ifdef HAVE_DISCORD
# include "network/discord.h"
# endif
# include "config.def.h"
# include "runtime_file.h"
# include "runloop.h"
# include "camera/camera_driver.h"
# include "location_driver.h"
# include "record/record_driver.h"
# ifdef HAVE_CONFIG_H
# include "config.h"
# endif
# ifdef HAVE_NETWORKING
# include <net/net_compat.h>
# include <net/net_socket.h>
# endif
# include <audio/audio_resampler.h>
# include "audio/audio_driver.h"
# include "gfx/gfx_animation.h"
# include "gfx/gfx_display.h"
# include "gfx/gfx_thumbnail.h"
# include "gfx/video_filter.h"
# include "input/input_osk.h"
# ifdef HAVE_MENU
# include "menu/menu_cbs.h"
# include "menu/menu_driver.h"
# include "menu/menu_input.h"
# include "menu/menu_dialog.h"
# include "menu/menu_input_bind_dialog.h"
# endif
# if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
# include "menu/menu_shader.h"
# endif
# ifdef HAVE_GFX_WIDGETS
# include "gfx/gfx_widgets.h"
# endif
# include "input/input_keymaps.h"
# include "input/input_remapping.h"
# ifdef HAVE_CHEEVOS
# include "cheevos/cheevos.h"
# include "cheevos/cheevos_menu.h"
# endif
# ifdef HAVE_NETWORKING
# include "network/netplay/netplay.h"
# include "network/netplay/netplay_private.h"
# ifdef HAVE_WIFI
# include "network/wifi_driver.h"
# endif
# endif
# ifdef HAVE_THREADS
# include <rthreads/rthreads.h>
# endif
# include "autosave.h"
# include "command.h"
# include "config.features.h"
# include "cores/internal_cores.h"
# include "content.h"
# include "core_info.h"
# include "dynamic.h"
# include "defaults.h"
# include "driver.h"
# include "msg_hash.h"
# include "paths.h"
# include "file_path_special.h"
# include "ui/ui_companion_driver.h"
# include "verbosity.h"
# include "frontend/frontend_driver.h"
# ifdef HAVE_THREADS
# include "gfx/video_thread_wrapper.h"
# endif
# include "gfx/video_display_server.h"
# ifdef HAVE_CRTSWITCHRES
# include "gfx/video_crt_switch.h"
# endif
# ifdef HAVE_BLUETOOTH
# include "bluetooth/bluetooth_driver.h"
# endif
# include "misc/cpufreq/cpufreq.h"
# include "led/led_driver.h"
# include "midi_driver.h"
# include "location_driver.h"
# include "core.h"
# include "configuration.h"
# include "list_special.h"
# include "core_option_manager.h"
# ifdef HAVE_CHEATS
# include "cheat_manager.h"
# endif
# ifdef HAVE_REWIND
# include "state_manager.h"
# endif
# include "tasks/task_content.h"
# include "tasks/task_file_transfer.h"
# include "tasks/task_powerstate.h"
# include "tasks/tasks_internal.h"
# include "performance_counters.h"
# include "version.h"
# include "version_git.h"
# include "retroarch.h"
# include "accessibility.h"
# if defined(HAVE_SDL) || defined(HAVE_SDL2) || defined(HAVE_SDL_DINGUX)
# include "SDL.h"
# endif
# ifdef HAVE_LAKKA
# include "lakka.h"
# endif
# define SHADER_FILE_WATCH_DELAY_MSEC 500
# define QUIT_DELAY_USEC 3 * 1000000 /* 3 seconds */
# define DEFAULT_NETWORK_GAMEPAD_PORT 55400
# define UDP_FRAME_PACKETS 16
# ifdef HAVE_ZLIB
# define DEFAULT_EXT "zip"
# else
# define DEFAULT_EXT ""
# endif
# ifdef HAVE_DYNAMIC
# define SYMBOL(x) do { \
function_t func = dylib_proc ( lib_handle_local , # x ) ; \
memcpy ( & current_core - > x , & func , sizeof ( func ) ) ; \
if ( ! current_core - > x ) { RARCH_ERR ( " Failed to load symbol: \" %s \" \n " , # x ) ; retroarch_fail ( 1 , " init_libretro_symbols() " ) ; } \
} while ( 0 )
# else
# define SYMBOL(x) current_core->x = x
# endif
# define SYMBOL_DUMMY(x) current_core->x = libretro_dummy_##x
# ifdef HAVE_FFMPEG
# define SYMBOL_FFMPEG(x) current_core->x = libretro_ffmpeg_##x
# endif
# ifdef HAVE_MPV
# define SYMBOL_MPV(x) current_core->x = libretro_mpv_##x
# endif
# ifdef HAVE_IMAGEVIEWER
# define SYMBOL_IMAGEVIEWER(x) current_core->x = libretro_imageviewer_##x
# endif
# if defined(HAVE_NETWORKING) && defined(HAVE_NETWORKGAMEPAD)
# define SYMBOL_NETRETROPAD(x) current_core->x = libretro_netretropad_##x
# endif
# if defined(HAVE_VIDEOPROCESSOR)
# define SYMBOL_VIDEOPROCESSOR(x) current_core->x = libretro_videoprocessor_##x
# endif
# ifdef HAVE_GONG
# define SYMBOL_GONG(x) current_core->x = libretro_gong_##x
# endif
# define CORE_SYMBOLS(x) \
x ( retro_init ) ; \
x ( retro_deinit ) ; \
x ( retro_api_version ) ; \
x ( retro_get_system_info ) ; \
x ( retro_get_system_av_info ) ; \
x ( retro_set_environment ) ; \
x ( retro_set_video_refresh ) ; \
x ( retro_set_audio_sample ) ; \
x ( retro_set_audio_sample_batch ) ; \
x ( retro_set_input_poll ) ; \
x ( retro_set_input_state ) ; \
x ( retro_set_controller_port_device ) ; \
x ( retro_reset ) ; \
x ( retro_run ) ; \
x ( retro_serialize_size ) ; \
x ( retro_serialize ) ; \
x ( retro_unserialize ) ; \
x ( retro_cheat_reset ) ; \
x ( retro_cheat_set ) ; \
x ( retro_load_game ) ; \
x ( retro_load_game_special ) ; \
x ( retro_unload_game ) ; \
x ( retro_get_region ) ; \
x ( retro_get_memory_data ) ; \
x ( retro_get_memory_size ) ;
# ifdef _WIN32
# define PERF_LOG_FMT "[PERF]: Avg (%s): %I64u ticks, %I64u runs.\n"
# else
# define PERF_LOG_FMT "[PERF]: Avg (%s): %llu ticks, %llu runs.\n"
# endif
static runloop_state_t runloop_state = { 0 } ;
/* GLOBAL POINTER GETTERS */
runloop_state_t * runloop_state_get_ptr ( void )
{
return & runloop_state ;
}
# ifdef HAVE_REWIND
bool state_manager_frame_is_reversed ( void )
{
return runloop_state . rewind_st . frame_is_reversed ;
}
# endif
content_state_t * content_state_get_ptr ( void )
{
return & runloop_state . content_st ;
}
/* Get the current subsystem rom id */
unsigned content_get_subsystem_rom_id ( void )
{
return runloop_state . content_st . pending_subsystem_rom_id ;
}
/* Get the current subsystem */
int content_get_subsystem ( void )
{
return runloop_state . content_st . pending_subsystem_id ;
}
struct retro_perf_counter * * retro_get_perf_counter_libretro ( void )
{
return runloop_state . perf_counters_libretro ;
}
unsigned retro_get_perf_count_libretro ( void )
{
return runloop_state . perf_ptr_libretro ;
}
void runloop_performance_counter_register ( struct retro_perf_counter * perf )
{
if ( perf - > registered
| | runloop_state . perf_ptr_libretro > = MAX_COUNTERS )
return ;
runloop_state . perf_counters_libretro [ runloop_state . perf_ptr_libretro + + ] = perf ;
perf - > registered = true ;
}
void runloop_log_counters (
struct retro_perf_counter * * counters , unsigned num )
{
unsigned i ;
for ( i = 0 ; i < num ; i + + )
{
if ( counters [ i ] - > call_cnt )
{
RARCH_LOG ( PERF_LOG_FMT ,
counters [ i ] - > ident ,
( uint64_t ) counters [ i ] - > total /
( uint64_t ) counters [ i ] - > call_cnt ,
( uint64_t ) counters [ i ] - > call_cnt ) ;
}
}
}
void runloop_perf_log ( void )
{
RARCH_LOG ( " [PERF]: Performance counters (libretro): \n " ) ;
runloop_log_counters ( runloop_state . perf_counters_libretro ,
runloop_state . perf_ptr_libretro ) ;
}
static bool runloop_environ_cb_get_system_info ( unsigned cmd , void * data )
{
runloop_state_t * runloop_st = & runloop_state ;
rarch_system_info_t * system = & runloop_st - > system ;
switch ( cmd )
{
case RETRO_ENVIRONMENT_SET_SUPPORT_NO_GAME :
* runloop_st - > load_no_content_hook = * ( const bool * ) data ;
break ;
case RETRO_ENVIRONMENT_SET_SUBSYSTEM_INFO :
{
unsigned i , j , size ;
const struct retro_subsystem_info * info =
( const struct retro_subsystem_info * ) data ;
settings_t * settings = config_get_ptr ( ) ;
unsigned log_level = settings - > uints . libretro_log_level ;
runloop_st - > subsystem_current_count = 0 ;
RARCH_LOG ( " [Environ]: SET_SUBSYSTEM_INFO. \n " ) ;
for ( i = 0 ; info [ i ] . ident ; i + + )
{
if ( log_level ! = RETRO_LOG_DEBUG )
continue ;
RARCH_LOG ( " Subsystem ID: %d \n Special game type: %s \n Ident: %s \n ID: %u \n Content: \n " ,
i ,
info [ i ] . desc ,
info [ i ] . ident ,
info [ i ] . id
) ;
for ( j = 0 ; j < info [ i ] . num_roms ; j + + )
{
RARCH_LOG ( " %s (%s) \n " ,
info [ i ] . roms [ j ] . desc , info [ i ] . roms [ j ] . required ?
" required " : " optional " ) ;
}
}
size = i ;
if ( log_level = = RETRO_LOG_DEBUG )
{
RARCH_LOG ( " Subsystems: %d \n " , i ) ;
if ( size > SUBSYSTEM_MAX_SUBSYSTEMS )
RARCH_WARN ( " Subsystems exceed subsystem max, clamping to %d \n " , SUBSYSTEM_MAX_SUBSYSTEMS ) ;
}
if ( system )
{
for ( i = 0 ; i < size & & i < SUBSYSTEM_MAX_SUBSYSTEMS ; i + + )
{
struct retro_subsystem_info * subsys_info = & runloop_st - > subsystem_data [ i ] ;
struct retro_subsystem_rom_info * subsys_rom_info = runloop_st - > subsystem_data_roms [ i ] ;
/* Nasty, but have to do it like this since
* the pointers are const char *
* ( if we don ' t free them , we get a memory leak ) */
if ( ! string_is_empty ( subsys_info - > desc ) )
free ( ( char * ) subsys_info - > desc ) ;
if ( ! string_is_empty ( subsys_info - > ident ) )
free ( ( char * ) subsys_info - > ident ) ;
subsys_info - > desc = strdup ( info [ i ] . desc ) ;
subsys_info - > ident = strdup ( info [ i ] . ident ) ;
subsys_info - > id = info [ i ] . id ;
subsys_info - > num_roms = info [ i ] . num_roms ;
if ( log_level = = RETRO_LOG_DEBUG )
if ( subsys_info - > num_roms > SUBSYSTEM_MAX_SUBSYSTEM_ROMS )
RARCH_WARN ( " Subsystems exceed subsystem max roms, clamping to %d \n " , SUBSYSTEM_MAX_SUBSYSTEM_ROMS ) ;
for ( j = 0 ; j < subsys_info - > num_roms & & j < SUBSYSTEM_MAX_SUBSYSTEM_ROMS ; j + + )
{
/* Nasty, but have to do it like this since
* the pointers are const char *
* ( if we don ' t free them , we get a memory leak ) */
if ( ! string_is_empty ( subsys_rom_info [ j ] . desc ) )
free ( ( char * )
subsys_rom_info [ j ] . desc ) ;
if ( ! string_is_empty (
subsys_rom_info [ j ] . valid_extensions ) )
free ( ( char * )
subsys_rom_info [ j ] . valid_extensions ) ;
subsys_rom_info [ j ] . desc =
strdup ( info [ i ] . roms [ j ] . desc ) ;
subsys_rom_info [ j ] . valid_extensions =
strdup ( info [ i ] . roms [ j ] . valid_extensions ) ;
subsys_rom_info [ j ] . required =
info [ i ] . roms [ j ] . required ;
subsys_rom_info [ j ] . block_extract =
info [ i ] . roms [ j ] . block_extract ;
subsys_rom_info [ j ] . need_fullpath =
info [ i ] . roms [ j ] . need_fullpath ;
}
subsys_info - > roms = subsys_rom_info ;
}
runloop_st - > subsystem_current_count =
size < = SUBSYSTEM_MAX_SUBSYSTEMS
? size
: SUBSYSTEM_MAX_SUBSYSTEMS ;
}
break ;
}
default :
return false ;
}
return true ;
}
# ifdef HAVE_DYNAMIC
/**
* libretro_get_environment_info :
* @ func : Function pointer for get_environment_info .
* @ load_no_content : If true , core should be able to auto - start
* without any content loaded .
*
* Sets environment callback in order to get statically known
* information from it .
*
* Fetched via environment callbacks instead of
* retro_get_system_info ( ) , as this info is part of extensions .
*
* Should only be called once right after core load to
* avoid overwriting the " real " environ callback .
*
* For statically linked cores , pass retro_set_environment as argument .
*/
void libretro_get_environment_info (
void ( * func ) ( retro_environment_t ) ,
bool * load_no_content )
{
runloop_state_t * runloop_st = & runloop_state ;
runloop_st - > load_no_content_hook = load_no_content ;
/* load_no_content gets set in this callback. */
func ( runloop_environ_cb_get_system_info ) ;
/* It's possible that we just set get_system_info callback
* to the currently running core .
*
* Make sure we reset it to the actual environment callback .
* Ignore any environment callbacks here in case we ' re running
* on the non - current core . */
runloop_st - > ignore_environment_cb = true ;
func ( runloop_environment_cb ) ;
runloop_st - > ignore_environment_cb = false ;
}
static dylib_t load_dynamic_core ( const char * path , char * buf ,
size_t size )
{
# if defined(ANDROID)
/* Can't resolve symlinks when dealing with cores
* installed via play feature delivery , because the
* source files have non - standard file names ( which
* will not be recognised by regular core handling
* routines ) */
bool resolve_symlinks = ! play_feature_delivery_enabled ( ) ;
# else
bool resolve_symlinks = true ;
# endif
/* Can't lookup symbols in itself on UWP */
# if !(defined(__WINRT__) || defined(WINAPI_FAMILY) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP)
if ( dylib_proc ( NULL , " retro_init " ) )
{
/* Try to verify that -lretro was not linked in from other modules
* since loading it dynamically and with - l will fail hard . */
RARCH_ERR ( " Serious problem. RetroArch wants to load libretro cores "
" dynamically, but it is already linked. \n " ) ;
RARCH_ERR ( " This could happen if other modules RetroArch depends on "
" link against libretro directly. \n " ) ;
RARCH_ERR ( " Proceeding could cause a crash. Aborting ... \n " ) ;
retroarch_fail ( 1 , " load_dynamic_core() " ) ;
}
# endif
/* Need to use absolute path for this setting. It can be
* saved to content history , and a relative path would
* break in that scenario . */
path_resolve_realpath ( buf , size , resolve_symlinks ) ;
return dylib_load ( path ) ;
}
static dylib_t libretro_get_system_info_lib ( const char * path ,
struct retro_system_info * info , bool * load_no_content )
{
dylib_t lib = dylib_load ( path ) ;
void ( * proc ) ( struct retro_system_info * ) ;
if ( ! lib )
return NULL ;
proc = ( void ( * ) ( struct retro_system_info * ) )
dylib_proc ( lib , " retro_get_system_info " ) ;
if ( ! proc )
{
dylib_close ( lib ) ;
return NULL ;
}
proc ( info ) ;
if ( load_no_content )
{
void ( * set_environ ) ( retro_environment_t ) ;
* load_no_content = false ;
set_environ = ( void ( * ) ( retro_environment_t ) )
dylib_proc ( lib , " retro_set_environment " ) ;
if ( set_environ )
libretro_get_environment_info ( set_environ , load_no_content ) ;
}
return lib ;
}
# endif
static void runloop_update_runtime_log (
runloop_state_t * runloop_st ,
const char * dir_runtime_log ,
const char * dir_playlist ,
bool log_per_core )
{
/* Initialise runtime log file */
runtime_log_t * runtime_log = runtime_log_init (
runloop_st - > runtime_content_path ,
runloop_st - > runtime_core_path ,
dir_runtime_log ,
dir_playlist ,
log_per_core ) ;
if ( ! runtime_log )
return ;
/* Add additional runtime */
runtime_log_add_runtime_usec ( runtime_log ,
runloop_st - > core_runtime_usec ) ;
/* Update 'last played' entry */
runtime_log_set_last_played_now ( runtime_log ) ;
/* Save runtime log file */
runtime_log_save ( runtime_log ) ;
/* Clean up */
free ( runtime_log ) ;
}
void runloop_runtime_log_deinit (
runloop_state_t * runloop_st ,
bool content_runtime_log ,
bool content_runtime_log_aggregate ,
const char * dir_runtime_log ,
const char * dir_playlist )
{
if ( verbosity_is_enabled ( ) )
{
int n ;
char log [ PATH_MAX_LENGTH ] = { 0 } ;
unsigned hours = 0 ;
unsigned minutes = 0 ;
unsigned seconds = 0 ;
runtime_log_convert_usec2hms (
runloop_st - > core_runtime_usec ,
& hours , & minutes , & seconds ) ;
n =
snprintf ( log , sizeof ( log ) ,
" [Core]: Content ran for a total of: "
" %02u hours, %02u minutes, %02u seconds. " ,
hours , minutes , seconds ) ;
if ( ( n < 0 ) | | ( n > = PATH_MAX_LENGTH ) )
n = 0 ; /* Just silence any potential gcc warnings... */
( void ) n ;
RARCH_LOG ( " %s \n " , log ) ;
}
/* Only write to file if content has run for a non-zero length of time */
if ( runloop_st - > core_runtime_usec > 0 )
{
/* Per core logging */
if ( content_runtime_log )
runloop_update_runtime_log ( runloop_st , dir_runtime_log , dir_playlist , true ) ;
/* Aggregate logging */
if ( content_runtime_log_aggregate )
runloop_update_runtime_log ( runloop_st , dir_runtime_log , dir_playlist , false ) ;
}
/* Reset runtime + content/core paths, to prevent any
* possibility of duplicate logging */
runloop_st - > core_runtime_usec = 0 ;
memset ( runloop_st - > runtime_content_path , 0 ,
sizeof ( runloop_st - > runtime_content_path ) ) ;
memset ( runloop_st - > runtime_core_path , 0 ,
sizeof ( runloop_st - > runtime_core_path ) ) ;
}
static bool runloop_clear_all_thread_waits (
unsigned clear_threads , void * data )
{
if ( clear_threads > 0 )
audio_driver_start ( false ) ;
else
audio_driver_stop ( ) ;
return true ;
}
static bool dynamic_verify_hw_context (
const char * video_ident ,
bool driver_switch_enable ,
enum retro_hw_context_type type ,
unsigned minor , unsigned major )
{
if ( driver_switch_enable )
return true ;
switch ( type )
{
case RETRO_HW_CONTEXT_VULKAN :
if ( ! string_is_equal ( video_ident , " vulkan " ) )
return false ;
break ;
# if defined(HAVE_OPENGL_CORE)
case RETRO_HW_CONTEXT_OPENGL_CORE :
if ( ! string_is_equal ( video_ident , " glcore " ) )
return false ;
break ;
# else
case RETRO_HW_CONTEXT_OPENGL_CORE :
# endif
case RETRO_HW_CONTEXT_OPENGLES2 :
case RETRO_HW_CONTEXT_OPENGLES3 :
case RETRO_HW_CONTEXT_OPENGLES_VERSION :
case RETRO_HW_CONTEXT_OPENGL :
if ( ! string_is_equal ( video_ident , " gl " ) & &
! string_is_equal ( video_ident , " glcore " ) )
return false ;
break ;
case RETRO_HW_CONTEXT_DIRECT3D :
if ( ! ( string_is_equal ( video_ident , " d3d11 " ) & & major = = 11 ) )
return false ;
break ;
default :
break ;
}
return true ;
}
static bool dynamic_request_hw_context ( enum retro_hw_context_type type ,
unsigned minor , unsigned major )
{
switch ( type )
{
case RETRO_HW_CONTEXT_NONE :
RARCH_LOG ( " Requesting no HW context. \n " ) ;
break ;
case RETRO_HW_CONTEXT_VULKAN :
# ifdef HAVE_VULKAN
RARCH_LOG ( " Requesting Vulkan context. \n " ) ;
break ;
# else
RARCH_ERR ( " Requesting Vulkan context, but RetroArch is not compiled against Vulkan. Cannot use HW context. \n " ) ;
return false ;
# endif
# if defined(HAVE_OPENGLES)
# if (defined(HAVE_OPENGLES2) || defined(HAVE_OPENGLES3))
case RETRO_HW_CONTEXT_OPENGLES2 :
case RETRO_HW_CONTEXT_OPENGLES3 :
RARCH_LOG ( " Requesting OpenGLES%u context. \n " ,
type = = RETRO_HW_CONTEXT_OPENGLES2 ? 2 : 3 ) ;
break ;
# if defined(HAVE_OPENGLES3)
case RETRO_HW_CONTEXT_OPENGLES_VERSION :
# ifndef HAVE_OPENGLES3_2
if ( major = = 3 & & minor = = 2 )
{
RARCH_ERR ( " Requesting OpenGLES%u.%u context, but RetroArch is compiled against a lesser version. Cannot use HW context. \n " ,
major , minor ) ;
return false ;
}
# endif
# if !defined(HAVE_OPENGLES3_2) && !defined(HAVE_OPENGLES3_1)
if ( major = = 3 & & minor = = 1 )
{
RARCH_ERR ( " Requesting OpenGLES%u.%u context, but RetroArch is compiled against a lesser version. Cannot use HW context. \n " ,
major , minor ) ;
return false ;
}
# endif
RARCH_LOG ( " Requesting OpenGLES%u.%u context. \n " ,
major , minor ) ;
break ;
# endif
# endif
case RETRO_HW_CONTEXT_OPENGL :
case RETRO_HW_CONTEXT_OPENGL_CORE :
RARCH_ERR ( " Requesting OpenGL context, but RetroArch "
" is compiled against OpenGLES. Cannot use HW context. \n " ) ;
return false ;
# elif defined(HAVE_OPENGL) || defined(HAVE_OPENGL_CORE)
case RETRO_HW_CONTEXT_OPENGLES2 :
case RETRO_HW_CONTEXT_OPENGLES3 :
RARCH_ERR ( " Requesting OpenGLES%u context, but RetroArch "
" is compiled against OpenGL. Cannot use HW context. \n " ,
type = = RETRO_HW_CONTEXT_OPENGLES2 ? 2 : 3 ) ;
return false ;
case RETRO_HW_CONTEXT_OPENGLES_VERSION :
RARCH_ERR ( " Requesting OpenGLES%u.%u context, but RetroArch "
" is compiled against OpenGL. Cannot use HW context. \n " ,
major , minor ) ;
return false ;
case RETRO_HW_CONTEXT_OPENGL :
RARCH_LOG ( " Requesting OpenGL context. \n " ) ;
break ;
case RETRO_HW_CONTEXT_OPENGL_CORE :
/* TODO/FIXME - we should do a check here to see if
* the requested core GL version is supported */
RARCH_LOG ( " Requesting core OpenGL context (%u.%u). \n " ,
major , minor ) ;
break ;
# endif
# if defined(HAVE_D3D9) || defined(HAVE_D3D11)
case RETRO_HW_CONTEXT_DIRECT3D :
switch ( major )
{
# ifdef HAVE_D3D9
case 9 :
RARCH_LOG ( " Requesting D3D9 context. \n " ) ;
break ;
# endif
# ifdef HAVE_D3D11
case 11 :
RARCH_LOG ( " Requesting D3D11 context. \n " ) ;
break ;
# endif
default :
RARCH_LOG ( " Requesting unknown context. \n " ) ;
return false ;
}
break ;
# endif
default :
RARCH_LOG ( " Requesting unknown context. \n " ) ;
return false ;
}
return true ;
}
static void libretro_log_cb (
enum retro_log_level level ,
const char * fmt , . . . )
{
va_list vp ;
settings_t * settings = config_get_ptr ( ) ;
unsigned libretro_log_level = settings - > uints . libretro_log_level ;
if ( ( unsigned ) level < libretro_log_level )
return ;
if ( ! verbosity_is_enabled ( ) )
return ;
va_start ( vp , fmt ) ;
switch ( level )
{
case RETRO_LOG_DEBUG :
RARCH_LOG_V ( " [libretro DEBUG] " , fmt , vp ) ;
break ;
case RETRO_LOG_INFO :
RARCH_LOG_OUTPUT_V ( " [libretro INFO] " , fmt , vp ) ;
break ;
case RETRO_LOG_WARN :
RARCH_WARN_V ( " [libretro WARN] " , fmt , vp ) ;
break ;
case RETRO_LOG_ERROR :
RARCH_ERR_V ( " [libretro ERROR] " , fmt , vp ) ;
break ;
default :
break ;
}
va_end ( vp ) ;
}
static size_t mmap_add_bits_down ( size_t n )
{
n | = n > > 1 ;
n | = n > > 2 ;
n | = n > > 4 ;
n | = n > > 8 ;
n | = n > > 16 ;
/* double shift to avoid warnings on 32bit (it's dead code,
* but compilers suck ) */
if ( sizeof ( size_t ) > 4 )
n | = n > > 16 > > 16 ;
return n ;
}
static size_t mmap_inflate ( size_t addr , size_t mask )
{
while ( mask )
{
size_t tmp = ( mask - 1 ) & ~ mask ;
/* to put in an 1 bit instead, OR in tmp+1 */
addr = ( ( addr & ~ tmp ) < < 1 ) | ( addr & tmp ) ;
mask = mask & ( mask - 1 ) ;
}
return addr ;
}
static size_t mmap_reduce ( size_t addr , size_t mask )
{
while ( mask )
{
size_t tmp = ( mask - 1 ) & ~ mask ;
addr = ( addr & tmp ) | ( ( addr > > 1 ) & ~ tmp ) ;
mask = ( mask & ( mask - 1 ) ) > > 1 ;
}
return addr ;
}
static size_t mmap_highest_bit ( size_t n )
{
n = mmap_add_bits_down ( n ) ;
return n ^ ( n > > 1 ) ;
}
static bool mmap_preprocess_descriptors (
rarch_memory_descriptor_t * first , unsigned count )
{
size_t top_addr = 1 ;
rarch_memory_descriptor_t * desc = NULL ;
const rarch_memory_descriptor_t * end = first + count ;
for ( desc = first ; desc < end ; desc + + )
{
if ( desc - > core . select ! = 0 )
top_addr | = desc - > core . select ;
else
top_addr | = desc - > core . start + desc - > core . len - 1 ;
}
top_addr = mmap_add_bits_down ( top_addr ) ;
for ( desc = first ; desc < end ; desc + + )
{
if ( desc - > core . select = = 0 )
{
if ( desc - > core . len = = 0 )
return false ;
if ( ( desc - > core . len & ( desc - > core . len - 1 ) ) ! = 0 )
return false ;
desc - > core . select = top_addr & ~ mmap_inflate ( mmap_add_bits_down ( desc - > core . len - 1 ) ,
desc - > core . disconnect ) ;
}
if ( desc - > core . len = = 0 )
desc - > core . len = mmap_add_bits_down ( mmap_reduce ( top_addr & ~ desc - > core . select ,
desc - > core . disconnect ) ) + 1 ;
if ( desc - > core . start & ~ desc - > core . select )
return false ;
while ( mmap_reduce ( top_addr & ~ desc - > core . select , desc - > core . disconnect ) > > 1 > desc - > core . len - 1 )
desc - > core . disconnect | = mmap_highest_bit ( top_addr & ~ desc - > core . select & ~ desc - > core . disconnect ) ;
desc - > disconnect_mask = mmap_add_bits_down ( desc - > core . len - 1 ) ;
desc - > core . disconnect & = desc - > disconnect_mask ;
while ( ( ~ desc - > disconnect_mask ) > > 1 & desc - > core . disconnect )
{
desc - > disconnect_mask > > = 1 ;
desc - > core . disconnect & = desc - > disconnect_mask ;
}
}
return true ;
}
static void runloop_deinit_core_options (
bool game_options_active ,
const char * path_core_options ,
core_option_manager_t * core_options )
{
/* Check whether game-specific options file is being used */
if ( ! string_is_empty ( path_core_options ) )
{
config_file_t * conf_tmp = NULL ;
/* We only need to save configuration settings for
* the current core
* > If game - specific options file exists , have
* to read it ( to ensure file only gets written
* if config values change )
* > Otherwise , create a new , empty config_file_t
* object */
if ( path_is_valid ( path_core_options ) )
conf_tmp = config_file_new_from_path_to_string ( path_core_options ) ;
if ( ! conf_tmp )
conf_tmp = config_file_new_alloc ( ) ;
if ( conf_tmp )
{
core_option_manager_flush (
core_options ,
conf_tmp ) ;
RARCH_LOG ( " [Core Options]: Saved %s-specific core options to \" %s \" \n " ,
game_options_active ? " game " : " folder " , path_core_options ) ;
config_file_write ( conf_tmp , path_core_options , true ) ;
config_file_free ( conf_tmp ) ;
conf_tmp = NULL ;
}
path_clear ( RARCH_PATH_CORE_OPTIONS ) ;
}
else
{
const char * path = core_options - > conf_path ;
core_option_manager_flush (
core_options ,
core_options - > conf ) ;
RARCH_LOG ( " [Core Options]: Saved core options file to \" %s \" \n " , path ) ;
config_file_write ( core_options - > conf , path , true ) ;
}
if ( core_options )
core_option_manager_free ( core_options ) ;
}
static bool validate_per_core_options ( char * s ,
size_t len , bool mkdir ,
const char * core_name , const char * game_name )
{
char config_directory [ PATH_MAX_LENGTH ] ;
config_directory [ 0 ] = ' \0 ' ;
if ( ( ! s )
| | ( len < 1 )
| | string_is_empty ( core_name )
| | string_is_empty ( game_name ) )
return false ;
fill_pathname_application_special ( config_directory ,
sizeof ( config_directory ) , APPLICATION_SPECIAL_DIRECTORY_CONFIG ) ;
fill_pathname_join_special_ext ( s ,
config_directory , core_name , game_name ,
" .opt " , len ) ;
/* No need to make a directory if file already exists... */
if ( mkdir & & ! path_is_valid ( s ) )
{
char new_path [ PATH_MAX_LENGTH ] ;
new_path [ 0 ] = ' \0 ' ;
fill_pathname_join ( new_path ,
config_directory , core_name , sizeof ( new_path ) ) ;
if ( ! path_is_directory ( new_path ) )
path_mkdir ( new_path ) ;
}
return true ;
}
static bool validate_game_options (
const char * core_name ,
char * s , size_t len , bool mkdir )
{
const char * game_name = path_basename ( path_get ( RARCH_PATH_BASENAME ) ) ;
return validate_per_core_options ( s , len , mkdir ,
core_name , game_name ) ;
}
/**
* game_specific_options :
*
* Returns : true ( 1 ) if a game specific core
* options path has been found ,
* otherwise false ( 0 ) .
* */
static bool validate_game_specific_options ( char * * output )
{
char game_options_path [ PATH_MAX_LENGTH ] ;
runloop_state_t * runloop_st = & runloop_state ;
game_options_path [ 0 ] = ' \0 ' ;
if ( ! validate_game_options (
runloop_st - > system . info . library_name ,
game_options_path ,
sizeof ( game_options_path ) , false ) | |
! path_is_valid ( game_options_path ) )
return false ;
RARCH_LOG ( " %s %s \n " ,
msg_hash_to_str ( MSG_GAME_SPECIFIC_CORE_OPTIONS_FOUND_AT ) ,
game_options_path ) ;
* output = strdup ( game_options_path ) ;
return true ;
}
static bool validate_folder_options (
char * s , size_t len , bool mkdir )
{
char folder_name [ PATH_MAX_LENGTH ] ;
runloop_state_t * runloop_st = & runloop_state ;
const char * core_name = runloop_st - > system . info . library_name ;
const char * game_path = path_get ( RARCH_PATH_BASENAME ) ;
folder_name [ 0 ] = ' \0 ' ;
if ( string_is_empty ( game_path ) )
return false ;
fill_pathname_parent_dir_name ( folder_name ,
game_path , sizeof ( folder_name ) ) ;
return validate_per_core_options ( s , len , mkdir ,
core_name , folder_name ) ;
}
/**
* validate_folder_specific_options :
*
* Returns : true ( 1 ) if a folder specific core
* options path has been found ,
* otherwise false ( 0 ) .
* */
static bool validate_folder_specific_options (
char * * output )
{
char folder_options_path [ PATH_MAX_LENGTH ] ;
folder_options_path [ 0 ] = ' \0 ' ;
if ( ! validate_folder_options (
folder_options_path ,
sizeof ( folder_options_path ) , false ) | |
! path_is_valid ( folder_options_path ) )
return false ;
RARCH_LOG ( " %s %s \n " ,
msg_hash_to_str ( MSG_FOLDER_SPECIFIC_CORE_OPTIONS_FOUND_AT ) ,
folder_options_path ) ;
* output = strdup ( folder_options_path ) ;
return true ;
}
/* Fetches core options path for current core/content
* - path : path from which options should be read
* from / saved to
* - src_path : in the event that ' path ' file does not
* yet exist , provides source path from which initial
* options should be extracted
*
* NOTE : caller must ensure
* path and src_path are NULL - terminated
* */
static void runloop_init_core_options_path (
settings_t * settings ,
char * path , size_t len ,
char * src_path , size_t src_len )
{
char * game_options_path = NULL ;
char * folder_options_path = NULL ;
runloop_state_t * runloop_st = & runloop_state ;
bool game_specific_options = settings - > bools . game_specific_options ;
/* Check whether game-specific options exist */
if ( game_specific_options & &
validate_game_specific_options ( & game_options_path ) )
{
/* Notify system that we have a valid core options
* override */
path_set ( RARCH_PATH_CORE_OPTIONS , game_options_path ) ;
runloop_st - > game_options_active = true ;
runloop_st - > folder_options_active = false ;
/* Copy options path */
strlcpy ( path , game_options_path , len ) ;
free ( game_options_path ) ;
}
/* Check whether folder-specific options exist */
else if ( game_specific_options & &
validate_folder_specific_options (
& folder_options_path ) )
{
/* Notify system that we have a valid core options
* override */
path_set ( RARCH_PATH_CORE_OPTIONS , folder_options_path ) ;
runloop_st - > game_options_active = false ;
runloop_st - > folder_options_active = true ;
/* Copy options path */
strlcpy ( path , folder_options_path , len ) ;
free ( folder_options_path ) ;
}
else
{
char global_options_path [ PATH_MAX_LENGTH ] ;
char per_core_options_path [ PATH_MAX_LENGTH ] ;
bool per_core_options_exist = false ;
bool per_core_options = ! settings - > bools . global_core_options ;
const char * path_core_options = settings - > paths . path_core_options ;
global_options_path [ 0 ] = ' \0 ' ;
per_core_options_path [ 0 ] = ' \0 ' ;
if ( per_core_options )
{
const char * core_name = runloop_st - > system . info . library_name ;
/* Get core-specific options path
* > if validate_per_core_options ( ) returns
* false , then per - core options are disabled ( due to
* unknown system errors . . . ) */
per_core_options = validate_per_core_options (
per_core_options_path , sizeof ( per_core_options_path ) , true ,
core_name , core_name ) ;
/* If we can use per-core options, check whether an options
* file already exists */
if ( per_core_options )
per_core_options_exist = path_is_valid ( per_core_options_path ) ;
}
/* If not using per-core options, or if a per-core options
* file does not yet exist , must fetch ' global ' options path */
if ( ! per_core_options | | ! per_core_options_exist )
{
const char * options_path = path_core_options ;
if ( ! string_is_empty ( options_path ) )
strlcpy ( global_options_path ,
options_path , sizeof ( global_options_path ) ) ;
else if ( ! path_is_empty ( RARCH_PATH_CONFIG ) )
fill_pathname_resolve_relative (
global_options_path , path_get ( RARCH_PATH_CONFIG ) ,
FILE_PATH_CORE_OPTIONS_CONFIG , sizeof ( global_options_path ) ) ;
}
/* Allocate correct path/src_path strings */
if ( per_core_options )
{
strlcpy ( path , per_core_options_path , len ) ;
if ( ! per_core_options_exist )
strlcpy ( src_path , global_options_path , src_len ) ;
}
else
strlcpy ( path , global_options_path , len ) ;
/* Notify system that we *do not* have a valid core options
* options override */
runloop_st - > game_options_active = false ;
runloop_st - > folder_options_active = false ;
}
}
static core_option_manager_t * runloop_init_core_options (
settings_t * settings ,
const struct retro_core_options_v2 * options_v2 )
{
bool categories_enabled = settings - > bools . core_option_category_enable ;
char options_path [ PATH_MAX_LENGTH ] ;
char src_options_path [ PATH_MAX_LENGTH ] ;
/* Ensure these are NULL-terminated */
options_path [ 0 ] = ' \0 ' ;
src_options_path [ 0 ] = ' \0 ' ;
/* Get core options file path */
runloop_init_core_options_path ( settings ,
options_path , sizeof ( options_path ) ,
src_options_path , sizeof ( src_options_path ) ) ;
if ( ! string_is_empty ( options_path ) )
return core_option_manager_new ( options_path ,
src_options_path , options_v2 ,
categories_enabled ) ;
return NULL ;
}
static core_option_manager_t * runloop_init_core_variables (
settings_t * settings , const struct retro_variable * vars )
{
char options_path [ PATH_MAX_LENGTH ] ;
char src_options_path [ PATH_MAX_LENGTH ] ;
/* Ensure these are NULL-terminated */
options_path [ 0 ] = ' \0 ' ;
src_options_path [ 0 ] = ' \0 ' ;
/* Get core options file path */
runloop_init_core_options_path (
settings ,
options_path , sizeof ( options_path ) ,
src_options_path , sizeof ( src_options_path ) ) ;
if ( ! string_is_empty ( options_path ) )
return core_option_manager_new_vars ( options_path , src_options_path , vars ) ;
return NULL ;
}
static void runloop_core_msg_queue_push (
struct retro_system_av_info * av_info ,
const struct retro_message_ext * msg )
{
double fps ;
unsigned duration_frames ;
enum message_queue_category category ;
/* Assign category */
switch ( msg - > level )
{
case RETRO_LOG_WARN :
category = MESSAGE_QUEUE_CATEGORY_WARNING ;
break ;
case RETRO_LOG_ERROR :
category = MESSAGE_QUEUE_CATEGORY_ERROR ;
break ;
case RETRO_LOG_INFO :
case RETRO_LOG_DEBUG :
default :
category = MESSAGE_QUEUE_CATEGORY_INFO ;
break ;
}
/* Get duration in frames */
fps = ( av_info & & ( av_info - > timing . fps > 0 ) ) ? av_info - > timing . fps : 60.0 ;
duration_frames = ( unsigned ) ( ( fps * ( float ) msg - > duration / 1000.0f ) + 0.5f ) ;
/* Note: Do not flush the message queue here - a core
* may need to send multiple notifications simultaneously */
runloop_msg_queue_push ( msg - > msg ,
msg - > priority , duration_frames ,
false , NULL , MESSAGE_QUEUE_ICON_DEFAULT ,
category ) ;
}
static void core_performance_counter_start (
struct retro_perf_counter * perf )
{
runloop_state_t * runloop_st = & runloop_state ;
bool runloop_perfcnt_enable = runloop_st - > perfcnt_enable ;
if ( runloop_perfcnt_enable )
{
perf - > call_cnt + + ;
perf - > start = cpu_features_get_perf_counter ( ) ;
}
}
static void core_performance_counter_stop ( struct retro_perf_counter * perf )
{
runloop_state_t * runloop_st = & runloop_state ;
bool runloop_perfcnt_enable = runloop_st - > perfcnt_enable ;
if ( runloop_perfcnt_enable )
perf - > total + = cpu_features_get_perf_counter ( ) - perf - > start ;
}
bool runloop_environment_cb ( unsigned cmd , void * data )
{
unsigned p ;
runloop_state_t * runloop_st = & runloop_state ;
recording_state_t * recording_st = recording_state_get_ptr ( ) ;
settings_t * settings = config_get_ptr ( ) ;
rarch_system_info_t * system = & runloop_st - > system ;
bool ignore_environment_cb = runloop_st - > ignore_environment_cb ;
if ( ignore_environment_cb )
return false ;
/* RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE gets called
* by every core on every frame . Handle it first ,
* to avoid the overhead of traversing the subsequent
* ( enormous ) case statement */
if ( cmd = = RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE )
{
if ( runloop_st - > core_options )
* ( bool * ) data = runloop_st - > core_options - > updated ;
else
* ( bool * ) data = false ;
return true ;
}
switch ( cmd )
{
case RETRO_ENVIRONMENT_GET_OVERSCAN :
{
bool video_crop_overscan = settings - > bools . video_crop_overscan ;
* ( bool * ) data = ! video_crop_overscan ;
RARCH_LOG ( " [Environ]: GET_OVERSCAN: %u \n " ,
( unsigned ) ! video_crop_overscan ) ;
}
break ;
case RETRO_ENVIRONMENT_GET_CAN_DUPE :
* ( bool * ) data = true ;
RARCH_LOG ( " [Environ]: GET_CAN_DUPE: true \n " ) ;
break ;
case RETRO_ENVIRONMENT_GET_VARIABLE :
{
unsigned log_level = settings - > uints . libretro_log_level ;
struct retro_variable * var = ( struct retro_variable * ) data ;
size_t opt_idx ;
if ( ! var )
return true ;
var - > value = NULL ;
if ( ! runloop_st - > core_options )
{
RARCH_LOG ( " [Environ]: GET_VARIABLE %s: not implemented. \n " ,
var - > key ) ;
return true ;
}
# ifdef HAVE_RUNAHEAD
if ( runloop_st - > core_options - > updated )
runloop_st - > has_variable_update = true ;
# endif
runloop_st - > core_options - > updated = false ;
if ( core_option_manager_get_idx ( runloop_st - > core_options ,
var - > key , & opt_idx ) )
var - > value = core_option_manager_get_val (
runloop_st - > core_options , opt_idx ) ;
if ( log_level = = RETRO_LOG_DEBUG )
{
char s [ 128 ] ;
s [ 0 ] = ' \0 ' ;
snprintf ( s , sizeof ( s ) , " [Environ]: GET_VARIABLE %s: \n \t %s \n " ,
var - > key , var - > value ? var - > value :
msg_hash_to_str ( MENU_ENUM_LABEL_VALUE_NOT_AVAILABLE ) ) ;
RARCH_LOG ( s ) ;
}
}
break ;
case RETRO_ENVIRONMENT_SET_VARIABLE :
{
unsigned log_level = settings - > uints . libretro_log_level ;
const struct retro_variable * var = ( const struct retro_variable * ) data ;
size_t opt_idx ;
size_t val_idx ;
/* If core passes NULL to the callback, return
* value indicates whether callback is supported */
if ( ! var )
return true ;
if ( string_is_empty ( var - > key ) | |
string_is_empty ( var - > value ) )
return false ;
if ( ! runloop_st - > core_options )
{
RARCH_LOG ( " [Environ]: SET_VARIABLE %s: not implemented. \n " ,
var - > key ) ;
return false ;
}
/* Check whether key is valid */
if ( ! core_option_manager_get_idx ( runloop_st - > core_options ,
var - > key , & opt_idx ) )
{
RARCH_LOG ( " [Environ]: SET_VARIABLE %s: invalid key. \n " ,
var - > key ) ;
return false ;
}
/* Check whether value is valid */
if ( ! core_option_manager_get_val_idx ( runloop_st - > core_options ,
opt_idx , var - > value , & val_idx ) )
{
RARCH_LOG ( " [Environ]: SET_VARIABLE %s: invalid value: %s \n " ,
var - > key , var - > value ) ;
return false ;
}
/* Update option value if core-requested value
* is not currently set */
if ( val_idx ! = runloop_st - > core_options - > opts [ opt_idx ] . index )
core_option_manager_set_val ( runloop_st - > core_options ,
opt_idx , val_idx , true ) ;
if ( log_level = = RETRO_LOG_DEBUG )
RARCH_LOG ( " [Environ]: SET_VARIABLE %s: \n \t %s \n " ,
var - > key , var - > value ) ;
}
break ;
/* SET_VARIABLES: Legacy path */
case RETRO_ENVIRONMENT_SET_VARIABLES :
RARCH_LOG ( " [Environ]: SET_VARIABLES. \n " ) ;
{
core_option_manager_t * new_vars = NULL ;
if ( runloop_st - > core_options )
{
runloop_deinit_core_options (
runloop_st - > game_options_active ,
path_get ( RARCH_PATH_CORE_OPTIONS ) ,
runloop_st - > core_options ) ;
runloop_st - > game_options_active = false ;
runloop_st - > folder_options_active = false ;
runloop_st - > core_options = NULL ;
}
if ( ( new_vars = runloop_init_core_variables (
settings ,
( const struct retro_variable * ) data ) ) )
runloop_st - > core_options = new_vars ;
}
break ;
case RETRO_ENVIRONMENT_SET_CORE_OPTIONS :
RARCH_LOG ( " [Environ]: SET_CORE_OPTIONS. \n " ) ;
{
/* Parse core_option_definition array to
* create retro_core_options_v2 struct */
struct retro_core_options_v2 * options_v2 =
core_option_manager_convert_v1 (
( const struct retro_core_option_definition * ) data ) ;
if ( runloop_st - > core_options )
{
runloop_deinit_core_options (
runloop_st - > game_options_active ,
path_get ( RARCH_PATH_CORE_OPTIONS ) ,
runloop_st - > core_options ) ;
runloop_st - > game_options_active = false ;
runloop_st - > folder_options_active = false ;
runloop_st - > core_options = NULL ;
}
if ( options_v2 )
{
/* Initialise core options */
core_option_manager_t * new_vars = runloop_init_core_options ( settings , options_v2 ) ;
if ( new_vars )
runloop_st - > core_options = new_vars ;
/* Clean up */
core_option_manager_free_converted ( options_v2 ) ;
}
}
break ;
case RETRO_ENVIRONMENT_SET_CORE_OPTIONS_INTL :
RARCH_LOG ( " [Environ]: RETRO_ENVIRONMENT_SET_CORE_OPTIONS_INTL. \n " ) ;
{
/* Parse core_options_intl to create
* retro_core_options_v2 struct */
struct retro_core_options_v2 * options_v2 =
core_option_manager_convert_v1_intl (
( const struct retro_core_options_intl * ) data ) ;
if ( runloop_st - > core_options )
{
runloop_deinit_core_options (
runloop_st - > game_options_active ,
path_get ( RARCH_PATH_CORE_OPTIONS ) ,
runloop_st - > core_options ) ;
runloop_st - > game_options_active = false ;
runloop_st - > folder_options_active = false ;
runloop_st - > core_options = NULL ;
}
if ( options_v2 )
{
/* Initialise core options */
core_option_manager_t * new_vars = runloop_init_core_options ( settings , options_v2 ) ;
if ( new_vars )
runloop_st - > core_options = new_vars ;
/* Clean up */
core_option_manager_free_converted ( options_v2 ) ;
}
}
break ;
case RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2 :
RARCH_LOG ( " [Environ]: RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2. \n " ) ;
{
core_option_manager_t * new_vars = NULL ;
const struct retro_core_options_v2 * options_v2 =
( const struct retro_core_options_v2 * ) data ;
bool categories_enabled =
settings - > bools . core_option_category_enable ;
if ( runloop_st - > core_options )
{
runloop_deinit_core_options (
runloop_st - > game_options_active ,
path_get ( RARCH_PATH_CORE_OPTIONS ) ,
runloop_st - > core_options ) ;
runloop_st - > game_options_active = false ;
runloop_st - > folder_options_active = false ;
runloop_st - > core_options = NULL ;
}
if ( options_v2 )
{
new_vars = runloop_init_core_options ( settings , options_v2 ) ;
if ( new_vars )
runloop_st - > core_options = new_vars ;
}
/* Return value does not indicate success.
* Callback returns ' true ' if core option
* categories are supported / enabled ,
* otherwise ' false ' . */
return categories_enabled ;
}
break ;
case RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2_INTL :
RARCH_LOG ( " [Environ]: RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2_INTL. \n " ) ;
{
/* Parse retro_core_options_v2_intl to create
* retro_core_options_v2 struct */
core_option_manager_t * new_vars = NULL ;
struct retro_core_options_v2 * options_v2 =
core_option_manager_convert_v2_intl (
( const struct retro_core_options_v2_intl * ) data ) ;
bool categories_enabled =
settings - > bools . core_option_category_enable ;
if ( runloop_st - > core_options )
{
runloop_deinit_core_options (
runloop_st - > game_options_active ,
path_get ( RARCH_PATH_CORE_OPTIONS ) ,
runloop_st - > core_options ) ;
runloop_st - > game_options_active = false ;
runloop_st - > folder_options_active = false ;
runloop_st - > core_options = NULL ;
}
if ( options_v2 )
{
/* Initialise core options */
new_vars = runloop_init_core_options ( settings , options_v2 ) ;
if ( new_vars )
runloop_st - > core_options = new_vars ;
/* Clean up */
core_option_manager_free_converted ( options_v2 ) ;
}
/* Return value does not indicate success.
* Callback returns ' true ' if core option
* categories are supported / enabled ,
* otherwise ' false ' . */
return categories_enabled ;
}
break ;
case RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY :
RARCH_DBG ( " [Environ]: RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY. \n " ) ;
{
const struct retro_core_option_display * core_options_display =
( const struct retro_core_option_display * ) data ;
if ( runloop_st - > core_options & & core_options_display )
core_option_manager_set_visible (
runloop_st - > core_options ,
core_options_display - > key ,
core_options_display - > visible ) ;
}
break ;
case RETRO_ENVIRONMENT_SET_CORE_OPTIONS_UPDATE_DISPLAY_CALLBACK :
RARCH_DBG ( " [Environ]: RETRO_ENVIRONMENT_SET_CORE_OPTIONS_UPDATE_DISPLAY_CALLBACK. \n " ) ;
{
const struct retro_core_options_update_display_callback
* update_display_callback =
( const struct retro_core_options_update_display_callback * ) data ;
if ( update_display_callback & &
update_display_callback - > callback )
runloop_st - > core_options_callback . update_display =
update_display_callback - > callback ;
else
runloop_st - > core_options_callback . update_display = NULL ;
}
break ;
case RETRO_ENVIRONMENT_GET_MESSAGE_INTERFACE_VERSION :
RARCH_LOG ( " [Environ]: GET_MESSAGE_INTERFACE_VERSION. \n " ) ;
/* Current API version is 1 */
* ( unsigned * ) data = 1 ;
break ;
case RETRO_ENVIRONMENT_SET_MESSAGE :
{
const struct retro_message * msg = ( const struct retro_message * ) data ;
RARCH_LOG ( " [Environ]: SET_MESSAGE: %s \n " , msg - > msg ) ;
# if defined(HAVE_GFX_WIDGETS)
if ( dispwidget_get_ptr ( ) - > active )
gfx_widget_set_libretro_message (
msg - > msg ,
roundf ( ( float ) msg - > frames / 60.0f * 1000.0f ) ) ;
else
# endif
runloop_msg_queue_push ( msg - > msg , 3 , msg - > frames ,
true , NULL , MESSAGE_QUEUE_ICON_DEFAULT ,
MESSAGE_QUEUE_CATEGORY_INFO ) ;
break ;
}
case RETRO_ENVIRONMENT_SET_MESSAGE_EXT :
{
const struct retro_message_ext * msg =
( const struct retro_message_ext * ) data ;
/* Log message, if required */
if ( msg - > target ! = RETRO_MESSAGE_TARGET_OSD )
{
settings_t * settings = config_get_ptr ( ) ;
unsigned log_level = settings - > uints . frontend_log_level ;
switch ( msg - > level )
{
case RETRO_LOG_DEBUG :
if ( log_level = = RETRO_LOG_DEBUG )
RARCH_LOG ( " [Environ]: SET_MESSAGE_EXT: %s \n " , msg - > msg ) ;
break ;
case RETRO_LOG_WARN :
RARCH_WARN ( " [Environ]: SET_MESSAGE_EXT: %s \n " , msg - > msg ) ;
break ;
case RETRO_LOG_ERROR :
RARCH_ERR ( " [Environ]: SET_MESSAGE_EXT: %s \n " , msg - > msg ) ;
break ;
case RETRO_LOG_INFO :
default :
RARCH_LOG ( " [Environ]: SET_MESSAGE_EXT: %s \n " , msg - > msg ) ;
break ;
}
}
/* Display message via OSD, if required */
if ( msg - > target ! = RETRO_MESSAGE_TARGET_LOG )
{
switch ( msg - > type )
{
/* Handle 'status' messages */
case RETRO_MESSAGE_TYPE_STATUS :
/* Note: We need to lock a mutex here. Strictly
* speaking , ' core_status_msg ' is not part
* of the message queue , but :
* - It may be implemented as a queue in the future
* - It seems unnecessary to create a new slock_t
* object for this type of message when
* _runloop_msg_queue_lock is already available
* We therefore just call runloop_msg_queue_lock ( ) /
* runloop_msg_queue_unlock ( ) in this case */
RUNLOOP_MSG_QUEUE_LOCK ( runloop_st ) ;
/* If a message is already set, only overwrite
* it if the new message has the same or higher
* priority */
if ( ! runloop_st - > core_status_msg . set | |
( runloop_st - > core_status_msg . priority < = msg - > priority ) )
{
if ( ! string_is_empty ( msg - > msg ) )
{
strlcpy ( runloop_st - > core_status_msg . str , msg - > msg ,
sizeof ( runloop_st - > core_status_msg . str ) ) ;
runloop_st - > core_status_msg . duration = ( float ) msg - > duration ;
runloop_st - > core_status_msg . set = true ;
}
else
{
/* Ensure sane behaviour if core sends an
* empty message */
runloop_st - > core_status_msg . str [ 0 ] = ' \0 ' ;
runloop_st - > core_status_msg . priority = 0 ;
runloop_st - > core_status_msg . duration = 0.0f ;
runloop_st - > core_status_msg . set = false ;
}
}
RUNLOOP_MSG_QUEUE_UNLOCK ( runloop_st ) ;
break ;
# if defined(HAVE_GFX_WIDGETS)
/* Handle 'alternate' non-queued notifications */
case RETRO_MESSAGE_TYPE_NOTIFICATION_ALT :
{
video_driver_state_t * video_st =
video_state_get_ptr ( ) ;
if ( dispwidget_get_ptr ( ) - > active )
gfx_widget_set_libretro_message (
msg - > msg , msg - > duration ) ;
else
runloop_core_msg_queue_push (
& video_st - > av_info , msg ) ;
}
break ;
/* Handle 'progress' messages */
case RETRO_MESSAGE_TYPE_PROGRESS :
{
video_driver_state_t * video_st =
video_state_get_ptr ( ) ;
if ( dispwidget_get_ptr ( ) - > active )
gfx_widget_set_progress_message (
msg - > msg , msg - > duration ,
msg - > priority , msg - > progress ) ;
else
runloop_core_msg_queue_push (
& video_st - > av_info , msg ) ;
}
break ;
# endif
/* Handle standard (queued) notifications */
case RETRO_MESSAGE_TYPE_NOTIFICATION :
default :
{
video_driver_state_t * video_st =
video_state_get_ptr ( ) ;
runloop_core_msg_queue_push (
& video_st - > av_info , msg ) ;
}
break ;
}
}
break ;
}
case RETRO_ENVIRONMENT_SET_ROTATION :
{
unsigned rotation = * ( const unsigned * ) data ;
bool video_allow_rotate = settings - > bools . video_allow_rotate ;
RARCH_LOG ( " [Environ]: SET_ROTATION: %u \n " , rotation ) ;
if ( ! video_allow_rotate )
return false ;
if ( system )
system - > rotation = rotation ;
if ( ! video_driver_set_rotation ( rotation ) )
return false ;
break ;
}
case RETRO_ENVIRONMENT_SHUTDOWN :
RARCH_LOG ( " [Environ]: SHUTDOWN. \n " ) ;
/* This case occurs when a core (internally) requests
* a shutdown event . Must save runtime log file here ,
* since normal command . c CMD_EVENT_CORE_DEINIT event
* will not occur until after the current content has
* been cleared ( causing log to be skipped ) */
runloop_runtime_log_deinit ( runloop_st ,
settings - > bools . content_runtime_log ,
settings - > bools . content_runtime_log_aggregate ,
settings - > paths . directory_runtime_log ,
settings - > paths . directory_playlist ) ;
/* Similarly, since the CMD_EVENT_CORE_DEINIT will
* be called * after * the runloop state has been
* cleared , must also perform the following actions
* here :
* - Disable any active config overrides
* - Unload any active input remaps */
# ifdef HAVE_CONFIGFILE
if ( runloop_st - > overrides_active )
{
/* Reload the original config */
config_unload_override ( ) ;
runloop_st - > overrides_active = false ;
}
# endif
if ( runloop_st - > remaps_core_active
| | runloop_st - > remaps_content_dir_active
| | runloop_st - > remaps_game_active
)
{
input_remapping_deinit ( ) ;
input_remapping_set_defaults ( true ) ;
}
else
input_remapping_restore_global_config ( true ) ;
runloop_st - > shutdown_initiated = true ;
runloop_st - > core_shutdown_initiated = true ;
break ;
case RETRO_ENVIRONMENT_SET_PERFORMANCE_LEVEL :
if ( system )
{
system - > performance_level = * ( const unsigned * ) data ;
RARCH_LOG ( " [Environ]: PERFORMANCE_LEVEL: %u. \n " ,
system - > performance_level ) ;
}
break ;
case RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY :
{
const char * dir_system = settings - > paths . directory_system ;
bool systemfiles_in_content_dir = settings - > bools . systemfiles_in_content_dir ;
if ( string_is_empty ( dir_system ) | | systemfiles_in_content_dir )
{
const char * fullpath = path_get ( RARCH_PATH_CONTENT ) ;
if ( ! string_is_empty ( fullpath ) )
{
char temp_path [ PATH_MAX_LENGTH ] ;
temp_path [ 0 ] = ' \0 ' ;
if ( string_is_empty ( dir_system ) )
RARCH_WARN ( " [Environ]: SYSTEM DIR is empty, assume CONTENT DIR %s \n " ,
fullpath ) ;
fill_pathname_basedir ( temp_path , fullpath , sizeof ( temp_path ) ) ;
dir_set ( RARCH_DIR_SYSTEM , temp_path ) ;
}
* ( const char * * ) data = dir_get_ptr ( RARCH_DIR_SYSTEM ) ;
RARCH_LOG ( " [Environ]: SYSTEM_DIRECTORY: \" %s \" . \n " ,
dir_system ) ;
}
else
{
* ( const char * * ) data = dir_system ;
RARCH_LOG ( " [Environ]: SYSTEM_DIRECTORY: \" %s \" . \n " ,
dir_system ) ;
}
}
break ;
case RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY :
RARCH_LOG ( " [Environ]: GET_SAVE_DIRECTORY. \n " ) ;
* ( const char * * ) data = runloop_st - > savefile_dir ;
break ;
case RETRO_ENVIRONMENT_GET_USERNAME :
* ( const char * * ) data = * settings - > paths . username ?
settings - > paths . username : NULL ;
RARCH_LOG ( " [Environ]: GET_USERNAME: \" %s \" . \n " ,
settings - > paths . username ) ;
break ;
case RETRO_ENVIRONMENT_GET_LANGUAGE :
# ifdef HAVE_LANGEXTRA
{
unsigned user_lang = * msg_hash_get_uint ( MSG_HASH_USER_LANGUAGE ) ;
* ( unsigned * ) data = user_lang ;
RARCH_LOG ( " [Environ]: GET_LANGUAGE: \" %u \" . \n " , user_lang ) ;
}
# endif
break ;
case RETRO_ENVIRONMENT_SET_PIXEL_FORMAT :
{
video_driver_state_t * video_st =
video_state_get_ptr ( ) ;
enum retro_pixel_format pix_fmt =
* ( const enum retro_pixel_format * ) data ;
switch ( pix_fmt )
{
case RETRO_PIXEL_FORMAT_0RGB1555 :
RARCH_LOG ( " [Environ]: SET_PIXEL_FORMAT: 0RGB1555. \n " ) ;
break ;
case RETRO_PIXEL_FORMAT_RGB565 :
RARCH_LOG ( " [Environ]: SET_PIXEL_FORMAT: RGB565. \n " ) ;
break ;
case RETRO_PIXEL_FORMAT_XRGB8888 :
RARCH_LOG ( " [Environ]: SET_PIXEL_FORMAT: XRGB8888. \n " ) ;
break ;
default :
return false ;
}
video_st - > pix_fmt = pix_fmt ;
break ;
}
case RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS :
{
static const char * libretro_btn_desc [ ] = {
" B (bottom) " , " Y (left) " , " Select " , " Start " ,
" D-Pad Up " , " D-Pad Down " , " D-Pad Left " , " D-Pad Right " ,
" A (right) " , " X (up) " ,
" L " , " R " , " L2 " , " R2 " , " L3 " , " R3 " ,
} ;
if ( system )
{
unsigned retro_id ;
const struct retro_input_descriptor * desc = NULL ;
memset ( ( void * ) & system - > input_desc_btn , 0 ,
sizeof ( system - > input_desc_btn ) ) ;
desc = ( const struct retro_input_descriptor * ) data ;
for ( ; desc - > description ; desc + + )
{
unsigned retro_port = desc - > port ;
retro_id = desc - > id ;
if ( desc - > port > = MAX_USERS )
continue ;
if ( desc - > id > = RARCH_FIRST_CUSTOM_BIND )
continue ;
switch ( desc - > device )
{
case RETRO_DEVICE_JOYPAD :
system - > input_desc_btn [ retro_port ]
[ retro_id ] = desc - > description ;
break ;
case RETRO_DEVICE_ANALOG :
switch ( retro_id )
{
case RETRO_DEVICE_ID_ANALOG_X :
switch ( desc - > index )
{
case RETRO_DEVICE_INDEX_ANALOG_LEFT :
system - > input_desc_btn [ retro_port ]
[ RARCH_ANALOG_LEFT_X_PLUS ] = desc - > description ;
system - > input_desc_btn [ retro_port ]
[ RARCH_ANALOG_LEFT_X_MINUS ] = desc - > description ;
break ;
case RETRO_DEVICE_INDEX_ANALOG_RIGHT :
system - > input_desc_btn [ retro_port ]
[ RARCH_ANALOG_RIGHT_X_PLUS ] = desc - > description ;
system - > input_desc_btn [ retro_port ]
[ RARCH_ANALOG_RIGHT_X_MINUS ] = desc - > description ;
break ;
}
break ;
case RETRO_DEVICE_ID_ANALOG_Y :
switch ( desc - > index )
{
case RETRO_DEVICE_INDEX_ANALOG_LEFT :
system - > input_desc_btn [ retro_port ]
[ RARCH_ANALOG_LEFT_Y_PLUS ] = desc - > description ;
system - > input_desc_btn [ retro_port ]
[ RARCH_ANALOG_LEFT_Y_MINUS ] = desc - > description ;
break ;
case RETRO_DEVICE_INDEX_ANALOG_RIGHT :
system - > input_desc_btn [ retro_port ]
[ RARCH_ANALOG_RIGHT_Y_PLUS ] = desc - > description ;
system - > input_desc_btn [ retro_port ]
[ RARCH_ANALOG_RIGHT_Y_MINUS ] = desc - > description ;
break ;
}
break ;
}
break ;
}
}
RARCH_LOG ( " [Environ]: SET_INPUT_DESCRIPTORS: \n " ) ;
{
unsigned log_level = settings - > uints . libretro_log_level ;
if ( log_level = = RETRO_LOG_DEBUG )
{
unsigned input_driver_max_users =
settings - > uints . input_max_users ;
for ( p = 0 ; p < input_driver_max_users ; p + + )
{
unsigned mapped_port = settings - > uints . input_remap_ports [ p ] ;
for ( retro_id = 0 ; retro_id < RARCH_FIRST_CUSTOM_BIND ; retro_id + + )
{
const char * description = system - > input_desc_btn [ mapped_port ] [ retro_id ] ;
if ( ! description )
continue ;
RARCH_LOG ( " \t RetroPad, Port %u, Button \" %s \" => \" %s \" \n " ,
p + 1 , libretro_btn_desc [ retro_id ] , description ) ;
}
}
}
}
runloop_st - > current_core . has_set_input_descriptors = true ;
}
break ;
}
case RETRO_ENVIRONMENT_SET_KEYBOARD_CALLBACK :
{
input_driver_state_t
* input_st = input_state_get_ptr ( ) ;
const struct retro_keyboard_callback * info =
( const struct retro_keyboard_callback * ) data ;
retro_keyboard_event_t * frontend_key_event = & runloop_st - > frontend_key_event ;
retro_keyboard_event_t * key_event = & runloop_st - > key_event ;
RARCH_LOG ( " [Environ]: SET_KEYBOARD_CALLBACK. \n " ) ;
if ( key_event )
* key_event = info - > callback ;
if ( frontend_key_event & & key_event )
* frontend_key_event = * key_event ;
/* If a core calls RETRO_ENVIRONMENT_SET_KEYBOARD_CALLBACK,
* then it is assumed that game focus mode is desired */
input_st - > game_focus_state . core_requested = true ;
break ;
}
case RETRO_ENVIRONMENT_GET_DISK_CONTROL_INTERFACE_VERSION :
RARCH_LOG ( " [Environ]: GET_DISK_CONTROL_INTERFACE_VERSION. \n " ) ;
/* Current API version is 1 */
* ( unsigned * ) data = 1 ;
break ;
case RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE :
{
const struct retro_disk_control_callback * control_cb =
( const struct retro_disk_control_callback * ) data ;
if ( system )
{
RARCH_LOG ( " [Environ]: SET_DISK_CONTROL_INTERFACE. \n " ) ;
disk_control_set_callback ( & system - > disk_control , control_cb ) ;
}
}
break ;
case RETRO_ENVIRONMENT_SET_DISK_CONTROL_EXT_INTERFACE :
{
const struct retro_disk_control_ext_callback * control_cb =
( const struct retro_disk_control_ext_callback * ) data ;
if ( system )
{
RARCH_LOG ( " [Environ]: SET_DISK_CONTROL_EXT_INTERFACE. \n " ) ;
disk_control_set_ext_callback ( & system - > disk_control , control_cb ) ;
}
}
break ;
case RETRO_ENVIRONMENT_GET_PREFERRED_HW_RENDER :
{
unsigned * cb = ( unsigned * ) data ;
settings_t * settings = config_get_ptr ( ) ;
const char * video_driver_name = settings - > arrays . video_driver ;
bool driver_switch_enable = settings - > bools . driver_switch_enable ;
RARCH_LOG ( " [Environ]: GET_PREFERRED_HW_RENDER, video driver name: %s. \n " , video_driver_name ) ;
if ( string_is_equal ( video_driver_name , " glcore " ) )
{
* cb = RETRO_HW_CONTEXT_OPENGL_CORE ;
RARCH_LOG ( " [Environ]: GET_PREFERRED_HW_RENDER - Context callback set to RETRO_HW_CONTEXT_OPENGL_CORE. \n " ) ;
}
else if ( string_is_equal ( video_driver_name , " gl " ) )
{
* cb = RETRO_HW_CONTEXT_OPENGL ;
RARCH_LOG ( " [Environ]: GET_PREFERRED_HW_RENDER - Context callback set to RETRO_HW_CONTEXT_OPENGL. \n " ) ;
}
else if ( string_is_equal ( video_driver_name , " vulkan " ) )
{
* cb = RETRO_HW_CONTEXT_VULKAN ;
RARCH_LOG ( " [Environ]: GET_PREFERRED_HW_RENDER - Context callback set to RETRO_HW_CONTEXT_VULKAN. \n " ) ;
}
else if ( ! strncmp ( video_driver_name , " d3d " , 3 ) )
{
* cb = RETRO_HW_CONTEXT_DIRECT3D ;
RARCH_LOG ( " [Environ]: GET_PREFERRED_HW_RENDER - Context callback set to RETRO_HW_CONTEXT_DIRECT3D. \n " ) ;
}
else
{
* cb = RETRO_HW_CONTEXT_NONE ;
RARCH_LOG ( " [Environ]: GET_PREFERRED_HW_RENDER - Context callback set to RETRO_HW_CONTEXT_NONE. \n " ) ;
}
if ( ! driver_switch_enable )
{
RARCH_LOG ( " [Environ]: Driver switching disabled, GET_PREFERRED_HW_RENDER will be ignored. \n " ) ;
return false ;
}
break ;
}
case RETRO_ENVIRONMENT_SET_HW_RENDER :
case RETRO_ENVIRONMENT_SET_HW_RENDER | RETRO_ENVIRONMENT_EXPERIMENTAL :
{
settings_t * settings = config_get_ptr ( ) ;
struct retro_hw_render_callback * cb =
( struct retro_hw_render_callback * ) data ;
video_driver_state_t * video_st =
video_state_get_ptr ( ) ;
struct retro_hw_render_callback * hwr =
VIDEO_DRIVER_GET_HW_CONTEXT_INTERNAL ( video_st ) ;
if ( ! cb )
{
RARCH_ERR ( " [Environ]: SET_HW_RENDER - No valid callback passed, returning... \n " ) ;
return false ;
}
RARCH_LOG ( " [Environ]: SET_HW_RENDER, context type: %s. \n " , hw_render_context_name ( cb - > context_type , cb - > version_major , cb - > version_minor ) ) ;
if ( ! dynamic_request_hw_context (
cb - > context_type , cb - > version_minor , cb - > version_major ) )
{
RARCH_ERR ( " [Environ]: SET_HW_RENDER - Dynamic request HW context failed. \n " ) ;
return false ;
}
if ( ! dynamic_verify_hw_context (
settings - > arrays . video_driver ,
settings - > bools . driver_switch_enable ,
cb - > context_type , cb - > version_minor , cb - > version_major ) )
{
RARCH_ERR ( " [Environ]: SET_HW_RENDER: Dynamic verify HW context failed. \n " ) ;
return false ;
}
# if defined(HAVE_OPENGL) || defined(HAVE_OPENGL_CORE)
/* TODO/FIXME - should check first if an OpenGL
* driver is running */
if ( cb - > context_type = = RETRO_HW_CONTEXT_OPENGL_CORE )
{
/* Ensure that the rest of the frontend knows
* we have a core context */
gfx_ctx_flags_t flags ;
flags . flags = 0 ;
BIT32_SET ( flags . flags , GFX_CTX_FLAGS_GL_CORE_CONTEXT ) ;
video_context_driver_set_flags ( & flags ) ;
}
# endif
cb - > get_current_framebuffer = video_driver_get_current_framebuffer ;
cb - > get_proc_address = video_driver_get_proc_address ;
/* Old ABI. Don't copy garbage. */
if ( cmd & RETRO_ENVIRONMENT_EXPERIMENTAL )
{
memcpy ( hwr ,
cb , offsetof ( struct retro_hw_render_callback , stencil ) ) ;
memset ( ( uint8_t * ) hwr + offsetof ( struct retro_hw_render_callback , stencil ) ,
0 , sizeof ( * cb ) - offsetof ( struct retro_hw_render_callback , stencil ) ) ;
}
else
memcpy ( hwr , cb , sizeof ( * cb ) ) ;
RARCH_LOG ( " Reached end of SET_HW_RENDER. \n " ) ;
break ;
}
case RETRO_ENVIRONMENT_SET_SUPPORT_NO_GAME :
{
bool state = * ( const bool * ) data ;
RARCH_LOG ( " [Environ]: SET_SUPPORT_NO_GAME: %s. \n " , state ? " yes " : " no " ) ;
if ( state )
content_set_does_not_need_content ( ) ;
else
content_unset_does_not_need_content ( ) ;
break ;
}
case RETRO_ENVIRONMENT_GET_LIBRETRO_PATH :
{
const char * * path = ( const char * * ) data ;
RARCH_LOG ( " [Environ]: GET_LIBRETRO_PATH. \n " ) ;
# ifdef HAVE_DYNAMIC
* path = path_get ( RARCH_PATH_CORE ) ;
# else
* path = NULL ;
# endif
break ;
}
case RETRO_ENVIRONMENT_SET_AUDIO_CALLBACK :
# ifdef HAVE_THREADS
{
recording_state_t
* recording_st = recording_state_get_ptr ( ) ;
audio_driver_state_t
* audio_st = audio_state_get_ptr ( ) ;
const struct
retro_audio_callback * cb = ( const struct retro_audio_callback * ) data ;
RARCH_LOG ( " [Environ]: SET_AUDIO_CALLBACK. \n " ) ;
# ifdef HAVE_NETWORKING
if ( netplay_driver_ctl ( RARCH_NETPLAY_CTL_IS_ENABLED , NULL ) )
return false ;
# endif
if ( recording_st - > data ) /* A/V sync is a must. */
return false ;
if ( cb )
audio_st - > callback = * cb ;
}
break ;
# else
return false ;
# endif
case RETRO_ENVIRONMENT_SET_FRAME_TIME_CALLBACK :
{
const struct retro_frame_time_callback * info =
( const struct retro_frame_time_callback * ) data ;
RARCH_LOG ( " [Environ]: SET_FRAME_TIME_CALLBACK. \n " ) ;
# ifdef HAVE_NETWORKING
/* retro_run() will be called in very strange and
* mysterious ways , have to disable it . */
if ( netplay_driver_ctl ( RARCH_NETPLAY_CTL_IS_ENABLED , NULL ) )
return false ;
# endif
runloop_st - > frame_time = * info ;
break ;
}
case RETRO_ENVIRONMENT_SET_AUDIO_BUFFER_STATUS_CALLBACK :
{
const struct retro_audio_buffer_status_callback * info =
( const struct retro_audio_buffer_status_callback * ) data ;
RARCH_LOG ( " [Environ]: SET_AUDIO_BUFFER_STATUS_CALLBACK. \n " ) ;
if ( info )
runloop_st - > audio_buffer_status . callback = info - > callback ;
else
runloop_st - > audio_buffer_status . callback = NULL ;
break ;
}
case RETRO_ENVIRONMENT_SET_MINIMUM_AUDIO_LATENCY :
{
unsigned audio_latency_default = settings - > uints . audio_latency ;
unsigned audio_latency_current =
( runloop_st - > audio_latency > audio_latency_default ) ?
runloop_st - > audio_latency : audio_latency_default ;
unsigned audio_latency_new ;
RARCH_LOG ( " [Environ]: RETRO_ENVIRONMENT_SET_MINIMUM_AUDIO_LATENCY. \n " ) ;
/* Sanitise input latency value */
runloop_st - > audio_latency = 0 ;
if ( data )
runloop_st - > audio_latency = * ( const unsigned * ) data ;
if ( runloop_st - > audio_latency > 512 )
{
RARCH_WARN ( " [Environ]: Requested audio latency of %u ms - limiting to maximum of 512 ms. \n " ,
runloop_st - > audio_latency ) ;
runloop_st - > audio_latency = 512 ;
}
/* Determine new set-point latency value */
if ( runloop_st - > audio_latency > = audio_latency_default )
audio_latency_new = runloop_st - > audio_latency ;
else
{
if ( runloop_st - > audio_latency ! = 0 )
RARCH_WARN ( " [Environ]: Requested audio latency of %u ms is less than frontend default of %u ms. "
" Using frontend default... \n " ,
runloop_st - > audio_latency , audio_latency_default ) ;
audio_latency_new = audio_latency_default ;
}
/* Check whether audio driver requires reinitialisation
* ( Identical to RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO ,
* without video driver initialisation ) */
if ( audio_latency_new ! = audio_latency_current )
{
recording_state_t
* recording_st = recording_state_get_ptr ( ) ;
bool video_fullscreen = settings - > bools . video_fullscreen ;
int reinit_flags = DRIVERS_CMD_ALL &
~ ( DRIVER_VIDEO_MASK | DRIVER_INPUT_MASK | DRIVER_MENU_MASK ) ;
RARCH_LOG ( " [Environ]: Setting audio latency to %u ms. \n " , audio_latency_new ) ;
command_event ( CMD_EVENT_REINIT , & reinit_flags ) ;
video_driver_set_aspect_ratio ( ) ;
/* Cannot continue recording with different
* parameters .
* Take the easiest route out and just restart
* the recording . */
if ( recording_st - > data )
{
runloop_msg_queue_push (
msg_hash_to_str ( MSG_RESTARTING_RECORDING_DUE_TO_DRIVER_REINIT ) ,
2 , 180 , false ,
NULL , MESSAGE_QUEUE_ICON_DEFAULT , MESSAGE_QUEUE_CATEGORY_INFO ) ;
command_event ( CMD_EVENT_RECORD_DEINIT , NULL ) ;
command_event ( CMD_EVENT_RECORD_INIT , NULL ) ;
}
/* Hide mouse cursor in fullscreen mode */
if ( video_fullscreen )
video_driver_hide_mouse ( ) ;
}
break ;
}
case RETRO_ENVIRONMENT_GET_RUMBLE_INTERFACE :
{
struct retro_rumble_interface * iface =
( struct retro_rumble_interface * ) data ;
RARCH_LOG ( " [Environ]: GET_RUMBLE_INTERFACE. \n " ) ;
iface - > set_rumble_state = input_set_rumble_state ;
break ;
}
case RETRO_ENVIRONMENT_GET_INPUT_DEVICE_CAPABILITIES :
{
uint64_t * mask = ( uint64_t * ) data ;
input_driver_state_t
* input_st = input_state_get_ptr ( ) ;
RARCH_LOG ( " [Environ]: GET_INPUT_DEVICE_CAPABILITIES. \n " ) ;
if ( ! input_st - > current_driver - > get_capabilities | |
! input_st - > current_data )
return false ;
* mask = input_driver_get_capabilities ( ) ;
break ;
}
case RETRO_ENVIRONMENT_GET_SENSOR_INTERFACE :
{
settings_t * settings = config_get_ptr ( ) ;
bool input_sensors_enable = settings - > bools . input_sensors_enable ;
struct retro_sensor_interface * iface = ( struct retro_sensor_interface * ) data ;
RARCH_LOG ( " [Environ]: GET_SENSOR_INTERFACE. \n " ) ;
if ( ! input_sensors_enable )
return false ;
iface - > set_sensor_state = input_set_sensor_state ;
iface - > get_sensor_input = input_get_sensor_state ;
break ;
}
case RETRO_ENVIRONMENT_GET_CAMERA_INTERFACE :
{
struct retro_camera_callback * cb =
( struct retro_camera_callback * ) data ;
camera_driver_state_t * camera_st = camera_state_get_ptr ( ) ;
RARCH_LOG ( " [Environ]: GET_CAMERA_INTERFACE. \n " ) ;
cb - > start = driver_camera_start ;
cb - > stop = driver_camera_stop ;
camera_st - > cb = * cb ;
camera_st - > active = ( cb - > caps ! = 0 ) ;
break ;
}
case RETRO_ENVIRONMENT_GET_LOCATION_INTERFACE :
{
struct retro_location_callback * cb =
( struct retro_location_callback * ) data ;
location_driver_state_t
* location_st = location_state_get_ptr ( ) ;
RARCH_LOG ( " [Environ]: GET_LOCATION_INTERFACE. \n " ) ;
cb - > start = driver_location_start ;
cb - > stop = driver_location_stop ;
cb - > get_position = driver_location_get_position ;
cb - > set_interval = driver_location_set_interval ;
if ( system )
system - > location_cb = * cb ;
location_st - > active = false ;
break ;
}
case RETRO_ENVIRONMENT_GET_LOG_INTERFACE :
{
struct retro_log_callback * cb = ( struct retro_log_callback * ) data ;
RARCH_LOG ( " [Environ]: GET_LOG_INTERFACE. \n " ) ;
cb - > log = libretro_log_cb ;
break ;
}
case RETRO_ENVIRONMENT_GET_PERF_INTERFACE :
{
struct retro_perf_callback * cb = ( struct retro_perf_callback * ) data ;
RARCH_LOG ( " [Environ]: GET_PERF_INTERFACE. \n " ) ;
cb - > get_time_usec = cpu_features_get_time_usec ;
cb - > get_cpu_features = cpu_features_get ;
cb - > get_perf_counter = cpu_features_get_perf_counter ;
cb - > perf_register = runloop_performance_counter_register ;
cb - > perf_start = core_performance_counter_start ;
cb - > perf_stop = core_performance_counter_stop ;
cb - > perf_log = runloop_perf_log ;
break ;
}
case RETRO_ENVIRONMENT_GET_CORE_ASSETS_DIRECTORY :
{
const char * * dir = ( const char * * ) data ;
const char * dir_core_assets = settings - > paths . directory_core_assets ;
* dir = * dir_core_assets ?
dir_core_assets : NULL ;
RARCH_LOG ( " [Environ]: CORE_ASSETS_DIRECTORY: \" %s \" . \n " ,
dir_core_assets ) ;
break ;
}
case RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO :
/**
* Update the system Audio / Video information .
* Will reinitialize audio / video drivers if needed .
* Used by RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO .
* */
{
const struct retro_system_av_info * * info = ( const struct retro_system_av_info * * ) & data ;
video_driver_state_t * video_st = video_state_get_ptr ( ) ;
struct retro_system_av_info * av_info = & video_st - > av_info ;
if ( data )
{
int reinit_flags = DRIVERS_CMD_ALL ;
settings_t * settings = config_get_ptr ( ) ;
float refresh_rate = ( * info ) - > timing . fps ;
unsigned crt_switch_resolution = settings - > uints . crt_switch_resolution ;
bool video_fullscreen = settings - > bools . video_fullscreen ;
bool video_has_resolution_list = video_display_server_has_resolution_list ( ) ;
bool video_switch_refresh_rate = false ;
bool no_video_reinit = true ;
/* Refresh rate switch for regular displays */
if ( video_has_resolution_list )
{
float refresh_mod = 0.0f ;
float video_refresh_rate = settings - > floats . video_refresh_rate ;
unsigned video_swap_interval = settings - > uints . video_swap_interval ;
unsigned video_bfi = settings - > uints . video_black_frame_insertion ;
bool video_windowed_full = settings - > bools . video_windowed_fullscreen ;
bool vrr_runloop_enable = settings - > bools . vrr_runloop_enable ;
/* Roundings to PAL & NTSC standards */
refresh_rate = ( refresh_rate > 54 & & refresh_rate < 60 ) ? 59.94f : refresh_rate ;
refresh_rate = ( refresh_rate > 49 & & refresh_rate < 55 ) ? 50.00f : refresh_rate ;
/* Black frame insertion + swap interval multiplier */
refresh_mod = video_bfi + 1.0f ;
refresh_rate = ( refresh_rate * refresh_mod * video_swap_interval ) ;
/* Fallback when target refresh rate is not exposed */
if ( ! video_display_server_has_refresh_rate ( refresh_rate ) )
refresh_rate = ( 60.0f * refresh_mod * video_swap_interval ) ;
/* Store original refresh rate on automatic change, and
* restore it in deinit_core and main_quit , because not all
* cores announce refresh rate via SET_SYSTEM_AV_INFO */
if ( ! video_st - > video_refresh_rate_original )
video_st - > video_refresh_rate_original = video_refresh_rate ;
/* Try to switch display rate when:
* - Not already at correct rate
* - In exclusive fullscreen
* - ' CRT SwitchRes ' OFF & ' Sync to Exact Content Framerate ' OFF
*/
video_switch_refresh_rate = (
refresh_rate ! = video_refresh_rate & &
! crt_switch_resolution & & ! vrr_runloop_enable & &
video_fullscreen & & ! video_windowed_full ) ;
}
no_video_reinit = (
crt_switch_resolution = = 0
& & video_switch_refresh_rate = = false
& & data
& & ( ( * info ) - > geometry . max_width = = av_info - > geometry . max_width )
& & ( ( * info ) - > geometry . max_height = = av_info - > geometry . max_height ) ) ;
/* First set new refresh rate and display rate, then after REINIT do
* another display rate change to make sure the change stays */
if ( video_switch_refresh_rate )
{
video_monitor_set_refresh_rate ( refresh_rate ) ;
video_display_server_set_refresh_rate ( refresh_rate ) ;
}
/* When not doing video reinit, we also must not do input and menu
* reinit , otherwise the input driver crashes and the menu gets
* corrupted . */
if ( no_video_reinit )
reinit_flags =
DRIVERS_CMD_ALL &
~ ( DRIVER_VIDEO_MASK | DRIVER_INPUT_MASK |
DRIVER_MENU_MASK ) ;
RARCH_LOG ( " [Environ]: SET_SYSTEM_AV_INFO: %ux%u, aspect: %.3f, fps: %.3f, sample rate: %.2f Hz. \n " ,
( * info ) - > geometry . base_width , ( * info ) - > geometry . base_height ,
( * info ) - > geometry . aspect_ratio ,
( * info ) - > timing . fps ,
( * info ) - > timing . sample_rate ) ;
memcpy ( av_info , * info , sizeof ( * av_info ) ) ;
command_event ( CMD_EVENT_REINIT , & reinit_flags ) ;
if ( no_video_reinit )
video_driver_set_aspect_ratio ( ) ;
if ( video_switch_refresh_rate )
video_display_server_set_refresh_rate ( refresh_rate ) ;
/* Cannot continue recording with different parameters.
* Take the easiest route out and just restart
* the recording . */
if ( recording_st - > data )
{
runloop_msg_queue_push (
msg_hash_to_str ( MSG_RESTARTING_RECORDING_DUE_TO_DRIVER_REINIT ) ,
2 , 180 , false ,
NULL , MESSAGE_QUEUE_ICON_DEFAULT , MESSAGE_QUEUE_CATEGORY_INFO ) ;
command_event ( CMD_EVENT_RECORD_DEINIT , NULL ) ;
command_event ( CMD_EVENT_RECORD_INIT , NULL ) ;
}
/* Hide mouse cursor in fullscreen after
* a RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO call . */
if ( video_fullscreen )
video_driver_hide_mouse ( ) ;
/* Recalibrate frame delay target */
if ( settings - > bools . video_frame_delay_auto )
video_st - > frame_delay_target = 0 ;
return true ;
}
return false ;
}
case RETRO_ENVIRONMENT_SET_SUBSYSTEM_INFO :
{
unsigned i ;
const struct retro_subsystem_info * info =
( const struct retro_subsystem_info * ) data ;
unsigned log_level = settings - > uints . libretro_log_level ;
if ( log_level = = RETRO_LOG_DEBUG )
RARCH_LOG ( " [Environ]: SET_SUBSYSTEM_INFO. \n " ) ;
for ( i = 0 ; info [ i ] . ident ; i + + )
{
unsigned j ;
if ( log_level ! = RETRO_LOG_DEBUG )
continue ;
RARCH_LOG ( " Special game type: %s \n Ident: %s \n ID: %u \n Content: \n " ,
info [ i ] . desc ,
info [ i ] . ident ,
info [ i ] . id
) ;
for ( j = 0 ; j < info [ i ] . num_roms ; j + + )
{
RARCH_LOG ( " %s (%s) \n " ,
info [ i ] . roms [ j ] . desc , info [ i ] . roms [ j ] . required ?
" required " : " optional " ) ;
}
}
if ( system )
{
struct retro_subsystem_info * info_ptr = NULL ;
free ( system - > subsystem . data ) ;
system - > subsystem . data = NULL ;
system - > subsystem . size = 0 ;
info_ptr = ( struct retro_subsystem_info * )
malloc ( i * sizeof ( * info_ptr ) ) ;
if ( ! info_ptr )
return false ;
system - > subsystem . data = info_ptr ;
memcpy ( system - > subsystem . data , info ,
i * sizeof ( * system - > subsystem . data ) ) ;
system - > subsystem . size = i ;
runloop_st - > current_core . has_set_subsystems = true ;
}
break ;
}
case RETRO_ENVIRONMENT_SET_CONTROLLER_INFO :
{
unsigned i , j ;
const struct retro_controller_info * info =
( const struct retro_controller_info * ) data ;
unsigned log_level = settings - > uints . libretro_log_level ;
RARCH_LOG ( " [Environ]: SET_CONTROLLER_INFO. \n " ) ;
for ( i = 0 ; info [ i ] . types ; i + + )
{
if ( log_level ! = RETRO_LOG_DEBUG )
continue ;
RARCH_LOG ( " Controller port: %u \n " , i + 1 ) ;
for ( j = 0 ; j < info [ i ] . num_types ; j + + )
RARCH_LOG ( " %s (ID: %u) \n " , info [ i ] . types [ j ] . desc ,
info [ i ] . types [ j ] . id ) ;
}
if ( system )
{
struct retro_controller_info * info_ptr = NULL ;
free ( system - > ports . data ) ;
system - > ports . data = NULL ;
system - > ports . size = 0 ;
info_ptr = ( struct retro_controller_info * ) calloc ( i , sizeof ( * info_ptr ) ) ;
if ( ! info_ptr )
return false ;
system - > ports . data = info_ptr ;
memcpy ( system - > ports . data , info ,
i * sizeof ( * system - > ports . data ) ) ;
system - > ports . size = i ;
}
break ;
}
case RETRO_ENVIRONMENT_SET_MEMORY_MAPS :
{
if ( system )
{
unsigned i ;
const struct retro_memory_map * mmaps =
( const struct retro_memory_map * ) data ;
rarch_memory_descriptor_t * descriptors = NULL ;
RARCH_LOG ( " [Environ]: SET_MEMORY_MAPS. \n " ) ;
free ( ( void * ) system - > mmaps . descriptors ) ;
system - > mmaps . descriptors = 0 ;
system - > mmaps . num_descriptors = 0 ;
descriptors = ( rarch_memory_descriptor_t * )
calloc ( mmaps - > num_descriptors ,
sizeof ( * descriptors ) ) ;
if ( ! descriptors )
return false ;
system - > mmaps . descriptors = descriptors ;
system - > mmaps . num_descriptors = mmaps - > num_descriptors ;
for ( i = 0 ; i < mmaps - > num_descriptors ; i + + )
system - > mmaps . descriptors [ i ] . core = mmaps - > descriptors [ i ] ;
mmap_preprocess_descriptors ( descriptors , mmaps - > num_descriptors ) ;
# ifndef NDEBUG
if ( sizeof ( void * ) = = 8 )
RARCH_LOG ( " ndx flags ptr offset start select disconn len addrspace \n " ) ;
else
RARCH_LOG ( " ndx flags ptr offset start select disconn len addrspace \n " ) ;
# endif
for ( i = 0 ; i < system - > mmaps . num_descriptors ; i + + )
{
const rarch_memory_descriptor_t * desc =
& system - > mmaps . descriptors [ i ] ;
char flags [ 7 ] ;
flags [ 0 ] = ' M ' ;
if ( ( desc - > core . flags & RETRO_MEMDESC_MINSIZE_8 ) = = RETRO_MEMDESC_MINSIZE_8 )
flags [ 1 ] = ' 8 ' ;
else if ( ( desc - > core . flags & RETRO_MEMDESC_MINSIZE_4 ) = = RETRO_MEMDESC_MINSIZE_4 )
flags [ 1 ] = ' 4 ' ;
else if ( ( desc - > core . flags & RETRO_MEMDESC_MINSIZE_2 ) = = RETRO_MEMDESC_MINSIZE_2 )
flags [ 1 ] = ' 2 ' ;
else
flags [ 1 ] = ' 1 ' ;
flags [ 2 ] = ' A ' ;
if ( ( desc - > core . flags & RETRO_MEMDESC_ALIGN_8 ) = = RETRO_MEMDESC_ALIGN_8 )
flags [ 3 ] = ' 8 ' ;
else if ( ( desc - > core . flags & RETRO_MEMDESC_ALIGN_4 ) = = RETRO_MEMDESC_ALIGN_4 )
flags [ 3 ] = ' 4 ' ;
else if ( ( desc - > core . flags & RETRO_MEMDESC_ALIGN_2 ) = = RETRO_MEMDESC_ALIGN_2 )
flags [ 3 ] = ' 2 ' ;
else
flags [ 3 ] = ' 1 ' ;
flags [ 4 ] = ( desc - > core . flags & RETRO_MEMDESC_BIGENDIAN ) ? ' B ' : ' b ' ;
flags [ 5 ] = ( desc - > core . flags & RETRO_MEMDESC_CONST ) ? ' C ' : ' c ' ;
flags [ 6 ] = 0 ;
RARCH_LOG ( " %03u %s %p %08X %08X %08X %08X %08X %s \n " ,
i + 1 , flags , desc - > core . ptr , desc - > core . offset , desc - > core . start ,
desc - > core . select , desc - > core . disconnect , desc - > core . len ,
desc - > core . addrspace ? desc - > core . addrspace : " " ) ;
}
}
else
{
RARCH_WARN ( " [Environ]: SET_MEMORY_MAPS, but system pointer not initialized.. \n " ) ;
}
break ;
}
case RETRO_ENVIRONMENT_SET_GEOMETRY :
{
video_driver_state_t * video_st = video_state_get_ptr ( ) ;
struct retro_system_av_info * av_info = & video_st - > av_info ;
struct retro_game_geometry * geom = ( struct retro_game_geometry * ) & av_info - > geometry ;
const struct retro_game_geometry * in_geom = ( const struct retro_game_geometry * ) data ;
if ( ! geom )
return false ;
/* Can potentially be called every frame,
* don ' t do anything unless required . */
if ( ( geom - > base_width ! = in_geom - > base_width ) | |
( geom - > base_height ! = in_geom - > base_height ) | |
( geom - > aspect_ratio ! = in_geom - > aspect_ratio ) )
{
geom - > base_width = in_geom - > base_width ;
geom - > base_height = in_geom - > base_height ;
geom - > aspect_ratio = in_geom - > aspect_ratio ;
RARCH_LOG ( " [Environ]: SET_GEOMETRY: %ux%u, aspect: %.3f. \n " ,
geom - > base_width , geom - > base_height , geom - > aspect_ratio ) ;
/* Forces recomputation of aspect ratios if
* using core - dependent aspect ratios . */
video_driver_set_aspect_ratio ( ) ;
/* TODO: Figure out what to do, if anything, with
recording . */
}
else
{
RARCH_LOG ( " [Environ]: SET_GEOMETRY. \n " ) ;
}
break ;
}
case RETRO_ENVIRONMENT_GET_CURRENT_SOFTWARE_FRAMEBUFFER :
{
video_driver_state_t * video_st = video_state_get_ptr ( ) ;
struct retro_framebuffer * fb = ( struct retro_framebuffer * ) data ;
if (
video_st - > poke
& & video_st - > poke - > get_current_software_framebuffer
& & video_st - > poke - > get_current_software_framebuffer (
video_st - > data , fb ) )
return true ;
return false ;
}
case RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE :
{
video_driver_state_t * video_st = video_state_get_ptr ( ) ;
const struct retro_hw_render_interface * * iface = ( const struct retro_hw_render_interface * * ) data ;
if (
video_st - > poke
& & video_st - > poke - > get_hw_render_interface
& & video_st - > poke - > get_hw_render_interface (
video_st - > data , iface ) )
return true ;
return false ;
}
case RETRO_ENVIRONMENT_SET_SUPPORT_ACHIEVEMENTS :
# ifdef HAVE_CHEEVOS
{
bool state = * ( const bool * ) data ;
RARCH_LOG ( " [Environ]: SET_SUPPORT_ACHIEVEMENTS: %s. \n " , state ? " yes " : " no " ) ;
rcheevos_set_support_cheevos ( state ) ;
}
# endif
break ;
case RETRO_ENVIRONMENT_SET_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE :
{
video_driver_state_t * video_st =
video_state_get_ptr ( ) ;
const struct retro_hw_render_context_negotiation_interface * iface =
( const struct retro_hw_render_context_negotiation_interface * ) data ;
RARCH_LOG ( " [Environ]: SET_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE. \n " ) ;
video_st - > hw_render_context_negotiation = iface ;
break ;
}
case RETRO_ENVIRONMENT_SET_SERIALIZATION_QUIRKS :
{
uint64_t * quirks = ( uint64_t * ) data ;
RARCH_LOG ( " [Environ]: SET_SERIALIZATION_QUIRKS. \n " ) ;
runloop_st - > current_core . serialization_quirks_v = * quirks ;
break ;
}
case RETRO_ENVIRONMENT_SET_HW_SHARED_CONTEXT :
# ifdef HAVE_LIBNX
RARCH_LOG ( " [Environ]: SET_HW_SHARED_CONTEXT - ignored for now. \n " ) ;
/* TODO/FIXME - Force this off for now for Switch
* until shared HW context can work there */
return false ;
# else
RARCH_LOG ( " [Environ]: SET_HW_SHARED_CONTEXT. \n " ) ;
runloop_st - > core_set_shared_context = true ;
# endif
break ;
case RETRO_ENVIRONMENT_GET_VFS_INTERFACE :
{
const uint32_t supported_vfs_version = 3 ;
static struct retro_vfs_interface vfs_iface =
{
/* VFS API v1 */
retro_vfs_file_get_path_impl ,
retro_vfs_file_open_impl ,
retro_vfs_file_close_impl ,
retro_vfs_file_size_impl ,
retro_vfs_file_tell_impl ,
retro_vfs_file_seek_impl ,
retro_vfs_file_read_impl ,
retro_vfs_file_write_impl ,
retro_vfs_file_flush_impl ,
retro_vfs_file_remove_impl ,
retro_vfs_file_rename_impl ,
/* VFS API v2 */
retro_vfs_file_truncate_impl ,
/* VFS API v3 */
retro_vfs_stat_impl ,
retro_vfs_mkdir_impl ,
retro_vfs_opendir_impl ,
retro_vfs_readdir_impl ,
retro_vfs_dirent_get_name_impl ,
retro_vfs_dirent_is_dir_impl ,
retro_vfs_closedir_impl
} ;
struct retro_vfs_interface_info * vfs_iface_info = ( struct retro_vfs_interface_info * ) data ;
if ( vfs_iface_info - > required_interface_version < = supported_vfs_version )
{
RARCH_LOG ( " Core requested VFS version >= v%d, providing v%d \n " , vfs_iface_info - > required_interface_version , supported_vfs_version ) ;
vfs_iface_info - > required_interface_version = supported_vfs_version ;
vfs_iface_info - > iface = & vfs_iface ;
system - > supports_vfs = true ;
}
else
{
RARCH_WARN ( " Core requested VFS version v%d which is higher than what we support (v%d) \n " , vfs_iface_info - > required_interface_version , supported_vfs_version ) ;
return false ;
}
break ;
}
case RETRO_ENVIRONMENT_GET_LED_INTERFACE :
{
struct retro_led_interface * ledintf =
( struct retro_led_interface * ) data ;
if ( ledintf )
ledintf - > set_led_state = led_driver_set_led ;
}
break ;
case RETRO_ENVIRONMENT_GET_AUDIO_VIDEO_ENABLE :
{
int result = 0 ;
video_driver_state_t
* video_st = video_state_get_ptr ( ) ;
audio_driver_state_t
* audio_st = audio_state_get_ptr ( ) ;
if ( ! audio_st - > suspended & &
audio_st - > active )
result | = 2 ;
if ( video_st - > active
& & ! ( video_st - > current_video - > frame = = video_null . frame ) )
result | = 1 ;
# ifdef HAVE_RUNAHEAD
if ( runloop_st - > request_fast_savestate )
result | = 4 ;
if ( audio_st - > hard_disable )
result | = 8 ;
# endif
# ifdef HAVE_NETWORKING
if ( netplay_driver_ctl ( RARCH_NETPLAY_CTL_IS_REPLAYING , NULL ) )
result & = ~ ( 1 | 2 ) ;
if ( netplay_driver_ctl ( RARCH_NETPLAY_CTL_IS_ENABLED , NULL ) )
result | = 4 ;
# endif
if ( data )
{
int * result_p = ( int * ) data ;
* result_p = result ;
}
break ;
}
case RETRO_ENVIRONMENT_GET_MIDI_INTERFACE :
{
struct retro_midi_interface * midi_interface =
( struct retro_midi_interface * ) data ;
if ( midi_interface )
{
midi_interface - > input_enabled = midi_driver_input_enabled ;
midi_interface - > output_enabled = midi_driver_output_enabled ;
midi_interface - > read = midi_driver_read ;
midi_interface - > write = midi_driver_write ;
midi_interface - > flush = midi_driver_flush ;
}
break ;
}
case RETRO_ENVIRONMENT_GET_FASTFORWARDING :
* ( bool * ) data = runloop_st - > fastmotion ;
break ;
case RETRO_ENVIRONMENT_SET_FASTFORWARDING_OVERRIDE :
{
struct retro_fastforwarding_override * fastforwarding_override =
( struct retro_fastforwarding_override * ) data ;
/* Record new retro_fastforwarding_override parameters
* and schedule application on the the next call of
* runloop_check_state ( ) */
if ( fastforwarding_override )
{
memcpy ( & runloop_st - > fastmotion_override . next ,
fastforwarding_override ,
sizeof ( runloop_st - > fastmotion_override . next ) ) ;
runloop_st - > fastmotion_override . pending = true ;
}
break ;
}
case RETRO_ENVIRONMENT_GET_THROTTLE_STATE :
{
video_driver_state_t
* video_st =
video_state_get_ptr ( ) ;
struct retro_throttle_state * throttle_state =
( struct retro_throttle_state * ) data ;
audio_driver_state_t * audio_st =
audio_state_get_ptr ( ) ;
bool menu_opened = false ;
bool core_paused = runloop_st - > paused ;
bool no_audio = ( audio_st - > suspended | | ! audio_st - > active ) ;
float core_fps = ( float ) video_st - > av_info . timing . fps ;
# ifdef HAVE_REWIND
if ( runloop_st - > rewind_st . frame_is_reversed )
{
throttle_state - > mode = RETRO_THROTTLE_REWINDING ;
throttle_state - > rate = 0.0f ;
break ; /* ignore vsync */
}
# endif
# ifdef HAVE_MENU
menu_opened = menu_state_get_ptr ( ) - > alive ;
if ( menu_opened )
core_paused = settings - > bools . menu_pause_libretro ;
# endif
if ( core_paused )
{
throttle_state - > mode = RETRO_THROTTLE_FRAME_STEPPING ;
throttle_state - > rate = 0.0f ;
break ; /* ignore vsync */
}
/* Base mode and rate. */
throttle_state - > mode = RETRO_THROTTLE_NONE ;
throttle_state - > rate = core_fps ;
if ( runloop_st - > fastmotion )
{
throttle_state - > mode = RETRO_THROTTLE_FAST_FORWARD ;
throttle_state - > rate * = runloop_get_fastforward_ratio (
settings , & runloop_st - > fastmotion_override . current ) ;
}
else if ( runloop_st - > slowmotion & & ! no_audio )
{
throttle_state - > mode = RETRO_THROTTLE_SLOW_MOTION ;
throttle_state - > rate / = ( settings - > floats . slowmotion_ratio > 0.0f ?
settings - > floats . slowmotion_ratio : 1.0f ) ;
}
/* VSync overrides the mode if the rate is limited by the display. */
if ( menu_opened | | /* Menu currently always runs with vsync on. */
( settings - > bools . video_vsync & & ! runloop_st - > force_nonblock
& & ! input_state_get_ptr ( ) - > nonblocking_flag ) )
{
float refresh_rate = video_driver_get_refresh_rate ( ) ;
if ( refresh_rate = = 0.0f )
refresh_rate = settings - > floats . video_refresh_rate ;
if ( refresh_rate < throttle_state - > rate | | ! throttle_state - > rate )
{
/* Keep the mode as fast forward even if vsync limits it. */
if ( refresh_rate < core_fps )
throttle_state - > mode = RETRO_THROTTLE_VSYNC ;
throttle_state - > rate = refresh_rate ;
}
}
/* Special behavior while audio output is not available. */
if ( no_audio & & throttle_state - > mode ! = RETRO_THROTTLE_FAST_FORWARD
& & throttle_state - > mode ! = RETRO_THROTTLE_VSYNC )
{
/* Keep base if frame limiter matching the core is active. */
retro_time_t core_limit = ( core_fps
? ( retro_time_t ) ( 1000000.0f / core_fps )
: ( retro_time_t ) 0 ) ;
retro_time_t frame_limit = runloop_st - > frame_limit_minimum_time ;
if ( abs ( ( int ) ( core_limit - frame_limit ) ) > 10 )
{
throttle_state - > mode = RETRO_THROTTLE_UNBLOCKED ;
throttle_state - > rate = 0.0f ;
}
}
break ;
}
case RETRO_ENVIRONMENT_GET_INPUT_BITMASKS :
/* Just falldown, the function will return true */
break ;
case RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION :
RARCH_LOG ( " [Environ]: GET_CORE_OPTIONS_VERSION. \n " ) ;
/* Current API version is 2 */
* ( unsigned * ) data = 2 ;
break ;
case RETRO_ENVIRONMENT_GET_TARGET_REFRESH_RATE :
{
/* Try to use the polled refresh rate first. */
float target_refresh_rate = video_driver_get_refresh_rate ( ) ;
float video_refresh_rate = settings ? settings - > floats . video_refresh_rate : 0.0 ;
/* If the above function failed [possibly because it is not
* implemented ] , use the refresh rate set in the config instead . */
if ( target_refresh_rate = = 0.0f & & video_refresh_rate ! = 0.0f )
target_refresh_rate = video_refresh_rate ;
* ( float * ) data = target_refresh_rate ;
break ;
}
case RETRO_ENVIRONMENT_GET_INPUT_MAX_USERS :
* ( unsigned * ) data = settings - > uints . input_max_users ;
break ;
/* Private environment callbacks.
*
* Should all be properly addressed in version 2.
* */
case RETRO_ENVIRONMENT_POLL_TYPE_OVERRIDE :
{
const unsigned * poll_type_data = ( const unsigned * ) data ;
if ( poll_type_data )
runloop_st - > core_poll_type_override = ( enum poll_type_override_t ) * poll_type_data ;
}
break ;
case RETRO_ENVIRONMENT_GET_CLEAR_ALL_THREAD_WAITS_CB :
* ( retro_environment_t * ) data = runloop_clear_all_thread_waits ;
break ;
case RETRO_ENVIRONMENT_SET_SAVE_STATE_IN_BACKGROUND :
{
bool state = * ( const bool * ) data ;
RARCH_LOG ( " [Environ]: SET_SAVE_STATE_IN_BACKGROUND: %s. \n " , state ? " yes " : " no " ) ;
set_save_state_in_background ( state ) ;
}
break ;
case RETRO_ENVIRONMENT_SET_CONTENT_INFO_OVERRIDE :
{
const struct retro_system_content_info_override * overrides =
( const struct retro_system_content_info_override * ) data ;
RARCH_LOG ( " [Environ]: RETRO_ENVIRONMENT_SET_CONTENT_INFO_OVERRIDE. \n " ) ;
/* Passing NULL always results in 'success' - this
* allows cores to test for frontend support of
* the RETRO_ENVIRONMENT_SET_CONTENT_INFO_OVERRIDE and
* RETRO_ENVIRONMENT_GET_GAME_INFO_EXT callbacks */
if ( ! overrides )
return true ;
return content_file_override_set ( overrides ) ;
}
break ;
case RETRO_ENVIRONMENT_GET_GAME_INFO_EXT :
{
content_state_t * p_content =
content_state_get_ptr ( ) ;
const struct retro_game_info_ext * * game_info_ext =
( const struct retro_game_info_ext * * ) data ;
RARCH_LOG ( " [Environ]: RETRO_ENVIRONMENT_GET_GAME_INFO_EXT. \n " ) ;
if ( ! game_info_ext )
return false ;
if ( p_content & &
p_content - > content_list & &
p_content - > content_list - > game_info_ext )
* game_info_ext = p_content - > content_list - > game_info_ext ;
else
{
RARCH_ERR ( " [Environ]: Failed to retrieve extended game info \n " ) ;
* game_info_ext = NULL ;
return false ;
}
}
break ;
default :
RARCH_LOG ( " [Environ]: UNSUPPORTED (#%u). \n " , cmd ) ;
return false ;
}
return true ;
}
bool libretro_get_system_info (
const char * path ,
struct retro_system_info * info ,
bool * load_no_content )
{
struct retro_system_info dummy_info ;
# ifdef HAVE_DYNAMIC
dylib_t lib ;
# endif
runloop_state_t * runloop_st = & runloop_state ;
if ( string_ends_with_size ( path ,
" builtin " , strlen ( path ) , STRLEN_CONST ( " builtin " ) ) )
return false ;
dummy_info . library_name = NULL ;
dummy_info . library_version = NULL ;
dummy_info . valid_extensions = NULL ;
dummy_info . need_fullpath = false ;
dummy_info . block_extract = false ;
# ifdef HAVE_DYNAMIC
lib = libretro_get_system_info_lib (
path , & dummy_info , load_no_content ) ;
if ( ! lib )
{
RARCH_ERR ( " %s: \" %s \" \n " ,
msg_hash_to_str ( MSG_FAILED_TO_OPEN_LIBRETRO_CORE ) ,
path ) ;
RARCH_ERR ( " Error(s): %s \n " , dylib_error ( ) ) ;
return false ;
}
# else
if ( load_no_content )
{
runloop_st - > load_no_content_hook = load_no_content ;
/* load_no_content gets set in this callback. */
retro_set_environment ( runloop_environ_cb_get_system_info ) ;
/* It's possible that we just set get_system_info callback
* to the currently running core .
*
* Make sure we reset it to the actual environment callback .
* Ignore any environment callbacks here in case we ' re running
* on the non - current core . */
runloop_st - > ignore_environment_cb = true ;
retro_set_environment ( runloop_environment_cb ) ;
runloop_st - > ignore_environment_cb = false ;
}
retro_get_system_info ( & dummy_info ) ;
# endif
memcpy ( info , & dummy_info , sizeof ( * info ) ) ;
runloop_st - > current_library_name [ 0 ] = ' \0 ' ;
runloop_st - > current_library_version [ 0 ] = ' \0 ' ;
runloop_st - > current_valid_extensions [ 0 ] = ' \0 ' ;
if ( ! string_is_empty ( dummy_info . library_name ) )
strlcpy ( runloop_st - > current_library_name ,
dummy_info . library_name ,
sizeof ( runloop_st - > current_library_name ) ) ;
if ( ! string_is_empty ( dummy_info . library_version ) )
strlcpy ( runloop_st - > current_library_version ,
dummy_info . library_version ,
sizeof ( runloop_st - > current_library_version ) ) ;
if ( dummy_info . valid_extensions )
strlcpy ( runloop_st - > current_valid_extensions ,
dummy_info . valid_extensions ,
sizeof ( runloop_st - > current_valid_extensions ) ) ;
info - > library_name = runloop_st - > current_library_name ;
info - > library_version = runloop_st - > current_library_version ;
info - > valid_extensions = runloop_st - > current_valid_extensions ;
# ifdef HAVE_DYNAMIC
dylib_close ( lib ) ;
# endif
return true ;
}
/**
* load_symbols :
* @ type : Type of core to be loaded .
* If CORE_TYPE_DUMMY , will
* load dummy symbols .
*
* Setup libretro callback symbols . Returns true on success ,
* or false if symbols could not be loaded .
* */
static bool init_libretro_symbols_custom (
runloop_state_t * runloop_st ,
enum rarch_core_type type ,
struct retro_core_t * current_core ,
const char * lib_path ,
void * _lib_handle_p )
{
# ifdef HAVE_DYNAMIC
/* the library handle for use with the SYMBOL macro */
dylib_t lib_handle_local ;
# endif
switch ( type )
{
case CORE_TYPE_PLAIN :
{
# ifdef HAVE_DYNAMIC
# ifdef HAVE_RUNAHEAD
dylib_t * lib_handle_p = ( dylib_t * ) _lib_handle_p ;
if ( ! lib_path | | ! lib_handle_p )
# endif
{
const char * path = path_get ( RARCH_PATH_CORE ) ;
if ( string_is_empty ( path ) )
{
RARCH_ERR ( " [Core]: Frontend is built for dynamic libretro cores, but "
" path is not set. Cannot continue. \n " ) ;
retroarch_fail ( 1 , " init_libretro_symbols() " ) ;
}
RARCH_LOG ( " [Core]: Loading dynamic libretro core from: \" %s \" \n " ,
path ) ;
if ( ! ( runloop_st - > lib_handle = load_dynamic_core (
path ,
path_get_ptr ( RARCH_PATH_CORE ) ,
path_get_realsize ( RARCH_PATH_CORE )
) ) )
{
RARCH_ERR ( " %s: \" %s \" \n Error(s): %s \n " ,
msg_hash_to_str ( MSG_FAILED_TO_OPEN_LIBRETRO_CORE ) ,
path , dylib_error ( ) ) ;
runloop_msg_queue_push ( msg_hash_to_str ( MSG_FAILED_TO_OPEN_LIBRETRO_CORE ) ,
1 , 180 , true , NULL , MESSAGE_QUEUE_ICON_DEFAULT , MESSAGE_QUEUE_CATEGORY_INFO ) ;
return false ;
}
lib_handle_local = runloop_st - > lib_handle ;
}
# ifdef HAVE_RUNAHEAD
else
{
/* for a secondary core, we already have a
* primary library loaded , so we can skip
* some checks and just load the library */
retro_assert ( lib_path ! = NULL & & lib_handle_p ! = NULL ) ;
lib_handle_local = dylib_load ( lib_path ) ;
if ( ! lib_handle_local )
return false ;
* lib_handle_p = lib_handle_local ;
}
# endif
# endif
CORE_SYMBOLS ( SYMBOL ) ;
}
break ;
case CORE_TYPE_DUMMY :
CORE_SYMBOLS ( SYMBOL_DUMMY ) ;
break ;
case CORE_TYPE_FFMPEG :
# ifdef HAVE_FFMPEG
CORE_SYMBOLS ( SYMBOL_FFMPEG ) ;
# endif
break ;
case CORE_TYPE_MPV :
# ifdef HAVE_MPV
CORE_SYMBOLS ( SYMBOL_MPV ) ;
# endif
break ;
case CORE_TYPE_IMAGEVIEWER :
# ifdef HAVE_IMAGEVIEWER
CORE_SYMBOLS ( SYMBOL_IMAGEVIEWER ) ;
# endif
break ;
case CORE_TYPE_NETRETROPAD :
# if defined(HAVE_NETWORKING) && defined(HAVE_NETWORKGAMEPAD)
CORE_SYMBOLS ( SYMBOL_NETRETROPAD ) ;
# endif
break ;
case CORE_TYPE_VIDEO_PROCESSOR :
# if defined(HAVE_VIDEOPROCESSOR)
CORE_SYMBOLS ( SYMBOL_VIDEOPROCESSOR ) ;
# endif
break ;
case CORE_TYPE_GONG :
# ifdef HAVE_GONG
CORE_SYMBOLS ( SYMBOL_GONG ) ;
# endif
break ;
}
return true ;
}
/**
* init_libretro_symbols :
* @ type : Type of core to be loaded .
* If CORE_TYPE_DUMMY , will
* load dummy symbols .
*
* Initializes libretro symbols and
* setups environment callback functions . Returns true on success ,
* or false if symbols could not be loaded .
* */
static bool init_libretro_symbols (
runloop_state_t * runloop_st ,
enum rarch_core_type type ,
struct retro_core_t * current_core )
{
/* Load symbols */
if ( ! init_libretro_symbols_custom ( runloop_st ,
type , current_core , NULL , NULL ) )
return false ;
# ifdef HAVE_RUNAHEAD
/* remember last core type created, so creating a
* secondary core will know what core type to use . */
runloop_st - > last_core_type = type ;
# endif
return true ;
}
bool libretro_get_shared_context ( void )
{
runloop_state_t * runloop_st = & runloop_state ;
return runloop_st - > core_set_shared_context ;
}
void runloop_system_info_free ( void )
{
runloop_state_t * runloop_st = & runloop_state ;
rarch_system_info_t * sys_info = & runloop_st - > system ;
if ( sys_info - > subsystem . data )
free ( sys_info - > subsystem . data ) ;
if ( sys_info - > ports . data )
free ( sys_info - > ports . data ) ;
if ( sys_info - > mmaps . descriptors )
free ( ( void * ) sys_info - > mmaps . descriptors ) ;
sys_info - > subsystem . data = NULL ;
sys_info - > subsystem . size = 0 ;
sys_info - > ports . data = NULL ;
sys_info - > ports . size = 0 ;
sys_info - > mmaps . descriptors = NULL ;
sys_info - > mmaps . num_descriptors = 0 ;
sys_info - > info . library_name = NULL ;
sys_info - > info . library_version = NULL ;
sys_info - > info . valid_extensions = NULL ;
sys_info - > info . need_fullpath = false ;
sys_info - > info . block_extract = false ;
runloop_st - > key_event = NULL ;
runloop_st - > frontend_key_event = NULL ;
memset ( & runloop_st - > system , 0 , sizeof ( rarch_system_info_t ) ) ;
}
/**
* uninit_libretro_sym :
*
* Frees libretro core .
*
* Frees all core options ,
* associated state , and
* unbind all libretro callback symbols .
* */
static void uninit_libretro_symbols (
struct retro_core_t * current_core )
{
runloop_state_t
* runloop_st = & runloop_state ;
input_driver_state_t
* input_st = input_state_get_ptr ( ) ;
audio_driver_state_t
* audio_st = audio_state_get_ptr ( ) ;
camera_driver_state_t
* camera_st = camera_state_get_ptr ( ) ;
location_driver_state_t
* location_st = location_state_get_ptr ( ) ;
# ifdef HAVE_DYNAMIC
if ( runloop_st - > lib_handle )
dylib_close ( runloop_st - > lib_handle ) ;
runloop_st - > lib_handle = NULL ;
# endif
memset ( current_core , 0 , sizeof ( struct retro_core_t ) ) ;
runloop_st - > core_set_shared_context = false ;
if ( runloop_st - > core_options )
{
runloop_deinit_core_options (
runloop_st - > game_options_active ,
path_get ( RARCH_PATH_CORE_OPTIONS ) ,
runloop_st - > core_options ) ;
runloop_st - > game_options_active = false ;
runloop_st - > folder_options_active = false ;
runloop_st - > core_options = NULL ;
}
runloop_system_info_free ( ) ;
audio_st - > callback . callback = NULL ;
audio_st - > callback . set_state = NULL ;
runloop_frame_time_free ( ) ;
runloop_audio_buffer_status_free ( ) ;
input_game_focus_free ( ) ;
runloop_fastmotion_override_free ( ) ;
runloop_core_options_cb_free ( ) ;
camera_st - > active = false ;
location_st - > active = false ;
/* Core has finished utilising the input driver;
* reset ' analog input requested ' flags */
memset ( & input_st - > analog_requested , 0 ,
sizeof ( input_st - > analog_requested ) ) ;
/* Performance counters no longer valid. */
runloop_st - > perf_ptr_libretro = 0 ;
memset ( runloop_st - > perf_counters_libretro , 0 ,
sizeof ( runloop_st - > perf_counters_libretro ) ) ;
}
# if defined(HAVE_RUNAHEAD)
static int16_t input_state_get_last ( unsigned port ,
unsigned device , unsigned index , unsigned id )
{
unsigned i ;
runloop_state_t * runloop_st = & runloop_state ;
if ( ! runloop_st - > input_state_list )
return 0 ;
/* find list item */
for ( i = 0 ; i < ( unsigned ) runloop_st - > input_state_list - > size ; i + + )
{
input_list_element * element =
( input_list_element * ) runloop_st - > input_state_list - > data [ i ] ;
if ( ( element - > port = = port ) & &
( element - > device = = device ) & &
( element - > index = = index ) )
{
if ( id < element - > state_size )
return element - > state [ id ] ;
return 0 ;
}
}
return 0 ;
}
static void free_retro_ctx_load_content_info ( struct
retro_ctx_load_content_info * dest )
{
if ( ! dest )
return ;
string_list_free ( ( struct string_list * ) dest - > content ) ;
if ( dest - > info )
free ( dest - > info ) ;
dest - > info = NULL ;
dest - > content = NULL ;
}
static struct retro_game_info * clone_retro_game_info ( const
struct retro_game_info * src )
{
struct retro_game_info * dest = ( struct retro_game_info * ) malloc (
sizeof ( struct retro_game_info ) ) ;
if ( ! dest )
return NULL ;
/* content_file_init() guarantees that all
* elements of the source retro_game_info
* struct will persist for the lifetime of
* the core . This means we do not have to
* copy any data ; pointer assignment is
* sufficient */
dest - > path = src - > path ;
dest - > data = src - > data ;
dest - > size = src - > size ;
dest - > meta = src - > meta ;
return dest ;
}
static struct retro_ctx_load_content_info
* clone_retro_ctx_load_content_info (
const struct retro_ctx_load_content_info * src )
{
struct retro_ctx_load_content_info * dest = NULL ;
if ( ! src | | src - > special )
return NULL ; /* refuse to deal with the Special field */
dest = ( struct retro_ctx_load_content_info * )
malloc ( sizeof ( * dest ) ) ;
if ( ! dest )
return NULL ;
dest - > info = NULL ;
dest - > content = NULL ;
dest - > special = NULL ;
if ( src - > info )
dest - > info = clone_retro_game_info ( src - > info ) ;
if ( src - > content )
dest - > content = string_list_clone ( src - > content ) ;
return dest ;
}
static void set_load_content_info (
runloop_state_t * runloop_st ,
const retro_ctx_load_content_info_t * ctx )
{
free_retro_ctx_load_content_info ( runloop_st - > load_content_info ) ;
free ( runloop_st - > load_content_info ) ;
runloop_st - > load_content_info = clone_retro_ctx_load_content_info ( ctx ) ;
}
/* RUNAHEAD - SECONDARY CORE */
# if defined(HAVE_DYNAMIC) || defined(HAVE_DYLIB)
static void strcat_alloc ( char * * dst , const char * s )
{
size_t len1 ;
char * src = * dst ;
if ( ! src )
{
if ( s )
{
size_t len = strlen ( s ) ;
if ( len ! = 0 )
{
char * _dst = ( char * ) malloc ( len + 1 ) ;
strcpy_literal ( _dst , s ) ;
src = _dst ;
}
else
src = NULL ;
}
else
src = ( char * ) calloc ( 1 , 1 ) ;
* dst = src ;
return ;
}
if ( ! s )
return ;
len1 = strlen ( src ) ;
if ( ! ( src = ( char * ) realloc ( src , len1 + strlen ( s ) + 1 ) ) )
return ;
* dst = src ;
strcpy_literal ( src + len1 , s ) ;
}
void runloop_secondary_core_destroy ( void )
{
runloop_state_t * runloop_st = & runloop_state ;
if ( ! runloop_st - > secondary_lib_handle )
return ;
/* unload game from core */
if ( runloop_st - > secondary_core . retro_unload_game )
runloop_st - > secondary_core . retro_unload_game ( ) ;
runloop_st - > core_poll_type_override = POLL_TYPE_OVERRIDE_DONTCARE ;
/* deinit */
if ( runloop_st - > secondary_core . retro_deinit )
runloop_st - > secondary_core . retro_deinit ( ) ;
memset ( & runloop_st - > secondary_core , 0 , sizeof ( struct retro_core_t ) ) ;
dylib_close ( runloop_st - > secondary_lib_handle ) ;
runloop_st - > secondary_lib_handle = NULL ;
filestream_delete ( runloop_st - > secondary_library_path ) ;
if ( runloop_st - > secondary_library_path )
free ( runloop_st - > secondary_library_path ) ;
runloop_st - > secondary_library_path = NULL ;
}
static char * get_tmpdir_alloc ( const char * override_dir )
{
const char * src = NULL ;
char * path = NULL ;
# ifdef _WIN32
# ifdef LEGACY_WIN32
DWORD plen = GetTempPath ( 0 , NULL ) + 1 ;
if ( ! ( path = ( char * ) malloc ( plen * sizeof ( char ) ) ) )
return NULL ;
path [ plen - 1 ] = 0 ;
GetTempPath ( plen , path ) ;
# else
DWORD plen = GetTempPathW ( 0 , NULL ) + 1 ;
wchar_t * wide_str = ( wchar_t * ) malloc ( plen * sizeof ( wchar_t ) ) ;
if ( ! wide_str )
return NULL ;
wide_str [ plen - 1 ] = 0 ;
GetTempPathW ( plen , wide_str ) ;
path = utf16_to_utf8_string_alloc ( wide_str ) ;
free ( wide_str ) ;
# endif
# else
# if defined ANDROID
src = override_dir ;
# else
{
char * tmpdir = getenv ( " TMPDIR " ) ;
if ( tmpdir )
src = tmpdir ;
else
src = " /tmp " ;
}
# endif
if ( src )
{
size_t len = strlen ( src ) ;
if ( len ! = 0 )
{
char * dst = ( char * ) malloc ( len + 1 ) ;
strcpy_literal ( dst , src ) ;
path = dst ;
}
}
else
path = ( char * ) calloc ( 1 , 1 ) ;
# endif
return path ;
}
static bool write_file_with_random_name ( char * * temp_dll_path ,
const char * tmp_path , const void * data , ssize_t dataSize )
{
int ext_len ;
unsigned i ;
char number_buf [ 32 ] ;
bool okay = false ;
const char * prefix = " tmp " ;
char * ext = NULL ;
time_t time_value = time ( NULL ) ;
unsigned _number_value = ( unsigned ) time_value ;
const char * src = path_get_extension ( * temp_dll_path ) ;
if ( src )
{
size_t len = strlen ( src ) ;
if ( len ! = 0 )
{
char * dst = ( char * ) malloc ( len + 1 ) ;
strcpy_literal ( dst , src ) ;
ext = dst ;
}
}
else
ext = ( char * ) calloc ( 1 , 1 ) ;
ext_len = ( int ) strlen ( ext ) ;
if ( ext_len > 0 )
{
strcat_alloc ( & ext , " . " ) ;
memmove ( ext + 1 , ext , ext_len ) ;
ext [ 0 ] = ' . ' ;
ext_len + + ;
}
/* Try up to 30 'random' filenames before giving up */
for ( i = 0 ; i < 30 ; i + + )
{
int number_value = _number_value * 214013 + 2531011 ;
int number = ( number_value > > 14 ) % 100000 ;
snprintf ( number_buf , sizeof ( number_buf ) , " %05d " , number ) ;
if ( * temp_dll_path )
free ( * temp_dll_path ) ;
* temp_dll_path = NULL ;
strcat_alloc ( temp_dll_path , tmp_path ) ;
strcat_alloc ( temp_dll_path , PATH_DEFAULT_SLASH ( ) ) ;
strcat_alloc ( temp_dll_path , prefix ) ;
strcat_alloc ( temp_dll_path , number_buf ) ;
strcat_alloc ( temp_dll_path , ext ) ;
if ( filestream_write_file ( * temp_dll_path , data , dataSize ) )
{
okay = true ;
break ;
}
}
if ( ext )
free ( ext ) ;
ext = NULL ;
return okay ;
}
static char * copy_core_to_temp_file (
const char * core_path ,
const char * dir_libretro )
{
char tmp_path [ PATH_MAX_LENGTH ] ;
bool failed = false ;
char * tmpdir = NULL ;
char * tmp_dll_path = NULL ;
void * dll_file_data = NULL ;
int64_t dll_file_size = 0 ;
const char * core_base_name = path_basename_nocompression ( core_path ) ;
if ( strlen ( core_base_name ) = = 0 )
return NULL ;
tmpdir = get_tmpdir_alloc ( dir_libretro ) ;
if ( ! tmpdir )
return NULL ;
tmp_path [ 0 ] = ' \0 ' ;
fill_pathname_join ( tmp_path ,
tmpdir , " retroarch_temp " ,
sizeof ( tmp_path ) ) ;
if ( ! path_mkdir ( tmp_path ) )
{
failed = true ;
goto end ;
}
if ( ! filestream_read_file ( core_path , & dll_file_data , & dll_file_size ) )
{
failed = true ;
goto end ;
}
strcat_alloc ( & tmp_dll_path , tmp_path ) ;
strcat_alloc ( & tmp_dll_path , PATH_DEFAULT_SLASH ( ) ) ;
strcat_alloc ( & tmp_dll_path , core_base_name ) ;
if ( ! filestream_write_file ( tmp_dll_path , dll_file_data , dll_file_size ) )
{
/* try other file names */
if ( ! write_file_with_random_name ( & tmp_dll_path ,
tmp_path , dll_file_data , dll_file_size ) )
failed = true ;
}
end :
if ( tmpdir )
free ( tmpdir ) ;
if ( dll_file_data )
free ( dll_file_data ) ;
tmpdir = NULL ;
dll_file_data = NULL ;
if ( ! failed )
return tmp_dll_path ;
if ( tmp_dll_path )
free ( tmp_dll_path ) ;
tmp_dll_path = NULL ;
return NULL ;
}
static bool runloop_environment_secondary_core_hook (
unsigned cmd , void * data )
{
runloop_state_t * runloop_st = & runloop_state ;
bool result = runloop_environment_cb ( cmd , data ) ;
if ( runloop_st - > has_variable_update )
{
if ( cmd = = RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE )
{
bool * bool_p = ( bool * ) data ;
* bool_p = true ;
runloop_st - > has_variable_update = false ;
return true ;
}
else if ( cmd = = RETRO_ENVIRONMENT_GET_VARIABLE )
runloop_st - > has_variable_update = false ;
}
return result ;
}
static void runloop_clear_controller_port_map ( void )
{
unsigned port ;
runloop_state_t * runloop_st = & runloop_state ;
for ( port = 0 ; port < MAX_USERS ; port + + )
runloop_st - > port_map [ port ] = - 1 ;
}
static bool secondary_core_create ( runloop_state_t * runloop_st ,
settings_t * settings )
{
unsigned port ;
bool contentless = false ;
bool is_inited = false ;
const enum rarch_core_type
last_core_type = runloop_st - > last_core_type ;
rarch_system_info_t * info = & runloop_st - > system ;
unsigned num_active_users = settings - > uints . input_max_users ;
if ( last_core_type ! = CORE_TYPE_PLAIN | |
! runloop_st - > load_content_info | |
runloop_st - > load_content_info - > special )
return false ;
if ( runloop_st - > secondary_library_path )
free ( runloop_st - > secondary_library_path ) ;
runloop_st - > secondary_library_path = NULL ;
runloop_st - > secondary_library_path = copy_core_to_temp_file (
path_get ( RARCH_PATH_CORE ) ,
settings - > paths . directory_libretro ) ;
if ( ! runloop_st - > secondary_library_path )
return false ;
/* Load Core */
if ( ! init_libretro_symbols_custom ( runloop_st ,
CORE_TYPE_PLAIN , & runloop_st - > secondary_core ,
runloop_st - > secondary_library_path ,
& runloop_st - > secondary_lib_handle ) )
return false ;
runloop_st - > secondary_core . symbols_inited = true ;
runloop_st - > secondary_core . retro_set_environment (
runloop_environment_secondary_core_hook ) ;
# ifdef HAVE_RUNAHEAD
runloop_st - > has_variable_update = true ;
# endif
runloop_st - > secondary_core . retro_init ( ) ;
content_get_status ( & contentless , & is_inited ) ;
runloop_st - > secondary_core . inited = is_inited ;
/* Load Content */
/* disabled due to crashes */
if ( ! runloop_st - > load_content_info | |
runloop_st - > load_content_info - > special )
return false ;
if ( ( runloop_st - > load_content_info - > content - > size > 0 ) & &
runloop_st - > load_content_info - > content - > elems [ 0 ] . data )
{
runloop_st - > secondary_core . game_loaded =
runloop_st - > secondary_core . retro_load_game (
runloop_st - > load_content_info - > info ) ;
if ( ! runloop_st - > secondary_core . game_loaded )
goto error ;
}
else if ( contentless )
{
runloop_st - > secondary_core . game_loaded =
runloop_st - > secondary_core . retro_load_game ( NULL ) ;
if ( ! runloop_st - > secondary_core . game_loaded )
goto error ;
}
else
runloop_st - > secondary_core . game_loaded = false ;
if ( ! runloop_st - > secondary_core . inited )
goto error ;
core_set_default_callbacks ( & runloop_st - > secondary_callbacks ) ;
runloop_st - > secondary_core . retro_set_video_refresh (
runloop_st - > secondary_callbacks . frame_cb ) ;
runloop_st - > secondary_core . retro_set_audio_sample (
runloop_st - > secondary_callbacks . sample_cb ) ;
runloop_st - > secondary_core . retro_set_audio_sample_batch (
runloop_st - > secondary_callbacks . sample_batch_cb ) ;
runloop_st - > secondary_core . retro_set_input_state (
runloop_st - > secondary_callbacks . state_cb ) ;
runloop_st - > secondary_core . retro_set_input_poll (
runloop_st - > secondary_callbacks . poll_cb ) ;
if ( info )
for ( port = 0 ; port < MAX_USERS ; port + + )
{
if ( port < info - > ports . size )
{
unsigned device = ( port < num_active_users ) ?
runloop_st - > port_map [ port ] : RETRO_DEVICE_NONE ;
runloop_st - > secondary_core . retro_set_controller_port_device (
port , device ) ;
}
}
runloop_clear_controller_port_map ( ) ;
return true ;
error :
runloop_secondary_core_destroy ( ) ;
return false ;
}
bool secondary_core_ensure_exists ( settings_t * settings )
{
runloop_state_t * runloop_st = & runloop_state ;
if ( ! runloop_st - > secondary_lib_handle )
if ( ! secondary_core_create ( runloop_st , settings ) )
return false ;
return true ;
}
# if defined(HAVE_RUNAHEAD) && defined(HAVE_DYNAMIC)
static bool secondary_core_deserialize ( settings_t * settings ,
const void * buffer , int size )
{
runloop_state_t * runloop_st = & runloop_state ;
if ( secondary_core_ensure_exists ( settings ) )
return runloop_st - > secondary_core . retro_unserialize ( buffer , size ) ;
runloop_secondary_core_destroy ( ) ;
return false ;
}
# endif
static void remember_controller_port_device ( long port , long device )
{
runloop_state_t * runloop_st = & runloop_state ;
if ( port > = 0 & & port < MAX_USERS )
runloop_st - > port_map [ port ] = ( int ) device ;
if ( runloop_st - > secondary_lib_handle
& & runloop_st - > secondary_core . retro_set_controller_port_device )
runloop_st - > secondary_core . retro_set_controller_port_device ( ( unsigned ) port , ( unsigned ) device ) ;
}
static void secondary_core_input_poll_null ( void ) { }
static bool secondary_core_run_use_last_input ( void )
{
retro_input_poll_t old_poll_function ;
retro_input_state_t old_input_function ;
runloop_state_t * runloop_st = & runloop_state ;
if ( ! secondary_core_ensure_exists ( config_get_ptr ( ) ) )
{
runloop_secondary_core_destroy ( ) ;
return false ;
}
old_poll_function = runloop_st - > secondary_callbacks . poll_cb ;
old_input_function = runloop_st - > secondary_callbacks . state_cb ;
runloop_st - > secondary_callbacks . poll_cb = secondary_core_input_poll_null ;
runloop_st - > secondary_callbacks . state_cb = input_state_get_last ;
runloop_st - > secondary_core . retro_set_input_poll (
runloop_st - > secondary_callbacks . poll_cb ) ;
runloop_st - > secondary_core . retro_set_input_state (
runloop_st - > secondary_callbacks . state_cb ) ;
runloop_st - > secondary_core . retro_run ( ) ;
runloop_st - > secondary_callbacks . poll_cb = old_poll_function ;
runloop_st - > secondary_callbacks . state_cb = old_input_function ;
runloop_st - > secondary_core . retro_set_input_poll (
runloop_st - > secondary_callbacks . poll_cb ) ;
runloop_st - > secondary_core . retro_set_input_state (
runloop_st - > secondary_callbacks . state_cb ) ;
return true ;
}
# else
void runloop_secondary_core_destroy ( void ) { }
static void remember_controller_port_device ( long port , long device ) { }
static void runloop_clear_controller_port_map ( void ) { }
# endif
static void mylist_resize ( my_list * list ,
int new_size , bool run_constructor )
{
int i ;
int new_capacity ;
int old_size ;
void * element = NULL ;
if ( new_size < 0 )
new_size = 0 ;
new_capacity = new_size ;
old_size = list - > size ;
if ( new_size = = old_size )
return ;
if ( new_size > list - > capacity )
{
if ( new_capacity < list - > capacity * 2 )
new_capacity = list - > capacity * 2 ;
/* try to realloc */
list - > data = ( void * * ) realloc (
( void * ) list - > data , new_capacity * sizeof ( void * ) ) ;
for ( i = list - > capacity ; i < new_capacity ; i + + )
list - > data [ i ] = NULL ;
list - > capacity = new_capacity ;
}
if ( new_size < = list - > size )
{
for ( i = new_size ; i < list - > size ; i + + )
{
element = list - > data [ i ] ;
if ( element )
{
list - > destructor ( element ) ;
list - > data [ i ] = NULL ;
}
}
}
else
{
for ( i = list - > size ; i < new_size ; i + + )
{
list - > data [ i ] = NULL ;
if ( run_constructor )
list - > data [ i ] = list - > constructor ( ) ;
}
}
list - > size = new_size ;
}
static void * mylist_add_element ( my_list * list )
{
int old_size = list - > size ;
if ( list )
mylist_resize ( list , old_size + 1 , true ) ;
return list - > data [ old_size ] ;
}
static void mylist_destroy ( my_list * * list_p )
{
my_list * list = NULL ;
if ( ! list_p )
return ;
list = * list_p ;
if ( list )
{
mylist_resize ( list , 0 , false ) ;
free ( list - > data ) ;
free ( list ) ;
* list_p = NULL ;
}
}
static void mylist_create ( my_list * * list_p , int initial_capacity ,
constructor_t constructor , destructor_t destructor )
{
my_list * list = NULL ;
if ( ! list_p )
return ;
list = * list_p ;
if ( list )
mylist_destroy ( list_p ) ;
list = ( my_list * ) malloc ( sizeof ( my_list ) ) ;
* list_p = list ;
list - > size = 0 ;
list - > constructor = constructor ;
list - > destructor = destructor ;
list - > data = ( void * * ) calloc ( initial_capacity , sizeof ( void * ) ) ;
list - > capacity = initial_capacity ;
}
static void * input_list_element_constructor ( void )
{
void * ptr = malloc ( sizeof ( input_list_element ) ) ;
input_list_element * element = ( input_list_element * ) ptr ;
element - > port = 0 ;
element - > device = 0 ;
element - > index = 0 ;
element - > state = ( int16_t * ) calloc ( 256 , sizeof ( int16_t ) ) ;
element - > state_size = 256 ;
return ptr ;
}
static void input_list_element_realloc (
input_list_element * element ,
unsigned int new_size )
{
if ( new_size > element - > state_size )
{
element - > state = ( int16_t * ) realloc ( element - > state ,
new_size * sizeof ( int16_t ) ) ;
memset ( & element - > state [ element - > state_size ] , 0 ,
( new_size - element - > state_size ) * sizeof ( int16_t ) ) ;
element - > state_size = new_size ;
}
}
static void input_list_element_expand (
input_list_element * element , unsigned int new_index )
{
unsigned int new_size = element - > state_size ;
if ( new_size = = 0 )
new_size = 32 ;
while ( new_index > = new_size )
new_size * = 2 ;
input_list_element_realloc ( element , new_size ) ;
}
static void input_list_element_destructor ( void * element_ptr )
{
input_list_element * element = ( input_list_element * ) element_ptr ;
if ( ! element )
return ;
free ( element - > state ) ;
free ( element_ptr ) ;
}
static void input_state_set_last (
runloop_state_t * runloop_st ,
unsigned port , unsigned device ,
unsigned index , unsigned id , int16_t value )
{
unsigned i ;
input_list_element * element = NULL ;
if ( ! runloop_st - > input_state_list )
mylist_create ( & runloop_st - > input_state_list , 16 ,
input_list_element_constructor ,
input_list_element_destructor ) ;
/* Find list item */
for ( i = 0 ; i < ( unsigned ) runloop_st - > input_state_list - > size ; i + + )
{
element = ( input_list_element * ) runloop_st - > input_state_list - > data [ i ] ;
if ( ( element - > port = = port ) & &
( element - > device = = device ) & &
( element - > index = = index )
)
{
if ( id > = element - > state_size )
input_list_element_expand ( element , id ) ;
element - > state [ id ] = value ;
return ;
}
}
element = NULL ;
if ( runloop_st - > input_state_list )
element = ( input_list_element * )
mylist_add_element ( runloop_st - > input_state_list ) ;
if ( element )
{
element - > port = port ;
element - > device = device ;
element - > index = index ;
if ( id > = element - > state_size )
input_list_element_expand ( element , id ) ;
element - > state [ id ] = value ;
}
}
static int16_t input_state_with_logging ( unsigned port ,
unsigned device , unsigned index , unsigned id )
{
runloop_state_t * runloop_st = & runloop_state ;
if ( runloop_st - > input_state_callback_original )
{
int16_t result =
runloop_st - > input_state_callback_original (
port , device , index , id ) ;
int16_t last_input =
input_state_get_last ( port , device , index , id ) ;
if ( result ! = last_input )
runloop_st - > input_is_dirty = true ;
/*arbitrary limit of up to 65536 elements in state array*/
if ( id < 65536 )
input_state_set_last ( runloop_st , port , device , index , id , result ) ;
return result ;
}
return 0 ;
}
static void reset_hook ( void )
{
runloop_state_t * runloop_st = & runloop_state ;
runloop_st - > input_is_dirty = true ;
if ( runloop_st - > retro_reset_callback_original )
runloop_st - > retro_reset_callback_original ( ) ;
}
static bool unserialize_hook ( const void * buf , size_t size )
{
runloop_state_t * runloop_st = & runloop_state ;
runloop_st - > input_is_dirty = true ;
if ( runloop_st - > retro_unserialize_callback_original )
return runloop_st - > retro_unserialize_callback_original ( buf , size ) ;
return false ;
}
static void add_input_state_hook ( runloop_state_t * runloop_st )
{
struct retro_callbacks * cbs = & runloop_st - > retro_ctx ;
if ( ! runloop_st - > input_state_callback_original )
{
runloop_st - > input_state_callback_original = cbs - > state_cb ;
cbs - > state_cb = input_state_with_logging ;
runloop_st - > current_core . retro_set_input_state ( cbs - > state_cb ) ;
}
if ( ! runloop_st - > retro_reset_callback_original )
{
runloop_st - > retro_reset_callback_original
= runloop_st - > current_core . retro_reset ;
runloop_st - > current_core . retro_reset = reset_hook ;
}
if ( ! runloop_st - > retro_unserialize_callback_original )
{
runloop_st - > retro_unserialize_callback_original = runloop_st - > current_core . retro_unserialize ;
runloop_st - > current_core . retro_unserialize = unserialize_hook ;
}
}
static void remove_input_state_hook ( runloop_state_t * runloop_st )
{
struct retro_callbacks * cbs = & runloop_st - > retro_ctx ;
if ( runloop_st - > input_state_callback_original )
{
cbs - > state_cb =
runloop_st - > input_state_callback_original ;
runloop_st - > current_core . retro_set_input_state ( cbs - > state_cb ) ;
runloop_st - > input_state_callback_original = NULL ;
mylist_destroy ( & runloop_st - > input_state_list ) ;
}
if ( runloop_st - > retro_reset_callback_original )
{
runloop_st - > current_core . retro_reset =
runloop_st - > retro_reset_callback_original ;
runloop_st - > retro_reset_callback_original = NULL ;
}
if ( runloop_st - > retro_unserialize_callback_original )
{
runloop_st - > current_core . retro_unserialize =
runloop_st - > retro_unserialize_callback_original ;
runloop_st - > retro_unserialize_callback_original = NULL ;
}
}
static void * runahead_save_state_alloc ( void )
{
runloop_state_t * runloop_st = & runloop_state ;
retro_ctx_serialize_info_t * savestate = ( retro_ctx_serialize_info_t * )
malloc ( sizeof ( retro_ctx_serialize_info_t ) ) ;
if ( ! savestate )
return NULL ;
savestate - > data = NULL ;
savestate - > data_const = NULL ;
savestate - > size = 0 ;
if ( ( runloop_st - > runahead_save_state_size > 0 )
& & runloop_st - > runahead_save_state_size_known )
{
savestate - > data = malloc ( runloop_st - > runahead_save_state_size ) ;
savestate - > data_const = savestate - > data ;
savestate - > size = runloop_st - > runahead_save_state_size ;
}
return savestate ;
}
static void runahead_save_state_free ( void * data )
{
retro_ctx_serialize_info_t * savestate = ( retro_ctx_serialize_info_t * ) data ;
if ( ! savestate )
return ;
free ( savestate - > data ) ;
free ( savestate ) ;
}
static void runahead_save_state_list_init (
runloop_state_t * runloop_st ,
size_t save_state_size )
{
runloop_st - > runahead_save_state_size = save_state_size ;
runloop_st - > runahead_save_state_size_known = true ;
mylist_create ( & runloop_st - > runahead_save_state_list , 16 ,
runahead_save_state_alloc , runahead_save_state_free ) ;
}
/* Hooks - Hooks to cleanup, and add dirty input hooks */
static void runahead_remove_hooks ( runloop_state_t * runloop_st )
{
if ( runloop_st - > original_retro_deinit )
{
runloop_st - > current_core . retro_deinit =
runloop_st - > original_retro_deinit ;
runloop_st - > original_retro_deinit = NULL ;
}
if ( runloop_st - > original_retro_unload )
{
runloop_st - > current_core . retro_unload_game =
runloop_st - > original_retro_unload ;
runloop_st - > original_retro_unload = NULL ;
}
remove_input_state_hook ( runloop_st ) ;
}
static void runahead_destroy ( runloop_state_t * runloop_st )
{
mylist_destroy ( & runloop_st - > runahead_save_state_list ) ;
runahead_remove_hooks ( runloop_st ) ;
runloop_runahead_clear_variables ( runloop_st ) ;
}
static void unload_hook ( void )
{
runloop_state_t * runloop_st = & runloop_state ;
runahead_remove_hooks ( runloop_st ) ;
runahead_destroy ( runloop_st ) ;
runloop_secondary_core_destroy ( ) ;
if ( runloop_st - > current_core . retro_unload_game )
runloop_st - > current_core . retro_unload_game ( ) ;
runloop_st - > core_poll_type_override = POLL_TYPE_OVERRIDE_DONTCARE ;
}
static void runahead_deinit_hook ( void )
{
runloop_state_t * runloop_st = & runloop_state ;
runahead_remove_hooks ( runloop_st ) ;
runahead_destroy ( runloop_st ) ;
runloop_secondary_core_destroy ( ) ;
if ( runloop_st - > current_core . retro_deinit )
runloop_st - > current_core . retro_deinit ( ) ;
}
static void runahead_add_hooks ( runloop_state_t * runloop_st )
{
if ( ! runloop_st - > original_retro_deinit )
{
runloop_st - > original_retro_deinit =
runloop_st - > current_core . retro_deinit ;
runloop_st - > current_core . retro_deinit = runahead_deinit_hook ;
}
if ( ! runloop_st - > original_retro_unload )
{
runloop_st - > original_retro_unload = runloop_st - > current_core . retro_unload_game ;
runloop_st - > current_core . retro_unload_game = unload_hook ;
}
add_input_state_hook ( runloop_st ) ;
}
/* Runahead Code */
static void runahead_error ( runloop_state_t * runloop_st )
{
runloop_st - > runahead_available = false ;
mylist_destroy ( & runloop_st - > runahead_save_state_list ) ;
runahead_remove_hooks ( runloop_st ) ;
runloop_st - > runahead_save_state_size = 0 ;
runloop_st - > runahead_save_state_size_known = true ;
}
static bool runahead_create ( runloop_state_t * runloop_st )
{
/* get savestate size and allocate buffer */
retro_ctx_size_info_t info ;
video_driver_state_t * video_st = video_state_get_ptr ( ) ;
runloop_st - > request_fast_savestate = true ;
core_serialize_size ( & info ) ;
runloop_st - > request_fast_savestate = false ;
runahead_save_state_list_init ( runloop_st , info . size ) ;
video_st - > runahead_is_active = video_st - > active ;
if ( ( runloop_st - > runahead_save_state_size = = 0 ) | |
! runloop_st - > runahead_save_state_size_known )
{
runahead_error ( runloop_st ) ;
return false ;
}
runahead_add_hooks ( runloop_st ) ;
runloop_st - > runahead_force_input_dirty = true ;
if ( runloop_st - > runahead_save_state_list )
mylist_resize ( runloop_st - > runahead_save_state_list , 1 , true ) ;
return true ;
}
static bool runahead_save_state ( runloop_state_t * runloop_st )
{
retro_ctx_serialize_info_t * serialize_info ;
bool okay = false ;
if ( ! runloop_st - > runahead_save_state_list )
return false ;
serialize_info =
( retro_ctx_serialize_info_t * ) runloop_st - > runahead_save_state_list - > data [ 0 ] ;
runloop_st - > request_fast_savestate = true ;
okay = core_serialize ( serialize_info ) ;
runloop_st - > request_fast_savestate = false ;
if ( okay )
return true ;
runahead_error ( runloop_st ) ;
return false ;
}
static bool runahead_load_state ( runloop_state_t * runloop_st )
{
bool okay = false ;
retro_ctx_serialize_info_t * serialize_info =
( retro_ctx_serialize_info_t * )
runloop_st - > runahead_save_state_list - > data [ 0 ] ;
bool last_dirty = runloop_st - > input_is_dirty ;
runloop_st - > request_fast_savestate = true ;
/* calling core_unserialize has side effects with
* netplay ( it triggers transmitting your save state )
call retro_unserialize directly from the core instead */
okay = runloop_st - > current_core . retro_unserialize (
serialize_info - > data_const , serialize_info - > size ) ;
runloop_st - > request_fast_savestate = false ;
runloop_st - > input_is_dirty = last_dirty ;
if ( ! okay )
runahead_error ( runloop_st ) ;
return okay ;
}
# if HAVE_DYNAMIC
static bool runahead_load_state_secondary ( void )
{
bool okay = false ;
runloop_state_t * runloop_st = & runloop_state ;
settings_t * settings = config_get_ptr ( ) ;
retro_ctx_serialize_info_t * serialize_info =
( retro_ctx_serialize_info_t * ) runloop_st - > runahead_save_state_list - > data [ 0 ] ;
runloop_st - > request_fast_savestate = true ;
okay =
secondary_core_deserialize ( settings ,
serialize_info - > data_const , ( int ) serialize_info - > size ) ;
runloop_st - > request_fast_savestate = false ;
if ( ! okay )
{
runloop_st - > runahead_secondary_core_available = false ;
runahead_error ( runloop_st ) ;
return false ;
}
return true ;
}
# endif
static void runahead_core_run_use_last_input ( runloop_state_t * runloop_st )
{
struct retro_callbacks * cbs = & runloop_st - > retro_ctx ;
retro_input_poll_t old_poll_function = cbs - > poll_cb ;
retro_input_state_t old_input_function = cbs - > state_cb ;
cbs - > poll_cb = retro_input_poll_null ;
cbs - > state_cb = input_state_get_last ;
runloop_st - > current_core . retro_set_input_poll ( cbs - > poll_cb ) ;
runloop_st - > current_core . retro_set_input_state ( cbs - > state_cb ) ;
runloop_st - > current_core . retro_run ( ) ;
cbs - > poll_cb = old_poll_function ;
cbs - > state_cb = old_input_function ;
runloop_st - > current_core . retro_set_input_poll ( cbs - > poll_cb ) ;
runloop_st - > current_core . retro_set_input_state ( cbs - > state_cb ) ;
}
static void do_runahead (
runloop_state_t * runloop_st ,
int runahead_count ,
bool runahead_hide_warnings ,
bool use_secondary )
{
int frame_number = 0 ;
bool last_frame = false ;
bool suspended_frame = false ;
# if defined(HAVE_DYNAMIC) || defined(HAVE_DYLIB)
const bool have_dynamic = true ;
# else
const bool have_dynamic = false ;
# endif
video_driver_state_t
* video_st = video_state_get_ptr ( ) ;
uint64_t frame_count = video_st - > frame_count ;
audio_driver_state_t
* audio_st = audio_state_get_ptr ( ) ;
if ( runahead_count < = 0 | | ! runloop_st - > runahead_available )
goto force_input_dirty ;
if ( ! runloop_st - > runahead_save_state_size_known )
{
if ( ! runahead_create ( runloop_st ) )
{
if ( ! runahead_hide_warnings )
runloop_msg_queue_push ( msg_hash_to_str ( MSG_RUNAHEAD_CORE_DOES_NOT_SUPPORT_SAVESTATES ) , 0 , 2 * 60 , true , NULL , MESSAGE_QUEUE_ICON_DEFAULT , MESSAGE_QUEUE_CATEGORY_INFO ) ;
goto force_input_dirty ;
}
}
/* Check for GUI */
/* Hack: If we were in the GUI, force a resync. */
if ( frame_count ! = runloop_st - > runahead_last_frame_count + 1 )
runloop_st - > runahead_force_input_dirty = true ;
runloop_st - > runahead_last_frame_count = frame_count ;
if ( ! use_secondary
| | ! have_dynamic
| | ! runloop_st - > runahead_secondary_core_available )
{
/* TODO: multiple savestates for higher performance
* when not using secondary core */
for ( frame_number = 0 ; frame_number < = runahead_count ; frame_number + + )
{
last_frame = frame_number = = runahead_count ;
suspended_frame = ! last_frame ;
if ( suspended_frame )
{
audio_st - > suspended = true ;
video_st - > active = false ;
}
if ( frame_number = = 0 )
core_run ( ) ;
else
runahead_core_run_use_last_input ( runloop_st ) ;
if ( suspended_frame )
{
video_st - > active = video_st - > runahead_is_active ;
audio_st - > suspended = false ;
}
if ( frame_number = = 0 )
{
if ( ! runahead_save_state ( runloop_st ) )
{
runloop_msg_queue_push ( msg_hash_to_str ( MSG_RUNAHEAD_FAILED_TO_SAVE_STATE ) , 0 , 3 * 60 , true , NULL , MESSAGE_QUEUE_ICON_DEFAULT , MESSAGE_QUEUE_CATEGORY_INFO ) ;
return ;
}
}
if ( last_frame )
{
if ( ! runahead_load_state ( runloop_st ) )
{
runloop_msg_queue_push ( msg_hash_to_str ( MSG_RUNAHEAD_FAILED_TO_LOAD_STATE ) , 0 , 3 * 60 , true , NULL , MESSAGE_QUEUE_ICON_DEFAULT , MESSAGE_QUEUE_CATEGORY_INFO ) ;
return ;
}
}
}
}
else
{
# if HAVE_DYNAMIC
if ( ! secondary_core_ensure_exists ( config_get_ptr ( ) ) )
{
runloop_secondary_core_destroy ( ) ;
runloop_st - > runahead_secondary_core_available = false ;
runloop_msg_queue_push ( msg_hash_to_str ( MSG_RUNAHEAD_FAILED_TO_CREATE_SECONDARY_INSTANCE ) , 0 , 3 * 60 , true , NULL , MESSAGE_QUEUE_ICON_DEFAULT , MESSAGE_QUEUE_CATEGORY_INFO ) ;
goto force_input_dirty ;
}
/* run main core with video suspended */
video_st - > active = false ;
core_run ( ) ;
video_st - > active = video_st - > runahead_is_active ;
if ( runloop_st - > input_is_dirty
| | runloop_st - > runahead_force_input_dirty )
{
runloop_st - > input_is_dirty = false ;
if ( ! runahead_save_state ( runloop_st ) )
{
runloop_msg_queue_push ( msg_hash_to_str ( MSG_RUNAHEAD_FAILED_TO_SAVE_STATE ) , 0 , 3 * 60 , true , NULL , MESSAGE_QUEUE_ICON_DEFAULT , MESSAGE_QUEUE_CATEGORY_INFO ) ;
return ;
}
if ( ! runahead_load_state_secondary ( ) )
{
runloop_msg_queue_push ( msg_hash_to_str ( MSG_RUNAHEAD_FAILED_TO_LOAD_STATE ) , 0 , 3 * 60 , true , NULL , MESSAGE_QUEUE_ICON_DEFAULT , MESSAGE_QUEUE_CATEGORY_INFO ) ;
return ;
}
for ( frame_number = 0 ; frame_number < runahead_count - 1 ; frame_number + + )
{
video_st - > active = false ;
audio_st - > suspended = true ;
audio_st - > hard_disable = true ;
runloop_st - > runahead_secondary_core_available =
secondary_core_run_use_last_input ( ) ;
audio_st - > hard_disable = false ;
audio_st - > suspended = false ;
video_st - > active = video_st - > runahead_is_active ;
}
}
audio_st - > suspended = true ;
audio_st - > hard_disable = true ;
runloop_st - > runahead_secondary_core_available =
secondary_core_run_use_last_input ( ) ;
audio_st - > hard_disable = false ;
audio_st - > suspended = false ;
# endif
}
runloop_st - > runahead_force_input_dirty = false ;
return ;
force_input_dirty :
core_run ( ) ;
runloop_st - > runahead_force_input_dirty = true ;
}
# endif
static retro_time_t runloop_core_runtime_tick (
runloop_state_t * runloop_st ,
float slowmotion_ratio ,
retro_time_t current_time )
{
video_driver_state_t * video_st = video_state_get_ptr ( ) ;
retro_time_t frame_time =
( 1.0 / video_st - > av_info . timing . fps ) * 1000000 ;
bool runloop_slowmotion = runloop_st - > slowmotion ;
bool runloop_fastmotion = runloop_st - > fastmotion ;
/* Account for slow motion */
if ( runloop_slowmotion )
return ( retro_time_t ) ( ( double ) frame_time * slowmotion_ratio ) ;
/* Account for fast forward */
if ( runloop_fastmotion )
{
/* Doing it this way means we miss the first frame after
* turning fast forward on , but it saves the overhead of
* having to do :
* retro_time_t current_usec = cpu_features_get_time_usec ( ) ;
* core_runtime_last = current_usec ;
* every frame when fast forward is off . */
retro_time_t current_usec = current_time ;
retro_time_t potential_frame_time = current_usec -
runloop_st - > core_runtime_last ;
runloop_st - > core_runtime_last = current_usec ;
if ( potential_frame_time < frame_time )
return potential_frame_time ;
}
return frame_time ;
}
static bool core_unload_game ( void )
{
runloop_state_t * runloop_st = & runloop_state ;
video_driver_free_hw_context ( ) ;
video_driver_set_cached_frame_ptr ( NULL ) ;
if ( runloop_st - > current_core . game_loaded )
{
RARCH_LOG ( " [Core]: Unloading game.. \n " ) ;
runloop_st - > current_core . retro_unload_game ( ) ;
runloop_st - > core_poll_type_override = POLL_TYPE_OVERRIDE_DONTCARE ;
runloop_st - > current_core . game_loaded = false ;
}
audio_driver_stop ( ) ;
return true ;
}
static void runloop_apply_fastmotion_override ( runloop_state_t * runloop_st , settings_t * settings )
{
video_driver_state_t * video_st = video_state_get_ptr ( ) ;
bool frame_time_counter_reset_after_fastforwarding = settings ?
settings - > bools . frame_time_counter_reset_after_fastforwarding : false ;
float fastforward_ratio_default = settings ?
settings - > floats . fastforward_ratio : 0.0f ;
float fastforward_ratio_last =
( runloop_st - > fastmotion_override . current . fastforward & &
( runloop_st - > fastmotion_override . current . ratio > = 0.0f ) ) ?
runloop_st - > fastmotion_override . current . ratio :
fastforward_ratio_default ;
float fastforward_ratio_current ;
memcpy ( & runloop_st - > fastmotion_override . current ,
& runloop_st - > fastmotion_override . next ,
sizeof ( runloop_st - > fastmotion_override . current ) ) ;
/* Check if 'fastmotion' state has changed */
if ( runloop_st - > fastmotion ! =
runloop_st - > fastmotion_override . current . fastforward )
{
input_driver_state_t * input_st = input_state_get_ptr ( ) ;
runloop_st - > fastmotion =
runloop_st - > fastmotion_override . current . fastforward ;
if ( input_st )
{
if ( runloop_st - > fastmotion )
input_st - > nonblocking_flag = true ;
else
input_st - > nonblocking_flag = false ;
}
if ( ! runloop_st - > fastmotion )
runloop_st - > fastforward_after_frames = 1 ;
driver_set_nonblock_state ( ) ;
/* Reset frame time counter when toggling
* fast - forward off , if required */
if ( ! runloop_st - > fastmotion & &
frame_time_counter_reset_after_fastforwarding )
video_st - > frame_time_count = 0 ;
/* Ensure fast forward widget is disabled when
* toggling fast - forward off
* ( required if RETRO_ENVIRONMENT_SET_FASTFORWARDING_OVERRIDE
* is called during core de - initialisation ) */
# if defined(HAVE_GFX_WIDGETS)
if ( dispwidget_get_ptr ( ) - > active & & ! runloop_st - > fastmotion )
video_st - > widgets_fast_forward = false ;
# endif
}
/* Update frame limit, if required */
fastforward_ratio_current = ( runloop_st - > fastmotion_override . current . fastforward & &
( runloop_st - > fastmotion_override . current . ratio > = 0.0f ) ) ?
runloop_st - > fastmotion_override . current . ratio :
fastforward_ratio_default ;
if ( fastforward_ratio_current ! = fastforward_ratio_last )
runloop_st - > frame_limit_minimum_time =
runloop_set_frame_limit ( & video_st - > av_info ,
fastforward_ratio_current ) ;
}
void runloop_event_deinit_core ( void )
{
video_driver_state_t
* video_st = video_state_get_ptr ( ) ;
runloop_state_t * runloop_st = & runloop_state ;
settings_t * settings = config_get_ptr ( ) ;
core_unload_game ( ) ;
video_driver_set_cached_frame_ptr ( NULL ) ;
if ( runloop_st - > current_core . inited )
{
RARCH_LOG ( " [Core]: Unloading core.. \n " ) ;
runloop_st - > current_core . retro_deinit ( ) ;
}
/* retro_deinit() may call
* RETRO_ENVIRONMENT_SET_FASTFORWARDING_OVERRIDE
* ( i . e . to ensure that fastforwarding is
* disabled on core close )
* > Check for any pending updates */
if ( runloop_st - > fastmotion_override . pending )
{
runloop_apply_fastmotion_override ( runloop_st ,
settings ) ;
runloop_st - > fastmotion_override . pending = false ;
}
RARCH_LOG ( " [Core]: Unloading core symbols.. \n " ) ;
uninit_libretro_symbols ( & runloop_st - > current_core ) ;
runloop_st - > current_core . symbols_inited = false ;
/* Restore original refresh rate, if it has been changed
* automatically in SET_SYSTEM_AV_INFO */
if ( video_st - > video_refresh_rate_original )
video_display_server_restore_refresh_rate ( ) ;
/* Recalibrate frame delay target */
if ( settings - > bools . video_frame_delay_auto )
video_st - > frame_delay_target = 0 ;
driver_uninit ( DRIVERS_CMD_ALL ) ;
# ifdef HAVE_CONFIGFILE
if ( runloop_st - > overrides_active )
{
/* Reload the original config */
config_unload_override ( ) ;
runloop_st - > overrides_active = false ;
}
# endif
# if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
runloop_st - > runtime_shader_preset_path [ 0 ] = ' \0 ' ;
# endif
if ( runloop_st - > remaps_core_active
| | runloop_st - > remaps_content_dir_active
| | runloop_st - > remaps_game_active
)
{
input_remapping_deinit ( ) ;
input_remapping_set_defaults ( true ) ;
}
else
input_remapping_restore_global_config ( true ) ;
}
static void runloop_path_init_savefile_internal ( void )
{
runloop_state_t * runloop_st = & runloop_state ;
path_deinit_savefile ( ) ;
path_init_savefile_new ( ) ;
if ( ! runloop_path_init_subsystem ( ) )
path_init_savefile_rtc ( runloop_st - > name . savefile ) ;
}
static bool event_init_content (
settings_t * settings ,
input_driver_state_t * input_st )
{
runloop_state_t * runloop_st = & runloop_state ;
bool contentless = false ;
bool is_inited = false ;
# ifdef HAVE_CHEEVOS
bool cheevos_enable =
settings - > bools . cheevos_enable ;
bool cheevos_hardcore_mode_enable =
settings - > bools . cheevos_hardcore_mode_enable ;
# endif
const enum rarch_core_type current_core_type = runloop_st - > current_core_type ;
content_get_status ( & contentless , & is_inited ) ;
runloop_st - > use_sram = ( current_core_type = = CORE_TYPE_PLAIN ) ;
/* No content to be loaded for dummy core,
* just successfully exit . */
if ( current_core_type = = CORE_TYPE_DUMMY )
return true ;
content_set_subsystem_info ( ) ;
content_get_status ( & contentless , & is_inited ) ;
/* If core is contentless, just initialise SRAM
* interface , otherwise fill all content - related
* paths */
if ( contentless )
runloop_path_init_savefile_internal ( ) ;
else
runloop_path_fill_names ( ) ;
if ( ! content_init ( ) )
return false ;
command_event_set_savestate_auto_index ( settings ) ;
if ( ! event_load_save_files ( runloop_st - > is_sram_load_disabled ) )
RARCH_LOG ( " [SRAM]: %s \n " ,
msg_hash_to_str ( MSG_SKIPPING_SRAM_LOAD ) ) ;
/*
Since the operations are asynchronous we can ' t
guarantee users will not use auto_load_state to cheat on
achievements so we forbid auto_load_state from happening
if cheevos_enable and cheevos_hardcode_mode_enable
are true .
*/
# ifdef HAVE_CHEEVOS
if ( ! cheevos_enable | | ! cheevos_hardcore_mode_enable )
# endif
if ( settings - > bools . savestate_auto_load )
command_event_load_auto_state ( ) ;
# ifdef HAVE_BSV_MOVIE
bsv_movie_deinit ( input_st ) ;
if ( bsv_movie_init ( input_st ) )
{
/* Set granularity upon success */
configuration_set_uint ( settings ,
settings - > uints . rewind_granularity , 1 ) ;
}
# endif
command_event ( CMD_EVENT_NETPLAY_INIT , NULL ) ;
return true ;
}
static void runloop_runtime_log_init ( runloop_state_t * runloop_st )
{
const char * content_path = path_get ( RARCH_PATH_CONTENT ) ;
const char * core_path = path_get ( RARCH_PATH_CORE ) ;
runloop_st - > core_runtime_last = cpu_features_get_time_usec ( ) ;
runloop_st - > core_runtime_usec = 0 ;
/* Have to cache content and core path here, otherwise
* logging fails if new content is loaded without
* closing existing content
* i . e . RARCH_PATH_CONTENT and RARCH_PATH_CORE get
* updated when the new content is loaded , which
* happens * before * command_event_runtime_log_deinit
* - > using RARCH_PATH_CONTENT and RARCH_PATH_CORE
* directly in command_event_runtime_log_deinit
* can therefore lead to the runtime of the currently
* loaded content getting written to the * new *
* content ' s log file . . . */
memset ( runloop_st - > runtime_content_path ,
0 , sizeof ( runloop_st - > runtime_content_path ) ) ;
memset ( runloop_st - > runtime_core_path ,
0 , sizeof ( runloop_st - > runtime_core_path ) ) ;
if ( ! string_is_empty ( content_path ) )
strlcpy ( runloop_st - > runtime_content_path ,
content_path ,
sizeof ( runloop_st - > runtime_content_path ) ) ;
if ( ! string_is_empty ( core_path ) )
strlcpy ( runloop_st - > runtime_core_path ,
core_path ,
sizeof ( runloop_st - > runtime_core_path ) ) ;
}
float runloop_set_frame_limit (
const struct retro_system_av_info * av_info ,
float fastforward_ratio )
{
if ( fastforward_ratio < 1.0f )
return 0.0f ;
return ( retro_time_t ) roundf ( 1000000.0f /
( av_info - > timing . fps * fastforward_ratio ) ) ;
}
float runloop_get_fastforward_ratio (
settings_t * settings ,
struct retro_fastforwarding_override * fastmotion_override )
{
if ( fastmotion_override - > fastforward
& & ( fastmotion_override - > ratio > = 0.0f ) )
return fastmotion_override - > ratio ;
return settings - > floats . fastforward_ratio ;
}
unsigned int retroarch_get_rotation ( void )
{
settings_t * settings = config_get_ptr ( ) ;
unsigned video_rotation = settings - > uints . video_rotation ;
return video_rotation + runloop_state . system . rotation ;
}
static void retro_run_null ( void ) { } /* Stub function callback impl. */
static bool core_verify_api_version ( void )
{
runloop_state_t * runloop_st = & runloop_state ;
unsigned api_version = runloop_st - > current_core . retro_api_version ( ) ;
RARCH_LOG ( " %s: %u \n %s %s: %u \n " ,
msg_hash_to_str ( MSG_VERSION_OF_LIBRETRO_API ) ,
api_version ,
FILE_PATH_LOG_INFO ,
msg_hash_to_str ( MSG_COMPILED_AGAINST_API ) ,
RETRO_API_VERSION
) ;
if ( api_version ! = RETRO_API_VERSION )
{
RARCH_WARN ( " %s \n " , msg_hash_to_str ( MSG_LIBRETRO_ABI_BREAK ) ) ;
return false ;
}
return true ;
}
static int16_t core_input_state_poll_late ( unsigned port ,
unsigned device , unsigned idx , unsigned id )
{
runloop_state_t * runloop_st = & runloop_state ;
if ( ! runloop_st - > current_core . input_polled )
input_driver_poll ( ) ;
runloop_st - > current_core . input_polled = true ;
return input_driver_state_wrapper ( port , device , idx , id ) ;
}
static void core_input_state_poll_maybe ( void )
{
runloop_state_t * runloop_st = & runloop_state ;
const enum poll_type_override_t
core_poll_type_override = runloop_st - > core_poll_type_override ;
unsigned new_poll_type = ( core_poll_type_override > POLL_TYPE_OVERRIDE_DONTCARE )
? ( core_poll_type_override - 1 )
: runloop_st - > current_core . poll_type ;
if ( new_poll_type = = POLL_TYPE_NORMAL )
input_driver_poll ( ) ;
}
static retro_input_state_t core_input_state_poll_return_cb ( void )
{
runloop_state_t * runloop_st = & runloop_state ;
const enum poll_type_override_t
core_poll_type_override = runloop_st - > core_poll_type_override ;
unsigned new_poll_type = ( core_poll_type_override > POLL_TYPE_OVERRIDE_DONTCARE )
? ( core_poll_type_override - 1 )
: runloop_st - > current_core . poll_type ;
if ( new_poll_type = = POLL_TYPE_LATE )
return core_input_state_poll_late ;
return input_driver_state_wrapper ;
}
/**
* core_init_libretro_cbs :
* @ data : pointer to retro_callbacks object
*
* Initializes libretro callbacks , and binds the libretro callbacks
* to default callback functions .
* */
static bool core_init_libretro_cbs ( struct retro_callbacks * cbs )
{
runloop_state_t * runloop_st = & runloop_state ;
retro_input_state_t state_cb = core_input_state_poll_return_cb ( ) ;
runloop_st - > current_core . retro_set_video_refresh ( video_driver_frame ) ;
runloop_st - > current_core . retro_set_audio_sample ( audio_driver_sample ) ;
runloop_st - > current_core . retro_set_audio_sample_batch ( audio_driver_sample_batch ) ;
runloop_st - > current_core . retro_set_input_state ( state_cb ) ;
runloop_st - > current_core . retro_set_input_poll ( core_input_state_poll_maybe ) ;
core_set_default_callbacks ( cbs ) ;
# ifdef HAVE_NETWORKING
if ( ! netplay_driver_ctl ( RARCH_NETPLAY_CTL_IS_DATA_INITED , NULL ) )
return true ;
core_set_netplay_callbacks ( ) ;
# endif
return true ;
}
static bool core_load ( unsigned poll_type_behavior )
{
video_driver_state_t * video_st = video_state_get_ptr ( ) ;
runloop_state_t * runloop_st = & runloop_state ;
runloop_st - > current_core . poll_type = poll_type_behavior ;
if ( ! core_verify_api_version ( ) )
return false ;
if ( ! core_init_libretro_cbs ( & runloop_st - > retro_ctx ) )
return false ;
runloop_st - > current_core . retro_get_system_av_info ( & video_st - > av_info ) ;
return true ;
}
bool runloop_event_init_core (
settings_t * settings ,
void * input_data ,
enum rarch_core_type type )
{
runloop_state_t * runloop_st = & runloop_state ;
input_driver_state_t * input_st = ( input_driver_state_t * ) input_data ;
video_driver_state_t * video_st = video_state_get_ptr ( ) ;
# ifdef HAVE_CONFIGFILE
bool auto_overrides_enable = settings - > bools . auto_overrides_enable ;
bool auto_remaps_enable = false ;
const char * dir_input_remapping = NULL ;
# endif
bool show_set_initial_disk_msg = false ;
unsigned poll_type_behavior = 0 ;
float fastforward_ratio = 0.0f ;
rarch_system_info_t * sys_info = & runloop_st - > system ;
if ( ! init_libretro_symbols ( runloop_st ,
type , & runloop_st - > current_core ) )
return false ;
if ( ! runloop_st - > current_core . retro_run )
runloop_st - > current_core . retro_run = retro_run_null ;
runloop_st - > current_core . symbols_inited = true ;
runloop_st - > current_core . retro_get_system_info ( & sys_info - > info ) ;
if ( ! sys_info - > info . library_name )
sys_info - > info . library_name = msg_hash_to_str ( MSG_UNKNOWN ) ;
if ( ! sys_info - > info . library_version )
sys_info - > info . library_version = " v0 " ;
fill_pathname_join_concat_noext (
video_st - > title_buf ,
msg_hash_to_str ( MSG_PROGRAM ) ,
" " ,
sys_info - > info . library_name ,
sizeof ( video_st - > title_buf ) ) ;
strlcat ( video_st - > title_buf , " " ,
sizeof ( video_st - > title_buf ) ) ;
strlcat ( video_st - > title_buf ,
sys_info - > info . library_version ,
sizeof ( video_st - > title_buf ) ) ;
strlcpy ( sys_info - > valid_extensions ,
sys_info - > info . valid_extensions ?
sys_info - > info . valid_extensions : DEFAULT_EXT ,
sizeof ( sys_info - > valid_extensions ) ) ;
# ifdef HAVE_CONFIGFILE
if ( auto_overrides_enable )
runloop_st - > overrides_active =
config_load_override ( & runloop_st - > system ) ;
# endif
/* Cannot access these settings-related parameters
* until * after * config overrides have been loaded */
# ifdef HAVE_CONFIGFILE
auto_remaps_enable = settings - > bools . auto_remaps_enable ;
dir_input_remapping = settings - > paths . directory_input_remapping ;
# endif
show_set_initial_disk_msg = settings - > bools . notification_show_set_initial_disk ;
poll_type_behavior = settings - > uints . input_poll_type_behavior ;
fastforward_ratio = runloop_get_fastforward_ratio (
settings , & runloop_st - > fastmotion_override . current ) ;
# ifdef HAVE_CHEEVOS
/* assume the core supports achievements unless it tells us otherwise */
rcheevos_set_support_cheevos ( true ) ;
# endif
/* Load auto-shaders on the next occasion */
# if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
video_st - > shader_presets_need_reload = true ;
runloop_st - > shader_delay_timer . timer_begin = false ; /* not initialized */
runloop_st - > shader_delay_timer . timer_end = false ; /* not expired */
# endif
/* reset video format to libretro's default */
video_st - > pix_fmt = RETRO_PIXEL_FORMAT_0RGB1555 ;
runloop_st - > current_core . retro_set_environment ( runloop_environment_cb ) ;
/* Load any input remap files
* > Note that we always cache the current global
* input settings when initialising a core
* ( regardless of whether remap files are loaded )
* so settings can be restored when the core is
* unloaded - i . e . core remapping options modified
* at runtime should not ' bleed through ' into the
* master config file */
input_remapping_cache_global_config ( ) ;
# ifdef HAVE_CONFIGFILE
if ( auto_remaps_enable )
config_load_remap ( dir_input_remapping , & runloop_st - > system ) ;
# endif
/* Per-core saves: reset redirection paths */
retroarch_path_set_redirect ( settings ) ;
video_driver_set_cached_frame_ptr ( NULL ) ;
runloop_st - > current_core . retro_init ( ) ;
runloop_st - > current_core . inited = true ;
/* Attempt to set initial disk index */
disk_control_set_initial_index (
& sys_info - > disk_control ,
path_get ( RARCH_PATH_CONTENT ) ,
runloop_st - > savefile_dir ) ;
if ( ! event_init_content ( settings , input_st ) )
{
runloop_st - > core_running = false ;
return false ;
}
/* Verify that initial disk index was set correctly */
disk_control_verify_initial_index ( & sys_info - > disk_control ,
show_set_initial_disk_msg ) ;
if ( ! core_load ( poll_type_behavior ) )
return false ;
runloop_st - > frame_limit_minimum_time =
runloop_set_frame_limit ( & video_st - > av_info ,
fastforward_ratio ) ;
runloop_st - > frame_limit_last_time = cpu_features_get_time_usec ( ) ;
runloop_runtime_log_init ( runloop_st ) ;
return true ;
}
# ifdef HAVE_RUNAHEAD
void runloop_runahead_clear_variables ( runloop_state_t * runloop_st )
{
video_driver_state_t
* video_st = video_state_get_ptr ( ) ;
runloop_st - > runahead_save_state_size = 0 ;
runloop_st - > runahead_save_state_size_known = false ;
video_st - > runahead_is_active = true ;
runloop_st - > runahead_available = true ;
runloop_st - > runahead_secondary_core_available = true ;
runloop_st - > runahead_force_input_dirty = true ;
runloop_st - > runahead_last_frame_count = 0 ;
}
# endif
void runloop_pause_checks ( void )
{
# ifdef HAVE_DISCORD
discord_userdata_t userdata ;
# endif
runloop_state_t * runloop_st = & runloop_state ;
bool is_paused = runloop_st - > paused ;
bool is_idle = runloop_st - > idle ;
# if defined(HAVE_GFX_WIDGETS)
video_driver_state_t * video_st = video_state_get_ptr ( ) ;
bool widgets_active = dispwidget_get_ptr ( ) - > active ;
if ( widgets_active )
video_st - > widgets_paused = is_paused ;
# endif
if ( is_paused )
{
# if defined(HAVE_GFX_WIDGETS)
if ( ! widgets_active )
# endif
runloop_msg_queue_push ( msg_hash_to_str ( MSG_PAUSED ) , 1 ,
1 , true ,
NULL , MESSAGE_QUEUE_ICON_DEFAULT , MESSAGE_QUEUE_CATEGORY_INFO ) ;
if ( ! is_idle )
video_driver_cached_frame ( ) ;
# ifdef HAVE_DISCORD
userdata . status = DISCORD_PRESENCE_GAME_PAUSED ;
command_event ( CMD_EVENT_DISCORD_UPDATE , & userdata ) ;
# endif
# ifndef HAVE_LAKKA_SWITCH
# ifdef HAVE_LAKKA
set_cpu_scaling_signal ( CPUSCALING_EVENT_FOCUS_MENU ) ;
# endif
# endif /* #ifndef HAVE_LAKKA_SWITCH */
}
else
{
# ifndef HAVE_LAKKA_SWITCH
# ifdef HAVE_LAKKA
set_cpu_scaling_signal ( CPUSCALING_EVENT_FOCUS_CORE ) ;
# endif
# endif /* #ifndef HAVE_LAKKA_SWITCH */
}
# if defined(HAVE_TRANSLATE) && defined(HAVE_GFX_WIDGETS)
if ( dispwidget_get_ptr ( ) - > ai_service_overlay_state = = 1 )
gfx_widgets_ai_service_overlay_unload ( ) ;
# endif
}
void runloop_frame_time_free ( void )
{
runloop_state_t * runloop_st = & runloop_state ;
memset ( & runloop_st - > frame_time , 0 ,
sizeof ( struct retro_frame_time_callback ) ) ;
runloop_st - > frame_time_last = 0 ;
runloop_st - > max_frames = 0 ;
}
void runloop_audio_buffer_status_free ( void )
{
runloop_state_t * runloop_st = & runloop_state ;
memset ( & runloop_st - > audio_buffer_status , 0 ,
sizeof ( struct retro_audio_buffer_status_callback ) ) ;
runloop_st - > audio_latency = 0 ;
}
void runloop_fastmotion_override_free ( void )
{
runloop_state_t
* runloop_st = & runloop_state ;
video_driver_state_t
* video_st = video_state_get_ptr ( ) ;
settings_t * settings = config_get_ptr ( ) ;
float fastforward_ratio = settings - > floats . fastforward_ratio ;
bool reset_frame_limit = runloop_st - > fastmotion_override . current . fastforward & &
( runloop_st - > fastmotion_override . current . ratio > = 0.0f ) & &
( runloop_st - > fastmotion_override . current . ratio ! = fastforward_ratio ) ;
runloop_st - > fastmotion_override . current . ratio = 0.0f ;
runloop_st - > fastmotion_override . current . fastforward = false ;
runloop_st - > fastmotion_override . current . notification = false ;
runloop_st - > fastmotion_override . current . inhibit_toggle = false ;
runloop_st - > fastmotion_override . next . ratio = 0.0f ;
runloop_st - > fastmotion_override . next . fastforward = false ;
runloop_st - > fastmotion_override . next . notification = false ;
runloop_st - > fastmotion_override . next . inhibit_toggle = false ;
runloop_st - > fastmotion_override . pending = false ;
if ( reset_frame_limit )
runloop_st - > frame_limit_minimum_time =
runloop_set_frame_limit ( & video_st - > av_info , fastforward_ratio ) ;
}
void runloop_core_options_cb_free ( void )
{
runloop_state_t
* runloop_st = & runloop_state ;
/* Only a single core options callback is used at present */
runloop_st - > core_options_callback . update_display = NULL ;
}
struct string_list * path_get_subsystem_list ( void )
{
runloop_state_t * runloop_st = & runloop_state ;
return runloop_st - > subsystem_fullpaths ;
}
bool runloop_path_init_subsystem ( void )
{
unsigned i , j ;
const struct retro_subsystem_info * info = NULL ;
runloop_state_t * runloop_st = & runloop_state ;
rarch_system_info_t * system = & runloop_st - > system ;
bool subsystem_path_empty = path_is_empty ( RARCH_PATH_SUBSYSTEM ) ;
const char * savefile_dir = runloop_st - > savefile_dir ;
if ( ! system | | subsystem_path_empty )
return false ;
/* For subsystems, we know exactly which RAM types are supported. */
info = libretro_find_subsystem_info (
system - > subsystem . data ,
system - > subsystem . size ,
path_get ( RARCH_PATH_SUBSYSTEM ) ) ;
/* We'll handle this error gracefully later. */
if ( info )
{
unsigned num_content = MIN ( info - > num_roms ,
subsystem_path_empty ?
0 : ( unsigned ) runloop_st - > subsystem_fullpaths - > size ) ;
for ( i = 0 ; i < num_content ; i + + )
{
for ( j = 0 ; j < info - > roms [ i ] . num_memory ; j + + )
{
char ext [ 32 ] ;
union string_list_elem_attr attr ;
char savename [ PATH_MAX_LENGTH ] ;
char path [ PATH_MAX_LENGTH ] ;
const struct retro_subsystem_memory_info * mem =
( const struct retro_subsystem_memory_info * )
& info - > roms [ i ] . memory [ j ] ;
path [ 0 ] = ext [ 0 ] = ' \0 ' ;
ext [ 0 ] = ' . ' ;
ext [ 1 ] = ' \0 ' ;
strlcat ( ext , mem - > extension , sizeof ( ext ) ) ;
strlcpy ( savename ,
runloop_st - > subsystem_fullpaths - > elems [ i ] . data ,
sizeof ( savename ) ) ;
path_remove_extension ( savename ) ;
if ( path_is_directory ( savefile_dir ) )
{
/* Use SRAM dir */
/* Redirect content fullpath to save directory. */
strlcpy ( path , savefile_dir , sizeof ( path ) ) ;
fill_pathname_dir ( path , savename , ext , sizeof ( path ) ) ;
}
else
fill_pathname ( path , savename , ext , sizeof ( path ) ) ;
RARCH_LOG ( " %s \" %s \" . \n " ,
msg_hash_to_str ( MSG_REDIRECTING_SAVEFILE_TO ) ,
path ) ;
attr . i = mem - > type ;
string_list_append ( ( struct string_list * ) savefile_ptr_get ( ) ,
path , attr ) ;
}
}
}
/* Let other relevant paths be inferred
from the main SRAM location . */
if ( ! retroarch_override_setting_is_set (
RARCH_OVERRIDE_SETTING_SAVE_PATH , NULL ) )
fill_pathname_noext ( runloop_st - > name . savefile ,
runloop_st - > runtime_content_path_basename ,
" .srm " ,
sizeof ( runloop_st - > name . savefile ) ) ;
if ( path_is_directory ( runloop_st - > name . savefile ) )
{
fill_pathname_dir ( runloop_st - > name . savefile ,
runloop_st - > runtime_content_path_basename ,
" .srm " ,
sizeof ( runloop_st - > name . savefile ) ) ;
RARCH_LOG ( " %s \" %s \" . \n " ,
msg_hash_to_str ( MSG_REDIRECTING_SAVEFILE_TO ) ,
runloop_st - > name . savefile ) ;
}
return true ;
}
void runloop_path_fill_names ( void )
{
runloop_state_t * runloop_st = & runloop_state ;
# ifdef HAVE_BSV_MOVIE
input_driver_state_t * input_st = input_state_get_ptr ( ) ;
# endif
runloop_path_init_savefile_internal ( ) ;
# ifdef HAVE_BSV_MOVIE
strlcpy ( input_st - > bsv_movie_state . movie_path ,
runloop_st - > name . savefile ,
sizeof ( input_st - > bsv_movie_state . movie_path ) ) ;
# endif
if ( string_is_empty ( runloop_st - > runtime_content_path_basename ) )
return ;
if ( string_is_empty ( runloop_st - > name . ups ) )
fill_pathname_noext ( runloop_st - > name . ups ,
runloop_st - > runtime_content_path_basename ,
" .ups " ,
sizeof ( runloop_st - > name . ups ) ) ;
if ( string_is_empty ( runloop_st - > name . bps ) )
fill_pathname_noext ( runloop_st - > name . bps ,
runloop_st - > runtime_content_path_basename ,
" .bps " ,
sizeof ( runloop_st - > name . bps ) ) ;
if ( string_is_empty ( runloop_st - > name . ips ) )
fill_pathname_noext ( runloop_st - > name . ips ,
runloop_st - > runtime_content_path_basename ,
" .ips " ,
sizeof ( runloop_st - > name . ips ) ) ;
}
2021-11-22 02:33:58 +00:00
void runloop_path_init_savefile ( void )
2021-11-22 02:27:23 +00:00
{
2021-11-22 02:33:58 +00:00
runloop_state_t * runloop_st = & runloop_state ;
2021-11-22 02:27:23 +00:00
bool should_sram_be_used = runloop_st - > use_sram
& & ! runloop_st - > is_sram_save_disabled ;
runloop_st - > use_sram = should_sram_be_used ;
if ( ! runloop_st - > use_sram )
{
RARCH_LOG ( " [SRAM]: %s \n " ,
msg_hash_to_str ( MSG_SRAM_WILL_NOT_BE_SAVED ) ) ;
return ;
}
command_event ( CMD_EVENT_AUTOSAVE_INIT , NULL ) ;
}
/* Creates folder and core options stub file for subsequent runs */
bool core_options_create_override ( bool game_specific )
{
char options_path [ PATH_MAX_LENGTH ] ;
runloop_state_t * runloop_st = & runloop_state ;
config_file_t * conf = NULL ;
options_path [ 0 ] = ' \0 ' ;
if ( ! game_specific )
{
/* Sanity check - cannot create a folder-specific
* override if a game - specific override is
* already active */
if ( runloop_st - > game_options_active )
goto error ;
/* Get options file path (folder-specific) */
if ( ! validate_folder_options (
options_path ,
sizeof ( options_path ) , true ) )
goto error ;
}
else
{
/* Get options file path (game-specific) */
if ( ! validate_game_options (
runloop_st - > system . info . library_name ,
options_path ,
sizeof ( options_path ) , true ) )
goto error ;
}
/* Open config file */
if ( ! ( conf = config_file_new_from_path_to_string ( options_path ) ) )
if ( ! ( conf = config_file_new_alloc ( ) ) )
goto error ;
/* Write config file */
core_option_manager_flush ( runloop_st - > core_options , conf ) ;
if ( ! config_file_write ( conf , options_path , true ) )
goto error ;
runloop_msg_queue_push (
msg_hash_to_str ( MSG_CORE_OPTIONS_FILE_CREATED_SUCCESSFULLY ) ,
1 , 100 , true ,
NULL , MESSAGE_QUEUE_ICON_DEFAULT , MESSAGE_QUEUE_CATEGORY_INFO ) ;
path_set ( RARCH_PATH_CORE_OPTIONS , options_path ) ;
runloop_st - > game_options_active = game_specific ;
runloop_st - > folder_options_active = ! game_specific ;
config_file_free ( conf ) ;
return true ;
error :
runloop_msg_queue_push (
msg_hash_to_str ( MSG_ERROR_SAVING_CORE_OPTIONS_FILE ) ,
1 , 100 , true ,
NULL , MESSAGE_QUEUE_ICON_DEFAULT , MESSAGE_QUEUE_CATEGORY_INFO ) ;
if ( conf )
config_file_free ( conf ) ;
return false ;
}
bool core_options_remove_override ( bool game_specific )
{
char new_options_path [ PATH_MAX_LENGTH ] ;
runloop_state_t * runloop_st = & runloop_state ;
settings_t * settings = config_get_ptr ( ) ;
core_option_manager_t * coreopts = runloop_st - > core_options ;
bool per_core_options = ! settings - > bools . global_core_options ;
const char * path_core_options = settings - > paths . path_core_options ;
const char * current_options_path = NULL ;
config_file_t * conf = NULL ;
bool folder_options_active = false ;
new_options_path [ 0 ] = ' \0 ' ;
/* Sanity check 1 - if there are no core options
* or no overrides are active , there is nothing to do */
if ( ! coreopts | |
( ! runloop_st - > game_options_active
& & ! runloop_st - > folder_options_active )
)
return true ;
/* Sanity check 2 - can only remove an override
* if the specified type is currently active */
if ( game_specific & & ! runloop_st - > game_options_active )
goto error ;
/* Get current options file path */
current_options_path = path_get ( RARCH_PATH_CORE_OPTIONS ) ;
if ( string_is_empty ( current_options_path ) )
goto error ;
/* Remove current options file, if required */
if ( path_is_valid ( current_options_path ) )
filestream_delete ( current_options_path ) ;
/* Reload any existing 'parent' options file
* > If we have removed a game - specific config ,
* check whether a folder - specific config
* exists */
if ( game_specific & &
validate_folder_options (
new_options_path ,
sizeof ( new_options_path ) , false ) & &
path_is_valid ( new_options_path ) )
folder_options_active = true ;
/* > If a folder-specific config does not exist,
* or we removed it , check whether we have a
* top - level config file */
if ( ! folder_options_active )
{
/* Try core-specific options, if enabled */
if ( per_core_options )
{
const char * core_name = runloop_st - > system . info . library_name ;
per_core_options = validate_per_core_options (
new_options_path , sizeof ( new_options_path ) , true ,
core_name , core_name ) ;
}
/* ...otherwise use global options */
if ( ! per_core_options )
{
if ( ! string_is_empty ( path_core_options ) )
strlcpy ( new_options_path ,
path_core_options , sizeof ( new_options_path ) ) ;
else if ( ! path_is_empty ( RARCH_PATH_CONFIG ) )
fill_pathname_resolve_relative (
new_options_path , path_get ( RARCH_PATH_CONFIG ) ,
FILE_PATH_CORE_OPTIONS_CONFIG , sizeof ( new_options_path ) ) ;
}
}
if ( string_is_empty ( new_options_path ) )
goto error ;
/* > If we have a valid file, load it */
if ( folder_options_active | |
path_is_valid ( new_options_path ) )
{
size_t i , j ;
if ( ! ( conf = config_file_new_from_path_to_string ( new_options_path ) ) )
goto error ;
for ( i = 0 ; i < coreopts - > size ; i + + )
{
struct core_option * option = NULL ;
struct config_entry_list * entry = NULL ;
option = ( struct core_option * ) & coreopts - > opts [ i ] ;
if ( ! option )
continue ;
entry = config_get_entry ( conf , option - > key ) ;
if ( ! entry | | string_is_empty ( entry - > value ) )
continue ;
/* Set current config value from file entry */
for ( j = 0 ; j < option - > vals - > size ; j + + )
{
if ( string_is_equal ( option - > vals - > elems [ j ] . data , entry - > value ) )
{
option - > index = j ;
break ;
}
}
}
coreopts - > updated = true ;
# ifdef HAVE_CHEEVOS
rcheevos_validate_config_settings ( ) ;
# endif
}
/* Update runloop status */
if ( folder_options_active )
{
path_set ( RARCH_PATH_CORE_OPTIONS , new_options_path ) ;
runloop_st - > game_options_active = false ;
runloop_st - > folder_options_active = true ;
}
else
{
path_clear ( RARCH_PATH_CORE_OPTIONS ) ;
runloop_st - > game_options_active = false ;
runloop_st - > folder_options_active = false ;
/* Update config file path/object stored in
* core option manager struct */
strlcpy ( coreopts - > conf_path , new_options_path ,
sizeof ( coreopts - > conf_path ) ) ;
if ( conf )
{
config_file_free ( coreopts - > conf ) ;
coreopts - > conf = conf ;
conf = NULL ;
}
}
runloop_msg_queue_push (
msg_hash_to_str ( MSG_CORE_OPTIONS_FILE_REMOVED_SUCCESSFULLY ) ,
1 , 100 , true ,
NULL , MESSAGE_QUEUE_ICON_DEFAULT , MESSAGE_QUEUE_CATEGORY_INFO ) ;
if ( conf )
config_file_free ( conf ) ;
return true ;
error :
runloop_msg_queue_push (
msg_hash_to_str ( MSG_ERROR_REMOVING_CORE_OPTIONS_FILE ) ,
1 , 100 , true ,
NULL , MESSAGE_QUEUE_ICON_DEFAULT , MESSAGE_QUEUE_CATEGORY_INFO ) ;
if ( conf )
config_file_free ( conf ) ;
return false ;
}
void core_options_reset ( void )
{
size_t i ;
runloop_state_t * runloop_st = & runloop_state ;
core_option_manager_t * coreopts = runloop_st - > core_options ;
/* If there are no core options, there
* is nothing to do */
if ( ! coreopts | | ( coreopts - > size < 1 ) )
return ;
for ( i = 0 ; i < coreopts - > size ; i + + )
coreopts - > opts [ i ] . index = coreopts - > opts [ i ] . default_index ;
coreopts - > updated = true ;
# ifdef HAVE_CHEEVOS
rcheevos_validate_config_settings ( ) ;
# endif
runloop_msg_queue_push (
msg_hash_to_str ( MSG_CORE_OPTIONS_RESET ) ,
1 , 100 , true ,
NULL , MESSAGE_QUEUE_ICON_DEFAULT , MESSAGE_QUEUE_CATEGORY_INFO ) ;
}
void core_options_flush ( void )
{
runloop_state_t * runloop_st = & runloop_state ;
core_option_manager_t * coreopts = runloop_st - > core_options ;
const char * path_core_options = path_get ( RARCH_PATH_CORE_OPTIONS ) ;
const char * core_options_file = NULL ;
bool success = false ;
char msg [ 256 ] ;
msg [ 0 ] = ' \0 ' ;
/* If there are no core options, there
* is nothing to do */
if ( ! coreopts | | ( coreopts - > size < 1 ) )
return ;
/* Check whether game/folder-specific options file
* is being used */
if ( ! string_is_empty ( path_core_options ) )
{
config_file_t * conf_tmp = NULL ;
/* Attempt to load existing file */
if ( path_is_valid ( path_core_options ) )
conf_tmp = config_file_new_from_path_to_string ( path_core_options ) ;
/* Create new file if required */
if ( ! conf_tmp )
conf_tmp = config_file_new_alloc ( ) ;
if ( conf_tmp )
{
core_option_manager_flush ( runloop_st - > core_options , conf_tmp ) ;
success = config_file_write ( conf_tmp , path_core_options , true ) ;
config_file_free ( conf_tmp ) ;
}
}
else
{
/* We are using the 'default' core options file */
path_core_options = runloop_st - > core_options - > conf_path ;
if ( ! string_is_empty ( path_core_options ) )
{
core_option_manager_flush (
runloop_st - > core_options ,
runloop_st - > core_options - > conf ) ;
/* We must *guarantee* that a file gets written
* to disk if any options differ from the current
* options file contents . Must therefore handle
* the case where the ' default ' file does not
* exist ( e . g . if it gets deleted manually while
* a core is running ) */
if ( ! path_is_valid ( path_core_options ) )
runloop_st - > core_options - > conf - > modified = true ;
success = config_file_write ( runloop_st - > core_options - > conf ,
path_core_options , true ) ;
}
}
/* Get options file name for display purposes */
if ( ! string_is_empty ( path_core_options ) )
core_options_file = path_basename ( path_core_options ) ;
if ( string_is_empty ( core_options_file ) )
core_options_file = msg_hash_to_str ( MENU_ENUM_LABEL_VALUE_UNKNOWN ) ;
/* Log result */
RARCH_LOG ( success ?
" [Core Options]: Saved core options to \" %s \" \n " :
" [Core Options]: Failed to save core options to \" %s \" \n " ,
path_core_options ? path_core_options : " UNKNOWN " ) ;
snprintf ( msg , sizeof ( msg ) , " %s \" %s \" " ,
success ?
msg_hash_to_str ( MSG_CORE_OPTIONS_FLUSHED ) :
msg_hash_to_str ( MSG_CORE_OPTIONS_FLUSH_FAILED ) ,
core_options_file ) ;
runloop_msg_queue_push (
msg , 1 , 100 , true ,
NULL , MESSAGE_QUEUE_ICON_DEFAULT , MESSAGE_QUEUE_CATEGORY_INFO ) ;
}
void runloop_msg_queue_push ( const char * msg ,
unsigned prio , unsigned duration ,
bool flush ,
char * title ,
enum message_queue_icon icon ,
enum message_queue_category category )
{
# if defined(HAVE_GFX_WIDGETS)
bool widgets_active = dispwidget_get_ptr ( ) - > active ;
# endif
# ifdef HAVE_ACCESSIBILITY
settings_t * settings = config_get_ptr ( ) ;
bool accessibility_enable = settings - > bools . accessibility_enable ;
unsigned accessibility_narrator_speech_speed = settings - > uints . accessibility_narrator_speech_speed ;
access_state_t * access_st = access_state_get_ptr ( ) ;
# endif
runloop_state_t * runloop_st = & runloop_state ;
RUNLOOP_MSG_QUEUE_LOCK ( runloop_st ) ;
# ifdef HAVE_ACCESSIBILITY
if ( is_accessibility_enabled (
accessibility_enable ,
access_st - > enabled ) )
accessibility_speak_priority (
accessibility_enable ,
accessibility_narrator_speech_speed ,
( char * ) msg , 0 ) ;
# endif
# if defined(HAVE_GFX_WIDGETS)
if ( widgets_active )
{
gfx_widgets_msg_queue_push (
NULL ,
msg ,
roundf ( ( float ) duration / 60.0f * 1000.0f ) ,
title ,
icon ,
category ,
prio ,
flush ,
# ifdef HAVE_MENU
menu_state_get_ptr ( ) - > alive
# else
false
# endif
) ;
duration = duration * 60 / 1000 ;
}
else
# endif
{
if ( flush )
msg_queue_clear ( & runloop_st - > msg_queue ) ;
msg_queue_push ( & runloop_st - > msg_queue , msg ,
prio , duration ,
title , icon , category ) ;
runloop_st - > msg_queue_size = msg_queue_size (
& runloop_st - > msg_queue ) ;
}
ui_companion_driver_msg_queue_push (
msg , prio , duration , flush ) ;
RUNLOOP_MSG_QUEUE_UNLOCK ( runloop_st ) ;
}
# ifdef HAVE_MENU
/* Display the libretro core's framebuffer onscreen. */
static bool display_menu_libretro (
runloop_state_t * runloop_st ,
input_driver_state_t * input_st ,
float slowmotion_ratio ,
bool libretro_running ,
retro_time_t current_time )
{
bool runloop_idle = runloop_st - > idle ;
video_driver_state_t * video_st = video_state_get_ptr ( ) ;
if ( video_st - > poke
& & video_st - > poke - > set_texture_enable )
video_st - > poke - > set_texture_enable ( video_st - > data , true , false ) ;
if ( libretro_running )
{
if ( ! input_st - > block_libretro_input )
input_st - > block_libretro_input = true ;
core_run ( ) ;
runloop_st - > core_runtime_usec + =
runloop_core_runtime_tick ( runloop_st , slowmotion_ratio , current_time ) ;
input_st - > block_libretro_input = false ;
return false ;
}
if ( runloop_idle )
{
# ifdef HAVE_DISCORD
discord_userdata_t userdata ;
userdata . status = DISCORD_PRESENCE_GAME_PAUSED ;
command_event ( CMD_EVENT_DISCORD_UPDATE , & userdata ) ;
# endif
return false ;
}
return true ;
}
# endif
# define HOTKEY_CHECK(cmd1, cmd2, cond, cond2) \
{ \
static bool old_pressed = false ; \
bool pressed = BIT256_GET ( current_bits , cmd1 ) ; \
if ( pressed & & ! old_pressed ) \
if ( cond ) \
command_event ( cmd2 , cond2 ) ; \
old_pressed = pressed ; \
}
# define HOTKEY_CHECK3(cmd1, cmd2, cmd3, cmd4, cmd5, cmd6) \
{ \
static bool old_pressed = false ; \
static bool old_pressed2 = false ; \
static bool old_pressed3 = false ; \
bool pressed = BIT256_GET ( current_bits , cmd1 ) ; \
bool pressed2 = BIT256_GET ( current_bits , cmd3 ) ; \
bool pressed3 = BIT256_GET ( current_bits , cmd5 ) ; \
if ( pressed & & ! old_pressed ) \
command_event ( cmd2 , ( void * ) ( intptr_t ) 0 ) ; \
else if ( pressed2 & & ! old_pressed2 ) \
command_event ( cmd4 , ( void * ) ( intptr_t ) 0 ) ; \
else if ( pressed3 & & ! old_pressed3 ) \
command_event ( cmd6 , ( void * ) ( intptr_t ) 0 ) ; \
old_pressed = pressed ; \
old_pressed2 = pressed2 ; \
old_pressed3 = pressed3 ; \
}
static enum runloop_state_enum runloop_check_state (
bool error_on_init ,
settings_t * settings ,
retro_time_t current_time )
{
input_bits_t current_bits ;
# ifdef HAVE_MENU
static input_bits_t last_input = { { 0 } } ;
# endif
uico_driver_state_t * uico_st = uico_state_get_ptr ( ) ;
input_driver_state_t * input_st = input_state_get_ptr ( ) ;
video_driver_state_t * video_st = video_state_get_ptr ( ) ;
gfx_display_t * p_disp = disp_get_ptr ( ) ;
runloop_state_t * runloop_st = & runloop_state ;
static bool old_focus = true ;
struct retro_callbacks * cbs = & runloop_st - > retro_ctx ;
bool is_focused = false ;
bool is_alive = false ;
uint64_t frame_count = 0 ;
bool focused = true ;
bool rarch_is_initialized = runloop_st - > is_inited ;
bool runloop_paused = runloop_st - > paused ;
bool pause_nonactive = settings - > bools . pause_nonactive ;
unsigned quit_gamepad_combo = settings - > uints . input_quit_gamepad_combo ;
# ifdef HAVE_MENU
struct menu_state * menu_st = menu_state_get_ptr ( ) ;
menu_handle_t * menu = menu_st - > driver_data ;
unsigned menu_toggle_gamepad_combo = settings - > uints . input_menu_toggle_gamepad_combo ;
bool menu_driver_binding_state = menu_st - > is_binding ;
bool menu_is_alive = menu_st - > alive ;
bool display_kb = menu_input_dialog_get_display_kb ( ) ;
# endif
# if defined(HAVE_GFX_WIDGETS)
bool widgets_active = dispwidget_get_ptr ( ) - > active ;
# endif
# ifdef HAVE_CHEEVOS
bool cheevos_hardcore_active = rcheevos_hardcore_active ( ) ;
# endif
# if defined(HAVE_TRANSLATE) && defined(HAVE_GFX_WIDGETS)
if ( dispwidget_get_ptr ( ) - > ai_service_overlay_state = = 3 )
{
command_event ( CMD_EVENT_PAUSE , NULL ) ;
dispwidget_get_ptr ( ) - > ai_service_overlay_state = 1 ;
}
# endif
# ifdef HAVE_LIBNX
/* Should be called once per frame */
if ( ! appletMainLoop ( ) )
return RUNLOOP_STATE_QUIT ;
# endif
# ifdef _3DS
/* Should be called once per frame */
if ( ! aptMainLoop ( ) )
return RUNLOOP_STATE_QUIT ;
# endif
BIT256_CLEAR_ALL_PTR ( & current_bits ) ;
input_st - > block_libretro_input = false ;
input_st - > block_hotkey = false ;
if ( input_st - > keyboard_mapping_blocked )
input_st - > block_hotkey = true ;
input_driver_collect_system_input ( input_st , settings , & current_bits ) ;
# ifdef HAVE_MENU
last_input = current_bits ;
if (
( ( menu_toggle_gamepad_combo ! = INPUT_COMBO_NONE ) & &
input_driver_button_combo (
menu_toggle_gamepad_combo ,
current_time ,
& last_input ) ) )
BIT256_SET ( current_bits , RARCH_MENU_TOGGLE ) ;
if ( menu_st - > input_driver_flushing_input > 0 )
{
bool input_active = bits_any_set ( current_bits . data , ARRAY_SIZE ( current_bits . data ) ) ;
menu_st - > input_driver_flushing_input = input_active
? menu_st - > input_driver_flushing_input
: ( menu_st - > input_driver_flushing_input - 1 ) ;
if ( input_active | | ( menu_st - > input_driver_flushing_input > 0 ) )
{
BIT256_CLEAR_ALL ( current_bits ) ;
if ( runloop_paused )
BIT256_SET ( current_bits , RARCH_PAUSE_TOGGLE ) ;
}
}
# endif
if ( ! VIDEO_DRIVER_IS_THREADED_INTERNAL ( video_st ) )
{
const ui_application_t * application = uico_st - > drv
? uico_st - > drv - > application
: NULL ;
if ( application )
application - > process_events ( ) ;
}
frame_count = video_st - > frame_count ;
is_alive = video_st - > current_video
? video_st - > current_video - > alive ( video_st - > data )
: true ;
is_focused = VIDEO_HAS_FOCUS ( video_st ) ;
# ifdef HAVE_MENU
if ( menu_driver_binding_state )
BIT256_CLEAR_ALL ( current_bits ) ;
# endif
/* Check fullscreen toggle */
{
bool fullscreen_toggled = ! runloop_paused
# ifdef HAVE_MENU
| | menu_is_alive ;
# else
;
# endif
HOTKEY_CHECK ( RARCH_FULLSCREEN_TOGGLE_KEY , CMD_EVENT_FULLSCREEN_TOGGLE ,
fullscreen_toggled , NULL ) ;
}
/* Check mouse grab toggle */
HOTKEY_CHECK ( RARCH_GRAB_MOUSE_TOGGLE , CMD_EVENT_GRAB_MOUSE_TOGGLE , true , NULL ) ;
/* Automatic mouse grab on focus */
if ( settings - > bools . input_auto_mouse_grab & &
is_focused & &
is_focused ! = runloop_st - > focused & &
! input_st - > grab_mouse_state )
command_event ( CMD_EVENT_GRAB_MOUSE_TOGGLE , NULL ) ;
runloop_st - > focused = is_focused ;
# ifdef HAVE_OVERLAY
if ( settings - > bools . input_overlay_enable )
{
static char prev_overlay_restore = false ;
static unsigned last_width = 0 ;
static unsigned last_height = 0 ;
unsigned video_driver_width = video_st - > width ;
unsigned video_driver_height = video_st - > height ;
bool check_next_rotation = true ;
bool input_overlay_hide_in_menu = settings - > bools . input_overlay_hide_in_menu ;
bool input_overlay_hide_when_gamepad_connected = settings - > bools . input_overlay_hide_when_gamepad_connected ;
bool input_overlay_auto_rotate = settings - > bools . input_overlay_auto_rotate ;
/* Check whether overlay should be hidden
* when a gamepad is connected */
if ( input_overlay_hide_when_gamepad_connected )
{
static bool last_controller_connected = false ;
bool controller_connected = ( input_config_get_device_name ( 0 ) ! = NULL ) ;
if ( controller_connected ! = last_controller_connected )
{
if ( controller_connected )
input_overlay_deinit ( ) ;
else
input_overlay_init ( ) ;
last_controller_connected = controller_connected ;
}
}
/* Check next overlay */
HOTKEY_CHECK ( RARCH_OVERLAY_NEXT , CMD_EVENT_OVERLAY_NEXT , true , & check_next_rotation ) ;
/* Ensure overlay is restored after displaying osk */
if ( input_st - > keyboard_linefeed_enable )
prev_overlay_restore = true ;
else if ( prev_overlay_restore )
{
if ( ! input_overlay_hide_in_menu )
input_overlay_init ( ) ;
prev_overlay_restore = false ;
}
/* Check whether video aspect has changed */
if ( ( video_driver_width ! = last_width ) | |
( video_driver_height ! = last_height ) )
{
/* Update scaling/offset factors */
command_event ( CMD_EVENT_OVERLAY_SET_SCALE_FACTOR , NULL ) ;
/* Check overlay rotation, if required */
if ( input_overlay_auto_rotate )
input_overlay_auto_rotate_ (
video_st - > width ,
video_st - > height ,
settings - > bools . input_overlay_enable ,
input_st - > overlay_ptr ) ;
last_width = video_driver_width ;
last_height = video_driver_height ;
}
}
# endif
/*
* If the Aspect Ratio is FULL then update the aspect ratio to the
* current video driver aspect ratio ( The full window )
*
* TODO / FIXME
* Should possibly be refactored to have last width & driver width & height
* only be done once when we are using an overlay OR using aspect ratio
* full
*/
if ( settings - > uints . video_aspect_ratio_idx = = ASPECT_RATIO_FULL )
{
static unsigned last_width = 0 ;
static unsigned last_height = 0 ;
unsigned video_driver_width = video_st - > width ;
unsigned video_driver_height = video_st - > height ;
/* Check whether video aspect has changed */
if ( ( video_driver_width ! = last_width ) | |
( video_driver_height ! = last_height ) )
{
/* Update set aspect ratio so the full matches the current video width & height */
command_event ( CMD_EVENT_VIDEO_SET_ASPECT_RATIO , NULL ) ;
last_width = video_driver_width ;
last_height = video_driver_height ;
}
}
/* Check quit key */
{
bool trig_quit_key , quit_press_twice ;
static bool quit_key = false ;
static bool old_quit_key = false ;
static bool runloop_exec = false ;
quit_key = BIT256_GET (
current_bits , RARCH_QUIT_KEY ) ;
trig_quit_key = quit_key & & ! old_quit_key ;
/* Check for quit gamepad combo */
if ( ! trig_quit_key & &
( ( quit_gamepad_combo ! = INPUT_COMBO_NONE ) & &
input_driver_button_combo (
quit_gamepad_combo ,
current_time ,
& current_bits ) ) )
trig_quit_key = true ;
old_quit_key = quit_key ;
quit_press_twice = settings - > bools . quit_press_twice ;
/* Check double press if enabled */
if ( trig_quit_key & & quit_press_twice )
{
static retro_time_t quit_key_time = 0 ;
retro_time_t cur_time = current_time ;
trig_quit_key = ( cur_time - quit_key_time < QUIT_DELAY_USEC ) ;
quit_key_time = cur_time ;
if ( ! trig_quit_key )
{
float target_hz = 0.0 ;
runloop_environment_cb (
RETRO_ENVIRONMENT_GET_TARGET_REFRESH_RATE , & target_hz ) ;
runloop_msg_queue_push ( msg_hash_to_str ( MSG_PRESS_AGAIN_TO_QUIT ) , 1 ,
QUIT_DELAY_USEC * target_hz / 1000000 ,
true , NULL ,
MESSAGE_QUEUE_ICON_DEFAULT , MESSAGE_QUEUE_CATEGORY_INFO ) ;
}
}
if ( RUNLOOP_TIME_TO_EXIT ( trig_quit_key ) )
{
bool quit_runloop = false ;
# ifdef HAVE_SCREENSHOTS
unsigned runloop_max_frames = runloop_st - > max_frames ;
if ( ( runloop_max_frames ! = 0 )
& & ( frame_count > = runloop_max_frames )
& & runloop_st - > max_frames_screenshot )
{
const char * screenshot_path = NULL ;
bool fullpath = false ;
if ( string_is_empty ( runloop_st - > max_frames_screenshot_path ) )
screenshot_path = path_get ( RARCH_PATH_BASENAME ) ;
else
{
fullpath = true ;
screenshot_path = runloop_st - > max_frames_screenshot_path ;
}
RARCH_LOG ( " Taking a screenshot before exiting... \n " ) ;
/* Take a screenshot before we exit. */
if ( ! take_screenshot ( settings - > paths . directory_screenshot ,
screenshot_path , false ,
video_driver_cached_frame_has_valid_framebuffer ( ) , fullpath , false ) )
{
RARCH_ERR ( " Could not take a screenshot before exiting. \n " ) ;
}
}
# endif
if ( runloop_exec )
runloop_exec = false ;
if ( runloop_st - > core_shutdown_initiated & &
settings - > bools . load_dummy_on_core_shutdown )
{
content_ctx_info_t content_info ;
content_info . argc = 0 ;
content_info . argv = NULL ;
content_info . args = NULL ;
content_info . environ_get = NULL ;
if ( task_push_start_dummy_core ( & content_info ) )
{
/* Loads dummy core instead of exiting RetroArch completely.
* Aborts core shutdown if invoked . */
runloop_st - > shutdown_initiated = false ;
runloop_st - > core_shutdown_initiated = false ;
}
else
quit_runloop = true ;
}
else
quit_runloop = true ;
runloop_st - > core_running = false ;
if ( quit_runloop )
{
old_quit_key = quit_key ;
retroarch_main_quit ( ) ;
return RUNLOOP_STATE_QUIT ;
}
}
}
# if defined(HAVE_MENU) || defined(HAVE_GFX_WIDGETS)
gfx_animation_update (
current_time ,
settings - > bools . menu_timedate_enable ,
settings - > floats . menu_ticker_speed ,
video_st - > width ,
video_st - > height ) ;
# if defined(HAVE_GFX_WIDGETS)
if ( widgets_active )
{
bool rarch_force_fullscreen = video_st - > force_fullscreen ;
bool video_is_fullscreen = settings - > bools . video_fullscreen | |
rarch_force_fullscreen ;
RUNLOOP_MSG_QUEUE_LOCK ( runloop_st ) ;
gfx_widgets_iterate (
p_disp ,
settings ,
video_st - > width ,
video_st - > height ,
video_is_fullscreen ,
settings - > paths . directory_assets ,
settings - > paths . path_font ,
VIDEO_DRIVER_IS_THREADED_INTERNAL ( video_st ) ) ;
RUNLOOP_MSG_QUEUE_UNLOCK ( runloop_st ) ;
}
# endif
# ifdef HAVE_MENU
if ( menu_is_alive )
{
enum menu_action action ;
static input_bits_t old_input = { { 0 } } ;
static enum menu_action
old_action = MENU_ACTION_CANCEL ;
struct menu_state * menu_st = menu_state_get_ptr ( ) ;
bool focused = false ;
input_bits_t trigger_input = current_bits ;
unsigned screensaver_timeout = settings - > uints . menu_screensaver_timeout ;
/* Get current time */
menu_st - > current_time_us = current_time ;
cbs - > poll_cb ( ) ;
bits_clear_bits ( trigger_input . data , old_input . data ,
ARRAY_SIZE ( trigger_input . data ) ) ;
action = ( enum menu_action ) menu_event (
settings ,
& current_bits , & trigger_input , display_kb ) ;
focused = pause_nonactive ? is_focused : true ;
focused = focused & & ! uico_st - > is_on_foreground ;
if ( action = = old_action )
{
retro_time_t press_time = current_time ;
if ( action = = MENU_ACTION_NOOP )
menu_st - > noop_press_time = press_time - menu_st - > noop_start_time ;
else
menu_st - > action_press_time = press_time - menu_st - > action_start_time ;
}
else
{
if ( action = = MENU_ACTION_NOOP )
{
menu_st - > noop_start_time = current_time ;
menu_st - > noop_press_time = 0 ;
if ( menu_st - > prev_action = = old_action )
menu_st - > action_start_time = menu_st - > prev_start_time ;
else
menu_st - > action_start_time = current_time ;
}
else
{
if ( menu_st - > prev_action = = action & &
menu_st - > noop_press_time < 200000 ) /* 250ms */
{
menu_st - > action_start_time = menu_st - > prev_start_time ;
menu_st - > action_press_time = current_time - menu_st - > action_start_time ;
}
else
{
menu_st - > prev_start_time = current_time ;
menu_st - > prev_action = action ;
menu_st - > action_press_time = 0 ;
}
}
}
/* Check whether menu screensaver should be enabled */
if ( ( screensaver_timeout > 0 ) & &
menu_st - > screensaver_supported & &
! menu_st - > screensaver_active & &
( ( menu_st - > current_time_us - menu_st - > input_last_time_us ) >
( ( retro_time_t ) screensaver_timeout * 1000000 ) ) )
{
menu_ctx_environment_t menu_environ ;
menu_environ . type = MENU_ENVIRON_ENABLE_SCREENSAVER ;
menu_environ . data = NULL ;
menu_st - > screensaver_active = true ;
menu_driver_ctl ( RARCH_MENU_CTL_ENVIRONMENT , & menu_environ ) ;
}
/* Iterate the menu driver for one frame. */
if ( menu_st - > pending_quick_menu )
{
/* If the user had requested that the Quick Menu
* be spawned during the previous frame , do this now
* and exit the function to go to the next frame .
*/
menu_entries_flush_stack ( NULL , MENU_SETTINGS ) ;
p_disp - > msg_force = true ;
generic_action_ok_displaylist_push ( " " , NULL ,
" " , 0 , 0 , 0 , ACTION_OK_DL_CONTENT_SETTINGS ) ;
menu_st - > selection_ptr = 0 ;
menu_st - > pending_quick_menu = false ;
}
else if ( ! menu_driver_iterate (
menu_st ,
p_disp ,
anim_get_ptr ( ) ,
settings ,
action , current_time ) )
{
if ( error_on_init )
{
content_ctx_info_t content_info = { 0 } ;
task_push_start_dummy_core ( & content_info ) ;
}
else
retroarch_menu_running_finished ( false ) ;
}
if ( focused | | ! runloop_st - > idle )
{
bool runloop_is_inited = runloop_st - > is_inited ;
bool menu_pause_libretro = settings - > bools . menu_pause_libretro ;
bool libretro_running = ! menu_pause_libretro
& & runloop_is_inited
& & ( runloop_st - > current_core_type ! = CORE_TYPE_DUMMY ) ;
if ( menu )
{
if ( BIT64_GET ( menu - > state , MENU_STATE_RENDER_FRAMEBUFFER )
! = BIT64_GET ( menu - > state , MENU_STATE_RENDER_MESSAGEBOX ) )
BIT64_SET ( menu - > state , MENU_STATE_RENDER_FRAMEBUFFER ) ;
if ( BIT64_GET ( menu - > state , MENU_STATE_RENDER_FRAMEBUFFER ) )
p_disp - > framebuf_dirty = true ;
if ( BIT64_GET ( menu - > state , MENU_STATE_RENDER_MESSAGEBOX )
& & ! string_is_empty ( menu - > menu_state_msg ) )
{
if ( menu - > driver_ctx - > render_messagebox )
menu - > driver_ctx - > render_messagebox (
menu - > userdata ,
menu - > menu_state_msg ) ;
if ( uico_st - > is_on_foreground )
{
if ( uico_st - > drv
& & uico_st - > drv - > render_messagebox )
uico_st - > drv - > render_messagebox ( menu - > menu_state_msg ) ;
}
}
if ( BIT64_GET ( menu - > state , MENU_STATE_BLIT ) )
{
if ( menu - > driver_ctx - > render )
menu - > driver_ctx - > render (
menu - > userdata ,
video_st - > width ,
video_st - > height ,
runloop_st - > idle ) ;
}
if ( menu_st - > alive & & ! runloop_st - > idle )
if ( display_menu_libretro ( runloop_st , input_st ,
settings - > floats . slowmotion_ratio ,
libretro_running , current_time ) )
video_driver_cached_frame ( ) ;
if ( menu - > driver_ctx - > set_texture )
menu - > driver_ctx - > set_texture ( menu - > userdata ) ;
menu - > state = 0 ;
}
if ( settings - > bools . audio_enable_menu & &
! libretro_running )
audio_driver_menu_sample ( ) ;
}
old_input = current_bits ;
old_action = action ;
if ( ! focused | | runloop_st - > idle )
return RUNLOOP_STATE_POLLED_AND_SLEEP ;
}
else
# endif
# endif
{
if ( runloop_st - > idle )
{
cbs - > poll_cb ( ) ;
return RUNLOOP_STATE_POLLED_AND_SLEEP ;
}
}
/* Check game focus toggle */
{
enum input_game_focus_cmd_type game_focus_cmd = GAME_FOCUS_CMD_TOGGLE ;
HOTKEY_CHECK ( RARCH_GAME_FOCUS_TOGGLE , CMD_EVENT_GAME_FOCUS_TOGGLE , true , & game_focus_cmd ) ;
}
/* Check if we have pressed the UI companion toggle button */
HOTKEY_CHECK ( RARCH_UI_COMPANION_TOGGLE , CMD_EVENT_UI_COMPANION_TOGGLE , true , NULL ) ;
/* Check close content key */
HOTKEY_CHECK ( RARCH_CLOSE_CONTENT_KEY , CMD_EVENT_CLOSE_CONTENT , true , NULL ) ;
# ifdef HAVE_MENU
/* Check if we have pressed the menu toggle button */
{
static bool old_pressed = false ;
char * menu_driver = settings - > arrays . menu_driver ;
bool pressed = BIT256_GET (
current_bits , RARCH_MENU_TOGGLE ) & &
! string_is_equal ( menu_driver , " null " ) ;
bool core_type_is_dummy = runloop_st - > current_core_type = = CORE_TYPE_DUMMY ;
if ( menu_st - > kb_key_state [ RETROK_F1 ] = = 1 )
{
if ( menu_st - > alive )
{
if ( rarch_is_initialized & & ! core_type_is_dummy )
{
retroarch_menu_running_finished ( false ) ;
menu_st - > kb_key_state [ RETROK_F1 ] =
( ( menu_st - > kb_key_state [ RETROK_F1 ] & 1 ) < < 1 ) | false ;
}
}
}
else if ( ( ! menu_st - > kb_key_state [ RETROK_F1 ] & &
( pressed & & ! old_pressed ) ) | |
core_type_is_dummy )
{
if ( menu_st - > alive )
{
if ( rarch_is_initialized & & ! core_type_is_dummy )
retroarch_menu_running_finished ( false ) ;
}
else
retroarch_menu_running ( ) ;
}
else
menu_st - > kb_key_state [ RETROK_F1 ] =
( ( menu_st - > kb_key_state [ RETROK_F1 ] & 1 ) < < 1 ) | false ;
old_pressed = pressed ;
}
/* Check if we have pressed the FPS toggle button */
HOTKEY_CHECK ( RARCH_FPS_TOGGLE , CMD_EVENT_FPS_TOGGLE , true , NULL ) ;
/* Check if we have pressed the netplay host toggle button */
HOTKEY_CHECK ( RARCH_NETPLAY_HOST_TOGGLE , CMD_EVENT_NETPLAY_HOST_TOGGLE , true , NULL ) ;
if ( menu_st - > alive )
{
float fastforward_ratio = runloop_get_fastforward_ratio ( settings ,
& runloop_st - > fastmotion_override . current ) ;
if ( ! settings - > bools . menu_throttle_framerate & & ! fastforward_ratio )
return RUNLOOP_STATE_MENU_ITERATE ;
return RUNLOOP_STATE_END ;
}
# endif
if ( pause_nonactive )
focused = is_focused ;
# ifdef HAVE_SCREENSHOTS
/* Check if we have pressed the screenshot toggle button */
HOTKEY_CHECK ( RARCH_SCREENSHOT , CMD_EVENT_TAKE_SCREENSHOT , true , NULL ) ;
# endif
/* Check if we have pressed the audio mute toggle button */
HOTKEY_CHECK ( RARCH_MUTE , CMD_EVENT_AUDIO_MUTE_TOGGLE , true , NULL ) ;
/* Check if we have pressed the OSK toggle button */
HOTKEY_CHECK ( RARCH_OSK , CMD_EVENT_OSK_TOGGLE , true , NULL ) ;
/* Check if we have pressed the recording toggle button */
HOTKEY_CHECK ( RARCH_RECORDING_TOGGLE , CMD_EVENT_RECORDING_TOGGLE , true , NULL ) ;
/* Check if we have pressed the streaming toggle button */
HOTKEY_CHECK ( RARCH_STREAMING_TOGGLE , CMD_EVENT_STREAMING_TOGGLE , true , NULL ) ;
/* Check if we have pressed the Run-Ahead toggle button */
HOTKEY_CHECK ( RARCH_RUNAHEAD_TOGGLE , CMD_EVENT_RUNAHEAD_TOGGLE , true , NULL ) ;
/* Check if we have pressed the AI Service toggle button */
HOTKEY_CHECK ( RARCH_AI_SERVICE , CMD_EVENT_AI_SERVICE_TOGGLE , true , NULL ) ;
if ( BIT256_GET ( current_bits , RARCH_VOLUME_UP ) )
command_event ( CMD_EVENT_VOLUME_UP , NULL ) ;
else if ( BIT256_GET ( current_bits , RARCH_VOLUME_DOWN ) )
command_event ( CMD_EVENT_VOLUME_DOWN , NULL ) ;
# ifdef HAVE_NETWORKING
/* Check Netplay */
HOTKEY_CHECK ( RARCH_NETPLAY_GAME_WATCH , CMD_EVENT_NETPLAY_GAME_WATCH , true , NULL ) ;
# endif
/* Check if we have pressed the pause button */
{
static bool old_frameadvance = false ;
static bool old_pause_pressed = false ;
bool frameadvance_pressed = false ;
bool trig_frameadvance = false ;
bool pause_pressed = BIT256_GET ( current_bits , RARCH_PAUSE_TOGGLE ) ;
# ifdef HAVE_CHEEVOS
if ( cheevos_hardcore_active )
{
static int unpaused_frames = 0 ;
/* Frame advance is not allowed when achievement hardcore is active */
if ( ! runloop_st - > paused )
{
/* Limit pause to approximately three times per second (depending on core framerate) */
if ( unpaused_frames < 20 )
{
+ + unpaused_frames ;
pause_pressed = false ;
}
}
else
unpaused_frames = 0 ;
}
else
# endif
{
frameadvance_pressed = BIT256_GET ( current_bits , RARCH_FRAMEADVANCE ) ;
trig_frameadvance = frameadvance_pressed & & ! old_frameadvance ;
/* FRAMEADVANCE will set us into pause mode. */
pause_pressed | = ! runloop_st - > paused
& & trig_frameadvance ;
}
/* Check if libretro pause key was pressed. If so, pause or
* unpause the libretro core . */
if ( focused )
{
if ( pause_pressed & & ! old_pause_pressed )
command_event ( CMD_EVENT_PAUSE_TOGGLE , NULL ) ;
else if ( ! old_focus )
command_event ( CMD_EVENT_UNPAUSE , NULL ) ;
}
else if ( old_focus )
command_event ( CMD_EVENT_PAUSE , NULL ) ;
old_focus = focused ;
old_pause_pressed = pause_pressed ;
old_frameadvance = frameadvance_pressed ;
if ( runloop_st - > paused )
{
bool toggle = ! runloop_st - > idle ? true : false ;
HOTKEY_CHECK ( RARCH_FULLSCREEN_TOGGLE_KEY ,
CMD_EVENT_FULLSCREEN_TOGGLE , true , & toggle ) ;
/* Check if it's not oneshot */
# ifdef HAVE_REWIND
if ( ! ( trig_frameadvance | | BIT256_GET ( current_bits , RARCH_REWIND ) ) )
# else
if ( ! trig_frameadvance )
# endif
focused = false ;
}
}
# ifdef HAVE_ACCESSIBILITY
# ifdef HAVE_TRANSLATE
/* Copy over the retropad state to a buffer for the translate service
to send off if it ' s run . */
if ( settings - > bools . ai_service_enable )
{
input_st - > ai_gamepad_state [ 0 ] = BIT256_GET ( current_bits , RETRO_DEVICE_ID_JOYPAD_B ) ;
input_st - > ai_gamepad_state [ 1 ] = BIT256_GET ( current_bits , RETRO_DEVICE_ID_JOYPAD_Y ) ;
input_st - > ai_gamepad_state [ 2 ] = BIT256_GET ( current_bits , RETRO_DEVICE_ID_JOYPAD_SELECT ) ;
input_st - > ai_gamepad_state [ 3 ] = BIT256_GET ( current_bits , RETRO_DEVICE_ID_JOYPAD_START ) ;
input_st - > ai_gamepad_state [ 4 ] = BIT256_GET ( current_bits , RETRO_DEVICE_ID_JOYPAD_UP ) ;
input_st - > ai_gamepad_state [ 5 ] = BIT256_GET ( current_bits , RETRO_DEVICE_ID_JOYPAD_DOWN ) ;
input_st - > ai_gamepad_state [ 6 ] = BIT256_GET ( current_bits , RETRO_DEVICE_ID_JOYPAD_LEFT ) ;
input_st - > ai_gamepad_state [ 7 ] = BIT256_GET ( current_bits , RETRO_DEVICE_ID_JOYPAD_RIGHT ) ;
input_st - > ai_gamepad_state [ 8 ] = BIT256_GET ( current_bits , RETRO_DEVICE_ID_JOYPAD_A ) ;
input_st - > ai_gamepad_state [ 9 ] = BIT256_GET ( current_bits , RETRO_DEVICE_ID_JOYPAD_X ) ;
input_st - > ai_gamepad_state [ 10 ] = BIT256_GET ( current_bits , RETRO_DEVICE_ID_JOYPAD_L ) ;
input_st - > ai_gamepad_state [ 11 ] = BIT256_GET ( current_bits , RETRO_DEVICE_ID_JOYPAD_R ) ;
input_st - > ai_gamepad_state [ 12 ] = BIT256_GET ( current_bits , RETRO_DEVICE_ID_JOYPAD_L2 ) ;
input_st - > ai_gamepad_state [ 13 ] = BIT256_GET ( current_bits , RETRO_DEVICE_ID_JOYPAD_R2 ) ;
input_st - > ai_gamepad_state [ 14 ] = BIT256_GET ( current_bits , RETRO_DEVICE_ID_JOYPAD_L3 ) ;
input_st - > ai_gamepad_state [ 15 ] = BIT256_GET ( current_bits , RETRO_DEVICE_ID_JOYPAD_R3 ) ;
}
# endif
# endif
if ( ! focused )
{
cbs - > poll_cb ( ) ;
return RUNLOOP_STATE_POLLED_AND_SLEEP ;
}
/* Apply any pending fastmotion override
* parameters */
if ( runloop_st - > fastmotion_override . pending )
{
runloop_apply_fastmotion_override ( runloop_st , settings ) ;
runloop_st - > fastmotion_override . pending = false ;
}
/* Check if we have pressed the fast forward button */
/* To avoid continuous switching if we hold the button down, we require
* that the button must go from pressed to unpressed back to pressed
* to be able to toggle between them .
*/
if ( ! runloop_st - > fastmotion_override . current . inhibit_toggle )
{
static bool old_button_state = false ;
static bool old_hold_button_state = false ;
bool new_button_state = BIT256_GET (
current_bits , RARCH_FAST_FORWARD_KEY ) ;
bool new_hold_button_state = BIT256_GET (
current_bits , RARCH_FAST_FORWARD_HOLD_KEY ) ;
bool check2 = new_button_state
& & ! old_button_state ;
if ( ! check2 )
check2 = old_hold_button_state ! = new_hold_button_state ;
if ( check2 )
{
if ( input_st - > nonblocking_flag )
{
input_st - > nonblocking_flag = false ;
runloop_st - > fastmotion = false ;
runloop_st - > fastforward_after_frames = 1 ;
}
else
{
input_st - > nonblocking_flag = true ;
runloop_st - > fastmotion = true ;
}
driver_set_nonblock_state ( ) ;
/* Reset frame time counter when toggling
* fast - forward off , if required */
if ( ! runloop_st - > fastmotion & &
settings - > bools . frame_time_counter_reset_after_fastforwarding )
video_st - > frame_time_count = 0 ;
}
old_button_state = new_button_state ;
old_hold_button_state = new_hold_button_state ;
}
/* Display fast-forward notification, unless
* disabled via override */
if ( ! runloop_st - > fastmotion_override . current . fastforward | |
runloop_st - > fastmotion_override . current . notification )
{
/* > Use widgets, if enabled */
# if defined(HAVE_GFX_WIDGETS)
if ( widgets_active )
video_st - > widgets_fast_forward =
settings - > bools . notification_show_fast_forward ?
runloop_st - > fastmotion : false ;
else
# endif
{
/* > If widgets are disabled, display fast-forward
* status via OSD text for 1 frame every frame */
if ( runloop_st - > fastmotion & &
settings - > bools . notification_show_fast_forward )
runloop_msg_queue_push (
msg_hash_to_str ( MSG_FAST_FORWARD ) , 1 , 1 , false , NULL , MESSAGE_QUEUE_ICON_DEFAULT , MESSAGE_QUEUE_CATEGORY_INFO ) ;
}
}
# if defined(HAVE_GFX_WIDGETS)
else
video_st - > widgets_fast_forward = false ;
# endif
/* Check if we have pressed any of the state slot buttons */
{
static bool old_should_slot_increase = false ;
static bool old_should_slot_decrease = false ;
bool should_slot_increase = BIT256_GET (
current_bits , RARCH_STATE_SLOT_PLUS ) ;
bool should_slot_decrease = BIT256_GET (
current_bits , RARCH_STATE_SLOT_MINUS ) ;
bool check1 = true ;
bool check2 = should_slot_increase & & ! old_should_slot_increase ;
int addition = 1 ;
int state_slot = settings - > ints . state_slot ;
if ( ! check2 )
{
check2 = should_slot_decrease & & ! old_should_slot_decrease ;
check1 = state_slot > 0 ;
addition = - 1 ;
}
/* Checks if the state increase/decrease keys have been pressed
* for this frame . */
if ( check2 )
{
char msg [ 128 ] ;
int cur_state_slot = state_slot ;
if ( check1 )
configuration_set_int ( settings , settings - > ints . state_slot ,
cur_state_slot + addition ) ;
msg [ 0 ] = ' \0 ' ;
snprintf ( msg , sizeof ( msg ) , " %s: %d " ,
msg_hash_to_str ( MSG_STATE_SLOT ) ,
settings - > ints . state_slot ) ;
runloop_msg_queue_push ( msg , 2 , 180 , true , NULL ,
MESSAGE_QUEUE_ICON_DEFAULT , MESSAGE_QUEUE_CATEGORY_INFO ) ;
RARCH_LOG ( " %s \n " , msg ) ;
}
old_should_slot_increase = should_slot_increase ;
old_should_slot_decrease = should_slot_decrease ;
}
/* Check if we have pressed any of the savestate buttons */
HOTKEY_CHECK ( RARCH_SAVE_STATE_KEY , CMD_EVENT_SAVE_STATE , true , NULL ) ;
HOTKEY_CHECK ( RARCH_LOAD_STATE_KEY , CMD_EVENT_LOAD_STATE , true , NULL ) ;
# ifdef HAVE_CHEEVOS
if ( ! cheevos_hardcore_active )
# endif
{
/* Check if rewind toggle was being held. */
{
# ifdef HAVE_REWIND
char s [ 128 ] ;
bool rewinding = false ;
unsigned t = 0 ;
s [ 0 ] = ' \0 ' ;
rewinding = state_manager_check_rewind (
& runloop_st - > rewind_st ,
BIT256_GET ( current_bits , RARCH_REWIND ) ,
settings - > uints . rewind_granularity ,
runloop_st - > paused ,
s , sizeof ( s ) , & t ) ;
# if defined(HAVE_GFX_WIDGETS)
if ( widgets_active )
video_st - > widgets_rewinding = rewinding ;
else
# endif
{
if ( rewinding )
runloop_msg_queue_push ( s , 0 , t , true , NULL ,
MESSAGE_QUEUE_ICON_DEFAULT , MESSAGE_QUEUE_CATEGORY_INFO ) ;
}
# endif
}
{
/* Checks if slowmotion toggle/hold was being pressed and/or held. */
static bool old_slowmotion_button_state = false ;
static bool old_slowmotion_hold_button_state = false ;
bool new_slowmotion_button_state = BIT256_GET (
current_bits , RARCH_SLOWMOTION_KEY ) ;
bool new_slowmotion_hold_button_state = BIT256_GET (
current_bits , RARCH_SLOWMOTION_HOLD_KEY ) ;
if ( new_slowmotion_button_state & & ! old_slowmotion_button_state )
runloop_st - > slowmotion = ! runloop_st - > slowmotion ;
else if ( old_slowmotion_hold_button_state ! = new_slowmotion_hold_button_state )
runloop_st - > slowmotion = new_slowmotion_hold_button_state ;
if ( runloop_st - > slowmotion )
{
if ( settings - > uints . video_black_frame_insertion )
if ( ! runloop_st - > idle )
video_driver_cached_frame ( ) ;
# if defined(HAVE_GFX_WIDGETS)
if ( ! widgets_active )
# endif
{
# ifdef HAVE_REWIND
struct state_manager_rewind_state
* rewind_st = & runloop_st - > rewind_st ;
if ( rewind_st - > frame_is_reversed )
runloop_msg_queue_push (
msg_hash_to_str ( MSG_SLOW_MOTION_REWIND ) , 1 , 1 , false , NULL ,
MESSAGE_QUEUE_ICON_DEFAULT , MESSAGE_QUEUE_CATEGORY_INFO ) ;
else
# endif
runloop_msg_queue_push (
msg_hash_to_str ( MSG_SLOW_MOTION ) , 1 , 1 , false ,
NULL , MESSAGE_QUEUE_ICON_DEFAULT , MESSAGE_QUEUE_CATEGORY_INFO ) ;
}
}
old_slowmotion_button_state = new_slowmotion_button_state ;
old_slowmotion_hold_button_state = new_slowmotion_hold_button_state ;
}
}
/* Check movie record toggle */
HOTKEY_CHECK ( RARCH_BSV_RECORD_TOGGLE , CMD_EVENT_BSV_RECORDING_TOGGLE , true , NULL ) ;
/* Check shader prev/next */
HOTKEY_CHECK ( RARCH_SHADER_NEXT , CMD_EVENT_SHADER_NEXT , true , NULL ) ;
HOTKEY_CHECK ( RARCH_SHADER_PREV , CMD_EVENT_SHADER_PREV , true , NULL ) ;
/* Check if we have pressed any of the disk buttons */
HOTKEY_CHECK3 (
RARCH_DISK_EJECT_TOGGLE , CMD_EVENT_DISK_EJECT_TOGGLE ,
RARCH_DISK_NEXT , CMD_EVENT_DISK_NEXT ,
RARCH_DISK_PREV , CMD_EVENT_DISK_PREV ) ;
/* Check if we have pressed the reset button */
HOTKEY_CHECK ( RARCH_RESET , CMD_EVENT_RESET , true , NULL ) ;
/* Check cheats */
HOTKEY_CHECK3 (
RARCH_CHEAT_INDEX_PLUS , CMD_EVENT_CHEAT_INDEX_PLUS ,
RARCH_CHEAT_INDEX_MINUS , CMD_EVENT_CHEAT_INDEX_MINUS ,
RARCH_CHEAT_TOGGLE , CMD_EVENT_CHEAT_TOGGLE ) ;
# if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
if ( settings - > bools . video_shader_watch_files )
{
static rarch_timer_t timer = { 0 } ;
static bool need_to_apply = false ;
if ( video_shader_check_for_changes ( ) )
{
need_to_apply = true ;
if ( ! timer . timer_begin )
{
timer . timeout_us = SHADER_FILE_WATCH_DELAY_MSEC * 1000 ;
timer . current = cpu_features_get_time_usec ( ) ;
timer . timeout_end = timer . current + timer . timeout_us ;
timer . timer_begin = true ;
timer . timer_end = false ;
}
}
/* If a file is modified atomically (moved/renamed from a different file),
* we have no idea how long that might take .
* If we ' re trying to re - apply shaders immediately after changes are made
* to the original file ( s ) , the filesystem might be in an in - between
* state where the new file hasn ' t been moved over yet and the original
* file was already deleted . This leaves us no choice but to wait an
* arbitrary amount of time and hope for the best .
*/
if ( need_to_apply )
{
timer . current = current_time ;
timer . timeout_us = timer . timeout_end - timer . current ;
if ( ! timer . timer_end
& & timer . timeout_us < = 0 )
{
timer . timer_end = true ;
timer . timer_begin = false ;
timer . timeout_end = 0 ;
need_to_apply = false ;
command_event ( CMD_EVENT_SHADERS_APPLY_CHANGES , NULL ) ;
}
}
}
if ( settings - > uints . video_shader_delay
& & ! runloop_st - > shader_delay_timer . timer_end )
{
if ( ! runloop_st - > shader_delay_timer . timer_begin )
{
runloop_st - > shader_delay_timer . timeout_us = settings - > uints . video_shader_delay * 1000 ;
runloop_st - > shader_delay_timer . current = cpu_features_get_time_usec ( ) ;
runloop_st - > shader_delay_timer . timeout_end = runloop_st - > shader_delay_timer . current
+ runloop_st - > shader_delay_timer . timeout_us ;
runloop_st - > shader_delay_timer . timer_begin = true ;
runloop_st - > shader_delay_timer . timer_end = false ;
}
else
{
runloop_st - > shader_delay_timer . current = current_time ;
runloop_st - > shader_delay_timer . timeout_us = runloop_st - > shader_delay_timer . timeout_end
- runloop_st - > shader_delay_timer . current ;
if ( runloop_st - > shader_delay_timer . timeout_us < = 0 )
{
runloop_st - > shader_delay_timer . timer_end = true ;
runloop_st - > shader_delay_timer . timer_begin = false ;
runloop_st - > shader_delay_timer . timeout_end = 0 ;
{
const char * preset = retroarch_get_shader_preset ( ) ;
enum rarch_shader_type type = video_shader_parse_type ( preset ) ;
apply_shader ( settings , type , preset , false ) ;
}
}
}
}
# endif
return RUNLOOP_STATE_ITERATE ;
}
/**
* runloop_iterate :
*
* Run Libretro core in RetroArch for one frame .
*
* Returns : 0 on success , 1 if we have to wait until
* button input in order to wake up the loop ,
* - 1 if we forcibly quit out of the RetroArch iteration loop .
* */
int runloop_iterate ( void )
{
unsigned i ;
enum analog_dpad_mode dpad_mode [ MAX_USERS ] ;
uico_driver_state_t * uico_st = uico_state_get_ptr ( ) ;
input_driver_state_t * input_st = input_state_get_ptr ( ) ;
audio_driver_state_t * audio_st = audio_state_get_ptr ( ) ;
video_driver_state_t * video_st = video_state_get_ptr ( ) ;
recording_state_t * recording_st = recording_state_get_ptr ( ) ;
camera_driver_state_t * camera_st = camera_state_get_ptr ( ) ;
settings_t * settings = config_get_ptr ( ) ;
runloop_state_t * runloop_st = & runloop_state ;
unsigned video_frame_delay = settings - > uints . video_frame_delay ;
unsigned video_frame_delay_effective = video_st - > frame_delay_effective ;
bool vrr_runloop_enable = settings - > bools . vrr_runloop_enable ;
unsigned max_users = settings - > uints . input_max_users ;
retro_time_t current_time = cpu_features_get_time_usec ( ) ;
# ifdef HAVE_MENU
bool menu_pause_libretro = settings - > bools . menu_pause_libretro ;
bool core_paused = runloop_st - > paused | | ( menu_pause_libretro & & menu_state_get_ptr ( ) - > alive ) ;
# else
bool core_paused = runloop_st - > paused ;
# endif
float slowmotion_ratio = settings - > floats . slowmotion_ratio ;
# ifdef HAVE_CHEEVOS
bool cheevos_enable = settings - > bools . cheevos_enable ;
# endif
bool audio_sync = settings - > bools . audio_sync ;
# ifdef HAVE_DISCORD
discord_state_t * discord_st = discord_state_get_ptr ( ) ;
if ( discord_st - > inited )
{
Discord_RunCallbacks ( ) ;
# ifdef DISCORD_DISABLE_IO_THREAD
Discord_UpdateConnection ( ) ;
# endif
}
# endif
if ( runloop_st - > frame_time . callback )
{
/* Updates frame timing if frame timing callback is in use by the core.
* Limits frame time if fast forward ratio throttle is enabled . */
retro_usec_t runloop_last_frame_time = runloop_st - > frame_time_last ;
retro_time_t current = current_time ;
bool is_locked_fps = ( runloop_st - > paused
| | input_st - > nonblocking_flag )
| ! ! recording_st - > data ;
retro_time_t delta = ( ! runloop_last_frame_time | | is_locked_fps )
? runloop_st - > frame_time . reference
: ( current - runloop_last_frame_time ) ;
if ( is_locked_fps )
runloop_st - > frame_time_last = 0 ;
else
{
runloop_st - > frame_time_last = current ;
if ( runloop_st - > slowmotion )
delta / = slowmotion_ratio ;
}
if ( ! core_paused )
runloop_st - > frame_time . callback ( delta ) ;
}
/* Update audio buffer occupancy if buffer status
* callback is in use by the core */
if ( runloop_st - > audio_buffer_status . callback )
{
bool audio_buf_active = false ;
unsigned audio_buf_occupancy = 0 ;
bool audio_buf_underrun = false ;
if ( ! ( runloop_st - > paused | |
! audio_st - > active | |
! audio_st - > output_samples_buf ) & &
audio_st - > current_audio - > write_avail & &
audio_st - > context_audio_data & &
audio_st - > buffer_size )
{
size_t audio_buf_avail ;
if ( ( audio_buf_avail = audio_st - > current_audio - > write_avail (
audio_st - > context_audio_data ) ) > audio_st - > buffer_size )
audio_buf_avail = audio_st - > buffer_size ;
audio_buf_occupancy = ( unsigned ) ( 100 - ( audio_buf_avail * 100 ) /
audio_st - > buffer_size ) ;
/* Elsewhere, we standardise on a 'low water mark'
* of 25 % of the total audio buffer size - use
* the same metric here ( can be made more sophisticated
* if required - i . e . determine buffer occupancy in
* terms of usec , and weigh this against the expected
* frame time ) */
audio_buf_underrun = audio_buf_occupancy < 25 ;
audio_buf_active = true ;
}
if ( ! core_paused )
runloop_st - > audio_buffer_status . callback (
audio_buf_active , audio_buf_occupancy , audio_buf_underrun ) ;
}
switch ( ( enum runloop_state_enum ) runloop_check_state (
global_get_ptr ( ) - > error_on_init ,
settings , current_time ) )
{
case RUNLOOP_STATE_QUIT :
runloop_st - > frame_limit_last_time = 0.0 ;
runloop_st - > core_running = false ;
command_event ( CMD_EVENT_QUIT , NULL ) ;
return - 1 ;
case RUNLOOP_STATE_POLLED_AND_SLEEP :
# ifdef HAVE_NETWORKING
/* FIXME: This is an ugly way to tell Netplay this... */
netplay_driver_ctl ( RARCH_NETPLAY_CTL_PAUSE , NULL ) ;
# endif
# if defined(HAVE_COCOATOUCH)
if ( ! uico_st - > is_on_foreground )
# endif
retro_sleep ( 10 ) ;
return 1 ;
case RUNLOOP_STATE_END :
# ifdef HAVE_NETWORKING
# ifdef HAVE_MENU
/* FIXME: This is an ugly way to tell Netplay this... */
if ( menu_pause_libretro & &
netplay_driver_ctl ( RARCH_NETPLAY_CTL_IS_ENABLED , NULL )
)
netplay_driver_ctl ( RARCH_NETPLAY_CTL_PAUSE , NULL ) ;
# endif
# endif
goto end ;
case RUNLOOP_STATE_MENU_ITERATE :
# ifdef HAVE_NETWORKING
/* FIXME: This is an ugly way to tell Netplay this... */
netplay_driver_ctl ( RARCH_NETPLAY_CTL_PAUSE , NULL ) ;
# endif
return 0 ;
case RUNLOOP_STATE_ITERATE :
runloop_st - > core_running = true ;
break ;
}
# ifdef HAVE_THREADS
if ( runloop_st - > autosave )
autosave_lock ( ) ;
# endif
# ifdef HAVE_BSV_MOVIE
/* Used for rewinding while playback/record. */
if ( input_st - > bsv_movie_state_handle )
input_st - > bsv_movie_state_handle - > frame_pos [ input_st - > bsv_movie_state_handle - > frame_ptr ]
= intfstream_tell ( input_st - > bsv_movie_state_handle - > file ) ;
# endif
if ( camera_st - > cb . caps
& & camera_st - > driver
& & camera_st - > driver - > poll
& & camera_st - > data )
camera_st - > driver - > poll ( camera_st - > data ,
camera_st - > cb . frame_raw_framebuffer ,
camera_st - > cb . frame_opengl_texture ) ;
/* Update binds for analog dpad modes. */
for ( i = 0 ; i < max_users ; i + + )
{
dpad_mode [ i ] = ( enum analog_dpad_mode )
settings - > uints . input_analog_dpad_mode [ i ] ;
switch ( dpad_mode [ i ] )
{
case ANALOG_DPAD_LSTICK :
case ANALOG_DPAD_RSTICK :
{
unsigned mapped_port = settings - > uints . input_remap_ports [ i ] ;
if ( input_st - > analog_requested [ mapped_port ] )
dpad_mode [ i ] = ANALOG_DPAD_NONE ;
}
break ;
case ANALOG_DPAD_LSTICK_FORCED :
dpad_mode [ i ] = ANALOG_DPAD_LSTICK ;
break ;
case ANALOG_DPAD_RSTICK_FORCED :
dpad_mode [ i ] = ANALOG_DPAD_RSTICK ;
break ;
default :
break ;
}
/* Push analog to D-Pad mappings to binds. */
if ( dpad_mode [ i ] ! = ANALOG_DPAD_NONE )
{
unsigned k ;
unsigned joy_idx = settings - > uints . input_joypad_index [ i ] ;
struct retro_keybind * general_binds = input_config_binds [ joy_idx ] ;
struct retro_keybind * auto_binds = input_autoconf_binds [ joy_idx ] ;
unsigned x_plus = RARCH_ANALOG_RIGHT_X_PLUS ;
unsigned y_plus = RARCH_ANALOG_RIGHT_Y_PLUS ;
unsigned x_minus = RARCH_ANALOG_RIGHT_X_MINUS ;
unsigned y_minus = RARCH_ANALOG_RIGHT_Y_MINUS ;
if ( dpad_mode [ i ] = = ANALOG_DPAD_LSTICK )
{
x_plus = RARCH_ANALOG_LEFT_X_PLUS ;
y_plus = RARCH_ANALOG_LEFT_Y_PLUS ;
x_minus = RARCH_ANALOG_LEFT_X_MINUS ;
y_minus = RARCH_ANALOG_LEFT_Y_MINUS ;
}
for ( k = RETRO_DEVICE_ID_JOYPAD_UP ; k < = RETRO_DEVICE_ID_JOYPAD_RIGHT ; k + + )
{
( auto_binds ) [ k ] . orig_joyaxis = ( auto_binds ) [ k ] . joyaxis ;
( general_binds ) [ k ] . orig_joyaxis = ( general_binds ) [ k ] . joyaxis ;
}
if ( ! INHERIT_JOYAXIS ( auto_binds ) )
{
unsigned j = x_plus + 3 ;
/* Inherit joyaxis from analogs. */
for ( k = RETRO_DEVICE_ID_JOYPAD_UP ; k < = RETRO_DEVICE_ID_JOYPAD_RIGHT ; k + + )
( auto_binds ) [ k ] . joyaxis = ( auto_binds ) [ j - - ] . joyaxis ;
}
if ( ! INHERIT_JOYAXIS ( general_binds ) )
{
unsigned j = x_plus + 3 ;
/* Inherit joyaxis from analogs. */
for ( k = RETRO_DEVICE_ID_JOYPAD_UP ; k < = RETRO_DEVICE_ID_JOYPAD_RIGHT ; k + + )
( general_binds ) [ k ] . joyaxis = ( general_binds ) [ j - - ] . joyaxis ;
}
}
}
if ( input_st & & ! input_st - > nonblocking_flag )
{
if ( settings - > bools . video_frame_delay_auto )
{
float refresh_rate = settings - > floats . video_refresh_rate ;
unsigned frame_time_interval = 8 ;
bool frame_time_update =
/* Skip some starting frames for stabilization */
video_st - > frame_count > 10 & &
video_st - > frame_count % frame_time_interval = = 0 ;
/* Set target moderately as half frame time with 0 delay */
if ( video_frame_delay = = 0 )
video_frame_delay = 1 / refresh_rate * 1000 / 2 ;
if ( video_st - > frame_delay_target ! = video_frame_delay )
{
video_st - > frame_delay_target = video_frame_delay_effective = video_frame_delay ;
RARCH_LOG ( " [Video]: Frame delay reset to %d. \n " , video_frame_delay ) ;
}
if ( video_frame_delay_effective > 0 & & frame_time_update )
{
unsigned i = 0 ;
unsigned frame_time = 0 ;
unsigned frame_time_frames = frame_time_interval - 1 ;
unsigned frame_time_target = 1000000.0f / refresh_rate ;
unsigned frame_time_limit_min = frame_time_target * 1.25 ;
unsigned frame_time_limit_med = frame_time_target * 1.50 ;
unsigned frame_time_limit_max = frame_time_target * 1.90 ;
unsigned frame_time_limit_cap = frame_time_target * 2.25 ;
unsigned frame_time_limit_ign = frame_time_target * 2.50 ;
unsigned frame_time_index =
( video_st - > frame_time_count &
( MEASURE_FRAME_TIME_SAMPLES_COUNT - 1 ) ) ;
/* Calculate average frame time to balance spikes */
for ( i = 1 ; i < frame_time_frames + 1 ; i + + )
{
retro_time_t frame_time_i = 0 ;
if ( i > ( unsigned ) frame_time_index )
continue ;
frame_time_i = video_st - > frame_time_samples [ frame_time_index - i ] ;
/* Ignore values when core is doing internal frame skipping */
if ( frame_time_i > frame_time_limit_ign )
frame_time_i = 0 ;
/* Limit maximum to prevent false positives */
else if ( frame_time_i > frame_time_limit_cap )
frame_time_i = frame_time_limit_cap ;
frame_time + = frame_time_i ;
}
frame_time / = frame_time_frames ;
if ( frame_time > frame_time_limit_min )
{
unsigned delay_decrease = 1 ;
/* Increase decrease the more frame time is off target */
if ( frame_time > frame_time_limit_med & & video_frame_delay_effective > delay_decrease )
{
delay_decrease + + ;
if ( frame_time > frame_time_limit_max & & video_frame_delay_effective > delay_decrease )
delay_decrease + + ;
}
video_frame_delay_effective - = delay_decrease ;
RARCH_LOG ( " [Video]: Frame delay decrease by %d to %d due to frame time: %d > %d. \n " ,
delay_decrease , video_frame_delay_effective , frame_time , frame_time_target ) ;
}
}
}
else
video_st - > frame_delay_target = video_frame_delay_effective = video_frame_delay ;
video_st - > frame_delay_effective = video_frame_delay_effective ;
if ( video_frame_delay_effective > 0 )
retro_sleep ( video_frame_delay_effective ) ;
}
{
# ifdef HAVE_RUNAHEAD
bool run_ahead_enabled = settings - > bools . run_ahead_enabled ;
unsigned run_ahead_num_frames = settings - > uints . run_ahead_frames ;
bool run_ahead_hide_warnings = settings - > bools . run_ahead_hide_warnings ;
bool run_ahead_secondary_instance = settings - > bools . run_ahead_secondary_instance ;
/* Run Ahead Feature replaces the call to core_run in this loop */
bool want_runahead = run_ahead_enabled & & run_ahead_num_frames > 0 ;
# ifdef HAVE_NETWORKING
want_runahead = want_runahead & & ! netplay_driver_ctl ( RARCH_NETPLAY_CTL_IS_ENABLED , NULL ) ;
# endif
if ( want_runahead )
do_runahead (
runloop_st ,
run_ahead_num_frames ,
run_ahead_hide_warnings ,
run_ahead_secondary_instance ) ;
else
# endif
core_run ( ) ;
}
/* Increment runtime tick counter after each call to
* core_run ( ) or run_ahead ( ) */
runloop_st - > core_runtime_usec + = runloop_core_runtime_tick (
runloop_st ,
slowmotion_ratio ,
current_time ) ;
# ifdef HAVE_CHEEVOS
if ( cheevos_enable )
rcheevos_test ( ) ;
# endif
# ifdef HAVE_CHEATS
cheat_manager_apply_retro_cheats ( ) ;
# endif
# ifdef HAVE_DISCORD
if ( discord_st - > inited & & discord_st - > ready )
discord_update ( DISCORD_PRESENCE_GAME ) ;
# endif
/* Restores analog D-pad binds temporarily overridden. */
for ( i = 0 ; i < max_users ; i + + )
{
if ( dpad_mode [ i ] ! = ANALOG_DPAD_NONE )
{
unsigned j ;
unsigned joy_idx = settings - > uints . input_joypad_index [ i ] ;
struct retro_keybind * general_binds = input_config_binds [ joy_idx ] ;
struct retro_keybind * auto_binds = input_autoconf_binds [ joy_idx ] ;
for ( j = RETRO_DEVICE_ID_JOYPAD_UP ; j < = RETRO_DEVICE_ID_JOYPAD_RIGHT ; j + + )
{
( auto_binds ) [ j ] . joyaxis = ( auto_binds ) [ j ] . orig_joyaxis ;
( general_binds ) [ j ] . joyaxis = ( general_binds ) [ j ] . orig_joyaxis ;
}
}
}
# ifdef HAVE_BSV_MOVIE
if ( input_st - > bsv_movie_state_handle )
{
input_st - > bsv_movie_state_handle - > frame_ptr =
( input_st - > bsv_movie_state_handle - > frame_ptr + 1 )
& input_st - > bsv_movie_state_handle - > frame_mask ;
input_st - > bsv_movie_state_handle - > first_rewind =
! input_st - > bsv_movie_state_handle - > did_rewind ;
input_st - > bsv_movie_state_handle - > did_rewind = false ;
}
# endif
# ifdef HAVE_THREADS
if ( runloop_st - > autosave )
autosave_unlock ( ) ;
# endif
end :
if ( vrr_runloop_enable )
{
/* Sync on video only, block audio later. */
if ( runloop_st - > fastforward_after_frames & & audio_sync )
{
if ( runloop_st - > fastforward_after_frames = = 1 )
{
/* Nonblocking audio */
if ( audio_st - > active & &
audio_st - > context_audio_data )
audio_st - > current_audio - > set_nonblock_state (
audio_st - > context_audio_data , true ) ;
audio_st - > chunk_size =
audio_st - > chunk_nonblock_size ;
}
runloop_st - > fastforward_after_frames + + ;
if ( runloop_st - > fastforward_after_frames = = 6 )
{
/* Blocking audio */
if ( audio_st - > active & &
audio_st - > context_audio_data )
audio_st - > current_audio - > set_nonblock_state (
audio_st - > context_audio_data ,
audio_sync ? false : true ) ;
audio_st - > chunk_size = audio_st - > chunk_block_size ;
runloop_st - > fastforward_after_frames = 0 ;
}
}
if ( runloop_st - > fastmotion )
runloop_st - > frame_limit_minimum_time =
runloop_set_frame_limit ( & video_st - > av_info ,
runloop_get_fastforward_ratio ( settings ,
& runloop_st - > fastmotion_override . current ) ) ;
else
runloop_st - > frame_limit_minimum_time =
runloop_set_frame_limit ( & video_st - > av_info ,
1.0f ) ;
}
/* if there's a fast forward limit, inject sleeps to keep from going too fast. */
if ( runloop_st - > frame_limit_minimum_time )
{
const retro_time_t end_frame_time = cpu_features_get_time_usec ( ) ;
const retro_time_t to_sleep_ms = (
( runloop_st - > frame_limit_last_time
+ runloop_st - > frame_limit_minimum_time )
- end_frame_time ) / 1000 ;
if ( to_sleep_ms > 0 )
{
unsigned sleep_ms = ( unsigned ) to_sleep_ms ;
/* Combat jitter a bit. */
runloop_st - > frame_limit_last_time + =
runloop_st - > frame_limit_minimum_time ;
if ( sleep_ms > 0 )
{
# if defined(HAVE_COCOATOUCH)
if ( ! uico_state_get_ptr ( ) - > is_on_foreground )
# endif
retro_sleep ( sleep_ms ) ;
}
return 1 ;
}
runloop_st - > frame_limit_last_time = end_frame_time ;
}
return 0 ;
}
void runloop_msg_queue_deinit ( void )
{
runloop_state_t * runloop_st = & runloop_state ;
RUNLOOP_MSG_QUEUE_LOCK ( runloop_st ) ;
msg_queue_deinitialize ( & runloop_st - > msg_queue ) ;
RUNLOOP_MSG_QUEUE_UNLOCK ( runloop_st ) ;
# ifdef HAVE_THREADS
slock_free ( runloop_st - > msg_queue_lock ) ;
runloop_st - > msg_queue_lock = NULL ;
# endif
runloop_st - > msg_queue_size = 0 ;
}
void runloop_msg_queue_init ( void )
{
runloop_state_t * runloop_st = & runloop_state ;
runloop_msg_queue_deinit ( ) ;
msg_queue_initialize ( & runloop_st - > msg_queue , 8 ) ;
# ifdef HAVE_THREADS
runloop_st - > msg_queue_lock = slock_new ( ) ;
# endif
}
void runloop_task_msg_queue_push (
retro_task_t * task , const char * msg ,
unsigned prio , unsigned duration ,
bool flush )
{
# if defined(HAVE_GFX_WIDGETS)
# ifdef HAVE_MENU
struct menu_state * menu_st = menu_state_get_ptr ( ) ;
# endif
# ifdef HAVE_ACCESSIBILITY
access_state_t * access_st = access_state_get_ptr ( ) ;
settings_t * settings = config_get_ptr ( ) ;
bool accessibility_enable = settings - > bools . accessibility_enable ;
unsigned accessibility_narrator_speech_speed = settings - > uints . accessibility_narrator_speech_speed ;
# endif
runloop_state_t * runloop_st = & runloop_state ;
bool widgets_active = dispwidget_get_ptr ( ) - > active ;
if ( widgets_active & & task - > title & & ! task - > mute )
{
RUNLOOP_MSG_QUEUE_LOCK ( runloop_st ) ;
ui_companion_driver_msg_queue_push ( msg ,
prio , task ? duration : duration * 60 / 1000 , flush ) ;
# ifdef HAVE_ACCESSIBILITY
if ( is_accessibility_enabled (
accessibility_enable ,
access_st - > enabled ) )
accessibility_speak_priority (
accessibility_enable ,
accessibility_narrator_speech_speed ,
( char * ) msg , 0 ) ;
# endif
gfx_widgets_msg_queue_push (
task ,
msg ,
duration ,
NULL ,
( enum message_queue_icon ) MESSAGE_QUEUE_CATEGORY_INFO ,
( enum message_queue_category ) MESSAGE_QUEUE_ICON_DEFAULT ,
prio ,
flush ,
# ifdef HAVE_MENU
menu_st - > alive
# else
false
# endif
) ;
RUNLOOP_MSG_QUEUE_UNLOCK ( runloop_st ) ;
}
else
# endif
runloop_msg_queue_push ( msg , prio , duration , flush , NULL , MESSAGE_QUEUE_ICON_DEFAULT , MESSAGE_QUEUE_CATEGORY_INFO ) ;
}
bool retroarch_get_current_savestate_path ( char * path , size_t len )
{
runloop_state_t * runloop_st = & runloop_state ;
settings_t * settings = config_get_ptr ( ) ;
int state_slot = settings ? settings - > ints . state_slot : 0 ;
const char * name_savestate = NULL ;
if ( ! path )
return false ;
name_savestate = runloop_st - > name . savestate ;
if ( string_is_empty ( name_savestate ) )
return false ;
if ( state_slot > 0 )
snprintf ( path , len , " %s%d " , name_savestate , state_slot ) ;
else if ( state_slot < 0 )
fill_pathname_join_delim ( path , name_savestate , " auto " , ' . ' , len ) ;
else
strlcpy ( path , name_savestate , len ) ;
return true ;
}
void runloop_set_current_core_type (
enum rarch_core_type type , bool explicitly_set )
{
runloop_state_t * runloop_st = & runloop_state ;
if ( runloop_st - > has_set_core )
return ;
if ( explicitly_set )
{
runloop_st - > has_set_core = true ;
runloop_st - > explicit_current_core_type = type ;
}
runloop_st - > current_core_type = type ;
}
bool core_set_default_callbacks ( void * data )
{
struct retro_callbacks * cbs = ( struct retro_callbacks * ) data ;
retro_input_state_t state_cb = core_input_state_poll_return_cb ( ) ;
cbs - > frame_cb = video_driver_frame ;
cbs - > sample_cb = audio_driver_sample ;
cbs - > sample_batch_cb = audio_driver_sample_batch ;
cbs - > state_cb = state_cb ;
cbs - > poll_cb = input_driver_poll ;
return true ;
}
# ifdef HAVE_REWIND
/**
* core_set_rewind_callbacks :
*
* Sets the audio sampling callbacks based on whether or not
* rewinding is currently activated .
* */
bool core_set_rewind_callbacks ( void )
{
runloop_state_t * runloop_st = & runloop_state ;
struct state_manager_rewind_state
* rewind_st = & runloop_st - > rewind_st ;
if ( rewind_st - > frame_is_reversed )
{
runloop_st - > current_core . retro_set_audio_sample ( audio_driver_sample_rewind ) ;
runloop_st - > current_core . retro_set_audio_sample_batch ( audio_driver_sample_batch_rewind ) ;
}
else
{
runloop_st - > current_core . retro_set_audio_sample ( audio_driver_sample ) ;
runloop_st - > current_core . retro_set_audio_sample_batch ( audio_driver_sample_batch ) ;
}
return true ;
}
# endif
# ifdef HAVE_NETWORKING
/**
* core_set_netplay_callbacks :
*
* Set the I / O callbacks to use netplay ' s interceding callback system . Should
* only be called while initializing netplay .
* */
bool core_set_netplay_callbacks ( void )
{
runloop_state_t * runloop_st = & runloop_state ;
/* Force normal poll type for netplay. */
runloop_st - > current_core . poll_type = POLL_TYPE_NORMAL ;
/* And use netplay's interceding callbacks */
runloop_st - > current_core . retro_set_video_refresh ( video_frame_net ) ;
runloop_st - > current_core . retro_set_audio_sample ( audio_sample_net ) ;
runloop_st - > current_core . retro_set_audio_sample_batch ( audio_sample_batch_net ) ;
runloop_st - > current_core . retro_set_input_state ( input_state_net ) ;
return true ;
}
/**
* core_unset_netplay_callbacks
*
* Unset the I / O callbacks from having used netplay ' s interceding callback
* system . Should only be called while uninitializing netplay .
*/
bool core_unset_netplay_callbacks ( void )
{
struct retro_callbacks cbs ;
runloop_state_t * runloop_st = & runloop_state ;
if ( ! core_set_default_callbacks ( & cbs ) )
return false ;
runloop_st - > current_core . retro_set_video_refresh ( cbs . frame_cb ) ;
runloop_st - > current_core . retro_set_audio_sample ( cbs . sample_cb ) ;
runloop_st - > current_core . retro_set_audio_sample_batch ( cbs . sample_batch_cb ) ;
runloop_st - > current_core . retro_set_input_state ( cbs . state_cb ) ;
return true ;
}
# endif
bool core_set_cheat ( retro_ctx_cheat_info_t * info )
{
runloop_state_t * runloop_st = & runloop_state ;
# if defined(HAVE_RUNAHEAD) && (defined(HAVE_DYNAMIC) || defined(HAVE_DYLIB))
settings_t * settings = config_get_ptr ( ) ;
bool run_ahead_enabled = false ;
unsigned run_ahead_frames = 0 ;
bool run_ahead_secondary_instance = false ;
bool want_runahead = false ;
if ( settings )
{
run_ahead_enabled = settings - > bools . run_ahead_enabled ;
run_ahead_frames = settings - > uints . run_ahead_frames ;
run_ahead_secondary_instance = settings - > bools . run_ahead_secondary_instance ;
want_runahead = run_ahead_enabled & & ( run_ahead_frames > 0 ) ;
# ifdef HAVE_NETWORKING
if ( want_runahead )
want_runahead = ! netplay_driver_ctl ( RARCH_NETPLAY_CTL_IS_ENABLED , NULL ) ;
# endif
}
# endif
runloop_st - > current_core . retro_cheat_set ( info - > index , info - > enabled , info - > code ) ;
# if defined(HAVE_RUNAHEAD) && (defined(HAVE_DYNAMIC) || defined(HAVE_DYLIB))
if ( want_runahead
& & run_ahead_secondary_instance
& & runloop_st - > runahead_secondary_core_available
& & secondary_core_ensure_exists ( settings )
& & runloop_st - > secondary_core . retro_cheat_set )
runloop_st - > secondary_core . retro_cheat_set (
info - > index , info - > enabled , info - > code ) ;
# endif
return true ;
}
bool core_reset_cheat ( void )
{
runloop_state_t * runloop_st = & runloop_state ;
# if defined(HAVE_RUNAHEAD) && (defined(HAVE_DYNAMIC) || defined(HAVE_DYLIB))
settings_t * settings = config_get_ptr ( ) ;
bool run_ahead_enabled = false ;
unsigned run_ahead_frames = 0 ;
bool run_ahead_secondary_instance = false ;
bool want_runahead = false ;
if ( settings )
{
run_ahead_enabled = settings - > bools . run_ahead_enabled ;
run_ahead_frames = settings - > uints . run_ahead_frames ;
run_ahead_secondary_instance = settings - > bools . run_ahead_secondary_instance ;
want_runahead = run_ahead_enabled & & ( run_ahead_frames > 0 ) ;
# ifdef HAVE_NETWORKING
if ( want_runahead )
want_runahead = ! netplay_driver_ctl ( RARCH_NETPLAY_CTL_IS_ENABLED , NULL ) ;
# endif
}
# endif
runloop_st - > current_core . retro_cheat_reset ( ) ;
# if defined(HAVE_RUNAHEAD) && (defined(HAVE_DYNAMIC) || defined(HAVE_DYLIB))
if ( want_runahead
& & run_ahead_secondary_instance
& & runloop_st - > runahead_secondary_core_available
& & secondary_core_ensure_exists ( settings )
& & runloop_st - > secondary_core . retro_cheat_reset )
runloop_st - > secondary_core . retro_cheat_reset ( ) ;
# endif
return true ;
}
bool core_set_poll_type ( unsigned type )
{
runloop_state_t * runloop_st = & runloop_state ;
runloop_st - > current_core . poll_type = type ;
return true ;
}
bool core_set_controller_port_device ( retro_ctx_controller_info_t * pad )
{
runloop_state_t * runloop_st = & runloop_state ;
input_driver_state_t * input_st = input_state_get_ptr ( ) ;
if ( ! pad )
return false ;
/* We are potentially 'connecting' a entirely different
* type of virtual input device , which may or may not
* support analog inputs . We therefore have to reset
* the ' analog input requested ' flag for this port - but
* since port mapping is arbitrary / mutable , it is easiest
* to simply reset the flags for all ports .
* Correct values will be registered at the next call
* of ' input_state ( ) ' */
memset ( & input_st - > analog_requested , 0 ,
sizeof ( input_st - > analog_requested ) ) ;
# ifdef HAVE_RUNAHEAD
remember_controller_port_device ( pad - > port , pad - > device ) ;
# endif
runloop_st - > current_core . retro_set_controller_port_device ( pad - > port , pad - > device ) ;
return true ;
}
bool core_get_memory ( retro_ctx_memory_info_t * info )
{
runloop_state_t * runloop_st = & runloop_state ;
if ( ! info )
return false ;
info - > size = runloop_st - > current_core . retro_get_memory_size ( info - > id ) ;
info - > data = runloop_st - > current_core . retro_get_memory_data ( info - > id ) ;
return true ;
}
bool core_load_game ( retro_ctx_load_content_info_t * load_info )
{
bool contentless = false ;
bool is_inited = false ;
bool game_loaded = false ;
runloop_state_t * runloop_st = & runloop_state ;
video_driver_set_cached_frame_ptr ( NULL ) ;
# ifdef HAVE_RUNAHEAD
set_load_content_info ( runloop_st , load_info ) ;
runloop_clear_controller_port_map ( ) ;
# endif
content_get_status ( & contentless , & is_inited ) ;
set_save_state_in_background ( false ) ;
if ( load_info & & load_info - > special )
game_loaded = runloop_st - > current_core . retro_load_game_special (
load_info - > special - > id , load_info - > info , load_info - > content - > size ) ;
else if ( load_info & & ! string_is_empty ( load_info - > content - > elems [ 0 ] . data ) )
game_loaded = runloop_st - > current_core . retro_load_game ( load_info - > info ) ;
else if ( contentless )
game_loaded = runloop_st - > current_core . retro_load_game ( NULL ) ;
runloop_st - > current_core . game_loaded = game_loaded ;
/* If 'game_loaded' is true at this point, then
* core is actually running ; register that any
* changes to global remap - related parameters
* should be reset once core is deinitialised */
if ( game_loaded )
input_remapping_enable_global_config_restore ( ) ;
return game_loaded ;
}
bool core_get_system_info ( struct retro_system_info * system )
{
runloop_state_t * runloop_st = & runloop_state ;
if ( ! system )
return false ;
runloop_st - > current_core . retro_get_system_info ( system ) ;
return true ;
}
bool core_unserialize ( retro_ctx_serialize_info_t * info )
{
runloop_state_t * runloop_st = & runloop_state ;
if ( ! info | | ! runloop_st - > current_core . retro_unserialize ( info - > data_const , info - > size ) )
return false ;
# if HAVE_NETWORKING
netplay_driver_ctl ( RARCH_NETPLAY_CTL_LOAD_SAVESTATE , info ) ;
# endif
return true ;
}
bool core_serialize ( retro_ctx_serialize_info_t * info )
{
runloop_state_t * runloop_st = & runloop_state ;
if ( ! info | | ! runloop_st - > current_core . retro_serialize ( info - > data , info - > size ) )
return false ;
return true ;
}
bool core_serialize_size ( retro_ctx_size_info_t * info )
{
runloop_state_t * runloop_st = & runloop_state ;
if ( ! info )
return false ;
info - > size = runloop_st - > current_core . retro_serialize_size ( ) ;
return true ;
}
uint64_t core_serialization_quirks ( void )
{
runloop_state_t * runloop_st = & runloop_state ;
return runloop_st - > current_core . serialization_quirks_v ;
}
bool core_reset ( void )
{
runloop_state_t * runloop_st = & runloop_state ;
video_driver_set_cached_frame_ptr ( NULL ) ;
runloop_st - > current_core . retro_reset ( ) ;
return true ;
}
bool core_run ( void )
{
runloop_state_t * runloop_st = & runloop_state ;
struct retro_core_t *
current_core = & runloop_st - > current_core ;
const enum poll_type_override_t
core_poll_type_override = runloop_st - > core_poll_type_override ;
unsigned new_poll_type = ( core_poll_type_override ! = POLL_TYPE_OVERRIDE_DONTCARE )
? ( core_poll_type_override - 1 )
: current_core - > poll_type ;
bool early_polling = new_poll_type = = POLL_TYPE_EARLY ;
bool late_polling = new_poll_type = = POLL_TYPE_LATE ;
# ifdef HAVE_NETWORKING
bool netplay_preframe = netplay_driver_ctl (
RARCH_NETPLAY_CTL_PRE_FRAME , NULL ) ;
if ( ! netplay_preframe )
{
/* Paused due to netplay. We must poll and display something so that a
* netplay peer pausing doesn ' t just hang . */
input_driver_poll ( ) ;
video_driver_cached_frame ( ) ;
return true ;
}
# endif
if ( early_polling )
input_driver_poll ( ) ;
else if ( late_polling )
current_core - > input_polled = false ;
current_core - > retro_run ( ) ;
if ( late_polling & & ! current_core - > input_polled )
input_driver_poll ( ) ;
# ifdef HAVE_NETWORKING
netplay_driver_ctl ( RARCH_NETPLAY_CTL_POST_FRAME , NULL ) ;
# endif
return true ;
}
bool core_has_set_input_descriptor ( void )
{
runloop_state_t * runloop_st = & runloop_state ;
return runloop_st - > current_core . has_set_input_descriptors ;
}
char * crt_switch_core_name ( void )
{
return ( char * ) runloop_state . system . info . library_name ;
}
2021-11-22 02:33:58 +00:00
void runloop_path_set_basename ( const char * path )
{
runloop_state_t * runloop_st = & runloop_state ;
char * dst = NULL ;
path_set ( RARCH_PATH_CONTENT , path ) ;
path_set ( RARCH_PATH_BASENAME , path ) ;
# ifdef HAVE_COMPRESSION
/* Removing extension is a bit tricky for compressed files.
* Basename means :
* / file / to / path / game . extension should be :
* / file / to / path / game
*
* Two things to consider here are : / file / to / path / is expected
* to be a directory and " game " is a single file . This is used for
* states and srm default paths .
*
* For compressed files we have :
*
* / file / to / path / comp .7 z # game . extension and
* / file / to / path / comp .7 z # folder / game . extension
*
* The choice I take here is :
* / file / to / path / game as basename . We might end up in a writable
* directory then and the name of srm and states are meaningful .
*
*/
path_basedir_wrapper ( runloop_st - > runtime_content_path_basename ) ;
if ( ! string_is_empty ( runloop_st - > runtime_content_path_basename ) )
fill_pathname_dir ( runloop_st - > runtime_content_path_basename , path , " " , sizeof ( runloop_st - > runtime_content_path_basename ) ) ;
# endif
if ( ( dst = strrchr ( runloop_st - > runtime_content_path_basename , ' . ' ) ) )
* dst = ' \0 ' ;
}
void runloop_path_set_names ( void )
{
runloop_state_t * runloop_st = & runloop_state ;
if ( ! retroarch_override_setting_is_set (
RARCH_OVERRIDE_SETTING_SAVE_PATH , NULL ) )
fill_pathname_noext ( runloop_st - > name . savefile ,
runloop_st - > runtime_content_path_basename ,
" .srm " , sizeof ( runloop_st - > name . savefile ) ) ;
if ( ! retroarch_override_setting_is_set (
RARCH_OVERRIDE_SETTING_STATE_PATH , NULL ) )
fill_pathname_noext ( runloop_st - > name . savestate ,
runloop_st - > runtime_content_path_basename ,
" .state " , sizeof ( runloop_st - > name . savestate ) ) ;
# ifdef HAVE_CHEATS
if ( ! string_is_empty ( runloop_st - > runtime_content_path_basename ) )
fill_pathname_noext ( runloop_st - > name . cheatfile ,
runloop_st - > runtime_content_path_basename ,
" .cht " , sizeof ( runloop_st - > name . cheatfile ) ) ;
# endif
}