本帖最后由 shane007 于 2023-8-12 13:57 编辑 & c9 a. Y5 \! l# ?8 H! D3 x' u
( e, i3 o3 I2 q* O" m, A
这个游戏原本是没有字幕的,但是在Scummvm论坛上发现了一个帖子,
% G/ |- U9 |8 j讨论的内容是关于在原本没有字幕的游戏上加上字幕。(最初的缘由,是为了帮助一位失聪的朋友)
( ?& V9 y' ?9 L! L% Rhttps://forums.scummvm.org/viewtopic.php?t=7168
- Z; e8 `% k5 {2 O/ z6 e- u3 V[Scummvm-devel] Displaying subtitles in voice-only games (narkive.com)
5 i! Z* ]3 N$ ?* v4 Y. q$ s
b4 n. z" d. W4 v1 }4 D6 e前段时间第七访客出了25周年纪念版(The 7th Guest 25th Anniversary Edition。
y. x/ S5 ~9 Y0 R9 G8 C' m9 ?在这个版本中,游戏的视频是有字幕的,因此把这个视频的字幕用到Scummvm中就成为了可能。9 b; O/ S1 e, [ j4 @# ^5 M
* @2 m' e& g& ?
我查看了一下最新版本的Scummvm的代码,发现老外已经加入了一部分代码。/ ^& V/ K( O9 D2 s- u3 I* O
在此基础上,汉化是大有希望的。此种汉化方法,也是前所未有的,如果真的实现了将会有重要意义。
% e! m8 w0 u5 l7 c7 p* Y* i: ~ S) d: A2 K2 S
类似游戏还有- · Groovie
- Y) _% n% w0 V( {# ?; Z- c - - The 7th Guest
# B/ B) x1 L6 g; S3 x, d1 P - - The 11th Hour
$ [5 v+ T# v, ]7 A
% A4 p* h# t6 u/ ^) y- O- · MADE
6 p! d1 b$ E+ E( ~7 U9 X2 b, G; i - - Return to Zork (descriptions are text only, while spoken dialogs lack% M) V H. J. `( q0 y: f
- any text)1 P _' _$ L5 B7 A4 h; m
- 2 }% P$ K' C7 G2 z* a2 V ~
- · SCI 1- P) P! p8 g7 \/ w# z
- - King's Quest V (CD Version). In this case, just enabling voice and text6 a3 e6 _- H/ g c
- simultanously would allow a 3rd party to create a set of MSG files
( @* w8 R6 L* [: x6 O7 o - containing the voice transcription/translation, as it happened with Gabriel
1 A. C5 w- L( e - Knight II.
" U7 o& Z8 z) _4 z! X' J* e: l# K - 3 S; N8 N" M3 ^$ @& L$ `; i; E) Q, C
- S2 c! h$ P) i5 m2 Z; X
- Future engines:
; W! I* Z2 n4 i; O4 e - # E% i$ _* K0 _1 i( k
- · SCI 21" d9 s F J9 m& }# J; _" {6 r( R
- - Gabriel Knight II: The Beast Within (Already a fan patch available in2 v8 Z2 E1 \) i) R$ t4 f! G
- order to enable the text display feature); t+ U9 @4 _! b4 A; O3 {
- - Phantasmagoria
3 U# P( O2 R' F4 Z, ~, x - - Rama/ a& B% Y4 t, A! c4 ]6 N5 J2 }; S
- - Shivers
( L# z9 H7 {+ P- K8 W) \' }: O - - Phantasmagoria II: A Puzzle Of Flesh8 B7 p& P' j' _( c0 t% p
- " j3 u! Y5 g) h n2 |1 \
- · SCI 33 m2 K7 h9 G0 N2 _, j) m3 U) e
- - Lighthouse: The Dark Being9 d% T7 L' X9 l6 I9 n& _
- / D' Z& A5 c4 ^$ K* a
- · M4/MADS
7 b! i7 j* r2 t u# ~- \4 x5 p - - Orion Burger
* m/ R5 b5 l+ o8 E0 v0 y! }
5 R2 ` Q$ ~1 [- · Mohawk
. i: e7 A7 b4 y0 ?+ m3 E8 w - - Myst
2 z3 T0 t5 g/ \2 v - - Riven: The Sequel to Myst
复制代码 z4 c" u3 m5 `2 [; s/ x
& j4 f' q, i8 v/ o. i" F) U* z1 ^
8 ^+ r( y: M1 s8 _% @" I
2 o# H" Z' y* a% o3 U* ~3 i相关代码如下
9 j: T% I J2 a- bool VideoPlayer::playFrame() {
2 Y% Y) H" r+ e+ \) Z* z. t - bool end = true;" z# N5 I/ _+ Z3 W9 z9 F
# M3 G3 U8 s0 V7 D% Z7 L( x5 V- // Process the next frame while the file is open0 {# D3 l' V; o, K u
- if (_file) {) P6 _) ^! b4 O) M3 Q) t
- end = playFrameInternal();
, l* B& E2 c$ S, w6 X( p - 6 _6 U! f( U s' ]" j
- _subtitles.drawSubtitle(_lastFrameTime - _startTime);
' c8 U$ @$ @% O - }
复制代码- bool Subtitles::drawSubtitle(uint32 timestamp, bool force) {
/ c+ x6 B; s9 j3 t - Common::String subtitle;" ^# l2 A9 q: F2 w
- if (_loaded) {5 ?: M$ F1 b. p6 R! I
- subtitle = _srtParser.getSubtitle(timestamp);/ I; M: b9 X$ Q+ X0 S: |
- } else if (_subtitleDev) {$ }% B; [- `" m3 S4 V4 u6 i
- subtitle = _fname;- V4 T& z$ d# X1 k: r0 Z% E t
- uint32 hours, mins, secs, msecs;
3 I7 p3 p L' x - secs = timestamp / 1000;
, r. ]* D( h% V+ i9 Y2 H$ Y - hours = secs / 3600;7 K3 ?1 E; A+ k! c$ A5 M0 J* N
- mins = (secs / 60) % 60;4 L5 ~/ j4 j; F8 T' n5 o5 b
- secs %= 60;
; O4 a8 \2 p9 t$ h% G - msecs = timestamp % 1000;
2 z. Q, D- s( U - subtitle += " " + Common::String::format("%02u:%02u:%02u,%03u", hours, mins, secs, msecs);6 \. N$ i" i1 V" ^* r2 T
- } else {
% m, ~& ~! g# a+ D. M. I2 L5 F - return false;' p8 {( f, b, f& Q% L
- }
4 P* D9 O% f9 g9 l+ |. [0 n - 8 f9 k8 b6 `: t( m4 Y* `
- int16 width = g_system->getOverlayWidth(),
6 G1 `2 O) ]# w5 B! J2 q! K - height = g_system->getOverlayHeight();% N. r7 C7 M9 H; z# [+ D
- " l# ^) {8 I2 m8 C0 @% T* M
- if (width != _lastOverlayWidth ||8 {) |9 ~' x* P( ^6 g w% n% \1 a
- height != _lastOverlayHeight) {
0 M7 B5 [+ y# i8 ~1 Q - _lastOverlayWidth = width;
6 t p3 L9 S+ g7 l w9 { h - _lastOverlayHeight = height;
% r& B* x; J( m9 i - / z% J k% e9 w6 Z5 G$ o4 x- }
- // Recalculate the real bounding box to use7 B4 H7 E* Z% E# z
- _realBBox = _requestedBBox;/ |$ L* o7 F& y8 t/ Q0 [9 L1 ^; j
+ I& H* ]9 m! @- S! w7 Q$ t- if (_realBBox.bottom > height) {
: O! u0 }" }; \* }6 p4 y4 h2 y/ S - // First try to move the bounding box0 P$ W6 g `/ k/ ]) P0 l
- _realBBox.top -= _realBBox.bottom - height;1 D% z! Z2 {, h$ ~2 L) E/ }' j4 a- s4 X
- _realBBox.bottom = height;
! p1 o4 n7 |! K% g8 C - }
# ^8 c' O( `$ P# u - if (_realBBox.top < 0) {2 c3 s; J! m+ Z* C: b2 T; ?- ]0 p
- // Not enough space
1 n6 m- |$ }, O1 v2 Q4 O - _realBBox.top = 0;- ~# [5 C# _; l7 U1 u6 B- _. U
- }0 ?2 X2 e$ m* K/ O1 B) n
- / A) @8 G+ j4 T( ?4 ~
- if (_realBBox.right > width) {8 g- S/ g0 n/ T* o* |% O. J
- // First try to move the bounding box- P9 t1 ~5 }6 K7 K# e9 z2 U* v
- _realBBox.left -= _realBBox.right - width;1 R( E- [( o' i9 Z' z6 D( |
- _realBBox.right = width;, g+ x- A- L# ^; ~: ]* d0 Z6 H
- }, \+ W& b% c8 J r2 Q0 \- ]) I' w
- if (_realBBox.left < 0) {
% V& Q C/ s/ v( ] - // Not enough space
5 A: u( d( A* y1 T# N, S W2 W- i - _realBBox.left = 0;8 O9 ?7 t2 W, s2 e C) Z/ r
- }' S+ w( m! }3 Y$ |5 R% V/ Y
- ( A* u2 A0 r0 C* Z% Y. b3 X5 y
- force = true;
1 `/ g. R! b. C$ r- J4 X- c0 f/ | - }, h8 M0 K2 I. J
- , m8 R3 [: ^$ r! u5 t6 g( j
- if (!force && _overlayHasAlpha && subtitle == _subtitle)" H: f0 Z- w/ S: r& k, p
- return false;# U! ?8 p. {( d" x6 {; D" P7 S
- : ]! w$ h$ i4 o3 ?: \# c* q
- if (force || subtitle != _subtitle) {: k& k ]2 y% d; n1 ]
- debug(1, "%d: %s", timestamp, subtitle.c_str());
3 P |! l8 W: V$ I8 P* ~8 |! r
+ ~$ e/ h f, H/ Z% f! c7 ^% W- _subtitle = subtitle;- S& }# g0 q! D! d6 g) W6 w& p
- renderSubtitle();
! P( C$ Y: W7 p( ]: C$ j6 Y, r - }" S: p. p) {5 R3 e5 C$ }6 B
7 E# |4 S6 b @2 V9 A4 I- if (_overlayHasAlpha) {
# m* G4 L$ X" g - // When we have alpha, draw the whole surface without thinking it more/ t5 C# E+ a' L; ]3 M
- g_system->copyRectToOverlay(_surface->getPixels(), _surface->pitch, _realBBox.left, _realBBox.top, _realBBox.width(), _realBBox.height());0 @- i8 r r/ b5 g7 u$ X* G
- } else {
( B6 o( z9 a0 _. z. _' [ - // When overlay doesn't have alpha, showing it hides the underlying game screen
$ V c( a* E* k# G/ |' T - // We force a copy of the game screen to the overlay by clearing it5 A R6 ^! A' h/ a8 E1 {4 |/ L
- // We then draw the smallest possible surface to minimize black rectangle behind text
% Q" L+ m) ~( Y( [& t9 z* D! Q - g_system->clearOverlay();
# O# j6 ^2 v( a" ]. o) f7 q - g_system->copyRectToOverlay((byte *)_surface->getPixels() + _drawRect.top * _surface->pitch + _drawRect.left * _surface->format.bytesPerPixel, _surface->pitch,, w h6 x- t# Z0 \* y' I
- _realBBox.left + _drawRect.left, _realBBox.top + _drawRect.top, _drawRect.width(), _drawRect.height());
# L; e7 W- M, K/ K [ - } L# V1 F; @) n) d* D
- " v' E& \: r$ y" I- r+ M
- return true;5 k t. o: j( p' m: Y) R1 r
- }3 d0 S( R! X6 F: D
复制代码
" a# ~+ `. w" O4 Q
- d$ T- b* N% J1 M5 ~3 C" |, {, D" T6 T$ X6 S0 r
, R4 Y* t# L" }( h/ [% g. d4 d( B5 t. M! L' R: K6 ?1 n# U
; x9 t- D/ m9 \. f7 A9 F
1 ]) d4 D& h, s2 r) k/ @0 Y! T |