Improved functionality

This commit is contained in:
G2-Games 2025-01-30 01:27:46 -06:00
parent 4cc6149ebd
commit 961fdaf245
3 changed files with 202 additions and 60 deletions

View file

@ -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() {
}
} }

View file

@ -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(())
} }
} }

View file

@ -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,
} }