搞定Lucifen,再次体会到rUGP有多难

7月底,暂离汉化一周年,忽然动了翻俺翼的念头,于是开始搞俺翼R的程序。出乎意料的是,只花了二十多天的业余时间,就把从解包到剧本封包的主要问题都解决了。跟当年搞君望LE的程序一比,天上地下——解决君望LE的解包和初步封包前后花了大半年。单纯以花费的时间衡量的话,君望LE大约比俺翼R难十倍到二十倍。即使考虑自身水平提高、改用Python开发、资料更丰富等因素,也无法忽视两种引擎复杂度的巨大差别。

首先是封包格式和对象种类。俺翼R等Lucifen引擎的游戏有多个LPK包,分别存放脚本、背景、立绘、语音等内容,每个包开头的前缀树和索引数组里记录着包内所有对象的文件名和位置大小。Crass早已提供了前缀树和索引数组的解码算法,只需要跟程序抓一下密钥就能用了……即使不会解析其中某些对象,至少能拆开LPK包。相比之下,君望LE的rio文件没有全局索引,而是把所有对象存成树形。一个对象如何连接到子对象与它的类型、内容有关,只有正确解析了一个对象,才能确认它的子对象存在哪儿、有多大。把rio文件中的所有对象取出来,几乎等于解析所有对象。这是一个可怕的工作:君望LE中已经确认的对象类型不少于88种,而且都是需要进一步解析的二进制数据。相比之下,俺翼R的LPK包解开后,需要进一步解析的基本只有elg、sob、tob三种对象。

图片解析方面差别不大。君望LE这边,westside的四个函数可以合并成一个;俺翼R这边,Crass的四个函数可以合并成两个。当然,原来的算法都是有效的;不过既然做静态替换,需要写逆算法的函数自然是越少越好。两边的难度都在算法清理和合并,写逆算法并不难。

脚本的情况和外层封包类似,虽然都是二进制脚本,但是俺翼R有全局索引信息和强特征,君望LE没有。俺翼R的tob脚本开头有记录命令组分割点的数组,而且命令组中除可自由调整的剧本正文外,每条命令开头都有特定标记。于是,很容易分割命令和提取文本,封包也不用顾忌正文长度的改变。君望LE的脚本复杂得多,11种脚本对象的解析依赖于更深一层约300种命令对象的解析,而且缺少共通的结构。我在RioX中采用的方法是,对假设树剪枝以定位每个对象开头的标记双字。虽然这个双字没有强特征,但是相邻对象标记双字的差和对象长度之间存在弱对应关系。一般情况下扫完整个脚本,假设树中就只剩一个假设了。封包时,对象长度有限制,标记双字也需要调整。

解决了解包封包,处理中文显示就有套路了:先改取字体的API,再改双字节字符判断。然后,就可以开始翻译了。

默默开坑——《俺たちに翼はないR》翻译开始

从12年8月21日君望开坑,到15年8月3日君望完整汉化发布,断断续续做了三年汉化。之后忙创业的事,度过了脱离汉化的一年。现在生活状态有所改变,准备抽一部分时间回归翻译。

随着公司的发展,虽然一直忙,但是有规律有计划的工作在减少。我需要把能招人分配出去的工作尽量分出去,腾出精力处理工作中发生的问题;反过来越来越多的突发问题也让我无法给自己分配计划性的工作,因为处理突发问题很可能造成这些工作的耽搁。结果就是,我一天到晚要时刻准备着,也会处理不少事,但是总有很多手头无事的碎片时间。既然如此,我想业余还是做点galgame翻译吧:有趣,能换换脑子,不怕被随时打断,没有进度压力。

至于翻《俺たちに翼はないR》,主要是两方面的考虑。一是有共鸣。其实当年我也曾想写自己的剧本的,也有过一个很模糊的构思。但是读到俺翼之后,发现我不用写了:我想写的主题甚至形式已经有人写了,而且达到了我无法企及的高度。那么现在我把它翻译了就好。二是文本难度很高,不担心被抢坑。当初君望从容填坑,有程序和文本双重困难护航;俺翼的程序虽然易破,文本难度却更高,长度也不亚于君望。从各方面查到的信息显示,俺翼有过两个组开坑,第一个组历经两年后弃坑,第二个组自12年接坑以来没多少动静,至少已经沉默一年多了。最近有人发出来的第一个组翻的文本我看了一眼,只占全剧本的不到10%。时至今日,游戏发售已经过了七年,会去翻而且能坚持下来的,除我之外未必再有吧。

考虑到文本难度太高、作品知名度偏低、我自己无法保证进度这三点,这次就不尝试拉队伍了,纯粹自己做。投入程度会低于君望,不保证进度,根据情况随时可能选择放弃。至于质量,只能说锻炼自己,尽力而为——毕竟王雀孙写俺翼时就是一副要把日语玩坏的架势,日本人阅读原文都可能觉得费劲。

无论如何,今天还是决定默默开坑,在此记录一下。作为前期准备,已经初步完成了《俺たちに翼はないR》的解包封包程序。最近二十几天利用工作之余跟踪程序编写代码,已经做到可以把翻译后的GBK编码的剧本封回去正常显示了,汉化所需的基本功能已完成。跟当初搞君望的程序相比,只花了大约二十分之一的时间——Lucifen就是这么简单,或者说rUGP就是这么难……

最后,我只是想默默做点有趣的事。看到本文的朋友们就不要替我公布了。

一道奥数题

昨天姜总在微信里发了几张图,说是今年奥数国家队选拔的题。心血来潮就去做了一道。做它其实是因为它不需要太多的知识,初中代数几何加上极限的epsilon-delta思想就够了。最喜欢这样的题了~ 更不要说这道题简洁优美。这才是真正的数学和数学探索,才不是被人民大众妖魔化的那些奇葩东西。

犯了很多错误后,具体证明整理了一两个小时。要在当年,这道题我应该还是能在规定时间内拿满分吧,不过一天也就一题的样子吧,距离国家队还是很遥远啊。

题目:求所有正整数映射到正整数的函数f,满足以下条件:对任意三个互不相同的正实数a、b、c,长为a、b、c的三条线可以构成三角形,当且仅当长为f(a)、f(b)、f(c)的三条线段可构成三角形。

证明:

命题1. f是单射
用反证法,若不成立则存在不同的正实数a和b,使f(a)=f(b),不妨设a<b
则a、a+b、3(a+b)/2不构成三角形(因a + (a+b) < 3(a+b)/2),故f(a)、f(a+b)、f(3(a+b)/2)不构成三角形
但是b、a+b、3(a+b)/2构成三角形(因b < a+b < 3(a+b)/2且b + (a+b) > 3(a+b)/2),因而f(b)=f(a)、f(a+b)、f(3(a+b)/2)构成三角形
矛盾
于是假设不成立,f为单射,可设f的逆映射为g

命题2. 对任何正实数a!=b,对所有非负实数x,f(a+b+x)或者不小于f(a)+f(b),或者不大于|f(a)-f(b)|
首先a + b <= a+b+x,a、b、a+b+x不构成三角形,故对所有非负实数x f(a+b+x) >= f(a) + f(b) 或 f(a+b+x) + f(a) <= f(b) 或 f(a+b+x) + f(b) <= f(a)
其中 f(a+b+x) + f(a) <= f(b) 或 f(a+b+x) + f(b) <= f(a) 即 f(a+b+x) <= f(b) - f(a) 或 f(a+b+x) <= f(a) - f(b)
亦即 f(a+b+x) <= max(f(b)-f(a), f(a)-f(b)) = |f(a)-f(b)| 得证f(a+b+x)或者不小于f(a)+f(b)或者不大于|f(a)-f(b)|

命题3. 对任何正实数a!=b,对所有非负实数x,或者f(a+b+x)总不小于f(a)+f(b),或者f(a+b+x)总不大于|f(a)-f(b)|
要证明的是不会出现两个非负实数x1<x2,使f(a+b+x1)和f(a+b+x2)一个不小于f(a)+f(b)另一个不大于|f(a)-f(b)|
设y=x2-x1,存在足够大的正整数n,使得正实数z = y/n < min(a,b)
对k=0,1,…,n-1,必然存在k使得f(a+b+x1+kz)和f(a+b+x1+(k+1)z)一个不小于f(a)+f(b)另一个不大于|f(a)-f(b)|
(因为如果这一点不成立,对应k=0和k=n的f(a+b+x1)和f(a+b+x2)将会同时不小于f(a)+f(b)或者同时不大于|f(a)-f(b)|,与假设不符)
于是|f(a+b+x1+kz) – f(a+b+x1+(k+1)z)| <= f(a)+f(b) – |f(a)-f(b)| = 2min(f(a), f(b))
另一方面,a、a+b+x1+kz、a+b+x1+(k+1)z构成三角形,所以|f(a+b+x1+kz) – f(a+b+x1+(k+1)z)| < f(a)
同理|f(a+b+x1+kz) – f(a+b+x1+(k+1)z)| < f(b)
所以|f(a+b+x1+kz) – f(a+b+x1+(k+1)z)| < min(f(a), f(b)) < 2min(f(a), f(b))
矛盾
于是命题3得证

命题4. 对任何正实数a!=b,对所有非负实数x,f(a+b+x) >= f(a)+f(b)
也就是排除命题3中的另一种可能性,对所有非负实数x,f(a+b+x)总不大于|f(a)-f(b)|
用反证法,若命题4不成立,根据命题3,对某组正实数a!=b,对所有非负实数x,f(a+b+x) <= |f(a)-f(b)|
设 c = g(max(f(a), f(b))),d = g(min(f(a), f(b))),则f(c+d+x) <= |f(c)-f(d)| = f(c) – f(d) < f(c)
考虑不构成三角形的c、c+d+x、c+d+x+c,故 f(c+d+x) + f(c+d+x+c) <= f(c)
令x=0和c/2,两式相加,有 f(c+d) + f(2c+d) + f(3c/2+d) + f(5c/2+d) <= 2f(c) 但是c、c+d+x、c+d+x+c/2构成三角形,f(c+d+x) + f(c+d+x+c/2) > f(c)
令x=0和c,两式相加,有 f(c+d) + f(2c+d) + f(3c/2+d) + f(5c/2+d) > 2f(c)
矛盾
于是假设不成立,命题4得证

命题5. f严格单调增
根据命题4,对任何正实数a<b,f(b) >= f(a) + f(b-a) > f(a)

命题6. 对任何e > 0,存在正实数x,使f(x) < e
nf(1/(n+1)!) < sigma_{i=2}^{n+1}{f(1/i!)}
根据命题4,对正实数a!=b,f(a)+f(b) <= f(a+b),故 nf(1/(n+1)!) < f( sigma_{i=2}^{n+1}{1/i!} )
由命题5,nf(1/(n+1)!) < f(1),就是说 f(1/(n+1)!) < f(1)/n 对任何e > 0,存在足够大的n使 f(1)/n < e,取 x = 1/(n+1)! 根据单调性即有 f(x) < e

命题7. 对任何正实数a!=b,f(a) + f(b) = f(a+b)
反证法,假设不成立,则存在正实数a!=b使f(a) + f(b) < f(a+b)
设e = f(a+b)-f(a)-f(b),根据命题6,存在正实数x,使f(x) < e/2,则f(a) + f(b) + 2f(x) < f(a+b),不妨设x<min(a,b) a、a+x/2、a+x构成三角形故f(a)+f(x)>f(a+x/2),同理f(b)+f(x)>f(b+x/2)
进一步地a+x/2、b+x/2、a+b构成三角形(a+x/2<a+b,b+x/2<a+b,a+x/2+b+x/2>a+b),故f(a+x/2) + f(b+x/2) > f(a+b)
于是f(a) + f(b) + 2f(x) > f(a+b),矛盾
于是假设不成立,命题得证

命题8. 对任何正实数a和b,f(a) + f(b) = f(a+b)
a!=b已证,a=b时 f(a) + f(b) = f(a) + f(a) = f(a) + f(a/3) + f(2a/3) = f(4a/3) + f(2a/3) = f(2a) = f(a+b)

然后就是教科书上最经典的函数方程了,先证有理数然后用单调性证无理数

在BitBucket拆分git代码库操作备忘

把一个大的代码库的其中一部分拆分出来产生新的代码库,保留提交历史。参考BitBucket官方说明和部分网文。

1. 创建新的空代码库newrepo。

2. 在本地把旧代码库clone下来并放进文件夹newrepo。

git clone git@bitbucket.org:user/oldrepo.git newrepo

3. 进入文件夹解除到远程库的链接。

git remote rm origin

4. 删除不需要的文件及其记录,可以是曾经存在但现在已删除的历史文件。其中git rm部分如果报错说匹配不到文件,就加入–ignore-unmatch选项。

git filter-branch -f –index-filter ‘git rm –cached -r dir1 dir2’ — –all

5. 删除变空的提交。这里会遗留一些合并时产生的提交。

git filter-branch -f –commit-filter ‘git_commit_non_empty_tree “$@”‘ — –all

6. 回收垃圾

git gc –aggressive –prune=now

7. 重设远程库并推送代码。

git remote add origin git@bitbucket.org/user/newrepo.git

git push origin master

五律·百日

行游爱峻奇,百夙进天梯。

烁日冬犹夏,流风北向西。

踯躅多顿挫,寂寞少凭依。

此去云山远,漂泊未有期。

——2015.11.16

七律·探父病感怀

严君病重信忽闻,
千里心萦忐忑深。
夜雨急趋归省路,
晨星怅叹旅羁人。
相扶莫挽凋颓势,
反哺无还养育恩。
惜取余时休错过,
流年无影岁无痕。

君望汉化:终于只剩我自己了

age社的游戏,过去多少年里大家都说“这个引擎太难破,解决了就能汉化了”。老实说,我一开始也是这样认为的。所以两年前下功夫搞定了文本解包封包的问题,并期待着有翻译把我的成果用起来,把“有生之年”的君望和Muv-Luv系列翻出来。

但是我很快发现自己想错了。其实在我看到Muv-Luv Alternative的坑在某组人手里晾了几年的时候,就该意识到的。翻译同样是难关。君望和Muv-Luv三部曲文本量都是巨大的,而且不容易翻:君望文风含蓄细腻,Muv-Luv三部曲则涉及大量政治军事设定。Muv-Luv三部曲有幸在几位八月党的手中变成了中文,君望则仍然在我和KFC的手里。

去年三月,君望第一章的汉化以KFC风铃工作室的名义发布。在汉化第一章的半年里,大家分头初翻,然后我和苍蓝各过一遍。虽然30%的文本靠我初翻,30%的文本靠我重翻,总的来说还是比较顺利的。但是,第一章毕竟只是个引子。遗憾的是,第一章发布之后,当我们面对第二章遥/水月线五倍于前的文本时,KFC这边几乎就没有人手来支持君望这边了,包括苍蓝。在最近的一年半里,除我之外的所有人一共只做过二期80多万字中7%的初翻和0.5%的润色。KFC的工作重心在近月及其续作上,现在和不远的将来都没有力量进行君望的汉化。所以,君望的汉化在很长时间里停滞了。

我的目的是填坑,不是占坑。既然KFC无法提供支援,而我的翻译技能也在这两年里成长起来,那么我的选择就只有脱离组织,独自汉化。经过几个月的奋斗,今天,遥线和水月线的全部文本终于完成了初翻和一次校润,第一章的文本也重翻了。接下来的两周我将尝试一下图片的替换,然后在8月27日水月生日那天发布遥线和水月线的汉化补丁。

这次发布的汉化,原文文本包括第一章大约100万字,其中77%的文本完全是我翻/校/润的,没别人碰过;为了提高质量和统一风格,其他23%我也重翻了。以个人名义发布,本身也意味着,所有文本的正确性和通顺程度都由我负责。可以预计到,一定会有人诟病翻译质量——其实作为玩家我也会的。翻译文艺作品本身就是困难的再创作,现有文本的质量我也不算满意。但是一个进入三十代的互联网码农在上班和加班之余搞出来的东西,毕竟是有限度的。不过,我有信心宣称我的翻译质量比大多数汉化组都高,也高于之前发布的第一章。

汉化是长期艰苦的义务劳动。汉化界的主力是学生,因为他们有更多时间。但是,真正在意质量的,啃硬骨头的,往往却是已经工作的大叔们。君望也是如此:开坑的时候摇旗呐喊的不少,过来翻了一点点就消失的也有几位,然而落实到出相对完整的汉化补丁,从程序到翻译都是我一个人的事了。我是真心想要把这件事做出来。我会坚持下去,至少把茜线和三章补全,给出完整的主线。

关于下倉バイオ的几部奇妙作品

Nitro+是个伟大的公司。一边是吃人老虚到处圈钱,一边是和志仓狼狈为奸,自家galgame也一点没丢下。奈良原一鉄、鋼屋ジン、下倉バイオ,接替老虚并把N+的传奇风格发扬广大:残酷的世界,叩人心弦的斗争,人性的沉沦与光辉。他们嘲讽现实,人在无奈的命运中变形、崩坏,然而继续挣扎前行。悲剧从人性幽深处挖出,让人不寒而栗。他们也讴歌梦想追求救赎——好吧,这其实是比较近的事了。我喜欢N+最大的原因是,它倾向于让登台的舞者们尽力起舞,引向角色个性必然的结局,即使前方只有残缺——而不是剪裁角色拼凑出一个令人难以信服的好结局。

第一次接触下倉バイオ的作品其实是今年初的事,读了之后感到相见恨晚。下倉バイオ在N+是个异类。虽然一开始他也尝试写典型N+味儿的作品(《月光のカルネヴァーレ》),也写得不错,却还是没有发挥出他真正的实力——我觉得他稍缺点阴暗的东西。从《スマガ》起就形成了他和津路参汰的组合,剧本和画面相得益彰:肉感到有点夸张的人设,配上奇异到有点荒唐的故事表现,营造出奇妙的气氛。他就在这奇妙的气氛中创造寓言般的世界,写出千奇百怪不落俗套的人物(千奇百怪不落俗套的变态?)。

我感到的下倉バイオ风格最强烈的特点,就是他通过设计游戏结构和玩法,提供了AVG游戏中可谓顶级的阅读体验。他不满足于传统的多线分支框架,用特别的形式把故事展现在读者面前,甚至把读者拉进故事。读者不用操心遍历收集,也不用费劲去跳过重复内容,他总是只把新的东西拿出来,而读者只要跟着他不断探索就够了。他喜欢引用跳出游戏本身的现实话题,喜欢用无字幕甚至无画面的倾诉直接对读者讲述和质问,喜欢用特别的游戏流程引导读者的感受。他擅长讲故事,剧情发展让人充满期待又难以预测,读到后则感觉确实应该如此。他的语言运用相当精彩,多样的说话方式凸现人物个性,文字游戏调节气氛,再加上些爱用语汇(“背に腹は代えられない”、“ガッデム”之类的),感觉是玩得游刃有余。

从某种意义上讲,下倉バイオ和田中ロミオ有点像,不过感觉上没那么反人类。虽然大局的把握上还稍显不足,我更喜欢他一点。另一方面,下倉バイオ其实没多少N+社传统的风格。老虚早先的作品普遍是偏阴暗的,文笔朴实到有点枯燥(我一直恶意地认为老虚文笔功力不足)。奈良原一鉄和鋼屋ジン用更丰富些的文笔继承了厚重阴郁的风格,不过也没有下倉バイオ这么大胆——当然故事题材也摆在那儿呢。

到目前为止,下倉バイオ在Nitro+主要做了五部作品:《月光のカルネヴァーレ》、《スマガ》、《スマガスペシャル》、《アザナエル》、《君と彼女と彼女の恋。》。《STEINS;GATE》和新版《Phantom》他也参加了,不过不是主笔,就不多说了。这里就写点对他主笔的五部作品的感想,尽量避免剧透所以也不会说太多。

一 月光のカルネヴァーレ

这正经是一部继承N+传统的作品。人狼,人偶,炼金术的夙愿。家族,主从,杀戮的宿命。近代电气文明的意大利城市。使用近代武器和特殊能力的搏斗。像鬼哭街,也像尘骸魔京。作品中有不少细节体现下倉バイオ的特点,比如语言运用方面,但是整体上正经是N+的黑暗传奇故事。下倉バイオ表现出高超的命题作文能力,真不愧是旧帝大文学部出身。

这部作品让我印象深刻的,倒不是happy end,而是bad end。因为下倉バイオ在bad end挖掘出了更触动人心的东西。Lunaria线bad end的悲恋,Rebecca线bad end中世界磨去热情的无奈和悲哀,都体现出下倉バイオ视点的独特。

三人生活 Rebecca的告别人狼 战斗中的Lunaria

二 スマガ和スマガスペシャル

这两个东西是正传和fan disc的关系,fan disc里是正传的补充和一个外传故事。スマガ的主题是“人生リベンジ”,人生重来。主角没有记忆,醒来时发现自己正从高空落下,落到一个魔女拼死战斗阻止恶魔毁灭世界的地方(某悲剧魔法少女的世界设定应该就是从这里起源的,反正是社内流用)。为了拯救所有的人,主角经历了许多的死去和重来,经历了十段各不相同的故事,最终用奇迹拯救了隐藏在世界背后的根源。讲“重来”的作品有很多,スマガ仍然有不少新意:周围的人知道主角可以重来时产生的隔阂,“世界的真相”本身的变动,等等。

“世界的真相”本身的变动是因为スマガ的另一个主题:“信念改变世界”。这是一个很有趣同时有一点哲学意味的话题。很多作品都会讲这个,不过スマガ是把它明文写入世界构造的,于是也有了很多有意思的展开。

スマガ是以“很长”著称的。十段故事,自然短不了。不过,会觉得它长,其实也是因为中后段故事显得有点失控。虽然前半段非常精彩,虽然游戏里的人物都说“没有理由就一切顺心的好结局,怎么会有呢”,但是后面剧情的处理还是有不够充分的嫌疑。好吧,其实N+社写长故事时最后有点虚也是传统了。

坠落的Spica战斗中的SpicaAlided沖姫々

三 アザナエル

几乎每个人看到《アザナエル》时的反应都是一样的:哎,这不是428吗?下倉バイオ敢于挑战殿堂作品的招牌游戏结构,胆子是真的不小。不过实际读过之后,就不得不承认他确实有这个底气。

《アザナエル》主要的看点和428类似,也是多个人物命运的交织。但是它和428显然不一样!你没有那么多影响世界发展的选项要选,也不用在一个人物故事停顿的时候想尽办法去别人线里解锁。所有要做的就是读了这个人的故事,再读那个人的。《アザナエル》强调的不再是蝴蝶效应,而是人和人之间的联系和冲突。全程只有几次关键点会影响故事走向,一周目就是多线切换毫无犹豫地狂奔到底,二周目下倉バイオ才允许你控制事件探索其他的可能性。

顺便一提,这又是一部把著名童谣《かごめかごめ》拿来用的作品,这也又是一部以秋叶原为舞台的作品。亲切死我了……

沙紅羅恵那千秋似鳥+ノーコ

四 君と彼女と彼女の恋。

是的,游戏标题里有个句号。其实我也是过了挺久才发现的。这部2013年的作品虽然不长,却可谓下倉バイオ行为艺术的巅峰。从整体构思到各种细枝末节,充满了各种让人耳目一新的内容。

首先主题就很震撼:现实和游戏的区别在哪里?而这部游戏所实践的,就是让你的游戏历程与真实的人生相似,独特而不可重复。围绕这个主题,这部作品演出了奇妙的故事。在剧情进展的某些阶段,存档会被清空,数据会被改写,游戏界面会变得截然不同。而在你到达结局的那一刻,你所安装的游戏中就只剩下和你选择的女主角相关的内容了,再也找不到另一个人的痕迹。

当然,这仍然不是现实。这部游戏本身就会对你说“如果你真的卸载了重装或是备份存档,我也无法阻拦”。但是,这是一种审美态度。当你经历完整个故事,而且发现它真的从细节上很大程度上做到了为你而独一无二的时候,你真的打算删档重装吗?我没有这样选择。打这部游戏,我久违地感到震撼。不仅是因为游戏只能来一次,也是因为它让我感到,它的质问是认真地对现实的人生发出的,它的回答也是认真地对现实的人生做出的。

美雪打击 世界改变美雪悬疑totono通关后