本帖最后由 shane007 于 2023-8-12 13:57 编辑 , N& F% {, L$ \7 [: D
* p9 ^+ j# q/ z8 Q5 o3 A7 v
这个游戏原本是没有字幕的,但是在Scummvm论坛上发现了一个帖子,
, e0 ?5 q% C9 y讨论的内容是关于在原本没有字幕的游戏上加上字幕。(最初的缘由,是为了帮助一位失聪的朋友)
% l( |* F7 v2 P8 b; w4 `https://forums.scummvm.org/viewtopic.php?t=71689 J0 L+ \! ]1 T& k' w
[Scummvm-devel] Displaying subtitles in voice-only games (narkive.com)
. W( _- }& C1 v2 N/ h! n
1 e9 q. M% h. j; G W4 t2 Z. b前段时间第七访客出了25周年纪念版(The 7th Guest 25th Anniversary Edition。
; H- {# w! C) Q7 z; m N7 u在这个版本中,游戏的视频是有字幕的,因此把这个视频的字幕用到Scummvm中就成为了可能。
' q5 G' y+ l8 j3 F& {/ G- K
5 L9 ^- g$ X! X% E8 p我查看了一下最新版本的Scummvm的代码,发现老外已经加入了一部分代码。
9 o* t2 ~6 c4 a& b9 y6 u在此基础上,汉化是大有希望的。此种汉化方法,也是前所未有的,如果真的实现了将会有重要意义。- k* r+ I: T4 ^' x q
8 M" Y% B- t2 X
类似游戏还有- · Groovie* L; t! i+ \ \: n/ d: r& M+ P
- - The 7th Guest5 d9 _- w7 h3 {0 R: Z
- - The 11th Hour
/ u* |. U7 ?* i! w q9 G9 V$ H
/ m1 W* `9 T4 Y# c% O- · MADE6 N8 C% N% ]* P: ]
- - Return to Zork (descriptions are text only, while spoken dialogs lack
0 e3 T2 O2 u0 y8 _/ ^- T - any text)
8 M2 j T, J# M1 A& l3 {& i9 v - 4 w' C1 u. x0 `/ @6 X Q
- · SCI 1+ _9 D" M& d9 j# z
- - King's Quest V (CD Version). In this case, just enabling voice and text6 I" t& g p0 m6 b! x8 Y2 Q0 j
- simultanously would allow a 3rd party to create a set of MSG files
: ^+ l; o; Y9 I e) K+ {9 f* a+ J4 O - containing the voice transcription/translation, as it happened with Gabriel
h5 }& H0 i, j$ v) ~ - Knight II.( O; f2 g5 ~" g3 C) ]5 J
3 H( Z8 I9 A+ n. W8 C, e6 {5 q- " m3 ~* y7 h% W8 E$ k; S
- Future engines:
; ^: t$ P" E* a6 f: ]" E0 v, M - ; V% c5 }, B8 x7 O5 o4 S
- · SCI 21
; [' ?4 m2 k/ x - - Gabriel Knight II: The Beast Within (Already a fan patch available in- }+ T8 I* l* a' L% b
- order to enable the text display feature)$ I3 J% _ q. ]4 T; K2 A% y+ h2 J, `; Y
- - Phantasmagoria* v- G' y5 ~% W x
- - Rama% n9 W, f$ I: t4 ^: ~2 R1 E1 f. Z# \3 G
- - Shivers" E* k4 x- s) t7 E# y/ n' Z$ z
- - Phantasmagoria II: A Puzzle Of Flesh4 b1 Q8 O' H& g
7 q( b+ {7 q- P# K- · SCI 3+ V) B: A% |- V. ~! j$ @* ?
- - Lighthouse: The Dark Being
# x1 Q8 [3 o9 k6 f9 m! v- }
4 G5 C1 c$ d, r- · M4/MADS
' d- X6 U0 ^! L# p - - Orion Burger
9 u7 l" i2 Y h3 \: C# f
5 ?" k5 t9 C4 P. X- · Mohawk
! j# L2 w0 o5 U - - Myst5 L. ~3 m! K# q8 o0 w& n( N g R
- - Riven: The Sequel to Myst
复制代码
/ k+ m* X; m6 I4 A% y/ m4 ~0 P: T1 H8 y* r
7 N; W8 a' R; U, G8 w# B. k
" @0 y5 b w( h. m
相关代码如下8 x2 O, [+ i( B! o1 Q% W
- bool VideoPlayer::playFrame() {3 p3 t4 v0 I' j- v, D2 v7 J
- bool end = true;
6 w# o3 n+ Y& i! {/ B0 X. K, u* g
1 K8 g6 q+ B0 L3 Y- // Process the next frame while the file is open" I# h2 F5 |; c2 n2 M. d
- if (_file) {$ Z; m4 E/ l6 g1 D2 f b
- end = playFrameInternal(); |, }& b8 j# F7 K. N4 Y+ O! N
- ; }" R) ?9 h& J& I1 j6 v5 I
- _subtitles.drawSubtitle(_lastFrameTime - _startTime);
/ N5 e g! l" [8 l! ~/ r9 P - }
复制代码- bool Subtitles::drawSubtitle(uint32 timestamp, bool force) {3 y0 @ |' C* b! `3 ]. R
- Common::String subtitle;" K( }9 ~# _0 k/ l# A
- if (_loaded) {
7 M8 G9 d. y+ L! U+ U& @" L - subtitle = _srtParser.getSubtitle(timestamp);( y5 K" d; A5 a5 c* }3 z
- } else if (_subtitleDev) {6 h- y8 w4 ?: y
- subtitle = _fname;
3 t7 ~, _7 j- s. a, Y, u, B" R# F - uint32 hours, mins, secs, msecs;, y( h1 _9 i& i. R
- secs = timestamp / 1000;' p8 E' ?! y5 [9 f# \ [' C
- hours = secs / 3600;
5 M" [, P5 j) P7 ~4 e' y - mins = (secs / 60) % 60;& Y: w0 c8 B. H; j. `8 K) k
- secs %= 60;8 E1 ?: y% S* ~5 b. W# f" s7 A2 b
- msecs = timestamp % 1000;
F$ [' x3 u: @ \, D+ U; e - subtitle += " " + Common::String::format("%02u:%02u:%02u,%03u", hours, mins, secs, msecs);
+ r- ]5 @. C6 d, m5 X% o - } else {. I W6 ]& n# [4 Q5 r5 x; B
- return false;
8 y- B( ]0 c: ?$ g8 m6 w7 Y! c - }
/ I6 V, s6 S0 \0 \: R - 9 ^! }1 l8 [- N7 X! e
- int16 width = g_system->getOverlayWidth(),
& C, _3 _, j/ l2 x7 b5 w - height = g_system->getOverlayHeight();
* ~& K% w7 \3 e& J0 F& {2 `- U, D Y - ; ~( U% A& Y. c# H4 }& Q* E
- if (width != _lastOverlayWidth ||: M4 C# h) n9 I/ a
- height != _lastOverlayHeight) {
9 R- f0 X6 [, `+ E! r9 V) n* l6 u - _lastOverlayWidth = width; [& \5 s: b& Q% K6 |! q( j
- _lastOverlayHeight = height;
- m0 |9 W4 T. Z- l8 @0 l$ _ - # A5 ^; ]$ Y; q+ U" [
- // Recalculate the real bounding box to use2 c( L+ D4 j# y! L
- _realBBox = _requestedBBox;
, |% _, T2 R8 V* |3 ~* C
; `* C0 w: B: C0 e. {- if (_realBBox.bottom > height) {9 n8 q' i: E1 n7 f; x
- // First try to move the bounding box
. |: _" N8 d; ]2 z Y, M - _realBBox.top -= _realBBox.bottom - height;: x( J% k( q( w0 c
- _realBBox.bottom = height;
: e1 o8 D/ X @" P" T/ i4 K* L3 j - }
! \! v8 i- q X, ~ - if (_realBBox.top < 0) {
# o c# \* O; ]5 C7 [; ?- A - // Not enough space
+ \) O. |% U7 M- h/ M0 P3 Z - _realBBox.top = 0;( _7 ]+ O; s) B6 \, a7 O
- }
# c1 E, }/ M& c
. y+ a) j* G v$ W6 P3 c- if (_realBBox.right > width) {% i# p* U3 `5 X$ A* U1 u
- // First try to move the bounding box% U% R# s# ^! ~$ H1 b- @6 r4 U
- _realBBox.left -= _realBBox.right - width;7 d i Q d( u$ x
- _realBBox.right = width;8 X: Z# p; L0 [$ f
- }
, m- G- X* G/ G& P - if (_realBBox.left < 0) {
) }: @5 i" N5 O. Z. c2 A5 P, o0 G - // Not enough space, s2 U" r, K: ~! t% ?
- _realBBox.left = 0;
3 @% }8 z' v4 p8 J! j& g! j - }, R0 |9 q+ f( `
- ; i; @: U5 j& U. U. S% {; n
- force = true;8 d; y- w% w) L
- }
w! K/ R( |$ ? D& k: \9 U; H - + c( p2 Z4 E9 S8 @% m
- if (!force && _overlayHasAlpha && subtitle == _subtitle)( x/ z# T9 g. s* u4 j
- return false;
u/ Y; K, a- I! h - T$ D4 q& Z' E+ b7 U. S) A, M
- if (force || subtitle != _subtitle) {) N7 f. u& K6 c$ u4 y
- debug(1, "%d: %s", timestamp, subtitle.c_str());
' x' R1 @3 y, z/ a9 y& D
2 D0 ?, V$ `' F6 v, }. T- _subtitle = subtitle;
0 U& P0 h' F, D - renderSubtitle();2 _9 s I4 [8 w% N7 [! D- a" S
- }( ]& q' L) p5 M+ W) w
' t8 V7 S2 Y5 d* l v1 q, l- if (_overlayHasAlpha) {
1 d! E4 j. @ D! e9 r - // When we have alpha, draw the whole surface without thinking it more
0 @9 j& L5 n S t, G( U - g_system->copyRectToOverlay(_surface->getPixels(), _surface->pitch, _realBBox.left, _realBBox.top, _realBBox.width(), _realBBox.height());
3 v# C2 C$ C. G6 A% x - } else {
5 E, R7 b; ]& E9 K' g6 c - // When overlay doesn't have alpha, showing it hides the underlying game screen
; o2 r$ _3 N& J9 G9 ~ - // We force a copy of the game screen to the overlay by clearing it% S5 ?2 @. |4 g3 G& I: _
- // We then draw the smallest possible surface to minimize black rectangle behind text
9 v* p8 U( \+ T/ i+ o, r# I - g_system->clearOverlay();- w' S$ h8 ]4 l7 H/ t2 H
- g_system->copyRectToOverlay((byte *)_surface->getPixels() + _drawRect.top * _surface->pitch + _drawRect.left * _surface->format.bytesPerPixel, _surface->pitch, ~% b" s, s$ @
- _realBBox.left + _drawRect.left, _realBBox.top + _drawRect.top, _drawRect.width(), _drawRect.height());
" @0 g% @0 K: f: a: G - }$ ~1 ]8 k! k% M B% E# e, D& ^
- 1 t& _0 K( t3 J8 e- r3 Q
- return true;
9 H0 r" N& o7 m' T5 ` - }
' s$ D. Z6 f+ i8 S, K. m3 t3 P
复制代码 4 ?, g0 b: m# m
4 x8 h/ M& ^7 B
+ ]2 T# s% F) M" a5 c: Q1 M4 n' Q1 z' \+ J# ]/ _4 }8 e0 Y: t
1 o/ g/ Z( n' B K; q
# c' ^3 v0 m( B0 O1 c
9 B1 w' K1 Q+ X3 u |