1768吧 关注:153贴子:1,034
  • 9回复贴,共1
关于金山公司的剑侠情缘系列游戏的图片格式
   (转载)  
  
  我对金山公司的剑侠情愿系列的图片是仰慕已久啊!最近实在憋不住,我就开始的艰难的图片格式探索.但是在发表这篇文 章的中心内容之前,我郑重声明,我对这篇文章所造成的后果不承担任何责任,我是一个游戏的探索者,没有对这种方法和使 用这种方法获得的任何数据作任何的商业化.请读者只是将获得的数据用于自己的研究.
     通过这种技术,剑侠情缘系列的所有图片就将是垂手可的!但是不要侵犯版权,一切图 片都是金山公司所有,自己用吧,不用找美工了!好爽啊!!
       好了,言归正传.我们研究的游戏是,剑侠情愿的 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",哈哈!!!再多查几个,是不是相对位置永远都是那样!



1楼2010-06-05 15:44回复
      今天接着写.上次说在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 是图片的"最大"高度]
    .....
    那么?就这样结束了么??远远没有,好戏还在后头!
    今天,我把他的图片格式公布如下。
       感兴趣的朋友可以试试。
      
    调色板的结构:
    


    2楼2010-06-05 15:44
    回复

      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;
      


      3楼2010-06-05 15:44
      回复
           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 


        4楼2010-06-05 15:44
        回复
          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;
          


          5楼2010-06-05 15:44
          回复
               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)
            


            6楼2010-06-05 15:44
            回复
                     {
                         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;
              


              7楼2010-06-05 15:44
              回复
                             ++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];
                


                8楼2010-06-05 15:44
                回复
                             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;
                  


                  9楼2010-06-05 15:44
                  回复
                           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);
                    }


                    10楼2010-06-05 15:44
                    回复