关于RPM引擎
kao0312
编辑于 2023年11月12日 08:10

引擎介绍

极其冷门的一个引擎,而且由于“RPM”有其它含义(Revolutions Per Minute 和 Requests Per Minute  之类的),直接在网上搜索很难得到相关的引擎信息。

使用该引擎游戏:

[だんでらいおん]たいせつなきみのために、ぼくにできるいちばんのこと

游戏目录结构:

代码块
JavaScript
自动换行
复制代码
[だんでらいおん]たいせつなきみのために、ぼくにできるいちばんのこと
├── Taikimi.exe
├── autorun.inf
├── bg.arc
├── bgm.arc
├── dat.arc
├── effect.arc
├── ev1.arc
├── ev2.arc
├── instdata.arc
├── movie
│   ├── Thumbs.db
│   ├── ed01movie.bmp
│   ├── ed02movie.bmp
│   └── opmovie.mpg
├── msg.arc
├── save
├── se.arc
├── std.arc
├── sys
│   ├── kidoku.dat
│   └── option.dat
├── sys.arc
├── system.arc
└── vo.arc

3 个目录, 21 个文件
复制成功

解包与封包

Garbro和Crass都能解包,但是不支持封包。

封包需要使用ssynn大佬的工具:https://github.com/ssynn/game_translation

但是每个游戏的密钥都不同,一个游戏的所有.arc文件共用同一个密钥。

可以用这个Python脚本猜出密钥:

代码块
Python
自动换行
复制代码
def find_key(sample):
    sample_len = len(sample)
    key_len = 2
    key = sample[-key_len:]
    while key_len * 2 < sample_len and sample[-key_len*2:-key_len] != key:
        key_len += 1
        key = sample[-key_len:]
    if key_len * 2 >= sample_len:
        return None
    offset = key_len - sample_len % key_len
    key_string = "".join(chr((256 - key[(i + offset) % key_len]) % 256) for i in range(key_len))
    return key_string

if __name__ == "__main__":
    arc_file_path = input("Enter the *.arc file: ")
    with open(arc_file_path, "rb") as f:
        f.seek(8)
        first_entry = f.read(32)
    key = find_key(first_entry)
    print(f"Guessed key: \"{key}\"")
复制成功

将输出的密钥(对于本游戏密钥为字符串“tair”)填入public_function.py的repack_arc函数的参数里,待封包的文件放在output目录下即可封包。

目前在第4016行:https://github.com/ssynn/game_translation/blob/cc06e998b7055865ee05b7f6db0804bbaecd37d2/src/public_function.py#L4016C9-L4016C19

文本提取与写入

游戏文本在文件msg.arc内,先用Garbro解包。得到一大堆jis编码的txt剧本文件。但是注意其中有一个文件“ver.txt”是以二进制编码的,记录了这个.arc文件的信息,后续提取文本的操作时应该跳过该文件,封包时和其他文件一起封包。

使用SExtractor,BIN引擎,纯文本正则模式,设置界面勾选生成UniversalInjector的JIS替换配置。

游戏文本编码为cp932,因此封包后还需要修编exe文件的编码范围和字符集。

字符集修改并没有完全通用的方法,因此这里使用JIS替换,不需要修改字符集。

对于本游戏的正则表达式:

代码块
Python
自动换行
复制代码
01_skip=^\/\/|^[\[<]
02_skip=^[ -~]*$
11_search=^【(.+)】$
12_search=^([^<>]+)<.+>$
13_search=^(.+)$
复制成功

可以先删除最后两条正则提取出人名制作人名字典协助翻译。然后再全部提取。

本游戏的人名字典:

代码块
JavaScript
自动换行
复制代码
{
   "トキ子": "时子",
   "すず": "铃",
   "アカネ": "茜",
   "ひな子": "雏子"
	}
复制成功

BIN引擎默认以cp932读取,gbk写入。但是有的字符在gbk编码里没有,比如本游戏对话中出现的“♪”,需要替换成其它字符(比如“~”)或者直接删掉。对于其它游戏,可能还有♡、・等。

翻译完后按上面的方法封包。

修改字符集

用od或者ida修改,因为只需要修改一个函数,直接静态分析,这里使用ida。

搜索CreateFontIndirectA函数,找到CharSet参数,把后面的1(1代表默认,有的游戏是80)改成86,保存。

IDA Pro

JIS替换

将生成UniversalInjector的JIS替换配置文件uif_config.json和相关dll文件(本游戏为dxgi.dll)放在游戏根目录下。

dll文件在SExtractor\tools\UniversalInjectorFramework目录下,具体使用哪个dll取决于具体游戏,懒的话可以全部复制。

原项目地址:https://github.com/AtomCrafty/UniversalInjectorFramework

顺带一提,对于本游戏默认中文字体太细影响观看体验。因此略微修改了生成的uif_config.json文件。启用font_manager,并将override_face的值改为了较粗的SimHei。

效果展示