/* * Copyright (c) 1999-2003 Robert Woodcock * Copyright (c) 2009 Timur Birsh * This code is hereby licensed for public consumption under either the * GNU GPL v2 or greater, or Larry Wall's Artistic license - your choice. * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "discid.h" #include #include #include #include static int cddb_sum(int n) { /* a number like 2344 becomes 2+3+4+4 (13) */ int ret=0; while (n > 0) { ret = ret + (n % 10); n = n / 10; } return ret; } #define DISCID_STRING_BUFFER_SIZE 2048 #define MISC_STRING_BUFFER_SIZE 512 static char discid_string[DISCID_STRING_BUFFER_SIZE]; static char discid_error_message[MISC_STRING_BUFFER_SIZE]; static bool discid_string_new = true; static char *command = "libcd-utils.a"; static bool verbose = false; static void set_error_message(const char *command, const char *devicename, const char * perror_tag) { if (verbose) { fprintf(stderr, "%s: %s: ", command, devicename); perror(perror_tag); } snprintf(discid_error_message, MISC_STRING_BUFFER_SIZE - 1, "%s %s: %s", command, devicename, strerror(errno)); } char *cdrom_get_error_message() { return discid_error_message; } static void append_discid_string(const char *str) { if ((str == NULL) || (*str == '\0')) { return; } // int str_len = strlen(str); if (discid_string_new) { strcpy(discid_string, str); discid_string_new = false; } else { strcat(discid_string, str); } } static void append_discid_formatted(const char * format, ...) { if ((format == NULL) || (*format == '\0')) { return; } char str[MISC_STRING_BUFFER_SIZE]; va_list argptr; va_start(argptr, format); vsprintf(str, format, argptr); va_end(argptr); append_discid_string(str); } char *cdrom_get_discid(const char *devicename, int musicbrainz) { int len; int drive, i, totaltime; long int cksum=0; struct cdrom_tochdr hdr; struct cdrom_tocentry *TocEntry; #if defined(__OpenBSD__) || defined(__NetBSD__) struct ioc_read_toc_entry t; #elif defined(__APPLE__) dk_cd_read_disc_info_t discInfoParams; #endif strcpy(discid_error_message, ""); discid_error_message[MISC_STRING_BUFFER_SIZE - 1] = '\0'; drive = open(devicename, O_RDONLY | O_NONBLOCK); if (drive < 0) { fprintf(stderr, "%s: %s: ", command, devicename); set_error_message(command, devicename, "open"); return NULL; } #if defined(__APPLE__) memset(&discInfoParams, 0, sizeof(discInfoParams)); discInfoParams.buffer = &hdr; discInfoParams.bufferLength = sizeof(hdr); if (ioctl(drive, DKIOCCDREADDISCINFO, &discInfoParams) < 0 || discInfoParams.bufferLength != sizeof(hdr)) { set_error_message(command, devicename, "DKIOCCDREADDISCINFO"); return NULL; } #else if (ioctl(drive, CDROMREADTOCHDR, &hdr) < 0) { set_error_message(command, devicename, "CDROMREADTOCHDR"); return NULL; } #endif // unsigned char first = hdr.cdth_trk0; unsigned char last = hdr.cdth_trk1; len = (last + 1) * sizeof(struct cdrom_tocentry); TocEntry = malloc(len); if (!TocEntry) { set_error_message(command, devicename, "Can't allocate memory for TOC entries"); return NULL; } #if defined(__OpenBSD__) t.starting_track = 0; #elif defined(__NetBSD__) t.starting_track = 1; #endif #if defined(__OpenBSD__) || defined(__NetBSD__) t.address_format = CDROM_LBA; t.data_len = len; t.data = TocEntry; memset(TocEntry, 0, len); if (ioctl(drive, CDIOREADTOCENTRYS, (char *) &t) < 0) { set_error_message(command, devicename, "CDIOREADTOCENTRYS"); } #elif defined(__APPLE__) dk_cd_read_track_info_t trackInfoParams; memset( &trackInfoParams, 0, sizeof( trackInfoParams ) ); trackInfoParams.addressType = kCDTrackInfoAddressTypeTrackNumber; trackInfoParams.bufferLength = sizeof( *TocEntry ); for (i = 0; i < last; i++) { trackInfoParams.address = i + 1; trackInfoParams.buffer = &TocEntry[i]; if (ioctl(drive, DKIOCCDREADTRACKINFO, &trackInfoParams) < 0) { set_error_message(command, devicename, "DKIOCCDREADTRACKINFO"); } } /* MacOS X on G5-based systems does not report valid info for * TocEntry[last-1].lastRecordedAddress + 1, so we compute the start * of leadout from the start+length of the last track instead */ TocEntry[last].cdte_track_address = TocEntry[last-1].trackSize + TocEntry[last-1].trackStartAddress; #else /* FreeBSD, Linux, Solaris */ for (i=0; i < last; i++) { /* tracks start with 1, but I must start with 0 on OpenBSD */ TocEntry[i].cdte_track = i + 1; TocEntry[i].cdte_format = CDROM_LBA; if (ioctl(drive, CDROMREADTOCENTRY, &TocEntry[i]) < 0) { set_error_message(command, devicename, "CDROMREADTOCENTRY"); return NULL; } } TocEntry[last].cdte_track = CDROM_LEADOUT; TocEntry[last].cdte_format = CDROM_LBA; if (ioctl(drive, CDROMREADTOCENTRY, &TocEntry[i]) < 0) { set_error_message(command, devicename, "CDROMREADTOCENTRY"); return NULL; } #endif #if defined(__FreeBSD__) TocEntry[i].cdte_track_address = ntohl(TocEntry[i].cdte_track_address); #endif for (i=0; i < last; i++) { #if defined(__FreeBSD__) TocEntry[i].cdte_track_address = ntohl(TocEntry[i].cdte_track_address); #endif cksum += cddb_sum((TocEntry[i].cdte_track_address + CD_MSF_OFFSET) / CD_FRAMES); } totaltime = ((TocEntry[last].cdte_track_address + CD_MSF_OFFSET) / CD_FRAMES) - ((TocEntry[0].cdte_track_address + CD_MSF_OFFSET) / CD_FRAMES); /* print discid */ if (! musicbrainz) { append_discid_formatted("%08lx ", (cksum % 0xff) << 24 | totaltime << 8 | last); } /* print number of tracks */ append_discid_formatted("%d", last); /* print frame offsets of all tracks */ for (i = 0; i < last; i++) { append_discid_formatted(" %d", TocEntry[i].cdte_track_address + CD_MSF_OFFSET); } if (musicbrainz) { append_discid_formatted(" %d", TocEntry[last].cdte_track_address + CD_MSF_OFFSET); } else { /* print length of disc in seconds */ append_discid_formatted(" %d", (TocEntry[last].cdte_track_address + CD_MSF_OFFSET) / CD_FRAMES); } free(TocEntry); close(drive); return discid_string; } #ifdef STANDALONE int main(int argc, char *argv[]) { int musicbrainz=0; char *devicename=DEVICE_NAME; command=argv[0]; if (argc >= 2 && ! strcmp(argv[1], "--musicbrainz")) { musicbrainz = 1; argc--; argv++; } if (argc == 2) { devicename = argv[1]; } else if (argc > 2) { fprintf(stderr, "Usage: %s [--musicbrainz] [devicename]\n", command); exit(1); } char *discid = cdrom_get_discid(devicename, musicbrainz); printf("%s\n", discid); return 0; } #endif