123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710 |
- /* RetroArch - A frontend for libretro.
- * Copyright (C) 2010-2014 - Hans-Kristian Arntzen
- * Copyright (C) 2011-2023 - Daniel De Matteis
- * Copyright (C) 2018-2023 - Dan Weiss
- * Copyright (C) 2022-2023 - Neil Fore
- *
- * RetroArch is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Found-
- * ation, either version 3 of the License, or (at your option) any later version.
- *
- * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with RetroArch.
- * If not, see <http://www.gnu.org/licenses/>.
- */
- #include <stdint.h>
- #ifdef HAVE_CONFIG_H
- #include "config.h"
- #endif
- #include <encodings/utf.h>
- #include <string/stdstring.h>
- #include <streams/file_stream.h>
- #include <time/rtime.h>
- #include "content.h"
- #include "core.h"
- #include "dynamic.h"
- #include "driver.h"
- #include "audio/audio_driver.h"
- #include "gfx/video_driver.h"
- #include "paths.h"
- #include "runloop.h"
- #include "verbosity.h"
- static int16_t input_state_get_last(unsigned port,
- unsigned device, unsigned index, unsigned id)
- {
- int i;
- runloop_state_t *runloop_st = runloop_state_get_ptr();
- if (!runloop_st->input_state_list)
- return 0;
- /* find list item */
- for (i = 0; i < runloop_st->input_state_list->size; i++)
- {
- input_list_element *element =
- (input_list_element*)runloop_st->input_state_list->data[i];
- if ( (element->port == port) &&
- (element->device == device) &&
- (element->index == index))
- {
- if (id < element->state_size)
- return element->state[id];
- return 0;
- }
- }
- return 0;
- }
- static void free_retro_ctx_load_content_info(struct
- retro_ctx_load_content_info *dest)
- {
- if (!dest)
- return;
- string_list_free((struct string_list*)dest->content);
- if (dest->info)
- free(dest->info);
- dest->info = NULL;
- dest->content = NULL;
- }
- static struct retro_game_info* clone_retro_game_info(const
- struct retro_game_info *src)
- {
- struct retro_game_info *dest = (struct retro_game_info*)malloc(
- sizeof(struct retro_game_info));
- if (!dest)
- return NULL;
- /* content_file_init() guarantees that all
- * elements of the source retro_game_info
- * struct will persist for the lifetime of
- * the core. This means we do not have to
- * copy any data; pointer assignment is
- * sufficient */
- dest->path = src->path;
- dest->data = src->data;
- dest->size = src->size;
- dest->meta = src->meta;
- return dest;
- }
- static struct retro_ctx_load_content_info
- *clone_retro_ctx_load_content_info(
- const struct retro_ctx_load_content_info *src)
- {
- struct retro_ctx_load_content_info *dest = NULL;
- if (!src || src->special)
- return NULL; /* refuse to deal with the Special field */
- dest = (struct retro_ctx_load_content_info*)
- malloc(sizeof(*dest));
- if (!dest)
- return NULL;
- dest->info = NULL;
- dest->content = NULL;
- dest->special = NULL;
- if (src->info)
- dest->info = clone_retro_game_info(src->info);
- if (src->content)
- dest->content = string_list_clone(src->content);
- return dest;
- }
- void runahead_set_load_content_info(void *data,
- const retro_ctx_load_content_info_t *ctx)
- {
- runloop_state_t *runloop_st = (runloop_state_t*)data;
- free_retro_ctx_load_content_info(runloop_st->load_content_info);
- free(runloop_st->load_content_info);
- runloop_st->load_content_info = clone_retro_ctx_load_content_info(ctx);
- }
- /* RUNAHEAD - SECONDARY CORE */
- #if defined(HAVE_DYNAMIC) || defined(HAVE_DYLIB)
- static void strcat_alloc(char **dst, const char *s)
- {
- size_t len1;
- char *src = *dst;
- if (!src)
- {
- if (s)
- {
- size_t len = strlen(s);
- if (len != 0)
- {
- char *_dst= (char*)malloc(len + 1);
- strcpy_literal(_dst, s);
- src = _dst;
- }
- else
- src = NULL;
- }
- else
- src = (char*)calloc(1,1);
- *dst = src;
- return;
- }
- if (!s)
- return;
- len1 = strlen(src);
- if (!(src = (char*)realloc(src, len1 + strlen(s) + 1)))
- return;
- *dst = src;
- strcpy_literal(src + len1, s);
- }
- void runahead_secondary_core_destroy(void *data)
- {
- runloop_state_t *runloop_st = (runloop_state_t*)data;
- if (!runloop_st->secondary_lib_handle)
- return;
- /* unload game from core */
- if (runloop_st->secondary_core.retro_unload_game)
- runloop_st->secondary_core.retro_unload_game();
- runloop_st->core_poll_type_override = POLL_TYPE_OVERRIDE_DONTCARE;
- /* deinit */
- if (runloop_st->secondary_core.retro_deinit)
- runloop_st->secondary_core.retro_deinit();
- memset(&runloop_st->secondary_core, 0, sizeof(struct retro_core_t));
- dylib_close(runloop_st->secondary_lib_handle);
- runloop_st->secondary_lib_handle = NULL;
- filestream_delete(runloop_st->secondary_library_path);
- if (runloop_st->secondary_library_path)
- free(runloop_st->secondary_library_path);
- runloop_st->secondary_library_path = NULL;
- }
- static char *get_tmpdir_alloc(const char *override_dir)
- {
- const char *src = NULL;
- char *path = NULL;
- #ifdef _WIN32
- #ifdef LEGACY_WIN32
- DWORD plen = GetTempPath(0, NULL) + 1;
- if (!(path = (char*)malloc(plen * sizeof(char))))
- return NULL;
- path[plen - 1] = 0;
- GetTempPath(plen, path);
- #else
- DWORD plen = GetTempPathW(0, NULL) + 1;
- wchar_t *wide_str = (wchar_t*)malloc(plen * sizeof(wchar_t));
- if (!wide_str)
- return NULL;
- wide_str[plen - 1] = 0;
- GetTempPathW(plen, wide_str);
- path = utf16_to_utf8_string_alloc(wide_str);
- free(wide_str);
- #endif
- #else
- #if defined ANDROID
- src = override_dir;
- #else
- {
- char *tmpdir = getenv("TMPDIR");
- if (tmpdir)
- src = tmpdir;
- else
- src = "/tmp";
- }
- #endif
- if (src)
- {
- size_t len = strlen(src);
- if (len != 0)
- {
- char *dst = (char*)malloc(len + 1);
- strcpy_literal(dst, src);
- path = dst;
- }
- }
- else
- path = (char*)calloc(1,1);
- #endif
- return path;
- }
- static bool write_file_with_random_name(char **temp_dll_path,
- const char *tmp_path, const void* data, ssize_t dataSize)
- {
- int i;
- size_t ext_len;
- char number_buf[32];
- bool okay = false;
- const char *prefix = "tmp";
- char *ext = NULL;
- time_t time_value = time(NULL);
- unsigned _number_value = (unsigned)time_value;
- const char *src = path_get_extension(*temp_dll_path);
- if (src)
- {
- size_t len = strlen(src);
- if (len != 0)
- {
- char *dst = (char*)malloc(len + 1);
- strcpy_literal(dst, src);
- ext = dst;
- }
- }
- else
- ext = (char*)calloc(1,1);
- ext_len = strlen(ext);
- if (ext_len > 0)
- {
- strcat_alloc(&ext, ".");
- memmove(ext + 1, ext, ext_len);
- ext[0] = '.';
- ext_len++;
- }
- /* Try up to 30 'random' filenames before giving up */
- for (i = 0; i < 30; i++)
- {
- int number_value = _number_value * 214013 + 2531011;
- int number = (number_value >> 14) % 100000;
- snprintf(number_buf, sizeof(number_buf), "%05d", number);
- if (*temp_dll_path)
- free(*temp_dll_path);
- *temp_dll_path = NULL;
- strcat_alloc(temp_dll_path, tmp_path);
- strcat_alloc(temp_dll_path, PATH_DEFAULT_SLASH());
- strcat_alloc(temp_dll_path, prefix);
- strcat_alloc(temp_dll_path, number_buf);
- strcat_alloc(temp_dll_path, ext);
- if (filestream_write_file(*temp_dll_path, data, dataSize))
- {
- okay = true;
- break;
- }
- }
- if (ext)
- free(ext);
- ext = NULL;
- return okay;
- }
- static char *copy_core_to_temp_file(
- const char *core_path,
- const char *dir_libretro)
- {
- char tmp_path[PATH_MAX_LENGTH];
- bool failed = false;
- char *tmpdir = NULL;
- char *tmp_dll_path = NULL;
- void *dll_file_data = NULL;
- int64_t dll_file_size = 0;
- const char *core_base_name = path_basename_nocompression(core_path);
- if (strlen(core_base_name) == 0)
- return NULL;
- if (!(tmpdir = get_tmpdir_alloc(dir_libretro)))
- return NULL;
- fill_pathname_join_special(tmp_path,
- tmpdir, "retroarch_temp",
- sizeof(tmp_path));
- if (!path_mkdir(tmp_path))
- {
- failed = true;
- goto end;
- }
- if (!filestream_read_file(core_path, &dll_file_data, &dll_file_size))
- {
- failed = true;
- goto end;
- }
- strcat_alloc(&tmp_dll_path, tmp_path);
- strcat_alloc(&tmp_dll_path, PATH_DEFAULT_SLASH());
- strcat_alloc(&tmp_dll_path, core_base_name);
- if (!filestream_write_file(tmp_dll_path, dll_file_data, dll_file_size))
- {
- /* try other file names */
- if (!write_file_with_random_name(&tmp_dll_path,
- tmp_path, dll_file_data, dll_file_size))
- failed = true;
- }
- end:
- if (tmpdir)
- free(tmpdir);
- if (dll_file_data)
- free(dll_file_data);
- tmpdir = NULL;
- dll_file_data = NULL;
- if (!failed)
- return tmp_dll_path;
- if (tmp_dll_path)
- free(tmp_dll_path);
- tmp_dll_path = NULL;
- return NULL;
- }
- static bool runloop_environment_secondary_core_hook(
- unsigned cmd, void *data)
- {
- runloop_state_t *runloop_st = runloop_state_get_ptr();
- bool result = runloop_environment_cb(cmd, data);
- if (runloop_st->flags & RUNLOOP_FLAG_HAS_VARIABLE_UPDATE)
- {
- if (cmd == RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE)
- {
- bool *bool_p = (bool*)data;
- *bool_p = true;
- runloop_st->flags &= ~RUNLOOP_FLAG_HAS_VARIABLE_UPDATE;
- return true;
- }
- else if (cmd == RETRO_ENVIRONMENT_GET_VARIABLE)
- runloop_st->flags &= ~RUNLOOP_FLAG_HAS_VARIABLE_UPDATE;
- }
- return result;
- }
- void runahead_clear_controller_port_map(void *data)
- {
- int port;
- runloop_state_t *runloop_st = (runloop_state_t*)data;
- for (port = 0; port < MAX_USERS; port++)
- runloop_st->port_map[port] = -1;
- }
- static bool secondary_core_create(runloop_state_t *runloop_st,
- settings_t *settings)
- {
- const enum rarch_core_type
- last_core_type = runloop_st->last_core_type;
- rarch_system_info_t *info = &runloop_st->system;
- unsigned num_active_users = settings->uints.input_max_users;
- uint8_t flags = content_get_flags();
- if ( (last_core_type != CORE_TYPE_PLAIN)
- || (!runloop_st->load_content_info)
- || ( runloop_st->load_content_info->special))
- return false;
- if (runloop_st->secondary_library_path)
- free(runloop_st->secondary_library_path);
- runloop_st->secondary_library_path = NULL;
- runloop_st->secondary_library_path = copy_core_to_temp_file(
- path_get(RARCH_PATH_CORE),
- settings->paths.directory_libretro);
- if (!runloop_st->secondary_library_path)
- return false;
- /* Load Core */
- if (!runloop_init_libretro_symbols(runloop_st,
- CORE_TYPE_PLAIN, &runloop_st->secondary_core,
- runloop_st->secondary_library_path,
- &runloop_st->secondary_lib_handle))
- return false;
- runloop_st->secondary_core.flags |= RETRO_CORE_FLAG_SYMBOLS_INITED;
- runloop_st->secondary_core.retro_set_environment(
- runloop_environment_secondary_core_hook);
- runloop_st->flags |= RUNLOOP_FLAG_HAS_VARIABLE_UPDATE;
- runloop_st->secondary_core.retro_init();
- if (flags & CONTENT_ST_FLAG_IS_INITED)
- runloop_st->secondary_core.flags |= RETRO_CORE_FLAG_INITED;
- else
- runloop_st->secondary_core.flags &= ~RETRO_CORE_FLAG_INITED;
- /* Load Content */
- /* disabled due to crashes */
- if ( (!runloop_st->load_content_info)
- || (runloop_st->load_content_info->special))
- return false;
- if ( ( runloop_st->load_content_info->content->size > 0)
- && runloop_st->load_content_info->content->elems[0].data)
- {
- if (!runloop_st->secondary_core.retro_load_game(
- runloop_st->load_content_info->info))
- {
- runloop_st->secondary_core.flags &= ~RETRO_CORE_FLAG_GAME_LOADED;
- goto error;
- }
- runloop_st->secondary_core.flags |= RETRO_CORE_FLAG_GAME_LOADED;
- }
- else if (flags & CONTENT_ST_FLAG_CORE_DOES_NOT_NEED_CONTENT)
- {
- if (!runloop_st->secondary_core.retro_load_game(NULL))
- {
- runloop_st->secondary_core.flags &= ~RETRO_CORE_FLAG_GAME_LOADED;
- goto error;
- }
- runloop_st->secondary_core.flags |= RETRO_CORE_FLAG_GAME_LOADED;
- }
- else
- runloop_st->secondary_core.flags &= ~RETRO_CORE_FLAG_GAME_LOADED;
- if (!(runloop_st->secondary_core.flags & RETRO_CORE_FLAG_INITED))
- goto error;
- core_set_default_callbacks(&runloop_st->secondary_callbacks);
- runloop_st->secondary_core.retro_set_video_refresh(
- runloop_st->secondary_callbacks.frame_cb);
- runloop_st->secondary_core.retro_set_audio_sample(
- runloop_st->secondary_callbacks.sample_cb);
- runloop_st->secondary_core.retro_set_audio_sample_batch(
- runloop_st->secondary_callbacks.sample_batch_cb);
- runloop_st->secondary_core.retro_set_input_state(
- runloop_st->secondary_callbacks.state_cb);
- runloop_st->secondary_core.retro_set_input_poll(
- runloop_st->secondary_callbacks.poll_cb);
- if (info)
- {
- int port;
- for (port = 0; port < MAX_USERS; port++)
- {
- if (port < (int)info->ports.size)
- {
- unsigned device = (port < (int)num_active_users) ?
- runloop_st->port_map[port] : RETRO_DEVICE_NONE;
- runloop_st->secondary_core.retro_set_controller_port_device(
- port, device);
- }
- }
- }
- #if defined(HAVE_DYNAMIC) || defined(HAVE_DYLIB)
- runahead_clear_controller_port_map(runloop_st);
- #endif
- return true;
- error:
- runahead_secondary_core_destroy(runloop_st);
- return false;
- }
- #if defined(HAVE_DYNAMIC) || defined(HAVE_DYLIB)
- bool secondary_core_ensure_exists(void *data, settings_t *settings)
- {
- runloop_state_t *runloop_st = (runloop_state_t*)data;
- if (!runloop_st->secondary_lib_handle)
- if (!secondary_core_create(runloop_st, settings))
- return false;
- return true;
- }
- #endif
- #if defined(HAVE_DYNAMIC)
- static bool secondary_core_deserialize(runloop_state_t *runloop_st,
- settings_t *settings,
- const void *data, size_t size)
- {
- bool ret = false;
- if (secondary_core_ensure_exists(runloop_st, settings))
- {
- runloop_st->flags |= RUNLOOP_FLAG_REQUEST_SPECIAL_SAVESTATE;
- ret = runloop_st->secondary_core.retro_unserialize(data, size);
- runloop_st->flags &= ~RUNLOOP_FLAG_REQUEST_SPECIAL_SAVESTATE;
- }
- else
- runahead_secondary_core_destroy(runloop_st);
- return ret;
- }
- #endif
- static void secondary_core_input_poll_null(void) { }
- static bool secondary_core_run_use_last_input(runloop_state_t *runloop_st)
- {
- retro_input_poll_t old_poll_function;
- retro_input_state_t old_input_function;
- if (!secondary_core_ensure_exists(runloop_st, config_get_ptr()))
- {
- runahead_secondary_core_destroy(runloop_st);
- return false;
- }
- old_poll_function = runloop_st->secondary_callbacks.poll_cb;
- old_input_function = runloop_st->secondary_callbacks.state_cb;
- runloop_st->secondary_callbacks.poll_cb = secondary_core_input_poll_null;
- runloop_st->secondary_callbacks.state_cb = input_state_get_last;
- runloop_st->secondary_core.retro_set_input_poll(
- runloop_st->secondary_callbacks.poll_cb);
- runloop_st->secondary_core.retro_set_input_state(
- runloop_st->secondary_callbacks.state_cb);
- runloop_st->secondary_core.retro_run();
- runloop_st->secondary_callbacks.poll_cb = old_poll_function;
- runloop_st->secondary_callbacks.state_cb = old_input_function;
- runloop_st->secondary_core.retro_set_input_poll(
- runloop_st->secondary_callbacks.poll_cb);
- runloop_st->secondary_core.retro_set_input_state(
- runloop_st->secondary_callbacks.state_cb);
- return true;
- }
- void runahead_remember_controller_port_device(void *data,
- long port, long device)
- {
- runloop_state_t *runloop_st = (runloop_state_t*)data;
- if (port >= 0 && port < MAX_USERS)
- runloop_st->port_map[port] = (int)device;
- if ( runloop_st->secondary_lib_handle
- && runloop_st->secondary_core.retro_set_controller_port_device)
- runloop_st->secondary_core.retro_set_controller_port_device((unsigned)port, (unsigned)device);
- }
- #else
- void runahead_secondary_core_destroy(void *data) { }
- #endif
- static void mylist_resize(my_list *list,
- int new_size, bool run_constructor)
- {
- int i;
- int new_capacity;
- int old_size;
- void *element = NULL;
- if (new_size < 0)
- new_size = 0;
- new_capacity = new_size;
- old_size = list->size;
- if (new_size == old_size)
- return;
- if (new_size > list->capacity)
- {
- if (new_capacity < list->capacity * 2)
- new_capacity = list->capacity * 2;
- /* try to realloc */
- list->data = (void**)realloc(
- (void*)list->data, new_capacity * sizeof(void*));
- for (i = list->capacity; i < new_capacity; i++)
- list->data[i] = NULL;
- list->capacity = new_capacity;
- }
- if (new_size <= list->size)
- {
- for (i = new_size; i < list->size; i++)
- {
- element = list->data[i];
- if (element)
- {
- list->destructor(element);
- list->data[i] = NULL;
- }
- }
- }
- else
- {
- for (i = list->size; i < new_size; i++)
- {
- list->data[i] = NULL;
- if (run_constructor)
- list->data[i] = list->constructor();
- }
- }
- list->size = new_size;
- }
- static void *mylist_add_element(my_list *list)
- {
- int old_size = list->size;
- if (list)
- mylist_resize(list, old_size + 1, true);
- return list->data[old_size];
- }
- static void mylist_destroy(my_list **list_p)
- {
- my_list *list = NULL;
- if (!list_p)
- return;
- list = *list_p;
- if (list)
- {
- mylist_resize(list, 0, false);
- free(list->data);
- free(list);
- *list_p = NULL;
- }
- }
- static void mylist_create(my_list **list_p, int initial_capacity,
- constructor_t constructor, destructor_t destructor)
- {
- my_list *list = NULL;
- if (!list_p)
- return;
- list = *list_p;
- if (list)
- mylist_destroy(list_p);
- list = (my_list*)malloc(sizeof(my_list));
- *list_p = list;
- list->size = 0;
- list->constructor = constructor;
- list->destructor = destructor;
- list->data = (void**)calloc(initial_capacity, sizeof(void*));
- list->capacity = initial_capacity;
- }
- static void *input_list_element_constructor(void)
- {
- void *ptr = malloc(sizeof(input_list_element));
- input_list_element *element = (input_list_element*)ptr;
- element->port = 0;
- element->device = 0;
- element->index = 0;
- element->state = (int16_t*)calloc(NAME_MAX_LENGTH,
- sizeof(int16_t));
- element->state_size = NAME_MAX_LENGTH;
- return ptr;
- }
- static void input_list_element_realloc(
- input_list_element *element,
- unsigned int new_size)
- {
- if (new_size > element->state_size)
- {
- element->state = (int16_t*)realloc(element->state,
- new_size * sizeof(int16_t));
- memset(&element->state[element->state_size], 0,
- (new_size - element->state_size) * sizeof(int16_t));
- element->state_size = new_size;
- }
- }
- static void input_list_element_expand(
- input_list_element *element, unsigned int new_index)
- {
- unsigned int new_size = element->state_size;
- if (new_size == 0)
- new_size = 32;
- while (new_index >= new_size)
- new_size *= 2;
- input_list_element_realloc(element, new_size);
- }
- static void input_list_element_destructor(void* element_ptr)
- {
- input_list_element *element = (input_list_element*)element_ptr;
- if (!element)
- return;
- free(element->state);
- free(element_ptr);
- }
- static void runahead_input_state_set_last(
- runloop_state_t *runloop_st,
- unsigned port, unsigned device,
- unsigned index, unsigned id, int16_t value)
- {
- unsigned i;
- input_list_element *element = NULL;
- if (!runloop_st->input_state_list)
- mylist_create(&runloop_st->input_state_list, 16,
- input_list_element_constructor,
- input_list_element_destructor);
- /* Find list item */
- for (i = 0; i < (unsigned)runloop_st->input_state_list->size; i++)
- {
- element = (input_list_element*)runloop_st->input_state_list->data[i];
- if ( (element->port == port) &&
- (element->device == device) &&
- (element->index == index)
- )
- {
- if (id >= element->state_size)
- input_list_element_expand(element, id);
- element->state[id] = value;
- return;
- }
- }
- element = NULL;
- if (runloop_st->input_state_list)
- element = (input_list_element*)
- mylist_add_element(runloop_st->input_state_list);
- if (element)
- {
- element->port = port;
- element->device = device;
- element->index = index;
- if (id >= element->state_size)
- input_list_element_expand(element, id);
- element->state[id] = value;
- }
- }
- static int16_t runahead_input_state_with_logging(unsigned port,
- unsigned device, unsigned index, unsigned id)
- {
- runloop_state_t *runloop_st = runloop_state_get_ptr();
- if (runloop_st->input_state_callback_original)
- {
- int16_t result =
- runloop_st->input_state_callback_original(
- port, device, index, id);
- int16_t last_input =
- input_state_get_last(port, device, index, id);
- if (result != last_input)
- runloop_st->flags |= RUNLOOP_FLAG_INPUT_IS_DIRTY;
- /*arbitrary limit of up to 65536 elements in state array*/
- if (id < 65536)
- runahead_input_state_set_last(runloop_st, port, device, index, id, result);
- return result;
- }
- return 0;
- }
- static void runahead_reset_hook(void)
- {
- runloop_state_t *runloop_st = runloop_state_get_ptr();
- runloop_st->flags |= RUNLOOP_FLAG_INPUT_IS_DIRTY;
- if (runloop_st->retro_reset_callback_original)
- runloop_st->retro_reset_callback_original();
- }
- static bool runahead_unserialize_hook(const void *buf, size_t size)
- {
- runloop_state_t *runloop_st = runloop_state_get_ptr();
- runloop_st->flags |= RUNLOOP_FLAG_INPUT_IS_DIRTY;
- if (runloop_st->retro_unserialize_callback_original)
- return runloop_st->retro_unserialize_callback_original(buf, size);
- return false;
- }
- static void runahead_add_input_state_hook(runloop_state_t *runloop_st)
- {
- struct retro_callbacks *cbs = &runloop_st->retro_ctx;
- if (!runloop_st->input_state_callback_original)
- {
- runloop_st->input_state_callback_original = cbs->state_cb;
- cbs->state_cb = runahead_input_state_with_logging;
- runloop_st->current_core.retro_set_input_state(cbs->state_cb);
- }
- if (!runloop_st->retro_reset_callback_original)
- {
- runloop_st->retro_reset_callback_original
- = runloop_st->current_core.retro_reset;
- runloop_st->current_core.retro_reset = runahead_reset_hook;
- }
- if (!runloop_st->retro_unserialize_callback_original)
- {
- runloop_st->retro_unserialize_callback_original = runloop_st->current_core.retro_unserialize;
- runloop_st->current_core.retro_unserialize = runahead_unserialize_hook;
- }
- }
- static void runahead_remove_input_state_hook(runloop_state_t *runloop_st)
- {
- struct retro_callbacks *cbs = &runloop_st->retro_ctx;
- if (runloop_st->input_state_callback_original)
- {
- cbs->state_cb =
- runloop_st->input_state_callback_original;
- runloop_st->current_core.retro_set_input_state(cbs->state_cb);
- runloop_st->input_state_callback_original = NULL;
- mylist_destroy(&runloop_st->input_state_list);
- }
- if (runloop_st->retro_reset_callback_original)
- {
- runloop_st->current_core.retro_reset =
- runloop_st->retro_reset_callback_original;
- runloop_st->retro_reset_callback_original = NULL;
- }
- if (runloop_st->retro_unserialize_callback_original)
- {
- runloop_st->current_core.retro_unserialize =
- runloop_st->retro_unserialize_callback_original;
- runloop_st->retro_unserialize_callback_original = NULL;
- }
- }
- static void *runahead_save_state_alloc(void)
- {
- runloop_state_t *runloop_st = runloop_state_get_ptr();
- retro_ctx_serialize_info_t *savestate = (retro_ctx_serialize_info_t*)
- malloc(sizeof(retro_ctx_serialize_info_t));
- if (!savestate)
- return NULL;
- savestate->data = NULL;
- savestate->data_const = NULL;
- savestate->size = 0;
- if ( (runloop_st->runahead_save_state_size > 0)
- && (runloop_st->flags & RUNLOOP_FLAG_RUNAHEAD_SAVE_STATE_SIZE_KNOWN))
- {
- savestate->data = malloc(runloop_st->runahead_save_state_size);
- savestate->data_const = savestate->data;
- savestate->size = runloop_st->runahead_save_state_size;
- }
- return savestate;
- }
- static void runahead_save_state_free(void *data)
- {
- retro_ctx_serialize_info_t *savestate = (retro_ctx_serialize_info_t*)data;
- if (!savestate)
- return;
- free(savestate->data);
- free(savestate);
- }
- static void runahead_save_state_list_init(
- runloop_state_t *runloop_st,
- size_t save_state_size)
- {
- runloop_st->runahead_save_state_size = save_state_size;
- runloop_st->flags |= RUNLOOP_FLAG_RUNAHEAD_SAVE_STATE_SIZE_KNOWN;
- mylist_create(&runloop_st->runahead_save_state_list, 16,
- runahead_save_state_alloc, runahead_save_state_free);
- }
- /* Hooks - Hooks to cleanup, and add dirty input hooks */
- static void runahead_remove_hooks(runloop_state_t *runloop_st)
- {
- if (runloop_st->original_retro_deinit)
- {
- runloop_st->current_core.retro_deinit =
- runloop_st->original_retro_deinit;
- runloop_st->original_retro_deinit = NULL;
- }
- if (runloop_st->original_retro_unload)
- {
- runloop_st->current_core.retro_unload_game =
- runloop_st->original_retro_unload;
- runloop_st->original_retro_unload = NULL;
- }
- runahead_remove_input_state_hook(runloop_st);
- }
- static void runahead_destroy(runloop_state_t *runloop_st)
- {
- mylist_destroy(&runloop_st->runahead_save_state_list);
- runahead_remove_hooks(runloop_st);
- runahead_clear_variables(runloop_st);
- }
- static void runahead_unload_hook(void)
- {
- runloop_state_t *runloop_st = runloop_state_get_ptr();
- runahead_remove_hooks(runloop_st);
- runahead_destroy(runloop_st);
- runahead_secondary_core_destroy(runloop_st);
- if (runloop_st->current_core.retro_unload_game)
- runloop_st->current_core.retro_unload_game();
- runloop_st->core_poll_type_override = POLL_TYPE_OVERRIDE_DONTCARE;
- }
- static void runahead_deinit_hook(void)
- {
- runloop_state_t *runloop_st = runloop_state_get_ptr();
- runahead_remove_hooks(runloop_st);
- runahead_destroy(runloop_st);
- runahead_secondary_core_destroy(runloop_st);
- if (runloop_st->current_core.retro_deinit)
- runloop_st->current_core.retro_deinit();
- }
- static void runahead_add_hooks(runloop_state_t *runloop_st)
- {
- if (!runloop_st->original_retro_deinit)
- {
- runloop_st->original_retro_deinit =
- runloop_st->current_core.retro_deinit;
- runloop_st->current_core.retro_deinit = runahead_deinit_hook;
- }
- if (!runloop_st->original_retro_unload)
- {
- runloop_st->original_retro_unload = runloop_st->current_core.retro_unload_game;
- runloop_st->current_core.retro_unload_game = runahead_unload_hook;
- }
- runahead_add_input_state_hook(runloop_st);
- }
- /* Runahead Code */
- static void runahead_error(runloop_state_t *runloop_st)
- {
- runloop_st->flags &= ~RUNLOOP_FLAG_RUNAHEAD_AVAILABLE;
- mylist_destroy(&runloop_st->runahead_save_state_list);
- runahead_remove_hooks(runloop_st);
- runloop_st->runahead_save_state_size = 0;
- runloop_st->flags |= RUNLOOP_FLAG_RUNAHEAD_SAVE_STATE_SIZE_KNOWN;
- }
- static bool runahead_create(runloop_state_t *runloop_st)
- {
- /* get savestate size and allocate buffer */
- retro_ctx_size_info_t info;
- video_driver_state_t *video_st = video_state_get_ptr();
- core_serialize_size_special(&info);
- runahead_save_state_list_init(runloop_st, info.size);
- if (video_st->flags & VIDEO_FLAG_ACTIVE)
- video_st->flags |= VIDEO_FLAG_RUNAHEAD_IS_ACTIVE;
- else
- video_st->flags &= ~VIDEO_FLAG_RUNAHEAD_IS_ACTIVE;
- if ( (runloop_st->runahead_save_state_size == 0)
- || !(runloop_st->flags & RUNLOOP_FLAG_RUNAHEAD_SAVE_STATE_SIZE_KNOWN))
- {
- runahead_error(runloop_st);
- return false;
- }
- runahead_add_hooks(runloop_st);
- runloop_st->flags |= RUNLOOP_FLAG_RUNAHEAD_FORCE_INPUT_DIRTY;
- if (runloop_st->runahead_save_state_list)
- mylist_resize(runloop_st->runahead_save_state_list, 1, true);
- return true;
- }
- static bool runahead_save_state(runloop_state_t *runloop_st)
- {
- retro_ctx_serialize_info_t *serialize_info;
- if (!runloop_st->runahead_save_state_list)
- return false;
- serialize_info =
- (retro_ctx_serialize_info_t*)runloop_st->runahead_save_state_list->data[0];
- if (core_serialize_special(serialize_info))
- return true;
- runahead_error(runloop_st);
- return false;
- }
- static bool runahead_load_state(runloop_state_t *runloop_st)
- {
- retro_ctx_serialize_info_t *serialize_info =
- (retro_ctx_serialize_info_t*)
- runloop_st->runahead_save_state_list->data[0];
- bool last_dirty = runloop_st->flags & RUNLOOP_FLAG_INPUT_IS_DIRTY;
- bool ret = core_unserialize_special(serialize_info);
- if (last_dirty)
- runloop_st->flags |= RUNLOOP_FLAG_INPUT_IS_DIRTY;
- else
- runloop_st->flags &= ~RUNLOOP_FLAG_INPUT_IS_DIRTY;
- if (!ret)
- runahead_error(runloop_st);
- return ret;
- }
- #if HAVE_DYNAMIC
- static bool runahead_load_state_secondary(runloop_state_t *runloop_st, settings_t *settings)
- {
- retro_ctx_serialize_info_t *serialize_info =
- (retro_ctx_serialize_info_t*)runloop_st->runahead_save_state_list->data[0];
- if (!secondary_core_deserialize(runloop_st,
- settings, serialize_info->data_const,
- serialize_info->size))
- {
- runloop_st->flags &= ~RUNLOOP_FLAG_RUNAHEAD_SECONDARY_CORE_AVAILABLE;
- runahead_error(runloop_st);
- return false;
- }
- return true;
- }
- #endif
- static void runahead_core_run_use_last_input(runloop_state_t *runloop_st)
- {
- struct retro_callbacks *cbs = &runloop_st->retro_ctx;
- retro_input_poll_t old_poll_function = cbs->poll_cb;
- retro_input_state_t old_input_function = cbs->state_cb;
- cbs->poll_cb = retro_input_poll_null;
- cbs->state_cb = input_state_get_last;
- runloop_st->current_core.retro_set_input_poll(cbs->poll_cb);
- runloop_st->current_core.retro_set_input_state(cbs->state_cb);
- runloop_st->current_core.retro_run();
- cbs->poll_cb = old_poll_function;
- cbs->state_cb = old_input_function;
- runloop_st->current_core.retro_set_input_poll(cbs->poll_cb);
- runloop_st->current_core.retro_set_input_state(cbs->state_cb);
- }
- void runahead_run(void *data,
- int runahead_count,
- bool runahead_hide_warnings,
- bool use_secondary)
- {
- runloop_state_t *runloop_st = (runloop_state_t*)data;
- int frame_number = 0;
- bool last_frame = false;
- bool suspended_frame = false;
- #if defined(HAVE_DYNAMIC) || defined(HAVE_DYLIB)
- const bool have_dynamic = true;
- settings_t *settings = config_get_ptr();
- #else
- const bool have_dynamic = false;
- #endif
- video_driver_state_t
- *video_st = video_state_get_ptr();
- uint64_t frame_count = video_st->frame_count;
- audio_driver_state_t
- *audio_st = audio_state_get_ptr();
- if ( runahead_count <= 0
- || !(runloop_st->flags & RUNLOOP_FLAG_RUNAHEAD_AVAILABLE))
- goto force_input_dirty;
- if (!(runloop_st->flags & RUNLOOP_FLAG_RUNAHEAD_SAVE_STATE_SIZE_KNOWN))
- {
- /* Disable runahead if current core reports
- * that it has an insufficient savestate
- * support level */
- if (!core_info_current_supports_runahead())
- {
- runahead_error(runloop_st);
- /* If core is incompatible with runahead,
- * log a warning but do not spam OSD messages.
- * Runahead menu entries are hidden when using
- * incompatible cores, so there is no mechanism
- * for users to respond to notifications. In
- * addition, auto-disabling runahead is a feature,
- * not a cause for 'concern'; OSD warnings should
- * be reserved for when a core reports that it is
- * runahead-compatible but subsequently fails in
- * execution */
- RARCH_WARN("[Run-Ahead]: %s\n", msg_hash_to_str(MSG_RUNAHEAD_CORE_DOES_NOT_SUPPORT_RUNAHEAD));
- goto force_input_dirty;
- }
- if (!runahead_create(runloop_st))
- {
- const char *runahead_failed_str =
- msg_hash_to_str(MSG_RUNAHEAD_CORE_DOES_NOT_SUPPORT_SAVESTATES);
- if (!runahead_hide_warnings)
- runloop_msg_queue_push(runahead_failed_str, 0, 2 * 60, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- RARCH_WARN("[Run-Ahead]: %s\n", runahead_failed_str);
- goto force_input_dirty;
- }
- }
- /* Check for GUI */
- /* Hack: If we were in the GUI, force a resync. */
- if (frame_count != runloop_st->runahead_last_frame_count + 1)
- runloop_st->flags |= RUNLOOP_FLAG_RUNAHEAD_FORCE_INPUT_DIRTY;
- runloop_st->runahead_last_frame_count = frame_count;
- if ( !use_secondary
- || !have_dynamic
- || !(runloop_st->flags & RUNLOOP_FLAG_RUNAHEAD_SECONDARY_CORE_AVAILABLE))
- {
- /* TODO: multiple savestates for higher performance
- * when not using secondary core */
- for (frame_number = 0; frame_number <= runahead_count; frame_number++)
- {
- last_frame = frame_number == runahead_count;
- suspended_frame = !last_frame;
- if (suspended_frame)
- {
- audio_st->flags |= AUDIO_FLAG_SUSPENDED;
- video_st->flags &= ~VIDEO_FLAG_ACTIVE;
- }
- if (frame_number == 0)
- core_run();
- else
- runahead_core_run_use_last_input(runloop_st);
- if (suspended_frame)
- {
- if (video_st->flags & VIDEO_FLAG_RUNAHEAD_IS_ACTIVE)
- video_st->flags |= VIDEO_FLAG_ACTIVE;
- else
- video_st->flags &= ~VIDEO_FLAG_ACTIVE;
- audio_st->flags &= ~AUDIO_FLAG_SUSPENDED;
- }
- if (frame_number == 0)
- {
- if (!runahead_save_state(runloop_st))
- {
- const char *runahead_failed_str =
- msg_hash_to_str(MSG_RUNAHEAD_FAILED_TO_SAVE_STATE);
- runloop_msg_queue_push(runahead_failed_str, 0, 3 * 60, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- RARCH_WARN("[Run-Ahead]: %s\n", runahead_failed_str);
- return;
- }
- }
- if (last_frame)
- {
- if (!runahead_load_state(runloop_st))
- {
- const char *runahead_failed_str =
- msg_hash_to_str(MSG_RUNAHEAD_FAILED_TO_LOAD_STATE);
- runloop_msg_queue_push(runahead_failed_str, 0, 3 * 60, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- RARCH_WARN("[Run-Ahead]: %s\n", runahead_failed_str);
- return;
- }
- }
- }
- }
- else
- {
- #if HAVE_DYNAMIC
- if (!secondary_core_ensure_exists(runloop_st, config_get_ptr()))
- {
- const char *runahead_failed_str =
- msg_hash_to_str(MSG_RUNAHEAD_FAILED_TO_CREATE_SECONDARY_INSTANCE);
- runahead_secondary_core_destroy(runloop_st);
- runloop_st->flags &= ~RUNLOOP_FLAG_RUNAHEAD_SECONDARY_CORE_AVAILABLE;
- runloop_msg_queue_push(runahead_failed_str, 0, 3 * 60, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- RARCH_WARN("[Run-Ahead]: %s\n", runahead_failed_str);
- goto force_input_dirty;
- }
- /* run main core with video suspended */
- video_st->flags &= ~VIDEO_FLAG_ACTIVE;
- core_run();
- if (video_st->flags & VIDEO_FLAG_RUNAHEAD_IS_ACTIVE)
- video_st->flags |= VIDEO_FLAG_ACTIVE;
- else
- video_st->flags &= ~VIDEO_FLAG_ACTIVE;
- if ( (runloop_st->flags & RUNLOOP_FLAG_INPUT_IS_DIRTY)
- || (runloop_st->flags & RUNLOOP_FLAG_RUNAHEAD_FORCE_INPUT_DIRTY))
- {
- runloop_st->flags &= ~RUNLOOP_FLAG_INPUT_IS_DIRTY;
- if (!runahead_save_state(runloop_st))
- {
- const char *runahead_failed_str =
- msg_hash_to_str(MSG_RUNAHEAD_FAILED_TO_SAVE_STATE);
- runloop_msg_queue_push(runahead_failed_str, 0, 3 * 60, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- RARCH_WARN("[Run-Ahead]: %s\n", runahead_failed_str);
- return;
- }
- if (!runahead_load_state_secondary(runloop_st, settings))
- {
- const char *runahead_failed_str =
- msg_hash_to_str(MSG_RUNAHEAD_FAILED_TO_LOAD_STATE);
- runloop_msg_queue_push(runahead_failed_str, 0, 3 * 60, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- RARCH_WARN("[Run-Ahead]: %s\n", runahead_failed_str);
- return;
- }
- for (frame_number = 0; frame_number < runahead_count - 1; frame_number++)
- {
- video_st->flags &= ~VIDEO_FLAG_ACTIVE;
- audio_st->flags |= AUDIO_FLAG_SUSPENDED
- | AUDIO_FLAG_HARD_DISABLE;
- if (secondary_core_run_use_last_input(runloop_st))
- runloop_st->flags |= RUNLOOP_FLAG_RUNAHEAD_SECONDARY_CORE_AVAILABLE;
- else
- runloop_st->flags &= ~RUNLOOP_FLAG_RUNAHEAD_SECONDARY_CORE_AVAILABLE;
- audio_st->flags &= ~(AUDIO_FLAG_SUSPENDED
- | AUDIO_FLAG_HARD_DISABLE);
- if (video_st->flags & VIDEO_FLAG_RUNAHEAD_IS_ACTIVE)
- video_st->flags |= VIDEO_FLAG_ACTIVE;
- else
- video_st->flags &= ~VIDEO_FLAG_ACTIVE;
- }
- }
- audio_st->flags |= AUDIO_FLAG_SUSPENDED
- | AUDIO_FLAG_HARD_DISABLE;
- if (secondary_core_run_use_last_input(runloop_st))
- runloop_st->flags |= RUNLOOP_FLAG_RUNAHEAD_SECONDARY_CORE_AVAILABLE;
- else
- runloop_st->flags &= ~RUNLOOP_FLAG_RUNAHEAD_SECONDARY_CORE_AVAILABLE;
- audio_st->flags &= ~(AUDIO_FLAG_SUSPENDED
- | AUDIO_FLAG_HARD_DISABLE);
- #endif
- }
- runloop_st->flags &= ~RUNLOOP_FLAG_RUNAHEAD_FORCE_INPUT_DIRTY;
- return;
- force_input_dirty:
- core_run();
- runloop_st->flags |= RUNLOOP_FLAG_RUNAHEAD_FORCE_INPUT_DIRTY;
- }
- /* Preemptive Frames */
- static int16_t preempt_input_state(unsigned port,
- unsigned device, unsigned index, unsigned id)
- {
- settings_t *settings = config_get_ptr();
- runloop_state_t *runloop_st = runloop_state_get_ptr();
- preempt_t *preempt = runloop_st->preempt_data;
- unsigned device_class = device & RETRO_DEVICE_MASK;
- unsigned *port_map = settings->uints.input_remap_port_map[port];
- uint8_t p;
- switch (device_class)
- {
- case RETRO_DEVICE_ANALOG:
- /* Add requested inputs to mask */
- while ((p = *(port_map++)) < MAX_USERS)
- preempt->analog_mask[p] |= (1 << (id + index * 2));
- break;
- case RETRO_DEVICE_LIGHTGUN:
- case RETRO_DEVICE_MOUSE:
- case RETRO_DEVICE_POINTER:
- /* Set pointing device for this port */
- while ((p = *(port_map++)) < MAX_USERS)
- preempt->ptr_dev[p] = device_class;
- break;
- default:
- break;
- }
- return input_driver_state_wrapper(port, device, index, id);
- }
- static const char* preempt_allocate(runloop_state_t *runloop_st,
- const uint8_t frames)
- {
- preempt_t *preempt = (preempt_t*)calloc(1, sizeof(preempt_t));
- retro_ctx_size_info_t info;
- uint8_t i;
- if (!(runloop_st->preempt_data = preempt))
- return msg_hash_to_str(MSG_PREEMPT_FAILED_TO_ALLOCATE);
- core_serialize_size_special(&info);
- if (!info.size)
- return msg_hash_to_str(MSG_PREEMPT_CORE_DOES_NOT_SUPPORT_SAVESTATES);
- preempt->state_size = info.size;
- preempt->frames = frames;
- for (i = 0; i < frames; i++)
- {
- preempt->buffer[i] = malloc(preempt->state_size);
- if (!preempt->buffer[i])
- return msg_hash_to_str(MSG_PREEMPT_FAILED_TO_ALLOCATE);
- }
- return NULL;
- }
- /**
- * preempt_deinit:
- *
- * Frees preempt object and unsets overrides.
- **/
- void preempt_deinit(void *data)
- {
- runloop_state_t *runloop_st = (runloop_state_t*)data;
- preempt_t *preempt = runloop_st->preempt_data;
- struct retro_core_t *current_core = &runloop_st->current_core;
- size_t i;
- if (!preempt)
- return;
- /* Free memory */
- for (i = 0; i < preempt->frames; i++)
- free(preempt->buffer[i]);
- free(preempt);
- runloop_st->preempt_data = NULL;
- /* Undo overrides */
- runloop_st->flags |= (RUNLOOP_FLAG_RUNAHEAD_AVAILABLE
- | RUNLOOP_FLAG_RUNAHEAD_SECONDARY_CORE_AVAILABLE);
- if (current_core->retro_set_input_poll)
- current_core->retro_set_input_poll(runloop_st->input_poll_callback_original);
- if (current_core->retro_set_input_state)
- current_core->retro_set_input_state(runloop_st->retro_ctx.state_cb);
- }
- /**
- * preempt_init:
- *
- * @return true on success, false on failure
- *
- * Allocates savestate buffer and sets overrides for preemptive frames.
- **/
- bool preempt_init(void *data)
- {
- runloop_state_t *runloop_st = (runloop_state_t*)data;
- settings_t *settings = config_get_ptr();
- const char *failed_str = NULL;
- if ( runloop_st->preempt_data
- || !settings->bools.preemptive_frames_enable
- || !settings->uints.run_ahead_frames
- || !(runloop_st->current_core.flags & RETRO_CORE_FLAG_GAME_LOADED))
- return false;
- /* Check if supported - same requirements as runahead */
- if (!core_info_current_supports_runahead())
- {
- failed_str = msg_hash_to_str(MSG_PREEMPT_CORE_DOES_NOT_SUPPORT_PREEMPT);
- goto error;
- }
- /* Set flags to block runahead and request 'same instance' states */
- runloop_st->flags &= ~(RUNLOOP_FLAG_RUNAHEAD_AVAILABLE
- | RUNLOOP_FLAG_RUNAHEAD_SECONDARY_CORE_AVAILABLE);
- /* Run at least one frame before attempting
- * retro_serialize_size or retro_serialize */
- if (video_state_get_ptr()->frame_count == 0)
- runloop_st->current_core.retro_run();
- /* Allocate - same 'frames' setting as runahead */
- if ((failed_str = preempt_allocate(runloop_st,
- settings->uints.run_ahead_frames)))
- goto error;
- /* Only poll in preempt_run() */
- runloop_st->current_core.retro_set_input_poll(retro_input_poll_null);
- /* Track requested analog states and pointing device types */
- runloop_st->current_core.retro_set_input_state(preempt_input_state);
- return true;
- error:
- preempt_deinit(runloop_st);
- if (!settings->bools.preemptive_frames_hide_warnings)
- runloop_msg_queue_push(
- failed_str, 0, 2 * 60, true,
- NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- RARCH_WARN("[Preemptive Frames]: %s\n", failed_str);
- return false;
- }
- static INLINE bool preempt_analog_input_dirty(preempt_t *preempt,
- retro_input_state_t state_cb, unsigned port, unsigned mapped_port)
- {
- int16_t state[20] = {0};
- uint8_t base, i;
- /* axes */
- for (i = 0; i < 2; i++)
- {
- base = i * 2;
- if (preempt->analog_mask[port] & (1 << (base )))
- state[base ] = state_cb(mapped_port, RETRO_DEVICE_ANALOG, i, 0);
- if (preempt->analog_mask[port] & (1 << (base + 1)))
- state[base + 1] = state_cb(mapped_port, RETRO_DEVICE_ANALOG, i, 1);
- }
- /* buttons */
- for (i = 0; i < RARCH_FIRST_CUSTOM_BIND; i++)
- {
- if (preempt->analog_mask[port] & (1 << (i + 4)))
- state[i + 4] = state_cb(mapped_port, RETRO_DEVICE_ANALOG,
- RETRO_DEVICE_INDEX_ANALOG_BUTTON, i);
- }
- if (memcmp(preempt->analog_state[port], state, sizeof(state)) == 0)
- return false;
- memcpy(preempt->analog_state[port], state, sizeof(state));
- return true;
- }
- static INLINE bool preempt_ptr_input_dirty(preempt_t *preempt,
- retro_input_state_t state_cb, unsigned device,
- unsigned port, unsigned mapped_port)
- {
- int16_t state[4] = {0};
- unsigned count_id = 0;
- unsigned x_id = 0;
- unsigned id, max_id;
- switch (device)
- {
- case RETRO_DEVICE_MOUSE:
- max_id = RETRO_DEVICE_ID_MOUSE_BUTTON_5;
- break;
- case RETRO_DEVICE_LIGHTGUN:
- x_id = RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X;
- max_id = RETRO_DEVICE_ID_LIGHTGUN_DPAD_RIGHT;
- break;
- case RETRO_DEVICE_POINTER:
- max_id = RETRO_DEVICE_ID_POINTER_PRESSED;
- count_id = RETRO_DEVICE_ID_POINTER_COUNT;
- break;
- default:
- return false;
- }
- /* x, y */
- state[0] = state_cb(mapped_port, device, 0, x_id );
- state[1] = state_cb(mapped_port, device, 0, x_id + 1);
- /* buttons */
- for (id = 2; id <= max_id; id++)
- state[2] |= state_cb(mapped_port, device, 0, id) ? 1 << id : 0;
- /* ptr count */
- if (count_id)
- state[3] = state_cb(mapped_port, device, 0, count_id);
- if (memcmp(preempt->ptrdev_state[port], state, sizeof(state)) == 0)
- return false;
- memcpy(preempt->ptrdev_state[port], state, sizeof(state));
- return true;
- }
- static INLINE void preempt_input_poll(preempt_t *preempt,
- runloop_state_t *runloop_st, settings_t *settings)
- {
- size_t p;
- int16_t joypad_state;
- retro_input_state_t state_cb = input_driver_state_wrapper;
- unsigned max_users = settings->uints.input_max_users;
- input_driver_poll();
- /* Check for input state changes */
- for (p = 0; p < max_users; p++)
- {
- unsigned mapped_port = settings->uints.input_remap_ports[p];
- unsigned device = settings->uints.input_libretro_device[mapped_port]
- & RETRO_DEVICE_MASK;
- switch (device)
- {
- case RETRO_DEVICE_JOYPAD:
- case RETRO_DEVICE_ANALOG:
- /* Check full digital joypad */
- joypad_state = state_cb(mapped_port, RETRO_DEVICE_JOYPAD,
- 0, RETRO_DEVICE_ID_JOYPAD_MASK);
- if (joypad_state != preempt->joypad_state[p])
- {
- preempt->joypad_state[p] = joypad_state;
- runloop_st->flags |= RUNLOOP_FLAG_INPUT_IS_DIRTY;
- }
- /* Check requested analogs */
- if ( preempt->analog_mask[p]
- && preempt_analog_input_dirty(preempt, state_cb, (unsigned)p, mapped_port))
- runloop_st->flags |= RUNLOOP_FLAG_INPUT_IS_DIRTY;
- break;
- case RETRO_DEVICE_MOUSE:
- case RETRO_DEVICE_LIGHTGUN:
- case RETRO_DEVICE_POINTER:
- /* Check full device state */
- if (preempt_ptr_input_dirty(
- preempt, state_cb, preempt->ptr_dev[p], (unsigned)p, mapped_port))
- runloop_st->flags |= RUNLOOP_FLAG_INPUT_IS_DIRTY;
- break;
- default:
- break;
- }
- }
- /* Clear requested inputs */
- memset(preempt->analog_mask, 0, max_users * sizeof(uint32_t));
- memset(preempt->ptr_dev, 0, max_users * sizeof(uint8_t));
- }
- /* macro for preempt_run */
- #define PREEMPT_NEXT_PTR(x) ((x + 1) % preempt->frames)
- /**
- * preempt_run:
- * @preempt : pointer to preemptive frames object
- *
- * Call in place of core_run() for preemptive frames.
- **/
- void preempt_run(preempt_t *preempt, void *data)
- {
- runloop_state_t *runloop_st = (runloop_state_t*)data;
- struct retro_core_t *current_core = &runloop_st->current_core;
- const char *failed_str = NULL;
- settings_t *settings = config_get_ptr();
- audio_driver_state_t *audio_st = audio_state_get_ptr();
- video_driver_state_t *video_st = video_state_get_ptr();
-
- /* Poll and check for dirty input */
- preempt_input_poll(preempt, runloop_st, settings);
- runloop_st->flags |= RUNLOOP_FLAG_REQUEST_SPECIAL_SAVESTATE;
- if ((runloop_st->flags & RUNLOOP_FLAG_INPUT_IS_DIRTY)
- && preempt->frame_count >= preempt->frames)
- {
- /* Suspend A/V and run preemptive frames */
- audio_st->flags |= AUDIO_FLAG_SUSPENDED;
- video_st->flags &= ~VIDEO_FLAG_ACTIVE;
- if (!current_core->retro_unserialize(
- preempt->buffer[preempt->start_ptr], preempt->state_size))
- {
- failed_str = msg_hash_to_str(MSG_PREEMPT_FAILED_TO_LOAD_STATE);
- goto error;
- }
- current_core->retro_run();
- preempt->replay_ptr = PREEMPT_NEXT_PTR(preempt->start_ptr);
- while (preempt->replay_ptr != preempt->start_ptr)
- {
- if (!current_core->retro_serialize(
- preempt->buffer[preempt->replay_ptr], preempt->state_size))
- {
- failed_str = msg_hash_to_str(MSG_PREEMPT_FAILED_TO_SAVE_STATE);
- goto error;
- }
- current_core->retro_run();
- preempt->replay_ptr = PREEMPT_NEXT_PTR(preempt->replay_ptr);
- }
- audio_st->flags &= ~AUDIO_FLAG_SUSPENDED;
- video_st->flags |= VIDEO_FLAG_ACTIVE;
- }
- /* Save current state and set start_ptr to oldest state */
- if (!current_core->retro_serialize(
- preempt->buffer[preempt->start_ptr], preempt->state_size))
- {
- failed_str = msg_hash_to_str(MSG_PREEMPT_FAILED_TO_SAVE_STATE);
- goto error;
- }
- preempt->start_ptr = PREEMPT_NEXT_PTR(preempt->start_ptr);
- runloop_st->flags &= ~(RUNLOOP_FLAG_REQUEST_SPECIAL_SAVESTATE
- | RUNLOOP_FLAG_INPUT_IS_DIRTY);
- /* Run normal frame */
- current_core->retro_run();
- preempt->frame_count++;
- return;
- error:
- runloop_st->flags &= ~(RUNLOOP_FLAG_REQUEST_SPECIAL_SAVESTATE
- | RUNLOOP_FLAG_INPUT_IS_DIRTY);
- audio_st->flags &= ~AUDIO_FLAG_SUSPENDED;
- video_st->flags |= VIDEO_FLAG_ACTIVE;
- preempt_deinit(runloop_st);
- if (!settings->bools.preemptive_frames_hide_warnings)
- runloop_msg_queue_push(
- failed_str, 0, 2 * 60, true,
- NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
- RARCH_ERR("[Preemptive Frames]: %s\n", failed_str);
- }
- void runahead_clear_variables(void *data)
- {
- runloop_state_t *runloop_st = (runloop_state_t*)data;
- video_driver_state_t *video_st = video_state_get_ptr();
- runloop_st->runahead_save_state_size = 0;
- runloop_st->flags &= ~RUNLOOP_FLAG_RUNAHEAD_SAVE_STATE_SIZE_KNOWN;
- video_st->flags |= VIDEO_FLAG_RUNAHEAD_IS_ACTIVE;
- runloop_st->flags |= RUNLOOP_FLAG_RUNAHEAD_AVAILABLE
- | RUNLOOP_FLAG_RUNAHEAD_SECONDARY_CORE_AVAILABLE
- | RUNLOOP_FLAG_RUNAHEAD_FORCE_INPUT_DIRTY;
- runloop_st->runahead_last_frame_count = 0;
- }
|