本帖最后由 shane007 于 2023-8-12 13:57 编辑 6 [+ a- l3 H. Z1 u1 I# t
# N: _, b; i; N4 j" B8 h! R这个游戏原本是没有字幕的,但是在Scummvm论坛上发现了一个帖子,
3 I7 k+ ?" x1 f4 C6 V' t讨论的内容是关于在原本没有字幕的游戏上加上字幕。(最初的缘由,是为了帮助一位失聪的朋友)6 i+ S0 ?3 v; f1 j
https://forums.scummvm.org/viewtopic.php?t=7168
`2 x4 O: u m Q[Scummvm-devel] Displaying subtitles in voice-only games (narkive.com)
0 ]- D0 C6 L0 C& x7 T( c- F+ ~/ ~/ A1 [6 D. q! }: Z
前段时间第七访客出了25周年纪念版(The 7th Guest 25th Anniversary Edition。
9 n! b. V5 X& [" U+ T2 D2 y; F: i在这个版本中,游戏的视频是有字幕的,因此把这个视频的字幕用到Scummvm中就成为了可能。& }) N ^% k1 M( R6 a) Q
* I, t- T) i% @/ X% o! R
我查看了一下最新版本的Scummvm的代码,发现老外已经加入了一部分代码。
* ~# m' C8 ?; |& {2 n在此基础上,汉化是大有希望的。此种汉化方法,也是前所未有的,如果真的实现了将会有重要意义。
9 q8 r$ g' k6 B# T; a' m7 V5 | O, E! M! @. l* \' N b
类似游戏还有- · Groovie# J Q& Y" [( h! J. h
- - The 7th Guest4 c( v. ?9 q; ]" S1 |* ?
- - The 11th Hour f6 l. ?2 }4 p0 o
( y- d/ ?7 P4 p" ^$ P- · MADE
. v+ o/ @3 @8 q5 w1 b* V+ o2 r - - Return to Zork (descriptions are text only, while spoken dialogs lack; B% q; c$ q; Z% V# ]3 i: l
- any text)
) H) L) Y! S; S6 N/ A/ y( a - 0 A5 M1 t6 @0 P9 c
- · SCI 1
, Y3 g7 C+ [7 D" O0 \" y$ | - - King's Quest V (CD Version). In this case, just enabling voice and text
3 i' A- }- \4 ?) ?, i) R! f - simultanously would allow a 3rd party to create a set of MSG files
% J& A& `3 r+ d6 ^ y - containing the voice transcription/translation, as it happened with Gabriel' `; J( O# u" |& m
- Knight II.% q5 [3 {- g# ]' s; g
1 E- v) d$ S! }9 E/ g2 H' |- ; D H: ?6 t) Q4 N3 t
- Future engines:
3 b) v) ~6 X" S0 r
5 |2 I7 @! R7 i8 j8 ~6 u, U7 O- · SCI 21- X8 V; u. U) r6 Z; ]
- - Gabriel Knight II: The Beast Within (Already a fan patch available in
& K b+ }0 l; A% o q - order to enable the text display feature)
7 K3 D4 y0 g0 B6 Z) R - - Phantasmagoria
, a9 Y$ I2 J6 _$ ~$ x+ L - - Rama
5 A x& N D( X6 F! F) n, n6 M - - Shivers% e) o. h6 g7 ~7 b6 b* \' H0 O# H
- - Phantasmagoria II: A Puzzle Of Flesh
+ g3 I6 @: e! S* E/ y
4 {% j8 a( E# k; c7 F" o2 Q- · SCI 3
j% h9 { C3 W - - Lighthouse: The Dark Being
+ P y. q2 L4 h- b% m% E- @) F
& u* i7 o5 A+ w- · M4/MADS
, f1 I: _, R+ Y( d6 x; h - - Orion Burger/ y. i+ e' k I$ E. H1 {; v: t" U1 F
- $ L2 a9 }6 T# m6 L9 a
- · Mohawk
7 e' e8 V) b. w, @! P5 I# I - - Myst2 h7 F! |9 [- P
- - Riven: The Sequel to Myst
复制代码
& R% }! T, o1 Z# P
2 ^6 _- A4 e3 i# n3 ~$ x: B% A7 ^; x
* i0 M6 e' H, M; l" _" l# ]相关代码如下9 q& ~( L( t( y* R K& L) ]
- bool VideoPlayer::playFrame() {8 v0 S# l' J9 {" v7 j9 u$ H
- bool end = true;) v; v9 e4 p. }* D$ t
- n% b- b1 x: T! k
- // Process the next frame while the file is open- V7 K& O ]$ D, d+ f" @% F
- if (_file) {2 w1 y7 ?' k' |) [1 L# R
- end = playFrameInternal();; S* r( ~9 `! E4 r& q' ?1 ^
- 8 ~0 v0 R5 m' i9 F. }6 e+ J
- _subtitles.drawSubtitle(_lastFrameTime - _startTime);
5 B- ~: P; \% o- t" n1 R$ [" w - }
复制代码- bool Subtitles::drawSubtitle(uint32 timestamp, bool force) {
7 i; u8 y3 a1 i" ` - Common::String subtitle;' H) u t' P h1 h+ r) ~
- if (_loaded) {
! K2 q' \3 o- }! p" j - subtitle = _srtParser.getSubtitle(timestamp);
5 v$ m& W/ \0 l3 l* q - } else if (_subtitleDev) {( t9 c, s! _. ?. R
- subtitle = _fname;
+ s6 p% C9 ?! b; \: w9 u, C4 b - uint32 hours, mins, secs, msecs;" g1 R [/ U- n& c
- secs = timestamp / 1000;
4 a( Z: y1 D' Q - hours = secs / 3600;
% K3 }+ v x( P% u9 a& z- r: ^5 `" u - mins = (secs / 60) % 60;
$ B9 V; J( o) h; T* N - secs %= 60;8 X3 T' ]/ T: |: L
- msecs = timestamp % 1000;- d4 c( e0 I3 ~8 U f! W
- subtitle += " " + Common::String::format("%02u:%02u:%02u,%03u", hours, mins, secs, msecs);
; `2 f& A4 R2 G! Z. ?6 W - } else {1 f' H' D6 S. {: {5 C( ~
- return false;
2 l# Z) `6 p- r& J9 f9 Z - }
% j, N @& m C! g1 g8 M
5 W2 n- i9 W& w+ m5 l- int16 width = g_system->getOverlayWidth(),+ Z& w: d d0 ?* W" ?
- height = g_system->getOverlayHeight();) `: [- \5 N0 E& z9 }
& B9 n4 y0 o5 ~7 x- if (width != _lastOverlayWidth ||8 Y0 W# H4 _! F) y* Q
- height != _lastOverlayHeight) {9 O. h2 h- u0 v( c+ b$ G
- _lastOverlayWidth = width;$ u! T3 `3 |1 m/ S
- _lastOverlayHeight = height;" |, v% z, k: c6 V0 p9 T& P
- , e; K2 I5 T; j/ X% l! d
- // Recalculate the real bounding box to use
" P; v+ ]8 {9 D) t0 {# V- R/ ^' w - _realBBox = _requestedBBox;: Z! K5 ?; W6 i3 j& O; @
/ N+ m0 C& ~) q4 e1 g1 D7 f" | g, O- if (_realBBox.bottom > height) {
2 T# n# I6 C. I1 H; m) i - // First try to move the bounding box
( ]& W5 L" q# g" o3 J* O* G - _realBBox.top -= _realBBox.bottom - height;
/ Y8 h1 C% s; g6 p - _realBBox.bottom = height;
5 G$ o6 [4 e0 a' [ - }
7 U9 g, C8 o. R- G* i& t+ F* }3 G3 G - if (_realBBox.top < 0) {: z; ]) f- Z7 _
- // Not enough space6 }' V7 P g7 _
- _realBBox.top = 0;
8 d1 o0 b3 v. Q+ i - }
& @) ^' ~/ A" e
( ~1 r% x& b( ^6 T+ m8 K- if (_realBBox.right > width) {# \# h7 q, d5 n0 }% p T0 t9 O
- // First try to move the bounding box
e! h$ e4 k; p4 C: G3 O - _realBBox.left -= _realBBox.right - width;. q2 W4 V0 J* S$ g) x$ ]) L
- _realBBox.right = width;7 Z" w8 l5 B. u. H
- }
6 ~5 ~6 e5 } R( n7 g1 ?, Y - if (_realBBox.left < 0) {3 Z) ^- i9 H1 l; m4 T
- // Not enough space( Z# b& R) j+ _% ~/ |8 t0 h
- _realBBox.left = 0;
, J7 w% x, `) ? - }
Y j# [- d& `2 K# a: N8 n - ' T7 a* a5 O# `+ {3 f$ [
- force = true;8 C9 c& Q, s4 \: I
- }
% u8 F, X8 @+ b- w0 x% n5 J - 8 ?# u1 ^2 M$ r% r4 v( _
- if (!force && _overlayHasAlpha && subtitle == _subtitle)9 ~( |) n6 V; G! q- ^3 C
- return false;* w, h$ I. z% b7 i$ M& X
- # s8 Y, e- l$ }& e' L9 x- I; q7 `
- if (force || subtitle != _subtitle) {/ h8 K1 q6 `% p5 o
- debug(1, "%d: %s", timestamp, subtitle.c_str());/ O W$ n+ i5 C
- 8 `9 x5 p1 X' H- c# O# E! G
- _subtitle = subtitle;% C7 t+ _: n9 [) A- @& p
- renderSubtitle();; E# q* j* N( }
- }
' i& e; o7 L+ @% Y4 C y
% M8 r$ l: Z$ L/ o- if (_overlayHasAlpha) {9 m/ S# J; i% D; \3 ?8 c
- // When we have alpha, draw the whole surface without thinking it more
* G7 n; |, [7 D; E8 A) M - g_system->copyRectToOverlay(_surface->getPixels(), _surface->pitch, _realBBox.left, _realBBox.top, _realBBox.width(), _realBBox.height());! R7 O5 v, u9 M9 D) q
- } else {
( W2 ?8 p7 i( U$ }; I$ B: n) o/ d - // When overlay doesn't have alpha, showing it hides the underlying game screen' y" F' D% L2 A b# R
- // We force a copy of the game screen to the overlay by clearing it
5 A! h8 o; H$ J' I% [ - // We then draw the smallest possible surface to minimize black rectangle behind text
) a. Y7 w4 c( ? - g_system->clearOverlay();
$ x" A0 X- C. |) C# h# [ - g_system->copyRectToOverlay((byte *)_surface->getPixels() + _drawRect.top * _surface->pitch + _drawRect.left * _surface->format.bytesPerPixel, _surface->pitch,1 d, a' a$ m/ g0 x# r, v& ~
- _realBBox.left + _drawRect.left, _realBBox.top + _drawRect.top, _drawRect.width(), _drawRect.height());
( U7 N G+ F3 |# J _ - }- M! V. O4 @" a. P4 C+ j& O
9 j2 C5 ^% }0 i+ h) I2 k- return true;9 g x( B1 G3 d* ^' u$ U* o7 ^. D; g4 \
- }+ a/ o+ E# {; n" y& w
复制代码 0 @: q5 z, w" M0 s
2 E; u* w0 J1 X" k3 h7 @' W @7 r$ ?8 K
- i0 D% `! J7 {* n* @, z: t" n+ W) E5 E
& X& C1 a( z# z, V
1 }6 m% X2 Z6 e" r |