mirror of
https://github.com/Dangoware/cd_reader.git
synced 2025-04-19 01:42:55 -05:00
Improved functionality
This commit is contained in:
parent
4cc6149ebd
commit
961fdaf245
3 changed files with 202 additions and 60 deletions
|
@ -159,7 +159,7 @@ pub enum Status {
|
||||||
/// Disc status possibilities returned by CDROM_DISC_STATUS ioctl
|
/// Disc status possibilities returned by CDROM_DISC_STATUS ioctl
|
||||||
#[derive(FromPrimitive, ToPrimitive)]
|
#[derive(FromPrimitive, ToPrimitive)]
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum DiscStatus {
|
pub enum DiscType {
|
||||||
NoInfo = 0,
|
NoInfo = 0,
|
||||||
Audio = 100,
|
Audio = 100,
|
||||||
Data1 = 101,
|
Data1 = 101,
|
||||||
|
@ -235,8 +235,6 @@ pub enum Capability {
|
||||||
Ram = 0x2000000,
|
Ram = 0x2000000,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Capability {
|
pub enum GenericPacketCommand {
|
||||||
pub fn compare() {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
167
src/main.rs
167
src/main.rs
|
@ -5,14 +5,15 @@ use std::io::Write;
|
||||||
use std::os::fd::RawFd;
|
use std::os::fd::RawFd;
|
||||||
use std::os::{fd::IntoRawFd, unix::fs::OpenOptionsExt};
|
use std::os::{fd::IntoRawFd, unix::fs::OpenOptionsExt};
|
||||||
use std::fs::OpenOptions;
|
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::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 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;
|
use thiserror::Error;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
|
@ -20,20 +21,58 @@ 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();
|
||||||
|
|
||||||
let status = cd_rom.status().unwrap();
|
println!("Getting drive status...");
|
||||||
println!("Drive status: {:?}", status);
|
let mut status = cd_rom.status().unwrap();
|
||||||
cd_rom.close().unwrap();
|
|
||||||
|
|
||||||
println!("Disc status: {:?}", cd_rom.disc_type());
|
if status == Status::NoInfo {
|
||||||
|
println!("Cannot get disc status");
|
||||||
let header = cd_rom.toc_header().unwrap();
|
exit(1);
|
||||||
for i in header.first_track..header.last_track {
|
} else if status == Status::NoDisc {
|
||||||
let entry = cd_rom.toc_entry(i);
|
println!("No disc inserted!");
|
||||||
println!("{:?}", entry.addr);
|
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() {
|
fn rip_cd() {
|
||||||
|
@ -113,6 +152,9 @@ pub enum CDRomError {
|
||||||
|
|
||||||
#[error("the drive is in use by another user")]
|
#[error("the drive is in use by another user")]
|
||||||
Busy,
|
Busy,
|
||||||
|
|
||||||
|
#[error("the address specified was invalid")]
|
||||||
|
InvalidAddress,
|
||||||
}
|
}
|
||||||
|
|
||||||
ioctl_none_bad!(cdrom_stop, op_to_ioctl(Operation::Stop));
|
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_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_header, op_to_ioctl(Operation::ReadTocHeader), structures::TocHeader);
|
||||||
ioctl_read_bad!(cdrom_read_toc_entry, op_to_ioctl(Operation::ReadTocEntry), structures::_TocEntry);
|
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 {
|
impl CDRom {
|
||||||
/// Creates a new interface to a system CD-ROM drive.
|
/// 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
|
/// Get the type of disc currently in the drive
|
||||||
pub fn disc_type(&mut self) -> Option<DiscStatus> {
|
pub 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()?
|
||||||
};
|
};
|
||||||
|
|
||||||
DiscStatus::from_i32(status)
|
DiscType::from_i32(status)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the Media Catalog Number of the current disc.
|
/// Get the Media Catalog Number of the current disc.
|
||||||
|
@ -186,27 +230,26 @@ impl CDRom {
|
||||||
Ok(header)
|
Ok(header)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn toc_entry(&mut self, index: u8) -> TocEntry {
|
pub fn toc_entry(&mut self, index: u8, address_type: AddressType) -> TocEntry {
|
||||||
let mut header = _TocEntry::default();
|
let mut entry = _TocEntry::default();
|
||||||
header.track = index;
|
entry.track = index;
|
||||||
header.format = AddressType::Lba as u8;
|
entry.format = address_type as u8;
|
||||||
|
|
||||||
unsafe {
|
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 {
|
let entry = TocEntry {
|
||||||
track: header.track,
|
track: entry.track,
|
||||||
adr: header.adr_ctrl >> 4,
|
adr: entry.adr_ctrl >> 4,
|
||||||
ctrl: header.adr_ctrl & 0x0F,
|
ctrl: entry.adr_ctrl & 0x0F,
|
||||||
addr: unsafe {
|
addr: unsafe {
|
||||||
match header.format {
|
match entry.format {
|
||||||
d if d == AddressType::Lba as u8 => Addr::Lba(header.addr.lba),
|
d if d == AddressType::Lba as u8 => Addr::Lba(entry.addr.lba),
|
||||||
d if d == AddressType::Msf as u8 => Addr::Msf(header.addr.msf),
|
d if d == AddressType::Msf as u8 => Addr::Msf(entry.addr.msf),
|
||||||
_ => panic!("Impossible value returned!")
|
_ => panic!("Impossible value returned!")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
datamode: header.datamode,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
entry
|
entry
|
||||||
|
@ -253,9 +296,40 @@ impl CDRom {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn subchannel(&mut self, track: u8) -> Result<SubChannel, CDRomError> {
|
||||||
|
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.
|
/// 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<Vec<i16>, CDRomError> {
|
pub fn read_audio(&mut self, address: Addr, frames: usize) -> Result<Vec<i16>, CDRomError> {
|
||||||
let mut buf = vec![0i16; (frames * constants::CD_FRAMESIZE_RAW as usize) / 2];
|
let mut buf = vec![0i16; (frames * constants::CD_FRAMESIZE_RAW as usize) / 2];
|
||||||
|
|
||||||
|
@ -306,31 +380,32 @@ impl CDRom {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_raw(&mut self, address: Msf) -> Box<[u8; 2352]> {
|
pub fn read_raw_into(&mut self, address: Addr, buf: &mut [u8]) -> Result<(), CDRomError> {
|
||||||
// TODO: Make this take LBA values too
|
let address = match address {
|
||||||
// MSF values are converted to LBA values via this formula:
|
Addr::Lba(a) => Msf::from_lba(a),
|
||||||
// lba = (((m * CD_SECS) + s) * CD_FRAMES + f) - CD_MSF_OFFSET;
|
Addr::Msf(msf) => msf,
|
||||||
// <https://docs.kernel.org/userspace-api/ioctl/cdrom.html>
|
};
|
||||||
|
|
||||||
if address.minute == 0 && address.second < 2 {
|
if address.invalid() {
|
||||||
panic!("MSF second cannot be less than 2!")
|
return Err(CDRomError::InvalidAddress)
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut argument = RawResult {
|
let mut argument = RawResult {
|
||||||
cdrom_msf: MsfLong {
|
buffer: buf.as_mut_ptr(),
|
||||||
min0: address.minute,
|
|
||||||
sec0: address.second,
|
|
||||||
frame0: address.frame,
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
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();
|
cdrom_read_raw(self.drive_fd, addr_of_mut!(argument)).unwrap();
|
||||||
|
|
||||||
argument.buffer
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Box::new(result)
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::{ffi::c_int, mem};
|
use std::{ffi::c_int, mem};
|
||||||
|
|
||||||
use crate::constants::AddressType;
|
use crate::constants::{self, AddressType};
|
||||||
|
|
||||||
/// Address in MSF format
|
/// Address in MSF format
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
@ -11,6 +11,29 @@ pub struct Msf {
|
||||||
pub frame: u8,
|
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
|
/// Address in either MSF or logical format
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
|
@ -25,6 +48,22 @@ pub enum Addr {
|
||||||
Msf(Msf),
|
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`]
|
/// This struct is used by [`crate::constants::PLAY_MSF`]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, Default)]
|
#[derive(Clone, Copy, Default)]
|
||||||
|
@ -46,7 +85,7 @@ pub struct MsfLong {
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub union RawResult {
|
pub union RawResult {
|
||||||
pub cdrom_msf: MsfLong,
|
pub cdrom_msf: MsfLong,
|
||||||
pub buffer: [u8; 2352],
|
pub buffer: *mut u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This struct is used by [`crate::constants::PLAY_TRACK_INDEX`]
|
/// This struct is used by [`crate::constants::PLAY_TRACK_INDEX`]
|
||||||
|
@ -95,13 +134,12 @@ impl Default for _TocEntry {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actually public version of [`_TocEntry`].
|
// Actually public version of [`_TocEntry`].
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct TocEntry {
|
pub struct TocEntry {
|
||||||
pub track: u8,
|
pub track: u8,
|
||||||
pub adr: u8,
|
pub adr: u8,
|
||||||
pub ctrl: u8,
|
pub ctrl: u8,
|
||||||
pub addr: Addr,
|
pub addr: Addr,
|
||||||
pub datamode: u8,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct VolCtl {
|
struct VolCtl {
|
||||||
|
@ -121,10 +159,41 @@ pub struct ReadAudio {
|
||||||
pub buf: *mut i16,
|
pub buf: *mut i16,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This struct is used with the CDROM_GET_MCN ioctl.
|
#[repr(C)]
|
||||||
/// Very few audio discs actually have Universal Product Code information,
|
#[derive(Clone, Copy)]
|
||||||
/// which should just be the Medium Catalog Number on the box. Also note
|
pub(crate) struct _SubChannel {
|
||||||
/// that the way the codeis written on CD is _not_ uniform across all discs!
|
pub format: u8,
|
||||||
struct Mcn {
|
pub audiostatus: u8,
|
||||||
medium_catalog_number: [u8; 14]
|
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,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue