summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCyrille Bagard <nocbos@gmail.com>2023-05-24 00:38:15 (GMT)
committerCyrille Bagard <nocbos@gmail.com>2023-05-24 00:38:15 (GMT)
commit5eab5f1bf3665e948e2054817fb688963dc86935 (patch)
tree0d0547b7fd90cf51992a449bdca0d9f7b38b3174
parent9f4abb8a20871c64b33f88ad5538bbbe111c1d4c (diff)
Define a first implementation of Kaitai parsing.
-rw-r--r--configure.ac6
-rw-r--r--plugins/Makefile.am1
-rw-r--r--plugins/kaitai/Makefile.am92
-rw-r--r--plugins/kaitai/array-int.h51
-rw-r--r--plugins/kaitai/array.c376
-rw-r--r--plugins/kaitai/array.h76
-rw-r--r--plugins/kaitai/core.c79
-rw-r--r--plugins/kaitai/core.h38
-rw-r--r--plugins/kaitai/expression.h134
-rw-r--r--plugins/kaitai/grammar.y1854
-rw-r--r--plugins/kaitai/parser-int.h55
-rw-r--r--plugins/kaitai/parser.c159
-rw-r--r--plugins/kaitai/parser.h63
-rw-r--r--plugins/kaitai/parsers/Makefile.am25
-rw-r--r--plugins/kaitai/parsers/attribute-int.h104
-rw-r--r--plugins/kaitai/parsers/attribute.c2074
-rw-r--r--plugins/kaitai/parsers/attribute.h154
-rw-r--r--plugins/kaitai/parsers/enum-int.h79
-rw-r--r--plugins/kaitai/parsers/enum.c765
-rw-r--r--plugins/kaitai/parsers/enum.h76
-rw-r--r--plugins/kaitai/parsers/instance-int.h59
-rw-r--r--plugins/kaitai/parsers/instance.c478
-rw-r--r--plugins/kaitai/parsers/instance.h68
-rw-r--r--plugins/kaitai/parsers/meta-int.h57
-rw-r--r--plugins/kaitai/parsers/meta.c319
-rw-r--r--plugins/kaitai/parsers/meta.h68
-rw-r--r--plugins/kaitai/parsers/struct-int.h75
-rw-r--r--plugins/kaitai/parsers/struct.c782
-rw-r--r--plugins/kaitai/parsers/struct.h80
-rw-r--r--plugins/kaitai/parsers/switch-int.h78
-rw-r--r--plugins/kaitai/parsers/switch.c706
-rw-r--r--plugins/kaitai/parsers/switch.h61
-rw-r--r--plugins/kaitai/parsers/type-int.h55
-rw-r--r--plugins/kaitai/parsers/type.c236
-rw-r--r--plugins/kaitai/parsers/type.h62
-rw-r--r--plugins/kaitai/python/Makefile.am25
-rw-r--r--plugins/kaitai/python/array.c265
-rw-r--r--plugins/kaitai/python/array.h45
-rw-r--r--plugins/kaitai/python/module.c132
-rw-r--r--plugins/kaitai/python/module.h41
-rw-r--r--plugins/kaitai/python/parser.c205
-rw-r--r--plugins/kaitai/python/parser.h45
-rw-r--r--plugins/kaitai/python/parsers/Makefile.am19
-rw-r--r--plugins/kaitai/python/parsers/attribute.c420
-rw-r--r--plugins/kaitai/python/parsers/attribute.h45
-rw-r--r--plugins/kaitai/python/parsers/enum.c468
-rw-r--r--plugins/kaitai/python/parsers/enum.h45
-rw-r--r--plugins/kaitai/python/parsers/instance.c280
-rw-r--r--plugins/kaitai/python/parsers/instance.h45
-rw-r--r--plugins/kaitai/python/parsers/meta.c366
-rw-r--r--plugins/kaitai/python/parsers/meta.h45
-rw-r--r--plugins/kaitai/python/parsers/module.c124
-rw-r--r--plugins/kaitai/python/parsers/module.h41
-rw-r--r--plugins/kaitai/python/parsers/struct.c376
-rw-r--r--plugins/kaitai/python/parsers/struct.h45
-rw-r--r--plugins/kaitai/python/parsers/type.c278
-rw-r--r--plugins/kaitai/python/parsers/type.h45
-rw-r--r--plugins/kaitai/python/record.c420
-rw-r--r--plugins/kaitai/python/record.h45
-rw-r--r--plugins/kaitai/python/records/Makefile.am18
-rw-r--r--plugins/kaitai/python/records/empty.c286
-rw-r--r--plugins/kaitai/python/records/empty.h45
-rw-r--r--plugins/kaitai/python/records/group.c305
-rw-r--r--plugins/kaitai/python/records/group.h45
-rw-r--r--plugins/kaitai/python/records/item.c394
-rw-r--r--plugins/kaitai/python/records/item.h45
-rw-r--r--plugins/kaitai/python/records/list.c336
-rw-r--r--plugins/kaitai/python/records/list.h45
-rw-r--r--plugins/kaitai/python/records/module.c122
-rw-r--r--plugins/kaitai/python/records/module.h41
-rw-r--r--plugins/kaitai/python/records/value.c335
-rw-r--r--plugins/kaitai/python/records/value.h45
-rw-r--r--plugins/kaitai/python/scope.c542
-rw-r--r--plugins/kaitai/python/scope.h51
-rw-r--r--plugins/kaitai/python/stream.c278
-rw-r--r--plugins/kaitai/python/stream.h45
-rw-r--r--plugins/kaitai/record-int.h73
-rw-r--r--plugins/kaitai/record.c408
-rw-r--r--plugins/kaitai/record.h88
-rw-r--r--plugins/kaitai/records/Makefile.am21
-rw-r--r--plugins/kaitai/records/empty-int.h57
-rw-r--r--plugins/kaitai/records/empty.c236
-rw-r--r--plugins/kaitai/records/empty.h58
-rw-r--r--plugins/kaitai/records/group-int.h58
-rw-r--r--plugins/kaitai/records/group.c382
-rw-r--r--plugins/kaitai/records/group.h65
-rw-r--r--plugins/kaitai/records/item-int.h58
-rw-r--r--plugins/kaitai/records/item.c309
-rw-r--r--plugins/kaitai/records/item.h65
-rw-r--r--plugins/kaitai/records/list-int.h60
-rw-r--r--plugins/kaitai/records/list.c424
-rw-r--r--plugins/kaitai/records/list.h71
-rw-r--r--plugins/kaitai/records/value-int.h57
-rw-r--r--plugins/kaitai/records/value.c336
-rw-r--r--plugins/kaitai/records/value.h65
-rw-r--r--plugins/kaitai/scope.c257
-rw-r--r--plugins/kaitai/scope.h72
-rw-r--r--plugins/kaitai/stream-int.h55
-rw-r--r--plugins/kaitai/stream.c237
-rw-r--r--plugins/kaitai/stream.h65
-rw-r--r--plugins/kaitai/tokens.l327
-rw-r--r--tests/plugins/kaitai.py2474
102 files changed, 22666 insertions, 0 deletions
diff --git a/configure.ac b/configure.ac
index f4fe90b..eb4693b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -631,6 +631,12 @@ AC_CONFIG_FILES([Makefile
plugins/java/Makefile
plugins/javadesc/Makefile
plugins/javadesc/python/Makefile
+ plugins/kaitai/Makefile
+ plugins/kaitai/parsers/Makefile
+ plugins/kaitai/records/Makefile
+ plugins/kaitai/python/Makefile
+ plugins/kaitai/python/parsers/Makefile
+ plugins/kaitai/python/records/Makefile
plugins/libcsem/Makefile
plugins/lnxsyscalls/Makefile
plugins/mobicore/Makefile
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index be0bd88..7ce4229 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -24,6 +24,7 @@ SUBDIRS = \
fmtp \
itanium \
javadesc \
+ kaitai \
mobicore \
pe \
yaml \
diff --git a/plugins/kaitai/Makefile.am b/plugins/kaitai/Makefile.am
new file mode 100644
index 0000000..cbc0f25
--- /dev/null
+++ b/plugins/kaitai/Makefile.am
@@ -0,0 +1,92 @@
+
+BUILT_SOURCES = grammar.h
+
+
+# On évite d'utiliser les variables personnalisées de type *_la_[YL]FLAGS
+# afin de conserver des noms de fichiers simples, ie sans le nom de la
+# bibliothèque de sortie en préfixe.
+
+AM_YFLAGS = -v -d -p kaitai_ -Wno-yacc -Wcounterexamples
+
+AM_LFLAGS = -P kaitai_ -o lex.yy.c --header-file=tokens.h \
+ -Dyyget_lineno=kaitai_get_lineno \
+ -Dyy_scan_bytes=kaitai__scan_bytes \
+ -Dyy_delete_buffer=kaitai__delete_buffer
+
+lib_LTLIBRARIES = libkaitai.la
+
+libdir = $(pluginslibdir)
+
+
+if BUILD_PYTHON_PACKAGE
+
+RUN_PATH = -Wl,-rpath,'$$ORIGIN/../chrysalide-libs'
+
+endif
+
+if BUILD_PYTHON3_BINDINGS
+
+PYTHON3_LIBADD = python/libkaitaipython.la
+
+if BUILD_DISCARD_LOCAL
+
+if BUILD_PYTHON_PACKAGE
+PYTHON3_RUN_PATH = -Wl,-rpath,'$$ORIGIN/..'
+else
+PYTHON3_RUN_PATH = -Wl,-rpath,'$$ORIGIN'
+endif
+
+else
+
+PYTHON3_RUN_PATH = -Wl,-rpath,$(abs_top_srcdir)/plugins/pychrysalide/.libs
+
+endif
+
+PYTHON3_LDFLAGS = $(PYTHON3_RUN_PATH) -L$(top_srcdir)/plugins/pychrysalide/.libs -l:pychrysalide.so
+
+PYTHON3_SUBDIRS = python
+
+endif
+
+
+libkaitai_la_SOURCES = \
+ array-int.h \
+ array.h array.c \
+ core.h core.c \
+ expression.h \
+ parser-int.h \
+ parser.h parser.c \
+ record-int.h \
+ record.h record.c \
+ scope.h scope.c \
+ stream-int.h \
+ stream.h stream.c \
+ tokens.l \
+ grammar.y
+
+libkaitai_la_LIBADD = \
+ parsers/libkaitaiparsers.la \
+ records/libkaitairecords.la \
+ $(PYTHON3_LIBADD)
+
+libkaitai_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) -I$(top_srcdir)/src
+
+libkaitai_la_LDFLAGS = \
+ -L$(top_srcdir)/src/.libs -lchrysacore \
+ $(PYTHON3_LDFLAGS)
+
+
+devdir = $(includedir)/chrysalide-$(subdir)
+
+dev_HEADERS = $(libkaitai_la_SOURCES:%c=)
+
+
+# Automake fait les choses à moitié
+CLEANFILES = grammar.h grammar.c grammar.output tokens.c tokens.h
+
+# Pareil : de tous les fichiers générés, seule la sortie de Flex saute pour les distributions !
+EXTRA_DIST = tokens.h
+
+
+# misc
+SUBDIRS = parsers records $(PYTHON3_SUBDIRS)
diff --git a/plugins/kaitai/array-int.h b/plugins/kaitai/array-int.h
new file mode 100644
index 0000000..123b16d
--- /dev/null
+++ b/plugins/kaitai/array-int.h
@@ -0,0 +1,51 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * array-int.h - prototypes pour les données associées à un flux de données Kaitai
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef PLUGINS_KAITAI_ARRAY_INT_H
+#define PLUGINS_KAITAI_ARRAY_INT_H
+
+
+#include "array.h"
+
+
+
+/* Tableau rassemblant des éléments divers (instance) */
+struct _GKaitaiArray
+{
+ GObject parent; /* A laisser en premier */
+
+ resolved_value_t *items; /* Eléments du tableau */
+ size_t count; /* Quantité de ces éléments */
+
+};
+
+/* Tableau rassemblant des éléments divers (classe) */
+struct _GKaitaiArrayClass
+{
+ GObjectClass parent; /* A laisser en premier */
+
+};
+
+
+
+#endif /* PLUGINS_KAITAI_ARRAY_INT_H */
diff --git a/plugins/kaitai/array.c b/plugins/kaitai/array.c
new file mode 100644
index 0000000..86f0856
--- /dev/null
+++ b/plugins/kaitai/array.c
@@ -0,0 +1,376 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * array.c - données associées à un flux de données Kaitai
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "array.h"
+
+
+#include <assert.h>
+#include <limits.h>
+#include <malloc.h>
+#include <string.h>
+
+
+#include "array-int.h"
+#include "expression.h"
+
+
+
+/* Initialise la classe des flux de données pour Kaitai. */
+static void g_kaitai_array_class_init(GKaitaiArrayClass *);
+
+/* Initialise un flux de données accessibles à Kaitai. */
+static void g_kaitai_array_init(GKaitaiArray *);
+
+/* Supprime toutes les références externes. */
+static void g_kaitai_array_dispose(GKaitaiArray *);
+
+/* Procède à la libération totale de la mémoire. */
+static void g_kaitai_array_finalize(GKaitaiArray *);
+
+/* Détermine la taille de la séquence d'octets du tableau. */
+static bool g_kaitai_array_compute_bytes_length(const GKaitaiArray *, size_t *);
+
+
+
+/* Indique le type défini pour un tableau rassemblant des éléments Kaitai. */
+G_DEFINE_TYPE(GKaitaiArray, g_kaitai_array, G_TYPE_OBJECT);
+
+
+/******************************************************************************
+* *
+* Paramètres : klass = classe à initialiser. *
+* *
+* Description : Initialise la classe des tableau d'éléments Kaitai. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_kaitai_array_class_init(GKaitaiArrayClass *klass)
+{
+ GObjectClass *object; /* Autre version de la classe */
+
+ object = G_OBJECT_CLASS(klass);
+
+ object->dispose = (GObjectFinalizeFunc/* ! */)g_kaitai_array_dispose;
+ object->finalize = (GObjectFinalizeFunc)g_kaitai_array_finalize;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : array = instance à initialiser. *
+* *
+* Description : Initialise un tableau rassemblant des éléments divers. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_kaitai_array_init(GKaitaiArray *array)
+{
+ array->items = NULL;
+ array->count = 0;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : array = instance d'objet GLib à traiter. *
+* *
+* Description : Supprime toutes les références externes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_kaitai_array_dispose(GKaitaiArray *array)
+{
+ G_OBJECT_CLASS(g_kaitai_array_parent_class)->dispose(G_OBJECT(array));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : array = instance d'objet GLib à traiter. *
+* *
+* Description : Procède à la libération totale de la mémoire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_kaitai_array_finalize(GKaitaiArray *array)
+{
+ G_OBJECT_CLASS(g_kaitai_array_parent_class)->finalize(G_OBJECT(array));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Constitue une amorce de tableau pour rassembler des éléments.*
+* *
+* Retour : Instance mise en place ou NULL en cas d'échec. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+GKaitaiArray *g_kaitai_array_new(void)
+{
+ GKaitaiArray *result; /* Structure à retourner */
+
+ result = g_object_new(G_TYPE_KAITAI_ARRAY, NULL);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : array = tableau Kaitai à consulter. *
+* *
+* Description : Dénombre le nombre d'éléments enregistrés. *
+* *
+* Retour : Taille du tableau manipulé. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+size_t g_kaitai_array_count_items(const GKaitaiArray *array)
+{
+ size_t result; /* Quantité à retourner */
+
+ result = array->count;
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : array = tableau Kaitai à compléter. *
+* item = élément Kaitai à archiver. *
+* *
+* Description : Intègre un élément supplémentaire dans un tableau Kaitai. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void g_kaitai_array_append_item(GKaitaiArray *array, const resolved_value_t *item)
+{
+ array->items = realloc(array->items, ++array->count * sizeof(resolved_value_t));
+
+ COPY_RESOLVED_VALUE(array->items[array->count - 1], *item);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : list = ensemble de correspondances attribut/binaire. *
+* index = indice de la correspondance visée. *
+* item = élément archivé dans le talbeau à fournir. [OUT] *
+* *
+* Description : Fournit un élément ciblé dans un tableau Kaitai. *
+* *
+* Retour : Validité de l'emplacmeent pour élément à renseigner. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool g_kaitai_array_get_item(const GKaitaiArray *array, size_t index, resolved_value_t *item)
+{
+ bool result; /* Bilan à retourner */
+
+ result = (index < array->count);
+
+ if (result)
+ COPY_RESOLVED_VALUE(*item, array->items[index]);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : array = tableau Kaitai à consulter. *
+* length = nombre d'octets représentés. [OUT] *
+* *
+* Description : Détermine la taille de la séquence d'octets du tableau. *
+* *
+* Retour : true si le tableau peut être converti en octets, ou false. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool g_kaitai_array_compute_bytes_length(const GKaitaiArray *array, size_t *length)
+{
+ bool result; /* Bilan à retourner */
+ size_t i; /* Boucle de parcours */
+ const resolved_value_t *item; /* Elément en cours d'analyse */
+ size_t extra; /* Taille d'un sous-tableau */
+
+ result = true;
+
+ *length = 0;
+
+ for (i = 0; i < array->count && result; i++)
+ {
+ item = &array->items[i];
+
+ switch (item->type)
+ {
+ case GVT_UNSIGNED_INTEGER:
+ result = (item->unsigned_integer <= UCHAR_MAX);
+ if (result) (*length)++;
+ break;
+
+ case GVT_SIGNED_INTEGER:
+ result = (0 <= item->signed_integer && item->signed_integer <= SCHAR_MAX);
+ if (result) (*length)++;
+ break;
+
+ case GVT_BYTES:
+ *length += item->bytes.len;
+ break;
+
+ case GVT_ARRAY:
+ result = g_kaitai_array_compute_bytes_length(item->array, &extra);
+ if (result) *length += extra;
+ break;
+
+ default:
+ result = false;
+ break;
+
+ }
+
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : array = tableau Kaitai à consulter. *
+* bytes = conversion en série d'octets équivalent. [OUT] *
+* *
+* Description : Convertit un tableau d'éléments en séquence d'octets. *
+* *
+* Retour : true si une série d'octets a pu être constituée, ou false. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool g_kaitai_array_convert_to_bytes(const GKaitaiArray *array, sized_string_t *bytes)
+{
+ bool result; /* Bilan à retourner */
+ size_t length; /* Taille de la chaîne finale */
+ size_t i; /* Boucle de parcours */
+ const resolved_value_t *item; /* Elément en cours d'analyse */
+ char *iter; /* Tête d'écriture */
+ sized_string_t extra; /* Données d'un sous-tableau */
+
+ /* Détermination de la taille finale */
+
+ result = g_kaitai_array_compute_bytes_length(array, &length);
+
+ /* Construction d'une chaîne d'octets si possible */
+
+ if (result)
+ {
+ bytes->data = malloc(length * sizeof(char));
+ bytes->len = length;
+
+ iter = bytes->data;
+
+ for (i = 0; i < array->count; i++)
+ {
+ item = &array->items[i];
+
+ switch (item->type)
+ {
+ case GVT_UNSIGNED_INTEGER:
+ *iter = item->unsigned_integer;
+ iter++;
+ break;
+
+ case GVT_SIGNED_INTEGER:
+ *iter = item->signed_integer;
+ iter++;
+ break;
+
+ case GVT_BYTES:
+ memcpy(iter, item->bytes.data, item->bytes.len);
+ iter += item->bytes.len;
+ break;
+
+ case GVT_ARRAY:
+ result = g_kaitai_array_convert_to_bytes(item->array, &extra);
+ assert(result);
+
+ memcpy(iter, extra.data, extra.len);
+ iter += extra.len;
+
+ exit_szstr(&extra);
+ break;
+
+ default:
+ break;
+
+ }
+
+ }
+
+ }
+
+ return result;
+
+}
diff --git a/plugins/kaitai/array.h b/plugins/kaitai/array.h
new file mode 100644
index 0000000..e93c7c0
--- /dev/null
+++ b/plugins/kaitai/array.h
@@ -0,0 +1,76 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * array.h - prototypes pour les données associées à un flux de données Kaitai
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef PLUGINS_KAITAI_ARRAY_H
+#define PLUGINS_KAITAI_ARRAY_H
+
+
+#include <glib-object.h>
+#include <stdbool.h>
+
+
+#include <common/szstr.h>
+
+
+
+/* expression.h : informations transportées par une expression */
+typedef struct _resolved_value_t resolved_value_t;
+
+
+
+#define G_TYPE_KAITAI_ARRAY g_kaitai_array_get_type()
+#define G_KAITAI_ARRAY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_KAITAI_ARRAY, GKaitaiArray))
+#define G_IS_KAITAI_ARRAY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_KAITAI_ARRAY))
+#define G_KAITAI_ARRAY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_KAITAI_ARRAY, GKaitaiArrayClass))
+#define G_IS_KAITAI_ARRAY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_KAITAI_ARRAY))
+#define G_KAITAI_ARRAY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_KAITAI_ARRAY, GKaitaiArrayClass))
+
+
+/* Tableau rassemblant des éléments divers (instance) */
+typedef struct _GKaitaiArray GKaitaiArray;
+
+/* Tableau rassemblant des éléments divers (classe) */
+typedef struct _GKaitaiArrayClass GKaitaiArrayClass;
+
+
+/* Indique le type défini pour un tableau rassemblant des éléments Kaitai. */
+GType g_kaitai_array_get_type(void);
+
+/* Constitue une amorce de tableau pour rassembler des éléments. */
+GKaitaiArray *g_kaitai_array_new(void);
+
+/* Dénombre le nombre d'éléments enregistrés. */
+size_t g_kaitai_array_count_items(const GKaitaiArray *);
+
+/* Intègre un élément supplémentaire dans un tableau Kaitai. */
+void g_kaitai_array_append_item(GKaitaiArray *, const resolved_value_t *);
+
+/* Fournit un élément ciblé dans un tableau Kaitai. */
+bool g_kaitai_array_get_item(const GKaitaiArray *, size_t, resolved_value_t *);
+
+/* Convertit un tableau d'éléments en séquence d'octets. */
+bool g_kaitai_array_convert_to_bytes(const GKaitaiArray *, sized_string_t *);
+
+
+
+#endif /* PLUGINS_KAITAI_ARRAY_H */
diff --git a/plugins/kaitai/core.c b/plugins/kaitai/core.c
new file mode 100644
index 0000000..65424a5
--- /dev/null
+++ b/plugins/kaitai/core.c
@@ -0,0 +1,79 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * core.c - prise en charge des descriptions de binaires au format 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 "core.h"
+
+
+#include <plugins/self.h>
+
+
+#ifdef HAVE_PYTHON3_BINDINGS
+# include "python/module.h"
+#endif
+
+
+#ifdef HAVE_PYTHON3_BINDINGS
+# define PG_REQ RL("PyChrysalide")
+#else
+# define PG_REQ NO_REQ
+#endif
+
+
+
+DEFINE_CHRYSALIDE_PLUGIN("Kaitai", "Content parser using Kaitai structure definitions",
+ PACKAGE_VERSION, CHRYSALIDE_WEBSITE("doc/kaitai"),
+ PG_REQ, AL(PGA_PLUGIN_INIT));
+
+
+
+/******************************************************************************
+* *
+* Paramètres : plugin = greffon à manipuler. *
+* *
+* Description : Prend acte du chargement du greffon. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *plugin)
+{
+ bool result; /* Bilan à retourner */
+
+ result = true;
+
+#ifdef HAVE_PYTHON3_BINDINGS
+
+ if (result)
+ result = add_kaitai_module_to_python_module();
+
+ if (result)
+ result = populate_kaitai_module();
+
+#endif
+
+ return result;
+
+}
diff --git a/plugins/kaitai/core.h b/plugins/kaitai/core.h
new file mode 100644
index 0000000..61241f4
--- /dev/null
+++ b/plugins/kaitai/core.h
@@ -0,0 +1,38 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * core.h - prototypes pour la prise en charge des descriptions de binaires au format 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/>.
+ */
+
+
+#ifndef _PLUGINS_KAITAI_CORE_H
+#define _PLUGINS_KAITAI_CORE_H
+
+
+#include <plugins/plugin.h>
+#include <plugins/plugin-int.h>
+
+
+
+/* Prend acte du chargement du greffon. */
+G_MODULE_EXPORT bool chrysalide_plugin_init(GPluginModule *);
+
+
+
+#endif /* _PLUGINS_KAITAI_CORE_H */
diff --git a/plugins/kaitai/expression.h b/plugins/kaitai/expression.h
new file mode 100644
index 0000000..06cf9cf
--- /dev/null
+++ b/plugins/kaitai/expression.h
@@ -0,0 +1,134 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * expression.h - déclarations de prototypes utiles aux résolutions d'expressions
+ *
+ * Copyright (C) 2022 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/>.
+ */
+
+
+#ifndef _PLUGINS_KAITAI_EXPRESSION_H
+#define _PLUGINS_KAITAI_EXPRESSION_H
+
+
+#include <stdbool.h>
+#include <sys/types.h>
+
+
+#include <common/szstr.h>
+
+
+#include "array.h"
+#include "scope.h"
+#include "stream.h"
+
+
+
+/* Type de valeur résolue */
+typedef enum _GrammarValueType
+{
+ GVT_ERROR, /* Erreur remontée */
+ GVT_UNSIGNED_INTEGER, /* Valeur entière #1 */
+ GVT_SIGNED_INTEGER, /* Valeur entière #2 */
+ GVT_FLOAT, /* Valeur fractionnée */
+ GVT_BOOLEAN, /* Valeur booléenne */
+ GVT_BYTES, /* Série d'octets dynamique */
+ GVT_ARRAY, /* Tableau d'éléments divers */
+ GVT_RECORD, /* Correspondance en place */
+ GVT_STREAM, /* Flux de données */
+
+} GrammarValueType;
+
+/* Informations transportées par une expression */
+typedef struct _resolved_value_t
+{
+ GrammarValueType type; /* Type de valeur portée */
+
+ union
+ {
+ unsigned long long unsigned_integer;/* Valeur entière #1 */
+ signed long long signed_integer; /* Valeur entière #2 */
+ double floating_number; /* Valeur à virgule flottante */
+ bool status; /* Valeur à deux états */
+ sized_string_t bytes; /* Série d'octets */
+
+ GKaitaiArray *array; /* Tableau d'éléments divers */
+ GMatchRecord *record; /* Correspondance désignée */
+ GKaitaiStream *stream; /* Flux de données pour Kaitai */
+
+ };
+
+} resolved_value_t;
+
+
+#define COPY_RESOLVED_VALUE(dst, src) \
+ do \
+ { \
+ (dst) = (src); \
+ switch ((dst).type) \
+ { \
+ case GVT_ARRAY: \
+ g_object_ref(G_OBJECT((dst).array)); \
+ break; \
+ case GVT_RECORD: \
+ g_object_ref(G_OBJECT((dst).record)); \
+ break; \
+ case GVT_STREAM: \
+ g_object_ref(G_OBJECT((dst).stream)); \
+ break; \
+ default: \
+ break; \
+ } \
+ } \
+ while (0)
+
+
+#define EXIT_RESOLVED_VALUE(v) \
+ switch ((v).type) \
+ { \
+ case GVT_ARRAY: \
+ g_clear_object(&(v).array); \
+ break; \
+ case GVT_RECORD: \
+ g_clear_object(&(v).record); \
+ break; \
+ case GVT_STREAM: \
+ g_clear_object(&(v).stream); \
+ break; \
+ default: \
+ break; \
+ }
+
+
+/* Interprète une expression en une valeur quelconque. */
+bool resolve_kaitai_expression_as_any(const kaitai_scope_t *, const char *, size_t, resolved_value_t *);
+
+/* Interprète une expression en valeur ciblée entière. */
+bool resolve_kaitai_expression_as_integer(const kaitai_scope_t *, const char *, size_t, resolved_value_t *);
+
+/* Interprète une expression en valeur ciblée booléenne. */
+bool resolve_kaitai_expression_as_boolean(const kaitai_scope_t *, const char *, size_t, resolved_value_t *);
+
+/* Interprète une expression en série d'octets. */
+bool resolve_kaitai_expression_as_bytes(const kaitai_scope_t *, const char *, size_t, resolved_value_t *);
+
+/* Interprète une expression en flux de données pour Kaitai. */
+bool resolve_kaitai_expression_as_stream(const kaitai_scope_t *, const char *, size_t, GKaitaiStream **);
+
+
+
+#endif /* _PLUGINS_KAITAI_EXPRESSION_H */
diff --git a/plugins/kaitai/grammar.y b/plugins/kaitai/grammar.y
new file mode 100644
index 0000000..9e31113
--- /dev/null
+++ b/plugins/kaitai/grammar.y
@@ -0,0 +1,1854 @@
+
+%{
+
+#include "expression.h"
+#include "tokens.h"
+
+
+/* Affiche un message d'erreur suite à l'analyse en échec. */
+static int yyerror(yyscan_t, const kaitai_scope_t *, resolved_value_t *, const char *);
+
+/* Interprète une expression en une valeur quelconque. */
+static bool _resolve_kaitai_expression_as_any(const kaitai_scope_t *, const char *, size_t, resolved_value_t *);
+
+/* Traduit les éventuels champs impliqués dans une expression. */
+static bool reduce_resolved_kaitai_expression(resolved_value_t *);
+
+
+%}
+
+
+%code requires {
+
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void *yyscan_t;
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <glib.h>
+
+#include "expression.h"
+#include "record.h"
+#include "records/item.h"
+#include "records/list.h"
+#include "records/value.h"
+
+}
+
+%union {
+
+ resolved_value_t value; /* Valeur portée */
+
+ unsigned long long unsigned_integer; /* Valeur entière #1 */
+ signed long long signed_integer; /* Valeur entière #2 */
+ double floating_number; /* Valeur à virgule flottante */
+ sized_string_t sized_cstring; /* Chaîne de caractères */
+ char byte; /* Octet unique */
+
+}
+
+
+/**
+ * Cf.
+ * http://stackoverflow.com/questions/34418381/how-to-reference-lex-or-parse-parameters-in-flex-rules/34420950
+ */
+
+%define api.pure full
+
+%parse-param { yyscan_t yyscanner } { const kaitai_scope_t *locals } { resolved_value_t *resolved }
+%lex-param { yyscan_t yyscanner }
+
+%code provides {
+
+#define YY_DECL \
+ int kaitai_lex(YYSTYPE *yylval_param, yyscan_t yyscanner)
+
+YY_DECL;
+
+
+
+#define SET_ERR(out) \
+ out.type = GVT_ERROR
+
+#define EXIT_WITH_ERR(out, lbl) \
+ do \
+ { \
+ SET_ERR(out); \
+ goto exit_ ## lbl; \
+ } \
+ while (0)
+
+#define CHECK_TYPE(arg, tp, out, lbl) \
+ if (arg.type != tp) EXIT_WITH_ERR(out, lbl)
+
+#define CHECK_TYPES(arg, tp1, tp2, out, lbl) \
+ if (arg.type != tp1 && arg.type != tp2) EXIT_WITH_ERR(out, lbl)
+
+#define REDUCE_EXPR(arg, out, lbl) \
+ if (!reduce_resolved_kaitai_expression(&arg)) \
+ EXIT_WITH_ERR(out, lbl)
+
+#define REDUCE_NUMERIC_EXPR(arg, out, lbl) \
+ if (!reduce_resolved_kaitai_expression(&arg)) \
+ EXIT_WITH_ERR(out, lbl); \
+ if (arg.type == GVT_SIGNED_INTEGER && arg.signed_integer >= 0) \
+ { \
+ arg.unsigned_integer = arg.signed_integer; \
+ arg.type = GVT_UNSIGNED_INTEGER; \
+ }
+
+
+#define ARITHMETIC_ADD_CODE(op1, op2, out, lbl) \
+ switch (op1.type) \
+ { \
+ case GVT_UNSIGNED_INTEGER: \
+ if (op2.type == GVT_UNSIGNED_INTEGER) \
+ { \
+ out.unsigned_integer = op1.unsigned_integer + op2.unsigned_integer; \
+ out.type = GVT_UNSIGNED_INTEGER; \
+ } \
+ else if (op2.type == GVT_SIGNED_INTEGER) \
+ { \
+ if (op1.unsigned_integer < -op2.signed_integer) \
+ { \
+ out.signed_integer = op1.unsigned_integer + op2.signed_integer; \
+ out.type = GVT_SIGNED_INTEGER; \
+ } \
+ else \
+ { \
+ out.unsigned_integer = op1.unsigned_integer + op2.signed_integer; \
+ out.type = GVT_UNSIGNED_INTEGER; \
+ } \
+ } \
+ else if (op2.type == GVT_FLOAT) \
+ { \
+ out.floating_number = op1.unsigned_integer + op2.floating_number; \
+ out.type = GVT_FLOAT; \
+ } \
+ else EXIT_WITH_ERR(out, lbl); \
+ break; \
+ \
+ case GVT_SIGNED_INTEGER: \
+ if (op2.type == GVT_UNSIGNED_INTEGER) \
+ { \
+ if (-op1.signed_integer > op2.unsigned_integer) \
+ { \
+ out.signed_integer = op1.signed_integer + op2.unsigned_integer; \
+ out.type = GVT_SIGNED_INTEGER; \
+ } \
+ else \
+ { \
+ out.unsigned_integer = op1.signed_integer + op2.unsigned_integer; \
+ out.type = GVT_UNSIGNED_INTEGER; \
+ } \
+ } \
+ else if (op2.type == GVT_SIGNED_INTEGER) \
+ { \
+ out.signed_integer = op1.signed_integer + op2.signed_integer; \
+ out.type = GVT_SIGNED_INTEGER; \
+ } \
+ else if (op2.type == GVT_FLOAT) \
+ { \
+ out.floating_number = op1.signed_integer + op2.floating_number; \
+ out.type = GVT_FLOAT; \
+ } \
+ else EXIT_WITH_ERR(out, lbl); \
+ break; \
+ \
+ case GVT_FLOAT: \
+ if (op2.type == GVT_UNSIGNED_INTEGER) \
+ { \
+ out.floating_number = op1.floating_number + op2.unsigned_integer; \
+ out.type = GVT_FLOAT; \
+ } \
+ else if (op2.type == GVT_SIGNED_INTEGER) \
+ { \
+ out.floating_number = op1.floating_number + op2.signed_integer; \
+ out.type = GVT_FLOAT; \
+ } \
+ else if (op2.type == GVT_FLOAT) \
+ { \
+ out.floating_number = op1.floating_number + op2.floating_number; \
+ out.type = GVT_FLOAT; \
+ } \
+ else EXIT_WITH_ERR(out, lbl); \
+ break; \
+ \
+ default: \
+ EXIT_WITH_ERR(out, lbl); \
+ break; \
+ \
+ }
+
+
+#define ARITHMETIC_SUB_CODE(op1, op2, out, lbl) \
+ switch (op1.type) \
+ { \
+ case GVT_UNSIGNED_INTEGER: \
+ if (op2.type == GVT_UNSIGNED_INTEGER) \
+ { \
+ if (op1.unsigned_integer < op2.unsigned_integer) \
+ { \
+ out.signed_integer = op1.unsigned_integer - op2.unsigned_integer; \
+ out.type = GVT_SIGNED_INTEGER; \
+ } \
+ else \
+ { \
+ out.unsigned_integer = op1.unsigned_integer - op2.unsigned_integer; \
+ out.type = GVT_UNSIGNED_INTEGER; \
+ } \
+ } \
+ else if (op2.type == GVT_SIGNED_INTEGER) \
+ { \
+ out.unsigned_integer = op1.unsigned_integer - op2.signed_integer; \
+ out.type = GVT_UNSIGNED_INTEGER; \
+ } \
+ else if (op2.type == GVT_FLOAT) \
+ { \
+ out.floating_number = op1.unsigned_integer - op2.floating_number; \
+ out.type = GVT_FLOAT; \
+ } \
+ else EXIT_WITH_ERR(out, lbl); \
+ break; \
+ \
+ case GVT_SIGNED_INTEGER: \
+ if (op2.type == GVT_UNSIGNED_INTEGER) \
+ { \
+ out.signed_integer = op1.signed_integer - op2.unsigned_integer; \
+ out.type = GVT_SIGNED_INTEGER; \
+ } \
+ else if (op2.type == GVT_SIGNED_INTEGER) \
+ { \
+ if (op1.signed_integer < op2.signed_integer) \
+ { \
+ out.signed_integer = op1.signed_integer - op2.signed_integer; \
+ out.type = GVT_SIGNED_INTEGER; \
+ } \
+ else \
+ { \
+ out.unsigned_integer = op1.signed_integer - op2.signed_integer; \
+ out.type = GVT_UNSIGNED_INTEGER; \
+ } \
+ } \
+ else if (op2.type == GVT_FLOAT) \
+ { \
+ out.floating_number = op1.signed_integer - op2.floating_number; \
+ out.type = GVT_FLOAT; \
+ } \
+ else EXIT_WITH_ERR(out, lbl); \
+ break; \
+ \
+ case GVT_FLOAT: \
+ if (op2.type == GVT_UNSIGNED_INTEGER) \
+ { \
+ out.floating_number = op1.floating_number - op2.unsigned_integer; \
+ out.type = GVT_FLOAT; \
+ } \
+ else if (op2.type == GVT_SIGNED_INTEGER) \
+ { \
+ out.floating_number = op1.floating_number - op2.signed_integer; \
+ out.type = GVT_FLOAT; \
+ } \
+ else if (op2.type == GVT_FLOAT) \
+ { \
+ out.floating_number = op1.floating_number - op2.floating_number; \
+ out.type = GVT_FLOAT; \
+ } \
+ else EXIT_WITH_ERR(out, lbl); \
+ break; \
+ \
+ default: \
+ EXIT_WITH_ERR(out, lbl); \
+ break; \
+ \
+ }
+
+
+#define ARITHMETIC_GENOP_CODE(op1, op2, _meth_, out, lbl) \
+ switch (op1.type) \
+ { \
+ case GVT_UNSIGNED_INTEGER: \
+ if (op2.type == GVT_UNSIGNED_INTEGER) \
+ { \
+ out.unsigned_integer = op1.unsigned_integer _meth_ op2.unsigned_integer; \
+ out.type = GVT_UNSIGNED_INTEGER; \
+ } \
+ else if (op2.type == GVT_SIGNED_INTEGER) \
+ { \
+ out.signed_integer = op1.unsigned_integer _meth_ op2.signed_integer; \
+ out.type = GVT_SIGNED_INTEGER; \
+ } \
+ else if (op2.type == GVT_FLOAT) \
+ { \
+ out.floating_number = op1.unsigned_integer _meth_ op2.floating_number; \
+ out.type = GVT_FLOAT; \
+ } \
+ else EXIT_WITH_ERR(out, lbl); \
+ break; \
+ \
+ case GVT_SIGNED_INTEGER: \
+ if (op2.type == GVT_UNSIGNED_INTEGER) \
+ { \
+ out.unsigned_integer = op1.signed_integer _meth_ op2.unsigned_integer; \
+ out.type = GVT_SIGNED_INTEGER; \
+ } \
+ else if (op2.type == GVT_SIGNED_INTEGER) \
+ { \
+ out.unsigned_integer = op1.signed_integer _meth_ op2.signed_integer; \
+ out.type = GVT_UNSIGNED_INTEGER; \
+ } \
+ else if (op2.type == GVT_FLOAT) \
+ { \
+ out.floating_number = op1.signed_integer _meth_ op2.floating_number; \
+ out.type = GVT_FLOAT; \
+ } \
+ else EXIT_WITH_ERR(out, lbl); \
+ break; \
+ \
+ case GVT_FLOAT: \
+ if (op2.type == GVT_UNSIGNED_INTEGER) \
+ { \
+ out.unsigned_integer = op1.floating_number _meth_ op2.unsigned_integer; \
+ out.type = GVT_FLOAT; \
+ } \
+ else if (op2.type == GVT_SIGNED_INTEGER) \
+ { \
+ out.signed_integer = op1.floating_number _meth_ op2.signed_integer; \
+ out.type = GVT_FLOAT; \
+ } \
+ else if (op2.type == GVT_FLOAT) \
+ { \
+ out.floating_number = op1.floating_number _meth_ op2.floating_number; \
+ out.type = GVT_FLOAT; \
+ } \
+ else EXIT_WITH_ERR(out, lbl); \
+ break; \
+ \
+ default: \
+ EXIT_WITH_ERR(out, lbl); \
+ break; \
+ \
+ }
+
+
+/**
+ * Cf. https://stackoverflow.com/questions/11720656/modulo-operation-with-negative-numbers/52529440#52529440
+ */
+#define EUCLIDEAN_MODULO(a, b, r) \
+ r = a % (signed long long)b; \
+ if (r < 0) \
+ r = (b < 0) ? r - b : r + b; \
+
+
+#define ARITHMETIC_MOD_CODE(op1, op2, out, lbl) \
+ switch (op1.type) \
+ { \
+ case GVT_UNSIGNED_INTEGER: \
+ if (op2.type == GVT_UNSIGNED_INTEGER) \
+ { \
+ EUCLIDEAN_MODULO(op1.unsigned_integer, op2.unsigned_integer, out.unsigned_integer); \
+ out.type = GVT_UNSIGNED_INTEGER; \
+ } \
+ else if (op2.type == GVT_SIGNED_INTEGER) \
+ { \
+ EUCLIDEAN_MODULO(op1.unsigned_integer, op2.signed_integer, out.signed_integer); \
+ out.type = GVT_SIGNED_INTEGER; \
+ } \
+ else EXIT_WITH_ERR(out, lbl); \
+ break; \
+ \
+ case GVT_SIGNED_INTEGER: \
+ if (op2.type == GVT_UNSIGNED_INTEGER) \
+ { \
+ EUCLIDEAN_MODULO(op1.signed_integer, op2.unsigned_integer, out.signed_integer); \
+ out.type = GVT_SIGNED_INTEGER; \
+ } \
+ else if (op2.type == GVT_SIGNED_INTEGER) \
+ { \
+ EUCLIDEAN_MODULO(op1.signed_integer, op2.signed_integer, out.signed_integer); \
+ out.type = GVT_SIGNED_INTEGER; \
+ } \
+ else EXIT_WITH_ERR(out, lbl); \
+ break; \
+ \
+ default: \
+ EXIT_WITH_ERR(out, lbl); \
+ break; \
+ \
+ }
+
+
+#define RELATIONAL_CODE(op1, op2, _meth_, out, lbl) \
+ switch (op1.type) \
+ { \
+ case GVT_UNSIGNED_INTEGER: \
+ if (op2.type == GVT_UNSIGNED_INTEGER) \
+ { \
+ out.status = (op1.unsigned_integer _meth_ op2.unsigned_integer); \
+ out.type = GVT_BOOLEAN; \
+ } \
+ else if (op2.type == GVT_SIGNED_INTEGER) \
+ { \
+ out.status = (op1.unsigned_integer _meth_ op2.signed_integer); \
+ out.type = GVT_BOOLEAN; \
+ } \
+ else EXIT_WITH_ERR(out, lbl); \
+ break; \
+ \
+ case GVT_SIGNED_INTEGER: \
+ if (op2.type == GVT_UNSIGNED_INTEGER) \
+ { \
+ out.status = (op1.signed_integer _meth_ op2.unsigned_integer); \
+ out.type = GVT_BOOLEAN; \
+ } \
+ else if (op2.type == GVT_SIGNED_INTEGER) \
+ { \
+ out.status = (op1.signed_integer _meth_ op2.signed_integer); \
+ out.type = GVT_BOOLEAN; \
+ } \
+ else EXIT_WITH_ERR(out, lbl); \
+ break; \
+ \
+ case GVT_BYTES: \
+ if (op2.type == GVT_BYTES) \
+ { \
+ int __ret; \
+ __ret = szmemcmp(&op1.bytes, &op2.bytes); \
+ out.status = (__ret _meth_ 0); \
+ out.type = GVT_BOOLEAN; \
+ } \
+ else if (op2.type == GVT_ARRAY) \
+ { \
+ sized_string_t __abytes_2; \
+ int __ret; \
+ if (!g_kaitai_array_convert_to_bytes(op2.array, &__abytes_2)) \
+ EXIT_WITH_ERR(out, lbl); \
+ __ret = szmemcmp(&op1.bytes, &__abytes_2); \
+ exit_szstr(&__abytes_2); \
+ out.status = (__ret _meth_ 0); \
+ out.type = GVT_BOOLEAN; \
+ } \
+ else EXIT_WITH_ERR(out, lbl); \
+ break; \
+ \
+ case GVT_ARRAY: \
+ if (op2.type == GVT_BYTES) \
+ { \
+ sized_string_t __abytes_1; \
+ int __ret; \
+ if (!g_kaitai_array_convert_to_bytes(op1.array, &__abytes_1)) \
+ EXIT_WITH_ERR(out, lbl); \
+ __ret = szmemcmp(&__abytes_1, &op2.bytes); \
+ exit_szstr(&__abytes_1); \
+ out.status = (__ret _meth_ 0); \
+ out.type = GVT_BOOLEAN; \
+ } \
+ else if (op2.type == GVT_ARRAY) \
+ { \
+ sized_string_t __abytes_1; \
+ sized_string_t __abytes_2; \
+ int __ret; \
+ if (!g_kaitai_array_convert_to_bytes(op1.array, &__abytes_1)) \
+ EXIT_WITH_ERR(out, lbl); \
+ if (!g_kaitai_array_convert_to_bytes(op2.array, &__abytes_2)) \
+ { \
+ exit_szstr(&__abytes_1); \
+ EXIT_WITH_ERR(out, lbl); \
+ } \
+ __ret = szmemcmp(&__abytes_1, &__abytes_2); \
+ exit_szstr(&__abytes_1); \
+ exit_szstr(&__abytes_2); \
+ out.status = (__ret _meth_ 0); \
+ out.type = GVT_BOOLEAN; \
+ } \
+ else EXIT_WITH_ERR(out, lbl); \
+ break; \
+ \
+ case GVT_STREAM: \
+ if (op2.type == GVT_STREAM) \
+ { \
+ GBinContent *__cnt_1; \
+ GBinContent *__cnt_2; \
+ __cnt_1 = g_kaitai_stream_get_content(op1.stream); \
+ __cnt_2 = g_kaitai_stream_get_content(op2.stream); \
+ out.status = (__cnt_1 _meth_ __cnt_2); \
+ out.type = GVT_BOOLEAN; \
+ g_object_unref(G_OBJECT(__cnt_1)); \
+ g_object_unref(G_OBJECT(__cnt_2)); \
+ } \
+ else EXIT_WITH_ERR(out, lbl); \
+ break; \
+ \
+ default: \
+ EXIT_WITH_ERR(out, lbl); \
+ break; \
+ \
+ }
+
+
+#define BITWISE_CODE(op1, op2, _meth_, out, lbl) \
+ switch (op1.type) \
+ { \
+ case GVT_UNSIGNED_INTEGER: \
+ if (op2.type == GVT_UNSIGNED_INTEGER) \
+ { \
+ out.unsigned_integer = (op1.unsigned_integer _meth_ op2.unsigned_integer); \
+ out.type = GVT_UNSIGNED_INTEGER; \
+ } \
+ else EXIT_WITH_ERR(out, lbl); \
+ break; \
+ \
+ case GVT_SIGNED_INTEGER: \
+ if (op2.type == GVT_UNSIGNED_INTEGER) \
+ { \
+ out.signed_integer = (op1.signed_integer _meth_ op2.unsigned_integer); \
+ out.type = GVT_SIGNED_INTEGER; \
+ } \
+ else EXIT_WITH_ERR(out, lbl); \
+ break; \
+ \
+ default: \
+ EXIT_WITH_ERR(out, lbl); \
+ break; \
+ \
+ }
+
+
+
+
+}
+
+
+%token <unsigned_integer> UNSIGNED_INTEGER
+%token <signed_integer> SIGNED_INTEGER
+%token <floating_number> FLOAT
+
+%token <sized_cstring> IDENTIFIER
+%token <sized_cstring> RAW_BYTES
+%token <byte> RAW_BYTE
+%token <sized_cstring> RAW_BYTES_WITH_ENDING_DOT
+%token <sized_cstring> PLAIN_BYTES
+
+%token <sized_cstring> ENCODING_NAME
+
+
+%token PLUS "+"
+%token MINUS "-"
+%token MUL "*"
+%token DIV "/"
+%token MOD "%"
+
+%token LT "<"
+%token LE "<="
+%token EQ "=="
+%token NE "!="
+%token GT ">"
+%token GE ">="
+
+%token SHIFT_LEFT "<<"
+%token SHIFT_RIGHT ">>"
+%token BIT_AND "&"
+%token BIT_OR "|"
+%token BIT_XOR "^"
+
+%token NOT "not"
+%token AND "and"
+%token OR "or"
+
+%token PAREN_O "("
+%token PAREN_C ")"
+%token HOOK_O "["
+%token HOOK_C "]"
+%token COMMA ","
+%token DOT "."
+
+%token QMARK "?"
+%token COLON ":"
+%token DOUBLE_COLON "::"
+
+%token METH_SIZE ".size"
+%token METH_LENGTH ".length"
+%token METH_REVERSE ".reverse"
+%token METH_SUBSTRING ".substring"
+%token METH_TO_I ".to_i"
+%token METH_TO_I_RAD ".to_i("
+%token METH_TO_S ".to_s"
+%token METH_TO_S_ENC ".to_s("
+
+%token ROOT "_root"
+%token PARENT "_parent"
+%token LAST "_"
+%token METH_IO "._io"
+
+%token TRUE_CONST "true"
+%token FALSE_CONST "false"
+
+
+ //%type <value> operand
+%type <value> any_expr
+ //%type <value> arithm_expr
+ //%type <value> arithm_op
+
+%type <value> boolean
+
+
+
+%type <value> arithmetic_expr
+%type <value> relational_expr
+%type <value> logical_expr
+%type <value> bitwise_expr
+%type <value> ternary_expr
+
+%type <value> convert_2_bytes
+%type <value> convert_2_integer
+
+
+%type <value> integer
+
+%type <value> float
+
+%type <value> bytes
+
+%type <value> bytes_concat
+%type <value> raw_bytes
+
+
+%type <value> array
+%type <value> array_items
+
+
+%type <value> field
+%type <value> enumeration
+%type <value> stream
+
+
+
+%destructor { printf("----------------------freeing %p...\n", &$$), fflush(NULL); } <*>
+
+
+ //%type <integer> INTEGER
+ //
+
+ //%type <integer> arithm_expr
+ //%type <integer> arithm_op
+
+ //%type <boolean> bool_expr
+ //%type <boolean> relational_op logical_op ternary_op
+
+
+ //%type <integer> constant
+
+
+
+
+
+/**
+ * Cf. https://en.wikipedia.org/wiki/Operators_in_C_and_C%2B%2B#Operator_precedence
+ */
+
+
+
+
+
+%left "?" ":"
+
+%left OR
+%left "and"
+
+/* 13 */
+%left "|"
+
+/* 12 */
+%left "^"
+
+/* 11 */
+%left "&"
+
+%left LT LE EQ NE GT GE
+
+/* 7 */
+%left "<<" ">>"
+
+
+%right NOT
+
+%left PLUS MINUS
+%left "*"
+%left DIV MOD
+
+
+%left "["
+
+
+
+
+%left ".size"
+%left ".length"
+%left ".reverse"
+%left ".substring"
+%left ".to_i"
+%left ".to_i("
+%left ".to_s"
+%left ".to_s("
+
+%left "._io"
+
+%left "."
+
+/* 1 */
+%right "::"
+
+
+%%
+
+ expressions : any_expr { *resolved = $1; }
+ ;
+
+ any_expr : boolean { $$ = $1; }
+ | bytes { $$ = $1; }
+ | integer { $$ = $1; }
+ | float { $$ = $1; }
+ | array { $$ = $1; }
+ | field { $$ = $1; }
+ | enumeration { $$ = $1; }
+ | stream { $$ = $1; }
+ | arithmetic_expr { $$ = $1; }
+ | relational_expr { $$ = $1; }
+ | logical_expr { $$ = $1; }
+ | bitwise_expr { $$ = $1; }
+ | ternary_expr { $$ = $1; }
+ | convert_2_bytes { $$ = $1; }
+ | convert_2_integer { $$ = $1; }
+ | "(" any_expr ")" { $$ = $2; }
+ ;
+
+
+/* Expressions impliquants formules et opérandes */
+
+ arithmetic_expr : any_expr "+" any_expr
+ {
+ REDUCE_NUMERIC_EXPR($1, $$, arithmetic_expr_plus);
+ REDUCE_NUMERIC_EXPR($3, $$, arithmetic_expr_plus);
+
+ if ($1.type == GVT_BYTES && $3.type == GVT_BYTES)
+ {
+ $$.bytes.len = $1.bytes.len + $3.bytes.len;
+ $$.bytes.data = malloc($$.bytes.len);
+
+ memcpy($$.bytes.data, $1.bytes.data, $1.bytes.len);
+ memcpy($$.bytes.data + $1.bytes.len, $3.bytes.data, $3.bytes.len);
+
+ $$.type = GVT_BYTES;
+
+ }
+
+ else if ($1.type == GVT_BYTES && $3.type == GVT_ARRAY)
+ {
+ sized_string_t __abytes_2;
+
+ if (!g_kaitai_array_convert_to_bytes($3.array, &__abytes_2))
+ EXIT_WITH_ERR($$, arithmetic_expr_plus);
+
+ $$.bytes.len = $1.bytes.len + __abytes_2.len;
+ $$.bytes.data = malloc($$.bytes.len);
+
+ memcpy($$.bytes.data, $1.bytes.data, $1.bytes.len);
+ memcpy($$.bytes.data + $1.bytes.len, __abytes_2.data, __abytes_2.len);
+
+ $$.type = GVT_BYTES;
+
+ exit_szstr(&__abytes_2);
+
+ }
+
+ else
+ {
+ ARITHMETIC_ADD_CODE($1, $3, $$, arithmetic_expr_plus);
+ }
+
+ exit_arithmetic_expr_plus:
+ EXIT_RESOLVED_VALUE($1);
+ EXIT_RESOLVED_VALUE($3);
+ }
+ | any_expr "-" any_expr
+ {
+ REDUCE_NUMERIC_EXPR($1, $$, arithmetic_expr_minus);
+ REDUCE_NUMERIC_EXPR($3, $$, arithmetic_expr_minus);
+ ARITHMETIC_SUB_CODE($1, $3, $$, arithmetic_expr_minus);
+ exit_arithmetic_expr_minus:
+ EXIT_RESOLVED_VALUE($1);
+ EXIT_RESOLVED_VALUE($3);
+ }
+ | any_expr "*" any_expr
+ {
+ REDUCE_NUMERIC_EXPR($1, $$, arithmetic_expr_mul);
+ REDUCE_NUMERIC_EXPR($3, $$, arithmetic_expr_mul);
+ ARITHMETIC_GENOP_CODE($1, $3, *, $$, arithmetic_expr_mul);
+ exit_arithmetic_expr_mul:
+ EXIT_RESOLVED_VALUE($1);
+ EXIT_RESOLVED_VALUE($3);
+ }
+ | any_expr "/" any_expr
+ {
+ REDUCE_NUMERIC_EXPR($1, $$, arithmetic_expr_div);
+ REDUCE_NUMERIC_EXPR($3, $$, arithmetic_expr_div);
+ ARITHMETIC_GENOP_CODE($1, $3, /, $$, arithmetic_expr_div);
+ exit_arithmetic_expr_div:
+ EXIT_RESOLVED_VALUE($1);
+ EXIT_RESOLVED_VALUE($3);
+ }
+ | any_expr "%" any_expr
+ {
+ REDUCE_NUMERIC_EXPR($1, $$, arithmetic_expr_mod);
+ REDUCE_NUMERIC_EXPR($3, $$, arithmetic_expr_mod);
+ ARITHMETIC_MOD_CODE($1, $3, $$, arithmetic_expr_mod);
+ exit_arithmetic_expr_mod:
+ EXIT_RESOLVED_VALUE($1);
+ EXIT_RESOLVED_VALUE($3);
+ }
+ ;
+
+
+ relational_expr : any_expr "<" any_expr
+ {
+ REDUCE_EXPR($1, $$, relational_expr_lt);
+ REDUCE_EXPR($3, $$, relational_expr_lt);
+ RELATIONAL_CODE($1, $3, <, $$, relational_expr_lt);
+ exit_relational_expr_lt:
+ EXIT_RESOLVED_VALUE($1);
+ EXIT_RESOLVED_VALUE($3);
+ }
+ | any_expr "<=" any_expr
+ {
+ REDUCE_EXPR($1, $$, relational_expr_le);
+ REDUCE_EXPR($3, $$, relational_expr_le);
+ RELATIONAL_CODE($1, $3, <=, $$, relational_expr_le);
+ exit_relational_expr_le:
+ EXIT_RESOLVED_VALUE($1);
+ EXIT_RESOLVED_VALUE($3);
+ }
+ | any_expr "==" any_expr
+ {
+ REDUCE_EXPR($1, $$, relational_expr_eq);
+ REDUCE_EXPR($3, $$, relational_expr_eq);
+ RELATIONAL_CODE($1, $3, ==, $$, relational_expr_eq);
+ exit_relational_expr_eq:
+ EXIT_RESOLVED_VALUE($1);
+ EXIT_RESOLVED_VALUE($3);
+ }
+ | any_expr "!=" any_expr
+ {
+ REDUCE_EXPR($1, $$, relational_expr_ne);
+ REDUCE_EXPR($3, $$, relational_expr_ne);
+ RELATIONAL_CODE($1, $3, !=, $$, relational_expr_ne);
+ exit_relational_expr_ne:
+ EXIT_RESOLVED_VALUE($1);
+ EXIT_RESOLVED_VALUE($3);
+ }
+ | any_expr ">" any_expr
+ {
+ REDUCE_EXPR($1, $$, relational_expr_gt);
+ REDUCE_EXPR($3, $$, relational_expr_gt);
+ RELATIONAL_CODE($1, $3, >, $$, relational_expr_gt);
+ exit_relational_expr_gt:
+ EXIT_RESOLVED_VALUE($1);
+ EXIT_RESOLVED_VALUE($3);
+ }
+ | any_expr ">=" any_expr
+ {
+ REDUCE_EXPR($1, $$, relational_expr_ge);
+ REDUCE_EXPR($3, $$, relational_expr_ge);
+ RELATIONAL_CODE($1, $3, >=, $$, relational_expr_ge);
+ exit_relational_expr_ge:
+ EXIT_RESOLVED_VALUE($1);
+ EXIT_RESOLVED_VALUE($3);
+ }
+ ;
+
+
+ bitwise_expr : any_expr "<<" any_expr
+ {
+ REDUCE_NUMERIC_EXPR($1, $$, bitwise_expr_shift_left);
+ REDUCE_NUMERIC_EXPR($3, $$, bitwise_expr_shift_left);
+ BITWISE_CODE($1, $3, <<, $$, bitwise_expr_shift_left);
+ exit_bitwise_expr_shift_left:
+ EXIT_RESOLVED_VALUE($1);
+ EXIT_RESOLVED_VALUE($3);
+ }
+ | any_expr ">>" any_expr
+ {
+ REDUCE_NUMERIC_EXPR($1, $$, bitwise_expr_shift_right);
+ REDUCE_NUMERIC_EXPR($3, $$, bitwise_expr_shift_right);
+ BITWISE_CODE($1, $3, >>, $$, bitwise_expr_shift_right);
+ exit_bitwise_expr_shift_right:
+ EXIT_RESOLVED_VALUE($1);
+ EXIT_RESOLVED_VALUE($3);
+ }
+ | any_expr "&" any_expr
+ {
+ REDUCE_NUMERIC_EXPR($1, $$, bitwise_expr_and);
+ REDUCE_NUMERIC_EXPR($3, $$, bitwise_expr_and);
+ BITWISE_CODE($1, $3, &, $$, bitwise_expr_and);
+ exit_bitwise_expr_and:
+ EXIT_RESOLVED_VALUE($1);
+ EXIT_RESOLVED_VALUE($3);
+ }
+ | any_expr "|" any_expr
+ {
+ REDUCE_NUMERIC_EXPR($1, $$, bitwise_expr_or);
+ REDUCE_NUMERIC_EXPR($3, $$, bitwise_expr_or);
+ BITWISE_CODE($1, $3, |, $$, bitwise_expr_or);
+ exit_bitwise_expr_or:
+ EXIT_RESOLVED_VALUE($1);
+ EXIT_RESOLVED_VALUE($3);
+ }
+ | any_expr "^" any_expr
+ {
+ REDUCE_NUMERIC_EXPR($1, $$, bitwise_expr_xor);
+ REDUCE_NUMERIC_EXPR($3, $$, bitwise_expr_xor);
+ BITWISE_CODE($1, $3, ^, $$, bitwise_expr_xor);
+ exit_bitwise_expr_xor:
+ EXIT_RESOLVED_VALUE($1);
+ EXIT_RESOLVED_VALUE($3);
+ }
+ ;
+
+
+ logical_expr : "not" any_expr
+ {
+ REDUCE_EXPR($2, $$, logical_expr_not);
+ CHECK_TYPE($2, GVT_BOOLEAN, $$, logical_expr_not);
+ $$.status = !$2.status;
+ $$.type = GVT_BOOLEAN;
+ exit_logical_expr_not:
+ EXIT_RESOLVED_VALUE($2);
+ }
+ | any_expr "and" any_expr
+ {
+ REDUCE_EXPR($1, $$, logical_expr_and);
+ CHECK_TYPE($1, GVT_BOOLEAN, $$, logical_expr_and);
+ REDUCE_EXPR($3, $$, logical_expr_and);
+ CHECK_TYPE($3, GVT_BOOLEAN, $$, logical_expr_and);
+ $$.status = $1.status && $3.status;
+ $$.type = GVT_BOOLEAN;
+ exit_logical_expr_and:
+ EXIT_RESOLVED_VALUE($1);
+ EXIT_RESOLVED_VALUE($3);
+ }
+ | any_expr "or" any_expr
+ {
+ REDUCE_EXPR($1, $$, logical_expr_or);
+ CHECK_TYPE($1, GVT_BOOLEAN, $$, logical_expr_or);
+ REDUCE_EXPR($3, $$, logical_expr_or);
+ CHECK_TYPE($3, GVT_BOOLEAN, $$, logical_expr_or);
+ $$.status = $1.status || $3.status;
+ $$.type = GVT_BOOLEAN;
+ exit_logical_expr_or:
+ EXIT_RESOLVED_VALUE($1);
+ EXIT_RESOLVED_VALUE($3);
+ }
+ ;
+
+
+ ternary_expr : any_expr "?" any_expr ":" any_expr
+ {
+ REDUCE_EXPR($1, $$, ternary_expr);
+ CHECK_TYPE($1, GVT_BOOLEAN, $$, ternary_expr);
+ if ($1.status)
+ COPY_RESOLVED_VALUE($$, $3);
+ else
+ COPY_RESOLVED_VALUE($$, $5);
+ exit_ternary_expr:
+ EXIT_RESOLVED_VALUE($1);
+ EXIT_RESOLVED_VALUE($3);
+ EXIT_RESOLVED_VALUE($5);
+ }
+ ;
+
+
+/* Conversions et méthodes particulières de types */
+
+ convert_2_bytes : any_expr ".to_s"
+ {
+ int __ret;
+
+ if ($1.type == GVT_UNSIGNED_INTEGER)
+ {
+ __ret = asprintf(&$$.bytes.data, "%llu", $1.unsigned_integer);
+ if (__ret == -1) EXIT_WITH_ERR($$, convert_2_bytes_to_s);
+
+ $$.bytes.len = __ret;
+ $$.type = GVT_BYTES;
+
+ }
+ else if ($1.type == GVT_SIGNED_INTEGER)
+ {
+ __ret = asprintf(&$$.bytes.data, "%lld", $1.signed_integer);
+ if (__ret == -1) EXIT_WITH_ERR($$, convert_2_bytes_to_s);
+
+ $$.bytes.len = __ret;
+ $$.type = GVT_BYTES;
+
+ }
+ else
+ EXIT_WITH_ERR($$, convert_2_bytes_to_s);
+
+ exit_convert_2_bytes_to_s:
+ EXIT_RESOLVED_VALUE($1);
+ }
+ | any_expr ".to_s(" ENCODING_NAME ")"
+ {
+ /**
+ * Cf. https://fossies.org/linux/libiconv/man/iconv_open.3.html
+ */
+
+ char *__fromcode;
+ gsize __bytes_read;
+ gsize __bytes_written;
+
+ if ($1.type != GVT_BYTES)
+ EXIT_WITH_ERR($$, convert_2_bytes_to_s_encoding);
+
+ __fromcode = strndup($3.data, $3.len);
+
+ $$.bytes.data = g_convert($1.bytes.data, $1.bytes.len,
+ __fromcode, "", &__bytes_read, &__bytes_written, NULL);
+
+ free(__fromcode);
+
+ if (__bytes_read != $1.bytes.len)
+ EXIT_WITH_ERR($$, convert_2_bytes_to_s_encoding);
+
+ $$.bytes.len = __bytes_written;
+ $$.type = GVT_BYTES;
+
+ exit_convert_2_bytes_to_s_encoding:
+ EXIT_RESOLVED_VALUE($1);
+ }
+ ;
+
+
+ convert_2_integer : any_expr ".length"
+ {
+ if ($1.type != GVT_BYTES)
+ EXIT_WITH_ERR($$, convert_2_integer_to_s);
+
+ $$.unsigned_integer = $1.bytes.len;
+ $$.type = GVT_UNSIGNED_INTEGER;
+
+ exit_convert_2_integer_to_s:
+ EXIT_RESOLVED_VALUE($1);
+ }
+ | any_expr ".to_i"
+ {
+ char *__n;
+ char *__end;
+
+ if ($1.type == GVT_FLOAT)
+ {
+ if ($1.floating_number < 0)
+ {
+ $$.signed_integer = $1.floating_number;
+ $$.type = GVT_SIGNED_INTEGER;
+ }
+ else
+ {
+ $$.unsigned_integer = $1.floating_number;
+ $$.type = GVT_UNSIGNED_INTEGER;
+ }
+
+ }
+
+ else if ($1.type == GVT_BOOLEAN)
+ {
+ $$.unsigned_integer = $1.status ? 1 : 0;
+ $$.type = GVT_UNSIGNED_INTEGER;
+ }
+
+ else if ($1.type == GVT_BYTES)
+ {
+ if ($1.bytes.len == 0)
+ EXIT_WITH_ERR($$, convert_2_integer_to_i);
+
+ __n = malloc($1.bytes.len + 1);
+ memcpy(__n, $1.bytes.data, $1.bytes.len);
+ __n[$1.bytes.len] = '\0';
+
+ if ($1.bytes.data[0] == '-')
+ {
+ if ($1.bytes.len == 1)
+ EXIT_WITH_ERR($$, convert_2_integer_to_i);
+
+ $$.signed_integer = strtoll(__n, &__end, 10);
+ $$.type = GVT_SIGNED_INTEGER;
+
+ if (errno == EINVAL || errno == ERANGE)
+ EXIT_WITH_ERR($$, convert_2_integer_to_i);
+
+ if (__end != &__n[$1.bytes.len])
+ EXIT_WITH_ERR($$, convert_2_integer_to_i);
+
+ }
+ else
+ {
+ if ($1.bytes.len == 1)
+ EXIT_WITH_ERR($$, convert_2_integer_to_i);
+
+ $$.unsigned_integer = strtoull(__n, &__end, 10);
+ $$.type = GVT_UNSIGNED_INTEGER;
+
+ if (errno == EINVAL || errno == ERANGE)
+ EXIT_WITH_ERR($$, convert_2_integer_to_i);
+
+ if (__end != &__n[$1.bytes.len])
+ EXIT_WITH_ERR($$, convert_2_integer_to_i);
+
+ }
+
+ free(__n);
+
+ }
+
+ else EXIT_WITH_ERR($$, convert_2_integer_to_i);
+
+ exit_convert_2_integer_to_i:
+ EXIT_RESOLVED_VALUE($1);
+ }
+ | any_expr ".to_i(" any_expr ")"
+ {
+ int __base;
+ char *__n;
+ char *__end;
+
+ if ($1.type == GVT_BYTES)
+ {
+ if ($1.bytes.len == 0)
+ EXIT_WITH_ERR($$, convert_2_integer_to_i_base);
+
+ if ($3.type == GVT_UNSIGNED_INTEGER)
+ {
+ __base = $3.unsigned_integer;
+ if (__base < 2)
+ EXIT_WITH_ERR($$, convert_2_integer_to_i_base);
+ }
+ else if ($3.type == GVT_SIGNED_INTEGER)
+ {
+ __base = $3.signed_integer;
+ if (__base < 2)
+ EXIT_WITH_ERR($$, convert_2_integer_to_i_base);
+ }
+ else EXIT_WITH_ERR($$, convert_2_integer_to_i_base);
+
+ __n = malloc($1.bytes.len + 1);
+ memcpy(__n, $1.bytes.data, $1.bytes.len);
+ __n[$1.bytes.len] = '\0';
+
+ if ($1.bytes.data[0] == '-')
+ {
+ if ($1.bytes.len == 1)
+ EXIT_WITH_ERR($$, convert_2_integer_to_i_base);
+
+ $$.signed_integer = strtoll(__n, &__end, __base);
+ $$.type = GVT_SIGNED_INTEGER;
+
+ if (errno == EINVAL || errno == ERANGE)
+ EXIT_WITH_ERR($$, convert_2_integer_to_i_base);
+
+ if (__end != &__n[$1.bytes.len])
+ EXIT_WITH_ERR($$, convert_2_integer_to_i_base);
+
+ }
+ else
+ {
+ if ($1.bytes.len == 1)
+ EXIT_WITH_ERR($$, convert_2_integer_to_i_base);
+
+ $$.unsigned_integer = strtoull(__n, &__end, __base);
+ $$.type = GVT_UNSIGNED_INTEGER;
+
+ if (errno == EINVAL || errno == ERANGE)
+ EXIT_WITH_ERR($$, convert_2_integer_to_i_base);
+
+ if (__end != &__n[$1.bytes.len])
+ EXIT_WITH_ERR($$, convert_2_integer_to_i_base);
+
+ }
+
+ free(__n);
+
+ }
+
+ else EXIT_WITH_ERR($$, convert_2_integer_to_i_base);
+
+ exit_convert_2_integer_to_i_base:
+ EXIT_RESOLVED_VALUE($1);
+ }
+ | any_expr ".size"
+ {
+ GRecordList *__list;
+
+ if ($1.type != GVT_RECORD) EXIT_WITH_ERR($$, convert_2_integer_size);
+ if (!G_IS_RECORD_LIST($1.record)) EXIT_WITH_ERR($$, convert_2_integer_size);
+
+ __list = G_RECORD_LIST($1.record);
+
+ $$.unsigned_integer = g_record_list_count_records(__list);
+ $$.type = GVT_UNSIGNED_INTEGER;
+
+ exit_convert_2_integer_size:
+ EXIT_RESOLVED_VALUE($1);
+ }
+ ;
+
+
+/* Types de base */
+
+ boolean : "true"
+ {
+ $$.status = true;
+ $$.type = GVT_BOOLEAN;
+ }
+ | "false"
+ {
+ $$.status = false;
+ $$.type = GVT_BOOLEAN;
+ }
+ ;
+
+
+ integer : UNSIGNED_INTEGER
+ {
+ $$.unsigned_integer = $1;
+ $$.type = GVT_UNSIGNED_INTEGER;
+ }
+ | SIGNED_INTEGER
+ {
+ $$.signed_integer = $1;
+ $$.type = GVT_SIGNED_INTEGER;
+ }
+ ;
+
+
+ float : FLOAT
+ {
+ $$.floating_number = $1;
+ $$.type = GVT_FLOAT;
+ }
+ ;
+
+
+ bytes : bytes_concat { $$ = $1; }
+ | PLAIN_BYTES
+ {
+ $$.bytes.len = $1.len;
+ $$.bytes.data = malloc($1.len);
+ memcpy($$.bytes.data, $1.data, $1.len);
+ $$.type = GVT_BYTES;
+ }
+ | any_expr ".reverse"
+ {
+ size_t __i;
+
+ CHECK_TYPE($1, GVT_BYTES, $$, bytes_reverse);
+
+ $$.bytes.data = malloc($1.bytes.len);
+ $$.bytes.len = $1.bytes.len;
+
+ for (__i = 0; __i < $1.bytes.len; __i++)
+ $$.bytes.data[__i] = $1.bytes.data[$1.bytes.len - __i - 1];
+
+ $$.type = GVT_BYTES;
+
+ exit_bytes_reverse:
+ EXIT_RESOLVED_VALUE($1);
+ }
+ | any_expr ".substring" "(" any_expr "," any_expr ")"
+ {
+ unsigned long long __from;
+ unsigned long long __to;
+
+ REDUCE_NUMERIC_EXPR($4, $$, bytes_reverse);
+ CHECK_TYPES($4, GVT_UNSIGNED_INTEGER, GVT_SIGNED_INTEGER, $$, bytes_substring);
+ REDUCE_NUMERIC_EXPR($6, $$, bytes_reverse);
+ CHECK_TYPES($6, GVT_UNSIGNED_INTEGER, GVT_SIGNED_INTEGER, $$, bytes_substring);
+
+ __from = ($4.type == GVT_UNSIGNED_INTEGER ? $4.unsigned_integer : $4.signed_integer);
+ __to = ($6.type == GVT_UNSIGNED_INTEGER ? $6.unsigned_integer : $6.signed_integer);
+
+ if (__from > __to) EXIT_WITH_ERR($$, bytes_substring);
+ if (__to >= $1.bytes.len) EXIT_WITH_ERR($$, bytes_substring);
+
+ $$.bytes.len = __to - __from + 1;
+ $$.bytes.data = malloc($$.bytes.len);
+
+ memcpy($$.bytes.data, &$1.bytes.data[__from], $$.bytes.len);
+
+ $$.type = GVT_BYTES;
+
+ exit_bytes_substring:
+ EXIT_RESOLVED_VALUE($1);
+ EXIT_RESOLVED_VALUE($4);
+ EXIT_RESOLVED_VALUE($6);
+ }
+ ;
+
+ bytes_concat : raw_bytes { $$ = $1; };
+ | bytes_concat raw_bytes
+ {
+ $$.bytes.len = $1.bytes.len + $2.bytes.len;
+ $$.bytes.data = malloc($$.bytes.len);
+ memcpy($$.bytes.data, $1.bytes.data, $1.bytes.len);
+ memcpy($$.bytes.data + $1.bytes.len, $2.bytes.data, $2.bytes.len);
+ $$.type = GVT_BYTES;
+ EXIT_RESOLVED_VALUE($1);
+ EXIT_RESOLVED_VALUE($2);
+ }
+ ;
+
+ raw_bytes : RAW_BYTES
+ {
+ $$.bytes.len = $1.len;
+ $$.bytes.data = malloc($1.len);
+ memcpy($$.bytes.data, $1.data, $1.len);
+ $$.type = GVT_BYTES;
+ }
+ | RAW_BYTE
+ {
+ $$.bytes.len = 1;
+ $$.bytes.data = malloc(1);
+ $$.bytes.data[0] = $1;
+ $$.type = GVT_BYTES;
+ }
+ | RAW_BYTES_WITH_ENDING_DOT
+ {
+ $$.bytes.len = $1.len;
+ $$.bytes.data = malloc($1.len);
+ memcpy($$.bytes.data, $1.data, $1.len);
+ $$.type = GVT_BYTES;
+ }
+ ;
+
+
+/* Tableau d'éléments variés */
+
+ array : "[" "]"
+ {
+ $$.array = g_kaitai_array_new();
+ $$.type = GVT_ARRAY;
+ }
+ | "[" array_items "]"
+ {
+ $$ = $2;
+ }
+ ;
+
+
+ array_items : any_expr
+ {
+ $$.array = g_kaitai_array_new();
+ $$.type = GVT_ARRAY;
+
+ g_kaitai_array_append_item($$.array, &$1);
+
+ EXIT_RESOLVED_VALUE($1);
+
+ }
+ | array_items "," any_expr
+ {
+ $$ = $1;
+ g_kaitai_array_append_item($$.array, &$3);
+ EXIT_RESOLVED_VALUE($3);
+ }
+ ;
+
+
+/* Accès aux objets Kaitai manipulés */
+
+ field : IDENTIFIER
+ {
+ $$.record = g_match_record_find_by_name(locals->parent,
+ $1.data, $1.len,
+ DIRECT_SEARCH_DEEP_LEVEL);
+
+ if ($$.record != NULL)
+ $$.type = GVT_RECORD;
+
+ /* Si aucune correspondance, le contenu brut est utilisé */
+ else
+ {
+ $$.bytes.len = $1.len;
+ $$.bytes.data = malloc($1.len);
+ memcpy($$.bytes.data, $1.data, $1.len);
+ $$.type = GVT_BYTES;
+ }
+
+ }
+ | "_root"
+ {
+ $$.record = get_root_record(locals);
+ if ($$.record == NULL) SET_ERR($$);
+ else $$.type = GVT_RECORD;
+ }
+ | "_parent"
+ {
+ $$.record = get_parent_record(locals);
+ if ($$.record == NULL) SET_ERR($$);
+ else $$.type = GVT_RECORD;
+ }
+ | "_"
+ {
+ $$.record = get_last_record(locals);
+ if ($$.record == NULL) SET_ERR($$);
+ else $$.type = GVT_RECORD;
+ }
+ | any_expr "." IDENTIFIER
+ {
+ if ($1.type != GVT_RECORD)
+ EXIT_WITH_ERR($$, field_dot);
+
+ $$.record = g_match_record_find_by_name($1.record,
+ $3.data, $3.len,
+ DIRECT_SEARCH_DEEP_LEVEL);
+
+ if ($$.record == NULL)
+ EXIT_WITH_ERR($$, field_dot);
+
+ $$.type = GVT_RECORD;
+
+ exit_field_dot:
+ EXIT_RESOLVED_VALUE($1);
+ }
+ | any_expr "[" any_expr "]"
+ {
+ size_t __index;
+ GRecordList *__list;
+ size_t __count;
+ GKaitaiArray *__array;
+
+ /* Indice de l'élément auquel accéder */
+
+ REDUCE_NUMERIC_EXPR($3, $$, field_indexed);
+
+ if ($3.type == GVT_UNSIGNED_INTEGER)
+ __index = $3.unsigned_integer;
+ else
+ EXIT_WITH_ERR($$, field_indexed);
+
+ /* Série à consulter */
+
+ REDUCE_EXPR($1, $$, field_indexed);
+
+ if ($1.type == GVT_RECORD && G_IS_RECORD_LIST($1.record))
+ {
+ __list = G_RECORD_LIST($1.record);
+ __count = g_record_list_count_records(__list);
+
+ if (__index >= __count)
+ EXIT_WITH_ERR($$, field_indexed);
+
+ $$.record = g_record_list_get_record(__list, __index);
+
+ if ($$.record == NULL)
+ EXIT_WITH_ERR($$, field_indexed);
+
+ $$.type = GVT_RECORD;
+
+ }
+
+ else if ($1.type == GVT_ARRAY)
+ {
+ __array = G_KAITAI_ARRAY($1.array);
+ __count = g_kaitai_array_count_items(__array);
+
+ if (__index >= __count)
+ EXIT_WITH_ERR($$, field_indexed);
+
+ if (!g_kaitai_array_get_item(__array, __index, &$$))
+ EXIT_WITH_ERR($$, field_indexed);
+
+ }
+
+ else
+ EXIT_WITH_ERR($$, field_indexed);
+
+ exit_field_indexed:
+ EXIT_RESOLVED_VALUE($1);
+ EXIT_RESOLVED_VALUE($3);
+ }
+ ;
+
+
+ enumeration : IDENTIFIER "::" IDENTIFIER
+ {
+ if (!g_match_record_resolve_enum(locals->parent, &$1, &$3, &$$))
+ SET_ERR($$);
+ }
+
+
+ stream : any_expr "._io"
+ {
+ GBinContent *__content;
+ mrange_t __range;
+
+ if ($1.type != GVT_RECORD)
+ EXIT_WITH_ERR($$, stream_io);
+
+ __content = g_match_record_get_content($1.record);
+ g_match_record_get_range($1.record, &__range);
+
+ $$.stream = g_kaitai_stream_new(__content, get_mrange_addr(&__range));
+ $$.type = GVT_STREAM;
+
+ g_object_unref(G_OBJECT(__content));
+
+ exit_stream_io:
+ EXIT_RESOLVED_VALUE($1);
+ }
+ ;
+
+
+%%
+
+
+/******************************************************************************
+* *
+* Paramètres : yyscanner = décodeur impliqué dans le processus. *
+* locals = variables locales pour les résolutions de types. *
+* out = valeur entière résultante. [OUT] *
+* msg = message d'erreur. *
+* *
+* Description : Affiche un message d'erreur suite à l'analyse en échec. *
+* *
+* Retour : 0 *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static int yyerror(yyscan_t yyscanner, const kaitai_scope_t *locals, resolved_value_t *resolved, const char *msg)
+{
+ printf("YYERROR line %d: %s\n", yyget_lineno(yyscanner), msg);
+
+ return 0;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : locals = variables locales pour les résolutions de types. *
+* text = définitions des règles à charger. *
+* length = longueur de ces définitions. *
+* out = valeur générique résultante. [OUT] *
+* *
+* Description : Interprète une expression en une valeur quelconque. *
+* *
+* Retour : Bilan à retourner. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool _resolve_kaitai_expression_as_any(const kaitai_scope_t *locals, const char *text, size_t length, resolved_value_t *out)
+{
+ bool result; /* Bilan à renvoyer */
+ yyscan_t lexstate; /* Gestion d'analyse lexicale */
+ char *real_text; /* Zone de travail effective */
+ size_t real_length; /* Taille associée */
+ YY_BUFFER_STATE state; /* Contexte d'analyse */
+ int status; /* Bilan d'une analyse */
+
+ result = false;
+
+ kaitai_lex_init(&lexstate);
+
+ assert(length > 0);
+
+ if (text[length - 1] == '.')
+ {
+ /**
+ * Si le contenu à analyser se termine par un point, la position finale
+ * de ce point est prise en compte. Pour ce faire, le marqueur "$" des
+ * expressions régulières est sollicité. Hors, ce dernier n'est reconnu
+ * que pour le caractère "\n" terminant une ligne.
+ *
+ * On l'ajoute donc artificiellement.
+ */
+
+ real_length = length + 1;
+
+ real_text = malloc(real_length);
+ memcpy(real_text, text, length);
+ real_text[length] = '\n';
+
+ }
+ else
+ {
+ real_text = (char *)text;
+ real_length = length;
+ }
+
+ state = kaitai__scan_bytes(real_text, real_length, lexstate);
+
+ if (text[length - 1] == '.')
+ free(real_text);
+
+ status = yyparse(lexstate, locals, out);
+
+ result = (status == EXIT_SUCCESS);
+
+ yy_delete_buffer(state, lexstate);
+
+ kaitai_lex_destroy(lexstate);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : locals = variables locales pour les résolutions de types. *
+* text = définitions des règles à charger. *
+* length = longueur de ces définitions. *
+* out = valeur générique résultante. [OUT] *
+* *
+* Description : Interprète une expression en une valeur quelconque. *
+* *
+* Retour : Bilan à retourner. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool resolve_kaitai_expression_as_any(const kaitai_scope_t *locals, const char *text, size_t length, resolved_value_t *out)
+{
+ bool result; /* Bilan à renvoyer */
+
+ result = _resolve_kaitai_expression_as_any(locals, text, length, out);
+
+ return result;
+
+}
+
+
+
+/******************************************************************************
+* *
+* Paramètres : in_out = expression résolue traitée. [OUT] *
+* *
+* Description : Traduit les éventuels champs impliqués dans une expression. *
+* *
+* Retour : Bilan à retourner. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool reduce_resolved_kaitai_expression(resolved_value_t *in_out)
+{
+ bool result; /* Bilan à renvoyer */
+ resolved_value_t deeper; /* Précision supplémentaire */
+
+ result = true;
+
+ while (result && in_out->type == GVT_RECORD)
+ {
+ if (G_IS_RECORD_VALUE(in_out->record))
+ result = g_record_value_compute_value(G_RECORD_VALUE(in_out->record), &deeper);
+
+ else if (G_IS_RECORD_ITEM(in_out->record))
+ result = g_record_item_get_value(G_RECORD_ITEM(in_out->record), &deeper);
+
+ else
+ break;
+
+ if (result)
+ {
+ EXIT_RESOLVED_VALUE(*in_out);
+ *in_out = deeper;
+ }
+
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : locals = variables locales pour les résolutions de types. *
+* text = définitions des règles à charger. *
+* length = longueur de ces définitions. *
+* out = valeur entière résultante. [OUT] *
+* *
+* Description : Interprète une expression en valeur ciblée entière. *
+* *
+* Retour : Bilan à retourner. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool resolve_kaitai_expression_as_integer(const kaitai_scope_t *locals, const char *text, size_t length, resolved_value_t *out)
+{
+ bool result; /* Bilan à renvoyer */
+
+ result = _resolve_kaitai_expression_as_any(locals, text, length, out);
+
+ if (result)
+ result = reduce_resolved_kaitai_expression(out);
+
+ if (result)
+ result = (out->type == GVT_UNSIGNED_INTEGER || out->type == GVT_SIGNED_INTEGER);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : locals = variables locales pour les résolutions de types. *
+* text = définitions des règles à charger. *
+* length = longueur de ces définitions. *
+* out = valeur booléenne résultante. [OUT] *
+* *
+* Description : Interprète une expression en valeur ciblée booléenne. *
+* *
+* Retour : Bilan à retourner. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool resolve_kaitai_expression_as_boolean(const kaitai_scope_t *locals, const char *text, size_t length, resolved_value_t *out)
+{
+ bool result; /* Bilan à renvoyer */
+
+ result = _resolve_kaitai_expression_as_any(locals, text, length, out);
+
+ if (result)
+ result = reduce_resolved_kaitai_expression(out);
+
+ if (result && out->type != GVT_BOOLEAN)
+ {
+ EXIT_RESOLVED_VALUE(*out);
+ result = false;
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : locals = variables locales pour les résolutions de types. *
+* text = définitions des règles à charger. *
+* length = longueur de ces définitions. *
+* out = valeur booléenne résultante. [OUT] *
+* *
+* Description : Interprète une expression en série d'octets. *
+* *
+* Retour : Bilan à retourner. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool resolve_kaitai_expression_as_bytes(const kaitai_scope_t *locals, const char *text, size_t length, resolved_value_t *out)
+{
+ bool result; /* Bilan à renvoyer */
+ char ch; /* Caractère unique spécifié */
+ sized_string_t converted; /* Conversion finale ? */
+
+ result = _resolve_kaitai_expression_as_any(locals, text, length, out);
+
+ if (result)
+ result = reduce_resolved_kaitai_expression(out);
+
+ if (result)
+ {
+ if (out->type == GVT_UNSIGNED_INTEGER)
+ {
+ ch = out->unsigned_integer;
+ result = (ch <= 0xff);
+
+ if (result)
+ {
+ EXIT_RESOLVED_VALUE(*out);
+
+ out->bytes.data = malloc(sizeof(char));
+ out->bytes.data[0] = ch;
+ out->bytes.len = 1;
+ out->type = GVT_BYTES;
+
+ }
+
+ }
+
+ else if (out->type == GVT_ARRAY)
+ {
+ result = g_kaitai_array_convert_to_bytes(out->array, &converted);
+
+ if (result)
+ {
+ EXIT_RESOLVED_VALUE(*out);
+
+ out->bytes = converted;
+ out->type = GVT_BYTES;
+
+ }
+
+ }
+
+ }
+
+ if (result && out->type != GVT_BYTES)
+ {
+ EXIT_RESOLVED_VALUE(*out);
+ result = false;
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : locals = variables locales pour les résolutions de types. *
+* text = définitions des règles à charger. *
+* length = longueur de ces définitions. *
+* stream = flux de données pour Kaitai résultant. [OUT] *
+* *
+* Description : Interprète une expression en flux de données pour Kaitai. *
+* *
+* Retour : Bilan à retourner. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool resolve_kaitai_expression_as_stream(const kaitai_scope_t *locals, const char *text, size_t length, GKaitaiStream **stream)
+{
+ bool result; /* Bilan à renvoyer */
+ resolved_value_t out; /* Elément générique obtenu */
+
+ result = _resolve_kaitai_expression_as_any(locals, text, length, &out);
+
+ if (result)
+ {
+ assert(out.type == GVT_STREAM);
+ *stream = out.stream;
+ }
+ else
+ *stream = NULL;
+
+ return result;
+
+}
diff --git a/plugins/kaitai/parser-int.h b/plugins/kaitai/parser-int.h
new file mode 100644
index 0000000..4ddb0f9
--- /dev/null
+++ b/plugins/kaitai/parser-int.h
@@ -0,0 +1,55 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * parser-int.h - prototypes pour les spécifications internes d'un lecteur 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/>.
+ */
+
+
+#ifndef PLUGINS_KAITAI_PARSER_INT_H
+#define PLUGINS_KAITAI_PARSER_INT_H
+
+
+#include "parser.h"
+
+
+
+/* Parcourt un contenu binaire selon des spécifications Kaitai. */
+typedef bool (* parse_kaitai_fc) (GKaitaiParser *, kaitai_scope_t *, GBinContent *, vmpa2t *, GMatchRecord **);
+
+
+
+/* Spécification d'un lecteur Kaitai (instance) */
+struct _GKaitaiParser
+{
+ GObject parent; /* A laisser en premier */
+
+};
+
+/* Spécification d'un lecteur Kaitai (classe) */
+struct _GKaitaiParserClass
+{
+ GObjectClass parent; /* A laisser en premier */
+
+ parse_kaitai_fc parse; /* Phase d'analyse de contenu */
+
+};
+
+
+
+#endif /* PLUGINS_KAITAI_PARSER_INT_H */
diff --git a/plugins/kaitai/parser.c b/plugins/kaitai/parser.c
new file mode 100644
index 0000000..77c15b3
--- /dev/null
+++ b/plugins/kaitai/parser.c
@@ -0,0 +1,159 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * parser.c - spécification d'un lecteur 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 "parser.h"
+
+
+#include "parser-int.h"
+
+
+
+/* Initialise la classe des lecteurs de spécification Kaitai. */
+static void g_kaitai_parser_class_init(GKaitaiParserClass *);
+
+/* Initialise un lecteur de spécification Kaitai. */
+static void g_kaitai_parser_init(GKaitaiParser *);
+
+/* Supprime toutes les références externes. */
+static void g_kaitai_parser_dispose(GKaitaiParser *);
+
+/* Procède à la libération totale de la mémoire. */
+static void g_kaitai_parser_finalize(GKaitaiParser *);
+
+
+
+/* Indique le type défini pour un lecteur de spécification Kaitai. */
+G_DEFINE_TYPE(GKaitaiParser, g_kaitai_parser, G_TYPE_OBJECT);
+
+
+/******************************************************************************
+* *
+* Paramètres : klass = classe à initialiser. *
+* *
+* Description : Initialise la classe des lecteurs de spécification Kaitai. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_kaitai_parser_class_init(GKaitaiParserClass *klass)
+{
+ GObjectClass *object; /* Autre version de la classe */
+
+ object = G_OBJECT_CLASS(klass);
+
+ object->dispose = (GObjectFinalizeFunc/* ! */)g_kaitai_parser_dispose;
+ object->finalize = (GObjectFinalizeFunc)g_kaitai_parser_finalize;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : parser = instance à initialiser. *
+* *
+* Description : Initialise un lecteur de spécification Kaitai. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_kaitai_parser_init(GKaitaiParser *parser)
+{
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : parser = instance d'objet GLib à traiter. *
+* *
+* Description : Supprime toutes les références externes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_kaitai_parser_dispose(GKaitaiParser *parser)
+{
+ G_OBJECT_CLASS(g_kaitai_parser_parent_class)->dispose(G_OBJECT(parser));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : parser = instance d'objet GLib à traiter. *
+* *
+* Description : Procède à la libération totale de la mémoire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_kaitai_parser_finalize(GKaitaiParser *parser)
+{
+ G_OBJECT_CLASS(g_kaitai_parser_parent_class)->finalize(G_OBJECT(parser));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : parser = structure Kaitai en cours de parcours. *
+* locals = variables locales pour les résolutions de types. *
+* content = données binaires à analyser et traduire. *
+* pos = 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 : - *
+* *
+******************************************************************************/
+
+bool g_kaitai_parser_parse_content(GKaitaiParser *parser, kaitai_scope_t *locals, GBinContent *content, vmpa2t *pos, GMatchRecord **record)
+{
+ bool result; /* Bilan à retourner */
+ GKaitaiParserClass *class; /* Classe de l'instance */
+
+ class = G_KAITAI_PARSER_GET_CLASS(parser);
+
+ result = class->parse(parser, locals, content, pos, record);
+
+ if (result)
+ remember_last_record(locals, *record);
+
+ return result;
+
+}
diff --git a/plugins/kaitai/parser.h b/plugins/kaitai/parser.h
new file mode 100644
index 0000000..4c17087
--- /dev/null
+++ b/plugins/kaitai/parser.h
@@ -0,0 +1,63 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * parser.h - prototypes pour la spécification d'un lecteur 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/>.
+ */
+
+
+#ifndef PLUGINS_KAITAI_PARSER_H
+#define PLUGINS_KAITAI_PARSER_H
+
+
+#include <glib-object.h>
+#include <stdbool.h>
+
+
+#include <analysis/content.h>
+
+
+#include "record.h"
+#include "scope.h"
+
+
+
+#define G_TYPE_KAITAI_PARSER g_kaitai_parser_get_type()
+#define G_KAITAI_PARSER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_KAITAI_PARSER, GKaitaiParser))
+#define G_IS_KAITAI_PARSER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_KAITAI_PARSER))
+#define G_KAITAI_PARSER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_KAITAI_PARSER, GKaitaiParserClass))
+#define G_IS_KAITAI_PARSER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_KAITAI_PARSER))
+#define G_KAITAI_PARSER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_KAITAI_PARSER, GKaitaiParserClass))
+
+
+/* Spécification d'un lecteur Kaitai (instance) */
+typedef struct _GKaitaiParser GKaitaiParser;
+
+/* Spécification d'un lecteur Kaitai (classe) */
+typedef struct _GKaitaiParserClass GKaitaiParserClass;
+
+
+/* Indique le type défini pour un lecteur de spécification Kaitai. */
+GType g_kaitai_parser_get_type(void);
+
+/* Parcourt un contenu binaire selon des spécifications Kaitai. */
+bool g_kaitai_parser_parse_content(GKaitaiParser *, kaitai_scope_t *, GBinContent *, vmpa2t *, GMatchRecord **);
+
+
+
+#endif /* PLUGINS_KAITAI_PARSER_H */
diff --git a/plugins/kaitai/parsers/Makefile.am b/plugins/kaitai/parsers/Makefile.am
new file mode 100644
index 0000000..c7e313b
--- /dev/null
+++ b/plugins/kaitai/parsers/Makefile.am
@@ -0,0 +1,25 @@
+
+noinst_LTLIBRARIES = libkaitaiparsers.la
+
+libkaitaiparsers_la_SOURCES = \
+ attribute-int.h \
+ attribute.h attribute.c \
+ enum-int.h \
+ enum.h enum.c \
+ instance-int.h \
+ instance.h instance.c \
+ meta-int.h \
+ meta.h meta.c \
+ struct-int.h \
+ struct.h struct.c \
+ switch-int.h \
+ switch.h switch.c \
+ type-int.h \
+ type.h type.c
+
+libkaitaiparsers_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) -I$(top_srcdir)/src
+
+
+devdir = $(includedir)/chrysalide-$(subdir)
+
+dev_HEADERS = $(libkaitaiparsers_la_SOURCES:%c=)
diff --git a/plugins/kaitai/parsers/attribute-int.h b/plugins/kaitai/parsers/attribute-int.h
new file mode 100644
index 0000000..ef64089
--- /dev/null
+++ b/plugins/kaitai/parsers/attribute-int.h
@@ -0,0 +1,104 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * attribute-int.h - prototypes pour les spécifications internes d'un attribut 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/>.
+ */
+
+
+#ifndef _PLUGINS_KAITAI_PARSERS_ATTRIBUTE_INT_H
+#define _PLUGINS_KAITAI_PARSERS_ATTRIBUTE_INT_H
+
+
+#include "attribute.h"
+#include "switch.h"
+#include "../parser-int.h"
+
+
+
+/* Indique l'étiquette à utiliser pour identifier un attribut. */
+typedef const char * (* get_attribute_label_fc) (const GKaitaiAttribute *);
+
+/* Spécification d'un attribut Kaitai (instance) */
+struct _GKaitaiAttribute
+{
+ GKaitaiParser parent; /* A laisser en premier */
+
+ char *raw_id; /* Identifiant Kaitai */
+ char *orig_id; /* Identifiant humain */
+
+ char *doc; /* Eventuelle documentation */
+
+ KaitaiAttributePayload payload; /* Forme de la spécialisation */
+
+ struct
+ {
+ /* KAP_FIXED_CONTENT */
+ sized_string_t fixed_content; /* Données brutes attendues */
+
+ /* KAP_BASIC_TYPE */
+ struct
+ {
+ BaseType basic; /* Type de base */
+
+ bool is_string; /* Renvoi vers une chaîne */
+
+ SourceEndian endian; /* Boutisme forcé ? */
+ bool has_endian; /* Présence de cette force */
+
+ };
+
+ /* KAP_USER_TYPE */
+ char *named_type; /* Type particulier */
+
+ /* KAP_DYNAMIC_TYPE */
+ GKaitaiSwitch *switchon; /* Détermination dynamique */
+
+ };
+
+ /* KAP_SIZED */
+ char *fixed_size; /* Taille déterminée */
+
+ KaitaiAttributeRepetition repetition; /* Forme de répétition */
+ char *repeat_controller; /* Indication sur la répétition*/
+
+ char *condition; /* Condition de chargement */
+
+ sized_string_t terminator; /* Marqueur de fin éventuel */
+ bool consume; /* Consommation dans le flux */
+ bool include; /* Intégration de ce marqueur */
+ bool eos_error; /* Gestion des erreurs en bout */
+
+};
+
+/* Spécification d'un attribut Kaitai (classe) */
+struct _GKaitaiAttributeClass
+{
+ GKaitaiParserClass parent; /* A laisser en premier */
+
+ get_attribute_label_fc get_label; /* Désignation d'une étiquette */
+
+};
+
+
+/* Met en place un lecteur d'attribut Kaitai. */
+bool g_kaitai_attribute_create(GKaitaiAttribute *, GYamlNode *, bool);
+
+
+
+#endif /* _PLUGINS_KAITAI_PARSERS_ATTRIBUTE_INT_H */
diff --git a/plugins/kaitai/parsers/attribute.c b/plugins/kaitai/parsers/attribute.c
new file mode 100644
index 0000000..c61fe99
--- /dev/null
+++ b/plugins/kaitai/parsers/attribute.c
@@ -0,0 +1,2074 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * attribute.c - spécification d'un attribut 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 "attribute.h"
+
+
+#include <assert.h>
+#include <string.h>
+
+
+#include <analysis/contents/restricted.h>
+#include <plugins/yaml/pair.h>
+
+
+#include "attribute-int.h"
+#include "../expression.h"
+#include "../scope.h"
+#include "../records/empty.h"
+#include "../records/item.h"
+#include "../records/list.h"
+
+
+
+/* -------------------- CORRESPONDANCE ENTRE ATTRIBUT ET BINAIRE -------------------- */
+
+
+/* Initialise la classe des attributs de spécification Kaitai. */
+static void g_kaitai_attribute_class_init(GKaitaiAttributeClass *);
+
+/* Initialise un attribut de spécification Kaitai. */
+static void g_kaitai_attribute_init(GKaitaiAttribute *);
+
+/* Supprime toutes les références externes. */
+static void g_kaitai_attribute_dispose(GKaitaiAttribute *);
+
+/* Procède à la libération totale de la mémoire. */
+static void g_kaitai_attribute_finalize(GKaitaiAttribute *);
+
+/* Traduit en type concret une chaîne de caractères. */
+static bool g_kaitai_attribute_resolve_type(GKaitaiAttribute *, const char *);
+
+/* Valide la cohérence des informations portées par l'attribut. */
+static bool g_kaitai_attribute_check(const GKaitaiAttribute *);
+
+/* Copie le coeur de la définition d'un lecteur d'attribut. */
+static GKaitaiAttribute *g_kaitai_attribute_dup_for(const GKaitaiAttribute *);
+
+
+
+/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */
+
+
+/* Parcourt un contenu binaire selon des spécifications Kaitai. */
+static bool _g_kaitai_attribute_parse_content(GKaitaiAttribute *, kaitai_scope_t *, GBinContent *, vmpa2t *, GMatchRecord **);
+
+/* Extrait d'un contenu une série d'octets avec terminaison. */
+static bool g_kaitai_attribute_parse_terminated_bytes(GKaitaiAttribute *, const kaitai_scope_t *, GBinContent *, vmpa2t *, GMatchRecord **);
+
+/* Détermine la zone de couverture finale d'une correspondance. */
+static bool g_kaitai_attribute_compute_maybe_terminated_range(const GKaitaiAttribute *, const kaitai_scope_t *, const GBinContent *, const vmpa2t *, phys_t *, mrange_t *);
+
+/* Parcourt un contenu binaire selon des spécifications Kaitai. */
+static bool g_kaitai_attribute_parse_content(GKaitaiAttribute *, kaitai_scope_t *, GBinContent *, vmpa2t *, GMatchRecord **);
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* CORRESPONDANCE ENTRE ATTRIBUT ET BINAIRE */
+/* ---------------------------------------------------------------------------------- */
+
+
+/* Indique le type défini pour un attribut de la spécification Kaitai. */
+G_DEFINE_TYPE(GKaitaiAttribute, g_kaitai_attribute, G_TYPE_KAITAI_PARSER);
+
+
+/******************************************************************************
+* *
+* Paramètres : klass = classe à initialiser. *
+* *
+* Description : Initialise la classe des attributs de spécification Kaitai. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_kaitai_attribute_class_init(GKaitaiAttributeClass *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_attribute_dispose;
+ object->finalize = (GObjectFinalizeFunc)g_kaitai_attribute_finalize;
+
+ parser = G_KAITAI_PARSER_CLASS(klass);
+
+ parser->parse = (parse_kaitai_fc)g_kaitai_attribute_parse_content;
+
+ klass->get_label = g_kaitai_attribute_get_raw_id;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : attrib = instance à initialiser. *
+* *
+* Description : Initialise un attribut de spécification Kaitai. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_kaitai_attribute_init(GKaitaiAttribute *attrib)
+{
+ attrib->raw_id = NULL;
+ attrib->orig_id = NULL;
+
+ attrib->doc = NULL;
+
+ attrib->payload = KAP_UNINITIALIZED;
+
+ attrib->repetition = KAR_NO_REPETITION;
+ attrib->repeat_controller = NULL;
+
+ attrib->condition = NULL;
+
+ init_szstr(&attrib->terminator);
+ attrib->consume = true;
+ attrib->include = false;
+ attrib->eos_error = true;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : attrib = instance d'objet GLib à traiter. *
+* *
+* Description : Supprime toutes les références externes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_kaitai_attribute_dispose(GKaitaiAttribute *attrib)
+{
+ if (attrib->payload & KAP_DYNAMIC_TYPE)
+ g_clear_object(&attrib->switchon);
+
+ G_OBJECT_CLASS(g_kaitai_attribute_parent_class)->dispose(G_OBJECT(attrib));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : attrib = instance d'objet GLib à traiter. *
+* *
+* Description : Procède à la libération totale de la mémoire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_kaitai_attribute_finalize(GKaitaiAttribute *attrib)
+{
+ if (attrib->raw_id != NULL)
+ free(attrib->raw_id);
+
+ if (attrib->orig_id != NULL)
+ free(attrib->orig_id);
+
+ if (attrib->doc != NULL)
+ free(attrib->doc);
+
+ if (attrib->payload & KAP_FIXED_CONTENT)
+ exit_szstr(&attrib->fixed_content);
+
+ else if (attrib->payload & KAP_USER_TYPE)
+ free(attrib->named_type);
+
+ if (attrib->fixed_size != NULL)
+ free(attrib->fixed_size);
+
+ if (attrib->repeat_controller != NULL)
+ free(attrib->repeat_controller);
+
+ if (attrib->condition != NULL)
+ free(attrib->condition);
+
+ exit_szstr(&attrib->terminator);
+
+ G_OBJECT_CLASS(g_kaitai_attribute_parent_class)->finalize(G_OBJECT(attrib));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : parent = noeud Yaml contenant l'attribut à constituer. *
+* *
+* Description : Construit un lecteur d'attribut Kaitai. *
+* *
+* Retour : Instance mise en place ou NULL en cas d'échec. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+GKaitaiAttribute *g_kaitai_attribute_new(GYamlNode *parent)
+{
+ GKaitaiAttribute *result; /* Structure à retourner */
+
+ result = g_object_new(G_TYPE_KAITAI_ATTRIBUTE, NULL);
+
+ if (!g_kaitai_attribute_create(result, parent, true))
+ g_clear_object(&result);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : attrib = lecteur d'attribut Kaitai à initialiser pleinement.*
+* parent = noeud Yaml contenant l'attribut à constituer. *
+* need_id = encadre la présence d'un champ "id". *
+* *
+* Description : Met en place un lecteur d'attribut Kaitai. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool g_kaitai_attribute_create(GKaitaiAttribute *attrib, GYamlNode *parent, bool need_id)
+{
+ bool result; /* Bilan à retourner */
+ GYamlNode *node; /* Noeud particulier présent */
+ const char *value; /* Valeur Yaml particulière */
+ char *rebuilt_value; /* Valeur Yaml rassemblée */
+ kaitai_scope_t fake; /* Contexte de circonstance */
+ resolved_value_t bytes; /* Données brutes obtenues */
+ GYamlNode *other_node; /* Autre noeud nécessaire */
+
+ result = false;
+
+ /* Identifiant obligatoire */
+
+ node = g_yaml_node_find_first_by_path(parent, "/id");
+
+ if (node != NULL)
+ {
+ assert(G_IS_YAML_PAIR(node));
+
+ value = g_yaml_pair_get_value(G_YAML_PAIR(node));
+ if (value == NULL)
+ {
+ g_object_unref(G_OBJECT(node));
+ goto bad_id;
+ }
+
+ attrib->raw_id = strdup(value);
+
+ g_object_unref(G_OBJECT(node));
+
+ }
+
+ else if (need_id)
+ goto bad_id;
+
+ /* Identifiant facultatif */
+
+ node = g_yaml_node_find_first_by_path(parent, "/-orig-id");
+
+ if (node != NULL)
+ {
+ assert(G_IS_YAML_PAIR(node));
+
+ value = g_yaml_pair_get_value(G_YAML_PAIR(node));
+ if (value == NULL)
+ {
+ g_object_unref(G_OBJECT(node));
+ goto bad_id;
+ }
+
+ attrib->orig_id = strdup(value);
+
+ g_object_unref(G_OBJECT(node));
+
+ }
+
+ /* Eventuelle documentation */
+
+ node = g_yaml_node_find_first_by_path(parent, "/doc");
+
+ if (node != NULL)
+ {
+ assert(G_IS_YAML_PAIR(node));
+
+ value = g_yaml_pair_get_value(G_YAML_PAIR(node));
+ if (value == NULL)
+ {
+ g_object_unref(G_OBJECT(node));
+ goto bad_doc;
+ }
+
+ attrib->doc = strdup(value);
+
+ g_object_unref(G_OBJECT(node));
+
+ }
+
+ /* Champ contents */
+
+ node = g_yaml_node_find_first_by_path(parent, "/contents");
+
+ if (node != NULL)
+ {
+ assert(G_IS_YAML_PAIR(node));
+
+ rebuilt_value = g_yaml_pair_aggregate_value(G_YAML_PAIR(node));
+
+ if (rebuilt_value == NULL)
+ {
+ g_object_unref(G_OBJECT(node));
+ goto bad_content;
+ }
+
+ fake.meta = NULL;
+ fake.root = NULL;
+ fake.parent = NULL;
+ fake.last = NULL;
+
+ if (!resolve_kaitai_expression_as_bytes(&fake, rebuilt_value, strlen(rebuilt_value), &bytes))
+ {
+ free(rebuilt_value);
+ g_object_unref(G_OBJECT(node));
+ goto bad_content;
+ }
+
+ free(rebuilt_value);
+
+ attrib->fixed_content = bytes.bytes;
+
+ g_object_unref(G_OBJECT(node));
+
+ attrib->payload |= KAP_FIXED_CONTENT;
+
+ }
+
+ /* Charge portée par un type */
+
+ node = g_yaml_node_find_first_by_path(parent, "/type");
+
+ if (node != NULL)
+ {
+ if (attrib->payload & KAP_FIXED_CONTENT)
+ {
+ printf("Can not handle fixed content and type definition at the same time for an attribute.\n");
+ goto bad_definition;
+ }
+
+ assert(G_IS_YAML_PAIR(node));
+
+ value = g_yaml_pair_get_value(G_YAML_PAIR(node));
+
+ if (value != NULL)
+ {
+ if (g_kaitai_attribute_resolve_type(attrib, value))
+ attrib->payload |= KAP_BASIC_TYPE;
+
+ else
+ {
+ attrib->named_type = strdup(value);
+ attrib->payload |= KAP_USER_TYPE;
+ }
+
+ }
+
+ else
+ {
+ attrib->switchon = g_kaitai_switch_new(parent, attrib);
+ if (attrib->switchon == NULL) goto bad_definition;
+
+ attrib->payload |= KAP_DYNAMIC_TYPE;
+
+ }
+
+ g_object_unref(G_OBJECT(node));
+
+ }
+
+ /* Répétitions contrôlées ? */
+
+ node = g_yaml_node_find_first_by_path(parent, "/repeat");
+
+ if (node != NULL)
+ {
+ assert(G_IS_YAML_PAIR(node));
+
+ value = g_yaml_pair_get_value(G_YAML_PAIR(node));
+
+ if (value != NULL)
+ {
+ if (strcmp(value, "eos") == 0)
+ attrib->repetition = KAR_END_OF_STREAM;
+
+ else if (strcmp(value, "expr") == 0)
+ {
+ other_node = g_yaml_node_find_first_by_path(parent, "/repeat-expr");
+
+ if (other_node != NULL)
+ {
+ if (G_IS_YAML_PAIR(other_node))
+ {
+ value = g_yaml_pair_get_value(G_YAML_PAIR(other_node));
+
+ if (value != NULL)
+ {
+ attrib->repetition = KAR_EXPRESSION;
+ attrib->repeat_controller = strdup(value);
+ }
+ else
+ printf("Expected repeat expression\n");
+
+ }
+
+ g_object_unref(G_OBJECT(other_node));
+
+ }
+
+ }
+
+ else if (strcmp(value, "until") == 0)
+ {
+ other_node = g_yaml_node_find_first_by_path(parent, "/repeat-until");
+
+ if (other_node != NULL)
+ {
+ assert(G_IS_YAML_PAIR(other_node));
+
+ value = g_yaml_pair_get_value(G_YAML_PAIR(other_node));
+
+ if (value != NULL)
+ {
+ attrib->repetition = KAR_UNTIL;
+ attrib->repeat_controller = strdup(value);
+ }
+ else
+ printf("Expected repeat expression\n");
+
+ }
+
+ g_object_unref(G_OBJECT(other_node));
+
+ }
+
+ }
+
+ g_object_unref(G_OBJECT(node));
+
+ }
+
+ /* Intégration sous condition ? */
+
+ node = g_yaml_node_find_first_by_path(parent, "/if");
+
+ if (node != NULL)
+ {
+ assert(G_IS_YAML_PAIR(node));
+
+ value = g_yaml_pair_get_value(G_YAML_PAIR(node));
+
+ if (value != NULL)
+ attrib->condition = strdup(value);
+
+ g_object_unref(G_OBJECT(node));
+
+ }
+
+ /* Taille fixée ? */
+
+ node = g_yaml_node_find_first_by_path(parent, "/size");
+
+ if (node != NULL)
+ {
+ assert(G_IS_YAML_PAIR(node));
+
+ value = g_yaml_pair_get_value(G_YAML_PAIR(node));
+
+ if (value != NULL)
+ {
+ attrib->fixed_size = strdup(value);
+ attrib->payload |= KAP_SIZED;
+ }
+
+ g_object_unref(G_OBJECT(node));
+
+ if ((attrib->payload & KAP_SIZED) == 0)
+ goto bad_content;
+
+ }
+
+ /* Prise en considération d'une taille maximale */
+
+ node = g_yaml_node_find_first_by_path(parent, "/size-eos");
+
+ if (node != NULL)
+ {
+ assert(G_IS_YAML_PAIR(node));
+
+ value = g_yaml_pair_get_value(G_YAML_PAIR(node));
+
+ if (value != NULL && strcmp(value, "true") == 0)
+ {
+ if (attrib->payload != KAP_UNINITIALIZED)
+ /* printf warning */;
+
+ attrib->payload |= KAP_SIZED_EOS;
+
+ }
+
+ g_object_unref(G_OBJECT(node));
+
+ }
+
+ /* Champ terminator */
+
+ node = g_yaml_node_find_first_by_path(parent, "/terminator");
+
+ if (node != NULL)
+ {
+ assert(G_IS_YAML_PAIR(node));
+
+ rebuilt_value = g_yaml_pair_aggregate_value(G_YAML_PAIR(node));
+
+ if (rebuilt_value == NULL)
+ {
+ g_object_unref(G_OBJECT(node));
+ goto bad_content;
+ }
+
+ fake.meta = NULL;
+ fake.root = NULL;
+ fake.parent = NULL;
+ fake.last = NULL;
+
+ if (!resolve_kaitai_expression_as_bytes(&fake, rebuilt_value, strlen(rebuilt_value), &bytes))
+ {
+ free(rebuilt_value);
+ g_object_unref(G_OBJECT(node));
+ goto bad_content;
+ }
+
+ free(rebuilt_value);
+
+ if (attrib->terminator.data != NULL)
+ printf("A ending content has already been specified (implicitly by the strz type)");
+
+ else
+ {
+ attrib->terminator.data = bytes.bytes.data;
+ attrib->terminator.len = bytes.bytes.len;
+ }
+
+ g_object_unref(G_OBJECT(node));
+
+ }
+
+ /* Champ consume */
+
+ node = g_yaml_node_find_first_by_path(parent, "/consume");
+
+ if (node != NULL)
+ {
+ assert(G_IS_YAML_PAIR(node));
+
+ value = g_yaml_pair_get_value(G_YAML_PAIR(node));
+
+ if (value != NULL)
+ {
+ if (strcmp(value, "true") == 0)
+ attrib->consume = true;
+
+ else if (strcmp(value, "false") == 0)
+ attrib->consume = false;
+
+ else
+ printf("Unsupported value for the 'consume' property (expecting true of false)");
+
+ }
+
+ g_object_unref(G_OBJECT(node));
+
+ }
+
+ /* Champ include */
+
+ node = g_yaml_node_find_first_by_path(parent, "/include");
+
+ if (node != NULL)
+ {
+ assert(G_IS_YAML_PAIR(node));
+
+ value = g_yaml_pair_get_value(G_YAML_PAIR(node));
+
+ if (value != NULL)
+ {
+ if (strcmp(value, "true") == 0)
+ attrib->include = true;
+
+ else if (strcmp(value, "false") == 0)
+ attrib->include = false;
+
+ else
+ printf("Unsupported value for the 'include' property (expecting true of false)");
+
+ }
+
+ g_object_unref(G_OBJECT(node));
+
+ }
+
+ /* Champ eos-error */
+
+ node = g_yaml_node_find_first_by_path(parent, "/eos-error");
+
+ if (node != NULL)
+ {
+ assert(G_IS_YAML_PAIR(node));
+
+ value = g_yaml_pair_get_value(G_YAML_PAIR(node));
+
+ if (value != NULL)
+ {
+ if (strcmp(value, "true") == 0)
+ attrib->eos_error = true;
+
+ if (strcmp(value, "false") == 0)
+ attrib->eos_error = false;
+
+ else
+ printf("Unsupported value for the 'eos_error' property (expecting true of false)");
+
+ }
+
+ g_object_unref(G_OBJECT(node));
+
+ }
+
+ /* Validation finale */
+
+ result = g_kaitai_attribute_check(attrib);
+
+ bad_definition:
+
+ bad_doc:
+ bad_id:
+ bad_content:
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : attrib = attribut Kaitai en cours de constitution. *
+* desc = chaîne de caractère à interpréter en type. *
+* *
+* Description : Traduit en type concret une chaîne de caractères. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool g_kaitai_attribute_resolve_type(GKaitaiAttribute *attrib, const char *desc)
+{
+ bool result; /* Bilan à retourner */
+
+ result = true;
+
+ attrib->basic = BTP_INVALID;
+ attrib->has_endian = false;
+
+ /**
+ * Cf. définition des types de base existants :
+ * http://doc.kaitai.io/user_guide.html#_fixed_size_structures
+ */
+
+#define RESOLVE_ENDIAN \
+ if (desc[2] == 'l') \
+ { \
+ if (desc[3] == 'e') \
+ { \
+ attrib->endian = SRE_LITTLE; \
+ attrib->has_endian = true; \
+ } \
+ } \
+ else if (desc[2] == 'b') \
+ { \
+ if (desc[3] == 'e') \
+ { \
+ attrib->endian = SRE_BIG; \
+ attrib->has_endian = true; \
+ } \
+ } \
+
+ /* Analyse de la chaîne fournie */
+
+ switch (desc[0])
+ {
+ case 'f':
+ switch (desc[1])
+ {
+ case '4':
+ attrib->basic = BTP_754R_32;
+ RESOLVE_ENDIAN;
+ break;
+
+ case '8':
+ attrib->basic = BTP_754R_64;
+ RESOLVE_ENDIAN;
+ break;
+
+ default:
+ result = false;
+ break;
+
+ }
+ break;
+
+ case 's':
+ switch (desc[1])
+ {
+ case '1':
+ attrib->basic = BTP_CHAR;
+ RESOLVE_ENDIAN;
+ break;
+
+ case '2':
+ attrib->basic = BTP_SHORT;
+ RESOLVE_ENDIAN;
+ break;
+
+ case '4':
+ attrib->basic = BTP_INT;
+ RESOLVE_ENDIAN;
+ break;
+
+ case '8':
+ attrib->basic = BTP_LONG_LONG;
+ RESOLVE_ENDIAN;
+ break;
+
+ case 't':
+ if (desc[2] == 'r')
+ {
+ attrib->basic = BTP_CHAR;
+ attrib->is_string = true;
+ if (desc[3] == 'z')
+ {
+ attrib->terminator.data = strdup("");
+ attrib->terminator.len = 1;
+ }
+ }
+ else
+ result = false;
+ break;
+
+ default:
+ result = false;
+ break;
+
+ }
+ break;
+
+ case 'u':
+ switch (desc[1])
+ {
+ case '1':
+ attrib->basic = BTP_UCHAR;
+ RESOLVE_ENDIAN;
+ break;
+
+ case '2':
+ attrib->basic = BTP_USHORT;
+ RESOLVE_ENDIAN;
+ break;
+
+ case '4':
+ attrib->basic = BTP_UINT;
+ RESOLVE_ENDIAN;
+ break;
+
+ case '8':
+ attrib->basic = BTP_ULONG_LONG;
+ RESOLVE_ENDIAN;
+ break;
+
+ default:
+ result = false;
+ break;
+
+ }
+ break;
+
+ default:
+ result = false;
+ break;
+
+ }
+
+ /* Vérification d'une comparaison complète */
+ if (result)
+ switch (attrib->basic)
+ {
+ case BTP_CHAR:
+ if (attrib->is_string)
+ {
+ if (attrib->terminator.data != NULL)
+ result = (desc[4] == 0);
+ else
+ result = (desc[3] == 0);
+ }
+ else
+ {
+ if (attrib->has_endian)
+ result = (desc[4] == 0);
+ else
+ result = (desc[2] == 0);
+ }
+ break;
+
+ default:
+ if (attrib->has_endian)
+ result = (desc[4] == 0);
+ else
+ result = (desc[2] == 0);
+ break;
+
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : attrib = attribut Kaitai à valider. *
+* *
+* Description : Valide la cohérence des informations portées par l'attribut. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool g_kaitai_attribute_check(const GKaitaiAttribute *attrib)
+{
+ bool result; /* Bilan à retourner */
+
+ result = true;
+
+ /**
+ * Une lecture de tous les octets restants ne doit correspondre qu'à des octets bruts.
+ */
+ if (attrib->payload & KAP_SIZED_EOS && attrib->payload != KAP_SIZED_EOS)
+ {
+ result = (attrib->payload & KAP_BASIC_TYPE) && attrib->is_string;
+
+ if (!result)
+ {
+ printf("Reading all the remaining bytes should only produce bytes.");
+ result = true;
+ }
+
+ }
+
+ /**
+ * Une chaîne (type str[z]) doit comporter une séquence de terminaison.
+ */
+ if ((attrib->payload & KAP_BASIC_TYPE) && attrib->is_string)
+ {
+ result = (attrib->terminator.data != NULL) || (attrib->payload & (KAP_SIZED | KAP_SIZED_EOS));
+
+ if (!result)
+ {
+ printf("An unsized string (str type with no size attribute) has to be link to a terminator sequence.");
+ goto exit;
+ }
+
+ }
+
+ /**
+ * Si une séquence d'octets finaux est spécifiées, alors l'attribut
+ * doit correspondre à un type str[z] (lecture) ou de taille fixée
+ * (validation post-lecture).
+ */
+ if (attrib->terminator.data != NULL)
+ {
+ result = ((attrib->payload & ~(KAP_FIXED_CONTENT | KAP_BASIC_TYPE | KAP_SIZED)) == 0);
+
+ if (result && (attrib->payload & KAP_BASIC_TYPE))
+ result = attrib->is_string;
+
+ if (!result)
+ {
+ printf("A useless terminator is specified.");
+ result = true;
+ goto exit;
+ }
+
+ }
+
+ /**
+ * Il n'est pas possible d'inclure un marqueur de fin sans le consommer.
+ */
+ if (!attrib->consume && attrib->include)
+ {
+ result = false;
+ printf("It is not possible to include a terminator without consuming it.");
+ }
+
+ exit:
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : attrib = lecteur d'attribut Kaitai à dupliquer. *
+* type = type utilisateur à associer au nouvel attribut. *
+* *
+* Description : Dérive un lecteur d'attribut Kaitai pour un type utilisateur.*
+* *
+* Retour : Instance mise en place ou NULL en cas d'échec. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+GKaitaiAttribute *g_kaitai_attribute_dup_for_user_type(const GKaitaiAttribute *attrib, const char *type)
+{
+ GKaitaiAttribute *result; /* Structure à retourner */
+
+ result = g_kaitai_attribute_dup_for(attrib);
+
+ result->payload = KAP_USER_TYPE;
+
+ result->named_type = strdup(type);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : attrib = lecteur d'attribut Kaitai à dupliquer. *
+* *
+* Description : Copie le coeur de la définition d'un lecteur d'attribut. *
+* *
+* Retour : Nouvelle instance à compléter. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static GKaitaiAttribute *g_kaitai_attribute_dup_for(const GKaitaiAttribute *attrib)
+{
+ GKaitaiAttribute *result; /* Structure à retourner */
+
+ result = g_object_new(G_TYPE_KAITAI_ATTRIBUTE, NULL);
+
+ /**
+ * Il n'y a rien à copier dans la structure parente.
+ *
+ * Les travaux de copie ne portent ainsi que sur le présent attribut.
+ */
+
+ result->raw_id = strdup(attrib->raw_id);
+
+ if (attrib->orig_id != NULL)
+ result->orig_id = strdup(attrib->orig_id);
+
+ if (attrib->doc != NULL)
+ result->doc = strdup(attrib->doc);
+
+ if (attrib->fixed_size != NULL)
+ result->fixed_size = strdup(attrib->fixed_size);
+
+ result->repetition = attrib->repetition;
+
+ if (attrib->repeat_controller != NULL)
+ result->repeat_controller = strdup(attrib->repeat_controller);
+
+ if (attrib->condition != NULL)
+ result->condition = strdup(attrib->condition);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : attrib = lecteur d'attribut Kaitai à consulter. *
+* *
+* Description : Indique l'étiquette à utiliser pour identifier un attribut. *
+* *
+* Retour : Valeur brute de l'identifiant. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+const char *g_kaitai_attribute_get_label(const GKaitaiAttribute *attrib)
+{
+ const char *result; /* Valeur à renvoyer */
+ GKaitaiAttributeClass *class; /* Classe de l'instance */
+
+ class = G_KAITAI_ATTRIBUTE_GET_CLASS(attrib);
+
+ result = class->get_label(attrib);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : attrib = lecteur d'attribut Kaitai à consulter. *
+* *
+* Description : Indique la désignation brute d'un identifiant Kaitai. *
+* *
+* Retour : Valeur brute de l'identifiant. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+const char *g_kaitai_attribute_get_raw_id(const GKaitaiAttribute *attrib)
+{
+ char *result; /* Valeur à renvoyer */
+
+ result = attrib->raw_id;
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : attrib = lecteur d'attribut Kaitai à consulter. *
+* *
+* Description : Indique la désignation originelle d'un identifiant Kaitai. *
+* *
+* Retour : Valeur originelle de l'identifiant. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+const char *g_kaitai_attribute_get_original_id(const GKaitaiAttribute *attrib)
+{
+ char *result; /* Valeur à renvoyer */
+
+ result = attrib->orig_id;
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : attrib = lecteur d'attribut Kaitai à consulter. *
+* *
+* Description : Fournit une éventuelle documentation concernant l'attribut. *
+* *
+* Retour : Description enregistrée ou NULL si absente. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+const char *g_kaitai_attribute_get_doc(const GKaitaiAttribute *attrib)
+{
+ char *result; /* Valeur à renvoyer */
+
+ result = attrib->doc;
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : attrib = lecteur d'attribut Kaitai à consulter. *
+* *
+* Description : Indique la nature de la charge représentée par l'attribut. *
+* *
+* Retour : Forme de contenu représenté par le lecteur d'attribut. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+KaitaiAttributePayload g_kaitai_attribute_get_payload(const GKaitaiAttribute *attrib)
+{
+ KaitaiAttributePayload result; /* Type de charge à renvoyer */
+
+ result = attrib->payload;
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : attrib = lecteur d'attribut Kaitai à consulter. *
+* basic = type de base Kaitai reconnu par le lecteur. [OUT]*
+* is_string = nature du type BTP_CHAR en sortie. [OUT] *
+* *
+* Description : Précise un éventuel type de base reconnu par le lecteur. *
+* *
+* Retour : Validité du type renseigné en argument. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool g_kaitai_attribute_get_basic_type(const GKaitaiAttribute *attrib, BaseType *basic, bool *is_string)
+{
+ bool result; /* Validité à retourner */
+
+ result = (attrib->payload & KAP_BASIC_TYPE);
+
+ if (result)
+ {
+ *basic = attrib->basic;
+ *is_string = attrib->is_string;
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : attrib = lecteur d'attribut Kaitai à consulter. *
+* content = contenu binaire à venir lire. *
+* range = espace disponible pour la lecture. *
+* out = tableau d'octets retournés. [OUT] *
+* len = taille de ce tableau alloué. [OUT] *
+* *
+* Description : Lit les octets d'une chaîne représentée. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool g_kaitai_attribute_read_truncated_bytes(const GKaitaiAttribute *attrib, const GBinContent *content, const mrange_t *range, bin_t **out, size_t *len)
+{
+ bool result; /* Bilan à retourner */
+ vmpa2t tmppos; /* Localisation modifiable */
+ const bin_t *data; /* Accès aux données brutes */
+
+ result = false;
+
+ if ((attrib->payload & KAP_SIZED) == 0)
+ goto bad_type;
+
+ copy_vmpa(&tmppos, get_mrange_addr(range));
+
+ *len = get_mrange_length(range);
+
+ data = g_binary_content_get_raw_access(content, &tmppos, *len);
+
+ *out = malloc(sizeof(bin_t) * (*len + 1));
+
+ memcpy(*out, data, *len);
+ (*out)[*len] = '\0';
+
+ result = true;
+
+ bad_type:
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : attrib = lecteur d'attribut Kaitai à consulter. *
+* *
+* Description : Détermine si l'attribue porte une valeur entière signée. *
+* *
+* Retour : Bilan de la consultation : true si un entier signé est visé. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool g_kaitai_attribute_handle_signed_integer(const GKaitaiAttribute *attrib)
+{
+ bool result; /* Bilan à retourner */
+
+ result = false;
+
+ if ((attrib->payload & KAP_BASIC_TYPE) == 0)
+ goto bad_type;
+
+ switch (attrib->basic)
+ {
+ case BTP_CHAR:
+ case BTP_SHORT:
+ case BTP_INT:
+ case BTP_LONG_LONG:
+ result = true;
+ break;
+
+ default:
+ break;
+
+ }
+
+ bad_type:
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : attrib = lecteur d'attribut Kaitai à consulter. *
+* content = contenu binaire à venir lire. *
+* range = espace de lecture. *
+* endian = boustime des données à respecter. *
+* out = valeur à sauvegarder sous une forme générique.[OUT]*
+* *
+* Description : Lit la valeur d'un élément Kaitai entier représenté. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool g_kaitai_attribute_read_value(const GKaitaiAttribute *attrib, const GBinContent *content, const mrange_t *range, SourceEndian endian, resolved_value_t *out)
+{
+ bool result; /* Bilan à retourner */
+ vmpa2t tmppos; /* Localisation modifiable */
+ const bin_t *data; /* Données brutes restituées */
+ int8_t stmp8; /* Valeur de 8 bits lue */
+ uint8_t tmp8; /* Valeur de 8 bits lue */
+ int16_t stmp16; /* Valeur de 16 bits lue */
+ uint16_t tmp16; /* Valeur de 16 bits lue */
+ int32_t stmp32; /* Valeur de 32 bits lue */
+ uint32_t tmp32; /* Valeur de 32 bits lue */
+ int64_t stmp64; /* Valeur de 64 bits lue */
+ uint64_t tmp64; /* Valeur de 64 bits lue */
+
+ result = false;
+
+ if (attrib->payload & (KAP_FIXED_CONTENT | KAP_SIZED | KAP_SIZED_EOS))
+ {
+ copy_vmpa(&tmppos, get_mrange_addr(range));
+
+ data = g_binary_content_get_raw_access(content, &tmppos, get_mrange_length(range));
+ result = (data != NULL);
+
+ if (result)
+ {
+ out->type = GVT_BYTES;
+
+ out->bytes.len = get_mrange_length(range);
+
+ out->bytes.data = malloc(out->bytes.len);
+ memcpy(out->bytes.data, data, out->bytes.len);
+
+ }
+
+ }
+
+ else if (attrib->payload & KAP_BASIC_TYPE)
+ {
+ copy_vmpa(&tmppos, get_mrange_addr(range));
+
+ switch (attrib->basic)
+ {
+ case BTP_CHAR:
+ if (attrib->is_string)
+ {
+ copy_vmpa(&tmppos, get_mrange_addr(range));
+
+ data = g_binary_content_get_raw_access(content, &tmppos, get_mrange_length(range));
+ result = (data != NULL);
+
+ if (result)
+ {
+ out->type = GVT_BYTES;
+
+ out->bytes.len = get_mrange_length(range);
+
+ out->bytes.data = malloc(out->bytes.len);
+ memcpy(out->bytes.data, data, out->bytes.len);
+
+ }
+
+ }
+ else
+ {
+ assert(get_mrange_length(range) == 1);
+ result = g_binary_content_read_s8(content, &tmppos, &stmp8);
+ out->type = GVT_SIGNED_INTEGER;
+ out->signed_integer = stmp8;
+ }
+ break;
+
+ case BTP_UCHAR:
+ assert(get_mrange_length(range) == 1);
+ result = g_binary_content_read_u8(content, &tmppos, &tmp8);
+ out->type = GVT_UNSIGNED_INTEGER;
+ out->unsigned_integer = tmp8;
+ break;
+
+ case BTP_SHORT:
+ assert(get_mrange_length(range) == 2);
+ result = g_binary_content_read_s16(content, &tmppos, endian, &stmp16);
+ out->type = GVT_SIGNED_INTEGER;
+ out->signed_integer = stmp16;
+ break;
+
+ case BTP_USHORT:
+ assert(get_mrange_length(range) == 2);
+ result = g_binary_content_read_u16(content, &tmppos, endian, &tmp16);
+ out->type = GVT_UNSIGNED_INTEGER;
+ out->unsigned_integer = tmp16;
+ break;
+
+ case BTP_INT:
+ assert(get_mrange_length(range) == 4);
+ result = g_binary_content_read_s32(content, &tmppos, endian, &stmp32);
+ out->type = GVT_SIGNED_INTEGER;
+ out->signed_integer = stmp32;
+ break;
+
+ case BTP_UINT:
+ assert(get_mrange_length(range) == 4);
+ result = g_binary_content_read_u32(content, &tmppos, endian, &tmp32);
+ out->type = GVT_UNSIGNED_INTEGER;
+ out->unsigned_integer = tmp32;
+ break;
+
+ case BTP_LONG_LONG:
+ assert(get_mrange_length(range) == 8);
+ result = g_binary_content_read_s64(content, &tmppos, endian, &stmp64);
+ out->type = GVT_SIGNED_INTEGER;
+ out->signed_integer = stmp64;
+ break;
+
+ case BTP_ULONG_LONG:
+ assert(get_mrange_length(range) == 8);
+ result = g_binary_content_read_u64(content, &tmppos, endian, &tmp64);
+ out->type = GVT_UNSIGNED_INTEGER;
+ out->unsigned_integer = tmp64;
+ break;
+
+ default:
+ break;
+
+ }
+
+ }
+
+ return result;
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* IMPLEMENTATION DES FONCTIONS DE CLASSE */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : attrib = structure Kaitai en cours de parcours. *
+* locals = variables locales pour les résolutions de types. *
+* content = données binaires à analyser et traduire. *
+* pos = 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_attribute_parse_content(GKaitaiAttribute *attrib, kaitai_scope_t *locals, GBinContent *content, vmpa2t *pos, GMatchRecord **record)
+{
+ bool result; /* Bilan à retourner */
+ resolved_value_t authorized; /* Validation des traitements */
+
+ mrange_t work_range; /* Définition de cette aire */
+ GBinContent *work_area; /* Aire de travail */
+ bool has_empty_size; /* Mémorise une taille nulle */
+
+
+ //unsigned long long value; /* Valeur entière finale */
+ //bool status; /* Bilan d'une conversion */
+
+
+ vmpa2t tmp; /* Position de travail */
+ phys_t diff; /* Différentiel de positions */
+ resolved_value_t resolved; /* Valeur entière obtenue */
+ phys_t max_size; /* Taille maximale imposée */
+
+
+ const bin_t *data; /* Données à comparer */
+ GKaitaiType *user_type; /* Définition particulière */
+
+
+ mrange_t range; /* Couverture appliquée */
+ SourceEndian endian; /* Boutisme à observer */
+ phys_t cur_diff; /* Avancée de lecture courante */
+
+
+ result = false;
+ *record = NULL;
+
+ /* Lecture soumise à condition ? */
+
+ if (attrib->condition != NULL)
+ {
+ result = resolve_kaitai_expression_as_boolean(locals,
+ attrib->condition,
+ strlen(attrib->condition),
+ &authorized);
+
+ if (!result || !authorized.status)
+ goto exit;
+
+ }
+
+ /* Zone de travail restreinte */
+
+ g_binary_content_compute_end_pos(content, &tmp);
+ diff = compute_vmpa_diff(pos, &tmp);
+
+ if (attrib->payload & KAP_SIZED)
+ {
+ result = resolve_kaitai_expression_as_integer(locals,
+ attrib->fixed_size,
+ strlen(attrib->fixed_size),
+ &resolved);
+
+ if (result)
+ {
+ if (resolved.type == GVT_UNSIGNED_INTEGER)
+ max_size = resolved.unsigned_integer;
+ else
+ {
+ assert(resolved.type == GVT_SIGNED_INTEGER);
+
+ if (resolved.signed_integer < 0)
+ result = false;
+ else
+ max_size = resolved.signed_integer;
+
+ }
+
+ if (result)
+ result = (diff >= max_size);
+
+ if (!result)
+ printf("Need more data!\n");
+
+ if (result && max_size < diff)
+ diff = max_size;
+
+ }
+
+ if (!result)
+ goto exit;
+
+ init_mrange(&work_range, pos, diff);
+ work_area = g_restricted_content_new_ro(content, &work_range);
+
+ has_empty_size = (diff == 0);
+
+ }
+ else
+ {
+ work_area = content;
+ has_empty_size = false;
+ }
+
+ /* Etablissement d'une zone de correspondance */
+
+ if (attrib->payload == KAP_UNINITIALIZED)
+ assert(false);
+
+ else if (attrib->payload & KAP_SIZED_EOS)
+ result = true;
+
+ else if (attrib->payload & KAP_FIXED_CONTENT)
+ {
+ if (diff >= attrib->fixed_content.len)
+ {
+ copy_vmpa(&tmp, pos);
+
+ data = g_binary_content_get_raw_access(work_area, &tmp, attrib->fixed_content.len);
+ assert(data != NULL);
+
+ result = (memcmp(data, attrib->fixed_content.data, attrib->fixed_content.len) == 0);
+
+ if (result)
+ diff = attrib->fixed_content.len;
+
+ }
+
+ }
+
+ else if (attrib->payload & KAP_BASIC_TYPE)
+ {
+ switch (attrib->basic)
+ {
+ case BTP_CHAR:
+ case BTP_UCHAR:
+ if (attrib->is_string)
+ {
+ if ((attrib->payload & KAP_SIZED) == 0)
+ result = g_kaitai_attribute_parse_terminated_bytes(attrib, locals, work_area, pos, record);
+ }
+ else
+ {
+ result = (diff >= 1);
+ diff = 1;
+ }
+ break;
+
+ case BTP_SHORT:
+ case BTP_USHORT:
+ result = (diff >= 2);
+ diff = 2;
+ break;
+
+ case BTP_INT:
+ case BTP_UINT:
+ case BTP_754R_32:
+ result = (diff >= 4);
+ diff = 4;
+ break;
+
+ case BTP_LONG_LONG:
+ case BTP_ULONG_LONG:
+ case BTP_754R_64:
+ result = (diff >= 8);
+ diff = 8;
+ break;
+
+ default:
+ break;
+
+ }
+
+ }
+
+ else if (attrib->payload & KAP_USER_TYPE)
+ {
+ user_type = find_sub_type(locals, attrib->named_type);
+
+ if (user_type != NULL)
+ {
+ result = g_kaitai_parser_parse_content(G_KAITAI_PARSER(user_type),
+ locals, work_area, pos, record);
+
+ if (result)
+ /**
+ * Le type utilisateur dérive du type GKaitaiStruct, qui ne possède pas
+ * d'identifiant propre. La correspondance produite est ainsi nominalement
+ * anonyme, ce qui empêche toute résolution.
+ *
+ * Le rattachement de l'étiquette de l'attribut d'origine est donc forcée ici.
+ */
+ g_match_record_fix_creator(*record, G_KAITAI_PARSER(attrib));
+
+
+ g_object_unref(G_OBJECT(user_type));
+
+ }
+
+ }
+
+ else if (attrib->payload & KAP_DYNAMIC_TYPE)
+ result = g_kaitai_parser_parse_content(G_KAITAI_PARSER(attrib->switchon), locals, work_area, pos, record);
+
+ else if (attrib->payload & KAP_SIZED)
+ {
+ /* Cas déjà traité en début de fonction */
+
+ }
+
+ /* Enregistrement de la correspondance */
+
+ if (result && *record == NULL)
+ {
+ /**
+ * On choisit de laisser la création de correspondances nulles.
+ *
+ * Cela permet de disposer de la présence de champs valides, même vides
+ * (cf. "4.10.3. Repeat until condition is met")
+ */
+
+ /* if (diff > 0) */
+ {
+ result = g_kaitai_attribute_compute_maybe_terminated_range(attrib, locals, content, pos, &diff, &range);
+
+ if (result)
+ {
+ if (has_empty_size)
+ *record = G_MATCH_RECORD(g_record_empty_new(G_KAITAI_PARSER(attrib), content, pos));
+
+ else
+ {
+ if (attrib->has_endian)
+ endian = attrib->endian;
+ else
+ endian = g_kaitai_meta_get_endian(locals->meta);
+
+ *record = G_MATCH_RECORD(g_record_item_new(attrib, work_area, &range, endian));
+
+ if (*record != NULL)
+ advance_vmpa(pos, diff);
+ else
+ result = false;
+
+ }
+
+ }
+
+ }
+
+ }
+
+ /* Libération de zone de travail restreinte ? */
+
+ if (attrib->payload & KAP_SIZED)
+ {
+ cur_diff = compute_vmpa_diff(get_mrange_addr(&work_range), pos);
+
+ /* Pour GCC... */
+ max_size = get_mrange_length(&work_range);
+
+ if (cur_diff < max_size)
+ advance_vmpa(pos, max_size - cur_diff);
+
+ assert(work_area != content);
+ g_object_unref(G_OBJECT(work_area));
+
+ }
+
+ exit:
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : attrib = structure Kaitai en cours de parcours. *
+* locals = variables locales pour les résolutions de types. *
+* content = données binaires à analyser et traduire. *
+* pos = tête de lecture courante. [OUT] *
+* record = noeud d'arborescence d'éléments rencontrés. [OUT] *
+* *
+* Description : Extrait d'un contenu une série d'octets avec terminaison. *
+* *
+* Retour : Bilan de l'opératon : true pour continuer, false sinon. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool g_kaitai_attribute_parse_terminated_bytes(GKaitaiAttribute *attrib, const kaitai_scope_t *locals, GBinContent *content, vmpa2t *pos, GMatchRecord **record)
+{
+ bool result; /* Bilan à retourner */
+ sized_string_t marker; /* Marqueur potentiel à tester */
+ vmpa2t iter; /* Tête de lecture courante */
+ vmpa2t end; /* Fin du parcours possible */
+ vmpa2t tmp; /* Position à mouvante */
+ phys_t diff; /* Avancée de lecture courante */
+ mrange_t range; /* Couverture appliquée */
+ SourceEndian endian; /* Boutisme à observer */
+
+ result = false;
+
+ /* Recherche du marqueur de fin */
+
+ marker.len = attrib->terminator.len;
+
+ copy_vmpa(&iter, pos);
+ g_binary_content_compute_end_pos(content, &end);
+
+ while (cmp_vmpa_by_phy(&iter, &end) < 0)
+ {
+ copy_vmpa(&tmp, &iter);
+
+ marker.data = (char *)g_binary_content_get_raw_access(content, &tmp, marker.len);
+ if (marker.data == NULL) break;
+
+ if (szmemcmp(&marker, &attrib->terminator) == 0)
+ {
+ result = true;
+ break;
+ }
+
+ advance_vmpa(&iter, 1);
+
+ }
+
+ /* Si la recherche a abouti */
+
+ if (result)
+ {
+ diff = compute_vmpa_diff(pos, &iter);
+
+ if (attrib->include)
+ diff += marker.len;
+
+ init_mrange(&range, pos, diff);
+
+ if (attrib->has_endian)
+ endian = attrib->endian;
+ else
+ endian = g_kaitai_meta_get_endian(locals->meta);
+
+ *record = G_MATCH_RECORD(g_record_item_new(attrib, content, &range, endian));
+
+ copy_vmpa(pos, &iter);
+
+ if (attrib->consume)
+ advance_vmpa(pos, marker.len);
+
+ }
+
+ /* Sinon l'absence de marqueur est-elle tolérée ? */
+
+ else if (!attrib->eos_error)
+ {
+ diff = compute_vmpa_diff(pos, &end);
+
+ init_mrange(&range, pos, diff);
+
+ if (attrib->has_endian)
+ endian = attrib->endian;
+ else
+ endian = g_kaitai_meta_get_endian(locals->meta);
+
+ *record = G_MATCH_RECORD(g_record_item_new(attrib, content, &range, endian));
+
+ copy_vmpa(pos, &end);
+
+ result = true;
+
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : attrib = structure Kaitai en cours de parcours. *
+* locals = variables locales pour les résolutions de types. *
+* content = données binaires à analyser et traduire. *
+* pos = tête de lecture courante. *
+* maxsize = taille maximale de la zone de correspondance. [OUT]*
+* range = zone de couverture à officialiser. [OUT] *
+* *
+* Description : Détermine la zone de couverture finale d'une correspondance. *
+* *
+* Retour : Bilan de l'opératon : true pour continuer, false sinon. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool g_kaitai_attribute_compute_maybe_terminated_range(const GKaitaiAttribute *attrib, const kaitai_scope_t *locals, const GBinContent *content, const vmpa2t *pos, phys_t *maxsize, mrange_t *range)
+{
+ bool result; /* Bilan à retourner */
+ sized_string_t marker; /* Marqueur potentiel à tester */
+ vmpa2t iter; /* Tête de lecture courante */
+ vmpa2t end; /* Fin du parcours possible */
+ vmpa2t tmp; /* Position à mouvante */
+ phys_t diff; /* Avancée de lecture courante */
+
+ if (attrib->terminator.data == NULL)
+ {
+ init_mrange(range, pos, *maxsize);
+ result = true;
+ }
+
+ else
+ {
+ result = false;
+
+ if (attrib->terminator.len > *maxsize)
+ goto exit;
+
+ /* Recherche du marqueur de fin */
+
+ marker.len = attrib->terminator.len;
+
+ copy_vmpa(&iter, pos);
+
+ copy_vmpa(&tmp, pos);
+ advance_vmpa(&tmp, *maxsize - marker.len);
+
+ while (cmp_vmpa_by_phy(&iter, &end) <= 0)
+ {
+ copy_vmpa(&tmp, &iter);
+
+ marker.data = (char *)g_binary_content_get_raw_access(content, &tmp, marker.len);
+ if (marker.data == NULL) break;
+
+ if (szmemcmp(&marker, &attrib->terminator) == 0)
+ {
+ result = true;
+ break;
+ }
+
+ advance_vmpa(&iter, 1);
+
+ }
+
+ /* Si la recherche a abouti */
+
+ if (result)
+ {
+ diff = compute_vmpa_diff(pos, &iter);
+
+ if (attrib->include)
+ init_mrange(range, pos, diff + marker.len);
+ else
+ init_mrange(range, pos, diff);
+
+ assert((diff + marker.len) <= *maxsize);
+
+ if (attrib->consume)
+ *maxsize = diff + marker.len;
+ else
+ *maxsize = diff;
+
+ }
+
+ /* Sinon l'absence de marqueur est-elle tolérée ? */
+
+ else if (!attrib->eos_error)
+ {
+ init_mrange(range, pos, *maxsize);
+ result = true;
+ }
+
+ }
+
+ exit:
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : attrib = structure Kaitai en cours de parcours. *
+* locals = variables locales pour les résolutions de types. *
+* content = données binaires à analyser et traduire. *
+* pos = 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_attribute_parse_content(GKaitaiAttribute *attrib, kaitai_scope_t *locals, GBinContent *content, vmpa2t *pos, GMatchRecord **record)
+{
+ bool result; /* Bilan à retourner */
+ resolved_value_t authorized; /* Validation des traitements */
+ GRecordList *list; /* Constitution d'une liste */
+ vmpa2t end; /* Position maximale du flux */
+ phys_t diff; /* Différentiel de positions */
+ GMatchRecord *child; /* Element de liste à intégrer */
+ resolved_value_t resolved; /* Valeur entière obtenue */
+ unsigned long long count; /* Nombre d'itérations à mener */
+ unsigned long long i; /* Boucle de parcours */
+ resolved_value_t loop; /* Poursuite des lectures ? */
+
+ if (attrib->repetition == KAR_NO_REPETITION)
+ result = _g_kaitai_attribute_parse_content(attrib, locals, content, pos, record);
+
+ else
+ {
+ /* Lecture soumise à condition ? */
+
+ if (attrib->condition != NULL)
+ {
+ result = resolve_kaitai_expression_as_boolean(locals,
+ attrib->condition,
+ strlen(attrib->condition),
+ &authorized);
+
+ if (!result || !authorized.status)
+ goto exit;
+
+ }
+
+ list = g_record_list_new(attrib, content, pos);
+
+ switch (attrib->repetition)
+ {
+ case KAR_END_OF_STREAM:
+
+ result = true;
+
+ g_binary_content_compute_end_pos(content, &end);
+ diff = compute_vmpa_diff(pos, &end);
+
+ while (diff > 0)
+ {
+ result = _g_kaitai_attribute_parse_content(attrib, locals, content, pos, &child);
+ if (!result) break;
+
+ g_record_list_add_record(list, child);
+ remember_last_record(locals, child);
+
+ diff = compute_vmpa_diff(pos, &end);
+
+ }
+
+ break;
+
+ case KAR_EXPRESSION:
+
+ result = resolve_kaitai_expression_as_integer(locals,
+ attrib->repeat_controller,
+ strlen(attrib->repeat_controller),
+ &resolved);
+
+ if (resolved.type == GVT_UNSIGNED_INTEGER)
+ count = resolved.unsigned_integer;
+ else
+ {
+ assert(resolved.type == GVT_SIGNED_INTEGER);
+
+ if (resolved.signed_integer < 0)
+ {
+ result = false;
+ break;
+ }
+
+ count = resolved.signed_integer;
+
+ }
+
+ for (i = 0; i < count; i++)
+ {
+ result = _g_kaitai_attribute_parse_content(attrib, locals, content, pos, &child);
+ if (!result) break;
+
+ g_record_list_add_record(list, child);
+ remember_last_record(locals, child);
+
+ }
+
+ break;
+
+ case KAR_UNTIL:
+
+ do
+ {
+ result = _g_kaitai_attribute_parse_content(attrib, locals, content, pos, &child);
+ if (!result) break;
+
+ g_record_list_add_record(list, child);
+ remember_last_record(locals, child);
+
+ result = resolve_kaitai_expression_as_boolean(locals,
+ attrib->repeat_controller,
+ strlen(attrib->repeat_controller),
+ &loop);
+ if (!result) break;
+
+ }
+ while (!loop.status);
+
+ break;
+
+ default:
+ break;
+
+ }
+
+ if (!result) g_clear_object(&list);
+
+ *record = G_MATCH_RECORD(list);
+
+ }
+
+ exit:
+
+ return result;
+
+}
diff --git a/plugins/kaitai/parsers/attribute.h b/plugins/kaitai/parsers/attribute.h
new file mode 100644
index 0000000..38b78d7
--- /dev/null
+++ b/plugins/kaitai/parsers/attribute.h
@@ -0,0 +1,154 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * attribute.h - prototypes pour la spécification d'un attribut 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/>.
+ */
+
+
+#ifndef _PLUGINS_KAITAI_PARSERS_ATTRIBUTE_H
+#define _PLUGINS_KAITAI_PARSERS_ATTRIBUTE_H
+
+
+#include <glib-object.h>
+#include <stdbool.h>
+
+
+#include <analysis/content.h>
+#include <analysis/types/basic.h>
+#include <plugins/yaml/node.h>
+
+
+#include "../expression.h"
+
+
+
+#define G_TYPE_KAITAI_ATTRIBUTE g_kaitai_attribute_get_type()
+#define G_KAITAI_ATTRIBUTE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_KAITAI_ATTRIBUTE, GKaitaiAttribute))
+#define G_IS_KAITAI_ATTRIBUTE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_KAITAI_ATTRIBUTE))
+#define G_KAITAI_ATTRIBUTE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_KAITAI_ATTRIBUTE, GKaitaiAttributeClass))
+#define G_IS_KAITAI_ATTRIBUTE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_KAITAI_ATTRIBUTE))
+#define G_KAITAI_ATTRIBUTE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_KAITAI_ATTRIBUTE, GKaitaiAttributeClass))
+
+
+/* Spécification d'un attribut Kaitai (instance) */
+typedef struct _GKaitaiAttribute GKaitaiAttribute;
+
+/* Spécification d'un attribut Kaitai (classe) */
+typedef struct _GKaitaiAttributeClass GKaitaiAttributeClass;
+
+
+/* Type de charge associée à un attribut */
+typedef enum _KaitaiAttributePayload
+{
+ KAP_UNINITIALIZED = (0 << 0), /* Type non initialisé */
+
+ KAP_FIXED_CONTENT = (1 << 0), /* Contenu brut attendu */
+ KAP_BASIC_TYPE = (1 << 1), /* Type prédéfini */
+ KAP_USER_TYPE = (1 << 2), /* Type personnalisé */
+ KAP_DYNAMIC_TYPE = (1 << 3), /* Type dynmatique */
+ KAP_SIZED = (1 << 4), /* Bourrage dimensionné */
+ KAP_SIZED_EOS = (1 << 5), /* Bourrage final */
+
+} KaitaiAttributePayload;
+
+/* Types de base reconnus par Kaitai */
+typedef enum _KaitaiBasicType
+{
+ KBT_U1, /* Octet non signé */
+ KBT_U2, /* Mot de 16 bits non signé */
+ KBT_U2LE, /* Mot de 16 bits non signé */
+ KBT_U2BE, /* Mot de 16 bits non signé */
+ KBT_U4, /* Mot de 32 bits non signé */
+ KBT_U4LE, /* Mot de 32 bits non signé */
+ KBT_U4BE, /* Mot de 32 bits non signé */
+ KBT_U8, /* Mot de 64 bits non signé */
+ KBT_U8LE, /* Mot de 64 bits non signé */
+ KBT_U8BE, /* Mot de 64 bits non signé */
+ KBT_S1, /* Octet signé */
+ KBT_S2, /* Mot de 16 bits signé */
+ KBT_S2LE, /* Mot de 16 bits signé */
+ KBT_S2BE, /* Mot de 16 bits signé */
+ KBT_S4, /* Mot de 32 bits signé */
+ KBT_S4LE, /* Mot de 32 bits signé */
+ KBT_S4BE, /* Mot de 32 bits signé */
+ KBT_S8, /* Mot de 64 bits signé */
+ KBT_S8LE, /* Mot de 64 bits signé */
+ KBT_S8BE, /* Mot de 64 bits signé */
+ KBT_F4, /* Flottant sur 32 bits */
+ KBT_F4BE, /* Flottant sur 32 bits */
+ KBT_F4LE, /* Flottant sur 32 bits */
+ KBT_F8, /* Flottant sur 64 bits */
+ KBT_F8BE, /* Flottant sur 64 bits */
+ KBT_F8LE, /* Flottant sur 64 bits */
+ KBT_STR, /* Chaîne de caractères */
+ KBT_STRZ, /* Chaîne de caractères + '\0' */
+
+} KaitaiBasicType;
+
+/* Formes de répétition d'une lecture d'attribut */
+typedef enum _KaitaiAttributeRepetition
+{
+ KAR_NO_REPETITION, /* Aucune forme de répétition */
+
+ KAR_END_OF_STREAM, /* Redites autant que possible */
+ KAR_EXPRESSION, /* Répétitions selon quantité */
+ KAR_UNTIL, /* Répétitions sous condition */
+
+} KaitaiAttributeRepetition;
+
+
+/* Indique le type défini pour un attribut de la spécification Kaitai. */
+GType g_kaitai_attribute_get_type(void);
+
+/* Construit un lecteur d'attribut Kaitai. */
+GKaitaiAttribute *g_kaitai_attribute_new(GYamlNode *);
+
+/* Dérive un lecteur d'attribut Kaitai pour un type utilisateur. */
+GKaitaiAttribute *g_kaitai_attribute_dup_for_user_type(const GKaitaiAttribute *, const char *);
+
+/* Indique l'étiquette à utiliser pour identifier un attribut. */
+const char *g_kaitai_attribute_get_label(const GKaitaiAttribute *);
+
+/* Indique la désignation brute d'un identifiant Kaitai. */
+const char *g_kaitai_attribute_get_raw_id(const GKaitaiAttribute *);
+
+/* Indique la désignation originelle d'un identifiant Kaitai. */
+const char *g_kaitai_attribute_get_original_id(const GKaitaiAttribute *);
+
+/* Fournit une éventuelle documentation concernant l'attribut. */
+const char *g_kaitai_attribute_get_doc(const GKaitaiAttribute *);
+
+/* Indique la nature de la charge représentée par l'attribut. */
+KaitaiAttributePayload g_kaitai_attribute_get_payload(const GKaitaiAttribute *);
+
+/* Précise un éventuel type de base reconnu par le lecteur. */
+bool g_kaitai_attribute_get_basic_type(const GKaitaiAttribute *, BaseType *, bool *);
+
+/* Lit les octets d'une chaîne représentée. */
+bool g_kaitai_attribute_read_truncated_bytes(const GKaitaiAttribute *, const GBinContent *, const mrange_t *, bin_t **, size_t *);
+
+/* Détermine si l'attribue porte une valeur entière signée. */
+bool g_kaitai_attribute_handle_signed_integer(const GKaitaiAttribute *);
+
+/* Lit la valeur d'un élément Kaitai entier représenté. */
+bool g_kaitai_attribute_read_value(const GKaitaiAttribute *, const GBinContent *, const mrange_t *, SourceEndian, resolved_value_t *);
+
+
+
+#endif /* _PLUGINS_KAITAI_PARSERS_ATTRIBUTE_H */
diff --git a/plugins/kaitai/parsers/enum-int.h b/plugins/kaitai/parsers/enum-int.h
new file mode 100644
index 0000000..b62ae41
--- /dev/null
+++ b/plugins/kaitai/parsers/enum-int.h
@@ -0,0 +1,79 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * enum-int.h - prototypes internes pour la 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/>.
+ */
+
+
+#ifndef PLUGINS_KAITAI_PARSERS_ENUM_INT_H
+#define PLUGINS_KAITAI_PARSERS_ENUM_INT_H
+
+
+#include "enum.h"
+
+
+
+/* ------------------------- MANIPULATION D'UNE ENUMERATION ------------------------- */
+
+
+/* Mémorisation d'une valeur d'énumération */
+typedef struct _enum_value_t
+{
+ resolved_value_t value; /* Valeur entière représentée */
+ char *label; /* Elément associé à une valeur*/
+ char *doc; /* Eventuelle documentation */
+
+} enum_value_t;
+
+
+
+/* ----------------------- GESTION D'UN GROUPE D'ENUMERATIONS ----------------------- */
+
+
+/* Définition d'un ensemble d'énumérations Kaitai (instance) */
+struct _GKaitaiEnum
+{
+ GObject parent; /* A laisser en premier */
+
+ char *name; /* Désignation de l'énumération*/
+
+ enum_value_t **cases_v2l; /* Choix indexés par valeur */
+ size_t cases_v2l_count; /* Quantité de ces choix */
+
+ enum_value_t **cases_l2v; /* Choix indexés par étiquette */
+ size_t cases_l2v_count; /* Quantité de ces choix */
+
+ enum_value_t *defcase; /* Choix par défaut ou NULL */
+
+};
+
+/* Définition d'un ensemble d'énumérations Kaitai (classe) */
+struct _GKaitaiEnumClass
+{
+ GObjectClass parent; /* A laisser en premier */
+
+};
+
+
+/* Met en place un groupe d'énumérations Kaitai. */
+bool g_kaitai_enum_create(GKaitaiEnum *, GYamlNode *);
+
+
+
+#endif /* PLUGINS_KAITAI_PARSERS_ENUM_INT_H */
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;
+
+}
diff --git a/plugins/kaitai/parsers/enum.h b/plugins/kaitai/parsers/enum.h
new file mode 100644
index 0000000..9e4bf2a
--- /dev/null
+++ b/plugins/kaitai/parsers/enum.h
@@ -0,0 +1,76 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * enum.h - prototypes pour la 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/>.
+ */
+
+
+#ifndef _PLUGINS_KAITAI_PARSERS_ENUM_H
+#define _PLUGINS_KAITAI_PARSERS_ENUM_H
+
+
+#include <glib-object.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+
+#include <common/szstr.h>
+#include <plugins/yaml/node.h>
+
+
+#include "../expression.h"
+
+
+
+#define G_TYPE_KAITAI_ENUM g_kaitai_enum_get_type()
+#define G_KAITAI_ENUM(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_KAITAI_ENUM, GKaitaiEnum))
+#define G_IS_KAITAI_ENUM(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_KAITAI_ENUM))
+#define G_KAITAI_ENUM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_KAITAI_ENUM, GKaitaiEnumClass))
+#define G_IS_KAITAI_ENUM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_KAITAI_ENUM))
+#define G_KAITAI_ENUM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_KAITAI_ENUM, GKaitaiEnumClass))
+
+
+/* Définition d'un ensemble d'énumérations Kaitai (instance) */
+typedef struct _GKaitaiEnum GKaitaiEnum;
+
+/* Définition d'un ensemble d'énumérations Kaitai (classe) */
+typedef struct _GKaitaiEnumClass GKaitaiEnumClass;
+
+
+/* Indique le type défini pour un ensemble d'énumérations Kaitai. */
+GType g_kaitai_enum_get_type(void);
+
+/* Construit un groupe d'énumérations Kaitai. */
+GKaitaiEnum *g_kaitai_enum_new(GYamlNode *);
+
+/* Fournit le nom principal d'une énumération. */
+const char *g_kaitai_enum_get_name(const GKaitaiEnum *);
+
+/* Traduit une étiquette brute en constante d'énumération. */
+bool g_kaitai_enum_find_value(const GKaitaiEnum *, const sized_string_t *, resolved_value_t *);
+
+/* Traduit une constante d'énumération en étiquette brute. */
+char *g_kaitai_enum_find_label(const GKaitaiEnum *, const resolved_value_t *, bool);
+
+/* Traduit une constante d'énumération en documentation. */
+char *g_kaitai_enum_find_documentation(const GKaitaiEnum *, const resolved_value_t *);
+
+
+
+#endif /* _PLUGINS_KAITAI_PARSERS_ENUM_H */
diff --git a/plugins/kaitai/parsers/instance-int.h b/plugins/kaitai/parsers/instance-int.h
new file mode 100644
index 0000000..6f098b4
--- /dev/null
+++ b/plugins/kaitai/parsers/instance-int.h
@@ -0,0 +1,59 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * instance-int.h - prototypes pour les spécifications internes d'une instance Kaitai
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _PLUGINS_KAITAI_PARSERS_INSTANCE_INT_H
+#define _PLUGINS_KAITAI_PARSERS_INSTANCE_INT_H
+
+
+#include "attribute-int.h"
+#include "instance.h"
+
+
+
+/* Spécification d'une instance Kaitai (instance) */
+struct _GKaitaiInstance
+{
+ GKaitaiAttribute parent; /* A laisser en premier */
+
+ char *name; /* Nom attribué à l'instance */
+
+ char *io; /* Contenu binaire forcé */
+ char *pos; /* Position forcée */
+ char *value; /* Formule pour calcul */
+
+};
+
+/* Spécification d'une instance Kaitai (classe) */
+struct _GKaitaiInstanceClass
+{
+ GKaitaiAttributeClass parent; /* A laisser en premier */
+
+};
+
+
+/* Met en place un lecteur d'instance Kaitai. */
+bool g_kaitai_instance_create(GKaitaiInstance *, GYamlNode *);
+
+
+
+#endif /* _PLUGINS_KAITAI_PARSERS_INSTANCE_INT_H */
diff --git a/plugins/kaitai/parsers/instance.c b/plugins/kaitai/parsers/instance.c
new file mode 100644
index 0000000..63db224
--- /dev/null
+++ b/plugins/kaitai/parsers/instance.c
@@ -0,0 +1,478 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * instance.c - spécification d'une instance 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 "instance.h"
+
+
+#include <assert.h>
+#include <malloc.h>
+#include <string.h>
+
+
+#include <plugins/yaml/pair.h>
+
+
+#include "instance-int.h"
+#include "../expression.h"
+#include "../records/value.h"
+
+
+
+/* -------------------- CORRESPONDANCE ENTRE INSTANCE ET BINAIRE -------------------- */
+
+
+/* Initialise la classe des instances de spécification Kaitai. */
+static void g_kaitai_instance_class_init(GKaitaiInstanceClass *);
+
+/* Initialise une instance de spécification Kaitai. */
+static void g_kaitai_instance_init(GKaitaiInstance *);
+
+/* Supprime toutes les références externes. */
+static void g_kaitai_instance_dispose(GKaitaiInstance *);
+
+/* Procède à la libération totale de la mémoire. */
+static void g_kaitai_instance_finalize(GKaitaiInstance *);
+
+
+
+/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */
+
+
+/* Parcourt un contenu binaire selon des spécifications Kaitai. */
+static bool g_kaitai_instance_parse_content(GKaitaiInstance *, kaitai_scope_t *, GBinContent *, vmpa2t *, GMatchRecord **);
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* CORRESPONDANCE ENTRE INSTANCE ET BINAIRE */
+/* ---------------------------------------------------------------------------------- */
+
+
+/* Indique le type défini pour une instance de la spécification Kaitai. */
+G_DEFINE_TYPE(GKaitaiInstance, g_kaitai_instance, G_TYPE_KAITAI_ATTRIBUTE);
+
+
+/******************************************************************************
+* *
+* Paramètres : klass = classe à initialiser. *
+* *
+* Description : Initialise la classe des instances de spécification Kaitai. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_kaitai_instance_class_init(GKaitaiInstanceClass *klass)
+{
+ GObjectClass *object; /* Autre version de la classe */
+ GKaitaiParserClass *parser; /* Ancêtre parent de la classe */
+ GKaitaiAttributeClass *attrib; /* Version parente de la classe*/
+
+ object = G_OBJECT_CLASS(klass);
+
+ object->dispose = (GObjectFinalizeFunc/* ! */)g_kaitai_instance_dispose;
+ object->finalize = (GObjectFinalizeFunc)g_kaitai_instance_finalize;
+
+ parser = G_KAITAI_PARSER_CLASS(klass);
+
+ parser->parse = (parse_kaitai_fc)g_kaitai_instance_parse_content;
+
+ attrib = G_KAITAI_ATTRIBUTE_CLASS(klass);
+
+ attrib->get_label = (get_attribute_label_fc)g_kaitai_instance_get_name;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : inst = instance à initialiser. *
+* *
+* Description : Initialise une instance de spécification Kaitai. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_kaitai_instance_init(GKaitaiInstance *inst)
+{
+ inst->name = NULL;
+
+ inst->io = NULL;
+ inst->pos = NULL;
+ inst->value = NULL;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : inst = instance d'objet GLib à traiter. *
+* *
+* Description : Supprime toutes les références externes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_kaitai_instance_dispose(GKaitaiInstance *inst)
+{
+ G_OBJECT_CLASS(g_kaitai_instance_parent_class)->dispose(G_OBJECT(inst));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : inst = instance d'objet GLib à traiter. *
+* *
+* Description : Procède à la libération totale de la mémoire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_kaitai_instance_finalize(GKaitaiInstance *inst)
+{
+ if (inst->name != NULL)
+ free(inst->name);
+
+ if (inst->io != NULL)
+ free(inst->io);
+
+ if (inst->pos != NULL)
+ free(inst->pos);
+
+ if (inst->value != NULL)
+ free(inst->value);
+
+ G_OBJECT_CLASS(g_kaitai_instance_parent_class)->finalize(G_OBJECT(inst));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : parent = noeud Yaml contenant l'instance à constituer. *
+* *
+* Description : Construit un lecteur d'instance Kaitai. *
+* *
+* Retour : Instance mise en place ou NULL en cas d'échec. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+GKaitaiInstance *g_kaitai_instance_new(GYamlNode *parent)
+{
+ GKaitaiInstance *result; /* Structure à retourner */
+
+ result = g_object_new(G_TYPE_KAITAI_INSTANCE, NULL);
+
+ if (!g_kaitai_instance_create(result, parent))
+ g_clear_object(&result);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : inst = lecteur d'instance Kaitai à initialiser pleinement. *
+* parent = noeud Yaml contenant l'instance à constituer. *
+* *
+* Description : Met en place un lecteur d'instance Kaitai. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool g_kaitai_instance_create(GKaitaiInstance *inst, GYamlNode *parent)
+{
+ bool result; /* Bilan à retourner */
+ const char *name; /* Désignation du type */
+ char *sub_path; /* Chemin d'accès suivant */
+ GYamlNode *sub; /* Contenu Yaml d'un type */
+ GYamlNode *node; /* Noeud particulier présent */
+ const char *value; /* Valeur Yaml particulière */
+
+ result = false;
+
+ /* Extraction du nom */
+
+ if (!G_IS_YAML_PAIR(parent))
+ goto exit;
+
+ name = g_yaml_pair_get_key(G_YAML_PAIR(parent));
+
+ inst->name = strdup(name);
+
+ /* Extraction des bases du type */
+
+ asprintf(&sub_path, "/%s/", name);
+ sub = g_yaml_node_find_first_by_path(parent, sub_path);
+ free(sub_path);
+
+ if (sub == NULL)
+ goto exit;
+
+ result = g_kaitai_attribute_create(G_KAITAI_ATTRIBUTE(inst), sub, false);
+
+ /* Eventuel contenu imposé */
+
+ node = g_yaml_node_find_first_by_path(sub, "/io");
+
+ if (node != NULL)
+ {
+ assert(G_IS_YAML_PAIR(node));
+
+ value = g_yaml_pair_get_value(G_YAML_PAIR(node));
+ if (value == NULL)
+ {
+ g_object_unref(G_OBJECT(node));
+ goto bad_loading;
+ }
+
+ inst->io = strdup(value);
+
+ g_object_unref(G_OBJECT(node));
+
+ }
+
+ /* Eventuelle positiion imposée */
+
+ node = g_yaml_node_find_first_by_path(sub, "/pos");
+
+ if (node != NULL)
+ {
+ assert(G_IS_YAML_PAIR(node));
+
+ value = g_yaml_pair_get_value(G_YAML_PAIR(node));
+ if (value == NULL)
+ {
+ g_object_unref(G_OBJECT(node));
+ goto bad_loading;
+ }
+
+ inst->pos = strdup(value);
+
+ g_object_unref(G_OBJECT(node));
+
+ }
+
+ /* Eventuelle formule de calcul d'une valeur */
+
+ node = g_yaml_node_find_first_by_path(sub, "/value");
+
+ if (node != NULL)
+ {
+ assert(G_IS_YAML_PAIR(node));
+
+ inst->value = g_yaml_pair_aggregate_value(G_YAML_PAIR(node));
+
+ g_object_unref(G_OBJECT(node));
+
+ if (inst->value == NULL)
+ goto bad_loading;
+
+ }
+
+ bad_loading:
+
+ g_object_unref(G_OBJECT(sub));
+
+ exit:
+
+ if (result)
+ result = (inst->pos != NULL || inst->value != NULL);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : inst = lecteur d'instance Kaitai à consulter. *
+* *
+* Description : Indique le nom attribué à une instance Kaitai. *
+* *
+* Retour : Désignation pointant l'instance. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+const char *g_kaitai_instance_get_name(const GKaitaiInstance *inst)
+{
+ char *result; /* Valeur à renvoyer */
+
+ result = inst->name;
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : inst = lecteur d'instance Kaitai à consulter. *
+* locals = variables locales pour les résolutions de types. *
+* value = valeur à sauvegarder sous une forme générique. [OUT]*
+* *
+* Description : Détermine la valeur d'un élément Kaitai entier calculé. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool g_kaitai_instance_compute_value(const GKaitaiInstance *inst, const kaitai_scope_t *locals, resolved_value_t *value)
+{
+ bool result; /* Bilan à retourner */
+
+ if (inst->value == NULL)
+ result = false;
+
+ else
+ result = resolve_kaitai_expression_as_any(locals,
+ inst->value,
+ strlen(inst->value),
+ value);
+
+ return result;
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* IMPLEMENTATION DES FONCTIONS DE CLASSE */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : inst = structure Kaitai en cours de parcours. *
+* locals = variables locales pour les résolutions de types. *
+* content = données binaires à analyser et traduire. *
+* pos = 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_instance_parse_content(GKaitaiInstance *inst, kaitai_scope_t *locals, GBinContent *content, vmpa2t *pos, GMatchRecord **record)
+{
+ bool result; /* Bilan à retourner */
+ GBinContent *work_area; /* Aire de travail */
+ GKaitaiStream *stream; /* Flux de données pour Kaitai */
+ resolved_value_t offset; /* Position à adopter */
+ vmpa2t forced_pos; /* Tete de lecture constituée */
+ GKaitaiParserClass *class; /* Classe parente à solliciter */
+
+ if (inst->value != NULL)
+ {
+ *record = G_MATCH_RECORD(g_record_value_new(inst, locals));
+
+ result = (*record != NULL);
+
+ }
+
+ else
+ {
+ /* Contenu particulier */
+
+ if (inst->io == NULL)
+ work_area = content;
+
+ else
+ {
+ result = resolve_kaitai_expression_as_stream(locals, inst->io, strlen(inst->io), &stream);
+ if (!result) goto exit;
+
+ work_area = g_kaitai_stream_get_content(stream);
+
+ g_object_unref(G_OBJECT(stream));
+
+ }
+
+ /* Tête de lecture */
+
+ g_binary_content_compute_start_pos(work_area, &forced_pos);
+
+ result = resolve_kaitai_expression_as_integer(locals, inst->pos, strlen(inst->pos), &offset);
+ if (!result) goto exit_with_content;
+
+ if (offset.type == GVT_UNSIGNED_INTEGER)
+ advance_vmpa(&forced_pos, offset.unsigned_integer);
+
+ else
+ {
+ assert(offset.type == GVT_SIGNED_INTEGER);
+
+ if (offset.signed_integer < 0)
+ {
+ result = false;
+ goto exit_with_content;
+ }
+
+ advance_vmpa(&forced_pos, offset.signed_integer);
+
+ }
+
+ /* Lecture */
+
+ class = G_KAITAI_PARSER_CLASS(g_kaitai_instance_parent_class);
+
+ result = class->parse(G_KAITAI_PARSER(inst), locals, work_area, &forced_pos, record);
+
+ exit_with_content:
+
+ if (work_area != content)
+ g_object_unref(G_OBJECT(work_area));
+
+ }
+
+ exit:
+
+ return result;
+
+}
diff --git a/plugins/kaitai/parsers/instance.h b/plugins/kaitai/parsers/instance.h
new file mode 100644
index 0000000..a9aee9a
--- /dev/null
+++ b/plugins/kaitai/parsers/instance.h
@@ -0,0 +1,68 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * instance.h - prototypes pour la spécification d'une instance Kaitai
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _PLUGINS_KAITAI_PARSERS_INSTANCE_H
+#define _PLUGINS_KAITAI_PARSERS_INSTANCE_H
+
+
+#include <glib-object.h>
+
+
+#include <plugins/yaml/node.h>
+
+
+#include "../expression.h"
+#include "../scope.h"
+
+
+
+#define G_TYPE_KAITAI_INSTANCE g_kaitai_instance_get_type()
+#define G_KAITAI_INSTANCE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_KAITAI_INSTANCE, GKaitaiInstance))
+#define G_IS_KAITAI_INSTANCE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_KAITAI_INSTANCE))
+#define G_KAITAI_INSTANCE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_KAITAI_INSTANCE, GKaitaiInstanceClass))
+#define G_IS_KAITAI_INSTANCE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_KAITAI_INSTANCE))
+#define G_KAITAI_INSTANCE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_KAITAI_INSTANCE, GKaitaiInstanceClass))
+
+
+/* Spécification d'une instance Kaitai (instance) */
+typedef struct _GKaitaiInstance GKaitaiInstance;
+
+/* Spécification d'une instance Kaitai (classe) */
+typedef struct _GKaitaiInstanceClass GKaitaiInstanceClass;
+
+
+/* Indique le type défini pour une instance de la spécification Kaitai. */
+GType g_kaitai_instance_get_type(void);
+
+/* Construit un lecteur d'instance Kaitai. */
+GKaitaiInstance *g_kaitai_instance_new(GYamlNode *);
+
+/* Indique le nom attribué à une instance Kaitai. */
+const char *g_kaitai_instance_get_name(const GKaitaiInstance *);
+
+/* Détermine la valeur d'un élément Kaitai entier calculé. */
+bool g_kaitai_instance_compute_value(const GKaitaiInstance *, const kaitai_scope_t *, resolved_value_t *);
+
+
+
+#endif /* _PLUGINS_KAITAI_PARSERS_INSTANCE_H */
diff --git a/plugins/kaitai/parsers/meta-int.h b/plugins/kaitai/parsers/meta-int.h
new file mode 100644
index 0000000..7d847ef
--- /dev/null
+++ b/plugins/kaitai/parsers/meta-int.h
@@ -0,0 +1,57 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * meta-int.h - prototypes internes pour la description globale d'une définition 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/>.
+ */
+
+
+#ifndef PLUGINS_KAITAI_PARSERS_META_INT_H
+#define PLUGINS_KAITAI_PARSERS_META_INT_H
+
+
+#include "meta.h"
+
+
+
+/* Description globale d'une définition Kaitai (instance) */
+struct _GKaitaiMeta
+{
+ GObject parent; /* A laisser en premier */
+
+ char *id; /* Identifiant attribué */
+ char *title; /* Désignation de la définition*/
+
+ SourceEndian endian; /* Boutisme par défaut */
+
+};
+
+/* Description globale d'une définition Kaitai (classe) */
+struct _GKaitaiMetaClass
+{
+ GObjectClass parent; /* A laisser en premier */
+
+};
+
+
+/* Met en place une description globale Kaitai. */
+bool g_kaitai_meta_create(GKaitaiMeta *, GYamlNode *);
+
+
+
+#endif /* PLUGINS_KAITAI_PARSERS_META_INT_H */
diff --git a/plugins/kaitai/parsers/meta.c b/plugins/kaitai/parsers/meta.c
new file mode 100644
index 0000000..dc30c73
--- /dev/null
+++ b/plugins/kaitai/parsers/meta.c
@@ -0,0 +1,319 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * meta.c - description globale d'une définition Kaitai
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "meta.h"
+
+
+#include <assert.h>
+#include <malloc.h>
+#include <string.h>
+
+
+#include <plugins/yaml/pair.h>
+
+
+#include "meta-int.h"
+
+
+
+/* Initialise la classe des descriptions globales Kaitai. */
+static void g_kaitai_meta_class_init(GKaitaiMetaClass *);
+
+/* Initialise une description globale de définition Kaitai. */
+static void g_kaitai_meta_init(GKaitaiMeta *);
+
+/* Supprime toutes les références externes. */
+static void g_kaitai_meta_dispose(GKaitaiMeta *);
+
+/* Procède à la libération totale de la mémoire. */
+static void g_kaitai_meta_finalize(GKaitaiMeta *);
+
+
+
+/* Indique le type défini pour une description globale Kaitai. */
+G_DEFINE_TYPE(GKaitaiMeta, g_kaitai_meta, G_TYPE_OBJECT);
+
+
+/******************************************************************************
+* *
+* Paramètres : klass = classe à initialiser. *
+* *
+* Description : Initialise la classe des descriptions globales Kaitai. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_kaitai_meta_class_init(GKaitaiMetaClass *klass)
+{
+ GObjectClass *object; /* Autre version de la classe */
+
+ object = G_OBJECT_CLASS(klass);
+
+ object->dispose = (GObjectFinalizeFunc/* ! */)g_kaitai_meta_dispose;
+ object->finalize = (GObjectFinalizeFunc)g_kaitai_meta_finalize;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : meta = instance à initialiser. *
+* *
+* Description : Initialise une description globale de définition Kaitai. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_kaitai_meta_init(GKaitaiMeta *meta)
+{
+ meta->id = NULL;
+ meta->title = NULL;
+
+ meta->endian = SRE_LITTLE;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : meta = instance d'objet GLib à traiter. *
+* *
+* Description : Supprime toutes les références externes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_kaitai_meta_dispose(GKaitaiMeta *meta)
+{
+ G_OBJECT_CLASS(g_kaitai_meta_parent_class)->dispose(G_OBJECT(meta));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : meta = instance d'objet GLib à traiter. *
+* *
+* Description : Procède à la libération totale de la mémoire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_kaitai_meta_finalize(GKaitaiMeta *meta)
+{
+ if (meta->id != NULL)
+ free(meta->id);
+
+ if (meta->title != NULL)
+ free(meta->title);
+
+ G_OBJECT_CLASS(g_kaitai_meta_parent_class)->finalize(G_OBJECT(meta));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : parent = noeud Yaml contenant l'attribut à constituer. *
+* *
+* Description : Construit une description globale Kaitai. *
+* *
+* Retour : Instance mise en place ou NULL en cas d'échec. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+GKaitaiMeta *g_kaitai_meta_new(GYamlNode *parent)
+{
+ GKaitaiMeta *result; /* Identifiant à retourner */
+
+ result = g_object_new(G_TYPE_KAITAI_META, NULL);
+
+ if (!g_kaitai_meta_create(result, parent))
+ g_clear_object(&result);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : meta = description globale à initialiser pleinement. *
+* parent = noeud Yaml contenant l'attribut à constituer. *
+* *
+* Description : Met en place une description globale Kaitai. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool g_kaitai_meta_create(GKaitaiMeta *meta, GYamlNode *parent)
+{
+ bool result; /* Bilan à retourner */
+ GYamlNode *node; /* Noeud particulier présent */
+ const char *value; /* Valeur Yaml particulière */
+
+ result = true;
+
+ /* Identifiant */
+
+ node = g_yaml_node_find_first_by_path(parent, "/meta/id");
+
+ if (node != NULL)
+ {
+ assert(G_IS_YAML_PAIR(node));
+
+ value = g_yaml_pair_get_value(G_YAML_PAIR(node));
+
+ if (value != NULL)
+ meta->id = strdup(value);
+
+ g_object_unref(G_OBJECT(node));
+
+ }
+
+ /* Titre */
+
+ node = g_yaml_node_find_first_by_path(parent, "/meta/title");
+
+ if (node != NULL)
+ {
+ assert(G_IS_YAML_PAIR(node));
+
+ value = g_yaml_pair_get_value(G_YAML_PAIR(node));
+
+ if (value != NULL)
+ meta->title = strdup(value);
+
+ g_object_unref(G_OBJECT(node));
+
+ }
+
+ /* Boutisme */
+
+ node = g_yaml_node_find_first_by_path(parent, "/meta/endian");
+
+ if (node != NULL)
+ {
+ assert(G_IS_YAML_PAIR(node));
+
+ value = g_yaml_pair_get_value(G_YAML_PAIR(node));
+
+ if (strcmp(value, "le") == 0)
+ meta->endian = SRE_LITTLE;
+
+ else if (strcmp(value, "be") == 0)
+ meta->endian = SRE_BIG;
+
+ g_object_unref(G_OBJECT(node));
+
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : meta = description globale à consulter. *
+* *
+* Description : Fournit l'identifié associé à une définiton Kaitai. *
+* *
+* Retour : Identifiant de définition complète ou NULL. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+const char *g_kaitai_meta_get_id(const GKaitaiMeta *meta)
+{
+ const char *result; /* Chaîne à retourner */
+
+ result = meta->id;
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : meta = description globale à consulter. *
+* *
+* Description : Fournit la désignation humaine d'une définiton Kaitai. *
+* *
+* Retour : Intitulé de définition OU NULL. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+const char *g_kaitai_meta_get_title(const GKaitaiMeta *meta)
+{
+ const char *result; /* Chaîne à retourner */
+
+ result = meta->title;
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : meta = description globale à consulter. *
+* *
+* Description : Indique le boustime observé par défaut par une définiton. *
+* *
+* Retour : Boustime, petit par défaut. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+SourceEndian g_kaitai_meta_get_endian(const GKaitaiMeta *meta)
+{
+ SourceEndian result; /* Chaîne à retourner */
+
+ result = meta->endian;
+
+ return result;
+
+}
diff --git a/plugins/kaitai/parsers/meta.h b/plugins/kaitai/parsers/meta.h
new file mode 100644
index 0000000..3797823
--- /dev/null
+++ b/plugins/kaitai/parsers/meta.h
@@ -0,0 +1,68 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * meta.h - prototypes pour la description globale d'une définition Kaitai
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _PLUGINS_KAITAI_PARSERS_META_H
+#define _PLUGINS_KAITAI_PARSERS_META_H
+
+
+#include <glib-object.h>
+
+
+#include <common/endianness.h>
+#include <plugins/yaml/node.h>
+
+
+
+#define G_TYPE_KAITAI_META g_kaitai_meta_get_type()
+#define G_KAITAI_META(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_KAITAI_META, GKaitaiMeta))
+#define G_IS_KAITAI_META(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_KAITAI_META))
+#define G_KAITAI_META_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_KAITAI_META, GKaitaiMetaClass))
+#define G_IS_KAITAI_META_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_KAITAI_META))
+#define G_KAITAI_META_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_KAITAI_META, GKaitaiMetaClass))
+
+
+/* Description globale d'une définition Kaitai (instance) */
+typedef struct _GKaitaiMeta GKaitaiMeta;
+
+/* Description globale d'une définition Kaitai (classe) */
+typedef struct _GKaitaiMetaClass GKaitaiMetaClass;
+
+
+/* Indique le type défini pour une description globale Kaitai. */
+GType g_kaitai_meta_get_type(void);
+
+/* Construit une description globale Kaitai. */
+GKaitaiMeta *g_kaitai_meta_new(GYamlNode *);
+
+/* Fournit l'identifié associé à une définiton Kaitai. */
+const char *g_kaitai_meta_get_id(const GKaitaiMeta *);
+
+/* Fournit la désignation humaine d'une définiton Kaitai. */
+const char *g_kaitai_meta_get_title(const GKaitaiMeta *);
+
+/* Indique le boustime observé par défaut par une définiton. */
+SourceEndian g_kaitai_meta_get_endian(const GKaitaiMeta *);
+
+
+
+#endif /* _PLUGINS_KAITAI_PARSERS_META_H */
diff --git a/plugins/kaitai/parsers/struct-int.h b/plugins/kaitai/parsers/struct-int.h
new file mode 100644
index 0000000..f34be32
--- /dev/null
+++ b/plugins/kaitai/parsers/struct-int.h
@@ -0,0 +1,75 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * struct-int.h - prototypes internes pour la 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/>.
+ */
+
+
+#ifndef PLUGINS_KAITAI_PARSERS_STRUCT_INT_H
+#define PLUGINS_KAITAI_PARSERS_STRUCT_INT_H
+
+
+#include "attribute.h"
+#include "instance.h"
+#include "struct.h"
+#include "../parser-int.h"
+
+
+
+/* Spécification d'une structure Kaitai (instance) */
+struct _GKaitaiStruct
+{
+ GKaitaiParser parent; /* A laisser en premier */
+
+ GKaitaiMeta *meta; /* Description globale */
+
+ GKaitaiAttribute **seq_items; /* Sous-attributs présents */
+ size_t seq_items_count; /* Quantité de ces attributs */
+
+ GKaitaiType **types; /* Types particuliers définis */
+ size_t types_count; /* Quantité de ces types */
+
+ GKaitaiInstance **instances; /* Instances prises en charge */
+ size_t instances_count; /* Quantité de ces instances */
+
+ GKaitaiEnum **enums; /* Enumérations locales */
+ size_t enums_count; /* Quantité de ces énumérations*/
+
+};
+
+/* Spécification d'une structure Kaitai (classe) */
+struct _GKaitaiStructClass
+{
+ GKaitaiParserClass parent; /* A laisser en premier */
+
+};
+
+
+/* Met en place un interpréteur de définitions Kaitai. */
+bool g_kaitai_structure_create_from_text(GKaitaiStruct *, const char *);
+
+/* Met en place un interpréteur de définitions Kaitai. */
+bool g_kaitai_structure_create_from_file(GKaitaiStruct *, const char *);
+
+/* Met en place un lecteur de définitions Kaitai. */
+bool g_kaitai_structure_create(GKaitaiStruct *, GYamlNode *);
+
+
+
+#endif /* PLUGINS_KAITAI_PARSERS_STRUCT_INT_H */
diff --git a/plugins/kaitai/parsers/struct.c b/plugins/kaitai/parsers/struct.c
new file mode 100644
index 0000000..6d97110
--- /dev/null
+++ b/plugins/kaitai/parsers/struct.c
@@ -0,0 +1,782 @@
+
+/* 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 "../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 un ensemble de définitions Kaitai. */
+static bool g_kaitai_structure_load(GKaitaiStruct *, GYamlNode *);
+
+
+
+/* --------------------- 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 *, vmpa2t *, 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->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->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_load(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 */
+
+ root = parse_yaml_from_file(filename);
+
+ if (root != NULL)
+ {
+ result = g_kaitai_structure_load(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. *
+* root = racine YAML à parcourir. *
+* *
+* Description : Charge un ensemble de définitions Kaitai. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool g_kaitai_structure_load(GKaitaiStruct *kstruct, GYamlNode *root)
+{
+ bool result; /* Bilan à retourner */
+
+ result = g_kaitai_structure_create(kstruct, root);
+
+ 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 */
+ bool failed; /* Détection d'un échec */
+
+ result = false;
+
+ /* Informations générales */
+
+ kstruct->meta = g_kaitai_meta_new(parent);
+ assert(kstruct->meta != NULL);
+
+ /* 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)
+ {
+ kstruct->types = calloc(count, sizeof(GKaitaiType *));
+ kstruct->types_count = count;
+
+ for (i = 0; i < count; i++)
+ {
+ kstruct->types[i] = g_kaitai_type_new(nodes[i]);
+ if (kstruct->types[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 = 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 (szstrcmp(name, other) == 0)
+ {
+ 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;
+ }
+
+ }
+
+ 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 */
+ bool status; /* Bilan de l'analyse */
+
+ g_binary_content_compute_start_pos(content, &pos);
+
+ init_record_scope(&locals, kstruct->meta);
+
+ status = g_kaitai_parser_parse_content(G_KAITAI_PARSER(kstruct), &locals, content, &pos, &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. *
+* pos = 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, vmpa2t *pos, 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, pos));
+
+ 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;
+
+ for (i = 0; i < kstruct->seq_items_count; i++)
+ {
+ result = g_kaitai_parser_parse_content(G_KAITAI_PARSER(kstruct->seq_items[i]),
+ locals, content, pos, &child);
+ if (!result) goto exit;
+
+ if (child != NULL)
+ {
+ g_record_group_add_record(group, child);
+ g_object_unref(G_OBJECT(child));
+ }
+
+ }
+
+ for (i = 0; i < kstruct->instances_count; i++)
+ {
+ result = g_kaitai_parser_parse_content(G_KAITAI_PARSER(kstruct->instances[i]),
+ locals, content, pos, &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;
+
+ }
+
+ return result;
+
+}
diff --git a/plugins/kaitai/parsers/struct.h b/plugins/kaitai/parsers/struct.h
new file mode 100644
index 0000000..4a2397a
--- /dev/null
+++ b/plugins/kaitai/parsers/struct.h
@@ -0,0 +1,80 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * struct.h - prototypes pour la 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/>.
+ */
+
+
+#ifndef _PLUGINS_KAITAI_PARSERS_STRUCT_H
+#define _PLUGINS_KAITAI_PARSERS_STRUCT_H
+
+
+#include <glib-object.h>
+#include <stdbool.h>
+
+
+#include <analysis/content.h>
+
+
+#include "enum.h"
+#include "meta.h"
+#include "type.h"
+#include "../record.h"
+
+
+
+#define G_TYPE_KAITAI_STRUCT g_kaitai_structure_get_type()
+#define G_KAITAI_STRUCT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_KAITAI_STRUCT, GKaitaiStruct))
+#define G_IS_KAITAI_STRUCT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_KAITAI_STRUCT))
+#define G_KAITAI_STRUCT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_KAITAI_STRUCT, GKaitaiStructClass))
+#define G_IS_KAITAI_STRUCT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_KAITAI_STRUCT))
+#define G_KAITAI_STRUCT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_KAITAI_STRUCT, GKaitaiStructClass))
+
+
+/* Spécification d'une structure Kaitai (instance) */
+typedef struct _GKaitaiStruct GKaitaiStruct;
+
+/* Spécification d'une structure Kaitai (classe) */
+typedef struct _GKaitaiStructClass GKaitaiStructClass;
+
+
+/* Indique le type défini pour une structure Kaitai. */
+GType g_kaitai_structure_get_type(void);
+
+/* Crée un nouvel interpréteur de structure Kaitai. */
+GKaitaiStruct *g_kaitai_structure_new_from_text(const char *);
+
+/* Crée un nouvel interpréteur de structure Kaitai. */
+GKaitaiStruct *g_kaitai_structure_new_from_file(const char *);
+
+/* Fournit la description globale d'une définition Kaitai. */
+GKaitaiMeta *g_kaitai_structure_get_meta(const GKaitaiStruct *);
+
+/* Recherche la définition d'un type nouveau pour Kaitai. */
+GKaitaiType *g_kaitai_structure_find_sub_type(const GKaitaiStruct *, const char *);
+
+/* Fournit un ensemble d'énumérations locales de la structure. */
+GKaitaiEnum *g_kaitai_structure_get_enum(const GKaitaiStruct *, const sized_string_t *);
+
+/* Parcourt un contenu binaire selon une description Kaitai. */
+GMatchRecord *g_kaitai_structure_parse(GKaitaiStruct *, GBinContent *);
+
+
+
+#endif /* _PLUGINS_KAITAI_PARSERS_STRUCT_H */
diff --git a/plugins/kaitai/parsers/switch-int.h b/plugins/kaitai/parsers/switch-int.h
new file mode 100644
index 0000000..a087e49
--- /dev/null
+++ b/plugins/kaitai/parsers/switch-int.h
@@ -0,0 +1,78 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * switch-int.h - prototypes internes pour la 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/>.
+ */
+
+
+#ifndef PLUGINS_KAITAI_PARSERS_SWITCH_INT_H
+#define PLUGINS_KAITAI_PARSERS_SWITCH_INT_H
+
+
+#include "switch.h"
+#include "../parser-int.h"
+
+
+
+/* ------------------------ BASCULE DYNAMIQUE SELON CONTEXTE ------------------------ */
+
+
+/* Mémorisation d'une valeur d'énumération */
+typedef struct _switch_case_t
+{
+ char *value; /* Valeur d'association */
+ char *type; /* Désignation du type associé */
+
+} switch_case_t;
+
+
+
+/* ----------------------- SELECTION DYNAMIQUE DE TYPE KAITAI ----------------------- */
+
+
+/* Sélection d'un type selon un contexte (instance) */
+struct _GKaitaiSwitch
+{
+ GKaitaiParser parent; /* A laisser en premier */
+
+ char *target; /* Source de bascule */
+
+ switch_case_t **cases; /* Choix de types potentiels */
+ size_t count; /* Quantité de ces choix */
+
+ switch_case_t *defcase; /* Choix par défaut ou NULL */
+
+ GKaitaiAttribute *generic; /* Attribut à dériver */
+
+};
+
+/* Sélection d'un type selon un contexte (classe) */
+struct _GKaitaiSwitchClass
+{
+ GKaitaiParserClass parent; /* A laisser en premier */
+
+};
+
+
+/* Met en place une sélection dynamique de type Kaitai. */
+bool g_kaitai_switch_create(GKaitaiSwitch *, GYamlNode *, GKaitaiAttribute *);
+
+
+
+#endif /* PLUGINS_KAITAI_PARSERS_SWITCH_INT_H */
diff --git a/plugins/kaitai/parsers/switch.c b/plugins/kaitai/parsers/switch.c
new file mode 100644
index 0000000..c823f27
--- /dev/null
+++ b/plugins/kaitai/parsers/switch.c
@@ -0,0 +1,706 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * switch.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 "switch.h"
+
+
+#include <assert.h>
+#include <errno.h>
+#include <malloc.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+#include <i18n.h>
+
+
+#include <common/extstr.h>
+#include <common/sort.h>
+#include <core/logs.h>
+#include <plugins/yaml/pair.h>
+
+
+#include "switch-int.h"
+#include "../expression.h"
+
+
+
+/* ------------------------ BASCULE DYNAMIQUE SELON CONTEXTE ------------------------ */
+
+
+/* Construit une valeur d'énumération à partir d'indications. */
+static switch_case_t *build_switch_case(const GYamlNode *, bool *);
+
+/* Supprime de la mémoire une bascule selon contexte. */
+static void delete_switch_case(switch_case_t *);
+
+/* Détermine si le cas correspond à une valeur de bascule. */
+static const char *is_suitable_switch_case_for_bytes(const switch_case_t *, const resolved_value_t *);
+
+/* Détermine si le cas correspond à une valeur de bascule. */
+static const char *is_suitable_switch_case_for_integer(const switch_case_t *, kaitai_scope_t *, const resolved_value_t *);
+
+
+
+/* ----------------------- SELECTION DYNAMIQUE DE TYPE KAITAI ----------------------- */
+
+
+/* Initialise la classe des sélections dynamiques de types. */
+static void g_kaitai_switch_class_init(GKaitaiSwitchClass *);
+
+/* Initialise une sélection dynamique de type Kaitai. */
+static void g_kaitai_switch_init(GKaitaiSwitch *);
+
+/* Supprime toutes les références externes. */
+static void g_kaitai_switch_dispose(GKaitaiSwitch *);
+
+/* Procède à la libération totale de la mémoire. */
+static void g_kaitai_switch_finalize(GKaitaiSwitch *);
+
+
+
+/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */
+
+
+/* Parcourt un contenu binaire selon des spécifications Kaitai. */
+static bool g_kaitai_switch_parse_content(GKaitaiSwitch *, kaitai_scope_t *, GBinContent *, vmpa2t *, GMatchRecord **);
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* BASCULE DYNAMIQUE SELON CONTEXTE */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* 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 switch_case_t *build_switch_case(const GYamlNode *node, bool *defcase)
+{
+ switch_case_t *result; /* Enregistrement à retourner */
+ const char *key; /* Clef d'une conversion */
+ const char *value; /* Valeur Yaml particulière */
+
+ result = NULL;
+
+ if (!G_IS_YAML_PAIR(node))
+ goto exit;
+
+ key = g_yaml_pair_get_key(G_YAML_PAIR(node));
+ value = g_yaml_pair_get_value(G_YAML_PAIR(node));
+
+ if (value == NULL)
+ goto exit;
+
+ result = malloc(sizeof(switch_case_t));
+
+ result->value = strdup(key);
+ result->type = strdup(value);
+
+ *defcase = (strcmp(key, "_") == 0);
+
+ exit:
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : swcase = valeur à traiter. *
+* *
+* Description : Supprime de la mémoire une bascule selon contexte. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+*****************************************************************************/
+
+static void delete_switch_case(switch_case_t *swcase)
+{
+ free(swcase->value);
+
+ free(swcase->type);
+
+ free(swcase);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : swcase = valeur à analyser. *
+* value = valeur à comparer. *
+* *
+* Description : Détermine si le cas correspond à une valeur de bascule. *
+* *
+* Retour : Type à utiliser ou NULL si aucune correspondance établie. *
+* *
+* Remarques : - *
+* *
+*****************************************************************************/
+
+static const char *is_suitable_switch_case_for_bytes(const switch_case_t *swcase, const resolved_value_t *value)
+{
+ const char *result; /* Désignation à retourner */
+ sized_string_t key; /* Changement de format */
+ bool valid; /* Validité des opérations */
+ int ret; /* Bilan d'une comparaison */
+
+ result = NULL;
+
+ key.data = swcase->value;
+ key.len = strlen(swcase->value);
+
+ valid = (key.len > 2);
+
+ if (valid)
+ valid = (swcase->value[0] == '"' || swcase->value[0] == '\'');
+
+ if (valid)
+ {
+ valid = (key.data[0] == key.data[key.len - 1]);
+
+ key.data++;
+ key.len -= 2;
+
+ }
+
+ if (valid)
+ {
+ if (value->type == GVT_BYTES)
+ {
+ ret = szmemcmp(&key, &value->bytes);
+
+ if (ret == 0)
+ result = swcase->type;
+
+ }
+
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : swcase = valeur à analyser. *
+* locals = variables locales pour les résolutions de types. *
+* value = valeur à comparer. *
+* *
+* Description : Détermine si le cas correspond à une valeur de bascule. *
+* *
+* Retour : Type à utiliser ou NULL si aucune correspondance établie. *
+* *
+* Remarques : - *
+* *
+*****************************************************************************/
+
+static const char *is_suitable_switch_case_for_integer(const switch_case_t *swcase, kaitai_scope_t *locals, const resolved_value_t *value)
+{
+ const char *result; /* Désignation à retourner */
+ bool valid; /* Validité des opérations */
+ resolved_value_t key; /* Changement de format */
+ unsigned long long unsigned_conv; /* Valeur convertie #1 */
+ long long signed_conv; /* Valeur convertie #2 */
+
+ result = NULL;
+
+ valid = (swcase->value[0] != '"' && swcase->value[0] != '\'');
+
+ if (valid)
+ {
+ if (strchr(swcase->value, ':') != NULL)
+ {
+ valid = resolve_kaitai_expression_as_integer(locals, swcase->value, strlen(swcase->value), &key);
+
+ if (valid)
+ {
+ if (key.type == GVT_UNSIGNED_INTEGER)
+ {
+ if (value->type == GVT_UNSIGNED_INTEGER)
+ {
+ if (key.unsigned_integer == value->unsigned_integer)
+ result = swcase->type;
+ }
+ else
+ {
+ if (key.unsigned_integer == value->signed_integer)
+ result = swcase->type;
+ }
+ }
+ else
+ {
+ if (value->type == GVT_UNSIGNED_INTEGER)
+ {
+ if (key.signed_integer == value->unsigned_integer)
+ result = swcase->type;
+ }
+ else
+ {
+ if (key.signed_integer == value->signed_integer)
+ result = swcase->type;
+ }
+ }
+
+ }
+
+ }
+
+ else
+ {
+ if (value->type == GVT_UNSIGNED_INTEGER)
+ {
+ unsigned_conv = strtoull(swcase->value, NULL, 10);
+
+ valid = (errno != ERANGE && errno != EINVAL);
+
+ if (valid && unsigned_conv == value->unsigned_integer)
+ result = swcase->type;
+
+ }
+ else
+ {
+ signed_conv = strtoll(swcase->value, NULL, 10);
+
+ valid = (errno != ERANGE && errno != EINVAL);
+
+ if (valid && signed_conv == value->signed_integer)
+ result = swcase->type;
+
+ }
+
+ }
+
+ }
+
+ return result;
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* SELECTION DYNAMIQUE DE TYPE KAITAI */
+/* ---------------------------------------------------------------------------------- */
+
+
+/* Indique le type défini pour un choix dynamique de type Kaitai. */
+G_DEFINE_TYPE(GKaitaiSwitch, g_kaitai_switch, G_TYPE_KAITAI_PARSER);
+
+
+/******************************************************************************
+* *
+* Paramètres : klass = classe à initialiser. *
+* *
+* Description : Initialise la classe des sélections dynamiques de types. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_kaitai_switch_class_init(GKaitaiSwitchClass *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_switch_dispose;
+ object->finalize = (GObjectFinalizeFunc)g_kaitai_switch_finalize;
+
+ parser = G_KAITAI_PARSER_CLASS(klass);
+
+ parser->parse = (parse_kaitai_fc)g_kaitai_switch_parse_content;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : kswitch = instance à initialiser. *
+* *
+* Description : Initialise une sélection dynamique de type Kaitai. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_kaitai_switch_init(GKaitaiSwitch *kswitch)
+{
+ kswitch->target = NULL;
+
+ kswitch->cases = NULL;
+ kswitch->count = 0;
+
+ kswitch->defcase = NULL;
+
+ kswitch->generic = NULL;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : kswitch = instance d'objet GLib à traiter. *
+* *
+* Description : Supprime toutes les références externes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_kaitai_switch_dispose(GKaitaiSwitch *kswitch)
+{
+ g_clear_object(&kswitch->generic);
+
+ G_OBJECT_CLASS(g_kaitai_switch_parent_class)->dispose(G_OBJECT(kswitch));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : kswitch = instance d'objet GLib à traiter. *
+* *
+* Description : Procède à la libération totale de la mémoire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_kaitai_switch_finalize(GKaitaiSwitch *kswitch)
+{
+ size_t i; /* Boucle de parcours */
+
+ if (kswitch->target != NULL)
+ free(kswitch->target);
+
+ for (i = 0; i < kswitch->count; i++)
+ delete_switch_case(kswitch->cases[i]);
+
+ if (kswitch->cases != NULL)
+ free(kswitch->cases);
+
+ if (kswitch->defcase != NULL)
+ delete_switch_case(kswitch->defcase);
+
+ G_OBJECT_CLASS(g_kaitai_switch_parent_class)->finalize(G_OBJECT(kswitch));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : parent = noeud Yaml contenant l'attribut à constituer. *
+* generic = lecteur d'attribut Kaitai à dériver. *
+* *
+* Description : Construit une sélection dynamique de type Kaitai. *
+* *
+* Retour : Instance mise en place ou NULL en cas d'échec. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+GKaitaiSwitch *g_kaitai_switch_new(GYamlNode *parent, GKaitaiAttribute *generic)
+{
+ GKaitaiSwitch *result; /* Identifiant à retourner */
+
+ result = g_object_new(G_TYPE_KAITAI_SWITCH, NULL);
+
+ if (!g_kaitai_switch_create(result, parent, generic))
+ g_clear_object(&result);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : kswitch = sélectionneur de type à initialiser pleinement. *
+* parent = noeud Yaml contenant l'attribut à constituer. *
+* generic = lecteur d'attribut Kaitai à dériver. *
+* *
+* Description : Met en place une sélection dynamique de type Kaitai. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool g_kaitai_switch_create(GKaitaiSwitch *kswitch, GYamlNode *parent, GKaitaiAttribute *generic)
+{
+ bool result; /* Bilan à retourner */
+ GYamlNode *node; /* Noeud de définition */
+ GYamlNode *subnode; /* Noeud de précisions */
+ const char *value; /* Valeur Yaml particulière */
+ GYamlNode *collec; /* Liste de noeuds à traiter */
+ GYamlNode **subnodes; /* Eventuels noeuds trouvés */
+ size_t count; /* Quantité de ces noeuds */
+ size_t i; /* Boucle de parcours */
+ bool defcase; /* Définition par défaut ? */
+ switch_case_t *swcase; /* Bascule à noter */
+
+ result = false;
+
+ node = g_yaml_node_find_first_by_path(parent, "/type/");
+ if (node == NULL) goto exit;
+
+ /* Source de la bascule */
+
+ subnode = g_yaml_node_find_first_by_path(node, "/switch-on");
+ assert(G_IS_YAML_PAIR(subnode));
+
+ value = g_yaml_pair_get_value(G_YAML_PAIR(subnode));
+ if (value == NULL)
+ {
+ g_object_unref(G_OBJECT(subnode));
+ goto bad_definition;
+ }
+
+ kswitch->target = strdup(value);
+
+ g_object_unref(G_OBJECT(subnode));
+
+ /* Conditions de bascule */
+
+ collec = g_yaml_node_find_first_by_path(node, "/cases/");
+ if (collec == NULL) goto bad_definition;
+ if (!G_IS_YAML_COLLEC(collec)) goto bad_definition;
+
+ subnodes = g_yaml_collection_get_nodes(G_YAML_COLLEC(collec), &count);
+
+ g_object_unref(G_OBJECT(collec));
+
+ if (count == 0) goto bad_definition;
+
+ for (i = 0; i < count; i++)
+ {
+ swcase = build_switch_case(subnodes[i], &defcase);
+ if (swcase == NULL) break;
+
+ g_object_unref(G_OBJECT(subnodes[i]));
+
+ kswitch->cases = realloc(kswitch->cases, ++kswitch->count * sizeof(switch_case_t *));
+ kswitch->cases[kswitch->count - 1] = swcase;
+
+ }
+
+ result = (i == count);
+
+ for (; i < count; i++)
+ g_object_unref(G_OBJECT(subnodes[i]));
+
+ if (subnodes != NULL)
+ free(subnodes);
+
+ /* Fin des procédures */
+
+ if (result)
+ {
+ kswitch->generic = generic;
+ g_object_ref(G_OBJECT(generic));
+ }
+
+ bad_definition:
+
+ g_object_unref(G_OBJECT(node));
+
+ exit:
+
+ return result;
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* IMPLEMENTATION DES FONCTIONS DE CLASSE */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : kswitch = structure Kaitai en cours de parcours. *
+* locals = variables locales pour les résolutions de types. *
+* content = données binaires à analyser et traduire. *
+* pos = 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_switch_parse_content(GKaitaiSwitch *kswitch, kaitai_scope_t *locals, GBinContent *content, vmpa2t *pos, GMatchRecord **record)
+{
+ bool result; /* Bilan à retourner */
+ GMatchRecord *reference; /* Correspondance à utiliser */
+ GKaitaiParser *creator; /* Lecteur d'origine */
+ KaitaiAttributePayload payload; /* Type de charge supportée */
+ BaseType basic; /* Type de base reconnu */
+ bool is_string; /* Type lié à une chaîne ? */
+#ifndef NDEBUG
+ bool status; /* Bilan d'une consultation */
+#endif
+ const char *final_type; /* Type à utiliser au final */
+ resolved_value_t value; /* Valeur de cible entière */
+ size_t i; /* Boucle de parcours */
+ GKaitaiAttribute *attrib; /* Lecteur approprié */
+
+ result = false;
+
+ /* Détermination de la forme de comparaison */
+
+ reference = g_match_record_find_by_name(locals->parent,
+ kswitch->target, strlen(kswitch->target),
+ DIRECT_SEARCH_DEEP_LEVEL);
+
+ if (reference == NULL)
+ goto exit;
+
+ creator = g_match_record_get_creator(reference);
+
+ if (creator == NULL)
+ goto exit_with_ref;
+
+ if (!G_IS_KAITAI_ATTRIBUTE(creator))
+ goto exit_with_creator;
+
+ payload = g_kaitai_attribute_get_payload(G_KAITAI_ATTRIBUTE(creator));
+
+ if ((payload & KAP_BASIC_TYPE) == 0)
+ goto exit_with_creator;
+
+#ifndef NDEBUG
+ status = g_kaitai_attribute_get_basic_type(G_KAITAI_ATTRIBUTE(creator), &basic, &is_string);
+ assert(status);
+#else
+ g_kaitai_attribute_get_basic_type(G_KAITAI_ATTRIBUTE(creator), &basic, &is_string);
+#endif
+
+ /* Détermination du type visé */
+
+ final_type = NULL;
+
+ if (is_string)
+ {
+ result = resolve_kaitai_expression_as_bytes(locals,
+ kswitch->target,
+ strlen(kswitch->target),
+ &value);
+ if (!result) goto exit_with_creator;
+
+ for (i = 0; i < kswitch->count; i++)
+ {
+ final_type = is_suitable_switch_case_for_bytes(kswitch->cases[i], &value);
+
+ if (final_type != NULL)
+ break;
+
+ }
+
+ }
+
+ else
+ {
+ if (basic == BTP_UCHAR || basic == BTP_USHORT || basic == BTP_UINT || basic == BTP_ULONG_LONG)
+ {
+ result = resolve_kaitai_expression_as_integer(locals,
+ kswitch->target,
+ strlen(kswitch->target),
+ &value);
+ if (!result) goto exit_with_creator;
+
+ for (i = 0; i < kswitch->count; i++)
+ {
+ final_type = is_suitable_switch_case_for_integer(kswitch->cases[i], locals, &value);
+
+ if (final_type != NULL)
+ break;
+
+ }
+
+ }
+
+ else
+ printf("other type: %u\n", basic);
+
+ }
+
+ if (final_type == NULL && kswitch->defcase != NULL)
+ final_type = kswitch->defcase->type;
+
+ /* Mise en place d'un attribut et analyse */
+
+ if (final_type != NULL)
+ {
+ attrib = g_kaitai_attribute_dup_for_user_type(kswitch->generic, final_type);
+
+ result = g_kaitai_parser_parse_content(G_KAITAI_PARSER(attrib), locals, content, pos, record);
+
+ g_object_unref(G_OBJECT(attrib));
+
+ }
+
+ exit_with_creator:
+
+ g_object_unref(G_OBJECT(creator));
+
+ exit_with_ref:
+
+ g_object_unref(G_OBJECT(reference));
+
+ exit:
+
+ return result;
+
+}
diff --git a/plugins/kaitai/parsers/switch.h b/plugins/kaitai/parsers/switch.h
new file mode 100644
index 0000000..c45237a
--- /dev/null
+++ b/plugins/kaitai/parsers/switch.h
@@ -0,0 +1,61 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * switch.h - prototypes pour la 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/>.
+ */
+
+
+#ifndef _PLUGINS_KAITAI_PARSERS_SWITCH_H
+#define _PLUGINS_KAITAI_PARSERS_SWITCH_H
+
+
+#include <glib-object.h>
+
+
+#include <plugins/yaml/node.h>
+
+
+#include "attribute.h"
+
+
+
+#define G_TYPE_KAITAI_SWITCH g_kaitai_switch_get_type()
+#define G_KAITAI_SWITCH(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_KAITAI_SWITCH, GKaitaiSwitch))
+#define G_IS_KAITAI_SWITCH(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_KAITAI_SWITCH))
+#define G_KAITAI_SWITCH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_KAITAI_SWITCH, GKaitaiSwitchClass))
+#define G_IS_KAITAI_SWITCH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_KAITAI_SWITCH))
+#define G_KAITAI_SWITCH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_KAITAI_SWITCH, GKaitaiSwitchClass))
+
+
+/* Sélection d'un type selon un contexte (instance) */
+typedef struct _GKaitaiSwitch GKaitaiSwitch;
+
+/* Sélection d'un type selon un contexte (classe) */
+typedef struct _GKaitaiSwitchClass GKaitaiSwitchClass;
+
+
+/* Indique le type défini pour un choix dynamique de type Kaitai. */
+GType g_kaitai_switch_get_type(void);
+
+/* Construit une sélection dynamique de type Kaitai. */
+GKaitaiSwitch *g_kaitai_switch_new(GYamlNode *, GKaitaiAttribute *);
+
+
+
+#endif /* _PLUGINS_KAITAI_PARSERS_SWITCH_H */
diff --git a/plugins/kaitai/parsers/type-int.h b/plugins/kaitai/parsers/type-int.h
new file mode 100644
index 0000000..4a4d939
--- /dev/null
+++ b/plugins/kaitai/parsers/type-int.h
@@ -0,0 +1,55 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * type-int.h - prototypes internes pour la définition d'un type particulier pour Kaitai
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef PLUGINS_KAITAI_PARSERS_TYPE_INT_H
+#define PLUGINS_KAITAI_PARSERS_TYPE_INT_H
+
+
+#include "struct-int.h"
+#include "type.h"
+
+
+
+/* Définition d'un type particulier nouveau pour Kaitai (instance) */
+struct _GKaitaiType
+{
+ GKaitaiStruct parent; /* A laisser en premier */
+
+ char *name; /* Nom du type particulier */
+
+};
+
+/* Définition d'un type particulier nouveau pour Kaitai (classe) */
+struct _GKaitaiTypeClass
+{
+ GKaitaiStructClass parent; /* A laisser en premier */
+
+};
+
+
+/* Met en place un lecteur de type pour Kaitai. */
+bool g_kaitai_type_create(GKaitaiType *, GYamlNode *);
+
+
+
+#endif /* PLUGINS_KAITAI_PARSERS_TYPE_INT_H */
diff --git a/plugins/kaitai/parsers/type.c b/plugins/kaitai/parsers/type.c
new file mode 100644
index 0000000..30d0373
--- /dev/null
+++ b/plugins/kaitai/parsers/type.c
@@ -0,0 +1,236 @@
+
+/* 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 "type.h"
+
+
+#include <malloc.h>
+#include <string.h>
+#include <plugins/yaml/pair.h>
+
+
+#include "type-int.h"
+#include "../parser.h"
+
+
+
+/* Initialise la classe des types particuliers pour Kaitai. */
+static void g_kaitai_type_class_init(GKaitaiTypeClass *);
+
+/* Initialise un type particulier pour Kaitai. */
+static void g_kaitai_type_init(GKaitaiType *);
+
+/* Supprime toutes les références externes. */
+static void g_kaitai_type_dispose(GKaitaiType *);
+
+/* Procède à la libération totale de la mémoire. */
+static void g_kaitai_type_finalize(GKaitaiType *);
+
+
+
+/* Indique le type défini pour un type particulier pour Kaitai. */
+G_DEFINE_TYPE(GKaitaiType, g_kaitai_type, G_TYPE_KAITAI_STRUCT);
+
+
+/******************************************************************************
+* *
+* Paramètres : klass = classe à initialiser. *
+* *
+* Description : Initialise la classe des types particuliers pour Kaitai. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_kaitai_type_class_init(GKaitaiTypeClass *klass)
+{
+ GObjectClass *object; /* Autre version de la classe */
+
+ object = G_OBJECT_CLASS(klass);
+
+ object->dispose = (GObjectFinalizeFunc/* ! */)g_kaitai_type_dispose;
+ object->finalize = (GObjectFinalizeFunc)g_kaitai_type_finalize;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : kstruct = instance à initialiser. *
+* *
+* Description : Initialise un type particulier pour Kaitai. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_kaitai_type_init(GKaitaiType *type)
+{
+ type->name = NULL;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : type = instance d'objet GLib à traiter. *
+* *
+* Description : Supprime toutes les références externes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_kaitai_type_dispose(GKaitaiType *type)
+{
+ G_OBJECT_CLASS(g_kaitai_type_parent_class)->dispose(G_OBJECT(type));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : type = instance d'objet GLib à traiter. *
+* *
+* Description : Procède à la libération totale de la mémoire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_kaitai_type_finalize(GKaitaiType *type)
+{
+ if (type->name != NULL)
+ free(type->name);
+
+ G_OBJECT_CLASS(g_kaitai_type_parent_class)->finalize(G_OBJECT(type));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : parent = noeud Yaml contenant l'attribut à constituer. *
+* *
+* Description : Construit un lecteur de type pour Kaitai. *
+* *
+* Retour : Instance mise en place ou NULL en cas d'échec. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+GKaitaiType *g_kaitai_type_new(GYamlNode *parent)
+{
+ GKaitaiType *result; /* Structure à retourner */
+
+ result = g_object_new(G_TYPE_KAITAI_TYPE, NULL);
+
+ if (!g_kaitai_type_create(result, parent))
+ g_clear_object(&result);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : type = lecteur de type Kaitai à initialiser pleinement. *
+* parent = noeud Yaml contenant l'attribut à constituer. *
+* *
+* Description : Met en place un lecteur de type pour Kaitai. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool g_kaitai_type_create(GKaitaiType *type, GYamlNode *parent)
+{
+ bool result; /* Bilan à retourner */
+ const char *name; /* Désignation du type */
+ char *sub_path; /* Chemin d'accès suivant */
+ GYamlNode *sub; /* Contenu Yaml d'un type */
+
+ result = false;
+
+ /* Extraction du nom */
+
+ if (!G_IS_YAML_PAIR(parent))
+ goto exit;
+
+ name = g_yaml_pair_get_key(G_YAML_PAIR(parent));
+
+ type->name = strdup(name);
+
+ /* Extraction des bases du type */
+
+ asprintf(&sub_path, "/%s/", name);
+ sub = g_yaml_node_find_first_by_path(parent, sub_path);
+ free(sub_path);
+
+ if (sub == NULL)
+ goto exit;
+
+ result = g_kaitai_structure_create(G_KAITAI_STRUCT(type), sub);
+
+ g_object_unref(G_OBJECT(sub));
+
+ exit:
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : type = définition de type particulier à consulter. *
+* *
+* Description : Indique le nom de scène du type représenté. *
+* *
+* Retour : Désignation humaine. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+const char *g_kaitai_type_get_name(const GKaitaiType *type)
+{
+ const char *result; /* Nom à retourner */
+
+ result = type->name;
+
+ return result;
+
+}
diff --git a/plugins/kaitai/parsers/type.h b/plugins/kaitai/parsers/type.h
new file mode 100644
index 0000000..0656c64
--- /dev/null
+++ b/plugins/kaitai/parsers/type.h
@@ -0,0 +1,62 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * type.h - prototypes pour la définition d'un type particulier pour Kaitai
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _PLUGINS_KAITAI_PARSERS_TYPE_H
+#define _PLUGINS_KAITAI_PARSERS_TYPE_H
+
+
+#include <glib-object.h>
+#include <stdbool.h>
+
+
+#include <plugins/yaml/node.h>
+
+
+
+#define G_TYPE_KAITAI_TYPE g_kaitai_type_get_type()
+#define G_KAITAI_TYPE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_KAITAI_TYPE, GKaitaiType))
+#define G_IS_KAITAI_TYPE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_KAITAI_TYPE))
+#define G_KAITAI_TYPE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_KAITAI_TYPE, GKaitaiTypeClass))
+#define G_IS_KAITAI_TYPE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_KAITAI_TYPE))
+#define G_KAITAI_TYPE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_KAITAI_TYPE, GKaitaiTypeClass))
+
+
+/* Définition d'un type particulier nouveau pour Kaitai (instance) */
+typedef struct _GKaitaiType GKaitaiType;
+
+/* Définition d'un type particulier nouveau pour Kaitai (classe) */
+typedef struct _GKaitaiTypeClass GKaitaiTypeClass;
+
+
+/* Indique le type défini pour un type particulier pour Kaitai. */
+GType g_kaitai_type_get_type(void);
+
+/* Construit un lecteur de type pour Kaitai. */
+GKaitaiType *g_kaitai_type_new(GYamlNode *);
+
+/* Indique le nom de scène du type représenté. */
+const char *g_kaitai_type_get_name(const GKaitaiType *);
+
+
+
+#endif /* _PLUGINS_KAITAI_PARSERS_TYPE_H */
diff --git a/plugins/kaitai/python/Makefile.am b/plugins/kaitai/python/Makefile.am
new file mode 100644
index 0000000..ab40744
--- /dev/null
+++ b/plugins/kaitai/python/Makefile.am
@@ -0,0 +1,25 @@
+
+noinst_LTLIBRARIES = libkaitaipython.la
+
+libkaitaipython_la_SOURCES = \
+ array.h array.c \
+ module.h module.c \
+ parser.h parser.c \
+ record.h record.c \
+ scope.h scope.c \
+ stream.h stream.c
+
+libkaitaipython_la_LIBADD = \
+ parsers/libkaitaipythonparsers.la \
+ records/libkaitaipythonrecords.la
+
+libkaitaipython_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) $(LIBPYTHON_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \
+ -I$(top_srcdir)/src -DNO_IMPORT_PYGOBJECT
+
+
+devdir = $(includedir)/chrysalide-$(subdir)
+
+dev_HEADERS = $(libkaitaipython_la_SOURCES:%c=)
+
+
+SUBDIRS = parsers records
diff --git a/plugins/kaitai/python/array.c b/plugins/kaitai/python/array.c
new file mode 100644
index 0000000..4973c76
--- /dev/null
+++ b/plugins/kaitai/python/array.c
@@ -0,0 +1,265 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * array.h - équivalent Python du fichier "plugins/kaitai/array.h"
+ *
+ * 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "array.h"
+
+
+#include <pygobject.h>
+
+
+#include <i18n.h>
+#include <plugins/pychrysalide/access.h>
+#include <plugins/pychrysalide/helpers.h>
+
+
+#include "../array-int.h"
+
+
+
+CREATE_DYN_CONSTRUCTOR(kaitai_array, G_TYPE_KAITAI_ARRAY);
+
+/* Initialise une instance sur la base du dérivé de GObject. */
+static int py_kaitai_array_init(PyObject *, PyObject *, PyObject *);
+
+/* Convertit un tableau Kaitai en série d'octets si possible. */
+static PyObject *py_kaitai_array___bytes__(PyObject *, PyObject *);
+
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet à initialiser (théoriquement). *
+* args = arguments fournis à l'appel. *
+* kwds = arguments de type key=val fournis. *
+* *
+* Description : Initialise une instance sur la base du dérivé de GObject. *
+* *
+* Retour : 0. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static int py_kaitai_array_init(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ int ret; /* Bilan de lecture des args. */
+
+#define KAITAI_ARRAY_DOC \
+ "KaitaiArray defines an array for collecting various Kaitai items." \
+ "\n" \
+ "Instances can be created using following constructor:\n" \
+ "\n" \
+ " KaitaiArray()" \
+ "\n" \
+ "In this implementation, arrays do not have to carry items all" \
+ " belonging to the same type. Access and conversions to bytes are" \
+ " handled and checked at runtime."
+
+ /* Initialisation d'un objet GLib */
+
+ ret = forward_pygobjet_init(self);
+ if (ret == -1) return -1;
+
+ return 0;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = classe représentant un binaire. *
+* args = arguments fournis à l'appel. *
+* *
+* Description : Convertit un tableau Kaitai en série d'octets si possible. *
+* *
+* Retour : Série d'octets ou None. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_kaitai_array___bytes__(PyObject *self, PyObject *args)
+{
+ PyObject *result; /* Représentation à renvoyer */
+ GKaitaiArray *array; /* Tableau à manipuler */
+ sized_string_t bytes; /* Version en série d'octets */
+ bool status; /* Bilan de la conversion */
+
+#define KAITAI_ARRAY_AS_BYTES_METHOD PYTHON_METHOD_DEF \
+( \
+ __bytes__, "$self, /", \
+ METH_NOARGS, py_kaitai_array, \
+ "Provide a bytes representation of the array, when possible" \
+ " and without implementing the Python buffer protocol.\n" \
+ "\n" \
+ "THe result is bytes or a *TypeError* exception is raised if" \
+ " the array is not suitable for a conversion to bytes." \
+)
+
+ array = G_KAITAI_ARRAY(pygobject_get(self));
+
+ status = g_kaitai_array_convert_to_bytes(array, &bytes);
+
+ if (status)
+ {
+ result = PyBytes_FromStringAndSize(bytes.data, bytes.len);
+ exit_szstr(&bytes);
+ }
+ else
+ {
+ PyErr_SetString(PyExc_TypeError, "unable to convert the Kaitai array to bytes");
+ result = NULL;
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Fournit un accès à une définition de type à diffuser. *
+* *
+* Retour : Définition d'objet pour Python. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+PyTypeObject *get_python_kaitai_array_type(void)
+{
+ static PyMethodDef py_kaitai_array_methods[] = {
+ KAITAI_ARRAY_AS_BYTES_METHOD,
+ { NULL }
+ };
+
+ static PyGetSetDef py_kaitai_array_getseters[] = {
+ { NULL }
+ };
+
+ static PyTypeObject py_kaitai_array_type = {
+
+ PyVarObject_HEAD_INIT(NULL, 0)
+
+ .tp_name = "pychrysalide.plugins.kaitai.KaitaiArray",
+ .tp_basicsize = sizeof(PyGObject),
+
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+
+ .tp_doc = KAITAI_ARRAY_DOC,
+
+ .tp_methods = py_kaitai_array_methods,
+ .tp_getset = py_kaitai_array_getseters,
+
+ .tp_init = py_kaitai_array_init,
+ .tp_new = py_kaitai_array_new,
+
+ };
+
+ return &py_kaitai_array_type;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Prend en charge l'objet 'pychrysalide.plugins...KaitaiArray. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool ensure_python_kaitai_array_is_registered(void)
+{
+ PyTypeObject *type; /* Type Python 'KaitaiArray' */
+ PyObject *module; /* Module à recompléter */
+ PyObject *dict; /* Dictionnaire du module */
+
+ type = get_python_kaitai_array_type();
+
+ if (!PyType_HasFeature(type, Py_TPFLAGS_READY))
+ {
+ module = get_access_to_python_module("pychrysalide.plugins.kaitai");
+
+ dict = PyModule_GetDict(module);
+
+ if (!register_class_for_pygobject(dict, G_TYPE_KAITAI_ARRAY, type))
+ return false;
+
+ }
+
+ return true;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : arg = argument quelconque à tenter de convertir. *
+* dst = destination des valeurs récupérées en cas de succès. *
+* *
+* Description : Tente de convertir en tableau d'éléments Kaitai. *
+* *
+* Retour : Bilan de l'opération, voire indications supplémentaires. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+int convert_to_kaitai_array(PyObject *arg, void *dst)
+{
+ int result; /* Bilan à retourner */
+
+ result = PyObject_IsInstance(arg, (PyObject *)get_python_kaitai_array_type());
+
+ switch (result)
+ {
+ case -1:
+ /* L'exception est déjà fixée par Python */
+ result = 0;
+ break;
+
+ case 0:
+ PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to Kaitai array");
+ break;
+
+ case 1:
+ *((GKaitaiArray **)dst) = G_KAITAI_ARRAY(pygobject_get(arg));
+ break;
+
+ default:
+ assert(false);
+ break;
+
+ }
+
+ return result;
+
+}
diff --git a/plugins/kaitai/python/array.h b/plugins/kaitai/python/array.h
new file mode 100644
index 0000000..aeba541
--- /dev/null
+++ b/plugins/kaitai/python/array.h
@@ -0,0 +1,45 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * array.h - prototypes pour l'équivalent Python du fichier "plugins/kaitai/array.h"
+ *
+ * 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef _PLUGINS_KAITAI_PYTHON_ARRAY_H
+#define _PLUGINS_KAITAI_PYTHON_ARRAY_H
+
+
+#include <Python.h>
+#include <stdbool.h>
+
+
+
+/* Fournit un accès à une définition de type à diffuser. */
+PyTypeObject *get_python_kaitai_array_type(void);
+
+/* Prend en charge l'objet 'pychrysalide.plugins.kaitai.KaitaiArray'. */
+bool ensure_python_kaitai_array_is_registered(void);
+
+/* Tente de convertir en tableau d'éléments Kaitai. */
+int convert_to_kaitai_array(PyObject *, void *);
+
+
+
+#endif /* _PLUGINS_KAITAI_PYTHON_ARRAY_H */
diff --git a/plugins/kaitai/python/module.c b/plugins/kaitai/python/module.c
new file mode 100644
index 0000000..fa1f9c2
--- /dev/null
+++ b/plugins/kaitai/python/module.c
@@ -0,0 +1,132 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * module.c - intégration du répertoire kaitai en tant que module
+ *
+ * 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "module.h"
+
+
+#include <Python.h>
+
+
+#include <plugins/pychrysalide/access.h>
+#include <plugins/pychrysalide/helpers.h>
+
+
+#include "array.h"
+#include "parser.h"
+#include "record.h"
+#include "scope.h"
+#include "stream.h"
+#include "parsers/module.h"
+#include "records/module.h"
+
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Ajoute le module 'plugins.kaitai' au module Python. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool add_kaitai_module_to_python_module(void)
+{
+ bool result; /* Bilan à retourner */
+ PyObject *super; /* Module à compléter */
+ PyObject *module; /* Sous-module mis en place */
+
+#define PYCHRYSALIDE_PLUGINS_KAITAI_DOC \
+ "kaitai is a module trying to reverse some of the effects produced by ProGuard.\n" \
+ "\n" \
+ "Its action is focused on reverting name obfuscation by running binary diffing against" \
+ " OpenSource packages from the AOSP."
+
+ static PyModuleDef py_chrysalide_kaitai_module = {
+
+ .m_base = PyModuleDef_HEAD_INIT,
+
+ .m_name = "pychrysalide.plugins.kaitai",
+ .m_doc = PYCHRYSALIDE_PLUGINS_KAITAI_DOC,
+
+ .m_size = -1,
+
+ };
+
+ result = false;
+
+ super = get_access_to_python_module("pychrysalide.plugins");
+
+ module = build_python_module(super, &py_chrysalide_kaitai_module);
+
+ result = (module != NULL);
+
+ assert(result);
+
+ if (result) result = add_kaitai_parsers_module();
+ if (result) result = add_kaitai_records_module();
+
+ if (!result)
+ Py_XDECREF(module);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Intègre les objets du module 'plugins.kaitai'. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool populate_kaitai_module(void)
+{
+ bool result; /* Bilan à retourner */
+
+ result = true;
+
+ if (result) result = ensure_python_kaitai_array_is_registered();
+ if (result) result = ensure_python_kaitai_parser_is_registered();
+ if (result) result = ensure_python_match_record_is_registered();
+ if (result) result = ensure_python_kaitai_scope_is_registered();
+ if (result) result = ensure_python_kaitai_stream_is_registered();
+
+ if (result) result = populate_kaitai_parsers_module();
+ if (result) result = populate_kaitai_records_module();
+
+ assert(result);
+
+ return result;
+
+}
diff --git a/plugins/kaitai/python/module.h b/plugins/kaitai/python/module.h
new file mode 100644
index 0000000..939de07
--- /dev/null
+++ b/plugins/kaitai/python/module.h
@@ -0,0 +1,41 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * module.h - prototypes pour l'intégration du répertoire kaitai en tant que module
+ *
+ * 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef _PLUGINS_KAITAI_PYTHON_MODULE_H
+#define _PLUGINS_KAITAI_PYTHON_MODULE_H
+
+
+#include <stdbool.h>
+
+
+
+/* Ajoute le module 'plugins.kaitai' au module Python. */
+bool add_kaitai_module_to_python_module(void);
+
+/* Intègre les objets du module 'plugins.kaitai'. */
+bool populate_kaitai_module(void);
+
+
+
+#endif /* _PLUGINS_KAITAI_PYTHON_MODULE_H */
diff --git a/plugins/kaitai/python/parser.c b/plugins/kaitai/python/parser.c
new file mode 100644
index 0000000..067d3b0
--- /dev/null
+++ b/plugins/kaitai/python/parser.c
@@ -0,0 +1,205 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * parser.h - équivalent Python du fichier "plugins/kaitai/parser.h"
+ *
+ * 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "parser.h"
+
+
+#include <pygobject.h>
+
+
+#include <plugins/pychrysalide/access.h>
+#include <plugins/pychrysalide/helpers.h>
+
+
+#include "../parser.h"
+
+
+
+CREATE_DYN_ABSTRACT_CONSTRUCTOR(kaitai_parser, G_TYPE_KAITAI_PARSER, NULL);
+
+/* Initialise une instance sur la base du dérivé de GObject. */
+static int py_kaitai_parser_init(PyObject *, PyObject *, PyObject *);
+
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet à initialiser (théoriquement). *
+* args = arguments fournis à l'appel. *
+* kwds = arguments de type key=val fournis. *
+* *
+* Description : Initialise une instance sur la base du dérivé de GObject. *
+* *
+* Retour : 0. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static int py_kaitai_parser_init(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ int ret; /* Bilan de lecture des args. */
+
+#define KAITAI_PARSER_DOC \
+ "KaitaiParser is the class providing support for parsing binary contents" \
+ " using a special declarative language." \
+ "\n" \
+ "It is the Python bindings for a C implementation of the specifications" \
+ " described at http://kaitai.io/."
+
+ /* Initialisation d'un objet GLib */
+
+ ret = forward_pygobjet_init(self);
+ if (ret == -1) return -1;
+
+ return 0;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Fournit un accès à une définition de type à diffuser. *
+* *
+* Retour : Définition d'objet pour Python. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+PyTypeObject *get_python_kaitai_parser_type(void)
+{
+ static PyMethodDef py_kaitai_parser_methods[] = {
+ { NULL }
+ };
+
+ static PyGetSetDef py_kaitai_parser_getseters[] = {
+ { NULL }
+ };
+
+ static PyTypeObject py_kaitai_parser_type = {
+
+ PyVarObject_HEAD_INIT(NULL, 0)
+
+ .tp_name = "pychrysalide.plugins.kaitai.KaitaiParser",
+ .tp_basicsize = sizeof(PyGObject),
+
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+
+ .tp_doc = KAITAI_PARSER_DOC,
+
+ .tp_methods = py_kaitai_parser_methods,
+ .tp_getset = py_kaitai_parser_getseters,
+
+ .tp_init = py_kaitai_parser_init,
+ .tp_new = py_kaitai_parser_new,
+
+ };
+
+ return &py_kaitai_parser_type;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Prend en charge l'objet 'pychrysalide.plugins...KaitaiParser.*
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool ensure_python_kaitai_parser_is_registered(void)
+{
+ PyTypeObject *type; /* Type Python 'KaitaiParser' */
+ PyObject *module; /* Module à recompléter */
+ PyObject *dict; /* Dictionnaire du module */
+
+ type = get_python_kaitai_parser_type();
+
+ if (!PyType_HasFeature(type, Py_TPFLAGS_READY))
+ {
+ module = get_access_to_python_module("pychrysalide.plugins.kaitai");
+
+ dict = PyModule_GetDict(module);
+
+ if (!register_class_for_pygobject(dict, G_TYPE_KAITAI_PARSER, type))
+ return false;
+
+ }
+
+ return true;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : arg = argument quelconque à tenter de convertir. *
+* dst = destination des valeurs récupérées en cas de succès. *
+* *
+* Description : Tente de convertir en lecteur de données Kaitai. *
+* *
+* Retour : Bilan de l'opération, voire indications supplémentaires. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+int convert_to_kaitai_parser(PyObject *arg, void *dst)
+{
+ int result; /* Bilan à retourner */
+
+ result = PyObject_IsInstance(arg, (PyObject *)get_python_kaitai_parser_type());
+
+ switch (result)
+ {
+ case -1:
+ /* L'exception est déjà fixée par Python */
+ result = 0;
+ break;
+
+ case 0:
+ PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to Kaitai parser");
+ break;
+
+ case 1:
+ *((GKaitaiParser **)dst) = G_KAITAI_PARSER(pygobject_get(arg));
+ break;
+
+ default:
+ assert(false);
+ break;
+
+ }
+
+ return result;
+
+}
diff --git a/plugins/kaitai/python/parser.h b/plugins/kaitai/python/parser.h
new file mode 100644
index 0000000..f4b6c96
--- /dev/null
+++ b/plugins/kaitai/python/parser.h
@@ -0,0 +1,45 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * parser.h - prototypes pour l'équivalent Python du fichier "plugins/kaitai/parser.h"
+ *
+ * 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef _PLUGINS_KAITAI_PYTHON_PARSER_H
+#define _PLUGINS_KAITAI_PYTHON_PARSER_H
+
+
+#include <Python.h>
+#include <stdbool.h>
+
+
+
+/* Fournit un accès à une définition de type à diffuser. */
+PyTypeObject *get_python_kaitai_parser_type(void);
+
+/* Prend en charge l'objet 'pychrysalide.plugins.kaitai.KaitaiParser'. */
+bool ensure_python_kaitai_parser_is_registered(void);
+
+/* Tente de convertir en lecteur de données Kaitai. */
+int convert_to_kaitai_parser(PyObject *, void *);
+
+
+
+#endif /* _PLUGINS_KAITAI_PYTHON_PARSER_H */
diff --git a/plugins/kaitai/python/parsers/Makefile.am b/plugins/kaitai/python/parsers/Makefile.am
new file mode 100644
index 0000000..4c418af
--- /dev/null
+++ b/plugins/kaitai/python/parsers/Makefile.am
@@ -0,0 +1,19 @@
+
+noinst_LTLIBRARIES = libkaitaipythonparsers.la
+
+libkaitaipythonparsers_la_SOURCES = \
+ attribute.h attribute.c \
+ enum.h enum.c \
+ instance.h instance.c \
+ meta.h meta.c \
+ module.h module.c \
+ struct.h struct.c \
+ type.h type.c
+
+libkaitaipythonparsers_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) $(LIBPYTHON_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \
+ -I$(top_srcdir)/src -DNO_IMPORT_PYGOBJECT
+
+
+devdir = $(includedir)/chrysalide-$(subdir)
+
+dev_HEADERS = $(libkaitaipythonparsers_la_SOURCES:%c=)
diff --git a/plugins/kaitai/python/parsers/attribute.c b/plugins/kaitai/python/parsers/attribute.c
new file mode 100644
index 0000000..c8ea314
--- /dev/null
+++ b/plugins/kaitai/python/parsers/attribute.c
@@ -0,0 +1,420 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * attribute.h - équivalent Python du fichier "plugins/kaitai/parsers/attribute.h"
+ *
+ * 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "attribute.h"
+
+
+#include <assert.h>
+#include <pygobject.h>
+
+
+#include <i18n.h>
+#include <plugins/pychrysalide/access.h>
+#include <plugins/pychrysalide/helpers.h>
+#include <plugins/yaml/python/node.h>
+
+
+#include "../parser.h"
+#include "../../parsers/attribute-int.h"
+
+
+
+CREATE_DYN_CONSTRUCTOR(kaitai_attribute, G_TYPE_KAITAI_ATTRIBUTE);
+
+/* Initialise une instance sur la base du dérivé de GObject. */
+static int py_kaitai_attribute_init(PyObject *, PyObject *, PyObject *);
+
+/* Indique la désignation brute d'un identifiant Kaitai. */
+static PyObject *py_kaitai_attribute_get_raw_id(PyObject *, void *);
+
+/* Indique la désignation originelle d'un identifiant Kaitai. */
+static PyObject *py_kaitai_attribute_get_original_id(PyObject *, void *);
+
+/* Fournit une éventuelle documentation concernant l'attribut. */
+static PyObject *py_kaitai_attribute_get_doc(PyObject *, void *);
+
+/* Détermine si l'attribue porte une valeur entière signée. */
+static PyObject *py_kaitai_attribute_get_handle_signed_integer(PyObject *, void *);
+
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet à initialiser (théoriquement). *
+* args = arguments fournis à l'appel. *
+* kwds = arguments de type key=val fournis. *
+* *
+* Description : Initialise une instance sur la base du dérivé de GObject. *
+* *
+* Retour : 0. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static int py_kaitai_attribute_init(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ GYamlNode *parent; /* Noeud Yaml de l'attribut */
+ int ret; /* Bilan de lecture des args. */
+ GKaitaiAttribute *attrib; /* Création GLib à transmettre */
+
+#define KAITAI_ATTRIBUTE_DOC \
+ "KaitaiAttribute is the class providing support for parsing binary" \
+ " contents using a special declarative language." \
+ "\n" \
+ "Instances can be created using the following constructor:\n" \
+ "\n" \
+ " KaitaiAttribute(parent)" \
+ "\n" \
+ "Where *parent* is a pychrysalide.plugins.yaml.YamlNode instance pointing" \
+ " to Yaml data to load.\n" \
+ "\n" \
+ "The class is the Python bindings for a C implementation of the Attribute" \
+ " structure described at https://doc.kaitai.io/ksy_diagram.html."
+
+ /* Récupération des paramètres */
+
+ ret = PyArg_ParseTuple(args, "O&", convert_to_yaml_node, &parent);
+ if (!ret) return -1;
+
+ /* Initialisation d'un objet GLib */
+
+ ret = forward_pygobjet_init(self);
+ if (ret == -1) return -1;
+
+ /* Eléments de base */
+
+ attrib = G_KAITAI_ATTRIBUTE(pygobject_get(self));
+
+ if (!g_kaitai_attribute_create(attrib, parent, true))
+ {
+ PyErr_SetString(PyExc_ValueError, _("Unable to create Kaitai attribute."));
+ return -1;
+ }
+
+ return 0;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet Python concerné par l'appel. *
+* closure = non utilisé ici. *
+* *
+* Description : Indique la désignation brute d'un identifiant Kaitai. *
+* *
+* Retour : Valeur brute de l'identifiant. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_kaitai_attribute_get_raw_id(PyObject *self, void *closure)
+{
+ PyObject *result; /* Valeur à retourner */
+ GKaitaiAttribute *attrib; /* Version native de l'attribut*/
+ const char *value; /* Valeur à transmettre */
+
+#define KAITAI_ATTRIBUTE_RAW_ID_ATTRIB PYTHON_GET_DEF_FULL \
+( \
+ raw_id, py_kaitai_attribute, \
+ "Raw value used by Kaitai to identify one attribute" \
+ " among others.\n" \
+ "\n" \
+ "The returned indentifier is a string value." \
+)
+
+ attrib = G_KAITAI_ATTRIBUTE(pygobject_get(self));
+
+ value = g_kaitai_attribute_get_raw_id(attrib);
+ assert(value != NULL);
+
+ result = PyUnicode_FromString(value);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet Python concerné par l'appel. *
+* closure = non utilisé ici. *
+* *
+* Description : Indique la désignation originelle d'un identifiant Kaitai. *
+* *
+* Retour : Valeur originelle de l'identifiant. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_kaitai_attribute_get_original_id(PyObject *self, void *closure)
+{
+ PyObject *result; /* Valeur à retourner */
+ GKaitaiAttribute *attrib; /* Version native de l'attribut*/
+ const char *value; /* Valeur à transmettre */
+
+#define KAITAI_ATTRIBUTE_ORIGINAL_ID_ATTRIB PYTHON_GET_DEF_FULL \
+( \
+ original_id, py_kaitai_attribute, \
+ "Optional alternative identifier for the attribute, as seen in" \
+ " the original specifications.\n" \
+ "\n" \
+ "The returned value is a string or *None*." \
+)
+
+ attrib = G_KAITAI_ATTRIBUTE(pygobject_get(self));
+
+ value = g_kaitai_attribute_get_original_id(attrib);
+
+ if (value == NULL)
+ {
+ result = Py_None;
+ Py_INCREF(result);
+ }
+ else
+ result = PyUnicode_FromString(value);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet Python concerné par l'appel. *
+* closure = non utilisé ici. *
+* *
+* Description : Fournit une éventuelle documentation concernant l'attribut. *
+* *
+* Retour : Description enregistrée ou None si absente. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_kaitai_attribute_get_doc(PyObject *self, void *closure)
+{
+ PyObject *result; /* Valeur à retourner */
+ GKaitaiAttribute *attrib; /* Version native de l'attribut*/
+ const char *doc; /* Documentation à transmettre */
+
+#define KAITAI_ATTRIBUTE_DOC_ATTRIB PYTHON_GET_DEF_FULL \
+( \
+ doc, py_kaitai_attribute, \
+ "Optional documentation for the attribute.\n" \
+ "\n" \
+ "The returned value is a string or *None*." \
+)
+
+ attrib = G_KAITAI_ATTRIBUTE(pygobject_get(self));
+
+ doc = g_kaitai_attribute_get_doc(attrib);
+
+ if (doc == NULL)
+ {
+ result = Py_None;
+ Py_INCREF(result);
+ }
+ else
+ result = PyUnicode_FromString(doc);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet Python concerné par l'appel. *
+* closure = non utilisé ici. *
+* *
+* Description : Détermine si l'attribue porte une valeur entière signée. *
+* *
+* Retour : Bilan de la consultation : True si un entier signé est visé. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_kaitai_attribute_get_handle_signed_integer(PyObject *self, void *closure)
+{
+ PyObject *result; /* Valeur à retourner */
+ GKaitaiAttribute *attrib; /* Version native de l'attribut*/
+ bool status; /* Bilan d'une consultation */
+
+#define KAITAI_ATTRIBUTE_HANDLE_SIGNED_INTEGER_ATTRIB PYTHON_GET_DEF_FULL \
+( \
+ handle_signed_integer, py_kaitai_attribute, \
+ "Sign of the carried integer value, if any: positive or negative?\n" \
+ "\n" \
+ "This status is provided as a boolean value." \
+)
+
+ attrib = G_KAITAI_ATTRIBUTE(pygobject_get(self));
+
+ status = g_kaitai_attribute_handle_signed_integer(attrib);
+
+ result = status ? Py_True : Py_False;
+ Py_INCREF(result);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Fournit un accès à une définition de type à diffuser. *
+* *
+* Retour : Définition d'objet pour Python. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+PyTypeObject *get_python_kaitai_attribute_type(void)
+{
+ static PyMethodDef py_kaitai_attribute_methods[] = {
+ { NULL }
+ };
+
+ static PyGetSetDef py_kaitai_attribute_getseters[] = {
+ KAITAI_ATTRIBUTE_RAW_ID_ATTRIB,
+ KAITAI_ATTRIBUTE_ORIGINAL_ID_ATTRIB,
+ KAITAI_ATTRIBUTE_DOC_ATTRIB,
+ KAITAI_ATTRIBUTE_HANDLE_SIGNED_INTEGER_ATTRIB,
+ { NULL }
+ };
+
+ static PyTypeObject py_kaitai_attribute_type = {
+
+ PyVarObject_HEAD_INIT(NULL, 0)
+
+ .tp_name = "pychrysalide.plugins.kaitai.parsers.KaitaiAttribute",
+ .tp_basicsize = sizeof(PyGObject),
+
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+
+ .tp_doc = KAITAI_ATTRIBUTE_DOC,
+
+ .tp_methods = py_kaitai_attribute_methods,
+ .tp_getset = py_kaitai_attribute_getseters,
+
+ .tp_init = py_kaitai_attribute_init,
+ .tp_new = py_kaitai_attribute_new,
+
+ };
+
+ return &py_kaitai_attribute_type;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Prend en charge l'objet 'pychrysalide....KaitaiAttribute. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool ensure_python_kaitai_attribute_is_registered(void)
+{
+ PyTypeObject *type; /* Type 'KaitaiAttribute' */
+ PyObject *module; /* Module à recompléter */
+ PyObject *dict; /* Dictionnaire du module */
+
+ type = get_python_kaitai_attribute_type();
+
+ if (!PyType_HasFeature(type, Py_TPFLAGS_READY))
+ {
+ module = get_access_to_python_module("pychrysalide.plugins.kaitai.parsers");
+
+ dict = PyModule_GetDict(module);
+
+ if (!ensure_python_kaitai_parser_is_registered())
+ return false;
+
+ if (!register_class_for_pygobject(dict, G_TYPE_KAITAI_ATTRIBUTE, type))
+ return false;
+
+ }
+
+ return true;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : arg = argument quelconque à tenter de convertir. *
+* dst = destination des valeurs récupérées en cas de succès. *
+* *
+* Description : Tente de convertir en attribut de données Kaitai. *
+* *
+* Retour : Bilan de l'opération, voire indications supplémentaires. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+int convert_to_kaitai_attribute(PyObject *arg, void *dst)
+{
+ int result; /* Bilan à retourner */
+
+ result = PyObject_IsInstance(arg, (PyObject *)get_python_kaitai_attribute_type());
+
+ switch (result)
+ {
+ case -1:
+ /* L'exception est déjà fixée par Python */
+ result = 0;
+ break;
+
+ case 0:
+ PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to Kaitai attribute");
+ break;
+
+ case 1:
+ *((GKaitaiAttribute **)dst) = G_KAITAI_ATTRIBUTE(pygobject_get(arg));
+ break;
+
+ default:
+ assert(false);
+ break;
+
+ }
+
+ return result;
+
+}
diff --git a/plugins/kaitai/python/parsers/attribute.h b/plugins/kaitai/python/parsers/attribute.h
new file mode 100644
index 0000000..931e769
--- /dev/null
+++ b/plugins/kaitai/python/parsers/attribute.h
@@ -0,0 +1,45 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * attribute.h - prototypes pour l'équivalent Python du fichier "plugins/kaitai/parsers/attribute.h"
+ *
+ * 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef _PLUGINS_KAITAI_PYTHON_PARSERS_ATTRIBUTE_H
+#define _PLUGINS_KAITAI_PYTHON_PARSERS_ATTRIBUTE_H
+
+
+#include <Python.h>
+#include <stdbool.h>
+
+
+
+/* Fournit un accès à une définition de type à diffuser. */
+PyTypeObject *get_python_kaitai_attribute_type(void);
+
+/* Prend en charge l'objet 'pychrysalide.plugins.kaitai.parsers.KaitaiAttribute'. */
+bool ensure_python_kaitai_attribute_is_registered(void);
+
+/* Tente de convertir en attribut de données Kaitai. */
+int convert_to_kaitai_attribute(PyObject *, void *);
+
+
+
+#endif /* _PLUGINS_KAITAI_PYTHON_PARSERS_ATTRIBUTE_H */
diff --git a/plugins/kaitai/python/parsers/enum.c b/plugins/kaitai/python/parsers/enum.c
new file mode 100644
index 0000000..9200c6f
--- /dev/null
+++ b/plugins/kaitai/python/parsers/enum.c
@@ -0,0 +1,468 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * enum.h - équivalent Python du fichier "plugins/kaitai/parsers/enum.h"
+ *
+ * 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "enum.h"
+
+
+#include <assert.h>
+#include <pygobject.h>
+
+
+#include <i18n.h>
+#include <plugins/pychrysalide/access.h>
+#include <plugins/pychrysalide/helpers.h>
+#include <plugins/yaml/python/node.h>
+
+
+#include "../../parsers/enum-int.h"
+
+
+
+CREATE_DYN_CONSTRUCTOR(kaitai_enum, G_TYPE_KAITAI_ENUM);
+
+/* Initialise une instance sur la base du dérivé de GObject. */
+static int py_kaitai_enum_init(PyObject *, PyObject *, PyObject *);
+
+/* Traduit une étiquette brute en constante d'énumération. */
+static PyObject *py_kaitai_enum_find_value(PyObject *, PyObject *);
+
+/* Traduit une constante d'énumération en étiquette brute. */
+static PyObject *py_kaitai_enum_find_label(PyObject *, PyObject *);
+
+/* Traduit une constante d'énumération en documentation. */
+static PyObject *py_kaitai_enum_find_documentation(PyObject *, PyObject *);
+
+/* Fournit le nom principal d'une énumération. */
+static PyObject *py_kaitai_enum_get_name(PyObject *, void *);
+
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet à initialiser (théoriquement). *
+* args = arguments fournis à l'appel. *
+* kwds = arguments de type key=val fournis. *
+* *
+* Description : Initialise une instance sur la base du dérivé de GObject. *
+* *
+* Retour : 0. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static int py_kaitai_enum_init(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ GYamlNode *parent; /* Noeud Yaml de l'attribut */
+ int ret; /* Bilan de lecture des args. */
+ GKaitaiEnum *kenum; /* Création GLib à transmettre */
+
+#define KAITAI_ENUM_DOC \
+ "The KaitaiEnum class maps integer constants to symbolic names using" \
+ " Kaitai definitions.\n" \
+ "\n" \
+ "Instances can be created using the following constructor:\n" \
+ "\n" \
+ " KaitaiEnum(parent)" \
+ "\n" \
+ "Where *parent* is a pychrysalide.plugins.yaml.YamlNode instance pointing" \
+ " to Yaml data to load.\n" \
+ "\n" \
+ "The class is the Python bindings for a C implementation of the EnumSpec" \
+ " structure described at https://doc.kaitai.io/ksy_diagram.html."
+
+ /* Récupération des paramètres */
+
+ ret = PyArg_ParseTuple(args, "O&", convert_to_yaml_node, &parent);
+ if (!ret) return -1;
+
+ /* Initialisation d'un objet GLib */
+
+ ret = forward_pygobjet_init(self);
+ if (ret == -1) return -1;
+
+ /* Eléments de base */
+
+ kenum = G_KAITAI_ENUM(pygobject_get(self));
+
+ if (!g_kaitai_enum_create(kenum, parent))
+ {
+ PyErr_SetString(PyExc_ValueError, _("Unable to create Kaitai enumeration."));
+ return -1;
+ }
+
+ return 0;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = instance de l'énumération Kaitai à manipuler. *
+* args = arguments fournis à l'appel. *
+* *
+* Description : Traduit une étiquette brute en constante d'énumération. *
+* *
+* Retour : Valeur retrouvée ou None en cas d'échec. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_kaitai_enum_find_value(PyObject *self, PyObject *args)
+{
+ PyObject *result; /* Instance à retourner */
+ const char *label; /* Etiquette à rechercher */
+ int ret; /* Bilan de lecture des args. */
+ GKaitaiEnum *kenum; /* Enumération Kaitai courante */
+ sized_string_t cstr; /* CHaîne avec sa longueur */
+ bool status; /* Bilan de la conversion */
+ resolved_value_t value; /* valeur à transformer */
+
+#define KAITAI_ENUM_FIND_VALUE_METHOD PYTHON_METHOD_DEF \
+( \
+ find_value, "$self, label", \
+ METH_VARARGS, py_kaitai_enum, \
+ "Translate a given enumeration label into its relative value.\n" \
+ "\n" \
+ "The *label* argument is expected to be a string.\n" \
+ "\n" \
+ "The result is an integer or *None* in case of resolution failure." \
+)
+
+ ret = PyArg_ParseTuple(args, "s", &label);
+ if (!ret) return NULL;
+
+ kenum = G_KAITAI_ENUM(pygobject_get(self));
+
+ cstr.data = (char *)label;
+ cstr.len = strlen(label);
+
+ status = g_kaitai_enum_find_value(kenum, &cstr, &value);
+
+ if (status)
+ {
+ if (value.type == GVT_UNSIGNED_INTEGER)
+ result = PyLong_FromUnsignedLongLong(value.unsigned_integer);
+ else
+ {
+ assert(value.type == GVT_SIGNED_INTEGER);
+ result = PyLong_FromLongLong(value.signed_integer);
+ }
+ }
+ else
+ {
+ result = Py_None;
+ Py_INCREF(result);
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = instance de l'énumération Kaitai à manipuler. *
+* args = arguments fournis à l'appel. *
+* *
+* Description : Traduit une constante d'énumération en étiquette brute. *
+* *
+* Retour : Désignation ou None en cas d'échec. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_kaitai_enum_find_label(PyObject *self, PyObject *args)
+{
+ PyObject *result; /* Instance à retourner */
+ int prefix; /* Préfixe attendu ? */
+ resolved_value_t value; /* valeur à transformer */
+ int ret; /* Bilan de lecture des args. */
+ GKaitaiEnum *kenum; /* Enumération Kaitai courante */
+ char *label; /* Etiquette reconstruite */
+
+#define KAITAI_ENUM_FIND_LABEL_METHOD PYTHON_METHOD_DEF \
+( \
+ find_label, "$self, value, / , prefix=False", \
+ METH_VARARGS, py_kaitai_enum, \
+ "Provide the label linked to a constant value within the current" \
+ " enumeration.\n" \
+ "\n" \
+ "The *value* is a simple integer, and *prefix* is a boolean indicating" \
+ " if the result has to integrate the enumeration name as a prefix.\n" \
+ "\n" \
+ "The result is a string or *None* in case of resolution failure." \
+)
+
+ prefix = 0;
+
+ ret = PyArg_ParseTuple(args, "K|p", &value.unsigned_integer, prefix);
+ if (!ret) return NULL;
+
+ kenum = G_KAITAI_ENUM(pygobject_get(self));
+
+ value.type = GVT_UNSIGNED_INTEGER;
+ label = g_kaitai_enum_find_label(kenum, &value, prefix);
+
+ if (label != NULL)
+ {
+ result = PyUnicode_FromString(label);
+ free(label);
+ }
+ else
+ {
+ result = Py_None;
+ Py_INCREF(result);
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = instance de l'énumération Kaitai à manipuler. *
+* args = arguments fournis à l'appel. *
+* *
+* Description : Traduit une constante d'énumération en documentation. *
+* *
+* Retour : Documentation associée à la valeur indiquée ou None. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_kaitai_enum_find_documentation(PyObject *self, PyObject *args)
+{
+ PyObject *result; /* Instance à retourner */
+ resolved_value_t value; /* valeur à transformer */
+ int ret; /* Bilan de lecture des args. */
+ GKaitaiEnum *kenum; /* Enumération Kaitai courante */
+ char *doc; /* Documentation obtenue */
+
+#define KAITAI_ENUM_FIND_DOCUMENTATION_METHOD PYTHON_METHOD_DEF \
+( \
+ find_documentation, "$self, value", \
+ METH_VARARGS, py_kaitai_enum, \
+ "Provide the optional documentation linked to a constant value within" \
+ " the current enumeration.\n" \
+ "\n" \
+ "The *value* is a simple integer.\n" \
+ "\n" \
+ "The result is a string or *None* if no documentation is registered" \
+ " for the provided value." \
+)
+
+ ret = PyArg_ParseTuple(args, "K", &value.unsigned_integer);
+ if (!ret) return NULL;
+
+ kenum = G_KAITAI_ENUM(pygobject_get(self));
+
+ value.type = GVT_UNSIGNED_INTEGER;
+ doc = g_kaitai_enum_find_documentation(kenum, &value);
+
+ if (doc != NULL)
+ {
+ result = PyUnicode_FromString(doc);
+ free(doc);
+ }
+ else
+ {
+ result = Py_None;
+ Py_INCREF(result);
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet Python concerné par l'appel. *
+* closure = non utilisé ici. *
+* *
+* Description : Fournit le nom principal d'une énumération. *
+* *
+* Retour : Désignation de l'énumération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_kaitai_enum_get_name(PyObject *self, void *closure)
+{
+ PyObject *result; /* Valeur à retourner */
+ GKaitaiEnum *kenum; /* Version native de l'objet */
+ const char *name; /* Valeur à transmettre */
+
+#define KAITAI_ENUM_NAME_ATTRIB PYTHON_GET_DEF_FULL \
+( \
+ name, py_kaitai_enum, \
+ "Name of the enumeration group, as a string value." \
+)
+
+ kenum = G_KAITAI_ENUM(pygobject_get(self));
+
+ name = g_kaitai_enum_get_name(kenum);
+
+ result = PyUnicode_FromString(name);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Fournit un accès à une définition de type à diffuser. *
+* *
+* Retour : Définition d'objet pour Python. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+PyTypeObject *get_python_kaitai_enum_type(void)
+{
+ static PyMethodDef py_kaitai_enum_methods[] = {
+ KAITAI_ENUM_FIND_VALUE_METHOD,
+ KAITAI_ENUM_FIND_LABEL_METHOD,
+ KAITAI_ENUM_FIND_DOCUMENTATION_METHOD,
+ { NULL }
+ };
+
+ static PyGetSetDef py_kaitai_enum_getseters[] = {
+ KAITAI_ENUM_NAME_ATTRIB,
+ { NULL }
+ };
+
+ static PyTypeObject py_kaitai_enum_type = {
+
+ PyVarObject_HEAD_INIT(NULL, 0)
+
+ .tp_name = "pychrysalide.plugins.kaitai.parsers.KaitaiEnum",
+ .tp_basicsize = sizeof(PyGObject),
+
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+
+ .tp_doc = KAITAI_ENUM_DOC,
+
+ .tp_methods = py_kaitai_enum_methods,
+ .tp_getset = py_kaitai_enum_getseters,
+
+ .tp_init = py_kaitai_enum_init,
+ .tp_new = py_kaitai_enum_new
+
+ };
+
+ return &py_kaitai_enum_type;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Prend en charge l'objet 'pychrysalide.plugins...KaitaiEnum. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool ensure_python_kaitai_enum_is_registered(void)
+{
+ PyTypeObject *type; /* Type Python 'KaitaiEnum' */
+ PyObject *module; /* Module à recompléter */
+ PyObject *dict; /* Dictionnaire du module */
+
+ type = get_python_kaitai_enum_type();
+
+ if (!PyType_HasFeature(type, Py_TPFLAGS_READY))
+ {
+ module = get_access_to_python_module("pychrysalide.plugins.kaitai.parsers");
+
+ dict = PyModule_GetDict(module);
+
+ if (!register_class_for_pygobject(dict, G_TYPE_KAITAI_ENUM, type))
+ return false;
+
+ }
+
+ return true;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : arg = argument quelconque à tenter de convertir. *
+* dst = destination des valeurs récupérées en cas de succès. *
+* *
+* Description : Tente de convertir en ensemble d'énumérations Kaitai. *
+* *
+* Retour : Bilan de l'opération, voire indications supplémentaires. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+int convert_to_kaitai_enum(PyObject *arg, void *dst)
+{
+ int result; /* Bilan à retourner */
+
+ result = PyObject_IsInstance(arg, (PyObject *)get_python_kaitai_enum_type());
+
+ switch (result)
+ {
+ case -1:
+ /* L'exception est déjà fixée par Python */
+ result = 0;
+ break;
+
+ case 0:
+ PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to Kaitai enumeration");
+ break;
+
+ case 1:
+ *((GKaitaiEnum **)dst) = G_KAITAI_ENUM(pygobject_get(arg));
+ break;
+
+ default:
+ assert(false);
+ break;
+
+ }
+
+ return result;
+
+}
diff --git a/plugins/kaitai/python/parsers/enum.h b/plugins/kaitai/python/parsers/enum.h
new file mode 100644
index 0000000..7172e69
--- /dev/null
+++ b/plugins/kaitai/python/parsers/enum.h
@@ -0,0 +1,45 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * enum.h - prototypes pour l'équivalent Python du fichier "plugins/kaitai/parsers/enum.h"
+ *
+ * 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef _PLUGINS_KAITAI_PYTHON_PARSERS_ENUM_H
+#define _PLUGINS_KAITAI_PYTHON_PARSERS_ENUM_H
+
+
+#include <Python.h>
+#include <stdbool.h>
+
+
+
+/* Fournit un accès à une définition de type à diffuser. */
+PyTypeObject *get_python_kaitai_enum_type(void);
+
+/* Prend en charge l'objet 'pychrysalide.plugins.kaitai.parsers.KaitaiEnum'. */
+bool ensure_python_kaitai_enum_is_registered(void);
+
+/* Tente de convertir en ensemble d'énumérations Kaitai. */
+int convert_to_kaitai_enum(PyObject *, void *);
+
+
+
+#endif /* _PLUGINS_KAITAI_PYTHON_PARSERS_ENUM_H */
diff --git a/plugins/kaitai/python/parsers/instance.c b/plugins/kaitai/python/parsers/instance.c
new file mode 100644
index 0000000..d55b58c
--- /dev/null
+++ b/plugins/kaitai/python/parsers/instance.c
@@ -0,0 +1,280 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * instance.h - équivalent Python du fichier "plugins/kaitai/parsers/instance.h"
+ *
+ * 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "instance.h"
+
+
+#include <assert.h>
+#include <pygobject.h>
+
+
+#include <i18n.h>
+#include <plugins/pychrysalide/access.h>
+#include <plugins/pychrysalide/helpers.h>
+#include <plugins/yaml/python/node.h>
+
+
+#include "attribute.h"
+#include "../../parsers/instance-int.h"
+
+
+
+CREATE_DYN_CONSTRUCTOR(kaitai_instance, G_TYPE_KAITAI_INSTANCE);
+
+/* Initialise une instance sur la base du dérivé de GObject. */
+static int py_kaitai_instance_init(PyObject *, PyObject *, PyObject *);
+
+/* Indique le nom attribué à une instance Kaitai. */
+static PyObject *py_kaitai_instance_get_name(PyObject *, void *);
+
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet à initialiser (théoriquement). *
+* args = arguments fournis à l'appel. *
+* kwds = arguments de type key=val fournis. *
+* *
+* Description : Initialise une instance sur la base du dérivé de GObject. *
+* *
+* Retour : 0. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static int py_kaitai_instance_init(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ GYamlNode *parent; /* Noeud Yaml de l'attribut */
+ int ret; /* Bilan de lecture des args. */
+ GKaitaiInstance *attrib; /* Création GLib à transmettre */
+
+#define KAITAI_INSTANCE_DOC \
+ "KaitaiInstance is the class providing support for Kaitai computed" \
+ " values.\n" \
+ "\n" \
+ "Instances can be created using the following constructor:\n" \
+ "\n" \
+ " KaitaiInstance(parent)" \
+ "\n" \
+ "Where *parent* is a pychrysalide.plugins.yaml.YamlNode instance pointing" \
+ " to Yaml data to load.\n" \
+ "\n" \
+ "The class is the Python bindings for a C implementation of the Instance" \
+ " structure described at https://doc.kaitai.io/ksy_diagram.html."
+
+ /* Récupération des paramètres */
+
+ ret = PyArg_ParseTuple(args, "O&", convert_to_yaml_node, &parent);
+ if (!ret) return -1;
+
+ /* Initialisation d'un objet GLib */
+
+ ret = forward_pygobjet_init(self);
+ if (ret == -1) return -1;
+
+ /* Eléments de base */
+
+ attrib = G_KAITAI_INSTANCE(pygobject_get(self));
+
+ if (!g_kaitai_instance_create(attrib, parent))
+ {
+ PyErr_SetString(PyExc_ValueError, _("Unable to create Kaitai instance."));
+ return -1;
+ }
+
+ return 0;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet Python concerné par l'appel. *
+* closure = non utilisé ici. *
+* *
+* Description : Indique le nom attribué à une instance Kaitai. *
+* *
+* Retour : Désignation pointant l'instance. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_kaitai_instance_get_name(PyObject *self, void *closure)
+{
+ PyObject *result; /* Valeur à retourner */
+ GKaitaiInstance *inst; /* Version native de l'instance*/
+ const char *name; /* Désignation à transmettre */
+
+#define KAITAI_INSTANCE_NAME_ATTRIB PYTHON_GET_DEF_FULL \
+( \
+ name, py_kaitai_instance, \
+ "Name used by Kaitai to identify the instance" \
+ " among others.\n" \
+ "\n" \
+ "The returned indentifier is a string value." \
+)
+
+ inst = G_KAITAI_INSTANCE(pygobject_get(self));
+
+ name = g_kaitai_instance_get_name(inst);
+ assert(name != NULL);
+
+ result = PyUnicode_FromString(name);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Fournit un accès à une définition de type à diffuser. *
+* *
+* Retour : Définition d'objet pour Python. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+PyTypeObject *get_python_kaitai_instance_type(void)
+{
+ static PyMethodDef py_kaitai_instance_methods[] = {
+ { NULL }
+ };
+
+ static PyGetSetDef py_kaitai_instance_getseters[] = {
+ KAITAI_INSTANCE_NAME_ATTRIB,
+ { NULL }
+ };
+
+ static PyTypeObject py_kaitai_instance_type = {
+
+ PyVarObject_HEAD_INIT(NULL, 0)
+
+ .tp_name = "pychrysalide.plugins.kaitai.parsers.KaitaiInstance",
+ .tp_basicsize = sizeof(PyGObject),
+
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+
+ .tp_doc = KAITAI_INSTANCE_DOC,
+
+ .tp_methods = py_kaitai_instance_methods,
+ .tp_getset = py_kaitai_instance_getseters,
+
+ .tp_init = py_kaitai_instance_init,
+ .tp_new = py_kaitai_instance_new,
+
+ };
+
+ return &py_kaitai_instance_type;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Prend en charge l'objet 'pychrysalide....KaitaiInstance. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool ensure_python_kaitai_instance_is_registered(void)
+{
+ PyTypeObject *type; /* Type 'KaitaiInstance' */
+ PyObject *module; /* Module à recompléter */
+ PyObject *dict; /* Dictionnaire du module */
+
+ type = get_python_kaitai_instance_type();
+
+ if (!PyType_HasFeature(type, Py_TPFLAGS_READY))
+ {
+ module = get_access_to_python_module("pychrysalide.plugins.kaitai.parsers");
+
+ dict = PyModule_GetDict(module);
+
+ if (!ensure_python_kaitai_attribute_is_registered())
+ return false;
+
+ if (!register_class_for_pygobject(dict, G_TYPE_KAITAI_INSTANCE, type))
+ return false;
+
+ }
+
+ return true;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : arg = argument quelconque à tenter de convertir. *
+* dst = destination des valeurs récupérées en cas de succès. *
+* *
+* Description : Tente de convertir en instance Kaitai. *
+* *
+* Retour : Bilan de l'opération, voire indications supplémentaires. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+int convert_to_kaitai_instance(PyObject *arg, void *dst)
+{
+ int result; /* Bilan à retourner */
+
+ result = PyObject_IsInstance(arg, (PyObject *)get_python_kaitai_instance_type());
+
+ switch (result)
+ {
+ case -1:
+ /* L'exception est déjà fixée par Python */
+ result = 0;
+ break;
+
+ case 0:
+ PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to Kaitai instance");
+ break;
+
+ case 1:
+ *((GKaitaiInstance **)dst) = G_KAITAI_INSTANCE(pygobject_get(arg));
+ break;
+
+ default:
+ assert(false);
+ break;
+
+ }
+
+ return result;
+
+}
diff --git a/plugins/kaitai/python/parsers/instance.h b/plugins/kaitai/python/parsers/instance.h
new file mode 100644
index 0000000..8a0a6cf
--- /dev/null
+++ b/plugins/kaitai/python/parsers/instance.h
@@ -0,0 +1,45 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * instance.h - prototypes pour l'équivalent Python du fichier "plugins/kaitai/parsers/instance.h"
+ *
+ * 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef _PLUGINS_KAITAI_PYTHON_PARSERS_INSTANCE_H
+#define _PLUGINS_KAITAI_PYTHON_PARSERS_INSTANCE_H
+
+
+#include <Python.h>
+#include <stdbool.h>
+
+
+
+/* Fournit un accès à une définition de type à diffuser. */
+PyTypeObject *get_python_kaitai_instance_type(void);
+
+/* Prend en charge l'objet 'pychrysalide.plugins.kaitai.parsers.KaitaiInstance'. */
+bool ensure_python_kaitai_instance_is_registered(void);
+
+/* Tente de convertir en instance Kaitai. */
+int convert_to_kaitai_instance(PyObject *, void *);
+
+
+
+#endif /* _PLUGINS_KAITAI_PYTHON_PARSERS_INSTANCE_H */
diff --git a/plugins/kaitai/python/parsers/meta.c b/plugins/kaitai/python/parsers/meta.c
new file mode 100644
index 0000000..3432640
--- /dev/null
+++ b/plugins/kaitai/python/parsers/meta.c
@@ -0,0 +1,366 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * meta.h - équivalent Python du fichier "plugins/kaitai/parsers/meta.h"
+ *
+ * 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "meta.h"
+
+
+#include <pygobject.h>
+
+
+#include <i18n.h>
+#include <plugins/pychrysalide/access.h>
+#include <plugins/pychrysalide/helpers.h>
+#include <plugins/pychrysalide/analysis/content.h>
+#include <plugins/yaml/python/node.h>
+
+
+#include "../../parsers/meta-int.h"
+
+
+
+CREATE_DYN_CONSTRUCTOR(kaitai_meta, G_TYPE_KAITAI_META);
+
+/* Initialise une instance sur la base du dérivé de GObject. */
+static int py_kaitai_meta_init(PyObject *, PyObject *, PyObject *);
+
+/* Fournit l'identifié associé à une définiton Kaitai. */
+static PyObject *py_kaitai_meta_get_id(PyObject *, void *);
+
+/* Fournit la désignation humaine d'une définiton Kaitai. */
+static PyObject *py_kaitai_meta_get_title(PyObject *, void *);
+
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet à initialiser (théoriquement). *
+* args = arguments fournis à l'appel. *
+* kwds = arguments de type key=val fournis. *
+* *
+* Description : Initialise une instance sur la base du dérivé de GObject. *
+* *
+* Retour : 0. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static int py_kaitai_meta_init(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ GYamlNode *parent; /* Noeud Yaml de l'attribut */
+ int ret; /* Bilan de lecture des args. */
+ GKaitaiMeta *kmeta; /* Création GLib à transmettre */
+
+#define KAITAI_META_DOC \
+ "The KaitaiMeta class stores general information about a Kaitai definition,"\
+ " such as required imports or the default endianness for reading values.\n" \
+ "\n" \
+ "Instances can be created using the following constructor:\n" \
+ "\n" \
+ " KaitaiMeta(parent)" \
+ "\n" \
+ "Where *parent* is a pychrysalide.plugins.yaml.YamlNode instance pointing" \
+ " to Yaml data to load.\n" \
+ "\n" \
+ "The class is the Python bindings for a C implementation of the MetaSpec" \
+ " structure described at https://doc.kaitai.io/ksy_diagram.html."
+
+ /* Récupération des paramètres */
+
+ ret = PyArg_ParseTuple(args, "O&", convert_to_yaml_node, &parent);
+ if (!ret) return -1;
+
+ /* Initialisation d'un objet GLib */
+
+ ret = forward_pygobjet_init(self);
+ if (ret == -1) return -1;
+
+ /* Eléments de base */
+
+ kmeta = G_KAITAI_META(pygobject_get(self));
+
+ if (!g_kaitai_meta_create(kmeta, parent))
+ {
+ PyErr_SetString(PyExc_ValueError, _("Unable to create Kaitai global description."));
+ return -1;
+ }
+
+ return 0;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet Python concerné par l'appel. *
+* closure = non utilisé ici. *
+* *
+* Description : Fournit l'identifié associé à une définiton Kaitai. *
+* *
+* Retour : Identifiant de définition complète ou None. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_kaitai_meta_get_id(PyObject *self, void *closure)
+{
+ PyObject *result; /* Valeur à retourner */
+ GKaitaiMeta *meta; /* Version native de l'objet */
+ const char *id; /* Valeur à transmettre */
+
+#define KAITAI_META_ID_ATTRIB PYTHON_GET_DEF_FULL \
+( \
+ id, py_kaitai_meta, \
+ "Identifier for the Kaitai definition, as a string" \
+ " value or *None* if any." \
+)
+
+ meta = G_KAITAI_META(pygobject_get(self));
+
+ id = g_kaitai_meta_get_id(meta);
+
+ if (id != NULL)
+ result = PyUnicode_FromString(id);
+
+ else
+ {
+ result = Py_None;
+ Py_INCREF(result);
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet Python concerné par l'appel. *
+* closure = non utilisé ici. *
+* *
+* Description : Fournit la désignation humaine d'une définiton Kaitai. *
+* *
+* Retour : Intitulé de définition OU None. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_kaitai_meta_get_title(PyObject *self, void *closure)
+{
+ PyObject *result; /* Valeur à retourner */
+ GKaitaiMeta *meta; /* Version native de l'objet */
+ const char *title; /* Valeur à transmettre */
+
+#define KAITAI_META_TITLE_ATTRIB PYTHON_GET_DEF_FULL \
+( \
+ title, py_kaitai_meta, \
+ "Humain description for the Kaitai definition, as a" \
+ " string value or *None* if any." \
+)
+
+ meta = G_KAITAI_META(pygobject_get(self));
+
+ title = g_kaitai_meta_get_title(meta);
+
+ if (title != NULL)
+ result = PyUnicode_FromString(title);
+
+ else
+ {
+ result = Py_None;
+ Py_INCREF(result);
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet Python concerné par l'appel. *
+* closure = non utilisé ici. *
+* *
+* Description : Fournit la désignation humaine d'une définiton Kaitai. *
+* *
+* Retour : Intitulé de définition OU None. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_kaitai_meta_get_endian(PyObject *self, void *closure)
+{
+ PyObject *result; /* Valeur à retourner */
+ GKaitaiMeta *meta; /* Version native de l'objet */
+ SourceEndian endian; /* Valeur à transmettre */
+
+#define KAITAI_META_ENDIAN_ATTRIB PYTHON_GET_DEF_FULL \
+( \
+ endian, py_kaitai_meta, \
+ "Default endianness for the Kaitai definition, as a" \
+ " pychrysalide.analysis.BinContent.SourceEndian value." \
+)
+
+ meta = G_KAITAI_META(pygobject_get(self));
+
+ endian = g_kaitai_meta_get_endian(meta);
+
+ result = cast_with_constants_group_from_type(get_python_binary_content_type(), "SourceEndian", endian);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Fournit un accès à une définition de type à diffuser. *
+* *
+* Retour : Définition d'objet pour Python. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+PyTypeObject *get_python_kaitai_meta_type(void)
+{
+ static PyMethodDef py_kaitai_meta_methods[] = {
+ { NULL }
+ };
+
+ static PyGetSetDef py_kaitai_meta_getseters[] = {
+ KAITAI_META_ID_ATTRIB,
+ KAITAI_META_TITLE_ATTRIB,
+ KAITAI_META_ENDIAN_ATTRIB,
+ { NULL }
+ };
+
+ static PyTypeObject py_kaitai_meta_type = {
+
+ PyVarObject_HEAD_INIT(NULL, 0)
+
+ .tp_name = "pychrysalide.plugins.kaitai.parsers.KaitaiMeta",
+ .tp_basicsize = sizeof(PyGObject),
+
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+
+ .tp_doc = KAITAI_META_DOC,
+
+ .tp_methods = py_kaitai_meta_methods,
+ .tp_getset = py_kaitai_meta_getseters,
+
+ .tp_init = py_kaitai_meta_init,
+ .tp_new = py_kaitai_meta_new
+
+ };
+
+ return &py_kaitai_meta_type;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Prend en charge l'objet 'pychrysalide.plugins...KaitaiMeta. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool ensure_python_kaitai_meta_is_registered(void)
+{
+ PyTypeObject *type; /* Type Python 'KaitaiMeta' */
+ PyObject *module; /* Module à recompléter */
+ PyObject *dict; /* Dictionnaire du module */
+
+ type = get_python_kaitai_meta_type();
+
+ if (!PyType_HasFeature(type, Py_TPFLAGS_READY))
+ {
+ module = get_access_to_python_module("pychrysalide.plugins.kaitai.parsers");
+
+ dict = PyModule_GetDict(module);
+
+ if (!register_class_for_pygobject(dict, G_TYPE_KAITAI_META, type))
+ return false;
+
+ }
+
+ return true;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : arg = argument quelconque à tenter de convertir. *
+* dst = destination des valeurs récupérées en cas de succès. *
+* *
+* Description : Tente de convertir en description globale Kaitai. *
+* *
+* Retour : Bilan de l'opération, voire indications supplémentaires. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+int convert_to_kaitai_meta(PyObject *arg, void *dst)
+{
+ int result; /* Bilan à retourner */
+
+ result = PyObject_IsInstance(arg, (PyObject *)get_python_kaitai_meta_type());
+
+ switch (result)
+ {
+ case -1:
+ /* L'exception est déjà fixée par Python */
+ result = 0;
+ break;
+
+ case 0:
+ PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to Kaitai global description");
+ break;
+
+ case 1:
+ *((GKaitaiMeta **)dst) = G_KAITAI_META(pygobject_get(arg));
+ break;
+
+ default:
+ assert(false);
+ break;
+
+ }
+
+ return result;
+
+}
diff --git a/plugins/kaitai/python/parsers/meta.h b/plugins/kaitai/python/parsers/meta.h
new file mode 100644
index 0000000..383cad9
--- /dev/null
+++ b/plugins/kaitai/python/parsers/meta.h
@@ -0,0 +1,45 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * meta.h - prototypes pour l'équivalent Python du fichier "plugins/kaitai/parsers/meta.h"
+ *
+ * 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef _PLUGINS_KAITAI_PYTHON_PARSERS_META_H
+#define _PLUGINS_KAITAI_PYTHON_PARSERS_META_H
+
+
+#include <Python.h>
+#include <stdbool.h>
+
+
+
+/* Fournit un accès à une définition de type à diffuser. */
+PyTypeObject *get_python_kaitai_meta_type(void);
+
+/* Prend en charge l'objet 'pychrysalide.plugins.kaitai.parsers.KaitaiMeta'. */
+bool ensure_python_kaitai_meta_is_registered(void);
+
+/* Tente de convertir en description globale Kaitai. */
+int convert_to_kaitai_meta(PyObject *, void *);
+
+
+
+#endif /* _PLUGINS_KAITAI_PYTHON_PARSERS_META_H */
diff --git a/plugins/kaitai/python/parsers/module.c b/plugins/kaitai/python/parsers/module.c
new file mode 100644
index 0000000..549f728
--- /dev/null
+++ b/plugins/kaitai/python/parsers/module.c
@@ -0,0 +1,124 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * module.c - intégration du répertoire parsers en tant que module
+ *
+ * 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "module.h"
+
+
+#include <Python.h>
+
+
+#include <plugins/pychrysalide/access.h>
+#include <plugins/pychrysalide/helpers.h>
+
+
+#include "attribute.h"
+#include "enum.h"
+#include "instance.h"
+#include "meta.h"
+#include "struct.h"
+#include "type.h"
+
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Ajoute le module 'plugins.kaitai.parsers' au module Python. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool add_kaitai_parsers_module(void)
+{
+ bool result; /* Bilan à retourner */
+ PyObject *super; /* Module à compléter */
+ PyObject *module; /* Sous-module mis en place */
+
+#define PYCHRYSALIDE_PLUGINS_KAITAI_PARSERS_DOC \
+ "This module provides implementation for several Kaitai" \
+ " definitions parsers."
+
+ static PyModuleDef py_chrysalide_kaitai_parsers_module = {
+
+ .m_base = PyModuleDef_HEAD_INIT,
+
+ .m_name = "pychrysalide.plugins.kaitai.parsers",
+ .m_doc = PYCHRYSALIDE_PLUGINS_KAITAI_PARSERS_DOC,
+
+ .m_size = -1,
+
+ };
+
+ result = false;
+
+ super = get_access_to_python_module("pychrysalide.plugins.kaitai");
+
+ module = build_python_module(super, &py_chrysalide_kaitai_parsers_module);
+
+ result = (module != NULL);
+
+ assert(result);
+
+ if (!result)
+ Py_XDECREF(module);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Intègre les objets du module 'plugins.kaitai.parsers'. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool populate_kaitai_parsers_module(void)
+{
+ bool result; /* Bilan à retourner */
+
+ result = true;
+
+ if (result) result = ensure_python_kaitai_attribute_is_registered();
+ if (result) result = ensure_python_kaitai_enum_is_registered();
+ if (result) result = ensure_python_kaitai_instance_is_registered();
+ if (result) result = ensure_python_kaitai_meta_is_registered();
+ if (result) result = ensure_python_kaitai_structure_is_registered();
+ if (result) result = ensure_python_kaitai_type_is_registered();
+
+ assert(result);
+
+ return result;
+
+}
diff --git a/plugins/kaitai/python/parsers/module.h b/plugins/kaitai/python/parsers/module.h
new file mode 100644
index 0000000..d0fdd66
--- /dev/null
+++ b/plugins/kaitai/python/parsers/module.h
@@ -0,0 +1,41 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * module.h - prototypes pour l'intégration du répertoire parsers en tant que module
+ *
+ * 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef _PLUGINS_KAITAI_PYTHON_PARSERS_MODULE_H
+#define _PLUGINS_KAITAI_PYTHON_PARSERS_MODULE_H
+
+
+#include <stdbool.h>
+
+
+
+/* Ajoute le module 'plugins.kaitai.parsers' au module Python. */
+bool add_kaitai_parsers_module(void);
+
+/* Intègre les objets du module 'plugins.kaitai.parsers'. */
+bool populate_kaitai_parsers_module(void);
+
+
+
+#endif /* _PLUGINS_KAITAI_PYTHON_PARSERS_MODULE_H */
diff --git a/plugins/kaitai/python/parsers/struct.c b/plugins/kaitai/python/parsers/struct.c
new file mode 100644
index 0000000..900cd1b
--- /dev/null
+++ b/plugins/kaitai/python/parsers/struct.c
@@ -0,0 +1,376 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * struct.h - équivalent Python du fichier "plugins/kaitai/struct.h"
+ *
+ * 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "struct.h"
+
+
+#include <pygobject.h>
+
+
+#include <i18n.h>
+#include <plugins/pychrysalide/access.h>
+#include <plugins/pychrysalide/helpers.h>
+#include <plugins/pychrysalide/analysis/content.h>
+
+
+#include "../parser.h"
+#include "../../parsers/struct-int.h"
+
+
+
+CREATE_DYN_CONSTRUCTOR(kaitai_structure, G_TYPE_KAITAI_STRUCT);
+
+/* Initialise une instance sur la base du dérivé de GObject. */
+static int py_kaitai_structure_init(PyObject *, PyObject *, PyObject *);
+
+/* Parcourt un contenu binaire selon une description Kaitai. */
+static PyObject *py_kaitai_structure_parse(PyObject *, PyObject *);
+
+/* Fournit la désignation humaine d'une définiton Kaitai. */
+static PyObject *py_kaitai_structure_get_meta(PyObject *, void *);
+
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet à initialiser (théoriquement). *
+* args = arguments fournis à l'appel. *
+* kwds = arguments de type key=val fournis. *
+* *
+* Description : Initialise une instance sur la base du dérivé de GObject. *
+* *
+* Retour : 0. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static int py_kaitai_structure_init(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ const char *text; /* Contenu de règles à traiter */
+ const char *filename; /* Fichier de définitions */
+ int ret; /* Bilan de lecture des args. */
+ GKaitaiStruct *kstruct; /* Création GLib à transmettre */
+
+ static char *kwlist[] = { "text", "filename", NULL };
+
+#define KAITAI_STRUCT_DOC \
+ "KaitaiStruct is the class providing support for parsing binary contents" \
+ " using a special declarative language." \
+ "\n" \
+ "Instances can be created using one of the following constructors:\n" \
+ "\n" \
+ " KaitaiStruct(text=str)" \
+ "\n" \
+ " KaitaiStruct(filename=str)" \
+ "\n" \
+ "Where *text* is a string containg a markup content to parse; the" \
+ " *filename* argument is an alternative string for a path pointing to the" \
+ " same kind of content. This path can be a real filename or a resource" \
+ " URI." \
+ "\n" \
+ "It is the Python bindings for a C implementation of the specifications" \
+ " described at http://kaitai.io/."
+
+ /* Récupération des paramètres */
+
+ text = NULL;
+ filename = NULL;
+
+ ret = PyArg_ParseTupleAndKeywords(args, kwds, "|ss", kwlist, &text, &filename);
+ if (!ret) return -1;
+
+ /* Initialisation d'un objet GLib */
+
+ ret = forward_pygobjet_init(self);
+ if (ret == -1) return -1;
+
+ /* Eléments de base */
+
+ kstruct = G_KAITAI_STRUCT(pygobject_get(self));
+
+ if (text != NULL)
+ {
+ if (!g_kaitai_structure_create_from_text(kstruct, text))
+ {
+ PyErr_SetString(PyExc_ValueError, _("Unable to create Kaitai structure."));
+ return -1;
+ }
+
+ }
+
+ else if (filename != NULL)
+ {
+ if (!g_kaitai_structure_create_from_file(kstruct, filename))
+ {
+ PyErr_SetString(PyExc_ValueError, _("Unable to create Kaitai structure."));
+ return -1;
+ }
+
+ }
+
+ else
+ {
+ PyErr_SetString(PyExc_ValueError, _("Unable to create empty Kaitai structure."));
+ return -1;
+ }
+
+ return 0;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = instance de l'interpréteur Kaitai à manipuler. *
+* args = arguments fournis à l'appel. *
+* *
+* Description : Parcourt un contenu binaire selon une description Kaitai. *
+* *
+* Retour : Arborescence d'éléments rencontrés selon les spécifications. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_kaitai_structure_parse(PyObject *self, PyObject *args)
+{
+ PyObject *result; /* Instance à retourner */
+ GBinContent *content; /* Contenu binaire à traiter */
+ int ret; /* Bilan de lecture des args. */
+ GKaitaiStruct *kstruct; /* Interpréteur Kaitai courant */
+ GMatchRecord *record; /* Ensemble de correspondances */
+
+#define KAITAI_STRUCTURE_PARSE_METHOD PYTHON_METHOD_DEF \
+( \
+ parse, "$self, content", \
+ METH_VARARGS, py_kaitai_structure, \
+ "Parse a binary content with the loaded specifications." \
+ "\n" \
+ "The content has to be a pychrysalide.analysis.BinContent instance.\n" \
+ "\n" \
+ "The result is *None* if the parsing failed, or a" \
+ " pychrysalide.plugins.kaitai.MatchRecord object for each attribute" \
+ " met." \
+)
+
+ ret = PyArg_ParseTuple(args, "O&", convert_to_binary_content, &content);
+ if (!ret) return NULL;
+
+ kstruct = G_KAITAI_STRUCT(pygobject_get(self));
+
+ record = g_kaitai_structure_parse(kstruct, content);
+
+ if (record != NULL)
+ {
+ result = pygobject_new(G_OBJECT(record));
+ g_object_unref(G_OBJECT(record));
+ }
+ else
+ {
+ result = Py_None;
+ Py_INCREF(result);
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet Python concerné par l'appel. *
+* closure = non utilisé ici. *
+* *
+* Description : Fournit la désignation humaine d'une définiton Kaitai. *
+* *
+* Retour : Intitulé de définition OU None. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_kaitai_structure_get_meta(PyObject *self, void *closure)
+{
+ PyObject *result; /* Valeur à retourner */
+ GKaitaiStruct *kstruct; /* Version native de l'objet */
+ GKaitaiMeta *meta; /* Informations à transmettre */
+
+#define KAITAI_STRUCTURE_META_ATTRIB PYTHON_GET_DEF_FULL \
+( \
+ meta, py_kaitai_structure, \
+ "Global description provided for the Kaitai definition, as a" \
+ " pychrysalide.plugins.kaitai.parsers.KaitaiMeta instance." \
+)
+
+ kstruct = G_KAITAI_STRUCT(pygobject_get(self));
+
+ meta = g_kaitai_structure_get_meta(kstruct);
+
+ if (meta != NULL)
+ {
+ result = pygobject_new(G_OBJECT(meta));
+ g_object_unref(G_OBJECT(meta));
+ }
+ else
+ {
+ result = Py_None;
+ Py_INCREF(result);
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Fournit un accès à une définition de type à diffuser. *
+* *
+* Retour : Définition d'objet pour Python. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+PyTypeObject *get_python_kaitai_structure_type(void)
+{
+ static PyMethodDef py_kaitai_structure_methods[] = {
+ KAITAI_STRUCTURE_PARSE_METHOD,
+ { NULL }
+ };
+
+ static PyGetSetDef py_kaitai_structure_getseters[] = {
+ KAITAI_STRUCTURE_META_ATTRIB,
+ { NULL }
+ };
+
+ static PyTypeObject py_kaitai_structure_type = {
+
+ PyVarObject_HEAD_INIT(NULL, 0)
+
+ .tp_name = "pychrysalide.plugins.kaitai.parsers.KaitaiStruct",
+ .tp_basicsize = sizeof(PyGObject),
+
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+
+ .tp_doc = KAITAI_STRUCT_DOC,
+
+ .tp_methods = py_kaitai_structure_methods,
+ .tp_getset = py_kaitai_structure_getseters,
+
+ .tp_init = py_kaitai_structure_init,
+ .tp_new = py_kaitai_structure_new
+
+ };
+
+ return &py_kaitai_structure_type;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Prend en charge l'objet 'pychrysalide.plugins...KaitaiStruct.*
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool ensure_python_kaitai_structure_is_registered(void)
+{
+ PyTypeObject *type; /* Type Python 'KaitaiStruct' */
+ PyObject *module; /* Module à recompléter */
+ PyObject *dict; /* Dictionnaire du module */
+
+ type = get_python_kaitai_structure_type();
+
+ if (!PyType_HasFeature(type, Py_TPFLAGS_READY))
+ {
+ module = get_access_to_python_module("pychrysalide.plugins.kaitai.parsers");
+
+ dict = PyModule_GetDict(module);
+
+ if (!ensure_python_kaitai_parser_is_registered())
+ return false;
+
+ if (!register_class_for_pygobject(dict, G_TYPE_KAITAI_STRUCT, type))
+ return false;
+
+ }
+
+ return true;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : arg = argument quelconque à tenter de convertir. *
+* dst = destination des valeurs récupérées en cas de succès. *
+* *
+* Description : Tente de convertir en structure de données Kaitai. *
+* *
+* Retour : Bilan de l'opération, voire indications supplémentaires. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+int convert_to_kaitai_structure(PyObject *arg, void *dst)
+{
+ int result; /* Bilan à retourner */
+
+ result = PyObject_IsInstance(arg, (PyObject *)get_python_kaitai_structure_type());
+
+ switch (result)
+ {
+ case -1:
+ /* L'exception est déjà fixée par Python */
+ result = 0;
+ break;
+
+ case 0:
+ PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to Kaitai structure");
+ break;
+
+ case 1:
+ *((GKaitaiStruct **)dst) = G_KAITAI_STRUCT(pygobject_get(arg));
+ break;
+
+ default:
+ assert(false);
+ break;
+
+ }
+
+ return result;
+
+}
diff --git a/plugins/kaitai/python/parsers/struct.h b/plugins/kaitai/python/parsers/struct.h
new file mode 100644
index 0000000..872f744
--- /dev/null
+++ b/plugins/kaitai/python/parsers/struct.h
@@ -0,0 +1,45 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * struct.h - prototypes pour l'équivalent Python du fichier "plugins/kaitai/struct.h"
+ *
+ * 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef _PLUGINS_KAITAI_PYTHON_PARSERS_STRUCT_H
+#define _PLUGINS_KAITAI_PYTHON_PARSERS_STRUCT_H
+
+
+#include <Python.h>
+#include <stdbool.h>
+
+
+
+/* Fournit un accès à une définition de type à diffuser. */
+PyTypeObject *get_python_kaitai_structure_type(void);
+
+/* Prend en charge l'objet 'pychrysalide.plugins.kaitai.parsers.KaitaiStruct'. */
+bool ensure_python_kaitai_structure_is_registered(void);
+
+/* Tente de convertir en structure de données Kaitai. */
+int convert_to_kaitai_structure(PyObject *, void *);
+
+
+
+#endif /* _PLUGINS_KAITAI_PYTHON_PARSERS_STRUCT_H */
diff --git a/plugins/kaitai/python/parsers/type.c b/plugins/kaitai/python/parsers/type.c
new file mode 100644
index 0000000..64a3419
--- /dev/null
+++ b/plugins/kaitai/python/parsers/type.c
@@ -0,0 +1,278 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * type.h - équivalent Python du fichier "plugins/kaitai/parsers/type.h"
+ *
+ * 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "type.h"
+
+
+#include <assert.h>
+#include <pygobject.h>
+
+
+#include <i18n.h>
+#include <plugins/pychrysalide/access.h>
+#include <plugins/pychrysalide/helpers.h>
+#include <plugins/yaml/python/node.h>
+
+
+#include "struct.h"
+#include "../../parsers/type-int.h"
+
+
+
+CREATE_DYN_CONSTRUCTOR(kaitai_type, G_TYPE_KAITAI_TYPE);
+
+/* Initialise une instance sur la base du dérivé de GObject. */
+static int py_kaitai_type_init(PyObject *, PyObject *, PyObject *);
+
+/* Indique le nom de scène du type représenté. */
+static PyObject *py_kaitai_type_get_name(PyObject *, void *);
+
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet à initialiser (théoriquement). *
+* args = arguments fournis à l'appel. *
+* kwds = arguments de type key=val fournis. *
+* *
+* Description : Initialise une instance sur la base du dérivé de GObject. *
+* *
+* Retour : 0. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static int py_kaitai_type_init(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ GYamlNode *parent; /* Noeud Yaml de l'attribut */
+ int ret; /* Bilan de lecture des args. */
+ GKaitaiType *attrib; /* Création GLib à transmettre */
+
+#define KAITAI_TYPE_DOC \
+ "The KaitaiType class provides support for user-defined type used in" \
+ " Kaitai definitions.\n" \
+ "\n" \
+ "Instances can be created using the following constructor:\n" \
+ "\n" \
+ " KaitaiType(parent)" \
+ "\n" \
+ "Where *parent* is a pychrysalide.plugins.yaml.YamlNode instance pointing" \
+ " to Yaml data to load.\n" \
+ "\n" \
+ "The class is the Python bindings for a C implementation of the TypesSpec" \
+ " structure described at https://doc.kaitai.io/ksy_diagram.html."
+
+ /* Récupération des paramètres */
+
+ ret = PyArg_ParseTuple(args, "O&", convert_to_yaml_node, &parent);
+ if (!ret) return -1;
+
+ /* Initialisation d'un objet GLib */
+
+ ret = forward_pygobjet_init(self);
+ if (ret == -1) return -1;
+
+ /* Eléments de base */
+
+ attrib = G_KAITAI_TYPE(pygobject_get(self));
+
+ if (!g_kaitai_type_create(attrib, parent))
+ {
+ PyErr_SetString(PyExc_ValueError, _("Unable to create Kaitai type."));
+ return -1;
+ }
+
+ return 0;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet Python concerné par l'appel. *
+* closure = non utilisé ici. *
+* *
+* Description : Indique le nom de scène du type représenté. *
+* *
+* Retour : Désignation humaine. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_kaitai_type_get_name(PyObject *self, void *closure)
+{
+ PyObject *result; /* Valeur à retourner */
+ GKaitaiType *type; /* Version native du type */
+ const char *name; /* Désignation à transmettre */
+
+#define KAITAI_TYPE_NAME_ATTRIB PYTHON_GET_DEF_FULL \
+( \
+ name, py_kaitai_type, \
+ "Name of the user-defined type, provided as a unique" \
+ " string value." \
+)
+
+ type = G_KAITAI_TYPE(pygobject_get(self));
+
+ name = g_kaitai_type_get_name(type);
+ assert(name != NULL);
+
+ result = PyUnicode_FromString(name);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Fournit un accès à une définition de type à diffuser. *
+* *
+* Retour : Définition d'objet pour Python. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+PyTypeObject *get_python_kaitai_type_type(void)
+{
+ static PyMethodDef py_kaitai_type_methods[] = {
+ { NULL }
+ };
+
+ static PyGetSetDef py_kaitai_type_getseters[] = {
+ KAITAI_TYPE_NAME_ATTRIB,
+ { NULL }
+ };
+
+ static PyTypeObject py_kaitai_type_type = {
+
+ PyVarObject_HEAD_INIT(NULL, 0)
+
+ .tp_name = "pychrysalide.plugins.kaitai.parsers.KaitaiType",
+ .tp_basicsize = sizeof(PyGObject),
+
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+
+ .tp_doc = KAITAI_TYPE_DOC,
+
+ .tp_methods = py_kaitai_type_methods,
+ .tp_getset = py_kaitai_type_getseters,
+
+ .tp_init = py_kaitai_type_init,
+ .tp_new = py_kaitai_type_new,
+
+ };
+
+ return &py_kaitai_type_type;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Prend en charge l'objet 'pychrysalide....parsers.KaitaiType. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool ensure_python_kaitai_type_is_registered(void)
+{
+ PyTypeObject *type; /* Type 'KaitaiType' */
+ PyObject *module; /* Module à recompléter */
+ PyObject *dict; /* Dictionnaire du module */
+
+ type = get_python_kaitai_type_type();
+
+ if (!PyType_HasFeature(type, Py_TPFLAGS_READY))
+ {
+ module = get_access_to_python_module("pychrysalide.plugins.kaitai.parsers");
+
+ dict = PyModule_GetDict(module);
+
+ if (!ensure_python_kaitai_structure_is_registered())
+ return false;
+
+ if (!register_class_for_pygobject(dict, G_TYPE_KAITAI_TYPE, type))
+ return false;
+
+ }
+
+ return true;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : arg = argument quelconque à tenter de convertir. *
+* dst = destination des valeurs récupérées en cas de succès. *
+* *
+* Description : Tente de convertir en type particulier pour Kaitai. *
+* *
+* Retour : Bilan de l'opération, voire indications supplémentaires. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+int convert_to_kaitai_type(PyObject *arg, void *dst)
+{
+ int result; /* Bilan à retourner */
+
+ result = PyObject_IsInstance(arg, (PyObject *)get_python_kaitai_type_type());
+
+ switch (result)
+ {
+ case -1:
+ /* L'exception est déjà fixée par Python */
+ result = 0;
+ break;
+
+ case 0:
+ PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to Kaitai type");
+ break;
+
+ case 1:
+ *((GKaitaiType **)dst) = G_KAITAI_TYPE(pygobject_get(arg));
+ break;
+
+ default:
+ assert(false);
+ break;
+
+ }
+
+ return result;
+
+}
diff --git a/plugins/kaitai/python/parsers/type.h b/plugins/kaitai/python/parsers/type.h
new file mode 100644
index 0000000..320bc71
--- /dev/null
+++ b/plugins/kaitai/python/parsers/type.h
@@ -0,0 +1,45 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * type.h - prototypes pour l'équivalent Python du fichier "plugins/kaitai/parsers/type.h"
+ *
+ * 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef _PLUGINS_KAITAI_PYTHON_PARSERS_TYPE_H
+#define _PLUGINS_KAITAI_PYTHON_PARSERS_TYPE_H
+
+
+#include <Python.h>
+#include <stdbool.h>
+
+
+
+/* Fournit un accès à une définition de type à diffuser. */
+PyTypeObject *get_python_kaitai_type_type(void);
+
+/* Prend en charge l'objet 'pychrysalide.plugins.kaitai.parsers.KaitaiType'. */
+bool ensure_python_kaitai_type_is_registered(void);
+
+/* Tente de convertir en type particulier pour Kaitai. */
+int convert_to_kaitai_type(PyObject *, void *);
+
+
+
+#endif /* _PLUGINS_KAITAI_PYTHON_PARSERS_TYPE_H */
diff --git a/plugins/kaitai/python/record.c b/plugins/kaitai/python/record.c
new file mode 100644
index 0000000..4194a9a
--- /dev/null
+++ b/plugins/kaitai/python/record.c
@@ -0,0 +1,420 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * record.h - équivalent Python du fichier "plugins/kaitai/record.h"
+ *
+ * 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "record.h"
+
+
+#include <pygobject.h>
+
+
+#include <plugins/pychrysalide/access.h>
+#include <plugins/pychrysalide/helpers.h>
+#include <plugins/pychrysalide/arch/vmpa.h>
+
+
+#include "parser.h"
+#include "../record.h"
+
+
+
+CREATE_DYN_ABSTRACT_CONSTRUCTOR(match_record, G_TYPE_MATCH_RECORD, NULL);
+
+/* Initialise une instance sur la base du dérivé de GObject. */
+static int py_match_record_init(PyObject *, PyObject *, PyObject *);
+
+/* Modifie la référence au créateur de la correspondance. */
+static int py_match_record_set_creator(PyObject *, PyObject *, void *);
+
+/* Renvoie vers le lecteur à l'origine de la correspondance. */
+static PyObject *py_match_record_get_creator(PyObject *, void *);
+
+/* Fournit le contenu lié à une correspondance établie. */
+static PyObject *py_match_record_get_content(PyObject *, void *);
+
+/* Calcule ou fournit la zone couverte par une correspondance. */
+static PyObject *py_match_record_get_range(PyObject *, void *);
+
+/* Lit les octets bruts couverts par une correspondance. */
+static PyObject *py_match_record_get_raw_bytes(PyObject *, void *);
+
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet à initialiser (théoriquement). *
+* args = arguments fournis à l'appel. *
+* kwds = arguments de type key=val fournis. *
+* *
+* Description : Initialise une instance sur la base du dérivé de GObject. *
+* *
+* Retour : 0. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static int py_match_record_init(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ int ret; /* Bilan de lecture des args. */
+
+#define MATCH_RECORD_DOC \
+ "MatchRecord is an abstract class providing mainly location and raw" \
+ " data of an area which has matched a part of a binary content."
+
+ /* Initialisation d'un objet GLib */
+
+ ret = forward_pygobjet_init(self);
+ if (ret == -1) return -1;
+
+ return 0;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = correspondance à manipuler. *
+* value = lecteur à l'origine de la correspondance. *
+* closure = adresse non utilisée ici. *
+* *
+* Description : Modifie la référence au créateur de la correspondance. *
+* *
+* Retour : Bilan de la définition. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static int py_match_record_set_creator(PyObject *self, PyObject *value, void *closure)
+{
+ int result; /* Bilan à renvoyer */
+ GMatchRecord *record; /* Version GLib de l'objet */
+ GKaitaiParser *parser; /* Lecteur à l'origine */
+
+ record = G_MATCH_RECORD(pygobject_get(self));
+
+ if (!convert_to_kaitai_parser(value, &parser))
+ result = -1;
+
+ else
+ {
+ g_match_record_fix_creator(record, parser);
+ result = 0;
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = correspondance à manipuler. *
+* closure = adresse non utilisée ici. *
+* *
+* Description : Renvoie vers le lecteur à l'origine de la correspondance. *
+* *
+* Retour : Lecteur à l'origine de la création. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_match_record_get_creator(PyObject *self, void *closure)
+{
+ PyObject *result; /* Instance à retourner */
+ GMatchRecord *record; /* Version GLib de l'objet */
+ GKaitaiParser *parser; /* Lecteur à l'origine */
+
+#define MATCH_RECORD_CREATOR_ATTRIB PYTHON_GETSET_DEF_FULL \
+( \
+ creator, py_match_record, \
+ "Provide or define the pychrysalide.plugins.kaitai.KaitaiParser instance" \
+ " which has created the record.\n" \
+ "\n" \
+ "This field should not be defined after the record creation in most cases." \
+)
+
+ record = G_MATCH_RECORD(pygobject_get(self));
+
+ parser = g_match_record_get_creator(record);
+
+ result = pygobject_new(G_OBJECT(parser));
+ g_object_unref(parser);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet Python concerné par l'appel. *
+* closure = non utilisé ici. *
+* *
+* Description : Fournit le contenu lié à une correspondance établie. *
+* *
+* Retour : Contenu binaire associé. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_match_record_get_content(PyObject *self, void *closure)
+{
+ PyObject *result; /* Valeur à retourner */
+ GMatchRecord *record; /* Conservation à consulter */
+ GBinContent *content; /* Contenu associé */
+
+#define MATCH_RECORD_CONTENT_ATTRIB PYTHON_GET_DEF_FULL \
+( \
+ content, py_match_record, \
+ "pychrysalide.analysis.BinContent instance linked to" \
+ " the match record." \
+)
+
+ record = G_MATCH_RECORD(pygobject_get(self));
+ content = g_match_record_get_content(record);
+
+ if (content != NULL)
+ {
+ result = pygobject_new(G_OBJECT(content));
+ g_object_unref(G_OBJECT(content));
+ }
+ else
+ {
+ result = Py_None;
+ Py_INCREF(result);
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet Python concerné par l'appel. *
+* closure = non utilisé ici. *
+* *
+* Description : Calcule ou fournit la zone couverte par une correspondance. *
+* *
+* Retour : Zone de couverture déterminée. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_match_record_get_range(PyObject *self, void *closure)
+{
+ PyObject *result; /* Valeur à retourner */
+ GMatchRecord *record; /* Conservation à consulter */
+ mrange_t range; /* Couverture courante */
+
+#define MATCH_RECORD_RANGE_ATTRIB PYTHON_GET_DEF_FULL \
+( \
+ range, py_match_record, \
+ "Area of the matched data for the parsed attribute" \
+ " against a given binary content.\n" \
+ "\n" \
+ "This property is a pychrysalide.arch.mrange instance." \
+)
+
+ record = G_MATCH_RECORD(pygobject_get(self));
+ g_match_record_get_range(record, &range);
+
+ result = build_from_internal_mrange(&range);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet Python concerné par l'appel. *
+* closure = non utilisé ici. *
+* *
+* Description : Lit les octets bruts couverts par une correspondance. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_match_record_get_raw_bytes(PyObject *self, void *closure)
+{
+ PyObject *result; /* Valeur à retourner */
+ GMatchRecord *record; /* Conservation à consulter */
+ bin_t *out; /* Données brutes à transmettre*/
+ size_t len; /* Quantité de ces données */
+
+#define MATCH_RECORD_RAW_BYTES_ATTRIB PYTHON_GET_DEF_FULL \
+( \
+ raw_bytes, py_match_record, \
+ "Raw bytes from the area covered by the record." \
+)
+
+ record = G_MATCH_RECORD(pygobject_get(self));
+
+ g_match_record_read_raw_bytes(record, &out, &len);
+
+ result = PyBytes_FromStringAndSize((char *)out, len);
+ free(out);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Fournit un accès à une définition de type à diffuser. *
+* *
+* Retour : Définition d'objet pour Python. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+PyTypeObject *get_python_match_record_type(void)
+{
+ static PyMethodDef py_match_record_methods[] = {
+ { NULL }
+ };
+
+ static PyGetSetDef py_match_record_getseters[] = {
+ MATCH_RECORD_CREATOR_ATTRIB,
+ MATCH_RECORD_CONTENT_ATTRIB,
+ MATCH_RECORD_RANGE_ATTRIB,
+ MATCH_RECORD_RAW_BYTES_ATTRIB,
+ { NULL }
+ };
+
+ static PyTypeObject py_match_record_type = {
+
+ PyVarObject_HEAD_INIT(NULL, 0)
+
+ .tp_name = "pychrysalide.plugins.kaitai.MatchRecord",
+ .tp_basicsize = sizeof(PyGObject),
+
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+
+ .tp_doc = MATCH_RECORD_DOC,
+
+ .tp_methods = py_match_record_methods,
+ .tp_getset = py_match_record_getseters,
+
+ .tp_init = py_match_record_init,
+ .tp_new = py_match_record_new,
+
+ };
+
+ return &py_match_record_type;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Prend en charge l'objet 'pychrysalide.plugins...MatchRecord. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool ensure_python_match_record_is_registered(void)
+{
+ PyTypeObject *type; /* Type Python 'MatchRecord' */
+ PyObject *module; /* Module à recompléter */
+ PyObject *dict; /* Dictionnaire du module */
+
+ type = get_python_match_record_type();
+
+ if (!PyType_HasFeature(type, Py_TPFLAGS_READY))
+ {
+ module = get_access_to_python_module("pychrysalide.plugins.kaitai");
+
+ dict = PyModule_GetDict(module);
+
+ if (!register_class_for_pygobject(dict, G_TYPE_MATCH_RECORD, type))
+ return false;
+
+ }
+
+ return true;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : arg = argument quelconque à tenter de convertir. *
+* dst = destination des valeurs récupérées en cas de succès. *
+* *
+* Description : Tente de convertir en conservation de correspondance. *
+* *
+* Retour : Bilan de l'opération, voire indications supplémentaires. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+int convert_to_match_record(PyObject *arg, void *dst)
+{
+ int result; /* Bilan à retourner */
+
+ result = PyObject_IsInstance(arg, (PyObject *)get_python_match_record_type());
+
+ switch (result)
+ {
+ case -1:
+ /* L'exception est déjà fixée par Python */
+ result = 0;
+ break;
+
+ case 0:
+ PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to match record");
+ break;
+
+ case 1:
+ *((GMatchRecord **)dst) = G_MATCH_RECORD(pygobject_get(arg));
+ break;
+
+ default:
+ assert(false);
+ break;
+
+ }
+
+ return result;
+
+}
diff --git a/plugins/kaitai/python/record.h b/plugins/kaitai/python/record.h
new file mode 100644
index 0000000..edf75fc
--- /dev/null
+++ b/plugins/kaitai/python/record.h
@@ -0,0 +1,45 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * record.h - prototypes pour l'équivalent Python du fichier "plugins/kaitai/record.h"
+ *
+ * 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef _PLUGINS_KAITAI_PYTHON_RECORD_H
+#define _PLUGINS_KAITAI_PYTHON_RECORD_H
+
+
+#include <Python.h>
+#include <stdbool.h>
+
+
+
+/* Fournit un accès à une définition de type à diffuser. */
+PyTypeObject *get_python_match_record_type(void);
+
+/* Prend en charge l'objet 'pychrysalide.plugins.kaitai.MatchRecord'. */
+bool ensure_python_match_record_is_registered(void);
+
+/* Tente de convertir en conservation de correspondance. */
+int convert_to_match_record(PyObject *, void *);
+
+
+
+#endif /* _PLUGINS_KAITAI_PYTHON_RECORD_H */
diff --git a/plugins/kaitai/python/records/Makefile.am b/plugins/kaitai/python/records/Makefile.am
new file mode 100644
index 0000000..1413228
--- /dev/null
+++ b/plugins/kaitai/python/records/Makefile.am
@@ -0,0 +1,18 @@
+
+noinst_LTLIBRARIES = libkaitaipythonrecords.la
+
+libkaitaipythonrecords_la_SOURCES = \
+ empty.h empty.c \
+ group.h group.c \
+ item.h item.c \
+ list.h list.c \
+ module.h module.c \
+ value.h value.c
+
+libkaitaipythonrecords_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) $(LIBPYTHON_CFLAGS) $(LIBPYGOBJECT_CFLAGS) \
+ -I$(top_srcdir)/src -DNO_IMPORT_PYGOBJECT
+
+
+devdir = $(includedir)/chrysalide-$(subdir)
+
+dev_HEADERS = $(libkaitaipythonrecords_la_SOURCES:%c=)
diff --git a/plugins/kaitai/python/records/empty.c b/plugins/kaitai/python/records/empty.c
new file mode 100644
index 0000000..9861a39
--- /dev/null
+++ b/plugins/kaitai/python/records/empty.c
@@ -0,0 +1,286 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * empty.c - équivalent Python du fichier "plugins/kaitai/parsers/empty.c"
+ *
+ * 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "empty.h"
+
+
+#include <assert.h>
+#include <pygobject.h>
+
+
+#include <i18n.h>
+#include <plugins/pychrysalide/access.h>
+#include <plugins/pychrysalide/helpers.h>
+#include <plugins/pychrysalide/analysis/content.h>
+#include <plugins/pychrysalide/arch/vmpa.h>
+
+
+#include "../parser.h"
+#include "../record.h"
+#include "../../records/empty-int.h"
+
+
+
+CREATE_DYN_CONSTRUCTOR(record_empty, G_TYPE_RECORD_EMPTY);
+
+/* Initialise une instance sur la base du dérivé de GObject. */
+static int py_record_empty_init(PyObject *, PyObject *, PyObject *);
+
+/* Produit une absence de valeur pour la correspondance. */
+static PyObject *py_record_empty_get_value(PyObject *, void *);
+
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet à initialiser (théoriquement). *
+* args = arguments fournis à l'appel. *
+* kwds = arguments de type key=val fournis. *
+* *
+* Description : Initialise une instance sur la base du dérivé de GObject. *
+* *
+* Retour : 0. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static int py_record_empty_init(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ GKaitaiParser *parser; /* Analyseur défini créateur */
+ GBinContent *content; /* Contenu binaire manipulé */
+ vmpa2t *pos; /* Tête de lecture courante */
+ int ret; /* Bilan de lecture des args. */
+ GRecordEmpty *empty; /* Création GLib à transmettre */
+
+#define RECORD_EMPTY_DOC \
+ "The RecordEmpty object reflects absolutely no match and should only get" \
+ " in some rare cases.\n" \
+ "\n" \
+ "Instances can be created using following constructor:\n" \
+ "\n" \
+ " RecordEmpty(parser, content, pos)" \
+ "\n" \
+ "Where *parser* is the creator of the record, as a" \
+ " pychrysalide.plugins.kaitai.KaitaiParser instance, *content* is a" \
+ " pychrysalide.analysis.BinContent instance providing the processed data" \
+ " and *pos* defines the current reading location, as a" \
+ " pychrysalide.arch.vmpa value."
+
+ /* Récupération des paramètres */
+
+ ret = PyArg_ParseTuple(args, "O&O&O&",
+ convert_to_kaitai_parser, &parser,
+ convert_to_binary_content, &content,
+ convert_any_to_vmpa, &pos);
+ if (!ret) return -1;
+
+ /* Initialisation d'un objet GLib */
+
+ ret = forward_pygobjet_init(self);
+ if (ret == -1) return -1;
+
+ /* Eléments de base */
+
+ empty = G_RECORD_EMPTY(pygobject_get(self));
+
+ if (!g_record_empty_create(empty, parser, content, pos))
+ {
+ clean_vmpa_arg(pos);
+
+ PyErr_SetString(PyExc_ValueError, _("Unable to create Kaitai stream."));
+ return -1;
+
+ }
+
+ clean_vmpa_arg(pos);
+
+ return 0;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet Python concerné par l'appel. *
+* closure = non utilisé ici. *
+* *
+* Description : Produit une absence de valeur pour la correspondance. *
+* *
+* Retour : None. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_record_empty_get_value(PyObject *self, void *closure)
+{
+ PyObject *result; /* Valeur à retourner */
+
+#define RECORD_EMPTY_VALUE_ATTRIB PYTHON_GET_DEF_FULL \
+( \
+ value, py_record_empty, \
+ "Always *None*.\n" \
+ "\n" \
+ "This attribute is only provided to mimic other" \
+ " record types." \
+)
+
+ result = Py_None;
+ Py_INCREF(result);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Fournit un accès à une définition de type à diffuser. *
+* *
+* Retour : Définition d'objet pour Python. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+PyTypeObject *get_python_record_empty_type(void)
+{
+ static PyMethodDef py_record_empty_methods[] = {
+ { NULL }
+ };
+
+ static PyGetSetDef py_record_empty_getseters[] = {
+ RECORD_EMPTY_VALUE_ATTRIB,
+ { NULL }
+ };
+
+ static PyTypeObject py_record_empty_type = {
+
+ PyVarObject_HEAD_INIT(NULL, 0)
+
+ .tp_name = "pychrysalide.plugins.kaitai.records.RecordEmpty",
+ .tp_basicsize = sizeof(PyGObject),
+
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+
+ .tp_doc = RECORD_EMPTY_DOC,
+
+ .tp_methods = py_record_empty_methods,
+ .tp_getset = py_record_empty_getseters,
+
+ .tp_init = py_record_empty_init,
+ .tp_new = py_record_empty_new,
+
+ };
+
+ return &py_record_empty_type;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Prend en charge l'objet 'pychrysalide...records.RecordEmpty. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool ensure_python_record_empty_is_registered(void)
+{
+ PyTypeObject *type; /* Type Python 'RecordEmpty' */
+ PyObject *module; /* Module à recompléter */
+ PyObject *dict; /* Dictionnaire du module */
+
+ type = get_python_record_empty_type();
+
+ if (!PyType_HasFeature(type, Py_TPFLAGS_READY))
+ {
+ module = get_access_to_python_module("pychrysalide.plugins.kaitai.records");
+
+ dict = PyModule_GetDict(module);
+
+ if (!ensure_python_match_record_is_registered())
+ return false;
+
+ if (!register_class_for_pygobject(dict, G_TYPE_RECORD_EMPTY, type))
+ return false;
+
+ }
+
+ return true;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : arg = argument quelconque à tenter de convertir. *
+* dst = destination des valeurs récupérées en cas de succès. *
+* *
+* Description : Tente de convertir en zone de correspondance vide. *
+* *
+* Retour : Bilan de l'opération, voire indications supplémentaires. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+int convert_to_record_empty(PyObject *arg, void *dst)
+{
+ int result; /* Bilan à retourner */
+
+ result = PyObject_IsInstance(arg, (PyObject *)get_python_record_empty_type());
+
+ switch (result)
+ {
+ case -1:
+ /* L'exception est déjà fixée par Python */
+ result = 0;
+ break;
+
+ case 0:
+ PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to record empty");
+ break;
+
+ case 1:
+ *((GRecordEmpty **)dst) = G_RECORD_EMPTY(pygobject_get(arg));
+ break;
+
+ default:
+ assert(false);
+ break;
+
+ }
+
+ return result;
+
+}
diff --git a/plugins/kaitai/python/records/empty.h b/plugins/kaitai/python/records/empty.h
new file mode 100644
index 0000000..ecd5fc9
--- /dev/null
+++ b/plugins/kaitai/python/records/empty.h
@@ -0,0 +1,45 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * empty.h - prototypes pour l'équivalent Python du fichier "plugins/kaitai/records/empty.h"
+ *
+ * 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef _PLUGINS_KAITAI_PYTHON_RECORDS_EMPTY_H
+#define _PLUGINS_KAITAI_PYTHON_RECORDS_EMPTY_H
+
+
+#include <Python.h>
+#include <stdbool.h>
+
+
+
+/* Fournit un accès à une définition de type à diffuser. */
+PyTypeObject *get_python_record_empty_type(void);
+
+/* Prend en charge l'objet 'pychrysalide.plugins.kaitai.records.RecordEmpty'. */
+bool ensure_python_record_empty_is_registered(void);
+
+/* Tente de convertir en zone de correspondance vide. */
+int convert_to_record_empty(PyObject *, void *);
+
+
+
+#endif /* _PLUGINS_KAITAI_PYTHON_RECORDS_EMPTY_H */
diff --git a/plugins/kaitai/python/records/group.c b/plugins/kaitai/python/records/group.c
new file mode 100644
index 0000000..a050043
--- /dev/null
+++ b/plugins/kaitai/python/records/group.c
@@ -0,0 +1,305 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * group.h - équivalent Python du fichier "plugins/kaitai/parsers/group.h"
+ *
+ * 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "group.h"
+
+
+#include <pygobject.h>
+#include <string.h>
+
+
+#include <i18n.h>
+#include <plugins/pychrysalide/access.h>
+#include <plugins/pychrysalide/helpers.h>
+#include <plugins/pychrysalide/analysis/content.h>
+#include <plugins/pychrysalide/arch/vmpa.h>
+#include <plugins/yaml/python/node.h>
+
+
+#include "../record.h"
+#include "../parsers/struct.h"
+#include "../../records/group-int.h"
+
+
+
+CREATE_DYN_CONSTRUCTOR(record_group, G_TYPE_RECORD_GROUP);
+
+/* Initialise une instance sur la base du dérivé de GObject. */
+static int py_record_group_init(PyObject *, PyObject *, PyObject *);
+
+/* Assure l'encadrement des accès aux champs d'une séquence. */
+static PyObject *py_record_group_getattr(PyObject *, char *);
+
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet à initialiser (théoriquement). *
+* args = arguments fournis à l'appel. *
+* kwds = arguments de type key=val fournis. *
+* *
+* Description : Initialise une instance sur la base du dérivé de GObject. *
+* *
+* Retour : 0. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static int py_record_group_init(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ GKaitaiStruct *kstruct; /* Séquence définie créatrice */
+ GBinContent *content; /* Contenu binaire analysé */
+ int ret; /* Bilan de lecture des args. */
+ GRecordGroup *group; /* Création GLib à transmettre */
+
+#define RECORD_GROUP_DOC \
+ "The RecordGroup class stores a map of parsed attributes with their" \
+ " relative values. Each of theses Kaitai attributes can be accessed as" \
+ " usual Python attribute.\n" \
+ "\n" \
+ "Instances can be created using the following constructor:\n" \
+ "\n" \
+ " RecordGroup(kstruct, content)" \
+ "\n" \
+ "Where the *kstruct* refers to a" \
+ " pychrysalide.plugins.kaitai.parsers.KaitaiStructure instance as the" \
+ " creator of the newly created object, and *content* points to a" \
+ " pychrysalide.analysis.BinContent instance."
+
+ /* Récupération des paramètres */
+
+ ret = PyArg_ParseTuple(args, "O&O&",
+ convert_to_kaitai_structure, &kstruct,
+ convert_to_binary_content, &content);
+ if (!ret) return -1;
+
+ /* Initialisation d'un objet GLib */
+
+ ret = forward_pygobjet_init(self);
+ if (ret == -1) return -1;
+
+ /* Eléments de base */
+
+ group = G_RECORD_GROUP(pygobject_get(self));
+
+ if (!g_record_group_create(group, kstruct, content))
+ {
+ PyErr_SetString(PyExc_ValueError, _("Unable to create record group."));
+ return -1;
+ }
+
+ return 0;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = structure C convertie en Python. *
+* name = nom du champ auquel un accès est demandé. *
+* *
+* Description : Assure l'encadrement des accès aux champs d'une séquence. *
+* *
+* Retour : Valeur du champ demandé. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_record_group_getattr(PyObject *self, char *name)
+{
+ PyObject *result; /* Elément à retourner */
+ GRecordGroup *group; /* Version native de l'objet */
+ GMatchRecord *found; /* Sous-élément identifié */
+ PyObject *w; /* Conversion du nom de champ */
+ PyTypeObject *tp; /* Type de l'objet manipulé */
+
+ group = G_RECORD_GROUP(pygobject_get(self));
+
+ found = g_match_record_find_by_name(G_MATCH_RECORD(group), name, strlen(name), DIRECT_SEARCH_DEEP_LEVEL);
+
+ if (found != NULL)
+ {
+ result = pygobject_new(G_OBJECT(found));
+ g_object_unref(G_OBJECT(found));
+ }
+
+ else
+ {
+ w = PyUnicode_InternFromString(name);
+ if (w == NULL) return NULL;
+
+ tp = Py_TYPE(self);
+
+ if (tp->tp_base->tp_getattro != NULL)
+ result = tp->tp_base->tp_getattro(self, w);
+
+ else
+ {
+ PyErr_Format(PyExc_AttributeError,
+ "type object '%.50s' has no attribute '%U'",
+ tp->tp_name, name);
+ result = NULL;
+ }
+
+ Py_DECREF(w);
+
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Fournit un accès à une définition de type à diffuser. *
+* *
+* Retour : Définition d'objet pour Python. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+PyTypeObject *get_python_record_group_type(void)
+{
+ static PyMethodDef py_record_group_methods[] = {
+ { NULL }
+ };
+
+ static PyGetSetDef py_record_group_getseters[] = {
+ { NULL }
+ };
+
+ static PyTypeObject py_record_group_type = {
+
+ PyVarObject_HEAD_INIT(NULL, 0)
+
+ .tp_name = "pychrysalide.plugins.kaitai.records.RecordGroup",
+ .tp_basicsize = sizeof(PyGObject),
+
+ .tp_getattr = py_record_group_getattr,
+
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+
+ .tp_doc = RECORD_GROUP_DOC,
+
+ .tp_methods = py_record_group_methods,
+ .tp_getset = py_record_group_getseters,
+
+ .tp_init = py_record_group_init,
+ .tp_new = py_record_group_new,
+
+ };
+
+ return &py_record_group_type;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Prend en charge l'objet 'pychrysalide...records.RecordGroup. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool ensure_python_record_group_is_registered(void)
+{
+ PyTypeObject *type; /* Type Python 'RecordGroup' */
+ PyObject *module; /* Module à recompléter */
+ PyObject *dict; /* Dictionnaire du module */
+
+ type = get_python_record_group_type();
+
+ if (!PyType_HasFeature(type, Py_TPFLAGS_READY))
+ {
+ module = get_access_to_python_module("pychrysalide.plugins.kaitai.records");
+
+ dict = PyModule_GetDict(module);
+
+ if (!ensure_python_match_record_is_registered())
+ return false;
+
+ if (!register_class_for_pygobject(dict, G_TYPE_RECORD_GROUP, type))
+ return false;
+
+ }
+
+ return true;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : arg = argument quelconque à tenter de convertir. *
+* dst = destination des valeurs récupérées en cas de succès. *
+* *
+* Description : Tente de convertir en correspondances attribut/binaire. *
+* *
+* Retour : Bilan de l'opération, voire indications supplémentaires. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+int convert_to_record_group(PyObject *arg, void *dst)
+{
+ int result; /* Bilan à retourner */
+
+ result = PyObject_IsInstance(arg, (PyObject *)get_python_record_group_type());
+
+ switch (result)
+ {
+ case -1:
+ /* L'exception est déjà fixée par Python */
+ result = 0;
+ break;
+
+ case 0:
+ PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to record group");
+ break;
+
+ case 1:
+ *((GRecordGroup **)dst) = G_RECORD_GROUP(pygobject_get(arg));
+ break;
+
+ default:
+ assert(false);
+ break;
+
+ }
+
+ return result;
+
+}
diff --git a/plugins/kaitai/python/records/group.h b/plugins/kaitai/python/records/group.h
new file mode 100644
index 0000000..3e12ffc
--- /dev/null
+++ b/plugins/kaitai/python/records/group.h
@@ -0,0 +1,45 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * group.h - prototypes pour l'équivalent Python du fichier "plugins/kaitai/records/group.h"
+ *
+ * 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef _PLUGINS_KAITAI_PYTHON_RECORDS_GROUP_H
+#define _PLUGINS_KAITAI_PYTHON_RECORDS_GROUP_H
+
+
+#include <Python.h>
+#include <stdbool.h>
+
+
+
+/* Fournit un accès à une définition de type à diffuser. */
+PyTypeObject *get_python_record_group_type(void);
+
+/* Prend en charge l'objet 'pychrysalide.plugins.kaitai.records.RecordGroup'. */
+bool ensure_python_record_group_is_registered(void);
+
+/* Tente de convertir en correspondances attribut/binaire. */
+int convert_to_record_group(PyObject *, void *);
+
+
+
+#endif /* _PLUGINS_KAITAI_PYTHON_RECORDS_GROUP_H */
diff --git a/plugins/kaitai/python/records/item.c b/plugins/kaitai/python/records/item.c
new file mode 100644
index 0000000..84c2c58
--- /dev/null
+++ b/plugins/kaitai/python/records/item.c
@@ -0,0 +1,394 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * item.h - équivalent Python du fichier "plugins/kaitai/parsers/item.h"
+ *
+ * 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "item.h"
+
+
+#include <assert.h>
+#include <pygobject.h>
+
+
+#include <i18n.h>
+#include <plugins/pychrysalide/access.h>
+#include <plugins/pychrysalide/helpers.h>
+#include <plugins/pychrysalide/analysis/content.h>
+#include <plugins/pychrysalide/arch/vmpa.h>
+#include <plugins/yaml/python/node.h>
+
+
+#include "../record.h"
+#include "../parsers/attribute.h"
+#include "../../records/item-int.h"
+
+
+
+CREATE_DYN_CONSTRUCTOR(record_item, G_TYPE_RECORD_ITEM);
+
+/* Initialise une instance sur la base du dérivé de GObject. */
+static int py_record_item_init(PyObject *, PyObject *, PyObject *);
+
+/* Lit la série d'octets d'un élément Kaitai entier représenté. */
+static PyObject *py_record_item_get_truncated_bytes(PyObject *, void *);
+
+/* Lit la valeur d'un élément Kaitai entier représenté. */
+static PyObject *py_record_item_get_value(PyObject *, void *);
+
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet à initialiser (théoriquement). *
+* args = arguments fournis à l'appel. *
+* kwds = arguments de type key=val fournis. *
+* *
+* Description : Initialise une instance sur la base du dérivé de GObject. *
+* *
+* Retour : 0. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static int py_record_item_init(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ GKaitaiAttribute *attrib; /* Attribut défini créateur */
+ GBinContent *content; /* Contenu binaire analysé */
+ mrange_t range; /* Espace couvert */
+ SourceEndian endian; /* Boutisme à observer */
+ int ret; /* Bilan de lecture des args. */
+ GRecordItem *item; /* Création GLib à transmettre */
+
+#define RECORD_ITEM_DOC \
+ "The RecordItem class remembers a match between a described attribute and" \
+ " its concret value read from parsed binary data." \
+ "\n" \
+ "Instances can be created using the following constructor:\n" \
+ "\n" \
+ " RecordItem(content, range, endian, attrib)" \
+ "\n" \
+ "Where the *attrib* arguments refers to a" \
+ " pychrysalide.plugins.kaitai.parsers.KaitaiAttribute instance as the" \
+ " creator of the newly created object, *content* points to a" \
+ " pychrysalide.analysis.BinContent instance, *range* is a" \
+ " pychrysalide.arch.mrange object, *endian* states with a" \
+ " pychrysalide.analysis.BinContent.SourceEndian hint the endianness used" \
+ " to read integer values."
+
+ /* Récupération des paramètres */
+
+ ret = PyArg_ParseTuple(args, "O&O&O&",
+ convert_to_kaitai_attribute, &attrib,
+ convert_to_binary_content, &content,
+ convert_any_to_mrange, &range,
+ convert_to_binary_content, &endian);
+ if (!ret) return -1;
+
+ /* Initialisation d'un objet GLib */
+
+ ret = forward_pygobjet_init(self);
+ if (ret == -1) return -1;
+
+ /* Eléments de base */
+
+ item = G_RECORD_ITEM(pygobject_get(self));
+
+ if (!g_record_item_create(item, attrib, content, &range, endian))
+ {
+ PyErr_SetString(PyExc_ValueError, _("Unable to create record item."));
+ return -1;
+ }
+
+ return 0;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet Python concerné par l'appel. *
+* closure = non utilisé ici. *
+* *
+* Description : Lit la série d'octets d'un élément Kaitai entier représenté. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_record_item_get_truncated_bytes(PyObject *self, void *closure)
+{
+ PyObject *result; /* Valeur à retourner */
+ GRecordItem *item; /* Version native de l'élément */
+ bool status; /* Bilan d'opération */
+ bin_t *out; /* Données brutes à transmettre*/
+ size_t len; /* Quantité de ces données */
+
+#define RECORD_ITEM_TRUNCATED_BYTES_ATTRIB PYTHON_GET_DEF_FULL \
+( \
+ truncated_bytes, py_record_item, \
+ "Raw bytes carried by the item (truncated if a separator" \
+ " is defined in the linked attribute), or None if irrelevant" \
+ " regarding to the type of the attribute." \
+)
+
+ item = G_RECORD_ITEM(pygobject_get(self));
+
+ status = g_record_item_get_truncated_bytes(item, &out, &len);
+
+ if (status)
+ {
+ result = PyBytes_FromStringAndSize((char *)out, len);
+ free(out);
+ }
+ else
+ {
+ result = Py_None;
+ Py_INCREF(result);
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet Python concerné par l'appel. *
+* closure = non utilisé ici. *
+* *
+* Description : Lit la valeur d'un élément Kaitai entier représenté. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_record_item_get_value(PyObject *self, void *closure)
+{
+ PyObject *result; /* Valeur à retourner */
+ GRecordItem *item; /* Version native de l'élément */
+ resolved_value_t resolved; /* Valeur sous forme générique */
+ bool status; /* Bilan d'opération */
+
+#define RECORD_ITEM_VALUE_ATTRIB PYTHON_GET_DEF_FULL \
+( \
+ value, py_record_item, \
+ "Carried value (as integer, bytes), or None in case of error." \
+)
+
+ result = NULL;
+
+ item = G_RECORD_ITEM(pygobject_get(self));
+
+ status = g_record_item_get_value(item, &resolved);
+
+ if (status)
+ switch (resolved.type)
+ {
+ case GVT_ERROR:
+ assert(false);
+ PyErr_Format(PyExc_RuntimeError,
+ _("Error got while parsing Kaitai definition should not have been exported!"));
+ result = NULL;
+ break;
+
+ case GVT_UNSIGNED_INTEGER:
+ result = PyLong_FromUnsignedLongLong(resolved.unsigned_integer);
+ break;
+
+ case GVT_SIGNED_INTEGER:
+ result = PyLong_FromLongLong(resolved.signed_integer);
+ break;
+
+ case GVT_FLOAT:
+ result = PyFloat_FromDouble(resolved.floating_number);
+ break;
+
+ case GVT_BOOLEAN:
+ result = resolved.status ? Py_True : Py_False;
+ Py_INCREF(result);
+ break;
+
+ case GVT_BYTES:
+ result = PyBytes_FromStringAndSize(resolved.bytes.data, resolved.bytes.len);
+ exit_szstr(&resolved.bytes);
+ break;
+
+ case GVT_ARRAY:
+ result = pygobject_new(G_OBJECT(resolved.array));
+ break;
+
+ case GVT_RECORD:
+ result = pygobject_new(G_OBJECT(resolved.record));
+ break;
+
+ case GVT_STREAM:
+ result = pygobject_new(G_OBJECT(resolved.stream));
+ break;
+
+ }
+
+ else
+ {
+ result = Py_None;
+ Py_INCREF(result);
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Fournit un accès à une définition de type à diffuser. *
+* *
+* Retour : Définition d'objet pour Python. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+PyTypeObject *get_python_record_item_type(void)
+{
+ static PyMethodDef py_record_item_methods[] = {
+ { NULL }
+ };
+
+ static PyGetSetDef py_record_item_getseters[] = {
+ RECORD_ITEM_TRUNCATED_BYTES_ATTRIB,
+ RECORD_ITEM_VALUE_ATTRIB,
+ { NULL }
+ };
+
+ static PyTypeObject py_record_item_type = {
+
+ PyVarObject_HEAD_INIT(NULL, 0)
+
+ .tp_name = "pychrysalide.plugins.kaitai.records.RecordItem",
+ .tp_basicsize = sizeof(PyGObject),
+
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+
+ .tp_doc = RECORD_ITEM_DOC,
+
+ .tp_methods = py_record_item_methods,
+ .tp_getset = py_record_item_getseters,
+
+ .tp_init = py_record_item_init,
+ .tp_new = py_record_item_new,
+
+ };
+
+ return &py_record_item_type;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Prend en charge l'objet 'pychrysalide....records.RecordItem. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool ensure_python_record_item_is_registered(void)
+{
+ PyTypeObject *type; /* Type Python 'RecordItem' */
+ PyObject *module; /* Module à recompléter */
+ PyObject *dict; /* Dictionnaire du module */
+
+ type = get_python_record_item_type();
+
+ if (!PyType_HasFeature(type, Py_TPFLAGS_READY))
+ {
+ module = get_access_to_python_module("pychrysalide.plugins.kaitai.records");
+
+ dict = PyModule_GetDict(module);
+
+ if (!ensure_python_match_record_is_registered())
+ return false;
+
+ if (!register_class_for_pygobject(dict, G_TYPE_RECORD_ITEM, type))
+ return false;
+
+ }
+
+ return true;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : arg = argument quelconque à tenter de convertir. *
+* dst = destination des valeurs récupérées en cas de succès. *
+* *
+* Description : Tente de convertir en correspondance attribut/binaire. *
+* *
+* Retour : Bilan de l'opération, voire indications supplémentaires. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+int convert_to_record_item(PyObject *arg, void *dst)
+{
+ int result; /* Bilan à retourner */
+
+ result = PyObject_IsInstance(arg, (PyObject *)get_python_record_item_type());
+
+ switch (result)
+ {
+ case -1:
+ /* L'exception est déjà fixée par Python */
+ result = 0;
+ break;
+
+ case 0:
+ PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to record item");
+ break;
+
+ case 1:
+ *((GRecordItem **)dst) = G_RECORD_ITEM(pygobject_get(arg));
+ break;
+
+ default:
+ assert(false);
+ break;
+
+ }
+
+ return result;
+
+}
diff --git a/plugins/kaitai/python/records/item.h b/plugins/kaitai/python/records/item.h
new file mode 100644
index 0000000..bde8a55
--- /dev/null
+++ b/plugins/kaitai/python/records/item.h
@@ -0,0 +1,45 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * item.h - prototypes pour l'équivalent Python du fichier "plugins/kaitai/records/item.h"
+ *
+ * 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef _PLUGINS_KAITAI_PYTHON_RECORDS_ITEM_H
+#define _PLUGINS_KAITAI_PYTHON_RECORDS_ITEM_H
+
+
+#include <Python.h>
+#include <stdbool.h>
+
+
+
+/* Fournit un accès à une définition de type à diffuser. */
+PyTypeObject *get_python_record_item_type(void);
+
+/* Prend en charge l'objet 'pychrysalide.plugins.kaitai.records.RecordItem'. */
+bool ensure_python_record_item_is_registered(void);
+
+/* Tente de convertir en correspondance attribut/binaire. */
+int convert_to_record_item(PyObject *, void *);
+
+
+
+#endif /* _PLUGINS_KAITAI_PYTHON_RECORDS_ITEM_H */
diff --git a/plugins/kaitai/python/records/list.c b/plugins/kaitai/python/records/list.c
new file mode 100644
index 0000000..d2eecbb
--- /dev/null
+++ b/plugins/kaitai/python/records/list.c
@@ -0,0 +1,336 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * list.h - équivalent Python du fichier "plugins/kaitai/parsers/list.h"
+ *
+ * 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "list.h"
+
+
+#include <pygobject.h>
+#include <string.h>
+
+
+#include <i18n.h>
+#include <plugins/pychrysalide/access.h>
+#include <plugins/pychrysalide/helpers.h>
+#include <plugins/pychrysalide/analysis/content.h>
+#include <plugins/pychrysalide/arch/vmpa.h>
+#include <plugins/yaml/python/node.h>
+
+
+#include "../record.h"
+#include "../parsers/attribute.h"
+#include "../../records/list-int.h"
+
+
+
+CREATE_DYN_CONSTRUCTOR(record_list, G_TYPE_RECORD_LIST);
+
+/* Initialise une instance sur la base du dérivé de GObject. */
+static int py_record_list_init(PyObject *, PyObject *, PyObject *);
+
+/* Dénombre le nombre de correspondances enregistrées. */
+static Py_ssize_t py_record_list_sq_length(PyObject *);
+
+/* Fournit un élément ciblé dans la liste de correspondances. */
+static PyObject *py_record_list_sq_item(PyObject *, Py_ssize_t);
+
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet à initialiser (théoriquement). *
+* args = arguments fournis à l'appel. *
+* kwds = arguments de type key=val fournis. *
+* *
+* Description : Initialise une instance sur la base du dérivé de GObject. *
+* *
+* Retour : 0. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static int py_record_list_init(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ int result; /* Bilan à retourner */
+ GKaitaiAttribute *attrib; /* Attribut défini créateur */
+ GBinContent *content; /* Contenu binaire analysé */
+ vmpa2t *addr; /* Adresse de symbole à ajouter*/
+ int ret; /* Bilan de lecture des args. */
+ GRecordList *list; /* Création GLib à transmettre */
+
+#define RECORD_LIST_DOC \
+ "The RecordList class collects a list of parsed attributes with their" \
+ " relative values. Each of theses Kaitai attributes can be accessed as" \
+ " subscriptable Python attribute.\n" \
+ "\n" \
+ "Instances can be created using the following constructor:\n" \
+ "\n" \
+ " RecordList(content, attrib)" \
+ "\n" \
+ "Where the *attrib* argument refers to the" \
+ " pychrysalide.plugins.kaitai.parsers.KaitaiAttribute instance used to" \
+ " create each record contained by the list and *content* points to a" \
+ " pychrysalide.analysis.BinContent instance."
+
+ result = 0;
+
+ /* Récupération des paramètres */
+
+ ret = PyArg_ParseTuple(args, "O&O&",
+ convert_to_kaitai_attribute, &attrib,
+ convert_to_binary_content, &content,
+ convert_any_to_vmpa, &addr);
+ if (!ret) return -1;
+
+ /* Initialisation d'un objet GLib */
+
+ ret = forward_pygobjet_init(self);
+ if (ret == -1)
+ {
+ result = -1;
+ goto exit;
+ }
+
+ /* Eléments de base */
+
+ list = G_RECORD_LIST(pygobject_get(self));
+
+ if (!g_record_list_create(list, attrib, content, addr))
+ {
+ PyErr_SetString(PyExc_ValueError, _("Unable to create record list."));
+ result = -1;
+ goto exit;
+ }
+
+ exit:
+
+ clean_vmpa_arg(addr);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = instance Python manipulée. *
+* *
+* Description : Dénombre le nombre de correspondances enregistrées. *
+* *
+* Retour : Taille de la liste représentée. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static Py_ssize_t py_record_list_sq_length(PyObject *self)
+{
+ Py_ssize_t result; /* Quantité à retourner */
+ GRecordList *list; /* Version native de l'objet */
+
+ list = G_RECORD_LIST(pygobject_get(self));
+
+ result = g_record_list_count_records(list);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = structure C convertie en Python. *
+* index = indice de la correspondance visée. *
+* *
+* Description : Fournit un élément ciblé dans la liste de correspondances. *
+* *
+* Retour : Instance de correspondance particulière, voire None. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_record_list_sq_item(PyObject *self, Py_ssize_t index)
+{
+ PyObject *result; /* Instance à retourner */
+ GRecordList *list; /* Version native de l'objet */
+ GMatchRecord *record; /* Correspondance retrouvée */
+
+ list = G_RECORD_LIST(pygobject_get(self));
+
+ record = g_record_list_get_record(list, index);
+
+ if (record != NULL)
+ {
+ result = pygobject_new(G_OBJECT(record));
+ g_object_unref(G_OBJECT(record));
+ }
+ else
+ {
+ result = Py_None;
+ Py_INCREF(result);
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Fournit un accès à une définition de type à diffuser. *
+* *
+* Retour : Définition d'objet pour Python. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+PyTypeObject *get_python_record_list_type(void)
+{
+ static PySequenceMethods py_record_list_sequence_methods = {
+
+ .sq_length = py_record_list_sq_length,
+ .sq_item = py_record_list_sq_item,
+
+ };
+
+ static PyMethodDef py_record_list_methods[] = {
+ { NULL }
+ };
+
+ static PyGetSetDef py_record_list_getseters[] = {
+ { NULL }
+ };
+
+ static PyTypeObject py_record_list_type = {
+
+ PyVarObject_HEAD_INIT(NULL, 0)
+
+ .tp_name = "pychrysalide.plugins.kaitai.records.RecordList",
+ .tp_basicsize = sizeof(PyGObject),
+
+ .tp_as_sequence = &py_record_list_sequence_methods,
+
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+
+ .tp_doc = RECORD_LIST_DOC,
+
+ .tp_methods = py_record_list_methods,
+ .tp_getset = py_record_list_getseters,
+
+ .tp_init = py_record_list_init,
+ .tp_new = py_record_list_new,
+
+ };
+
+ return &py_record_list_type;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Prend en charge l'objet 'pychrysalide....records.RecordList. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool ensure_python_record_list_is_registered(void)
+{
+ PyTypeObject *type; /* Type Python 'RecordList' */
+ PyObject *module; /* Module à recompléter */
+ PyObject *dict; /* Dictionnaire du module */
+
+ type = get_python_record_list_type();
+
+ if (!PyType_HasFeature(type, Py_TPFLAGS_READY))
+ {
+ module = get_access_to_python_module("pychrysalide.plugins.kaitai.records");
+
+ dict = PyModule_GetDict(module);
+
+ if (!ensure_python_match_record_is_registered())
+ return false;
+
+ if (!register_class_for_pygobject(dict, G_TYPE_RECORD_LIST, type))
+ return false;
+
+ }
+
+ return true;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : arg = argument quelconque à tenter de convertir. *
+* dst = destination des valeurs récupérées en cas de succès. *
+* *
+* Description : Tente de convertir en correspondance attribut/binaire. *
+* *
+* Retour : Bilan de l'opération, voire indications supplémentaires. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+int convert_to_record_list(PyObject *arg, void *dst)
+{
+ int result; /* Bilan à retourner */
+
+ result = PyObject_IsInstance(arg, (PyObject *)get_python_record_list_type());
+
+ switch (result)
+ {
+ case -1:
+ /* L'exception est déjà fixée par Python */
+ result = 0;
+ break;
+
+ case 0:
+ PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to record list");
+ break;
+
+ case 1:
+ *((GRecordList **)dst) = G_RECORD_LIST(pygobject_get(arg));
+ break;
+
+ default:
+ assert(false);
+ break;
+
+ }
+
+ return result;
+
+}
diff --git a/plugins/kaitai/python/records/list.h b/plugins/kaitai/python/records/list.h
new file mode 100644
index 0000000..53572a9
--- /dev/null
+++ b/plugins/kaitai/python/records/list.h
@@ -0,0 +1,45 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * list.h - prototypes pour l'équivalent Python du fichier "plugins/kaitai/records/list.h"
+ *
+ * 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef _PLUGINS_KAITAI_PYTHON_RECORDS_LIST_H
+#define _PLUGINS_KAITAI_PYTHON_RECORDS_LIST_H
+
+
+#include <Python.h>
+#include <stdbool.h>
+
+
+
+/* Fournit un accès à une définition de type à diffuser. */
+PyTypeObject *get_python_record_list_type(void);
+
+/* Prend en charge l'objet 'pychrysalide.plugins.kaitai.records.RecordList'. */
+bool ensure_python_record_list_is_registered(void);
+
+/* Tente de convertir en correspondance attribut/binaire. */
+int convert_to_record_list(PyObject *, void *);
+
+
+
+#endif /* _PLUGINS_KAITAI_PYTHON_RECORDS_LIST_H */
diff --git a/plugins/kaitai/python/records/module.c b/plugins/kaitai/python/records/module.c
new file mode 100644
index 0000000..ea33c31
--- /dev/null
+++ b/plugins/kaitai/python/records/module.c
@@ -0,0 +1,122 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * module.c - intégration du répertoire records en tant que module
+ *
+ * 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "module.h"
+
+
+#include <Python.h>
+
+
+#include <plugins/pychrysalide/access.h>
+#include <plugins/pychrysalide/helpers.h>
+
+
+#include "empty.h"
+#include "group.h"
+#include "item.h"
+#include "list.h"
+#include "value.h"
+
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Ajoute le module 'plugins.kaitai.records' au module Python. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool add_kaitai_records_module(void)
+{
+ bool result; /* Bilan à retourner */
+ PyObject *super; /* Module à compléter */
+ PyObject *module; /* Sous-module mis en place */
+
+#define PYCHRYSALIDE_PLUGINS_KAITAI_RECORDS_DOC \
+ "This module is providing objects used to link structure" \
+ " definitions with their data."
+
+ static PyModuleDef py_chrysalide_kaitai_records_module = {
+
+ .m_base = PyModuleDef_HEAD_INIT,
+
+ .m_name = "pychrysalide.plugins.kaitai.records",
+ .m_doc = PYCHRYSALIDE_PLUGINS_KAITAI_RECORDS_DOC,
+
+ .m_size = -1,
+
+ };
+
+ result = false;
+
+ super = get_access_to_python_module("pychrysalide.plugins.kaitai");
+
+ module = build_python_module(super, &py_chrysalide_kaitai_records_module);
+
+ result = (module != NULL);
+
+ assert(result);
+
+ if (!result)
+ Py_XDECREF(module);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Intègre les objets du module 'plugins.kaitai.records'. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool populate_kaitai_records_module(void)
+{
+ bool result; /* Bilan à retourner */
+
+ result = true;
+
+ if (result) result = ensure_python_record_empty_is_registered();
+ if (result) result = ensure_python_record_group_is_registered();
+ if (result) result = ensure_python_record_item_is_registered();
+ if (result) result = ensure_python_record_list_is_registered();
+ if (result) result = ensure_python_record_value_is_registered();
+
+ assert(result);
+
+ return result;
+
+}
diff --git a/plugins/kaitai/python/records/module.h b/plugins/kaitai/python/records/module.h
new file mode 100644
index 0000000..ff631dc
--- /dev/null
+++ b/plugins/kaitai/python/records/module.h
@@ -0,0 +1,41 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * module.h - prototypes pour l'intégration du répertoire records en tant que module
+ *
+ * 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef _PLUGINS_KAITAI_PYTHON_RECORDS_MODULE_H
+#define _PLUGINS_KAITAI_PYTHON_RECORDS_MODULE_H
+
+
+#include <stdbool.h>
+
+
+
+/* Ajoute le module 'plugins.kaitai.records' au module Python. */
+bool add_kaitai_records_module(void);
+
+/* Intègre les objets du module 'plugins.kaitai.records'. */
+bool populate_kaitai_records_module(void);
+
+
+
+#endif /* _PLUGINS_KAITAI_PYTHON_RECORDS_MODULE_H */
diff --git a/plugins/kaitai/python/records/value.c b/plugins/kaitai/python/records/value.c
new file mode 100644
index 0000000..bd4ad74
--- /dev/null
+++ b/plugins/kaitai/python/records/value.c
@@ -0,0 +1,335 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * value.c - équivalent Python du fichier "plugins/kaitai/parsers/value.c"
+ *
+ * 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "value.h"
+
+
+#include <assert.h>
+#include <pygobject.h>
+
+
+#include <i18n.h>
+#include <plugins/pychrysalide/access.h>
+#include <plugins/pychrysalide/helpers.h>
+#include <plugins/pychrysalide/analysis/content.h>
+#include <plugins/pychrysalide/arch/vmpa.h>
+#include <plugins/yaml/python/node.h>
+
+
+#include "../record.h"
+#include "../scope.h"
+#include "../parsers/instance.h"
+#include "../../records/value-int.h"
+
+
+
+CREATE_DYN_CONSTRUCTOR(record_value, G_TYPE_RECORD_VALUE);
+
+/* Initialise une instance sur la base du dérivé de GObject. */
+static int py_record_value_init(PyObject *, PyObject *, PyObject *);
+
+/* Lit la valeur d'un élément Kaitai entier représenté. */
+static PyObject *py_record_value_get_value(PyObject *, void *);
+
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet à initialiser (théoriquement). *
+* args = arguments fournis à l'appel. *
+* kwds = arguments de type key=val fournis. *
+* *
+* Description : Initialise une instance sur la base du dérivé de GObject. *
+* *
+* Retour : 0. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static int py_record_value_init(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ GKaitaiInstance *inst; /* Instance définie créatrice */
+ kaitai_scope_t *locals; /* Environnement local */
+ int ret; /* Bilan de lecture des args. */
+ GRecordValue *value; /* Création GLib à transmettre */
+
+#define RECORD_VALUE_DOC \
+ "The RecordValue class stores a link to an instance used to compute a" \
+ " given value." \
+ "\n" \
+ "Instances can be created using the following constructor:\n" \
+ "\n" \
+ " RecordValue(inst, locals)" \
+ "\n" \
+ "Where the *inst* arguments refers to a" \
+ " pychrysalide.plugins.kaitai.parsers.KaitaiInstance instance as the" \
+ " creator of the newly created object, *locals* points to a" \
+ " pychrysalide.plugins.kaitai.KaitaiScope structure used as current scope."
+
+ /* Récupération des paramètres */
+
+ ret = PyArg_ParseTuple(args, "O&O&",
+ convert_to_kaitai_instance, &inst,
+ convert_to_kaitai_scope, &locals);
+ if (!ret) return -1;
+
+ /* Initialisation d'un objet GLib */
+
+ ret = forward_pygobjet_init(self);
+ if (ret == -1) return -1;
+
+ /* Eléments de base */
+
+ value = G_RECORD_VALUE(pygobject_get(self));
+
+ if (!g_record_value_create(value, inst, locals))
+ {
+ PyErr_SetString(PyExc_ValueError, _("Unable to create record value."));
+ return -1;
+ }
+
+ return 0;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet Python concerné par l'appel. *
+* closure = non utilisé ici. *
+* *
+* Description : Lit la valeur d'un élément Kaitai entier représenté. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_record_value_get_value(PyObject *self, void *closure)
+{
+ PyObject *result; /* Valeur à retourner */
+ GRecordValue *value; /* Version native de l'élément */
+ resolved_value_t resolved; /* Valeur sous forme générique */
+ bool status; /* Bilan d'opération */
+
+#define RECORD_VALUE_VALUE_ATTRIB PYTHON_GET_DEF_FULL \
+( \
+ value, py_record_value, \
+ "Carried value (as integer, bytes), or None in case of error." \
+)
+
+ result = NULL;
+
+ value = G_RECORD_VALUE(pygobject_get(self));
+
+ status = g_record_value_compute_and_aggregate_value(value, &resolved);
+
+ if (status)
+ switch (resolved.type)
+ {
+ case GVT_ERROR:
+ assert(false);
+ PyErr_Format(PyExc_RuntimeError,
+ _("Error got while parsing Kaitai definition should not have been exported!"));
+ result = NULL;
+ break;
+
+ case GVT_UNSIGNED_INTEGER:
+ result = PyLong_FromUnsignedLongLong(resolved.unsigned_integer);
+ break;
+
+ case GVT_SIGNED_INTEGER:
+ result = PyLong_FromLongLong(resolved.signed_integer);
+ break;
+
+ case GVT_FLOAT:
+ result = PyFloat_FromDouble(resolved.floating_number);
+ break;
+
+ case GVT_BOOLEAN:
+ result = resolved.status ? Py_True : Py_False;
+ Py_INCREF(result);
+ break;
+
+ case GVT_BYTES:
+ result = PyBytes_FromStringAndSize(resolved.bytes.data, resolved.bytes.len);
+ exit_szstr(&resolved.bytes);
+ break;
+
+ case GVT_ARRAY:
+ result = pygobject_new(G_OBJECT(resolved.array));
+ break;
+
+ case GVT_RECORD:
+ result = pygobject_new(G_OBJECT(resolved.record));
+ break;
+
+ case GVT_STREAM:
+ result = pygobject_new(G_OBJECT(resolved.stream));
+ break;
+
+ }
+
+ else
+ {
+ result = Py_None;
+ Py_INCREF(result);
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Fournit un accès à une définition de type à diffuser. *
+* *
+* Retour : Définition d'objet pour Python. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+PyTypeObject *get_python_record_value_type(void)
+{
+ static PyMethodDef py_record_value_methods[] = {
+ { NULL }
+ };
+
+ static PyGetSetDef py_record_value_getseters[] = {
+ RECORD_VALUE_VALUE_ATTRIB,
+ { NULL }
+ };
+
+ static PyTypeObject py_record_value_type = {
+
+ PyVarObject_HEAD_INIT(NULL, 0)
+
+ .tp_name = "pychrysalide.plugins.kaitai.records.RecordValue",
+ .tp_basicsize = sizeof(PyGObject),
+
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+
+ .tp_doc = RECORD_VALUE_DOC,
+
+ .tp_methods = py_record_value_methods,
+ .tp_getset = py_record_value_getseters,
+
+ .tp_init = py_record_value_init,
+ .tp_new = py_record_value_new,
+
+ };
+
+ return &py_record_value_type;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Prend en charge l'objet 'pychrysalide...records.RecordValue. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool ensure_python_record_value_is_registered(void)
+{
+ PyTypeObject *type; /* Type Python 'RecordValue' */
+ PyObject *module; /* Module à recompléter */
+ PyObject *dict; /* Dictionnaire du module */
+
+ type = get_python_record_value_type();
+
+ if (!PyType_HasFeature(type, Py_TPFLAGS_READY))
+ {
+ module = get_access_to_python_module("pychrysalide.plugins.kaitai.records");
+
+ dict = PyModule_GetDict(module);
+
+ if (!ensure_python_match_record_is_registered())
+ return false;
+
+ if (!register_class_for_pygobject(dict, G_TYPE_RECORD_VALUE, type))
+ return false;
+
+ }
+
+ return true;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : arg = argument quelconque à tenter de convertir. *
+* dst = destination des valeurs récupérées en cas de succès. *
+* *
+* Description : Tente de convertir en valeur calculée. *
+* *
+* Retour : Bilan de l'opération, voire indications supplémentaires. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+int convert_to_record_value(PyObject *arg, void *dst)
+{
+ int result; /* Bilan à retourner */
+
+ result = PyObject_IsInstance(arg, (PyObject *)get_python_record_value_type());
+
+ switch (result)
+ {
+ case -1:
+ /* L'exception est déjà fixée par Python */
+ result = 0;
+ break;
+
+ case 0:
+ PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to record value");
+ break;
+
+ case 1:
+ *((GRecordValue **)dst) = G_RECORD_VALUE(pygobject_get(arg));
+ break;
+
+ default:
+ assert(false);
+ break;
+
+ }
+
+ return result;
+
+}
diff --git a/plugins/kaitai/python/records/value.h b/plugins/kaitai/python/records/value.h
new file mode 100644
index 0000000..16cadcb
--- /dev/null
+++ b/plugins/kaitai/python/records/value.h
@@ -0,0 +1,45 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * value.h - prototypes pour l'équivalent Python du fichier "plugins/kaitai/records/value.h"
+ *
+ * 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef _PLUGINS_KAITAI_PYTHON_RECORDS_VALUE_H
+#define _PLUGINS_KAITAI_PYTHON_RECORDS_VALUE_H
+
+
+#include <Python.h>
+#include <stdbool.h>
+
+
+
+/* Fournit un accès à une définition de type à diffuser. */
+PyTypeObject *get_python_record_value_type(void);
+
+/* Prend en charge l'objet 'pychrysalide.plugins.kaitai.records.RecordValue'. */
+bool ensure_python_record_value_is_registered(void);
+
+/* Tente de convertir en valeur calculée. */
+int convert_to_record_value(PyObject *, void *);
+
+
+
+#endif /* _PLUGINS_KAITAI_PYTHON_RECORDS_VALUE_H */
diff --git a/plugins/kaitai/python/scope.c b/plugins/kaitai/python/scope.c
new file mode 100644
index 0000000..b11dc81
--- /dev/null
+++ b/plugins/kaitai/python/scope.c
@@ -0,0 +1,542 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * scope.c - équivalent Python du fichier "plugins/kaitai/scope.c"
+ *
+ * Copyright (C) 2020 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "scope.h"
+
+
+#include <assert.h>
+#include <pygobject.h>
+
+
+#include <plugins/pychrysalide/access.h>
+#include <plugins/pychrysalide/helpers.h>
+
+
+#include "record.h"
+#include "parsers/meta.h"
+#include "../record.h"
+#include "../parsers/meta.h"
+
+
+
+/* Rassemblement de données d'un paquet */
+typedef struct _py_kaitai_scope_t
+{
+ PyObject_HEAD /* A laisser en premier */
+
+ kaitai_scope_t *native; /* Tampon de données lié */
+
+} py_kaitai_scope_t;
+
+
+/* Libère de la mémoire un objet Python 'py_kaitai_scope_t'. */
+static void py_kaitai_scope_dealloc(py_kaitai_scope_t *);
+
+/* Initialise un objet Python de type 'py_kaitai_scope_t'. */
+static int py_kaitai_scope_init(py_kaitai_scope_t *, PyObject *, PyObject *);
+
+/* Conserve le souvenir de la dernière correspondance effectuée. */
+static PyObject *py_kaitai_scope_remember_last_record(PyObject *, PyObject *);
+
+/* Recherche la définition d'un type nouveau pour Kaitai. */
+static PyObject *py_kaitai_scope_find_sub_type(PyObject *, PyObject *);
+
+/* Retourne le souvenir d'une correspondance racine. */
+static PyObject *py_kaitai_scope_get_root_record(PyObject *, void *);
+
+/* Retourne le souvenir de la correspondance parente effectuée. */
+static PyObject *py_kaitai_scope_get_parent_record(PyObject *, void *);
+
+/* Retourne le souvenir de la dernière correspondance effectuée. */
+static PyObject *py_kaitai_scope_get_last_record(PyObject *, void *);
+
+
+
+/******************************************************************************
+* *
+* Paramètres : self = tampon de données à supprimer. *
+* *
+* Description : Libère de la mémoire un objet Python 'py_kaitai_scope_t'. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void py_kaitai_scope_dealloc(py_kaitai_scope_t *self)
+{
+ reset_record_scope(self->native);
+
+ Py_TYPE(self)->tp_free((PyObject *)self);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = instance d'objet à initialiser. *
+* args = arguments passés pour l'appel. *
+* kwds = mots clefs éventuellement fournis en complément. *
+* *
+* Description : Initialise un objet Python de type 'py_kaitai_scope_t'. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static int py_kaitai_scope_init(py_kaitai_scope_t *self, PyObject *args, PyObject *kwds)
+{
+ int result; /* Bilan à retourner */
+ GKaitaiMeta *meta; /* Informations globale */
+ int ret; /* Bilan de lecture des args. */
+
+#define KAITAI_SCOPE_DOC \
+ "The KaitaiScope object stores a local environment which freezes" \
+ " a particular state of the Kaitai parser. It allows the dynamic" \
+ " resolving of values contained in a Kaitai expression.\n" \
+ "\n" \
+ "Instances can be created using the following constructor:\n" \
+ "\n" \
+ " KaitaiScope(meta)" \
+ "\n" \
+ "Where *meta* is a pychrysalide.plugins.kaitai.parsers.KaitaiMeta" \
+ " instance pointing to global information about the Kaitai" \
+ " definition."
+
+ ret = PyArg_ParseTuple(args, "O&", convert_to_kaitai_meta, &meta);
+ if (!ret) return -1;
+
+ self->native = malloc(sizeof(kaitai_scope_t));
+
+ init_record_scope(self->native, meta);
+
+ result = 0;
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = environnement local à manipuler. *
+* args = arguments fournis pour la conduite de l'opération. *
+* *
+* Description : Conserve le souvenir de la dernière correspondance effectuée.*
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_kaitai_scope_remember_last_record(PyObject *self, PyObject *args)
+{
+ PyObject *result; /* Bilan à faire remonter */
+ GMatchRecord *record; /* Correspondance à utiliser */
+ int ret; /* Bilan de lecture des args. */
+ py_kaitai_scope_t *locals; /* Instance à manipuler */
+
+#define KAITAI_SCOPE_REMEMBER_LAST_RECORD_METHOD PYTHON_METHOD_DEF \
+( \
+ remember_last_record, "$self, record, /", \
+ METH_VARARGS, py_kaitai_scope, \
+ "Store a record as the last parsed record.\n" \
+ "\n" \
+ "This *record* is expected to be a" \
+ " pychrysalide.plugins.kaitai.MatchRecord instance." \
+)
+
+ ret = PyArg_ParseTuple(args, "O&", convert_to_match_record, &record);
+ if (!ret) return NULL;
+
+ locals = (py_kaitai_scope_t *)self;
+
+ remember_last_record(locals->native, record);
+
+ result = Py_None;
+ Py_INCREF(result);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = environnement local à manipuler. *
+* args = arguments fournis pour la conduite de l'opération. *
+* *
+* Description : Recherche la définition d'un type nouveau pour Kaitai. *
+* *
+* Retour : Type prêt à emploi ou NULL si non trouvé. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_kaitai_scope_find_sub_type(PyObject *self, PyObject *args)
+{
+ PyObject *result; /* Bilan à faire remonter */
+ const char *name; /* Désignation à retrouver */
+ int ret; /* Bilan de lecture des args. */
+ py_kaitai_scope_t *locals; /* Instance à manipuler */
+ GKaitaiType *type; /* Définition à identifier */
+
+#define KAITAI_SCOPE_FIND_SUB_TYPE_METHOD PYTHON_METHOD_DEF \
+( \
+ find_sub_type, "$self, name, /", \
+ METH_VARARGS, py_kaitai_scope, \
+ "Retrieve the type structure linked to a given name.\n" \
+ "\n" \
+ "This *name* has to be a string.\n" \
+ "\n" \
+ "The result is a known" \
+ " pychrysalide.plugins.kaitai.parsers.KaitaiType instance" \
+ " or *None* if the name has not been registered during" \
+ " the parsing." \
+)
+
+ ret = PyArg_ParseTuple(args, "s", &name);
+ if (!ret) return NULL;
+
+ locals = (py_kaitai_scope_t *)self;
+
+ type = find_sub_type(locals->native, name);
+
+ result = pygobject_new(G_OBJECT(type));
+ g_object_unref(G_OBJECT(type));
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = environnement local à consulter. *
+* closure = adresse non utilisée ici. *
+* *
+* Description : Retourne le souvenir d'une correspondance racine. *
+* *
+* Retour : Dernière correspondance établie ou None. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_kaitai_scope_get_root_record(PyObject *self, void *closure)
+{
+ PyObject *result; /* Conversion à retourner */
+ py_kaitai_scope_t *locals; /* Instance à manipuler */
+ GMatchRecord *record; /* Correspondance à convertir */
+
+#define KAITAI_SCOPE_ROOT_RECORD_ATTRIB PYTHON_GET_DEF_FULL \
+( \
+ root_record, py_kaitai_scope, \
+ "Provide the first record for a parsed content.\n" \
+ "\n" \
+ "The result is a pychrysalide.plugins.kaitai.MatchRecord" \
+ " instance or *None*." \
+)
+
+ locals = (py_kaitai_scope_t *)self;
+
+ record = get_root_record(locals->native);
+
+ if (record != NULL)
+ {
+ result = pygobject_new(G_OBJECT(record));
+ g_object_unref(G_OBJECT(record));
+ }
+ else
+ {
+ result = Py_None;
+ Py_INCREF(result);
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = environnement local à consulter. *
+* closure = adresse non utilisée ici. *
+* *
+* Description : Retourne le souvenir de la correspondance parente effectuée. *
+* *
+* Retour : Dernière correspondance établie ou None. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_kaitai_scope_get_parent_record(PyObject *self, void *closure)
+{
+ PyObject *result; /* Conversion à retourner */
+ py_kaitai_scope_t *locals; /* Instance à manipuler */
+ GMatchRecord *record; /* Correspondance à convertir */
+
+#define KAITAI_SCOPE_PARENT_RECORD_ATTRIB PYTHON_GET_DEF_FULL \
+( \
+ parent_record, py_kaitai_scope, \
+ "Provide the current parent record for a parsed content.\n" \
+ "\n" \
+ "The result is a pychrysalide.plugins.kaitai.MatchRecord" \
+ " instance or *None*." \
+)
+
+ locals = (py_kaitai_scope_t *)self;
+
+ record = get_parent_record(locals->native);
+
+ if (record != NULL)
+ {
+ result = pygobject_new(G_OBJECT(record));
+ g_object_unref(G_OBJECT(record));
+ }
+ else
+ {
+ result = Py_None;
+ Py_INCREF(result);
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = environnement local à consulter. *
+* closure = adresse non utilisée ici. *
+* *
+* Description : Retourne le souvenir de la dernière correspondance effectuée.*
+* *
+* Retour : Dernière correspondance établie ou None. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_kaitai_scope_get_last_record(PyObject *self, void *closure)
+{
+ PyObject *result; /* Conversion à retourner */
+ py_kaitai_scope_t *locals; /* Instance à manipuler */
+ GMatchRecord *record; /* Correspondance à convertir */
+
+#define KAITAI_SCOPE_LAST_RECORD_ATTRIB PYTHON_GET_DEF_FULL \
+( \
+ last_record, py_kaitai_scope, \
+ "Provide the last createdrecord for a parsed content.\n" \
+ "\n" \
+ "The result is a pychrysalide.plugins.kaitai.MatchRecord" \
+ " instance or *None*." \
+)
+
+ locals = (py_kaitai_scope_t *)self;
+
+ record = get_last_record(locals->native);
+
+ if (record != NULL)
+ {
+ result = pygobject_new(G_OBJECT(record));
+ g_object_unref(G_OBJECT(record));
+ }
+ else
+ {
+ result = Py_None;
+ Py_INCREF(result);
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Fournit un accès à une définition de type à diffuser. *
+* *
+* Retour : Définition d'objet pour Python. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+PyTypeObject *get_python_kaitai_scope_type(void)
+{
+ static PyMethodDef py_kaitai_scope_methods[] = {
+ KAITAI_SCOPE_REMEMBER_LAST_RECORD_METHOD,
+ KAITAI_SCOPE_FIND_SUB_TYPE_METHOD,
+ { NULL }
+ };
+
+ static PyGetSetDef py_kaitai_scope_getseters[] = {
+ KAITAI_SCOPE_ROOT_RECORD_ATTRIB,
+ KAITAI_SCOPE_PARENT_RECORD_ATTRIB,
+ KAITAI_SCOPE_LAST_RECORD_ATTRIB,
+ { NULL }
+ };
+
+ static PyTypeObject py_kaitai_scope_type = {
+
+ PyVarObject_HEAD_INIT(NULL, 0)
+
+ .tp_name = "pychrysalide.plugins.kaitai.KaitaiScope",
+ .tp_basicsize = sizeof(py_kaitai_scope_t),
+
+ .tp_dealloc = (destructor)py_kaitai_scope_dealloc,
+
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+
+ .tp_doc = KAITAI_SCOPE_DOC,
+
+ .tp_methods = py_kaitai_scope_methods,
+ .tp_getset = py_kaitai_scope_getseters,
+
+ .tp_init = (initproc)py_kaitai_scope_init,
+ .tp_new = PyType_GenericNew,
+
+ };
+
+ return &py_kaitai_scope_type;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : module = module dont la définition est à compléter. *
+* *
+* Description : Prend en charge l'objet 'pychrysalide.common.PackedBuffer'. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool ensure_python_kaitai_scope_is_registered(void)
+{
+ PyTypeObject *type; /* Type Python 'PackedBuffer' */
+ PyObject *module; /* Module à recompléter */
+
+ type = get_python_kaitai_scope_type();
+
+ if (!PyType_HasFeature(type, Py_TPFLAGS_READY))
+ {
+ if (PyType_Ready(type) != 0)
+ return false;
+
+ module = get_access_to_python_module("pychrysalide.plugins.kaitai");
+
+ if (!register_python_module_object(module, type))
+ return false;
+
+ }
+
+ return true;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : locals = structure interne à copier en objet Python. *
+* *
+* Description : Convertit une structure 'kaitai_scope_t' en objet Python. *
+* *
+* Retour : Object Python résultant de la conversion opérée. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+PyObject *build_from_internal_kaitai_scope(const kaitai_scope_t *locals)
+{
+ PyObject *result; /* Instance à retourner */
+ PyTypeObject *type; /* Type à instancier */
+
+ type = get_python_kaitai_scope_type();
+
+ result = PyObject_CallObject((PyObject *)type, NULL);
+
+ copy_record_scope(((py_kaitai_scope_t *)result)->native, locals);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : arg = argument quelconque à tenter de convertir. *
+* dst = destination des valeurs récupérées en cas de succès. *
+* *
+* Description : Tente de convertir en environnement local pour Kaitai. *
+* *
+* Retour : Bilan de l'opération, voire indications supplémentaires. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+int convert_to_kaitai_scope(PyObject *arg, void *dst)
+{
+ int result; /* Bilan à retourner */
+
+ result = PyObject_IsInstance(arg, (PyObject *)get_python_kaitai_scope_type());
+
+ switch (result)
+ {
+ case -1:
+ /* L'exception est déjà fixée par Python */
+ result = 0;
+ break;
+
+ case 0:
+ PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to Kaitai scope");
+ break;
+
+ case 1:
+ *((kaitai_scope_t **)dst) = ((py_kaitai_scope_t *)arg)->native;
+ break;
+
+ default:
+ assert(false);
+ break;
+
+ }
+
+ return result;
+
+}
diff --git a/plugins/kaitai/python/scope.h b/plugins/kaitai/python/scope.h
new file mode 100644
index 0000000..9353b06
--- /dev/null
+++ b/plugins/kaitai/python/scope.h
@@ -0,0 +1,51 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * scope.h - prototypes pour l'équivalent Python du fichier "plugins/kaitai/scope.h"
+ *
+ * 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef _PLUGINS_KAITAI_PYTHON_SCOPE_H
+#define _PLUGINS_KAITAI_PYTHON_SCOPE_H
+
+
+#include <Python.h>
+#include <stdbool.h>
+
+
+#include "../scope.h"
+
+
+
+/* Fournit un accès à une définition de type à diffuser. */
+PyTypeObject *get_python_kaitai_scope_type(void);
+
+/* Prend en charge l'objet 'pychrysalide.common.PackedBuffer'. */
+bool ensure_python_kaitai_scope_is_registered(void);
+
+/* Convertit une structure 'kaitai_scope_t' en objet Python. */
+PyObject *build_from_internal_kaitai_scope(const kaitai_scope_t *);
+
+/* Tente de convertir en environnement local pour Kaitai. */
+int convert_to_kaitai_scope(PyObject *, void *);
+
+
+
+#endif /* _PLUGINS_KAITAI_PYTHON_SCOPE_H */
diff --git a/plugins/kaitai/python/stream.c b/plugins/kaitai/python/stream.c
new file mode 100644
index 0000000..985e3c3
--- /dev/null
+++ b/plugins/kaitai/python/stream.c
@@ -0,0 +1,278 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * stream.h - équivalent Python du fichier "plugins/kaitai/stream.h"
+ *
+ * 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include "stream.h"
+
+
+#include <pygobject.h>
+
+
+#include <i18n.h>
+#include <plugins/pychrysalide/access.h>
+#include <plugins/pychrysalide/helpers.h>
+#include <plugins/pychrysalide/analysis/content.h>
+#include <plugins/pychrysalide/arch/vmpa.h>
+
+
+#include "../stream-int.h"
+
+
+
+CREATE_DYN_CONSTRUCTOR(kaitai_stream, G_TYPE_KAITAI_STREAM);
+
+/* Initialise une instance sur la base du dérivé de GObject. */
+static int py_kaitai_stream_init(PyObject *, PyObject *, PyObject *);
+
+/* Détermine si la fin des données a été atteinte. */
+static PyObject *py_kaitai_stream_get_eof(PyObject *, void *);
+
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet à initialiser (théoriquement). *
+* args = arguments fournis à l'appel. *
+* kwds = arguments de type key=val fournis. *
+* *
+* Description : Initialise une instance sur la base du dérivé de GObject. *
+* *
+* Retour : 0. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static int py_kaitai_stream_init(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ GBinContent *content; /* Contenu binaire manipulé */
+ vmpa2t *pos; /* Tête de lecture courante */
+ int ret; /* Bilan de lecture des args. */
+ GKaitaiStream *stream; /* Création GLib à transmettre */
+
+#define KAITAI_STREAM_DOC \
+ "KaitaiStream collects all the information useful for the processing of" \
+ " binary data." \
+ "\n" \
+ "Instances can be created using following constructor:\n" \
+ "\n" \
+ " KaitaiStream(content, pos)" \
+ "\n" \
+ "Where *content* is a pychrysalide.analysis.BinContent instance providing" \
+ " the processed data and *pos* defines the current reading location, as a" \
+ " pychrysalide.arch.vmpa value."
+
+ /* Récupération des paramètres */
+
+ ret = PyArg_ParseTuple(args, "O&O&", convert_to_binary_content, &content, convert_any_to_vmpa, &pos);
+ if (!ret) return -1;
+
+ /* Initialisation d'un objet GLib */
+
+ ret = forward_pygobjet_init(self);
+ if (ret == -1) return -1;
+
+ /* Eléments de base */
+
+ stream = G_KAITAI_STREAM(pygobject_get(self));
+
+ if (!g_kaitai_stream_create(stream, content, pos))
+ {
+ clean_vmpa_arg(pos);
+
+ PyErr_SetString(PyExc_ValueError, _("Unable to create Kaitai stream."));
+ return -1;
+
+ }
+
+ clean_vmpa_arg(pos);
+
+ return 0;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : self = objet Python concerné par l'appel. *
+* closure = non utilisé ici. *
+* *
+* Description : Détermine si la fin des données a été atteinte. *
+* *
+* Retour : True si la tête de lecture est en position finale, ou False. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static PyObject *py_kaitai_stream_get_eof(PyObject *self, void *closure)
+{
+ PyObject *result; /* Valeur à retourner */
+ GKaitaiStream *stream; /* Version native dyu flux */
+ bool status; /* Etat de la position courante*/
+
+#define KAITAI_STREAM_EOF_ATTRIB PYTHON_GET_DEF_FULL \
+( \
+ eof, py_kaitai_stream, \
+ "Boolean value stating if the end of the stream" \
+ " has been reached or not." \
+)
+
+ stream = G_KAITAI_STREAM(pygobject_get(self));
+
+ status = g_kaitai_stream_has_reached_eof(stream);
+
+ result = status ? Py_True : Py_False;
+ Py_INCREF(result);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Fournit un accès à une définition de type à diffuser. *
+* *
+* Retour : Définition d'objet pour Python. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+PyTypeObject *get_python_kaitai_stream_type(void)
+{
+ static PyMethodDef py_kaitai_stream_methods[] = {
+ { NULL }
+ };
+
+ static PyGetSetDef py_kaitai_stream_getseters[] = {
+ KAITAI_STREAM_EOF_ATTRIB,
+ { NULL }
+ };
+
+ static PyTypeObject py_kaitai_stream_type = {
+
+ PyVarObject_HEAD_INIT(NULL, 0)
+
+ .tp_name = "pychrysalide.plugins.kaitai.KaitaiStream",
+ .tp_basicsize = sizeof(PyGObject),
+
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+
+ .tp_doc = KAITAI_STREAM_DOC,
+
+ .tp_methods = py_kaitai_stream_methods,
+ .tp_getset = py_kaitai_stream_getseters,
+
+ .tp_init = py_kaitai_stream_init,
+ .tp_new = py_kaitai_stream_new,
+
+ };
+
+ return &py_kaitai_stream_type;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : - *
+* *
+* Description : Prend en charge l'objet 'pychrysalide.plugins...KaitaiStream.*
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool ensure_python_kaitai_stream_is_registered(void)
+{
+ PyTypeObject *type; /* Type Python 'KaitaiStream' */
+ PyObject *module; /* Module à recompléter */
+ PyObject *dict; /* Dictionnaire du module */
+
+ type = get_python_kaitai_stream_type();
+
+ if (!PyType_HasFeature(type, Py_TPFLAGS_READY))
+ {
+ module = get_access_to_python_module("pychrysalide.plugins.kaitai");
+
+ dict = PyModule_GetDict(module);
+
+ if (!register_class_for_pygobject(dict, G_TYPE_KAITAI_STREAM, type))
+ return false;
+
+ }
+
+ return true;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : arg = argument quelconque à tenter de convertir. *
+* dst = destination des valeurs récupérées en cas de succès. *
+* *
+* Description : Tente de convertir en flux de données pour Kaitai. *
+* *
+* Retour : Bilan de l'opération, voire indications supplémentaires. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+int convert_to_kaitai_stream(PyObject *arg, void *dst)
+{
+ int result; /* Bilan à retourner */
+
+ result = PyObject_IsInstance(arg, (PyObject *)get_python_kaitai_stream_type());
+
+ switch (result)
+ {
+ case -1:
+ /* L'exception est déjà fixée par Python */
+ result = 0;
+ break;
+
+ case 0:
+ PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to Kaitai stream");
+ break;
+
+ case 1:
+ *((GKaitaiStream **)dst) = G_KAITAI_STREAM(pygobject_get(arg));
+ break;
+
+ default:
+ assert(false);
+ break;
+
+ }
+
+ return result;
+
+}
diff --git a/plugins/kaitai/python/stream.h b/plugins/kaitai/python/stream.h
new file mode 100644
index 0000000..4f61358
--- /dev/null
+++ b/plugins/kaitai/python/stream.h
@@ -0,0 +1,45 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * stream.h - prototypes pour l'équivalent Python du fichier "plugins/kaitai/stream.h"
+ *
+ * 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef _PLUGINS_KAITAI_PYTHON_STREAM_H
+#define _PLUGINS_KAITAI_PYTHON_STREAM_H
+
+
+#include <Python.h>
+#include <stdbool.h>
+
+
+
+/* Fournit un accès à une définition de type à diffuser. */
+PyTypeObject *get_python_kaitai_stream_type(void);
+
+/* Prend en charge l'objet 'pychrysalide.plugins.kaitai.KaitaiStream'. */
+bool ensure_python_kaitai_stream_is_registered(void);
+
+/* Tente de convertir en flux de données pour Kaitai. */
+int convert_to_kaitai_stream(PyObject *, void *);
+
+
+
+#endif /* _PLUGINS_KAITAI_PYTHON_STREAM_H */
diff --git a/plugins/kaitai/record-int.h b/plugins/kaitai/record-int.h
new file mode 100644
index 0000000..5ce5b2c
--- /dev/null
+++ b/plugins/kaitai/record-int.h
@@ -0,0 +1,73 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * record-int.h - prototypes internes pour la mémorisation d'une correspondance avec un attribut 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/>.
+ */
+
+
+#ifndef PLUGINS_KAITAI_RECORD_INT_H
+#define PLUGINS_KAITAI_RECORD_INT_H
+
+
+#include "record.h"
+
+
+#include "parser.h"
+
+
+
+/* Calcule ou fournit la zone couverte par une correspondance. */
+typedef void (* get_record_range_fc) (const GMatchRecord *, mrange_t *);
+
+/* Recherche la correspondance associée à un identifiant. */
+typedef GMatchRecord * (* find_record_by_name_fc) (GMatchRecord *, const char *, size_t, unsigned int);
+
+/* Transforme une énumération en constante entière. */
+typedef bool (* resolve_record_enum_fc) (const GMatchRecord *, const sized_string_t *, const sized_string_t *, resolved_value_t *);
+
+
+/* Correspondance entre un attribut et du binaire (instance) */
+struct _GMatchRecord
+{
+ GObject parent; /* A laisser en premier */
+
+ GKaitaiParser *creator; /* Lecteur à l'origine */
+
+ GBinContent *content; /* Contenu binaire analysé */
+
+};
+
+/* Correspondance entre un attribut et du binaire (classe) */
+struct _GMatchRecordClass
+{
+ GObjectClass parent; /* A laisser en premier */
+
+ get_record_range_fc get_range; /* Fourniture de couverture */
+ find_record_by_name_fc find; /* Recherche selon identifiant */
+ resolve_record_enum_fc resolve; /* Conversion d'une énumération*/
+
+};
+
+
+/* Met en place une correspondance entre attribut et binaire. */
+bool g_match_record_create(GMatchRecord *, GKaitaiParser *, GBinContent *);
+
+
+
+#endif /* PLUGINS_KAITAI_RECORD_INT_H */
diff --git a/plugins/kaitai/record.c b/plugins/kaitai/record.c
new file mode 100644
index 0000000..5717b17
--- /dev/null
+++ b/plugins/kaitai/record.c
@@ -0,0 +1,408 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * record.c - définition d'une correspondance avec un attribut 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 "record.h"
+
+
+#include <assert.h>
+
+
+#include "expression.h"
+#include "record-int.h"
+#include "parsers/attribute.h"
+
+
+
+/* Initialise la classe des correspondances avec du binaire. */
+static void g_match_record_class_init(GMatchRecordClass *);
+
+/* Initialise une correspondance avec du binaire. */
+static void g_match_record_init(GMatchRecord *);
+
+/* Supprime toutes les références externes. */
+static void g_match_record_dispose(GMatchRecord *);
+
+/* Procède à la libération totale de la mémoire. */
+static void g_match_record_finalize(GMatchRecord *);
+
+/* Recherche la correspondance associée à un identifiant. */
+static GMatchRecord *_g_match_record_find_by_name(GMatchRecord *, const char *, size_t, unsigned int);
+
+
+
+/* Indique le type défini pour une correspondance avec du binaire. */
+G_DEFINE_TYPE(GMatchRecord, g_match_record, G_TYPE_OBJECT);
+
+
+/******************************************************************************
+* *
+* Paramètres : klass = classe à initialiser. *
+* *
+* Description : Initialise la classe des correspondances avec du binaire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_match_record_class_init(GMatchRecordClass *klass)
+{
+ GObjectClass *object; /* Autre version de la classe */
+
+ object = G_OBJECT_CLASS(klass);
+
+ object->dispose = (GObjectFinalizeFunc/* ! */)g_match_record_dispose;
+ object->finalize = (GObjectFinalizeFunc)g_match_record_finalize;
+
+ klass->find = (find_record_by_name_fc)_g_match_record_find_by_name;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : record = instance à initialiser. *
+* *
+* Description : Initialise une correspondance avec du binaire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_match_record_init(GMatchRecord *record)
+{
+ record->creator = NULL;
+
+ record->content = NULL;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : record = instance d'objet GLib à traiter. *
+* *
+* Description : Supprime toutes les références externes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_match_record_dispose(GMatchRecord *record)
+{
+ G_OBJECT_CLASS(g_match_record_parent_class)->dispose(G_OBJECT(record));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : record = instance d'objet GLib à traiter. *
+* *
+* Description : Procède à la libération totale de la mémoire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_match_record_finalize(GMatchRecord *record)
+{
+ G_OBJECT_CLASS(g_match_record_parent_class)->finalize(G_OBJECT(record));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : record = correspondance à initialiser pleinement. *
+* creator = lecteur à l'origine de la correspondance. *
+* content = contenu binaire lié à la correspondance. *
+* *
+* Description : Met en place une correspondance entre attribut et binaire. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool g_match_record_create(GMatchRecord *record, GKaitaiParser *creator, GBinContent *content)
+{
+ bool result; /* Bilan à retourner */
+
+ result = true;
+
+ record->creator = creator;
+ g_object_ref(G_OBJECT(creator));
+
+ record->content = content;
+
+ if (content != NULL)
+ g_object_ref(G_OBJECT(content));
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : record = correspondance à consulter. *
+* *
+* Description : Renvoie vers le lecteur à l'origine de la correspondance. *
+* *
+* Retour : Lecteur à l'origine de la création. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+GKaitaiParser *g_match_record_get_creator(const GMatchRecord *record)
+{
+ GKaitaiParser *result; /* Instance à retourner */
+
+ result = record->creator;
+
+ g_object_ref(G_OBJECT(result));
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : record = correspondance à modifier. *
+* creator = lecteur à l'origine de la correspondance. *
+* *
+* Description : Modifie la référence au créateur de la correspondance. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void g_match_record_fix_creator(GMatchRecord *record, GKaitaiParser *creator)
+{
+ g_object_unref(G_OBJECT(record->creator));
+
+ record->creator = creator;
+ g_object_ref(G_OBJECT(creator));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : record = correspondance à consulter. *
+* *
+* Description : Fournit le contenu lié à une correspondance établie. *
+* *
+* Retour : Contenu binaire associé. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+GBinContent *g_match_record_get_content(const GMatchRecord *record)
+{
+ GBinContent *result; /* Instance à retourner */
+
+ result = record->content;
+
+ if (result != NULL)
+ g_object_ref(G_OBJECT(result));
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : record = correspondance à consulter. *
+* range = zone de couverture déterminée. [OUT] *
+* *
+* Description : Calcule ou fournit la zone couverte par une correspondance. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void g_match_record_get_range(const GMatchRecord *record, mrange_t *range)
+{
+ GMatchRecordClass *class; /* Classe de l'instance */
+
+ class = G_MATCH_RECORD_GET_CLASS(record);
+
+ class->get_range(record, range);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : record = correspondance établie à consulter. *
+* out = tableau d'octets retournés. [OUT] *
+* len = taille de ce tableau alloué. [OUT] *
+* *
+* Description : Lit les octets bruts couverts par une correspondance. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void g_match_record_read_raw_bytes(const GMatchRecord *record, bin_t **out, size_t *len)
+{
+ mrange_t range; /* Zone de correspondance */
+ const bin_t *data; /* Accès aux données brutes */
+
+ g_match_record_get_range(record, &range);
+
+ *len = get_mrange_length(&range);
+
+ data = g_binary_content_get_raw_access(record->content, get_mrange_addr(&range), *len);
+ assert(data != NULL);
+
+ *out = malloc(sizeof(bin_t) * (*len + 1));
+
+ memcpy(*out, data, *len);
+ (*out)[*len] = '\0';
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : record = correspondance à consulter. *
+* name = désignation de l'élément recherché. *
+* len = taille de cette désignation. *
+* level = profondeur maximale à atteindre (fond : 0). *
+* *
+* Description : Recherche la correspondance associée à un identifiant. *
+* *
+* Retour : Correspondance trouvée ou NULL. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static GMatchRecord *_g_match_record_find_by_name(GMatchRecord *record, const char *name, size_t len, unsigned int level)
+{
+ GMatchRecord *result; /* Trouvaille à retourner */
+ const char *label; /* Etiquette à manipuler */
+ size_t label_len; /* Taille de cette étiquette */
+
+ result = NULL;
+
+ if (G_IS_KAITAI_ATTRIBUTE(record->creator))
+ {
+ label = g_kaitai_attribute_get_label(G_KAITAI_ATTRIBUTE(record->creator));
+
+ label_len = strlen(label);
+
+ if (label_len == len && strncmp(label, name, len) == 0)
+ {
+ result = record;
+ g_object_ref(G_OBJECT(result));
+ }
+
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : record = correspondance à consulter. *
+* name = désignation de l'élément recherché. *
+* len = taille de cette désignation. *
+* level = profondeur maximale à atteindre (fond : 0). *
+* *
+* Description : Recherche la correspondance associée à un identifiant. *
+* *
+* Retour : Correspondance trouvée ou NULL. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+GMatchRecord *g_match_record_find_by_name(GMatchRecord *record, const char *name, size_t len, unsigned int level)
+{
+ GMatchRecord *result; /* Trouvaille à retourner */
+ GMatchRecordClass *class; /* Classe de l'instance */
+
+ class = G_MATCH_RECORD_GET_CLASS(record);
+
+ result = class->find(record, name, len, level);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : record = correspondance à consulter. *
+* name = désignation de l'élément recherché. *
+* label = étiquette de l'élément constant à traduire. *
+* value = valeur entière correspondante. [OUT] *
+* *
+* Description : Transforme une énumération en constante entière. *
+* *
+* Retour : Bilan de l'opération : true si la résolution est réalisée. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool g_match_record_resolve_enum(const GMatchRecord *record, const sized_string_t *name, const sized_string_t *label, resolved_value_t *value)
+{
+ bool result; /* Bilan à retourner */
+ GMatchRecordClass *class; /* Classe de l'instance */
+
+ class = G_MATCH_RECORD_GET_CLASS(record);
+
+ if (class->resolve == NULL)
+ result = false;
+
+ else
+ result = class->resolve(record, name, label, value);
+
+ return result;
+
+}
diff --git a/plugins/kaitai/record.h b/plugins/kaitai/record.h
new file mode 100644
index 0000000..7db8187
--- /dev/null
+++ b/plugins/kaitai/record.h
@@ -0,0 +1,88 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * record.h - prototypes pour la définition d'une correspondance avec un attribut 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/>.
+ */
+
+
+#ifndef _PLUGINS_KAITAI_RECORD_H
+#define _PLUGINS_KAITAI_RECORD_H
+
+
+#include <glib-object.h>
+
+
+#include <analysis/content.h>
+#include <arch/vmpa.h>
+#include <common/szstr.h>
+
+
+
+/* Depuis parser.h : spécification d'un lecteur Kaitai (instance) */
+typedef struct _GKaitaiParser GKaitaiParser;
+
+/* Depuis expression.h : informations transportées par une expression */
+typedef struct _resolved_value_t resolved_value_t;
+
+
+
+#define G_TYPE_MATCH_RECORD g_match_record_get_type()
+#define G_MATCH_RECORD(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_MATCH_RECORD, GMatchRecord))
+#define G_IS_MATCH_RECORD(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_MATCH_RECORD))
+#define G_MATCH_RECORD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_MATCH_RECORD, GMatchRecordClass))
+#define G_IS_MATCH_RECORD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_MATCH_RECORD))
+#define G_MATCH_RECORD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_MATCH_RECORD, GMatchRecordClass))
+
+
+/* Correspondance entre un attribut et du binaire (instance) */
+typedef struct _GMatchRecord GMatchRecord;
+
+/* Correspondance entre un attribut et du binaire (classe) */
+typedef struct _GMatchRecordClass GMatchRecordClass;
+
+
+/* Indique le type défini pour une correspondance avec du binaire. */
+GType g_match_record_get_type(void);
+
+/* Renvoie vers le lecteur à l'origine de la correspondance. */
+GKaitaiParser *g_match_record_get_creator(const GMatchRecord *);
+
+/* Modifie la référence au créateur de la correspondance. */
+void g_match_record_fix_creator(GMatchRecord *, GKaitaiParser *);
+
+/* Fournit le contenu lié à une correspondance établie. */
+GBinContent *g_match_record_get_content(const GMatchRecord *);
+
+/* Calcule ou fournit la zone couverte par une correspondance. */
+void g_match_record_get_range(const GMatchRecord *, mrange_t *);
+
+/* Lit les octets bruts couverts par une correspondance. */
+void g_match_record_read_raw_bytes(const GMatchRecord *, bin_t **, size_t *);
+
+#define DIRECT_SEARCH_DEEP_LEVEL 1
+
+/* Recherche la correspondance associée à un identifiant. */
+GMatchRecord *g_match_record_find_by_name(GMatchRecord *, const char *, size_t, unsigned int);
+
+/* Transforme une énumération en constante entière. */
+bool g_match_record_resolve_enum(const GMatchRecord *, const sized_string_t *, const sized_string_t *, resolved_value_t *);
+
+
+
+#endif /* _PLUGINS_KAITAI_RECORD_H */
diff --git a/plugins/kaitai/records/Makefile.am b/plugins/kaitai/records/Makefile.am
new file mode 100644
index 0000000..c11e823
--- /dev/null
+++ b/plugins/kaitai/records/Makefile.am
@@ -0,0 +1,21 @@
+
+noinst_LTLIBRARIES = libkaitairecords.la
+
+libkaitairecords_la_SOURCES = \
+ empty-int.h \
+ empty.h empty.c \
+ group-int.h \
+ group.h group.c \
+ item-int.h \
+ item.h item.c \
+ list-int.h \
+ list.h list.c \
+ value-int.h \
+ value.h value.c
+
+libkaitairecords_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) -I$(top_srcdir)/src
+
+
+devdir = $(includedir)/chrysalide-$(subdir)
+
+dev_HEADERS = $(libkaitairecords_la_SOURCES:%c=)
diff --git a/plugins/kaitai/records/empty-int.h b/plugins/kaitai/records/empty-int.h
new file mode 100644
index 0000000..2c6cefa
--- /dev/null
+++ b/plugins/kaitai/records/empty-int.h
@@ -0,0 +1,57 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * empty-int.h - prototypes internes pour la notification d'une absence de correspondance attendue
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _PLUGINS_KAITAI_RECORDS_EMPTY_INT_H
+#define _PLUGINS_KAITAI_RECORDS_EMPTY_INT_H
+
+
+#include "empty.h"
+
+
+#include "../record-int.h"
+
+
+
+/* Marque d'une zone de correspondance vide (instance) */
+struct _GRecordEmpty
+{
+ GMatchRecord parent; /* A laisser en premier */
+
+ vmpa2t pos; /* Début d'une zone vide */
+
+};
+
+/* Marque d'une zone de correspondance vide (classe) */
+struct _GRecordEmptyClass
+{
+ GMatchRecordClass parent; /* A laisser en premier */
+
+};
+
+
+/* Met en place une zone de correspondance vide. */
+bool g_record_empty_create(GRecordEmpty *, GKaitaiParser *, GBinContent *, const vmpa2t *);
+
+
+
+#endif /* _PLUGINS_KAITAI_RECORDS_EMPTY_INT_H */
diff --git a/plugins/kaitai/records/empty.c b/plugins/kaitai/records/empty.c
new file mode 100644
index 0000000..e5121e1
--- /dev/null
+++ b/plugins/kaitai/records/empty.c
@@ -0,0 +1,236 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * empty.c - conservation d'une correspondance entre attribut et binaire
+ *
+ * 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 "empty.h"
+
+
+#include <assert.h>
+#include <stdarg.h>
+#include <string.h>
+
+
+#include "empty-int.h"
+
+
+
+/* ------------------ DEFINITION D'UNE ZONE DE CORRESPONDANCE VIDE ------------------ */
+
+
+/* Initialise la classe des zones de correspondance vides. */
+static void g_record_empty_class_init(GRecordEmptyClass *);
+
+/* Initialise une zone de correspondance vide. */
+static void g_record_empty_init(GRecordEmpty *);
+
+/* Supprime toutes les références externes. */
+static void g_record_empty_dispose(GRecordEmpty *);
+
+/* Procède à la libération totale de la mémoire. */
+static void g_record_empty_finalize(GRecordEmpty *);
+
+
+
+/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */
+
+
+/* Calcule ou fournit la zone couverte par une correspondance. */
+static void g_record_empty_get_range(const GRecordEmpty *, mrange_t *);
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* DEFINITION D'UNE ZONE DE CORRESPONDANCE VIDE */
+/* ---------------------------------------------------------------------------------- */
+
+
+/* Indique le type défini pour une zone de correspondance vide. */
+G_DEFINE_TYPE(GRecordEmpty, g_record_empty, G_TYPE_MATCH_RECORD);
+
+
+/******************************************************************************
+* *
+* Paramètres : klass = classe à initialiser. *
+* *
+* Description : Initialise la classe des zones de correspondance vides. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_record_empty_class_init(GRecordEmptyClass *klass)
+{
+ GObjectClass *object; /* Autre version de la classe */
+ GMatchRecordClass *record; /* Version parente de la classe*/
+
+ object = G_OBJECT_CLASS(klass);
+
+ object->dispose = (GObjectFinalizeFunc/* ! */)g_record_empty_dispose;
+ object->finalize = (GObjectFinalizeFunc)g_record_empty_finalize;
+
+ record = G_MATCH_RECORD_CLASS(klass);
+
+ record->get_range = (get_record_range_fc)g_record_empty_get_range;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : empty = instance à initialiser. *
+* *
+* Description : Initialise une zone de correspondance vide. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_record_empty_init(GRecordEmpty *empty)
+{
+ init_vmpa(&empty->pos, VMPA_NO_PHYSICAL, VMPA_NO_VIRTUAL);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : empty = instance d'objet GLib à traiter. *
+* *
+* Description : Supprime toutes les références externes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_record_empty_dispose(GRecordEmpty *empty)
+{
+ G_OBJECT_CLASS(g_record_empty_parent_class)->dispose(G_OBJECT(empty));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : empty = instance d'objet GLib à traiter. *
+* *
+* Description : Procède à la libération totale de la mémoire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_record_empty_finalize(GRecordEmpty *empty)
+{
+ G_OBJECT_CLASS(g_record_empty_parent_class)->finalize(G_OBJECT(empty));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : parser = analyseur à l'origine de la correspondance. *
+* content = contenu binaire lié à la correspondance. *
+* pos = emplacement de la zone vide à construire. *
+* *
+* Description : Crée une zone de correspondance vide à une position donnée. *
+* *
+* Retour : Instance mise en place ou NULL en cas d'échec. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+GRecordEmpty *g_record_empty_new(GKaitaiParser *parser, GBinContent *content, const vmpa2t *pos)
+{
+ GRecordEmpty *result; /* Structure à retourner */
+
+ result = g_object_new(G_TYPE_RECORD_EMPTY, NULL);
+
+ if (!g_record_empty_create(result, parser, content, pos))
+ g_clear_object(&result);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : empty = correspondance à initialiser pleinement. *
+* parser = analyseur à l'origine de la correspondance. *
+* content = contenu binaire lié à la correspondance. *
+* pos = emplacement de la zone vide à construire. *
+* *
+* Description : Met en place une zone de correspondance vide. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool g_record_empty_create(GRecordEmpty *empty, GKaitaiParser *parser, GBinContent *content, const vmpa2t *pos)
+{
+ bool result; /* Bilan à retourner */
+
+ result = g_match_record_create(G_MATCH_RECORD(empty), parser, content);
+
+ if (result)
+ copy_vmpa(&empty->pos, pos);
+
+ return result;
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* IMPLEMENTATION DES FONCTIONS DE CLASSE */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : empty = correspondance à consulter. *
+* range = zone de couverture déterminée. [OUT] *
+* *
+* Description : Calcule ou fournit la zone couverte par une correspondance. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_record_empty_get_range(const GRecordEmpty *empty, mrange_t *range)
+{
+ init_mrange(range, &empty->pos, 0);
+
+}
diff --git a/plugins/kaitai/records/empty.h b/plugins/kaitai/records/empty.h
new file mode 100644
index 0000000..4e89b62
--- /dev/null
+++ b/plugins/kaitai/records/empty.h
@@ -0,0 +1,58 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * empty.h - prototypes pour la notification d'une absence de correspondance attendue
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _PLUGINS_KAITAI_RECORDS_EMPTY_H
+#define _PLUGINS_KAITAI_RECORDS_EMPTY_H
+
+
+#include <glib-object.h>
+
+
+#include "../record.h"
+
+
+
+#define G_TYPE_RECORD_EMPTY g_record_empty_get_type()
+#define G_RECORD_EMPTY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_RECORD_EMPTY, GRecordEmpty))
+#define G_IS_RECORD_EMPTY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_RECORD_EMPTY))
+#define G_RECORD_EMPTY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_RECORD_EMPTY, GRecordEmptyClass))
+#define G_IS_RECORD_EMPTY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_RECORD_EMPTY))
+#define G_RECORD_EMPTY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_RECORD_EMPTY, GRecordEmptyClass))
+
+
+/* Marque d'une zone de correspondance vide (instance) */
+typedef struct _GRecordEmpty GRecordEmpty;
+
+/* Marque d'une zone de correspondance vide (classe) */
+typedef struct _GRecordEmptyClass GRecordEmptyClass;
+
+
+/* Indique le type défini pour une zone de correspondance vide. */
+GType g_record_empty_get_type(void);
+
+/* Crée une zone de correspondance vide à une position donnée. */
+GRecordEmpty *g_record_empty_new(GKaitaiParser *, GBinContent *, const vmpa2t *);
+
+
+
+#endif /* _PLUGINS_KAITAI_RECORDS_EMPTY_H */
diff --git a/plugins/kaitai/records/group-int.h b/plugins/kaitai/records/group-int.h
new file mode 100644
index 0000000..1fd0162
--- /dev/null
+++ b/plugins/kaitai/records/group-int.h
@@ -0,0 +1,58 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * group-int.h - prototypes internes pour la conservation d'un groupe de correspondance avec du binaire
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _PLUGINS_KAITAI_RECORDS_GROUP_INT_H
+#define _PLUGINS_KAITAI_RECORDS_GROUP_INT_H
+
+
+#include "group.h"
+
+
+#include "../record-int.h"
+
+
+
+/* Groupe de correspondances établies entre attributs et binaire (instance) */
+struct _GRecordGroup
+{
+ GMatchRecord parent; /* A laisser en premier */
+
+ GMatchRecord **children; /* Sous-correspondances */
+ size_t count; /* Taille de cette série */
+
+};
+
+/* Groupe de correspondances établies entre attributs et binaire (classe) */
+struct _GRecordGroupClass
+{
+ GMatchRecordClass parent; /* A laisser en premier */
+
+};
+
+
+/* Met en place une série de correspondances attribut/binaire. */
+bool g_record_group_create(GRecordGroup *, GKaitaiStruct *, GBinContent *);
+
+
+
+#endif /* _PLUGINS_KAITAI_RECORDS_GROUP_INT_H */
diff --git a/plugins/kaitai/records/group.c b/plugins/kaitai/records/group.c
new file mode 100644
index 0000000..13327c8
--- /dev/null
+++ b/plugins/kaitai/records/group.c
@@ -0,0 +1,382 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * group.c - conservation d'un groupe de correspondance avec du binaire
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "group.h"
+
+
+#include <malloc.h>
+
+
+#include "group-int.h"
+#include "../parsers/attribute.h"
+
+
+
+/* ------------------ DEFINITION D'UNE SEQUENCE DE CORRESPONDANCES ------------------ */
+
+
+/* Initialise la classe des groupes de correspondances. */
+static void g_record_group_class_init(GRecordGroupClass *);
+
+/* Initialise une série de correspondances attributs/binaire. */
+static void g_record_group_init(GRecordGroup *);
+
+/* Supprime toutes les références externes. */
+static void g_record_group_dispose(GRecordGroup *);
+
+/* Procède à la libération totale de la mémoire. */
+static void g_record_group_finalize(GRecordGroup *);
+
+
+
+/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */
+
+
+/* Calcule ou fournit la zone couverte par une correspondance. */
+static void g_record_group_get_range(const GRecordGroup *, mrange_t *);
+
+/* Recherche la correspondance associée à un identifiant. */
+static GMatchRecord *g_record_group_find_by_name(GRecordGroup *, const char *, size_t, unsigned int);
+
+/* Transforme une énumération en constante entière. */
+static bool g_record_group_resolve_enum(const GRecordGroup *, const sized_string_t *, const sized_string_t *, resolved_value_t *);
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* DEFINITION D'UNE SEQUENCE DE CORRESPONDANCES */
+/* ---------------------------------------------------------------------------------- */
+
+
+/* Indique le type défini pour une série de correspondances entre attributes et binaire. */
+G_DEFINE_TYPE(GRecordGroup, g_record_group, G_TYPE_MATCH_RECORD);
+
+
+/******************************************************************************
+* *
+* Paramètres : klass = classe à initialiser. *
+* *
+* Description : Initialise la classe des groupes de correspondances. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_record_group_class_init(GRecordGroupClass *klass)
+{
+ GObjectClass *object; /* Autre version de la classe */
+ GMatchRecordClass *record; /* Version parente de la classe*/
+
+ object = G_OBJECT_CLASS(klass);
+
+ object->dispose = (GObjectFinalizeFunc/* ! */)g_record_group_dispose;
+ object->finalize = (GObjectFinalizeFunc)g_record_group_finalize;
+
+ record = G_MATCH_RECORD_CLASS(klass);
+
+ record->get_range = (get_record_range_fc)g_record_group_get_range;
+ record->find = (find_record_by_name_fc)g_record_group_find_by_name;
+ record->resolve = (resolve_record_enum_fc)g_record_group_resolve_enum;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : group = instance à initialiser. *
+* *
+* Description : Initialise une série de correspondances attributs/binaire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_record_group_init(GRecordGroup *group)
+{
+ group->children = NULL;
+ group->count = 0;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : group = instance d'objet GLib à traiter. *
+* *
+* Description : Supprime toutes les références externes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_record_group_dispose(GRecordGroup *group)
+{
+ size_t i; /* Boucle de parcours */
+
+ for (i = 0; i < group->count; i++)
+ g_clear_object(&group->children[i]);
+
+ G_OBJECT_CLASS(g_record_group_parent_class)->dispose(G_OBJECT(group));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : group = instance d'objet GLib à traiter. *
+* *
+* Description : Procède à la libération totale de la mémoire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_record_group_finalize(GRecordGroup *group)
+{
+ if (group->children != NULL)
+ free(group->children);
+
+ G_OBJECT_CLASS(g_record_group_parent_class)->finalize(G_OBJECT(group));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : content = contenu binaire lié à la correspondance. *
+* kstruct = analyseur à l'origine de la correspondance. *
+* *
+* Description : Crée une nouvelle série de correspondances attribut/binaire. *
+* *
+* Retour : Instance mise en place ou NULL en cas d'échec. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+GRecordGroup *g_record_group_new(GKaitaiStruct *kstruct, GBinContent *content)
+{
+ GRecordGroup *result; /* Structure à retourner */
+
+ result = g_object_new(G_TYPE_RECORD_GROUP, NULL);
+
+ if (!g_record_group_create(result, kstruct, content))
+ g_clear_object(&result);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : group = correspondance à initialiser pleinement. *
+* kstruct = analyseur à l'origine de la correspondance. *
+* content = contenu binaire lié à la correspondance. *
+* *
+* Description : Met en place une série de correspondances attribut/binaire. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool g_record_group_create(GRecordGroup *group, GKaitaiStruct *kstruct, GBinContent *content)
+{
+ bool result; /* Bilan à retourner */
+
+ result = g_match_record_create(G_MATCH_RECORD(group), G_KAITAI_PARSER(kstruct), content);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : group = ensemble de correspondances attribut/binaire. *
+* record = sous-corresponde à intégrer. *
+* *
+* Description : Ajoute une correspondance supplémentaire à une série. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void g_record_group_add_record(GRecordGroup *group, GMatchRecord *record)
+{
+ group->children = realloc(group->children, ++group->count * sizeof(GMatchRecord));
+
+ group->children[group->count - 1] = record;
+ g_object_ref(G_OBJECT(record));
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* IMPLEMENTATION DES FONCTIONS DE CLASSE */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : group = correspondance à consulter. *
+* range = zone de couverture déterminée. [OUT] *
+* *
+* Description : Calcule ou fournit la zone couverte par une correspondance. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_record_group_get_range(const GRecordGroup *group, mrange_t *range)
+{
+ vmpa2t start; /* Position de départ */
+ mrange_t range_0; /* Première zone couverte */
+ mrange_t range_n; /* Dernière zone couverte */
+ vmpa2t end; /* Position de d'arrivée */
+ phys_t length; /* Taille de zone couverte */
+
+ if (group->count == 0)
+ {
+ init_vmpa(&start, VMPA_NO_PHYSICAL, VMPA_NO_VIRTUAL);
+ init_mrange(range, &start, VMPA_NO_PHYSICAL);
+ }
+
+ else
+ {
+ g_match_record_get_range(group->children[0], &range_0);
+ g_match_record_get_range(group->children[group->count - 1], &range_n);
+
+ copy_vmpa(&start, get_mrange_addr(&range_0));
+
+ compute_mrange_end_addr(&range_n, &end);
+ length = compute_vmpa_diff(&start, &end);
+
+ init_mrange(range, &start, length);
+
+ }
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : group = ensemble de correspondances attribut/binaire. *
+* name = désignation de l'élément recherché. *
+* len = taille de cette désignation. *
+* level = profondeur maximale à atteindre (fond : 0). *
+* *
+* Description : Recherche la correspondance associée à un identifiant. *
+* *
+* Retour : Correspondance trouvée ou NULL. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static GMatchRecord *g_record_group_find_by_name(GRecordGroup *group, const char *name, size_t len, unsigned int level)
+{
+ GMatchRecord *result; /* Correspondance à renvoyer */
+ GMatchRecordClass *class; /* Classe parente normalisée */
+ size_t i; /* Boucle de parcours */
+
+ class = G_MATCH_RECORD_CLASS(g_record_group_parent_class);
+
+ /**
+ * Le cas d'un type utilisateur peut rattacher un attribut Kaitai à un groupe...
+ */
+ result = class->find(G_MATCH_RECORD(group), name, len, level);
+
+ if (level > 0)
+ {
+ level--;
+
+ for (i = 0; i < group->count && result == NULL; i++)
+ result = g_match_record_find_by_name(group->children[i], name, len, level);
+
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : group = ensemble de correspondances attribut/binaire. *
+* name = désignation de l'élément recherché. *
+* label = étiquette de l'élément constant à traduire. *
+* value = valeur entière correspondante. [OUT] *
+* *
+* Description : Transforme une énumération en constante entière. *
+* *
+* Retour : Bilan de l'opération : true si la résolution est réalisée. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool g_record_group_resolve_enum(const GRecordGroup *group, const sized_string_t *name, const sized_string_t *label, resolved_value_t *value)
+{
+ bool result; /* Bilan à retourner */
+ GMatchRecord *base; /* Autre version du groupe */
+ size_t i; /* Boucle de parcours */
+ GKaitaiEnum *kenum; /* Enumération à consulter */
+
+ result = false;
+
+ base = G_MATCH_RECORD(group);
+
+ if (G_IS_KAITAI_STRUCT(base->creator))
+ {
+ kenum = g_kaitai_structure_get_enum(G_KAITAI_STRUCT(base->creator), name);
+
+ if (kenum != NULL)
+ {
+ result = g_kaitai_enum_find_value(kenum, label, value);
+ g_object_unref(G_OBJECT(kenum));
+ }
+
+ }
+
+ for (i = 0; i < group->count && !result; i++)
+ result = g_match_record_resolve_enum(group->children[i], name, label, value);
+
+ return result;
+
+}
diff --git a/plugins/kaitai/records/group.h b/plugins/kaitai/records/group.h
new file mode 100644
index 0000000..454dade
--- /dev/null
+++ b/plugins/kaitai/records/group.h
@@ -0,0 +1,65 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * group.h - prototypes pour la conservation d'un groupe de correspondance avec du binaire
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _PLUGINS_KAITAI_RECORDS_GROUP_H
+#define _PLUGINS_KAITAI_RECORDS_GROUP_H
+
+
+#include <glib-object.h>
+
+
+#include <analysis/content.h>
+
+
+#include "../record.h"
+#include "../parsers/struct.h"
+
+
+
+#define G_TYPE_RECORD_GROUP g_record_group_get_type()
+#define G_RECORD_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_RECORD_GROUP, GRecordGroup))
+#define G_IS_RECORD_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_RECORD_GROUP))
+#define G_RECORD_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_RECORD_GROUP, GRecordGroupClass))
+#define G_IS_RECORD_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_RECORD_GROUP))
+#define G_RECORD_GROUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_RECORD_GROUP, GRecordGroupClass))
+
+
+/* Groupe de correspondances établies entre attributs et binaire (instance) */
+typedef struct _GRecordGroup GRecordGroup;
+
+/* Groupe de correspondances établies entre attributs et binaire (classe) */
+typedef struct _GRecordGroupClass GRecordGroupClass;
+
+
+/* Indique le type défini pour une série de correspondances entre attributes et binaire. */
+GType g_record_group_get_type(void);
+
+/* Crée une nouvelle série de correspondances attribut/binaire. */
+GRecordGroup *g_record_group_new(GKaitaiStruct *, GBinContent *);
+
+/* Ajoute une correspondance supplémentaire à une série. */
+void g_record_group_add_record(GRecordGroup *, GMatchRecord *);
+
+
+
+#endif /* _PLUGINS_KAITAI_RECORDS_GROUP_H */
diff --git a/plugins/kaitai/records/item-int.h b/plugins/kaitai/records/item-int.h
new file mode 100644
index 0000000..56e0a41
--- /dev/null
+++ b/plugins/kaitai/records/item-int.h
@@ -0,0 +1,58 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * item-int.h - prototypes internes pour la conservation d'une correspondance entre attribut et binaire
+ *
+ * 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/>.
+ */
+
+
+#ifndef _PLUGINS_KAITAI_RECORDS_ITEM_INT_H
+#define _PLUGINS_KAITAI_RECORDS_ITEM_INT_H
+
+
+#include "item.h"
+
+
+#include "../record-int.h"
+
+
+
+/* Correspondance établie entre un attribut et du binaire (instance) */
+struct _GRecordItem
+{
+ GMatchRecord parent; /* A laisser en premier */
+
+ mrange_t range; /* Zone de binaire couverte */
+ SourceEndian endian; /* Boutisme des données imposé */
+
+};
+
+/* Correspondance établie entre un attribut et du binaire (classe) */
+struct _GRecordItemClass
+{
+ GMatchRecordClass parent; /* A laisser en premier */
+
+};
+
+
+/* Met en place une correspondance entre attribut et binaire. */
+bool g_record_item_create(GRecordItem *, GKaitaiAttribute *, GBinContent *, const mrange_t *, SourceEndian);
+
+
+
+#endif /* _PLUGINS_KAITAI_RECORDS_ITEM_INT_H */
diff --git a/plugins/kaitai/records/item.c b/plugins/kaitai/records/item.c
new file mode 100644
index 0000000..0bcf9f7
--- /dev/null
+++ b/plugins/kaitai/records/item.c
@@ -0,0 +1,309 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * item.c - conservation d'une correspondance entre attribut et binaire
+ *
+ * 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 "item.h"
+
+
+#include <assert.h>
+#include <string.h>
+
+
+#include "item-int.h"
+
+
+
+/* -------------------- DEFINITION D'UNE CORRESPONDANCE UNITAIRE -------------------- */
+
+
+/* Initialise la classe des correspondances attribut/binaire. */
+static void g_record_item_class_init(GRecordItemClass *);
+
+/* Initialise une correspondance entre attribut et binaire. */
+static void g_record_item_init(GRecordItem *);
+
+/* Supprime toutes les références externes. */
+static void g_record_item_dispose(GRecordItem *);
+
+/* Procède à la libération totale de la mémoire. */
+static void g_record_item_finalize(GRecordItem *);
+
+
+
+/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */
+
+
+/* Calcule ou fournit la zone couverte par une correspondance. */
+static void g_record_item_get_range(const GRecordItem *, mrange_t *);
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* DEFINITION D'UNE CORRESPONDANCE UNITAIRE */
+/* ---------------------------------------------------------------------------------- */
+
+
+/* Indique le type défini pour une correspondance entre un attribut et du binaire. */
+G_DEFINE_TYPE(GRecordItem, g_record_item, G_TYPE_MATCH_RECORD);
+
+
+/******************************************************************************
+* *
+* Paramètres : klass = classe à initialiser. *
+* *
+* Description : Initialise la classe des correspondances attribut/binaire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_record_item_class_init(GRecordItemClass *klass)
+{
+ GObjectClass *object; /* Autre version de la classe */
+ GMatchRecordClass *record; /* Version parente de la classe*/
+
+ object = G_OBJECT_CLASS(klass);
+
+ object->dispose = (GObjectFinalizeFunc/* ! */)g_record_item_dispose;
+ object->finalize = (GObjectFinalizeFunc)g_record_item_finalize;
+
+ record = G_MATCH_RECORD_CLASS(klass);
+
+ record->get_range = (get_record_range_fc)g_record_item_get_range;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : item = instance à initialiser. *
+* *
+* Description : Initialise une correspondance entre attribut et binaire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_record_item_init(GRecordItem *item)
+{
+ copy_mrange(&item->range, UNUSED_MRANGE_PTR);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : item = instance d'objet GLib à traiter. *
+* *
+* Description : Supprime toutes les références externes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_record_item_dispose(GRecordItem *item)
+{
+ G_OBJECT_CLASS(g_record_item_parent_class)->dispose(G_OBJECT(item));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : item = instance d'objet GLib à traiter. *
+* *
+* Description : Procède à la libération totale de la mémoire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_record_item_finalize(GRecordItem *item)
+{
+ G_OBJECT_CLASS(g_record_item_parent_class)->finalize(G_OBJECT(item));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : attrib = analyseur à l'origine de la correspondance. *
+* content = contenu binaire lié à la correspondance. *
+* range = zone couverture par la correspondance. *
+* endian = boustime des données à respecter. *
+* *
+* Description : Crée une nouvelle correspondance entre attribut et binaire. *
+* *
+* Retour : Instance mise en place ou NULL en cas d'échec. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+GRecordItem *g_record_item_new(GKaitaiAttribute *attrib, GBinContent *content, const mrange_t *range, SourceEndian endian)
+{
+ GRecordItem *result; /* Structure à retourner */
+
+ result = g_object_new(G_TYPE_RECORD_ITEM, NULL);
+
+ if (!g_record_item_create(result, attrib, content, range, endian))
+ g_clear_object(&result);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : item = correspondance à initialiser pleinement. *
+* attrib = analyseur à l'origine de la correspondance. *
+* content = contenu binaire lié à la correspondance. *
+* range = zone couverte par la correspondance. *
+* endian = boustime des données à respecter. *
+* *
+* Description : Met en place une correspondance entre attribut et binaire. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool g_record_item_create(GRecordItem *item, GKaitaiAttribute *attrib, GBinContent *content, const mrange_t *range, SourceEndian endian)
+{
+ bool result; /* Bilan à retourner */
+
+ result = g_match_record_create(G_MATCH_RECORD(item), G_KAITAI_PARSER(attrib), content);
+
+ if (result)
+ {
+ copy_mrange(&item->range, range);
+
+ item->endian = endian;
+
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : item = correspondance à consulter. *
+* out = tableau d'octets retournés. [OUT] *
+* len = taille de ce tableau alloué. [OUT] *
+* *
+* Description : Lit la série d'octets d'un élément Kaitai entier représenté. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool g_record_item_get_truncated_bytes(const GRecordItem *item, bin_t **out, size_t *len)
+{
+ bool result; /* Bilan à retourner */
+ GKaitaiParser *parser; /* Attribut associé à l'élément*/
+
+ parser = g_match_record_get_creator(G_MATCH_RECORD(item));
+ assert(G_IS_KAITAI_ATTRIBUTE(parser));
+
+ result = g_kaitai_attribute_read_truncated_bytes(G_KAITAI_ATTRIBUTE(parser),
+ G_MATCH_RECORD(item)->content,
+ &item->range,
+ out, len);
+
+ g_object_unref(G_OBJECT(parser));
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : item = correspondance à consulter. *
+* out = valeur à sauvegarder sous une forme générique. [OUT] *
+* *
+* Description : Lit la valeur d'un élément Kaitai entier représenté. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool g_record_item_get_value(const GRecordItem *item, resolved_value_t *out)
+{
+ bool result; /* Bilan à retourner */
+ GKaitaiParser *parser; /* Attribut associé à l'élément*/
+
+ parser = g_match_record_get_creator(G_MATCH_RECORD(item));
+ assert(G_IS_KAITAI_ATTRIBUTE(parser));
+
+ result = g_kaitai_attribute_read_value(G_KAITAI_ATTRIBUTE(parser),
+ G_MATCH_RECORD(item)->content,
+ &item->range,
+ item->endian, out);
+
+ g_object_unref(G_OBJECT(parser));
+
+ return result;
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* IMPLEMENTATION DES FONCTIONS DE CLASSE */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : item = correspondance à consulter. *
+* range = zone de couverture déterminée. [OUT] *
+* *
+* Description : Calcule ou fournit la zone couverte par une correspondance. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_record_item_get_range(const GRecordItem *item, mrange_t *range)
+{
+ copy_mrange(range, &item->range);
+
+}
diff --git a/plugins/kaitai/records/item.h b/plugins/kaitai/records/item.h
new file mode 100644
index 0000000..1286a89
--- /dev/null
+++ b/plugins/kaitai/records/item.h
@@ -0,0 +1,65 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * item.h - prototypes pour la conservation d'une correspondance entre attribut et binaire
+ *
+ * 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/>.
+ */
+
+
+#ifndef _PLUGINS_KAITAI_RECORDS_ITEM_H
+#define _PLUGINS_KAITAI_RECORDS_ITEM_H
+
+
+#include <glib-object.h>
+
+
+#include "../record.h"
+#include "../parsers/attribute.h"
+
+
+
+#define G_TYPE_RECORD_ITEM g_record_item_get_type()
+#define G_RECORD_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_RECORD_ITEM, GRecordItem))
+#define G_IS_RECORD_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_RECORD_ITEM))
+#define G_RECORD_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_RECORD_ITEM, GRecordItemClass))
+#define G_IS_RECORD_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_RECORD_ITEM))
+#define G_RECORD_ITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_RECORD_ITEM, GRecordItemClass))
+
+
+/* Correspondance établie entre un attribut et du binaire (instance) */
+typedef struct _GRecordItem GRecordItem;
+
+/* Correspondance établie entre un attribut et du binaire (classe) */
+typedef struct _GRecordItemClass GRecordItemClass;
+
+
+/* Indique le type défini pour une correspondance entre un attribut et du binaire. */
+GType g_record_item_get_type(void);
+
+/* Crée une nouvelle correspondance entre attribut et binaire. */
+GRecordItem *g_record_item_new(GKaitaiAttribute *, GBinContent *, const mrange_t *, SourceEndian);
+
+/* Lit la série d'octets d'un élément Kaitai entier représenté. */
+bool g_record_item_get_truncated_bytes(const GRecordItem *, bin_t **, size_t *);
+
+/* Lit la valeur d'un élément Kaitai entier représenté. */
+bool g_record_item_get_value(const GRecordItem *, resolved_value_t *);
+
+
+
+#endif /* _PLUGINS_KAITAI_RECORDS_ITEM_H */
diff --git a/plugins/kaitai/records/list-int.h b/plugins/kaitai/records/list-int.h
new file mode 100644
index 0000000..88b411d
--- /dev/null
+++ b/plugins/kaitai/records/list-int.h
@@ -0,0 +1,60 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * list-int.h - prototypes internes pour la conservation d'une liste de correspondance avec du binaire
+ *
+ * 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/>.
+ */
+
+
+#ifndef _PLUGINS_KAITAI_RECORDS_LIST_INT_H
+#define _PLUGINS_KAITAI_RECORDS_LIST_INT_H
+
+
+#include "list.h"
+
+
+#include "../record-int.h"
+
+
+
+/* Liste de correspondances établies entre attributs et binaire (instance) */
+struct _GRecordList
+{
+ GMatchRecord parent; /* A laisser en premier */
+
+ vmpa2t pos; /* Début de zone */
+
+ GMatchRecord **children; /* Sous-correspondances */
+ size_t count; /* Taille de cette série */
+
+};
+
+/* Liste de correspondances établies entre attributs et binaire (classe) */
+struct _GRecordListClass
+{
+ GMatchRecordClass parent; /* A laisser en premier */
+
+};
+
+
+/* Met en place une série de correspondances attribut/binaire. */
+bool g_record_list_create(GRecordList *, GKaitaiAttribute *, GBinContent *, const vmpa2t *);
+
+
+
+#endif /* _PLUGINS_KAITAI_RECORDS_LIST_INT_H */
diff --git a/plugins/kaitai/records/list.c b/plugins/kaitai/records/list.c
new file mode 100644
index 0000000..1a36bf5
--- /dev/null
+++ b/plugins/kaitai/records/list.c
@@ -0,0 +1,424 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * list.c - conservation d'une liste de correspondance avec du binaire
+ *
+ * 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 "list.h"
+
+
+#include <assert.h>
+#include <malloc.h>
+
+
+#include "list-int.h"
+
+
+
+/* ------------------ DEFINITION D'UNE SEQUENCE DE CORRESPONDANCES ------------------ */
+
+
+/* Initialise la classe des listes de correspondances. */
+static void g_record_list_class_init(GRecordListClass *);
+
+/* Initialise une série de correspondances attributs/binaire. */
+static void g_record_list_init(GRecordList *);
+
+/* Supprime toutes les références externes. */
+static void g_record_list_dispose(GRecordList *);
+
+/* Procède à la libération totale de la mémoire. */
+static void g_record_list_finalize(GRecordList *);
+
+
+
+/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */
+
+
+/* Calcule ou fournit la zone couverte par une correspondance. */
+static void g_record_list_get_range(const GRecordList *, mrange_t *);
+
+/* Recherche la correspondance associée à un identifiant. */
+static GMatchRecord *g_record_list_find_by_name(GRecordList *, const char *, size_t, unsigned int);
+
+/* Transforme une énumération en constante entière. */
+static bool g_record_list_resolve_enum(const GRecordList *, const sized_string_t *, const sized_string_t *, resolved_value_t *);
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* DEFINITION D'UNE SEQUENCE DE CORRESPONDANCES */
+/* ---------------------------------------------------------------------------------- */
+
+
+/* Indique le type défini pour une série de correspondances entre attributes et binaire. */
+G_DEFINE_TYPE(GRecordList, g_record_list, G_TYPE_MATCH_RECORD);
+
+
+/******************************************************************************
+* *
+* Paramètres : klass = classe à initialiser. *
+* *
+* Description : Initialise la classe des listes de correspondances. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_record_list_class_init(GRecordListClass *klass)
+{
+ GObjectClass *object; /* Autre version de la classe */
+ GMatchRecordClass *record; /* Version parente de la classe*/
+
+ object = G_OBJECT_CLASS(klass);
+
+ object->dispose = (GObjectFinalizeFunc/* ! */)g_record_list_dispose;
+ object->finalize = (GObjectFinalizeFunc)g_record_list_finalize;
+
+ record = G_MATCH_RECORD_CLASS(klass);
+
+ record->get_range = (get_record_range_fc)g_record_list_get_range;
+ record->find = (find_record_by_name_fc)g_record_list_find_by_name;
+ record->resolve = (resolve_record_enum_fc)g_record_list_resolve_enum;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : list = instance à initialiser. *
+* *
+* Description : Initialise une série de correspondances attributs/binaire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_record_list_init(GRecordList *list)
+{
+ list->children = NULL;
+ list->count = 0;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : list = instance d'objet GLib à traiter. *
+* *
+* Description : Supprime toutes les références externes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_record_list_dispose(GRecordList *list)
+{
+ size_t i; /* Boucle de parcours */
+
+ for (i = 0; i < list->count; i++)
+ g_clear_object(&list->children[i]);
+
+ G_OBJECT_CLASS(g_record_list_parent_class)->dispose(G_OBJECT(list));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : list = instance d'objet GLib à traiter. *
+* *
+* Description : Procède à la libération totale de la mémoire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_record_list_finalize(GRecordList *list)
+{
+ if (list->children != NULL)
+ free(list->children);
+
+ G_OBJECT_CLASS(g_record_list_parent_class)->finalize(G_OBJECT(list));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : attrib = analyseur à l'origine de la correspondance. *
+* content = contenu binaire lié à la correspondance. *
+* pos = début de la zone de couverture de la liste. *
+* *
+* Description : Crée une nouvelle série de correspondances attribut/binaire. *
+* *
+* Retour : Instance mise en place ou NULL en cas d'échec. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+GRecordList *g_record_list_new(GKaitaiAttribute *attrib, GBinContent *content, const vmpa2t *pos)
+{
+ GRecordList *result; /* Structure à retourner */
+
+ result = g_object_new(G_TYPE_RECORD_LIST, NULL);
+
+ if (!g_record_list_create(result, attrib, content, pos))
+ g_clear_object(&result);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : list = correspondance à initialiser pleinement. *
+* attrib = analyseur à l'origine de la correspondance. *
+* content = contenu binaire lié à la correspondance. *
+* *
+* Description : Met en place une série de correspondances attribut/binaire. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool g_record_list_create(GRecordList *list, GKaitaiAttribute *attrib, GBinContent *content, const vmpa2t *pos)
+{
+ bool result; /* Bilan à retourner */
+
+ result = g_match_record_create(G_MATCH_RECORD(list), G_KAITAI_PARSER(attrib), content);
+
+ if (result)
+ copy_vmpa(&list->pos, pos);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : list = ensemble de correspondances attribut/binaire. *
+* *
+* Description : Dénombre le nombre de correspondances enregistrées. *
+* *
+* Retour : Taille de la liste représentée. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+size_t g_record_list_count_records(const GRecordList *list)
+{
+ size_t result; /* Quantité à retourner */
+
+ result = list->count;
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : list = ensemble de correspondances attribut/binaire. *
+* record = sous-corresponde à intégrer. *
+* *
+* Description : Ajoute une correspondance supplémentaire à une série. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void g_record_list_add_record(GRecordList *list, GMatchRecord *record)
+{
+ list->children = realloc(list->children, ++list->count * sizeof(GMatchRecord));
+
+ list->children[list->count - 1] = record;
+ g_object_ref(G_OBJECT(record));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : list = ensemble de correspondances attribut/binaire. *
+* index = indice de la correspondance visée. *
+* *
+* Description : Fournit un élément ciblé dans la liste de correspondances. *
+* *
+* Retour : Instance de correspondance particulière, voire NULL. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+GMatchRecord *g_record_list_get_record(const GRecordList *list, size_t index)
+{
+ GMatchRecord *result; /* Instance à retourner */
+
+ if (index < list->count)
+ {
+ result = list->children[index];
+ g_object_ref(G_OBJECT(result));
+ }
+ else
+ result = NULL;
+
+ return result;
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* IMPLEMENTATION DES FONCTIONS DE CLASSE */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : list = correspondance à consulter. *
+* range = zone de couverture déterminée. [OUT] *
+* *
+* Description : Calcule ou fournit la zone couverte par une correspondance. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_record_list_get_range(const GRecordList *list, mrange_t *range)
+{
+ vmpa2t start; /* Position de départ */
+ mrange_t range_0; /* Première zone couverte */
+ mrange_t range_n; /* Dernière zone couverte */
+ vmpa2t end; /* Position de d'arrivée */
+ phys_t length; /* Taille de zone couverte */
+
+ assert(list->count > 0);
+
+ if (list->count == 0)
+ {
+ init_vmpa(&start, VMPA_NO_PHYSICAL, VMPA_NO_VIRTUAL);
+ init_mrange(range, &start, VMPA_NO_PHYSICAL);
+ }
+
+ else
+ {
+ g_match_record_get_range(list->children[0], &range_0);
+ g_match_record_get_range(list->children[list->count - 1], &range_n);
+
+ copy_vmpa(&start, get_mrange_addr(&range_0));
+
+ compute_mrange_end_addr(&range_n, &end);
+ length = compute_vmpa_diff(&start, &end);
+
+ init_mrange(range, &start, length);
+
+ }
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : list = ensemble de correspondances attribut/binaire. *
+* name = désignation de l'élément recherché. *
+* len = taille de cette désignation. *
+* level = profondeur maximale à atteindre (fond : 0). *
+* *
+* Description : Recherche la correspondance associée à un identifiant. *
+* *
+* Retour : Correspondance trouvée ou NULL. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static GMatchRecord *g_record_list_find_by_name(GRecordList *list, const char *name, size_t len, unsigned int level)
+{
+ GMatchRecord *result; /* Correspondance à renvoyer */
+ GMatchRecordClass *class; /* Classe parente normalisée */
+ size_t i; /* Boucle de parcours */
+
+ class = G_MATCH_RECORD_CLASS(g_record_list_parent_class);
+
+ result = class->find(G_MATCH_RECORD(list), name, len, level);
+
+ if (level > 0)
+ {
+ for (i = 0; i < list->count && result == NULL; i++)
+ result = g_match_record_find_by_name(list->children[i], name, len, level);
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : list = ensemble de correspondances attribut/binaire. *
+* name = désignation de l'élément recherché. *
+* label = étiquette de l'élément constant à traduire. *
+* value = valeur entière correspondante. [OUT] *
+* *
+* Description : Transforme une énumération en constante entière. *
+* *
+* Retour : Bilan de l'opération : true si la résolution est réalisée. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool g_record_list_resolve_enum(const GRecordList *list, const sized_string_t *name, const sized_string_t *label, resolved_value_t *value)
+{
+ bool result; /* Bilan à retourner */
+ size_t i; /* Boucle de parcours */
+
+ /**
+ * Comme les types peuvent être sélectionnés dynamiquement, le parcours
+ * de l'ensemble des sous-noeuds doit être effectué.
+ */
+
+ result = false;
+
+ for (i = 0; i < list->count && !result; i++)
+ result = g_match_record_resolve_enum(list->children[i], name, label, value);
+
+ return result;
+
+}
diff --git a/plugins/kaitai/records/list.h b/plugins/kaitai/records/list.h
new file mode 100644
index 0000000..03e593e
--- /dev/null
+++ b/plugins/kaitai/records/list.h
@@ -0,0 +1,71 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * list.h - prototypes pour la conservation d'une liste de correspondance avec du binaire
+ *
+ * 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/>.
+ */
+
+
+#ifndef _PLUGINS_KAITAI_RECORDS_LIST_H
+#define _PLUGINS_KAITAI_RECORDS_LIST_H
+
+
+#include <glib-object.h>
+
+
+#include <analysis/content.h>
+
+
+#include "../record.h"
+#include "../parsers/attribute.h"
+
+
+
+#define G_TYPE_RECORD_LIST g_record_list_get_type()
+#define G_RECORD_LIST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_RECORD_LIST, GRecordList))
+#define G_IS_RECORD_LIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_RECORD_LIST))
+#define G_RECORD_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_RECORD_LIST, GRecordListClass))
+#define G_IS_RECORD_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_RECORD_LIST))
+#define G_RECORD_LIST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_RECORD_LIST, GRecordListClass))
+
+
+/* Liste de correspondances établies entre attributs et binaire (instance) */
+typedef struct _GRecordList GRecordList;
+
+/* Liste de correspondances établies entre attributs et binaire (classe) */
+typedef struct _GRecordListClass GRecordListClass;
+
+
+/* Indique le type défini pour une série de correspondances entre attributes et binaire. */
+GType g_record_list_get_type(void);
+
+/* Crée une nouvelle série de correspondances attribut/binaire. */
+GRecordList *g_record_list_new(GKaitaiAttribute *, GBinContent *, const vmpa2t *);
+
+/* Dénombre le nombre de correspondances enregistrées. */
+size_t g_record_list_count_records(const GRecordList *);
+
+/* Ajoute une correspondance supplémentaire à une série. */
+void g_record_list_add_record(GRecordList *, GMatchRecord *);
+
+/* Fournit un élément ciblé dans la liste de correspondances. */
+GMatchRecord *g_record_list_get_record(const GRecordList *, size_t);
+
+
+
+#endif /* _PLUGINS_KAITAI_RECORDS_LIST_H */
diff --git a/plugins/kaitai/records/value-int.h b/plugins/kaitai/records/value-int.h
new file mode 100644
index 0000000..6a84a7f
--- /dev/null
+++ b/plugins/kaitai/records/value-int.h
@@ -0,0 +1,57 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * value-int.h - prototypes internes pour la conservation d'une instance virtuelle
+ *
+ * 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/>.
+ */
+
+
+#ifndef _PLUGINS_KAITAI_RECORDS_VALUE_INT_H
+#define _PLUGINS_KAITAI_RECORDS_VALUE_INT_H
+
+
+#include "value.h"
+
+
+#include "../record-int.h"
+
+
+
+/* Valeur calculée selon des correspondances parallèles (instance) */
+struct _GRecordValue
+{
+ GMatchRecord parent; /* A laisser en premier */
+
+ kaitai_scope_t locals; /* Sauvegarde de contexte */
+
+};
+
+/* Valeur calculée selon des correspondances parallèles (classe) */
+struct _GRecordValueClass
+{
+ GMatchRecordClass parent; /* A laisser en premier */
+
+};
+
+
+/* Met en place une valeur calculée selon des correspondances. */
+bool g_record_value_create(GRecordValue *, GKaitaiInstance *, const kaitai_scope_t *);
+
+
+
+#endif /* _PLUGINS_KAITAI_RECORDS_VALUE_INT_H */
diff --git a/plugins/kaitai/records/value.c b/plugins/kaitai/records/value.c
new file mode 100644
index 0000000..cafe5c3
--- /dev/null
+++ b/plugins/kaitai/records/value.c
@@ -0,0 +1,336 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * value.c - conservation d'une correspondance entre attribut et binaire
+ *
+ * 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 "value.h"
+
+
+#include <assert.h>
+#include <stdarg.h>
+#include <string.h>
+
+
+#include "value-int.h"
+#include "../parsers/attribute.h"
+
+
+
+/* -------------------- DEFINITION D'UNE CORRESPONDANCE UNITAIRE -------------------- */
+
+
+/* Initialise la classe des valeurs purement calculées. */
+static void g_record_value_class_init(GRecordValueClass *);
+
+/* Initialise une correspondance entre attribut et binaire. */
+static void g_record_value_init(GRecordValue *);
+
+/* Supprime toutes les références externes. */
+static void g_record_value_dispose(GRecordValue *);
+
+/* Procède à la libération totale de la mémoire. */
+static void g_record_value_finalize(GRecordValue *);
+
+
+
+/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */
+
+
+/* Calcule ou fournit la zone couverte par une correspondance. */
+static void g_record_value_get_range(const GRecordValue *, mrange_t *);
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* DEFINITION D'UNE CORRESPONDANCE UNITAIRE */
+/* ---------------------------------------------------------------------------------- */
+
+
+/* Indique le type défini pour une valeur calculée selon des correspondances établies. */
+G_DEFINE_TYPE(GRecordValue, g_record_value, G_TYPE_MATCH_RECORD);
+
+
+/******************************************************************************
+* *
+* Paramètres : klass = classe à initialiser. *
+* *
+* Description : Initialise la classe des valeurs purement calculées. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_record_value_class_init(GRecordValueClass *klass)
+{
+ GObjectClass *object; /* Autre version de la classe */
+ GMatchRecordClass *record; /* Version parente de la classe*/
+
+ object = G_OBJECT_CLASS(klass);
+
+ object->dispose = (GObjectFinalizeFunc/* ! */)g_record_value_dispose;
+ object->finalize = (GObjectFinalizeFunc)g_record_value_finalize;
+
+ record = G_MATCH_RECORD_CLASS(klass);
+
+ record->get_range = (get_record_range_fc)g_record_value_get_range;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : value = instance à initialiser. *
+* *
+* Description : Initialise une correspondance entre attribut et binaire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_record_value_init(GRecordValue *value)
+{
+ init_record_scope(&value->locals, NULL);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : value = instance d'objet GLib à traiter. *
+* *
+* Description : Supprime toutes les références externes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_record_value_dispose(GRecordValue *value)
+{
+ reset_record_scope(&value->locals);
+
+ G_OBJECT_CLASS(g_record_value_parent_class)->dispose(G_OBJECT(value));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : value = instance d'objet GLib à traiter. *
+* *
+* Description : Procède à la libération totale de la mémoire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_record_value_finalize(GRecordValue *value)
+{
+ G_OBJECT_CLASS(g_record_value_parent_class)->finalize(G_OBJECT(value));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : inst = analyseur à l'origine de la correspondance. *
+* locals = correspondances courantes pour résolutions. *
+* *
+* Description : Crée une nouvelle valeur calculée à partir d'une instance. *
+* *
+* Retour : Instance mise en place ou NULL en cas d'échec. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+GRecordValue *g_record_value_new(GKaitaiInstance *inst, const kaitai_scope_t *locals)
+{
+ GRecordValue *result; /* Structure à retourner */
+
+ result = g_object_new(G_TYPE_RECORD_VALUE, NULL);
+
+ if (!g_record_value_create(result, inst, locals))
+ g_clear_object(&result);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : value = correspondance à initialiser pleinement. *
+* inst = analyseur à l'origine de la correspondance. *
+* locals = correspondances courantes pour résolutions. *
+* *
+* Description : Met en place une valeur calculée à partir d'une instance. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool g_record_value_create(GRecordValue *value, GKaitaiInstance *inst, const kaitai_scope_t *locals)
+{
+ bool result; /* Bilan à retourner */
+
+ result = g_match_record_create(G_MATCH_RECORD(value), G_KAITAI_PARSER(inst), NULL);
+
+ if (result)
+ copy_record_scope(&value->locals, locals);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : value = correspondance à consulter. *
+* value = valeur à sauvegarder sous une forme générique. [OUT] *
+* *
+* Description : Détermine la valeur d'un élément Kaitai calculé. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool g_record_value_compute_value(const GRecordValue *value, resolved_value_t *out)
+{
+ bool result; /* Bilan à retourner */
+ GKaitaiParser *parser; /* Instance liée à l'élément */
+
+ parser = g_match_record_get_creator(G_MATCH_RECORD(value));
+ assert(G_IS_KAITAI_ATTRIBUTE(parser));
+
+ result = g_kaitai_instance_compute_value(G_KAITAI_INSTANCE(parser),
+ &value->locals,
+ out);
+
+ g_object_unref(G_OBJECT(parser));
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : value = correspondance à consulter. *
+* value = valeur à sauvegarder sous une forme générique. [OUT] *
+* *
+* Description : Détermine et ajuste la valeur d'un élément Kaitai calculé. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool g_record_value_compute_and_aggregate_value(const GRecordValue *value, resolved_value_t *out)
+{
+ bool result; /* Bilan à retourner */
+ GKaitaiParser *parser; /* Instance liée à l'élément */
+ sized_string_t converted; /* Conversion finale ? */
+
+ parser = g_match_record_get_creator(G_MATCH_RECORD(value));
+ assert(G_IS_KAITAI_ATTRIBUTE(parser));
+
+ result = g_kaitai_instance_compute_value(G_KAITAI_INSTANCE(parser),
+ &value->locals,
+ out);
+
+ g_object_unref(G_OBJECT(parser));
+
+ if (result)
+ {
+ /**
+ * Lorsque c'est possible, les tableaux Kaitai sont transformés en série
+ * d'octets.
+ *
+ * Même si les tableaux ont une grande portée en interne des règles
+ * Kaitai (par exemple pour constituer une table de constantes de
+ * référence), il en est différemment à l'extérieur du processus de
+ * traitement : les tableaux sont le plus souvent destinés à manipuler
+ * les octets représentés directement (par exemple :
+ * "contents: [0xca, 0xfe, 0xba, 0xbe]").
+ *
+ * Pour les valeurs d'instance dont le type n'est pas explicite,
+ * le choix est fait de tenter de simplifier la vie de l'utilisateur
+ * en lui fournissant directement les octets qu'il attend probablement
+ * plutôt qu'un tableau contenant des octets à extraire.
+ */
+
+ if (out->type == GVT_ARRAY)
+ {
+ if (g_kaitai_array_convert_to_bytes(out->array, &converted))
+ {
+ EXIT_RESOLVED_VALUE(*out);
+
+ out->bytes = converted;
+ out->type = GVT_BYTES;
+
+ }
+
+ }
+
+ }
+
+ return result;
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* IMPLEMENTATION DES FONCTIONS DE CLASSE */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : value = correspondance à consulter. *
+* range = zone de couverture déterminée. [OUT] *
+* *
+* Description : Calcule ou fournit la zone couverte par une correspondance. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_record_value_get_range(const GRecordValue *value, mrange_t *range)
+{
+ copy_mrange(range, UNUSED_MRANGE_PTR);
+
+}
diff --git a/plugins/kaitai/records/value.h b/plugins/kaitai/records/value.h
new file mode 100644
index 0000000..8ee9cdd
--- /dev/null
+++ b/plugins/kaitai/records/value.h
@@ -0,0 +1,65 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * value.h - prototypes pour la conservation d'une correspondance entre attribut et binaire
+ *
+ * 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/>.
+ */
+
+
+#ifndef _PLUGINS_KAITAI_RECORDS_VALUE_H
+#define _PLUGINS_KAITAI_RECORDS_VALUE_H
+
+
+#include <glib-object.h>
+
+
+#include "../record.h"
+#include "../parsers/instance.h"
+
+
+
+#define G_TYPE_RECORD_VALUE g_record_value_get_type()
+#define G_RECORD_VALUE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_RECORD_VALUE, GRecordValue))
+#define G_IS_RECORD_VALUE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_RECORD_VALUE))
+#define G_RECORD_VALUE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_RECORD_VALUE, GRecordValueClass))
+#define G_IS_RECORD_VALUE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_RECORD_VALUE))
+#define G_RECORD_VALUE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_RECORD_VALUE, GRecordValueClass))
+
+
+/* Valeur calculée selon des correspondances parallèles (instance) */
+typedef struct _GRecordValue GRecordValue;
+
+/* Valeur calculée selon des correspondances parallèles (classe) */
+typedef struct _GRecordValueClass GRecordValueClass;
+
+
+/* Indique le type défini pour une valeur calculée selon des correspondances établies. */
+GType g_record_value_get_type(void);
+
+/* Crée une nouvelle valeur calculée à partir d'une instance. */
+GRecordValue *g_record_value_new(GKaitaiInstance *, const kaitai_scope_t *);
+
+/* Détermine la valeur d'un élément Kaitai entier calculé. */
+bool g_record_value_compute_value(const GRecordValue *, resolved_value_t *);
+
+/* Détermine et ajuste la valeur d'un élément Kaitai calculé. */
+bool g_record_value_compute_and_aggregate_value(const GRecordValue *, resolved_value_t *);
+
+
+
+#endif /* _PLUGINS_KAITAI_RECORDS_VALUE_H */
diff --git a/plugins/kaitai/scope.c b/plugins/kaitai/scope.c
new file mode 100644
index 0000000..6d1d47a
--- /dev/null
+++ b/plugins/kaitai/scope.c
@@ -0,0 +1,257 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * scope.c - recherches d'éléments de lecture
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "scope.h"
+
+
+#include "parsers/struct.h"
+
+
+
+/******************************************************************************
+* *
+* Paramètres : locals = contexte de variables locales à initialiser. *
+* meta = informations générales à disposition. *
+* *
+* Description : Initialise un contexte pour correspondances Kaitai établies. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void init_record_scope(kaitai_scope_t *locals, GKaitaiMeta *meta)
+{
+ locals->meta = meta;
+
+ if (meta != NULL)
+ g_object_ref(G_OBJECT(meta));
+
+ locals->root = NULL;
+ locals->parent = NULL;
+ locals->last = NULL;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : locals = contexte de variables locales à réinitialiser. *
+* *
+* Description : Vide un contexte de correspondances Kaitai établies. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void reset_record_scope(kaitai_scope_t *locals)
+{
+ g_clear_object(&locals->meta);
+
+ g_clear_object(&locals->root);
+ g_clear_object(&locals->parent);
+ g_clear_object(&locals->last);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : dest = contexte de variables locales à initialiser. *
+* src = contexte de variables locales à copier. *
+* *
+* Description : Copie un contexte de correspondances Kaitai établies. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void copy_record_scope(kaitai_scope_t *dest, const kaitai_scope_t *src)
+{
+ reset_record_scope(dest);
+
+#define COPY_SCOPE_ITEM(itm) \
+ dest->itm = src->itm; \
+ if (dest->itm != NULL) \
+ g_object_ref(G_OBJECT(dest->itm));
+
+ COPY_SCOPE_ITEM(meta);
+
+ COPY_SCOPE_ITEM(root);
+ COPY_SCOPE_ITEM(parent);
+ COPY_SCOPE_ITEM(last);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : locals = variables locales pour les résolutions de types. *
+* *
+* Description : Retourne le souvenir d'une correspondance racine. *
+* *
+* Retour : Dernière correspondance établie ou NULL. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+GMatchRecord *get_root_record(const kaitai_scope_t *locals)
+{
+ GMatchRecord *result; /* Instance à retourner */
+
+ result = locals->root;
+
+ if (result != NULL)
+ g_object_ref(G_OBJECT(result));
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : locals = variables locales pour les résolutions de types. *
+* *
+* Description : Retourne le souvenir de la correspondance parente effectuée. *
+* *
+* Retour : Dernière correspondance établie ou NULL. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+GMatchRecord *get_parent_record(const kaitai_scope_t *locals)
+{
+ GMatchRecord *result; /* Instance à retourner */
+
+ result = locals->parent;
+
+ if (result != NULL)
+ g_object_ref(G_OBJECT(result));
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : locals = variables locales pour les résolutions de types. *
+* record = dernière correspondance établie. *
+* *
+* Description : Conserve le souvenir de la dernière correspondance effectuée.*
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void remember_last_record(kaitai_scope_t *locals, GMatchRecord *record)
+{
+ g_clear_object(&locals->last);
+
+ locals->last = record;
+
+ if (record != NULL)
+ g_object_ref(G_OBJECT(record));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : locals = variables locales pour les résolutions de types. *
+* *
+* Description : Retourne le souvenir de la dernière correspondance effectuée.*
+* *
+* Retour : Dernière correspondance établie ou NULL. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+GMatchRecord *get_last_record(const kaitai_scope_t *locals)
+{
+ GMatchRecord *result; /* Instance à retourner */
+
+ result = locals->last;
+
+ if (result != NULL)
+ g_object_ref(G_OBJECT(result));
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : locals = variables locales pour les résolutions de types. *
+* 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 *find_sub_type(const kaitai_scope_t *locals, const char *name)
+{
+ GKaitaiType *result; /* Instance à retourner */
+ size_t i; /* Boucle de parcours */
+ GKaitaiParser *parser; /* Lecteur d'origine */
+
+ GMatchRecord *list[] = { locals->root, locals->parent, locals->last };
+
+ result = NULL;
+
+ for (i = 0; i < 3; i++)
+ {
+ if (list[i] == NULL)
+ continue;
+
+ parser = g_match_record_get_creator(list[i]);
+
+ if (G_IS_KAITAI_STRUCT(parser))
+ result = g_kaitai_structure_find_sub_type(G_KAITAI_STRUCT(parser), name);
+
+ g_object_unref(G_OBJECT(parser));
+
+ if (result != NULL)
+ break;
+
+ }
+
+ return result;
+
+}
diff --git a/plugins/kaitai/scope.h b/plugins/kaitai/scope.h
new file mode 100644
index 0000000..5dc52bf
--- /dev/null
+++ b/plugins/kaitai/scope.h
@@ -0,0 +1,72 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * scope.h - prototypes pour les recherches d'éléments de lecture
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _PLUGINS_KAITAI_SCOPE_H
+#define _PLUGINS_KAITAI_SCOPE_H
+
+
+#include "record.h"
+#include "parsers/meta.h"
+#include "parsers/type.h"
+
+
+
+/* Accès aux différentes variables de contexte */
+typedef struct _kaitai_scope_t
+{
+ GKaitaiMeta *meta; /* Informations globales */
+
+ GMatchRecord *root; /* Variable "_root" */
+ GMatchRecord *parent; /* Variable "_parent" */
+ GMatchRecord *last; /* Variable "_" */
+
+} kaitai_scope_t;
+
+
+/* Initialise un contexte pour correspondances Kaitai établies. */
+void init_record_scope(kaitai_scope_t *, GKaitaiMeta *);
+
+/* Vide un contexte de correspondances Kaitai établies. */
+void reset_record_scope(kaitai_scope_t *);
+
+/* Copie un contexte de correspondances Kaitai établies. */
+void copy_record_scope(kaitai_scope_t *, const kaitai_scope_t *);
+
+/* Retourne le souvenir d'une correspondance racine. */
+GMatchRecord *get_root_record(const kaitai_scope_t *);
+
+/* Retourne le souvenir de la correspondance parente effectuée. */
+GMatchRecord *get_parent_record(const kaitai_scope_t *);
+
+/* Conserve le souvenir de la dernière correspondance effectuée. */
+void remember_last_record(kaitai_scope_t *, GMatchRecord *);
+
+/* Retourne le souvenir de la dernière correspondance effectuée. */
+GMatchRecord *get_last_record(const kaitai_scope_t *);
+
+/* Recherche la définition d'un type nouveau pour Kaitai. */
+GKaitaiType *find_sub_type(const kaitai_scope_t *, const char *);
+
+
+
+#endif /* _PLUGINS_KAITAI_SCOPE_H */
diff --git a/plugins/kaitai/stream-int.h b/plugins/kaitai/stream-int.h
new file mode 100644
index 0000000..50b71df
--- /dev/null
+++ b/plugins/kaitai/stream-int.h
@@ -0,0 +1,55 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * stream-int.h - prototypes pour les données associées à un flux de données Kaitai
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef PLUGINS_KAITAI_STREAM_INT_H
+#define PLUGINS_KAITAI_STREAM_INT_H
+
+
+#include "stream.h"
+
+
+
+/* Flux de données à disposition d'une analyse Kaitai (instance) */
+struct _GKaitaiStream
+{
+ GObject parent; /* A laisser en premier */
+
+ GBinContent *content; /* Contenu brut manipulé */
+ vmpa2t pos; /* Tête de lecture dans le flux*/
+
+};
+
+/* Flux de données à disposition d'une analyse Kaitai (classe) */
+struct _GKaitaiStreamClass
+{
+ GObjectClass parent; /* A laisser en premier */
+
+};
+
+
+/* Met en place un flux de données pour Kaitai. */
+bool g_kaitai_stream_create(GKaitaiStream *, GBinContent *, const vmpa2t *);
+
+
+
+#endif /* PLUGINS_KAITAI_STREAM_INT_H */
diff --git a/plugins/kaitai/stream.c b/plugins/kaitai/stream.c
new file mode 100644
index 0000000..66d0f8e
--- /dev/null
+++ b/plugins/kaitai/stream.c
@@ -0,0 +1,237 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * stream.c - données associées à un flux de données Kaitai
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "stream.h"
+
+
+#include "stream-int.h"
+
+
+
+/* Initialise la classe des flux de données pour Kaitai. */
+static void g_kaitai_stream_class_init(GKaitaiStreamClass *);
+
+/* Initialise un flux de données accessibles à Kaitai. */
+static void g_kaitai_stream_init(GKaitaiStream *);
+
+/* Supprime toutes les références externes. */
+static void g_kaitai_stream_dispose(GKaitaiStream *);
+
+/* Procède à la libération totale de la mémoire. */
+static void g_kaitai_stream_finalize(GKaitaiStream *);
+
+
+
+/* Indique le type défini pour un flux de données manipulé par une lecture Kaitai. */
+G_DEFINE_TYPE(GKaitaiStream, g_kaitai_stream, G_TYPE_OBJECT);
+
+
+/******************************************************************************
+* *
+* Paramètres : klass = classe à initialiser. *
+* *
+* Description : Initialise la classe des flux de données pour Kaitai. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_kaitai_stream_class_init(GKaitaiStreamClass *klass)
+{
+ GObjectClass *object; /* Autre version de la classe */
+
+ object = G_OBJECT_CLASS(klass);
+
+ object->dispose = (GObjectFinalizeFunc/* ! */)g_kaitai_stream_dispose;
+ object->finalize = (GObjectFinalizeFunc)g_kaitai_stream_finalize;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : stream = instance à initialiser. *
+* *
+* Description : Initialise un flux de données accessibles à Kaitai. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_kaitai_stream_init(GKaitaiStream *stream)
+{
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : stream = instance d'objet GLib à traiter. *
+* *
+* Description : Supprime toutes les références externes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_kaitai_stream_dispose(GKaitaiStream *stream)
+{
+ G_OBJECT_CLASS(g_kaitai_stream_parent_class)->dispose(G_OBJECT(stream));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : stream = instance d'objet GLib à traiter. *
+* *
+* Description : Procède à la libération totale de la mémoire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_kaitai_stream_finalize(GKaitaiStream *stream)
+{
+ G_OBJECT_CLASS(g_kaitai_stream_parent_class)->finalize(G_OBJECT(stream));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : content = contenu binaire parcouru par une analyse Kaitai. *
+* pos = tête de lecture courante. *
+* *
+* Description : Rassemble les éléments constituant un flux de données Kaitai.*
+* *
+* Retour : Instance mise en place ou NULL en cas d'échec. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+GKaitaiStream *g_kaitai_stream_new(GBinContent *content, const vmpa2t *pos)
+{
+ GKaitaiStream *result; /* Structure à retourner */
+
+ result = g_object_new(G_TYPE_KAITAI_STREAM, NULL);
+
+ if (!g_kaitai_stream_create(result, content, pos))
+ g_clear_object(&result);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : attrib = lecteur d'attribut Kaitai à initialiser pleinement.*
+* content = contenu binaire parcouru par une analyse Kaitai. *
+* pos = tête de lecture courante. *
+* *
+* Description : Met en place un flux de données pour Kaitai. *
+* *
+* Retour : Bilan de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool g_kaitai_stream_create(GKaitaiStream *stream, GBinContent *content, const vmpa2t *pos)
+{
+ bool result; /* Bilan à retourner */
+
+ result = true;
+
+ stream->content = content;
+ g_object_ref(G_OBJECT(content));
+
+ copy_vmpa(&stream->pos, pos);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : stream = flux de données Kaitai à consulter. *
+* *
+* Description : Indique le contenu de données binaires lié au flux Kaitai. *
+* *
+* Retour : Contenu binaire associé au flux de données. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+GBinContent *g_kaitai_stream_get_content(const GKaitaiStream *stream)
+{
+ GBinContent *result; /* Instance à renvoyer */
+
+ result = stream->content;
+
+ g_object_ref(G_OBJECT(result));
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : stream = flux de données Kaitai à consulter. *
+* *
+* Description : Détermine si la fin des données a été atteinte. *
+* *
+* Retour : true si la tête de lecture est en position finale, ou false. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool g_kaitai_stream_has_reached_eof(const GKaitaiStream *stream)
+{
+ bool result; /* Bilan à retourner */
+ vmpa2t end; /* Position finale du flux */
+ int ret; /* Bilan d'une comparaison */
+
+ g_binary_content_compute_end_pos(stream->content, &end);
+
+ ret = cmp_vmpa_by_phy(&stream->pos, &end);
+
+ result = (ret == 0);
+
+ return result;
+
+}
diff --git a/plugins/kaitai/stream.h b/plugins/kaitai/stream.h
new file mode 100644
index 0000000..ee82c6d
--- /dev/null
+++ b/plugins/kaitai/stream.h
@@ -0,0 +1,65 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * stream.h - prototypes pour les données associées à un flux de données Kaitai
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef PLUGINS_KAITAI_STREAM_H
+#define PLUGINS_KAITAI_STREAM_H
+
+
+#include <glib-object.h>
+#include <stdbool.h>
+
+
+#include <analysis/content.h>
+
+
+
+#define G_TYPE_KAITAI_STREAM g_kaitai_stream_get_type()
+#define G_KAITAI_STREAM(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_KAITAI_STREAM, GKaitaiStream))
+#define G_IS_KAITAI_STREAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_KAITAI_STREAM))
+#define G_KAITAI_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_KAITAI_STREAM, GKaitaiStreamClass))
+#define G_IS_KAITAI_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_KAITAI_STREAM))
+#define G_KAITAI_STREAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_KAITAI_STREAM, GKaitaiStreamClass))
+
+
+/* Flux de données à disposition d'une analyse Kaitai (instance) */
+typedef struct _GKaitaiStream GKaitaiStream;
+
+/* Flux de données à disposition d'une analyse Kaitai (classe) */
+typedef struct _GKaitaiStreamClass GKaitaiStreamClass;
+
+
+/* Indique le type défini pour un flux de données manipulé par une lecture Kaitai. */
+GType g_kaitai_stream_get_type(void);
+
+/* Rassemble les éléments constituant un flux de données Kaitai. */
+GKaitaiStream *g_kaitai_stream_new(GBinContent *, const vmpa2t *);
+
+/* Indique le contenu de données binaires lié au flux Kaitai. */
+GBinContent *g_kaitai_stream_get_content(const GKaitaiStream *);
+
+/* Détermine si la fin des données a été atteinte. */
+bool g_kaitai_stream_has_reached_eof(const GKaitaiStream *);
+
+
+
+#endif /* PLUGINS_KAITAI_STREAM_H */
diff --git a/plugins/kaitai/tokens.l b/plugins/kaitai/tokens.l
new file mode 100644
index 0000000..3ddf40d
--- /dev/null
+++ b/plugins/kaitai/tokens.l
@@ -0,0 +1,327 @@
+
+%top {
+
+#include <assert.h>
+#include <malloc.h>
+#include <string.h>
+
+#include <common/extstr.h>
+
+#include "grammar.h"
+
+}
+
+
+%{
+
+#define PUSH_STATE(s) yy_push_state(s, yyscanner)
+#define POP_STATE yy_pop_state(yyscanner)
+
+%}
+
+
+%option bison-bridge reentrant
+%option stack
+%option nounput
+ //%option noinput
+%option noyywrap
+%option noyy_top_state
+%option yylineno
+%option never-interactive
+
+
+%x encoding
+%x escaped_str
+%x plain_str
+
+
+%%
+
+
+%{
+
+ /* no init C code */
+
+%}
+
+
+"+" { return PLUS; }
+"-" { return MINUS; }
+"*" { return MUL; }
+"/" { return DIV; }
+"%" { return MOD; }
+
+"<" { return LT; }
+"<=" { return LE; }
+"==" { return EQ; }
+"!=" { return NE; }
+">" { return GT; }
+">=" { return GE; }
+
+"<<" { return SHIFT_LEFT; }
+">>" { return SHIFT_RIGHT; }
+"&" { return BIT_AND; }
+"|" { return BIT_OR; }
+"^" { return BIT_XOR; }
+
+"not" { return NOT; }
+"and" { return AND; }
+"or" { return OR; }
+
+"(" { return PAREN_O; }
+")" { return PAREN_C; }
+"[" { return HOOK_O; }
+"]" { return HOOK_C; }
+"," { return COMMA; }
+"." { return DOT; }
+
+"?" { return QMARK; }
+":" { return COLON; }
+"::" { return DOUBLE_COLON; }
+
+".size" { return METH_SIZE; }
+".length" { return METH_LENGTH; }
+".reverse" { return METH_REVERSE; }
+".substring" { return METH_SUBSTRING; }
+".to_i" { return METH_TO_I; }
+".to_i(" { return METH_TO_I_RAD; }
+".to_s" { return METH_TO_S; }
+".to_s(" { PUSH_STATE(encoding); return METH_TO_S_ENC; }
+
+"_root" { return ROOT; }
+"_parent" { return PARENT; }
+"_" { return LAST; }
+"._io" { return METH_IO; }
+
+"true" { return TRUE_CONST; }
+"false" { return FALSE_CONST; }
+
+
+%{ /* Lecteurs de valeurs entières */ %}
+
+0[bB][01]+ {
+ char *__end;
+ yylval->unsigned_integer = strtoull(yytext + 2, &__end, 2);
+ if (__end != (yytext + yyleng))
+ YY_FATAL_ERROR("failed to parse integer");
+ return UNSIGNED_INTEGER;
+ }
+
+0[bB][01]{1,4}(_[01]{4})+ {
+ char *__tmp;
+ char *__end;
+ __tmp = strdup(yytext);
+ __tmp = strrpl(__tmp, "_", "");
+ yylval->unsigned_integer = strtoull(__tmp + 2, &__end, 2);
+ if (__end != (__tmp + strlen(__tmp)))
+ {
+ free(__tmp);
+ YY_FATAL_ERROR("failed to parse integer");
+ }
+ else free(__tmp);
+ return UNSIGNED_INTEGER;
+ }
+
+(0|[1-9][0-9]*) {
+ char *__end;
+ yylval->unsigned_integer = strtoull(yytext, &__end, 10);
+ if (__end != (yytext + yyleng))
+ YY_FATAL_ERROR("failed to parse integer");
+ return UNSIGNED_INTEGER;
+ }
+
+[1-9][0-9]{0,2}(_[1-9][0-9]{2})+ {
+ char *__tmp;
+ char *__end;
+ __tmp = strdup(yytext);
+ __tmp = strrpl(__tmp, "_", "");
+ yylval->unsigned_integer = strtoull(__tmp, &__end, 10);
+ if (__end != (__tmp + strlen(__tmp)))
+ {
+ free(__tmp);
+ YY_FATAL_ERROR("failed to parse integer");
+ }
+ else free(__tmp);
+ return UNSIGNED_INTEGER;
+ }
+
+-(0|[1-9][0-9]*) {
+ char *__end;
+ yylval->signed_integer = strtoll(yytext, &__end, 10);
+ if (__end != (yytext + yyleng))
+ YY_FATAL_ERROR("failed to parse integer");
+ return SIGNED_INTEGER;
+ }
+
+-[1-9][0-9]{0,2}(_[1-9][0-9]{2})+ {
+ char *__tmp;
+ char *__end;
+ __tmp = strdup(yytext);
+ __tmp = strrpl(__tmp, "_", "");
+ yylval->signed_integer = strtoll(__tmp, &__end, 10);
+ if (__end != (__tmp + strlen(__tmp)))
+ {
+ free(__tmp);
+ YY_FATAL_ERROR("failed to parse integer");
+ }
+ else free(__tmp);
+ return SIGNED_INTEGER;
+ }
+
+0[xX][0-9a-fA-F]+ {
+ char *__end;
+ yylval->unsigned_integer = strtoull(yytext, &__end, 16);
+ if (__end != (yytext + yyleng))
+ YY_FATAL_ERROR("failed to parse integer");
+ return UNSIGNED_INTEGER;
+ }
+
+0[xX][0-9a-fA-F]{1,4}(_[0-9a-fA-F]{4})+ {
+ char *__tmp;
+ char *__end;
+ __tmp = strdup(yytext);
+ __tmp = strrpl(__tmp, "_", "");
+ yylval->unsigned_integer = strtoull(__tmp, &__end, 16);
+ if (__end != (__tmp + strlen(__tmp)))
+ {
+ free(__tmp);
+ YY_FATAL_ERROR("failed to parse integer");
+ }
+ else free(__tmp);
+ return UNSIGNED_INTEGER;
+ }
+
+
+
+-?(0|[1-9][0-9]*\.[0-9]+) {
+ char *__end;
+ yylval->floating_number = strtod(yytext, &__end);
+ if (__end != (yytext + yyleng))
+ YY_FATAL_ERROR("failed to parse float");
+ return FLOAT;
+ }
+
+
+%{ /* Paramètre d'encodage */ %}
+
+<encoding>["'][-_A-Za-z0-9 ]+["'] {
+ yylval->sized_cstring.data = yytext + 1;
+ yylval->sized_cstring.len = yyleng - 2;
+ return ENCODING_NAME;
+ }
+
+<encoding>")" { POP_STATE; return PAREN_C; }
+
+
+[a-z][a-z0-9_]* {
+ yylval->sized_cstring.data = yytext;
+ yylval->sized_cstring.len = yyleng;
+ return IDENTIFIER;
+ }
+
+[^\\\[\],"'()\.: ]+ {
+ yylval->sized_cstring.data = yytext;
+ yylval->sized_cstring.len = yyleng;
+ return PLAIN_BYTES;
+ }
+
+
+%{ /* Lecteurs des tableaux de définition d'octets */ %}
+
+"\"" { PUSH_STATE(escaped_str); }
+
+
+<escaped_str>[^\\"]+ {
+ yylval->sized_cstring.data = yytext;
+ yylval->sized_cstring.len = yyleng;
+ return RAW_BYTES;
+ }
+
+<escaped_str>"\\a" { yylval->byte = '\a'; return RAW_BYTE; }
+<escaped_str>"\\b" { yylval->byte = '\b'; return RAW_BYTE; }
+<escaped_str>"\\t" { yylval->byte = '\t'; return RAW_BYTE; }
+<escaped_str>"\\n" { yylval->byte = '\n'; return RAW_BYTE; }
+<escaped_str>"\\v" { yylval->byte = '\v'; return RAW_BYTE; }
+<escaped_str>"\\f" { yylval->byte = '\f'; return RAW_BYTE; }
+<escaped_str>"\\r" { yylval->byte = '\r'; return RAW_BYTE; }
+<escaped_str>"\\e" { yylval->byte = '\e'; return RAW_BYTE; }
+<escaped_str>"\\\"" { yylval->byte = '"'; return RAW_BYTE; }
+<escaped_str>"\\'" { yylval->byte = '\''; return RAW_BYTE; }
+<escaped_str>"\\\\" { yylval->byte = '\\'; return RAW_BYTE; }
+<escaped_str>"\\0" { yylval->byte = '\0'; return RAW_BYTE; }
+
+<escaped_str>\\[0-9]{1,3} {
+ char __tmp[4];
+ memcpy(__tmp, yytext + 1, yyleng - 1);
+ __tmp[yyleng] = '\0';
+ yylval->byte = strtoull(__tmp, NULL, 8);
+ return RAW_BYTE;
+ }
+
+<escaped_str>"\"" { POP_STATE; }
+
+
+
+
+"'" { PUSH_STATE(plain_str); }
+
+<plain_str>[^']+ {
+ yylval->sized_cstring.data = yytext;
+ yylval->sized_cstring.len = yyleng;
+ return PLAIN_BYTES;
+ }
+
+<plain_str>['] { POP_STATE; }
+
+[.]$ {
+#ifndef NDEBUG
+ int ch;
+#endif
+ yylval->sized_cstring.data = yytext;
+ yylval->sized_cstring.len = yyleng;
+#ifndef NDEBUG
+ ch = input(yyscanner);
+ assert(ch == '\n');
+#else
+ input(yyscanner);
+#endif
+ return RAW_BYTES_WITH_ENDING_DOT;
+ }
+
+[^\\\[\],"'()\.: ]+[.]$ {
+#ifndef NDEBUG
+ int ch;
+#endif
+ yylval->sized_cstring.data = yytext;
+ yylval->sized_cstring.len = yyleng;
+#ifndef NDEBUG
+ ch = input(yyscanner);
+ assert(ch == '\n');
+#else
+ input(yyscanner);
+#endif
+ return RAW_BYTES_WITH_ENDING_DOT;
+ }
+
+
+%{ /* Actions par défaut */ %}
+
+<*>[ \t\n]+ { }
+
+<*>. {
+ char *msg;
+ int ret;
+ ret = asprintf(&msg,
+ "Unhandled token in rule definition: '%s'",
+ yytext);
+ if (ret == -1)
+ YY_FATAL_ERROR("Unhandled token in undisclosed rule definition");
+ else
+ {
+ YY_FATAL_ERROR(msg);
+ free(msg);
+ }
+ }
+
+
+%%
diff --git a/tests/plugins/kaitai.py b/tests/plugins/kaitai.py
new file mode 100644
index 0000000..b1e8881
--- /dev/null
+++ b/tests/plugins/kaitai.py
@@ -0,0 +1,2474 @@
+#!/usr/bin/python3-dbg
+# -*- coding: utf-8 -*-
+
+import locale
+
+from chrysacase import ChrysalideTestCase
+from pychrysalide.analysis.contents import MemoryContent
+from pychrysalide.plugins.kaitai.parsers import KaitaiStruct
+
+
+class TestKaitaiStruct(ChrysalideTestCase):
+ """TestCase for the KaitaiStruct parsing."""
+
+
+ @classmethod
+ def setUpClass(cls):
+
+ super(TestKaitaiStruct, cls).setUpClass()
+
+ cls.log('Setting locale suitable for floats...')
+
+ cls._old_locale = locale.getlocale(locale.LC_NUMERIC)
+
+ locale.setlocale(locale.LC_NUMERIC, 'C')
+
+
+ @classmethod
+ def tearDownClass(cls):
+
+ super(TestKaitaiStruct, cls).tearDownClass()
+
+ cls.log('Reverting locale...')
+
+ locale.setlocale(locale.LC_NUMERIC, cls._old_locale)
+
+
+
+ #################################
+ ### 4. Kaitai Struct language
+ #################################
+
+
+ def testKaitaiFixedLength(self):
+ """Load fixed-size structures."""
+
+ # Cf. 4.1. Fixed-size structures
+
+ definitions = '''
+meta:
+ id: mydesc
+ title: My Long Title
+ endian: be
+seq:
+ - id: field0
+ type: u4
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ self.assertEqual(kstruct.meta.id, 'mydesc')
+ self.assertEqual(kstruct.meta.title, 'My Long Title')
+
+ content = MemoryContent(b'\x01\x02\x03\x04')
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.field0.range.length, 4)
+ self.assertEqual(parsed.field0.value, 0x01020304)
+
+ definitions = '''
+meta:
+ endian: le
+seq:
+ - id: field0
+ type: u4
+ - id: field1
+ type: u4be
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ self.assertIsNone(kstruct.meta.id)
+ self.assertIsNone(kstruct.meta.title)
+
+ content = MemoryContent(b'\x01\x02\x03\x04\x01\x02\x03\x04')
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.field0.range.length, 4)
+ self.assertEqual(parsed.field0.value, 0x04030201)
+
+ self.assertEqual(parsed.field1.range.length, 4)
+ self.assertEqual(parsed.field1.value, 0x01020304)
+
+
+ definitions = '''
+seq:
+ - id: field0
+ type: u1
+ - id: field1
+ size: 2
+ - id: field2
+ size: field0 + 1
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ content = MemoryContent(b'\x01\x02\x03\x04\x05')
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.field0.range.length, 1)
+ self.assertEqual(parsed.field0.value, 0x01)
+
+ self.assertEqual(parsed.field1.range.length, 2)
+ self.assertEqual(parsed.field1.truncated_bytes, b'\x02\x03')
+
+ self.assertEqual(parsed.field2.range.length, 2)
+ self.assertEqual(parsed.field2.truncated_bytes, b'\x04\x05')
+
+
+ def testDocstrings(self):
+ """Handle Kaitai documentation."""
+
+ # Cf. 4.2. Docstrings
+
+ definitions = '''
+seq:
+ - id: rating
+ type: s4
+ doc: Rating, can be negative
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ content = MemoryContent(b'\x01\x02\x02\x04')
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.rating.creator.doc, 'Rating, can be negative')
+
+
+ def testKaitaiContents(self):
+ """Read various forms of fixed content."""
+
+ # Cf. 4.3. Checking for "magic" signatures
+
+ definitions = '''
+seq:
+ - id: field0
+ contents: [ 0, 0x10, '22', "50 ]
+'''
+
+ # ValueError: Unable to create Kaitai structure.
+ with self.assertRaisesRegex(ValueError, "Unable to create Kaitai structure"):
+ kstruct = KaitaiStruct(definitions)
+ self.assertIsNotNone(kstruct)
+
+
+ definitions = '''
+seq:
+ - id: field0
+ contents: [ 0x41, 66, 'CD' ]
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ content = MemoryContent(b'ABCD')
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.field0.range.length, 4)
+
+ self.assertEqual(parsed.field0.value, b'ABCD')
+
+
+ definitions = '''
+seq:
+ - id: field0
+ contents: ABCD
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ content = MemoryContent(b'ABCD')
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.field0.range.length, 4)
+
+
+ definitions = '''
+seq:
+ - id: field0
+ contents: "ABCD"
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ content = MemoryContent(b'ABCD')
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.field0.range.length, 4)
+
+
+ definitions = '''
+seq:
+ - id: field0
+ contents:
+ - 0x41
+ - "B"
+ - CD
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ content = MemoryContent(b'ABCD')
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.field0.range.length, 4)
+
+
+ def testVariableLengthStructures(self):
+ """Parse variable-length structures."""
+
+ # Cf. 4.4. Variable-length structures
+
+ definitions = '''
+seq:
+ - id: my_len
+ type: u1
+ - id: my_str
+ type: str
+ size: my_len
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ content = MemoryContent(b'\x03ABC')
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.my_len.value, 3)
+
+ self.assertEqual(parsed.my_str.value, b'ABC')
+
+
+ definitions = '''
+seq:
+ - id: my_len
+ type: u1
+ - id: my_str
+ type: str
+ size: my_len * 2
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ content = MemoryContent(b'\x03ABCDEF')
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.my_len.value, 3)
+
+ self.assertEqual(parsed.my_str.value, b'ABCDEF')
+
+
+ definitions = '''
+seq:
+ - id: field0
+ size-eos: true
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ content = MemoryContent(b'\x01\x02\x02\x03')
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(content, parsed.content)
+
+ self.assertEqual(parsed.range.addr.phys, 0)
+ self.assertEqual(parsed.range.length, len(content.data))
+
+
+ def testDelimitedStructures(self):
+ """Parse delimited structures."""
+
+ # Cf. 4.5. Delimited structures
+
+ definitions = '''
+seq:
+ - id: my_string
+ type: str
+ terminator: 0
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ content = MemoryContent(b'ABC\x00DEF')
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.my_string.value, b'ABC')
+
+
+ definitions = '''
+seq:
+ - id: my_string
+ type: strz
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ content = MemoryContent(b'ABC\x00DEF')
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.my_string.value, b'ABC')
+
+
+ definitions = '''
+seq:
+ - id: name
+ type: str
+ size: 8
+ terminator: 0
+ - id: guard
+ size: 1
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ content = MemoryContent(b'ABC\x00\x00\x00\x00\x00x\x00')
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.name.value, b'ABC')
+
+ self.assertEqual(parsed.guard.value, b'x')
+
+
+ def __passed__testEnums(self):
+ """Parse delimited structures."""
+
+ # Cf. 4.6. Enums (named integer constants)
+
+ pass
+
+
+ def testSubTypes(self):
+ """Includes subtypes definitions."""
+
+ # Cf. 4.7. Substructures (subtypes)
+
+ definitions = '''
+seq:
+ - id: field0
+ type: custom_type
+ - id: field1
+ type: custom_type
+ - id: field2
+ type: custom_type
+types:
+ custom_type:
+ seq:
+ - id: len
+ type: u1
+ - id: value
+ size: len
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ content = MemoryContent(b'\x01\xaa\x02\xbb\xbb\x03\xcc\xcc\xcc')
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.field0.len.value, 1)
+ self.assertEqual(parsed.field0.value.truncated_bytes, b'\xaa')
+
+ self.assertEqual(parsed.field1.len.value, 2)
+ self.assertEqual(parsed.field1.value.truncated_bytes, b'\xbb\xbb')
+
+ self.assertEqual(parsed.field2.len.value, 3)
+ self.assertEqual(parsed.field2.value.truncated_bytes, b'\xcc\xcc\xcc')
+
+
+ def testOtherAttributesAccess(self):
+ """Access attributes in other types."""
+
+ # Cf. 4.8. Accessing attributes in other types
+
+ definitions = '''
+seq:
+ - id: header
+ type: main_header
+ - id: body
+ size: header.body_len
+types:
+ main_header:
+ seq:
+ - id: magic
+ contents: FMT
+ - id: body_len
+ type: u1
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ content = MemoryContent(b'FMT\x04\xaa\xbb\xcc\xdd')
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.header.magic.raw_bytes, b'FMT')
+ self.assertEqual(parsed.header.magic.range.length, 3)
+
+ self.assertEqual(parsed.header.body_len.value, 4)
+
+ self.assertEqual(parsed.body.raw_bytes, b'\xaa\xbb\xcc\xdd')
+ self.assertEqual(parsed.body.range.length, 4)
+
+
+ def testConditionals(self):
+ """Read Kaitai values according to previous loaded values."""
+
+ # Cf. 4.9. Conditionals
+
+ definitions = '''
+seq:
+ - id: field1
+ type: u1
+ - id: field2
+ type: u1
+ - id: field3
+ type: u1
+ if: field1 + field2 > 10
+ - id: field4
+ type: u1
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ content = MemoryContent(b'\x01\x02\x03\x04')
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.field1.value, 0x01)
+ self.assertEqual(parsed.field2.value, 0x02)
+ self.assertFalse(hasattr(parsed, 'field3'))
+ self.assertEqual(parsed.field4.value, 0x03)
+
+
+ definitions = '''
+seq:
+ - id: field1
+ type: u1
+ - id: field2
+ type: u1
+ - id: field3
+ type: u1
+ if: field1 + field2 > 1
+ - id: field4
+ type: u1
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ content = MemoryContent(b'\x01\x02\x03\x04')
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.field1.value, 0x01)
+ self.assertEqual(parsed.field2.value, 0x02)
+ self.assertTrue(hasattr(parsed, 'field3'))
+ self.assertEqual(parsed.field4.value, 0x04)
+
+
+ definitions = '''
+seq:
+ - id: field1
+ type: u1
+ - id: field2
+ type: u1
+ - id: field3
+ type: u1
+ if: field1 + field2 == threshold::three
+ - id: field4
+ type: u1
+enums:
+ threshold:
+ 1: one
+ 2: two
+ 3: three
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ content = MemoryContent(b'\x01\x02\x03\x04')
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.field1.value, 0x01)
+ self.assertEqual(parsed.field2.value, 0x02)
+ self.assertTrue(hasattr(parsed, 'field3'))
+ self.assertEqual(parsed.field4.value, 0x04)
+
+
+ def testRepeatedReadUntilEOS(self):
+ """Read items until the end of the stream."""
+
+ # Cf. 4.10.1. Repeat until end of stream
+
+ definitions = '''
+seq:
+ - id: field0
+ type: u2be
+ repeat: eos
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ content = MemoryContent(b'\x01\x00\x02\x00\x03\x00\x04\x00')
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(len(parsed.field0), len(content.data) / 2)
+
+ for i in range(4):
+ self.assertEqual(parsed.field0[i].value, (i + 1) << 8)
+
+
+ def testRepeatedReadAccordingToCounter(self):
+ """Repeat read of items for a nomber of times."""
+
+ # Cf. 4.10.2. Repeat for a number of times
+
+ definitions = '''
+seq:
+ - id: field0
+ type: u1
+ - id: field1
+ type: u1
+ repeat: expr
+ repeat-expr: 1
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ content = MemoryContent(b'\x01\x01')
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.field0.value, 0x01)
+
+ self.assertEqual(len(parsed.field1), 1)
+
+ for i in range(1):
+ self.assertEqual(parsed.field1[i].value, i + 1)
+
+ definitions = '''
+seq:
+ - id: field0
+ type: u1
+ - id: field1
+ type: u1
+ - id: field2
+ type: u2
+ repeat: expr
+ repeat-expr: field0 + field1
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ content = MemoryContent(b'\x01\x02\x01\x00\x02\x00\x03\x00')
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.field0.value, 0x01)
+ self.assertEqual(parsed.field1.value, 0x02)
+
+ self.assertEqual(len(parsed.field2), 3)
+
+ for i in range(3):
+ self.assertEqual(parsed.field2[i].value, i + 1)
+
+
+ def testRepeatUntilConditionIsMet(self):
+ """Repeat until condition is met."""
+
+ # Cf. 4.10.3. Repeat until condition is met
+
+ definitions = '''
+seq:
+ - id: numbers
+ type: u1
+ repeat: until
+ repeat-until: _ == 0xff
+ - id: extra
+ type: u1
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ content = MemoryContent(b'\x01\x02\xff\xcc')
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(len(parsed.numbers), 3)
+
+ for i in range(2):
+ self.assertEqual(parsed.numbers[i].value, i + 1)
+
+ self.assertEqual(parsed.numbers[2].value, 0xff)
+
+ self.assertEqual(parsed.extra.value, 0xcc)
+
+ definitions = '''
+seq:
+ - id: records
+ type: buffer_with_len
+ repeat: until
+ repeat-until: _.len == 0
+types:
+ buffer_with_len:
+ seq:
+ - id: len
+ type: u1
+ - id: value
+ size: len
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ content = MemoryContent(b'\x02\xaa\xaa\x01\xbb\x00')
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.records[0].len.value, 2)
+ self.assertEqual(parsed.records[0].value.raw_bytes, b'\xaa\xaa')
+
+ self.assertEqual(parsed.records[1].len.value, 1)
+ self.assertEqual(parsed.records[1].value.raw_bytes, b'\xbb')
+
+ self.assertEqual(parsed.records[2].len.value, 0)
+ self.assertEqual(parsed.records[2].value.raw_bytes, b'')
+
+
+ def testParseTLVImplementation(self):
+ """Parse a typical TLV implementation."""
+
+ # Cf. 4.11. Typical TLV implementation (switching types on an expression)
+
+ definitions = '''
+seq:
+ - id: record
+ type: rec_def
+ repeat: eos
+types:
+ rec_def:
+ seq:
+ - id: rec_type
+ type: u1
+ - id: len
+ type: u1
+ - id: body
+ size: len
+ type:
+ switch-on: rec_type
+ cases:
+ 1: rec_type_1
+ 2: rec_type_2
+ rec_type_1:
+ seq:
+ - id: field1
+ type: u1
+ rec_type_2:
+ seq:
+ - id: field2
+ type: u2
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ content = MemoryContent(b'\x01\x01\xaa\x02\x02\xcc\xbb')
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(len(parsed.record), 2)
+
+ self.assertEqual(parsed.record[0].rec_type.value, 1)
+ self.assertEqual(parsed.record[0].len.value, 1)
+ self.assertEqual(parsed.record[0].body.field1.value, 0xaa)
+
+ self.assertEqual(parsed.record[1].rec_type.value, 2)
+ self.assertEqual(parsed.record[1].len.value, 2)
+ self.assertEqual(parsed.record[1].body.field2.value, 0xbbcc)
+
+
+ def testInstanceWithDataBeyondTheSequence(self):
+ """Build instances with data beyond the sequence."""
+
+ # Cf. 4.12. Instances: data beyond the sequence
+
+ definitions = '''
+instances:
+ some_integer:
+ pos: 0x4
+ type: u1
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ content = MemoryContent(b'\x01\x02\x03\x04\x05\x06\x07\x08')
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.some_integer.value, 5)
+
+
+ definitions = '''
+seq:
+ - id: file_offset
+ type: u1
+ - id: file_size
+ type: u1
+instances:
+ body:
+ pos: file_offset
+ size: file_size
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ content = MemoryContent(b'\x04\x02\x90\x90ABCD')
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.file_offset.value, 4)
+
+ self.assertEqual(parsed.file_size.value, 2)
+
+ self.assertEqual(parsed.body.value, b'AB')
+
+
+ def testValueInstances(self):
+ """Build value instances"""
+
+ # Cf. 4.13. Value instances
+
+ definitions = '''
+seq:
+ - id: length
+ type: u1
+ - id: extra
+ type: u1
+instances:
+ length_extended:
+ value: length * 3 + extra
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ content = MemoryContent(b'\x01\x02\x03\x04')
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.length.value, 1)
+
+ self.assertEqual(parsed.extra.value, 2)
+
+ self.assertEqual(parsed.length_extended.value, 5)
+
+
+ def testBitSizedIntegers(self):
+ """Read bit-sized integers."""
+
+ # Cf. 4.14. Bit-sized integers
+
+ definitions = '''
+seq:
+ - id: packed_1
+ type: u1
+instances:
+ version:
+ value: (packed_1 & 0b11110000) >> 4
+ len_header:
+ value: packed_1 & 0b00001111
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ content = MemoryContent(b'\x9a')
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.packed_1.value, 0x9a)
+
+ self.assertEqual(parsed.version.value, 0x9)
+
+ self.assertEqual(parsed.len_header.value, 0xa)
+
+
+ def __passed__testBitSizedIntegersBigEndian(self):
+ """Read bit-sized integers."""
+
+ # Cf. 4.14.1. Big-endian order
+
+ pass
+
+
+ def __passed__testBitSizedIntegersLittleEndian(self):
+ """Read bit-sized integers."""
+
+ # Cf. 4.14.2. Little-endian order
+
+ pass
+
+
+ def __passed__testBitSizedIntegersSpecifiedEndianness(self):
+ """Read bit-sized integers with specified bit endianness."""
+
+ # Cf. 4.14.3. Specifying bit endianness
+
+ pass
+
+
+
+ #################################
+ ### 5. Streams and substreams
+ #################################
+
+
+ def testTotalSizeLimit(self):
+ """Limit total size of structure."""
+
+ # Cf. 5.1. Limiting total size of structure
+
+ definitions = '''
+seq:
+ - id: body_len
+ type: u1
+ - id: random
+ size: 2
+ - id: comment
+ size: body_len - 2
+ - id: extra
+ type: u1
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ content = MemoryContent(b'\x05\x01\x02---\xbb')
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.body_len.value, 0x05)
+
+ self.assertEqual(parsed.random.raw_bytes, b'\x01\x02')
+
+ self.assertEqual(parsed.comment.raw_bytes, b'---')
+
+ self.assertEqual(parsed.extra.raw_bytes, b'\xbb')
+
+
+ definitions = '''
+seq:
+ - id: body_len
+ type: u1
+ - id: body
+ type: record_body
+ size: body_len
+ - id: extra
+ type: u1
+types:
+ record_body:
+ seq:
+ - id: random
+ size: 2
+ - id: comment
+ size-eos: true
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ content = MemoryContent(b'\x05\x01\x02---\xbb')
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.body_len.value, 0x05)
+
+ self.assertEqual(parsed.body.random.raw_bytes, b'\x01\x02')
+
+ self.assertEqual(parsed.body.comment.raw_bytes, b'---')
+
+ self.assertEqual(parsed.extra.raw_bytes, b'\xbb')
+
+
+ def testRepeatSizeLimit(self):
+ """Repeating until total size reaches limit."""
+
+ # Cf. 5.2. Repeating until total size reaches limit
+
+ content = MemoryContent(b'\x03\x00\x01\x02\xbb')
+
+ definitions = '''
+seq:
+ - id: total_len
+ type: u1
+ - id: files
+ type: file_entries
+ size: total_len
+ - id: extra
+ type: u1
+types:
+ file_entries:
+ seq:
+ - id: entries
+ type: entry
+ repeat: eos
+ entry:
+ seq:
+ - id: index
+ type: u1
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.total_len.value, 3)
+
+ self.assertEqual(len(parsed.files.entries), 3)
+
+ for i in range(3):
+ self.assertEqual(parsed.files.entries[i].index.value, i)
+
+ self.assertEqual(parsed.extra.value, 0xbb)
+
+
+ def testRelativePositioning(self):
+ """Parse with relative positioning."""
+
+ # Cf. 5.3. Relative positioning
+
+ content = MemoryContent(b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\0xe\x0f')
+
+ definitions = '''
+seq:
+ - id: some_header
+ size: 4
+ - id: body
+ type: block
+ size: 12
+types:
+ block:
+ seq:
+ - id: foo
+ type: u1
+ instances:
+ some_bytes_in_the_middle:
+ pos: 4
+ size: 4
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.some_header.value, b'\x00\x01\x02\x03')
+ self.assertEqual(parsed.body.foo.value, 0x04)
+
+ self.assertEqual(parsed.body.some_bytes_in_the_middle.value, b'\x08\x09\x0a\x0b')
+
+
+ def testAbsolutePositioning(self):
+ """Read from absolute position."""
+
+ # Cf. 5.4. Absolute positioning
+
+ content = MemoryContent(b'\x06\x03\x00\x00\x00\x00\x01\x02\x03\xbb')
+
+ definitions = '''
+seq:
+ - id: items
+ size: 10
+ type: entry
+ repeat: eos
+types:
+ entry:
+ seq:
+ - id: ofs_body
+ type: u1
+ - id: len_body
+ type: u1
+ instances:
+ body:
+ io: _root._io
+ pos: ofs_body
+ size: len_body
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.items[0].ofs_body.value, 6)
+ self.assertEqual(parsed.items[0].len_body.value, 3)
+
+ self.assertEqual(parsed.items[0].body.value, b'\x01\x02\x03')
+
+
+ def testSubstreamChoice(self):
+ """Choose a substream."""
+
+ # Cf. 5.5. Choosing a substream
+
+ content = MemoryContent(b'\xaa\xaa\xaa\xaa\x01\x02\x03\x04\x05\x06\x07\x08\x02\x03')
+
+ definitions = '''
+seq:
+ - id: global_header
+ size: 4
+ - id: block_one
+ type: big_container
+ size: 8
+ - id: block_two
+ type: smaller_container
+ size: 2
+types:
+ big_container:
+ seq:
+ - id: some_header
+ size: 8
+ smaller_container:
+ seq:
+ - id: ofs_in_big
+ type: u1
+ - id: len_in_big
+ type: u1
+ instances:
+ something_in_big:
+ io: _root.block_one._io
+ pos: ofs_in_big
+ size: len_in_big
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.block_two.ofs_in_big.value, 2)
+ self.assertEqual(parsed.block_two.len_in_big.value, 3)
+
+ self.assertEqual(parsed.block_two.something_in_big.value, b'\x03\x04\x05')
+
+
+ def __passed__testContentPreProcessing(self):
+ """Process content before parsing."""
+
+ # Cf. 5.6. Processing: dealing with compressed, obfuscated and encrypted data
+
+ pass
+
+
+
+ ##############################
+ ### 6. Expression language
+ ##############################
+
+
+ def testBasicDataTypes(self):
+ """Handle basic data types."""
+
+ # Cf. 6.1. Basic data types
+
+ definitions = '''
+seq:
+ - id: field1
+ type: u1
+ - id: field2
+ type: u2
+ - id: field4
+ type: u4
+ - id: field8
+ type: u8
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ content = MemoryContent(b'\x01\x02\x02\x04\x04\x04\x04\x08\x08\x08\x08\x08\x08\x08\x08')
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.field1.range.length, 1)
+ self.assertEqual(parsed.field2.range.length, 2)
+ self.assertEqual(parsed.field4.range.length, 4)
+ self.assertEqual(parsed.field8.range.length, 8)
+
+ definitions = '''
+seq:
+ - id: field1
+ type: u1
+ - id: field4
+ type: u4le
+ - id: field4bis
+ type: u4be
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ content = MemoryContent(b'\x01\x02\x03\x04\x05\x02\x03\x04\x05')
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.field1.value, 0x01)
+ self.assertEqual(parsed.field4.value, 0x05040302)
+ self.assertEqual(parsed.field4bis.value, 0x02030405)
+
+
+ definitions = '''
+instances:
+ number1:
+ value: 0xdead_cafe
+ number2:
+ value: 0xdead_cafe_dead_cafe
+ number3:
+ value: 12_345_678
+ number4:
+ value: 0b10100011
+ number5:
+ value: 0b1010_0011_1010_0011
+'''
+
+ content = MemoryContent(b'')
+
+ kstruct = KaitaiStruct(definitions)
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.number1.value, 0xdeadcafe)
+
+ self.assertEqual(parsed.number2.value, 0xdeadcafedeadcafe)
+
+ self.assertEqual(parsed.number3.value, 12345678)
+
+ self.assertEqual(parsed.number4.value, 0b10100011)
+
+ self.assertEqual(parsed.number5.value, 0b1010001110100011)
+
+
+ definitions = '''
+seq:
+ - id: op0
+ type: u1
+instances:
+ result:
+ value: 0xdeadcafe + op0
+ result2:
+ value: 0XdeadCAFE + op0
+'''
+
+ content = MemoryContent(b'\x00')
+
+ kstruct = KaitaiStruct(definitions)
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.result.value, 0xdeadcafe)
+
+ self.assertEqual(parsed.result2.value, 0xdeadcafe)
+
+
+ definitions = '''
+instances:
+ bytes1:
+ value: []
+ bytes2:
+ value: [ ]
+ bytes3:
+ value: [ 0x90 ]
+ bytes4:
+ value: [foo, 0, A, 0xa, 42]
+'''
+
+ content = MemoryContent(b'')
+
+ kstruct = KaitaiStruct(definitions)
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.bytes1.value, b'')
+
+ self.assertEqual(parsed.bytes2.value, b'')
+
+ self.assertEqual(parsed.bytes3.value, b'\x90')
+
+ self.assertEqual(parsed.bytes4.value, b'\x66\x6f\x6f\x00\x41\x0a\x2a')
+
+
+ definitions = '''
+instances:
+ escaped:
+ value: '[ "\\a\\b\\t\\n\\v\\f", "\\0", 0, " \\r\\e\\\"\\123" ]'
+'''
+
+ content = MemoryContent(b'')
+
+ kstruct = KaitaiStruct(definitions)
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.escaped.value, b'\x07\x08\x09\x0a\x0b\x0c\x00\x00 \x0d\x1b\x22\x53')
+
+
+ definitions_0 = r'''
+instances:
+ escaped:
+ value: "[ \"\\a\\b\\t\\n\\v\\f\", \"\\0\", 0, \"\\r\\e\\\"'\\123\" ]"
+'''
+
+ definitions_1 = r'''
+instances:
+ escaped:
+ value: [ "\\a\\b\\t\\n\\v\\f", "\\0", 0, "\\r\\e\\\"'\\123" ]
+'''
+
+ definitions_2 = '''
+instances:
+ escaped:
+ value: [ "\\\\a\\\\b\\\\t\\\\n\\\\v\\\\f", "\\\\0", 0, "\\\\r\\\\e\\\\\\"'\\\\123" ]
+'''
+
+ for d in [ definitions_0, definitions_1, definitions_2 ]:
+
+ content = MemoryContent(b'')
+
+ kstruct = KaitaiStruct(d)
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.escaped.value, b'\x07\x08\x09\x0a\x0b\x0c\x00\x00\x0d\x1b\x22\x27\x53')
+
+
+ def __passed__testUserDefinedTypes(self):
+ """Create user-defined types."""
+
+ # Cf. 6.2.1. User-defined types
+
+ pass
+
+
+ def testArrays(self):
+ """Create various arrays."""
+
+ # Cf. 6.2.2. Arrays
+
+ definitions = '''
+instances:
+ result_0:
+ value: "[]"
+ result_1:
+ value: "[CAFE, 0, BABE]"
+ result_2:
+ value: "[CAFE, 0, BABE] == 'CAFE' + [ 0x00 ] + 'BABE'"
+ result_3:
+ value: "[CAFE, 0, BABE] == [ 0x43, 0x41, 0x46, 0x45, 0x00, 0x42, 0x41, 0x42, 0x45 ]"
+ result_4:
+ value: "[foo, 0, A, 0xa, 42] == [ 0x66, 0x6f, 0x6f, 0x00, 0x41, 0x0a, 0x2a ]"
+ result_5:
+ value: "[1, 0x55, '▒,3', 3] == [ 0x01, 0x55, 0xe2, 0x96, 0x92, 0x2c, 0x33, 0x03 ]"
+'''
+
+ content = MemoryContent(b'')
+
+ kstruct = KaitaiStruct(definitions)
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.result_0.value, b'')
+
+ self.assertEqual(parsed.result_1.value, b'CAFE\x00BABE')
+
+
+ definitions = '''
+seq:
+ - id: indexes
+ type: u1
+ repeat: eos
+instances:
+ table:
+ value: "[ [ 1, 2, 3 ], [ 4, 5, 6 ], [ 7, 8, 9 ] ]"
+ ref:
+ value: indexes
+ result_0:
+ value: table
+ result_1:
+ value: ref
+ result_2:
+ value: table[indexes[0]][indexes[1] - 1]
+ result_3:
+ value: table[indexes[0]][ref[1]]
+'''
+
+ content = MemoryContent(b'\x01\x02\x03\x04')
+
+ kstruct = KaitaiStruct(definitions)
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.result_0.value.value, b'\x01\x02\x03\x04\x05\x06\x07\x08\x09')
+
+ self.assertEqual(type(parsed.result_1).__name__, 'RecordValue') # result_1
+ self.assertEqual(type(parsed.result_1.value).__name__, 'RecordValue') # result_1.ref
+ self.assertEqual(type(parsed.result_1.value.value).__name__, 'RecordList') # result_1.ref.table
+
+ self.assertEqual(parsed.result_1.value.value[3].value, 0x04)
+
+ self.assertEqual(parsed.result_2.value, 5)
+
+ self.assertEqual(parsed.result_3.value, 6)
+
+
+ def testArithmeticOperators(self):
+ """Compute with arithmetic operators."""
+
+ # Cf. 6.3.1. Arithmetic operators
+
+ definitions = '''
+seq:
+ - id: op0
+ type: u1
+ - id: op1
+ type: u1
+instances:
+ result_0:
+ value: op0 + op1 * 3
+ result_1:
+ value: (2 + op0) * op1
+ result_2:
+ value: 7 * 2.0
+ result_3:
+ value: 7 / 2.0
+ result_4:
+ value: -5 % 3
+ result_5:
+ value: 4 % 3
+ result_6:
+ value: 6 - 3 - -4.0
+'''
+
+ content = MemoryContent(b'\x02\x03\x04\x05')
+
+ kstruct = KaitaiStruct(definitions)
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.result_0.value, 11)
+
+ self.assertEqual(parsed.result_1.value, 12)
+
+ self.assertEqual(parsed.result_2.value, 14.0)
+
+ self.assertEqual(parsed.result_3.value, 3.5)
+
+ self.assertEqual(parsed.result_4.value, 1)
+
+ self.assertEqual(parsed.result_5.value, 1)
+
+ self.assertEqual(parsed.result_6.value, 7.0)
+
+
+ definitions = '''
+seq:
+ - id: base
+ size: 3
+instances:
+ result_0:
+ value: "'xXx ' + base + ' -- %< --'"
+'''
+
+ content = MemoryContent(b'ABC')
+
+ kstruct = KaitaiStruct(definitions)
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.result_0.value, b'xXx ABC -- %< --')
+
+
+ definitions = '''
+seq:
+ - id: nums
+ type: u1
+ repeat: eos
+instances:
+ computed:
+ value: nums[0] + nums[3]
+ computed2:
+ value: nums[0] * nums.size + nums[3]
+ computed3:
+ value: nums[0] * nums[nums.size - 1]
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ content = MemoryContent(b'\x01\x02\x03\x04')
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.computed.value, 5)
+
+ self.assertEqual(parsed.computed2.value, 8)
+
+ self.assertEqual(parsed.computed3.value, 4)
+
+
+ def testRelationalOperators(self):
+ """Compute with relational operators."""
+
+ # Cf. 6.3.2. Relational operators
+
+ definitions = '''
+seq:
+ - id: op0
+ type: u1
+ - id: op1
+ type: u1
+ - id: op2
+ size: 3
+instances:
+ result0:
+ value: op0 == op1
+ result1:
+ value: op0 != op1
+ result2:
+ value: op2 == 'ABC'
+ result3:
+ value: op2 < 'ABCD'
+ result4:
+ value: (op0 + 1) >= op1
+ result5:
+ value: "(op0 + 1) == 'ABC'.length"
+'''
+
+ content = MemoryContent(b'\x02\x03ABCD')
+
+ kstruct = KaitaiStruct(definitions)
+
+ parsed = kstruct.parse(content)
+
+ self.assertFalse(parsed.result0.value)
+
+ self.assertTrue(parsed.result1.value)
+
+ self.assertTrue(parsed.result2.value)
+
+ self.assertTrue(parsed.result3.value)
+
+ self.assertTrue(parsed.result4.value)
+
+ self.assertTrue(parsed.result5.value)
+
+
+ def testBitwiseOperators(self):
+ """Compute with bitwise operators."""
+
+ # Cf. 6.3.3. Bitwise operators
+
+ definitions = '''
+seq:
+ - id: op0
+ type: u1
+ - id: op1
+ type: u1
+ - id: op2
+ type: u1
+instances:
+ result_0:
+ value: op0 & op1
+ result_1:
+ value: op1 << op0 >> 1
+ result_2:
+ value: (op2 | 0x80) >> 1
+'''
+
+ content = MemoryContent(b'\x02\x07\x01')
+
+ kstruct = KaitaiStruct(definitions)
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.result_0.value, 0x2)
+
+ self.assertEqual(parsed.result_1.value, 14)
+
+ self.assertEqual(parsed.result_2.value, 0x40)
+
+
+ def testLogicalOperators(self):
+ """Compute with logical boolean operators."""
+
+ # Cf. 6.3.4. Logical (boolean) operators
+
+ definitions = '''
+seq:
+ - id: op0
+ type: u1
+ - id: op1
+ type: u1
+instances:
+ result_0:
+ value: (op0 > 0) and not false
+ result_1:
+ value: op0 == 1 or op1 == 2
+'''
+
+ content = MemoryContent(b'\x01\x02')
+
+ kstruct = KaitaiStruct(definitions)
+
+ parsed = kstruct.parse(content)
+
+ self.assertTrue(parsed.result_0.value)
+
+ self.assertTrue(parsed.result_1.value)
+
+
+ def testTernaryOperator(self):
+ """Offer challenges to the ternary operator."""
+
+ # Cf. 6.3.5. Ternary (if-then-else) operator
+
+ definitions = '''
+seq:
+ - id: op0
+ type: u1
+ - id: op1
+ type: u1
+ - id: op2
+ type: u1
+instances:
+ result_0:
+ value: 'op0 == 0x80 ? op1 + 1 : op1 * op2'
+ result_1:
+ value: 'op0 < 0x80 ? op1 + 1 : op1 * op2'
+ result_1:
+ value: 'op0 < 0x80 ? op1 + 1 : op1 * op2'
+ result_2:
+ value: '(op0 + 0x10) >= 0x90 ? true : 123'
+ result_3:
+ value: '(op0 + 0x10) >= 0x90 and false ? true : 123'
+'''
+
+ content = MemoryContent(b'\x80\x03\x04')
+
+ kstruct = KaitaiStruct(definitions)
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.result_0.value, 4)
+
+ self.assertEqual(parsed.result_1.value, 12)
+
+ self.assertTrue(parsed.result_2.value)
+
+ self.assertEqual(parsed.result_3.value, 123)
+
+
+ def testIntegersMethods(self):
+ """Run methods from integers."""
+
+ # Cf. 6.4.1. Integers
+
+ definitions = '''
+instances:
+ bytes1:
+ value: 123.to_s == "123" and -123.to_s == '-123'
+'''
+
+ content = MemoryContent(b'')
+
+ kstruct = KaitaiStruct(definitions)
+
+ parsed = kstruct.parse(content)
+
+ self.assertTrue(parsed.bytes1.value)
+
+
+ def testFloatsMethods(self):
+ """Run methods from floating numbers."""
+
+ # Cf. 6.4.2. Floating point numbers
+
+ definitions = '''
+instances:
+ result_0:
+ value: 2.32.to_i == 2 and -7.0.to_i == -7
+'''
+
+ content = MemoryContent(b'')
+
+ kstruct = KaitaiStruct(definitions)
+
+ parsed = kstruct.parse(content)
+
+ self.assertTrue(parsed.result_0.value)
+
+
+ def XXXtestByteArraysAndStringsMethods(self):
+ """Run methods from byte arrays and strings."""
+
+ # Cf. 6.4.3. Byte arrays
+ # 6.4.4. Strings
+
+ definitions = '''
+instances:
+ result_1:
+ value: '[].length == 0'
+ result_2:
+ value: "'edcba'.reverse == 'XXabcdeXX'.substring(2, 6)"
+ result_3:
+ value: "'123'.to_i == 123 and '-123'.to_i == -123"
+ result_4:
+ value: "[ 0x50, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x63, 0x69, 0xc3, 0xb3, 0x6e, 0x2e, 0x73, 0x78, 0x69 ].to_s('utf-8')"
+ result_5:
+ value: "'1010'.to_i(2) == 10 and 'cc'.to_i(16) == 204"
+'''
+
+ content = MemoryContent(b'')
+
+ kstruct = KaitaiStruct(definitions)
+
+ parsed = kstruct.parse(content)
+
+ self.assertTrue(parsed.result_1.value)
+
+ self.assertTrue(parsed.result_2.value)
+
+ self.assertTrue(parsed.result_3.value)
+
+ # Cf. https://docs.gtk.org/glib/character-set.html
+ # https://developer-old.gnome.org/glib/stable/glib-Character-Set-Conversion.html#g-convert
+ self.assertEqual(parsed.result_4.value.decode('utf-8'), 'Presentación.sxi')
+
+ self.assertTrue(parsed.result_5.value)
+
+
+ def __passed__testEnumsMethods(self):
+ """Run methods from booleans."""
+
+ # Cf. 6.4.5. Enums
+
+ pass
+
+
+ def testBooleansMethods(self):
+ """Run methods from booleans."""
+
+ # Cf. 6.4.6. Booleans
+
+ definitions = '''
+instances:
+ result_0:
+ value: true.to_i == 1
+ result_1:
+ value: (1 == 2).to_i == 0
+'''
+
+ content = MemoryContent(b'')
+
+ kstruct = KaitaiStruct(definitions)
+
+ parsed = kstruct.parse(content)
+
+ self.assertTrue(parsed.result_0.value)
+
+ self.assertTrue(parsed.result_1.value)
+
+
+ def testUserDefinedTypes(self):
+ """Retrieve user-defined types."""
+
+ # Cf. 6.4.7. User-defined types
+
+ definitions = '''
+instances:
+ result_0:
+ value: _root
+'''
+
+ content = MemoryContent(b'')
+
+ kstruct = KaitaiStruct(definitions)
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.result_0.value, parsed)
+
+
+ def __passed__testArraysMethods(self):
+ """Run methods from arrays."""
+
+ # Cf. 6.4.8. Array types
+
+ pass
+
+
+ def __passed__testStreamsMethods(self):
+ """Run methods from streams."""
+
+ # Cf. 6.4.9. Streams
+
+ pass
+
+
+
+ ##############################
+ ### 7. Advanced techniques
+ ##############################
+
+
+ def testSwitchOverStrings(self):
+ """Switch over strings."""
+
+ # Cf. 7.1.1. Switching over strings
+
+ definitions = '''
+seq:
+ - id: rec_type
+ type: strz
+ - id: body
+ type:
+ switch-on: rec_type
+ cases:
+ '"KETCHUP"': rec_type_1
+ '"MUSTARD"': rec_type_2
+ '"GUACAMOLE"': rec_type_3
+types:
+ rec_type_1:
+ instances:
+ direct:
+ value: 1
+ rec_type_2:
+ instances:
+ direct:
+ value: 2
+ rec_type_3:
+ instances:
+ direct:
+ value: 3
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ content = MemoryContent(b'GUACAMOLE\x00')
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.rec_type.value, b'GUACAMOLE')
+
+ self.assertEqual(parsed.body.direct.value, 3)
+
+
+ def testSwitchOverEnums(self):
+ """Switch over enumerations."""
+
+ # Cf. 7.1.2. Switching over enums
+
+ definitions = '''
+seq:
+ - id: rec_type
+ type: u1
+ enum: media
+ - id: body
+ type:
+ switch-on: rec_type
+ cases:
+ 'media::cdrom': rec_type_1
+ 'media::dvdrom': rec_type_2
+ 'media::cassette': rec_type_3
+types:
+ rec_type_1:
+ instances:
+ direct:
+ value: 1
+ rec_type_2:
+ instances:
+ direct:
+ value: 2
+ rec_type_3:
+ instances:
+ direct:
+ value: 3
+enums:
+ media:
+ 1: cdrom
+ 2: dvdrom
+ 3: cassette
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ content = MemoryContent(b'\x01')
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.rec_type.value, 1)
+
+ self.assertEqual(parsed.body.direct.value, 1)
+
+
+ def testFourCC(self):
+ """Recognize four character code."""
+
+ # Cf. 7.1.3. FourCC
+
+ definitions = '''
+seq:
+ - id: fourcc
+ type: u4le
+ enum: pixel_formats
+ - id: len
+ type: u1
+ - id: body
+ size: len
+ type:
+ switch-on: fourcc
+ cases:
+ 'pixel_formats::rgb2': block_rgb2
+ 'pixel_formats::rle4': block_rle4
+ 'pixel_formats::rle8': block_rle8
+types:
+ block_rgb2:
+ instances:
+ direct:
+ value: 2
+ block_rle4:
+ instances:
+ direct:
+ value: 4
+ block_rle8:
+ instances:
+ direct:
+ value: 8
+enums:
+ pixel_formats:
+ 0x32424752: rgb2
+ 0x34454C52: rle4
+ 0x38454C52: rle8
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ content = MemoryContent(b'RLE4\x05ABCDE')
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.fourcc.value, 0x34454C52)
+
+ self.assertEqual(parsed.len.value, 0x5)
+
+ self.assertEqual(parsed.body.direct.value, 4)
+
+
+ def testNothing(self):
+ """Do nothing."""
+
+ # Cf. 7.2. Do nothing
+
+ definitions = '''
+seq:
+ - id: field_0
+ size: 1
+ - id: field_1
+ type: dummy_1
+ - id: field_2
+ type: dummy_2
+ - id: field_3
+ type: dummy_3
+ - id: field_4
+ type: dummy_4
+ - id: field_5
+ size: 1
+types:
+ # One can use empty JSON object syntax to avoid specifying any of
+ # `seq`, `instances`, etc, sections.
+ dummy_1: {}
+ # One can use explicit doc to note that there's nothing there.
+ dummy_2:
+ doc: This type is intentionally left blank.
+ # One can use empty `seq` or `instances` or `types` section, any
+ # other empty sections, or any combination of thereof.
+ dummy_3:
+ seq: []
+ instances: {}
+ types: {}
+ # One can use a very explicit notion of the fact that we want to parse 0 bytes.
+ dummy_4:
+ seq:
+ - id: no_value
+ size: 0
+'''
+
+ content = MemoryContent(b'az')
+
+ kstruct = KaitaiStruct(definitions)
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.field_0.value, b'a')
+
+ self.assertEqual(type(parsed.field_1).__name__, 'RecordEmpty')
+ self.assertEqual(parsed.field_1.range.length, 0)
+
+ self.assertEqual(type(parsed.field_2).__name__, 'RecordEmpty')
+ self.assertEqual(parsed.field_2.range.length, 0)
+
+ self.assertEqual(type(parsed.field_3).__name__, 'RecordEmpty')
+ self.assertEqual(parsed.field_3.range.length, 0)
+
+ self.assertEqual(type(parsed.field_4.no_value).__name__, 'RecordEmpty')
+ self.assertEqual(parsed.field_4.no_value.range.length, 0)
+
+ self.assertEqual(parsed.field_5.value, b'z')
+
+
+ def testConsumeIncludeTerminators(self):
+ """Consume and/or include terminators."""
+
+ # Cf. 7.3.1. Terminator: consume or include?
+
+ definitions = '''
+seq:
+ - id: str1
+ type: str
+ terminator: 0x2e # `.`
+ - id: str2
+ type: str
+ terminator: 0x2e # `.`
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ content = MemoryContent(b'foo.bar.')
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.str1.value, b'foo')
+
+ self.assertEqual(parsed.str2.value, b'bar')
+
+
+ definitions = '''
+seq:
+ - id: str1
+ type: str
+ terminator: 0x2e # `.`
+ include: true
+ - id: str2
+ type: str
+ terminator: 0x2e # `.`
+ eos-error: false
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ content = MemoryContent(b'foo.bar')
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.str1.value, b'foo.')
+
+ self.assertEqual(parsed.str2.value, b'bar')
+
+
+ definitions = '''
+seq:
+ - id: str1
+ type: str
+ terminator: 0x2e # `.`
+ consume: false
+ - id: the_rest
+ type: str
+ size-eos: true
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ content = MemoryContent(b'foo.bar.')
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.str1.value, b'foo')
+
+ self.assertEqual(parsed.the_rest.value, b'.bar.')
+
+
+ definitions = '''
+seq:
+ - id: str1
+ type: str
+ terminator: .
+ - id: the_rest
+ type: str
+ size-eos: true
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ content = MemoryContent(b'foo.bar.')
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.str1.value, b'foo')
+
+ self.assertEqual(parsed.the_rest.value, b'bar.')
+
+
+ definitions = '''
+seq:
+ - id: str1
+ type: str
+ terminator: xxx.
+ - id: the_rest
+ type: str
+ size-eos: true
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ content = MemoryContent(b'fooxxx.bar.')
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.str1.value, b'foo')
+
+ self.assertEqual(parsed.the_rest.value, b'bar.')
+
+
+ def testIgnoreErrorsInDelimitedStructures(self):
+ """Ignore errors in delimited structures."""
+
+ # Cf. 7.3.2. Ignoring errors in delimited structures
+
+ definitions = '''
+seq:
+ - id: my_string
+ type: str
+ terminator: 0
+ eos-error: false
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ content = MemoryContent(b'\x61\x62\x63\x00\x64\x65\x66')
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.my_string.value, b'abc')
+
+
+ kstruct = KaitaiStruct(definitions)
+
+ content = MemoryContent(b'\x61\x62\x63\x00')
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.my_string.value, b'abc')
+
+
+ kstruct = KaitaiStruct(definitions)
+
+ content = MemoryContent(b'\x61\x62\x63')
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.my_string.value, b'abc')
+
+
+ def __passed__testImportTypesFromOtherFiles(self):
+ """Import types from other files."""
+
+ # Cf. 7.4. Importing types from other files
+
+ pass
+
+
+ def __passed__testPlugExternalCodeForOpaqueTypes(self):
+ """Plug external code for opaque types."""
+
+ # Cf. 7.5. Opaque types: plugging in external code
+
+ pass
+
+
+ def __passed__testCustomProcessingRoutines(self):
+ """Handle custom processing routines."""
+
+ # Cf. 7.6. Custom processing routines
+
+ pass
+
+
+ def __passed__testParentTypeEnforcing(self):
+ """Enforce parent type."""
+
+ # Cf. 7.7. Enforcing parent type
+
+ pass
+
+
+ def testTypecasting(self):
+ """Ensure there is no need for typecasting."""
+
+ # Cf. 7.8. Typecasting
+
+ definitions = '''
+seq:
+ - id: num_sections
+ type: u1
+ - id: sections
+ type: section
+ repeat: expr
+ repeat-expr: num_sections
+types:
+ section:
+ seq:
+ - id: sect_type
+ type: u1
+ - id: body
+ type:
+ switch-on: sect_type
+ cases:
+ 1: sect_header
+ 2: sect_color_data
+ sect_header:
+ seq:
+ - id: width
+ type: u1
+ - id: height
+ type: u1
+ sect_color_data:
+ seq:
+ - id: rgb
+ size: 3
+instances:
+ check_0:
+ value: sections[0].body.width * sections[0].body.height
+ check_1:
+ value: sections[1].body.rgb
+ check_2:
+ value: sections[2].body.width * sections[2].body.height
+ check_3:
+ value: sections[3].body.rgb
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ content = MemoryContent(b'\x04\x01\x02\x04\x02ABC\x01\x03\x05\x02UVW')
+
+ parsed = kstruct.parse(content)
+
+ # Vérifications externes
+
+ self.assertEqual(parsed.num_sections.value, 4)
+
+ self.assertEqual(len(parsed.sections), 4)
+
+ self.assertEqual(parsed.sections[0].body.width.value + parsed.sections[0].body.height.value, 6)
+
+ self.assertEqual(parsed.sections[1].body.rgb.value, b'ABC')
+
+ self.assertEqual(parsed.sections[2].body.width.value + parsed.sections[2].body.height.value, 8)
+
+ self.assertEqual(parsed.sections[3].body.rgb.value, b'UVW')
+
+ # Vérifications internes
+
+ self.assertEqual(parsed.check_0.value, 8)
+
+ self.assertEqual(parsed.check_1.value.value, b'ABC')
+
+ self.assertEqual(parsed.check_2.value, 15)
+
+ self.assertEqual(parsed.check_3.value.value, b'UVW')
+
+
+
+ ##########################
+ ### 8. Common pitfalls
+ ##########################
+
+
+ def testReadTypeWithSubstream(self):
+ """Read user-type with substream."""
+
+ # Cf. 8.1. Specifying size creates a substream
+
+ definitions = '''
+seq:
+ - id: header
+ size: 4
+ - id: block
+ type: block
+ size: 4 # <= important size designation, creates a substream
+instances:
+ byte_3:
+ pos: 3
+ type: u1
+types:
+ block:
+ instances:
+ byte_3:
+ pos: 3
+ type: u1
+ byte_3_alt:
+ io: _root._io # <= thanks to this, always points to a byte in main stream
+ pos: 3
+ type: u1
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ content = MemoryContent(b'\x00\x01\x02\x03\x04\x05\x06\x07')
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.header.value, b'\x00\x01\x02\x03')
+
+ self.assertEqual(parsed.byte_3.value, 0x03)
+
+ self.assertEqual(parsed.block.byte_3.value, 0x07)
+
+ self.assertEqual(parsed.block.byte_3_alt.value, 0x03)
+
+
+ definitions = '''
+seq:
+ - id: header
+ size: 4
+ - id: block
+ type: block
+instances:
+ byte_3:
+ pos: 3
+ type: u1
+types:
+ block:
+ instances:
+ byte_3:
+ pos: 3
+ type: u1
+ byte_3_alt:
+ io: _root._io # <= thanks to this, always points to a byte in main stream
+ pos: 3
+ type: u1
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ content = MemoryContent(b'\x00\x01\x02\x03\x04\x05\x06\x07')
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.header.value, b'\x00\x01\x02\x03')
+
+ self.assertEqual(parsed.byte_3.value, 0x03)
+
+ self.assertEqual(parsed.block.byte_3.value, 0x03)
+
+ self.assertEqual(parsed.block.byte_3_alt.value, 0x03)
+
+
+ def testReadTypeWithoutSubstream(self):
+ """Read user-type without substream."""
+
+ # Cf. 8.2. Not specifying size does not create a substream
+
+ definitions = '''
+seq:
+ - id: header
+ size: 2
+ - id: block_as_type1
+ type: type1
+ size: 2 # <= important, creates a substream
+types:
+ type1:
+ seq:
+ - id: val1
+ size: 2
+ type2:
+ seq:
+ - id: val2
+ size: 2
+instances:
+ block_as_type2:
+ io: block_as_type1._io
+ pos: 0
+ type: type2
+ internal_check:
+ value: block_as_type2._io == _root._io
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ content = MemoryContent(b'aabb')
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.header.value, b'aa')
+
+ self.assertEqual(parsed.block_as_type1.val1.value, b'bb')
+
+ self.assertEqual(parsed.block_as_type2.val2.value, b'bb')
+
+ self.assertFalse(parsed.internal_check.value)
+
+
+ definitions = '''
+seq:
+ - id: header
+ size: 2
+ - id: block_as_type1
+ type: type1
+types:
+ type1:
+ seq:
+ - id: val1
+ size: 2
+ type2:
+ seq:
+ - id: val2
+ size: 2
+instances:
+ block_as_type2:
+ io: block_as_type1._io
+ pos: 0
+ type: type2
+ internal_check:
+ value: block_as_type2._io == _root._io
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ content = MemoryContent(b'aabb')
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.header.value, b'aa')
+
+ self.assertEqual(parsed.block_as_type1.val1.value, b'bb')
+
+ self.assertEqual(parsed.block_as_type2.val2.value, b'aa')
+
+ self.assertTrue(parsed.internal_check.value)
+
+
+ def __passed__testSizedProcess(self):
+ """Provide a sized data to processing."""
+
+ # Cf. 8.3. Applying process without a size
+
+ pass
+
+
+ def __passed__testRelatedKeys(self):
+ """Check refering keys and their related YAML nodes."""
+
+ # Cf. 8.4. Keys relating to the whole array and to each element in repeated attributes
+
+ pass
+
+
+
+ #######################
+ ### x. Extra checks
+ #######################
+
+
+ def testMssingField(self):
+ """Raise error on missing field."""
+
+ definitions = '''
+seq:
+ - id: field0
+ size-eos: true
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ content = MemoryContent(b'\x01\x02\x02\x03')
+
+ parsed = kstruct.parse(content)
+ self.assertIsNotNone(parsed)
+
+ self.assertEqual(parsed.field0.creator.raw_id, 'field0')
+
+ self.assertEqual(parsed.field0.value, b'\x01\x02\x02\x03')
+
+ # AttributeError: 'pychrysalide.plugins.kaitai.records.RecordList' object has no attribute 'xxxx'
+ with self.assertRaisesRegex(AttributeError, "object has no attribute 'xxxx'"):
+ print(parsed.xxxx)
+
+
+ def testLEB128Values(self):
+ """Read some Little Endian Base 128 values."""
+
+ definitions = '''
+seq:
+ - id: groups
+ type: group
+ repeat: until
+ repeat-until: not _.has_next
+types:
+ group:
+ -webide-representation: '{value}'
+ doc: |
+ One byte group, clearly divided into 7-bit "value" chunk and 1-bit "continuation" flag.
+ seq:
+ - id: b
+ type: u1
+ instances:
+ has_next:
+ value: (b & 0b1000_0000) != 0
+ doc: If true, then we have more bytes to read
+ value:
+ value: b & 0b0111_1111
+ doc: The 7-bit (base128) numeric value chunk of this group
+instances:
+ len:
+ value: groups.size
+ value:
+ value: >-
+ groups[0].value
+ + (len >= 2 ? (groups[1].value << 7) : 0)
+ + (len >= 3 ? (groups[2].value << 14) : 0)
+ + (len >= 4 ? (groups[3].value << 21) : 0)
+ + (len >= 5 ? (groups[4].value << 28) : 0)
+ + (len >= 6 ? (groups[5].value << 35) : 0)
+ + (len >= 7 ? (groups[6].value << 42) : 0)
+ + (len >= 8 ? (groups[7].value << 49) : 0)
+ doc: Resulting unsigned value as normal integer
+ sign_bit:
+ value: '1 << (7 * len - 1)'
+ value_signed:
+ value: '(value ^ sign_bit) - sign_bit'
+ doc-ref: https://graphics.stanford.edu/~seander/bithacks.html#VariableSignExtend
+'''
+
+ kstruct = KaitaiStruct(definitions)
+
+ content = MemoryContent(b'\xe5\x8e\x26')
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.len.value, 3)
+
+ self.assertEqual(parsed.value.value, parsed.value_signed.value)
+
+ self.assertEqual(parsed.value.value, 624485)
+
+
+ content = MemoryContent(b'\xc0\xbb\x78')
+
+ parsed = kstruct.parse(content)
+
+ self.assertEqual(parsed.len.value, 3)
+
+ self.assertNotEqual(parsed.value.value, parsed.value_signed.value)
+
+ self.assertEqual(parsed.value_signed.value, -123456)