aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/classes/audio_clip.rs93
-rw-r--r--src/classes/cubemap.rs2
-rw-r--r--src/classes/mesh.rs2
-rw-r--r--src/classes/mod.rs6
-rw-r--r--src/classes/streamed_resource.rs50
-rw-r--r--src/classes/streaming_info.rs (renamed from src/classes/streaminginfo.rs)3
-rw-r--r--src/classes/texture2d.rs2
-rw-r--r--src/object/read.rs1
8 files changed, 152 insertions, 7 deletions
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<Self> {
+ 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<impl Read + Seek>) -> Result<Vec<u8>> {
+ 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<Self> {
+ 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<Self> {
+ 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<impl Read + Seek>) -> Result<Vec<u8>> {
+ 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/streaminginfo.rs b/src/classes/streaming_info.rs
index d593a5b..9f7ce23 100644
--- a/src/classes/streaminginfo.rs
+++ b/src/classes/streaming_info.rs
@@ -1,11 +1,10 @@
-use std::io::{Read, Seek, SeekFrom};
-
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 {
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)?))