all repos — mgba @ cb8d60e21108402b1e50d57787d2c8fa3ea84fe9

mGBA Game Boy Advance Emulator

GUI: Make savestate screens show the relevant savestate picture
Jeffrey Pfau jeffrey@endrift.com
Thu, 03 Sep 2015 01:58:50 -0700
commit

cb8d60e21108402b1e50d57787d2c8fa3ea84fe9

parent

9b14cc607dd12fec58d893b178d1bd4aaca21610

M src/gba/gui/gui-runner.csrc/gba/gui/gui-runner.c

@@ -10,6 +10,8 @@ #include "gba/serialize.h"

#include "util/gui/file-select.h" #include "util/gui/font.h" #include "util/gui/menu.h" +#include "util/memory.h" +#include "util/png-io.h" #include "util/vfs.h" enum {

@@ -30,13 +32,46 @@ RUNNER_STATE_8 = 0x80000,

RUNNER_STATE_9 = 0x90000, }; -static void _drawBackground(struct GUIBackground* background) { +static void _drawBackground(struct GUIBackground* background, void* context) { + UNUSED(context); struct GBAGUIBackground* gbaBackground = (struct GBAGUIBackground*) background; if (gbaBackground->p->drawFrame) { gbaBackground->p->drawFrame(gbaBackground->p, true); } } +static void _drawState(struct GUIBackground* background, void* id) { + struct GBAGUIBackground* gbaBackground = (struct GBAGUIBackground*) background; + int stateId = ((int) id) >> 16; + if (gbaBackground->p->drawScreenshot) { + struct VFile* vf = GBAGetState(gbaBackground->p->context.gba, 0, stateId, false); + uint32_t* pixels = anonymousMemoryMap(VIDEO_HORIZONTAL_PIXELS * VIDEO_VERTICAL_PIXELS * 4); + bool success = false; + if (vf && isPNG(vf) && pixels) { + png_structp png = PNGReadOpen(vf, PNG_HEADER_BYTES); + png_infop info = png_create_info_struct(png); + png_infop end = png_create_info_struct(png); + if (png && info && end) { + success = PNGReadHeader(png, info); + success = success && PNGReadPixels(png, info, pixels, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, VIDEO_HORIZONTAL_PIXELS); + success = success && PNGReadFooter(png, end); + } + PNGReadClose(png, info, end); + } + if (vf) { + vf->close(vf); + } + if (success) { + gbaBackground->p->drawScreenshot(gbaBackground->p, pixels, true); + } else if (gbaBackground->p->drawFrame) { + gbaBackground->p->drawFrame(gbaBackground->p, true); + } + if (pixels) { + mappedMemoryFree(pixels, VIDEO_HORIZONTAL_PIXELS * VIDEO_VERTICAL_PIXELS * 4); + } + } +} + static void _updateLux(struct GBALuminanceSource* lux) { UNUSED(lux); }

@@ -72,6 +107,12 @@ GBAContextDeinit(&runner->context);

} void GBAGUIRunloop(struct GBAGUIRunner* runner) { + struct GBAGUIBackground drawState = { + .d = { + .draw = _drawState + }, + .p = runner + }; struct GUIMenu pauseMenu = { .title = "Game Paused", .index = 0,

@@ -80,12 +121,12 @@ };

struct GUIMenu stateSaveMenu = { .title = "Save state", .index = 0, - .background = &runner->background.d + .background = &drawState.d }; struct GUIMenu stateLoadMenu = { .title = "Load state", .index = 0, - .background = &runner->background.d + .background = &drawState.d }; GUIMenuItemListInit(&pauseMenu.items, 0); GUIMenuItemListInit(&stateSaveMenu.items, 9);
M src/gba/gui/gui-runner.hsrc/gba/gui/gui-runner.h

@@ -37,6 +37,7 @@ void (*gameLoaded)(struct GBAGUIRunner*);

void (*gameUnloaded)(struct GBAGUIRunner*); void (*prepareForFrame)(struct GBAGUIRunner*); void (*drawFrame)(struct GBAGUIRunner*, bool faded); + void (*drawScreenshot)(struct GBAGUIRunner*, const uint32_t* pixels, bool faded); void (*paused)(struct GBAGUIRunner*); void (*unpaused)(struct GBAGUIRunner*); uint16_t (*pollGameInput)(struct GBAGUIRunner*);
M src/platform/3ds/main.csrc/platform/3ds/main.c

@@ -112,15 +112,39 @@ }

} static void _drawFrame(struct GBAGUIRunner* runner, bool faded) { + UNUSED(runner); + GSPGPU_FlushDataCache(0, renderer.outputBuffer, 256 * VIDEO_VERTICAL_PIXELS * 2); GX_SetDisplayTransfer(0, renderer.outputBuffer, GX_BUFFER_DIM(256, VIDEO_VERTICAL_PIXELS), tex->data, GX_BUFFER_DIM(256, VIDEO_VERTICAL_PIXELS), 0x000002202); + gspWaitForPPF(); GSPGPU_FlushDataCache(0, tex->data, 256 * VIDEO_VERTICAL_PIXELS * 2); + sf2d_draw_texture_scale_blend(tex, 40, 296, 1, -1, 0xFFFFFF3F | (faded ? 0 : 0xC0)); #if RESAMPLE_LIBRARY == RESAMPLE_BLIP_BUF if (!hasSound) { blip_clear(runner->context.gba->audio.left); blip_clear(runner->context.gba->audio.right); } #endif +} + +static void _drawScreenshot(struct GBAGUIRunner* runner, const uint32_t* pixels, bool faded) { + UNUSED(runner); + u16* newPixels = linearMemAlign(256 * VIDEO_VERTICAL_PIXELS * 2, 0x80); + unsigned y, x; + for (y = 0; y < VIDEO_VERTICAL_PIXELS; ++y) { + for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) { + u16 pixel = (*pixels >> 19) & 0x1F; + pixel |= (*pixels >> 5) & 0x7C0; + pixel |= (*pixels << 8) & 0xF800; + newPixels[y * 256 + x] = pixel; + ++pixels; + } + memset(&newPixels[y * 256 + VIDEO_HORIZONTAL_PIXELS], 0, 32); + } + GSPGPU_FlushDataCache(0, (void*) newPixels, VIDEO_HORIZONTAL_PIXELS * VIDEO_VERTICAL_PIXELS * 2); + GX_SetDisplayTransfer(0, (void*) newPixels, GX_BUFFER_DIM(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS), tex->data, GX_BUFFER_DIM(256, VIDEO_VERTICAL_PIXELS), 0x000002202); gspWaitForPPF(); + linearFree(newPixels); + GSPGPU_FlushDataCache(0, (void*) tex->data, 256 * VIDEO_VERTICAL_PIXELS * 2); sf2d_draw_texture_scale_blend(tex, 40, 296, 1, -1, 0xFFFFFF3F | (faded ? 0 : 0xC0)); }

@@ -264,6 +288,7 @@ .gameLoaded = _gameLoaded,

.gameUnloaded = _gameUnloaded, .prepareForFrame = 0, .drawFrame = _drawFrame, + .drawScreenshot = _drawScreenshot, .paused = _gameUnloaded, .unpaused = _gameLoaded, .pollGameInput = _pollGameInput
M src/util/gui.hsrc/util/gui.h

@@ -29,7 +29,7 @@ GUI_INPUT_MAX = 0x20

}; struct GUIBackground { - void (*draw)(struct GUIBackground*); + void (*draw)(struct GUIBackground*, void* context); }; struct GUIParams {
M src/util/gui/menu.csrc/util/gui/menu.c

@@ -71,7 +71,7 @@ }

params->drawStart(); if (menu->background) { - menu->background->draw(menu->background); + menu->background->draw(menu->background, GUIMenuItemListGetPointer(&menu->items, menu->index)->data); } if (params->guiPrepare) { params->guiPrepare();