冒险解谜游戏中文网 ChinaAVG
标题:
【Opengl游戏汉化 #5】 尼比鲁:秘密年代 (NiBiRu)字幕显示函数
[打印本页]
作者:
shane007
时间:
2023-8-27 13:49
标题:
【Opengl游戏汉化 #5】 尼比鲁:秘密年代 (NiBiRu)字幕显示函数
本帖最后由 shane007 于 2023-9-5 16:49 编辑
u- ?% ~$ O' J- ^0 V! J
) h# |) h! {4 _
这个游戏也是opengl的游戏,本游戏的和魔法英雄历险记(Tale of a Hero)这个游戏用的是同一个引擎
9 J5 J* g# S: S" R7 a* r( R
因此代码基本类似,对glGenLists的调用处也是6处,因此参照以上帖子即可。
8 o1 q7 r" }% [* u
https://www.chinaavg.com/forum.php?mod=viewthread&tid=154081
9 v; J9 O0 |. _# J8 `
第1处
7 F O4 L( w- l& a5 [" e9 j
void sub_451474(char *a1, ...)
5 c* g$ H4 R; H0 T7 H. j
{
- ]; J( m! m7 C
GLsizei v1; // eax
1 O2 w7 Q, W/ O8 v8 ?) [5 e% E
char lists; // [esp+4h] [ebp-800h]
& }; ?/ M* s6 j
va_list va; // [esp+810h] [ebp+Ch]
3 \: E' `4 F* M7 y8 |/ M5 l
3 _! _! A! `9 G
va_start(va, a1);
3 T) |- L4 A: n, W4 b. y/ `& \
if ( a1 )
B. A5 L3 I9 s: N- }; d
{
6 f1 Y( y5 T& j
vsprintf(&lists, a1, va);
& H, E1 t. j, p9 p
glPushAttrib(0x20000u);
% \4 k$ k2 t E1 s: S( z T i' @
glListBase(base);
0 U, x) n: o v' H4 V6 r' R
v1 = strlen(&lists);
( I7 _! R, {% v Z4 D) q: V
glCallLists(v1, 0x1401u, &lists);
; m1 F5 B* k, T ]8 r2 A
glPopAttrib();
$ j/ v+ \- A% S/ T
}
4 m; A/ }6 m* H2 Y
}
复制代码
7 Y2 Z$ G6 k! ^( [
2 W9 W& j# Q" ^4 W* O3 W
- H9 t+ h+ K7 X
第2处
2 y( `" T- p) M* n) f. R( l
void sub_4514F6(GLint x, int a2, int a3, char *a4, ...)
, } P- m& G% w# p! M% `8 F
{
3 k1 h: K! U: C: g) n
int v4; // eax
) e3 `5 C3 S5 P
GLsizei v5; // eax
( Y; ^, K/ f8 S' X0 X! q7 ^/ t5 x
char lists; // [esp+4h] [ebp-800h]
* W; p* W! Z6 T$ i3 O9 T
va_list va; // [esp+81Ch] [ebp+18h]
$ `# g# F- T M
. \. m+ x" y+ \. N
va_start(va, a4);
( i: }% V' |8 X0 {
if ( a4 )
' A* l3 N5 H* e! F
{
" s( Z1 t4 K, w* @7 u
sub_44F8A0(a3);
4 p8 D$ x6 N5 ]9 B# Z
v4 = sub_40BB44();
$ @7 t/ K! [7 c9 x% L B' v& w
glRasterPos2i(x, v4 - a2);
4 @2 U( f% X; w) L% K( J) Q+ n5 w
vsprintf(&lists, a4, va);
: o+ v1 c9 I8 w; k( [$ ?. e
glPushAttrib(0x20000u);
- C, G" o A0 ~- E
glListBase(base);
) b+ {- M2 M, L4 K i' F
v5 = strlen(&lists);
1 U2 A2 d9 a! ]: \( A: Z" l* ~
glCallLists(v5, 0x1401u, &lists);
8 x: O( i. `/ V0 Z) f
glPopAttrib();
5 v7 ?) m9 n1 q/ L
}
2 r2 h# Z& n$ a4 a2 w+ d$ E
}
复制代码
, X2 L0 f: o+ T) F% N
+ I" p3 k' n$ R: u
- N3 H& e. C( e* u( L6 I) ~" \0 H
第3处
- w# W6 } g' q: M% T& J+ q
0 F* X: r5 r6 x+ h( }( W2 t! L
void sub_451810(int a1, GLint y, int a3, int a4, int a5, GLuint base, char *a7, ...)
. H- D- { y4 q+ @! P6 H- M$ r
{
% g4 F. T6 D, X" K% S* H, q
GLvoid *v7; // ST08_4
) V+ `6 y3 Y) }! q# _9 b9 V
GLsizei v8; // eax
! ~; F8 D# G0 r. x( |
int v9; // [esp+0h] [ebp-1018h]
0 k. \' c- u% b- [* G+ R
char v10; // [esp+8h] [ebp-1010h]
2 l; u* } o: Q" x( \ M6 ^8 V
GLvoid *lists; // [esp+100Ch] [ebp-Ch]
5 R- [( w9 `! x% p& |& B3 {
char v12[5]; // [esp+1013h] [ebp-5h]
0 H" {5 \% y% G0 y3 p
va_list va; // [esp+103Ch] [ebp+24h]
; g2 {0 u7 S* n1 ? b
0 F+ V* S& `( ^/ M& v7 M4 d$ B! }0 ~
va_start(va, a7);
! q& {1 r) |) z s3 X6 B& P
v9 = 1;
0 O) A, u: c4 O B& O
if ( a7 )
; t l3 a' s- N1 l" V7 {
{
2 b$ j! z+ k- \2 i- o$ k6 f
vsprintf(&v10, a7, va);
5 [7 [( F; B$ z! f8 ]
glPushAttrib(0x20000u);
: _0 u: V" w! y. U! W6 O: z
glListBase(base);
! I8 o5 e. k0 e* t# z {/ u
lists = &v10;
1 T. S/ _# e! Q7 w5 |
do
: [& y% J6 B8 D; N- \
{
( `' d2 c4 C7 n3 \2 i8 {
*(_DWORD *)&v12[1] = lists;
% y6 C; j/ m' D c; V
while ( **(_BYTE **)&v12[1] && **(_BYTE **)&v12[1] != 10 )
& f7 V/ W s0 C3 f
++*(_DWORD *)&v12[1];
+ p- n' N0 d. X# x
v12[0] = **(_BYTE **)&v12[1];
& ~8 p W' }% B& K- I4 Q
**(_BYTE **)&v12[1] = 0;
& X$ }! s' J: l
if ( v12[0] == 10 && *(GLvoid **)&v12[1] != lists && *(_BYTE *)(*(_DWORD *)&v12[1] - 1) == 13 )
! l' H1 N! V! U( _1 [5 L
*(_BYTE *)(*(_DWORD *)&v12[1] - 1) = 0;
" S( f- o) ~9 A( I1 p- w: ?0 n
y -= a5;
) b/ G0 w& A9 u. K$ _
glRasterPos2i(*(_DWORD *)a4 + a1, y);
* g' b% |( m5 P. p# @
v7 = lists;
* G; z3 g5 c$ S8 _. h v# ?. m
v8 = strlen((const char *)lists);
5 e% Z' _/ @1 ~9 x
glCallLists(v8, 0x1401u, v7);
. c6 c% V2 U! r
if ( v9 < a3 )
5 z& A+ w$ ?& e# t
a4 += 4;
8 H" {; q7 @% Y' J
++v9;
9 b6 A9 k' w6 K8 K) B' q7 e
lists = (GLvoid *)(*(_DWORD *)&v12[1] + 1);
/ m8 E* A X, D7 {, p' ^
}
0 |) B5 E2 G0 S0 Q
while ( v12[0] );
2 @4 G( t# N1 U. l
glPopAttrib();
5 J6 h' |* O/ I6 n% u6 p9 O, @ r( r C
}
( X# N9 E2 c0 B. E) T- g8 z% V7 ?6 X
}
8 P8 r0 T @. T; T) j
复制代码
4 z/ K& C) w/ j" K9 L" J4 O/ O
调用处,将 这句nop掉之后,游戏对话字幕不显示,因此,451810是对话显示函数。.text:004512D0 E8 3B 05 00 00 call sub_451810
1 B# V) g, A3 e, R1 E
% z! t+ b' ~7 U% h/ L
.text:004512B1 52 push edx ; char *
' Z* r" j4 v" _
.text:004512B2 8B 45 30 mov eax, [ebp+base]
3 W& r0 g! s- Z, u9 O& a( T7 V
.text:004512B5 50 push eax ; base
7 K( b9 m3 n2 X' s3 F5 M" w$ ?8 M, X
.text:004512B6 8B 4D 20 mov ecx, [ebp+arg_18]
, S+ ?7 ]' ^0 ^2 H! U% ^1 Q: r5 c. r0 g
.text:004512B9 51 push ecx ; int
8 Z& k8 Q' s; D
.text:004512BA 8B 55 1C mov edx, [ebp+arg_14]
; ~. ^7 t2 a; N" K
.text:004512BD 52 push edx ; int
4 q: E+ t- v9 }# p
.text:004512BE 8B 45 18 mov eax, [ebp+arg_10]
7 F* _6 t: Z, s# H) E! O. Z
.text:004512C1 50 push eax ; int
: B' m( J2 B" S' ?% t$ H( |
.text:004512C2 8B 4D 0C mov ecx, [ebp+y]
1 Q" M4 R& b3 [( ?& a
.text:004512C5 03 4D 14 add ecx, [ebp+arg_C]
5 c' ~ {6 I' E2 o/ h
.text:004512C8 03 4D 24 add ecx, [ebp+arg_1C]
9 m: u e' p( C: k7 v ^, G
.text:004512CB 51 push ecx ; y
/ D% f, T9 ]% X8 d& s( o
.text:004512CC 8B 55 08 mov edx, [ebp+x]
6 ~9 C& _, H1 ]
.text:004512CF 52 push edx ; int
# m& C/ r" ?5 z
.text:004512D0 E8 3B 05 00 00 call sub_451810
复制代码
& t' i- x3 g; Q, C$ N' X
chatGPT整理过之后,如下
- P% Q6 z( R3 l- V3 j. s/ h' ?. [
+ x* C% s5 Q E F
void renderText(int xOffset, GLint yOffset, int numLines, int lineOffset, int lineHeight, GLuint displayListBase, char *text, ...)
1 N( d) l3 k/ p+ f @; E+ E
{
! }/ {% q" X! l. t ]
GLvoid *textData; // 存储字符数据的指针
4 Z% W4 ^2 c" X. W$ ~9 i
GLsizei textLength; // 字符数据的长度
1 G/ s) R; N2 o" |
int lineIndex; // 行索引
8 C |( c# s9 {9 `3 z0 c( V6 _7 d9 E
char formattedText[5]; // 存储格式化后的字符数据
, |) E6 B$ M! r5 t( J/ G0 w
GLvoid *currentLine; // 当前行的字符数据指针
" \- S! {' {4 F: a
char buffer[5]; // 字符数据缓冲区
# J2 v" E: u/ u
va_list args; // 变长参数列表
9 n1 I" A( j' M( v) G: V) R: a
7 d' }* y/ j9 G7 i4 Z' G
va_start(args, text); // 初始化变长参数列表
" I4 f3 @" I2 J
lineIndex = 1; // 初始化行索引为1
' ]/ b- B. Z' f# v4 Y! b5 ]$ |
; H' K+ Y2 S( m, c9 ?
if (text) // 如果文本不为空
) c. D$ q! z( ^# S5 o/ V+ e( ]
{
) K& e% N& _8 D$ b- n3 H' V; L
vsprintf(formattedText, text, args); // 使用变长参数列表格式化字符串,并将结果存储到formattedText中
$ b1 E: V+ R. L" ~
glPushAttrib(0x20000u); // 推送OpenGL属性状态
# t* _5 Y4 E- P, }, y
glListBase(displayListBase); // 设置显示列表的基础值
! M3 u( B8 [0 `) _+ `: I$ W- O
currentLine = formattedText; // 将currentLine指向formattedText的内存位置
. F1 l0 q% G$ g# r* b0 M i4 @
7 C b2 y7 H8 q! Z. A2 H5 w
do
' }: \/ S8 D* u; N3 _- K
{
- l5 p+ l, m. E5 j% c4 |& r! A' _# f) _
*(_DWORD *)&buffer[1] = currentLine; // 将currentLine的内存地址存储到buffer数组的第二个元素
' V9 c: }+ K$ U( e4 r
& X# W) t Q$ {8 Q" z" ^
while (**(_BYTE **)&buffer[1] && **(_BYTE **)&buffer[1] != 10) // 循环直到遇到换行符或字符串结束
4 Q3 M2 Z1 ]* a& P3 S
++*(_DWORD *)&buffer[1]; // 递增buffer数组的第二个元素
, N% o6 }) ^$ A2 o' b/ T/ N$ ?
2 f9 m: y3 l2 W2 B5 g6 o7 p x
buffer[0] = **(_BYTE **)&buffer[1]; // 将换行符存储到buffer数组的第一个元素
/ _- Y% d& r8 Q2 O8 }# g' y3 S3 v
**(_BYTE **)&buffer[1] = 0; // 将换行符替换为字符串结束符
& U: |- O4 O6 g' H0 _
, w5 V5 w+ n! ]0 ?
if (buffer[0] == 10 && *(GLvoid **)&buffer[1] != currentLine && *(_BYTE *)(*(_DWORD *)&buffer[1] - 1) == 13)
' A$ ?! d& n* K" A; m% O4 R# Q5 Z
*(_BYTE *)(*(_DWORD *)&buffer[1] - 1) = 0; // 处理换行符和回车符
7 U& Z6 _: T% S: D; T
: |# O% l# p# U+ j4 A1 b8 I
yOffset -= lineHeight; // 更新y坐标
c( _! P% p# C' [2 ?7 C0 v, A
glRasterPos2i(*(_DWORD *)lineOffset + xOffset, yOffset); // 设置光栅化位置
% h0 z+ T \2 c$ Y6 I+ H
textData = currentLine; // 将textData指向currentLine
$ x0 ]$ ~; A. m; _ v% R( A
textLength = strlen((const char *)textData); // 计算字符数据的长度
# @) t2 A6 F8 m4 U% O9 w1 R9 S
glCallLists(textLength, 0x1401u, textData); // 调用显示列表
8 s1 m# [0 h, ]
* X/ o; J# D1 E) @0 w( Q9 }. g
if (lineIndex < numLines)
9 E* a. V# [2 I* S- H# k- T6 u
lineOffset += 4; // 更新lineOffset
0 U9 R8 ?. C/ i3 K+ n1 Z
++lineIndex; // 递增行索引
9 l! f0 D7 W4 x |
currentLine = (GLvoid *)(*(_DWORD *)&buffer[1] + 1); // 更新currentLine指向下一个字符数据
. Q" S! C! t% r( B5 } }# p
}
; M! M9 Z. Y9 F
while (buffer[0]); // 当字符数组buffer的第一个元素不为0时继续循环
l; ]$ p. g5 Q7 ~% E; ?$ N% \, y4 }
glPopAttrib(); // 恢复OpenGL属性状态
. J0 e( n% i8 L9 M
}
- h& W: H7 f+ Z/ W* y( }0 s+ J2 V
}
/ ] ?+ k4 p/ F& U m
复制代码
( q9 X/ i3 J. h) R4 _; j
含义解释如下,设置字符串颜色还需要寻找,也许是在调用此方法之前
: m# F: X+ ]% |9 M) B* s
, a, W# H6 S' R3 w% {4 o7 s# W
int xOffset, 字幕的x坐标
/ G+ V8 _5 C, V1 `: N
GLint yOffset, 字幕的y坐标
7 |7 @. _1 L: f% U
int numLines, 字幕的行数(较长的字幕为2,一般为1)
+ M/ E9 N+ G5 W, e! S
int lineOffset, 这个需要再研究
+ M1 c1 s1 l9 Z9 ^6 t
int lineHeight, 字体高度(或行高)
& I4 Y( k I2 }* W A1 c& P
GLuint displayListBase, 这个字体对应的base值
! ^5 c( f: k+ _3 T7 y' h
char *text, ... 字符串
' G; k# r! E ?; `6 N
* [8 k) o' L! z! ~4 \- f
作者:
shane007
时间:
2023-9-4 18:53
顶上来,顶上来
% R0 M, H& b; ?! C' s% M5 G
欢迎光临 冒险解谜游戏中文网 ChinaAVG (https://chinaavg.com/)
Powered by Discuz! X3.2