From 961fdaf2458505d9323f45a528cdada504baa60c Mon Sep 17 00:00:00 2001 From: G2-Games Date: Thu, 30 Jan 2025 01:27:46 -0600 Subject: [PATCH] Improved functionality --- src/constants.rs | 6 +- src/main.rs | 167 +++++++++++++++++++++++++++++++++------------- src/structures.rs | 89 +++++++++++++++++++++--- 3 files changed, 202 insertions(+), 60 deletions(-) diff --git a/src/constants.rs b/src/constants.rs index d4a7a99..ad78ab4 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -159,7 +159,7 @@ pub enum Status { /// Disc status possibilities returned by CDROM_DISC_STATUS ioctl #[derive(FromPrimitive, ToPrimitive)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum DiscStatus { +pub enum DiscType { NoInfo = 0, Audio = 100, Data1 = 101, @@ -235,8 +235,6 @@ pub enum Capability { Ram = 0x2000000, } -impl Capability { - pub fn compare() { +pub enum GenericPacketCommand { - } } diff --git a/src/main.rs b/src/main.rs index d3f164e..6acf901 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,14 +5,15 @@ use std::io::Write; use std::os::fd::RawFd; use std::os::{fd::IntoRawFd, unix::fs::OpenOptionsExt}; use std::fs::OpenOptions; -use std::ptr::addr_of_mut; +use std::process::exit; +use std::ptr::{addr_of, addr_of_mut}; -use constants::{op_to_ioctl, AddressType, DiscStatus, Operation, Status}; +use constants::{op_to_ioctl, AddressType, DiscType, Operation, Status}; use nix::errno::Errno; -use nix::{ioctl_none_bad, ioctl_read_bad, ioctl_readwrite_bad, ioctl_write_int_bad, libc}; +use nix::{ioctl_none_bad, ioctl_read_bad, ioctl_readwrite_bad, ioctl_write_int_bad, ioctl_write_ptr_bad, libc}; use num_traits::FromPrimitive as _; -use structures::{Addr, AddrUnion, Msf, MsfLong, RawResult, ReadAudio, TocEntry, TocHeader, _TocEntry}; +use structures::{Addr, AddrUnion, Msf, MsfLong, RawResult, ReadAudio, SubChannel, TocEntry, TocHeader, _SubChannel, _TocEntry}; use thiserror::Error; #[macro_use] @@ -20,20 +21,58 @@ extern crate num_derive; fn main() { let mut cd_rom = CDRom::new().unwrap(); + cd_rom.set_lock(true).unwrap(); - let status = cd_rom.status().unwrap(); - println!("Drive status: {:?}", status); - cd_rom.close().unwrap(); + println!("Getting drive status..."); + let mut status = cd_rom.status().unwrap(); - println!("Disc status: {:?}", cd_rom.disc_type()); - - let header = cd_rom.toc_header().unwrap(); - for i in header.first_track..header.last_track { - let entry = cd_rom.toc_entry(i); - println!("{:?}", entry.addr); + if status == Status::NoInfo { + println!("Cannot get disc status"); + exit(1); + } else if status == Status::NoDisc { + println!("No disc inserted!"); + exit(1); } - cd_rom.set_lock(false).unwrap(); + while status != Status::DiscOK { + status = cd_rom.status().unwrap(); + if status == Status::TrayOpen { + cd_rom.close().unwrap(); + } + + std::thread::sleep(std::time::Duration::from_secs(1)); + } + println!("Drive status:\t{:?}", status); + + let disc_type = cd_rom.disc_type(); + println!("Disc type:\t{:?}", disc_type.unwrap_or(DiscType::NoInfo)); + if disc_type != Some(DiscType::Audio) { + println!("\nNot an audio CD! Will not continue."); + exit(0); + } + + println!("Disc MCN: {}", cd_rom.mcn().unwrap_or_default()); + + let header = cd_rom.toc_header().unwrap(); + println!("Disc contains {} tracks", header.last_track); + + for i in header.first_track..header.last_track { + let entry = cd_rom.toc_entry(i, AddressType::Msf); + + println!("Track {:>4} -------", entry.track); + + match entry.addr { + Addr::Lba(a) => { + let msf = Msf::from_lba(a); + println!("\tMSF: {:02}:{:02}.{:02}\n\tLBA: {}", msf.minute, msf.second, msf.frame, a); + } + Addr::Msf(a) => { + let lba = a.to_lba(); + println!("\tMSF: {:02}:{:02}.{:02}\n\tLBA: {}", a.minute, a.second, a.frame, lba); + } + } + println!(); + } } fn rip_cd() { @@ -113,6 +152,9 @@ pub enum CDRomError { #[error("the drive is in use by another user")] Busy, + + #[error("the address specified was invalid")] + InvalidAddress, } ioctl_none_bad!(cdrom_stop, op_to_ioctl(Operation::Stop)); @@ -127,6 +169,8 @@ ioctl_readwrite_bad!(cdrom_read_raw, op_to_ioctl(Operation::ReadRaw), structures ioctl_read_bad!(cdrom_get_mcn, op_to_ioctl(Operation::GetMcn), [u8; 14]); ioctl_read_bad!(cdrom_read_toc_header, op_to_ioctl(Operation::ReadTocHeader), structures::TocHeader); ioctl_read_bad!(cdrom_read_toc_entry, op_to_ioctl(Operation::ReadTocEntry), structures::_TocEntry); +ioctl_readwrite_bad!(cdrom_subchannel, op_to_ioctl(Operation::SubChannel), structures::_SubChannel); +ioctl_read_bad!(cdrom_seek, op_to_ioctl(Operation::Seek), structures::MsfLong); impl CDRom { /// Creates a new interface to a system CD-ROM drive. @@ -152,12 +196,12 @@ impl CDRom { } /// Get the type of disc currently in the drive - pub fn disc_type(&mut self) -> Option { + pub fn disc_type(&mut self) -> Option { let status = unsafe { cdrom_disc_status(self.drive_fd).ok()? }; - DiscStatus::from_i32(status) + DiscType::from_i32(status) } /// Get the Media Catalog Number of the current disc. @@ -186,27 +230,26 @@ impl CDRom { Ok(header) } - pub fn toc_entry(&mut self, index: u8) -> TocEntry { - let mut header = _TocEntry::default(); - header.track = index; - header.format = AddressType::Lba as u8; + pub fn toc_entry(&mut self, index: u8, address_type: AddressType) -> TocEntry { + let mut entry = _TocEntry::default(); + entry.track = index; + entry.format = address_type as u8; unsafe { - cdrom_read_toc_entry(self.drive_fd, addr_of_mut!(header)).unwrap(); + cdrom_read_toc_entry(self.drive_fd, addr_of_mut!(entry)).unwrap(); } let entry = TocEntry { - track: header.track, - adr: header.adr_ctrl >> 4, - ctrl: header.adr_ctrl & 0x0F, + track: entry.track, + adr: entry.adr_ctrl >> 4, + ctrl: entry.adr_ctrl & 0x0F, addr: unsafe { - match header.format { - d if d == AddressType::Lba as u8 => Addr::Lba(header.addr.lba), - d if d == AddressType::Msf as u8 => Addr::Msf(header.addr.msf), + match entry.format { + d if d == AddressType::Lba as u8 => Addr::Lba(entry.addr.lba), + d if d == AddressType::Msf as u8 => Addr::Msf(entry.addr.msf), _ => panic!("Impossible value returned!") } }, - datamode: header.datamode, }; entry @@ -253,9 +296,40 @@ impl CDRom { } } + pub fn subchannel(&mut self, track: u8) -> Result { + let mut argument = _SubChannel::default(); + argument.trk = track; + + unsafe { + cdrom_subchannel(self.drive_fd, addr_of_mut!(argument)).unwrap(); + } + + Ok(SubChannel { + audiostatus: argument.audiostatus, + adr: argument.adr_ctrl >> 4, + ctrl: argument.adr_ctrl & 0x0F, + trk: argument.trk, + ind: argument.ind, + absaddr: unsafe { + match argument.format { + d if d == AddressType::Lba as u8 => Addr::Lba(argument.absaddr.lba), + d if d == AddressType::Msf as u8 => Addr::Msf(argument.absaddr.msf), + _ => panic!("Impossible value returned!") + } + }, + reladdr: unsafe { + match argument.format { + d if d == AddressType::Lba as u8 => Addr::Lba(argument.reladdr.lba), + d if d == AddressType::Msf as u8 => Addr::Msf(argument.reladdr.msf), + _ => panic!("Impossible value returned!") + } + } + }) + } + /// Read audio from the CD. /// - /// The buffer will be constructed automatically. + /// This method is a convenience method around [`CDRom::read_audio_into`]. pub fn read_audio(&mut self, address: Addr, frames: usize) -> Result, CDRomError> { let mut buf = vec![0i16; (frames * constants::CD_FRAMESIZE_RAW as usize) / 2]; @@ -306,31 +380,32 @@ impl CDRom { Ok(()) } - pub fn read_raw(&mut self, address: Msf) -> Box<[u8; 2352]> { - // TODO: Make this take LBA values too - // MSF values are converted to LBA values via this formula: - // lba = (((m * CD_SECS) + s) * CD_FRAMES + f) - CD_MSF_OFFSET; - // + pub fn read_raw_into(&mut self, address: Addr, buf: &mut [u8]) -> Result<(), CDRomError> { + let address = match address { + Addr::Lba(a) => Msf::from_lba(a), + Addr::Msf(msf) => msf, + }; - if address.minute == 0 && address.second < 2 { - panic!("MSF second cannot be less than 2!") + if address.invalid() { + return Err(CDRomError::InvalidAddress) } let mut argument = RawResult { - cdrom_msf: MsfLong { - min0: address.minute, - sec0: address.second, - frame0: address.frame, - ..Default::default() - } + buffer: buf.as_mut_ptr(), }; - let result = unsafe { + // Set the union value to the MSF values + argument.cdrom_msf = MsfLong { + min0: address.minute, + sec0: address.second, + frame0: address.frame, + ..Default::default() + }; + + unsafe { cdrom_read_raw(self.drive_fd, addr_of_mut!(argument)).unwrap(); - - argument.buffer }; - Box::new(result) + Ok(()) } } diff --git a/src/structures.rs b/src/structures.rs index f890bb2..b782631 100644 --- a/src/structures.rs +++ b/src/structures.rs @@ -1,6 +1,6 @@ use std::{ffi::c_int, mem}; -use crate::constants::AddressType; +use crate::constants::{self, AddressType}; /// Address in MSF format #[repr(C)] @@ -11,6 +11,29 @@ pub struct Msf { pub frame: u8, } +impl Msf { + pub fn to_lba(&self) -> i32 { + (((self.minute as i32 * constants::CD_SECS) + self.second as i32) * constants::CD_FRAMES + self.frame as i32) - constants::CD_MSF_OFFSET + } + + pub fn from_lba(lba: i32) -> Self { + let offset_a = lba + constants::CD_MSF_OFFSET; + Msf { + minute: ((offset_a / constants::CD_FRAMES) / 60) as u8, + second: ((offset_a / constants::CD_FRAMES) % 60) as u8, + frame: (offset_a % 75) as u8, + } + } + + pub fn invalid(&self) -> bool { + if self.minute == 0 && self.second < 2 { + true + } else { + false + } + } +} + /// Address in either MSF or logical format #[repr(C)] #[derive(Clone, Copy)] @@ -25,6 +48,22 @@ pub enum Addr { Msf(Msf), } +impl Addr { + pub fn into_msf(self) -> Msf { + match self { + Addr::Lba(a) => Msf::from_lba(a), + Addr::Msf(msf) => msf, + } + } + + pub fn into_lba(self) -> i32 { + match self { + Addr::Lba(a) => a, + Addr::Msf(msf) => msf.to_lba(), + } + } +} + /// This struct is used by [`crate::constants::PLAY_MSF`] #[repr(C)] #[derive(Clone, Copy, Default)] @@ -46,7 +85,7 @@ pub struct MsfLong { #[repr(C)] pub union RawResult { pub cdrom_msf: MsfLong, - pub buffer: [u8; 2352], + pub buffer: *mut u8, } /// This struct is used by [`crate::constants::PLAY_TRACK_INDEX`] @@ -95,13 +134,12 @@ impl Default for _TocEntry { } // Actually public version of [`_TocEntry`]. -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub struct TocEntry { pub track: u8, pub adr: u8, pub ctrl: u8, pub addr: Addr, - pub datamode: u8, } struct VolCtl { @@ -121,10 +159,41 @@ pub struct ReadAudio { pub buf: *mut i16, } -/// This struct is used with the CDROM_GET_MCN ioctl. -/// Very few audio discs actually have Universal Product Code information, -/// which should just be the Medium Catalog Number on the box. Also note -/// that the way the codeis written on CD is _not_ uniform across all discs! -struct Mcn { - medium_catalog_number: [u8; 14] +#[repr(C)] +#[derive(Clone, Copy)] +pub(crate) struct _SubChannel { + pub format: u8, + pub audiostatus: u8, + pub adr_ctrl: u8, + pub trk: u8, + pub ind: u8, + pub absaddr: AddrUnion, + pub reladdr: AddrUnion, +} + +impl Default for _SubChannel { + fn default() -> Self { + unsafe { + Self { + format: AddressType::Msf as u8, + audiostatus: 0, + adr_ctrl: 0, + trk: 0, + ind: 0, + absaddr: mem::zeroed(), + reladdr: mem::zeroed(), + } + } + } +} + +#[derive(Debug, Clone, Copy)] +pub struct SubChannel { + pub audiostatus: u8, + pub adr: u8, + pub ctrl: u8, + pub trk: u8, + pub ind: u8, + pub absaddr: Addr, + pub reladdr: Addr, }