unity_asset_binary/audio/
converter.rs1use super::formats::AudioCompressionFormat;
7use super::types::{AudioClip, AudioClipMeta, StreamingInfo};
8use crate::error::{BinaryError, Result};
9use crate::object::UnityObject;
10use crate::unity_version::UnityVersion;
11
12pub struct AudioClipConverter {
17 version: UnityVersion,
18}
19
20impl AudioClipConverter {
21 pub fn new(version: UnityVersion) -> Self {
23 Self { version }
24 }
25
26 pub fn from_unity_object(&self, obj: &UnityObject) -> Result<AudioClip> {
31 self.parse_binary_data(&obj.info.data)
34 }
35
36 #[allow(clippy::field_reassign_with_default)]
38 fn parse_binary_data(&self, data: &[u8]) -> Result<AudioClip> {
39 if data.is_empty() {
40 return Err(BinaryError::invalid_data("Empty audio data"));
41 }
42
43 let mut reader = crate::reader::BinaryReader::new(data, crate::reader::ByteOrder::Little);
44 let mut clip = AudioClip::default();
45
46 clip.name = reader
48 .read_aligned_string()
49 .unwrap_or_else(|_| "UnknownAudio".to_string());
50
51 if self.version.major < 5 {
53 let format = reader.read_i32().unwrap_or(0);
55 let type_ = reader.read_i32().unwrap_or(0);
56 let is_3d = reader.read_bool().unwrap_or(false);
57 let use_hardware = reader.read_bool().unwrap_or(false);
58
59 clip.meta = AudioClipMeta::Legacy {
60 format,
61 type_,
62 is_3d,
63 use_hardware,
64 };
65 } else {
66 let load_type = reader.read_i32().unwrap_or(0);
68 let channels = reader.read_i32().unwrap_or(2);
69 let frequency = reader.read_i32().unwrap_or(44100);
70 let bits_per_sample = reader.read_i32().unwrap_or(16);
71 let length = reader.read_f32().unwrap_or(0.0);
72 let is_tracker_format = reader.read_bool().unwrap_or(false);
73 let subsound_index = reader.read_i32().unwrap_or(0);
74 let preload_audio_data = reader.read_bool().unwrap_or(true);
75 let load_in_background = reader.read_bool().unwrap_or(false);
76 let legacy_3d = reader.read_bool().unwrap_or(false);
77
78 let compression_format_val = reader.read_i32().unwrap_or(0);
79 let compression_format = AudioCompressionFormat::from(compression_format_val);
80
81 clip.meta = AudioClipMeta::Modern {
82 load_type,
83 channels,
84 frequency,
85 bits_per_sample,
86 length,
87 is_tracker_format,
88 subsound_index,
89 preload_audio_data,
90 load_in_background,
91 legacy_3d,
92 compression_format,
93 };
94
95 if self.version.major >= 2017 {
97 clip.ambisonic = reader.read_bool().ok();
98 }
99 }
100
101 let stream_offset = reader.read_u64().unwrap_or(0);
103 let stream_size = reader.read_u32().unwrap_or(0);
104 let stream_path = reader.read_aligned_string().unwrap_or_default();
105
106 clip.stream_info = StreamingInfo {
107 offset: stream_offset,
108 size: stream_size,
109 path: stream_path,
110 };
111
112 let data_size = reader.read_u32().unwrap_or(0);
114 if data_size > 0 && reader.remaining() >= data_size as usize {
115 clip.data = reader.read_bytes(data_size as usize).unwrap_or_default();
116 } else if reader.remaining() > 0 {
117 let remaining_data = reader.read_remaining();
119 clip.data = remaining_data.to_vec();
120 }
121
122 clip.size = clip.data.len() as u64;
123
124 Ok(clip)
125 }
126
127 pub fn supported_formats(&self) -> Vec<AudioCompressionFormat> {
129 let mut formats = vec![
130 AudioCompressionFormat::PCM,
131 AudioCompressionFormat::Vorbis,
132 AudioCompressionFormat::ADPCM,
133 ];
134
135 if self.version.major >= 4 {
137 formats.push(AudioCompressionFormat::MP3);
138 }
139
140 if self.version.major >= 5 {
141 formats.push(AudioCompressionFormat::AAC);
142 }
143
144 formats
150 }
151
152 pub fn can_process(&self, format: AudioCompressionFormat) -> bool {
154 self.supported_formats().contains(&format)
155 }
156
157 pub fn load_streaming_data(&self, clip: &AudioClip) -> Result<Vec<u8>> {
159 if clip.stream_info.path.is_empty() {
160 return Err(BinaryError::invalid_data("No streaming path specified"));
161 }
162
163 use std::fs;
165 use std::path::Path;
166
167 let stream_path = Path::new(&clip.stream_info.path);
168
169 let possible_paths = [
171 stream_path.to_path_buf(),
172 Path::new("StreamingAssets").join(stream_path),
173 Path::new("..").join(stream_path),
174 ];
175
176 for path in &possible_paths {
177 if path.exists() {
178 match fs::File::open(path) {
179 Ok(mut file) => {
180 use std::io::{Read, Seek, SeekFrom};
181
182 if file.seek(SeekFrom::Start(clip.stream_info.offset)).is_err() {
184 continue; }
186
187 let mut buffer = vec![0u8; clip.stream_info.size as usize];
189 match file.read_exact(&mut buffer) {
190 Ok(_) => return Ok(buffer),
191 Err(_) => continue, }
193 }
194 Err(_) => continue, }
196 }
197 }
198
199 Err(BinaryError::generic(format!(
200 "Could not load streaming data from: {}",
201 clip.stream_info.path
202 )))
203 }
204
205 pub fn get_audio_data(&self, clip: &AudioClip) -> Result<Vec<u8>> {
207 if !clip.data.is_empty() {
208 Ok(clip.data.clone())
210 } else if clip.is_streamed() {
211 self.load_streaming_data(clip)
213 } else {
214 Err(BinaryError::invalid_data("No audio data available"))
215 }
216 }
217}
218
219pub type AudioClipProcessor = AudioClipConverter;