本帖最后由 shane007 于 2023-8-12 13:57 编辑
! f6 U! U; Z+ `, ~
" { P( Z$ N! D$ c9 s6 X( Q1 a5 i这个游戏原本是没有字幕的,但是在Scummvm论坛上发现了一个帖子,/ X* Z* O* F; [4 G* \( r
讨论的内容是关于在原本没有字幕的游戏上加上字幕。(最初的缘由,是为了帮助一位失聪的朋友)
' I" D- a, J/ F% U# M( j& whttps://forums.scummvm.org/viewtopic.php?t=7168
! ]2 ]. c' J5 ?* ~. e8 g' i+ t7 \[Scummvm-devel] Displaying subtitles in voice-only games (narkive.com)# o2 ?/ X- X+ P: \0 L
& ~5 ~ m5 R# L% g/ r
前段时间第七访客出了25周年纪念版(The 7th Guest 25th Anniversary Edition。6 x# `( I! @* I( u* @$ C3 l
在这个版本中,游戏的视频是有字幕的,因此把这个视频的字幕用到Scummvm中就成为了可能。
" \1 `3 Y0 s. g& k. h* p: N4 y
- u. G8 R" z! h- k& T" d我查看了一下最新版本的Scummvm的代码,发现老外已经加入了一部分代码。
3 \6 I( r4 J0 }6 p G9 t在此基础上,汉化是大有希望的。此种汉化方法,也是前所未有的,如果真的实现了将会有重要意义。3 p% \4 M: R2 L3 G$ ~! f
) e; r" Y1 M" V类似游戏还有- · Groovie
0 `& a: Q1 J3 s2 W, i- J' x, D8 [ - - The 7th Guest
" v( ?1 o# ~+ m/ x6 } - - The 11th Hour2 l! e( L4 Q+ ^2 J5 G0 ]
& y! ?7 g. i; W" h' c- · MADE
9 S) I' N1 n# z" T9 U# [$ D$ E - - Return to Zork (descriptions are text only, while spoken dialogs lack% a1 J3 y/ k: ?3 b+ Z0 [& t0 D
- any text)
" f; D+ F8 W0 H( v: e$ a. L
5 g: [% A( F+ Y. [: ~- · SCI 1
0 C+ ? J! V, d$ [. V2 ~ - - King's Quest V (CD Version). In this case, just enabling voice and text
* n+ C% l( w) L3 h5 N3 n - simultanously would allow a 3rd party to create a set of MSG files! T @* V# ~' ^2 u5 {/ `
- containing the voice transcription/translation, as it happened with Gabriel
H: j. y% d& b7 k- R$ N - Knight II.
' M4 L/ _2 L$ V g3 H. s
1 E9 E: p' @' L1 m9 s
' U( U- L" Y9 t9 y- Future engines:/ P& ~$ R0 P' f' y
" C# g$ ~2 G' N! J; l2 z- · SCI 21/ _1 U& n, x0 Q+ T3 }$ N8 o
- - Gabriel Knight II: The Beast Within (Already a fan patch available in
$ U0 r h1 o9 q6 ~4 }! b5 C - order to enable the text display feature)
3 x7 q2 n3 c* p% }8 X& R3 [ - - Phantasmagoria& p" |7 ~3 h5 D3 v" Q+ Q
- - Rama- Y4 E/ t3 H( l: A- }
- - Shivers/ c: H/ S: Y* P" K. }9 j; d
- - Phantasmagoria II: A Puzzle Of Flesh. g) J1 W6 @' Z2 H* G
) m" P5 e9 E; O" }- a- · SCI 3
- o M4 k# n6 H5 O/ L" s - - Lighthouse: The Dark Being. \2 z5 i( x, U' B) x" q* u
: w( w" `4 g8 X& \- · M4/MADS
" D0 T9 \9 O0 m4 S - - Orion Burger
( ~ |6 V1 B% Q3 B. |- R
& h: X$ t) p# ?* ]2 l9 R# L- · Mohawk
" L* n3 W# `! @% ~6 r8 G - - Myst
+ J! |$ b( e7 N, I# i+ z$ U) @: k - - Riven: The Sequel to Myst
复制代码
D g5 p! h5 c- H
: C2 n5 V7 D5 ~& G7 H$ w% S% j( V5 t: w
6 I: D0 _9 v4 B4 h! H
相关代码如下 T( l& p! m: m# E
- bool VideoPlayer::playFrame() {' F0 W1 h7 C! Z* e0 ~! g
- bool end = true;* z* I8 H- O5 A4 c$ }# \
- " U' f5 m% Z! B# L! P5 Q7 Y* S
- // Process the next frame while the file is open
: L1 ?. v6 E( f, ^& @! U' S: m, d - if (_file) {/ `) ]) j: P3 G) C Y( f: u' w
- end = playFrameInternal();+ H2 w6 [2 m" d4 P, l X7 n8 ^
: K! T' E7 {2 }9 P2 f- _subtitles.drawSubtitle(_lastFrameTime - _startTime);
/ f' q( g v; G! y9 ]* g/ A8 { - }
复制代码- bool Subtitles::drawSubtitle(uint32 timestamp, bool force) {
8 W" Z" k0 L7 M4 n - Common::String subtitle;
, K, c3 W/ Y6 P. B - if (_loaded) {8 s; I$ U7 f4 d4 A
- subtitle = _srtParser.getSubtitle(timestamp);
% {% u9 z3 Z5 ^; E - } else if (_subtitleDev) {
0 j! j8 }# \ h/ V7 F$ J - subtitle = _fname;. f* r0 u, c" g3 G* C; R* @* r# c
- uint32 hours, mins, secs, msecs;/ X/ V' p6 v1 q
- secs = timestamp / 1000;% f9 j/ D* x/ F
- hours = secs / 3600;5 \( |1 {, {: t3 r5 Y. z8 m
- mins = (secs / 60) % 60;
8 Q1 w1 \ G! J. D' i - secs %= 60;
; a3 I1 L& D3 M3 r B, @ - msecs = timestamp % 1000;3 T4 i- H; N: M5 x
- subtitle += " " + Common::String::format("%02u:%02u:%02u,%03u", hours, mins, secs, msecs);
' ~7 n; }% b a g; ~6 ?5 a - } else {9 w# Q8 Y) g S
- return false;( p( I! n6 N2 }; i
- }- e0 E; d8 |! D4 z
- " [, H0 @( f. Z
- int16 width = g_system->getOverlayWidth(),7 C2 L# A* @, ^
- height = g_system->getOverlayHeight();
$ e* V" S( l' b* O$ Z
* H9 T: ?( n8 M- e1 M% ?: D- if (width != _lastOverlayWidth ||/ ^( Q: j+ |4 W, [8 f
- height != _lastOverlayHeight) {3 d `! w# K+ \: B
- _lastOverlayWidth = width;( W+ m% @& T, i9 w
- _lastOverlayHeight = height; [8 [1 M$ F) f" H9 B1 s
2 y O9 ~$ x. ^7 E1 V5 }! R' X7 |- // Recalculate the real bounding box to use$ @. ?. b, d( L M
- _realBBox = _requestedBBox;" {& o' O+ V- @- {2 T0 ]3 F& A
- ( B& C5 x5 S- W* _
- if (_realBBox.bottom > height) {
* ^ i) w( D3 P" D - // First try to move the bounding box7 O U& v/ R, H) x
- _realBBox.top -= _realBBox.bottom - height;& Z6 V+ B5 b: d1 c( W
- _realBBox.bottom = height;, Q0 z/ D5 d% h9 B6 q6 r6 m3 X
- }6 @2 x' e+ X# L4 e2 \4 h/ r
- if (_realBBox.top < 0) {% Z! a+ Y, ~* y* {
- // Not enough space
( n/ B9 x1 i: q3 C/ \# A - _realBBox.top = 0;
8 x' d! t# s, A5 B# C) l" O - }
+ P1 f% G h* O% |: J: m+ }
* T( [, X' X! X$ M. N- if (_realBBox.right > width) {& C, P4 G/ D8 e) j5 ^
- // First try to move the bounding box
8 q7 K. e, t1 g, M5 s6 M2 E0 i) P - _realBBox.left -= _realBBox.right - width;" O1 i& N0 H( U* D) ?' ]' @8 O
- _realBBox.right = width;! G, g7 f3 h4 p6 f
- }1 ?2 W7 M( Y }0 n, u$ t
- if (_realBBox.left < 0) {
+ }6 ]* G/ z7 T/ m - // Not enough space
) U9 E5 i" X* J+ ^ - _realBBox.left = 0;
, V& Q9 o9 ^2 }. b - }
+ Z4 [2 X# |& y* n- i( C" ^ - / d8 t4 ^" p1 G8 [; Y& O" O7 V) `
- force = true;
! H! F, A4 e8 [ - }5 |# l+ L0 l2 `- G& ~% v1 r
- # F5 S5 N( I# G& i- O
- if (!force && _overlayHasAlpha && subtitle == _subtitle)" {& f% _9 C0 Q7 [- F
- return false;
( o2 A1 f7 W- ~% V
0 }' z4 u4 Z0 g' i% c2 q- if (force || subtitle != _subtitle) {
* k8 B/ D! q$ R& G( p0 V - debug(1, "%d: %s", timestamp, subtitle.c_str());3 O: y" b6 l, {, }7 `/ d3 v
- 8 Z) a* ]$ c( H4 p: X0 O+ ^
- _subtitle = subtitle;+ _& n$ P- C. [2 u- }. l/ N. ]
- renderSubtitle();, E- j6 ^! v, b0 `. t
- }. t, j/ ~2 m2 F
% S) ` ?+ |7 J, T# x- if (_overlayHasAlpha) {3 r: @6 }9 F' x/ l; W
- // When we have alpha, draw the whole surface without thinking it more
: p9 Y+ R/ i% t: |& g% N/ \ - g_system->copyRectToOverlay(_surface->getPixels(), _surface->pitch, _realBBox.left, _realBBox.top, _realBBox.width(), _realBBox.height());
: `; G! P( @9 S5 E' o2 o: j5 t - } else {
; ^2 C$ A* j: s W& { - // When overlay doesn't have alpha, showing it hides the underlying game screen
* C- Z% ^# o- s. [) ` - // We force a copy of the game screen to the overlay by clearing it
% h, H) G' |( Z) Q3 l - // We then draw the smallest possible surface to minimize black rectangle behind text
* v* I/ l4 ^* ?* {/ H ^ - g_system->clearOverlay();
8 L, I' ~, l5 f( e - g_system->copyRectToOverlay((byte *)_surface->getPixels() + _drawRect.top * _surface->pitch + _drawRect.left * _surface->format.bytesPerPixel, _surface->pitch,
! [# m0 _5 s* }: A4 z% ~0 p - _realBBox.left + _drawRect.left, _realBBox.top + _drawRect.top, _drawRect.width(), _drawRect.height());0 g! \7 O C/ Y- v( W. @
- }
5 x1 j: P" _* Z; p1 W2 ^( U
1 |! a4 K1 E7 c1 M7 ^! B6 j- return true;
4 l4 d& ^0 r; o2 b! k5 F [ - }
; n+ b: q* b& X. U7 H% r0 f4 ~
复制代码 # G+ x& N w( ]+ C5 M
, A" j# Y* g9 w6 M
5 S! i6 U+ f/ O0 e9 `2 l9 Z
/ O f. C9 M8 M, q: y" A
# x# ]$ }( P! N$ j* L
# |* l% m& b# \# ~; u. L9 T
+ ^ y. V3 a# d, T0 Y |