/** * libretro_get_current_core_pathname: * @name : Sanitized name of libretro core. * @size : Size of @name * * Transforms a library id to a name suitable as a pathname. **/ void libretro_get_current_core_pathname(char *name, size_t size) { size_t i; struct retro_system_info info; const char *id = msg_hash_to_str(MSG_UNKNOWN); if (size == 0) return; core_get_system_info(&info); if (info.library_name) id = info.library_name; if (!id || strlen(id) >= size) { name[0] = '\0'; return; } name[strlen(id)] = '\0'; for (i = 0; id[i] != '\0'; i++) { char c = id[i]; if (isspace((int)c) || isblank((int)c)) name[i] = '_'; else name[i] = tolower((int)c); } }
bool runloop_ctl(enum runloop_ctl_state state, void *data) { switch (state) { case RUNLOOP_CTL_SHADER_DIR_DEINIT: shader_dir_free(&runloop_shader_dir); break; case RUNLOOP_CTL_SHADER_DIR_INIT: return shader_dir_init(&runloop_shader_dir); case RUNLOOP_CTL_SYSTEM_INFO_INIT: core_get_system_info(&runloop_system.info); if (!runloop_system.info.library_name) runloop_system.info.library_name = msg_hash_to_str(MSG_UNKNOWN); if (!runloop_system.info.library_version) runloop_system.info.library_version = "v0"; video_driver_set_title_buf(); strlcpy(runloop_system.valid_extensions, runloop_system.info.valid_extensions ? runloop_system.info.valid_extensions : DEFAULT_EXT, sizeof(runloop_system.valid_extensions)); break; case RUNLOOP_CTL_GET_CORE_OPTION_SIZE: { unsigned *idx = (unsigned*)data; if (!idx) return false; *idx = core_option_manager_size(runloop_core_options); } break; case RUNLOOP_CTL_HAS_CORE_OPTIONS: return runloop_core_options; case RUNLOOP_CTL_CORE_OPTIONS_LIST_GET: { core_option_manager_t **coreopts = (core_option_manager_t**)data; if (!coreopts) return false; *coreopts = runloop_core_options; } break; case RUNLOOP_CTL_SYSTEM_INFO_GET: { rarch_system_info_t **system = (rarch_system_info_t**)data; if (!system) return false; *system = &runloop_system; } break; case RUNLOOP_CTL_SYSTEM_INFO_FREE: /* No longer valid. */ if (runloop_system.subsystem.data) free(runloop_system.subsystem.data); runloop_system.subsystem.data = NULL; runloop_system.subsystem.size = 0; if (runloop_system.ports.data) free(runloop_system.ports.data); runloop_system.ports.data = NULL; runloop_system.ports.size = 0; if (runloop_system.mmaps.descriptors) free((void *)runloop_system.mmaps.descriptors); runloop_system.mmaps.descriptors = NULL; runloop_system.mmaps.num_descriptors = 0; runloop_key_event = NULL; runloop_frontend_key_event = NULL; audio_driver_unset_callback(); memset(&runloop_system, 0, sizeof(rarch_system_info_t)); break; case RUNLOOP_CTL_SET_FRAME_TIME_LAST: runloop_frame_time_last_enable = true; break; case RUNLOOP_CTL_UNSET_FRAME_TIME_LAST: if (!runloop_ctl(RUNLOOP_CTL_IS_FRAME_TIME_LAST, NULL)) return false; runloop_frame_time_last = 0; runloop_frame_time_last_enable = false; break; case RUNLOOP_CTL_SET_OVERRIDES_ACTIVE: runloop_overrides_active = true; break; case RUNLOOP_CTL_UNSET_OVERRIDES_ACTIVE: runloop_overrides_active = false; break; case RUNLOOP_CTL_IS_OVERRIDES_ACTIVE: return runloop_overrides_active; case RUNLOOP_CTL_SET_GAME_OPTIONS_ACTIVE: runloop_game_options_active = true; break; case RUNLOOP_CTL_UNSET_GAME_OPTIONS_ACTIVE: runloop_game_options_active = false; break; case RUNLOOP_CTL_IS_GAME_OPTIONS_ACTIVE: return runloop_game_options_active; case RUNLOOP_CTL_IS_FRAME_TIME_LAST: return runloop_frame_time_last_enable; case RUNLOOP_CTL_SET_FRAME_LIMIT: runloop_set_frame_limit = true; break; case RUNLOOP_CTL_UNSET_FRAME_LIMIT: runloop_set_frame_limit = false; break; case RUNLOOP_CTL_SHOULD_SET_FRAME_LIMIT: return runloop_set_frame_limit; case RUNLOOP_CTL_GET_PERFCNT: { bool **perfcnt = (bool**)data; if (!perfcnt) return false; *perfcnt = &runloop_perfcnt_enable; } break; case RUNLOOP_CTL_SET_PERFCNT_ENABLE: runloop_perfcnt_enable = true; break; case RUNLOOP_CTL_UNSET_PERFCNT_ENABLE: runloop_perfcnt_enable = false; break; case RUNLOOP_CTL_IS_PERFCNT_ENABLE: return runloop_perfcnt_enable; case RUNLOOP_CTL_SET_NONBLOCK_FORCED: runloop_force_nonblock = true; break; case RUNLOOP_CTL_UNSET_NONBLOCK_FORCED: runloop_force_nonblock = false; break; case RUNLOOP_CTL_IS_NONBLOCK_FORCED: return runloop_force_nonblock; case RUNLOOP_CTL_SET_FRAME_TIME: { const struct retro_frame_time_callback *info = (const struct retro_frame_time_callback*)data; #ifdef HAVE_NETPLAY global_t *global = global_get_ptr(); /* retro_run() will be called in very strange and * mysterious ways, have to disable it. */ if (global->netplay.enable) return false; #endif runloop_frame_time = *info; } break; case RUNLOOP_CTL_GET_WINDOWED_SCALE: { unsigned **scale = (unsigned**)data; if (!scale) return false; *scale = (unsigned*)&runloop_pending_windowed_scale; } break; case RUNLOOP_CTL_SET_WINDOWED_SCALE: { unsigned *idx = (unsigned*)data; if (!idx) return false; runloop_pending_windowed_scale = *idx; } break; case RUNLOOP_CTL_SET_LIBRETRO_PATH: { const char *fullpath = (const char*)data; if (!fullpath) return false; config_set_active_core_path(fullpath); } break; case RUNLOOP_CTL_CLEAR_CONTENT_PATH: *runloop_fullpath = '\0'; break; case RUNLOOP_CTL_GET_CONTENT_PATH: { char **fullpath = (char**)data; if (!fullpath) return false; *fullpath = (char*)runloop_fullpath; } break; case RUNLOOP_CTL_SET_CONTENT_PATH: { const char *fullpath = (const char*)data; if (!fullpath) return false; strlcpy(runloop_fullpath, fullpath, sizeof(runloop_fullpath)); } break; case RUNLOOP_CTL_CLEAR_DEFAULT_SHADER_PRESET: *runloop_default_shader_preset = '\0'; break; case RUNLOOP_CTL_GET_DEFAULT_SHADER_PRESET: { char **preset = (char**)data; if (!preset) return false; *preset = (char*)runloop_default_shader_preset; } break; case RUNLOOP_CTL_SET_DEFAULT_SHADER_PRESET: { const char *preset = (const char*)data; if (!preset) return false; strlcpy(runloop_default_shader_preset, preset, sizeof(runloop_default_shader_preset)); } break; case RUNLOOP_CTL_FRAME_TIME_FREE: memset(&runloop_frame_time, 0, sizeof(struct retro_frame_time_callback)); runloop_frame_time_last = 0; runloop_max_frames = 0; break; case RUNLOOP_CTL_STATE_FREE: runloop_perfcnt_enable = false; runloop_idle = false; runloop_paused = false; runloop_slowmotion = false; runloop_frame_time_last_enable = false; runloop_set_frame_limit = false; runloop_overrides_active = false; runloop_ctl(RUNLOOP_CTL_FRAME_TIME_FREE, NULL); break; case RUNLOOP_CTL_GLOBAL_FREE: { global_t *global = NULL; command_event(CMD_EVENT_TEMPORARY_CONTENT_DEINIT, NULL); command_event(CMD_EVENT_SUBSYSTEM_FULLPATHS_DEINIT, NULL); command_event(CMD_EVENT_RECORD_DEINIT, NULL); command_event(CMD_EVENT_LOG_FILE_DEINIT, NULL); rarch_ctl(RARCH_CTL_UNSET_BLOCK_CONFIG_READ, NULL); runloop_ctl(RUNLOOP_CTL_CLEAR_CONTENT_PATH, NULL); runloop_overrides_active = false; core_unset_input_descriptors(); global = global_get_ptr(); memset(global, 0, sizeof(struct global)); config_free_state(); } break; case RUNLOOP_CTL_CLEAR_STATE: driver_ctl(RARCH_DRIVER_CTL_DEINIT, NULL); runloop_ctl(RUNLOOP_CTL_STATE_FREE, NULL); runloop_ctl(RUNLOOP_CTL_GLOBAL_FREE, NULL); break; case RUNLOOP_CTL_SET_MAX_FRAMES: { unsigned *ptr = (unsigned*)data; if (!ptr) return false; runloop_max_frames = *ptr; } break; case RUNLOOP_CTL_IS_IDLE: return runloop_idle; case RUNLOOP_CTL_SET_IDLE: { bool *ptr = (bool*)data; if (!ptr) return false; runloop_idle = *ptr; } break; case RUNLOOP_CTL_IS_SLOWMOTION: return runloop_slowmotion; case RUNLOOP_CTL_SET_SLOWMOTION: { bool *ptr = (bool*)data; if (!ptr) return false; runloop_slowmotion = *ptr; } break; case RUNLOOP_CTL_SET_PAUSED: { bool *ptr = (bool*)data; if (!ptr) return false; runloop_paused = *ptr; } break; case RUNLOOP_CTL_IS_PAUSED: return runloop_paused; case RUNLOOP_CTL_MSG_QUEUE_PULL: runloop_msg_queue_lock(); { const char **ret = (const char**)data; if (!ret) return false; *ret = msg_queue_pull(runloop_msg_queue); } runloop_msg_queue_unlock(); break; case RUNLOOP_CTL_MSG_QUEUE_FREE: #ifdef HAVE_THREADS slock_free(_runloop_msg_queue_lock); _runloop_msg_queue_lock = NULL; #endif break; case RUNLOOP_CTL_MSG_QUEUE_CLEAR: msg_queue_clear(runloop_msg_queue); break; case RUNLOOP_CTL_MSG_QUEUE_DEINIT: if (!runloop_msg_queue) return true; runloop_msg_queue_lock(); msg_queue_free(runloop_msg_queue); runloop_msg_queue_unlock(); runloop_ctl(RUNLOOP_CTL_MSG_QUEUE_FREE, NULL); runloop_msg_queue = NULL; break; case RUNLOOP_CTL_MSG_QUEUE_INIT: runloop_ctl(RUNLOOP_CTL_MSG_QUEUE_DEINIT, NULL); runloop_msg_queue = msg_queue_new(8); retro_assert(runloop_msg_queue); #ifdef HAVE_THREADS _runloop_msg_queue_lock = slock_new(); retro_assert(_runloop_msg_queue_lock); #endif break; case RUNLOOP_CTL_TASK_INIT: { #ifdef HAVE_THREADS settings_t *settings = config_get_ptr(); bool threaded_enable = settings->threaded_data_runloop_enable; #else bool threaded_enable = false; #endif task_queue_ctl(TASK_QUEUE_CTL_DEINIT, NULL); task_queue_ctl(TASK_QUEUE_CTL_INIT, &threaded_enable); } break; case RUNLOOP_CTL_SET_CORE_SHUTDOWN: runloop_core_shutdown_initiated = true; break; case RUNLOOP_CTL_UNSET_CORE_SHUTDOWN: runloop_core_shutdown_initiated = false; break; case RUNLOOP_CTL_IS_CORE_SHUTDOWN: return runloop_core_shutdown_initiated; case RUNLOOP_CTL_SET_SHUTDOWN: runloop_shutdown_initiated = true; break; case RUNLOOP_CTL_UNSET_SHUTDOWN: runloop_shutdown_initiated = false; break; case RUNLOOP_CTL_IS_SHUTDOWN: return runloop_shutdown_initiated; case RUNLOOP_CTL_SET_EXEC: runloop_exec = true; break; case RUNLOOP_CTL_UNSET_EXEC: runloop_exec = false; break; case RUNLOOP_CTL_IS_EXEC: return runloop_exec; case RUNLOOP_CTL_DATA_DEINIT: task_queue_ctl(TASK_QUEUE_CTL_DEINIT, NULL); break; case RUNLOOP_CTL_IS_CORE_OPTION_UPDATED: if (!runloop_core_options) return false; return core_option_manager_updated(runloop_core_options); case RUNLOOP_CTL_CORE_OPTION_PREV: { unsigned *idx = (unsigned*)data; if (!idx) return false; core_option_manager_prev(runloop_core_options, *idx); if (ui_companion_is_on_foreground()) ui_companion_driver_notify_refresh(); } break; case RUNLOOP_CTL_CORE_OPTION_NEXT: { unsigned *idx = (unsigned*)data; if (!idx) return false; core_option_manager_next(runloop_core_options, *idx); if (ui_companion_is_on_foreground()) ui_companion_driver_notify_refresh(); } break; case RUNLOOP_CTL_CORE_OPTIONS_GET: { struct retro_variable *var = (struct retro_variable*)data; if (!runloop_core_options || !var) return false; RARCH_LOG("Environ GET_VARIABLE %s:\n", var->key); core_option_manager_get(runloop_core_options, var); RARCH_LOG("\t%s\n", var->value ? var->value : msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NOT_AVAILABLE)); } break; case RUNLOOP_CTL_CORE_OPTIONS_INIT: { char *game_options_path = NULL; bool ret = false; char buf[PATH_MAX_LENGTH] = {0}; global_t *global = global_get_ptr(); settings_t *settings = config_get_ptr(); const char *options_path = settings->path.core_options; const struct retro_variable *vars = (const struct retro_variable*)data; if (string_is_empty(options_path) && !string_is_empty(global->path.config)) { fill_pathname_resolve_relative(buf, global->path.config, file_path_str(FILE_PATH_CORE_OPTIONS_CONFIG), sizeof(buf)); options_path = buf; } if (settings->game_specific_options) ret = rarch_game_specific_options(&game_options_path); if(ret) { runloop_ctl(RUNLOOP_CTL_SET_GAME_OPTIONS_ACTIVE, NULL); runloop_core_options = core_option_manager_new(game_options_path, vars); free(game_options_path); } else { runloop_ctl(RUNLOOP_CTL_UNSET_GAME_OPTIONS_ACTIVE, NULL); runloop_core_options = core_option_manager_new(options_path, vars); } } break; case RUNLOOP_CTL_CORE_OPTIONS_FREE: if (runloop_core_options) core_option_manager_free(runloop_core_options); runloop_core_options = NULL; break; case RUNLOOP_CTL_CORE_OPTIONS_DEINIT: { global_t *global = global_get_ptr(); if (!runloop_core_options) return false; /* check if game options file was just created and flush to that file instead */ if(global && !string_is_empty(global->path.core_options_path)) { core_option_manager_flush_game_specific(runloop_core_options, global->path.core_options_path); global->path.core_options_path[0] = '\0'; } else core_option_manager_flush(runloop_core_options); if (runloop_ctl(RUNLOOP_CTL_IS_GAME_OPTIONS_ACTIVE, NULL)) runloop_ctl(RUNLOOP_CTL_UNSET_GAME_OPTIONS_ACTIVE, NULL); runloop_ctl(RUNLOOP_CTL_CORE_OPTIONS_FREE, NULL); } break; case RUNLOOP_CTL_KEY_EVENT_GET: { retro_keyboard_event_t **key_event = (retro_keyboard_event_t**)data; if (!key_event) return false; *key_event = &runloop_key_event; } break; case RUNLOOP_CTL_FRONTEND_KEY_EVENT_GET: { retro_keyboard_event_t **key_event = (retro_keyboard_event_t**)data; if (!key_event) return false; *key_event = &runloop_frontend_key_event; } break; case RUNLOOP_CTL_HTTPSERVER_INIT: #if defined(HAVE_HTTPSERVER) && defined(HAVE_ZLIB) httpserver_init(8888); #endif break; case RUNLOOP_CTL_HTTPSERVER_DESTROY: #if defined(HAVE_HTTPSERVER) && defined(HAVE_ZLIB) httpserver_destroy(); #endif break; case RUNLOOP_CTL_NONE: default: break; } return true; }
/* Take frame bottom-up. */ static bool screenshot_dump( const char *name_base, const void *frame, unsigned width, unsigned height, int pitch, bool bgr24, void *userbuf, bool savestate, bool is_idle, bool is_paused, bool fullpath, bool use_thread) { char screenshot_path[PATH_MAX_LENGTH]; uint8_t *buf = NULL; settings_t *settings = config_get_ptr(); retro_task_t *task; screenshot_task_state_t *state; const char *screenshot_dir = settings->paths.directory_screenshot; struct retro_system_info system_info; screenshot_path[0] = '\0'; if (!core_get_system_info(&system_info)) return false; task = task_init(); state = (screenshot_task_state_t*)calloc(1, sizeof(*state)); state->shotname[0] = '\0'; /* If fullpath is true, name_base already contains a static path + filename to save the screenshot to. */ if (fullpath) strlcpy(state->filename, name_base, sizeof(state->filename)); else { if (string_is_empty(screenshot_dir) || settings->bools.screenshots_in_content_dir) { fill_pathname_basedir(screenshot_path, name_base, sizeof(screenshot_path)); screenshot_dir = screenshot_path; } } state->is_idle = is_idle; state->is_paused = is_paused; state->bgr24 = bgr24; state->height = height; state->width = width; state->pitch = pitch; state->frame = frame; state->userbuf = userbuf; state->silence = savestate; state->history_list_enable = settings->bools.history_list_enable; state->pixel_format_type = video_driver_get_pixel_format(); if (!fullpath) { if (savestate) snprintf(state->filename, sizeof(state->filename), "%s.png", name_base); else { if (settings->bools.auto_screenshot_filename) { const char *screenshot_name = NULL; if (path_is_empty(RARCH_PATH_CONTENT)) { if (string_is_empty(system_info.library_name)) screenshot_name = "RetroArch"; else screenshot_name = system_info.library_name; } else screenshot_name = path_basename(name_base); fill_str_dated_filename(state->shotname, screenshot_name, IMG_EXT, sizeof(state->shotname)); } else snprintf(state->shotname, sizeof(state->shotname), "%s.png", path_basename(name_base)); fill_pathname_join(state->filename, screenshot_dir, state->shotname, sizeof(state->filename)); } } #if defined(HAVE_RPNG) buf = (uint8_t*)malloc(width * height * 3); if (!buf) { if (task) free(task); free(state); return false; } state->out_buffer = buf; #endif task->type = TASK_TYPE_BLOCKING; task->state = state; task->handler = task_screenshot_handler; if (use_thread) { if (!savestate) task->title = strdup(msg_hash_to_str(MSG_TAKING_SCREENSHOT)); if (!task_queue_push(task)) { /* There is already a blocking task going on */ if (task->title) task_free_title(task); free(task); if (state->out_buffer) free(state->out_buffer); free(state); return false; } } else { if (task) free(task); return screenshot_dump_direct(state); } return true; }