本帖最后由 shane007 于 2023-8-12 13:57 编辑 ) D* k* n+ B9 ?% A+ D
/ b. @/ ]' Z/ x" X. q7 W; R这个游戏原本是没有字幕的,但是在Scummvm论坛上发现了一个帖子,
, W) u* B& D; o4 t t讨论的内容是关于在原本没有字幕的游戏上加上字幕。(最初的缘由,是为了帮助一位失聪的朋友)
) r/ f) n" ^; q) y" Fhttps://forums.scummvm.org/viewtopic.php?t=7168
$ I1 N/ H- y1 D* i: T7 {. Y[Scummvm-devel] Displaying subtitles in voice-only games (narkive.com)
4 W$ n' y9 W) m
- @8 i- a* L3 Z( E# S: x* O. @1 E! L前段时间第七访客出了25周年纪念版(The 7th Guest 25th Anniversary Edition。$ Y @! ]4 {& ]0 R
在这个版本中,游戏的视频是有字幕的,因此把这个视频的字幕用到Scummvm中就成为了可能。! y# x# m2 p' h6 R x6 }
7 F! M$ p& y+ T- @3 l
我查看了一下最新版本的Scummvm的代码,发现老外已经加入了一部分代码。
( d; P1 x4 a& x7 [+ }在此基础上,汉化是大有希望的。此种汉化方法,也是前所未有的,如果真的实现了将会有重要意义。
/ g3 a# S4 W5 |+ L' r. X r: O; q: K' y C
类似游戏还有- · Groovie' \6 ?/ g' f4 w1 ~) ?. I# L
- - The 7th Guest& D! P$ [) E7 B5 C. b3 R
- - The 11th Hour
" \( u) C% |& x" w5 F) T/ f
% g1 q" ^- j8 b ^/ b& N% J- · MADE& ^9 F$ H6 z& Q9 v: {1 S" Y
- - Return to Zork (descriptions are text only, while spoken dialogs lack, I2 b M0 i& j
- any text)
0 z5 U$ k# n0 a2 T
3 n& C& p: C8 U u, _* W2 R- · SCI 1" Y* {. F5 S9 I2 G9 h8 k* |4 `9 V
- - King's Quest V (CD Version). In this case, just enabling voice and text
# V) J3 ~% P% ]3 `' D - simultanously would allow a 3rd party to create a set of MSG files* j) v) O3 |" q
- containing the voice transcription/translation, as it happened with Gabriel6 L5 s) p; q' m/ F
- Knight II.' _, ?0 V; h- u( ?0 ]5 E2 P9 o
- " ^! N5 K% `2 `
- 4 v0 Y- w# [- s( `
- Future engines:. B/ S9 D% p( C. m0 s" q% l
6 w! S4 w+ v( N, d L" F- · SCI 21
$ z8 }# l1 `9 E* `7 n+ E - - Gabriel Knight II: The Beast Within (Already a fan patch available in7 m& g9 @/ B2 h# w1 |( C
- order to enable the text display feature)$ a" ?- E2 M: Z' Q
- - Phantasmagoria+ _4 V6 z7 n( k% X# A0 P8 w+ C
- - Rama
) S, S! E+ f% T* n: G8 e+ {5 Q/ _ - - Shivers( l% t9 k5 E* R% S3 Q3 @0 X+ C
- - Phantasmagoria II: A Puzzle Of Flesh7 O! J0 t5 n( X- ~# H
# j/ t7 U- {$ R0 j1 Q- · SCI 3
9 e2 E" w% \6 a) g: R# X - - Lighthouse: The Dark Being
y& }) [ n- \: ]& g7 S - + a) t8 D. T/ n, z
- · M4/MADS3 ^! V4 w2 A. _ m& m" N( G/ C
- - Orion Burger
# F4 h! H2 l3 M
1 z; c3 H# h' L6 k i: Z- · Mohawk
& @; v3 i, I* e* W; d, ^8 F - - Myst$ B" n. ^. o" Y3 a4 m" A8 F6 J
- - Riven: The Sequel to Myst
复制代码 3 V1 r K; F* ?* x' s3 s
) }) C$ f+ o6 A" e% G
9 M! @) R8 l) p
- |9 G* u* f- M7 ~$ Y4 o6 m1 d7 H相关代码如下9 g, l8 [6 W5 {% b
- bool VideoPlayer::playFrame() {/ w& q5 \, e2 P# n0 q$ ~
- bool end = true;
1 R8 y! B- N9 {! o
; F/ n( W& Y0 g" B0 P- // Process the next frame while the file is open+ P' ?9 A- ]9 R* y6 O2 b! g9 l
- if (_file) {
* u+ a/ N" \" _ - end = playFrameInternal();
( T$ U) y; r4 d! b - ) Y* @& x# S9 C/ \7 M6 U2 g3 I5 e
- _subtitles.drawSubtitle(_lastFrameTime - _startTime);
8 i# t+ D1 X/ @2 y" z0 R - }
复制代码- bool Subtitles::drawSubtitle(uint32 timestamp, bool force) {
3 h) b2 s( x+ j0 A% q) ~ - Common::String subtitle;
6 p$ \" i- b3 c% t% K3 a ] - if (_loaded) {) _! f* D t ^4 U& ~1 D
- subtitle = _srtParser.getSubtitle(timestamp);& A% `' x+ Z2 u/ h4 ^) ?: T( D
- } else if (_subtitleDev) {
" V8 [- @# { ~$ @, h. J" O - subtitle = _fname;! V" x7 \8 Q6 K
- uint32 hours, mins, secs, msecs;
# r9 k" Q3 {$ p2 w/ O - secs = timestamp / 1000;
; p' U/ O0 r" @$ ] e5 k - hours = secs / 3600;' f& B2 O, r3 h# p4 a
- mins = (secs / 60) % 60;
& f/ @$ w, a# P- V - secs %= 60; d! s# O/ Q$ U' X' M: _) [+ B# T
- msecs = timestamp % 1000;
$ F* q* I; L- W: d/ Y% u - subtitle += " " + Common::String::format("%02u:%02u:%02u,%03u", hours, mins, secs, msecs);
8 _# ^. ?2 O- y - } else {
; M5 `' g, u2 `* B - return false;
) T7 s, n3 f3 ]% D" K - }
& s9 I8 G' d: U% \
2 G2 b" `8 F. V- g2 |8 B- L" U- int16 width = g_system->getOverlayWidth(),) I( G3 S3 X j9 X. F$ ^; H
- height = g_system->getOverlayHeight();
9 E" @& C! n9 Y* w% d8 }- h
3 W* X6 b& `7 K ?3 ^. V- if (width != _lastOverlayWidth ||! b# E# ]1 v. r9 Q/ o+ z! T" ^
- height != _lastOverlayHeight) {
- g; ^! d1 I, u( G8 w9 W' V8 n" j- i - _lastOverlayWidth = width;3 d* w1 r2 H$ x1 @
- _lastOverlayHeight = height;
/ q3 |+ a9 B/ L- O0 d. r - 4 }* D9 I5 b8 z* L* I1 m* X$ l. X" X
- // Recalculate the real bounding box to use9 q3 @8 m9 u3 w) T
- _realBBox = _requestedBBox;* a" E& X6 P) H% Y, y( P! ^2 ?
- 7 k: e' k" @, K2 H# o
- if (_realBBox.bottom > height) {
) p& Q, b) r% Z - // First try to move the bounding box
1 e; t: @+ l" B. K - _realBBox.top -= _realBBox.bottom - height;
) x3 a+ {% b7 z6 Q* { - _realBBox.bottom = height;7 n. {% F& d. w1 ]
- }
- d+ |3 I: I6 M+ A$ {2 u - if (_realBBox.top < 0) { t1 r: W6 G- i( ?. x; B3 X
- // Not enough space
, n- z0 Q5 ?: p' G; r3 J - _realBBox.top = 0;( X5 Q1 `: B6 I5 c
- }5 R7 ~( ~8 J8 f0 O C
8 X% q+ E9 o+ X0 l- if (_realBBox.right > width) {* D$ m/ C3 c' Y% K) D$ Q9 e
- // First try to move the bounding box
' k. U2 O. L) G# m5 V1 E - _realBBox.left -= _realBBox.right - width;1 v ?4 _' o% l; X/ M
- _realBBox.right = width;5 p- h9 G2 d7 r$ L( ^. v
- }+ a+ \ R, I4 @( r
- if (_realBBox.left < 0) {$ E5 w, N- r/ J& d. p0 G
- // Not enough space
2 l+ [) ?0 L6 s4 Q - _realBBox.left = 0;
* ]0 t6 V3 L- }/ A( T - }2 T3 X$ t2 m5 {+ a& x
- 9 d, y! S$ L5 ^8 Q. B5 Y8 \
- force = true;
G# P( s! n$ G7 F$ [ - }2 m+ j* v( h8 _' e( h% c/ p+ I
9 E0 U; }) Z1 Z7 `- if (!force && _overlayHasAlpha && subtitle == _subtitle)6 u* \" j. _5 x T
- return false;
7 |! ]7 R7 S6 L- t
4 d4 \# _4 R( h3 \8 A- if (force || subtitle != _subtitle) {
/ V3 p" R+ `, W$ s1 t4 ?! u6 Y, X* L - debug(1, "%d: %s", timestamp, subtitle.c_str());! ^6 I$ j; w5 a- Q+ s3 ^
; g3 R. u0 R+ e: O- _subtitle = subtitle;: u" @2 {/ S( K4 y( I( i
- renderSubtitle();
7 O, \! F8 [8 q0 W' i5 y! C - }# j7 ]/ I; J/ Z5 Q5 R
- : ?! u7 P7 G/ L3 i% \4 j
- if (_overlayHasAlpha) {
" o/ I+ {1 w6 i - // When we have alpha, draw the whole surface without thinking it more
, X$ @+ O. \$ L; \" u1 f" C - g_system->copyRectToOverlay(_surface->getPixels(), _surface->pitch, _realBBox.left, _realBBox.top, _realBBox.width(), _realBBox.height());8 V7 T! I5 o% c: U
- } else {4 N v# z" H2 q1 T) O R$ K. b
- // When overlay doesn't have alpha, showing it hides the underlying game screen
- I/ E/ t; S: o2 y$ X! p - // We force a copy of the game screen to the overlay by clearing it8 |5 u' b! k5 n2 v' ]5 Y5 l
- // We then draw the smallest possible surface to minimize black rectangle behind text# [7 x) \( }' c6 X
- g_system->clearOverlay();
1 S# v$ D+ Z& O% [0 o - g_system->copyRectToOverlay((byte *)_surface->getPixels() + _drawRect.top * _surface->pitch + _drawRect.left * _surface->format.bytesPerPixel, _surface->pitch,
! m' b+ K0 y* a( f% U - _realBBox.left + _drawRect.left, _realBBox.top + _drawRect.top, _drawRect.width(), _drawRect.height());9 Q4 o6 y& K3 t3 L2 i
- }
/ m$ o9 D+ W$ b, ?; q" }6 B# g
0 K+ I; q! I/ l2 K- return true;
$ s9 |4 G" u$ b/ l( j0 K& s - }+ G5 S* G/ t) W i, S
复制代码 ' i. M. }! D2 K S& D% |; T0 J
8 A% i, o, J& i) a/ T
R6 s* m' B) w/ m1 {# N& r7 w5 X$ D9 H$ r. c$ N" F
6 y$ l# m; T2 d
% W0 d: R9 }3 b% m) V& s9 O& Z# r) x' X. i
|