xmrs 0.10.3

A library to edit SoundTracker data with pleasure
Documentation
use alloc::string::ToString;
use alloc::vec;
use alloc::vec::Vec;
use bincode::error::DecodeError;
use serde::Deserialize;

const MAX_MIXPLUGINS: usize = 64;

#[derive(Deserialize, Debug, Clone)]
#[repr(C)]
pub struct SndMixPluginInfo {
    pub id1: u32,
    pub id2: u32,
    pub input_routing: u32,
    pub output_routing: u32,
    pub reserved: [u32; 4],
}

#[derive(Deserialize, Debug, Clone)]
#[repr(C)]
pub struct MixPlugin {
    pub info: SndMixPluginInfo,
    pub data: Option<Vec<u8>>,
}

#[derive(Debug)]
pub struct Plugins {
    channel_settings: Vec<u32>,
    mix: Vec<MixPlugin>,
}

impl Default for Plugins {
    fn default() -> Self {
        Self {
            channel_settings: vec![0; 64],
            mix: vec![
                MixPlugin {
                    info: SndMixPluginInfo {
                        id1: 0,
                        id2: 0,
                        input_routing: 0,
                        output_routing: 0,
                        reserved: [0; 4],
                    },
                    data: None,
                };
                MAX_MIXPLUGINS
            ],
        }
    }
}

impl Plugins {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn load(source: &[u8]) -> Result<(Self, usize), DecodeError> {
        let mut data = source;
        let mut plugins = Self::new();

        while data.len() >= 8 {
            let plugin_id = u32::from_le_bytes(data[0..4].try_into().unwrap());
            let plugin_size = u32::from_le_bytes(data[4..8].try_into().unwrap()) as usize;

            if data.len() < plugin_size {
                return Err(DecodeError::OtherString(
                    "Plugin Size too big!?".to_string(),
                ));
            }

            if plugin_id == u32::from_le_bytes(*b"CHFX") {
                data = &data[8..];
                for ch in 0..64 {
                    if ch * 4 < plugin_size {
                        plugins.channel_settings[ch] =
                            u32::from_le_bytes(data[ch * 4..(ch + 1) * 4].try_into().unwrap());
                    } else {
                        return Err(DecodeError::LimitExceeded);
                    }
                }
                data = &data[plugin_size..];
            } else if data[0] == b'F' && data[1] == b'X' && data[2] >= b'0' && data[3] >= b'0' {
                let plugin_index = ((data[2] - b'0') * 10 + (data[3] - b'0')) as usize;
                data = &data[8..];
                if plugin_index >= MAX_MIXPLUGINS {
                    return Err(DecodeError::OtherString(
                        "Plugin Index overflow!?".to_string(),
                    ));
                }
                let info: (SndMixPluginInfo, usize) =
                    bincode::serde::decode_from_slice::<SndMixPluginInfo, _>(
                        data,
                        bincode::config::legacy(),
                    )?;
                plugins.mix[plugin_index].info = info.0;
                data = &data[info.1..];

                let extra_size = plugin_size - info.1;
                if extra_size != 0 {
                    let d = &data[0..extra_size].to_vec();
                    plugins.mix[plugin_index].data = Some(d.clone());
                }
                data = &data[extra_size..];
            } else {
                break;
            }
        }
        Ok((plugins, source.len() - data.len()))
    }
}