static bool video_frame_scale(const void *data, unsigned width, unsigned height, size_t pitch) { driver_t *driver = driver_get_ptr(); RARCH_PERFORMANCE_INIT(video_frame_conv); if (!data) return false; if (video_driver_get_pixel_format() != RETRO_PIXEL_FORMAT_0RGB1555) return false; if (data == RETRO_HW_FRAME_BUFFER_VALID) return false; RARCH_PERFORMANCE_START(video_frame_conv); driver->scaler.in_width = width; driver->scaler.in_height = height; driver->scaler.out_width = width; driver->scaler.out_height = height; driver->scaler.in_stride = pitch; driver->scaler.out_stride = width * sizeof(uint16_t); scaler_ctx_scale(&driver->scaler, driver->scaler_out, data); RARCH_PERFORMANCE_STOP(video_frame_conv); return true; }
bool init_video_pixel_converter(unsigned size) { /* This function can be called multiple times * without deiniting first on consoles. */ deinit_pixel_converter(); /* If pixel format is not 0RGB1555, we don't need to do * any internal pixel conversion. */ if (video_driver_get_pixel_format() != RETRO_PIXEL_FORMAT_0RGB1555) return true; RARCH_WARN("0RGB1555 pixel format is deprecated, and will be slower. For 15/16-bit, RGB565 format is preferred.\n"); scaler_ptr = (video_pixel_scaler_t*)calloc(1, sizeof(*scaler_ptr)); if (!scaler_ptr) goto error; scaler_ptr->scaler = (struct scaler_ctx*)calloc(1, sizeof(*scaler_ptr->scaler)); if (!scaler_ptr->scaler) goto error; scaler_ptr->scaler->scaler_type = SCALER_TYPE_POINT; scaler_ptr->scaler->in_fmt = SCALER_FMT_0RGB1555; /* TODO: Pick either ARGB8888 or RGB565 depending on driver. */ scaler_ptr->scaler->out_fmt = SCALER_FMT_RGB565; if (!scaler_ctx_gen_filter(scaler_ptr->scaler)) goto error; scaler_ptr->scaler_out = calloc(sizeof(uint16_t), size * size); if (!scaler_ptr->scaler_out) goto error; return true; error: if (scaler_ptr->scaler_out) free(scaler_ptr->scaler_out); if (scaler_ptr->scaler) free(scaler_ptr->scaler); if (scaler_ptr) free(scaler_ptr); scaler_ptr = NULL; return false; }
/** * recording_init: * * Initializes recording. * * Returns: true (1) if successful, otherwise false (0). **/ bool recording_init(void) { char recording_file[PATH_MAX_LENGTH] = {0}; struct ffemu_params params = {0}; global_t *global = global_get_ptr(); settings_t *settings = config_get_ptr(); struct retro_system_av_info *av_info = video_viewport_get_system_av_info(); const struct retro_hw_render_callback *hw_render = (const struct retro_hw_render_callback*)video_driver_callback(); bool *recording_enabled = recording_is_enabled(); if (!*recording_enabled) return false; if (global->inited.core.type == CORE_TYPE_DUMMY) { RARCH_WARN("%s\n", msg_hash_to_str(MSG_USING_LIBRETRO_DUMMY_CORE_RECORDING_SKIPPED)); return false; } if (!settings->video.gpu_record && hw_render->context_type) { RARCH_WARN("%s.\n", msg_hash_to_str(MSG_HW_RENDERED_MUST_USE_POSTSHADED_RECORDING)); return false; } RARCH_LOG("%s: FPS: %.4f, Sample rate: %.4f\n", msg_hash_to_str(MSG_CUSTOM_TIMING_GIVEN), (float)av_info->timing.fps, (float)av_info->timing.sample_rate); strlcpy(recording_file, global->record.path, sizeof(recording_file)); if (global->record.use_output_dir) fill_pathname_join(recording_file, global->record.output_dir, global->record.path, sizeof(recording_file)); params.out_width = av_info->geometry.base_width; params.out_height = av_info->geometry.base_height; params.fb_width = av_info->geometry.max_width; params.fb_height = av_info->geometry.max_height; params.channels = 2; params.filename = recording_file; params.fps = av_info->timing.fps; params.samplerate = av_info->timing.sample_rate; params.pix_fmt = (video_driver_get_pixel_format() == RETRO_PIXEL_FORMAT_XRGB8888) ? FFEMU_PIX_ARGB8888 : FFEMU_PIX_RGB565; params.config = NULL; if (*global->record.config) params.config = global->record.config; if (video_driver_ctl(RARCH_DISPLAY_CTL_SUPPORTS_RECORDING, NULL)) { unsigned gpu_size; struct video_viewport vp = {0}; video_driver_viewport_info(&vp); if (!vp.width || !vp.height) { RARCH_ERR("Failed to get viewport information from video driver. " "Cannot start recording ...\n"); return false; } params.out_width = vp.width; params.out_height = vp.height; params.fb_width = next_pow2(vp.width); params.fb_height = next_pow2(vp.height); if (settings->video.force_aspect && (video_driver_get_aspect_ratio() > 0.0f)) params.aspect_ratio = video_driver_get_aspect_ratio(); else params.aspect_ratio = (float)vp.width / vp.height; params.pix_fmt = FFEMU_PIX_BGR24; global->record.gpu_width = vp.width; global->record.gpu_height = vp.height; RARCH_LOG("%s %u x %u\n", msg_hash_to_str(MSG_DETECTED_VIEWPORT_OF), vp.width, vp.height); gpu_size = vp.width * vp.height * 3; if (!video_driver_ctl(RARCH_DISPLAY_CTL_GPU_RECORD_INIT, &gpu_size)) return false; } else { if (global->record.width || global->record.height) { params.out_width = global->record.width; params.out_height = global->record.height; } if (settings->video.force_aspect && (video_driver_get_aspect_ratio() > 0.0f)) params.aspect_ratio = video_driver_get_aspect_ratio(); else params.aspect_ratio = (float)params.out_width / params.out_height; if (settings->video.post_filter_record && video_driver_ctl(RARCH_DISPLAY_CTL_FRAME_FILTER_ALIVE, NULL)) { unsigned max_width = 0; unsigned max_height = 0; params.pix_fmt = FFEMU_PIX_RGB565; if (video_driver_ctl(RARCH_DISPLAY_CTL_FRAME_FILTER_IS_32BIT, NULL)) params.pix_fmt = FFEMU_PIX_ARGB8888; rarch_softfilter_get_max_output_size( video_driver_frame_filter_get_ptr(), &max_width, &max_height); params.fb_width = next_pow2(max_width); params.fb_height = next_pow2(max_height); } } RARCH_LOG("%s %s @ %ux%u. (FB size: %ux%u pix_fmt: %u)\n", msg_hash_to_str(MSG_RECORDING_TO), global->record.path, params.out_width, params.out_height, params.fb_width, params.fb_height, (unsigned)params.pix_fmt); if (!record_driver_init_first(&recording_driver, &recording_data, ¶ms)) { RARCH_ERR("%s\n", msg_hash_to_str(MSG_FAILED_TO_START_RECORDING)); event_command(EVENT_CMD_GPU_RECORD_DEINIT); return false; } return true; }
/* Take frame bottom-up. */ static bool screenshot_dump( const char *global_name_base, const char *folder, const void *frame, unsigned width, unsigned height, int pitch, bool bgr24) { char filename[PATH_MAX_LENGTH] = {0}; char shotname[256] = {0}; bool ret = false; settings_t *settings = config_get_ptr(); #if defined(HAVE_ZLIB_DEFLATE) && defined(HAVE_RPNG) uint8_t *out_buffer = NULL; struct scaler_ctx scaler = {0}; #endif if (settings->auto_screenshot_filename) { fill_dated_filename(shotname, IMG_EXT, sizeof(shotname)); fill_pathname_join(filename, folder, shotname, sizeof(filename)); } else { snprintf(shotname, sizeof(shotname),"%s.png", path_basename(global_name_base)); fill_pathname_join(filename, folder, shotname, sizeof(filename)); } #ifdef _XBOX1 d3d_video_t *d3d = (d3d_video_t*)video_driver_get_ptr(true); D3DSurface *surf = NULL; d3d->dev->GetBackBuffer(-1, D3DBACKBUFFER_TYPE_MONO, &surf); if (XGWriteSurfaceToFile(surf, filename) == S_OK) ret = true; surf->Release(); #elif defined(HAVE_ZLIB_DEFLATE) && defined(HAVE_RPNG) out_buffer = (uint8_t*)malloc(width * height * 3); if (!out_buffer) return false; video_frame_convert_to_bgr24( &scaler, out_buffer, (const uint8_t*)frame + ((int)height - 1) * pitch, width, height, -pitch, bgr24); scaler_ctx_gen_reset(&scaler); ret = rpng_save_image_bgr24( filename, out_buffer, width, height, width * 3 ); free(out_buffer); #elif defined(HAVE_RBMP) enum rbmp_source_type bmp_type = RBMP_SOURCE_TYPE_DONT_CARE; if (bgr24) bmp_type = RBMP_SOURCE_TYPE_BGR24; else if (video_driver_get_pixel_format() == RETRO_PIXEL_FORMAT_XRGB8888) bmp_type = RBMP_SOURCE_TYPE_XRGB888; ret = rbmp_save_image(filename, frame, width, height, pitch, bmp_type); #endif #ifdef HAVE_IMAGEVIEWER if (ret == true) { if (content_push_to_history_playlist(g_defaults.image_history, filename, "imageviewer", "builtin")) playlist_write_file(g_defaults.image_history); } #endif return ret; }
static void *psp_init(const video_info_t *video, const input_driver_t **input, void **input_data) { /* TODO : add ASSERT() checks or use main RAM if * VRAM is too low for desired video->input_scale. */ void *pspinput = NULL; int pixel_format, lut_pixel_format, lut_block_count; unsigned int red_shift, color_mask; void *displayBuffer, *LUT_r, *LUT_b; psp1_video_t *psp = (psp1_video_t*)calloc(1, sizeof(psp1_video_t)); if (!psp) return NULL; sceGuInit(); psp->vp.x = 0; psp->vp.y = 0; psp->vp.width = SCEGU_SCR_WIDTH; psp->vp.height = SCEGU_SCR_HEIGHT; psp->vp.full_width = SCEGU_SCR_WIDTH; psp->vp.full_height = SCEGU_SCR_HEIGHT; /* Make sure anything using uncached pointers reserves * whole cachelines (memory address and size need to be a multiple of 64) * so it isn't overwritten by an unlucky cache writeback. * * This includes display lists since the Gu library uses * uncached pointers to write to them. */ /* Allocate more space if bigger display lists are needed. */ psp->main_dList = memalign(64, 256); psp->frame_dList = memalign(64, 256); psp->menu.dList = memalign(64, 256); psp->menu.frame = memalign(16, 2 * 480 * 272); psp->frame_coords = memalign(64, (((PSP_FRAME_SLICE_COUNT * sizeof(psp1_sprite_t)) + 63) & ~63)); psp->menu.frame_coords = memalign(64, (((PSP_FRAME_SLICE_COUNT * sizeof(psp1_sprite_t)) + 63) & ~63)); memset(psp->frame_coords, 0, PSP_FRAME_SLICE_COUNT * sizeof(psp1_sprite_t)); memset(psp->menu.frame_coords, 0, PSP_FRAME_SLICE_COUNT * sizeof(psp1_sprite_t)); sceKernelDcacheWritebackInvalidateAll(); psp->frame_coords = TO_UNCACHED_PTR(psp->frame_coords); psp->menu.frame_coords = TO_UNCACHED_PTR(psp->menu.frame_coords); psp->frame_coords->v0.x = 60; psp->frame_coords->v0.y = 0; psp->frame_coords->v0.u = 0; psp->frame_coords->v0.v = 0; psp->frame_coords->v1.x = 420; psp->frame_coords->v1.y = SCEGU_SCR_HEIGHT; psp->frame_coords->v1.u = 256; psp->frame_coords->v1.v = 240; psp->vsync = video->vsync; psp->rgb32 = video->rgb32; if(psp->rgb32) { u32 i; uint32_t* LUT_r_local = (uint32_t*)(SCEGU_VRAM_BP32_2); uint32_t* LUT_b_local = (uint32_t*)(SCEGU_VRAM_BP32_2) + (1 << 8); red_shift = 8 + 8; color_mask = 0xFF; lut_block_count = (1 << 8) / 8; psp->texture = (void*)(LUT_b_local + (1 << 8)); psp->draw_buffer = SCEGU_VRAM_BP32_0; psp->bpp_log2 = 2; pixel_format = GU_PSM_8888; lut_pixel_format = GU_PSM_T32; displayBuffer = SCEGU_VRAM_BP32_1; for (i = 0; i < (1 << 8); i++) { LUT_r_local[i] = i; LUT_b_local[i] = i << (8 + 8); } LUT_r = (void*)LUT_r_local; LUT_b = (void*)LUT_b_local; } else { u16 i; uint16_t* LUT_r_local = (uint16_t*)(SCEGU_VRAM_BP_2); uint16_t* LUT_b_local = (uint16_t*)(SCEGU_VRAM_BP_2) + (1 << 5); red_shift = 6 + 5; color_mask = 0x1F; lut_block_count = (1 << 5) / 8; psp->texture = (void*)(LUT_b_local + (1 << 5)); psp->draw_buffer = SCEGU_VRAM_BP_0; psp->bpp_log2 = 1; pixel_format = (video_driver_get_pixel_format() == RETRO_PIXEL_FORMAT_0RGB1555) ? GU_PSM_5551 : GU_PSM_5650 ; lut_pixel_format = GU_PSM_T16; displayBuffer = SCEGU_VRAM_BP_1; for (i = 0; i < (1 << 5); i++) { LUT_r_local[i] = i; LUT_b_local[i] = i << (5 + 6); } LUT_r = (void*)LUT_r_local; LUT_b = (void*)LUT_b_local; } psp->tex_filter = video->smooth? GU_LINEAR : GU_NEAREST; /* TODO: check if necessary. */ sceDisplayWaitVblankStart(); sceGuDisplay(GU_FALSE); sceGuStart(GU_DIRECT, psp->main_dList); sceGuDrawBuffer(pixel_format, TO_GU_POINTER(psp->draw_buffer), SCEGU_VRAM_WIDTH); sceGuDispBuffer(SCEGU_SCR_WIDTH, SCEGU_SCR_HEIGHT, TO_GU_POINTER(displayBuffer), SCEGU_VRAM_WIDTH); sceGuClearColor(0); sceGuScissor(0, 0, SCEGU_SCR_WIDTH, SCEGU_SCR_HEIGHT); sceGuEnable(GU_SCISSOR_TEST); sceGuTexFilter(psp->tex_filter, psp->tex_filter); sceGuTexWrap (GU_CLAMP, GU_CLAMP); sceGuEnable(GU_TEXTURE_2D); sceGuDisable(GU_DEPTH_TEST); sceGuCallMode(GU_FALSE); sceGuFinish(); sceGuSync(0, 0); /* TODO : check if necessary */ sceDisplayWaitVblankStart(); sceGuDisplay(GU_TRUE); pspDebugScreenSetColorMode(pixel_format); pspDebugScreenSetBase(psp->draw_buffer); /* fill frame_dList : */ sceGuStart(GU_CALL, psp->frame_dList); sceGuTexMode(pixel_format, 0, 0, GU_FALSE); sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGB); sceGuEnable(GU_BLEND); /* green only */ sceGuBlendFunc(GU_ADD, GU_FIX, GU_FIX, 0x0000FF00, 0xFFFFFFFF); sceGuDrawArray(GU_SPRITES, GU_TEXTURE_32BITF | GU_VERTEX_32BITF | GU_TRANSFORM_2D, PSP_FRAME_VERTEX_COUNT, NULL, (void*)(psp->frame_coords)); /* restore */ sceGuBlendFunc(GU_ADD, GU_FIX, GU_FIX, 0xFFFFFFFF, 0xFFFFFFFF); sceGuTexMode(lut_pixel_format, 0, 0, GU_FALSE); sceGuClutMode(pixel_format, red_shift, color_mask, 0); sceGuClutLoad(lut_block_count, LUT_r); sceGuDrawArray(GU_SPRITES, GU_TEXTURE_32BITF | GU_VERTEX_32BITF | GU_TRANSFORM_2D, PSP_FRAME_VERTEX_COUNT, NULL, (void*)(psp->frame_coords)); sceGuClutMode(pixel_format, 0, color_mask, 0); sceGuClutLoad(lut_block_count, LUT_b); sceGuDrawArray(GU_SPRITES, GU_TEXTURE_32BITF | GU_VERTEX_32BITF | GU_TRANSFORM_2D, PSP_FRAME_VERTEX_COUNT, NULL, (void*)(psp->frame_coords)); sceGuFinish(); if (input && input_data) { pspinput = input_psp.init(); *input = pspinput ? &input_psp : NULL; *input_data = pspinput; } psp->vblank_not_reached = true; sceKernelRegisterSubIntrHandler(PSP_VBLANK_INT, 0, psp_on_vblank, psp); sceKernelEnableSubIntr(PSP_VBLANK_INT, 0); psp->keep_aspect = true; psp->should_resize = true; psp->hw_render = false; return psp; error: RARCH_ERR("PSP1 video could not be initialized.\n"); return (void*)-1; }
/** * recording_init: * * Initializes recording. * * Returns: true (1) if successful, otherwise false (0). **/ bool recording_init(void) { char recording_file[PATH_MAX_LENGTH] = {0}; struct ffemu_params params = {0}; global_t *global = global_get_ptr(); driver_t *driver = driver_get_ptr(); settings_t *settings = config_get_ptr(); struct retro_system_av_info *av_info = video_viewport_get_system_av_info(); const struct retro_hw_render_callback *hw_render = (const struct retro_hw_render_callback*)video_driver_callback(); if (!global->record.enable) return false; if (global->libretro_dummy) { RARCH_WARN(RETRO_LOG_INIT_RECORDING_SKIPPED); return false; } if (!settings->video.gpu_record && hw_render->context_type) { RARCH_WARN("Libretro core is hardware rendered. Must use post-shaded recording as well.\n"); return false; } RARCH_LOG("Custom timing given: FPS: %.4f, Sample rate: %.4f\n", (float)av_info->timing.fps, (float)av_info->timing.sample_rate); strlcpy(recording_file, global->record.path, sizeof(recording_file)); if (global->record.use_output_dir) fill_pathname_join(recording_file, global->record.output_dir, global->record.path, sizeof(recording_file)); params.out_width = av_info->geometry.base_width; params.out_height = av_info->geometry.base_height; params.fb_width = av_info->geometry.max_width; params.fb_height = av_info->geometry.max_height; params.channels = 2; params.filename = recording_file; params.fps = av_info->timing.fps; params.samplerate = av_info->timing.sample_rate; params.pix_fmt = (video_driver_get_pixel_format() == RETRO_PIXEL_FORMAT_XRGB8888) ? FFEMU_PIX_ARGB8888 : FFEMU_PIX_RGB565; params.config = NULL; if (*global->record.config) params.config = global->record.config; if (settings->video.gpu_record && driver->video->read_viewport) { struct video_viewport vp = {0}; video_driver_viewport_info(&vp); if (!vp.width || !vp.height) { RARCH_ERR("Failed to get viewport information from video driver. " "Cannot start recording ...\n"); return false; } params.out_width = vp.width; params.out_height = vp.height; params.fb_width = next_pow2(vp.width); params.fb_height = next_pow2(vp.height); if (settings->video.force_aspect && (video_driver_get_aspect_ratio() > 0.0f)) params.aspect_ratio = video_driver_get_aspect_ratio(); else params.aspect_ratio = (float)vp.width / vp.height; params.pix_fmt = FFEMU_PIX_BGR24; global->record.gpu_width = vp.width; global->record.gpu_height = vp.height; RARCH_LOG("Detected viewport of %u x %u\n", vp.width, vp.height); global->record.gpu_buffer = (uint8_t*)malloc(vp.width * vp.height * 3); if (!global->record.gpu_buffer) { RARCH_ERR("Failed to allocate GPU record buffer.\n"); return false; } } else { if (global->record.width || global->record.height) { params.out_width = global->record.width; params.out_height = global->record.height; } if (settings->video.force_aspect && (video_driver_get_aspect_ratio() > 0.0f)) params.aspect_ratio = video_driver_get_aspect_ratio(); else params.aspect_ratio = (float)params.out_width / params.out_height; if (settings->video.post_filter_record && video_driver_frame_filter_alive()) { unsigned max_width = 0; unsigned max_height = 0; if (video_driver_frame_filter_is_32bit()) params.pix_fmt = FFEMU_PIX_ARGB8888; else params.pix_fmt = FFEMU_PIX_RGB565; rarch_softfilter_get_max_output_size( video_driver_frame_filter_get_ptr(), &max_width, &max_height); params.fb_width = next_pow2(max_width); params.fb_height = next_pow2(max_height); } } RARCH_LOG("Recording to %s @ %ux%u. (FB size: %ux%u pix_fmt: %u)\n", global->record.path, params.out_width, params.out_height, params.fb_width, params.fb_height, (unsigned)params.pix_fmt); if (!record_driver_init_first(&driver->recording, &driver->recording_data, ¶ms)) { RARCH_ERR(RETRO_LOG_INIT_RECORDING_FAILED); event_command(EVENT_CMD_GPU_RECORD_DEINIT); return false; } return true; }
/* Take frame bottom-up. */ static bool screenshot_dump( const char *name_base, const void *frame, unsigned width, unsigned height, int pitch, bool bgr24, void *userbuf, bool savestate, bool is_idle, bool is_paused) { char screenshot_path[PATH_MAX_LENGTH]; uint8_t *buf = NULL; #ifdef _XBOX1 d3d_video_t *d3d = (d3d_video_t*)video_driver_get_ptr(true); #endif settings_t *settings = config_get_ptr(); retro_task_t *task = (retro_task_t*)calloc(1, sizeof(*task)); screenshot_task_state_t *state = (screenshot_task_state_t*) calloc(1, sizeof(*state)); const char *screenshot_dir = settings->paths.directory_screenshot; screenshot_path[0] = '\0'; if (string_is_empty(screenshot_dir) || settings->bools.screenshots_in_content_dir) { fill_pathname_basedir(screenshot_path, name_base, sizeof(screenshot_path)); screenshot_dir = screenshot_path; } state->is_idle = is_idle; state->is_paused = is_paused; state->bgr24 = bgr24; state->height = height; state->width = width; state->pitch = pitch; state->frame = frame; state->userbuf = userbuf; state->silence = savestate; state->history_list_enable = settings->bools.history_list_enable; state->pixel_format_type = video_driver_get_pixel_format(); if (savestate) snprintf(state->filename, sizeof(state->filename), "%s.png", name_base); else { if (settings->bools.auto_screenshot_filename) fill_str_dated_filename(state->shotname, path_basename(name_base), IMG_EXT, sizeof(state->shotname)); else snprintf(state->shotname, sizeof(state->shotname), "%s.png", path_basename(name_base)); fill_pathname_join(state->filename, screenshot_dir, state->shotname, sizeof(state->filename)); } #ifdef _XBOX1 d3d->dev->GetBackBuffer(-1, D3DBACKBUFFER_TYPE_MONO, &state->surf); #elif defined(HAVE_RPNG) buf = (uint8_t*)malloc(width * height * 3); if (!buf) { if (task) free(task); free(state); return false; } state->out_buffer = buf; #endif task->type = TASK_TYPE_BLOCKING; task->state = state; task->handler = task_screenshot_handler; if (!savestate) task->title = strdup(msg_hash_to_str(MSG_TAKING_SCREENSHOT)); task_queue_push(task); return true; }
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; }
/* Take frame bottom-up. */ static bool screenshot_dump( const char *name_base, const void *frame, unsigned width, unsigned height, int pitch, bool bgr24, void *userbuf, bool savestate, bool is_idle, bool is_paused, bool fullpath, bool use_thread) { char screenshot_path[PATH_MAX_LENGTH]; uint8_t *buf = NULL; settings_t *settings = config_get_ptr(); retro_task_t *task; screenshot_task_state_t *state; const char *screenshot_dir = settings->paths.directory_screenshot; struct retro_system_info system_info; screenshot_path[0] = '\0'; if (!core_get_system_info(&system_info)) return false; task = task_init(); state = (screenshot_task_state_t*)calloc(1, sizeof(*state)); state->shotname[0] = '\0'; /* If fullpath is true, name_base already contains a static path + filename to save the screenshot to. */ if (fullpath) strlcpy(state->filename, name_base, sizeof(state->filename)); else { if (string_is_empty(screenshot_dir) || settings->bools.screenshots_in_content_dir) { fill_pathname_basedir(screenshot_path, name_base, sizeof(screenshot_path)); screenshot_dir = screenshot_path; } } state->is_idle = is_idle; state->is_paused = is_paused; state->bgr24 = bgr24; state->height = height; state->width = width; state->pitch = pitch; state->frame = frame; state->userbuf = userbuf; state->silence = savestate; state->history_list_enable = settings->bools.history_list_enable; state->pixel_format_type = video_driver_get_pixel_format(); if (!fullpath) { if (savestate) snprintf(state->filename, sizeof(state->filename), "%s.png", name_base); else { if (settings->bools.auto_screenshot_filename) { const char *screenshot_name = NULL; if (path_is_empty(RARCH_PATH_CONTENT)) { if (string_is_empty(system_info.library_name)) screenshot_name = "RetroArch"; else screenshot_name = system_info.library_name; } else screenshot_name = path_basename(name_base); fill_str_dated_filename(state->shotname, screenshot_name, IMG_EXT, sizeof(state->shotname)); } else snprintf(state->shotname, sizeof(state->shotname), "%s.png", path_basename(name_base)); fill_pathname_join(state->filename, screenshot_dir, state->shotname, sizeof(state->filename)); } } #if defined(HAVE_RPNG) buf = (uint8_t*)malloc(width * height * 3); if (!buf) { if (task) free(task); free(state); return false; } state->out_buffer = buf; #endif task->type = TASK_TYPE_BLOCKING; task->state = state; task->handler = task_screenshot_handler; if (use_thread) { if (!savestate) task->title = strdup(msg_hash_to_str(MSG_TAKING_SCREENSHOT)); if (!task_queue_push(task)) { /* There is already a blocking task going on */ if (task->title) task_free_title(task); free(task); if (state->out_buffer) free(state->out_buffer); free(state); return false; } } else { if (task) free(task); return screenshot_dump_direct(state); } return true; }