Darktide Mod Manager #39

Merged
lucas merged 91 commits from feat/dtmm into master 2023-03-01 22:27:42 +01:00
5 changed files with 49 additions and 71 deletions
Showing only changes of commit 9077d791b2 - Show all commits

1
Cargo.lock generated
View file

@ -1934,6 +1934,7 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
name = "sdk" name = "sdk"
version = "0.2.0" version = "0.2.0"
dependencies = [ dependencies = [
"bitflags",
"byteorder", "byteorder",
"color-eyre", "color-eyre",
"csv-async", "csv-async",

View file

@ -4,6 +4,7 @@ version = "0.2.0"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
bitflags = "1.3.2"
byteorder = "1.4.3" byteorder = "1.4.3"
color-eyre = "0.6.2" color-eyre = "0.6.2"
csv-async = { version = "1.2.4", features = ["tokio", "serde"] } csv-async = { version = "1.2.4", features = ["tokio", "serde"] }

View file

@ -1,6 +1,7 @@
use std::io::{Cursor, Read, Seek, Write}; use std::io::{Cursor, Read, Seek, Write};
use std::path::Path; use std::path::Path;
use bitflags::bitflags;
use color_eyre::eyre::Context; use color_eyre::eyre::Context;
use color_eyre::{eyre, Result}; use color_eyre::{eyre, Result};
use futures::future::join_all; use futures::future::join_all;
@ -489,10 +490,18 @@ impl BundleFileVariant {
} }
} }
bitflags! {
#[derive(Default)]
pub struct Properties: u32 {
const DATA = 0b100;
}
}
pub struct BundleFile { pub struct BundleFile {
file_type: BundleFileType, file_type: BundleFileType,
name: String, name: String,
variants: Vec<BundleFileVariant>, variants: Vec<BundleFileVariant>,
props: Properties,
} }
impl BundleFile { impl BundleFile {
@ -501,6 +510,7 @@ impl BundleFile {
file_type, file_type,
name, name,
variants: Vec::new(), variants: Vec::new(),
props: Properties::empty(),
} }
} }
@ -508,12 +518,8 @@ impl BundleFile {
self.variants.push(variant) self.variants.push(variant)
} }
#[tracing::instrument( #[tracing::instrument(name = "File::read", skip(ctx, r))]
name = "File::read", pub fn from_reader<R>(ctx: &crate::Context, r: &mut R, props: Properties) -> Result<Self>
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>
where where
R: Read + Seek, R: Read + Seek,
{ {
@ -561,6 +567,7 @@ impl BundleFile {
variants, variants,
file_type, file_type,
name, name,
props,
}) })
} }
@ -617,6 +624,10 @@ impl BundleFile {
} }
} }
pub fn props(&self) -> Properties {
self.props
}
pub fn base_name(&self) -> &String { pub fn base_name(&self) -> &String {
&self.name &self.name
} }

View file

@ -1,4 +1,5 @@
use std::io::{BufReader, Cursor, Read, Seek, SeekFrom, Write}; use std::io::{BufReader, Cursor, Read, Seek, SeekFrom, Write};
use std::mem::size_of;
use std::path::Path; use std::path::Path;
use color_eyre::eyre::{self, Context, Result}; 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 oodle_sys::{OodleLZ_CheckCRC, OodleLZ_FuzzSafe, CHUNK_SIZE};
use crate::binary::sync::*; use crate::binary::sync::*;
use crate::bundle::file::Properties;
use crate::murmur::{HashGroup, Murmur64}; use crate::murmur::{HashGroup, Murmur64};
pub(crate) mod database; pub(crate) mod database;
pub(crate) mod file; pub(crate) mod file;
pub use file::{BundleFile, BundleFileType}; pub use file::{BundleFile, BundleFileType, BundleFileVariant};
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)] #[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
enum BundleFormat { 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 { pub struct Bundle {
format: BundleFormat, format: BundleFormat,
properties: [Murmur64; 32], properties: [Murmur64; 32],
headers: Vec<EntryHeader>,
files: Vec<BundleFile>, files: Vec<BundleFile>,
name: String, name: String,
} }
@ -100,7 +55,6 @@ impl Bundle {
name, name,
format: BundleFormat::F8, format: BundleFormat::F8,
properties: [0.into(); 32], properties: [0.into(); 32],
headers: Vec::new(),
files: Vec::new(), files: Vec::new(),
} }
} }
@ -119,15 +73,22 @@ impl Bundle {
pub fn add_file(&mut self, file: BundleFile) { pub fn add_file(&mut self, file: BundleFile) {
tracing::trace!("Adding file {}", file.name(false, None)); tracing::trace!("Adding file {}", file.name(false, None));
let header = EntryHeader { let existing_index = self
extension_hash: file.file_type().into(), .files
name_hash: Murmur64::hash(file.base_name().as_bytes()), .iter()
// TODO: Hard coded until we know what this is .enumerate()
flags: 0x0, .find(|(_, f)| **f == file)
}; .map(|val| val.0);
self.files.push(file); 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()))] #[tracing::instrument(skip(ctx, binary), fields(len_binary = binary.as_ref().len()))]
@ -154,9 +115,13 @@ impl Bundle {
*prop = Murmur64::from(r.read_u64()?); *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 { 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; let num_chunks = r.read_u32()? as usize;
@ -227,9 +192,8 @@ 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);
for i in 0..num_entries { for (i, props) in file_props.iter().enumerate() {
let meta = headers.get(i).unwrap(); let file = BundleFile::from_reader(ctx, &mut r, *props)
let file = BundleFile::from_reader(ctx, &mut r, meta)
.wrap_err_with(|| format!("failed to read file {i}"))?; .wrap_err_with(|| format!("failed to read file {i}"))?;
files.push(file); files.push(file);
} }
@ -237,7 +201,6 @@ impl Bundle {
Ok(Self { Ok(Self {
name: bundle_name, name: bundle_name,
format, format,
headers,
files, files,
properties, properties,
}) })
@ -255,8 +218,10 @@ impl Bundle {
w.write_u64((*prop).into())?; w.write_u64((*prop).into())?;
} }
for meta in self.headers.iter() { for file in self.files.iter() {
meta.to_writer(&mut w)?; 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 = { let unpacked_data = {

View file

@ -7,5 +7,5 @@ pub mod murmur;
pub use binary::{FromBinary, ToBinary}; pub use binary::{FromBinary, ToBinary};
pub use bundle::database::BundleDatabase; pub use bundle::database::BundleDatabase;
pub use bundle::decompress; pub use bundle::decompress;
pub use bundle::{Bundle, BundleFile, BundleFileType}; pub use bundle::{Bundle, BundleFile, BundleFileType, BundleFileVariant};
pub use context::Context; pub use context::Context;