用c++创建一个自定义的exe文件

Creating a CUSTOM exe file in C++

本文关键字:自定义 一个 exe 文件 c++ 创建      更新时间:2023-10-16

我正在制作一个简单的游戏引擎,只是为了体验它。但我意识到,我不知道如何将用户自定义游戏导出为独立的可执行文件。例如(这不是我真正的游戏引擎,它只是为讨论提供了一个简单的参考),假设我们有以下非常简单的代码:

#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!";
    else
        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);
}

在这个例子中,用户可以创建他们自己定制的琐事,然后可以在调用RunGame之后立即测试它。现在我希望能够将他们的游戏与他们提供的琐事信息保存为自己的。exe(基本上它将从调用RunGame开始执行)。我该怎么做呢?

需要明确的是,这并不是关于制作游戏最简单/最快的方法。它正在寻找如何从代码中构建独立的可执行文件。

如果您真的想要将数据存储在。exe本身中:

一个可执行文件有一个头文件,它定义了它的大小,边界和其他有用的东西给操作系统,所以,本质上,操作系统知道代码和数据段在哪里开始和结束,它最终使用这个信息加载到内存中,当它被要求运行。

由于操作系统知道(除了。exe的文件大小)可执行文件的实际结束位置,这也意味着任何粘贴在。exe的"计算"结束(通过头)之后的数据不会对二进制文件产生负面影响。它仍然可以正常加载和执行。

可以滥用此属性将数据连接到可执行文件末尾。

我将留给你这个测试,使用Windows捆绑的写字板应用程序作为其他一些数据的"主机":

  • 转到C:Windows并将write.exe(写字板)复制到另一个文件夹,这样我们就可以在不损坏任何东西的情况下进行实验。

  • 将另一个文件带到该文件夹,任何文件都可以。在我的示例中,数据文件将是名为"myfancyfile.pdf"的PDF文件

  • 现在,打开命令提示符,使用COPY命令将两个文件缝合在一起,确保.exe在前面:

    copy /B write.exe+myfancyfile.pdf mynewprogram.exe
    
    • copy的/B标志表示"二进制复制",所以基本上两个文件都粘贴在一起,没有任何类型的文本或数据转换。
  • 尝试运行"mynewprogram.exe"。意识到它运行正常:-)

用数据自我修改.exe不仅可行,而且不会对功能产生负面影响。话虽如此,它仍然是一种丑陋的持久化数据的方式。

享受编写解决方案的乐趣。

您不希望这样做。更好的方法是将琐事保存为某种自定义格式(例如,.txt, .dat,…)。

游戏只是处理这些数据。

首先考虑。txt文件的格式,例如。

首先假设有一个数字,表示这是哪个条目。其次是问题,然后是答案。这个,你必须自己决定。

<标题>例子trivia-data.txt h1> P_4

现在你已经创建了你的数据,你只需要用你想要的方式来构建你的程序。你应该从文件中读取问题,然后打印出来,比较答案,而不是写入文件。查找 std::ifstream 来读取您的文件。

一开始你可以问你的用户他是想创建一个测验还是玩一个已经存在的测验。


<标题>编辑:

因为这听起来很像作业,所以我只提供一些伪代码。

我会选择这样的方法(伪代码):

print "Would you like to create(c) or play(p) a quiz? Answer(c/p): "
input = get_input() // 'c' or 'p'
if input == 'c'
    // now do what I posted with some loops to create a couple of questions
else
    print "Please provide an URL to the quiz-data you would like to play: "
    url = get_input() // C:/test.txt
    // read in data, print out questions, do comparisons and print answers etc

这比你的方法简单得多,这也使得其他人可以创建测验,而不仅仅是你。

构建可执行文件是非常重要的。您首先需要遵守目标操作系统的ABI,以便它能够找到您的程序的入口点。下一步将决定您的程序如何能够访问系统资源:可能您希望您的可执行文件实现动态链接,以便它可以访问共享库,并且您需要加载所需的各种.dll或.so文件。您需要编写的所有指令将因操作系统而异,您可能需要引入逻辑来检测确切的平台并做出明智的决定,并且您需要针对32位和64位进行更改。

此时,你已经准备好开始为你的游戏发出机器指令了。

一个合理的选择是(就像Unity所做的那样)在你的引擎中提供一个"空白"的可执行文件。您的引擎本身将是一个共享库(.dll或.so),空白可执行文件将只是一个包装器,它加载共享库并调用其中的函数,其中包含指向其数据部分的指针。

生成用户的可执行文件将包括加载适当的空白,对其进行特定于平台的修改,以告诉它您打算提供给它的数据部分的大小,并以适当的格式写入数据。或者,您可以简单地使用一个空白,其中包含您写入值的原始结构的嵌入副本,就像在内存中填充结构一样:

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() {
    run_engine(&gameDef);
}

你将针对你的引擎的共享库存根编译它,并将其作为可执行文件发出,然后你将查找可执行文件格式的平台特定细节,定位"gameDef"在其中的位置。你可以将空白读入内存,并将"gameDef"的定义替换为基于用户输入的定义。

但是许多引擎所做的只是提供或要求用户安装编译器(Unity依赖于c#)。因此,他们不需要调整可执行文件和做所有这些疯狂的平台特定的事情,而是简单地输出一个C/c++程序并编译它。

// 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 failedn";
        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";
    cpp.close();
    if (!invoke_compiler(filename, ".cpp")) {
        std::cerr << "compile failedn";
        return false;
    }
    if (!invoke_linker(filename)) {
        std::cerr << "link failedn";
        return false;
    }
}

如果"RunGame"不是你的引擎的一部分,而是用户提供的,那么你可以将其作为cpp代码的一部分发出。否则,这里的意图是调用你的库。

在Linux下你可以用

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

g++ -Wall -O3 -o ${filename} ${filename}.o -lengine_library
相关文章: