本帖最后由 shane007 于 2023-8-12 13:57 编辑
6 s' f: G9 X3 Y2 L& F$ z' P3 t* p1 o
这个游戏原本是没有字幕的,但是在Scummvm论坛上发现了一个帖子,
6 I: f( U( E" U" S$ ]讨论的内容是关于在原本没有字幕的游戏上加上字幕。(最初的缘由,是为了帮助一位失聪的朋友)+ R# k+ }! Y% B# x4 x: {
https://forums.scummvm.org/viewtopic.php?t=7168, L, j2 C( L- ?
[Scummvm-devel] Displaying subtitles in voice-only games (narkive.com)
, ]0 ?5 B N9 ^ X( C7 t8 N# {% ?; t; u$ W. t/ F
前段时间第七访客出了25周年纪念版(The 7th Guest 25th Anniversary Edition。
' u, ]+ N+ U6 R4 Z+ T在这个版本中,游戏的视频是有字幕的,因此把这个视频的字幕用到Scummvm中就成为了可能。) @7 t, |6 d7 }1 `4 m
, F2 p; U0 X$ r x$ P; ~我查看了一下最新版本的Scummvm的代码,发现老外已经加入了一部分代码。
. b) z& X4 C( A; [在此基础上,汉化是大有希望的。此种汉化方法,也是前所未有的,如果真的实现了将会有重要意义。
7 ^/ A* w1 X! q5 ]3 K8 S- W% k! {7 K6 I8 a* \8 a
类似游戏还有- · Groovie8 @1 k0 n2 i" ?% g J1 N
- - The 7th Guest
8 i1 }2 _1 U0 C$ K+ Z8 x' g - - The 11th Hour
- w% `% m! c. @9 Z) |5 P - g! n7 A; q5 y! `
- · MADE
1 c5 w, ?. a O; v+ d% o - - Return to Zork (descriptions are text only, while spoken dialogs lack
/ g: L" b4 F" m8 d - any text)
[- P3 Z% a3 D- p/ a - & O2 B. n+ I& C3 Z4 n! V4 j4 N: y
- · SCI 1! Q# I# b) N# @0 u% {
- - King's Quest V (CD Version). In this case, just enabling voice and text
+ v( ^7 @" ?4 z" H$ s- t; i6 h - simultanously would allow a 3rd party to create a set of MSG files1 d; n3 C. d4 i2 I6 G- q4 X
- containing the voice transcription/translation, as it happened with Gabriel
4 W) j) x1 H1 k - Knight II.
, d/ s) W- w, B; X
: \# k$ h, Z2 A* a0 s- ) e# c0 S& R3 C( g8 y8 u% L
- Future engines:) f* V* k. N3 i5 r* g: W% s% f
- ; X( |) ^6 r5 y% u) g* \
- · SCI 21/ N% \) d& _- N" w( E( P
- - Gabriel Knight II: The Beast Within (Already a fan patch available in
1 Z- T7 n4 H" S& |: g* l - order to enable the text display feature)
' ?2 [( `6 C3 C& S, p - - Phantasmagoria) h, m# _7 n) p7 f+ O+ r
- - Rama9 b! A3 A0 H$ V7 K9 O
- - Shivers
& x w5 I; D0 k9 ]- {9 @ - - Phantasmagoria II: A Puzzle Of Flesh
( I! R ]8 T) P5 v- K
; K% w7 S" h( ]/ l3 b- · SCI 37 M, M: k( ^% f; {% [
- - Lighthouse: The Dark Being
8 M, z4 b* \; J2 u7 j1 j$ i" n
$ c0 Z. W- S C5 u. L* A1 q- · M4/MADS
8 ]- }: {" Q6 q( {1 M4 Q - - Orion Burger+ s! ?2 ?7 F* O( V' K- O5 [$ `) y
6 k* y$ l% @' V) }- q( n' h- · Mohawk: g" K4 g+ w4 S' r' B
- - Myst6 d( v2 q4 u0 d9 s
- - Riven: The Sequel to Myst
复制代码
}. O9 f- _; @5 C0 w2 j, L- R/ V+ f1 H ^- K5 H6 C
3 A$ s$ |2 g; Z' N$ |. }7 j p9 f2 ^4 u( ]5 P5 L4 _* ?" V
相关代码如下
7 x3 _- Y8 I% ?8 u' V- bool VideoPlayer::playFrame() {
2 }1 p* G! M: c - bool end = true;
4 m; n% j$ I% z* T
. w' q! Q. L, a- // Process the next frame while the file is open
" U4 y* E! _ K: @ - if (_file) {
+ z; U) z( X& s1 p7 y - end = playFrameInternal();
. q. t( C, c4 b ~# i
* `: e( f- T/ N9 v- _subtitles.drawSubtitle(_lastFrameTime - _startTime);- I# ?6 F: J) c" g4 N t( m7 L
- }
复制代码- bool Subtitles::drawSubtitle(uint32 timestamp, bool force) {/ `4 U6 ]9 s- q1 }
- Common::String subtitle;
Z' @0 _ M3 M7 z8 W7 N - if (_loaded) {5 I& z( v; C% ?: b2 l' U+ k
- subtitle = _srtParser.getSubtitle(timestamp);
2 \- J% f' A4 A1 P* u - } else if (_subtitleDev) {
. L$ E( V6 g% f2 T# T# k - subtitle = _fname;* Q6 T& @7 V" o/ @4 U$ [
- uint32 hours, mins, secs, msecs;$ D! {8 W0 v" G9 r K
- secs = timestamp / 1000;( o+ w2 t: B1 f3 B3 U: q
- hours = secs / 3600;
3 F. \9 J" \7 } a) I: | - mins = (secs / 60) % 60;! q* ` ?' v% W+ H( p2 a
- secs %= 60;8 S8 K1 F. j; i
- msecs = timestamp % 1000;# q! W/ J: P4 \" G* T2 ?
- subtitle += " " + Common::String::format("%02u:%02u:%02u,%03u", hours, mins, secs, msecs);
! E! t9 h/ m# U4 U# D, {, o$ Z - } else {
0 r" N/ J* R- P% F; K, {$ s - return false;3 m- q' Z8 O( y1 ?! i: S
- }
& G3 [- h( U! O) g0 i; @8 ^
/ R) s1 k. x g& t- _% z- int16 width = g_system->getOverlayWidth(),+ O! w/ r2 }% k1 [/ K X
- height = g_system->getOverlayHeight();
: M1 T% ^& D8 a$ x' @+ v/ M - . v3 k1 G0 g& p
- if (width != _lastOverlayWidth ||& K2 Q, z6 F' U" X, U+ j } I
- height != _lastOverlayHeight) {: S+ g4 ~0 _- ~
- _lastOverlayWidth = width;
; _, P4 g+ a& {* j - _lastOverlayHeight = height;
n; ~* B& e* E6 I1 k - - H$ _# T4 R' R3 O, t- B5 }) n+ h
- // Recalculate the real bounding box to use0 z) n! ?" b" @' J5 d. q
- _realBBox = _requestedBBox;" ^, ?9 L/ G0 T9 X* i8 l
3 d) D$ r, P+ [0 ]' R" F* V8 l- if (_realBBox.bottom > height) {
6 [2 }8 E# ?# s2 m% s8 w; f9 h - // First try to move the bounding box
' ~" Y9 e5 G! e5 A+ i - _realBBox.top -= _realBBox.bottom - height;
, \4 h8 R2 X& `. b9 |1 T! e1 U - _realBBox.bottom = height;
( k, @: {3 ]" X5 g - }
. \) M* q& v! J* V+ V2 j4 R9 U, ` - if (_realBBox.top < 0) {
3 T' H" L# M y9 Z6 {' Y$ \ - // Not enough space
* p; W7 `; j: o5 C: A9 I$ W8 n: E - _realBBox.top = 0;3 M: P. m, w( O p( E
- }
! m9 J+ k% f& g
' M2 o+ n/ Q6 @; J0 R- if (_realBBox.right > width) {
5 M: C! c0 p; R" O# U - // First try to move the bounding box
7 r6 o9 t% g0 o2 Z( p l- }3 T( z$ ~ - _realBBox.left -= _realBBox.right - width;% \9 a4 o3 E# Z! U; j4 L' `
- _realBBox.right = width;
6 p; y/ B. l7 a% _ - }) L0 k! N; Y* M u
- if (_realBBox.left < 0) {
1 E/ t' a: n4 j$ _2 d8 N8 u, { - // Not enough space
V# c+ j; `5 c4 ^ - _realBBox.left = 0;
) p; E8 S- B p5 u) @ - }' R( c6 e) @0 A$ n" }2 E4 s
- & s+ \8 T: Q' ^" f
- force = true;
3 Q* T5 J0 `2 g* X/ n - }# l' @) p' z" O& |! {
- @( Q2 `, y- N8 b- if (!force && _overlayHasAlpha && subtitle == _subtitle)* x" {. _' i6 X$ k& q* ]3 A# `7 }" s
- return false;
. k) w" n8 w0 d, |; ~6 i' h7 A
0 a: p2 U% A3 P3 A- if (force || subtitle != _subtitle) {4 c; X; i' v8 a1 d
- debug(1, "%d: %s", timestamp, subtitle.c_str());& W' S. @: P! H w& c: J
r& Z6 I4 t0 m/ F9 r- v& _: {- _subtitle = subtitle;
, n. k% F& O! @0 l, \% R' ^) v - renderSubtitle();
" j, A5 C8 \/ g - }
( h& c! k& W' J; l& N. B, n) H - # V4 C C) U0 e5 b0 D+ N
- if (_overlayHasAlpha) {/ v( @8 m2 H8 d# a; V+ E" V) {
- // When we have alpha, draw the whole surface without thinking it more5 S$ p) g5 L9 Z' a2 E3 i# w# w: I
- g_system->copyRectToOverlay(_surface->getPixels(), _surface->pitch, _realBBox.left, _realBBox.top, _realBBox.width(), _realBBox.height());2 L- W9 m8 Q' {; f* s5 a6 X
- } else {& H( D/ C2 W% Z3 ^! y0 k' }
- // When overlay doesn't have alpha, showing it hides the underlying game screen: R% d! R1 n* O& J' c2 u9 R
- // We force a copy of the game screen to the overlay by clearing it
! t" e6 f( p* H1 C/ n1 r, ~ - // We then draw the smallest possible surface to minimize black rectangle behind text
t8 {) P3 ^* U$ W - g_system->clearOverlay();
2 Q1 n" w' E2 i. s6 l' s1 u - g_system->copyRectToOverlay((byte *)_surface->getPixels() + _drawRect.top * _surface->pitch + _drawRect.left * _surface->format.bytesPerPixel, _surface->pitch,, k( }, T8 R1 `; X: {& I
- _realBBox.left + _drawRect.left, _realBBox.top + _drawRect.top, _drawRect.width(), _drawRect.height());
, y6 N$ o) B8 P7 b- g4 g% h, S - }
/ e6 \ U. ?. t6 p# b# f {$ w+ C- U
! v9 E$ e4 ] Y. G$ B/ R- return true;0 _8 w6 {. J1 [2 |& [
- }
+ z" ?" N v9 S
复制代码 # E" E, d- e+ L! q& {
9 z1 Y9 [4 ~& q8 |# K: I( }
/ T- Y" Z/ p# f3 S, {% _* u. j
- \8 m; ]7 m: @3 _/ |; p
! o, O! ~. _& U5 d5 O) F' P8 x
D& o- j# ^7 Y. E( ]+ ~9 b" g; q2 e5 B) Y4 D& X' |) C
|