ArcaneLight ArcaneLight - 1 month ago 6x
C++ Question

Creating a CUSTOM exe file in C++

I'm working on a simple game engine just for the experience of it. I've realized, though, that I have no idea how to export the user's custom game as its own standalone executable. For example (this is not my actual game engine, it just provides an easy reference for discussion), suppose we had the following very simple code:

#include "stdafx.h"
#include <iostream>
#include <string>

using namespace std;

void RunGame(string question, string answer)
string submission;
cout << question << endl;
getline(cin, submission);
if (submission == answer)
cout << "Correct!";
cout << "Wrong!";

int main()
string question;
string answer;

cout << "Enter Question:" << endl;
getline(cin, question);
cout << "Enter Answer:" << endl;
getline(cin, answer);

RunGame(question, answer);

In this example the user gets to create their own customized bit of trivia, and then can test it immediately afterwards when RunGame is called. Now I want to be able to save their game with the trivia information they provided as its own .exe (basically it will perform from the call to RunGame onwards). How would I go about doing that?

To be clear, this isn't a question about what is the easiest/fastest way to make a game. It is looking for how to build a standalone, executable file from within code.


Building an executable is non-trivial. You will first need to comply with the target operating systems' ABI so that it can find your program's entry point. The next step will be deciding how your program is going to be able to access system resources: probably you'll want your executable to implement dynamic linking so it can access shared libraries, and you'll need to load the various .dll or .so files you're going to need. All the instructions you'll need to write for this will vary from OS to OS, you may need to introduce logic to detect the exact platform and make informed decisions, and you will need to vary for 32 vs 64 bit.

At this point you're about ready to start emitting the machine instructions for your game.

A reasonable alternative here is (as done by Unity) to provide a "blank" executable with your engine. Your engine itself would be a shared library (.dll or .so) and the blank executable would simply be a wrapper that loads the shared library and invokes a function in it with a pointer to something in it's data section.

Generating your user's executable would comprise loading the appropriate blank, making platform-specific modifications to it to tell it the size of the data section you're intended to provide it with and writing your data in the appropriate format. Or, you could simply have a blank that has an embedded copy of the raw structure into which you write values, just like populating a struct in memory:

struct GameDefinition {
    constexpr size_t AuthorNameLen = 80;
    char author_[AutherNameLen+1];
    constexpr size_t PublisherNameLen = 80;
    char publisher_[PublisherNameLen+1];
    constexpr size_t GameNameLen = 80;
    char name_[GameNameLen+1];
    constexpr size_t QuestionLen = 80;
    constexpr size_t AnswerLen = 80;
    char question_[QuestionLen+1];
    char answer_[AnswerLen+1];

static GameDefinition gameDef;

#include "engine_library.h"  // for run_engine

int main() {

You'd compile this againsst the shared-library stub for your engine, and emit it as an executable, then you'd look up the platform-specific details of the executable format, locate the position of "gameDef" in it. The you'd read the blank into memory, and write it out with the definition of "gameDef" replaced with the one based on user input.

But what many engines do is simply ship or require the user to install a compiler (Unity relies on C#). So instead of having to tweak executables and do all this crazy platform-specific stuff, they simply output a C/C++ program and compile it.

// game-generator
bool make_game(std::string filename, std::string q, std::string a) {
    std::ostream cpp(filename + ".cpp");
    if (!cpp.is_open()) {
        std::cerr << "open failed\n";
        return false;
    cpp << "#include <engine.h>\n";
    cpp << "Gamedef gd(\"" << gameName << "\", \"" << authorName << \");\n";
    cpp << "int main() {\n";
    cpp << "  gd.q = \"" << q << \"\n";
    cpp << "  gd.a = \"" << a << \"\n";
    cpp << "  RunGame(gd);\n";
    cpp << "}\n";

    if (!invoke_compiler(filename, ".cpp")) {
        std::cerr << "compile failed\n";
        return false;
    if (!invoke_linker(filename)) {
        std::cerr << "link failed\n";
        return false;

If "RunGame" is not part of your engine but user-supplied, then you could emit that as part of the cpp code. Otherwise, the intent here is that it's making a call into your library.

Under Linux you might compile this with

g++ -Wall -O3 -o ${filename}.o ${filename}.cpp

and then

g++ -Wall -O3 -o ${filename} ${filename}.o -lengine_library

to link it against your engine's library.