use bincode::error::DecodeError;
use serde::{Deserialize, Serialize};
use alloc::format;
use alloc::{vec, vec::Vec};
use super::xmheader::{XmFlagType, XmHeader};
use super::xminstrument::XmInstrument;
use super::xmpattern::XmPattern;
use super::xmsample::XMSAMPLE_HEADER_SIZE;
use crate::codepage::Codepage;
use crate::compatibility_profile::CompatibilityProfile;
use crate::fixed::units::Volume;
use crate::import::import_memory::{ImportMemory, MemoryType};
use crate::import::orders_helper;
use crate::import::patternslot::PatternSlot;
use crate::module::Module;
use crate::period_helper::FrequencyType;
#[derive(Default, Serialize, Deserialize, Debug)]
pub struct XmModule {
pub header: XmHeader,
pub pattern_order: Vec<u8>,
pub pattern: Vec<XmPattern>,
pub instrument: Vec<XmInstrument>,
}
impl XmModule {
pub fn load(data: &[u8]) -> Result<Self, DecodeError> {
let original_data = data;
let (data_after_header, header, pattern_order) = XmHeader::load(data)?;
let mut cursor = original_data.len() - data_after_header.len();
let mut data = data_after_header;
let mut pattern: Vec<XmPattern> = vec![];
for _i in 0..header.number_of_patterns {
let (d2, xmp) = XmPattern::load(data, header.number_of_channels)?;
cursor += data.len() - d2.len();
data = d2;
pattern.push(xmp);
}
if pattern_order.len() > pattern.len() {
let empty_ones = pattern_order.len() - pattern.len();
let empty = XmPattern::new(64, header.number_of_channels.into());
pattern.extend(core::iter::repeat_n(empty, empty_ones));
}
let mut instrument_starts: Vec<usize> =
Vec::with_capacity(header.number_of_instruments as usize);
let mut instrument: Vec<XmInstrument> = vec![];
for _i in 0..header.number_of_instruments {
instrument_starts.push(cursor);
let (d2, xmi) = XmInstrument::load(data)?;
cursor += data.len() - d2.len();
data = d2;
instrument.push(xmi);
}
let mut xm = XmModule {
header,
pattern_order,
pattern,
instrument,
};
const HEADER_NAME_OFF: usize = 17;
const HEADER_NAME_LEN: usize = 20;
const HEADER_TRACKER_NAME_OFF: usize = 38;
const HEADER_TRACKER_NAME_LEN: usize = 20;
const INSTR_NAME_OFF_IN_RECORD: usize = 4; const INSTR_NAME_LEN: usize = 22;
const SAMPLE_NAME_OFF_IN_HEADER: usize = 18; const SAMPLE_NAME_LEN: usize = 22;
let n = original_data.len();
let mut text_fields: Vec<&[u8]> = Vec::new();
if n >= HEADER_NAME_OFF + HEADER_NAME_LEN {
text_fields.push(&original_data[HEADER_NAME_OFF..HEADER_NAME_OFF + HEADER_NAME_LEN]);
}
if n >= HEADER_TRACKER_NAME_OFF + HEADER_TRACKER_NAME_LEN {
text_fields.push(
&original_data
[HEADER_TRACKER_NAME_OFF..HEADER_TRACKER_NAME_OFF + HEADER_TRACKER_NAME_LEN],
);
}
for (i, &start) in instrument_starts.iter().enumerate() {
let name_start = start + INSTR_NAME_OFF_IN_RECORD;
if name_start + INSTR_NAME_LEN <= n {
text_fields.push(&original_data[name_start..name_start + INSTR_NAME_LEN]);
}
let xmih_len = xm.instrument[i].instrument_header_len as usize;
for s_i in 0..xm.instrument[i].sample.len() {
let sample_header_start = start + xmih_len + s_i * XMSAMPLE_HEADER_SIZE;
let name_start = sample_header_start + SAMPLE_NAME_OFF_IN_HEADER;
if name_start + SAMPLE_NAME_LEN <= n {
text_fields.push(&original_data[name_start..name_start + SAMPLE_NAME_LEN]);
}
}
}
let codepage = Codepage::detect_from_fields(&text_fields);
if n >= HEADER_NAME_OFF + HEADER_NAME_LEN {
xm.header.name = codepage
.decode_name(&original_data[HEADER_NAME_OFF..HEADER_NAME_OFF + HEADER_NAME_LEN]);
}
if n >= HEADER_TRACKER_NAME_OFF + HEADER_TRACKER_NAME_LEN {
xm.header.tracker_name = codepage.decode_name(
&original_data
[HEADER_TRACKER_NAME_OFF..HEADER_TRACKER_NAME_OFF + HEADER_TRACKER_NAME_LEN],
);
}
for (i, &start) in instrument_starts.iter().enumerate() {
let name_start = start + INSTR_NAME_OFF_IN_RECORD;
if name_start + INSTR_NAME_LEN <= n {
xm.instrument[i].header.name =
codepage.decode_name(&original_data[name_start..name_start + INSTR_NAME_LEN]);
}
let xmih_len = xm.instrument[i].instrument_header_len as usize;
for s_i in 0..xm.instrument[i].sample.len() {
let sample_header_start = start + xmih_len + s_i * XMSAMPLE_HEADER_SIZE;
let name_start = sample_header_start + SAMPLE_NAME_OFF_IN_HEADER;
if name_start + SAMPLE_NAME_LEN <= n {
let name = codepage
.decode_name(&original_data[name_start..name_start + SAMPLE_NAME_LEN]);
xm.instrument[i].sample[s_i].set_name(name);
}
}
}
Ok(xm)
}
pub fn to_module(&self) -> Module {
let mut module = Module {
name: self.header.name.clone(),
comment: format!(
"{} ({}.{:02})",
self.header.tracker_name,
self.header.version_number >> 8,
self.header.version_number & 0xFF
),
profile: CompatibilityProfile::ft2(),
frequency_type: match self.header.flags {
XmFlagType::XmAmigaFrequencies => FrequencyType::AmigaFrequencies,
XmFlagType::XmLinearFrequencies => FrequencyType::LinearFrequencies,
},
restart_position: self.header.restart_position as usize,
default_tempo: self.header.default_tempo as usize,
default_bpm: self.header.default_bpm as usize,
pattern_order: orders_helper::parse_orders(&self.pattern_order),
pattern: vec![],
pattern_names: vec![],
channel_names: vec![],
channel_defaults: vec![],
instrument: vec![],
midi_macros: None,
pattern_highlight: crate::module::PatternHighlight::default(),
mix_plugins: None,
pitch_wheel_depth: 2,
mix_volume: Volume::from_ratio(48, 128),
};
let patterns: Vec<Vec<Vec<PatternSlot>>> =
self.pattern.iter().map(|p| p.pattern.clone()).collect();
let mut im = ImportMemory::default();
module.pattern = im.unpack_patterns(
module.frequency_type,
MemoryType::Xm,
&module.pattern_order,
&patterns,
);
for i in &self.instrument {
module.instrument.push(i.to_instrument())
}
module
}
}