mirror of
https://github.com/LIJI32/SameBoy.git
synced 2026-10-12 02:06:38 +00:00
102 lines
3.3 KiB
C
102 lines
3.3 KiB
C
#include "emulate.h"
|
|
|
|
#include <glib.h>
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "Core/gb.h"
|
|
|
|
// Auto-generated via `glib-compile-resources` from `resources.gresource.xml`.
|
|
#include "build/obj/XdgThumbnailer/resources.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 *async_input_callback(GB_gameboy_t *gb)
|
|
{
|
|
(void)gb;
|
|
return NULL;
|
|
}
|
|
|
|
static void log_callback(GB_gameboy_t *gb, const char *string, GB_log_attributes attributes)
|
|
{
|
|
(void)gb, (void)string, (void)attributes; // Swallow any logs.
|
|
}
|
|
|
|
static void vblank_callback(GB_gameboy_t *gb, GB_vblank_type_t type)
|
|
{
|
|
(void)type; // Ignore the type, we use VBlank counting as a kind of pacing (and to avoid tearing).
|
|
|
|
unsigned *nb_frames_left = GB_get_user_data(gb);
|
|
(*nb_frames_left)--;
|
|
|
|
// *Do* render the very last frame.
|
|
if (*nb_frames_left == 1) {
|
|
GB_set_rendering_disabled(gb, false);
|
|
}
|
|
}
|
|
|
|
static uint32_t rgb_encode(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b)
|
|
{
|
|
uint32_t rgba;
|
|
// The GdkPixbuf that will be created from the screen buffer later, expects components in the
|
|
// order [red, green, blue, alpha], from a uint8_t[] buffer.
|
|
// But SameBoy requires a uint32_t[] buffer, and don't know the endianness of `uint32_t`.
|
|
// So we treat each uint32_t as a 4-byte buffer, and write the bytes accordingly.
|
|
// This is guaranteed to not be UB, because casting a `T*` to any flavour of `char*` accesses
|
|
// and modifies the `T`'s "object representation".
|
|
uint8_t *bytes = (uint8_t *)&rgba;
|
|
bytes[0] = r;
|
|
bytes[1] = g;
|
|
bytes[2] = b;
|
|
bytes[3] = 0xFF;
|
|
return rgba;
|
|
}
|
|
|
|
uint8_t emulate(const char *path, uint32_t screen[static GB_SCREEN_WIDTH * GB_SCREEN_HEIGHT])
|
|
{
|
|
GB_gameboy_t gb;
|
|
GB_init(&gb, GB_MODEL_CGB_E);
|
|
|
|
const char *last_dot = strrchr(path, '.');
|
|
bool is_isx = last_dot && strcmp(last_dot + 1, "isx") == 0;
|
|
if (is_isx ? GB_load_isx(&gb, path) : GB_load_rom(&gb, path)) {
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
GError *error = NULL;
|
|
GBytes *boot_rom = g_resource_lookup_data(resources_get_resource(), "/thumbnailer/cgb_boot_fast.bin",
|
|
G_RESOURCE_LOOKUP_FLAGS_NONE, &error);
|
|
g_assert_no_error(error); // This shouldn't be able to fail.
|
|
size_t boot_rom_size;
|
|
const uint8_t *boot_rom_data = g_bytes_get_data(boot_rom, &boot_rom_size);
|
|
g_assert_cmpuint(boot_rom_size, ==, BOOT_ROM_SIZE);
|
|
GB_load_boot_rom_from_buffer(&gb, boot_rom_data, boot_rom_size);
|
|
g_bytes_unref(boot_rom);
|
|
|
|
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); // Anything bizarre the ROM does during emulation, we don't care about.
|
|
GB_set_color_correction_mode(&gb, GB_COLOR_CORRECTION_MODERN_BALANCED);
|
|
|
|
unsigned nb_frames_left = NB_FRAMES_TO_EMULATE;
|
|
GB_set_user_data(&gb, &nb_frames_left);
|
|
|
|
GB_set_rendering_disabled(&gb, true);
|
|
GB_set_turbo_mode(&gb, true, true);
|
|
while (nb_frames_left) {
|
|
GB_run(&gb);
|
|
}
|
|
|
|
int cgb_flag = GB_read_memory(&gb, 0x143) & 0xC0;
|
|
GB_free(&gb);
|
|
return cgb_flag;
|
|
}
|