msg_tool\scripts\bgi\audio/
audio.rs1use crate::ext::io::*;
3use crate::scripts::base::*;
4use crate::types::*;
5use anyhow::Result;
6use std::io::{Read, Seek, SeekFrom, Write};
7
8#[derive(Debug)]
9pub struct BgiAudioBuilder {}
11
12impl BgiAudioBuilder {
13 pub fn new() -> Self {
15 Self {}
16 }
17}
18
19impl ScriptBuilder for BgiAudioBuilder {
20 fn default_encoding(&self) -> Encoding {
21 Encoding::Utf8
22 }
23
24 fn build_script(
25 &self,
26 buf: Vec<u8>,
27 _filename: &str,
28 _encoding: Encoding,
29 _archive_encoding: Encoding,
30 config: &ExtraConfig,
31 _archive: Option<&Box<dyn Script>>,
32 ) -> Result<Box<dyn Script + Send + Sync>> {
33 Ok(Box::new(BgiAudio::new(MemReader::new(buf), config)?))
34 }
35
36 fn build_script_from_file(
37 &self,
38 filename: &str,
39 _encoding: Encoding,
40 _archive_encoding: Encoding,
41 config: &ExtraConfig,
42 _archive: Option<&Box<dyn Script>>,
43 ) -> Result<Box<dyn Script + Send + Sync>> {
44 let file = std::fs::File::open(filename)?;
45 let f = std::io::BufReader::new(file);
46 Ok(Box::new(BgiAudio::new(f, config)?))
47 }
48
49 fn build_script_from_reader<'a>(
50 &self,
51 reader: Box<dyn ReadSeek + Send + Sync + 'a>,
52 _filename: &str,
53 _encoding: Encoding,
54 _archive_encoding: Encoding,
55 config: &ExtraConfig,
56 _archive: Option<&Box<dyn Script>>,
57 ) -> Result<Box<dyn Script + Send + Sync + 'a>> {
58 Ok(Box::new(BgiAudio::new(reader, config)?))
59 }
60
61 fn extensions(&self) -> &'static [&'static str] {
62 &[]
63 }
64
65 fn script_type(&self) -> &'static ScriptType {
66 &ScriptType::BGIAudio
67 }
68
69 fn is_this_format(&self, _filename: &str, buf: &[u8], buf_len: usize) -> Option<u8> {
70 if buf_len >= 8 && buf[4..].starts_with(b"bw ") {
71 Some(10)
72 } else {
73 None
74 }
75 }
76
77 fn is_audio(&self) -> bool {
78 true
79 }
80}
81
82#[derive(Debug)]
83pub struct BgiAudio {
85 data: MemReader,
86}
87
88impl BgiAudio {
89 pub fn new<R: Read + Seek>(mut reader: R, _config: &ExtraConfig) -> Result<Self> {
94 let offset = reader.read_u32()?;
95 let len = reader.stream_length()?;
96 if (offset as u64) > len {
97 return Err(anyhow::anyhow!("Invalid offset in BGI audio file"));
98 }
99 let mut magic = [0; 4];
100 reader.read_exact(&mut magic)?;
101 if magic != *b"bw " {
102 return Err(anyhow::anyhow!(
103 "Invalid magic in BGI audio file: {:?}",
104 magic
105 ));
106 }
107 reader.seek(SeekFrom::Start(offset as u64))?;
108 let mut data = Vec::new();
109 reader.read_to_end(&mut data)?;
110 Ok(Self {
111 data: MemReader::new(data),
112 })
113 }
114}
115
116impl Script for BgiAudio {
117 fn default_output_script_type(&self) -> OutputScriptType {
118 OutputScriptType::Custom
119 }
120
121 fn default_format_type(&self) -> FormatOptions {
122 FormatOptions::None
123 }
124
125 fn is_output_supported(&self, output: OutputScriptType) -> bool {
126 matches!(output, OutputScriptType::Custom)
127 }
128
129 fn custom_output_extension<'a>(&'a self) -> &'a str {
130 "ogg"
131 }
132
133 fn custom_export(&self, filename: &std::path::Path, _encoding: Encoding) -> Result<()> {
134 let mut writer = std::fs::File::create(filename)?;
135 writer.write_all(&self.data.data)?;
136 writer.flush()?;
137 Ok(())
138 }
139}