/* 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 .
 */
#include "spec.h"
#include 
#include 
#include 
#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      */
    operands_format *format;                /* Définition des opérandes    */
    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->format = create_operands_format();
    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_operands_format(spec->format);
    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)
{
    bool result;                            /* Bilan à renvoyer            */
    if (spec->prefix == NULL && prefix == NULL)
        result = true;
    else if (spec->prefix != NULL && prefix != NULL)
        result = strcmp(spec->prefix, prefix) == 0;
    else
        result = false;
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : spec = spécification d'encodage à consulter.                 *
*                                                                             *
*  Description : Fournit le gestionnaire des définitions d'opérandes.         *
*                                                                             *
*  Retour      : Structure assurant la définition des opérandes               *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
operands_format *get_format_in_encoding_spec(const encoding_spec *spec)
{
    return spec->format;
}
/******************************************************************************
*                                                                             *
*  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.                   *
*                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           */
    bool bad_exit;                          /* Ajout d'une sortie d'échec ?*/
    char *keyword;                          /* Mot clef appelable en code  */
    bool quick_exit;                        /* Inclusion de sortie rapide ?*/
    const char *new_ins;                    /* Nouvelle définition de nom  */
    result = true;
    bad_exit = false;
    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 &= mark_decoding_rules(spec->rules, spec->bits, spec->conversions);
    result &= declare_used_bits_fields(spec->bits, fd, wide);
    result &= declare_used_intermediate_conversions(spec->conversions, fd, spec->bits, pp, wide);
    result &= declare_syntax_items(spec->syntax, fd, spec->bits, spec->conversions, wide);
    dprintf(fd, "\n");
    result &= declare_hook_functions(spec->hooks, false, fd);
    /* 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);
    result &= define_used_intermediate_conversions(spec->conversions, fd, arch, spec->bits, pp, &bad_exit);
    /* Inclusion des éventuelles règles */
    quick_exit = false;
    result &= write_decoding_rules(spec->rules, false, CAT_SEE,
                                   fd, arch, subarch, spec->bits, spec->conversions, pp, &quick_exit);
    result &= write_decoding_rules(spec->rules, false, CAT_UNPREDICTABLE,
                                   fd, arch, subarch, spec->bits, spec->conversions, pp, &quick_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, false, fd);
    result &= write_decoding_rules(spec->rules, false, CAT_CHECKED_CALL,
                                   fd, arch, subarch, spec->bits, spec->conversions, pp, &quick_exit);
    result &= write_decoding_rules(spec->rules, false, CAT_CALL,
                                   fd, arch, subarch, spec->bits, spec->conversions, pp, &quick_exit);
    /* Création des opérandes */
    result &= define_syntax_items(spec->syntax, fd, arch, spec->bits, spec->conversions, pp, &bad_exit);
    /* Conclusion de la procédure */
    if (quick_exit)
    {
        dprintf(fd, "\t quick_exit:\n");
        dprintf(fd, "\n");
    }
    dprintf(fd, "\t\tg_arch_instruction_set_encoding(instr, \"%s\");\n", spec->prefix);
    dprintf(fd, "\n");
    dprintf(fd, "\t\treturn instr;\n");
    dprintf(fd, "\n");
    if (bad_exit)
    {
        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;
}
/******************************************************************************
*                                                                             *
*  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.   *
*                sep     = caractère de séparation avant les détails.         *
*                details = particularités de l'instruction.                   *
*                pp      = pré-processeur pour les échanges de chaînes.       *
*                prefix  = préfixe pour le type de définitions d'opérandes.   *
*                                                                             *
*  Description : Traduit en code une sous-fonction de désassemblage.          *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
bool write_encoding_spec_format_disass(const encoding_spec *spec, int fd, const char *arch, const char *subarch, const char *ins, char sep, const char *details, const pre_processor *pp, const char *prefix)
{
    bool result;                            /* Bilan à retourner           */
    bool quick_exit;                        /* Inclusion de sortie rapide ?*/
    bool bad_exit;                          /* Ajout d'une sortie d'échec ?*/
    const char *new_ins;                    /* Nouvelle définition de nom  */
    result = true;
    /* Déclarations préalables */
    dprintf(fd, "\tGArchInstruction *result;               /* Instruction créée à renvoyer*/\n");
    dprintf(fd, "\tSourceEndian endian;                    /* Boutisme lié au binaire     */\n");
    dprintf(fd, "\n");
    result &= declare_hook_functions(spec->hooks, true, fd);
    /* Création de l'instruction en elle-même */
    new_ins = get_new_keyword_from_syntax_items(spec->syntax);
    if (new_ins != NULL)
        dprintf(fd, "\tresult = g_%s_instruction_new(\"%s\");\n", arch, new_ins);
    else
    {
        if (sep == '\0')
            dprintf(fd, "\tresult = g_%s_instruction_new(\"%s\");\n", arch, ins);
        else
            dprintf(fd, "\tresult = g_%s_instruction_new(\"%s%c%s\");\n", arch, ins, sep, details);
    }
    dprintf(fd, "\n");
    /* Inscriptions des éventuelles fonctions ou propriété à lier */
    result &= write_hook_functions(spec->hooks, true, fd);
    quick_exit = false;
    result &= write_decoding_rules(spec->rules, true, CAT_CHECKED_CALL,
                                   fd, arch, subarch, spec->bits, spec->conversions, pp, &quick_exit);
    result &= write_decoding_rules(spec->rules, true, CAT_CALL,
                                   fd, arch, subarch, spec->bits, spec->conversions, pp, &quick_exit);
    /* Création des opérandes */
    dprintf(fd, "\tendian = g_arch_processor_get_endianness(G_ARCH_PROCESSOR(proc));\n");
    dprintf(fd, "\n");
    bad_exit = false;
    result &= define_operands_loading(spec->format, fd, arch, prefix, &bad_exit);
    /* Conclusion de la procédure */
    dprintf(fd, "\treturn result;\n");
    dprintf(fd, "\n");
    if (quick_exit || bad_exit)
    {
        if (quick_exit)
            dprintf(fd, " quick_exit:\n");
        if (bad_exit)
            dprintf(fd, " bad_exit:\n");
        dprintf(fd, "\n");
        dprintf(fd, "\tg_object_unref(G_OBJECT(result));\n");
        dprintf(fd, "\treturn NULL;\n");
        dprintf(fd, "\n");
    }
    return result;
}