summaryrefslogtreecommitdiff
path: root/plugins/kaitai/parsers/enum.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/kaitai/parsers/enum.c')
-rw-r--r--plugins/kaitai/parsers/enum.c765
1 files changed, 765 insertions, 0 deletions
diff --git a/plugins/kaitai/parsers/enum.c b/plugins/kaitai/parsers/enum.c
new file mode 100644
index 0000000..267aaba
--- /dev/null
+++ b/plugins/kaitai/parsers/enum.c
@@ -0,0 +1,765 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * enum.h - gestion des énumérations 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 "enum.h"
+
+
+#include <malloc.h>
+#include <string.h>
+
+
+#include <i18n.h>
+
+
+#include <common/extstr.h>
+#include <common/sort.h>
+#include <core/logs.h>
+#include <plugins/yaml/collection.h>
+#include <plugins/yaml/pair.h>
+
+
+#include "enum-int.h"
+
+
+
+/* ------------------------- MANIPULATION D'UNE ENUMERATION ------------------------- */
+
+
+/* Construit une valeur d'énumération à partir d'indications. */
+static enum_value_t *build_enum_value(GYamlNode *, bool *);
+
+/* Supprime de la mémoire une valeur d'énumération. */
+static void delete_enum_value(enum_value_t *);
+
+/* Etablit la comparaison entre deux valeurs d'énumération. */
+static int compare_enum_values_by_value(const enum_value_t **, const enum_value_t **);
+
+/* Etablit la comparaison entre deux noms d'énumération. */
+static int compare_enum_values_by_label(const enum_value_t **, const enum_value_t **);
+
+/* Etablit la comparaison entre deux noms d'énumération. */
+static int compare_enum_values_by_sized_label(const sized_string_t *, const enum_value_t **);
+
+
+
+/* ----------------------- GESTION D'UN GROUPE D'ENUMERATIONS ----------------------- */
+
+
+/* Initialise la classe des groupes d'énumérations Kaitai. */
+static void g_kaitai_enum_class_init(GKaitaiEnumClass *);
+
+/* Initialise un groupe d'énumérations Kaitai. */
+static void g_kaitai_enum_init(GKaitaiEnum *);
+
+/* Supprime toutes les références externes. */
+static void g_kaitai_enum_dispose(GKaitaiEnum *);
+
+/* Procède à la libération totale de la mémoire. */
+static void g_kaitai_enum_finalize(GKaitaiEnum *);
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* MANIPULATION D'UNE ENUMERATION */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : node = noeud Yaml à venir lire. *
+* defcase = indique si une valeur par défaut est visée. [OUT] *
+* *
+* Description : Construit une valeur d'énumération à partir d'indications. *
+* *
+* Retour : Structure de valeur mise en place. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static enum_value_t *build_enum_value(GYamlNode *node, bool *defcase)
+{
+ enum_value_t *result; /* Valeur à retourner */
+ const char *key; /* Clef d'une énumération */
+ kaitai_scope_t fake; /* Contexte de circonstance */
+ resolved_value_t kval; /* Valeur à indexer */
+ const char *value; /* Valeur Yaml particulière */
+ char *path; /* Chemin d'accès suivant */
+ GYamlNode *children; /* Sous-noeuds rattachés */
+ GYamlNode *sub; /* Sous-noeud à considérer */
+
+ result = NULL;
+
+ *defcase = false;
+
+ if (!G_IS_YAML_PAIR(node))
+ goto bad_node;
+
+ /* Identification de la valeur énumérative */
+
+ key = g_yaml_pair_get_key(G_YAML_PAIR(node));
+
+ if (strcmp(key, "_") == 0)
+ {
+ /**
+ * Exemple de choix par défaut :
+ * http://doc.kaitai.io/user_guide.html#tlv
+ */
+
+ kval.type = GVT_UNSIGNED_INTEGER;
+ kval.unsigned_integer = ~0llu;
+
+ *defcase = true;
+
+ }
+
+ else
+ {
+ fake.meta = NULL;
+ fake.root = NULL;
+ fake.parent = NULL;
+ fake.last = NULL;
+
+ if (!resolve_kaitai_expression_as_integer(&fake, key, strlen(key), &kval))
+ goto bad_node;
+
+ }
+
+ /* Récupération des éléments associés à la valeur */
+
+ value = g_yaml_pair_get_value(G_YAML_PAIR(node));
+
+ if (value != NULL)
+ {
+ result = malloc(sizeof(enum_value_t));
+
+ result->value = kval;
+ result->label = strdup(value);
+ result->doc = NULL;
+
+ }
+ else
+ {
+ /**
+ * Les énumérations peuvent comporter un commentaire associé
+ * sous forme d'un élément de documentation complémentaire.
+ *
+ * Cf. http://doc.kaitai.io/user_guide.html#verbose-enums
+ */
+
+ asprintf(&path, "/%s/", key);
+ children = g_yaml_node_find_first_by_path(node, path);
+ free(path);
+
+ if (!G_IS_YAML_COLLEC(children))
+ goto bad_value;
+
+ /* Identifiant */
+
+ sub = g_yaml_node_find_first_by_path(children, "/id");
+
+ if (!G_IS_YAML_PAIR(sub))
+ goto bad_sub_value;
+
+ value = g_yaml_pair_get_value(G_YAML_PAIR(sub));
+
+ if (value == NULL)
+ goto bad_sub_value;
+
+ result = malloc(sizeof(enum_value_t));
+
+ result->value = kval;
+ result->label = strdup(value);
+ result->doc = NULL;
+
+ g_object_unref(G_OBJECT(sub));
+
+ /* Documentation */
+
+ sub = g_yaml_node_find_first_by_path(children, "/doc");
+
+ if (!G_IS_YAML_PAIR(sub))
+ goto bad_sub_value;
+
+ value = g_yaml_pair_get_value(G_YAML_PAIR(sub));
+
+ if (value == NULL)
+ goto bad_sub_value;
+
+ result->doc = strdup(value);
+
+ bad_sub_value:
+
+ g_clear_object(&sub);
+
+ g_object_unref(G_OBJECT(children));
+
+ bad_value:
+
+ ;
+
+ }
+
+ bad_node:
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : value = valeur à traiter. *
+* *
+* Description : Supprime de la mémoire une valeur d'énumération. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void delete_enum_value(enum_value_t *value)
+{
+ EXIT_RESOLVED_VALUE(value->value);
+
+ free(value->label);
+
+ if (value->doc != NULL)
+ free(value->doc);
+
+ free(value);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : a = premières informations à consulter. *
+* b = secondes informations à consulter. *
+* *
+* Description : Etablit la comparaison entre deux valeurs d'énumération. *
+* *
+* Retour : Bilan : -1 (a < b), 0 (a == b) ou 1 (a > b). *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static int compare_enum_values_by_value(const enum_value_t **a, const enum_value_t **b)
+{
+ int result; /* Bilan à retourner */
+ const resolved_value_t *value_a; /* Raccouri d'accès pour a */
+ const resolved_value_t *value_b; /* Raccouri d'accès pour b */
+
+ value_a = &(*a)->value;
+ value_b = &(*b)->value;
+
+ if (value_a->type == GVT_UNSIGNED_INTEGER && value_b->type == GVT_UNSIGNED_INTEGER)
+ result = sort_unsigned_long_long(value_a->unsigned_integer, value_b->unsigned_integer);
+
+ else if (value_a->type == GVT_UNSIGNED_INTEGER && value_b->type == GVT_UNSIGNED_INTEGER)
+ result = sort_signed_long_long(value_a->signed_integer, value_b->signed_integer);
+
+ else
+ {
+ /**
+ * Le code Python a deux options : soit fournir un équivalent à la
+ * structure resolved_value_t lors de l'appel correspondant à cette
+ * fonction compare_enum_values_by_value(), soit fournir un nombre
+ * directement.
+ *
+ * Comme PyArg_ParseTuple() est obligée de trancher entre non-signé
+ * et signé, le parti est pris de considérer le non-signé coté Python.
+ * On s'adapte en conséquence ici.
+ *
+ * La structure resolved_value_t est une union, donc les valeurs
+ * sont potientiellement au mauvais format mais bien présentes.
+ */
+
+ /**
+ * result = sort_unsigned_long_long(value_a->type, value_b->type);
+ */
+
+ result = sort_unsigned_long_long(value_a->unsigned_integer, value_b->unsigned_integer);
+
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : a = premières informations à consulter. *
+* b = secondes informations à consulter. *
+* *
+* Description : Etablit la comparaison entre deux noms d'énumération. *
+* *
+* Retour : Bilan : -1 (a < b), 0 (a == b) ou 1 (a > b). *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static int compare_enum_values_by_label(const enum_value_t **a, const enum_value_t **b)
+{
+ int result; /* Bilan à retourner */
+
+ result = strcmp((*a)->label, (*b)->label);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : l = premières informations à consulter. *
+* b = secondes informations à consulter. *
+* *
+* Description : Etablit la comparaison entre deux noms d'énumération. *
+* *
+* Retour : Bilan : -1 (a < b), 0 (a == b) ou 1 (a > b). *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static int compare_enum_values_by_sized_label(const sized_string_t *l, const enum_value_t **b)
+{
+ int result; /* Bilan à retourner */
+
+ result = szstrcmp(l, (*b)->label);
+
+ return result;
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* GESTION D'UN GROUPE D'ENUMERATIONS */
+/* ---------------------------------------------------------------------------------- */
+
+
+/* Indique le type défini pour un ensemble d'énumérations Kaitai. */
+G_DEFINE_TYPE(GKaitaiEnum, g_kaitai_enum, G_TYPE_OBJECT);
+
+
+/******************************************************************************
+* *
+* Paramètres : klass = classe à initialiser. *
+* *
+* Description : Initialise la classe des groupes d'énumérations Kaitai. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_kaitai_enum_class_init(GKaitaiEnumClass *klass)
+{
+ GObjectClass *object; /* Autre version de la classe */
+
+ object = G_OBJECT_CLASS(klass);
+
+ object->dispose = (GObjectFinalizeFunc/* ! */)g_kaitai_enum_dispose;
+ object->finalize = (GObjectFinalizeFunc)g_kaitai_enum_finalize;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : kenum = instance à initialiser. *
+* *
+* Description : Initialise un groupe d'énumérations Kaitai. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_kaitai_enum_init(GKaitaiEnum *kenum)
+{
+ kenum->name = NULL;
+
+ kenum->cases_v2l = NULL;
+ kenum->cases_v2l_count = 0;
+
+ kenum->cases_l2v = NULL;
+ kenum->cases_l2v_count = 0;
+
+ kenum->defcase = NULL;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : kenum = instance d'objet GLib à traiter. *
+* *
+* Description : Supprime toutes les références externes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_kaitai_enum_dispose(GKaitaiEnum *kenum)
+{
+ G_OBJECT_CLASS(g_kaitai_enum_parent_class)->dispose(G_OBJECT(kenum));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : kenum = instance d'objet GLib à traiter. *
+* *
+* Description : Procède à la libération totale de la mémoire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_kaitai_enum_finalize(GKaitaiEnum *kenum)
+{
+ size_t i; /* Boucle de parcours */
+
+ if (kenum->name != NULL)
+ free(kenum->name);
+
+ for (i = 0; i < kenum->cases_v2l_count; i++)
+ delete_enum_value(kenum->cases_v2l[i]);
+
+ if (kenum->cases_v2l != NULL)
+ free(kenum->cases_v2l);
+
+ if (kenum->cases_l2v != NULL)
+ free(kenum->cases_l2v);
+
+ if (kenum->defcase != NULL)
+ delete_enum_value(kenum->defcase);
+
+ G_OBJECT_CLASS(g_kaitai_enum_parent_class)->finalize(G_OBJECT(kenum));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : parent = noeud Yaml contenant l'attribut à constituer. *
+* *
+* Description : Construit un groupe d'énumérations Kaitai. *
+* *
+* Retour : Instance mise en place ou NULL en cas d'échec. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+GKaitaiEnum *g_kaitai_enum_new(GYamlNode *parent)
+{
+ GKaitaiEnum *result; /* Identifiant à retourner */
+
+ result = g_object_new(G_TYPE_KAITAI_ENUM, NULL);
+
+ if (!g_kaitai_enum_create(result, parent))
+ g_clear_object(&result);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : kenum = groupe d'énumérations à initialiser pleinement. *
+* parent = noeud Yaml contenant l'attribut à constituer. *
+* *
+* Description : Met en place un groupe d'énumérations Kaitai. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool g_kaitai_enum_create(GKaitaiEnum *kenum, GYamlNode *parent)
+{
+ bool result; /* Bilan à retourner */
+ char *path; /* Chemin des valeurs */
+ 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 */
+ bool defcase; /* Définition par défaut ? */
+ enum_value_t *value; /* Valeur énumérative nouvelle */
+ bool found; /* Présence de partage existant*/
+ size_t index; /* Indice du point d'insertion */
+
+ result = false;
+
+ /* Récupération du nom */
+
+ if (!G_IS_YAML_PAIR(parent)) goto exit;
+
+ kenum->name = strdup(g_yaml_pair_get_key(G_YAML_PAIR(parent)));
+
+ /* Association de valeurs */
+
+ path = strdup("/");
+ path = stradd(path, kenum->name);
+ path = stradd(path, "/");
+
+ collec = g_yaml_node_find_first_by_path(parent, path);
+
+ free(path);
+
+ 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)
+ {
+ for (i = 0; i < count; i++)
+ {
+ value = build_enum_value(nodes[i], &defcase);
+ if (value == NULL) break;
+
+ if (defcase)
+ {
+ if (kenum->defcase != NULL)
+ {
+ log_variadic_message(LMT_WARNING,
+ _("Multiple definition of the defaut value for the enumeration '%s'"),
+ kenum->name);
+
+ delete_enum_value(value);
+ break;
+
+ }
+
+ /**
+ * Exemple de choix par défaut :
+ * http://doc.kaitai.io/user_guide.html#tlv
+ */
+
+ kenum->defcase = value;
+
+ }
+
+ else
+ {
+ kenum->cases_v2l = qinsert(kenum->cases_v2l, &kenum->cases_v2l_count, sizeof(enum_value_t *),
+ (__compar_fn_t)compare_enum_values_by_value, &value);
+
+ found = bsearch_index(&value, kenum->cases_l2v, kenum->cases_l2v_count, sizeof(enum_value_t *),
+ (__compar_fn_t)compare_enum_values_by_label, &index);
+
+ if (found)
+ log_variadic_message(LMT_WARNING,
+ _("Multiple occurrence of the label %s in the enumeration '%s'"),
+ value->label, kenum->name);
+
+ else
+ kenum->cases_l2v = _qinsert(kenum->cases_l2v, &kenum->cases_l2v_count, sizeof(enum_value_t *),
+ &value, index);
+
+ }
+
+ g_object_unref(G_OBJECT(nodes[i]));
+
+ }
+
+ result = (i == count);
+
+ for (; i < count; i++)
+ g_object_unref(G_OBJECT(nodes[i]));
+
+ free(nodes);
+
+ }
+
+ g_object_unref(G_OBJECT(collec));
+
+ }
+
+ exit:
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : kenum = groupe d'énumérations à consulter. *
+* *
+* Description : Fournit le nom principal d'une énumération. *
+* *
+* Retour : Désignation de l'énumération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+const char *g_kaitai_enum_get_name(const GKaitaiEnum *kenum)
+{
+ const char *result; /* Chaîne à retourner */
+
+ result = kenum->name;
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : kenum = groupe d'énumérations à consulter. *
+* label = étiquette de l'élément constant à traduire. *
+* value = valeur concrète correspondante. [OUT] *
+* *
+* Description : Traduit une étiquette brute en constante d'énumération. *
+* *
+* Retour : Bilan de la conversion. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool g_kaitai_enum_find_value(const GKaitaiEnum *kenum, const sized_string_t *label, resolved_value_t *value)
+{
+ bool result; /* Présence à retourner */
+ size_t index; /* Indice du point d'insertion */
+
+ result = bsearch_index(label, kenum->cases_l2v, kenum->cases_l2v_count, sizeof(enum_value_t *),
+ (__compar_fn_t)compare_enum_values_by_sized_label, &index);
+
+ if (result)
+ *value = kenum->cases_l2v[index]->value;
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : kenum = groupe d'énumérations à consulter. *
+* value = valeur concrète à transformer. *
+* prefix = détermine l'ajout d'un préfixe éventuel. *
+* *
+* Description : Traduit une constante d'énumération en étiquette brute. *
+* *
+* Retour : Désignation ou NULL en cas d'échec. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+char *g_kaitai_enum_find_label(const GKaitaiEnum *kenum, const resolved_value_t *value, bool prefix)
+{
+ char *result; /* Etiquette à retourner */
+ enum_value_t faked; /* Copie d'élément recherché */
+ bool found; /* Présence de partage existant*/
+ size_t index; /* Indice du point d'insertion */
+ const enum_value_t *item; /* Elément retrouvé par valeur */
+
+ faked.value = *value;
+
+ found = bsearch_index(&faked, kenum->cases_v2l, kenum->cases_v2l_count, sizeof(enum_value_t *),
+ (__compar_fn_t)compare_enum_values_by_value, &index);
+
+ if (found)
+ item = kenum->cases_l2v[index];
+ else
+ item = kenum->defcase;
+
+ if (item != NULL)
+ {
+ if (prefix)
+ asprintf(&result, "%s::%s", kenum->name, item->label);
+ else
+ result = strdup(item->label);
+ }
+
+ else
+ result = NULL;
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : kenum = groupe d'énumérations à consulter. *
+* value = valeur concrète à transformer. *
+* *
+* Description : Traduit une constante d'énumération en documentation. *
+* *
+* Retour : Documentation associée à la valeur indiquée ou NULL. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+char *g_kaitai_enum_find_documentation(const GKaitaiEnum *kenum, const resolved_value_t *value)
+{
+ char *result; /* Documentation à retourner */
+ enum_value_t faked; /* Copie d'élément recherché */
+ bool found; /* Présence de partage existant*/
+ size_t index; /* Indice du point d'insertion */
+ const enum_value_t *item; /* Elément retrouvé par valeur */
+
+ faked.value = *value;
+
+ found = bsearch_index(&faked, kenum->cases_v2l, kenum->cases_v2l_count, sizeof(enum_value_t *),
+ (__compar_fn_t)compare_enum_values_by_value, &index);
+
+ if (found)
+ item = kenum->cases_l2v[index];
+ else
+ item = kenum->defcase;
+
+ if (item != NULL)
+ result = strdup(item->doc);
+ else
+ result = NULL;
+
+ return result;
+
+}