本帖最后由 shane007 于 2023-8-12 13:57 编辑 ' o. b7 {' K# w2 i: n* |
) ^7 A1 O# _$ D+ @& u. g( v0 k1 c7 E
这个游戏原本是没有字幕的,但是在Scummvm论坛上发现了一个帖子,
, n* f8 r& b3 P& q5 K讨论的内容是关于在原本没有字幕的游戏上加上字幕。(最初的缘由,是为了帮助一位失聪的朋友)
; N6 Y9 U% m7 d) m4 shttps://forums.scummvm.org/viewtopic.php?t=7168& Z) @9 t5 K- j
[Scummvm-devel] Displaying subtitles in voice-only games (narkive.com)
^( e7 E0 Q5 z, M. M5 E7 v
! [* \% U$ _ ]$ }2 i8 m前段时间第七访客出了25周年纪念版(The 7th Guest 25th Anniversary Edition。
+ X- ^" X9 x0 u, r在这个版本中,游戏的视频是有字幕的,因此把这个视频的字幕用到Scummvm中就成为了可能。8 f' I; ^- ?9 a* v
* b$ H) ~/ t0 U3 [4 [2 S0 ^( {我查看了一下最新版本的Scummvm的代码,发现老外已经加入了一部分代码。3 }/ t0 C) ^0 U `% [" R
在此基础上,汉化是大有希望的。此种汉化方法,也是前所未有的,如果真的实现了将会有重要意义。
8 E5 v" N' s' ?. }! t
; J, x4 y: W" a7 e( I类似游戏还有- · Groovie
! [6 A* ]3 S T- C; D& |* ] - - The 7th Guest
4 O9 {, S; h0 K* s6 g- v4 `7 I - - The 11th Hour! E; `+ H7 P( A7 }; s. n
& _( O+ @7 T- X2 ~- · MADE, F I4 Y1 i# U5 N$ C7 H* J
- - Return to Zork (descriptions are text only, while spoken dialogs lack' C) ~% I- m: A9 x
- any text)6 D" i9 W7 p0 D& u/ F
& @$ E+ B0 @5 l. J. V9 S* c- · SCI 1; ~5 ]& h+ J% Y$ {
- - King's Quest V (CD Version). In this case, just enabling voice and text9 H1 b& R9 ~ q5 l. H# B! m; N
- simultanously would allow a 3rd party to create a set of MSG files' P* A8 g; B2 P* Z6 g
- containing the voice transcription/translation, as it happened with Gabriel) R: j2 o" S. e, S; l" v
- Knight II.4 A( n8 ^9 D, X8 s1 g2 q
- 0 d6 e, e( v- V4 M/ E* U$ s D
0 v' G. H% i8 Q. B' D8 P' S- Future engines:! G* D" N( u& y3 a. a$ S' c' I) i! u
: f% {' u: q+ H- · SCI 21
3 L" R- |, | Y; `/ L - - Gabriel Knight II: The Beast Within (Already a fan patch available in
# m8 b2 }- n2 C: g ?$ K - order to enable the text display feature)
/ {5 ?% @* n: @4 B3 |) u8 D - - Phantasmagoria
: E- C* L4 i3 ]2 e$ o1 j4 ^( E - - Rama% O9 k& a) i- Z
- - Shivers# k* |/ A( q0 P, ]% d2 `3 D8 c ?
- - Phantasmagoria II: A Puzzle Of Flesh
5 r( K. W/ r5 D9 k1 @2 F
) e' `5 n4 f' p9 { ]" @- · SCI 3
1 H7 I7 Q/ y7 S" n/ t - - Lighthouse: The Dark Being) W( p4 c# n7 U) r
+ c; A4 Z& a* X1 ~- · M4/MADS
- t0 Q6 ]7 K B+ t! }0 H% i+ ] - - Orion Burger
0 A4 l$ q; _# e) ]
; e% }( S6 J* M# E4 c; E! H- · Mohawk
3 d8 |3 e! D0 S1 N F - - Myst
: t8 _ \. g+ F% f7 \3 R( s9 w - - Riven: The Sequel to Myst
复制代码
5 @: j6 D& x$ _: k# \3 s; e$ u) P
! L( A8 }- A0 ~) Q7 a) c7 x0 J4 P/ W, g4 ?
( E0 Y+ k2 h& ~" J1 z3 ^5 L. y相关代码如下
) H, K/ t! f( h# f- Y; }$ c- bool VideoPlayer::playFrame() {) {( N/ R* e W# s2 d5 Z1 I* X
- bool end = true;
$ V0 ?/ |- f3 Z: j/ t - T1 \$ {- C5 n P
- // Process the next frame while the file is open9 N" G' Y$ e! E
- if (_file) {' C, {7 ]% Q+ r6 u5 l
- end = playFrameInternal();
- b! j' }# }* k& q
2 n5 z0 C) }5 t8 |% ~. w- r! t+ j- _subtitles.drawSubtitle(_lastFrameTime - _startTime);; m( Z- B p1 ~& E; D' h8 k
- }
复制代码- bool Subtitles::drawSubtitle(uint32 timestamp, bool force) {' c: F3 \% [* f% x; u; A3 _' l
- Common::String subtitle;
9 p6 S* ]+ t$ d |2 W H6 ` - if (_loaded) {
$ }& @& t8 ^ R - subtitle = _srtParser.getSubtitle(timestamp);
3 l8 [* E+ y) a3 s R - } else if (_subtitleDev) {+ C9 b- E; j' l. |
- subtitle = _fname;! G$ G$ I" z, s1 t2 u3 Z" [4 X! y
- uint32 hours, mins, secs, msecs;$ Y5 ?0 S- A0 V% f. r8 y6 ?
- secs = timestamp / 1000;
; L% [8 ~6 n* L% h - hours = secs / 3600;0 s$ _' \4 C; C! `+ V1 D& N
- mins = (secs / 60) % 60;
8 d% _# u4 `2 X% `# f - secs %= 60;" u: u1 @6 R. W7 l+ W
- msecs = timestamp % 1000;3 _( x; R) k) Y' Q8 { a5 x, y
- subtitle += " " + Common::String::format("%02u:%02u:%02u,%03u", hours, mins, secs, msecs);1 u/ V' d$ q! ?% N
- } else {
+ i$ d! s8 ^" _- _ i) } - return false;! P+ U9 s# U8 U3 l @8 ` C
- }1 d$ @# r5 `& M; i6 i8 _( [% c5 R
$ l7 G8 c3 x) d3 z4 u- int16 width = g_system->getOverlayWidth(),* e/ v: ?$ Y X* d: l+ r
- height = g_system->getOverlayHeight();9 j- ?2 N: P9 l
- + ^: e/ ^/ y' W
- if (width != _lastOverlayWidth ||. L- B' m$ w' ^7 R$ L
- height != _lastOverlayHeight) {
7 y1 Q9 H7 L: \1 H+ X" T2 j& I7 C - _lastOverlayWidth = width;
, J9 u. {# ]4 l B% \ - _lastOverlayHeight = height;: y: q' Q1 T3 j
5 r/ F" }6 w1 i8 i# M: |- // Recalculate the real bounding box to use8 X) ?- S+ h% K% X
- _realBBox = _requestedBBox;9 J7 p8 E& l; v* J2 F$ s
- 4 A6 G( R3 g) Q! ^8 q
- if (_realBBox.bottom > height) {
4 |4 r4 \: k1 P) f; f - // First try to move the bounding box
( _1 m* V* Z4 S8 q# v" v3 @% J: R) F - _realBBox.top -= _realBBox.bottom - height;
6 H4 u1 {3 ?$ k/ N - _realBBox.bottom = height;& D( n2 y! `) I# P3 d
- }- r3 J$ {5 a+ J- S$ K' R
- if (_realBBox.top < 0) {
' D* f; X7 [2 N; I& i9 j - // Not enough space' a2 I- u, `" R. B' p
- _realBBox.top = 0;
. V) O3 K O' X# } - }' Y: f! E3 p( w$ y
& \3 h; W* V3 j* _) v a- if (_realBBox.right > width) {
" r* O; D) _, n- [2 [) V - // First try to move the bounding box
7 L/ y$ A6 s# _) C; [5 }; l - _realBBox.left -= _realBBox.right - width;$ @- }; f* A8 N9 V5 [( ]
- _realBBox.right = width;
+ a* a9 v* d9 w# O8 ~ - }
8 b# A- F% ?7 T" o# l, { - if (_realBBox.left < 0) {
$ f h, U2 W0 f* V$ v% x - // Not enough space
z9 y" h/ n) S3 a% U - _realBBox.left = 0;
7 N* Y# H# G5 o+ D, t - }& r, O1 q* B* _/ g4 x
- 9 j- X, [. r2 E% \8 p/ j
- force = true;
; B# [# P% W" M* N( a0 w8 K4 l - }
5 b1 I' Q- H$ \6 I - - S, w: o3 {. N: r; X6 h
- if (!force && _overlayHasAlpha && subtitle == _subtitle)
: m4 s( @( K% @ - return false;9 C. O# @! ~6 {% ~" Y! X
- / [! ]( c$ N. ^
- if (force || subtitle != _subtitle) {5 D1 I5 K) A2 i. R6 w/ I8 S) d
- debug(1, "%d: %s", timestamp, subtitle.c_str()); z }4 F( L" f7 M# S
) X S- `8 ]% a" Q7 g4 N9 t7 l- _subtitle = subtitle;- n2 f9 |+ ]( L) S( j ~
- renderSubtitle();
, V5 \7 }& E2 C: Z6 C% _8 O - }. b6 b, ^+ R6 s+ k5 J
5 h$ q; r! H$ n( \- if (_overlayHasAlpha) {3 j& Y# i% V& o1 w& b3 n
- // When we have alpha, draw the whole surface without thinking it more
7 g& A* k3 T/ z6 s I - g_system->copyRectToOverlay(_surface->getPixels(), _surface->pitch, _realBBox.left, _realBBox.top, _realBBox.width(), _realBBox.height());
5 j9 ]9 W+ v+ W2 Y' H, A - } else {! x1 H% i; ?" S5 e1 a1 l. s
- // When overlay doesn't have alpha, showing it hides the underlying game screen
8 t' i& E' O$ D$ o: f3 N - // We force a copy of the game screen to the overlay by clearing it
4 ?. `% G- n6 O - // We then draw the smallest possible surface to minimize black rectangle behind text9 q \$ g2 g" l6 g
- g_system->clearOverlay();
9 y I' @" v, s! j* ? B {+ J8 q; n - g_system->copyRectToOverlay((byte *)_surface->getPixels() + _drawRect.top * _surface->pitch + _drawRect.left * _surface->format.bytesPerPixel, _surface->pitch,( M/ \8 M/ X' t8 O, o7 X
- _realBBox.left + _drawRect.left, _realBBox.top + _drawRect.top, _drawRect.width(), _drawRect.height()); P$ o+ J' T I" A h, r
- }
5 O3 w& Y. i) d
% F3 C5 \' }7 w7 ^0 j6 `1 @! }- return true;
: y P7 ^4 ~4 c3 W) F - }- Q% C" s7 s$ Y# t) o, B
复制代码
$ B. m/ U: R+ y" u( w' X8 ?" t2 B* d" P) i" ~
( J& d% [% g$ \6 t! P! Z/ y6 \8 K2 d+ O1 Z' h5 c- t/ C6 d
4 A! i2 a' @+ n" i- \1 X/ L& j, E& f& D) ]9 ~/ e |
" e) @" W1 s: z4 X. P; J
|