SDL-1.2.14
[sdl_omap.git] / src / cdrom / macosx / 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_MACOSX
25
26 #include "SDL_syscdrom_c.h"
27
28 #pragma mark -- Globals --
29
30 static FSRef**         tracks;
31 static FSVolumeRefNum* volumes;
32 static CDstatus        status;
33 static int             nextTrackFrame;
34 static int             nextTrackFramesRemaining;
35 static int             fakeCD;
36 static int             currentTrack;
37 static int             didReadTOC;
38 static int             cacheTOCNumTracks;
39 static int             currentDrive; /* Only allow 1 drive in use at a time */
40
41 #pragma mark -- Prototypes --
42
43 static const char *SDL_SYS_CDName   (int drive);
44 static int         SDL_SYS_CDOpen   (int drive);
45 static int         SDL_SYS_CDGetTOC (SDL_CD *cdrom);
46 static CDstatus    SDL_SYS_CDStatus (SDL_CD *cdrom, int *position);
47 static int         SDL_SYS_CDPlay   (SDL_CD *cdrom, int start, int length);
48 static int         SDL_SYS_CDPause  (SDL_CD *cdrom);
49 static int         SDL_SYS_CDResume (SDL_CD *cdrom);
50 static int         SDL_SYS_CDStop   (SDL_CD *cdrom);
51 static int         SDL_SYS_CDEject  (SDL_CD *cdrom);
52 static void        SDL_SYS_CDClose  (SDL_CD *cdrom);
53
54 #pragma mark -- Helper Functions --
55
56 /* Read a list of tracks from the volume */
57 static int LoadTracks (SDL_CD *cdrom)
58 {
59     /* Check if tracks are already loaded */
60     if  ( tracks[cdrom->id] != NULL )
61         return 0;
62         
63     /* Allocate memory for tracks */
64     tracks[cdrom->id] = (FSRef*) SDL_calloc (1, sizeof(**tracks) * cdrom->numtracks);
65     if (tracks[cdrom->id] == NULL) {
66         SDL_OutOfMemory ();
67         return -1;
68     }
69     
70     /* Load tracks */
71     if (ListTrackFiles (volumes[cdrom->id], tracks[cdrom->id], cdrom->numtracks) < 0)
72         return -1;
73
74     return 0;
75 }
76
77 /* Find a file for a given start frame and length */
78 static FSRef* GetFileForOffset (SDL_CD *cdrom, int start, int length,  int *outStartFrame, int *outStopFrame)
79 {
80     int i;
81     
82     for (i = 0; i < cdrom->numtracks; i++) {
83     
84         if (cdrom->track[i].offset <= start &&
85             start < (cdrom->track[i].offset + cdrom->track[i].length))
86             break;
87     }
88     
89     if (i == cdrom->numtracks)
90         return NULL;
91         
92     currentTrack = i;
93
94     *outStartFrame = start - cdrom->track[i].offset;
95     
96     if ((*outStartFrame + length) < cdrom->track[i].length) {
97         *outStopFrame = *outStartFrame + length;
98         length = 0;
99         nextTrackFrame = -1;
100         nextTrackFramesRemaining = -1;
101     }
102     else {
103         *outStopFrame = -1;
104         length -= cdrom->track[i].length - *outStartFrame;
105         nextTrackFrame = cdrom->track[i+1].offset;
106         nextTrackFramesRemaining = length;
107     }
108     
109     return &tracks[cdrom->id][i];
110 }
111
112 /* Setup another file for playback, or stop playback (called from another thread) */
113 static void CompletionProc (SDL_CD *cdrom)
114 {
115     
116     Lock ();
117     
118     if (nextTrackFrame > 0 && nextTrackFramesRemaining > 0) {
119     
120         /* Load the next file to play */
121         int startFrame, stopFrame;
122         FSRef *file;
123         
124         PauseFile ();
125         ReleaseFile ();
126                 
127         file = GetFileForOffset (cdrom, nextTrackFrame, 
128             nextTrackFramesRemaining, &startFrame, &stopFrame);
129         
130         if (file == NULL) {
131             status = CD_STOPPED;
132             Unlock ();
133             return;
134         }
135         
136         LoadFile (file, startFrame, stopFrame);
137         
138         SetCompletionProc (CompletionProc, cdrom);
139         
140         PlayFile ();
141     }
142     else {
143     
144         /* Release the current file */
145         PauseFile ();
146         ReleaseFile ();
147         status = CD_STOPPED;
148     }
149     
150     Unlock ();
151 }
152
153
154 #pragma mark -- Driver Functions --
155
156 /* Initialize */
157 int SDL_SYS_CDInit (void) 
158 {
159     /* Initialize globals */
160     volumes = NULL;
161     tracks  = NULL;
162     status  = CD_STOPPED;
163     nextTrackFrame = -1;
164     nextTrackFramesRemaining = -1;
165     fakeCD  = SDL_FALSE;
166     currentTrack = -1;
167     didReadTOC = SDL_FALSE;
168     cacheTOCNumTracks = -1;
169     currentDrive = -1;
170     
171     /* Fill in function pointers */
172     SDL_CDcaps.Name   = SDL_SYS_CDName;
173     SDL_CDcaps.Open   = SDL_SYS_CDOpen;
174     SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC;
175     SDL_CDcaps.Status = SDL_SYS_CDStatus;
176     SDL_CDcaps.Play   = SDL_SYS_CDPlay;
177     SDL_CDcaps.Pause  = SDL_SYS_CDPause;
178     SDL_CDcaps.Resume = SDL_SYS_CDResume;
179     SDL_CDcaps.Stop   = SDL_SYS_CDStop;
180     SDL_CDcaps.Eject  = SDL_SYS_CDEject;
181     SDL_CDcaps.Close  = SDL_SYS_CDClose;
182
183     /* 
184         Read the list of "drives"
185         
186         This is currently a hack that infers drives from
187         mounted audio CD volumes, rather than
188         actual CD-ROM devices - which means it may not
189         act as expected sometimes.
190     */
191     
192     /* Find out how many cd volumes are mounted */
193     SDL_numcds = DetectAudioCDVolumes (NULL, 0);
194
195     /*
196         If there are no volumes, fake a cd device
197         so tray empty can be reported.
198     */
199     if (SDL_numcds == 0) {
200     
201         fakeCD = SDL_TRUE;
202         SDL_numcds = 1;
203         status = CD_TRAYEMPTY;
204         
205         return 0;
206     }
207     
208     /* Allocate space for volumes */
209     volumes = (FSVolumeRefNum*) SDL_calloc (1, sizeof(*volumes) * SDL_numcds);
210     if (volumes == NULL) {
211         SDL_OutOfMemory ();
212         return -1;
213     }
214     
215     /* Allocate space for tracks */
216     tracks = (FSRef**) SDL_calloc (1, sizeof(*tracks) * (SDL_numcds + 1));
217     if (tracks == NULL) {
218         SDL_OutOfMemory ();
219         return -1;
220     }
221     
222     /* Mark the end of the tracks array */
223     tracks[ SDL_numcds ] = (FSRef*)-1;
224     
225     /* 
226         Redetect, now save all volumes for later
227         Update SDL_numcds just in case it changed
228     */
229     {
230         int numVolumes = SDL_numcds;
231         
232         SDL_numcds = DetectAudioCDVolumes (volumes, numVolumes);
233         
234         /* If more cds suddenly show up, ignore them */
235         if (SDL_numcds > numVolumes) {
236             SDL_SetError ("Some CD's were added but they will be ignored");
237             SDL_numcds = numVolumes;
238         }
239     }
240     
241     return 0;
242 }
243
244 /* Shutdown and cleanup */
245 void SDL_SYS_CDQuit(void)
246 {
247     ReleaseFile();
248     
249     if (volumes != NULL)
250         free (volumes);
251         
252     if (tracks != NULL) {
253     
254         FSRef **ptr;
255         for (ptr = tracks; *ptr != (FSRef*)-1; ptr++)
256             if (*ptr != NULL)
257                 free (*ptr);
258             
259         free (tracks);
260     }
261 }
262
263 /* Get the Unix disk name of the volume */
264 static const char *SDL_SYS_CDName (int drive)
265 {
266     /*
267      * !!! FIXME: PBHGetVolParmsSync() is gone in 10.6,
268      * !!! FIXME:  replaced with FSGetVolumeParms(), which
269      * !!! FIXME:  isn't available before 10.5.  :/
270      */
271     return "Mac OS X CD-ROM Device";
272
273 #if 0
274     OSStatus     err = noErr;
275     HParamBlockRec  pb;
276     GetVolParmsInfoBuffer   volParmsInfo;
277    
278     if (fakeCD)
279         return "Fake CD-ROM Device";
280
281     pb.ioParam.ioNamePtr = NULL;
282     pb.ioParam.ioVRefNum = volumes[drive];
283     pb.ioParam.ioBuffer = (Ptr)&volParmsInfo;
284     pb.ioParam.ioReqCount = (SInt32)sizeof(volParmsInfo);
285     err = PBHGetVolParmsSync(&pb);
286
287     if (err != noErr) {
288         SDL_SetError ("PBHGetVolParmsSync returned %d", err);
289         return NULL;
290     }
291
292     return volParmsInfo.vMDeviceID;
293 #endif
294 }
295
296 /* Open the "device" */
297 static int SDL_SYS_CDOpen (int drive)
298 {
299     /* Only allow 1 device to be open */
300     if (currentDrive >= 0) {
301         SDL_SetError ("Only one cdrom is supported");
302         return -1;
303     }
304     else
305         currentDrive = drive;
306
307     return drive;
308 }
309
310 /* Get the table of contents */
311 static int SDL_SYS_CDGetTOC (SDL_CD *cdrom)
312 {
313     if (fakeCD) {
314         SDL_SetError (kErrorFakeDevice);
315         return -1;
316     }
317     
318     if (didReadTOC) {
319         cdrom->numtracks = cacheTOCNumTracks;
320         return 0;
321     }
322     
323     
324     ReadTOCData (volumes[cdrom->id], cdrom);
325     didReadTOC = SDL_TRUE;
326     cacheTOCNumTracks = cdrom->numtracks;
327     
328     return 0;
329 }
330
331 /* Get CD-ROM status */
332 static CDstatus SDL_SYS_CDStatus (SDL_CD *cdrom, int *position)
333 {
334     if (position) {
335         int trackFrame;
336         
337         Lock ();
338         trackFrame = GetCurrentFrame ();
339         Unlock ();
340     
341         *position = cdrom->track[currentTrack].offset + trackFrame;
342     }
343     
344     return status;
345 }
346
347 /* Start playback */
348 static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length)
349 {
350     int startFrame, stopFrame;
351     FSRef *ref;
352     
353     if (fakeCD) {
354         SDL_SetError (kErrorFakeDevice);
355         return -1;
356     }
357     
358     Lock();
359     
360     if (LoadTracks (cdrom) < 0)
361         return -2;
362     
363     if (PauseFile () < 0)
364         return -3;
365         
366     if (ReleaseFile () < 0)
367         return -4;
368     
369     ref = GetFileForOffset (cdrom, start, length, &startFrame, &stopFrame);
370     if (ref == NULL) {
371         SDL_SetError ("SDL_SYS_CDPlay: No file for start=%d, length=%d", start, length);
372         return -5;
373     }
374     
375     if (LoadFile (ref, startFrame, stopFrame) < 0)
376         return -6;
377     
378     SetCompletionProc (CompletionProc, cdrom);
379     
380     if (PlayFile () < 0)
381         return -7;
382     
383     status = CD_PLAYING;
384     
385     Unlock();
386     
387     return 0;
388 }
389
390 /* Pause playback */
391 static int SDL_SYS_CDPause(SDL_CD *cdrom)
392 {
393     if (fakeCD) {
394         SDL_SetError (kErrorFakeDevice);
395         return -1;
396     }
397     
398     Lock ();
399     
400     if (PauseFile () < 0) {
401         Unlock ();
402         return -2;
403     }
404     
405     status = CD_PAUSED;
406     
407     Unlock ();
408     
409     return 0;
410 }
411
412 /* Resume playback */
413 static int SDL_SYS_CDResume(SDL_CD *cdrom)
414 {
415     if (fakeCD) {
416         SDL_SetError (kErrorFakeDevice);
417         return -1;
418     }
419     
420     Lock ();
421     
422     if (PlayFile () < 0) {
423         Unlock ();
424         return -2;
425     }
426         
427     status = CD_PLAYING;
428     
429     Unlock ();
430     
431     return 0;
432 }
433
434 /* Stop playback */
435 static int SDL_SYS_CDStop(SDL_CD *cdrom)
436 {
437     if (fakeCD) {
438         SDL_SetError (kErrorFakeDevice);
439         return -1;
440     }
441     
442     Lock ();
443     
444     if (PauseFile () < 0) {
445         Unlock ();
446         return -2;
447     }
448         
449     if (ReleaseFile () < 0) {
450         Unlock ();
451         return -3;
452     }
453         
454     status = CD_STOPPED;
455     
456     Unlock ();
457     
458     return 0;
459 }
460
461 /* Eject the CD-ROM (Unmount the volume) */
462 static int SDL_SYS_CDEject(SDL_CD *cdrom)
463 {
464     OSStatus err;
465     pid_t dissenter;
466
467     if (fakeCD) {
468         SDL_SetError (kErrorFakeDevice);
469         return -1;
470     }
471     
472     Lock ();
473     
474     if (PauseFile () < 0) {
475         Unlock ();
476         return -2;
477     }
478         
479     if (ReleaseFile () < 0) {
480         Unlock ();
481         return -3;
482     }
483     
484     status = CD_STOPPED;
485     
486         /* Eject the volume */
487         err = FSEjectVolumeSync(volumes[cdrom->id], kNilOptions, &dissenter);
488
489         if (err != noErr) {
490         Unlock ();
491                 SDL_SetError ("PBUnmountVol returned %d", err);
492                 return -4;
493         }
494     
495     status = CD_TRAYEMPTY;
496
497     /* Invalidate volume and track info */
498     volumes[cdrom->id] = 0;
499     free (tracks[cdrom->id]);
500     tracks[cdrom->id] = NULL;
501     
502     Unlock ();
503     
504     return 0;
505 }
506
507 /* Close the CD-ROM */
508 static void SDL_SYS_CDClose(SDL_CD *cdrom)
509 {
510     currentDrive = -1;
511     return;
512 }
513
514 #endif /* SDL_CDROM_MACOSX */