本帖最后由 shane007 于 2023-8-12 13:57 编辑 * _* f7 e4 u( Q/ b1 ^( H
% z: d! u# ?4 t v# K u, ~这个游戏原本是没有字幕的,但是在Scummvm论坛上发现了一个帖子,
/ `+ J1 g6 f: V1 K! ~5 ~ F讨论的内容是关于在原本没有字幕的游戏上加上字幕。(最初的缘由,是为了帮助一位失聪的朋友)
. @8 h" j7 W# X6 W! ~7 _https://forums.scummvm.org/viewtopic.php?t=7168
# K4 y) Q# q' q/ r- t! k7 o[Scummvm-devel] Displaying subtitles in voice-only games (narkive.com). c J2 E% h4 _- d1 o/ ?
% y* g! _: l! L8 m# u4 n前段时间第七访客出了25周年纪念版(The 7th Guest 25th Anniversary Edition。
; K" i- o3 u- \* @8 g9 `在这个版本中,游戏的视频是有字幕的,因此把这个视频的字幕用到Scummvm中就成为了可能。
; q+ V3 Z) ?- R
& @1 w( }9 S* x% u o! j: S/ S我查看了一下最新版本的Scummvm的代码,发现老外已经加入了一部分代码。8 J' w) c3 w/ x, @& m
在此基础上,汉化是大有希望的。此种汉化方法,也是前所未有的,如果真的实现了将会有重要意义。: ]6 T! Q% _, d
/ P. t8 l3 u6 q2 ~* h; x1 z
类似游戏还有- · Groovie
- P* s) b: G6 ~ - - The 7th Guest
' I5 o* _: e5 \$ f/ W; r - - The 11th Hour% _+ f. t* F6 v3 f3 j" U# N
1 k/ g( {1 o( c7 P& l l- · MADE7 s+ u" g7 q) ^: q: L
- - Return to Zork (descriptions are text only, while spoken dialogs lack# M: d6 q1 k1 m, U3 ~ ~8 A
- any text)* |, z1 X- X8 l' K2 K
" g* }0 `6 b: X7 y, t- · SCI 1& b) w3 ~; v$ _( F+ `; _1 V. K
- - King's Quest V (CD Version). In this case, just enabling voice and text, m8 p3 r Y5 l, V9 D+ A% e
- simultanously would allow a 3rd party to create a set of MSG files
! I! T a* R' }* i; Y - containing the voice transcription/translation, as it happened with Gabriel
: S# g) Q/ v% j4 S - Knight II.
# T" _' \( w! ~" x5 |+ i2 l y$ G; A' H - 1 W# c8 Q1 {0 _
) b( T. F( \. z- Future engines:
( v) ?) z) w% p
- B V+ o% S; C7 J' o- · SCI 21; ^7 u& ^3 u! W5 N6 O5 `4 ]6 x1 x) S
- - Gabriel Knight II: The Beast Within (Already a fan patch available in# A5 \* u6 I P: m n/ ]
- order to enable the text display feature)
* Y# t" ^ D, a4 V7 M - - Phantasmagoria6 ~6 v: X- a# I# ?, [3 T& _
- - Rama
: y9 y+ X- Z: W- ~( |1 j. u0 c6 D - - Shivers+ `5 x; a4 }; }2 m, ^
- - Phantasmagoria II: A Puzzle Of Flesh# {# ^5 T0 L8 h+ n: D9 o
- [! w+ c9 ? K
- · SCI 3
^9 b. } a# z- I - - Lighthouse: The Dark Being1 S: B7 h- i+ W2 I9 y, ~
- ) _* J. p2 r$ ^; N+ l- Q* C- T: l# ~
- · M4/MADS% x7 M; M* j! k1 ]( E$ P
- - Orion Burger. T. W, d9 ]+ o/ V- N9 v
- $ F. k3 u, b; a% N4 J: F4 k
- · Mohawk
8 [% e' _ O( h - - Myst1 N) d. z$ ?6 r% Z2 t6 A* j8 k
- - Riven: The Sequel to Myst
复制代码
& G2 W+ W' C$ R; m, u3 T+ ^4 [0 I9 |" P
5 D2 T9 S P' _/ h: M1 S
6 D' v0 y2 @3 h相关代码如下9 R1 `" P( F8 }, \" `0 F7 w, h' H
- bool VideoPlayer::playFrame() {& B7 L9 ^; o r/ u; \
- bool end = true;
% i" I8 \* I* ^) |; u7 H" A y
' H1 {1 j0 ?6 l* ^+ S- // Process the next frame while the file is open
0 h* F% J n& e9 F+ i! s - if (_file) {
5 t& I0 ?( Z1 } - end = playFrameInternal();
) D7 k: M3 x1 G* E# b
- \ N- q" }9 e) k& D- _subtitles.drawSubtitle(_lastFrameTime - _startTime);
0 _" l# e& i& F! A7 d6 l - }
复制代码- bool Subtitles::drawSubtitle(uint32 timestamp, bool force) {4 g7 ?! S% O2 A6 p- c: @$ p* k
- Common::String subtitle;# i. Y# {( ]/ ^9 X' _5 I3 x
- if (_loaded) {( } F; P+ ~) }* K$ F
- subtitle = _srtParser.getSubtitle(timestamp);) Y9 o K8 T6 ]1 g" b
- } else if (_subtitleDev) {
" j' p6 b$ F3 H; F/ _& ~. e4 } - subtitle = _fname;
( X7 |7 b% O, X0 j! H! O2 g' C - uint32 hours, mins, secs, msecs;
+ b6 I( |, w) d. F4 r% c k0 ^ - secs = timestamp / 1000;
# V& a1 q7 X7 { b) ^: F) n - hours = secs / 3600;
! E! L3 T, I/ f/ Q - mins = (secs / 60) % 60;
" w! R6 z# _2 N9 `. U - secs %= 60;( j; l1 v0 {$ k" K1 e6 m# M
- msecs = timestamp % 1000;
( a% [* k% R* p' w - subtitle += " " + Common::String::format("%02u:%02u:%02u,%03u", hours, mins, secs, msecs);
Q; m: I' P3 n% k. a - } else {% n% y5 l3 I" q
- return false;( Z! I- F2 Q7 P/ ~8 o; N) \
- }
2 o8 [: A9 W% p N& h" x - 9 h- v) d! s0 S
- int16 width = g_system->getOverlayWidth(),' }" ?$ O" n6 H( T
- height = g_system->getOverlayHeight();
g4 [+ `/ H( g. B: ?5 i - * w! n: u& S. x) e
- if (width != _lastOverlayWidth ||' ?$ F' B" B. |+ A3 P8 l9 Z/ [7 z
- height != _lastOverlayHeight) {3 [" U) k" O K9 N9 l( ~$ U
- _lastOverlayWidth = width;, k. l& e# t! H$ L
- _lastOverlayHeight = height;
, [: W- u2 @3 p! u - 4 w. E0 \3 P4 H' S2 P
- // Recalculate the real bounding box to use
! D6 X1 @4 h# K - _realBBox = _requestedBBox;
1 C- x0 w( _3 y- l/ [. s - 5 A w( L& I9 d( l% H2 _/ b/ |
- if (_realBBox.bottom > height) {
. L; k4 B( H; s - // First try to move the bounding box* b% o2 Q9 H( k" k
- _realBBox.top -= _realBBox.bottom - height;4 G; S4 Z% Q$ A9 \$ C
- _realBBox.bottom = height;7 P! ]" L6 G& F. t
- }
2 ?/ v9 T+ w1 @; e - if (_realBBox.top < 0) {' }+ e& I5 M f0 Q5 n6 {: h- N: G
- // Not enough space* z! q$ S5 m9 Y) d0 w( O
- _realBBox.top = 0;+ x: ]3 k/ a; p/ |6 {1 |+ `
- }
a3 x9 ^- X" ~6 Q* j) p - 8 ^$ F" {- T& Y% W% u! ?0 ?! ]: Q
- if (_realBBox.right > width) {
W( D; G2 T7 i$ o - // First try to move the bounding box3 J, Y& p# z' o# Z, U1 C H0 N
- _realBBox.left -= _realBBox.right - width;+ X5 ^6 E7 _2 n9 @7 k& j
- _realBBox.right = width;
) x! D, Y* S* O4 G - }
) i8 y2 p) P) p" u: f - if (_realBBox.left < 0) {/ C3 I8 z) i' ?
- // Not enough space9 ~' B E) g2 Q4 ?
- _realBBox.left = 0;
9 o! a8 K# N$ {5 g4 T - }
5 M- z3 d, ?0 N6 A0 j7 }1 f6 ~* Y: _ - / {& ?* P5 c# S9 f- r- i7 G
- force = true;
- n7 Q8 Y& B, f: F* ?" E( m: U - }& e$ ~/ c% z8 b) l
$ |4 K8 R0 P- k3 g: ?; J- if (!force && _overlayHasAlpha && subtitle == _subtitle); p: C8 |0 ^5 S8 ^
- return false;7 N7 e4 Z9 B d2 d+ [+ S- l% J
! m2 e1 y( |; a) |- if (force || subtitle != _subtitle) {! o6 z/ S+ O" e L3 A4 q5 X1 h0 w
- debug(1, "%d: %s", timestamp, subtitle.c_str());
3 k$ B/ A/ x8 G1 y - / j+ Q' t! q1 o; `
- _subtitle = subtitle;
2 ?4 U- m3 _6 v9 x - renderSubtitle();, m4 w8 B5 x* W7 I
- }
2 D, H! ]+ j; ^' T( ?$ S- o - & j2 F# ~( Z. J# P
- if (_overlayHasAlpha) {. S# S/ J! X( v; Z% B- `5 r
- // When we have alpha, draw the whole surface without thinking it more8 {2 @& |6 \+ z" B0 l+ V, J
- g_system->copyRectToOverlay(_surface->getPixels(), _surface->pitch, _realBBox.left, _realBBox.top, _realBBox.width(), _realBBox.height());( u+ c, p! @% n; j& e- o) E' n
- } else {3 ]/ r: N! H" x8 X. Q9 G
- // When overlay doesn't have alpha, showing it hides the underlying game screen
! U I' @ E" ^' l8 ]) U1 m/ G$ u - // We force a copy of the game screen to the overlay by clearing it
3 L: i9 }! O4 t- }8 t" w* |$ Z - // We then draw the smallest possible surface to minimize black rectangle behind text
* a1 K- o! s. w$ w- s7 u - g_system->clearOverlay();
6 W' t7 v3 @0 A& M+ K$ G! O4 Q1 ^ - g_system->copyRectToOverlay((byte *)_surface->getPixels() + _drawRect.top * _surface->pitch + _drawRect.left * _surface->format.bytesPerPixel, _surface->pitch,5 B2 `1 o. y$ x( B) `& Q" p
- _realBBox.left + _drawRect.left, _realBBox.top + _drawRect.top, _drawRect.width(), _drawRect.height());" T0 k w- U+ v: K: F9 z
- }
/ g4 i+ ^; H& F; V: a) e# u; c
) e( Q% Z+ ~: g$ o# K; |" z1 }# d- return true;2 j; S; q& ~1 P' {7 p& Z3 a
- }& N8 s3 {0 d2 _0 u4 e
复制代码 / F% N |* X# F9 i
4 L0 T4 E4 j% P/ r3 \! J r& O2 H; @ ^/ I' S$ t
$ T# [7 W" ~2 L6 i, N
1 t- W! S8 Q ~ l" U! @! \! C
4 z" q$ d8 Q6 b: N8 T( J$ x
! |* |& R9 P$ _* S$ g: I |