From 798b6279c27a6937243186147f0a7d7a0992183f Mon Sep 17 00:00:00 2001 From: lifegpc Date: Thu, 12 Jun 2025 13:35:44 +0800 Subject: [PATCH] BgiArchiveV1 add DSC decode support --- src/scripts/bgi/archive/v1.rs | 101 +++++++++++++++++++++++++++++++++- 1 file changed, 100 insertions(+), 1 deletion(-) diff --git a/src/scripts/bgi/archive/v1.rs b/src/scripts/bgi/archive/v1.rs index a609543..5214bfb 100644 --- a/src/scripts/bgi/archive/v1.rs +++ b/src/scripts/bgi/archive/v1.rs @@ -1,3 +1,4 @@ +use super::dsc::*; use crate::ext::io::*; use crate::scripts::base::*; use crate::types::*; @@ -108,12 +109,17 @@ struct Entry { reader: Arc>, pos: usize, base_offset: u64, + script_type: Option, } impl ArchiveContent for Entry { fn name(&self) -> &str { &self.header.filename } + + fn script_type(&self) -> Option<&ScriptType> { + self.script_type.as_ref() + } } impl Read for Entry { @@ -197,6 +203,48 @@ impl Script for BgiArchive { } } +struct MemEntry { + name: String, + data: MemReader, +} + +impl Read for MemEntry { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + self.data.read(buf) + } +} + +impl ArchiveContent for MemEntry { + fn name(&self) -> &str { + &self.name + } + + fn script_type(&self) -> Option<&ScriptType> { + detect_script_type(&self.data.data, self.data.data.len(), &self.name) + } + + fn data(&mut self) -> Result> { + Ok(self.data.data.clone()) + } + + fn to_data<'a>(&'a mut self) -> Result> { + Ok(Box::new(&mut self.data)) + } +} + +fn detect_script_type(buf: &[u8], buf_len: usize, filename: &str) -> Option<&'static ScriptType> { + if buf_len >= 28 && buf.starts_with(b"BurikoCompiledScriptVer1.00\0") { + return Some(&ScriptType::BGI); + } + let filename = filename.to_lowercase(); + if filename.ends_with("._bp") { + return Some(&ScriptType::BGIBp); + } else if filename.ends_with("._bsi") { + return Some(&ScriptType::BGIBsi); + } + None +} + struct BgiArchiveIter<'a, T: Iterator, R: Read + Seek> { entries: T, reader: Arc>, @@ -213,12 +261,63 @@ impl<'a, T: Iterator, R: Read + Seek + 'static> Iterat Some(e) => e, None => return None, }; - let entry = Entry { + let mut entry = Entry { header: entry.clone(), reader: self.reader.clone(), pos: 0, base_offset: self.base_offset, + script_type: None, }; + let mut buf = [0u8; 32]; + match entry.read(&mut buf) { + Ok(_) => {} + Err(e) => { + return Some(Err(anyhow::anyhow!( + "Failed to read entry '{}': {}", + entry.header.filename, + e + ))); + } + } + entry.pos = 0; + if buf.starts_with(b"DSC FORMAT 1.00") { + let data = match entry.data() { + Ok(data) => data, + Err(e) => { + return Some(Err(anyhow::anyhow!( + "Failed to read DSC data for '{}': {}", + entry.header.filename, + e + ))); + } + }; + entry.pos = 0; + let dsc = match DscDecoder::new(&data) { + Ok(dsc) => dsc, + Err(e) => { + return Some(Err(anyhow::anyhow!( + "Failed to create DSC decoder for '{}': {}", + entry.header.filename, + e + ))); + } + }; + let decoded = match dsc.unpack() { + Ok(decoded) => decoded, + Err(e) => { + return Some(Err(anyhow::anyhow!( + "Failed to unpack DSC data for '{}': {}", + entry.header.filename, + e + ))); + } + }; + return Some(Ok(Box::new(MemEntry { + name: entry.header.filename.clone(), + data: MemReader::new(decoded), + }))); + } + entry.script_type = detect_script_type(&buf, buf.len(), &entry.header.filename).cloned(); Some(Ok(Box::new(entry))) } }