Pygame游戏开发之五
小试牛刀
大部分游戏都会对图像文件进行加密,这样你就很难看到它的图片信息,游戏就更加添加了神秘感。有加密自然有解密,所谓山外青山楼外楼,再高的加密手段都可以被破解,加密只是提高技术门槛,让一部分人看不到你的图片资源。
在进行加密之前,让我们首先把文件的结构组织一下,用文件夹来管理各类文件。
root/
dat/
Animation/
Background/
Control/
Monster/
Player/
img/
Animation/
Background/
Control/
Monster/
Player/
snd/
src/
Tools/
其中root表示游戏的根目录,img和dat分别表示加密前和加密后的图片文件的目录,其下有相同的目录结构,snd用于存放声音文件,src存放的是后缀是.py、.pyc等的python源文件。并且我们将加密的程序放在Tools目录下。
首先我们要大致了解加密的过程,所谓加密就是先将数据以二进制的形式从文件中读进来,然后采用适当的加密算法将数据变成另外一种形式的数据,再存到文件中,这样原本的文件格式就被当前数据替换掉了。这里我们是对img中所有的后缀为png的文件加密,然后存到dat文件中,因为要对大批量文件进行处理,最好的方法是采用批处理。
我们在Tools文件夹下建立一个Process.bat的批处理文件,并且对其进行编辑,写下以下命令:
1 cd ../../img/
2 dir /s /b *.png > ../src/Tools/FileName.txt
3 cd ..
4 xcopy /s /t img dat
5 cd src/Tools/
6 encode.exe FileName.txt img dat
命令cd后跟一个目录表示改变当前目录,如果跟的是“..”则表示跳到当前目录的父目录下,例如第1句表示从Tools/目录连跳两层到root下并且进入img目录中,然后我们利用第2条命令将所有需要加密的文件的文件名输出到Tools目录下的FileName.txt文件中。
dir *.png > file 表示将当前文件下所有后缀为png的文件的文件信息输出到file文件中。但是它包含文件信息不止文件名,所以可以采用/b开关,表示采用空格式(Blank,没有标题信息或摘要),并且要求当前目录以及子目录的png文件都要输出,只要添加/s开关即可。
然后我们回到root文件夹下,将img下的子文件夹全部复制到dat文件夹下,但是不复制文件,可以采用xcopy命令来实现。
最后调用encode.exe将FileName.txt文件中列出的文件加密后输出到dat文件夹下。encode.exe是我自己写的一个加密的小程序,这里为了方便我用C语言来实现。
代码如下:
#include <string>
#include <vector>
using namespace std;
#define key 367
FILE *fpSourceFile;
FILE *fpReadFile, *fpWriteFile;
char szReadFileName[ MAX_PATH ];
vector <char> vec;
string Replace(string Src, string from, string to) {
string Tmp;
string::size_type nCount = Src.find(from);
if(nCount != string::npos) {
Tmp = Src.substr(0, nCount);
Tmp = Tmp + to + "\\" + Src.substr(nCount + from.size() + 1);
nCount = Tmp.find(".");
if(nCount != string::npos)
Tmp = Tmp.substr(0, nCount+1);
return Tmp + "zty";
}
return "";
}
int main(int argc, char* argv[]) {
int i;
if(argc == 4) {
fpSourceFile = fopen(argv[1], "rt");
while(fgets(szReadFileName, MAX_PATH, fpSourceFile)) {
szReadFileName[ strlen(szReadFileName) - 1 ] = '\0';
fpReadFile = fopen(szReadFileName, "rb");
if(fpReadFile) {
fseek(fpReadFile, 0, SEEK_END);
long nFileSize = ftell(fpReadFile);
fseek(fpReadFile, 0, SEEK_SET);
vec.resize(nFileSize);
fread((void *)&vec[0], nFileSize, 1, fpReadFile);
for(i = 0; i < vec.size(); i++) {
vec[i] = (vec[i] ^ key);
}
fclose(fpReadFile);
string outFile = Replace(string(szReadFileName), string(argv[2]), string(argv[3]));
fpWriteFile = fopen(outFile.c_str(), "wb");
fwrite((void *)&vec[0], nFileSize, 1, fpWriteFile);
fclose(fpWriteFile);
}
}
fclose(fpSourceFile);
}else {
printf("请运行Process.bat文件\n");
}
return 0;
}
起初,读入的数据是存到vec这个向量中的,然后我们通过遍历vec的每一个字节对数据进行加密,加密的方法很多,这里采用最简单的异或(二进制位相同为0,不同为1)方式,这样解密也比较简单。来看一个例子:
对于102(二进制表示为1100110),我们将它和给定的key(十进制367、二进制表示为101101111)值进行异或,如下:
001100110 (102)
101101111 (367)
—————
100001001 (265)
原本的数据102就转化成了和原先看似无关的数据265,如此一来,将所有的数据加密完毕后,原本的图像文件的文件头信息也被毁了,根本无法用常用的图像工具打开。加密的目的就达到了。当然如果加密完后连你自己也不知道怎么解密,那么这件事情就没意义了…-_-|||,所以我们还要对加密好数据进行解密,当然这要在程序中控制。
这个只需将原先的load_image函数进行一个修改即可。
def load_image(file) :
do_load()
return Surface
do_load()函数首先要用二进制方式读取file文件,并且通过read()接口得到文件中所有的二进制数据,通过tell()接口得到文件的大小。
然后利用以下的循环就可以将数据进行解密了。
i = 0
data = []
while i < filesize :
data.append( chr( (ord(filedata[i]) ^ key) % 256 ) )
i += 1
data = ''.join(data)
在这里因为用的是异或操作,所以解密和加密的过程是一样的,因为异或满足结合律,一个数异或上它本身等于0,任何数异或0等于它本身。于是可以这样想,某个数据异或了key(加密的过程),再异或一次key(解密的过程),就好比:Num^key^key = Num^(key^key) = Num^0=Num这样原先的数据就恢复了。
因为python中没有字符的概念,我们可以通过ord(‘x’) 得到’x’的ASCII码,进行解密操作后,再用chr(num)得到num的字符形式,最后将所有的字符数据通过join接口连接到一起变成数据流,然后通过write接口将它暂存到buf.png文件中,再调用pygame.image.load('buf.png')将它读入,就得到了file对应的Surface。(未完待续)