本帖最后由 shane007 于 2023-8-12 13:57 编辑
; f7 o- H K; R" ~# @- G
7 b3 y0 O! U" i, Z. `+ m/ {这个游戏原本是没有字幕的,但是在Scummvm论坛上发现了一个帖子,, j8 z- _ m1 H% f7 B z
讨论的内容是关于在原本没有字幕的游戏上加上字幕。(最初的缘由,是为了帮助一位失聪的朋友)3 E7 _+ [- V% a% D0 [4 i1 d: ^
https://forums.scummvm.org/viewtopic.php?t=71680 b& t' h% `+ L/ S
[Scummvm-devel] Displaying subtitles in voice-only games (narkive.com)
8 p" y* F6 c. g1 d9 I0 c! c0 x
5 v% l# s8 ?% e; O- I前段时间第七访客出了25周年纪念版(The 7th Guest 25th Anniversary Edition。$ o. a( j! D3 m% ?& Y
在这个版本中,游戏的视频是有字幕的,因此把这个视频的字幕用到Scummvm中就成为了可能。
0 e" H: o7 h; f; I9 ?; d) `% ?2 H0 ]- b) C* O
我查看了一下最新版本的Scummvm的代码,发现老外已经加入了一部分代码。) m1 l% Y! B7 ?( {5 _1 [8 T
在此基础上,汉化是大有希望的。此种汉化方法,也是前所未有的,如果真的实现了将会有重要意义。/ u. S+ T7 R# j' X1 }0 u% Q
% R; K* A# _/ Z% t/ g类似游戏还有- · Groovie
/ n- \, p1 h: f - - The 7th Guest
5 A) X! _% o) ?4 E# B2 N9 j - - The 11th Hour0 x& Y/ N! Y( J E5 @
, F% R1 t, ^& N& q- · MADE# b- h( ?6 S- u; Y) x5 _% C
- - Return to Zork (descriptions are text only, while spoken dialogs lack
4 b" u! j2 @5 W/ [# S - any text)
$ I( f+ m( [3 C( [0 @! [ x
0 h! Y/ s" J' P% L5 [' m d5 W [- · SCI 1' q' z+ S5 B. [% V/ N
- - King's Quest V (CD Version). In this case, just enabling voice and text
& N2 R1 e8 |9 q" J0 `+ S - simultanously would allow a 3rd party to create a set of MSG files
2 K! i, y) E; p/ [0 Z5 j - containing the voice transcription/translation, as it happened with Gabriel( ]( W7 ^7 B4 {
- Knight II.
- H# X; e9 t+ I - ) Q$ d0 c- ~% b/ \
- : X( S6 s3 Z0 @) x* w) M
- Future engines:
, Y! ?$ r: m3 Z9 ~" t' U7 s9 Q; N - . h% Q2 ?! R' R5 |$ n' @
- · SCI 21
$ n) j7 m" {2 M7 v - - Gabriel Knight II: The Beast Within (Already a fan patch available in4 U7 I2 b5 Q7 G# W
- order to enable the text display feature): ]" M$ j+ G4 w/ W
- - Phantasmagoria9 U) j( a' ]) `
- - Rama
: L# {/ L, T- o, B - - Shivers
. n4 h# V1 V8 Q: W: P - - Phantasmagoria II: A Puzzle Of Flesh3 H3 |" Z0 Z) A! J/ Q/ h
4 V/ B$ d# x3 A' f _; s- · SCI 3! {( m0 ~1 u1 ?) s6 s
- - Lighthouse: The Dark Being
+ d+ _! g0 c0 c0 @9 P; c6 k - / }" b0 {9 @% K+ D# r
- · M4/MADS
7 A7 q! G; x$ N2 C, Y3 v! d - - Orion Burger+ c' [$ U* P u* D+ v$ P
- 7 N; t) ?' c. O
- · Mohawk f: N- x. P5 \# c Z0 ~5 G, _; W
- - Myst
7 m5 P/ H% x( H# Y4 Z - - Riven: The Sequel to Myst
复制代码
; m) q1 j8 x7 z
+ Z0 y: k2 s" X u% s
4 r. L( |! b6 J" r* b" {# u6 e
& ^# f! o. q" \9 @9 s相关代码如下
& u6 O. X: h0 s/ Z; e% I- bool VideoPlayer::playFrame() {' t7 R7 K: ~. s5 F# O {9 l# Y5 N" }
- bool end = true;
$ w- Q# r+ a, Q4 C P' |+ ]9 p - , k5 M& j( A, h) g |
- // Process the next frame while the file is open6 N# L5 \% u7 o' ~( j+ Q4 _
- if (_file) {
+ V$ q G0 n6 ^6 Y; n9 I: n$ ` - end = playFrameInternal();4 J( Y$ _4 r! d( C
; ?& S. P( T6 M1 H' d- _subtitles.drawSubtitle(_lastFrameTime - _startTime);# P% \3 {8 n% a& T3 ?$ P5 p
- }
复制代码- bool Subtitles::drawSubtitle(uint32 timestamp, bool force) {
& `. N$ @6 k+ f1 q - Common::String subtitle;+ x6 u/ I4 o0 B# x
- if (_loaded) {
$ e1 }# G1 }9 U9 F- H2 z - subtitle = _srtParser.getSubtitle(timestamp);* n$ i/ k1 w L) e) k3 G; S1 v
- } else if (_subtitleDev) {
5 U, {, h2 ]4 i% E. C) U - subtitle = _fname;
( d f0 t. y/ b k; t - uint32 hours, mins, secs, msecs;
2 \, w! H( c4 a4 _& g0 P - secs = timestamp / 1000;
) c& N% S9 {8 }+ t1 ~$ J - hours = secs / 3600;
4 f$ Z9 a% H# L* T, S - mins = (secs / 60) % 60;
6 `6 Z2 ], d; S6 z; S - secs %= 60;% y% @' w* M8 X, z# m$ ~( q
- msecs = timestamp % 1000;
( h" V5 O- n2 o2 A I - subtitle += " " + Common::String::format("%02u:%02u:%02u,%03u", hours, mins, secs, msecs);
9 r1 A6 k: d6 O0 ?: t% e - } else {
0 ?3 L8 x' B U, p2 O9 W2 P+ R' U - return false;
& C' p8 n# j( V$ y; V - }
) l b( X% ~" w- w) v! j8 y3 N# L
+ h/ ]0 J& Y; N3 K- int16 width = g_system->getOverlayWidth()," |* \4 f, p' z6 r; l+ n8 D
- height = g_system->getOverlayHeight();9 `; p0 `0 c: K7 B% b
4 F: O( i s; y" o% E! ^# Q" _! o- if (width != _lastOverlayWidth ||
& t! r" Q1 B* ?: ` - height != _lastOverlayHeight) {3 k7 `' m1 M- `
- _lastOverlayWidth = width;7 R6 h- a5 h8 C; I
- _lastOverlayHeight = height;/ K0 |- m0 K- O' e, v
}2 Z! v2 q$ x: u- // Recalculate the real bounding box to use
/ x `; q! \' q/ Y" ^. u - _realBBox = _requestedBBox;* b! t) X% \6 r
7 N; `+ A# y+ n- if (_realBBox.bottom > height) {
) C5 {2 s2 E* v- ]' C8 g - // First try to move the bounding box
/ ]2 P/ o. } w# a- Q2 i/ x7 @8 O' K, c - _realBBox.top -= _realBBox.bottom - height;- y8 u- \, `3 ?
- _realBBox.bottom = height;# H- f* c9 ]* X' Y4 K$ u
- }
. v$ D% `6 D6 E2 e) U - if (_realBBox.top < 0) {0 y" W* T% z6 B! j$ o0 ^
- // Not enough space2 |! G& L) Z& b+ Q" Z% W$ D
- _realBBox.top = 0;% g; ?: a @) r6 ~, a
- }/ ~4 ^2 S( I+ P6 ]' m
- # M* ] d* y* h& A! h/ B' V
- if (_realBBox.right > width) {
! e: f3 j' ]$ n - // First try to move the bounding box
5 t, \" C/ Q2 l/ R0 A - _realBBox.left -= _realBBox.right - width;$ Z) N7 K, U' K. w5 ]" x, x
- _realBBox.right = width;7 ^( }9 T1 f( r/ b/ a
- }
/ ]& @8 E/ R5 n2 G' `8 U, [ - if (_realBBox.left < 0) {
, V/ d/ W0 `$ B5 ?. u9 {1 x - // Not enough space
9 s, L9 K" {+ Y" H - _realBBox.left = 0;
8 T( ~- v w3 G9 ~9 [7 u - }
7 w' X# ^$ N7 @* u+ p. K - 9 w; |7 A/ ]5 y1 g' }) V$ F
- force = true;
8 {, P3 q: j; p9 t) M2 Q5 ]! {6 b5 _ - }
" v6 c6 \; r/ h D& h) @7 U: R4 V - ) p% T% K$ J. G; K! i
- if (!force && _overlayHasAlpha && subtitle == _subtitle)
/ n V$ E2 I$ _. G - return false;
6 o# {' I1 ]# P4 g
. {) y8 }' ~0 M6 N- if (force || subtitle != _subtitle) {
1 C: B: {; p$ A: h1 C1 p; j - debug(1, "%d: %s", timestamp, subtitle.c_str());
! [) {+ ~* d4 x) S! q
5 F2 n) P9 l( M1 c- j- _subtitle = subtitle;
! I( G. `' R, L: Q% U2 g" v+ Z- z - renderSubtitle();: {" R# L/ j/ O
- }
9 }. e7 O) g N4 A - , F1 W) I, o5 z7 v
- if (_overlayHasAlpha) {* ?' D8 ~# W9 {- d0 ^9 p
- // When we have alpha, draw the whole surface without thinking it more
0 G, f; P) c1 n9 ]# l5 M Z, v - g_system->copyRectToOverlay(_surface->getPixels(), _surface->pitch, _realBBox.left, _realBBox.top, _realBBox.width(), _realBBox.height());) v( ?8 A. \/ y1 H3 E7 b
- } else {
! ~3 L# ^5 j! l/ U& c - // When overlay doesn't have alpha, showing it hides the underlying game screen" c) i% t$ b( {
- // We force a copy of the game screen to the overlay by clearing it
r. \: {+ \6 I- e" g4 Q - // We then draw the smallest possible surface to minimize black rectangle behind text
" u) F! t7 g: p" i) e/ c - g_system->clearOverlay();
! x. V. E0 Y8 |$ x1 g7 _ - g_system->copyRectToOverlay((byte *)_surface->getPixels() + _drawRect.top * _surface->pitch + _drawRect.left * _surface->format.bytesPerPixel, _surface->pitch,
) F' Y7 s& j2 z4 |6 p" f4 B/ \; I3 N w - _realBBox.left + _drawRect.left, _realBBox.top + _drawRect.top, _drawRect.width(), _drawRect.height());
0 S' q, V7 u1 j9 Y - }
7 C6 ]- i) k1 Y' p2 g8 t+ E: x* K - # V4 I8 T3 P& y
- return true;; E1 |$ U- x, {. {7 F
- }
) I# Q2 L, u9 g. X
复制代码 # q: A3 l7 {' o* q* [. j
8 x7 }9 w' K6 J8 U0 V
) O2 ]/ l6 t4 H. [# i, m- z3 _5 h/ [! ^
* d# _; z! C1 [+ s2 ~7 O: ~6 u' s$ R5 |3 v/ s" @
' G( H) J8 ?5 C |