1
0
mirror of https://github.com/libretro/RetroArch synced 2024-07-03 00:38:44 +00:00

Runahead system

This commit is contained in:
Dwedit 2018-03-28 14:22:07 -05:00
parent 2143540c8c
commit f5e0346fc2
28 changed files with 1841 additions and 5 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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
View File

@ -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);

View File

@ -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;

View File

@ -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(&current_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;
}

View File

@ -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:
*

View File

@ -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
============================================================ */

View File

@ -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,

View File

@ -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."

View 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

View 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

View File

@ -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;

View File

@ -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,

View File

@ -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,

View File

@ -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 */

View File

@ -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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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