SameBoy/gtk3/vram_viewer_window.c

636 lines
21 KiB
C

#include "vram_viewer_window.h"
#include <stdbool.h>
#include "util.h"
#define tileset_buffer_length 256 * 192 * 4
#define tilemap_buffer_length 256 * 256 * 4
struct _VramViewerWindow {
GtkWindowClass parent_class;
GtkStack *stack;
GtkComboBoxText *tileset_palette_selector;
GtkToggleButton *tileset_toggle_grid_button;
GtkDrawingArea *tileset_canvas;
GtkComboBoxText *tilemap_palette_selector;
GtkComboBoxText *tilemap_tilemap_selector;
GtkToggleButton *tilemap_toggle_grid_button;
GtkToggleButton *tilemap_toggle_scrolling_button;
GtkComboBoxText *tilemap_tileset_selector;
GtkDrawingArea *tilemap_canvas;
GtkTreeView *sprites;
GtkTreeView *palettes;
GtkCellRendererText *palette_cell_renderer_0;
GtkCellRendererText *palette_cell_renderer_1;
GtkCellRendererText *palette_cell_renderer_2;
GtkCellRendererText *palette_cell_renderer_3;
GtkLabel *status;
bool is_cgb;
uint16_t oam_count;
uint8_t oam_height;
uint8_t palette_data[16][0x40];
GB_oam_info_t oam_info[40];
Rect scroll_rect;
uint8_t gb_lcdc;
uint8_t *gb_vram;
uint32_t *tileset_buffer;
uint32_t *tilemap_buffer;
};
G_DEFINE_TYPE(VramViewerWindow, vram_viewer_window, GTK_TYPE_WINDOW);
static void visible_tab_changed(GObject *stack, GParamSpec *pspec, VramViewerWindow *window) {
if (gtk_widget_in_destruction (GTK_WIDGET (stack))) return;
gtk_label_set_text(window->status, "");
}
static gboolean draw_tileset_canvas(GtkWidget *widget, cairo_t *cr, VramViewerWindow *window) {
GtkStyleContext *context = gtk_widget_get_style_context(widget);
int width = gtk_widget_get_allocated_width(widget);
int height = gtk_widget_get_allocated_height(widget);
gtk_render_background(context, cr, 0, 0, width, height);
gtk_render_frame(context, cr, 0, 0, width, height);
cairo_surface_t *surface = cairo_image_surface_create_for_data(
(unsigned char *) window->tileset_buffer,
CAIRO_FORMAT_RGB24,
256,
192,
cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, 256)
);
cairo_scale(cr, 2.0, 2.0);
cairo_set_source_surface(cr, surface, 0, 0);
cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_NEAREST);
cairo_paint(cr);
if (gtk_toggle_button_get_active(window->tileset_toggle_grid_button)) {
cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.25);
cairo_set_line_width(cr, 1);
const int divisions_x = 256 / 8;
const int divisions_y = 192 / 8;
for (int i = 0; i < divisions_x; i++) {
const int j = 256 * i;
cairo_move_to(cr, j / divisions_x, 0);
cairo_line_to(cr, j / divisions_x, 192);
}
for (int i = 0; i < divisions_y; i++) {
const int j = 192 * i;
cairo_move_to(cr, 0, j / divisions_y);
cairo_line_to(cr, 256, j / divisions_y);
}
cairo_stroke(cr);
}
return false;
}
static gboolean draw_tilemap_canvas(GtkWidget *widget, cairo_t *cr, VramViewerWindow *window) {
GtkStyleContext *context = gtk_widget_get_style_context(widget);
int width = gtk_widget_get_allocated_width(widget);
int height = gtk_widget_get_allocated_height(widget);
gtk_render_background(context, cr, 0, 0, width, height);
gtk_render_frame(context, cr, 0, 0, width, height);
cairo_surface_t *surface = cairo_image_surface_create_for_data(
(unsigned char *) window->tilemap_buffer,
CAIRO_FORMAT_RGB24,
256,
256,
cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, 256)
);
cairo_scale(cr, 2.0, 2.0);
cairo_set_source_surface(cr, surface, 0, 0);
cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_NEAREST);
cairo_paint(cr);
if (gtk_toggle_button_get_active(window->tilemap_toggle_grid_button)) {
cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.25);
cairo_set_line_width(cr, 1);
const int divisions = 256 / 8;
for (int i = 0; i < divisions; i++) {
const int j = 256 * i;
cairo_move_to(cr, j / divisions, 0);
cairo_line_to(cr, j / divisions, 256);
cairo_move_to(cr, 0, j / divisions);
cairo_line_to(cr, 256, j / divisions);
}
cairo_stroke(cr);
}
if (gtk_toggle_button_get_active(window->tilemap_toggle_scrolling_button)) {
cairo_rectangle(cr, -2, -2, width + 2, height + 2);
for (unsigned x = 0; x < 2; x++) {
for (unsigned y = 0; y < 2; y++) {
Rect rect = window->scroll_rect;
rect.x -= 256 * x;
rect.y -= 256 * y;
cairo_rectangle(cr, rect.x, rect.y, rect.width, rect.height);
}
}
cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
cairo_set_line_width(cr, 2);
cairo_set_line_join(cr, CAIRO_LINE_JOIN_ROUND);
cairo_set_source_rgba(cr, 0.2, 0.2, 0.2, 0.5);
cairo_fill_preserve(cr);
cairo_clip_preserve(cr);
cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.6);
cairo_stroke(cr);
}
return false;
}
static gboolean tileset_canvas_motion(GtkWidget *widget, GdkEventMotion *event, VramViewerWindow *window) {
int x, y;
if (event->is_hint) {
gdk_window_get_pointer(event->window, &x, &y, NULL);
}
else {
x = event->x;
y = event->y;
}
// Compensate for our canvas scale
x /= 2;
y /= 2;
uint8_t bank = x >= 128? 1 : 0;
x &= 127;
uint16_t tile = x / 8 + y / 8 * 16;
const char *format =
"Tile number"
" <span font_family=\"monospace\">$%02x</span>"
" at"
" <span font_family=\"monospace\">%d:$%04x</span>";
g_autofree char *markup = g_markup_printf_escaped(
format,
tile & 0xFF,
bank, 0x8000 + tile * 0x10
);
gtk_label_set_markup(window->status, markup);
return true;
}
static gboolean tilemap_canvas_motion(GtkWidget *widget, GdkEventMotion *event, VramViewerWindow *window) {
if (!window->gb_vram) {
gtk_label_set_text(window->status, "");
return false;
}
int x, y;
if (event->is_hint) {
gdk_window_get_pointer (event->window, &x, &y, NULL);
}
else {
x = event->x;
y = event->y;
}
// Compensate for our canvas scale
x /= 2;
y /= 2;
uint16_t map_offset = x / 8 + y / 8 * 32;
uint16_t map_base = 0x1800;
GB_map_type_t map_type = GB_MAP_AUTO;
const gchar *map_type_id = vram_viewer_get_tilemap_type_id(window);
if (g_strcmp0("auto", map_type_id) != 0) {
map_type = (g_strcmp0("9800", map_type_id) == 0)? GB_MAP_9800 : GB_MAP_9C00;
}
GB_tileset_type_t tileset_type = GB_TILESET_AUTO;
const gchar *tileset_type_id = vram_viewer_get_tileset_type_id(window);
if (g_strcmp0("auto", tileset_type_id) != 0) {
tileset_type = (g_strcmp0("8800", tileset_type_id) == 0)? GB_TILESET_8800 : GB_TILESET_8000;
}
if (map_type == GB_MAP_9C00 || (map_type == GB_MAP_AUTO && window->gb_lcdc & 0x08)) {
map_base = 0x1c00;
}
if (tileset_type == GB_TILESET_AUTO) {
tileset_type = (window->gb_lcdc & 0x10)? GB_TILESET_8800 : GB_TILESET_8000;
}
uint8_t tile = window->gb_vram[map_base + map_offset];
uint16_t tile_address = 0;
if (tileset_type == GB_TILESET_8000) {
tile_address = 0x8000 + tile * 0x10;
}
else {
tile_address = 0x9000 + (int8_t)tile * 0x10;
}
if (window->is_cgb) {
uint8_t attributes = window->gb_vram[map_base + map_offset + 0x2000];
const char *format =
"Tile number"
" <span font_family=\"monospace\">$%02x</span>"
" <span font_family=\"monospace\">(%d:$%04x)</span>"
" at map address"
" <span font_family=\"monospace\">$%04x</span>"
" (Attributes: <span font_family=\"monospace\">%c%c%c%d%d</span>)";
g_autofree char *markup = g_markup_printf_escaped(
format,
tile,
attributes & 0x8? 1 : 0,
tile_address,
0x8000 + map_base + map_offset,
(attributes & 0x80) ? 'P' : '-',
(attributes & 0x40) ? 'V' : '-',
(attributes & 0x20) ? 'H' : '-',
attributes & 0x8? 1 : 0,
attributes & 0x7
);
gtk_label_set_markup(window->status, markup);
}
else {
const char *format =
"Tile number"
" <span font_family=\"monospace\">$%02x ($%04x)</span>"
" at map address"
" <span font_family=\"monospace\">$%04x</span>";
g_autofree char *markup = g_markup_printf_escaped(
format,
tile,
tile_address,
0x8000 + map_base + map_offset
);
gtk_label_set_markup(window->status, markup);
}
return true;
}
static void vram_viewer_window_init(VramViewerWindow *window) {
gtk_widget_init_template(GTK_WIDGET(window));
window->tileset_buffer = g_new0(uint32_t, tileset_buffer_length);
window->tilemap_buffer = g_new0(uint32_t, tilemap_buffer_length);
window->is_cgb = false;
window->oam_count = 0;
window->oam_height = 0;
gtk_widget_add_events(GTK_WIDGET(window->tileset_canvas), GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
gtk_widget_add_events(GTK_WIDGET(window->tilemap_canvas), GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
}
static void vram_viewer_finalize(GObject *object) {
VramViewerWindow *window = (VramViewerWindow *) object;
g_free(window->tilemap_buffer);
g_free(window->tileset_buffer);
window->gb_vram = NULL;
G_OBJECT_CLASS(vram_viewer_window_parent_class)->finalize(object);
}
static void vram_viewer_window_class_init(VramViewerWindowClass *class) {
gtk_widget_class_set_template_from_resource(GTK_WIDGET_CLASS(class), RESOURCE_PREFIX "ui/vram_viewer_window.ui");
gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), VramViewerWindow, stack);
gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), VramViewerWindow, tileset_palette_selector);
gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), VramViewerWindow, tileset_toggle_grid_button);
gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), VramViewerWindow, tileset_canvas);
gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), VramViewerWindow, tilemap_palette_selector);
gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), VramViewerWindow, tilemap_tilemap_selector);
gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), VramViewerWindow, tilemap_toggle_grid_button);
gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), VramViewerWindow, tilemap_toggle_scrolling_button);
gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), VramViewerWindow, tilemap_tileset_selector);
gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), VramViewerWindow, tilemap_canvas);
gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), VramViewerWindow, sprites);
gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), VramViewerWindow, palettes);
gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), VramViewerWindow, palette_cell_renderer_0);
gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), VramViewerWindow, palette_cell_renderer_1);
gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), VramViewerWindow, palette_cell_renderer_2);
gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), VramViewerWindow, palette_cell_renderer_3);
gtk_widget_class_bind_template_child(GTK_WIDGET_CLASS(class), VramViewerWindow, status);
gtk_widget_class_bind_template_callback(GTK_WIDGET_CLASS(class), visible_tab_changed);
gtk_widget_class_bind_template_callback(GTK_WIDGET_CLASS(class), draw_tileset_canvas);
gtk_widget_class_bind_template_callback(GTK_WIDGET_CLASS(class), draw_tilemap_canvas);
gtk_widget_class_bind_template_callback(GTK_WIDGET_CLASS(class), tileset_canvas_motion);
gtk_widget_class_bind_template_callback(GTK_WIDGET_CLASS(class), tilemap_canvas_motion);
G_OBJECT_CLASS(class)->finalize = vram_viewer_finalize;
}
VramViewerWindow *vram_viewer_new(void) {
return g_object_new (VRAM_VIEWER_WINDOW_TYPE, NULL);
}
const gchar *vram_viewer_active_tab_name(VramViewerWindow *window) {
return gtk_stack_get_visible_child_name(window->stack);
}
const gchar *vram_viewer_get_tileset_palette_id(VramViewerWindow *window) {
return gtk_combo_box_get_active_id(GTK_COMBO_BOX(window->tileset_palette_selector));
}
const gchar *vram_viewer_get_tilemap_palette_id(VramViewerWindow *window) {
return gtk_combo_box_get_active_id(GTK_COMBO_BOX(window->tilemap_palette_selector));
}
const gchar *vram_viewer_get_tilemap_type_id(VramViewerWindow *window) {
return gtk_combo_box_get_active_id(GTK_COMBO_BOX(window->tilemap_tilemap_selector));
}
const gchar *vram_viewer_get_tileset_type_id(VramViewerWindow *window) {
return gtk_combo_box_get_active_id(GTK_COMBO_BOX(window->tilemap_tileset_selector));
}
static gboolean update_sprite_list(VramViewerWindow *window) {
GtkTreeIter iter;
GtkTreeModel *model = gtk_tree_view_get_model(window->sprites);
GtkListStore *store;
if (!model) {
GtkListStore *new_store = gtk_list_store_new(7,
GDK_TYPE_PIXBUF, // Preview image
G_TYPE_STRING, // X position
G_TYPE_STRING, // Y position
G_TYPE_STRING, // Tile
G_TYPE_STRING, // Tile Address
G_TYPE_STRING, // OAM Address
G_TYPE_STRING // Attributes
);
gtk_tree_view_set_model(window->sprites, GTK_TREE_MODEL(new_store));
store = new_store;
}
else {
store = GTK_LIST_STORE(model);
gtk_list_store_clear(store);
}
gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter);
for (unsigned row = 0; row < window->oam_count; ++row) {
g_autoptr(GBytes) bytes = g_bytes_new(window->oam_info[row].image, 128 * sizeof(uint32_t));
g_autoptr(GdkPixbuf) pixbuf = gdk_pixbuf_new_from_bytes(
bytes,
GDK_COLORSPACE_RGB, true, 8, 8, window->oam_height, 8 * sizeof(uint32_t)
);
g_autoptr(GdkPixbuf) dest = gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, 8 * 2, window->oam_height * 2);
gdk_pixbuf_scale(pixbuf, dest,
0, 0, 8 * 2, window->oam_height * 2,
0, 0, 2.0, 2.0,
GDK_INTERP_NEAREST
);
g_autofree gchar *str_1 = g_strdup_printf("%i", window->oam_info[row].x - 8);
g_autofree gchar *str_2 = g_strdup_printf("%i", window->oam_info[row].y - 16);
g_autofree gchar *str_3 = g_strdup_printf("$%02x", window->oam_info[row].tile);
g_autofree gchar *str_4 = g_strdup_printf("$%04x", 0x8000 + window->oam_info[row].tile * 0x10);
g_autofree gchar *str_5 = g_strdup_printf("$%04x", window->oam_info[row].oam_addr);
g_autofree gchar *str_6 = window->is_cgb
? g_strdup_printf("%c%c%c%d%d",
window->oam_info[row].flags & 0x80? 'P' : '-',
window->oam_info[row].flags & 0x40? 'Y' : '-',
window->oam_info[row].flags & 0x20? 'X' : '-',
window->oam_info[row].flags & 0x08? 1 : 0,
window->oam_info[row].flags & 0x07)
: g_strdup_printf("%c%c%c%d",
window->oam_info[row].flags & 0x80? 'P' : '-',
window->oam_info[row].flags & 0x40? 'Y' : '-',
window->oam_info[row].flags & 0x20? 'X' : '-',
window->oam_info[row].flags & 0x10? 1 : 0);
gtk_list_store_insert_with_values(store, &iter, -1,
0, dest,
1, str_1,
2, str_2,
3, str_3,
4, str_4,
5, str_5,
6, str_6,
-1
);
}
return false;
}
static void palette_color_data_func(GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data_ptr) {
const gchar *title = gtk_tree_view_column_get_title(col);
const uint8_t color_index = g_ascii_strtoll(&title[6], NULL, 10);
const uint8_t column_index = 2 + (2 * color_index);
GValue color_val = G_VALUE_INIT;
gtk_tree_model_get_value(model, iter, column_index, &color_val);
gint color = g_value_get_int(&color_val);
gchar *color_string = g_strdup_printf("#%06x", color);
gint lightness = 0.299 * ((color >> 16) & 0xFF) + 0.587 * ((color >> 8) & 0xFF) + 0.114 * (color & 0xFF);
GValue color_str = G_VALUE_INIT;
g_value_init(&color_str, G_TYPE_STRING);
g_value_set_string(&color_str, color_string);
g_object_set_property(G_OBJECT(renderer), "background", &color_str);
GValue fg_color_str = G_VALUE_INIT;
g_value_init(&fg_color_str, G_TYPE_STRING);
g_value_set_static_string(&fg_color_str, (lightness > 0x7F)? "#000000" : "#FFFFFF");
g_object_set_property(G_OBJECT(renderer), "foreground", &fg_color_str);
g_value_unset(&color_val);
g_value_unset(&color_str);
g_value_unset(&fg_color_str);
g_free(color_string);
}
static gboolean update_palettes(VramViewerWindow *window) {
GtkTreeIter iter;
GtkTreeModel *model = gtk_tree_view_get_model(window->palettes);
GtkListStore *store;
if (!model) {
GtkListStore *new_store = gtk_list_store_new(9,
G_TYPE_STRING, // Name
G_TYPE_STRING, // Color 0 string
G_TYPE_INT, // Color 0 integer
G_TYPE_STRING, // Color 1 string
G_TYPE_INT, // Color 1 integer
G_TYPE_STRING, // Color 2 string
G_TYPE_INT, // Color 2 integer
G_TYPE_STRING, // Color 3 string
G_TYPE_INT // Color 3 integer
);
gtk_tree_view_set_model(window->palettes, GTK_TREE_MODEL(new_store));
store = new_store;
GtkTreeViewColumn *column_0 = gtk_tree_view_get_column(window->palettes, 1);
GtkTreeViewColumn *column_1 = gtk_tree_view_get_column(window->palettes, 2);
GtkTreeViewColumn *column_2 = gtk_tree_view_get_column(window->palettes, 3);
GtkTreeViewColumn *column_3 = gtk_tree_view_get_column(window->palettes, 4);
gtk_tree_view_column_set_cell_data_func(column_0, GTK_CELL_RENDERER(window->palette_cell_renderer_0), palette_color_data_func, NULL, NULL);
gtk_tree_view_column_set_cell_data_func(column_1, GTK_CELL_RENDERER(window->palette_cell_renderer_1), palette_color_data_func, NULL, NULL);
gtk_tree_view_column_set_cell_data_func(column_2, GTK_CELL_RENDERER(window->palette_cell_renderer_2), palette_color_data_func, NULL, NULL);
gtk_tree_view_column_set_cell_data_func(column_3, GTK_CELL_RENDERER(window->palette_cell_renderer_3), palette_color_data_func, NULL, NULL);
}
else {
store = GTK_LIST_STORE(model);
gtk_list_store_clear(store);
}
gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter);
for (unsigned row = 0; row < 16; ++row) {
uint8_t offset = (row & 7) * 4;
uint16_t color_0 = (window->palette_data[row][((0 + offset) << 1) + 1] << 8) | window->palette_data[row][((0 + offset) << 1)];
uint16_t color_1 = (window->palette_data[row][((1 + offset) << 1) + 1] << 8) | window->palette_data[row][((1 + offset) << 1)];
uint16_t color_2 = (window->palette_data[row][((2 + offset) << 1) + 1] << 8) | window->palette_data[row][((2 + offset) << 1)];
uint16_t color_3 = (window->palette_data[row][((3 + offset) << 1) + 1] << 8) | window->palette_data[row][((3 + offset) << 1)];
g_autofree gchar *str_0 = g_strdup_printf("%s %d", row >=8 ? "Object" : "Background", row & 7);
g_autofree gchar *str_1 = g_strdup_printf("$%04x", color_0 & 0x7FFF);
g_autofree gchar *str_3 = g_strdup_printf("$%04x", color_1 & 0x7FFF);
g_autofree gchar *str_5 = g_strdup_printf("$%04x", color_2 & 0x7FFF);
g_autofree gchar *str_7 = g_strdup_printf("$%04x", color_3 & 0x7FFF);
gtk_list_store_insert_with_values(store, &iter, -1,
0, str_0,
1, str_1,
2, convert_color(color_0),
3, str_3,
4, convert_color(color_1),
5, str_5,
6, convert_color(color_2),
7, str_7,
8, convert_color(color_3),
-1
);
}
return false;
}
void vram_viewer_update(VramViewerWindow *window, GB_gameboy_t *gb) {
if (!gtk_widget_is_visible(GTK_WIDGET(window))) return;
window->is_cgb = GB_is_cgb(gb);
window->gb_lcdc = ((uint8_t *)GB_get_direct_access(gb, GB_DIRECT_ACCESS_IO, NULL, NULL))[GB_IO_LCDC];
window->gb_vram = GB_get_direct_access(gb, GB_DIRECT_ACCESS_VRAM, NULL, NULL);
const gchar *active_tab_name = vram_viewer_active_tab_name(window);
if (g_strcmp0(active_tab_name, VRAM_VIEWER_TAB_TILESET) == 0) {
const gchar *palette_id = vram_viewer_get_tileset_palette_id(window);
GB_palette_type_t palette_type = g_str_has_prefix(palette_id, "bg")? GB_PALETTE_BACKGROUND : GB_PALETTE_OAM;
uint8_t palette_index = g_ascii_digit_value(palette_id[palette_type == GB_PALETTE_OAM ? 3 : 2]);
GB_draw_tileset(gb, window->tileset_buffer,
palette_type,
palette_index
);
}
else if (g_strcmp0(active_tab_name, VRAM_VIEWER_TAB_TILEMAP) == 0) {
const gchar *palette_id = vram_viewer_get_tilemap_palette_id(window);
uint8_t palette_index = 0;
GB_palette_type_t palette_type = GB_PALETTE_AUTO;
if (g_strcmp0("auto", palette_id) != 0) {
palette_type = g_str_has_prefix(palette_id, "bg")? GB_PALETTE_BACKGROUND : GB_PALETTE_OAM;
palette_index = g_ascii_digit_value(palette_id[palette_type == GB_PALETTE_OAM ? 3 : 2]);
}
GB_map_type_t map_type = GB_MAP_AUTO;
const gchar *map_type_id = vram_viewer_get_tilemap_type_id(window);
if (g_strcmp0("auto", map_type_id) != 0) {
map_type = (g_strcmp0("9800", map_type_id) == 0)? GB_MAP_9800 : GB_MAP_9C00;
}
GB_tileset_type_t tileset_type = GB_TILESET_AUTO;
const gchar *tileset_type_id = vram_viewer_get_tileset_type_id(window);
if (g_strcmp0("auto", tileset_type_id) != 0) {
tileset_type = (g_strcmp0("8800", tileset_type_id) == 0)? GB_TILESET_8800 : GB_TILESET_8000;
}
GB_draw_tilemap(gb, window->tilemap_buffer,
palette_type,
palette_index,
map_type,
tileset_type
);
window->scroll_rect = (Rect){
GB_read_memory(gb, 0xFF00 | GB_IO_SCX),
GB_read_memory(gb, 0xFF00 | GB_IO_SCY),
160, 144
};
}
else if (g_strcmp0(active_tab_name, VRAM_VIEWER_TAB_SPRITES) == 0) {
window->oam_count = GB_get_oam_info(gb, (GB_oam_info_t *)window->oam_info, &window->oam_height);
g_idle_add((GSourceFunc) update_sprite_list, window);
}
else if (g_strcmp0(active_tab_name, VRAM_VIEWER_TAB_PALETTES) == 0) {
size_t size;
for (unsigned row = 0; row < 16; ++row) {
uint8_t *palette_data = GB_get_direct_access(gb, row >= 8? GB_DIRECT_ACCESS_OBP : GB_DIRECT_ACCESS_BGP, &size, NULL);
memcpy(window->palette_data[row], palette_data, size);
}
g_idle_add((GSourceFunc) update_palettes, window);
}
}
void vram_viewer_clear(VramViewerWindow *window) {
g_idle_remove_by_data(window);
memset(window->tilemap_buffer, 0, tilemap_buffer_length * sizeof(uint32_t));
memset(window->tileset_buffer, 0, tileset_buffer_length * sizeof(uint32_t));
g_autoptr(GtkTreeModel) sprites_model = gtk_tree_view_get_model(window->sprites);
if (sprites_model) gtk_list_store_clear(GTK_LIST_STORE(sprites_model));
g_autoptr(GtkTreeModel) palettes_model = gtk_tree_view_get_model(window->palettes);
if (palettes_model) gtk_list_store_clear(GTK_LIST_STORE(palettes_model));
}