From ee7c529eda2d65f997ca7eef8397ea6158e012cc Mon Sep 17 00:00:00 2001 From: MrDulfin Date: Thu, 27 Feb 2025 19:02:45 -0500 Subject: [PATCH] Relocated linux implementation to its own module --- src/lib.rs | 0 src/main.rs | 300 +--------------------------------------- src/platform/linux.rs | 300 ++++++++++++++++++++++++++++++++++++++++ src/platform/mod.rs | 5 + src/platform/windows.rs | 0 5 files changed, 309 insertions(+), 296 deletions(-) create mode 100644 src/lib.rs create mode 100644 src/platform/linux.rs create mode 100644 src/platform/mod.rs create mode 100644 src/platform/windows.rs diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/main.rs b/src/main.rs index 2ee5e37..23410fe 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,21 +1,13 @@ mod constants; mod structures; +mod platform; use std::io::Write; -use std::mem; -use std::os::fd::RawFd; -use std::os::{fd::IntoRawFd, unix::fs::OpenOptionsExt}; -use std::fs::OpenOptions; use std::process::exit; -use std::ptr::{addr_of, addr_of_mut}; -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, ioctl_write_ptr_bad, libc}; +use constants::{AddressType, DiscType, Status}; +use structures::{Addr, Msf}; -use num_traits::FromPrimitive as _; -use structures::{Addr, AddrUnion, Msf, MsfLong, RawResult, ReadAudio, SubChannel, TocEntry, TocHeader, _SubChannel, _TocEntry}; -use thiserror::Error; #[macro_use] extern crate num_derive; @@ -116,288 +108,4 @@ fn rip_cd() { frame += 1; } -} - -/// Access to a CD-ROM drive on the system. -pub struct CDRom { - drive_fd: RawFd, -} - -#[derive(Error, Debug, Clone)] -pub enum CDRomError { - #[error("internal system error")] - Errno(#[from] nix::errno::Errno), - - #[error("no disc in drive to read")] - NoDisc, - - #[error("the CD does not contain cd-audio")] - NotAudioCD, - - #[error("the drive's door is locked for some reason")] - DoorLocked, - - #[error("this drive does not support the function")] - Unsupported, - - #[error("the drive is in use by another user")] - Busy, - - #[error("the address specified was invalid")] - InvalidAddress, - - #[error("the buffer size was too small; needed at least {0} bytes, got {1} bytes")] - InvalidBufferSize(usize, usize), -} - -ioctl_none_bad!(cdrom_stop, op_to_ioctl(Operation::Stop)); -ioctl_none_bad!(cdrom_start, op_to_ioctl(Operation::Start)); -ioctl_none_bad!(cdrom_eject, op_to_ioctl(Operation::Eject)); -ioctl_write_int_bad!(cdrom_lock_door, op_to_ioctl(Operation::LockDoor)); -ioctl_none_bad!(cdrom_close_tray, op_to_ioctl(Operation::CloseTray)); -ioctl_none_bad!(cdrom_status, op_to_ioctl(Operation::DriveStatus)); -ioctl_none_bad!(cdrom_disc_status, op_to_ioctl(Operation::DiscStatus)); -ioctl_readwrite_bad!(cdrom_read_audio, op_to_ioctl(Operation::ReadAudio), structures::ReadAudio); -ioctl_readwrite_bad!(cdrom_read_raw, op_to_ioctl(Operation::ReadRaw), [u8]); -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. - pub fn new() -> Option { - let drive_file = OpenOptions::new() - .read(true) - .custom_flags(libc::O_NONBLOCK | libc::O_RDONLY) - .open("/dev/sr0") - .ok()?; - - Some(Self { - drive_fd: drive_file.into_raw_fd(), - }) - } - - /// Get the currently reported status of the drive. - pub fn status(&mut self) -> Option { - let status = unsafe { - cdrom_status(self.drive_fd).unwrap() - }; - - Status::from_i32(status) - } - - /// Get the type of disc currently in the drive - pub fn disc_type(&mut self) -> Option { - let status = unsafe { - cdrom_disc_status(self.drive_fd).ok()? - }; - - DiscType::from_i32(status) - } - - /// Get the Media Catalog Number of the current disc. - /// - /// Many discs do not contain this information. - pub fn mcn(&mut self) -> Option { - let mut buffer = [0u8; 14]; - - unsafe { - cdrom_get_mcn(self.drive_fd, addr_of_mut!(buffer)).ok()?; - } - - let string = String::from_utf8_lossy(&buffer[..buffer.len() - 1]).into_owned(); - Some(string) - } - - pub fn toc_header(&mut self) -> Result { - let mut header = TocHeader::default(); - - if unsafe { - cdrom_read_toc_header(self.drive_fd, addr_of_mut!(header)) - }.is_err_and(|e| e == Errno::ENOMEDIUM) { - return Err(CDRomError::NoDisc) - } - - Ok(header) - } - - 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!(entry)).unwrap(); - } - - let entry = TocEntry { - track: entry.track, - adr: entry.adr_ctrl >> 4, - ctrl: entry.adr_ctrl & 0x0F, - addr: unsafe { - 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!") - } - }, - }; - - entry - } - - pub fn set_lock(&mut self, locked: bool) -> Result<(), CDRomError> { - let result = match unsafe { - cdrom_lock_door(self.drive_fd, locked as i32) - } { - Ok(v) => v, - Err(e) => match e { - Errno::EBUSY => return Err(CDRomError::Busy), - _ => return Err(CDRomError::Errno(e)), - }, - }; - - match result { - constants::EDRIVE_CANT_DO_THIS => Err(CDRomError::Unsupported), - _ => Ok(()) - } - } - - pub fn eject(&mut self) -> Result<(), CDRomError> { - let status = unsafe { - cdrom_eject(self.drive_fd).unwrap() - }; - - if status == 2 { - return Err(CDRomError::DoorLocked) - } - - Ok(()) - } - - pub fn close(&mut self) -> Result<(), CDRomError> { - let status = unsafe { - cdrom_close_tray(self.drive_fd).unwrap() - }; - - match status { - d if d == Errno::ENOSYS as i32 => Err(CDRomError::Unsupported), - libc::EBUSY => Err(CDRomError::DoorLocked), - _ => Ok(()), - } - } - - pub fn subchannel(&mut self) -> Result { - let mut argument = _SubChannel::default(); - - 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. - /// - /// 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]; - - self.read_audio_into(address, frames, &mut buf)?; - - Ok(buf) - } - - /// Read audio from the CD into a preallocated buffer. - /// - /// The buffer must be large enough to hold the audio for all the frames you want to read. - /// Since the values are [`i16`]s, the equation for the buffer size is `(n_frames * 2352) / 2` - pub fn read_audio_into(&mut self, address: Addr, frames: usize, buf: &mut [i16]) -> Result<(), CDRomError> { - let (addr, addr_format) = match address { - Addr::Lba(lba) => (AddrUnion { lba }, AddressType::Lba), - Addr::Msf(msf) => { - if msf.minute == 0 && msf.second < 2 { - panic!("MSF second cannot be less than 2!") - } - - (AddrUnion { msf }, AddressType::Msf) - }, - }; - - if frames < 1 || frames > 75 { - panic!("Invalid number of frames!") - } - - if buf.len() < (frames * constants::CD_FRAMESIZE_RAW as usize) / 2 { - panic!("Buffer is too small!") - } - - let mut ra = ReadAudio { - addr, - addr_format, - nframes: frames as i32, - buf: buf.as_mut_ptr() - }; - - let status = unsafe { - cdrom_read_audio(self.drive_fd, addr_of_mut!(ra)) - }?; - - if status != 0 { - return Err(Errno::from_raw(status).into()); - } - - Ok(()) - } - - 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.invalid() { - return Err(CDRomError::InvalidAddress) - } - - if buf.len() < constants::CD_FRAMESIZE_RAW as usize { - return Err(CDRomError::InvalidBufferSize(constants::CD_FRAMESIZE_RAW as usize, buf.len())) - } - - buf[0] = address.minute; - buf[1] = address.second; - buf[2] = address.frame; - - unsafe { - cdrom_read_raw(self.drive_fd, addr_of_mut!(*buf)).unwrap(); - }; - - Ok(()) - } -} +} \ No newline at end of file diff --git a/src/platform/linux.rs b/src/platform/linux.rs new file mode 100644 index 0000000..2725008 --- /dev/null +++ b/src/platform/linux.rs @@ -0,0 +1,300 @@ +/// Access to a CD-ROM drive on the system. +use std::os::fd::RawFd; +use std::os::{fd::IntoRawFd, unix::fs::OpenOptionsExt}; + +use std::fs::OpenOptions; +use std::ptr::addr_of_mut; + + +use nix::errno::Errno; +use nix::{ioctl_none_bad, ioctl_read_bad, ioctl_readwrite_bad, ioctl_write_int_bad, libc}; + +use num_traits::FromPrimitive as _; + +use crate::constants::{op_to_ioctl, AddressType, DiscType, Operation, Status, CD_FRAMESIZE_RAW}; +use crate::structures::{Addr, AddrUnion, Msf, MsfLong, ReadAudio, SubChannel, TocEntry, TocHeader, _SubChannel, _TocEntry}; +use thiserror::Error; + + +pub struct CDRom { + drive_fd: RawFd, +} + +#[derive(Error, Debug, Clone)] +pub enum CDRomError { + #[error("internal system error")] + Errno(#[from] nix::errno::Errno), + + #[error("no disc in drive to read")] + NoDisc, + + #[error("the CD does not contain cd-audio")] + NotAudioCD, + + #[error("the drive's door is locked for some reason")] + DoorLocked, + + #[error("this drive does not support the function")] + Unsupported, + + #[error("the drive is in use by another user")] + Busy, + + #[error("the address specified was invalid")] + InvalidAddress, + + #[error("the buffer size was too small; needed at least {0} bytes, got {1} bytes")] + InvalidBufferSize(usize, usize), +} + +ioctl_none_bad!(cdrom_stop, op_to_ioctl(Operation::Stop)); +ioctl_none_bad!(cdrom_start, op_to_ioctl(Operation::Start)); +ioctl_none_bad!(cdrom_eject, op_to_ioctl(Operation::Eject)); +ioctl_write_int_bad!(cdrom_lock_door, op_to_ioctl(Operation::LockDoor)); +ioctl_none_bad!(cdrom_close_tray, op_to_ioctl(Operation::CloseTray)); +ioctl_none_bad!(cdrom_status, op_to_ioctl(Operation::DriveStatus)); +ioctl_none_bad!(cdrom_disc_status, op_to_ioctl(Operation::DiscStatus)); +ioctl_readwrite_bad!(cdrom_read_audio, op_to_ioctl(Operation::ReadAudio), ReadAudio); +ioctl_readwrite_bad!(cdrom_read_raw, op_to_ioctl(Operation::ReadRaw), [u8]); +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), TocHeader); +ioctl_read_bad!(cdrom_read_toc_entry, op_to_ioctl(Operation::ReadTocEntry), _TocEntry); +ioctl_readwrite_bad!(cdrom_subchannel, op_to_ioctl(Operation::SubChannel), _SubChannel); +ioctl_read_bad!(cdrom_seek, op_to_ioctl(Operation::Seek), MsfLong); + +impl CDRom { + /// Creates a new interface to a system CD-ROM drive. + pub fn new() -> Option { + let drive_file = OpenOptions::new() + .read(true) + .custom_flags(libc::O_NONBLOCK | libc::O_RDONLY) + .open("/dev/sr0") + .ok()?; + + Some(Self { + drive_fd: drive_file.into_raw_fd(), + }) + } + + /// Get the currently reported status of the drive. + pub fn status(&mut self) -> Option { + let status = unsafe { + cdrom_status(self.drive_fd).unwrap() + }; + + Status::from_i32(status) + } + + /// Get the type of disc currently in the drive + pub fn disc_type(&mut self) -> Option { + let status = unsafe { + cdrom_disc_status(self.drive_fd).ok()? + }; + + DiscType::from_i32(status) + } + + /// Get the Media Catalog Number of the current disc. + /// + /// Many discs do not contain this information. + pub fn mcn(&mut self) -> Option { + let mut buffer = [0u8; 14]; + + unsafe { + cdrom_get_mcn(self.drive_fd, addr_of_mut!(buffer)).ok()?; + } + + let string = String::from_utf8_lossy(&buffer[..buffer.len() - 1]).into_owned(); + Some(string) + } + + pub fn toc_header(&mut self) -> Result { + let mut header = TocHeader::default(); + + if unsafe { + cdrom_read_toc_header(self.drive_fd, addr_of_mut!(header)) + }.is_err_and(|e| e == Errno::ENOMEDIUM) { + return Err(CDRomError::NoDisc) + } + + Ok(header) + } + + 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!(entry)).unwrap(); + } + + let entry = TocEntry { + track: entry.track, + adr: entry.adr_ctrl >> 4, + ctrl: entry.adr_ctrl & 0x0F, + addr: unsafe { + 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!") + } + }, + }; + + entry + } + + pub fn set_lock(&mut self, locked: bool) -> Result<(), CDRomError> { + let result = match unsafe { + cdrom_lock_door(self.drive_fd, locked as i32) + } { + Ok(v) => v, + Err(e) => match e { + Errno::EBUSY => return Err(CDRomError::Busy), + _ => return Err(CDRomError::Errno(e)), + }, + }; + + match result { + EDRIVE_CANT_DO_THIS => Err(CDRomError::Unsupported), + _ => Ok(()) + } + } + + pub fn eject(&mut self) -> Result<(), CDRomError> { + let status = unsafe { + cdrom_eject(self.drive_fd).unwrap() + }; + + if status == 2 { + return Err(CDRomError::DoorLocked) + } + + Ok(()) + } + + pub fn close(&mut self) -> Result<(), CDRomError> { + let status = unsafe { + cdrom_close_tray(self.drive_fd).unwrap() + }; + + match status { + d if d == Errno::ENOSYS as i32 => Err(CDRomError::Unsupported), + libc::EBUSY => Err(CDRomError::DoorLocked), + _ => Ok(()), + } + } + + pub fn subchannel(&mut self) -> Result { + let mut argument = _SubChannel::default(); + + 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. + /// + /// 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 * CD_FRAMESIZE_RAW as usize) / 2]; + + self.read_audio_into(address, frames, &mut buf)?; + + Ok(buf) + } + + /// Read audio from the CD into a preallocated buffer. + /// + /// The buffer must be large enough to hold the audio for all the frames you want to read. + /// Since the values are [`i16`]s, the equation for the buffer size is `(n_frames * 2352) / 2` + pub fn read_audio_into(&mut self, address: Addr, frames: usize, buf: &mut [i16]) -> Result<(), CDRomError> { + let (addr, addr_format) = match address { + Addr::Lba(lba) => (AddrUnion { lba }, AddressType::Lba), + Addr::Msf(msf) => { + if msf.minute == 0 && msf.second < 2 { + panic!("MSF second cannot be less than 2!") + } + + (AddrUnion { msf }, AddressType::Msf) + }, + }; + + if frames < 1 || frames > 75 { + panic!("Invalid number of frames!") + } + + if buf.len() < (frames * CD_FRAMESIZE_RAW as usize) / 2 { + panic!("Buffer is too small!") + } + + let mut ra = ReadAudio { + addr, + addr_format, + nframes: frames as i32, + buf: buf.as_mut_ptr() + }; + + let status = unsafe { + cdrom_read_audio(self.drive_fd, addr_of_mut!(ra)) + }?; + + if status != 0 { + return Err(Errno::from_raw(status).into()); + } + + Ok(()) + } + + 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.invalid() { + return Err(CDRomError::InvalidAddress) + } + + if buf.len() < CD_FRAMESIZE_RAW as usize { + return Err(CDRomError::InvalidBufferSize(CD_FRAMESIZE_RAW as usize, buf.len())) + } + + buf[0] = address.minute; + buf[1] = address.second; + buf[2] = address.frame; + + unsafe { + cdrom_read_raw(self.drive_fd, addr_of_mut!(*buf)).unwrap(); + }; + + Ok(()) + } +} diff --git a/src/platform/mod.rs b/src/platform/mod.rs new file mode 100644 index 0000000..782612f --- /dev/null +++ b/src/platform/mod.rs @@ -0,0 +1,5 @@ +#[cfg(target_os="linux")] +pub mod linux; + +#[cfg(target_os="windows")] +pub mod windows; \ No newline at end of file diff --git a/src/platform/windows.rs b/src/platform/windows.rs new file mode 100644 index 0000000..e69de29