diff options
Diffstat (limited to 'plugins/kaitai/parsers/attribute.c')
-rw-r--r-- | plugins/kaitai/parsers/attribute.c | 2074 |
1 files changed, 2074 insertions, 0 deletions
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; + +} |