本帖最后由 shane007 于 2023-9-2 00:06 编辑
5 b Y, Z7 g9 m' z: ]
& k2 {6 g# M; V" c3 \4 h9 G: F该游戏是scummvm支持的少数几款FMV AVG。
8 d4 t" h1 r( \ w' P视频采用了一种叫做RL2的格式。5 d. O- E4 T! m5 U& d
参考以下格式和Scummvm中的代码,可以想办法将RL2中的raw data部分转换为wav,
8 {' U k; G2 D q3 T) [* l然后用whisper语音识别之后就能配上字幕了。9 c' t- N1 ^3 b3 t+ O9 u
此外,rl2格式用potplayer也能直接播放。
) j( C' `+ X3 e* ]/ ~: P1 T
( t; u% u3 f1 C. e文件格式
0 n' L4 D* D* l3 Ohttps://wiki.multimedia.cx/index.php/RL27 G$ @7 R+ e5 n& R! o( S- l
0 g/ q& n4 ~& ~% d6 m: h. k- + 0 dword Header -- "FORM"
9 k' _; d+ X% a8 s( D% L - + 4 dword BackSize -- size of the background frame& O' M$ K8 ^" g4 I7 ]6 T/ n
- + 8 dword Signature -- "RLV2" or "RLV3"4 A# @. o9 |$ L. w/ r* k: J5 s5 }
- + C dword DataSize -- size of the data past this point BIG-ENDIAN
' z& i) _: Z8 D; O; ^& M - + 10 dword NumFrames -- number of frames @* {! n. S o+ u2 d
- + 14 word Method -- encoding method. ignored, zero2 Q: F' W" ]' j
- + 16 word SoundRate -- sound sample rate in some obscure format. zero means no sound
; G* D6 U( Y( @ c - + 18 word Rate -- sound sample rate in Hz9 {" Z! U+ f, q4 X
- + 1A word Channels -- number of sound channels
* ?1 M6 e" V# o. U0 X - + 1C word DefSoundSize -- size of the single sound chunk. see notes below
4 k' {; M' q4 t s( r, M - + 1E word VideoBase -- initial drawing offset within 320x200 viewport
( o8 U1 i! E6 a! H9 v% T - + 20 dword ClrCount -- number of used colors
?6 C+ a3 K2 |) f& f - + 24 RGB Palette[256] -- 256 RGB triplets. full 8-bit entries
- b8 U( K+ [: a( x+ u - -- if Signature == "RLV3" AND BackSize <> 0/ s, R; S( i* V4 [, q
- +324 byte BackFrame[BackSize] -- encoded background frame. ignore VideoBase during it's decoding
; M+ E( E3 ~+ O2 A - --, m& ]+ A) S- Y [7 A, k- M0 ]
- +xxx dword ChunkSize[NumFrames] -- complete size of the chunk for each frame (audio+video)
5 e* E* B8 E9 L7 S, \ - +yyy dword ChunkOffs[NumFrames] -- offset of the each frame chunk from the start of file; h* s& O0 w% f7 o& a& ^* c
- +zzz dword SoundSize[NumFrames] -- size of the audio portion in the frame chunk
2 ]* H" I/ c' U7 p9 T4 Q - -- for each frame --
4 S. J% D. C; T1 V c - +xxx byte Audio[SoundSize[frame_nr] & 0xFFFF] -- raw 8-bit audio5 Z Y; }2 s, N
- +yyy byte Video[...] -- compressed video stream
复制代码
7 Q$ L3 m/ c# G参考代码(有问题,但可参考)- using System;
) I( _; N% A& _% L - using System.IO;
9 E" W+ ?% Q4 [. B2 A8 x5 L - using System.Text;
! t. p3 x. J$ X: [; A( B0 T# y
2 @9 ~1 U- U% u9 n: W/ E7 a6 B8 f- public class RL2ToWavConverter# E$ B2 a. N Y0 J1 P3 L
- {7 j& @& N! U/ c% M
- public static void ConvertToWav(string inputFile, string outputFile)7 ?4 q4 j' B8 ]6 l
- {
. r9 Y; {, D% U% E" `, K - using (FileStream fs = new FileStream(inputFile, FileMode.Open, FileAccess.Read))
. q, z' j1 S4 ?% q* e - using (BinaryReader reader = new BinaryReader(fs))
9 J5 g- i% U! ?& _ - {
3 `+ g" H# G* T- a/ S! m1 I - // 读取头部& p/ @8 n7 |' s7 z0 i
- string header = Encoding.ASCII.GetString(reader.ReadBytes(4));( y& N* l6 z, K7 W2 i. d) H
- if (header != "FORM")
3 u% ]& I+ [# s - {
+ i* A% F( ^2 Q- @ - Console.WriteLine("无效的.rl2文件格式。");
3 K! v( t- I$ S1 Y- b" _! b - return;
- k. q" E Y) [( ~3 O/ G1 K - }% i4 }& k3 n/ P( p1 u6 B3 t$ q
8 W S. y9 j+ O) d0 v7 ]4 a- uint backSize = reader.ReadUInt32();4 t( ]% r4 A2 P0 X
- string signature = Encoding.ASCII.GetString(reader.ReadBytes(4));
6 o% _4 ]- Q& L4 a - uint dataSize = reader.ReadUInt32();' O% e: a+ S- q$ f2 A) I: B |
- uint numFrames = reader.ReadUInt32();
/ n3 X# }/ \9 ?! ? - ushort method = reader.ReadUInt16();
8 U( R- B7 Y( b3 Q; a% h2 [' \ - ushort soundRate = reader.ReadUInt16();
8 K, A! ?- `. Z7 o* J: m - ushort rate = reader.ReadUInt16();
r. l8 G8 L& p8 M, _ - ushort channels = reader.ReadUInt16();! ^4 A0 q6 V: a' [
- ushort defSoundSize = reader.ReadUInt16();
/ @3 j3 R$ G$ \) m& _1 S" { - ushort videoBase = reader.ReadUInt16();
) r9 Z9 f# n! ]9 v7 J1 L8 O% K - uint clrCount = reader.ReadUInt32();4 N# Z1 j' Z% ?
- uint[] chunkSize = new uint[numFrames];# c! M" S% q( ~; @7 ?' y3 C
- uint[] chunkOffs = new uint[numFrames];
( W: ]1 }% x+ l - uint[] soundSize = new uint[numFrames];8 h; A! m: s) Q6 J+ R' i
" h" N, [3 ^! a3 _2 p ]- if (signature != "RLV2" && signature != "RLV3")! W+ X: e* m+ y" f2 r% Q3 ?; k
- {
& |+ L3 E) z+ p5 @; r - Console.WriteLine("不支持的签名。");4 n7 \" Q6 M3 P5 N
- return;
/ F5 [- T5 i" y" n9 I - }! _$ n; u; T9 n0 l- Q- |# D
- $ P$ b" H6 ]3 v
- // 读取块信息
6 m# F/ ]# F& k# p& [: q; } - for (int i = 0; i < numFrames; i++)
' [3 k: h# v, s* U1 m; M$ o1 ]* x - {% u, X9 Z; B! F7 T Q2 s0 r
- chunkSize[i] = reader.ReadUInt32();# c; E6 R& a& n. R) i2 a
- chunkOffs[i] = reader.ReadUInt32();
* [9 D( w" t( Y" S - soundSize[i] = reader.ReadUInt32();
; |9 C+ m2 j. H; i6 [; j - }0 E! I7 G! h. _' G* A, Q( P
& ~9 b$ p' \; ~; f- I1 ~- // 如果存在背景帧,请跳过它& A- E# c z8 b" u% [ D! b( o# w. i
- if (signature == "RLV3" && backSize != 0)/ j9 G( g* j: w$ G- j
- {# a* e# Y. j2 j) ]$ D' w/ t1 W
- reader.BaseStream.Seek(backSize, SeekOrigin.Current);
, Z, }* \8 t# A6 L - }( O5 v. ], ~) ^4 u/ A. ~/ ?
" h Y/ [ \1 ^* d4 p- // 创建一个WAV文件并写入音频数据
$ T$ M3 M% a0 c8 h1 ]' e5 \- C. p - using (BinaryWriter wavWriter = new BinaryWriter(File.Open(outputFile, FileMode.Create)))
/ _( _" }0 V% O3 D. e - {
7 e/ F, v3 _, {7 s - // 写入WAV头部
3 E5 y @6 |5 h - wavWriter.Write(Encoding.ASCII.GetBytes("RIFF"));& x1 R: B h* I: y% X
- wavWriter.Write(36 + dataSize); // 总文件大小 - 8
% e5 j6 L9 `2 e' _9 j - wavWriter.Write(Encoding.ASCII.GetBytes("WAVE"));% X! a+ X- _( \# z
- wavWriter.Write(Encoding.ASCII.GetBytes("fmt "));- o% ^( i% a$ t& D) K+ o! V
- wavWriter.Write(16); // fmt块大小$ |. S* M' B8 ^- V- a9 E$ Y0 L. s
- wavWriter.Write((ushort)1); // 音频格式(PCM)
- B9 ?% z1 z9 ] - wavWriter.Write(channels); // 声道数% j- s1 Y: D. ]& k
- wavWriter.Write(rate); // 采样率$ @. { D6 `, h7 I2 b
- wavWriter.Write(rate * channels * defSoundSize / 8); // 每秒字节数
7 r, }+ c6 \+ C - wavWriter.Write((ushort)(channels * defSoundSize / 8)); // 每个采样点字节数
i# I9 I, j& h - wavWriter.Write(defSoundSize); // 每个样本的位深度
1 S3 }8 o0 G B4 M- b1 I+ C - wavWriter.Write(Encoding.ASCII.GetBytes("data"));, q; E6 b$ ?& i; p& R- |1 q
- wavWriter.Write(dataSize); // 数据大小
% e' v. M9 C4 u; l - % R% ], ^; O. t: M8 a z
- // 从.rl2文件中读取并写入PCM音频数据
% l5 y+ T: z9 D: B" \( z - for (int i = 0; i < numFrames; i++)$ h' G; d6 K$ E
- {
# s! ^. P9 r3 F/ J - byte[] audioData = reader.ReadBytes((int)soundSize[i]);
' w0 c! j! O1 u - wavWriter.Write(audioData);& o' }! i- M+ P4 R
- }
/ [% v2 g/ B. X - }5 p1 l' F3 h" d' }2 |. t
- }2 Z$ j0 G6 U2 `/ ]3 L
- }
$ ^8 F$ L, t8 b$ g% ]: H - }/ \% |( n+ R" j' Q" }
1 p6 B5 ~4 {2 a/ ]( @, A$ ]) G- class Program! ~5 s4 q( N' F7 U1 O4 i& P
- {
- T- v9 _3 m+ `7 W/ m$ q7 F - static void Main(string[] args)
/ d9 C9 T' B+ J( S4 c j t4 | - {' ?0 ~4 K% B$ ]* }: J6 N
- string inputFile = "N1275510.RL2";0 b5 t0 E1 u2 X- ]+ `! z
- string outputFile = "output.wav";
% D, Q9 T% U9 |2 K; a! ^ - RL2ToWavConverter.ConvertToWav(inputFile, outputFile);0 O7 y7 r* C* L4 l# N
- Console.WriteLine("转换完成。");) L' Y" p; {# t4 K! h4 K
- }4 ^/ ~- m o5 [2 z
- }
4 ^$ m5 s* ?+ J' x/ e
复制代码 0 E X/ o/ w- c2 ?1 B3 k. ~
0 }$ v4 U1 Y0 O; Y2 N+ k( |* T6 N% U! w+ }& [+ k5 \
|