[GTK3] Add model and boot ROM CLI overrides
This commit is contained in:
parent
557bfd117e
commit
4caa1d0266
185
gtk3/main.c
185
gtk3/main.c
@ -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);
|
if (user_data->bootrom_path) {
|
||||||
boot_rom_data = g_bytes_get_data(boot_rom_f, &boot_rom_size);
|
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;
|
||||||
|
|
||||||
GB_load_boot_rom_from_buffer(&gb, boot_rom_data, boot_rom_size);
|
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);
|
||||||
|
@ -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>
|
||||||
|
@ -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>
|
||||||
|
@ -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,};
|
||||||
|
Loading…
Reference in New Issue
Block a user