summaryrefslogtreecommitdiff
path: root/plugins/kaitai/parsers/struct.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/kaitai/parsers/struct.c')
-rw-r--r--plugins/kaitai/parsers/struct.c837
1 files changed, 837 insertions, 0 deletions
diff --git a/plugins/kaitai/parsers/struct.c b/plugins/kaitai/parsers/struct.c
new file mode 100644
index 0000000..d447cf3
--- /dev/null
+++ b/plugins/kaitai/parsers/struct.c
@@ -0,0 +1,837 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * struct.c - définition d'une structure Kaitai
+ *
+ * Copyright (C) 2019 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 <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "struct.h"
+
+
+#include <assert.h>
+#include <string.h>
+
+
+#include <plugins/yaml/collection.h>
+#include <plugins/yaml/parser.h>
+
+
+#include "struct-int.h"
+#include "../import.h"
+#include "../parser.h"
+#include "../records/empty.h"
+#include "../records/group.h"
+
+
+
+/* ---------------------- LECTURE D'UNE TRANCHE DE DEFINITIONS ---------------------- */
+
+
+/* Initialise la classe des structuts de spécification Kaitai. */
+static void g_kaitai_structure_class_init(GKaitaiStructClass *);
+
+/* Initialise un structut de spécification Kaitai. */
+static void g_kaitai_structure_init(GKaitaiStruct *);
+
+/* Supprime toutes les références externes. */
+static void g_kaitai_structure_dispose(GKaitaiStruct *);
+
+/* Procède à la libération totale de la mémoire. */
+static void g_kaitai_structure_finalize(GKaitaiStruct *);
+
+/* Charge les éventuelles dépendances de la définition. */
+static bool g_kaitai_structure_load_imports(GKaitaiStruct *);
+
+
+
+/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */
+
+
+/* Parcourt un contenu binaire selon des spécifications Kaitai. */
+static bool g_kaitai_structure_parse_content(GKaitaiStruct *, kaitai_scope_t *, GBinContent *, ext_vmpa_t *, GMatchRecord **);
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* LECTURE D'UNE TRANCHE DE DEFINITIONS */
+/* ---------------------------------------------------------------------------------- */
+
+
+/* Indique le type défini pour un structut de la spécification Kaitai. */
+G_DEFINE_TYPE(GKaitaiStruct, g_kaitai_structure, G_TYPE_KAITAI_PARSER);
+
+
+/******************************************************************************
+* *
+* Paramètres : klass = classe à initialiser. *
+* *
+* Description : Initialise la classe des structuts de spécification Kaitai. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_kaitai_structure_class_init(GKaitaiStructClass *klass)
+{
+ GObjectClass *object; /* Autre version de la classe */
+ GKaitaiParserClass *parser; /* Version parente de la classe*/
+
+ object = G_OBJECT_CLASS(klass);
+
+ object->dispose = (GObjectFinalizeFunc/* ! */)g_kaitai_structure_dispose;
+ object->finalize = (GObjectFinalizeFunc)g_kaitai_structure_finalize;
+
+ parser = G_KAITAI_PARSER_CLASS(klass);
+
+ parser->parse = (parse_kaitai_fc)g_kaitai_structure_parse_content;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : kstruct = instance à initialiser. *
+* *
+* Description : Initialise un structure de spécification Kaitai. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_kaitai_structure_init(GKaitaiStruct *kstruct)
+{
+ kstruct->filename = NULL;
+
+ kstruct->meta = NULL;
+
+ kstruct->seq_items = NULL;
+ kstruct->seq_items_count = 0;
+
+ kstruct->types = NULL;
+ kstruct->types_count = 0;
+
+ kstruct->instances = NULL;
+ kstruct->instances_count = 0;
+
+ kstruct->enums = NULL;
+ kstruct->enums_count = 0;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : kstruct = instance d'objet GLib à traiter. *
+* *
+* Description : Supprime toutes les références externes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_kaitai_structure_dispose(GKaitaiStruct *kstruct)
+{
+ size_t i; /* Boucle de parcours */
+
+ g_clear_object(&kstruct->meta);
+
+ for (i = 0; i < kstruct->seq_items_count; i++)
+ g_clear_object(&kstruct->seq_items[i]);
+
+ for (i = 0; i < kstruct->types_count; i++)
+ g_clear_object(&kstruct->types[i]);
+
+ for (i = 0; i < kstruct->instances_count; i++)
+ g_clear_object(&kstruct->instances[i]);
+
+ for (i = 0; i < kstruct->enums_count; i++)
+ g_clear_object(&kstruct->enums[i]);
+
+ G_OBJECT_CLASS(g_kaitai_structure_parent_class)->dispose(G_OBJECT(kstruct));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : kstruct = instance d'objet GLib à traiter. *
+* *
+* Description : Procède à la libération totale de la mémoire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_kaitai_structure_finalize(GKaitaiStruct *kstruct)
+{
+ if (kstruct->filename != NULL)
+ free(kstruct->filename);
+
+ if (kstruct->seq_items != NULL)
+ free(kstruct->seq_items);
+
+ if (kstruct->types != NULL)
+ free(kstruct->types);
+
+ if (kstruct->instances != NULL)
+ free(kstruct->instances);
+
+ if (kstruct->enums != NULL)
+ free(kstruct->enums);
+
+ G_OBJECT_CLASS(g_kaitai_structure_parent_class)->finalize(G_OBJECT(kstruct));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : text = définitions textuelles d'un contenu brut. *
+* *
+* Description : Crée un nouvel interpréteur de structure Kaitai. *
+* *
+* Retour : Instance mise en place ou NULL en cas d'échec. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+GKaitaiStruct *g_kaitai_structure_new_from_text(const char *text)
+{
+ GKaitaiStruct *result; /* Structure à retourner */
+
+ result = g_object_new(G_TYPE_KAITAI_STRUCT, NULL);
+
+ if (!g_kaitai_structure_create_from_text(result, text))
+ g_clear_object(&result);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : kstruct = lecteur de définition à initialiser pleinement. *
+* text = définitions textuelles d'un contenu brut. *
+* *
+* Description : Met en place un interpréteur de définitions Kaitai. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool g_kaitai_structure_create_from_text(GKaitaiStruct *kstruct, const char *text)
+{
+ bool result; /* Bilan à retourner */
+ GYamlNode *root; /* Noeud racine YAML */
+
+ root = parse_yaml_from_text(text, strlen(text));
+
+ if (root != NULL)
+ {
+ result = g_kaitai_structure_create(kstruct, root);
+ g_object_unref(G_OBJECT(root));
+ }
+ else
+ {
+ fprintf(stderr, "The provided YAML content seems invalid");
+ result = false;
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : filename = chemin vers des définitions de règles. *
+* *
+* Description : Crée un nouvel interpréteur de structure Kaitai. *
+* *
+* Retour : Instance mise en place ou NULL en cas d'échec. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+GKaitaiStruct *g_kaitai_structure_new_from_file(const char *filename)
+{
+ GKaitaiStruct *result; /* Structure à retourner */
+
+ result = g_object_new(G_TYPE_KAITAI_STRUCT, NULL);
+
+ if (!g_kaitai_structure_create_from_file(result, filename))
+ g_clear_object(&result);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : kstruct = lecteur de définition à initialiser pleinement. *
+* filename = chemin vers des définitions de règles. *
+* *
+* Description : Met en place un interpréteur de définitions Kaitai. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool g_kaitai_structure_create_from_file(GKaitaiStruct *kstruct, const char *filename)
+{
+ bool result; /* Bilan à retourner */
+ GYamlNode *root; /* Noeud racine YAML */
+
+ kstruct->filename = strdup(filename);
+
+ root = parse_yaml_from_file(filename);
+
+ if (root != NULL)
+ {
+ result = g_kaitai_structure_create(kstruct, root);
+ g_object_unref(G_OBJECT(root));
+ }
+ else
+ {
+ fprintf(stderr, "The provided YAML content seems invalid");
+ result = false;
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : kstruct = lecteur de définition à initialiser pleinement. *
+* parent = noeud Yaml contenant l'attribut à constituer. *
+* *
+* Description : Met en place un lecteur de définitions Kaitai. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool g_kaitai_structure_create(GKaitaiStruct *kstruct, GYamlNode *parent)
+{
+ bool result; /* Bilan à retourner */
+ GYamlNode *collec; /* Liste de noeuds à traiter */
+ GYamlNode **nodes; /* Eventuels noeuds trouvés */
+ size_t count; /* Quantité de ces noeuds */
+ size_t i; /* Boucle de parcours */
+ size_t first; /* Premier emplacement dispo. */
+ bool failed; /* Détection d'un échec */
+
+ result = false;
+
+ /* Informations générales */
+
+ kstruct->meta = g_kaitai_meta_new(parent);
+ assert(kstruct->meta != NULL);
+
+ result = g_kaitai_structure_load_imports(kstruct);
+ if (!result) goto bad_loading;
+
+ /* Séquence */
+
+ collec = g_yaml_node_find_first_by_path(parent, "/seq/");
+
+ if (collec != NULL)
+ {
+ if (G_IS_YAML_COLLEC(collec))
+ nodes = g_yaml_collection_get_nodes(G_YAML_COLLEC(collec), &count);
+ else
+ count = 0;
+
+ if (count > 0)
+ {
+ kstruct->seq_items = calloc(count, sizeof(GKaitaiAttribute *));
+ kstruct->seq_items_count = count;
+
+ for (i = 0; i < count; i++)
+ {
+ kstruct->seq_items[i] = g_kaitai_attribute_new(nodes[i]);
+ if (kstruct->seq_items[i] == NULL) break;
+
+ g_object_unref(G_OBJECT(nodes[i]));
+
+ }
+
+ failed = (i < count);
+
+ for (; i < count; i++)
+ g_object_unref(G_OBJECT(nodes[i]));
+
+ free(nodes);
+
+ if (failed)
+ goto bad_loading;
+
+ }
+
+ g_object_unref(G_OBJECT(collec));
+
+ }
+
+ /* Types particuliers éventuels */
+
+ collec = g_yaml_node_find_first_by_path(parent, "/types/");
+
+ if (collec != NULL)
+ {
+ if (G_IS_YAML_COLLEC(collec))
+ nodes = g_yaml_collection_get_nodes(G_YAML_COLLEC(collec), &count);
+ else
+ count = 0;
+
+ if (count > 0)
+ {
+ first = kstruct->types_count;
+
+ kstruct->types_count += count;
+ kstruct->types = realloc(kstruct->types, kstruct->types_count * sizeof(GKaitaiType *));
+
+ for (i = 0; i < count; i++)
+ {
+ kstruct->types[first + i] = g_kaitai_type_new(nodes[i]);
+ if (kstruct->types[first + i] == NULL) break;
+
+ g_object_unref(G_OBJECT(nodes[i]));
+
+ }
+
+ failed = (i < count);
+
+ for (; i < count; i++)
+ g_object_unref(G_OBJECT(nodes[i]));
+
+ free(nodes);
+
+ if (failed)
+ goto bad_loading;
+
+ }
+
+ g_object_unref(G_OBJECT(collec));
+
+ }
+
+ /* Instances éventuelles */
+
+ collec = g_yaml_node_find_first_by_path(parent, "/instances/");
+
+ if (collec != NULL)
+ {
+ if (G_IS_YAML_COLLEC(collec))
+ nodes = g_yaml_collection_get_nodes(G_YAML_COLLEC(collec), &count);
+ else
+ count = 0;
+
+ if (count > 0)
+ {
+ kstruct->instances = calloc(count, sizeof(GKaitaiInstance *));
+ kstruct->instances_count = count;
+
+ for (i = 0; i < count; i++)
+ {
+ kstruct->instances[i] = g_kaitai_instance_new(nodes[i]);
+ if (kstruct->instances[i] == NULL) break;
+
+ g_object_unref(G_OBJECT(nodes[i]));
+
+ }
+
+ failed = (i < count);
+
+ for (; i < count; i++)
+ g_object_unref(G_OBJECT(nodes[i]));
+
+ free(nodes);
+
+ if (failed)
+ goto bad_loading;
+
+ }
+
+ g_object_unref(G_OBJECT(collec));
+
+ }
+
+ /* Enumérations éventuelles */
+
+ collec = g_yaml_node_find_first_by_path(parent, "/enums/");
+
+ if (collec != NULL)
+ {
+ if (G_IS_YAML_COLLEC(collec))
+ nodes = g_yaml_collection_get_nodes(G_YAML_COLLEC(collec), &count);
+ else
+ count = 0;
+
+ if (count > 0)
+ {
+ kstruct->enums = calloc(count, sizeof(GKaitaiEnum *));
+ kstruct->enums_count = count;
+
+ for (i = 0; i < count; i++)
+ {
+ kstruct->enums[i] = g_kaitai_enum_new(nodes[i]);
+ if (kstruct->enums[i] == NULL) break;
+
+ g_object_unref(G_OBJECT(nodes[i]));
+
+ }
+
+ failed = (i < count);
+
+ for (; i < count; i++)
+ g_object_unref(G_OBJECT(nodes[i]));
+
+ free(nodes);
+
+ if (failed)
+ goto bad_loading;
+
+ }
+
+ g_object_unref(G_OBJECT(collec));
+
+ }
+
+ /* Sortie heureuse */
+
+ result = true;
+
+ bad_loading:
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : kstruct = lecteur de définition à initialiser pleinement. *
+* *
+* Description : Charge les éventuelles dépendances de la définition. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool g_kaitai_structure_load_imports(GKaitaiStruct *kstruct)
+{
+ bool result; /* Bilan d'opération à renvoyer*/
+ const char * const *dependencies; /* Liste d'imports requis */
+ size_t count; /* Quantité de ces imports */
+ size_t i; /* Boucle de parcours */
+ GKaitaiType *imported; /* Structure importée */
+
+ result = true;
+
+ dependencies = g_kaitai_meta_get_dependencies(kstruct->meta, &count);
+
+ for (i = 0; i < count; i++)
+ {
+ imported = import_kaitai_definition(dependencies[i], kstruct->filename);
+ if (imported == NULL) break;
+
+ kstruct->types_count++;
+ kstruct->types = realloc(kstruct->types, kstruct->types_count * sizeof(GKaitaiType *));
+
+ kstruct->types[kstruct->types_count - 1] = imported;
+
+ }
+
+ result = (i == count);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : kstruct = structure Kaitai à consulter. *
+* *
+* Description : Fournit la description globale d'une définition Kaitai. *
+* *
+* Retour : Description de la définition Kaitai courante. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+GKaitaiMeta *g_kaitai_structure_get_meta(const GKaitaiStruct *kstruct)
+{
+ GKaitaiMeta *result; /* Informations à retourner */
+
+ result = kstruct->meta;
+
+ if (result != NULL)
+ g_object_ref(G_OBJECT(result));
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : kstruct = structure Kaitai en cours de parcours. *
+* name = désignation principale des énumérations ciblées. *
+* *
+* Description : Fournit un ensemble d'énumérations locales de la structure. *
+* *
+* Retour : Enumérations locales ou NULL si non trouvée. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+GKaitaiEnum *g_kaitai_structure_get_enum(const GKaitaiStruct *kstruct, const sized_string_t *name)
+{
+ GKaitaiEnum *result; /* Instance à retourner */
+ size_t i; /* Boucle de parcours */
+ const char *other; /* Autre désignation à comparer*/
+
+ result = NULL;
+
+ for (i = 0; i < kstruct->enums_count; i++)
+ {
+ other = g_kaitai_enum_get_name(kstruct->enums[i]);
+
+ if (strncmp(name->data, other, name->len) == 0) // FIXME
+ {
+ result = kstruct->enums[i];
+ g_object_ref(G_OBJECT(result));
+ break;
+ }
+
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : kstruct = structure Kaitai en cours de parcours. *
+* name = désignation du type particulier ciblé. *
+* *
+* Description : Recherche la définition d'un type nouveau pour Kaitai. *
+* *
+* Retour : Type prêt à emploi ou NULL si non trouvé. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+GKaitaiType *g_kaitai_structure_find_sub_type(const GKaitaiStruct *kstruct, const char *name)
+{
+ GKaitaiType *result; /* Instance à retourner */
+ size_t i; /* Boucle de parcours */
+ const char *other; /* Autre désignation à comparer*/
+
+ result = NULL;
+
+ for (i = 0; i < kstruct->types_count; i++)
+ {
+ other = g_kaitai_type_get_name(kstruct->types[i]);
+
+ if (strcmp(name, other) == 0)
+ {
+ result = kstruct->types[i];
+ g_object_ref(G_OBJECT(result));
+ break;
+ }
+
+ result = g_kaitai_structure_find_sub_type(G_KAITAI_STRUCT(kstruct->types[i]), name);
+ if (result != NULL) break;
+
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : kstruct = structure Kaitai en cours de parcours. *
+* content = contenu binaire en cours de traitement. *
+* *
+* Description : Parcourt un contenu binaire selon une description Kaitai. *
+* *
+* Retour : Arborescence d'éléments rencontrés selon les spécifications. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+GMatchRecord *g_kaitai_structure_parse(GKaitaiStruct *kstruct, GBinContent *content)
+{
+ GMatchRecord *result; /* Arborescence à retourner */
+ vmpa2t pos; /* Tête de lecture */
+ kaitai_scope_t locals; /* Variables locales */
+ ext_vmpa_t epos; /* Tête de lecture complète */
+
+ g_binary_content_compute_start_pos(content, &pos);
+
+ init_record_scope(&locals, kstruct->meta);
+
+ init_evmpa_from_vmpa(&epos, &pos);
+
+ g_kaitai_parser_parse_content(G_KAITAI_PARSER(kstruct), &locals, content, &epos, &result);
+
+ return result;
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* IMPLEMENTATION DES FONCTIONS DE CLASSE */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : kstruct = structure Kaitai en cours de parcours. *
+* locals = variables locales pour les résolutions de types. *
+* content = données binaires à analyser et traduire. *
+* epos = tête de lecture courante. [OUT] *
+* record = noeud d'arborescence d'éléments rencontrés. [OUT] *
+* *
+* Description : Parcourt un contenu binaire selon des spécifications Kaitai. *
+* *
+* Retour : Bilan de l'opératon : true pour continuer, false sinon. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool g_kaitai_structure_parse_content(GKaitaiStruct *kstruct, kaitai_scope_t *locals, GBinContent *content, ext_vmpa_t *epos, GMatchRecord **record)
+{
+ bool result; /* Bilan à retourner */
+ GRecordGroup *group; /* Ensemble à constituer */
+ GMatchRecord *old; /* Sauvegarde de valeur */
+ size_t i; /* Boucle de parcours */
+ GMatchRecord *child; /* Nouvel élément mis en place */
+
+ result = true;
+
+ /* Si le groupe est vide */
+ if ((kstruct->seq_items_count + kstruct->instances_count) == 0)
+ {
+ *record = G_MATCH_RECORD(g_record_empty_new(G_KAITAI_PARSER(kstruct), content, &epos->base));
+
+ if (locals->root == NULL)
+ locals->root = *record;
+
+ }
+
+ /* Sinon on construit selon les définitions fournies */
+ else
+ {
+ group = g_record_group_new(kstruct, content);
+ *record = G_MATCH_RECORD(group);
+
+ if (locals->root == NULL)
+ locals->root = *record;
+
+ old = locals->parent;
+ locals->parent = *record;
+
+ /**
+ * Les instances sont à charger avant les éléments fixes car
+ * des références au premières peuvent être attendues dans ces derniers.
+ *
+ * Les évolutions de la tête de lecture n'ont en revanche normalement
+ * pas d'incidence sur le chargement des éléments fixes.
+ */
+
+ for (i = 0; i < kstruct->instances_count; i++)
+ {
+ result = g_kaitai_parser_parse_content(G_KAITAI_PARSER(kstruct->instances[i]),
+ locals, content, epos, &child);
+ if (!result) goto exit;
+
+ if (child != NULL)
+ {
+ g_record_group_add_record(group, child);
+ g_object_unref(G_OBJECT(child));
+ }
+
+ }
+
+ /**
+ * Seconde phase.
+ */
+
+ locals->parent = *record;
+
+ for (i = 0; i < kstruct->seq_items_count; i++)
+ {
+ result = g_kaitai_parser_parse_content(G_KAITAI_PARSER(kstruct->seq_items[i]),
+ locals, content, epos, &child);
+ if (!result) goto exit;
+
+ if (child != NULL)
+ {
+ g_record_group_add_record(group, child);
+ g_object_unref(G_OBJECT(child));
+ }
+
+ }
+
+ exit:
+
+ locals->parent = old;
+
+ if (!result)
+ g_clear_object(record);
+
+ }
+
+ return result;
+
+}