Embedding Files in an Executable - SDL/Windows - Part 5
Tags: cpp, programming, desktop, sdl
Published: 2023-03-20 05:24:35

In this example we will test this system using the SDL library.

Let's change the patch.

If using only 2 arguments it will get the size of the file itself
```
patch main.exe data.txt
```

If we use 3 arguments we can return to the original file size
```
patch main.exe data.txt 246784
```

**1) Let's update the patch:**

```cpp
#include <fstream>
#include <iostream>
#include <string> 
#include <vector>
#include <sstream>

std::vector<char> file_read_bin(const std::string& fileName) {
  std::string filePath = fileName;
  std::ifstream file(filePath, std::ios::binary);
  if (file.fail()) {
    std::cerr << "Open file error " << filePath << std::endl;
   } 
 // Read bytes 
  std::vector<char> bytFile((std::istreambuf_iterator<char>(file)),
                             std::istreambuf_iterator<char>());
  file.close(); 
  return bytFile;
}

void file_write_bin(const std::string& filePath, std::vector<char> bytFile) { 
  // Write bytes
  std::ofstream outFile(filePath, std::ios::binary);
  if (outFile.fail()) {
    std::cerr << "Open file error " << filePath << " to write" << std::endl;  
  } 
  outFile.write(bytFile.data(), bytFile.size());
  outFile.close();

  std::cout << "File sucessfull updated" << std::endl;
} 

int main(int argc, char *argv[]){   
 
  int correctSize = 0;

  if (argc < 2) {
      std::cout << "Require 2 or 3 args: program.exe data.txt 163857\n";
      return 1;
  }

  const char* program_file = argv[1];
  const char* data_file = argv[2];

  std::vector<char> bytFile = file_read_bin( program_file );
  std::vector<char> bytText = file_read_bin( data_file );

  int size = bytFile.size(); 

  if(argc == 3) {
    correctSize = size;    
  }else if(argc == 4) {
    correctSize = std::stoi(argv[3]);
  }

  int diff = size - correctSize;
 
  // Logs
  std::cout << "size: " << size << "\n";
  std::cout << "correctSize: " << correctSize << "\n";

  // Remove old code
  if(size > correctSize ){
    bytFile.erase(bytFile.end() - diff, bytFile.end() );
  }

  // Add content
  bytFile.insert(bytFile.end(), bytText.begin(), bytText.end());

  // Add content size
  bytFile.insert(bytFile.end(), {
    static_cast<char>((bytText.size() >> 24) & 0xFF),
    static_cast<char>((bytText.size() >> 16) & 0xFF),
    static_cast<char>((bytText.size() >> 8) & 0xFF),
    static_cast<char>((bytText.size() >> 0) & 0xFF)
  });
 
  // Add file name
  std::string data_filename_str(data_file);
  data_filename_str+="\0";
  bytFile.insert(bytFile.end(), data_filename_str.c_str(), data_filename_str.c_str() + data_filename_str.size() );
 
  // Add name size
  bytFile.insert( bytFile.end(), data_filename_str.size() );  

  // Add symbol exist new file
  std::string symbol = "^";
  bytFile.insert(bytFile.end(), symbol.c_str(), symbol.c_str() + symbol.size() );
 
  // Rewrite 
  file_write_bin(program_file, bytFile);

  return 0;
}
```

**2) Compile**

```
g++ patch.cpp -o patch
```

**3) Create patchFiles lib**

Now separate that system. Put in a file "patchFiles.h"

We want it to be insanely simple to use.

patch! patch! end!

With few lines:

```cpp
#include "./patchFiles.h"

// Get files
std::map<std::string, FileInfo> files = getFiles(); 

//Use
std::cout << files["data.txt"].contentString;  
```

For this we will use a map:
```cpp
struct FileInfo {
    int fileNameSize;
    std::string fileName;
    int contentSize;
    int contentStart;
    int gap;
    std::string contentString;
    std::vector<char> contentVector;    
};

std::map<std::string, FileInfo> files; 
```

patchFiles.h
```cpp
#include <map>
#include <string>
#include <vector>
#include <filesystem>

#include <fstream>
#include <iostream>
#include <windows.h>
#include <sstream>
#include <string> 
#include <locale>

struct FileInfo {
    int fileNameSize;
    std::string fileName;
    int contentSize;
    int contentStart;
    int gap;
    std::string contentString;
    std::vector<char> contentVector;    
};

int assetsCount = 0;
std::map<std::string, FileInfo> files; 

std::string getFileName(){
    char filename[MAX_PATH];
    GetModuleFileNameA(NULL, filename, MAX_PATH);
    std::string filepath(filename);
    std::string basename = filepath.substr(filepath.find_last_of("\\/") + 1);
    return filename;
}

std::vector<char> file_read_bin(const std::string& fileName) {
  std::string filePath = fileName;
  std::ifstream file(filePath, std::ios::binary);
  if (file.fail()) {
    std::cerr << "Erro ao abrir o arquivo " << filePath << std::endl;
   } 
 // Read bytes 
  std::vector<char> bytFile((std::istreambuf_iterator<char>(file)),
                             std::istreambuf_iterator<char>());
  file.close(); 
  return bytFile;
}

std::string getHexValue(const std::vector<char>& bytFile, int size, int byteCount) {
    std::stringstream ss;
    ss << std::hex << std::setfill('0') << std::setw(2) << static_cast<unsigned int>(static_cast<unsigned char>(bytFile[size - byteCount]));
    return ss.str();
}

int hexToInt(std::string hexStr) {
    return std::stoi(hexStr, nullptr, 16);
}

int getFileNamesize(std::vector<char> bytFile, int size){

    std::string sizeHex = getHexValue(bytFile, size, 2); 
    return hexToInt( sizeHex );
}

std::string getFileName(std::vector<char> bytFile, int size, int fileNameSize){

    std::string fileName = "";
    int startfileName = fileNameSize + 2;
    for (int i = startfileName; i > 2; i--) { 
        std::string byteInStringFormat;
        byteInStringFormat.push_back(static_cast<char>(bytFile[size - i])); // convert byte to char
        fileName += byteInStringFormat;
    } 
    return fileName;
}

int getContentSize(std::vector<char> bytFile, int size, int fileNameSize){

    std::string byte_24 = getHexValue(bytFile, size, fileNameSize + 3);
    std::string byte_16 = getHexValue(bytFile, size, fileNameSize + 4);
    std::string byte_08 = getHexValue(bytFile, size, fileNameSize + 5);
    std::string byte_00 = getHexValue(bytFile, size, fileNameSize + 6);

    std::stringstream ss;   
    ss << std::hex << byte_00 << byte_08 << byte_16 << byte_24;
    int contentSize;
    ss >> contentSize;
    return contentSize;
}

std::string getContent(std::vector<char> bytFile, int size, int fileNameSize, int contentSize, int contentStart){

    std::string content = "";

    for (int i = contentStart; i > fileNameSize+3+3; i--) { 
        std::string byteInStringFormat;
        byteInStringFormat.push_back(static_cast<char>(bytFile[size - i])); // convert byte to char 
        content += byteInStringFormat;
    }

    return content;
}

int getContent(std::map<std::string, FileInfo>& files, const std::vector<char>& bytFile, int size) {

    if( bytFile[size - 1] != '^') { // Exist new file?
        std::cout << "end" << "\n";    
        return 0; 
    }

    std::cout << assetsCount << "-----------------" << "\n"; 

    // Get file name size in last byte 
    int fileNameSize = getFileNamesize(bytFile, size);
    std::cout << "content name size: " << fileNameSize << "\n";
     
    // Get file name in last byte 
    std::string fileName = getFileName(bytFile, size, fileNameSize);
    std::cout << "content name: " << fileName << "\n"; 

    // Get content size in 4 bytes
    int contentSize = getContentSize(bytFile, size, fileNameSize);
    std::cout << "content size: " << contentSize << "\n";   


    // Get contentStart from final of file 
    int contentStart = contentSize+fileNameSize+3+3;
    int gap = contentStart - contentSize;
    
    // Get content
    std::string contentString = "";   
    std::vector<char> contentVector;

    if(fileName.find(".txt") != fileName.npos){

      contentString = getContent(bytFile, size, fileNameSize, contentSize, contentStart);

    }else if(fileName.find(".png") != fileName.npos){

        for(int i = bytFile.size() - contentSize - gap; i < bytFile.size() + gap; i++) {
            contentVector.push_back(bytFile[i]);
        }
    }

    files[ fileName ] = {fileNameSize, fileName, contentSize, contentStart, gap, contentString, contentVector};
    assetsCount+=1;

    // End
    int final = size - (contentStart + 1);
    std::cout << "-----------------" << "\n"; 
    
    getContent(files, bytFile, final+1); // get new file

    return 0;
}

std::map<std::string, FileInfo> getFiles() {

    // Read file  
    std::string filename = getFileName();
    std::vector<char> bytFile = file_read_bin( filename ); 
    int size = bytFile.size(); 

    // Logs
    std::cout << "file size: " << size << std::endl;
    std::cout << "last byte: " << getHexValue(bytFile, size, 1) << std::endl;
    
    getContent(files, bytFile, size); 
 
    return files;
}
```

**4) Download SDL**

Download and export inside the project folder:

SDL2_image-devel-2.6.3-mingw.zip
https://github.com/libsdl-org/SDL_image/releases/download/release-2.6.3/SDL2_image-devel-2.6.3-mingw.zip

SDL2-devel-2.26.4-mingw.zip
https://github.com/libsdl-org/SDL/releases/download/release-2.26.4/SDL2-devel-2.26.4-mingw.zip

You can leave the folders with these same names.
```
SDL2_image-devel-2.6.3-mingw
SDL2-devel-2.26.4-mingw
```

We're not even going to install it. Let's use it anyway.

Just grab these two files from bin folders and drop them in the project directory:
```
SDL2.dll
SDL2_image.dll
```


**5) Show images**

I took two free images from Pexels and adjusted the size: 1280x720

![Image app](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/899pvtvcwx2zney81vr4.png)

![Image app](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/agy0ayu8ul5xl78k8yzh.png)

Download these images. Rename one of the images to "bg.png"
https://dev-to-uploads.s3.amazonaws.com/uploads/articles/899pvtvcwx2zney81vr4.png
https://dev-to-uploads.s3.amazonaws.com/uploads/articles/agy0ayu8ul5xl78k8yzh.png

Also create a file: data.txt
Content: 123

Now just import our lib and use:

main.cpp
```cpp
 
#include <SDL.h>
#include <SDL_image.h>

#include "./patchFiles.h"
 
int main(int argc, char *argv[]){    

    // Get assets
    std::map<std::string, FileInfo> files = getFiles(); 

    // Use
    std::vector<char> bytImage = files["bg.png"].contentVector;
    std::cout << files["data.txt"].contentString; 
 
    SDL_Init(SDL_INIT_VIDEO);

    SDL_Window* window = SDL_CreateWindow("App", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, SDL_WINDOW_SHOWN);
    SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, 0);
    SDL_RWops* rwops = SDL_RWFromMem(bytImage.data(), bytImage.size());
    SDL_Surface* surface = IMG_Load_RW(rwops, 1);  
    SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface);
    
    SDL_FreeSurface(surface);
   
    bool quit = false;
    while (!quit) {
        SDL_Event event;
        while (SDL_PollEvent(&event)) {
            switch (event.type) {
                case SDL_QUIT:
                    quit = true;
                    break;
                case SDL_KEYDOWN:
                    if (event.key.keysym.sym == SDLK_ESCAPE) {
                        quit = true;
                        break;
                    }
            }
        }

        SDL_RenderClear(renderer);
        SDL_RenderCopy(renderer, texture, NULL, NULL);
        SDL_RenderPresent(renderer);
    }

    SDL_DestroyTexture(texture);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();

    return 0;  
}

```

**6) Compile**

Let's compile using all of that, no makefile or anything:
```
g++ -std=c++17 main.cpp -o main.exe -I.\ -I.\SDL2-devel-2.26.4-mingw\SDL2-2.26.4\x86_64-w64-mingw32\include\SDL2 -I.\SDL2_image-devel-2.6.3-mingw\SDL2_image-2.6.3\x86_64-w64-mingw32\include\SDL2 -L.\SDL2_image-devel-2.6.3-mingw\SDL2_image-2.6.3\x86_64-w64-mingw32\lib -L.\SDL2-devel-2.26.4-mingw\SDL2-2.26.4\x86_64-w64-mingw32\lib -w -Wl,-subsystem,console -lmingw32 -lSDL2main -lSDL2 -lSDL2_image
```

**7) Now Patch**

Now let's add the assets in this recipe:

```
patch main.exe data.txt
patch main.exe bg.png
```

**8) Run**
Run the program:

```
main
```

![Image app](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/shikmp9b1pmnojvnov2f.png)

**9) Test**
Now let's test if this all really works.
Let's change the assets without having to compile.

Create a folder and drop the image there or delete it.
Rename the other image to "bg.png"
Change the content of data.txt to "0987654321" or whatever.

There in the prompt you can read what was the first file size.
```
> patch main.exe data.txt
size: 246784
correctSize: 246784
```

Let's make the executable return to this size using a third argument:

```
patch main.exe data.txt 246784
patch main.exe bg.png
```

**8) Run**
Run the program:

```
main
```

![Image app](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9j4a6pl7zncfp7j358gf.png)



## Negative aspects

This method can be useful in repetitive tests to save compilation time. For example, a game engine that needs to render games over and over again as quickly as possible. Since it is quite tedious to constantly wait for the compilation.

However, it may not be appropriate to distribute this modified file.

Antiviruses can report it as a suspicious file or false positives:

**Modified** - 1 security vendor and no sandboxes flagged this file as malicious
https://www.virustotal.com/gui/file/f0f6cdbe71d7323e535cb333aa3b6592b7fa3b2623d7013246f19aa2e271a60d

**Normal** - No security vendors and no sandboxes flagged this file as malicious
https://www.virustotal.com/gui/file/099db7271ae6ba2014e86a5666d0602807e4ede039e02e8c65762f472280014d




### Embedding Files in an Executable - Icons & Details - Part 6
https://dev.to/gurigraphics/embedding-files-in-an-executable-icons-details-part-6-1j9l