本帖最后由 shane007 于 2023-8-12 13:57 编辑
7 j) V, ]! t; j, q
7 I* C) u6 l; v这个游戏原本是没有字幕的,但是在Scummvm论坛上发现了一个帖子,
" w5 y r5 N& {讨论的内容是关于在原本没有字幕的游戏上加上字幕。(最初的缘由,是为了帮助一位失聪的朋友)
& j% Z0 A8 q& a6 A' Ohttps://forums.scummvm.org/viewtopic.php?t=7168) O- a4 W3 r5 k, s$ T/ f
[Scummvm-devel] Displaying subtitles in voice-only games (narkive.com)3 p# {/ X0 q: g6 O8 y( _. ]+ R
: U5 \& ?) b/ Y* K/ `
前段时间第七访客出了25周年纪念版(The 7th Guest 25th Anniversary Edition。( W6 U) u/ X1 ` P2 L0 A- ]
在这个版本中,游戏的视频是有字幕的,因此把这个视频的字幕用到Scummvm中就成为了可能。
: }1 s0 ^4 [% t* e. f; P2 T- M" Z" f# f# o) I6 v0 a
我查看了一下最新版本的Scummvm的代码,发现老外已经加入了一部分代码。
2 J6 m7 L' K6 L在此基础上,汉化是大有希望的。此种汉化方法,也是前所未有的,如果真的实现了将会有重要意义。* d: m+ D, E- r, \1 y6 I
/ v( [& U9 \! K0 ^- p# l% W
类似游戏还有- · Groovie
5 D3 g5 ~2 F& F/ {% T0 y - - The 7th Guest& z5 r) K6 ~9 u+ ], `9 r* y2 a
- - The 11th Hour
8 V) m. \$ y+ \& Q k
& a' L! @1 d; B5 g4 T2 C- · MADE2 R/ y- o! O' Q5 k( h3 b8 k$ A3 G
- - Return to Zork (descriptions are text only, while spoken dialogs lack
, a9 A" \6 r. U - any text)
: B* T: `: C6 _0 n5 |0 h* N
- H3 s/ f' |5 Q! w* ] \- · SCI 1% I: _: p7 ~$ b, l
- - King's Quest V (CD Version). In this case, just enabling voice and text, N6 D( T0 h2 S+ r i5 e- k! F
- simultanously would allow a 3rd party to create a set of MSG files
8 G' z% [0 ~! j6 e, z4 ^. L7 o8 t7 `3 A - containing the voice transcription/translation, as it happened with Gabriel$ g) g: p$ ~- \3 e0 p$ C k1 @
- Knight II.
1 G: z p$ r$ F. \5 C+ K
: l. D2 W4 \# ^. y/ y) \! {
: I8 D4 g8 g$ U/ A3 o e r- Future engines:! a6 @+ X: @) {( ]" O
- ) Q. J. y9 \; k9 `- h/ c1 d
- · SCI 21
6 {; \1 P. \7 e/ P& y% N/ X1 N - - Gabriel Knight II: The Beast Within (Already a fan patch available in( g( o/ K+ Y) o$ X" t+ c0 B
- order to enable the text display feature)7 j! U4 _0 A. t4 [/ C5 b; @
- - Phantasmagoria- [' H& p- c1 B+ P; [5 _: x
- - Rama) Q( S* C1 g5 p
- - Shivers
/ M* y7 ?. @1 {1 X$ Z2 f. i - - Phantasmagoria II: A Puzzle Of Flesh
& Y1 T9 o: X+ P - " K4 e" w7 J6 T- G) {3 `
- · SCI 3! c% k _3 `8 C1 w G3 ^
- - Lighthouse: The Dark Being
; I2 U( V; k& y$ I, Q6 d) \
, c3 \ {, L1 F- y3 m7 Q- · M4/MADS
9 I: Y" a {) o; U8 ] - - Orion Burger
; U% ?% l2 o: z: ?
6 c- h% W' Z: m3 n: I- · Mohawk
9 [0 o6 E3 S2 v) Z7 a - - Myst+ g' d& _/ n+ Z) ^! ?
- - Riven: The Sequel to Myst
复制代码
. X( {/ r+ ^# u; z
4 y( P2 R1 x) h" Z3 H5 R {9 g: j8 I3 P V5 s7 m
" {1 t* v' K' {6 f
相关代码如下
- p1 w4 g( c$ N+ z4 R- bool VideoPlayer::playFrame() {, F' ~9 n) s( k; Z5 H" R7 `
- bool end = true;/ m) ~: P% k: g K$ _- L
+ k% O% T1 A8 r6 e m- // Process the next frame while the file is open
{0 ]9 i3 z# `3 J - if (_file) {
' N5 h: t, s% h4 Z5 U1 u8 S# [ - end = playFrameInternal();
; B. @$ m$ f7 k8 i% H - 4 l5 w2 B- ~7 l' N& P1 A: j+ A
- _subtitles.drawSubtitle(_lastFrameTime - _startTime);, o8 e- o9 i" {+ E' b D& M; P/ u
- }
复制代码- bool Subtitles::drawSubtitle(uint32 timestamp, bool force) { R2 N% ]7 Y1 p$ J; k' x
- Common::String subtitle;
, O; l1 k1 Z" r" I+ h - if (_loaded) {: x0 M3 r- v* ]1 d
- subtitle = _srtParser.getSubtitle(timestamp);: J& G% e F7 c6 h) n
- } else if (_subtitleDev) {5 J# T' g7 z' q! F8 U5 S, V$ G
- subtitle = _fname;
8 s# m' E3 D5 y1 a2 X" ^9 m9 r - uint32 hours, mins, secs, msecs;+ [3 t* a9 e+ T6 q$ O! ^
- secs = timestamp / 1000;
/ O% s8 |' S8 p - hours = secs / 3600;
1 r/ l+ J" E h( [: M - mins = (secs / 60) % 60;( m7 `+ z K0 X" m2 F4 U
- secs %= 60;. y U4 r# H; w8 l* h
- msecs = timestamp % 1000;
6 F6 B; z2 D! d; y8 Z/ M. \* }, N - subtitle += " " + Common::String::format("%02u:%02u:%02u,%03u", hours, mins, secs, msecs);4 }5 w- `) a |+ o" f
- } else {
1 E( m' d/ F; c! q$ O! ~* D - return false;0 r' }' B. A x4 U4 u) B# r
- }
' }4 A! @3 w; g0 d9 ^# ~
; }% a* H8 ^) D- int16 width = g_system->getOverlayWidth(),4 c1 u8 v/ t% d' c/ M: ], B4 y, `& [
- height = g_system->getOverlayHeight();- T9 r. @6 } W* W( r* R
- C. g6 P8 z Z3 W- if (width != _lastOverlayWidth ||# {% g; ?0 E6 r- _' d, a
- height != _lastOverlayHeight) {
P7 M4 Q& y' V/ S- B; K9 L - _lastOverlayWidth = width;
/ p3 B* j/ U5 W5 V - _lastOverlayHeight = height;
# S8 L" h# `' c: e
% _3 }( T0 _, l; M. v' M* r- // Recalculate the real bounding box to use% `( h5 R$ @' T3 I# O' S5 U
- _realBBox = _requestedBBox;) F- a6 C5 P% k, c- L* p
- 3 p. ~9 G+ u% }* W. k# t. a& X7 Z
- if (_realBBox.bottom > height) {
% r2 m, o' |! t+ h6 @ - // First try to move the bounding box
) y0 `1 h% m8 [5 y6 C; s - _realBBox.top -= _realBBox.bottom - height;4 b3 m% M' |4 }# X9 v, g' v7 C8 |
- _realBBox.bottom = height;
" P' a8 t( `! `; v - }" X( c T, l J% P- r* m2 @
- if (_realBBox.top < 0) {
$ a& n# o9 c* k( B& W; E - // Not enough space V: q' k+ M8 l% b; ?8 B! }; m
- _realBBox.top = 0;$ \. o7 s7 D. J$ _& \) P
- }
+ F* q V' n, q3 S% F - ' k- F; m" z$ x1 a3 u
- if (_realBBox.right > width) {
/ n( D3 f- Q1 W7 H" n4 { - // First try to move the bounding box
) m9 t3 c$ U W4 v* k5 U/ \ - _realBBox.left -= _realBBox.right - width;
, D: W; f0 K+ ^6 ? - _realBBox.right = width;
; X; H) Z9 M. B3 s( F+ o% g# { - }2 f" T" S; F* q9 q* w2 Q
- if (_realBBox.left < 0) {
) v1 S9 }; {, i; F) r9 }3 m/ d - // Not enough space
: \0 _& o; G6 {' F2 e - _realBBox.left = 0;
, i7 Q( x+ ?2 i" k - }' U3 X V5 O' |: G4 @9 V
- " D- d8 z0 S4 C$ G
- force = true;
9 `, [3 j& l D - }
* x2 X# ^8 g# V+ L/ p q7 D* n7 r - 6 m1 b* F6 A# x" B3 q5 R7 z& q
- if (!force && _overlayHasAlpha && subtitle == _subtitle)8 S* n4 @" R3 [0 T w/ T
- return false;- `& h p/ N5 w3 k6 m
( V* D1 E+ y v, L- w0 o$ Q( o! b- if (force || subtitle != _subtitle) {
+ t, h5 G3 h" {2 i, s - debug(1, "%d: %s", timestamp, subtitle.c_str());& B7 V. O* W& \ ~7 y
- ; I5 o! s! x' T" k2 f( k
- _subtitle = subtitle;6 j( s0 D) s' q3 a/ m: X
- renderSubtitle();
) `8 f, o E* V/ } - }& ?0 _$ y6 I) r: i- B" j
4 z' m( w( i% C4 E3 }- if (_overlayHasAlpha) {3 r$ ~ \" _' ?6 y2 J
- // When we have alpha, draw the whole surface without thinking it more
S; _8 I6 p$ g# r {* q/ }( s - g_system->copyRectToOverlay(_surface->getPixels(), _surface->pitch, _realBBox.left, _realBBox.top, _realBBox.width(), _realBBox.height()); p3 k& u5 p. y$ S/ l( ^
- } else {
0 U/ n' K: z, t9 ]2 |: V: P - // When overlay doesn't have alpha, showing it hides the underlying game screen9 P9 ?5 m1 M( N1 R, a; ^6 n0 b
- // We force a copy of the game screen to the overlay by clearing it: R5 c# I+ h9 [
- // We then draw the smallest possible surface to minimize black rectangle behind text# X2 s% n- O4 ^# p7 Y
- g_system->clearOverlay();( T" P+ t0 T" b. ^
- g_system->copyRectToOverlay((byte *)_surface->getPixels() + _drawRect.top * _surface->pitch + _drawRect.left * _surface->format.bytesPerPixel, _surface->pitch,
& ~& h8 N/ v3 D" U - _realBBox.left + _drawRect.left, _realBBox.top + _drawRect.top, _drawRect.width(), _drawRect.height());
2 W0 r; ?" m+ R1 ~4 F) F - } A9 n0 Q' M2 v0 t
- 9 n. I2 S6 ]3 G6 ^/ P, ^1 \
- return true;9 `, ^1 Z7 r8 H: [
- }) M; c; }' C4 D
复制代码
/ g4 E. m; I9 F" d
& E. W8 q/ Q: R8 G1 L. ?
( t A6 v- F- Q% V
0 ]$ o. }6 Y6 Z0 `/ U" c) f- w7 g1 g/ A" `: k# p: N W
# d# n; G8 ?% b
1 @) O( H l+ g) U |