From 8adc24ecbcaf1766b50b782032dff4c2b9e50207 Mon Sep 17 00:00:00 2001 From: bulzipke Date: Sat, 4 Sep 2021 01:14:03 +0900 Subject: [PATCH] (3DS) Add bottom screen menu (#12470) * (3DS) Add bottom screen menu -> User can save/load state on botom screen with thumbnail. -> Call a save_state_to_file() when RAM state has data to write a disk. -> If the bottom screen needs updating, swap the bottom framebuffers. Add: SAVE/LODE STATE TO RAM -> This is useful for devices with slow I/O -> 3DS bottom save state use CMD_EVENT_SAVE_STATE_TO_RAM -> 3DS bottom load state use CMD_EVENT_LOAD_STATE when RAM state has no data -> 3DS bottom load state use CMD_EVENT_LOAD_STATE_FROM_RAM when RAM sate has data * Rewrite path_get_state to retroarch_get_current_savestate_path * Fix unterminated state_path --- Makefile.ctr | 5 + Makefile.ctr.salamander | 5 + command.h | 3 + content.h | 9 + frontend/drivers/platform_ctr.c | 14 +- gfx/common/ctr_common.h | 38 +- gfx/drivers/ctr_gfx.c | 735 +++++++++++++++++++++++++++--- gfx/drivers_font/ctr_font.c | 65 ++- input/drivers_joypad/ctr_joypad.c | 8 - intl/msg_hash_us.h | 32 ++ menu/menu_setting.c | 2 + msg_hash.h | 9 + retroarch.c | 88 ++-- retroarch.h | 2 + tasks/task_save.c | 155 +++++++ 15 files changed, 1051 insertions(+), 119 deletions(-) diff --git a/Makefile.ctr b/Makefile.ctr index c463e93bd6..54c607ef13 100644 --- a/Makefile.ctr +++ b/Makefile.ctr @@ -2,6 +2,7 @@ TARGET := retroarch_3ds LIBRETRO = DEBUG = 0 +CONSOLE_LOG = 0 GRIFFIN_BUILD = 1 WHOLE_ARCHIVE_LINK = 0 BUILD_3DSX = 1 @@ -154,6 +155,10 @@ else CFLAGS += -O3 endif +ifeq ($(CONSOLE_LOG), 1) + CFLAGS += -DCONSOLE_LOG +endif + ifeq ($(LIBCTRU_NO_DEPRECATION), 1) CFLAGS += -DLIBCTRU_NO_DEPRECATION endif diff --git a/Makefile.ctr.salamander b/Makefile.ctr.salamander index 861a599a44..1d16ede085 100644 --- a/Makefile.ctr.salamander +++ b/Makefile.ctr.salamander @@ -2,6 +2,7 @@ TARGET := retroarch_3ds_salamander LIBRETRO = DEBUG = 0 +CONSOLE_LOG = 0 BUILD_3DSX = 1 BUILD_3DS = 0 BUILD_CIA = 1 @@ -95,6 +96,10 @@ else CFLAGS += -O3 endif +ifeq ($(CONSOLE_LOG), 1) + CFLAGS += -DCONSOLE_LOG +endif + CFLAGS += -I. -Ideps/7zip -Ideps/stb -Ilibretro-common/include -Ilibretro-common/include/compat/zlib #CFLAGS += -DRARCH_INTERNAL diff --git a/command.h b/command.h index da06f01df4..e408a11473 100644 --- a/command.h +++ b/command.h @@ -79,13 +79,16 @@ enum event_command CMD_EVENT_UNLOAD_CORE, CMD_EVENT_CLOSE_CONTENT, CMD_EVENT_LOAD_STATE, + CMD_EVENT_LOAD_STATE_FROM_RAM, /* Swaps the current state with what's on the undo load buffer */ CMD_EVENT_UNDO_LOAD_STATE, /* Rewrites a savestate on disk */ CMD_EVENT_UNDO_SAVE_STATE, CMD_EVENT_SAVE_STATE, + CMD_EVENT_SAVE_STATE_TO_RAM, CMD_EVENT_SAVE_STATE_DECREMENT, CMD_EVENT_SAVE_STATE_INCREMENT, + CMD_EVENT_RAM_STATE_TO_FILE, /* Takes screenshot. */ CMD_EVENT_TAKE_SCREENSHOT, /* Quits RetroArch. */ diff --git a/content.h b/content.h index 26edc3f287..c54de0322a 100644 --- a/content.h +++ b/content.h @@ -45,6 +45,15 @@ bool content_load_ram_file(unsigned slot); /* Save a RAM state from memory to disk. */ bool content_save_ram_file(unsigned slot, bool compress); +/* Load a state from memory. */ +bool content_load_state_from_ram(void); + +/* Save a state to memory. */ +bool content_save_state_to_ram(void); + +/* Save a ram state from memory to disk. */ +bool content_ram_state_to_file(const char *path); + /* Load a state from disk to memory. */ bool content_load_state(const char* path, bool load_to_backup_buffer, bool autoload); diff --git a/frontend/drivers/platform_ctr.c b/frontend/drivers/platform_ctr.c index 5d41dea9c9..4b8e178e17 100644 --- a/frontend/drivers/platform_ctr.c +++ b/frontend/drivers/platform_ctr.c @@ -162,8 +162,10 @@ static void frontend_ctr_deinit(void* data) verbosity_enable(); retro_main_log_file_init(NULL, false); +#ifdef CONSOLE_LOG if (ctr_bottom_screen_enabled && (ctr_fork_mode == FRONTEND_FORK_NONE)) wait_for_input(); +#endif CFGU_GetModelNintendo2DS(¬_2DS); @@ -419,13 +421,15 @@ void gfxSetFramebufferInfo(gfxScreen_t screen, u8 id) id, (u32*)gfxBottomFramebuffers[id], (u32*)gfxBottomFramebuffers[id], - 240 * 2, - GSP_RGB565_OES); + 240 * 3, + GSP_BGR8_OES); } } #endif +#ifdef CONSOLE_LOG PrintConsole* ctrConsole; +#endif static void frontend_ctr_init(void* data) { @@ -436,10 +440,10 @@ static void frontend_ctr_init(void* data) verbosity_enable(); - gfxInit(GSP_BGR8_OES, GSP_RGB565_OES, false); + gfxInit(GSP_BGR8_OES, GSP_BGR8_OES, false); u32 topSize = 400 * 240 * 3; - u32 bottomSize = 320 * 240 * 2; + u32 bottomSize = 320 * 240 * 3; #ifdef USE_CTRULIB_2 linearFree(gfxGetFramebuffer(GFX_TOP, GFX_LEFT, NULL, NULL)); @@ -473,7 +477,9 @@ static void frontend_ctr_init(void* data) gfxSetFramebufferInfo(GFX_BOTTOM, 0); gfxSet3D(true); +#ifdef CONSOLE_LOG ctrConsole = consoleInit(GFX_BOTTOM, NULL); +#endif /* enable access to all service calls when possible. */ if (svchax_init) diff --git a/gfx/common/ctr_common.h b/gfx/common/ctr_common.h index 7a724e65c0..658a08f638 100644 --- a/gfx/common/ctr_common.h +++ b/gfx/common/ctr_common.h @@ -21,8 +21,11 @@ #define COLOR_ABGR(r, g, b, a) (((unsigned)(a) << 24) | ((b) << 16) | ((g) << 8) | ((r) << 0)) -#define CTR_TOP_FRAMEBUFFER_WIDTH 400 -#define CTR_TOP_FRAMEBUFFER_HEIGHT 240 +#define CTR_TOP_FRAMEBUFFER_WIDTH 400 +#define CTR_TOP_FRAMEBUFFER_HEIGHT 240 +#define CTR_BOTTOM_FRAMEBUFFER_WIDTH 320 +#define CTR_BOTTOM_FRAMEBUFFER_HEIGHT 240 +#define CTR_STATE_DATE_SIZE 11 #ifdef USE_CTRULIB_2 extern u8* gfxTopLeftFramebuffers[2]; @@ -30,7 +33,9 @@ extern u8* gfxTopRightFramebuffers[2]; extern u8* gfxBottomFramebuffers[2]; #endif +#ifdef CONSOLE_LOG extern PrintConsole* ctrConsole; +#endif extern const u8 ctr_sprite_shbin[]; extern const u32 ctr_sprite_shbin_size; @@ -58,6 +63,13 @@ typedef enum CTR_VIDEO_MODE_LAST } ctr_video_mode_enum; +typedef enum +{ + CTR_BOTTOM_MENU_NOT_AVAILABLE = 0, + CTR_BOTTOM_MENU_DEFAULT, + CTR_BOTTOM_MENU_SELECT, +} ctr_bottom_menu; + typedef struct ctr_video { struct @@ -67,6 +79,7 @@ typedef struct ctr_video void* left; void* right; }top; + void* bottom; }drawbuffers; void* depthbuffer; @@ -116,11 +129,10 @@ typedef struct ctr_video bool overlay_full_screen; #endif - void* empty_framebuffer; - aptHookCookie lcd_aptHook; ctr_video_mode_enum video_mode; int current_buffer_top; + int current_buffer_bottom; bool p3d_event_pending; bool ppf_event_pending; @@ -133,6 +145,17 @@ typedef struct ctr_video int size; }vertex_cache; + bool init_bottom_menu; + bool refresh_bottom_menu; + bool render_font_bottom; + bool render_state_from_png_file; + bool state_data_on_ram; + bool state_data_exist; + char state_date[CTR_STATE_DATE_SIZE]; + int state_slot; + ctr_bottom_menu bottom_menu; + ctr_bottom_menu prev_bottom_menu; + struct ctr_bottom_texture_data *bottom_textures; } ctr_video_t; typedef struct ctr_texture @@ -156,6 +179,13 @@ struct ctr_overlay_data }; #endif +struct ctr_bottom_texture_data +{ + uintptr_t texture; + ctr_vertex_t* frame_coords; + ctr_scale_vector_t scale_vector; +}; + static INLINE void ctr_set_scale_vector(ctr_scale_vector_t* vec, int viewport_width, int viewport_height, int texture_width, int texture_height) diff --git a/gfx/drivers/ctr_gfx.c b/gfx/drivers/ctr_gfx.c index db2f202078..778008952b 100644 --- a/gfx/drivers/ctr_gfx.c +++ b/gfx/drivers/ctr_gfx.c @@ -50,6 +50,16 @@ #include "../../tasks/tasks_internal.h" #endif +enum +{ + CTR_TEXTURE_BOTTOM_MENU, + CTR_TEXTURE_STATE_THUMBNAIL, + CTR_TEXTURE_LAST +}; + +/* TODO/FIXME - global referenced outside */ +extern uint64_t lifecycle_state; + /* An annoyance... * Have to keep track of bottom screen enable state * externally, otherwise cannot detect current state @@ -244,6 +254,551 @@ static void ctr_update_viewport( } +static const char *ctr_texture_path(unsigned id) +{ + switch (id) + { + case CTR_TEXTURE_BOTTOM_MENU: + return "ctr/bottom_menu.png"; + case CTR_TEXTURE_STATE_THUMBNAIL: + { + static char texture_path[PATH_MAX_LENGTH]; + + char state_path[PATH_MAX_LENGTH]; + if (!retroarch_get_current_savestate_path(state_path, + sizeof(state_path))) + return NULL; + + snprintf(texture_path, sizeof(texture_path), + "%s.png", state_path); + + return path_basename(texture_path); + } + } + + return NULL; +} + +static void ctr_update_state_date(void *data) +{ + ctr_video_t *ctr = (ctr_video_t*)data; + + time_t now = time(NULL); + + struct tm *t = localtime(&now); + sprintf(ctr->state_date, "%02d/%02d/%d", + t->tm_mon + 1, t->tm_mday, t->tm_year + 1900); +} + +static bool ctr_update_state_date_from_file(void *data) +{ + char state_path[PATH_MAX_LENGTH]; + ctr_video_t *ctr = (ctr_video_t*)data; + + if (!retroarch_get_current_savestate_path(state_path, sizeof(state_path))) + return false; + +#ifdef USE_CTRULIB_2 + time_t mtime; + bool file_exists = archive_getmtime(state_path + 5, &mtime) == 0; +#else + u64 mtime; + bool file_exists = sdmc_getmtime(state_path + 5, &mtime) == 0; +#endif + if (!file_exists) + { + ctr->state_data_exist = false; + snprintf(ctr->state_date, sizeof(ctr->state_date), "00/00/0000"); + return false; + } + + ctr->state_data_exist = true; + +#ifdef USE_CTRULIB_2 + struct tm *t = localtime(&mtime); +#else + time_t ft = mtime; + struct tm *t = localtime(&ft); +#endif + sprintf(ctr->state_date, "%02d/%02d/%d", + t->tm_mon + 1, t->tm_mday, t->tm_year + 1900); + + return true; +} + +static void ctr_state_thumbnail_geom(void *data) +{ + ctr_video_t *ctr = (ctr_video_t *) data; + struct ctr_bottom_texture_data *o = NULL; + const int target_width = 120; + const int target_height = 90; + unsigned width, height; + + if (ctr) + o = &ctr->bottom_textures[CTR_TEXTURE_STATE_THUMBNAIL]; + + if (!o) + return; + + ctr_texture_t *texture = (ctr_texture_t *) o->texture; + if (!texture) + return; + + float scale = (float) target_width / texture->active_width; + if (target_width > texture->active_width * scale) { + scale = (float) (target_width + 1) / texture->active_width; + } + + o->frame_coords->u0 = 0; + o->frame_coords->v0 = 0; + o->frame_coords->u1 = texture->active_width; + o->frame_coords->v1 = texture->active_height; + + int x_offset = 184; + int y_offset = 46 + (target_height - texture->active_height * scale) / 2; + + o->frame_coords->x0 = x_offset; + o->frame_coords->y0 = y_offset; + o->frame_coords->x1 = o->frame_coords->x0 + texture->active_width * scale; + o->frame_coords->y1 = o->frame_coords->y0 + texture->active_height * scale; + + ctr_set_scale_vector(&o->scale_vector, + CTR_BOTTOM_FRAMEBUFFER_WIDTH, + CTR_BOTTOM_FRAMEBUFFER_HEIGHT, + texture->width, + texture->height); +} + +static bool ctr_load_bottom_texture(void *data) +{ + unsigned i; + const char *dir_assets; + + ctr_video_t *ctr = (ctr_video_t *)data; + settings_t *settings = config_get_ptr(); + + for (i = 0; i < CTR_TEXTURE_LAST; i++) + { + if (i == CTR_TEXTURE_STATE_THUMBNAIL) + dir_assets = dir_get_ptr(RARCH_DIR_SAVESTATE); + else + dir_assets = settings->paths.directory_assets; + + if (gfx_display_reset_textures_list( + ctr_texture_path(i), dir_assets, + &ctr->bottom_textures[i].texture, + TEXTURE_FILTER_MIPMAP_LINEAR, NULL, NULL)) + { + struct ctr_bottom_texture_data *o = &ctr->bottom_textures[i]; + o->frame_coords = linearAlloc(sizeof(ctr_vertex_t)); + + if (i == CTR_TEXTURE_STATE_THUMBNAIL) + { + ctr_state_thumbnail_geom(ctr); + ctr->render_state_from_png_file = true; + } + else + { + ctr_texture_t *texture = (ctr_texture_t *) o->texture; + + o->frame_coords->u0 = 0; + o->frame_coords->v0 = 0; + o->frame_coords->u1 = texture->width; + o->frame_coords->v1 = texture->height; + + o->frame_coords->x0 = 0; + o->frame_coords->y0 = 0; + o->frame_coords->x1 = o->frame_coords->x0 + texture->width; + o->frame_coords->y1 = o->frame_coords->y0 + texture->height; + + ctr_set_scale_vector(&o->scale_vector, + CTR_BOTTOM_FRAMEBUFFER_WIDTH, + CTR_BOTTOM_FRAMEBUFFER_HEIGHT, + texture->width, + texture->height); + } + } + else if (i == CTR_TEXTURE_BOTTOM_MENU) + return false; + } + + return true; +} + +static void save_state_to_file(void *data) +{ + ctr_video_t *ctr = (ctr_video_t*)data; + + char state_path[PATH_MAX_LENGTH]; + global_t *global = global_get_ptr(); + const char *name_savestate = global->name.savestate; + + if (ctr->state_slot > 0) + snprintf(state_path, sizeof(state_path), "%s%d", name_savestate, + ctr->state_slot); + else if (ctr->state_slot < 0) + fill_pathname_join_delim(state_path, + name_savestate, "auto", '.', sizeof(state_path)); + else + strlcpy(state_path, name_savestate, sizeof(state_path)); + + command_event(CMD_EVENT_RAM_STATE_TO_FILE, state_path); +} + +static void bottom_menu_control(void* data, bool lcd_bottom) +{ + touchPosition state_tmp_touch; + uint32_t state_tmp = 0; + ctr_video_t *ctr = (ctr_video_t*)data; + settings_t *settings = config_get_ptr(); + int config_slot = settings->ints.state_slot; + + if (!ctr->init_bottom_menu) + { + if (ctr_load_bottom_texture(ctr)) + { + ctr_update_state_date_from_file(ctr); + ctr->bottom_menu = CTR_BOTTOM_MENU_DEFAULT; + } + + ctr->init_bottom_menu = true; + } + + BIT64_CLEAR(lifecycle_state, RARCH_MENU_TOGGLE); + + state_tmp = hidKeysDown(); + hidTouchRead(&state_tmp_touch); + + if (state_tmp & KEY_TOUCH) + { +#ifdef CONSOLE_LOG + BIT64_SET(lifecycle_state, RARCH_MENU_TOGGLE); + return; +#endif + if (!rarch_ctl(RARCH_CTL_CORE_IS_RUNNING, NULL)) + return; + + if (!lcd_bottom || + ctr->bottom_menu == CTR_BOTTOM_MENU_NOT_AVAILABLE) + { + BIT64_SET(lifecycle_state, RARCH_MENU_TOGGLE); + return; + } + + switch (ctr->bottom_menu) + { + case CTR_BOTTOM_MENU_DEFAULT: + BIT64_SET(lifecycle_state, RARCH_MENU_TOGGLE); + break; + case CTR_BOTTOM_MENU_SELECT: + if (state_tmp_touch.px > 8 && + state_tmp_touch.px < 164 && + state_tmp_touch.py > 9 && + state_tmp_touch.py < 86) + { + BIT64_SET(lifecycle_state, RARCH_MENU_TOGGLE); + } + else if (state_tmp_touch.px > 8 && + state_tmp_touch.px < 164 && + state_tmp_touch.py > 99 && + state_tmp_touch.py < 230) + { + char screenshot_full_path[PATH_MAX_LENGTH]; + + struct ctr_bottom_texture_data *o = + &ctr->bottom_textures[CTR_TEXTURE_STATE_THUMBNAIL]; + + ctr_texture_t *texture = (ctr_texture_t *) o->texture; + if (texture) + linearFree(texture->data); + else + { + o->texture = (uintptr_t) calloc(1, sizeof(ctr_texture_t)); + o->frame_coords = linearAlloc(sizeof(ctr_vertex_t)); + texture = (ctr_texture_t *) o->texture; + } + + texture->width = ctr->texture_width; + texture->height = ctr->texture_width; + texture->active_width = ctr->frame_coords->u1; + texture->active_height = ctr->frame_coords->v1; + + texture->data = linearAlloc( + ctr->texture_width * ctr->texture_height * + (ctr->rgb32? 4:2)); + + memcpy(texture->data, ctr->texture_swizzled, + ctr->texture_width * ctr->texture_height * + (ctr->rgb32? 4:2)); + + ctr_state_thumbnail_geom(ctr); + + ctr->state_data_exist = true; + ctr->state_data_on_ram = true; + ctr->render_state_from_png_file = false; + + ctr_update_state_date(ctr); + + command_event(CMD_EVENT_SAVE_STATE_TO_RAM, NULL); + + if (settings->bools.savestate_thumbnail_enable) + { + sprintf(screenshot_full_path, "%s/%s", + dir_get_ptr(RARCH_DIR_SAVESTATE), + ctr_texture_path(CTR_TEXTURE_STATE_THUMBNAIL)); + + take_screenshot(NULL, screenshot_full_path, true, + video_driver_cached_frame_has_valid_framebuffer(), + true, true); + } + + BIT64_SET(lifecycle_state, RARCH_MENU_TOGGLE); + } + else if (state_tmp_touch.px > 176 && + state_tmp_touch.px < 311 && + state_tmp_touch.py > 9 && + state_tmp_touch.py < 230 && + ctr->state_data_exist) + { + if (ctr->state_data_on_ram) + command_event(CMD_EVENT_LOAD_STATE_FROM_RAM, NULL); + else + command_event(CMD_EVENT_LOAD_STATE, NULL); + BIT64_SET(lifecycle_state, RARCH_MENU_TOGGLE); + } + break; + } + + ctr->refresh_bottom_menu = true; + } + + if (ctr->bottom_menu == CTR_BOTTOM_MENU_NOT_AVAILABLE || + !rarch_ctl(RARCH_CTL_CORE_IS_RUNNING, NULL)) + return; + + if (ctr->state_slot != config_slot) + { + if (ctr->state_data_on_ram) + { + save_state_to_file(ctr); + ctr->state_data_on_ram = false; + } + + ctr->state_slot = config_slot; + + struct ctr_bottom_texture_data *o = &ctr->bottom_textures[CTR_TEXTURE_STATE_THUMBNAIL]; + ctr_texture_t *texture = (ctr_texture_t *) o->texture; + + if (texture) + { + linearFree(texture->data); + linearFree(o->frame_coords); + o->texture = 0; + } + + if (ctr_update_state_date_from_file(ctr)) + { + if(gfx_display_reset_textures_list( + ctr_texture_path(CTR_TEXTURE_STATE_THUMBNAIL), + dir_get_ptr(RARCH_DIR_SAVESTATE), + &o->texture, + TEXTURE_FILTER_MIPMAP_LINEAR, NULL, NULL)) + { + o->frame_coords = linearAlloc(sizeof(ctr_vertex_t)); + ctr_state_thumbnail_geom(ctr); + ctr->render_state_from_png_file = true; + } + } + + ctr->refresh_bottom_menu = true; + } + + if (menu_driver_is_alive()) + ctr->bottom_menu = CTR_BOTTOM_MENU_SELECT; + else + ctr->bottom_menu = CTR_BOTTOM_MENU_DEFAULT; + + if (ctr->prev_bottom_menu != ctr->bottom_menu) + { + ctr->prev_bottom_menu = ctr->bottom_menu; + ctr->refresh_bottom_menu = true; + } +} + +static void font_driver_render_msg_bottom( + void *data, + const char *msg, + const void *_params, + void *font_data) +{ + ctr_video_t *ctr = (ctr_video_t*)data; + + ctr->render_font_bottom = true; + font_driver_render_msg(ctr, msg, _params, font_data); + ctr->render_font_bottom = false; +} + +static void ctr_render_bottom_screen(void *data) +{ + ctr_video_t *ctr = (ctr_video_t*)data; + + if (!ctr) + return; + + if (!ctr->refresh_bottom_menu) + return; + + struct font_params params = { 0, }; + params.text_align = TEXT_ALIGN_CENTER; + params.color = COLOR_ABGR(255, 255, 255, 255); + + switch (ctr->bottom_menu) + { + case CTR_BOTTOM_MENU_NOT_AVAILABLE: + { + params.scale = 1.6f; + params.x = 0.0f; + params.y = 0.5f; + + font_driver_render_msg_bottom(ctr, + msg_hash_to_str(MSG_3DS_BOTTOM_MENU_ASSET_NOT_FOUND), + ¶ms, NULL); + } + break; + case CTR_BOTTOM_MENU_DEFAULT: + { + params.scale = 1.6f; + params.x = 0.0f; + params.y = 0.5f; + + font_driver_render_msg_bottom(ctr, + msg_hash_to_str(MSG_3DS_BOTTOM_MENU_DEFAULT), + ¶ms, NULL); + } + break; + case CTR_BOTTOM_MENU_SELECT: + { + params.scale = 1.48f; + params.color = COLOR_ABGR(255, 255, 255, 255); + + /* draw state thumbnail */ + if (ctr->state_data_exist) { + struct ctr_bottom_texture_data *o = (struct ctr_bottom_texture_data*) + &ctr->bottom_textures[CTR_TEXTURE_STATE_THUMBNAIL]; + + ctr_texture_t *texture = (ctr_texture_t *) o->texture; + if (texture) + { + GPU_TEXCOLOR colorType = GPU_RGBA8; + if (!ctr->render_state_from_png_file && !ctr->rgb32) + colorType = GPU_RGB565; + + ctrGuSetTexture(GPU_TEXUNIT0, VIRT_TO_PHYS(texture->data), + texture->width, texture->height, + GPU_TEXTURE_MAG_FILTER(GPU_LINEAR) | GPU_TEXTURE_MIN_FILTER(GPU_LINEAR) | + GPU_TEXTURE_WRAP_S(GPU_CLAMP_TO_EDGE) | GPU_TEXTURE_WRAP_T(GPU_CLAMP_TO_EDGE), + colorType); + + GPUCMD_AddWrite(GPUREG_GSH_BOOLUNIFORM, 0); + ctrGuSetVertexShaderFloatUniform(0, (float*)&o->scale_vector, 1); + ctrGuSetAttributeBuffersAddress(VIRT_TO_PHYS(o->frame_coords)); + + GPU_SetViewport(NULL, + VIRT_TO_PHYS(ctr->drawbuffers.bottom), + 0, 0, CTR_BOTTOM_FRAMEBUFFER_HEIGHT, + CTR_BOTTOM_FRAMEBUFFER_WIDTH); + GPU_DrawArray(GPU_GEOMETRY_PRIM, 0, 1); + } + else + { + params.x = 0.266f; + params.y = 0.64f; + font_driver_render_msg_bottom(ctr, + msg_hash_to_str(MSG_3DS_BOTTOM_MENU_NO_STATE_THUMBNAIL), + ¶ms, NULL); + } + } + else + { + params.x = 0.266f; + params.y = 0.64f; + font_driver_render_msg_bottom(ctr, + msg_hash_to_str(MSG_3DS_BOTTOM_MENU_NO_STATE_DATA), + ¶ms, NULL); + } + + /* draw bottom menu */ + struct ctr_bottom_texture_data *o = &ctr->bottom_textures[CTR_TEXTURE_BOTTOM_MENU]; + ctr_texture_t *texture = (ctr_texture_t *) o->texture; + + ctrGuSetTexture(GPU_TEXUNIT0, VIRT_TO_PHYS(texture->data), + texture->width, texture->height, + GPU_TEXTURE_MAG_FILTER(GPU_LINEAR) | GPU_TEXTURE_MIN_FILTER(GPU_LINEAR) | + GPU_TEXTURE_WRAP_S(GPU_CLAMP_TO_EDGE) | GPU_TEXTURE_WRAP_T(GPU_CLAMP_TO_EDGE), + GPU_RGBA8); + + GPUCMD_AddWrite(GPUREG_GSH_BOOLUNIFORM, 0); + ctrGuSetVertexShaderFloatUniform(0, (float*)&o->scale_vector, 1); + ctrGuSetAttributeBuffersAddress(VIRT_TO_PHYS(o->frame_coords)); + + GPU_SetViewport(NULL, + VIRT_TO_PHYS(ctr->drawbuffers.bottom), + 0, 0, CTR_BOTTOM_FRAMEBUFFER_HEIGHT, + CTR_BOTTOM_FRAMEBUFFER_WIDTH); + GPU_DrawArray(GPU_GEOMETRY_PRIM, 0, 1); + + /* draw resume game */ + params.x = -0.178f; + params.y = 0.78f; + + font_driver_render_msg_bottom(ctr, + msg_hash_to_str(MSG_3DS_BOTTOM_MENU_RESUME), + ¶ms, NULL); + + /* draw create restore point */ + params.x = -0.178f; + params.y = 0.33f; + + font_driver_render_msg_bottom(ctr, + msg_hash_to_str(MSG_3DS_BOTTOM_MENU_SAVE_STATE), + ¶ms, NULL); + + /* draw load restore point */ + params.x = 0.266f; + params.y = 0.24f; + + font_driver_render_msg_bottom(ctr, + msg_hash_to_str(MSG_3DS_BOTTOM_MENU_LOAD_STATE), + ¶ms, NULL); + + /* draw date */ + params.x = 0.266f; + params.y = 0.87f; + font_driver_render_msg_bottom(ctr, ctr->state_date, + ¶ms, NULL); + } + break; + } +} + +static void ctr_set_bottom_screen_enable(bool enabled) +{ + Handle lcd_handle; + u8 not_2DS; + + CFGU_GetModelNintendo2DS(¬_2DS); + if(not_2DS && srvGetServiceHandle(&lcd_handle, "gsp::Lcd") >= 0) + { + u32 *cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = enabled? 0x00110040: 0x00120040; + cmdbuf[1] = 2; + svcSendSyncRequest(lcd_handle); + svcCloseHandle(lcd_handle); + } + + ctr_bottom_screen_enabled = enabled; +} + static void ctr_lcd_aptHook(APT_HookType hook, void* param) { ctr_video_t *ctr = (ctr_video_t*)param; @@ -303,16 +858,12 @@ static void ctr_lcd_aptHook(APT_HookType hook, void* param) if ((hook == APTHOOK_ONSUSPEND) || (hook == APTHOOK_ONRESTORE) || (hook == APTHOOK_ONWAKEUP)) { - Handle lcd_handle; - u8 not_2DS; - CFGU_GetModelNintendo2DS(¬_2DS); - if(not_2DS && srvGetServiceHandle(&lcd_handle, "gsp::Lcd") >= 0) + ctr_set_bottom_screen_enable(hook == APTHOOK_ONSUSPEND); + + if (ctr->state_data_on_ram) { - u32 *cmdbuf = getThreadCommandBuffer(); - cmdbuf[0] = ((hook == APTHOOK_ONSUSPEND) || ctr_bottom_screen_enabled)? 0x00110040: 0x00120040; - cmdbuf[1] = 2; - svcSendSyncRequest(lcd_handle); - svcCloseHandle(lcd_handle); + save_state_to_file(ctr); + ctr->state_data_on_ram = false; } } @@ -342,34 +893,11 @@ static bool ctr_tasks_finder(retro_task_t *task,void *userdata) task_finder_data_t ctr_tasks_finder_data = {ctr_tasks_finder, NULL}; #endif -static void ctr_set_bottom_screen_enable(void* data, bool enabled) -{ - Handle lcd_handle; - u8 not_2DS; - ctr_video_t *ctr = (ctr_video_t*)data; - - if (!ctr) - return; - - gfxBottomFramebuffers[0] = enabled ? (u8*)ctrConsole->frameBuffer: - (u8*)ctr->empty_framebuffer; - - CFGU_GetModelNintendo2DS(¬_2DS); - if(not_2DS && srvGetServiceHandle(&lcd_handle, "gsp::Lcd") >= 0) - { - u32 *cmdbuf = getThreadCommandBuffer(); - cmdbuf[0] = enabled? 0x00110040: 0x00120040; - cmdbuf[1] = 2; - svcSendSyncRequest(lcd_handle); - svcCloseHandle(lcd_handle); - } - - ctr_bottom_screen_enabled = enabled; -} static void* ctr_init(const video_info_t* video, input_driver_t** input, void** input_data) { + size_t i; float refresh_rate; u8 device_model = 0xFF; void* ctrinput = NULL; @@ -392,6 +920,7 @@ static void* ctr_init(const video_info_t* video, ctr->drawbuffers.top.left = vramAlloc(CTR_TOP_FRAMEBUFFER_WIDTH * CTR_TOP_FRAMEBUFFER_HEIGHT * 2 * sizeof(uint32_t)); ctr->drawbuffers.top.right = (void*)((uint32_t*)ctr->drawbuffers.top.left + CTR_TOP_FRAMEBUFFER_WIDTH * CTR_TOP_FRAMEBUFFER_HEIGHT); + ctr->drawbuffers.bottom = vramAlloc(CTR_BOTTOM_FRAMEBUFFER_WIDTH * CTR_BOTTOM_FRAMEBUFFER_HEIGHT * 2 * sizeof(uint32_t)); ctr->display_list_size = 0x4000; ctr->display_list = linearAlloc(ctr->display_list_size * sizeof(uint32_t)); @@ -401,6 +930,22 @@ static void* ctr_init(const video_info_t* video, ctr->vertex_cache.buffer = linearAlloc(ctr->vertex_cache.size * sizeof(ctr_vertex_t)); ctr->vertex_cache.current = ctr->vertex_cache.buffer; + ctr->bottom_textures = (struct ctr_bottom_texture_data *)calloc(CTR_TEXTURE_LAST, + sizeof(*ctr->bottom_textures)); + + ctr->init_bottom_menu = false; + ctr->state_data_exist = false; + ctr->state_data_on_ram = false; + ctr->render_font_bottom = false; + ctr->refresh_bottom_menu = true; + ctr->render_state_from_png_file = false; + ctr->bottom_menu = CTR_BOTTOM_MENU_NOT_AVAILABLE; + ctr->prev_bottom_menu = CTR_BOTTOM_MENU_NOT_AVAILABLE; + ctr->state_slot = settings->ints.state_slot; + + snprintf(ctr->state_date, sizeof(ctr->state_date), "%s", "00/00/0000"); + ctr->state_date[CTR_STATE_DATE_SIZE - 1] = '\0'; + ctr->rgb32 = video->rgb32; ctr->texture_width = video->input_scale * RARCH_SCALE_BASE; ctr->texture_height = video->input_scale * RARCH_SCALE_BASE; @@ -512,6 +1057,7 @@ static void* ctr_init(const video_info_t* video, ctr->smooth = video->smooth; ctr->vsync = video->vsync; ctr->current_buffer_top = 0; + ctr->current_buffer_bottom = 0; /* Only O3DS and O3DSXL support running in 'dual-framebuffer' * mode with the parallax barrier disabled @@ -520,9 +1066,6 @@ static void* ctr_init(const video_info_t* video, CFGU_GetSystemModel(&device_model); /* (0 = O3DS, 1 = O3DSXL, 2 = N3DS, 3 = 2DS, 4 = N3DSXL, 5 = N2DSXL) */ ctr->supports_parallax_disable = (device_model == 0) || (device_model == 1); - ctr->empty_framebuffer = linearAlloc(320 * 240 * 2); - memset(ctr->empty_framebuffer, 0, 320 * 240 * 2); - refresh_rate = (32730.0 * 8192.0) / 4481134.0; driver_ctl(RARCH_DRIVER_CTL_SET_REFRESH_RATE, &refresh_rate); @@ -539,7 +1082,7 @@ static void* ctr_init(const video_info_t* video, /* Set bottom screen enable state, if required */ if (lcd_bottom != ctr_bottom_screen_enabled) - ctr_set_bottom_screen_enable(ctr, lcd_bottom); + ctr_set_bottom_screen_enable(lcd_bottom); gspSetEventCallback(GSPGPU_EVENT_VBlank0, (ThreadFunc)ctr_vsync_hook, ctr, false); @@ -557,12 +1100,10 @@ static bool ctr_frame(void* data, const void* frame, unsigned pitch, const char* msg, video_frame_info_t *video_info) { static uint64_t currentTick,lastTick; - touchPosition state_tmp_touch; - extern GSPGPU_FramebufferInfo topFramebufferInfo; + extern GSPGPU_FramebufferInfo topFramebufferInfo, bottomFramebufferInfo; extern u8* gfxSharedMemory; extern u8 gfxThreadID; uint32_t diff; - uint32_t state_tmp = 0; ctr_video_t *ctr = (ctr_video_t*)data; static float fps = 0.0; static int total_frames = 0; @@ -584,6 +1125,7 @@ static bool ctr_frame(void* data, const void* frame, #ifdef HAVE_GFX_WIDGETS bool widgets_active = video_info->widgets_active; #endif + bool lcd_bottom = false; if (!width || !height || !settings) { @@ -591,12 +1133,10 @@ static bool ctr_frame(void* data, const void* frame, return true; } - state_tmp = hidKeysDown(); - hidTouchRead(&state_tmp_touch); - if((state_tmp & KEY_TOUCH) && (state_tmp_touch.py < 120)) - { - ctr_set_bottom_screen_enable(ctr, !ctr_bottom_screen_enabled); - } + lcd_bottom = settings->bools.video_3ds_lcd_bottom; + if (lcd_bottom != ctr_bottom_screen_enabled) + ctr_set_bottom_screen_enable(lcd_bottom); + bottom_menu_control(data, lcd_bottom); if (ctr->p3d_event_pending) { @@ -742,11 +1282,23 @@ static bool ctr_frame(void* data, const void* frame, custom_vp_height ); - ctrGuSetMemoryFill(true, (u32*)ctr->drawbuffers.top.left, 0x00000000, - (u32*)ctr->drawbuffers.top.left + 2 * CTR_TOP_FRAMEBUFFER_WIDTH * CTR_TOP_FRAMEBUFFER_HEIGHT, - 0x201, NULL, 0x00000000, - 0, - 0x201); + if (ctr->refresh_bottom_menu) + { + ctrGuSetMemoryFill(true, (u32*)ctr->drawbuffers.top.left, 0x00000000, + (u32*)ctr->drawbuffers.top.left + 2 * CTR_TOP_FRAMEBUFFER_WIDTH * CTR_TOP_FRAMEBUFFER_HEIGHT, + 0x201, + (u32*)ctr->drawbuffers.bottom, 0x00000000, + (u32*)ctr->drawbuffers.bottom + 2 * CTR_BOTTOM_FRAMEBUFFER_WIDTH * CTR_BOTTOM_FRAMEBUFFER_HEIGHT, + 0x201); + } + else + { + ctrGuSetMemoryFill(true, (u32*)ctr->drawbuffers.top.left, 0x00000000, + (u32*)ctr->drawbuffers.top.left + 2 * CTR_TOP_FRAMEBUFFER_WIDTH * CTR_TOP_FRAMEBUFFER_HEIGHT, + 0x201, NULL, 0x00000000, + 0, + 0x201); + } GPUCMD_SetBufferOffset(0); @@ -923,6 +1475,12 @@ static bool ctr_frame(void* data, const void* frame, gfx_widgets_frame(video_info); #endif +#ifndef CONSOLE_LOG + if (ctr_bottom_screen_enabled && + rarch_ctl(RARCH_CTL_CORE_IS_RUNNING, NULL)) + ctr_render_bottom_screen(ctr); +#endif + if (msg) font_driver_render_msg(ctr, msg, NULL, NULL); @@ -943,10 +1501,21 @@ static bool ctr_frame(void* data, const void* frame, CTRGU_RGBA8, gfxTopRightFramebuffers[ctr->current_buffer_top], 240, CTRGU_RGB8, CTRGU_MULTISAMPLE_NONE); +#ifndef CONSOLE_LOG + ctrGuDisplayTransfer(true, + ctr->drawbuffers.bottom, + 240, + 320, + CTRGU_RGBA8, + gfxBottomFramebuffers[ctr->current_buffer_bottom], + 240, + CTRGU_RGB8, + CTRGU_MULTISAMPLE_NONE); +#endif /* Swap buffers : */ #ifdef USE_CTRULIB_2 - u32 *buf0, *buf1; + u32 *buf0, *buf1, *bottom; u32 stride; buf0 = (u32*)gfxTopLeftFramebuffers[ctr->current_buffer_top]; @@ -970,6 +1539,16 @@ static bool ctr_frame(void* data, const void* frame, gspPresentBuffer(GFX_TOP, ctr->current_buffer_top, buf0, buf1, stride, (1<<8)|((1^bit5)<<6)|((bit5)<<5)|GSP_BGR8_OES); + +#ifndef CONSOLE_LOG + if (ctr->refresh_bottom_menu) + { + bottom = (u32*)gfxBottomFramebuffers[ctr->current_buffer_bottom]; + stride = 240 * 3; + gspPresentBuffer(GFX_BOTTOM, ctr->current_buffer_bottom, bottom, bottom, + stride, GSP_BGR8_OES); + } +#endif #else topFramebufferInfo. active_framebuf = ctr->current_buffer_top; @@ -990,7 +1569,7 @@ static bool ctr_frame(void* data, const void* frame, framebuf1_vaddr = (u32*)gfxTopRightFramebuffers[ctr->current_buffer_top]; else topFramebufferInfo. - framebuf1_vaddr = topFramebufferInfo.framebuf0_vaddr; + framebuf1_vaddr = topFramebufferInfo.framebuf0_vaddr; topFramebufferInfo. framebuf_widthbytesize = 240 * 3; @@ -1008,11 +1587,43 @@ static bool ctr_frame(void* data, const void* frame, framebufferInfoHeader[0x0] ^= 1; framebufferInfo[framebufferInfoHeader[0x0]] = topFramebufferInfo; framebufferInfoHeader[0x1] = 1; + +#ifndef CONSOLE_LOG + if (ctr->refresh_bottom_menu) + { + bottomFramebufferInfo. + active_framebuf = ctr->current_buffer_bottom; + bottomFramebufferInfo. + framebuf0_vaddr = (u32*)gfxBottomFramebuffers[ + ctr->current_buffer_bottom]; + + bottomFramebufferInfo. + framebuf1_vaddr = (u32*)(gfxBottomFramebuffers[ + ctr->current_buffer_bottom] + 240 * 3); + bottomFramebufferInfo. + framebuf_widthbytesize = 240 * 3; + + bottomFramebufferInfo.format = GSP_BGR8_OES; + bottomFramebufferInfo. + framebuf_dispselect = ctr->current_buffer_bottom; + bottomFramebufferInfo.unk = 0x00000000; + + u8* framebufferInfoHeader2 = gfxSharedMemory+0x200+gfxThreadID*0x80+0x40; + GSPGPU_FramebufferInfo* + framebufferInfo2 = + (GSPGPU_FramebufferInfo*)&framebufferInfoHeader2[0x4]; + framebufferInfoHeader2[0x0] ^= 1; + framebufferInfo2[framebufferInfoHeader2[0x0]] = bottomFramebufferInfo; + framebufferInfoHeader2[0x1] = 1; + } +#endif #endif ctr->current_buffer_top ^= 1; + ctr->current_buffer_bottom ^= 1; ctr->p3d_event_pending = true; ctr->ppf_event_pending = true; + ctr->refresh_bottom_menu = false; return true; } @@ -1047,16 +1658,33 @@ static bool ctr_suppress_screensaver(void* data, bool enable) static void ctr_free(void* data) { + unsigned i; ctr_video_t* ctr = (ctr_video_t*)data; if (!ctr) return; + if (ctr->state_data_on_ram) + save_state_to_file(ctr); + aptUnhook(&ctr->lcd_aptHook); gspSetEventCallback(GSPGPU_EVENT_VBlank0, NULL, NULL, true); shaderProgramFree(&ctr->shader); DVLB_Free(ctr->dvlb); vramFree(ctr->drawbuffers.top.left); + vramFree(ctr->drawbuffers.bottom); + for (i = 0; i < CTR_TEXTURE_LAST; i++) + { + struct ctr_bottom_texture_data *o = &ctr->bottom_textures[i]; + ctr_texture_t *texture = (ctr_texture_t *) o->texture; + if (texture) + { + linearFree(texture->data); + linearFree(o->frame_coords); + o->texture = 0; + } + } + free(ctr->bottom_textures); linearFree(ctr->display_list); linearFree(ctr->texture_linear); linearFree(ctr->texture_swizzled); @@ -1064,7 +1692,6 @@ static void ctr_free(void* data) linearFree(ctr->menu.texture_linear); linearFree(ctr->menu.texture_swizzled); linearFree(ctr->menu.frame_coords); - linearFree(ctr->empty_framebuffer); linearFree(ctr->vertex_cache.buffer); linearFree(ctr); #if 0 diff --git a/gfx/drivers_font/ctr_font.c b/gfx/drivers_font/ctr_font.c index ccd2de61c3..e58f600516 100644 --- a/gfx/drivers_font/ctr_font.c +++ b/gfx/drivers_font/ctr_font.c @@ -42,7 +42,8 @@ typedef struct { ctr_texture_t texture; - ctr_scale_vector_t scale_vector; + ctr_scale_vector_t scale_vector_top; + ctr_scale_vector_t scale_vector_bottom; const font_renderer_driver_t* font_driver; void* font_data; } ctr_font_t; @@ -95,7 +96,15 @@ static void* ctr_font_init_font(void* data, const char* font_path, linearFree(tmp); #endif - ctr_set_scale_vector(&font->scale_vector, 400, 240, font->texture.width, font->texture.height); + ctr_set_scale_vector(&font->scale_vector_top, + CTR_TOP_FRAMEBUFFER_WIDTH, + CTR_TOP_FRAMEBUFFER_HEIGHT, + font->texture.width, font->texture.height); + + ctr_set_scale_vector(&font->scale_vector_bottom, + CTR_BOTTOM_FRAMEBUFFER_WIDTH, + CTR_BOTTOM_FRAMEBUFFER_HEIGHT, + font->texture.width, font->texture.height); return font; } @@ -174,11 +183,12 @@ static void ctr_font_render_line( switch (text_align) { case TEXT_ALIGN_RIGHT: - x -= ctr_font_get_message_width(font, msg, msg_len, scale); + x += width - ctr_font_get_message_width(font, msg, msg_len, scale); break; case TEXT_ALIGN_CENTER: - x -= ctr_font_get_message_width(font, msg, msg_len, scale) / 2; + x += width / 2 - + ctr_font_get_message_width(font, msg, msg_len, scale) / 2; break; } @@ -231,7 +241,11 @@ static void ctr_font_render_line( return; GPUCMD_AddWrite(GPUREG_GSH_BOOLUNIFORM, 0); - ctrGuSetVertexShaderFloatUniform(0, (float*)&font->scale_vector, 1); + if (!ctr->render_font_bottom) + ctrGuSetVertexShaderFloatUniform(0, (float*)&font->scale_vector_top, 1); + else + ctrGuSetVertexShaderFloatUniform(0, (float*)&font->scale_vector_bottom, 1); + GSPGPU_FlushDataCache(ctr->vertex_cache.current, (v - ctr->vertex_cache.current) * sizeof(ctr_vertex_t)); ctrGuSetAttributeBuffers(2, @@ -261,20 +275,31 @@ static void ctr_font_render_line( GPU_TEXTURE_WRAP_S(GPU_CLAMP_TO_EDGE) | GPU_TEXTURE_WRAP_T(GPU_CLAMP_TO_EDGE), GPU_L8); - GPU_SetViewport(NULL, - VIRT_TO_PHYS(ctr->drawbuffers.top.left), - 0, 0, CTR_TOP_FRAMEBUFFER_HEIGHT, - ctr->video_mode == CTR_VIDEO_MODE_2D_800X240 - ? CTR_TOP_FRAMEBUFFER_WIDTH * 2 : CTR_TOP_FRAMEBUFFER_WIDTH); - - GPU_DrawArray(GPU_GEOMETRY_PRIM, 0, v - ctr->vertex_cache.current); - - if (ctr->video_mode == CTR_VIDEO_MODE_3D) + if (!ctr->render_font_bottom) { GPU_SetViewport(NULL, - VIRT_TO_PHYS(ctr->drawbuffers.top.right), - 0, 0, CTR_TOP_FRAMEBUFFER_HEIGHT, - CTR_TOP_FRAMEBUFFER_WIDTH); + VIRT_TO_PHYS(ctr->drawbuffers.top.left), + 0, 0, CTR_TOP_FRAMEBUFFER_HEIGHT, + ctr->video_mode == CTR_VIDEO_MODE_2D_800X240 + ? CTR_TOP_FRAMEBUFFER_WIDTH * 2 : CTR_TOP_FRAMEBUFFER_WIDTH); + + GPU_DrawArray(GPU_GEOMETRY_PRIM, 0, v - ctr->vertex_cache.current); + + if (ctr->video_mode == CTR_VIDEO_MODE_3D) + { + GPU_SetViewport(NULL, + VIRT_TO_PHYS(ctr->drawbuffers.top.right), + 0, 0, CTR_TOP_FRAMEBUFFER_HEIGHT, + CTR_TOP_FRAMEBUFFER_WIDTH); + GPU_DrawArray(GPU_GEOMETRY_PRIM, 0, v - ctr->vertex_cache.current); + } + } + else + { + GPU_SetViewport(NULL, + VIRT_TO_PHYS(ctr->drawbuffers.bottom), + 0, 0, CTR_BOTTOM_FRAMEBUFFER_HEIGHT, + CTR_BOTTOM_FRAMEBUFFER_WIDTH); GPU_DrawArray(GPU_GEOMETRY_PRIM, 0, v - ctr->vertex_cache.current); } @@ -363,8 +388,10 @@ static void ctr_font_render_msg( alpha, r_dark, g_dark, b_dark, alpha_dark; ctr_font_t * font = (ctr_font_t*)data; ctr_video_t *ctr = (ctr_video_t*)userdata; - unsigned width = ctr->vp.full_width; - unsigned height = ctr->vp.full_height; + unsigned width = ctr->render_font_bottom ? + CTR_BOTTOM_FRAMEBUFFER_WIDTH : CTR_TOP_FRAMEBUFFER_WIDTH; + unsigned height = ctr->render_font_bottom ? + CTR_BOTTOM_FRAMEBUFFER_HEIGHT : CTR_TOP_FRAMEBUFFER_HEIGHT; settings_t *settings = config_get_ptr(); float video_msg_pos_x = settings->floats.video_msg_pos_x; float video_msg_pos_y = settings->floats.video_msg_pos_y; diff --git a/input/drivers_joypad/ctr_joypad.c b/input/drivers_joypad/ctr_joypad.c index 8695bd77af..1eb718fdb9 100644 --- a/input/drivers_joypad/ctr_joypad.c +++ b/input/drivers_joypad/ctr_joypad.c @@ -32,9 +32,6 @@ static uint32_t pad_state; static int16_t analog_state[DEFAULT_MAX_PADS][2][2]; -/* TODO/FIXME - global referenced outside */ -extern uint64_t lifecycle_state; - static const char *ctr_joypad_name(unsigned pad) { return "3DS Controller"; @@ -193,11 +190,6 @@ static void ctr_joypad_poll(void) analog_state[0][RETRO_DEVICE_INDEX_ANALOG_RIGHT] [RETRO_DEVICE_ID_ANALOG_X] = ctr_joypad_fix_range(state_tmp_right_analog.dx); analog_state[0][RETRO_DEVICE_INDEX_ANALOG_RIGHT] [RETRO_DEVICE_ID_ANALOG_Y] = -ctr_joypad_fix_range(state_tmp_right_analog.dy); - BIT64_CLEAR(lifecycle_state, RARCH_MENU_TOGGLE); - - if((state_tmp & KEY_TOUCH) && (state_tmp_touch.py > 120)) - BIT64_SET(lifecycle_state, RARCH_MENU_TOGGLE); - /* panic button */ if((state_tmp & KEY_START) && (state_tmp & KEY_SELECT) && diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index 381aa1867b..d2dac3b39f 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -11255,6 +11255,10 @@ MSG_HASH( MSG_FAILED_TO_SAVE_SRAM, "Failed to save SRAM" ) +MSG_HASH( + MSG_FAILED_TO_LOAD_SRAM, + "Failed to load SRAM" + ) MSG_HASH( MSG_FAILED_TO_SAVE_STATE_TO, "Failed to save state to" @@ -12674,6 +12678,34 @@ MSG_HASH( MENU_ENUM_LABEL_VALUE_CTR_VIDEO_MODE_2D_800X240, "2D (High Resolution)" ) +MSG_HASH( + MSG_3DS_BOTTOM_MENU_DEFAULT, + "Tap the Touch Screen to go\nto the Retroarch menu" + ) +MSG_HASH( + MSG_3DS_BOTTOM_MENU_ASSET_NOT_FOUND, + "bottom_menu.png not found\nin the assets/ctr folder" + ) +MSG_HASH( + MSG_3DS_BOTTOM_MENU_NO_STATE_DATA, + "No\nData" + ) +MSG_HASH( + MSG_3DS_BOTTOM_MENU_NO_STATE_THUMBNAIL, + "No\nScreenshot" + ) +MSG_HASH( + MSG_3DS_BOTTOM_MENU_RESUME, + "Resume Game" + ) +MSG_HASH( + MSG_3DS_BOTTOM_MENU_SAVE_STATE, + "Create\nRestore Point" + ) +MSG_HASH( + MSG_3DS_BOTTOM_MENU_LOAD_STATE, + "Load\nRestore Point" + ) #endif #ifdef HAVE_QT MSG_HASH( diff --git a/menu/menu_setting.c b/menu/menu_setting.c index b14b23a543..893625038e 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -17382,8 +17382,10 @@ static bool setting_append_list( general_write_handler, general_read_handler, SD_FLAG_CMD_APPLY_AUTO); +#ifdef CONSOLE_LOG MENU_SETTINGS_LIST_CURRENT_ADD_CMD(list, list_info, CMD_EVENT_REINIT_FROM_TOGGLE); #endif +#endif #ifdef HAVE_NETWORKING CONFIG_BOOL( diff --git a/msg_hash.h b/msg_hash.h index dc5ac135e0..8e3233c4a4 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -399,6 +399,7 @@ enum msg_hash_enums MSG_LOADING_STATE, MSG_FAILED_TO_SAVE_STATE_TO, MSG_FAILED_TO_SAVE_SRAM, + MSG_FAILED_TO_LOAD_SRAM, MSG_STATE_SIZE, MSG_FAILED_TO_LOAD_CONTENT, MSG_COULD_NOT_READ_CONTENT_FILE, @@ -3222,6 +3223,14 @@ enum msg_hash_enums MSG_MANUAL_CONTENT_SCAN_M3U_CLEANUP, MSG_MANUAL_CONTENT_SCAN_END, + MSG_3DS_BOTTOM_MENU_DEFAULT, + MSG_3DS_BOTTOM_MENU_ASSET_NOT_FOUND, + MSG_3DS_BOTTOM_MENU_NO_STATE_DATA, + MSG_3DS_BOTTOM_MENU_NO_STATE_THUMBNAIL, + MSG_3DS_BOTTOM_MENU_RESUME, + MSG_3DS_BOTTOM_MENU_SAVE_STATE, + MSG_3DS_BOTTOM_MENU_LOAD_STATE, + MSG_LAST, /* Ensure sizeof(enum) == sizeof(int) */ diff --git a/retroarch.c b/retroarch.c index 95012718de..f10bb3e0e4 100644 --- a/retroarch.c +++ b/retroarch.c @@ -7980,6 +7980,31 @@ static void path_clear_all(void) path_clear(RARCH_PATH_BASENAME); } +bool retroarch_get_current_savestate_path(char *path, size_t len) +{ + struct rarch_state *p_rarch = &rarch_st; + const global_t *global = &p_rarch->g_extern; + settings_t *settings = p_rarch->configuration_settings; + int state_slot = settings ? settings->ints.state_slot : 0; + const char *name_savestate = NULL; + + if (!path || !global) + return false; + + name_savestate = global->name.savestate; + if (string_is_empty(name_savestate)) + return false; + + if (state_slot > 0) + snprintf(path, len, "%s%d", name_savestate, state_slot); + else if (state_slot < 0) + fill_pathname_join_delim(path, name_savestate, "auto", '.', len); + else + strlcpy(path, name_savestate, len); + + return true; +} + enum rarch_content_type path_is_media_type(const char *path) { char ext_lower[128]; @@ -11451,20 +11476,7 @@ static bool command_event_main_state( state_path[0] = msg[0] = '\0'; - if (global) - { - int state_slot = settings->ints.state_slot; - const char *name_savestate = global->name.savestate; - - if (state_slot > 0) - snprintf(state_path, sizeof(state_path), "%s%d", - name_savestate, state_slot); - else if (state_slot < 0) - fill_pathname_join_delim(state_path, - name_savestate, "auto", '.', sizeof(state_path)); - else - strlcpy(state_path, name_savestate, sizeof(state_path)); - } + retroarch_get_current_savestate_path(state_path, sizeof(state_path)); core_serialize_size(&info); @@ -11473,6 +11485,7 @@ static bool command_event_main_state( switch (cmd) { case CMD_EVENT_SAVE_STATE: + case CMD_EVENT_SAVE_STATE_TO_RAM: { bool savestate_auto_index = settings->bools.savestate_auto_index; @@ -11481,7 +11494,10 @@ static bool command_event_main_state( bool frame_time_counter_reset_after_save_state = settings->bools.frame_time_counter_reset_after_save_state; - content_save_state(state_path, true, false); + if (cmd == CMD_EVENT_SAVE_STATE) + content_save_state(state_path, true, false); + else + content_save_state_to_ram(); /* Clean up excess savestates if necessary */ if (savestate_auto_index && (savestate_max_keep > 0)) @@ -11499,24 +11515,33 @@ static bool command_event_main_state( } break; case CMD_EVENT_LOAD_STATE: - if (content_load_state(state_path, false, false)) + case CMD_EVENT_LOAD_STATE_FROM_RAM: { + bool res = false; + if (cmd == CMD_EVENT_LOAD_STATE) + res = content_load_state(state_path, false, false); + else + res = content_load_state_from_ram(); + + if (res) + { #ifdef HAVE_CHEEVOS - if (rcheevos_hardcore_active()) - { - rcheevos_pause_hardcore(); - runloop_msg_queue_push(msg_hash_to_str(MSG_CHEEVOS_HARDCORE_MODE_DISABLED), 0, 180, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); - } + if (rcheevos_hardcore_active()) + { + rcheevos_pause_hardcore(); + runloop_msg_queue_push(msg_hash_to_str(MSG_CHEEVOS_HARDCORE_MODE_DISABLED), 0, 180, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); + } #endif - ret = true; + ret = true; #ifdef HAVE_NETWORKING - netplay_driver_ctl(RARCH_NETPLAY_CTL_LOAD_SAVESTATE, NULL); + netplay_driver_ctl(RARCH_NETPLAY_CTL_LOAD_SAVESTATE, NULL); #endif - { - bool frame_time_counter_reset_after_load_state = - settings->bools.frame_time_counter_reset_after_load_state; - if (frame_time_counter_reset_after_load_state) - p_rarch->video_driver_frame_time_count = 0; + { + bool frame_time_counter_reset_after_load_state = + settings->bools.frame_time_counter_reset_after_load_state; + if (frame_time_counter_reset_after_load_state) + p_rarch->video_driver_frame_time_count = 0; + } } } push_msg = false; @@ -12238,11 +12263,13 @@ bool command_event(enum event_command cmd, void *data) return false; break; case CMD_EVENT_UNDO_LOAD_STATE: + case CMD_EVENT_UNDO_SAVE_STATE: + case CMD_EVENT_LOAD_STATE_FROM_RAM: if (!command_event_main_state(p_rarch, cmd)) return false; break; - case CMD_EVENT_UNDO_SAVE_STATE: - if (!command_event_main_state(p_rarch, cmd)) + case CMD_EVENT_RAM_STATE_TO_FILE: + if (!content_ram_state_to_file((char *) data)) return false; break; case CMD_EVENT_RESIZE_WINDOWED_SCALE: @@ -12274,6 +12301,7 @@ bool command_event(enum event_command cmd, void *data) #endif return false; case CMD_EVENT_SAVE_STATE: + case CMD_EVENT_SAVE_STATE_TO_RAM: { bool savestate_auto_index = settings->bools.savestate_auto_index; int state_slot = settings->ints.state_slot; diff --git a/retroarch.h b/retroarch.h index cddcd16317..44988e6c4d 100644 --- a/retroarch.h +++ b/retroarch.h @@ -2114,6 +2114,8 @@ typedef enum apple_view_type APPLE_VIEW_TYPE_METAL } apple_view_type_t; +bool retroarch_get_current_savestate_path(char *path, size_t len); + RETRO_END_DECLS #endif diff --git a/tasks/task_save.c b/tasks/task_save.c index 7195c3870f..d2f85522b9 100644 --- a/tasks/task_save.c +++ b/tasks/task_save.c @@ -151,6 +151,10 @@ static struct save_state_buf undo_save_buf; * Can be restored with undo_load_state(). */ static struct save_state_buf undo_load_buf; +/* Buffer that stores state instead of file. + * This is useful for devices with slow I/O. */ +static struct save_state_buf ram_buf; + #ifdef HAVE_THREADS /* TODO/FIXME - global state - perhaps move outside this file */ static struct autosave_st autosave_state; @@ -1669,6 +1673,15 @@ bool content_reset_savestate_backups(void) undo_load_buf.path[0] = '\0'; undo_load_buf.size = 0; + if (ram_buf.data) + { + free(ram_buf.data); + ram_buf.data = NULL; + } + + ram_buf.path[0] = '\0'; + ram_buf.size = 0; + return true; } @@ -1808,6 +1821,148 @@ static bool dump_to_file_desperate(const void *data, return true; } +/** + * content_load_state_from_ram: + * Load a state from ram. + * + * Returns: true if successful, false otherwise. + **/ +bool content_load_state_from_ram(void) +{ + size_t temp_data_size; + bool ret = false; + void* temp_data = NULL; + + RARCH_LOG("[State]: %s, %u %s.\n", + msg_hash_to_str(MSG_LOADING_STATE), + (unsigned)ram_buf.size, + msg_hash_to_str(MSG_BYTES)); + + /* We need to make a temporary copy of the buffer, to allow the swap below */ + temp_data = malloc(ram_buf.size); + temp_data_size = ram_buf.size; + memcpy(temp_data, ram_buf.data, ram_buf.size); + + /* Swap the current state with the backup state. This way, we can undo + what we're undoing */ + content_save_state("RAM", false, false); + + ret = content_deserialize_state(temp_data, temp_data_size); + + /* Clean up the temporary copy */ + free(temp_data); + temp_data = NULL; + + if (!ret) + { + RARCH_ERR("[State]: %s.\n", + msg_hash_to_str(MSG_FAILED_TO_LOAD_SRAM)); + } + + return ret; +} + +/** + * content_save_state_from_ram: + * Save a state to ram. + * + * Returns: true if successful, false otherwise. + **/ +bool content_save_state_to_ram(void) +{ + retro_ctx_size_info_t info; + void *data = NULL; + size_t serial_size; + + core_serialize_size(&info); + + if (info.size == 0) + return false; + serial_size = info.size; + + if (!save_state_in_background) + { + data = content_get_serialized_data(&serial_size); + + if (!data) + { + RARCH_ERR("[State]: %s.\n", + msg_hash_to_str(MSG_FAILED_TO_SAVE_SRAM)); + return false; + } + + RARCH_LOG("[State]: %s, %u %s.\n", + msg_hash_to_str(MSG_SAVING_STATE), + (unsigned)serial_size, + msg_hash_to_str(MSG_BYTES)); + } + + if (!data) + data = content_get_serialized_data(&serial_size); + + if (!data) + { + RARCH_ERR("[State]: %s.\n", + msg_hash_to_str(MSG_FAILED_TO_SAVE_SRAM)); + return false; + } + + /* If we were holding onto an old state already, clean it up first */ + if (ram_buf.data) + { + free(ram_buf.data); + ram_buf.data = NULL; + } + + ram_buf.data = malloc(serial_size); + if (!ram_buf.data) + { + free(data); + return false; + } + + memcpy(ram_buf.data, data, serial_size); + free(data); + ram_buf.size = serial_size; + + return true; +} + +/** + * content_ram_state_to_file: + * @path : path of ram state that shall be written to. + * Save a ram state from memory to disk. + * + * Returns: true if successful, false otherwise. + **/ +bool content_ram_state_to_file(const char *path) +{ + settings_t *settings = config_get_ptr(); +#if defined(HAVE_ZLIB) + bool compress_files = settings->bools.save_file_compression; +#else + bool compress_files = false; +#endif + bool write_success; + + if (!path) + return false; + + if (!ram_buf.data) + return false; + +#if defined(HAVE_ZLIB) + if (compress_files) + write_success = rzipstream_write_file( + path, ram_buf.data, ram_buf.size); + else +#endif + write_success = filestream_write_file( + path, ram_buf.data, ram_buf.size); + + return write_success; +} + /** * content_save_ram_file: * @path : path of RAM state that shall be written to.