/* * Copyright (C) 2006 Mike Melanson (mike at multimedia.cx) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * Compact Disc Detection Experiment * * compile with: * cc -Wall cdexp.c -o cdexp */ #define THIS_IS_LINUX //#define THIS_IS_SUN //#define THIS_IS_FREEBSD #include #include #include #include #include #include #include #include #define CD_SECONDS_PER_MINUTE 60 #define CD_FRAMES_PER_SECOND 75 #define CD_RAW_FRAME_SIZE 2352 #define LEADOUT_TRACK 0xAA typedef struct _cdrom_toc_entry { int track_mode; int first_frame; int first_frame_minute; int first_frame_second; int first_frame_frame; } cdrom_toc_entry; typedef struct _cdrom_toc { int first_track; int last_track; int total_tracks; cdrom_toc_entry *toc_entries; cdrom_toc_entry leadout_track; } cdrom_toc; #ifdef THIS_IS_LINUX #include void read_cdrom_toc(int fd, cdrom_toc *toc) { struct cdrom_tochdr tochdr; struct cdrom_tocentry tocentry; int i; /* fetch the table of contents */ if (ioctl(fd, CDROMREADTOCHDR, &tochdr) == -1) { perror("CDROMREADTOCHDR"); return; } toc->first_track = tochdr.cdth_trk0; toc->last_track = tochdr.cdth_trk1; toc->total_tracks = toc->last_track - toc->first_track + 1; /* allocate space for the toc entries */ toc->toc_entries = (cdrom_toc_entry *)malloc(toc->total_tracks * sizeof(cdrom_toc_entry)); if (!toc->toc_entries) { perror("malloc"); return; } /* fetch each toc entry */ for (i = toc->first_track; i <= toc->last_track; i++) { memset(&tocentry, 0, sizeof(tocentry)); tocentry.cdte_track = i; tocentry.cdte_format = CDROM_MSF; if (ioctl(fd, CDROMREADTOCENTRY, &tocentry) == -1) { perror("CDROMREADTOCENTRY"); return; } toc->toc_entries[i-1].track_mode = (tocentry.cdte_ctrl & 0x04) ? 1 : 0; toc->toc_entries[i-1].first_frame_minute = tocentry.cdte_addr.msf.minute; toc->toc_entries[i-1].first_frame_second = tocentry.cdte_addr.msf.second; toc->toc_entries[i-1].first_frame_frame = tocentry.cdte_addr.msf.frame; toc->toc_entries[i-1].first_frame = (tocentry.cdte_addr.msf.minute * CD_SECONDS_PER_MINUTE * CD_FRAMES_PER_SECOND) + (tocentry.cdte_addr.msf.second * CD_FRAMES_PER_SECOND) + tocentry.cdte_addr.msf.frame; } /* fetch the leadout as well */ memset(&tocentry, 0, sizeof(tocentry)); tocentry.cdte_track = LEADOUT_TRACK; tocentry.cdte_format = CDROM_MSF; if (ioctl(fd, CDROMREADTOCENTRY, &tocentry) == -1) { perror("CDROMREADTOCENTRY"); return; } toc->leadout_track.track_mode = (tocentry.cdte_ctrl & 0x04) ? 1 : 0; toc->leadout_track.first_frame_minute = tocentry.cdte_addr.msf.minute; toc->leadout_track.first_frame_second = tocentry.cdte_addr.msf.second; toc->leadout_track.first_frame_frame = tocentry.cdte_addr.msf.frame; toc->leadout_track.first_frame = (tocentry.cdte_addr.msf.minute * CD_SECONDS_PER_MINUTE * CD_FRAMES_PER_SECOND) + (tocentry.cdte_addr.msf.second * CD_FRAMES_PER_SECOND) + tocentry.cdte_addr.msf.frame; } void read_cdrom_frame(int fd, int frame, unsigned char data[CD_RAW_FRAME_SIZE]) { struct cdrom_msf msf; /* read from starting frame... */ msf.cdmsf_min0 = frame / CD_SECONDS_PER_MINUTE / CD_FRAMES_PER_SECOND; msf.cdmsf_sec0 = (frame / CD_FRAMES_PER_SECOND) % CD_SECONDS_PER_MINUTE; msf.cdmsf_frame0 = frame % CD_FRAMES_PER_SECOND; /* read until ending track (starting frame + 1)... */ msf.cdmsf_min1 = (frame + 1) / CD_SECONDS_PER_MINUTE / CD_FRAMES_PER_SECOND; msf.cdmsf_sec1 = ((frame + 1) / CD_FRAMES_PER_SECOND) % CD_SECONDS_PER_MINUTE; msf.cdmsf_frame1 = (frame + 1) % CD_FRAMES_PER_SECOND; /* MSF structure is the input to the ioctl */ memcpy(data, &msf, sizeof(msf)); /* read a frame */ if(ioctl(fd, CDROMREADRAW, data, data) < 0) { perror("CDROMREADRAW"); return; } } #endif #ifdef THIS_IS_SUN #include #include #include void read_cdrom_toc(int fd, cdrom_toc *toc) { } void read_cdrom_frame(int fd, int frame, unsigned char data[CD_RAW_FRAME_SIZE]) { } #endif #ifdef THIS_IS_FREEBSD #include void read_cdrom_toc(int fd, cdrom_toc *toc) { struct ioc_toc_header tochdr; struct ioc_read_toc_entry te; struct cd_toc_entry *tocent; int i; /* read TOC header */ if ( ioctl(fd, CDIOREADTOCHEADER, &tochdr) == -1 ) { printf ("error in ioctl CDROMREADTOCHDR\n"); return -1; } toc->first_track = tochdr.starting_track; toc->last_track = tochdr.ending_track; toc->total_tracks = toc->last_track - toc->first_track + 1; /* allocate space for all of the toc entries */ tocent = (struct cd_toc_entry *) malloc(sizeof(struct cd_toc_entry) * toc->total_tracks); te.address_format = CD_MSF_FORMAT; te.starting_track = 0; te.data_len = toc->total_tracks * sizeof(struct cd_toc_entry); te.data = this->tocent; /* fetch the whole toc */ if ( ioctl(fd, CDIOREADTOCENTRYS, &te) == -1 ){ printf ("error in ioctl CDROMREADTOCENTRY\n"); return; } /* allocate space for the internal toc entries */ toc->toc_entries = (cdrom_toc_entry *)malloc(toc->total_tracks * sizeof(cdrom_toc_entry)); if (!toc->toc_entries) { perror("malloc"); free(tocent); return; } /* copy over the relevant information from each toc entry */ for (i = 0; i < toc->total_tracks; i++) { toc->toc_entries[i-1].track_mode = tocentry.cdte_datamode; toc->toc_entries[i-1].first_frame_minute = tocentry.cdte_addr.msf.minute; toc->toc_entries[i-1].first_frame_second = tocentry.cdte_addr.msf.second; toc->toc_entries[i-1].first_frame_frame = tocentry.cdte_addr.msf.frame; toc->toc_entries[i-1].first_frame = } } void read_cdrom_frame(int fd, int frame, unsigned char data[CD_RAW_FRAME_SIZE]) { } #endif int main(int argc, char *argv[]) { int fd; int status; cdrom_toc toc; unsigned char frame[CD_RAW_FRAME_SIZE]; int i, j, k; int iso_start_index; printf ("CD Detection Experiment\n"); if (argc < 2) { printf ("Usage: cdexp \n\n"); return 0; } /* open CD-ROM */ fd = open(argv[1], O_RDONLY | O_NONBLOCK); if (fd == -1) { perror(argv[1]); return 1; } read_cdrom_toc(fd, &toc); printf ("toc:\n first track = %d\n last track = %d\n\ntoc entries:\n", toc.first_track, toc.last_track); printf ("leadout track: MSF: %02d:%02d:%02d, first frame = %d\n", toc.leadout_track.first_frame_minute, toc.leadout_track.first_frame_second, toc.leadout_track.first_frame_frame, toc.leadout_track.first_frame); for (i = toc.first_track; i <= toc.last_track; i++) { printf ("track %2d, %s, MSF: %02d:%02d:%02d, first frame = %d\n", i, (toc.toc_entries[i-1].track_mode == 0) ? "audio" : " data", toc.toc_entries[i-1].first_frame_minute, toc.toc_entries[i-1].first_frame_second, toc.toc_entries[i-1].first_frame_frame, toc.toc_entries[i-1].first_frame); /* if this is a data track, get the 16th frame and look for data mode * and iso9660 signature ('CD001') */ if (toc.toc_entries[i-1].track_mode == 1) { read_cdrom_frame(fd, toc.toc_entries[i-1].first_frame+16, frame); /* for (j = 0; j < 4; j++) { for (k = 0; k < 16; k++) { printf (" %02X", frame[j * 16 + k]); } printf ("\n"); } */ if (frame[0x0F] == 1) { printf (" mode 1 data\n"); iso_start_index = 0x10; } else if (frame[0x0F]) { if (frame[0x12] & 0x20) { printf (" mode 2, form 2 data\n"); } else { printf (" mode 2, form 1 data\n"); } iso_start_index = 0x18; } if ((frame[iso_start_index + 1] == 'C') && (frame[iso_start_index + 2] == 'D') && (frame[iso_start_index + 3] == '0') && (frame[iso_start_index + 4] == '0') && (frame[iso_start_index + 5] == '1')) { printf (" iso9660 fs signature found\n"); printf (" system id = "); for (j = 8; j < 40; j++) { printf ("%c", frame[iso_start_index + j]); } printf ("\n"); printf (" volume id = "); for (j = 40; j < 72; j++) { printf ("%c", frame[iso_start_index + j]); } printf ("\n"); } } } /* close CD-ROM */ status = close(fd); if (status != 0) printf ("close() returned status %d\n", status); return 0; }