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@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 */ 42static char *SDL_cdlist[MAX_DRIVES]; 43static MCIDEVICEID SDL_mciID[MAX_DRIVES]; 44#ifdef BROKEN_MCI_PAUSE 45static int SDL_paused[MAX_DRIVES]; 46#endif 47static int SDL_CD_end_position; 48 49/* The system-dependent CD control functions */ 50static const char *SDL_SYS_CDName(int drive); 51static int SDL_SYS_CDOpen(int drive); 52static int SDL_SYS_CDGetTOC(SDL_CD *cdrom); 53static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position); 54static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length); 55static int SDL_SYS_CDPause(SDL_CD *cdrom); 56static int SDL_SYS_CDResume(SDL_CD *cdrom); 57static int SDL_SYS_CDStop(SDL_CD *cdrom); 58static int SDL_SYS_CDEject(SDL_CD *cdrom); 59static void SDL_SYS_CDClose(SDL_CD *cdrom); 60 61 62/* Add a CD-ROM drive to our list of valid drives */ 63static 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 82int 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 */ 112static 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 126static const char *SDL_SYS_CDName(int drive) 127{ 128 return(SDL_cdlist[drive]); 129} 130 131static 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 164static 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 */ 234static 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 */ 300static 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 */ 317static 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 */ 326static 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 */ 356static 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 */ 362static 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 */ 368static void SDL_SYS_CDClose(SDL_CD *cdrom) 369{ 370 SDL_SYS_CDioctl(cdrom->id, MCI_CLOSE, MCI_WAIT, NULL); 371} 372 373void 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 */ 387