解密RimWorld环世界地形数据
作者:互联网
这是来自粉丝的求助,其实这个问题应该求助那些参与多语言翻译和模组制作的人。
随着国力的不断强大,很多跨国的产品不管游戏、电影、机器还是软件系统,都越来越多的有中国人参与,这个也不例外。
我从来不喜欢直接告诉你那是什么,我更乐于告诉你我是怎么知道的,时间紧迫,本文将把几个小时的解密笔记搬上来了事~
首先,打开存档(RimWorld.Ideology.v1.3.3066.rev1166)
C:\Users\Administrator\AppData\LocalLow\Ludeon Studios\RimWorld by Ludeon Studios\Saves
这是个xml文件,文件较大,可能需要一些有内存映射技术的专业文本编辑器来提高速度;
接着是英语水平了,搜索地形的英文:terrain
最后反复F3几次“查找下一个”锁定terrainGrid节点,因为前后有roof屋顶设置
这个topGridDeflate节点应该就是地表数据了,从“乱码”风格看,应该就是base64编码
开始敲C++代码进行解码,代码略,解密出来看不出什么,为什么?
因为地形经常一大块地方是一样的,这里看不到大量重复的数值~
所以,要么是加密,要么是压缩(压缩其实也是加密),从节点名称Deflate猜测:是压缩
在开发者模式看到大量的C#堆栈调用信息,开发语言应该就是C#,因此用C#解压缩
private void button1_Click(object sender, EventArgs e)
{
byte[] buf = Convert.FromBase64String(this.textBox1.Text);
using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
{
ms.Write(buf, 0, buf.Length);
ms.Position = 0;
using (System.IO.Compression.DeflateStream ds = new System.IO.Compression.DeflateStream(ms, System.IO.Compression.CompressionMode.Decompress))
{
ds.Flush();
byte[] data = new byte[65536 * 2];
int len = ds.Read(data, 0, data.Length);
ds.Close();
// view key point
int size = (int)System.Math.Sqrt((double)(len / 2));
for (int y = 8; y <= 11; y++)
{
for (int x = 33; x < 36; x++)
{
int idx = GetByteOffset(x, y, size, 2);
ushort w_10_10 = (ushort)(data[idx] + data[idx + 1] * 256);
System.Diagnostics.Debug.Print("(" + x.ToString() + "," + y.ToString() + ")=" + w_10_10.ToString());
}
}
// display
this.textBox2.Text = System.Text.Encoding.UTF8.GetString(data, 0, len);
}
}
}
最后解出来101250字节,地图是225x225=50625个格子,因此是每个格子2个字节数据
(代码后面加上转为2字节16位整数查看)
用开发者模式修改左上角一个格子的数据,存档,再次解密,在对比:
只有100800和100801两字节改变,说明x是水平向右,而y是从下向上;
继续开发者模式,连续设置不同的地形,列出对应的16位数值:
左下角连续土地块:0x86A1=34465(-31071)
下一行第一格土地:0x86A1=34465(-31071)已确认
沼泽:0x8606=34310(-31226)
浅水:0x2CB5=11445
浅海水:0xFF89=65417(-119)
流动的浅水:0x66D4=26324
深水:0x798C=31116
深海水:0xA790=42896(-22640)
流动的深水:0x8015=32789(-32747)
粗糙的砂岩地面:0x?=?
数值上看不出高度或类型,猜测为地形定义的ID值,不管是哪种,只要这个值一样即可
至少对当前存档有效,新建一个地图,再次试验:
确定该值对不同存档有效(相同游戏版本)
那么解析一个图片,然后根据图像颜色生成地形数据的代码,很自然的就有了
private void button2_Click(object sender, EventArgs e)
{
ushort[] w_vectorTab = new ushort[8];
w_vectorTab[0] = 0x86A1; // 土地
w_vectorTab[1] = 0x8606; // 沼泽
w_vectorTab[2] = 0x2CB5; // 浅水
w_vectorTab[3] = 0xFF89; // 浅海水
w_vectorTab[4] = 0x66D4; // 流动的浅水
w_vectorTab[5] = 0x798C; // 深水
w_vectorTab[6] = 0xA790; // 深海水
w_vectorTab[7] = 0x8015; // 流动的深水
System.Drawing.Color[] s_colorTab = new Color[8];
s_colorTab[0] = Color.FromArgb(255, 255, 255); // 白色=土地
s_colorTab[1] = Color.FromArgb( 0, 128, 0); // 墨绿=沼泽
s_colorTab[2] = Color.FromArgb( 0, 255, 0); // 绿色=浅水
s_colorTab[3] = Color.FromArgb(255, 0, 255); // 紫色=浅海水
s_colorTab[4] = Color.FromArgb( 0, 255, 255); // 青色=流动的浅水
s_colorTab[5] = Color.FromArgb( 0, 0, 0); // 黑色=深水()
s_colorTab[6] = Color.FromArgb(128, 0, 128); // 深蓝=深海水()
s_colorTab[7] = Color.FromArgb(255, 255, 0); // 黄色=流动的深水
System.Drawing.Bitmap bmp = new Bitmap("C:\\Users\\Administrator\\Desktop\\terrain.bmp");
//ushort[] data = new ushort[bmp.Width * bmp.Width];
byte[] data = new byte[bmp.Width * bmp.Width * 2];
// try
for (int y = 0; y < bmp.Height; y++)
{
for (int x = 0; x < bmp.Width; x++)
{
System.Drawing.Color c = bmp.GetPixel(x, y);
// 0 if not found
int k = 0;
// find
for (int i = 0; i < s_colorTab.Length; i++)
{
if (s_colorTab[i] == c)
{
k = i;
break;
}
}
//System.Diagnostics.Debug.Assert(k >= 0);
int idx = (bmp.Height - 1 - y) * bmp.Width + x;
// x86 little endian
ushort key = w_vectorTab[k];
data[idx * 2] = (byte)(key & 0x00FF);
data[idx * 2 + 1] = (byte)((key >> 8) & 0x00FF);
}
}
// bmp will auto-dispose
using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
{
using (System.IO.Compression.DeflateStream ds = new System.IO.Compression.DeflateStream(ms, System.IO.Compression.CompressionMode.Compress, true))
{
ds.Write(data, 0, data.Length);
ds.Close();
byte[] buf = ms.ToArray();
ms.Close();
string str = Convert.ToBase64String(buf);
this.textBox1.Text = InsertCrLf(str, 100, "\r\n");
}
}
// ret
}
读取的文件固定放在桌面的terrain.bmp位图,兼顾原有100字节换行风格
粘贴到存档位置覆盖,保存,进去重新加载即可
同样的,屋顶的解析也是类似:
无:0
山岩顶:0x2A44=10820
那么根据图片来生成山岩顶的代码,依小姐姐的葫芦曲线画瓢
private void button3_Click(object sender, EventArgs e)
{
ushort[] w_vectorTab = new ushort[3];
w_vectorTab[0] = 0x0000; // 无
w_vectorTab[1] = 0; // 建筑屋顶
w_vectorTab[2] = 0x2A44; // 山岩顶
System.Drawing.Color[] s_colorTab = new Color[8];
s_colorTab[0] = Color.FromArgb(255, 255, 255); // 白色=无
s_colorTab[1] = Color.FromArgb( 0, 128, 0); // 墨绿=建筑屋顶
s_colorTab[2] = Color.FromArgb( 0, 0, 0); // 黑色=山岩顶
System.Drawing.Bitmap bmp = new Bitmap("C:\\Users\\Administrator\\Desktop\\roof.bmp");
//ushort[] data = new ushort[bmp.Width * bmp.Width];
byte[] data = new byte[bmp.Width * bmp.Width * 2];
// try
for (int y = 0; y < bmp.Height; y++)
{
for (int x = 0; x < bmp.Width; x++)
{
System.Drawing.Color c = bmp.GetPixel(x, y);
// 0 if not found
int k = 0;
// find
for (int i = 0; i < s_colorTab.Length; i++)
{
if (s_colorTab[i] == c)
{
k = i;
break;
}
}
//System.Diagnostics.Debug.Assert(k >= 0);
int idx = (bmp.Height - 1 - y) * bmp.Width + x;
// x86 little endian
ushort key = w_vectorTab[k];
data[idx * 2] = (byte)(key & 0x00FF);
data[idx * 2 + 1] = (byte)((key >> 8) & 0x00FF);
}
}
// bmp will auto-dispose
using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
{
using (System.IO.Compression.DeflateStream ds = new System.IO.Compression.DeflateStream(ms, System.IO.Compression.CompressionMode.Compress, true))
{
ds.Write(data, 0, data.Length);
ds.Close();
byte[] buf = ms.ToArray();
ms.Close();
string str = Convert.ToBase64String(buf);
this.textBox1.Text = InsertCrLf(str, 100, "\r\n");
}
}
// ret
}
无图但有真相
标签:colorTab,RimWorld,Color,vectorTab,解密,System,地形,bmp,new 来源: https://blog.csdn.net/prsniper/article/details/120620329