国产精品天干天干,亚洲毛片在线,日韩gay小鲜肉啪啪18禁,女同Gay自慰喷水

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

紅色警戒2地圖編碼研究匯總

2019-12-01 22:30 作者:不妙脆角  | 我要投稿

地圖編碼研究記錄

HDTT咸魚

個人項目github

https://github.com/HDTTclear/MapEditor_RA2

?

一、前言

1.1 License

本文內(nèi)容源于CNC_Maps_Renderer開源項目的部分代碼。

https://github.com/zzattack/ccmaps-net


首先按照開源協(xié)議,應(yīng)附程序License。

"
以下許可證僅適用于程序中不包含位于源文件頂部的沖突許可證的那些部分。這些包括OpenRA和XCC項目的部分,這些部分是根據(jù)GPL v3許可的。

(麻省理工學院許可證)

版權(quán)所有(c)2007-2013 Frank Razenberg

特此授予任何獲得本軟件和相關(guān)文檔文件(“軟件”)副本的人免費許可,無限制地交易本軟件,包括但不限于使用,復制,修改,合并的權(quán)利根據(jù)以下條件,出版,分發(fā),再許可和/或出售本軟件的副本,并允許向其提供本軟件的人員這樣做:

上述版權(quán)聲明和本許可聲明應(yīng)包含在本軟件的所有副本或?qū)嵸|(zhì)部分中。

本軟件按“原樣”提供,不提供任何明示或暗示的擔保,包括但不限于適銷性,適用于特定用途和不侵權(quán)的擔保。在任何情況下,作者或版權(quán)所有者均不對任何索賠,損害或其他責任承擔任何責任,無論是在合同,侵權(quán)行為還是其他方面的行為,由本軟件引起或與之相關(guān),或與本軟件的使用或其他交易有關(guān)。軟件。
"

1.2 不懂代碼也能讀程序

?????? 文件格式部分基本全是定義的類,不會代碼不要緊,能看懂英文,當成自然語言來讀就好了。

?

以下來源于Microsoft關(guān)于Csharp的教程。

1.2.1 Class

1.2.1.1 定義

類就像特定對象的藍圖。在現(xiàn)實世界中,每個物體都有一些顏色,形狀和功能。例如,豪華車法拉利。法拉利是豪華車型的對象。豪華車是指定速度,顏色,形狀,內(nèi)飾等特定特征的類別。因此,任何制造滿足這些要求的汽車的公司都是豪華車型的對象。例如,寶馬,蘭博基尼,凱迪拉克的每一輛車都是名為“豪華轎車”的車型。在這里,“豪華車”是一個級別,每一輛實體車都是豪華車類的對象。

同樣,在面向?qū)ο蟮木幊讨?,類定義了某些屬性,字段,事件,方法等。類定義了數(shù)據(jù)的種類和它們的對象將具有的功能。

通過類,您可以通過將其他類型,方法和事件的變量組合在一起來創(chuàng)建自己的自定義類型。

在C#中,可以使用class關(guān)鍵字定義類。

1.2.1.2 構(gòu)造


1.2.1.3 C#構(gòu)造函數(shù)

類可以具有參數(shù)化或參數(shù)較少的構(gòu)造函數(shù)。創(chuàng)建類的實例時將調(diào)用構(gòu)造函數(shù)。可以使用訪問修飾符和類名來定義構(gòu)造函數(shù):<access modifiers> <class name>(){ }

?

這個說的是如何快速定義一個東西為一個特定的類。

?

二、重識地圖

要編程,就要從數(shù)據(jù)結(jié)構(gòu)的角度分析地圖。

首先是[標題]這種,它在程序里相當于字典的索引,在程序中查找[標題],來讀取內(nèi)容。

然后是地圖的注釋,在yrm里,“;”是注釋符號,

這一點從地圖的開頭就能看出:

注釋是什么意思呢,就是告訴程序不要讀這行。

?

然后是很關(guān)鍵的,[IsoMapPack5]和[OverLayPack]

?

可以看到它們的格式是……一堆看起來是亂碼的東西。但很顯然,這些“亂碼“只包含0~9、a~z,A~Z和+號和/號。

它們其實是64位編碼!

附一下最初找到的資料,給了我很大幫助:

https://www.modenc.renegadeprojects.com/IsoMapPack5

http://www.shikadi.net/moddingwiki/Command_%26_Conquer_Tileset_Format

?

具體而言,是把地圖的每一塊Tile的參數(shù)合成一個bytes格式的串,然后再把所有Tile的串合成一個總串,最后再把這個串壓縮成64位編碼。

?

這里我必須說一下,IsoMapPack5使用的是miniLZO解壓縮方法,而OverLayPack使用的是LCW算法,也就是所謂的format80算法。這是很重要的信息。

?

1.3 關(guān)于本文的聲明

本文內(nèi)容源于CNC_Maps_Renderer開源項目的部分代碼。

https://github.com/zzattack/ccmaps-net

本文的目的是總結(jié)國內(nèi)外資料,便于有意向研究地圖編碼及其他問題的研究者迅速掌握原理和架構(gòu)。在我看來,有開源程序和已知的地圖的解讀方式已經(jīng)是個奇跡。

?

本文的編寫者是HDTT,遵循開源程序CNC_Maps_Renderer的License。

?

因此,本文是可以由他人自由編輯、復制、發(fā)表的。但是,如果需要長篇復制,請保留本文1.3 關(guān)于本文的聲明。

三、程序

3.1 背景介紹

lz發(fā)現(xiàn)zzattack寫的地圖截圖器CNC_Maps_Renderer是開源的,于是開始從逆向研究變成讀代碼。
為什么要讀這個軟件呢?因為它能讀取yrm文件并渲染出游戲中的地圖樣式,說明它是具有解碼地圖、處理地圖文件、調(diào)用游戲內(nèi)部文件、渲染地圖顯示的全套功能的,理解了它就理解了地編乃至游戲?qū)Φ貓D的讀取、編輯、保存、顯示。

再加上相比Xcc Mixer的c++項目,它是用Csharp寫的。csharp的特點是函數(shù)的定義和聲明都要寫在一起,相比C++只給你一個空蕩蕩的頭文件好讀得多。
首先介紹一下這個開源項目的直觀架構(gòu)。
項目鏈接為:https://github.com/zzattack/ccmaps-net
主文件包括
·?CNCMaps.Engine(游戲引擎,來自openRA項目)
·?CNCMaps.FileFormats(地圖文件格式相關(guān))
下面三個我沒看↓
·?CNCMaps.Objects.Westwood
·?CNCMaps.Renderer.GUI
·?CNCMaps.Renderer

其中,F(xiàn)ileFormat文件夾里的代碼是對我最重要的,因為其詳細記錄了地圖的儲存方式和相關(guān)函數(shù)。
在該目錄下包括三個文件夾:
Encodings(與地圖有關(guān)的一系列編碼函數(shù))
Map(儲存了地圖文件格式)
VirtualFileSystem(虛擬文件格式,與地圖有關(guān)的底層文件格式)

接下來將依次從底層格式、與地圖有關(guān)的數(shù)據(jù)結(jié)構(gòu)、與地圖有關(guān)的操作函數(shù)來敘述。

?

?


3.2 文件的定義

?

3.2.1 虛擬文件系統(tǒng)

首先是介紹虛擬文件系統(tǒng)(VirtualFileSystem)。該文件夾中,和地圖有關(guān)的文件是VirtualFile.cs、VirtualTextFile.cs、MemoryFile.cs。

所謂VirtualFile,也就是虛擬文件,意思就是給一個類(Class)定義寫入、讀取、查找三大基本功能,這個類不就相當于一個文件了嗎。之所以要重新重新定義,是因為地圖有其特有的文件結(jié)構(gòu),按著結(jié)構(gòu)定義一個虛擬文件格式,會大大簡化主函數(shù)的寫法。



首先介紹最底層的VirtualFile.cs文件。它的部分定義如下:

(不想看代碼可以直接跳過代碼部分)

public class VirtualFile : Stream {//Stream是一個抽象類。有寫入、讀取、查找三個功能

????????public Stream BaseStream { get; internal protected set; }//TODO Returns the underlying stream.

????????protected int BaseOffset;

????????protected long Size;

????????protected long Pos;

????????virtual public string FileName { get; set; }

????????//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:

?

????????byte[] _buff;

????????readonly bool _isBuffered;

????????bool _isBufferInitialized;

?

????????public VirtualFile(Stream baseStream, string filename, int baseOffset, long fileSize, bool isBuffered = false) {

????????????Size = fileSize;

????????????BaseOffset = baseOffset;

????????????BaseStream = baseStream;

????????????_isBuffered = isBuffered;

????????????FileName = filename;

????????}

?

????????public VirtualFile(Stream baseStream, string filename = "", bool isBuffered = false) {//重載

????????????BaseStream = baseStream;

????????????BaseOffset = 0;;

????????????Size = baseStream.Length;

????????????_isBuffered = isBuffered;

????????????FileName = filename;

????????}

這個定義的類內(nèi)含參數(shù):offset,文件大小,指針位置,文件名稱,是否緩存,是否初始化。

這個類的函數(shù)針對地圖文件的特點,對讀取、寫入、查找操作進行了改寫。

之后地圖文件的創(chuàng)建都是基于這個虛擬文件類。



可以把它想象成順豐收快遞的小哥,一個快遞進來以后,快遞尺寸、快遞大小、快遞物品類型、是否經(jīng)過安檢、是否需要包裝等信息都被錄入系統(tǒng),便于分類和調(diào)用。


3.2.2 VirtualTextFile.cs

然后是VirtualTextFile.cs

它不是很重要,只在讀取文件的時候用了一下。它的主要作用是定義了如何讀取地圖文件。

????????public virtual string ReadLine() {

????????????// works for ascii only!

????????????var builder = new StringBuilder(80);

????????????while (CanRead) {

????????????????char c = (char)ReadByte();

????????????????if (c == '\n')

????????????????????break;

????????????????else if (c != '\r')

????????????????????builder.Append(c);

????????????}

????????????return builder.ToString();

????????}

?

?

?


3.2.2 MemoryFile

然后是基于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是什么函數(shù)

由MemoryFile的定義可知,在讀取過程中是將地圖的地形信息讀取到了內(nèi)存(或者說是緩存)中。它是調(diào)用了VirtualFile的第二種定義方法,也就是只輸入緩存區(qū)數(shù)據(jù)和是否緩存(默認Ture)。這個類的作用是使得在讀取地圖時可以只輸入至少一個參數(shù)——也就是地圖文件的Stream——就能將地圖讀入內(nèi)存(Meomory)。

?

舉例而言,在MapFile.cs文件中,地形數(shù)據(jù)是這樣讀入的。

var mf = new MemoryFile(isoMapPack);//TODO mf是mapfile的意思嗎?

?

?

3.3 數(shù)據(jù)格式

3.3.1 MapObject.cs

3.3.1.1 綜述

MapObject聲明了地圖的所有物體信息。

public class MapObject {

????????public IsoTile Tile;

????}

????public class NamedMapObject : MapObject {

????????public string Name { get; set; }

????}

????public class NumberedMapObject : MapObject {

????????public virtual int Number { get; set; }

????}

?

MapObject將物體分成了兩類。

一類是NamedMapObject,下級分類有Aircraft、Infantry、Smudge、Structure、Unit;這類的屬性比較簡單,默認只有名字一個屬性。

一類是NumberedMapObject,下級分類有IsoTile、Overlay、Waypoint。這個類的屬性默認有名字、字符串兩個屬性。

?

我們注意到NamedMapObject的自身屬性是不涉及坐標的,

例如:

????public class Structure : NamedMapObject {

????????public Structure(string owner, string name, short health, short direction) {

????????????Owner = owner;//所屬方,請注意這里要輸入的是string而不是int,因此可能不是對應(yīng)所屬方的硬編碼

????????????Name = name;//名稱

????????????Health = health;//血量

????????????Direction = direction;//建筑方向?

????????}

因此推測讀取地圖信息時是不會直接把坐標存到這類物體內(nèi)部的,那是怎么存呢?推測是在坐標上聲明存在這樣一個物體。

而NumberedMapObject都需要坐標,或者所謂鍵值,例如

public class Overlay : NumberedMapObject {

????????public byte OverlayID { get; set; }

????????public byte OverlayValue { get; set; }

????????public Overlay(byte overlayID, byte overlayValue) {

????????????OverlayID = overlayID;

????????????OverlayValue = overlayValue;

????????}

????????public override int Number {

????????????get { return OverlayID; }

????????????set { OverlayID = (byte)value; }

????????}

????}

就定義了ID和鍵值。

3.3.1.2 IsoTile

IsoTile有七個參數(shù),其中ushort是兩個字節(jié)、byte是一個字節(jié)。

?

public class IsoTile : NumberedMapObject {

//TODO 前四個參數(shù)意義我不是很清楚

????????public ushort Dx;//ushort Unsigned 16-bit integer 2-bytes

??????? public ushort Dy;

????????public ushort Rx;

????????public ushort Ry;

????????public byte Z;//1 bytes 高度?

????????public short TileNum;//16-bit TileNum,也就是聲明了是哪個塊

????????public byte SubTile;//1 bytes 子塊

?

//這里聲明了定義方法

????????public IsoTile(ushort p1, ushort p2, ushort rx, ushort ry, byte z, short tilenum, byte subtile) {

????????????Dx = p1;

????????????Dy = p2;

????????????Rx = rx;

????????????Ry = ry;

????????????Z = z;

????????????TileNum = tilenum;

????????????SubTile = subtile;

????????}

這里定義了一個函數(shù)。

????????public List<byte> ToMapPack5Entry() {

????????????var ret = new List<byte>();

????????????ret.AddRange(BitConverter.GetBytes(Rx));//2 bytes

????????????ret.AddRange(BitConverter.GetBytes(Ry));//2 bytes

????????????ret.AddRange(BitConverter.GetBytes(TileNum));//2 bytes

????????????ret.Add(0); ret.Add(0);//1+1 bytes

????????????ret.Add(SubTile);//1 bytes

????????????ret.Add(Z);//1 bytes

????????????ret.Add(0);//1 bytes

????????????return ret;

????????}

????}

注意,ToMapPack5Entry是編碼時一個重要函數(shù)。可以看到,它將11個字節(jié)的信息按順序組成了一個字符串,分別是:Rx+Ry+TileNum+0+0+SubTile+Z+0,并存儲在為元素為byte類的list:ret中。

請注意這里加了三個零,意義是什么呢?分隔符?

并且一個值得注意的事情是,dx和dy是沒有被存進去的。

3.3.1.3 Overlay

定義了覆蓋物的ID和鍵值。我猜是對應(yīng)了是什么,和 是哪個?//TODO

public class Overlay : NumberedMapObject {

????????public byte OverlayID { get; set; }

????????public byte OverlayValue { get; set; }

????????public Overlay(byte overlayID, byte overlayValue) {

????????????OverlayID = overlayID;

????????????OverlayValue = overlayValue;

????????}

????????public override int Number {

????????????get { return OverlayID; }

????????????set { OverlayID = (byte)value; }

????????}

????}

?

?

3.3.2 TileLayer.cs

定義了一個能容納全圖Tile的類。就像一個坐標紙。

public class TileLayer : IEnumerable<IsoTile> {

??????? //IEnumerable<T>是一個保證給定類是可迭代的接口.表示實現(xiàn)的類IEnumerable<T>可以被認為并用作一系列元素。

??????? //T表示序列中元素的數(shù)據(jù)類型

??????? IsoTile[,] isoTiles;//TODO 這是什么

????????private Size fullSize;//Size是指定一個矩陣 Size(Int32, Int32)指定一個m*n的矩陣

?

????????public TileLayer(int w, int h)

????????????: this(new Size(w, h)) {//創(chuàng)建一個新的空白TileLay方法

????????}

?

????????public TileLayer(Size fullSize) {

????????????this.fullSize = fullSize;

????????????isoTiles = new IsoTile[fullSize.Width * 2 - 1, fullSize.Height];//TODO

????????}

?

????????public int Width {

????????????get { return fullSize.Width; }

????????}

?

????????public int Height {

????????????get { return fullSize.Height; }

????????}

?

????????public virtual IsoTile this[int x, int y] {

????????????get {

????????????????if (0 <= x && x < isoTiles.GetLength(0) && 0 <= y && y < isoTiles.GetLength(1))

????????????????????return isoTiles[x, y];

????????????????else

????????????????????return null;

????????????}

????????????set {

????????????????isoTiles[x, y] = value;

????????????}

????????}

?

?

在TileLayer里,寫明了如何把數(shù)據(jù)壓縮并存儲。

?

public void SerializeIsoMapPack5(IniFile.IniSection isoMapPack5) {

????????????int cells = (Width * 2 - 1) * Height;

????????????int lzoPackSize = cells * 11 + 4; // last 4 bytes contains a lzo pack header saying no more data is left

????????????

????????????var isoMapPack = new byte[lzoPackSize];

????????????var isoMapPack2 = new byte[lzoPackSize];//TODO 這是什么

????????????long di = 0;

????????????foreach (var tile in this.isoTiles) {

????????????????var bs = tile.ToMapPack5Entry().ToArray();//ToMapPack5Entry的定義在MapObjects.cs

????????????????????????????????????????????????????????? //ToArray將ArrayList轉(zhuǎn)換為Array:

??????????????? Array.Copy(bs, 0, isoMapPack, di, 11);//把bs復制給isoMapPack,從di索引開始復制11個字節(jié)

????????????????di += 11;//一次循環(huán)復制11個字節(jié)

????????????}

?

????????????var compressed = Format5.Encode(isoMapPack, 5);

????????????string compressed64 = Convert.ToBase64String(compressed);

????????????

????????????int i = 1;

????????????int idx = 0;

????????????isoMapPack5.Clear();

????????????while (idx < compressed64.Length) {

????????????????int adv = Math.Min(74, compressed64.Length - idx);//74是什么

????????????????isoMapPack5.SetValue(i++.ToString(), compressed64.Substring(idx, adv));//start length

????????????????idx += adv;//idx=adv+1

????????????}

????????}

?

?

3.3.3 MapFile.cs

3.3.3.1 定義

讀取文件使用了MapFile.cs,它定義了地圖文件類,記錄了地圖尺寸、瓷磚信息、覆蓋物、臟污、Terrain、建筑、兵種、單位、飛行單位、路徑點、內(nèi)置ini這些地圖的屬性。

?

可以看到MapFile類是基于IniFile類定義的。

public class MapFile : IniFile {

????????private static readonly Logger Logger = LogManager.GetCurrentClassLogger();

?

????????public Rectangle FullSize { get; private set; }//TODO Rectangle(Point, Size) 是一個矩陣/Rectangle(Int32, Int32, Int32, Int32)

??????? public Rectangle LocalSize { get; private set; }

?

????????public TileLayer Tiles;//TileLayer的類定義在TileLayer.cs里

????????public readonly List<Overlay> Overlays = new List<Overlay>();//list<>表示可通過索引訪問

????????public readonly List<Smudge> Smudges = new List<Smudge>();

????????public readonly List<Terrain> Terrains = new List<Terrain>();

????????public readonly List<Structure> Structures = new List<Structure>();

????????public readonly List<Infantry> Infantries = new List<Infantry>();

????????public readonly List<Unit> Units = new List<Unit>();

????????public readonly List<Aircraft> Aircrafts = new List<Aircraft>();

????????public readonly List<Waypoint> Waypoints = new List<Waypoint>();

????????public readonly List<IniSection> MiscSections = new List<IniSection>();

????????public Lighting Lighting;

?

?

3.3.3.2 初始化地圖的函數(shù)

MapFile.cs聲明了如何初始化一張地圖??梢钥闯鲎x到了是把地圖讀到了信息流中,再存到了緩存區(qū),并用讀取到的文件包含的地圖信息給地圖類的各個屬性賦值。

?

說明一下:GetSection在別的文件中有定義,總之它能返回輸入的標簽所對應(yīng)的地圖內(nèi)容。

?

解釋一下流程:
1.在讀取的地圖文件中獲得標簽為"Map"的內(nèi)容,并賦值給MapFile類的屬性??梢杂脤懽职宕蜷_地圖文件,可以查找到這個。


它聲明了地圖的尺寸、類型、能顯示的尺寸。
2.根據(jù)地圖尺寸生成一個全新的TileLayer陣列,命名為Tiles。(從感性的角度看,這就像是制造了一個專門的容器,為之后讀取地圖的地形并儲存做準備。)
3.調(diào)用讀取其信息的函數(shù),并寫入log日志中。
4.地圖燈光。


至此地圖讀取就結(jié)束了。

?

public void Initialize() {//讀入地圖文件中的[Map]

????????????var map = GetSection("Map");//這里的"Map"指的是地圖文件里的了

????????????string[] size = map.ReadString("Size").Split(',');//size是地圖文件里的那行,有四個參數(shù)

????????????FullSize = new Rectangle(int.Parse(size[0]), int.Parse(size[1]), int.Parse(size[2]), int.Parse(size[3]));

??????????? //rectangle 生成一個矩陣 FullSize是一個矩陣

????????????Tiles = new TileLayer(FullSize.Width, FullSize.Height);//TODO fullsize的定義不知道在哪兒

????????????size = map.ReadString("LocalSize").Split(',');//指的是可顯示的那個localsize

????????????LocalSize = new Rectangle(int.Parse(size[0]), int.Parse(size[1]), int.Parse(size[2]), int.Parse(size[3]));//parse 轉(zhuǎn)換為數(shù)字

?

????????????Logger.Info("Reading map");//write log.Info Debug 是日志輸出的不同等級

????????????Logger.Debug("Reading tiles");

????????????ReadTiles();//TODO 定義在下面

?

????????????Logger.Debug("Reading map overlay");

????????????ReadOverlay();//TODO 定義在下面

?

????????????Logger.Debug("Reading map terrain objects");

????????????ReadTerrain();//TODO 定義在下面

?

????????????Logger.Debug("Reading map smudge objects");

????????????ReadSmudges();//TODO 定義在下面

?

????????????Logger.Debug("Reading infantry on map");

????????????ReadInfantry();//TODO where is declaration?

?

????????????Logger.Debug("Reading vehicles on map");

????????????ReadUnits();//TODO where is declaration?

?

????????????Logger.Debug("Reading aircraft on map");

????????????ReadAircraft();//TODO where is declaration?

?

????????????Logger.Debug("Reading map structures");

????????????ReadStructures();//TODO where is declaration?

?

????????????Logger.Debug("Waypoints");

????????????ReadWaypoints();//TODO where is declaration?

?

????????????Lighting = new Lighting(GetOrCreateSection("Lighting"));//TODO這是地圖光照嗎?

????????}

?

上面的流程實現(xiàn)后,一張地圖就非常清晰地存在了電腦內(nèi)存中,很方便地可以調(diào)用。
雖然上面的流程是如此直觀,但不弄清讀取和存儲的具體細節(jié),就相當于沒用的偽代碼。我只關(guān)心地形Tiles,也就是對應(yīng)ReadTiles();
接下來我就詳細解釋一下它的實現(xiàn)。

?

3.3.3.2 ReadTile

直接上代碼流程:

?

1.讀取地圖文件中[IsoMapPack5]的內(nèi)容,賦值給mapSection這個變量.
p.s.GetSection函數(shù)也是定義的。之后會說怎么實現(xiàn)的,在本程序中只需要知道如何使用即可。
2.將mapSection合并成一整個字符串、轉(zhuǎn)碼,并賦值給定義一個byte []類的lzoData,用于之后的解壓縮。
3.讀取地圖大小,乘起來得到一共有多少塊Tile。
4.lzoPackSize=11*cells+4;前文說過,每個Tile是11個字節(jié),至于+4,注釋里說了是一個休止符號。
lzoPackSize也是用于解壓縮的參數(shù)之一。lzoPackSize就等于解壓前的數(shù)據(jù)大小,因為解壓前數(shù)據(jù)大小就應(yīng)該等于
11*Tile數(shù)+4;
5.重點來了,解壓縮,使用Format5的壓縮方法,其實也就是miniLZO壓縮方法。
請注意,這里大大簡化了miniLZO的使用,因為miniLZO解壓縮是需要四個參數(shù)的,而這里通過又寫了一層外殼函數(shù),將參數(shù)縮減到了兩個,也就是:待解壓數(shù)據(jù)lzoData和與解壓前的大小相同的byte[]類isoMapPack。
再解釋一下,DecodeInto這個函數(shù)的第二個參數(shù)isoMapPack在運行結(jié)束后會被賦予解壓后的數(shù)據(jù)。

?

private void ReadTiles() {//Tile是地形塊的意思 “瓷磚”

????????????var mapSection = GetSection("IsoMapPack5");//指的是地圖文件中的"[IsoMapPack5]"

????????????

????????????byte[] lzoData = Convert.FromBase64String(mapSection.ConcatenatedValues());

??????????? //concatenated的意思是連接起來,應(yīng)該是把mapSection連接成一個長的整體再一起轉(zhuǎn)碼

??????????? //所以IzoData是一個64位連續(xù)的串

????????????int cells = (FullSize.Width * 2 - 1) * FullSize.Height;

????????????//每個cell占 11 bytes,最后四個btyes是終止符

????????????int lzoPackSize = cells * 11 + 4; // last 4 bytes contains a lzo pack header saying no more data is left

?

????????????var isoMapPack = new byte[lzoPackSize];//lzoPackSize長度的byte,可通過index訪問

????????????//var 格式受讀入定義.isoMapPack是一串bytes

????????????uint totalDecompressSize = Format5.DecodeInto(lzoData, isoMapPack);//TODO 源,目標 輸入應(yīng)該是解碼后長度,isoMapPack被賦值解碼值了

????????????//uint??0 to 4,294,967,295??Unsigned 32-bit integer?System.UInt32

?

?



就這樣,解壓結(jié)束后,我們得到了一個總的解壓后大?。簍otalDecompressSize,
和已經(jīng)儲存了所有Tile信息的byte[]類isoMapPack。
但是我們?nèi)绾螐膇soMapPack這個一整個數(shù)組中還原地圖的每個Tile呢?

?

因為我們已經(jīng)知道了壓縮文件的結(jié)構(gòu),也就是每11個字節(jié)就是一個Tile,并且順序分別為rx,ry,tilenom,zero1,subtile,z,zero2。所以我們只要11個11個讀就可以。

?

讀取以后,計算出dx和dy(這里我沒弄懂rx ry dx dy都是干什么的)//TODO
然后根據(jù)dx和dy作為行和列,賦值給之前生成的空白陣列Tiles。

就這樣,地形的信息就完全存在了電腦內(nèi)存里。

?

var mf = new MemoryFile(isoMapPack);//TODO mf是mapfile的意思嗎?

????????????//MemoryFile的定義見MemoryFile.cs

????????????int numtiles = 0;

????????????for (int i = 0; i < cells; i++) {

????????????????//TODO 這些值是什么。

????????????????ushort rx = mf.ReadUInt16();//ushort????0 to 65,535?Unsigned 16-bit integer?System.UInt16

????????????????ushort ry = mf.ReadUInt16();

????????????????short tilenum = mf.ReadInt16();//short??-32,768 to 32,767???Signed 16-bit integer???System.Int16

????????????????short zero1 = mf.ReadInt16();//Reads a 2-byte signed integer from the current stream and advances the current position of the stream by two bytes.

????????????????byte subtile = mf.ReadByte();//Reads a byte from the stream and advances the position within the stream by one byte, or returns -1 if at the end of the stream.

????????????????byte z = mf.ReadByte();

????????????????byte zero2 = mf.ReadByte();

????????????????//一次循環(huán)讀11 bytes

????????????????int dx = rx - ry + FullSize.Width - 1;

????????????????int dy = rx + ry - FullSize.Width - 1;

????????????????//上面是一個線性變換 旋轉(zhuǎn)45度、拉長、平移

????????????????numtiles++;//在最后日志用了一下

????????????????if (dx >= 0 && dx < 2 * Tiles.Width &&

????????????????????dy >= 0 && dy < 2 * Tiles.Height) {

????????????????????var tile = new IsoTile((ushort)dx, (ushort)dy, rx, ry, z, tilenum, subtile);//IsoTile定義是NumberedMapObject

????????????????????Tiles[(ushort)dx, (ushort)dy / 2] = tile;//給瓷磚賦值

????????????????}

????????????}

?

????????????// fix missing tiles

?

????????????// import tiles

????????????for (ushort y = 0; y < FullSize.Height; y++) {

????????????????for (ushort x = 0; x <= FullSize.Width * 2 - 2; x++) {

????????????????????var isoTile = Tiles[x, y];//從這兒來看,isoTile指的是一塊瓷磚,Tile是一個二維數(shù)組,存著所有瓷磚

????????????????????//isoTile的定義在TileLayer.cs里

????????????????????if (isoTile == null) {

????????????????????????// fix null tiles to blank

????????????????????????ushort dx = (ushort)(x);

????????????????????????ushort dy = (ushort)(y * 2 + x % 2);

????????????????????????ushort rx = (ushort)((dx + dy) / 2 + 1);

????????????????????????ushort ry = (ushort)(dy - rx + FullSize.Width + 1);

????????????????????????Tiles[x, y] = new IsoTile(dx, dy, rx, ry, 0, 0, 0);//TODO IsoTile有七個參數(shù),定義在112行

????????????????????}

????????????????}

????????????}

?

?

四、編碼方式

就簡單說幾句,miniLZO是用來解壓縮IsoMapPack5的。Format80是用來解壓縮OverLay的。

Format80實際上就是LCW(http://www.shikadi.net/moddingwiki/Westwood_LCW)

The LCW compression algorithm was Westwood Studios' response to Unisys' enforcement of their patent on the LZW algorithm. Before its real name was known to the fanbase, the compression format was generally referred to as "Format 80", because it is indicated with byte value 0x80 in the frame headers of?Command & Conquer 1 SHP files, and because all compressed content ends on byte 0x80.

The internally used name, LCW, stands for Lempel–Castle–Welch, a variation on?Lempel–Ziv–Welch, with "Castle" referring to Louis Castle, who wrote the algorithm. It is unknown why this name was chosen, though, since neither the algorithm nor its output resemble LZW in any way. In fact, its final compressed data is much more similar to?Code-based RLE compression, but with a much-extended set of commands. It is entirely possible the only reason for the name is that the algorithm was their replacement for LZW.

?


紅色警戒2地圖編碼研究匯總的評論 (共 條)

分享到微博請遵守國家法律
都昌县| 腾冲县| 福建省| 花莲县| 介休市| 聂荣县| 习水县| 镇沅| 会东县| 高密市| 裕民县| 东方市| 湘西| 巴彦县| 永平县| 塔河县| 霞浦县| 江津市| 肃宁县| 安乡县| 裕民县| 三都| 崇礼县| 寻乌县| 中江县| 尖扎县| 广灵县| 文化| 吕梁市| 宁乡县| 遵义市| 清徐县| 平阴县| 阳山县| 竹溪县| 罗源县| 疏附县| 青岛市| 琼海市| 宿松县| 吉林省|