本帖最后由 shane007 于 2023-8-12 13:57 编辑 & o9 Q% W* h% n) T+ I2 D3 d
9 q# e/ F- Y3 N" J3 E. y6 d
这个游戏原本是没有字幕的,但是在Scummvm论坛上发现了一个帖子,0 `% B: B f( ^1 f2 \9 c$ u, r
讨论的内容是关于在原本没有字幕的游戏上加上字幕。(最初的缘由,是为了帮助一位失聪的朋友)
7 M5 w0 L, |% K0 C6 Vhttps://forums.scummvm.org/viewtopic.php?t=7168- W1 F8 O! M; ^4 n7 s6 i
[Scummvm-devel] Displaying subtitles in voice-only games (narkive.com)
/ _, G* ~' t4 F. B# |- q8 n, q
前段时间第七访客出了25周年纪念版(The 7th Guest 25th Anniversary Edition。+ X( y* S5 A/ c6 a- w8 T
在这个版本中,游戏的视频是有字幕的,因此把这个视频的字幕用到Scummvm中就成为了可能。
8 S* H5 N2 ^; }
8 ^; I1 t0 p/ s& x( Q! N# g我查看了一下最新版本的Scummvm的代码,发现老外已经加入了一部分代码。
6 I8 p5 ]* e1 q. x0 Z在此基础上,汉化是大有希望的。此种汉化方法,也是前所未有的,如果真的实现了将会有重要意义。
2 z, u; P, g4 o/ |. W! q7 f! {
4 j& c, \/ Z. V* p( Y类似游戏还有- · Groovie: [7 c9 f, }4 G0 e& `7 y
- - The 7th Guest
' a4 n# \) Q6 G4 B; n9 n - - The 11th Hour; ^2 N4 X+ s) G, o; i0 F
( R# S+ H N6 v' g. F- · MADE
/ F$ T7 a3 r- m; p' K9 h0 v - - Return to Zork (descriptions are text only, while spoken dialogs lack- h1 m1 |# e5 l
- any text)+ Q5 ]; f( c; m1 x# I
- ; ?% ?) q% o" N8 o/ L
- · SCI 1: i( C8 e& b; y; b1 p+ j* ^3 [( ?
- - King's Quest V (CD Version). In this case, just enabling voice and text1 s& k2 Z5 b4 y
- simultanously would allow a 3rd party to create a set of MSG files
8 p# W4 q$ U$ k$ F t X: p; `% n - containing the voice transcription/translation, as it happened with Gabriel6 X. U% c% j5 J8 G" i* B
- Knight II.1 h# P+ `8 A3 ^$ M# N* y% c
$ U0 c* \0 ^9 M k; l5 N
6 x3 V, f$ z- B7 r7 [- Future engines:
- C' t1 n' q4 @8 \# X" w: e8 t) h& c - * e f. c- F0 C( X& U3 a) V
- · SCI 21
4 F. {. U. @! k& ]6 K' R - - Gabriel Knight II: The Beast Within (Already a fan patch available in
- S8 B# Y5 e. c" [! z8 Z" j - order to enable the text display feature)- i% V# y! I% ~8 N7 |7 g3 e8 }
- - Phantasmagoria9 C$ n" }+ B7 N2 i% ]. V$ A: ~
- - Rama
% C4 l. B) I6 Z9 n - - Shivers; c0 v3 u& w4 ?$ q# m
- - Phantasmagoria II: A Puzzle Of Flesh
1 k' Y' `- Z4 P. X# { C, s# t - & W k S4 x8 K% i* a' l
- · SCI 3
& o0 x6 R6 U. H% w: D4 }/ `: u- ] - - Lighthouse: The Dark Being- l$ ?& Z' G0 P- z
- m$ j+ W5 y! C6 q/ Y. L- · M4/MADS) @) A1 _/ K3 N. I! _5 s2 X9 T
- - Orion Burger5 E$ ^7 R5 A; |5 Z R
- 2 b( @5 L6 U; q! v n( Z
- · Mohawk
) b" ]% Q8 [7 T. |+ f% z. u9 M8 y - - Myst
5 Z8 Z, o% l# h" C. P5 A6 ` - - Riven: The Sequel to Myst
复制代码 ; c( q7 V2 k0 {+ ?9 P
K1 w5 R( R9 j* k# |
6 U& k7 q1 L# X
6 g9 B. x, E0 O8 f8 C1 p0 q
相关代码如下
1 G0 h4 u8 C k1 x- bool VideoPlayer::playFrame() {0 T' H J( j1 s" o
- bool end = true;
6 S% {) j2 k: F - $ n! W1 z$ y1 A- B
- // Process the next frame while the file is open
1 g4 v, Y7 }0 b - if (_file) {
1 z1 Y* D h1 @5 ~0 D' r5 {2 ~ J t - end = playFrameInternal();3 B* J. r" w# z$ H) ?; g3 _
/ h7 \3 T! Z* d5 g* C- _subtitles.drawSubtitle(_lastFrameTime - _startTime);1 a/ F! ~% f/ i+ s
- }
复制代码- bool Subtitles::drawSubtitle(uint32 timestamp, bool force) {
, x6 x( Z. B1 ~! a- K( [ N - Common::String subtitle;) {9 I( u; ]# H& h; `9 a% A. P% {
- if (_loaded) {
$ F Z: Y* N3 L! D9 C4 g - subtitle = _srtParser.getSubtitle(timestamp);
5 S `! ?( p! b l; s/ s - } else if (_subtitleDev) {
- o, ^! G( ~' x* c4 ^+ | - subtitle = _fname;
; W9 t- l! P; o& @+ R - uint32 hours, mins, secs, msecs;
- s# z% @; l: E0 V1 X - secs = timestamp / 1000;
/ G% {+ q! J" E( C - hours = secs / 3600;" g) G, p! d$ D' C/ B! K u1 D
- mins = (secs / 60) % 60;+ P1 j* B6 u4 I
- secs %= 60;
1 ]( Z% R8 C% U9 g m! j - msecs = timestamp % 1000;
5 Q3 }, }1 q# R9 l9 x - subtitle += " " + Common::String::format("%02u:%02u:%02u,%03u", hours, mins, secs, msecs);) ~4 u# W2 x: g8 t: d0 H7 e; T9 _
- } else {
( h- }" c1 a1 z9 L1 M7 N" }; i - return false;
; \( j6 T2 L: Z9 ^' u6 a - }& T( ~1 p3 U$ l9 U4 f. {$ ^
- # V( c: `+ }. W
- int16 width = g_system->getOverlayWidth(),( ^; {9 {; x1 ~0 h5 |" q
- height = g_system->getOverlayHeight();' S+ R5 y: t: [% q/ y% C# o
- : o0 n" X6 Z3 Q5 `4 M
- if (width != _lastOverlayWidth ||, f E7 L# ?2 @1 Z
- height != _lastOverlayHeight) {; X) o6 E {4 ^3 p
- _lastOverlayWidth = width;: P/ ?2 Q/ m2 _ r" ^. `, Q3 h
- _lastOverlayHeight = height;" x; M& T" i& K' ^ w
2 {$ F6 L6 `2 A' I9 d+ v- // Recalculate the real bounding box to use/ W, m/ b9 O( L
- _realBBox = _requestedBBox;
5 s0 o3 i9 Y2 J W1 G$ B. b
0 U" _5 Z: {+ U6 v- if (_realBBox.bottom > height) {
3 {5 R! O5 e: W" K7 ]) G - // First try to move the bounding box
- z* p; I' @) j2 `* y+ ` - _realBBox.top -= _realBBox.bottom - height;4 G% J& z$ m, n% E; c$ v# Y
- _realBBox.bottom = height;
$ Z* a3 e; N7 t( Y0 Q- O - }
9 a* p$ @8 `) I3 e! r& B - if (_realBBox.top < 0) {
- L: G4 \' {! S# `/ Q6 O/ C - // Not enough space, H( E- o2 V# u3 D2 Y# E; F
- _realBBox.top = 0;! Z# u$ \" e& a. M) z+ p2 Q- y
- }" G9 L4 g1 r' S: r& a0 g: u) x
- 4 ?, [+ j+ O# p3 |
- if (_realBBox.right > width) {3 s& J. [7 T5 X- @/ o; k
- // First try to move the bounding box
2 q: V4 W1 b1 W' n/ q - _realBBox.left -= _realBBox.right - width;9 ~# p$ q- v9 z1 C. U
- _realBBox.right = width;; {, h: j! }& B9 D2 ^ H
- }
/ t' E" u4 L- P% d2 j K - if (_realBBox.left < 0) {, }. C! g9 y) E1 _1 t2 ]$ G
- // Not enough space
# { z4 e/ h: r* } - _realBBox.left = 0;7 S7 }/ S5 `8 i! T
- }% b a) ^- Q: Z( l) Q6 z# O$ x
- ' c% p/ M; S: g' t- ?( Y9 v! k" V0 g
- force = true;
; L$ {; M: L' ?: Y9 o& l. F2 q - }9 N `% ^0 b8 c, M9 } {$ n
- 4 C7 q% @, c$ [% E% b0 z
- if (!force && _overlayHasAlpha && subtitle == _subtitle)
8 n" F4 C/ E2 ^! { S$ c$ a - return false;5 b9 ?5 W- O! j& p) i
- 4 g( E* a+ ~! |5 h+ G8 g1 R: z0 Q
- if (force || subtitle != _subtitle) {
2 e/ }# w) t3 B5 \8 F; i - debug(1, "%d: %s", timestamp, subtitle.c_str());
: s+ H) K h( B6 C. P& g" {
4 t0 S/ G* ~+ S- m R& e' h+ T- _subtitle = subtitle;
* T4 e- d6 ]8 ] - renderSubtitle();( o! K: p! X4 r
- }- P7 M, ?, D/ P, n' c- B: E' ^7 D
% {; N0 h+ ]6 v0 r- W- if (_overlayHasAlpha) {! s1 |( S8 Y; n( X# W+ z! L, I
- // When we have alpha, draw the whole surface without thinking it more6 D! H9 f# I F
- g_system->copyRectToOverlay(_surface->getPixels(), _surface->pitch, _realBBox.left, _realBBox.top, _realBBox.width(), _realBBox.height());. V0 U1 ?6 [ Z/ P% k+ z: K3 X
- } else {: D, d0 |! t& O# l% i5 w) ~5 L
- // When overlay doesn't have alpha, showing it hides the underlying game screen
) U" ?7 N; E( W7 m2 r3 H6 y - // We force a copy of the game screen to the overlay by clearing it
; g$ i8 g/ {- F" } e' S T4 l$ X - // We then draw the smallest possible surface to minimize black rectangle behind text
, o+ G' B" J/ y; f9 Q& L( Z - g_system->clearOverlay();7 o+ @% g8 n% x3 {0 \9 j
- g_system->copyRectToOverlay((byte *)_surface->getPixels() + _drawRect.top * _surface->pitch + _drawRect.left * _surface->format.bytesPerPixel, _surface->pitch,* R* i" `* H: A. b% q* p
- _realBBox.left + _drawRect.left, _realBBox.top + _drawRect.top, _drawRect.width(), _drawRect.height());# X9 A) B% d7 O* I$ Z# [; g& _7 \# F( O# u
- }
7 v$ ^/ E! Y& {8 w8 C" F0 t! Y8 x
b; K5 J1 a0 W' Y+ E" \. X/ W- return true;8 p% E. d: z* r/ w; J) O$ Y
- }2 l9 V z+ `! M6 N% B! I1 @/ {# ?
复制代码 7 {. D% j+ a- l2 U
' e% g: k6 |& e \4 o# O1 a
- T Q, b; @- L; u" B
. Y% `2 `) k! l' O- x* V% F( Y1 Y) S$ i, O g2 t9 Z* t Y3 F( E
- g, m& H. K+ v8 [/ N9 r6 |4 k* g9 ?7 L/ I" _8 F2 M- E4 B
|