本帖最后由 shane007 于 2023-9-2 00:06 编辑
& |9 M5 R7 w. p4 o
' f( J. }! G0 o h3 S该游戏是scummvm支持的少数几款FMV AVG。/ `2 z: R6 U; v. m& t0 \8 S
视频采用了一种叫做RL2的格式。
" b7 |7 B5 t6 g4 l' e参考以下格式和Scummvm中的代码,可以想办法将RL2中的raw data部分转换为wav,) F* [6 t0 B4 F/ ~2 q6 z' `
然后用whisper语音识别之后就能配上字幕了。- N5 \2 \* _/ f* r
此外,rl2格式用potplayer也能直接播放。
' q. T3 G2 N9 r, q$ S7 P# i6 d7 K, A( r3 R$ q, ^" R+ |
文件格式8 u1 X$ ~9 g) j
https://wiki.multimedia.cx/index.php/RL2
9 _$ g% \$ G6 ?2 m' O: }$ M* k! g
. `$ z3 W6 S9 ~; a% q- + 0 dword Header -- "FORM": d9 k& ?% p0 \3 D0 N$ \
- + 4 dword BackSize -- size of the background frame
+ E% J- R+ h4 t8 G# G. d q - + 8 dword Signature -- "RLV2" or "RLV3"
+ L9 s, E6 J7 n; y2 b" Q# G7 [ - + C dword DataSize -- size of the data past this point BIG-ENDIAN1 m4 {% I8 G9 l. A" {
- + 10 dword NumFrames -- number of frames' O4 M: P' L4 {! i
- + 14 word Method -- encoding method. ignored, zero
# R f4 B: I6 w - + 16 word SoundRate -- sound sample rate in some obscure format. zero means no sound
1 f9 ?* Y3 K9 U: ~$ N - + 18 word Rate -- sound sample rate in Hz; [3 z: R! s) _
- + 1A word Channels -- number of sound channels) m" l3 Z# K x3 {! m& ?
- + 1C word DefSoundSize -- size of the single sound chunk. see notes below
7 I. m; D% I( u' { - + 1E word VideoBase -- initial drawing offset within 320x200 viewport
5 _& B8 H; {! V - + 20 dword ClrCount -- number of used colors: R( E" V: J4 I$ t" C9 S& |
- + 24 RGB Palette[256] -- 256 RGB triplets. full 8-bit entries& m/ n# L6 ]( |) f& s% Y
- -- if Signature == "RLV3" AND BackSize <> 0+ J; G ?. r' s$ M
- +324 byte BackFrame[BackSize] -- encoded background frame. ignore VideoBase during it's decoding
( e7 m) b9 t+ Z9 k f - --
# @/ t4 {+ a( w6 i' g - +xxx dword ChunkSize[NumFrames] -- complete size of the chunk for each frame (audio+video)+ _) T* i$ ~( X3 b* V; @
- +yyy dword ChunkOffs[NumFrames] -- offset of the each frame chunk from the start of file) _' p p) g; @+ Y
- +zzz dword SoundSize[NumFrames] -- size of the audio portion in the frame chunk3 d( f/ e+ B+ Z% x8 {" C
- -- for each frame --6 x: ~( n9 |: y# u
- +xxx byte Audio[SoundSize[frame_nr] & 0xFFFF] -- raw 8-bit audio
& {9 D' k1 l& S& p0 l4 z9 ~ - +yyy byte Video[...] -- compressed video stream
复制代码 6 l j- e K ^! g2 r( z* y' N) x0 P
参考代码(有问题,但可参考)- using System;
i v7 o- E0 w# ~- ` - using System.IO;9 Z! C. R# T8 C) W$ o7 ^& Z: b8 p
- using System.Text;
8 \( L& n. V- V - 8 M; a5 T3 B! a2 c
- public class RL2ToWavConverter
4 B& T5 x0 t4 c) Z% c- H - {
% Y" F/ D8 q6 p. X - public static void ConvertToWav(string inputFile, string outputFile)
1 Q9 ?, A. c1 N8 k0 p' T. X - {$ k5 E% t: K/ {- m: ?$ L9 f% D
- using (FileStream fs = new FileStream(inputFile, FileMode.Open, FileAccess.Read))
/ K9 w6 Z" o) n, a9 k - using (BinaryReader reader = new BinaryReader(fs))
1 }; n9 \9 L. ~4 v; Z1 D% T - {
; \) B5 O/ o. Z0 A: d$ { - // 读取头部
! d: \' {' C2 w! ~ - string header = Encoding.ASCII.GetString(reader.ReadBytes(4));; H0 v. J5 F( h
- if (header != "FORM")9 S0 t, ^7 ~+ T3 {8 g& a7 B
- {
1 {9 _; G, a* n6 d8 C, U - Console.WriteLine("无效的.rl2文件格式。");
T! }' }- R$ b/ | - return;
% `. {5 g8 a0 Y9 T, Q - }
k6 m% B& @5 Q! s* r, a
" ?0 \" \9 b* _0 L8 y6 z: j" c& D- uint backSize = reader.ReadUInt32();- \1 P4 X6 S, j( c9 _
- string signature = Encoding.ASCII.GetString(reader.ReadBytes(4));2 Q+ h1 w! Q9 }% i7 u: N9 x
- uint dataSize = reader.ReadUInt32();
& p% Q' H7 ] r" o: C. f4 o. B# E - uint numFrames = reader.ReadUInt32();$ L9 m" Y9 T. K3 Q
- ushort method = reader.ReadUInt16();
- x$ P0 V" Y: N! V. d' o* _ - ushort soundRate = reader.ReadUInt16();9 v9 N; ]1 u! a
- ushort rate = reader.ReadUInt16();9 W% c( u) c' d) _7 u6 `; d. U
- ushort channels = reader.ReadUInt16();
/ f% T% P: Q, H4 [- P! k7 V( t' ?7 W - ushort defSoundSize = reader.ReadUInt16();
) w H. [# r4 M4 \ - ushort videoBase = reader.ReadUInt16();
5 a' h) \8 L5 `' D% ] - uint clrCount = reader.ReadUInt32();
" r0 u |& h5 e; d" p - uint[] chunkSize = new uint[numFrames];
0 K6 M1 T3 R* q, F+ `" k5 G# N - uint[] chunkOffs = new uint[numFrames];
( E6 _+ C3 `+ J! ?1 a - uint[] soundSize = new uint[numFrames];4 T7 W& k4 E0 M
- & m! [- ~5 [. _" ?# d, K
- if (signature != "RLV2" && signature != "RLV3")7 u% V+ J1 b* U" {* c
- {
4 o* e$ O! _: V" l+ H5 P. B9 d - Console.WriteLine("不支持的签名。");
* k7 E; n. V; ^2 B% i0 r - return;# p7 {* k& ?+ ~- c/ W
- }0 p8 J* S6 j/ l0 m u
- ) o- Q( v& ?1 f; n8 p! H# }* U/ b
- // 读取块信息! d) Y: A. n/ u+ j: q
- for (int i = 0; i < numFrames; i++)
- z' |. N) D7 c f$ K5 r - {
. z$ D$ i% \0 O' e8 S3 p! W9 N5 I) R; d - chunkSize[i] = reader.ReadUInt32();
0 {& P1 h( \3 I+ O* R - chunkOffs[i] = reader.ReadUInt32();
8 M; ^5 t8 d8 ~( K. ~/ l* Q - soundSize[i] = reader.ReadUInt32();
! W3 X, l) U9 K3 b1 K& Z. J - }. T, h/ O$ z0 g4 A3 ] ]+ v" g
- ( N+ m: `+ E* O4 N5 P3 k/ G
- // 如果存在背景帧,请跳过它
1 O' s4 t) p3 U6 }+ i5 q1 q - if (signature == "RLV3" && backSize != 0)+ c' c" n/ Z/ |0 F/ B
- {5 Y _' P& {5 D- A( h3 \
- reader.BaseStream.Seek(backSize, SeekOrigin.Current);! R. I/ H! q& w! H0 x, O
- }
" G' i) S& V+ T6 {; ?8 J$ z - " T" C7 g' a& G' }
- // 创建一个WAV文件并写入音频数据* g! }6 |; C- W8 v( X ~7 j! _
- using (BinaryWriter wavWriter = new BinaryWriter(File.Open(outputFile, FileMode.Create)))
* Y3 g& X& c/ |) B% p - {
2 k6 `6 k; I9 k2 [$ Q5 s9 l S - // 写入WAV头部" D' S3 @- o: r. K9 t5 C. O6 ~! ?
- wavWriter.Write(Encoding.ASCII.GetBytes("RIFF"));5 J: r+ w. O B& e
- wavWriter.Write(36 + dataSize); // 总文件大小 - 8
2 s( t' k# s8 g7 B - wavWriter.Write(Encoding.ASCII.GetBytes("WAVE"));: R! K2 P9 o! s# P* [% Y
- wavWriter.Write(Encoding.ASCII.GetBytes("fmt "));# C! b. X3 J1 _4 R5 P+ Q( [7 x" [
- wavWriter.Write(16); // fmt块大小1 m4 B9 ]2 w C/ |# p+ ]( q6 V/ y
- wavWriter.Write((ushort)1); // 音频格式(PCM)2 w; P' x1 k9 z ^- A; X6 O
- wavWriter.Write(channels); // 声道数. f( c# U* J5 I7 F& [
- wavWriter.Write(rate); // 采样率5 p4 v8 y s% K2 E7 N9 b% R
- wavWriter.Write(rate * channels * defSoundSize / 8); // 每秒字节数* \( U1 _' h2 l# w {! b
- wavWriter.Write((ushort)(channels * defSoundSize / 8)); // 每个采样点字节数
( b- C- a8 m( J0 c$ ~, f7 D; S - wavWriter.Write(defSoundSize); // 每个样本的位深度
1 o) e( E& u3 p - wavWriter.Write(Encoding.ASCII.GetBytes("data"));" I; O$ W+ _2 i! v9 n% J
- wavWriter.Write(dataSize); // 数据大小, Z0 x/ w& P0 }" i/ W c
- 2 F) D3 Y X$ Y: c" Y
- // 从.rl2文件中读取并写入PCM音频数据) ^5 o+ H% S% Q' F+ ?
- for (int i = 0; i < numFrames; i++)
! w! v3 |+ E2 I* u) M4 @9 L$ | - {. K4 h, S2 x/ U# [$ k: C7 k+ v4 R# a1 D
- byte[] audioData = reader.ReadBytes((int)soundSize[i]);
9 ]8 ]+ [4 E% M2 i/ p" D/ ~9 B2 s - wavWriter.Write(audioData);9 D2 }& q/ a- j7 j
- }( h$ ^9 V' D8 l: f
- }
% o* K1 C# l4 N% ^+ Y9 e, K; ? - }4 _' x, N3 Q8 d9 g
- }# L% }& E2 u7 u
- }* B3 c0 u( w5 W+ c4 k
- ' j8 e0 a4 K* k& b
- class Program
' B4 {) P1 j& _ E/ J9 q - { N6 x( x2 X7 Y, C, H. K
- static void Main(string[] args)
4 |% N5 ^! I4 E( b% E - {
) ` D9 H7 P, L - string inputFile = "N1275510.RL2";9 [% @# k) V1 s5 S+ Z' S |2 s7 e7 O
- string outputFile = "output.wav";
1 W1 `% ?$ H( E2 [9 ^2 @. D - RL2ToWavConverter.ConvertToWav(inputFile, outputFile);2 H/ A# q# P+ m# K8 f: j
- Console.WriteLine("转换完成。");
N: R) a5 H6 q" K - }* u% A! ?$ I0 t1 R' ]4 K
- }, F( S) b% y9 q9 G2 t3 I
复制代码
! }6 n F" ]( H: t
' M1 u- c' ?9 m$ |$ y! N2 o/ M" }5 h/ P f
|