用C ++创建CUSTOM exe文件

时间:2016-09-16 17:36:07

标签: c++

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

#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开始执行)。我该怎么做呢?

要说清楚,这不是一个关于制作游戏最简单/最快方法的问题。它正在寻找如何从代码中构建独立的可执行文件。

3 个答案:

答案 0 :(得分:3)

如果确实想要将数据存储在.exe本身中:

可执行文件有一个标题,用于定义操作系统的大小,边界和其他有用的东西,因此,基本上,操作系统知道代码和数据部分的开始和结束位置,并最终使用此信息加载当要求它运行时.exe到内存。

由于操作系统知道(除了.exe的文件大小)可执行文件实际结束的位置,这也意味着在 .exe的“计算”结束后(通过标题)粘贴的任何数据赢了t对二进制产生负面影响。它仍然会加载并执行得很好。

您可以滥用此属性在可执行文件结束后连接数据。

我会让你接受这个测试,使用Windows的捆绑式写字板应用程序作为其他一些数据的“主机”:

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

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

  • 现在,打开命令提示符并使用COPY命令将两个文件拼接在一起,确保.exe首先出现:

    copy /B write.exe+myfancyfile.pdf mynewprogram.exe
    
    • copy的 / B 标志表示“二进制复制”,因此基本上两个文件都粘贴在一起,没有任何文本或数据转换。
  • 尝试运行“mynewprogram.exe”。意识到它运行得很好: - )

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

为解决方案编写乐趣。

答案 1 :(得分:1)

你不想这样做。更好的方法是以某种自定义格式保存琐事(例如,.txt,.dat,..)。

然后游戏只处理这些数据。

首先考虑一下.txt中的格式。

首先让我们说一个数字,表明这是一个条目。第二个是问题,然后是答案。这个,你必须自己决定。

示例trivia-data.txt

1
How old is actor X from show Y?
32 years
2
...
...
#include <iostream> // std::cout, std::endl
#include <fstream>  // std::ifstream, std::ofstream
using namespace std;
int main()
{
    // create file
    ofstream ofile("trivia-data.txt");

    // define your data
    int num_of_question = 1;
    string question, answer;
    getline(cin, question);
    getline(cin, answer);

    // write your data to the file
    ofile << num_of_question << '\n';
    ofile << question << '\n';
    ofile << answer << '\n';

    // close the file
    ofile.close();

    return 0;
}

现在您已经创建了数据,您只需要以您希望的方式构建您的程序。您应该从文件中读取并打印出问题并比较答案和不答案,而不是写入文件。查看 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

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

答案 2 :(得分:1)

构建可执行文件非常重要。您首先需要遵守目标操作系统的ABI,以便它能够找到您的程序的入口点。下一步将决定您的程序如何能够访问系统资源:可能您希望您的可执行文件实现动态链接,以便它可以访问共享库,并且您需要加载各种.dll或。所以你需要的文件。您需要为此编写的所有指令因操作系统而异,您可能需要引入逻辑来检测确切的平台并做出明智的决策,并且您需要针对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 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";
    cpp.close();

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

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

在Linux下,你可以用

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

然后

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

将其链接到您的引擎库。