[GTK3] Add model and boot ROM CLI overrides

This commit is contained in:
Maximilian Mader 2019-09-24 00:34:21 +02:00
parent b976938be8
commit e00eecaaea
Signed by: Max
GPG Key ID: F71D56A3151C4FB3
4 changed files with 176 additions and 31 deletions

View File

@ -10,9 +10,13 @@
#define str(x) #x #define str(x) #x
#define xstr(x) str(x) #define xstr(x) str(x)
#define SETTINGS_FILE "sameboy-gtk3-settings.ini"
typedef struct UserData { typedef struct UserData {
bool fullscreen; bool fullscreen;
GFile *file; GFile *file;
const gchar* bootrom_path;
GB_model_t model;
} UserData; } UserData;
typedef struct{ typedef struct{
@ -22,13 +26,15 @@ typedef struct{
static void run(UserData *user_data); static void run(UserData *user_data);
GtkApplication *main_application; static GKeyFile *key_file;
GtkBuilder *builder; static GtkApplication *main_application;
GtkApplicationWindow *main_window; static GtkBuilder *builder;
GtkGLArea *gl_area; static GtkApplicationWindow *main_window;
shader_t shader; static GtkGLArea *gl_area;
static shader_t shader;
static gchar* settings_file_path;
GB_gameboy_t gb; static GB_gameboy_t gb;
static uint32_t *image_buffers[3]; static uint32_t *image_buffers[3];
static unsigned char current_buffer; static unsigned char current_buffer;
@ -40,29 +46,40 @@ static Rect rect;
static bool running = true; static bool running = true;
unsigned char number_of_buffers(void) { static void save_settings(void) {
GError *error = NULL;
// Save as a file.
if (!g_key_file_save_to_file(key_file, settings_file_path, &error)) {
g_warning ("Error saving %s: %s", settings_file_path, error->message);
g_error_free(error);
return;
}
}
static unsigned char number_of_buffers(void) {
bool should_blend = true; bool should_blend = true;
return should_blend? 3 : 2; return should_blend? 3 : 2;
} }
void flip(void) { static void flip(void) {
current_buffer = (current_buffer + 1) % number_of_buffers(); current_buffer = (current_buffer + 1) % number_of_buffers();
} }
uint32_t *get_pixels(void) { static uint32_t *get_pixels(void) {
return image_buffers[(current_buffer + 1) % number_of_buffers()]; return image_buffers[(current_buffer + 1) % number_of_buffers()];
} }
uint32_t *get_current_buffer(void) { static uint32_t *get_current_buffer(void) {
return image_buffers[current_buffer]; return image_buffers[current_buffer];
} }
uint32_t *get_previous_buffer(void) { static uint32_t *get_previous_buffer(void) {
return image_buffers[(current_buffer + 2) % number_of_buffers()]; return image_buffers[(current_buffer + 2) % number_of_buffers()];
} }
void render_texture(void *pixels, void *previous) { static void render_texture(void *pixels, void *previous) {
static void *_pixels = NULL; static void *_pixels = NULL;
if (pixels) { if (pixels) {
_pixels = pixels; _pixels = pixels;
@ -86,7 +103,7 @@ static void vblank(GB_gameboy_t *gb) {
} }
} }
void update_viewport(void) { static void update_viewport(void) {
int win_width = gtk_widget_get_allocated_width(GTK_WIDGET(gl_area)); int win_width = gtk_widget_get_allocated_width(GTK_WIDGET(gl_area));
int win_height = gtk_widget_get_allocated_height(GTK_WIDGET(gl_area)); int win_height = gtk_widget_get_allocated_height(GTK_WIDGET(gl_area));
@ -190,10 +207,24 @@ static void activate_about(GSimpleAction *action, GVariant *parameter, gpointer
gtk_widget_hide(GTK_WIDGET(dialog)); gtk_widget_hide(GTK_WIDGET(dialog));
} }
// app.open_gtk_debugger GAction
// Opens the GTK debugger
static void activate_open_gtk_debugger(GSimpleAction *action, GVariant *parameter, gpointer user_data) {
gtk_window_set_interactive_debugging(true);
}
// app.preferences GAction
// Opens the preferences window
static void activate_preferences(GSimpleAction *action, GVariant *parameter, gpointer user_data) {
gtk_widget_show_all(GTK_WIDGET(get_object("preferences")));
}
// List of GActions for the `app` prefix // List of GActions for the `app` prefix
static GActionEntry app_entries[] = { static GActionEntry app_entries[] = {
{ "quit", activate_quit, NULL, NULL, NULL }, { "quit", activate_quit, NULL, NULL, NULL },
{ "about", activate_about, NULL, NULL, NULL }, { "about", activate_about, NULL, NULL, NULL },
{ "open_gtk_debugger", activate_open_gtk_debugger, NULL, NULL, NULL },
{ "preferences", activate_preferences, NULL, NULL, NULL },
}; };
G_MODULE_EXPORT void on_quit(GtkWidget *w, gpointer app) { G_MODULE_EXPORT void on_quit(GtkWidget *w, gpointer app) {
@ -264,6 +295,20 @@ static void startup(GApplication *app, gpointer user_data_gptr) {
g_signal_connect(gl_area, "unrealize", G_CALLBACK(gl_finish), NULL); g_signal_connect(gl_area, "unrealize", G_CALLBACK(gl_finish), NULL);
gtk_container_add(GTK_CONTAINER(main_window), GTK_WIDGET(gl_area)); gtk_container_add(GTK_CONTAINER(main_window), GTK_WIDGET(gl_area));
GError *error = NULL;
settings_file_path = g_build_filename(g_get_user_config_dir(), SETTINGS_FILE, NULL);
key_file = g_key_file_new();
g_print("Trying to load settings from %s\n", settings_file_path);
if (!g_key_file_load_from_file(key_file, settings_file_path, G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS, &error)) {
if (!g_error_matches(error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) {
g_warning("Error loading %s: %s", settings_file_path, error->message);
}
g_error_free(error);
}
// Handle the whole menubar situation … // Handle the whole menubar situation …
if (show_menubar()) { if (show_menubar()) {
// Show a classic menubar // Show a classic menubar
@ -340,8 +385,7 @@ static void open(GApplication *app, GFile **files, gint n_files, const gchar *hi
if (n_files > 1) { if (n_files > 1) {
g_printerr("More than one file specified\n"); g_printerr("More than one file specified\n");
g_application_quit(app); exit(EXIT_FAILURE);
return;
} }
user_data->file = files[0]; user_data->file = files[0];
@ -352,6 +396,8 @@ static void open(GApplication *app, GFile **files, gint n_files, const gchar *hi
static void shutdown(GApplication *app, GFile **files, gint n_files, const gchar *hint, gpointer user_data_gptr) { static void shutdown(GApplication *app, GFile **files, gint n_files, const gchar *hint, gpointer user_data_gptr) {
g_print("SHUTDOWN\n"); g_print("SHUTDOWN\n");
save_settings();
} }
// This function gets called after the parsing of the commandline options has occurred. // This function gets called after the parsing of the commandline options has occurred.
@ -368,6 +414,57 @@ static gint handle_local_options(GApplication *app, GVariantDict *options, gpoin
user_data->fullscreen = true; user_data->fullscreen = true;
} }
// Handle model override
GVariant *model_name_var = g_variant_dict_lookup_value(options, "model", G_VARIANT_TYPE_STRING);
if (model_name_var != NULL) {
const gchar *model_name = g_variant_get_string(model_name_var, NULL);
// TODO: Synchronize with GB_model_t (Core/gb.h)
if (g_str_has_prefix(model_name, "DMG")) {
if (g_str_has_suffix(model_name, "-B") || g_strcmp0(model_name, "DMG") == 0) {
user_data->model = GB_MODEL_DMG_B;
}
else {
user_data->model = GB_MODEL_DMG_B;
g_printerr("Unsupported revision: %s\nFalling back to DMG-B", model_name);
}
}
else if (g_str_has_prefix(model_name, "SGB")) {
if (g_str_has_suffix(model_name, "-NTSC") || g_strcmp0(model_name, "SGB") == 0) {
user_data->model = GB_MODEL_SGB;
}
else if (g_str_has_suffix(model_name, "-PAL")) {
user_data->model = GB_MODEL_SGB | GB_MODEL_PAL_BIT;
}
else if (g_str_has_suffix(model_name, "2")) {
user_data->model = GB_MODEL_SGB2;
}
else {
user_data->model = GB_MODEL_SGB2;
g_printerr("Unsupported revision: %s\nFalling back to SGB2", model_name);
}
}
else if (g_str_has_prefix(model_name, "CGB")) {
if (g_str_has_suffix(model_name, "-C")) {
user_data->model = GB_MODEL_CGB_C;
}
else if (g_str_has_suffix(model_name, "-E") || g_strcmp0(model_name, "CGB") == 0) {
user_data->model = GB_MODEL_CGB_E;
}
else {
user_data->model = GB_MODEL_CGB_E;
g_printerr("Unsupported revision: %s\nFalling back to CGB-E", model_name);
}
}
else if (g_str_has_prefix(model_name, "AGB")) {
user_data->model = GB_MODEL_AGB;
}
else {
g_printerr("Unknown model: %s\n", model_name);
exit(EXIT_FAILURE);
}
}
return -1; return -1;
} }
@ -417,7 +514,7 @@ static void update_window_geometry() {
static void run(UserData *user_data) { static void run(UserData *user_data) {
GB_model_t prev_model = GB_get_model(&gb); GB_model_t prev_model = GB_get_model(&gb);
GB_model_t model = GB_MODEL_CGB_E; GB_model_t model = user_data->model? user_data->model : GB_MODEL_CGB_E; // TODO: Model from config
if (GB_is_inited(&gb)) { if (GB_is_inited(&gb)) {
GB_switch_model_and_reset(&gb, model); GB_switch_model_and_reset(&gb, model);
@ -446,15 +543,57 @@ static void run(UserData *user_data) {
// GB_apu_set_sample_callback(&gb, gb_audio_callback); // GB_apu_set_sample_callback(&gb, gb_audio_callback);
} }
GError *gerror; GError *error;
char *boot_rom_path;
GBytes *boot_rom_f; GBytes *boot_rom_f;
const guchar *boot_rom_data; const guchar *boot_rom_data;
gsize boot_rom_size; gsize boot_rom_size;
boot_rom_f = g_resources_lookup_data(RESOURCE_PREFIX "bootroms/cgb_boot.bin", G_RESOURCE_LOOKUP_FLAGS_NONE, &gerror);
boot_rom_data = g_bytes_get_data(boot_rom_f, &boot_rom_size);
GB_load_boot_rom_from_buffer(&gb, boot_rom_data, boot_rom_size); if (user_data->bootrom_path) {
g_print("Trying to load boot ROM from %s\n", user_data->bootrom_path);
if (GB_load_boot_rom(&gb, user_data->bootrom_path)) {
g_printerr("Falling back to internal boot ROM\n");
goto internal_bootrom;
}
}
else { internal_bootrom:
switch (model) {
case GB_MODEL_DMG_B:
boot_rom_path = RESOURCE_PREFIX "bootroms/dmg_boot.bin";
break;
case GB_MODEL_SGB:
case GB_MODEL_SGB_PAL:
case GB_MODEL_SGB_NO_SFC:
boot_rom_path = RESOURCE_PREFIX "bootroms/sgb_boot.bin";
break;
case GB_MODEL_SGB2:
case GB_MODEL_SGB2_NO_SFC:
boot_rom_path = RESOURCE_PREFIX "bootroms/sgb2_boot.bin";
break;
case GB_MODEL_CGB_C:
case GB_MODEL_CGB_E:
boot_rom_path = RESOURCE_PREFIX "bootroms/cgb_boot.bin";
break;
case GB_MODEL_AGB:
boot_rom_path = RESOURCE_PREFIX "bootroms/agb_boot.bin";
break;
}
boot_rom_f = g_resources_lookup_data(boot_rom_path, G_RESOURCE_LOOKUP_FLAGS_NONE, &error);
if (boot_rom_f == NULL) {
g_printerr("Failed to load internal boot ROM: %s\n", boot_rom_path);
g_error_free(error);
exit(EXIT_FAILURE);
}
boot_rom_data = g_bytes_get_data(boot_rom_f, &boot_rom_size);
GB_load_boot_rom_from_buffer(&gb, boot_rom_data, boot_rom_size);
}
if (user_data->file != NULL) { if (user_data->file != NULL) {
GB_load_rom(&gb, g_file_get_path(user_data->file)); GB_load_rom(&gb, g_file_get_path(user_data->file));
@ -493,6 +632,8 @@ int main(int argc, char *argv[]) {
GOptionEntry entries[] = { GOptionEntry entries[] = {
{ "version", 'v', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, NULL, "Show the application version", NULL }, { "version", 'v', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, NULL, "Show the application version", NULL },
{ "fullscreen", 'f', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, NULL, "Start in fullscreen mode", NULL }, { "fullscreen", 'f', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, NULL, "Start in fullscreen mode", NULL },
{ "bootrom", 'b', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &user_data.bootrom_path, "Path to the boot ROM to use", "<file path>" },
{ "model", 'm', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, NULL, "Override the model type to emulate", "<model type>" },
{ NULL } { NULL }
}; };
// Setup our command line information // Setup our command line information
@ -507,10 +648,6 @@ int main(int argc, char *argv[]) {
g_signal_connect(main_application, "open", G_CALLBACK(open), &user_data); g_signal_connect(main_application, "open", G_CALLBACK(open), &user_data);
g_signal_connect(main_application, "shutdown", G_CALLBACK(shutdown), &user_data); g_signal_connect(main_application, "shutdown", G_CALLBACK(shutdown), &user_data);
#ifndef NDEBUG
//gtk_window_set_interactive_debugging(true);
#endif
// Start our GApplication main loop // Start our GApplication main loop
int status = g_application_run(G_APPLICATION(main_application), argc, argv); int status = g_application_run(G_APPLICATION(main_application), argc, argv);
g_object_unref(main_application); g_object_unref(main_application);

View File

@ -382,6 +382,14 @@ Author: Maximilian Mader
<attribute name="action">win.open_vram_viewer</attribute> <attribute name="action">win.open_vram_viewer</attribute>
</item> </item>
</section> </section>
<section>
<attribute name="id">developer-section-4</attribute>
<item>
<attribute name="label" translatable="yes">Show GTK Debugger</attribute>
<attribute name="action">app.open_gtk_debugger</attribute>
</item>
</section>
</submenu> </submenu>
<submenu> <submenu>

View File

@ -500,7 +500,7 @@ Maximilian Mader https://github.com/max-m</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="margin_bottom">2</property> <property name="margin_bottom">2</property>
<property name="resource">/io/github/sameboy/CPU.png</property> <property name="resource">/io/github/sameboy/pixmaps/CPU.png</property>
<property name="icon_size">3</property> <property name="icon_size">3</property>
</object> </object>
<packing> <packing>
@ -645,7 +645,7 @@ Maximilian Mader https://github.com/max-m</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="margin_bottom">2</property> <property name="margin_bottom">2</property>
<property name="resource">/io/github/sameboy/Display.png</property> <property name="resource">/io/github/sameboy/pixmaps/Display.png</property>
<property name="icon_size">3</property> <property name="icon_size">3</property>
</object> </object>
<packing> <packing>
@ -728,7 +728,7 @@ Maximilian Mader https://github.com/max-m</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="margin_bottom">2</property> <property name="margin_bottom">2</property>
<property name="resource">/io/github/sameboy/Speaker.png</property> <property name="resource">/io/github/sameboy/pixmaps/Speaker.png</property>
<property name="icon_size">3</property> <property name="icon_size">3</property>
</object> </object>
<packing> <packing>
@ -935,7 +935,7 @@ Maximilian Mader https://github.com/max-m</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="margin_bottom">2</property> <property name="margin_bottom">2</property>
<property name="resource">/io/github/sameboy/Joypad.png</property> <property name="resource">/io/github/sameboy/pixmaps/Joypad.png</property>
<property name="icon_size">3</property> <property name="icon_size">3</property>
</object> </object>
<packing> <packing>

View File

@ -71,8 +71,8 @@ bool init_shader_with_name(shader_t *shader, const char *name)
return false; return false;
} }
GError *error; GError *error = NULL;
GBytes *master_shader_f; GBytes *master_shader_f = NULL;
static const gchar *master_shader_code; static const gchar *master_shader_code;
static gsize master_shader_code_size; static gsize master_shader_code_size;
static char final_shader_code[0x10801] = {0,}; static char final_shader_code[0x10801] = {0,};