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
#[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 {
}
}

View file

@ -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<DiscStatus> {
pub fn disc_type(&mut self) -> Option<DiscType> {
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<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.
///
/// 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> {
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;
// <https://docs.kernel.org/userspace-api/ioctl/cdrom.html>
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 {
buffer: buf.as_mut_ptr(),
};
// Set the union value to the MSF values
argument.cdrom_msf = MsfLong {
min0: address.minute,
sec0: address.second,
frame0: address.frame,
..Default::default()
}
};
let result = unsafe {
unsafe {
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 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,
}