summaryrefslogtreecommitdiff
path: root/src/gui/panels/bintree.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/panels/bintree.c')
-rw-r--r--src/gui/panels/bintree.c724
1 files changed, 724 insertions, 0 deletions
diff --git a/src/gui/panels/bintree.c b/src/gui/panels/bintree.c
new file mode 100644
index 0000000..9963f94
--- /dev/null
+++ b/src/gui/panels/bintree.c
@@ -0,0 +1,724 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * bintree.c - panneau d'accueil par défaut
+ *
+ * Copyright (C) 2012-2017 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * Chrysalide is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Chrysalide is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "bintree.h"
+
+
+#include <malloc.h>
+#include <regex.h>
+
+
+#include <i18n.h>
+
+
+#include "panel-int.h"
+#include "../../gtkext/tmgt.h"
+
+
+
+/* Origine de la dernière ouverture/fermeture reproductible */
+typedef enum _UserActionType
+{
+ UAT_COLLAPSE, /* Fermeture totale */
+ UAT_EXPAND, /* Ouverture totale */
+ UAT_DEPTH, /* Descente contrôlée */
+
+} UserActionType;
+
+
+/* Panneau de présentation des portions (instance) */
+struct _GBintreePanel
+{
+ GPanelItem parent; /* A laisser en premier */
+
+ GtkBuilder *builder; /* Constructeur utilisé */
+
+ GLoadedBinary *binary; /* Binaire représenté */
+ regex_t *filter; /* Filtre appliqué ou NULL */
+
+ UserActionType last; /* Dernière action */
+
+ GtkTreeIter *top; /* Transfert de racine */
+
+};
+
+/* Panneau de présentation des portions (classe) */
+struct _GBintreePanelClass
+{
+ GPanelItemClass parent; /* A laisser en premier */
+
+};
+
+
+/* Colonnes de la liste des messages */
+typedef enum _BinaryTreeColumn
+{
+ BTC_ICON, /* Image de représentation */
+ BTC_CAPTION, /* Désignation de l'élément */
+ BTC_START, /* Position de départ */
+ BTC_END, /* Position d'arrivée */
+ BTC_RIGHTS, /* Droits d'accès */
+
+ BTC_MATCHED, /* Correspondance établie ? */
+ BTC_PORTION /* Elément interne représenté */
+
+} BinaryTreeColumn;
+
+
+/* Initialise la classe des panneaux d'affichage des portions. */
+static void g_bintree_panel_class_init(GBintreePanelClass *);
+
+/* Initialise une instance de panneau d'affichage des portions. */
+static void g_bintree_panel_init(GBintreePanel *);
+
+/* Supprime toutes les références externes. */
+static void g_bintree_panel_dispose(GBintreePanel *);
+
+/* Procède à la libération totale de la mémoire. */
+static void g_bintree_panel_finalize(GBintreePanel *);
+
+/* Parcourt un ensemble de portions. */
+static bool populate_tree_with_portion(GBinPortion *, GBinPortion *, BinaryPortionVisit, GBintreePanel *);
+
+/* Réagit à un changement d'affichage principal de contenu. */
+static void update_panel_with_binary_portions(GBintreePanel *, GLoadedBinary *);
+
+/* Modifie la profondeur affichée des portions présentes. */
+static void on_depth_spin_value_changed(GtkSpinButton *, GtkTreeView *);
+
+/* Prend note du changement de filtre sur les portions. */
+static void on_search_entry_changed(GtkSearchEntry *, GBintreePanel *);
+
+/* Parcourt un arbre en place et retire les branches filtrées. */
+static void apply_filter_on_portions(GtkTreeStore *);
+
+/* Réagit au changement de sélection des portions. */
+static void on_bintree_selection_changed(GtkTreeSelection *, GBintreePanel *);
+
+
+
+/* Indique le type défini pour un panneau d'affichage des portions. */
+G_DEFINE_TYPE(GBintreePanel, g_bintree_panel, G_TYPE_PANEL_ITEM);
+
+
+/******************************************************************************
+* *
+* Paramètres : klass = classe à initialiser. *
+* *
+* Description : Initialise la classe des panneaux d'affichage des portions. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_bintree_panel_class_init(GBintreePanelClass *klass)
+{
+ GObjectClass *object; /* Autre version de la classe */
+ GEditorItemClass *editem; /* Encore une autre vision... */
+
+ object = G_OBJECT_CLASS(klass);
+
+ object->dispose = (GObjectFinalizeFunc/* ! */)g_bintree_panel_dispose;
+ object->finalize = (GObjectFinalizeFunc)g_bintree_panel_finalize;
+
+ editem = G_EDITOR_ITEM_CLASS(klass);
+
+ editem->update_binary = (update_item_binary_fc)update_panel_with_binary_portions;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : panel = instance à initialiser. *
+* *
+* Description : Initialise une instance de panneau d'affichage des portions. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_bintree_panel_init(GBintreePanel *panel)
+{
+ GEditorItem *base; /* Version basique d'instance */
+ GPanelItem *pitem; /* Version parente du panneau */
+ GtkTreeView *treeview; /* Affichage de la liste */
+ GtkCellRenderer *renderer; /* Moteur de rendu de colonne */
+ GtkTreeViewColumn *column; /* Colonne de la liste */
+
+ /* Eléments de base */
+
+ base = G_EDITOR_ITEM(panel);
+
+ base->name = PANEL_BINTREE_ID;
+
+ pitem = G_PANEL_ITEM(panel);
+
+ pitem->personality = PIP_SINGLETON;
+ pitem->lname = _("Binary tree");
+ pitem->dock_at_startup = true;
+ pitem->path = strdup("eN");
+
+ /* Compléments propres */
+
+ panel->binary = NULL;
+ panel->filter = NULL;
+
+ panel->last = UAT_EXPAND;
+
+ /* Représentation graphique */
+
+ panel->builder = gtk_builder_new_from_resource("/org/chrysalide/gui/panels/bintree.ui");
+
+ base->widget = GTK_WIDGET(gtk_builder_get_object(panel->builder, "box"));
+ g_object_ref(G_OBJECT(base->widget));
+ gtk_widget_unparent(base->widget);
+
+ /* Liste des projets récents */
+
+ treeview = GTK_TREE_VIEW(gtk_builder_get_object(panel->builder, "treeview"));
+
+ column = gtk_tree_view_column_new();
+ gtk_tree_view_append_column(treeview, column);
+ gtk_tree_view_set_expander_column(treeview, column);
+
+ renderer = gtk_cell_renderer_pixbuf_new();
+ gtk_tree_view_column_pack_start(column, renderer, FALSE);
+ gtk_tree_view_column_add_attribute(column, renderer, "surface", BTC_ICON);
+
+ renderer = gtk_cell_renderer_text_new();
+ gtk_tree_view_column_pack_start(column, renderer, TRUE);
+ gtk_tree_view_column_add_attribute(column, renderer, "markup", BTC_CAPTION);
+
+ column = gtk_tree_view_column_new();
+ gtk_tree_view_append_column(treeview, column);
+
+ renderer = gtk_cell_renderer_text_new();
+ g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
+ gtk_tree_view_column_pack_start(column, renderer, TRUE);
+ gtk_tree_view_column_add_attribute(column, renderer, "markup", BTC_START);
+
+ column = gtk_tree_view_column_new();
+ gtk_tree_view_append_column(treeview, column);
+
+ renderer = gtk_cell_renderer_text_new();
+ g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
+ gtk_tree_view_column_pack_start(column, renderer, TRUE);
+ gtk_tree_view_column_add_attribute(column, renderer, "markup", BTC_END);
+
+ column = gtk_tree_view_column_new();
+ gtk_tree_view_append_column(treeview, column);
+
+ renderer = gtk_cell_renderer_text_new();
+ gtk_tree_view_column_pack_start(column, renderer, TRUE);
+ gtk_tree_view_column_add_attribute(column, renderer, "markup", BTC_RIGHTS);
+
+ /* Connexion des signaux */
+
+ gtk_builder_add_callback_symbols(panel->builder,
+ "gtk_tree_view_collapse_all", G_CALLBACK(gtk_tree_view_collapse_all),
+ "gtk_tree_view_expand_all", G_CALLBACK(gtk_tree_view_expand_all),
+ "on_depth_spin_value_changed", G_CALLBACK(on_depth_spin_value_changed),
+ "on_search_entry_changed", G_CALLBACK(on_search_entry_changed),
+ "on_bintree_selection_changed", G_CALLBACK(on_bintree_selection_changed),
+ NULL);
+
+ gtk_builder_connect_signals(panel->builder, panel);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : panel = instance d'objet GLib à traiter. *
+* *
+* Description : Supprime toutes les références externes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_bintree_panel_dispose(GBintreePanel *panel)
+{
+ g_object_unref(G_OBJECT(panel->builder));
+
+ if (panel->binary != NULL)
+ g_object_unref(G_OBJECT(panel->binary));
+
+ G_OBJECT_CLASS(g_bintree_panel_parent_class)->dispose(G_OBJECT(panel));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : panel = instance d'objet GLib à traiter. *
+* *
+* Description : Procède à la libération totale de la mémoire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_bintree_panel_finalize(GBintreePanel *panel)
+{
+ if (panel->filter != NULL)
+ {
+ regfree(panel->filter);
+ free(panel->filter);
+ }
+
+ G_OBJECT_CLASS(g_bintree_panel_parent_class)->finalize(G_OBJECT(panel));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Crée un panneau présentant l'arborescence des portions. *
+* *
+* Retour : Adresse de la structure mise en place. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+GPanelItem *g_bintree_panel_new(void)
+{
+ GBintreePanel *result; /* Structure à retourner */
+
+ result = g_object_new(G_TYPE_BINTREE_PANEL, NULL);
+
+ return G_PANEL_ITEM(result);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : panel = portion de binaire à traiter. *
+* parent = portion parent de la portion visitée. *
+* visit = indication sur le sens de la visite. *
+* panel = lien vers toutes les autres informations utiles. *
+* *
+* Description : Parcourt un ensemble de portions. *
+* *
+* Retour : true pour continuer la visite. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool populate_tree_with_portion(GBinPortion *portion, GBinPortion *parent, BinaryPortionVisit visit, GBintreePanel *panel)
+{
+ const char *desc; /* Description d'origine */
+ bool fmatched; /* Correspondance rencontrée ? */
+ regmatch_t match; /* Position d'un filtre */
+ char *node_caption; /* Etiquette de nouveau noeud */
+ const mrange_t *range; /* Espace de portion à traiter */
+ VMPA_BUFFER(offset); /* Décalage physique */
+ char *node_start; /* Position pour nouveau noeud */
+ vmpa2t end; /* Zone de construction temp. */
+ char *node_end; /* Bordure pour nouveau noeud */
+ PortionAccessRights rights; /* Droits d'accès à analyser */
+ char hrights[4]; /* Version humainement lisible */
+ char *node_rights; /* Droits pour nouveau noeud */
+ cairo_surface_t *icon; /* Miniature de décoration */
+ GtkTreeStore *store; /* Modèle de gestion */
+ GtkTreeIter iter; /* Point d'insertion */
+ GtkTreeIter *save; /* Sauvegarde d'une position */
+
+ if (parent == NULL)
+ return true;
+
+ /* Insertion de la portion courante */
+
+ if (visit == BPV_ENTER || visit == BPV_SHOW)
+ {
+ /* Etiquette */
+
+ desc = g_binary_portion_get_desc(portion);
+
+ fmatched = is_content_matching(panel->filter, desc, &match);
+ node_caption = build_highlighted_name(desc, &match, 0);
+
+ /* Point de départ */
+
+ range = g_binary_portion_get_range(portion);
+
+ vmpa2_phys_to_string(get_mrange_addr(range), MDS_UNDEFINED, offset, NULL);
+
+ fmatched |= is_content_matching(panel->filter, offset, &match);
+ node_start = build_highlighted_name(offset, &match, 0);
+
+ /* Point d'arrivée */
+
+ compute_mrange_end_addr(range, &end);
+
+ vmpa2_phys_to_string(&end, MDS_UNDEFINED, offset, NULL);
+
+ fmatched |= is_content_matching(panel->filter, offset, &match);
+ node_end = build_highlighted_name(offset, &match, 0);
+
+ /* Droits nominaux */
+
+ rights = g_binary_portion_get_rights(portion);
+
+ hrights[0] = (rights & PAC_READ ? 'r' : '-');
+ hrights[1] = (rights & PAC_WRITE ? 'w' : '-');
+ hrights[2] = (rights & PAC_EXEC ? 'x' : '-');
+ hrights[3] = '\0';
+
+ fmatched |= is_content_matching(panel->filter, hrights, &match);
+ node_rights = build_highlighted_name(hrights, &match, 0);
+
+ /* Intégration */
+
+ icon = NULL;
+
+ store = GTK_TREE_STORE(gtk_builder_get_object(panel->builder, "store"));
+
+ gtk_tree_store_append(store, &iter, panel->top);
+
+ gtk_tree_store_set(store, &iter,
+ BTC_ICON, icon,
+ BTC_CAPTION, node_caption,
+ BTC_START, node_start,
+ BTC_END, node_end,
+ BTC_RIGHTS, node_rights,
+ BTC_MATCHED, fmatched,
+ BTC_PORTION, portion,
+ -1);
+
+ free(node_caption);
+ free(node_start);
+ free(node_end);
+ free(node_rights);
+
+ if (icon != NULL)
+ cairo_surface_destroy(icon);
+
+ }
+
+ /* Définition de la hiérarchie */
+
+ if (visit == BPV_ENTER)
+ {
+ save = gtk_tree_iter_copy(panel->top);
+
+ g_object_set_data_full(G_OBJECT(portion), "_save", save, (GDestroyNotify)gtk_tree_iter_free);
+
+ *panel->top = iter;
+
+ }
+
+ else if (visit == BPV_EXIT)
+ {
+ save = g_object_get_data(G_OBJECT(portion), "_save");
+
+ *panel->top = *save;
+
+ g_object_set_data(G_OBJECT(portion), "_save", NULL);
+
+ }
+
+ return true;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : panel = panneau à mettre à jour. *
+* binary = nouvelle instance de binaire analysé. *
+* *
+* Description : Réagit à un changement d'affichage principal de contenu. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void update_panel_with_binary_portions(GBintreePanel *panel, GLoadedBinary *binary)
+{
+ GtkTreeStore *store; /* Modèle de gestion */
+ GExeFormat *format; /* Format du binaire */
+ GBinPortion *portions; /* Couche première de portions */
+ GtkTreeIter top; /* Racine de l'arborescence */
+ gint max_depth; /* Profondeur maximale */
+ GtkSpinButton *depth_spin; /* Bouton de variation */
+ GtkTreeView *treeview; /* Arborescence constituée */
+
+ /* Réinitialisation */
+
+ if (panel->binary != NULL)
+ g_object_unref(G_OBJECT(panel->binary));
+
+ panel->binary = binary;
+
+ store = GTK_TREE_STORE(gtk_builder_get_object(panel->builder, "store"));
+
+ gtk_tree_store_clear(store);
+
+ /* Chargement */
+
+ if (binary != NULL)
+ {
+ g_object_ref(G_OBJECT(binary));
+
+ format = g_loaded_binary_get_format(binary);
+
+ portions = g_exe_format_get_portions(format);
+
+ gtk_tree_store_append(store, &top, NULL);
+
+ gtk_tree_store_set(store, &top,
+ BTC_ICON, NULL,
+ BTC_CAPTION, g_loaded_binary_get_name(binary, false),
+ -1);
+
+ panel->top = &top;
+ g_binary_portion_visit(portions, (visit_portion_fc)populate_tree_with_portion, panel);
+
+ g_object_unref(G_OBJECT(portions));
+
+ g_object_unref(G_OBJECT(format));
+
+ }
+
+ /* Détermination de la profondeur maximale */
+
+ gboolean compute_max_depth(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gint *max)
+ {
+ gint depth; /* Profondeur du point courant */
+
+ depth = gtk_tree_store_iter_depth(GTK_TREE_STORE(model), iter);
+
+ if (depth > *max)
+ *max = depth;
+
+ return FALSE;
+
+ }
+
+ max_depth = 0;
+
+ gtk_tree_model_foreach(GTK_TREE_MODEL(store), (GtkTreeModelForeachFunc)compute_max_depth, &max_depth);
+
+ depth_spin = GTK_SPIN_BUTTON(gtk_builder_get_object(panel->builder, "depth_spin"));
+
+ gtk_spin_button_set_range(depth_spin, 0, max_depth);
+
+ /* Restauration au mieux de l'affichage */
+
+ treeview = GTK_TREE_VIEW(gtk_builder_get_object(panel->builder, "treeview"));
+
+ switch (panel->last)
+ {
+ case UAT_COLLAPSE:
+ gtk_tree_view_collapse_all(treeview);
+ break;
+
+ case UAT_EXPAND:
+ gtk_tree_view_expand_all(treeview);
+ break;
+
+ case UAT_DEPTH:
+ on_depth_spin_value_changed(depth_spin, treeview);
+ break;
+
+ }
+
+ if (panel->filter != NULL)
+ apply_filter_on_portions(store);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : button = bouton de réglage de l'affichage. *
+* treeview = arborescence dont l'affichage est à moduler. *
+* *
+* Description : Modifie la profondeur affichée des portions présentes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void on_depth_spin_value_changed(GtkSpinButton *button, GtkTreeView *treeview)
+{
+ gint max_depth; /* Profondeur maximale */
+ GtkTreeModel *model; /* Modèle de gestion */
+
+ max_depth = gtk_spin_button_get_value_as_int(button);
+
+ gboolean apply_max_depth(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer unused)
+ {
+ gint depth; /* Profondeur du point courant */
+
+ depth = gtk_tree_store_iter_depth(GTK_TREE_STORE(model), iter);
+
+ if (depth < max_depth)
+ gtk_tree_view_expand_to_path(treeview, path);
+
+ return FALSE;
+
+ }
+
+ gtk_tree_view_collapse_all(treeview);
+
+ model = gtk_tree_view_get_model(treeview);
+
+ gtk_tree_model_foreach(model, (GtkTreeModelForeachFunc)apply_max_depth, NULL);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : entry = zone de texte avec un nouveau filtre d'affichage. *
+* panel = panneau contenant les informations globales. *
+* *
+* Description : Prend note du changement de filtre sur les portions. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void on_search_entry_changed(GtkSearchEntry *entry, GBintreePanel *panel)
+{
+ update_regex_on_search_entry_changed(entry, &panel->filter);
+
+ if (panel->binary != NULL)
+ {
+ g_object_ref(G_OBJECT(panel->binary));
+ update_panel_with_binary_portions(panel, panel->binary);
+ g_object_unref(G_OBJECT(panel->binary));
+ }
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : store = gestionnaire de contenu d'une arborescence donnée. *
+* *
+* Description : Parcourt un arbre en place et retire les branches filtrées. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void apply_filter_on_portions(GtkTreeStore *store)
+{
+ GtkTreeIter root; /* Racine de l'arboresence */
+
+ void check_portion_iter(GtkTreeIter *iter)
+ {
+ GtkTreeModel *model; /* Version alternative */
+ gint children_count; /* Nombre d'enfants présents */
+ gint i; /* Boucle de parcours */
+ GtkTreeIter child; /* Pointeur vers la descendance*/
+ gboolean fmatched; /* Correspondance immédiate ? */
+
+ model = GTK_TREE_MODEL(store);
+
+ children_count = gtk_tree_model_iter_n_children(model, iter);
+
+ for (i = children_count; i > 0; i--)
+ if (gtk_tree_model_iter_nth_child(model, &child, iter, i - 1))
+ check_portion_iter(&child);
+
+ children_count = gtk_tree_model_iter_n_children(model, iter);
+
+ gtk_tree_model_get(model, iter, BTC_MATCHED, &fmatched, -1);
+
+ if (!fmatched && children_count == 0)
+ gtk_tree_store_remove(store, iter);
+
+ }
+
+ if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &root))
+ check_portion_iter(&root);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : selection = sélection modifiée. *
+* panel = structure contenant les informations maîtresses. *
+* *
+* Description : Réagit au changement de sélection des portions. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void on_bintree_selection_changed(GtkTreeSelection *selection, GBintreePanel *panel)
+{
+ GtkTreeIter iter; /* Point de sélection */
+ GtkTreeModel *model; /* Modèle de gestion */
+ GBinPortion *portion; /* Portion à traiter */
+ const mrange_t *range; /* Couverture dudit symbole */
+ GtkDisplayPanel *display; /* Afficheur effectif de code */
+
+ if (gtk_tree_selection_get_selected(selection, &model, &iter))
+ {
+ gtk_tree_model_get(model, &iter, BTC_PORTION, &portion, -1);
+
+ if (portion != NULL)
+ {
+ range = g_binary_portion_get_range(portion);
+
+ display = g_editor_item_get_current_view(G_EDITOR_ITEM(panel));
+ gtk_display_panel_request_move(display, get_mrange_addr(range));
+
+ g_object_unref(G_OBJECT(portion));
+
+ }
+
+ }
+
+}