/* Chrysalide - Outil d'analyse de fichiers binaires
 * output.h - prototypes pour l'impression des instructions désassemblées
 *
 * Copyright (C) 2010-2013 Cyrille Bagard
 *
 *  This file is part of Chrysalide.
 *
 *  Chrysalide 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.
 *
 *  Chrysalide 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 "output.h"
#include 
#include "../../arch/processor.h"
#include "../../common/extstr.h"
#include "../../decomp/lang/asm.h"
#include "../../format/format.h"
#include "../../gui/panels/log.h"
#define ROUTINE_INTRO_MSG "; --------------- BEGIN OF PROCEDURE ---------------"
#define ROUTINE_OUTRO_MSG "; ---------------- END OF PROCEDURE ----------------"
/******************************************************************************
*                                                                             *
*  Paramètres  : buffer   = tampon de récueil des résultats d'impression.     *
*                format   = format du binaire traité.                         *
*                instrs   = ensemble d'instructions à traiter.                *
*                routines = liste de routines intervenant dans le flot.       *
*                count    = quantité de ces routines.                         *
*                status   = barre de statut avec progression à mettre à jour. *
*                                                                             *
*  Description : Transcrit du code désassemblé en texte humainement lisible.  *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
void print_disassembled_instructions(GCodeBuffer *buffer, GExeFormat *format, GArchProcessor *proc, GtkStatusStack *status)
{
    GLangOutput *output;                    /* Modèle de sortie adéquat    */
    GPortionLayer *layer;                   /* Couche première de portions */
    GBinPortion **portions;                 /* Morceaux d'encadrement      */
    size_t portions_count;                  /* Taille de cette liste       */
    size_t portion_index;                   /* Prochaine portion à traiter */
    GBinSymbol **symbols;                   /* Symboles à représenter      */
    size_t sym_count;                       /* Qté de symboles présents    */
    size_t sym_index;                       /* Prochain symbole non traité */
    MemoryDataSize msize;                   /* Taille du bus d'adresses    */
    const GBinContent *content;             /* Contenu binaire global      */
    size_t count;                           /* Nombre total d'instructions */
    activity_id_t id;                       /* Identifiant de progression  */
    bool expect_outro;                      /* Fin de zone de code définie */
    size_t i;                               /* Boucle de parcours          */
    GArchInstruction *instr;                /* Instruction à traiter       */
    const vmpa2t *iaddr;                    /* Adresse d'instruction       */
    vmpa2t outro_addr;                      /* Adresse de fin de code      */
    GBufferLine *line;
    const vmpa2t *paddr;                    /* Adresse de portion          */
    const vmpa2t *saddr;                    /* Adresse de symbole          */
    int compared;                           /* Bilan d'une comparaison     */
    SymbolType stype;                       /* Type de symbole trouvé      */
    const char *label;                      /* Etiquette ciblant un symbole*/
    mrange_t range;                         /* Couverture sans surface     */
    GDbComment *comment;                    /* Commentaire à ajouter       */
    const char *text;
    char *prefixed;
    unsigned int _missing = 0;
    output = g_asm_output_new();
    layer = g_exe_format_get_main_layer(format);
    portions = g_portion_layer_collect_all_portions(layer, &portions_count);
    portion_index = 0;
    symbols = g_binary_format_get_symbols(G_BIN_FORMAT(format), &sym_count);
    sym_index = 0;
    msize = g_arch_processor_get_memory_size(proc);
    content = g_binary_format_get_content(G_BIN_FORMAT(format));
    count = g_arch_processor_count_disassembled_instructions(proc);
    id = gtk_status_stack_add_activity(status, _("Printing all disassebled parts..."), count);
    expect_outro = false;
    for (i = 0; i < count; i++)
    {
        instr = g_arch_processor_get_disassembled_instruction(proc, i);
        iaddr = get_mrange_addr(g_arch_instruction_get_range(instr));
        /* Fin d'une portion de code précédente ? */
        if (expect_outro && cmp_vmpa(iaddr, &outro_addr) >= 0)
        {
            expect_outro = false;
            init_mrange(&range, &outro_addr, 0);
            line = g_code_buffer_prepare_new_line(buffer, &range);
            g_buffer_line_add_flag(line, BLF_IS_LABEL);
            g_buffer_line_fill_mrange(line, msize, msize);
            g_code_buffer_append_new_line(buffer, line);
            line = g_code_buffer_prepare_new_line(buffer, &range);
            g_buffer_line_add_flag(line, BLF_IS_LABEL);
            g_buffer_line_fill_mrange(line, msize, msize);
            g_buffer_line_start_merge_at(line, BLC_ASSEMBLY_HEAD);
            g_buffer_line_append_text(line, BLC_ASSEMBLY_HEAD,
                                      ROUTINE_OUTRO_MSG, strlen(ROUTINE_OUTRO_MSG), RTT_COMMENT, NULL);
            g_code_buffer_append_new_line(buffer, line);
            line = g_code_buffer_prepare_new_line(buffer, &range);
            g_buffer_line_add_flag(line, BLF_IS_LABEL);
            g_buffer_line_fill_mrange(line, msize, msize);
            g_code_buffer_append_new_line(buffer, line);
        }
        /* Début d'une nouvelle portion ? */
        while (portion_index < portions_count)
        {
            paddr = get_mrange_addr(g_binary_portion_get_range(portions[portion_index]));
            if (cmp_vmpa_by_phy(iaddr, paddr) != 0)
                break;
            g_binary_portion_print(portions[portion_index], buffer, msize);
            portion_index++;
        }
        /* Début d'un nouveau symbole ? */
        if (sym_index < sym_count)
        {
            iaddr = get_mrange_addr(g_arch_instruction_get_range(instr));
            saddr = get_mrange_addr(g_binary_symbol_get_range(symbols[sym_index]));
            /* On écarte les symboles qu'on ne sait pas réintroduire */
            for (compared = cmp_vmpa(iaddr, saddr);
                 compared > 0;
                 compared = cmp_vmpa(iaddr, saddr))
            {
                log_variadic_message(LMT_BAD_BINARY,
                                     _("Unable to find a proper location for symbol '%s' @ 0x%08x"),
                                     g_binary_symbol_get_label(symbols[sym_index]), get_virt_addr(saddr));
                _missing++;
                if (++sym_index == sym_count)
                    goto no_more_symbol_finally;
                saddr = get_mrange_addr(g_binary_symbol_get_range(symbols[sym_index]));
            }
            if (compared == 0)
            {
                /* Coupure pour une nouvelle routine */
                stype = g_binary_symbol_get_target_type(symbols[sym_index]);
                if (stype == STP_ROUTINE || stype == STP_FUNCTION || stype == STP_ENTRY_POINT)
                {
                    /* Impression de la marque de début */
                    init_mrange(&range, get_mrange_addr(g_binary_symbol_get_range(symbols[sym_index])), 0);
                    line = g_code_buffer_prepare_new_line(buffer, &range);
                    g_buffer_line_add_flag(line, BLF_IS_LABEL);
                    g_buffer_line_fill_mrange(line, msize, msize);
                    g_code_buffer_append_new_line(buffer, line);
                    line = g_code_buffer_prepare_new_line(buffer, &range);
                    g_buffer_line_add_flag(line, BLF_IS_LABEL);
                    g_buffer_line_fill_mrange(line, msize, msize);
                    g_buffer_line_start_merge_at(line, BLC_ASSEMBLY_HEAD);
                    g_buffer_line_append_text(line, BLC_ASSEMBLY_HEAD,
                                              ROUTINE_INTRO_MSG, strlen(ROUTINE_INTRO_MSG), RTT_COMMENT, NULL);
                    g_code_buffer_append_new_line(buffer, line);
                    line = g_code_buffer_prepare_new_line(buffer, &range);
                    g_buffer_line_add_flag(line, BLF_IS_LABEL);
                    g_buffer_line_fill_mrange(line, msize, msize);
                    g_code_buffer_append_new_line(buffer, line);
                    /* Mémorisation de la fin */
                    compute_mrange_end_addr(g_binary_symbol_get_range(symbols[sym_index]), &outro_addr);
                    expect_outro = true;
                }
                /* Etiquette ? */
                label = g_binary_symbol_get_label(symbols[sym_index]);
                if (label != NULL)
                {
                    init_mrange(&range, get_mrange_addr(g_binary_symbol_get_range(symbols[sym_index])), 0);
                    line = g_code_buffer_prepare_new_line(buffer, &range);
                    g_buffer_line_add_flag(line, BLF_IS_LABEL);
                    g_buffer_line_fill_mrange(line, msize, msize);
                    g_buffer_line_start_merge_at(line, BLC_ASSEMBLY_HEAD);
                    g_buffer_line_append_text(line, BLC_ASSEMBLY_HEAD, label, strlen(label), RTT_LABEL, NULL);
                    g_buffer_line_append_text(line, BLC_ASSEMBLY_HEAD, ":", 1, RTT_PUNCT, NULL);
                    g_code_buffer_append_new_line(buffer, line);
                }
            }
        }
 no_more_symbol_finally:
        line = g_arch_instruction_print(instr, buffer, msize, content, ASX_INTEL);
        if (g_arch_instruction_get_flags(instr) & AIF_RETURN_POINT)
            g_buffer_line_add_flag(line, BLF_BOOKMARK);
        if (sym_index < sym_count)
        {
            iaddr = get_mrange_addr(g_arch_instruction_get_range(instr));
            saddr = get_mrange_addr(g_binary_symbol_get_range(symbols[sym_index]));
            if (cmp_vmpa(iaddr, saddr) == 0)
            {
                /* Point d'entrée ? */
                if (g_binary_symbol_get_target_type(symbols[sym_index]) == STP_ENTRY_POINT)
                    g_buffer_line_add_flag(line, BLF_ENTRYPOINT);
                /* Début d'un groupe bien cohérent avec les alignements ? */
                if (g_binary_symbol_is_block_start(symbols[sym_index]))
                    g_buffer_line_add_flag(line, BLF_WIDTH_MANAGER);
                /* Commentaire ? */
                comment = g_binary_symbol_get_comment(symbols[sym_index]);
                if (comment != NULL)
                {
                    /* FIXME : appliquer ! */
                    text = g_db_comment_get_text(comment);
                    prefixed = strdup("; ");
                    prefixed = stradd(prefixed, text);
                    g_buffer_line_append_text(line, BLC_COMMENTS, prefixed, strlen(prefixed), RTT_COMMENT, NULL);
                    free(prefixed);
                }
                sym_index++;
            }
        }
        g_code_buffer_append_new_line(buffer, line);
        gtk_status_stack_update_activity_value(status, id, 1);
    }
    gtk_status_stack_remove_activity(status, id);
    g_object_unref(G_OBJECT(content));
    if (portions != NULL)
        free(portions);
    g_object_unref(G_OBJECT(layer));
    g_object_unref(G_OBJECT(output));
    fprintf(stderr, "MISSING :: %u symbols\n", _missing);
}