/* Chrysalide - Outil d'analyse de fichiers binaires * endianness.c - manipulation abstraite des nombres * * Copyright (C) 2009-2018 Cyrille Bagard * * This file is part of Chrysalide. * * Chrysalide is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * Chrysalide is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Chrysalide. If not, see . */ #include "endianness.h" #include #include #include /** * Mutualisation des aiguillages... */ #if __BYTE_ORDER != __LITTLE_ENDIAN && __BYTE_ORDER != __BIG_ENDIAN /* __PDP_ENDIAN et Cie... */ # error "Congratulations! Your byte order is not supported!" #endif /* ---------------------------------------------------------------------------------- */ /* CONVERSION ENTRE BOUTISMES */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : value = valeur d'origine à manipuler. * * endian = ordre des bits dans la source. * * * * Description : Adapte un nombre sur 16 bits à un boutisme donné. * * * * Retour : Valeur transformée au besoin. * * * * Remarques : - * * * ******************************************************************************/ uint16_t swap_u16(const uint16_t *value, SourceEndian endian) { uint16_t result; /* Valeur à retourner */ switch (endian) { case SRE_LITTLE: #if __BYTE_ORDER == __LITTLE_ENDIAN result = *value; #elif __BYTE_ORDER == __BIG_ENDIAN result = ((*value >> 0) & 0xff) << 8 | ((*value >> 8) & 0xff) << 0; #endif break; case SRE_BIG: #if __BYTE_ORDER == __LITTLE_ENDIAN result = ((*value >> 0) & 0xff) << 8 | ((*value >> 8) & 0xff) << 0; #elif __BYTE_ORDER == __BIG_ENDIAN result = *value; #endif break; default: assert(false); result = -1; break; } return result; } /****************************************************************************** * * * Paramètres : value = valeur d'origine à manipuler. * * endian = ordre des bits dans la source. * * * * Description : Adapte un nombre sur 16 bits à un boutisme donné. * * * * Retour : Valeur transformée au besoin. * * * * Remarques : - * * * ******************************************************************************/ uint32_t swap_u32(const uint32_t *value, SourceEndian endian) { uint32_t result; /* Valeur à retourner */ switch (endian) { case SRE_LITTLE: #if __BYTE_ORDER == __LITTLE_ENDIAN result = *value; #elif __BYTE_ORDER == __BIG_ENDIAN result = ((*value >> 0) & 0xff) << 24 | ((*value >> 8) & 0xff) << 16 | ((*value >> 16) & 0xff) << 8 | ((*value >> 24) & 0xff) << 0; #endif break; case SRE_BIG: #if __BYTE_ORDER == __LITTLE_ENDIAN result = ((*value >> 0) & 0xff) << 24 | ((*value >> 8) & 0xff) << 16 | ((*value >> 16) & 0xff) << 8 | ((*value >> 24) & 0xff) << 0; #elif __BYTE_ORDER == __BIG_ENDIAN result = *value; #endif break; default: assert(false); result = -1; break; } return result; } /****************************************************************************** * * * Paramètres : value = valeur d'origine à manipuler. * * endian = ordre des bits dans la source. * * * * Description : Adapte un nombre sur 16 bits à un boutisme donné. * * * * Retour : Valeur transformée au besoin. * * * * Remarques : - * * * ******************************************************************************/ uint64_t swap_u64(const uint64_t *value, SourceEndian endian) { uint64_t result; /* Valeur à retourner */ switch (endian) { case SRE_LITTLE: #if __BYTE_ORDER == __LITTLE_ENDIAN result = *value; #elif __BYTE_ORDER == __BIG_ENDIAN result = ((*value >> 0) & 0xff) << 56 | ((*value >> 8) & 0xff) << 48 | ((*value >> 16) & 0xff) << 40 | ((*value >> 24) & 0xff) << 32 | ((*value >> 32) & 0xff) << 24 | ((*value >> 40) & 0xff) << 16 | ((*value >> 48) & 0xff) << 8 | ((*value >> 56) & 0xff) << 0; #endif break; case SRE_BIG: #if __BYTE_ORDER == __LITTLE_ENDIAN result = ((*value >> 0) & 0xff) << 56 | ((*value >> 8) & 0xff) << 48 | ((*value >> 16) & 0xff) << 40 | ((*value >> 24) & 0xff) << 32 | ((*value >> 32) & 0xff) << 24 | ((*value >> 40) & 0xff) << 16 | ((*value >> 48) & 0xff) << 8 | ((*value >> 56) & 0xff) << 0; #elif __BYTE_ORDER == __BIG_ENDIAN result = *value; #endif break; default: assert(false); result = -1; break; } return result; } /* ---------------------------------------------------------------------------------- */ /* BOUTISME DES ENTREES / SORTIES */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : target = lieu d'enregistrement de la lecture. [OUT] * * data = flux de données à analyser. * * pos = position courante dans ce flux. [OUT] * * end = limite des données à analyser. * * low = position éventuelle des 4 bits visés. [OUT] * * * * Description : Lit un nombre non signé sur 4 bits. * * * * Retour : Bilan de l'opération : true en cas de succès, false sinon. * * * * Remarques : - * * * ******************************************************************************/ bool read_u4(uint8_t *target, const bin_t *data, phys_t *pos, phys_t end, bool *low) { if (end < 1) return false; if (*pos > (end - 1)) return false; if (*low) { *target = data[*pos] & 0x0f; *low = false; } else { *target = (data[*pos] & 0xf0) >> 4; *low = true; *pos += 1; } return true; } /****************************************************************************** * * * Paramètres : target = lieu d'enregistrement de la lecture. [OUT] * * data = flux de données à analyser. * * pos = position courante dans ce flux. [OUT] * * end = limite des données à analyser. * * * * Description : Lit un nombre non signé sur un octet. * * * * Retour : Bilan de l'opération : true en cas de succès, false sinon. * * * * Remarques : - * * * ******************************************************************************/ bool read_u8(uint8_t *target, const bin_t *data, phys_t *pos, phys_t end) { if (end < 1) return false; if (*pos > (end - 1)) return false; *target = data[*pos]; *pos += 1; return true; } /****************************************************************************** * * * Paramètres : target = lieu d'enregistrement de la lecture. [OUT] * * data = flux de données à analyser. * * pos = position courante dans ce flux. [OUT] * * end = limite des données à analyser. * * endian = ordre des bits dans la source. * * * * Description : Lit un nombre non signé sur deux octets. * * * * Retour : Bilan de l'opération : true en cas de succès, false sinon. * * * * Remarques : - * * * ******************************************************************************/ bool read_u16(uint16_t *target, const bin_t *data, phys_t *pos, phys_t end, SourceEndian endian) { if (end < 2) return false; if (*pos > (end - 2)) return false; switch (endian) { case SRE_LITTLE: #if __BYTE_ORDER == __LITTLE_ENDIAN *target = data[*pos] | (uint16_t)data[*pos + 1] << 8; #elif __BYTE_ORDER == __BIG_ENDIAN *target = data[*pos + 1] | (uint16_t)data[*pos] << 8; #endif break; case SRE_LITTLE_WORD: #if __BYTE_ORDER == __LITTLE_ENDIAN *target = data[*pos] << 8 | (uint16_t)data[*pos + 1]; #elif __BYTE_ORDER == __BIG_ENDIAN *target = data[*pos + 1] << 8 | (uint16_t)data[*pos]; #endif break; case SRE_BIG_WORD: #if __BYTE_ORDER == __LITTLE_ENDIAN *target = data[*pos + 1] << 8 | (uint16_t)data[*pos]; #elif __BYTE_ORDER == __BIG_ENDIAN *target = data[*pos] << 8 | (uint16_t)data[*pos + 1]; #endif break; case SRE_BIG: #if __BYTE_ORDER == __LITTLE_ENDIAN *target = data[*pos + 1] | (uint16_t)data[*pos] << 8; #elif __BYTE_ORDER == __BIG_ENDIAN *target = data[*pos] | (uint16_t)data[*pos + 1] << 8; #endif break; default: return false; break; } *pos += 2; return true; } /****************************************************************************** * * * Paramètres : target = lieu d'enregistrement de la lecture. [OUT] * * data = flux de données à analyser. * * pos = position courante dans ce flux. [OUT] * * end = limite des données à analyser. * * endian = ordre des bits dans la source. * * * * Description : Lit un nombre non signé sur quatre octets. * * * * Retour : Bilan de l'opération : true en cas de succès, false sinon. * * * * Remarques : - * * * ******************************************************************************/ bool read_u32(uint32_t *target, const bin_t *data, phys_t *pos, phys_t end, SourceEndian endian) { if (end < 4) return false; if (*pos > (end - 4)) return false; switch (endian) { case SRE_LITTLE: #if __BYTE_ORDER == __LITTLE_ENDIAN *target = data[*pos] | (uint32_t)data[*pos + 1] << 8; *target |= data[*pos + 2] << 16 | (uint32_t)data[*pos + 3] << 24; #elif __BYTE_ORDER == __BIG_ENDIAN *target = data[*pos + 3] | (uint32_t)data[*pos + 2] << 8; *target |= data[*pos + 1] << 16 | (uint32_t)data[*pos] << 24; #endif break; case SRE_LITTLE_WORD: #if __BYTE_ORDER == __LITTLE_ENDIAN *target = data[*pos] << 8 | (uint32_t)data[*pos + 1]; *target |= data[*pos + 2] << 24 | (uint32_t)data[*pos + 3] << 16; #elif __BYTE_ORDER == __BIG_ENDIAN *target = data[*pos + 3] << 8 | (uint32_t)data[*pos + 2]; *target |= data[*pos + 1] << 24 | (uint32_t)data[*pos] << 16; #endif break; case SRE_BIG_WORD: #if __BYTE_ORDER == __LITTLE_ENDIAN *target = data[*pos + 3] << 8 | (uint32_t)data[*pos + 2]; *target |= data[*pos + 1] << 24 | (uint32_t)data[*pos] << 16; #elif __BYTE_ORDER == __BIG_ENDIAN *target = data[*pos] << 8 | (uint32_t)data[*pos + 1]; *target |= data[*pos + 2] << 24 | (uint32_t)data[*pos + 3] << 16; #endif break; case SRE_BIG: #if __BYTE_ORDER == __LITTLE_ENDIAN *target = data[*pos + 3] | (uint32_t)data[*pos + 2] << 8; *target |= data[*pos + 1] << 16 | (uint32_t)data[*pos] << 24; #elif __BYTE_ORDER == __BIG_ENDIAN *target = data[*pos] | (uint32_t)data[*pos + 1] << 8; *target |= data[*pos + 2] << 16 | (uint32_t)data[*pos + 3] << 24; #endif break; default: return false; break; } *pos += 4; return true; } /****************************************************************************** * * * Paramètres : target = lieu d'enregistrement de la lecture. [OUT] * * data = flux de données à analyser. * * pos = position courante dans ce flux. [OUT] * * end = limite des données à analyser. * * endian = ordre des bits dans la source. * * * * Description : Lit un nombre non signé sur huit octets. * * * * Retour : Bilan de l'opération : true en cas de succès, false sinon. * * * * Remarques : - * * * ******************************************************************************/ bool read_u64(uint64_t *target, const bin_t *data, phys_t *pos, phys_t end, SourceEndian endian) { if (end < 8) return false; if (*pos > (end - 8)) return false; switch (endian) { case SRE_LITTLE: #if __BYTE_ORDER == __LITTLE_ENDIAN *target = (uint64_t)data[*pos] | (uint64_t)data[*pos + 1] << 8; *target |= (uint64_t)data[*pos + 2] << 16 | (uint64_t)data[*pos + 3] << 24; *target |= (uint64_t)data[*pos + 4] << 32 | (uint64_t)data[*pos + 5] << 40; *target |= (uint64_t)data[*pos + 6] << 48 | (uint64_t)data[*pos + 7] << 56; #elif __BYTE_ORDER == __BIG_ENDIAN *target = (uint64_t)data[*pos + 7] | (uint64_t)data[*pos + 6] << 8; *target |= (uint64_t)data[*pos + 5] << 16 | (uint64_t)data[*pos + 4] << 24; *target |= (uint64_t)data[*pos + 3] << 32 | (uint64_t)data[*pos + 2] << 40; *target |= (uint64_t)data[*pos + 1] << 48 | (uint64_t)data[*pos] << 56; #endif break; case SRE_LITTLE_WORD: #if __BYTE_ORDER == __LITTLE_ENDIAN *target = (uint64_t)data[*pos] << 8 | (uint64_t)data[*pos + 1]; *target |= (uint64_t)data[*pos + 2] << 24 | (uint64_t)data[*pos + 3] << 16; *target |= (uint64_t)data[*pos + 4] << 40 | (uint64_t)data[*pos + 5] << 32; *target |= (uint64_t)data[*pos + 6] << 56 | (uint64_t)data[*pos + 7] << 48; #elif __BYTE_ORDER == __BIG_ENDIAN *target = (uint64_t)data[*pos + 7] << 8 | (uint64_t)data[*pos + 6]; *target |= (uint64_t)data[*pos + 5] << 24 | (uint64_t)data[*pos + 4] << 16; *target |= (uint64_t)data[*pos + 3] << 40 | (uint64_t)data[*pos + 2] << 32; *target |= (uint64_t)data[*pos + 1] << 56 | (uint64_t)data[*pos] << 48; #endif break; case SRE_BIG_WORD: #if __BYTE_ORDER == __LITTLE_ENDIAN *target = (uint64_t)data[*pos + 7] << 8 | (uint64_t)data[*pos + 6]; *target |= (uint64_t)data[*pos + 5] << 24 | (uint64_t)data[*pos + 4] << 16; *target |= (uint64_t)data[*pos + 3] << 40 | (uint64_t)data[*pos + 2] << 32; *target |= (uint64_t)data[*pos + 1] << 56 | (uint64_t)data[*pos] << 48; #elif __BYTE_ORDER == __BIG_ENDIAN *target = (uint64_t)data[*pos] << 8| (uint64_t)data[*pos + 1]; *target |= (uint64_t)data[*pos + 2] << 24 | (uint64_t)data[*pos + 3] << 16; *target |= (uint64_t)data[*pos + 4] << 40 | (uint64_t)data[*pos + 5] << 32; *target |= (uint64_t)data[*pos + 6] << 56 | (uint64_t)data[*pos + 7] << 48; #endif break; case SRE_BIG: #if __BYTE_ORDER == __LITTLE_ENDIAN *target = (uint64_t)data[*pos + 7] | (uint64_t)data[*pos + 6] << 8; *target |= (uint64_t)data[*pos + 5] << 16 | (uint64_t)data[*pos + 4] << 24; *target |= (uint64_t)data[*pos + 3] << 32 | (uint64_t)data[*pos + 2] << 40; *target |= (uint64_t)data[*pos + 1] << 48 | (uint64_t)data[*pos] << 56; #elif __BYTE_ORDER == __BIG_ENDIAN *target = (uint64_t)data[*pos] | (uint64_t)data[*pos + 1] << 8; *target |= (uint64_t)data[*pos + 2] << 16 | (uint64_t)data[*pos + 3] << 24; *target |= (uint64_t)data[*pos + 4] << 32 | (uint64_t)data[*pos + 5] << 40; *target |= (uint64_t)data[*pos + 6] << 48 | (uint64_t)data[*pos + 7] << 56; #endif break; default: return false; break; } *pos += 8; return true; } /****************************************************************************** * * * Paramètres : value = source de la valeur à transcrire. * * size = taille de cette source de données. * * data = flux de données à modifier. [OUT] * * pos = position courante dans ce flux. [OUT] * * end = limite des données à analyser. * * endian = ordre des bits dans la source. * * * * Description : Ecrit un nombre non signé sur n octets. * * * * Retour : Bilan de l'opération : true en cas de succès, false sinon. * * * * Remarques : - * * * ******************************************************************************/ bool _write_un(const bin_t *value, size_t size, bin_t *data, off_t *pos, off_t end, SourceEndian endian) { size_t i; /* Boucle de parcours */ if (end < size) return false; if (*pos > (end - size)) return false; switch (endian) { case SRE_LITTLE: #if __BYTE_ORDER == __LITTLE_ENDIAN memcpy(&data[*pos], value, size); (*pos) += size; #elif __BYTE_ORDER == __BIG_ENDIAN for (i = 0; i < size; i++, (*pos)++) *(data + *pos) = *(value + size - i - 1); #endif break; case SRE_BIG: #if __BYTE_ORDER == __LITTLE_ENDIAN for (i = 0; i < size; i++, (*pos)++) *(data + *pos) = *(value + size - i - 1); #elif __BYTE_ORDER == __BIG_ENDIAN memcpy(&data[*pos], value, size); (*pos) += size; #endif break; default: return false; break; } return true; } /****************************************************************************** * * * Paramètres : target = lieu d'enregistrement de la lecture. [OUT] * * data = flux de données à analyser. * * pos = position courante dans ce flux. [OUT] * * end = limite des données à analyser. * * endian = ordre des bits dans la source. * * * * Description : Lit un nombre hexadécimal non signé sur deux octets. * * * * Retour : Bilan de l'opération : true en cas de succès, false sinon. * * * * Remarques : - * * * ******************************************************************************/ bool strtou8(uint8_t *target, const char *data, size_t *pos, size_t end, SourceEndian endian) { size_t i; /* Boucle de parcours */ if (end < 2) return false; if (*pos > (end - 2)) return false; *target = 0; for (i = 0; i < 2; i++) switch (data[*pos + i]) { case '0' ... '9': *target |= ((data[*pos + i] - '0') << (4 * (1 - i))); break; case 'A' ... 'F': *target |= ((data[*pos + i] + 10 - 'A') << (4 * (1 - i))); break; case 'a' ... 'f': *target |= ((data[*pos + i] + 10 - 'a') << (4 * (1 - i))); break; } *pos += 2; return true; } /****************************************************************************** * * * Paramètres : n = nombre d'octets constituant le nombre à lire. * * data = flux de données à analyser. * * pos = position courante dans ce flux. [OUT] * * end = limite des données à analyser. * * endian = ordre des bits dans la source. * * ... = lieu d'enregistrement de la lecture. [OUT] * * * * Description : Lit un nombre hexadécimal non signé sur n octets. * * * * Retour : Bilan de l'opération : true en cas de succès, false sinon. * * * * Remarques : - * * * ******************************************************************************/ bool _strtoun(uint8_t n, const char *data, size_t *pos, size_t end, SourceEndian endian, ...) { bool result; /* Bilan à renvoyer */ va_list ap; /* Arguments variables */ uint8_t *target8; /* Enregistrement sur 8 bits */ uint16_t *target16; /* Enregistrement sur 16 bits */ uint32_t *target32; /* Enregistrement sur 32 bits */ uint64_t *target64; /* Enregistrement sur 64 bits */ uint8_t i; /* Boucle de parcours #1 */ size_t j; /* Boucle de parcours #2 */ uint8_t tmp; /* Valeur temporaire de 8 bits */ if (end < (n * 2)) return false; if (*pos > (end - (n * 2))) return false; /* Récupération de la destination */ va_start(ap, endian); switch (n) { case 1: target8 = va_arg(ap, uint8_t *); *target8 = 0; target64 = (uint64_t *)target8; break; case 2: target16 = va_arg(ap, uint16_t *); *target16 = 0; target64 = (uint64_t *)target16; break; case 4: target32 = va_arg(ap, uint32_t *); *target32 = 0; target64 = (uint64_t *)target32; break; case 8: target64 = va_arg(ap, uint64_t *); *target64 = 0ull; break; default: va_end(ap); return false; break; } va_end(ap); /* Lecture des données */ result = true; for (i = 0; i < n && result; i++) { tmp = 0; for (j = 0; j < 2 && result; j++) switch (data[*pos + j]) { case '0' ... '9': tmp |= ((data[*pos + j] - '0') << (4 * (1 - j))); break; case 'A' ... 'F': tmp |= ((data[*pos + j] + 10 - 'A') << (4 * (1 - j))); break; case 'a' ... 'f': tmp |= ((data[*pos + j] + 10 - 'a') << (4 * (1 - j))); break; default: result = false; break; } *pos += 2; switch (endian) { case SRE_LITTLE: #if __BYTE_ORDER == __LITTLE_ENDIAN *target64 |= ((uint64_t)tmp) << (8 * i); #elif __BYTE_ORDER == __BIG_ENDIAN *target64 |= ((uint64_t)tmp) << (8 * (n - 1 - i)); #endif break; case SRE_BIG: #if __BYTE_ORDER == __LITTLE_ENDIAN *target64 |= ((uint64_t)tmp) << (8 * (n - 1 - i)); #elif __BYTE_ORDER == __BIG_ENDIAN *target64 |= ((uint64_t)tmp) << (8 * i); #endif break; default: return false; break; } } return result; }