packages feed

MusicBrainz-libdiscid-0.5.0.0: libdiscid/src/disc_darwin.c

/* --------------------------------------------------------------------------

   MusicBrainz -- The Internet music metadatabase

   Copyright (C) 2006 Robert Kaye
   Copyright (C) 1999 Marc E E van Woerkom
   
   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.
   
   This library 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
   Lesser General Public License for more details.
   
   You should have received a copy of the GNU Lesser General Public
   License along with this library; if not, write to the Free Software
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

     $Id: mb_darwin.cpp,v 1.4 2005/10/28 22:05:27 robert Exp $

----------------------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <assert.h>
#include <paths.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/IOBSD.h>
#include <IOKit/storage/IOCDMedia.h>
#include <IOKit/storage/IOMedia.h>
#include <IOKit/storage/IOCDTypes.h>
#include <IOKit/storage/IOCDMediaBSDClient.h>
#include <IOKit/storage/IOMediaBSDClient.h>

#include <CoreFoundation/CoreFoundation.h>

#include "discid/discid.h"
#include "discid/discid_private.h"

#define TOC_BUFFER_LEN 2048
#define MAXPATHLEN     1024

static char defaultDevice[MAXPATHLEN] = "\0";

static kern_return_t find_ejectable_cd_media( io_iterator_t *mediaIterator )
{
    mach_port_t         masterPort;
    kern_return_t       kernResult;
    CFMutableDictionaryRef   classesToMatch;

    kernResult = IOMasterPort( MACH_PORT_NULL, &masterPort );
    if ( kernResult != KERN_SUCCESS )
        return kernResult;

    // CD media are instances of class kIOCDMediaClass.
    classesToMatch = IOServiceMatching( kIOCDMediaClass );
    if ( classesToMatch != NULL )
    {
        // Each IOMedia object has a property with key kIOMediaEjectableKey
        // which is true if the media is indeed ejectable. So add this
        // property to the CFDictionary for matching.
        CFDictionarySetValue( classesToMatch, CFSTR( kIOMediaEjectableKey ), kCFBooleanTrue );
    }

    return IOServiceGetMatchingServices( masterPort, classesToMatch, mediaIterator );
}

static kern_return_t get_device_file_path( io_iterator_t mediaIterator, char *deviceFilePath, CFIndex maxPathSize )
{
    io_object_t nextMedia;
    kern_return_t kernResult = KERN_FAILURE;
 
    *deviceFilePath = '\0';
    nextMedia = IOIteratorNext( mediaIterator );
    if ( nextMedia )
    {
        CFTypeRef   deviceFilePathAsCFString;
        deviceFilePathAsCFString = IORegistryEntryCreateCFProperty(
                                nextMedia, CFSTR( kIOBSDNameKey ),
                                kCFAllocatorDefault, 0 );

       *deviceFilePath = '\0';
        if ( deviceFilePathAsCFString )
        {
            size_t devPathLength;
            strcpy( deviceFilePath, _PATH_DEV );
            // Add "r" before the BSD node name from the I/O Registry
            // to specify the raw disk node. The raw disk node receives
            // I/O requests directly and does not go through the
            // buffer cache.
            strcat( deviceFilePath, "r");
            devPathLength = strlen( deviceFilePath );
            if ( CFStringGetCString( deviceFilePathAsCFString,
                                     deviceFilePath + devPathLength,
                                     maxPathSize - devPathLength,
                                     kCFStringEncodingASCII ) )
                kernResult = KERN_SUCCESS;

            CFRelease( deviceFilePathAsCFString );
        }
    }
    IOObjectRelease( nextMedia );

    return kernResult;
}

static void read_disc_mcn(int fd, mb_disc_private *disc)
{
    dk_cd_read_mcn_t cd_read_mcn;
    bzero(&cd_read_mcn, sizeof(cd_read_mcn));
	
    if(ioctl(fd, DKIOCCDREADMCN, &cd_read_mcn) == -1) {
        fprintf(stderr, "Warning: Unable to read the disc's media catalog number.\n");
    } else {
        strncpy( disc->mcn, cd_read_mcn.mcn, MCN_STR_LENGTH );
    }
}

static void read_disc_isrc(int fd, mb_disc_private *disc, int track)
{
    dk_cd_read_isrc_t	cd_read_isrc;
    bzero(&cd_read_isrc, sizeof(cd_read_isrc));
    cd_read_isrc.track = track;
	
    if(ioctl(fd, DKIOCCDREADISRC, &cd_read_isrc) == -1) {
        fprintf(stderr, "Warning: Unable to read the international standard recording code (ISRC) for track %i\n", track);
        return;
    } else {
        strncpy( disc->isrc[track], cd_read_isrc.isrc, ISRC_STR_LENGTH );
    }
}

int mb_disc_has_feature_unportable(enum discid_feature feature) {
	switch(feature) {
		case DISCID_FEATURE_READ:
		case DISCID_FEATURE_MCN:
		case DISCID_FEATURE_ISRC:
			return 1;
		default:
			return 0;
	}
}


char *mb_disc_get_default_device_unportable(void) 
{
    kern_return_t kernResult;
    io_iterator_t mediaIterator;

    *defaultDevice = 0;

    kernResult = find_ejectable_cd_media( &mediaIterator );
    if ( kernResult != KERN_SUCCESS )
        return "";

    kernResult = get_device_file_path( mediaIterator, defaultDevice, MAXPATHLEN - 1);
    if ( kernResult != KERN_SUCCESS )
        return "";

    return defaultDevice;
}

int mb_disc_read_unportable(mb_disc_private *disc, const char *device, unsigned int features) 
{
	int fd;
	int i;
	dk_cd_read_toc_t toc;
	CDTOC *cdToc;

	if (device == NULL || *device == 0) {
		device = mb_disc_get_default_device_unportable();
	}

	if (!*device) {
		snprintf(disc->error_msg, MB_ERROR_MSG_LENGTH,
			 "No CD-ROMs found. Please insert a disc and try again."
			);
		return 0;
	}
  
	fd = open(device, O_RDONLY | O_NONBLOCK);
	if (fd < 0) {
		snprintf(disc->error_msg, MB_ERROR_MSG_LENGTH,
			 "Cannot open '%s'", device);
		return 0;
	}
  
	memset(&toc, 0, sizeof(toc));
	toc.format = kCDTOCFormatTOC;
	toc.formatAsTime = 0;
	toc.buffer = (char *)malloc(TOC_BUFFER_LEN);
	toc.bufferLength = TOC_BUFFER_LEN;
	if (ioctl(fd, DKIOCCDREADTOC, &toc) < 0 ) {
		snprintf(disc->error_msg, MB_ERROR_MSG_LENGTH,
			 "Cannot read TOC from '%s'", device);
		free(toc.buffer);
		return 0;
	}
	if ( toc.bufferLength < sizeof(CDTOC) ) {
		snprintf(disc->error_msg, MB_ERROR_MSG_LENGTH,
			 "Short TOC was returned from '%s'", device);
		free(toc.buffer);
		return 0;
	}

	// Read in the media catalogue number
	if (features & DISCID_FEATURE_MCN) {
		read_disc_mcn(fd, disc);
	}

	cdToc = (CDTOC *)toc.buffer;
	int numDesc = CDTOCGetDescriptorCount(cdToc);

	int numTracks = 0;
	for(i = 0; i < numDesc; i++) {
		CDTOCDescriptor *desc = &cdToc->descriptors[i];

		if (desc->session > 1) {
			continue;
		}

		if (desc->point == 0xA2 && desc->adr == 1) {
			disc->track_offsets[0] = CDConvertMSFToLBA(desc->p)
						 + 150;
		}

		if (desc->point <= 99 && desc->adr == 1) {
			disc->track_offsets[1 + numTracks] = CDConvertMSFToLBA(
								desc->p) + 150;

			// Read in the IRSC codes for tracks
			if (features & DISCID_FEATURE_ISRC) {
				read_disc_isrc(fd, disc, 1 + numTracks);
			}

			numTracks++;
		}
	}
	disc->first_track_num = 1;
	disc->last_track_num = numTracks;


	close(fd);
	free(toc.buffer);
  
	return 1;
}