創世紀繁體中文化

六代六種做法

技術篇

選哪條路,取決於手上有什麼。有可用的原始碼就移植,有維護中的開源引擎就修改,兩者都沒有就反組譯原版,再自己寫一個引擎。

代手上有什麼做法主要風險
I開源重製 open_ultima,但只有世界地圖、基礎地牢和地面戰鬥接手後中文化,把缺的遊戲內容補完上游太早期,補完等於寫半個遊戲
II一個 Windows 移植版執行檔,沒有符號Ghidra 反編譯當行為參考,引擎手寫反編譯碼纏著 MFC,不能直接用
IIILairWare Mac 版的 MIT 原始碼用 SDL2 重建 Mac API,遊戲邏輯幾乎不改Classic Mac 程式的 32 位元假設
IV維護中的開源引擎 xu4掛在引擎的文字出口,載入時查表換成中文文字路徑不只一條
V沒有可用的引擎,只有原版IDA Pro 反組譯當真值,Go 從零重寫全部遊戲邏輯都要自己寫
VIScummVM 的 Nuvie 引擎給引擎加補丁,載入時查表換成中文對話虛擬機和 Big5 衝突

共同原則:不把中文寫回原版檔案

老遊戲的資料檔靠固定位移找字串。把一句英文換成長度不同的中文,後面的內容全部錯位。所以 II 到 VI 代都讓原版資料保持唯讀,譯文放在外部檔案,執行時再覆蓋上去。查不到譯文就退回原文,畫面不會缺字。

這樣做還有一個好處:專案可以公開程式碼和譯文,不必散布原版資料,玩家自備就好。

有原始碼:III 代用 SDL2 重建 Mac API

LairWare 的 Mac 版是 C 寫的,遊戲邏輯很完整,綁定 Mac 的部分集中在繪圖和視窗。專案在 src/compat/ 用 SDL2 重建 QuickDraw 和 Toolbox 的函式,上游程式幾乎不改就能編譯。

文字的路也很短:全遊戲的繪字都走 UDrawThemePascalString,換掉這一處,所有文字就都是中文。

代價是 Classic Mac 的舊習慣。上游大量使用 "\p..." 這種 Pascal 字串,要用 clang 的 -fpascal-strings 編譯。還有一個藏得很深的錯誤,寫在驗證那一篇。

有開源引擎:IV 代選 xu4

IV 代最初評估的是 u4remastered,比較後放棄:

u4remasteredxu4(採用)
技術C64 的 6502 組合語言,23,101 行C++、Allegro 5
平台只有 Commodore 64Linux、Windows、Mac 原生
文字單位元組,8×8 字元,每行 16 字可以 16 位元碼位
中文化要從零重寫引擎掛在文字出口即可

u4remastered 的 talk.json 修過數十個對白錯誤,後來拿來當翻譯底本。xu4 遊戲內的捲動文字都經過 screenMessageN,掛在這裡就涵蓋主要文字。麻煩在商店:中文住在 Boron 腳本裡,走另一條路徑,一連串錯誤直到實機測試才現形。

有引擎但不能改資料:VI 代的 Plan B

VI 代第一個想法是直接把 Big5 寫進對話腳本。這個虛擬機把 0xA1 到 0xFE 當作指令,而 Big5 的第一個位元組剛好落在同一段。試了六個判別方法,還是有解決不了的邊界情況。

Plan B 放棄改腳本。虛擬機照常跑英文,輸出前用英文原句查表,換成中文。不會有對齊問題,譯文長度也不受限。缺點是輸出路徑有好幾條,漏掉一條就漏出一處英文,最後用到八個掛鉤點。戰鬥訊息裡的人名是執行時才決定的,整句查不到,又加了片段替換表。

只有執行檔:II 代和 V 代

這兩代都把反編譯當對照來源,不照抄。專案裡稱它為 oracle,意思是拿來問「原版在這裡會怎麼做」的標準答案。

II 代的對象是 John Alderson 的 Windows Native Ultima II,約 46,000 行 Ghidra 反編譯結果,夾著 MFC 執行環境。真正有用的是演算法:亂數公式、戰鬥命中、地牢格式、行星座標。引擎另外用 SDL2 寫乾淨版。

V 代的規模最大。DOS 版是 16 位元,只能讀組合語言。FM Towns 版是 32 位元的 Phar Lap 格式,IDA 的 Hex-Rays 可以反編譯成 C,所以遊戲邏輯都從這一版讀。同一張光碟上還有日文和英文兩個執行檔,兩者的差異就是雙位元組文字路徑。

反編譯結果也不能全信。實際踩過三種錯誤:把可寫的全域變數當常數,整段函式被摺成一行;參數列是空的,函式被安靜截斷,後面的機制整個不見;把全域變數傳播成字面值,看起來像「只有第一格有效」。遇到這些形狀就要回去讀組合語言。

來源:u2-cht 工程文件、u3-cht 在地化筆記、u4-cht README、u4-cht 商店文字、u5-cht 專案憲章、u6-cht README。