阅读新闻

关于金山公司的剑侠情缘系列游戏的图片格式 (转)

[日期:2006-10-22] 来源:  作者: [字体: ]
         
关于金山公司的剑侠情缘系列游戏的图片格式
  我对金山公司的剑侠情愿系列的图片是仰慕已久啊!最近实在憋不住,我就开始的艰难的图片格式探索.但是在发表这篇文章的中心内容之前,我郑重声明,我对这篇文章所造成的后果不承担任何责任,我是一个游戏的探索者,没有对这种方法和使用这种方法获得的任何数据作任何的商业化.请读者只是将获得的数据用于自己的研究.
    通过这种技术,剑侠情缘系列的所有图片就将是垂手可的!但是不要侵犯版权,一切图片都是金山公司所有,自己用吧,不用找美工了!好爽啊!!
      好了,言归正传.我们研究的游戏是,剑侠情愿的 demo2 ,研究之后的技术是通用的.
    我们来看,在demo2 中有 "Data" 这样一个文件夹,在这里,所有的文件都是 *.dat.
我们知道这里是游戏数据的仓库,那么,他的图片肯定放在这里,看见哪个 graphic.dat 了么?图片肯定就在里面向你傻笑!还有一个叫 gamedata.dat 猜想他里面肯定是 游戏的脚本之类的东东,我们使用一个叫 "WinHex" 的工具软件(好象是黑客 :> ),打开gamedata.dat ,里面是什么?请看下文(别急吗!)
    在gamedata.dat 中是游戏的脚本还有一些对话信息,至于是以什么格式存储的,我不多说了.我们的重点是得到图片格式.现在干什么?找啊.我第一个想到的就是 "南宫飞云",
找吧!!找到了好多,注意有一些叫什么 "z02-南宫飞云-sp.mpc",有好多叫什么 *.mpc ,你明白了么?"mpc"肯定就是图片的扩展名。那么接下来呢,我到 graphic.dat 中找
"z02-南宫飞云-sp.mpc",结果,什么都没有,怎么,完蛋了?我猜想肯定文件名被转换成数字,变成编号了,于是呼,我开始疯狂的查找有关与文件名转换成数字的方法,结果,你都猜到了,我被搞的差点仙去!!我想我一辈子也别想知道 西山居是怎么转的,因为随便就能找到一种方法。就这样结束了么?
     实在难受,我就随便的看 gamedata.dat 中的内容,突然发现 "MPC File Ver2.0",哦,我知道了,这是mpc的第二版.会不会是一个标记呢?我也写过游戏数据的封装,我的图片标记就是 "清风引擎图片 Ver1.0",每张图片都有,那么!!!哈哈!!!
     你知道我要做什么?我到graphic.dat 中查找 "MPC File Ver2.0",谢天谢地,西山居竟然是这样...哈哈...我统计了一下,在graphic.dat 中有 1300多个 "MPC File Ver2.0",啊,我们找了!!!开始破解文件头格式吧!!!也!!!
     我们来想想,文件头中有什么,应该是,图片的宽.高.长度.是否压缩.有几张小图片.图片的点是什么格式(8,16,24...),是不是有调色板(游戏引擎自定义的调色板),那么只要找到一个数据来测试,就可以一步一步的揭开,最容易想的就是图片的宽和高.多数是固定的.现在,我们在 gamedata.dat  中找一些关于宽和高的信息,很简单,我们知道了"\mpc\ui\dialog\panel.mpc"的宽是"440",高是"90",都是写英文和中文,你查看gamedata.dat 时就会发现的.我将宽和高转化成16进制的数,凭经验,宽在前,高在后,
查找 "  B8 01 00 00 45 00 00 00 "找到了么?你可以发现就一个,说明什么??还有,你看到在 这个数字的前边不远就是 "MPC File ver2.0",哈哈!!!再多查几个,是不是相对位置永远都是那样!


  今天接着写.上次说在gamedata.dat 还有 graphic.dat 中查找 mpc...的内容,其实.
graphic.dat 中的内容是压缩的(图片头只是压缩了一部分),所以从这两个文件中我们不可能简单的提取出图片信息.但是游戏在运行时肯定要解压,那么图片的信息就应该能够以原型出现在内存中,那么我们就来察看运行时的游戏内存.
  使用winhex 的 ram 编辑工具,察看jxqydemo2 的主内存(进程名称为sword).
找到某个 MPC File Ver2.0...
  没时间了,以后再写.

想写好一部文章真的挺难。
好了,努力一点。
    上回说到使用WinHex查看jxqyDemo2的内存,不知道诸位有否去做了?结果么便是非常的少了,不过总会有点收获吧。前文提到“\mpc\ui\dialog\panel.mpc"的宽是"440",高是"90",都是写英文和中文,你查看gamedata.dat 时就会发现的.我将宽和高转化成16进制的数,凭经验,宽在前,高在后,查找 "  B8 01 00 00 45 00 00 00 ”那么,我们就再来找"  B8 01 00 00 45 00 00 00 ",结果?!他的上面是不是也有“MPC File Ver2.0”,先不管他。多找几个"MPC File Ver2.0",比如,我知道“剑侠情缘”的最下面的控制面板(就是放物品小图标和武功小图标的大长条),他的宽是"640",高是"69",二进制就是“80 02 00 00 45 00 00 00”,找他!好了这一下就能够确定图片结构中是:
标志 :   MPC File Ver2.0                                 [16字节之后  :  xx                                                      [这个Int(4字节)是之后的图片数据的长度]
之后  :  yy                                                       [这个Int 是图片的“最大”宽度]
之后  :  zz                                                       [这个Int 是图片的"最大"高度]
.....
那么?就这样结束了么??远远没有,好戏还在后头!




今天,我把他的图片格式公布如下。
  感兴趣的朋友可以试试。
  
调色板的结构:

struct   MPCPALETE
{
   unsigned              blue:8;
   unsigned              green:8;
   unsigned              red:8;
   unsigned              alpha:8; //不知道什么作用
}
图片数据的头结构:
struct   MPCPICDATAHEADER
{
   int                         picDataSize;
   int                         picWidth;
   int                         picHeight;
   int                         NULL1;       // 0   //不知道是什么
   int                         NULL2;       // 0
}
MPC 图片文件的头结构:
struct  MPCFILEHEADER
{
   char                     mpcFlag[16];             // 标志 "MPC File Ver2.0"
   int                        mpcNULL1[12];         // 0
   int                        mpcPicDataSize;       // 图片数据长度
   int                        mpcMaxWidth;          // 所有图片中最大的宽度
   int                        mpcMaxHeight;
   int                        mpcPicCount;
   int                        mpcPicBitCount;        // 8
   int                        mpcPalleteSize;        //  调色板数据的长度
   int                        mpcNull3;                  // 0
}

MPC 文件的结构如下:
先是一个头结构:mpcFileheader
接下来是调色板信息:每个颜色32位(MPCPALETE),长度是mpcFileHeader.mpcPalleteSize  .
接下来是偏移表,每个偏移量用一个整数(4字节)表示,有多少张图片,就有多少个偏移量。
最后就是图片数据了。
  图片数据有一个头结构 ( MPCPICDATAHEADER ),这里的图片的长度不包括这个头结构(重点注意),头结构之后就是压缩后的图片数据了
还有,图片的压缩其实很简单。

比如:(16进制)
   84 04 23 45 23 35 ....
那么,他是什么意思呢?
  84 ?
  先来看 “80”,在二进制中,一个字节最大 “FF” (1111 1111),“80”(1000 0000),其实,“80”,相当与 “-0”,就是负号,那么‘81’,就是“-1”,那么,这个有什么用呢?
  你可以看到很多的 “7F”,你问我“为什么你能发现压缩的规律”,其实就是这些分布规则的“7F”告诉我的,你看,"7F"(也就是127),之后恰好有 127 字节,接着又是一个“7F”....,所以我猜测"7F"就是这一块颜色索引的长度,那么,所有的都是么?"84“又是什么呢?你知道,就是 ”-4“ 了,其实啊,就是透明色的个数,当然是连续的透明色的个数。那么”04“? 他啊,就是不透明的连续颜色索引的个数!那么如下的一段压缩码:
  84 03 23 23 23 89 06 89 89 89 89 89 89 89 ... ...
是什么意思呢?
  
  有兴趣的朋友可以发贴来猜猜!!!





代码:
      我可是从家里电脑抄到纸上,再从纸上抄到这里,大家要珍惜啊。


void  savebmp(const char *bmpfileName,unsigned char *
                        bmpData,int nDataLen,int nWidth,int nHeight);

void  mpc2bmp(const char *mpcFileName)
{
   FILE                *mpcfile=NULL;
   int                   nbmpFileNameLen=strlen(mpcFileName);
   char                *bmpfileName=new   char[nbmpFileNameLen+3];
   char                *bmpFileName=new  char[nbmpFileNameLen+3];
   memset(bmpFileName,0,nbmpFileNameLen+3);
   memset(bmpfileName,0,nbmpFileNameLen+3);
   strcpy(bmpFileName,mpcFileName);
   strcat(bmpFileName,"%d.bmp");
   nbmpFileNameLen-=4;
  
   MPCFILEHEADER         mpcFileHeader;
   MPCPICDATAHEADER  mpcPicDataHeader;
   unsigned  char           *picData=NULL;
   unsigned  char           *bmpData=NULL;
   int                                row=0;
   int                                col=0;
   int                                maxrow=0;
   int                                maxcol=0;
   int                                nextColorOffset=0;
   int                                nextColorOffsetInBmp=0;
   int                                npicCount=0;
   int                                bmpBufLen=0;
   int                                mpcBufLen=0;
   int                                i=0;
  
   mpcfile=fopen(mpcFileName,"rb");
   if(mpcfile==NULL)
   return;
   fread(&mpcFileHeader,1,sizeof(mpbFileHeader),mpcfile);
   MPCPALETE                  *mpcPalete=new   MPCPALETE  mpcFileHeader.mpcPalleteSize];
   fread(mpcPalete,4,mpcFileHeader.mpcPalleteSize,mpcfile);
   int                                 *mpcPicOffset=new  int[mpcFileHeader.mpcPicCount];
   fread(mpcPicOffset=n,1,mpcFileHeader.mpcPicCount*4,mpcfile);
   fread(&mpcPicDataHeader,1,sizeof(mpcPicDataHeader),mpcfile);
   mpcBufLen=mpcPicDataHeader.picSize-sizeof(mpcPicDataHeader);
   picData=new  unsigned  char[mpcBufLen];
   fread(picData,1,mpcBufLen,mpcfile);
   bmpBufLen=mpcPicDataHeader.picHeight*(mpcPicDataHeader.picWidth*3+mpcPicDataHeader.picWidth%4);
   bmpData=new unsigned char[bmpBufLen];
   maxcol=mpcPicDataHeader.picWidth;
   maxrow=mpcPicDataHeader.picHeight;
  
   while(1)
   {
       col   =0;
       row =0;
       nextColorOffset=0;
       nextColorOffsetInBmp=0;
      
       while(nextColorOffset<mpcBufLen)
       {
           if(picData[nextColorOffset]>0x80)
           {
               i=picData[nextColorOffset]&0x7F;
               while(i>0)
               {
                    bmpData[nextColorOffsetInBmp++]=0xFF;
                    bmpData[nextColorOffsetInBmp++]=0x00;
                    bmpData[nextColorOffsetInBmp++]=0xFF;
                    --i;
               }
               col=0;
               ++row;
           }
           ++nextColorOffset;
       }
       i=picData[nextColorOffset];
       ++nextColorOffset;
       while(i>0)
       {
            bmpData[nextColorOffsetInBmp]=
            mpcPalete[picData[nextColorOffset]].blue;
            ++nextColorOffsetInBmp;
            bmpData[nextColorOffsetInBmp]=
            mpcPalete[picData[nextColorOffset]].green;
            ++nextColorOffsetInBmp;
            bmpData[nextColorOffsetInBmp]=
            mpcPalete[picData[nextColorOffset]].red;
            ++nextColorOffsetInBmp;
            ++nextColorOffset;
            ++col;
            ++i;
        }
        if(col==maxcol)
        {
             for(i=0;i<mpcPicDataHeader.picWidth%4;i++)
            {
                 bmpData[nextColorOffsetInBmp]=0;
                 ++nextColorOffsetInBmp;
            }
            col=0;
            ++row;
         }
     }
    
     wsprintf(bmpfileName,bmpFileName,npicCount);
     savebmp(bmpfileName,bmpData,bmpBufLen,
                     mpcPicDataHeader.picWidth,
                     mpcPicDataHeader.picHeight);
     delete     bmpData;
     delete     picData;
     ++npicCount;
     if(npicCount<mpcFileHeader.mpcPicCount)
     {
          fread(&mpcPicDataHeader,1,sizeof(mpcPicDataHeader),mpcfile);
          mpcBufLen=mpcPicDataHeader.picSize-
                             sizeof(mpcPicDataHeader);
          picData=new       unsigned char[mpcBufLen];
          bmpBufLen=mpcPicDataHeader.picHeight*
                              mpcPicDataHeader.picWidth*3+
                              mpcPicDataHeader.picWidth%4);
          bmpData=new unsigned char[bmpBufLen];
          
          fread(picData,1,mpcbufLen,mpcFile);
       }
       else
            break;
      
}

void  savebmp(const char *bmpfileName,unsigned char *
                        bmpData,int nDataLen,int nWidth,int nHeight)
{
      FILE                                  *bmpFile=NULL;
      BITMAPFILEHEADER          bmpHeader;
      BITMAPINFO                      bmpInfo;
      unsigned char                   *tempData;
      int                                     nPitch=nWidth*3+nWidth%4;
      tempData=bmpData+nDataLen-nPitch;
      bmpFile=fopen(bmpfileName,"w+b");
      if(bmpFile==NULL)
             return ;

      bmpHeader.bfOffBits=54;
      bmpHeader.bfSize=nDataLen+54;
      bmpHeader.bfType=19778;         //"BM"
      bmpHeader.bfReserved1=0;
      bmpHeader.bfReserved2=0;
      
      bmpInfo.bmiHeader.biSize=40;
      bmpInfo.bmiHeader.biWidth=nWidth;
      bmpInfo.bmiHeader.biHeight=nHeight;
      bmpInfo.bmiHeader.biPlanes=1;
      bmpInfo.bmiHeader.biBitCount=24;
      bmpInfo.bmiHeader.biCompression=0;
      bmpInfo.bmiHeader.biSizeImage=nDataLen;
      bmpInfo.bmiHeader.biXPelsPerMeter=3780;
      bmpInfo.bmiHeader.biYPelsPerMeter=3780;
      bmpInfo.bmiHeader.biClrUsed=0;
      bmpInfo.bmiHeader.biClrImportant=0;

      fwrite(&bmpHeader,1,sizeof(bmpHeader),bmpFile);
      fwrite(&bmpInfo,1,sizeof(bmpInfo),bmpFile);
      
      fseek(bmpFile,(-1)*sizeof(bmpInfo.bmiColors),SEEK_CUR);
      
      for(;nHeight>0;--nHeight)
      {
           fwrite(tempData,1,nPicth,bmpFile);
           tempData-=nPitch;
      }
      fclose(bmpFile);
}





阅读:
录入:zhigu

评论 】 【 推荐 】 【 打印
上一篇:Using Vertex Texture Displacement for Realistic Water Rendering(上)
下一篇:关于在 VB 中实现最短路径搜索的简单解决方案
相关新闻      
本文评论       全部评论
  我靠,你牛。。   (jonike ,05/10/2007 10:54:33 )
发表评论
字数
姓名:

  • 尊重网上道德,遵守中华人民共和国的各项有关法律法规
  • 承担一切因您的行为而直接或间接导致的民事或刑事法律责任
  • 本站管理人员有权保留或删除其管辖留言中的任意内容
  • 本站有权在网站内转载或引用您的评论
  • 参与本评论即表明您已经阅读并接受上述条款