diff --git a/Makefile.common b/Makefile.common index 0bcd0a2e89..ed89f33fd3 100644 --- a/Makefile.common +++ b/Makefile.common @@ -232,6 +232,7 @@ endif OBJ += frontend/frontend_driver.o \ retroarch.o \ + runloop.o \ command.o \ msg_hash.o \ intl/msg_hash_us.o \ diff --git a/griffin/griffin.c b/griffin/griffin.c index ec0204448f..2ce708bbdb 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -1191,6 +1191,7 @@ GIT RETROARCH ============================================================ */ #include "../retroarch.c" +#include "../runloop.c" #include "../command.c" #include "../libretro-common/queues/task_queue.c" diff --git a/retroarch.c b/retroarch.c index 56a16cf364..a4ff515a8b 100644 --- a/retroarch.c +++ b/retroarch.c @@ -1,6 +1,6 @@ /* RetroArch - A frontend for libretro. * Copyright (C) 2010-2014 - Hans-Kristian Arntzen - * Copyright (C) 2011-2017 - Daniel De Matteis + * Copyright (C) 2011-2021 - Daniel De Matteis * Copyright (C) 2012-2015 - Michael Lelli * Copyright (C) 2014-2017 - Jean-André Santoni * Copyright (C) 2016-2019 - Brad Parker @@ -9200,14 +9200,12 @@ static bool path_init_subsystem(struct rarch_state *p_rarch) return true; } -static void path_init_savefile(void) +static void path_init_savefile(runloop_state_t *p_runloop) { - bool should_sram_be_used = runloop_state.rarch_use_sram - && !runloop_state.rarch_is_sram_save_disabled; + bool should_sram_be_used = p_runloop->rarch_use_sram + && !p_runloop->rarch_is_sram_save_disabled; - runloop_state.rarch_use_sram = should_sram_be_used; - - if (!runloop_state.rarch_use_sram) + if (!(p_runloop->rarch_use_sram = should_sram_be_used)) { RARCH_LOG("[SRAM]: %s\n", msg_hash_to_str(MSG_SRAM_WILL_NOT_BE_SAVED)); @@ -10409,36 +10407,35 @@ bool menu_driver_is_alive(void) /* MESSAGE QUEUE */ -static void retroarch_msg_queue_deinit(void) +static void runloop_msg_queue_deinit(runloop_state_t *p_runloop) { RUNLOOP_MSG_QUEUE_LOCK(runloop_state); - msg_queue_deinitialize(&runloop_state.msg_queue); + msg_queue_deinitialize(&p_runloop->msg_queue); RUNLOOP_MSG_QUEUE_UNLOCK(runloop_state); #ifdef HAVE_THREADS - slock_free(runloop_state.msg_queue_lock); - runloop_state.msg_queue_lock = NULL; + slock_free(p_runloop->msg_queue_lock); + p_runloop->msg_queue_lock = NULL; #endif - runloop_state.msg_queue_size = 0; + p_runloop->msg_queue_size = 0; } -static void retroarch_msg_queue_init(void) +static void runloop_msg_queue_init(runloop_state_t *p_runloop) { - retroarch_msg_queue_deinit(); - msg_queue_initialize(&runloop_state.msg_queue, 8); + runloop_msg_queue_deinit(p_runloop); + msg_queue_initialize(&p_runloop->msg_queue, 8); #ifdef HAVE_THREADS - runloop_state.msg_queue_lock = slock_new(); + p_runloop->msg_queue_lock = slock_new(); #endif } #ifdef HAVE_THREADS -static void retroarch_autosave_deinit(void) +static void runloop_autosave_deinit(runloop_state_t *p_runloop) { - const bool rarch_use_sram = runloop_state.rarch_use_sram; - if (rarch_use_sram) + if (p_runloop->rarch_use_sram) autosave_deinit(); } #endif @@ -12027,7 +12024,7 @@ static bool command_event_disk_control_append_image( return false; #ifdef HAVE_THREADS - retroarch_autosave_deinit(); + runloop_autosave_deinit(&runloop_state); #endif /* TODO/FIXME: Need to figure out what to do with subsystems case. */ @@ -13888,7 +13885,7 @@ bool command_event(enum event_command cmd, void *data) break; case CMD_EVENT_AUTOSAVE_INIT: #ifdef HAVE_THREADS - retroarch_autosave_deinit(); + runloop_autosave_deinit(&runloop_state); { #ifdef HAVE_NETWORKING unsigned autosave_interval = @@ -15368,7 +15365,7 @@ void main_exit(void *args) runloop_state.rarch_block_config_read = false; #endif - retroarch_msg_queue_deinit(); + runloop_msg_queue_deinit(&runloop_state); driver_uninit(p_rarch, DRIVERS_CMD_ALL); retro_main_log_file_deinit(); @@ -15469,7 +15466,7 @@ int rarch_main(int argc, char *argv[], void *data) input_config_set_device(i, RETRO_DEVICE_JOYPAD); } - retroarch_msg_queue_init(); + runloop_msg_queue_init(&runloop_state); if (p_rarch->current_frontend_ctx) { @@ -15583,439 +15580,6 @@ int main(int argc, char *argv[]) #endif /* CORE OPTIONS */ -static const char *core_option_manager_parse_value_label( - const char *value, const char *value_label) -{ - /* 'value_label' may be NULL */ - const char *label = string_is_empty(value_label) ? - value : value_label; - - if (string_is_empty(label)) - return NULL; - - /* Any label starting with a digit (or +/-) - * cannot be a boolean string, and requires - * no further processing */ - if (ISDIGIT((unsigned char)*label) || - (*label == '+') || - (*label == '-')) - return label; - - /* Core devs have a habit of using arbitrary - * strings to label boolean values (i.e. enabled, - * Enabled, on, On, ON, true, True, TRUE, disabled, - * Disabled, off, Off, OFF, false, False, FALSE). - * These should all be converted to standard ON/OFF - * strings - * > Note: We require some duplication here - * (e.g. MENU_ENUM_LABEL_ENABLED *and* - * MENU_ENUM_LABEL_VALUE_ENABLED) in order - * to match both localised and non-localised - * strings. This function is not performance - * critical, so these extra comparisons do - * no harm */ - if (string_is_equal_noncase(label, msg_hash_to_str(MENU_ENUM_LABEL_ENABLED)) || - string_is_equal_noncase(label, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ENABLED)) || - string_is_equal_noncase(label, "enable") || - string_is_equal_noncase(label, "on") || - string_is_equal_noncase(label, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ON)) || - string_is_equal_noncase(label, "true") || - string_is_equal_noncase(label, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_TRUE))) - label = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ON); - else if (string_is_equal_noncase(label, msg_hash_to_str(MENU_ENUM_LABEL_DISABLED)) || - string_is_equal_noncase(label, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DISABLED)) || - string_is_equal_noncase(label, "disable") || - string_is_equal_noncase(label, "off") || - string_is_equal_noncase(label, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF)) || - string_is_equal_noncase(label, "false") || - string_is_equal_noncase(label, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_FALSE))) - label = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF); - - return label; -} - -static bool core_option_manager_parse_variable( - core_option_manager_t *opt, size_t idx, - const struct retro_variable *var, - config_file_t *config_src) -{ - size_t i; - union string_list_elem_attr attr; - const char *val_start = NULL; - char *value = NULL; - char *desc_end = NULL; - struct core_option *option = (struct core_option*)&opt->opts[idx]; - struct config_entry_list - *entry = NULL; - - /* All options are visible by default */ - option->visible = true; - - if (!string_is_empty(var->key)) - option->key = strdup(var->key); - if (!string_is_empty(var->value)) - value = strdup(var->value); - - if (!string_is_empty(value)) - desc_end = strstr(value, "; "); - - if (!desc_end) - goto error; - - *desc_end = '\0'; - - if (!string_is_empty(value)) - option->desc = strdup(value); - - val_start = desc_end + 2; - option->vals = string_split(val_start, "|"); - - if (!option->vals) - goto error; - - /* Legacy core option interface has no concept - * of value labels - * > Use actual values for display purposes */ - attr.i = 0; - option->val_labels = string_list_new(); - - if (!option->val_labels) - goto error; - - /* > Loop over values and 'extract' labels */ - for (i = 0; i < option->vals->size; i++) - { - const char *value = option->vals->elems[i].data; - const char *value_label = core_option_manager_parse_value_label( - value, NULL); - - /* Redundant safely check... */ - value_label = string_is_empty(value_label) ? - value : value_label; - - /* Append value label string */ - string_list_append(option->val_labels, value_label, attr); - } - - /* Legacy core option interface always uses first - * defined value as the default */ - option->default_index = 0; - option->index = 0; - - if (config_src) - entry = config_get_entry(config_src, option->key); - else - entry = config_get_entry(opt->conf, option->key); - - /* Set current config value */ - if (entry && !string_is_empty(entry->value)) - { - for (i = 0; i < option->vals->size; i++) - { - if (string_is_equal(option->vals->elems[i].data, entry->value)) - { - option->index = i; - break; - } - } - } - - free(value); - - return true; - -error: - free(value); - return false; -} - -static bool core_option_manager_parse_option( - core_option_manager_t *opt, size_t idx, - const struct retro_core_option_definition *option_def, - config_file_t *config_src) -{ - size_t i; - union string_list_elem_attr attr; - struct config_entry_list - *entry = NULL; - size_t num_vals = 0; - struct core_option *option = (struct core_option*)&opt->opts[idx]; - const struct retro_core_option_value - *values = option_def->values; - - /* All options are visible by default */ - option->visible = true; - - if (!string_is_empty(option_def->key)) - option->key = strdup(option_def->key); - - if (!string_is_empty(option_def->desc)) - option->desc = strdup(option_def->desc); - - if (!string_is_empty(option_def->info)) - option->info = strdup(option_def->info); - - /* Get number of values */ - for (;;) - { - if (string_is_empty(values[num_vals].value)) - break; - num_vals++; - } - - if (num_vals < 1) - return false; - - /* Initialise string lists */ - attr.i = 0; - option->vals = string_list_new(); - option->val_labels = string_list_new(); - - if (!option->vals || !option->val_labels) - return false; - - /* Initialise default value */ - option->default_index = 0; - option->index = 0; - - /* Extract value/label pairs */ - for (i = 0; i < num_vals; i++) - { - const char *value = values[i].value; - const char *value_label = values[i].label; - - /* Append value string - * > We know that 'value' is always valid */ - string_list_append(option->vals, value, attr); - - /* Value label requires additional processing */ - value_label = core_option_manager_parse_value_label( - value, value_label); - - /* > Redundant safely check... */ - value_label = string_is_empty(value_label) ? - value : value_label; - - /* Append value label string */ - string_list_append(option->val_labels, value_label, attr); - - /* Check whether this value is the default setting */ - if (!string_is_empty(option_def->default_value)) - { - if (string_is_equal(option_def->default_value, value)) - { - option->default_index = i; - option->index = i; - } - } - } - - if (config_src) - entry = config_get_entry(config_src, option->key); - else - entry = config_get_entry(opt->conf, option->key); - - /* Set current config value */ - if (entry && !string_is_empty(entry->value)) - { - for (i = 0; i < option->vals->size; i++) - { - if (string_is_equal(option->vals->elems[i].data, entry->value)) - { - option->index = i; - break; - } - } - } - - return true; -} - -/** - * core_option_manager_free: - * @opt : options manager handle - * - * Frees core option manager handle. - **/ -static void core_option_manager_free(core_option_manager_t *opt) -{ - size_t i; - - if (!opt) - return; - - for (i = 0; i < opt->size; i++) - { - if (opt->opts[i].desc) - free(opt->opts[i].desc); - if (opt->opts[i].info) - free(opt->opts[i].info); - if (opt->opts[i].key) - free(opt->opts[i].key); - - if (opt->opts[i].vals) - string_list_free(opt->opts[i].vals); - if (opt->opts[i].val_labels) - string_list_free(opt->opts[i].val_labels); - - opt->opts[i].desc = NULL; - opt->opts[i].info = NULL; - opt->opts[i].key = NULL; - opt->opts[i].vals = NULL; - } - - if (opt->conf) - config_file_free(opt->conf); - free(opt->opts); - free(opt); -} - -/** - * core_option_manager_new_vars: - * @conf_path : Filesystem path to write core option config file to. - * @src_conf_path : Filesystem path from which to load initial config settings. - * @vars : Pointer to variable array handle. - * - * Legacy version of core_option_manager_new(). - * Creates and initializes a core manager handle. - * - * Returns: handle to new core manager handle, otherwise NULL. - **/ -static core_option_manager_t *core_option_manager_new_vars( - const char *conf_path, const char *src_conf_path, - const struct retro_variable *vars) -{ - const struct retro_variable *var; - size_t size = 0; - config_file_t *config_src = NULL; - core_option_manager_t *opt = (core_option_manager_t*) - malloc(sizeof(*opt)); - - if (!opt) - return NULL; - - opt->conf = NULL; - opt->conf_path[0] = '\0'; - opt->opts = NULL; - opt->size = 0; - opt->updated = false; - - if (!string_is_empty(conf_path)) - if (!(opt->conf = config_file_new_from_path_to_string(conf_path))) - if (!(opt->conf = config_file_new_alloc())) - goto error; - - strlcpy(opt->conf_path, conf_path, sizeof(opt->conf_path)); - - /* Load source config file, if required */ - if (!string_is_empty(src_conf_path)) - config_src = config_file_new_from_path_to_string(src_conf_path); - - for (var = vars; var->key && var->value; var++) - size++; - - if (size == 0) - goto error; - - opt->opts = (struct core_option*)calloc(size, sizeof(*opt->opts)); - if (!opt->opts) - goto error; - - opt->size = size; - size = 0; - - for (var = vars; var->key && var->value; size++, var++) - { - if (!core_option_manager_parse_variable(opt, size, var, config_src)) - goto error; - } - - if (config_src) - config_file_free(config_src); - - return opt; - -error: - if (config_src) - config_file_free(config_src); - core_option_manager_free(opt); - return NULL; -} - -/** - * core_option_manager_new: - * @conf_path : Filesystem path to write core option config file to. - * @src_conf_path : Filesystem path from which to load initial config settings. - * @option_defs : Pointer to variable array handle. - * - * Creates and initializes a core manager handle. - * - * Returns: handle to new core manager handle, otherwise NULL. - **/ -static core_option_manager_t *core_option_manager_new( - const char *conf_path, const char *src_conf_path, - const struct retro_core_option_definition *option_defs) -{ - const struct retro_core_option_definition *option_def; - size_t size = 0; - config_file_t *config_src = NULL; - core_option_manager_t *opt = (core_option_manager_t*) - malloc(sizeof(*opt)); - - if (!opt) - return NULL; - - opt->conf = NULL; - opt->conf_path[0] = '\0'; - opt->opts = NULL; - opt->size = 0; - opt->updated = false; - - if (!string_is_empty(conf_path)) - if (!(opt->conf = config_file_new_from_path_to_string(conf_path))) - if (!(opt->conf = config_file_new_alloc())) - goto error; - - strlcpy(opt->conf_path, conf_path, sizeof(opt->conf_path)); - - /* Load source config file, if required */ - if (!string_is_empty(src_conf_path)) - config_src = config_file_new_from_path_to_string(src_conf_path); - - /* Note: 'option_def->info == NULL' is valid */ - for (option_def = option_defs; - option_def->key && option_def->desc && option_def->values[0].value; - option_def++) - size++; - - if (size == 0) - goto error; - - opt->opts = (struct core_option*)calloc(size, sizeof(*opt->opts)); - if (!opt->opts) - goto error; - - opt->size = size; - size = 0; - - /* Note: 'option_def->info == NULL' is valid */ - for (option_def = option_defs; - option_def->key && option_def->desc && option_def->values[0].value; - size++, option_def++) - if (!core_option_manager_parse_option(opt, size, option_def, config_src)) - goto error; - - if (config_src) - config_file_free(config_src); - - return opt; - -error: - if (config_src) - config_file_free(config_src); - core_option_manager_free(opt); - return NULL; -} /** * core_option_manager_flush: @@ -16023,7 +15587,7 @@ error: * * Writes core option key-pair values to file. **/ -static void core_option_manager_flush( +void core_option_manager_flush( config_file_t *conf, core_option_manager_t *opt) { @@ -34143,40 +33707,6 @@ force_input_dirty: } #endif -static retro_time_t rarch_core_runtime_tick( - float slowmotion_ratio, - retro_time_t current_time) -{ - retro_time_t frame_time = - (1.0 / runloop_state.av_info.timing.fps) * 1000000; - bool runloop_slowmotion = runloop_state.slowmotion; - bool runloop_fastmotion = runloop_state.fastmotion; - - /* Account for slow motion */ - if (runloop_slowmotion) - return (retro_time_t)((double)frame_time * slowmotion_ratio); - - /* Account for fast forward */ - if (runloop_fastmotion) - { - /* Doing it this way means we miss the first frame after - * turning fast forward on, but it saves the overhead of - * having to do: - * retro_time_t current_usec = cpu_features_get_time_usec(); - * libretro_core_runtime_last = current_usec; - * every frame when fast forward is off. */ - retro_time_t current_usec = current_time; - retro_time_t potential_frame_time = current_usec - - runloop_state.libretro_core_runtime_last; - runloop_state.libretro_core_runtime_last = current_usec; - - if (potential_frame_time < frame_time) - return potential_frame_time; - } - - return frame_time; -} - static void retroarch_print_features(void) { char buf[2048]; @@ -35147,7 +34677,7 @@ static bool retroarch_parse_input_and_config( return verbosity_enabled; } -static bool retroarch_validate_per_core_options(char *s, +bool retroarch_validate_per_core_options(char *s, size_t len, bool mkdir, const char *core_name, const char *game_name) { @@ -35183,7 +34713,7 @@ static bool retroarch_validate_per_core_options(char *s, return true; } -static bool retroarch_validate_game_options( +bool retroarch_validate_game_options( char *s, size_t len, bool mkdir) { const char *core_name = runloop_state.system.info.library_name; @@ -35193,7 +34723,7 @@ static bool retroarch_validate_game_options( core_name, game_name); } -static bool retroarch_validate_folder_options( +bool retroarch_validate_folder_options( char *s, size_t len, bool mkdir) { char folder_name[PATH_MAX_LENGTH]; @@ -35557,7 +35087,7 @@ bool retroarch_main_init(int argc, char *argv[]) if (!string_is_empty(global->record.path)) command_event(CMD_EVENT_RECORD_INIT, NULL); - path_init_savefile(); + path_init_savefile(&runloop_state); command_event(CMD_EVENT_SET_PER_GAME_RESOLUTION, NULL); @@ -35873,56 +35403,6 @@ void retroarch_menu_running_finished(bool quit) #endif } -/** - * rarch_game_specific_options: - * - * Returns: true (1) if a game specific core - * options path has been found, - * otherwise false (0). - **/ -static bool rarch_game_specific_options(char **output) -{ - char game_options_path[PATH_MAX_LENGTH]; - game_options_path[0] ='\0'; - - if (!retroarch_validate_game_options( - game_options_path, - sizeof(game_options_path), false) || - !path_is_valid(game_options_path)) - return false; - - RARCH_LOG("%s %s\n", - msg_hash_to_str(MSG_GAME_SPECIFIC_CORE_OPTIONS_FOUND_AT), - game_options_path); - *output = strdup(game_options_path); - return true; -} - -/** - * rarch_folder_specific_options: - * - * Returns: true (1) if a folder specific core - * options path has been found, - * otherwise false (0). - **/ -static bool rarch_folder_specific_options(char **output) -{ - char folder_options_path[PATH_MAX_LENGTH]; - folder_options_path[0] ='\0'; - - if (!retroarch_validate_folder_options( - folder_options_path, - sizeof(folder_options_path), false) || - !path_is_valid(folder_options_path)) - return false; - - RARCH_LOG("%s %s\n", - msg_hash_to_str(MSG_FOLDER_SPECIFIC_CORE_OPTIONS_FOUND_AT), - folder_options_path); - *output = strdup(folder_options_path); - return true; -} - static void runloop_task_msg_queue_push( retro_task_t *task, const char *msg, unsigned prio, unsigned duration, @@ -35974,142 +35454,6 @@ static void runloop_task_msg_queue_push( runloop_msg_queue_push(msg, prio, duration, flush, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); } -/* Fetches core options path for current core/content - * - path: path from which options should be read - * from/saved to - * - src_path: in the event that 'path' file does not - * yet exist, provides source path from which initial - * options should be extracted - * - * NOTE: caller must ensure - * path and src_path are NULL-terminated - * */ -static void runloop_init_core_options_path( - runloop_state_t *p_runloop, - settings_t *settings, - char *path, size_t len, - char *src_path, size_t src_len) -{ - char *game_options_path = NULL; - char *folder_options_path = NULL; - bool game_specific_options = settings->bools.game_specific_options; - - /* Check whether game-specific options exist */ - if (game_specific_options && - rarch_game_specific_options(&game_options_path)) - { - /* Notify system that we have a valid core options - * override */ - path_set(RARCH_PATH_CORE_OPTIONS, game_options_path); - p_runloop->game_options_active = true; - p_runloop->folder_options_active = false; - - /* Copy options path */ - strlcpy(path, game_options_path, len); - - free(game_options_path); - } - /* Check whether folder-specific options exist */ - else if (game_specific_options && - rarch_folder_specific_options(&folder_options_path)) - { - /* Notify system that we have a valid core options - * override */ - path_set(RARCH_PATH_CORE_OPTIONS, folder_options_path); - p_runloop->game_options_active = false; - p_runloop->folder_options_active = true; - - /* Copy options path */ - strlcpy(path, folder_options_path, len); - - free(folder_options_path); - } - else - { - char global_options_path[PATH_MAX_LENGTH]; - char per_core_options_path[PATH_MAX_LENGTH]; - bool per_core_options_exist = false; - bool per_core_options = !settings->bools.global_core_options; - const char *path_core_options = settings->paths.path_core_options; - - global_options_path[0] = '\0'; - per_core_options_path[0] = '\0'; - - if (per_core_options) - { - const char *core_name = p_runloop->system.info.library_name; - /* Get core-specific options path - * > if retroarch_validate_per_core_options() returns - * false, then per-core options are disabled (due to - * unknown system errors...) */ - per_core_options = retroarch_validate_per_core_options( - per_core_options_path, sizeof(per_core_options_path), true, - core_name, core_name); - - /* If we can use per-core options, check whether an options - * file already exists */ - if (per_core_options) - per_core_options_exist = path_is_valid(per_core_options_path); - } - - /* If not using per-core options, or if a per-core options - * file does not yet exist, must fetch 'global' options path */ - if (!per_core_options || !per_core_options_exist) - { - const char *options_path = path_core_options; - - if (!string_is_empty(options_path)) - strlcpy(global_options_path, - options_path, sizeof(global_options_path)); - else if (!path_is_empty(RARCH_PATH_CONFIG)) - fill_pathname_resolve_relative( - global_options_path, path_get(RARCH_PATH_CONFIG), - FILE_PATH_CORE_OPTIONS_CONFIG, sizeof(global_options_path)); - } - - /* Allocate correct path/src_path strings */ - if (per_core_options) - { - strlcpy(path, per_core_options_path, len); - - if (!per_core_options_exist) - strlcpy(src_path, global_options_path, src_len); - } - else - strlcpy(path, global_options_path, len); - - /* Notify system that we *do not* have a valid core options - * options override */ - p_runloop->game_options_active = false; - p_runloop->folder_options_active = false; - } -} - -static core_option_manager_t *runloop_init_core_options( - runloop_state_t *p_runloop, - settings_t *settings, - const struct retro_core_option_definition *option_defs) -{ - char options_path[PATH_MAX_LENGTH]; - char src_options_path[PATH_MAX_LENGTH]; - - /* Ensure these are NULL-terminated */ - options_path[0] = '\0'; - src_options_path[0] = '\0'; - - /* Get core options file path */ - runloop_init_core_options_path( - p_runloop, - settings, - options_path, sizeof(options_path), - src_options_path, sizeof(src_options_path)); - - if (!string_is_empty(options_path)) - return core_option_manager_new( - options_path, src_options_path, option_defs); - return NULL; -} - void retroarch_init_task_queue(void) { #ifdef HAVE_THREADS @@ -36196,7 +35540,7 @@ bool rarch_ctl(enum rarch_ctl_state state, void *data) input_mapper_reset(&p_rarch->input_driver_mapper); #ifdef HAVE_THREADS - retroarch_autosave_deinit(); + runloop_autosave_deinit(&runloop_state); #endif command_event(CMD_EVENT_RECORD_DEINIT, NULL); @@ -36382,86 +35726,6 @@ bool rarch_ctl(enum rarch_ctl_state state, void *data) return true; } -static void runloop_deinit_core_options( - runloop_state_t *p_runloop, - const char *path_core_options) -{ - /* Check whether game-specific options file is being used */ - if (!string_is_empty(path_core_options)) - { - config_file_t *conf_tmp = NULL; - - /* We only need to save configuration settings for - * the current core - * > If game-specific options file exists, have - * to read it (to ensure file only gets written - * if config values change) - * > Otherwise, create a new, empty config_file_t - * object */ - if (path_is_valid(path_core_options)) - conf_tmp = config_file_new_from_path_to_string(path_core_options); - - if (!conf_tmp) - conf_tmp = config_file_new_alloc(); - - if (conf_tmp) - { - core_option_manager_flush( - conf_tmp, - p_runloop->core_options); - RARCH_LOG("[Core Options]: Saved %s-specific core options to \"%s\"\n", - p_runloop->game_options_active - ? "game" - : "folder", - path_core_options); - config_file_write(conf_tmp, path_core_options, true); - config_file_free(conf_tmp); - conf_tmp = NULL; - } - path_clear(RARCH_PATH_CORE_OPTIONS); - } - else - { - const char *path = p_runloop->core_options->conf_path; - core_option_manager_flush( - p_runloop->core_options->conf, - p_runloop->core_options); - RARCH_LOG("[Core Options]: Saved core options file to \"%s\"\n", path); - config_file_write(p_runloop->core_options->conf, path, true); - } - - p_runloop->game_options_active = false; - p_runloop->folder_options_active = false; - - if (p_runloop->core_options) - core_option_manager_free(p_runloop->core_options); - p_runloop->core_options = NULL; -} - -static core_option_manager_t *runloop_init_core_variables( - runloop_state_t *p_runloop, - settings_t *settings, - const struct retro_variable *vars) -{ - char options_path[PATH_MAX_LENGTH]; - char src_options_path[PATH_MAX_LENGTH]; - - /* Ensure these are NULL-terminated */ - options_path[0] = '\0'; - src_options_path[0] = '\0'; - - /* Get core options file path */ - runloop_init_core_options_path( - p_runloop, - settings, - options_path, sizeof(options_path), - src_options_path, sizeof(src_options_path)); - - if (!string_is_empty(options_path)) - return core_option_manager_new_vars(options_path, src_options_path, vars); - return NULL; -} - bool retroarch_is_forced_fullscreen(void) { return runloop_state.rarch_force_fullscreen; @@ -36920,13 +36184,13 @@ void runloop_msg_queue_push(const char *msg, enum message_queue_icon icon, enum message_queue_category category) { - struct rarch_state *p_rarch = &rarch_st; + struct rarch_state *p_rarch = &rarch_st; #if defined(HAVE_GFX_WIDGETS) - bool widgets_active = runloop_state.widgets_active; + bool widgets_active = runloop_state.widgets_active; #endif #ifdef HAVE_ACCESSIBILITY - settings_t *settings = p_rarch->configuration_settings; - bool accessibility_enable = settings->bools.accessibility_enable; + settings_t *settings = p_rarch->configuration_settings; + bool accessibility_enable = settings->bools.accessibility_enable; unsigned accessibility_narrator_speech_speed = settings->uints.accessibility_narrator_speech_speed; #endif @@ -37044,73 +36308,71 @@ static bool input_driver_toggle_button_combo( return true; break; case INPUT_TOGGLE_HOLD_START: - { - static rarch_timer_t timer = {0}; - - if (!BIT256_GET_PTR(p_input, RETRO_DEVICE_ID_JOYPAD_START)) { - /* timer only runs while start is held down */ - RARCH_TIMER_END(timer); - return false; + static rarch_timer_t timer = {0}; + + if (!BIT256_GET_PTR(p_input, RETRO_DEVICE_ID_JOYPAD_START)) + { + /* timer only runs while start is held down */ + RARCH_TIMER_END(timer); + return false; + } + + /* User started holding down the start button, start the timer */ + if (!timer.timer_begin) + { + uint64_t current_usec = cpu_features_get_time_usec(); + RARCH_TIMER_BEGIN_NEW_TIME_USEC(timer, + current_usec, + HOLD_BTN_DELAY_SEC * 1000000); + timer.timer_begin = true; + timer.timer_end = false; + } + + RARCH_TIMER_TICK(timer, current_time); + + if (!timer.timer_end && RARCH_TIMER_HAS_EXPIRED(timer)) + { + /* start has been held down long enough, + * stop timer and enter menu */ + RARCH_TIMER_END(timer); + return true; + } } - - /* User started holding down the start button, start the timer */ - if (!timer.timer_begin) - { - uint64_t current_usec = cpu_features_get_time_usec(); - RARCH_TIMER_BEGIN_NEW_TIME_USEC(timer, - current_usec, - HOLD_BTN_DELAY_SEC * 1000000); - timer.timer_begin = true; - timer.timer_end = false; - } - - RARCH_TIMER_TICK(timer, current_time); - - if (!timer.timer_end && RARCH_TIMER_HAS_EXPIRED(timer)) - { - /* start has been held down long enough, - * stop timer and enter menu */ - RARCH_TIMER_END(timer); - return true; - } - - return false; - } + break; case INPUT_TOGGLE_HOLD_SELECT: - { - static rarch_timer_t timer = {0}; - - if (!BIT256_GET_PTR(p_input, RETRO_DEVICE_ID_JOYPAD_SELECT)) { - /* timer only runs while select is held down */ - RARCH_TIMER_END(timer); - return false; + static rarch_timer_t timer = {0}; + + if (!BIT256_GET_PTR(p_input, RETRO_DEVICE_ID_JOYPAD_SELECT)) + { + /* timer only runs while select is held down */ + RARCH_TIMER_END(timer); + return false; + } + + /* user started holding down the select button, start the timer */ + if (!timer.timer_begin) + { + uint64_t current_usec = cpu_features_get_time_usec(); + RARCH_TIMER_BEGIN_NEW_TIME_USEC(timer, + current_usec, + HOLD_BTN_DELAY_SEC * 1000000); + timer.timer_begin = true; + timer.timer_end = false; + } + + RARCH_TIMER_TICK(timer, current_time); + + if (!timer.timer_end && RARCH_TIMER_HAS_EXPIRED(timer)) + { + /* select has been held down long enough, + * stop timer and enter menu */ + RARCH_TIMER_END(timer); + return true; + } } - - /* user started holding down the select button, start the timer */ - if (!timer.timer_begin) - { - uint64_t current_usec = cpu_features_get_time_usec(); - RARCH_TIMER_BEGIN_NEW_TIME_USEC(timer, - current_usec, - HOLD_BTN_DELAY_SEC * 1000000); - timer.timer_begin = true; - timer.timer_end = false; - } - - RARCH_TIMER_TICK(timer, current_time); - - if (!timer.timer_end && RARCH_TIMER_HAS_EXPIRED(timer)) - { - /* select has been held down long enough, - * stop timer and enter menu */ - RARCH_TIMER_END(timer); - return true; - } - - return false; - } + break; default: case INPUT_TOGGLE_NONE: break; @@ -37141,7 +36403,8 @@ static bool menu_display_libretro( core_run(); runloop_state.libretro_core_runtime_usec += - rarch_core_runtime_tick(slowmotion_ratio, current_time); + runloop_core_runtime_tick(&runloop_state, + slowmotion_ratio, current_time); runloop_state.input_driver_block_libretro_input = false; return false; @@ -38576,7 +37839,8 @@ int runloop_iterate(void) /* Increment runtime tick counter after each call to * core_run() or run_ahead() */ - runloop_state.libretro_core_runtime_usec += rarch_core_runtime_tick( + runloop_state.libretro_core_runtime_usec += runloop_core_runtime_tick( + &runloop_state, slowmotion_ratio, current_time); diff --git a/retroarch.h b/retroarch.h index 71a2718572..b64cd5eb2b 100644 --- a/retroarch.h +++ b/retroarch.h @@ -27,6 +27,10 @@ #include #include +#ifdef HAVE_THREADS +#include +#endif + #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -43,6 +47,7 @@ #include "core_type.h" #include "core.h" +#include "core_option_manager.h" #ifdef HAVE_MENU #include "menu/menu_defines.h" @@ -79,6 +84,9 @@ RETRO_BEGIN_DECLS * 3 - Late */ +#define MEASURE_FRAME_TIME_SAMPLES_COUNT (2 * 1024) +#define AUDIO_BUFFER_FREE_SAMPLES_COUNT (8 * 1024) + enum rarch_ctl_state { RARCH_CTL_NONE = 0, @@ -321,6 +329,12 @@ bool rarch_ctl(enum rarch_ctl_state state, void *data); int retroarch_get_capabilities(enum rarch_capabilities type, char *s, size_t len); +bool retroarch_validate_game_options( + char *s, size_t len, bool mkdir); + +bool retroarch_validate_folder_options( + char *s, size_t len, bool mkdir); + void retroarch_override_setting_set(enum rarch_override_setting enum_idx, void *data); void retroarch_override_setting_unset(enum rarch_override_setting enum_idx, void *data); @@ -2019,6 +2033,290 @@ typedef enum apple_view_type APPLE_VIEW_TYPE_METAL } apple_view_type_t; +typedef struct input_game_focus_state +{ + bool enabled; + bool core_requested; +} input_game_focus_state_t; + + +typedef struct runloop runloop_state_t; + +struct runloop +{ + double audio_source_ratio_original; + double audio_source_ratio_current; + struct retro_system_av_info av_info; /* double alignment */ + + retro_time_t libretro_core_runtime_last; + retro_time_t libretro_core_runtime_usec; + retro_time_t frame_limit_minimum_time; + retro_time_t frame_limit_last_time; + retro_time_t frame_time_samples[ + MEASURE_FRAME_TIME_SAMPLES_COUNT]; + + retro_usec_t frame_time_last; /* int64_t alignment */ + + uint64_t frame_time_count; + uint64_t frame_count; +#ifdef HAVE_RUNAHEAD + uint64_t last_frame_count_runahead; +#endif + + uint64_t free_audio_samples_count; + + float *audio_output_samples_buf; + float *audio_input_data; + + int16_t *audio_output_samples_conv_buf; +#ifdef HAVE_REWIND + int16_t *audio_rewind_buf; +#endif + + struct retro_audio_callback audio_callback; /* ptr alignment */ + msg_queue_t msg_queue; /* ptr alignment */ +#ifdef HAVE_THREADS + slock_t *msg_queue_lock; + slock_t *display_lock; + slock_t *context_lock; +#endif + + core_option_manager_t *core_options; + retro_keyboard_event_t key_event; /* ptr alignment */ + retro_keyboard_event_t frontend_key_event; /* ptr alignment */ + + rarch_system_info_t system; /* ptr alignment */ + struct retro_frame_time_callback frame_time; /* ptr alignment */ + struct retro_audio_buffer_status_callback audio_buffer_status; /* ptr alignment */ + + void *audio_context_audio_data; + void *audio_resampler_data; + +#ifdef HAVE_REWIND + size_t audio_rewind_ptr; + size_t audio_rewind_size; +#endif +#ifdef HAVE_RUNAHEAD + size_t runahead_save_state_size; +#endif + size_t audio_buffer_size; + size_t audio_data_ptr; + size_t audio_chunk_size; + size_t audio_chunk_nonblock_size; + size_t audio_chunk_block_size; + size_t msg_queue_size; + + unsigned pending_windowed_scale; + unsigned max_frames; + unsigned audio_latency; + unsigned free_audio_samples_buf[ + AUDIO_BUFFER_FREE_SAMPLES_COUNT]; + + struct retro_fastforwarding_override fastmotion_override; /* float alignment */ + float audio_rate_control_delta; + float audio_input_sample_rate; + float audio_volume_gain; +#ifdef HAVE_AUDIOMIXER + float audio_mixer_volume_gain; +#endif + + input_game_focus_state_t game_focus_state; /* bool alignment */ +#ifdef HAVE_GFX_WIDGETS + bool widgets_active; + bool widgets_persisting; + bool widgets_paused; + bool widgets_fast_forward; + bool widgets_rewinding; +#endif + bool audio_active; + bool audio_use_float; + bool audio_suspended; + bool audio_control; + bool audio_mute_enable; +#ifdef HAVE_AUDIOMIXER + bool audio_mixer_mute_enable; + bool audio_mixer_active; +#endif + bool missing_bios; + bool force_nonblock; + bool paused; + bool idle; + bool slowmotion; + bool fastmotion; + bool shutdown_initiated; + bool core_shutdown_initiated; + bool core_running; + bool perfcnt_enable; + bool video_active; + bool video_started_fullscreen; +#ifdef HAVE_RUNAHEAD + bool runahead_video_active; +#endif + bool game_options_active; + bool folder_options_active; + bool autosave; +#ifdef HAVE_CONFIGFILE + bool overrides_active; + bool remaps_core_active; + bool remaps_game_active; + bool remaps_content_dir_active; +#endif +#ifdef HAVE_SCREENSHOTS + bool max_frames_screenshot; + char max_frames_screenshot_path[PATH_MAX_LENGTH]; +#endif +#ifdef HAVE_NETWORKING +/* Only used before init_netplay */ + bool netplay_enabled; + bool netplay_is_client; + /* Used to avoid recursive netplay calls */ + bool in_netplay; + bool netplay_client_deferred; + bool is_mitm; +#endif + bool has_set_username; + bool rarch_error_on_init; + bool rarch_force_fullscreen; + bool has_set_core; + bool has_set_verbosity; + bool has_set_libretro; + bool has_set_libretro_directory; + bool has_set_save_path; + bool has_set_state_path; +#ifdef HAVE_PATCH + bool has_set_ups_pref; + bool has_set_bps_pref; + bool has_set_ips_pref; +#endif +#ifdef HAVE_QT + bool qt_is_inited; +#endif + bool has_set_log_to_file; + bool rarch_is_inited; + bool rarch_is_switching_display_mode; + bool rarch_is_sram_load_disabled; + bool rarch_is_sram_save_disabled; + bool rarch_use_sram; + bool rarch_ups_pref; + bool rarch_bps_pref; + bool rarch_ips_pref; +#ifdef HAVE_PATCH + bool rarch_patch_blocked; +#endif + bool video_driver_window_title_update; + + /** + * dynamic.c:dynamic_request_hw_context will try to set + * flag data when the context + * is in the middle of being rebuilt; in these cases we will save flag + * data and set this to true. + * When the context is reinit, it checks this, reads from + * deferred_flag_data and cleans it. + * + * TODO - Dirty hack, fix it better + */ + bool deferred_video_context_driver_set_flags; + bool ignore_environment_cb; + bool core_set_shared_context; + + /* Graphics driver requires RGBA byte order data (ABGR on little-endian) + * for 32-bit. + * This takes effect for overlay and shader cores that wants to load + * data into graphics driver. Kinda hackish to place it here, it is only + * used for GLES. + * TODO: Refactor this better. */ + bool video_driver_use_rgba; + + /* If set during context deinit, the driver should keep + * graphics context alive to avoid having to reset all + * context state. */ + bool video_driver_cache_context; + + /* Set to true by driver if context caching succeeded. */ + bool video_driver_cache_context_ack; + +#ifdef HAVE_ACCESSIBILITY + /* Is text-to-speech accessibility turned on? */ + bool accessibility_enabled; +#endif +#ifdef HAVE_CONFIGFILE + bool rarch_block_config_read; +#endif +#if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL) + bool cli_shader_disable; +#endif + + bool location_driver_active; + bool bluetooth_driver_active; + bool wifi_driver_active; + bool camera_driver_active; +#ifdef HAVE_VIDEO_FILTER + bool video_driver_state_out_rgb32; +#endif + bool video_driver_crt_switching_active; + bool video_driver_crt_dynamic_super_width; + bool video_driver_threaded; + +#ifdef HAVE_RUNAHEAD + bool has_variable_update; + bool runahead_save_state_size_known; + bool request_fast_savestate; + bool hard_disable_audio; + + bool input_is_dirty; +#endif + +#if defined(HAVE_NETWORKING) + bool has_set_netplay_mode; + bool has_set_netplay_ip_address; + bool has_set_netplay_ip_port; + bool has_set_netplay_stateless_mode; + bool has_set_netplay_check_frames; +#endif + + bool input_driver_keyboard_linefeed_enable; + + bool input_driver_block_hotkey; + bool input_driver_block_libretro_input; + bool input_driver_nonblock_state; + bool input_driver_grab_mouse_state; + +#ifdef HAVE_MENU + bool menu_input_dialog_keyboard_display; + /* Is the menu driver still running? */ + bool menu_driver_alive; + /* Are we binding a button inside the menu? */ + bool menu_driver_is_binding; +#endif + + bool recording_enable; + bool streaming_enable; + + bool midi_drv_input_enabled; + bool midi_drv_output_enabled; + + bool midi_drv_output_pending; + + bool main_ui_companion_is_on_foreground; + bool keyboard_mapping_blocked; +#if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL) + bool shader_presets_need_reload; +#endif +#ifdef HAVE_RUNAHEAD + bool runahead_available; + bool runahead_secondary_core_available; + bool runahead_force_input_dirty; +#endif +}; + +bool retroarch_validate_per_core_options(char *s, + size_t len, bool mkdir, + const char *core_name, const char *game_name); + +void core_option_manager_flush( + config_file_t *conf, + core_option_manager_t *opt); + RETRO_END_DECLS #endif diff --git a/retroarch_data.h b/retroarch_data.h index c7434517dc..ebba7122d1 100644 --- a/retroarch_data.h +++ b/retroarch_data.h @@ -74,12 +74,8 @@ #define BSV_MOVIE_IS_PLAYBACK_OFF() (p_rarch->bsv_movie_state_handle && !p_rarch->bsv_movie_state.movie_playback) #endif -#define MEASURE_FRAME_TIME_SAMPLES_COUNT (2 * 1024) - #define TIME_TO_FPS(last_time, new_time, frames) ((1000000.0f * (frames)) / ((new_time) - (last_time))) -#define AUDIO_BUFFER_FREE_SAMPLES_COUNT (8 * 1024) - #define MENU_SOUND_FORMATS "ogg|mod|xm|s3m|mp3|flac|wav" #define MIDI_DRIVER_BUF_SIZE 4096 @@ -1494,12 +1490,6 @@ struct input_keyboard_line bool enabled; }; -typedef struct input_game_focus_state -{ - bool enabled; - bool core_requested; -} input_game_focus_state_t; - #ifdef HAVE_RUNAHEAD typedef bool(*runahead_load_state_function)(const void*, size_t); #endif @@ -1655,275 +1645,6 @@ struct discord_state typedef struct discord_state discord_state_t; #endif -struct runloop -{ - double audio_source_ratio_original; - double audio_source_ratio_current; - struct retro_system_av_info av_info; /* double alignment */ - - retro_time_t libretro_core_runtime_last; - retro_time_t libretro_core_runtime_usec; - retro_time_t frame_limit_minimum_time; - retro_time_t frame_limit_last_time; - retro_time_t frame_time_samples[ - MEASURE_FRAME_TIME_SAMPLES_COUNT]; - - retro_usec_t frame_time_last; /* int64_t alignment */ - - uint64_t frame_time_count; - uint64_t frame_count; -#ifdef HAVE_RUNAHEAD - uint64_t last_frame_count_runahead; -#endif - - uint64_t free_audio_samples_count; - - float *audio_output_samples_buf; - float *audio_input_data; - - int16_t *audio_output_samples_conv_buf; -#ifdef HAVE_REWIND - int16_t *audio_rewind_buf; -#endif - - struct retro_audio_callback audio_callback; /* ptr alignment */ - msg_queue_t msg_queue; /* ptr alignment */ -#ifdef HAVE_THREADS - slock_t *msg_queue_lock; - slock_t *display_lock; - slock_t *context_lock; -#endif - - core_option_manager_t *core_options; - retro_keyboard_event_t key_event; /* ptr alignment */ - retro_keyboard_event_t frontend_key_event; /* ptr alignment */ - - rarch_system_info_t system; /* ptr alignment */ - struct retro_frame_time_callback frame_time; /* ptr alignment */ - struct retro_audio_buffer_status_callback audio_buffer_status; /* ptr alignment */ - - void *audio_context_audio_data; - void *audio_resampler_data; - -#ifdef HAVE_REWIND - size_t audio_rewind_ptr; - size_t audio_rewind_size; -#endif -#ifdef HAVE_RUNAHEAD - size_t runahead_save_state_size; -#endif - size_t audio_buffer_size; - size_t audio_data_ptr; - size_t audio_chunk_size; - size_t audio_chunk_nonblock_size; - size_t audio_chunk_block_size; - size_t msg_queue_size; - - unsigned pending_windowed_scale; - unsigned max_frames; - unsigned audio_latency; - unsigned free_audio_samples_buf[ - AUDIO_BUFFER_FREE_SAMPLES_COUNT]; - - struct retro_fastforwarding_override fastmotion_override; /* float alignment */ - float audio_rate_control_delta; - float audio_input_sample_rate; - float audio_volume_gain; -#ifdef HAVE_AUDIOMIXER - float audio_mixer_volume_gain; -#endif - - input_game_focus_state_t game_focus_state; /* bool alignment */ -#ifdef HAVE_GFX_WIDGETS - bool widgets_active; - bool widgets_persisting; - bool widgets_paused; - bool widgets_fast_forward; - bool widgets_rewinding; -#endif - bool audio_active; - bool audio_use_float; - bool audio_suspended; - bool audio_control; - bool audio_mute_enable; -#ifdef HAVE_AUDIOMIXER - bool audio_mixer_mute_enable; - bool audio_mixer_active; -#endif - bool missing_bios; - bool force_nonblock; - bool paused; - bool idle; - bool slowmotion; - bool fastmotion; - bool shutdown_initiated; - bool core_shutdown_initiated; - bool core_running; - bool perfcnt_enable; - bool video_active; - bool video_started_fullscreen; -#ifdef HAVE_RUNAHEAD - bool runahead_video_active; -#endif - bool game_options_active; - bool folder_options_active; - bool autosave; -#ifdef HAVE_CONFIGFILE - bool overrides_active; - bool remaps_core_active; - bool remaps_game_active; - bool remaps_content_dir_active; -#endif -#ifdef HAVE_SCREENSHOTS - bool max_frames_screenshot; - char max_frames_screenshot_path[PATH_MAX_LENGTH]; -#endif -#ifdef HAVE_NETWORKING -/* Only used before init_netplay */ - bool netplay_enabled; - bool netplay_is_client; - /* Used to avoid recursive netplay calls */ - bool in_netplay; - bool netplay_client_deferred; - bool is_mitm; -#endif - bool has_set_username; - bool rarch_error_on_init; - bool rarch_force_fullscreen; - bool has_set_core; - bool has_set_verbosity; - bool has_set_libretro; - bool has_set_libretro_directory; - bool has_set_save_path; - bool has_set_state_path; -#ifdef HAVE_PATCH - bool has_set_ups_pref; - bool has_set_bps_pref; - bool has_set_ips_pref; -#endif -#ifdef HAVE_QT - bool qt_is_inited; -#endif - bool has_set_log_to_file; - bool rarch_is_inited; - bool rarch_is_switching_display_mode; - bool rarch_is_sram_load_disabled; - bool rarch_is_sram_save_disabled; - bool rarch_use_sram; - bool rarch_ups_pref; - bool rarch_bps_pref; - bool rarch_ips_pref; -#ifdef HAVE_PATCH - bool rarch_patch_blocked; -#endif - bool video_driver_window_title_update; - - /** - * dynamic.c:dynamic_request_hw_context will try to set - * flag data when the context - * is in the middle of being rebuilt; in these cases we will save flag - * data and set this to true. - * When the context is reinit, it checks this, reads from - * deferred_flag_data and cleans it. - * - * TODO - Dirty hack, fix it better - */ - bool deferred_video_context_driver_set_flags; - bool ignore_environment_cb; - bool core_set_shared_context; - - /* Graphics driver requires RGBA byte order data (ABGR on little-endian) - * for 32-bit. - * This takes effect for overlay and shader cores that wants to load - * data into graphics driver. Kinda hackish to place it here, it is only - * used for GLES. - * TODO: Refactor this better. */ - bool video_driver_use_rgba; - - /* If set during context deinit, the driver should keep - * graphics context alive to avoid having to reset all - * context state. */ - bool video_driver_cache_context; - - /* Set to true by driver if context caching succeeded. */ - bool video_driver_cache_context_ack; - -#ifdef HAVE_ACCESSIBILITY - /* Is text-to-speech accessibility turned on? */ - bool accessibility_enabled; -#endif -#ifdef HAVE_CONFIGFILE - bool rarch_block_config_read; -#endif -#if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL) - bool cli_shader_disable; -#endif - - bool location_driver_active; - bool bluetooth_driver_active; - bool wifi_driver_active; - bool camera_driver_active; -#ifdef HAVE_VIDEO_FILTER - bool video_driver_state_out_rgb32; -#endif - bool video_driver_crt_switching_active; - bool video_driver_crt_dynamic_super_width; - bool video_driver_threaded; - -#ifdef HAVE_RUNAHEAD - bool has_variable_update; - bool runahead_save_state_size_known; - bool request_fast_savestate; - bool hard_disable_audio; - - bool input_is_dirty; -#endif - -#if defined(HAVE_NETWORKING) - bool has_set_netplay_mode; - bool has_set_netplay_ip_address; - bool has_set_netplay_ip_port; - bool has_set_netplay_stateless_mode; - bool has_set_netplay_check_frames; -#endif - - bool input_driver_keyboard_linefeed_enable; - - bool input_driver_block_hotkey; - bool input_driver_block_libretro_input; - bool input_driver_nonblock_state; - bool input_driver_grab_mouse_state; - -#ifdef HAVE_MENU - bool menu_input_dialog_keyboard_display; - /* Is the menu driver still running? */ - bool menu_driver_alive; - /* Are we binding a button inside the menu? */ - bool menu_driver_is_binding; -#endif - - bool recording_enable; - bool streaming_enable; - - bool midi_drv_input_enabled; - bool midi_drv_output_enabled; - - bool midi_drv_output_pending; - - bool main_ui_companion_is_on_foreground; - bool keyboard_mapping_blocked; -#if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL) - bool shader_presets_need_reload; -#endif -#ifdef HAVE_RUNAHEAD - bool runahead_available; - bool runahead_secondary_core_available; - bool runahead_force_input_dirty; -#endif -}; - -typedef struct runloop runloop_state_t; - struct rarch_state { videocrt_switch_t crt_switch_st; /* double alignment */ diff --git a/retroarch_fwd_decls.h b/retroarch_fwd_decls.h index 209263deba..d910f2b20d 100644 --- a/retroarch_fwd_decls.h +++ b/retroarch_fwd_decls.h @@ -44,14 +44,18 @@ static bool midi_driver_set_all_sounds_off(struct rarch_state *p_rarch); static const void *midi_driver_find_handle(int index); static bool midi_driver_flush(void); -static void runloop_deinit_core_options( +retro_time_t runloop_core_runtime_tick( + runloop_state_t *p_runloop, + float slowmotion_ratio, + retro_time_t current_time); +void runloop_deinit_core_options( runloop_state_t *p_runloop, const char *path_core_options); -static core_option_manager_t *runloop_init_core_variables( +core_option_manager_t *runloop_init_core_variables( runloop_state_t *p_runloop, settings_t *settings, const struct retro_variable *vars); -static core_option_manager_t *runloop_init_core_options( +core_option_manager_t *runloop_init_core_options( runloop_state_t *p_runloop, settings_t *settings, const struct retro_core_option_definition *option_defs); diff --git a/runloop.c b/runloop.c new file mode 100755 index 0000000000..11205c8371 --- /dev/null +++ b/runloop.c @@ -0,0 +1,771 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2011 - Daniel De Matteis + * + * 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 . + */ + +#include + +#include + +#ifdef HAVE_THREADS +#include +#endif + +#include "configuration.h" +#include "file_path_special.h" +#include "paths.h" +#include "retroarch.h" +#include "verbosity.h" + +/** + * runloop_game_specific_options: + * + * Returns: true (1) if a game specific core + * options path has been found, + * otherwise false (0). + **/ +static bool runloop_game_specific_options(char **output) +{ + char game_options_path[PATH_MAX_LENGTH]; + game_options_path[0] ='\0'; + + if (!retroarch_validate_game_options( + game_options_path, + sizeof(game_options_path), false) || + !path_is_valid(game_options_path)) + return false; + + RARCH_LOG("%s %s\n", + msg_hash_to_str(MSG_GAME_SPECIFIC_CORE_OPTIONS_FOUND_AT), + game_options_path); + *output = strdup(game_options_path); + return true; +} + + +/** + * runloop_folder_specific_options: + * + * Returns: true (1) if a folder specific core + * options path has been found, + * otherwise false (0). + **/ +static bool runloop_folder_specific_options(char **output) +{ + char folder_options_path[PATH_MAX_LENGTH]; + folder_options_path[0] ='\0'; + + if (!retroarch_validate_folder_options( + folder_options_path, + sizeof(folder_options_path), false) || + !path_is_valid(folder_options_path)) + return false; + + RARCH_LOG("%s %s\n", + msg_hash_to_str(MSG_FOLDER_SPECIFIC_CORE_OPTIONS_FOUND_AT), + folder_options_path); + *output = strdup(folder_options_path); + return true; +} + +static const char *core_option_manager_parse_value_label( + const char *value, const char *value_label) +{ + /* 'value_label' may be NULL */ + const char *label = string_is_empty(value_label) ? + value : value_label; + + if (string_is_empty(label)) + return NULL; + + /* Any label starting with a digit (or +/-) + * cannot be a boolean string, and requires + * no further processing */ + if (ISDIGIT((unsigned char)*label) || + (*label == '+') || + (*label == '-')) + return label; + + /* Core devs have a habit of using arbitrary + * strings to label boolean values (i.e. enabled, + * Enabled, on, On, ON, true, True, TRUE, disabled, + * Disabled, off, Off, OFF, false, False, FALSE). + * These should all be converted to standard ON/OFF + * strings + * > Note: We require some duplication here + * (e.g. MENU_ENUM_LABEL_ENABLED *and* + * MENU_ENUM_LABEL_VALUE_ENABLED) in order + * to match both localised and non-localised + * strings. This function is not performance + * critical, so these extra comparisons do + * no harm */ + if (string_is_equal_noncase(label, msg_hash_to_str(MENU_ENUM_LABEL_ENABLED)) || + string_is_equal_noncase(label, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ENABLED)) || + string_is_equal_noncase(label, "enable") || + string_is_equal_noncase(label, "on") || + string_is_equal_noncase(label, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ON)) || + string_is_equal_noncase(label, "true") || + string_is_equal_noncase(label, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_TRUE))) + label = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ON); + else if (string_is_equal_noncase(label, msg_hash_to_str(MENU_ENUM_LABEL_DISABLED)) || + string_is_equal_noncase(label, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DISABLED)) || + string_is_equal_noncase(label, "disable") || + string_is_equal_noncase(label, "off") || + string_is_equal_noncase(label, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF)) || + string_is_equal_noncase(label, "false") || + string_is_equal_noncase(label, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_FALSE))) + label = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF); + + return label; +} + +static bool core_option_manager_parse_variable( + core_option_manager_t *opt, size_t idx, + const struct retro_variable *var, + config_file_t *config_src) +{ + size_t i; + union string_list_elem_attr attr; + const char *val_start = NULL; + char *value = NULL; + char *desc_end = NULL; + struct core_option *option = (struct core_option*)&opt->opts[idx]; + struct config_entry_list + *entry = NULL; + + /* All options are visible by default */ + option->visible = true; + + if (!string_is_empty(var->key)) + option->key = strdup(var->key); + if (!string_is_empty(var->value)) + value = strdup(var->value); + + if (!string_is_empty(value)) + desc_end = strstr(value, "; "); + + if (!desc_end) + goto error; + + *desc_end = '\0'; + + if (!string_is_empty(value)) + option->desc = strdup(value); + + val_start = desc_end + 2; + option->vals = string_split(val_start, "|"); + + if (!option->vals) + goto error; + + /* Legacy core option interface has no concept + * of value labels + * > Use actual values for display purposes */ + attr.i = 0; + option->val_labels = string_list_new(); + + if (!option->val_labels) + goto error; + + /* > Loop over values and 'extract' labels */ + for (i = 0; i < option->vals->size; i++) + { + const char *value = option->vals->elems[i].data; + const char *value_label = core_option_manager_parse_value_label( + value, NULL); + + /* Redundant safely check... */ + value_label = string_is_empty(value_label) ? + value : value_label; + + /* Append value label string */ + string_list_append(option->val_labels, value_label, attr); + } + + /* Legacy core option interface always uses first + * defined value as the default */ + option->default_index = 0; + option->index = 0; + + if (config_src) + entry = config_get_entry(config_src, option->key); + else + entry = config_get_entry(opt->conf, option->key); + + /* Set current config value */ + if (entry && !string_is_empty(entry->value)) + { + for (i = 0; i < option->vals->size; i++) + { + if (string_is_equal(option->vals->elems[i].data, entry->value)) + { + option->index = i; + break; + } + } + } + + free(value); + + return true; + +error: + free(value); + return false; +} + + +static bool core_option_manager_parse_option( + core_option_manager_t *opt, size_t idx, + const struct retro_core_option_definition *option_def, + config_file_t *config_src) +{ + size_t i; + union string_list_elem_attr attr; + struct config_entry_list + *entry = NULL; + size_t num_vals = 0; + struct core_option *option = (struct core_option*)&opt->opts[idx]; + const struct retro_core_option_value + *values = option_def->values; + + /* All options are visible by default */ + option->visible = true; + + if (!string_is_empty(option_def->key)) + option->key = strdup(option_def->key); + + if (!string_is_empty(option_def->desc)) + option->desc = strdup(option_def->desc); + + if (!string_is_empty(option_def->info)) + option->info = strdup(option_def->info); + + /* Get number of values */ + for (;;) + { + if (string_is_empty(values[num_vals].value)) + break; + num_vals++; + } + + if (num_vals < 1) + return false; + + /* Initialise string lists */ + attr.i = 0; + option->vals = string_list_new(); + option->val_labels = string_list_new(); + + if (!option->vals || !option->val_labels) + return false; + + /* Initialise default value */ + option->default_index = 0; + option->index = 0; + + /* Extract value/label pairs */ + for (i = 0; i < num_vals; i++) + { + const char *value = values[i].value; + const char *value_label = values[i].label; + + /* Append value string + * > We know that 'value' is always valid */ + string_list_append(option->vals, value, attr); + + /* Value label requires additional processing */ + value_label = core_option_manager_parse_value_label( + value, value_label); + + /* > Redundant safely check... */ + value_label = string_is_empty(value_label) ? + value : value_label; + + /* Append value label string */ + string_list_append(option->val_labels, value_label, attr); + + /* Check whether this value is the default setting */ + if (!string_is_empty(option_def->default_value)) + { + if (string_is_equal(option_def->default_value, value)) + { + option->default_index = i; + option->index = i; + } + } + } + + if (config_src) + entry = config_get_entry(config_src, option->key); + else + entry = config_get_entry(opt->conf, option->key); + + /* Set current config value */ + if (entry && !string_is_empty(entry->value)) + { + for (i = 0; i < option->vals->size; i++) + { + if (string_is_equal(option->vals->elems[i].data, entry->value)) + { + option->index = i; + break; + } + } + } + + return true; +} + + +/** + * core_option_manager_free: + * @opt : options manager handle + * + * Frees core option manager handle. + **/ +static void core_option_manager_free(core_option_manager_t *opt) +{ + size_t i; + + if (!opt) + return; + + for (i = 0; i < opt->size; i++) + { + if (opt->opts[i].desc) + free(opt->opts[i].desc); + if (opt->opts[i].info) + free(opt->opts[i].info); + if (opt->opts[i].key) + free(opt->opts[i].key); + + if (opt->opts[i].vals) + string_list_free(opt->opts[i].vals); + if (opt->opts[i].val_labels) + string_list_free(opt->opts[i].val_labels); + + opt->opts[i].desc = NULL; + opt->opts[i].info = NULL; + opt->opts[i].key = NULL; + opt->opts[i].vals = NULL; + } + + if (opt->conf) + config_file_free(opt->conf); + free(opt->opts); + free(opt); +} + + +/** + * core_option_manager_new: + * @conf_path : Filesystem path to write core option config file to. + * @src_conf_path : Filesystem path from which to load initial config settings. + * @option_defs : Pointer to variable array handle. + * + * Creates and initializes a core manager handle. + * + * Returns: handle to new core manager handle, otherwise NULL. + **/ +static core_option_manager_t *core_option_manager_new( + const char *conf_path, const char *src_conf_path, + const struct retro_core_option_definition *option_defs) +{ + const struct retro_core_option_definition *option_def; + size_t size = 0; + config_file_t *config_src = NULL; + core_option_manager_t *opt = (core_option_manager_t*) + malloc(sizeof(*opt)); + + if (!opt) + return NULL; + + opt->conf = NULL; + opt->conf_path[0] = '\0'; + opt->opts = NULL; + opt->size = 0; + opt->updated = false; + + if (!string_is_empty(conf_path)) + if (!(opt->conf = config_file_new_from_path_to_string(conf_path))) + if (!(opt->conf = config_file_new_alloc())) + goto error; + + strlcpy(opt->conf_path, conf_path, sizeof(opt->conf_path)); + + /* Load source config file, if required */ + if (!string_is_empty(src_conf_path)) + config_src = config_file_new_from_path_to_string(src_conf_path); + + /* Note: 'option_def->info == NULL' is valid */ + for (option_def = option_defs; + option_def->key && option_def->desc && option_def->values[0].value; + option_def++) + size++; + + if (size == 0) + goto error; + + opt->opts = (struct core_option*)calloc(size, sizeof(*opt->opts)); + if (!opt->opts) + goto error; + + opt->size = size; + size = 0; + + /* Note: 'option_def->info == NULL' is valid */ + for (option_def = option_defs; + option_def->key && option_def->desc && option_def->values[0].value; + size++, option_def++) + if (!core_option_manager_parse_option(opt, size, option_def, config_src)) + goto error; + + if (config_src) + config_file_free(config_src); + + return opt; + +error: + if (config_src) + config_file_free(config_src); + core_option_manager_free(opt); + return NULL; +} + + +/** + * core_option_manager_new_vars: + * @conf_path : Filesystem path to write core option config file to. + * @src_conf_path : Filesystem path from which to load initial config settings. + * @vars : Pointer to variable array handle. + * + * Legacy version of core_option_manager_new(). + * Creates and initializes a core manager handle. + * + * Returns: handle to new core manager handle, otherwise NULL. + **/ +static core_option_manager_t *core_option_manager_new_vars( + const char *conf_path, const char *src_conf_path, + const struct retro_variable *vars) +{ + const struct retro_variable *var; + size_t size = 0; + config_file_t *config_src = NULL; + core_option_manager_t *opt = (core_option_manager_t*) + malloc(sizeof(*opt)); + + if (!opt) + return NULL; + + opt->conf = NULL; + opt->conf_path[0] = '\0'; + opt->opts = NULL; + opt->size = 0; + opt->updated = false; + + if (!string_is_empty(conf_path)) + if (!(opt->conf = config_file_new_from_path_to_string(conf_path))) + if (!(opt->conf = config_file_new_alloc())) + goto error; + + strlcpy(opt->conf_path, conf_path, sizeof(opt->conf_path)); + + /* Load source config file, if required */ + if (!string_is_empty(src_conf_path)) + config_src = config_file_new_from_path_to_string(src_conf_path); + + for (var = vars; var->key && var->value; var++) + size++; + + if (size == 0) + goto error; + + opt->opts = (struct core_option*)calloc(size, sizeof(*opt->opts)); + if (!opt->opts) + goto error; + + opt->size = size; + size = 0; + + for (var = vars; var->key && var->value; size++, var++) + { + if (!core_option_manager_parse_variable(opt, size, var, config_src)) + goto error; + } + + if (config_src) + config_file_free(config_src); + + return opt; + +error: + if (config_src) + config_file_free(config_src); + core_option_manager_free(opt); + return NULL; +} + +/* Fetches core options path for current core/content + * - path: path from which options should be read + * from/saved to + * - src_path: in the event that 'path' file does not + * yet exist, provides source path from which initial + * options should be extracted + * + * NOTE: caller must ensure + * path and src_path are NULL-terminated + * */ +void runloop_init_core_options_path( + runloop_state_t *p_runloop, + settings_t *settings, + char *path, size_t len, + char *src_path, size_t src_len) +{ + char *game_options_path = NULL; + char *folder_options_path = NULL; + bool game_specific_options = settings->bools.game_specific_options; + + /* Check whether game-specific options exist */ + if (game_specific_options && + runloop_game_specific_options(&game_options_path)) + { + /* Notify system that we have a valid core options + * override */ + path_set(RARCH_PATH_CORE_OPTIONS, game_options_path); + p_runloop->game_options_active = true; + p_runloop->folder_options_active = false; + + /* Copy options path */ + strlcpy(path, game_options_path, len); + + free(game_options_path); + } + /* Check whether folder-specific options exist */ + else if (game_specific_options && + runloop_folder_specific_options(&folder_options_path)) + { + /* Notify system that we have a valid core options + * override */ + path_set(RARCH_PATH_CORE_OPTIONS, folder_options_path); + p_runloop->game_options_active = false; + p_runloop->folder_options_active = true; + + /* Copy options path */ + strlcpy(path, folder_options_path, len); + + free(folder_options_path); + } + else + { + char global_options_path[PATH_MAX_LENGTH]; + char per_core_options_path[PATH_MAX_LENGTH]; + bool per_core_options_exist = false; + bool per_core_options = !settings->bools.global_core_options; + const char *path_core_options = settings->paths.path_core_options; + + global_options_path[0] = '\0'; + per_core_options_path[0] = '\0'; + + if (per_core_options) + { + const char *core_name = p_runloop->system.info.library_name; + /* Get core-specific options path + * > if retroarch_validate_per_core_options() returns + * false, then per-core options are disabled (due to + * unknown system errors...) */ + per_core_options = retroarch_validate_per_core_options( + per_core_options_path, sizeof(per_core_options_path), true, + core_name, core_name); + + /* If we can use per-core options, check whether an options + * file already exists */ + if (per_core_options) + per_core_options_exist = path_is_valid(per_core_options_path); + } + + /* If not using per-core options, or if a per-core options + * file does not yet exist, must fetch 'global' options path */ + if (!per_core_options || !per_core_options_exist) + { + const char *options_path = path_core_options; + + if (!string_is_empty(options_path)) + strlcpy(global_options_path, + options_path, sizeof(global_options_path)); + else if (!path_is_empty(RARCH_PATH_CONFIG)) + fill_pathname_resolve_relative( + global_options_path, path_get(RARCH_PATH_CONFIG), + FILE_PATH_CORE_OPTIONS_CONFIG, sizeof(global_options_path)); + } + + /* Allocate correct path/src_path strings */ + if (per_core_options) + { + strlcpy(path, per_core_options_path, len); + + if (!per_core_options_exist) + strlcpy(src_path, global_options_path, src_len); + } + else + strlcpy(path, global_options_path, len); + + /* Notify system that we *do not* have a valid core options + * options override */ + p_runloop->game_options_active = false; + p_runloop->folder_options_active = false; + } +} + + +core_option_manager_t *runloop_init_core_variables( + runloop_state_t *p_runloop, + settings_t *settings, + const struct retro_variable *vars) +{ + char options_path[PATH_MAX_LENGTH]; + char src_options_path[PATH_MAX_LENGTH]; + + /* Ensure these are NULL-terminated */ + options_path[0] = '\0'; + src_options_path[0] = '\0'; + + /* Get core options file path */ + runloop_init_core_options_path( + p_runloop, + settings, + options_path, sizeof(options_path), + src_options_path, sizeof(src_options_path)); + + if (!string_is_empty(options_path)) + return core_option_manager_new_vars(options_path, src_options_path, vars); + return NULL; +} + + +void runloop_deinit_core_options( + runloop_state_t *p_runloop, + const char *path_core_options) +{ + /* Check whether game-specific options file is being used */ + if (!string_is_empty(path_core_options)) + { + config_file_t *conf_tmp = NULL; + + /* We only need to save configuration settings for + * the current core + * > If game-specific options file exists, have + * to read it (to ensure file only gets written + * if config values change) + * > Otherwise, create a new, empty config_file_t + * object */ + if (path_is_valid(path_core_options)) + conf_tmp = config_file_new_from_path_to_string(path_core_options); + + if (!conf_tmp) + conf_tmp = config_file_new_alloc(); + + if (conf_tmp) + { + core_option_manager_flush( + conf_tmp, + p_runloop->core_options); + RARCH_LOG("[Core Options]: Saved %s-specific core options to \"%s\"\n", + p_runloop->game_options_active + ? "game" + : "folder", + path_core_options); + config_file_write(conf_tmp, path_core_options, true); + config_file_free(conf_tmp); + conf_tmp = NULL; + } + path_clear(RARCH_PATH_CORE_OPTIONS); + } + else + { + const char *path = p_runloop->core_options->conf_path; + core_option_manager_flush( + p_runloop->core_options->conf, + p_runloop->core_options); + RARCH_LOG("[Core Options]: Saved core options file to \"%s\"\n", path); + config_file_write(p_runloop->core_options->conf, path, true); + } + + p_runloop->game_options_active = false; + p_runloop->folder_options_active = false; + + if (p_runloop->core_options) + core_option_manager_free(p_runloop->core_options); + p_runloop->core_options = NULL; +} + + +retro_time_t runloop_core_runtime_tick( + runloop_state_t *p_runloop, + float slowmotion_ratio, + retro_time_t current_time) +{ + retro_time_t frame_time = + (1.0 / p_runloop->av_info.timing.fps) * 1000000; + bool runloop_slowmotion = p_runloop->slowmotion; + bool runloop_fastmotion = p_runloop->fastmotion; + + /* Account for slow motion */ + if (runloop_slowmotion) + return (retro_time_t)((double)frame_time * slowmotion_ratio); + + /* Account for fast forward */ + if (runloop_fastmotion) + { + /* Doing it this way means we miss the first frame after + * turning fast forward on, but it saves the overhead of + * having to do: + * retro_time_t current_usec = cpu_features_get_time_usec(); + * libretro_core_runtime_last = current_usec; + * every frame when fast forward is off. */ + retro_time_t current_usec = current_time; + retro_time_t potential_frame_time = current_usec - + p_runloop->libretro_core_runtime_last; + p_runloop->libretro_core_runtime_last = current_usec; + + if (potential_frame_time < frame_time) + return potential_frame_time; + } + + return frame_time; +} + +core_option_manager_t *runloop_init_core_options( + runloop_state_t *p_runloop, + settings_t *settings, + const struct retro_core_option_definition *option_defs) +{ + char options_path[PATH_MAX_LENGTH]; + char src_options_path[PATH_MAX_LENGTH]; + + /* Ensure these are NULL-terminated */ + options_path[0] = '\0'; + src_options_path[0] = '\0'; + + /* Get core options file path */ + runloop_init_core_options_path( + p_runloop, + settings, + options_path, sizeof(options_path), + src_options_path, sizeof(src_options_path)); + + if (!string_is_empty(options_path)) + return core_option_manager_new( + options_path, src_options_path, option_defs); + return NULL; +}