/* OpenIDA - Outil d'analyse de fichiers binaires
* gcodebuffer.h - prototypes pour l'affichage d'un fragment de code d'assemblage
*
* Copyright (C) 2010-2012 Cyrille Bagard
*
* This file is part of OpenIDA.
*
* OpenIDA 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.
*
* OpenIDA 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 Foobar. If not, see .
*/
#include "gcodebuffer.h"
#include
#include
#include "chrysamarshal.h"
#include "delayed-int.h"
/* -------------------------- PARCOURS DU CODE D'UN TAMPON -------------------------- */
#define G_TYPE_BUFFER_SCAN g_buffer_scan_get_type()
#define G_BUFFER_SCAN(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), g_buffer_scan_get_type(), GDelayedExport))
#define G_IS_BUFFER_SCAN(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_buffer_scan_get_type()))
#define G_BUFFER_SCAN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_BUFFER_SCAN, GDelayedExportClass))
#define G_IS_BUFFER_SCAN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_BUFFER_SCAN))
#define G_BUFFER_SCAN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_BUFFER_SCAN, GDelayedExportClass))
/* Ensembles binaires à désassembler (instance) */
typedef struct _GBufferScan
{
GDelayedWork parent; /* A laisser en premier */
GCodeBuffer *buffer; /* Tampon à manipuler */
vmpa_t start; /* Début du parcours ou 0 */
vmpa_t end; /* Fin du parcours ou VMPA_MAX */
char *message; /* Message de progression */
process_line_fc process; /* Fonction de traitement réel */
void *user_data; /* Données à faire suivre */
} GBufferScan;
/* Ensembles binaires à désassembler (classe) */
typedef struct _GBufferScanClass
{
GDelayedWorkClass parent; /* A laisser en premier */
} GBufferScanClass;
/* Indique le type défini pour les tâches d'exportation différée. */
static GType g_buffer_scan_get_type(void);
/* Initialise la classe des tâches d'exportation différée. */
static void g_buffer_scan_class_init(GBufferScanClass *);
/* Initialise une tâche d'exportation différée. */
static void g_buffer_scan_init(GBufferScan *);
/* Crée une tâche d'exportation différée. */
static GBufferScan *g_buffer_scan_new(GCodeBuffer *, vmpa_t, vmpa_t, const char *, process_line_fc, void *);
/* Assure l'exportation en différé. */
static void g_buffer_scan_process(GBufferScan *, GtkExtStatusBar *);
/* -------------------------- TAMPON POUR CODE DESASSEMBLE -------------------------- */
/* Tampon pour code désassemblé (instance) */
struct _GCodeBuffer
{
GObject parent; /* A laisser en premier */
BufferLineColumn main_column; /* Colonne principale */
GBufferLine **lines; /* Liste des lignes intégrées */
size_t count; /* Quantité en cache */
size_t used; /* Quantité utilisée */
size_t indent; /* Indentation des lignes */
};
/* Tampon pour code désassemblé (classe) */
struct _GCodeBufferClass
{
GObjectClass parent; /* A laisser en premier */
};
/* Taille des allocations de masse */
#define LINE_ALLOC_BULK 20
/* Procède à l'initialisation d'une classe de tampon de code. */
static void g_code_buffer_class_init(GCodeBufferClass *);
/* Procède à l'initialisation d'un tampon pour code désassemblé. */
static void g_code_buffer_init(GCodeBuffer *);
/* Convertit une adresse en indice de ligne. */
static size_t _g_code_buffer_get_index_from_address(const GCodeBuffer *, vmpa_t, bool);
/* Convertit une adresse en indice de ligne. */
static size_t g_code_buffer_get_index_from_address(GCodeBuffer *, vmpa_t, bool);
/* ---------------------- VUE PARTICULIERE D'UN TAMPON DE CODE ---------------------- */
/* Vue d'un tampon pour code désassemblé (instance) */
struct _GBufferView
{
GObject parent; /* A laisser en premier */
GCodeBuffer *buffer; /* Tampon de code visualisé */
vmpa_t start; /* Première ligne intégrée */
vmpa_t end; /* Dernière ligne intégrée */
GFontCache *fcache; /* Cache pour les polices */
gint line_height; /* Hauteur maximale des lignes */
gint max_widths[BLC_COUNT]; /* Taille cachée des colonnes */
gint left_margin; /* Marge gauche + espace */
gint left_text; /* Début d'impression du code */
gint last_width; /* Plus grande col. de fusion */
BufferLineColumn last_merge; /* Colonne de fusion extrême */
buffer_line_draw_fc drawing_extra; /* Fonction d'accompagnement */
void *drawing_data; /* Donnée utilisateur */
GSList *highlighted; /* Segments mis en évidence */
};
/* Vue d'un tampon pour code désassemblé (classe) */
struct _GBufferViewClass
{
GObjectClass parent; /* A laisser en premier */
/* Signaux */
void (* need_redraw) (GBufferView *);
};
#define HEIGHT_CACHED(view) ((view)->line_height != -1)
#define WIDTHS_CACHED(view) ((view)->max_widths[0] != -1)
/* Procède à l'initialisation d'une classe de vue de tampon. */
static void g_buffer_view_class_init(GBufferViewClass *);
/* Procède à l'initialisation d'une vue d'un tampon pour code. */
static void g_buffer_view_init(GBufferView *);
/* Réinitialise le cache de la hauteur des lignes. */
static void g_buffer_view_reset_required_height(GBufferView *);
/* Réinitialise le cache des largeurs de colonne calculées. */
static void g_buffer_view_reset_required_widths(GBufferView *);
/* Calcule les largeurs requises par une visualisation. */
static void g_buffer_view_compute_required_widths(GBufferView *, bool, bool);
/* ---------------------------------------------------------------------------------- */
/* PARCOURS DU CODE D'UN TAMPON */
/* ---------------------------------------------------------------------------------- */
/* Indique le type défini pour les tâches d'exportation différée. */
G_DEFINE_TYPE(GBufferScan, g_buffer_scan, G_TYPE_DELAYED_WORK);
/******************************************************************************
* *
* Paramètres : klass = classe à initialiser. *
* *
* Description : Initialise la classe des tâches d'exportation différée. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_buffer_scan_class_init(GBufferScanClass *klass)
{
}
/******************************************************************************
* *
* Paramètres : disass = instance à initialiser. *
* *
* Description : Initialise une tâche d'exportation différée. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_buffer_scan_init(GBufferScan *disass)
{
G_DELAYED_WORK(disass)->run = (run_task_fc)g_buffer_scan_process;
}
/******************************************************************************
* *
* Paramètres : buffer = tampon à manipuler. *
* start = première adresse visée ou 0. *
* end = dernière adresse visée ou VMPA_MAX. *
* message = message à afficher lors de la progression. *
* process = fonction assurant le traitement effectif. *
* data = données utilisateur à faire suivre. *
* *
* Description : Crée une tâche d'exportation différée. *
* *
* Retour : Tâche créée. *
* *
* Remarques : - *
* *
******************************************************************************/
static GBufferScan *g_buffer_scan_new(GCodeBuffer *buffer, vmpa_t start, vmpa_t end, const char *message, process_line_fc process, void *data)
{
GBufferScan *result; /* Tâche à retourner */
result = g_object_new(G_TYPE_BUFFER_SCAN, NULL);
result->buffer = buffer;
g_object_ref(G_OBJECT(buffer));
result->start = start;
result->end = end;
result->message = strdup(message);
result->process = process;
result->user_data = data;
return result;
}
/******************************************************************************
* *
* Paramètres : scan = parcours à mener. *
* statusbar = barre de statut à tenir informée. *
* *
* Description : Assure l'exportation en différé. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_buffer_scan_process(GBufferScan *scan, GtkExtStatusBar *statusbar)
{
size_t first; /* Première ligne visée */
size_t last; /* Dernière ligne visée + 1 */
GBufferLine **lines; /* Liste des lignes à traiter */
bstatus_id_t id; /* Identifiant de statut */
size_t i; /* Boucle de parcours */
/* TODO : lock scan->buffer->lines */
first = g_code_buffer_get_index_from_address(scan->buffer, scan->start, true);
last = g_code_buffer_get_index_from_address(scan->buffer, scan->end, false);
lines = scan->buffer->lines;
id = gtk_extended_status_bar_push(statusbar, scan->message, true);
if (scan->buffer->used > 0)
for (i = first; i <= last; i++)
{
if (!scan->process(scan->buffer, lines[i], scan->user_data))
break;
gtk_extended_status_bar_update_activity(statusbar, id,
(i - first) * 1.0 / (last - first));
}
/* TODO : unlock scan->buffer->lines */
gtk_extended_status_bar_remove(statusbar, id);
/* Avertit le commanditaire... */
scan->process(scan->buffer, NULL, scan->user_data);
}
/* ---------------------------------------------------------------------------------- */
/* TAMPON POUR CODE DESASSEMBLE */
/* ---------------------------------------------------------------------------------- */
/* Détermine le type du composant de tampon pour code désassemblé. */
G_DEFINE_TYPE(GCodeBuffer, g_code_buffer, G_TYPE_OBJECT);
/******************************************************************************
* *
* Paramètres : class = classe de composant GTK à initialiser. *
* *
* Description : Procède à l'initialisation d'une classe de tampon de code. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_code_buffer_class_init(GCodeBufferClass *class)
{
}
/******************************************************************************
* *
* Paramètres : buffer = composant GTK à initialiser. *
* *
* Description : Procède à l'initialisation d'un tampon pour code désassemblé.*
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_code_buffer_init(GCodeBuffer *buffer)
{
}
/******************************************************************************
* *
* Paramètres : main = colonne à référencer comme étant la principale. *
* *
* Description : Crée un nouveau composant de tampon pour code désassemblé. *
* *
* Retour : Composant GTK créé. *
* *
* Remarques : - *
* *
******************************************************************************/
GCodeBuffer *g_code_buffer_new(BufferLineColumn main)
{
GCodeBuffer *result; /* Composant à retourner */
result = g_object_new(G_TYPE_CODE_BUFFER, NULL);
result->main_column = main;
return result;
}
/******************************************************************************
* *
* Paramètres : buffer = composant GTK à mettre à jour. *
* addr = adresse où va se situer la ligne. *
* first = indique si on l'arrête à la première ou la dernière.*
* *
* Description : Convertit une adresse en indice de ligne. *
* *
* Retour : Indice de l'adresse trouvée, ou 0 en cas d'échec. *
* *
* Remarques : - *
* *
******************************************************************************/
static size_t _g_code_buffer_get_index_from_address(const GCodeBuffer *buffer, vmpa_t addr, bool first)
{
size_t result; /* Indice à retourner */
if (addr == VMPA_MAX)
return (buffer->used > 0 ? buffer->used - 1 : 0);
/* TODO : coder un parcours plus optimal ! */
for (result = 0; result < buffer->used; result++)
if (g_buffer_line_get_address(buffer->lines[result]) == addr)
break;
if (!first)
for (; result < (buffer->used - 1); result++)
if (g_buffer_line_get_address(buffer->lines[result + 1]) != addr)
break;
return result;
}
/******************************************************************************
* *
* Paramètres : buffer = composant GTK à mettre à jour. *
* addr = adresse où va se situer la ligne. *
* first = indique si on l'arrête à la première ou la dernière.*
* *
* Description : Convertit une adresse en indice de ligne. *
* *
* Retour : Indice de l'adresse trouvée, ou 0 en cas d'échec. *
* *
* Remarques : - *
* *
******************************************************************************/
static size_t g_code_buffer_get_index_from_address(GCodeBuffer *buffer, vmpa_t addr, bool first)
{
size_t result; /* Indice à retourner */
result = _g_code_buffer_get_index_from_address(buffer, addr, first);
/**
* Par commodités, on évite certaines instructions en cas d'échec dans les
* fonctions d'appels : la condition des boucles utilisant l'indice retourné (0)
* fait son office directement !
*/
if (result == buffer->used)
result = 0;
return result;
}
/******************************************************************************
* *
* Paramètres : buffer = composant GTK à mettre à jour. *
* addr = adresse où va se situer la ligne. *
* *
* Description : Ajoute une nouvelle ligne à un tampon pour code désassemblé. *
* *
* Retour : Nouvelle ligne vierge à écrire. *
* *
* Remarques : - *
* *
******************************************************************************/
GBufferLine *g_code_buffer_append_new_line(GCodeBuffer *buffer, vmpa_t addr)
{
GBufferLine *result; /* Instance à retourner */
size_t i; /* Boucle de parcours */
if (buffer->used == buffer->count)
{
buffer->count += LINE_ALLOC_BULK;
buffer->lines = (GBufferLine **)realloc(buffer->lines,
buffer->count * sizeof(GBufferLine *));
}
result = g_buffer_line_new(addr, buffer->main_column);
buffer->lines[buffer->used++] = result;
for (i = 0; i < buffer->indent; i++)
g_buffer_line_insert_text(result, BLC_ASSEMBLY_HEAD, " ", 4, RTT_RAW);
return result;
}
/******************************************************************************
* *
* Paramètres : buffer = composant GTK à mettre à jour. *
* addr = adresse où va se situer la ligne. *
* before = emplacement de l'insertion. *
* *
* Description : Ajoute une nouvelle ligne à un tampon pour code désassemblé. *
* *
* Retour : Nouvelle ligne vierge à écrire. *
* *
* Remarques : - *
* *
******************************************************************************/
GBufferLine *g_code_buffer_insert_at(GCodeBuffer *buffer, vmpa_t addr, bool before)
{
GBufferLine *result; /* Instance à retourner */
size_t index; /* Indice de la ligne visée */
index = _g_code_buffer_get_index_from_address(buffer, addr, true /* FIXME : after */);
if (index == buffer->used) return NULL;
if (buffer->used == buffer->count)
{
buffer->count += LINE_ALLOC_BULK;
buffer->lines = (GBufferLine **)realloc(buffer->lines,
buffer->count * sizeof(GBufferLine *));
}
if (before)
{
memmove(&buffer->lines[index + 1], &buffer->lines[index],
sizeof(GBufferLine *) * (buffer->used - index));
buffer->used++;
result = g_buffer_line_new(addr, buffer->main_column);
buffer->lines[index] = result;
}
else
/* FIXME */
;
return result;
}
/******************************************************************************
* *
* Paramètres : buffer = composant GTK à mettre à jour. *
* line = point d'insertion. *
* *
* Description : Ajoute une nouvelle ligne à un tampon pour code désassemblé. *
* *
* Retour : Nouvelle ligne vierge à écrire. *
* *
* Remarques : - *
* *
******************************************************************************/
GBufferLine *g_code_buffer_insert_after(GCodeBuffer *buffer, GBufferLine *line)
{
/* FIXME */
return NULL;
}
/******************************************************************************
* *
* Paramètres : buffer = composant GTK à mettre à jour. *
* addr = adresse où retrouver la ligne recherchée. *
* *
* Description : Retrouve une ligne au sein d'un tampon avec une adresse. *
* *
* Retour : Line retrouvée ou NULL en cas d'échec. *
* *
* Remarques : - *
* *
******************************************************************************/
GBufferLine *g_code_buffer_find_line_by_addr(const GCodeBuffer *buffer, vmpa_t addr)
{
GBufferLine *result; /* Instance à retourner */
size_t index; /* Indice de la ligne visée */
index = _g_code_buffer_get_index_from_address(buffer, addr, true);
if (index == buffer->used)
result = NULL;
else
result = buffer->lines[index];
return result;
}
/******************************************************************************
* *
* Paramètres : buffer = composant GTK à mettre à jour. *
* *
* Description : Augmente l'indentation des prochaines lignes. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_code_buffer_inc_indentation(GCodeBuffer *buffer)
{
buffer->indent++;
}
/******************************************************************************
* *
* Paramètres : buffer = composant GTK à mettre à jour. *
* *
* Description : Diminue l'indentation des prochaines lignes. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_code_buffer_dec_indentation(GCodeBuffer *buffer)
{
/* BUG_ON(buffer->indent == 0) */
buffer->indent--;
}
/******************************************************************************
* *
* Paramètres : buffer = tampon de données à utiliser. *
* start = première adresse visée ou 0. *
* end = dernière adresse visée ou VMPA_MAX. *
* message = message à afficher lors de la progression. *
* process = fonction assurant le traitement effectif. *
* data = données utilisateur à faire suivre. *
* *
* Description : Lance un parcours des différentes lignes du tampon de code. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_buffer_code_scan(GCodeBuffer *buffer, vmpa_t start, vmpa_t end, const char *message, process_line_fc process, void *data)
{
GBufferScan *scan; /* Procédure de parcours */
GWorkQueue *queue; /* Gestionnaire de différés */
scan = g_buffer_scan_new(buffer, start, end, message, process, data);
queue = get_work_queue();
g_work_queue_schedule_work(queue, G_DELAYED_WORK(scan));
}
/* ---------------------------------------------------------------------------------- */
/* VUE PARTICULIERE D'UN TAMPON DE CODE */
/* ---------------------------------------------------------------------------------- */
/* Détermine le type de la vue d'un tampon pour code désassemblé. */
G_DEFINE_TYPE(GBufferView, g_buffer_view, G_TYPE_OBJECT);
/******************************************************************************
* *
* Paramètres : class = classe de composant GTK à initialiser. *
* *
* Description : Procède à l'initialisation d'une classe de vue de tampon. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_buffer_view_class_init(GBufferViewClass *class)
{
g_signal_new("need-redraw",
G_TYPE_BUFFER_VIEW,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GBufferViewClass, need_redraw),
NULL, NULL,
g_cclosure_user_marshal_VOID__OBJECT,
G_TYPE_NONE, 0);
}
/******************************************************************************
* *
* Paramètres : buffer = composant GTK à initialiser. *
* *
* Description : Procède à l'initialisation d'une vue d'un tampon pour code. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_buffer_view_init(GBufferView *buffer)
{
buffer->fcache = g_font_cache_new();
g_buffer_view_reset_required_height(buffer);
g_buffer_view_reset_required_widths(buffer);
}
/******************************************************************************
* *
* Paramètres : buffer = tampon à représenter à l'écran. *
* widget = composant GTK de destination pour le rendu. *
* *
* Description : Crée une nouvelle vue d'un tampon pour code désassemblé. *
* *
* Retour : Composant GTK créé. *
* *
* Remarques : - *
* *
******************************************************************************/
GBufferView *g_buffer_view_new(GCodeBuffer *buffer)
{
GBufferView *result; /* Composant à retourner */
result = g_object_new(G_TYPE_BUFFER_VIEW, NULL);
g_object_ref(G_OBJECT(buffer));
result->buffer = buffer;
result->start = 0;
result->end = VMPA_MAX;
return result;
}
/******************************************************************************
* *
* Paramètres : view = visualisateur à mettre à jour. *
* first = première ligne à imprimer. *
* last = première ligne hors cadre. *
* *
* Description : Restreint le champ d'application de l'affichage. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_buffer_view_restrict(GBufferView *view, vmpa_t start, vmpa_t end)
{
view->start = start;
view->end = end;
}
/******************************************************************************
* *
* Paramètres : view = visualisateur à mettre à jour. *
* first = première ligne à imprimer ou NULL. [OUT] *
* last = première ligne hors cadre ou NULL. [OUT] *
* *
* Description : Indique le champ d'application de l'affichage. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_buffer_view_get_restrictions(GBufferView *view, vmpa_t *start, vmpa_t *end)
{
if (start != NULL) *start = view->start;
if (end != NULL) *end = view->end;
}
/******************************************************************************
* *
* Paramètres : view = visualisateur à consulter. *
* *
* Description : Fournit le tampon de code lié à un visualisateur donné. *
* *
* Retour : Tampon de code associé au gestionnaire d'affichage. *
* *
* Remarques : - *
* *
******************************************************************************/
GCodeBuffer *g_buffer_view_get_buffer(const GBufferView *view)
{
return view->buffer;
}
/******************************************************************************
* *
* Paramètres : view = visualisation à consulter. *
* *
* Description : Réinitialise le cache de la hauteur des lignes. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_buffer_view_reset_required_height(GBufferView *view)
{
view->line_height = -1;
}
/******************************************************************************
* *
* Paramètres : view = visualisation à consulter. *
* *
* Description : Réinitialise le cache des largeurs de colonne calculées. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_buffer_view_reset_required_widths(GBufferView *view)
{
unsigned int i; /* Boucle de parcours */
for (i = 0; i < BLC_COUNT; i++)
view->max_widths[i] = -1;
}
/******************************************************************************
* *
* Paramètres : view = visualisation à mettre à jour. *
* *
* Description : Calcule la hauteur requise par une visualisation. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_buffer_view_compute_required_height(GBufferView *view)
{
view->line_height = 17;
}
/******************************************************************************
* *
* Paramètres : view = visualisation à mettre à jour. *
* addr = indique si les positions doivent être affichées. *
* code = indique si le code binaire doit être affiché. *
* *
* Description : Calcule les largeurs requises par une visualisation. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_buffer_view_compute_required_widths(GBufferView *view, bool addr, bool code)
{
GBufferLine **lines; /* Liste des lignes à traiter */
size_t first; /* Première ligne intégrée */
size_t last; /* Dernière ligne intégrée */
size_t i; /* Boucle de parcours #1 */
unsigned int j; /* Boucle de parcours #2 */
gint width; /* Largeur d'une colonne */
BufferLineColumn merge; /* Début de fusion de colonnes */
if (!HEIGHT_CACHED(view))
g_buffer_view_compute_required_height(view);
lines = view->buffer->lines;
first = g_code_buffer_get_index_from_address(view->buffer, view->start, true);
last = g_code_buffer_get_index_from_address(view->buffer, view->end, false);
view->left_margin = 2 * view->line_height;
view->left_text = 2.5 * view->line_height;
view->last_width = 0;
view->last_merge = BLC_INVALID;
if (view->buffer->used > 0)
for (i = first; i <= last; i++)
{
for (j = 0; j < BLC_COUNT; j++)
{
width = g_buffer_line_get_column_width(lines[i], j);
view->max_widths[j] = MAX(view->max_widths[j], width);
}
width = g_buffer_line_get_merge_width(lines[i], &merge, addr, code);
view->last_width = MAX(view->last_width, width);
if (merge != BLC_COUNT)
{
if (view->last_merge == BLC_INVALID)
view->last_merge = merge;
else
view->last_merge = MAX(view->last_merge, merge);
}
}
if (view->last_merge == BLC_INVALID)
view->last_merge = BLC_COUNT;
}
/******************************************************************************
* *
* Paramètres : view = visualisation à consulter. *
* *
* Description : Fournit la hauteur d'impression d'une ligne visualisée. *
* *
* Retour : Hauteur de ligne en pixel. *
* *
* Remarques : - *
* *
******************************************************************************/
gint g_buffer_view_get_line_height(GBufferView *view)
{
if (!HEIGHT_CACHED(view))
g_buffer_view_compute_required_height(view);
return view->line_height;
}
/******************************************************************************
* *
* Paramètres : view = visualisation à consulter. *
* width = largeur requise pour une pleine visualisation. [OUT]*
* height = hauteur requise pour une pleine visualisation. [OUT]*
* addr = indique si les positions doivent être affichées. *
* code = indique si le code binaire doit être affiché. *
* *
* Description : Fournit les dimensions requises par une visualisation. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_buffer_view_get_size(GBufferView *view, gint *width, gint *height, bool addr, bool code)
{
gint col_width; /* Calcul selon les colonnes */
gint full_width; /* Calcul selon les fusions */
BufferLineColumn i; /* Boucle de parcours */
size_t first; /* Première ligne intégrée */
size_t last; /* Dernière ligne intégrée */
if (!WIDTHS_CACHED(view))
g_buffer_view_compute_required_widths(view, addr, code);
*width = view->left_text;
*height = view->line_height;
col_width = 0;
full_width = 0;
/* Première méthode */
for (i = 0; i < BLC_COUNT; i++)
{
if (i == BLC_ADDRESS && !addr) continue;
if (i == BLC_BINARY && !code) continue;
col_width += view->max_widths[i];
if ((i + 1) < BLC_COUNT)
col_width += COL_MARGIN;
}
/* Seconde méthode */
for (i = 0; i < view->last_merge; i++)
{
if (i == BLC_ADDRESS && !addr) continue;
if (i == BLC_BINARY && !code) continue;
full_width += view->max_widths[i];
if ((i + 1) < view->last_merge)
full_width += COL_MARGIN;
}
full_width += view->last_width + COL_MARGIN;
/* Mise en concurrence et poursuite... */
*width += + MAX(col_width, full_width);
first = g_code_buffer_get_index_from_address(view->buffer, view->start, true);
last = g_code_buffer_get_index_from_address(view->buffer, view->end, false);
*height *= (last - first + 1);
}
/******************************************************************************
* *
* Paramètres : view = vue de tampon à mettre à jour. *
* x = abscisse de la zone principale à traiter. *
* y = ordonnée de la zone principale à traiter. *
* caret = position du curseur à construire. [OUT] *
* *
* Description : Calcule la position idéale de curseur pour un point donné. *
* *
* Retour : Adresse si une a pu être déterminée, VMPA_INVALID sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
vmpa_t g_buffer_view_compute_caret(GBufferView *view, gint x, gint y, GdkRectangle *caret)
{
size_t index; /* Indice de la ligne trouvée */
GBufferLine *line; /* Ligne sous le pointeur */
gint tmp_x; /* Copie de travail modifiable */
GBufferSegment *segment; /* Segment visé par le pointeur*/
size_t first; /* Première ligne intégrée */
line = g_buffer_view_find_line_at(view, y, &index);
if (line == NULL) return VMPA_INVALID;
tmp_x = x;
tmp_x -= view->left_text;
if (tmp_x < 0) return VMPA_INVALID;
segment = g_buffer_line_get_segment_at(line, view->max_widths, &tmp_x, true);
if (segment == NULL) printf("no segment\n");
if (segment == NULL) return VMPA_INVALID;
caret->x = (x - tmp_x) + g_buffer_segment_get_caret_position(segment, tmp_x);
first = g_code_buffer_get_index_from_address(view->buffer, view->start, true);
caret->y = (index - first) * view->line_height;
caret->width = 2;
caret->height = view->line_height;
return g_buffer_line_get_address(line);
}
/******************************************************************************
* *
* Paramètres : view = vue de tampon à mettre à jour. *
* caret = position du curseur à faire évoluer. *
* ctrl = indique la demande d'un parcours rapide. *
* dir = direction du parcours. *
* addr = indique si les positions doivent être affichées. *
* code = indique si le code binaire doit être affiché. *
* *
* Description : Déplace le curseur au sein d'une vue de tampon. *
* *
* Retour : Adresse si une a pu être déterminée, VMPA_INVALID sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
vmpa_t g_buffer_view_move_caret(GBufferView *view, GdkRectangle *caret, bool ctrl, GdkScrollDirection dir, bool addr, bool code)
{
bool result; /* Actualisation à renvoyer */
bool computed; /* Récursivité pris en compte */
gint lheight; /* Hauteur d'une ligne */
gint left_pos; /* Retour à la ligne */
gint right_pos; /* Position d'extrème droite */
BufferLineColumn i; /* Boucle de parcours */
size_t first; /* Première ligne intégrée */
size_t last; /* Dernière ligne intégrée */
GBufferLine *line; /* Ligne sous le pointeur */
result = VMPA_INVALID;
computed = false;
switch (dir)
{
case GDK_SCROLL_UP:
case GDK_SCROLL_DOWN:
lheight = g_buffer_view_get_line_height(view);
break;
case GDK_SCROLL_LEFT:
case GDK_SCROLL_RIGHT:
left_pos = view->left_text;
if (addr) left_pos += view->max_widths[BLC_ADDRESS] + COL_MARGIN;
if (code) left_pos += view->max_widths[BLC_BINARY] + COL_MARGIN;
right_pos = left_pos;
for (i = BLC_ASSEMBLY_HEAD; i < BLC_COUNT; i++)
right_pos += view->max_widths[i] + COL_MARGIN;
break;
}
first = g_code_buffer_get_index_from_address(view->buffer, view->start, true);
last = g_code_buffer_get_index_from_address(view->buffer, view->end, false);
switch (dir)
{
case GDK_SCROLL_UP:
result = (caret->y >= (first * lheight));
if (result)
caret->y -= lheight;
break;
case GDK_SCROLL_DOWN:
result = ((caret->y + lheight) < (last * lheight));
if (result)
caret->y += lheight;
break;
case GDK_SCROLL_LEFT:
line = g_buffer_view_find_line_at(view, caret->y, NULL);
if (line == NULL) break;
result = g_buffer_line_move_caret(line, caret, ctrl, GDK_SCROLL_LEFT, addr, code);
if (caret->x < left_pos)
{
caret->x = right_pos;
result = g_buffer_view_move_caret(view, caret, ctrl, GDK_SCROLL_UP, addr, code);
if (result == VMPA_INVALID)
caret->x = left_pos;
else
computed = true;
}
break;
case GDK_SCROLL_RIGHT:
line = g_buffer_view_find_line_at(view, caret->y, NULL);
if (line == NULL) break;
result = g_buffer_line_move_caret(line, caret, ctrl, GDK_SCROLL_RIGHT, addr, code);
if (!result)
{
caret->x = left_pos;
result = g_buffer_view_move_caret(view, caret, ctrl, GDK_SCROLL_DOWN, addr, code);
if (result == VMPA_INVALID)
caret->x = right_pos;
else
computed = true;
}
break;
}
if (result && !computed)
result = g_buffer_view_compute_caret(view, caret->x, caret->y, caret);
return result;
}
/******************************************************************************
* *
* Paramètres : view = vue de tampon à mettre à jour. *
* *
* Description : Supprime toute mise en évidence de segments. *
* *
* Retour : true si un besoin d'actualisation d'affichage se fait sentir.*
* *
* Remarques : - *
* *
******************************************************************************/
bool g_buffer_view_unhighlight_segments(GBufferView *view)
{
GSList *iter; /* Boucle de parcours */
GBufferSegment *segment; /* Segment visé par le pointeur*/
if (g_slist_length(view->highlighted) == 0)
return false;
for (iter = view->highlighted; iter != NULL; iter = view->highlighted)
{
segment = G_BUFFER_SEGMENT(iter->data);
g_buffer_segment_set_style(segment, SRS_CLASSIC);
g_object_unref(G_OBJECT(segment));
view->highlighted = g_slist_remove_link(view->highlighted, iter);
}
return true;
}
/******************************************************************************
* *
* Paramètres : view = vue de tampon à mettre à jour. *
* x = abscisse de la zone principale à traiter. *
* y = ordonnée de la zone principale à traiter. *
* *
* Description : Surligne tous les segments similaires à celui sous la souris.*
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_buffer_view_highlight_segments(GBufferView *view, gint x, gint y)
{
GBufferLine *line; /* Ligne sous le pointeur */
GBufferSegment *segment; /* Segment visé par le pointeur*/
bool need_redraw; /* Besoin d'actualisation ? */
size_t first; /* Première ligne intégrée */
size_t last; /* Dernière ligne intégrée */
size_t i; /* Boucle de parcours */
line = g_buffer_view_find_line_at(view, y, NULL);
if (line == NULL) return;
do
{
vmpa_t addr;
addr = g_buffer_line_get_address(line);
printf(" ... x2 clic at 0x%08lx\n", addr);
}
while (0);
x -= view->left_text;
if (x < 0) return;
segment = g_buffer_line_get_segment_at(line, view->max_widths, &x, false);
printf(" ... seg @%d ? %p\n", x, segment);
if (segment == NULL) return;
printf("text :: '%s'\n", g_buffer_segment_get_text(segment));
if (view->highlighted != NULL)
need_redraw = g_buffer_view_unhighlight_segments(view);
else
need_redraw = false;
first = g_code_buffer_get_index_from_address(view->buffer, view->start, true);
last = g_code_buffer_get_index_from_address(view->buffer, view->end, false);
for (i = first; i < last; i++)
view->highlighted = g_buffer_line_highlight_all_same_segments(view->buffer->lines[i],
view->highlighted, segment);
if (g_slist_length(view->highlighted) > 0)
need_redraw = true;
if (need_redraw)
g_signal_emit_by_name(view, "need-redraw");
}
/******************************************************************************
* *
* Paramètres : view = visualisation à mettre à jour. *
* method = procédure à appeler à chaque dessin de ligne. *
* data = donnée utilisateur à passer lors des appels. *
* *
* Description : Définit à une procédure à appeler lors des dessins de ligne. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_buffer_view_define_extra_drawing(GBufferView *view, buffer_line_draw_fc method, void *data)
{
view->drawing_extra = method;
view->drawing_data = data;
}
/******************************************************************************
* *
* Paramètres : view = visualisation à représenter. *
* event = informations liées à l'événement. *
* gc = contexte graphique à utiliser pour les pinceaux. *
* fake_x = abscisse réelle du point 0 à l'écran. *
* fake_y = ordonnée réelle du point 0 à l'écran. *
* addr = indique si les positions doivent être affichées. *
* code = indique si le code binaire doit être affiché. *
* *
* Description : Imprime la visualisation du tampon de code désassemblé. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_buffer_view_draw(const GBufferView *view, const GdkEventExpose *event, GdkGC *gc, gint fake_x, gint fake_y, bool addr, bool code)
{
cairo_t *cairo; /* Gestionnaire de rendu */
gint real_x; /* Abscisse réelle pour tampon */
gint real_y; /* Ordonnée réelle pour tampon */
size_t first; /* Première ligne visée */
size_t end; /* Dernière ligne avant limite */
size_t last; /* Dernière ligne visée + 1 */
gint y; /* Point de départ + décallage */
GBufferLine **lines; /* Liste des lignes à traiter */
size_t i; /* Boucle de parcours */
cairo = gdk_cairo_create(event->window);
gdk_cairo_region(cairo, event->region);
real_x = fake_x + view->left_text;
real_y = fake_y + event->area.y;
first = g_code_buffer_get_index_from_address(view->buffer, view->start, true);
first += (real_y / view->line_height);
last = first + (event->area.height / view->line_height);
if (event->area.height % view->line_height > 0) last++;
end = g_code_buffer_get_index_from_address(view->buffer, view->end, false);
last = MIN(last, end);
y = event->area.y - (real_y % view->line_height);
lines = view->buffer->lines;
if (view->buffer->used > 0)
for (i = first; i <= last; i++)
{
/* TODO : skip if... */
/*
if (view->drawing_extra != NULL)
view->drawing_extra(lines[i], drawable, gc, fake_x, y, view->drawing_data);
*/
g_buffer_line_draw(lines[i], cairo, view->fcache,
view->max_widths, real_x, y, addr, code);
y += view->line_height;
}
cairo_destroy(cairo);
}
/******************************************************************************
* *
* Paramètres : view = visualisation à consulter. *
* y = ordonnée comprise dans la ligne recherchée. *
* idx = indice de la ligne trouvée ou NULL. [OUT] *
* *
* Description : Fournit la ligne présente à une ordonnée donnée. *
* *
* Retour : Ligne retrouvée ou NULL si aucune. *
* *
* Remarques : - *
* *
******************************************************************************/
GBufferLine *g_buffer_view_find_line_at(GBufferView *view, gint y, size_t *idx)
{
gint lheight; /* Hauteur d'une ligne */
size_t index; /* Indice attendu */
lheight = g_buffer_view_get_line_height(view);
index = y / lheight;
if (idx != NULL)
*idx = index;
return (index < view->buffer->used ? view->buffer->lines[index] : NULL);
}
/******************************************************************************
* *
* Paramètres : view = composant GTK à consulter. *
* addr = adresse à présenter à l'écran. *
* x = position horizontale au sein du composant. [OUT] *
* y = position verticale au sein du composant. [OUT] *
* *
* Description : Indique la position d'affichage d'une adresse donnée. *
* *
* Retour : true si l'adresse fait partie du composant, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_buffer_view_get_address_coordinates(GBufferView *view, vmpa_t addr, gint *x, gint *y)
{
gint lheight; /* Hauteur d'une ligne */
vmpa_t current; /* Adresse parcourue */
size_t first; /* Première ligne intégrée */
size_t last; /* Dernière ligne intégrée */
size_t i; /* Boucle de parcours */
*x = 0;
*y = 0;
lheight = g_buffer_view_get_line_height(view);
current = VMPA_MAX;
first = g_code_buffer_get_index_from_address(view->buffer, view->start, true);
last = g_code_buffer_get_index_from_address(view->buffer, view->end, false);
if (view->buffer->used > 0)
for (i = first; i <= last; i++)
{
current = g_buffer_line_get_address(view->buffer->lines[i]);
if (current == addr)
break;
if (current > addr)
return false;
*y += lheight;
}
return (current == addr);
}