From f62bfc95e359b243cca002fdefb63114f9c2efaa Mon Sep 17 00:00:00 2001 From: Matt Borgerson Date: Sat, 3 Jun 2023 22:22:24 -0700 Subject: [PATCH] ui: Improve extra snapshot data storage - Store snapshot extra data in BE, per convention - Add a version field for back compat - Some minor refactoring and renaming for clarity --- ui/xemu-snapshots.c | 207 ++++++++++++++++++------------------------- ui/xemu-snapshots.h | 33 ++----- ui/xemu-thumbnail.cc | 23 +++-- ui/xui/main-menu.cc | 16 ++-- 4 files changed, 114 insertions(+), 165 deletions(-) diff --git a/ui/xemu-snapshots.c b/ui/xemu-snapshots.c index becb0dd859..ec719f99e9 100644 --- a/ui/xemu-snapshots.c +++ b/ui/xemu-snapshots.c @@ -49,88 +49,65 @@ const char **g_snapshot_shortcut_index_key_map[] = { &g_config.general.snapshots.shortcuts.f8, }; -static bool xemu_snapshots_load_thumbnail(BlockDriverState *bs_ro, - XemuSnapshotData *data, - int64_t *offset) -{ - int res = bdrv_load_vmstate(bs_ro, (uint8_t *)&data->thumbnail, *offset, - sizeof(TextureBuffer) - - sizeof(data->thumbnail.buffer)); - if (res != sizeof(TextureBuffer) - sizeof(data->thumbnail.buffer)) - return false; - *offset += res; - - data->thumbnail.buffer = g_malloc(data->thumbnail.size); - - res = bdrv_load_vmstate(bs_ro, (uint8_t *)data->thumbnail.buffer, *offset, - data->thumbnail.size); - if (res != data->thumbnail.size) { - return false; - } - *offset += res; - - return true; -} - static void xemu_snapshots_load_data(BlockDriverState *bs_ro, QEMUSnapshotInfo *info, XemuSnapshotData *data, Error **err) { - int res; - XemuSnapshotHeader header; + data->xbe_title_name = NULL; + data->gl_thumbnail = 0; + + int res = bdrv_snapshot_load_tmp(bs_ro, info->id_str, info->name, err); + if (res < 0) { + return; + } + + uint32_t header[3]; int64_t offset = 0; - - data->xbe_title_present = false; - data->thumbnail_present = false; - res = bdrv_snapshot_load_tmp(bs_ro, info->id_str, info->name, err); - if (res < 0) - return; - res = bdrv_load_vmstate(bs_ro, (uint8_t *)&header, offset, sizeof(header)); - if (res != sizeof(header)) - goto error; - offset += res; - - if (header.magic != XEMU_SNAPSHOT_DATA_MAGIC) - goto error; - - res = bdrv_load_vmstate(bs_ro, (uint8_t *)&data->xbe_title_len, offset, - sizeof(data->xbe_title_len)); - if (res != sizeof(data->xbe_title_len)) - goto error; - offset += res; - - data->xbe_title = (char *)g_malloc(data->xbe_title_len); - - res = bdrv_load_vmstate(bs_ro, (uint8_t *)data->xbe_title, offset, - data->xbe_title_len); - if (res != data->xbe_title_len) - goto error; - offset += res; - - data->xbe_title_present = (offset <= sizeof(header) + header.size); - - if (offset == sizeof(header) + header.size) + if (res != sizeof(header)) { return; + } + offset += res; - if (!xemu_snapshots_load_thumbnail(bs_ro, data, &offset)) { - goto error; + if (be32_to_cpu(header[0]) != XEMU_SNAPSHOT_DATA_MAGIC || + be32_to_cpu(header[1]) != XEMU_SNAPSHOT_DATA_VERSION) { + return; } - data->thumbnail_present = (offset <= sizeof(header) + header.size); - - if (data->thumbnail_present) { - glGenTextures(1, &data->gl_thumbnail); - xemu_snapshots_render_thumbnail(data->gl_thumbnail, &data->thumbnail); + size_t size = be32_to_cpu(header[2]); + uint8_t *buf = g_malloc(size); + res = bdrv_load_vmstate(bs_ro, buf, offset, size); + if (res != size) { + g_free(buf); + return; } - return; -error: - g_free(data->xbe_title); - data->xbe_title_present = false; + const size_t xbe_title_name_size = buf[0]; + offset = 1; - g_free(data->thumbnail.buffer); - data->thumbnail_present = false; + if (xbe_title_name_size) { + data->xbe_title_name = (char *)g_malloc(xbe_title_name_size + 1); + memcpy(data->xbe_title_name, &buf[offset], xbe_title_name_size); + data->xbe_title_name[xbe_title_name_size] = 0; + offset += xbe_title_name_size; + } + + const size_t thumbnail_size = be32_to_cpu(*(uint32_t *)&buf[offset]); + offset += 4; + + if (thumbnail_size) { + GLuint thumbnail; + glGenTextures(1, &thumbnail); + if (xemu_snapshots_load_png_to_texture(thumbnail, &buf[offset], + thumbnail_size)) { + data->gl_thumbnail = thumbnail; + } else { + glDeleteTextures(1, &thumbnail); + } + offset += thumbnail_size; + } + + g_free(buf); } static void xemu_snapshots_all_load_data(QEMUSnapshotInfo **info, @@ -144,12 +121,8 @@ static void xemu_snapshots_all_load_data(QEMUSnapshotInfo **info, if (*data) { for (int i = 0; i < xemu_snapshots_len; ++i) { - if ((*data)[i].xbe_title_present) { - g_free((*data)[i].xbe_title); - } - - if ((*data)[i].thumbnail_present) { - g_free((*data)[i].thumbnail.buffer); + g_free((*data)[i].xbe_title_name); + if ((*data)[i].gl_thumbnail) { glDeleteTextures(1, &((*data)[i].gl_thumbnail)); } } @@ -250,65 +223,61 @@ void xemu_snapshots_delete(const char *vm_name, Error **err) void xemu_snapshots_save_extra_data(QEMUFile *f) { + size_t xbe_title_name_size = 0; + char *xbe_title_name = NULL; struct xbe *xbe_data = xemu_get_xbe_info(); - - int64_t xbe_title_len = 0; - char *xbe_title = g_utf16_to_utf8(xbe_data->cert->m_title_name, 40, NULL, - &xbe_title_len, NULL); - xbe_title_len++; - - XemuSnapshotHeader header = { XEMU_SNAPSHOT_DATA_MAGIC, 0 }; - - header.size += sizeof(xbe_title_len); - header.size += xbe_title_len; - - TextureBuffer *thumbnail = xemu_snapshots_extract_thumbnail(); - if (thumbnail && thumbnail->buffer) { - header.size += sizeof(TextureBuffer) - sizeof(thumbnail->buffer); - header.size += thumbnail->size; + if (xbe_data && xbe_data->cert) { + glong items_written = 0; + xbe_title_name = g_utf16_to_utf8(xbe_data->cert->m_title_name, 40, NULL, &items_written, NULL); + if (xbe_title_name) { + xbe_title_name_size = items_written; + } } - qemu_put_buffer(f, (const uint8_t *)&header, sizeof(header)); - qemu_put_buffer(f, (const uint8_t *)&xbe_title_len, sizeof(xbe_title_len)); - qemu_put_buffer(f, (const uint8_t *)xbe_title, xbe_title_len); + size_t thumbnail_size = 0; + void *thumbnail_buf = xemu_snapshots_create_framebuffer_thumbnail_png(&thumbnail_size); - if (thumbnail && thumbnail->buffer) { - qemu_put_buffer(f, (const uint8_t *)thumbnail, - sizeof(TextureBuffer) - sizeof(thumbnail->buffer)); - qemu_put_buffer(f, (const uint8_t *)thumbnail->buffer, thumbnail->size); + qemu_put_be32(f, XEMU_SNAPSHOT_DATA_MAGIC); + qemu_put_be32(f, XEMU_SNAPSHOT_DATA_VERSION); + qemu_put_be32(f, 1 + xbe_title_name_size + 4 + thumbnail_size); + + qemu_put_byte(f, xbe_title_name_size); + if (xbe_title_name_size) { + qemu_put_buffer(f, (const uint8_t *)xbe_title_name, xbe_title_name_size); + g_free(xbe_title_name); } - g_free(xbe_title); - - if (thumbnail && thumbnail->buffer) { - g_free(thumbnail->buffer); + qemu_put_be32(f, thumbnail_size); + if (thumbnail_size) { + qemu_put_buffer(f, (const uint8_t *)thumbnail_buf, thumbnail_size); + g_free(thumbnail_buf); } - g_free(thumbnail); - xemu_snapshots_dirty = true; } bool xemu_snapshots_offset_extra_data(QEMUFile *f) { - size_t ret; - XemuSnapshotHeader header; - ret = qemu_get_buffer(f, (uint8_t *)&header, sizeof(header)); - if (ret != sizeof(header)) { - return false; + unsigned int v; + uint32_t version; + uint32_t size; + + v = qemu_get_be32(f); + if (v != XEMU_SNAPSHOT_DATA_MAGIC) { + qemu_file_skip(f, -4); + return true; } - if (header.magic == XEMU_SNAPSHOT_DATA_MAGIC) { - /* - * qemu_file_skip only works if you aren't skipping past its buffer. - * Unfortunately, it's not usable here. - */ - void *buf = g_malloc(header.size); - qemu_get_buffer(f, buf, header.size); - g_free(buf); - } else { - qemu_file_skip(f, -((int)sizeof(header))); - } + version = qemu_get_be32(f); + (void)version; + + /* qemu_file_skip only works if you aren't skipping past internal buffer limit. + * Unfortunately, it's not usable here. + */ + size = qemu_get_be32(f); + void *buf = g_malloc(size); + qemu_get_buffer(f, buf, size); + g_free(buf); return true; } diff --git a/ui/xemu-snapshots.h b/ui/xemu-snapshots.h index 792b9d3c4f..9a33e687b4 100644 --- a/ui/xemu-snapshots.h +++ b/ui/xemu-snapshots.h @@ -26,33 +26,19 @@ extern "C" { #include "qemu/osdep.h" #include "block/snapshot.h" +#include + +#define XEMU_SNAPSHOT_DATA_MAGIC 0x78656d75 // 'xemu' +#define XEMU_SNAPSHOT_DATA_VERSION 1 -#define XEMU_SNAPSHOT_DATA_MAGIC 0x78656d75 #define XEMU_SNAPSHOT_THUMBNAIL_WIDTH 160 #define XEMU_SNAPSHOT_THUMBNAIL_HEIGHT 120 extern const char **g_snapshot_shortcut_index_key_map[]; -#pragma pack(1) -typedef struct TextureBuffer { - int channels; - unsigned long size; - void *buffer; -} TextureBuffer; -#pragma pack() - -typedef struct XemuSnapshotHeader { - uint32_t magic; - uint32_t size; -} XemuSnapshotHeader; - typedef struct XemuSnapshotData { - int64_t xbe_title_len; - char *xbe_title; - bool xbe_title_present; - TextureBuffer thumbnail; - bool thumbnail_present; - unsigned int gl_thumbnail; + char *xbe_title_name; + GLuint gl_thumbnail; } XemuSnapshotData; // Implemented in xemu-snapshots.c @@ -67,10 +53,9 @@ bool xemu_snapshots_offset_extra_data(QEMUFile *f); void xemu_snapshots_mark_dirty(void); // Implemented in xemu-thumbnail.cc -void xemu_snapshots_set_framebuffer_texture(unsigned int tex, bool flip); -void xemu_snapshots_render_thumbnail(unsigned int tex, - TextureBuffer *thumbnail); -TextureBuffer *xemu_snapshots_extract_thumbnail(void); +void xemu_snapshots_set_framebuffer_texture(GLuint tex, bool flip); +bool xemu_snapshots_load_png_to_texture(GLuint tex, void *buf, size_t size); +void *xemu_snapshots_create_framebuffer_thumbnail_png(size_t *size); #ifdef __cplusplus } diff --git a/ui/xemu-thumbnail.cc b/ui/xemu-thumbnail.cc index 86df3ef2b4..592408393c 100644 --- a/ui/xemu-thumbnail.cc +++ b/ui/xemu-thumbnail.cc @@ -33,14 +33,13 @@ void xemu_snapshots_set_framebuffer_texture(GLuint tex, bool flip) display_flip = flip; } -void xemu_snapshots_render_thumbnail(GLuint tex, TextureBuffer *thumbnail) +bool xemu_snapshots_load_png_to_texture(GLuint tex, void *buf, size_t size) { std::vector pixels; unsigned int width, height, channels; - if (fpng::fpng_decode_memory( - thumbnail->buffer, thumbnail->size, pixels, width, height, channels, - thumbnail->channels) != fpng::FPNG_DECODE_SUCCESS) { - return; + if (fpng::fpng_decode_memory(buf, size, pixels, width, height, channels, + 3) != fpng::FPNG_DECODE_SUCCESS) { + return false; } glActiveTexture(GL_TEXTURE0); @@ -52,9 +51,11 @@ void xemu_snapshots_render_thumbnail(GLuint tex, TextureBuffer *thumbnail) glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels.data()); + + return true; } -TextureBuffer *xemu_snapshots_extract_thumbnail() +void *xemu_snapshots_create_framebuffer_thumbnail_png(size_t *size) { /* * Avoids crashing if a snapshot is made on a thread with no GL context @@ -74,10 +75,8 @@ TextureBuffer *xemu_snapshots_extract_thumbnail() return NULL; } - TextureBuffer *thumbnail = (TextureBuffer *)g_malloc(sizeof(TextureBuffer)); - thumbnail->buffer = g_malloc(png.size() * sizeof(uint8_t)); - thumbnail->channels = 3; - thumbnail->size = png.size() * sizeof(uint8_t); - memcpy(thumbnail->buffer, png.data(), thumbnail->size); - return thumbnail; + void *buf = g_malloc(png.size()); + memcpy(buf, png.data(), png.size()); + *size = png.size(); + return buf; } diff --git a/ui/xui/main-menu.cc b/ui/xui/main-menu.cc index bdef623c2f..4d174102c7 100644 --- a/ui/xui/main-menu.cc +++ b/ui/xui/main-menu.cc @@ -955,8 +955,8 @@ void MainMenuSnapshotsView::Draw() bool search_buf_equal = false; for (int i = m_snapshots_len - 1; i >= 0; i--) { - if (g_config.general.snapshots.filter_current_game && m_extra_data[i].xbe_title_present && - (strcmp(m_current_title_name, m_extra_data[i].xbe_title) != 0)) { + if (g_config.general.snapshots.filter_current_game && m_extra_data[i].xbe_title_name && + (strcmp(m_current_title_name, m_extra_data[i].xbe_title_name) != 0)) { continue; } @@ -968,8 +968,8 @@ void MainMenuSnapshotsView::Draw() keep_entry |= g_match_info_matches(match); g_match_info_free(match); - if (m_extra_data[i].xbe_title_present) { - g_regex_match(m_search_regex, m_extra_data[i].xbe_title, (GRegexMatchFlags)0, &match); + if (m_extra_data[i].xbe_title_name) { + g_regex_match(m_search_regex, m_extra_data[i].xbe_title_name, (GRegexMatchFlags)0, &match); keep_entry |= g_match_info_matches(match); g_free(match); } @@ -982,14 +982,10 @@ void MainMenuSnapshotsView::Draw() search_buf_equal |= g_strcmp0(m_search_buf.c_str(), m_snapshots[i].name) == 0; ImGui::PushID(i); - GLuint thumbnail = 0; - if (m_extra_data[i].thumbnail_present) { - thumbnail = m_extra_data[i].gl_thumbnail; - } SnapshotBigButton( m_snapshots + i, - m_extra_data[i].xbe_title_present ? m_extra_data[i].xbe_title : "Unknown", - thumbnail + m_extra_data[i].xbe_title_name ? m_extra_data[i].xbe_title_name : "Unknown", + m_extra_data[i].gl_thumbnail ); ImGui::PopID(); }