RetroArch/runahead/secondary_core.c

465 lines
11 KiB
C
Raw Normal View History

2018-03-28 14:22:07 -05:00
#if defined(HAVE_DYNAMIC) && HAVE_DYNAMIC
#include <string.h>
#include <time.h>
#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX)
#ifndef LEGACY_WIN32
#define LEGACY_WIN32
#endif
#endif
#include <boolean.h>
#include <encodings/utf.h>
#include <compat/fopen_utf8.h>
#include <compat/unlink_utf8.h>
#include <dynamic/dylib.h>
#include <file/file_path.h>
2018-03-28 14:22:07 -05:00
#include "mem_util.h"
#include "../core.h"
2018-03-29 15:15:47 +02:00
#include "../dynamic.h"
#include "../paths.h"
#include "../content.h"
2018-03-28 14:22:07 -05:00
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);
2018-03-28 14:22:07 -05:00
static char* copy_core_to_temp_file(void);
2018-03-28 14:22:07 -05:00
static void* read_file_data_alloc(const char *fileName, int *size);
2018-03-28 14:22:07 -05:00
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);
2018-03-28 14:22:07 -05:00
static void* InputListElementConstructor(void);
2018-03-28 14:22:07 -05:00
static void secondary_core_clear(void);
2018-03-28 14:22:07 -05:00
static bool secondary_core_create(void);
2018-03-28 14:22:07 -05:00
bool secondary_core_run_no_input_polling(void);
2018-03-28 14:22:07 -05:00
bool secondary_core_deserialize(const void *buffer, int size);
2018-03-28 14:22:07 -05:00
void secondary_core_destroy(void);
2018-03-28 14:22:07 -05:00
void set_last_core_type(enum rarch_core_type type);
2018-03-28 14:22:07 -05:00
void remember_controller_port_device(long port, long device);
2018-03-28 14:22:07 -05:00
void clear_controller_port_map(void);
static void free_file(FILE **file_p);
char* get_temp_directory_alloc(void)
{
#ifdef _WIN32
#ifdef LEGACY_WIN32
2018-03-29 15:46:39 +02:00
DWORD pathLength = GetTempPath(0, NULL) + 1;
char *path = (char*)malloc(pathLength * sizeof(char));
2018-03-28 14:22:07 -05:00
path[pathLength - 1] = 0;
GetTempPath(pathLength, path);
return path;
#else
char *path;
2018-03-29 15:46:39 +02:00
DWORD pathLength = GetTempPathW(0, NULL) + 1;
wchar_t *wideStr = (wchar_t*)malloc(pathLength * sizeof(wchar_t));
2018-03-28 14:22:07 -05:00
wideStr[pathLength - 1] = 0;
GetTempPathW(pathLength, wideStr);
path = utf16_to_utf8_string_alloc(wideStr);
free(wideStr);
return path;
#endif
#else
2018-03-29 15:51:20 +02:00
char *path = strcpy_alloc_force(getenv("TMPDIR"));
2018-03-28 14:22:07 -05:00
return path;
#endif
}
char* copy_core_to_temp_file(void)
{
bool okay = false;
char *tempDirectory = NULL;
char *retroarchTempPath = NULL;
char *tempDllPath = NULL;
void *dllFileData = NULL;
int dllFileSize = 0;
const char *corePath = path_get(RARCH_PATH_CORE);
const char *coreBaseName = path_basename(corePath);
2018-03-28 14:22:07 -05:00
if (strlen(coreBaseName) == 0)
goto failed;
tempDirectory = get_temp_directory_alloc();
if (!tempDirectory)
2018-03-28 14:22:07 -05:00
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);
2018-03-28 14:22:07 -05:00
if (!okay)
goto failed;
dllFileData = read_file_data_alloc(corePath, &dllFileSize);
if (!dllFileData)
2018-03-28 14:22:07 -05:00
goto failed;
2018-03-28 14:22:07 -05:00
strcat_alloc(&tempDllPath, retroarchTempPath);
strcat_alloc(&tempDllPath, coreBaseName);
okay = write_file_data(tempDllPath, dllFileData, dllFileSize);
2018-03-28 14:22:07 -05:00
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;
int fileSize = 0;
size_t bytesRead = 0;
2018-03-28 14:22:07 -05:00
#ifdef _WIN32
int64_t fileSizeLong = 0;
#else
off64_t fileSizeLong = 0;
#endif
FILE *f = (FILE*)fopen_utf8(fileName, "rb");
if (!f)
2018-03-28 14:22:07 -05:00
goto failed;
2018-03-28 14:22:07 -05:00
fseek(f, 0, SEEK_END);
#ifdef _WIN32
2018-03-29 15:46:39 +02:00
fileSizeLong = _ftelli64(f);
2018-03-28 14:22:07 -05:00
#else
2018-03-29 15:46:39 +02:00
fileSizeLong = ftello64(f);
2018-03-28 14:22:07 -05:00
#endif
fseek(f, 0, SEEK_SET);
2018-03-28 14:22:07 -05:00
/* 256MB file size limit for DLL files */
if (fileSizeLong < 0 || fileSizeLong > 256 * 1024 * 1024)
goto failed;
2018-03-28 14:22:07 -05:00
fileSize = (int)fileSizeLong;
data = malloc(fileSize);
if (!data)
2018-03-28 14:22:07 -05:00
goto failed;
2018-03-28 14:22:07 -05:00
bytesRead = fread(data, 1, fileSize, f);
2018-03-28 14:22:07 -05:00
if ((int)bytesRead != (int)fileSize)
goto failed;
2018-03-28 14:22:07 -05:00
success:
free_file(&f);
if (size)
*size = fileSize;
2018-03-28 14:22:07 -05:00
return data;
failed:
free_ptr(&data);
free_file(&f);
if (size)
*size = 0;
2018-03-28 14:22:07 -05:00
return NULL;
}
bool write_file_data(const char *fileName, const void *data, int dataSize)
{
bool okay = false;
2018-03-28 14:22:07 -05:00
size_t bytesWritten = 0;
FILE *f = (FILE*)fopen_utf8(fileName, "wb");
2018-03-28 14:22:07 -05:00
if (!f)
goto failed;
2018-03-28 14:22:07 -05:00
bytesWritten = fwrite(data, 1, dataSize, f);
2018-03-28 14:22:07 -05:00
if (bytesWritten != dataSize)
goto failed;
2018-03-28 14:22:07 -05:00
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)
2018-03-28 14:22:07 -05:00
{
int i;
2018-03-28 14:22:07 -05:00
char numberBuf[32];
bool okay = false;
const int maxAttempts = 30;
const char *prefix = "tmp";
time_t timeValue = time(NULL);
2018-03-28 14:22:07 -05:00
unsigned int numberValue = (unsigned int)timeValue;
int number = 0;
char *ext = strcpy_alloc_force(path_get_extension(*tempDllPath));
int extLen = strlen(ext);
2018-03-28 14:22:07 -05:00
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;
2018-03-28 14:22:07 -05:00
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 ||
load_content_info->special)
2018-03-28 14:22:07 -05:00
return false;
free_str(&secondary_library_path);
secondary_library_path = copy_core_to_temp_file();
if (!secondary_library_path)
2018-03-28 14:22:07 -05:00
return false;
2018-03-28 14:22:07 -05:00
/* 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 || load_content_info->special)
2018-03-28 14:22:07 -05:00
{
/* 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);
2018-03-28 14:22:07 -05:00
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)
2018-03-28 14:22:07 -05:00
{
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;
2018-03-28 14:22:07 -05:00
return true;
}
bool secondary_core_run_no_input_polling(void)
{
if (!secondary_module)
2018-03-28 14:22:07 -05:00
{
bool okay = secondary_core_create();
2018-03-28 14:22:07 -05:00
if (!okay)
return false;
}
secondary_core.retro_run();
return true;
}
bool secondary_core_deserialize(const void *buffer, int size)
{
if (!secondary_module)
2018-03-28 14:22:07 -05:00
{
bool okay = secondary_core_create();
2018-03-28 14:22:07 -05:00
if (!okay)
{
secondary_core_destroy();
return false;
}
}
return secondary_core.retro_unserialize(buffer, size);
}
void secondary_core_destroy(void)
{
if (secondary_module)
2018-03-28 14:22:07 -05:00
{
/* unload game from core */
if (secondary_core.retro_unload_game)
2018-03-28 14:22:07 -05:00
secondary_core.retro_unload_game();
/* deinit */
if (secondary_core.retro_deinit)
2018-03-28 14:22:07 -05:00
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 && secondary_core.retro_set_controller_port_device)
2018-03-28 14:22:07 -05:00
secondary_core.retro_set_controller_port_device(port, device);
}
void clear_controller_port_map(void)
{
unsigned port;
2018-03-28 14:22:07 -05:00
for (port = 0; port < 16; port++)
port_map[port] = -1;
}
static void free_file(FILE **file_p)
{
bool result = false;
if (!file_p || !*file_p)
2018-03-28 14:22:07 -05:00
return;
result = fclose(*file_p) != 0;
2018-03-28 14:22:07 -05:00
*file_p = NULL;
return;
}
#else
#include <boolean.h>
#include "../core.h"
2018-03-28 14:22:07 -05:00
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