mocari 0.1.2

A pure Rust Live2D/Cubism-compatible runtime
Documentation
use crate::{Error, Result};

use super::{Endianness, Moc3Header, Moc3SectionOffsets};

const U32_SIZE: usize = 4;

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct Moc3CountInfo {
    parts: u32,
    deformers: u32,
    warp_deformers: u32,
    rotation_deformers: u32,
    art_meshes: u32,
    parameters: u32,
    part_keyforms: u32,
    warp_deformer_keyforms: u32,
    rotation_deformer_keyforms: u32,
    art_mesh_keyforms: u32,
    keyform_positions: u32,
    parameter_binding_indices: u32,
    keyform_bindings: u32,
    parameter_bindings: u32,
    keys: u32,
    uvs: u32,
    position_indices: u32,
    drawable_masks: u32,
    draw_order_groups: u32,
    draw_order_group_objects: u32,
    glue: u32,
    glue_info: u32,
    glue_keyforms: u32,
}

impl Moc3CountInfo {
    pub fn parse(bytes: &[u8]) -> Result<Self> {
        let header = Moc3Header::parse(bytes)?;
        let offsets = Moc3SectionOffsets::parse(bytes)?;
        let offset = usize::try_from(offsets.count_info_offset())
            .map_err(|_| invalid_counts("count info offset does not fit in platform usize"))?;
        let required_len = header.version().count_info_word_count() * U32_SIZE;

        if bytes.len().saturating_sub(offset) < required_len {
            return Err(invalid_counts("count info table is incomplete"));
        }

        let read = |index| read_u32(bytes, offset + index * U32_SIZE, header.endianness());

        Ok(Self {
            parts: read(0),
            deformers: read(1),
            warp_deformers: read(2),
            rotation_deformers: read(3),
            art_meshes: read(4),
            parameters: read(5),
            part_keyforms: read(6),
            warp_deformer_keyforms: read(7),
            rotation_deformer_keyforms: read(8),
            art_mesh_keyforms: read(9),
            keyform_positions: read(10),
            parameter_binding_indices: read(11),
            keyform_bindings: read(12),
            parameter_bindings: read(13),
            keys: read(14),
            uvs: read(15),
            position_indices: read(16),
            drawable_masks: read(17),
            draw_order_groups: read(18),
            draw_order_group_objects: read(19),
            glue: read(20),
            glue_info: read(21),
            glue_keyforms: read(22),
        })
    }

    pub fn parts(&self) -> u32 {
        self.parts
    }

    pub fn deformers(&self) -> u32 {
        self.deformers
    }

    pub fn warp_deformers(&self) -> u32 {
        self.warp_deformers
    }

    pub fn rotation_deformers(&self) -> u32 {
        self.rotation_deformers
    }

    pub fn art_meshes(&self) -> u32 {
        self.art_meshes
    }

    pub fn parameters(&self) -> u32 {
        self.parameters
    }

    pub fn part_keyforms(&self) -> u32 {
        self.part_keyforms
    }

    pub fn warp_deformer_keyforms(&self) -> u32 {
        self.warp_deformer_keyforms
    }

    pub fn rotation_deformer_keyforms(&self) -> u32 {
        self.rotation_deformer_keyforms
    }

    pub fn art_mesh_keyforms(&self) -> u32 {
        self.art_mesh_keyforms
    }

    pub fn keyform_positions(&self) -> u32 {
        self.keyform_positions
    }

    pub fn parameter_binding_indices(&self) -> u32 {
        self.parameter_binding_indices
    }

    pub fn keyform_bindings(&self) -> u32 {
        self.keyform_bindings
    }

    pub fn parameter_bindings(&self) -> u32 {
        self.parameter_bindings
    }

    pub fn keys(&self) -> u32 {
        self.keys
    }

    pub fn uvs(&self) -> u32 {
        self.uvs
    }

    pub fn position_indices(&self) -> u32 {
        self.position_indices
    }

    pub fn drawable_masks(&self) -> u32 {
        self.drawable_masks
    }

    pub fn draw_order_groups(&self) -> u32 {
        self.draw_order_groups
    }

    pub fn draw_order_group_objects(&self) -> u32 {
        self.draw_order_group_objects
    }

    pub fn glue(&self) -> u32 {
        self.glue
    }

    pub fn glue_info(&self) -> u32 {
        self.glue_info
    }

    pub fn glue_keyforms(&self) -> u32 {
        self.glue_keyforms
    }
}

fn read_u32(bytes: &[u8], offset: usize, endianness: Endianness) -> u32 {
    let raw = [
        bytes[offset],
        bytes[offset + 1],
        bytes[offset + 2],
        bytes[offset + 3],
    ];

    match endianness {
        Endianness::Little => u32::from_le_bytes(raw),
        Endianness::Big => u32::from_be_bytes(raw),
    }
}

fn invalid_counts(message: impl Into<String>) -> Error {
    Error::InvalidMoc3 {
        message: message.into(),
    }
}