本帖最后由 shane007 于 2023-8-12 13:57 编辑 + D% F* f' J. H8 c" v2 v
- N K1 L7 z2 z这个游戏原本是没有字幕的,但是在Scummvm论坛上发现了一个帖子,& }1 V& Q, G: m( P5 h
讨论的内容是关于在原本没有字幕的游戏上加上字幕。(最初的缘由,是为了帮助一位失聪的朋友)5 \1 }' P, Q7 U S& p
https://forums.scummvm.org/viewtopic.php?t=71686 M" Q' J# F" r( N6 S; w
[Scummvm-devel] Displaying subtitles in voice-only games (narkive.com); T# K' p# Z/ B- E0 E+ f+ D: Y9 n2 f
7 s$ m, }7 d6 W, ^% p `
前段时间第七访客出了25周年纪念版(The 7th Guest 25th Anniversary Edition。( h9 @' D3 Z2 C% [1 U- `5 w
在这个版本中,游戏的视频是有字幕的,因此把这个视频的字幕用到Scummvm中就成为了可能。2 j, E) h, o) x$ H- B* E, b0 _
! T* j1 f5 E1 s& O r3 y我查看了一下最新版本的Scummvm的代码,发现老外已经加入了一部分代码。
( {. x2 m4 b8 n4 D5 n在此基础上,汉化是大有希望的。此种汉化方法,也是前所未有的,如果真的实现了将会有重要意义。2 f p& J! H3 N& z1 f# b
2 i6 X2 M; z/ l, ?- t9 I类似游戏还有- · Groovie
+ B4 ?5 v5 N i - - The 7th Guest9 U. x! P" X# A$ e# }' [- H$ A
- - The 11th Hour$ _7 u8 O: k" u+ D ^8 B; T
" C# ]0 I% h8 X c- · MADE
) {+ c; y$ @. z+ @6 N" Q U" w - - Return to Zork (descriptions are text only, while spoken dialogs lack4 J" F% p; g, U* p8 E
- any text)' z4 }& y8 {0 G' ~; u$ w
- & A# m, Z2 w4 i6 Q) K) \- R
- · SCI 1
" f( Z# l" B, `# ?( C; l+ ? - - King's Quest V (CD Version). In this case, just enabling voice and text' k) e A. Q/ |6 v# P8 r
- simultanously would allow a 3rd party to create a set of MSG files
+ w1 V3 t9 [& x4 Q6 M) _2 Q - containing the voice transcription/translation, as it happened with Gabriel
5 j& F0 A$ s3 H: J5 P# K - Knight II.% t9 C6 t3 {* L2 \0 E r
1 n6 B1 i0 I" N) f- $ {5 T& j) M- [0 B
- Future engines:
$ W# R# X5 I& Y8 |; N - ( G2 l& m5 x" _- {
- · SCI 21
$ C7 e' T& r* b6 M% N - - Gabriel Knight II: The Beast Within (Already a fan patch available in7 y" V2 f# z T2 @6 z$ U* k
- order to enable the text display feature)
* Q5 M+ }9 O# Z6 ?: I% b6 b+ d2 ~ - - Phantasmagoria- K! u! x. u" V( }2 \
- - Rama
4 @/ z M4 O% [8 I$ v - - Shivers" V- p4 Q0 I, y# W; \2 ]: U0 b
- - Phantasmagoria II: A Puzzle Of Flesh
+ F$ S) ?) f% B" v6 j l, e. x
" `) a5 k) r' v4 e- · SCI 3$ Z" R; w( Y* W7 g/ M7 c
- - Lighthouse: The Dark Being
0 N; y) T: r) Z. K1 | - # J. `0 F5 P; ^, G, T: q" m" ?! ?
- · M4/MADS
; g: j2 D% d' C- L - - Orion Burger+ s4 c- j3 h& u r( ~& M. K
- & W% I {- R) n5 L: s
- · Mohawk
' Y! i0 L# P, \, l3 y; D7 `; ^1 d; \( Z - - Myst3 u( a- s E+ `
- - Riven: The Sequel to Myst
复制代码 / B* U# x A7 q/ V
/ d$ i2 w- j: u( }/ q5 V) _
7 D2 y3 u) k) k. A5 ^0 b! E9 S( M5 Q* v% J" W
相关代码如下
* x$ e3 J! K3 A6 z$ F- bool VideoPlayer::playFrame() {
, y* @* y: X7 l7 D5 H+ ~5 V - bool end = true;
9 T, N: z% E" W% ]/ S* ? - ; E ]& y) d4 `3 C& A( |
- // Process the next frame while the file is open
, F& O3 S% E. c$ ` - if (_file) {
3 Z" t5 X) f1 k* p - end = playFrameInternal();
$ S$ c: c5 o2 P# M& X' o
" p4 J, `& c* K! a5 f1 _* i; S5 F- _subtitles.drawSubtitle(_lastFrameTime - _startTime);
+ t) S& y; w1 b n1 J! I - }
复制代码- bool Subtitles::drawSubtitle(uint32 timestamp, bool force) {2 ]) D* P2 h: m: I. N J% h. y
- Common::String subtitle;
o+ n1 T& H7 X# l - if (_loaded) {
; C! g) s/ S9 l+ i$ q. e* A - subtitle = _srtParser.getSubtitle(timestamp);! i: Q* W8 \; { e! |1 D. [
- } else if (_subtitleDev) {
9 S8 @ H, w- v7 D7 R8 h& h - subtitle = _fname;; `6 h( v2 Y6 ?; U7 v2 ~
- uint32 hours, mins, secs, msecs;) m( j; n3 m6 A; C1 m: h& X. x# {
- secs = timestamp / 1000;
l' V. I9 k: i9 e# t - hours = secs / 3600;
0 L2 `; e! ~5 s& g - mins = (secs / 60) % 60;$ X" ?8 y( Z/ o) P& `* X
- secs %= 60;* {1 ^7 t) A4 M" Q+ a' y
- msecs = timestamp % 1000;8 z! d: u/ X* ^
- subtitle += " " + Common::String::format("%02u:%02u:%02u,%03u", hours, mins, secs, msecs);8 I; E7 [% f, [/ j
- } else {
3 b% @( J( t, J0 o" Q7 c! P9 |& z - return false;+ M* o7 B0 G" \* @5 ?( }9 ~3 h
- }5 ]. s. F) H( {6 y/ c R. o J
- ! C& T- }5 N/ Z& Q
- int16 width = g_system->getOverlayWidth(),/ ~6 N. X# r9 n& ^
- height = g_system->getOverlayHeight();6 U6 j+ M: l) H( q1 {; W$ k& J
- ; L7 x( N$ t% c- n
- if (width != _lastOverlayWidth ||
; d5 ?: E, }* m6 T5 r: I - height != _lastOverlayHeight) {
/ A& k) X' Z- v( d: m - _lastOverlayWidth = width;
" u, u6 C# p, ~# l6 r: B# E - _lastOverlayHeight = height;
6 H4 ?0 ?2 h) g$ j8 U - % t* g- G! m' R% R% e. W
- // Recalculate the real bounding box to use
; P* c& G) e. G$ p) E: Z) b& Y* M3 P - _realBBox = _requestedBBox;
4 s$ w4 f! F; p* X - 2 n! v1 l; P/ p$ U" l
- if (_realBBox.bottom > height) {" O" k% u& A- _) U- |
- // First try to move the bounding box
A* q* L _, q( z+ r# q - _realBBox.top -= _realBBox.bottom - height;0 I+ ]% S2 `! ?( q& `0 k
- _realBBox.bottom = height;
* } }' B- Z9 {, P- K - }
4 f( A; y3 l7 U* |, }- k - if (_realBBox.top < 0) {
8 V1 o6 e- N0 X3 [* e2 h+ c - // Not enough space7 W, }* K( _/ t6 k# u) Q
- _realBBox.top = 0;! o! k2 m5 e3 O( N/ E
- }
; n& d; f: G6 c - 9 w$ ?& J# x) l! ^3 h8 L
- if (_realBBox.right > width) {8 P" J# ~6 t3 p; t) u
- // First try to move the bounding box- A4 _; G2 p$ r4 T6 w
- _realBBox.left -= _realBBox.right - width;
8 A" h7 X5 J9 @% o" x: W - _realBBox.right = width;& q4 { F: s0 j5 F# S* X
- }- p" \& P9 Z2 d
- if (_realBBox.left < 0) {, C. _4 q9 g# L, u" k$ T
- // Not enough space
3 G) F" O; m [8 k4 q! W' |& J - _realBBox.left = 0;6 E, |' P" n* H* B% T; I) o
- }
3 z4 X* E( D$ o
; @/ D h& L7 T( a6 _4 y- force = true;
; ~* D1 k) ?: L8 k - }
1 Z6 P. A. s5 T0 l$ C
0 T' u: a# c: p- if (!force && _overlayHasAlpha && subtitle == _subtitle)! D, Y. H h. N7 e2 l' a: w3 V
- return false;
3 |4 |) ?4 h4 Q0 L( n i5 Y% S; X! C+ ~ - % y: c0 D1 {4 o0 l% h
- if (force || subtitle != _subtitle) {
* ~# s: r4 `+ b/ v& T0 |+ ~ - debug(1, "%d: %s", timestamp, subtitle.c_str());0 B9 s8 F+ ~9 f6 d" o f- d
R* ?% Q$ D0 I9 k8 l: u- _subtitle = subtitle;! g$ L% J# ]) K+ y
- renderSubtitle();( v! c7 ~% X4 G( L
- }
9 l) t+ l) y3 g5 ^ - * H, j: V/ m* F. `! c8 v* U$ {
- if (_overlayHasAlpha) {- Y* P1 h- R8 Z1 G
- // When we have alpha, draw the whole surface without thinking it more* n" Q F* t {- ] T a L
- g_system->copyRectToOverlay(_surface->getPixels(), _surface->pitch, _realBBox.left, _realBBox.top, _realBBox.width(), _realBBox.height());
: U: b9 ~0 F' |& w2 F - } else {; h% R; E+ b+ h0 ^$ u, M6 o
- // When overlay doesn't have alpha, showing it hides the underlying game screen u6 \( H0 U/ v* N @6 ^: J9 \
- // We force a copy of the game screen to the overlay by clearing it' _ D" K+ z, q8 g6 {3 B
- // We then draw the smallest possible surface to minimize black rectangle behind text5 V9 d9 D( a/ ?. I, `5 ?0 S5 P
- g_system->clearOverlay();2 D$ k! _1 l2 |
- g_system->copyRectToOverlay((byte *)_surface->getPixels() + _drawRect.top * _surface->pitch + _drawRect.left * _surface->format.bytesPerPixel, _surface->pitch,* A+ P$ ]4 X0 N" E2 D$ v& m8 U8 H
- _realBBox.left + _drawRect.left, _realBBox.top + _drawRect.top, _drawRect.width(), _drawRect.height());' E( a0 ]. Q6 V7 h$ |. B }; I+ ]
- }# c1 E, F; s1 V
- ' o8 O9 k) M- k0 C
- return true;
4 s! W$ a0 r; Y7 E! G& A0 f - }
" d- f- O- l7 Y1 Q7 E0 g
复制代码
/ u; p' a$ C- [3 d: C$ G6 A6 q
- s1 R) |# e! }$ B# i1 H: t# E6 K0 U2 ^
" n2 ?0 {0 g! L) }2 a, o% V
0 x5 P! U( w1 ]1 s( g$ v0 D
" ^/ y e4 d) \# R4 d7 s, c: v' F3 f9 c' O* a
7 i5 {# u1 O7 `4 L3 k5 \
|