金庸群侠传资源压缩数据结构研究

金庸群侠传是智冠科技90年代出品的精品DOS游戏,

 

其资源压缩包格式紧凑而科学,这里我们一起学习一下其数据结构。并且编写一个能够读取解析它的程序,

 

 

 

以下是我对 Hdgrp资源文件解包的运行结果展示




下面我们看一下资源类型:

 

其资源包括 idx和grp文件,idx记录了各个资源的索引、grp(group pictures?)存储了具体数据。

 

另外以Mmap.col作为整体的调色板,存储各种颜色数据。

 

idx文件每4个byte为一个section,每个section记录一个资源图片的endoffset,即又是下一个资源图片的startoffset。

 

----------

int32  |  endoffset

 

grp文件,以idx文件为基准,按offset区间划分存储图片,每个图片资源:

 

---------

int16 | w

int16 | h

int16 | x (不知道有什么用)

int16 | y(不知道有什么用)

以下h行,每行支持多个section,每个section数据结构

----------

int8 | 该行字节数

[section]

int8 | t 透明像素点个数

int8 | nt 非透明像素点个数

nt * int8 | 该点对应调色板数据,如1,则对应调色板第一个颜色值

 

 

调色板文件Mmap.col

一共256*3 byte,256色,每个颜色按rgb 除以4存放,按顺序存放

----------

byte | r

byte | g

byte | b

 

这样就可以解每个资源文件了。下面是部分核心的C#代码,我使用WPF的bitmapimage进行渲染。

 

 

 



    class GameResource
    {
        #region 单例
        static public GameResource Instance
        {
            get
            {
                if(_instance==null){
                    _instance = new GameResource();    
                }
                return _instance;
            }
        }

        static GameResource _instance = null;
        private GameResource()
        {
            ImageFiles = new Dictionary<string, List<Image>>();
        }
        #endregion

        public Dictionary<string, List<Image>> ImageFiles;
    }

    .............

        /// <summary>
        /// 初始化调色板
        /// </summary>
        public void InitColors() 
        {
            FileStream f = new FileStream("data/Mmap.col", FileMode.Open);
            BinaryReader reader = new BinaryReader(f);
            for (int i = 0; i < 256; ++i)
            {
                byte[] color = reader.ReadBytes(3);
                for (int j = 0; j < color.Length; ++j)
                {
                    color[j] = (byte)((int)color[j] * 4);
                }
                colorMap[i] = color;
            }
            reader.Close();
            f.Close();
        }

        Dictionary<int, byte[]> colorMap = new Dictionary<int, byte[]>();

        private BitmapImage ReadImage(BinaryReader reader, int length)
        {
            int w = reader.ReadInt16();
            int h = reader.ReadInt16();
            int x = reader.ReadInt16();
            int y = reader.ReadInt16();

            List<System.Windows.Media.Color> colors = new List<System.Windows.Media.Color>();
            colors.Add(System.Windows.Media.Colors.Blue);
            colors.Add(System.Windows.Media.Colors.Green);
            colors.Add(System.Windows.Media.Colors.Red);
            
            BitmapPalette palette = new BitmapPalette(colors);
            PixelFormat pf = PixelFormats.Bgra32;

            int stride = (w * pf.BitsPerPixel + 7) / 8;
            byte[] pixels = new byte[h * stride];
            for (int i = 0; i < pixels.Length; ++i)
            {
                pixels[i] = 0x00;
            }
            int p = 0;
            try
            {
                for (int i = 0; i < h; ++i) //H行
                {
                    p = i * w * pf.BitsPerPixel / 8;
                    int count = reader.ReadByte(); //该行字节数
                    int offset = 0;

                    while (offset < count)
                    {
                        int transparentPixs = reader.ReadByte(); //透明像素个数
                        offset++;
                        p += transparentPixs * pf.BitsPerPixel / 8;
                        int nonTransPix = reader.ReadByte(); //非透明像素个数
                        offset++;
                        for (int j = 0; j < nonTransPix; ++j)
                        {
                            int colorKey = reader.ReadByte();

                            pixels[p] = colorMap[colorKey][2]; //b
                            p++;
                            pixels[p] = colorMap[colorKey][1]; //g
                            p++;
                            pixels[p] = colorMap[colorKey][0]; //r
                            p++;
                            pixels[p] = 0xFF; //a
                            p++;

                            offset++;
                        }
                    }
                }
            }
            catch (Exception e)
            {
                MessageBox.Show(p.ToString());
            }
            
            BitmapSource image = BitmapSource.Create(
                w,
                h,
                96,
                96,
                pf,
                palette,
                pixels,
                stride);
            PngBitmapEncoder encoder = new PngBitmapEncoder();
            
            MemoryStream memoryStream = new MemoryStream();
            BitmapImage bImg = new BitmapImage();
            encoder.Frames.Add(BitmapFrame.Create(image));
            encoder.Save(memoryStream);

            bImg.BeginInit();
            bImg.StreamSource = new MemoryStream(memoryStream.ToArray());
            bImg.EndInit();

            memoryStream.Close();
            return bImg;
        }

        public List<Image> LoadImages(string filename)
        {
            List<Image> rst = new List<Image>();
            FileStream f = new FileStream(filename + ".idx", FileMode.Open);
            BinaryReader reader = new BinaryReader(f);

            FileStream gf = new FileStream(filename + ".grp", FileMode.Open);
            BinaryReader greader = new BinaryReader(gf);
            try
            {
                int startOffset = 0;
                while (f.CanRead)
                {
                    int endOffset = reader.ReadInt32();
                    Image image = new Image();
                    image.Source = ReadImage(greader, endOffset-startOffset);
                    rst.Add(image);
                    startOffset = endOffset;
                }
            }
            catch (Exception e)
            {

            }
            greader.Close();
            gf.Close();
            reader.Close();
            f.Close();
            return rst;
        }

        public void LoadResource(string filename)
        {
            GameResource.Instance.ImageFiles.Add(filename, LoadImages(filename));
        }

..................


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
RPGViewer图片资源提取工具 作者:Van 说明: 下载RPGViewer之后,不要忘记同时下载RPGViewerSupportFile。解压RPGViewer后,将RPGViewerSupportFile拷贝到解压之后的目录,然后选择“解压到当前文件夹”即可。 有需要的朋友可以做为研究之用 郑重声明 本程序提供的导入导出功能仅供个人学习研究之用,图像之版权属相关公司所有,请勿将提取的图片或导入修改后的图象文件用于其它用途 功能简介 一、浏览 执行文件菜单中的打开。如果你是第一次查看这个游戏,RPGViewer会弹出对话框让你选择游戏的路径,一般选择游戏主程序所在目录即可 之后就可以用浏览菜单或者浏览工具栏查看游戏中的图片了。 如果遇到不支持的图片格式或者读取图片时发生错误,RPGViewer会在状态栏显示相关的出错信息。 二、搜索 可以根据文件名查找图片。支持查找的游戏列表详见附录1 搜索支持模糊查找、区分大小写和正则表达式,正则表达式的具体定义参见附录3 三、导出(支持bmp、jpg、png和mng格式,mng格式的说明参见附录2。另外支持导出为三国群英传的SHP格式) RPGViewer提供三种导出功能: 1、当前帧 导出当前显示的图片 2、当前图片 当图片仅有一帧时,和导出“当前帧”相同。否则将导出该图片的所有帧。 3、所有图片 导出所有图片(!注意:如果图片有多帧,那么此功能会导出所有帧!) 注:有些导出图片的高度是负的,可能在某些图片浏览器中不能正常显示。建议使用Irfanview或PhotoShop进行查看和编辑 全部导出功能会将所有的图片导出到目标文件夹中,文件名依次为1-1.bmp,2-1.bmp... 四、导入(支持bmp、gif、jpg和png格式) 你可以导入bmp或者png(支持透明色和alpha通道)格式的图片(注:“导入”操作只是引入了一个替换的标记,此时并未进行实际的替换) 如果想取消对当前图片的替换可以使用“导入”菜单中的“还原” 全部图像都替换完之后,执行“导入”菜单中的“保存”,所作的替换就会生效 五、压缩包操作 可以提取游戏数据包中的所有文件,并支持部分游戏的文件替换。支持解包和替换的游戏列表详见附录1 附录1: 浏览和导出支持以下游戏: 大宇:轩辕剑系列(2代、3代、4代以及它们的外传)、轩辕伏魔录,仙剑1(DOS&WIN;版)、新仙剑、仙剑2、仙剑3和仙剑3外传 智冠:金庸群侠传(光盘&硬盘版)、武林群侠传、三国群侠传、天龙八部(部分) 奥汀:三国群英传1~6、幻世录1~2 宇峻:绝代双骄1~3、幻想三国志1、2 弘煜:风色幻想1、SP&2、3、4 光谱:富甲天下3、富甲天下4 汉堂:阿玛迪斯战记、天地劫-神魔至尊传、天地劫序传-幽城幻剑录、天地劫外章-寰神结 新瑞狮:吕布与貂蝉、反三国志、天河传说 目标:秦殇、秦殇前传-复活 金山:新剑侠情缘、月影传说、剑侠情缘2、地雷战、决战朝鲜 KOEI:三国志1~5(头像文件)、三国志6~11、SanInternet、SanBattleField、英杰传系列、 太阁4~5,信长之野望3Win版、信长之野望6(头像文件)、信长之野望7~12、 真三国无双3、 水浒传天命之誓&天导108星、成吉思汗4、王国兴亡录 TGL:神奇传说——远征奥德赛1&2、神奇传说1~3、战国美少女1&2 Falcom:伊苏1、伊苏2(部分图片调色板不对)、英雄传说6(部分)、失落的羽翼、圣界的奇迹、绯苍幻想曲 ego:圣魔大战、新圣魔大战、艾伦希亚战记、红泪、苍月、魔法少年、永远的羁绊、我的美丽天使、秘境传说、乱世奇缘 KEY:Kanon、AIR KID:梦之翼、Never7、Ever17、秋之回忆1~3、秋之回忆-想君 ELF:龙骑士4、同级生2(DOS&Win;版)、下级生 Illusion:欲望的血液4、尾行2、尾行3、BattleRaper2、人工少女2、波动少女1.5、波动少女2 SoftMax:西风狂诗曲 NWC:英雄无敌2、英雄无敌3 EIDOS:盟军敢死队-使命召唤&深入敌后 UBI:英雄无敌5测试版 其它:郑问之三国志 字体:三国志2~5、San9&10;(存为png格式可以保留alpha通道)、MagicWin 导入支持以下游戏: 三1~5头像,三6~10,三11的头像,英杰传系列,大航海4,信长7、8、11、12,成吉思汗4,王国兴亡录 地雷战 三国群英传1~3的PAK文件(仅限于其中的SHP格式)(注:未经严格测试,替换前请一定要备份,以避免不必要的损失) 部分支持信长12和三国志11的bin文件的导入 注: 曹操传的meff不支持导入 查找和解包支持以下游戏: 大宇:CPK(仙剑3和仙剑3外传) 奥汀:PAK(三国群英传1~3、幻世录1、幻世录2)、PCK(三国群英传4&5) 宇峻:*Combat.dat、*Man.dat、*Role.dat(幻想三国志1、2) 弘煜:BMP、FACE、MANBMP、MAPBMP(风色幻想1&SP;)、PAK(风色幻想2)、JBF(风色幻想3&4) 汉堂:DAT(幽城幻剑录、寰神结) TGL:PAC(神奇传说3、远征奥德赛1&2)、PAK(战国美少女2) Falcom:YS(伊苏1)、ED6_DT??.dat(英雄传说6) ego:DAT、TPF(乱世奇缘) KID:DAT ELF:ARC illusion:PP SoftMax:ZMK(西风狂诗曲) NWC:AGG(英雄无敌2)、LOD(英雄无敌3) EIDOS:DIR(盟军敢死队) UBI:PAK(英雄无敌5测试版) 替换支持以下游戏: 奥汀:PAK(三国群英传1~3) 附录2: mng导出格式简介 此格式对应多帧的PNG图片,效果类似于GIF动画,但支持RGB+alpha通道并且无损压缩。可以使用IrfanView、XnView查看,GIMP编辑(相关支持软件可以去http://www.libpng.com/pub/mng/mngapps.html查看) MNG IE插件:http://entropymine.com/jason/mng4ie/(可以到http://free.ys168.com/?pigspy下载,里面提供了注册文件reg.dat和卸载文件unreg.dat) 装了该插件之后可以直接用IE打开mng文件 注1、此导出格式仅适用于生成动画(具体包括:轩辕剑的tsw图片,绝代双骄2&3、幻想三国志1&2的战斗和法术图片,金山的ASF.PAK,西风狂诗曲的OBC文件) 注2、如果用这种格式保存帧数太多、图像范围太大的图片(比如全屏幕的法术),可能需要花较多的时间生成,同时生成的图片也可能会比较大) 注3、你可以使用相关工具进一步减少mng的尺寸(比如使用delta-PNG方式压缩) 注4、当提取当前帧时,会自动保存为png格式 附录3: 正则表达式 具体参见http://msdn2.microsoft.com/en-us/library/k3zs4axe(en-us,VS.80).aspx中的“Regular Expression Syntax” 一些正则表达式的例子: 严格匹配face:^face$ 模糊查找face:face 查找mFace???.shp的文件:mFace...\.shp$

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值