SDL 1.2.13
[crawl:crawl-sdl.git] / src / cdrom / macosx / SDL_syscdrom.c
1 /*
2     SDL - Simple DirectMedia Layer
3     Copyright (C) 1997-2006 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     OSStatus     err = noErr;
267     HParamBlockRec  pb;
268     GetVolParmsInfoBuffer   volParmsInfo;
269    
270     if (fakeCD)
271         return "Fake CD-ROM Device";
272
273     pb.ioParam.ioNamePtr = NULL;
274     pb.ioParam.ioVRefNum = volumes[drive];
275     pb.ioParam.ioBuffer = (Ptr)&volParmsInfo;
276     pb.ioParam.ioReqCount = (SInt32)sizeof(volParmsInfo);
277     err = PBHGetVolParmsSync(&pb);
278
279     if (err != noErr) {
280         SDL_SetError ("PBHGetVolParmsSync returned %d", err);
281         return NULL;
282     }
283
284     return volParmsInfo.vMDeviceID;
285 }
286
287 /* Open the "device" */
288 static int SDL_SYS_CDOpen (int drive)
289 {
290     /* Only allow 1 device to be open */
291     if (currentDrive >= 0) {
292         SDL_SetError ("Only one cdrom is supported");
293         return -1;
294     }
295     else
296         currentDrive = drive;
297
298     return drive;
299 }
300
301 /* Get the table of contents */
302 static int SDL_SYS_CDGetTOC (SDL_CD *cdrom)
303 {
304     if (fakeCD) {
305         SDL_SetError (kErrorFakeDevice);
306         return -1;
307     }
308     
309     if (didReadTOC) {
310         cdrom->numtracks = cacheTOCNumTracks;
311         return 0;
312     }
313     
314     
315     ReadTOCData (volumes[cdrom->id], cdrom);
316     didReadTOC = SDL_TRUE;
317     cacheTOCNumTracks = cdrom->numtracks;
318     
319     return 0;
320 }
321
322 /* Get CD-ROM status */
323 static CDstatus SDL_SYS_CDStatus (SDL_CD *cdrom, int *position)
324 {
325     if (position) {
326         int trackFrame;
327         
328         Lock ();
329         trackFrame = GetCurrentFrame ();
330         Unlock ();
331     
332         *position = cdrom->track[currentTrack].offset + trackFrame;
333     }
334     
335     return status;
336 }
337
338 /* Start playback */
339 static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length)
340 {
341     int startFrame, stopFrame;
342     FSRef *ref;
343     
344     if (fakeCD) {
345         SDL_SetError (kErrorFakeDevice);
346         return -1;
347     }
348     
349     Lock();
350     
351     if (LoadTracks (cdrom) < 0)
352         return -2;
353     
354     if (PauseFile () < 0)
355         return -3;
356         
357     if (ReleaseFile () < 0)
358         return -4;
359     
360     ref = GetFileForOffset (cdrom, start, length, &startFrame, &stopFrame);
361     if (ref == NULL) {
362         SDL_SetError ("SDL_SYS_CDPlay: No file for start=%d, length=%d", start, length);
363         return -5;
364     }
365     
366     if (LoadFile (ref, startFrame, stopFrame) < 0)
367         return -6;
368     
369     SetCompletionProc (CompletionProc, cdrom);
370     
371     if (PlayFile () < 0)
372         return -7;
373     
374     status = CD_PLAYING;
375     
376     Unlock();
377     
378     return 0;
379 }
380
381 /* Pause playback */
382 static int SDL_SYS_CDPause(SDL_CD *cdrom)
383 {
384     if (fakeCD) {
385         SDL_SetError (kErrorFakeDevice);
386         return -1;
387     }
388     
389     Lock ();
390     
391     if (PauseFile () < 0) {
392         Unlock ();
393         return -2;
394     }
395     
396     status = CD_PAUSED;
397     
398     Unlock ();
399     
400     return 0;
401 }
402
403 /* Resume playback */
404 static int SDL_SYS_CDResume(SDL_CD *cdrom)
405 {
406     if (fakeCD) {
407         SDL_SetError (kErrorFakeDevice);
408         return -1;
409     }
410     
411     Lock ();
412     
413     if (PlayFile () < 0) {
414         Unlock ();
415         return -2;
416     }
417         
418     status = CD_PLAYING;
419     
420     Unlock ();
421     
422     return 0;
423 }
424
425 /* Stop playback */
426 static int SDL_SYS_CDStop(SDL_CD *cdrom)
427 {
428     if (fakeCD) {
429         SDL_SetError (kErrorFakeDevice);
430         return -1;
431     }
432     
433     Lock ();
434     
435     if (PauseFile () < 0) {
436         Unlock ();
437         return -2;
438     }
439         
440     if (ReleaseFile () < 0) {
441         Unlock ();
442         return -3;
443     }
444         
445     status = CD_STOPPED;
446     
447     Unlock ();
448     
449     return 0;
450 }
451
452 /* Eject the CD-ROM (Unmount the volume) */
453 static int SDL_SYS_CDEject(SDL_CD *cdrom)
454 {
455     OSStatus err;
456     pid_t dissenter;
457
458     if (fakeCD) {
459         SDL_SetError (kErrorFakeDevice);
460         return -1;
461     }
462     
463     Lock ();
464     
465     if (PauseFile () < 0) {
466         Unlock ();
467         return -2;
468     }
469         
470     if (ReleaseFile () < 0) {
471         Unlock ();
472         return -3;
473     }
474     
475     status = CD_STOPPED;
476     
477         /* Eject the volume */
478         err = FSEjectVolumeSync(volumes[cdrom->id], kNilOptions, &dissenter);
479
480         if (err != noErr) {
481         Unlock ();
482                 SDL_SetError ("PBUnmountVol returned %d", err);
483                 return -4;
484         }
485     
486     status = CD_TRAYEMPTY;
487
488     /* Invalidate volume and track info */
489     volumes[cdrom->id] = 0;
490     free (tracks[cdrom->id]);
491     tracks[cdrom->id] = NULL;
492     
493     Unlock ();
494     
495     return 0;
496 }
497
498 /* Close the CD-ROM */
499 static void SDL_SYS_CDClose(SDL_CD *cdrom)
500 {
501     currentDrive = -1;
502     return;
503 }
504
505 #endif /* SDL_CDROM_MACOSX */