一个游戏的汉化实例
【介绍】
前几天,tbsgame.net 的 Lejardo老大,经过 MSN Messager 与我联系,说是想汉化一游戏
—— War In The Pacific : The struggle against Japan 1941-1945,是一个古老的2D回合
制游戏,我想也只有骨灰级的玩家会体贴这个。
以前,曾在该网站,到场了《铁血同盟2黄金版》的汉化事情。但是,那是有完整的C源码的
汉化,与这次的难度不行等量齐观。
从TLF的FTP里下载了一个硬盘版,谁知不能升级最新的1.8补丁,于是从已经升级的人那里
失掉1.8的exe,虽然在正式游戏时会出错,但好歹能看到选单画面和任务简报了。
在DX9之前,险些每个英文游戏都有一套本身的字符显示引擎,利用DirectDraw的2D游戏把
字符寄存在Surface中,利用Direct3D的3D游戏则把字符寄存在Texture中,显示时根据字符找
到相应的Surface或Texture,然后把它们贴上屏幕。
汉化的要领,一种是利用WINDOWS的GDI函数,直接操纵HDC。这种要领的缺陷是服从低下,这
也是为什么在DX9之前,英文游戏要利用本身的字符输出引擎的缘故原由;另一种要领是修改原有的
引擎,使其支持中文输出,但如果游戏没有利用unicode,就会牵涉到一个单字节、双字节的问
题,在没有源码的环境下十分麻烦;末了一种是把游戏阐发透彻后,外接一个dll,利用本身的
字符输出引擎。
考虑到该游戏是一个2D游戏,并且现在的电脑配置已远远高于该游戏的推荐配置,所以预备使
用第一种要领进行汉化。
【阐发】
起首要搞明白游戏原本的字符显示机制。
用ODBG载入,加-w参数,让游戏在窗口形式下运转。
任意找一显示出来的字符串,然后在内存中搜刮,下内存断点,步步跟踪,层层深入,末了找到
了这里:
代码:--------------------------------------------------------------------------------
0057E5E0 /$ A1 105D6004 mov eax,dword ptr ds:[4605D10]
0057E5E5 |. 83EC 18 sub esp,18
0057E5E8 |. 85C0 test eax,eax
0057E5EA |. 0F84 79010000 je war_in_t.0057E769
0057E5F0 |. 8B0D 080B0E04 mov ecx,dword ptr ds:[40E0B08] ; [ecx]+10 = 颜色
0057E5F6 |. 85C9 test ecx,ecx
0057E5F8 |. 0F84 6B010000 je war_in_t.0057E769
0057E5FE |. 55 push ebp
0057E5FF |. 8B6C24 2C mov ebp,dword ptr ss:[esp+2C]
0057E603 |. 85ED test ebp,ebp
0057E605 |. 56 push esi
0057E606 |. 57 push edi
0057E607 |. 75 06 jnz short war_in_t.0057E60F
0057E609 |. 8B2D 54840F04 mov ebp,dword ptr ds:[40F8454] ; 目标surface
0057E60F |> 8B4424 30 mov eax,dword ptr ss:[esp+30]
0057E613 |. 8D70 01 lea esi,dword ptr ds:[eax+1]
0057E616 |> 8A10 /mov dl,byte ptr ds:[eax]
0057E618 |. 40 |inc eax
0057E619 |. 84D2 |test dl,dl
0057E61B |.^ 75 F9 \jnz short war_in_t.0057E616
0057E61D |. 2BC6 sub eax,esi
0057E61F |. 8BF0 mov esi,eax
0057E621 |. 81FE 58020000 cmp esi,258
0057E627 |. 897424 0C mov dword ptr ss:[esp+C],esi
0057E62B |. 7C 0C jl short war_in_t.0057E639
0057E62D |. C74424 0C 57020000 mov dword ptr ss:[esp+C],257
0057E635 |. 8B7424 0C mov esi,dword ptr ss:[esp+C]
0057E639 |> 8B4424 28 mov eax,dword ptr ss:[esp+28]
0057E63D |. 8B5424 2C mov edx,dword ptr ss:[esp+2C]
0057E641 |. 33FF xor edi,edi
0057E643 |. 85F6 test esi,esi
0057E645 |. A3 48740604 mov dword ptr ds:[4067448],eax
0057E64A |. 8915 E4760604 mov dword ptr ds:[40676E4],edx
0057E650 |. 897C24 10 mov dword ptr ss:[esp+10],edi
0057E654 |. 0F8E 0C010000 jle war_in_t.0057E766
0057E65A |. 53 push ebx
0057E65B |. 8B5C24 3C mov ebx,dword ptr ss:[esp+3C]
0057E65F |. 90 nop
0057E660 |> 8B7424 34 /mov esi,dword ptr ss:[esp+34]
0057E664 |. 0FB63437 |movzx esi,byte ptr ds:[edi+esi]
0057E668 |. 83FE 20 |cmp esi,20
0057E66B |. 0F8C CB000000 |jl war_in_t.0057E73C
0057E671 |. 3B31 |cmp esi,dword ptr ds:[ecx] ; [ecx] == 80
0057E673 |. 0F8D DB000000 |jge war_in_t.0057E754 ; 非法字符?
0057E679 |. 803D 88860F04 00 |cmp byte ptr ds:[40F8688],0
0057E680 |. 74 05 |je short war_in_t.0057E687
0057E682 |. 8B79 04 |mov edi,dword ptr ds:[ecx+4]
0057E685 |. EB 0C |jmp short war_in_t.0057E693
0057E687 |> 8BFE |mov edi,esi
0057E689 |. 6BFF 38 |imul edi,edi,38
0057E68C |. 8BBC0F 24080000 |mov edi,dword ptr ds:[edi+ecx+824]
0057E693 |> 833D 585E6004 00 |cmp dword ptr ds:[4605E58],0
0057E69A |. 7E 4A |jle short war_in_t.0057E6E6
0057E69C |. 894424 18 |mov dword ptr ss:[esp+18],eax
0057E6A0 |. 03C7 |add eax,edi
0057E6A2 |. 894424 20 |mov dword ptr ss:[esp+20],eax
0057E6A6 |. 895424 1C |mov dword ptr ss:[esp+1C],edx
0057E6AA |. 8B41 08 |mov eax,dword ptr ds:[ecx+8]
0057E6AD |. 03C2 |add eax,edx
0057E6AF |. 85DB |test ebx,ebx
0057E6B1 |. 894424 24 |mov dword ptr ss:[esp+24],eax
0057E6B5 |. 74 10 |je short war_in_t.0057E6C7
0057E6B7 |. 8D4C24 18 |lea ecx,dword ptr ss:[esp+18]
0057E6BB |. 51 |push ecx
0057E6BC |. 53 |push ebx
0057E6BD |. 6A 00 |push 0
0057E6BF |. E8 FC75FFFF |call war_in_t.00575CC0
0057E6C4 |. 83C4 0C |add esp,0C
0057E6C7 |> 8B15 585E6004 |mov edx,dword ptr ds:[4605E58]
0057E6CD |. 6A 00 |push 0 ; /Arg5 = 00000000
0057E6CF |. 55 |push ebp ; |Arg4
0057E6D0 |. 6A 00 |push 0 ; |Arg3 = 00000000
0057E6D2 |. 52 |push edx ; |Arg2 => 00000000
0057E6D3 |. 8D4424 28 |lea eax,dword ptr ss:[esp+28] ; |
0057E6D7 |. 50 |push eax ; |Arg1
0057E6D8 |. E8 33CAFFFF |call war_in_t.0057B110 ; \war_in_t.0057B110
0057E6DD |. 8B0D 080B0E04 |mov ecx,dword ptr ds:[40E0B08]
0057E6E3 |. 83C4 14 |add esp,14
0057E6E6 |> 56 |push esi
0057E6E7 |. 51 |push ecx
0057E6E8 |. E8 F3B3FFFF |call war_in_t.00579AE0 ; 颜色相关
0057E6ED |. 6BF6 38 |imul esi,esi,38
0057E6F0 |. 8B0D E4760604 |mov ecx,dword ptr ds:[40676E4]
0057E6F6 |. 8B15 48740604 |mov edx,dword ptr ds:[4067448]
0057E6FC |. A1 080B0E04 |mov eax,dword ptr ds:[40E0B08]
0057E701 |. 55 |push ebp
0057E702 |. 6A 00 |push 0
0057E704 |. 68 A00F0000 |push 0FA0
0057E709 |. 6A 00 |push 0
0057E70B |. 53 |push ebx
0057E70C |. 51 |push ecx
0057E70D |. 52 |push edx
0057E70E |. 8D8C06 20080000 |lea ecx,dword ptr ds:[esi+eax+820]
0057E715 |. 51 |push ecx
0057E716 |. E8 C5F7FFFF |call war_in_t.0057DEE0
0057E71B |. A1 48740604 |mov eax,dword ptr ds:[4067448]
0057E720 |. 8B0D 080B0E04 |mov ecx,dword ptr ds:[40E0B08]
0057E726 |. 8B15 E4760604 |mov edx,dword ptr ds:[40676E4]
0057E72C |. 83C4 28 |add esp,28
0057E72F |. 03C7 |add eax,edi
0057E731 |. 8B7C24 14 |mov edi,dword ptr ss:[esp+14]
0057E735 |. A3 48740604 |mov dword ptr ds:[4067448],eax
0057E73A |. EB 18 |jmp short war_in_t.0057E754
0057E73C |> 83FE 0A |cmp esi,0A
0057E73F |. 75 13 |jnz short war_in_t.0057E754
0057E741 |. A1 74720604 |mov eax,dword ptr ds:[4067274]
0057E746 |. A3 48740604 |mov dword ptr ds:[4067448],eax
0057E74B |. 0351 08 |add edx,dword ptr ds:[ecx+8]
0057E74E |. 8915 E4760604 |mov dword ptr ds:[40676E4],edx
0057E754 |> 8B7424 10 |mov esi,dword ptr ss:[esp+10]
0057E758 |. 47 |inc edi
0057E759 |. 3BFE |cmp edi,esi
0057E75B |. 897C24 14 |mov dword ptr ss:[esp+14],edi
0057E75F |.^ 0F8C FBFEFFFF \jl war_in_t.0057E660
0057E765 |. 5B pop ebx
0057E766 |> 5F pop edi
0057E767 |. 5E pop esi
0057E768 |. 5D pop ebp
0057E769 |> 83C4 18 add esp,18
0057E76C \. C3 retn
--------------------------------------------------------------------------------
从0057E660到0057E75F的循环是把字符串中的字符逐一显示出来。
其中
代码:--------------------------------------------------------------------------------
0057E671 |. 3B31 |cmp esi,dword ptr ds:[ecx] ; [ecx] == 80
0057E673 |. 0F8D DB000000 |jge war_in_t.0057E754 ; 非法字符?
--------------------------------------------------------------------------------
过滤了大于80(十进制128)的字符,这便是为什么中文无法显示的缘故原由。固然,简略的把
这个果断去掉是不行行的。
这个函数有3个参数:输出的字符串和坐标(x,y),而如果我们要用本身的代码取代,还需要
晓得两样工具:颜色,以及输出的目标地(目标Surface)。
把 0057E6E8 的 “call 00579AE0”nop掉,发明全部字符不再有颜色变化,可见这个函数
同颜色有关。连续跟踪,发明颜色寄存在[40E0B08],一个全局的struct中。
而目标Surface是经过跟踪 0057E716 的“call 0057DEE0”失掉的。
接下来要做的,便是把这个函数更换成我们本身的。
【动手】
这个游戏利用了DirectDraw7,先写一个DirectDraw7的步伐:
代码:--------------------------------------------------------------------------------
#include
#include
#pragma comment(lib,"ddraw.lib")
#pragma comment(lib,"dxguid.lib")
#define GSM_CAPTION GetSystemMetrics(SM_CYCAPTION) //标题栏
#define GSM_CXBORDER GetSystemMetrics(SM_CXFIXEDFRAME) //不行调边框
#define GSM_CYBORDER GetSystemMetrics(SM_CYFIXEDFRAME)
#define GSM_CYMENU GetSystemMetrics(SM_CYMENU) //如果有菜单
#define MAXWIDTH 640 //游戏显示区大小
#define MAXHEIGHT 480
//全局变量
LPDIRECTDRAW7 lpDD; //DirectDraw对象
LPDIRECTDRAWSURFACE7 lpDDSPrimary; //DirectDraw主表面
LPDIRECTDRAWSURFACE7 lpDDSBack; //后台缓冲表面
char szMsg1[] = "001 Tutorial";
char szMsg2[] = "按ESC加入";
BOOL bActive = TRUE;
HWND hwnd;
HFONT hfont;
//函数声明
LRESULT CALLBACK WinProc (HWND hWnd, UINT message, WPARAM wParam,
LPARAM lParam);
BOOL InitWindow (HINSTANCE hInstance, int nCmdShow);
BOOL InitDDraw (void); //初始化DirectX
void FreeDDraw (void); //开释DirectX对象
void MainLoop (void); //游戏主循环
//-------------------------------------------------------
//函数:WinMain()
//功效:Win32使用步伐入口函数.进行初始化事情,处置惩罚消息循环
//-------------------------------------------------------
int WINAPI
WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine,
int nCmdShow)
{
MSG msg;
//初始化主窗口
if (!InitWindow (hInstance, nCmdShow))
return FALSE;
//初始化DirectDraw环境,并完成DirectDraw功效
if (!InitDDraw ())
{
MessageBox (GetActiveWindow (), "初始化DirectDraw历程中出错!", "Error",
MB_OK);
FreeDDraw ();
DestroyWindow (GetActiveWindow ());
return FALSE;
}
hfont =
CreateFont (15, 0, 0, 0, FW_EXTRALIGHT, FALSE, FALSE, FALSE, ANSI_CHARSET,
OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, 3,
VARIABLE_PITCH | FF_DONTCARE, "Lucida Sans Unicode");
while (1)
{
if (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE))
{
//如果有消息就处置惩罚消息
if (!GetMessage (&msg, NULL, 0, 0))
return msg.wParam;
TranslateMessage (&msg);
DispatchMessage (&msg);
}
else if (bActive)
{ //如果步伐处于激活状态,进入游戏主循环
MainLoop ();
}
//等待消息
else
WaitMessage ();
}
return msg.wParam;
}
//--------------------------------------
//函数:InitWindow()
//功效:创立主窗口
//--------------------------------------
static BOOL
InitWindow (HINSTANCE hInstance, int nCmdShow)
{
WNDCLASS wc; //窗口类布局
//添补窗口类布局
- 文章作者: 福州军威计算机技术有限公司
军威网络是福州最专业的电脑维修公司,专业承接福州电脑维修、上门维修、IT外包、企业电脑包年维护、局域网网络布线、网吧承包等相关维修服务。
版权声明:原创作品,允许转载,转载时请务必以超链接形式标明文章原始出处 、作者信息和声明。否则将追究法律责任。
TAG:
评论加载中...
|