用c++创建一个自定义的exe文件
Creating a CUSTOM exe file in 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开始执行)。我该怎么做呢?
需要明确的是,这并不是关于制作游戏最简单/最快的方法。它正在寻找如何从代码中构建独立的可执行文件。
如果您真的想要将数据存储在。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
- 如何编写一个接受如下断言消息的自定义断言函数:assert(false) << "assertio
- C++自定义流操纵器,用于更改流上的下一个字符串
- 编译一个自定义的tf操作,其中输入是5d张量
- 带有自定义deleter的std::unique_ptr对象的大小(一个由ref捕获的lambda)
- C++ 一个自定义类字符串,将其分配给 C 样式字符串
- C++,当函子不是一个选项时,我如何编写带有自定义函数调用的模板化 RAII 包装器?
- 我写了一个自定义的咖啡层.但是在训练期间,它说“**层不需要向后计算”
- 尝试自定义一个函数来对不同种类元素的向量进行排序
- 创建一个包含对 <int、自定义类的最小堆>
- 编译自定义咖啡层时,LevelDB 中出现一个奇怪的错误
- QlineEdit:显示一个处理过的文本,而不是输入的文本,而是保留它(自定义回声模式)
- HRESULT:将自定义代码与系统一个区分开
- 我正在制作一个自动化器,并且我需要自定义它才能在将鼠标钥匙放下时单击
- glib:在另一个线程上处理自定义 GMainContext* 循环,不引发信号处理程序
- 如何设置此视觉工作室 (2015) 自定义生成步骤(工具?基本上,我想要一个修改头文件(c ++)的预处理器步骤
- 这个递归类需要一个自定义析构函数?
- 写一个最小的自定义操作员:std :: Sort需要std :: __ lg为我的类型解释
- 我正在声明一个自定义优先级队列,包括 pair<pair<int,int>int >,如何清除它?
- 我正在制作一个自动点击器,我需要自定义它以仅在按住左键单击时才单击
- C 例外 - 每个库或自定义一个