Add support to read embbed control block (tested game: https://vndb.org/v19829 )

This commit is contained in:
2026-04-07 12:01:27 +08:00
parent 80150784ab
commit 7b0de4468b
17 changed files with 376 additions and 4 deletions

View File

@@ -33,6 +33,8 @@ pub mod psd;
pub mod rc4;
#[cfg(feature = "utils-serde-base64bytes")]
pub mod serde_base64bytes;
#[cfg(feature = "utils-simple-pack")]
pub mod simple_pack;
#[cfg(feature = "utils-str")]
pub mod str;
pub mod struct_pack;

100
src/utils/simple_pack.rs Normal file
View File

@@ -0,0 +1,100 @@
use crate::ext::io::*;
use crate::types::*;
use crate::utils::encoding::*;
use anyhow::Result;
use std::io::{Read, Seek};
pub struct SimplePack<T: Read> {
inner: T,
total: u64,
current: u64,
}
impl<T: Read> SimplePack<T> {
pub fn next<'a>(&'a mut self) -> Result<Option<SimplePackEntry<'a, T>>> {
if self.current >= self.total {
return Ok(None);
}
let name = self.read_cstring()?;
let name = decode_to_string(Encoding::Utf8, name.as_bytes(), true)?;
let entry_size = self.read_u64()?;
Ok(Some(SimplePackEntry {
pack: self,
total: entry_size,
current: 0,
name,
}))
}
}
impl<T: Read> Read for SimplePack<T> {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
let remaining = self.total - self.current;
if remaining == 0 {
return Ok(0);
}
let to_read = std::cmp::min(remaining, buf.len() as u64) as usize;
let bytes_read = self.inner.read(&mut buf[..to_read])?;
self.current += bytes_read as u64;
Ok(bytes_read)
}
}
pub struct SimplePackEntry<'a, T: Read> {
pub pack: &'a mut SimplePack<T>,
total: u64,
current: u64,
pub name: String,
}
impl<'a, T: Read> Drop for SimplePackEntry<'a, T> {
fn drop(&mut self) {
let to_skip = self.total - self.current;
if to_skip > 0 {
if let Err(e) = self.pack.skip(to_skip) {
eprintln!("Failed to skip remaining bytes in SimplePackEntry: {}", e);
crate::COUNTER.inc_error();
}
}
}
}
impl<'a, T: Read> Read for SimplePackEntry<'a, T> {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
let remaining = self.total - self.current;
if remaining == 0 {
return Ok(0);
}
let to_read = std::cmp::min(remaining, buf.len() as u64) as usize;
let bytes_read = self.pack.read(&mut buf[..to_read])?;
self.current += bytes_read as u64;
Ok(bytes_read)
}
}
pub fn read_simple_pack<'a, T: Read + Seek + 'a>(
mut reader: T,
) -> Result<SimplePack<Box<dyn Read + 'a>>> {
reader.read_and_equal(b"SPCK")?;
let flags = reader.read_u8()?;
// not compressed
if flags == 0 {
let pos = reader.stream_position()?;
let total = reader.stream_length()? - pos;
Ok(SimplePack {
inner: Box::new(reader),
total,
current: 0,
})
} else {
let compressed_size = reader.read_u64()?;
let uncompressed_size = reader.read_u64()?;
let compressed = reader.take(compressed_size);
let decompressed = zstd::stream::read::Decoder::new(compressed)?;
Ok(SimplePack {
inner: Box::new(decompressed),
total: uncompressed_size,
current: 0,
})
}
}