《双星物语》游戏资源格式分析与解包

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《双星物语》游戏资源格式分析与解包相关的知识,希望对你有一定的参考价值。

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

 

以上是关于《双星物语》游戏资源格式分析与解包的主要内容,如果未能解决你的问题,请参考以下文章

struct--二进制数据结构的打包与解包

[转帖]压缩与解压缩

python 组合与解包

Python序列封包与解包

Python序列封包与解包

python 抓包与解包