/* Chrysalide - Outil d'analyse de fichiers binaires
 * bits.c - compréhension et manipulation des champs de bits
 *
 * 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 "bits.h"


#include <assert.h>
#include <inttypes.h>
#include <malloc.h>
#include <stdint.h>
#include <string.h>


#include "helpers.h"





/* --------------------------- GESTION DES CHAMPS DE BITS --------------------------- */


/* Elément d'un mot décodé */
struct _raw_bitfield
{
    char *name;                             /* Désignation humaine         */
    unsigned int start;                     /* Position de départ          */
    unsigned int length;                    /* Taille du champ             */

    bool used;                              /* Champ défini & utilisé      */

};




/* Représentation de l'ensemble des bits de codage */
struct _coding_bits
{
    raw_bitfield *fields;                   /* Champs de bits détectés     */
    size_t bf_count;                        /* Nombre de ces champs        */
    uint64_t bits;                          /* Bits invariables            */
    uint64_t mask;                          /* Emplacement de ces bits     */
    unsigned int curpos;                    /* Position pendant l'analyse  */

};














/******************************************************************************
*                                                                             *
*  Paramètres  : field = champ de bits à consulter.                           *
*                                                                             *
*  Description : Indique le nombre de bits utilisés par le champ.             *
*                                                                             *
*  Retour      : Nombre de bits considérés.                                   *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

unsigned int get_raw_bitfield_length(const raw_bitfield *field)
{
    return field->length;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : field = champ de bits à traiter.                             *
*                                                                             *
*  Description : Marque un champ de bits comme étant utile.                   *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void mark_raw_bitfield_as_used(raw_bitfield *field)
{
    field->used = true;

}











/******************************************************************************
*                                                                             *
*  Paramètres  : -                                                            *
*                                                                             *
*  Description : Crée un nouveau gestionnaire des bits d'encodage brut.       *
*                                                                             *
*  Retour      : Nouvelle structure prête à emploi.                           *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

coding_bits *create_coding_bits(void)
{
    coding_bits *result;                    /* Définition vierge à renvoyer*/

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

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : bits = gestionnaire d'un ensemble de bits à libérer.         *
*                                                                             *
*  Description : Supprime de la mémoire un gestionnaire de bits d'encodage.   *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void delete_coding_bits(coding_bits *bits)
{
    size_t i;                               /* Boucle de parcours          */

    for (i = 0; i < bits->bf_count; i++)
        free(bits->fields[i].name);

    if (bits->fields != NULL)
        free(bits->fields);

    free(bits);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : bits   = gestionnaire de bits d'encodage brut à consulter.   *
*                name   = désignation humaine du champ remarqué.              *
*                length = taille du champ à mémoriser.                        *
*                                                                             *
*  Description : Note la présence d'un champ remarquable dans une définition. *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void register_named_field_in_bits(coding_bits *bits, char *name, unsigned int length)
{
    raw_bitfield *field;                    /* Nouveau champ à constituer  */

    assert((bits->curpos + length) < 64);

    bits->fields = (raw_bitfield *)realloc(bits->fields,
                                              ++bits->bf_count * sizeof(raw_bitfield));

    field = &bits->fields[bits->bf_count - 1];

    field->name = make_string_lower(name);
    field->start = bits->curpos;
    field->length = length;

    bits->curpos += length;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : bits = gestionnaire de bits d'encodage brut à consulter.     *
*                val  = valeur du bit à prendre en compte.                    *
*                                                                             *
*  Description : Note la présence d'un bit invariable dans une définition.    *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void register_bit_in_bits(coding_bits *bits, int val)
{
    assert(bits->curpos < 64);

    bits->bits |= (val ? 1 : 0) << bits->curpos;
    bits->mask |= 1 << bits->curpos;

    bits->curpos++;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : bits = gestionnaire de bits d'encodage brut à consulter.     *
*                                                                             *
*  Description : Indique le nombre de bits traités.                           *
*                                                                             *
*  Retour      : Quantité, positive ou nulle.                                 *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

unsigned int count_coded_bits(const coding_bits *bits)
{
    return bits->curpos;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : bits = gestionnaire d'encodage brut à consulter.             *
*                name = désignation humaine du champ à retrouver.             *
*                                                                             *
*  Description : Recherche un champ donné dans un ensemble de champs de bits. *
*                                                                             *
*  Retour      : Structure associée au champ trouvé ou NULL en cas d'échec.   *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

raw_bitfield *find_named_field_in_bits(const coding_bits *bits, const char *name)
{
    raw_bitfield *result;                   /* Champ de bits à retourner   */
    size_t i;                               /* Boucle de parcours          */

    result = NULL;

    for (i = 0; i < bits->bf_count && result == NULL; i++)
        if (strcmp(bits->fields[i].name, name) == 0)
            result = &bits->fields[i];

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : spec    = spécification servant de base à l'opération.       *
*                fd      = descripteur d'un flux ouvert en écriture.          *
*                wide    = taille des mots manipulés (en bits).               *
*                                                                             *
*  Description : Déclare les variables C associées aux champs de bits.        *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool declare_used_bits_fields(const coding_bits *bits, int fd, unsigned int wide)
{
    size_t i;                               /* Boucle de parcours          */

    for (i = 0; i < bits->bf_count; i++)
        if (bits->fields[i].used)
            dprintf(fd, "\t\tuint%u_t raw_%s;\n", wide, bits->fields[i].name);

    return true;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : spec    = spécification servant de base à l'opération.       *
*                fd      = descripteur d'un flux ouvert en écriture.          *
*                                                                             *
*  Description : Vérifie que les bits fixes correspondent au masque attendu.  *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool check_bits_correctness(const coding_bits *bits, int fd)
{
    switch (bits->curpos)
    {
        case 8:
            dprintf(fd, "\t\tif ((raw & 0x%" PRIx8 ") != 0x%" PRIx8 ") return NULL;\n",
                    (uint8_t)bits->mask, (uint8_t)bits->bits);
            break;

        case 16:
            dprintf(fd, "\t\tif ((raw & 0x%" PRIx16 ") != 0x%" PRIx16 ") return NULL;\n",
                    (uint16_t)bits->mask, (uint16_t)bits->bits);
            break;

        case 32:
            dprintf(fd, "\t\tif ((raw & 0x%" PRIx32 ") != 0x%" PRIx32 ") return NULL;\n",
                    (uint32_t)bits->mask, (uint32_t)bits->bits);
            break;

        case 64:
            dprintf(fd, "\t\tif ((raw & 0x%" PRIx64 ") != 0x%" PRIx64 ") return NULL;\n",
                    bits->mask, bits->bits);
            break;

    }

    return true;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : spec    = spécification servant de base à l'opération.       *
*                fd      = descripteur d'un flux ouvert en écriture.          *
*                                                                             *
*  Description : Définit les variables C associées aux champs de bits.        *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool define_used_bits_fields(const coding_bits *bits, int fd)
{
    size_t i;                               /* Boucle de parcours          */
    raw_bitfield *rf;                       /* Accès confortable à un champ*/

    for (i = 0; i < bits->bf_count; i++)
    {
        rf = &bits->fields[i];
        if (!rf->used) continue;

        dprintf(fd, "\t\traw_%s = (_raw >> %u) & 0x%llx;\n", rf->name, rf->start, (1ull << rf->length) - 1);

    }

    return true;

}