news 2026/10/16 1:36:43

d2r-mobile 物品注释系统指南:自备 oldnames.tsv 与 comments.tsv 定制暗金 / 套装 / 符文之语译名与点评

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
d2r-mobile 物品注释系统指南:自备 oldnames.tsv 与 comments.tsv 定制暗金 / 套装 / 符文之语译名与点评

【免费下载链接】d2r-mobile

Open-source iOS / macOS compatibility layer for Diablo II: Resurrected with the Reign of the Warlock expansion. Runs your own Switch copy natively on iPhone, iPad and Mac — full touch controls, 60 fps, new endgame modes. Bring your own game files. 开源的《暗黑破坏神 II:狱火重生》术士君临 iOS / macOS 兼容层

项目地址:https://gitcode.com/gh_mirrors/d2/d2r-mobile
点击查看免费下载

本篇技术指南面向《暗黑破坏神 II:狱火重生》iOS / macOS 兼容层项目 d2r-mobile,围绕 dm/ios/tools/annot/README.zh-CN.md 所定义的「物品注释用表」展开:讲解两张可选数据表(oldnames.tsv旧译名对照表、comments.tsv点评表)的格式、字段语义、启用方式与安全校验机制,并深入到 annot_strings.py 与 gen_romfs_overlay.py 的源码实现,说明它们在 RomFS 覆盖层流水线中的实际工作方式。读完你将掌握:如何自备译名表把游戏名「换回旧译名」、如何自备点评表给暗金 / 套装 / 符文之语加一句点评,以及为什么「原文校验」能保证游戏换版本时绝不会换错。

背景:一套挂在字符串表上的注释系统

d2r-mobile 通过 RomFS 覆盖层(overlay)直接改写 Switch 版游戏资源,在不动游戏逻辑的前提下实现界面与文本的定制。物品注释(annotations)正是这条流水线中的一环:它把暗金、套装、符文之语的名字、点评、可变属性范围、符文序号、底材档次、区域等级、神殿效果、掉落名等信息,全部以「改写游戏字符串文件(data/local/lng/strings/下的.json)」的方式注入游戏。

这套系统全部由脚本实现,核心包括:

  • annot_strings.py:主入口,负责全部注释步骤的编排,也是两张表(oldnames.tsv/comments.tsv)的读取者;
  • annot_ranges.py:给暗金 / 套装 / 符文之语名称加可变属性范围;
  • annot_world.py:区域等级、神殿效果注释;
  • annot_drop.py:地上掉落名(药水、卷轴、宝石、符文、钥匙、底材的缩写 / 上色 / 加注);
  • gen_romfs_overlay.py:把脚本产物组装成可加载的覆盖层数据档。

两张用户可自备的表放在dm/ios/tools/annot/目录下,缺省不随公开仓库发布。没有它们时,除「换回旧译名」与「加点评」两步自动跳过外,其余注释照常生效。

两张表总览

文件内容是否进公开仓库
comments.tsv暗金 / 套装 / 符文之语的一句点评(按一个第三方模组的点评逐条改写的)不进。属于改编,公开仓库不带;没有这份表时不加点评,其余注释照常
oldnames.tsv旧译名对照表:把一部分名字换回老玩家熟悉的旧版中文译名不进。它是成套的他人译名,公开仓库不带;没有这份表时「换回旧译名」这一步自动不做,其余注释照常

两张表都是制表符分隔(Tab-separated)、UTF-8 编码、首行为表头的文本文件,把文件放进dm/ios/tools/annot/目录即可启用(构建脚本按路径annot/oldnames.tsv、annot/comments.tsv探测,见 annot_strings.py 与同文件comments()中os.path.join(HERE, 'annot', 'comments.tsv')的写法)。

自备旧译名对照表(oldnames.tsv)

表格式

文件 键 原文校验 旧译名 item-names KhalimFlail 2717cc0f (你要换成的名字)
  • 文件:游戏字符串文件名,不带.json。字符串文件位于游戏资源的data/local/lng/strings/下(源码中对应常量STR = '/lng/strings/',见 annot_strings.py)。例如item-names对应item-names.json。
  • 键:该文件里那一条的Key(字符串条目键名)。
  • 原文校验:这一条现行简体中文原文(UTF-8)的 SHA-1 前 8 位十六进制。构建时逐条核对,原文变了(游戏换了版本)会报错,免得换错。
  • 旧译名:换成的名字;要换行写\n。

原文校验:为什么表里不存原文

表里不保存游戏原文本身,只保存它的 SHA-1 前 8 位校验值。源码实现如下(annot_strings.py):

def text_hash(t): """原文校验:游戏现行文字的 SHA-1 前 8 位(表里不存原文,只存这个, 防原包换版本后照旧改)。""" return hashlib.sha1(t.encode('utf-8')).hexdigest()[:8]

old_names()在构建时逐行取出文件+.json对应的字符串表、按键键取到现行文本,先做两件事(annot_strings.py):

  1. 查键:s.get(f, k)返回None时报错「annot_strings:%s 里没有键 %s」——表里的键在游戏字符串表里找不到会直接中断构建;
  2. 核对原文:text_hash(cur) != row['原文校验']时报告「原文变了(校验 %s,现行「%s」)——原包换了版本,核过再改表」。

这意味着:你在表中看到的校验值 8 位十六进制是针对特定游戏版本的。如果日后游戏更新导致简体中文原文变动,构建会主动报错而不是默默替换,把「换错名字」的风险挡在产物生成之前。

键定位的底层机制

oldnames.tsv之所以能「按按键名定位、只改 zhCN 一列」,依赖 annot_strings.py 中Strings类的按键改写:

def _iter(self, name, key): self._load(name) return list(re.finditer(r'"Key":\s*"%s",.*?"zhCN":\s*("(?:[^"\\]|\\.)*")' % re.escape(key.replace('"', '\\"')), self.text[name], re.S))

它用正则定位"Key": "<key>"及其后第一个"zhCN": "..."的值,只替换 zhCN 列、保留原文件排版。值得注意的是set()的实现(annot_strings.py):同一文件里同键多处会一起改,返回修改处数;且_load()会先在已有替换结果(repl)之上续改,因此annot_strings与前面的touch_strings修改同一份文件时能自然叠加。

一个额外细节:物品名键可能出现在两份表里

annot_strings.py 专门说明:

# 物品名的键可能同时出现在 chinese-overlay.json(它后加载、同键覆盖),两处都改 ITEM_FILES = ('item-names.json', 'chinese-overlay.json')

游戏字符串表中存在后加载、同键覆盖的chinese-overlay.json,因此旧译名、点评、底材档次等步骤对物品名都是两处同改(ITEM_FILES两个文件都s.set),保证游戏显示层取到的是改后版本。

自备点评表(comments.tsv)

表格式

文件 键 名字颜色 点评 item-names (字符串的 Key) unique (你写的一句话)
  • 文件:item-names(暗金、套装件)或item-runes(符文之语)。
  • 键:该条字符串的Key;表里的键在游戏字符串表里找不到会报错。
  • 名字颜色:set= 套装(绿),其余按暗金(金)。
  • 点评:显示在名字下面的一行。里面提到的技能名要用换回旧译名之后的名字(如果你同时带了旧译名对照表)。

颜色码与显示行

comments()的实现(annot_strings.py)把点评拼进原名字串:

c, color = row['点评'], '\u00ffc2' if row['名字颜色'] == 'set' else '\u00ffc4'
  • ÿc2= 套装绿,ÿc4= 暗金 / 符文之语金;
  • 点评显示在名字下面,串的拼法是点评行\n<名字色>名字。游戏会把多行名字倒着画(最后一行在最上面),所以写进字符串表后实际呈现为「名字在上、点评在下」;
  • 若原名字串本身是多行('\n' in t),则add()会保留原来的行结构,把点评插到名字行之前、颜色行之间(annot_strings.py)。

对于文件 == 'item-runes'的符文之语,点评写入('item-runes.json', 'chinese-overlay.json');其他(暗金 / 套装件)写入ITEM_FILES两份。

点评里的技能名必须用换回后的旧译名

comments()内部有一道自检:从old_rows()(如果你同时带了oldnames.tsv)里收集被换名技能——文件 == 'skills'且键以skillname开头——建立「现行名 → 旧译名」映射,然后检查每条点评:

bad = [a for a in renamed if a in c and renamed[a] not in c] if bad: raise SystemExit('annot_strings:%s 的点评「%s」里有 3.3 现行技能名 %s(应写旧译名 %s)')

也就是说:点评文本中不许出现某个已换旧译名技能的现行 3.3 简体名,一旦出现构建即中断,并指出应当改写的旧译名。这样保证点评里提到的技能名与游戏内实际显示的(换回后的)名字一致。两个技能同名、只改了一个的情况(如「专注」)有专门豁免:换名后仍在使用中的名字不算错(annot_strings.py)。

完整流水线:各步骤如何协作

apply_all()把全部步骤按固定顺序作用在同一个Strings实例上(annot_strings.py):

return {'旧译名': old_names(s), '可变范围': ann_ranges(s, romfs), '点评': comments(s), '符文序号': rune_numbers(s), '底材档次': base_tiers(s, romfs), '区域等级': ann_levels(s, romfs), '神殿效果': ann_shrines(s, romfs), '掉落名': ann_drop(s, romfs)}

顺序是有讲究的:旧译名在前,后面的点评、符文序号、掉落名都挂在「换回后的名字」上;掉落名最后做(杂物类按规则表缩短 / 加色 / 加说明,在旧译名与符文序号之后)。build()在此基础上再出一份「文字框版」并生成dm_textbox_plain.tsv对照表(见下文)。

文字框版与宿主侧替换(dm_textbox.c)

普通文字框(TextBoxWidget)不认颜色码:游戏加载时把ÿc换成私用区字符U+E07E,文字框会把*5原样画出来、把换行画成空格(编年史列表真机实测)。因此注释流水线除「注释版」外再产出「文字框版」:同一流水线只做改名类步骤(旧译名、符文序号、底材档次、区域等级),不做装饰类步骤(可变范围行、点评行、神殿效果行、掉落名的缩写 / 颜色 / 说明行)。两份逐键对照后生成dm_textbox_plain.tsv,由宿主在文字框写字入口(观测门 14,dm_textbox.c)按表做整串 / 子串替换:

  • 样式里"options": { "pipeCharacterBehavior": "handle" }的文字框(聊天区那几行)认颜色码,原样不动;
  • 其余不认颜色码的(编年史列表等)按表换成文字框版;
  • 原版自带的串不在表里,一个字节都不动;注释开关关掉时覆盖层里没有这张表,门直接返回,所有文字框保持原样。

构建集成:开关与变体

gen_romfs_overlay.py 把物品注释作为「走覆盖层的自定义开关」接入构建:

FEATURES=[('item_notes','annot_strings')] # 物品注释与旧译名
  • 构建时按组合生成全部2^n份覆盖层:out本身 = 全关(宿主验收 / 触摸链用),其余组合写到out_variants/<键+键…>/data,各带features.txt;
  • annot_strings.build(romfs, temp, repl)在传入的已替换表repl之上继续改——同一文件多个特性都改时自然叠加;
  • 特性模块自带的旁路文件(不进 CASC 的,如物品注释的文字框对照表dm_textbox_plain.tsv)写在变体目录根上,宿主通过DM_ROMFS_OVERLAY找到它。

App 启动时按dm_custom_get挑一份变体,重启生效。也就是说:开启「物品注释」开关 = 启用item_notes变体 = 让annot_strings流水线跑一遍;而你要不要展示点评 / 换回旧译名,取决于annot/目录下是否放了comments.tsv/oldnames.tsv。

独立运行与验收

annot_strings.py支持脱离完整构建单独运行(annot_strings.py):

python3 annot_strings.py <RomFS> <输出目录>

它会打印统计字典({'旧译名': n, '可变范围': n, '点评': n, '符文序号': n, '底材档次': n, '区域等级': n, '神殿效果': n, '掉落名': n, '文字框对照': n})以及每个改写后文件的去向。没有旧译名表或点评表时,对应步骤打印提示后跳过:

annot_strings:没有旧译名对照表 <…>/annot/oldnames.tsv,不换旧译名(其余注释照常) annot_strings:没有点评表 <…>/annot/comments.tsv,不加点评(其余注释照常)

实操清单:如何自备并验证两张表

  1. 定位字符串文件:从游戏资源中解包data/local/lng/strings/下的item-names.json、item-runes.json、skills.json等(项目内 touch_strings.py 与 annot_strings.py 均通过build_manifest.psv+read_layout()按suffix='/lng/strings/' + 文件名读取)。
  2. 查键:找到要改的条目,抄下其Key与现行 zhCN 原文。校验值可用hashlib.sha1(原文.encode('utf-8')).hexdigest()[:8]计算。
  3. 填表:按上文格式写oldnames.tsv或comments.tsv,存到dm/ios/tools/annot/目录,UTF-8 编码、Tab 分隔、首行表头(旧译名表表头的列名是中文,保持原文:文件 键 原文校验 旧译名)。
  4. 构建验证:重新跑覆盖层构建,观察输出统计与错误信息。所有「找不到键」「原文变了」「点评含现行技能名」都会中断构建并给出具体条目,这是系统设计的主动安全阀——宁可报错,也不换错。
  5. 运行时验证:开启item_notes变体(App 自定义页开关,重启生效),在说明框(TooltipsPanel)与聊天区查看带色点评,在编年史列表等文字框处核对dm_textbox_plain.tsv的降级呈现。

边界与限制

  • 两张表不属于公开仓库的发布内容(分别为改编他人点评、成套他人译名),你需要自行取得或编写;公开仓库只提供格式与校验约定。
  • 校验值对版本敏感:若游戏更新导致简中原文变化,旧表会失效并报错,需要重新对表。
  • item-runes(符文之语)的点评键会同时写入item-runes.json与chinese-overlay.json;item-names(暗金 / 套装件)写入item-names.json与chinese-overlay.json。
  • 点评里的技能名必须使用换回旧译名之后的写法,否则构建被拦;如果你不带oldnames.tsv,则不受此约束(但游戏内技能名也是现行名,两相自洽)。

这套设计把「内容自备、机制内置」落得很干净:核心注释引擎、校验规则、构建集成全部开源,而文案本身由玩家自由替换,兼顾了合规与可玩性。

【免费下载链接】d2r-mobile

Open-source iOS / macOS compatibility layer for Diablo II: Resurrected with the Reign of the Warlock expansion. Runs your own Switch copy natively on iPhone, iPad and Mac — full touch controls, 60 fps, new endgame modes. Bring your own game files. 开源的《暗黑破坏神 II:狱火重生》术士君临 iOS / macOS 兼容层

项目地址:https://gitcode.com/gh_mirrors/d2/d2r-mobile
点击查看免费下载

相关推荐

上一篇:3分钟快速上手:如何使用rmats2sashimiplot进行RNA-seq数据可视化分析
下一篇:从快捷指令到Python代码:python-shortcuts文件格式转换完全指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/16 1:36:03

CSS transform: skew 深度解析:从数学原理到实战避坑指南

1. 从一次界面错位说起&#xff1a;为什么偏偏是skew前阵子帮一个做数据可视化的朋友调界面&#xff0c;他拿着屏幕截图问我&#xff1a;“为什么我这个卡片列表在鼠标悬停时&#xff0c;文字会跟着一起歪掉&#xff1f;我只想让背景斜切一下。”我一看代码&#xff0c;问题出在…

作者头像 李华
网站建设 2026/10/16 1:33:47

VxWorks下UDP通信编程实战:从Socket模板到踩坑优化

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/16 1:32:13

NegPy能做什么?这款开源胶片负片处理工具的完整介绍

【免费下载链接】NegPy Tool for processing film negatives. 项目地址&#xff1a; https://gitcode.com/gh_mirrors/ne/NegPy 点击查看 免费下载 NegPy 是一款免费开源的胶片负片处理工具&#xff0c;专为负片扫描而设计。它不只是简单地把负片反转成正片&#xff0c;而是用…

作者头像 李华
网站建设 2026/10/16 1:29:31

游戏商店与拍卖系统架构设计:客户端服务端分工与安全防护实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华