[Retro Games] 패미컴 (or NES) 게임 한글화 튜토리얼 #05

in #kr7 years ago (edited)

안녕하세요? 

패미컴 튜토리얼 글로 인사드립니다. 이번 글에서는 지난시간의 튜토리얼을 바탕으로 실제 게임에서 대사 데이타를 찾아내는 과정을 그림으로 자세히 설명할 예정입니다. 이전의 튜토리얼은 아래 링크에서 확인해 주세요.

5. 대사 위치 검색방법


이번 튜토리얼에서는 지난 두시간에 걸친 이론 공부가 실제로 게임 한글화에 적용되는 과정을 확인 하실 수 있겠네요. 하나하나 차근차근 알아보도록 해요.

 

5.1 준비물


이번 튜토리얼에는 두가지 준비물이 필요합니다. 

- 영문패치된/원본 게임롬 공유는 불법입니다. 영문패치 링크로 대체합니다.

  • FCEUX 에뮬레이터 (링크)

5.2 게임내 첫 대사 검색


일반적으로 한 게임 안에서 대사 처리 루틴은 거의 일정합니다. 즉 하나의 루틴을 여러 상황에 돌려가며 사용합니다. (스위트홈은 예외적으로 사용되는 대사 루틴이 많습니다 eg. 일반 대사, 아이템, 몬스터, 캐릭터 이름, etc.). 따라서, 게임 가장 처음의 대사를 사용해서 대략적인 대사처리 루틴을 확인하는 방법이 일반적으로 많이 사용됩니다. 

스위트홈 영문패치판에서 오프닝 이후에 가장 처음 접하는 대사는 아래 그림에서 확인할수 있듯이 "Also remember" 로 시작하는 대사입니다. 튜토리얼#04 에서 대부분의 정식발매 NES게임에서는 1번방법을 사용하여 대사를 출력한다고 설명드렸는데, 스위트홈 역시 1번방법을 사용하여 대사를 출력합니다. 즉, 미리 패턴테이블에 글자타일을 입력한 후 네임테이블을 변경하여 대사를 출력하는 방법이죠. 그러면 차근차근 살펴봅시다.

Fig. 1 - 게임내 첫 대사와 네임테이블


먼저 Fig.1에서와 같이 네임테이블에서 글자타일 A의 정보를 살펴봅니다. 네임테이블의 A위에 마우스 커서를 올려놓으면 그림에서 처럼 Tile ID 와 PPU Address 정보를 얻을 수 있습니다. 즉, PPU 메모리의 $2288 ($는 주소의 위치를 표기, #$는 값을 표기) 에 값 #$A0 이 입력되어 있다는 이야기네요. FCEUX-Debug-Hex Editor-View-PPU Memory로 이동해서 $2288주소를 확인해보면 역시 #$A0값이 입력되어져 있는것을 확인할 수 있습니다. 아래그림에서 처럼말이죠.

Fig. 2 - PPU Memory의 $2288 위치와 그 값


이제 어떻게 PPU메모리의 $2288위치에 #$A0이 입력되어졌는지 알아낼 차례입니다. 이를 위해서 디버거를 이용해야하는데, 위의 그림에서 처럼 $2288 (#$A0) 값에 마우스 커서를 위치한 후 우클릭으로 'Add write breakpoint for address $2288' 을 선택합니다. 여기까지 완료하시면 아래의 그림처럼 Debug 창이 자동으로 열립니다.


Fig. 3 - Debug Breakpoint 의 조건지정


여기서 'Edit'를 클릭 하신 후,  Condition 창에 A==#A0 을 입력합니다. 디버그의 Breakpoint는 게임 진행 중간에 해당 메모리 (예제의 경우 PPU Memory 의 $2288)에 무언가 입력되어질 때 게임을 멈추고 코드를 확인하는 디버그 툴입니다. Condition 창에 조건을 입력해주면 게임이 정지되는 포인트 (Breakpoint)를 조금 더 특정할 수 있는데, 예제의 경우 A 레지스터의 값이 #$A0이고 그게 $2288에 입력되는 순간 게임을 정지하고 코드를 보자는 명령입니다. 이 후 다시 게임을 첫대사 위치까지 진행시키면 (주로 강제세이브/로드 기능을 이용) 게임이 멈추고 디버그창이 나타납니다.


Fig. 4 - 네임테이블 입력루틴 추적


위와 같은 화면이 나타납니다. 게임은 멈춰있고 디버그내의 커서는 아래의 코드에 멈춰 있습니다. 

>0F:C309: 8D 07 20     STA PPU DATA = #$00


먼저 코드에서  가장 먼저 보이는 0F는 F번 뱅크의 값이라는 이야기 입니다. 뱅크가 무엇인지 모르시겠다면 튜토리얼 #02를 참고해주세요. 그 후에 이어지는 C309는 $C309, 즉 주소의 위치고 그 뒤의 3바이트 값 8D 07 20은 6502 어셈블리 코드입니다. 여기서 사용된 코드는 #$8D YY XX 로 주소 $XXYY에 레지스터 A의 값을 입력 (STA, store) 하라는 코드입니다. 예제 그림에는 STA PPU DATA 라고 적혀있는데 여러분의 화면에는 STA $2007 이라고 찍혀있을거에요.

정리하면, 레지스터 A 에 저장된 값을 해당 주소값 ($2007)에 입력하라는 (혹은 저장 하라는) 의미입니다. 여기서 $2007 주소는 특별한 주소로 CPU에서 PPU의 메모리에 값을 입력할 때 사용되는 레지스터이며 PPU 에 레지스터 A의 값 (이 경우 A에는 #$A0 이 들어있음)을 입력하라는 코드죠. 이 코드에 의해서 #$A0 값이 PPU주소 $2288에 입력되므로 디버거가 알아차리고 게임을 멈춘겁니다. 

여기서 어떻게 레지스터 A 에 #$A0값이 들어갔는지 알아야 원래의 대사 위치를 역으로 추적해볼 수 있겠죠? 이를 위해서 디버거 창을 조금 위로 올려서 앞의 코드를 조사합니다.


0F:C306: B9 30 03     LDA $0330,Y @ $0331 = #$A0


여기 바로 앞의 코드가 있네요. LDA 는 load to A 하라는 의미로 값을 레지스터 A로 불러오라는 의미입니다. 위의 코드는 자주사용되는데, 주소값 $0330+Y (레지스터)의 값을 A레지스터로 불러오라는 명령이에요. Y 레지스터의 값을 미리 조절해서 여러 주소의 값을 순차적으로 A에 입력할 수 있습니다. 즉, 반복문에 사용되는데 반복문을 하드웨어 단에서 보면 이런 느낌입니다. 


*) LDY #$08
   LDA $0330,Y
   STA $2007
   DEY
   BNE *)ZERO 플래그가 생성되지 않는다면 (Y 레지스터가 0이 아니라면) 이동해서 반복


Y레지스터에 값 #$08을 넣고 $0338의 값을 A레지스터로 불러서 PPU로 입력하고 Y레지스터값 1감소. Y값이 0인지 확인 후 0이 아니면 이 작업을 반복하는 것이죠. 총 8개의 데이타가 PPU로 이동하겠네요.


다시 본론으로 돌아가보면, PPU주소 $2288의 값 #$A0은 CPU 메모리 $0331에서 복사된 값이였네요. 이제 같은 작업을 한번더 합니다. 어떻게 $0331에 #$A0값이 들어갔는지를 알아보기 위해서죠.


Fig. 5 - $0301 입력루틴 추적


위의 그림에서 볼 수 있듯이, $0331의 값 #$A0은 $0754로부터 복사된 값이였네요. 그런데 $0754부터의 값들을 보면 뭔가 대사같은 느낌이 옵니다. 패턴테이블을 불러서 보면 (Debug - PPU View) 아니나다를까 대사타일 고유넘버들이 가지런히 저장되어있네요. 즉, 데이타를 대조해보면 대사 (글자타일 고유넘버의 집합)이라는 것을 알 수 있습니다.


A0 C5 D0 C8 FF CB BE C6 BE C6 BB BE CB
를 패턴테이블과 대조해보면 [Also Remember]


그런데 이거 익숙하지 않나요? 네, 버퍼입니다. 버퍼가 뭔지 잘 모르신다면 튜토리얼 #04를 참고해주세요. 간략히 말하면, 롬내의 실제 대사 데이타가 CPU에서 일련의 처리를 거친 후 PPU로 이동되기전에 모여들있는 대기소같은 곳이죠. 그럼 거의 다 왔습니다. 한번 더 앞의 과정을 반복해보면 롬의 어느부분의 어떤 데이타가 처리된 후 버퍼에 저장되었는지 알 수 있겠네요.


Fig. 6 - 롬 내부의 대사 데이타 추적


드디어 실체가 나왔네요! 롬 내부의 첫글자 (A) 데이타의 위치는 0C뱅크의 $909E였습니다. 코드를 보면 $909E에서 #$00을 읽어서 $0754+X 위치 (버퍼)에 저장하기 전에 #$A0을 더해주네요. 그래서 롬 내부의 #$00이 #$A0로 저장되는거였군요. 앞으로의 튜토리얼의 워밍업으로 처리 루틴을 살펴보면,


LDA ($CC),Y    -> $CD $CC에 저장된 주소+Y 값을 A레지스터에 로드 (롬에서 대사값 로드)
INC $00CB      -> $CB값을 1 증가 (나중의 코드에서 Y값을 저장하는 용도로 쓰임)
CMP #$EB       -> 로드된 대사값이 #$EB이상인지 비교
BCS $C410      -> 값이 #$EB보다 크면 제어코드(줄바꿈, 입력대기 등)이므로 서브루틴
JSR $C401      -> 값이 #$EB보다 작으면 $C401서브루틴으로 이동하고 처리 후,
JMP $C3F1      -> $C3F1로 점프
CMP #$5F       -> 아까 로드된 대사값이 #$5F인지 비교해서
BEQ $C40E      -> 그렇다면 $C40E로 이동 (5F의 값은 대사창에서 띄어쓰기에 해당)
TAY
BMI $C3D4      -> 로드된 대사값이 #$80보다 큰지 비교해서 그렇다면 일본어 탁음루틴
CLC
ADC #$A0       -> 이도저도 다 아니면 #$A0을 더해서
STA $0754,X    -> 버퍼에 저장
INC            -> 버퍼의 위치를 수정 +1
RTS            -> 서브루틴 종료


오늘도 이야기가 길어졌군요. 이제 한글화의 첫걸음을 이제 막 떼신 것을 축하드립니다! 네, 번역은 아주 아주 나~중 일이랍니다. 갈길이 멀지만 오늘은 여기서 마무리 할게요.

모두 좋은하루 되시고 질문은 댓글로 남겨주세요. 

Sort:  

아직 잘 이해는 안되지만 좋은 글 감사합니다.
오늘도 좋은 하루 되세요 팔로 드리고 가요- 좋은 글 또 기다리겠습니다.

방문감사드려요!~ 저도 팔로우 할게요!

대강은 알고 있지만 다시 보니 근성이 엄청 필요하겠군요.. ㅠ_ㅠ

네, 그래서인지 마무리까지 가기가 항상 힘든거 같네요 ㅎㅎ

헉 .. 한글화는 한번도 안해봤지만 정말 고된 작업이네요 ㅠㅠ....

방문 감사드려요! 그래도 번역작업보다는 이런 과정이 재미는 더 있는것 같아요 ㅎㅎ

잘 보고 있습니다. :D
그리고 다음 강좌도 기다리고 있습니다.
감사합니다.

Congratulations @drgkim! You received a personal award!

1 Year on Steemit

Click here to view your Board

Support SteemitBoard's project! Vote for its witness and get one more award!

Congratulations @drgkim! You received a personal award!

Happy Birthday! - You are on the Steem blockchain for 2 years!

You can view your badges on your Steem Board and compare to others on the Steem Ranking

Vote for @Steemitboard as a witness to get one more award and increased upvotes!