fix(sdk): Fix file header binary format
The file header format is a bit more complex than I first realized, especially around when a path to `data/` is included, and which size field determines its file name length.
This commit is contained in:
parent
110108004d
commit
df06182ca0
2 changed files with 74 additions and 23 deletions
|
@ -412,6 +412,7 @@ impl std::fmt::Display for BundleFileType {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct BundleFileHeader {
|
struct BundleFileHeader {
|
||||||
variant: u32,
|
variant: u32,
|
||||||
|
unknown_1: u8,
|
||||||
size: usize,
|
size: usize,
|
||||||
len_data_file_name: usize,
|
len_data_file_name: usize,
|
||||||
}
|
}
|
||||||
|
@ -420,6 +421,8 @@ pub struct BundleFileVariant {
|
||||||
property: u32,
|
property: u32,
|
||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
data_file_name: Option<String>,
|
data_file_name: Option<String>,
|
||||||
|
// Seems to be related to whether there is a data path.
|
||||||
|
unknown_1: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BundleFileVariant {
|
impl BundleFileVariant {
|
||||||
|
@ -432,6 +435,7 @@ impl BundleFileVariant {
|
||||||
property: 0,
|
property: 0,
|
||||||
data: Vec::new(),
|
data: Vec::new(),
|
||||||
data_file_name: None,
|
data_file_name: None,
|
||||||
|
unknown_1: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -461,30 +465,38 @@ impl BundleFileVariant {
|
||||||
R: Read + Seek,
|
R: Read + Seek,
|
||||||
{
|
{
|
||||||
let variant = r.read_u32()?;
|
let variant = r.read_u32()?;
|
||||||
r.skip_u8(0)?;
|
let unknown_1 = r.read_u8()?;
|
||||||
let size = r.read_u32()? as usize;
|
let size = r.read_u32()? as usize;
|
||||||
r.skip_u8(1)?;
|
r.skip_u8(1)?;
|
||||||
let len_data_file_name = r.read_u32()? as usize;
|
let len_data_file_name = r.read_u32()? as usize;
|
||||||
|
|
||||||
Ok(BundleFileHeader {
|
Ok(BundleFileHeader {
|
||||||
size,
|
size,
|
||||||
|
unknown_1,
|
||||||
variant,
|
variant,
|
||||||
len_data_file_name,
|
len_data_file_name,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
fn write_header<W>(&self, w: &mut W) -> Result<()>
|
fn write_header<W>(&self, w: &mut W, props: Properties) -> Result<()>
|
||||||
where
|
where
|
||||||
W: Write + Seek,
|
W: Write + Seek,
|
||||||
{
|
{
|
||||||
w.write_u32(self.property)?;
|
w.write_u32(self.property)?;
|
||||||
w.write_u8(0)?;
|
w.write_u8(self.unknown_1)?;
|
||||||
w.write_u32(self.data.len() as u32)?;
|
|
||||||
w.write_u8(1)?;
|
|
||||||
|
|
||||||
let len_data_file_name = self.data_file_name.as_ref().map(|s| s.len()).unwrap_or(0);
|
let len_data_file_name = self.data_file_name.as_ref().map(|s| s.len()).unwrap_or(0);
|
||||||
w.write_u32(len_data_file_name as u32)?;
|
|
||||||
|
if props.contains(Properties::DATA) {
|
||||||
|
w.write_u32(len_data_file_name as u32)?;
|
||||||
|
w.write_u8(1)?;
|
||||||
|
w.write_u32(0)?;
|
||||||
|
} else {
|
||||||
|
w.write_u32(self.data.len() as u32)?;
|
||||||
|
w.write_u8(1)?;
|
||||||
|
w.write_u32(len_data_file_name as u32)?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -528,36 +540,63 @@ impl BundleFile {
|
||||||
let name = ctx.lookup_hash(hash, HashGroup::Filename);
|
let name = ctx.lookup_hash(hash, HashGroup::Filename);
|
||||||
|
|
||||||
let header_count = r.read_u32()? as usize;
|
let header_count = r.read_u32()? as usize;
|
||||||
|
tracing::trace!(header_count);
|
||||||
let mut headers = Vec::with_capacity(header_count);
|
let mut headers = Vec::with_capacity(header_count);
|
||||||
r.skip_u32(0)?;
|
r.skip_u32(0)?;
|
||||||
|
|
||||||
for _ in 0..header_count {
|
for i in 0..header_count {
|
||||||
let header = BundleFileVariant::read_header(r)?;
|
let span = tracing::info_span!("Read file header", i);
|
||||||
|
let _enter = span.enter();
|
||||||
|
|
||||||
|
let header = BundleFileVariant::read_header(r)
|
||||||
|
.wrap_err_with(|| format!("failed to read header {i}"))?;
|
||||||
|
|
||||||
|
if props.contains(Properties::DATA) {
|
||||||
|
tracing::debug!("props: {props:?} | unknown_1: {}", header.unknown_1)
|
||||||
|
}
|
||||||
|
|
||||||
headers.push(header);
|
headers.push(header);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut variants = Vec::with_capacity(header_count);
|
let mut variants = Vec::with_capacity(header_count);
|
||||||
for (i, header) in headers.into_iter().enumerate() {
|
for (i, header) in headers.into_iter().enumerate() {
|
||||||
let span = tracing::info_span!("Read file header {}", i, size = header.size);
|
let span = tracing::info_span!(
|
||||||
|
"Read file data {}",
|
||||||
|
i,
|
||||||
|
size = header.size,
|
||||||
|
len_data_file_name = header.len_data_file_name
|
||||||
|
);
|
||||||
let _enter = span.enter();
|
let _enter = span.enter();
|
||||||
|
|
||||||
let mut data = vec![0; header.size];
|
let (data, data_file_name) = if props.contains(Properties::DATA) {
|
||||||
r.read_exact(&mut data)
|
let data = vec![];
|
||||||
.wrap_err_with(|| format!("failed to read header {i}"))?;
|
|
||||||
|
|
||||||
let data_file_name = if header.len_data_file_name > 0 {
|
|
||||||
let s = r
|
let s = r
|
||||||
.read_string_len(header.len_data_file_name)
|
.read_string_len(header.size)
|
||||||
.wrap_err("failed to read data file name")?;
|
.wrap_err("failed to read data file name")?;
|
||||||
Some(s)
|
|
||||||
|
(data, Some(s))
|
||||||
} else {
|
} else {
|
||||||
None
|
let mut data = vec![0; header.size];
|
||||||
|
r.read_exact(&mut data)
|
||||||
|
.wrap_err_with(|| format!("failed to read file {i}"))?;
|
||||||
|
|
||||||
|
let data_file_name = if header.len_data_file_name > 0 {
|
||||||
|
let s = r
|
||||||
|
.read_string_len(header.len_data_file_name)
|
||||||
|
.wrap_err("failed to read data file name")?;
|
||||||
|
Some(s)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
(data, data_file_name)
|
||||||
};
|
};
|
||||||
|
|
||||||
let variant = BundleFileVariant {
|
let variant = BundleFileVariant {
|
||||||
property: header.variant,
|
property: header.variant,
|
||||||
data,
|
data,
|
||||||
data_file_name,
|
data_file_name,
|
||||||
|
unknown_1: header.unknown_1,
|
||||||
};
|
};
|
||||||
|
|
||||||
variants.push(variant);
|
variants.push(variant);
|
||||||
|
@ -584,16 +623,26 @@ impl BundleFile {
|
||||||
|
|
||||||
for variant in self.variants.iter() {
|
for variant in self.variants.iter() {
|
||||||
w.write_u32(variant.property())?;
|
w.write_u32(variant.property())?;
|
||||||
w.write_u8(0)?;
|
w.write_u8(variant.unknown_1)?;
|
||||||
w.write_u32(variant.size() as u32)?;
|
|
||||||
w.write_u8(1)?;
|
|
||||||
|
|
||||||
let len_data_file_name = variant.data_file_name().map(|s| s.len()).unwrap_or(0);
|
let len_data_file_name = variant.data_file_name().map(|s| s.len()).unwrap_or(0);
|
||||||
w.write_u32(len_data_file_name as u32)?;
|
|
||||||
|
if self.props.contains(Properties::DATA) {
|
||||||
|
w.write_u32(len_data_file_name as u32)?;
|
||||||
|
w.write_u8(1)?;
|
||||||
|
w.write_u32(0)?;
|
||||||
|
} else {
|
||||||
|
w.write_u32(variant.size() as u32)?;
|
||||||
|
w.write_u8(1)?;
|
||||||
|
w.write_u32(len_data_file_name as u32)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for variant in self.variants.iter() {
|
for variant in self.variants.iter() {
|
||||||
w.write_all(&variant.data)?;
|
w.write_all(&variant.data)?;
|
||||||
|
if let Some(s) = &variant.data_file_name {
|
||||||
|
w.write_all(s.as_bytes())?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(w.into_inner())
|
Ok(w.into_inner())
|
||||||
|
|
|
@ -180,8 +180,6 @@ impl Bundle {
|
||||||
unpacked_size_tracked -= CHUNK_SIZE;
|
unpacked_size_tracked -= CHUNK_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
tracing::trace!(raw_size = raw_buffer.len());
|
|
||||||
|
|
||||||
decompressed.append(&mut raw_buffer);
|
decompressed.append(&mut raw_buffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -196,7 +194,11 @@ impl Bundle {
|
||||||
|
|
||||||
let mut r = Cursor::new(decompressed);
|
let mut r = Cursor::new(decompressed);
|
||||||
let mut files = Vec::with_capacity(num_entries);
|
let mut files = Vec::with_capacity(num_entries);
|
||||||
|
tracing::trace!(num_files = num_entries);
|
||||||
for (i, props) in file_props.iter().enumerate() {
|
for (i, props) in file_props.iter().enumerate() {
|
||||||
|
let span = tracing::trace_span!("Read file {}", i);
|
||||||
|
let _enter = span.enter();
|
||||||
|
|
||||||
let file = BundleFile::from_reader(ctx, &mut r, *props)
|
let file = BundleFile::from_reader(ctx, &mut r, *props)
|
||||||
.wrap_err_with(|| format!("failed to read file {i}"))?;
|
.wrap_err_with(|| format!("failed to read file {i}"))?;
|
||||||
files.push(file);
|
files.push(file);
|
||||||
|
|
Loading…
Add table
Reference in a new issue