mirror of
https://github.com/libretro/RetroArch
synced 2025-03-03 04:14:00 +00:00
Runahead system
This commit is contained in:
parent
2143540c8c
commit
f5e0346fc2
@ -589,6 +589,12 @@ static const float slowmotion_ratio = 3.0;
|
||||
/* Maximum fast forward ratio. */
|
||||
static const float fastforward_ratio = 0.0;
|
||||
|
||||
/* Run core logic one or more frames ahead then load the state back to reduce perceived input lag. */
|
||||
static const unsigned run_ahead_frames = 1;
|
||||
|
||||
/* When using the Run Ahead feature, use a secondary instance of the core. */
|
||||
static const bool run_ahead_secondary_instance = true;
|
||||
|
||||
/* Enable stdin/network command interface. */
|
||||
static const bool network_cmd_enable = false;
|
||||
static const uint16_t network_cmd_port = 55355;
|
||||
|
@ -1220,6 +1220,8 @@ static struct config_bool_setting *populate_settings_bool(settings_t *settings,
|
||||
SETTING_BOOL("ui_menubar_enable", &settings->bools.ui_menubar_enable, true, true, false);
|
||||
SETTING_BOOL("suspend_screensaver_enable", &settings->bools.ui_suspend_screensaver_enable, true, true, false);
|
||||
SETTING_BOOL("rewind_enable", &settings->bools.rewind_enable, true, rewind_enable, false);
|
||||
SETTING_BOOL("run_ahead_enabled", &settings->bools.run_ahead_enabled, true, false, false);
|
||||
SETTING_BOOL("run_ahead_secondary_instance", &settings->bools.run_ahead_secondary_instance, true, false, false);
|
||||
SETTING_BOOL("audio_sync", &settings->bools.audio_sync, true, audio_sync, false);
|
||||
SETTING_BOOL("video_shader_enable", &settings->bools.video_shader_enable, true, shader_enable, false);
|
||||
SETTING_BOOL("video_shader_watch_files", &settings->bools.video_shader_watch_files, true, video_shader_watch_files, false);
|
||||
@ -1513,6 +1515,8 @@ static struct config_uint_setting *populate_settings_uint(settings_t *settings,
|
||||
SETTING_UINT("video_msg_bgcolor_green", &settings->uints.video_msg_bgcolor_green, true, message_bgcolor_green, false);
|
||||
SETTING_UINT("video_msg_bgcolor_blue", &settings->uints.video_msg_bgcolor_blue, true, message_bgcolor_blue, false);
|
||||
|
||||
SETTING_UINT("run_ahead_frames", &settings->uints.run_ahead_frames, true, 1, false);
|
||||
|
||||
*size = count;
|
||||
|
||||
return tmp;
|
||||
|
@ -223,6 +223,8 @@ typedef struct settings
|
||||
bool playlist_entry_remove;
|
||||
bool playlist_entry_rename;
|
||||
bool rewind_enable;
|
||||
bool run_ahead_enabled;
|
||||
bool run_ahead_secondary_instance;
|
||||
bool pause_nonactive;
|
||||
bool block_sram_overwrite;
|
||||
bool savestate_auto_index;
|
||||
@ -380,6 +382,8 @@ typedef struct settings
|
||||
unsigned input_remap_ids[MAX_USERS][RARCH_CUSTOM_BIND_LIST_END];
|
||||
|
||||
unsigned led_map[MAX_LEDS];
|
||||
|
||||
unsigned run_ahead_frames;
|
||||
} uints;
|
||||
|
||||
struct
|
||||
|
3
core.h
3
core.h
@ -170,6 +170,9 @@ bool core_set_poll_type(unsigned *type);
|
||||
/* Runs the core for one frame. */
|
||||
bool core_run(void);
|
||||
|
||||
/* Runs the core for one frame, but does not trigger any input polling */
|
||||
bool core_run_no_input_polling(void);
|
||||
|
||||
bool core_init(void);
|
||||
|
||||
bool core_deinit(void *data);
|
||||
|
14
core_impl.c
14
core_impl.c
@ -44,6 +44,9 @@
|
||||
#include "gfx/video_driver.h"
|
||||
#include "audio/audio_driver.h"
|
||||
|
||||
#include "runahead/copy_load_info.h"
|
||||
#include "runahead/secondary_core.h"
|
||||
|
||||
struct retro_callbacks retro_ctx;
|
||||
struct retro_core_t current_core;
|
||||
|
||||
@ -262,6 +265,9 @@ bool core_set_controller_port_device(retro_ctx_controller_info_t *pad)
|
||||
{
|
||||
if (!pad)
|
||||
return false;
|
||||
|
||||
remember_controller_port_device(pad->port, pad->device);
|
||||
|
||||
current_core.retro_set_controller_port_device(pad->port, pad->device);
|
||||
return true;
|
||||
}
|
||||
@ -277,6 +283,8 @@ bool core_get_memory(retro_ctx_memory_info_t *info)
|
||||
|
||||
bool core_load_game(retro_ctx_load_content_info_t *load_info)
|
||||
{
|
||||
set_load_content_info(load_info);
|
||||
|
||||
bool contentless = false;
|
||||
bool is_inited = false;
|
||||
|
||||
@ -424,6 +432,12 @@ bool core_run(void)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool core_run_no_input_polling(void)
|
||||
{
|
||||
current_core.retro_run();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool core_load(unsigned poll_type_behavior)
|
||||
{
|
||||
current_core.poll_type = poll_type_behavior;
|
||||
|
35
dynamic.c
35
dynamic.c
@ -66,9 +66,11 @@
|
||||
#include "msg_hash.h"
|
||||
#include "verbosity.h"
|
||||
|
||||
#include "runahead/secondary_core.h"
|
||||
|
||||
#ifdef HAVE_DYNAMIC
|
||||
#define SYMBOL(x) do { \
|
||||
function_t func = dylib_proc(lib_handle, #x); \
|
||||
function_t func = dylib_proc(lib_handle_local, #x); \
|
||||
memcpy(¤t_core->x, &func, sizeof(func)); \
|
||||
if (current_core->x == NULL) { RARCH_ERR("Failed to load symbol: \"%s\"\n", #x); retroarch_fail(1, "init_libretro_sym()"); } \
|
||||
} while (0)
|
||||
@ -383,14 +385,32 @@ bool libretro_get_system_info(const char *path,
|
||||
* Setup libretro callback symbols. Returns true on success,
|
||||
* or false if symbols could not be loaded.
|
||||
**/
|
||||
static bool load_symbols(enum rarch_core_type type, struct retro_core_t *current_core)
|
||||
bool init_libretro_sym_custom(enum rarch_core_type type, struct retro_core_t *current_core, const char *lib_path, dylib_t *lib_handle_p)
|
||||
{
|
||||
/* the library handle for use with the SYMBOL macro */
|
||||
dylib_t lib_handle_local;
|
||||
switch (type)
|
||||
{
|
||||
case CORE_TYPE_PLAIN:
|
||||
#ifdef HAVE_DYNAMIC
|
||||
if (!load_dynamic_core())
|
||||
return false;
|
||||
|
||||
if (lib_path == NULL || lib_handle_p == NULL)
|
||||
{
|
||||
if (!load_dynamic_core())
|
||||
return false;
|
||||
lib_handle_local = lib_handle;
|
||||
}
|
||||
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 == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
*lib_handle_p = lib_handle_local;
|
||||
}
|
||||
#endif
|
||||
|
||||
SYMBOL(retro_init);
|
||||
@ -615,6 +635,11 @@ static bool load_symbols(enum rarch_core_type type, struct retro_core_t *current
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool load_symbols(enum rarch_core_type type, struct retro_core_t *current_core)
|
||||
{
|
||||
return init_libretro_sym_custom(type, current_core, NULL, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* init_libretro_sym:
|
||||
* @type : Type of core to be loaded.
|
||||
@ -634,6 +659,8 @@ bool init_libretro_sym(enum rarch_core_type type, struct retro_core_t *current_c
|
||||
if (!load_symbols(type, current_core))
|
||||
return false;
|
||||
|
||||
/* remember last core type created, so creating a secondary core will know what core type to use */
|
||||
set_last_core_type(type);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <boolean.h>
|
||||
#include <retro_common_api.h>
|
||||
#include <libretro.h>
|
||||
#include <dynamic/dylib.h>
|
||||
|
||||
#include "core_type.h"
|
||||
|
||||
@ -132,6 +133,8 @@ bool libretro_get_shared_context(void);
|
||||
bool init_libretro_sym(enum rarch_core_type type,
|
||||
struct retro_core_t *core);
|
||||
|
||||
bool init_libretro_sym_custom(enum rarch_core_type type, struct retro_core_t *current_core, const char *lib_path, dylib_t *lib_handle_p);
|
||||
|
||||
/**
|
||||
* uninit_libretro_sym:
|
||||
*
|
||||
|
@ -56,6 +56,9 @@ COMPATIBILITY
|
||||
|
||||
#include "../libretro-common/compat/compat_fnmatch.c"
|
||||
#include "../libretro-common/compat/fopen_utf8.c"
|
||||
#if defined(HAVE_DYNAMIC) && HAVE_DYNAMIC
|
||||
#include "../libretro-common/compat/unlink_utf8.c"
|
||||
#endif
|
||||
#include "../libretro-common/memmap/memalign.c"
|
||||
|
||||
/*============================================================
|
||||
@ -1252,6 +1255,13 @@ MENU
|
||||
#include "../libretro-common/net/net_http_parse.c"
|
||||
#endif
|
||||
|
||||
#include "../runahead/mem_util.c"
|
||||
#include "../runahead/secondary_core.c"
|
||||
#include "../runahead/run_ahead.c"
|
||||
#include "../runahead/copy_load_info.c"
|
||||
#include "../runahead/dirty_input.c"
|
||||
#include "../runahead/mylist.c"
|
||||
|
||||
/*============================================================
|
||||
DEPENDENCIES
|
||||
============================================================ */
|
||||
|
@ -989,6 +989,12 @@ MSG_HASH(MENU_ENUM_LABEL_SHUTDOWN,
|
||||
"shutdown")
|
||||
MSG_HASH(MENU_ENUM_LABEL_SLOWMOTION_RATIO,
|
||||
"slowmotion_ratio")
|
||||
MSG_HASH(MENU_ENUM_LABEL_RUN_AHEAD_ENABLED,
|
||||
"run_ahead_enabled")
|
||||
MSG_HASH(MENU_ENUM_LABEL_RUN_AHEAD_SECONDARY_INSTANCE,
|
||||
"run_ahead_secondary_instance")
|
||||
MSG_HASH(MENU_ENUM_LABEL_RUN_AHEAD_FRAMES,
|
||||
"run_ahead_frames")
|
||||
MSG_HASH(MENU_ENUM_LABEL_SORT_SAVEFILES_ENABLE,
|
||||
"sort_savefiles_enable")
|
||||
MSG_HASH(MENU_ENUM_LABEL_SORT_SAVESTATES_ENABLE,
|
||||
|
@ -1445,6 +1445,12 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_SHUTDOWN,
|
||||
"Shutdown")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_SLOWMOTION_RATIO,
|
||||
"Slow-Motion Ratio")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_RUN_AHEAD_ENABLED,
|
||||
"Run-Ahead to Reduce Latency")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_RUN_AHEAD_FRAMES,
|
||||
"Number of Frames to Run Ahead")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_RUN_AHEAD_SECONDARY_INSTANCE,
|
||||
"Runahead Use Second Instance")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_SORT_SAVEFILES_ENABLE,
|
||||
"Sort Saves In Folders")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_SORT_SAVESTATES_ENABLE,
|
||||
@ -2720,6 +2726,18 @@ MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_SLOWMOTION_RATIO,
|
||||
"When in slow motion, content will slow down by the factor specified/set."
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_RUN_AHEAD_ENABLED,
|
||||
"Run core logic one or more frames ahead then load the state back to reduce perceived input lag."
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_RUN_AHEAD_FRAMES,
|
||||
"The number of frames to run ahead. Causes gameplay issues such as jitter if you exceed the number of lag frames internal to the game."
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_RUN_AHEAD_SECONDARY_INSTANCE,
|
||||
"Use a second instance of the RetroArch core to run ahead. Prevents audio problems due to loading state."
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_REWIND_ENABLE,
|
||||
"Enable rewinding. This will take a performance hit when playing."
|
||||
|
30
libretro-common/compat/unlink_utf8.c
Normal file
30
libretro-common/compat/unlink_utf8.c
Normal file
@ -0,0 +1,30 @@
|
||||
#include <compat/unlink_utf8.h>
|
||||
#include <encodings/utf.h>
|
||||
#include "boolean.h"
|
||||
#include <malloc.h>
|
||||
|
||||
#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX)
|
||||
#ifndef LEGACY_WIN32
|
||||
#define LEGACY_WIN32
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#define NOMINMAX
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
bool unlink_utf8(const char * filename)
|
||||
{
|
||||
#if defined(LEGACY_WIN32)
|
||||
bool result = DeleteFileA(filename_w);
|
||||
#else
|
||||
wchar_t * filename_w = utf8_to_utf16_string_alloc(filename);
|
||||
bool result = DeleteFileW(filename_w);
|
||||
free(filename_w);
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
23
libretro-common/include/compat/unlink_utf8.h
Normal file
23
libretro-common/include/compat/unlink_utf8.h
Normal file
@ -0,0 +1,23 @@
|
||||
#ifndef __UNLINK_UTF8_H
|
||||
#define __UNLINK_UTF8_H
|
||||
|
||||
#include "boolean.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#if __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
bool unlink_utf8(const char * filename);
|
||||
|
||||
#if __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#define unlink_utf8 unlink
|
||||
#endif
|
||||
#endif
|
@ -172,6 +172,9 @@ default_sublabel_macro(action_bind_sublabel_savestate_auto_index, MENU_
|
||||
default_sublabel_macro(action_bind_sublabel_block_sram_overwrite, MENU_ENUM_SUBLABEL_BLOCK_SRAM_OVERWRITE)
|
||||
default_sublabel_macro(action_bind_sublabel_fastforward_ratio, MENU_ENUM_SUBLABEL_FASTFORWARD_RATIO)
|
||||
default_sublabel_macro(action_bind_sublabel_slowmotion_ratio, MENU_ENUM_SUBLABEL_SLOWMOTION_RATIO)
|
||||
default_sublabel_macro(action_bind_sublabel_run_ahead_enabled, MENU_ENUM_SUBLABEL_RUN_AHEAD_ENABLED)
|
||||
default_sublabel_macro(action_bind_sublabel_run_ahead_secondary_instance, MENU_ENUM_SUBLABEL_RUN_AHEAD_SECONDARY_INSTANCE)
|
||||
default_sublabel_macro(action_bind_sublabel_run_ahead_frames, MENU_ENUM_SUBLABEL_RUN_AHEAD_FRAMES)
|
||||
default_sublabel_macro(action_bind_sublabel_rewind, MENU_ENUM_SUBLABEL_REWIND_ENABLE)
|
||||
default_sublabel_macro(action_bind_sublabel_rewind_granularity, MENU_ENUM_SUBLABEL_REWIND_GRANULARITY)
|
||||
default_sublabel_macro(action_bind_sublabel_libretro_log_level, MENU_ENUM_SUBLABEL_LIBRETRO_LOG_LEVEL)
|
||||
@ -1109,6 +1112,15 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs,
|
||||
case MENU_ENUM_LABEL_SLOWMOTION_RATIO:
|
||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_slowmotion_ratio);
|
||||
break;
|
||||
case MENU_ENUM_LABEL_RUN_AHEAD_ENABLED:
|
||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_run_ahead_enabled);
|
||||
break;
|
||||
case MENU_ENUM_LABEL_RUN_AHEAD_SECONDARY_INSTANCE:
|
||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_run_ahead_secondary_instance);
|
||||
break;
|
||||
case MENU_ENUM_LABEL_RUN_AHEAD_FRAMES:
|
||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_run_ahead_frames);
|
||||
break;
|
||||
case MENU_ENUM_LABEL_FASTFORWARD_RATIO:
|
||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_fastforward_ratio);
|
||||
break;
|
||||
|
@ -4963,6 +4963,15 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data)
|
||||
menu_displaylist_parse_settings_enum(menu, info,
|
||||
MENU_ENUM_LABEL_SLOWMOTION_RATIO,
|
||||
PARSE_ONLY_FLOAT, false);
|
||||
menu_displaylist_parse_settings_enum(menu, info,
|
||||
MENU_ENUM_LABEL_RUN_AHEAD_ENABLED,
|
||||
PARSE_ONLY_BOOL, false);
|
||||
menu_displaylist_parse_settings_enum(menu, info,
|
||||
MENU_ENUM_LABEL_RUN_AHEAD_FRAMES,
|
||||
PARSE_ONLY_UINT, false);
|
||||
menu_displaylist_parse_settings_enum(menu, info,
|
||||
MENU_ENUM_LABEL_RUN_AHEAD_SECONDARY_INSTANCE,
|
||||
PARSE_ONLY_BOOL, false);
|
||||
if (settings->bools.menu_show_advanced_settings)
|
||||
menu_displaylist_parse_settings_enum(menu, info,
|
||||
MENU_ENUM_LABEL_MENU_THROTTLE_FRAMERATE,
|
||||
|
@ -4834,6 +4834,51 @@ static bool setting_append_list(
|
||||
general_read_handler);
|
||||
menu_settings_list_current_add_range(list, list_info, 1, 10, 0.1, true, true);
|
||||
|
||||
CONFIG_BOOL(
|
||||
list, list_info,
|
||||
&settings->bools.run_ahead_enabled,
|
||||
MENU_ENUM_LABEL_RUN_AHEAD_ENABLED,
|
||||
MENU_ENUM_LABEL_VALUE_RUN_AHEAD_ENABLED,
|
||||
false,
|
||||
MENU_ENUM_LABEL_VALUE_OFF,
|
||||
MENU_ENUM_LABEL_VALUE_ON,
|
||||
&group_info,
|
||||
&subgroup_info,
|
||||
parent_group,
|
||||
general_write_handler,
|
||||
general_read_handler,
|
||||
SD_FLAG_NONE
|
||||
);
|
||||
|
||||
CONFIG_UINT(
|
||||
list, list_info,
|
||||
&settings->uints.run_ahead_frames,
|
||||
MENU_ENUM_LABEL_RUN_AHEAD_FRAMES,
|
||||
MENU_ENUM_LABEL_VALUE_RUN_AHEAD_FRAMES,
|
||||
1,
|
||||
&group_info,
|
||||
&subgroup_info,
|
||||
parent_group,
|
||||
general_write_handler,
|
||||
general_read_handler);
|
||||
menu_settings_list_current_add_range(list, list_info, 1, 6, 1, true, true);
|
||||
|
||||
CONFIG_BOOL(
|
||||
list, list_info,
|
||||
&settings->bools.run_ahead_secondary_instance,
|
||||
MENU_ENUM_LABEL_RUN_AHEAD_SECONDARY_INSTANCE,
|
||||
MENU_ENUM_LABEL_VALUE_RUN_AHEAD_SECONDARY_INSTANCE,
|
||||
false,
|
||||
MENU_ENUM_LABEL_VALUE_OFF,
|
||||
MENU_ENUM_LABEL_VALUE_ON,
|
||||
&group_info,
|
||||
&subgroup_info,
|
||||
parent_group,
|
||||
general_write_handler,
|
||||
general_read_handler,
|
||||
SD_FLAG_NONE
|
||||
);
|
||||
|
||||
CONFIG_BOOL(
|
||||
list, list_info,
|
||||
&settings->bools.menu_throttle_framerate,
|
||||
|
@ -1262,6 +1262,9 @@ enum msg_hash_enums
|
||||
MENU_LABEL(THUMBNAILS_DIRECTORY),
|
||||
|
||||
MENU_LABEL(SLOWMOTION_RATIO),
|
||||
MENU_LABEL(RUN_AHEAD_ENABLED),
|
||||
MENU_LABEL(RUN_AHEAD_SECONDARY_INSTANCE),
|
||||
MENU_LABEL(RUN_AHEAD_FRAMES),
|
||||
MENU_LABEL(TURBO),
|
||||
|
||||
/* Privacy settings */
|
||||
|
12
retroarch.c
12
retroarch.c
@ -118,6 +118,8 @@
|
||||
|
||||
#include "command.h"
|
||||
|
||||
#include "runahead/run_ahead.h"
|
||||
|
||||
#define _PSUPP(var, name, desc) printf(" %s:\n\t\t%s: %s\n", name, desc, _##var##_supp ? "yes" : "no")
|
||||
|
||||
#define FAIL_CPU(simd_type) do { \
|
||||
@ -3248,7 +3250,15 @@ int runloop_iterate(unsigned *sleep_ms)
|
||||
if ((settings->uints.video_frame_delay > 0) && !input_nonblock_state)
|
||||
retro_sleep(settings->uints.video_frame_delay);
|
||||
|
||||
core_run();
|
||||
/* Run Ahead Feature replaces the call to core_run in this loop */
|
||||
if (settings->bools.run_ahead_enabled && settings->uints.run_ahead_frames > 0)
|
||||
{
|
||||
run_ahead(settings->uints.run_ahead_frames, settings->bools.run_ahead_secondary_instance);
|
||||
}
|
||||
else
|
||||
{
|
||||
core_run();
|
||||
}
|
||||
|
||||
#ifdef HAVE_CHEEVOS
|
||||
if (runloop_check_cheevos())
|
||||
|
177
runahead/copy_load_info.c
Normal file
177
runahead/copy_load_info.c
Normal file
@ -0,0 +1,177 @@
|
||||
#include "libretro.h"
|
||||
#include "mem_util.h"
|
||||
#include "core.h"
|
||||
#include <string.h>
|
||||
#include <malloc.h>
|
||||
#include "lists/string_list.h"
|
||||
|
||||
retro_ctx_load_content_info_t *load_content_info;
|
||||
enum rarch_core_type last_core_type;
|
||||
|
||||
static void free_retro_game_info(struct retro_game_info *dest)
|
||||
{
|
||||
if (dest == NULL) return;
|
||||
FREE(dest->path);
|
||||
FREE(dest->data);
|
||||
FREE(dest->meta);
|
||||
}
|
||||
|
||||
static struct retro_game_info* clone_retro_game_info(const struct retro_game_info *src)
|
||||
{
|
||||
struct retro_game_info *dest;
|
||||
if (src == NULL) return NULL;
|
||||
dest = (struct retro_game_info*)malloc_zero(sizeof(struct retro_game_info));
|
||||
dest->path = strcpy_alloc(src->path);
|
||||
dest->data = memcpy_alloc(src->data, src->size);
|
||||
dest->size = src->size;
|
||||
dest->meta = strcpy_alloc(src->meta);
|
||||
return dest;
|
||||
}
|
||||
|
||||
static void free_string_list(struct string_list *dest)
|
||||
{
|
||||
int i;
|
||||
if (dest == NULL) return;
|
||||
for (i = 0; i < dest->size; i++)
|
||||
{
|
||||
FREE(dest->elems[i].data);
|
||||
}
|
||||
FREE(dest->elems);
|
||||
}
|
||||
|
||||
static struct string_list* clone_string_list(const struct string_list *src)
|
||||
{
|
||||
int i;
|
||||
struct string_list *dest;
|
||||
if (src == NULL) return NULL;
|
||||
dest = (struct string_list*)malloc_zero(sizeof(struct string_list));
|
||||
dest->size = src->size;
|
||||
dest->cap = src->cap;
|
||||
dest->elems = (struct string_list_elem*)malloc_zero(sizeof(struct string_list_elem) * dest->size);
|
||||
for (i = 0; i < src->size; i++)
|
||||
{
|
||||
dest->elems[i].data = strcpy_alloc(src->elems[i].data);
|
||||
dest->elems[i].attr = src->elems[i].attr;
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* for cloning the Special field, however, attempting to use this feature crashes retroarch */
|
||||
static void free_retro_subsystem_memory_info(struct retro_subsystem_memory_info *dest)
|
||||
{
|
||||
if (dest == NULL) return;
|
||||
FREE(dest->extension);
|
||||
}
|
||||
|
||||
static void clone_retro_subsystem_memory_info(struct retro_subsystem_memory_info* dest, const struct retro_subsystem_memory_info *src)
|
||||
{
|
||||
dest->extension = strcpy_alloc(src->extension);
|
||||
dest->type = src->type;
|
||||
}
|
||||
|
||||
static void free_retro_subsystem_rom_info(struct retro_subsystem_rom_info *dest)
|
||||
{
|
||||
int i;
|
||||
if (dest == NULL) return;
|
||||
FREE(dest->desc);
|
||||
FREE(dest->valid_extensions);
|
||||
for (i = 0; i < dest->num_memory; i++)
|
||||
{
|
||||
free_retro_subsystem_memory_info((struct retro_subsystem_memory_info*)&dest->memory[i]);
|
||||
}
|
||||
FREE(dest->memory);
|
||||
}
|
||||
|
||||
static void clone_retro_subsystem_rom_info(struct retro_subsystem_rom_info *dest, const struct retro_subsystem_rom_info *src)
|
||||
{
|
||||
int i;
|
||||
retro_subsystem_memory_info *memory;
|
||||
dest->need_fullpath = src->need_fullpath;
|
||||
dest->block_extract = src->block_extract;
|
||||
dest->required = src->required;
|
||||
dest->num_memory = src->num_memory;
|
||||
dest->desc = strcpy_alloc(src->desc);
|
||||
dest->valid_extensions = strcpy_alloc(src->valid_extensions);
|
||||
memory = (struct retro_subsystem_memory_info*)malloc_zero(dest->num_memory * sizeof(struct retro_subsystem_memory_info));
|
||||
dest->memory = memory;
|
||||
for (i = 0; i < dest->num_memory; i++)
|
||||
{
|
||||
clone_retro_subsystem_memory_info(&memory[i], &src->memory[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void free_retro_subsystem_info(struct retro_subsystem_info *dest)
|
||||
{
|
||||
int i;
|
||||
if (dest == NULL) return;
|
||||
FREE(dest->desc);
|
||||
FREE(dest->ident);
|
||||
for (i = 0; i < dest->num_roms; i++)
|
||||
{
|
||||
free_retro_subsystem_rom_info((struct retro_subsystem_rom_info*)&dest->roms[i]);
|
||||
}
|
||||
FREE(dest->roms);
|
||||
}
|
||||
|
||||
static retro_subsystem_info* clone_retro_subsystem_info(struct const retro_subsystem_info *src)
|
||||
{
|
||||
int i;
|
||||
retro_subsystem_info *dest;
|
||||
retro_subsystem_rom_info *roms;
|
||||
if (src == NULL) return NULL;
|
||||
dest = (struct retro_subsystem_info*)malloc_zero(sizeof(struct retro_subsystem_info));
|
||||
dest->desc = strcpy_alloc(src->desc);
|
||||
dest->ident = strcpy_alloc(src->ident);
|
||||
dest->num_roms = src->num_roms;
|
||||
dest->id = src->id;
|
||||
|
||||
roms = (struct retro_subsystem_rom_info*)malloc_zero(src->num_roms * sizeof(struct retro_subsystem_rom_info));
|
||||
dest->roms = roms;
|
||||
for (i = 0; i < src->num_roms; i++)
|
||||
{
|
||||
clone_retro_subsystem_rom_info(&roms[i], &src->roms[i]);
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void free_retro_ctx_load_content_info(struct retro_ctx_load_content_info *dest)
|
||||
{
|
||||
if (dest == NULL) return;
|
||||
free_retro_game_info(dest->info);
|
||||
free_string_list((struct string_list*)dest->content);
|
||||
FREE(dest->info);
|
||||
FREE(dest->content);
|
||||
#if 0
|
||||
free_retro_subsystem_info((retro_subsystem_info*)dest->special);
|
||||
FREE(dest->special);
|
||||
#endif
|
||||
}
|
||||
|
||||
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;
|
||||
if (src == NULL || src->special != NULL) return NULL; /* refuse to deal with the Special field */
|
||||
|
||||
dest = (struct retro_ctx_load_content_info*)malloc_zero(sizeof(struct retro_ctx_load_content_info));
|
||||
dest->info = clone_retro_game_info(src->info);
|
||||
dest->content = clone_string_list(src->content);
|
||||
dest->special = NULL;
|
||||
#if 0
|
||||
dest->special = clone_retro_subsystem_info(src->special);
|
||||
#endif
|
||||
return dest;
|
||||
}
|
||||
|
||||
void set_load_content_info(const retro_ctx_load_content_info_t *ctx)
|
||||
{
|
||||
free_retro_ctx_load_content_info(load_content_info);
|
||||
free(load_content_info);
|
||||
load_content_info = clone_retro_ctx_load_content_info(ctx);
|
||||
}
|
||||
|
||||
void set_last_core_type(enum rarch_core_type type)
|
||||
{
|
||||
last_core_type = type;
|
||||
}
|
16
runahead/copy_load_info.h
Normal file
16
runahead/copy_load_info.h
Normal file
@ -0,0 +1,16 @@
|
||||
#ifndef __COPY_LOAD_INFO_H__
|
||||
#define __COPY_LOAD_INFO_H__
|
||||
|
||||
#include "retro_common_api.h"
|
||||
#include "libretro.h"
|
||||
#include "core.h"
|
||||
#include "boolean.h"
|
||||
|
||||
RETRO_BEGIN_DECLS
|
||||
|
||||
void set_load_content_info(const retro_ctx_load_content_info_t *ctx);
|
||||
void set_last_core_type(enum rarch_core_type type);
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
#endif
|
166
runahead/dirty_input.c
Normal file
166
runahead/dirty_input.c
Normal file
@ -0,0 +1,166 @@
|
||||
#include "core.h"
|
||||
#include "boolean.h"
|
||||
#include "mylist.h"
|
||||
#include "dynamic.h"
|
||||
#include "mem_util.h"
|
||||
|
||||
bool input_is_dirty;
|
||||
static MyList *inputStateList;
|
||||
|
||||
typedef struct InputListElement_t
|
||||
{
|
||||
unsigned port;
|
||||
unsigned device;
|
||||
unsigned index;
|
||||
int16_t state[36];
|
||||
} InputListElement;
|
||||
|
||||
typedef struct retro_core_t _retro_core_t;
|
||||
extern _retro_core_t current_core;
|
||||
extern struct retro_callbacks retro_ctx;
|
||||
|
||||
typedef bool(*LoadStateFunction)(const void*, size_t);
|
||||
|
||||
static function_t retro_reset_callback_original = NULL;
|
||||
static LoadStateFunction retro_unserialize_callback_original = NULL;
|
||||
static retro_input_state_t input_state_callback_original;
|
||||
|
||||
static void reset_hook(void);
|
||||
static bool unserialze_hook(const void *buf, size_t size);
|
||||
|
||||
static void* InputListElementConstructor(void)
|
||||
{
|
||||
void *ptr;
|
||||
const int size = sizeof(InputListElement);
|
||||
ptr = malloc_zero(size);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static void input_state_destory(void)
|
||||
{
|
||||
mylist_destroy(&inputStateList);
|
||||
}
|
||||
|
||||
static void input_state_setlast(unsigned port, unsigned device, unsigned index, unsigned id, int16_t value)
|
||||
{
|
||||
int i;
|
||||
InputListElement *element;
|
||||
|
||||
if (inputStateList == NULL)
|
||||
{
|
||||
mylist_create(&inputStateList, 16, InputListElementConstructor, free);
|
||||
}
|
||||
|
||||
/* find list item */
|
||||
for (i = 0; i < inputStateList->size; i++)
|
||||
{
|
||||
element = (InputListElement*)inputStateList->data[i];
|
||||
if (element->port == port && element->device == device && element->index == index)
|
||||
{
|
||||
element->state[id] = value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
element = (InputListElement*)mylist_add_element(inputStateList);
|
||||
element->port = port;
|
||||
element->device = device;
|
||||
element->index = index;
|
||||
element->state[id] = value;
|
||||
}
|
||||
|
||||
static int16_t input_state_getlast(unsigned port, unsigned device, unsigned index, unsigned id)
|
||||
{
|
||||
int i;
|
||||
InputListElement *element;
|
||||
if (inputStateList == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
/* find list item */
|
||||
for (i = 0; i < inputStateList->size; i++)
|
||||
{
|
||||
element = (InputListElement*)inputStateList->data[i];
|
||||
if (element->port == port && element->device == device && element->index == index)
|
||||
{
|
||||
return element->state[id];
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int16_t input_state_with_logging(unsigned port, unsigned device, unsigned index, unsigned id)
|
||||
{
|
||||
if (input_state_callback_original != NULL)
|
||||
{
|
||||
int16_t result = input_state_callback_original(port, device, index, id);
|
||||
int16_t lastInput = input_state_getlast(port, device, index, id);
|
||||
if (result != lastInput)
|
||||
{
|
||||
input_is_dirty = true;
|
||||
}
|
||||
input_state_setlast(port, device, index, id, result);
|
||||
return result;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void reset_hook(void)
|
||||
{
|
||||
input_is_dirty = true;
|
||||
if (retro_reset_callback_original)
|
||||
{
|
||||
retro_reset_callback_original();
|
||||
}
|
||||
}
|
||||
|
||||
static bool unserialze_hook(const void *buf, size_t size)
|
||||
{
|
||||
input_is_dirty = true;
|
||||
if (retro_unserialize_callback_original)
|
||||
{
|
||||
return retro_unserialize_callback_original(buf, size);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void add_input_state_hook(void)
|
||||
{
|
||||
if (input_state_callback_original == NULL)
|
||||
{
|
||||
input_state_callback_original = retro_ctx.state_cb;
|
||||
retro_ctx.state_cb = input_state_with_logging;
|
||||
current_core.retro_set_input_state(retro_ctx.state_cb);
|
||||
}
|
||||
if (retro_reset_callback_original == NULL)
|
||||
{
|
||||
retro_reset_callback_original = current_core.retro_reset;
|
||||
current_core.retro_reset = reset_hook;
|
||||
}
|
||||
if (retro_unserialize_callback_original == NULL)
|
||||
{
|
||||
retro_unserialize_callback_original = current_core.retro_unserialize;
|
||||
current_core.retro_unserialize = unserialze_hook;
|
||||
}
|
||||
}
|
||||
|
||||
void remove_input_state_hook(void)
|
||||
{
|
||||
if (input_state_callback_original != NULL)
|
||||
{
|
||||
retro_ctx.state_cb = input_state_callback_original;
|
||||
current_core.retro_set_input_state(retro_ctx.state_cb);
|
||||
input_state_callback_original = NULL;
|
||||
input_state_destory();
|
||||
}
|
||||
if (retro_reset_callback_original != NULL)
|
||||
{
|
||||
current_core.retro_reset = retro_reset_callback_original;
|
||||
retro_reset_callback_original = NULL;
|
||||
}
|
||||
if (retro_unserialize_callback_original != NULL)
|
||||
{
|
||||
current_core.retro_unserialize = retro_unserialize_callback_original;
|
||||
retro_unserialize_callback_original = NULL;
|
||||
}
|
||||
}
|
||||
|
15
runahead/dirty_input.h
Normal file
15
runahead/dirty_input.h
Normal file
@ -0,0 +1,15 @@
|
||||
#ifndef __DIRTY_INPUT_H___
|
||||
#define __DIRTY_INPUT_H___
|
||||
|
||||
#include "retro_common_api.h"
|
||||
#include "boolean.h"
|
||||
|
||||
RETRO_BEGIN_DECLS
|
||||
|
||||
extern bool input_is_dirty;
|
||||
void add_input_state_hook(void);
|
||||
void remove_input_state_hook(void);
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
#endif
|
95
runahead/mem_util.c
Normal file
95
runahead/mem_util.c
Normal file
@ -0,0 +1,95 @@
|
||||
#include "mem_util.h"
|
||||
|
||||
void *malloc_zero(size_t size)
|
||||
{
|
||||
void *ptr;
|
||||
ptr = malloc(size);
|
||||
memset(ptr, 0, size);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void free_str(char **str_p)
|
||||
{
|
||||
free_ptr((void**)str_p);
|
||||
}
|
||||
|
||||
void free_ptr(void **data_p)
|
||||
{
|
||||
if (data_p == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (*data_p == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
free(*data_p);
|
||||
*data_p = NULL;
|
||||
}
|
||||
|
||||
void *memcpy_alloc(const void *src, size_t size)
|
||||
{
|
||||
void *result;
|
||||
result = malloc(size);
|
||||
memcpy(result, src, size);
|
||||
return result;
|
||||
}
|
||||
|
||||
char *strcpy_alloc(const char *sourceStr)
|
||||
{
|
||||
size_t len;
|
||||
char *result;
|
||||
if (sourceStr == NULL)
|
||||
{
|
||||
len = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
len = strlen(sourceStr);
|
||||
}
|
||||
if (len == 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
result = (char*)malloc(len + 1);
|
||||
strcpy(result, sourceStr);
|
||||
return result;
|
||||
}
|
||||
|
||||
char *strcpy_alloc_force(const char *sourceStr)
|
||||
{
|
||||
char *result;
|
||||
result = strcpy_alloc(sourceStr);
|
||||
if (result == NULL)
|
||||
{
|
||||
result = (char*)malloc_zero(1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void strcat_alloc(char ** destStr_p, const char *appendStr)
|
||||
{
|
||||
size_t len1, len2, newLen;
|
||||
char *destStr;
|
||||
|
||||
destStr = *destStr_p;
|
||||
|
||||
if (destStr == NULL)
|
||||
{
|
||||
destStr = strcpy_alloc_force(appendStr);
|
||||
*destStr_p = destStr;
|
||||
return;
|
||||
}
|
||||
|
||||
if (appendStr == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
len1 = strlen(destStr);
|
||||
len2 = strlen(appendStr);
|
||||
newLen = len1 + len2 + 1;
|
||||
destStr = (char*)realloc(destStr, newLen);
|
||||
*destStr_p = destStr;
|
||||
strcpy(destStr + len1, appendStr);
|
||||
}
|
26
runahead/mem_util.h
Normal file
26
runahead/mem_util.h
Normal file
@ -0,0 +1,26 @@
|
||||
#ifndef __MEM_UTIL__
|
||||
#define __MEM_UTIL__
|
||||
|
||||
#include "retro_common_api.h"
|
||||
#include "boolean.h"
|
||||
|
||||
#include <malloc.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#define FREE(xxxx) if ((xxxx) != NULL) { free((void*)(xxxx)); } (xxxx) = NULL
|
||||
|
||||
RETRO_BEGIN_DECLS
|
||||
|
||||
void *malloc_zero(size_t size);
|
||||
void free_str(char **str_p);
|
||||
void free_ptr(void **data_p);
|
||||
char *strcpy_alloc(const char *sourceStr);
|
||||
char *strcpy_alloc_force(const char *sourceStr);
|
||||
void strcat_alloc(char ** destStr_p, const char *appendStr);
|
||||
void *memcpy_alloc(const void *src, size_t size);
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
#endif
|
||||
|
151
runahead/mylist.c
Normal file
151
runahead/mylist.c
Normal file
@ -0,0 +1,151 @@
|
||||
#include "mylist.h"
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
#include "mem_util.h"
|
||||
|
||||
void mylist_resize(MyList *list, int newSize, bool runConstructor)
|
||||
{
|
||||
int newCapacity;
|
||||
int oldSize;
|
||||
int i;
|
||||
void *element;
|
||||
if (newSize < 0) newSize = 0;
|
||||
if (list == NULL) return;
|
||||
newCapacity = newSize;
|
||||
oldSize = list->size;
|
||||
if (newSize == oldSize) return;
|
||||
if (newSize > list->capacity)
|
||||
{
|
||||
if (newCapacity < list->capacity * 2)
|
||||
{
|
||||
newCapacity = list->capacity * 2;
|
||||
}
|
||||
/* try to realloc */
|
||||
list->data = (void**)realloc((void*)list->data, newCapacity * sizeof(void*));
|
||||
for (i = list->capacity; i < newCapacity; i++)
|
||||
{
|
||||
list->data[i] = NULL;
|
||||
}
|
||||
list->capacity = newCapacity;
|
||||
}
|
||||
if (newSize <= list->size)
|
||||
{
|
||||
for (i = newSize; i < list->size; i++)
|
||||
{
|
||||
element = list->data[i];
|
||||
if (element != NULL)
|
||||
{
|
||||
list->Destructor(element);
|
||||
list->data[i] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = list->size; i < newSize; i++)
|
||||
{
|
||||
if (runConstructor)
|
||||
{
|
||||
list->data[i] = list->Constructor();
|
||||
}
|
||||
else
|
||||
{
|
||||
list->data[i] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
list->size = newSize;
|
||||
}
|
||||
|
||||
void *mylist_add_element(MyList *list)
|
||||
{
|
||||
int oldSize;
|
||||
if (list == NULL) return NULL;
|
||||
oldSize = list->size;
|
||||
mylist_resize(list, oldSize + 1, true);
|
||||
return list->data[oldSize];
|
||||
}
|
||||
|
||||
void mylist_create(MyList **list_p, int initialCapacity, constructor_t constructor, destructor_t destructor)
|
||||
{
|
||||
MyList *list;
|
||||
if (list_p == NULL) return;
|
||||
if (initialCapacity < 0) initialCapacity = 0;
|
||||
list = *list_p;
|
||||
if (list != NULL)
|
||||
{
|
||||
mylist_destroy(list_p);
|
||||
}
|
||||
list = (MyList*)malloc(sizeof(MyList));
|
||||
*list_p = list;
|
||||
list->size = 0;
|
||||
list->Constructor = constructor;
|
||||
list->Destructor = destructor;
|
||||
if (initialCapacity > 0)
|
||||
{
|
||||
list->data = (void**)malloc_zero(initialCapacity * sizeof(void*));
|
||||
list->capacity = initialCapacity;
|
||||
}
|
||||
else
|
||||
{
|
||||
list->data = NULL;
|
||||
list->capacity = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void mylist_destroy(MyList **list_p)
|
||||
{
|
||||
if (list_p == NULL) return;
|
||||
MyList *list;
|
||||
list = *list_p;
|
||||
if (list != NULL)
|
||||
{
|
||||
mylist_resize(list, 0, false);
|
||||
free(list->data);
|
||||
free(list);
|
||||
*list_p = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void mylist_assign(MyList *list, int index, void *value)
|
||||
{
|
||||
void *oldElement;
|
||||
if (index < 0 || index >= list->size)
|
||||
{
|
||||
return;
|
||||
}
|
||||
oldElement = list->data[index];
|
||||
list->Destructor(oldElement);
|
||||
list->data[index] = value;
|
||||
}
|
||||
|
||||
void mylist_remove_at(MyList *list, int index)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (index < 0 || index >= list->size)
|
||||
{
|
||||
return;
|
||||
}
|
||||
mylist_assign(list, index, NULL);
|
||||
for (i = index + 1; i < list->size; i++)
|
||||
{
|
||||
list->data[i - 1] = list->data[i];
|
||||
}
|
||||
list->size--;
|
||||
list->data[list->size] = NULL;
|
||||
}
|
||||
|
||||
void mylist_pop_front(MyList *list)
|
||||
{
|
||||
mylist_remove_at(list, 0);
|
||||
}
|
||||
|
||||
void mylist_push_back(MyList *list, void *value)
|
||||
{
|
||||
int oldSize;
|
||||
if (list == NULL) return;
|
||||
oldSize = list->size;
|
||||
mylist_resize(list, oldSize + 1, false);
|
||||
list->data[oldSize] = value;
|
||||
}
|
32
runahead/mylist.h
Normal file
32
runahead/mylist.h
Normal file
@ -0,0 +1,32 @@
|
||||
#ifndef __MYLIST_H__
|
||||
#define __MYLIST_H__
|
||||
|
||||
#include "retro_common_api.h"
|
||||
#include "boolean.h"
|
||||
|
||||
RETRO_BEGIN_DECLS
|
||||
|
||||
typedef void* (*constructor_t)(void);
|
||||
typedef void(*destructor_t)(void*);
|
||||
|
||||
typedef struct MyList_t
|
||||
{
|
||||
void **data;
|
||||
int capacity;
|
||||
int size;
|
||||
constructor_t Constructor;
|
||||
destructor_t Destructor;
|
||||
} MyList;
|
||||
|
||||
void *mylist_add_element(MyList *list);
|
||||
void mylist_resize(MyList *list, int newSize, bool runConstructor);
|
||||
void mylist_create(MyList **list_p, int initialCapacity, constructor_t constructor, destructor_t destructor);
|
||||
void mylist_destroy(MyList **list_p);
|
||||
void mylist_assign(MyList *list, int index, void *value);
|
||||
void mylist_remove_at(MyList *list, int index);
|
||||
void mylist_pop_front(MyList *list);
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
#endif
|
||||
|
425
runahead/run_ahead.c
Normal file
425
runahead/run_ahead.c
Normal file
@ -0,0 +1,425 @@
|
||||
#include "core.h"
|
||||
#include "dynamic.h"
|
||||
#include "audio/audio_driver.h"
|
||||
#include "gfx/video_driver.h"
|
||||
#include "boolean.h"
|
||||
#include <stddef.h>
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
#include "dirty_input.h"
|
||||
#include "mylist.h"
|
||||
#include "secondary_core.h"
|
||||
|
||||
static void *runahead_save_state_alloc(void);
|
||||
static void runahead_save_state_free(void *state);
|
||||
static void runahead_save_state_list_init(size_t saveStateSize);
|
||||
static void runahead_save_state_list_destroy(void);
|
||||
static void runahead_save_state_list_rotate(void);
|
||||
|
||||
static void add_hooks(void);
|
||||
static void remove_hooks(void);
|
||||
static void deinit_hook(void);
|
||||
static void unload_hook(void);
|
||||
|
||||
static void runahead_clear_variables(void);
|
||||
static void runahead_check_for_gui(void);
|
||||
static void runahead_error(void);
|
||||
static bool runahead_create(void);
|
||||
static bool runahead_save_state(void);
|
||||
static bool runahead_load_state(void);
|
||||
static bool runahead_load_state_secondary(void);
|
||||
static bool runahead_run_secondary(void);
|
||||
static void runahead_suspend_audio(void);
|
||||
static void runahead_resume_audio(void);
|
||||
static void runahead_suspend_video(void);
|
||||
static void runahead_resume_video(void);
|
||||
void run_ahead(int runAheadCount, bool useSecondary);
|
||||
void runahead_destroy(void);
|
||||
|
||||
static void runahead_destroy(void);
|
||||
static bool runahead_create(void);
|
||||
|
||||
static size_t runahead_save_state_size = -1;
|
||||
|
||||
/* Save State List for Run Ahead */
|
||||
static MyList *runahead_save_state_list;
|
||||
|
||||
static void *runahead_save_state_alloc(void)
|
||||
{
|
||||
retro_ctx_serialize_info_t *savestate;
|
||||
savestate = (retro_ctx_serialize_info_t*)malloc(sizeof(retro_ctx_serialize_info_t));
|
||||
savestate->size = runahead_save_state_size;
|
||||
if (runahead_save_state_size > 0 && runahead_save_state_size != -1)
|
||||
{
|
||||
savestate->data = malloc(runahead_save_state_size);
|
||||
savestate->data_const = savestate->data;
|
||||
}
|
||||
else
|
||||
{
|
||||
savestate->data = NULL;
|
||||
savestate->data_const = NULL;
|
||||
savestate->size = 0;
|
||||
}
|
||||
return savestate;
|
||||
}
|
||||
|
||||
static void runahead_save_state_free(void *state)
|
||||
{
|
||||
retro_ctx_serialize_info_t *savestate;
|
||||
savestate = (retro_ctx_serialize_info_t*)state;
|
||||
if (savestate == NULL) return;
|
||||
free(savestate->data);
|
||||
free(savestate);
|
||||
}
|
||||
|
||||
static void runahead_save_state_list_init(size_t saveStateSize)
|
||||
{
|
||||
runahead_save_state_size = saveStateSize;
|
||||
mylist_create(&runahead_save_state_list, 16, runahead_save_state_alloc, runahead_save_state_free);
|
||||
}
|
||||
|
||||
static void runahead_save_state_list_destroy(void)
|
||||
{
|
||||
mylist_destroy(&runahead_save_state_list);
|
||||
}
|
||||
|
||||
static void runahead_save_state_list_rotate(void)
|
||||
{
|
||||
int i;
|
||||
void *element;
|
||||
void *firstElement;
|
||||
firstElement = runahead_save_state_list->data[0];
|
||||
for (i = 1; i < runahead_save_state_list->size; i++)
|
||||
{
|
||||
runahead_save_state_list->data[i - 1] = runahead_save_state_list->data[i - 1];
|
||||
}
|
||||
runahead_save_state_list->data[runahead_save_state_list->size - 1] = firstElement;
|
||||
}
|
||||
|
||||
/* Hooks - Hooks to cleanup, and add dirty input hooks */
|
||||
|
||||
static function_t originalRetroDeinit = NULL;
|
||||
static function_t originalRetroUnload = NULL;
|
||||
|
||||
typedef struct retro_core_t _retro_core_t;
|
||||
extern _retro_core_t current_core;
|
||||
extern struct retro_callbacks retro_ctx;
|
||||
|
||||
static void deinit_hook(void);
|
||||
static void unload_hook(void);
|
||||
|
||||
static void add_hooks(void)
|
||||
{
|
||||
if (originalRetroDeinit == NULL)
|
||||
{
|
||||
originalRetroDeinit = current_core.retro_deinit;
|
||||
current_core.retro_deinit = deinit_hook;
|
||||
}
|
||||
if (originalRetroUnload == NULL)
|
||||
{
|
||||
originalRetroUnload = current_core.retro_unload_game;
|
||||
current_core.retro_unload_game = unload_hook;
|
||||
}
|
||||
add_input_state_hook();
|
||||
}
|
||||
|
||||
static void remove_hooks(void)
|
||||
{
|
||||
if (originalRetroDeinit != NULL)
|
||||
{
|
||||
current_core.retro_deinit = originalRetroDeinit;
|
||||
originalRetroDeinit = NULL;
|
||||
}
|
||||
if (originalRetroUnload != NULL)
|
||||
{
|
||||
current_core.retro_unload_game = originalRetroUnload;
|
||||
originalRetroUnload = NULL;
|
||||
}
|
||||
remove_input_state_hook();
|
||||
}
|
||||
|
||||
static void deinit_hook(void)
|
||||
{
|
||||
remove_hooks();
|
||||
runahead_destroy();
|
||||
secondary_core_destroy();
|
||||
if (current_core.retro_deinit)
|
||||
{
|
||||
current_core.retro_deinit();
|
||||
}
|
||||
}
|
||||
|
||||
static void unload_hook(void)
|
||||
{
|
||||
remove_hooks();
|
||||
runahead_destroy();
|
||||
secondary_core_destroy();
|
||||
if (current_core.retro_unload_game)
|
||||
{
|
||||
current_core.retro_unload_game();
|
||||
}
|
||||
}
|
||||
|
||||
/* Runahead Code */
|
||||
|
||||
static bool runahead_video_driver_is_active = true;
|
||||
static bool runahead_available = true;
|
||||
static bool runahead_secondary_core_available = true;
|
||||
static bool runahead_force_input_dirty = true;
|
||||
static uint64_t runahead_last_frame_count = 0;
|
||||
|
||||
static void runahead_clear_variables(void)
|
||||
{
|
||||
runahead_save_state_size = -1;
|
||||
runahead_video_driver_is_active = true;
|
||||
runahead_available = true;
|
||||
runahead_secondary_core_available = true;
|
||||
runahead_force_input_dirty = true;
|
||||
runahead_last_frame_count = 0;
|
||||
}
|
||||
|
||||
static void runahead_check_for_gui(void)
|
||||
{
|
||||
/* Hack: If we were in the GUI, force a resync. */
|
||||
bool is_dirty;
|
||||
bool is_alive, is_focused;
|
||||
uint64_t frame_count;
|
||||
video_driver_get_status(&frame_count, &is_alive, &is_focused);
|
||||
if (frame_count != runahead_last_frame_count + 1)
|
||||
{
|
||||
runahead_force_input_dirty = true;
|
||||
}
|
||||
runahead_last_frame_count = frame_count;
|
||||
}
|
||||
|
||||
void run_ahead(int runAheadCount, bool useSecondary)
|
||||
{
|
||||
int frameNumber;
|
||||
bool okay;
|
||||
bool lastFrame;
|
||||
bool suspendedFrame;
|
||||
|
||||
if (runAheadCount <= 0 || !runahead_available)
|
||||
{
|
||||
core_run();
|
||||
runahead_force_input_dirty = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (runahead_save_state_size == -1)
|
||||
{
|
||||
if (!runahead_create())
|
||||
{
|
||||
/*runloop_msg_queue_push("RunAhead has been disabled because the core does not support savestates", 1, 180, true);*/
|
||||
core_run();
|
||||
runahead_force_input_dirty = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
runahead_check_for_gui();
|
||||
|
||||
if (!useSecondary || !HAVE_DYNAMIC || !runahead_secondary_core_available)
|
||||
{
|
||||
/* TODO: multiple savestates for higher performance when not using secondary core */
|
||||
for (frameNumber = 0; frameNumber <= runAheadCount; frameNumber++)
|
||||
{
|
||||
lastFrame = frameNumber == runAheadCount;
|
||||
suspendedFrame = !lastFrame;
|
||||
if (suspendedFrame)
|
||||
{
|
||||
runahead_suspend_audio();
|
||||
runahead_suspend_video();
|
||||
}
|
||||
if (frameNumber == 0)
|
||||
{
|
||||
core_run();
|
||||
}
|
||||
else
|
||||
{
|
||||
core_run_no_input_polling();
|
||||
}
|
||||
if (suspendedFrame)
|
||||
{
|
||||
runahead_resume_video();
|
||||
runahead_resume_audio();
|
||||
}
|
||||
if (frameNumber == 0)
|
||||
{
|
||||
if (!runahead_save_state())
|
||||
{
|
||||
/*runloop_msg_queue_push("RunAhead has been disabled due to save state failure", 1, 180, true);*/
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (lastFrame)
|
||||
{
|
||||
if (!runahead_load_state())
|
||||
{
|
||||
/*runloop_msg_queue_push("RunAhead has been disabled due to load state failure", 1, 180, true);*/
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#if HAVE_DYNAMIC
|
||||
/* run main core with video suspended */
|
||||
runahead_suspend_video();
|
||||
core_run();
|
||||
runahead_resume_video();
|
||||
|
||||
if (input_is_dirty || runahead_force_input_dirty)
|
||||
{
|
||||
input_is_dirty = false;
|
||||
if (!runahead_save_state())
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!runahead_load_state_secondary())
|
||||
{
|
||||
/*runloop_msg_queue_push("Could not create a secondary core. RunAhead will only use the main core now.", 1, 180, true);*/
|
||||
return;
|
||||
}
|
||||
for (int frameCount = 0; frameCount < runAheadCount - 1; frameCount++)
|
||||
{
|
||||
runahead_suspend_video();
|
||||
runahead_suspend_audio();
|
||||
okay = runahead_run_secondary();
|
||||
runahead_resume_audio();
|
||||
runahead_resume_video();
|
||||
if (!okay)
|
||||
{
|
||||
/*runloop_msg_queue_push("Could not create a secondary core. RunAhead will only use the main core now.", 1, 180, true);*/
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
runahead_suspend_audio();
|
||||
okay = runahead_run_secondary();
|
||||
runahead_resume_audio();
|
||||
if (!okay)
|
||||
{
|
||||
/*runloop_msg_queue_push("Could not create a secondary core. RunAhead will only use the main core now.", 1, 180, true);*/
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
runahead_force_input_dirty = false;
|
||||
}
|
||||
|
||||
static void runahead_error(void)
|
||||
{
|
||||
runahead_available = false;
|
||||
runahead_save_state_list_destroy();
|
||||
remove_hooks();
|
||||
runahead_save_state_size = 0;
|
||||
}
|
||||
|
||||
static bool runahead_create(void)
|
||||
{
|
||||
/* get savestate size and allocate buffer */
|
||||
retro_ctx_size_info_t info;
|
||||
core_serialize_size(&info);
|
||||
|
||||
runahead_save_state_list_init(info.size);
|
||||
runahead_video_driver_is_active = video_driver_is_active();
|
||||
|
||||
if (runahead_save_state_size == 0 || runahead_save_state_size == -1)
|
||||
{
|
||||
runahead_error();
|
||||
return false;
|
||||
}
|
||||
add_hooks();
|
||||
runahead_force_input_dirty = true;
|
||||
mylist_resize(runahead_save_state_list, 1, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool runahead_save_state(void)
|
||||
{
|
||||
bool okay;
|
||||
retro_ctx_serialize_info_t *serialize_info;
|
||||
serialize_info = (retro_ctx_serialize_info_t*)runahead_save_state_list->data[0];
|
||||
okay = core_serialize(serialize_info);
|
||||
if (!okay)
|
||||
{
|
||||
runahead_error();
|
||||
}
|
||||
return okay;
|
||||
}
|
||||
|
||||
static bool runahead_load_state(void)
|
||||
{
|
||||
bool okay;
|
||||
bool lastDirty;
|
||||
retro_ctx_serialize_info_t *serialize_info;
|
||||
serialize_info = (retro_ctx_serialize_info_t*)runahead_save_state_list->data[0];
|
||||
lastDirty = input_is_dirty;
|
||||
okay = core_unserialize(serialize_info);
|
||||
input_is_dirty = lastDirty;
|
||||
if (!okay)
|
||||
{
|
||||
runahead_error();
|
||||
}
|
||||
return okay;
|
||||
}
|
||||
|
||||
static bool runahead_load_state_secondary(void)
|
||||
{
|
||||
bool okay;
|
||||
retro_ctx_serialize_info_t *serialize_info;
|
||||
serialize_info = (retro_ctx_serialize_info_t*)runahead_save_state_list->data[0];
|
||||
okay = secondary_core_deserialize(serialize_info->data_const, serialize_info->size);
|
||||
if (!okay)
|
||||
{
|
||||
runahead_secondary_core_available = false;
|
||||
}
|
||||
return okay;
|
||||
}
|
||||
|
||||
static bool runahead_run_secondary(void)
|
||||
{
|
||||
bool okay;
|
||||
okay = secondary_core_run_no_input_polling();
|
||||
if (!okay)
|
||||
{
|
||||
runahead_secondary_core_available = false;
|
||||
}
|
||||
return okay;
|
||||
}
|
||||
|
||||
static void runahead_suspend_audio(void)
|
||||
{
|
||||
audio_driver_suspend();
|
||||
}
|
||||
|
||||
static void runahead_resume_audio(void)
|
||||
{
|
||||
audio_driver_resume();
|
||||
}
|
||||
|
||||
static void runahead_suspend_video(void)
|
||||
{
|
||||
video_driver_unset_active();
|
||||
}
|
||||
|
||||
static void runahead_resume_video(void)
|
||||
{
|
||||
if (runahead_video_driver_is_active)
|
||||
{
|
||||
video_driver_set_active();
|
||||
}
|
||||
else
|
||||
{
|
||||
video_driver_unset_active();
|
||||
}
|
||||
}
|
||||
|
||||
void runahead_destroy(void)
|
||||
{
|
||||
runahead_save_state_list_destroy();
|
||||
remove_hooks();
|
||||
runahead_clear_variables();
|
||||
}
|
487
runahead/secondary_core.c
Normal file
487
runahead/secondary_core.c
Normal file
@ -0,0 +1,487 @@
|
||||
#if defined(HAVE_DYNAMIC) && HAVE_DYNAMIC
|
||||
|
||||
#include <string.h>
|
||||
#include <malloc.h>
|
||||
#include <time.h>
|
||||
|
||||
#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX)
|
||||
#ifndef LEGACY_WIN32
|
||||
#define LEGACY_WIN32
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "mem_util.h"
|
||||
#include "boolean.h"
|
||||
#include "encodings/utf.h"
|
||||
#include "compat/fopen_utf8.h"
|
||||
#include "compat/unlink_utf8.h"
|
||||
#include "dynamic/dylib.h"
|
||||
#include "dynamic.h"
|
||||
#include "core.h"
|
||||
#include "file/file_path.h"
|
||||
#include "paths.h"
|
||||
#include "content.h"
|
||||
|
||||
static int port_map[16];
|
||||
|
||||
typedef struct retro_core_t _retro_core_t;
|
||||
typedef struct retro_callbacks retro_callbacks_t;
|
||||
|
||||
static char *secondary_library_path;
|
||||
static dylib_t secondary_module;
|
||||
static _retro_core_t secondary_core;
|
||||
static struct retro_callbacks secondary_callbacks;
|
||||
|
||||
extern retro_ctx_load_content_info_t *load_content_info;
|
||||
extern enum rarch_core_type last_core_type;
|
||||
extern struct retro_callbacks retro_ctx;
|
||||
|
||||
static char* get_temp_directory_alloc(void);
|
||||
static char* copy_core_to_temp_file(void);
|
||||
static void* read_file_data_alloc(const char *fileName, int *size);
|
||||
static bool write_file_data(const char *fileName, const void *data, int dataSize);
|
||||
static bool write_file_with_random_name(char **tempDllPath, const char *retroarchTempPath, const void* data, int dataSize);
|
||||
static void* InputListElementConstructor(void);
|
||||
static void secondary_core_clear(void);
|
||||
static bool secondary_core_create(void);
|
||||
bool secondary_core_run_no_input_polling(void);
|
||||
bool secondary_core_deserialize(const void *buffer, int size);
|
||||
void secondary_core_destroy(void);
|
||||
void set_last_core_type(enum rarch_core_type type);
|
||||
void remember_controller_port_device(long port, long device);
|
||||
void clear_controller_port_map(void);
|
||||
|
||||
static void free_file(FILE **file_p);
|
||||
|
||||
char* get_temp_directory_alloc(void)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
#ifdef LEGACY_WIN32
|
||||
DWORD pathLength;
|
||||
char *path;
|
||||
|
||||
pathLength = GetTempPath(0, NULL) + 1;
|
||||
path = (char*)malloc(pathLength * sizeof(char));
|
||||
path[pathLength - 1] = 0;
|
||||
GetTempPath(pathLength, path);
|
||||
return path;
|
||||
#else
|
||||
DWORD pathLength;
|
||||
wchar_t *wideStr;
|
||||
char *path;
|
||||
|
||||
pathLength = GetTempPathW(0, NULL) + 1;
|
||||
wideStr = (wchar_t*)malloc(pathLength * sizeof(wchar_t));
|
||||
wideStr[pathLength - 1] = 0;
|
||||
GetTempPathW(pathLength, wideStr);
|
||||
|
||||
path = utf16_to_utf8_string_alloc(wideStr);
|
||||
free(wideStr);
|
||||
return path;
|
||||
#endif
|
||||
#else
|
||||
char *path;
|
||||
path = strcpy_alloc_force(getenv("TMPDIR");
|
||||
return path;
|
||||
#endif
|
||||
}
|
||||
|
||||
char* copy_core_to_temp_file(void)
|
||||
{
|
||||
bool okay;
|
||||
const char *corePath = NULL; /* ptr to static buffer, do not need to free this */
|
||||
const char *coreBaseName = NULL; /* ptr to static buffer, do not need to free this */
|
||||
char *tempDirectory = NULL;
|
||||
char *retroarchTempPath = NULL;
|
||||
char *tempDllPath = NULL;
|
||||
void *dllFileData = NULL;
|
||||
int dllFileSize = 0;
|
||||
|
||||
corePath = path_get(RARCH_PATH_CORE);
|
||||
coreBaseName = path_basename(corePath);
|
||||
|
||||
if (strlen(coreBaseName) == 0)
|
||||
{
|
||||
goto failed;
|
||||
}
|
||||
|
||||
tempDirectory = get_temp_directory_alloc();
|
||||
if (tempDirectory == NULL)
|
||||
{
|
||||
goto failed;
|
||||
}
|
||||
|
||||
strcat_alloc(&retroarchTempPath, tempDirectory);
|
||||
strcat_alloc(&retroarchTempPath, path_default_slash());
|
||||
strcat_alloc(&retroarchTempPath, "retroarch_temp");
|
||||
strcat_alloc(&retroarchTempPath, path_default_slash());
|
||||
|
||||
okay = path_mkdir(retroarchTempPath);
|
||||
if (!okay)
|
||||
{
|
||||
goto failed;
|
||||
}
|
||||
|
||||
dllFileData = read_file_data_alloc(corePath, &dllFileSize);
|
||||
if (dllFileData == NULL)
|
||||
{
|
||||
goto failed;
|
||||
}
|
||||
strcat_alloc(&tempDllPath, retroarchTempPath);
|
||||
strcat_alloc(&tempDllPath, coreBaseName);
|
||||
okay = write_file_data(tempDllPath, dllFileData, dllFileSize);
|
||||
if (!okay)
|
||||
{
|
||||
/* try other file names */
|
||||
okay = write_file_with_random_name(&tempDllPath, retroarchTempPath, dllFileData, dllFileSize);
|
||||
if (!okay)
|
||||
{
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
success:
|
||||
free_str(&tempDirectory);
|
||||
free_str(&retroarchTempPath);
|
||||
free_ptr(&dllFileData);
|
||||
return tempDllPath;
|
||||
|
||||
failed:
|
||||
free_str(&tempDirectory);
|
||||
free_str(&retroarchTempPath);
|
||||
free_str(&tempDllPath);
|
||||
free_ptr(&dllFileData);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void* read_file_data_alloc(const char *fileName, int *size)
|
||||
{
|
||||
void *data = NULL;
|
||||
FILE *f = NULL;
|
||||
int fileSize = 0;
|
||||
size_t bytesRead = 0;
|
||||
#ifdef _WIN32
|
||||
int64_t fileSizeLong = 0;
|
||||
#else
|
||||
off64_t fileSizeLong = 0;
|
||||
#endif
|
||||
f = (FILE*)fopen_utf8(fileName, "rb");
|
||||
if (f == NULL)
|
||||
{
|
||||
goto failed;
|
||||
}
|
||||
fseek(f, 0, SEEK_END);
|
||||
#ifdef _WIN32
|
||||
fileSizeLong = _ftelli64(f);
|
||||
#else
|
||||
fileSizeLong = ftello64(f);
|
||||
#endif
|
||||
fseek(f, 0, SEEK_SET);
|
||||
/* 256MB file size limit for DLL files */
|
||||
if (fileSizeLong < 0 || fileSizeLong > 256 * 1024 * 1024)
|
||||
{
|
||||
goto failed;
|
||||
}
|
||||
fileSize = (int)fileSizeLong;
|
||||
data = malloc(fileSize);
|
||||
if (data == NULL)
|
||||
{
|
||||
goto failed;
|
||||
}
|
||||
bytesRead = fread(data, 1, fileSize, f);
|
||||
if ((int)bytesRead != (int)fileSize)
|
||||
{
|
||||
goto failed;
|
||||
}
|
||||
success:
|
||||
free_file(&f);
|
||||
if (size != NULL) *size = fileSize;
|
||||
return data;
|
||||
failed:
|
||||
free_ptr(&data);
|
||||
free_file(&f);
|
||||
if (size != NULL) *size = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool write_file_data(const char *fileName, const void *data, int dataSize)
|
||||
{
|
||||
bool okay = false;
|
||||
FILE *f = NULL;
|
||||
size_t bytesWritten = 0;
|
||||
|
||||
f = (FILE*)fopen_utf8(fileName, "wb");
|
||||
if (f == NULL) goto failed;
|
||||
bytesWritten = fwrite(data, 1, dataSize, f);
|
||||
if (bytesWritten != dataSize)
|
||||
{
|
||||
goto failed;
|
||||
}
|
||||
success:
|
||||
free_file(&f);
|
||||
return true;
|
||||
failed:
|
||||
free_file(&f);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool write_file_with_random_name(char **tempDllPath, const char *retroarchTempPath, const void* data, int dataSize)
|
||||
{
|
||||
int extLen;
|
||||
char *ext = NULL;
|
||||
bool okay = false;
|
||||
const int maxAttempts = 30;
|
||||
const char *prefix = "tmp";
|
||||
char numberBuf[32];
|
||||
time_t timeValue = time(NULL);
|
||||
unsigned int numberValue = (unsigned int)timeValue;
|
||||
int number = 0;
|
||||
int i;
|
||||
|
||||
ext = strcpy_alloc_force(path_get_extension(*tempDllPath));
|
||||
extLen = strlen(ext);
|
||||
if (extLen > 0)
|
||||
{
|
||||
strcat_alloc(&ext, ".");
|
||||
memmove(ext + 1, ext, extLen);
|
||||
ext[0] = '.';
|
||||
extLen++;
|
||||
}
|
||||
|
||||
|
||||
/* try up to 30 'random' filenames before giving up */
|
||||
for (i = 0; i < 30; i++)
|
||||
{
|
||||
numberValue = numberValue * 214013 + 2531011;
|
||||
number = (numberValue >> 14) % 100000;
|
||||
sprintf(numberBuf, "%05d", number);
|
||||
free_str(tempDllPath);
|
||||
strcat_alloc(tempDllPath, retroarchTempPath);
|
||||
strcat_alloc(tempDllPath, prefix);
|
||||
strcat_alloc(tempDllPath, numberBuf);
|
||||
strcat_alloc(tempDllPath, ext);
|
||||
okay = write_file_data(*tempDllPath, data, dataSize);
|
||||
if (okay)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
success:
|
||||
free_str(&ext);
|
||||
return true;
|
||||
failed:
|
||||
free_str(&ext);
|
||||
return false;
|
||||
}
|
||||
|
||||
void secondary_core_clear(void)
|
||||
{
|
||||
secondary_library_path = NULL;
|
||||
secondary_module = NULL;
|
||||
memset(&secondary_core, 0, sizeof(struct retro_core_t));
|
||||
}
|
||||
|
||||
bool secondary_core_create(void)
|
||||
{
|
||||
long port, device;
|
||||
bool contentless, is_inited;
|
||||
|
||||
if (last_core_type != CORE_TYPE_PLAIN || load_content_info == NULL || load_content_info->special != NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
free_str(&secondary_library_path);
|
||||
secondary_library_path = copy_core_to_temp_file();
|
||||
if (secondary_library_path == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
/* Load Core */
|
||||
if (init_libretro_sym_custom(CORE_TYPE_PLAIN, &secondary_core, secondary_library_path, &secondary_module))
|
||||
{
|
||||
secondary_core.symbols_inited = true;
|
||||
|
||||
core_set_default_callbacks(&secondary_callbacks);
|
||||
secondary_core.retro_set_video_refresh(secondary_callbacks.frame_cb);
|
||||
secondary_core.retro_set_audio_sample(secondary_callbacks.sample_cb);
|
||||
secondary_core.retro_set_audio_sample_batch(secondary_callbacks.sample_batch_cb);
|
||||
secondary_core.retro_set_input_state(secondary_callbacks.state_cb);
|
||||
secondary_core.retro_set_input_poll(secondary_callbacks.poll_cb);
|
||||
secondary_core.retro_set_environment(rarch_environment_cb);
|
||||
|
||||
secondary_core.retro_init();
|
||||
|
||||
content_get_status(&contentless, &is_inited);
|
||||
secondary_core.inited = is_inited;
|
||||
|
||||
/* Load Content */
|
||||
if (load_content_info == NULL || load_content_info->special != NULL)
|
||||
{
|
||||
/* disabled due to crashes */
|
||||
return false;
|
||||
#if 0
|
||||
secondary_core.game_loaded = secondary_core.retro_load_game_special(loadContentInfo.special->id, loadContentInfo.info, loadContentInfo.content->size);
|
||||
if (!secondary_core.game_loaded)
|
||||
{
|
||||
secondary_core_destroy();
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else if (load_content_info->content->size > 0 && load_content_info->content->elems[0].data != NULL)
|
||||
{
|
||||
secondary_core.game_loaded = secondary_core.retro_load_game(load_content_info->info);
|
||||
if (!secondary_core.game_loaded)
|
||||
{
|
||||
secondary_core_destroy();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (contentless)
|
||||
{
|
||||
secondary_core.game_loaded = secondary_core.retro_load_game(NULL);
|
||||
if (!secondary_core.game_loaded)
|
||||
{
|
||||
secondary_core_destroy();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
secondary_core.game_loaded = false;
|
||||
}
|
||||
if (!secondary_core.inited)
|
||||
{
|
||||
secondary_core_destroy();
|
||||
return false;
|
||||
}
|
||||
|
||||
for (port = 0; port < 16; port++)
|
||||
{
|
||||
device = port_map[port];
|
||||
if (device >= 0)
|
||||
{
|
||||
secondary_core.retro_set_controller_port_device(port, device);
|
||||
}
|
||||
}
|
||||
clear_controller_port_map();
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool secondary_core_run_no_input_polling(void)
|
||||
{
|
||||
bool okay;
|
||||
if (secondary_module == NULL)
|
||||
{
|
||||
okay = secondary_core_create();
|
||||
if (!okay)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
secondary_core.retro_run();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool secondary_core_deserialize(const void *buffer, int size)
|
||||
{
|
||||
bool okay;
|
||||
if (secondary_module == NULL)
|
||||
{
|
||||
okay = secondary_core_create();
|
||||
if (!okay)
|
||||
{
|
||||
secondary_core_destroy();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return secondary_core.retro_unserialize(buffer, size);
|
||||
}
|
||||
|
||||
void secondary_core_destroy(void)
|
||||
{
|
||||
if (secondary_module != NULL)
|
||||
{
|
||||
/* unload game from core */
|
||||
if (secondary_core.retro_unload_game != NULL)
|
||||
{
|
||||
secondary_core.retro_unload_game();
|
||||
}
|
||||
/* deinit */
|
||||
if (secondary_core.retro_deinit != NULL)
|
||||
{
|
||||
secondary_core.retro_deinit();
|
||||
}
|
||||
memset(&secondary_core, 0, sizeof(struct retro_core_t));
|
||||
|
||||
dylib_close(secondary_module);
|
||||
secondary_module = NULL;
|
||||
unlink_utf8(secondary_library_path);
|
||||
free_str(&secondary_library_path);
|
||||
}
|
||||
}
|
||||
|
||||
void remember_controller_port_device(long port, long device)
|
||||
{
|
||||
if (port >= 0 && port < 16)
|
||||
{
|
||||
port_map[port] = device;
|
||||
}
|
||||
if (secondary_module != NULL && secondary_core.retro_set_controller_port_device != NULL)
|
||||
{
|
||||
secondary_core.retro_set_controller_port_device(port, device);
|
||||
}
|
||||
}
|
||||
|
||||
void clear_controller_port_map(void)
|
||||
{
|
||||
int port;
|
||||
for (port = 0; port < 16; port++)
|
||||
{
|
||||
port_map[port] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static void free_file(FILE **file_p)
|
||||
{
|
||||
bool result;
|
||||
if (file_p == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (*file_p == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
result = fclose(*file_p) != 0;
|
||||
*file_p = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
#else
|
||||
#include "boolean.h"
|
||||
#include "core.h"
|
||||
bool secondary_core_run_no_input_polling(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bool secondary_core_deserialize(const void *buffer, int size)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
void secondary_core_destroy(void)
|
||||
{
|
||||
/* do nothing */
|
||||
}
|
||||
void remember_controller_port_device(long port, long device)
|
||||
{
|
||||
/* do nothing */
|
||||
}
|
||||
#endif
|
||||
|
19
runahead/secondary_core.h
Normal file
19
runahead/secondary_core.h
Normal file
@ -0,0 +1,19 @@
|
||||
#ifndef __SECONDARY_CORE_H__
|
||||
#define __SECONDARY_CORE_H__
|
||||
|
||||
#include "core_type.h"
|
||||
#include "retro_common_api.h"
|
||||
#include "boolean.h"
|
||||
|
||||
RETRO_BEGIN_DECLS
|
||||
|
||||
bool secondary_core_run_no_input_polling();
|
||||
bool secondary_core_deserialize(const void *buffer, int size);
|
||||
void secondary_core_destroy();
|
||||
void set_last_core_type(enum rarch_core_type type);
|
||||
void remember_controller_port_device(long port, long device);
|
||||
void clear_controller_port_map();
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
#endif
|
Loading…
x
Reference in New Issue
Block a user