ufftools 0.1.0

reader/writer and inspect CLI for the UFF character package format. includes the uff decoder lib for use in other projects.
Documentation
/// Box type constants (§ "Box Type Constants").
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum BoxType {
    Attack          = 0,
    AttackInactive  = 1,
    Hurt            = 2,
    Stun            = 3,
    PhysicalExtent  = 4,
    Grab            = 5,
    GuardAir        = 6,
    GuardHigh       = 7,
    GuardMed        = 8,
    GuardLow        = 9,
    ThrowInvincible = 10,
    Invincible      = 11,
}

impl BoxType {
    pub fn from_u8(v: u8) -> Option<Self> {
        match v {
            0  => Some(Self::Attack),
            1  => Some(Self::AttackInactive),
            2  => Some(Self::Hurt),
            3  => Some(Self::Stun),
            4  => Some(Self::PhysicalExtent),
            5  => Some(Self::Grab),
            6  => Some(Self::GuardAir),
            7  => Some(Self::GuardHigh),
            8  => Some(Self::GuardMed),
            9  => Some(Self::GuardLow),
            10 => Some(Self::ThrowInvincible),
            11 => Some(Self::Invincible),
            _  => None,
        }
    }
}

/// loop_mode values for AnimHeader.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum LoopMode {
    Once     = 0,
    Loop     = 1,
    PingPong = 2,
}

impl LoopMode {
    pub fn from_u8(v: u8) -> Self {
        match v {
            1 => Self::Loop,
            2 => Self::PingPong,
            _ => Self::Once,
        }
    }
}

// ── SpriteTable entry ────────────────────────────────────────────────────────

#[derive(Debug, Clone)]
pub struct SpriteEntry {
    pub src_x:       u16,
    pub src_y:       u16,
    /// 0 → use AnimHeader.frame_w
    pub src_w:       u16,
    /// 0 → use AnimHeader.frame_h
    pub src_h:       u16,
    pub pivot_x:     i16,
    pub pivot_y:     i16,
    /// bit 0 = FLIP_H, bit 1 = FLIP_V
    pub entry_flags: u8,
    pub tag:         u8,
    /// 0 → derive from default_fps
    pub duration_ms: u16,
}

// ── Hitbox / combat data ─────────────────────────────────────────────────────

#[derive(Debug, Clone)]
pub struct Hitbox {
    pub name:               String,
    pub box_type:           BoxType,
    pub enabled:            bool,
    pub knockback:          bool,
    pub x:                  f32,
    pub y:                  f32,
    pub width:              f32,
    pub height:             f32,
    pub damage:             u16,
    pub hitstun:            u16,
    pub blockstun:          u16,
    pub knockback_angle:    f32,
    pub knockback_strength: f32,
    /// Normalized −180..180 degrees.
    pub rotation:           f32,
}

// ── Audio ────────────────────────────────────────────────────────────────────

#[derive(Debug, Clone)]
pub struct AudioCue {
    /// References a clip by name in the audio pool.
    pub clip_id: String,
    pub volume:  f32,
    pub pitch:   f32,
}

// ── Per-frame data ───────────────────────────────────────────────────────────

#[derive(Debug, Clone)]
pub struct HitboxFrame {
    /// 0-based frame index.
    pub id:      u16,
    pub label:   String,
    /// STAX source program (may be empty).
    pub program: String,
    /// `key=val key=val …` pairs (may be empty).
    pub tags:    String,
    pub hitboxes: Vec<Hitbox>,
    pub audio_cues: Vec<AudioCue>,
}

// ── Animation ────────────────────────────────────────────────────────────────

#[derive(Debug, Clone)]
pub struct UffAnimation {
    pub name:        String,
    pub default_fps: u16,
    pub loop_mode:   LoopMode,
    /// Pixel data flags (see uffp_format.md for meanings).
    pub pixel_flags: u8,
    pub sheet_w:     u16,
    pub sheet_h:     u16,
    /// 0 = variable (use each entry's src_w).
    pub frame_w:     u16,
    /// 0 = variable (use each entry's src_h).
    pub frame_h:     u16,
    /// Per-animation ground anchor override in px from bottom.
    /// 0 = inherit from package floor_y.
    pub floor_y_override: u16,
    pub sprite_table: Vec<SpriteEntry>,
    /// One entry per frame; empty when hitbox_size was 0.
    pub frames:      Vec<HitboxFrame>,
    /// Raw pixel block bytes (zlib-compressed per pixel_flags); None when absent.
    pub pixel_data:  Option<Vec<u8>>,
}

// ── Top-level package ────────────────────────────────────────────────────────

#[derive(Debug, Clone)]
pub struct UffPackage {
    pub char_name:  String,
    /// Global ground anchor in px from frame bottom (0 = bottom edge).
    pub floor_y:    u16,
    pub animations: Vec<UffAnimation>,
}