#include <stdio.h>
#include <sys/mount.h>
#include <sys/param.h>
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/storage/IOCDTypes.h>
#include <IOKit/storage/IOCDMedia.h>
#define DEBUG 0
#define DEBUG_ASSERT_COMPONENT_NAME_STRING "CDTOC"
#if DEBUG
#define DEBUG_ASSERT_MESSAGE(componentNameString, \
assertionString, \
exceptionLabelString, \
errorString, \
fileName, \
lineNumber, \
errorCode) \
DebugAssert(componentNameString, \
assertionString, \
exceptionLabelString, \
errorString, \
fileName, \
lineNumber, \
errorCode) \
static void
DebugAssert ( const char * componentNameString,
const char * assertionString,
const char * exceptionLabelString,
const char * errorString,
const char * fileName,
long lineNumber,
int errorCode )
{
if ( ( assertionString != NULL ) && ( *assertionString != '\0' ) )
printf ( "Assertion failed: %s: %s\n", componentNameString, assertionString );
else
printf ( "Check failed: %s:\n", componentNameString );
if ( exceptionLabelString != NULL )
printf ( " %s\n", exceptionLabelString );
if ( errorString != NULL )
printf ( " %s\n", errorString );
if ( fileName != NULL )
printf ( " file: %s\n", fileName );
if ( lineNumber != 0 )
printf ( " line: %ld\n", lineNumber );
if ( errorCode != 0 )
printf ( " error: %d\n", errorCode );
}
#endif
#include <AssertMacros.h>
enum
{
kDigitalDataBit = 2,
kDigitalDataMask = ( 1 << kDigitalDataBit )
};
#define kAudioTrackString "Track is Audio"
#define kDataTrackString "Track is Data"
enum
{
kUnknown = -1,
kAllDevices = 0,
kSpecificDevice = 1
};
struct SubQTOCInfo
{
UInt8 sessionNumber;
#ifdef __LITTLE_ENDIAN__
UInt8 control:4;
UInt8 address:4;
#else
UInt8 address:4;
UInt8 control:4;
#endif
UInt8 tno;
UInt8 point;
UInt8 ATIP[3];
UInt8 zero;
union {
struct {
UInt8 minutes;
UInt8 seconds;
UInt8 frames;
} startPosition;
struct {
UInt8 firstTrackNum;
UInt8 discType;
UInt8 reserved;
} A0PMSF;
struct {
UInt8 lastTrackNum;
UInt8 reserved[2];
} A1PMSF;
struct {
UInt8 minutes;
UInt8 seconds;
UInt8 frames;
} leadOutStartPosition;
struct {
UInt8 skipIntervalPointers;
UInt8 skipTrackPointers;
UInt8 reserved;
} B1;
struct {
UInt8 skipNumber[3];
} B2toB4;
struct {
UInt8 minutes;
UInt8 seconds;
UInt8 frames;
} hybridDiscLeadInArea;
} PMSF;
};
typedef struct SubQTOCInfo SubQTOCInfo;
static IOReturn
ValidateArguments ( int argc, const char * argv[], int * selector );
static void
PrintUsage ( void );
static UInt8 *
CreateBufferFromCFData ( CFDataRef theData );
static IOReturn
PrintTOCForAllCDMedia ( void );
static IOReturn
PrintTOCForDisc ( io_object_t cdMedia );
static IOReturn
PrintTOCForBSDNode ( const char * bsdNode );
static void
PrintTOCData ( CDTOC * TOCInfo, OptionBits options );
int
main ( int argc, const char * argv[] )
{
int result = 0;
int selector = 0;
result = ValidateArguments ( argc, argv, &selector );
require_noerr_action ( result, ErrorExit, PrintUsage ( ) );
switch ( selector )
{
case kAllDevices:
result = PrintTOCForAllCDMedia ( );
require_action ( ( result == kIOReturnSuccess ), ErrorExit, result = -1 );
break;
case kSpecificDevice:
result = PrintTOCForBSDNode ( argv[argc - 1] );
require_action ( ( result == kIOReturnSuccess ), ErrorExit, result = -1 );
break;
default:
break;
}
ErrorExit:
return result;
}
static IOReturn
ValidateArguments ( int argc, const char * argv[], int * selector )
{
IOReturn result = kIOReturnError;
require ( ( argc > 1 ), ErrorExit );
require ( ( argc < 4 ), ErrorExit );
if ( argc == 2 )
{
require ( ( strcmp ( argv[1], "-a" ) == 0 ), ErrorExit );
*selector = kAllDevices;
}
else if ( argc == 3 )
{
require ( ( strcmp ( argv[1], "-d" ) == 0 ), ErrorExit );
*selector = kSpecificDevice;
}
result = kIOReturnSuccess;
ErrorExit:
return result;
}
static IOReturn
PrintTOCForAllCDMedia ( void )
{
IOReturn error = kIOReturnSuccess;
io_iterator_t iter = MACH_PORT_NULL;
io_object_t cdMedia = MACH_PORT_NULL;
error = IOServiceGetMatchingServices ( kIOMasterPortDefault,
IOServiceMatching ( kIOCDMediaClass ),
&iter );
require_string ( ( error == kIOReturnSuccess ), ErrorExit, "No CD media found\n" );
cdMedia = IOIteratorNext ( iter );
if ( cdMedia == MACH_PORT_NULL )
{
printf ( "No CD media found\n" );
}
while ( cdMedia != MACH_PORT_NULL )
{
error = PrintTOCForDisc ( cdMedia );
IOObjectRelease ( cdMedia );
cdMedia = IOIteratorNext ( iter );
}
IOObjectRelease ( iter );
iter = NULL;
ErrorExit:
return error;
}
static IOReturn
PrintTOCForDisc ( io_object_t cdMedia )
{
IOReturn error = kIOReturnSuccess;
CFMutableDictionaryRef properties = 0;
UInt8 * TOCInfo = NULL;
CFDataRef data = NULL;
error = IORegistryEntryCreateCFProperties ( cdMedia,
&properties,
kCFAllocatorDefault,
kNilOptions );
require ( ( error == kIOReturnSuccess ), ErrorExit );
data = ( CFDataRef ) CFDictionaryGetValue ( properties,
CFSTR ( kIOCDMediaTOCKey ) );
require ( ( data != NULL ), ReleaseProperties );
TOCInfo = CreateBufferFromCFData ( data );
require ( ( TOCInfo != NULL ), ReleaseProperties );
PrintTOCData ( ( CDTOC * ) TOCInfo, 0 );
free ( ( char * ) TOCInfo );
ReleaseProperties:
require_quiet ( ( properties != NULL ), ErrorExit );
CFRelease ( properties );
properties = NULL;
ErrorExit:
return error;
}
static IOReturn
PrintTOCForBSDNode ( const char * bsdNode )
{
IOReturn error = kIOReturnError;
io_object_t object = MACH_PORT_NULL;
char * bsdName = NULL;
char deviceName[MAXPATHLEN];
sprintf ( deviceName, "%s", bsdNode );
if ( !strncmp ( deviceName, "/dev/r", 6 ) )
{
bsdName = &deviceName[6];
}
else if ( !strncmp ( deviceName, "/dev/", 5 ) )
{
bsdName = &deviceName[5];
}
else
{
bsdName = deviceName;
}
require_action ( ( strncmp ( bsdName, "disk", 4 ) == 0 ), ErrorExit, PrintUsage ( ) );
object = IOServiceGetMatchingService ( kIOMasterPortDefault,
IOBSDNameMatching ( kIOMasterPortDefault, 0, bsdName ) );
if ( object == MACH_PORT_NULL )
{
printf ( "No device node found at %s\n", bsdNode );
}
require ( ( object != MACH_PORT_NULL ), ErrorExit );
if ( IOObjectConformsTo ( object, kIOCDMediaClass ) == false )
{
printf ( "Device node found at %s is not CD media\n", bsdNode );
}
require ( IOObjectConformsTo ( object, kIOCDMediaClass ), ReleaseObject );
PrintTOCForDisc ( object );
ReleaseObject:
IOObjectRelease ( object );
object = NULL;
ErrorExit:
return error;
}
UInt8 *
CreateBufferFromCFData ( CFDataRef theData )
{
CFRange range;
CFIndex bufferLength = CFDataGetLength ( theData ) + 1;
UInt8 * buffer = ( UInt8 * ) malloc ( bufferLength );
range = CFRangeMake ( 0, bufferLength );
if ( buffer != NULL )
CFDataGetBytes ( theData, range, buffer );
return buffer;
}
void
PrintTOCData ( CDTOC * TOCInfo, OptionBits options )
{
SubQTOCInfo * trackDescriptorPtr = NULL;
UInt16 numberOfDescriptors = 0;
if ( TOCInfo != NULL )
{
numberOfDescriptors = CDTOCGetDescriptorCount ( TOCInfo );
printf ( "//---------------------------------------\n" );
printf ( "//CDTOC info\n" );
printf ( "//---------------------------------------\n\n" );
printf ( "Number of descriptors\t= %d\n", numberOfDescriptors );
if ( numberOfDescriptors <= 0 )
{
printf ( "No tracks on this CD...\n" );
}
printf ( "length\t\t\t= %d\n", OSSwapBigToHostInt16 ( TOCInfo->length ) );
printf ( "sessionFirst\t\t= %d\n", TOCInfo->sessionFirst );
printf ( "sessionLast\t\t= %d\n\n", TOCInfo->sessionLast );
trackDescriptorPtr = ( SubQTOCInfo * ) TOCInfo->descriptors;
while ( numberOfDescriptors > 0 )
{
printf ( "//---------------------------------------\n" );
if ( trackDescriptorPtr->point < 100 )
{
printf ( "// SubQTOCInfo - Track %d\n", trackDescriptorPtr->point );
}
else
{
printf ( "// SubQTOCInfo - Track %x\n", trackDescriptorPtr->point );
}
printf ( "//---------------------------------------\n\n" );
printf ( "sessionNumber\t\t= %d\n", trackDescriptorPtr->sessionNumber );
printf ( "address\t\t\t= %d\n", trackDescriptorPtr->address );
printf ( "control\t\t\t= %d\t", trackDescriptorPtr->control );
printf ( "%s\n", ( trackDescriptorPtr->control & kDigitalDataMask || ( trackDescriptorPtr->point < 1 || trackDescriptorPtr->point > 99 ) ) ? kDataTrackString : kAudioTrackString );
printf ( "tno\t\t\t= %d\n", trackDescriptorPtr->tno );
printf ( "ATIP[0]\t\t\t= %d\n", trackDescriptorPtr->ATIP[0] );
printf ( "ATIP[1]\t\t\t= %d\n", trackDescriptorPtr->ATIP[1] );
printf ( "ATIP[2]\t\t\t= %d\n", trackDescriptorPtr->ATIP[2] );
printf ( "zero\t\t\t= %d\n", trackDescriptorPtr->zero );
switch ( trackDescriptorPtr->point )
{
case 0xA0:
printf ( "firstTrackNum\t\t= %d\n", trackDescriptorPtr->PMSF.A0PMSF.firstTrackNum );
printf ( "discType\t\t= %d\t", trackDescriptorPtr->PMSF.A0PMSF.discType );
switch ( trackDescriptorPtr->PMSF.A0PMSF.discType )
{
case 0:
printf ( "(CD-DA or CD-ROM with first track in Mode 1)\n" );
break;
case 16:
printf ( "(CD-I disc)\n" );
break;
case 32:
printf ( "(CD-ROM XA disc with first track in Mode 2)\n" );
break;
}
printf ( "reserved\t\t= %d\n", trackDescriptorPtr->PMSF.A0PMSF.reserved );
break;
case 0xA1:
printf ( "lastTrackNum\t\t= %d\n", trackDescriptorPtr->PMSF.A1PMSF.lastTrackNum );
printf ( "reserved[0]\t\t= %d\n", trackDescriptorPtr->PMSF.A1PMSF.reserved[0] );
printf ( "reserved[1]\t\t= %d\n", trackDescriptorPtr->PMSF.A1PMSF.reserved[1] );
break;
case 0xA2:
printf ( "\nLead-Out Area\n" );
printf ( "minutes\t= %d\n", trackDescriptorPtr->PMSF.leadOutStartPosition.minutes );
printf ( "seconds\t= %d\n", trackDescriptorPtr->PMSF.leadOutStartPosition.seconds );
printf ( "frames\t= %d\n", trackDescriptorPtr->PMSF.leadOutStartPosition.frames );
break;
case 0xB0:
printf ( "minutes\t= %d\n", trackDescriptorPtr->PMSF.leadOutStartPosition.minutes );
printf ( "seconds\t= %d\n", trackDescriptorPtr->PMSF.leadOutStartPosition.seconds );
printf ( "frames\t= %d\n", trackDescriptorPtr->PMSF.leadOutStartPosition.frames );
break;
case 0xB1:
printf ( "skipIntervalPointers = %d\n", trackDescriptorPtr->PMSF.B1.skipIntervalPointers );
printf ( "skipTrackPointers = %d\n", trackDescriptorPtr->PMSF.B1.skipTrackPointers );
printf ( "reserved = %d\n", trackDescriptorPtr->PMSF.B1.reserved );
break;
case 0xB2:
case 0xB3:
case 0xB4:
printf ( "skipnumber[0] = %d\n", trackDescriptorPtr->PMSF.B2toB4.skipNumber[0] );
printf ( "skipnumber[1] = %d\n", trackDescriptorPtr->PMSF.B2toB4.skipNumber[1] );
printf ( "skipnumber[2] = %d\n", trackDescriptorPtr->PMSF.B2toB4.skipNumber[2] );
break;
case 0xC0:
printf ( "minutes = %d\n", trackDescriptorPtr->PMSF.hybridDiscLeadInArea.minutes );
printf ( "seconds = %d\n", trackDescriptorPtr->PMSF.hybridDiscLeadInArea.seconds );
printf ( "frames = %d\n", trackDescriptorPtr->PMSF.hybridDiscLeadInArea.frames );
break;
default:
if ( trackDescriptorPtr->address == 1 )
{
printf ( "minutes\t\t\t= %d\n", trackDescriptorPtr->PMSF.startPosition.minutes );
printf ( "seconds\t\t\t= %d\n", trackDescriptorPtr->PMSF.startPosition.seconds );
printf ( "frames\t\t\t= %d\n", trackDescriptorPtr->PMSF.startPosition.frames );
}
else if ( trackDescriptorPtr->address == 5 )
{
printf ( "This is a weird address number, must be a skip interval..." );
printf ( "minutes = %d\n", trackDescriptorPtr->PMSF.startPosition.minutes );
printf ( "seconds = %d\n", trackDescriptorPtr->PMSF.startPosition.seconds );
printf ( "frames = %d\n", trackDescriptorPtr->PMSF.startPosition.frames );
}
break;
}
trackDescriptorPtr++;
numberOfDescriptors--;
printf ( "\n" );
}
}
}
void
PrintUsage ( void )
{
printf ( "\n" );
printf ( "Usage: CDTOC [-a] [-d label]\n" );
printf ( "\t\t" );
printf ( "-a Print the TOC Data from all available CDs\n" );
printf ( "\t\t" );
printf ( "-d Enter a specific bsd-style disk path to use" );
printf ( "\t\t" );
printf ( "e.g. /dev/disk0, /dev/rdisk2, disk3" );
printf ( "\n" );
}