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。(未完待續)