Home | History | Annotate | Download | only in macos
      1 /*
      2     SDL - Simple DirectMedia Layer
      3     Copyright (C) 1997-2012 Sam Lantinga
      4 
      5     This library is free software; you can redistribute it and/or
      6     modify it under the terms of the GNU Lesser General Public
      7     License as published by the Free Software Foundation; either
      8     version 2.1 of the License, or (at your option) any later version.
      9 
     10     This library is distributed in the hope that it will be useful,
     11     but WITHOUT ANY WARRANTY; without even the implied warranty of
     12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13     Lesser General Public License for more details.
     14 
     15     You should have received a copy of the GNU Lesser General Public
     16     License along with this library; if not, write to the Free Software
     17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     18 
     19     Sam Lantinga
     20     slouken (at) libsdl.org
     21 */
     22 #include "SDL_config.h"
     23 
     24 #ifdef SDL_CDROM_MACOS
     25 
     26 /* MacOS functions for system-level CD-ROM audio control */
     27 
     28 #include <Devices.h>
     29 #include <Files.h>
     30 #include <LowMem.h> /* Use entry table macros, not functions in InterfaceLib  */
     31 
     32 #include "SDL_cdrom.h"
     33 #include "../SDL_syscdrom.h"
     34 #include "SDL_syscdrom_c.h"
     35 
     36 /* Added by Matt Slot */
     37 #if !defined(LMGetUnitTableEntryCount)
     38   #define LMGetUnitTableEntryCount()   *(short *)0x01D2
     39 #endif
     40 
     41 /* The maximum number of CD-ROM drives we'll detect */
     42 #define MAX_DRIVES	26
     43 
     44 /* A list of available CD-ROM drives */
     45 static long SDL_cdversion = 0;
     46 static struct {
     47 	short		dRefNum;
     48 	short		driveNum;
     49 	long		frames;
     50 	char		name[256];
     51 	Boolean		hasAudio;
     52 	} SDL_cdlist[MAX_DRIVES];
     53 static StringPtr gDriverName = "\p.AppleCD";
     54 
     55 /* The system-dependent CD control functions */
     56 static const char *SDL_SYS_CDName(int drive);
     57 static int SDL_SYS_CDOpen(int drive);
     58 static int SDL_SYS_CDGetTOC(SDL_CD *cdrom);
     59 static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position);
     60 static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length);
     61 static int SDL_SYS_CDPause(SDL_CD *cdrom);
     62 static int SDL_SYS_CDResume(SDL_CD *cdrom);
     63 static int SDL_SYS_CDStop(SDL_CD *cdrom);
     64 static int SDL_SYS_CDEject(SDL_CD *cdrom);
     65 static void SDL_SYS_CDClose(SDL_CD *cdrom);
     66 
     67 static short SDL_SYS_ShortToBCD(short value)
     68 {
     69 	return((value % 10) + (value / 10) * 0x10); /* Convert value to BCD */
     70 }
     71 
     72 static short SDL_SYS_BCDToShort(short value)
     73 {
     74 	return((value % 0x10) + (value / 0x10) * 10); /* Convert value from BCD */
     75 }
     76 
     77 int  SDL_SYS_CDInit(void)
     78 {
     79 	SInt16			dRefNum = 0;
     80 	SInt16			first, last;
     81 
     82 	SDL_numcds = 0;
     83 
     84 	/* Check that the software is available */
     85 	if (Gestalt(kGestaltAudioCDSelector, &SDL_cdversion) ||
     86 			!SDL_cdversion) return(0);
     87 
     88 	/* Fill in our driver capabilities */
     89 	SDL_CDcaps.Name = SDL_SYS_CDName;
     90 	SDL_CDcaps.Open = SDL_SYS_CDOpen;
     91 	SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC;
     92 	SDL_CDcaps.Status = SDL_SYS_CDStatus;
     93 	SDL_CDcaps.Play = SDL_SYS_CDPlay;
     94 	SDL_CDcaps.Pause = SDL_SYS_CDPause;
     95 	SDL_CDcaps.Resume = SDL_SYS_CDResume;
     96 	SDL_CDcaps.Stop = SDL_SYS_CDStop;
     97 	SDL_CDcaps.Eject = SDL_SYS_CDEject;
     98 	SDL_CDcaps.Close = SDL_SYS_CDClose;
     99 
    100 	/* Walk the list, count each AudioCD driver, and save the refnums */
    101 	first = -1;
    102 	last = 0 - LMGetUnitTableEntryCount();
    103 	for(dRefNum = first; dRefNum >= last; dRefNum--) {
    104 		Str255		driverName;
    105 		StringPtr	namePtr;
    106 		DCtlHandle	deviceEntry;
    107 
    108 		deviceEntry = GetDCtlEntry(dRefNum);
    109 		if (! deviceEntry) continue;
    110 
    111 		/* Is this an .AppleCD ? */
    112 		namePtr = (*deviceEntry)->dCtlFlags & (1L << dRAMBased) ?
    113 				((StringPtr) ((DCtlPtr) deviceEntry)->dCtlDriver + 18) :
    114 				((StringPtr) (*deviceEntry)->dCtlDriver + 18);
    115 		BlockMoveData(namePtr, driverName, namePtr[0]+1);
    116 		if (driverName[0] > gDriverName[0]) driverName[0] = gDriverName[0];
    117 		if (! EqualString(driverName, gDriverName, false, false)) continue;
    118 
    119 		/* Record the basic info for each drive */
    120 		SDL_cdlist[SDL_numcds].dRefNum = dRefNum;
    121 		BlockMoveData(namePtr + 1, SDL_cdlist[SDL_numcds].name, namePtr[0]);
    122 		SDL_cdlist[SDL_numcds].name[namePtr[0]] = 0;
    123 		SDL_cdlist[SDL_numcds].hasAudio = false;
    124 		SDL_numcds++;
    125 	}
    126 	return(0);
    127 }
    128 
    129 static const char *SDL_SYS_CDName(int drive)
    130 {
    131 	return(SDL_cdlist[drive].name);
    132 }
    133 
    134 static int get_drivenum(int drive)
    135 {
    136 	QHdr *driveQ = GetDrvQHdr();
    137 	DrvQEl *driveElem;
    138 
    139 	/* Update the drive number */
    140 	SDL_cdlist[drive].driveNum = 0;
    141 	if ( driveQ->qTail ) {
    142 		driveQ->qTail->qLink = 0;
    143 	}
    144 	for ( driveElem=(DrvQEl *)driveQ->qHead; driveElem;
    145 	      driveElem = (DrvQEl *)driveElem->qLink ) {
    146 		if ( driveElem->dQRefNum == SDL_cdlist[drive].dRefNum ) {
    147 			SDL_cdlist[drive].driveNum = driveElem->dQDrive;
    148 			break;
    149 		}
    150 	}
    151 	return(SDL_cdlist[drive].driveNum);
    152 }
    153 
    154 static int SDL_SYS_CDOpen(int drive)
    155 {
    156 	return(drive);
    157 }
    158 
    159 static int SDL_SYS_CDGetTOC(SDL_CD *cdrom)
    160 {
    161 	CDCntrlParam		cdpb;
    162 	CDTrackData			tracks[SDL_MAX_TRACKS];
    163 	long				i, leadout;
    164 
    165 	/* Get the number of tracks on the CD by examining the TOC */
    166 	SDL_memset(&cdpb, 0, sizeof(cdpb));
    167 	cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
    168 	cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
    169 	cdpb.csCode = kReadTOC;
    170 	cdpb.csParam.words[0] = kGetTrackRange;
    171 	if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) {
    172 		SDL_SetError("PBControlSync() failed");
    173 		return(-1);
    174 	}
    175 
    176 	cdrom->numtracks =
    177 			SDL_SYS_BCDToShort(cdpb.csParam.bytes[1]) -
    178 			SDL_SYS_BCDToShort(cdpb.csParam.bytes[0]) + 1;
    179 	if ( cdrom->numtracks > SDL_MAX_TRACKS )
    180 		cdrom->numtracks = SDL_MAX_TRACKS;
    181 	cdrom->status = CD_STOPPED;
    182 	cdrom->cur_track = 0; /* Apparently these are set elsewhere */
    183 	cdrom->cur_frame = 0; /* Apparently these are set elsewhere */
    184 
    185 
    186 	/* Get the lead out area of the CD by examining the TOC */
    187 	SDL_memset(&cdpb, 0, sizeof(cdpb));
    188 	cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
    189 	cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
    190 	cdpb.csCode = kReadTOC;
    191 	cdpb.csParam.words[0] = kGetLeadOutArea;
    192 	if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) {
    193 		SDL_SetError("PBControlSync() failed");
    194 		return(-1);
    195 	}
    196 
    197 	leadout = MSF_TO_FRAMES(
    198 			SDL_SYS_BCDToShort(cdpb.csParam.bytes[0]),
    199 			SDL_SYS_BCDToShort(cdpb.csParam.bytes[1]),
    200 			SDL_SYS_BCDToShort(cdpb.csParam.bytes[2]));
    201 
    202 	/* Get an array of track locations by examining the TOC */
    203 	SDL_memset(tracks, 0, sizeof(tracks));
    204 	SDL_memset(&cdpb, 0, sizeof(cdpb));
    205 	cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
    206 	cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
    207 	cdpb.csCode = kReadTOC;
    208 	cdpb.csParam.words[0] = kGetTrackEntries;	/* Type of Query */
    209 	* ((long *) (cdpb.csParam.words+1)) = (long) tracks;
    210 	cdpb.csParam.words[3] = cdrom->numtracks * sizeof(tracks[0]);
    211 	* ((char *) (cdpb.csParam.words+4)) = 1;	/* First track */
    212 	if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) {
    213 		SDL_SetError("PBControlSync() failed");
    214 		return(-1);
    215 	}
    216 
    217 	/* Read all the track TOC entries */
    218 	SDL_cdlist[cdrom->id].hasAudio = false;
    219 	for ( i=0; i<cdrom->numtracks; ++i )
    220 		{
    221 		cdrom->track[i].id = i+1;
    222 		if (tracks[i].entry.control & kDataTrackMask)
    223 			cdrom->track[i].type = SDL_DATA_TRACK;
    224 		else
    225 			{
    226 			cdrom->track[i].type = SDL_AUDIO_TRACK;
    227 			SDL_cdlist[SDL_numcds].hasAudio = true;
    228 			}
    229 
    230 		cdrom->track[i].offset = MSF_TO_FRAMES(
    231 				SDL_SYS_BCDToShort(tracks[i].entry.min),
    232 				SDL_SYS_BCDToShort(tracks[i].entry.min),
    233 				SDL_SYS_BCDToShort(tracks[i].entry.frame));
    234 		cdrom->track[i].length = MSF_TO_FRAMES(
    235 				SDL_SYS_BCDToShort(tracks[i+1].entry.min),
    236 				SDL_SYS_BCDToShort(tracks[i+1].entry.min),
    237 				SDL_SYS_BCDToShort(tracks[i+1].entry.frame)) -
    238 				cdrom->track[i].offset;
    239 		}
    240 
    241 	/* Apparently SDL wants a fake last entry */
    242 	cdrom->track[i].offset = leadout;
    243 	cdrom->track[i].length = 0;
    244 
    245 	return(0);
    246 }
    247 
    248 /* Get CD-ROM status */
    249 static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position)
    250 {
    251 	CDCntrlParam cdpb;
    252 	CDstatus status = CD_ERROR;
    253 	Boolean spinning = false;
    254 
    255 	if (position) *position = 0;
    256 
    257 	/* Get the number of tracks on the CD by examining the TOC */
    258 	if ( ! get_drivenum(cdrom->id) ) {
    259 		return(CD_TRAYEMPTY);
    260 	}
    261 	SDL_memset(&cdpb, 0, sizeof(cdpb));
    262 	cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
    263 	cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
    264 	cdpb.csCode = kReadTOC;
    265 	cdpb.csParam.words[0] = kGetTrackRange;
    266 	if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) {
    267 		SDL_SetError("PBControlSync() failed");
    268 		return(CD_ERROR);
    269 	}
    270 
    271 	cdrom->numtracks =
    272 			SDL_SYS_BCDToShort(cdpb.csParam.bytes[1]) -
    273 			SDL_SYS_BCDToShort(cdpb.csParam.bytes[0]) + 1;
    274 	if ( cdrom->numtracks > SDL_MAX_TRACKS )
    275 		cdrom->numtracks = SDL_MAX_TRACKS;
    276 	cdrom->cur_track = 0; /* Apparently these are set elsewhere */
    277 	cdrom->cur_frame = 0; /* Apparently these are set elsewhere */
    278 
    279 
    280 	if (1 || SDL_cdlist[cdrom->id].hasAudio) {
    281 		/* Get the current playback status */
    282 		SDL_memset(&cdpb, 0, sizeof(cdpb));
    283 		cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
    284 		cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
    285 		cdpb.csCode = kAudioStatus;
    286 		if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) {
    287 			SDL_SetError("PBControlSync() failed");
    288 			return(-1);
    289 		}
    290 
    291 		switch(cdpb.csParam.cd.status) {
    292 			case kStatusPlaying:
    293 				status = CD_PLAYING;
    294 				spinning = true;
    295 				break;
    296 			case kStatusPaused:
    297 				status = CD_PAUSED;
    298 				spinning = true;
    299 				break;
    300 			case kStatusMuted:
    301 				status = CD_PLAYING; /* What should I do here? */
    302 				spinning = true;
    303 				break;
    304 			case kStatusDone:
    305 				status = CD_STOPPED;
    306 				spinning = true;
    307 				break;
    308 			case kStatusStopped:
    309 				status = CD_STOPPED;
    310 				spinning = false;
    311 				break;
    312 			case kStatusError:
    313 			default:
    314 				status = CD_ERROR;
    315 				spinning = false;
    316 				break;
    317 			}
    318 
    319 		if (spinning && position) *position = MSF_TO_FRAMES(
    320 				SDL_SYS_BCDToShort(cdpb.csParam.cd.minute),
    321 				SDL_SYS_BCDToShort(cdpb.csParam.cd.second),
    322 				SDL_SYS_BCDToShort(cdpb.csParam.cd.frame));
    323 		}
    324 	else
    325 		status = CD_ERROR; /* What should I do here? */
    326 
    327 	return(status);
    328 }
    329 
    330 /* Start play */
    331 static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length)
    332 {
    333 	CDCntrlParam cdpb;
    334 
    335 	/* Pause the current audio playback to avoid audible artifacts */
    336 	if ( SDL_SYS_CDPause(cdrom) < 0 ) {
    337 		return(-1);
    338 	}
    339 
    340 	/* Specify the AudioCD playback mode */
    341 	SDL_memset(&cdpb, 0, sizeof(cdpb));
    342 	cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
    343 	cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
    344 	cdpb.csCode = kSetPlayMode;
    345 	cdpb.csParam.bytes[0] = false;			/* Repeat? */
    346 	cdpb.csParam.bytes[1] = kPlayModeSequential;	/* Play mode */
    347 	/* Treat as soft error, NEC Drive doesnt support this call  */
    348 	PBControlSync((ParmBlkPtr) &cdpb);
    349 
    350 #if 1
    351 	/* Specify the end of audio playback */
    352 	SDL_memset(&cdpb, 0, sizeof(cdpb));
    353 	cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
    354 	cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
    355 	cdpb.csCode = kAudioStop;
    356 	cdpb.csParam.words[0] = kBlockPosition;		/* Position Mode */
    357 	*(long *) (cdpb.csParam.words + 1) = start+length-1; /* Search Address */
    358 	if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) {
    359 		SDL_SetError("PBControlSync() failed");
    360 		return(-1);
    361 	}
    362 
    363 	/* Specify the start of audio playback, and start it */
    364 	SDL_memset(&cdpb, 0, sizeof(cdpb));
    365 	cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
    366 	cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
    367 	cdpb.csCode = kAudioPlay;
    368 	cdpb.csParam.words[0] = kBlockPosition;			/* Position Mode */
    369 	*(long *) (cdpb.csParam.words + 1) = start+1;	/* Search Address */
    370 	cdpb.csParam.words[3] = false;					/* Stop address? */
    371 	cdpb.csParam.words[4] = kStereoPlayMode;		/* Audio Play Mode */
    372 	if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) {
    373 		SDL_SetError("PBControlSync() failed");
    374 		return(-1);
    375 	}
    376 #else
    377 	/* Specify the end of audio playback */
    378 	FRAMES_TO_MSF(start+length, &m, &s, &f);
    379 	SDL_memset(&cdpb, 0, sizeof(cdpb));
    380 	cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
    381 	cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
    382 	cdpb.csCode = kAudioStop;
    383 	cdpb.csParam.words[0] = kTrackPosition;			/* Position Mode */
    384 	cdpb.csParam.words[1] = 0;						/* Search Address (hiword)*/
    385 	cdpb.csParam.words[2] = 						/* Search Address (loword)*/
    386 			SDL_SYS_ShortToBCD(cdrom->numtracks);
    387 	if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) {
    388 		SDL_SetError("PBControlSync() failed");
    389 		return(-1);
    390 	}
    391 
    392 	/* Specify the start of audio playback, and start it */
    393 	FRAMES_TO_MSF(start, &m, &s, &f);
    394 	SDL_memset(&cdpb, 0, sizeof(cdpb));
    395 	cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
    396 	cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
    397 	cdpb.csCode = kAudioPlay;
    398 	cdpb.csParam.words[0] = kTrackPosition;			/* Position Mode */
    399 	cdpb.csParam.words[1] = 0;						/* Search Address (hiword)*/
    400 	cdpb.csParam.words[2] = SDL_SYS_ShortToBCD(1);	/* Search Address (loword)*/
    401 	cdpb.csParam.words[3] = false;					/* Stop address? */
    402 	cdpb.csParam.words[4] = kStereoPlayMode;		/* Audio Play Mode */
    403 	if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) {
    404 		SDL_SetError("PBControlSync() failed");
    405 		return(-1);
    406 	}
    407 #endif
    408 
    409 	return(0);
    410 }
    411 
    412 /* Pause play */
    413 static int SDL_SYS_CDPause(SDL_CD *cdrom)
    414 {
    415 	CDCntrlParam cdpb;
    416 
    417 	SDL_memset(&cdpb, 0, sizeof(cdpb));
    418 	cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
    419 	cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
    420 	cdpb.csCode = kAudioPause;
    421 	cdpb.csParam.words[0] = 0;	/* Pause/Continue Flag (hiword) */
    422 	cdpb.csParam.words[1] = 1;	/* Pause/Continue Flag (loword) */
    423 	if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) {
    424 		SDL_SetError("PBControlSync() failed");
    425 		return(-1);
    426 	}
    427 	return(0);
    428 }
    429 
    430 /* Resume play */
    431 static int SDL_SYS_CDResume(SDL_CD *cdrom)
    432 {
    433 	CDCntrlParam cdpb;
    434 
    435 	SDL_memset(&cdpb, 0, sizeof(cdpb));
    436 	cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
    437 	cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
    438 	cdpb.csCode = kAudioPause;
    439 	cdpb.csParam.words[0] = 0;	/* Pause/Continue Flag (hiword) */
    440 	cdpb.csParam.words[1] = 0;	/* Pause/Continue Flag (loword) */
    441 	if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) {
    442 		SDL_SetError("PBControlSync() failed");
    443 		return(-1);
    444 	}
    445 	return(0);
    446 }
    447 
    448 /* Stop play */
    449 static int SDL_SYS_CDStop(SDL_CD *cdrom)
    450 {
    451 	CDCntrlParam cdpb;
    452 
    453 	SDL_memset(&cdpb, 0, sizeof(cdpb));
    454 	cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
    455 	cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
    456 	cdpb.csCode = kAudioStop;
    457 	cdpb.csParam.words[0] = 0;		/* Position Mode */
    458 	cdpb.csParam.words[1] = 0;		/* Search Address (hiword) */
    459 	cdpb.csParam.words[2] = 0;		/* Search Address (loword) */
    460 	if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) {
    461 		SDL_SetError("PBControlSync() failed");
    462 		return(-1);
    463 	}
    464 	return(0);
    465 }
    466 
    467 /* Eject the CD-ROM */
    468 static int SDL_SYS_CDEject(SDL_CD *cdrom)
    469 {
    470 	Boolean	disk = false;
    471 	QHdr *driveQ = GetDrvQHdr();
    472 	DrvQEl *driveElem;
    473 	HParamBlockRec hpb;
    474 	ParamBlockRec cpb;
    475 
    476 	for ( driveElem = (DrvQEl *) driveQ->qHead; driveElem; driveElem =
    477 			  (driveElem) ? ((DrvQEl *) driveElem->qLink) :
    478 			  ((DrvQEl *) driveQ->qHead) ) {
    479 		if ( driveQ->qTail ) {
    480 			driveQ->qTail->qLink = 0;
    481 		}
    482 		if ( driveElem->dQRefNum != SDL_cdlist[cdrom->id].dRefNum ) {
    483 			continue;
    484 		}
    485 
    486 		/* Does drive contain mounted volume? If not, skip */
    487 		SDL_memset(&hpb, 0, sizeof(hpb));
    488 		hpb.volumeParam.ioVRefNum = driveElem->dQDrive;
    489 		if ( PBHGetVInfoSync(&hpb) != noErr ) {
    490 			continue;
    491 		}
    492 		if ( (UnmountVol(0, driveElem->dQDrive) == noErr) &&
    493 		     (Eject(0, driveElem->dQDrive) == noErr) ) {
    494 			driveElem = 0; /* Clear pointer to reset our loop */
    495 			disk = true;
    496 		}
    497 	}
    498 
    499 	/* If no disk is present, just eject the tray */
    500 	if (! disk) {
    501 		SDL_memset(&cpb, 0, sizeof(cpb));
    502 		cpb.cntrlParam.ioVRefNum = 0; /* No Drive */
    503 		cpb.cntrlParam.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
    504 		cpb.cntrlParam.csCode = kEjectTheDisc;
    505 		if ( PBControlSync((ParmBlkPtr)&cpb) != noErr ) {
    506 			SDL_SetError("PBControlSync() failed");
    507 			return(-1);
    508 		}
    509 	}
    510 	return(0);
    511 }
    512 
    513 /* Close the CD-ROM handle */
    514 static void SDL_SYS_CDClose(SDL_CD *cdrom)
    515 {
    516 	return;
    517 }
    518 
    519 void SDL_SYS_CDQuit(void)
    520 {
    521 	while(SDL_numcds--)
    522 		SDL_memset(SDL_cdlist + SDL_numcds, 0, sizeof(SDL_cdlist[0]));
    523 }
    524 
    525 #endif /* SDL_CDROM_MACOS */
    526