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_OS2
25
26/* Functions for system-level CD-ROM audio control */
27
28#define INCL_MCIOS2
29#include <os2.h>
30#include <os2me.h>
31
32#include "SDL_cdrom.h"
33#include "../SDL_syscdrom.h"
34
35/* Size of MCI result buffer (in bytes) */
36#define MCI_CMDRETBUFSIZE	128
37
38/* The maximum number of CD-ROM drives we'll detect */
39#define MAX_DRIVES	16
40
41/* A list of available CD-ROM drives */
42static char *SDL_cdlist[MAX_DRIVES];
43//static dev_t SDL_cdmode[MAX_DRIVES];
44
45/* The system-dependent CD control functions */
46static const char *SDL_SYS_CDName(int drive);
47static int SDL_SYS_CDOpen(int drive);
48static int SDL_SYS_CDGetTOC(SDL_CD *cdrom);
49static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position);
50static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length);
51static int SDL_SYS_CDPause(SDL_CD *cdrom);
52static int SDL_SYS_CDResume(SDL_CD *cdrom);
53static int SDL_SYS_CDStop(SDL_CD *cdrom);
54static int SDL_SYS_CDEject(SDL_CD *cdrom);
55static void SDL_SYS_CDClose(SDL_CD *cdrom);
56
57/* MCI Timing Functions */
58#define	MCI_MMTIMEPERSECOND		3000
59#define	FRAMESFROMMM(mmtime)		(((mmtime)*CD_FPS)/MCI_MMTIMEPERSECOND)
60
61
62/* Ready for MCI CDAudio Devices */
63int  SDL_SYS_CDInit(void)
64{
65int i; /* generig counter */
66MCI_SYSINFO_PARMS		msp;	/* Structure to MCI SysInfo parameters */
67CHAR 						SysInfoRet[MCI_CMDRETBUFSIZE];	/* Buffer for MCI Command result */
68
69/* Fill in our driver capabilities */
70SDL_CDcaps.Name = SDL_SYS_CDName;
71SDL_CDcaps.Open = SDL_SYS_CDOpen;
72SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC;
73SDL_CDcaps.Status = SDL_SYS_CDStatus;
74SDL_CDcaps.Play = SDL_SYS_CDPlay;
75SDL_CDcaps.Pause = SDL_SYS_CDPause;
76SDL_CDcaps.Resume = SDL_SYS_CDResume;
77SDL_CDcaps.Stop = SDL_SYS_CDStop;
78SDL_CDcaps.Eject = SDL_SYS_CDEject;
79SDL_CDcaps.Close = SDL_SYS_CDClose;
80
81/* Get the number of CD ROMs in the System */
82/* Clean SysInfo structure */
83SDL_memset(&msp, 0x00, sizeof(MCI_SYSINFO_PARMS));
84/* Prepare structure to Ask Numer of Audio CDs */
85msp.usDeviceType = MCI_DEVTYPE_CD_AUDIO;	/* CD Audio Type */
86msp.pszReturn = (PSZ)&SysInfoRet; 	/* Return Structure */
87msp.ulRetSize = MCI_CMDRETBUFSIZE; 	/* Size of ret struct */
88if (LOUSHORT(mciSendCommand(0,MCI_SYSINFO, MCI_SYSINFO_QUANTITY | MCI_WAIT, (PVOID)&msp, 0)) != MCIERR_SUCCESS) return(CD_ERROR);
89SDL_numcds = atoi(SysInfoRet);
90if (SDL_numcds > MAX_DRIVES) SDL_numcds = MAX_DRIVES; /* Limit maximum CD number */
91
92/* Get and Add their system name to the SDL_cdlist */
93msp.pszReturn = (PSZ)&SysInfoRet; 				/* Return Structure */
94msp.ulRetSize = MCI_CMDRETBUFSIZE; 			/* Size of ret struct */
95msp.usDeviceType = MCI_DEVTYPE_CD_AUDIO;		/* CD Audio Type */
96for (i=0; i<SDL_numcds; i++)
97	{
98	msp.ulNumber = i+1;
99	mciSendCommand(0,MCI_SYSINFO, MCI_SYSINFO_NAME | MCI_WAIT,&msp, 0);
100	SDL_cdlist[i] = SDL_strdup(SysInfoRet);
101	if ( SDL_cdlist[i] == NULL )
102		{
103		SDL_OutOfMemory();
104		return(-1);
105		}
106	}
107return(0);
108}
109
110/* Return CDAudio System Dependent Device Name - Ready for MCI*/
111static const char *SDL_SYS_CDName(int drive)
112{
113return(SDL_cdlist[drive]);
114}
115
116/* Open CDAudio Device - Ready for MCI */
117static int SDL_SYS_CDOpen(int drive)
118{
119MCI_OPEN_PARMS	mop;
120MCI_SET_PARMS msp;
121MCI_GENERIC_PARMS mgp;
122
123/* Open the device */
124mop.hwndCallback = (HWND)NULL;		// None
125mop.usDeviceID = (USHORT)NULL;		// Will be returned.
126mop.pszDeviceType = (PSZ)SDL_cdlist[drive];		// CDAudio Device
127if (LOUSHORT(mciSendCommand(0,MCI_OPEN,MCI_WAIT,&mop, 0)) != MCIERR_SUCCESS) return(CD_ERROR);
128/* Set time format */
129msp.hwndCallback = (HWND)NULL;		// None
130msp.ulTimeFormat = MCI_FORMAT_MSF;	// Minute : Second : Frame structure
131msp.ulSpeedFormat = (ULONG)NULL;		// No change
132msp.ulAudio = (ULONG)NULL;				// No Channel
133msp.ulLevel = (ULONG)NULL;				// No Volume
134msp.ulOver = (ULONG)NULL;				// No Delay
135msp.ulItem = (ULONG)NULL;				// No item
136msp.ulValue = (ULONG)NULL;				// No value for item flag
137if (LOUSHORT(mciSendCommand(mop.usDeviceID,MCI_SET,MCI_WAIT | MCI_SET_TIME_FORMAT,&msp, 0)) == MCIERR_SUCCESS) return (mop.usDeviceID);
138/* Error setting time format? - Close opened device */
139mgp.hwndCallback = (HWND)NULL;		// None
140mciSendCommand(mop.usDeviceID,MCI_CLOSE,MCI_WAIT,&mgp, 0);
141return(CD_ERROR);
142}
143
144/* Get CD Table Of Contents - Ready for MCI */
145static int SDL_SYS_CDGetTOC(SDL_CD *cdrom)
146{
147MCI_TOC_PARMS mtp;
148MCI_STATUS_PARMS msp;
149MCI_TOC_REC * mtr;
150INT i;
151
152/* Correction because MCI cannot read TOC while CD is playing (it'll stop!) */
153if (cdrom->status == CD_PLAYING || cdrom->status == CD_PAUSED) return 0;
154
155/* Get Number of Tracks */
156msp.hwndCallback = (HWND)NULL; /* None */
157msp.ulReturn = (ULONG)NULL; /* We want this information */
158msp.ulItem = MCI_STATUS_NUMBER_OF_TRACKS;
159msp.ulValue = (ULONG)NULL; /* No additional information */
160if (LOUSHORT(mciSendCommand(cdrom->id,MCI_STATUS,MCI_WAIT | MCI_STATUS_ITEM,&msp, 0)) != MCIERR_SUCCESS) return(CD_ERROR);
161cdrom->numtracks = msp.ulReturn;
162if ( cdrom->numtracks > SDL_MAX_TRACKS )
163	{
164	cdrom->numtracks = SDL_MAX_TRACKS;
165	}
166/* Alocate space for TOC data */
167mtr = (MCI_TOC_REC *)SDL_malloc(cdrom->numtracks*sizeof(MCI_TOC_REC));
168if ( mtr == NULL )
169	{
170	SDL_OutOfMemory();
171	return(-1);
172	}
173/* Get TOC from CD */
174mtp.pBuf = mtr;
175mtp.ulBufSize = cdrom->numtracks*sizeof(MCI_TOC_REC);
176if (LOUSHORT(mciSendCommand(cdrom->id,MCI_GETTOC,MCI_WAIT,&mtp, 0)) != MCIERR_SUCCESS)
177	{
178	SDL_OutOfMemory();
179	SDL_free(mtr);
180	return(CD_ERROR);
181	}
182/* Fill SDL Tracks Structure */
183for (i=0; i<cdrom->numtracks; i++)
184	{
185	/* Set Track ID */
186	cdrom->track[i].id = (mtr+i)->TrackNum;
187	/* Set Track Type */
188	msp.hwndCallback = (HWND)NULL; /* None */
189	msp.ulReturn = (ULONG)NULL; /* We want this information */
190	msp.ulItem = MCI_CD_STATUS_TRACK_TYPE;
191	msp.ulValue = (ULONG)((mtr+i)->TrackNum); /* Track Number? */
192	if (LOUSHORT(mciSendCommand(cdrom->id,MCI_STATUS,MCI_WAIT | MCI_TRACK | MCI_STATUS_ITEM,&msp, 0)) != MCIERR_SUCCESS)
193		{
194		SDL_free(mtr);
195		return (CD_ERROR);
196		}
197	if (msp.ulReturn==MCI_CD_TRACK_AUDIO) cdrom->track[i].type = SDL_AUDIO_TRACK;
198	else cdrom->track[i].type = SDL_DATA_TRACK;
199	/* Set Track Length - values from MCI are in MMTIMEs - 3000 MMTIME = 1 second */
200	cdrom->track[i].length = FRAMESFROMMM((mtr+i)->ulEndAddr - (mtr+i)->ulStartAddr);
201	/* Set Track Offset */
202	cdrom->track[i].offset = FRAMESFROMMM((mtr+i)->ulStartAddr);
203	}
204SDL_free(mtr);
205return(0);
206}
207
208
209/* Get CD-ROM status - Ready for MCI */
210static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position)
211{
212CDstatus status;
213MCI_STATUS_PARMS msp;
214
215/* Get Status from MCI */
216msp.hwndCallback = (HWND)NULL; /* None */
217msp.ulReturn = (ULONG)NULL; /* We want this information */
218msp.ulItem = MCI_STATUS_MODE;
219msp.ulValue = (ULONG)NULL; /* No additional information */
220if (LOUSHORT(mciSendCommand(cdrom->id,MCI_STATUS,MCI_WAIT | MCI_STATUS_ITEM,&msp, 0)) != MCIERR_SUCCESS) status = CD_ERROR;
221else
222	{
223	switch(msp.ulReturn)
224		{
225		case	MCI_MODE_NOT_READY:
226			status = CD_TRAYEMPTY;
227			break;
228		case	MCI_MODE_PAUSE:
229			status = CD_PAUSED;
230			break;
231		case	MCI_MODE_PLAY:
232			status = CD_PLAYING;
233			break;
234		case	MCI_MODE_STOP:
235			status = CD_STOPPED;
236			break;
237		/* These cases should not occour */
238		case	MCI_MODE_RECORD:
239		case	MCI_MODE_SEEK:
240		default:
241			status = CD_ERROR;
242			break;
243		}
244	}
245
246/* Determine position */
247if (position != NULL) /* The SDL $&$&%# CDROM call sends NULL pointer here! */
248	{
249		if ((status == CD_PLAYING) || (status == CD_PAUSED))
250		{
251		/* Get Position */
252		msp.hwndCallback = (HWND)NULL; /* None */
253		msp.ulReturn = (ULONG)NULL; /* We want this information */
254		msp.ulItem = MCI_STATUS_POSITION;
255		msp.ulValue = (ULONG)NULL; /* No additiona info */
256		if (LOUSHORT(mciSendCommand(cdrom->id,MCI_STATUS,MCI_WAIT | MCI_STATUS_ITEM,&msp, 0)) != MCIERR_SUCCESS) return (CD_ERROR);
257		/* Convert from MSF (format selected in the Open process) to Frames (format that will be returned) */
258		*position = MSF_TO_FRAMES(MSF_MINUTE(msp.ulReturn),MSF_SECOND(msp.ulReturn),MSF_FRAME(msp.ulReturn));
259		}
260	else *position = 0;
261	}
262return(status);
263}
264
265/* Start play - Ready for MCI */
266static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length)
267{
268MCI_GENERIC_PARMS mgp;
269MCI_STATUS_PARMS msp;
270MCI_PLAY_PARMS	mpp;
271ULONG min,sec,frm;
272
273/* Start MSF */
274FRAMES_TO_MSF(start, &min, &sec, &frm);
275MSF_MINUTE(mpp.ulFrom) = min;
276MSF_SECOND(mpp.ulFrom) = sec;
277MSF_FRAME(mpp.ulFrom) = frm;
278/* End MSF */
279FRAMES_TO_MSF(start+length, &min, &sec, &frm);
280MSF_MINUTE(mpp.ulTo) = min;
281MSF_SECOND(mpp.ulTo) = sec;
282MSF_FRAME(mpp.ulTo) = frm;
283#ifdef DEBUG_CDROM
284	fprintf(stderr, "Trying to play from %d:%d:%d to %d:%d:%d\n",
285	playtime.cdmsf_min0, playtime.cdmsf_sec0, playtime.cdmsf_frame0,
286	playtime.cdmsf_min1, playtime.cdmsf_sec1, playtime.cdmsf_frame1);
287#endif
288/* Verifies if it is paused first... and if it is, unpause before stopping it. */
289msp.hwndCallback = (HWND)NULL; /* None */
290msp.ulReturn = (ULONG)NULL; /* We want this information */
291msp.ulItem = MCI_STATUS_MODE;
292msp.ulValue = (ULONG)NULL; /* No additional information */
293if (LOUSHORT(mciSendCommand(cdrom->id,MCI_STATUS,MCI_WAIT | MCI_STATUS_ITEM,&msp, 0)) == MCIERR_SUCCESS)
294	{
295	if (msp.ulReturn == MCI_MODE_PAUSE)
296		{
297		mgp.hwndCallback = (HWND)NULL;		// None
298		mciSendCommand(cdrom->id,MCI_RESUME,0,&mgp, 0);
299		}
300	}
301/* Now play it. */
302mpp.hwndCallback = (HWND)NULL;		// We do not want the info. temp
303if (LOUSHORT(mciSendCommand(cdrom->id,MCI_PLAY,MCI_FROM | MCI_TO,&mpp, 0)) == MCIERR_SUCCESS) return 0;
304return (CD_ERROR);
305}
306
307/* Pause play - Ready for MCI */
308static int SDL_SYS_CDPause(SDL_CD *cdrom)
309{
310MCI_GENERIC_PARMS mgp;
311
312mgp.hwndCallback = (HWND)NULL;		// None
313if (LOUSHORT(mciSendCommand(cdrom->id,MCI_PAUSE,MCI_WAIT,&mgp, 0)) == MCIERR_SUCCESS) return 0;
314return(CD_ERROR);
315}
316
317/* Resume play - Ready for MCI */
318static int SDL_SYS_CDResume(SDL_CD *cdrom)
319{
320MCI_GENERIC_PARMS mgp;
321
322mgp.hwndCallback = (HWND)NULL;		// None
323if (LOUSHORT(mciSendCommand(cdrom->id,MCI_RESUME,MCI_WAIT,&mgp, 0)) == MCIERR_SUCCESS) return 0;
324return(CD_ERROR);
325}
326
327/* Stop play - Ready for MCI */
328static int SDL_SYS_CDStop(SDL_CD *cdrom)
329{
330MCI_GENERIC_PARMS mgp;
331MCI_STATUS_PARMS msp;
332
333/* Verifies if it is paused first... and if it is, unpause before stopping it. */
334msp.hwndCallback = (HWND)NULL; /* None */
335msp.ulReturn = (ULONG)NULL; /* We want this information */
336msp.ulItem = MCI_STATUS_MODE;
337msp.ulValue = (ULONG)NULL; /* No additional information */
338if (LOUSHORT(mciSendCommand(cdrom->id,MCI_STATUS,MCI_WAIT | MCI_STATUS_ITEM,&msp, 0)) == MCIERR_SUCCESS)
339	{
340	if (msp.ulReturn == MCI_MODE_PAUSE)
341		{
342		mgp.hwndCallback = (HWND)NULL;		// None
343		mciSendCommand(cdrom->id,MCI_RESUME,0,&mgp, 0);
344		}
345	}
346/* Now stops the media */
347mgp.hwndCallback = (HWND)NULL;		// None
348if (LOUSHORT(mciSendCommand(cdrom->id,MCI_STOP,MCI_WAIT,&mgp, 0)) == MCIERR_SUCCESS) return 0;
349return(CD_ERROR);
350}
351
352/* Eject the CD-ROM - Ready for MCI */
353static int SDL_SYS_CDEject(SDL_CD *cdrom)
354{
355MCI_SET_PARMS msp;
356
357msp.hwndCallback = (HWND)NULL;		// None
358msp.ulTimeFormat = (ULONG)NULL;		// No change
359msp.ulSpeedFormat = (ULONG)NULL;		// No change
360msp.ulAudio = (ULONG)NULL;				// No Channel
361msp.ulLevel = (ULONG)NULL;				// No Volume
362msp.ulOver = (ULONG)NULL;				// No Delay
363msp.ulItem = (ULONG)NULL;					// No item
364msp.ulValue = (ULONG)NULL;					// No value for item flag
365if (LOUSHORT(mciSendCommand(cdrom->id,MCI_SET,MCI_WAIT | MCI_SET_DOOR_OPEN,&msp, 0)) == MCIERR_SUCCESS) return 0;
366return(CD_ERROR);
367}
368
369/* Close the CD-ROM handle - Ready for MCI */
370static void SDL_SYS_CDClose(SDL_CD *cdrom)
371{
372MCI_GENERIC_PARMS mgp;
373
374mgp.hwndCallback = (HWND)NULL;		// None
375mciSendCommand(cdrom->id,MCI_CLOSE,MCI_WAIT,&mgp, 0);
376}
377
378/* Finalize CDROM Subsystem - Ready for MCI */
379void SDL_SYS_CDQuit(void)
380{
381int i;
382
383if ( SDL_numcds > 0 )
384	{
385	for ( i=0; i<SDL_numcds; ++i )
386		{
387		SDL_free(SDL_cdlist[i]);
388		}
389	SDL_numcds = 0;
390	}
391}
392
393#endif /* SDL_CDROM_OS2 */
394