本帖最后由 shane007 于 2023-9-5 16:49 编辑 8 i* E6 `) v9 ]$ J5 H
$ g) I6 M, j5 ^' M2 Z1 }( f j4 w+ a
这个游戏也是opengl的游戏,本游戏的和魔法英雄历险记(Tale of a Hero)这个游戏用的是同一个引擎! p. V* `+ }1 f0 M6 Y# L
因此代码基本类似,对glGenLists的调用处也是6处,因此参照以上帖子即可。
; U) ^) [$ U# M8 D ]7 H, C3 xhttps://www.chinaavg.com/forum.php?mod=viewthread&tid=1540817 K; Q. L. z5 W% q, @
第1处
& v; p( F. |& e1 `4 A# z- void sub_451474(char *a1, ...)
( E* X* K! ]; p- W1 f( y - {! ?# H+ H `% v
- GLsizei v1; // eax7 D6 c0 ?( f* D/ V) u; `& |
- char lists; // [esp+4h] [ebp-800h]
3 D! l9 [- k- q. l2 I/ p* _ - va_list va; // [esp+810h] [ebp+Ch]! s; x! a' S2 o; i* {9 F- g
- 0 ?1 _. D/ K2 S, u5 C" p( O( d9 {! ^
- va_start(va, a1);
( q$ y% @7 V* T( F9 a - if ( a1 )
: M& {% C6 [4 D( _! @ - {
8 V! h* R$ H2 U* N! p; y- O' | - vsprintf(&lists, a1, va);
; g6 v; u$ \: l- f - glPushAttrib(0x20000u);5 Y7 L" Z) |) b0 D
- glListBase(base);
& y, Y: W! ]% b0 O - v1 = strlen(&lists);
$ W! `& s0 u* X# u - glCallLists(v1, 0x1401u, &lists);+ s& u2 w4 P' u! J! ^
- glPopAttrib();% U& E) Y; a2 ~9 f
- }" S. b8 [3 k% a( g! }8 R m1 m
- }
复制代码 7 Y( ^" | r* r" {8 p8 Q5 Y
0 X3 ]0 U4 x/ r% t D. }, ^; Y" \2 e$ _0 h9 ?. K8 ?- U" r7 Q! L1 O1 H
第2处
' ?% Z# F& ~: z/ a- void sub_4514F6(GLint x, int a2, int a3, char *a4, ...)- w: s5 J6 F2 f- U1 B# |; a/ J
- {
5 Z$ s! G& }6 s f( {& v - int v4; // eax
, k% K; M# x# O% E - GLsizei v5; // eax
7 B) H1 U# K: x0 O) J - char lists; // [esp+4h] [ebp-800h]! Z! g+ U% i" A6 m7 @! a
- va_list va; // [esp+81Ch] [ebp+18h]% s, i/ x0 w. c) X% N6 ]$ p5 \
+ L, H3 K: Z- a$ _6 x y/ }; {- va_start(va, a4);
" K0 B/ f* ]+ x - if ( a4 )$ H8 p8 ^( T. Z3 p: ]- z9 w% b/ C
- {
8 K: k/ m3 D: o. C3 s% r; w6 u/ \ - sub_44F8A0(a3);
- y" k9 h; `$ X - v4 = sub_40BB44(); R0 K9 p: G! N8 I
- glRasterPos2i(x, v4 - a2);4 `" E8 ^9 b! ~& E3 |9 m/ j$ ~
- vsprintf(&lists, a4, va);% @9 p8 a' q5 C! |) h. e: g
- glPushAttrib(0x20000u);$ s1 L/ \- ~. h5 m4 S% t
- glListBase(base);
* _! l2 Z! W+ ?8 I. e5 O. t - v5 = strlen(&lists);9 q; v# \$ n+ G S0 i
- glCallLists(v5, 0x1401u, &lists);6 a+ `& b$ e/ e# E# o, f: Z- Q
- glPopAttrib();
; R/ r3 z& e9 n \! O% g! u - }
3 M; E0 g) c% ?2 E - }
复制代码
]- A% p% u+ ~& R5 |
( |; O h+ z! T' r+ F2 t) j1 j* r" B6 x: \7 m
第3处; [3 {, w0 p3 i( d
- ) G6 C6 ?. ^8 Q. S6 S1 C
- void sub_451810(int a1, GLint y, int a3, int a4, int a5, GLuint base, char *a7, ...)
. K4 i: I+ ~3 z% U) K/ @& P# M E5 R/ T - {( _' R+ ~2 H1 O# F
- GLvoid *v7; // ST08_4
0 i! {: \5 G/ L+ c& c" C5 ~ - GLsizei v8; // eax
+ H2 i! S% D# I& ]' L% Z4 l8 b3 M - int v9; // [esp+0h] [ebp-1018h]) R( S, N. S7 t/ J4 s E/ v: ?! ~
- char v10; // [esp+8h] [ebp-1010h]
* `2 K8 V2 g z( P( W - GLvoid *lists; // [esp+100Ch] [ebp-Ch]
! ]+ G% G, n( Y' p - char v12[5]; // [esp+1013h] [ebp-5h]2 K' k. j% F9 h8 U( ] q1 I
- va_list va; // [esp+103Ch] [ebp+24h]' w( m! V! b, d9 @2 r& r) p2 r
+ u8 L) b. I; P1 P: H4 S- va_start(va, a7);
3 N! h8 o' x4 F3 g - v9 = 1;
6 I# R4 n M3 p - if ( a7 )
" Q' m6 @/ i E0 K. a' y' [( z! Q - {
" U# o+ j/ H7 a - vsprintf(&v10, a7, va);8 R$ N2 |3 T1 S8 [5 }' D" R9 l( s
- glPushAttrib(0x20000u);
+ {5 {9 q5 x7 V( P3 C - glListBase(base);
) I6 o, S9 \8 R ?# C% p3 X/ x - lists = &v10;# i; Y& i8 x- N
- do$ y& D( t0 J0 i& q) H% S
- {
: d' B( F, e1 z - *(_DWORD *)&v12[1] = lists;
7 x6 ], ?0 Q' e; A3 w0 Z - while ( **(_BYTE **)&v12[1] && **(_BYTE **)&v12[1] != 10 )
. ?9 t; W' U5 u, E - ++*(_DWORD *)&v12[1];
3 O+ C7 W9 G+ d; c2 }3 q - v12[0] = **(_BYTE **)&v12[1];
1 G, H1 Y/ n6 M - **(_BYTE **)&v12[1] = 0;1 X1 T& l. F% e0 a
- if ( v12[0] == 10 && *(GLvoid **)&v12[1] != lists && *(_BYTE *)(*(_DWORD *)&v12[1] - 1) == 13 )
. I* C: x) t2 P - *(_BYTE *)(*(_DWORD *)&v12[1] - 1) = 0;
! \- H0 z0 Q# b2 m - y -= a5;
. F! p1 f0 ?* a5 Z# N - glRasterPos2i(*(_DWORD *)a4 + a1, y);' S: d: ^3 ^+ [ l. ]0 c; _% D
- v7 = lists;
! f0 L0 F2 i$ i+ X! v- g3 R, @ - v8 = strlen((const char *)lists);
$ \5 i o; d: L( G% o( U& \ - glCallLists(v8, 0x1401u, v7);
# O/ ]& W9 X* c0 d/ U" e! n- @ - if ( v9 < a3 )5 w7 q; o, K5 Q+ |" X2 h
- a4 += 4;
: E. R, m7 m; G - ++v9;
) ^3 G/ @7 c) ]# H) v - lists = (GLvoid *)(*(_DWORD *)&v12[1] + 1);1 e; M D6 e+ s/ H% R: t
- }$ b6 w5 ^( I$ j
- while ( v12[0] );
$ ]+ J& b) w. z0 i0 W - glPopAttrib();
) w Q1 t% X- m" Z/ [ - }
" a9 ^: ^6 i8 a7 @( J% b - } h! p2 i: k2 G6 T
复制代码 7 y/ x0 u- U5 z4 G# M+ C3 f
调用处,将 这句nop掉之后,游戏对话字幕不显示,因此,451810是对话显示函数。.text:004512D0 E8 3B 05 00 00 call sub_451810" |3 V% X2 b8 E" _' D3 G o
% w# d/ g1 N; N5 J1 F) [
- .text:004512B1 52 push edx ; char *
/ J6 [$ o5 G/ R# ~* Z6 ^ - .text:004512B2 8B 45 30 mov eax, [ebp+base]9 g0 a# v4 ^% L6 J9 p
- .text:004512B5 50 push eax ; base
" \! h$ Y# E6 K$ I: z/ m* R4 b - .text:004512B6 8B 4D 20 mov ecx, [ebp+arg_18]
1 S2 c4 g6 w0 Z" V - .text:004512B9 51 push ecx ; int
/ z; a1 p" q* ~: ?( l3 K - .text:004512BA 8B 55 1C mov edx, [ebp+arg_14]: K/ l2 ~1 [7 M' _6 x, |, j+ _
- .text:004512BD 52 push edx ; int+ V! x. F( @5 } |9 s
- .text:004512BE 8B 45 18 mov eax, [ebp+arg_10]* V! [6 P( p6 P5 ^" A0 D7 y, h% T
- .text:004512C1 50 push eax ; int
5 E8 y! A5 ~0 a9 Z$ [ - .text:004512C2 8B 4D 0C mov ecx, [ebp+y]
/ @3 S# q+ Y0 c$ {6 O - .text:004512C5 03 4D 14 add ecx, [ebp+arg_C]$ L( U" |$ n) }3 ~
- .text:004512C8 03 4D 24 add ecx, [ebp+arg_1C]
; n* d% I2 F2 H7 a - .text:004512CB 51 push ecx ; y' J. r, q4 S9 P# [
- .text:004512CC 8B 55 08 mov edx, [ebp+x]( {0 H3 F; u& Z! w5 i b
- .text:004512CF 52 push edx ; int. {% \! U) e; p7 N8 F; Q
- .text:004512D0 E8 3B 05 00 00 call sub_451810
复制代码 % T4 K) }; D E8 Y0 x
chatGPT整理过之后,如下
; N2 C$ C2 p3 c3 `$ N0 e3 w. O8 g/ B* {& \' q
- void renderText(int xOffset, GLint yOffset, int numLines, int lineOffset, int lineHeight, GLuint displayListBase, char *text, ...)
& P. h$ @ Y+ y: o2 W* Z( p - {8 ^3 t, O; J6 X0 X9 I. B
- GLvoid *textData; // 存储字符数据的指针. S- G6 d2 \/ f3 K* H
- GLsizei textLength; // 字符数据的长度1 B/ n% Z& c" Z" c/ |
- int lineIndex; // 行索引
2 Y! p& ~+ o2 u1 P - char formattedText[5]; // 存储格式化后的字符数据
8 {, Y2 ^- F2 i: m, M$ G - GLvoid *currentLine; // 当前行的字符数据指针8 P/ X, n" D/ ^4 d1 Z' ~+ `
- char buffer[5]; // 字符数据缓冲区
& o. u7 c8 G% z1 T9 G/ A$ y3 I - va_list args; // 变长参数列表, B4 C" D1 \4 u4 G
: p t& _. ?% J3 U& o- va_start(args, text); // 初始化变长参数列表2 @- L) `% e& D( o3 E; D3 Z* ^
- lineIndex = 1; // 初始化行索引为15 j( P2 j( L, k: g7 w
0 z9 u3 p' S: U8 G- if (text) // 如果文本不为空
: P: m0 p- ?1 d" |: A - {( N4 t7 H7 H" B" |! t8 u
- vsprintf(formattedText, text, args); // 使用变长参数列表格式化字符串,并将结果存储到formattedText中9 h8 B' i4 J' x
- glPushAttrib(0x20000u); // 推送OpenGL属性状态
0 D% o6 r) p! m2 v5 `. ]+ N+ } - glListBase(displayListBase); // 设置显示列表的基础值. Y, h5 ^ R! ^4 x3 t& C
- currentLine = formattedText; // 将currentLine指向formattedText的内存位置
. O& j1 o0 r5 S. B {! G
, R0 m, c- y. V4 H0 d8 K% _3 B7 s: D- do
. Y" g! m4 U8 ?1 k' H - {# X- }! Z7 O1 a' Y2 A5 v
- *(_DWORD *)&buffer[1] = currentLine; // 将currentLine的内存地址存储到buffer数组的第二个元素4 c8 A5 ^6 ?( N# E1 t8 G
, l6 t' ` M) @# a( [/ \- while (**(_BYTE **)&buffer[1] && **(_BYTE **)&buffer[1] != 10) // 循环直到遇到换行符或字符串结束
9 g8 N, d* L8 Y" \+ r& Y - ++*(_DWORD *)&buffer[1]; // 递增buffer数组的第二个元素
. }5 g6 W& P8 n0 |4 ~" r - 4 J; {# ^4 |9 a6 J/ h
- buffer[0] = **(_BYTE **)&buffer[1]; // 将换行符存储到buffer数组的第一个元素
# J% G- C: P- d/ l% {5 v9 K0 f - **(_BYTE **)&buffer[1] = 0; // 将换行符替换为字符串结束符
, P, K4 {' ]4 t0 E) M2 N - ) B; p3 u+ j7 z R% n; q
- if (buffer[0] == 10 && *(GLvoid **)&buffer[1] != currentLine && *(_BYTE *)(*(_DWORD *)&buffer[1] - 1) == 13)6 S' D! l! ]# l4 D% b
- *(_BYTE *)(*(_DWORD *)&buffer[1] - 1) = 0; // 处理换行符和回车符% ?, j: c: }: P: j! }9 e# @/ j
' b/ r9 [+ L! h$ }- yOffset -= lineHeight; // 更新y坐标
4 \5 u) @) U, B5 ~2 _ q - glRasterPos2i(*(_DWORD *)lineOffset + xOffset, yOffset); // 设置光栅化位置
. h( n; Y7 n! V- w) G4 c - textData = currentLine; // 将textData指向currentLine- o- l. A/ ~& Y3 }: e
- textLength = strlen((const char *)textData); // 计算字符数据的长度
0 G9 M3 h# F9 Z U b# R. u - glCallLists(textLength, 0x1401u, textData); // 调用显示列表' T/ H8 b9 k& V& s) |2 o# C# Y
- % d( m5 ?7 O* _/ t* G7 } ]
- if (lineIndex < numLines)
3 L( N9 I& b5 V - lineOffset += 4; // 更新lineOffset9 L5 _7 t5 t6 w- @
- ++lineIndex; // 递增行索引$ ]. A" b' D: M
- currentLine = (GLvoid *)(*(_DWORD *)&buffer[1] + 1); // 更新currentLine指向下一个字符数据# P9 g: i2 |+ ]6 ~) Y1 e
- }& @4 }6 L1 `* k
- while (buffer[0]); // 当字符数组buffer的第一个元素不为0时继续循环
/ V4 t9 C! ]/ r3 U4 B# q+ f - glPopAttrib(); // 恢复OpenGL属性状态, Z6 |/ A0 l. K" W+ c' A* A
- }
9 s/ Q3 B7 u( K' r- S% G2 g' \ - }
5 g; p7 A" d# A4 Q* R
复制代码 & o7 W$ K- g4 K! r( M6 |4 }% W
含义解释如下,设置字符串颜色还需要寻找,也许是在调用此方法之前
0 ^/ }7 [/ p+ \" p
2 R# L" h9 T+ K6 N2 _8 C3 yint xOffset, 字幕的x坐标) s/ b$ H! t) E: d. g: R( h1 d
GLint yOffset, 字幕的y坐标
5 t: u, @7 B/ m- x; Iint numLines, 字幕的行数(较长的字幕为2,一般为1): G7 e! K! K' G+ q
int lineOffset, 这个需要再研究
1 L% F/ n: o1 Y h! ?int lineHeight, 字体高度(或行高)" O+ K0 U0 j' [$ n& `1 h" o
GLuint displayListBase, 这个字体对应的base值
# N& S; T3 d% R% W8 Rchar *text, ... 字符串
; f/ v8 B! @$ n# s) Y" f; S
/ P6 { {5 V- L+ _9 g+ Q. Q |