/* OpenIDA - Outil d'analyse de fichiers binaires
 * immediate.c - opérandes représentant des valeurs numériques
 *
 * Copyright (C) 2009 Cyrille Bagard
 *
 *  This file is part of OpenIDA.
 *
 *  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 "immediate.h"
#include 
#include 
#include 
#include "operand-int.h"
#include "../common/extstr.h"
/* Définition d'un opérande de valeur numérique (instance) */
struct _GImmOperand
{
    GArchOperand parent;                    /* Instance parente            */
    AsmOperandSize size;                    /* Taille de l'opérande        */
    /**
     * Note : dans le cas d'une valeur signée,
     * signed_imm contient la valeur lue/donnée, et
     * unsigned_imm la valeur humainement lisible (ie. positive).
     */
    union
    {
        uint8_t val8;                       /* Valeur sur 8 bits           */
        uint16_t val16;                     /* Valeur sur 16 bits          */
        uint32_t val32;                     /* Valeur sur 32 bits          */
        uint64_t val64;                     /* Valeur sur 64 bits          */
    } unsigned_imm;
    union
    {
        int8_t val8;                        /* Valeur sur 8 bits           */
        int16_t val16;                      /* Valeur sur 16 bits          */
        int32_t val32;                      /* Valeur sur 32 bits          */
        int64_t val64;                      /* Valeur sur 64 bits          */
    } signed_imm;
};
/* Définition d'un opérande de valeur numérique (classe) */
struct _GImmOperandClass
{
    GArchOperandClass parent;               /* Classe parente              */
};
/* Initialise la classe des lignes de descriptions initiales. */
static void g_imm_operand_class_init(GImmOperandClass *);
/* Initialise la classe des lignes de descriptions initiales. */
static void g_imm_operand_init(GImmOperand *);
/* Traduit un opérande en version humainement lisible. */
static char *g_imm_operand_get_text(const GImmOperand *, const exe_format *, AsmSyntax);
/* Indique le type défini pour un opérande de valeur numérique. */
G_DEFINE_TYPE(GImmOperand, g_imm_operand, G_TYPE_ARCH_OPERAND);
/******************************************************************************
*                                                                             *
*  Paramètres  : klass = classe à initialiser.                                *
*                                                                             *
*  Description : Initialise la classe des lignes de descriptions initiales.   *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void g_imm_operand_class_init(GImmOperandClass *klass)
{
}
/******************************************************************************
*                                                                             *
*  Paramètres  : operand = instance à initialiser.                            *
*                                                                             *
*  Description : Initialise la classe des lignes de descriptions initiales.   *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void g_imm_operand_init(GImmOperand *operand)
{
    GArchOperand *parent;                   /* Instance parente            */
    parent = G_ARCH_OPERAND(operand);
    parent->get_text = (get_operand_text_fc)g_imm_operand_get_text;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : size   = taille de l'opérande souhaitée.                     *
*                data   = flux de données à analyser.                         *
*                pos    = position courante dans ce flux. [OUT]               *
*                len    = taille totale des données à analyser.               *
*                endian = ordre des bits dans la source.                      *
*                                                                             *
*  Description : Crée un opérande réprésentant une valeur numérique.          *
*                                                                             *
*  Retour      : Instruction mise en place.                                   *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
GArchOperand *g_imm_operand_new_from_data(MemoryDataSize size, const bin_t *data, off_t *pos, off_t len, SourceEndian endian)
{
    GImmOperand *result;                    /* Opérande à retourner        */
    result = g_object_new(G_TYPE_IMM_OPERAND, NULL);
    result->size = size;
    switch (size)
    {
        case AOS_8_BITS_UNSIGNED:
            if (!read_u8(&result->unsigned_imm.val8, data, pos, len, endian))
                goto gionfd_error;
            break;
        case AOS_16_BITS_UNSIGNED:
            if (!read_u16(&result->unsigned_imm.val16, data, pos, len, endian))
                goto gionfd_error;
            break;
        case AOS_32_BITS_UNSIGNED:
            if (!read_u32(&result->unsigned_imm.val32, data, pos, len, endian))
                goto gionfd_error;
            break;
        case AOS_8_BITS_SIGNED:
            if (!read_u8(&result->signed_imm.val8, data, pos, len, endian))
                goto gionfd_error;
            break;
        case AOS_16_BITS_SIGNED:
            if (!read_u16(&result->signed_imm.val16, data, pos, len, endian))
                goto gionfd_error;
            break;
        case AOS_32_BITS_SIGNED:
            if (!read_u32(&result->signed_imm.val32, data, pos, len, endian))
                goto gionfd_error;
            break;
    }
    return G_ARCH_OPERAND(result);
 gionfd_error:
    /* TODO : free */
    return NULL;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : size = taille de l'opérande souhaitée.                       *
*                ...  = valeur sur x bits à venir récupérer.                  *
*                                                                             *
*  Description : Crée un opérande réprésentant une valeur numérique.          *
*                                                                             *
*  Retour      : Instruction mise en place.                                   *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
GArchOperand *g_imm_operand_new_from_value(AsmOperandSize size, ...)
{
    GImmOperand *result;                    /* Opérande à retourner        */
    va_list ap;                             /* Liste des compléments       */
    uint8_t uval8;                          /* Valeur sur 8 bits           */
    uint16_t uval16;                        /* Valeur sur 16 bits          */
    uint32_t uval32;                        /* Valeur sur 32 bits          */
    uint64_t uval64;                        /* Valeur sur 64 bits          */
    int8_t sval8;                           /* Valeur sur 8 bits           */
    int16_t sval16;                         /* Valeur sur 16 bits          */
    int32_t sval32;                         /* Valeur sur 32 bits          */
    int64_t sval64;                         /* Valeur sur 64 bits          */
    if (size == AOS_UNDEFINED) return NULL;
    result = g_object_new(G_TYPE_IMM_OPERAND, NULL);
    result->size = size;
    va_start(ap, size);
    switch (size)
    {
        /* Pour GCC... */
        case AOS_UNDEFINED:
            break;
        case AOS_8_BITS_UNSIGNED:
            uval8 = (uint8_t)va_arg(ap, unsigned int);
            result->unsigned_imm.val8 = uval8;
            break;
        case AOS_16_BITS_UNSIGNED:
            uval16 = (uint16_t)va_arg(ap, unsigned int);
            result->unsigned_imm.val16 = uval16;
            break;
        case AOS_32_BITS_UNSIGNED:
            uval32 = (uint32_t)va_arg(ap, unsigned int);
            result->unsigned_imm.val32 = uval32;
            break;
        case AOS_64_BITS_UNSIGNED:
            uval64 = (uint64_t)va_arg(ap, unsigned int);
            result->unsigned_imm.val64 = uval64;
            break;
        case AOS_8_BITS_SIGNED:
            sval8 = (int8_t)va_arg(ap, int);
            result->signed_imm.val8 = sval8;
            break;
        case AOS_16_BITS_SIGNED:
            sval16 = (int16_t)va_arg(ap, int);
            result->signed_imm.val16 = sval16;
            break;
        case AOS_32_BITS_SIGNED:
            sval32 = (int32_t)va_arg(ap, int);
            result->signed_imm.val32 = sval32;
            break;
        case AOS_64_BITS_SIGNED:
            sval64 = (int64_t)va_arg(ap, int);
            result->signed_imm.val64 = sval64;
            break;
    }
    va_end(ap);
    return G_ARCH_OPERAND(result);
}
/******************************************************************************
*                                                                             *
*  Paramètres  : operand = structure dont le contenu est à définir.           *
*                                                                             *
*  Description : Indique le signe d'une valeur immédiate.                     *
*                                                                             *
*  Retour      : true si la valeur est strictement négative, false sinon.     *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
bool g_imm_operand_is_negative(const GImmOperand *operand)
{
    bool result;                            /* Bilan à renvoyer            */
    result = false;
    switch (operand->size)
    {
        case AOS_8_BITS_SIGNED:
            result = (operand->signed_imm.val8 & 0x80);
            break;
        case AOS_16_BITS_SIGNED:
            result = (operand->signed_imm.val16 & 0x8000);
            break;
        case AOS_32_BITS_SIGNED:
            result = (operand->signed_imm.val32 & 0x80000000);
            break;
        case AOS_64_BITS_SIGNED:
            result = (operand->signed_imm.val64 & 0x8000000000000000ll);
            break;
        default:
            /* Traitement non nécessaire */
            break;
    }
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : operand = opérande à traiter.                                *
*                format  = format du binaire manipulé.                        *
*                syntax  = type de représentation demandée.                   *
*                                                                             *
*  Description : Traduit un opérande en version humainement lisible.          *
*                                                                             *
*  Retour      : Chaîne de caractères à libérer de la mémoire.                *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static char *g_imm_operand_get_text(const GImmOperand *operand, const exe_format *format, AsmSyntax syntax)
{
    char *result;                           /* Chaîne à retourner          */
    char *label;                            /* Etiquette de symbole        */
    SymbolType symtype;                     /* Type de symbole             */
    vmpa_t address;                         /* Décallage final constaté    */
    char buffer[256];                       /* Complément d'information    */
    /* Valeur brute */
    result = (char *)calloc(19, sizeof(char));
    switch (syntax)
    {
        case ASX_INTEL:
            switch (operand->size)
            {
                case MDS_UNDEFINED:
                    snprintf(result, 19, "$0x???");
                    break;
                case AOS_8_BITS_UNSIGNED:
                    snprintf(result, 19, "0x%hhx", operand->unsigned_imm.val8);
                    break;
                case AOS_16_BITS_UNSIGNED:
                    snprintf(result, 19, "0x%hx", operand->unsigned_imm.val16);
                    break;
                case AOS_32_BITS_UNSIGNED:
                    snprintf(result, 19, "0x%x", operand->unsigned_imm.val32);
                    break;
                case AOS_64_BITS_UNSIGNED:
                    snprintf(result, 19, "0x%llx", operand->unsigned_imm.val64);
                    break;
                case AOS_8_BITS_SIGNED:
                    if (g_imm_operand_is_negative(operand))
                        snprintf(result, 19, "0x%hhx", ~operand->signed_imm.val8 + 1);
                    else
                        snprintf(result, 19, "0x%hhx", operand->signed_imm.val8);
                    break;
                case AOS_16_BITS_SIGNED:
                    if (g_imm_operand_is_negative(operand))
                        snprintf(result, 19, "0x%hx", ~operand->signed_imm.val16 + 1);
                    else
                        snprintf(result, 19, "0x%hx", operand->signed_imm.val16);
                    break;
                case AOS_32_BITS_SIGNED:
                    if (g_imm_operand_is_negative(operand))
                        snprintf(result, 19, "0x%x", ~operand->signed_imm.val32 + 1);
                    else
                        snprintf(result, 19, "0x%x", operand->signed_imm.val32);
                    break;
                case AOS_64_BITS_SIGNED:
                    if (g_imm_operand_is_negative(operand))
                        snprintf(result, 19, "0x%llx", ~operand->signed_imm.val64 + 1);
                    else
                        snprintf(result, 19, "0x%llx", operand->signed_imm.val64);
                    break;
            }
            break;
        case ASX_ATT:
            switch (operand->size)
            {
                case MDS_UNDEFINED:
                    snprintf(result, 19, "$0x???");
                    break;
                case AOS_8_BITS_UNSIGNED:
                    snprintf(result, 19, "$0x%hhx", operand->unsigned_imm.val8);
                    break;
                case AOS_16_BITS_UNSIGNED:
                    snprintf(result, 19, "$0x%hx", operand->unsigned_imm.val16);
                    break;
                case AOS_32_BITS_UNSIGNED:
                    snprintf(result, 19, "$0x%x", operand->unsigned_imm.val32);
                    break;
                case AOS_64_BITS_UNSIGNED:
                    snprintf(result, 19, "$0x%llx", operand->unsigned_imm.val64);
                    break;
                case AOS_8_BITS_SIGNED:
                    snprintf(result, 19, "$0x%hhx", ~operand->signed_imm.val8 + 1);
                    break;
                case AOS_16_BITS_SIGNED:
                    snprintf(result, 19, "$0x%hx", ~operand->signed_imm.val16 + 1);
                    break;
                case AOS_32_BITS_SIGNED:
                    snprintf(result, 19, "$0x%x", ~operand->signed_imm.val32 + 1);
                    break;
                case AOS_64_BITS_SIGNED:
                    snprintf(result, 19, "$0x%llx", ~operand->signed_imm.val64 + 1);
                    break;
            }
            break;
    }
    /* Complément d'information */
    if (operand->size == AOS_32_BITS_SIGNED || operand->size == AOS_32_BITS_UNSIGNED)   /* FIXME */
    {
        address = operand->unsigned_imm.val32; /* FIXME !!! */
        if (resolve_exe_symbol(format, &label, &symtype, &address))
        {
            switch (symtype)
            {
                case STP_SECTION:
                    if (address == 0) snprintf(buffer, 256, " <%s>", label);
                    else snprintf(buffer, 256, " <%s+0x%llx>", label, address);
                    result = stradd(result, buffer);
                    break;
                case STP_STRING:
                    label = escape_crlf(label);
                    label = strrpl(label, "<", "<");
                    label = strrpl(label, ">", ">");
                    snprintf(buffer, 256, " \"%s\"", label);
                    result = stradd(result, buffer);
                    break;
            }
            free(label);
        }
    }
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : operand = opérande à traiter.                                *
*                addr    = valeur résultante. [OUT]                           *
*                                                                             *
*  Description : Convertit une valeur immédiate en adresse de type vmpa_t.    *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
bool g_imm_operand_to_vmpa_t(const GImmOperand *operand, vmpa_t *addr)
{
    bool result;                            /* Bilan à renvoyer            */
    result = true;
    switch (operand->size)
    {
        case AOS_8_BITS_UNSIGNED:
            *addr = operand->unsigned_imm.val8;
            break;
        case AOS_16_BITS_UNSIGNED:
            *addr = operand->unsigned_imm.val16;
            break;
        case AOS_32_BITS_UNSIGNED:
            *addr = operand->unsigned_imm.val32;
            break;
        case AOS_64_BITS_UNSIGNED:
            *addr = operand->unsigned_imm.val64;
            break;
        default:
            result = false;
            break;
    }
    return result;
}