普通视图

发现新文章,点击刷新页面。
昨天以前Cerallin's blog

介绍一些我喜欢的漫画家(一)

作者 Cerallin
2023年7月29日 16:31

介绍一些我喜欢的漫画家:藤丸、あるぷ、関谷あさみ和こしの。未满18岁不要点进来哦。

藤丸

第一次对藤丸有印象是看了他的《幸福期の隣人》[1]。现在想来,明明是经典的“人类衰退”世界观。当时没怎么注意,反倒是对牛油果丰胸印象深刻(笑死)。后来这篇作品被收录在藤丸的第二本单行本中,与其他剧情相独立的作品共同构建了一个废土世界观。

《8月の灯》[2,3]或许是藤丸最有人气的作品,上述所谓“废土世界观”就是从这篇开始,又以其续篇结束。尽量不剧透地讲,该作品题目来自于日本盂兰盆节的习俗,在水面上放置水灯祈福,以及接引灵魂。理所当然地,该作品有着牛郎织女般不舍的基调。

藤丸的作品非常有生活情趣。《Life is a battle field》[4]中,女经纪人模仿自家明星男友摆出招牌姿势,让我想起《百年孤独》里的一些描写。《夏待ちの日》[5]里用许多小格子体现空间的狭小,相对地又富有生活气息。《幸福期の隣人》[1]第一页的三个格子,用两人之间随意的对话和两家一跃之隔的二楼窗户,刻画出青梅竹马之间的亲密。毫不意外地,后文出现了男主翻窗户去对面家的剧情。本篇中有不少镜头暗示太平洋水位不断上升,但是海边人们的生活日常却还没什么改变。

图1: 单行本里的间页。女经纪人和她的明星男友。

あるぷ

初识あるぷ是看了他的《闇憑村》系列[68]:笨蛋魅魔干好事反把自己搭进去的故事。该系列还有一篇后日谈[9],甚至上了期刊封面。时隔两年,画风发生了些许变化,变得萌系了一些。

比起多人主角的题材,あるぷ老师描绘的二人故事更受我喜欢。例如泳池艳遇[10],假装被催眠来哄骗男朋友[11],学校里两个人的秘密[12,13]之类的。

除了在期刊杂志上连载,あるぷ老师也喜欢投稿预印本同人志。看起来他非常喜欢lovelive! sunshine!!,出了好多本《xxの休日》系列[1422]

関谷あさみ

关谷老师以两件事情广为人知。一是其擅长刻画人物形象,对小女孩神态和心理的描写无人能出其右state of art;二是其所在课题组社团z心灵代码(クオリディア・コード)中的兄妹关系领域深耕多年,硕果累累。

关谷老师的早期连载[23,24]剧情黑暗。直到初次在BAVEL投稿的《先生》系列[2527],因为故事视角以各个女主角为出发点,表面上来看又还算温馨。该系列描述了一个小学老师借助职位之便染指多个学生的故事,依旧留有黑暗的故事风格。

近年来,关谷老师减少了故事中的黑暗元素,愿意给予笔下的小女孩以真正的幸福,虽然随时可能突然刀人[28]。例如,邻家女孩和她的青梅竹马[29,30],时髦女孩和她的乡下男友[31]。不过,关谷老师对有背德感的恋情仍然情有独钟,除师生关系外,对兄妹爱也颇有研究[32,33]

目前,关谷老师减少了在期刊上发表新成果的频率,但是时常被BAVEL约稿封面。 预印本同人志方面,除心灵代码兄妹本[3436]之外,还开拓了两三个新领域,如魅魔系列[37,38]和梦魔系列[3941]

图2: 最后一格的悄悄话『到时候再做』。[30]

こしの

第一次知道こしの老师是在一区期刊COMIC BAVEL上翻到的一篇作品[42],讲述的是家里停水的女大学生借用隔壁邻居浴室的故事。

こしの老师最广为人知作品可能是《迷い猫の恩返し》[43],以至于两年半以后又出了续篇[44]。其女主角是一只离家出走的萝莉,并不是猫妖,感觉有些标题欺诈。

本人所述,こしの老师喜欢的题材是打破禁忌、性格麻烦的男女和青春期的少男少女。实际上,除了出道的几篇(看起来选题有编辑参与),后续的作品一路放飞自我,在伦理方面已经超出了我的接受范围,实属遗憾。

こしの老师的画风细腻,在COMIC BAVEL出道之前其实画过一两年的TL漫画,画风进步显著。作品中使用网点阴影点缀人体线条的做法我非常喜欢。

Fin.

夜の参考文献

[1]
藤丸, “幸福期の隣人,” COMIC 快楽天, vol. 287, pp. 11–30, Jul. 2019.
[2]
藤丸, “8月の灯,” COMIC 快楽天, vol. 265, pp. 109–128, Sep. 2017.
[3]
藤丸, “8月の灯(了),” COMIC 快楽天, vol. 293, pp. 41–60, Jan. 2020.
[4]
藤丸, “Life is a battle field,” COMIC 快楽天, vol. 250, pp. 139–151, Jun. 2016.
[5]
藤丸, “夏待ちの日,” COMIC はぴにんぐ, vol. 4, pp. 1–10, Dec. 2018.
[6]
あるぷ, “闇憑村 第一夜,” COMIC アンスリウム, vol. 72, pp. 14–42, Apr. 2019.
[7]
あるぷ, “闇憑村 第二夜,” COMIC アンスリウム, vol. 75, pp. 135–160, Jul. 2019.
[8]
あるぷ, “闇憑村 第三夜,” COMIC アンスリウム, vol. 78, pp. 15–38, Oct. 2019.
[9]
あるぷ, “魔狂の湯,” COMIC アンスリウム, vol. 103, pp. 41–60, Nov. 2021.
[10]
あるぷ, “艶カウント!” COMIC アンスリウム, vol. 56, pp. 77–96, Oct. 2017.
[11]
あるぷ, “やりたいことは?” COMIC アンスリウム, vol. 82, pp. 15–30, Nov. 2020.
[12]
あるぷ, “ルーティーン,” COMIC BAVEL, vol. 90, pp. 137–166, Oct. 2022.
[13]
あるぷ, “続・ルーティーン,” COMIC BAVEL, vol. 94, pp. 111–120, Feb. 2023.
[14]
リンゴヤ, “高海の休日.” あるぷ, Mar. 18, 2018.
[15]
リンゴヤ, “渡辺の休日.” あるぷ, Aug. 12, 2018.
[16]
リンゴヤ, “渡辺の休日~episode of tsuki~.” あるぷ, Mar. 14, 2019.
[17]
リンゴヤ, “高海の休日~2日目~.” あるぷ, Aug. 12, 2019.
[18]
リンゴヤ, “津島の休日.” あるぷ, Dec. 07, 2019.
[19]
リンゴヤ, “松浦の休日.” あるぷ, Dec. 31, 2019.
[20]
リンゴヤ, “桜坂の休日.” あるぷ, Aug. 14, 2022.
[21]
リンゴヤ, “黒澤の休日.” あるぷ, Dec. 31, 2021.
[22]
リンゴヤ, “桜内の休日.” あるぷ, Dec. 31, 2022.
[23]
関谷あさみ, YOUR DOG. 茜新社, 2008.
[24]
関谷あさみ, 僕らの境界. 茜新社, 2016.
[25]
関谷あさみ, “先生、,” COMIC BAVEL, vol. 20, pp. 193–216, Dec. 2016.
[26]
関谷あさみ, “先生、第2話,” COMIC BAVEL, vol. 24, pp. 192–211, Apr. 2017.
[27]
関谷あさみ, “先生、最終話,” COMIC BAVEL, vol. 28, pp. 167–192, Aug. 2017.
[28]
不可不可, “無題.” 関谷あさみ.
[29]
関谷あさみ, “隣の家の彼女,” COMIC BAVEL, vol. 42, pp. 43–62, Oct. 2018.
[30]
関谷あさみ, “描き下るし 隣の家の彼女,” in ラフスケッチ, 文苑堂, 2019, pp. 193–194.
[31]
関谷あさみ, “波際より,” COMIC BAVEL, vol. 42, pp. 19–38, Oct. 2019.
[32]
関谷あさみ, “4% - shallow sleep,” COMIC BAVEL, vol. 58, pp. 43–74, Jun. 2018.
[33]
関谷あさみ, “4%,” COMIC BAVEL, vol. 58, pp. 17–24, Feb. 2020.
[34]
不可不可, “この世界の終わりまで.” 関谷あさみ, Aug. 12, 2018.
[35]
不可不可, “湯けむり旅情 房総編.” 関谷あさみ, Aug. 23, 2021.
[36]
不可不可, “ファミリーコンプレックス.” 関谷あさみ, Dec. 30, 2021.
[37]
不可不可, “インスタントサッキュ.” 関谷あさみ, Sep. 20, 2021.
[38]
不可不可, “インスタントサッキュ2.” 関谷あさみ, Aug. 14, 2022.
[39]
不可不可, “マヨナカ侵略者.” 関谷あさみ, May 05, 2022.
[40]
不可不可, “まひるの悪魔.” 関谷あさみ, Jan. 07, 2023.
[41]
不可不可, “夜ふけの悪魔.” 関谷あさみ, Jun. 03, 2023.
[42]
こしの, “バスロマン,” COMIC BAVEL, vol. 58, pp. 279–300, Feb. 2020.
[43]
こしの, “迷い猫の恩返し,” COMIC BAVEL, vol. 54, pp. 279–300, Oct. 2019.
[44]
こしの, “迷い猫の恩返しさんっ,” COMIC BAVEL, vol. 87, pp. 39–58, Jul. 2022.

使用MATLAB绘制3D图形

作者 Cerallin
2023年6月1日 14:42

近日帮别人用MATLAB画图,记录一下。 MATLAB版本为最新的R2023a。毕竟学校买了正版,不用白不用。

使用两个向量生成meshgrid

1
2
3
4
5
% meshgrid 的两个参数长度需保持一致
[gamma_arr, nh_arr] = meshgrid( ...
0.077562 : 4E-7 : 0.0775699999, ...
3.8794E19 : 4.5E14 : 3.88029999E19 ...
);

假设meshgrid的参数中两个向量长度为N,则运行结果是得到两个NxN的矩阵X和Y,例子如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>> [X, Y] = meshgrid(1:4, 5:8)

X =

1 2 3 4
1 2 3 4
1 2 3 4
1 2 3 4


Y =

5 5 5 5
6 6 6 6
7 7 7 7
8 8 8 8

这样一来, 表示二维直角坐标系上的一个点。

计算因变量的值

1
2
3
4
5
6
7
8
9
10
11
12
13
% 预分配矩阵内存
F_mat = zeros(length(gamma_arr), length(nh_arr));

% 愚蠢地逐个计算
for i = 1 : length(gamma_arr)
% 用 parfor 小小地加速一下
parfor j = 1 : length(nh_arr)
gamma = gamma_arr(i, j);
nh = nh_arr(i, j);
% F 为我定义的自由能函数,其中 jnkbt, N, NC 为常量
F_mat(i, j) = F(gamma, nh, jnkbt, N, NC);
end
end

这里我对F_mat的计算方法不是很满意。虽然可以用parfor并行计算,但是还是想用类似于闭包的东西写得简洁一些(并且相信MATLAB做足了优化,不需要我手动并行了)。

3D作图

MATLAB绘制的3D曲面
1
2
% surf 可以作出一个平面
surf(gamma_arr, nh_arr, F_mat)

自此图就画好了。

附录:一些常见技巧

定义一个函数

1
2
3
function S = SUM(a, b)
S = a + b;
end

其中S是函数的返回值,当函数执行完毕,S的值就是函数的返回值。实际上,函数名称和函数返回变量可以重名,例如可以把第一行修改为function F = F(a, b)

求解方程的数值解

1
2
3
4
5
6
7
8
syms x;

% Equation
eq = x*x + 2x - 3;

% default
res = vpasolve(eq == 0, x);
res = vpasolve(eq == 0, x, 100); % in which 100 is an initial guess

首先需要定义若干需要求解的符号变量(用于解方程的符号而不是真正的编程变量),然后调用vpasolve即可。顺便一提solve可以求解析解,但是一旦求不出来,就会先报一个warning然后fallback到vpasolve求解。

判断一个数是否为实数

MATLAB提供了一个函数isreal用于判断一个数是实数或者复数。

1
2
3
4
5
r = 1;
nr = 1 + 3i;

isreal(r); % 1(true)
isreal(nr); % 0(false)

文档指出,虚部为0的数会被认为是复数:

1
2
c = complex(1) % 1.0 + 0.0i
isreal(c) % 1(true)

vpasolve的返回值可能是虚部为0的复数,因此需要改用虚部是否为0判断是否为实数:

1
2
c = complex(10) % 10.0 + 0.0i
imag(c) != 0 % 1(true), i.e., is real.

Fin.

NDS架构简要分析

作者 Cerallin
2023年3月10日 17:12

原文标题:Nintendo DS Architecture - A Practical Analysis[1]。原作者:Rodrigo Copetti,译:Cerallin(我自己)。

译者序

两个月以来我一直在开发NDS的自制游戏,从零开始。当初这篇文章带我入门了NDS的底层架构,除libnds的文档外,就属这篇文章对我帮助最深。每当我想放弃时就来看看,然后就会发现自己之前思路的错误之处,就又能继续下去了。

为了让更多的人看到这篇充满希望(?)的文章,这周我花时间翻译了一下。没想到很快就翻译完了。原网站不久就会把我的翻译部署上去吧,在那之前,我先水篇博客(

实际上国内也不乏神贴,例如NDS平台破解&烧录史。但这些文章一般从玩家的角度考察NDS世界,自制游戏开发者视角的文档少之又少。想来国内就没有NDS自制游戏的土壤,因为NDS不算正式进入过中国市场,极客们都去搞破解汉化和改版去了。时至今日我才试探NDS编程,应该算是逆时代而行了。

总而言之,本文讨论的范围甚广,对于自制游戏来说,很多细节还需要我自己去深入研究。如果你想了解NDS运行的原理,那么这篇文章正适合你。如果好奇PC以外的计算机的架构,也可以看看原作者该系列的其他文章。如果觉得本文对你有所帮助,可以去原文网址底部捐赠,或购买电子书

顺便一提,本译文遵循CC 4.0协议。虽然本网站的全部文章都有版权声明四个字,但还是强调一下。

快速入门

这款游戏机回应了掌机生态中无法满足的许多需求,有一些创新也有一些妥协,不过这种组合可能会为新颖而独创的内容铺平道路。


CPU

和任天堂的上一代掌机一样,NDS的系统围绕一个名为CPU NTR 的大芯片展开。其中“NTR”是“Nitro”的缩写,是最初的NDS的代号。

CPU NTR使用两个不同的ARM CPU实现了一个有趣的多处理器架构。在ARM Holdings正式发布多处理器解决方案之前这个设计就完成了。因此,考虑到当前的技术实现,可能有人认为NDS的多处理器架构不是很正统。

设计

虽然这不是本系列第一次分析游戏机的并行系统,但是NDS的设计确与其他游戏机有很大不同。例如,NDS的设计有别于世嘉土星“实验性质”的主从配置,也不同于PS1或者N64的“协处理器”方案。NDS包括两台非常独立的计算机,可以分别独立执行操作。每台计算机都有一条专用的总线。这种设计方法被称为非对称多处理(Asymmetric multiprocessing)。 CPU因此而相互依赖,并将左右这款游戏机的整体性能。

话是这么说,现在我们来看看这两个CPU:

ARM7TDMI
ARM7 的结构与组成部分。

首先说说我们较为熟悉的CPU。ARM7TDMIGBA的CPU相同,但现在运行速度提高到约34 MHz(原速度的两倍)。它仍然包含所有原有功能(特别是Thumb)。

然后讲讲新变化:由于任天堂的工程师将ARM7放置在大多数I/O端口旁边,这个CPU将负责调节和协助I/O操作。事实上,另一个处理器不能直接连接I/O。如你所见,ARM7不是负责整个系统的“主”处理器。它负责在多个组件之间传递数据,是用来分担一部分主CPU负载的“子处理器”。

ARM946E-S
ARM9 的结构与组成部分。

这是运行频率约67 MHz的“主”CPU。如果忽略掉时运不济的ARM8系列,可以认为ARM946E-S是ARM7的“下一代”处理器。作为ARM9系列的一部分,它不仅继承了ARM7TDMI的所有特性,还包括一些额外的部分[2]

  • ARMv5TEISA:与以前的4代相比,支持了一些新的指令,乘法器也更快。
    • 如果仔细观察核心的名称,字母“E”表示增强型(Enhanced)DSP。这意味着这些新指令大多与信号处理有关。
  • 5级流水线:与之前的3级流水线相比,这是另一个提升。
  • 12 KB L1 高速缓存:该核心新增有高速缓存,其中8 KB用于指令,4 KB用于数据。
  • 48 KB紧耦合存储器(Tightly-Coupled Memory, TCM):类似于Scratchpad内存,但该内存分别存储指令(32 KB)和数据(16 KB)。

任天堂在ARM9周围还添加了以下组件:

  • 一个除法器和平方根单元,以加速这些类型的操作(ARM9本身不能执行这种类型的数学计算)。
  • 一个直接内存访问控制器(Direct Memory Access Controller, DMAC):独立于CPU加速内存传输。结合使用高速缓存,CPU和DMA有并发运行的潜力。
    • 缓存和DMA可以提供很多性能,但也会造成新的问题,如数据可靠性。开发者需要手动维护内存一致性,例如,先刷新write-buffer然后再触发DMA。

就NDS这硬件,也许不难发现孩子们喜欢这款游戏机的真正原因?

互连

到目前为止,我已经介绍了这两个CPU如何单独工作。但是作为一个整体运行,它们需要不断合作。为了实现这一点,两个CPU直接使用专用的FIFO单元[3]进行“对话”,该数据块包含两个64字节队列(最多16个元素),用于双向通信

FIFO单元示意图。

具体工作方式如下:发送方CPU(发送消息的CPU)将一个32位数据块放入队列中,接收方CPU可以从队列中拉取该块并对其执行所需的操作。

每当队列上写入一个值,它可以被任一CPU主动获取(轮询),但这需要不间断地检查新消息(开销会很大)。或者,也可以激活一个中断单元,以便在队列中有新消息时通知接收方。

主存储器

就像GBA一样,NDS的RAM也被分散在许多不同的位置,以便根据访问速率来安排数据远近。总之,可用通用内存如下:

该掌机的RAM模型。
  • 32位总线的32 KB WRAM(Work RAM):用于存储在ARM7和ARM9之间共享的快速数据。
    • 请记住,同一地址一次只能由一个 CPU 访问。
  • 32位总线的64 KB WRAM:同样用于快速数据,但只能从ARM7访问,就像GBA一样。
  • 16位总线的4 MB PSRAM:速度较慢,可以由任一CPU访问,由内存接口单元控制。
    • 伪静态随机存取存储器(Pseudo SRAM, PSRAM)是DRAM的一种变种,与DRAM相比,PSRAM芯片自己就能执行刷新操作。因此,它的行为类似于 SRAM(DRAM的一种替代品,更快但更昂贵)。这种设计让我想起了1T-SRAM

向后兼容性

尽管架构与前代有很大不同,但NDS仍成功地保留了关键部分,从而原生支持GameBoy Advance (GBA)游戏。

但是,为了将NDS转为“内部”GBA,前者包括一组软件例程(software routines),可以将游戏机设置为AGB兼容模式(AGB Compatibility Mode)。此过程中,实际上会终止ARM9,禁用大部分“特殊”硬件,重定向总线,将ARM7置于控制位置,并将其频率降至16.78 MHz。最后,ARM7开始引导初始的AGB BIOS,该BIOS引导GamePak卡带(就像本来的GBA一样)。这种模式仍然展示出一些GBA所没有的特征,例如游戏显示时有黑边框(在下一节中我们将看到新屏幕分辨率恰好更大)。此外,由于NDS有两个屏幕,所以用户可以设置用哪个屏幕显示GBA游戏。

一旦进入GBA模式,就没有退路了,必须重置游戏机才能重新激活其余硬件。

秘密和限制

单个廉价芯片中安装了这么多复杂组件,强制他们协作导致出了一些问题,这并不是什么神秘的事情。

性能阉割

有时候我真想知道任天堂计划如何使用这两个CPU,以及他们是否清楚这种设计会损失一些性能。

让我们从ARM9开始。该CPU的运行速度是ARM7的两倍,但大部分(甚至是全部)的I/O都依赖于ARM7。因此等待ARM7的响应可能导致ARM9频繁阻塞。不仅如此,ARM9的外部总线速度只有一半,因此存在一些瓶颈问题。

此外,主存储器总线只有16位。无论哪个CPU需要从存储器中提取一个字(32位宽),接口都会阻塞CPU,然后占用高达3个“等待”周期,直到拼成一个完整的字。内存访问不连续时影响最严重,每次内存访问都会阻塞。这个问题在指令获取时也会出现。不幸的是,当时的ARM不支持顺序操作码获取(sequential opcode fetching),这也影响到了Thumb指令(因为每次获取16位数据都要以32位的块为单位进行)。另一方面,这个一些资料所谓的“penalty”,可以充分利用缓存和TCM来缓解。

总的来说,这意味着在最坏的情况下,这款“猛烈”的ARM9 66 MHz的处理能力实际上会被降低到只有约8 MHz,如果程序的缓存/TCM的利用率极低的话。

如果需要详细报告,我建议查看Martin Korth的文档[4],尤其是“DS Memory Timings”一节。

关于硬件选择的问题

当初研究GBA的CPU时,我对ARM7的潜力感到非常惊讶:该CPU不仅可以完成设计之内的任务,还可以协助完成其他任务,例如提供音频序列或伪3D图形。

与此相关的是,在商业化ARM7的过程中,ARM Holdings与DEC合作设计了ARM芯片的高端版本。为此,DEC采用了他们的Alpha处理器的数据通路设计,并结合了ARM的设计[5]。研发出的一系列名为StrongARM的新CPU,速度得令人惊讶。以去除某些功能(如Thumb指令集和调试功能)为代价,DEC成功地跨越了兆赫门槛,触及高达233 MHz的运行速度。作为一名普通用户,如果你准备购买新的ARM电脑(例如RiscPC),摆在你面前的是两个选择:一台搭载40 MHz ARM710老旧处理器的电脑,或者一台运行速度快约582%的StrongARM电脑。 StrongARM的影响如此巨大,以至于ARM Holdings吸收了一些StrongARM的特性,用于生产他们的下一代CPU,也就是ARM9及之后的系列。其余就是些老黄历了。

但这里就是我的问题所在:明明见到了ARM世日新月异的发展,为什么任天堂最终选择了一个速度极慢的ARM9处理器,再加上一个速度更慢的ARM7,而不是选择一个更快的ARM9(甚至是一个StrongARM)呢?抛砖引玉,其他公司(例如苹果)就在新出的Newton PDA系列中采用了StrongARM处理器。

我不是要批评任天堂的决策,但我认为新兴技术数量多到很难让人视而不见。我猜可能是为了保全电池寿命(译注:从充满电到用到没电的使用时长)并且控制生产成本(通过使用与GBA相同的CPU)。

图形

这一部分有些不太寻常,因为这款游戏机不仅有多个屏幕需要绘制,而且把传统的图块(tile)引擎与现代渲染器结合在了一起。

首先说说硬件:NDS包含两个LCD屏幕,每个屏幕的都有256x192个像素,比GBA多约20%个像素点。它们可以显示262144种颜色(18位),刷新率约60 Hz。

架构

图形子系统可以绘制2D和3D对象。前者由二维几何图形组成,其中填充了8x8像素的位图(称为“图块”),而后者则使用顶点绘制三维对象(多边形)。

深入了解操作这些屏幕的内部芯片,我们可以观察到这个掌机具有独特的2D和3D几何图形硬件。2D数据由我们熟悉的引擎 PPU(现在只称之为2D引擎)操作,而3D数据由一个全新的子系统处理。值得一提的是,虽然这并不是第一款推出3D图形功能的游戏机,但它仍然是第一款使用自研图形渲染器来呈现3D图形的游戏机。

不同图形单元的布局。

现在,这两个引擎必须连接到两个屏幕其中之一。对于只有2D图形的游戏来说,这不是问题,因为每个屏幕都有一个2D引擎。然而,那些想展示前沿特性的游戏却只有一个3D引擎可用。因此,每次只能在一个屏幕上使用3D功能。但是2D和3D对象如何混合显示?别急,我先分别解释清楚这两个引擎,之后再讨论这个问题。

使用2D图形构建帧

在我们检查各阶段之前,我建议先阅读有关GBA PPU的这篇文章,因为在这里我只会提到构建“下一代”2D游戏的变化。另外,由于有两个引擎,这里我们把第一个引擎称为主引擎(Main),第二个则称为辅助引擎(Sub)。虽然这并不一定意味着哪个屏幕连接了哪个引擎,而且主引擎比辅助引擎包含更多的功能。

我将借用新超级马里奥兄弟(New Super Mario Bros)的资源(assets)来帮助解释。

图块(Tiles)
从VRAM中提取的图块。出于演示目的,使用了默认调色板。

到目前为止,我们都知道基本的图块系统是如何工作的,但是NDS的图块处理有什么特别之处呢?其实,总共有656 KB VRAM可用,这一块被分割成不同的bank:四个128 KB bank、一个64 KB bank、一个32 KB bank和三个16 KB bank。开发者可以随便往bank里填充数据并将引擎指向所需数据的位置。两个引擎都可以从这些bank读数据,但是它们不能同时访问同一个bank。

尽管如此,数据排布方式还是有一些限制。例如,ARM7只能访问两个128 KB的bank,这两个bank不能存储sprite;最后16 KB bank只能被“辅助引擎”访问;等等等等……你懂得。

最后一句,3D引擎我们稍后再讨论,它可以访问其中某些bank以获取纹理(texture)。

背景类型

Super Nintendo时代开始,PPU一直致力于为构建背景图层提供更多的灵活性。 14年后,有这样一款芯片,可以获取图块并应用许多仿射变换。甚至,最终可以从frame buffer构建图层。

在我们讨论2D引擎显示背景的不同模式之前,让我们先了解一下引擎可以生成哪些类型的背景:

  • 字符类型(Character type)组:这些背景类型遵循传统的图块系统,通过填充图块来渲染帧。
    • 静态(Static)或者说“文本”(text)背景:普通背景,最大支持512x512像素,256色和16个调色板。包括了所有典型的特效(effects)(H/V翻转、H/V滚动、马赛克、alpha混合),外加一个额外的 "褪色”效果。最多可使用1024个图块。
    • 仿射(Affine)背景:一个带有仿射变换(affine transformations)的背景,不支持水平/垂直翻转,并且只能获取256个图块(最大值的四分之一)。图层大小为1024x1024像素。
    • 仿射扩展(Affine Extended)背景:与affine背景相同,但是可使用1024个图块,并且支持水平/垂直翻转。
  • 位图(Bitmap)类型组:引擎不再处理图块,直接把VRAM用作frame buffer。所有的位图类型都继承了character affine背景的所有效果。
    • 256色扩展(256 colours Extended):使用512x512 px的帧缓冲区。
    • 直接颜色扩展(Direct colour Extended):与256色扩展类似,但frame buffer支持的颜色提高到32768种(15位)。
    • 大屏幕(Large screen):利用所有四块128 KB大的VRAM块来渲染一个1024x512 px大的frame-buffer。

开发者不能任意选择背景类型。但是游戏机提供了一系列背景模式,为各类型设置了不同的组合。

背景模式
背景图层 0 (BG0)。这个特定的图层将在某些扫描线上水平位移以模拟云的移动。
背景图层 2 (BG2)。
背景图层 3 (BG3)。

背景类型实战演示。主引擎和辅助引擎都提供了六种操作模式,所有模式都能生成四个背景图层,但每个图层的能力有所不同:

  • 模式0:4个static图层。
  • 模式1:3个static图层+1个affine图层。
  • 模式2:2个static图层+2个affine图层。
  • 模式3:3个static图层+1个extended affine图层。
  • 模式4:2个static图层+1个affine图层+1个extendine affine图层。
  • 模式5:2个static图层+2个extended affine图层。
    • 这是最常用的模式,因为极其灵活。
  • 模式6:3D引擎渲染的1个static图层+ 1个large screen。
    • 由于VRAM bank的空间只够一个frame buffer,因此只有主引擎能使用该模式。
Sprites
渲染的Sprite图层.

Sprites或者说“objects”继承了GBA PPU的功能,但新增两个重要功能。

首先,OAM(存储sprite条目(sprites entries)的区域)的大小现在有2 KB,使每屏每帧可以显示多达128个sprite。因此,两个引擎都分配有1 KB。

其次,OAM现在可以引用VRAM中的位图,而不仅仅使用图块和调色板。这是一种与tile系统不同的方法。实际上,同一帧中可以同时存在这两种sprite“模式”,因为此选项是在每个单独的sprite上设置的。

结果
合并所有图层……是不是少了什么?

由于每个图层都是即时渲染的,最后阶段需要合并所有内容并将其发送到所选屏幕。以前基于PPU渲染游戏机基本如此,但这是否意味着NDS也到这里结束了?

还没呢!主引擎仍然必须从另一个引擎——最强大的引擎——获取图层。

3D 加速

如果你之前玩过NDS,那么你就知道这款游戏机可以显示一定数量的3D图形。不同于GBA游戏,这些3D图形并非由CPU处理。不过,CPU-NTR包括两个组件来构建3D引擎。有趣的是,任天堂采用的设计让我想起了SGI的RCP

回顾“背景模式”部分,你会注意到每个模式都至少有一个static背景,这是因为你可以用3D引擎生成的图形来填充该图层。唯一需要注意的是只有主引擎可以这样做,这也是模式6仅适用于主引擎的原因之一。

几何引擎
几何引擎的架构。

如果读过第四代或第五代游戏机的文章,你可能会想知道……SIMD处理器哪去了?这是个好问题,因为ARM9并不擅长矢量运算,而且我不认为专用除法器很够用。这就是为什么任天堂嵌入了一个叫做几何引擎的组件,它负责顶点变换投影光线裁剪剔除多边形排序,后者对于正确使用透明特性是必不可少的。

这个引擎有一些严格的限制,特别是它能够处理的多边形数量:有额外的248 KB RAM可用于存储处理过的几何体,数量可以达到2048个三角形或1706个四边形。使用多边形条带的话(而不是单个多边形)还可以存储更多。为了对这个数字有一些概念,我建议查看之前文章中的“交互模型”部分,你会发现这个限制令人担忧,但不要忘记掌机的屏幕分辨率也要小得多……所以算是抵消了一点。

无论如何,这个引擎是通过一个Command FIFO来控制的,其数据是由CPU或DMA填充。该FIFO可以存储256个条目,并且它还有一个叫做PIPE的缓冲区,用于存储额外的四个命令(总共260个命令)。

渲染引擎
渲染引擎的架构。

渲染引擎负责将向量转换为像素(光栅化),着色(纹理映射)并应用光照和其他效果。它依靠透视校正Gouraud 着色分别用来处理插值纹理和光照。此外,该单元提供一些现代特性,例如fogalpha混合深度缓冲Z缓冲,或是一种被称为W缓冲的变体),模板测试抗锯齿。虽然最后一项非常原始(将多边形的外部边缘设置透明而已),而且只能用于不透明像素。

渲染系统采用了新旧结合的方式:它没有直接渲染到frame buffer,而是采用行缓存渲染(line buffer rendering),在每一条扫描线上填充像素(类似于2D引擎),并将结果存储在一个较小的缓缓冲区中。这是因为3D引擎必须与2D绘图器同步工作。

没有传统的frame buffer,光栅化器采用了扫描线渲染(scan-line rendering),遍历每个扫描线以处理其中的多边形边缘。 Arisotura(MelonDS模拟器的开发者)称,对于每个四边形,渲染器只能在每条扫描线上填充一个span[6]。这可能会有隐患,因为如果四边形是凹四边形或者有交叉的边,结果会变得一团糟。

关于效果,该单元还提供shadowing和一个被称为Toon Shading的独特功能(又称Cel Shading):该单元不可编程,但也可以通过改变照明参数达到卡通效果。

结果
这就对了嘛!

渲染引擎将不会将结果写回frame buffer以供显示,而是写入一个名为Color Buffer的块中,该块可以存储多达48条扫描线。 2D引擎会按照先进先出(FIFO)的方式获取每条扫描线,以填充BG0图层。

3D渲染在2D渲染之前开始,使后者在必要时能够应用图形变换到新图层。主引擎还允许捕获生成的2D帧、3D帧或组合帧,将其与VRAM中的另一个帧混合(blend),并将结果写回VRAM,之后可以用于显示。

在控制方面,由于采用了基于双缓冲的机制,渲染引擎还允许在帧中间改变参数,该机制可以保留旧状态的副本,直到当前帧绘制完成。因此,不会出现图像撕裂的现象。

知名游戏比较

一些最初在NDS上发布的游戏试图模仿另一台游戏机(即N64)上的游戏。玩家可能会看到两种版本之间存在重大差异,我想简要总结一下原因:

第一个例子
超级马力欧64(1996) 以320×240像素渲染。
超级马力欧64 DS(2004) 以256x192像素渲染。
第二个例子
马力欧卡丁车64(1996) 以320×240像素渲染。
马力欧卡丁车DS(2005) 以256x192像素渲染。

所以,为了解释这里发生了什么,我将根据一些论坛用户的说法,整理出几条不同的解释:

  • NDS版的纹理看起来更加方块 → 渲染引擎没有使用任何滤波器,因此纹理使用“最近邻”方法进行插值。
  • NDS版的纹理看起来更加丰富 → 渲染引擎没有4KB TMEM的限制。相反,除了提供的压缩机制外,最多还有512KB的VRAM可用,因此可以加载更多数据。
  • NDS模型的边缘出现了锯齿 → 与N64相比,NDS模型的分辨率较低。
  • NDS的纹理当从远处看起来会出现失真的情况 → 光栅化器使用的是定点坐标。低分辨率和mip-mapping的缺乏也加剧了走形程度。

简要的概述这就是这些。如果想要了解更专业的情况,你可能需要深入研究两个引擎,甚至反汇编这两个游戏来研究所使用的功能以及他们的用法。

交互模型

我更新了wee model查看器,添加了“最近邻插值”功能。这样你就可以使用你自己的GPU来查看NDS模型了。

请前往原网站体验交互式小部件。

新马力欧兄弟(2004) 636个三角形。
任天狗狗(2005) 750个三角形。

尽管我们讨论了图形子系统的诸多限制,但很多游戏确实充分利用了它的功能。

音频

大多数音频改进都集中在增强GBA所提供的那些PCM声道上。我们之前看到过,GBA游戏最终将软件序列优先于PSG,结果非常令人印象深刻。

因此,新的音频系统总共有16个PCM声道(channels),可以将混音任务转移到硬件上。 PCM采样可以是8比特GBA风格)、16比特(最高解析度)或ACPCM(压缩形式)。无论如何,混音器都会产生一个立体声信号,可以通过扬声器(现在是立体声)或耳机播放。它还可以将生成的立体声数据写入WRAM,让子处理器(ARM7)能够应用一些效果,例如混响。

尽管以上内容已经介绍了不少,但这是否意味着NDS终于可以播放编码音乐(例如MP3)?这是可行的(实际上,许多自制程序都实现了某种形式的音频解码),但音频解码需要大量的带宽和处理能力[7]。所以,音频序列仍然是可行最高的选项。

有关PSG的一个(也许两个)古怪之处

NDS可以运行GBA游戏,因此它应该具有类似于前代PSG的功能(无论是通过硬件还是软件实现)。正好,最后6个声道包含一个“PSG模式”,允许其中任何一个合成脉冲或自定义波形,并且其中只有两个声道可以产生噪声。但是GBA游戏没有使用其中任何一个功能!

你看,混音器的输出频率是32 kHz,解析度为10比特(远低于输入样本的质量)。此外,混音器没有执行任何形式的插值来平滑精度的损失。这些限制对样本来说并不理想,因为它会引入噪声。尽管这种现象的实际感知取决于你的听觉能力(我不会注意到有“噪声”,除非我把音量调高并将其与16比特版本放到一起比较),但这仍然是从使用8比特解析度软件混合采样以来的一大进步。与之相比,PSG的声音的混叠效应更为棘手,因为降采样信号可能会引入错误的谐波,使原始的PSG音调失真。然而,像《新超级马里奥兄弟》这样的游戏,脉冲波伴奏就用得很欢,所以我不认为PSG完全没有用处。

回到主题,GBA游戏是如何处理的呢?答案是没有处理。任天堂为GBA模式适配了一个单独的声音系统(在同一外壳里),其中包括符合GBA的规格的私有的声道和混音器。这样,GBA游戏就不会被新混音器限制所影响。不幸的是,NDS游戏无法使用这个子系统,因为它与NDS的系统相隔离。换句话说,它不能输出到NDS的混音器。

交互式比较

我构建了这个交互式小部件,让你可以自己来回比较,从而理解新的音频系统如何影响新一代游戏的配乐。每个小部件都可以播放相同的曲子,但允许你在旧版和新版之间切换(建议戴上耳机以更好地体验差异)。试一试!

请前往原网站体验交互式小部件。

不得不说,我得稍微增加GBA原声音乐的增益才能把音量调整正常,这往往会影响信噪比(来回切换时请记住这一点)。无论如何,我希望您能感受到声音子系统是如何演变的。

提升难度

现在请让我展示一些棘手的情况。移植游戏的原平台有一些独特的音频功能,在NDS上重现这些功能可不简单。现在请你来当评委:

请前往原网站体验交互式小部件。

正如你从第一个示例中听到的(特别是在最后10秒钟内),要与SNES的S-SMP所提供的功能相竞争有些困难。

必须承认,第二个例子是我故意放上去的。我的意思是,究竟发生了什么?就好像这个新的编排最初就是为雅达利游戏机而设计的一样。如果你问我,我认为NDS可以处理某种形式的FM到PCM重新采样,所以这个新的极简主义编排可能只是一种创造性的方法。

I/O

长话短说,I/O严格交由ARM7处理。事实上,除传递数据之外,你不会看到ARM7处理器有多少操作……这真是太可惜了。

卡带与内存的访问

有一个连接三个endpoint的外部内存接口:Slot-1(用于放置NDS卡带)、Slot-2(用于放置GBA卡带或配件)以及4 MB PSRAM(主内存)。两个CPU均可访问该接口。接口中包含可以修改的寄存器,以便设定CPU优先级,在同一时间有两个总线请求时优先哪颗CPU。

外部存储器模型,标记了数据总线的宽度。

现在要说重要的一点:NDS卡带没有内存映射。为了让任一CPU读取游戏数据,首先必须把内容复制到RAM中。这是通过向卡带发送包含8位命令和32位地址引用的数据块来实现的。之后,可以通过32位寄存器或DMA手动获取数据。数据总线宽度只有8位,但速度最高可达5.96 MB每秒(如任天堂所述)。

用于保存备份的芯片(如EEPROM、FLASH 和 FRAM)可以通过SPI总线(串行)访问。该总线使用自己的一套命令集,并连接到一个24位地址总线。

使用本来的引脚排布的话,Slot-2卡槽是内存映射的。但在DS模式下,为了适配提供扩展功能的硬件(额外的RAM、震动等),地址会被移位。就像GBA一样,ROM总线为16位宽,RAM总线则有8位宽。

外部设备

ARM7也连接到另一个SPI节点,即触摸屏控制器的接口。该接口可以操作底部屏幕(电阻式,需要使用触控笔)和闪存存储器(固件就存储在这里,稍后再做详细介绍)。

愿望之屋 天使的记忆(2007)前面提到的总机难题。为了营救小姑娘,玩家必须同时用两根手指滑动屏幕才能开灯。
但如果你做错了……

这个触摸屏的一个有趣的特点,除了检测X/Y位置的功能,它还可以返回对角线位置(用于计算“压力值”,表示施加压力的区域)。不幸的是,这个特性从未在官方SDK中公开。据我所知,没有游戏最终使用了这个未记录的特性,除了自制游戏。

许多人指出,《愿望之屋 天使的记忆》依靠这个特性来解决其中一个谜题,需要用户同时使用两个手指。然而,情况并非如此。在使用no$gba debugger实验之后,可以发现这个谜题并没有用到压力数据,而是检查X/Y值是否交替剧烈变化。游戏将这种效果解释为用户用两个手指按压屏幕的结果。

最后,同一堆东西里还有实时时钟 (real-time clock, RTC)。

无线网络

最后但同样重要的是,掌机包含一个运行在2.4 GHz频段的无线控制器,提供了一些创新功能:

  • Internet Play:使任何游戏都能使用标准Wi-Fi连接连接到LAN网络。
  • Multi-card Play:使用专有协议,最多可以联机16台掌机。
  • Single-card Play:游戏可以将上传程序到另一个没装游戏卡的DS。

操作系统

我觉得基本可以认为,这一世代的每款游戏机都带有了某种交互界面。 NDS继承了以轻量级API为基础的操作系统模型,以简化I/O访问;但同时也提供了一个精简的用户界面,以供调整设置或者运行“应用程序”。

话虽如此,它的操作系统分散在多个芯片中。让我们从启动时读取的芯片开始。

入口点(Entry point)

在某一时刻,ARM7和ARM9将需要初始化硬件。 NTR-CPU包括两个不同的小型ROM芯片来做这件事:

  • 一个连接到ARM9总线的4 KB BIOS
  • 一个连接到ARM7总线的16 KB BIOS

当掌机启动时,每个CPU从各自的ROM启动引导。这是因为它们的复位向量(reset vector)指向每个芯片(作为参考,ARM9的向量位于0xFFFF0000,而ARM7的向量位于0x00000000[8]

继续推进,两个BIOS都存储两组例程:引导代码和中断调用。考虑到前代掌机的历史,后者我们并不陌生,然而前者的复杂度却增加了:除了硬件初始化之外,ARM7的代码还将对DS卡带(如果插入了的话)进行一些检查来确保安全性。

运行引导代码后,两个CPU将同步,以便它们可以开始充当“单个机器”:事实证明,ARM9的加载完成要比ARM7快得多。于是ARM9向ARM7发送一个4比特值,在半无限循环中阻塞等待ARM7的响应。一旦ARM7响应,两者同时“越过终点线”,也就是说,它们现在同步了。

机会之窗

如果您现在拥有或者曾经拥有NDS,您可能会注意到只能在游戏机开机前插入卡带才能玩游戏。这是因为ARM7的BIOS在启动时对卡带进行了一些检查(更多详情参见最后一节),如果所有测试都通过,ARM7的游戏可执行程序将被复制到WRAM,ARM9的程序则被复制到主内存。

如果出于某些原因未能复制可执行文件(卡带无效或在启动时未找到可执行文件),则无法启动游戏。用户将不得不重置游戏机才能玩游戏。

交互界面

无论是否插卡,系统都将加载交互界面完成引导。这只是一个驻留在外部256 KB闪存存储器上的程序[9]

主界面。
每次NDS开机时都会看到此界面。在有效插入一张卡时“Nintendo” logo会显示出来。
设置界面。

存储界面的芯片还存储着固件、一些用户设置(语言、昵称、生日、闹钟和欢迎消息)以及一些系统设置(触摸屏校准、首次启动标志、固件版本和Wi-Fi设置)。

这个界面与其他同期的游戏机界面基本上差不多。用户依靠它来启动游戏、更改设置、下载游戏(使用“Download Play”功能)或者玩玩Pictochat:一个开放的聊天室软件,可以与附近的NDS交流。

值得强调的是,只读数据和可写数据都驻留在同一个可重写的芯片中,因此理论上固件可以被覆盖!幸运的是(或者出于显而易见的原因),任天堂在主板上的一个点(称为SL1)上放置了跳线来保护芯片的上四分之一(64 KB)不被写入,拆卸电池仓后可以看到这个点。然而,仍然可以覆盖闪存的其余部分,虽然结果也是灾难性的[10]

可更新性

任天堂为了修复一些安全漏洞,曾多次更新这个固件(确切地说是5次)。用户无法自行安装这些更新(回想一下SL1的保护)。但是,任天堂在下一批生产的产品中嵌入了更新后的固件。

游戏

这里能说的可就多了,首要原因是这个游戏机确实启发开发者和艺术家们想出了非常创新的设计。让我们来看看……

存储介质

这个游戏机可以从三个来源运行游戏,其中只有两个可以充分地利用硬件:

一款零售游戏的例子。
  • NDS或者说“Slot-1”卡带:用于加载原生NDS游戏的主要介质,这是用于发行NDS游戏的唯一介质。
  • GBA或者说“Slot-2”卡带:该插槽使得掌机能够原生游玩GBA游戏。Slot-1游戏也可以访问此插槽,因此可以插入扩展卡带以增强NDS游戏。这提供了诸多功能如更多RAM、更丰富的输入控制或反馈设备(例如振动包)。
  • Download Play或“无线多重启动(Wireless MultiBoot)”:原多重启动(MultiBoot)功能的升级版,使另一个带有NDS游戏的掌机能够使用无线通信上传程序。下载的内容存储在WRAM中,传输完成后启动。由于WRAM是易失性存储器,数据在关机时将丢失。
    • 获得授权的零售商可以使用此功能部署下载站,邀请用户在到店参观时下载演示版游戏。

程序的结构

你已经见过BIOS需要分别为ARM9和ARM7编写不同的代码,在游戏中基本也是同样的情况。NDS卡的结构如下:

  • Header(4 KB):包含元数据(各个可执行文件的位置、序列号等)。
  • Secure Area(16 KB):出于复制保护的目的而设置。我们将在本文的最后一部分详细介绍相关内容。
  • Main content(大小可变):卡带的其余部分,仅包含可执行文件和游戏数据(图形、声音等)。使用任天堂的SDK制作的零售游戏使用内置的文件系统将数据层次化地组织为文件和目录。

开发生态

对于有意为该掌机开发游戏的游戏工作室,任天堂分发了硬件套件和SDK,其中包括许多实用工具。

硬件

被称为IS-NITRO-EMULATOR的开发套件由一个中等大小的蓝色盒子组成,包含DS的大部分内部硬件和I/O接口[11]。套件通过一根粗电缆连接到一个充当“控制器”和显示器的伪NDS外壳。根据需求,开发工具包还可以增加可选功能,例如音频/视频输出、Wi-Fi(默认情况下使用以太网模拟)和调试功能。我原以为后者已经包含在内了,但我意识到这些设备也可以被测试团队使用。

该套件可以读取DS卡带,但是需要使用较大的卡片和可更换的备份芯片。这些卡片使用被称为IS-NITRO-WRITER的另一个单元刷写。

软件

软件套件包括用于与开发工具包交互的实用程序、C/C++工具链和硬件API。需要提到的一点是,文档明确禁止绕过提供的API直接访问硬件:尽管游戏将在裸机上运行,但如果它执行“违禁”操作,任天堂将不会批准其分发。违禁操作包括直接控制ARM7,超出显示分辨率或者关闭LCD屏幕。

这些措施情有可原,例如为了保证质量水平。尽管在我看来,把ARM7拘束在I/O任务里纯属浪费……

交互的自由

脑锻炼(2005) 新品类的游戏吸引了青少年之外的受众。

随着新型交互形式的出现,工作室就有机会优先考虑游戏体验而不是图形显示。

在电子消费品中,一款把触摸屏、麦克风、Wi-Fi和一个实时时钟封装到一起的掌机第一次出现。一些游戏甚至提出了新的互动形式,比如指示用户侧握掌机。

网络服务

以前的竞争对手获得成功之后,任天堂也加入了在线多人游戏俱乐部,并部署了他们的集中式基础设施。使用“Internet Play”的游戏可以连接到任天堂的服务器(称为Nintendo Wi-Fi Connection)在线游玩游戏。

反盗版和自制游戏

即使DS卡没有受到光盘诅咒的影响,任天堂还是实施了一些保护措施以保持对游戏发行的控制。

安全机制

让我们看看各个领域的安全机制:

加密系统

NDS主要使用对称加密系统对内存接口和Slot-1卡之间的通信进行加密。在讨论如何加密之前,我们需要先聊一聊所使用的算法以及如何生成用于加密的密钥。

卡片的“Header”区域包含一个名为Gamecode的值(游戏的唯一标识符),内存接口抓取这个块来生成KEY1并使用它来加密进一步发送到卡片的命令。 KEY1加密基于Blowfish算法

之后,KEY1与内部时钟和卡带Header的一些其他值混合,生成一个新的密钥,称为KEY2。 KEY2与KEY1的根本区别在于,前者使用随机值所以无法被预测。 KEY2加密使用多个异或与移位操作来混淆数据。

DS卡的校验

正如我们上面看到的那样,BIOS包括一些例程,在启动时校验NDS游戏卡。工作原理如下:

  1. 掌机检索卡带的芯片ID,将其保存在RAM上,然后启用KEY1加密。
  2. “安全区”的前2KB也被复制到RAM上。该块的前8B存储了一个名为Secure Area ID的字符串,后面的数据包含校验和(CRC16类型)与一些其他元数据。
  3. 虽然“Secure Area ID”已经用KEY1加密过了,但是还要用KEY2再次加密,之后用到时再解密。如果解密后的值与字符串encryObj相匹配,说明卡片通过了校验第一次关。此后,该字符串被销毁,防止算法泄露。如果验证失败,Secure Area的2KB就会被无意义数据填满,阻止读取卡片的其他部分。
  4. 第二个测试流程为再次检索芯片ID,然后用KEY2随机加密若干次(使用内部时钟做种)。如果芯片ID的值与存储的第一个芯片ID相匹配,则第二次测试通过
  5. 最后,Secure area的其余部分以随机顺序被取走并在RAM中重新构建。之后就该执行固件了。

如果一切顺利,固件将在RAM中找到该卡所需的可执行文件,从而成功启动游戏。否则,游戏选择框将显示为灰色。

Download Play 保护

通过Download Play收到的程序必须由任天堂使用RSA签名(只有任天堂知道私钥)进行签名。

该检查由固件执行。

击败

如果你是自制软件使用者,那么你可能会遇到可以运行该软件的选项。事实是,在发现目前的破解方法之前,黑客们很难绕过任天堂复杂的反盗版系统。

Existing Slot-2

由于GBA子系统仍然在没有实施任何保护的情况下执行卡带(除了trademark tricks),现有的GBA闪存卡仍然与NDS兼容。这使得运行GBA自制软件成为可能,如果你不介意错过DS游戏独有的所有新功能,那就可以正常运行。

一如既往,烧录卡带也能运行盗版ROM,但由于任天堂不能改变GBA的保护系统(因为它有可能使现有游戏无法使用),所以只得处理这个问题。

Enhanced Slot-2

秘密地深入研究DS BIOS和固件后,最终发现可以把NDS卡的执行重定向到GBA插槽。尽管NDS卡还没有被破解,但这种方法允许暂时绕过Slot-1卡的安全系统,并在DS模式下执行Slot-2程序

因此,市面上出现了一种新一代的Slot-2烧录卡。它们内嵌了ARM9代码,一旦从Slot-1启动就会执行。引导本身是使用发现的上述方法之一(被称为“passthrough方法”)实现的:

  • 使用PassMe卡:该方法需要一个真正的Slot-1卡。 PassMe是一种处于Slot-1和真正的游戏卡之间的适配器。它基本上篡改了从卡片发送到游戏机的header,以欺骗控制台执行Slot-2代码,并在此过程中加载Slot-2卡带。
  • 使用WifiMe:该方法需要一台带有兼容Wi-Fi卡的PC。修改驱动程序,然后可以广播自定义程序让NDS通过Download Play下载。[12] 一旦启动,它就会重定向执行到Slot-2。这种方法利用了固件不会检查二进制文件特定区域的RSA签名这件事。
  • 使用 FlashMe:一劳永逸的方案。通过连接先前的方法并桥接SL1终端,可以运行一个自制程序,该程序可以在闪存卡存在的情况下覆盖固件以从Slot-2引导。
  • 这个破解还删除了从Download Play引导自制程序时的数字签名检查。

如预期的那样,任天堂推出了包含新固件的新版NDS,修补了这些漏洞。因此,黑客更多地侧重于寻找BIOS的漏洞(很难修补)。

Native Slot-1

著名NDS模拟器“NO$GBA”的开发者Martin Korth随后成功提取了BIOS并逆向工程了Slot-1的安全机制。这样一来,就可以用更新的工具和文档揭示NDS真实的安全机制。正如你在本文中看到的那样,加密系统只要没有被破解就可以运行。这是对称加密系统的一个局限,非对称加密系统(例如RSA,永远不会存储私钥)就不会。

无论如何,这导致市场涌入了大量的即插即用Slot-1烧录卡。这些卡可以在任何类型的掌机上原生运行NDS程序。随着“NoPass”卡的推出,passthrough方法也得到了改进。这些烧录卡可以在没有真正的游戏的情况下加载Slot-2烧录卡。

由于加密系统无法更改除法对现有的所有零售游戏造成影响,任天堂最终输掉了这场战斗。现在,他们唯一剩下的选择就是走法律途径,就像为前代游戏机所做的一样。

在我看来,与以前的游戏机的自制方法相比,烧录卡确实简单得引人注目。在之前的文章中我曾经描述过,用户必须深入迷宫般繁琐的步骤才能运行自制或盗版游戏。

就NDS而言,烧录卡就像零售游戏一样销售。我觉得游戏工作室看到用户轻松就能诉诸盗版,情况确实堪忧。

另一件事是,市场上出现的闪存卡品牌的数量也令人惊讶(不计所有仿冒品)。从技术角度看,烧录卡只不过是SD卡适配器[13]。每张卡与其他卡的唯一区别是引导代码和SD读卡器。一些制造商还投入更多的精力设计更好的文件浏览器(称为内核/固件),还加入了一些额外的硬件。

结语

到这里就结束了!

我目前用于本研究的NDS。 实际上我的第一台很久以前就被我卖掉了......好奇它现在在哪。

好嘞!我觉得我已经谈到了我想说的内容的99%……

我希望我对某些部分的批评听起来不会太苛刻。不要误会我的意思,我仍然认为这款游戏机是优秀的工程!只不过有一些缺陷让我怀疑它是否真的是一种对性价比的妥协,还是设计缺陷的一部分。说实话,这并不妨碍当时11岁的我想要一台NDS。所以我觉得这对公司来说已经算是“任务完成”了!

还要感谢我的朋友们和MelonDS社区的大家抽出时间检查我的草稿并提出大量更正。 NDS给我一个机会让我写下之前我就感兴趣的许多话题。虽然担心我一口吃了个胖子,但希望你能够喜欢这篇文章。

下篇文章见! Rodrigo

参考文献

[1]
R. Copetti, “Nintendo DS architecture - a practical analysis.” 2020. Available: https://www.copetti.org/writings/consoles/nintendo-ds/
[2]
A. Holdings, “ARM946E-s technical reference manual.” 2022. Available: https://developer.arm.com/documentation/ddi0201/d/introduction/about-the-arm946e-s-processor
[3]
C. Double, “ARM9-ARM7 FIFO.” 2005. Available: http://double.nz/nintendo_ds/nds_develop7.html
[4]
M. Korth, “Gameboy advance / nintendo DS.” 2014. Available: https://problemkaputt.de/gbatek.htm
[5]
D. Jaggar, “ARM microprocessor rise and fall.” 2020. Available: https://www.youtube.com/watch?v=%5c_6sh097Dk5k
[6]
Arisotura, “The DS GPU and its fun quirks.” 2018. Available: http://melonds.kuribo64.net/comments.php?id=56
[7]
osdl.sourceforge.net, “A guide to homebrew development for the nintendo DS.” 2008. Available: http://osdl.sourceforge.net/main/documentation/misc/nintendo-DS/homebrew-guide/HomebrewForDS.html
[8]
psiloveomega, “Booting the nintendo DS – a technical summary.” 2010. Available: https://corgids.wordpress.com/2017/07/28/booting-the-nintendo-ds-a-technical-summary/
[9]
[11]
nsmbhd.net, “IS NITRO EMULATOR listing.” 2018. Available: https://nsmbhd.net/thread/4438-nintendo-ds-dev-hardware-is-nitro-emulator-and-co/#61422
[12]
[13]
Acekard, “Acekard RPG 4.06 source code.” 2007. Available: https://gbatemp.net/threads/acekard-rpg-os-menu-4-06-and-sourcs-code-out.69656/

在x86笔记本上编译安装YouCompleteMe插件

作者 Cerallin
2023年2月27日 20:09

最近入手了一台x86架构的小笔记本电脑,CPU是1核2线程。想作为临时维护机,平时连接其他计算机,或者临时开发。于是安装了AntiX系统(为数不多还在维护x86软件仓库的发行版),并且准备使用Vim作为编辑器(VSCode开销太大装不上)。

Vim的包管理

使用vim-plug管理Vim插件,使用方法与Vundle类似。

1
2
3
4
5
6
7
8
9
10
call plug#begin()

Plug 'vim-airline/vim-airline'
Plug 'scrooloose/nerdtree'

Plug 'ycm-core/YouCompleteMe', { 'on': 'NERDTreeToggle' }

" ...

call plug#end()

安装YouCompleteMe

使用:PlugInstall安装的只不过是YouCompleteMe的外壳。为了实现C语言的自动不全,还需要合适的后端:clangd v15+。

clangd是clang的language server,在clang-13之后官方不再提供x86二进制包。而YouCompleteMe所需的clangd最低版本是v15.0.1(目前的lts是v15.0.7)。无论如何,都需要自行编译clang了。

编译clang工具链

下载llvm-project

下载

1
wget https://github.com/llvm/llvm-project/releases/download/llvmorg-15.0.7/llvm-project-15.0.7.src.tar.xz

然后解压

1
tar xf llvm-project-15.0.7.src.tar.xz

CMake选项

进入源代码文件夹,使用cmake生成编译选项。

1
2
3
4
5
6
7
8
cd llvm-project-15.0.7.src
cmake -S llvm \
-B build \
-G "Unix Makefiles" \
-DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra" \
-DMAKE_BUILD_TYPE=X86 \
-DLLVM_TARGETS_TO_BUILD=X86 \
-DMAKE_INSTALL_PREFIX=$HOME/.llvm

上面的cmake指令可以编译llvm、clang和clangd(包含在clang-tools-extra里),最后安装到$HOME/.llvm目录。为了编译速度,我舍弃了X86以外的所有架构,因为1核2线程实在是难以忍受地慢,而且除X86以外的交叉编译显然在其他计算机上运行。

注意 编译clang时内存使用量最高可达2.5G。一开始我大意了,以为笔记本2G足矣,重启一次之后不得不又开了2G swap。

编译 && 测试 && 安装

当前目录是llvm-project-15.0.7.src

1
2
3
4
cd build
make -j2
make check # 可选
make install
图1: 编译运行结果图。

图1所示,编译运行测试的时间就要将近5个小时,而编译则需要1天零13小时。实际上make check运行报错了,有51个test case没有通过。考虑到clang早已放弃对x86的支持,我猜测没通过的test case里有一部分是x86平台不适用的代码。

编译安装YouCompleteMe

上文提到,llvm被安装到$HOME/.llvm目录,编译YouCompleteMe的时候需要指定llvm路径,需要把下面的指令里的$HOME/.llvm修改为你的llvm的安装路径。

1
2
3
cd ~/.vim/plugged/YouCompleteMe
EXTRA_CMAKE_ARGS="-DPATH_TO_LLVM_ROOT=$HOME/.llvm" \
python3 ./install.py --clangd-completer --verbose

--verbose用于显示CMake的编译进度,可以不加。该命令执行完成会报错No pre built Clang 15.0.1 binaries for this system.,无视。

配置YouCompleteMe

为了让YouCompleteMe识别到clangd的安装位置,需要在.vimrc里加上一行:

1
let g:ycm_clangd_binary_path = "/path/to/clangd"

其中/path/to/clangd替换为clangd的路径。

Fin.

编译安装Grit

作者 Cerallin
2023年1月29日 14:44

Grit是一款C语言编写的GBA/NDS图片格式转换软件,由Jasper Vijn (Cearn)开发。 Grit可以接受JPG、PNG等许多格式(只要受FreeImage支持),输出可以是C/asm数组也可以是原始二进制文件。总而言之就是非常强大,GBA/NDS homebrew必备。

环境

软件版本表
软件 版本
GCC 9.4.0
FreeImage 3.18.0

下载

作者的个人网站上提供了Windows版的可执行文件。然鹅,我需要Linux版的(虽然是WSL)。幸好,作者慷慨地将源码也公布出来了,遵循MIT协议。

编译

初次尝试

源码中可以看到有Makefile,试着make一下?报错×3。

1
2
cldib/cldib_conv.cpp:182:10: error: cannot convert ‘bool’ to ‘CLDIB*’ in return
182 | return false;

怎么会有人在返回结构体指针的函数里返回false……无奈把所有的return false改成return NULL

安装依赖

再次make,提示无法链接libfreeimage,安装即可。

1
sudo apt install libfreeimage-dev

但是,还是无法链接freeimage库。

1
2
3
4
g++  -s -static -o grit build/grit_main.o build/cli.o build/fi.o  -L. -lgrit -lcldib -lfreeimage
/usr/bin/ld: cannot find -lfreeimage
collect2: error: ld returned 1 exit status
make: *** [Makefile:127: grit] Error 1

修复Makefile

这是为什么呢?这一切都怪-static

作者坚持仅在Linux平台上加上这个flag,意为链接静态链接库而不是动态库。正如Windows版二进制包里附带了freeimage.dll,或许他想要发行完全“portable”的软件。但是,为了编译,freeimage的头文件我都下了,不在乎便携性了。更何况apt安装的freeimage库只有动态链接库,想要静态链接自然是找不到。

于是在Makefile:40:19处把-static删掉再make,编译成功。

但是warning。

锦上添花(可选)

1
2
/usr/bin/ld: ./libgrit.a(grit_xp.o): in function `grit_xp_c(GritRec*)':
grit_xp.cpp:(.text+0x745): warning: the use of `tmpnam' is dangerous, better use `mkstemp'

全局搜索看到,作者留下了这样一段话:

Also, I'm using tmpnam(), which apparently isn't recommended due to safety, but I don't know a suitable, portable alternative.

Google一下"Windows mkstemp"得知,Windows确实没有这个函数的实现。但反正现在我只想编译Linux版的二进制文件,直接全部替换掉。然后把编译完名为grit的文件拖进$PATH的某个路径里。

All done.

有关RetroArch的一点踩坑笔记

作者 Cerallin
2023年1月5日 14:03

RetroArch是一个跨平台的前端软件,为游戏机模拟器、游戏引擎和多媒体播放器提供统一的接口。自从RetroArch登录了Steam,就可以统计古早游戏的游戏时长了,颇有成就感。但是,Steam版的RetroArch与原版有些出入,导致有些问题很难通过网络搜索解决。更何况原版RetroArch配置模拟器后端就有很多坑,Steam版RetroArch的配置难度只增不减。本文记录了一些Steam版RetroArch的配置流程,绝大多数情况都与原版RetroArch的配置流程相同,还有一部分是Steam版特有的步骤。

各种文档

RetroArch官方网站

RetroArch build bot

Libretro文档

准备游戏

下载游戏。

绝大多数游戏文件属于“ROM image”,简称ROM,是只读映像的意思。不同平台的游戏文件有不同的文件后缀,例如Game Boy的游戏可以以gba或者gbc结尾,NDS的游戏以nds结尾。而PS的游戏,因为是以光盘为载体,因此一般以imgiso结尾。

为了成功读取光盘,需要配套的标记文件,标注了光盘映像的各个区域内容的起止,通常以cue结尾。对于我们的游戏而言,CUE文件结构非常简单,因为只需要标注游戏内容的起止范围即可,也就是全部范围。 CUE文件内容如下:

1
2
3
FILE "/path/to/game.img" BINARY
TRACK 01 MODE1/2352
INDEX 01 00:00:00

有的大型游戏因为一个光盘装不下,会分成两三个盘。虽然读取第一个光盘即可开始游戏,但是RetroArch并没有实现游戏中间换盘的机制。因此需要在启动游戏之前告诉模拟器一共有几个游戏光盘。为此,我们还需要准备一个m3u文件,格式如下。

1
2
/path/to/game (A).cue
/path/to/game (B).cue

准备模拟器

下载模拟器核心

RetroArch是一个游戏机模拟器前端,需要对应的后端才能运行相应的游戏。 Steam版RetroArch从Steam上以DLC的形式可以下载一部分支持的模拟器后端,但并不是全部,比如PS2的模拟器PCSX2就没有在Steam上架。此时我们需要手动下载,最好的去处就是官方的build bot网站。例如,当前RetroArch最新的稳定版版本号为1.14.0,64位Windows系统可以从这个页面点击下载对应的RetroArch_cores.7z。

当然,从Steam下载最大的好处就是可以自动更新模拟器核心,否则需要手动更新(经典模拟器的核心不需要更新这么频繁就是了)。

下载对应的系统文件

虽然模拟器不违法,但是厂商永远有办法给玩家添堵,即使是几十年前就已经停产了的怀旧机型。无一例外,所有模拟器都依赖于正版游戏机的一部分软件才能完整运行(通常为BIOS)。出于法律方面的考虑,模拟器开发者不能直接将这部分内容直接提供给玩家,因此需要我们自己去互联网种发掘。模拟器需要什么文件可以查看相关文档确认,例如,PCSX2核心推荐使用SCPH-10000型号的BIOS

最终设置

虽然现在像素风已经成为一种独特的美术风格,但是老游戏运行起来的画面的样子其实并不如设计师所想,也与当年的玩家所见不同。像素糊,谁说当年的游戏就没有在追求立体感和真实感呢?为了最大地还原游戏想要呈现的显示效果,我们需要合适的shader(着色器)。例如,为还原psp、nds等隔行扫描的屏幕,可以采用crt-easymode的着色器,每两行抽调一行,得到更好的显示效果。

当然,正如用钢琴演奏巴洛克时期的键琴作品并不显著地阻碍人们对艺术的欣赏,对游戏画面什么都不做在一部分人看来也许别有一番风味。

萝卜白菜各有所爱,还需自行定夺。

P.S. Steam版RetroArch不能识别核心的问题。

如果发现RetroArch中某个核心不能识别,而显示为xxx.dll的情况,则需要额外的步骤来修复。这是因为RetroArch没有关于该核心的信息造成的。从build bot的前端资源里下载info.zip,解压到RetroArch的info文件夹里,就可以了。

Fin.

配置Mutt二三事

作者 Cerallin
2022年11月11日 20:00

与无数国人一样,我也是从王垠的《Mutt email 程序使用入门》入门的。很荣幸,当初我直接找到了这篇博文的原始地址,而不是满天飞的CSDN转载。我想要使用Mutt的初衷,不仅仅是觉得tui应用很酷,而且也有想要在自动化shell脚本里加入发送邮件的功能。后来,我把Mutt安装到每一台我所使用的服务器上,Mutt配置也在各台计算机之间传来传去,就像其他费时费力的配置脚本一样,例如.vimrc(笑)。再后来,我接触到NeoMutt。与Mutt一样,NeoMutt也是使用纯C语言实现的项目,并且使用与Mutt相同的图标:一只像素风格的小狗。“Teaching an Old Dog New Tricks.”,NoeMutt的Github主页信息栏里,似乎蕴含着Mutt的历史与未来。

一个完整的邮件客户端(Mail user agent, MUA)至少应该能够做两件事情: 1. 查看邮件 2. 发送邮件。接下来也将分成这两大部分介绍Mutt的使用方法。

查看邮件

协议

从服务器上获取邮件有两种协议,POP3和IMAP。 粗略来理解,可以认为基于IMAP协议会把本地的操作(已读、删除)同步到云端;而POP3则不会。作为颇有类Unix组件化哲学的Mutt,在查看邮件方面也有广泛对接的程序,例如fetchmail,一个可以定期将邮件下载回本地的后台程序。当然,Mutt也保证自己是开箱即用的,不使用其他软件下载邮件也可以,Mutt支持直接查看服务器上邮件。不过,直接查看邮件有一个问题,IMAP/POP3协议支持查询邮件目录而不下载,如果在Mutt里配置显示邮件行数的话,那一列就全都是0了,除非打开一封邮件看看。

显示

控制台的资源总是很紧张,每一个字符都会占用一个显示位,不多也不少。因此合理安排显示的内容、每列的宽度与间隔就显得很重要。顺便一提Mutt对CJK字符宽度的计算是正确的,不会像某些愚蠢的软件一样出现对不齐的问题(说的就是你,snap,以及到底是谁给服务器安装中文版的Ubuntu的……)。

邮件渲染

现代大多数的邮件除了会发一份HTML格式的文件之外,也会照顾到老旧的MUA(Mutt:正是在下)而附带一份纯文本的“fallback”。但是,不是所有的邮件都是如此,至少Mutt写出这样一份有好几种预备方案的健壮的邮件是很困难的(Mutt何苦为难Mutt)。所以,当接收到仅有一份HTML内容的邮件时,用默认配置的Mutt就只能阅读HTML源码了。

如何解决这个问题呢?

正巧,之前我了解过一个著名的cli浏览器w3m,它可以实现一些浏览器的基本功能。那么,Mutt是否可以使用w3m渲染HTML邮件呢?

答案是肯定的。

省流总结如下:

  1. 创建一个~/.mutt/mailcap文件,
  2. 在上述文件里写入一行text/html; w3m -I %{charset} -T text/html; copiousoutput;
  3. 把原本的~/.muttrc文件移动到~/.mutt/muttrc

注意 顺便一提,w3m无法安装在WSL1里。

发送邮件

编辑器

写邮件或者回信的时候,Mutt允许你自定义编辑器,通过指定EDITOR的值。 EDITOR其实是常见的变量,例如crontab也需要这个值作为默认编辑器(没有则默认vi)。在我的shell初始化脚本里,EDITOR值为vim。当然,你可以指定其为emacsnano、甚至VSCode(code)。

命令行调用

在shell里调用mutt/neomutt非常简单,令它接收管道的输出作为自己的输入就好了。一行代码就是,echo "something" | mutt -s "Subject" you@addr.com

有的时候,还可以添加一些类似于.muttrc文件里的选项的设置:

1
echo "mail content" | mutt  -e "set content_type = \"text/html\"" -s "Subject" you@addr.com

注意-e参数一定要在最前面。

与cat、sed一样,Mutt是从管道里接收数据的软件,因此可以写类似以下形式的代码:

1
2
3
4
5
6
7
8
9
10
11
mutt -s "Are ya ready kids?" spongebob@pineapple.house <<MAIL
Who lives in a pineapple under the sea?
Spongebob squarepants!
Absorbant and yellow and porous is he?
Spongebob squarepants!
If nautical nonsense is something you wish?
Spongebob squarepants!
Then drop on the deck and flop like a fish!
Spongebob squarepants!
Ready?
MAIL

最后是一个小细节。 Mutt的指令是mutt,NeoMutt的指令是neomutt。其实就此与mutt诀别也没什么,但是我还是舍不得破坏~/.zsh_history里的指令统一性。(并没有这种东西)

一个简单的处理方法是在~/.bashrc或者~/.zshrc里添加一行alias mutt='neomutt'

MSMTP

MSMTP是一个SMTP客户端,配合mutt使用,可以实现更丰富的发信功能,比如,本地的日志记录,设置SOCKS代理等。 Ubuntu上大多数软件的配置流程都是轻松的,因为基本是开箱即用的。但那都是服务级应用,MSMTP这种用户级应用还是要踩不少坑的。

就比如说,我喜欢把所有的用户级日志放在~/logs目录下。而MSMTP,就经常报错:msmtp: cannot log to /home/julia/logs/msmtp.log: cannot open: No such file or directory。创建日志文件并将文件权限设置成777并不能解决这个问题。更神秘的是,这个bug时有时无,从Ubuntu 18.04到Ubuntu 22.04,不论是物理机还是WSL兼容层,像一个u0,飘荡在Linux系统里。

这是为什么呢?

没想到,这样的小问题,居然困扰了我4年。从刚假设邮件服务器刚开始使用Mutt,一直到给邮箱服务器上SSL并使用NeoMutt作为客户端的刚刚。 或许,都怪我懒得修吧。

查阅资料可知,查看/etc/apparmor.d/usr.bin.msmtp可以看到msmtp被设定为对特定pattern的文件有写入权限:

1
2
3
4
5
6
owner @{HOME}/.msmtp*.log wk,
/var/log/msmtp wk,

...

owner @{HOME}/.cache/msmtp/*.log wk,

可以看到,我设置的日志文件/home/julia/logs/msmtp.log不满足任一pattern。但是我懒得改这个文件,所以我选择把日志文件设置为/home/julia/.msmtp-$(date +%Y-%m).log。问题顺利得到解决。

除了突然冒出来的apparmor。

apparmor是什么?

以前我从未听说。

Ubuntu居然默认安装着这种东西,以前完全没有注意到。

它甚至不是一个开机启动的守护进程,而仅仅是一系列传统Unix自主访问控制(discretionary access control, DAC)策略。

只有在追查这个bug的时候,我才发现了它,静悄悄地影响着几乎每一个后台运行的服务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{21:35}~ ➭ aa-status
apparmor module is loaded.
You do not have enough privilege to read the profile set.
{21:36}~ ➭ sudo aa-status
[sudo] password for julia:
apparmor module is loaded.
34 profiles are loaded.
34 profiles are in enforce mode.

...

0 profiles are in complain mode.
6 processes have profiles defined.
6 processes are in enforce mode.

...

怪恐怖的。

Fin.

MongoDB语法速查

作者 Cerallin
2022年11月1日 10:28

近几天接手维护的项目采用MongDB作为数据库。速度学习了一下用法,记录一下。

数据库操作

进入/创建数据库

1
use myDatabase;

删除数据库

1
db.dropDatabase('myDatabase');

集合操作

创建集合

1
db.createCollection('myCollection');

删除集合

1
db.myCollection.drop();

清空集合

1
2
db.myCollection.remove({}); // outdated
db.myCollection.deleteMany({});

增删改查文档

查询文档

1
2
3
4
5
6
7
8
9
10
11
// 查找 id = 1 的一个数据
db.myCollection.findOne({id: 1});

// 查找 id = 1 的一个数据的name字段
db.myCollection.findOne({id: 1}).name;

// 查找 id > 3 的所有数据
db.myCollection.find({id: {$gt: 3}});

// 查找 id > 10 并且 name == 'John'的所有数据
db.myCollection.find({id: {$gt: 10}, name: 'John'});

插入文档

1
2
3
4
5
6
// 插入一个数据
db.myCollection.insertOne({id: 1, name: 'Julia'});

// 插入多个数据
// 注意,id 可以重复,但是后端程序不一定这样认为,就会报错
db.myCollection.insertOne({id: 1, name: 'Ruby'}, {id: 1, name: 'Sapphire'});

更新文档

1
2
3
4
5
6
7
8
9
// 将 id > 5 的文档的 name 字段都设为 'Julia'
db.myCollection.update(
{
id: { $lt: 5 }
},
{
$set: { name: 'Julia' }
}
);

删除文档

1
2
3
4
5
// 删除一个 id = 1 的文档
db.myCollection.deleteOne({id: 1});

// 删除所有 id > 10 的文档
db.myCollection.deleteMany({id: {$gt: 10}});

没了

在CentOS 7上安装GCC 10

作者 Cerallin
2022年9月22日 10:05

在一台没有管理员权限的CentOS 7上,我想要安装的某个软件需要用到CMake,但是没有安装。编译CMake的时候报错不支持c++11标准。 gcc -v一看,果然,版本4.8。接下来就是编译安装最新版GCC(10.4)了。

操作系统

此次的服务器是CentOS 7。

1
2
$ uname -r
3.10.0-862.el7.x86_64

安装依赖

GCC有三个必选的依赖:mpcmpfrgmp。可以联网的服务器可以使用gcc自带的脚本./contrib/download_prerequisites下载解压,但是这个服务器无法联网,所以先下载到我自己的电脑上再上传。

阅读脚本可知,所需三个依赖的版本和URL分别为:

1
2
3
http://gcc.gnu.org/pub/gcc/infrastructure/gmp-6.1.0.tar.bz2
http://gcc.gnu.org/pub/gcc/infrastructure/mpfr-3.1.6.tar.bz2
http://gcc.gnu.org/pub/gcc/infrastructure/mpc-1.0.3.tar.gz

上传脚本到服务器的GCC源码目录,注释掉脚本里下载压缩包的部分(如下所示),然后运行指令./contrib/download_prerequisites --no-isl --no-verify。之后就可以编译了。

1
2
3
4
5
6
7
8
9
# 第218行
for ar in $(echo_archives)
do
if [ ${force} -gt 0 ]; then rm -f "${directory}/${ar}"; fi
[ -e "${directory}/${ar}" ] \
|| ${fetch} --no-verbose -O "${directory}/${ar}" "${base_url}${ar}" \
|| die "Cannot download ${ar} from ${base_url}"
done
unset ar

编译GCC

GCC要编译三次,真是漫长的等待。

1
2
3
4
5
6
7
cd gcc-10.4.0/
mkdir build && cd build

../configure \
--disable-multilib \
--prefix=$HOME/.gcc/10.4.0 \
--enable-languages=c,c++,fortran

安装目录指定为$HOME/.gcc/10.4.0disable-multilib意为只编译64位系统相关文件,跳过32位系统相关的编译任务。 enable-languages指定只编译c、c++、fortran三种语言的编译器,其实gcc还可以支持这些语言:c, ada, c++, go, brig, d, fortran, objc, obj-c++, gm2。

安装GCC

1
2
3
make install

ln -s $HOME/.gcc/10.2.0 $HOME/.gcc/default

make install似乎还有一些编译任务要执行,可以指定多线程数加速。下面一行创建软连接是方便以后切换版本。之后需要在.bashrc里添加一些全局变量:

1
2
3
4
5
6
7
export PATH=$HOME/.gcc/default/bin:$PATH

export LD_LIBRARY_PATH=$HOME/.gcc/10.2.0/lib64:$LD_LIRBARY_PATH

export CC=$HOME/.gcc/default/bin/gcc
export CXX=$HOME/.gcc/default/bin/g++
export FC=$HOME/.gcc/default/bin/gfortran

CMake编译选项

当使用CMake编译软件的时候,可以使用如下代码指定编译器:

1
2
3
4
5
6
cmake \
-DCMAKE_C_COMPILER=$HOME/.gcc/default/bin/gcc \
-DCMAKE_CXX_COMPILER=$HOME/.gcc/default/bin/g++ \
-DCMAKE_Fortran_COMPILER=$HOME/.gcc/default/bin/gfortran \
-DCMAKE_PREFIX_PATH=$HOME/.gcc/default \
-B build -S .

碎碎念

昨晚要安装一个软件,需要先安装C++的YAML库。编译安装YAML需要CMake,而CMake编译失败了,因为需要gcc支持c++11标准。但是,gcc 4.8是好多年前的编译器了,早在我刚上高中的时候这个版本还都是最火的gcc版本(当然现在很过时了)。于是安装gcc。一开始没看文档,折腾了一晚上。今早照着别人的教程安装,被依赖版本毒打,因为我安装的版本都太新了,而且据(GCC Wiki)说,如果我编译GCC的时候是动态链接这三个库的,那么之后运行GCC的时候总要动态链接这三个库,也就是需要把他们加入编译器全局变量里。

这显然是不能接受的。我只是想临时地使用这三个库,不想以后我的所有程序都要链接他们。最后,对着GCC官方Wiki,终于安装成功了。

事实证明,即使是GCC 4.8,也是可以编译GCC 10.4的!

虽然后来在服务器上发现了GCC 11.2,但是事到如今装都装了……

Fin.

使用LaTeX与MuseScore排版乐谱

作者 Cerallin
2022年9月13日 15:17

最近心血来潮,趁着摸鱼时间和中秋假期把迄今为止我扒谱的所有曲目整理出来,挑选我最喜欢的几首,打算装订成册。本想如果MuseScore支持的话,打算全部复制粘贴到一个文件里,生成PDF然后打印。但查了查发现似乎没有这种操作,遂改为用LaTeX排版。

可行性分析

LaTeX与MuseScore的协同

初步的构想是MuseScore生成乐谱,LaTeX负责整合PDF、控制页脚页数、封面和目录。

在LaTeX中使用pdfpages包可以添加PDF文件,代码如下,其中指定的pagecommand选项可以添加LaTeX中定义的页眉页脚。如果能够隐藏MuseScore生成的PDF的页眉页脚,就不会与LaTeX冲突,因而可以自定义页眉与页脚。

1
2
\usepackage{pdfpages}
\includepdf[pages=-, pagecommand={}]{./path/to/example.pdf}

双面翻页

为了装订成册,最终LaTeX生成的PDF必须是双面翻页的书本格式,也就是说,纸张内侧(奇数页左和偶数页右)的空白要比外侧更大一些。

关键技术问题

章节与目录

为了在开头自动生成一个目录(TOC),我尝试使用\section控制章节,但发现随后插入的乐谱与章节页之间的空白十分浪费,所以希望可以在不显示章节的情况下也能生成TOC。搜索资料之后,设计出\fakesection命令如下,可以在正文中不显示章节名的情况下设置TOC中的章节名。其中\numberline{}取消了TOC中章节前面的数字。

1
2
3
4
5
\newcommand{\fakesection}[1]{
\par\refstepcounter{section} % 维护 section 计数器,虽然感觉没有必要……
\sectionmark{#1}
\addcontentsline{toc}{section}{\protect\numberline{}#1}
}

页眉与页脚

首先要取消MuseScore的页眉页脚。查阅MuseScore论坛可知,MuseScore可以导入导出乐谱样式,一种扩展名为mss的XML配置文件。部分选项如下:

1
2
3
4
<showHeader>0</showHeader>
<showPageNumber>1</showPageNumber>
<showPageNumberOne>0</showPageNumberOne>
<showFooter>1</showFooter>

显然0和1就是否和是的意思。使用如下所示代码可以批量修改乐谱样式。

1
mscore -S mystyle.mss example.mscz -o output.mscz

单双页与页数设置

为了节约空间,两份乐谱之间不应该有空白页。而MuseScore生成乐谱时第一页总是奇数页,应当根据上一份乐谱设置单/双页开始。

查阅资料可知,软件中有指定起始页的功能,不过一个一个鼠标去点实在是太麻烦了。有没有批量修改的方案呢?

答案是有的。

虽然MuseScore保存时默认的格式是mscz(压缩了的MuseScore文件),实际上还有另一个未压缩的,以XML格式存储信息的mscx格式。可以看到其中存储了样式和所有音符的信息。

而首页页数,通过<page-offset>标签控制(默认为0)。

解决方案

乐谱按时间顺序排序

18年以来,我的乐谱在好几个电脑之间传递,有些乐谱的确切的修改时间已经不可考了。还有些乐谱在漫长的时间里经历过几次勘误,修改时间相对于于初次定稿时间落后了太多。

好在文件里其实保存了乐谱的创建时间,接下来的排序就以此为基准。

mscx格式的MuseScore文件本质上是XML,元信息中保存的创建时间格式如下:

1
<metaTag name="creationDate">2022-08-09</metaTag>

首先扫描所有乐谱文件然后存储列表到list.txt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
getCreationDate() {
local file=$1

grep 'creationDate' $file | # 查找metaTag
sed 's_.*">__g;s_<.*>__g'
}

getCreationDateWithFormat() {
local file=$1

grep 'creationDate' $file | # 查找metaTag
sed 's_.*">__g;s_<.*>__g' |
xargs date +"%h %d, %Y" -d
}

exec 3>list.txt

ls -haltr *.mscx | awk '{print $9}'| while read file; do
name=${file%%.*} # 去除扩展名
pdf=$name.pdf
name=${name//_/ } # 下划线转空格
echo "$name|$(getCreationDateWithFormat $file)|$pdf|$(getCreationDate $file)" >&3
done

然后使用如下指令重新排序:

1
awk -F '|' '{print $NF,"|",$0}' list.txt | sort | awk -F '|' '{print $4}'

MuseScore批量导出PDF

MuseScore支持一次性执行批量任务。相比于shell批处理的好处是不需要反复创建销毁MuseScore的Qt窗体,节约开销,加快执行速度。

但是本次不可以使用批量任务,因为导出一份乐谱之前,需要知道它的第一页究竟是奇数页还是偶数页。

设置page-offset的函数如下,美中不足的一点是多次执行会造成一堆空格。

1
2
3
4
5
6
7
8
9
10
11
set_offset() {
sed -i 's_^<page-offset.*$__' "$1"
ed "$1" <<EOF
8a
<page-offset>$offset</page-offset>
.

wq
EOF
}

ed是一种命令行文件编辑器,sed是其升级版。ed部分代码的意思是在第8行后插入一行。

总结的碎碎念

折腾了一周多的时间(虽然一半时间用于修缮谱子),终于整理出一份80页A4纸打印的曲谱集,本来还想在每章开头写点什么,最终还是懒得写。但是现在章节页实在是太空了,电脑上看的时候还不觉得,打印出来之后才觉得难受……目录字体调得太大了,显得有些呆笨。乐谱标题太靠上,上边的空间过于紧张。外侧间距留小了,没有考虑到装订的时候会切一部分下去,失算了。但是如果外间距再小一点,好多谱子就会多出一行,浪费很多的纸面空间……目前还没想好怎么办。

总而言之,LaTeX模板已经上传至GitHub。

项目地址

Fin.

Serial Experiments Lain: 一系列模糊了虚拟和现实的实验(下)

作者 Cerallin
2022年4月13日 13:10

きおくにないことわ、なかったこと。

きおくなんて、ただのきろく。

きろくなんて、かきかえて、しまえばいい。

——れいん,Serial Experiments Lain

注意 本文涉及剧透和大量的个人理解。

玲音的存在性

玲音的身世

一直观看到最后几集的观众都会发现这样一个事实:作为学生的玲音的家庭完完全全是伪造出来的,是一层伪装。这层伪装从第一集开始就一直误导着玲音和观众,让我们以为玲音从一开始就是一个普通的人类。但实际上,玲音并不是人,至少一开始不是。如玲音自己所说,人的肉体与精神,与计算机与软件进程是对应关系。玲音一开始只存在于Wired世界,而她的肉体是被制造出来的容器,承载玲音的精神,或者说进程。有人认为,第一集中玲音在电车上听到的窃窃私语,以及上课时指尖发散的烟雾,都是她刚刚进入肉体不稳定的证明。

玲音的身份

图1: 玲音表记法[1](汉化&嵌字by Cerallin)。从左到右依次为偷窥狂、Wired世界的人格、内向的中学二年级学生、作为神明的玲音,以及PS版游戏的看板娘。

图1展示了玲音的5种人格,其中右一是游戏中的人格,未在动画中出现,在此不做讨论。实际上,我们不需要拘泥于严谨的心理学或是精神分析对玲音的人格和精神状态进行分析。作为艺术作品,Lain中玲音的各种人格具有一定的象征意义,不一定非要是人格分裂。接下来我将以身份而非人格称呼她们。

首先是左边的三个:玲音自己一开始也没有察觉到的偷窥狂人格、Wired世界的人格以及现实中的人格,对应的是弗洛伊德的三个我:本我、超我和自我。

剧中,玲音对于自己偷窥狂的身份一开始并不知情。随着剧情的发展,玲音得知自己真的是偷窥狂并且是谣言的制造源头的时候,想要亲手掐死这个“龌龊的自己”。值得注意的是,虽然她意识到了这个身份的自己的丑恶,但并不否认其与自身的统一性。

Wired世界的玲音是玲音的超我,她自信、强大、有自己的一套行事准则,对自己看不惯的事情嗤之以鼻,以阵营九宫格来说的话或许属于守序善良。

现实中的玲音则在二者之间寻求平衡。有时,玲音会在超我的驱动下行事,例如在橘综研被逼问和在Wired世界中冲浪时,超我人格占主导;有时,玲音也会满足自身最原始的欲望,例如删除修改了学校其他所有人的记忆,但唯独保留了挚友艾丽丝的记忆,这毫无疑问是出于私心。

不从精神分析的角度出发,这三个身份也有其互联网角度的含义。

首先是跟踪狂。第8集中英利说,“这些人都是你呀。你和我一样,遍布于网络世界。”同时,一排排的酷似玲音的木偶脑袋安装在衣着各式各样的人的身上,说明人类通过玲音来连接到互联网。有评论家[2]认为,这些玲音的复制品不应该被解释为单个碎片身份的不完整碎片,而是可以解释为她在噪音世界中接收到的扭曲图像。

如果要从通信工程领域中选取一个名词实现相同的功能,那么这个词应当是“协议”,是人类通过互联网传输数据的协议。既然每个人连接网络交换信息都需要玲音的参与,那么,不论玲音自身是否愿意偷窥,所有的信息都会以玲音为媒介传播。

在现实世界中,各个电子设备之间交换数据时,信息被途中多个交换设备处理和分发。在信息交换的角度,这些设备组成了一条信道;而如果想要强调信息传播的物理介质,也可以说是一条链路。在一个稍微复杂一点的网络拓扑中,任何设备既可以是发送信息的源头,也可以是交换信息的媒介。而跟踪狂的Lain,从哲学的角度解读,是全世界所有电子设备的一种抽象属性的集合,这个抽象的属性代表了电子设备作为信息交换媒介的能力。

然后是Wired世界中的玲音和神明的玲音。在上面有关玲音身世的部分已经提到,玲音是人造的产物,但玲音的诞生时间却早于其进入Wired世界的时间。玲音是全体人类(甚至地球上全部生物)的集体潜意识,被橘综研修改后变成了Wired网络的神明,只不过,她的神性是本征的,而不是人类赋予的。(当然,此处的神明并不是指宗教的神明,而是指互联网的神明。详细的区别将在后面的章节讨论。)从本剧一开始,观众们和玲音就频频发现有别人认识但自己不知道的玲音出现,并且总是在Cyberia酒吧里。实际上,Cyberia酒吧里的玲音是Knights制造出来的。 Knights利用玲音做一系列不为人知的实验,其中一定包括对人的记忆的改造和利用玲音监听的情况。

最后是现实世界的玲音。现实中的玲音本来内向、不善言辞。但是经常也会脱离给人内向的印象,比如对黑客技术展示出兴趣的时候,以及安装了Psyche模块之后突然变得开朗的时候。在英利看来,现实中的玲音不过是肉体,是承载灵魂,或者说程序的、脆弱不堪的容器,是可以轻易抛弃的。但实际上,从第13集玲音自问自答的思辨来看,肉体的玲音才是所有身份中权限最高的。从此可以看出作者对于赛博飞升的否定态度:记忆不仅仅是记录,肉体也不仅仅是容器。从互联网上汲取的整理好的简要的知识,终究比不上屏幕外的生活中的一次体验。

当今时代,听惯了数字音乐的各位,是否注意过插拔有线耳机时,与Lain中经常出现的声音一样的,模拟信号噪音呢?

互联网的神明

到动画后期,玲音与英利之间的对决升格为主要矛盾。二人斗争的焦点,就是所谓“神明的权限”,或者说,英利想要取代玲音,获得对Wired的完全掌握。那么,互联网的神明是什么呢?

首先需要明确什么是互联网。在Lain的世界中,Wired可以连接机器与机器、机器与人类,以及人类与人类。 Wired存在的最终目的,就是实现,或者说取代人与人之间的连接。一开始,人类以终端为媒介连接到Wired,协议7的出现使得人类可以直接连接Wired而不通过终端,也就实现了人类与人类的直接连接。机器与人类的连接是协议升级过程中的中间形态。

其次是神明的含义。英利指出,神明是无处不在的。前文提到,玲音的本我代表所有电子设备作为信息交换媒介时体现的抽象属性,这也是玲音强大能力的来源。所以每当电子设备交换一次信息,就相当于玲音证明了自己的存在,以及行使了神明的力量。

Lain的部分哲学议题

记忆与记录

きおくにないことわ、なかったこと。
きおくなんて、ただのきろく。
きろくなんて、かきかえて、しまえばいい。

随着协议7的部署,玲音也进化成了神明。 Wired与人类社会融为一体,不分彼此。玲音掌控着Wired世界的所有信息交换,进而掌握全人类存在的证明。玲音行使自己的能力,把全人类的记忆中有关自己的部分全部删除,唯独保留了艾丽丝的记忆。玲音本以为艾丽丝会因此而高兴,没想到艾丽丝央求她把自己的记忆也删除掉,不想只有自己保留着痛苦的回忆。

Lain的世界里,人的存在由存在记录证明。而人的存在记录,就保存在其他人的记忆里。

虚拟的真实

Wired与终端设备极大拓宽了人的视野,以及获取信息的能力。我们可以将之看作是五感之外的抽象的“第六感”。只不过,从终端上获取的知识永远具有抽象的属性,这是与用五感接受的信息最明显的不同。

在剧情的中段,玲音沉溺于Wired世界,站在天台上思考人生时,反而认为Wired世界比起现实世界更加真实。或许,玲音认为从Wired获取信息密度更大,并以此作为比较两个世界真实性的判据。实际上,Wired虽然拓宽了获取信息的广度,似乎突破了现实的某种极限,但也是一种新的牢笼。

所谓的牢笼有两方面。一种是Wired存在本身弱化了人类与世界相连接的其他途径,人本可以用五感感受世界,现在绝大多数时间里却仅仅通过Wired感受世界,人认识世界的途径变得单一了。另一种是人需要通过终端连接Wired,而不能直接连接Wired,终端成为了更加自由地连接Wired的阻碍。

对于上述第一点,动画中并没有进行更深的探讨;对于第二点,则给出了一种解决方案:升级Wired协议。第4集中玲音的父亲给出忠告,代表了剧中一方势力对Wired的态度:“Wired世界终究只是用来收发信息、相互交流的空间。切不可与现实世界相混淆。”但从始至终玲音都没有听取这个忠告。她始终认为,“两者的界限,并没有划分得那么明确。”

Lain中留白的问题

Lain中提出了许多问题,对于其中一些问题作者有着明示或者暗示出的立场。对于更多的情况,作者只是抛出一个问题,答案留由观众思考。 Lain的最后三集,并没有完全解决其他集数里提出的所有问题,但这不能说是一次失败的尝试。事实上,玲音的身份之谜以及她在Wired世界和现实世界中的力量本质几乎都被完全解释了。然而,更大的主题被故意留白:什么是“真实”?科技如何改变社会?科技如何影响人类的灵魂?[3]

科技对于人类的影响

沙林毒气事件使得日本社会对于科技的危害有了切骨的体会。不久之后互联网时代的到来,日本社会对于互联网始终保持质疑的担忧的态度。而Lain将互联网深入现实可能造成的后果表现得淋漓尽致。虽然最终没有给出明确的答案,但是从剧中各方势力的博弈与最结局体现了作者对于科技的态度。

橘综研代表了政府,要对Wired施加监控。 Knights是无政府主义黑客团体,利用Wired和玲音达成自己的目的。而个人势的英利,则想要利用Wired和玲音成神。最终,Knights集体被暗杀、英利的神性被证伪并被打败。作者借玲音表达了态度:互联网世界和现实世界终将交织在一起,但是不应该作恶。正如2000年初,谷歌公司将“不作恶”(Don't be evil)作为公司的座右铭并添加到了行为准则之中(虽然2015年谷歌因为删除该句而引起一阵风波)。

Cyberia是剧中的一处酒吧,是少年少女消遣的去处,代表着自由、时髦、新潮、先进。其名来源于Douglas Rushkoff所著同名小说,同时Cyberia也是1994年世界上第一家网吧的名字[4]。在本作中,Cyberia是充满着DJ音乐、迪斯科球灯光和舞池中狂欢的年轻人。新奇又开放,正如现实中互联网初生时网上冲浪可以见到的景象。然而,掩藏在流行文化背后的,是互联网野蛮生长缺少监管的潜在危害。少年少女们无所顾忌地进出酒吧却无人监管,隐喻未成年人可以接触到互联网上的驳杂信息却无人监管的情况。

互联网时代的身份拓扑

正如图1所表示的,玲音具有多种身份。互联网时代人的身份与前互联网时代有很大的不同:互联网世界的身份有很强的匿名性而且与现实世界的身份在一定程度上是隔离的,甚至是独立的。一个人可以有多个账号,如果把互联网账号看成一个人的身份,则身份之间不等价,但多种身份都指向一个人的存在。一个账号可以被转手,或者多人共享,他人很难察觉终端背后是否是相同的人。

图2: 基督教的三位一体与JavaScript的三位一体(笑)

互联网账号的存在是不连续的,当一个账号发出消息,它的存在性就得到了短暂的体现;当一个账号陷入沉寂,没有人会知道他什么时候再次上线,可能是一分钟,可能是一天,也可能不再上线了。

Further more

Lain中的克苏鲁元素

Serial Experiments Lain的游戏企划其实比动画要早,但是因为种种原因,动画先行播放,而游戏随后发布。 Lain的游戏剧情比动画更加黑暗,剧情与动画也有诸多出入。我认为,动画与游戏如此不同的很大一部分原因在于动画的剧本作者,小中千昭。

小中千昭在剧本里夹带了一定量的私货:克苏鲁元素以及一位名叫艾丽丝的观察员。小中千昭酷爱克苏鲁,在Lain以及后续参与的作品例如数码宝贝3驯兽师之王以及迪迦奥特曼里,都加入了一定量的克苏鲁神话名词、不可名状的怪物以及san值归零发狂的普通人(调查员)。

Lain中,最富有克苏鲁色彩的,就是第12集中成神的玲音与英利的对决,代表普通人类的艾丽丝不能目睹英利的全貌,面对一只若隐若现的手和突然聚集在一起的显示器和电线,毫不意外地发狂然后被玲音删除了记忆。

顺便一提,小中千昭的作品里总是有一位名为艾丽丝的角色。从ありす in Cyberland的主人公爱丽丝,到Lain中的瑞城艾莉丝,再到Tamers中的加藤树莉(以Lain中的艾莉丝为原型,并且名字借自艾丽丝的闺蜜)。

数码宝贝3驯兽师之王

《数码宝贝3驯兽师之王》(Digimon Tamers,以下简称Tamers)于2001年4月开播,系列构成与Lain相同,均由小中千昭担任。

Tamers具有Lain相似的世界观和设定,很多方面只是把Lain中的元素重组并转化为更加通俗易懂的内容。

Tamers充分展现出人类对于科技的复杂情感:一方面是认为科技改善生活的积极乐观;另一方面是科技威力远超发明者想象而失控的恐怖。 Tamers中古乐兽是掌控所有数码兽进化的程序,但在数码侏儒的帮助下被转化成了一只数码兽,并且对于自己作为数码兽的自我认识坚信不疑。 Lain中,主角玲音被反派boss英利政宗称为具有了肉体的计算机程序,其肉体是橘综研(剧中规模超然的科技公司)研发的人造人。 Tamers中一开始的剧情矛盾是数码兽相继出现在了现实世界,将近结局时提到,数码兽的肉体是通过高科技技术合成蛋白质实现的。

Tamers里,数码兽虽然由人类创造,但又供奉起同为数码兽的神明。作为神明的青龙兽产生了将数码生命原理变成数码兽的愿望,却是数码侏儒实现了他的愿望,使得古乐兽得以诞生。 Lain中,英利始终坚信自己是Wired世界的神明,却始终没有意识到自身的神权是由谁赋予的。因此,在两部作品中,都出现了自封神明但最终被证伪的角色。

Lain中,Wired世界是可以直接影响现实世界的。一个贯穿全篇的隐喻是,阳光下阴影的面积代表了Wired世界对现实世界的影响程度。而在Tamers里,贯通各个章节的主线就是调查研究数码世界对现实世界的侵蚀和影响。

在对网络世界的态度上,两部作品也展现出了一致的态度。 Lain中提到,Wired世界不是现实世界的上层目录,Wired世界和现实世界是相互纠缠的、等价的,Lain最终消除了世界上所有人关于自己的记忆,使Wired世界不再干扰现实世界。 Tamers的结局,数码兽最终都不得不回到数码世界中去。作者借剧中“大人”之口传达道,数码兽如果始终存在于现实世界,迟早会变成最终boss那样的怪物。

人物设定上,Tamers中的配角加藤树莉还继承了Lain中加藤树莉的名字和瑞城爱丽丝的经历和声优,是同时担任两部作品系列构成的小中千昭的个人愿望,“希望这个角色最终能够获得幸福”。

结论

毫无疑问,Lain是一部cult片:与主流思想格格不入,但一小撮爱好者却对其推崇备至。正如剧中玲音对洋娃娃自言自语所说,“一个人只能认知他知道的事情”,人的认知不能超过他的认知边界。

对于Lain的欣赏也是如此。

在互联网上查找资料的时候,我看了一些我认同的观点,也看到了许多不认同的观点。有些想法我会认为其构思精妙,但这不意味着我习得了他的思考,而只是碰巧和他有相同的想法罢了。一千个人眼中有一千个哈姆雷特。不同人对Lain的解读注定是不同的。如果一个人关注剧中玲音的精神状态,或许会始终注意整个片子里阴郁的氛围;如果他对互联网和信息技术理解非常深入,或许会始终审视其中科幻的部分是否合理。

作者的想法,和观众的审美理解注定不会完全相同。在看到BD附带的动画脚本内容之前,我没有意识到玲音名字的汉字和假名写法代指不同的人格这件事。在看了PS版游戏说明书之前,我也不知道游戏和动画的内核究竟有什么不同。

Lain诞生于互联网起步期,它的部分内容超出了所处的时代,有些内容却也受时代所限。在互联网诞生的初期,人们对计算机网络的展望是期望用它构建起人与人的连接,基于SMTP协议的邮件分发以及基于FTP等协议的文件分享。

在物联网仰卧起坐,元宇宙贩卖概念的今天,互联网接下来连接什么、如何连接,都还没有定论。

Fin.

参考文献

[1]
シオドア, “Club cyberia: Serial experiments lain20周年企画イベント,” 2018. https://web.archive.org/web/20220328081740/https://mousou0322.wixsite.com/club-cyberia/contents
[2]
C. Jackson, “Topologies of identity in serial experiments lain,” Mechademia, vol. 7, no. 1, pp. 191–201, 2012.
[3]
M. Poirier, “Amime review: Serial experiments lain,” 2001. https://web.archive.org/web/20110826092828/http://www.ex.org/5.2/25-anime_followup_lain.html
[4]
A. Escobar, D. Hess, I. Licha, W. Sibley, M. Strathern, and J. Sutz, “Welcome to cyberia: Notes on the anthropology of cyberculture,” Current anthropology, vol. 35, no. 3, pp. 211–231, 1994.

如何配置一个学术写作博客(二)

作者 Cerallin
2022年4月11日 21:26

有关我的博客的初步的探索中已经列出一系列好用的hexo主题和插件,但仍留有三个问题没有解决:TOC中的参考文献无法取消自动编号;bib文件需要手动生成;graphviz的dot文件也必须手动生成。其中第一个问题因我为hexo贡献了代码而得到了解决,后两个问题则延续到了今天。曾经的解决方案,预处理脚本,不能让hexo-server监听到上述两种文件的变化。本文将提供相对完美的解决方案,使需要重新绘图或者更新引用的时候,hexo-server可以自动更新。

hexo

去年刚刚开始使用hexo的时候,所选hexo版本为5.4.0。不久前本站升级到了6.1.0,因为hexo从该版本开始才实装了我的贡献:TOC中的参考文献可以取消自动编号,就如本文的附录一样。

hexo主题

我的hexo主题hexo-theme-yuzu更新到了2.2版本(或许不久就要更新2.3了)。

相比于去年,JS代码稍微多了点,但我已经非常克制了……

比较重要的更新,大概是:

  • 自动切换浅色/深色模式
  • 代码块添加复制按钮
  • TOC移动到了文章上方

学术博客上方的两朵乌云

bibliography的解决方案

查阅pandoc文档发现,可以为每个md文件指定对应的bib文件,使用方法是在metadata里加入bibliography: file.bib这样的选项。

尝试后发现,引用失败。

Debug发现,hexo的renderer接口并不会把metadata段(每个文件开头三个减号里的内容)传给renderer。因此,我们需要分别写两个metadata段,一个给hexo,一个给pandoc。

于是,一篇文章可以长这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
---
title: "Serial Experiments Lain: 一系列模糊了虚拟和现实的实验(上)"
tags:
- 二次元
categories:
- 浅思
date: 2022-04-05 10:47:14
---

---
bibliography: source/_bibs/Serial Experiments Lain.bib
---

... blah blah ...

> In the 1980s, the synthesizer became a common, viable tool for musicians. [@klemens201221st]

... blah blah ...

Fin.

可以看到,第二段metadata只有bibliography,虽然丑陋但至少实现了功能。

需要注意的是,_config.yml中有关pandoc的部分不能指定bibliography文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
pandoc:
extra:
# pandoc 的交叉引用插件
- filter: pandoc-crossref

# 换了个参考文献格式
# - csl: "GB7714.csl"
- csl: "elsevier-with-titles.csl"

# 注释掉,很重要。
# - bibliography: bibfile.bib

# 留空就好
- citeproc:

graphviz的解决方案

一个简单而美好的愿望是,互联网上已经有了基于hexo的从dot语言生成SVG的插件。抱着这样的幻想,我查遍GitHub,结果发现其他懒蛋都是用JS即时渲染出SVG的。

这可不行。我想要静态生成。

于是我写了一个简单的插件,实现了SVG静态生成:hexo-renderer-viz

安装了这个插件之后,我把所有的dot文件都放在source/images/graphviz文件夹中。 source/images/graphviz/a.dot最终会被渲染为source/images/graphviz/a.svg

附录:本站使用的hexo插件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 功能增强
- hexo-html-minifier # 删除空行缩减网页大小
- hexo-pagination # 分页
- hexo-generator-archive # 归档
- hexo-generator-baidu-sitemap # 网站地图
- hexo-generator-category # 分类
- hexo-generator-feed # RSS
- hexo-generator-tag # 标签
# 部署
- hexo-deployer-git
- hexo-deployer-sftp
# 学术插件
- hexo-filter-mathjax # LaTeX 公式
- hexo-renderer-pandoc # pandoc 是搞学术的精髓
- hexo-renderer-viz # graphviz 绘图
# 体验优化
- hexo-filter-fix-cjk-spacing # 删除 CJK 字符间的多余空格
- hexo-filter-text-autospace # 在 CJK 字符和拉丁字符间添加间距(并不是空格)

Fin.

Serial Experiments Lain: 一系列模糊了虚拟和现实的实验(上)

作者 Cerallin
2022年4月5日 10:47

Serial Experiments Lain(以下简称Lain)是由Production 2nd企划,Triangle Staff负责制作的原创实验性动画,1998年7月6日起在东京电视台首播,全13话。作为实验动画,Lain在大部分方面的表现手法都非常前卫,并对后世类似题材作品产生了深远影响。其脚本、分镜、音乐、色彩运用等方面表现十分前卫,且探讨的哲学内容之丰富,至今少有能与之匹敌的动画。

引言

Serial Experiment Lain是一部实验动画,随着游戏、杂志连载而发展起来的当之无愧的连载实验作品。其主角岩仓玲音,不仅在日本而且在海外用户中也很受欢迎,常常被海外极客们视为互联网深度的代表和象征[1,2]

Lain严格来说可能不算赛博朋克。众所周知,赛博朋克的赛博意为“Cybernetics”,即控制论,表示人类的科技反过来控制人类。朋克代表思想解放和非主流思想,许多赛博朋克作品将这一元素体现为个人意志对反乌托邦高压统治的反抗。

赛博朋克有三大母题,赛博空间、人工智能和反乌托邦。 Serial Experiment Lain主要描写和讨论了有关赛博空间的种种现象和命题。而对于其他两点则只是一笔带过。

Lain标题解析

Serial Experiments Lain是实验动画,如同那些实验音乐一样,给人以光怪陆离的印象。 Lain的标题可以直接翻译为“系列实验玲音”,其中玲音是剧中主角的名字。可以发现,该标题是不符合严谨的英语文法的。如果放入一个介词变成“Serial Experiments of Lain”,就可以解释称“玲音的系列实验”。

但这样解释真的是作者的本意吗?考虑到这是一个日本动画,我们可以跳出严谨的英语文法来考虑标题的含义。

如果我们忽略属格带来的屈折变化,就可以认为Serial Experiments和Lain直接构成名词短语。这样一来,标题的含义为“系列实验的玲音”,即玲音参加到了系列实验之中,且很可能是实验的核心人物、实验对象或实验实施的媒介。不仅剧中的Lain在不断发生变化,而且动画本身在表现形式上也在进行实验,意图探究动画表现力的边界,以及赛博世界与现实世界的边界。

Lain诞生的社会与时代背景

赛博朋克起于欧美,却兴盛发展于隔海相望的日本。日本街头的景色成为赛博朋克视觉元素的重要组成部分。

1950—1970年的日本经济高速,农村人口向城市高度集聚。无论在水平还是垂直方向上,无序和过度的扩张、细密化的分工和组织、人与人在经济和文化上的割裂,这些过度膨胀的立体城市特征,便是赛博朋克中进一步奇观化的城市的灵感来源。日本都市中的坚硬楼房、工业化、无处不在的标准设计尺度,给人钢铁丛林的感觉。人们如蝼蚁一般,在科技的操控下迷失在追求自由的虚幻中。

《攻壳机动队》便是描述科技异化脱胎于人类又反作用于人类的一部漫画,将表示控制论的“赛博”二字表现得淋漓尽致。

1988年至1989年间发生的“宫崎勤事件”对日本动画产业是一个毁灭性的打击,并导致动漫产业长时间低迷。虽然受到“宫崎勤事件”的影响,但是,相比其他工业产业,动漫产业基于自身深厚的积淀,在经济危机里依然有巨大的影响力以及雪球效应。经济危机中的日本,需要廉价并且通俗易懂的文化娱乐产品,而日后成为日本特色文化的黑白漫画,在此时顺应潮流而兴起。日本政府于是将软文化提升定为国策,这无疑默默地促进了动漫产业的发展。

20世纪90年代初,日本股票和房地产市场泡沫相继破灭,此后日本经济长期处于低迷状态。 90年代末,日本经济“失去的十年”的提法就见诸报端,现如今,已经是“失去的三十年”了。日本经济泡沫破灭的同时,随着购入个人电脑的门槛大幅降低,互联网开始在全世界范围内普及。自1993年起,日本的网民数量飞速增长,1997年的网民数量几乎是比1996年翻了一番。日本虽然可能具有发达国家中较低的个人电脑占有数,但却引领了世界的移动互联网发展[35]。网络固然拉近了陌生人之间的距离,但经济泡沫破裂对日本的民族自信和全社会的社交关系的打击也是毁灭性的。

1995年,奥姆真理教制造了震惊世界的东京地铁沙林毒气恐怖袭击,造成13人死亡及5510人以上受伤。奥姆真理教事件的阴影长期笼罩着当代日本社会的各个方面,助长了全社会的不安感。这一事件本身可以被视为体现了当代日本社会对技术的复杂愿景的许多特征元素,即认识到技术的危险,但仍对其潜在能力感到敬畏[6]。正如村上春树所说,“1995年1月和3月发生的阪神大震灾和地铁沙林事件,是日本战后划历史的具有极其重要意义的两大悲剧,是即使说日本人的意识状态因此而前后截然不同也不为过的重大事件。有可能作为一对灾难、作为在讲述我们的精神史方面无可忽视的大型里程碑存续下去。”[7]

在时代的背景下,1995年播出的动画《新世纪福音战士》(下称EVA)描述了社会里冷漠的人际关系,反映出当时时代的青年对未来的不确定的怀疑和无所适从的无助感,引起广泛共鸣。其主角碇真嗣是“失去的十年”中一代人的缩影,对于父辈强加给自己的“时代使命”抱着不理解的、逃避的态度,在自我怀疑中成长。在21世纪开始后,日本逐渐习惯了自身的经济状态,反映社会对泡沫破灭前后巨大落差的文艺作品逐渐减少并最终消失。

相比于《攻壳机动队》讨论肉体和灵魂、人智与程序的关系;EVA表现年轻人普遍的孤独和人与人之间的交流障碍;Lain更加侧重于描绘互联网,或者说赛博世界对于现实世界的影响。 Lain中各方势力对于Wired(剧中的互联网)和玲音的态度体现了日本社会对于互联网和新技术的复杂情感。一方面,二战时日本承受了世界上独一无二的两次核打击;另一方面,日本的经济曾经飞速发展,在许多科技领域也一度走在时代前列。这些对于科技发展的矛盾心理始终萦绕在日本的科幻作品中,日本民众的心里,久久不能消散。

Lain的前瞻性

在现实的互联网世界里,计算机之间只要遵循联网协议和传输控制协议簇(TCP/IP)就可实现通信。在互联网工程任务组(IETF)刚刚为IPv4地址数量可能不够而未雨绸缪、日本个人电脑普及率不到25%的时候,Lain就大胆预测:作为IPv4替代的IPv6协议,虽然有着约个地址,但还是遭遇地址紧缺的情况。

图1: 公元1997-2010年日本网民数量和普及率的变化图[8]。其中平成9年为公元1997年,平成10年为公历1998年,以此类推,平成22年为公元2010年。

图1展示了1997-2010年日本网民数量和普及率的变化。可以看到,Lain在日本首次播出的1998年远不是日本互联网用户增速最快的时期,甚至只是日本互联网刚刚起步的时期。

与大多数科幻作品一样,Lain中描述了许多近未来的科幻场景。 Lain中所做出的预言在现实中有的已经实现,有的正在实现。例如,第一话中,初中学校的教室黑板上写着C语言;而现实世界中,2020年开始,日本把编程教育作为必修课程普及到了小学和初中。第四集中的汽车自动驾驶和智慧城市等。剧中,NAVI是可以基于语音交互的个人计算机,而当今世界的人工智能助理如Alexa、Siri、Google Home等正在以缓慢的速度渗透全世界。对于赛博世界的幻想,Lain表现得更加大胆,例如让Wired(剧中类似互联网的存在)基于人类的集体潜意识而建立,实现人类摆脱设备而联网。

不过,其中有些内容是赛博朋克的老生常谈,已然不再新鲜,因此也不是Lain首次提出。本文主要介绍Lain中展现出的有关互联网的现象和作者对未来互联网发展所作出的“预言”。 Lain一方面认为互联网即将有飞速发展,另一方面又对互联网的阴暗面表达了担忧。

隐私泄露和网络流言

剧中玲音发觉自己被黑色轿车中的两位黑衣男子监视,甚至发现他们比自己更加了解自己。玲音的四个身份(或者说人格)中有一个就是无处不在的偷窥狂。

剧中,玲音因为被贴上了偷窥狂的标签而被几乎所有人敬而远之,玲音的好友艾丽丝也深受流言蜚语的折磨。在现实世界中,隐私是有关互联网讨论最多的事情之一。

西方国家标榜自由,对于Cookies、IP追踪、浏览行为分析等技术抱有反对态度者甚多。大型网站总是需要使用“Accept Cookies”的弹窗来询问用户是否允许Cookies保存用户行为数据,即使用户不知道在用户看不到的地方服务方是否遵循了规则。

而在中国,中国人对于隐私的态度则过分开放。 2018年,百度CEO李彦宏称,“我想中国人可以更加开放,对隐私问题没有那么敏感。如果他们愿意用隐私交换便捷性,很多情况下他们是愿意的,那我们就可以用数据做一些事情。”但也有评论者指出,不同意用户协议就无法使用软件,很多貌似自愿,都是“被自愿”[9]

Infornography

Infornography是一个英语中本不存在的合成词,由信息的“information”和色情的“pornography”组成,形容对获取、操纵和共享信息的沉迷或痴迷。该词也是Lain第11集的标题。

第11集中的玲音觉醒了Wired世界的神明的力量。观众跟玲音一起,听着DJ的混音音乐,看着前几集的意识流混剪,伴随没有一句台词。总有许多镜头一闪而过,或是代码,或是文字片段:有日文,也有英文。镜头之间看起来毫无关系,又好似有必然的联系。

我认为,这一段意识流剪辑的观看方式大致为两种:一种是逐步暂停,等到获取页面上每一个角落的信息之后再继续观看的;另一种则是从头到尾不暂停,用眼睛收集到多少信息就了解多少信息,而不会再看第二遍了。

不论是上述哪种,都体现了Infornography这一概念:前者是获取尽可能多的信息的偏执,后者是喜欢目不暇接的信息量的浮躁。在我看来,作者是借这一段意识流剪辑,不仅打破第四面墙将观众自身纳入到审美客体当中,而且能够引起观众的反思和自我批判。

自上个世纪80年代起,新中国的人民对文化作品的欣赏和学习门槛降低了不少。欣赏电视剧《红楼梦》《三国演义》《水浒》代替了阅读原著;学习古诗词只需熟读其中的“名句”即可;通过影视剧了解明清历史等等。

发展到如今,人们已经没有耐心看完时长40分钟的视频,人们的生活习惯从“快餐化”转变成了“碎片化”。通过几分钟长的视频,或是几千字的文章就能获取信息,这种便利性备受推崇;但另一方面,几分钟就能了解到的知识或许具有一定的结构,但注定不够全面、深入。自大的互联网冲浪者以为自己看了两三个视频和文章就习得了相关知识,与其他网友吹嘘自己的学识,丝毫不觉得自己肤浅。

Lain中的所谓“Infornography”甚至要更进一步,如果说学习“碎片化”的知识仍是在学习,则信息成瘾的人浏览信息失去了目的性,只是在浏览信息而已。他们在借信息缓解焦虑,就如物质成瘾的患者一样,通过攫取信息这种行为本身获得力比多,而不是其背后的知识。

不知道屏幕背后的你在短视频上花费了多少时间,在打开短视频软件之前你是否带有一定的目的,还是说,只是消磨时间而已。

消磨时间,一定要用浏览无用信息这种方式吗?

互联网与现实世界的紧密耦合

Lain中,Wired的链路层通过“舒曼共振”实现,而“舒曼共振”是包围地球的电磁场。因此,人类始终在Wired的笼罩之下,Wired也深深嵌入到现实生活中,深刻影响着现实生活。剧中协议7的出现使得人类可以直接连接Wired而不通过设备,冯诺依曼架构的IO被人的感官所取代,人可以不通过屏幕而是直接获取信息。

虽然现实中互联网与人类日常生活的结合没有那么紧密,但当代互联网也已经深深嵌入到现实中。在新冠疫情的影响下,互联网的影响力前所未有地扩大了。移动支付、外卖快递、远程会议……一切的一切都以互联网为交换信息的媒介。互联网早已成为现代人生产生活中不可或缺的一部分。

Lain的实验性

动画表现力的边界

EVA相同,Lain的制作成本也不高,经常出现静止画面,只是通过镜头语言和运镜技巧,让人观看不至于觉得枯燥。每集开头和第11集前半存在大量重用画面,看起来就非常经费友好。

与EVA的64秒静止画面和直接使用原画的画面相比,Lain没有在动画画面上做出那么激进的表现。但Lain在表现手法上也面临巨大的挑战:想要用有形的画面表示无形的信息是无比困难的。正如Scott Bukatman所说,“信息时代的新兴的电子技术是无形的,在人类的时空体验之外循环往复。”[10] Lain为了体现互联网的种种特性,使用蒸汽波和荧光绿制造科幻的感觉,与传统黑客题材影视作品一脉相承。为了体现Wired的匿名性,Lain中也使用了只有眼睛或者耳朵的小黑人来表现。为了展现网上冲浪搜索信息的行为,Lain剪辑出意识流的不连贯的信息,而最终拼凑出完整的人类可读的篇章。

此外,Lain为了模糊自身与现实的界限,使用了很多很像电视节目的片段。比如第九集中突然插入纪录片式的网络历史节目,如果是偶然切换电视台看到,还以为是真的,因为节目上显示的时间和当前时间一致:作者早已按照Lain首播时间算好该集播出时间了。

模糊的第四面墙

所谓第四面墙,是指话剧和观众之间无形的屏障,是现实和虚构的界面,也是审美主体和客体的界限。信息时代,隔阂了虚拟与现实的屏幕,当之无愧地成为了第四面墙的新的载体。

突破第四面墙一般是指艺术作品中的角色不仅意识到自己仅仅是故事中的角色,而且可能具有与观众直接交互的能力。传统的突破第四面墙的角色是有弱点的:其能力不过是作者赋予的。观众可能会被角色突然突破第四面墙的行为吓一跳,但回过神来就会意识到,角色永远只能说出作者精心设计好的台词,而在创作时,作者永远不能预测全部读者的行为。

Lain则有所不同。从始至终,Lain中都没有角色明确指出自己只是故事中的角色。第十三集开头,玲音与观众直接对话,或许可以算是传统的突破第四面墙的行为。

但是Lain突破了审美主题和审美客体的本来清晰的界限。现实世界和观众都被纳入到Lain想要表达的内涵当中去了。

剧中有很多梗,与现实中的计算机技术和产品一一对应。比如剧中流行的个人计算机,被称为NAVI,是受Apple公司的Knowledge Navigator的影响。 NAVI造型和UI界面从Apple、NeXTSTEP、BeOS取材甚多。玲音获得的Psyche处理器以1990年代出现的个人电脑部件Overdrive处理器为原型,而动画后半期出现的第一台iMac是动画首次放映前刚刚公布的机型。

剧中对世界观的塑造也采用大量现实中真实存在的科学和伪科学名词,以塑造与现实极为相似的世界:舒曼共振、费城实验等等。

当我们思考剧中所展示的互联网世界对现实世界的影响,总是会不自主地带入到当今世界中,因为剧中展现的现象和现实中存在的普遍现象别无二致。

第十一集的意识流片段充斥着大量的信息。观众如果试图找出其中蕴含的逻辑,只会一无所获。该集标题名为“Infornography”,但整个动画都没有相关解释。因此我认为,该集将观众试图找出意识流中的理性逻辑这一行为也纳入到审美客体中,表达对现代人“信息成瘾”的讽刺。

Lain中的赛博世界

纵横交错的电线

在当今日本的老城区,头顶交错的电线是人们早就习以为常的景色。这种通过电线杆架设在空中的电线,是明治维新之后日本飞速发展带来的产物。但是,随着电线的老化以及时代的进步,架空配电技术逐渐显露出各种缺点:应对台风等极端气候的能力不足、容易受到撞杆、断线等外力影响等。因此,目前日本电网新架设的线路基本都采用地下电缆供电,而已有的复杂架空电路运输网络因为资金问题一直保留到了现在。在Lain中,交缠在一起蛛网般的电线和延申到未知的远方的电线杆的镜头,总是伴随着模拟电路失真的底噪杂音出现,代表着人与人的连接,或者说Wired世界对现实世界的影响。

图2: 日本街头的电线网络。(a)和(c)分别摄于横滨老郊区和新大久保,可以看到老城区的架空电线错综复杂。(b)摄于横滨港未来。新街区多采用地下电缆,没有电线杆显得十分整洁。(d)《Lain》中的电线杆和电线,隐喻Wired(剧中的互联网)世界连接所有人类。

全球脑与个人主体性的消解

人は、繋がでいてるよ。

图3: 第11集中,肉身联网的玲音。玲音不再需要计算机处理IO,而可以直接连接到Wired。英利说,玲音是计算机程序,我们人人都是计算机程序。而肉体不过是容纳我们的硬件罢了。

Lain花费大量篇幅或隐讳或直接地设定和解释了全球脑这样一个系统论的概念。

全球脑是以地球为基础,数量巨大的拥有发达大脑和创造力的所有人类个体,他们借助于各种信息处理工具,通过各种通信方式,彼此之间相互作用,结合成的具有神经系统特征的自组织巨型网络。该网络具有比人脑更高级的信息处理能力和创造力。它所表现的智能,就是地球智能[11]

显然,剧中反复出现的Cyberia夜总会就是受到了Douglas Rushkoff在1994年发表的同名书的影响。而Cyberia中则讨论了网络文化下在盖亚理论中的全球脑概念。

所谓全球脑,在赛博朋克影视作品中出现得不少,例如从Neuromancer到黑客帝国都有的“matrix”。但是这些作品都没有像Lain这样把人的个体消解推进到如此激进的程度:每个人都变成了全球网络的神经元,人的主体性和个体性被彻底地消解了。

Lain中给出了全球脑的一种可行的实现方式,即“舒曼共振”,这是Lain的科幻设定最“硬”的部分。由于“舒曼共振”覆盖全球的特性,使得人类被动地接入Wired,不可逃避的。正如剧中所说,“人与人是连接着的。”想要摆脱Wired的控制,人只能逃到没有任何其他人存在的地界。

有关集体潜意识和全球脑的概念在动画中出现得也不少,从EVA的人类补完计划,到Lain中的Wired,再到Code Geass R2中的阿卡夏之剑系统。不过,只有Lain做到了较为硬科幻地给出全球脑的设定与实现方式。

to Be continued ->

参考文献

[1]
将来の終わり, “きみは「serial experiments lain」を知っているか 20周年を経ていまだ続く実験,” 2018. https://web.archive.org/web/20220204095029/https://nlab.itmedia.co.jp/nl/articles/1807/21/news005.html
[2]
shiki02, “海外の『serial experiments lain』コミュニティについて(mal d’archive),” 2018. https://web.archive.org/web/20201211021344/https://www.excite.co.jp/news/article/Getnews_2065068/
[3]
N. Gottlieb and M. McLelland, “The internet in japan,” Japanese cybercultures, pp. 1–16, 2003.
[4]
Y. Lee, H. Kim, I. Lee, and J. Kim, “The empirical study on use value of mobile internet,” Proceedings of Korean human-computer interaction, vol. 11, pp. 4–5, 2002.
[5]
K. Ishii, “Internet use via mobile phone in japan,” Telecommunications Policy, vol. 28, no. 1, pp. 43–58, 2004.
[6]
S. J. Napier, “When the machines stop: Fantasy, reality, and terminal identity in "neon genesis evangelion" and "serial experiments lain",” Science Fiction Studies, pp. 418–435, 2002.
[7]
村上春树, 地下. 上海译林出版社, 2011.
[9]
[10]
S. Bukatman, Terminal identity. Duke University Press, 1993. Available: https://www.jstor.org/stable/4241108
[11]
杨友三, 全球脑. 吉林出版集团有限责任公司, 2010.

CentOS 8更新内核

作者 Cerallin
2022年3月30日 10:36

Linux内核被爆出漏洞,需要更新。

实验环境

系统:CentOS 8 内核版本:4.18.0-240.1.1.el8_3.x86_64

实验步骤

更新源

2022年1月31日,CentOS团队终于从官方镜像中移除CentOS 8的所有包。现在他们被转移到https://vault.centos.org

编辑/etc/yum.repos.d/目录下的文件:

1
2
3
CentOS-Linux-BaseOS.repo
CentOS-Linux-AppStream.repo
CentOS-Linux-PowerTools.repo
1
2
3
4
5
6
# [baseos]
baseurl=https://vault.centos.org/centos/$releasever/BaseOS/$basearch/os/
# [appstream]
baseurl=https://vault.centos.org/centos/$releasever/AppStream/$basearch/os/
# [powertools]
baseurl=https://vault.centos.org/centos/$releasever/PowerTools/$basearch/os/

安装elrepo源

1
2
rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org
yum install https://www.elrepo.org/elrepo-release-8.el8.elrepo.noarch.rpm

当然,国内用户可以自行选择清华、中科大或者阿里源。

安装内核

1
2
3
4
# 查看可安装版本
yum --disablerepo="*" --enablerepo="elrepo-kernel" list available
# kernel-lt是目前的lts版本,5.4.188
yum --enablerepo="elrepo-kernel" install kernel-lt

更新引导

1
grub2-set-default 0

验证内核版本

1
2
$ uname -r
5.4.188-1.el8.elrepo.x86_64

Fin.

符号回归与LaTeX公式

作者 Cerallin
2022年1月10日 13:56

前文讲到,gplearn支持导出dot语言脚本,进而可以利用graphviz画图。本文将要尝试实现,将gplearn生成的公式转换成人类可读格式。

公式转换流程

图1: 公式转换流程图

本文所介绍的公式转换流程如图1所示。

需要下载安装的Python库

  • requests
  • sympy

SymPy

18年的stackoverflow回答给出了一种解决方案:使用SymPy进行公式转换。

1
2
3
4
5
6
7
8
9
10
converter = {
'sub': lambda x, y : x - y,
'div': lambda x, y : x/y,
'mul': lambda x, y : x*y,
'add': lambda x, y : x + y,
'neg': lambda x : -x,
'pow': lambda x, y : x**y
}

sympy.sympify('sqrt(div(add(1.000, X0), mul(-0.993, X0)))', locals=converter)

上述代码将公式转换成sympy的内部格式,然后使用sympy.latex就可以输出LaTeX公式了。

LaTeX公式在线转换API

LaTeX公式也不是非常直观,为了将其转换为SVG格式图片,我写出并部署了一个基于MathJax的在线转换API。 URL为https://mathjax.cerallin.top/?latex=E%3Dmc^2

结合gplearn使用

按照图1所示流程,代码可以分成三部分。

  1. gplearn -> SymPy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from sympy import sqrt, log, abs, max, min, sin, cos, tan

# 转换成人类可读的公式
converter = {
'sub': lambda x, y: x - y,
'div': lambda x, y: x / y,
'mul': lambda x, y: x * y,
'add': lambda x, y: x + y,
'sqrt': lambda x : sqrt(x),
'log': lambda x : log(x),
'abs': lambda x : abs(x),
'neg': lambda x : -x,
'inv': lambda x : 1 / x,
'max': lambda x, y: max(x, y),
'min': lambda x, y: min(x, y),
'sin': lambda x : sin(x),
'cos': lambda x : cos(x),
'tan': lambda x : tan(x),
}

formula = sympy.sympify(str(est_gp._program), locals=converter)
  1. SymPy -> LaTeX
1
2
# 转换成LaTeX公式
formula_latex = sympy.latex(formula)
  1. LaTeX -> SVG
1
2
3
4
5
6
7
8
9
10
11
# 根据LaTeX公式转换成SVG图片
import requests

svg_res = requests.get('https://mathjax.cerallin.top/', {
'latex': formula_latex
}).text

# 输出SVG图片到文件
with open('SymbolicRegressor'+'.svg', "w", encoding='utf-8') as f:
f.write("%s" % svg_res)
f.close()

结果展示

gplearn输出结果

gplearn预测的符号树

SymPy公式

1
X3 + (X2 + X5)*(X0*X1*X5 + X1) + 43.151

LaTeX公式

1
2
3
$$
X_{3} + \left(X_{2} + X_{5}\right) \left(X_{0} X_{1} X_{5} + X_{1}\right) + 43.151
$$

SVG图片

根据LaTeX公式生成的SVG图片

Fin.

使用VSCode的学术写作

作者 Cerallin
2022年1月9日 16:17

有研究[1]表明,LaTeX用户比Word用户进行学术写作更慢,在相同的时间内写的文本更少,并产生了更多的排版、拼写、语法和格式错误。但是,LaTeX也有其Word无法比拟的有点:排版和内容分离,更便于版本管理和格式切换。本文简单介绍如何在Ubuntu或者WSL2环境下进行LaTeX写作。

下载安装LaTex

下载TexLive

最简单的办法是使用apt安装。但该版本TexLive往往不是最新的。最新版本(2021)的TexLive可以从清华源下载并安装。

1
aria2c https://mirrors.tuna.tsinghua.edu.cn/CTAN/systems/texlive/Images/texlive2021-20210325.iso

下载CLI GUI组件库,这样在接下来安装的时候就有图形界面了。

1
sudo apt-get install perl-tk

安装TexLive

挂载ISO,运行安装程序,然后卸载。挂载路径不推荐设置成/mnt,可以在家目录里随便创建一个目录然后挂载。

1
2
3
4
5
sudo mount -o loop texlive.iso /mnt
cd /mnt
sudo ./install-tl -gui
cd -
sudo umount /mnt

Windows的安装更简单了,双击ISO挂载,直接运行安装程序,然后右键卸载。

设置环境变量

在你的~/.bashrc(zsh的话是~/.zshrc)文件里添加如下几行:

1
2
3
export PATH=/usr/local/texlive/2021/bin/x86_64-linux:$PATH
export MANPATH=/usr/local/texlive/2021/texmf-dist/doc/man:$MANPATH
export INFOPATH=/usr/local/texlive/2021/texmf-dist/doc/info:$INFOPATH

测试安装是否成功

1
2
3
4
tlmgr --version
pdftex --version
xetex --version
luatex --version

安装VSCode的LaTex插件

LaTeX Workshop

配置VSCode

在你的全局user settings里添加如下内容:

1
2
3
4
{
"latex-workshop.view.pdf.viewer": "tab",
"latex-workshop.latex.recipe.default": "lastUsed"
}

其中view.pdf.viewer指定预览文件的浏览器,可以是vscode内置tab,也可以是browser。 latex.recipe.default指定默认使用的latex编译方案,有first和lastUsed两种取值。

下面的代码定义了四个编译方案,两个给中文,两个给英文,两个给带参考文献的交叉引用,两个没有引用。

嗯,我相信你听懂了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
{
"latex-workshop.latex.recipes": [
{
"name": "xelatex",
"tools": [
"xelatex"
]
},
{
"name": "latexmk",
"tools": [
"latexmk"
]
},
{
"name": "pdflatex -> bibtex -> pdflatex*2",
"tools": [
"pdflatex",
"bibtex",
"pdflatex",
"pdflatex"
]
},
{
"name": "xelatex -> bibtex -> xelatex*2",
"tools": [
"xelatex",
"bibtex",
"xelatex",
"xelatex"
]
}
]
}

P.S. 推荐在LaTeX项目的workspace里将files.autoSave设置成onFocusChange。虽然LaTeX编译总是很快,但在电池供电场景,频繁编译显然会导致耗电加剧。

Fin.

参考文献

[1]
M. Knauff and J. Nejasmic, “An efficiency comparison of document preparation systems used in academic research and development,” PloS one, vol. 9, no. 12, p. e115069, 2014.

如何使Windows自动切换深色模式以及修改壁纸

作者 Cerallin
2022年1月5日 22:25

本文从之前的解决方案发展而来。在其基础思路指导下新实现了电脑登录时自动切换深色模式,以及壁纸的功能。

本文所描述的代码被托管到了win-auto-dark

前情提要

基于WSL的cron计划任务实现的自动切换深色模式,在实际使用中体现出来一个问题:当电脑休眠或者关机时,计划任务不会被执行。

一种解决思路是,在用户登录时也执行一次脚本。但是,crontab只有开机hook, i.e., @reboot

查阅资料发现,Windows的任务计划提供了用户登录触发器。 真香

好的,接下来该挑战未知的领域了:VBScript (VBS)。

VBS实现深色模式的自动切换

为什么选VBS呢,因为我记得VBS是Windows系列系统(至少从XP开始)首选的脚本语言,且没有弹窗。

其实在此之前我还试图用bat实现来着,但是写了一部分浑身不适,只好作罢。如前文所说,切换深色模式只需要修改两个注册表值:AppsUseLightThemeSysUsesLightTheme

更换壁纸初尝试

VBS与注册表交互看起来还算简单。首先创建一个shell交互对象,然后依法调用其RegWrite或者RegRead方法。注意,RegWrite的最后一个参数制定了注册表项的类型:REG_DWORD

1
2
3
Set RegObj = WScript.CreateObject("WScript.Shell")
regPath = "HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Themes\Personalize\AppsUseLightTheme"
reg.RegWrite(regPath, True, "REG_DWORD")

让我们对象化一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Class MyReg
' Set value of a variable of Windows registry.
Private Function SetVar(var_name, value)
Dim RegObj, regPath
Set RegObj = WScript.CreateObject("WScript.Shell")
regPath = "HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Themes\Personalize\"
' It is important to specify "DWORD" as the variable's type
SetVar = RegObj.RegWrite(regPath & var_name, value, "REG_DWORD")
End Function

' Set value of "AppsUseLightTheme",
' using "Let" instead of "Set" means it's not an object.
Public Property Let AppTheme(theme)
AppTheme = SetVar("AppsUseLightTheme", theme)
End Property
End Class

配置

请参考GitHub项目的Readme进行配置。

P.S. PowerShell实现更换壁纸的代码也在仓库中。

附录:VBS踩坑指南

  1. VB不区分大小写,不论是关键字、变量名还是类名等等,都不区分大小写。所以类似Java形式的WindowObject windowObject = new WindowObject()是行不通的。所以,VB中应该写成Set w_obj = New WindowObject.

  2. VB中对变量进行赋值时,如果是对象则用Set关键字,如上一条所示。如果是其他类型则没有前缀。

  3. Class的Property支持Getter和Setter。但是,如果是普通类型赋值,要写Property Let,e.g., 上一节的AppTheme;如果是对象赋值,则需要写Property Set。

  4. 函数调用有两种写法:rtn = func(a, b, c),或者func a, b, c。应注意,没有返回值的函数或者子程序被调用时不加括号。

下次再也不写VBS了

Fin.

一种基于类UNIX系统进行高效并行执行的方法

作者 Cerallin
2022年1月1日 17:32

本文介绍一种利用GNU parallel进行高并发运行计算密集型并行任务的方案。上次用这软件是在写爬虫批量下载漫画...没想到科研中也遇到了类似的问题。遂记录下来与大家分享。

系统要求

首先,Windows系统是不可以的。从官网上看,GNU parallel没有win32 build。或者,我正在找替代或者解决方案。

已知的尝试是在git-bash里安装parallel,但是安装是能安装,却不能正常工作。在git-bash里安装parallel参考了stackoverflow的一篇回答

我在git-bash里运行了以下指令成功安装parallel,虽然安装过程中不停地报错。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ (wget -O - pi.dk/3 || lynx -source pi.dk/3 || curl pi.dk/3/ || \
fetch -o - http://pi.dk/3 ) > install.sh
$ bash ./install.sh
$ parallel --version
GNU parallel 20211222
Copyright (C) 2007-2021 Ole Tange, http://ole.tange.dk and Free Software
Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
GNU parallel comes with no warranty.

Web site: https://www.gnu.org/software/parallel

When using programs that use GNU Parallel to process data for publication
please cite as described in 'parallel --citation'.

问题描述

图1: 任务节点依赖关系图

本文将要处理的问题具有图1所示结构,即某一个步骤完全由有限个并行执行的任务组成。这些并行任务之间并没有任何依赖关系,但下一步依赖于所有这些任务,即当这些并行任务都结束时,下一项任务才能开始。

以下载一话漫画为例。

假设某连载漫画的第5话有20页图片,在某漫画网站上,该漫画信息页面的路径为/comic/:comic_id,获取一张图片的路径为/gallery/:gallery_id/:pic_file,其中:pic_file是以.png或者.jpg结尾的图片名。

在漫画信息页面上显示有comic_idgallery_id的对应关系,每一个comic_id有且仅有一个gallery_id与之对应。 :pic_file的列表和次序可以通过带有:gallery_id请求某API获取。

好吧,这些都不重要,不要乱爬漫画站。

解决方案

可以看到,上述任务可以使用图1所示结构来描述,获取comic_idgallery_id以及:pic_file列表可以看作是并行任务集合的前置准备。所有图片的下载可以是高并发的,彼此之间并没有什么依赖关系。任务完成时所有图片都已经下载完毕。

那么,如何使用GNU parallel写出最快速的漫画爬虫呢? shell脚本的部分内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# comic_id := user defined number
# gallery_id := get gallery_id with comic_id
# pic_list := Array of pic_files fetched with gallery_id

# download_pic could download a picture at once.
#
# @param $pic filename of picture
# @param $order order of picture
function download_pic() {
local pic=$1
local order=$2

# Suffix of the picture, e.g., 'png', 'jpg'.
local suffix=${pic##*\.}

curl -s $PIC_API/gallery/$pic -o "${order}.${suffix}"
}

# Export the function so that parallel could load it.
export -f download_pic

parallel -j 4 \
download_pic \
::: ${pic_list[@]}

其中,:::非常有特点,既不是一个:也不是两个:

-j选项指定使用多少个CPU核心参与并行任务的执行,未指定则默认全部(物理核数)。 download_pic为要并行执行的任务,:::后边是任务参数。

当有多个:::时会自动枚举全部选项,比如,parallel echo ::: 1 2 ::: a b c的一种可能的输出结果为:

1
2
3
4
5
6
7
$ parallel echo ::: 1 2 ::: a b c
1 a
1 b
1 c
2 a
2 b
2 c

P.S. 为了最高的效率,parallel不会很及时地进行输出,而是先缓存起来。因此命令行或者文件输出和任务执行情况不同步。

注意事项

正如系统要求中parallel的输出中所提到的,当论文数据处理用到了该工具的时候,应该按照parallel的作者给出的形式[1]进行引用。

1
2
3
4
5
6
7
8
$ parallel --citation
# ...
Academic tradition requires you to cite works you base your article on.
When using programs that use GNU Parallel to process data for publication
please cite:

O. Tange (2011): GNU Parallel - The Command-Line Power Tool,
;login: The USENIX Magazine, February 2011:42-47.

Fin.

参考文献

[1]
O. Tange, GNU parallel - the command-line power tool,” The USENIX Magazine, vol. 36, no. 1, pp. 42–47, 2011.
❌
❌