diff --git a/Cargo.lock b/Cargo.lock index 32a8b6e..b966b22 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -299,6 +299,17 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "sha1" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aacc4cc499359472b4abe1bf11d0b12e688af9a805fa5e3016f9a386dc2d0214" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "sha2" version = "0.11.0" @@ -340,6 +351,7 @@ dependencies = [ "clap", "hex", "lofty", + "sha1", "sha2", ] diff --git a/Cargo.toml b/Cargo.toml index 58e223c..bb5fde9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,4 +10,5 @@ authors = ["Owen Feldman"] clap = { version = "4.6.6", features = ["derive"] } hex = "0.4.3" lofty = "0.25.1" +sha1 = "0.11.0" sha2 = "0.11.0" diff --git a/src/disc_id.rs b/src/disc_id.rs new file mode 100644 index 0000000..64aa71a --- /dev/null +++ b/src/disc_id.rs @@ -0,0 +1,69 @@ +use std::{convert, ops::Index}; + +const B64TABLE: &str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._"; + +fn b64(data: &[u8]) -> String { + let binary: Vec = data + .iter() + .flat_map(|b| (0..8).rev().map(move |i| (b >> i) & 1)) + .collect(); + let mut chunks: Vec> = binary.chunks(6).map(|s| s.to_vec()).collect(); + let last = chunks.last_mut().unwrap(); + if last.len() < 6 { + for _ in last.len()..6 { + last.push(0); + } + } + let decimal: Vec = chunks + .iter() + .map(|bits| bits.iter().fold(0, |acc, &bit| (acc << 1) | bit)) + .collect(); + let mut converted: Vec = Vec::new(); + + for dec in decimal { + converted.push(B64TABLE.chars().nth(dec as usize).unwrap().to_string()); + } + match data.len() % 3 { + 1 => { + converted.push("--".to_string()); + } + 2 => { + converted.push("-".to_string()); + } + _ => {} + } + converted.join("") +} +pub fn compute_disc_id(first_track: u8, last_track: u8, leadout: u32, offsets: &[u32]) -> String { + let mut toc = String::new(); + toc.push_str(&format!("{first_track:02X}{last_track:02X}{leadout:08X}")); + for i in 0..99 { + let offset = offsets.get(i).copied().unwrap_or(0); + toc.push_str(&format!("{:08X}", offset)); + } + toc +} +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn known_example() { + let first_track = 1; + let last_track = 12; + let leadout = 224556; + let offsets = [ + 150, 9078, 13528, 34182, 53768, 70987, 96424, 118425, 136793, 159514, 179777, 198006, + ]; + assert_eq!( + compute_disc_id(first_track, last_track, leadout, &offsets), + "T_prJXQSrqbnH8OE.dgOKsHm5Uw-" + ); + } + #[test] + fn base64() { + assert_eq!(b64("Man".as_bytes()), "TWFu".to_string()); + assert_eq!(b64("M".as_bytes()), "TQ--".to_string()); + assert_eq!(b64("Ma".as_bytes()), "TWE-".to_string()); + } +} diff --git a/src/main.rs b/src/main.rs index 7d80de4..777c2fa 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,5 @@ +mod disc_id; + use clap::{ Parser, Subcommand, builder::styling::{AnsiColor, Effects, Styles},