pub mod header;
pub mod parser;
pub mod types;
pub use header::{HeaderFormatInfo, HeaderValidation, SerializedFileHeader, validate_header};
pub use parser::{FileStatistics, ParsingStats, SerializedFile, SerializedFileParser};
pub use types::{FileIdentifier, ObjectInfo, SerializedType, TypeRegistry, class_ids};
pub type Asset = SerializedFile;
pub struct AssetProcessor {
file: Option<SerializedFile>,
}
impl AssetProcessor {
pub fn new() -> Self {
Self { file: None }
}
pub fn parse_from_bytes(&mut self, data: Vec<u8>) -> crate::error::Result<()> {
let file = SerializedFileParser::from_bytes(data)?;
self.file = Some(file);
Ok(())
}
pub fn parse_from_file<P: AsRef<std::path::Path>>(
&mut self,
path: P,
) -> crate::error::Result<()> {
let data = std::fs::read(path).map_err(|e| {
crate::error::BinaryError::generic(format!("Failed to read file: {}", e))
})?;
self.parse_from_bytes(data)
}
#[cfg(feature = "async")]
pub async fn parse_from_bytes_async(&mut self, data: Vec<u8>) -> crate::error::Result<()> {
let file = SerializedFileParser::from_bytes_async(data).await?;
self.file = Some(file);
Ok(())
}
pub fn file(&self) -> Option<&SerializedFile> {
self.file.as_ref()
}
pub fn file_mut(&mut self) -> Option<&mut SerializedFile> {
self.file.as_mut()
}
pub fn objects_of_type(&self, type_id: i32) -> Vec<&ObjectInfo> {
self.file
.as_ref()
.map(|f| f.objects_of_type(type_id))
.unwrap_or_default()
}
pub fn find_object(&self, path_id: i64) -> Option<&ObjectInfo> {
self.file.as_ref().and_then(|f| f.find_object(path_id))
}
pub fn find_type(&self, class_id: i32) -> Option<&SerializedType> {
self.file.as_ref().and_then(|f| f.find_type(class_id))
}
pub fn statistics(&self) -> Option<FileStatistics> {
self.file.as_ref().map(|f| f.statistics())
}
pub fn validate(&self) -> crate::error::Result<()> {
self.file
.as_ref()
.ok_or_else(|| crate::error::BinaryError::generic("No file loaded"))?
.validate()
}
pub fn create_type_registry(&self) -> Option<TypeRegistry> {
self.file.as_ref().map(|f| f.create_type_registry())
}
pub fn clear(&mut self) {
self.file = None;
}
pub fn has_file(&self) -> bool {
self.file.is_some()
}
pub fn unity_version(&self) -> Option<&str> {
self.file.as_ref().map(|f| f.unity_version.as_str())
}
pub fn format_version(&self) -> Option<u32> {
self.file.as_ref().map(|f| f.header.version)
}
pub fn target_platform(&self) -> Option<i32> {
self.file.as_ref().map(|f| f.target_platform)
}
}
impl Default for AssetProcessor {
fn default() -> Self {
Self::new()
}
}
pub fn create_processor() -> AssetProcessor {
AssetProcessor::default()
}
pub fn parse_serialized_file(data: Vec<u8>) -> crate::error::Result<SerializedFile> {
SerializedFileParser::from_bytes(data)
}
pub fn parse_serialized_file_from_path<P: AsRef<std::path::Path>>(
path: P,
) -> crate::error::Result<SerializedFile> {
let data = std::fs::read(path)
.map_err(|e| crate::error::BinaryError::generic(format!("Failed to read file: {}", e)))?;
SerializedFileParser::from_bytes(data)
}
#[cfg(feature = "async")]
pub async fn parse_serialized_file_async(data: Vec<u8>) -> crate::error::Result<SerializedFile> {
SerializedFileParser::from_bytes_async(data).await
}
pub fn get_file_info<P: AsRef<std::path::Path>>(path: P) -> crate::error::Result<AssetFileInfo> {
let data = std::fs::read(&path)
.map_err(|e| crate::error::BinaryError::generic(format!("Failed to read file: {}", e)))?;
let mut reader = crate::reader::BinaryReader::new(&data, crate::reader::ByteOrder::Big);
let header = SerializedFileHeader::from_reader(&mut reader)?;
reader.set_byte_order(header.byte_order());
let unity_version = if header.version >= 7 {
reader.read_cstring().unwrap_or_default()
} else {
String::new()
};
let target_platform = if header.version >= 8 {
reader.read_i32().unwrap_or(0)
} else {
0
};
Ok(AssetFileInfo {
path: path.as_ref().to_string_lossy().to_string(),
format_version: header.version,
unity_version,
target_platform,
file_size: header.file_size,
is_big_endian: header.endian != 0,
supports_type_tree: header.supports_type_trees(),
})
}
pub fn is_valid_serialized_file<P: AsRef<std::path::Path>>(path: P) -> bool {
match std::fs::read(path) {
Ok(data) => {
if data.len() < 20 {
return false;
}
let mut reader = crate::reader::BinaryReader::new(&data, crate::reader::ByteOrder::Big);
match SerializedFileHeader::from_reader(&mut reader) {
Ok(header) => header.is_valid(),
Err(_) => false,
}
}
Err(_) => false,
}
}
#[derive(Debug, Clone)]
pub struct AssetFileInfo {
pub path: String,
pub format_version: u32,
pub unity_version: String,
pub target_platform: i32,
pub file_size: u64,
pub is_big_endian: bool,
pub supports_type_tree: bool,
}
pub fn get_supported_versions() -> Vec<u32> {
(5..=50).collect() }
pub fn is_version_supported(version: u32) -> bool {
(5..=50).contains(&version)
}
pub fn get_parsing_options(version: u32) -> ParsingOptions {
ParsingOptions {
enable_type_tree: version >= 13,
use_big_ids: version >= 14,
supports_script_types: version >= 11,
supports_ref_types: version >= 20,
uses_extended_format: version >= 22,
}
}
#[derive(Debug, Clone)]
pub struct ParsingOptions {
pub enable_type_tree: bool,
pub use_big_ids: bool,
pub supports_script_types: bool,
pub supports_ref_types: bool,
pub uses_extended_format: bool,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_processor_creation() {
let processor = create_processor();
assert!(!processor.has_file());
}
#[test]
fn test_version_support() {
assert!(is_version_supported(19));
assert!(is_version_supported(5));
assert!(!is_version_supported(100));
}
#[test]
fn test_parsing_options() {
let options = get_parsing_options(19);
assert!(options.enable_type_tree);
assert!(options.use_big_ids);
assert!(options.supports_script_types);
let old_options = get_parsing_options(10);
assert!(!old_options.enable_type_tree);
assert!(!old_options.use_big_ids);
}
#[test]
fn test_supported_versions() {
let versions = get_supported_versions();
assert!(versions.contains(&19));
assert!(versions.contains(&5));
assert!(!versions.is_empty());
}
}