web-dev-qa-db-fra.com

Comment incorporer un fichier dans un exécutable?

J'ai un petit exécutable de démonstration écrit en C++ qui ne dépend que d'une image PNG de 5 ko chargée avant de pouvoir s'exécuter, qui est utilisée pour un texte de pixel que j'ai créé. À cause de ce fichier, je devrais distribuer une archive Zip au lieu d’un seul fichier exécutable, ce qui crée suffisamment de friction entre le téléchargement et le "jeu" pour que, à mon avis, certains n’en aient pas la volonté.

Ma question est la suivante: est-il possible d'incorporer le fichier PNG (et tout autre fichier réellement) dans l'exécutable ou le code source de sorte qu'il s'agisse d'un fichier unique et que l'exécutable puisse l'utiliser?

J'ai la possibilité d'analyser le fichier PNG en tant que flux d'octets, il n'a donc pas besoin d'être converti en données de pixels.

Merci d'avance! (Il existe d'autres questions portant un titre similaire, mais elles et leurs réponses semblent aborder des problèmes plus spécifiques et n'ont pas été très utiles)

edit : Le compilateur est Visual C++ 2010 et ceci est sous Windows (bien que je veuille éviter les utilitaires spécifiques à Windows pour cela)

edit2 : La réponse d'Alf apparaissait comme la méthode la plus portable. J'ai donc rapidement écrit une fonction permettant d'analyser le fichier PNG dans un fichier TXT ou en-tête pouvant être lu comme un tableau unsigned char. Dans ce formulaire, il semble être identique au fichier PNG lui-même, mais mon chargeur png n'acceptera pas le tableau. Lors du chargement de la mémoire, l’analyseur PNG utilise un (void * buffer, size_t length) s’il le faut.

Le code si vous voulez voir, mais j'accepterai quand même d'autres réponses si vous pensez qu'elles sont meilleures que cette méthode:

void compileImagePNGtoBinary(char * filename, char * output){

    FILE * file = fopen(filename, "rb");
    FILE * out = fopen(output, "w");

    unsigned char buffer[32];
    size_t count;
    fprintf(out, "#pragma once \n\n static unsigned char TEXT_PNG_BYTES[] = { ");
    while(!feof(file)){
            count = fread(buffer, 1, 32, file);

            for(int n = 0; n < count; ++n){
                    fprintf(out, "0x%02X, ", buffer[n]);
            };
    };
    fprintf(out, "};");
    fclose(file);
    fclose(out);

};

Final Edit : ImageMagick qu'Alf a également mentionné a fait exactement ce dont j'avais besoin, merci!

23
Anne Quinn

Un moyen portable est de définir une fonction comme

typedef unsigned char Byte;

Byte const* pngFileData()
{
    static Byte const data =
    {
        // Byte data generated by a helper program.
    };
    return data;
}

Ensuite, tout ce que vous avez à faire est d’écrire un petit programme d’aide qui lit le fichier PNG au format binaire et génère le texte d’initialisation entre accolades C++. Edit : @awoodland a souligné dans un commentaire à la question que ImageMagick a un si petit programme d'aide…

Bien entendu, pour un programme spécifique à Windows, utilisez plutôt le schéma de ressources Windows ordinaire.

Vive & hth.,

10

Regardez XD:

http://www.fourmilab.ch/xd/

Enfin, xd peut lire un fichier binaire et émettre une déclaration de données en langage C contenant les données du fichier. Ceci est pratique lorsque vous souhaitez incorporer des données binaires dans des programmes C.

Personnellement, j'utiliserais des ressources pour Windows, mais si vous avez besoin d'un moyen réellement portable qui ne nécessite pas de connaître le format de l'exécutable, c'est la voie à suivre. PNG, JPG, peu importe ...

9
Roddy

Base64 encode le fichier et le met dans une chaîne quelque part dans votre code;)

5
fabrizioM

Cela dépend du format de l'exécutable, ce qui signifie intrinsèquement dépendant du système d'exploitation/compilateur. Windows propose pour cela le système de ressources mentionné dans cette question .

4
Puppy

Vous pouvez incorporer n’importe quel fichier arbitraire dans les ressources de votre programme: (MSDN), ressource définie par l’utilisateur .

Une déclaration de définition de ressource définie par l'utilisateur définit une ressource contenant des données spécifiques à l'application. Les données peuvent avoir n'importe quel format et peuvent être définies soit comme contenu d'un fichier donné (si le paramètre filename est donné), soit comme une série de nombres et de chaînes (si le bloc de données brutes est spécifié).

nameID typeID filename

Le nom de fichier spécifie le nom d'un fichier contenant les données binaires de la ressource. Le contenu du fichier est inclus en tant que ressource. RC n'interprète aucune donnée binaire. Il incombe au programmeur de s'assurer que les données sont correctement alignées pour l'architecture de l'ordinateur cible.

Une fois que vous avez terminé, vous pouvez utiliser la fonction LoadResource pour accéder aux octets contenus dans le fichier.

3
Mark Ransom

Sur Linux, je l'utilise. Il est basé sur quelques exemples que j'ai trouvés en essayant de faire des démos 4k, bien que légèrement modifiés. Je crois que cela peut également fonctionner sur Windows, mais pas avec l’assemblage en ligne VS par défaut. Ma solution consiste à #définir une macro pour utiliser ce code ou le système de ressources Windows suggéré par @MarkRansom (assez pénible de fonctionner, mais fonctionne éventuellement).

//USAGE: call BINDATA(name, file.txt) and access the char array &name.

#ifndef EMBED_DATA_H
#define EMBED_DATA_H

#ifdef _WIN32
//#error The VS ASM compiler won't work with this, but you can get external ones to do the trick
#define BINDATA #error BINDATA requires nasm
#else

__asm__(
".altmacro\n" \
".macro binfile p q\n" \
"   .global \\p\n" \
"\\p:\n" \
"   .incbin \\q\n" \
"\\p&_end:\n" \
"   .byte 0\n" \
"   .global \\p&_len\n" \
"\\p&_len:\n" \
"   .int(\\p&_end - \\p)\n" \
".endm\n\t"
);

#ifdef __cplusplus
    extern "C" {
#endif

#define BINDATA(n, s) \
__asm__("\n\n.data\n\tbinfile " #n " \"" #s "\"\n"); \
extern char n; \
extern int n##_len;

#ifdef __cplusplus
    }
#endif

#endif

#endif
0
jozxyqk