From c9d73559c5b6798f97af5cece3069c3184c44cdd Mon Sep 17 00:00:00 2001 From: rgiot Date: Mon, 6 Jul 2020 22:05:22 +0200 Subject: [PATCH] Add support of themes --- examples/list_info.rs | 8 ++- src/db.rs | 123 ++++++++++++++++++++++++++++++++---------- src/generator.rs | 2 + src/main.rs | 8 +-- 4 files changed, 107 insertions(+), 34 deletions(-) diff --git a/examples/list_info.rs b/examples/list_info.rs index f442d657..051bbe54 100644 --- a/examples/list_info.rs +++ b/examples/list_info.rs @@ -6,7 +6,13 @@ fn main() { for key in mix.keys() { let music = mix.music(key).unwrap(); if let Ok(info) = music.info() { - println!("{}", info); + println!("{} - {} - {} theme(s)", + key, + info, + music.header() + .map(|h| h.nb_themes()) + .unwrap_or(1) + ); } } } \ No newline at end of file diff --git a/src/db.rs b/src/db.rs index 7167cc78..703fc681 100644 --- a/src/db.rs +++ b/src/db.rs @@ -1,7 +1,7 @@ use rand::seq::IteratorRandom; use rand::rngs::ThreadRng; use itertools::Itertools; - +use rand::Rng; pub use std::convert::TryFrom; include!(concat!(env!("OUT_DIR"), "/", "db.rs")); @@ -64,38 +64,46 @@ impl MusicHeader { pub fn len() -> usize { 35 as usize } - + pub fn need_system(&self) -> bool { self.need_system != 0 } - + pub fn relative_music_info(&self) -> usize { (self.music_info - self.music_begin) as usize } - + pub fn load_adress(&self) -> u16 { self.music_begin } - + pub fn init_call_address(&self) -> u16 { self.init_music } - + pub fn play_call_address(&self) -> u16 { self.music_play } - + + pub fn nb_themes(&self) -> u8 { + self.last_theme - self.first_theme + 1 + } + + pub fn theme_code(&self, nb: u8) -> u8 { + self.first_theme + nb + } + } impl TryFrom<&[u8]> for MusicHeader { type Error = String; - + fn try_from(stream: &[u8]) -> Result { - + if stream.len() != Self::len() { return Err("Wrong size".to_owned()); } - + for (idx, chr) in "Megachur".chars().enumerate() { if stream[25 + idx] as char != chr { return Err( @@ -108,15 +116,15 @@ impl TryFrom<&[u8]> for MusicHeader { ); } } - + let byte = |idx: usize| { stream[idx] }; - + let word = |idx: usize| { stream[idx] as u16 + 256*(stream[idx+1] as u16) }; - + Ok(Self { music_begin: word(0), music_adr: word(2), @@ -135,7 +143,7 @@ impl TryFrom<&[u8]> for MusicHeader { music_date_rip_year: byte(23), need_system: byte(24), eof_length: word(33), - + }) } } @@ -143,21 +151,22 @@ impl TryFrom<&[u8]> for MusicHeader { #[derive(Debug)] pub struct Music { name: String, - data: &'static [u8] + data: &'static [u8], + selected_theme: u8 } impl Music { - - + + pub fn header(&self) -> Result { MusicHeader::try_from(&self.data[..MusicHeader::len()]) } - + pub fn info(&self) -> Result { let header = self.header()?; let start = &self.data[header.relative_music_info()..]; - + let mut info = String::new(); let mut first = true; for chr in start.iter() { @@ -174,10 +183,10 @@ impl Music { info += &(*chr as char).to_string(); } } - + Ok(info) } - + // panic if info not properly set pub fn author(&self) -> Result { let infos = self.info()?; @@ -186,7 +195,7 @@ impl Music { let author = bloc.split(")").next().unwrap(); Ok(author.to_owned()) } - + // panic if info not properly set pub fn title(&self) -> Result { let infos = self.info()?; @@ -194,11 +203,29 @@ impl Music { let title = line.split('(').next().unwrap(); Ok(title.to_owned()) } - + pub fn content(&self) -> &[u8] { &self.data } + + pub fn select_theme(&mut self, theme: u8) { + self.selected_theme = theme + } + + pub fn selected_theme(&self) -> u8 { + self.selected_theme + } + + pub fn is_theme_valid(&self) -> bool { + self.selected_theme() < self.header().unwrap().nb_themes() + } + pub fn select_random_theme(&mut self, rng: &mut ThreadRng) { + self.select_theme( + rng.gen::() % self.header().unwrap().nb_themes() + ) + } + } @@ -209,17 +236,55 @@ impl CpcMix { pub fn new() -> Self { Self{} } - + pub fn keys(&self) -> impl Iterator { StaticMap::keys().iter() } - - pub fn music(&self, key: &str) -> Option { + + /// Select the music of interest in the database: + /// - `` Select theme 0 of music + /// - `:` Select theme of music + /// - `:?` Select a random theme for music + /// + /// Return None if: + /// - music is not found + /// - music is found but theme does not exists + pub fn music(&self, key: &str, rng: &mut ThreadRng) -> Option { + + let (key, theme) = if key.contains(":") { + let words = key.split(':').collect::>(); + (words[0], words[1]) + } + else { + (key, "0") + }; + StaticMap::get(key) - .and_then(|data| Some(Music{name: key.to_owned(), data})) + .and_then(|data| Some(Music{ + name: key.to_owned(), + data, + selected_theme: 0 + })) + .and_then(|mut m| { + let theme = if theme == "?" { + m.select_random_theme(rng); + } + else { + let theme = u8::from_str_radix(theme, 10).unwrap(); + m.select_theme(theme); + }; + if m.is_theme_valid() { + Some(m) + } + else { + None + } + }) } - + pub fn random(&self, rng: &mut ThreadRng) -> Music { - self.music(self.keys().choose(rng).unwrap()).unwrap() + let mut music = self.music(self.keys().choose(rng).unwrap(), rng).unwrap(); + music.select_random_theme(rng); + music } } \ No newline at end of file diff --git a/src/generator.rs b/src/generator.rs index 64728a58..5c88ceec 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -35,6 +35,7 @@ impl Music { } code += &format!(" + ld a, {}; theme call {} ; init loop @@ -49,6 +50,7 @@ loop jp loop ", + header.theme_code(self.selected_theme()), header.init_call_address(), header.play_call_address() ); diff --git a/src/main.rs b/src/main.rs index d72c4094..6eb93284 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,7 +23,7 @@ fn main() { with having such values: - random: play a random music - list: list all musics keys. Take an optional argument that corresponds to the author. - - plays the music with key . If it does not exists, search a music of author + - plays the music with key . Music key can be postfixed by : to choose a theme different than the first one. If it does not exists, search a music of author . ", args[0]); let cpcip = &args[1]; let cmd = &args[2]; @@ -37,7 +37,7 @@ with having such values: "list" => { let repr = mix.keys() - .map(|k| (k, mix.music(k).unwrap())) + .map(|k| (k, mix.music(k, &mut rand::thread_rng()).unwrap())) .map(|(k, m) | ( k, m.title().unwrap_or("".to_owned()), @@ -68,7 +68,7 @@ with having such values: ); return; } - any => mix.music(any) + any => mix.music(any, &mut rng) }; let music = match music { @@ -78,7 +78,7 @@ with having such values: if let Some(music) = mix.keys() - .map(|k| mix.music(k).unwrap()) + .map(|k| mix.music(k, &mut rand::thread_rng()).unwrap()) .filter(|m| { m.author().unwrap_or("??".to_owned()).to_lowercase() == args[2] })