预谋
决定开发流星学院是在2008年04月12日的星期六早上。那时还在成都出差,躺在床上就突发奇想要重现这个游戏。这个游戏虽然很“弱智”,可当时我和我LP还玩得蛮起劲的。我就想啊:要是做出来了以后在家里,大家一起玩,重温一下当年的那种打到装备时候的兴奋啊,也蛮不错的。所以起床后马上就开始动手做。
我这人就是有这个毛病,如果想玩什么就不能只是想想而已,一定要动手去做。
那么,第一件事,就是要获得此游戏中的图片、声音等资源素材,没有这些什么都是空谈。
分析
从狗狗上找到了当年那版客户端,下载下来,游戏程序目录下的文件开始分析。
资源文件类型有 *.tex *.ani *.ini *.wav *.midi
声音文件格式是midi和wav,这个是现成的,可以直接利用。
*.tex这个根据名称来猜测,应该就是texture
*.ani这个猜测应该就是animation
*.ini貌似是加密了的配置文件,记事本打开全是乱码,暂时不管
这个游戏是2D的,通过IDA反汇编客户端发现使用DirectDraw实现,那么图像资源应该都是图片纹理,如果是动画的话最可能是纹理动画。游戏目录下的*.tex文件应该就是图片纹理,*.ani应该是动画。
用二进制编辑器打开几个*.tex文件分析,发现。不是标准的图像格式,没有BMP头,也没有JPG/PNG的那些节标记,也不像是压缩过(相邻的N字节有很多相同的),所以我猜测这应该是一种自定义的BMP文件,签名有一个自定义头,后面就是RGB数据.
如何分界呢,这个自定义的BMP头是多大?从哪里开始才是RGB数据区?
这个比较容易算出来。首先用二进制编辑器打开一个场景背景图,这个背景图是800*600, RGB表示一个像素常用的就是16位/24位/32位(在心中默念咒语100遍祈祷是24位的RGB,处理起来最简单),用800*600*像素字节数计算出各种可能的RGB数据区大小,与实际文件大小相近的既是。(如果是从内存中某个对象DUMP出来的,也可能被内存对齐影响,)
RGB16 = 800 * 600 * 2 = 960000bytes
RGB24 = 800 * 600 * 3 = 1440000bytes
RGB32 = 800 * 600 * 4 = 1920000bytes
而实际的文件大小是960032,看来是采用的RGB16格式,文件头是32bytes,没有内存对齐。
确定了文件头范围,就该确定表示图片大小在文件头中的最可能的位置了。
比对了数个文件后,确认此位置既是长宽值的保存位置。
像素格式就不用分析了,直接写了一个小程序,读取这个文件,用GDI+拿各种RGB16格式都试一次填充到Image对象,然后输出正常的图像文件,看正常的就是的了。 最后确定,此图像是ARGB4444格式,这种格式很少见啊,我只在PSP和NDS的平台上看到过这种图像的使用,毕竟在不透明情况下色彩总数太少了。
分析完tex文件,就接着分析ani文件。分析ani文件还真是花了我一番功夫,主要是在边界范围的确定上,没有任何可以精确量化的计算过程,完全凭借自己的直觉和10多次尝试,最后终于提取出所有的动画格式了。不啰嗦过程了,我描述不清,帖代码。
void CViewerDlg::ProcessFile(CString strFile) { CFile file; if( !file.Open( strFile, CFile::modeRead, NULL) ) { AfxMessageBox( strFile ); return; } file.Seek( 0, CFile::begin); int nIndex = 0; while(true) { if( file.GetPosition() >= file.GetLength() - 1 ) break; file.Seek( 0x18, CFile::current); unsigned __int32 ulImgWidth = 0; file.Read( &ulImgWidth, sizeof(ulImgWidth)); unsigned __int32 ulImgHeight = 0; file.Read( &ulImgHeight, sizeof(ulImgHeight)); ulImgHeight += 1; unsigned __int32 ulDataSizePerLine = 0; file.Read( &ulDataSizePerLine, sizeof(ulDataSizePerLine)); file.Seek( 0x4, CFile::current); Bitmap bmp( ulImgWidth, ulImgHeight, PixelFormat32bppARGB); Graphics gp(&bmp); gp.Clear(Color::Transparent); BYTE * pSrc = new BYTE[ulDataSizePerLine]; for( UINT y = 0; y < bmp.GetHeight() - 1; y++) { Rect rect( 0, y, bmp.GetWidth(), 1); if( file.Read( pSrc, ulDataSizePerLine) <= 0 ) break; BitmapData stBmpData = {0}; if( bmp.LockBits( &rect, ImageLockModeWrite, PixelFormat32bppARGB, &stBmpData) == Ok ) { LPDWORD pDest = (LPDWORD)stBmpData.Scan0; for( UINT x = 0; x < bmp.GetWidth(); x++) { WORD wPixel = pSrc[2*x] | pSrc[2*x + 1] << 8; pDest [x] = Convert4444(wPixel); } bmp.UnlockBits(&stBmpData); }; } delete [] pSrc; CLSID pngClsid; GetEncoderClsid( L"image/png", &pngClsid); nIndex++; CString strTemp; strTemp.Format( _T("%s.%d.png"), strFile, nIndex); bmp.Save( strTemp, &pngClsid); } file.Close(); }
提取
分析完成后就可以开始批量提取了,没什么技术含量。就是在分析的基础上写提取工具。只是在提取tex文件的过程中,发现有些图片提取出来的图片完全乱色了。
回过头重新分析,居然有2种RGB格式 565和4444,而在文件头中有一个值标明了此文件RGB值类型
今次就写到这里,如果你是这款游戏的开发者/策划者,并偶然看到此贴,请与我联系:) 我需要一点数值设计的资料。