/* Chrysalide - Outil d'analyse de fichiers binaires
 * symbol.c - gestion des symboles dans un binaire
 *
 * Copyright (C) 2009-2017 Cyrille Bagard
 *
 *  This file is part of Chrysalide.
 *
 *  Chrysalide is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  Chrysalide is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with Chrysalide.  If not, see <http://www.gnu.org/licenses/>.
 */


#include "symbol.h"


#include <assert.h>
#include <string.h>


#include "symbol-int.h"
#include "../glibext/linegen-int.h"



/* --------------------- FONCTIONNALITES BASIQUES POUR SYMBOLES --------------------- */


/* Initialise la classe des symboles d'exécutables. */
static void g_binary_symbol_class_init(GBinSymbolClass *);

/* Initialise une instance de symbole d'exécutable. */
static void g_binary_symbol_init(GBinSymbol *);

/* Procède à l'initialisation de l'interface de génération. */
static void g_binary_symbol_interface_init(GLineGeneratorInterface *);

/* Supprime toutes les références externes. */
static void g_binary_symbol_dispose(GBinSymbol *);

/* Procède à la libération totale de la mémoire. */
static void g_binary_symbol_finalize(GBinSymbol *);



/* ------------------------ OFFRE DE CAPACITES DE GENERATION ------------------------ */


/* Indique le nombre de ligne prêtes à être générées. */
static size_t g_binary_symbol_count_lines(const GBinSymbol *);

/* Retrouve l'emplacement correspondant à une position donnée. */
static void g_binary_symbol_compute_addr(const GBinSymbol *, gint, vmpa2t *, size_t, size_t);

/* Détermine si le conteneur s'inscrit dans une plage donnée. */
static int g_binary_symbol_contains_addr(const GBinSymbol *, const vmpa2t *, size_t, size_t);

/* Renseigne sur les propriétés liées à un générateur. */
static BufferLineFlags g_binary_symbol_get_flags(const GBinSymbol *, size_t, size_t);

/* Imprime dans une ligne de rendu le contenu représenté. */
static void g_binary_symbol_print(GBinSymbol *, GBufferLine *, size_t, size_t, const GBinContent *);



/* ---------------------------------------------------------------------------------- */
/*                       FONCTIONNALITES BASIQUES POUR SYMBOLES                       */
/* ---------------------------------------------------------------------------------- */


/* Indique le type défini pour un symbole d'exécutable. */
G_DEFINE_TYPE_WITH_CODE(GBinSymbol, g_binary_symbol, G_TYPE_OBJECT,
                        G_IMPLEMENT_INTERFACE(G_TYPE_LINE_GENERATOR, g_binary_symbol_interface_init));


/******************************************************************************
*                                                                             *
*  Paramètres  : klass = classe à initialiser.                                *
*                                                                             *
*  Description : Initialise la classe des symboles d'exécutables.             *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_binary_symbol_class_init(GBinSymbolClass *klass)
{
    GObjectClass *object;                   /* Autre version de la classe  */

    object = G_OBJECT_CLASS(klass);

    object->dispose = (GObjectFinalizeFunc/* ! */)g_binary_symbol_dispose;
    object->finalize = (GObjectFinalizeFunc)g_binary_symbol_finalize;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : symbol = instance à initialiser.                             *
*                                                                             *
*  Description : Initialise une instance de symbole d'exécutable.             *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_binary_symbol_init(GBinSymbol *symbol)
{
    g_binary_symbol_set_target_type(symbol, STP_COUNT);

    g_binary_symbol_set_status(symbol, SSS_INTERNAL);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : iface = interface GLib à initialiser.                        *
*                                                                             *
*  Description : Procède à l'initialisation de l'interface de génération.     *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_binary_symbol_interface_init(GLineGeneratorInterface *iface)
{
    iface->count = (linegen_count_lines_fc)g_binary_symbol_count_lines;
    iface->compute = (linegen_compute_fc)g_binary_symbol_compute_addr;
    iface->contains = (linegen_contains_fc)g_binary_symbol_contains_addr;
    iface->get_flags = (linegen_get_flags_fc)g_binary_symbol_get_flags;
    iface->print = (linegen_print_fc)g_binary_symbol_print;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : symbol = instance d'objet GLib à traiter.                    *
*                                                                             *
*  Description : Supprime toutes les références externes.                     *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_binary_symbol_dispose(GBinSymbol *symbol)
{
    G_OBJECT_CLASS(g_binary_symbol_parent_class)->dispose(G_OBJECT(symbol));

}


/******************************************************************************
*                                                                             *
*  Paramètres  : symbol = instance d'objet GLib à traiter.                    *
*                                                                             *
*  Description : Procède à la libération totale de la mémoire.                *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_binary_symbol_finalize(GBinSymbol *symbol)
{
    if (symbol->alt != NULL)
        free(symbol->alt);

    G_OBJECT_CLASS(g_binary_symbol_parent_class)->finalize(G_OBJECT(symbol));

}


/******************************************************************************
*                                                                             *
*  Paramètres  : range = espace couvert par le nouveau symbole.               *
*                type  = type de symbole à créer.                             *
*                                                                             *
*  Description : Crée un nouveau symbole d'exécutable.                        *
*                                                                             *
*  Retour      : Adresse de l'instance mise en place ou NULL en cas d'échec.  *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

GBinSymbol *g_binary_symbol_new(const mrange_t *range, SymbolType type)
{
    GBinSymbol *result;                     /* Nouveau symbole à renvoyer  */

    result = g_object_new(G_TYPE_BIN_SYMBOL, NULL);

    g_binary_symbol_set_range(result, range);
    g_binary_symbol_set_target_type(result, type);

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : a = premier symbole à analyser.                              *
*                b = second symbole à analyser.                               *
*                                                                             *
*  Description : Compare deux symboles d'exécutable selon leurs propriétés.   *
*                                                                             *
*  Retour      : Bilan de la comparaison : -1, 0 ou 1 (-1 par défaut).        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

int g_binary_symbol_cmp(const GBinSymbol * const *a, const GBinSymbol * const *b)
{
    int result;                             /* Bilan à retourner           */

    result = cmp_mrange(&(*a)->range, &(*b)->range);

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : symbol = symbole à analyser.                                 *
*                addr   = localisation à venir comparer à celle du symbole.   *
*                                                                             *
*  Description : Compare un symbole et une localisation.                      *
*                                                                             *
*  Retour      : Bilan de la comparaison : -1, 0 ou 1 (-1 par défaut).        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

int g_binary_symbol_cmp_with_vmpa(const GBinSymbol *symbol, const vmpa2t *addr)
{
    int result;                             /* Bilan à retourner           */

    result = cmp_mrange_with_vmpa(&symbol->range, addr);

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : symbol = symbole à mettre à jour.                            *
*                range  = plage mémoire ou physique déclarée.                 *
*                                                                             *
*  Description : Définit la couverture physique / en mémoire d'un symbole.    *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void g_binary_symbol_set_range(GBinSymbol *symbol, const mrange_t *range)
{
    copy_mrange(&symbol->range, range);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : symbol = symbole à venir consulter.                          *
*                                                                             *
*  Description : Fournit l'emplacement où se situe un symbole.                *
*                                                                             *
*  Retour      : Zone mémoire couverte par le symbole.                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

const mrange_t *g_binary_symbol_get_range(const GBinSymbol *symbol)
{
    return &symbol->range;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : symbol = symbole à venir modifier.                           *
*                type   = type de symbole représenté.                         *
*                                                                             *
*  Description : Définit le type du symbole.                                  *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void g_binary_symbol_set_target_type(GBinSymbol *symbol, SymbolType type)
{
    symbol->type = type;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : symbol = symbole à venir consulter.                          *
*                                                                             *
*  Description : Fournit le type du symbole.                                  *
*                                                                             *
*  Retour      : Type de symbole représenté.                                  *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

SymbolType g_binary_symbol_get_target_type(const GBinSymbol *symbol)
{
    return symbol->type;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : symbol = symbole à venir modifier.                           *
*                status = état de la visibilité du symbole représenté.        *
*                                                                             *
*  Description : Définit la visibilité du symbole.                            *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void g_binary_symbol_set_status(GBinSymbol *symbol, SymbolStatus status)
{
    symbol->status = status;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : symbol = symbole à venir consulter.                          *
*                                                                             *
*  Description : Fournit la visibilité du symbole.                            *
*                                                                             *
*  Retour      : Etat de la visibilité du symbole représenté.                 *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

SymbolStatus g_binary_symbol_get_status(const GBinSymbol *symbol)
{
    return symbol->status;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : symbol = symbole à venir consulter.                          *
*                                                                             *
*  Description : Fournit une étiquette pour viser un symbole.                 *
*                                                                             *
*  Retour      : Chaîne de caractères renvoyant au symbole.                   *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

const char *g_binary_symbol_get_label(const GBinSymbol *symbol)
{
    const char *result;                     /* Etiquette à retourner       */

    if (symbol->alt != NULL)
        result = symbol->alt;

    else if (G_BIN_SYMBOL_GET_CLASS(symbol)->get_label != NULL)
        result = G_BIN_SYMBOL_GET_CLASS(symbol)->get_label(symbol);

    else
        result = NULL;

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : symbol = symbole à venir consulter.                          *
*                alt    = désignation humaine alternative à favoriser.        *
*                                                                             *
*  Description : Définit un autre nom pour le symbole.                        *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void g_binary_symbol_set_alt_label(GBinSymbol *symbol, const char *alt)
{
    if (symbol->alt != NULL)
        free(symbol->alt);

    if (alt == NULL)
        symbol->alt = NULL;
    else
        symbol->alt = strdup(alt);

}



/* ---------------------------------------------------------------------------------- */
/*                          OFFRE DE CAPACITES DE GENERATION                          */
/* ---------------------------------------------------------------------------------- */


/******************************************************************************
*                                                                             *
*  Paramètres  : symbol = générateur à consulter pour futur usage.            *
*                                                                             *
*  Description : Détermine si un symbole pour faire office de générateur.     *
*                                                                             *
*  Retour      : Instance de générateur si les capacités sont là.             *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

GLineGenerator *g_binary_symbol_produce_label(GBinSymbol *symbol)
{
    GLineGenerator *result;                 /* Instance à retourner        */
    const char *label;                      /* Etiquette à insérer         */

    label = g_binary_symbol_get_label(symbol);

    result = (label != NULL ? G_LINE_GENERATOR(symbol) : NULL);

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : symbol = générateur à consulter.                             *
*                                                                             *
*  Description : Indique le nombre de ligne prêtes à être générées.           *
*                                                                             *
*  Retour      : Nombre de lignes devant apparaître au final.                 *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static size_t g_binary_symbol_count_lines(const GBinSymbol *symbol)
{
    return 1;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : symbol = générateur à consulter.                             *
*                x      = position géographique sur la ligne concernée.       *
*                addr   = position en mémoire à analyser.                     *
*                index  = indice de cette même ligne dans le tampon global.   *
*                repeat = indice d'utilisations successives du générateur.    *
*                                                                             *
*  Description : Retrouve l'emplacement correspondant à une position donnée.  *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void g_binary_symbol_compute_addr(const GBinSymbol *symbol, gint x, vmpa2t *addr, size_t index, size_t repeat)
{
    copy_vmpa(addr, get_mrange_addr(&symbol->range));

}


/******************************************************************************
*                                                                             *
*  Paramètres  : symbol = générateur à consulter.                             *
*                addr   = position en mémoire à analyser.                     *
*                index  = indice de cette même ligne dans le tampon global.   *
*                repeat = indice d'utilisations successives du générateur.    *
*                                                                             *
*  Description : Détermine si le conteneur s'inscrit dans une plage donnée.   *
*                                                                             *
*  Retour      : Bilan de la détermination, utilisable en comparaisons.       *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static int g_binary_symbol_contains_addr(const GBinSymbol *symbol, const vmpa2t *addr, size_t index, size_t repeat)
{
    int result;                             /* Conclusion à retourner      */

    /**
     * En tant que générateur, le symbole ne couvre qu'une ou plusieurs lignes
     * uniquement à son adresse de départ.
     *
     * On ne doit donc pas considérer l'ensemble de la taille du symbole en
     * utilisant par exemple un appel comme :
     *
     *    result = cmp_mrange_with_vmpa(&symbol->range, addr);
     *
     */

    result = cmp_vmpa(addr, get_mrange_addr(&symbol->range));

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : symbol = générateur à consulter.                             *
*                index  = indice de cette même ligne dans le tampon global.   *
*                repeat = indice d'utilisations successives du générateur.    *
*                                                                             *
*  Description : Renseigne sur les propriétés liées à un générateur.          *
*                                                                             *
*  Retour      : Propriétés particulières associées.                          *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static BufferLineFlags g_binary_symbol_get_flags(const GBinSymbol *symbol, size_t index, size_t repeat)
{
    return BLF_IS_LABEL;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : symbol  = générateur à utiliser pour l'impression.           *
*                line    = ligne de rendu à compléter.                        *
*                index   = indice de cette même ligne dans le tampon global.  *
*                repeat  = indice d'utilisations successives du générateur.   *
*                content = éventuel contenu binaire brut à imprimer.          *
*                                                                             *
*  Description : Imprime dans une ligne de rendu le contenu représenté.       *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_binary_symbol_print(GBinSymbol *symbol, GBufferLine *line, size_t index, size_t repeat, const GBinContent *content)
{
    const char *label;                      /* Etiquette à insérer         */

    g_buffer_line_fill_vmpa(line, get_mrange_addr(&symbol->range), MDS_32_BITS_UNSIGNED, MDS_32_BITS_UNSIGNED);

    label = g_binary_symbol_get_label(symbol);

    g_buffer_line_start_merge_at(line, BLC_ASSEMBLY_HEAD);
    g_buffer_line_append_text(line, BLC_ASSEMBLY_HEAD, SL(label), RTT_LABEL, NULL);
    g_buffer_line_append_text(line, BLC_ASSEMBLY_HEAD, ":", 1, RTT_PUNCT, NULL);

}