xjj5158 发表于 2011-1-27 01:18

看见有人想学汉化,这里有一份看雪论坛的文章,希望能给在这方面有意向的童鞋提供帮助。

本帖最后由 xjj5158 于 2011-2-15 21:45 编辑


【原创】DirectX 9 游戏汉化详解
http://bbs.blacksheepgame.com/images/pediy/statusicon/user_offline.gif





郁闷死,复制过来格式完全乱了。。。{:3_92:}

去原帖地址看吧。。就是下面那个链接。。。

这是由鸾霄汉化组负责的无厘头太空战的汉化教程。。希望大家多多支持3dm汉化组哦。。。{:3_118:}

如果可能请贡献自己的一份力量。{:3_113:}




标 题: 【原创】DirectX 9 游戏汉化详解
作 者: noword_forever
时 间: 2010-05-25,14:52:51
链 接: http://bbs.pediy.com/showthread.php?t=113739

【文章标题】: DirectX 9 游戏汉化详解
【文章作者】: noword
【软件名称】: 无厘头太空战役
【下载地址】: http://www.verycd.com/topics/2819995/
--------------------------------------------------------------------------------
【前言】
先copy一段此游戏介绍:

这是一个独特的战略游戏,具有即时战略与塔防的混合风格,玩家将扮演庞大太空舰队的最高指挥官,你可以自定飞船的构造,摆放飞船的位置,下达命令,然后观看绚丽的射击与爆炸。移动和爆炸时会有动态模糊效果。支持自定义地图。

想玩中文版,两个游戏论坛,3DM和YX上,都有人说要汉化,等了几个月,没有下文,说是技术原因。于是决定自己来试试看。


【困难何在】
此游戏的文本都在data目录下,都是明文的文本文件。修改data\strings.ini,将
   代码:
MAINMENU_QUIT      = "Exit"
改成
   代码:
MAINMENU_QUIT      = "退出"

进入游戏后,不出所料,无法显示此中文。

茫茫多的游戏爱好者,在尝试汉化某款自己心仪的游戏时,都是死在了这一步——找了半天文本资源,然后翻成中文,满心欢喜和期待的进入游戏,面对的却是一堆乱码或一片空白。满腔热情,化为乌有,无可奈何,黯然神伤。

本文的目的,就是希望能够帮助这些有志于游戏汉化的同学,主要介绍了如何让一个英文的游戏,能够正确的显示出中文。


【调试分析】
DirectX 9游戏的启动流程是这样的,先执行Direct3DCreate9,返回值是一个IDirect3D9句柄,然后执行IDirect3D9->CreateDevice,得到IDirect3DDevice9句柄。
有了IDirect3DDevice9就能使用DirectX 9的一切绘图手段,而我们最关心的就是能够使用D3DXCreateFont来创建ID3DXFont,继而能够非常方便快捷的在游戏中显示文字。

用ODBG载入游戏的exe文件,“查找所有模块间的调用”,找“d3d9.Direct3DCreate9”:
   代码:
00501974   .BB 500A5400      mov   ebx, 00540A50                   ;ASCII "Initialising 3D Engine"00501979   .E8 22130000      call    00502CA00050197E   .6A 20            push    2000501980   .8977 18            mov   dword ptr , esi00501983   .E8 2E8B0100      call    <jmp.&d3d9.Direct3DCreate9>00501988   .85C0               test    eax, eax0050198A   .8947 10            mov   dword ptr , eax         ;edi+10 = 58d550
往下找,就能找到IDirect3D9->CreateDevice:
   代码:
00501B4C   .8D77 14            lea   esi, dword ptr 00501B4F   .56               push    esi                              ;58d554=> IDirect3DDevice900501B50   .8D4F 40            lea   ecx, dword ptr 00501B53   .51               push    ecx00501B54   .6A 40            push    4000501B56   .EB 14            jmp   short 00501B6C...00501B6C   >8B4F 18            mov   ecx, dword ptr 00501B6F   .8B47 10            mov   eax, dword ptr 00501B72   .8B10               mov   edx, dword ptr 00501B74   .8B52 40            mov   edx, dword ptr 00501B77   .51               push    ecx00501B78   .8B4C24 24          mov   ecx, dword ptr 00501B7C   .51               push    ecx00501B7D   .55               push    ebp00501B7E   .50               push    eax00501B7F   .FFD2               call    edx                           ;IDirect3D9::CreateDevice

由于是所谓的COM接口,没有十分明显的标志,不是很好找。

在微软的DirectX SDK d3d9.h文件中,IDirect3D9的接口是这样的:

   代码:
DECLARE_INTERFACE_(IDirect3D9, IUnknown){      /*** IUnknown methods ***/      STDMETHOD(QueryInterface)(THIS_ REFIID riid, void** ppvObj) PURE;      STDMETHOD_(ULONG,AddRef)(THIS) PURE;      STDMETHOD_(ULONG,Release)(THIS) PURE;      /*** IDirect3D9 methods ***/      STDMETHOD(RegisterSoftwareDevice)(THIS_ void* pInitializeFunction) PURE;      STDMETHOD_(UINT, GetAdapterCount)(THIS) PURE;      STDMETHOD(GetAdapterIdentifier)(THIS_ UINT Adapter,DWORD Flags,D3DADAPTER_IDENTIFIER9* pIdentifier) PURE;      STDMETHOD_(UINT, GetAdapterModeCount)(THIS_ UINT Adapter,D3DFORMAT Format) PURE;      STDMETHOD(EnumAdapterModes)(THIS_ UINT Adapter,D3DFORMAT Format,UINT Mode,D3DDISPLAYMODE* pMode) PURE;      STDMETHOD(GetAdapterDisplayMode)(THIS_ UINT Adapter,D3DDISPLAYMODE* pMode) PURE;      STDMETHOD(CheckDeviceType)(THIS_ UINT Adapter,D3DDEVTYPE DevType,D3DFORMAT AdapterFormat,D3DFORMAT BackBufferFormat,BOOL bWindowed) PURE;      STDMETHOD(CheckDeviceFormat)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT AdapterFormat,DWORD Usage,D3DRESOURCETYPE RType,D3DFORMAT CheckFormat) PURE;      STDMETHOD(CheckDeviceMultiSampleType)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT SurfaceFormat,BOOL Windowed,D3DMULTISAMPLE_TYPE MultiSampleType,DWORD* pQualityLevels) PURE;      STDMETHOD(CheckDepthStencilMatch)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT AdapterFormat,D3DFORMAT RenderTargetFormat,D3DFORMAT DepthStencilFormat) PURE;      STDMETHOD(CheckDeviceFormatConversion)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT SourceFormat,D3DFORMAT TargetFormat) PURE;      STDMETHOD(GetDeviceCaps)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DCAPS9* pCaps) PURE;      STDMETHOD_(HMONITOR, GetAdapterMonitor)(THIS_ UINT Adapter) PURE;      STDMETHOD(CreateDevice)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,HWND hFocusWindow,DWORD BehaviorFlags,D3DPRESENT_PARAMETERS* pPresentationParameters,IDirect3DDevice9** ppReturnedDeviceInterface) PURE;            #ifdef D3D_DEBUG_INFO      LPCWSTR Version;      #endif};

CreateDevice是第17个函数,所以它的地址是(17-1)*4 = 0x40,
00501B74   .8B52 40            mov   edx, dword ptr
这里的edx+40就是这么来的。

如果觉得算起来很麻烦的话,还有一个简单的方法,可以自己编译一个d3d9的程序,然后反汇编看看。

如果还觉得麻烦,还有个更简单的方法,直接往下找,通常会有一些调试文本能够帮助定位,例如:
00501B86   .BB 600B5400      mov   ebx, 00540B60                   ;ASCII "CreateDevice"
...
00501BBB   .BB 700B5400      mov   ebx, 00540B70                   ;ASCII "CreateDevice failed again"
当然,这个办法并不通用,有效与否完全要看游戏作者的脸色。

知道了IDirect3DDevice9的地址,就能植入我们自己的初始化ID3DXFont的代码。

有了ID3DXFont,下面就是要找到用于显示文字的函数,并用我们自己的代码来替换实现。

随便找一段游戏中出现的文字,比如开始菜单上出现的“Full 1.37”,ALT-M,进入内存窗口,在所有搜到的该字符串上下“内存访问”断点,最终会找到地址在4FFF50的一个函数。

该函数返回时,用的是“retn 14”,在4FFF50处用“retn 14”改写,切回到游戏后没有任何字符出现,说明这个函数正是用来显示字符的。

原谅我在这里对于怎么找到4FFF50的过程含糊其辞,一笔带过了。确实没有什么取巧的方法,完全取决于破解的功力,良好的耐力,以及一点点好运气。而这也正是游戏汉化的难点所在。

找到字符串显示的函数后,还要弄清楚该函数的接口。

在屏幕上显示一个字符串,通常需要知道这些要素:字符串、显示的位置(X,Y坐标)、颜色、字符的大小,以及一些flag用于表示左对齐,右对齐,居中,加粗,倾斜等。

前面说过,返回用的是“retn 14”,说明栈里有20(10进制的14h)/4=5个参数,当进入该函数时,栈上的数据是这样的:
   代码:
0012FE84   00453C48返回到 GSB_1_37.00453C48 来自 GSB_1_37.004FFF500012FE88   0012FEB8ASCII "Full 1.37"0012FE8C   000000000012FE90   404000000012FE94   FFFFFFFF0012FE98   447D8000
第一个,很明显就是要显示的字符串,后面几个是什么玩意儿呢?

回到调用004FFF50的地方,在00453C1D下断点:
   代码:
00453C1D|> \D94424 14          fld   dword ptr 00453C21|.51               push    ecx00453C22|.D91C24             fstp    dword ptr                   ;参数51024.000453C25|.6A FF            push    -1                               ;参数400453C27|.D905 F4125400      fld   dword ptr 00453C2D|.83EC 08            sub   esp, 800453C30|.D95C24 04          fstp    dword ptr                 ;参数33.000453C34|.8BD0               mov   edx, eax00453C36|.D9EE               fldz00453C38|.D91C24             fstp    dword ptr                   ;参数20.000453C3B|.56               push    esi                              ;参数100453C3C|.BE 01000000      mov   esi, 100453C41|.8BCE               mov   ecx, esi00453C43|.E8 08C30A00      call    004FFF50

可以看到,参数4是一个固定值,第二、三、五参数都是浮点数。

这里有一个技巧,在函数开始的地方修改参数的值,看看会发生什么变化,很快就能知道参数的作用。

参数4是颜色值,Alpha和RGB值都是FF,就是白色,与在游戏中看到的字符颜色相同。
参数2是X坐标,参数3是Y坐标,参数5用于调整。
需要注意的是,还有两个参数是通过寄存器ECX和EDX传递的,ECX用于表示左对齐(0),右对齐(1)和居中(2),EDX是固定值58D6D0,一个全局的结构或类。

此游戏有两种字体,因此用于显示字符的几大要素,现在还缺一个,就是不知道如何判断字符的大小。

在用于显示文字的004FFF50的上下断点,多跟几次,就会发现,每次调用以前,都会调用一个call:
   代码:
00453BD5|.BE 44035300      mov   esi, 00530344                  ;ASCII "zekton16.dds"00453BDA|.E8 91B50A00      call    004FF170    004479E7|.BE 54035300      mov   esi, 00530354                  ;ASCII "cwfont20.dds"004479EC|.E8 7F770B00      call    004FF170
在游戏目录data\font下面有两个文件zekton16.dds.dat和cwfont20.dds.dat,由此判断004FF170应该是用于选择字体的函数。

总结一下:
第一步,找到IDirect3DDevice9句柄,用于初始化ID3DXFont。
第二步,找到显示文字的函数,用自己的代码实现之。
第三步,逐步找到其他需要修改的地方,比如用于得到字符串宽度、高度,指定宽度的字符串换行显示等函数。


【具体实现】
我用的是注入dll,然后打内存补丁的方式。这样的好处是便于更新,可以任意修改实现过程,以后游戏出了新版本,也只要修改几个地址变量,重新编译一下就可以了。
另外一个好处是,不以文件补丁的形式发布,没有版权问题。(有人关心这个吗?)

源码在这里:
http://gsbzhcn.googlecode.com/svn/trunk/src

简单的介绍一下流程:
1.CreateProcess启动游戏的exe,并使之处于挂起状态。
2.VirtualAllocEx在游戏进程上申请一块内存,WriteProcessMemory往里写入要注入的dll名称。
3.GetProcAddress得到LoadLibraryA的地址。
4.CreateRemoteThread运行LoadLibraryA,注入dll,dll载入时会为游戏进程打上补丁。
6.WaitForSingleObject等候dll载入完成。
7.VirtualFreeEx清理掉之前申请的内存。
8.ResumeThread让打过补丁的游戏进程运行起来。

内存补丁主要是让游戏在关键的地方跳转到我们的dll,执行一段代码后再跳回去,或者直接用dll里的函数代替之。


【结尾】
此游戏的汉化正在http://code.google.com/p/gsbzhcn/ 进行,文本不多,奈何翻译人手也不多,希望有兴趣的同学能够参与。

--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!

                                                       2010年05月25日 14:52:11




这些只是个别案例,发出来只是为了供大家参考学习。。

这里有一篇乐神大大发的学习文章,想要学汉化的可以去看看,具体了解下汉化这份工作。


地址:http://bbs.blacksheepgame.com/showtopic-1580338-1.html   



请大家多多顶下那帖,也算为汉化做点贡献吧。。{:3_113:}

xjj5158 发表于 2011-1-27 01:22

{:3_132:}自己顶一个、、沙发、、、

xjj5158 发表于 2011-1-27 12:42

再不回复下这贴就沉下去了。。。。{:3_151:}


大家帮帮忙支持下吧。。。{:3_155:}

miaomiaoyeach 发表于 2011-1-27 14:33

这个!把我吓跑了!

xjj5158 发表于 2011-1-27 15:31

回复 4# miaomiaoyeach

{:3_172:}这东西把很多人都吓跑了。。。

jjxjzlty 发表于 2011-1-27 18:30

我是被吓进来的
具体的代码什么的一点也不懂,让我情何以堪

alan8904 发表于 2011-1-27 18:45

这文章很早就看过了。。但是水平有限,有些懂有些不懂 。。。其实这只是某种游戏其中的一种汉化方法,很多游戏的汉化都要用不同的方法的,所以这些方法不是所有游戏都适用的。。{:3_155:}

dieings 发表于 2011-1-27 19:04

这一大堆的代码·······{:3_145:}

cayenneand 发表于 2011-1-27 19:35

一大堆代码看就晕了。。。。{:3_91:}

xjj5158 发表于 2011-1-27 21:59

回复 7# alan8904


    {:3_193:}嗯。。我知道的。。
看见论坛里有人抱怨没汉化教程,所以我才发的这个。。
希望会有点帮助吧、。。

Ag127 发表于 2011-1-27 23:00

{:3_153:}有点意思!

xjj5158 发表于 2011-1-27 23:15

回复 11# Ag127


    {:3_47:} 有意思就研究一下吧。。

Ag127 发表于 2011-1-27 23:34

回复 12# xjj5158


   哥们,我会研究的,但不会成功的,这么多的汇编语言看着就难受啊。

TGOOLOK 发表于 2011-1-28 00:11

表示完全看不懂

testerhook 发表于 2011-1-28 01:07

这种教学有什么用?
没有汇编和破解的基础,有几个人能看懂?
真正的教学可不是那么好写的。

xjj5158 发表于 2011-1-28 01:24

回复 15# testerhook


    嗯。。谢谢你的意见。。以后我会尽量发点实用的。。{:3_113:}

    我也不懂这些知识,以为对大家会有帮助。{:3_95:}

testerhook 发表于 2011-1-28 01:46

回复 16# xjj5158


    写教学真的很累人的,我也不是专门打消你的积极性。
    你转发的这篇教学,对于有基础的人还是有帮助的。

但是一个普通的程序员,要学会汇编和破解可不是件容易的事情啊。
这里面的水太深了{:3_166:}

xjj5158 发表于 2011-1-28 11:45

回复 17# testerhook


    {:3_92:}看来我果然太天真了。。

denlux 发表于 2011-1-31 15:24

学到些东西,顶起。

batevil 发表于 2011-2-7 14:14

看雪,好像是个很厉害的论坛。

wsgshkkc 发表于 2012-12-29 22:10

          支持

iaozhen 发表于 2012-12-30 02:30

要花点时间看要是会了以后可以汉化些2DGAME

能否追回丶那场 发表于 2012-12-30 03:26

密集恐惧症的路过{:3_174:}
页: [1]
查看完整版本: 看见有人想学汉化,这里有一份看雪论坛的文章,希望能给在这方面有意向的童鞋提供帮助。