diff --git a/Makefile b/Makefile index eeda729f1..dc6645c73 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/XdgThumbnailer/emulate.c b/XdgThumbnailer/emulate.c new file mode 100644 index 000000000..0c8c84810 --- /dev/null +++ b/XdgThumbnailer/emulate.c @@ -0,0 +1,96 @@ +#include "emulate.h" + +#include +#include + +#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; +} diff --git a/XdgThumbnailer/emulate.h b/XdgThumbnailer/emulate.h new file mode 100644 index 000000000..480837032 --- /dev/null +++ b/XdgThumbnailer/emulate.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include + +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]); diff --git a/XdgThumbnailer/main.c b/XdgThumbnailer/main.c index 54b4c5848..663b60317 100644 --- a/XdgThumbnailer/main.c +++ b/XdgThumbnailer/main.c @@ -8,6 +8,7 @@ #include #include +#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; } diff --git a/XdgThumbnailer/thumbnail.c b/XdgThumbnailer/thumbnail.c index 8b59753fa..f95a89086 100644 --- a/XdgThumbnailer/thumbnail.c +++ b/XdgThumbnailer/thumbnail.c @@ -2,44 +2,15 @@ #include #include +#include #include -#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); diff --git a/XdgThumbnailer/thumbnail.h b/XdgThumbnailer/thumbnail.h index 35db9ff75..8880adb71 100644 --- a/XdgThumbnailer/thumbnail.h +++ b/XdgThumbnailer/thumbnail.h @@ -3,8 +3,5 @@ #include #include -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);