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_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]); }
bool netplay_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_get_memory(&mem_info); content_get_crc(&content_crc_ptr); header[0] = htonl(*content_crc_ptr); header[1] = htonl(netplay_impl_magic()); header[2] = htonl(mem_info.size); if (!socket_send_all_blocking(netplay->fd, header, sizeof(header), false)) return false; if (!netplay_send_nickname(netplay, netplay->fd)) { RARCH_ERR("%s\n", msg_hash_to_str(MSG_FAILED_TO_SEND_NICKNAME_TO_HOST)); 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("%s\n", msg_hash_to_str(MSG_FAILED_TO_RECEIVE_SRAM_DATA_FROM_HOST)); return false; } if (!netplay_get_nickname(netplay, netplay->fd)) { RARCH_ERR("%s\n", msg_hash_to_str(MSG_FAILED_TO_RECEIVE_NICKNAME_FROM_HOST)); return false; } snprintf(msg, sizeof(msg), "%s: \"%s\"", msg_hash_to_str(MSG_CONNECTED_TO), netplay->other_nick); RARCH_LOG("%s\n", msg); runloop_msg_queue_push(msg, 1, 180, false); 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 || !d3d->renderchain_driver->add_state_tracker) return true; mem_info.id = RETRO_MEMORY_SYSTEM_RAM; core_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; }
static void *gl_glsl_init(void *data, const char *path) { unsigned i; struct shader_program_info shader_prog_info; bool shader_support = false; #ifdef GLSL_DEBUG char *error_string = NULL; #endif 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; (void)shader_support; #ifndef HAVE_OPENGLES RARCH_LOG("[GLSL]: Checking GLSL shader support ...\n"); 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 (!string_is_empty(path)) { bool ret = false; const char *path_ext = path_get_extension(path); if (string_is_equal_fast(path_ext, "glslp", 5)) { conf = config_file_new(path); if (conf) { ret = video_shader_read_conf_cgp(conf, glsl->shader); glsl->shader->modern = true; } } else if (string_is_equal_fast(path_ext, "glsl", 4)) { 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; } 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; } if (!string_is_empty(path)) 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_OPENGLES 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->alias_define = '\0'; for (i = 0; i < glsl->shader->passes; i++) { if (*glsl->shader->pass[i].alias) { char define[128]; define[0] = '\0'; snprintf(define, sizeof(define), "#define %s_ALIAS\n", glsl->shader->pass[i].alias); strlcat(glsl->alias_define, define, sizeof(glsl->alias_define)); } } shader_prog_info.vertex = stock_vertex; shader_prog_info.fragment = stock_fragment; shader_prog_info.is_file = false; if (!gl_glsl_compile_program(glsl, 0, &glsl->prg[0], &shader_prog_info)) { RARCH_ERR("GLSL stock programs failed to compile.\n"); goto error; } if (!gl_glsl_compile_programs(glsl, &glsl->prg[1])) goto error; if (!gl_load_luts(glsl->shader, glsl->lut_textures)) { 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->prg[i].id, &glsl->uniforms[i]); #ifdef GLSL_DEBUG if (!gl_check_error(&error_string)) { RARCH_ERR("%s\n", error_string); free(error_string); 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; mem_info.id = RETRO_MEMORY_SYSTEM_RAM; core_get_memory(&mem_info); info.wram = (uint8_t*)mem_info.data; info.info = glsl->shader->variable; info.info_elem = glsl->shader->variables; info.script = NULL; info.script_class = NULL; #ifdef HAVE_PYTHON info.script = glsl->shader->script; if (*glsl->shader->script_class) info.script_class= glsl->shader->script_class; #endif info.script_is_file = NULL; glsl->state_tracker = state_tracker_init(&info); if (!glsl->state_tracker) RARCH_WARN("Failed to init state tracker.\n"); } glsl->prg[glsl->shader->passes + 1] = glsl->prg[0]; glsl->uniforms[glsl->shader->passes + 1] = glsl->uniforms[0]; if (glsl->shader->modern) { shader_prog_info.vertex = glsl_core ? stock_vertex_core_blend : stock_vertex_modern_blend; shader_prog_info.fragment = glsl_core ? stock_fragment_core_blend : stock_fragment_modern_blend; shader_prog_info.is_file = false; gl_glsl_compile_program( glsl, VIDEO_SHADER_STOCK_BLEND, &glsl->prg[VIDEO_SHADER_STOCK_BLEND], &shader_prog_info ); gl_glsl_find_uniforms(glsl, 0, glsl->prg[VIDEO_SHADER_STOCK_BLEND].id, &glsl->uniforms[VIDEO_SHADER_STOCK_BLEND]); } else { glsl->prg[VIDEO_SHADER_STOCK_BLEND] = glsl->prg[0]; glsl->uniforms[VIDEO_SHADER_STOCK_BLEND] = glsl->uniforms[0]; } #ifdef HAVE_SHADERPIPELINE #ifdef HAVE_OPENGLES if (gl_query_extension("GL_OES_standard_derivatives")) { shader_prog_info.vertex = glsl_core ? stock_vertex_xmb_ribbon_modern : stock_vertex_xmb_ribbon_legacy; shader_prog_info.fragment = stock_fragment_xmb; } else { shader_prog_info.vertex = stock_vertex_xmb_ribbon_simple_legacy; shader_prog_info.fragment = stock_fragment_xmb_ribbon_simple; } #else shader_prog_info.vertex = glsl_core ? stock_vertex_xmb_ribbon_modern : stock_vertex_xmb_ribbon_legacy; shader_prog_info.fragment = stock_fragment_xmb; #endif shader_prog_info.is_file = false; gl_glsl_compile_program( glsl, VIDEO_SHADER_MENU, &glsl->prg[VIDEO_SHADER_MENU], &shader_prog_info); gl_glsl_find_uniforms(glsl, 0, glsl->prg[VIDEO_SHADER_MENU].id, &glsl->uniforms[VIDEO_SHADER_MENU]); shader_prog_info.vertex = glsl_core ? stock_vertex_xmb_simple_modern : stock_vertex_xmb_ribbon_simple_legacy; shader_prog_info.fragment = stock_fragment_xmb_ribbon_simple; gl_glsl_compile_program( glsl, VIDEO_SHADER_MENU_2, &glsl->prg[VIDEO_SHADER_MENU_2], &shader_prog_info); gl_glsl_find_uniforms(glsl, 0, glsl->prg[VIDEO_SHADER_MENU_2].id, &glsl->uniforms[VIDEO_SHADER_MENU_2]); #if defined(HAVE_OPENGLES) shader_prog_info.vertex = stock_vertex_xmb_snow_modern; #else shader_prog_info.vertex = glsl_core ? stock_vertex_xmb_snow_modern : stock_vertex_xmb_snow_legacy; #endif shader_prog_info.fragment = stock_fragment_xmb_simple_snow; gl_glsl_compile_program( glsl, VIDEO_SHADER_MENU_3, &glsl->prg[VIDEO_SHADER_MENU_3], &shader_prog_info); gl_glsl_find_uniforms(glsl, 0, glsl->prg[VIDEO_SHADER_MENU_3].id, &glsl->uniforms[VIDEO_SHADER_MENU_3]); #if defined(HAVE_OPENGLES) shader_prog_info.vertex = stock_vertex_xmb_snow_modern; #else shader_prog_info.vertex = glsl_core ? stock_vertex_xmb_snow_modern : stock_vertex_xmb_snow_legacy; #endif shader_prog_info.fragment = stock_fragment_xmb_snow; gl_glsl_compile_program( glsl, VIDEO_SHADER_MENU_4, &glsl->prg[VIDEO_SHADER_MENU_4], &shader_prog_info); gl_glsl_find_uniforms(glsl, 0, glsl->prg[VIDEO_SHADER_MENU_4].id, &glsl->uniforms[VIDEO_SHADER_MENU_4]); #if defined(HAVE_OPENGLES) shader_prog_info.vertex = stock_vertex_xmb_snow_modern; #else shader_prog_info.vertex = glsl_core ? stock_vertex_xmb_snow_modern : stock_vertex_xmb_snow_legacy; #endif shader_prog_info.fragment = stock_fragment_xmb_bokeh; gl_glsl_compile_program( glsl, VIDEO_SHADER_MENU_5, &glsl->prg[VIDEO_SHADER_MENU_5], &shader_prog_info); gl_glsl_find_uniforms(glsl, 0, glsl->prg[VIDEO_SHADER_MENU_5].id, &glsl->uniforms[VIDEO_SHADER_MENU_5]); #endif gl_glsl_reset_attrib(glsl); for (i = 0; i < GFX_MAX_SHADERS; i++) { glGenBuffers(1, &glsl->vbo[i].vbo_primary); glGenBuffers(1, &glsl->vbo[i].vbo_secondary); } return glsl; error: gl_glsl_destroy_resources(glsl); if (conf) config_file_free(conf); if (glsl) free(glsl); return NULL; }
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 = (cg_shader_data_t*)data; if (!cg->shader->variables) return true; for (i = 0; i < cg->shader->variables; i++) { unsigned memtype; switch (cg->shader->variable[i].ram_type) { case RARCH_STATE_WRAM: memtype = RETRO_MEMORY_SYSTEM_RAM; break; default: memtype = -1u; } mem_info.id = memtype; core_get_memory(&mem_info); if ((memtype != -1u) && (cg->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_get_memory(&mem_info); tracker_info.wram = (uint8_t*)mem_info.data; tracker_info.info = cg->shader->variable; tracker_info.info_elem = cg->shader->variables; #ifdef HAVE_PYTHON if (*cg->shader->script_path) { tracker_info.script = cg->shader->script_path; tracker_info.script_is_file = true; } tracker_info.script_class = *cg->shader->script_class ? cg->shader->script_class : NULL; #endif cg->state_tracker = state_tracker_init(&tracker_info); if (!cg->state_tracker) RARCH_WARN("Failed to initialize state tracker.\n"); return true; }
bool netplay_handshake(netplay_t *netplay) { size_t i; uint32_t local_pmagic, remote_pmagic; unsigned sram_size, remote_sram_size; retro_ctx_memory_info_t mem_info; char msg[512]; uint32_t *content_crc_ptr = NULL; void *sram = NULL; uint32_t header[5] = {0}; bool is_server = netplay->is_server; int compression = 0; msg[0] = '\0'; mem_info.id = RETRO_MEMORY_SAVE_RAM; core_get_memory(&mem_info); content_get_crc(&content_crc_ptr); local_pmagic = netplay_platform_magic(); header[0] = htonl(*content_crc_ptr); header[1] = htonl(netplay_impl_magic()); header[2] = htonl(mem_info.size); header[3] = htonl(local_pmagic); header[4] = htonl(NETPLAY_COMPRESSION_SUPPORTED); if (!socket_send_all_blocking(netplay->fd, header, sizeof(header), false)) return false; if (!socket_receive_all_blocking(netplay->fd, header, sizeof(header))) { strlcpy(msg, msg_hash_to_str(MSG_FAILED_TO_RECEIVE_HEADER_FROM_CLIENT), sizeof(msg)); goto error; } if (*content_crc_ptr != ntohl(header[0])) { strlcpy(msg, msg_hash_to_str(MSG_CONTENT_CRC32S_DIFFER), sizeof(msg)); goto error; } if (netplay_impl_magic() != ntohl(header[1])) { strlcpy(msg, "Implementations differ. Make sure you're using exact same " "libretro implementations and RetroArch version.", sizeof(msg)); goto error; } /* Some cores only report the correct sram size late, so we can't actually * error out if the sram size seems wrong. */ sram_size = mem_info.size; remote_sram_size = ntohl(header[2]); if (sram_size != 0 && remote_sram_size != 0 && sram_size != remote_sram_size) { RARCH_WARN("Content SRAM sizes do not correspond.\n"); } /* We only care about platform magic if our core is quirky */ remote_pmagic = ntohl(header[3]); if ((netplay->quirks & NETPLAY_QUIRK_ENDIAN_DEPENDENT) && netplay_endian_mismatch(local_pmagic, remote_pmagic)) { RARCH_ERR("Endianness mismatch with an endian-sensitive core.\n"); strlcpy(msg, "This core does not support inter-architecture netplay " "between these systems.", sizeof(msg)); goto error; } if ((netplay->quirks & NETPLAY_QUIRK_PLATFORM_DEPENDENT) && (local_pmagic != remote_pmagic)) { RARCH_ERR("Platform mismatch with a platform-sensitive core.\n"); strlcpy(msg, "This core does not support inter-architecture netplay.", sizeof(msg)); goto error; } /* Clear any existing compression */ if (netplay->compression_stream) netplay->compression_backend->stream_free(netplay->compression_stream); if (netplay->decompression_stream) netplay->decompression_backend->stream_free(netplay->decompression_stream); /* Check what compression is supported */ compression = ntohl(header[4]); compression &= NETPLAY_COMPRESSION_SUPPORTED; if (compression & NETPLAY_COMPRESSION_ZLIB) { netplay->compression_backend = trans_stream_get_zlib_deflate_backend(); if (!netplay->compression_backend) netplay->compression_backend = trans_stream_get_pipe_backend(); } else { netplay->compression_backend = trans_stream_get_pipe_backend(); } netplay->decompression_backend = netplay->compression_backend->reverse; /* Allocate our compression stream */ netplay->compression_stream = netplay->compression_backend->stream_new(); netplay->decompression_stream = netplay->decompression_backend->stream_new(); if (!netplay->compression_stream || !netplay->decompression_stream) { RARCH_ERR("Failed to allocate compression transcoder!\n"); return false; } /* Client sends nickname first, server replies with nickname */ if (!is_server) { if (!netplay_send_nickname(netplay, netplay->fd)) { strlcpy(msg, msg_hash_to_str(MSG_FAILED_TO_SEND_NICKNAME_TO_HOST), sizeof(msg)); goto error; } } if (!netplay_get_nickname(netplay, netplay->fd)) { if (is_server) strlcpy(msg, msg_hash_to_str(MSG_FAILED_TO_GET_NICKNAME_FROM_CLIENT), sizeof(msg)); else strlcpy(msg, msg_hash_to_str(MSG_FAILED_TO_RECEIVE_NICKNAME_FROM_HOST), sizeof(msg)); goto error; } if (is_server) { if (!netplay_send_nickname(netplay, netplay->fd)) { RARCH_ERR("%s\n", msg_hash_to_str(MSG_FAILED_TO_SEND_NICKNAME_TO_CLIENT)); return false; } } /* Server sends SRAM, client receives */ if (is_server) { /* Send SRAM data to the client */ sram = mem_info.data; if (!socket_send_all_blocking(netplay->fd, sram, sram_size, false)) { RARCH_ERR("%s\n", msg_hash_to_str(MSG_FAILED_TO_SEND_SRAM_DATA_TO_CLIENT)); return false; } } else { /* Get SRAM data from User 1. */ if (sram_size != 0 && sram_size == remote_sram_size) { sram = mem_info.data; if (!socket_receive_all_blocking(netplay->fd, sram, sram_size)) { RARCH_ERR("%s\n", msg_hash_to_str(MSG_FAILED_TO_RECEIVE_SRAM_DATA_FROM_HOST)); return false; } } else if (remote_sram_size != 0) { /* We can't load this, but we still need to get rid of the data */ uint32_t quickbuf; while (remote_sram_size > 0) { if (!socket_receive_all_blocking(netplay->fd, &quickbuf, (remote_sram_size > sizeof(uint32_t)) ? sizeof(uint32_t) : remote_sram_size)) { RARCH_ERR("%s\n", msg_hash_to_str(MSG_FAILED_TO_RECEIVE_SRAM_DATA_FROM_HOST)); return false; } if (remote_sram_size > sizeof(uint32_t)) remote_sram_size -= sizeof(uint32_t); else remote_sram_size = 0; } } } /* Reset our frame count so it's consistent with the server */ netplay->self_frame_count = netplay->other_frame_count = 0; netplay->read_frame_count = 1; for (i = 0; i < netplay->buffer_size; i++) { netplay->buffer[i].used = false; if (i == netplay->self_ptr) { netplay_delta_frame_ready(netplay, &netplay->buffer[i], 0); netplay->buffer[i].have_remote = true; netplay->other_ptr = i; netplay->read_ptr = NEXT_PTR(i); } else { netplay->buffer[i].used = false; } } if (is_server) { netplay_log_connection(&netplay->other_addr, 0, netplay->other_nick); } else { snprintf(msg, sizeof(msg), "%s: \"%s\"", msg_hash_to_str(MSG_CONNECTED_TO), netplay->other_nick); RARCH_LOG("%s\n", msg); runloop_msg_queue_push(msg, 1, 180, false); } return true; error: if (msg[0]) { RARCH_ERR("%s\n", msg); runloop_msg_queue_push(msg, 1, 180, false); } return false; }
static int httpserver_handle_basic_info(struct mg_connection* conn, void* cbdata) { 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", }; unsigned p, q, r; retro_ctx_api_info_t api; retro_ctx_region_info_t region; retro_ctx_memory_info_t sram; retro_ctx_memory_info_t rtc; retro_ctx_memory_info_t sysram; retro_ctx_memory_info_t vram; char core_path[PATH_MAX_LENGTH] = {0}; const char* pixel_format = NULL; const struct retro_subsystem_info* subsys = NULL; const struct retro_subsystem_rom_info* rom = NULL; const struct retro_subsystem_memory_info* mem = NULL; const struct retro_controller_description* ctrl = NULL; const char* comma = NULL; const struct core_option* opts = NULL; const struct retro_system_av_info* av_info = NULL; const core_option_manager_t* core_opts = NULL; const struct mg_request_info * req = mg_get_request_info(conn); const settings_t * settings = config_get_ptr(); rarch_system_info_t *system = runloop_get_system_info(); if (string_is_empty(system->info.library_name)) return httpserver_error(conn, 500, "Core not initialized in %s", __FUNCTION__); if (!core_is_game_loaded()) return httpserver_error(conn, 500, "Game not loaded in %s", __FUNCTION__); json_string_encode(core_path, sizeof(core_path), config_get_active_core_path()); core_api_version(&api); core_get_region(®ion); switch (video_driver_get_pixel_format()) { case RETRO_PIXEL_FORMAT_0RGB1555: pixel_format = "RETRO_PIXEL_FORMAT_0RGB1555"; break; case RETRO_PIXEL_FORMAT_XRGB8888: pixel_format = "RETRO_PIXEL_FORMAT_XRGB8888"; break; case RETRO_PIXEL_FORMAT_RGB565: pixel_format = "RETRO_PIXEL_FORMAT_RGB565"; break; default: pixel_format = "?"; break; } sram.id = RETRO_MEMORY_SAVE_RAM; core_get_memory(&sram); rtc.id = RETRO_MEMORY_RTC; core_get_memory(&rtc); sysram.id = RETRO_MEMORY_SYSTEM_RAM; core_get_memory(&sysram); vram.id = RETRO_MEMORY_VIDEO_RAM; core_get_memory(&vram); mg_printf(conn, "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n" "{" "\"corePath\":\"%s\"," "\"apiVersion\":%u," "\"systemInfo\":" "{" "\"libraryName\":\"%s\"," "\"libraryVersion\":\"%s\"," "\"validExtensions\":\"%s\"," "\"needsFullpath\":%s," "\"blockExtract\":%s" "}," "\"region\":\"%s\"," "\"pixelFormat\":\"%s\"," "\"rotation\":%u," "\"performaceLevel\":%u," "\"supportsNoGame\":%s," #ifdef HAVE_CHEEVOS "\"frontendSupportsAchievements\":true," "\"coreSupportsAchievements\":%s," #else "\"frontendSupportsAchievements\":false," "\"coreSupportsAchievements\":null," #endif "\"saveRam\":{\"pointer\":\"%" PRIXPTR "\",\"size\":" STRING_REP_UINT64 "}," "\"rtcRam\":{\"pointer\":\"%" PRIXPTR "\",\"size\":" STRING_REP_UINT64 "}," "\"systemRam\":{\"pointer\":\"%" PRIXPTR "\",\"size\":" STRING_REP_UINT64 "}," "\"videoRam\":{\"pointer\":\"%" PRIXPTR "\",\"size\":" STRING_REP_UINT64 "},", core_path, api.version, system->info.library_name, system->info.library_version, system->info.valid_extensions, system->info.need_fullpath ? "true" : "false", system->info.block_extract ? "true" : "false", region.region ? "RETRO_REGION_PAL" : "RETRO_REGION_NTSC", pixel_format, system->rotation, system->performance_level, content_does_not_need_content() ? "true" : "false", #ifdef HAVE_CHEEVOS cheevos_get_support_cheevos() ? "true" : "false", #endif (uintptr_t)sram.data, sram.size, (uintptr_t)rtc.data, rtc.size, (uintptr_t)sysram.data, sysram.size, (uintptr_t)vram.data, vram.size ); mg_printf(conn, "\"subSystems\":["); subsys = system->subsystem.data; for (p = 0; p < system->subsystem.size; p++, subsys++) { mg_printf(conn, "%s{\"id\":%u,\"description\":\"%s\",\"identifier\":\"%s\",\"roms\":[", p == 0 ? "" : ",", subsys->id, subsys->desc, subsys->ident); rom = subsys->roms; for (q = 0; q < subsys->num_roms; q++, rom++) { mg_printf(conn, "%s{" "\"description\":\"%s\"," "\"extensions\":\"%s\"," "\"needsFullpath\":%s," "\"blockExtract\":%s," "\"required\":%s," "\"memory\":[", q == 0 ? "" : ",", rom->desc, rom->valid_extensions, rom->need_fullpath ? "true" : "false", rom->block_extract ? "true" : "false", rom->required ? "true" : "false" ); mem = rom->memory; comma = ""; for (r = 0; r < rom->num_memory; r++, mem++) { mg_printf(conn, "%s{\"extension\":\"%s\",\"type\":%u}", comma, mem->extension, mem->type); comma = ","; } mg_printf(conn, "]}"); } mg_printf(conn, "]}"); } av_info = video_viewport_get_system_av_info(); mg_printf(conn, "],\"avInfo\":{" "\"geometry\":{" "\"baseWidth\":%u," "\"baseHeight\":%u," "\"maxWidth\":%u," "\"maxHeight\":%u," "\"aspectRatio\":%f" "}," "\"timing\":{" "\"fps\":%f," "\"sampleRate\":%f" "}" "},", av_info->geometry.base_width, av_info->geometry.base_height, av_info->geometry.max_width, av_info->geometry.max_height, av_info->geometry.aspect_ratio, av_info->timing.fps, av_info->timing.sample_rate ); mg_printf(conn, "\"ports\":["); comma = ""; for (p = 0; p < system->ports.size; p++) { ctrl = system->ports.data[p].types; for (q = 0; q < system->ports.data[p].num_types; q++, ctrl++) { mg_printf(conn, "%s{\"id\":%u,\"description\":\"%s\"}", comma, ctrl->id, ctrl->desc); comma = ","; } } mg_printf(conn, "],\"inputDescriptors\":["); comma = ""; if (core_has_set_input_descriptor()) { for (p = 0; p < settings->input.max_users; p++) { for (q = 0; q < RARCH_FIRST_CUSTOM_BIND; q++) { const char* description = system->input_desc_btn[p][q]; if (description) { mg_printf(conn, "%s{\"player\":%u,\"button\":\"%s\",\"description\":\"%s\"}", comma, p + 1, libretro_btn_desc[q], description ); comma = ","; } } } } mg_printf(conn, "],\"coreOptions\":["); rarch_ctl(RARCH_CTL_CORE_OPTIONS_LIST_GET, (void*)&core_opts); opts = core_opts->opts; for (p = 0; p < core_opts->size; p++, opts++) { mg_printf(conn, "%s{\"key\":\"%s\",\"description\":\"%s\",\"values\":[", p == 0 ? "" : ",", opts->key, opts->desc); comma = ""; for (q = 0; q < opts->vals->size; q++) { mg_printf(conn, "%s\"%s\"", comma, opts->vals->elems[q].data); comma = ","; } mg_printf(conn, "]}"); } mg_printf(conn, "]}"); return 1; }
bool netplay_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("%s\n", msg_hash_to_str(MSG_FAILED_TO_RECEIVE_HEADER_FROM_CLIENT)); return false; } content_get_crc(&content_crc_ptr); if (*content_crc_ptr != ntohl(header[0])) { RARCH_ERR("%s\n", msg_hash_to_str(MSG_CONTENT_CRC32S_DIFFER)); return false; } if (netplay_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_get_memory(&mem_info); if (mem_info.size != ntohl(header[2])) { RARCH_ERR("Content SRAM sizes do not correspond.\n"); return false; } if (!netplay_get_nickname(netplay, netplay->fd)) { RARCH_ERR("%s\n", msg_hash_to_str(MSG_FAILED_TO_GET_NICKNAME_FROM_CLIENT)); 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, false)) { RARCH_ERR("%s\n", msg_hash_to_str(MSG_FAILED_TO_SEND_SRAM_DATA_TO_CLIENT)); return false; } if (!netplay_send_nickname(netplay, netplay->fd)) { RARCH_ERR("%s\n", msg_hash_to_str(MSG_FAILED_TO_SEND_NICKNAME_TO_CLIENT)); return false; } #ifndef HAVE_SOCKET_LEGACY netplay_log_connection(&netplay->other_addr, 0, netplay->other_nick); #endif return true; }