标签:
作为一款 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
标签:
原文地址:http://www.cnblogs.com/NekoDev/p/5929644.html