Je cherche un moyen d'incorporer facilement toutes les données binaires externes dans une application C/C++ compilée par GCC.
Un bon exemple de ce que j'aimerais faire est de gérer le code du shader - je peux simplement le conserver dans des fichiers source comme const char* shader = "source here";
mais c'est extrêmement peu pratique.
Je voudrais que le compilateur le fasse pour moi: lors de la compilation (étape de liaison), lire le fichier "foo.bar" et lier son contenu à mon programme, afin que je puisse accéder au contenu en tant que données binaires à partir du code.
Peut être utile pour les petites applications que j'aimerais distribuer sous forme de fichier .exe unique.
Est-ce que GCC prend en charge quelque chose comme ça?
Il y a plusieurs possibilités:
utiliser la capacité de ld pour transformer n'importe quel fichier en objet ( incorporer des blobs binaires en utilisant gcc mingw ):
ld -r -b binary -o binary.o foo.bar # then link in binary.o
utilisez un utilitaire bin2c
/bin2h
pour transformer n'importe quel fichier en un tableau d'octets ( Intégrer l'image dans le code, sans utiliser la section des ressources ou des images externes )
Mise à jour: Voici un exemple plus complet de la façon d'utiliser les données liées à l'exécutable en utilisant ld -r -b binary
:
#include <stdio.h>
// a file named foo.bar with some example text is 'imported' into
// an object file using the following command:
//
// ld -r -b binary -o foo.bar.o foo.bar
//
// That creates an bject file named "foo.bar.o" with the following
// symbols:
//
// _binary_foo_bar_start
// _binary_foo_bar_end
// _binary_foo_bar_size
//
// Note that the symbols are addresses (so for example, to get the
// size value, you have to get the address of the _binary_foo_bar_size
// symbol).
//
// In my example, foo.bar is a simple text file, and this program will
// dump the contents of that file which has been linked in by specifying
// foo.bar.o as an object file input to the linker when the progrma is built
extern char _binary_foo_bar_start[];
extern char _binary_foo_bar_end[];
int main(void)
{
printf( "address of start: %p\n", &_binary_foo_bar_start);
printf( "address of end: %p\n", &_binary_foo_bar_end);
for (char* p = _binary_foo_bar_start; p != _binary_foo_bar_end; ++p) {
putchar( *p);
}
return 0;
}
Mise à jour 2 - Obtenir la taille de la ressource: je n'ai pas pu lire correctement la taille _binary_foo_bar_size. Au moment de l'exécution, gdb me montre la bonne taille de la ressource texte en utilisant display (unsigned int)&_binary_foo_bar_size
. Mais l'assigner à une variable a toujours donné une mauvaise valeur. Je pourrais résoudre ce problème de la manière suivante:
unsigned int iSize = (unsigned int)(&_binary_foo_bar_end - &_binary_foo_bar_start)
C'est une solution de contournement, mais cela fonctionne bien et n'est pas trop laid.
En plus des suggestions déjà mentionnées, sous linux, vous pouvez utiliser l'outil de vidage hexadécimal xxd, qui a une fonctionnalité pour générer un fichier d'en-tête C:
xxd -i mybinary > myheader.h
Le .incbin
Directive GAS peut être utilisée pour cette tâche. Voici une bibliothèque sous licence totalement gratuite qui s'enroule autour d'elle:
https://github.com/graphitemaster/incbin
Récapituler. La méthode incbin est comme ça. Vous avez un fichier Assembly thing.s que vous compilez avec gcc -c thing.s
.section .rodata
.global thing
.type thing, @object
.align 4
thing:
.incbin "meh.bin"
thing_end:
.global thing_size
.type thing_size, @object
.align 4
thing_size:
.int thing_end - thing
Dans votre code c ou cpp, vous pouvez le référencer avec:
extern const char thing[];
extern const char* thing_end;
extern int thing_size;
Vous liez donc le .o résultant au reste des unités de compilation. Le crédit est dû à @John Ripley avec sa réponse ici: C/C++ avec GCC: ajouter statiquement des fichiers de ressources à l'exécutable/bibliothèque
Mais ce qui précède n'est pas aussi pratique que ce qu'incbin peut vous donner. Pour accomplir ce qui précède avec incbin, vous n'avez pas besoin d'écrire d'assembleur. Juste ce qui suit fera l'affaire:
#include "incbin.h"
INCBIN(thing, "meh.bin");
int main(int argc, char* argv[])
{
// Now use thing
printf("thing=%p\n", gThingData);
printf("thing len=%d\n", gThingSize);
}
Vous pouvez le faire dans un fichier d'en-tête:
#ifndef SHADER_SRC_HPP
#define SHADER_SRC_HPP
const char* shader= "
//source
";
#endif
et il suffit de l'inclure.
Une autre façon est de lire le fichier shader.