use alloc::string::String;
use crate::import::bin_reader::{bytes_to_trimmed_string, BinReader, ImportError};
pub const IT_HEADER_SIZE: usize = 192;
#[derive(Debug)]
pub struct ItHeader {
id: String,
pub song_name: String,
pub rows_per_beat: u8,
pub rows_per_measure: u8,
pub order_number: u16,
pub instrument_number: u16,
pub sample_number: u16,
pub pattern_number: u16,
pub created_with_tracker: u16,
pub compatible_with_tracker: u16,
pub flags: u16,
pub special_flags: u16,
pub global_volume: u8,
pub mix_volume: u8,
pub initial_speed: u8,
pub initial_bpm: u8,
pub pan_separation: u8,
pub pitch_wheel_depth: u8,
pub message_length: u16,
pub message_offset: u32,
pub reserved: String,
pub initial_channel_pan: [u8; 64],
pub initial_channel_volume: [u8; 64],
}
impl ItHeader {
pub fn load(data: &[u8]) -> Result<(Self, usize), ImportError> {
let mut r = BinReader::new(data);
let id_bytes: [u8; 4] = r.read_array()?;
let song_name_bytes: [u8; 26] = r.read_array()?;
let rows_per_beat = r.read_u8()?;
let rows_per_measure = r.read_u8()?;
let order_number = r.read_u16_le()?;
let instrument_number = r.read_u16_le()?;
let sample_number = r.read_u16_le()?;
let pattern_number = r.read_u16_le()?;
let created_with_tracker = r.read_u16_le()?;
let compatible_with_tracker = r.read_u16_le()?;
let flags = r.read_u16_le()?;
let special_flags = r.read_u16_le()?;
let global_volume = r.read_u8()?;
let mix_volume = r.read_u8()?;
let initial_speed = r.read_u8()?;
let initial_bpm = r.read_u8()?;
let pan_separation = r.read_u8()?;
let pitch_wheel_depth = r.read_u8()?;
let message_length = r.read_u16_le()?;
let message_offset = r.read_u32_le()?;
let reserved_bytes: [u8; 4] = r.read_array()?;
let initial_channel_pan: [u8; 64] = r.read_array()?;
let initial_channel_volume: [u8; 64] = r.read_array()?;
let header = ItHeader {
id: bytes_to_trimmed_string(&id_bytes),
song_name: bytes_to_trimmed_string(&song_name_bytes),
rows_per_beat,
rows_per_measure,
order_number,
instrument_number,
sample_number,
pattern_number,
created_with_tracker,
compatible_with_tracker,
flags,
special_flags,
global_volume,
mix_volume,
initial_speed,
initial_bpm,
pan_separation,
pitch_wheel_depth,
message_length,
message_offset,
reserved: bytes_to_trimmed_string(&reserved_bytes),
initial_channel_pan,
initial_channel_volume,
};
if !header.is_it_header() {
return Err(ImportError::InvalidMagic("Not an IT Module?"));
}
Ok((header, IT_HEADER_SIZE))
}
pub fn get_size() -> usize {
IT_HEADER_SIZE
}
pub fn is_it_header(&self) -> bool {
self.id == "IMPM"
}
pub fn is_ompt(&self) -> bool {
self.reserved == "OMPT"
}
pub fn is_post20(&self) -> bool {
(self.created_with_tracker >> 8) >= 2
}
pub fn is_stereo(&self) -> bool {
(self.flags & (1 << 0)) != 0
}
pub fn is_mixing(&self) -> bool {
(self.flags & (1 << 1)) != 0
}
pub fn is_instruments_used(&self) -> bool {
(self.flags & (1 << 2)) != 0
}
pub fn is_linear_slides(&self) -> bool {
(self.flags & (1 << 3)) != 0
}
pub fn is_old_effects(&self) -> bool {
(self.flags & (1 << 4)) != 0
}
pub fn is_g_linked_with_e_f(&self) -> bool {
(self.flags & (1 << 5)) != 0
}
pub fn is_midi_pitch_controlled(&self) -> bool {
(self.flags & (1 << 6)) != 0
}
pub fn is_embedded_midi_macros(&self) -> bool {
(self.flags & (1 << 7)) != 0
}
pub fn is_song_message_attached(&self) -> bool {
(self.special_flags & (1 << 0)) != 0
}
pub fn is_edit_history_embedded(&self) -> bool {
(self.special_flags & (1 << 1)) != 0
}
pub fn is_highlight_embedded(&self) -> bool {
(self.special_flags & (1 << 2)) != 0
}
pub fn is_embedded_midi_macro(&self) -> bool {
(self.special_flags & (1 << 3)) != 0
}
}