Created trait for CDRom

This commit is contained in:
MrDulfin 2025-03-02 14:14:12 -05:00
parent ee7c529eda
commit 937715fb9c
7 changed files with 135 additions and 59 deletions

3
.gitignore vendored
View file

@ -1 +1,4 @@
/target /target
Cargo.lock
.cargo

View file

@ -1,5 +1,5 @@
[package] [package]
name = "cd_read" name = "sunny_milk"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
@ -10,6 +10,7 @@ nix = { version = "0.29.0", features = ["ioctl"] }
num-derive = "0.4.2" num-derive = "0.4.2"
num-traits = "0.2.19" num-traits = "0.2.19"
thiserror = "2.0.11" thiserror = "2.0.11"
windows = "0.60.0"
[profile.release] [profile.release]
strip = true # Automatically strip symbols from the binary. strip = true # Automatically strip symbols from the binary.

View file

@ -3,8 +3,6 @@
use num_traits::ToPrimitive; use num_traits::ToPrimitive;
pub const EDRIVE_CANT_DO_THIS: i32 = nix::errno::Errno::EOPNOTSUPP as i32;
/// CDROM ioctl byte, from <linux/cdrom.h> /// CDROM ioctl byte, from <linux/cdrom.h>
pub const IOC_BYTE: u8 = 0x53; pub const IOC_BYTE: u8 = 0x53;

View file

@ -0,0 +1,93 @@
use constants::AddressType;
use thiserror::Error;
use crate::{constants::{DiscType, Status}, structures::{Addr, SubChannel, TocEntry, TocHeader}};
#[macro_use]
extern crate num_derive;
pub mod constants;
pub mod structures;
pub mod platform;
#[cfg(target_os="linux")]
pub type CDRom = platform::linux::CDRomLinux;
#[cfg(target_os="windows")]
pub type CDRom = platform::windows::CDRomWindows;
#[derive(Error, Debug, Clone)]
pub enum CDRomError {
#[cfg(target_os="linux")]
#[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),
}
pub trait CDRomTrait {
/// Get the currently reported status of the drive.
fn status(&mut self) -> Option<Status>;
/// Get the type of disc currently in the drive
fn disc_type(&mut self) -> Option<DiscType>;
/// Get the Media Catalog Number of the current disc.
///
/// Many discs do not contain this information.
fn mcn(&mut self) -> Option<String>;
fn toc_header(&mut self) -> Result<TocHeader, CDRomError>;
fn toc_entry(&mut self, index: u8, address_type: AddressType) -> TocEntry;
fn set_lock(&mut self, locked: bool) -> Result<(), CDRomError>;
fn eject(&mut self) -> Result<(), CDRomError>;
fn close(&mut self) -> Result<(), CDRomError>;
fn subchannel(&mut self) -> Result<SubChannel, CDRomError>;
/// Read audio from the CD.
///
/// This method is a convenience method around [`CDRom::read_audio_into`].
fn read_audio(&mut self, address: Addr, frames: usize) -> Result<Vec<i16>, CDRomError>;
/// 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`
fn read_audio_into(
&mut self,
address: Addr,
frames: usize,
buf: &mut [i16]
) -> Result<(), CDRomError>;
fn read_raw_into(
&mut self,
address: Addr,
buf: &mut [u8]
) -> Result<(), CDRomError>;
}

View file

@ -1,20 +1,13 @@
mod constants;
mod structures;
mod platform;
use std::io::Write; use std::io::Write;
use std::process::exit; use std::process::exit;
use constants::{AddressType, DiscType, Status}; use sunny_milk::constants::{AddressType, DiscType, Status, CD_FRAMESIZE_RAW};
use structures::{Addr, Msf}; use sunny_milk::structures::{Addr, Msf};
use sunny_milk::{CDRom, CDRomTrait};
#[macro_use]
extern crate num_derive;
fn main() { fn main() {
let mut cd_rom = CDRom::new().unwrap(); let mut cd_rom = CDRom::new().unwrap();
cd_rom.set_lock(true).unwrap(); // cd_rom.set_lock(true).unwrap();
println!("Getting drive status..."); println!("Getting drive status...");
let mut status = cd_rom.status().unwrap(); let mut status = cd_rom.status().unwrap();
@ -80,7 +73,7 @@ fn rip_cd() {
println!("Disc status: {:?}", cd_rom.disc_type().unwrap()); println!("Disc status: {:?}", cd_rom.disc_type().unwrap());
let mut raw_output = std::fs::File::create("raw_cd").unwrap(); let mut raw_output = std::fs::File::create("raw_cd").unwrap();
let mut buffer = vec![32u8; constants::CD_FRAMESIZE_RAW as usize]; let mut buffer = vec![32u8; CD_FRAMESIZE_RAW as usize];
let mut frame = 0i32; let mut frame = 0i32;
loop { loop {

View file

@ -13,40 +13,14 @@ use num_traits::FromPrimitive as _;
use crate::constants::{op_to_ioctl, AddressType, DiscType, Operation, Status, CD_FRAMESIZE_RAW}; 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 crate::structures::{Addr, AddrUnion, Msf, MsfLong, ReadAudio, SubChannel, TocEntry, TocHeader, _SubChannel, _TocEntry};
use thiserror::Error; use crate::{CDRomError, CDRomTrait};
pub const EDRIVE_CANT_DO_THIS: i32 = nix::errno::Errno::EOPNOTSUPP as i32;
pub struct CDRom { pub struct CDRomLinux {
drive_fd: RawFd, 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_stop, op_to_ioctl(Operation::Stop));
ioctl_none_bad!(cdrom_start, op_to_ioctl(Operation::Start)); ioctl_none_bad!(cdrom_start, op_to_ioctl(Operation::Start));
ioctl_none_bad!(cdrom_eject, op_to_ioctl(Operation::Eject)); ioctl_none_bad!(cdrom_eject, op_to_ioctl(Operation::Eject));
@ -62,7 +36,7 @@ ioctl_read_bad!(cdrom_read_toc_entry, op_to_ioctl(Operation::ReadTocEntry), _Toc
ioctl_readwrite_bad!(cdrom_subchannel, op_to_ioctl(Operation::SubChannel), _SubChannel); ioctl_readwrite_bad!(cdrom_subchannel, op_to_ioctl(Operation::SubChannel), _SubChannel);
ioctl_read_bad!(cdrom_seek, op_to_ioctl(Operation::Seek), MsfLong); ioctl_read_bad!(cdrom_seek, op_to_ioctl(Operation::Seek), MsfLong);
impl CDRom { impl CDRomLinux {
/// Creates a new interface to a system CD-ROM drive. /// Creates a new interface to a system CD-ROM drive.
pub fn new() -> Option<Self> { pub fn new() -> Option<Self> {
let drive_file = OpenOptions::new() let drive_file = OpenOptions::new()
@ -75,9 +49,10 @@ impl CDRom {
drive_fd: drive_file.into_raw_fd(), drive_fd: drive_file.into_raw_fd(),
}) })
} }
}
impl CDRomTrait for CDRomLinux {
/// Get the currently reported status of the drive. /// Get the currently reported status of the drive.
pub fn status(&mut self) -> Option<Status> { fn status(&mut self) -> Option<Status> {
let status = unsafe { let status = unsafe {
cdrom_status(self.drive_fd).unwrap() cdrom_status(self.drive_fd).unwrap()
}; };
@ -86,7 +61,7 @@ impl CDRom {
} }
/// Get the type of disc currently in the drive /// Get the type of disc currently in the drive
pub fn disc_type(&mut self) -> Option<DiscType> { fn disc_type(&mut self) -> Option<DiscType> {
let status = unsafe { let status = unsafe {
cdrom_disc_status(self.drive_fd).ok()? cdrom_disc_status(self.drive_fd).ok()?
}; };
@ -97,7 +72,7 @@ impl CDRom {
/// Get the Media Catalog Number of the current disc. /// Get the Media Catalog Number of the current disc.
/// ///
/// Many discs do not contain this information. /// Many discs do not contain this information.
pub fn mcn(&mut self) -> Option<String> { fn mcn(&mut self) -> Option<String> {
let mut buffer = [0u8; 14]; let mut buffer = [0u8; 14];
unsafe { unsafe {
@ -108,7 +83,7 @@ impl CDRom {
Some(string) Some(string)
} }
pub fn toc_header(&mut self) -> Result<TocHeader, CDRomError> { fn toc_header(&mut self) -> Result<TocHeader, CDRomError> {
let mut header = TocHeader::default(); let mut header = TocHeader::default();
if unsafe { if unsafe {
@ -120,7 +95,7 @@ impl CDRom {
Ok(header) Ok(header)
} }
pub fn toc_entry(&mut self, index: u8, address_type: AddressType) -> TocEntry { fn toc_entry(&mut self, index: u8, address_type: AddressType) -> TocEntry {
let mut entry = _TocEntry::default(); let mut entry = _TocEntry::default();
entry.track = index; entry.track = index;
entry.format = address_type as u8; entry.format = address_type as u8;
@ -145,7 +120,7 @@ impl CDRom {
entry entry
} }
pub fn set_lock(&mut self, locked: bool) -> Result<(), CDRomError> { fn set_lock(&mut self, locked: bool) -> Result<(), CDRomError> {
let result = match unsafe { let result = match unsafe {
cdrom_lock_door(self.drive_fd, locked as i32) cdrom_lock_door(self.drive_fd, locked as i32)
} { } {
@ -162,7 +137,7 @@ impl CDRom {
} }
} }
pub fn eject(&mut self) -> Result<(), CDRomError> { fn eject(&mut self) -> Result<(), CDRomError> {
let status = unsafe { let status = unsafe {
cdrom_eject(self.drive_fd).unwrap() cdrom_eject(self.drive_fd).unwrap()
}; };
@ -174,7 +149,7 @@ impl CDRom {
Ok(()) Ok(())
} }
pub fn close(&mut self) -> Result<(), CDRomError> { fn close(&mut self) -> Result<(), CDRomError> {
let status = unsafe { let status = unsafe {
cdrom_close_tray(self.drive_fd).unwrap() cdrom_close_tray(self.drive_fd).unwrap()
}; };
@ -186,7 +161,7 @@ impl CDRom {
} }
} }
pub fn subchannel(&mut self) -> Result<SubChannel, CDRomError> { fn subchannel(&mut self) -> Result<SubChannel, CDRomError> {
let mut argument = _SubChannel::default(); let mut argument = _SubChannel::default();
unsafe { unsafe {
@ -218,8 +193,8 @@ impl CDRom {
/// Read audio from the CD. /// Read audio from the CD.
/// ///
/// This method is a convenience method around [`CDRom::read_audio_into`]. /// This method is a convenience method around [`CDRomLinux::read_audio_into`].
pub fn read_audio(&mut self, address: Addr, frames: usize) -> Result<Vec<i16>, CDRomError> { fn read_audio(&mut self, address: Addr, frames: usize) -> Result<Vec<i16>, CDRomError> {
let mut buf = vec![0i16; (frames * CD_FRAMESIZE_RAW as usize) / 2]; let mut buf = vec![0i16; (frames * CD_FRAMESIZE_RAW as usize) / 2];
self.read_audio_into(address, frames, &mut buf)?; self.read_audio_into(address, frames, &mut buf)?;
@ -231,7 +206,7 @@ impl CDRom {
/// ///
/// The buffer must be large enough to hold the audio for all the frames you want to read. /// 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` /// 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> { fn read_audio_into(&mut self, address: Addr, frames: usize, buf: &mut [i16]) -> Result<(), CDRomError> {
let (addr, addr_format) = match address { let (addr, addr_format) = match address {
Addr::Lba(lba) => (AddrUnion { lba }, AddressType::Lba), Addr::Lba(lba) => (AddrUnion { lba }, AddressType::Lba),
Addr::Msf(msf) => { Addr::Msf(msf) => {
@ -269,7 +244,7 @@ impl CDRom {
Ok(()) Ok(())
} }
pub fn read_raw_into( fn read_raw_into(
&mut self, &mut self,
address: Addr, address: Addr,
buf: &mut [u8] buf: &mut [u8]

View file

@ -0,0 +1,13 @@
use crate::{CDRomError, CDRomTrait};
pub struct CDRomWindows {}
impl CDRomWindows {
pub fn new() -> Result<Self, CDRomError> {
Ok(Self { })
}
}
impl CDRomTrait for CDRomWindows {
}