use crate::*;
fn minimal_package() -> UffPackage {
UffPackage {
char_name: "TestChar".into(),
floor_y: 8,
animations: vec![
UffAnimation {
name: "idle".into(),
default_fps: 12,
loop_mode: LoopMode::Loop,
pixel_flags: 0,
sheet_w: 128,
sheet_h: 64,
frame_w: 64,
frame_h: 64,
floor_y_override: 0,
sprite_table: vec![
SpriteEntry {
src_x: 0, src_y: 0, src_w: 0, src_h: 0,
pivot_x: -32, pivot_y: -32,
entry_flags: 0, tag: 0, duration_ms: 0,
},
SpriteEntry {
src_x: 64, src_y: 0, src_w: 0, src_h: 0,
pivot_x: -32, pivot_y: -32,
entry_flags: 1, tag: 0, duration_ms: 83,
},
],
frames: vec![
HitboxFrame {
id: 0,
label: "start".into(),
program: "".into(),
tags: "vulnerable=true".into(),
hitboxes: vec![
Hitbox {
name: "body".into(),
box_type: BoxType::Hurt,
enabled: true,
knockback: false,
x: 10.0, y: 5.0, width: 40.0, height: 55.0,
damage: 0, hitstun: 0, blockstun: 0,
knockback_angle: 0.0, knockback_strength: 0.0,
rotation: 0.0,
},
],
audio_cues: vec![],
},
HitboxFrame {
id: 1,
label: "".into(),
program: "LOO 0\nLOP".into(),
tags: "".into(),
hitboxes: vec![],
audio_cues: vec![
AudioCue {
clip_id: "step".into(),
volume: 0.8,
pitch: 1.0,
},
],
},
],
pixel_data: Some(vec![0xDE, 0xAD, 0xBE, 0xEF]),
},
],
}
}
#[test]
fn round_trip_minimal() {
let original = minimal_package();
let bytes = write_uff(&original).expect("write failed");
let parsed = read_uff(&bytes).expect("read failed");
assert_eq!(parsed.char_name, original.char_name);
assert_eq!(parsed.floor_y, original.floor_y);
assert_eq!(parsed.animations.len(), 1);
let anim = &parsed.animations[0];
assert_eq!(anim.name, "idle");
assert_eq!(anim.default_fps, 12);
assert!(matches!(anim.loop_mode, LoopMode::Loop));
assert_eq!(anim.sheet_w, 128);
assert_eq!(anim.frame_w, 64);
assert_eq!(anim.sprite_table.len(), 2);
assert_eq!(anim.sprite_table[1].src_x, 64);
assert_eq!(anim.sprite_table[1].entry_flags, 1);
assert_eq!(anim.sprite_table[1].duration_ms, 83);
assert_eq!(anim.frames.len(), 2);
let f0 = &anim.frames[0];
assert_eq!(f0.id, 0);
assert_eq!(f0.label, "start");
assert_eq!(f0.tags, "vulnerable=true");
assert_eq!(f0.hitboxes.len(), 1);
let hb = &f0.hitboxes[0];
assert_eq!(hb.name, "body");
assert!(matches!(hb.box_type, BoxType::Hurt));
assert!(hb.enabled);
assert!(!hb.knockback);
assert_eq!(hb.width, 40.0);
assert_eq!(f0.audio_cues.len(), 0);
let f1 = &anim.frames[1];
assert_eq!(f1.program, "LOO 0\nLOP");
assert_eq!(f1.audio_cues.len(), 1);
assert_eq!(f1.audio_cues[0].clip_id, "step");
assert_eq!(f1.audio_cues[0].volume, 0.8);
assert_eq!(anim.pixel_data, Some(vec![0xDE, 0xAD, 0xBE, 0xEF]));
}
#[test]
fn bad_magic_rejected() {
let mut bytes = write_uff(&minimal_package()).unwrap();
bytes[0] = b'X';
assert!(read_uff(&bytes).is_err());
}
#[test]
fn bad_version_rejected() {
let mut bytes = write_uff(&minimal_package()).unwrap();
bytes[4] = 99;
assert!(read_uff(&bytes).is_err());
}
#[test]
fn no_hitboxes_no_pixel_data() {
let pkg = UffPackage {
char_name: "Empty".into(),
floor_y: 0,
animations: vec![UffAnimation {
name: "stand".into(),
default_fps: 1,
loop_mode: LoopMode::Once,
pixel_flags: 0,
sheet_w: 0, sheet_h: 0, frame_w: 0, frame_h: 0,
floor_y_override: 0,
sprite_table: vec![],
frames: vec![],
pixel_data: None,
}],
};
let bytes = write_uff(&pkg).unwrap();
let parsed = read_uff(&bytes).unwrap();
assert!(parsed.animations[0].frames.is_empty());
assert!(parsed.animations[0].pixel_data.is_none());
}