diff --git a/src/scripts/bgi/archive/v1.rs b/src/scripts/bgi/archive/v1.rs index 5214bfb..1f28392 100644 --- a/src/scripts/bgi/archive/v1.rs +++ b/src/scripts/bgi/archive/v1.rs @@ -1,3 +1,4 @@ +use super::bse::*; use super::dsc::*; use crate::ext::io::*; use crate::scripts::base::*; @@ -6,7 +7,7 @@ use crate::utils::encoding::encode_string; use crate::utils::struct_pack::*; use anyhow::Result; use msg_tool_macro::*; -use std::io::{Read, Seek, Write}; +use std::io::{Read, Seek, SeekFrom, Write}; use std::sync::{Arc, Mutex}; #[derive(Debug)] @@ -130,7 +131,7 @@ impl Read for Entry { format!("Failed to lock mutex: {}", e), ) })?; - reader.seek(std::io::SeekFrom::Start( + reader.seek(SeekFrom::Start( self.base_offset + self.header.offset as u64 + self.pos as u64, ))?; let bytes_read = buf.len().min(self.header.size as usize - self.pos); @@ -143,6 +144,46 @@ impl Read for Entry { } } +impl Seek for Entry { + fn seek(&mut self, pos: SeekFrom) -> std::io::Result { + let new_pos = match pos { + SeekFrom::Start(offset) => offset as usize, + SeekFrom::End(offset) => { + if offset < 0 { + if (-offset) as usize > self.header.size as usize { + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + "Seek from end exceeds file length", + )); + } + self.header.size as usize - (-offset) as usize + } else { + self.header.size as usize + offset as usize + } + } + SeekFrom::Current(offset) => { + if offset < 0 { + if (-offset) as usize > self.pos { + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + "Seek from current exceeds current position", + )); + } + self.pos.saturating_sub((-offset) as usize) + } else { + self.pos + offset as usize + } + } + }; + self.pos = new_pos; + Ok(self.pos as u64) + } + + fn stream_position(&mut self) -> std::io::Result { + Ok(self.pos as u64) + } +} + #[derive(Debug)] pub struct BgiArchive { reader: Arc>, @@ -312,11 +353,41 @@ impl<'a, T: Iterator, R: Read + Seek + 'static> Iterat ))); } }; + let reader = MemReader::new(decoded); + if reader.data.starts_with(b"BSE 1.") { + match BseReader::new(reader, detect_script_type, &entry.header.filename) { + Ok(bse_reader) => { + return Some(Ok(Box::new(bse_reader))); + } + Err(e) => { + return Some(Err(anyhow::anyhow!( + "Failed to create BSE reader for '{}': {}", + entry.header.filename, + e + ))); + } + }; + } return Some(Ok(Box::new(MemEntry { name: entry.header.filename.clone(), - data: MemReader::new(decoded), + data: reader, }))); } + if buf.starts_with(b"BSE 1.") { + let filename = entry.header.filename.clone(); + match BseReader::new(entry, detect_script_type, &filename) { + Ok(bse_reader) => { + return Some(Ok(Box::new(bse_reader))); + } + Err(e) => { + return Some(Err(anyhow::anyhow!( + "Failed to create BSE reader for '{}': {}", + &filename, + e + ))); + } + }; + } entry.script_type = detect_script_type(&buf, buf.len(), &entry.header.filename).cloned(); Some(Ok(Box::new(entry))) }