跳至内容

Jixun's Blog 填坑还是开坑,这是个好问题。

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

※ 注释可能有一些地方写错了,不要在意细节..

最后看看效果:

中文汉化效果

知识共享许可协议 本作品采用知识共享署名-相同方式共享 4.0 国际许可协议进行许可。

评论区