PC88游戏简体补丁制作流程(自用记录)

该话题被推 个人随笔 其它
49
瑜瑜
瑜瑜

707

PC-88 简体补丁制作流程记录

好像全网都没人发过相关内容,也许是独一份(

仅用作个人制作记录。以下为正文内容。

最初,好 homi 推荐了几个 PC-88 的老游戏。我看了一下,感觉还挺有意思,就打算尝试制作。本来以为流程和 PC-98 差不多,结果还是比想象中复杂了一点。(musume_08)

具体思路和现在的 Galgame 补丁制作差不多:

text
解包 -> 提文本 -> 文本注回 -> 封包

到了 PC-98/88,则变成:

text
拆碟 -> 提文本 -> 文本注回 -> 封包

除此之外,还需要额外处理字库。

1. 拆碟

一般来说,只要游戏厂商没有自己整一些妙妙东西,使用的都是标准的 D88、NFD、FDI 等格式,自己找工具拆就可以了。

另外,还有一些比较高端的厂商会做出自己的改动,使用自定义轨道布局。这种情况就需要自行分析,elf 的《FOX》就是一个例子。

具体程序相关的内容不在这里展开,只说明整体思路。

2. 文本提取与注回

这里有两个思路。

一种是老老实实地逆文本 OP,计算偏移,然后进行提取和注回。

另一种就是爆提截断。

截断是什么意思?顾名思义,就是原来的文本占用多少字节,注入回去的文本依旧占用多少字节。这样一来,对于源文件而言,就只是替换了内容,并没有改变任何原本的布局。

当然,缺点是有时译文句子过长,最后可能导致话说到一半就没了。

总之,爆提截断就是处理陌生文本格式的最优方案(大雾)。

3. 字库

这是最关键的部分。毕竟前面两步做完以后,最终还是要让游戏能够正常显示文本,所以字库重建是不可缺少的一环。

如果玩过 PC-88 的游戏,应该对 FONT.ROMKANJI1.ROM 比较熟悉。作为模拟器中常见的字库文件,它们承载了许多游戏的文字显示内容。

简单来说,FONT.ROM 是 PC-88 前期一些纯片假名游戏使用的字库,每个槽位大小为 8×8,这也是为什么看上去比较费眼睛。

下图是渲染后的样子:

KANJI1.ROM 则是包含平假名、片假名和汉字的字库,渲染如下:

3.1 KANJI1.ROM 的基本结构

先说相对简单的 KANJI1.ROM 修改。

要修改字库,自然不可能人工用 PS 一个字一个字地改,肯定要交给程序处理。在此之前,先说明几个相关术语。

  • glyph:ROM 文件中的连续字模槽编号。

  • ROM address:PC-8801 字库硬件使用的地址。

  • file byte offsetKANJI1.ROM 文件中的实际位置。

有了这些概念之后,自然就需要对应的计算公式。

先不考虑 JIS 映射,只看一个字在 ROM 中是如何保存的。

一个汉字的单行宽度为 16 像素,即 16 bits。因为 1 Byte = 8 bits,所以每行占用:

text
16 / 8 = 2 Bytes

在 16×16 点阵中,单字行高为 16,因此单个字模的总字节数为:

text
16 × 2 = 32 Bytes

32(十进制)= 0x20(十六进制)

字库中的字模是连续存储的。glyph 从 0 开始递增,每个字占用固定的 0x20 字节空间。

假设 y 从 0 开始计数,那么第 y 行之前已经有 y 行,每行占用 2 Bytes,因此:

text
row_offset = y × 2

再看横坐标 x

  • 0 <= x <= 7:位于当前行的左半边。

  • 8 <= x <= 15:位于当前行的右半边。

因此可以得到:

text
side = floor(x / 8)
     = x >> 3

将这些关系组合起来:

text
offset = glyph × 0x20 + y × 2 + (x >> 3)

复杂的部分位于后续的映射:

text
JIS code -> ROM address / glyph

上述内容只是在说明:已经知道 glyph 时,如何找到 16×16 字模中的某一个像素。

完整的定位公式为:

text
base        = glyph × 0x20
byte_offset = base + y × 2 + (x >> 3)
mask        = 0x80 >> (x & 7)

约束条件为:

text
0 <= x < 16
0 <= y < 16

3.2 ROM address、glyph 与文件偏移

下面进入映射设计环节。

从给出的 KANJI1.ROM 渲染图中,大致可以划分出两个区域:

  • 非汉字区域:标点、数字、拉丁字母等。

  • 汉字区域:常用汉字。

我们要修改的就是汉字区域中的一部分。

一个 ROM address 对应一行 16-bit 点阵数据,也就是文件中的两个字节。一个字有 16 行,因此一个完整字模占用连续 16 个 ROM address。

ROM address 和 glyph 的关系为:

text
ROM address = glyph << 4
glyph       = ROM address >> 4

左移四位相当于乘以 16,因为每个字模占用 16 个行地址。

file byte offset 是打开 KANJI1.ROM 后,真正用于数组索引或者文件定位的字节位置。由于一个 ROM address 对应两个文件字节:

text
file byte offset = ROM address × 2
                 = ROM address << 1

结合前面的关系:

text
file byte offset = (glyph << 4) << 1
                 = glyph << 5
                 = glyph × 0x20

由此可以得到完整的换算关系:

text
ROM address = glyph << 4
glyph       = ROM address >> 4
file offset = ROM address << 1
file offset = glyph << 5
file offset = glyph × 0x20

3.3 JIS 区点码

JIS X 0208 使用“区、点”描述字符位置:

text
区号 = JIS 高字节 - 0x20
点号 = JIS 低字节 - 0x20

JIS = ((区号 + 0x20) << 8) | (点号 + 0x20)

例如:

text
JIS 0x3021

区号 = 0x30 - 0x20 = 16
点号 = 0x21 - 0x20 = 1

所以,0x3021 就是 JIS 第 16 区第 1 点,对应第一水准汉字“亜”。

第一水准汉字位于 JIS 第 16~47 区:

text
第 16 区:0x3021~0x307E
第 17 区:0x3121~0x317E
...
第 46 区:0x4E21~0x4E7E
第 47 区:0x4F21~0x4F53

前 31 区每区最多有 94 个字符,最后一区有 51 个字符:

text
31 × 94 + 51 = 2965
第一水准汉字使用以下公式:
text
rom_address =
    ((jis & 0x0060) << 9)
    | ((jis & 0x1F00) << 1)
    | ((jis & 0x001F) << 4)

使用 JIS 0x3021 代入第一水准地址公式,可以得到:

text
ROM address = 0x6010

换算成 glyph:

text
glyph = 0x6010 >> 4
      = 0x0601
      = 1537

换算成文件字节偏移:

text
file byte offset = 0x6010 << 1
                 = 0xC020

因此,该字模占用:

text
ROM address:0x6010~0x601F
glyph index:0x0601
文件范围:0xC020~0xC03F

如果已经知道字模基准 ROM address = A,那么像素 (x, y) 所在的文件字节为:

text
byte_offset = A × 2 + y × 2 + (x >> 3)
bit_mask    = 0x80 >> (x & 7)

例如:

text
A = 0x6010
x = 12
y = 5

计算字模起点:

text
字模起点 = 0x6010 × 2
         = 0xC020

计算行内偏移:

text
行内偏移 = 5 × 2
         = 0x0A

计算左右半边:

text
左右半边 = 12 >> 3
         = 1

得到文件字节位置:

text
byte_offset = 0xC020 + 0x0A + 1
            = 0xC02B

计算像素对应的 bit:

text
bit_mask = 0x80 >> (12 & 7)
         = 0x80 >> 4
         = 0x08

所以,该像素位于文件字节 0xC02B0x08 位。

3.4 动态映射

了解这些计算之后,就可以正式进入动态映射的设计。

区别于现在常用的日繁固定映射表,PC-88 更多时候可以选择动态映射。

何为动态?顾名思义,就是不硬性规定一个固定映射关系,例如“你 -> 凛”,而是选择借用一个游戏能够显示、但当前文本没有使用的日文编码槽。然后,将这个槽中的日文字模替换成中文字模,同时把译文中的中文字符编码成该槽原来的 CP932 双字节编码。

例如,假设中文“简”无法用 CP932 表示,但找到了一个当前文本未使用的日文载体字符“鰯”。

首先进行以下换算:

text
载体字符“鰯”
-> CP932
-> JIS code
-> ROM address

文本注入时,游戏得到的是“鰯”对应的 CP932 编码。游戏随后按照正常流程计算“鰯”对应的 ROM address,并从 KANJI1.ROM 中读取该槽位的字形。

但是,由于我们已经将这个槽位中的字模重绘成了“简”,所以游戏最终显示出来的就是“简”。

对于那种纯片假地狱的 PC-88 游戏,处理起来就比较麻烦了。因为那些游戏制作时还没有 KANJI1.ROM 镜像,程序自然也不会有主动读取 KANJI1.ROM 的对应逻辑。此外,还涉及从原本的 8×8 点阵片假名转换成 16×16 汉字,相当麻烦,所以这里不做细致展开。具体分析做起来和新做一个游戏没什么区别,感觉不如直接移植()。

示例

PC-88《トリロジー 九鬼妖華真伝》


PC-88 随机屋随机屋名作 again

本文版权遵循 CC BY-NC 协议 本站版权政策

(。>︿<。) 已经一滴回复都不剩了哦~