程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 《雙星物語》游戲資源格式分析與解包,《雙星物語》資源

《雙星物語》游戲資源格式分析與解包,《雙星物語》資源

編輯:C++入門知識

《雙星物語》游戲資源格式分析與解包,《雙星物語》資源


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

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved