Embedding Big Files in an Executable - Windows - Part 4
Tags: cpp, programming, desktop
Published: 2023-03-19 22:59:49

A file with this size 16515603 in hexadecimal is 0x1302FC. To store large values, up to 4 gigabytes, we will need 4 bytes: 
```
0xFC, 0x02, 0x13, 0x00
```

We will use:
```cpp
  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)
  });
```

**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 << "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;
}

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 << "Erro ao abrir o arquivo " << filePath << " para escrita" << std::endl;  
  } 
  outFile.write(bytFile.data(), bytFile.size());
  outFile.close();

  std::cout << "Arquivo alterado com sucesso" << std::endl;
} 

int main(int argc, char *argv[]){   

  if (argc != 4) {
      std::cout << "Require 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 correctSize = std::stoi(argv[3]); 

  int size = bytFile.size(); 
  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) Update the program.cpp**

As the function is getting too big with too many responsibilities, now let's refactor the code and separate these functions:
```cpp
int getFileNamesize(std::vector<char> bytFile, int size)
std::string getFileName(std::vector<char> bytFile, int size, int fileNameSize)
int getContentSize(std::vector<char> bytFile, int size, int fileNameSize)
std::string getContent(std::vector<char> bytFile, int size, int fileNameSize, int contentSize, int contentStart)
int getContent(std::vector<char> bytFile, int size) 
```
program.cpp
```cpp
#include <fstream>
#include <iostream>
#include <windows.h>
#include <sstream>
#include <string> 
#include <locale>

int assetsCount = 0;
 
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::vector<char> bytFile, int size) {

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

    assetsCount+=1;

    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 content
    int contentStart = contentSize+fileNameSize+3+3;
    std::string content = getContent(bytFile, size, fileNameSize, contentSize, contentStart);     


    // Print big string
    int stringSize = content.size();    
    int maxLineSize = 80; 
    for (int i = 0; i < stringSize; i += maxLineSize) {
        int lineSize = std::min(stringSize - i, maxLineSize);
        std::cout << content.substr(i, lineSize) << std::endl;
    }

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

    return 0;
}

int main(int argc, char *argv[]){   
 
    // Read file
    std::string filename = std::string(argv[0]) + ".exe";
    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;
    //std::cout << "int size : " << contentSize << std::endl;    
     
    // console "pt-BR" accentuation errors
    system("chcp 1252 > nul"); 
    setlocale(LC_ALL, "pt_BR.UTF-8");

    getContent(bytFile, size);

    return 0;
}

```

**4) Compile**

```
g++ program.cpp -o program
```

**5) Exec and get "file size"**

```
program
```
```
file size: 172032
last byte: 00
end
```

**6) Create um big content data3.txt**

Create the file "data3.txt" and add this content:

https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.4/jquery.min.js

To make it easier to verify that everything went right, you can add a "ã" at the beginning and end of this file:

```
ã/*! jQuery v3.6.4 |
```
```
(C.jQuery=C.$=S),S});ã
```

**7) Apply pacth**

```
patch program.exe data3.txt 172032
```

**8) Run program.exe**
```
program
```
```
file size: 261846
last byte: 5e
1-----------------
content name size: 9
content name: data3.txt
content size: 89799
ã/*! jQuery v3.6.4 

(...)

ned"==typeof e&&(C.jQuery=C.$=S),S});ã
-----------------
end
```

**8) Apply a new patch with new size**

```
patch program.exe data3.txt 261846
```

**9) Run program.exe**

First clear console

```
cls
```
```
program
```
Result
```
file size: 351660
last byte: 5e
1-----------------
content name size: 9
content name: data3.txt
content size: 89799
ã/*! jQuery v3.6.4 |

(...)

ned"==typeof e&&(C.jQuery=C.$=S),S});ã
-----------------
2-----------------
content name size: 9
content name: data3.txt
content size: 89799
ã/*! jQuery v3.6.4 |

(...)

ned"==typeof e&&(C.jQuery=C.$=S),S});ã
-----------------
end
```

### Embedding Files in an Executable - SDL/Windows - Part 5
https://dev.to/gurigraphics/embedding-files-in-an-executable-sdlwindows-part-5-2he4