/* Chrysalide - Outil d'analyse de fichiers binaires
* browser.c - accès à des définitions Kaitai depuis ROST
*
* Copyright (C) 2023 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 Chrysalide. If not, see .
*/
#include "browser.h"
#include
#include
#include
#include "browser-int.h"
#include "../records/bits.h"
#include "../records/delayed.h"
#include "../records/item.h"
#include "../records/list.h"
/* ---------------------- PARCOURS DE CORRESPONDANCES ETABLIES ---------------------- */
/* Initialise la classe des parcours de correspondances Kaitai. */
static void g_kaitai_browser_class_init(GKaitaiBrowserClass *);
/* Initialise un parcours de correspondances Kaitai. */
static void g_kaitai_browser_init(GKaitaiBrowser *);
/* Supprime toutes les références externes. */
static void g_kaitai_browser_dispose(GKaitaiBrowser *);
/* Procède à la libération totale de la mémoire. */
static void g_kaitai_browser_finalize(GKaitaiBrowser *);
/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */
/* Indique le nom associé à une expression d'évaluation. */
static char *g_kaitai_browser_get_name(const GKaitaiBrowser *);
/* Lance une résolution d'élément à solliciter. */
static bool g_kaitai_browser_resolve(GKaitaiBrowser *, const char *, GScanContext *, GScanScope *, GScanRegisteredItem **);
/* Réduit une expression à une forme plus simple. */
static bool g_kaitai_browser_reduce(GKaitaiBrowser *, GScanContext *, GScanScope *, GScanExpression **);
/* Effectue une extraction d'élément à partir d'une série. */
static GObject *g_kaitai_browser_extract_at(GKaitaiBrowser *, const GScanExpression *, GScanContext *, GScanScope *);
/* ---------------------------------------------------------------------------------- */
/* PARCOURS DE CORRESPONDANCES ETABLIES */
/* ---------------------------------------------------------------------------------- */
/* Indique le type défini pour un parcours de correspondances Kaitai pour ROST. */
G_DEFINE_TYPE(GKaitaiBrowser, g_kaitai_browser, G_TYPE_SCAN_REGISTERED_ITEM);
/******************************************************************************
* *
* Paramètres : klass = classe à initialiser. *
* *
* Description : Initialise la classe des parcours de correspondances Kaitai. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_kaitai_browser_class_init(GKaitaiBrowserClass *klass)
{
GObjectClass *object; /* Autre version de la classe */
GScanRegisteredItemClass *registered; /* Version de classe parente */
object = G_OBJECT_CLASS(klass);
object->dispose = (GObjectFinalizeFunc/* ! */)g_kaitai_browser_dispose;
object->finalize = (GObjectFinalizeFunc)g_kaitai_browser_finalize;
registered = G_SCAN_REGISTERED_ITEM_CLASS(klass);
registered->get_name = (get_registered_item_name_fc)g_kaitai_browser_get_name;
registered->resolve = (resolve_registered_item_fc)g_kaitai_browser_resolve;
registered->reduce = (reduce_registered_item_fc)g_kaitai_browser_reduce;
registered->extract = (extract_registered_item_at)g_kaitai_browser_extract_at;
}
/******************************************************************************
* *
* Paramètres : browser = instance à initialiser. *
* *
* Description : Initialise un parcours de correspondances Kaitai. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_kaitai_browser_init(GKaitaiBrowser *browser)
{
browser->path = NULL;
browser->record = NULL;
}
/******************************************************************************
* *
* Paramètres : browser = instance d'objet GLib à traiter. *
* *
* Description : Supprime toutes les références externes. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_kaitai_browser_dispose(GKaitaiBrowser *browser)
{
g_clear_object(&browser->record);
G_OBJECT_CLASS(g_kaitai_browser_parent_class)->dispose(G_OBJECT(browser));
}
/******************************************************************************
* *
* Paramètres : browser = instance d'objet GLib à traiter. *
* *
* Description : Procède à la libération totale de la mémoire. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_kaitai_browser_finalize(GKaitaiBrowser *browser)
{
if (browser->path != NULL)
free(browser->path);
G_OBJECT_CLASS(g_kaitai_browser_parent_class)->finalize(G_OBJECT(browser));
}
/******************************************************************************
* *
* Paramètres : path = chemin vers l'enregistrement fourni. *
* record = correspondance racine à considérer. *
* *
* Description : Crée un nouveau parcours de correspondances Kaitai. *
* *
* Retour : Instance mise en place ou NULL en cas d'échec. *
* *
* Remarques : - *
* *
******************************************************************************/
GKaitaiBrowser *g_kaitai_browser_new(const char *path, GMatchRecord *record)
{
GKaitaiBrowser *result; /* Structure à retourner */
result = g_object_new(G_TYPE_KAITAI_BROWSER, NULL);
if (!g_kaitai_browser_create(result, path, record))
g_clear_object(&result);
return result;
}
/******************************************************************************
* *
* Paramètres : browser = encadrement d'un parcours de correspondances. *
* path = chemin vers l'enregistrement fourni. *
* record = correspondance racine à considérer. *
* *
* Description : Met en place un nouveau parcours de correspondances Kaitai. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_kaitai_browser_create(GKaitaiBrowser *browser, const char *path, GMatchRecord *record)
{
bool result; /* Bilan à retourner */
result = true;
if (path != NULL)
browser->path = strdup(path);
browser->record = record;
g_object_ref(G_OBJECT(browser->record));
return result;
}
/* ---------------------------------------------------------------------------------- */
/* IMPLEMENTATION DES FONCTIONS DE CLASSE */
/* ---------------------------------------------------------------------------------- */
/******************************************************************************
* *
* Paramètres : item = élément d'appel à consulter. *
* *
* Description : Indique le nom associé à une expression d'évaluation. *
* *
* Retour : Désignation humaine de l'expression d'évaluation. *
* *
* Remarques : - *
* *
******************************************************************************/
static char *g_kaitai_browser_get_name(const GKaitaiBrowser *item)
{
char *result; /* Désignation à retourner */
int ret; /* Statut de construction */
if (item->path == NULL)
result = strdup("kaitai://");
else
{
ret = asprintf(&result, "kaitai://%s", item->path);
assert(ret > 0);
if (ret <= 0)
result = strdup("kaitai://???");
}
return result;
}
/******************************************************************************
* *
* Paramètres : item = élément d'appel à consulter. *
* target = désignation de l'objet d'appel à identifier. *
* ctx = contexte de suivi de l'analyse courante. *
* scope = portée courante des variables locales. *
* out = zone d'enregistrement de la résolution opérée. [OUT]*
* *
* Description : Lance une résolution d'élément à solliciter. *
* *
* Retour : Bilan de l'opération : false en cas d'erreur irrécupérable. *
* *
* Remarques : - *
* *
******************************************************************************/
static bool g_kaitai_browser_resolve(GKaitaiBrowser *item, const char *target, GScanContext *ctx, GScanScope *scope, GScanRegisteredItem **out)
{
bool result; /* Bilan à retourner */
GMatchRecord *found; /* Correspondance trouvée */
char *path;
int ret; /* Statut de construction */
found = g_match_record_find_by_name(item->record, target, strlen(target), 1);
result = (found != NULL);
if (result)
{
ret = asprintf(&path, "%s.%s", item->path, target);
assert(ret > 0);
if (ret <= 0)
path = strdup("!?");
*out = G_SCAN_REGISTERED_ITEM(g_kaitai_browser_new(path, found));
free(path);
g_object_unref(G_OBJECT(found));
}
return result;
}
/******************************************************************************
* *
* Paramètres : item = élément d'appel à consulter. *
* ctx = contexte de suivi de l'analyse courante. *
* scope = portée courante des variables locales. *
* out = zone d'enregistrement de la réduction opérée. [OUT] *
* *
* Description : Réduit une expression à une forme plus simple. *
* *
* Retour : Bilan de l'opération : false en cas d'erreur irrécupérable. *
* *
* Remarques : - *
* *
******************************************************************************/
static bool g_kaitai_browser_reduce(GKaitaiBrowser *item, GScanContext *ctx, GScanScope *scope, GScanExpression **out)
{
bool result; /* Bilan à retourner */
size_t count; /* Décompte total à considérer */
resolved_value_t value; /* Valeur brute à transformer */
if (G_IS_RECORD_LIST(item->record))
{
count = g_record_list_count_records(G_RECORD_LIST(item->record));
*out = g_scan_literal_expression_new(LVT_BOOLEAN, (bool []){ count > 0 });
result = true;
}
else
{
if (G_IS_RECORD_BIT_FIELD(item->record))
result = g_record_bit_field_get_value(G_RECORD_BIT_FIELD(item->record), &value);
else if (G_IS_RECORD_DELAYED(item->record))
result = g_record_delayed_compute_and_aggregate_value(G_RECORD_DELAYED(item->record), &value);
else if (G_IS_RECORD_ITEM(item->record))
result = g_record_item_get_value(G_RECORD_ITEM(item->record), &value);
else
result = false;
if (result)
{
switch (value.type)
{
case GVT_UNSIGNED_INTEGER:
*out = g_scan_literal_expression_new(LVT_UNSIGNED_INTEGER, &value.unsigned_integer);
break;
case GVT_SIGNED_INTEGER:
*out = g_scan_literal_expression_new(LVT_SIGNED_INTEGER, &value.signed_integer);
break;
case GVT_FLOAT:
/* TODO */
break;
case GVT_BOOLEAN:
*out = g_scan_literal_expression_new(LVT_BOOLEAN, &value.status);
break;
case GVT_BYTES:
*out = g_scan_literal_expression_new(LVT_STRING, &value.bytes);
break;
default:
break;
}
EXIT_RESOLVED_VALUE(value);
}
}
return result;
}
/******************************************************************************
* *
* Paramètres : item = élément d'appel à consulter. *
* index = indice de l'élément à cibler. *
* ctx = contexte de suivi de l'analyse courante. *
* scope = portée courante des variables locales. *
* *
* Description : Effectue une extraction d'élément à partir d'une série. *
* *
* Retour : Elément de série obtenu ou NULL si erreur irrécupérable. *
* *
* Remarques : - *
* *
******************************************************************************/
static GObject *g_kaitai_browser_extract_at(GKaitaiBrowser *item, const GScanExpression *index, GScanContext *ctx, GScanScope *scope)
{
GObject *result; /* Elément récupéré à renvoyer */
GScanLiteralExpression *literal; /* Accès direct à l'indice */
LiteralValueType vtype; /* Type de valeur portée */
unsigned long long at; /* Valeur concrète du point */
bool status; /* Bilan d'obtention d'indice */
GRecordList *list; /* Accès direct à la liste */
size_t count; /* Décompte total à considérer */
GMatchRecord *found; /* Correspondance trouvée */
char *path;
int ret; /* Statut de construction */
result = NULL;
/* Validations préliminaires */
if (!G_IS_RECORD_LIST(item->record)) goto exit;
if (!G_IS_SCAN_LITERAL_EXPRESSION(index)) goto exit;
literal = G_SCAN_LITERAL_EXPRESSION(index);
vtype = g_scan_literal_expression_get_value_type(literal);
if (vtype != LVT_UNSIGNED_INTEGER) goto exit;
status = g_scan_literal_expression_get_unsigned_integer_value(literal, &at);
if (!status) goto exit;
list = G_RECORD_LIST(item->record);
count = g_record_list_count_records(list);
if (at >= count) goto exit;
/* Récupération de l'élément visé */
found = g_record_list_get_record(list, at);
if (found == NULL) goto exit;
ret = asprintf(&path, "%s[%llu]", item->path, at);
assert(ret > 0);
if (ret <= 0)
path = strdup("!?");
result = G_OBJECT(g_kaitai_browser_new(path, found));
free(path);
g_object_unref(G_OBJECT(found));
exit:
return result;
}