本帖最后由 shane007 于 2023-8-12 13:57 编辑
9 M4 [4 G2 B8 n9 }+ `4 m) K/ R2 R2 p1 n f( ]8 x* F
这个游戏原本是没有字幕的,但是在Scummvm论坛上发现了一个帖子,
3 i6 C/ [. @+ R讨论的内容是关于在原本没有字幕的游戏上加上字幕。(最初的缘由,是为了帮助一位失聪的朋友)
6 x$ ?# k2 D8 R- E/ G' T) Ehttps://forums.scummvm.org/viewtopic.php?t=7168& k* }9 y# Y p% c3 E
[Scummvm-devel] Displaying subtitles in voice-only games (narkive.com)$ R" h7 a& [2 Y: }( {
% T" Z1 E+ `8 T) P% J$ h
前段时间第七访客出了25周年纪念版(The 7th Guest 25th Anniversary Edition。, s3 j M; [; ]
在这个版本中,游戏的视频是有字幕的,因此把这个视频的字幕用到Scummvm中就成为了可能。/ M2 ~& Q$ }6 s" ^
3 |) g* ~, i& t& f$ o0 k
我查看了一下最新版本的Scummvm的代码,发现老外已经加入了一部分代码。
/ Y4 H9 H& J( X3 l5 z在此基础上,汉化是大有希望的。此种汉化方法,也是前所未有的,如果真的实现了将会有重要意义。5 ]5 U k5 {# A$ d F0 k& k. }
* O* K1 B/ y7 z
类似游戏还有- · Groovie
& }/ \6 A. b) S! v+ P - - The 7th Guest1 X1 r. O' U4 D2 T" V
- - The 11th Hour" D" x: O( f. T1 O% N
" r7 s* X) Z# I( S! A- · MADE0 \# D- }2 w7 D& H: I8 L
- - Return to Zork (descriptions are text only, while spoken dialogs lack
* N7 x; e: h8 [: ~' q- c) q - any text)
1 m6 ]% u1 u2 l) j! y6 { - ' ^; n5 b, T3 Q6 m/ P4 m0 L
- · SCI 1
) O8 [, A& A* h' K+ T3 q6 z - - King's Quest V (CD Version). In this case, just enabling voice and text
7 L0 F! a X7 t" A - simultanously would allow a 3rd party to create a set of MSG files
( o' F5 P1 ~) _6 |1 ] - containing the voice transcription/translation, as it happened with Gabriel9 [! M# f& H" h
- Knight II.
, b) o& J, t I! \: A8 d+ ?
+ c& B$ E! ^/ _' P+ X" W
, f- p9 k2 a4 ^! O9 R F% ~$ v; v- Future engines:: b, c, |- O- w8 e6 ?! M
- , }3 Y3 A: {2 D6 L$ y1 ^% P/ W
- · SCI 21
8 c3 \: B0 D0 f1 n3 ] - - Gabriel Knight II: The Beast Within (Already a fan patch available in$ I0 _! Y* D3 [2 V% x% h% _
- order to enable the text display feature)
2 g9 t$ P3 I ] - - Phantasmagoria
0 M2 i7 G: _* e - - Rama
: Y4 R; J" l' f8 |/ W - - Shivers
1 Q* h- e8 q- G( ]7 A - - Phantasmagoria II: A Puzzle Of Flesh
) m- W+ \1 W+ g8 x - ; A g+ \# w" i5 n5 G+ L1 }5 b7 F
- · SCI 3
8 Z3 t& P' n' x. @- C( \; P - - Lighthouse: The Dark Being: o# d9 q, v Q9 W( U! [
- : |' Y, N- ^5 Z# C& J' F
- · M4/MADS
% X: b3 s1 P2 H$ {# d B - - Orion Burger
+ _2 V: g2 E$ ~9 p$ V8 k8 C - 2 |9 E+ D5 X# K
- · Mohawk
2 q0 j/ g6 G" R. r$ p& G7 ]$ _ - - Myst. j1 p( Z: y( g# w
- - Riven: The Sequel to Myst
复制代码
6 ?# ?2 m0 b; n E! b0 w7 _6 }6 Y# z6 D. H! ~ A3 W/ C
' ~/ N( e ] n! H( Y# V( s* f
3 r; }' w, q, d3 T
相关代码如下$ V/ N6 R1 K) v; T) s' m( W
- bool VideoPlayer::playFrame() {& T+ {7 C! f+ j+ N) l6 C
- bool end = true;0 c6 F q5 l+ }% {5 ^, Q! b
! r( a+ `1 k! n+ j5 N) @- // Process the next frame while the file is open
* d# m$ {8 r- H - if (_file) {
: f( w9 ]9 D2 }- h8 s4 [ - end = playFrameInternal();
9 }5 B: n& `6 |! v+ `
/ k% `/ l' T% p1 V4 i- _subtitles.drawSubtitle(_lastFrameTime - _startTime);8 `' |. [" \" E1 w4 |, \
- }
复制代码- bool Subtitles::drawSubtitle(uint32 timestamp, bool force) {' a1 H1 T+ `, S e' f* F7 ^
- Common::String subtitle;. x7 w1 g% c& C7 v
- if (_loaded) {* R4 {0 h C4 ?9 J# r
- subtitle = _srtParser.getSubtitle(timestamp);
1 ^9 Q* k7 G) z - } else if (_subtitleDev) {
3 y8 b9 T5 g8 B5 F; g3 C5 T. P - subtitle = _fname;
6 O$ B+ N2 ~0 \5 R& [ - uint32 hours, mins, secs, msecs;
: D) ?$ Z m X8 P - secs = timestamp / 1000;
3 _* M+ J5 O5 X( { - hours = secs / 3600;
3 e( w. j4 M$ J. h - mins = (secs / 60) % 60;
# d: Q# q# C1 k3 m - secs %= 60;
2 F# m6 _4 m( o4 `' ?; } - msecs = timestamp % 1000;
3 s" z: O1 B: p5 ]5 W7 C# R - subtitle += " " + Common::String::format("%02u:%02u:%02u,%03u", hours, mins, secs, msecs);4 F) k* j i/ h+ X' g( l V
- } else {
3 Z2 s9 T" \$ `: f) q3 @ N - return false;
7 R: l, ]# l. J* f9 j* q - }
x6 O9 L& Y2 L- l - 7 S5 a9 o% P" m( E% w+ s( Q. s! u' q
- int16 width = g_system->getOverlayWidth(),
- k" J' k) o+ F4 R0 r) N# t - height = g_system->getOverlayHeight();/ [% |8 X: S% c; t# d; G! R& |
9 M; S9 Y2 n2 x* c f0 u9 e4 z" J% K- if (width != _lastOverlayWidth ||
, C7 N+ _# z+ Q% P4 v+ X8 W - height != _lastOverlayHeight) {
" e# q1 E, K. H5 E$ U - _lastOverlayWidth = width;4 _+ w+ d' h3 x% L7 V g
- _lastOverlayHeight = height;
, e W* T& F$ h' R% u - ' J. v3 Q$ b- `+ c$ W5 O
- // Recalculate the real bounding box to use
- `# i% h0 {; g9 y0 w! ?+ f' T& S, s - _realBBox = _requestedBBox;- y& [) x c; s$ e
$ v3 {9 i: Y& e- if (_realBBox.bottom > height) {
8 y1 Q! E! X; X# [ - // First try to move the bounding box
$ x* w8 x% {1 y9 |7 O; L - _realBBox.top -= _realBBox.bottom - height;
: T; p! K# M! x3 L - _realBBox.bottom = height;5 h6 e; P1 Z; M" r$ I- \: f
- }! ~4 |' O' T* L ]8 |
- if (_realBBox.top < 0) {+ t' l! A$ h9 b/ ~
- // Not enough space0 z* ~: i' @4 p7 u6 u
- _realBBox.top = 0; `% a1 {0 g# ~: g& X9 F, }5 \* j( l X
- }
3 O& H* w2 a) V - 4 r+ K4 d! ~/ w$ a2 }: l
- if (_realBBox.right > width) {, L6 w) k( J0 W( F- N% D! e
- // First try to move the bounding box% L3 a: w4 K& X+ k8 h6 x E9 W$ i
- _realBBox.left -= _realBBox.right - width;
7 k6 d8 v( W! I& }; ?7 f - _realBBox.right = width;
+ {5 C: g+ a' @5 L0 _. o+ y7 T - }
9 s7 ]( L2 J+ s/ _ - if (_realBBox.left < 0) {
+ r1 J/ D( N5 }% M0 e - // Not enough space) T. y9 w% D! Q: q; w
- _realBBox.left = 0;
9 y- q* G/ S' t6 v. H9 O - }- u- W" v& Q, d* u: t
- * k8 A- d& R" f2 c
- force = true;
3 q+ @# _' q1 }* B9 r7 y8 L) q - }
0 ]0 c c/ O0 C0 J% d _) Q
7 B* Z$ H% R/ f5 ~% H6 J1 T- if (!force && _overlayHasAlpha && subtitle == _subtitle)! `: T! c; e' ?
- return false;) q8 f: W% S: {/ C. C; W: l
; F+ J5 N: }( z d) c# V. o5 A- if (force || subtitle != _subtitle) {7 ?1 Y! M A0 M
- debug(1, "%d: %s", timestamp, subtitle.c_str());
. q: Z1 B/ p' k! k5 v1 _+ B K4 ] - `' M- [2 c5 l' l) M; G1 n; B" P+ b2 y
- _subtitle = subtitle;
: ? \# S( u: v - renderSubtitle();
3 P4 t& q x% q8 Q( u2 `4 X* d& M - }
$ \' l* |2 @7 j: E - ) E2 \! v n& j4 c, M
- if (_overlayHasAlpha) {
2 ~ m5 }: x) d5 H6 H - // When we have alpha, draw the whole surface without thinking it more
5 e4 Q2 B2 n+ k8 z2 M - g_system->copyRectToOverlay(_surface->getPixels(), _surface->pitch, _realBBox.left, _realBBox.top, _realBBox.width(), _realBBox.height());
, K9 ^) X: J0 C$ f2 c - } else {1 g9 o" Y& w5 \1 l
- // When overlay doesn't have alpha, showing it hides the underlying game screen6 S1 {, a$ Y( T* o) R
- // We force a copy of the game screen to the overlay by clearing it4 t9 _+ l2 m( h7 e: u: [5 V! q; D6 o
- // We then draw the smallest possible surface to minimize black rectangle behind text4 f# S8 J9 h* o+ G2 w5 {4 H
- g_system->clearOverlay();7 w$ i0 p* Y; I5 c% E3 s! Y1 t) v
- g_system->copyRectToOverlay((byte *)_surface->getPixels() + _drawRect.top * _surface->pitch + _drawRect.left * _surface->format.bytesPerPixel, _surface->pitch,
4 @, K& @+ z, g9 C1 h5 f+ l0 c - _realBBox.left + _drawRect.left, _realBBox.top + _drawRect.top, _drawRect.width(), _drawRect.height());
4 _! v, @4 W, b1 U - }' z6 O, ~# ^* q# X7 \; H1 O1 x
- % M& I; [0 }) j# ~8 w7 Y
- return true;
" q4 F+ Y5 r% K- L5 v - }
% F5 d0 O" K" t$ g9 L
复制代码
$ S* S8 s7 T' L. v# D f! E" P
; Q2 U$ \ V* Z* i9 ]& A
" m" @% T# V, p# s6 P- B3 ]3 J+ }; ]$ T, s8 t5 _* ~
# s8 |% A0 ]+ ^$ a% I
8 b5 _- R8 Z6 r% ^- I% {, ~: b, }8 V( B9 v' b, O' B
|