/** * video_shader_resolve_relative: * @shader : Shader pass handle. * @ref_path : Relative shader path. * * Resolves relative shader path (@ref_path) into absolute * shader paths. **/ void video_shader_resolve_relative(struct video_shader *shader, const char *ref_path) { unsigned i; size_t tmp_path_size = 4096 * sizeof(char); char *tmp_path = (char*)malloc(4096 * sizeof(char)); tmp_path[0] = '\0'; for (i = 0; i < shader->passes; i++) { if (!*shader->pass[i].source.path) continue; strlcpy(tmp_path, shader->pass[i].source.path, tmp_path_size); fill_pathname_resolve_relative(shader->pass[i].source.path, ref_path, tmp_path, sizeof(shader->pass[i].source.path)); } for (i = 0; i < shader->luts; i++) { strlcpy(tmp_path, shader->lut[i].path, tmp_path_size); fill_pathname_resolve_relative(shader->lut[i].path, ref_path, tmp_path, sizeof(shader->lut[i].path)); } if (*shader->script_path) { strlcpy(tmp_path, shader->script_path, tmp_path_size); fill_pathname_resolve_relative(shader->script_path, ref_path, tmp_path, sizeof(shader->script_path)); } free(tmp_path); }
/** * video_shader_resolve_relative: * @shader : Shader pass handle. * @ref_path : Relative shader path. * * Resolves relative shader path (@ref_path) into absolute * shader paths. **/ void video_shader_resolve_relative(struct video_shader *shader, const char *ref_path) { unsigned i; char tmp_path[4096] = {0}; for (i = 0; i < shader->passes; i++) { if (!*shader->pass[i].source.path) continue; strlcpy(tmp_path, shader->pass[i].source.path, sizeof(tmp_path)); fill_pathname_resolve_relative(shader->pass[i].source.path, ref_path, tmp_path, sizeof(shader->pass[i].source.path)); } for (i = 0; i < shader->luts; i++) { strlcpy(tmp_path, shader->lut[i].path, sizeof(tmp_path)); fill_pathname_resolve_relative(shader->lut[i].path, ref_path, tmp_path, sizeof(shader->lut[i].path)); } if (*shader->script_path) { strlcpy(tmp_path, shader->script_path, sizeof(tmp_path)); fill_pathname_resolve_relative(shader->script_path, ref_path, tmp_path, sizeof(shader->script_path)); } }
static void add_sub_conf(config_file_t *conf, char *path, config_file_cb_t *cb, bool no_checks) { char real_path[PATH_MAX_LENGTH]; config_file_t *sub_conf = NULL; struct config_include_list *head = conf->includes; struct config_include_list *node = (struct config_include_list*) malloc(sizeof(*node)); if (node) { node->next = NULL; /* Add include list */ node->path = strdup(path); if (head) { while (head->next) head = head->next; head->next = node; } else conf->includes = node; } real_path[0] = '\0'; #ifdef _WIN32 if (!string_is_empty(conf->path)) fill_pathname_resolve_relative(real_path, conf->path, path, sizeof(real_path)); #else #ifndef __CELLOS_LV2__ if (*path == '~') { const char *home = getenv("HOME"); strlcpy(real_path, home ? home : "", sizeof(real_path)); strlcat(real_path, path + 1, sizeof(real_path)); } else #endif if (!string_is_empty(conf->path)) fill_pathname_resolve_relative(real_path, conf->path, path, sizeof(real_path)); #endif sub_conf = (config_file_t*) config_file_new_internal(real_path, conf->include_depth + 1, cb, no_checks); if (!sub_conf) return; /* Pilfer internal list. */ add_child_list(conf, sub_conf); config_file_free(sub_conf); }
static void menu_init_history(void *data) { rgui_handle_t *rgui = (rgui_handle_t*)data; if (rgui && rgui->history) { rom_history_free(rgui->history); rgui->history = NULL; } if (*g_extern.config_path) { char history_path[PATH_MAX]; if (*g_settings.game_history_path) strlcpy(history_path, g_settings.game_history_path, sizeof(history_path)); else { fill_pathname_resolve_relative(history_path, g_extern.config_path, ".retroarch-game-history.txt", sizeof(history_path)); } RARCH_LOG("[RGUI]: Opening history: %s.\n", history_path); rgui->history = rom_history_init(history_path, g_settings.game_history_size); } }
static int file_archive_extract_cb(const char *name, const char *valid_exts, const uint8_t *cdata, unsigned cmode, uint32_t csize, uint32_t size, uint32_t checksum, void *userdata) { const char *ext = path_get_extension(name); struct zip_extract_userdata *data = (struct zip_extract_userdata*)userdata; /* Extract first content that matches our list. */ if (ext && string_list_find_elem(data->ext, ext)) { char new_path[PATH_MAX_LENGTH] = {0}; if (data->extraction_directory) fill_pathname_join(new_path, data->extraction_directory, path_basename(name), sizeof(new_path)); else fill_pathname_resolve_relative(new_path, data->zip_path, path_basename(name), sizeof(new_path)); data->first_extracted_file_path = strdup(new_path); data->found_content = file_archive_perform_mode(new_path, valid_exts, cdata, cmode, csize, size, 0, NULL); return 0; } return 1; }
static void add_sub_conf(config_file_t *conf, char *line) { char real_path[PATH_MAX_LENGTH]; config_file_t *sub_conf = NULL; char *path = extract_value(line, false); if (!path) return; add_include_list(conf, path); real_path[0] = '\0'; #ifdef _WIN32 fill_pathname_resolve_relative(real_path, conf->path, path, sizeof(real_path)); #else #ifndef __CELLOS_LV2__ if (*path == '~') { const char *home = getenv("HOME"); strlcpy(real_path, home ? home : "", sizeof(real_path)); strlcat(real_path, path + 1, sizeof(real_path)); } else #endif fill_pathname_resolve_relative(real_path, conf->path, path, sizeof(real_path)); #endif sub_conf = (config_file_t*) config_file_new_internal(real_path, conf->include_depth + 1); if (!sub_conf) { free(path); return; } /* Pilfer internal list. */ add_child_list(conf, sub_conf); config_file_free(sub_conf); free(path); }
static int zip_extract_cb(const char *name, const char *valid_exts, const uint8_t *cdata, unsigned cmode, uint32_t csize, uint32_t size, uint32_t checksum, void *userdata) { struct zip_extract_userdata *data = (struct zip_extract_userdata*)userdata; /* Extract first content that matches our list. */ const char *ext = path_get_extension(name); if (ext && string_list_find_elem(data->ext, ext)) { char new_path[PATH_MAX_LENGTH] = {0}; if (data->extraction_directory) fill_pathname_join(new_path, data->extraction_directory, path_basename(name), sizeof(new_path)); else fill_pathname_resolve_relative(new_path, data->zip_path, path_basename(name), sizeof(new_path)); switch (cmode) { case ZLIB_MODE_UNCOMPRESSED: data->found_content = zlib_write_file(new_path, cdata, size); return false; case ZLIB_MODE_DEFLATE: { int ret = 0; zlib_file_handle_t handle = {0}; if (!zlib_inflate_data_to_file_init(&handle, cdata, csize, size)) return 0; do{ ret = zlib_inflate_data_to_file_iterate(handle.stream); }while(ret == 0); if (zlib_inflate_data_to_file(&handle, ret, new_path, valid_exts, cdata, csize, size, checksum)) { strlcpy(data->zip_path, new_path, data->zip_path_size); data->found_content = true; return 0; } return 0; } default: return 0; } } return 1; }
static bool load_shader(const char *cgp_path, unsigned i) { char path_buf[PATH_MAX]; fill_pathname_resolve_relative(path_buf, cgp_path, cg_shader->pass[i].source.cg, sizeof(path_buf)); RARCH_LOG("Loading Cg/HLSL shader: \"%s\".\n", path_buf); if (!load_program(i + 1, path_buf, true)) return false; return true; }
static bool load_shader(hlsl_shader_data_t *hlsl, void *data, const char *cgp_path, unsigned i) { char path_buf[PATH_MAX_LENGTH] = {0}; fill_pathname_resolve_relative(path_buf, cgp_path, hlsl->cg_shader->pass[i].source.path, sizeof(path_buf)); RARCH_LOG("Loading Cg/HLSL shader: \"%s\".\n", path_buf); if (!load_program(hlsl, data, i + 1, path_buf, true)) return false; return true; }
static int file_archive_extract_cb(const char *name, const char *valid_exts, const uint8_t *cdata, unsigned cmode, uint32_t csize, uint32_t size, uint32_t checksum, struct archive_extract_userdata *userdata) { const char *ext = path_get_extension(name); /* Extract first file that matches our list. */ if (ext && string_list_find_elem(userdata->ext, ext)) { char new_path[PATH_MAX_LENGTH]; char wanted_file[PATH_MAX_LENGTH]; const char *delim = NULL; new_path[0] = wanted_file[0] = '\0'; if (userdata->extraction_directory) fill_pathname_join(new_path, userdata->extraction_directory, path_basename(name), sizeof(new_path)); else fill_pathname_resolve_relative(new_path, userdata->archive_path, path_basename(name), sizeof(new_path)); userdata->first_extracted_file_path = strdup(new_path); delim = path_get_archive_delim(userdata->archive_path); if (delim) { strlcpy(wanted_file, delim + 1, sizeof(wanted_file)); if (!string_is_equal_noncase(userdata->extracted_file_path, wanted_file)) return 1; /* keep searching for the right file */ } else strlcpy(wanted_file, userdata->archive_path, sizeof(wanted_file)); if (file_archive_perform_mode(new_path, valid_exts, cdata, cmode, csize, size, 0, userdata)) userdata->found_file = true; return 0; } return 1; }
static char *xml_replace_if_file(char *content, const char *path, xmlNodePtr node, const char *src_prop) { char prop[64]; if (!xml_get_prop(prop, sizeof(prop), node, src_prop)) return content; free(content); content = NULL; char shader_path[PATH_MAX]; fill_pathname_resolve_relative(shader_path, path, (const char*)prop, sizeof(shader_path)); RARCH_LOG("Loading external source from \"%s\".\n", shader_path); if (read_file(shader_path, (void**)&content) >= 0) return content; else return NULL; }
static bool zip_extract_cb(const char *name, const uint8_t *cdata, unsigned cmode, uint32_t csize, uint32_t size, uint32_t crc32, void *userdata) { struct zip_extract_userdata *data = (struct zip_extract_userdata*)userdata; /* Extract first content that matches our list. */ const char *ext = path_get_extension(name); if (ext && string_list_find_elem(data->ext, ext)) { char new_path[PATH_MAX]; if (data->extraction_directory) fill_pathname_join(new_path, data->extraction_directory, path_basename(name), sizeof(new_path)); else fill_pathname_resolve_relative(new_path, data->zip_path, path_basename(name), sizeof(new_path)); switch (cmode) { /* Uncompressed. */ case 0: data->found_content = write_file(new_path, cdata, size); return false; /* Deflate. */ case 8: if (zlib_inflate_data_to_file(new_path, cdata, csize, size, crc32)) { strlcpy(data->zip_path, new_path, data->zip_path_size); data->found_content = true; return false; } return false; default: return false; } } return true; }
static bool hlsl_load_shader(hlsl_shader_data_t *hlsl, void *data, const char *cgp_path, unsigned i) { struct shader_program_info program_info; char path_buf[PATH_MAX_LENGTH] = {0}; program_info.combined = path_buf; program_info.is_file = true; fill_pathname_resolve_relative(path_buf, cgp_path, hlsl->cg_shader->pass[i].source.path, sizeof(path_buf)); RARCH_LOG("Loading Cg/HLSL shader: \"%s\".\n", path_buf); hlsl->d3d = (d3d_video_t*)data; if (!hlsl_compile_program(hlsl, data, i + 1, &hlsl->prg[i + 1], &program_info)) return false; return true; }
static bool zip_extract_cb(const char *name, const uint8_t *cdata, unsigned cmode, uint32_t csize, uint32_t size, uint32_t crc32, void *userdata) { struct zip_extract_userdata *data = (struct zip_extract_userdata*)userdata; // Extract first ROM that matches our list. const char *ext = path_get_extension(name); if (ext && string_list_find_elem(data->ext, ext)) { char new_path[PATH_MAX]; fill_pathname_resolve_relative(new_path, data->zip_path, path_basename(name), sizeof(new_path)); switch (cmode) { case 0: // Uncompressed data->found_rom = write_file(new_path, cdata, size); return false; case 8: // Deflate if (zlib_inflate_data_to_file(new_path, cdata, csize, size, crc32)) { strlcpy(data->zip_path, new_path, data->zip_path_size); data->found_rom = true; return false; } else return false; default: return false; } } return true; }
bool runloop_ctl(enum runloop_ctl_state state, void *data) { static rarch_dir_list_t runloop_shader_dir; static char runloop_fullpath[PATH_MAX_LENGTH]; static rarch_system_info_t runloop_system; static unsigned runloop_pending_windowed_scale; static retro_keyboard_event_t runloop_key_event = NULL; static unsigned runloop_max_frames = false; static bool runloop_frame_time_last = false; static bool runloop_set_frame_limit = false; static bool runloop_paused = false; static bool runloop_idle = false; static bool runloop_exec = false; static bool runloop_slowmotion = false; static bool runloop_shutdown_initiated = false; static bool runloop_core_shutdown_initiated = false; static bool runloop_perfcnt_enable = false; static bool runloop_overrides_active = false; static bool runloop_game_options_active = false; #ifdef HAVE_THREADS static slock_t *runloop_msg_queue_lock = NULL; #endif static core_info_t *core_info_current = NULL; static core_info_list_t *core_info_curr_list = NULL; settings_t *settings = config_get_ptr(); switch (state) { case RUNLOOP_CTL_DATA_ITERATE: rarch_task_check(); return true; case RUNLOOP_CTL_SHADER_DIR_DEINIT: shader_dir_free(&runloop_shader_dir); return true; case RUNLOOP_CTL_SHADER_DIR_INIT: return shader_dir_init(&runloop_shader_dir); case RUNLOOP_CTL_SYSTEM_INFO_INIT: core.retro_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"; #ifndef RARCH_CONSOLE strlcpy(runloop_system.title_buf, msg_hash_to_str(MSG_PROGRAM), sizeof(runloop_system.title_buf)); strlcat(runloop_system.title_buf, " : ", sizeof(runloop_system.title_buf)); #endif strlcat(runloop_system.title_buf, runloop_system.info.library_name, sizeof(runloop_system.title_buf)); strlcat(runloop_system.title_buf, " ", sizeof(runloop_system.title_buf)); strlcat(runloop_system.title_buf, runloop_system.info.library_version, sizeof(runloop_system.title_buf)); strlcpy(runloop_system.valid_extensions, runloop_system.info.valid_extensions ? runloop_system.info.valid_extensions : DEFAULT_EXT, sizeof(runloop_system.valid_extensions)); runloop_system.block_extract = runloop_system.info.block_extract; break; case RUNLOOP_CTL_GET_CORE_OPTION_SIZE: { unsigned *idx = (unsigned*)data; if (!idx) return false; *idx = core_option_size(runloop_system.core_options); } return true; case RUNLOOP_CTL_HAS_CORE_OPTIONS: return runloop_system.core_options; case RUNLOOP_CTL_CURRENT_CORE_LIST_FREE: if (core_info_curr_list) core_info_list_free(core_info_curr_list); core_info_curr_list = NULL; return true; case RUNLOOP_CTL_CURRENT_CORE_LIST_INIT: core_info_curr_list = core_info_list_new(); return true; case RUNLOOP_CTL_CURRENT_CORE_LIST_GET: { core_info_list_t **core = (core_info_list_t**)data; if (!core) return false; *core = core_info_curr_list; } return true; case RUNLOOP_CTL_CURRENT_CORE_FREE: if (core_info_current) free(core_info_current); core_info_current = NULL; return true; case RUNLOOP_CTL_CURRENT_CORE_INIT: core_info_current = (core_info_t*)calloc(1, sizeof(core_info_t)); if (!core_info_current) return false; return true; case RUNLOOP_CTL_CURRENT_CORE_GET: { core_info_t **core = (core_info_t**)data; if (!core) return false; *core = core_info_current; } return true; case RUNLOOP_CTL_SYSTEM_INFO_GET: { rarch_system_info_t **system = (rarch_system_info_t**)data; if (!system) return false; *system = &runloop_system; } return true; case RUNLOOP_CTL_SYSTEM_INFO_FREE: if (runloop_system.core_options) { core_option_flush(runloop_system.core_options); core_option_free(runloop_system.core_options); } runloop_system.core_options = NULL; /* No longer valid. */ if (runloop_system.special) free(runloop_system.special); runloop_system.special = NULL; if (runloop_system.ports) free(runloop_system.ports); runloop_system.ports = NULL; runloop_key_event = NULL; global_get_ptr()->frontend_key_event = NULL; audio_driver_unset_callback(); memset(&runloop_system, 0, sizeof(rarch_system_info_t)); break; case RUNLOOP_CTL_IS_FRAME_COUNT_END: { uint64_t *frame_count = NULL; video_driver_ctl(RARCH_DISPLAY_CTL_GET_FRAME_COUNT, &frame_count); return runloop_max_frames && (*frame_count >= runloop_max_frames); } case RUNLOOP_CTL_SET_FRAME_TIME_LAST: runloop_frame_time_last = true; break; case RUNLOOP_CTL_UNSET_FRAME_TIME_LAST: runloop_frame_time_last = 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; 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; } return true; 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_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; strlcpy(settings->libretro, fullpath, sizeof(settings->libretro)); } 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_CHECK_FOCUS: if (settings->pause_nonactive) return video_driver_ctl(RARCH_DISPLAY_CTL_IS_FOCUSED, NULL); return true; case RUNLOOP_CTL_CHECK_IDLE_STATE: { event_cmd_state_t *cmd = (event_cmd_state_t*)data; bool focused = runloop_ctl(RUNLOOP_CTL_CHECK_FOCUS, NULL); check_pause(settings, focused, runloop_cmd_triggered(cmd, RARCH_PAUSE_TOGGLE), runloop_cmd_triggered(cmd, RARCH_FRAMEADVANCE)); if (!runloop_ctl(RUNLOOP_CTL_CHECK_PAUSE_STATE, cmd) || !focused) return false; break; } case RUNLOOP_CTL_CHECK_STATE: { bool tmp = false; event_cmd_state_t *cmd = (event_cmd_state_t*)data; if (!cmd || runloop_idle) return false; if (runloop_cmd_triggered(cmd, RARCH_SCREENSHOT)) event_command(EVENT_CMD_TAKE_SCREENSHOT); if (runloop_cmd_triggered(cmd, RARCH_MUTE)) event_command(EVENT_CMD_AUDIO_MUTE_TOGGLE); if (runloop_cmd_triggered(cmd, RARCH_OSK)) { if (input_driver_ctl(RARCH_INPUT_CTL_IS_KEYBOARD_LINEFEED_ENABLED, NULL)) input_driver_ctl(RARCH_INPUT_CTL_UNSET_KEYBOARD_LINEFEED_ENABLED, NULL); else input_driver_ctl(RARCH_INPUT_CTL_SET_KEYBOARD_LINEFEED_ENABLED, NULL); } if (runloop_cmd_press(cmd, RARCH_VOLUME_UP)) event_command(EVENT_CMD_VOLUME_UP); else if (runloop_cmd_press(cmd, RARCH_VOLUME_DOWN)) event_command(EVENT_CMD_VOLUME_DOWN); #ifdef HAVE_NETPLAY tmp = runloop_cmd_triggered(cmd, RARCH_NETPLAY_FLIP); netplay_driver_ctl(RARCH_NETPLAY_CTL_FLIP_PLAYERS, &tmp); tmp = runloop_cmd_triggered(cmd, RARCH_FULLSCREEN_TOGGLE_KEY); netplay_driver_ctl(RARCH_NETPLAY_CTL_FULLSCREEN_TOGGLE, &tmp); #endif if (!runloop_ctl(RUNLOOP_CTL_CHECK_IDLE_STATE, data)) return false; check_fast_forward_button( runloop_cmd_triggered(cmd, RARCH_FAST_FORWARD_KEY), runloop_cmd_press (cmd, RARCH_FAST_FORWARD_HOLD_KEY), runloop_cmd_pressed (cmd, RARCH_FAST_FORWARD_HOLD_KEY)); check_stateslots(settings, runloop_cmd_triggered(cmd, RARCH_STATE_SLOT_PLUS), runloop_cmd_triggered(cmd, RARCH_STATE_SLOT_MINUS) ); if (runloop_cmd_triggered(cmd, RARCH_SAVE_STATE_KEY)) event_command(EVENT_CMD_SAVE_STATE); else if (runloop_cmd_triggered(cmd, RARCH_LOAD_STATE_KEY)) event_command(EVENT_CMD_LOAD_STATE); state_manager_check_rewind(runloop_cmd_press(cmd, RARCH_REWIND)); tmp = runloop_cmd_press(cmd, RARCH_SLOWMOTION); runloop_ctl(RUNLOOP_CTL_CHECK_SLOWMOTION, &tmp); if (runloop_cmd_triggered(cmd, RARCH_MOVIE_RECORD_TOGGLE)) runloop_ctl(RUNLOOP_CTL_CHECK_MOVIE, NULL); check_shader_dir(&runloop_shader_dir, runloop_cmd_triggered(cmd, RARCH_SHADER_NEXT), runloop_cmd_triggered(cmd, RARCH_SHADER_PREV)); if (runloop_cmd_triggered(cmd, RARCH_DISK_EJECT_TOGGLE)) event_command(EVENT_CMD_DISK_EJECT_TOGGLE); else if (runloop_cmd_triggered(cmd, RARCH_DISK_NEXT)) event_command(EVENT_CMD_DISK_NEXT); else if (runloop_cmd_triggered(cmd, RARCH_DISK_PREV)) event_command(EVENT_CMD_DISK_PREV); if (runloop_cmd_triggered(cmd, RARCH_RESET)) event_command(EVENT_CMD_RESET); cheat_manager_state_checks( runloop_cmd_triggered(cmd, RARCH_CHEAT_INDEX_PLUS), runloop_cmd_triggered(cmd, RARCH_CHEAT_INDEX_MINUS), runloop_cmd_triggered(cmd, RARCH_CHEAT_TOGGLE)); } break; case RUNLOOP_CTL_CHECK_PAUSE_STATE: { bool check_is_oneshot; event_cmd_state_t *cmd = (event_cmd_state_t*)data; if (!cmd) return false; check_is_oneshot = runloop_cmd_triggered(cmd, RARCH_FRAMEADVANCE) || runloop_cmd_press(cmd, RARCH_REWIND); if (!runloop_paused) return true; if (runloop_cmd_triggered(cmd, RARCH_FULLSCREEN_TOGGLE_KEY)) { event_command(EVENT_CMD_FULLSCREEN_TOGGLE); video_driver_ctl(RARCH_DISPLAY_CTL_CACHED_FRAME_RENDER, NULL); } if (!check_is_oneshot) return false; } break; case RUNLOOP_CTL_CHECK_SLOWMOTION: { bool *ptr = (bool*)data; if (!ptr) return false; runloop_slowmotion = *ptr; if (!runloop_slowmotion) return false; if (settings->video.black_frame_insertion) video_driver_ctl(RARCH_DISPLAY_CTL_CACHED_FRAME_RENDER, NULL); if (state_manager_frame_is_reversed()) runloop_msg_queue_push_new(MSG_SLOW_MOTION_REWIND, 0, 30, true); else runloop_msg_queue_push_new(MSG_SLOW_MOTION, 0, 30, true); } break; case RUNLOOP_CTL_CHECK_MOVIE: if (bsv_movie_ctl(BSV_MOVIE_CTL_PLAYBACK_ON, NULL)) return runloop_ctl(RUNLOOP_CTL_CHECK_MOVIE_PLAYBACK, NULL); if (!bsv_movie_ctl(BSV_MOVIE_CTL_IS_INITED, NULL)) return runloop_ctl(RUNLOOP_CTL_CHECK_MOVIE_INIT, NULL); return runloop_ctl(RUNLOOP_CTL_CHECK_MOVIE_RECORD, NULL); case RUNLOOP_CTL_CHECK_MOVIE_RECORD: if (!bsv_movie_ctl(BSV_MOVIE_CTL_IS_INITED, NULL)) return false; runloop_msg_queue_push_new( MSG_MOVIE_RECORD_STOPPED, 2, 180, true); RARCH_LOG("%s\n", msg_hash_to_str(MSG_MOVIE_RECORD_STOPPED)); event_command(EVENT_CMD_BSV_MOVIE_DEINIT); break; case RUNLOOP_CTL_CHECK_MOVIE_INIT: if (bsv_movie_ctl(BSV_MOVIE_CTL_IS_INITED, NULL)) return false; { char msg[128]; char path[PATH_MAX_LENGTH]; settings->rewind_granularity = 1; if (settings->state_slot > 0) snprintf(path, sizeof(path), "%s%d", bsv_movie_get_path(), settings->state_slot); else strlcpy(path, bsv_movie_get_path(), sizeof(path)); strlcat(path, ".bsv", sizeof(path)); snprintf(msg, sizeof(msg), "%s \"%s\".", msg_hash_to_str(MSG_STARTING_MOVIE_RECORD_TO), path); bsv_movie_init_handle(path, RARCH_MOVIE_RECORD); if (!bsv_movie_ctl(BSV_MOVIE_CTL_IS_INITED, NULL)) return false; else if (bsv_movie_ctl(BSV_MOVIE_CTL_IS_INITED, NULL)) { runloop_msg_queue_push(msg, 1, 180, true); RARCH_LOG("%s \"%s\".\n", msg_hash_to_str(MSG_STARTING_MOVIE_RECORD_TO), path); } else { runloop_msg_queue_push_new( MSG_FAILED_TO_START_MOVIE_RECORD, 1, 180, true); RARCH_ERR("%s\n", msg_hash_to_str(MSG_FAILED_TO_START_MOVIE_RECORD)); } } break; case RUNLOOP_CTL_CHECK_MOVIE_PLAYBACK: if (!bsv_movie_ctl(BSV_MOVIE_CTL_END, NULL)) return false; runloop_msg_queue_push_new( MSG_MOVIE_PLAYBACK_ENDED, 1, 180, false); RARCH_LOG("%s\n", msg_hash_to_str(MSG_MOVIE_PLAYBACK_ENDED)); event_command(EVENT_CMD_BSV_MOVIE_DEINIT); bsv_movie_ctl(BSV_MOVIE_CTL_UNSET_END, NULL); bsv_movie_ctl(BSV_MOVIE_CTL_UNSET_PLAYBACK, NULL); break; case RUNLOOP_CTL_STATE_FREE: runloop_perfcnt_enable = false; runloop_idle = false; runloop_paused = false; runloop_slowmotion = false; runloop_frame_time_last = false; runloop_set_frame_limit = false; runloop_overrides_active = false; runloop_max_frames = 0; break; case RUNLOOP_CTL_GLOBAL_FREE: { global_t *global; event_command(EVENT_CMD_TEMPORARY_CONTENT_DEINIT); event_command(EVENT_CMD_SUBSYSTEM_FULLPATHS_DEINIT); event_command(EVENT_CMD_RECORD_DEINIT); event_command(EVENT_CMD_LOG_FILE_DEINIT); rarch_ctl(RARCH_CTL_UNSET_BLOCK_CONFIG_READ, NULL); runloop_ctl(RUNLOOP_CTL_CLEAR_CONTENT_PATH, NULL); runloop_overrides_active = false; global = global_get_ptr(); memset(global, 0, sizeof(struct global)); } 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: { bool *ptr = (bool*)data; if (!ptr) return false; *ptr = runloop_slowmotion; } break; 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_FREE: #ifdef HAVE_THREADS slock_free(runloop_msg_queue_lock); runloop_msg_queue_lock = NULL; #endif break; case RUNLOOP_CTL_MSG_QUEUE_DEINIT: if (!g_msg_queue) return true; runloop_ctl(RUNLOOP_CTL_MSG_QUEUE_LOCK, NULL); msg_queue_free(g_msg_queue); runloop_ctl(RUNLOOP_CTL_MSG_QUEUE_UNLOCK, NULL); runloop_ctl(RUNLOOP_CTL_MSG_QUEUE_FREE, NULL); g_msg_queue = NULL; break; case RUNLOOP_CTL_MSG_QUEUE_INIT: runloop_ctl(RUNLOOP_CTL_MSG_QUEUE_DEINIT, NULL); g_msg_queue = msg_queue_new(8); retro_assert(g_msg_queue); #ifdef HAVE_THREADS runloop_msg_queue_lock = slock_new(); retro_assert(runloop_msg_queue_lock); #endif break; case RUNLOOP_CTL_MSG_QUEUE_LOCK: #ifdef HAVE_THREADS slock_lock(runloop_msg_queue_lock); #endif break; case RUNLOOP_CTL_MSG_QUEUE_UNLOCK: #ifdef HAVE_THREADS slock_unlock(runloop_msg_queue_lock); #endif break; case RUNLOOP_CTL_PREPARE_DUMMY: #ifdef HAVE_MENU menu_driver_ctl(RARCH_MENU_CTL_UNSET_LOAD_NO_CONTENT, NULL); #endif runloop_data_clear_state(); runloop_ctl(RUNLOOP_CTL_CLEAR_CONTENT_PATH, NULL); rarch_ctl(RARCH_CTL_LOAD_CONTENT, NULL); 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: rarch_task_deinit(); break; case RUNLOOP_CTL_IS_CORE_OPTION_UPDATED: return runloop_system.core_options ? core_option_updated(runloop_system.core_options) : false; case RUNLOOP_CTL_CORE_OPTION_PREV: { unsigned *idx = (unsigned*)data; if (!idx) return false; core_option_prev(runloop_system.core_options, *idx); if (ui_companion_is_on_foreground()) ui_companion_driver_notify_refresh(); } return true; case RUNLOOP_CTL_CORE_OPTION_NEXT: { unsigned *idx = (unsigned*)data; if (!idx) return false; core_option_next(runloop_system.core_options, *idx); if (ui_companion_is_on_foreground()) ui_companion_driver_notify_refresh(); } return true; case RUNLOOP_CTL_CORE_OPTIONS_GET: { struct retro_variable *var = (struct retro_variable*)data; if (!runloop_system.core_options || !var) return false; RARCH_LOG("Environ GET_VARIABLE %s:\n", var->key); core_option_get(runloop_system.core_options, var); RARCH_LOG("\t%s\n", var->value ? var->value : "N/A"); } return true; case RUNLOOP_CTL_CORE_OPTIONS_INIT: { char *game_options_path = NULL; bool ret = false; const struct retro_variable *vars = (const struct retro_variable*)data; char buf[PATH_MAX_LENGTH] = {0}; global_t *global = global_get_ptr(); const char *options_path = settings->core_options_path; if (!*options_path && *global->path.config) { fill_pathname_resolve_relative(buf, global->path.config, "retroarch-core-options.cfg", 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_system.core_options = core_option_new(game_options_path, vars); free(game_options_path); } else { runloop_ctl(RUNLOOP_CTL_UNSET_GAME_OPTIONS_ACTIVE, NULL); runloop_system.core_options = core_option_new(options_path, vars); } } break; case RUNLOOP_CTL_CORE_OPTIONS_DEINIT: if (!runloop_system.core_options) return false; core_option_flush(runloop_system.core_options); core_option_free(runloop_system.core_options); runloop_system.core_options = NULL; return true; 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_NONE: default: return false; } return true; }
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; }
static config_file_t *open_default_config_file(void) { config_file_t *conf = NULL; #if defined(_WIN32) && !defined(_XBOX) char conf_path[PATH_MAX]; char app_path[PATH_MAX]; fill_pathname_application_path(app_path, sizeof(app_path)); fill_pathname_resolve_relative(conf_path, app_path, "retroarch.cfg", sizeof(conf_path)); conf = config_file_new(conf_path); if (!conf) { const char *appdata = getenv("APPDATA"); if (appdata) { fill_pathname_join(conf_path, appdata, "retroarch.cfg", sizeof(conf_path)); conf = config_file_new(conf_path); } } if (conf) strlcpy(g_extern.config_path, conf_path, sizeof(g_extern.config_path)); #elif !defined(__CELLOS_LV2__) && !defined(_XBOX) char conf_path[PATH_MAX]; const char *xdg = getenv("XDG_CONFIG_HOME"); const char *home = getenv("HOME"); // XDG_CONFIG_HOME falls back to $HOME/.config. if (xdg) snprintf(conf_path, sizeof(conf_path), "%s/retroarch/retroarch.cfg", xdg); else if (home) snprintf(conf_path, sizeof(conf_path), "%s/.config/retroarch/retroarch.cfg", home); if (xdg || home) { RARCH_LOG("Looking for config in: \"%s\".\n", conf_path); conf = config_file_new(conf_path); } // Fallback to $HOME/.retroarch.cfg. if (!conf && home) { snprintf(conf_path, sizeof(conf_path), "%s/.retroarch.cfg", home); RARCH_LOG("Looking for config in: \"%s\".\n", conf_path); conf = config_file_new(conf_path); } // Try this as a last chance ... if (!conf) { #ifndef GLOBAL_CONFIG_DIR #define GLOBAL_CONFIG_DIR "/etc" #endif fill_pathname_join(conf_path, GLOBAL_CONFIG_DIR, "retroarch.cfg", sizeof(conf_path)); RARCH_LOG("Looking for config in: \"%s\".\n", conf_path); conf = config_file_new(conf_path); } if (conf) strlcpy(g_extern.config_path, conf_path, sizeof(g_extern.config_path)); #endif return conf; }
static config_file_t *open_default_config_file(void) { config_file_t *conf = NULL; #if defined(_WIN32) && !defined(_XBOX) char conf_path[PATH_MAX]; char app_path[PATH_MAX]; fill_pathname_application_path(app_path, sizeof(app_path)); fill_pathname_resolve_relative(conf_path, app_path, "retroarch.cfg", sizeof(conf_path)); conf = config_file_new(conf_path); if (!conf) { const char *appdata = getenv("APPDATA"); if (appdata) { fill_pathname_join(conf_path, appdata, "retroarch.cfg", sizeof(conf_path)); conf = config_file_new(conf_path); } } // Try to create a new config file. if (!conf) { conf = config_file_new(NULL); bool saved = false; if (conf) // Since this is a clean config file, we can safely use config_save_on_exit. { fill_pathname_resolve_relative(conf_path, app_path, "retroarch.cfg", sizeof(conf_path)); config_set_bool(conf, "config_save_on_exit", true); saved = config_file_write(conf, conf_path); } if (saved) RARCH_WARN("Created new config file in: \"%s\".\n", conf_path); // WARN here to make sure user has a good chance of seeing it. else { RARCH_ERR("Failed to create new config file in: \"%s\".\n", conf_path); config_file_free(conf); conf = NULL; } } if (conf) strlcpy(g_extern.config_path, conf_path, sizeof(g_extern.config_path)); #elif !defined(__CELLOS_LV2__) && !defined(_XBOX) char conf_path[PATH_MAX]; const char *xdg = getenv("XDG_CONFIG_HOME"); const char *home = getenv("HOME"); // XDG_CONFIG_HOME falls back to $HOME/.config. if (xdg) fill_pathname_join(conf_path, xdg, "retroarch/retroarch.cfg", sizeof(conf_path)); else if (home) fill_pathname_join(conf_path, home, ".config/retroarch/retroarch.cfg", sizeof(conf_path)); if (xdg || home) { RARCH_LOG("Looking for config in: \"%s\".\n", conf_path); conf = config_file_new(conf_path); } // Fallback to $HOME/.retroarch.cfg. if (!conf && home) { fill_pathname_join(conf_path, home, ".retroarch.cfg", sizeof(conf_path)); RARCH_LOG("Looking for config in: \"%s\".\n", conf_path); conf = config_file_new(conf_path); } // Try to create a new config file. if (!conf && (home || xdg)) { // XDG_CONFIG_HOME falls back to $HOME/.config. if (xdg) fill_pathname_join(conf_path, xdg, "retroarch/retroarch.cfg", sizeof(conf_path)); else if (home) fill_pathname_join(conf_path, home, ".config/retroarch/retroarch.cfg", sizeof(conf_path)); char basedir[PATH_MAX]; fill_pathname_basedir(basedir, conf_path, sizeof(basedir)); if (path_mkdir(basedir)) { #ifndef GLOBAL_CONFIG_DIR #define GLOBAL_CONFIG_DIR "/etc" #endif char skeleton_conf[PATH_MAX]; fill_pathname_join(skeleton_conf, GLOBAL_CONFIG_DIR, "retroarch.cfg", sizeof(skeleton_conf)); conf = config_file_new(skeleton_conf); if (conf) RARCH_WARN("Using skeleton config \"%s\" as base for a new config file.\n", skeleton_conf); else conf = config_file_new(NULL); bool saved = false; if (conf) { config_set_bool(conf, "config_save_on_exit", true); // Since this is a clean config file, we can safely use config_save_on_exit. saved = config_file_write(conf, conf_path); } if (saved) RARCH_WARN("Created new config file in: \"%s\".\n", conf_path); // WARN here to make sure user has a good chance of seeing it. else { RARCH_ERR("Failed to create new config file in: \"%s\".\n", conf_path); config_file_free(conf); conf = NULL; } } } if (conf) strlcpy(g_extern.config_path, conf_path, sizeof(g_extern.config_path)); #endif return conf; }
static bool environment_cb(unsigned cmd, void *data) { switch (cmd) { case RETRO_ENVIRONMENT_GET_OVERSCAN: *(bool*)data = !g_settings.video.crop_overscan; RARCH_LOG("Environ GET_OVERSCAN: %u\n", (unsigned)!g_settings.video.crop_overscan); break; case RETRO_ENVIRONMENT_GET_CAN_DUPE: *(bool*)data = true; RARCH_LOG("Environ GET_CAN_DUPE: true\n"); break; case RETRO_ENVIRONMENT_GET_VARIABLE: { struct retro_variable *var = (struct retro_variable*)data; RARCH_LOG("Environ GET_VARIABLE %s:\n", var->key); if (g_extern.system.core_options) core_option_get(g_extern.system.core_options, var); else var->value = NULL; RARCH_LOG("\t%s\n", var->value ? var->value : "N/A"); break; } case RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE: *(bool*)data = g_extern.system.core_options ? core_option_updated(g_extern.system.core_options) : false; break; case RETRO_ENVIRONMENT_SET_VARIABLES: { RARCH_LOG("Environ SET_VARIABLES.\n"); if (g_extern.system.core_options) { core_option_flush(g_extern.system.core_options); core_option_free(g_extern.system.core_options); } const struct retro_variable *vars = (const struct retro_variable*)data; const char *options_path = g_settings.core_options_path; char buf[PATH_MAX]; if (!*options_path && *g_extern.config_path) { fill_pathname_resolve_relative(buf, g_extern.config_path, ".retroarch-core-options.cfg", sizeof(buf)); options_path = buf; } g_extern.system.core_options = core_option_new(options_path, vars); break; } case RETRO_ENVIRONMENT_SET_MESSAGE: { const struct retro_message *msg = (const struct retro_message*)data; RARCH_LOG("Environ SET_MESSAGE: %s\n", msg->msg); if (g_extern.msg_queue) { msg_queue_clear(g_extern.msg_queue); msg_queue_push(g_extern.msg_queue, msg->msg, 1, msg->frames); } break; } case RETRO_ENVIRONMENT_SET_ROTATION: { unsigned rotation = *(const unsigned*)data; RARCH_LOG("Environ SET_ROTATION: %u\n", rotation); if (!g_settings.video.allow_rotate) break; g_extern.system.rotation = rotation; if (driver.video && driver.video->set_rotation) { if (driver.video_data) video_set_rotation_func(rotation); } else return false; break; } case RETRO_ENVIRONMENT_SHUTDOWN: RARCH_LOG("Environ SHUTDOWN.\n"); g_extern.system.shutdown = true; break; case RETRO_ENVIRONMENT_SET_PERFORMANCE_LEVEL: g_extern.system.performance_level = *(const unsigned*)data; RARCH_LOG("Environ PERFORMANCE_LEVEL: %u.\n", g_extern.system.performance_level); break; case RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY: *(const char **)data = *g_settings.system_directory ? g_settings.system_directory : NULL; RARCH_LOG("Environ SYSTEM_DIRECTORY: \"%s\".\n", g_settings.system_directory); break; case RETRO_ENVIRONMENT_SET_PIXEL_FORMAT: { enum retro_pixel_format pix_fmt = *(const enum retro_pixel_format*)data; switch (pix_fmt) { case RETRO_PIXEL_FORMAT_0RGB1555: RARCH_LOG("Environ SET_PIXEL_FORMAT: 0RGB1555.\n"); break; case RETRO_PIXEL_FORMAT_RGB565: RARCH_LOG("Environ SET_PIXEL_FORMAT: RGB565.\n"); break; case RETRO_PIXEL_FORMAT_XRGB8888: RARCH_LOG("Environ SET_PIXEL_FORMAT: XRGB8888.\n"); break; default: return false; } g_extern.system.pix_fmt = pix_fmt; break; } case RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS: { memset(g_extern.system.input_desc_btn, 0, sizeof(g_extern.system.input_desc_btn)); const struct retro_input_descriptor *desc = (const struct retro_input_descriptor*)data; for (; desc->description; desc++) { if (desc->port >= MAX_PLAYERS) continue; if (desc->device != RETRO_DEVICE_JOYPAD) // Ignore all others for now. continue; if (desc->id >= RARCH_FIRST_CUSTOM_BIND) continue; g_extern.system.input_desc_btn[desc->port][desc->id] = desc->description; } static const char *libretro_btn_desc[] = { "B (bottom)", "Y (left)", "Select", "Start", "D-Pad Up", "D-Pad Down", "D-Pad Left", "D-Pad Right", "A (right)", "X (up)", "L", "R", "L2", "R2", "L3", "R3", }; RARCH_LOG("Environ SET_INPUT_DESCRIPTORS:\n"); for (unsigned p = 0; p < MAX_PLAYERS; p++) { for (unsigned id = 0; id < RARCH_FIRST_CUSTOM_BIND; id++) { const char *desc = g_extern.system.input_desc_btn[p][id]; if (desc) { RARCH_LOG("\tRetroPad, Player %u, Button \"%s\" => \"%s\"\n", p + 1, libretro_btn_desc[id], desc); } } } break; } case RETRO_ENVIRONMENT_SET_KEYBOARD_CALLBACK: { RARCH_LOG("Environ SET_KEYBOARD_CALLBACK.\n"); const struct retro_keyboard_callback *info = (const struct retro_keyboard_callback*)data; g_extern.system.key_event = info->callback; break; } case RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE: RARCH_LOG("Environ SET_DISK_CONTROL_INTERFACE.\n"); g_extern.system.disk_control = *(const struct retro_disk_control_callback*)data; break; case RETRO_ENVIRONMENT_SET_HW_RENDER: { RARCH_LOG("Environ SET_HW_RENDER.\n"); struct retro_hw_render_callback *cb = (struct retro_hw_render_callback*)data; switch (cb->context_type) { case RETRO_HW_CONTEXT_NONE: RARCH_LOG("Requesting no HW context.\n"); break; #if defined(HAVE_OPENGLES2) case RETRO_HW_CONTEXT_OPENGLES2: RARCH_LOG("Requesting OpenGLES2 context.\n"); driver.video = &video_gl; break; case RETRO_HW_CONTEXT_OPENGL: RARCH_ERR("Requesting OpenGL context, but RetroArch is compiled against OpenGLES2. Cannot use HW context.\n"); return false; #elif defined(HAVE_OPENGL) case RETRO_HW_CONTEXT_OPENGLES2: RARCH_ERR("Requesting OpenGLES2 context, but RetroArch is compiled against OpenGL. Cannot use HW context.\n"); return false; case RETRO_HW_CONTEXT_OPENGL: RARCH_LOG("Requesting OpenGL context.\n"); driver.video = &video_gl; break; #endif default: RARCH_LOG("Requesting unknown context.\n"); return false; } cb->get_current_framebuffer = driver_get_current_framebuffer; cb->get_proc_address = driver_get_proc_address; memcpy(&g_extern.system.hw_render_callback, cb, sizeof(*cb)); break; } case RETRO_ENVIRONMENT_SET_SUPPORT_NO_GAME: { bool state = *(const bool*)data; RARCH_LOG("Environ SET_SUPPORT_NO_GAME: %s.\n", state ? "yes" : "no"); g_extern.system.no_game = state; break; } case RETRO_ENVIRONMENT_GET_LIBRETRO_PATH: { const char **path = (const char**)data; #ifdef HAVE_DYNAMIC *path = g_settings.libretro; #else *path = NULL; #endif break; } default: RARCH_LOG("Environ UNSUPPORTED (#%u).\n", cmd); return false; } return true; }
void menu_init(void) { rgui = rgui_init(); if (rgui == NULL) { RARCH_ERR("Could not initialize menu.\n"); rarch_fail(1, "menu_init()"); } rgui->trigger_state = 0; rgui->old_input_state = 0; rgui->do_held = false; rgui->frame_buf_show = true; rgui->current_pad = 0; #ifdef HAVE_DYNAMIC if (path_is_directory(g_settings.libretro)) strlcpy(rgui->libretro_dir, g_settings.libretro, sizeof(rgui->libretro_dir)); else if (*g_settings.libretro) { fill_pathname_basedir(rgui->libretro_dir, g_settings.libretro, sizeof(rgui->libretro_dir)); libretro_get_system_info(g_settings.libretro, &rgui->info, NULL); } #else retro_get_system_info(&rgui->info); #endif #ifdef HAVE_FILEBROWSER if (!(strlen(g_settings.rgui_browser_directory) > 0)) strlcpy(g_settings.rgui_browser_directory, default_paths.filebrowser_startup_dir, sizeof(g_settings.rgui_browser_directory)); rgui->browser = (filebrowser_t*)calloc(1, sizeof(*(rgui->browser))); if (rgui->browser == NULL) { RARCH_ERR("Could not initialize filebrowser.\n"); rarch_fail(1, "menu_init()"); } strlcpy(rgui->browser->current_dir.extensions, rgui->info.valid_extensions, sizeof(rgui->browser->current_dir.extensions)); // Look for zips to extract as well. if (*rgui->info.valid_extensions) { strlcat(rgui->browser->current_dir.extensions, "|zip", sizeof(rgui->browser->current_dir.extensions)); } strlcpy(rgui->browser->current_dir.root_dir, g_settings.rgui_browser_directory, sizeof(rgui->browser->current_dir.root_dir)); filebrowser_iterate(rgui->browser, FILEBROWSER_ACTION_RESET); #endif #ifdef HAVE_SHADER_MANAGER shader_manager_init(rgui); #endif if (*g_extern.config_path) { char history_path[PATH_MAX]; if (*g_settings.game_history_path) strlcpy(history_path, g_settings.game_history_path, sizeof(history_path)); else { fill_pathname_resolve_relative(history_path, g_extern.config_path, ".retroarch-game-history.txt", sizeof(history_path)); } RARCH_LOG("[RGUI]: Opening history: %s.\n", history_path); rgui->history = rom_history_init(history_path, g_settings.game_history_size); } rgui->last_time = rarch_get_time_usec(); }
static bool input_overlay_load_overlay(input_overlay_t *ol, config_file_t *conf, const char *config_path, struct overlay *overlay, unsigned index) { size_t i; char overlay_path_key[64]; char overlay_name_key[64]; char overlay_path[PATH_MAX]; char overlay_resolved_path[PATH_MAX]; snprintf(overlay_path_key, sizeof(overlay_path_key), "overlay%u_overlay", index); if (config_get_path(conf, overlay_path_key, overlay_path, sizeof(overlay_path))) { struct texture_image img = {0}; fill_pathname_resolve_relative(overlay_resolved_path, config_path, overlay_path, sizeof(overlay_resolved_path)); if (texture_image_load(&img, overlay_resolved_path)) overlay->image = img; else { RARCH_ERR("[Overlay]: Failed to load image: %s.\n", overlay_resolved_path); return false; } } snprintf(overlay_name_key, sizeof(overlay_name_key), "overlay%u_name", index); config_get_array(conf, overlay_name_key, overlay->name, sizeof(overlay->name)); // By default, we stretch the overlay out in full. overlay->x = overlay->y = 0.0f; overlay->w = overlay->h = 1.0f; char overlay_rect_key[64]; snprintf(overlay_rect_key, sizeof(overlay_rect_key), "overlay%u_rect", index); char overlay_rect[256]; if (config_get_array(conf, overlay_rect_key, overlay_rect, sizeof(overlay_rect))) { struct string_list *list = string_split(overlay_rect, ", "); if (list->size < 4) { RARCH_ERR("[Overlay]: Failed to split rect \"%s\" into at least four tokens.\n", overlay_rect); return false; } overlay->x = (float)strtod(list->elems[0].data, NULL); overlay->y = (float)strtod(list->elems[1].data, NULL); overlay->w = (float)strtod(list->elems[2].data, NULL); overlay->h = (float)strtod(list->elems[3].data, NULL); string_list_free(list); } char overlay_full_screen_key[64]; snprintf(overlay_full_screen_key, sizeof(overlay_full_screen_key), "overlay%u_full_screen", index); overlay->full_screen = false; config_get_bool(conf, overlay_full_screen_key, &overlay->full_screen); char overlay_descs_key[64]; snprintf(overlay_descs_key, sizeof(overlay_descs_key), "overlay%u_descs", index); unsigned descs = 0; if (!config_get_uint(conf, overlay_descs_key, &descs)) { RARCH_ERR("[Overlay]: Failed to read number of descs from config key: %s.\n", overlay_descs_key); return false; } overlay->descs = (struct overlay_desc*)calloc(descs, sizeof(*overlay->descs)); if (!overlay->descs) { RARCH_ERR("[Overlay]: Failed to allocate descs.\n"); return false; } overlay->size = descs; char conf_key[64]; bool normalized = false; snprintf(conf_key, sizeof(conf_key), "overlay%u_normalized", index); config_get_bool(conf, conf_key, &normalized); float alpha_mod = 1.0f; snprintf(conf_key, sizeof(conf_key), "overlay%u_alpha_mod", index); config_get_float(conf, conf_key, &alpha_mod); float range_mod = 1.0f; snprintf(conf_key, sizeof(conf_key), "overlay%u_range_mod", index); config_get_float(conf, conf_key, &range_mod); for (i = 0; i < overlay->size; i++) { if (!input_overlay_load_desc(ol, conf, &overlay->descs[i], index, i, overlay->image.width, overlay->image.height, normalized, alpha_mod, range_mod)) { RARCH_ERR("[Overlay]: Failed to load overlay descs for overlay #%u.\n", (unsigned)i); return false; } } // Precache load image array for simplicity. overlay->load_images = (struct texture_image*)calloc(1 + overlay->size, sizeof(struct texture_image)); if (!overlay->load_images) { RARCH_ERR("[Overlay]: Failed to allocate load_images.\n"); return false; } if (overlay->image.pixels) overlay->load_images[overlay->load_images_size++] = overlay->image; for (i = 0; i < overlay->size; i++) { if (overlay->descs[i].image.pixels) { overlay->descs[i].image_index = overlay->load_images_size; overlay->load_images[overlay->load_images_size++] = overlay->descs[i].image; } } // Assume for now that scaling center is in the middle. // TODO: Make this configurable. overlay->block_scale = false; overlay->center_x = overlay->x + 0.5f * overlay->w; overlay->center_y = overlay->y + 0.5f * overlay->h; return true; }
/** * rarch_environment_cb: * @cmd : Identifier of command. * @data : Pointer to data. * * Environment callback function implementation. * * Returns: true (1) if environment callback command could * be performed, otherwise false (0). **/ bool rarch_environment_cb(unsigned cmd, void *data) { unsigned p; driver_t *driver = driver_get_ptr(); settings_t *settings = config_get_ptr(); global_t *global = global_get_ptr(); rarch_system_info_t *system = rarch_system_info_get_ptr(); char buf[PATH_MAX_LENGTH]; if (ignore_environment_cb) return false; switch (cmd) { case RETRO_ENVIRONMENT_GET_OVERSCAN: *(bool*)data = !settings->video.crop_overscan; RARCH_LOG("Environ GET_OVERSCAN: %u\n", (unsigned)!settings->video.crop_overscan); break; case RETRO_ENVIRONMENT_GET_CAN_DUPE: *(bool*)data = true; RARCH_LOG("Environ GET_CAN_DUPE: true\n"); break; case RETRO_ENVIRONMENT_GET_VARIABLE: { struct retro_variable *var = (struct retro_variable*)data; RARCH_LOG("Environ GET_VARIABLE %s:\n", var->key); if (system && system->core_options) core_option_get(system->core_options, var); else var->value = NULL; RARCH_LOG("\t%s\n", var->value ? var->value : "N/A"); break; } case RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE: *(bool*)data = system->core_options ? core_option_updated(system->core_options) : false; break; case RETRO_ENVIRONMENT_SET_VARIABLES: { RARCH_LOG("Environ SET_VARIABLES.\n"); if (system && system->core_options) { core_option_flush(system->core_options); core_option_free(system->core_options); } { const struct retro_variable *vars = (const struct retro_variable*)data; char buf[PATH_MAX_LENGTH] = {0}; const char *options_path = settings->core_options_path; if (!*options_path && *global->path.config) { fill_pathname_resolve_relative(buf, global->path.config, "retroarch-core-options.cfg", sizeof(buf)); options_path = buf; } system->core_options = core_option_new(options_path, vars); } break; } case RETRO_ENVIRONMENT_SET_MESSAGE: { const struct retro_message *msg = (const struct retro_message*)data; RARCH_LOG("Environ SET_MESSAGE: %s\n", msg->msg); rarch_main_msg_queue_push(msg->msg, 1, msg->frames, true); break; } case RETRO_ENVIRONMENT_SET_ROTATION: { unsigned rotation = *(const unsigned*)data; RARCH_LOG("Environ SET_ROTATION: %u\n", rotation); if (!settings->video.allow_rotate) break; system->rotation = rotation; if (!video_driver_set_rotation(rotation)) return false; break; } case RETRO_ENVIRONMENT_SHUTDOWN: RARCH_LOG("Environ SHUTDOWN.\n"); system->shutdown = true; global->core_shutdown_initiated = true; break; case RETRO_ENVIRONMENT_SET_PERFORMANCE_LEVEL: system->performance_level = *(const unsigned*)data; RARCH_LOG("Environ PERFORMANCE_LEVEL: %u.\n", system->performance_level); break; case RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY: if (!settings->system_directory || settings->system_directory[0] == '\0') { RARCH_WARN("SYSTEM DIR is empty, assume CONTENT DIR %s\n",global->path.fullpath); fill_pathname_basedir(buf, global->path.fullpath, sizeof(buf)); } *(const char**)data = *settings->system_directory ? settings->system_directory : buf; RARCH_LOG("Environ SYSTEM_DIRECTORY: \"%s\".\n", settings->system_directory); break; case RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY: *(const char**)data = *global->dir.savefile ? global->dir.savefile : NULL; RARCH_LOG("Environ SAVE_DIRECTORY: \"%s\".\n", global->dir.savefile); break; case RETRO_ENVIRONMENT_GET_USERNAME: *(const char**)data = *settings->username ? settings->username : NULL; RARCH_LOG("Environ GET_USERNAME: \"%s\".\n", settings->username); break; case RETRO_ENVIRONMENT_GET_LANGUAGE: *(unsigned *)data = settings->user_language; RARCH_LOG("Environ GET_LANGUAGE: \"%u\".\n", settings->user_language); break; case RETRO_ENVIRONMENT_SET_PIXEL_FORMAT: { enum retro_pixel_format pix_fmt = *(const enum retro_pixel_format*)data; switch (pix_fmt) { case RETRO_PIXEL_FORMAT_0RGB1555: RARCH_LOG("Environ SET_PIXEL_FORMAT: 0RGB1555.\n"); break; case RETRO_PIXEL_FORMAT_RGB565: RARCH_LOG("Environ SET_PIXEL_FORMAT: RGB565.\n"); break; case RETRO_PIXEL_FORMAT_XRGB8888: RARCH_LOG("Environ SET_PIXEL_FORMAT: XRGB8888.\n"); break; default: return false; } video_driver_set_pixel_format(pix_fmt); break; } case RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS: { unsigned retro_id, retro_port; const struct retro_input_descriptor *desc = NULL; static const char *libretro_btn_desc[] = { "B (bottom)", "Y (left)", "Select", "Start", "D-Pad Up", "D-Pad Down", "D-Pad Left", "D-Pad Right", "A (right)", "X (up)", "L", "R", "L2", "R2", "L3", "R3", }; memset(system->input_desc_btn, 0, sizeof(system->input_desc_btn)); desc = (const struct retro_input_descriptor*)data; for (; desc->description; desc++) { retro_port = desc->port; retro_id = desc->id; if (desc->port >= MAX_USERS) continue; /* Ignore all others for now. */ if (desc->device != RETRO_DEVICE_JOYPAD && desc->device != RETRO_DEVICE_ANALOG) continue; if (desc->id >= RARCH_FIRST_CUSTOM_BIND) continue; if (desc->device == RETRO_DEVICE_ANALOG) { switch (retro_id) { case RETRO_DEVICE_ID_ANALOG_X: switch (desc->index) { case RETRO_DEVICE_INDEX_ANALOG_LEFT: system->input_desc_btn[retro_port][RARCH_ANALOG_LEFT_X_PLUS] = desc->description; system->input_desc_btn[retro_port][RARCH_ANALOG_LEFT_X_MINUS] = desc->description; break; case RETRO_DEVICE_INDEX_ANALOG_RIGHT: system->input_desc_btn[retro_port][RARCH_ANALOG_RIGHT_X_PLUS] = desc->description; system->input_desc_btn[retro_port][RARCH_ANALOG_RIGHT_X_MINUS] = desc->description; break; } break; case RETRO_DEVICE_ID_ANALOG_Y: switch (desc->index) { case RETRO_DEVICE_INDEX_ANALOG_LEFT: system->input_desc_btn[retro_port][RARCH_ANALOG_LEFT_Y_PLUS] = desc->description; system->input_desc_btn[retro_port][RARCH_ANALOG_LEFT_Y_MINUS] = desc->description; break; case RETRO_DEVICE_INDEX_ANALOG_RIGHT: system->input_desc_btn[retro_port][RARCH_ANALOG_RIGHT_Y_PLUS] = desc->description; system->input_desc_btn[retro_port][RARCH_ANALOG_RIGHT_Y_MINUS] = desc->description; break; } break; } } else system->input_desc_btn[retro_port][retro_id] = desc->description; } RARCH_LOG("Environ SET_INPUT_DESCRIPTORS:\n"); for (p = 0; p < settings->input.max_users; p++) { for (retro_id = 0; retro_id < RARCH_FIRST_CUSTOM_BIND; retro_id++) { const char *description = system->input_desc_btn[p][retro_id]; if (!description) continue; RARCH_LOG("\tRetroPad, User %u, Button \"%s\" => \"%s\"\n", p + 1, libretro_btn_desc[retro_id], description); } } global->has_set.input_descriptors = true; break; } case RETRO_ENVIRONMENT_SET_KEYBOARD_CALLBACK: { const struct retro_keyboard_callback *info = (const struct retro_keyboard_callback*)data; RARCH_LOG("Environ SET_KEYBOARD_CALLBACK.\n"); system->key_event = info->callback; global->frontend_key_event = system->key_event; break; } case RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE: RARCH_LOG("Environ SET_DISK_CONTROL_INTERFACE.\n"); system->disk_control = *(const struct retro_disk_control_callback*)data; break; case RETRO_ENVIRONMENT_SET_HW_RENDER: case RETRO_ENVIRONMENT_SET_HW_RENDER | RETRO_ENVIRONMENT_EXPERIMENTAL: { struct retro_hw_render_callback *hw_render = video_driver_callback(); struct retro_hw_render_callback *cb = (struct retro_hw_render_callback*)data; RARCH_LOG("Environ SET_HW_RENDER.\n"); switch (cb->context_type) { case RETRO_HW_CONTEXT_NONE: RARCH_LOG("Requesting no HW context.\n"); break; #if defined(HAVE_OPENGLES2) case RETRO_HW_CONTEXT_OPENGLES2: #if defined(HAVE_OPENGLES3) case RETRO_HW_CONTEXT_OPENGLES3: #endif RARCH_LOG("Requesting OpenGLES%u context.\n", cb->context_type == RETRO_HW_CONTEXT_OPENGLES2 ? 2 : 3); break; #if defined(HAVE_OPENGLES3) case RETRO_HW_CONTEXT_OPENGLES_VERSION: RARCH_LOG("Requesting OpenGLES%u.%u context.\n", cb->version_major, cb->version_minor); break; #endif case RETRO_HW_CONTEXT_OPENGL: case RETRO_HW_CONTEXT_OPENGL_CORE: RARCH_ERR("Requesting OpenGL context, but RetroArch is compiled against OpenGLES2. Cannot use HW context.\n"); return false; #elif defined(HAVE_OPENGL) case RETRO_HW_CONTEXT_OPENGLES2: case RETRO_HW_CONTEXT_OPENGLES3: RARCH_ERR("Requesting OpenGLES%u context, but RetroArch is compiled against OpenGL. Cannot use HW context.\n", cb->context_type == RETRO_HW_CONTEXT_OPENGLES2 ? 2 : 3); return false; case RETRO_HW_CONTEXT_OPENGLES_VERSION: RARCH_ERR("Requesting OpenGLES%u.%u context, but RetroArch is compiled against OpenGL. Cannot use HW context.\n", cb->version_major, cb->version_minor); return false; case RETRO_HW_CONTEXT_OPENGL: RARCH_LOG("Requesting OpenGL context.\n"); break; case RETRO_HW_CONTEXT_OPENGL_CORE: RARCH_LOG("Requesting core OpenGL context (%u.%u).\n", cb->version_major, cb->version_minor); break; #endif default: RARCH_LOG("Requesting unknown context.\n"); return false; } cb->get_current_framebuffer = video_driver_get_current_framebuffer; cb->get_proc_address = video_driver_get_proc_address; if (cmd & RETRO_ENVIRONMENT_EXPERIMENTAL) /* Old ABI. Don't copy garbage. */ memcpy(hw_render, cb, offsetof(struct retro_hw_render_callback, stencil)); else memcpy(hw_render, cb, sizeof(*cb)); break; } case RETRO_ENVIRONMENT_SET_SUPPORT_NO_GAME: { bool state = *(const bool*)data; RARCH_LOG("Environ SET_SUPPORT_NO_GAME: %s.\n", state ? "yes" : "no"); system->no_content = state; break; } case RETRO_ENVIRONMENT_GET_LIBRETRO_PATH: { const char **path = (const char**)data; #ifdef HAVE_DYNAMIC *path = settings->libretro; #else *path = NULL; #endif break; } /* FIXME - PS3 audio driver needs to be fixed so that threaded * audio works correctly (audio is already on a thread for PS3 * audio driver so that's probably the problem) */ #if defined(HAVE_THREADS) && !defined(__CELLOS_LV2__) case RETRO_ENVIRONMENT_SET_AUDIO_CALLBACK: { const struct retro_audio_callback *info = (const struct retro_audio_callback*)data; RARCH_LOG("Environ SET_AUDIO_CALLBACK.\n"); if (driver->recording_data) /* A/V sync is a must. */ return false; #ifdef HAVE_NETPLAY if (global->netplay.enable) return false; #endif audio_driver_set_callback(info); break; } #endif case RETRO_ENVIRONMENT_SET_FRAME_TIME_CALLBACK: { const struct retro_frame_time_callback *info = (const struct retro_frame_time_callback*)data; RARCH_LOG("Environ SET_FRAME_TIME_CALLBACK.\n"); #ifdef HAVE_NETPLAY /* retro_run() will be called in very strange and * mysterious ways, have to disable it. */ if (global->netplay.enable) return false; #endif system->frame_time = *info; break; } case RETRO_ENVIRONMENT_GET_RUMBLE_INTERFACE: { struct retro_rumble_interface *iface = (struct retro_rumble_interface*)data; RARCH_LOG("Environ GET_RUMBLE_INTERFACE.\n"); iface->set_rumble_state = input_driver_set_rumble_state; break; } case RETRO_ENVIRONMENT_GET_INPUT_DEVICE_CAPABILITIES: { uint64_t *mask = (uint64_t*)data; RARCH_LOG("Environ GET_INPUT_DEVICE_CAPABILITIES.\n"); if (driver->input && driver->input->get_capabilities && driver->input_data) *mask = input_driver_get_capabilities(); else return false; break; } case RETRO_ENVIRONMENT_GET_SENSOR_INTERFACE: { struct retro_sensor_interface *iface = (struct retro_sensor_interface*)data; RARCH_LOG("Environ GET_SENSOR_INTERFACE.\n"); iface->set_sensor_state = input_sensor_set_state; iface->get_sensor_input = input_sensor_get_input; break; } case RETRO_ENVIRONMENT_GET_CAMERA_INTERFACE: { struct retro_camera_callback *cb = (struct retro_camera_callback*)data; RARCH_LOG("Environ GET_CAMERA_INTERFACE.\n"); cb->start = driver_camera_start; cb->stop = driver_camera_stop; system->camera_callback = *cb; driver->camera_active = cb->caps != 0; break; } case RETRO_ENVIRONMENT_GET_LOCATION_INTERFACE: { struct retro_location_callback *cb = (struct retro_location_callback*)data; RARCH_LOG("Environ GET_LOCATION_INTERFACE.\n"); cb->start = driver_location_start; cb->stop = driver_location_stop; cb->get_position = driver_location_get_position; cb->set_interval = driver_location_set_interval; system->location_callback = *cb; driver->location_active = true; break; } case RETRO_ENVIRONMENT_GET_LOG_INTERFACE: { struct retro_log_callback *cb = (struct retro_log_callback*)data; RARCH_LOG("Environ GET_LOG_INTERFACE.\n"); cb->log = rarch_log_libretro; break; } case RETRO_ENVIRONMENT_GET_PERF_INTERFACE: { struct retro_perf_callback *cb = (struct retro_perf_callback*)data; RARCH_LOG("Environ GET_PERF_INTERFACE.\n"); cb->get_time_usec = rarch_get_time_usec; cb->get_cpu_features = rarch_get_cpu_features; cb->get_perf_counter = rarch_get_perf_counter; cb->perf_register = retro_perf_register; /* libretro specific path. */ cb->perf_start = rarch_perf_start; cb->perf_stop = rarch_perf_stop; cb->perf_log = retro_perf_log; /* libretro specific path. */ break; } case RETRO_ENVIRONMENT_GET_CORE_ASSETS_DIRECTORY: { const char **dir = (const char**)data; *dir = *settings->core_assets_directory ? settings->core_assets_directory : NULL; RARCH_LOG("Environ CORE_ASSETS_DIRECTORY: \"%s\".\n", settings->core_assets_directory); break; } case RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO: { RARCH_LOG("Environ SET_SYSTEM_AV_INFO.\n"); return driver_update_system_av_info( (const struct retro_system_av_info*)data); } case RETRO_ENVIRONMENT_SET_SUBSYSTEM_INFO: { unsigned i, j; const struct retro_subsystem_info *info = (const struct retro_subsystem_info*)data; RARCH_LOG("Environ SET_SUBSYSTEM_INFO.\n"); for (i = 0; info[i].ident; i++) { RARCH_LOG("Special game type: %s\n", info[i].desc); RARCH_LOG(" Ident: %s\n", info[i].ident); RARCH_LOG(" ID: %u\n", info[i].id); RARCH_LOG(" Content:\n"); for (j = 0; j < info[i].num_roms; j++) { RARCH_LOG(" %s (%s)\n", info[i].roms[j].desc, info[i].roms[j].required ? "required" : "optional"); } } free(system->special); system->special = (struct retro_subsystem_info*) calloc(i, sizeof(*system->special)); if (!system->special) return false; memcpy(system->special, info, i * sizeof(*system->special)); system->num_special = i; break; } case RETRO_ENVIRONMENT_SET_CONTROLLER_INFO: { unsigned i, j; const struct retro_controller_info *info = (const struct retro_controller_info*)data; RARCH_LOG("Environ SET_CONTROLLER_INFO.\n"); for (i = 0; info[i].types; i++) { RARCH_LOG("Controller port: %u\n", i + 1); for (j = 0; j < info[i].num_types; j++) RARCH_LOG(" %s (ID: %u)\n", info[i].types[j].desc, info[i].types[j].id); } free(system->ports); system->ports = (struct retro_controller_info*) calloc(i, sizeof(*system->ports)); if (!system->ports) return false; memcpy(system->ports, info, i * sizeof(*system->ports)); system->num_ports = i; break; } case RETRO_ENVIRONMENT_SET_GEOMETRY: { struct retro_system_av_info *av_info = video_viewport_get_system_av_info(); const struct retro_game_geometry *in_geom = (const struct retro_game_geometry*)data; struct retro_game_geometry *geom = av_info ? (struct retro_game_geometry*)&av_info->geometry : NULL; RARCH_LOG("Environ SET_GEOMETRY.\n"); /* Can potentially be called every frame, * don't do anything unless required. */ if (geom->base_width != in_geom->base_width || geom->base_height != in_geom->base_height || geom->aspect_ratio != in_geom->aspect_ratio) { geom->base_width = in_geom->base_width; geom->base_height = in_geom->base_height; geom->aspect_ratio = in_geom->aspect_ratio; RARCH_LOG("SET_GEOMETRY: %ux%u, aspect: %.3f.\n", geom->base_width, geom->base_height, geom->aspect_ratio); /* Forces recomputation of aspect ratios if * using core-dependent aspect ratios. */ event_command(EVENT_CMD_VIDEO_SET_ASPECT_RATIO); /* TODO: Figure out what to do, if anything, with recording. */ } break; } /* Private extensions for internal use, not part of libretro API. */ case RETRO_ENVIRONMENT_SET_LIBRETRO_PATH: RARCH_LOG("Environ (Private) SET_LIBRETRO_PATH.\n"); if (path_file_exists((const char*)data)) strlcpy(settings->libretro, (const char*)data, sizeof(settings->libretro)); else return false; break; case RETRO_ENVIRONMENT_EXEC: case RETRO_ENVIRONMENT_EXEC_ESCAPE: *global->path.fullpath = '\0'; if (data) strlcpy(global->path.fullpath, (const char*)data, sizeof(global->path.fullpath)); #if defined(RARCH_CONSOLE) if (driver->frontend_ctx && driver->frontend_ctx->set_fork) driver->frontend_ctx->set_fork(true, true); #elif defined(HAVE_DYNAMIC) rarch_main_set_state(RARCH_ACTION_STATE_LOAD_CONTENT); #endif if (cmd == RETRO_ENVIRONMENT_EXEC_ESCAPE) { RARCH_LOG("Environ (Private) EXEC_ESCAPE.\n"); global->exec = true; } else RARCH_LOG("Environ (Private) EXEC.\n"); break; default: RARCH_LOG("Environ UNSUPPORTED (#%u).\n", cmd); return false; } return true; }
static bool get_texture_image(const char *shader_path, xmlNodePtr ptr) { if (gl_teximage_cnt >= MAX_TEXTURES) { RARCH_WARN("Too many texture images. Ignoring ...\n"); return true; } bool linear = true; char filename[PATH_MAX]; char filter[64]; char id[64]; xml_get_prop(filename, sizeof(filename), ptr, "file"); xml_get_prop(filter, sizeof(filter), ptr, "filter"); xml_get_prop(id, sizeof(id), ptr, "id"); struct texture_image img; if (!*id) { RARCH_ERR("Could not find ID in texture.\n"); return false; } if (!*filename) { RARCH_ERR("Could not find filename in texture.\n"); return false; } if (strcmp(filter, "nearest") == 0) linear = false; char tex_path[PATH_MAX]; fill_pathname_resolve_relative(tex_path, shader_path, (const char*)filename, sizeof(tex_path)); RARCH_LOG("Loading texture image from: \"%s\" ...\n", tex_path); if (!texture_image_load(tex_path, &img)) { RARCH_ERR("Failed to load texture image from: \"%s\"\n", tex_path); return false; } strlcpy(gl_teximage_uniforms[gl_teximage_cnt], (const char*)id, sizeof(gl_teximage_uniforms[0])); glGenTextures(1, &gl_teximage[gl_teximage_cnt]); pglActiveTexture(GL_TEXTURE0 + gl_teximage_cnt + 1); glBindTexture(GL_TEXTURE_2D, gl_teximage[gl_teximage_cnt]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, BORDER_FUNC); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, BORDER_FUNC); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, linear ? GL_LINEAR : GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, linear ? GL_LINEAR : GL_NEAREST); glPixelStorei(GL_UNPACK_ALIGNMENT, 4); glTexImage2D(GL_TEXTURE_2D, 0, driver.gfx_use_rgba ? GL_RGBA : RARCH_GL_INTERNAL_FORMAT32, img.width, img.height, 0, driver.gfx_use_rgba ? GL_RGBA : RARCH_GL_TEXTURE_TYPE32, RARCH_GL_FORMAT32, img.pixels); pglActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, 0); free(img.pixels); gl_teximage_cnt++; return true; }
static bool input_overlay_load_overlay(config_file_t *conf, const char *config_path, struct overlay *overlay, unsigned index) { char overlay_path_key[64]; char overlay_name_key[64]; char overlay_path[PATH_MAX]; char overlay_resolved_path[PATH_MAX]; snprintf(overlay_path_key, sizeof(overlay_path_key), "overlay%u_overlay", index); if (!config_get_path(conf, overlay_path_key, overlay_path, sizeof(overlay_path))) { RARCH_ERR("[Overlay]: Config key: %s is not set.\n", overlay_path_key); return false; } snprintf(overlay_name_key, sizeof(overlay_name_key), "overlay%u_name", index); config_get_array(conf, overlay_name_key, overlay->name, sizeof(overlay->name)); fill_pathname_resolve_relative(overlay_resolved_path, config_path, overlay_path, sizeof(overlay_resolved_path)); struct texture_image img = {0}; if (!texture_image_load(overlay_resolved_path, &img)) { RARCH_ERR("Failed to load image: %s.\n", overlay_path); return false; } overlay->image = img.pixels; overlay->width = img.width; overlay->height = img.height; // By default, we stretch the overlay out in full. overlay->x = overlay->y = 0.0f; overlay->w = overlay->h = 1.0f; char overlay_rect_key[64]; snprintf(overlay_rect_key, sizeof(overlay_rect_key), "overlay%u_rect", index); char overlay_rect[256]; if (config_get_array(conf, overlay_rect_key, overlay_rect, sizeof(overlay_rect))) { struct string_list *list = string_split(overlay_rect, ", "); if (list->size < 4) { RARCH_ERR("[Overlay]: Failed to split rect \"%s\" into at least four tokens.\n", overlay_rect); return false; } overlay->x = strtod(list->elems[0].data, NULL); overlay->y = strtod(list->elems[1].data, NULL); overlay->w = strtod(list->elems[2].data, NULL); overlay->h = strtod(list->elems[3].data, NULL); string_list_free(list); } char overlay_full_screen_key[64]; snprintf(overlay_full_screen_key, sizeof(overlay_full_screen_key), "overlay%u_full_screen", index); overlay->full_screen = false; config_get_bool(conf, overlay_full_screen_key, &overlay->full_screen); char overlay_descs_key[64]; snprintf(overlay_descs_key, sizeof(overlay_descs_key), "overlay%u_descs", index); unsigned descs = 0; if (!config_get_uint(conf, overlay_descs_key, &descs)) { RARCH_ERR("[Overlay]: Failed to read number of descs from config key: %s.\n", overlay_descs_key); return false; } overlay->descs = (struct overlay_desc*)calloc(descs, sizeof(*overlay->descs)); if (!overlay->descs) { RARCH_ERR("[Overlay]: Failed to allocate descs.\n"); return false; } overlay->size = descs; for (size_t i = 0; i < overlay->size; i++) { if (!input_overlay_load_desc(conf, &overlay->descs[i], index, i, img.width, img.height)) { RARCH_ERR("[Overlay]: Failed to load overlay descs for overlay #%u.\n", (unsigned)i); return false; } } // Assume for now that scaling center is in the middle. // TODO: Make this configurable. overlay->block_scale = false; overlay->center_x = overlay->x + 0.5f * overlay->w; overlay->center_y = overlay->y + 0.5f * overlay->h; return true; }
static bool input_overlay_load_desc(input_overlay_t *ol, config_file_t *conf, struct overlay_desc *desc, unsigned ol_index, unsigned desc_index, unsigned width, unsigned height, bool normalized, float alpha_mod, float range_mod) { bool ret = true; char overlay_desc_key[64]; snprintf(overlay_desc_key, sizeof(overlay_desc_key), "overlay%u_desc%u", ol_index, desc_index); char overlay_desc_image_key[64]; snprintf(overlay_desc_image_key, sizeof(overlay_desc_image_key), "overlay%u_desc%u_overlay", ol_index, desc_index); char image_path[PATH_MAX]; if (config_get_path(conf, overlay_desc_image_key, image_path, sizeof(image_path))) { char path[PATH_MAX]; fill_pathname_resolve_relative(path, ol->overlay_path, image_path, sizeof(path)); struct texture_image img = {0}; if (texture_image_load(&img, path)) desc->image = img; } char overlay_desc_normalized_key[64]; snprintf(overlay_desc_normalized_key, sizeof(overlay_desc_normalized_key), "overlay%u_desc%u_normalized", ol_index, desc_index); config_get_bool(conf, overlay_desc_normalized_key, &normalized); bool by_pixel = !normalized; if (by_pixel && (width == 0 || height == 0)) { RARCH_ERR("[Overlay]: Base overlay is not set and not using normalized coordinates.\n"); return false; } char overlay[256]; if (!config_get_array(conf, overlay_desc_key, overlay, sizeof(overlay))) { RARCH_ERR("[Overlay]: Didn't find key: %s.\n", overlay_desc_key); return false; } struct string_list *list = string_split(overlay, ", "); if (!list) { RARCH_ERR("[Overlay]: Failed to split overlay desc.\n"); return false; } if (list->size < 6) { string_list_free(list); RARCH_ERR("[Overlay]: Overlay desc is invalid. Requires at least 6 tokens.\n"); return false; } const char *x = list->elems[1].data; const char *y = list->elems[2].data; const char *box = list->elems[3].data; char *key = list->elems[0].data; char *save; desc->key_mask = 0; if (strcmp(key, "analog_left") == 0) desc->type = OVERLAY_TYPE_ANALOG_LEFT; else if (strcmp(key, "analog_right") == 0) desc->type = OVERLAY_TYPE_ANALOG_RIGHT; else if (strstr(key, "retrok_") == key) { desc->type = OVERLAY_TYPE_KEYBOARD; desc->key_mask = input_translate_str_to_rk(key + 7); } else { const char *tmp; desc->type = OVERLAY_TYPE_BUTTONS; for (tmp = strtok_r(key, "|", &save); tmp; tmp = strtok_r(NULL, "|", &save)) { if (strcmp(tmp, "nul") != 0) desc->key_mask |= UINT64_C(1) << input_translate_str_to_bind_id(tmp); } if (desc->key_mask & (UINT64_C(1) << RARCH_OVERLAY_NEXT)) { char overlay_target_key[64]; snprintf(overlay_target_key, sizeof(overlay_target_key), "overlay%u_desc%u_next_target", ol_index, desc_index); config_get_array(conf, overlay_target_key, desc->next_index_name, sizeof(desc->next_index_name)); } } float width_mod = by_pixel ? (1.0f / width) : 1.0f; float height_mod = by_pixel ? (1.0f / height) : 1.0f; desc->x = (float)strtod(x, NULL) * width_mod; desc->y = (float)strtod(y, NULL) * height_mod; if (!strcmp(box, "radial")) desc->hitbox = OVERLAY_HITBOX_RADIAL; else if (!strcmp(box, "rect")) desc->hitbox = OVERLAY_HITBOX_RECT; else { RARCH_ERR("[Overlay]: Hitbox type (%s) is invalid. Use \"radial\" or \"rect\".\n", box); ret = false; goto end; } if (desc->type == OVERLAY_TYPE_ANALOG_LEFT || desc->type == OVERLAY_TYPE_ANALOG_RIGHT) { if (desc->hitbox != OVERLAY_HITBOX_RADIAL) { RARCH_ERR("[Overlay]: Analog hitbox type must be \"radial\".\n"); ret = false; goto end; } char overlay_analog_saturate_key[64]; snprintf(overlay_analog_saturate_key, sizeof(overlay_analog_saturate_key), "overlay%u_desc%u_saturate_pct", ol_index, desc_index); if (!config_get_float(conf, overlay_analog_saturate_key, &desc->analog_saturate_pct)) desc->analog_saturate_pct = 1.0f; } desc->range_x = (float)strtod(list->elems[4].data, NULL) * width_mod; desc->range_y = (float)strtod(list->elems[5].data, NULL) * height_mod; desc->mod_x = desc->x - desc->range_x; desc->mod_w = 2.0f * desc->range_x; desc->mod_y = desc->y - desc->range_y; desc->mod_h = 2.0f * desc->range_y; char conf_key[64]; snprintf(conf_key, sizeof(conf_key), "overlay%u_desc%u_alpha_mod", ol_index, desc_index); desc->alpha_mod = alpha_mod; config_get_float(conf, conf_key, &desc->alpha_mod); snprintf(conf_key, sizeof(conf_key), "overlay%u_desc%u_range_mod", ol_index, desc_index); desc->range_mod = range_mod; config_get_float(conf, conf_key, &desc->range_mod); snprintf(conf_key, sizeof(conf_key), "overlay%u_desc%u_movable", ol_index, desc_index); desc->movable = false; desc->delta_x = 0.0f; desc->delta_y = 0.0f; config_get_bool(conf, conf_key, &desc->movable); desc->range_x_mod = desc->range_x; desc->range_y_mod = desc->range_y; end: if (list) string_list_free(list); return ret; }
bool input_overlay_load_overlays(input_overlay_t *ol) { unsigned i; config_file_t *conf = NULL; if (!ol) return false; conf = config_file_new(ol->overlay_path); if (!conf) { RARCH_ERR("Failed to load config file: %s.\n", ol->overlay_path); return false; } if (!config_get_uint(conf, "overlays", &ol->config.overlays.size)) { RARCH_ERR("overlays variable not defined in config.\n"); goto error; } if (!ol->config.overlays.size) goto error; ol->overlays = (struct overlay*)calloc( ol->config.overlays.size, sizeof(*ol->overlays)); if (!ol->overlays) goto error; ol->size = ol->config.overlays.size; ol->pos = 0; ol->resolve_pos = 0; for (i = 0; i < ol->size; i++) { char conf_key[64]; char overlay_full_screen_key[64]; struct overlay *overlay = &ol->overlays[i]; if (!overlay) continue; snprintf(overlay->config.descs.key, sizeof(overlay->config.descs.key), "overlay%u_descs", i); if (!config_get_uint(conf, overlay->config.descs.key, &overlay->config.descs.size)) { RARCH_ERR("[Overlay]: Failed to read number of descs from config key: %s.\n", overlay->config.descs.key); goto error; } overlay->descs = (struct overlay_desc*) calloc(overlay->config.descs.size, sizeof(*overlay->descs)); if (!overlay->descs) { RARCH_ERR("[Overlay]: Failed to allocate descs.\n"); goto error; } overlay->size = overlay->config.descs.size; snprintf(overlay_full_screen_key, sizeof(overlay_full_screen_key), "overlay%u_full_screen", i); overlay->full_screen = false; config_get_bool(conf, overlay_full_screen_key, &overlay->full_screen); overlay->config.normalized = false; overlay->config.alpha_mod = 1.0f; overlay->config.range_mod = 1.0f; snprintf(conf_key, sizeof(conf_key), "overlay%u_normalized", i); config_get_bool(conf, conf_key, &overlay->config.normalized); snprintf(conf_key, sizeof(conf_key), "overlay%u_alpha_mod", i); config_get_float(conf, conf_key, &overlay->config.alpha_mod); snprintf(conf_key, sizeof(conf_key), "overlay%u_range_mod", i); config_get_float(conf, conf_key, &overlay->config.range_mod); /* Precache load image array for simplicity. */ overlay->load_images = (struct texture_image*) calloc(1 + overlay->size, sizeof(struct texture_image)); if (!overlay->load_images) { RARCH_ERR("[Overlay]: Failed to allocate load_images.\n"); goto error; } snprintf(overlay->config.paths.key, sizeof(overlay->config.paths.key), "overlay%u_overlay", i); config_get_path(conf, overlay->config.paths.key, overlay->config.paths.path, sizeof(overlay->config.paths.path)); if (overlay->config.paths.path[0] != '\0') { char overlay_resolved_path[PATH_MAX_LENGTH]; struct texture_image img = {0}; fill_pathname_resolve_relative(overlay_resolved_path, ol->overlay_path, overlay->config.paths.path, sizeof(overlay_resolved_path)); if (!texture_image_load(&img, overlay_resolved_path)) { RARCH_ERR("[Overlay]: Failed to load image: %s.\n", overlay_resolved_path); ol->loading_status = OVERLAY_IMAGE_TRANSFER_ERROR; goto error; } overlay->image = img; } snprintf(overlay->config.names.key, sizeof(overlay->config.names.key), "overlay%u_name", i); config_get_array(conf, overlay->config.names.key, overlay->name, sizeof(overlay->name)); /* By default, we stretch the overlay out in full. */ overlay->x = overlay->y = 0.0f; overlay->w = overlay->h = 1.0f; snprintf(overlay->config.rect.key, sizeof(overlay->config.rect.key), "overlay%u_rect", i); if (config_get_array(conf, overlay->config.rect.key, overlay->config.rect.array, sizeof(overlay->config.rect.array))) { struct string_list *list = string_split(overlay->config.rect.array, ", "); if (!list || list->size < 4) { RARCH_ERR("[Overlay]: Failed to split rect \"%s\" into at least four tokens.\n", overlay->config.rect.array); string_list_free(list); goto error; } overlay->x = (float)strtod(list->elems[0].data, NULL); overlay->y = (float)strtod(list->elems[1].data, NULL); overlay->w = (float)strtod(list->elems[2].data, NULL); overlay->h = (float)strtod(list->elems[3].data, NULL); string_list_free(list); } /* Assume for now that scaling center is in the middle. * TODO: Make this configurable. */ overlay->block_scale = false; overlay->center_x = overlay->x + 0.5f * overlay->w; overlay->center_y = overlay->y + 0.5f * overlay->h; } ol->state = OVERLAY_STATUS_DEFERRED_LOADING; config_file_free(conf); return true; error: config_file_free(conf); ol->state = OVERLAY_STATUS_DEFERRED_ERROR; return false; }