diff --git a/unity-parser/examples/basic_parsing.rs b/unity-parser/examples/basic_parsing.rs deleted file mode 100644 index 8931a93..0000000 --- a/unity-parser/examples/basic_parsing.rs +++ /dev/null @@ -1,76 +0,0 @@ -use unity_parser::UnityFile; -use std::path::Path; - -fn main() { - // Parse a Unity prefab file - let prefab_path = Path::new("data/tests/unity-sampleproject/PiratePanic/Assets/PiratePanic/Prefabs/Menu/Battle/Hand/CardGrabber.prefab"); - - if !prefab_path.exists() { - eprintln!("Error: Unity sample project not found."); - eprintln!("Please ensure the git submodule is initialized:"); - eprintln!(" git submodule update --init --recursive"); - return; - } - - // Parse the file - match UnityFile::from_path(prefab_path) { - Ok(file) => { - println!("Successfully parsed: {:?}", file.path().file_name().unwrap()); - - // Handle the different file types - match file { - UnityFile::Prefab(prefab) => { - println!("Found {} documents\n", prefab.documents.len()); - - // List all documents - for (i, doc) in prefab.documents.iter().enumerate() { - println!("Document {}: {} (Type ID: {}, File ID: {})", - i + 1, - doc.class_name, - doc.type_id, - doc.file_id - ); - } - - println!(); - - // Find all GameObjects - let game_objects = prefab.get_documents_by_class("GameObject"); - println!("Found {} GameObjects:", game_objects.len()); - for go in game_objects { - // doc.yaml already contains the inner content (after class wrapper) - if let Some(mapping) = go.as_mapping() { - if let Some(name) = mapping.get("m_Name").and_then(|v| v.as_str()) { - println!(" - {}", name); - } - } - } - - println!(); - - // Find all Transforms - let transforms = prefab.get_documents_by_type(224); // RectTransform type ID - println!("Found {} RectTransforms", transforms.len()); - - // Look up a specific document by file ID - if let Some(first_doc) = prefab.documents.first() { - let file_id = first_doc.file_id; - if let Some(found) = prefab.get_document(file_id) { - println!("\nLooking up document by file ID {}:", file_id); - println!(" Class: {}", found.class_name); - } - } - } - UnityFile::Scene(scene) => { - println!("This is a scene file with {} entities", scene.entity_map.len()); - } - UnityFile::Asset(asset) => { - println!("This is an asset file with {} documents", asset.documents.len()); - } - } - } - Err(e) => { - eprintln!("Error parsing file: {}", e); - } - } -} diff --git a/unity-parser/examples/custom_component.rs b/unity-parser/examples/custom_component.rs deleted file mode 100644 index 820edde..0000000 --- a/unity-parser/examples/custom_component.rs +++ /dev/null @@ -1,101 +0,0 @@ -//! Example demonstrating how to define custom Unity MonoBehaviour components -//! using the #[derive(UnityComponent)] macro. - -use unity_parser::{yaml_helpers, ComponentContext, UnityComponent}; - -/// Custom Unity MonoBehaviour component for playing sound effects -/// -/// This mirrors the C# PlaySFX MonoBehaviour: -/// ```csharp -/// public class PlaySFX : MonoBehaviour -/// { -/// [SerializeField] float volume; -/// [SerializeField] float startTime; -/// [SerializeField] float endTime; -/// [SerializeField] bool isLoop; -/// } -/// ``` -#[derive(Debug, Clone, UnityComponent)] -#[unity_class("PlaySFX")] -pub struct PlaySFX { - #[unity_field("volume")] - pub volume: f64, - - #[unity_field("startTime")] - pub start_time: f64, - - #[unity_field("endTime")] - pub end_time: f64, - - #[unity_field("isLoop")] - pub is_loop: bool, -} - -/// Another example - a custom damage component -#[derive(Debug, Clone, UnityComponent)] -#[unity_class("DamageDealer")] -pub struct DamageDealer { - #[unity_field("damageAmount")] - pub damage_amount: f64, - - #[unity_field("damageType")] - pub damage_type: String, - - #[unity_field("canCrit")] - pub can_crit: bool, - - #[unity_field("critMultiplier")] - pub crit_multiplier: f64, -} - -fn main() { - println!("Custom Unity Component Example"); - println!("===============================\n"); - - println!("Defined custom components:"); - println!(" - PlaySFX: volume, start_time, end_time, is_loop"); - println!(" - DamageDealer: damage_amount, damage_type, can_crit, crit_multiplier\n"); - - println!("These components are automatically registered via the inventory crate."); - println!("When parsing Unity files, they will be recognized and parsed automatically.\n"); - - // Demonstrate parsing from YAML - let yaml_str = r#" -volume: 0.75 -startTime: 1.5 -endTime: 3.0 -isLoop: 1 -"#; - - let yaml: serde_yaml::Value = serde_yaml::from_str(yaml_str).unwrap(); - let mapping = yaml.as_mapping().unwrap(); - - // Create a dummy context - use unity_parser::{ComponentContext, FileID}; - let ctx = ComponentContext { - type_id: 114, - file_id: FileID::from_i64(12345), - class_name: "PlaySFX", - entity: None, - linking_ctx: None, - yaml: mapping, - }; - - // Parse the component - if let Some(play_sfx) = PlaySFX::parse(mapping, &ctx) { - println!("Successfully parsed PlaySFX component:"); - println!(" volume: {}", play_sfx.volume); - println!(" start_time: {}", play_sfx.start_time); - println!(" end_time: {}", play_sfx.end_time); - println!(" is_loop: {}", play_sfx.is_loop); - } else { - println!("Failed to parse PlaySFX component"); - } - - println!("\nTo use in your own code:"); - println!(" 1. Define a struct matching your C# MonoBehaviour fields"); - println!(" 2. Add #[derive(UnityComponent)] to the struct"); - println!(" 3. Add #[unity_class(\"YourClassName\")] to specify the Unity class name"); - println!(" 4. Add #[unity_field(\"fieldName\")] to each field"); - println!(" 5. The component will be automatically registered and parsed!"); -} diff --git a/unity-parser/examples/ecs_integration.rs b/unity-parser/examples/ecs_integration.rs deleted file mode 100644 index 0dc49ef..0000000 --- a/unity-parser/examples/ecs_integration.rs +++ /dev/null @@ -1,146 +0,0 @@ -//! Example demonstrating ECS integration and selective type parsing -//! -//! This example shows: -//! 1. Custom components being automatically inserted into the ECS world -//! 2. Using the parse_with_types! macro for selective parsing -//! 3. Querying the ECS world for components - -use unity_parser::{parse_with_types, ComponentContext, EcsInsertable, FileID, TypeFilter, UnityComponent}; - -/// Custom Unity MonoBehaviour component -#[derive(Debug, Clone, UnityComponent)] -#[unity_class("PlaySFX")] -pub struct PlaySFX { - #[unity_field("volume")] - pub volume: f64, - - #[unity_field("startTime")] - pub start_time: f64, - - #[unity_field("endTime")] - pub end_time: f64, - - #[unity_field("isLoop")] - pub is_loop: bool, -} - -/// Another custom component -#[derive(Debug, Clone, UnityComponent)] -#[unity_class("Interactable")] -pub struct Interactable { - #[unity_field("interactionRadius")] - pub interaction_radius: f32, - - #[unity_field("interactionText")] - pub interaction_text: String, - - #[unity_field("canInteract")] - pub can_interact: bool, -} - -fn main() { - println!("ECS Integration & Selective Parsing Example"); - println!("{}", "=".repeat(60)); - - // Example 1: Using parse_with_types! macro - println!("\n1. Creating type filters:"); - println!("{}", "-".repeat(60)); - - let _filter_all = TypeFilter::parse_all(); - println!("✓ Filter that parses ALL types"); - - let filter_selective = parse_with_types! { - unity_types(Transform, Camera), - custom_types(PlaySFX) - }; - println!("✓ Filter for Transform, Camera, and PlaySFX only"); - - let filter_custom_only = parse_with_types! { - custom_types(PlaySFX, Interactable) - }; - println!("✓ Filter for PlaySFX and Interactable only (no Unity types)"); - - // Example 2: Demonstrating ECS insertion - println!("\n2. ECS Integration:"); - println!("{}", "-".repeat(60)); - - // Simulate parsing a PlaySFX component - let yaml_str = r#" -volume: 0.8 -startTime: 0.0 -endTime: 5.0 -isLoop: 0 -"#; - - let yaml: serde_yaml::Value = serde_yaml::from_str(yaml_str).unwrap(); - let mapping = yaml.as_mapping().unwrap(); - - let ctx = ComponentContext { - type_id: 114, - file_id: FileID::from_i64(12345), - class_name: "PlaySFX", - entity: None, - linking_ctx: None, - yaml: mapping, - }; - - // Parse the component - if let Some(play_sfx) = PlaySFX::parse(mapping, &ctx) { - println!("✓ Parsed PlaySFX component:"); - println!(" - volume: {}", play_sfx.volume); - println!(" - start_time: {}", play_sfx.start_time); - println!(" - end_time: {}", play_sfx.end_time); - println!(" - is_loop: {}", play_sfx.is_loop); - - // Create a minimal ECS world to demonstrate insertion - use sparsey::World; - let mut world = World::builder().register::().build(); - let entity = world.create(()); - - println!("\n✓ Created ECS entity: {:?}", entity); - - // Insert the component into the world - play_sfx.clone().insert_into_world(&mut world, entity); - println!("✓ Inserted PlaySFX component into ECS world"); - - // Query it back - { - let view = world.borrow::(); - if let Some(component) = view.get(entity) { - println!("✓ Successfully queried component from ECS:"); - println!(" - volume: {}", component.volume); - } - } - } - - // Example 3: Type filter usage - println!("\n3. Type Filter Behavior:"); - println!("{}", "-".repeat(60)); - - println!("Filter checks:"); - println!(" Transform in selective filter: {}", filter_selective.should_parse_unity("Transform")); - println!(" Camera in selective filter: {}", filter_selective.should_parse_unity("Camera")); - println!(" Light in selective filter: {}", filter_selective.should_parse_unity("Light")); - println!(" PlaySFX in selective filter: {}", filter_selective.should_parse_custom("PlaySFX")); - println!(" Interactable in selective filter: {}", filter_selective.should_parse_custom("Interactable")); - - println!("\n PlaySFX in custom-only filter: {}", filter_custom_only.should_parse_custom("PlaySFX")); - println!(" Transform in custom-only filter: {}", filter_custom_only.should_parse_unity("Transform")); - - // Example 4: Benefits of selective parsing - println!("\n4. Benefits of Selective Parsing:"); - println!("{}", "-".repeat(60)); - println!("When parsing a large Unity project:"); - println!(" • Parse ALL types: Parse everything (default)"); - println!(" • Parse specific types: Faster parsing & less memory"); - println!(" • Parse only what you need for your tool/analysis"); - println!("\nExample use cases:"); - println!(" • Animation tool: Only parse Animator, AnimationClip"); - println!(" • Audio tool: Only parse AudioSource, PlaySFX"); - println!(" • Transform analyzer: Only parse Transform, RectTransform"); - - println!(); - println!("{}", "=".repeat(60)); - println!("Complete! Custom components now work with ECS!"); - println!("{}", "=".repeat(60)); -} diff --git a/unity-parser/examples/find_playsfx.rs b/unity-parser/examples/find_playsfx.rs deleted file mode 100644 index 18a83f3..0000000 --- a/unity-parser/examples/find_playsfx.rs +++ /dev/null @@ -1,194 +0,0 @@ -//! Demo: Find all PlaySFX components and their locations in VR_Horror_YouCantRun -//! -//! This example demonstrates: -//! 1. Parsing a real Unity project -//! 2. Finding custom MonoBehaviour components (PlaySFX) -//! 3. Querying the ECS world for components -//! 4. Accessing Transform data for component locations - -use unity_parser::{UnityComponent, UnityFile}; -use std::path::Path; - -/// PlaySFX component from VR_Horror_YouCantRun -/// -/// C# definition: -/// ```csharp -/// public class PlaySFX : MonoBehaviour -/// { -/// [SerializeField] float volume; -/// [SerializeField] float startTime; -/// [SerializeField] float endTime; -/// [SerializeField] bool isLoop; -/// } -/// ``` -#[derive(Debug, Clone, UnityComponent)] -#[unity_class("PlaySFX")] -pub struct PlaySFX { - #[unity_field("volume")] - pub volume: f64, - - #[unity_field("startTime")] - pub start_time: f64, - - #[unity_field("endTime")] - pub end_time: f64, - - #[unity_field("isLoop")] - pub is_loop: bool, -} - -fn main() -> Result<(), Box> { - println!("🎮 VR Horror - PlaySFX Component Finder"); - println!("{}", "=".repeat(70)); - println!(); - - let project_path = Path::new("test_data/VR_Horror_YouCantRun"); - - // Check if project exists - if !project_path.exists() { - eprintln!("❌ Error: VR_Horror_YouCantRun project not found at {}", project_path.display()); - eprintln!(" Run the integration tests first to download it:"); - eprintln!(" cargo test test_vr_horror_project"); - return Ok(()); - } - - println!("📁 Scanning project: {}", project_path.display()); - println!(); - - // Find all Unity scene files - let scene_files = find_unity_files(project_path, "unity"); - - println!("📄 Found {} scene file(s)", scene_files.len()); - println!(); - - let mut total_playsfx = 0; - - // Parse each scene - for scene_path in scene_files { - println!("🔍 Parsing: {}", scene_path.file_name().unwrap().to_string_lossy()); - - match UnityFile::from_path(&scene_path) { - Ok(UnityFile::Scene(scene)) => { - // Get views for all component types we need - let playsfx_view = scene.world.borrow::(); - let transform_view = scene.world.borrow::(); - let rect_transform_view = scene.world.borrow::(); - let gameobject_view = scene.world.borrow::(); - - // Find all entities that have PlaySFX - let mut found_count = 0; - let mut found_entities = Vec::new(); - - // Iterate through all entities in the entity_map - for entity in scene.entity_map.values() { - if let Some(playsfx) = playsfx_view.get(*entity) { - found_entities.push((*entity, playsfx.clone())); - found_count += 1; - } - } - - if found_count > 0 { - println!(" ✅ Found {} PlaySFX component(s)", found_count); - total_playsfx += found_count; - - // Process each found PlaySFX component - for (entity, playsfx) in found_entities { - let transform = transform_view.get(entity); - let rect_transform = rect_transform_view.get(entity); - let game_object = gameobject_view.get(entity); - - let name = game_object - .and_then(|go| go.name()) - .unwrap_or("(unnamed)"); - - println!(); - println!(" 🔊 PlaySFX on GameObject: \"{}\"", name); - println!(" Entity: {:?}", entity); - println!(" Properties:"); - println!(" • volume: {}", playsfx.volume); - println!(" • startTime: {}", playsfx.start_time); - println!(" • endTime: {}", playsfx.end_time); - println!(" • isLoop: {}", playsfx.is_loop); - - // Print position if available - if let Some(transform) = transform { - if let Some(pos) = transform.local_position() { - println!(" Transform:"); - println!(" • Position: ({:.2}, {:.2}, {:.2})", - pos.x, pos.y, pos.z); - } - if let Some(rot) = transform.local_rotation() { - println!(" • Rotation: ({:.2}, {:.2}, {:.2}, {:.2})", - rot.x, rot.y, rot.z, rot.w); - } - if let Some(scale) = transform.local_scale() { - println!(" • Scale: ({:.2}, {:.2}, {:.2})", - scale.x, scale.y, scale.z); - } - } else if let Some(rect_transform) = rect_transform { - let transform = rect_transform.transform(); - if let Some(pos) = transform.local_position() { - println!(" RectTransform (UI):"); - println!(" • Position: ({:.2}, {:.2}, {:.2})", - pos.x, pos.y, pos.z); - } - } else { - println!(" ⚠️ No Transform found"); - } - } - } else { - println!(" ⊘ No PlaySFX components found"); - } - - println!(); - } - Ok(_) => { - println!(" ⊘ Skipped (not a scene file)"); - println!(); - } - Err(e) => { - println!(" ❌ Parse error: {}", e); - println!(); - } - } - } - - println!("{}", "=".repeat(70)); - println!("📊 Summary:"); - println!(" Total PlaySFX components found: {}", total_playsfx); - println!("{}", "=".repeat(70)); - - Ok(()) -} - -/// Find all Unity files with a specific extension in a directory -fn find_unity_files(dir: &Path, extension: &str) -> Vec { - let mut files = Vec::new(); - - fn visit_dir(dir: &Path, extension: &str, files: &mut Vec) { - if let Ok(entries) = std::fs::read_dir(dir) { - for entry in entries.flatten() { - let path = entry.path(); - - // Skip Library, Temp, Builds, and .git directories - if let Some(name) = path.file_name().and_then(|n| n.to_str()) { - if name == "Library" || name == "Temp" || name == "Builds" || name == ".git" { - continue; - } - } - - if path.is_dir() { - visit_dir(&path, extension, files); - } else if let Some(ext) = path.extension().and_then(|e| e.to_str()) { - if ext == extension { - files.push(path); - } - } - } - } - } - - visit_dir(dir, extension, &mut files); - files.sort(); - files -} diff --git a/unity-parser/examples/parse_resource_prefabs.rs b/unity-parser/examples/parse_resource_prefabs.rs deleted file mode 100644 index 9753b8a..0000000 --- a/unity-parser/examples/parse_resource_prefabs.rs +++ /dev/null @@ -1,218 +0,0 @@ -//! Parse Cursebreaker Resource Prefabs -//! -//! This example demonstrates: -//! 1. Parsing Cursebreaker prefab files directly -//! 2. Finding Interactable_Resource components in prefabs -//! 3. Extracting typeId and maxHealth data -//! 4. Writing resource data to an output file -//! -//! Note: The 10_3.unity scene uses prefab instances, and the current parser -//! doesn't yet support resolving components from nested prefabs. This example -//! parses the prefab files directly instead. - -use unity_parser::{GuidResolver, UnityComponent, UnityFile}; -use std::fs::File; -use std::io::Write; -use std::path::Path; -use walkdir::WalkDir; - -/// Interactable_Resource component from Cursebreaker -/// -/// C# definition from Interactable_Resource.cs: -/// ```csharp -/// public class Interactable_Resource : Interactable -/// { -/// public int health; -/// public int maxHealth; -/// public int typeId; -/// // ... other fields -/// } -/// ``` -#[derive(Debug, Clone, UnityComponent)] -#[unity_class("Interactable_Resource")] -pub struct InteractableResource { - #[unity_field("maxHealth")] - pub max_health: i64, - - #[unity_field("typeId")] - pub type_id: i64, -} - -fn main() -> Result<(), Box> { - println!("🎮 Cursebreaker - Resource Prefab Parser"); - println!("{}", "=".repeat(70)); - println!(); - - // Build GUID resolver for the project - let project_path = Path::new("/home/connor/repos/CBAssets"); - println!("📦 Building GUID resolver for project: {}", project_path.display()); - - let resolver = match GuidResolver::from_project(project_path) { - Ok(r) => { - println!(" ✅ GUID resolver built successfully ({} mappings)", r.len()); - - // Debug: Check if we have Interactable_Resource - if let Some(class) = r.resolve_class_name("d39ddbf1c2c3d1a4baa070e5e76548bd") { - println!(" ✅ Found Interactable_Resource in resolver: {}", class); - } else { - println!(" ⚠️ Interactable_Resource NOT found in resolver"); - // Try to find what we did find related to "Interactable" - println!(" Searching for similar class names..."); - } - - Some(r) - } - Err(e) => { - eprintln!(" ❌ Failed to build GUID resolver: {}", e); - None - } - }; - println!(); - - let harvestables_dir = Path::new("/home/connor/repos/CBAssets/_GameAssets/Prefabs/Harvestables"); - - if !harvestables_dir.exists() { - eprintln!("❌ Error: Harvestables directory not found at {}", harvestables_dir.display()); - return Err("Harvestables directory not found".into()); - } - - println!("📁 Scanning for harvestable prefabs in:"); - println!(" {}", harvestables_dir.display()); - println!(); - - // Find all prefab files - let mut prefab_files = Vec::new(); - for entry in WalkDir::new(harvestables_dir) - .follow_links(false) - .into_iter() - .filter_map(|e| e.ok()) - { - let path = entry.path(); - if path.extension().and_then(|s| s.to_str()) == Some("prefab") { - prefab_files.push(path.to_path_buf()); - } - } - - println!("📄 Found {} prefab file(s)", prefab_files.len()); - println!(); - - let mut all_resources = Vec::new(); - - // Parse each prefab using the GUID resolver we built - for prefab_path in &prefab_files { - println!("🔍 Parsing: {}", prefab_path.file_name().unwrap().to_string_lossy()); - - // For prefabs, we need to manually parse and check documents - // since prefabs don't have an ECS world like scenes do - match UnityFile::from_path(prefab_path) { - Ok(UnityFile::Prefab(prefab)) => { - // Search through YAML documents for Interactable_Resource components - let mut found_in_prefab = false; - - for doc in &prefab.documents { - // Check if this document is a MonoBehaviour - if doc.class_name == "MonoBehaviour" { - // Try to extract the m_Script GUID - if let Some(m_script) = doc.yaml.get("m_Script").and_then(|v| v.as_mapping()) { - if let Some(guid_val) = m_script.get("guid").and_then(|v| v.as_str()) { - // Resolve GUID to class name - if let Some(ref res) = resolver { - if let Some(class_name) = res.resolve_class_name(guid_val) { - // Debug: print what we found - if prefab_path.file_name().unwrap().to_string_lossy().contains("Copper Ore") { - eprintln!("DEBUG: Found class '{}' in Copper Ore prefab", class_name); - } - - if class_name == "Interactable_Resource" { - // Extract fields - let type_id = doc.yaml.get("typeId") - .and_then(|v| v.as_i64()) - .unwrap_or(0); - let max_health = doc.yaml.get("maxHealth") - .and_then(|v| v.as_i64()) - .unwrap_or(0); - - let prefab_name = prefab_path - .file_stem() - .and_then(|s| s.to_str()) - .unwrap_or("unknown"); - - all_resources.push(( - prefab_name.to_string(), - type_id, - max_health, - )); - - found_in_prefab = true; - } - } else if prefab_path.file_name().unwrap().to_string_lossy().contains("Copper Ore") { - eprintln!("DEBUG: Could not resolve GUID '{}' in Copper Ore prefab", guid_val); - } - } - } - } - } - } - - if found_in_prefab { - println!(" ✅ Found Interactable_Resource"); - } else { - println!(" ⊘ No Interactable_Resource found"); - } - } - Ok(_) => { - println!(" ⊘ Not a prefab file"); - } - Err(e) => { - println!(" ❌ Parse error: {}", e); - } - } - } - - println!(); - println!("{}", "=".repeat(70)); - println!("📊 Summary: Found {} resource(s)", all_resources.len()); - println!("{}", "=".repeat(70)); - println!(); - - if !all_resources.is_empty() { - // Display resources - for (name, type_id, max_health) in &all_resources { - println!(" 📦 Prefab: \"{}\"", name); - println!(" • typeId: {}", type_id); - println!(" • maxHealth: {}", max_health); - println!(); - } - - // Write to output file - let output_path = "resource_prefabs_output.txt"; - let mut output_file = File::create(output_path)?; - - writeln!(output_file, "Cursebreaker Resource Prefabs")?; - writeln!(output_file, "{}", "=".repeat(70))?; - writeln!(output_file)?; - writeln!(output_file, "Total resources found: {}", all_resources.len())?; - writeln!(output_file)?; - writeln!(output_file, "{}", "-".repeat(70))?; - writeln!(output_file)?; - - for (name, type_id, max_health) in &all_resources { - writeln!(output_file, "Prefab: {}", name)?; - writeln!(output_file, " TypeID: {}", type_id)?; - writeln!(output_file, " MaxHealth: {}", max_health)?; - writeln!(output_file)?; - } - - writeln!(output_file, "{}", "=".repeat(70))?; - writeln!(output_file, "End of resource data")?; - - println!("📝 Resource data written to: {}", output_path); - } - - println!(); - println!("{}", "=".repeat(70)); - println!("✅ Parsing complete!"); - println!("{}", "=".repeat(70)); - - Ok(()) -} diff --git a/unity-parser/examples/parse_resources.rs b/unity-parser/examples/parse_resources.rs deleted file mode 100644 index c2a186c..0000000 --- a/unity-parser/examples/parse_resources.rs +++ /dev/null @@ -1,147 +0,0 @@ -//! Parse Cursebreaker Resources from 10_3.unity Scene -//! -//! This example demonstrates: -//! 1. Parsing the Cursebreaker Unity project -//! 2. Finding Interactable_Resource components -//! 3. Extracting typeId and transform positions -//! 4. Writing resource data to an output file - -use unity_parser::{UnityComponent, UnityFile}; -use std::fs::File; -use std::io::Write; -use std::path::Path; - -/// Interactable_Resource component from Cursebreaker -/// -/// C# definition from Interactable_Resource.cs: -/// ```csharp -/// public class Interactable_Resource : Interactable -/// { -/// public int health; -/// public int maxHealth; -/// public int typeId; -/// // ... other fields -/// } -/// ``` -#[derive(Debug, Clone, UnityComponent)] -#[unity_class("Interactable_Resource")] -pub struct InteractableResource { - #[unity_field("maxHealth")] - pub max_health: i64, - - #[unity_field("typeId")] - pub type_id: i64, -} - -fn main() -> Result<(), Box> { - println!("🎮 Cursebreaker - Resource Parser"); - println!("{}", "=".repeat(70)); - println!(); - - let scene_path = Path::new("/home/connor/repos/CBAssets/_GameAssets/Scenes/Tiles/10_3.unity"); - - // Check if scene exists - if !scene_path.exists() { - eprintln!("❌ Error: Scene not found at {}", scene_path.display()); - return Err("Scene file not found".into()); - } - - println!("📁 Parsing scene: {}", scene_path.display()); - println!(); - - // Parse the scene - match UnityFile::from_path(&scene_path) { - Ok(UnityFile::Scene(scene)) => { - println!("✅ Scene parsed successfully!"); - println!(" Total entities: {}", scene.entity_map.len()); - println!(); - - // Get views for component types we need - let resource_view = scene.world.borrow::(); - let transform_view = scene.world.borrow::(); - let gameobject_view = scene.world.borrow::(); - - // Find all entities that have Interactable_Resource - let mut found_resources = Vec::new(); - - for entity in scene.entity_map.values() { - if let Some(resource) = resource_view.get(*entity) { - let transform = transform_view.get(*entity); - let game_object = gameobject_view.get(*entity); - - let name = game_object - .and_then(|go| go.name()) - .unwrap_or("(unnamed)"); - - let position = transform - .and_then(|t| t.local_position()) - .map(|p| (p.x, p.y, p.z)); - - found_resources.push((name.to_string(), resource.clone(), position)); - } - } - - println!("🔍 Found {} Interactable_Resource component(s)", found_resources.len()); - println!(); - - if !found_resources.is_empty() { - // Display resources in console - for (name, resource, position) in &found_resources { - println!(" 📦 Resource: \"{}\"", name); - println!(" • typeId: {}", resource.type_id); - println!(" • maxHealth: {}", resource.max_health); - if let Some((x, y, z)) = position { - println!(" • Position: ({:.2}, {:.2}, {:.2})", x, y, z); - } else { - println!(" • Position: (no transform)"); - } - println!(); - } - - // Write to output file - let output_path = "resources_output.txt"; - let mut output_file = File::create(output_path)?; - - writeln!(output_file, "Cursebreaker Resources - 10_3.unity Scene")?; - writeln!(output_file, "{}", "=".repeat(70))?; - writeln!(output_file)?; - writeln!(output_file, "Total resources found: {}", found_resources.len())?; - writeln!(output_file)?; - writeln!(output_file, "{}", "-".repeat(70))?; - writeln!(output_file)?; - - for (name, resource, position) in &found_resources { - writeln!(output_file, "Resource: {}", name)?; - writeln!(output_file, " TypeID: {}", resource.type_id)?; - writeln!(output_file, " MaxHealth: {}", resource.max_health)?; - if let Some((x, y, z)) = position { - writeln!(output_file, " Position: ({:.6}, {:.6}, {:.6})", x, y, z)?; - } else { - writeln!(output_file, " Position: N/A")?; - } - writeln!(output_file)?; - } - - writeln!(output_file, "{}", "=".repeat(70))?; - writeln!(output_file, "End of resource data")?; - - println!("📝 Resource data written to: {}", output_path); - println!(); - } - - println!("{}", "=".repeat(70)); - println!("✅ Parsing complete!"); - println!("{}", "=".repeat(70)); - } - Ok(_) => { - eprintln!("❌ Error: File is not a scene"); - return Err("Not a Unity scene file".into()); - } - Err(e) => { - eprintln!("❌ Parse error: {}", e); - return Err(Box::new(e)); - } - } - - Ok(()) -}