本帖最后由 shane007 于 2023-9-2 00:06 编辑 3 a5 y. P/ w4 N
% \ e) d8 ^; V4 a' `6 H$ z" ~7 ~3 g
该游戏是scummvm支持的少数几款FMV AVG。
& e$ T# y" l/ x0 m! ]6 x, P3 v5 S2 w视频采用了一种叫做RL2的格式。/ t' w7 z4 F( W$ r
参考以下格式和Scummvm中的代码,可以想办法将RL2中的raw data部分转换为wav,
% T& L' o! e" R( U7 Z {然后用whisper语音识别之后就能配上字幕了。4 o" K# k. P$ ~7 s6 \, i/ n. ^
此外,rl2格式用potplayer也能直接播放。
2 Q# o% }( E L* Y+ d3 ~2 m# U! u. a8 S& k! {
文件格式3 }/ P5 G9 A9 _$ {6 C* @% S
https://wiki.multimedia.cx/index.php/RL2; |1 F2 O. o/ L+ f; \
- 4 w- d/ r( l- |4 o* L
- + 0 dword Header -- "FORM"+ A( p h ]% ?6 J! }( ]
- + 4 dword BackSize -- size of the background frame
9 h8 [2 r- O1 h/ @9 M - + 8 dword Signature -- "RLV2" or "RLV3"
: t( o/ h) Z( x. |: s \8 ^ - + C dword DataSize -- size of the data past this point BIG-ENDIAN y2 W1 l8 [# G$ I% A. L# ^
- + 10 dword NumFrames -- number of frames
& Q) }, k2 h g+ y* p - + 14 word Method -- encoding method. ignored, zero1 x' R S+ S& C1 [1 h
- + 16 word SoundRate -- sound sample rate in some obscure format. zero means no sound
$ H& H" u f) ~& w. w, X" k - + 18 word Rate -- sound sample rate in Hz
* U3 S& Y, a, C- q! o, i* m - + 1A word Channels -- number of sound channels* d, Q$ _$ l1 e) F
- + 1C word DefSoundSize -- size of the single sound chunk. see notes below+ }0 ^. X" X8 m
- + 1E word VideoBase -- initial drawing offset within 320x200 viewport
/ O! L6 v& Y( S - + 20 dword ClrCount -- number of used colors
1 `- H7 ^! _! D Z d$ T - + 24 RGB Palette[256] -- 256 RGB triplets. full 8-bit entries
9 N* N; V Q/ G; c( w0 W - -- if Signature == "RLV3" AND BackSize <> 0- M+ D' Q# O( ]+ e r) }* P T
- +324 byte BackFrame[BackSize] -- encoded background frame. ignore VideoBase during it's decoding2 V& [" O' o, _3 Y ?
- --
5 z2 l' u G5 Y" b - +xxx dword ChunkSize[NumFrames] -- complete size of the chunk for each frame (audio+video)
. O, Z# Q! s) U+ e: B0 i: K - +yyy dword ChunkOffs[NumFrames] -- offset of the each frame chunk from the start of file8 {0 j" r$ H0 r6 w; s
- +zzz dword SoundSize[NumFrames] -- size of the audio portion in the frame chunk- b% D" W, x5 R; d
- -- for each frame --$ ]/ X m( ?5 s6 _1 X4 J
- +xxx byte Audio[SoundSize[frame_nr] & 0xFFFF] -- raw 8-bit audio
8 @% o3 y5 A$ h7 o8 H8 h8 ?& n - +yyy byte Video[...] -- compressed video stream
复制代码 / Q+ S. y+ y' D2 X
参考代码(有问题,但可参考)- using System;! B4 x {/ L* n' g
- using System.IO;) _& J1 j, ~7 V1 D6 G! u6 `4 @
- using System.Text;
* h; t. y. C6 ]
% N+ ?* [5 u% n8 }9 a& s7 C0 H9 P- public class RL2ToWavConverter
8 a# l# L8 Y9 [- D - {& ^+ M& |7 `6 y/ i: A# Z8 }
- public static void ConvertToWav(string inputFile, string outputFile)
5 {* V3 `2 J L, @+ G5 f @ - {
9 F8 y2 h# `+ H- M: ?7 [( |; r$ Z - using (FileStream fs = new FileStream(inputFile, FileMode.Open, FileAccess.Read))" U5 V2 R5 p% `: B! h
- using (BinaryReader reader = new BinaryReader(fs))
7 l2 @0 S2 T4 O, t - {
4 w6 c4 w! _' b( c - // 读取头部
* j. p. i% e- [ - string header = Encoding.ASCII.GetString(reader.ReadBytes(4));1 h2 Q$ P' n' M5 Q, m8 i4 I! n
- if (header != "FORM")3 c. ` D& U$ q6 k6 _+ Y
- {
3 d, U% u. a3 o" l - Console.WriteLine("无效的.rl2文件格式。");
+ n3 Q: X+ v+ Y y - return;
6 O w' ^+ s' @: N9 u7 J - }
7 h h# _6 o/ J - ! \: j1 E! b; a3 v
- uint backSize = reader.ReadUInt32();5 Q4 V8 [) n* I) i+ I# q0 w
- string signature = Encoding.ASCII.GetString(reader.ReadBytes(4));
- P8 v2 J% u" m- T- N& f' e - uint dataSize = reader.ReadUInt32();, I, Z g9 @0 d
- uint numFrames = reader.ReadUInt32();8 G8 r" \9 L/ i) f
- ushort method = reader.ReadUInt16();
2 M& w. A: m( B9 H - ushort soundRate = reader.ReadUInt16();: m4 s+ R( s5 B. e# W9 Y
- ushort rate = reader.ReadUInt16();
* o0 V. _* m! C' F& q - ushort channels = reader.ReadUInt16();- w: E5 F9 I/ t Y/ O! p+ ^0 O# s
- ushort defSoundSize = reader.ReadUInt16();
# A# [/ i& n" V/ C; A- ^3 _* M - ushort videoBase = reader.ReadUInt16();5 ?; M* X, V9 T- B% f
- uint clrCount = reader.ReadUInt32();
3 ]) u7 {& t: d9 S - uint[] chunkSize = new uint[numFrames];
' n9 m2 T8 H4 N$ ` - uint[] chunkOffs = new uint[numFrames];' b( I X9 y4 T3 S% W% D
- uint[] soundSize = new uint[numFrames];! U+ z4 b# q+ G6 \
: p, Y8 u7 _. I* p. M+ h- if (signature != "RLV2" && signature != "RLV3")- s; W# l2 _4 }2 c: ]
- {
5 r; g6 _4 e. F/ }- U) t - Console.WriteLine("不支持的签名。");9 n9 X) K. M# J q8 o) ?9 d3 w" ^! e7 x
- return;
! [. b- U3 Q. m1 ]9 O2 p1 | - }
( h/ N- \4 f: e6 h! r a
5 U; M& t- p' x& i- // 读取块信息
) I. M7 G* A i2 Z0 L; ~ - for (int i = 0; i < numFrames; i++)8 W" f! S8 Y; k6 G9 F
- {
3 _: _' `8 }7 f4 p; X - chunkSize[i] = reader.ReadUInt32();
' p2 j" `' U; K1 l! q; R - chunkOffs[i] = reader.ReadUInt32();0 z3 h( @; W+ w
- soundSize[i] = reader.ReadUInt32();
) g0 }! `& v, e - }0 @4 j6 t- _8 W6 @
- * |5 h& b R$ j9 U
- // 如果存在背景帧,请跳过它, g: m8 S# r* ^+ [5 Z) `( n. t( n
- if (signature == "RLV3" && backSize != 0)
$ K3 v, h* X: \ - {: R# m; o6 |3 [! }) p5 c- c( O
- reader.BaseStream.Seek(backSize, SeekOrigin.Current);8 K- Y. H& z# M3 B! F o1 q
- }
7 Q' T8 J/ I* m - : l+ {% x+ j L K' ]3 n# W
- // 创建一个WAV文件并写入音频数据
$ Z' O, \. s! } - using (BinaryWriter wavWriter = new BinaryWriter(File.Open(outputFile, FileMode.Create)))
( L7 M7 M. |$ I9 D+ A. k$ i# O. c+ A - {8 ]6 v- h& X! t+ |/ W6 ]9 g( f- ?- m
- // 写入WAV头部/ k4 W8 L0 I1 U4 P( b' P' _ q
- wavWriter.Write(Encoding.ASCII.GetBytes("RIFF"));
6 [2 Z" a1 f6 V2 K! n- {; F( K - wavWriter.Write(36 + dataSize); // 总文件大小 - 8
+ ]2 g i- \" C8 b! e7 l - wavWriter.Write(Encoding.ASCII.GetBytes("WAVE"));+ W6 _9 P* V4 ?5 F' c
- wavWriter.Write(Encoding.ASCII.GetBytes("fmt ")); F9 \7 H; w ^/ p5 ?
- wavWriter.Write(16); // fmt块大小$ i5 x9 a+ x$ H! J# W2 Y5 T. m
- wavWriter.Write((ushort)1); // 音频格式(PCM)7 E( _; Q8 y" t3 m2 y# ?
- wavWriter.Write(channels); // 声道数7 b3 u/ o9 M! \( A& ?4 I3 I
- wavWriter.Write(rate); // 采样率
0 n: h) j) b0 n7 J/ J - wavWriter.Write(rate * channels * defSoundSize / 8); // 每秒字节数5 u G7 u1 {: |/ j1 X/ m% @0 r
- wavWriter.Write((ushort)(channels * defSoundSize / 8)); // 每个采样点字节数
% @& u4 y8 f. { - wavWriter.Write(defSoundSize); // 每个样本的位深度# W5 |" |0 J. G7 _8 Y7 E
- wavWriter.Write(Encoding.ASCII.GetBytes("data"));
. e4 O: M5 e: T2 T) Q - wavWriter.Write(dataSize); // 数据大小& p3 b1 O, U6 O' Q* {0 i. a' k
6 b, @, }6 I7 w' g2 I- // 从.rl2文件中读取并写入PCM音频数据
5 q1 Q( }6 U) g, _+ A - for (int i = 0; i < numFrames; i++)
4 D& h/ O8 I+ T- B1 X - {
3 V% m7 ?, L" Y - byte[] audioData = reader.ReadBytes((int)soundSize[i]);. E& ~+ G1 _0 }9 P
- wavWriter.Write(audioData);
' e: A# u1 F! } - }
; i! E# J4 `+ k - }
7 F7 ~. c- }- K8 R: L o Y - }
, Z: I1 V! l% V - }
% h3 L1 ?' V6 P# o0 f+ L! t8 x' k - }: y. U2 \2 ~! G3 i3 K% C
- / V- y8 p4 A8 |" ~9 l
- class Program9 c/ `8 Q) b2 c/ ?5 {" g
- {/ X( I, x: j+ d! o5 a6 V" {7 q
- static void Main(string[] args)
! h6 e9 e4 d' i, n- ], v* t - {
! D% y2 r5 D- e9 v - string inputFile = "N1275510.RL2";9 o5 Z* }+ \2 g1 U4 s3 B# C" ]
- string outputFile = "output.wav";1 s3 X6 C1 H/ D6 i0 p
- RL2ToWavConverter.ConvertToWav(inputFile, outputFile);3 y9 m1 r# E9 u8 @2 y9 R
- Console.WriteLine("转换完成。");+ ]/ t$ V( Z) o. S% B. w: i# _6 P
- }
3 o! E& C5 ~9 _9 m9 @3 \ - }
3 @; t& t3 y: P* f. u" }
复制代码
1 ?' p9 Z9 Y* c8 V9 t1 N, U0 g9 \8 l; B
$ |- C3 D$ b; B9 I' |
|