标签:
作为一款 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