/* Chrysalide - Outil d'analyse de fichiers binaires
* binportion.c - représentation graphique de portions de binaire
*
* Copyright (C) 2013 Cyrille Bagard
*
* This file is part of Chrysalide.
*
* 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 "gbinportion.h"
#include
#include
#include
#include
#include
#include "../common/extstr.h"
/* ------------------------------- PORTION DE BINAIRE ------------------------------- */
/* Portion de données binaires quelconques (instance) */
struct _GBinPortion
{
GObject parent; /* A laisser en premier */
const unsigned int *level; /* Profondeur de la portion */
char *code; /* Code de la couleur de fond */
char *desc; /* Désignation humaine */
mrange_t range; /* Emplacement dans le code */
PortionAccessRights rights; /* Droits d'accès */
};
/* Portion de données binaires quelconques (classe) */
struct _GBinPortionClass
{
GObjectClass parent; /* A laisser en premier */
};
/* Initialise la classe des portions de données binaires. */
static void g_binary_portion_class_init(GBinPortionClass *);
/* Initialise une instance de portion de données binaires. */
static void g_binary_portion_init(GBinPortion *);
/* Supprime toutes les références externes. */
static void g_binary_portion_dispose(GBinPortion *);
/* Procède à la libération totale de la mémoire. */
static void g_binary_portion_finalize(GBinPortion *);
/* Définit le niveau de profondeur pour une branche de portions. */
static void g_binary_portion_set_level(GBinPortion *, const unsigned int *);
/* Détermine l'aire d'une sous-portion. */
static bool g_binary_portion_compute_sub_area(const GBinPortion *, phys_t, const GdkRectangle *, GdkRectangle *);
/* Détermine si une portion contient une adresse donnée. */
static bool g_portion_layer_contains_addr(const GBinPortion *, const vmpa2t *);
/* -------------------------- COUCHES DE PORTIONS BINAIRES -------------------------- */
/* Couche de portions binaires quelconques (instance) */
struct _GPortionLayer
{
GObject parent; /* A laisser en premier */
phys_t length; /* Taille de portion globale */
const char *name; /* Désignation de la couche */
unsigned int level; /* Profondeur de la portion */
GPortionLayer *sub_layer; /* Eventuelle couche inférieure*/
GBinPortion **portions; /* Portions incluses */
size_t count; /* Quantité d'inclusions */
};
/* Couche de portions binaires quelconques (classe) */
struct _GPortionLayerClass
{
GObjectClass parent; /* A laisser en premier */
};
/* Initialise la classe des couches de portions binaires. */
static void g_portion_layer_class_init(GPortionLayerClass *);
/* Initialise une instance de couche de portions binaires. */
static void g_portion_layer_init(GPortionLayer *);
/* Supprime toutes les références externes. */
static void g_portion_layer_dispose(GPortionLayer *);
/* Procède à la libération totale de la mémoire. */
static void g_portion_layer_finalize(GPortionLayer *);
/* ---------------------------------------------------------------------------------- */
/* PORTION DE BINAIRE */
/* ---------------------------------------------------------------------------------- */
/* Indique le type défini par la GLib pour les portions de données binaires. */
G_DEFINE_TYPE(GBinPortion, g_binary_portion, G_TYPE_OBJECT);
/******************************************************************************
* *
* Paramètres : klass = classe à initialiser. *
* *
* Description : Initialise la classe des portions de données binaires. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_binary_portion_class_init(GBinPortionClass *klass)
{
GObjectClass *object; /* Autre version de la classe */
object = G_OBJECT_CLASS(klass);
object->dispose = (GObjectFinalizeFunc/* ! */)g_binary_portion_dispose;
object->finalize = (GObjectFinalizeFunc)g_binary_portion_finalize;
}
/******************************************************************************
* *
* Paramètres : portion = instance à initialiser. *
* *
* Description : Initialise une instance de portion de données binaires. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_binary_portion_init(GBinPortion *portion)
{
portion->level = NULL;
}
/******************************************************************************
* *
* Paramètres : portion = instance d'objet GLib à traiter. *
* *
* Description : Supprime toutes les références externes. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_binary_portion_dispose(GBinPortion *portion)
{
G_OBJECT_CLASS(g_binary_portion_parent_class)->dispose(G_OBJECT(portion));
}
/******************************************************************************
* *
* Paramètres : portion = instance d'objet GLib à traiter. *
* *
* Description : Procède à la libération totale de la mémoire. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_binary_portion_finalize(GBinPortion *portion)
{
free(portion->code);
if (portion->desc != NULL)
free(portion->desc);
G_OBJECT_CLASS(g_binary_portion_parent_class)->finalize(G_OBJECT(portion));
}
/******************************************************************************
* *
* Paramètres : code = désignation humaine de la couleur de fond. *
* *
* Description : Crée une description de partie de code vierge. *
* *
* Retour : Instance mise en place. *
* *
* Remarques : - *
* *
******************************************************************************/
GBinPortion *g_binary_portion_new(const char *code)
{
GBinPortion *result; /* Structure à retourner */
result = g_object_new(G_TYPE_BIN_PORTION, NULL);
result->code = strdup(code);
return result;
}
/******************************************************************************
* *
* Paramètres : portion = description de partie à mettre à jour. *
* level = niveau de profondeur à associer. *
* *
* Description : Définit le niveau de profondeur pour une branche de portions.*
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_binary_portion_set_level(GBinPortion *portion, const unsigned int *level)
{
portion->level = level;
}
/******************************************************************************
* *
* Paramètres : a = premières informations à consulter. *
* b = secondes informations à consulter. *
* *
* Description : Etablit la comparaison ascendante entre deux portions. *
* *
* Retour : Bilan : -1 (a < b), 0 (a == b) ou 1 (a > b). *
* *
* Remarques : - *
* *
******************************************************************************/
int g_binary_portion_compare(const GBinPortion **a, const GBinPortion **b)
{
int result; /* Bilan à retourner */
const vmpa2t *addr_a; /* Adresse de la portion 'a' */
const vmpa2t *addr_b; /* Adresse de la portion 'b' */
const unsigned int *level_a; /* Niveau de la portion 'a' */
const unsigned int *level_b; /* Niveau de la portion 'b' */
addr_a = get_mrange_addr(&(*a)->range);
addr_b = get_mrange_addr(&(*b)->range);
result = cmp_vmpa(addr_a, addr_b);
if (result == 0)
{
level_a = (*a)->level;
level_b = (*b)->level;
if (level_a != NULL && level_b != NULL)
{
if (*level_a < *level_b)
result = -1;
else if (*level_a > *level_b)
result = 1;
}
}
return result;
}
/******************************************************************************
* *
* Paramètres : portion = description de partie à mettre à jour. *
* desc = nom à donner à la partie. *
* *
* Description : Attribue une description humaine à une partie de code. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_binary_portion_set_desc(GBinPortion *portion, const char *desc)
{
if (portion->desc != NULL) free(portion->desc);
portion->desc = strdup(desc);
}
/******************************************************************************
* *
* Paramètres : portion = description de partie à consulter. *
* *
* Description : Fournit la description attribuée à une partie de code. *
* *
* Retour : Nom donné à la partie. *
* *
* Remarques : - *
* *
******************************************************************************/
const char *g_binary_portion_get_desc(const GBinPortion *portion)
{
return portion->desc;
}
/******************************************************************************
* *
* Paramètres : portion = description de partie à mettre à jour. *
* addr = emplacement de la section à conserver. *
* size = taille de la section à conserver. *
* *
* Description : Définit les valeurs utiles d'une partie de code binaire. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_binary_portion_set_values(GBinPortion *portion, const vmpa2t *addr, phys_t size)
{
init_mrange(&portion->range, addr, size);
}
/******************************************************************************
* *
* Paramètres : portion = description de partie à mettre à jour. *
* *
* Description : Fournit l'emplacement d'une partie de code binaire. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
const mrange_t *g_binary_portion_get_range(const GBinPortion *portion)
{
return &portion->range;
}
/******************************************************************************
* *
* Paramètres : portion = description de partie à mettre à jour. *
* rights = droits d'accès de la partie. *
* *
* Description : Définit les droits associés à une partie de code. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_binary_portion_set_rights(GBinPortion *portion, PortionAccessRights rights)
{
portion->rights = rights;
}
/******************************************************************************
* *
* Paramètres : portion = description de partie à consulter. *
* *
* Description : Fournit les droits associés à une partie de code. *
* *
* Retour : Droits d'accès de la partie. *
* *
* Remarques : - *
* *
******************************************************************************/
PortionAccessRights g_binary_portion_get_rights(const GBinPortion *portion)
{
return portion->rights;
}
/******************************************************************************
* *
* Paramètres : portion = portion mère à consulter. *
* full = taille totale de la couche parente. *
* area = étendue de représentation de la portion mère. *
* sub_area = étendue de représentation de la portion fille. *
* *
* Description : Détermine l'aire d'une sous-portion. *
* *
* Retour : true si la sous-surface a été calculée correctement. *
* *
* Remarques : - *
* *
******************************************************************************/
static bool g_binary_portion_compute_sub_area(const GBinPortion *portion, phys_t full, const GdkRectangle *area, GdkRectangle *sub_area)
{
phys_t length; /* Taille de la portion */
phys_t start; /* Position de départ */
length = get_mrange_length(&portion->range);
/* On saute les portions comme le segment GNU_STACK... */
if (length == 0) return false;
start = get_phy_addr(get_mrange_addr(&portion->range));
sub_area->y = area->y;
sub_area->height = area->height;
sub_area->x = area->x + (start * area->width) / full;
sub_area->width = (length * area->width) / full;
return true;
}
/******************************************************************************
* *
* Paramètres : portion = portion mère à consulter. *
* addr = adresse du point de recherche. *
* *
* Description : Détermine si une portion contient une adresse donnée. *
* *
* Retour : true ou false selon le résultat. *
* *
* Remarques : - *
* *
******************************************************************************/
static bool g_portion_layer_contains_addr(const GBinPortion *portion, const vmpa2t *addr)
{
bool result; /* Bilan à retourner */
result = false;
/* Portion non allouée en mémoire -> adresse nulle ; on écarte */
if (get_virt_addr(get_mrange_addr(&portion->range)) == 0)
goto not_found;
result = mrange_contains_addr(&portion->range, addr);
not_found:
return result;
}
/******************************************************************************
* *
* Paramètres : portion = description de partie à consulter. *
* buffer = espace où placer ledit contenu. *
* msize = taille idéale des positions et adresses; *
* *
* Description : Insère dans un tampon une description de portion. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_binary_portion_print(const GBinPortion *portion, GCodeBuffer *buffer, MemoryDataSize msize)
{
mrange_t range; /* Couverture à fournir */
GBufferLine *line; /* Nouvelle ligne à éditer */
char rights[64]; /* Traduction en texte */
/* On ne traite pas les portions anonymes ! */
if (portion->desc == NULL) return;
init_mrange(&range, get_mrange_addr(&portion->range), 0);
line = g_code_buffer_prepare_new_line(buffer, &range);
g_buffer_line_fill_mrange(line, msize, msize);
g_buffer_line_add_flag(line, BLF_WIDTH_MANAGER);
g_code_buffer_append_new_line(buffer, line);
/* Séparation */
line = g_code_buffer_prepare_new_line(buffer, &range);
g_buffer_line_fill_mrange(line, msize, msize);
g_buffer_line_start_merge_at(line, BLC_ASSEMBLY_HEAD);
g_buffer_line_insert_text(line, BLC_ASSEMBLY_HEAD,
"; ======================================================", 56, RTT_COMMENT);
g_code_buffer_append_new_line(buffer, line);
/* Retour à la ligne */
line = g_code_buffer_prepare_new_line(buffer, &range);
g_buffer_line_fill_mrange(line, msize, msize);
g_buffer_line_start_merge_at(line, BLC_ASSEMBLY_HEAD);
g_buffer_line_insert_text(line, BLC_ASSEMBLY_HEAD, "; ", 2, RTT_COMMENT);
g_code_buffer_append_new_line(buffer, line);
/* Description */
line = g_code_buffer_prepare_new_line(buffer, &range);
g_buffer_line_fill_mrange(line, msize, msize);
g_buffer_line_start_merge_at(line, BLC_ASSEMBLY_HEAD);
g_buffer_line_insert_text(line, BLC_ASSEMBLY_HEAD, "; ", 2, RTT_COMMENT);
g_buffer_line_insert_text(line, BLC_ASSEMBLY_HEAD, portion->desc, strlen(portion->desc), RTT_COMMENT);
snprintf(rights, sizeof(rights), " (%s%s%s%s)",
_("rights: "),
portion->rights & PAC_READ ? "r" : "-",
portion->rights & PAC_WRITE ? "w" : "-",
portion->rights & PAC_EXEC ? "x" : "-");
g_buffer_line_insert_text(line, BLC_ASSEMBLY_HEAD, rights, strlen(rights), RTT_COMMENT);
g_code_buffer_append_new_line(buffer, line);
/* Retour à la ligne */
line = g_code_buffer_prepare_new_line(buffer, &range);
g_buffer_line_fill_mrange(line, msize, msize);
g_buffer_line_start_merge_at(line, BLC_ASSEMBLY_HEAD);
g_buffer_line_insert_text(line, BLC_ASSEMBLY_HEAD, "; ", 2, RTT_COMMENT);
g_code_buffer_append_new_line(buffer, line);
line = g_code_buffer_prepare_new_line(buffer, &range);
g_buffer_line_fill_mrange(line, msize, msize);
g_code_buffer_append_new_line(buffer, line);
}
/******************************************************************************
* *
* Paramètres : portion = description de partie à mettre à jour. *
* tooltip = astuce à compléter. [OUT] *
* *
* Description : Prépare une astuce concernant une portion pour son affichage.*
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_binary_portion_query_tooltip(GBinPortion *portion, GtkTooltip *tooltip)
{
char *markup; /* Description à construire */
VMPA_BUFFER(value); /* Traduction en texte */
/* Nom */
if (portion->desc != NULL)
{
markup = strdup("");
markup = stradd(markup, portion->desc);
markup = stradd(markup, "\n");
markup = stradd(markup, "\n");
}
else markup = strdup("");
markup = stradd(markup, "taille : ");
mrange_length_to_string(&portion->range, MDS_UNDEFINED, value, NULL);
markup = stradd(markup, value);
markup = stradd(markup, "\n");
/* Localisation */
markup = stradd(markup, "");
markup = stradd(markup, _("Localisation"));
markup = stradd(markup, "\n");
markup = stradd(markup, _("physical: from "));
mrange_phys_to_string(&portion->range, MDS_UNDEFINED, true, value, NULL);
markup = stradd(markup, value);
markup = stradd(markup, _(" to "));
mrange_phys_to_string(&portion->range, MDS_UNDEFINED, false, value, NULL);
markup = stradd(markup, value);
markup = stradd(markup, "\n");
markup = stradd(markup, _("memory: from "));
mrange_virt_to_string(&portion->range, MDS_UNDEFINED, true, value, NULL);
markup = stradd(markup, value);
markup = stradd(markup, _(" to "));
mrange_virt_to_string(&portion->range, MDS_UNDEFINED, false, value, NULL);
markup = stradd(markup, value);
markup = stradd(markup, "\n\n");
/* Droits d'accès */
markup = stradd(markup, "");
markup = stradd(markup, _("Rights"));
markup = stradd(markup, "\n");
snprintf(value, 2 * VMPA_MAX_SIZE, "%s%s%s",
portion->rights & PAC_READ ? "r" : "-",
portion->rights & PAC_WRITE ? "w" : "-",
portion->rights & PAC_EXEC ? "x" : "-");
markup = stradd(markup, value);
/* Impression finale */
gtk_tooltip_set_markup(tooltip, markup);
free(markup);
}
/******************************************************************************
* *
* Paramètres : portion = description de partie à consulter. *
* cr = contexte graphique pour le dessin. *
* area = étendue mise à disposition. *
* *
* Description : Représente la portion sur une bande dédiée. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_binary_portion_draw(const GBinPortion *portion, GtkStyleContext *context, cairo_t *cr, const GdkRectangle *area)
{
//cairo_set_line_width(cr, 1.0);
cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
gtk_style_context_save(context);
gtk_style_context_add_class(context, portion->code);
gtk_render_background(context, cr, area->x, area->y, area->width, area->height);
gtk_render_frame(context, cr, area->x, area->y, area->width, area->height);
gtk_style_context_restore(context);
}
/* ---------------------------------------------------------------------------------- */
/* COUCHES DE PORTIONS BINAIRES */
/* ---------------------------------------------------------------------------------- */
/* Indique le type défini par la GLib pour les couches de portions binaires. */
G_DEFINE_TYPE(GPortionLayer, g_portion_layer, G_TYPE_OBJECT);
/******************************************************************************
* *
* Paramètres : klass = classe à initialiser. *
* *
* Description : Initialise la classe des couches de portions binaires. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_portion_layer_class_init(GPortionLayerClass *klass)
{
GObjectClass *object; /* Autre version de la classe */
object = G_OBJECT_CLASS(klass);
object->dispose = (GObjectFinalizeFunc/* ! */)g_portion_layer_dispose;
object->finalize = (GObjectFinalizeFunc)g_portion_layer_finalize;
}
/******************************************************************************
* *
* Paramètres : layer = instance à initialiser. *
* *
* Description : Initialise une instance de couche de portions binaires. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_portion_layer_init(GPortionLayer *layer)
{
layer->level = 0;
}
/******************************************************************************
* *
* Paramètres : layer = instance d'objet GLib à traiter. *
* *
* Description : Supprime toutes les références externes. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_portion_layer_dispose(GPortionLayer *layer)
{
size_t i; /* Boucle de parcours */
if (layer->sub_layer != NULL)
g_object_unref(G_OBJECT(layer->sub_layer));
for (i = 0; i < layer->count; i++)
g_object_unref(G_OBJECT(layer->portions[i]));
G_OBJECT_CLASS(g_portion_layer_parent_class)->dispose(G_OBJECT(layer));
}
/******************************************************************************
* *
* Paramètres : layer = instance d'objet GLib à traiter. *
* *
* Description : Procède à la libération totale de la mémoire. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_portion_layer_finalize(GPortionLayer *layer)
{
if (layer->portions != NULL)
free(layer->portions);
G_OBJECT_CLASS(g_portion_layer_parent_class)->finalize(G_OBJECT(layer));
}
/******************************************************************************
* *
* Paramètres : length =
* name = désignation pouvant servir de suffixe aux portions. *
* *
* Description : Crée une nouvelle couche de portions binaires. *
* *
* Retour : Instance mise en place. *
* *
* Remarques : - *
* *
******************************************************************************/
GPortionLayer *g_portion_layer_new(phys_t length, const char *name)
{
GPortionLayer *result; /* Structure à retourner */
result = g_object_new(G_TYPE_BIN_PORTION, NULL);
result->length = length;
result->name = name;
return result;
}
/******************************************************************************
* *
* Paramètres : layer = couche rassemblant des portions à modifier. *
* Paramètres : sub = couche inférieure à rattacher à la couche courante. *
* *
* Description : Attache une couche à une autre en tant que couche inférieure.*
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_portion_layer_attach_sub(GPortionLayer *layer, GPortionLayer *sub)
{
void set_layers_length(GPortionLayer *parent, GPortionLayer *child)
{
if (child->length == NO_LENGTH_YET)
{
assert(parent->length != NO_LENGTH_YET);
child->length = parent->length;
if (child->sub_layer != NULL)
set_layers_length(child, child->sub_layer);
}
}
void set_layers_depth(GPortionLayer *parent, GPortionLayer *child)
{
child->level = parent->level + 1;
if (child->sub_layer != NULL)
set_layers_length(child, child->sub_layer);
}
set_layers_length(layer, sub);
set_layers_depth(layer, sub);
layer->sub_layer = sub;
}
/******************************************************************************
* *
* Paramètres : layer = couche rassemblant les portions d'un même niveau. *
* portion = portion à inclure dans la définition courante. *
* *
* Description : Procède à l'inclusion d'une portion dans une couche. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_portion_layer_include(GPortionLayer *layer, GBinPortion *portion)
{
GPortionLayer *sub; /* Sous couche indispensable */
bool conflict; /* Conflit dû aux débordements */
const mrange_t *range; /* Emplacement de la portion */
size_t i; /* Boucle de parcours */
const mrange_t *other; /* Emplacements déjà occupés */
/**
* On prend ici en compte le genre de situations suivantes :
*
* [21] .bss NOBITS 00088240 07823c 0018c8 00 WA 0 0 8
* [22] __libc_freeres_ptrs NOBITS 00089b08 07823c 000018 00 WA 0 0 4
* [23] .comment PROGBITS 00000000 07823c 000022 01 MS 0 0 1
*
* Pendant le désassemblage, la procédure n'aime pas trop les intersections
* de zones mémoire.
*/
conflict = false;
range = g_binary_portion_get_range(portion);
for (i = 0; i < layer->count && !conflict; i++)
{
other = g_binary_portion_get_range(layer->portions[i]);
conflict = mrange_intersects_mrange(range, other);
}
/* La portion recouvre-t-elle une portion déjà existante ? */
if (conflict)
{
if (layer->sub_layer == NULL)
{
sub = g_portion_layer_new(layer->length, layer->name);
g_portion_layer_attach_sub(layer, sub);
}
g_portion_layer_include(layer->sub_layer, portion);
}
/* Sinon on l'intègre dans la couche courante */
else
{
layer->portions = (GBinPortion **)realloc(layer->portions,
++layer->count * sizeof(GBinPortion *));
layer->portions[layer->count - 1] = portion;
g_binary_portion_set_level(portion, &layer->level);
qsort(layer->portions, layer->count, sizeof(GBinPortion *), (__compar_fn_t)g_binary_portion_compare);
}
}
/******************************************************************************
* *
* Paramètres : layer = couche première à parcourir intégralement. *
* count = nombre de portions trouvées et renvoyées. [OUT] *
* *
* Description : Fournit une liste triée de portions d'un binaire. *
* *
* Retour : Liste de définitions de zones à libérer après usage. *
* *
* Remarques : - *
* *
******************************************************************************/
GBinPortion **g_portion_layer_collect_all_portions(const GPortionLayer *layer, size_t *count)
{
GBinPortion **result; /* Liste construite à renvoyer */
GBinPortion **do_collect(const GPortionLayer *l, GBinPortion **lst, size_t *c)
{
size_t start; /* Indice de départ des ajouts */
size_t i; /* Boucle de parcours */
start = *c;
*c += l->count;
lst = (GBinPortion **)realloc(lst, *c * sizeof(GBinPortion *));
for (i = 0; i < l->count; i++)
lst[start + i] = l->portions[i];
return lst;
}
result = do_collect(layer, NULL, count);
qsort(result, *count, sizeof(GBinPortion *), (__compar_fn_t)g_binary_portion_compare);
return result;
}
/******************************************************************************
* *
* Paramètres : layer = couche de portions à parcourir pour les recherches. *
* x = abscisse du point de recherche. *
* area = étendue de portion mère, puis celle trouvée. [OUT] *
* *
* Description : Recherche la portion présente à un point donné. *
* *
* Retour : Portion trouvée à l'endroit indiqué. *
* *
* Remarques : - *
* *
******************************************************************************/
GBinPortion *g_portion_layer_find_portion_at_pos(const GPortionLayer *layer, gint x, GdkRectangle *area)
{
GBinPortion *result; /* Portion à retourner */
size_t i; /* Boucle de parcours */
GBinPortion *sub; /* Portion incluse à traiter */
GdkRectangle sub_area; /* Etendue d'une sous-portion */
if (layer->sub_layer != NULL)
result = g_portion_layer_find_portion_at_pos(layer->sub_layer, x, area);
else
result = NULL;
for (i = 0; i < layer->count && result == NULL; i++)
{
sub = layer->portions[i];
if (!g_binary_portion_compute_sub_area(sub, layer->length, area, &sub_area))
continue;
if (sub_area.x <= x && x < (sub_area.x + sub_area.width))
{
result = sub;
*area = sub_area;
}
}
return result;
}
/******************************************************************************
* *
* Paramètres : layer = couche de portions à parcourir pour les recherches. *
* addr = adresse du point de recherche. *
* area = étendue de portion mère, puis celle trouvée. [OUT] *
* *
* Description : Recherche la portion présente à une adresse donnée. *
* *
* Retour : Portion trouvée à l'endroit indiqué. *
* *
* Remarques : - *
* *
******************************************************************************/
GBinPortion *g_portion_layer_find_portion_at_addr(const GPortionLayer *layer, const vmpa2t *addr, GdkRectangle *area)
{
GBinPortion *result; /* Portion à retourner */
size_t i; /* Boucle de parcours #1 */
GBinPortion *sub; /* Portion incluse à traiter */
GdkRectangle sub_area; /* Etendue d'une sous-portion */
if (layer->sub_layer != NULL)
result = g_portion_layer_find_portion_at_addr(layer->sub_layer, addr, area);
else
result = NULL;
for (i = 0; i < layer->count && result == NULL; i++)
{
sub = layer->portions[i];
if (!g_portion_layer_contains_addr(sub, addr))
continue;
if (!g_binary_portion_compute_sub_area(sub, layer->length, area, &sub_area))
continue;
result = sub;
*area = sub_area;
}
return result;
}
/******************************************************************************
* *
* Paramètres : layer = couche de portions à parcourir pour les recherches. *
* x = abscisse du point de recherche. *
* area = étendue de représentation de la portion mère. *
* addr = adresse correspondante. [OUT] *
* *
* Description : Fournit la position correspondant à une adresse donnée. *
* *
* Retour : Succès de la traduction. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_portion_layer_get_addr_from_pos(GPortionLayer *layer, gint x, const GdkRectangle *area, vmpa2t *addr)
{
GdkRectangle owner_area; /* Aire de contenance */
GBinPortion *owner; /* Conteneur propriétaire */
owner_area = *area;
owner = g_portion_layer_find_portion_at_pos(layer, x, &owner_area);
if (owner == NULL) return false;
copy_vmpa(addr, get_mrange_addr(&owner->range));
advance_vmpa(addr, (get_mrange_length(&owner->range) * (x - owner_area.x)) / owner_area.width);
return true;
}
/******************************************************************************
* *
* Paramètres : layer = couche de portions à parcourir pour les recherches. *
* addr = adresse du point de recherche. *
* area = étendue de représentation de la portion mère. *
* x = position correspondante. [OUT] *
* *
* Description : Fournit l'adresse correspondant à une position donnée. *
* *
* Retour : Succès de la traduction. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_portion_layer_get_pos_from_addr(GPortionLayer *layer, const vmpa2t *addr, const GdkRectangle *area, gint *x)
{
GdkRectangle owner_area; /* Aire de contenance */
GBinPortion *owner; /* Conteneur propriétaire */
phys_t diff; /* Décallage à appliquer */
owner_area = *area;
owner = g_portion_layer_find_portion_at_addr(layer, addr, &owner_area);
if (owner == NULL) return false;
diff = compute_vmpa_diff(addr, get_mrange_addr(&owner->range));
*x = owner_area.x + (diff * owner_area.width) / get_mrange_length(&owner->range);
return true;
}
/******************************************************************************
* *
* Paramètres : layer = première couche amorçant la visite. *
* visitor = fonction à appeler à chaque étape de la descente. *
* data = adresse pointant vers des données de l'utilisateur.*
* *
* Description : Parcours un ensemble de portions binaires. *
* *
* Retour : true si la visite a été jusqu'à son terme, false sinon. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_portion_layer_visit(const GPortionLayer *layer, visit_portion_fc visitor, void *data)
{
bool result; /* Etat à retourner */
size_t i; /* Boucle de parcours */
if (layer->sub_layer != NULL)
result = g_portion_layer_visit(layer->sub_layer, visitor, data);
else
result = true;
for (i = 0; i < layer->count && result; i++)
result = visitor(layer->portions[i], data);
return result;
}
/******************************************************************************
* *
* Paramètres : layer = couche de portions à consulter. *
* x = abscisse du point de recherche. *
* y = ordonnée du point de recherche. *
* area = étendue de représentation de la portion mère. *
* tooltip = astuce à compléter. [OUT] *
* *
* Description : Prépare une astuce concernant une portion pour son affichage.*
* *
* Retour : TRUE pour valider l'affichage. *
* *
* Remarques : - *
* *
******************************************************************************/
gboolean g_portion_layer_query_tooltip(const GPortionLayer *layer, gint x, gint y, const GdkRectangle *area, GtkTooltip *tooltip)
{
GBinPortion *selected; /* Portion à décrire ici */
selected = g_portion_layer_find_portion_at_pos(layer, x, (GdkRectangle []) { *area });
if (selected == NULL) return FALSE;
g_binary_portion_query_tooltip(selected, tooltip);
return TRUE;
}
/******************************************************************************
* *
* Paramètres : layer = couche de portions à consulter. *
* cr = contexte graphique pour le dessin. *
* area = étendue mise à disposition. *
* *
* Description : Représente une couche de portions sur une bande dédiée. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_portion_layer_draw(const GPortionLayer *layer, GtkStyleContext *context, cairo_t *cr, const GdkRectangle *area)
{
size_t i; /* Boucle de parcours */
GBinPortion *sub; /* Portion incluse à montrer */
GdkRectangle sub_area; /* Etendue d'une sous-portion */
for (i = 0; i < layer->count; i++)
{
sub = layer->portions[i];
if (!g_binary_portion_compute_sub_area(sub, layer->length, area, &sub_area))
continue;
g_binary_portion_draw(sub, context, cr, &sub_area);
}
if (layer->sub_layer != NULL)
g_portion_layer_draw(layer->sub_layer, context, cr, area);
}