219 lines
8.4 KiB
Rust
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(())
|
|
}
|