SFC中文化經驗談(四)—反組譯/概念篇— - 模擬器

By Charlie
at 2012-11-24T16:34
at 2012-11-24T16:34
Table of Contents
【雜言】
我有點後悔寫這系列文章了....比我預想的還難寫(抱頭
先說明一下,因為我不可能從計算機概論或寫編譯器談起,
所以這系列的經驗談是假定讀者有資工背景或具基本程式能力。
反組譯這邊則最好有修過組合語言或系統程式這類課程,具備基本概念即可。
一般來說,資工大三~大四學生應該就具備充足知識了.....。
之前三篇中提到的工具我並沒有解釋如何使用,因為那是自己花時間摸一下就會用的。
但這篇的反組譯模擬器我會稍微提一下用法,因為這邊有些"經驗上"的用法。
【正文】
~PART I. Rom、Ram、VRam~
跟中文化有關係的SFC記憶體是Rom、Ram跟VRam。
Rom應該都知道了,就是包含程式與資料的遊戲"軟體"部分
(實際是存放在卡匣硬體上,這點就跟存放在光碟中的軟體一樣)。
Ram則是用來放置遊戲記錄與暫存資料的地方,
SFC會用7E:0000~7F:FFFF這個特殊SFC位址來代表Ram裡的資料位置。
所以SFC的金手指碼"基本上"都是7E、7F開頭,就是為了處理Ram裡的數值。
我們也隨時可以用snes9x v1.43.ep9r8將遊戲目前Ram的內容dump出來。
http://0rz.tw/cT4l6 (第一個紅框應該在Dump Ram,不是Dump Palette,我畫錯了)
VRam則是存放顯示在螢幕上的內容。
根據遊戲設計不同,最多會有4個圖層與一些sprites....。
(如果是用Snes9X的玩家,在遊戲中按數字鍵1~5就能看出圖層來)
VRam在每個圖層上要顯示的東西都會分成兩部分:
(1)8x8為單位的圖塊(tile):
依模式不同每個點有可能是2bits~4bits(4色~16色)顯示。
基本上這些就像一塊塊可重複使用的拼圖。
(2)每個位置要使用的圖塊編號:
螢幕上(嚴格講是某圖層或sprites)由左到右、由上到下依序使用的圖塊編號。
相當於是指明螢幕上(嚴格講是某圖層或sprites)每個地方要用什麼圖塊。
(以前做的示意圖 http://0rz.tw/NgBGU )
就結果而言,要看到螢幕上有什麼字就代表VRam裡有該文字的圖塊,
並在要顯示的位置給圖塊編號(不是字庫代碼)。
這些圖塊即便是每個點以2bits(4色)顯示的8x8最小文字圖,
也要用到16 bytes的連續資料來顯示。因此遊戲時常會用DMA的方式,
直接把未壓縮的連續文字圖資料一口氣在Rom、Ram、VRam間搬運,提升處理速度。
(DMA的寫法很重要,以後會用實例解釋,現在先有個印象就好)
以流程來說,要完成文字的顯示工作,我"看過還有印象的"主要有4種方式:
(1)Rom --(解壓、挑字)--> Ram --(整個字串)--> VRam
這可能使用在"使用字庫較大,並顯示名詞文本"的情況下。
因為名詞長度一般了不起8個字,但因為字庫太大,
所以先在Rom裡依使用順序挑出字圖,複製到Ram裡成為字串圖,
再把Ram裡整個字串圖複製到VRam顯示出來。
若是有壓縮的字圖就要先解壓,再一個一個byte放進Ram裡,
若是未壓縮的字圖就DMA直接整個字圖傳進Ram裡,效率相對較高。
有時劇情文字也會用這種方式處理。
(2)Rom --(解壓、挑字)--> Ram --(挑字)--> VRam
這可能使用在"使用字庫較大,並顯示劇情文本"的情況下。
在Ram裡排成字串圖會發生的問題就是...有些字會重複被使用。
因為Ram裡還要放一堆其他圖像、數據,所以其實沒太多空間浪費。
如果一段對話很長,這時可以把會用到的字圖挑"一份"到Ram裡,
再根據劇情代碼從這個"Ram裡的小字庫"挑字圖到VRam顯示出來。
有時非字庫的文字圖也會用這種方式處理,例如皇騎1的地圖名稱。
(3)Rom --(挑字)--> VRam
這相當於是上面的簡化版,直接從Rom裡依序挑字到VRam。
但可能因為速度慢(從Rom挑越多字越慢)或因為文字圖必須未經壓縮,
感覺不常被使用在劇情文字。可能用在指令名稱等顯示在固定位置的內容。
(4)Rom --(解壓、整個字庫)--> Ram --(挑字)--> VRam
這可能使用在字庫夠小的情況,特別是遊戲只有使用平、片假名,沒幾個漢字時,
可以輕鬆地把整個字庫全塞到Ram裡。之後只要看文本代碼用到哪些字,
再依序拷貝到VRam就行,對遊戲而言可說是最方便的了.....
.....然後做中文化的就淚目了T____T
如果看到某些SFC漢化遊戲改不出"只有平、片假名的小字庫",那可能卡這邊了。
(或是代碼數量不足等其他原因.....也可能只是單純反組譯不熟 XD)
因為這部份我不會提出實例(解釋不完),所以我把我在皇騎1的處理概念解釋一下:
<i>想辦法拆成對應不同條件的小字庫:
例如皇騎1戰鬥時可以切換成有動畫或無動畫,攻擊名稱會因而有所差別。
所以我可以把所有攻擊名稱會用到的中文字,重新分成兩個不同小字庫,
各別對應有動畫跟無動畫兩種情形。
這樣只要在原架構下,寫段函式去判斷這時是有動畫或無動畫,
再依情況載入不同的小字庫就行。
缺點是....拆出來的小字庫都要小於原字庫,且可能發生代碼重複使用的問題,
也就是人名、道具名等需要統一代碼的地方不適合使用。
但如果只是指令名或是人名、道具名不多的情況就還蠻好用的。
<ii>簡單說.....就是想辦法硬幹成(1):
例如皇騎1的狀態畫面有可能顯示上千種不同人名、道具名、職業名....
字庫最少要700個中文字,而且也沒額外條件好拆字庫時.....就只好吐血吧。
先在Ram裡找一塊沒被使用的空間,寫個函式把當下用到的中文字圖
依序寫到前述的空間,另外要同時計算目前寫了幾個字圖到Ram裡。
寫完後再把所有的字圖依序複製成為VRam裡特定位置的圖塊,
並將原本顯示日文的位置所對應的編號,固定成特定位置開始的連續編號。
這樣不管字庫多大,理論上都能"動態地"從Rom裡挑字圖到Ram,
再從Ram複製字串到"固定的"VRam位置。
光是要讓皇騎1狀態畫面不受限地顯示中文,我就多加了10幾個函式。
因為這邊其實還同時併發8x8顯示與代碼數量不足這兩個問題。
不然我通常將一個地方改中文化,只需要加1~3個函式,這裡真的改太大....
~Part II. 反組譯log檔~
所謂的反組譯,簡單來說就是把0與1機械碼實際運作的過程(機器看得懂,人看不懂),
改用較高階、精簡的指令集與16進位數字表現出來(也就是組合語言)。
相對於程式碼是給人看、機械碼是給電腦看,組合語言就介於中間。
因為每行都相當於是機器硬體上的一個簡單動作,所以會比高階程式語言瑣碎得多。
我們先來看看怎麼用snes9x v1.43.ep9r8將遊戲中的運作過程dump出來。
載入Rom檔後,按下右邊debuger介面中的Run鍵,開始遊戲。
根據需求上的不同,有兩種方式決定何時開始dump:
(1)不知道資料在Rom裡位址的情況:
例如,當我想把ロシュフォル教会改成中文,但卻找不到這些字在Rom的哪裡時,
可以在文字出現前的畫面,先去右邊debuger介面勾選Loggin下的cpu欄,
再把滑鼠移到遊戲視窗,立即按鍵顯示出目標"ロシュフォル教会"。
再馬上取消掉debuger介面中cpu欄的勾選,就會dump出勾選其間的組語代碼,
(上述動作越快越好,因為一秒就會dump出幾十MB的組語代碼.....)
並存在與Rom同一資料夾下的"Rom檔名0000.log"。
http://0rz.tw/hgpPL
在這種情況下要知道確切的相關程式開始位置比較麻煩,
如果知道現在顯示文字的代碼,可以用這代碼在log檔中搜尋。
如果不知道可以搜尋何時有DMA的動作,或是觀察VRam中的變化,
再反推相關程式的開始位置(以後會給實例說明)。
(2)知道資料在Rom裡位址的情況:
按下介面中的Breakpoints鍵,貼上位址並勾選3個中斷時機,
當遊戲對該位址資料進行讀寫時就會自動中斷停止,
這時按下Step Over鍵就會開始一行一行執行指令。
http://0rz.tw/yLJAr
配合(1)用UltraEdit搜尋這幾行指令在log檔的位置,
就知道相關程式從何處開始,不用在幾十MB的組語代碼中大海撈針。
這時我們終於得到log檔,甚至知道程式大概從哪裡開始。
把(1)中例子的log檔打開來看,即使在我緩慢的準系統上只跑了一秒,
就產生了20MB的龐大log資料,每個欄位代表的意義如下:
(省略後面不重要的部分,"無視"那欄的暫存器我沒遇過要改的情況,故...無視)
SFC位址 機器碼 組語代碼 A暫存 X暫存 Y暫存 無視 bank
======== ======== ===================== ====== ====== ====== ====== =====
$00/BC7F E8 INX A:FF01 X:001C Y:5555 D:0000 DB:00
$00/BC80 E8 INX A:FF01 X:001D Y:5555 D:0000 DB:00
$00/BC81 E0 28 00 CPX #$0028 A:FF01 X:001E Y:5555 D:0000 DB:00
$00/BC84 90 E2 BCC $E2 [$BC68] A:FF01 X:001E Y:5555 D:0000 DB:00
$00/BC68 DA PHX A:FF01 X:001E Y:5555 D:0000 DB:00
$00/BC69 20 8C BC JSR $BC8C [$00:BC8C] A:FF01 X:001E Y:5555 D:0000 DB:00
$00/BC8C 86 1C STX $1C [$00:001C] A:FF01 X:001E Y:5555 D:0000 DB:00
$00/BC8E A9 01 LDA #$01 A:FF01 X:001E Y:5555 D:0000 DB:00
$00/BC90 8D 5E 16 STA $165E [$00:165E] A:FF01 X:001E Y:5555 D:0000 DB:00
.....(略)
SFC位址: 目前的指令位址$oo/xxxx,oo是第oo個bank,xxxx是該bank裡的位址。
機器碼: 實際執行的二進位指令碼,只是在此以16進位表示比較容易看。
一個基本的SFC指令會包含1~4個bytes,第1個byte一定是指令本身的代號。
第2~4個byte(如果有的話)都是資料內容。
組語代碼:即使機器碼用16進位表示也只是"變短",很難記住這機器碼對應什麼指令。
所以需要用組合語言的方式來簡單表示指令與資料內容。
我隨便拿上面例子來解釋,例如....
‧INX 表示X暫存器的值加1。
‧CPX #$0028 表示把X暫存器的值跟數字0x0028做比較
(#$表示資料就是"數字",只有$的話表示資料是"位址內的值")。
‧BCC $E2 表示前面比較結果是"小於"的話,移動0xe2的距離。
因為讀了2個bytes,所以現在程式指標位址是0x00bc84 + 2 = 0x00bc86。
移動距離是0xe2-0x100(0xe2是負數)=向前移0x1e,
也就是移到0x00bc86-0x1e=0x00bc68的位址。
如果前面比較結果是"大於等於"的話,就略過繼續處理0x00bc86的指令。
[$BC68]是反組譯器提供的額外資訊,表示這邊程式指標跳到0x00bc68。
‧PHX 表示把X值放到堆疊(stack)中。
‧JSR $BC8C 表示跳到同一個bank中,位址0xbc8c的位址。
後面[ ]的內容一樣是反組譯器提供的額外資訊....
‧STX $1C 表示把X暫存器的內容存到同bank中,位址0x001c的位址。
‧LDA #$01 表示把數值0x01載入A暫存器中。
‧STA $165E 表示把A暫存器中的內容存到同bank中,位址0x165e的位址。
熟悉的話,只要看到組語代碼就能知道每行在幹什麼。
不熟悉的話可以參考我第一篇提到的網頁,裡面有所有指令代碼的解釋。
A暫存: SFC主要有3個2 bytes暫存器:A、X跟Y。
A暫存器是功能較強的,可以做加減、AND、OR、shift、比大小等工作。
大部分數學運算會在A暫存器裡完成。
X、Y暫存:這兩個暫存器比較算輔助性質,只能做"加1"等少數極基本功能。
經常用來輔助A暫存器,完成一些較複雜的計算或資料搬運功能。
bank值: 之前有說過程式執行時不太會切換bank,如此可以提升程式執行效率。
從 JSR $BC8C 這例子可以看出來,即使實際要跳過去的位址是0x00bc8c,
最前面00這個bank值卻被省略了,連帶也有效節省了指令長度 。
===========================================================================
....先寫到這邊吧,下週再給實際的中文化例子。
看看如何解讀log檔,並且加上新的函式來突破原本程式的限制。
中間有些概念如果有錯的話,也隨時歡迎指正。
--
Tags:
模擬器
All Comments

By Hardy
at 2012-11-28T13:52
at 2012-11-28T13:52

By Tristan Cohan
at 2012-12-02T05:33
at 2012-12-02T05:33

By Queena
at 2012-12-03T06:56
at 2012-12-03T06:56

By Agnes
at 2012-12-04T22:40
at 2012-12-04T22:40

By Bethany
at 2012-12-06T06:40
at 2012-12-06T06:40

By James
at 2012-12-06T22:32
at 2012-12-06T22:32

By Ula
at 2012-12-11T05:41
at 2012-12-11T05:41

By Franklin
at 2012-12-15T06:40
at 2012-12-15T06:40

By Charlie
at 2012-12-17T23:13
at 2012-12-17T23:13

By Selena
at 2012-12-21T12:39
at 2012-12-21T12:39

By Michael
at 2012-12-21T20:19
at 2012-12-21T20:19

By David
at 2012-12-23T05:00
at 2012-12-23T05:00
Related Posts
復活邪神3繁體漢化版 incubus大簡易版

By Ivy
at 2012-11-24T08:50
at 2012-11-24T08:50
問一款老舊的FC遊戲

By Mason
at 2012-11-23T23:59
at 2012-11-23T23:59
一款電腦遊戲

By Zanna
at 2012-11-23T21:13
at 2012-11-23T21:13
益智方塊類遊戲

By Ivy
at 2012-11-23T20:49
at 2012-11-23T20:49
你覺得最難的遊戲?

By Victoria
at 2012-11-23T14:23
at 2012-11-23T14:23