/* Chrysalide - Outil d'analyse de fichiers binaires
 * project.c - gestion d'un groupe de fichiers binaires
 *
 * Copyright (C) 2008-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 "project.h"
#include 
#include 
#include 
#include 
#include "../common/xml.h"
#include "../core/formats.h"
#include "../core/params.h"
#include "../glibext/signal.h"
#include "../gtkext/easygtk.h"
#include "../glibext/delayed-int.h"
#include "../gtkext/gtkblockview.h"
#include "../gtkext/gtkgraphview.h"
#include "../gtkext/gtksourceview.h"
#include "../gui/panels/log.h"
#include "../gui/panels/panel.h"
/* ------------------------- DEFINITION D'UN PROJET INTERNE ------------------------- */
/* Conservation d'un contenu chargé */
typedef struct _loaded_content
{
    GBinContent *content;                   /* Contenu binaire en place    */
    ProjectContentState state;              /* Renseigne le type de contenu*/
} loaded_content;
/* Conservation d'un binaire chargé */
typedef struct _loaded_binary
{
    GLoadedBinary *binary;                  /* Binaire en question         */
    GtkViewPanel *views[BVW_COUNT];         /* Composants pour l'affichage */
    GtkWidget *scrollwindows[BVW_COUNT];    /* Supports pour l'affichage   */
    GEditorItem *item;                      /* Support d'affichage final   */
} loaded_binary;
/* Projet d'étude regroupant les binaires analysés (instance) */
struct _GStudyProject
{
    GObject parent;                         /* A laisser en premier        */
    GObject *ref;                           /* Espace de référencement     */
    char *filename;                         /* Lieu d'enregistrement       */
    loaded_content *contents;               /* Contenus binaires chargés   */
    size_t contents_count;                  /* Nombre de ces contenus      */
    GMutex cnt_mutex;                       /* Modification de la liste    */
    loaded_binary **binaries;               /* Fichiers binaires associés  */
    size_t binaries_count;                  /* Nombre de ces fichiers      */
    GMutex bin_mutex;                       /* Modification de la liste    */
};
/* Projet d'étude regroupant les binaires analysés  (classe) */
struct _GStudyProjectClass
{
    GObjectClass parent;                    /* A laisser en premier        */
};
/* Initialise la classe des projets d'étude. */
static void g_study_project_class_init(GStudyProjectClass *);
/*Initialise une instance de projet d'étude. */
static void g_study_project_init(GStudyProject *);
/* Supprime de l'écran un projet en place. */
static void g_study_project_hide(const GStudyProject *);
/* ----------------------- AMORCE POUR CHARGEMENT DE CONTENUS ----------------------- */
/* Ensembles binaires à désassembler (instance) */
struct _GDelayedStudy
{
    GDelayedWork parent;                    /* A laisser en premier        */
    GStudyProject *project;                 /* Projet de rattachement      */
    GBinContent *content;                   /* Contenu binaire à traiter   */
    ProjectContentState state;              /* Renseigne le type de contenu*/
    bool only_preload;                      /* Enregistrement seulement ?  */
};
/* Ensembles binaires à désassembler (classe) */
struct _GDelayedStudyClass
{
    GDelayedWorkClass parent;               /* A laisser en premier        */
};
/* Initialise la classe des intégrations de binaires à étudier. */
static void g_delayed_study_class_init(GDelayedStudyClass *);
/* Initialise une intégration de binaire à étudier. */
static void g_delayed_study_init(GDelayedStudy *);
/* Supprime toutes les références externes. */
static void g_delayed_study_dispose(GDelayedStudy *);
/* Procède à la libération totale de la mémoire. */
static void g_delayed_study_finalize(GDelayedStudy *);
/* Prépare une intégration de binaire au projet courant. */
static void g_delayed_study_process(GDelayedStudy *, GtkExtStatusBar *);
/* ---------------------------------------------------------------------------------- */
/*                           DEFINITION D'UN PROJET INTERNE                           */
/* ---------------------------------------------------------------------------------- */
/* Indique le type défini pour un projet d'étude. */
G_DEFINE_TYPE(GStudyProject, g_study_project, G_TYPE_OBJECT);
/******************************************************************************
*                                                                             *
*  Paramètres  : klass = classe à initialiser.                                *
*                                                                             *
*  Description : Initialise la classe des projets d'étude.                    *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void g_study_project_class_init(GStudyProjectClass *klass)
{
}
/******************************************************************************
*                                                                             *
*  Paramètres  : project = instance à initialiser.                            *
*                                                                             *
*  Description : Initialise une instance de projet d'étude.                   *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void g_study_project_init(GStudyProject *project)
{
    g_mutex_init(&project->cnt_mutex);
    g_mutex_init(&project->bin_mutex);
}
/******************************************************************************
*                                                                             *
*  Paramètres  : -                                                            *
*                                                                             *
*  Description : Crée un nouveau projet vierge.                               *
*                                                                             *
*  Retour      : Instance mise en place.                                      *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
GStudyProject *g_study_project_new(GObject *ref)
{
    GStudyProject *result;                  /* Composant à retourner       */
    result = g_object_new(G_TYPE_STUDY_PROJECT, NULL);
    g_object_ref(ref);
    result->ref = ref;
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : filename = chemin d'accès au fichier à charger.              *
*                                                                             *
*  Description : Crée un projet à partir du contenu XML d'un fichier.         *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
GStudyProject *g_study_project_open(GObject *ref, const char *filename)
{
    GStudyProject *result;                  /* Adresse à retourner         */
    xmlDocPtr xdoc;                         /* Structure XML chargée       */
    xmlXPathContextPtr context;             /* Contexte pour les XPath     */
    unsigned int root_contents;             /* Quantité de contenus majeurs*/
    GAsyncQueue *sema;                      /* Sémaphore taisant son nom   */
    xmlXPathObjectPtr xobject;              /* Cible d'une recherche       */
    unsigned int i;                         /* Boucle de parcours          */
    size_t access_len;                      /* Taille d'un chemin interne  */
    char *access;                           /* Chemin pour une sous-config.*/
    GBinContent *content;                   /* Contenu binaire retrouvé    */
    long state;                             /* Etat de ce contenu binaire  */
    bool status;                            /* Bilan d'une lecture         */
    GDelayedStudy *dstudy;                  /* Etude complémentaire à mener*/
    GLoadedBinary *binary;                  /* Représentation à intégrer   */
    if (!open_xml_file(filename, &xdoc, &context)) return NULL;
    result = g_study_project_new(ref);
    result->filename = strdup(filename);
    /* Préparations aux traitements parallèles */
    root_contents = 0;
    sema = g_async_queue_new();
    void ack_content_processing(GDelayedStudy *dstudy, GAsyncQueue *aqueue)
    {
        g_async_queue_push(aqueue, GINT_TO_POINTER(1));
    }
    /* Chargement des contenus binaires attachés */
    xobject = get_node_xpath_object(context, "/ChrysalideProject/Contents/Content");
    for (i = 0; i < XPATH_OBJ_NODES_COUNT(xobject); i++)
    {
        access_len = strlen("/ChrysalideProject/Contents/Content[position()=") + SIZE_T_MAXLEN + strlen("]") + 1;
        access = calloc(access_len, sizeof(char));
        snprintf(access, access_len, "/ChrysalideProject/Contents/Content[position()=%u]", i + 1);
        content = g_binary_content_new_from_xml(context, access, filename);
        status = get_node_prop_long_value(context, access, "state", &state);
        free(access);
        if (content == NULL)
        {
            log_variadic_message(LMT_ERROR, _("Unable to load the binary content #%u ; skipping..."), i);
            continue;
        }
        if (!status)
        {
            log_variadic_message(LMT_ERROR, _("Bad state for content '%s' ; skipping..."),
                                 g_binary_content_describe(content, true));
            continue;
        }
        /* Le contenu peut être un conteneur ? */
        if (state == PCS_ROOT)
        {
            dstudy = g_delayed_study_new(result, content, state);
            g_signal_connect(dstudy, "work-completed", G_CALLBACK(ack_content_processing), sema);
            g_delayed_study_preload_only(dstudy);
            root_contents++;
            study_new_content(dstudy);
        }
    }
    if(xobject != NULL)
        xmlXPathFreeObject(xobject);
    /* Attente pour la réception de contenus supplémentaires éventuels */
    for (i = 0; i < root_contents; i++)
        g_async_queue_pop(sema);
    g_async_queue_unref(sema);
    /* Chargement des binaires analysés */
    xobject = get_node_xpath_object(context, "/ChrysalideProject/Binaries/Binary");
    for (i = 0; i < XPATH_OBJ_NODES_COUNT(xobject); i++)
    {
        access_len = strlen("/ChrysalideProject/Binaries/Binary[position()=") + SIZE_T_MAXLEN + strlen("]") + 1;
        access = calloc(access_len, sizeof(char));
        snprintf(access, access_len, "/ChrysalideProject/Binaries/Binary[position()=%u]", i + 1);
        binary = g_loaded_binary_new_from_xml(context, access, result);
        free(access);
        if (binary != NULL)
        {
            g_signal_connect_to_main(binary, "disassembly-done",
                                     G_CALLBACK(g_study_project_add_loaded_binary), result,
                                     g_cclosure_marshal_VOID__VOID);
            g_loaded_binary_analyse(binary);
        }
    }
    if(xobject != NULL)
        xmlXPathFreeObject(xobject);
    /* Fin du chargement */
    close_xml_file(xdoc, context);
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : project  = project à sauvegarder.                            *
*                filename = nom de fichier à utiliser ou NULL pour l'existant.*
*                                                                             *
*  Description : Procède à l'enregistrement d'un projet donné.                *
*                                                                             *
*  Retour      : true si l'enregistrement s'est déroule sans encombre.        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
bool g_study_project_save(GStudyProject *project, const char *filename)
{
    bool result;                            /* Bilan à retourner           */
    xmlDocPtr xdoc;                         /* Document XML à créer        */
    xmlXPathContextPtr context;             /* Contexte pour les recherches*/
    const char *final;                      /* Lieu d'enregistrement final */
    size_t i;                               /* Boucle de parcours          */
    size_t access_len;                      /* Taille d'un chemin interne  */
    char *access;                           /* Chemin pour une sous-config.*/
    result = create_new_xml_file(&xdoc, &context);
    result &= (ensure_node_exist(xdoc, context, "/ChrysalideProject") != NULL);
    final = filename != NULL ? filename : project->filename;
    /* Enregistrement des contenus binaires attachés */
    for (i = 0; i < project->contents_count && result; i++)
    {
        if (project->contents[i].state == PCS_INTERNAL) continue;
        access_len = strlen("/ChrysalideProject/Contents/Content[position()=") + SIZE_T_MAXLEN + strlen("]") + 1;
        access = calloc(access_len, sizeof(char));
        snprintf(access, access_len, "/ChrysalideProject/Contents/Content[position()=%zu]", i + 1);
        result = g_binary_content_save(project->contents[i].content, xdoc, context, access, final);
        if (result)
            result = add_long_attribute_to_node(xdoc, context, access, "state", project->contents[i].state);
        free(access);
    }
    /* Enregistrement des binaires analysés */
    for (i = 0; i < project->binaries_count && result; i++)
    {
        access_len = strlen("/ChrysalideProject/Binaries/Binary[position()=") + SIZE_T_MAXLEN + strlen("]") + 1;
        access = calloc(access_len, sizeof(char));
        snprintf(access, access_len, "/ChrysalideProject/Binaries/Binary[position()=%zu]", i + 1);
        result = g_loaded_binary_save(project->binaries[i]->binary, xdoc, context, access);
        free(access);
    }
    /* Sauvegarde finale */
    result &= save_xml_file(xdoc, final);
    if (result && filename != NULL)
    {
        if (project->filename != NULL) free(project->filename);
        project->filename = strdup(filename);
    }
    close_xml_file(xdoc, context);
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : project = project à consulter.                               *
*                                                                             *
*  Description : Indique le chemin du fichier destiné à la sauvegarde.        *
*                                                                             *
*  Retour      : Chemin de fichier pour l'enregistrement ou NULL si indéfini. *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
const char *g_study_project_get_filename(const GStudyProject *project)
{
    return project->filename;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : project = projet dont le contenu est à compléter.            *
*                content = contenu binaire à mémoriser pour le projet.        *
*                state   = état du contenu à conserver.                       *
*                                                                             *
*  Description : Assure l'intégration d'un contenu binaire dans un projet.    *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
void g_study_project_add_binary_content(GStudyProject *project, GBinContent *content, ProjectContentState state)
{
    loaded_content *new;                    /* Nouveau contenu à définir   */
    g_mutex_lock(&project->cnt_mutex);
    project->contents = (loaded_content *)realloc(project->contents,
                                                  ++project->contents_count * sizeof(loaded_content));
    new = &project->contents[project->contents_count - 1];
    g_object_ref(G_OBJECT(content));
    new->content = content;
    new->state = state;
    g_mutex_unlock(&project->cnt_mutex);
}
/******************************************************************************
*                                                                             *
*  Paramètres  : project = projet dont le contenu est à compléter.            *
*                hash    = empreinte du contenu à retrouver.                  *
*                                                                             *
*  Description : Recherche un contenu binaire du projet selon son empreinte.  *
*                                                                             *
*  Retour      : Contenu avec propriété transférée ou NULL en cas d'échec.    *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
GBinContent *g_study_project_find_binary_content_by_hash(GStudyProject *project, const char *hash)
{
    GBinContent *result;                    /* Trouvaille à retourner      */
    size_t i;                               /* Boucle de parcours          */
    GBinContent *iter;                      /* Contenu binaire analysé     */
    const gchar *other;                     /* Autre empreinte à comparer  */
    result = NULL;
    g_mutex_lock(&project->cnt_mutex);
    for (i = 0; i < project->contents_count && result == NULL; i++)
    {
        iter = project->contents[i].content;
        other = g_binary_content_get_cheksum(iter);
        if (strcmp(hash, other) == 0)
        {
            g_object_ref(G_OBJECT(iter));
            result = iter;
        }
    }
    g_mutex_unlock(&project->cnt_mutex);
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : binary  = élément binaire tout juste désassemblé.            *
*                project = projet dont le contenu est à compléter.            *
*                                                                             *
*  Description : Assure l'intégration d'un élément binaire dans un projet.    *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
void g_study_project_add_loaded_binary(GLoadedBinary *binary, GStudyProject *project)
{
    size_t index;                           /* Indice du nouveau binaire   */
    index = g_study_project_attach_binary(project, binary);
    gboolean scroll_for_the_first_time(GtkWidget *widget, GdkEvent *event, GLoadedBinary *binary)
    {
        GBinFormat *format;                 /* Format associé au binaire   */
        GBinSymbol *symbol;                 /* Point d'entrée trouvé       */
        const mrange_t *range;              /* Emplacement de ce point     */
        g_signal_handlers_disconnect_by_func(widget, G_CALLBACK(scroll_for_the_first_time), binary);
        format = G_BIN_FORMAT(g_loaded_binary_get_format(binary));
        /* TODO : se rabattre sur des symboles identifiés comme point d'entrée si on ne trouve pas les principaux ci dessous */
        if (g_binary_format_find_symbol_by_label(format, "main", &symbol)
            || g_binary_format_find_symbol_by_label(format, "_start", &symbol)
            || g_binary_format_find_symbol_by_label(format, "entry_point", &symbol))
        {
            range = g_binary_symbol_get_range(symbol);
            gtk_view_panel_scroll_to_address(GTK_VIEW_PANEL(widget), get_mrange_addr(range), SPT_CENTER);
        }
        return FALSE;
    }
    g_signal_connect(project->binaries[index]->views[BVW_BLOCK], "size-allocate",
                     G_CALLBACK(scroll_for_the_first_time), binary);
    g_panel_item_dock(G_PANEL_ITEM(project->binaries[index]->item));
}
/******************************************************************************
*                                                                             *
*  Paramètres  : project = project à effacer de la mémoire.                   *
*                binary  = fichier binaire à associer au projet actuel.       *
*                                                                             *
*  Description : Attache un fichier donné à un projet donné.                  *
*                                                                             *
*  Retour      : Emplacement de l'élément créé.                               *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
size_t g_study_project_attach_binary(GStudyProject *project, GLoadedBinary *binary)
{
    size_t result;                          /* Indice à retourner          */
    loaded_binary *loaded;                  /* Structure à renvoyer        */
    BinaryView i;                           /* Boucle de parcours          */
    GtkWidget *view;                        /* Affichage du binaire        */
    GtkWidget *scroll;                      /* Surface d'exposition        */
    const char *name;                       /* Titre associé au binaire    */
    const char *lname;                      /* Description du binaire      */
    loaded = (loaded_binary *)calloc(1, sizeof(loaded_binary));
    loaded->binary = binary;
    for (i = 0; i < BVW_COUNT; i++)
    {
        /* Préparation du support visuel */
        switch (i)
        {
            case BVW_BLOCK:
                view = gtk_block_view_new(/*MRD_BLOCK*/);
                break;
            case BVW_GRAPH:
                view = gtk_graph_view_new();
                break;
            case BVW_SOURCE:
                view = gtk_source_view_new();
                break;
            default: /* GCC ! */
                break;
        }
        gtk_widget_show(view);
        loaded->views[i] = GTK_VIEW_PANEL(view);
        gtk_view_panel_attach_binary(loaded->views[i], binary, i);
        /* Intégration finale dans un support défilant */
        scroll = qck_create_scrolled_window(NULL, NULL);
        gtk_container_add(GTK_CONTAINER(scroll), view);
        loaded->scrollwindows[i] = scroll;
    }
    /* Support graphique final */
    scroll = loaded->scrollwindows[BVW_BLOCK];
    name = g_loaded_binary_get_name(binary, false);
    lname = g_loaded_binary_get_name(binary, true);
    loaded->item = g_panel_item_new(project->ref, name, lname, scroll, "M");
    /* Enregistrement dans le projet */
    g_mutex_lock(&project->bin_mutex);
    project->binaries = (loaded_binary **)realloc(project->binaries,
                                                  ++project->binaries_count * sizeof(loaded_binary *));
    result = project->binaries_count - 1;
    project->binaries[result] = loaded;
    g_mutex_unlock(&project->bin_mutex);
    update_project_area(project);
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : project = project à effacer de la mémoire.                   *
*                binary  = fichier binaire à dissocier au projet actuel.      *
*                                                                             *
*  Description : Détache un fichier donné à un projet donné.                  *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
void g_study_project_detach_binary(GStudyProject *project, GLoadedBinary *binary)
{
    //GtkDockPanel *dpanel;                   /* Support de panneaux         */
    //GDockItem *ditem;                       /* Support d'affichage utilisé */
    size_t i;                               /* Boucle de parcours          */
    //dpanel = GTK_DOCK_PANEL(g_object_get_data(project->ref, "binpanel"));
    //ditem = gtk_dock_panel_get_item_from_binary(project, binary); FIXME !!
    //gtk_dock_panel_remove_item(dpanel, ditem);
    for (i = 0; i < project->binaries_count; i++)
        if (project->binaries[i]->binary == binary) break;
    if ((i + 1) < project->binaries_count)
        memmove(&project->binaries[i], &project->binaries[i + 1], (project->binaries_count - i - 1) * sizeof(loaded_binary *));
    project->binaries = (loaded_binary **)realloc(project->binaries,
                                                   --project->binaries_count * sizeof(loaded_binary *));
    update_project_area(project);
}
/******************************************************************************
*                                                                             *
*  Paramètres  : project = projet à consulter.                                *
*                binary  = binaire chargé, encadré et concerné.               *
*                kind    = type d'affichage requis.                           *
*                view    = afficheur effectif quelconque. [OUT]               *
*                                                                             *
*  Description : Fournit un support d'affichage donné pour un binaire chargé. *
*                                                                             *
*  Retour      : Composant GTK dédié à un affichage particulier.              *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
GtkWidget *g_study_project_get_view_for_binary(const GStudyProject *project, const GLoadedBinary *binary, BinaryView kind, GtkViewPanel **view)
{
    GtkWidget *result;                      /* Composant GTK à retourner   */
    size_t i;                               /* Boucle de parcours          */
    result = NULL;
    *view = NULL;
    for (i = 0; i < project->binaries_count; i++)
        if (project->binaries[i]->binary == binary)
        {
            result = project->binaries[i]->scrollwindows[kind];
            *view = project->binaries[i]->views[kind];
            break;
        }
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : project = projet dont le contenu est à afficher.             *
*                                                                             *
*  Description : Met en place un projet à l'écran.                            *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
void g_study_project_display(const GStudyProject *project)
{
    size_t i;                               /* Boucle de parcours          */
    for (i = 0; i < project->binaries_count; i++)
        g_panel_item_dock(G_PANEL_ITEM(project->binaries[i]->item));
}
/******************************************************************************
*                                                                             *
*  Paramètres  : project = projet dont le contenu est à cacher.               *
*                                                                             *
*  Description : Supprime de l'écran un projet en place.                      *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void g_study_project_hide(const GStudyProject *project)
{
    size_t i;                               /* Boucle de parcours          */
    for (i = 0; i < project->binaries_count; i++)
        g_panel_item_undock(G_PANEL_ITEM(project->binaries[i]->item));
}
/******************************************************************************
*                                                                             *
*  Paramètres  : project = projet dont le contenu est à afficher.             *
*                count = nombre de binaires pris en compte. [OUT]             *
*                                                                             *
*  Description : Fournit l'ensemble des binaires associés à un projet.        *
*                                                                             *
*  Retour      : Liste à libérer de la mémoire.                               *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
GLoadedBinary **g_study_project_get_binaries(const GStudyProject *project, size_t *count)
{
    GLoadedBinary **result;                 /* Tableau à retourner         */
    size_t i;                               /* Boucle de parcours          */
    *count = project->binaries_count;
    result = (GLoadedBinary **)calloc(*count, sizeof(GLoadedBinary *));
    for (i = 0; i < *count; i++)
    {
        result[i] = project->binaries[i]->binary;
        g_object_ref(G_OBJECT(result[i]));
    }
    return result;
}
/* ---------------------------------------------------------------------------------- */
/*                           GESTION GLOBALISEE DES PROJETS                           */
/* ---------------------------------------------------------------------------------- */
/******************************************************************************
*                                                                             *
*  Paramètres  : project = éventuel adresse à renvoyer désormais.             *
*                                                                             *
*  Description : Fournit l'adresse du projet courant.                         *
*                                                                             *
*  Retour      : Adresse du projet ouvert ou NULL si aucun (!).               *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
GStudyProject *_get_current_study_project(GStudyProject *project)
{
    static GStudyProject *result = NULL;    /* Adresse à retourner         */
    if (project != NULL)
    {
        if (result != NULL)
        {
            g_study_project_hide(result);
            g_object_unref(G_OBJECT(result));
        }
        result = project;
    }
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : -                                                            *
*                                                                             *
*  Description : Fournit le gestionnaire des projets connus.                  *
*                                                                             *
*  Retour      : Instance de gestion unique.                                  *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
GtkRecentManager *get_projects_manager(void)
{
    static GtkRecentManager *result = NULL; /* Singleton à retourner       */
    if (result == NULL)
    {
        result = gtk_recent_manager_get_default();
        //gtk_recent_manager_purge_items(result, NULL);
    }
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : project = projet à traiter.                                  *
*                                                                             *
*  Description : Place un projet au sommet de la pile des projets récents.    *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
void push_project_into_recent_list(const GStudyProject *project)
{
    GtkRecentManager *manager;              /* Gestionnaire global         */
    char *qualified;                        /* Chemin avec 'file://'       */
    GtkRecentData recent;                   /* Données complètes           */
    if (project->filename == NULL)
        return;
    /* Constitution de la liste des projets récents */
    /* Constitution de la liste des projets récents */
    manager = get_projects_manager();
    qualified = (char *)calloc(strlen("file://") + strlen(project->filename) + 1, sizeof(char));
    strcpy(qualified, "file://");
    strcat(qualified, project->filename);
    memset(&recent, 0, sizeof(GtkRecentData));
    recent.mime_type = "application/chrysalide.project";
    recent.app_name = "Chrysalide";
    recent.app_exec = "chrysalide %f";
    gtk_recent_manager_add_full(manager, qualified, &recent);
    free(qualified);
    /* Pour la prochaine ouverture du programme... */
    g_generic_config_set_value(get_main_configuration(), MPK_LAST_PROJECT, project->filename);
}
/* ---------------------------------------------------------------------------------- */
/*                         AMORCE POUR CHARGEMENT DE CONTENUS                         */
/* ---------------------------------------------------------------------------------- */
/* Indique le type défini pour les tâches de préparations d'étude. */
G_DEFINE_TYPE(GDelayedStudy, g_delayed_study, G_TYPE_DELAYED_WORK);
/******************************************************************************
*                                                                             *
*  Paramètres  : klass = classe à initialiser.                                *
*                                                                             *
*  Description : Initialise la classe des intégrations de binaires à étudier. *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void g_delayed_study_class_init(GDelayedStudyClass *klass)
{
    GObjectClass *object;                   /* Autre version de la classe  */
    GDelayedWorkClass *work;                /* Version en classe parente   */
    object = G_OBJECT_CLASS(klass);
    work = G_DELAYED_WORK_CLASS(klass);
    object->dispose = (GObjectFinalizeFunc/* ! */)g_delayed_study_dispose;
    object->finalize = (GObjectFinalizeFunc)g_delayed_study_finalize;
    work->run = (run_task_fc)g_delayed_study_process;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : dstudy = instance à initialiser.                             *
*                                                                             *
*  Description : Initialise une intégration de binaire à étudier.             *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void g_delayed_study_init(GDelayedStudy *dstudy)
{
    dstudy->only_preload = false;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : binary = instance d'objet GLib à traiter.                    *
*                                                                             *
*  Description : Supprime toutes les références externes.                     *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void g_delayed_study_dispose(GDelayedStudy *dstudy)
{
    g_object_unref(G_OBJECT(dstudy->project));
    g_object_unref(G_OBJECT(dstudy->content));
    G_OBJECT_CLASS(g_delayed_study_parent_class)->dispose(G_OBJECT(dstudy));
}
/******************************************************************************
*                                                                             *
*  Paramètres  : binary = instance d'objet GLib à traiter.                    *
*                                                                             *
*  Description : Procède à la libération totale de la mémoire.                *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void g_delayed_study_finalize(GDelayedStudy *dstudy)
{
    G_OBJECT_CLASS(g_delayed_study_parent_class)->finalize(G_OBJECT(dstudy));
}
/******************************************************************************
*                                                                             *
*  Paramètres  : project = projet dont le contenu est à compléter.            *
*                content = contenu binaire chargé à analyser.                 *
*                state   = état du contenu à conserver.                       *
*                                                                             *
*  Description : Crée une tâche d'intégration de contenu binaire.             *
*                                                                             *
*  Retour      : Tâche créée.                                                 *
*                                                                             *
*  Remarques   : L'appelant perd la propriété du contenu.                     *
*                                                                             *
******************************************************************************/
GDelayedStudy *g_delayed_study_new(GStudyProject *project, GBinContent *content, ProjectContentState state)
{
    GDelayedStudy *result;            /* Tâche à retourner           */
    result = g_object_new(G_TYPE_DELAYED_STUDY, NULL);
    g_object_ref(G_OBJECT(project));
    result->project = project;
    g_object_ref(G_OBJECT(content));
    result->content = content;
    result->state = state;
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : dstudy    = intégration à mener.                             *
*                statusbar = barre de statut à tenir informée.                *
*                                                                             *
*  Description : Prépare une intégration de binaire au projet courant.        *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
static void g_delayed_study_process(GDelayedStudy *dstudy, GtkExtStatusBar *statusbar)
{
    FormatMatchStatus status;               /* Statut d'une reconnaissance */
    char *target;                           /* Sous-traitance requise      */
    GLoadedBinary *binary;                  /* Représentation chargée      */
    status = find_matching_format(dstudy->content, NULL, &target);
    switch (status)
    {
        case FMS_MATCHED:
            if (dstudy->only_preload)
                g_study_project_add_binary_content(dstudy->project, dstudy->content, dstudy->state);
            else
            {
                binary = g_loaded_binary_new(dstudy->content);
                if (binary != NULL)
                {
                    g_study_project_add_binary_content(dstudy->project, dstudy->content, dstudy->state);
                    g_signal_connect_to_main(binary, "disassembly-done",
                                             G_CALLBACK(g_study_project_add_loaded_binary),
                                             dstudy->project, g_cclosure_marshal_VOID__VOID);
                    g_loaded_binary_analyse(binary);
                }
            }
            break;
        case FMS_FORWARDED:
            /**
             * L'émetteur de ce type de réponse a pour charge de
             * reprogrammer lui même l'analyse de nouveaux contenus.
             */
            log_variadic_message(LMT_PROCESS, _("binary '%s' contains other binaries..."),
                                 g_binary_content_describe(dstudy->content, true));
            if (dstudy->state == PCS_ROOT)
                g_study_project_add_binary_content(dstudy->project, dstudy->content, PCS_ROOT);
            break;
        default:
            /**
             * Les jeux sont faits pour le contenu binaire courant.
             */
            log_variadic_message(LMT_PROCESS, _("Unknown binary format for '%s'..."),
                                 g_binary_content_describe(dstudy->content, true));
            break;
    }
}
/******************************************************************************
*                                                                             *
*  Paramètres  : dstudy = tâche d'analyse de contenu pour projet à mener.     *
*                                                                             *
*  Description : Limite l'étude et l'intégration d'un contenu binaire.        *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
void g_delayed_study_preload_only(GDelayedStudy *dstudy)
{
    dstudy->only_preload = true;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : content = contenu binaire chargé à analyser.                 *
*                state   = état du contenu à conserver.                       *
*                                                                             *
*  Description : Programme l'étude et l'intégration d'un contenu binaire.     *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
void qck_study_new_content(GBinContent *content, ProjectContentState state)
{
    GDelayedStudy *dstudy;                  /* Etude à conduire            */
    dstudy = g_delayed_study_new(get_current_project(), content, state);
    study_new_content(dstudy);
}
/******************************************************************************
*                                                                             *
*  Paramètres  : content = contenu binaire chargé à analyser.                 *
*                state   = état du contenu à conserver.                       *
*                                                                             *
*  Description : Programme l'étude et l'intégration d'un contenu binaire.     *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
void study_new_content(GDelayedStudy *dstudy)
{
    GWorkQueue *queue;                      /* Gestionnaire de différés    */
    queue = get_work_queue();
    g_work_queue_schedule_work(queue, G_DELAYED_WORK(dstudy), DEFAULT_WORK_GROUP);
}