FC 魂斗罗汉化日记 (第二集)
第二集 - 序章汉化
(目录)
首先搞懂剧情在说什么。这部分参考了魂斗罗剧情、百科以及英文版。
知道汉化的文字后,就需要修改程序代码了。
最初的中文实现想法是这样的:
- 两个字节告诉程序,哪个 PPU 需要切换到哪个贴图集,以及切换的序号。
- 中文字符,其中前两位为
10
,让程序知道这是中文字。
觉得可行之后,就想办法优化一下,让程序方便显示:
这样,每个中文字符就只需要占用一个字节。程序寻找的话,只需要左移两次 (等价于 ×2),然后依次 +1
就能得到需要显示的贴图了。
为了方便,糊了个简单的小程序辅助我:
然后就是依次导出位图,画图复制,然后使用 YY-CHR 导入到 Rom 内。
中文显示代码
参考大字汉化教程,使用 VirtuaNES Debug 下断点找到 CBB7
。这一条指令负责写出数据到 NamedTable。
根据上下文,得知程序会循环读取 $0700
偏移处的数据并显示,对其下写入断点,定位到 04:97FD
。
另外,切换 PPU 图案的函数在 07:FACE
,就是写出你想切换的 BANK 到 $07F0 + PPU
,然后调用这个函数就行了。
对话数据读取代码:
; ROM 位置: 012B78 (04:AB68)
; 原始函数: LoadNextChar
; LoadNextChar (01180D / 04:97FD): JMP AB68
.org $AB68
; LoadNextChar
; 04,AB68
; byte[0043] 等待帧数, 如果不是 0 则跳出
DEC $0043
BNE _END
LDA #$08
STA $0043
; byte [0046] = 当前对话文字指针偏移
LDA $0046
ASL ; Shift left
TAY
; WORD [9848] = 对话文字指针
LDA $9848,Y ; WORD: 0x4E 0x98 (984E)
STA $0002 ;
LDA $9849,Y ;
STA $0003 ;
; byte [0047] = 当前对话文字偏移
LDY $0047 ; Y = [0047] (01)
INC $0047 ; [0047]++
LDA ($02),Y ; A = _THE_CHR
; 控制符检测
CMP #$FF
BEQ _END_WITH_CARRY
CMP #$FE
BNE _SKIP_LINE_BREAK
; 换行符号
INC $0048 ; Y Position?
JSR _UpdatePosition
_READ_NEXT_CHAR:
LDY $0047
INC $0047
LDA ($02),Y
jmp _END_CONTROL_CHAR
_SKIP_LINE_BREAK:
CMP #$FD
BNE _SKIP_ENABLE_CHINESE
LDA #$01
STA $07EE
jmp _READ_NEXT_CHAR
_SKIP_ENABLE_CHINESE:
_END_CONTROL_CHAR:
STA $0008
CMP #$00
BEQ _SKIP_NO_SOUND
AND #$C0
CMP #$C0
BEQ _SKIP_NO_SOUND
LDA #$0E
JSR $F9BC
_SKIP_NO_SOUND:
JSR PutNextDialogChar
; 因为是中文, 到时候看情况是否需要加延迟。
;LDA $0008
;BPL _END
LDA #$0C
STA $0043
_END:
CLC
RTS
_END_WITH_CARRY:
; 清空中文标记
LDA #$00
STA $07EE
SEC
RTS
PutNextDialogChar:
LDX $0021 ; X = [0021]; Offset, usually 0x00.
; 写出信息标记?
LDA #$01 ; A = 0x01
STA $0700,X ; [0700] = 0x01
; 写出位置
LDA $004A ; A = [004A]
STA $0701,X ; [0701] = A
LDA $0049 ; A = [0049]
STA $0702,X ; [0702] = A
; LDY $0047 ; 还原当前对话的偏移
; Loop - 检查中文字符
_Loop_SearchForChinese:
LDA ($02),Y ; A = 当前字符
AND #$C0 ; 检查第一位是不是 1
CMP #$C0 ; 中文控制符
BNE _END_CHINESE; 跳过中文字符处理
; 是中文控制符
; 加入两个字符,并,自循环
; 写出 2 个控制符
TXA ;
PHA ; push X
LDA ($02),Y ; A = ChineseCtrlChar
AND #$7 ;
TAX ; X = A
INY ; Y++
; 此时的 stack: X
; 此时的值: X = PPU 序号
; Y = 正常的序号
LDA ($02),Y ; A = NextChar
STA $07F0,X ; [07FX] = A (CHR_BANK_X)
INY ; Y++
PLA
TAX ; pop X
STY $0047 ; 储存对话偏移
INC $0047
JSR $FACE
JMP _Loop_SearchForChinese
_END_CHINESE:
LDA ($02),Y
STA $0703,X ; [0704] = A
LDA #$FF ; A = __END_MARK__
STA $0704,X ; [0705] = A
TXA ; A = X
CLC ; Clear Carry
ADC #$05 ; Add 06 to A
STA $0021 ; [0021] = A
INC $0049 ; [0049]++
RTS
_UpdatePosition:
; 读取下一行的位置的偏移地址
LDA $0046
ASL
TAY
LDA $97DB,Y
STA $0000
LDA $97DC,Y
STA $0001
; 写出 PPU 定位地址
LDA $0048
ASL
TAY
LDA ($00),Y
STA $0049
INY
LDA ($00),Y
STA $004A
RTS
中文显示代码:
; 07,FB40: 01FB50
; STA $2007
.org $FB40
; A = THE VALUE
; 检查是否为中文字符
PHA ; push A
AND #$C0
CMP #$80
BNE _NOT_CHINESE
; 检查中文 flag
LDA $07EE
CMP #$00
BEQ _NOT_CHINESE
; 中文字符!
PLA ; pop A
; 获取字符信息
ASL
ASL
; A = Tile ID
; 写出左上角
STA $2007
ADC #$1
; 写出右上角
STA $2007
ADC #$1
PHA
LDA $07EE
CMP #$01
BEQ _TYPE_01
CMP #$02
BEQ _TYPE_02
CMP #$03
BEQ _TYPE_03
JMP _NOT_CHINESE
_NOT_CHINESE:
PLA ; pop A
_DISPLAY:
STA $2007
_EXIT:
; JMP $CB9E
RTS
_TYPE_01_JMP:
; JMP _TYPE_01
_TYPE_02_JMP:
; JMP _TYPE_02
_TYPE_01:
; 寻找左下角的坐标
LDA $0049 ; A = LOW_ADDR
CLC
ADC #$1F
LDA $004A
ADC #$00
STA $2006 ; Write Hi Part.
CLC
LDA $0049
ADC #$1F
STA $2006 ; Write Lo Part.
CLC
INC $0049 ; 跳过写出了的位置
PLA ; pop A
STA $2007
ADC #$1
JMP _DISPLAY
_TYPE_02:
; 寻找左下角的坐标
LDA $004A ; A = LOW_ADDR
CLC
ADC #$1F
LDA $004B
ADC #$00
STA $2006 ; Write Hi Part.
CLC
LDA $004A
ADC #$1F
STA $2006 ; Write Lo Part.
CLC
INC $004A ; 跳过写出了的位置
PLA ; pop A
STA $2007
ADC #$1
JMP _DISPLAY
_TYPE_03:
; 寻找左下角的坐标
LDA $0043 ; A = LOW_ADDR
CLC
ADC #$1F
LDA $0044 ; HI ADDR
ADC #$00
STA $2006 ; Write Hi Part.
CLC
LDA $0043
ADC #$1F
STA $2006 ; Write Lo Part.
CLC
INC $0043 ; 跳过写出了的位置
PLA ; pop A
STA $2007
ADC #$1
JMP _DISPLAY
※ 注释可能有一些地方写错了,不要在意细节..
最后看看效果: