KOEI/삼국지 시리즈

삼국지5PK 일판에 한글을 출력시키고 말겠다. (1부)

K66Google 2022. 2. 15. 23:59

1. 서론

작년 12월부터 갑자기 뭔가에 홀린 것 마냥 스팀판 삼국지 시리즈를 구입하고 한글 출력 패치를 만들기 시작한 나는, 10PK부터 차례대로 밑으로 내려가면서 한글 출력 패치를 만들기 시작했다.

10PK와 9PK는 폰트 파일이 따로 있기 때문에 폰트만 재작업을 하고 메시지 파일은 정발판에서 가져오는 식으로 해결했다.

8PK, 7PK, 6PK는 노시디 패치된 정발판 실행파일이 윈도우10에서 테스트 모드가 아니여도 작동된다는 점을 이용하여 비교적 쉽게 해결할 수 있었다.

 

그러나 삼국지5PK (그리고 나중에 작업해야 할 4PK)는 그러한 방식이 통하지 않는다.

정발판 실행파일을 쓰려고 하면... 5PK는 일단 16비트 실행파일이라 64bit 윈도우에서는 바로 작동되지 않는다.

winevdm이라는 프로그램을 끼워서 배포하면 되지 않냐고? 그럼 음악 재생은 어떻게 할 건데? CD 넣고 키라고 할 건가?

5PK는 koeicda.dll도 쓰지 않기 때문에 _inmm이나 mp3.dll 꼼수도 불가능하다.

결국 5PK 정발판 실행파일은 스팀판 한글 출력 패치에 이용할 수 없다라는 결론에 다다르게 된다. 그래서 나는 스팀판 실행파일을 개조하기로 하였다.

 

 

2. 프로그램 로케일 변경 / 반각 필터링 제거

올리디버거로 수정하기 전 '스팀리스' 라는 프로그램으로 헥스 변경 감지를 못 하게 해야 한다. 그래야 수정한 실행파일이 정상 실행된다.

 

- 프로그램 로케일 한국어로 변경
0041E910 : PUSH 0x80 -> PUSH 0x81

(폰트 이름은 Crystaltile2에서 MS라고 검색하면 폰트 이름이 뜨는데 거기서 굴림체로 수정.)

 

- 반각 필터링 제거 1
0049C849 : CMP EAX, 0x9F -> CMP EAX, 0xC8
0049C850 : CMP EAX, 0xE0 -> CMP EAX, 0xA1

 

- 반각 필터링 제거 2

0041ED86 : ADD ECX, 0xE1 -> ADD ECX, 0x161
0041ED8E : ADD ECX, 0x161 -> ADD ECX, 0xE1

 

이제 반각 가타카나의 전각 히라가나 강제 변환을 해제시켜야 한다. 안 그러면 '한글' 이 '쪽쭈혻쯤' 으로 나온다.

보통 CMP **, 0xA6 으로 검색해보면 의심스러운 코드가 찾아지는데 이상하게도 이 실행파일에서는 검색이 되지 않았다.

삽질 끝에 CMP AL, 0xA5라는 값을 찾아내서 그 밑의 JL을 JMP로 바꿔주었다.

그러나 결론부터 말하면, 이렇게 해도 해제되지 않았다. 이 코드는 돈에이의 함정이었던 것이다.

 

하지만 이 때의 나는 해제된 것으로 오인(誤認)하고, 다른 작업을 진행하기로 했다. 

 

 

3. 글자 밑이 잘리는 문제

한글판의 sndata.s5를 가져오니까 장수 이름이 한글로 바뀌었다.

그런데 글자 밑이 조금씩 잘려서 출력이 되고 있다. 어떻게 해야 똑바로 나오게 할 수 있을까.

예전에 본 블로그 게시물 (현재는 비공개 처리됨) 에 나온 방법을 써먹어보기로 하였다.

 

Ollydbg에서 Ctrl + N 누른 다음, 열리는 창에서 GetTextMetricsA라고 쳤다. 그러면 해당 함수가 목록에 떠 있는데 그걸 선택하고 엔터 키 치면 뭔가 목록이 또 나온다. 또 나온 목록에서 또 엔터 키 친다.

그러면 이런 동네로 오는데, 먼저 GetTextMetricsA 위에 있는 pTextmetric의 주소를 메모한다. (004E0840)

그런 다음, GetTextMetricsA 밑의 ReleaseDC라고 써 있는 곳 바로 다음 코드에 있는 것을 메모장에 옮겨 적는다.

 

MOV DWORD PTR SS:[ESP+0x434],0x94

 

다 옮겨 적었으면 빈 자리를 찾아야 한다. 빈 자리는... 그냥 INT3만 있거나 DB 00만 있는 곳을 찾아보면 나온다.

하지만 해당 자리를 실행파일이 아예 취급하지 않거나, 다른 일을 하는데 써먹을 수도 있기 때문에, 저장해서 실행해보고 튕기면 다른 빈 자리를 찾고... 이런 식으로 작업해야 한다.

아무튼 빈 자리를 찾았으면 이렇게 입력한다.

 

ADD BYTE PTR DS:[004E0840],2

MOV DWORD PTR SS:[ESP+0x434],0x94
RETN

 

004E0840은 아까 메모한 pTextmetric의 주소다. 그리고 MOV... 어쩌구는 아까 ReleaseDC 밑에 있던 코드를 옮겨 적은 것이고, RETN은 다시 제자리로 복귀하는 명령이다.

 

이제 이 내용을 입력한 주소를 메모해야 한다. 004AADBF다.

 

다시 ReleaseDC 밑으로 돌아가서, 해당 자리를 CALL 004AADBF 로 바꾼다.

그러면 프로그램은 004AADBF까지 가서 코드를 실행하고 다시 이 자리로 돌아오게 될 것이다.

 

자... 그럼 결과는?

 

글자 밑이 안 잘리고 깔끔하게 출력되고 있다. 성공!

 

 

4. 뒷 바이트가 0xFD , 0xFE인 문자들이 ?로 뜨는 문제 / 신무장 이름 입력 개조

무장 이름을 보고 있으니 황보이 황보?로 뜨는 문제를 확인했다. '숭' 자가 제대로 표시되지 않는 것이다.

일단 끝 바이트가 0xFD, 0xFE로 끝나는 문자는 다음과 같다. (한국어 인코딩 기준으로)

 

 

 앞 바이트  ~FD  ~FE
 B0~  괄  괆
 B1~  깰  깸
 B2~  끗  끙
 B3~  뇜  뇝
 B4~  덤  덥
 B5~  딴  딸
 B6~  랖  랗
 B7~  륨  륩
 B8~  뫙  뫼
 B9~  법  벗
 BA~  빡  빤
 BB~  생  샤
 BC~  숫  숭
 BD~  쐬  쐰
 BE~  엌  엎
 BF~  웡  웨
 C0~  절  젊
 C1~  집  짓
 C2~  찹  찻
 C3~  츳  층
 C4~  퀸  퀼
 C5~  퉈  퉜
 C6~  폿  퐁
 C7~  혜  혠
 C8~  힛  힝

이 표에서 '' 자는 BC~ 대역에 있다. (0xBCFE)

'숭' 자가 0xFE로 끝나는 문자라서 게임에서 제대로 표시가 되지 않는 것이다.

 

그럼 왜 0xFD, 0xFE 글자들은 표시가 안되는 것일까?

한국어 인코딩(CP949)뒷 바이트가 0xFD, 0xFE인 곳에도 글자를 배당해 놓았다. 

그러나 일본어 인코딩(Shift-JIS)뒷 바이트로 0x7F, 0xFD, 0xFE, 0xFF의 뒷 바이트를 사용하지 않는다.

이로 인해 Shift-JIS는 뒷 바이트가 0xFD, 0xFE인 글자들은 무시해버리고, ?로 출력이 되는 것이다.

 

그럼 Ollydbg에서 어떻게 하면 해결 할 수 있을까?

일단 CMP **, 0xFD... 이런 식으로 검색해본다. 0xFD로 검색하는 이유는 0xFD부터 안 나오니까 그런 거다.

 

* TIP

**에는 EAX, EBX, ECX, EDX, AX, BX, CX, DX, AH, BH, CH, DH, AL, BL, CL, DL 이런 식으로 다 집어넣고 검색해봐야 한다. 보통은 E*X  *L 시리즈에서 나올 확률이 높다.

 

검색해보니, 00493D8F에 의심가는 코드가 하나 떴다. 위쪽에 보니 CMP AL,0x7F 라는 코드도 있다. 0x7F도 Shift-JIS 상에선 취급을 안하는 영역인데... 거기에 0xFD와 짝꿍이라... 굉장히 의심이 간다.

 

* 결론

00493D8F : CMP AL,0xFD -> CMP AL,0xFF 로 변경.

 

잠시 Ollydbg는 종료하고, 이제 해당 뒷 바이트의 글자들이 제대로 출력되는지 확인하기 위해, 실행파일에서 이름 목록 뜨는 부분을 먼저 개조해보기로 하였다.

색깔이 같은 것끼리가 짝꿍이고, 출력될 글자 범위는 일판과 똑같은 개수로 맞춰줘야 한다. (안 그러면 표시할 글자 개수까지 바꿔줘야 하는데 귀찮다.)

일단 EUC-KR의 한글 2350자만 우겨넣어 보기로 한다. 헥스 값은 바이트 플립 (앞 바이트 / 뒷 바이트를 거꾸로) 해서 Crystaltile2 상에다 삽입한다.

 

 그룹  정해진 한자 개수  우겨넣을 한글 범위 - 
실행파일에 입력할 바이너리
 아  41  B0A1(가) ~ B0C9(걸)  - A1 B0 / C9 B0
 이  58  B0CA(걺) ~ B146(켉)  - CA B0 / 46 B1
 *euc-kr 영역초과
 우  27  B1A1(괌) ~ B1BB(굳) - A1 B1 / BB B1
 에  54  B1BC(굴) ~ B1F1(깐) - BC B1 / F1 B1
 오  35  B1F2(깔) ~ B257(쾉) - F2 B1 / 57 B2
 *euc-kr 영역초과
 카  130+105 = 235  B2A1(깹) ~ B3CF(넒)  - A1 B2 / CF B3
 키  130+52 = 182  B3D0(넓) ~ B4C9(능) - D0 B3 / C9 B4
 쿠  46  B4CA(늦) ~ B4F7(덖) - CA B4 / F7 B4
 케  108  B4F8(던) ~ B5A7(덴) - F8 B4 / A7 B5
 코  130+42 = 172  B5A8(델) ~ B697(텢) - A8 B5 / 97 B6
 *euc-kr 영역초과
 사  112  B6A1(땀) ~ B753(톁) - A1 B6 / 53 B7
 *euc-kr 영역초과
 시  390+7 = 397  B7A1(래) ~ B9B5(뭇) - A1 B7 / B5 B9
 스  40  B9B6(뭉) ~ B9DD(반) - B6 B9 / DD B9
 세  115  B9DE(받) ~ BA94(틪) - DE B9 / 94 BA
 *euc-kr 영역초과
 소  99  BAA1(벙) ~ BB46(팂) - A1 BA / 46 BB
 *euc-kr 영역초과
 타  109  BBA1(빨) ~ BC50(퍹) - A1 BB / 50 BC
 *euc-kr 영역초과
 치  86  BCA1(샥) ~ BCF6(수) - A1 BC / F6 BC
 츠  29  BCF7(숙) ~ BD56(폲) - F7 BC / 56 BD
 *euc-kr 영역초과
 테  66  BDA1(숯) ~ BDE2(썩) - A1 BD / E2 BD
 토  120  BDE3(썬) ~ BE9E(풛) - E3 BD / 9E BE
 *euc-kr 영역초과
 나  19  BEA1(쐴) ~ BEB3(쓱) - A1 BE / B3 BE
 니  19  BEB4(쓴) ~ BEC6(아) - B4 BE / C6 BE
 누  1  BEC7(악)                - C7 BE / C7 BE
 네  12  BEC8(안) ~ BED3(앙)  - C8 BE / D3 BE
 노  14  BED4(앝) ~ BEE1(얀) - D4 BE / E1 BE
 하  118  BEE2(얄) ~ BF9B(퓵) - E2 BE / 9B BF 
*euc-kr 영역초과
 히  87  BFA1(에) ~ BFF7(웍) - A1 BF / F7 BF
 후  68  BFF8(원) ~ C07E(X) - F8 BF / 7E C0
 *euc-kr 영역초과
 헤  35  C0A1(웩) ~ C0C3(읔) - A1 C0 / C3 C0
 호  97  C0C4(읕) ~ C167(햓) - C4 C0 / 67 C1
 *euc-kr 영역초과
 마  33  C1A1(점) ~ C1C1(좋) - A1 C1 / C1 C1
 미  16  C1C2(좌) ~ C1D1(죙) - C2 C1 / D1 C1
 무  10  C1D2(죠) ~ C1DB(줆) - D2 C1 / DB C1
 메  17  C1DC(줌) ~ C1EC(쥴) - DC C1 / EC C1
 모  27  C1ED(쥼) ~ C24A(헖) - ED C1 / 4A C2
 *euc-kr 영역초과
 야  18  C2A1(징) ~ C2B2(짼) - A1 C2 / B2 C2
 유  32  C2B3(쨀) ~ C2D2(쫘) - B3 C2 / D2 C2
 요  40  C2D3(쫙) ~ C2FA(찮) - D3 C2 / FA C2
 히라가나  83  C2FB(찰) ~ C391(횗) - FB C2 / 91 C3 
*euc-kr 영역 초과
 가타카나  86  C3A1(찼) ~ C3F6(츙) - A1 C3 / F6 C3
 라  19  C3F7(츠) ~ C44C(횸) - F7 C3 / 4C C4
 *euc-kr 영역 초과
 리  66  C4A1(치) ~ C4E2(콰) - A1 C4 / E2 C4
 루  5  C4E3(콱) ~ C4E7(쾅) - E3 C4 / E7 C4
 레  33  C4E8(쾌) ~ C54B(휣) - E8 C4 / 4B C5
 *euc-kr 영역 초과
 로  29  C5A1(큄) ~ C5BD(탐) - A1 C5 / BD C5
 와  19  C5BE(탑) ~ C5D0(털) - BE C5 / D0 C5
 영수  75   C5D1(턺) ~ C65E(X) - D1 C5 / 5E C6
 *euc-kr 영역 초과
 기호  108  C6A1(퉤) ~ C74F(X) - A1 C6 / 4F C7
 *euc-kr 영역 초과
 제2  (130x26)+4 = 3384  C7A1(퐈) ~ D9A0(X) - A1 C7 / A0 D9
 외자  59  제외

 

이렇게 하니 뒷 바이트가 0xFD로 끝나는 글자들은 정상 출력이 되기 시작했다.

('츳' 자는 C3 FD로, 0xFD로 끝나는 글자다.)

그러나 0xFE로 끝나는 글자들은 여전히 출력이 ?로 나오고 있다.

 

나는 이 문제 때문에 다시 Ollydbg에서 온갖 삽질을 하다가 TextoutA 함수 근처에서 한 가지 사실을 알아냈다.

그건 바로, 뒷 바이트가 0xFE인 글자들은 게임 상에서 0x7E로 바뀌어서 출력된다는 점이었다.

귀신이 곡할 노릇이다. 왜 뒷 바이트가 0x7E로 바뀌어서 출력될까?

 

그렇게 온갖 삽질을 하다가 0049C89A의 AND EAX, 0x7F7F에서 발걸음을 멈췄다.

어쩌면 이 값을 바꾸면 뭔가 변화가 있을지도 모르겠다는 생각이 들었기 때문이다.

 

그리고 그 값을 0x7FFF로 바꿔보니까... 거짓말같이 0xFE로 끝나는 글자들도 출력이 되기 시작했다.

그럼 끝난 건가?

이렇게 하면 진짜로 다 해결된걸까?

 

아니다.

0xB0CA ~ 0xB146 까지만 출력되는 영역에 0xB1C3, 0xB1C4 글자들까지 침범해서 출력되고 있기 때문이다.

아무래도 저게 정답이 아닌 모양이다. 일단 0x7FFF는 0x7F7F로 되돌리고, 이 문제는 나중에 해결하도록 하자...

 

 

5. 무장열전 창 폭이 좁은 문제

일판 무장열전 창은 한글판 무장열전 창에 비해 폭이 좁아서 분명 이런 식으로 오른쪽 부분의 문장이 잘리게 될 거다. 창 크기를 조절하는 방법은 없을까? 나는 인터넷을 뒤적거렸다.

 

그러다가 대항해시대 카페에서 해법을 찾아냈다. (링크)

실행파일 내에 68 xx xx xx xx  68 xx xx xx xx 이런 식으로 코드가 있는 것 같은데, 그걸 수정하면 되는 모양이다.

(첫 번째 68~ 은 높이 / 두 번째 68~ 은 너비)

문제는 그 많고 많은 68~ 코드 중 어느 것이 무장열전 창과 관계가 있는지 모른다는 것이다.

어쩔 수 없지... 노가다를 하는 수밖에 없다. 대충 칼무리로 가로 픽셀을 재본다. 대략 320px 정도...?

그럼 320을 16진수한 140 -> 01 40 -> 40 01 (바이트 플립) - 68 40 01 00 00 뭐 이런 식으로 검색해보면 되지 않을까?

그렇게 나는 또 삽질을 시작했다.

 

검색해서 나오는 자리의 값을 수정하고, 저장하고, 실행해서 변한 게 없으면 끄고, 다음으로 가고... 이런 식으로 노가다를 반복해가다가 겨우 0x68180 대역에서 변화를 확인했다.

너비만 320px에서 430px로 바꿔주도록 한다.

 

* 결론

0x68180 대역 : 68 40 01 00 00 -> 68 AE 01 00 00 으로 변경.

 

 

- 스크롤 압박으로 인해 2부에서 계속 -