本帖最后由 shane007 于 2023-8-12 13:57 编辑 * m. R& O* i- ~% T* Q" b
' N6 h7 ~3 P# l) N% _这个游戏原本是没有字幕的,但是在Scummvm论坛上发现了一个帖子,* |9 `6 z& g# R' J: n
讨论的内容是关于在原本没有字幕的游戏上加上字幕。(最初的缘由,是为了帮助一位失聪的朋友)
$ q3 Q; n) E4 l9 W8 w; K& thttps://forums.scummvm.org/viewtopic.php?t=7168
1 B7 ]- _& X! y( ]) _, a[Scummvm-devel] Displaying subtitles in voice-only games (narkive.com)
% a: f. ^6 P1 T- X0 c! v0 j H0 q7 U; D' d( q; x5 I) Q
前段时间第七访客出了25周年纪念版(The 7th Guest 25th Anniversary Edition。- G; d, W5 x2 ~! M/ p
在这个版本中,游戏的视频是有字幕的,因此把这个视频的字幕用到Scummvm中就成为了可能。- T$ D+ a( K. Z1 w: h$ p
3 G. y- l; v, c1 r( B我查看了一下最新版本的Scummvm的代码,发现老外已经加入了一部分代码。
* f& K, D/ V3 |0 i在此基础上,汉化是大有希望的。此种汉化方法,也是前所未有的,如果真的实现了将会有重要意义。/ D- X) }, o7 g( S7 Z" x8 d
4 {) @) q& ^/ F1 y类似游戏还有- · Groovie
: k; V! [! U' q4 T5 a# N - - The 7th Guest+ z4 U; v/ h% @
- - The 11th Hour. z f& j! |& Z) e" _3 V- n8 {
' [& i3 S* A! W6 j- · MADE1 R2 ^( a! p! w3 q: k n
- - Return to Zork (descriptions are text only, while spoken dialogs lack
, Q9 t# W' v7 W1 d" _7 N! l9 W: Y - any text)! A# s; R- A* V# Y
- 2 \, d( w. c7 ]& O: q
- · SCI 1
% Y9 ^3 g0 y2 n# B - - King's Quest V (CD Version). In this case, just enabling voice and text
w, K1 H0 o' A - simultanously would allow a 3rd party to create a set of MSG files
4 u' Y! e* v( w0 b5 @ - containing the voice transcription/translation, as it happened with Gabriel
) a% }7 f7 b0 Q6 B6 a1 H( \ - Knight II.; o' J5 F2 [2 i* [) c6 w
- % \" ` A, R7 [3 s1 ~9 H9 K
- % t$ s1 ?5 S/ }" T
- Future engines:# ^, G3 r1 F( d
- 5 R! |) n) L5 Y0 t l+ U0 Q/ X
- · SCI 21$ E3 a/ L1 u5 }7 \
- - Gabriel Knight II: The Beast Within (Already a fan patch available in' n W- O2 ^0 i. `, O% m
- order to enable the text display feature)
9 M8 _+ ?! ~- l - - Phantasmagoria
8 S) t: M) v0 k9 F5 ? b - - Rama) O, X& P7 M M% v! q% d0 M
- - Shivers7 f$ @6 @9 Z; |/ ]7 W
- - Phantasmagoria II: A Puzzle Of Flesh
& P/ W: y8 c# Y3 u' J7 t+ v w - : r; T- u! J7 R# J2 y$ O: n) P
- · SCI 3/ l$ P6 k6 v9 e: D6 e
- - Lighthouse: The Dark Being
2 |- v2 b- Z9 s6 Y: c1 {/ F6 u - " A7 W2 B7 O% O8 `/ a! y
- · M4/MADS
# h9 U& O l8 x. A) I- j - - Orion Burger7 I6 L6 A3 u W' O
- " r3 b" b# V% \4 V. A+ P6 W6 ~
- · Mohawk& c2 w* |& J ]' M, b
- - Myst0 Y5 M& P7 W4 _$ ^5 q# B% _. E
- - Riven: The Sequel to Myst
复制代码 ) K5 u0 M# y: {$ w
: F& p) }% Z% o' ` c
; Z A- ]; {8 H) L2 e$ Q8 D
* }8 y5 }+ j! v0 `5 c3 y4 D
相关代码如下# H# `- e" T! c4 f
- bool VideoPlayer::playFrame() {
" P M3 ^ S2 h, ?+ D. D - bool end = true;
' D' g4 R) v8 \( I - 4 M9 Y1 D) U" P
- // Process the next frame while the file is open
$ L% h1 A" h$ r5 |6 k% Z* ?; o) P# i - if (_file) {
2 [: M2 i! [* t, I# E5 N3 F - end = playFrameInternal();4 x. O+ {& M# b T+ u" U0 _
2 g" Y; v# j( k2 i% G) B, Z' t, L- _subtitles.drawSubtitle(_lastFrameTime - _startTime);
# U' q( l1 A8 H- r, b6 u - }
复制代码- bool Subtitles::drawSubtitle(uint32 timestamp, bool force) {0 q! B$ v, \8 z/ I
- Common::String subtitle;- d) C- n- u+ |7 ^8 G
- if (_loaded) {
4 T" B1 j5 H5 S L( D" Z9 P - subtitle = _srtParser.getSubtitle(timestamp);# @% Y/ ^6 @7 w8 p
- } else if (_subtitleDev) {
; l6 @" p r; J7 O( [5 Y9 c - subtitle = _fname;
. p4 n: [2 ?7 d4 f Z/ u% L - uint32 hours, mins, secs, msecs;6 E# H6 f0 M6 \& v* d9 x# s
- secs = timestamp / 1000;
4 l+ B! [9 c3 H2 |2 t) s - hours = secs / 3600;2 M4 N) c6 a, r0 Z: W; H7 V5 [
- mins = (secs / 60) % 60;
7 w/ B$ A8 z8 e4 s - secs %= 60;
, N3 v% Z1 _0 a' R" K) L& l) N - msecs = timestamp % 1000;
# {& d, p) w9 w8 x8 E - subtitle += " " + Common::String::format("%02u:%02u:%02u,%03u", hours, mins, secs, msecs);
+ K9 T, p( W) n5 C n8 v2 V - } else {
" H" Q; W- |2 F4 E7 w* a/ i - return false;' L" W& C; T f/ j
- }0 {1 V, y: O: a/ W% k1 B# S
( q$ G( E O( V* t$ i! L, N! n- int16 width = g_system->getOverlayWidth(),
' B) B" ~. z( s" m - height = g_system->getOverlayHeight();( @+ z; e0 a7 \% T
) X$ u$ P9 [3 k: t. H# A- if (width != _lastOverlayWidth ||
# y" t$ C0 H4 g o5 W - height != _lastOverlayHeight) {
/ o2 ]) A9 O3 u! o2 T - _lastOverlayWidth = width;. F- \9 Z' t% ~5 m1 |
- _lastOverlayHeight = height;
2 s8 O# h' B5 v
N, a3 n( O; a" [2 N- // Recalculate the real bounding box to use# z$ I% [. y4 j" V; `
- _realBBox = _requestedBBox;- t2 @. |! z' D! m3 `
# ]8 F2 Q* M. v4 {' @# `# {2 r- }- if (_realBBox.bottom > height) {0 p: |+ v8 h4 u0 H& L. k. I. H" H6 ~
- // First try to move the bounding box( W5 H9 z. M' C
- _realBBox.top -= _realBBox.bottom - height;
9 g7 L* U: o0 ] [* P - _realBBox.bottom = height;, V. u* E- ~, w. l& d) J
- }8 F& @3 C0 q. t: {2 p
- if (_realBBox.top < 0) {
( P* g( p5 ?8 ^, I8 j6 D - // Not enough space& w4 M1 w6 }* A, n$ {$ |2 Y
- _realBBox.top = 0;. e' Y$ y4 U6 X, y! x3 a
- }
5 l2 z: I. K/ J0 ], ^8 v5 U
& e! Q5 N% Z: S8 w9 C& l- if (_realBBox.right > width) {
3 V! N1 }8 ^" [& I - // First try to move the bounding box
$ D/ c8 e1 w+ e7 F% K, r - _realBBox.left -= _realBBox.right - width;
% \4 R% Z6 J4 u! d - _realBBox.right = width;7 `4 H& l2 A7 y
- }
# a3 M& k' n0 R6 l8 g - if (_realBBox.left < 0) {5 f2 q3 Y1 b# k1 O$ l
- // Not enough space; Q$ l( [3 s' B J
- _realBBox.left = 0;
( C$ z5 W7 n) _8 n - }
7 y4 |( z+ ~$ \: O# P7 x9 P2 i
( e- g2 ]" [& B2 `/ |- force = true;5 l8 o4 z3 \0 A/ O; y/ N2 {
- }0 R& A# b, v6 N7 s
1 G" ?7 R0 j3 k. x3 Q2 B1 {: X- if (!force && _overlayHasAlpha && subtitle == _subtitle)# ]7 B* e% O1 g; y, C
- return false;
- A3 y8 W+ _+ s8 z& V
4 M' a- n# Z' W9 B/ j8 e! f+ J- if (force || subtitle != _subtitle) {2 M- {9 a" Q" M$ P1 ^
- debug(1, "%d: %s", timestamp, subtitle.c_str());% {0 Y% s! g8 x) b. _. S4 o
# F1 x C8 P" n" F- _subtitle = subtitle;7 j+ a( v9 }; e$ `: s. C6 C
- renderSubtitle();
) K9 {6 k- n$ ^( d - }# Z1 p$ m+ D7 r; A" K; q0 U8 Z' N0 X
- ) [7 w0 Y) \) Z& r! h
- if (_overlayHasAlpha) {
5 z, Z! S) |; F- J5 p* W) y# y - // When we have alpha, draw the whole surface without thinking it more4 {, b3 X/ x6 ]0 J5 R! D! v- p3 _* h
- g_system->copyRectToOverlay(_surface->getPixels(), _surface->pitch, _realBBox.left, _realBBox.top, _realBBox.width(), _realBBox.height());
. ?. o5 F/ `+ S - } else {* p! s% J" ~7 @$ K) ~) A: r! V
- // When overlay doesn't have alpha, showing it hides the underlying game screen
1 X: B Z* K7 X4 h% N6 B - // We force a copy of the game screen to the overlay by clearing it9 i# w+ W# V0 Q7 [0 _" v
- // We then draw the smallest possible surface to minimize black rectangle behind text
. E* [0 A) a/ X; }9 J( p - g_system->clearOverlay();
, G4 O7 {6 m$ ~ - g_system->copyRectToOverlay((byte *)_surface->getPixels() + _drawRect.top * _surface->pitch + _drawRect.left * _surface->format.bytesPerPixel, _surface->pitch,$ |7 }( W, e) k, z
- _realBBox.left + _drawRect.left, _realBBox.top + _drawRect.top, _drawRect.width(), _drawRect.height());% ?& |0 ^2 V" O W- l7 W" ~! H k
- }' f7 g3 y/ P+ C( O
* D; S0 j ~+ h% Y$ u7 C$ w7 b- return true;" r/ B- z5 Z: J
- }8 Y, F* V# \2 {, N
复制代码
6 S- k9 S4 L/ g5 N8 [% H8 `/ S2 N2 E6 `; Z4 w2 Y7 c
5 X" a, f5 v! c: S& }
. `: F% J, V8 k v5 i, I" `% Z8 _ F( r$ E
& E2 H2 X! |% {' w/ [: \' a
6 e5 q' Y& Y/ V+ S3 h! W2 E
|