reskit/src/reskit/soundtrack.rs

172 lines
5.4 KiB
Rust

use std::{error::Error, fs::File, io::Read, str::from_utf8, slice::Iter, convert::TryInto};
use flate2::read::ZlibDecoder;
const DMF_MAGIC_NUMBER: &'static str = ".DelekDefleMask.";
const DMF_SUPPORTED_VERSION: u8 = 0x18;
const DMF_MD: u8 = 0x02;
const DMF_MD_ENHANCED_CH3: u8 = 0x42;
#[derive(Debug)]
pub enum FrameMode {
Ntsc,
Pal,
Custom( u8, u8, u8 )
}
pub struct DmfModule {
platform: u8,
version: u8,
time_base: u8,
speed_a: u8,
speed_b: u8,
frame_mode: FrameMode,
rows_per_pattern: u32,
patterns: Vec<Vec<u8>>
}
fn get_string( bytes: &mut Iter<'_, u8>, take: usize ) -> Result<String, Box<dyn Error>> {
let took = bytes.take( take );
let took: Vec<u8> = took.cloned().collect();
let took = from_utf8( &took )?.to_string();
Ok( took )
}
fn get_u8( bytes: &mut Iter<'_, u8> ) -> Result<u8, Box<dyn Error>> {
let took = bytes.take( 1 ).cloned().next().ok_or( "invalid file: expected 1 byte" )?;
Ok( took )
}
fn get_u32( bytes: &mut Iter<'_, u8> ) -> Result<u32, Box<dyn Error>> {
let took = bytes.take( 4 );
let took: Vec<u8> = took.cloned().collect();
let took = u32::from_le_bytes( took[0..4].try_into()? );
Ok( took )
}
fn skip( bytes: &mut Iter<'_, u8>, by: usize ) {
for i in 0..by {
bytes.next();
}
}
impl DmfModule {
pub fn from_file( path: &str ) -> Result<DmfModule, Box<dyn Error>> {
let mut file = File::open( path )?;
let mut compressed: Vec<u8> = Vec::new();
file.read_to_end( &mut compressed )?;
let mut inflator = ZlibDecoder::new( &compressed[..] );
let mut bytes: Vec<u8> = Vec::new();
inflator.read_to_end( &mut bytes )?;
let mut bytes = bytes.iter();
if get_string( bytes.by_ref(), 16 )? != DMF_MAGIC_NUMBER {
return Err( format!( "invalid file: missing DMF header" ) )?
}
println!( ".DelekDefleMask. DMF file" );
let version = get_u8( bytes.by_ref() )?;
if version != DMF_SUPPORTED_VERSION {
return Err( format!( "invalid file: unsupported version: {}", version ) )?
}
let platform = get_u8( bytes.by_ref() )?;
match platform {
DMF_MD => println!( "Mode:\tSega Megadrive" ),
DMF_MD_ENHANCED_CH3 => println!( "Mode:\t\tSega Megadrive (Ext. Ch. 3 Mode)" ),
_ => return Err( "invalid file: invalid console format" )?
};
let song_name_len = get_u8( bytes.by_ref() )?;
println!( "Song name:\t{}", get_string( bytes.by_ref(), song_name_len.into() )? );
let song_author_len = get_u8( bytes.by_ref() )?;
println!( "Song author:\t{}", get_string( bytes.by_ref(), song_author_len.into() )? );
// Burn 2 bytes, don't care about the highlight patterns
skip( bytes.by_ref(), 2 );
// DMF format appears to subtract by 1 from the base time shown in the UI
// Base time is multiplied by speed_a and speed_b so setting it to 1 and
// getting 0 doesn't make any sense.
let time_base = get_u8( bytes.by_ref() )? + 1;
let speed_a = get_u8( bytes.by_ref() )?;
let speed_b = get_u8( bytes.by_ref() )?;
println!( "Time base:\t{}", time_base );
println!( "Speed A:\t{}", speed_a );
println!( "Speed B:\t{}", speed_b );
let frame_mode = match get_u8( bytes.by_ref() )? {
0 => FrameMode::Pal,
1 => FrameMode::Ntsc,
_ => return Err( "invalid file: invalid frame mode" )?
};
let custom_frame_mode = get_u8( bytes.by_ref() )? == 1;
let custom_frame_mode_hz_1 = get_u8( bytes.by_ref() )?;
let custom_frame_mode_hz_2 = get_u8( bytes.by_ref() )?;
let custom_frame_mode_hz_3 = get_u8( bytes.by_ref() )?;
let frame_mode = if custom_frame_mode {
FrameMode::Custom( custom_frame_mode_hz_1, custom_frame_mode_hz_2, custom_frame_mode_hz_3 )
} else {
frame_mode
};
println!( "Frame mode:\t{:?}", frame_mode );
let rows_per_pattern = get_u32( bytes.by_ref() )?;
println!( "Rows/pattern:\t{}", rows_per_pattern );
let patterns_count = get_u8( bytes.by_ref() )?;
println!( "Patterns:\t{}", patterns_count );
println!( "\nPattern Matrix:" );
let system_total_channels = if platform == DMF_MD {
println!( "FM1 FM2 FM3 FM4 FM5 FM6 SN1 SN2 SN3 SN4" );
10
} else {
println!( "FM1 FM2 OP1 OP2 OP3 OP4 FM4 FM5 FM6 SN1 SN2 SN3 SN4" );
13
};
let mut patterns: Vec<Vec<u8>> = vec![vec![0; system_total_channels]; patterns_count.into()];
for channel in 0..system_total_channels {
for pattern in 0..patterns_count {
let channel: usize = channel.into();
let pattern: usize = pattern.into();
patterns[ pattern ][ channel ] = get_u8( bytes.by_ref() )?;
}
}
for pattern in &patterns {
for channel in pattern {
print!( "{:#04X?} ", channel );
}
print!( "\n" );
}
print!( "\n" );
// TODO !!
Ok(
DmfModule { platform, version, time_base, speed_a, speed_b, frame_mode, rows_per_pattern, patterns }
)
}
pub fn to_esf( self ) -> Result<Vec<u8>, Box<dyn Error>> {
// TODO !!
todo!()
}
}