看见有人想学汉化,这里有一份看雪论坛的文章,希望能给在这方面有意向的童鞋提供帮助。
本帖最后由 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:} {:3_132:}自己顶一个、、沙发、、、 再不回复下这贴就沉下去了。。。。{:3_151:}
大家帮帮忙支持下吧。。。{:3_155:} 这个!把我吓跑了! 回复 4# miaomiaoyeach
{:3_172:}这东西把很多人都吓跑了。。。 我是被吓进来的
具体的代码什么的一点也不懂,让我情何以堪 这文章很早就看过了。。但是水平有限,有些懂有些不懂 。。。其实这只是某种游戏其中的一种汉化方法,很多游戏的汉化都要用不同的方法的,所以这些方法不是所有游戏都适用的。。{:3_155:} 这一大堆的代码·······{:3_145:} 一大堆代码看就晕了。。。。{:3_91:} 回复 7# alan8904
{:3_193:}嗯。。我知道的。。
看见论坛里有人抱怨没汉化教程,所以我才发的这个。。
希望会有点帮助吧、。。 {:3_153:}有点意思! 回复 11# Ag127
{:3_47:} 有意思就研究一下吧。。 回复 12# xjj5158
哥们,我会研究的,但不会成功的,这么多的汇编语言看着就难受啊。 表示完全看不懂 这种教学有什么用?
没有汇编和破解的基础,有几个人能看懂?
真正的教学可不是那么好写的。 回复 15# testerhook
嗯。。谢谢你的意见。。以后我会尽量发点实用的。。{:3_113:}
我也不懂这些知识,以为对大家会有帮助。{:3_95:} 回复 16# xjj5158
写教学真的很累人的,我也不是专门打消你的积极性。
你转发的这篇教学,对于有基础的人还是有帮助的。
但是一个普通的程序员,要学会汇编和破解可不是件容易的事情啊。
这里面的水太深了{:3_166:} 回复 17# testerhook
{:3_92:}看来我果然太天真了。。 学到些东西,顶起。 看雪,好像是个很厉害的论坛。 支持 要花点时间看要是会了以后可以汉化些2DGAME 密集恐惧症的路过{:3_174:}
页:
[1]