/* Chrysalide - Outil d'analyse de fichiers binaires
 * formats.c - enregistrement et fourniture des formats de binaires supportés
 *
 * Copyright (C) 2015 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 "formats.h"
#include 
#include 
#include 
#include "../format/elf/elf.h"
/* Caractéristiques d'un processeur */
typedef struct _format_t
{
    char *key;                              /* Clef pour un accès rapide   */
    char *name;                             /* Désignation humaine         */
    format_match_fc is_matching;            /* Recherche de correspondance */
    format_load_fc load;                    /* Procédure de chargement     */
} format_t;
/* Mémorisation des types de formats enregistrés */
static format_t *_formats_definitions = NULL;
static size_t _formats_definitions_count = 0;
/* Verrou pour des accès atomiques */
/* ... */
/* Retrouve l'enregistrement correspondant à une architecture. */
static format_t *find_format_by_key(const char *);
/******************************************************************************
*                                                                             *
*  Paramètres  : key         = désignation rapide et interne d'un format.     *
*                name        = désignation humaine au format de binaire.      *
*                is_matching = procédure de détection associée.               *
*                load        = procédure de chargement associée.              *
*                                                                             *
*  Description : Enregistre un format de contenu binaire donné.               *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
bool register_format_type(const char *key, const char *name, format_match_fc is_matching, format_load_fc load)
{
    format_t *new;                          /* Nouvel élément à définir    */
    /* TODO : if find()... -> unref(), ret false */
    /* TODO : lock */
    _formats_definitions = (format_t *)realloc(_formats_definitions,
                                             ++_formats_definitions_count * sizeof(format_t));
    new = &_formats_definitions[_formats_definitions_count - 1];
    new->key = strdup(key);
    new->name = strdup(name);
    new->is_matching = is_matching;
    new->load = load;
    /* TODO : unlock */
    return true;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : -                                                            *
*                                                                             *
*  Description : Charge les définitions de formats "natifs".                  *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
bool load_hard_coded_formats_definitions(void)
{
    bool result;                            /* Bilan à retourner           */
    result = true;
    result &= register_format_type("elf", "Executable and Linkable Format", elf_is_matching, g_elf_format_new);
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : -                                                            *
*                                                                             *
*  Description : Décharge toutes les définitions de formats.                  *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
void unload_formats_definitions(void)
{
    size_t i;                               /* Boucle de parcours          */
    for (i = 0; i < _formats_definitions_count; i++)
    {
        free(_formats_definitions[i].key);
        free(_formats_definitions[i].name);
    }
    if (_formats_definitions != NULL)
        free(_formats_definitions);
    _formats_definitions = NULL;
    _formats_definitions_count = 0;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : key = nom technique du processeur recherché.                 *
*                                                                             *
*  Description : Retrouve l'enregistrement correspondant à une architecture.  *
*                                                                             *
*  Retour      : Définition trouvée ou NULL en cas d'échec.                   *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static format_t *find_format_by_key(const char *key)
{
    format_t *result;                       /* Trouvaille à retourner      */
    size_t i;                               /* Boucle de parcours          */
    /**
     * Le verrou d'accès global doit être posé !
     */
    result = NULL;
    if (key != NULL)
        for (i = 0; i < _formats_definitions_count; i++)
            if (strcmp(_formats_definitions[i].key, key) == 0)
                result = &_formats_definitions[i];
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : key = nom technique du format recherché.                     *
*                                                                             *
*  Description : Fournit le nom humain du format binaire visé.                *
*                                                                             *
*  Retour      : Désignation humaine trouvée ou NULL.                         *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
const char *get_binary_format_name(const char *key)
{
    const char *result;                     /* Description à retourner     */
    format_t *def;                          /* Définition d'architecture   */
    /* TODO : lock */
    def = find_format_by_key(key);
    if (def == NULL)
        result = NULL;
    else
        result = def->name;
    /* TODO : unlock */
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : content = contenu binaire à parcourir.                       *
*                                                                             *
*  Description : Identifie un format binaire par son contenu.                 *
*                                                                             *
*  Retour      : Identifiant du format binaire trouvé ou NULL si échec.       *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
const char *find_matching_format(GBinContent *content)
{
    const char *result;                     /* Identifiant à retourner     */
    size_t i;                               /* Boucle de parcours          */
    format_t *def;                          /* Définition d'architecture   */
    result = NULL;
    /* TODO : lock */
    for (i = 0; i < _formats_definitions_count && result == NULL; i++)
    {
        def = &_formats_definitions[i];
        if (def->is_matching(content))
            result = def->key;
    }
    /* TODO : unlock */
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : key     = nom technique du processeur recherché.             *
*                content = contenu binaire pré-chargé à traiter.              *
*                                                                             *
*  Description : Charge le format binaire correspondant à un type.            *
*                                                                             *
*  Retour      : Format binaire instancié.                                    *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
GBinFormat *load_new_named_format(const char *key, GBinContent *content)
{
    GBinFormat *result;                     /* Instance à retourner        */
    format_t *def;                          /* Définition d'architecture   */
    /* TODO : lock */
    def = find_format_by_key(key);
    if (def == NULL)
        result = NULL;
    else
        result = def->load(content);
    /* TODO : unlock */
    return result;
}