本帖最后由 shane007 于 2023-8-12 13:57 编辑 * a2 _8 l" L( A! Z
( i" Q* o: i3 }6 J% T+ V
这个游戏原本是没有字幕的,但是在Scummvm论坛上发现了一个帖子,
; ~* I, L5 V6 e& {讨论的内容是关于在原本没有字幕的游戏上加上字幕。(最初的缘由,是为了帮助一位失聪的朋友)/ W/ R/ X5 ^! D# J; V+ C
https://forums.scummvm.org/viewtopic.php?t=7168" Q6 K8 P$ X7 I
[Scummvm-devel] Displaying subtitles in voice-only games (narkive.com)
p) T4 C" e3 O) ?" g! Q; w, O
# J' \* a4 K/ s4 U G前段时间第七访客出了25周年纪念版(The 7th Guest 25th Anniversary Edition。
# c+ u- S2 p! }& s% V) o C在这个版本中,游戏的视频是有字幕的,因此把这个视频的字幕用到Scummvm中就成为了可能。7 K, j! {. n, m$ I8 |
" h+ e2 I3 n. n" m我查看了一下最新版本的Scummvm的代码,发现老外已经加入了一部分代码。" }6 l* W( u& K! `8 ]+ w( G2 x
在此基础上,汉化是大有希望的。此种汉化方法,也是前所未有的,如果真的实现了将会有重要意义。; Z. {: H: _" N7 m: M* p
9 i! q* i" P) K
类似游戏还有- · Groovie, p/ K7 S0 I/ _2 R
- - The 7th Guest* P6 A$ V% A* a N: u- ~
- - The 11th Hour, a# U1 C5 u9 E5 [
8 o& \; @% d4 r2 P2 i* ~- · MADE5 D& l( Y3 H% a7 x
- - Return to Zork (descriptions are text only, while spoken dialogs lack
' _$ P! J% t7 H. b. f" m% ]' M - any text)
. `& J$ j% M7 J$ Z - 5 d! D+ l7 C% f* G1 @, j
- · SCI 1
, g# w; G2 g. G/ u8 q8 D" a5 ] - - King's Quest V (CD Version). In this case, just enabling voice and text
( }9 P# x% V+ d% v- d* ] - simultanously would allow a 3rd party to create a set of MSG files
, a; X# O) \" }5 I' w - containing the voice transcription/translation, as it happened with Gabriel
* a8 D' p. o& F6 z - Knight II.7 r$ Z/ K3 ^) c/ s# O" l# u" M; k
- 4 U" A7 X: r9 Y
& X+ k) Y* S [+ j- Future engines:
# g6 I# ?3 r7 g% P
" d# O; z- P% _" C& ~- c: h- · SCI 21
$ D! o5 r/ ?' o0 l- x1 \$ q" f - - Gabriel Knight II: The Beast Within (Already a fan patch available in' @5 U7 L" j7 h, F
- order to enable the text display feature) J+ c2 V4 A& v$ I& Q' k8 K
- - Phantasmagoria% ?5 i5 N' u+ m3 [3 @* d
- - Rama) G% x" }' r; U6 W# p1 u
- - Shivers. b I+ n m& x6 D, R# }3 w
- - Phantasmagoria II: A Puzzle Of Flesh
& c* E" C% J1 O1 K$ F/ n6 T
- ?" {5 B4 B( a/ V' e' o- · SCI 3
' r, K. v+ s* q- A2 j - - Lighthouse: The Dark Being
9 r; c& e8 B2 N. G6 H
9 I8 {1 Y. C3 @8 r- · M4/MADS
, Q' s( S, N& T: n, W/ w$ b5 S' w - - Orion Burger6 l# Z& @- E2 q( t/ `) y% d
- - G7 y$ l; _4 K
- · Mohawk
, ]+ F$ @& R# n" X! U3 h0 t# ? - - Myst3 [7 H( M" B$ g2 y" F9 z
- - Riven: The Sequel to Myst
复制代码 ) S9 j1 ?2 n" h7 U9 e2 @
) N/ k/ J0 Z, ]# b( B
5 s# Z; C6 n: m# d1 ^8 d) C, G# k1 P/ H" k. r2 Z
相关代码如下% h4 T5 z v: O8 u d% @
- bool VideoPlayer::playFrame() {
8 O: I7 j' {5 r) u6 C) C. |, D f - bool end = true;/ i% I/ a8 s$ D, @
- , `8 K7 C3 G* {+ w; W7 I$ s& P3 X
- // Process the next frame while the file is open* p w' u# n3 M" e8 @. Z
- if (_file) {
; z4 t7 L0 a) O; C) |7 g - end = playFrameInternal();8 b4 d) R! w* A: U4 q& c
- - L0 w" `5 Z3 d1 S* b# q
- _subtitles.drawSubtitle(_lastFrameTime - _startTime);7 B: p0 B% _* P1 Y
- }
复制代码- bool Subtitles::drawSubtitle(uint32 timestamp, bool force) {( P5 r5 I3 c; u1 M6 g
- Common::String subtitle;; o' U: { i" J+ B( f& J G
- if (_loaded) {
( w7 S$ E# V# q" } - subtitle = _srtParser.getSubtitle(timestamp);
; A' d$ b+ i+ P; b. f - } else if (_subtitleDev) {7 Q W& R9 ~; ?" l4 }. u+ ~
- subtitle = _fname;8 q3 }+ w: T8 L1 x6 t2 D$ ]
- uint32 hours, mins, secs, msecs;
9 `. V9 v* B1 i" R. ^: P - secs = timestamp / 1000;
+ j. \3 w. k7 u$ Z - hours = secs / 3600;4 b0 R4 V, Y6 Q9 H& y% z% ]
- mins = (secs / 60) % 60;
' l: X. A9 U* L5 D - secs %= 60;
6 u4 n* ]6 h8 ]4 D - msecs = timestamp % 1000;7 ^+ u# i# L) T
- subtitle += " " + Common::String::format("%02u:%02u:%02u,%03u", hours, mins, secs, msecs);1 I1 ^( \3 J# g# t, ]$ s. f
- } else {
j0 w7 G, J8 c1 R4 f - return false;+ C: C6 z& w7 U% y; z
- }
: [+ h9 U6 G! I$ ^) D: f/ m; l
) ]8 h/ t! f; ~, T u' t7 _- int16 width = g_system->getOverlayWidth(),0 `, f0 ~( s% _/ ~
- height = g_system->getOverlayHeight();
; Y% Z# v" k/ v- Z) t6 ?
8 _/ ]2 v L- X5 `9 w! R5 S- if (width != _lastOverlayWidth ||
0 m6 U7 }# |* c4 q - height != _lastOverlayHeight) {
' V( W+ z$ t3 L! S5 v - _lastOverlayWidth = width;
. e7 i" ]6 |4 V" k9 o. _ - _lastOverlayHeight = height;
$ I; y& R& e% S8 y# E f# B" H - , V* [/ Z$ Q7 [- h. t2 b3 l. g
- // Recalculate the real bounding box to use
/ s- b5 ?* X2 D7 P+ ^ G# ^ - _realBBox = _requestedBBox;# P$ h) {6 D' X! l
" s" X+ J2 `, `( x) G; y& U+ f5 a% ~- if (_realBBox.bottom > height) {3 e- S; M$ F: _* J8 U/ l
- // First try to move the bounding box( h- U9 T! I$ \8 ^
- _realBBox.top -= _realBBox.bottom - height;
, a) z# v) z8 m! l& u- k: k - _realBBox.bottom = height;# G1 x5 _3 E4 U. a# G) h1 h
- }
9 U) V7 c$ f- s8 G: E - if (_realBBox.top < 0) {
% o$ Y a! I7 z; F2 j, ?- r+ b - // Not enough space3 c. @& T/ q) o. T1 s, ~
- _realBBox.top = 0;
. g! x* k7 s! M - }9 \7 R" A$ \# z% T& z9 K! z
- / h, i2 _/ P0 R% W1 Q
- if (_realBBox.right > width) {5 e% ~& j1 _& p' ]% R& B
- // First try to move the bounding box/ \' j' N9 {* n+ K* Z7 T1 P
- _realBBox.left -= _realBBox.right - width;6 n! J: i5 k2 o3 ~$ }
- _realBBox.right = width;! h n4 G4 B+ z
- }
) T1 Y3 e' l' X4 D( S - if (_realBBox.left < 0) {
/ S- W% Z4 g; w9 c: y) f5 | - // Not enough space
1 }/ z7 ~1 A& t - _realBBox.left = 0;- W4 ?. x8 I# G/ @& L
- }
6 v7 B. G1 _, V( {( p3 M8 @
" g; _# {3 a4 v- v- G- force = true;# S4 [' | x$ [7 q# S) t
- }& I5 ?' X3 m$ V
' `) k1 W2 p& A: c3 s- if (!force && _overlayHasAlpha && subtitle == _subtitle); g8 Z! u0 ]% b& v7 R0 B8 }4 e
- return false;
) e% M. @$ r% f& E" m
- ^1 b* Y* ^' ^3 P0 c& m- if (force || subtitle != _subtitle) {
3 R6 M0 J; ]% k2 s* u - debug(1, "%d: %s", timestamp, subtitle.c_str());
5 b% Z4 q! h( }1 K6 X0 ? - . ~- j, A& y( {. N
- _subtitle = subtitle;* a7 F3 E! B+ A$ N
- renderSubtitle();- s6 d' w7 u5 s) v+ l' j' L
- }) t9 k+ _! M1 b6 T) }
- $ M ]% F& I1 n5 i
- if (_overlayHasAlpha) {1 A3 y5 K) F4 i' W3 G
- // When we have alpha, draw the whole surface without thinking it more
4 Y) s0 D) v) r' c& f - g_system->copyRectToOverlay(_surface->getPixels(), _surface->pitch, _realBBox.left, _realBBox.top, _realBBox.width(), _realBBox.height());: C/ t% Q& A! F, g' X; E' \
- } else {
2 c8 v1 l! D/ P7 ] - // When overlay doesn't have alpha, showing it hides the underlying game screen
/ S/ V" f! \: a) V' J% V - // We force a copy of the game screen to the overlay by clearing it
5 G9 q; T7 x. p9 ?% N9 f# X$ d - // We then draw the smallest possible surface to minimize black rectangle behind text
/ @; J, r& M- p9 f& B - g_system->clearOverlay();$ A- a* y: k6 Q& d i( f' I! R+ Z
- g_system->copyRectToOverlay((byte *)_surface->getPixels() + _drawRect.top * _surface->pitch + _drawRect.left * _surface->format.bytesPerPixel, _surface->pitch,
j5 G0 l8 O l O2 l - _realBBox.left + _drawRect.left, _realBBox.top + _drawRect.top, _drawRect.width(), _drawRect.height());
9 j/ v% `% O3 T3 i - }" O3 y" f5 C. E# r+ L
- 9 F* {2 I0 ~+ q
- return true;; R( x& K; r4 u, m# j/ ?
- }6 D# w4 d7 _! m. ^3 ]; U
复制代码 2 d f1 C- `4 D2 g& `
& l9 L( i- K: b, \: ?" m5 M
1 V6 Q' e: I$ P q* o3 p& z5 x* x5 H
/ c& U2 X! d" H, `+ o% e* U
- n, D2 k5 a) i# G& G
6 M0 e _. W4 x, |* ? |