本帖最后由 shane007 于 2023-8-12 13:57 编辑
* r$ i( T$ U# o# O$ I, z: e4 o& `9 S$ u# E- ?8 W; `
这个游戏原本是没有字幕的,但是在Scummvm论坛上发现了一个帖子,7 I( ^5 \. e6 Y8 Z
讨论的内容是关于在原本没有字幕的游戏上加上字幕。(最初的缘由,是为了帮助一位失聪的朋友), B* f( T4 ]: n- _4 r
https://forums.scummvm.org/viewtopic.php?t=7168& { @1 N7 }% D+ ~
[Scummvm-devel] Displaying subtitles in voice-only games (narkive.com)& o0 ^6 L* j: o) q4 T
% h. V `0 f+ ^6 a) ~& B! c
前段时间第七访客出了25周年纪念版(The 7th Guest 25th Anniversary Edition。
+ u" x0 C0 [0 g V在这个版本中,游戏的视频是有字幕的,因此把这个视频的字幕用到Scummvm中就成为了可能。
1 b" @ l7 e8 d, s! Y* ^% f" Q* t- _- V# m% H, m( k/ H0 W
我查看了一下最新版本的Scummvm的代码,发现老外已经加入了一部分代码。
+ v$ R1 C9 e9 C. z9 W5 S2 u在此基础上,汉化是大有希望的。此种汉化方法,也是前所未有的,如果真的实现了将会有重要意义。
/ m6 E2 ~! m, J8 L$ e* j% \# _
* q1 p+ Z% k/ R* U( p类似游戏还有- · Groovie/ y" w: q+ R$ `8 i: O
- - The 7th Guest- k* }8 T6 `! N9 s1 `3 \
- - The 11th Hour
+ y, _7 w0 A! ~0 D8 w$ N. |
. g3 K3 t* T4 k. g/ {0 U- · MADE
5 _$ S7 H8 r/ X7 a1 G - - Return to Zork (descriptions are text only, while spoken dialogs lack3 g0 x. z9 K" W7 V# B) Q8 |
- any text)) k( O6 R: k( G `# e
- 2 \/ D# ^7 Z; x. N8 S
- · SCI 1: o! i5 w; a0 R3 L2 p% h
- - King's Quest V (CD Version). In this case, just enabling voice and text0 I* ~& f2 `& L7 G
- simultanously would allow a 3rd party to create a set of MSG files
; k1 Q' [$ S* ^0 j4 ]* O9 k - containing the voice transcription/translation, as it happened with Gabriel
2 Q. Q O* m5 } - Knight II.+ C* M1 g8 H" U$ }. d6 E; z
! V: V. d U" W2 ?4 {
4 v9 M( Z2 c, ~6 a; U+ F3 I3 H- Future engines:! O6 [, G( b+ z5 k6 n$ I: O4 ^
- 5 r( J- u9 D* Q2 x3 z/ K9 n
- · SCI 213 q$ E8 d* p+ w8 h4 o
- - Gabriel Knight II: The Beast Within (Already a fan patch available in
& B$ Y5 X- r- |3 V) ]% H& H - order to enable the text display feature)5 j8 r8 N) G/ _. n7 s5 j
- - Phantasmagoria
' ?( Q' D3 h/ \, l* e - - Rama
( P1 J+ `) e# b( q/ f! i - - Shivers
" Z0 Z: N Z$ l - - Phantasmagoria II: A Puzzle Of Flesh
' k! h/ o% w& Y2 K |, ` - ( E6 X% M/ o1 U' a: \
- · SCI 3. w! q3 [8 k! q5 z+ z1 I* F
- - Lighthouse: The Dark Being q) O( b5 B+ V/ d, v% k5 c
- 1 s( T8 h) s) i8 }1 N9 D8 [4 \
- · M4/MADS
" q: y4 E \+ O - - Orion Burger. [& |7 t" [. h+ k3 E
. k8 n8 u% v7 W- · Mohawk
) S R" }/ S% G! J0 W2 e0 G - - Myst/ C. V; N8 m3 e) W8 `1 U
- - Riven: The Sequel to Myst
复制代码
6 Y* q$ I* z6 C) V1 `# L6 R& G. D
( l2 x: F' N: G* ?4 A" [8 V3 a. W5 T% n& |, B4 B* l' ?
* `: V9 |6 A: h( g8 b4 Q6 i# f相关代码如下
- C" [6 K& ^( B% ], c7 f/ e- bool VideoPlayer::playFrame() {
) A% a% O( P* x$ `# V: v# _) j; R - bool end = true;( Q$ o2 Q" `' `
- ) B; y, P& t7 v/ k& ~$ U5 G: x
- // Process the next frame while the file is open
: Y7 ^9 j5 u: n1 ~3 N - if (_file) {
/ g) H( `' O+ Y( @* [ - end = playFrameInternal();
* b' W& `, ] b. X9 u3 _
5 I6 D0 b& [, e- r; |! n- _subtitles.drawSubtitle(_lastFrameTime - _startTime);# J2 D7 w6 l1 f
- }
复制代码- bool Subtitles::drawSubtitle(uint32 timestamp, bool force) {6 z2 l& b7 q' _
- Common::String subtitle;
' p' n z1 L: J/ p; U - if (_loaded) {
- ]( o9 g+ x' `' _8 P; M - subtitle = _srtParser.getSubtitle(timestamp);
$ F+ X# J% e; \/ h, y - } else if (_subtitleDev) {/ T2 b5 V( Q1 p. z0 R
- subtitle = _fname;# @9 H9 D( h0 ^3 h' A
- uint32 hours, mins, secs, msecs;' i& v( S: |: B1 k. E" f' q
- secs = timestamp / 1000;
' X z- T7 v/ ~0 t - hours = secs / 3600;
% F, J& a9 c9 m4 E5 a# Y - mins = (secs / 60) % 60;" q1 I- E/ W! Y) L1 \
- secs %= 60;! f- ~, p G' x7 P
- msecs = timestamp % 1000;
! @8 o' }+ P O- t- y q a( R - subtitle += " " + Common::String::format("%02u:%02u:%02u,%03u", hours, mins, secs, msecs);
- a$ Y# g& ?) v6 M# h5 N0 e+ @0 o - } else {0 a" S/ X$ n/ Y2 I# ^' w: r7 a
- return false;% [2 ?1 z, N$ p( r
- }
, ^! f" D: v8 l! x( ?% U. s8 i# @
5 }8 W+ C7 y3 [! K$ s. w' R- int16 width = g_system->getOverlayWidth(),
& T9 h5 w! s/ n3 g C1 h - height = g_system->getOverlayHeight();
, E1 @" m% b9 w" K* q& v - " c2 {0 [+ o3 J: S! ?8 E" m5 Y
- if (width != _lastOverlayWidth ||* g& W6 h! q) a! _2 s. A$ c$ t* \
- height != _lastOverlayHeight) {! Y* Q4 k; q" a$ _
- _lastOverlayWidth = width;
9 X9 k C5 n7 n7 d/ o - _lastOverlayHeight = height;
. q4 _3 C' v0 B' s* G+ d - ) L4 U" ], s$ N* V+ b; H) J" W' J
- // Recalculate the real bounding box to use9 z; L5 V/ q8 r/ G% q
- _realBBox = _requestedBBox;& C0 t* K4 S$ T# e/ n& V. c) m* ~
- + z! I0 x3 M( K X2 M7 a9 _6 b3 S
- if (_realBBox.bottom > height) {7 {9 I L+ |8 h* B$ x- ^
- // First try to move the bounding box
; Z/ t+ l k& I7 i0 ~; ` - _realBBox.top -= _realBBox.bottom - height;. T* I/ K. K! P4 A0 f3 ?6 d* P8 {
- _realBBox.bottom = height;
. U1 T7 z5 f# G/ H - }
" D q4 t7 ?. B( B, k9 s. Z - if (_realBBox.top < 0) {4 K0 X0 {6 y% |/ g0 g
- // Not enough space; I& U5 f, o8 ]: Q! f2 Y+ E: h& K
- _realBBox.top = 0;
* V2 [0 R6 e, X2 t$ h4 k - }
2 O) S8 v* \. h( [) _ r$ k" z - ' r/ X+ E7 }$ Q
- if (_realBBox.right > width) {2 K* L: c; M* v; D
- // First try to move the bounding box0 I+ T; r! f9 P
- _realBBox.left -= _realBBox.right - width;% D; s" p& Y; @$ I9 F H% h
- _realBBox.right = width;1 }. G! J) W, [
- }) X7 I. a! a% x
- if (_realBBox.left < 0) {
- g$ ]) `% x/ P# G+ w3 {! l h7 @ - // Not enough space
. e5 W. ~, B, h3 w# D - _realBBox.left = 0;
5 c. ]* }) H/ x1 S% T - }
: y, g! ^, F) o/ z" c
/ U, [: L) z/ f1 [- force = true;
Z- H% w3 Y/ e - }
9 S; V5 V4 o/ p& R - 0 B B5 y1 s- O
- if (!force && _overlayHasAlpha && subtitle == _subtitle)
) x/ f# j4 n1 I l - return false;
4 w/ n. ^, ]" ]2 O5 a
: v6 o7 j' O! Q$ ^- if (force || subtitle != _subtitle) {
0 {. I. w, F- d+ c - debug(1, "%d: %s", timestamp, subtitle.c_str());2 y5 v2 ^9 r4 m( k& E. ~
/ ^! j5 Q: z7 {/ J) u. g- _subtitle = subtitle;
# v8 M8 n6 R4 _! W0 T7 {4 f - renderSubtitle();2 W6 ]$ E. z* }9 r0 e$ G9 v+ M( t
- }
& r' x$ F$ o; r: k) x' K; D - 8 T D4 a, F6 t
- if (_overlayHasAlpha) {
( u: d9 A- V+ n( B: @; k& E4 K - // When we have alpha, draw the whole surface without thinking it more2 d' V' a' p, m: c+ _/ A* t
- g_system->copyRectToOverlay(_surface->getPixels(), _surface->pitch, _realBBox.left, _realBBox.top, _realBBox.width(), _realBBox.height());7 p/ H' _) ^' x0 P, ]5 ]- Q
- } else {
/ e$ c& R3 D' ~# e - // When overlay doesn't have alpha, showing it hides the underlying game screen
4 T$ ]& _( d. ^. P2 W - // We force a copy of the game screen to the overlay by clearing it, K {5 k! |2 m' `" \
- // We then draw the smallest possible surface to minimize black rectangle behind text
$ g" O) f) q+ Z9 V9 Y$ w - g_system->clearOverlay();+ u: w4 U5 _3 s6 y2 _& i
- g_system->copyRectToOverlay((byte *)_surface->getPixels() + _drawRect.top * _surface->pitch + _drawRect.left * _surface->format.bytesPerPixel, _surface->pitch,
4 c' v! y: p: K& S7 e: Y3 f' ~ - _realBBox.left + _drawRect.left, _realBBox.top + _drawRect.top, _drawRect.width(), _drawRect.height());' k4 q5 x) _9 V+ S
- }
" B. ]4 [; {. A1 Q4 e
: Y% d2 v; L g9 \" @% W2 s& n' U- return true;+ x0 x- A, f+ t, ?
- }
5 z& `) w9 S5 r- I
复制代码
. {# S% U1 m( r& B. \! C: ^
" J5 F( {4 M( ?
/ F1 p/ n6 f) d' ~7 f* d- b( d* U% ]9 @7 a9 y" B3 y
9 U. s9 u6 i8 y# p) Q1 B% o& r
) \# f3 s6 f- H" [, K- i7 u0 K: l+ @. N; v3 O9 U& e
|