红警DIY论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

搜索
查看: 148|回复: 8

关于FA2编辑器的解码问题

[复制链接]
发表于 2019-5-14 01:50:19 | 显示全部楼层 |阅读模式
贴吧吞楼严重,故转阵地整理重发。
【摘要】试图弄清地图的数据格式,便于使用程序读取。
【介绍】地图的主要数据为地形和覆盖物,查阅资料^[1][2]得知,它们分别对应文本文件中的[IsoMapPack5]和[OverLayPack]。经过查资料和测试,发现它们是以64位编码,并使用特殊压缩方式^[2]压缩的,确切地说,分别使用了miniLZO^[1]和LCW^[2][3]方法。试图将其还原,正文将详述。本文卡在了解压缩这一步上。

【正文】
首先确定[IsoMapPack5]和[OverLayPack]。【步骤】将一张成图的这两项值分别复制给等大的空白地图。【结果】发现确实是分别对应地形(悬崖等)和覆盖物(矿物等)。

其次是确定数据格式。【步骤】使用python(最后附代码)对地图文件的[IsoMapPack5]进行读取、数据标准化(逐行读取合成一个长字符串)、解码(64位解码)。【结果】得到了一个数组。根据资料,该数组就是压缩后的地形数据。

第三是在不解压的情况下推测文件存储方式。【步骤】分别创建 16×16,32×32,64×64,128×128的草地空地图,分别提取[IsoMapPack5]并进行Base64解码,得到byte array数组,记录其长度。【结果】列表长度分别为2042、8199、32901、131928.列表长度是四倍四倍的增加,和单元格的增加一致。【推测】储存方式是对地形块挨个编号挨个存储。

第四是确定压缩方式。查询得知,[IsoMapPack5]使用的是miniLZO^[1],非常适用于重复度高的地形文件。而[OverlayPack]使用的是westwood公司的LCW压缩算法^[3]。然后目前就卡在这里了,因为尚未找到可用的程序。

最后记录一下细节:64位编码的最末可能用"="做补位,因此在写程序时使用等号切割字符串时要手动补上。


然后进行了一些其他测试:

1.关于存储方式的测试
【步骤】在16×16空地图上放置一个脏的地形块。
【结果】:发现放置第一个的时候,长度有较大增长(10),在【相邻】位置放置第二个的时候,长度较小增长(2),在【非相邻】位置放置第3个,长度较大增长。
【解释】
地表数据采取miniLZO压缩方法,新增的地形块X会打断重复的字符串,减小重复长度和指回距离,造成压缩结果变长。而相邻位置的地形块就不会打断了,而是会与第一块地形块X共同压缩成一个新的字符串,因此压缩结果不会显著增加。

附原理:
LZO压缩算法压缩原理
LZO压缩算法采用(重复长度L,指回距离D)代替当前已经在历史字符串中出现过的字符串,其中,重复长度是指,后出现的字符串与先出现的字符串中连续相同部分的长度;指回距离是指,先后两个相同字符串之间相隔的距离(每个字节为一个单位);如果没出现过(定义为新字符),则首先输出新字符的个数,再输出新字符。例如,待处理的字符串为“ABCDEFGHABCDEFJKLM”,压缩算法逐个处理字符,处理ABCDEFGH时没发现重复字符;处理到ABCDEF时发现这些字符在历史字符串中已经出现过,计算重复长度为6,指回距离(当前A离历史A的距离)为8,则用(6,8)代替ABCDEF;处理到JKLM时没发现重复字符,字符串到此处理完毕,则整个字符串被压缩成:(08)h ABCDEFGH(6,8)(04)h JKLM,其中h表示16进制。

2.关于解压的测试【TODO】
miniLZO在解压时需要输入压缩前原始数据的大小。这无从得知。暂时没法做。

【代码】
# -*- coding: utf-8 -*-
import struct
import base64

#读取
file = open('1632.txt', 'r');
binstr = file.read();
#数据标准化处理,因为复制过来的数据是一行行的
alist=binstr.splitlines();
totalstr='';

for line in alist:
    totalstr=totalstr+line.split("=")[1];
totalstr=totalstr+'=-';#文件末尾的补位符号会被切掉,因此要手动补一下

#解码得到byte array
totalstr=bytes(totalstr,"utf8");#str转byte
a=base64.b64decode(totalstr);#解码
lista=list(a);#十进制数组

print(len(a))

【参考资料】
1. IsoMapPack5 https://www.modenc.renegadeprojects.com/IsoMapPack5
2. OverLayPack https://www.modenc.renegadeprojects.com/OverLayPack
3. LCW http://www.shikadi.net/moddingwiki/Westwood_LCW
4. Command & Conquer Tileset Format http://www.shikadi.net/moddingwi ... quer_Tileset_Format
5. Understanding RA format tmp terrain files?https://forums.cncnet.org/topic/ ... -tmp-terrain-files/

评分

参与人数 2威严 +53 DIY币 +10 收起 理由
DexterBell + 3 + 10 赞美の心
kenosis + 50

查看全部评分

 楼主| 发表于 2019-5-14 01:53:55 | 显示全部楼层
本帖最后由 HDTT 于 2019-5-14 09:40 编辑

复制[OverlayPack]至空白地图后的结果
(见附件)
覆盖物被保留
复制[IsoMapPack5]至空白地图后的结果
(见附件)
结果:地形被保留
复制[IsoMapPack5]至空白非同种类型地图后的结果
(见附件)
结果:因为蓝色的海冰与草地无对应,故出现空缺。或许可通过修改对应关系解决。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
发表于 2019-5-14 02:55:11 | 显示全部楼层
图片直接传附件,不要用贴吧链接
发表于 2019-5-14 03:31:27 这篇帖子是使用手机发表的! | 显示全部楼层
这是要重做地编吗
 楼主| 发表于 2019-5-14 16:28:27 | 显示全部楼层

还没想法,只是想弄清数据格式弄个基础研究
发表于 2019-5-15 04:38:06 | 显示全部楼层
HDTT 发表于 2019-5-14 16:28
还没想法,只是想弄清数据格式弄个基础研究

好吧
 楼主| 发表于 7 天前 | 显示全部楼层
最新信息:
在github上找到了CnCnet的开源项目,包含了解读地图文件的csharp程序。
从逆向研究变成读代码理解规则ing,等完全弄明白会整理详细介绍一下,目前就先在贴吧单线程更了。

有兴趣的可以看一下:
开源项目:https://github.com/zzattack/ccmaps-net
 楼主| 发表于 6 天前 | 显示全部楼层
读完代码来更一手。
本帖内容源于CNC_Maps_Renderer开源项目的部分代码。
首先按照开源协议,应附程序License

"
以下许可证仅适用于程序中不包含位于源文件顶部的冲突许可证的那些部分。这些包括OpenRA和XCC项目的部分,这些部分是根据GPL v3许可的。

(麻省理工学院许可证)

版权所有(c)2007-2013 Frank Razenberg

特此授予任何获得本软件和相关文档文件(“软件”)副本的人免费许可,无限制地交易本软件,包括但不限于使用,复制,修改,合并的权利根据以下条件,出版,分发,再许可和/或出售本软件的副本,并允许向其提供本软件的人员这样做:

上述版权声明和本许可声明应包含在本软件的所有副本或实质部分中。

本软件按“原样”提供,不提供任何明示或暗示的担保,包括但不限于适销性,适用于特定用途和不侵权的担保。在任何情况下,作者或版权所有者均不对任何索赔,损害或其他责任承担任何责任,无论是在合同,侵权行为还是其他方面的行为,由本软件引起或与之相关,或与本软件的使用或其他交易有关。软件。
"
———————————————开始程序篇———————————————
lz发现zzattack写的地图截图器CNC_Maps_Renderer是开源的,于是开始从逆向研究变成读代码。
为什么要读这个软件呢?因为它能读取yrm文件并渲染出游戏中的地图样式,说明它是具有解码地图、处理地图文件、调用游戏内部文件、渲染地图显示的全套功能的,理解了它就理解了地编乃至游戏对地图的读取、编辑、保存、显示。

再加上相比Xcc Mixer的c++项目,它是用Csharp写的。csharp的特点是函数的定义和声明都要写在一起,相比C++只给你一个空荡荡的头文件好读得多。

首先介绍一下这个开源项目的直观架构。
项目链接为:https://github.com/zzattack/ccmaps-net
主文件包括
· CNCMaps.Engine(游戏引擎,来自openRA项目)
· CNCMaps.FileFormats(地图文件格式相关)
下面三个我没看↓
· CNCMaps.Objects.Westwood
· CNCMaps.Renderer.GUI
· CNCMaps.Renderer

其中,FileFormat文件夹里的代码是对我最重要的,因为其详细记录了地图的储存方式和相关函数。
在该目录下包括三个文件夹:
Encodings(与地图有关的一系列编码函数)
Map(储存了地图文件格式)
VirtualFileSystem(虚拟文件格式,与地图有关的底层文件格式)

接下来将依次从底层格式、与地图有关的数据结构、与地图有关的操作函数来叙述。
———————————————————底层格式部分—————————————————————
首先是介绍虚拟文件系统(VirtualFileSystem)。该文件夹中,和地图有关的文件是VirtualFile.cs、VirtualTextFile.csMemoryFile.cs。
所谓VirtualFile,也就是虚拟文件,意思就是给一个类(Class)定义写入、读取、查找三大基本功能,这个类不就相当于一个文件了吗。之所以要重新重新定义,是因为地图有其特有的文件结构,按着结构定义一个虚拟文件格式,会大大简化主函数的写法。


首先介绍最底层的VirtualFile.cs文件。它的部分定义如下:
(不想看代码可以直接跳过代码部分)
  1. <div style="color: rgb(212, 212, 212); background-color: rgb(30, 30, 30); font-family: Consolas, &quot;Courier New&quot;, monospace; line-height: 19px; white-space: pre;"><div><span style="color: #569cd6;">public</span> <span style="color: #569cd6;">class</span> <span style="color: #4ec9b0;">VirtualFile</span> : <span style="color: #4ec9b0;">Stream</span> {<span style="color: #6a9955;">//Stream是一个抽象类。有写入、读取、查找三个功能</span></div><div>        <span style="color: #569cd6;">public</span> <span style="color: #4ec9b0;">Stream</span> <span style="color: #9cdcfe;">BaseStream</span> { <span style="color: #569cd6;">get</span>; <span style="color: #569cd6;">internal</span> <span style="color: #569cd6;">protected</span> <span style="color: #569cd6;">set</span>; }<span style="color: #6a9955;">//TODO Returns the underlying stream.</span></div><div>        <span style="color: #569cd6;">protected</span> <span style="color: #569cd6;">int</span> <span style="color: #9cdcfe;">BaseOffset</span>;</div><div>        <span style="color: #569cd6;">protected</span> <span style="color: #569cd6;">long</span> <span style="color: #9cdcfe;">Size</span>;</div><div>        <span style="color: #569cd6;">protected</span> <span style="color: #569cd6;">long</span> <span style="color: #9cdcfe;">Pos</span>;</div><div>        <span style="color: #569cd6;">virtual</span> <span style="color: #569cd6;">public</span> <span style="color: #569cd6;">string</span> <span style="color: #9cdcfe;">FileName</span> { <span style="color: #569cd6;">get</span>; <span style="color: #569cd6;">set</span>; }</div><div>        <span style="color: #6a9955;">//In C#, for overriding the base class method in a derived class, you have to declare a base class method as virtual and derived class method asoverride:</span></div>
  2. <div>        <span style="color: #569cd6;">byte</span>[] <span style="color: #9cdcfe;">_buff</span>;</div><div>        <span style="color: #569cd6;">readonly</span> <span style="color: #569cd6;">bool</span> <span style="color: #9cdcfe;">_isBuffered</span>;</div><div>        <span style="color: #569cd6;">bool</span> <span style="color: #9cdcfe;">_isBufferInitialized</span>;</div>
  3. <div>        <span style="color: #569cd6;">public</span> <span style="color: #dcdcaa;">VirtualFile</span>(<span style="color: #4ec9b0;">Stream</span> <span style="color: #9cdcfe;">baseStream</span>, <span style="color: #569cd6;">string</span> <span style="color: #9cdcfe;">filename</span>, <span style="color: #569cd6;">int</span> <span style="color: #9cdcfe;">baseOffset</span>, <span style="color: #569cd6;">long</span> <span style="color: #9cdcfe;">fileSize</span>, <span style="color: #569cd6;">bool</span> <span style="color: #9cdcfe;">isBuffered</span> = <span style="color: #569cd6;">false</span>) {</div><div>            <span style="color: #9cdcfe;">Size</span> = <span style="color: #9cdcfe;">fileSize</span>;</div><div>            <span style="color: #9cdcfe;">BaseOffset</span> = <span style="color: #9cdcfe;">baseOffset</span>;</div><div>            <span style="color: #9cdcfe;">BaseStream</span> = <span style="color: #9cdcfe;">baseStream</span>;</div><div>            <span style="color: #9cdcfe;">_isBuffered</span> = <span style="color: #9cdcfe;">isBuffered</span>;</div><div>            <span style="color: #9cdcfe;">FileName</span> = <span style="color: #9cdcfe;">filename</span>;</div><div>        }</div>
  4. <div>        <span style="color: #569cd6;">public</span> <span style="color: #dcdcaa;">VirtualFile</span>(<span style="color: #4ec9b0;">Stream</span> <span style="color: #9cdcfe;">baseStream</span>, <span style="color: #569cd6;">string</span> <span style="color: #9cdcfe;">filename</span> = <span style="color: #ce9178;">""</span>, <span style="color: #569cd6;">bool</span> <span style="color: #9cdcfe;">isBuffered</span> = <span style="color: #569cd6;">false</span>) {<span style="color: #6a9955;">//重载</span></div><div>            <span style="color: #9cdcfe;">BaseStream</span> = <span style="color: #9cdcfe;">baseStream</span>;</div><div>            <span style="color: #9cdcfe;">BaseOffset</span> = <span style="color: #b5cea8;">0;;</span></div><div>            <span style="color: #9cdcfe;">Size</span> = <span style="color: #9cdcfe;">baseStream</span>.<span style="color: #9cdcfe;">Length</span>;</div><div>            <span style="color: #9cdcfe;">_isBuffered</span> = <span style="color: #9cdcfe;">isBuffered</span>;</div><div>            <span style="color: #9cdcfe;">FileName</span> = <span style="color: #9cdcfe;">filename</span>;</div><div>        }</div></div>
复制代码
这个定义的类内含参数:offset,文件大小,指针位置,文件名称,是否缓存,是否初始化。
这个类的函数针对地图文件的特点,对读取、写入、查找操作进行了改写。
之后地图文件的创建都是基于这个虚拟文件类。


可以把它想象成顺丰收快递的小哥,一个快递进来以后,快递尺寸、快递大小、快递物品类型、是否经过安检、是否需要包装等信息都被录入系统,便于分类和调用。


然后是VirtualTextFile.cs
它不是很重要,只在读取文件的时候用了一下。它的主要作用是定义了如何读取地图文件。
  1. <div style="color: rgb(212, 212, 212); background-color: rgb(30, 30, 30); font-family: Consolas, &quot;Courier New&quot;, monospace; line-height: 19px; white-space: pre;"><div>        <span style="color: #569cd6;">public</span> <span style="color: #569cd6;">virtual</span> <span style="color: #569cd6;">string</span> <span style="color: #dcdcaa;">ReadLine</span>() {</div><div>            <span style="color: #6a9955;">// works for ascii only!</span></div><div>            <span style="color: #569cd6;">var</span> <span style="color: #9cdcfe;">builder</span> = <span style="color: #569cd6;">new</span> <span style="color: #4ec9b0;">StringBuilder</span>(<span style="color: #b5cea8;">80</span>);</div><div>            <span style="color: #c586c0;">while</span> (<span style="color: #9cdcfe;">CanRead</span>) {</div><div>                <span style="color: #569cd6;">char</span> <span style="color: #9cdcfe;">c</span> = (<span style="color: #569cd6;">char</span>)<span style="color: #dcdcaa;">ReadByte</span>();</div><div>                <span style="color: #c586c0;">if</span> (<span style="color: #9cdcfe;">c</span> == <span style="color: #ce9178;">'</span><span style="color: #d7ba7d;">\n</span><span style="color: #ce9178;">'</span>)</div><div>                    <span style="color: #c586c0;">break</span>;</div><div>                <span style="color: #c586c0;">else</span> <span style="color: #c586c0;">if</span> (<span style="color: #9cdcfe;">c</span> != <span style="color: #ce9178;">'</span><span style="color: #d7ba7d;">\r</span><span style="color: #ce9178;">'</span>)</div><div>                    <span style="color: #9cdcfe;">builder</span>.<span style="color: #dcdcaa;">Append</span>(<span style="color: #9cdcfe;">c</span>);</div><div>            }</div><div>            <span style="color: #c586c0;">return</span> <span style="color: #9cdcfe;">builder</span>.<span style="color: #dcdcaa;">ToString</span>();</div><div>        }</div>
  2. </div>
复制代码




然后是基于VirtualFile类的MemoryFile,姑且称它为稍微上层一点的类。
public class MemoryFile : VirtualFile {//定义见VirtualFile.cs,它的基类是Stream

        public MemoryFile(byte[] buffer, bool isBuffered = true) :
            base(new MemoryStream(buffer), "MemoryFile", 0, buffer.Length, isBuffered) { }// //TODO Calling the base class method?
            //MemoryStream:Creates a stream whose backing store is memory.
            //base是什么函数


由MemoryFile的定义可知,在读取过程中是将地图的地形信息读取到了内存(或者说是缓存)中。它是调用了VirtualFile的第二种定义方法,也就是只输入缓存区数据和是否缓存(默认Ture)。这个类的作用是使得在读取地图时可以只输入至少一个参数——也就是地图文件的Stream——就能将地图读入内存(Meomory)。
举例而言,在MapFile.cs文件中,地形数据是这样读入的。
var mf = new MemoryFile(isoMapPack);//TODO mf是mapfile的意思吗?

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
 楼主| 发表于 3 天前 | 显示全部楼层
刚发现代码复制的时候是html格式的,想看就粘到txt文件里再改后缀为html吧
您需要登录后才可以回帖 登录 | 注册

本版积分规则

小黑屋|Archiver|手机版|管理员邮箱|红警DIY官方论坛

GMT+8, 2019-5-25 05:22 , Processed in 0.067500 second(s), 16 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表