[GTK3] Add model and boot ROM CLI overrides
This commit is contained in:
parent
b976938be8
commit
e00eecaaea
183
gtk3/main.c
183
gtk3/main.c
@ -10,9 +10,13 @@
|
||||
#define str(x) #x
|
||||
#define xstr(x) str(x)
|
||||
|
||||
#define SETTINGS_FILE "sameboy-gtk3-settings.ini"
|
||||
|
||||
typedef struct UserData {
|
||||
bool fullscreen;
|
||||
GFile *file;
|
||||
const gchar* bootrom_path;
|
||||
GB_model_t model;
|
||||
} UserData;
|
||||
|
||||
typedef struct{
|
||||
@ -22,13 +26,15 @@ typedef struct{
|
||||
|
||||
static void run(UserData *user_data);
|
||||
|
||||
GtkApplication *main_application;
|
||||
GtkBuilder *builder;
|
||||
GtkApplicationWindow *main_window;
|
||||
GtkGLArea *gl_area;
|
||||
shader_t shader;
|
||||
static GKeyFile *key_file;
|
||||
static GtkApplication *main_application;
|
||||
static GtkBuilder *builder;
|
||||
static GtkApplicationWindow *main_window;
|
||||
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 unsigned char current_buffer;
|
||||
|
||||
@ -40,29 +46,40 @@ static Rect rect;
|
||||
|
||||
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;
|
||||
|
||||
return should_blend? 3 : 2;
|
||||
}
|
||||
|
||||
void flip(void) {
|
||||
static void flip(void) {
|
||||
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()];
|
||||
}
|
||||
|
||||
uint32_t *get_current_buffer(void) {
|
||||
static uint32_t *get_current_buffer(void) {
|
||||
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()];
|
||||
}
|
||||
|
||||
void render_texture(void *pixels, void *previous) {
|
||||
static void render_texture(void *pixels, void *previous) {
|
||||
static void *_pixels = NULL;
|
||||
if (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_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));
|
||||
}
|
||||
|
||||
// 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
|
||||
static GActionEntry app_entries[] = {
|
||||
{ "quit", activate_quit, 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) {
|
||||
@ -264,6 +295,20 @@ static void startup(GApplication *app, gpointer user_data_gptr) {
|
||||
g_signal_connect(gl_area, "unrealize", G_CALLBACK(gl_finish), NULL);
|
||||
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 …
|
||||
if (show_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) {
|
||||
g_printerr("More than one file specified\n");
|
||||
g_application_quit(app);
|
||||
return;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
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) {
|
||||
g_print("SHUTDOWN\n");
|
||||
|
||||
save_settings();
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
@ -417,7 +514,7 @@ static void update_window_geometry() {
|
||||
|
||||
static void run(UserData *user_data) {
|
||||
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)) {
|
||||
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);
|
||||
}
|
||||
|
||||
GError *gerror;
|
||||
GError *error;
|
||||
char *boot_rom_path;
|
||||
GBytes *boot_rom_f;
|
||||
const guchar *boot_rom_data;
|
||||
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);
|
||||
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) {
|
||||
GB_load_rom(&gb, g_file_get_path(user_data->file));
|
||||
@ -493,6 +632,8 @@ int main(int argc, char *argv[]) {
|
||||
GOptionEntry entries[] = {
|
||||
{ "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 },
|
||||
{ "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 }
|
||||
};
|
||||
// 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, "shutdown", G_CALLBACK(shutdown), &user_data);
|
||||
|
||||
#ifndef NDEBUG
|
||||
//gtk_window_set_interactive_debugging(true);
|
||||
#endif
|
||||
|
||||
// Start our GApplication main loop
|
||||
int status = g_application_run(G_APPLICATION(main_application), argc, argv);
|
||||
g_object_unref(main_application);
|
||||
|
@ -382,6 +382,14 @@ Author: Maximilian Mader
|
||||
<attribute name="action">win.open_vram_viewer</attribute>
|
||||
</item>
|
||||
</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>
|
||||
|
@ -500,7 +500,7 @@ Maximilian Mader https://github.com/max-m</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</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>
|
||||
</object>
|
||||
<packing>
|
||||
@ -645,7 +645,7 @@ Maximilian Mader https://github.com/max-m</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</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>
|
||||
</object>
|
||||
<packing>
|
||||
@ -728,7 +728,7 @@ Maximilian Mader https://github.com/max-m</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</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>
|
||||
</object>
|
||||
<packing>
|
||||
@ -935,7 +935,7 @@ Maximilian Mader https://github.com/max-m</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</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>
|
||||
</object>
|
||||
<packing>
|
||||
|
@ -71,8 +71,8 @@ bool init_shader_with_name(shader_t *shader, const char *name)
|
||||
return false;
|
||||
}
|
||||
|
||||
GError *error;
|
||||
GBytes *master_shader_f;
|
||||
GError *error = NULL;
|
||||
GBytes *master_shader_f = NULL;
|
||||
static const gchar *master_shader_code;
|
||||
static gsize master_shader_code_size;
|
||||
static char final_shader_code[0x10801] = {0,};
|
||||
|
Loading…
Reference in New Issue
Block a user