作為一款 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