diff --git a/cheats.h b/cheats.h index a651a63b5..d0688fce3 100644 --- a/cheats.h +++ b/cheats.h @@ -269,7 +269,7 @@ void S9xDeleteCheatGroup (uint32 index); bool8 S9xLoadCheatFile (const char *filename); bool8 S9xSaveCheatFile (const char *filename); void S9xUpdateCheatsInMemory (void); -bool8 S9xImportCheatsFromDatabase (const char *filename); +int S9xImportCheatsFromDatabase(const char *filename); void S9xCheatsDisable (void); void S9xCheatsEnable (void); char *S9xCheatValidate (char *cheat); diff --git a/cheats2.cpp b/cheats2.cpp index b232604a7..4809ef32d 100644 --- a/cheats2.cpp +++ b/cheats2.cpp @@ -701,6 +701,32 @@ void S9xUpdateCheatsInMemory (void) } } +static int S9xCheatIsDuplicate (char *name, char *code) +{ + unsigned int i; + + for (i = 0; i < Cheat.g.size(); i++) + { + if (!strcmp (name, Cheat.g[i].name)) + { + char *code_string = S9xCheatGroupToText (i); + char *validated = S9xCheatValidate (code); + + if (validated && !strcmp (code_string, validated)) + { + free (code_string); + free (validated); + return TRUE; + } + + free (code_string); + free (validated); + } + } + + return FALSE; +} + static void S9xLoadCheatsFromBMLNode (bml_node *n) { unsigned int i; @@ -725,7 +751,7 @@ static void S9xLoadCheatsFromBMLNode (bml_node *n) if (bml_find_sub(c, "enabled")) enabled = true; - if (desc && code) + if (desc && code && !S9xCheatIsDuplicate (desc, code)) { int index = S9xAddCheatGroup (desc, code); @@ -878,7 +904,7 @@ void S9xCheatsEnable (void) } } -bool8 S9xImportCheatsFromDatabase (const char *filename) +int S9xImportCheatsFromDatabase (const char *filename) { bml_node *bml; char sha256_txt[65]; @@ -888,7 +914,7 @@ bool8 S9xImportCheatsFromDatabase (const char *filename) bml = bml_parse_file (filename); if (!bml) - return FALSE; + return -1; /* No file */ for (i = 0; i < 32; i++) { @@ -909,7 +935,7 @@ bool8 S9xImportCheatsFromDatabase (const char *filename) { S9xLoadCheatsFromBMLNode (bml->child[i]); bml_free_node (bml); - return TRUE; + return 0; } } } @@ -917,5 +943,5 @@ bool8 S9xImportCheatsFromDatabase (const char *filename) bml_free_node (bml); - return FALSE; + return -2; /* No codes */ } diff --git a/gtk/Makefile.am b/gtk/Makefile.am index 7bf7fa7ea..e1d5d5834 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -8,7 +8,7 @@ snes9x_gtk_CXXFLAGS = -fno-exceptions -fno-rtti endif noinst_LIBRARIES = -AM_CPPFLAGS = -I../apu/bapu -I$(top_srcdir) -I.. -DSNES9XLOCALEDIR=\""$(snes9xlocaledir)"\" +AM_CPPFLAGS = -I../apu/bapu -I$(top_srcdir) -I.. -DDATADIR=\""$(snes9xdatadir)"\" -DSNES9XLOCALEDIR=\""$(snes9xlocaledir)"\" CLEANFILES = \ src/gtk_snes9x_ui.cpp \ diff --git a/gtk/configure.ac b/gtk/configure.ac index 336e35a80..07f8c5c9a 100644 --- a/gtk/configure.ac +++ b/gtk/configure.ac @@ -34,7 +34,10 @@ AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE") AM_GLIB_GNU_GETTEXT snes9xlocaledir='${prefix}/${DATADIRNAME}/locale' +snes9xdatadir='${prefix}/${DATADIRNAME}/snes9x' + AC_SUBST(snes9xlocaledir) +AC_SUBST(snes9xdatadir) AC_ARG_WITH(debug, [AS_HELP_STRING([--with(out)-debug], @@ -152,9 +155,9 @@ AC_ARG_WITH(screenshot, AC_ARG_WITH(gtk3, [AS_HELP_STRING([--with(out)-gtk3], - [Build with GTK+ 3 if available (default: without)])], + [Build with GTK+ 3 if available (default: with)])], [], - [with_gtk3=no]) + [with_gtk3=yes]) if test yes = "$with_debug" ; then CFLAGS="$CFLAGS -g" @@ -499,7 +502,7 @@ echo "Snes9x will build with support for the following:" echo "" if test yes = "$GTK3_WARNING" ; then - echo " GTK+ 3.0 (experimental)" + echo " GTK+ 3.0" else echo " GTK+ 2.0" fi diff --git a/gtk/data/Makefile.am b/gtk/data/Makefile.am index 72e39cc96..fa7f7ccc0 100644 --- a/gtk/data/Makefile.am +++ b/gtk/data/Makefile.am @@ -13,6 +13,9 @@ icon32x32_DATA = snes9x_32x32.png iconscalabledir = $(datadir)/icons/hicolor/scalable/apps iconscalable_DATA = snes9x.svg +cheatsdir = $(datadir)/snes9x +cheats_DATA = ../../data/cheats.bml + install-data-hook: mv -f $(DESTDIR)$(datadir)/icons/hicolor/16x16/apps/snes9x_16x16.png \ $(DESTDIR)$(datadir)/icons/hicolor/16x16/apps/snes9x.png diff --git a/gtk/src/gtk_cheat.cpp b/gtk/src/gtk_cheat.cpp index 74c413c42..a1eacf6d3 100644 --- a/gtk/src/gtk_cheat.cpp +++ b/gtk/src/gtk_cheat.cpp @@ -41,12 +41,37 @@ event_remove_code (GtkButton *button, gpointer data) ((Snes9xCheats *) data)->remove_code (); } +static void +event_search_database (GtkButton *button, gpointer data) +{ + ((Snes9xCheats *) data)->search_database (); +} + +static void +event_delete_all_cheats (GtkButton *button, gpointer data) +{ + ((Snes9xCheats *) data)->delete_all_cheats (); +} + static void event_code_toggled (GtkCellRendererToggle *cell_renderer, gchar *path, gpointer data) { - ((Snes9xCheats *) data)->toggle_code (path); + int enabled = !gtk_cell_renderer_toggle_get_active (cell_renderer); + + ((Snes9xCheats *) data)->toggle_code (path, enabled); + + return; +} + +void +event_row_activated (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *column, + gpointer data) +{ + ((Snes9xCheats *) data)->row_activated (path); return; } @@ -60,13 +85,17 @@ Snes9xCheats::Snes9xCheats (void) { { "add_code", G_CALLBACK (event_add_code) }, { "remove_code", G_CALLBACK (event_remove_code) }, + { "search_database", G_CALLBACK (event_search_database) }, + { "delete_all_cheats", G_CALLBACK (event_delete_all_cheats) }, { NULL, NULL} }; view = GTK_TREE_VIEW (get_widget ("cheat_treeview")); + g_signal_connect (view, "row-activated", G_CALLBACK (event_row_activated), (gpointer) this); renderer = gtk_cell_renderer_toggle_new (); + gtk_cell_renderer_toggle_set_activatable (GTK_CELL_RENDERER_TOGGLE (renderer), TRUE); gtk_tree_view_insert_column_with_attributes (view, -1, "", @@ -86,6 +115,8 @@ Snes9xCheats::Snes9xCheats (void) renderer, "text", COLUMN_DESCRIPTION, NULL); + GtkTreeViewColumn *column = gtk_tree_view_get_column (view, 1); + gtk_tree_view_column_set_resizable (column, TRUE); renderer = gtk_cell_renderer_text_new (); gtk_tree_view_insert_column_with_attributes (view, @@ -94,6 +125,9 @@ Snes9xCheats::Snes9xCheats (void) renderer, "text", COLUMN_CHEAT, NULL); + column = gtk_tree_view_get_column (view, 2); + gtk_tree_view_column_set_resizable (column, TRUE); + store = gtk_list_store_new (NUM_COLS, G_TYPE_BOOLEAN, @@ -182,14 +216,25 @@ void Snes9xCheats::refresh_tree_view (void) { GtkTreeIter iter; + unsigned int list_size; - gtk_list_store_clear (store); + list_size = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (store), NULL); + + if (Cheat.g.size () == 0) + return; + + for (unsigned int i = 0; i < Cheat.g.size() - list_size; i++) + gtk_list_store_append (store, &iter); + + gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter); for (unsigned int i = 0; i < Cheat.g.size (); i++) { char *str = S9xCheatGroupToText (i); - gtk_list_store_append (store, &iter); + if (i > 0) + gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter); + gtk_list_store_set (store, &iter, COLUMN_DESCRIPTION, !strcmp (Cheat.g [i].name, "") ? _("No description") @@ -200,7 +245,6 @@ Snes9xCheats::refresh_tree_view (void) delete[] str; } - return; } @@ -228,31 +272,127 @@ void Snes9xCheats::remove_code (void) { int index = get_selected_index (); + GtkTreeIter iter; if (index < 0) return; + gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (store), &iter, NULL, index); + gtk_list_store_remove (store, &iter); + S9xDeleteCheatGroup (index); - refresh_tree_view (); + return; +} + +void +Snes9xCheats::delete_all_cheats (void) +{ + S9xDeleteCheats (); + gtk_list_store_clear (store); return; } void -Snes9xCheats::toggle_code (const gchar *path) +Snes9xCheats::search_database (void) { - int index = get_index_from_path (path); + std::string filename; + int result; + int reason = 0; - if (index < 0) + filename = S9xGetFilename ("cheats.bml", CHEAT_DIR); + if (!(result = S9xImportCheatsFromDatabase (filename.c_str ()))) + { + refresh_tree_view (); return; + } - if (Cheat.g[index].enabled) - S9xDisableCheatGroup (index); - else + if (result < reason) + reason = result; + + char *config_dir = get_config_dir (); + filename = std::string (config_dir) + "/cheats.bml"; + free (config_dir); + if (!(result = S9xImportCheatsFromDatabase (filename.c_str ()))) + { + refresh_tree_view (); + return; + } + + if (result < reason) + reason = result; + + + filename = std::string (DATADIR) + "/cheats.bml"; + if (!(result = S9xImportCheatsFromDatabase (filename.c_str ()))) + { + refresh_tree_view (); + return; + } + + if (result < reason) + reason = result; + + filename = S9xGetFilename ("cheats.bml", ROM_DIR); + if (!(result = S9xImportCheatsFromDatabase (filename.c_str ()))) + { + refresh_tree_view (); + return; + } + + if (result < reason) + reason = result; + + GtkMessageDialog *dialog; + GtkDialogFlags flags = GTK_DIALOG_DESTROY_WITH_PARENT; + dialog = GTK_MESSAGE_DIALOG (gtk_message_dialog_new (get_window (), + flags, + GTK_MESSAGE_INFO, + GTK_BUTTONS_CLOSE, + reason == -1 ? _("Couldn't Find Cheats Database") : + _("No Matching Game Found"))); + gtk_message_dialog_format_secondary_markup(GTK_MESSAGE_DIALOG (dialog), + reason == -1 ? + _("The database file cheats.bml was not found. It is normally installed with " + "Snes9x, but you may also place a custom copy in your configuration or cheats directory.") : + _("No matching game was found in the databases. If you are using a non-official " + "translation or modified copy, you may be able to find and manually enter the codes.")); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (GTK_WIDGET (dialog)); + + return; +} + +void +Snes9xCheats::row_activated (GtkTreePath *path) +{ + gint *indices = gtk_tree_path_get_indices (path); + char *cheat_text; + + cheat_text = S9xCheatGroupToText (indices[0]); + set_entry_text ("code_entry", cheat_text); + delete[] cheat_text; + set_entry_text ("description_entry", Cheat.g[indices[0]].name); + + return; +} + +void +Snes9xCheats::toggle_code (const gchar *path, int enabled) +{ + GtkTreeIter iter; + int index = get_index_from_path (path); + + GtkTreePath *treepath = gtk_tree_path_new_from_string (path); + gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &iter, treepath); + gtk_list_store_set (store, &iter, COLUMN_ENABLED, enabled, -1); + + if (enabled) S9xEnableCheatGroup (index); + else + S9xDisableCheatGroup (index); - refresh_tree_view (); return; } diff --git a/gtk/src/gtk_cheat.h b/gtk/src/gtk_cheat.h index 8480932c7..a8baf94de 100644 --- a/gtk/src/gtk_cheat.h +++ b/gtk/src/gtk_cheat.h @@ -15,7 +15,10 @@ class Snes9xCheats : public GtkBuilderWindow void show (void); void add_code (void); void remove_code (void); - void toggle_code (const gchar *path); + void search_database (void); + void delete_all_cheats (void); + void toggle_code (const gchar *path, int enabled); + void row_activated (GtkTreePath *path); private: void refresh_tree_view (void); diff --git a/gtk/src/snes9x.ui b/gtk/src/snes9x.ui index 99573bda9..b468cb8c0 100644 --- a/gtk/src/snes9x.ui +++ b/gtk/src/snes9x.ui @@ -256,8 +256,8 @@ 0.10000000000000001 - 512 - 350 + 720 + 480 False GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 5 @@ -438,6 +438,36 @@ 5 + + + Delete All Cheats + True + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + + False + False + 6 + + + + + Search Cheat Database + True + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + + False + False + 7 + + False