冒险解谜游戏中文网 ChinaAVG

标题: 【转】DirectX 9 游戏汉化详解 [打印本页]

作者: shane007    时间: 2011-1-30 14:12
标题: 【转】DirectX 9 游戏汉化详解
本帖最后由 shane007 于 2011-1-30 14:13 编辑 ) X: k  o# B2 r; D! I7 \
; J% v& v- k7 f6 y& g: z; T4 F
作 者: noword_forever% _, y& f2 _1 O& n
时 间: 2010-05-25,14:52:51# k. p' ~& R, z' S: H6 d  R9 {$ o. q
链 接: http://bbs.pediy.com/showthread.php?t=113739. \- P6 {; y; q) c  R

7 d4 p& O( V( Q9 i0 }【文章标题】: DirectX 9 游戏汉化详解
% D4 L) q2 ?5 S+ F. k【文章作者】: noword1 Q) v. E2 R1 l5 g
【软件名称】: 无厘头太空战役
: S  B# v; X1 T0 u/ m! }4 L! j【下载地址】: http://www.verycd.com/topics/2819995/
% v0 ]5 v" A8 U' ^  L--------------------------------------------------------------------------------; ?2 c: F( ?- P. j% U: ?
  【前言】
& `1 i! l6 ], _# }' Z3 B  先copy一段此游戏介绍:0 Z( p1 J2 G* m* Q% w. I- l. j
  3 E$ }: r; Y2 @/ A
  这是一个独特的战略游戏,具有即时战略与塔防的混合风格,玩家将扮演庞大太空舰队的最高指挥官,你可以自定飞船的构造,摆放飞船的位置,下达命令,然后观看绚丽的射击与爆炸。移动和爆炸时会有动态模糊效果。支持自定义地图。1 G* Q: e- V: i2 w0 y  E
  - c2 a- `& g6 ]9 l7 h
  想玩中文版,两个游戏论坛,3DM和YX上,都有人说要汉化,等了几个月,没有下文,说是技术原因。于是决定自己来试试看。
6 b9 @4 L1 @  S8 P! [. b! E. x( Z  9 g1 A- X  r8 O+ G" X
  & Y& L9 L% k( a
  【困难何在】+ K. ~$ ]; ~% a5 R$ w9 L1 @
  此游戏的文本都在data目录下,都是明文的文本文件。修改data\strings.ini,将2 \& {: [( }6 e9 k2 M4 ]" e1 Q, f3 q* Y
  ' k" C5 d1 a' T, S$ [
代码:2 x. r4 |6 @2 L# e
  MAINMENU_QUIT        = "Exit"
2 P& D/ R) l& w& E$ T# q  4 b% y2 i# w$ a
  改成
, L; f, H1 b% [7 m. n1 w1 ]" t  
( N0 o- d. P. d) s代码:
# o/ s1 D: {8 U0 |! ~4 J  MAINMENU_QUIT        = "退出"9 P5 j' b( m+ C' N0 Y9 E
  
  Y& i, H9 p8 U3 R  `3 n  g  ; T7 |! o) ~" Z; v0 J
  进入游戏后,不出所料,无法显示此中文。  r* w9 K# q) b1 X. h. F
  5 O* X) @& C9 I* X
  茫茫多的游戏爱好者,在尝试汉化某款自己心仪的游戏时,都是死在了这一步——找了半天文本资源,然后翻成中文,满心欢喜和期待的进入游戏,面对的却是一堆乱码或一片空白。满腔热情,化为乌有,无可奈何,黯然神伤。5 |* ^1 \) f! ]& z. k' v5 h
  ' N6 U( \  w! h9 i+ V
  本文的目的,就是希望能够帮助这些有志于游戏汉化的同学,主要介绍了如何让一个英文的游戏,能够正确的显示出中文。  Q! C2 v( ]# x  E
  
% _( M! Y* k3 G  w9 Y7 B  
8 A, r# [7 W8 F/ p  【调试分析】' z4 k% e/ B6 J6 T
  DirectX 9游戏的启动流程是这样的,先执行Direct3DCreate9,返回值是一个IDirect3D9句柄,然后执行IDirect3D9->CreateDevice,得到IDirect3DDevice9句柄。  b% }/ Q$ O7 V# _
  有了IDirect3DDevice9就能使用DirectX 9的一切绘图手段,而我们最关心的就是能够使用D3DXCreateFont来创建ID3DXFont,继而能够非常方便快捷的在游戏中显示文字。
$ [: s2 U. n  R9 C- S0 J  8 @; {  y" a; b1 ?# o% y1 q2 U5 Z; y
  用ODBG载入游戏的exe文件,“查找所有模块间的调用”,找“d3d9.Direct3DCreate9”:4 G7 C" t! k  I" Q  w: v6 V
  ' u3 @  u- [$ i  @( K
代码:
' h" S: \5 o! [" \: {8 J0 p2 ^  00501974   .  BB 500A5400        mov     ebx, 00540A50                   ;  ASCII "Initialising 3D Engine"  b- h& j2 u; I. [
  00501979   .  E8 22130000        call    00502CA01 p4 X4 K% e* N9 R2 b( J
  0050197E   .  6A 20              push    202 v% T/ a  A! B
  00501980   .  8977 18            mov     dword ptr [edi+18], esi
1 N0 e; k# r7 U% M0 n+ v# A! t  P  00501983   .  E8 2E8B0100        call    <jmp.&d3d9.Direct3DCreate9>
4 a" ?9 \" S3 _# ~1 l4 N  00501988   .  85C0               test    eax, eax1 S( V/ u2 d( r6 N1 }& R* I
  0050198A   .  8947 10            mov     dword ptr [edi+10], eax         ;  edi+10 = 58d550
) i& e4 a" G5 ?* W0 I( L& z# Y  
  b& b6 E' h, R, m6 L  往下找,就能找到IDirect3D9->CreateDevice:1 i8 S( }  Q6 M6 w( R+ M8 J+ X
  
% D( \; r: D; O% z代码:8 a; `; O6 @- `3 Z' b0 _( N
  00501B4C   .  8D77 14            lea     esi, dword ptr [edi+14]* j. s1 j( h$ k) D: b, v
  00501B4F   .  56                 push    esi                              ;  58d554  => IDirect3DDevice9
1 `  M5 a3 M% N  00501B50   .  8D4F 40            lea     ecx, dword ptr [edi+40]4 T. c# Q1 Z5 G- i; o7 p) E1 @$ W0 X% z
  00501B53   .  51                 push    ecx, _; S- n# K3 C) u3 [! D- @& Q
  00501B54   .  6A 40              push    40
: w5 v* B5 ~2 D- V2 O  00501B56   .  EB 14              jmp     short 00501B6C
' D$ G9 _6 D) O2 i, f, w+ _3 ]  ...
; t' C  D8 @: W  00501B6C   >  8B4F 18            mov     ecx, dword ptr [edi+18]; |3 \6 d& A1 h6 C* R$ w
  00501B6F   .  8B47 10            mov     eax, dword ptr [edi+10]
0 H% l, a9 \$ a$ f! ^. u9 d: o  00501B72   .  8B10               mov     edx, dword ptr [eax]0 a+ q% |: I. ~$ |! F& W
  00501B74   .  8B52 40            mov     edx, dword ptr [edx+40]
3 w+ f5 ^5 J" Y; ?# a9 z6 }  00501B77   .  51                 push    ecx$ e% o4 \9 S. _4 B. H
  00501B78   .  8B4C24 24          mov     ecx, dword ptr [esp+24]
$ e0 X1 X% |/ n( \4 n- Q- i  00501B7C   .  51                 push    ecx
! L& O# m$ ]5 i& }  00501B7D   .  55                 push    ebp
& _& f; o. V: f) x  00501B7E   .  50                 push    eax4 p7 q1 o' P1 ?/ G: a
  00501B7F   .  FFD2               call    edx                             ;  IDirect3D9::CreateDevice
) J7 k2 ^' `8 M7 H  
% e: T+ j) H; U- o3 g1 Q( ]  + J. `; K# k6 P# p1 V
  由于是所谓的COM接口,没有十分明显的标志,不是很好找。, g; V6 g2 ]: w! U) H6 h* Z+ _. d
  " f" W% @$ d3 z) k, c. E
  在微软的DirectX SDK d3d9.h文件中,IDirect3D9的接口是这样的:2 ^6 S7 i7 }' p) ^/ w+ p0 I
  : q: [- V1 {( X1 c% h+ ?. l6 B
  
: t1 ]6 E. u4 k5 e代码:
. q6 x! v: D3 h" Q" G2 Y. V  DECLARE_INTERFACE_(IDirect3D9, IUnknown)6 a3 _! B6 K8 a" a) i
  {
$ m2 L( R+ k: c, ], D) p+ Z0 o      /*** IUnknown methods ***/
0 b6 j$ V* {" w; K      STDMETHOD(QueryInterface)(THIS_ REFIID riid, void** ppvObj) PURE;( H4 b& P  V. j+ J" ]
      STDMETHOD_(ULONG,AddRef)(THIS) PURE;& |2 f7 M$ ]. X: d9 o
      STDMETHOD_(ULONG,Release)(THIS) PURE;6 b3 G, G* N) @0 P1 q. W5 R
  ! x  ], B$ v4 R% O
      /*** IDirect3D9 methods ***/0 r4 O% C9 H) o  m. F# @- ]
      STDMETHOD(RegisterSoftwareDevice)(THIS_ void* pInitializeFunction) PURE;
9 N+ O" g* m4 ^3 n6 V# j  I      STDMETHOD_(UINT, GetAdapterCount)(THIS) PURE;$ m9 d1 C/ p) ~& i$ y
      STDMETHOD(GetAdapterIdentifier)(THIS_ UINT Adapter,DWORD Flags,D3DADAPTER_IDENTIFIER9* pIdentifier) PURE;
9 Y$ H* m: R2 f      STDMETHOD_(UINT, GetAdapterModeCount)(THIS_ UINT Adapter,D3DFORMAT Format) PURE;
; A" u8 b# Y( A9 J. a+ `3 i8 [9 j: E      STDMETHOD(EnumAdapterModes)(THIS_ UINT Adapter,D3DFORMAT Format,UINT Mode,D3DDISPLAYMODE* pMode) PURE;
. k5 k: Y+ b; |      STDMETHOD(GetAdapterDisplayMode)(THIS_ UINT Adapter,D3DDISPLAYMODE* pMode) PURE;
  ?8 X9 M' ~2 P6 X. C. r0 X      STDMETHOD(CheckDeviceType)(THIS_ UINT Adapter,D3DDEVTYPE DevType,D3DFORMAT AdapterFormat,D3DFORMAT BackBufferFormat,BOOL bWindowed) PURE;
$ C. F+ ]% `! o) y8 x      STDMETHOD(CheckDeviceFormat)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT AdapterFormat,DWORD Usage,D3DRESOURCETYPE RType,D3DFORMAT CheckFormat) PURE;
6 U) a1 k1 f' ]6 w4 ]7 H+ Q      STDMETHOD(CheckDeviceMultiSampleType)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT SurfaceFormat,BOOL Windowed,D3DMULTISAMPLE_TYPE MultiSampleType,DWORD* pQualityLevels) PURE;
& F# z  _' G+ u( z9 z& O: _8 p$ E5 g      STDMETHOD(CheckDepthStencilMatch)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT AdapterFormat,D3DFORMAT RenderTargetFormat,D3DFORMAT DepthStencilFormat) PURE;
6 t+ w  v! ?4 N9 T' H5 c2 _7 I! |      STDMETHOD(CheckDeviceFormatConversion)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DFORMAT SourceFormat,D3DFORMAT TargetFormat) PURE;2 m. o) e7 Z" P% \: D/ H2 O; g
      STDMETHOD(GetDeviceCaps)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,D3DCAPS9* pCaps) PURE;
. q* {+ w' t. |  t9 O) H( }      STDMETHOD_(HMONITOR, GetAdapterMonitor)(THIS_ UINT Adapter) PURE;" e# |% K9 c7 L3 O0 M% x* @2 C
      STDMETHOD(CreateDevice)(THIS_ UINT Adapter,D3DDEVTYPE DeviceType,HWND hFocusWindow,DWORD BehaviorFlags,D3DPRESENT_PARAMETERS* pPresentationParameters,IDirect3DDevice9** ppReturnedDeviceInterface) PURE;1 u4 ]! L0 w* U
      
; M+ k; Y2 b, C0 Q6 j4 F      #ifdef D3D_DEBUG_INFO
( `/ m, |7 Q5 e% U5 B, {. J8 O      LPCWSTR Version;
# W/ P8 x/ C3 z  x      #endif& t" D; j: y! M$ u% P
  };
8 e( ^2 D, e" A) V# B  
% S. h3 f! g. |' J' K$ P& l8 o5 w" [  7 d, [2 K9 ]( O( N( X' y3 L
  CreateDevice是第17个函数,所以它的地址是(17-1)*4 = 0x40,
: O6 s5 x! ^. s% E$ |9 [/ u7 [  00501B74   .  8B52 40            mov     edx, dword ptr [edx+40]; C+ K- k1 J2 B% y) X. y: Q' o
  这里的edx+40就是这么来的。
' F3 l; Y4 T. k/ I3 q5 n  
$ P2 x4 P# ^8 x/ V* P  如果觉得算起来很麻烦的话,还有一个简单的方法,可以自己编译一个d3d9的程序,然后反汇编看看。
7 X5 S5 n+ e. U7 e0 I. C7 ?  
4 G5 H0 R3 k. @* k. d  如果还觉得麻烦,还有个更简单的方法,直接往下找,通常会有一些调试文本能够帮助定位,例如:# R, D; J+ X! `7 A, q
  00501B86   .  BB 600B5400        mov     ebx, 00540B60                   ;  ASCII "CreateDevice"4 i: X* x8 W0 l8 b9 N0 l# L
  ...# c: L! K! n5 i  F6 }" V
  00501BBB   .  BB 700B5400        mov     ebx, 00540B70                   ;  ASCII "CreateDevice failed again"( O& T+ u' c  q% z4 m# b% D6 S
  当然,这个办法并不通用,有效与否完全要看游戏作者的脸色。
" h- [% |: |* A0 a1 s  
' R% _) G6 |" q  知道了IDirect3DDevice9的地址,就能植入我们自己的初始化ID3DXFont的代码。
3 x7 |$ m9 E* j  
! x* c' Q: o: B" O( H  有了ID3DXFont,下面就是要找到用于显示文字的函数,并用我们自己的代码来替换实现。9 O, h+ L, b- Q$ V
  $ U5 q8 \( t& \
  随便找一段游戏中出现的文字,比如开始菜单上出现的“Full 1.37”,ALT-M,进入内存窗口,在所有搜到的该字符串上下“内存访问”断点,最终会找到地址在4FFF50的一个函数。
9 ~8 J: L' u$ e7 A+ [  $ |& v0 l, A3 I3 s" l
  该函数返回时,用的是“retn 14”,在4FFF50处用“retn 14”改写,切回到游戏后没有任何字符出现,说明这个函数正是用来显示字符的。. G& w  [5 T! y0 J. f' A
  ' r2 {* P3 p8 F% M
  原谅我在这里对于怎么找到4FFF50的过程含糊其辞,一笔带过了。确实没有什么取巧的方法,完全取决于破解的功力,良好的耐力,以及一点点好运气。而这也正是游戏汉化的难点所在。3 N5 s& y$ p# Q/ m9 R/ q7 C
  
5 s6 f' |6 j' {7 G0 \  找到字符串显示的函数后,还要弄清楚该函数的接口。
. x3 B+ f; q/ Z( n( m  
) O2 _* L8 A. V, g2 \4 S+ e  在屏幕上显示一个字符串,通常需要知道这些要素:字符串、显示的位置(X,Y坐标)、颜色、字符的大小,以及一些flag用于表示左对齐,右对齐,居中,加粗,倾斜等。
- i  C- N( x1 U/ v+ F& o0 ~+ \  7 o, R& \- Z# \+ Q& _- f+ ]
  前面说过,返回用的是“retn 14”,说明栈里有20(10进制的14h)/4=5个参数,当进入该函数时,栈上的数据是这样的:
0 z3 M0 Z3 q& u* y  6 \- Q1 ~8 Z- j$ j
代码:0 K1 J3 h, s/ D- ?5 _9 P: f
  0012FE84   00453C48  返回到 GSB_1_37.00453C48 来自 GSB_1_37.004FFF50  \; ]6 Q2 a9 P1 x
  0012FE88   0012FEB8  ASCII "Full 1.37"; y/ {" |+ `% ^
  0012FE8C   00000000
1 O5 q* ~: Z6 p, R: a. q, o  0012FE90   40400000
+ Q/ V& I! B3 l7 d4 X5 p  0012FE94   FFFFFFFF/ ?: g2 k) Q" o* }$ s- a
  0012FE98   447D80006 U) I( A6 S; F' X
  ' g9 c) m8 N/ |6 E: f7 H
  第一个,很明显就是要显示的字符串,后面几个是什么玩意儿呢?' ?& I/ M$ L1 z  O1 F4 ~
  ! L+ n. a. N/ d7 _- F% L) f" u$ f
  回到调用004FFF50的地方,在00453C1D下断点:( [' \6 }) t0 b
  9 D  s" O7 y! `; y% D2 a
代码:
" d9 `% L# u9 a4 E" E3 Z& o  00453C1D  |> \D94424 14          fld     dword ptr [esp+14]
# C( T' A+ v1 k6 ^  00453C21  |.  51                 push    ecx
+ ?/ r+ U2 f$ U( w  00453C22  |.  D91C24             fstp    dword ptr [esp]                  ;  参数5  1024.09 n5 x& M2 [; U. h
  00453C25  |.  6A FF              push    -1                               ;  参数4/ ~; D8 h& L- K& L
  00453C27  |.  D905 F4125400      fld     dword ptr [5412F4]
1 l% s1 l9 ^+ u  00453C2D  |.  83EC 08            sub     esp, 8
) x3 x' \/ P' H' W  00453C30  |.  D95C24 04          fstp    dword ptr [esp+4]                ;  参数3  3.0$ ^% D- L1 _8 T0 i
  00453C34  |.  8BD0               mov     edx, eax
2 Z) G; g4 o  _  00453C36  |.  D9EE               fldz# f' [/ K2 q( K5 @+ F. [
  00453C38  |.  D91C24             fstp    dword ptr [esp]                  ;  参数2  0.0% @3 Y5 \" U/ C
  00453C3B  |.  56                 push    esi                              ;  参数1( B! z1 c6 K9 c' e8 y* B" ?
  00453C3C  |.  BE 01000000        mov     esi, 1
. X2 |" N" ]& T4 i6 U( p" Y; y3 V  00453C41  |.  8BCE               mov     ecx, esi: c1 Q0 ~/ S) K) I/ x
  00453C43  |.  E8 08C30A00        call    004FFF508 x- N0 v$ D0 b+ b7 t8 E; N" l
  
7 e4 ]$ j5 I' ?/ t8 r- i& V" P( I  3 G* o7 @) i0 A4 T9 v- y' Q
  可以看到,参数4是一个固定值,第二、三、五参数都是浮点数。
0 C: v- c8 M# |  }7 i  8 O3 }  i# u0 E+ _4 j" `
  这里有一个技巧,在函数开始的地方修改参数的值,看看会发生什么变化,很快就能知道参数的作用。
0 R: ^1 b; D! X& Z  
# V" m' S4 A5 j' Z8 R# R  参数4是颜色值,Alpha和RGB值都是FF,就是白色,与在游戏中看到的字符颜色相同。5 N8 m' u: Z% M: }$ U
  参数2是X坐标,参数3是Y坐标,参数5用于调整。2 {7 A9 a( b6 {- ]
  需要注意的是,还有两个参数是通过寄存器ECX和EDX传递的,ECX用于表示左对齐(0),右对齐(1)和居中(2),EDX是固定值58D6D0,一个全局的结构或类。
. k: W% {7 N& M$ i' b  1 ]0 |$ A' o! M
  此游戏有两种字体,因此用于显示字符的几大要素,现在还缺一个,就是不知道如何判断字符的大小。9 y3 m, j: b9 X& N. c: Q
  * X. p% L- s" J& @. d' Q) w# p
  在用于显示文字的004FFF50的上下断点,多跟几次,就会发现,每次调用以前,都会调用一个call:) g, a" \7 H* b5 A* h& H2 L
  
+ T& z7 o, V5 U代码:% S7 H8 e3 T( s6 C' y
  00453BD5  |.  BE 44035300        mov     esi, 00530344                    ;  ASCII "zekton16.dds"/ M% k. ~  g) g2 O
  00453BDA  |.  E8 91B50A00        call    004FF170
2 y5 y; B8 d7 `& U  
& a0 l- Y8 @7 s6 k  004479E7  |.  BE 54035300        mov     esi, 00530354                    ;  ASCII "cwfont20.dds"" R8 _  |/ R) X# ]$ L
  004479EC  |.  E8 7F770B00        call    004FF170; R0 q0 G- I$ U' Y
  
  v" a) _8 x) _  在游戏目录data\font下面有两个文件zekton16.dds.dat和cwfont20.dds.dat,由此判断004FF170应该是用于选择字体的函数。$ A: Z" O, h4 u' G
  5 U) K! F( j% {2 H( H1 ^6 q. Z9 k
  总结一下:5 e4 s. o0 p5 U& I6 m
  第一步,找到IDirect3DDevice9句柄,用于初始化ID3DXFont。
1 P3 Q$ F8 i1 V4 s2 R% Q* o  第二步,找到显示文字的函数,用自己的代码实现之。2 @6 b0 t0 f: O7 O. c2 o' c
  第三步,逐步找到其他需要修改的地方,比如用于得到字符串宽度、高度,指定宽度的字符串换行显示等函数。/ ~- u- f' A3 b* F+ I
  $ {( S1 x# Y* {8 X+ B
  3 h2 g0 R; ]# y. j& g6 V: z/ J
  【具体实现】
: Q' H8 a$ l- R  我用的是注入dll,然后打内存补丁的方式。这样的好处是便于更新,可以任意修改实现过程,以后游戏出了新版本,也只要修改几个地址变量,重新编译一下就可以了。6 q* a  r/ ^: I7 z6 Y3 _# f' D# ~
  另外一个好处是,不以文件补丁的形式发布,没有版权问题。(有人关心这个吗?)
6 c( X. s1 Q8 s9 |5 \( j  & h2 ~8 e6 ?) T) q! H, b) Y# b& P" J
  源码在这里:
. v, }8 Z4 B- t" R* \  http://gsbzhcn.googlecode.com/svn/trunk/src; I* U9 J6 e6 F& P  g9 P0 ?
  ( G3 M, C% W; i; |/ \
  简单的介绍一下流程:7 _, T& t1 l8 Y% _3 ]
  1.CreateProcess启动游戏的exe,并使之处于挂起状态。: {* B) P3 ^" z5 n, z' T+ t
  2.VirtualAllocEx在游戏进程上申请一块内存,WriteProcessMemory往里写入要注入的dll名称。
& B' C3 r* s  m/ u8 q' s0 D8 D8 \6 v  3.GetProcAddress得到LoadLibraryA的地址。2 s8 E) H; z; S9 [8 l
  4.CreateRemoteThread运行LoadLibraryA,注入dll,dll载入时会为游戏进程打上补丁。
9 R, F& F: S4 E6 R) J, C  6.WaitForSingleObject等候dll载入完成。$ ~1 \, |3 K* ~2 r3 O
  7.VirtualFreeEx清理掉之前申请的内存。
9 Y- J! ]! s1 z' q' k2 [1 _  8.ResumeThread让打过补丁的游戏进程运行起来。$ E8 r* r9 ~0 x( @- L$ d
  4 a/ F& A2 N( ]( ^- ^
  内存补丁主要是让游戏在关键的地方跳转到我们的dll,执行一段代码后再跳回去,或者直接用dll里的函数代替之。
* k* y* g" s# J4 G0 ]  
: d  y6 D" u2 C8 U# o+ F  
- O. X) v* D9 y2 R* y! C8 S  【结尾】' N) x" Y5 x/ `% c+ ?3 p: Y
  此游戏的汉化正在http://code.google.com/p/gsbzhcn/ 进行,文本不多,奈何翻译人手也不多,希望有兴趣的同学能够参与。% K/ ^6 Z* e1 b+ v
  
& h5 W. v5 Z3 p  D, Z--------------------------------------------------------------------------------' c9 z' Q$ ^- W/ @  ]/ \
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!
" a! N: O+ d$ _
8 s4 j; ^5 O! g/ j0 ~                                                       2010年05月25日 14:52:11
作者: zhdam    时间: 2011-2-6 20:47
楼主忒牛了
作者: tengbin    时间: 2011-3-10 15:19
高手啊,仰望。
作者: 笨蛋狐狸    时间: 2011-3-11 10:19
提示: 作者被禁止或删除 内容自动屏蔽




欢迎光临 冒险解谜游戏中文网 ChinaAVG (https://www.chinaavg.com/) Powered by Discuz! X3.2