feat(sdk): Implement file properties
This commit is contained in:
parent
6b01511d22
commit
9077d791b2
5 changed files with 49 additions and 71 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -1934,6 +1934,7 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
|||
name = "sdk"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"byteorder",
|
||||
"color-eyre",
|
||||
"csv-async",
|
||||
|
|
|
@ -4,6 +4,7 @@ version = "0.2.0"
|
|||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
bitflags = "1.3.2"
|
||||
byteorder = "1.4.3"
|
||||
color-eyre = "0.6.2"
|
||||
csv-async = { version = "1.2.4", features = ["tokio", "serde"] }
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use std::io::{Cursor, Read, Seek, Write};
|
||||
use std::path::Path;
|
||||
|
||||
use bitflags::bitflags;
|
||||
use color_eyre::eyre::Context;
|
||||
use color_eyre::{eyre, Result};
|
||||
use futures::future::join_all;
|
||||
|
@ -489,10 +490,18 @@ impl BundleFileVariant {
|
|||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(Default)]
|
||||
pub struct Properties: u32 {
|
||||
const DATA = 0b100;
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BundleFile {
|
||||
file_type: BundleFileType,
|
||||
name: String,
|
||||
variants: Vec<BundleFileVariant>,
|
||||
props: Properties,
|
||||
}
|
||||
|
||||
impl BundleFile {
|
||||
|
@ -501,6 +510,7 @@ impl BundleFile {
|
|||
file_type,
|
||||
name,
|
||||
variants: Vec::new(),
|
||||
props: Properties::empty(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -508,12 +518,8 @@ impl BundleFile {
|
|||
self.variants.push(variant)
|
||||
}
|
||||
|
||||
#[tracing::instrument(
|
||||
name = "File::read",
|
||||
skip_all,
|
||||
fields(name = %meta.name_hash, ext = %meta.extension_hash, flags = meta.flags)
|
||||
)]
|
||||
pub fn from_reader<R>(ctx: &crate::Context, r: &mut R, meta: &EntryHeader) -> Result<Self>
|
||||
#[tracing::instrument(name = "File::read", skip(ctx, r))]
|
||||
pub fn from_reader<R>(ctx: &crate::Context, r: &mut R, props: Properties) -> Result<Self>
|
||||
where
|
||||
R: Read + Seek,
|
||||
{
|
||||
|
@ -561,6 +567,7 @@ impl BundleFile {
|
|||
variants,
|
||||
file_type,
|
||||
name,
|
||||
props,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -617,6 +624,10 @@ impl BundleFile {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn props(&self) -> Properties {
|
||||
self.props
|
||||
}
|
||||
|
||||
pub fn base_name(&self) -> &String {
|
||||
&self.name
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use std::io::{BufReader, Cursor, Read, Seek, SeekFrom, Write};
|
||||
use std::mem::size_of;
|
||||
use std::path::Path;
|
||||
|
||||
use color_eyre::eyre::{self, Context, Result};
|
||||
|
@ -6,12 +7,13 @@ use color_eyre::{Help, Report, SectionExt};
|
|||
use oodle_sys::{OodleLZ_CheckCRC, OodleLZ_FuzzSafe, CHUNK_SIZE};
|
||||
|
||||
use crate::binary::sync::*;
|
||||
use crate::bundle::file::Properties;
|
||||
use crate::murmur::{HashGroup, Murmur64};
|
||||
|
||||
pub(crate) mod database;
|
||||
pub(crate) mod file;
|
||||
|
||||
pub use file::{BundleFile, BundleFileType};
|
||||
pub use file::{BundleFile, BundleFileType, BundleFileVariant};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
|
||||
enum BundleFormat {
|
||||
|
@ -40,56 +42,9 @@ impl From<BundleFormat> for u32 {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct EntryHeader {
|
||||
name_hash: Murmur64,
|
||||
extension_hash: Murmur64,
|
||||
flags: u32,
|
||||
}
|
||||
|
||||
impl EntryHeader {
|
||||
#[tracing::instrument(name = "EntryHeader::from_reader", skip_all)]
|
||||
fn from_reader<R>(r: &mut R) -> Result<Self>
|
||||
where
|
||||
R: Read + Seek,
|
||||
{
|
||||
let extension_hash = Murmur64::from(r.read_u64()?);
|
||||
let name_hash = Murmur64::from(r.read_u64()?);
|
||||
let flags = r.read_u32()?;
|
||||
|
||||
// NOTE: Known values so far:
|
||||
// - 0x0: seems to be the default
|
||||
// - 0x4: seems to be used for files that point to something in `data/`
|
||||
// seems to correspond to a change in value in the header's 'unknown_3'
|
||||
if flags != 0x0 {
|
||||
tracing::debug!(
|
||||
flags,
|
||||
"Unexpected meta flags for file {name_hash:016X}.{extension_hash:016X}",
|
||||
);
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
name_hash,
|
||||
extension_hash,
|
||||
flags,
|
||||
})
|
||||
}
|
||||
|
||||
#[tracing::instrument(name = "EntryHeader::to_writer", skip_all)]
|
||||
fn to_writer<W>(&self, w: &mut W) -> Result<()>
|
||||
where
|
||||
W: Write + Seek,
|
||||
{
|
||||
w.write_u64(self.extension_hash.into())?;
|
||||
w.write_u64(self.name_hash.into())?;
|
||||
w.write_u32(self.flags)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Bundle {
|
||||
format: BundleFormat,
|
||||
properties: [Murmur64; 32],
|
||||
headers: Vec<EntryHeader>,
|
||||
files: Vec<BundleFile>,
|
||||
name: String,
|
||||
}
|
||||
|
@ -100,7 +55,6 @@ impl Bundle {
|
|||
name,
|
||||
format: BundleFormat::F8,
|
||||
properties: [0.into(); 32],
|
||||
headers: Vec::new(),
|
||||
files: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
@ -119,15 +73,22 @@ impl Bundle {
|
|||
|
||||
pub fn add_file(&mut self, file: BundleFile) {
|
||||
tracing::trace!("Adding file {}", file.name(false, None));
|
||||
let header = EntryHeader {
|
||||
extension_hash: file.file_type().into(),
|
||||
name_hash: Murmur64::hash(file.base_name().as_bytes()),
|
||||
// TODO: Hard coded until we know what this is
|
||||
flags: 0x0,
|
||||
};
|
||||
let existing_index = self
|
||||
.files
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(_, f)| **f == file)
|
||||
.map(|val| val.0);
|
||||
|
||||
self.files.push(file);
|
||||
self.headers.push(header);
|
||||
|
||||
if let Some(i) = existing_index {
|
||||
self.files.swap_remove(i);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_file<S: AsRef<str>>(&self, name: S) -> Option<&BundleFile> {
|
||||
self.files.iter().find(|f| f.base_name().eq(name.as_ref()))
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(ctx, binary), fields(len_binary = binary.as_ref().len()))]
|
||||
|
@ -154,9 +115,13 @@ impl Bundle {
|
|||
*prop = Murmur64::from(r.read_u64()?);
|
||||
}
|
||||
|
||||
let mut headers = Vec::with_capacity(num_entries);
|
||||
let mut file_props = Vec::with_capacity(num_entries);
|
||||
for _ in 0..num_entries {
|
||||
headers.push(EntryHeader::from_reader(&mut r)?);
|
||||
// Skip two u64 that contain the extension hash and file name hash.
|
||||
// We don't need them here, since we're reading the whole bundle into memory
|
||||
// anyways.
|
||||
r.seek(SeekFrom::Current((2 * size_of::<u64>()) as i64))?;
|
||||
file_props.push(Properties::from_bits_truncate(r.read_u32()?));
|
||||
}
|
||||
|
||||
let num_chunks = r.read_u32()? as usize;
|
||||
|
@ -227,9 +192,8 @@ impl Bundle {
|
|||
|
||||
let mut r = Cursor::new(decompressed);
|
||||
let mut files = Vec::with_capacity(num_entries);
|
||||
for i in 0..num_entries {
|
||||
let meta = headers.get(i).unwrap();
|
||||
let file = BundleFile::from_reader(ctx, &mut r, meta)
|
||||
for (i, props) in file_props.iter().enumerate() {
|
||||
let file = BundleFile::from_reader(ctx, &mut r, *props)
|
||||
.wrap_err_with(|| format!("failed to read file {i}"))?;
|
||||
files.push(file);
|
||||
}
|
||||
|
@ -237,7 +201,6 @@ impl Bundle {
|
|||
Ok(Self {
|
||||
name: bundle_name,
|
||||
format,
|
||||
headers,
|
||||
files,
|
||||
properties,
|
||||
})
|
||||
|
@ -255,8 +218,10 @@ impl Bundle {
|
|||
w.write_u64((*prop).into())?;
|
||||
}
|
||||
|
||||
for meta in self.headers.iter() {
|
||||
meta.to_writer(&mut w)?;
|
||||
for file in self.files.iter() {
|
||||
w.write_u64(file.file_type().into())?;
|
||||
w.write_u64(Murmur64::hash(file.base_name().as_bytes()).into())?;
|
||||
w.write_u32(file.props().bits())?;
|
||||
}
|
||||
|
||||
let unpacked_data = {
|
||||
|
|
|
@ -7,5 +7,5 @@ pub mod murmur;
|
|||
pub use binary::{FromBinary, ToBinary};
|
||||
pub use bundle::database::BundleDatabase;
|
||||
pub use bundle::decompress;
|
||||
pub use bundle::{Bundle, BundleFile, BundleFileType};
|
||||
pub use bundle::{Bundle, BundleFile, BundleFileType, BundleFileVariant};
|
||||
pub use context::Context;
|
||||
|
|
Loading…
Add table
Reference in a new issue