Emulate the ROM to be thumbnailed

Now all that's left is actually rendering that!
This commit is contained in:
ISSOtm
2024-06-27 00:00:32 +02:00
parent b3cecf2413
commit 446fc15521
6 changed files with 150 additions and 67 deletions

View File

@@ -338,7 +338,7 @@ endif
cocoa: $(BIN)/SameBoy.app
quicklook: $(BIN)/SameBoy.qlgenerator
xdg-thumbnailer: $(BIN)/XdgThumbnailer/sameboy-thumbnailer $(BIN)/SDL/cgb_boot_fast.bin
xdg-thumbnailer: $(BIN)/XdgThumbnailer/sameboy-thumbnailer $(BIN)/XdgThumbnailer/cgb_boot_fast.bin $(patsubst QuickLook/%.png,$(BIN)/XdgThumbnailer/%.png,$(wildcard QuickLook/*.png))
sdl: $(SDL_TARGET) $(BIN)/SDL/dmg_boot.bin $(BIN)/SDL/mgb_boot.bin $(BIN)/SDL/cgb0_boot.bin $(BIN)/SDL/cgb_boot.bin $(BIN)/SDL/agb_boot.bin $(BIN)/SDL/sgb_boot.bin $(BIN)/SDL/sgb2_boot.bin $(BIN)/SDL/LICENSE $(BIN)/SDL/registers.sym $(BIN)/SDL/background.bmp $(BIN)/SDL/Shaders $(BIN)/SDL/Palettes
bootroms: $(BIN)/BootROMs/agb_boot.bin $(BIN)/BootROMs/cgb_boot.bin $(BIN)/BootROMs/cgb0_boot.bin $(BIN)/BootROMs/dmg_boot.bin $(BIN)/BootROMs/mgb_boot.bin $(BIN)/BootROMs/sgb_boot.bin $(BIN)/BootROMs/sgb2_boot.bin
tester: $(TESTER_TARGET) $(BIN)/tester/dmg_boot.bin $(BIN)/tester/cgb_boot.bin $(BIN)/tester/agb_boot.bin $(BIN)/tester/sgb_boot.bin $(BIN)/tester/sgb2_boot.bin
@@ -615,41 +615,51 @@ $(BIN)/tester/sameboy_tester.exe: $(CORE_OBJECTS) $(SDL_OBJECTS)
-@$(MKDIR) -p $(dir $@)
$(CC) $^ -o $@ $(LDFLAGS) -Wl,/subsystem:console
$(BIN)/SDL/%.bin: $(BOOTROMS_DIR)/%.bin
-@$(MKDIR) -p $(dir $@)
cp -f $^ $@
$(BIN)/tester/%.bin: $(BOOTROMS_DIR)/%.bin
-@$(MKDIR) -p $(dir $@)
cp -f $^ $@
cp -f $< $@
$(BIN)/SameBoy.app/Contents/Resources/%.bin: $(BOOTROMS_DIR)/%.bin
-@$(MKDIR) -p $(dir $@)
cp -f $^ $@
cp -f $< $@
$(BIN)/SameBoy-iOS.app/%.bin: $(BOOTROMS_DIR)/%.bin
-@$(MKDIR) -p $(dir $@)
cp -f $^ $@
cp -f $< $@
$(BIN)/XdgThumbnailer/%.png: QuickLook/%.png
-@$(MKDIR) -p $(dir $@)
cp -f $< $@
$(BIN)/XdgThumbnailer/%.bin: $(BOOTROMS_DIR)/%.bin
-@$(MKDIR) -p $(dir $@)
cp -f $< $@
$(BIN)/SDL/%.bin: $(BOOTROMS_DIR)/%.bin
-@$(MKDIR) -p $(dir $@)
cp -f $< $@
$(BIN)/SDL/LICENSE: LICENSE
-@$(MKDIR) -p $(dir $@)
grep -v "^ " $^ > $@
grep -v "^ " $< > $@
$(BIN)/SDL/registers.sym: Misc/registers.sym
-@$(MKDIR) -p $(dir $@)
cp -f $^ $@
cp -f $< $@
$(BIN)/SDL/background.bmp: SDL/background.bmp
-@$(MKDIR) -p $(dir $@)
cp -f $^ $@
cp -f $< $@
$(BIN)/SDL/Shaders: Shaders
$(BIN)/SDL/Shaders: $(wildcard Shaders/*.fsh)
-@$(MKDIR) -p $@
cp -rf Shaders/*.fsh $@
cp -rf $^ $@
touch $@
$(BIN)/SDL/Palettes: Misc/Palettes
$(BIN)/SDL/Palettes: $(wildcard Misc/Palettes/*.sbp)
-@$(MKDIR) -p $@
cp -rf Misc/Palettes/*.sbp $@
cp -rf $^ $@
touch $@
# Boot ROMs
@@ -693,6 +703,7 @@ install: sdl xdg-thumbnailer $(DESTDIR)$(PREFIX)/share/mime/packages/sameboy.xml
-@$(MKDIR) -p $(dir $(DESTDIR)$(PREFIX))
mkdir -p $(DESTDIR)$(DATA_DIR)/ $(DESTDIR)$(PREFIX)/bin/
cp -rf $(BIN)/SDL/* $(DESTDIR)$(DATA_DIR)/
cp -rf $(BIN)/XdgThumbnailer/* $(DESTDIR)$(DATA_DIR)/
mv $(DESTDIR)$(DATA_DIR)/sameboy $(DESTDIR)$(PREFIX)/bin/sameboy
ifeq ($(DESTDIR),)
-update-mime-database -n $(PREFIX)/share/mime

96
XdgThumbnailer/emulate.c Normal file
View File

@@ -0,0 +1,96 @@
#include "emulate.h"
#include <glib.h>
#include <stdint.h>
#include "Core/gb.h"
#include "Core/memory.h"
#include "glibconfig.h"
#define NB_FRAMES_TO_EMULATE (60 * 10)
#define BOOT_ROM_SIZE (0x100 + 0x800) // The two "parts" of it, which are stored contiguously.
static char *boot_rom;
void load_boot_rom(void)
{
static char const *boot_rom_path = DATA_DIR "/cgb_boot_fast.bin";
size_t length;
GError *error = NULL;
g_file_get_contents(boot_rom_path, &boot_rom, &length, &error);
if (error) {
g_error("Error loading boot ROM from \"%s\": %s", boot_rom_path, error->message);
// NOTREACHED
}
else if (length != BOOT_ROM_SIZE) {
g_error("Error loading boot ROM from \"%s\": expected to read %d bytes, got %zu",
boot_rom_path, BOOT_ROM_SIZE, length);
// NOTREACHED
}
}
void unload_boot_rom(void) { g_free(boot_rom); }
/* --- */
static char *async_input_callback(GB_gameboy_t *gb)
{
return NULL;
}
static void log_callback(GB_gameboy_t *gb, const char *string, GB_log_attributes attributes)
{
// Swallow any logs.
}
static void vblank_callback(GB_gameboy_t *gb, GB_vblank_type_t type)
{
unsigned nb_frames_left = GPOINTER_TO_UINT(GB_get_user_data(gb));
nb_frames_left--;
GB_set_user_data(gb, GUINT_TO_POINTER(nb_frames_left));
// *Do* render the very last frame.
if (nb_frames_left == 0) {
GB_set_rendering_disabled(gb, false);
}
}
static uint32_t rgb_encode(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b)
{
return r | g << 8 | b << 16 | 0xFF << 24;
}
unsigned emulate(enum FileKind kind, unsigned char const *rom, size_t rom_size, uint32_t screen[static 160 * 144])
{
GB_gameboy_t gb;
GB_init(&gb, GB_MODEL_CGB_E);
GB_load_boot_rom_from_buffer(&gb, (unsigned char const *)boot_rom, sizeof(boot_rom));
if (kind == KIND_ISX) {
g_assert_not_reached(); // TODO: implement GB_load_isx_from_buffer
}
else {
GB_load_rom_from_buffer(&gb, rom, rom_size);
}
GB_set_user_data(&gb, GUINT_TO_POINTER(NB_FRAMES_TO_EMULATE));
GB_set_vblank_callback(&gb, vblank_callback);
GB_set_pixels_output(&gb, screen);
GB_set_rgb_encode_callback(&gb, rgb_encode);
GB_set_async_input_callback(&gb, async_input_callback);
GB_set_log_callback(&gb, log_callback);
GB_set_color_correction_mode(&gb, GB_COLOR_CORRECTION_MODERN_BALANCED);
GB_set_rendering_disabled(&gb, true);
GB_set_turbo_mode(&gb, true, true);
while (GPOINTER_TO_UINT(GB_get_user_data(&gb))) {
GB_run(&gb);
}
unsigned cgb_flag = GB_read_memory(&gb, 0x143) & 0xC0;
GB_free(&gb);
return cgb_flag;
}

15
XdgThumbnailer/emulate.h Normal file
View File

@@ -0,0 +1,15 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
enum FileKind {
KIND_GB,
KIND_GBC,
KIND_ISX,
};
void load_boot_rom(void);
void unload_boot_rom(void);
unsigned emulate(enum FileKind kind, unsigned char const *rom, size_t rom_size, uint32_t screen[static 160 * 144]);

View File

@@ -8,6 +8,7 @@
#include <stdlib.h>
#include <string.h>
#include "emulate.h"
#include "tasks.h"
#include "thumbnail.h"
@@ -110,7 +111,7 @@ int main(int argc, char const *argv[])
// unsigned active_worker_threads = 0;
// Create the task queue *before* starting to accept tasks from D-Bus.
init_tasks();
load_boot_roms();
load_boot_rom();
// Likewise, create the main loop before then, so it can be aborted even before entering it.
main_loop = g_main_loop_new(NULL, FALSE);
@@ -129,12 +130,12 @@ int main(int argc, char const *argv[])
g_info("Waiting for outstanding tasks...");
cleanup_tasks(); // Also waits for any remaining tasks.
// "Pedantic" cleanup for Valgrind et al.
unload_boot_roms();
unload_boot_rom();
g_main_loop_unref(main_loop);
g_bus_unown_name(owner_id);
if (thumbnailer_interface) {
g_dbus_interface_skeleton_unexport(G_DBUS_INTERFACE_SKELETON(thumbnailer_interface));
g_object_unref(thumbnailer_interface);
}
g_object_unref(thumbnailer_interface);
return 0;
}

View File

@@ -2,44 +2,15 @@
#include <gio/gio.h>
#include <glib.h>
#include <stdint.h>
#include <stdlib.h>
#include "Core/gb.h"
#include "XdgThumbnailer/tasks.h"
#include "emulate.h"
#include "main.h"
#include "tasks.h"
#define THUMBNAILING_ERROR_DOMAIN (g_quark_from_static_string("thumbnailing"))
enum FileKind {
KIND_GB,
KIND_GBC,
KIND_ISX,
};
#define BOOT_ROM_SIZE (0x100 + 0x800) // The two "parts" of it, which are stored contiguously.
static char *boot_rom;
void load_boot_roms(void)
{
static char const *boot_rom_path = DATA_DIR "/cgb_boot_fast.bin";
size_t length;
GError *error = NULL;
g_file_get_contents(boot_rom_path, &boot_rom, &length, &error);
if (error) {
g_error("Error loading boot ROM from \"%s\": %s", boot_rom_path, error->message);
// NOTREACHED
}
else if (length != BOOT_ROM_SIZE) {
g_error("Error loading boot ROM from \"%s\": expected to read %d bytes, got %zu",
boot_rom_path, BOOT_ROM_SIZE, length);
// NOTREACHED
}
}
void unload_boot_roms(void) { g_free(boot_rom); }
struct TaskData {
char *contents;
size_t length;
@@ -59,19 +30,11 @@ static void generate_thumbnail(GTask *task, void *source_object, void *data,
{
struct TaskData *task_data = data;
GB_gameboy_t gb;
GB_init(&gb, GB_MODEL_CGB_E);
GB_load_boot_rom_from_buffer(&gb, (unsigned char const *)boot_rom, sizeof(boot_rom));
if (task_data->kind == KIND_ISX) {
g_assert_not_reached(); // TODO: implement GB_load_isx_from_buffer
}
else {
GB_load_rom_from_buffer(&gb, (unsigned char const *)task_data->contents, task_data->length);
}
// TODO
GB_free(&gb);
uint32_t screen[160 * 144];
unsigned cgb_flag = emulate(task_data->kind, (unsigned char const *)task_data->contents,
task_data->length, screen);
// TODO: generate the thumbnail from `screen` and `cgb_flag`.
(void)cgb_flag;
g_task_return_boolean(task, TRUE);
g_object_unref(task);
@@ -112,7 +75,7 @@ static void on_thumbnailing_end(GObject *source_object, GAsyncResult *res, void
g_assert_null(source_object); // The object that was passed to `g_task_new`.
GTask *task = G_TASK(res);
g_debug("Ending thumbnailing for \"%s\"", g_task_get_name(task));
g_info("Ending thumbnailing for \"%s\"", g_task_get_name(task));
unsigned handle = GPOINTER_TO_UINT(user_data);
char const *uri = g_task_get_name(task);

View File

@@ -3,8 +3,5 @@
#include <glib.h>
#include <gio/gio.h>
void load_boot_roms(void);
void unload_boot_roms(void);
void start_thumbnailing(unsigned handle, GCancellable *cancellable, gboolean is_urgent,
char const *uri, char const *mime_type);