SDL-1.2.14
[sdl_omap.git] / src / cdrom / win32 / 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_WIN32
25
26 /* Functions for system-level CD-ROM audio control */
27
28 #define WIN32_LEAN_AND_MEAN
29 #include <windows.h>
30 #include <mmsystem.h>
31
32 #include "SDL_cdrom.h"
33 #include "../SDL_syscdrom.h"
34
35 /* This really broken?? */
36 #define BROKEN_MCI_PAUSE        /* Pausing actually stops play -- Doh! */
37
38 /* The maximum number of CD-ROM drives we'll detect (Don't change!) */
39 #define MAX_DRIVES      26      
40
41 /* A list of available CD-ROM drives */
42 static char *SDL_cdlist[MAX_DRIVES];
43 static MCIDEVICEID SDL_mciID[MAX_DRIVES];
44 #ifdef BROKEN_MCI_PAUSE
45 static int SDL_paused[MAX_DRIVES];
46 #endif
47 static int SDL_CD_end_position;
48
49 /* The system-dependent CD control functions */
50 static const char *SDL_SYS_CDName(int drive);
51 static int SDL_SYS_CDOpen(int drive);
52 static int SDL_SYS_CDGetTOC(SDL_CD *cdrom);
53 static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position);
54 static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length);
55 static int SDL_SYS_CDPause(SDL_CD *cdrom);
56 static int SDL_SYS_CDResume(SDL_CD *cdrom);
57 static int SDL_SYS_CDStop(SDL_CD *cdrom);
58 static int SDL_SYS_CDEject(SDL_CD *cdrom);
59 static void SDL_SYS_CDClose(SDL_CD *cdrom);
60
61
62 /* Add a CD-ROM drive to our list of valid drives */
63 static void AddDrive(char *drive)
64 {
65         int i;
66
67         if ( SDL_numcds < MAX_DRIVES ) {
68                 /* Add this drive to our list */
69                 i = SDL_numcds;
70                 SDL_cdlist[i] = SDL_strdup(drive);
71                 if ( SDL_cdlist[i] == NULL ) {
72                         SDL_OutOfMemory();
73                         return;
74                 }
75                 ++SDL_numcds;
76 #ifdef CDROM_DEBUG
77   fprintf(stderr, "Added CD-ROM drive: %s\n", drive);
78 #endif
79         }
80 }
81
82 int  SDL_SYS_CDInit(void)
83 {
84         /* checklist: Drive 'A' - 'Z' */
85         int i;
86         char drive[4];
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         /* Scan the system for CD-ROM drives */
101         for ( i='A'; i<='Z'; ++i ) {
102                 SDL_snprintf(drive, SDL_arraysize(drive), "%c:\\", i);
103                 if ( GetDriveType(drive) == DRIVE_CDROM ) {
104                         AddDrive(drive);
105                 }
106         }
107         SDL_memset(SDL_mciID, 0, sizeof(SDL_mciID));
108         return(0);
109 }
110
111 /* General ioctl() CD-ROM command function */
112 static int SDL_SYS_CDioctl(int id, UINT msg, DWORD flags, void *arg)
113 {
114         MCIERROR mci_error;
115
116         mci_error = mciSendCommand(SDL_mciID[id], msg, flags, (DWORD_PTR)arg);
117         if ( mci_error ) {
118                 char error[256];
119
120                 mciGetErrorString(mci_error, error, 256);
121                 SDL_SetError("mciSendCommand() error: %s", error);
122         }
123         return(!mci_error ? 0 : -1);
124 }
125
126 static const char *SDL_SYS_CDName(int drive)
127 {
128         return(SDL_cdlist[drive]);
129 }
130
131 static int SDL_SYS_CDOpen(int drive)
132 {
133         MCI_OPEN_PARMS mci_open;
134         MCI_SET_PARMS mci_set;
135         char device[3];
136         DWORD flags;
137
138         /* Open the requested device */
139         mci_open.lpstrDeviceType = (LPCSTR) MCI_DEVTYPE_CD_AUDIO;
140         device[0] = *SDL_cdlist[drive];
141         device[1] = ':';
142         device[2] = '\0';
143         mci_open.lpstrElementName = device;
144         flags =
145           (MCI_OPEN_TYPE|MCI_OPEN_SHAREABLE|MCI_OPEN_TYPE_ID|MCI_OPEN_ELEMENT);
146         if ( SDL_SYS_CDioctl(0, MCI_OPEN, flags, &mci_open) < 0 ) {
147                 flags &= ~MCI_OPEN_SHAREABLE;
148                 if ( SDL_SYS_CDioctl(0, MCI_OPEN, flags, &mci_open) < 0 ) {
149                         return(-1);
150                 }
151         }
152         SDL_mciID[drive] = mci_open.wDeviceID;
153
154         /* Set the minute-second-frame time format */
155         mci_set.dwTimeFormat = MCI_FORMAT_MSF;
156         SDL_SYS_CDioctl(drive, MCI_SET, MCI_SET_TIME_FORMAT, &mci_set);
157
158 #ifdef BROKEN_MCI_PAUSE
159         SDL_paused[drive] = 0;
160 #endif
161         return(drive);
162 }
163
164 static int SDL_SYS_CDGetTOC(SDL_CD *cdrom)
165 {
166         MCI_STATUS_PARMS mci_status;
167         int i, okay;
168         DWORD flags;
169
170         okay = 0;
171         mci_status.dwItem = MCI_STATUS_NUMBER_OF_TRACKS;
172         flags = MCI_STATUS_ITEM | MCI_WAIT;
173         if ( SDL_SYS_CDioctl(cdrom->id, MCI_STATUS, flags, &mci_status) == 0 ) {
174                 cdrom->numtracks = mci_status.dwReturn;
175                 if ( cdrom->numtracks > SDL_MAX_TRACKS ) {
176                         cdrom->numtracks = SDL_MAX_TRACKS;
177                 }
178                 /* Read all the track TOC entries */
179                 flags = MCI_STATUS_ITEM | MCI_TRACK | MCI_WAIT;
180                 for ( i=0; i<cdrom->numtracks; ++i ) {
181                         cdrom->track[i].id = i+1;
182                         mci_status.dwTrack = cdrom->track[i].id;
183 #ifdef MCI_CDA_STATUS_TYPE_TRACK
184                         mci_status.dwItem = MCI_CDA_STATUS_TYPE_TRACK;
185                         if ( SDL_SYS_CDioctl(cdrom->id, MCI_STATUS, flags,
186                                                         &mci_status) < 0 ) {
187                                 break;
188                         }
189                         if ( mci_status.dwReturn == MCI_CDA_TRACK_AUDIO ) {
190                                 cdrom->track[i].type = SDL_AUDIO_TRACK;
191                         } else {
192                                 cdrom->track[i].type = SDL_DATA_TRACK;
193                         }
194 #else
195                         cdrom->track[i].type = SDL_AUDIO_TRACK;
196 #endif
197                         mci_status.dwItem = MCI_STATUS_POSITION;
198                         if ( SDL_SYS_CDioctl(cdrom->id, MCI_STATUS, flags,
199                                                         &mci_status) < 0 ) {
200                                 break;
201                         }
202                         cdrom->track[i].offset = MSF_TO_FRAMES(
203                                         MCI_MSF_MINUTE(mci_status.dwReturn),
204                                         MCI_MSF_SECOND(mci_status.dwReturn),
205                                         MCI_MSF_FRAME(mci_status.dwReturn));
206                         cdrom->track[i].length = 0;
207                         if ( i > 0 ) {
208                                 cdrom->track[i-1].length =
209                                                 cdrom->track[i].offset-
210                                                 cdrom->track[i-1].offset;
211                         }
212                 }
213                 if ( i == cdrom->numtracks ) {
214                         mci_status.dwTrack = cdrom->track[i - 1].id;
215                         mci_status.dwItem = MCI_STATUS_LENGTH;
216                         if ( SDL_SYS_CDioctl(cdrom->id, MCI_STATUS, flags,
217                                                         &mci_status) == 0 ) {
218                                 cdrom->track[i - 1].length = MSF_TO_FRAMES(
219                                         MCI_MSF_MINUTE(mci_status.dwReturn),
220                                         MCI_MSF_SECOND(mci_status.dwReturn),
221                                         MCI_MSF_FRAME(mci_status.dwReturn));
222                                 /* compute lead-out offset */
223                                 cdrom->track[i].offset = cdrom->track[i - 1].offset +
224                                         cdrom->track[i - 1].length;
225                                 cdrom->track[i].length = 0;
226                                 okay = 1;
227                         }
228                 }
229         }
230         return(okay ? 0 : -1);
231 }
232
233 /* Get CD-ROM status */
234 static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position)
235 {
236         CDstatus status;
237         MCI_STATUS_PARMS mci_status;
238         DWORD flags;
239
240         flags = MCI_STATUS_ITEM | MCI_WAIT;
241         mci_status.dwItem = MCI_STATUS_MODE;
242         if ( SDL_SYS_CDioctl(cdrom->id, MCI_STATUS, flags, &mci_status) < 0 ) {
243                 status = CD_ERROR;
244         } else {
245                 switch (mci_status.dwReturn) {
246                         case MCI_MODE_NOT_READY:
247                         case MCI_MODE_OPEN:
248                                 status = CD_TRAYEMPTY;
249                                 break;
250                         case MCI_MODE_STOP:
251 #ifdef BROKEN_MCI_PAUSE
252                                 if ( SDL_paused[cdrom->id] ) {
253                                         status = CD_PAUSED;
254                                 } else {
255                                         status = CD_STOPPED;
256                                 }
257 #else
258                                 status = CD_STOPPED;
259 #endif /* BROKEN_MCI_PAUSE */
260                                 break;
261                         case MCI_MODE_PLAY:
262 #ifdef BROKEN_MCI_PAUSE
263                                 if ( SDL_paused[cdrom->id] ) {
264                                         status = CD_PAUSED;
265                                 } else {
266                                         status = CD_PLAYING;
267                                 }
268 #else
269                                 status = CD_PLAYING;
270 #endif /* BROKEN_MCI_PAUSE */
271                                 break;
272                         case MCI_MODE_PAUSE:
273                                 status = CD_PAUSED;
274                                 break;
275                         default:
276                                 status = CD_ERROR;
277                                 break;
278                 }
279         }
280         if ( position ) {
281                 if ( status == CD_PLAYING || (status == CD_PAUSED) ) {
282                         mci_status.dwItem = MCI_STATUS_POSITION;
283                         if ( SDL_SYS_CDioctl(cdrom->id, MCI_STATUS, flags,
284                                                         &mci_status) == 0 ) {
285                                 *position = MSF_TO_FRAMES(
286                                         MCI_MSF_MINUTE(mci_status.dwReturn),
287                                         MCI_MSF_SECOND(mci_status.dwReturn),
288                                         MCI_MSF_FRAME(mci_status.dwReturn));
289                         } else {
290                                 *position = 0;
291                         }
292                 } else {
293                         *position = 0;
294                 }
295         }
296         return(status);
297 }
298
299 /* Start play */
300 static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length)
301 {
302         MCI_PLAY_PARMS mci_play;
303         int m, s, f;
304         DWORD flags;
305
306         flags = MCI_FROM | MCI_TO | MCI_NOTIFY;
307         mci_play.dwCallback = 0;
308         FRAMES_TO_MSF(start, &m, &s, &f);
309         mci_play.dwFrom = MCI_MAKE_MSF(m, s, f);
310         FRAMES_TO_MSF(start+length, &m, &s, &f);
311         mci_play.dwTo = MCI_MAKE_MSF(m, s, f);
312         SDL_CD_end_position = mci_play.dwTo;
313         return(SDL_SYS_CDioctl(cdrom->id, MCI_PLAY, flags, &mci_play));
314 }
315
316 /* Pause play */
317 static int SDL_SYS_CDPause(SDL_CD *cdrom)
318 {
319 #ifdef BROKEN_MCI_PAUSE
320         SDL_paused[cdrom->id] = 1;
321 #endif
322         return(SDL_SYS_CDioctl(cdrom->id, MCI_PAUSE, MCI_WAIT, NULL));
323 }
324
325 /* Resume play */
326 static int SDL_SYS_CDResume(SDL_CD *cdrom)
327 {
328 #ifdef BROKEN_MCI_PAUSE
329         MCI_STATUS_PARMS mci_status;
330         int okay;
331         int flags;
332
333         okay = 0;
334         /* Play from the current play position to the end position set earlier */
335         flags = MCI_STATUS_ITEM | MCI_WAIT;
336         mci_status.dwItem = MCI_STATUS_POSITION;
337         if ( SDL_SYS_CDioctl(cdrom->id, MCI_STATUS, flags, &mci_status) == 0 ) {
338                 MCI_PLAY_PARMS mci_play;
339
340                 flags = MCI_FROM | MCI_TO | MCI_NOTIFY;
341                 mci_play.dwCallback = 0;
342                 mci_play.dwFrom = mci_status.dwReturn;
343                 mci_play.dwTo = SDL_CD_end_position;
344                 if (SDL_SYS_CDioctl(cdrom->id,MCI_PLAY,flags,&mci_play) == 0) {
345                         okay = 1;
346                         SDL_paused[cdrom->id] = 0;
347                 }
348         }
349         return(okay ? 0 : -1);
350 #else
351         return(SDL_SYS_CDioctl(cdrom->id, MCI_RESUME, MCI_WAIT, NULL));
352 #endif /* BROKEN_MCI_PAUSE */
353 }
354
355 /* Stop play */
356 static int SDL_SYS_CDStop(SDL_CD *cdrom)
357 {
358         return(SDL_SYS_CDioctl(cdrom->id, MCI_STOP, MCI_WAIT, NULL));
359 }
360
361 /* Eject the CD-ROM */
362 static int SDL_SYS_CDEject(SDL_CD *cdrom)
363 {
364         return(SDL_SYS_CDioctl(cdrom->id, MCI_SET, MCI_SET_DOOR_OPEN, NULL));
365 }
366
367 /* Close the CD-ROM handle */
368 static void SDL_SYS_CDClose(SDL_CD *cdrom)
369 {
370         SDL_SYS_CDioctl(cdrom->id, MCI_CLOSE, MCI_WAIT, NULL);
371 }
372
373 void SDL_SYS_CDQuit(void)
374 {
375         int i;
376
377         if ( SDL_numcds > 0 ) {
378                 for ( i=0; i<SDL_numcds; ++i ) {
379                         SDL_free(SDL_cdlist[i]);
380                         SDL_cdlist[i] = NULL;
381                 }
382                 SDL_numcds = 0;
383         }
384 }
385
386 #endif /* SDL_CDROM_WIN32 */