use crate::import::import_memory::{ImportMemory, MemoryType};
use crate::import::orders_helper;
use crate::import::patternslot::PatternSlot;
use crate::prelude::*;
use bincode::error::DecodeError;
use alloc::format;
use alloc::string::{String, ToString};
use alloc::vec;
use alloc::vec::Vec;
use super::it_edit_history::{ItEditHistory, ItEditHistoryEntry};
use super::it_header::ItHeader;
use super::it_instrument::ItInstrument;
use super::it_midi_macros::ItMidiMacros;
use super::it_pattern::ItPattern;
use super::it_plugins::Plugins;
use super::it_sample_header::ItSampleHeader;
use super::it_x_names::ItXNames;
#[derive(Debug)]
#[repr(C)]
pub struct ItModule {
header: ItHeader,
orders: Vec<u8>,
edit_history: Option<Vec<ItEditHistoryEntry>>,
midi_macros: Option<ItMidiMacros>,
pattern_names: Vec<String>,
channel_names: Vec<String>,
plugins: Option<Plugins>,
message: String,
instruments: Vec<ItInstrument>,
samples_header: Vec<ItSampleHeader>,
patterns: Vec<Vec<Vec<PatternSlot>>>,
samples: Vec<Option<SampleDataType>>,
}
impl ItModule {
pub fn load(ser_it_module: &[u8]) -> Result<Self, DecodeError> {
let data = ser_it_module;
if data.len() < ItHeader::get_size() {
return Err(DecodeError::ArrayLengthMismatch {
required: ItHeader::get_size(),
found: data.len(),
});
}
let header = ItHeader::load(data)?;
let data = &data[header.1..];
if data.len() < header.0.order_number as usize {
return Err(DecodeError::ArrayLengthMismatch {
required: header.0.order_number as usize,
found: data.len(),
});
}
let orders = &data[..header.0.order_number as usize];
let data = &data[header.0.order_number as usize..];
if data.len() < 4 * header.0.instrument_number as usize {
return Err(DecodeError::ArrayLengthMismatch {
required: 4 * header.0.instrument_number as usize,
found: data.len(),
});
}
let instrument_offsets_u8 = &data[..4 * header.0.instrument_number as usize];
let instrument_offsets: Vec<u32> = instrument_offsets_u8
.chunks_exact(4)
.map(|chunk| u32::from_le_bytes(chunk.try_into().unwrap()))
.collect();
let data = &data[4 * header.0.instrument_number as usize..];
if data.len() < 4 * header.0.sample_number as usize {
return Err(DecodeError::ArrayLengthMismatch {
required: 4 * header.0.sample_number as usize,
found: data.len(),
});
}
let sample_header_offsets_u8 = &data[..4 * header.0.sample_number as usize];
let sample_header_offsets: Vec<u32> = sample_header_offsets_u8
.chunks_exact(4)
.map(|chunk| u32::from_le_bytes(chunk.try_into().unwrap()))
.collect();
let data = &data[4 * header.0.sample_number as usize..];
if data.len() < 4 * header.0.pattern_number as usize {
return Err(DecodeError::ArrayLengthMismatch {
required: 4 * header.0.pattern_number as usize,
found: data.len(),
});
}
let pattern_offsets_u8 = &data[..4 * header.0.pattern_number as usize];
let pattern_offsets: Vec<u32> = pattern_offsets_u8
.chunks_exact(4)
.map(|chunk| u32::from_le_bytes(chunk.try_into().unwrap()))
.collect();
let mut data = &data[4 * header.0.pattern_number as usize..];
let edit_history = if header.0.is_edit_history_embedded() {
let (edit_history, l) = ItEditHistory::load(data)?;
data = &data[l..];
edit_history
} else {
None
};
let midi_macros = if header.0.is_embedded_midi_macro() || header.0.is_embedded_midi_macros()
{
let r = bincode::serde::decode_from_slice::<ItMidiMacros, _>(
data,
bincode::config::legacy(),
)?;
data = &data[r.1..];
Some(r.0)
} else {
None
};
let pattern_names = if ItXNames::is_pnam(data) {
data = &data[4..];
let (pattern_names, l) = ItXNames::load(data, 32)?;
data = &data[l..];
pattern_names
} else {
vec![]
};
let channel_names = if ItXNames::is_cnam(data) {
data = &data[4..];
let (channel_names, l) = ItXNames::load(data, 20)?;
data = &data[l..];
channel_names
} else {
vec![]
};
let (plugins, _l) = if let Ok(p) = Plugins::load(data) {
(Some(p.0), p.1)
} else {
(None, 0)
};
let message = if header.0.is_song_message_attached() && header.0.message_length != 0 {
let start = header.0.message_offset as usize;
let end = start + header.0.message_length as usize;
if ser_it_module[start..].len() < header.0.message_length as usize {
return Err(DecodeError::ArrayLengthMismatch {
required: header.0.message_length as usize,
found: ser_it_module[start..].len(),
});
}
let src = &ser_it_module[start..end];
String::from_utf8_lossy(src).trim().to_string()
} else {
String::new()
};
let mut instruments: Vec<ItInstrument> = vec![];
for i in 0..header.0.instrument_number {
let i_seek = instrument_offsets[i as usize] as usize;
if ser_it_module.len() < i_seek {
return Err(DecodeError::LimitExceeded);
}
let data = &ser_it_module[i_seek..];
if !header.0.is_post20() {
instruments.push(ItInstrument::load_pre2(data)?);
} else {
instruments.push(ItInstrument::load_post2(data)?);
}
}
let mut samples_header: Vec<ItSampleHeader> = vec![];
for i in 0..header.0.sample_number {
let i_seek = sample_header_offsets[i as usize] as usize;
if ser_it_module.len() < i_seek {
return Err(DecodeError::LimitExceeded);
}
let data = &ser_it_module[i_seek..];
let sample_h = bincode::serde::decode_from_slice::<ItSampleHeader, _>(
data,
bincode::config::legacy(),
)?;
samples_header.push(sample_h.0);
}
let mut patterns: Vec<Vec<Vec<PatternSlot>>> = vec![];
for pattern_seek in &pattern_offsets {
if ser_it_module.len() < *pattern_seek as usize {
return Err(DecodeError::LimitExceeded);
}
if *pattern_seek != 0 {
let data = &ser_it_module[*pattern_seek as usize..];
let itpattern = ItPattern::load(data)?;
let pattern = itpattern.unpack()?;
patterns.push(pattern);
} else {
patterns.push(vec![vec![]]);
}
}
let mut samples = vec![];
for sh in &samples_header {
if sh.is_associated_sample() {
let start = sh.sample_pointer as usize;
if ser_it_module.len() < start {
return Err(DecodeError::LimitExceeded);
}
let sample = sh.get_sample_data(&ser_it_module[start..])?;
if !sample.is_empty() {
samples.push(Some(sample));
} else {
samples.push(None);
}
} else {
samples.push(None);
}
}
let it = Self {
header: header.0,
orders: orders.to_vec(),
edit_history,
midi_macros,
pattern_names,
channel_names,
plugins,
message,
instruments,
samples_header,
patterns,
samples,
};
Ok(it)
}
pub fn to_module(&self) -> Module {
let it_type = if self.header.is_ompt() { "MP" } else { "IT" };
let mut module = Module {
name: self.header.song_name.clone(),
comment: format!(
"{}: {}.{} (compatibility: {}.{})\n\n{}",
it_type,
self.header.created_with_tracker >> 8,
self.header.created_with_tracker & 0x00FF,
self.header.compatible_with_tracker >> 8,
self.header.compatible_with_tracker & 0x00FF,
self.message
),
format: ModuleFormat::It,
quirks: PlaybackQuirks::default(),
frequency_type: if self.header.is_linear_slides() {
FrequencyType::LinearFrequencies
} else {
FrequencyType::AmigaFrequencies
},
restart_position: 0,
default_tempo: self.header.initial_speed as usize,
default_bpm: self.header.initial_bpm as usize,
pattern_order: orders_helper::parse_orders(&self.orders),
pattern: vec![],
pattern_names: self.pattern_names.clone(),
channel_names: self.channel_names.clone(),
instrument: vec![],
};
let mut im = ImportMemory::default();
module.pattern = im.unpack_patterns(
module.frequency_type,
MemoryType::It,
&module.pattern_order,
&self.patterns,
);
let mut samples: Vec<Sample> = vec![];
let mut vibratos: Vec<Vibrato> = vec![];
for (i, sh) in self.samples_header.iter().enumerate() {
let pdata = if sh.is_associated_sample() && i < self.samples.len() {
&self.samples[i]
} else {
&None
};
let s = sh.to_sample(pdata);
samples.push(s);
let vibrato = sh.to_vibrato();
vibratos.push(vibrato);
}
if self.header.is_instruments_used() {
for i in self.instruments.iter() {
let mut instrument = i.prepare_instrument();
if let InstrumentType::Default(instrdef) = &mut instrument.instr_type {
let first_sample_idx = instrdef.sample_for_pitch.iter().find_map(|s| *s);
if let Some(si) = first_sample_idx {
if si < vibratos.len() {
instrdef.vibrato = vibratos[si];
}
}
for i3 in instrdef.sample_for_pitch.into_iter().flatten() {
if instrdef.sample.len() <= i3 {
instrdef.sample.resize_with(i3 + 1, || None);
}
if instrdef.sample[i3].is_none() && samples.len() > i3 {
instrdef.sample[i3] = Some(samples[i3].clone());
}
}
}
module.instrument.push(instrument);
}
} else {
for (index, s) in samples.iter().enumerate() {
let mut instrdef: InstrDefault = InstrDefault::default();
instrdef.vibrato = vibratos[index];
instrdef.sample.push(Some(s.clone()));
instrdef.change_all_sample_for_pitch(0);
let instrument = Instrument {
name: s.name.clone(),
muted: false,
instr_type: InstrumentType::Default(instrdef),
};
module.instrument.push(instrument);
}
}
module
}
}