红色警戒吧 关注:289,941贴子:4,586,127

【火星向】地编逻辑 之 地图文件数据格式及处理

只看楼主收藏回复

本帖内容源于CNC_Maps_Renderer开源项目的部分代码。
一楼按照开源协议附License。


IP属地:北京1楼2019-05-17 15:34回复
    之后,lz发现zzattack写的地图截图器CNC_Maps_Renderer是开源的,于是开始从逆向研究变成读代码。
    为什么要读这个软件呢?因为它能读取yrm文件并渲染出游戏中的视角,说明它是具有解码、处理、调用游戏文件、渲染的全套功能的,理解了它就理解了地编乃至游戏的处理机制。
    再加上它是用Csharp写的,相比xcc的cpp项目更容易读。
    下面开始读代码


    IP属地:北京3楼2019-05-17 15:39
    回复
      2025-07-31 20:08:30
      广告
      不感兴趣
      开通SVIP免广告
      lz只是为了研究地图的数据格式,因此就看FileFormat文件就可以了

      在FileFormat目录下,详细地分为了编码、地图、虚拟文件系统三类。


      IP属地:北京4楼2019-05-17 15:43
      回复
        接下来将依次从底层环境、表层处理、操作函数介绍
        首先是虚拟文件系统。和地图有关的文件是VirtualFile.cs和MemoryFile.cs

        首先介绍最底层的VirtualFile.cs文件。
        【理论】这个文件基于Stream类定义了一个新类VirtualFile。这个类是针对地图文件的特点,对读取、写入、查找操作进行了改写。内含参数:offset,文件大小,指针位置,文件名称,是否缓存,是否初始化。
        【形象】可以把它想象成一个仓库,一头猪进来,它的名字、体重、是什么猪、是否检疫等信息都被分门别类的储存起来,便于主程序调用。

        然后是稍微上层一点的,基于VirtualFile类的MemoryFile类,它定义于MemoryFile.cs

        它的作用是调用了VirtualFIle类的第二种定义方法(上上张图最后一行),即只需要输入文件的信息,而不需要输入其他参数。
        【作用】MemoryFile在读取地图(MapFile.cs)的过程中会用于读取地形字符串。

        一些主线无关的技术细节:
        1.由MemoryFile的定义可知,在读取过程中是将地图的地形信息读取到了内存中。
        2.VirtualFile的offset和pos参数是用于指示从哪里开始读取缓存区信息。其第二种定义方法就是默认了offset=0,也就是从头开始读。


        IP属地:北京5楼2019-05-17 16:04
        回复
          虚拟文件系统部分保证了读取和更改地图时的方便,避免在主程序中大量出现底层函数调用,增加了可读性。
          下面开始介绍文件格式部分,也就是我本来在试图弄懂的部分。

          【MapObject.cs】
          MapObject.cs声明了所有地图信息的类定义,目的是结构化地处理地图各部分信息。

          其中我只关注IsoTile和OverLay,它们分别是单个Tile(如一块1×1的草地)和覆盖物(如矿石)的类定义。
          Tile:

          Tile有七个参数,分别是地图坐标、地图转换坐标、高度、tile序号(也就是哪种Tile)、SubTile(比如悬崖就有的占两块Tile)。我标注并数了一下,这七个参数共11个字节(这也与我之前查到的资料吻合)。
          ToMapPack5Entry是一个把这些参数转为bytes类并连成一个整体的操作。这是为了在编码时能把整张图的Tile合成一整个整体。

          OverLay就很简单了,不过是记录一下覆盖物的ID和值,来与游戏中的覆盖物一一对应

          【TileLayer.cs】
          这个文件定义了整张地图的类。它描述了整张地图的所有地块,可以说是很重要的。它被定义为一个可以储存<IsoTile>类的列表,并拥有地图大小、长宽等基本信息。
          另外它还定义了如何把这些IsoTile进行处理并存在地图文件里。这对我是最重要的,因为我可以学习逻辑这个用别的语言写。

          简要叙述一下流程:
          1.读取地图的长、宽,生成Tile的总数=cells。并且因为刚才说过,Tile的七个参数一共11个字节,因此定义[lzoPackSize]为11*cell+4。生成一个IzoPackSize长度的byte类。
          2.依次读取TileLayer列表中的Tile,使用上面说过的ToMapPack5Entry函数依次转化为字节数组,并合在一起。
          3.编码。
          4.压缩。
          (后面会详细说方法)
          5.把压缩得到的结果赋值给isoMapPack5。
          地图文件里的isoMapPack5的那堆字符就是这么来的。


          IP属地:北京6楼2019-05-17 16:30
          回复
            上文说了如何把地图的信息转化为编码。
            一会儿继续写如何把编码读回地图。


            IP属地:北京7楼2019-05-17 16:31
            回复
              读地图的部分懒得介绍了,暂时用不到,目前先试试手写地形块。


              IP属地:北京8楼2019-05-18 00:09
              回复
                大佬你的二楼被吞了


                IP属地:上海来自Android客户端9楼2019-05-18 00:15
                收起回复
                  2025-07-31 20:02:30
                  广告
                  不感兴趣
                  开通SVIP免广告
                  哎呀,顶顶0.0


                  10楼2019-05-18 02:05
                  回复


                    IP属地:广东来自Android客户端11楼2019-05-18 09:30
                    回复
                      必须码 留着以后学了这方面的知识看..


                      IP属地:北京13楼2019-05-18 11:22
                      回复
                        啧啧啧


                        IP属地:广东来自Android客户端14楼2019-05-18 11:51
                        回复
                          难得的技术帖


                          IP属地:浙江来自iPhone客户端15楼2019-05-18 17:59
                          回复

                            最新进展:实现了输入数字直接输出地图文件的IsoMapPack5部分。也就是实现了数据→地形的直接转化。
                            之所以没有做手动输入整张地图是因为还没弄清楚地图的坐标是怎么定义的。


                            IP属地:北京16楼2019-05-18 18:48
                            收起回复