本帖最后由 shane007 于 2023-8-12 13:57 编辑 2 D9 ]3 E; J5 F8 i
0 J/ A# T7 j; Q1 `* i2 \6 K& i
这个游戏原本是没有字幕的,但是在Scummvm论坛上发现了一个帖子,* W1 o) s- w+ V: s
讨论的内容是关于在原本没有字幕的游戏上加上字幕。(最初的缘由,是为了帮助一位失聪的朋友)
9 g$ [) v& g- S# |2 I$ ?https://forums.scummvm.org/viewtopic.php?t=7168
7 Z, V( Z- A& j' U/ y* z" u+ p[Scummvm-devel] Displaying subtitles in voice-only games (narkive.com)% z3 F4 l$ w9 w) ?0 |/ v7 d
5 k; Q, w* k8 ]/ P! U. e前段时间第七访客出了25周年纪念版(The 7th Guest 25th Anniversary Edition。
8 b( C5 w# @% i5 k0 U7 z# `- X在这个版本中,游戏的视频是有字幕的,因此把这个视频的字幕用到Scummvm中就成为了可能。
5 {" m+ |+ o% h6 r/ y; ~0 z+ \: P& D) o3 p- }/ x2 J
我查看了一下最新版本的Scummvm的代码,发现老外已经加入了一部分代码。
/ s! e0 o' Z+ `0 d在此基础上,汉化是大有希望的。此种汉化方法,也是前所未有的,如果真的实现了将会有重要意义。" x* x) S y, _5 y0 {2 B
b0 ]/ o8 a9 \% ~5 _
类似游戏还有- · Groovie0 m1 E1 | C/ o& N! z( E5 `
- - The 7th Guest% b' ?5 I( c$ w; X; W9 ~
- - The 11th Hour
+ g/ ~; m& H, L: M - 5 n& [2 h9 x$ x8 U
- · MADE7 w# I5 o. S6 L% a3 x; o+ U
- - Return to Zork (descriptions are text only, while spoken dialogs lack
' `7 k0 B: m2 D; r( {8 h# O - any text)
# f$ T& r; J# m. `6 R' r
# \1 t' H. P; \, Q$ G- · SCI 1
; g/ {. C* Y" H# F - - King's Quest V (CD Version). In this case, just enabling voice and text
7 R; m5 @4 Z1 Q h - simultanously would allow a 3rd party to create a set of MSG files5 U0 Z5 J3 y+ k( `1 a# X& Q9 j
- containing the voice transcription/translation, as it happened with Gabriel
4 O; s0 Z3 N* D# q9 G# f6 t7 W - Knight II.
' p1 ^- V6 }! F - 9 \9 O* X0 r- p. Y" u
- 5 r5 z1 y$ U( F
- Future engines:! Z, i: n' N; U5 a
- 4 { J+ b7 z% L( |! R2 k' ]4 Y
- · SCI 21
2 B( ]* T% S# J D3 A" t+ k5 p1 U - - Gabriel Knight II: The Beast Within (Already a fan patch available in
4 k, S3 ^- v0 z* w' g; _. U2 E - order to enable the text display feature)
8 l6 J9 f! z* r* B - - Phantasmagoria1 e& ?; _- ^* q! y" [3 w* X
- - Rama8 }3 Q7 @ u2 ~3 ?1 u* P
- - Shivers
4 m$ T: N+ _$ o - - Phantasmagoria II: A Puzzle Of Flesh
/ H5 e# ?" ~ c) m4 s# K% ? - : s' c$ h+ P+ D2 n) X/ F
- · SCI 37 q5 v0 O+ {' g
- - Lighthouse: The Dark Being
, k- X8 J3 n# ?( b$ _( l3 X6 H' Y: | x - 6 t3 j4 w, t: \7 o; K
- · M4/MADS1 F/ p9 g: b, [9 S3 b4 f h3 ?3 b3 z4 T
- - Orion Burger6 K. w+ G! M% ]4 i& U! u9 T
4 D( D# W% Y+ B2 `& X- · Mohawk$ N- |% D8 F7 K3 S9 P( O
- - Myst
1 F f2 C0 l8 j0 ?; W - - Riven: The Sequel to Myst
复制代码
n( b+ R3 c$ P
& A- L$ j1 N& B' u: U+ P
; R& H M2 ^" D3 d8 X
0 e, g1 Z5 e9 N9 {相关代码如下
% w, F: ]+ E- T, [# z j- bool VideoPlayer::playFrame() {, Q* X6 h. l: j( H% [% p8 J
- bool end = true;
7 v& F: T. F- B. u; [
! R4 V* u+ z! |+ Z9 y- // Process the next frame while the file is open
6 Z/ N$ q9 H% o) f6 t, q5 s - if (_file) {9 v3 }1 A8 }: D" E6 [4 t2 A
- end = playFrameInternal();
( C5 I8 s( p2 c
( j7 M) ^* G& g) `2 ~$ l: ^1 q- _subtitles.drawSubtitle(_lastFrameTime - _startTime);
0 s) h) T+ M+ t, x& t - }
复制代码- bool Subtitles::drawSubtitle(uint32 timestamp, bool force) {
# z. c: ~8 W% I6 Z* V0 _$ Z4 R - Common::String subtitle;
8 [) }% l; N% U) y# u3 U( e4 b - if (_loaded) {3 G' D2 E2 G7 H; r
- subtitle = _srtParser.getSubtitle(timestamp);
* ?, _/ W5 ?4 g" S$ S7 D& j - } else if (_subtitleDev) {* `# X; Y5 X1 N4 ]+ Y
- subtitle = _fname;! K r: P1 |2 z3 j) C
- uint32 hours, mins, secs, msecs;% I, s% M9 L5 a" a5 ]7 T
- secs = timestamp / 1000;* n: ~+ e2 h3 G, _& W
- hours = secs / 3600;1 b% f8 {2 R7 \5 @# Z
- mins = (secs / 60) % 60;* W; ~( R/ n5 {, Z; c5 N3 L# Z
- secs %= 60;
+ Q4 @% Z, g# S/ k. K& P - msecs = timestamp % 1000;9 d0 V$ Q, y9 _& Y9 F1 o& W% H9 t
- subtitle += " " + Common::String::format("%02u:%02u:%02u,%03u", hours, mins, secs, msecs);% Z- Z4 B* `0 ]5 h# J X) G
- } else {
- K& g$ h9 R+ O6 M3 W) e, V8 P - return false;2 X) U4 `2 e; s) r/ u
- }- |' u* A% R+ W3 e5 N" x4 F: @ A
3 C; A* b' o1 F4 ^- int16 width = g_system->getOverlayWidth(),% v- S5 r8 q* }# |2 L6 i
- height = g_system->getOverlayHeight();) z8 _! `6 e) }$ O. t
$ s" \1 Y7 H5 H& T1 p) @- E- if (width != _lastOverlayWidth ||
0 `: H9 c; ]8 a+ V& S# v - height != _lastOverlayHeight) {+ }* S* K$ Z7 l" |+ ^) P
- _lastOverlayWidth = width;4 W! ]4 K# F7 m) I% Q. }
- _lastOverlayHeight = height;
) M. l' u* @8 h - 3 o+ {/ o* J: h/ X
- // Recalculate the real bounding box to use2 p5 X- s! I& Q/ A7 I7 S1 |
- _realBBox = _requestedBBox;+ U4 A6 i( F8 ]
; j, u. Z1 E% k, `: W; L+ |- if (_realBBox.bottom > height) {' w# O6 V. a @. Y2 Z% ]) ?6 Z
- // First try to move the bounding box
5 r5 O5 P- ?- e9 c! }3 z4 r - _realBBox.top -= _realBBox.bottom - height;8 Z4 ~; r. u: X- P0 b
- _realBBox.bottom = height;+ @% Z( E7 g' G2 W7 L) Y
- }
* D7 ]1 ` ?" g0 g8 s$ z - if (_realBBox.top < 0) {1 M k* U+ V. O# o. a2 R: k! M2 g0 v
- // Not enough space
) T& `* }) A) j) `3 {8 ~ x# O/ x - _realBBox.top = 0;# s( \; Y0 {4 h1 O. R" V8 |
- }: i, j" y2 t, O. n& F
9 c) J& t% N$ W! r& D- if (_realBBox.right > width) {' o% s+ |4 w: p2 K
- // First try to move the bounding box
1 `8 q7 D% q7 ~. G5 ?9 E - _realBBox.left -= _realBBox.right - width;
" ^- M* Q: ~# O. R5 ?' N* ] - _realBBox.right = width;
2 |2 k% m/ P/ i. R/ G - }% [( p8 I. Y5 p d, N6 a% Q3 O
- if (_realBBox.left < 0) {
/ r( ]! R& E) }; j - // Not enough space2 J, a+ s+ I0 b, M" ]$ K$ R( E
- _realBBox.left = 0;/ ` e0 D' e' h' B. ~9 \
- }* o5 A% b b, V. T$ Q) S
- % q9 ~* x ^" G+ E; F' t6 w$ G! F
- force = true;
) V2 f- F6 K: E# _+ o$ x% |0 I - }1 |6 I w3 ?+ |+ i3 v/ K2 e) ^
- ; C+ S h- V: x3 C
- if (!force && _overlayHasAlpha && subtitle == _subtitle)
" _' a& u! V, x/ K0 n' B - return false;+ E( F4 {. `$ g: l1 h5 N
8 g; e) F' z8 I0 d- if (force || subtitle != _subtitle) {
# D! M/ N2 B: r0 Y! t/ Q - debug(1, "%d: %s", timestamp, subtitle.c_str());
# f) ^4 K0 u$ {' _ - ) B) q- [$ p) G, c
- _subtitle = subtitle;
" [/ i+ G5 T- u8 G - renderSubtitle();' v, U& r2 Q+ P$ b: `
- }; v' J; U6 d9 T
- / G7 G% n) B$ K- Z
- if (_overlayHasAlpha) {$ a/ x' t: e# e
- // When we have alpha, draw the whole surface without thinking it more
) c5 z9 P) g! y' r) F* W! A$ H# ] - g_system->copyRectToOverlay(_surface->getPixels(), _surface->pitch, _realBBox.left, _realBBox.top, _realBBox.width(), _realBBox.height());
' ]1 g0 E9 l, d7 v - } else {2 W* z3 _) h8 y
- // When overlay doesn't have alpha, showing it hides the underlying game screen/ X/ y. w8 E5 H7 o/ [$ r9 h
- // We force a copy of the game screen to the overlay by clearing it
: j6 Z4 H" u7 q9 \6 g - // We then draw the smallest possible surface to minimize black rectangle behind text M8 ?$ P, J, ?5 P
- g_system->clearOverlay();) ]$ R& ^$ S: @* r& F5 Z q: o
- g_system->copyRectToOverlay((byte *)_surface->getPixels() + _drawRect.top * _surface->pitch + _drawRect.left * _surface->format.bytesPerPixel, _surface->pitch,8 `6 w: v" k! E
- _realBBox.left + _drawRect.left, _realBBox.top + _drawRect.top, _drawRect.width(), _drawRect.height());; y; k) _) g" d* ?* j0 G2 o. P* X
- }
' h) ~ Z# _5 P) m `( ?4 [. H, {5 @ - * a" C/ y1 z0 ^/ x- n+ t* u
- return true;0 s. E4 a, w3 Y7 [8 b% ?, ]3 @
- }& x: E; C+ G0 v' l
复制代码
9 h8 A# L+ J; X( \0 ]4 K& e8 b) S# ^) A+ d
& O5 B7 R% U6 x$ p1 |% O, ]
6 Z0 R' \& S3 T5 B5 s
! G# B9 k( Z _: ]: p' b
8 d- ]7 H5 [! G" V) C1 f0 G) }4 ? p# N0 j6 D3 ^1 p6 [& a& ^, l. |
|