/* * small tool to read xa-files (form2 too, yea!) from CD's * * (c) 1996,1997 Gerd Knorr * * uses the CDROMREADMODE2 ioctl which allows to read * raw frames (2336 bytes) * * Works with IDE (2.0.29 is ok) and SCSI (requires >= 2.1.34) * * compile: 'make readxa' * usage: try 'readxa -h' * * Known Bugs / TODO-List * - It can't handle interleaved files correctly. * */ #include #include #include #include #include #include #include #include #include #include extern char *sys_errlist[]; static const char *device_list[] = { /* try most common first ... */ "/dev/cdrom", "/dev/scd0", "/dev/sr0", "/dev/hdc", "/dev/hdd", "/dev/scd1", "/dev/sr1", "/dev/scd2", "/dev/sr2", "/dev/scd3", "/dev/sr3", "/dev/hda", "/dev/hdb", "/dev/hde", "/dev/hdf", "/dev/hdg", "/dev/hdh", NULL }; int print_info = 0; int dump_raw = 0; void usage() { fprintf(stderr, "usage: readxa options\n" "readxa needs either a file or a device and track number\n" " -f file give filename\n" " -d dev give device name\n" " -t trk give track number\n" " -i prints some informations about the file and quit\n" " -r dumps the complete sector (raw, 2336 bytes) instead\n" " of the 2048 (form1) / 2324 (form2) data bytes\n"); } int read_raw_frame(int fd, int lba, unsigned char *buf) { struct cdrom_msf *msf; int rc; msf = (struct cdrom_msf*) buf; msf->cdmsf_min0 = (lba + CD_BLOCK_OFFSET) / CD_FRAMES / CD_SECS; msf->cdmsf_sec0 = (lba + CD_BLOCK_OFFSET) / CD_FRAMES % CD_SECS; msf->cdmsf_frame0 = (lba + CD_BLOCK_OFFSET) % CD_FRAMES; rc = ioctl(fd,CDROMREADMODE2,buf); if (-1 == rc) perror("ioctl CDROMREADMODE2"); return rc; } int main(int argc, char *argv[]) { int file, cdrom, rc, c, size; unsigned long block; unsigned char *buf; struct stat st, dev_st; int device; struct cdrom_tocentry toc,toc2; char *filename = NULL, *devname = NULL; int tracknr = 0; /* parse options */ for (;;) { c = getopt(argc, argv, "ihrf:d:t:"); if (c == -1) break; switch (c) { case 'i': print_info = 1; break; case 'r': dump_raw = 1; break; case 'f': filename = optarg; break; case 'd': devname = optarg; break; case 't': tracknr = atoi(optarg); break; case 'h': default: usage(); exit(1); } } if (filename) { /* have a file -- get start sector */ if (-1 == (file = open(filename,O_RDONLY))) { fprintf(stderr,"open %s: %s\n",filename,sys_errlist[errno]); exit(1); } fstat(file,&st); size = st.st_size; block = 0; if (-1 == ioctl(file,FIBMAP,&block)) { perror("ioctl FIBMAP"); exit(1); } if (print_info) { fprintf(stderr,"first sector: %lu, size: %d\n",block,size); } close(file); /* get block device */ for (device = 0; device_list[device] != NULL; device++) { if (-1 == stat(device_list[device],&dev_st)) continue; if (!S_ISBLK(dev_st.st_mode)) continue; if (dev_st.st_rdev == st.st_dev) break; } if (device_list[device] == NULL) { fprintf(stderr,"can't find special file for %02x:%02x\n", MAJOR(st.st_dev),MINOR(st.st_dev)); exit(1); } if (print_info) { fprintf(stderr,"block device: %s\n",device_list[device]); } } else if (devname && tracknr) { /* have a device + track -- read toc */ if (-1 == (file = open(devname,O_RDONLY))) { fprintf(stderr,"open %s: %s\n",devname,sys_errlist[errno]); exit(1); } toc.cdte_track = tracknr; toc.cdte_format = CDROM_LBA; if (-1 == ioctl(file,CDROMREADTOCENTRY,&toc)) { perror("ioctl CDROMREADTOCENTRY"); exit(1); } toc2.cdte_track = tracknr+1; toc2.cdte_format = CDROM_LBA; if (-1 == ioctl(file,CDROMREADTOCENTRY,&toc2)) { toc2.cdte_track = CDROM_LEADOUT; toc2.cdte_format = CDROM_LBA; if (-1 == ioctl(file,CDROMREADTOCENTRY,&toc2)) { perror("ioctl CDROMREADTOCENTRY"); exit(1); } } block = toc.cdte_addr.lba; size = (toc2.cdte_addr.lba - block) * 2048; if (print_info) { fprintf(stderr,"first sector of track %d: %lu\n",tracknr,block); fprintf(stderr,"first sector of %s: %d, size <= %d\n", (toc2.cdte_track != CDROM_LEADOUT) ? "next track" : "leadout", toc2.cdte_addr.lba,size); } close(file); } else { usage(); exit(1); } /* read sector (raw) */ if (-1 == (cdrom = open(devname ? devname : device_list[device],O_RDONLY))) { fprintf(stderr,"open %s: %s\n",devname ? devname : device_list[device],sys_errlist[errno]); exit(1); } if (NULL == (buf = malloc(CD_FRAMESIZE_RAW0))) { fprintf(stderr,"Out of memory\n"); exit(1); } if (0 != (rc = read_raw_frame(cdrom,block,buf))) { perror("read error"); exit(1); } if (print_info) { if (buf[0] == buf[4] && buf[1] == buf[5] && buf[2] == buf[6] && buf[3] == buf[7] && buf[2] != 0 && buf[0] < 8 && buf[1] < 8) { fprintf(stderr,"XA sectors - "); fprintf(stderr,"form %s - ",buf[2] & 0x20 ? "2" : "1"); if (buf[2] & 0x08) fprintf(stderr,"data\n"); if (buf[2] & 0x04) fprintf(stderr,"audio\n"); if (buf[2] & 0x02) fprintf(stderr,"video\n"); if (buf[0]) fprintf(stderr, "fileno %d\n", buf[4]); if (buf[1]) fprintf(stderr, "channel %d\n", buf[5]); } else { fprintf(stderr,"normal sectors\n"); } } else { for (;;) { int offset, len; if (buf[0] == buf[4] && buf[1] == buf[5] && buf[2] == buf[6] && buf[3] == buf[7] && buf[2] != 0 && buf[0] < 8 && buf[1] < 8) { offset = 8; len = (buf[2] & 0x20) ? 2324 : 2048; } else { offset = 0; len = 2048; } if (dump_raw) write(1,buf,CD_FRAMESIZE_RAW0); else write(1,buf+offset, (size < len) ? size : len); size -= 2048 /* len */; block++; if (size <= 0) break; if (0 != (rc = read_raw_frame(cdrom,block,buf))) { perror("read error"); exit(1); } } } close(cdrom); free(buf); exit(0); }