SDL-1.2.14
[sdl_omap.git] / src / cdrom / macos / SDL_syscdrom.c
1 /*
2     SDL - Simple DirectMedia Layer
3     Copyright (C) 1997-2009 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@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 */