本帖最后由 shane007 于 2023-8-12 13:57 编辑 # f2 {7 t- A: u3 v# w6 s8 Z8 b
. G3 j4 l9 _8 ~9 A8 B
这个游戏原本是没有字幕的,但是在Scummvm论坛上发现了一个帖子,
# H# Q2 r, Z6 N9 T2 i3 f( G讨论的内容是关于在原本没有字幕的游戏上加上字幕。(最初的缘由,是为了帮助一位失聪的朋友), u, z3 K3 o1 P/ k# q Q: t, _" V
https://forums.scummvm.org/viewtopic.php?t=7168
# o" n, o0 {) J* `! w& T2 Y) d4 R[Scummvm-devel] Displaying subtitles in voice-only games (narkive.com); ^& X* U7 D: \
. x9 x* l$ L# J- s- r5 S1 b
前段时间第七访客出了25周年纪念版(The 7th Guest 25th Anniversary Edition。
: m% ^9 l& e+ a, [: u: z' u在这个版本中,游戏的视频是有字幕的,因此把这个视频的字幕用到Scummvm中就成为了可能。
8 ^% W. U9 j/ w- k
9 I1 L6 C1 {- o我查看了一下最新版本的Scummvm的代码,发现老外已经加入了一部分代码。
8 y% `+ Y3 B- o. ]6 j# m1 u8 D在此基础上,汉化是大有希望的。此种汉化方法,也是前所未有的,如果真的实现了将会有重要意义。; S- p1 T2 a2 v4 I8 w$ }; S
8 ]( T- N7 {6 g0 K$ _* R8 y7 Z
类似游戏还有- · Groovie$ s: _$ B" T9 }9 h' j* `! t
- - The 7th Guest
- o; C' r3 }+ m: e( ^ - - The 11th Hour1 E/ R3 l: r, }8 U, F! i* b" X
- + [# m3 p. Y: R. k% g; y# g
- · MADE
- H- e% j8 ]! Q8 w- h" x [4 e6 J& s - - Return to Zork (descriptions are text only, while spoken dialogs lack! G$ B9 N: h% N2 Q6 |1 b
- any text): S0 P) i! F$ ^! I. Z% Q5 f
- $ Q" ]) f0 d3 P+ o9 I; C
- · SCI 1
5 @( j2 F. c- g% h1 I; ] - - King's Quest V (CD Version). In this case, just enabling voice and text
) x0 X8 {, w) q2 `0 {# c( c - simultanously would allow a 3rd party to create a set of MSG files& {+ V1 g0 e) D4 W/ `$ y
- containing the voice transcription/translation, as it happened with Gabriel
1 J( u2 v6 [" f% F8 w9 J( V - Knight II./ t- ]* W) g |( ^& f0 q. O
* b. Z. R7 _% Z ?
6 m1 v9 q) E. C- Future engines:
& b2 x* w1 _3 s8 v M
' T, K' }. N. N& N: z2 C' c- · SCI 21
: R- v" |7 h/ o5 f - - Gabriel Knight II: The Beast Within (Already a fan patch available in( @+ Q- S" S s2 _: z+ E
- order to enable the text display feature). @# ~( D4 R+ `; h9 @3 @/ q6 D. V; o
- - Phantasmagoria
$ l( Q% i; b% }9 I* {4 R1 `/ ] - - Rama% S i7 L: B" E) u' k
- - Shivers
6 ?$ Q4 r T( _ Q. f - - Phantasmagoria II: A Puzzle Of Flesh
9 d5 U$ I3 k: W [2 v) R9 F - 8 r0 E |# P9 D0 C
- · SCI 3# Z7 k# o) S8 p8 g& p
- - Lighthouse: The Dark Being/ N! |2 z5 _) c& ]/ a& E
- / s5 W" I" C# V |2 r* {
- · M4/MADS. G5 g! h% J' @! F8 Y7 U1 N
- - Orion Burger
/ Y+ w! O9 L4 O. A( I
3 p9 s( N1 n0 J+ l# p7 o. v2 ]" j2 ^- · Mohawk
5 s2 i+ {! {" Q, f - - Myst+ i* Q' h j1 o7 Z2 |
- - Riven: The Sequel to Myst
复制代码
; b. t8 q5 _+ @" j
8 i" N2 f* s" T+ Y: X. _9 |0 ]0 ^( I; b& `8 ~: y
" ?" y1 v2 `" v/ @& v) H% z
相关代码如下
5 u0 o2 O3 F1 x: q- n) q/ j- i- bool VideoPlayer::playFrame() {
- z; B" r, [/ T$ C) ]9 U# B* d2 t8 v - bool end = true;
1 T, x; p1 u3 n" n( D - . b" S, F, J6 m. ]8 x6 b9 M. P
- // Process the next frame while the file is open# G6 x, T; L; F0 C1 e5 ?: [! M4 V4 m
- if (_file) {* p* G$ D% P0 N+ j, a3 h
- end = playFrameInternal();+ g% }/ h% [' X0 X4 T. g
- ) V6 l0 K9 s% H2 M) u/ u
- _subtitles.drawSubtitle(_lastFrameTime - _startTime);" R9 y9 n& f$ |" _- J# Z# J6 b% T
- }
复制代码- bool Subtitles::drawSubtitle(uint32 timestamp, bool force) {
% ?. F+ x8 y' R, U" ~- L' d; m: P7 T - Common::String subtitle;
- a {* ~& `& O: X. A0 p7 J - if (_loaded) {
8 h' G6 g2 _( O/ w - subtitle = _srtParser.getSubtitle(timestamp);
; H! G. ~" u$ X9 B! q - } else if (_subtitleDev) {
. O) y2 r5 g: M, ~) y& W - subtitle = _fname;
1 ?+ k/ i+ {: T% W* u - uint32 hours, mins, secs, msecs;
) z1 S" O7 V5 L - secs = timestamp / 1000;! H3 _1 x! c |5 l3 C0 _, Q" ]. i8 S
- hours = secs / 3600;
: ^1 h. l- L9 ?+ \! u# f - mins = (secs / 60) % 60;
- h' }8 h- B$ K5 c7 \7 ~ r - secs %= 60;
; U5 d ]1 \5 I; F1 Q) i - msecs = timestamp % 1000;& G4 k( Y* }: B1 |
- subtitle += " " + Common::String::format("%02u:%02u:%02u,%03u", hours, mins, secs, msecs);
4 B2 o5 `/ n5 w) R - } else {
# |8 E1 h6 q$ q+ C9 r - return false;
, ~$ y# z1 f" X5 P- e& U - }
9 d: S1 K2 A) o; G; O
7 m- l# r+ q! Z& m9 C5 a9 Q- int16 width = g_system->getOverlayWidth(),
3 Y# }- ?- B4 b' k8 v6 j - height = g_system->getOverlayHeight();+ ~# X% }! o' n% ?% d
- 6 Y3 K7 a. a0 `- ~/ {
- if (width != _lastOverlayWidth ||
0 R/ Z6 }7 M$ ?$ Q4 G - height != _lastOverlayHeight) {! ]8 W7 n' `1 T; @( W7 V: d! Z
- _lastOverlayWidth = width;( x. x" W* O* U4 y
- _lastOverlayHeight = height;8 h5 _/ s7 n5 C$ ~. G$ f2 f
- + e8 v, M7 c7 k; e
- // Recalculate the real bounding box to use
* g6 G9 ?! c. d9 X" [1 `+ }6 R - _realBBox = _requestedBBox;
7 t7 j M9 c0 t6 U5 d: j
& w, w+ `" d: w- if (_realBBox.bottom > height) {$ B% ~2 m% r {) l
- // First try to move the bounding box' C9 k, F l* Z
- _realBBox.top -= _realBBox.bottom - height;; \% \5 V6 W0 m; A& P4 G" j0 e8 I3 }
- _realBBox.bottom = height;+ w3 g! H! h1 u2 Z
- }
* W3 s6 V5 I) y - if (_realBBox.top < 0) {
1 _4 u, P. C* p! N- T - // Not enough space
5 Y$ d3 H8 m- g, g7 v" G7 F* S+ S7 V - _realBBox.top = 0;
) Z7 t- ~& Z. d - }5 Y. o. Q" D# |: b o: W) g
7 H/ T: h1 p! d- K% Z$ E5 T- if (_realBBox.right > width) {( y( w, f& D, W$ S& r/ D; o4 g- K
- // First try to move the bounding box
& r' A; Z! J+ f6 g - _realBBox.left -= _realBBox.right - width;
6 S3 V3 J: t2 ^" R! d$ ?' D - _realBBox.right = width;
( K% b. T& f9 e3 R - }, j" |0 s& B$ }, [" X
- if (_realBBox.left < 0) {
5 X) t0 l+ ]& z+ }" m4 e - // Not enough space9 ]+ n+ o: q7 h9 M* y
- _realBBox.left = 0;
}1 H% l0 {# |7 `$ G - }
3 D& A' V( t0 U4 P
4 M+ h2 L! t! q0 _4 y; N+ q& T- force = true;1 X' Q# h9 `1 g5 w4 {
- }6 Y. B3 n& w5 F" e" \
- 4 C8 A1 r# W5 O$ G' M- h) P
- if (!force && _overlayHasAlpha && subtitle == _subtitle)' e" L: w2 d( ~( m1 y
- return false;4 N! M2 C' O& Z {. S$ a! b6 i
- ! s7 M% e* o- ~9 T6 k) }
- if (force || subtitle != _subtitle) {3 }* S; W0 [- Z$ Y* O
- debug(1, "%d: %s", timestamp, subtitle.c_str());' ?& y. W. ~ d4 _# \* k6 t
# a5 S# n+ L+ r( O$ |, F( }8 k- _subtitle = subtitle;; p5 g/ \# ^/ b& B0 B( V
- renderSubtitle();
7 K7 d5 J' Y( |) z, X - }
# i# o$ S8 I( F - 1 v# S8 y5 _4 p- R8 d% `- w
- if (_overlayHasAlpha) {! K" j7 n* D" X1 @* O! F% Y$ h; t
- // When we have alpha, draw the whole surface without thinking it more
% n/ b8 e# U( F9 [& C3 o; G - g_system->copyRectToOverlay(_surface->getPixels(), _surface->pitch, _realBBox.left, _realBBox.top, _realBBox.width(), _realBBox.height());
4 g Y6 m/ E& e - } else {; c% F$ n4 H& d0 i
- // When overlay doesn't have alpha, showing it hides the underlying game screen& e. z+ | I$ r- e* O- i
- // We force a copy of the game screen to the overlay by clearing it7 N9 b/ p' a1 c1 j4 U$ h
- // We then draw the smallest possible surface to minimize black rectangle behind text6 c9 C- R. H' |! l0 g: p. }
- g_system->clearOverlay();
/ M4 M/ R0 s% i. {( Y# b \ - g_system->copyRectToOverlay((byte *)_surface->getPixels() + _drawRect.top * _surface->pitch + _drawRect.left * _surface->format.bytesPerPixel, _surface->pitch,
Q9 h4 Z5 h0 |# i2 A% l4 e# W9 _ - _realBBox.left + _drawRect.left, _realBBox.top + _drawRect.top, _drawRect.width(), _drawRect.height());
: Q* p% r; S6 N) {, R' } - }
6 g! s9 i9 \, b5 P" {' E* j. a. M
0 X# v5 l; b- R/ p& R5 g* T3 A- return true;
5 V4 S1 K( H) c s4 f: ^ - }! ~5 M2 B/ M( w/ s
复制代码
4 v3 X5 T X5 q" q2 N9 w8 r- C% G! z5 O h6 E6 f: E
/ c7 t: {5 A5 F: a& @
; Q; r- e, |8 V5 l; x0 G7 O" b+ J! w& ^4 N, a7 S6 W
& I: }: n H$ v8 z1 d9 P5 C- O1 C- s& [4 }* a6 L
|