本帖最后由 shane007 于 2023-8-12 13:57 编辑
+ Q1 i) I9 I! O* j7 A5 n" T: a+ I% S$ ~' ~
这个游戏原本是没有字幕的,但是在Scummvm论坛上发现了一个帖子,
3 G& T: I& |& Z- K讨论的内容是关于在原本没有字幕的游戏上加上字幕。(最初的缘由,是为了帮助一位失聪的朋友)
2 P: c6 W! V, m0 v1 K" p6 Xhttps://forums.scummvm.org/viewtopic.php?t=7168
9 h P+ N% g9 ~- T- j' U/ q! u[Scummvm-devel] Displaying subtitles in voice-only games (narkive.com)
7 n0 p& j. i: d& s# V c: |4 @0 ~4 O5 \4 d) F
前段时间第七访客出了25周年纪念版(The 7th Guest 25th Anniversary Edition。
U' i1 s( n; N7 Q# f8 p& [在这个版本中,游戏的视频是有字幕的,因此把这个视频的字幕用到Scummvm中就成为了可能。
2 G. l- o! T3 \$ k! D
# U+ \3 A% u6 [% d/ }我查看了一下最新版本的Scummvm的代码,发现老外已经加入了一部分代码。8 q+ j4 A* T' h, F9 w
在此基础上,汉化是大有希望的。此种汉化方法,也是前所未有的,如果真的实现了将会有重要意义。: ^; U& v s2 e; y8 H
. Z2 S( r4 n0 \. F( l4 m; p类似游戏还有- · Groovie
" W3 Q. l4 B! D9 l - - The 7th Guest
1 }- H( K: E0 i2 D0 r h9 b3 e - - The 11th Hour
/ G8 m3 O9 F* Z3 E! _; D! H) S* i# i - 1 Z+ V! k) j+ D6 a9 ^/ o$ b: p3 e. i4 ]
- · MADE
4 }- V+ k ^% g2 T: [ - - Return to Zork (descriptions are text only, while spoken dialogs lack
+ t8 `9 I# B( G8 c5 o - any text)
6 H6 y2 r* ^4 B' l( o
1 j3 Z$ p; S5 g, F0 D/ S- · SCI 1
. C3 i. j. z; D+ F. i( J t - - King's Quest V (CD Version). In this case, just enabling voice and text1 ^/ ?' u+ `7 Q7 h. Y/ ]
- simultanously would allow a 3rd party to create a set of MSG files& b, p x1 A3 n) n6 J8 G
- containing the voice transcription/translation, as it happened with Gabriel* q7 B! P2 F/ w w' c( C$ ]
- Knight II.- T$ I h! d) G0 z
- ! r: n' y: w) F$ e3 d' K
; b" v2 m! A! r) [" V4 v) ?& ]2 F- Future engines:
% `: ^. e& o" ~; a' `
% H; D$ I& y& K- · SCI 21
' g+ }2 M1 X+ x) H4 L1 y - - Gabriel Knight II: The Beast Within (Already a fan patch available in' A9 A7 f, a2 |* c: m) d
- order to enable the text display feature)8 l- g# z" f! v/ Y) r9 a
- - Phantasmagoria
: O4 i1 A- I: `& w5 k - - Rama( K1 ^) |& T0 u" V. |
- - Shivers
7 i- E+ T3 ^' G - - Phantasmagoria II: A Puzzle Of Flesh4 ^6 h, s$ S9 O+ B! X( i
- - Q4 }7 f+ P- C2 X
- · SCI 31 I( r7 l" I9 }+ e( D5 o/ r
- - Lighthouse: The Dark Being
& a W( I6 O2 I, B - ) I: _3 m$ ?# N- O7 V4 `0 M/ o
- · M4/MADS
1 Z* X$ M* P# N8 Y - - Orion Burger
( }* o" m8 x, e9 v& ~ - 1 N2 p6 y D. D* p5 y" h
- · Mohawk
0 ~7 G( [- J) U; X, t0 `/ w - - Myst: n3 ?. c! b) A+ U2 u
- - Riven: The Sequel to Myst
复制代码
$ f3 z4 L2 B& }6 P) g
?! X6 q$ p: @& I% b0 Q6 U/ q* q: M. @4 J0 G# a7 e9 d( N
: C, P" C' w) M: w: x0 l6 l相关代码如下$ S: J4 y) j- A; n
- bool VideoPlayer::playFrame() {
% |+ K" [7 t+ i6 ] - bool end = true;- i' X9 }2 B* d i% s
- 6 i( S4 |! {% C) n* ^3 X: F0 o
- // Process the next frame while the file is open/ j) g! h8 ]! h! t! K8 }9 `
- if (_file) {+ O2 a4 l; _& ~; {( f
- end = playFrameInternal();" C5 R8 x& Q" S# s! T& R( G% a
- . Z, A, T0 q7 B2 j2 S4 C
- _subtitles.drawSubtitle(_lastFrameTime - _startTime);: t, ^/ _$ `& ]7 ^* n" X3 C- t
- }
复制代码- bool Subtitles::drawSubtitle(uint32 timestamp, bool force) {& _; j( r! m! ?& {: K1 z. I
- Common::String subtitle;4 N# o! e5 L% B" h3 [& z
- if (_loaded) {
! N/ u3 L5 T+ H* p) ?$ A- C - subtitle = _srtParser.getSubtitle(timestamp);$ M2 I0 `. c1 p0 q {
- } else if (_subtitleDev) {4 j2 I) d6 z3 e; h6 I1 {2 w
- subtitle = _fname;
) D7 P8 u; U0 _. I0 @0 N - uint32 hours, mins, secs, msecs;
. M" Q, \1 G# o9 S7 c R1 ~( q! o - secs = timestamp / 1000;
( A' ], q" P- [ - hours = secs / 3600;$ x. M! N* b( l4 t3 h1 ~
- mins = (secs / 60) % 60;
! I+ U5 [2 }. y7 P; M9 D - secs %= 60;
0 M$ `+ q3 R# c, N. F - msecs = timestamp % 1000;7 L7 Y6 r, Z1 X! J, C
- subtitle += " " + Common::String::format("%02u:%02u:%02u,%03u", hours, mins, secs, msecs);) M8 N" N' l) p9 c2 V
- } else {
2 t, h8 Q5 S7 D! p - return false;
$ K1 g( h1 @7 Q- |4 Y7 B - }/ [9 ?) [* |1 A+ j
% c7 _$ R! i+ S- int16 width = g_system->getOverlayWidth(), i6 b1 ?0 p" o# L. f# o
- height = g_system->getOverlayHeight();3 y9 B4 f2 x, v
' [" ]: B; G- u! K1 ], H+ R- if (width != _lastOverlayWidth ||9 n- U1 G# @1 L* p3 C0 [- Q
- height != _lastOverlayHeight) {+ }, D3 |1 ?! q# B; k
- _lastOverlayWidth = width;
( h* w3 `' d- x" d2 ^# n. W - _lastOverlayHeight = height;
9 I7 J' @4 h2 t, s; b' Y7 g" u - / Z) J5 _# l5 a" v7 G- z
- // Recalculate the real bounding box to use5 b6 w$ a2 I r7 R# v9 Y' S1 p
- _realBBox = _requestedBBox;' b v1 f9 j/ Z$ N e
- T6 J i! s# N- [
- if (_realBBox.bottom > height) {5 V" u. ]: `) g, D6 X c& k- A
- // First try to move the bounding box" |+ T6 s* Y4 L. F% c
- _realBBox.top -= _realBBox.bottom - height;7 \ n9 M; x6 l. C" K& t
- _realBBox.bottom = height;& H, a; r h* U9 H0 Y: `4 ]
- }
9 d8 R6 m( v: v - if (_realBBox.top < 0) {
6 f+ a( n, v6 T1 l4 l9 r - // Not enough space
. h- g, i% q8 }- Q3 q k5 H - _realBBox.top = 0;' s5 _8 i4 ]9 Y! Q) p% `7 Y
- }
2 ^( p; `% }' t - + ]5 h8 F* e. z
- if (_realBBox.right > width) {& e! K5 j% S- y; g; }' n
- // First try to move the bounding box
0 |% y% P: l/ t& P' x! Z - _realBBox.left -= _realBBox.right - width;
' u/ o0 Z; _: `* n* c* l" i% _ - _realBBox.right = width;/ E* s2 o5 b: A8 i& {
- }
" J F0 I1 r0 K4 \( P - if (_realBBox.left < 0) {
& P$ W$ s+ u+ _/ ^4 ]$ g1 k' } - // Not enough space
* i! O5 C h+ V$ s& O - _realBBox.left = 0;, u# C* s, X0 }$ \$ `% _; D1 ]
- }
. x* v9 c6 \# s8 _6 }) E* Z d - 5 n7 H2 Q# U6 d9 ?% w1 |! K
- force = true;( ]; v0 e$ m! E* i% b
- }
$ W$ P, `% k( G) X - 2 z' v/ ]* B( p$ |9 w
- if (!force && _overlayHasAlpha && subtitle == _subtitle)
7 g+ i7 O- m- }4 I# P: T! I - return false;; i% l# B& H8 x/ c& q7 [' N
( [) j; r4 G! H3 b( c- if (force || subtitle != _subtitle) {) }! _0 o' X/ u
- debug(1, "%d: %s", timestamp, subtitle.c_str());& ]/ G; m1 {! U
2 x9 ]. n, \$ \% ]0 x- _subtitle = subtitle;' Z' ?" k& l R( n% m1 T0 N8 d: `" H
- renderSubtitle();# B8 H0 c; j1 f/ h; }
- }
/ u o2 E* ^! w0 a - 3 P3 }+ d- O" ~) Z8 |" Y% Q, n
- if (_overlayHasAlpha) {
* f, Y- z( {4 ?: b6 G2 f3 X - // When we have alpha, draw the whole surface without thinking it more
m. I e3 U7 t; w - g_system->copyRectToOverlay(_surface->getPixels(), _surface->pitch, _realBBox.left, _realBBox.top, _realBBox.width(), _realBBox.height());7 _7 n( [ B6 t) H( m' p. p
- } else {
& p) P I2 l; q: ~8 a+ x - // When overlay doesn't have alpha, showing it hides the underlying game screen5 c5 Q( b+ A6 b& g3 ~& t
- // We force a copy of the game screen to the overlay by clearing it) x% E3 G( q1 Y' n9 j3 Y' P
- // We then draw the smallest possible surface to minimize black rectangle behind text
, [# E q. _0 ^ - g_system->clearOverlay();
3 r2 ~- R+ f5 U: C4 Y- O8 | - g_system->copyRectToOverlay((byte *)_surface->getPixels() + _drawRect.top * _surface->pitch + _drawRect.left * _surface->format.bytesPerPixel, _surface->pitch,
2 |' i: C$ m2 K& h: d7 @ - _realBBox.left + _drawRect.left, _realBBox.top + _drawRect.top, _drawRect.width(), _drawRect.height());- k- y: I) _4 y6 u2 ^' K
- }
" ~/ {: `. |: |) T4 ?
1 S& }: T! ^$ `# n- return true;
* l9 d3 c+ F5 Y5 X - }
: c+ m3 R- j0 q) }$ V; h( ~; s& R7 v
复制代码
4 `' d2 H+ i. `0 r" t2 B
C9 o# x3 V; }1 |: N; r' w R+ N# b8 J# V3 r
2 | m! l: \2 C9 Y* f |6 m. r9 r d1 l4 a
. t* p, N! H& ]; P+ F6 m: g( |5 z- V
% i5 S5 R+ v x+ w- |
|