米米的博客

做了一点微小的工作

当你的代码出现未定义行为时,你会得到一个 segmentation fault 和一堆损坏的数据。当 Jeff Dean 的代码出现未定义行为时,一个独角兽会踏着彩虹从天而降并给每个人提供免费的冰激凌。

在前面的文章 Base64 编码的原理与实现中,笔者讲到了 Base64 编码的诸多用处,例如编码图片以嵌入 HTML 代码。如果你对此有所了解,同时还是一个炉石玩家,就不难发现,暴雪推出的卡组代码功能,本质上就是用一个 Base64 编码的字符串来存储卡组信息。毕竟,你有时会见到以 = 或者 == 结尾的卡组代码,这正是 Base64 编码的典型特征。

注释

当把卡组代码粘贴到炉石传说时,客户端将忽略以#字符开头的行,这就提供了在卡组代码中插入注释的可能性。一个例外是在卡组代码最前面的,以###开头的第一行。如果可以的话,它将被用作卡组名称。你可以从炉石中复制一套卡组到文本编辑器看看效果。
没有被注释的那一行,包含了除卡组名称外的所有信息。玩家在各大论坛、社区分享卡组时,也往往只需要这一行卡组代码。

DBF ID

为了了解编码的机制,我们需要先介绍 DBF ID。这是每张炉石卡牌(包括可收集卡牌、衍生卡牌、冒险模式专属卡牌、英雄皮肤等)的唯一标识符 —— 是的,这也是能够卡出下图这种 Bug 的原因。皮肤被标记为了一张紫卡。

啦啦啦遇到的Bug

而炉石卡组代码正是使用 DBF ID 来表示每张卡牌。DBF ID 和卡牌的对应关系可在游戏文件中解包得到。更加方便的方法是使用 HearthstoneJSON,这个网站上提供了相关 API 进行查询。你也可以直接通过这个链接下载最新的 cards.collectible.json,这里面包含了所有可收集卡牌的信息,例如各种语言的卡牌名称、描述等。

格式

如前所述,卡组代码是 Base64 编码的字节串,我们先来解码它。许多编程语言都可以做到这一点,以最好的语言 $PHP 为例:

1
2
3
4
5
$deckstring = "AAEBAf0GAA/OBpcHzAjiDP8PyBTmFrasAq6wAqW+Avi/Avm/AqLNAvjQAqbvAgA=";
#这是一个非常有趣的萨满卡组
$binary = base64_decode($deckstring);
$hex = bin2hex($binary);
#对于这个卡组,$hex="00010101fd06000fce069707cc08e20cff0fc814e616b6ac02aeb002a5be02f8bf02f9bf02a2cd02f8d002a6ef0200"

这样就能得到初步解码后的十六进制字符串。如果你不是很擅长编程,也可以搜索一些现成的 Base64 解码工具,看看它是如何工作的。
将这个十六进制字符串按每两个元素一组切割,再把它们转化为无符号整型。也就是说,它们可以是 0x00-0xff,即 0-255。一种实现的方法是这样:

1
2
$arr = str_split($hex, 2);
$arr = array_map("hexdec", $arr);

这样我们就得到了一个数组 $arr,它的每个元素都是 8 位无符号整数。** 更具体的来说,它的编码方式其实是 varint,一种自解释、不定长的编码方案。** 炉石卡组代码中包含卡牌 DBF ID 和卡牌数量等信息,它们都是整数,但大小相差很多。如果用定长编码,将会需要大量填 0,并且在未来推出 DBF ID 更大的卡牌后可能会不兼容。而 varint 编码则非常适合炉石卡组的情况。对于 varint,我们还需要进一步的解码,才能读取出有用的信息:

1
2
3
4
5
6
7
8
9
10
11
function read_varint(&$data) {
$shift = 0;
$result = 0;
do {
$c = array_shift($data);
$result |= ($c & 0x7f) << $shift;
$shift += 7;
}
while ($c & 0x80);
return $result;
}

这里的 array_shift 会将数组的第一个元素移出数组,并返回它的值。而 |&<< 等都是位运算的运算符。read_varint 函数完成了解码过程,如果有兴趣的话可以试试用 C 或 Python 实现它。
不断地执行 read_varint($arr),直到取完 $arr 中的元素,就能把内容全部解码出来。解码的结果同样是一个无符号整数的数组,但每个元素的大小不再是只有 8 位,其元素个数也少于原始的 $arr

根据作用,我们可以把卡组代码解码后的这个数组分为两个部分:元数据块和卡牌块。

阅读全文 »

在数据传输的时候,并不是所有的字符都可以受到支持,很多时候只能传输可见字符,对于不可见字符的传输需要经过特殊处理,这就是 Base64 产生的原因。Base64 编码是一种基于 64 个可打印字符来表示二进制数据的表示方法。由于,所以每 6 个比特为一个单元,对应某个可打印字符。3 个字节有 24 个比特,对应于 4 个 Base64 单元,即 3 个字节可由 4 个可打印字符来表示。在 Base64 中的可打印字符包括字母 A-Z、a-z、数字 0-9,这样共有 62 个字符,此外两个可打印符号在不同的系统中而不同。一些如 uuencode 的其他编码方法,和之后 BinHex 的版本使用不同的 64 字符集来代表 6 个二进制数字,但是不被称为 Base64。
Base64 常用于在通常处理文本数据的场合,表示、传输、存储一些二进制数据,包括 MIME 的电子邮件及 XML 的一些复杂数据。用 Base64 编码图片也是互联网上常见的做法。

Base64 的字符索引如下表所示:

数值字符数值字符数值字符数值字符
0A16Q32g48w
1B17R33h49x
2C18S34i50y
3D19T35j51z
4E20U36k520
5F21V37l531
6G22W38m542
7H23X39n553
8I24Y40o564
9J25Z41p575
10K26a42q586
11L27b43r597
12M28c44s608
13N29d45t619
14O30e46u62+
15P31f47v63/

这是一个根据上表进行编码的例子:

如果要编码的字节数不能被 3 整除,最后会多出 1 个或 2 个字节,那么可以使用下面的方法进行处理:先使用 0 字节值在末尾补足,使其能够被 3 整除,然后再进行 Base64 的编码。在编码后的 Base64 文本后加上一个或两个 = 号,代表补足的字节数。也就是说,当最后剩余两个八位字节(2 个 byte)时,最后一个 6 位的 Base64 字节块有四位是 0 值,最后附加上两个等号;如果最后剩余一个八位字节(1 个 byte)时,最后一个 6 位的 Base 字节块有两位是 0 值,最后附加一个等号。可以参考下面的例子:

阅读全文 »

即将到来的 12 月天宇将先后上演「金星最亮」「双子座流星雨极大」「水星西大距」和「小熊座流星雨极大」等多部天象大片,天文爱好者们千万不要错过。

火星合海王星

2018 年 12 月 7 日入夜后西南方天空将可观赏到火星合海王星的天象,火星将至海王星北面 0.04 度(相当于 2 角分)的近距离通过,位于宝瓶座。火星视亮度 + 0.1 等,海王星 + 7.9 等,需用小型望远镜观测。

火星合海王星

双子座流星雨极大期

每到岁末,双子座流星雨(Geminids,00004 GEM)都会如期而至。作为北半球三大流星雨之一,它的流量非常稳定,并且全球绝大多数地区的公众都有机会观测到它。今年双子座流星雨的观测条件不错,预报的极大值出现在 12 月 14 日晚 20 时 30 分,天顶每时出现率 ZHR 为 120。

辐射点在 12 月 14 日入夜后自东北偏东方升起,即入夜后就有机会见到双子座流星出现。今年月相接近上弦,但对观测影响不大,尤其是后半夜观测条件很好,但要注意防寒保暖。

双子座流星雨

水星西大距

12 月 15 日,今年最后一次水星西大距将上演。如果大气透明度足够高,在此后几天的日出前,用肉眼或借助双筒望远镜均可在黎明前的东方低空寻觅到水星的神秘身影。

水星西大距

水星的视运动是这样的:东大距 → 半轮 → 近日点 → 留 → 下合 → 极长视直径 → 留 → 降交点 → 半轮 → 西大距 → 远日点 → 极短视直径 → 上合 → 升交点 → 东大距,周期是 116 日(水星的会合周期),而水星东大距 → 西大距平均间隔为 41 天。这次水星西大距,日出时位于东南方天空,与太阳的最大角距为 21 度,亮度约 - 0.5 等,是今年观测水星的第四次最佳时机。

小熊座流星雨

小熊座流星雨(Ursids,00015 URS)往往是全年精彩天象的收官之作,该流星雨的活动期为 12 月 17 日至 12 月 26 日。天文预报显示,今年该流星雨极大可能出现在 12 月 22 日 4 时 53 分。

小熊座流星雨

小熊座流星雨在 1949 年和 1986 年曾出现过两次爆发,但今年没有爆发的预期,届时每小时最大天顶流量为 10 颗左右,而且极大正好赶上满月,观测条件不是太好。


部分图片来自维基百科,采用 CC BY-SA 3.0 许可证,作者为 Brocken Inaglory 以及 Brian Murahashi 和 Jim Albers。部分图片来自 Stellarium,该软件为开源软件,采用 GPL3.0 协议。

拓展阅读:Can You Spot December’s Ursid Meteors?

0%