static bool init_record(bsv_movie_t *handle, const char *path) { retro_ctx_size_info_t info; uint32_t state_size; uint32_t header[4] = {0}; uint32_t *content_crc_ptr = NULL; handle->file = fopen(path, "wb"); if (!handle->file) { RARCH_ERR("Couldn't open BSV \"%s\" for recording.\n", path); return false; } content_ctl(CONTENT_CTL_GET_CRC, &content_crc_ptr); /* This value is supposed to show up as * BSV1 in a HEX editor, big-endian. */ header[MAGIC_INDEX] = swap_if_little32(BSV_MAGIC); header[CRC_INDEX] = swap_if_big32(*content_crc_ptr); core_ctl(CORE_CTL_RETRO_SERIALIZE_SIZE, &info); state_size = info.size; header[STATE_SIZE_INDEX] = swap_if_big32(state_size); fwrite(header, 4, sizeof(uint32_t), handle->file); handle->min_file_pos = sizeof(header) + state_size; handle->state_size = state_size; if (state_size) { retro_ctx_serialize_info_t serial_info; handle->state = (uint8_t*)malloc(state_size); if (!handle->state) return false; serial_info.data = handle->state; serial_info.size = state_size; core_ctl(CORE_CTL_RETRO_SERIALIZE, &serial_info); fwrite(handle->state, 1, state_size, handle->file); } return true; }
static PyObject* py_read_vram(PyObject *self, PyObject *args) { unsigned addr; size_t max; retro_ctx_memory_info_t mem_info; const uint8_t *data = NULL; mem_info.id = RETRO_MEMORY_VIDEO_RAM; core_ctl(CORE_CTL_RETRO_GET_MEMORY, &mem_info); data = (const uint8_t*)mem_info.data; (void)self; if (!data) { Py_INCREF(Py_None); return Py_None; } max = mem_info.size; if (!PyArg_ParseTuple(args, "I", &addr)) return NULL; if (addr >= max) { Py_INCREF(Py_None); return Py_None; } return PyLong_FromLong(data[addr]); }
/** * save_ram_file: * @path : path of RAM state that shall be written to. * @type : type of memory * * Save a RAM state from memory to disk. * */ static bool save_ram_file(ram_type_t *ram) { retro_ctx_memory_info_t mem_info; mem_info.id = ram->type; core_ctl(CORE_CTL_RETRO_GET_MEMORY, &mem_info); if (!mem_info.data || mem_info.size == 0) return false; if (!retro_write_file(ram->path, mem_info.data, mem_info.size)) { RARCH_ERR("%s.\n", msg_hash_to_str(MSG_FAILED_TO_SAVE_SRAM)); RARCH_WARN("Attempting to recover ...\n"); /* In case the file could not be written to, * the fallback function 'dump_to_file_desperate' * will be called. */ if (!dump_to_file_desperate(mem_info.data, mem_info.size, ram->type)) { RARCH_WARN("Failed ... Cannot recover save file.\n"); } return false; } RARCH_LOG("%s \"%s\".\n", msg_hash_to_str(MSG_SAVED_SUCCESSFULLY_TO), ram->path); return true; }
static bool event_init_core(void *data) { retro_ctx_environ_info_t info; settings_t *settings = config_get_ptr(); if (!core_ctl(CORE_CTL_RETRO_SYMBOLS_INIT, data)) return false; runloop_ctl(RUNLOOP_CTL_SYSTEM_INFO_INIT, NULL); /* auto overrides: apply overrides */ if(settings->auto_overrides_enable) { if (config_load_override()) runloop_ctl(RUNLOOP_CTL_SET_OVERRIDES_ACTIVE, NULL); else runloop_ctl(RUNLOOP_CTL_UNSET_OVERRIDES_ACTIVE, NULL); } /* reset video format to libretro's default */ video_driver_set_pixel_format(RETRO_PIXEL_FORMAT_0RGB1555); info.env = rarch_environment_cb; core_ctl(CORE_CTL_RETRO_SET_ENVIRONMENT, &info); /* Auto-remap: apply remap files */ if(settings->auto_remaps_enable) config_load_remap(); /* Per-core saves: reset redirection paths */ rarch_ctl(RARCH_CTL_SET_PATHS_REDIRECT, NULL); if (!core_ctl(CORE_CTL_RETRO_INIT, NULL)) return false; if (!event_init_content()) return false; if (!core_ctl(CORE_CTL_INIT, NULL)) return false; return true; }
/** * save_state: * @path : path of saved state that shall be written to. * * Save a state from memory to disk. * * Returns: true if successful, false otherwise. **/ static bool content_save_state(const char *path) { retro_ctx_serialize_info_t serial_info; retro_ctx_size_info_t info; bool ret = false; void *data = NULL; core_ctl(CORE_CTL_RETRO_SERIALIZE_SIZE, &info); RARCH_LOG("%s: \"%s\".\n", msg_hash_to_str(MSG_SAVING_STATE), path); if (info.size == 0) return false; data = malloc(info.size); if (!data) return false; RARCH_LOG("%s: %d %s.\n", msg_hash_to_str(MSG_STATE_SIZE), (int)info.size, msg_hash_to_str(MSG_BYTES)); serial_info.data = data; serial_info.size = info.size; ret = core_ctl(CORE_CTL_RETRO_SERIALIZE, &serial_info); if (ret) ret = retro_write_file(path, data, info.size); else { RARCH_ERR("%s \"%s\".\n", msg_hash_to_str(MSG_FAILED_TO_SAVE_STATE_TO), path); } free(data); return ret; }
static void event_deinit_core(bool reinit) { #ifdef HAVE_CHEEVOS cheevos_ctl(CHEEVOS_CTL_UNLOAD, NULL); #endif core_ctl(CORE_CTL_RETRO_UNLOAD_GAME, NULL); core_ctl(CORE_CTL_RETRO_DEINIT, NULL); if (reinit) { int flags = DRIVERS_CMD_ALL; driver_ctl(RARCH_DRIVER_CTL_UNINIT, &flags); } /* auto overrides: reload the original config */ if (runloop_ctl(RUNLOOP_CTL_IS_OVERRIDES_ACTIVE, NULL)) { config_unload_override(); runloop_ctl(RUNLOOP_CTL_UNSET_OVERRIDES_ACTIVE, NULL); } }
uint32_t *np_bsv_header_generate(size_t *size, uint32_t magic) { retro_ctx_serialize_info_t serial_info; retro_ctx_size_info_t info; uint32_t *content_crc_ptr; size_t serialize_size, header_size; uint32_t *header, bsv_header[4] = {0}; core_ctl(CORE_CTL_RETRO_SERIALIZE_SIZE, &info); serialize_size = info.size; header_size = sizeof(bsv_header) + serialize_size; *size = header_size; header = (uint32_t*)malloc(header_size); if (!header) goto error; content_ctl(CONTENT_CTL_GET_CRC, &content_crc_ptr); bsv_header[MAGIC_INDEX] = swap_if_little32(BSV_MAGIC); bsv_header[SERIALIZER_INDEX] = swap_if_big32(magic); bsv_header[CRC_INDEX] = swap_if_big32(*content_crc_ptr); bsv_header[STATE_SIZE_INDEX] = swap_if_big32(serialize_size); serial_info.data = header + 4; serial_info.size = serialize_size; if (serialize_size && !core_ctl(CORE_CTL_RETRO_SERIALIZE, &serial_info)) goto error; memcpy(header, bsv_header, sizeof(bsv_header)); return header; error: if (header) free(header); return NULL; }
bool np_send_info(netplay_t *netplay) { unsigned sram_size; retro_ctx_memory_info_t mem_info; char msg[512] = {0}; uint32_t *content_crc_ptr = NULL; void *sram = NULL; uint32_t header[3] = {0}; mem_info.id = RETRO_MEMORY_SAVE_RAM; core_ctl(CORE_CTL_RETRO_GET_MEMORY, &mem_info); content_ctl(CONTENT_CTL_GET_CRC, &content_crc_ptr); header[0] = htonl(*content_crc_ptr); header[1] = htonl(np_impl_magic()); header[2] = htonl(mem_info.size); if (!socket_send_all_blocking(netplay->fd, header, sizeof(header))) return false; if (!np_send_nickname(netplay, netplay->fd)) { RARCH_ERR("Failed to send nick to host.\n"); return false; } /* Get SRAM data from User 1. */ sram = mem_info.data; sram_size = mem_info.size; if (!socket_receive_all_blocking(netplay->fd, sram, sram_size)) { RARCH_ERR("Failed to receive SRAM data from host.\n"); return false; } if (!np_get_nickname(netplay, netplay->fd)) { RARCH_ERR("Failed to receive nick from host.\n"); return false; } snprintf(msg, sizeof(msg), "Connected to: \"%s\"", netplay->other_nick); RARCH_LOG("%s\n", msg); runloop_msg_queue_push(msg, 1, 180, false); return true; }
static void bsv_movie_frame_rewind(bsv_movie_t *handle) { handle->did_rewind = true; if ( (handle->frame_ptr <= 1) && (handle->frame_pos[0] == handle->min_file_pos)) { /* If we're at the beginning... */ handle->frame_ptr = 0; fseek(handle->file, handle->min_file_pos, SEEK_SET); } else { /* First time rewind is performed, the old frame is simply replayed. * However, playing back that frame caused us to read data, and push * data to the ring buffer. * * Sucessively rewinding frames, we need to rewind past the read data, * plus another. */ handle->frame_ptr = (handle->frame_ptr - (handle->first_rewind ? 1 : 2)) & handle->frame_mask; fseek(handle->file, handle->frame_pos[handle->frame_ptr], SEEK_SET); } if (ftell(handle->file) <= (long)handle->min_file_pos) { /* We rewound past the beginning. */ if (!handle->playback) { retro_ctx_serialize_info_t serial_info; /* If recording, we simply reset * the starting point. Nice and easy. */ fseek(handle->file, 4 * sizeof(uint32_t), SEEK_SET); serial_info.data = handle->state; serial_info.size = handle->state_size; core_ctl(CORE_CTL_RETRO_SERIALIZE, &serial_info); fwrite(handle->state, 1, handle->state_size, handle->file); } else fseek(handle->file, handle->min_file_pos, SEEK_SET); } }
static int16_t netplay_get_spectate_input(netplay_t *netplay, bool port, unsigned device, unsigned idx, unsigned id) { int16_t inp; retro_ctx_input_state_info_t input_info; if (socket_receive_all_blocking(netplay->fd, (char*)&inp, sizeof(inp))) return swap_if_big16(inp); RARCH_ERR("Connection with host was cut.\n"); runloop_msg_queue_push("Connection with host was cut.", 1, 180, true); input_info.cb = netplay->cbs.state_cb; core_ctl(CORE_CTL_RETRO_SET_INPUT_STATE, &input_info); return netplay->cbs.state_cb(port, device, idx, id); }
bool np_bsv_parse_header(const uint32_t *header, uint32_t magic) { retro_ctx_size_info_t info; uint32_t *content_crc_ptr; uint32_t in_crc, in_magic, in_state_size; uint32_t in_bsv = swap_if_little32(header[MAGIC_INDEX]); if (in_bsv != BSV_MAGIC) { RARCH_ERR("BSV magic mismatch, got 0x%x, expected 0x%x.\n", in_bsv, BSV_MAGIC); return false; } in_magic = swap_if_big32(header[SERIALIZER_INDEX]); if (in_magic != magic) { RARCH_ERR("Magic mismatch, got 0x%x, expected 0x%x.\n", in_magic, magic); return false; } in_crc = swap_if_big32(header[CRC_INDEX]); content_ctl(CONTENT_CTL_GET_CRC, &content_crc_ptr); if (in_crc != *content_crc_ptr) { RARCH_ERR("CRC32 mismatch, got 0x%x, expected 0x%x.\n", in_crc, *content_crc_ptr); return false; } core_ctl(CORE_CTL_RETRO_SERIALIZE_SIZE, &info); in_state_size = swap_if_big32(header[STATE_SIZE_INDEX]); if (in_state_size != info.size) { RARCH_ERR("Serialization size mismatch, got 0x%x, expected 0x%x.\n", (unsigned)in_state_size, (unsigned)info.size); return false; } return true; }
bool init_netplay(void) { struct retro_callbacks cbs = {0}; settings_t *settings = config_get_ptr(); global_t *global = global_get_ptr(); if (!global->netplay.enable) return false; if (bsv_movie_ctl(BSV_MOVIE_CTL_START_PLAYBACK, NULL)) { RARCH_WARN("%s\n", msg_hash_to_str(MSG_NETPLAY_FAILED_MOVIE_PLAYBACK_HAS_STARTED)); return false; } core_ctl(CORE_CTL_SET_CBS, &cbs); if (*global->netplay.server) { RARCH_LOG("Connecting to netplay host...\n"); global->netplay.is_client = true; } else RARCH_LOG("Waiting for client...\n"); netplay_data = (netplay_t*)netplay_new( global->netplay.is_client ? global->netplay.server : NULL, global->netplay.port ? global->netplay.port : RARCH_DEFAULT_PORT, global->netplay.sync_frames, &cbs, global->netplay.is_spectate, settings->username); if (netplay_data) return true; global->netplay.is_client = false; RARCH_WARN("%s\n", msg_hash_to_str(MSG_NETPLAY_FAILED)); runloop_msg_queue_push( msg_hash_to_str(MSG_NETPLAY_FAILED), 0, 180, false); return false; }
/** * retro_init_libretro_cbs: * @data : pointer to retro_callbacks object * * Initializes libretro callbacks, and binds the libretro callbacks * to default callback functions. **/ static bool retro_init_libretro_cbs(void *data) { struct retro_callbacks *cbs = (struct retro_callbacks*)data; #ifdef HAVE_NETPLAY global_t *global = global_get_ptr(); #endif if (!cbs) return false; core.retro_set_video_refresh(video_driver_frame); core.retro_set_audio_sample(audio_driver_sample); core.retro_set_audio_sample_batch(audio_driver_sample_batch); core.retro_set_input_state(input_state_poll); core.retro_set_input_poll(input_poll_maybe); core_ctl(CORE_CTL_SET_CBS, cbs); #ifdef HAVE_NETPLAY if (!netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_DATA_INITED, NULL)) return true; /* Force normal poll type for netplay. */ core_poll_type = POLL_TYPE_NORMAL; if (global->netplay.is_spectate) { core.retro_set_input_state( (global->netplay.is_client ? input_state_spectate_client : input_state_spectate) ); } else { core.retro_set_video_refresh(video_frame_net); core.retro_set_audio_sample(audio_sample_net); core.retro_set_audio_sample_batch(audio_sample_batch_net); core.retro_set_input_state(input_state_net); } #endif return true; }
static bool d3d_init_imports(d3d_video_t *d3d) { retro_ctx_memory_info_t mem_info; state_tracker_t *state_tracker = NULL; state_tracker_info tracker_info = {0}; if (!d3d->shader.variables) return true; if (!d3d->renderchain_driver->add_state_tracker) return true; mem_info.id = RETRO_MEMORY_SYSTEM_RAM; core_ctl(CORE_CTL_RETRO_GET_MEMORY, &mem_info); tracker_info.wram = (uint8_t*)mem_info.data; tracker_info.info = d3d->shader.variable; tracker_info.info_elem = d3d->shader.variables; #ifdef HAVE_PYTHON if (*d3d->shader.script_path) { tracker_info.script = d3d->shader.script_path; tracker_info.script_is_file = true; } tracker_info.script_class = *d3d->shader.script_class ? d3d->shader.script_class : NULL; #endif state_tracker = state_tracker_init(&tracker_info); if (!state_tracker) { RARCH_ERR("Failed to initialize state tracker.\n"); return false; } d3d->renderchain_driver->add_state_tracker(d3d->renderchain_data, state_tracker); return true; }
/** * load_ram_file: * @path : path of RAM state that will be loaded from. * @type : type of memory * * Load a RAM state from disk to memory. */ static bool load_ram_file(void *data) { ssize_t rc; retro_ctx_memory_info_t mem_info; void *buf = NULL; ram_type_t *ram = (ram_type_t*)data; if (!ram) return false; mem_info.id = ram->type; core_ctl(CORE_CTL_RETRO_GET_MEMORY, &mem_info); if (mem_info.size == 0 || !mem_info.data) return false; if (!retro_read_file(ram->path, &buf, &rc)) return false; if (rc > 0) { if (rc > (ssize_t)mem_info.size) { RARCH_WARN("SRAM is larger than implementation expects, " "doing partial load (truncating %u %s %s %u).\n", (unsigned)rc, msg_hash_to_str(MSG_BYTES), msg_hash_to_str(MSG_TO), (unsigned)mem_info.size); rc = mem_info.size; } memcpy(mem_info.data, buf, rc); } if (buf) free(buf); return true; }
static void event_main_state(unsigned cmd) { retro_ctx_size_info_t info; char path[PATH_MAX_LENGTH] = {0}; char msg[128] = {0}; global_t *global = global_get_ptr(); settings_t *settings = config_get_ptr(); if (settings->state_slot > 0) snprintf(path, sizeof(path), "%s%d", global->name.savestate, settings->state_slot); else if (settings->state_slot < 0) fill_pathname_join_delim(path, global->name.savestate, "auto", '.', sizeof(path)); else strlcpy(path, global->name.savestate, sizeof(path)); core_ctl(CORE_CTL_RETRO_SERIALIZE_SIZE, &info); if (info.size) { switch (cmd) { case EVENT_CMD_SAVE_STATE: event_save_state(path, msg, sizeof(msg)); break; case EVENT_CMD_LOAD_STATE: event_load_state(path, msg, sizeof(msg)); break; } } else strlcpy(msg, msg_hash_to_str( MSG_CORE_DOES_NOT_SUPPORT_SAVESTATES), sizeof(msg)); runloop_msg_queue_push(msg, 2, 180, true); RARCH_LOG("%s\n", msg); }
void autosave_event_init(void) { unsigned i; settings_t *settings = config_get_ptr(); global_t *global = global_get_ptr(); if (settings->autosave_interval < 1 || !global->savefiles) return; if (!(autosave_state.list = (autosave_t**)calloc(global->savefiles->size, sizeof(*autosave_state.list)))) return; autosave_state.num = global->savefiles->size; for (i = 0; i < global->savefiles->size; i++) { retro_ctx_memory_info_t mem_info; const char *path = global->savefiles->elems[i].data; unsigned type = global->savefiles->elems[i].attr.i; mem_info.id = type; core_ctl(CORE_CTL_RETRO_GET_MEMORY, &mem_info); if (mem_info.size <= 0) continue; autosave_state.list[i] = autosave_new(path, mem_info.data, mem_info.size, settings->autosave_interval); if (!autosave_state.list[i]) RARCH_WARN("%s\n", msg_hash_to_str(MSG_AUTOSAVE_FAILED)); } }
/** * np_impl_magic: * * Not really a hash, but should be enough to differentiate * implementations from each other. * * Subtle differences in the implementation will not be possible to spot. * The alternative would have been checking serialization sizes, but it * was troublesome for cross platform compat. **/ uint32_t np_impl_magic(void) { size_t i, len; retro_ctx_api_info_t api_info; unsigned api; uint32_t res = 0; rarch_system_info_t *info = NULL; const char *lib = NULL; const char *ver = PACKAGE_VERSION; core_ctl(CORE_CTL_RETRO_API_VERSION, &api_info); api = api_info.version; runloop_ctl(RUNLOOP_CTL_SYSTEM_INFO_GET, &info); if (info) lib = info->info.library_name; res |= api; len = strlen(lib); for (i = 0; i < len; i++) res ^= lib[i] << (i & 0xf); lib = info->info.library_version; len = strlen(lib); for (i = 0; i < len; i++) res ^= lib[i] << (i & 0xf); len = strlen(ver); for (i = 0; i < len; i++) res ^= ver[i] << ((i & 0xf) + 16); return res; }
/** * load_content: * @special : subsystem of content to be loaded. Can be NULL. * content : * * Load content file (for libretro core). * * Returns : true if successful, otherwise false. **/ static bool load_content( struct string_list *temporary_content, struct retro_game_info *info, const struct string_list *content, const struct retro_subsystem_info *special, struct string_list* additional_path_allocs ) { unsigned i; retro_ctx_load_content_info_t load_info; if (!info || !additional_path_allocs) return false; for (i = 0; i < content->size; i++) { int attr = content->elems[i].attr.i; bool need_fullpath = attr & 2; bool require_content = attr & 4; const char *path = content->elems[i].data; if (require_content && string_is_empty(path)) { RARCH_LOG("libretro core requires content, " "but nothing was provided.\n"); return false; } info[i].path = NULL; if (*path) info[i].path = path; if (!need_fullpath && !string_is_empty(path)) { if (!load_content_into_memory(&info[i], i, path)) return false; } else { RARCH_LOG("Content loading skipped. Implementation will" " load it on its own.\n"); #ifdef HAVE_COMPRESSION if (!load_content_from_compressed_archive( temporary_content, &info[i], i, additional_path_allocs, need_fullpath, path)) return false; #endif } } load_info.content = content; load_info.special = special; load_info.info = info; if (!core_ctl(CORE_CTL_RETRO_LOAD_GAME, &load_info)) { RARCH_ERR("%s.\n", msg_hash_to_str(MSG_FAILED_TO_LOAD_CONTENT)); return false; } #ifdef HAVE_CHEEVOS if (!special) { const void *load_data = NULL; cheevos_ctl(CHEEVOS_CTL_SET_CHEATS, NULL); if (*content->elems[0].data) load_data = info; cheevos_ctl(CHEEVOS_CTL_LOAD, (void*)load_data); } #endif return true; }
/** * content_load_state: * @path : path that state will be loaded from. * * Load a state from disk to memory. * * Returns: true if successful, false otherwise. **/ static bool content_load_state(const char *path) { unsigned i; ssize_t size; retro_ctx_serialize_info_t serial_info; unsigned num_blocks = 0; void *buf = NULL; struct sram_block *blocks = NULL; settings_t *settings = config_get_ptr(); global_t *global = global_get_ptr(); bool ret = retro_read_file(path, &buf, &size); RARCH_LOG("%s: \"%s\".\n", msg_hash_to_str(MSG_LOADING_STATE), path); if (!ret || size < 0) goto error; RARCH_LOG("%s: %u %s.\n", msg_hash_to_str(MSG_STATE_SIZE), (unsigned)size, msg_hash_to_str(MSG_BYTES)); if (settings->block_sram_overwrite && global->savefiles && global->savefiles->size) { RARCH_LOG("%s.\n", msg_hash_to_str(MSG_BLOCKING_SRAM_OVERWRITE)); blocks = (struct sram_block*) calloc(global->savefiles->size, sizeof(*blocks)); if (blocks) { num_blocks = global->savefiles->size; for (i = 0; i < num_blocks; i++) blocks[i].type = global->savefiles->elems[i].attr.i; } } for (i = 0; i < num_blocks; i++) { retro_ctx_memory_info_t mem_info; mem_info.id = blocks[i].type; core_ctl(CORE_CTL_RETRO_GET_MEMORY, &mem_info); blocks[i].size = mem_info.size; } for (i = 0; i < num_blocks; i++) if (blocks[i].size) blocks[i].data = malloc(blocks[i].size); /* Backup current SRAM which is overwritten by unserialize. */ for (i = 0; i < num_blocks; i++) { if (blocks[i].data) { retro_ctx_memory_info_t mem_info; const void *ptr = NULL; mem_info.id = blocks[i].type; core_ctl(CORE_CTL_RETRO_GET_MEMORY, &mem_info); ptr = mem_info.data; if (ptr) memcpy(blocks[i].data, ptr, blocks[i].size); } } serial_info.data_const = buf; serial_info.size = size; ret = core_ctl(CORE_CTL_RETRO_UNSERIALIZE, &serial_info); /* Flush back. */ for (i = 0; i < num_blocks; i++) { if (blocks[i].data) { retro_ctx_memory_info_t mem_info; void *ptr = NULL; mem_info.id = blocks[i].type; core_ctl(CORE_CTL_RETRO_GET_MEMORY, &mem_info); ptr = mem_info.data; if (ptr) memcpy(ptr, blocks[i].data, blocks[i].size); } } for (i = 0; i < num_blocks; i++) free(blocks[i].data); free(blocks); free(buf); if (!ret) goto error; return true; error: RARCH_ERR("%s \"%s\".\n", msg_hash_to_str(MSG_FAILED_TO_LOAD_STATE), path); return false; }
bool core_ctl(enum core_ctl_state state, void *data) { static bool has_set_input_descriptors = false; static struct retro_callbacks retro_ctx; switch (state) { case CORE_CTL_RETRO_CHEAT_SET: { retro_ctx_cheat_info_t *info = (retro_ctx_cheat_info_t*)data; core.retro_cheat_set(info->index, info->enabled, info->code); } break; case CORE_CTL_RETRO_CHEAT_RESET: core.retro_cheat_reset(); break; case CORE_CTL_RETRO_API_VERSION: { retro_ctx_api_info_t *api = (retro_ctx_api_info_t*)data; api->version = core.retro_api_version(); } break; case CORE_CTL_SET_POLL_TYPE: { unsigned *poll_type = (unsigned*)data; core_poll_type = *poll_type; } break; case CORE_CTL_RETRO_SYMBOLS_INIT: { enum rarch_core_type *core_type = (enum rarch_core_type*)data; if (!core_type) return false; init_libretro_sym(*core_type, &core); } break; case CORE_CTL_RETRO_SET_CONTROLLER_PORT_DEVICE: { retro_ctx_controller_info_t *pad = (retro_ctx_controller_info_t*)data; if (!pad) return false; core.retro_set_controller_port_device(pad->port, pad->device); } break; case CORE_CTL_RETRO_GET_MEMORY: { retro_ctx_memory_info_t *info = (retro_ctx_memory_info_t*)data; if (!info) return false; info->size = core.retro_get_memory_size(info->id); info->data = core.retro_get_memory_data(info->id); } break; case CORE_CTL_RETRO_LOAD_GAME: { retro_ctx_load_content_info_t *load_info = (retro_ctx_load_content_info_t*)data; if (!load_info) return false; if (load_info->special) return core.retro_load_game_special(load_info->special->id, load_info->info, load_info->content->size); return core.retro_load_game(*load_info->content->elems[0].data ? load_info->info : NULL); } case CORE_CTL_RETRO_GET_SYSTEM_INFO: { struct retro_system_info *system = (struct retro_system_info*)data; if (!system) return false; core.retro_get_system_info(system); } break; case CORE_CTL_RETRO_UNSERIALIZE: { retro_ctx_serialize_info_t *info = (retro_ctx_serialize_info_t*)data; if (!info) return false; if (!core.retro_unserialize(info->data_const, info->size)) return false; } break; case CORE_CTL_RETRO_SERIALIZE: { retro_ctx_serialize_info_t *info = (retro_ctx_serialize_info_t*)data; if (!info) return false; if (!core.retro_serialize(info->data, info->size)) return false; } break; case CORE_CTL_RETRO_SERIALIZE_SIZE: { retro_ctx_size_info_t *info = (retro_ctx_size_info_t *)data; if (!info) return false; info->size = core.retro_serialize_size(); } break; case CORE_CTL_RETRO_CTX_FRAME_CB: { retro_ctx_frame_info_t *info = (retro_ctx_frame_info_t*)data; if (!info || !retro_ctx.frame_cb) return false; retro_ctx.frame_cb( info->data, info->width, info->height, info->pitch); } break; case CORE_CTL_RETRO_CTX_POLL_CB: if (!retro_ctx.poll_cb) return false; retro_ctx.poll_cb(); break; case CORE_CTL_RETRO_SET_ENVIRONMENT: { retro_ctx_environ_info_t *info = (retro_ctx_environ_info_t*)data; if (!info) return false; core.retro_set_environment(info->env); } break; case CORE_CTL_RETRO_GET_SYSTEM_AV_INFO: { struct retro_system_av_info *av_info = (struct retro_system_av_info*)data; if (!av_info) return false; core.retro_get_system_av_info(av_info); } break; case CORE_CTL_RETRO_RESET: core.retro_reset(); break; case CORE_CTL_RETRO_INIT: core.retro_init(); break; case CORE_CTL_RETRO_DEINIT: core.retro_deinit(); uninit_libretro_sym(&core); break; case CORE_CTL_RETRO_UNLOAD_GAME: video_driver_ctl(RARCH_DISPLAY_CTL_DEINIT_HW_CONTEXT, NULL); audio_driver_ctl(RARCH_AUDIO_CTL_STOP, NULL); core.retro_unload_game(); break; case CORE_CTL_RETRO_RUN: switch (core_poll_type) { case POLL_TYPE_EARLY: input_poll(); break; case POLL_TYPE_LATE: core_input_polled = false; break; } if (core.retro_run) core.retro_run(); if (core_poll_type == POLL_TYPE_LATE && !core_input_polled) input_poll(); break; case CORE_CTL_SET_CBS: return retro_set_default_callbacks(data); case CORE_CTL_SET_CBS_REWIND: retro_set_rewind_callbacks(); break; case CORE_CTL_INIT: { settings_t *settings = config_get_ptr(); core_poll_type = settings->input.poll_type_behavior; if (!core_ctl(CORE_CTL_VERIFY_API_VERSION, NULL)) return false; if (!retro_init_libretro_cbs(&retro_ctx)) return false; core_ctl(CORE_CTL_RETRO_GET_SYSTEM_AV_INFO, video_viewport_get_system_av_info()); runloop_ctl(RUNLOOP_CTL_SET_FRAME_LIMIT, NULL); } break; case CORE_CTL_DEINIT: return retro_uninit_libretro_cbs(&retro_ctx); case CORE_CTL_VERIFY_API_VERSION: { unsigned api_version = core.retro_api_version(); RARCH_LOG("Version of libretro API: %u\n", api_version); RARCH_LOG("Compiled against API: %u\n", RETRO_API_VERSION); if (api_version != RETRO_API_VERSION) { RARCH_WARN("%s\n", msg_hash_to_str(MSG_LIBRETRO_ABI_BREAK)); return false; } } break; case CORE_CTL_HAS_SET_INPUT_DESCRIPTORS: return has_set_input_descriptors; case CORE_CTL_SET_INPUT_DESCRIPTORS: has_set_input_descriptors = true; break; case CORE_CTL_UNSET_INPUT_DESCRIPTORS: has_set_input_descriptors = false; break; case CORE_CTL_NONE: default: break; } 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 retro_keyboard_event_t runloop_frontend_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 msg_queue_t *runloop_msg_queue = NULL; settings_t *settings = config_get_ptr(); switch (state) { case RUNLOOP_CTL_DATA_ITERATE: task_queue_ctl(TASK_QUEUE_CTL_CHECK, NULL); break; 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_ctl(CORE_CTL_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"; 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)); 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); } break; case RUNLOOP_CTL_HAS_CORE_OPTIONS: return runloop_system.core_options; 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: 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; runloop_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; } 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_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); break; 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_cmd_ctl(EVENT_CMD_TAKE_SCREENSHOT, NULL); if (runloop_cmd_triggered(cmd, RARCH_MUTE)) event_cmd_ctl(EVENT_CMD_AUDIO_MUTE_TOGGLE, NULL); 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_cmd_ctl(EVENT_CMD_VOLUME_UP, NULL); else if (runloop_cmd_press(cmd, RARCH_VOLUME_DOWN)) event_cmd_ctl(EVENT_CMD_VOLUME_DOWN, NULL); #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_cmd_ctl(EVENT_CMD_SAVE_STATE, NULL); else if (runloop_cmd_triggered(cmd, RARCH_LOAD_STATE_KEY)) event_cmd_ctl(EVENT_CMD_LOAD_STATE, NULL); 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_cmd_ctl(EVENT_CMD_DISK_EJECT_TOGGLE, NULL); else if (runloop_cmd_triggered(cmd, RARCH_DISK_NEXT)) event_cmd_ctl(EVENT_CMD_DISK_NEXT, NULL); else if (runloop_cmd_triggered(cmd, RARCH_DISK_PREV)) event_cmd_ctl(EVENT_CMD_DISK_PREV, NULL); if (runloop_cmd_triggered(cmd, RARCH_RESET)) event_cmd_ctl(EVENT_CMD_RESET, NULL); 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_cmd_ctl(EVENT_CMD_FULLSCREEN_TOGGLE, NULL); 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(msg_hash_to_str(MSG_SLOW_MOTION_REWIND), 0, 30, true); else runloop_msg_queue_push(msg_hash_to_str(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( msg_hash_to_str(MSG_MOVIE_RECORD_STOPPED), 2, 180, true); RARCH_LOG("%s\n", msg_hash_to_str(MSG_MOVIE_RECORD_STOPPED)); event_cmd_ctl(EVENT_CMD_BSV_MOVIE_DEINIT, NULL); 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( msg_hash_to_str(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( msg_hash_to_str(MSG_MOVIE_PLAYBACK_ENDED), 1, 180, false); RARCH_LOG("%s\n", msg_hash_to_str(MSG_MOVIE_PLAYBACK_ENDED)); event_cmd_ctl(EVENT_CMD_BSV_MOVIE_DEINIT, NULL); 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 = NULL; event_cmd_ctl(EVENT_CMD_TEMPORARY_CONTENT_DEINIT, NULL); event_cmd_ctl(EVENT_CMD_SUBSYSTEM_FULLPATHS_DEINIT, NULL); event_cmd_ctl(EVENT_CMD_RECORD_DEINIT, NULL); event_cmd_ctl(EVENT_CMD_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; 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_PUSH: { runloop_ctx_msg_info_t *msg_info = (runloop_ctx_msg_info_t*)data; if (!msg_info || !runloop_msg_queue) return false; msg_queue_push(runloop_msg_queue, msg_info->msg, msg_info->prio, msg_info->duration); if (ui_companion_is_on_foreground()) { const ui_companion_driver_t *ui = ui_companion_get_ptr(); if (ui->msg_queue_push) ui->msg_queue_push(msg_info->msg, msg_info->prio, msg_info->duration, msg_info->flush); } } break; case RUNLOOP_CTL_MSG_QUEUE_PULL: { const char **ret = (const char**)data; if (!ret) return false; *ret = msg_queue_pull(runloop_msg_queue); } 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_ctl(RUNLOOP_CTL_MSG_QUEUE_LOCK, NULL); msg_queue_free(runloop_msg_queue); runloop_ctl(RUNLOOP_CTL_MSG_QUEUE_UNLOCK, NULL); 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_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_TASK_INIT: { bool threaded_enable = false; #ifdef HAVE_THREADS threaded_enable = settings->threaded_data_runloop_enable; #endif task_queue_ctl(TASK_QUEUE_CTL_INIT, &threaded_enable); } break; case RUNLOOP_CTL_PREPARE_DUMMY: #ifdef HAVE_MENU menu_driver_ctl(RARCH_MENU_CTL_UNSET_LOAD_NO_CONTENT, NULL); #endif runloop_ctl(RUNLOOP_CTL_DATA_DEINIT, NULL); runloop_ctl(RUNLOOP_CTL_TASK_INIT, NULL); 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: task_queue_ctl(TASK_QUEUE_CTL_DEINIT, NULL); break; case RUNLOOP_CTL_IS_CORE_OPTION_UPDATED: if (!runloop_system.core_options) return false; return core_option_updated(runloop_system.core_options); 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(); } break; 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(); } break; 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"); } 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(); const char *options_path = settings->core_options_path; const struct retro_variable *vars = (const struct retro_variable*)data; 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; /* check if game options file was just created and flush to that file instead */ if(!string_is_empty(runloop_system.game_options_path)) { core_option_flush_game_specific(runloop_system.core_options, runloop_system.game_options_path); runloop_system.game_options_path[0] = '\0'; } else core_option_flush(runloop_system.core_options); core_option_free(runloop_system.core_options); if (runloop_ctl(RUNLOOP_CTL_IS_GAME_OPTIONS_ACTIVE, NULL)) runloop_ctl(RUNLOOP_CTL_UNSET_GAME_OPTIONS_ACTIVE, NULL); runloop_system.core_options = 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_NONE: default: break; } return true; }
/** * runloop_iterate: * * Run Libretro core in RetroArch for one frame. * * Returns: 0 on success, 1 if we have to wait until * button input in order to wake up the loop, * -1 if we forcibly quit out of the RetroArch iteration loop. **/ int runloop_iterate(unsigned *sleep_ms) { unsigned i; event_cmd_state_t cmd; event_cmd_state_t *cmd_ptr = &cmd; retro_time_t current, target, to_sleep_ms; static retro_usec_t frame_time_last = 0; static retro_time_t frame_limit_minimum_time = 0.0; static retro_time_t frame_limit_last_time = 0.0; static retro_input_t last_input = 0; settings_t *settings = config_get_ptr(); rarch_system_info_t *system = NULL; cmd.state[1] = last_input; cmd.state[0] = input_keys_pressed(); last_input = cmd.state[0]; runloop_ctl(RUNLOOP_CTL_SYSTEM_INFO_GET, &system); if (runloop_ctl(RUNLOOP_CTL_IS_FRAME_TIME_LAST, NULL)) { frame_time_last = 0; runloop_ctl(RUNLOOP_CTL_UNSET_FRAME_TIME_LAST, NULL); } if (runloop_ctl(RUNLOOP_CTL_SHOULD_SET_FRAME_LIMIT, NULL)) { struct retro_system_av_info *av_info = video_viewport_get_system_av_info(); float fastforward_ratio = (settings->fastforward_ratio == 0.0f) ? 1.0f : settings->fastforward_ratio; frame_limit_last_time = retro_get_time_usec(); frame_limit_minimum_time = (retro_time_t)roundf(1000000.0f / (av_info->timing.fps * fastforward_ratio)); runloop_ctl(RUNLOOP_CTL_UNSET_FRAME_LIMIT, NULL); } if (input_driver_ctl(RARCH_INPUT_CTL_IS_FLUSHING_INPUT, NULL)) { input_driver_ctl(RARCH_INPUT_CTL_UNSET_FLUSHING_INPUT, NULL); if (cmd.state[0]) { cmd.state[0] = 0; /* If core was paused before entering menu, evoke * pause toggle to wake it up. */ if (runloop_ctl(RUNLOOP_CTL_IS_PAUSED, NULL)) BIT64_SET(cmd.state[0], RARCH_PAUSE_TOGGLE); input_driver_ctl(RARCH_INPUT_CTL_SET_FLUSHING_INPUT, NULL); } } if (system->frame_time.callback) { /* Updates frame timing if frame timing callback is in use by the core. * Limits frame time if fast forward ratio throttle is enabled. */ bool is_slowmotion; retro_time_t current = retro_get_time_usec(); retro_time_t delta = current - frame_time_last; bool is_locked_fps = (runloop_ctl(RUNLOOP_CTL_IS_PAUSED, NULL) || input_driver_ctl(RARCH_INPUT_CTL_IS_NONBLOCK_STATE, NULL)) | !!recording_driver_get_data_ptr(); runloop_ctl(RUNLOOP_CTL_IS_SLOWMOTION, &is_slowmotion); if (!frame_time_last || is_locked_fps) delta = system->frame_time.reference; if (!is_locked_fps && is_slowmotion) delta /= settings->slowmotion_ratio; frame_time_last = current; if (is_locked_fps) frame_time_last = 0; system->frame_time.callback(delta); } cmd.state[2] = cmd.state[0] & ~cmd.state[1]; /* trigger */ if (runloop_cmd_triggered(cmd_ptr, RARCH_OVERLAY_NEXT)) event_cmd_ctl(EVENT_CMD_OVERLAY_NEXT, NULL); if (runloop_cmd_triggered(cmd_ptr, RARCH_FULLSCREEN_TOGGLE_KEY)) { bool fullscreen_toggled = !runloop_ctl(RUNLOOP_CTL_IS_PAUSED, NULL); #ifdef HAVE_MENU fullscreen_toggled = fullscreen_toggled || menu_driver_ctl(RARCH_MENU_CTL_IS_ALIVE, NULL); #endif if (fullscreen_toggled) event_cmd_ctl(EVENT_CMD_FULLSCREEN_TOGGLE, NULL); } if (runloop_cmd_triggered(cmd_ptr, RARCH_GRAB_MOUSE_TOGGLE)) event_cmd_ctl(EVENT_CMD_GRAB_MOUSE_TOGGLE, NULL); #ifdef HAVE_MENU if (runloop_cmd_menu_press(cmd_ptr) || rarch_ctl(RARCH_CTL_IS_DUMMY_CORE, NULL)) { if (menu_driver_ctl(RARCH_MENU_CTL_IS_ALIVE, NULL)) { if (rarch_ctl(RARCH_CTL_IS_INITED, NULL) && !rarch_ctl(RARCH_CTL_IS_DUMMY_CORE, NULL)) rarch_ctl(RARCH_CTL_MENU_RUNNING_FINISHED, NULL); } else rarch_ctl(RARCH_CTL_MENU_RUNNING, NULL); } #endif #ifdef HAVE_OVERLAY runloop_iterate_linefeed_overlay(settings); #endif if (runloop_iterate_time_to_exit( runloop_cmd_press(cmd_ptr, RARCH_QUIT_KEY)) != 1) { frame_limit_last_time = 0.0; return -1; } #ifdef HAVE_MENU if (menu_driver_ctl(RARCH_MENU_CTL_IS_ALIVE, NULL)) { menu_ctx_iterate_t iter; bool focused = runloop_ctl(RUNLOOP_CTL_CHECK_FOCUS, NULL) && !ui_companion_is_on_foreground(); bool is_idle = runloop_ctl(RUNLOOP_CTL_IS_IDLE, NULL); enum menu_action action = (enum menu_action) menu_input_frame_retropad(cmd.state[0], cmd.state[2]); iter.action = action; if (!menu_driver_ctl(RARCH_MENU_CTL_ITERATE, &iter)) rarch_ctl(RARCH_CTL_MENU_RUNNING_FINISHED, NULL); if (focused || !is_idle) menu_driver_ctl(RARCH_MENU_CTL_RENDER, NULL); if (!focused || is_idle) { *sleep_ms = 10; return 1; } goto end; } #endif if (!runloop_ctl(RUNLOOP_CTL_CHECK_STATE, &cmd)) { /* RetroArch has been paused. */ core_ctl(CORE_CTL_RETRO_CTX_POLL_CB, NULL); *sleep_ms = 10; return 1; } #if defined(HAVE_THREADS) lock_autosave(); #endif #ifdef HAVE_NETPLAY netplay_driver_ctl(RARCH_NETPLAY_CTL_PRE_FRAME, NULL); #endif if (bsv_movie_ctl(BSV_MOVIE_CTL_IS_INITED, NULL)) bsv_movie_ctl(BSV_MOVIE_CTL_SET_FRAME_START, NULL); if (system->camera_callback.caps) driver_camera_poll(); /* Update binds for analog dpad modes. */ for (i = 0; i < settings->input.max_users; i++) { if (!settings->input.analog_dpad_mode[i]) continue; input_push_analog_dpad(settings->input.binds[i], settings->input.analog_dpad_mode[i]); input_push_analog_dpad(settings->input.autoconf_binds[i], settings->input.analog_dpad_mode[i]); } if ((settings->video.frame_delay > 0) && !input_driver_ctl(RARCH_INPUT_CTL_IS_NONBLOCK_STATE, NULL)) retro_sleep(settings->video.frame_delay); core_ctl(CORE_CTL_RETRO_RUN, NULL); #ifdef HAVE_CHEEVOS /* Test the achievements. */ cheevos_test(); #endif for (i = 0; i < settings->input.max_users; i++) { if (!settings->input.analog_dpad_mode[i]) continue; input_pop_analog_dpad(settings->input.binds[i]); input_pop_analog_dpad(settings->input.autoconf_binds[i]); } if (bsv_movie_ctl(BSV_MOVIE_CTL_IS_INITED, NULL)) bsv_movie_ctl(BSV_MOVIE_CTL_SET_FRAME_END, NULL); #ifdef HAVE_NETPLAY netplay_driver_ctl(RARCH_NETPLAY_CTL_POST_FRAME, NULL); #endif #if defined(HAVE_THREADS) unlock_autosave(); #endif #ifdef HAVE_MENU end: #endif if (!settings->fastforward_ratio) return 0; current = retro_get_time_usec(); target = frame_limit_last_time + frame_limit_minimum_time; to_sleep_ms = (target - current) / 1000; if (to_sleep_ms > 0) { *sleep_ms = (unsigned)to_sleep_ms; /* Combat jitter a bit. */ frame_limit_last_time += frame_limit_minimum_time; return 1; } frame_limit_last_time = retro_get_time_usec(); return 0; }
/** * event_init_controllers: * * Initialize libretro controllers. **/ static void event_init_controllers(void) { unsigned i; settings_t *settings = config_get_ptr(); rarch_system_info_t *info = NULL; runloop_ctl(RUNLOOP_CTL_SYSTEM_INFO_GET, &info); for (i = 0; i < MAX_USERS; i++) { retro_ctx_controller_info_t pad; const char *ident = NULL; bool set_controller = false; const struct retro_controller_description *desc = NULL; unsigned device = settings->input.libretro_device[i]; if (i < info->ports.size) desc = libretro_find_controller_description( &info->ports.data[i], device); if (desc) ident = desc->desc; if (!ident) { /* If we're trying to connect a completely unknown device, * revert back to JOYPAD. */ if (device != RETRO_DEVICE_JOYPAD && device != RETRO_DEVICE_NONE) { /* Do not fix settings->input.libretro_device[i], * because any use of dummy core will reset this, * which is not a good idea. */ RARCH_WARN("Input device ID %u is unknown to this " "libretro implementation. Using RETRO_DEVICE_JOYPAD.\n", device); device = RETRO_DEVICE_JOYPAD; } ident = "Joypad"; } switch (device) { case RETRO_DEVICE_NONE: RARCH_LOG("Disconnecting device from port %u.\n", i + 1); set_controller = true; break; case RETRO_DEVICE_JOYPAD: break; default: /* Some cores do not properly range check port argument. * This is broken behavior of course, but avoid breaking * cores needlessly. */ RARCH_LOG("Connecting %s (ID: %u) to port %u.\n", ident, device, i + 1); set_controller = true; break; } if (set_controller) { pad.device = device; pad.port = i; core_ctl(CORE_CTL_RETRO_SET_CONTROLLER_PORT_DEVICE, &pad); } } }
/** * event_cmd_ctl: * @cmd : Event command index. * * Performs program event command with index @cmd. * * Returns: true (1) on success, otherwise false (0). **/ bool event_cmd_ctl(enum event_command cmd, void *data) { unsigned i = 0; bool boolean = false; settings_t *settings = config_get_ptr(); rarch_system_info_t *info = NULL; runloop_ctl(RUNLOOP_CTL_SYSTEM_INFO_GET, &info); (void)i; switch (cmd) { case EVENT_CMD_MENU_REFRESH: #ifdef HAVE_MENU menu_driver_ctl(RARCH_MENU_CTL_REFRESH, NULL); #endif break; case EVENT_CMD_SET_PER_GAME_RESOLUTION: #if defined(GEKKO) { unsigned width = 0, height = 0; event_cmd_ctl(EVENT_CMD_VIDEO_SET_ASPECT_RATIO, NULL); if (video_driver_get_video_output_size(&width, &height)) { char msg[128] = {0}; video_driver_set_video_mode(width, height, true); if (width == 0 || height == 0) strlcpy(msg, "Resolution: DEFAULT", sizeof(msg)); else snprintf(msg, sizeof(msg),"Resolution: %dx%d",width, height); runloop_msg_queue_push(msg, 1, 100, true); } } #endif break; case EVENT_CMD_LOAD_CONTENT_PERSIST: #ifdef HAVE_DYNAMIC event_cmd_ctl(EVENT_CMD_LOAD_CORE, NULL); #endif rarch_ctl(RARCH_CTL_LOAD_CONTENT, NULL); break; #ifdef HAVE_FFMPEG case EVENT_CMD_LOAD_CONTENT_FFMPEG: rarch_ctl(RARCH_CTL_LOAD_CONTENT_FFMPEG, NULL); break; #endif case EVENT_CMD_LOAD_CONTENT_IMAGEVIEWER: rarch_ctl(RARCH_CTL_LOAD_CONTENT_IMAGEVIEWER, NULL); break; case EVENT_CMD_LOAD_CONTENT: { #ifdef HAVE_DYNAMIC event_cmd_ctl(EVENT_CMD_LOAD_CONTENT_PERSIST, NULL); #else char *fullpath = NULL; runloop_ctl(RUNLOOP_CTL_GET_CONTENT_PATH, &fullpath); runloop_ctl(RUNLOOP_CTL_SET_LIBRETRO_PATH, settings->libretro); event_cmd_ctl(EVENT_CMD_EXEC, (void*)fullpath); event_cmd_ctl(EVENT_CMD_QUIT, NULL); #endif } break; case EVENT_CMD_LOAD_CORE_DEINIT: #ifdef HAVE_MENU menu_driver_ctl(RARCH_MENU_CTL_SYSTEM_INFO_DEINIT, NULL); #endif break; case EVENT_CMD_LOAD_CORE_PERSIST: event_cmd_ctl(EVENT_CMD_LOAD_CORE_DEINIT, NULL); { #ifdef HAVE_MENU bool *ptr = NULL; struct retro_system_info *system = NULL; menu_driver_ctl(RARCH_MENU_CTL_SYSTEM_INFO_GET, &system); if (menu_driver_ctl(RARCH_MENU_CTL_LOAD_NO_CONTENT_GET, &ptr)) { core_info_ctx_find_t info_find; #if defined(HAVE_DYNAMIC) if (!(*settings->libretro)) return false; libretro_get_system_info(settings->libretro, system, ptr); #endif info_find.path = settings->libretro; if (!core_info_ctl(CORE_INFO_CTL_LOAD, &info_find)) return false; } #endif } break; case EVENT_CMD_LOAD_CORE: event_cmd_ctl(EVENT_CMD_LOAD_CORE_PERSIST, NULL); #ifndef HAVE_DYNAMIC event_cmd_ctl(EVENT_CMD_QUIT, NULL); #endif break; case EVENT_CMD_LOAD_STATE: /* Immutable - disallow savestate load when * we absolutely cannot change game state. */ if (bsv_movie_ctl(BSV_MOVIE_CTL_IS_INITED, NULL)) return false; #ifdef HAVE_NETPLAY if (netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_DATA_INITED, NULL)) return false; #endif #ifdef HAVE_CHEEVOS if (settings->cheevos.hardcore_mode_enable) return false; #endif event_main_state(cmd); break; case EVENT_CMD_RESIZE_WINDOWED_SCALE: { unsigned idx = 0; unsigned *window_scale = NULL; runloop_ctl(RUNLOOP_CTL_GET_WINDOWED_SCALE, &window_scale); if (*window_scale == 0) return false; settings->video.scale = *window_scale; if (!settings->video.fullscreen) event_cmd_ctl(EVENT_CMD_REINIT, NULL); runloop_ctl(RUNLOOP_CTL_SET_WINDOWED_SCALE, &idx); } break; case EVENT_CMD_MENU_TOGGLE: #ifdef HAVE_MENU if (menu_driver_ctl(RARCH_MENU_CTL_IS_ALIVE, NULL)) rarch_ctl(RARCH_CTL_MENU_RUNNING_FINISHED, NULL); else rarch_ctl(RARCH_CTL_MENU_RUNNING, NULL); #endif break; case EVENT_CMD_CONTROLLERS_INIT: event_init_controllers(); break; case EVENT_CMD_RESET: RARCH_LOG("%s.\n", msg_hash_to_str(MSG_RESET)); runloop_msg_queue_push(msg_hash_to_str(MSG_RESET), 1, 120, true); #ifdef HAVE_CHEEVOS cheevos_ctl(CHEEVOS_CTL_SET_CHEATS, NULL); #endif core_ctl(CORE_CTL_RETRO_RESET, NULL); break; case EVENT_CMD_SAVE_STATE: #ifdef HAVE_CHEEVOS if (settings->cheevos.hardcore_mode_enable) return false; #endif if (settings->savestate_auto_index) settings->state_slot++; event_main_state(cmd); break; case EVENT_CMD_SAVE_STATE_DECREMENT: /* Slot -1 is (auto) slot. */ if (settings->state_slot >= 0) settings->state_slot--; break; case EVENT_CMD_SAVE_STATE_INCREMENT: settings->state_slot++; break; case EVENT_CMD_TAKE_SCREENSHOT: if (!take_screenshot()) return false; break; case EVENT_CMD_UNLOAD_CORE: runloop_ctl(RUNLOOP_CTL_PREPARE_DUMMY, NULL); event_cmd_ctl(EVENT_CMD_LOAD_CORE_DEINIT, NULL); break; case EVENT_CMD_QUIT: rarch_ctl(RARCH_CTL_QUIT, NULL); break; case EVENT_CMD_CHEEVOS_HARDCORE_MODE_TOGGLE: #ifdef HAVE_CHEEVOS cheevos_ctl(CHEEVOS_CTL_TOGGLE_HARDCORE_MODE, NULL); #endif break; case EVENT_CMD_REINIT: { struct retro_hw_render_callback *hwr = NULL; video_driver_ctl(RARCH_DISPLAY_CTL_HW_CONTEXT_GET, &hwr); if (hwr->cache_context) video_driver_ctl( RARCH_DISPLAY_CTL_SET_VIDEO_CACHE_CONTEXT, NULL); else video_driver_ctl( RARCH_DISPLAY_CTL_UNSET_VIDEO_CACHE_CONTEXT, NULL); video_driver_ctl( RARCH_DISPLAY_CTL_UNSET_VIDEO_CACHE_CONTEXT_ACK, NULL); event_cmd_ctl(EVENT_CMD_RESET_CONTEXT, NULL); video_driver_ctl( RARCH_DISPLAY_CTL_UNSET_VIDEO_CACHE_CONTEXT, NULL); /* Poll input to avoid possibly stale data to corrupt things. */ input_driver_ctl(RARCH_INPUT_CTL_POLL, NULL); #ifdef HAVE_MENU menu_display_ctl( MENU_DISPLAY_CTL_SET_FRAMEBUFFER_DIRTY_FLAG, NULL); if (menu_driver_ctl(RARCH_MENU_CTL_IS_ALIVE, NULL)) event_cmd_ctl(EVENT_CMD_VIDEO_SET_BLOCKING_STATE, NULL); #endif } break; case EVENT_CMD_CHEATS_DEINIT: cheat_manager_state_free(); break; case EVENT_CMD_CHEATS_INIT: event_cmd_ctl(EVENT_CMD_CHEATS_DEINIT, NULL); event_init_cheats(); break; case EVENT_CMD_CHEATS_APPLY: cheat_manager_apply_cheats(); break; case EVENT_CMD_REWIND_DEINIT: #ifdef HAVE_NETPLAY if (netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_DATA_INITED, NULL)) return false; #endif #ifdef HAVE_CHEEVOS if (settings->cheevos.hardcore_mode_enable) return false; #endif state_manager_event_deinit(); break; case EVENT_CMD_REWIND_INIT: #ifdef HAVE_CHEEVOS if (settings->cheevos.hardcore_mode_enable) return false; #endif #ifdef HAVE_NETPLAY if (!netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_DATA_INITED, NULL)) #endif init_rewind(); break; case EVENT_CMD_REWIND_TOGGLE: if (settings->rewind_enable) event_cmd_ctl(EVENT_CMD_REWIND_INIT, NULL); else event_cmd_ctl(EVENT_CMD_REWIND_DEINIT, NULL); break; case EVENT_CMD_AUTOSAVE_DEINIT: #ifdef HAVE_THREADS autosave_event_deinit(); #endif break; case EVENT_CMD_AUTOSAVE_INIT: event_cmd_ctl(EVENT_CMD_AUTOSAVE_DEINIT, NULL); #ifdef HAVE_THREADS autosave_event_init(); #endif break; case EVENT_CMD_AUTOSAVE_STATE: event_save_auto_state(); break; case EVENT_CMD_AUDIO_STOP: if (!audio_driver_ctl(RARCH_AUDIO_CTL_ALIVE, NULL)) return false; if (!audio_driver_ctl(RARCH_AUDIO_CTL_STOP, NULL)) return false; break; case EVENT_CMD_AUDIO_START: if (audio_driver_ctl(RARCH_AUDIO_CTL_ALIVE, NULL)) return false; if (!settings->audio.mute_enable && !audio_driver_ctl(RARCH_AUDIO_CTL_START, NULL)) { RARCH_ERR("Failed to start audio driver. " "Will continue without audio.\n"); audio_driver_ctl(RARCH_AUDIO_CTL_UNSET_ACTIVE, NULL); } break; case EVENT_CMD_AUDIO_MUTE_TOGGLE: { const char *msg = !settings->audio.mute_enable ? msg_hash_to_str(MSG_AUDIO_MUTED): msg_hash_to_str(MSG_AUDIO_UNMUTED); if (!audio_driver_ctl(RARCH_AUDIO_CTL_MUTE_TOGGLE, NULL)) { RARCH_ERR("%s.\n", msg_hash_to_str(MSG_FAILED_TO_UNMUTE_AUDIO)); return false; } runloop_msg_queue_push(msg, 1, 180, true); RARCH_LOG("%s\n", msg); } break; case EVENT_CMD_OVERLAY_DEINIT: #ifdef HAVE_OVERLAY input_overlay_free(); #endif break; case EVENT_CMD_OVERLAY_INIT: event_cmd_ctl(EVENT_CMD_OVERLAY_DEINIT, NULL); #ifdef HAVE_OVERLAY input_overlay_init(); #endif break; case EVENT_CMD_OVERLAY_NEXT: #ifdef HAVE_OVERLAY input_overlay_next(settings->input.overlay_opacity); #endif break; case EVENT_CMD_DSP_FILTER_DEINIT: audio_driver_dsp_filter_free(); break; case EVENT_CMD_DSP_FILTER_INIT: event_cmd_ctl(EVENT_CMD_DSP_FILTER_DEINIT, NULL); if (!*settings->audio.dsp_plugin) break; audio_driver_dsp_filter_init(settings->audio.dsp_plugin); break; case EVENT_CMD_GPU_RECORD_DEINIT: video_driver_ctl(RARCH_DISPLAY_CTL_GPU_RECORD_DEINIT, NULL); break; case EVENT_CMD_RECORD_DEINIT: if (!recording_deinit()) return false; break; case EVENT_CMD_RECORD_INIT: event_cmd_ctl(EVENT_CMD_HISTORY_DEINIT, NULL); if (!recording_init()) return false; break; case EVENT_CMD_HISTORY_DEINIT: if (g_defaults.history) { content_playlist_write_file(g_defaults.history); content_playlist_free(g_defaults.history); } g_defaults.history = NULL; break; case EVENT_CMD_HISTORY_INIT: event_cmd_ctl(EVENT_CMD_HISTORY_DEINIT, NULL); if (!settings->history_list_enable) return false; RARCH_LOG("%s: [%s].\n", msg_hash_to_str(MSG_LOADING_HISTORY_FILE), settings->content_history_path); g_defaults.history = content_playlist_init( settings->content_history_path, settings->content_history_size); break; case EVENT_CMD_CORE_INFO_DEINIT: core_info_ctl(CORE_INFO_CTL_LIST_DEINIT, NULL); break; case EVENT_CMD_CORE_INFO_INIT: event_cmd_ctl(EVENT_CMD_CORE_INFO_DEINIT, NULL); if (*settings->libretro_directory) core_info_ctl(CORE_INFO_CTL_LIST_INIT, NULL); break; case EVENT_CMD_CORE_DEINIT: { struct retro_hw_render_callback *hwr = NULL; video_driver_ctl(RARCH_DISPLAY_CTL_HW_CONTEXT_GET, &hwr); event_deinit_core(true); if (hwr) memset(hwr, 0, sizeof(*hwr)); break; } case EVENT_CMD_CORE_INIT: if (!event_init_core(data)) return false; break; case EVENT_CMD_VIDEO_APPLY_STATE_CHANGES: video_driver_ctl(RARCH_DISPLAY_CTL_APPLY_STATE_CHANGES, NULL); break; case EVENT_CMD_VIDEO_SET_NONBLOCKING_STATE: boolean = true; /* fall-through */ case EVENT_CMD_VIDEO_SET_BLOCKING_STATE: video_driver_ctl(RARCH_DISPLAY_CTL_SET_NONBLOCK_STATE, &boolean); break; case EVENT_CMD_VIDEO_SET_ASPECT_RATIO: video_driver_ctl(RARCH_DISPLAY_CTL_SET_ASPECT_RATIO, NULL); break; case EVENT_CMD_AUDIO_SET_NONBLOCKING_STATE: boolean = true; /* fall-through */ case EVENT_CMD_AUDIO_SET_BLOCKING_STATE: audio_driver_set_nonblocking_state(boolean); break; case EVENT_CMD_OVERLAY_SET_SCALE_FACTOR: #ifdef HAVE_OVERLAY input_overlay_set_scale_factor(settings->input.overlay_scale); #endif break; case EVENT_CMD_OVERLAY_SET_ALPHA_MOD: #ifdef HAVE_OVERLAY input_overlay_set_alpha_mod(settings->input.overlay_opacity); #endif break; case EVENT_CMD_AUDIO_REINIT: { int flags = DRIVER_AUDIO; driver_ctl(RARCH_DRIVER_CTL_UNINIT, &flags); driver_ctl(RARCH_DRIVER_CTL_INIT, &flags); } break; case EVENT_CMD_RESET_CONTEXT: { /* RARCH_DRIVER_CTL_UNINIT clears the callback struct so we * need to make sure to keep a copy */ struct retro_hw_render_callback *hwr = NULL; struct retro_hw_render_callback hwr_copy; int flags = DRIVERS_CMD_ALL; video_driver_ctl(RARCH_DISPLAY_CTL_HW_CONTEXT_GET, &hwr); memcpy(&hwr_copy, hwr, sizeof(hwr_copy)); driver_ctl(RARCH_DRIVER_CTL_UNINIT, &flags); memcpy(hwr, &hwr_copy, sizeof(*hwr)); driver_ctl(RARCH_DRIVER_CTL_INIT, &flags); } break; case EVENT_CMD_QUIT_RETROARCH: rarch_ctl(RARCH_CTL_FORCE_QUIT, NULL); break; case EVENT_CMD_SHUTDOWN: #if defined(__linux__) && !defined(ANDROID) runloop_msg_queue_push("Shutting down...", 1, 180, true); rarch_ctl(RARCH_CTL_FORCE_QUIT, NULL); system("shutdown -P now"); #endif break; case EVENT_CMD_REBOOT: #if defined(__linux__) && !defined(ANDROID) runloop_msg_queue_push("Rebooting...", 1, 180, true); rarch_ctl(RARCH_CTL_FORCE_QUIT, NULL); system("shutdown -r now"); #endif break; case EVENT_CMD_RESUME: rarch_ctl(RARCH_CTL_MENU_RUNNING_FINISHED, NULL); if (ui_companion_is_on_foreground()) ui_companion_driver_toggle(); break; case EVENT_CMD_RESTART_RETROARCH: if (!frontend_driver_set_fork(FRONTEND_FORK_RESTART)) return false; break; case EVENT_CMD_MENU_SAVE_CURRENT_CONFIG: event_save_current_config(); break; case EVENT_CMD_MENU_SAVE_CONFIG: if (!event_save_core_config()) return false; break; case EVENT_CMD_SHADERS_APPLY_CHANGES: #ifdef HAVE_MENU menu_shader_manager_apply_changes(); #endif break; case EVENT_CMD_PAUSE_CHECKS: if (runloop_ctl(RUNLOOP_CTL_IS_PAUSED, NULL)) { RARCH_LOG("%s\n", msg_hash_to_str(MSG_PAUSED)); event_cmd_ctl(EVENT_CMD_AUDIO_STOP, NULL); if (settings->video.black_frame_insertion) video_driver_ctl(RARCH_DISPLAY_CTL_CACHED_FRAME_RENDER, NULL); } else { RARCH_LOG("%s\n", msg_hash_to_str(MSG_UNPAUSED)); event_cmd_ctl(EVENT_CMD_AUDIO_START, NULL); } break; case EVENT_CMD_PAUSE_TOGGLE: boolean = runloop_ctl(RUNLOOP_CTL_IS_PAUSED, NULL); boolean = !boolean; runloop_ctl(RUNLOOP_CTL_SET_PAUSED, &boolean); event_cmd_ctl(EVENT_CMD_PAUSE_CHECKS, NULL); break; case EVENT_CMD_UNPAUSE: boolean = false; runloop_ctl(RUNLOOP_CTL_SET_PAUSED, &boolean); event_cmd_ctl(EVENT_CMD_PAUSE_CHECKS, NULL); break; case EVENT_CMD_PAUSE: boolean = true; runloop_ctl(RUNLOOP_CTL_SET_PAUSED, &boolean); event_cmd_ctl(EVENT_CMD_PAUSE_CHECKS, NULL); break; case EVENT_CMD_MENU_PAUSE_LIBRETRO: #ifdef HAVE_MENU if (menu_driver_ctl(RARCH_MENU_CTL_IS_ALIVE, NULL)) { if (settings->menu.pause_libretro) event_cmd_ctl(EVENT_CMD_AUDIO_STOP, NULL); else event_cmd_ctl(EVENT_CMD_AUDIO_START, NULL); } else { if (settings->menu.pause_libretro) event_cmd_ctl(EVENT_CMD_AUDIO_START, NULL); } #endif break; case EVENT_CMD_SHADER_DIR_DEINIT: runloop_ctl(RUNLOOP_CTL_SHADER_DIR_DEINIT, NULL); break; case EVENT_CMD_SHADER_DIR_INIT: event_cmd_ctl(EVENT_CMD_SHADER_DIR_DEINIT, NULL); if (!runloop_ctl(RUNLOOP_CTL_SHADER_DIR_INIT, NULL)) return false; break; case EVENT_CMD_SAVEFILES: { global_t *global = global_get_ptr(); if (!global->savefiles || !global->sram.use) return false; for (i = 0; i < global->savefiles->size; i++) { ram_type_t ram; ram.type = global->savefiles->elems[i].attr.i; ram.path = global->savefiles->elems[i].data; RARCH_LOG("%s #%u %s \"%s\".\n", msg_hash_to_str(MSG_SAVING_RAM_TYPE), ram.type, msg_hash_to_str(MSG_TO), ram.path); content_ctl(CONTENT_CTL_SAVE_RAM_FILE, &ram); } } return true; case EVENT_CMD_SAVEFILES_DEINIT: { global_t *global = global_get_ptr(); if (!global) break; if (global->savefiles) string_list_free(global->savefiles); global->savefiles = NULL; } break; case EVENT_CMD_SAVEFILES_INIT: { global_t *global = global_get_ptr(); global->sram.use = global->sram.use && !global->sram.save_disable; #ifdef HAVE_NETPLAY global->sram.use = global->sram.use && (!netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_DATA_INITED, NULL) || !global->netplay.is_client); #endif if (!global->sram.use) RARCH_LOG("%s\n", msg_hash_to_str(MSG_SRAM_WILL_NOT_BE_SAVED)); if (global->sram.use) event_cmd_ctl(EVENT_CMD_AUTOSAVE_INIT, NULL); } break; case EVENT_CMD_BSV_MOVIE_DEINIT: bsv_movie_ctl(BSV_MOVIE_CTL_DEINIT, NULL); break; case EVENT_CMD_BSV_MOVIE_INIT: event_cmd_ctl(EVENT_CMD_BSV_MOVIE_DEINIT, NULL); bsv_movie_ctl(BSV_MOVIE_CTL_INIT, NULL); break; case EVENT_CMD_NETPLAY_DEINIT: #ifdef HAVE_NETPLAY deinit_netplay(); #endif break; case EVENT_CMD_NETWORK_DEINIT: #ifdef HAVE_NETWORKING network_deinit(); #endif break; case EVENT_CMD_NETWORK_INIT: #ifdef HAVE_NETWORKING network_init(); #endif break; case EVENT_CMD_NETPLAY_INIT: event_cmd_ctl(EVENT_CMD_NETPLAY_DEINIT, NULL); #ifdef HAVE_NETPLAY if (!init_netplay()) return false; #endif break; case EVENT_CMD_NETPLAY_FLIP_PLAYERS: #ifdef HAVE_NETPLAY netplay_driver_ctl(RARCH_NETPLAY_CTL_FLIP_PLAYERS, NULL); #endif break; case EVENT_CMD_FULLSCREEN_TOGGLE: if (!video_driver_ctl(RARCH_DISPLAY_CTL_HAS_WINDOWED, NULL)) return false; /* If we go fullscreen we drop all drivers and * reinitialize to be safe. */ settings->video.fullscreen = !settings->video.fullscreen; event_cmd_ctl(EVENT_CMD_REINIT, NULL); break; case EVENT_CMD_COMMAND_DEINIT: input_driver_ctl(RARCH_INPUT_CTL_COMMAND_DEINIT, NULL); break; case EVENT_CMD_COMMAND_INIT: event_cmd_ctl(EVENT_CMD_COMMAND_DEINIT, NULL); input_driver_ctl(RARCH_INPUT_CTL_COMMAND_INIT, NULL); break; case EVENT_CMD_REMOTE_DEINIT: input_driver_ctl(RARCH_INPUT_CTL_REMOTE_DEINIT, NULL); break; case EVENT_CMD_REMOTE_INIT: event_cmd_ctl(EVENT_CMD_REMOTE_DEINIT, NULL); input_driver_ctl(RARCH_INPUT_CTL_REMOTE_INIT, NULL); break; case EVENT_CMD_TEMPORARY_CONTENT_DEINIT: content_ctl(CONTENT_CTL_DEINIT, NULL); break; case EVENT_CMD_SUBSYSTEM_FULLPATHS_DEINIT: { global_t *global = global_get_ptr(); if (!global) break; if (global->subsystem_fullpaths) string_list_free(global->subsystem_fullpaths); global->subsystem_fullpaths = NULL; } break; case EVENT_CMD_LOG_FILE_DEINIT: retro_main_log_file_deinit(); break; case EVENT_CMD_DISK_APPEND_IMAGE: { const char *path = (const char*)data; if (string_is_empty(path)) return false; return event_disk_control_append_image(path); } case EVENT_CMD_DISK_EJECT_TOGGLE: if (info && info->disk_control_cb.get_num_images) { const struct retro_disk_control_callback *control = (const struct retro_disk_control_callback*) &info->disk_control_cb; if (control) { bool new_state = !control->get_eject_state(); event_disk_control_set_eject(new_state, true); } } else runloop_msg_queue_push( msg_hash_to_str(MSG_CORE_DOES_NOT_SUPPORT_DISK_OPTIONS), 1, 120, true); break; case EVENT_CMD_DISK_NEXT: if (info && info->disk_control_cb.get_num_images) { const struct retro_disk_control_callback *control = (const struct retro_disk_control_callback*) &info->disk_control_cb; if (!control) return false; if (!control->get_eject_state()) return false; event_check_disk_next(control); } else runloop_msg_queue_push( msg_hash_to_str(MSG_CORE_DOES_NOT_SUPPORT_DISK_OPTIONS), 1, 120, true); break; case EVENT_CMD_DISK_PREV: if (info && info->disk_control_cb.get_num_images) { const struct retro_disk_control_callback *control = (const struct retro_disk_control_callback*) &info->disk_control_cb; if (!control) return false; if (!control->get_eject_state()) return false; event_check_disk_prev(control); } else runloop_msg_queue_push( msg_hash_to_str(MSG_CORE_DOES_NOT_SUPPORT_DISK_OPTIONS), 1, 120, true); break; case EVENT_CMD_RUMBLE_STOP: for (i = 0; i < MAX_USERS; i++) { input_driver_set_rumble_state(i, RETRO_RUMBLE_STRONG, 0); input_driver_set_rumble_state(i, RETRO_RUMBLE_WEAK, 0); } break; case EVENT_CMD_GRAB_MOUSE_TOGGLE: { bool ret = false; static bool grab_mouse_state = false; grab_mouse_state = !grab_mouse_state; if (grab_mouse_state) ret = input_driver_ctl(RARCH_INPUT_CTL_GRAB_MOUSE, NULL); else ret = input_driver_ctl(RARCH_INPUT_CTL_UNGRAB_MOUSE, NULL); if (!ret) return false; RARCH_LOG("%s: %s.\n", msg_hash_to_str(MSG_GRAB_MOUSE_STATE), grab_mouse_state ? "yes" : "no"); if (grab_mouse_state) video_driver_ctl(RARCH_DISPLAY_CTL_HIDE_MOUSE, NULL); else video_driver_ctl(RARCH_DISPLAY_CTL_SHOW_MOUSE, NULL); } break; case EVENT_CMD_PERFCNT_REPORT_FRONTEND_LOG: rarch_perf_log(); break; case EVENT_CMD_VOLUME_UP: event_set_volume(0.5f); break; case EVENT_CMD_VOLUME_DOWN: event_set_volume(-0.5f); break; case EVENT_CMD_SET_FRAME_LIMIT: runloop_ctl(RUNLOOP_CTL_SET_FRAME_LIMIT, NULL); break; case EVENT_CMD_EXEC: return event_cmd_exec(data); case EVENT_CMD_NONE: default: return false; } return true; }
static bool init_playback(bsv_movie_t *handle, const char *path) { uint32_t state_size; uint32_t *content_crc_ptr = NULL; uint32_t header[4] = {0}; handle->playback = true; handle->file = fopen(path, "rb"); if (!handle->file) { RARCH_ERR("Couldn't open BSV file \"%s\" for playback.\n", path); return false; } if (fread(header, sizeof(uint32_t), 4, handle->file) != 4) { RARCH_ERR("Couldn't read movie header.\n"); return false; } /* Compatibility with old implementation that * used incorrect documentation. */ if (swap_if_little32(header[MAGIC_INDEX]) != BSV_MAGIC && swap_if_big32(header[MAGIC_INDEX]) != BSV_MAGIC) { RARCH_ERR("Movie file is not a valid BSV1 file.\n"); return false; } content_ctl(CONTENT_CTL_GET_CRC, &content_crc_ptr); if (swap_if_big32(header[CRC_INDEX]) != *content_crc_ptr) RARCH_WARN("CRC32 checksum mismatch between content file and saved content checksum in replay file header; replay highly likely to desync on playback.\n"); state_size = swap_if_big32(header[STATE_SIZE_INDEX]); if (state_size) { retro_ctx_serialize_info_t serial_info; retro_ctx_size_info_t info; handle->state = (uint8_t*)malloc(state_size); handle->state_size = state_size; if (!handle->state) return false; if (fread(handle->state, 1, state_size, handle->file) != state_size) { RARCH_ERR("Couldn't read state from movie.\n"); return false; } core_ctl(CORE_CTL_RETRO_SERIALIZE_SIZE, &info); if (info.size == state_size) { serial_info.data_const = handle->state; serial_info.size = state_size; core_ctl(CORE_CTL_RETRO_UNSERIALIZE, &serial_info); } else RARCH_WARN("Movie format seems to have a different serializer version. Will most likely fail.\n"); } handle->min_file_pos = sizeof(header) + state_size; return true; }
unsigned menu_input_frame_retropad(retro_input_t input, retro_input_t trigger_input) { menu_animation_ctx_delta_t delta; float delta_time; static bool initial_held = true; static bool first_held = false; static const retro_input_t input_repeat = (1UL << RETRO_DEVICE_ID_JOYPAD_UP) | (1UL << RETRO_DEVICE_ID_JOYPAD_DOWN) | (1UL << RETRO_DEVICE_ID_JOYPAD_LEFT) | (1UL << RETRO_DEVICE_ID_JOYPAD_RIGHT) | (1UL << RETRO_DEVICE_ID_JOYPAD_B) | (1UL << RETRO_DEVICE_ID_JOYPAD_A) | (1UL << RETRO_DEVICE_ID_JOYPAD_L) | (1UL << RETRO_DEVICE_ID_JOYPAD_R); bool set_scroll = false; size_t new_scroll_accel = 0; menu_input_t *menu_input = menu_input_get_ptr(); settings_t *settings = config_get_ptr(); if (!menu_input) return 0; core_ctl(CORE_CTL_RETRO_CTX_POLL_CB, NULL); /* don't run anything first frame, only capture held inputs * for old_input_state. */ if (input & input_repeat) { if (!first_held) { first_held = true; menu_input->delay.timer = initial_held ? 12 : 6; menu_input->delay.count = 0; } if (menu_input->delay.count >= menu_input->delay.timer) { set_scroll = true; first_held = false; trigger_input |= input & input_repeat; menu_navigation_ctl(MENU_NAVIGATION_CTL_GET_SCROLL_ACCEL, &new_scroll_accel); new_scroll_accel = MIN(new_scroll_accel + 1, 64); } initial_held = false; } else { set_scroll = true; first_held = false; initial_held = true; } if (set_scroll) menu_navigation_ctl(MENU_NAVIGATION_CTL_SET_SCROLL_ACCEL, &new_scroll_accel); menu_animation_ctl(MENU_ANIMATION_CTL_DELTA_TIME, &delta_time); delta.current = delta_time; if (menu_animation_ctl(MENU_ANIMATION_CTL_IDEAL_DELTA_TIME_GET, &delta)) menu_input->delay.count += delta.ideal; if (menu_input->keyboard.display) { /* send return key to close keyboard input window */ if (trigger_input & (UINT64_C(1) << settings->menu_cancel_btn)) input_keyboard_event(true, '\n', '\n', 0, RETRO_DEVICE_KEYBOARD); trigger_input = 0; } return menu_input_frame_build(trigger_input); }
bool np_get_info(netplay_t *netplay) { unsigned sram_size; uint32_t header[3]; retro_ctx_memory_info_t mem_info; uint32_t *content_crc_ptr = NULL; const void *sram = NULL; if (!socket_receive_all_blocking(netplay->fd, header, sizeof(header))) { RARCH_ERR("Failed to receive header from client.\n"); return false; } content_ctl(CONTENT_CTL_GET_CRC, &content_crc_ptr); if (*content_crc_ptr != ntohl(header[0])) { RARCH_ERR("Content CRC32s differ. Cannot use different games.\n"); return false; } if (np_impl_magic() != ntohl(header[1])) { RARCH_ERR("Implementations differ, make sure you're using exact same " "libretro implementations and RetroArch version.\n"); return false; } mem_info.id = RETRO_MEMORY_SAVE_RAM; core_ctl(CORE_CTL_RETRO_GET_MEMORY, &mem_info); if (mem_info.size != ntohl(header[2])) { RARCH_ERR("Content SRAM sizes do not correspond.\n"); return false; } if (!np_get_nickname(netplay, netplay->fd)) { RARCH_ERR("Failed to get nickname from client.\n"); return false; } /* Send SRAM data to our User 2. */ sram = mem_info.data; sram_size = mem_info.size; if (!socket_send_all_blocking(netplay->fd, sram, sram_size)) { RARCH_ERR("Failed to send SRAM data to client.\n"); return false; } if (!np_send_nickname(netplay, netplay->fd)) { RARCH_ERR("Failed to send nickname to client.\n"); return false; } #ifndef HAVE_SOCKET_LEGACY np_log_connection(&netplay->other_addr, 0, netplay->other_nick); #endif return true; }
static bool gl_cg_load_imports(void *data) { unsigned i; retro_ctx_memory_info_t mem_info; struct state_tracker_info tracker_info = {0}; cg_shader_data_t *cg_data = (cg_shader_data_t*)data; if (!cg_data->shader->variables) return true; for (i = 0; i < cg_data->shader->variables; i++) { unsigned memtype; switch (cg_data->shader->variable[i].ram_type) { case RARCH_STATE_WRAM: memtype = RETRO_MEMORY_SYSTEM_RAM; break; default: memtype = -1u; } mem_info.id = memtype; core_ctl(CORE_CTL_RETRO_GET_MEMORY, &mem_info); if ((memtype != -1u) && (cg_data->shader->variable[i].addr >= mem_info.size)) { RARCH_ERR("Address out of bounds.\n"); return false; } } mem_info.data = NULL; mem_info.size = 0; mem_info.id = RETRO_MEMORY_SYSTEM_RAM; core_ctl(CORE_CTL_RETRO_GET_MEMORY, &mem_info); tracker_info.wram = (uint8_t*)mem_info.data; tracker_info.info = cg_data->shader->variable; tracker_info.info_elem = cg_data->shader->variables; #ifdef HAVE_PYTHON if (*cg_data->shader->script_path) { tracker_info.script = cg_data->shader->script_path; tracker_info.script_is_file = true; } tracker_info.script_class = *cg_data->shader->script_class ? cg_data->shader->script_class : NULL; #endif cg_data->state_tracker = state_tracker_init(&tracker_info); if (!cg_data->state_tracker) RARCH_WARN("Failed to initialize state tracker.\n"); return true; }
static void *gl_glsl_init(void *data, const char *path) { unsigned i; config_file_t *conf = NULL; const char *stock_vertex = NULL; const char *stock_fragment = NULL; glsl_shader_data_t *glsl = (glsl_shader_data_t*) calloc(1, sizeof(glsl_shader_data_t)); if (!glsl) return NULL; #ifndef HAVE_OPENGLES2 RARCH_LOG("Checking GLSL shader support ...\n"); bool shader_support = glCreateProgram && glUseProgram && glCreateShader && glDeleteShader && glShaderSource && glCompileShader && glAttachShader && glDetachShader && glLinkProgram && glGetUniformLocation && glUniform1i && glUniform1f && glUniform2fv && glUniform4fv && glUniformMatrix4fv && glGetShaderiv && glGetShaderInfoLog && glGetProgramiv && glGetProgramInfoLog && glDeleteProgram && glGetAttachedShaders && glGetAttribLocation && glEnableVertexAttribArray && glDisableVertexAttribArray && glVertexAttribPointer && glGenBuffers && glBufferData && glDeleteBuffers && glBindBuffer; if (!shader_support) { RARCH_ERR("GLSL shaders aren't supported by your OpenGL driver.\n"); goto error; } #endif glsl->shader = (struct video_shader*)calloc(1, sizeof(*glsl->shader)); if (!glsl->shader) goto error; if (path) { bool ret; const char *path_ext = path_get_extension(path); if (string_is_equal(path_ext, "glsl")) { strlcpy(glsl->shader->pass[0].source.path, path, sizeof(glsl->shader->pass[0].source.path)); glsl->shader->passes = 1; glsl->shader->modern = true; ret = true; } else if (string_is_equal(path_ext, "glslp")) { conf = config_file_new(path); if (conf) { ret = video_shader_read_conf_cgp(conf, glsl->shader); glsl->shader->modern = true; } else ret = false; } else ret = false; if (!ret) { RARCH_ERR("[GL]: Failed to parse GLSL shader.\n"); goto error; } } else { RARCH_WARN("[GL]: Stock GLSL shaders will be used.\n"); glsl->shader->passes = 1; glsl->shader->pass[0].source.string.vertex = strdup(glsl_core ? stock_vertex_core : stock_vertex_modern); glsl->shader->pass[0].source.string.fragment = strdup(glsl_core ? stock_fragment_core : stock_fragment_modern); glsl->shader->modern = true; } video_shader_resolve_relative(glsl->shader, path); video_shader_resolve_parameters(conf, glsl->shader); if (conf) { config_file_free(conf); conf = NULL; } stock_vertex = (glsl->shader->modern) ? stock_vertex_modern : stock_vertex_legacy; stock_fragment = (glsl->shader->modern) ? stock_fragment_modern : stock_fragment_legacy; if (glsl_core) { stock_vertex = stock_vertex_core; stock_fragment = stock_fragment_core; } #ifdef HAVE_OPENGLES2 if (!glsl->shader->modern) { RARCH_ERR("[GL]: GLES context is used, but shader is not modern. Cannot use it.\n"); goto error; } #else if (glsl_core && !glsl->shader->modern) { RARCH_ERR("[GL]: GL core context is used, but shader is not core compatible. Cannot use it.\n"); goto error; } #endif /* Find all aliases we use in our GLSLP and add #defines for them so * that a shader can choose a fallback if we are not using a preset. */ *glsl->glsl_alias_define = '\0'; for (i = 0; i < glsl->shader->passes; i++) { if (*glsl->shader->pass[i].alias) { char define[128] = {0}; snprintf(define, sizeof(define), "#define %s_ALIAS\n", glsl->shader->pass[i].alias); strlcat(glsl->glsl_alias_define, define, sizeof(glsl->glsl_alias_define)); } } if (!(glsl->gl_program[0] = gl_glsl_compile_program(glsl, stock_vertex, stock_fragment, 0))) { RARCH_ERR("GLSL stock programs failed to compile.\n"); goto error; } if (!gl_glsl_compile_programs(glsl, &glsl->gl_program[1])) goto error; if (!gl_load_luts(glsl->shader, glsl->gl_teximage)) { RARCH_ERR("[GL]: Failed to load LUTs.\n"); goto error; } for (i = 0; i <= glsl->shader->passes; i++) gl_glsl_find_uniforms(glsl, i, glsl->gl_program[i], &glsl->gl_uniforms[i]); #ifdef GLSL_DEBUG if (!gl_check_error()) RARCH_WARN("Detected GL error in GLSL.\n"); #endif if (glsl->shader->variables) { retro_ctx_memory_info_t mem_info; struct state_tracker_info info = {0}; mem_info.id = RETRO_MEMORY_SYSTEM_RAM; core_ctl(CORE_CTL_RETRO_GET_MEMORY, &mem_info); info.wram = (uint8_t*)mem_info.data; info.info = glsl->shader->variable; info.info_elem = glsl->shader->variables; #ifdef HAVE_PYTHON info.script = glsl->shader->script; info.script_class = *glsl->shader->script_class ? glsl->shader->script_class : NULL; #endif glsl->gl_state_tracker = state_tracker_init(&info); if (!glsl->gl_state_tracker) RARCH_WARN("Failed to init state tracker.\n"); } glsl->gl_program[glsl->shader->passes + 1] = glsl->gl_program[0]; glsl->gl_uniforms[glsl->shader->passes + 1] = glsl->gl_uniforms[0]; if (glsl->shader->modern) { glsl->gl_program[GL_SHADER_STOCK_BLEND] = gl_glsl_compile_program( glsl, glsl_core ? stock_vertex_core_blend : stock_vertex_modern_blend, glsl_core ? stock_fragment_core_blend : stock_fragment_modern_blend, GL_SHADER_STOCK_BLEND); gl_glsl_find_uniforms(glsl, 0, glsl->gl_program[GL_SHADER_STOCK_BLEND], &glsl->gl_uniforms[GL_SHADER_STOCK_BLEND]); } else { glsl->gl_program [GL_SHADER_STOCK_BLEND] = glsl->gl_program[0]; glsl->gl_uniforms[GL_SHADER_STOCK_BLEND] = glsl->gl_uniforms[0]; } gl_glsl_reset_attrib(glsl); for (i = 0; i < GFX_MAX_SHADERS; i++) { glGenBuffers(1, &glsl->glsl_vbo[i].vbo_primary); glGenBuffers(1, &glsl->glsl_vbo[i].vbo_secondary); } return glsl; error: gl_glsl_destroy_resources(glsl); if (glsl) free(glsl); return NULL; }