Files
cursebreaker-parser-rust/unity-parser/examples/parse_resource_prefabs.rs
2026-01-03 14:04:12 +00:00

219 lines
8.4 KiB
Rust

//! 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<dyn std::error::Error>> {
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(())
}