From d5aa53fafed0dbf6d43943e3b88b99693821e5cd Mon Sep 17 00:00:00 2001 From: metamuffin Date: Sun, 23 Mar 2025 20:18:10 +0100 Subject: audio clips --- .gitignore | 3 +- Cargo.lock | 230 ++++++++++++++++++++++++++++++++++++++- Cargo.toml | 1 + exporter/Cargo.toml | 1 + exporter/src/bin/audioclips.rs | 64 +++++++++++ src/classes/audio_clip.rs | 93 ++++++++++++++++ src/classes/cubemap.rs | 2 +- src/classes/mesh.rs | 2 +- src/classes/mod.rs | 6 +- src/classes/streamed_resource.rs | 50 +++++++++ src/classes/streaming_info.rs | 51 +++++++++ src/classes/streaminginfo.rs | 52 --------- src/classes/texture2d.rs | 2 +- src/object/read.rs | 1 + 14 files changed, 497 insertions(+), 61 deletions(-) create mode 100644 exporter/src/bin/audioclips.rs create mode 100644 src/classes/audio_clip.rs create mode 100644 src/classes/streamed_resource.rs create mode 100644 src/classes/streaming_info.rs delete mode 100644 src/classes/streaminginfo.rs diff --git a/.gitignore b/.gitignore index faa49a3..835805d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ -/target +/target* +/perf* /samples /src/bin/debug.rs diff --git a/Cargo.lock b/Cargo.lock index 7279d7d..48b8fb9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -79,12 +79,28 @@ version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" +[[package]] +name = "aotuv_lancer_vorbis_sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c22da1eae146e1ac2956da45bd1856b2f9ad788e6f7af535b861df497d726a1" +dependencies = [ + "cc", + "ogg_next_sys", +] + [[package]] name = "arbitrary" version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" +[[package]] +name = "arbitrary-int" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "825297538d77367557b912770ca3083f778a196054b3ee63b22673c4a3cae0a5" + [[package]] name = "arg_enum_proc_macro" version = "0.3.4" @@ -137,6 +153,29 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +[[package]] +name = "bilge" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc707ed8ebf81de5cd6c7f48f54b4c8621760926cdf35a57000747c512e67b57" +dependencies = [ + "arbitrary-int", + "bilge-impl", +] + +[[package]] +name = "bilge-impl" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "feb11e002038ad243af39c2068c8a72bcf147acf05025dcdb916fcc000adb2d8" +dependencies = [ + "itertools 0.11.0", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "bit_field" version = "0.10.2" @@ -305,6 +344,16 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +[[package]] +name = "errno" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +dependencies = [ + "libc", + "windows-sys", +] + [[package]] name = "exporter" version = "0.1.0" @@ -315,6 +364,7 @@ dependencies = [ "glam", "gltf", "gltf-json", + "hound", "log", "serde_json", "serde_yml", @@ -355,6 +405,19 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "fsbex" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "362e570e47b98ed9966c238a72cb0b64120f9dc6d44373c35b27ae2223ca7dcc" +dependencies = [ + "bilge", + "lewton", + "phf", + "tap", + "vorbis_rs", +] + [[package]] name = "getrandom" version = "0.2.15" @@ -446,6 +509,12 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hound" +version = "3.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62adaabb884c94955b19907d60019f4e145d091c75345379e70d1ee696f7854f" + [[package]] name = "humansize" version = "2.1.3" @@ -527,6 +596,15 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.12.1" @@ -593,6 +671,16 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" +[[package]] +name = "lewton" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "777b48df9aaab155475a83a7df3070395ea1ac6902f5cd062b8f2b028075c030" +dependencies = [ + "byteorder 1.5.0", + "tinyvec", +] + [[package]] name = "libc" version = "0.2.171" @@ -762,6 +850,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "ogg_next_sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c990730c2782b922815753a62af535e4205267df190dfbd8aa5d74e11d7dcc3" +dependencies = [ + "cc", +] + [[package]] name = "once_cell" version = "1.20.3" @@ -774,6 +871,48 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_macros", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_macros" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher", +] + [[package]] name = "pkg-config" version = "0.3.32" @@ -817,6 +956,29 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro2" version = "1.0.93" @@ -913,7 +1075,7 @@ dependencies = [ "built", "cfg-if", "interpolate_name", - "itertools", + "itertools 0.12.1", "libc", "libfuzzer-sys", "log", @@ -929,7 +1091,7 @@ dependencies = [ "rand_chacha", "simd_helpers", "system-deps", - "thiserror", + "thiserror 1.0.69", "v_frame", "wasm-bindgen", ] @@ -1093,6 +1255,12 @@ dependencies = [ "quote", ] +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + [[package]] name = "smallvec" version = "1.14.0" @@ -1129,6 +1297,12 @@ dependencies = [ "version-compare", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "target-lexicon" version = "0.12.16" @@ -1158,7 +1332,16 @@ version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl 2.0.12", ] [[package]] @@ -1172,6 +1355,17 @@ dependencies = [ "syn", ] +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tiff" version = "0.9.1" @@ -1183,6 +1377,21 @@ dependencies = [ "weezl", ] +[[package]] +name = "tinyvec" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "toml" version = "0.8.20" @@ -1239,6 +1448,7 @@ version = "0.1.0" dependencies = [ "anyhow", "env_logger", + "fsbex", "glam", "humansize", "image", @@ -1286,6 +1496,20 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "vorbis_rs" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "874075ad757c0f6031d4706ed75703a9ba6c711164a0b1dab55af86800cd618f" +dependencies = [ + "aotuv_lancer_vorbis_sys", + "errno", + "getrandom", + "ogg_next_sys", + "thiserror 2.0.12", + "tinyvec", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index 702632a..462a69c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,3 +21,4 @@ glam = { version = "0.30.0", features = ["serde"] } texpresso = "2.0.1" image = "0.25.5" texture2ddecoder = { git = "https://github.com/UniversalGameExtraction/texture2ddecoder", rev = "d2b4653fda298f1da39917da86bc509b17879808" } +fsbex = "0.3.0" diff --git a/exporter/Cargo.toml b/exporter/Cargo.toml index 02eca5c..12fe97f 100644 --- a/exporter/Cargo.toml +++ b/exporter/Cargo.toml @@ -14,3 +14,4 @@ glam = { version = "0.30.0", features = ["serde"] } gltf-json = "1.4.1" gltf = "1.4.1" bytemuck = "1.22.0" +hound = "3.5.1" diff --git a/exporter/src/bin/audioclips.rs b/exporter/src/bin/audioclips.rs new file mode 100644 index 0000000..cc16a22 --- /dev/null +++ b/exporter/src/bin/audioclips.rs @@ -0,0 +1,64 @@ +#![feature(array_chunks)] +use std::{ + env::args, + fs::{File, create_dir_all}, + io::{BufReader, BufWriter, Write}, +}; +use unity_tools::{ + assetbundle::AssetBundle, + classes::audio_clip::{AudioClip, AudioCompressionFormat}, +}; + +fn main() -> anyhow::Result<()> { + env_logger::init_from_env("LOG"); + let file = BufReader::new(File::open(args().nth(1).unwrap()).unwrap()); + let mut bundle = AssetBundle::open(file, "samples")?; + + let mut i = 0; + create_dir_all("/tmp/a").unwrap(); + + for ob in bundle.all_toplevel_of_class("AudioClip") { + let clip = ob.load(&mut bundle)?.parse::()?; + + if clip.compression_format == AudioCompressionFormat::Vorbis { + let mut ogg = BufWriter::new(File::create(format!( + "/tmp/a/{}_{i}.ogg", + clip.name.replace("/", "-").replace(".", "-") + ))?); + ogg.write_all(&clip.read_ogg(&bundle.fs)?)?; + } + + // let wav = BufWriter::new(File::create(format!( + // "/tmp/a/{}_{i}.wav", + // clip.name.replace("/", "-").replace(".", "-") + // ))?); + // let mut writer = hound::WavWriter::new( + // wav, + // WavSpec { + // bits_per_sample: 32, + // channels: clip.channels as u16, + // sample_format: hound::SampleFormat::Float, + // sample_rate: clip.frequency as u32, + // }, + // )?; + + // if clip.channels == 2 { + // for (l, r) in samples[0] + // .clone() + // .into_iter() + // .zip(samples[1].clone().into_iter()) + // { + // writer.write_sample(l)?; + // writer.write_sample(r)?; + // } + // } else { + // todo!() + // } + + // writer.finalize()?; + + i += 1; + } + + Ok(()) +} diff --git a/src/classes/audio_clip.rs b/src/classes/audio_clip.rs new file mode 100644 index 0000000..18bd297 --- /dev/null +++ b/src/classes/audio_clip.rs @@ -0,0 +1,93 @@ +use super::streamed_resource::StreamedResource; +use crate::{ + object::{Value, parser::FromValue}, + unityfs::UnityFS, +}; +use anyhow::{Result, anyhow, bail}; +use log::info; +use serde::Serialize; +use std::io::{Cursor, Read, Seek}; + +#[derive(Debug, Serialize)] +pub struct AudioClip { + pub resource: StreamedResource, + pub channels: i32, + pub ambisonic: bool, + pub bits_per_sample: i32, + pub compression_format: AudioCompressionFormat, + pub frequency: i32, + pub is_tracker_format: bool, + pub legacy_3d: bool, + pub length: f32, + pub name: String, + pub subsound_index: i32, +} + +impl FromValue for AudioClip { + fn from_value(v: Value) -> Result { + let mut fields = v.as_class("AudioClip")?; + Ok(AudioClip { + resource: fields.field("m_Resource")?, + ambisonic: fields.field("m_Ambisonic")?, + bits_per_sample: fields.field("m_BitsPerSample")?, + compression_format: fields.field("m_CompressionFormat")?, + channels: fields.field("m_Channels")?, + frequency: fields.field("m_Frequency")?, + is_tracker_format: fields.field("m_IsTrackerFormat")?, + legacy_3d: fields.field("m_Legacy3D")?, + length: fields.field("m_Length")?, + name: fields.field("m_Name")?, + subsound_index: fields.field("m_SubsoundIndex")?, + }) + } +} + +impl AudioClip { + pub fn read_ogg(&self, fs: &UnityFS) -> Result> { + let data = self.resource.read(&fs)?; + match self.compression_format { + AudioCompressionFormat::Vorbis => { + info!("reading vorbis FMOD sound bank"); + let bank = fsbex::Bank::new(Cursor::new(data))?; + assert_eq!(bank.format(), fsbex::AudioFormat::Vorbis); + assert_eq!(u32::from(bank.num_streams()), 1); + let stream = bank.into_iter().next().unwrap(); + + let mut buf = Cursor::new(Vec::new()); + stream.write(&mut buf)?; + Ok(buf.into_inner()) + } + x => todo!("audio format {x:?}"), + } + } +} + +impl FromValue for AudioCompressionFormat { + fn from_value(v: Value) -> Result { + let x = v + .as_i32() + .ok_or(anyhow!("expected i32 AudioCompressionFormat"))?; + if x < 10 { + Ok(unsafe { std::mem::transmute(x) }) + } else { + bail!("AudioCompressionFormat out of range") + } + } +} + +#[allow(non_camel_case_types)] +#[repr(i32)] +#[derive(Debug, Serialize, PartialEq, Clone, Copy)] + +pub enum AudioCompressionFormat { + PCM, + Vorbis, + ADPCM, + MP3, + PSMVAG, + HEVAG, + XMA, + AAC, + GCADPCM, + ATRAC9, +} diff --git a/src/classes/cubemap.rs b/src/classes/cubemap.rs index bd80408..653ec38 100644 --- a/src/classes/cubemap.rs +++ b/src/classes/cubemap.rs @@ -1,4 +1,4 @@ -use super::{streaminginfo::StreamingInfo, texture2d::TextureFormat}; +use super::{streaming_info::StreamingInfo, texture2d::TextureFormat}; use crate::object::{Value, parser::FromValue}; use anyhow::Result; use glam::{UVec2, Vec3Swizzles, uvec2, vec2, vec3}; diff --git a/src/classes/mesh.rs b/src/classes/mesh.rs index 96e42c2..09efe66 100644 --- a/src/classes/mesh.rs +++ b/src/classes/mesh.rs @@ -1,4 +1,4 @@ -use super::streaminginfo::StreamingInfo; +use super::streaming_info::StreamingInfo; use crate::object::{Value, parser::FromValue}; use anyhow::{Result, anyhow, bail}; use glam::{Mat4, Vec2, Vec3, Vec3A, Vec4}; diff --git a/src/classes/mod.rs b/src/classes/mod.rs index c312c6a..2bba6c1 100644 --- a/src/classes/mod.rs +++ b/src/classes/mod.rs @@ -3,7 +3,7 @@ pub mod gameobject; pub mod material; pub mod mesh; pub mod pptr; -pub mod streaminginfo; +pub mod streaming_info; pub mod texture2d; pub mod transform; pub mod vectors; @@ -11,6 +11,8 @@ pub mod renderer; pub mod shader; pub mod cubemap; pub mod render_settings; +pub mod audio_clip; +pub mod streamed_resource; use crate::object::{Value, parser::FromValue}; use anyhow::Result; @@ -21,7 +23,7 @@ use mesh::Mesh; use pptr::PPtr; use serde::Serialize; use std::collections::BTreeMap; -use streaminginfo::StreamingInfo; +use streaming_info::StreamingInfo; use texture2d::Texture2D; use transform::Transform; diff --git a/src/classes/streamed_resource.rs b/src/classes/streamed_resource.rs new file mode 100644 index 0000000..945c8e1 --- /dev/null +++ b/src/classes/streamed_resource.rs @@ -0,0 +1,50 @@ +use crate::{ + object::{Value, parser::FromValue}, + unityfs::UnityFS, +}; +use anyhow::{Result, anyhow, bail}; +use serde::Serialize; +use std::io::{Read, Seek, SeekFrom}; + +#[derive(Debug, Serialize)] +pub struct StreamedResource { + pub offset: u64, + pub size: u64, + pub path: String, +} + +impl FromValue for StreamedResource { + fn from_value(v: Value) -> Result { + let mut fields = v.as_class("StreamedResource")?; + Ok(StreamedResource { + offset: fields.field("m_Offset")?, + size: fields.field("m_Size")?, + path: fields.field("m_Source")?, + }) + } +} + +impl StreamedResource { + pub fn read(&self, fs: &UnityFS) -> Result> { + if !self.path.starts_with("archive:") { + bail!( + "StreamedResource path does not start on 'archive:' ({:?})", + self.path + ) + } + let nodeinfo = fs + .header + .nodes() + .iter() + .find(|n| self.path.ends_with(&n.name)) + .ok_or(anyhow!("node with path {:?} not found", self.path))? + .to_owned(); + + let mut buf = Vec::new(); + + let mut node = fs.read(&nodeinfo)?; + node.seek(SeekFrom::Start(self.offset))?; + node.take(self.size).read_to_end(&mut buf)?; + Ok(buf) + } +} diff --git a/src/classes/streaming_info.rs b/src/classes/streaming_info.rs new file mode 100644 index 0000000..9f7ce23 --- /dev/null +++ b/src/classes/streaming_info.rs @@ -0,0 +1,51 @@ +use crate::{ + object::{Value, parser::FromValue}, + unityfs::UnityFS, +}; +use anyhow::{Result, anyhow, bail}; +use serde::Serialize; +use std::io::{Read, Seek, SeekFrom}; + +#[derive(Debug, Serialize)] +pub struct StreamingInfo { + pub offset: u64, + pub path: String, + pub size: u32, +} + +impl FromValue for StreamingInfo { + fn from_value(v: Value) -> Result { + let mut fields = v.as_class("StreamingInfo")?; + Ok(StreamingInfo { + offset: fields.field("offset")?, + size: fields.field("size")?, + path: fields.field("path")?, + }) + } +} + +impl StreamingInfo { + pub fn read(&self, fs: &UnityFS) -> Result> { + if !self.path.starts_with("archive:") { + bail!( + "StreamingInfo path does not start on 'archive:' ({:?})", + self.path + ) + } + let nodeinfo = fs + .header + .nodes() + .iter() + .find(|n| self.path.ends_with(&n.name)) + .ok_or(anyhow!("node with path {:?} not found", self.path))? + .to_owned(); + + let mut buf = Vec::new(); + + let mut node = fs.read(&nodeinfo)?; + node.seek(SeekFrom::Start(self.offset))?; + node.take(self.size as u64).read_to_end(&mut buf)?; + + Ok(buf) + } +} diff --git a/src/classes/streaminginfo.rs b/src/classes/streaminginfo.rs deleted file mode 100644 index d593a5b..0000000 --- a/src/classes/streaminginfo.rs +++ /dev/null @@ -1,52 +0,0 @@ -use std::io::{Read, Seek, SeekFrom}; - -use crate::{ - object::{Value, parser::FromValue}, - unityfs::UnityFS, -}; -use anyhow::{Result, anyhow, bail}; -use serde::Serialize; - -#[derive(Debug, Serialize)] -pub struct StreamingInfo { - pub offset: u64, - pub path: String, - pub size: u32, -} - -impl FromValue for StreamingInfo { - fn from_value(v: Value) -> Result { - let mut fields = v.as_class("StreamingInfo")?; - Ok(StreamingInfo { - offset: fields.field("offset")?, - size: fields.field("size")?, - path: fields.field("path")?, - }) - } -} - -impl StreamingInfo { - pub fn read(&self, fs: &UnityFS) -> Result> { - if !self.path.starts_with("archive:") { - bail!( - "StreamingInfo path does not start on 'archive:' ({:?})", - self.path - ) - } - let nodeinfo = fs - .header - .nodes() - .iter() - .find(|n| self.path.ends_with(&n.name)) - .ok_or(anyhow!("node with path {:?} not found", self.path))? - .to_owned(); - - let mut buf = Vec::new(); - - let mut node = fs.read(&nodeinfo)?; - node.seek(SeekFrom::Start(self.offset))?; - node.take(self.size as u64).read_to_end(&mut buf)?; - - Ok(buf) - } -} diff --git a/src/classes/texture2d.rs b/src/classes/texture2d.rs index 8f87625..41820b0 100644 --- a/src/classes/texture2d.rs +++ b/src/classes/texture2d.rs @@ -1,4 +1,4 @@ -use super::streaminginfo::StreamingInfo; +use super::streaming_info::StreamingInfo; use crate::object::{Value, parser::FromValue}; use anyhow::{Result, anyhow, bail}; use image::{DynamicImage, ImageBuffer, Luma, Rgb, Rgba}; diff --git a/src/object/read.rs b/src/object/read.rs index 013874c..1f79e30 100644 --- a/src/object/read.rs +++ b/src/object/read.rs @@ -33,6 +33,7 @@ impl Value { "SInt32" => Ok(Value::I32(data.read_i32(e)?)), "SInt64" => Ok(Value::I64(data.read_i64(e)?)), "bool" => Ok(Value::Bool(data.read_u8()? != 0)), + "FileSize" => Ok(Value::U64(data.read_u64(e)?)), "float" => { data.align(4)?; Ok(Value::F32(data.read_f32(e)?)) -- cgit v1.2.3-70-g09d2