本帖最后由 shane007 于 2023-8-12 13:57 编辑 : D* j4 s* V5 ]; f1 m) d2 L
1 r4 q! c" S2 J; N9 D3 L2 f
这个游戏原本是没有字幕的,但是在Scummvm论坛上发现了一个帖子,
, }4 g4 Z- J$ A7 t讨论的内容是关于在原本没有字幕的游戏上加上字幕。(最初的缘由,是为了帮助一位失聪的朋友)
! u$ Z( ^4 `) p2 Q% p/ W) L& Uhttps://forums.scummvm.org/viewtopic.php?t=7168
( j0 z+ H1 ]0 b; g, ^ b) P8 W[Scummvm-devel] Displaying subtitles in voice-only games (narkive.com)) h u9 A' R3 z; a' H3 ~7 [
( x1 K$ P1 d+ W. M: B; i |5 ~( i
前段时间第七访客出了25周年纪念版(The 7th Guest 25th Anniversary Edition。
4 p8 X* t) j" x! D在这个版本中,游戏的视频是有字幕的,因此把这个视频的字幕用到Scummvm中就成为了可能。
) @$ `7 U) O! u( g
$ x! X/ e7 O3 N5 F9 C: x: d0 e% U我查看了一下最新版本的Scummvm的代码,发现老外已经加入了一部分代码。
6 Q$ N; I. h. O+ j在此基础上,汉化是大有希望的。此种汉化方法,也是前所未有的,如果真的实现了将会有重要意义。0 O1 z8 L% f: B& A; D3 _$ n
. c( Q. {6 J1 f类似游戏还有- · Groovie* o- ^# B% I0 t( t& ~7 Y/ u" ^
- - The 7th Guest
/ N5 [% G; J; |- E% A7 \ - - The 11th Hour; X# Q M( t( B1 Q! g6 V& y+ K( d
- 0 i5 {* c. G* Y) i% Y" r5 b$ P. a
- · MADE N- i4 n& S9 x$ L
- - Return to Zork (descriptions are text only, while spoken dialogs lack
0 |% M/ v* ]8 o! S% f" u - any text)2 }' a7 @4 a0 K, S% {. E
. |* m/ w3 I' _/ J8 H7 ^/ {1 n9 ?1 b- · SCI 1
+ Z+ G0 a' b" I8 k2 I9 ? - - King's Quest V (CD Version). In this case, just enabling voice and text
6 R6 W8 O3 ]! k - simultanously would allow a 3rd party to create a set of MSG files2 A& K; @7 X8 Q2 c: _/ t7 A& ^4 N
- containing the voice transcription/translation, as it happened with Gabriel
* T9 {' _# y; \% y( _ - Knight II.
' k) |/ L; v8 D9 T/ H3 v - " R* ~) ]: J) `1 ~4 r, M$ u
- 0 K7 q7 w, L9 K. U6 `0 q( T
- Future engines:
( ~8 C5 C7 h( G! F8 O
2 @* a, U8 i7 [3 j+ m- · SCI 21
5 r8 s% y6 N- I6 R - - Gabriel Knight II: The Beast Within (Already a fan patch available in
7 H; `2 y! R9 s( g. z- A - order to enable the text display feature)
$ B0 t/ ~6 i4 L2 R: O* p, {5 S - - Phantasmagoria
. F) M" A( W+ c - - Rama
, S# I; Y0 v% g1 n - - Shivers+ b6 A9 R6 e8 \: c K( w: {
- - Phantasmagoria II: A Puzzle Of Flesh
- y* w/ Q0 q- q( B8 R( p4 F
' E" T4 `; z9 ~3 y& f- · SCI 3
o& F8 ^8 W, b# w7 Y - - Lighthouse: The Dark Being
3 K( _: E" x6 K6 m6 g2 l
0 a+ x& c3 J) w. e( w- · M4/MADS
# w& m' v: }# G( [, F5 _# ] g2 R - - Orion Burger E* f1 T! K1 |2 L% z4 v
/ Y: Q; Z! D4 k$ _8 T1 ]- · Mohawk! O& [+ Y9 H1 c5 e
- - Myst
0 U- A2 g" P# A" e3 v - - Riven: The Sequel to Myst
复制代码 . e1 s+ [1 i: u2 |9 G; e
$ v5 a6 }& W0 F4 `$ e
; U7 |+ B* {4 `) Y; Q% }
; k% m; H7 O. @. Y+ R. w: V, i相关代码如下
% @9 r }9 v1 c- bool VideoPlayer::playFrame() {" V! b s) v" ~5 J; _4 u. X& N
- bool end = true;0 x9 ~ a! Y3 }& a' L+ R
- " |* `1 \5 P: R1 }# N' i, a
- // Process the next frame while the file is open; U3 J, M. t. r* Z" M1 t- v5 c# L
- if (_file) {
9 c5 W9 G# `+ n! a5 T - end = playFrameInternal();. j* }- L N$ W9 ~5 k! }
9 m& p5 z8 h. I# W% S: |- _subtitles.drawSubtitle(_lastFrameTime - _startTime);$ u8 H4 H1 M9 V6 w
- }
复制代码- bool Subtitles::drawSubtitle(uint32 timestamp, bool force) {
; x+ g( `* C* t( F - Common::String subtitle;
8 \$ U* q& e9 q5 N1 ^ - if (_loaded) {7 C0 e* r$ y, E* W. \
- subtitle = _srtParser.getSubtitle(timestamp);% B" t8 G, _8 Z8 x7 ~1 F6 o" B
- } else if (_subtitleDev) {& c! {5 I; a- J9 s. n
- subtitle = _fname;
% q' t: M- [. b7 q! o' Q0 |5 w - uint32 hours, mins, secs, msecs;
. @" ~( M: s; G2 A' G8 E - secs = timestamp / 1000;% Q) e% w$ w" f1 u9 }9 P9 c! v0 ?
- hours = secs / 3600;
6 { s& T; D5 { - mins = (secs / 60) % 60;. k5 E4 W4 D7 [! g$ g. w
- secs %= 60;
, O; j. l. o8 | - msecs = timestamp % 1000;
# e7 q' g$ u1 `# L- z/ C - subtitle += " " + Common::String::format("%02u:%02u:%02u,%03u", hours, mins, secs, msecs);
3 _% c1 t! R" K( q5 D9 p - } else {
$ }4 y; W$ g j& ] - return false;
4 E& ^+ a3 F) \' O" t - }% k0 Y9 Y! f7 O
- 0 O/ G2 c( m7 S5 ]; ~% f' W
- int16 width = g_system->getOverlayWidth(),
9 `0 Y7 g( J/ y% x$ D - height = g_system->getOverlayHeight();
7 r. m; r. e+ T - 9 \; W. K) ~6 K" F4 ?8 v
- if (width != _lastOverlayWidth ||/ ]2 s9 T5 E6 r. ^$ M9 B2 \
- height != _lastOverlayHeight) {2 y2 c4 v' ~$ V
- _lastOverlayWidth = width;
7 E& E- H- }5 R - _lastOverlayHeight = height;
% ^, O& p' z9 E8 r& g$ E - ; |% u/ U- _* ^! N
- // Recalculate the real bounding box to use2 R4 j* H4 c; Z' G6 B' R& v
- _realBBox = _requestedBBox; P/ `" d: A3 |1 Y
3 k. y+ \" I/ ?* [. V8 c: o5 _* T- if (_realBBox.bottom > height) {
1 f0 q% o N1 A6 z* \ - // First try to move the bounding box! a4 k! E$ j4 i' D7 X) [% h
- _realBBox.top -= _realBBox.bottom - height;
' l& ? H" @- K4 v% e, k - _realBBox.bottom = height;
1 @# i1 E& O$ P8 g" `! Y - }% w( m, X) N- u8 `* F& E
- if (_realBBox.top < 0) {
. |( m3 D. |& O2 k - // Not enough space
) E( _+ e% O0 G$ }. B. j6 v2 } - _realBBox.top = 0;
5 Y4 W$ @/ d5 G2 M } - }
* L1 K! B X. c0 }0 H8 F
" g4 V! r+ @2 K( m& F- if (_realBBox.right > width) {
7 A% O1 L2 l8 r3 v( M7 Y# T- H. R) w - // First try to move the bounding box
8 V" S: S! n: v% `3 J7 m. G% t! e - _realBBox.left -= _realBBox.right - width;
8 B5 j% W6 A/ x" h$ O% B - _realBBox.right = width;
3 j% z$ n' c! i - }# P" W6 K2 I% i
- if (_realBBox.left < 0) {
1 a: F' y% p P$ L$ O% [2 r8 ?( i8 V - // Not enough space& W& W7 O% X: j+ S7 O+ p0 }4 T
- _realBBox.left = 0;3 A0 z8 y* W6 K) V, U1 r; W
- }
, p! W; G4 g6 R F2 D: j. s2 u - 1 G( N0 X- Y7 s1 Q6 k
- force = true;3 j/ s7 |% c: C1 D3 x* y
- }
9 O' x# B8 ]7 X9 z9 p g$ I3 S
+ M; S) I% M' U- @" |" P# D& s- if (!force && _overlayHasAlpha && subtitle == _subtitle)0 u3 a9 C5 J9 k
- return false;" w% P( y8 w$ w+ J) D
+ O J" z' M9 J/ ?) v( }5 A- if (force || subtitle != _subtitle) {8 T1 k( l' D" } z! a+ n
- debug(1, "%d: %s", timestamp, subtitle.c_str());! G: X& }9 k" Z3 j6 a
. Q% h9 \* r( a+ k3 M+ ~- _subtitle = subtitle;
9 R2 t# Y: H+ z g& ~ - renderSubtitle();
; }' ]9 v4 V% ~ - }
9 n, G3 g" f0 t( }
& g; a1 T5 ~; Q7 X- if (_overlayHasAlpha) {
& c' ]6 T: U4 u - // When we have alpha, draw the whole surface without thinking it more% O. y9 Q" Y$ R
- g_system->copyRectToOverlay(_surface->getPixels(), _surface->pitch, _realBBox.left, _realBBox.top, _realBBox.width(), _realBBox.height());7 u1 c: b7 H2 |; d
- } else {
7 `2 }" F& _& u2 q2 F: p - // When overlay doesn't have alpha, showing it hides the underlying game screen K5 g+ o; G: M+ J2 a# k
- // We force a copy of the game screen to the overlay by clearing it- U- d8 a( f ~$ Y @& w3 K
- // We then draw the smallest possible surface to minimize black rectangle behind text: ?* `. W3 i- U P
- g_system->clearOverlay();
/ T, `7 f* f% @4 k$ L% Y - g_system->copyRectToOverlay((byte *)_surface->getPixels() + _drawRect.top * _surface->pitch + _drawRect.left * _surface->format.bytesPerPixel, _surface->pitch,/ `7 @7 D' y' {. H2 U
- _realBBox.left + _drawRect.left, _realBBox.top + _drawRect.top, _drawRect.width(), _drawRect.height());" d' Q; G+ ^3 W1 K: H
- }7 K! {' f/ E! e2 @3 ^4 k
+ P$ G8 q# O) ]0 G* V! ?6 @- return true;
c: g8 j/ N. g% C - }
g2 ^6 \ L( t4 \! i( Y4 s
复制代码 ( V) v! `. c+ S ^
5 J) B/ H0 |, A& y% p6 {
6 T( b/ R* Y1 E# G: q" `
3 z4 W% ~" R; E3 i, n$ x
4 z! ]7 y5 U5 ~7 L: D
6 ^3 G( ?7 S8 U( D% ]' x; d0 a& g; M# E3 r2 G; g$ a
|