use std::ffi::{CStr, CString}; use std::os::raw::{c_char, c_float, c_int, c_void}; // Include the generated bindings include!(concat!(env!("OUT_DIR"), "/bindings.rs")); // Headless texture loader functions with debug prints extern "C" fn headless_texture_loader(path: *const c_char) -> *mut c_void { unsafe { let path_str = if !path.is_null() { CStr::from_ptr(path).to_string_lossy() } else { "NULL".into() }; println!("DEBUG: texture_loader called with path: {}", path_str); let ptr = std::alloc::alloc(std::alloc::Layout::from_size_align(8, 8).unwrap()); println!("DEBUG: texture_loader returning: {:?}", ptr); ptr as *mut c_void } } extern "C" fn headless_texture_unloader(texture: *mut c_void) -> () { println!("DEBUG: texture_unloader called with texture: {:?}", texture); if !texture.is_null() && texture as usize > 1 { unsafe { println!("DEBUG: deallocating texture: {:?}", texture); std::alloc::dealloc(texture as *mut u8, std::alloc::Layout::from_size_align(8, 8).unwrap()); println!("DEBUG: texture deallocation completed"); } } else { println!("DEBUG: skipping deallocation (null or invalid pointer)"); } println!("DEBUG: texture_unloader returning"); } #[no_mangle] pub extern "C" fn test_spine_basic() -> c_int { unsafe { println!("Starting spine test..."); spine_bone_set_y_down(false); println!("Set y_down..."); // Load real spineboy atlas data let atlas_file = std::fs::read_to_string("../../../examples/spineboy/export/spineboy-pma.atlas") .expect("Failed to read atlas file"); let atlas_data = CString::new(atlas_file).unwrap(); let atlas_dir = CString::new("../../../examples/spineboy/export/").unwrap(); println!("About to load atlas..."); let atlas = spine_atlas_load_callback( atlas_data.as_ptr(), atlas_dir.as_ptr(), Some(headless_texture_loader), Some(headless_texture_unloader), ); println!("Atlas loaded: {:?}", atlas); if atlas.is_null() { println!("Atlas is null!"); return 1; // Failed to load atlas } // Load real spineboy skeleton data (binary format like the C test) println!("Reading skeleton file..."); let skeleton_file = std::fs::read("../../../examples/spineboy/export/spineboy-pro.skel") .expect("Failed to read skeleton file"); println!("Skeleton file size: {} bytes", skeleton_file.len()); let skeleton_path = CString::new("../../../examples/spineboy/export/spineboy-pro.skel").unwrap(); println!("About to call spine_skeleton_data_load_binary..."); let result = spine_skeleton_data_load_binary(atlas, skeleton_file.as_ptr(), skeleton_file.len() as i32, skeleton_path.as_ptr()); println!("spine_skeleton_data_load_binary returned: {:?}", result); if result.is_null() { println!("Result is null!"); return 2; } println!("About to call spine_skeleton_data_result_get_data..."); println!("Result pointer: {:?}", result); println!("Result is null: {}", result.is_null()); // Try to read the error first to see if result is valid println!("Checking if result has error..."); let error_ptr = spine_skeleton_data_result_get_error(result); println!("Error check completed. Error ptr: {:?}", error_ptr); if !error_ptr.is_null() { let error_str = CStr::from_ptr(error_ptr); println!("Found error: {:?}", error_str); spine_skeleton_data_result_dispose(result); spine_atlas_dispose(atlas); return 2; } println!("No error found, getting skeleton data..."); let skeleton_data = spine_skeleton_data_result_get_data(result); if skeleton_data.is_null() { let error = spine_skeleton_data_result_get_error(result); if !error.is_null() { let error_str = CStr::from_ptr(error); eprintln!("Skeleton data error: {:?}", error_str); } spine_skeleton_data_result_dispose(result); spine_atlas_dispose(atlas); return 2; // Failed to load skeleton data } println!("Skeleton data is valid: {:?}", skeleton_data); // Test skeleton creation immediately println!("Creating skeleton..."); let skeleton = spine_skeleton_create(skeleton_data); println!("Skeleton create returned: {:?}", skeleton); if skeleton.is_null() { spine_skeleton_data_result_dispose(result); spine_atlas_dispose(atlas); return 3; // Failed to create skeleton } // Test basic operations println!("Calling spine_skeleton_setup_pose..."); spine_skeleton_setup_pose(skeleton); println!("Setup pose completed"); println!("Calling spine_skeleton_update_world_transform_1..."); spine_skeleton_update_world_transform_1(skeleton, 1); // SPINE_PHYSICS_UPDATE = 1 println!("Update world transform completed"); println!("Getting skeleton position..."); let x = spine_skeleton_get_x(skeleton); println!("Got x: {}", x); let y = spine_skeleton_get_y(skeleton); println!("Got y: {}", y); // Cleanup println!("Disposing skeleton..."); spine_skeleton_dispose(skeleton); println!("Skeleton disposed"); println!("Disposing skeleton data result..."); spine_skeleton_data_result_dispose(result); println!("Skeleton data result disposed"); // Test atlas disposal to get proper crash backtrace println!("About to call spine_atlas_dispose - crash expected..."); spine_atlas_dispose(atlas); println!("Atlas disposal completed successfully"); // Verify we got reasonable values println!("Verifying values..."); if x.is_finite() && y.is_finite() { println!("SUCCESS! Test completed successfully"); 0 // Success } else { println!("FAILED! Invalid values"); 4 // Invalid values } } } // WASM export for web testing #[cfg(target_arch = "wasm32")] mod wasm { use super::*; use std::os::raw::c_int; #[no_mangle] pub extern "C" fn run_spine_test() -> c_int { test_spine_basic() } } #[cfg(test)] mod tests { use super::*; #[test] fn test_spine_basic_works() { let result = test_spine_basic(); assert_eq!(result, 0, "Spine basic test should succeed"); } }