Add BGI Audio extract support

This commit is contained in:
2025-08-09 22:50:20 +08:00
parent 6cb8ca5f95
commit c34f4ac23d
9 changed files with 150 additions and 1 deletions

View File

@@ -489,6 +489,10 @@ fn detect_script_type(buf: &[u8], buf_len: usize, filename: &str) -> Option<&'st
if buf_len >= 16 && buf.starts_with(b"CompressedBG___") {
return Some(&ScriptType::BGICbg);
}
#[cfg(feature = "bgi-audio")]
if buf_len >= 8 && buf[4..].starts_with(b"bw ") {
return Some(&ScriptType::BGIAudio);
}
let filename = filename.to_lowercase();
if filename.ends_with("._bp") {
return Some(&ScriptType::BGIBp);

View File

@@ -491,6 +491,10 @@ fn detect_script_type(buf: &[u8], buf_len: usize, filename: &str) -> Option<&'st
if buf_len >= 16 && buf.starts_with(b"CompressedBG___") {
return Some(&ScriptType::BGICbg);
}
#[cfg(feature = "bgi-audio")]
if buf_len >= 8 && buf[4..].starts_with(b"bw ") {
return Some(&ScriptType::BGIAudio);
}
let filename = filename.to_lowercase();
if filename.ends_with("._bp") {
return Some(&ScriptType::BGIBp);

View File

@@ -0,0 +1,127 @@
use crate::ext::io::*;
use crate::scripts::base::*;
use crate::types::*;
use anyhow::Result;
use std::io::{Read, Seek, SeekFrom, Write};
#[derive(Debug)]
pub struct BgiAudioBuilder {}
impl BgiAudioBuilder {
pub fn new() -> Self {
Self {}
}
}
impl ScriptBuilder for BgiAudioBuilder {
fn default_encoding(&self) -> Encoding {
Encoding::Utf8
}
fn build_script(
&self,
buf: Vec<u8>,
_filename: &str,
_encoding: Encoding,
_archive_encoding: Encoding,
config: &ExtraConfig,
_archive: Option<&Box<dyn Script>>,
) -> Result<Box<dyn Script>> {
Ok(Box::new(BgiAudio::new(MemReader::new(buf), config)?))
}
fn build_script_from_file(
&self,
filename: &str,
_encoding: Encoding,
_archive_encoding: Encoding,
config: &ExtraConfig,
_archive: Option<&Box<dyn Script>>,
) -> Result<Box<dyn Script>> {
let file = std::fs::File::open(filename)?;
let f = std::io::BufReader::new(file);
Ok(Box::new(BgiAudio::new(f, config)?))
}
fn build_script_from_reader(
&self,
reader: Box<dyn ReadSeek>,
_filename: &str,
_encoding: Encoding,
_archive_encoding: Encoding,
config: &ExtraConfig,
_archive: Option<&Box<dyn Script>>,
) -> Result<Box<dyn Script>> {
Ok(Box::new(BgiAudio::new(reader, config)?))
}
fn extensions(&self) -> &'static [&'static str] {
&[]
}
fn script_type(&self) -> &'static ScriptType {
&ScriptType::BGIAudio
}
fn is_this_format(&self, _filename: &str, buf: &[u8], buf_len: usize) -> Option<u8> {
if buf_len >= 8 && buf[4..].starts_with(b"bw ") {
Some(10)
} else {
None
}
}
}
#[derive(Debug)]
pub struct BgiAudio {
data: MemReader,
}
impl BgiAudio {
pub fn new<R: Read + Seek>(mut reader: R, _config: &ExtraConfig) -> Result<Self> {
let offset = reader.read_u32()?;
let len = reader.stream_length()?;
if (offset as u64) > len {
return Err(anyhow::anyhow!("Invalid offset in BGI audio file"));
}
let mut magic = [0; 4];
reader.read_exact(&mut magic)?;
if magic != *b"bw " {
return Err(anyhow::anyhow!(
"Invalid magic in BGI audio file: {:?}",
magic
));
}
reader.seek(SeekFrom::Start(offset as u64))?;
let mut data = Vec::new();
reader.read_to_end(&mut data)?;
Ok(Self {
data: MemReader::new(data),
})
}
}
impl Script for BgiAudio {
fn default_output_script_type(&self) -> OutputScriptType {
OutputScriptType::Custom
}
fn default_format_type(&self) -> FormatOptions {
FormatOptions::None
}
fn is_output_supported(&self, output: OutputScriptType) -> bool {
matches!(output, OutputScriptType::Custom)
}
fn custom_output_extension<'a>(&'a self) -> &'a str {
"ogg"
}
fn custom_export(&self, filename: &std::path::Path, _encoding: Encoding) -> Result<()> {
let mut writer = std::fs::File::create(filename)?;
writer.write_all(&self.data.data)?;
writer.flush()?;
Ok(())
}
}

View File

@@ -0,0 +1 @@
pub mod audio;

View File

@@ -1,5 +1,7 @@
#[cfg(feature = "bgi-arc")]
pub mod archive;
#[cfg(feature = "bgi-audio")]
pub mod audio;
pub mod bp;
pub mod bsi;
#[cfg(feature = "bgi-img")]

View File

@@ -98,6 +98,8 @@ lazy_static::lazy_static! {
Box::new(circus::archive::crm::CrmArchiveBuilder::new()),
#[cfg(feature = "circus-img")]
Box::new(circus::image::crxd::CrxdImageBuilder::new()),
#[cfg(feature = "bgi-audio")]
Box::new(bgi::audio::audio::BgiAudioBuilder::new()),
];
pub static ref ALL_EXTS: Vec<String> =
BUILDER.iter().flat_map(|b| b.extensions()).map(|s| s.to_string()).collect();