作為一款 2001 年發行的老游戲,封包算法應該不會很復雜才對,抱著這樣想法的博主,嘗試著去分析游戲資源包的封包格式,最後成功將資源解包,下面我們來看看雙星物語的游戲資源包封包格式;
游戲資源包以 dat 作為擴展名,一共有兩個,分別是 wav.dat 和 BIN.dat,其中 wav.dat 體積較小,先從它下手,用十六進制編輯器打開後,可以看到整齊的文件頭部,經過觀察發現,整個資源包以【包頭】【文件類型信息】【文件信息】【文件數據】這樣子的結構組織而成;
首先是【包頭】,大小為 8 字節,前 4 個字節固定為十進制數據 12345678(十六進制 0x00BC614E),後 4 個字節為資源包裡面所包含的文件類型數量,比如:wav、scr、dec、pal、txm、chr 等等;
接下來是【文件類型信息】,大小為 12 字節,前 4 個字節為該類型文件的文件擴展名,中間 4 字節為該種類文件的文件數量,後 4 個字節為這類文件信息在資源包裡面的偏移值;
然後是【文件信息】,大小 16 字節,前 8 個字節是文件名,不包含擴展名,中間 4 個字節是文件大小,後面 4 個字節是文件數據在資源包裡面的偏移值;
最後是【文件數據】,這些數據按照【文件信息】裡面的描述排列在一起;
根據上面描述,我們可以整理出下面三個最重要的結構體:
// 包頭 struct PACK_HEADER { __int32 nSig; __int32 nFileTypeNumber; }; // 文件類型信息 struct FILETYPE_INFO { char szType[4]; __int32 nOffset; __int32 nFileNumber; }; // 文件信息 struct FILE_INFO { char szFileName[8]; __int32 nSize; __int32 nOffset; };
接下來,只需要按照上面描述的數據結構去讀取資源包,就可以提取出所有的資源文件,遺憾的是,解包之後得到的資源文件,除了 wav 音頻文件之外,其它文件都經過了一定的處理;
下面是源代碼,用 Visual Studio 創建一個 Win32 控制台工程,然後將生成的 exe 文件放置到與 wav.dat、BIN.dat 兩個文件同級的目錄中,雙擊即可解包,解包時會同時建立同名的目錄;
#include <windows.h> #include <stdio.h> struct PACK_HEADER { inline PACK_HEADER() { memset(this, 0, sizeof(PACK_HEADER)); } __int32 nSig; __int32 nFileTypeNumber; }; struct FILETYPE_INFO { inline FILETYPE_INFO() { memset(this, 0, sizeof(FILETYPE_INFO)); } char szType[4]; __int32 nOffset; __int32 nFileNumber; }; struct FILE_INFO { inline FILE_INFO() { memset(this, 0, sizeof(FILE_INFO)); } char szFileName[8]; __int32 nSize; __int32 nOffset; }; BOOL WriteDataToFile(const char * szPack, const char * szFile, const char * szExt, const void * pData, const int nSize) { BOOL bRet = TRUE; FILE * pFile = 0; char szFileNameOut[MAX_PATH] = { 0 }; if (0 != szPack && 0 != szFile && 0 != szExt && 0 != pData && 0 != nSize) { sprintf(szFileNameOut, "%s/%s.%s", szPack, szFile, szExt); pFile = fopen(szFileNameOut, "wb"); if (0 != pFile) { if (nSize != fwrite(pData, 1, nSize, pFile)) { bRet = FALSE; } fclose(pFile); pFile = 0; } else { bRet = FALSE; } } else { bRet = FALSE; } return bRet; } BOOL ExtractPack(const char * szPack) { BOOL bRet = TRUE; char szFullPackName[MAX_PATH] = { 0 }; FILE * pFilePack = 0; PACK_HEADER PackHeader; FILETYPE_INFO * pFileTypeInfoList = 0; FILE_INFO * pFileInfoList = 0; void * pFileData = 0; if (0 != szPack) { CreateDirectoryA(szPack, 0); sprintf(szFullPackName, "%s.dat", szPack); pFilePack = fopen(szFullPackName, "rb"); if (0 != pFilePack) { // 包頭 if (sizeof(PACK_HEADER) == fread( &PackHeader, 1, sizeof(PACK_HEADER), pFilePack)) { // 文件類型信息頭 pFileTypeInfoList = new FILETYPE_INFO[PackHeader.nFileTypeNumber]; if (0 != pFileTypeInfoList) { if (sizeof(FILETYPE_INFO) * PackHeader.nFileTypeNumber == fread( pFileTypeInfoList, 1, sizeof(FILETYPE_INFO) * PackHeader.nFileTypeNumber, pFilePack)) { // 文件信息頭 for (int i = 0; i < PackHeader.nFileTypeNumber; ++i) { pFileInfoList = new FILE_INFO[pFileTypeInfoList[i].nFileNumber]; if (0 != pFileInfoList) { fseek(pFilePack, pFileTypeInfoList[i].nOffset, SEEK_SET); fread(pFileInfoList, 1, sizeof(FILE_INFO) * pFileTypeInfoList[i].nFileNumber, pFilePack); // 文件數據 for (int j = 0; j < pFileTypeInfoList[i].nFileNumber; ++j) { pFileData = malloc(pFileInfoList[j].nSize); if (0 != pFileData) { printf( "%s.%s \n", pFileInfoList[j].szFileName, pFileTypeInfoList[i].szType); fseek(pFilePack, pFileInfoList[j].nOffset, SEEK_SET); fread(pFileData, 1, pFileInfoList[j].nSize, pFilePack); WriteDataToFile( szPack, pFileInfoList[j].szFileName, pFileTypeInfoList[i].szType, pFileData, pFileInfoList[j].nSize); free(pFileData); pFileData = 0; } } delete[] pFileInfoList; pFileInfoList = 0; } } } delete[] pFileTypeInfoList; pFileTypeInfoList = 0; } } fclose(pFilePack); pFilePack = 0; } else { bRet = FALSE; } } else { bRet = FALSE; } return bRet; } int main(int argc, char * argv[]) { ExtractPack("wav"); ExtractPack("BIN"); return 0; }
以上代碼僅供學習參考使用,游戲中的所有數據,其所有權歸游戲開發商所有,請各位不要把資源用於任何商業用途;
本文為原創技術文章,轉載請注明出處,謝謝;
本文鏈接:http://www.cnblogs.com/NekoDev/p/5929644.html