/* Chrysalide - Outil d'analyse de fichiers binaires
 * spec.c - représentation complète d'un encodage
 *
 * Copyright (C) 2014 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 <http://www.gnu.org/licenses/>.
 */


#include "spec.h"


#include <malloc.h>
#include <regex.h>
#include <string.h>


#include "helpers.h"



/* Mémorisation d'un encodage complet */
struct _encoding_spec
{
    char *prefix;                           /* Distinction principale      */
    char *lprefix;                          /* Distinction en minuscules   */
    unsigned int index;                     /* Distinction secondaire      */

    coding_bits *bits;                      /* Encodage des bits associés  */
    asm_syntax *syntax;                     /* Calligraphe d'assemblage    */
    conv_list *conversions;                 /* Conversions des données     */
    instr_hooks *hooks;                     /* Fonctions complémentaires   */
    decoding_rules *rules;                  /* Règles supplémentaires      */

};



/******************************************************************************
*                                                                             *
*  Paramètres  : -                                                            *
*                                                                             *
*  Description : Crée un nouveau suivi de l'encodage d'une instruction.       *
*                                                                             *
*  Retour      : Nouvelle structure prête à emploi.                           *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

encoding_spec *create_encoding_spec(void)
{
    encoding_spec *result;                  /* Définition vierge à renvoyer*/

    result = (encoding_spec *)calloc(1, sizeof(encoding_spec));

    result->bits = create_coding_bits();
    result->syntax = create_asm_syntax();
    result->conversions = create_conv_list();
    result->hooks = create_instr_hooks();
    result->rules = create_decoding_rules();

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : spec = spécification d'encodage à libérer de la mémoire.     *
*                                                                             *
*  Description : Supprime de la mémoire un suivi d'encodage d'une instruction.*
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void delete_encoding_spec(encoding_spec *spec)
{
    delete_coding_bits(spec->bits);
    delete_asm_syntax(spec->syntax);
    delete_conv_list(spec->conversions);
    delete_instr_hooks(spec->hooks);
    delete_decoding_rules(spec->rules);

    free(spec);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : spec   = spécification d'encodage à compléter.               *
*                prefix = distinction principale entre les définitions.       *
*                index  = distinction secondaire entre les définitions.       *
*                                                                             *
*  Description : Définit le nom de code d'une spécification d'encodage.       *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void define_encoding_spec_code_name(encoding_spec *spec, char *prefix, unsigned int index)
{
    spec->prefix = prefix;
    spec->lprefix = make_string_lower(strdup(prefix));
    spec->index = index;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : spec   = spécification d'encodage à consulter.               *
*                prefix = distinction principale entre les définitions.       *
*                                                                             *
*  Description : Indique si une spécification se range dans une catégorie.    *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool has_encoding_spec_prefix(const encoding_spec *spec, const char *prefix)
{
    return (strcmp(spec->prefix, prefix) == 0);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : spec = spécification d'encodage à consulter.                 *
*                                                                             *
*  Description : Fournit le gestionnaire des bits d'un encodage d'instruction.*
*                                                                             *
*  Retour      : Structure assurant le suivi des bits.                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

coding_bits *get_bits_in_encoding_spec(const encoding_spec *spec)
{
    return spec->bits;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : spec = spécification d'encodage à consulter.                 *
*                                                                             *
*  Description : Fournit l'indicateur des écritures correctes d'assembleur.   *
*                                                                             *
*  Retour      : Structure assurant la gestion des éléments de syntaxe.       *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

asm_syntax *get_syntax_in_encoding_spec(const encoding_spec *spec)
{
    return spec->syntax;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : spec = spécification d'encodage à consulter.                 *
*                                                                             *
*  Description : Fournit la liste des fonctions de conversion.                *
*                                                                             *
*  Retour      : Structure assurant la gestion des fonctions de conversion.   *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

conv_list *get_conversions_in_encoding_spec(const encoding_spec *spec)
{
    return spec->conversions;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : spec = spécification d'encodage à consulter.                 *
*                                                                             *
*  Description : Fournit la liste des fonctions à lier à une instruction.     *
*                                                                             *
*  Retour      : Structure assurant la gestion des fonctions de conversion.   *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

instr_hooks *get_hooks_in_encoding_spec(const encoding_spec *spec)
{
    return spec->hooks;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : spec = spécification d'encodage à consulter.                 *
*                                                                             *
*  Description : Fournit un ensemble de règles supplémentaires éventuel.      *
*                                                                             *
*  Retour      : Structure assurant la gestion de ces règles.                 *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

decoding_rules *get_rules_in_encoding_spec(const encoding_spec *spec)
{
    return spec->rules;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : spec    = spécification servant de base à l'opération.       *
*                fd      = descripteur d'un flux ouvert en écriture.          *
*                arch    = architecture visée par l'opération.                *
*                subarch = sous-catégorie de cette même architecture.         *
*                ins     = désignation première de l'instruction manipulée.   *
*                details = particularités de l'instruction.                   *
*                keyword = nom clef de l'instruction utilisable dans du code. *
*                wide    = taille des mots manipulés (en bits).               *
*                pp      = pré-processeur pour les échanges de chaînes.       *
*                                                                             *
*  Description : Traduit en code une sous-fonction de désassemblage.          *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool write_encoding_spec_disass(const encoding_spec *spec, int fd, const char *arch, const char *subarch, const char *ins, const char *details, unsigned int wide, const pre_processor *pp)
{
    bool result;                            /* Bilan à retourner           */
    char *keyword;                          /* Mot clef appelable en code  */
    bool exit;                              /* Inclusion de sortie rapide ?*/
    const char *new_ins;                    /* Nouvelle définition de nom  */

    result = true;

    keyword = make_callable(ins, false);

    dprintf(fd, "\tGArchInstruction *%s_decode_%s%s_%s%u(uint%u_t _raw)\n",
            arch, keyword, details, spec->lprefix, spec->index, wide);

    dprintf(fd, "\t{\n");

    dprintf(fd, "\t\tGArchInstruction *instr;\n");

    /* Déclaration des champs à retrouver */

    result &= mark_syntax_items(spec->syntax, spec->bits, spec->conversions);

    result &= declare_used_bits_fields(spec->bits, fd, wide);

    result &= declare_syntax_items(spec->syntax, fd, spec->bits, spec->conversions, wide);

    dprintf(fd, "\n");

    /* Vérification que le décodage est possible */

    result &= check_bits_correctness(spec->bits, fd);

    dprintf(fd, "\n");

    /* Définition des champs bruts */

    result &= define_used_bits_fields(spec->bits, fd);

    dprintf(fd, "\n");

    /* Inclusion des éventuelles règles */

    exit = false;

    result &= write_decoding_rules(spec->rules, CAT_SEE,
                                   fd, arch, subarch, spec->bits, spec->conversions, pp, &exit);

    result &= write_decoding_rules(spec->rules, CAT_UNPREDICTABLE,
                                   fd, arch, subarch, spec->bits, spec->conversions, pp, &exit);

    /* Création de l'instruction en elle-même */

    new_ins = get_new_keyword_from_syntax_items(spec->syntax);

    dprintf(fd, "\t\tinstr = g_%s_instruction_new(\"%s\");\n", arch, new_ins != NULL ? new_ins : ins);

    dprintf(fd, "\n");

    /* Inscriptions des éventuelles fonctions ou propriété à lier */

    result &= write_hook_functions(spec->hooks, fd);

    result &= write_decoding_rules(spec->rules, CAT_CALL,
                                   fd, arch, subarch, spec->bits, spec->conversions, pp, &exit);

    /* Création des opérandes */

    result &= define_syntax_items(spec->syntax, fd, arch, spec->bits, spec->conversions, pp);

    /* Conclusion de la procédure */

    if (exit)
    {
        dprintf(fd, "\t quick_exit:\n");
        dprintf(fd, "\n");
    }

    dprintf(fd, "\t\treturn instr;\n");

    dprintf(fd, "\n");

    dprintf(fd, "\t bad_exit:\n");

    dprintf(fd, "\n");

    dprintf(fd, "\t\tg_object_unref(G_OBJECT(instr));\n");
    dprintf(fd, "\t\treturn NULL;\n");

    dprintf(fd, "\n");

    dprintf(fd, "\t}\n");

    dprintf(fd, "\n");

    dprintf(fd, "\tif (result == NULL)\n");
    dprintf(fd, "\t\tresult = %s_decode_%s%s_%s%u(raw);\n",
            arch, keyword, details, spec->lprefix, spec->index);

    dprintf(fd, "\n");

    free(keyword);

    return result;

}