/* 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);
}