本帖最后由 shane007 于 2023-8-12 13:57 编辑
) J. S* }( z/ R2 J# X2 [& j3 Y3 O6 Y; |, O, r; R9 M
这个游戏原本是没有字幕的,但是在Scummvm论坛上发现了一个帖子,: T% w% h4 u7 o# M9 V% M$ A0 Y7 h
讨论的内容是关于在原本没有字幕的游戏上加上字幕。(最初的缘由,是为了帮助一位失聪的朋友) t8 Q/ J5 i5 V8 h* [; s2 D
https://forums.scummvm.org/viewtopic.php?t=7168
- T- x0 i. {* ?$ u* r2 \[Scummvm-devel] Displaying subtitles in voice-only games (narkive.com)
9 }8 H, w( i n* q5 {' C( [5 U" ^* v$ @4 G9 D2 p" l' C
前段时间第七访客出了25周年纪念版(The 7th Guest 25th Anniversary Edition。& m$ j2 N9 U( J. g" D3 t
在这个版本中,游戏的视频是有字幕的,因此把这个视频的字幕用到Scummvm中就成为了可能。3 p$ }& d" ^; q1 h0 p
* V9 K) e) Z' S" m我查看了一下最新版本的Scummvm的代码,发现老外已经加入了一部分代码。4 C- Y, j# E& w' O4 K
在此基础上,汉化是大有希望的。此种汉化方法,也是前所未有的,如果真的实现了将会有重要意义。
% h3 V+ b9 A4 ]% p# f$ A9 V
3 C& `) ]* e7 ~类似游戏还有- · Groovie
! n8 m# `: X- ^+ ^ - - The 7th Guest/ S$ [9 K, I w, a3 O" U, V
- - The 11th Hour" n6 L Q# N- s C! T( |
- 3 h/ q5 a- G" @# g/ [
- · MADE9 ]8 S. O8 v7 M. K x- t$ @
- - Return to Zork (descriptions are text only, while spoken dialogs lack7 Y/ ~3 H: Z' |3 }; }- j7 K
- any text)
' A' @, R; q$ Z
0 c5 \2 `) X' x8 Q9 |- · SCI 1+ B8 V% z" @5 j) I
- - King's Quest V (CD Version). In this case, just enabling voice and text
% r+ V- @8 J; [$ T8 Z; K) L* M - simultanously would allow a 3rd party to create a set of MSG files' F5 f% k& l7 \9 u4 a) W+ v
- containing the voice transcription/translation, as it happened with Gabriel
9 I0 \- h0 b9 P( j; m2 Y6 G& a - Knight II.
& X& H+ ]5 f+ l2 ^6 H
1 U) v8 h9 b. X5 S- 8 Z! d# \; x/ u+ K0 x
- Future engines:
% P$ f% n2 _7 ~3 U" l - 5 o m) G6 \! \! Y! [: S% S
- · SCI 21
- j4 e9 |* V! s7 \' R - - Gabriel Knight II: The Beast Within (Already a fan patch available in
5 _0 P; ]' h/ |1 Q$ W - order to enable the text display feature)
6 |$ I7 F Z) T& `: J6 H - - Phantasmagoria" ?; A: M& l* e$ y, T0 C7 P
- - Rama
: R5 F8 K6 ?' c1 `3 Q# o. L - - Shivers1 s9 B1 I- X0 {( |* n( V; ~/ _
- - Phantasmagoria II: A Puzzle Of Flesh& N) d+ R* T3 {8 U# @# m
% O7 T# y. \7 X$ |- · SCI 3
& A' }4 Y, j h* z - - Lighthouse: The Dark Being* H+ g: ~# a: n: e7 E2 w$ m
9 E1 i, `/ O1 p3 e+ c( x& [- · M4/MADS$ L0 q5 m% S2 [
- - Orion Burger& X3 T! e9 }# a8 {( g
% ^$ `6 Z) ]2 B6 L; d* `" H/ A- · Mohawk/ P5 f0 O# ~2 z7 Q; `1 L* `% V
- - Myst
: [' F: M0 l% k. E6 ~ - - Riven: The Sequel to Myst
复制代码 6 t& A+ d" D2 E( e$ ?5 X, w
7 a. C3 _, n1 O$ ]$ O+ Z8 E) I" U6 ?* |, n4 i# g
4 u! l$ Z4 |& |/ f5 F
相关代码如下8 \* `. ~' e) m9 F$ b
- bool VideoPlayer::playFrame() {8 v5 {4 B& o2 ~) u
- bool end = true;7 C" Z) d* R+ c4 S8 K
- ) Y% K2 @) d5 n; w! G3 F0 k4 A
- // Process the next frame while the file is open4 {- U, L3 k" n! \! Z
- if (_file) {0 y' y: n$ j/ c4 T; o1 j- J
- end = playFrameInternal();* y# n6 _2 o* ?5 t& n: J
- # G) Q! o' |, c' _3 h1 s0 n. C# n: H
- _subtitles.drawSubtitle(_lastFrameTime - _startTime);4 t0 o9 w9 X! Z; ], ~
- }
复制代码- bool Subtitles::drawSubtitle(uint32 timestamp, bool force) {& x7 w7 P9 {( d. y
- Common::String subtitle;( W) N! \ O6 d4 s K6 i2 `+ U, k
- if (_loaded) {2 U z" Q' j8 y
- subtitle = _srtParser.getSubtitle(timestamp);( E7 E }$ \: o- Z1 \5 l% L- I
- } else if (_subtitleDev) {
+ M0 C# C" g+ t' n3 W" D" W) f r - subtitle = _fname;
3 w4 f# Z m2 h8 w# K* r - uint32 hours, mins, secs, msecs;5 p4 `' C2 Y7 {$ S
- secs = timestamp / 1000;
' H$ E1 r! E( n% \$ V" \+ s - hours = secs / 3600;
( s. [4 e$ b. Q - mins = (secs / 60) % 60;
7 T4 n6 I, { A6 T# A2 X - secs %= 60;. @; e/ z) C. Z! A
- msecs = timestamp % 1000;
- X8 F+ _0 M1 N0 c$ a2 v3 e - subtitle += " " + Common::String::format("%02u:%02u:%02u,%03u", hours, mins, secs, msecs);
" i% ?9 U- D8 w8 h, [% D' ? - } else {
6 h5 x% D4 l4 [5 Z8 T2 U4 _ - return false;
+ f3 b' W6 \& K. J0 {5 j6 m - }+ K* K1 v/ A- \# c, M- `
- 3 F1 z' T# j3 P7 ~$ w2 \9 S
- int16 width = g_system->getOverlayWidth(),9 `1 ~( [& h* o9 F: @6 e0 G- T3 [
- height = g_system->getOverlayHeight(); E- t6 Z( I9 h2 K8 M. |0 s
( ^4 W# w+ R5 w$ V2 ~2 z. L& J( }- if (width != _lastOverlayWidth ||% E+ L; D7 r9 p, E
- height != _lastOverlayHeight) {; K" p! E5 z6 [9 o
- _lastOverlayWidth = width;
. t& A0 I& L0 m- ~2 c- \) S* g - _lastOverlayHeight = height;
' H" y9 o+ J7 A V* G% q - 9 t; [- D" x4 m U- f& ?
- // Recalculate the real bounding box to use
& l G8 f( \( q. X7 V - _realBBox = _requestedBBox;
$ {/ |: L3 d8 ~) |% {! P, O0 X
! c1 I# [4 c7 L) R2 c- if (_realBBox.bottom > height) {9 w6 W0 X5 h+ i j
- // First try to move the bounding box
. Y; ?6 q8 J+ S( M - _realBBox.top -= _realBBox.bottom - height;4 Q. [) S& D+ i$ Q! `8 }" e
- _realBBox.bottom = height;
1 c3 `: j) R& x( Q4 `; t, c5 @8 } - }3 V9 ?( J2 S Z$ o% H5 C
- if (_realBBox.top < 0) { _: p3 H5 b4 o. M q! }4 D0 |; q0 r
- // Not enough space2 B" `7 x! N {& x9 {# e
- _realBBox.top = 0;
2 P, B8 U. c! l9 Y - }
9 ]# W6 A4 t3 c6 f" @ - " q+ [& u$ Y B; u
- if (_realBBox.right > width) {: q( I1 q G; e; n1 P. z0 J% O
- // First try to move the bounding box
9 H0 t! v4 i5 Y* {0 j - _realBBox.left -= _realBBox.right - width;
7 Z7 `& n2 F8 q - _realBBox.right = width;8 C& |+ L8 l( K6 w& J- z: [
- }
0 q7 ?: }* v8 d - if (_realBBox.left < 0) {
# X- o+ r/ ~7 \. o - // Not enough space O q" F+ p4 z9 Y
- _realBBox.left = 0;
4 F) y# R- @5 _. D; ~ - }/ s. J, g' l$ H% i \
- 7 b2 H, p. a+ V
- force = true;
5 ~! f% a/ F8 t4 k3 i! ~" |2 z - }; s; [& F5 A o2 ]
- 8 E& b1 r0 |" u9 f
- if (!force && _overlayHasAlpha && subtitle == _subtitle)4 Q G+ j+ b1 C5 h' W) U2 J8 B
- return false;
+ r3 t+ U4 s- r
& x% ^4 \5 \6 n0 W5 f/ h7 E" n- if (force || subtitle != _subtitle) {, Q |! W' S+ B( }$ g' E
- debug(1, "%d: %s", timestamp, subtitle.c_str());
% x5 G2 G! V K6 R! _4 w - T3 f. Y4 b6 o" T- Q
- _subtitle = subtitle;
. {. ^+ P! P+ A+ P- `7 H - renderSubtitle();+ b+ E E! ~0 E& z; s% h+ y4 w* L
- }
2 E/ H+ d4 g5 ]$ } - % I8 y( D1 L3 S
- if (_overlayHasAlpha) {
; Q- p. h7 \, ?# d/ o5 D4 W) A - // When we have alpha, draw the whole surface without thinking it more
9 m1 d8 u& ]0 S - g_system->copyRectToOverlay(_surface->getPixels(), _surface->pitch, _realBBox.left, _realBBox.top, _realBBox.width(), _realBBox.height());
4 j D, g( b- S6 \8 t" d6 ] f. y3 p - } else {1 w- o8 V6 x$ N& P# e$ T7 p6 C7 x W
- // When overlay doesn't have alpha, showing it hides the underlying game screen
) {3 W9 O# X } - // We force a copy of the game screen to the overlay by clearing it# B1 ^" ?- Q- k- d/ o5 F
- // We then draw the smallest possible surface to minimize black rectangle behind text. Y0 a7 g; n/ E
- g_system->clearOverlay();
* z: x+ E& E/ f D/ D. P. e( F! ~ - g_system->copyRectToOverlay((byte *)_surface->getPixels() + _drawRect.top * _surface->pitch + _drawRect.left * _surface->format.bytesPerPixel, _surface->pitch,5 d/ J/ ]! p9 o; c
- _realBBox.left + _drawRect.left, _realBBox.top + _drawRect.top, _drawRect.width(), _drawRect.height());
/ P3 r, ?, U' [! }4 r - }
! y# H# ?) n$ `! T+ z
2 s+ @* m& H) m6 r" c- return true;9 s4 A' v9 `, r/ p: n& ~. ^
- }( H+ o& S# z9 V* U& [$ y3 j) R
复制代码
- Z' |; o' j) U {3 w* r" m
& E9 E, J: s7 E. L
. \. t& e' k& D, z- @2 T/ h5 X' b* m7 T; n$ n5 x7 R v/ K; ^0 X
" r- L- P/ ?# `4 E0 H+ K1 K! l: r: e( @3 m: r9 D' u
. `. p1 w' }. A# b" X1 `4 W |