Release 960712
[wine] / multimedia / mcistring.c
1 /*
2  * MCI stringinterface
3  *
4  * Copyright 1995 Marcus Meissner
5  */
6 /* FIXME: special commands of device drivers should be handled by those drivers
7  */
8
9 #ifndef WINELIB
10
11 #include <unistd.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <fcntl.h>
16 #include <sys/ioctl.h>
17 #include "windows.h"
18 #include "ldt.h"
19 #include "callback.h"
20 #include "user.h"
21 #include "driver.h"
22 #include "mmsystem.h"
23 #include "stddebug.h"
24 #include "debug.h"
25 #include "xmalloc.h"
26
27
28 extern MCI_OPEN_DRIVER_PARMS    mciDrv[MAXMCIDRIVERS];
29
30 /* FIXME: I need to remember the aliasname of a spec. driver. 
31  *        and this is the easiest way. *sigh*
32  */
33 extern MCI_OPEN_PARMS           mciOpenDrv[MAXMCIDRIVERS];
34
35 LONG DrvDefDriverProc(DWORD dwDevID, HDRVR hDriv, WORD wMsg, 
36                       DWORD dwParam1, DWORD dwParam2);
37
38 LONG WAVE_DriverProc(DWORD dwDevID, HDRVR hDriv, WORD wMsg, 
39                      DWORD dwParam1, DWORD dwParam2);
40 LONG MIDI_DriverProc(DWORD dwDevID, HDRVR hDriv, WORD wMsg, 
41                      DWORD dwParam1, DWORD dwParam2);
42 LONG CDAUDIO_DriverProc(DWORD dwDevID, HDRVR hDriv, WORD wMsg, 
43                         DWORD dwParam1, DWORD dwParam2);
44 LONG ANIM_DriverProc(DWORD dwDevID, HDRVR hDriv, WORD wMsg, 
45                      DWORD dwParam1, DWORD dwParam2);
46
47 /* The reason why I just don't lowercase the keywords array in 
48  * mciSendString is left as an exercise to the reader.
49  */
50 #define STRCMP(x,y) lstrcmpi32A(x,y)
51
52 /* standard functionparameters for all functions */
53 #define _MCISTR_PROTO_ \
54         WORD wDevID,WORD uDevTyp,LPSTR lpstrReturnString,UINT uReturnLength,\
55         LPCSTR dev,LPSTR *keywords,UINT nrofkeywords,DWORD dwFlags
56
57 /* copy string to return pointer including necessary checks 
58  * for use in mciSendString()
59  */
60 #define _MCI_STR(s) do {\
61         dprintf_mci(stddeb,"->returns \"%s\"",s);\
62         if (lpstrReturnString) {\
63             lstrcpyn32A(lpstrReturnString,s,uReturnLength);\
64             dprintf_mci(stddeb,"-->\"%s\"\n",lpstrReturnString);\
65         }\
66 } while(0)
67
68 /* calling DriverProc. We need to pass the struct as SEGMENTED POINTER. */
69 #define _MCI_CALL_DRIVER(cmd,params) \
70         switch(uDevTyp) {\
71         case MCI_DEVTYPE_CD_AUDIO:\
72                 res=CDAUDIO_DriverProc(mciDrv[wDevID].wDeviceID,0,cmd,dwFlags, (DWORD)(params));\
73                 break;\
74         case MCI_DEVTYPE_WAVEFORM_AUDIO:\
75                 res=WAVE_DriverProc(mciDrv[wDevID].wDeviceID,0,cmd,dwFlags,(DWORD)(params));\
76                 break;\
77         case MCI_DEVTYPE_SEQUENCER:\
78                 res=MIDI_DriverProc(mciDrv[wDevID].wDeviceID,0,cmd,dwFlags,(DWORD)(params));\
79                 break;\
80         case MCI_DEVTYPE_ANIMATION:\
81                 res=ANIM_DriverProc(mciDrv[wDevID].wDeviceID,0,cmd,dwFlags,(DWORD)(params));\
82                 break;\
83         case MCI_DEVTYPE_DIGITAL_VIDEO:\
84                 dprintf_mci(stddeb,"_MCI_CALL_DRIVER //No DIGITAL_VIDEO yet !\n");\
85                 res=MCIERR_DEVICE_NOT_INSTALLED;\
86                 break;\
87         default:\
88                 dprintf_mci(stddeb,"_MCI_CALL_DRIVER //Invalid Device Name '%s' !\n",dev);\
89                 res=MCIERR_INVALID_DEVICE_NAME;\
90                 break;\
91         }
92 /* we need to have strings in 16 bit space for some things 
93  * FIXME: this is bad.
94  */
95 #define _MCI_STRDUP_TO_SEG(dest,source) {\
96         HANDLE  x;\
97         x=USER_HEAP_ALLOC(strlen(source));\
98         dest=(LPSTR)MAKELONG(x,USER_HeapSel);\
99         strcpy(PTR_SEG_TO_LIN(dest),source);\
100 }
101
102 /* print a DWORD in the specified timeformat */
103 static void
104 _MCISTR_printtf(char *buf,UINT uDevType,DWORD timef,DWORD val) {
105         *buf='\0';
106         switch (timef) {
107         case MCI_FORMAT_MILLISECONDS:
108         case MCI_FORMAT_FRAMES:
109         case MCI_FORMAT_BYTES:
110         case MCI_FORMAT_SAMPLES:
111         case MCI_VD_FORMAT_TRACK:
112         /*case MCI_SEQ_FORMAT_SONGPTR: sameas MCI_VD_FORMAT_TRACK */
113                 sprintf(buf,"%ld",val);
114                 break;
115         case MCI_FORMAT_HMS:
116                 /* well, the macros have the same content*/
117                 /*FALLTRHOUGH*/
118         case MCI_FORMAT_MSF:
119                 sprintf(buf,"%d:%d:%d",
120                         MCI_HMS_HOUR(val),
121                         MCI_HMS_MINUTE(val),
122                         MCI_HMS_SECOND(val)
123                 );
124                 break;
125         case MCI_FORMAT_TMSF:
126                 sprintf(buf,"%d:%d:%d:%d",
127                         MCI_TMSF_TRACK(val),
128                         MCI_TMSF_MINUTE(val),
129                         MCI_TMSF_SECOND(val),
130                         MCI_TMSF_FRAME(val)
131                 );
132                 break;
133         default:
134                 fprintf(stdnimp,__FILE__":MCISTR_Status:missing timeformat for %ld, report.\n",timef);
135                 strcpy(buf,"0"); /* hmm */
136                 break;
137         }
138         return;
139 }
140 /* possible different return types */
141 #define _MCISTR_int     1
142 #define _MCISTR_time    2
143 #define _MCISTR_bool    3
144 #define _MCISTR_tfname  4
145 #define _MCISTR_mode    5
146 #define _MCISTR_divtype 6
147 #define _MCISTR_seqtype 7
148 #define _MCISTR_vdmtype 8
149 #define _MCISTR_devtype 9
150
151 static void
152 _MCISTR_convreturn(int type,DWORD dwReturn,LPSTR lpstrReturnString,
153         WORD uReturnLength,WORD uDevTyp,int timef
154 ) {
155         switch (type) {
156         case _MCISTR_vdmtype:
157                 switch (dwReturn) {
158                 case MCI_VD_MEDIA_CLV:_MCI_STR("CLV");break;
159                 case MCI_VD_MEDIA_CAV:_MCI_STR("CAV");break;
160                 default:
161                 case MCI_VD_MEDIA_OTHER:_MCI_STR("other");break;
162                 }
163                 break;
164         case _MCISTR_seqtype:
165                 switch (dwReturn) {
166                 case MCI_SEQ_NONE:_MCI_STR("none");break;
167                 case MCI_SEQ_SMPTE:_MCI_STR("smpte");break;
168                 case MCI_SEQ_FILE:_MCI_STR("file");break;
169                 case MCI_SEQ_MIDI:_MCI_STR("midi");break;
170                 default:fprintf(stdnimp,__FILE__":MCISTR_Status:missing sequencer mode %ld\n",dwReturn);
171                 }
172                 break;
173         case _MCISTR_mode:
174                 switch (dwReturn) {
175                 case MCI_MODE_NOT_READY:_MCI_STR("not ready");break;
176                 case MCI_MODE_STOP:_MCI_STR("stopped");break;
177                 case MCI_MODE_PLAY:_MCI_STR("playing");break;
178                 case MCI_MODE_RECORD:_MCI_STR("recording");break;
179                 case MCI_MODE_SEEK:_MCI_STR("seeking");break;
180                 case MCI_MODE_PAUSE:_MCI_STR("paused");break;
181                 case MCI_MODE_OPEN:_MCI_STR("open");break;
182                 default:break;
183                 }
184                 break;
185         case _MCISTR_bool:
186                 if (dwReturn)
187                         _MCI_STR("true");
188                 else
189                         _MCI_STR("false");
190                 break;
191         case _MCISTR_int:{
192                 char    buf[16];
193                 sprintf(buf,"%ld",dwReturn);
194                 _MCI_STR(buf);
195                 break;
196         }
197         case _MCISTR_time: {    
198                 char    buf[100];
199                 _MCISTR_printtf(buf,uDevTyp,timef,dwReturn);
200                 _MCI_STR(buf);
201                 break;
202         }
203         case _MCISTR_tfname:
204                 switch (timef) {
205                 case MCI_FORMAT_MILLISECONDS:_MCI_STR("milliseconds");break;
206                 case MCI_FORMAT_FRAMES:_MCI_STR("frames");break;
207                 case MCI_FORMAT_BYTES:_MCI_STR("bytes");break;
208                 case MCI_FORMAT_SAMPLES:_MCI_STR("samples");break;
209                 case MCI_FORMAT_HMS:_MCI_STR("hms");break;
210                 case MCI_FORMAT_MSF:_MCI_STR("msf");break;
211                 case MCI_FORMAT_TMSF:_MCI_STR("tmsf");break;
212                 default:
213                         fprintf(stdnimp,__FILE__":MCISTR_Status:missing timeformat for %d, report.\n",timef);
214                         break;
215                 }
216                 break;
217         case _MCISTR_divtype:
218                 switch (dwReturn) {
219                 case MCI_SEQ_DIV_PPQN:_MCI_STR("PPQN");break;
220                 case MCI_SEQ_DIV_SMPTE_24:_MCI_STR("SMPTE 24 frame");break;
221                 case MCI_SEQ_DIV_SMPTE_25:_MCI_STR("SMPTE 25 frame");break;
222                 case MCI_SEQ_DIV_SMPTE_30:_MCI_STR("SMPTE 30 frame");break;
223                 case MCI_SEQ_DIV_SMPTE_30DROP:_MCI_STR("SMPTE 30 frame drop");break;
224                 }
225         case _MCISTR_devtype:
226                 switch (dwReturn) {
227                 case MCI_DEVTYPE_VCR:_MCI_STR("vcr");break;
228                 case MCI_DEVTYPE_VIDEODISC:_MCI_STR("videodisc");break;
229                 case MCI_DEVTYPE_CD_AUDIO:_MCI_STR("cd audio");break;
230                 case MCI_DEVTYPE_OVERLAY:_MCI_STR("overlay");break;
231                 case MCI_DEVTYPE_DAT:_MCI_STR("dat");break;
232                 case MCI_DEVTYPE_SCANNER:_MCI_STR("scanner");break;
233                 case MCI_DEVTYPE_ANIMATION:_MCI_STR("animation");break;
234                 case MCI_DEVTYPE_DIGITAL_VIDEO:_MCI_STR("digital video");break;
235                 case MCI_DEVTYPE_OTHER:_MCI_STR("other");break;
236                 case MCI_DEVTYPE_WAVEFORM_AUDIO:_MCI_STR("waveform audio");break;
237                 case MCI_DEVTYPE_SEQUENCER:_MCI_STR("sequencer");break;
238                 default:fprintf(stdnimp,__FILE__":_MCISTR_convreturn:unknown device type %ld, report.\n",dwReturn);break;
239                 }
240                 break;
241         default:
242                 fprintf(stdnimp,__FILE__":_MCISTR_convreturn:unknown resulttype %d, report.\n",type);
243                 break;
244         }
245 }
246
247 #define FLAG1(str,flag) \
248         if (!STRCMP(keywords[i],str)) {\
249                 dwFlags |= flag;\
250                 i++;\
251                 continue;\
252         }
253 #define FLAG2(str1,str2,flag) \
254         if (!STRCMP(keywords[i],str1) && (i+1<nrofkeywords) && !STRCMP(keywords[i+1],str2)) {\
255                 dwFlags |= flag;\
256                 i+=2;\
257                 continue;\
258         }
259
260 /* All known subcommands are implemented in single functions to avoid
261  * bloat and a xxxx lines long mciSendString(). All commands are of the
262  * format MCISTR_Cmd(_MCISTR_PROTO_) where _MCISTR_PROTO_ is the above
263  * defined line of arguments. (This is just for easy enhanceability.)
264  * All functions return the MCIERR_ errorvalue as DWORD. Returnvalues
265  * for the calls are in lpstrReturnString (If I mention return values
266  * in function headers, I mean returnvalues in lpstrReturnString.)
267  * Integers are sprintf("%d")ed integers. Boolean values are 
268  * "true" and "false". 
269  * timeformat depending values are "%d" "%d:%d" "%d:%d:%d" "%d:%d:%d:%d"
270  * FIXME: is above line correct?
271  *
272  * Preceding every function is a list of implemented/known arguments.
273  * Feel free to add missing arguments.
274  *
275  */
276
277 /*
278  * Opens the specified MCI driver. 
279  * Arguments: <name> 
280  * Optional:
281  *      "shareable"
282  *      "alias <aliasname>"
283  *      "element <elementname>"
284  * Additional:
285  * waveform audio:
286  *      "buffer <nrBytesPerSec>"
287  * Animation:
288  *      "nostatic"      increaste nr of nonstatic colours
289  *      "parent <windowhandle>"
290  *      "style <mask>"  bitmask of WS_xxxxx (see windows.h)
291  *      "style child"   WS_CHILD
292  *      "style overlap" WS_OVERLAPPED
293  *      "style popup"   WS_POPUP
294  * Overlay:
295  *      "parent <windowhandle>"
296  *      "style <mask>"  bitmask of WS_xxxxx (see windows.h)
297  *      "style child"   WS_CHILD
298  *      "style overlap" WS_OVERLAPPED
299  *      "style popup"   WS_POPUP
300  * Returns nothing.
301  */
302 static DWORD
303 MCISTR_Open(_MCISTR_PROTO_) {
304         int             res,i;
305         char            *s;
306         union {
307                 MCI_OPEN_PARMS  openParams;
308                 MCI_WAVE_OPEN_PARMS     waveopenParams;
309                 MCI_ANIM_OPEN_PARMS     animopenParams;
310                 MCI_OVLY_OPEN_PARMS     ovlyopenParams;
311         } U;
312
313         U.openParams.lpstrElementName = NULL;
314         s=strchr(dev,'!');
315         if (s!=NULL) {
316                 *s++='\0';
317                 _MCI_STRDUP_TO_SEG(U.openParams.lpstrElementName,s);
318         }
319         if (!STRCMP(dev,"cdaudio")) {
320                 uDevTyp=MCI_DEVTYPE_CD_AUDIO;
321         } else if (!STRCMP(dev,"waveaudio")) {
322                 uDevTyp=MCI_DEVTYPE_WAVEFORM_AUDIO;
323         } else if (!STRCMP(dev,"sequencer")) {
324                 uDevTyp=MCI_DEVTYPE_SEQUENCER;
325         } else if (!STRCMP(dev,"animation1")) {
326                 uDevTyp=MCI_DEVTYPE_ANIMATION;
327         } else if (!STRCMP(dev,"avivideo")) {
328                 uDevTyp=MCI_DEVTYPE_DIGITAL_VIDEO;
329         } else {
330                 return MCIERR_INVALID_DEVICE_NAME;
331         }
332         wDevID=0;
333         while(mciDrv[wDevID].wType) {
334                 if (++wDevID>=MAXMCIDRIVERS) {
335                         dprintf_mci(stddeb, __FILE__":MCISTR_Open:MAXMCIDRIVERS reached!\n");
336                         return MCIERR_INTERNAL;
337                 }
338         }
339         mciDrv[wDevID].wType            = uDevTyp;
340         mciDrv[wDevID].wDeviceID        = wDevID;
341         U.openParams.dwCallback         = 0;
342         U.openParams.wDeviceID          = wDevID;
343         U.ovlyopenParams.dwStyle        = 0; 
344         U.animopenParams.dwStyle        = 0; 
345
346         _MCI_STRDUP_TO_SEG(U.openParams.lpstrDeviceType,dev);
347         U.openParams.lpstrAlias         = NULL;
348         dwFlags |= MCI_OPEN_TYPE;
349         i=0;
350         while (i<nrofkeywords) {
351                 FLAG1("shareable",MCI_OPEN_SHAREABLE);
352                 if (!strcmp(keywords[i],"alias") && (i+1<nrofkeywords)) {
353                         dwFlags |= MCI_OPEN_ALIAS;
354                         _MCI_STRDUP_TO_SEG(U.openParams.lpstrAlias,keywords[i]);
355                         i+=2;
356                         continue;
357                 }
358                 if (!strcmp(keywords[i],"element") && (i+1<nrofkeywords)) {
359                         dwFlags |= MCI_OPEN_ELEMENT;
360                         _MCI_STRDUP_TO_SEG(U.openParams.lpstrElementName,keywords[i]);
361                         i+=2;
362                         continue;
363                 }
364                 switch (uDevTyp) {
365                 case MCI_DEVTYPE_ANIMATION:
366                 case MCI_DEVTYPE_DIGITAL_VIDEO:
367                         FLAG1("nostatic",MCI_ANIM_OPEN_NOSTATIC);
368                         if (!STRCMP(keywords[i],"parent") && (i+1<nrofkeywords)) {
369                                 dwFlags |= MCI_ANIM_OPEN_PARENT;
370                                 sscanf(keywords[i+1],"%hu",&(U.animopenParams.hWndParent));
371                                 i+=2;
372                                 continue;
373                         }
374                         if (!STRCMP(keywords[i],"style") && (i+1<nrofkeywords)) {
375                                 DWORD   st;
376
377                                 dwFlags |= MCI_ANIM_OPEN_WS;
378                                 if (!STRCMP(keywords[i+1],"popup")) {
379                                         U.animopenParams.dwStyle |= WS_POPUP; 
380                                 } else if (!STRCMP(keywords[i+1],"overlap")) {
381                                         U.animopenParams.dwStyle |= WS_OVERLAPPED; 
382                                 } else if (!STRCMP(keywords[i+1],"child")) {
383                                         U.animopenParams.dwStyle |= WS_CHILD; 
384                                 } else if (sscanf(keywords[i+1],"%ld",&st)) {
385                                         U.animopenParams.dwStyle |= st; 
386                                 } else
387                                         fprintf(stdnimp,__FILE__":MCISTR_Open:unknown 'style' keyword %s, please report.\n",keywords[i+1]);
388                                 i+=2;
389                                 continue;
390                         }
391                         break;
392                 case MCI_DEVTYPE_WAVEFORM_AUDIO:
393                         if (!STRCMP(keywords[i],"buffer") && (i+1<nrofkeywords)) {
394                                 dwFlags |= MCI_WAVE_OPEN_BUFFER;
395                                 sscanf(keywords[i+1],"%ld",&(U.waveopenParams.dwBufferSeconds));
396                         }
397                         break;
398                 case MCI_DEVTYPE_OVERLAY:
399                         /* looks just like anim, but without NOSTATIC */
400                         if (!STRCMP(keywords[i],"parent") && (i+1<nrofkeywords)) {
401                                 dwFlags |= MCI_OVLY_OPEN_PARENT;
402                                 sscanf(keywords[i+1],"%hd",&(U.ovlyopenParams.hWndParent));
403                                 i+=2;
404                                 continue;
405                         }
406                         if (!STRCMP(keywords[i],"style") && (i+1<nrofkeywords)) {
407                                 DWORD   st;
408
409                                 dwFlags |= MCI_OVLY_OPEN_WS;
410                                 if (!STRCMP(keywords[i+1],"popup")) {
411                                         U.ovlyopenParams.dwStyle |= WS_POPUP; 
412                                 } else if (!STRCMP(keywords[i+1],"overlap")) {
413                                         U.ovlyopenParams.dwStyle |= WS_OVERLAPPED; 
414                                 } else if (!STRCMP(keywords[i+1],"child")) {
415                                         U.ovlyopenParams.dwStyle |= WS_CHILD; 
416                                 } else if (sscanf(keywords[i+1],"%ld",&st)) {
417                                         U.ovlyopenParams.dwStyle |= st; 
418                                 } else
419                                         fprintf(stdnimp,__FILE__":MCISTR_Open:unknown 'style' keyword %s, please report.\n",keywords[i+1]);
420                                 i+=2;
421                                 continue;
422                         }
423                         break;
424                 }
425                 fprintf(stdnimp,__FILE__":MCISTR_Open:unknown parameter passed %s, please report.\n",keywords[i]);
426                 i++;
427         }
428         _MCI_CALL_DRIVER( MCI_OPEN, MAKE_SEGPTR(&U) );
429         if (res==0)
430                 memcpy(&mciOpenDrv[wDevID],&U.openParams,sizeof(MCI_OPEN_PARMS));
431         return res;
432 }
433
434 /* A help function for a lot of others ... 
435  * for instance status/play/record/seek etc.
436  */
437 DWORD
438 _MCISTR_determine_timeformat(LPCSTR dev,WORD wDevID,WORD uDevTyp,int *timef) {
439         MCI_STATUS_PARMS        statusParams;
440         DWORD                   dwFlags;
441         int                     res;
442
443         dwFlags = MCI_STATUS_ITEM;
444         statusParams.dwItem     = MCI_STATUS_TIME_FORMAT;
445         statusParams.dwReturn   = 0;
446         _MCI_CALL_DRIVER( MCI_STATUS, MAKE_SEGPTR(&statusParams) );
447         if (res==0) *timef=statusParams.dwReturn;
448         return res;
449 }
450
451 /* query status of MCI drivers
452  * Arguments:
453  * Required: 
454  *      "mode"  - returns "not ready" "paused" "playing" "stopped" "open" 
455  *                "parked" "recording" "seeking" ....
456  * Basics:
457  *      "current track" - returns current track as integer
458  *      "length [track <nr>]"   - returns length [of track <nr>] in current 
459  *                              timeformat
460  *      "number of tracks" - returns number of tracks as integer
461  *      "position [track <nr>]" - returns position [in track <nr>] in current 
462  *                              timeformat
463  *      "ready"                 - checks if device is ready to play, -> bool
464  *      "start position"        - returns start position in timeformat
465  *      "time format"           - returns timeformat (list of possible values:
466  *                              "ms" "msf" "milliseconds" "hmsf" "tmsf" "frames"
467  *                              "bytes" "samples" "hms")
468  *      "media present"         - returns if media is present as bool
469  * Animation:
470  *      "forward"               - returns "true" if device is playing forwards
471  *      "speed"                 - returns speed for device
472  *      "palette handle"        - returns palette handle
473  *      "window handle"         - returns window handle
474  *      "stretch"               - returns stretch bool
475  * MIDI sequencer:
476  *      "division type"         - ? returns "PPQN" "SMPTE 24 frame" 
477  *                      "SMPTE 25 frame" "SMPTE 30 frame" "SMPTE 30 drop frame"
478  *      "tempo"                 - current tempo in (PPQN? speed in frames, SMPTE*? speed in hsmf)
479  *      "offset"                - offset in dito.
480  *      "port"                  - midi port as integer
481  *      "slave"                 - slave device ("midi","file","none","smpte")
482  *      "master"                - masterdevice (dito.)
483  * Overlay:
484  *      "window handle"         - see animation
485  *      "stretch"               - dito
486  * Video Disc:
487  *      "speed"                 - speed as integer
488  *      "forward"               - returns bool (when playing forward)
489  *      "side"                  - returns 1 or 2
490  *      "media type"            - returns "CAV" "CLV" "other"
491  *      "disc size"             - returns "8" or "12"
492  * WAVEFORM audio:
493  *      "input"                 - base queries on input set
494  *      "output"                - base queries on output set
495  *      "format tag"            - return integer format tag
496  *      "channels"              - return integer nr of channels
497  *      "bytespersec"           - return average nr of bytes/sec
498  *      "samplespersec"         - return nr of samples per sec
499  *      "bitspersample"         - return bitspersample
500  *      "alignment"             - return block alignment
501  *      "level"                 - return level?
502  */
503
504 #define ITEM1(str,item,xtype) \
505         if (!STRCMP(keywords[i],str)) {\
506                 statusParams.dwItem = item;\
507                 type = xtype;\
508                 i++;\
509                 continue;\
510         }
511 #define ITEM2(str1,str2,item,xtype) \
512         if (    !STRCMP(keywords[i],str1) &&\
513                 (i+1<nrofkeywords) &&\
514                 !STRCMP(keywords[i+1],str2)\
515         ) {\
516                 statusParams.dwItem = item;\
517                 type = xtype;\
518                 i+=2;\
519                 continue;\
520         }
521 #define ITEM3(str1,str2,str3,item,xtype) \
522         if (    !STRCMP(keywords[i],str1) &&\
523                 (i+2<nrofkeywords) &&\
524                 !STRCMP(keywords[i+1],str2) &&\
525                 !STRCMP(keywords[i+2],str3)\
526         ) {\
527                 statusParams.dwItem = item;\
528                 type = xtype;\
529                 i+=3;\
530                 continue;\
531         }
532 static DWORD
533 MCISTR_Status(_MCISTR_PROTO_) {
534         MCI_STATUS_PARMS        statusParams;
535         int                     type = 0,i,res,timef;
536
537         statusParams.dwCallback = 0;
538         dwFlags |= MCI_STATUS_ITEM;
539         res = _MCISTR_determine_timeformat(dev,wDevID,uDevTyp,&timef);
540         if (res) return res;
541
542         statusParams.dwReturn   = 0;
543         statusParams.dwItem     = 0;
544         i = 0;
545
546         while (i<nrofkeywords) {
547                 if (!STRCMP(keywords[i],"track") && (i+1<nrofkeywords)) {
548                         sscanf(keywords[i+1],"%ld",&(statusParams.dwTrack));
549                         dwFlags |= MCI_TRACK;
550                         i+=2;
551                         continue;
552                 }
553                 FLAG1("start",MCI_STATUS_START);
554                 /* generic things */
555                 ITEM2("current","track",MCI_STATUS_CURRENT_TRACK,_MCISTR_time);
556                 ITEM2("time","format",MCI_STATUS_TIME_FORMAT,_MCISTR_tfname);
557                 ITEM1("ready",MCI_STATUS_READY,_MCISTR_bool);
558                 ITEM1("mode",MCI_STATUS_MODE,_MCISTR_mode);
559                 ITEM3("number","of","tracks",MCI_STATUS_NUMBER_OF_TRACKS,_MCISTR_int);
560                 ITEM1("length",MCI_STATUS_LENGTH,_MCISTR_time);
561                 ITEM1("position",MCI_STATUS_POSITION,_MCISTR_time);
562                 ITEM2("media","present",MCI_STATUS_MEDIA_PRESENT,_MCISTR_bool);
563
564                 switch (uDevTyp) {
565                 case MCI_DEVTYPE_ANIMATION:
566                 case MCI_DEVTYPE_DIGITAL_VIDEO:
567                         ITEM2("palette","handle",MCI_ANIM_STATUS_HPAL,_MCISTR_int);
568                         ITEM2("window","handle",MCI_ANIM_STATUS_HWND,_MCISTR_int);
569                         ITEM1("stretch",MCI_ANIM_STATUS_STRETCH,_MCISTR_bool);
570                         ITEM1("speed",MCI_ANIM_STATUS_SPEED,_MCISTR_int);
571                         ITEM1("forward",MCI_ANIM_STATUS_FORWARD,_MCISTR_bool);
572                         break;
573                 case MCI_DEVTYPE_SEQUENCER:
574                         /* just completing the list, not working correctly */
575                         ITEM2("division","type",MCI_SEQ_STATUS_DIVTYPE,_MCISTR_divtype);
576                         /* tempo ... PPQN in frames/second, SMPTE in hmsf */
577                         ITEM1("tempo",MCI_SEQ_STATUS_TEMPO,_MCISTR_int);
578                         ITEM1("port",MCI_SEQ_STATUS_PORT,_MCISTR_int);
579                         ITEM1("slave",MCI_SEQ_STATUS_SLAVE,_MCISTR_seqtype);
580                         ITEM1("master",MCI_SEQ_STATUS_SLAVE,_MCISTR_seqtype);
581                         /* offset ... PPQN in frames/second, SMPTE in hmsf */
582                         ITEM1("offset",MCI_SEQ_STATUS_SLAVE,_MCISTR_time);
583                         break;
584                 case MCI_DEVTYPE_OVERLAY:
585                         ITEM2("window","handle",MCI_OVLY_STATUS_HWND,_MCISTR_int);
586                         ITEM1("stretch",MCI_OVLY_STATUS_STRETCH,_MCISTR_bool);
587                         break;
588                 case MCI_DEVTYPE_VIDEODISC:
589                         ITEM1("speed",MCI_VD_STATUS_SPEED,_MCISTR_int);
590                         ITEM1("forward",MCI_VD_STATUS_FORWARD,_MCISTR_bool);
591                         ITEM1("side",MCI_VD_STATUS_SIDE,_MCISTR_int);
592                         ITEM2("media","type",MCI_VD_STATUS_SIDE,_MCISTR_vdmtype);
593                         /* returns 8 or 12 */
594                         ITEM2("disc","size",MCI_VD_STATUS_DISC_SIZE,_MCISTR_int);
595                         break;
596                 case MCI_DEVTYPE_WAVEFORM_AUDIO:
597                         /* I am not quite sure if foll. 2 lines are right. */
598                         FLAG1("input",MCI_WAVE_INPUT);
599                         FLAG1("output",MCI_WAVE_OUTPUT);
600
601                         ITEM2("format","tag",MCI_WAVE_STATUS_FORMATTAG,_MCISTR_int);
602                         ITEM1("channels",MCI_WAVE_STATUS_CHANNELS,_MCISTR_int);
603                         ITEM1("bytespersec",MCI_WAVE_STATUS_AVGBYTESPERSEC,_MCISTR_int);
604                         ITEM1("samplespersec",MCI_WAVE_STATUS_SAMPLESPERSEC,_MCISTR_int);
605                         ITEM1("bitspersample",MCI_WAVE_STATUS_BITSPERSAMPLE,_MCISTR_int);
606                         ITEM1("alignment",MCI_WAVE_STATUS_BLOCKALIGN,_MCISTR_int);
607                         ITEM1("level",MCI_WAVE_STATUS_LEVEL,_MCISTR_int);
608                         break;
609                 }
610                 fprintf(stdnimp,__FILE__":MCISTR_Status:unknown keyword '%s'\n",keywords[i]);
611                 i++;
612         }
613         if (!statusParams.dwItem) 
614                 return MCIERR_MISSING_STRING_ARGUMENT;
615
616         _MCI_CALL_DRIVER( MCI_STATUS, MAKE_SEGPTR(&statusParams) );
617         if (res==0)
618                 _MCISTR_convreturn(type,statusParams.dwReturn,lpstrReturnString,uReturnLength,uDevTyp,timef);
619         return res;
620 }
621 #undef ITEM1
622 #undef ITEM2
623 #undef ITEM3
624
625 /* set specified parameters in respective MCI drivers
626  * Arguments:
627  *      "door open"     eject media or somesuch
628  *      "door close"    load media
629  *      "time format <timeformatname>"  "ms" "milliseconds" "msf" "hmsf" 
630  *                                      "tmsf" "SMPTE 24" "SMPTE 25" "SMPTE 30"
631  *                                      "SMPTE drop 30"
632  *      "audio [all|left|right] [on|off]" sets specified audiochannel on or off
633  *      "video [on|off]"                sets video on/off
634  * Waveform audio:
635  *      "formattag pcm"         sets format to pcm
636  *      "formattag <nr>"        sets integer formattag value
637  *      "any input"             accept input from any known source
638  *      "any output"            output to any known destination
639  *      "input <nr>"            input from source <nr>
640  *      "output <nr>"           output to destination <nr>
641  *      "channels <nr>"         sets nr of channels 
642  *      "bytespersec <nr>"      sets average bytes per second
643  *      "samplespersec <nr>"    sets average samples per second (1 sample can
644  *                              be 2 bytes!) 
645  *      "alignment <nr>"        sets the blockalignment to <nr>
646  *      "bitspersample <nr>"    sets the nr of bits per sample
647  * Sequencer:
648  *      "master [midi|file|smpte|none]" sets the midi master device
649  *      "slave [midi|file|smpte|none]" sets the midi master device
650  *      "port mapper"           midioutput to portmapper
651  *      "port <nr>"             midioutput to specified port
652  *      "tempo <nr>"            tempo of track (depends on timeformat/divtype)
653  *      "offset <nr>"           start offset?
654  */
655 static DWORD
656 MCISTR_Set(_MCISTR_PROTO_) {
657         union   {
658                 MCI_SET_PARMS           setParams;
659                 MCI_WAVE_SET_PARMS      wavesetParams;
660                 MCI_SEQ_SET_PARMS       seqsetParams;
661         } U;
662         int     i,res;
663
664         U.setParams.dwCallback = 0;
665         i = 0;
666         while (i<nrofkeywords) {
667                 FLAG2("door","open",MCI_SET_DOOR_OPEN);
668                 FLAG2("door","closed",MCI_SET_DOOR_CLOSED);
669
670                 if (    !STRCMP(keywords[i],"time") && 
671                         (i+2<nrofkeywords) &&
672                         !STRCMP(keywords[i+1],"format")
673                 ) {
674                         dwFlags |= MCI_SET_TIME_FORMAT;
675
676                         /* FIXME:is this a shortcut for milliseconds or
677                          *       minutes:seconds? */
678                         if (!STRCMP(keywords[i+2],"ms"))
679                                 U.setParams.dwTimeFormat = MCI_FORMAT_MILLISECONDS;
680
681                         if (!STRCMP(keywords[i+2],"milliseconds"))
682                                 U.setParams.dwTimeFormat = MCI_FORMAT_MILLISECONDS;
683                         if (!STRCMP(keywords[i+2],"msf"))
684                                 U.setParams.dwTimeFormat = MCI_FORMAT_MSF;
685                         if (!STRCMP(keywords[i+2],"hms"))
686                                 U.setParams.dwTimeFormat = MCI_FORMAT_HMS;
687                         if (!STRCMP(keywords[i+2],"frames"))
688                                 U.setParams.dwTimeFormat = MCI_FORMAT_FRAMES;
689                         if (!STRCMP(keywords[i+2],"track"))
690                                 U.setParams.dwTimeFormat = MCI_VD_FORMAT_TRACK;
691                         if (!STRCMP(keywords[i+2],"bytes"))
692                                 U.setParams.dwTimeFormat = MCI_FORMAT_BYTES;
693                         if (!STRCMP(keywords[i+2],"samples"))
694                                 U.setParams.dwTimeFormat = MCI_FORMAT_SAMPLES;
695                         if (!STRCMP(keywords[i+2],"tmsf"))
696                                 U.setParams.dwTimeFormat = MCI_FORMAT_TMSF;
697                         if (    !STRCMP(keywords[i+2],"song") && 
698                                 (i+3<nrofkeywords) &&
699                                 !STRCMP(keywords[i+3],"pointer")
700                         )
701                                 U.setParams.dwTimeFormat = MCI_SEQ_FORMAT_SONGPTR;
702                         if (!STRCMP(keywords[i+2],"smpte") && (i+3<nrofkeywords)) {
703                                 if (!STRCMP(keywords[i+3],"24"))
704                                         U.setParams.dwTimeFormat = MCI_FORMAT_SMPTE_24;
705                                 if (!STRCMP(keywords[i+3],"25"))
706                                         U.setParams.dwTimeFormat = MCI_FORMAT_SMPTE_25;
707                                 if (!STRCMP(keywords[i+3],"30"))
708                                         U.setParams.dwTimeFormat = MCI_FORMAT_SMPTE_30;
709                                 if (!STRCMP(keywords[i+3],"drop") && (i+4<nrofkeywords) && !STRCMP(keywords[i+4],"30")) {
710                                         U.setParams.dwTimeFormat = MCI_FORMAT_SMPTE_30DROP;
711                                         i++;
712                                 }
713                                 i++;
714                                 /*FALLTHROUGH*/
715                         }
716                         i+=3;
717                         continue;
718                 }
719                 if (!STRCMP(keywords[i],"audio") && (i+1<nrofkeywords)) {
720                         dwFlags |= MCI_SET_AUDIO;
721                         if (!STRCMP(keywords[i+1],"all"))
722                                 U.setParams.dwAudio = MCI_SET_AUDIO_ALL;
723                         if (!STRCMP(keywords[i+1],"left"))
724                                 U.setParams.dwAudio = MCI_SET_AUDIO_LEFT;
725                         if (!STRCMP(keywords[i+1],"right"))
726                                 U.setParams.dwAudio = MCI_SET_AUDIO_RIGHT;
727                         i+=2;
728                         continue;
729                 }
730                 FLAG1("video",MCI_SET_VIDEO);
731                 FLAG1("on",MCI_SET_ON);
732                 FLAG1("off",MCI_SET_OFF);
733                 switch (uDevTyp) {
734                 case MCI_DEVTYPE_WAVEFORM_AUDIO:
735                         FLAG2("any","input",MCI_WAVE_SET_ANYINPUT);
736                         FLAG2("any","output",MCI_WAVE_SET_ANYOUTPUT);
737
738                         if (    !STRCMP(keywords[i],"formattag") && 
739                                 (i+1<nrofkeywords) &&
740                                 !STRCMP(keywords[i+1],"pcm")
741                         ) {
742                                 dwFlags |= MCI_WAVE_SET_FORMATTAG;
743                                 U.wavesetParams.wFormatTag = WAVE_FORMAT_PCM;
744                                 i+=2;
745                                 continue;
746                         }
747
748                         /* <keyword> <integer> */
749 #define WII(str,flag,fmt,element) \
750                         if (!STRCMP(keywords[i],str) && (i+1<nrofkeywords)) {\
751                                 sscanf(keywords[i+1],fmt,&(U.wavesetParams. element ));\
752                                 dwFlags |= flag;\
753                                 i+=2;\
754                                 continue;\
755                         }
756                         WII("formattag",MCI_WAVE_SET_FORMATTAG,"%hu",wFormatTag);
757                         WII("channels",MCI_WAVE_SET_CHANNELS,"%hu",nChannels);
758                         WII("bytespersec",MCI_WAVE_SET_AVGBYTESPERSEC,"%lu",nAvgBytesPerSec);
759                         WII("samplespersec",MCI_WAVE_SET_SAMPLESPERSEC,"%lu",nSamplesPerSec);
760                         WII("alignment",MCI_WAVE_SET_BLOCKALIGN,"%hu",nBlockAlign);
761                         WII("bitspersample",MCI_WAVE_SET_BITSPERSAMPLE,"%hu",wBitsPerSample);
762                         WII("input",MCI_WAVE_INPUT,"%hu",wInput);
763                         WII("output",MCI_WAVE_OUTPUT,"%hu",wOutput);
764 #undef WII
765                         break;
766                 case MCI_DEVTYPE_SEQUENCER:
767                         if (!STRCMP(keywords[i],"master") && (i+1<nrofkeywords)) {
768                                 dwFlags |= MCI_SEQ_SET_MASTER;
769                                 if (!STRCMP(keywords[i+1],"midi"))
770                                         U.seqsetParams.dwMaster = MCI_SEQ_MIDI;
771                                 if (!STRCMP(keywords[i+1],"file"))
772                                         U.seqsetParams.dwMaster = MCI_SEQ_FILE;
773                                 if (!STRCMP(keywords[i+1],"smpte"))
774                                         U.seqsetParams.dwMaster = MCI_SEQ_SMPTE;
775                                 if (!STRCMP(keywords[i+1],"none"))
776                                         U.seqsetParams.dwMaster = MCI_SEQ_NONE;
777                                 i+=2;
778                                 continue;
779                         }
780                         if (!STRCMP(keywords[i],"slave") && (i+1<nrofkeywords)) {
781                                 dwFlags |= MCI_SEQ_SET_SLAVE;
782                                 if (!STRCMP(keywords[i+1],"midi"))
783                                         U.seqsetParams.dwMaster = MCI_SEQ_MIDI;
784                                 if (!STRCMP(keywords[i+1],"file"))
785                                         U.seqsetParams.dwMaster = MCI_SEQ_FILE;
786                                 if (!STRCMP(keywords[i+1],"smpte"))
787                                         U.seqsetParams.dwMaster = MCI_SEQ_SMPTE;
788                                 if (!STRCMP(keywords[i+1],"none"))
789                                         U.seqsetParams.dwMaster = MCI_SEQ_NONE;
790                                 i+=2;
791                                 continue;
792                         }
793                         if (    !STRCMP(keywords[i],"port") && 
794                                 (i+1<nrofkeywords) &&
795                                 !STRCMP(keywords[i+1],"mapper")
796                         ) {
797                                 U.seqsetParams.dwPort=-1;/* FIXME:not sure*/
798                                 dwFlags |= MCI_SEQ_SET_PORT;
799                                 i+=2;
800                                 continue;
801                         }
802 #define SII(str,flag,element) \
803                         if (!STRCMP(keywords[i],str) && (i+1<nrofkeywords)) {\
804                                 sscanf(keywords[i+1],"%ld",&(U.seqsetParams. element ));\
805                                 dwFlags |= flag;\
806                                 i+=2;\
807                                 continue;\
808                         }
809                         SII("tempo",MCI_SEQ_SET_TEMPO,dwTempo);
810                         SII("port",MCI_SEQ_SET_PORT,dwPort);
811                         SII("offset",MCI_SEQ_SET_PORT,dwOffset);
812                 }
813                 i++;
814         }
815         if (!dwFlags)
816                 return MCIERR_MISSING_STRING_ARGUMENT;
817         _MCI_CALL_DRIVER( MCI_SET, MAKE_SEGPTR(&U) );
818         return res;
819 }
820
821 /* specify break key
822  * Arguments: 
823  *      "off"           disable break
824  *      "on <keyid>"    enable break on key with keyid
825  * (I strongly suspect, that there is another parameter:
826  *      "window <handle>"       
827  * but I don't see it mentioned in my documentation.
828  * Returns nothing.
829  */
830 static DWORD
831 MCISTR_Break(_MCISTR_PROTO_) {
832         MCI_BREAK_PARMS breakParams;
833         int     res,i;
834
835         /*breakParams.hwndBreak ? */
836         i=0;while (i<nrofkeywords) {
837                 FLAG1("off",MCI_BREAK_OFF);
838                 if (!strcmp(keywords[i],"on") && (nrofkeywords>i+1)) {
839                         dwFlags&=~MCI_BREAK_OFF;
840                         dwFlags|=MCI_BREAK_KEY;
841                         sscanf(keywords[i+1],"%d",&(breakParams.nVirtKey));
842                         i+=2;
843                         continue;
844                 }
845                 i++;
846         }
847         _MCI_CALL_DRIVER( MCI_BREAK, MAKE_SEGPTR(&breakParams) );
848         return res;
849 }
850
851 #define ITEM1(str,item,xtype) \
852         if (!STRCMP(keywords[i],str)) {\
853                 gdcParams.dwItem = item;\
854                 type = xtype;\
855                 i++;\
856                 continue;\
857         }
858 #define ITEM2(str1,str2,item,xtype) \
859         if (    !STRCMP(keywords[i],str1) &&\
860                 (i+1<nrofkeywords) &&\
861                 !STRCMP(keywords[i+1],str2)\
862         ) {\
863                 gdcParams.dwItem = item;\
864                 type = xtype;\
865                 i+=2;\
866                 continue;\
867         }
868 #define ITEM3(str1,str2,str3,item,xtype) \
869         if (    !STRCMP(keywords[i],str1) &&\
870                 (i+2<nrofkeywords) &&\
871                 !STRCMP(keywords[i+1],str2) &&\
872                 !STRCMP(keywords[i+2],str3)\
873         ) {\
874                 gdcParams.dwItem = item;\
875                 type = xtype;\
876                 i+=3;\
877                 continue;\
878         }
879 /* get device capabilities of MCI drivers
880  * Arguments:
881  * Generic:
882  *      "device type"   returns device name as string
883  *      "has audio"     returns bool
884  *      "has video"     returns bool
885  *      "uses files"    returns bool
886  *      "compound device"       returns bool
887  *      "can record"    returns bool
888  *      "can play"      returns bool
889  *      "can eject"     returns bool
890  *      "can save"      returns bool
891  * Animation:
892  *      "palettes"      returns nr of available palette entries
893  *      "windows"       returns nr of available windows
894  *      "can reverse"   returns bool
895  *      "can stretch"   returns bool
896  *      "slow play rate"        returns the slow playrate
897  *      "fast play rate"        returns the fast playrate
898  *      "normal play rate"      returns the normal playrate
899  * Overlay:
900  *      "windows"       returns nr of available windows
901  *      "can stretch"   returns bool
902  *      "can freeze"    returns bool
903  * Videodisc:
904  *      "cav"           assume CAV discs (default if no disk inserted)
905  *      "clv"           assume CLV discs 
906  *      "can reverse"   returns bool
907  *      "slow play rate"        returns the slow playrate
908  *      "fast play rate"        returns the fast playrate
909  *      "normal play rate"      returns the normal playrate
910  * Waveform audio:
911  *      "inputs"        returns nr of inputdevices
912  *      "outputs"       returns nr of outputdevices
913  */
914 static DWORD
915 MCISTR_Capability(_MCISTR_PROTO_) {
916         MCI_GETDEVCAPS_PARMS    gdcParams;
917         int     type=0,i,res;
918
919         gdcParams.dwCallback = 0;
920         if (!nrofkeywords)
921                 return MCIERR_MISSING_STRING_ARGUMENT;
922         /* well , thats default */
923         dwFlags |= MCI_GETDEVCAPS_ITEM;
924         gdcParams.dwItem = 0;
925         i=0;
926         while (i<nrofkeywords) {
927                 ITEM2("device","type",MCI_GETDEVCAPS_DEVICE_TYPE,_MCISTR_devtype);
928                 ITEM2("has","audio",MCI_GETDEVCAPS_HAS_AUDIO,_MCISTR_bool);
929                 ITEM2("has","video",MCI_GETDEVCAPS_HAS_VIDEO,_MCISTR_bool);
930                 ITEM2("uses","files",MCI_GETDEVCAPS_USES_FILES,_MCISTR_bool);
931                 ITEM2("compound","device",MCI_GETDEVCAPS_COMPOUND_DEVICE,_MCISTR_bool);
932                 ITEM2("can","record",MCI_GETDEVCAPS_CAN_RECORD,_MCISTR_bool);
933                 ITEM2("can","play",MCI_GETDEVCAPS_CAN_PLAY,_MCISTR_bool);
934                 ITEM2("can","eject",MCI_GETDEVCAPS_CAN_EJECT,_MCISTR_bool);
935                 ITEM2("can","save",MCI_GETDEVCAPS_CAN_SAVE,_MCISTR_bool);
936                 switch (uDevTyp) {
937                 case MCI_DEVTYPE_ANIMATION:
938                         ITEM1("palettes",MCI_ANIM_GETDEVCAPS_PALETTES,_MCISTR_int);
939                         ITEM1("windows",MCI_ANIM_GETDEVCAPS_MAX_WINDOWS,_MCISTR_int);
940                         ITEM2("can","reverse",MCI_ANIM_GETDEVCAPS_CAN_REVERSE,_MCISTR_bool);
941                         ITEM2("can","stretch",MCI_ANIM_GETDEVCAPS_CAN_STRETCH,_MCISTR_bool);
942                         ITEM3("slow","play","rate",MCI_ANIM_GETDEVCAPS_SLOW_RATE,_MCISTR_int);
943                         ITEM3("fast","play","rate",MCI_ANIM_GETDEVCAPS_FAST_RATE,_MCISTR_int);
944                         ITEM3("normal","play","rate",MCI_ANIM_GETDEVCAPS_NORMAL_RATE,_MCISTR_int);
945                         break;
946                 case MCI_DEVTYPE_OVERLAY:
947                         ITEM1("windows",MCI_OVLY_GETDEVCAPS_MAX_WINDOWS,_MCISTR_int);
948                         ITEM2("can","freeze",MCI_OVLY_GETDEVCAPS_CAN_FREEZE,_MCISTR_bool);
949                         ITEM2("can","stretch",MCI_OVLY_GETDEVCAPS_CAN_STRETCH,_MCISTR_bool);
950                         break;
951                 case MCI_DEVTYPE_VIDEODISC:
952                         FLAG1("cav",MCI_VD_GETDEVCAPS_CAV);
953                         FLAG1("clv",MCI_VD_GETDEVCAPS_CLV);
954                         ITEM2("can","reverse",MCI_VD_GETDEVCAPS_CAN_REVERSE,_MCISTR_bool);
955                         ITEM3("slow","play","rate",MCI_VD_GETDEVCAPS_SLOW_RATE,_MCISTR_int);
956                         ITEM3("fast","play","rate",MCI_VD_GETDEVCAPS_FAST_RATE,_MCISTR_int);
957                         ITEM3("normal","play","rate",MCI_VD_GETDEVCAPS_NORMAL_RATE,_MCISTR_int);
958                         break;
959                 case MCI_DEVTYPE_WAVEFORM_AUDIO:
960                         ITEM1("inputs",MCI_WAVE_GETDEVCAPS_INPUTS,_MCISTR_int);
961                         ITEM1("outputs",MCI_WAVE_GETDEVCAPS_OUTPUTS,_MCISTR_int);
962                         break;
963                 }
964                 i++;
965         }
966         _MCI_CALL_DRIVER( MCI_GETDEVCAPS, MAKE_SEGPTR(&gdcParams) );
967         /* no timeformat needed */
968         if (res==0)
969                 _MCISTR_convreturn(type,gdcParams.dwReturn,lpstrReturnString,uReturnLength,uDevTyp,0);
970         return res;
971 }
972 #undef ITEM1
973 #undef ITEM2
974 #undef ITEM3
975 /* resumes operation of device. no arguments, no return values */
976 static DWORD
977 MCISTR_Resume(_MCISTR_PROTO_) {
978         MCI_GENERIC_PARMS       genParams;
979         int     res;
980
981         genParams.dwCallback=0;
982         _MCI_CALL_DRIVER( MCI_RESUME, MAKE_SEGPTR(&genParams) );
983         return res;
984 }
985
986 /* pauses operation of device. no arguments, no return values */
987 static DWORD
988 MCISTR_Pause(_MCISTR_PROTO_) {
989         MCI_GENERIC_PARMS       genParams;
990         int                     res;
991         genParams.dwCallback=0;
992         _MCI_CALL_DRIVER( MCI_PAUSE, MAKE_SEGPTR(&genParams) );
993         return res;
994 }
995
996 /* stops operation of device. no arguments, no return values */
997 static DWORD
998 MCISTR_Stop(_MCISTR_PROTO_) {
999         MCI_GENERIC_PARMS       genParams;
1000         int                     res;
1001         genParams.dwCallback=0;
1002         _MCI_CALL_DRIVER( MCI_STOP, MAKE_SEGPTR(&genParams) );
1003         return res;
1004 }
1005
1006 /* starts recording.
1007  * Arguments:
1008  *      "overwrite"     overwrite existing things
1009  *      "insert"        insert at current position
1010  *      "to <time>"     record up to <time> (specified in timeformat)
1011  *      "from <time>"   record from <time> (specified in timeformat)
1012  */
1013 static DWORD
1014 MCISTR_Record(_MCISTR_PROTO_) {
1015         int                     i,res,timef,nrargs,j,k,a[4];
1016         char                    *parsestr;
1017         MCI_RECORD_PARMS        recordParams;
1018
1019         res = _MCISTR_determine_timeformat(dev,wDevID,uDevTyp,&timef);
1020         if (res) return res;
1021
1022         switch (timef) {
1023         case MCI_FORMAT_MILLISECONDS:
1024         case MCI_FORMAT_FRAMES:
1025         case MCI_FORMAT_BYTES:
1026         case MCI_FORMAT_SAMPLES:
1027                 nrargs=1;
1028                 parsestr="%d";
1029                 break;
1030         case MCI_FORMAT_HMS:
1031         case MCI_FORMAT_MSF:
1032                 parsestr="%d:%d:%d";
1033                 nrargs=3;
1034                 break;
1035         case MCI_FORMAT_TMSF:
1036                 parsestr="%d:%d:%d:%d";
1037                 nrargs=4;
1038                 break;
1039         default:fprintf(stdnimp,"mciSendString:PLAY:unknown timeformat %d, please report.\n",timef);
1040                 parsestr="%d";
1041                 nrargs=1;
1042                 break;
1043         }
1044         recordParams.dwCallback = 0;
1045         i = 0;
1046         while (i<nrofkeywords) {
1047                 if (!strcmp(keywords[i],"to") && (i+1<nrofkeywords)) {
1048                         dwFlags |= MCI_TO;
1049                         a[0]=a[1]=a[2]=a[3]=0;
1050                         j=sscanf(keywords[i+1],parsestr,&a[0],&a[1],&a[2],&a[3]);
1051                         /* add up all integers we got, if we have more 
1052                          * shift them. (Well I should use the macros in 
1053                          * mmsystem.h, right).
1054                          */
1055                         recordParams.dwTo=0;
1056                         for (k=0;k<j;k++)
1057                                 recordParams.dwTo+=a[k]<<(8*(nrargs-k));
1058                         i+=2;
1059                         continue;
1060                 }
1061                 if (!strcmp(keywords[i],"from") && (i+1<nrofkeywords)) {
1062                         dwFlags |= MCI_FROM;
1063                         a[0]=a[1]=a[2]=a[3]=0;
1064                         j=sscanf(keywords[i+1],parsestr,&a[0],&a[1],&a[2],&a[3]);
1065                         /* dito. */
1066                         recordParams.dwFrom=0;
1067                         for (k=0;k<j;k++)
1068                                 recordParams.dwFrom+=a[k]<<(8*(nrargs-k));
1069                         i+=2;
1070                         continue;
1071                 }
1072                 FLAG1("insert",MCI_RECORD_INSERT);
1073                 FLAG1("overwrite",MCI_RECORD_OVERWRITE);
1074                 i++;
1075         }
1076         _MCI_CALL_DRIVER( MCI_RECORD, MAKE_SEGPTR(&recordParams) );
1077         return res;
1078 }
1079
1080 /* play media
1081  * Arguments:
1082  *      "to <time>"     play up to <time> (specified in set timeformat)
1083  *      "from <time>"   play from <time> (specified in set timeformat)
1084  * Animation:
1085  *      "slow"          play slow
1086  *      "fast"          play fast 
1087  *      "scan"          play as fast as possible (with audio disabled perhaps)
1088  *      "reverse"       play reverse
1089  *      "speed <fps>"   play with specified frames per second
1090  * Videodisc:
1091  *      "slow"          play slow
1092  *      "fast"          play fast 
1093  *      "scan"          play as fast as possible (with audio disabled perhaps)
1094  *      "reverse"       play reverse
1095  *      "speed <fps>"   play with specified frames per second
1096  */
1097 static DWORD
1098 MCISTR_Play(_MCISTR_PROTO_) {
1099         int                     i,res,timef,nrargs,j,k,a[4];
1100         char                    *parsestr;
1101         union {
1102                 MCI_PLAY_PARMS          playParams;
1103                 MCI_VD_PLAY_PARMS       vdplayParams;
1104                 MCI_ANIM_PLAY_PARMS     animplayParams;
1105         } U;
1106
1107         res = _MCISTR_determine_timeformat(dev,wDevID,uDevTyp,&timef);
1108         if (res) return res;
1109         switch (timef) {
1110         case MCI_FORMAT_MILLISECONDS:
1111         case MCI_FORMAT_FRAMES:
1112         case MCI_FORMAT_BYTES:
1113         case MCI_FORMAT_SAMPLES:
1114                 nrargs=1;
1115                 parsestr="%d";
1116                 break;
1117         case MCI_FORMAT_HMS:
1118         case MCI_FORMAT_MSF:
1119                 parsestr="%d:%d:%d";
1120                 nrargs=3;
1121                 break;
1122         case MCI_FORMAT_TMSF:
1123                 parsestr="%d:%d:%d:%d";
1124                 nrargs=4;
1125                 break;
1126         default:fprintf(stdnimp,"mciSendString:PLAY:unknown timeformat %d, please report.\n",timef);
1127                 parsestr="%d";
1128                 nrargs=1;
1129                 break;
1130         }
1131         U.playParams.dwCallback=0;
1132         i=0;
1133         while (i<nrofkeywords) {
1134                 if (!strcmp(keywords[i],"to") && (i+1<nrofkeywords)) {
1135                         dwFlags |= MCI_TO;
1136                         a[0]=a[1]=a[2]=a[3]=0;
1137                         j=sscanf(keywords[i+1],parsestr,&a[0],&a[1],&a[2],&a[3]);
1138                         /* add up all integers we got, if we have more 
1139                          * shift them. (Well I should use the macros in 
1140                          * mmsystem.h, right).
1141                          */
1142                         U.playParams.dwTo=0;
1143                         for (k=0;k<j;k++)
1144                                 U.playParams.dwTo+=a[k]<<(8*(nrargs-k));
1145                         i+=2;
1146                         continue;
1147                 }
1148                 if (!strcmp(keywords[i],"from") && (i+1<nrofkeywords)) {
1149                         dwFlags |= MCI_FROM;
1150                         a[0]=a[1]=a[2]=a[3]=0;
1151                         j=sscanf(keywords[i+1],parsestr,&a[0],&a[1],&a[2],&a[3]);
1152                         /* dito. */
1153                         U.playParams.dwFrom=0;
1154                         for (k=0;k<j;k++)
1155                                 U.playParams.dwFrom+=a[k]<<(8*(nrargs-k));
1156                         i+=2;
1157                         continue;
1158                 }
1159                 switch (uDevTyp) {
1160                 case MCI_DEVTYPE_VIDEODISC:
1161                         FLAG1("slow",MCI_VD_PLAY_SLOW);
1162                         FLAG1("fast",MCI_VD_PLAY_FAST);
1163                         FLAG1("scan",MCI_VD_PLAY_SCAN);
1164                         FLAG1("reverse",MCI_VD_PLAY_REVERSE);
1165                         if (!STRCMP(keywords[i],"speed") && (i+1<nrofkeywords)) {
1166                                 dwFlags |= MCI_VD_PLAY_SPEED;
1167                                 sscanf(keywords[i+1],"%ld",&(U.vdplayParams.dwSpeed));
1168                                 i+=2;
1169                                 continue;
1170                         }
1171                         break;
1172                 case MCI_DEVTYPE_ANIMATION:
1173                         FLAG1("slow",MCI_ANIM_PLAY_SLOW);
1174                         FLAG1("fast",MCI_ANIM_PLAY_FAST);
1175                         FLAG1("scan",MCI_ANIM_PLAY_SCAN);
1176                         FLAG1("reverse",MCI_ANIM_PLAY_REVERSE);
1177                         if (!STRCMP(keywords[i],"speed") && (i+1<nrofkeywords)) {
1178                                 dwFlags |= MCI_ANIM_PLAY_SPEED;
1179                                 sscanf(keywords[i+1],"%ld",&(U.animplayParams.dwSpeed));
1180                                 i+=2;
1181                                 continue;
1182                         }
1183                         break;
1184                 }
1185                 i++;
1186         }
1187         _MCI_CALL_DRIVER( MCI_PLAY, MAKE_SEGPTR(&U) );
1188         return res;
1189 }
1190
1191 /* seek to a specified position
1192  * Arguments:
1193  *      "to start"      seek to start of medium
1194  *      "to end"        seek to end of medium
1195  *      "to <time>"     seek to <time> specified in current timeformat
1196  */
1197 static DWORD
1198 MCISTR_Seek(_MCISTR_PROTO_) {
1199         int             i,res,timef,nrargs,j,k,a[4];
1200         char            *parsestr;
1201         MCI_SEEK_PARMS  seekParams;
1202
1203         res = _MCISTR_determine_timeformat(dev,wDevID,uDevTyp,&timef);
1204         if (res) return res;
1205         switch (timef) {
1206         case MCI_FORMAT_MILLISECONDS:
1207         case MCI_FORMAT_FRAMES:
1208         case MCI_FORMAT_BYTES:
1209         case MCI_FORMAT_SAMPLES:
1210                 nrargs=1;
1211                 parsestr="%d";
1212                 break;
1213         case MCI_FORMAT_HMS:
1214         case MCI_FORMAT_MSF:
1215                 parsestr="%d:%d:%d";
1216                 nrargs=3;
1217                 break;
1218         case MCI_FORMAT_TMSF:
1219                 parsestr="%d:%d:%d:%d";
1220                 nrargs=4;
1221                 break;
1222         default:fprintf(stdnimp,"mciSendString:SEEK:unknown timeformat %d, please report.\n",timef);
1223                 parsestr="%d";
1224                 nrargs=1;
1225                 break;
1226         }
1227         seekParams.dwCallback=0;
1228         i=0;
1229         while (i<nrofkeywords) {
1230                 if (    !STRCMP(keywords[i],"to") && (i+1<nrofkeywords)) {
1231                         if (!STRCMP(keywords[i+1],"start")) {
1232                                 dwFlags|=MCI_SEEK_TO_START;
1233                                 seekParams.dwTo=0;
1234                                 i+=2;
1235                                 continue;
1236                         }
1237                         if (!STRCMP(keywords[i+1],"end")) {
1238                                 dwFlags|=MCI_SEEK_TO_END;
1239                                 seekParams.dwTo=0;
1240                                 i+=2;
1241                                 continue;
1242                         }
1243                         dwFlags|=MCI_TO;
1244                         i+=2;
1245                         a[0]=a[1]=a[2]=a[3]=0;
1246                         j=sscanf(keywords[i+1],parsestr,&a[0],&a[1],&a[2],&a[3]);
1247                         seekParams.dwTo=0;
1248                         for (k=0;k<j;k++)
1249                                 seekParams.dwTo+=a[k]<<(8*(nrargs-k));
1250                         continue;
1251                 }
1252                 switch (uDevTyp) {
1253                 case MCI_DEVTYPE_VIDEODISC:
1254                         FLAG1("reverse",MCI_VD_SEEK_REVERSE);
1255                         break;
1256                 }
1257                 i++;
1258         }
1259         _MCI_CALL_DRIVER( MCI_SEEK, MAKE_SEGPTR(&seekParams) );
1260         return res;
1261 }
1262
1263 /* close media/driver */
1264 static DWORD
1265 MCISTR_Close(_MCISTR_PROTO_) {
1266         MCI_GENERIC_PARMS       closeParams;
1267         int                     res;
1268
1269         _MCI_CALL_DRIVER( MCI_CLOSE, MAKE_SEGPTR(&closeParams) );
1270         return res;
1271 }
1272
1273 /* return information.
1274  * Arguments:
1275  *      "product"       return product name (human readable)
1276  *      "file"          return filename
1277  * Animation:
1278  *      "text"          returns text?
1279  * Overlay:
1280  *      "text"          returns text?
1281  */
1282 static DWORD
1283 MCISTR_Info(_MCISTR_PROTO_) {
1284         MCI_INFO_PARMS  infoParams;
1285         DWORD           sflags;
1286         int             i,res;
1287
1288         sflags = dwFlags;
1289         i=0;while (i<nrofkeywords) {
1290                 FLAG1("product",MCI_INFO_PRODUCT);
1291                 FLAG1("file",MCI_INFO_FILE);
1292                 switch (uDevTyp) {
1293                 case MCI_DEVTYPE_ANIMATION:
1294                         FLAG1("text",MCI_ANIM_INFO_TEXT);
1295                         break;
1296                 case MCI_DEVTYPE_OVERLAY:
1297                         FLAG1("text",MCI_OVLY_INFO_TEXT);
1298                         break;
1299                 }
1300                 i++;
1301         }
1302         if (dwFlags == sflags)
1303                 return MCIERR_MISSING_STRING_ARGUMENT;
1304         /* MCI driver will fill in lpstrReturn, dwRetSize.
1305          * FIXME: I don't know if this is correct behaviour
1306          */
1307         _MCI_CALL_DRIVER( MCI_INFO, MAKE_SEGPTR(&infoParams) );
1308         if (res==0)
1309                 _MCI_STR(infoParams.lpstrReturn);
1310         return res;
1311 }
1312
1313 DWORD mciSysInfo(DWORD dwFlags,LPMCI_SYSINFO_PARMS lpParms);
1314
1315 /* query MCI driver itself for information
1316  * Arguments:
1317  *      "installname"   return install name of <device> (system.ini)
1318  *      "quantity"      return nr of installed drivers
1319  *      "open"          open drivers only (additional flag)
1320  *      "name <nr>"     return nr of devices with <devicetyp>
1321  *      "name all"      return nr of all devices
1322  *
1323  * FIXME: mciSysInfo() is broken I think.
1324  */
1325 static DWORD
1326 MCISTR_Sysinfo(_MCISTR_PROTO_) {
1327         MCI_SYSINFO_PARMS       sysinfoParams;
1328         int                     i,res;
1329
1330         sysinfoParams.lpstrReturn       = lpstrReturnString;
1331         sysinfoParams.dwRetSize         = uReturnLength;
1332         sysinfoParams.wDeviceType       = uDevTyp;
1333         i=0;
1334         while (i<nrofkeywords) {
1335                 FLAG1("installname",MCI_SYSINFO_INSTALLNAME);
1336                 FLAG1("quantity",MCI_SYSINFO_INSTALLNAME);
1337                 FLAG1("open",MCI_SYSINFO_OPEN);
1338                 if (!strcmp(keywords[i],"name") && (i+1<nrofkeywords)) {
1339                         sscanf(keywords[i+1],"%ld",&(sysinfoParams.dwNumber));
1340                         dwFlags |= MCI_SYSINFO_NAME;
1341                         i+=2;
1342                         continue;
1343                 }
1344                 i++;
1345         }
1346         res=mciSysInfo(dwFlags,&sysinfoParams);
1347         if (dwFlags & MCI_SYSINFO_QUANTITY) {
1348                 char    buf[100];
1349
1350                 sprintf(buf,"%ld",*(long*)PTR_SEG_TO_LIN(lpstrReturnString));
1351                 _MCI_STR(buf);
1352         }
1353         /* no need to copy anything back, mciSysInfo did it for us */
1354         return res;
1355 }
1356
1357 /* load file
1358  * Argument: "<filename>"
1359  * Overlay: "at <left> <top> <right> <bottom>" additional
1360  */
1361 static DWORD
1362 MCISTR_Load(_MCISTR_PROTO_) {
1363         union {
1364                 MCI_LOAD_PARMS  loadParams;
1365                 MCI_OVLY_LOAD_PARMS     ovlyloadParams;
1366         } U;
1367         int             i,len,res;
1368         char            *s;
1369         HANDLE          x;
1370
1371         i=0;len=0;
1372         while (i<nrofkeywords) {
1373                 switch (uDevTyp) {
1374                 case MCI_DEVTYPE_OVERLAY:
1375                         if (!STRCMP(keywords[i],"at") && (i+4<nrofkeywords)) {
1376                                 dwFlags |= MCI_OVLY_RECT;
1377                                 sscanf(keywords[i+1],"%hd",&(U.ovlyloadParams.rc.left));
1378                                 sscanf(keywords[i+2],"%hd",&(U.ovlyloadParams.rc.top));
1379                                 sscanf(keywords[i+3],"%hd",&(U.ovlyloadParams.rc.right));
1380                                 sscanf(keywords[i+4],"%hd",&(U.ovlyloadParams.rc.bottom));
1381                                 memcpy(keywords+i,keywords+(i+5),nrofkeywords-(i+5));
1382                                 continue;
1383                         }
1384                         break;
1385                 }
1386                 len+=strlen(keywords[i])+1;
1387                 i++;
1388         }
1389         s=(char*)xmalloc(len);
1390         *s='\0';
1391         while (i<nrofkeywords) {
1392                 strcat(s,keywords[i]);
1393                 i++;
1394                 if (i<nrofkeywords) strcat(s," ");
1395         }
1396         /* FIXME: messy, but I strongly suspect we have to use a 
1397          * segmented pointer, so I am doing that
1398          */
1399         x=USER_HEAP_ALLOC(len);
1400         U.loadParams.lpfilename=(LPSTR)MAKELONG(x,USER_HeapSel);
1401         strcpy(PTR_SEG_TO_LIN(U.loadParams.lpfilename),s);
1402         free(s);
1403         dwFlags |= MCI_LOAD_FILE;
1404         _MCI_CALL_DRIVER( MCI_LOAD, MAKE_SEGPTR(&U) );
1405         USER_HEAP_FREE(x);
1406         return res;
1407 }
1408
1409 /* save to file
1410  * Argument: "<filename>"
1411  * Overlay: "at <left> <top> <right> <bottom>" additional
1412  */
1413 static DWORD
1414 MCISTR_Save(_MCISTR_PROTO_) {
1415         union {
1416                 MCI_SAVE_PARMS  saveParams;
1417                 MCI_OVLY_SAVE_PARMS     ovlysaveParams;
1418         } U;
1419         int             i,len,res;
1420         char            *s;
1421         HANDLE          x;
1422
1423         i=0;len=0;
1424         while (i<nrofkeywords) {
1425                 switch (uDevTyp) {
1426                 case MCI_DEVTYPE_OVERLAY:
1427                         if (!STRCMP(keywords[i],"at") && (i+4<nrofkeywords)) {
1428                                 dwFlags |= MCI_OVLY_RECT;
1429                                 sscanf(keywords[i+1],"%hd",&(U.ovlysaveParams.rc.left));
1430                                 sscanf(keywords[i+2],"%hd",&(U.ovlysaveParams.rc.top));
1431                                 sscanf(keywords[i+3],"%hd",&(U.ovlysaveParams.rc.right));
1432                                 sscanf(keywords[i+4],"%hd",&(U.ovlysaveParams.rc.bottom));
1433                                 memcpy(keywords+i,keywords+(i+5),nrofkeywords-(i+5));
1434                                 continue;
1435                         }
1436                         break;
1437                 }
1438                 len+=strlen(keywords[i])+1;
1439                 i++;
1440         }
1441         s=(char*)xmalloc(len);
1442         *s='\0';
1443         while (i<nrofkeywords) {
1444                 strcat(s,keywords[i]);
1445                 i++;
1446                 if (i<nrofkeywords) strcat(s," ");
1447         }
1448         /* FIXME: messy, but I strongly suspect we have to use a 
1449          * segmented pointer, so I am doing that
1450          */
1451         x=USER_HEAP_ALLOC(len);
1452         U.saveParams.lpfilename=(LPSTR)MAKELONG(x,USER_HeapSel);
1453         strcpy(PTR_SEG_TO_LIN(U.saveParams.lpfilename),s);
1454         free(s);
1455         dwFlags |= MCI_LOAD_FILE;
1456         _MCI_CALL_DRIVER( MCI_SAVE, MAKE_SEGPTR(&U) );
1457         USER_HEAP_FREE(x);
1458         return res;
1459 }
1460
1461 /* prepare device for input/output
1462  * (only applyable to waveform audio)
1463  */
1464 static DWORD
1465 MCISTR_Cue(_MCISTR_PROTO_) {
1466         MCI_GENERIC_PARMS       cueParams;
1467         int                     i,res;
1468
1469         i=0;
1470         while (i<nrofkeywords) {
1471                 switch (uDevTyp) {
1472                 case MCI_DEVTYPE_WAVEFORM_AUDIO:
1473                         FLAG1("input",MCI_WAVE_INPUT);
1474                         FLAG1("output",MCI_WAVE_OUTPUT);
1475                         break;
1476                 }
1477                 i++;
1478         }
1479         _MCI_CALL_DRIVER( MCI_CUE, MAKE_SEGPTR(&cueParams) );
1480         return res;
1481 }
1482
1483 /* delete information */
1484 static DWORD
1485 MCISTR_Delete(_MCISTR_PROTO_) {
1486         int     timef,nrargs,i,j,k,a[4],res;
1487         char    *parsestr;
1488         MCI_WAVE_DELETE_PARMS   deleteParams;
1489
1490         /* only implemented for waveform audio */
1491         if (uDevTyp != MCI_DEVTYPE_WAVEFORM_AUDIO)
1492                 return MCIERR_UNSUPPORTED_FUNCTION; /* well it fits */
1493         res = _MCISTR_determine_timeformat(dev,wDevID,uDevTyp,&timef);
1494         if (res) return res;
1495         switch (timef) {
1496         case MCI_FORMAT_MILLISECONDS:
1497         case MCI_FORMAT_FRAMES:
1498         case MCI_FORMAT_BYTES:
1499         case MCI_FORMAT_SAMPLES:
1500                 nrargs=1;
1501                 parsestr="%d";
1502                 break;
1503         case MCI_FORMAT_HMS:
1504         case MCI_FORMAT_MSF:
1505                 parsestr="%d:%d:%d";
1506                 nrargs=3;
1507                 break;
1508         case MCI_FORMAT_TMSF:
1509                 parsestr="%d:%d:%d:%d";
1510                 nrargs=4;
1511                 break;
1512         default:fprintf(stdnimp,"mciSendString:DELETE:unknown timeformat %d, please report.\n",timef);
1513                 parsestr="%d";
1514                 nrargs=1;
1515                 break;
1516         }
1517         i=0;
1518         while (i<nrofkeywords) {
1519                 if (!strcmp(keywords[i],"to") && (i+1<nrofkeywords)) {
1520                         dwFlags |= MCI_TO;
1521                         a[0]=a[1]=a[2]=a[3]=0;
1522                         j=sscanf(keywords[i+1],parsestr,&a[0],&a[1],&a[2],&a[3]);
1523                         /* add up all integers we got, if we have more 
1524                          * shift them. (Well I should use the macros in 
1525                          * mmsystem.h, right).
1526                          */
1527                         deleteParams.dwTo=0;
1528                         for (k=0;k<j;k++)
1529                                 deleteParams.dwTo+=a[k]<<(8*(nrargs-k));
1530                         i+=2;
1531                         continue;
1532                 }
1533                 if (!strcmp(keywords[i],"from") && (i+1<nrofkeywords)) {
1534                         dwFlags |= MCI_FROM;
1535                         a[0]=a[1]=a[2]=a[3]=0;
1536                         j=sscanf(keywords[i+1],parsestr,&a[0],&a[1],&a[2],&a[3]);
1537                         /* dito. */
1538                         deleteParams.dwFrom=0;
1539                         for (k=0;k<j;k++)
1540                                 deleteParams.dwFrom+=a[k]<<(8*(nrargs-k));
1541                         i+=2;
1542                         continue;
1543                 }
1544                 i++;
1545         }
1546         _MCI_CALL_DRIVER( MCI_DELETE, MAKE_SEGPTR(&deleteParams) );
1547         return res;
1548 }
1549
1550 /* send command to device. only applies to videodisc */
1551 static DWORD
1552 MCISTR_Escape(_MCISTR_PROTO_) {
1553         MCI_VD_ESCAPE_PARMS     escapeParams;
1554         int                     i,len,res;
1555         char            *s;
1556         HANDLE          x;
1557
1558         if (uDevTyp != MCI_DEVTYPE_VIDEODISC)
1559                 return MCIERR_UNSUPPORTED_FUNCTION;
1560         i=0;len=0;
1561         while (i<nrofkeywords) {
1562                 len+=strlen(keywords[i])+1;
1563                 i++;
1564         }
1565         s=(char*)xmalloc(len);
1566         *s='\0';
1567         while (i<nrofkeywords) {
1568                 strcat(s,keywords[i]);
1569                 i++;
1570                 if (i<nrofkeywords) strcat(s," ");
1571         }
1572         /* FIXME: messy, but I strongly suspect we have to use a 
1573          * segmented pointer, so I am doing that
1574          */
1575         x=USER_HEAP_ALLOC(len);
1576         escapeParams.lpstrCommand=(LPSTR)MAKELONG(x,USER_HeapSel);
1577         strcpy(PTR_SEG_TO_LIN(escapeParams.lpstrCommand),s);
1578         free(s);
1579         dwFlags |= MCI_VD_ESCAPE_STRING;
1580         _MCI_CALL_DRIVER( MCI_ESCAPE, MAKE_SEGPTR(&escapeParams) );
1581         USER_HEAP_FREE(x);
1582         return res;
1583 }
1584
1585 /* unfreeze [part of] the overlayed video 
1586  * only applyable to Overlay devices
1587  */
1588 static DWORD
1589 MCISTR_Unfreeze(_MCISTR_PROTO_) {
1590         MCI_OVLY_RECT_PARMS     unfreezeParams;
1591         int                     i,res;
1592
1593         if (uDevTyp != MCI_DEVTYPE_OVERLAY)
1594                 return MCIERR_UNSUPPORTED_FUNCTION;
1595         i=0;while (i<nrofkeywords) {
1596                 if (!STRCMP(keywords[i],"at") && (i+4<nrofkeywords)) {
1597                         sscanf(keywords[i+1],"%hd",&(unfreezeParams.rc.left));
1598                         sscanf(keywords[i+2],"%hd",&(unfreezeParams.rc.top));
1599                         sscanf(keywords[i+3],"%hd",&(unfreezeParams.rc.right));
1600                         sscanf(keywords[i+4],"%hd",&(unfreezeParams.rc.bottom));
1601                         dwFlags |= MCI_OVLY_RECT;
1602                         i+=5;
1603                         continue;
1604                 }
1605                 i++;
1606         }
1607         _MCI_CALL_DRIVER( MCI_UNFREEZE, MAKE_SEGPTR(&unfreezeParams) );
1608         return res;
1609 }
1610 /* freeze [part of] the overlayed video 
1611  * only applyable to Overlay devices
1612  */
1613 static DWORD
1614 MCISTR_Freeze(_MCISTR_PROTO_) {
1615         MCI_OVLY_RECT_PARMS     freezeParams;
1616         int                     i,res;
1617
1618         if (uDevTyp != MCI_DEVTYPE_OVERLAY)
1619                 return MCIERR_UNSUPPORTED_FUNCTION;
1620         i=0;while (i<nrofkeywords) {
1621                 if (!STRCMP(keywords[i],"at") && (i+4<nrofkeywords)) {
1622                         sscanf(keywords[i+1],"%hd",&(freezeParams.rc.left));
1623                         sscanf(keywords[i+2],"%hd",&(freezeParams.rc.top));
1624                         sscanf(keywords[i+3],"%hd",&(freezeParams.rc.right));
1625                         sscanf(keywords[i+4],"%hd",&(freezeParams.rc.bottom));
1626                         dwFlags |= MCI_OVLY_RECT;
1627                         i+=5;
1628                         continue;
1629                 }
1630                 i++;
1631         }
1632         _MCI_CALL_DRIVER( MCI_FREEZE, MAKE_SEGPTR(&freezeParams) );
1633         return res;
1634 }
1635
1636 /* copy parts of image to somewhere else 
1637  * "source [at <left> <top> <right> <bottom>]"  source is framebuffer [or rect]
1638  * "destination [at <left> <top> <right> <bottom>]"     destination is framebuffer [or rect]
1639  * Overlay:
1640  * "frame [at <left> <top> <right> <bottom>]"   frame is framebuffer [or rect]
1641  *                                              where the video input is placed
1642  * "video [at <left> <top> <right> <bottom>]"   video is whole video [or rect]
1643  *                                              (defining part of input to
1644  *                                              be displayed)
1645  *
1646  * FIXME: This whole junk is passing multiple rectangles.
1647  *      I don't know how to do that with the present interface.
1648  *      (Means code below is broken)
1649  */
1650 static DWORD
1651 MCISTR_Put(_MCISTR_PROTO_) {
1652         union {
1653                 MCI_OVLY_RECT_PARMS     ovlyputParams;
1654                 MCI_ANIM_RECT_PARMS     animputParams;
1655         } U;
1656         int     i,res;
1657         i=0;while (i<nrofkeywords) {
1658                 switch (uDevTyp) {
1659                 case MCI_DEVTYPE_ANIMATION:
1660                         FLAG1("source",MCI_ANIM_PUT_SOURCE);
1661                         FLAG1("destination",MCI_ANIM_PUT_DESTINATION);
1662                         if (!STRCMP(keywords[i],"at") && (i+4<nrofkeywords)) {
1663                                 sscanf(keywords[i+1],"%hd",&(U.animputParams.rc.left));
1664                                 sscanf(keywords[i+2],"%hd",&(U.animputParams.rc.top));
1665                                 sscanf(keywords[i+3],"%hd",&(U.animputParams.rc.right));
1666                                 sscanf(keywords[i+4],"%hd",&(U.animputParams.rc.bottom));
1667                                 dwFlags |= MCI_ANIM_RECT;
1668                                 i+=5;
1669                                 continue;
1670                         }
1671                         break;
1672                 case MCI_DEVTYPE_OVERLAY:
1673                         FLAG1("source",MCI_OVLY_PUT_SOURCE);
1674                         FLAG1("destination",MCI_OVLY_PUT_DESTINATION);
1675                         FLAG1("video",MCI_OVLY_PUT_VIDEO);
1676                         FLAG1("frame",MCI_OVLY_PUT_FRAME);
1677                         if (!STRCMP(keywords[i],"at") && (i+4<nrofkeywords)) {
1678                                 sscanf(keywords[i+1],"%hd",&(U.ovlyputParams.rc.left));
1679                                 sscanf(keywords[i+2],"%hd",&(U.ovlyputParams.rc.top));
1680                                 sscanf(keywords[i+3],"%hd",&(U.ovlyputParams.rc.right));
1681                                 sscanf(keywords[i+4],"%hd",&(U.ovlyputParams.rc.bottom));
1682                                 dwFlags |= MCI_OVLY_RECT;
1683                                 i+=5;
1684                                 continue;
1685                         }
1686                         break;
1687                 }
1688                 i++;
1689         }
1690         _MCI_CALL_DRIVER( MCI_PUT, MAKE_SEGPTR(&U) );
1691         return res;
1692 }
1693
1694 /* palette behaviour changing
1695  * (Animation only)
1696  *      "normal"        realize the palette normally
1697  *      "background"    realize the palette as background palette
1698  */
1699 static DWORD
1700 MCISTR_Realize(_MCISTR_PROTO_) {
1701         MCI_GENERIC_PARMS       realizeParams;
1702         int                     i,res;
1703
1704         if (uDevTyp != MCI_DEVTYPE_ANIMATION)
1705                 return MCIERR_UNSUPPORTED_FUNCTION;
1706         i=0;
1707         while (i<nrofkeywords) {
1708                 FLAG1("background",MCI_ANIM_REALIZE_BKGD);
1709                 FLAG1("normal",MCI_ANIM_REALIZE_NORM);
1710                 i++;
1711         }
1712         _MCI_CALL_DRIVER( MCI_REALIZE, MAKE_SEGPTR(&realizeParams) );
1713         return res;
1714 }
1715
1716 /* videodisc spinning
1717  *      "up"
1718  *      "down"
1719  */
1720 static DWORD
1721 MCISTR_Spin(_MCISTR_PROTO_) {
1722         MCI_GENERIC_PARMS       spinParams;
1723         int                     i,res;
1724
1725         if (uDevTyp != MCI_DEVTYPE_VIDEODISC)
1726                 return MCIERR_UNSUPPORTED_FUNCTION;
1727         i=0;
1728         while (i<nrofkeywords) {
1729                 FLAG1("up",MCI_VD_SPIN_UP);
1730                 FLAG1("down",MCI_VD_SPIN_UP);
1731                 i++;
1732         }
1733         _MCI_CALL_DRIVER( MCI_SPIN, MAKE_SEGPTR(&spinParams) );
1734         return res;
1735 }
1736
1737 /* step single frames
1738  *      "reverse"       optional flag
1739  *      "by <nr>"       for <nr> frames
1740  */
1741 static DWORD
1742 MCISTR_Step(_MCISTR_PROTO_) {
1743         union   {
1744                 MCI_ANIM_STEP_PARMS     animstepParams;
1745                 MCI_VD_STEP_PARMS       vdstepParams;
1746         } U;
1747         int     i,res;
1748
1749         i=0;
1750         while (i<nrofkeywords) {
1751                 switch (uDevTyp) {
1752                 case MCI_DEVTYPE_ANIMATION:
1753                         FLAG1("reverse",MCI_ANIM_STEP_REVERSE);
1754                         if (!STRCMP(keywords[i],"by") && (i+1<nrofkeywords)) {
1755                                 sscanf(keywords[i+1],"%ld",&(U.animstepParams.dwFrames));
1756                                 dwFlags |= MCI_ANIM_STEP_FRAMES;
1757                                 i+=2;
1758                                 continue;
1759                         }
1760                         break;
1761                 case MCI_DEVTYPE_VIDEODISC:
1762                         FLAG1("reverse",MCI_VD_STEP_REVERSE);
1763                         if (!STRCMP(keywords[i],"by") && (i+1<nrofkeywords)) {
1764                                 sscanf(keywords[i+1],"%ld",&(U.vdstepParams.dwFrames));
1765                                 dwFlags |= MCI_VD_STEP_FRAMES;
1766                                 i+=2;
1767                                 continue;
1768                         }
1769                         break;
1770                 }
1771                 i++;
1772         }
1773         _MCI_CALL_DRIVER( MCI_STEP, MAKE_SEGPTR(&U) );
1774         return res;
1775 }
1776
1777 /* update animation window
1778  * Arguments:
1779  *      "at <left> <top> <right> <bottom>" only in this rectangle
1780  *      "hdc"           device context
1781  */
1782 static DWORD
1783 MCISTR_Update(_MCISTR_PROTO_) {
1784         int             i,res;
1785         MCI_ANIM_UPDATE_PARMS   updateParams;
1786
1787         i=0;
1788         while (i<nrofkeywords) {
1789                 if (!STRCMP(keywords[i],"at") && (i+4<nrofkeywords)) {
1790                         sscanf(keywords[i+1],"%hd",&(updateParams.rc.left));
1791                         sscanf(keywords[i+2],"%hd",&(updateParams.rc.top));
1792                         sscanf(keywords[i+3],"%hd",&(updateParams.rc.right));
1793                         sscanf(keywords[i+4],"%hd",&(updateParams.rc.bottom));
1794                         dwFlags |= MCI_ANIM_RECT;
1795                         i+=5;
1796                         continue;
1797                 }
1798                 if (!STRCMP(keywords[i],"hdc") && (i+1<nrofkeywords)) {
1799                         dwFlags |= MCI_ANIM_UPDATE_HDC;
1800                         sscanf(keywords[i+1],"%hd",&(updateParams.hDC));
1801                         i+=2;
1802                         continue;
1803                 }
1804                 i++;
1805         }
1806         _MCI_CALL_DRIVER( MCI_UPDATE, MAKE_SEGPTR(&updateParams) );
1807         return res;
1808 }
1809
1810 /* where command for animation and overlay drivers.
1811  * just returns the specified rectangle as a string
1812  * Arguments:
1813  *      "source"
1814  *      "destination"
1815  * Overlay special:
1816  *      "video"
1817  *      "frame"
1818  */
1819 static DWORD
1820 MCISTR_Where(_MCISTR_PROTO_) {
1821         union {
1822                 MCI_ANIM_RECT_PARMS     animwhereParams;
1823                 MCI_OVLY_RECT_PARMS     ovlywhereParams;
1824         } U;
1825         int     i,res;
1826
1827         i=0;
1828         while (i<nrofkeywords) {
1829                 switch (uDevTyp) {
1830                 case MCI_DEVTYPE_ANIMATION:
1831                         FLAG1("source",MCI_ANIM_WHERE_SOURCE);
1832                         FLAG1("destination",MCI_ANIM_WHERE_DESTINATION);
1833                         break;
1834                 case MCI_DEVTYPE_OVERLAY:
1835                         FLAG1("source",MCI_OVLY_WHERE_SOURCE);
1836                         FLAG1("destination",MCI_OVLY_WHERE_DESTINATION);
1837                         FLAG1("video",MCI_OVLY_WHERE_VIDEO);
1838                         FLAG1("frame",MCI_OVLY_WHERE_FRAME);
1839                         break;
1840                 }
1841                 i++;
1842         }
1843         _MCI_CALL_DRIVER( MCI_WHERE, MAKE_SEGPTR(&U) );
1844         if (res==0) {
1845                 char    buf[100];
1846                 switch (uDevTyp) {
1847                 case MCI_DEVTYPE_ANIMATION:
1848                         sprintf(buf,"%d %d %d %d",
1849                                 U.animwhereParams.rc.left,
1850                                 U.animwhereParams.rc.top,
1851                                 U.animwhereParams.rc.right,
1852                                 U.animwhereParams.rc.bottom
1853                         );
1854                         break;
1855                 case MCI_DEVTYPE_OVERLAY:
1856                         sprintf(buf,"%d %d %d %d",
1857                                 U.ovlywhereParams.rc.left,
1858                                 U.ovlywhereParams.rc.top,
1859                                 U.ovlywhereParams.rc.right,
1860                                 U.ovlywhereParams.rc.bottom
1861                         );
1862                         break;
1863                 default:strcpy(buf,"0 0 0 0");break;
1864                 }
1865                 _MCI_STR(buf);
1866         }
1867         return  res;
1868 }
1869
1870 static DWORD
1871 MCISTR_Window(_MCISTR_PROTO_) {
1872         int     i,res;
1873         char    *s;
1874         union   {
1875                 MCI_ANIM_WINDOW_PARMS   animwindowParams;
1876                 MCI_OVLY_WINDOW_PARMS   ovlywindowParams;
1877         } U;
1878
1879         s=NULL;
1880         i=0;
1881         while (i<nrofkeywords) {
1882                 switch (uDevTyp) {
1883                 case MCI_DEVTYPE_ANIMATION:
1884                         if (!STRCMP(keywords[i],"handle") && (i+1<nrofkeywords)) {
1885                                 dwFlags |= MCI_ANIM_WINDOW_HWND;
1886                                 if (!STRCMP(keywords[i+1],"default")) 
1887                                         U.animwindowParams.hWnd = MCI_OVLY_WINDOW_DEFAULT;
1888                                 else
1889                                         sscanf(keywords[i+1],"%hd",&(U.animwindowParams.hWnd));
1890                                 i+=2;
1891                                 continue;
1892                         }
1893                         if (!STRCMP(keywords[i],"state") && (i+1<nrofkeywords)) {
1894                                 dwFlags |= MCI_ANIM_WINDOW_STATE;
1895                                 if (!STRCMP(keywords[i+1],"hide"))
1896                                         U.animwindowParams.nCmdShow = SW_HIDE;
1897                                 if (!STRCMP(keywords[i+1],"iconic"))
1898                                         U.animwindowParams.nCmdShow = SW_SHOWMINNOACTIVE; /* correct? */
1899                                 if (!STRCMP(keywords[i+1],"minimized"))
1900                                         U.animwindowParams.nCmdShow = SW_SHOWMINIMIZED;
1901                                 if (!STRCMP(keywords[i+1],"maximized"))
1902                                         U.animwindowParams.nCmdShow = SW_SHOWMAXIMIZED;
1903                                 if (!STRCMP(keywords[i+1],"minimize"))
1904                                         U.animwindowParams.nCmdShow = SW_MINIMIZE;
1905                                 if (!STRCMP(keywords[i+1],"normal"))
1906                                         U.animwindowParams.nCmdShow = SW_NORMAL;
1907                                 if (!STRCMP(keywords[i+1],"show"))
1908                                         U.animwindowParams.nCmdShow = SW_SHOW;
1909                                 if (!STRCMP(keywords[i+1],"no") && (i+2<nrofkeywords)) {
1910                                         if (!STRCMP(keywords[i+2],"active"))
1911                                                 U.animwindowParams.nCmdShow = SW_SHOWNOACTIVATE;
1912                                         if (!STRCMP(keywords[i+2],"action"))
1913                                                 U.animwindowParams.nCmdShow = SW_SHOWNA;/* correct?*/
1914                                         i++;
1915                                 }
1916                                 i+=2;
1917                                 continue;
1918                         }
1919                         /* text is enclosed in " ... " as it seems */
1920                         if (!STRCMP(keywords[i],"text")) {
1921                                 char    *t;
1922                                 int     len,j,k;
1923
1924                                 if (keywords[i+1][0]!='"') {
1925                                         i++;
1926                                         continue;
1927                                 }
1928                                 dwFlags |= MCI_ANIM_WINDOW_TEXT;
1929                                 len     = strlen(keywords[i+1])+1;
1930                                 j       = i+2;
1931                                 while (j<nrofkeywords) {
1932                                         len += strlen(keywords[j])+1;
1933                                         if (strchr(keywords[j],'"'))
1934                                                 break;
1935                                         j++;
1936                                 }
1937                                 s=(char*)xmalloc(len);
1938                                 strcpy(s,keywords[i+1]+1);
1939                                 k=j;j=i+2;
1940                                 while (j<=k) {
1941                                         strcat(s," ");
1942                                         strcat(s,keywords[j]);
1943                                 }
1944                                 if ((t=strchr(s,'"'))) *t='\0';
1945                                 /* FIXME: segmented pointer? */
1946                                 U.animwindowParams.lpstrText = s;
1947                                 i=k+1;
1948                                 continue;
1949                         }
1950                         FLAG1("stretch",MCI_ANIM_WINDOW_ENABLE_STRETCH);
1951                         break;
1952                 case MCI_DEVTYPE_OVERLAY:
1953                         if (!STRCMP(keywords[i],"handle") && (i+1<nrofkeywords)) {
1954                                 dwFlags |= MCI_OVLY_WINDOW_HWND;
1955                                 if (!STRCMP(keywords[i+1],"default")) 
1956                                         U.ovlywindowParams.hWnd = MCI_OVLY_WINDOW_DEFAULT;
1957                                 else
1958                                         sscanf(keywords[i+1],"%hd",&(U.ovlywindowParams.hWnd));
1959                                 i+=2;
1960                                 continue;
1961                         }
1962                         if (!STRCMP(keywords[i],"state") && (i+1<nrofkeywords)) {
1963                                 dwFlags |= MCI_OVLY_WINDOW_STATE;
1964                                 if (!STRCMP(keywords[i+1],"hide"))
1965                                         U.ovlywindowParams.nCmdShow = SW_HIDE;
1966                                 if (!STRCMP(keywords[i+1],"iconic"))
1967                                         U.ovlywindowParams.nCmdShow = SW_SHOWMINNOACTIVE; /* correct? */
1968                                 if (!STRCMP(keywords[i+1],"minimized"))
1969                                         U.ovlywindowParams.nCmdShow = SW_SHOWMINIMIZED;
1970                                 if (!STRCMP(keywords[i+1],"maximized"))
1971                                         U.ovlywindowParams.nCmdShow = SW_SHOWMAXIMIZED;
1972                                 if (!STRCMP(keywords[i+1],"minimize"))
1973                                         U.ovlywindowParams.nCmdShow = SW_MINIMIZE;
1974                                 if (!STRCMP(keywords[i+1],"normal"))
1975                                         U.ovlywindowParams.nCmdShow = SW_NORMAL;
1976                                 if (!STRCMP(keywords[i+1],"show"))
1977                                         U.ovlywindowParams.nCmdShow = SW_SHOW;
1978                                 if (!STRCMP(keywords[i+1],"no") && (i+2<nrofkeywords)) {
1979                                         if (!STRCMP(keywords[i+2],"active"))
1980                                                 U.ovlywindowParams.nCmdShow = SW_SHOWNOACTIVATE;
1981                                         if (!STRCMP(keywords[i+2],"action"))
1982                                                 U.ovlywindowParams.nCmdShow = SW_SHOWNA;/* correct?*/
1983                                         i++;
1984                                 }
1985                                 i+=2;
1986                                 continue;
1987                         }
1988                         /* text is enclosed in " ... " as it seems */
1989                         if (!STRCMP(keywords[i],"text")) {
1990                                 char    *t;
1991                                 int     len,j,k;
1992
1993                                 if (keywords[i+1][0]!='"') {
1994                                         i++;
1995                                         continue;
1996                                 }
1997                                 dwFlags |= MCI_OVLY_WINDOW_TEXT;
1998                                 len     = strlen(keywords[i+1])+1;
1999                                 j       = i+2;
2000                                 while (j<nrofkeywords) {
2001                                         len += strlen(keywords[j])+1;
2002                                         if (strchr(keywords[j],'"'))
2003                                                 break;
2004                                         j++;
2005                                 }
2006                                 s=(char*)xmalloc(len);
2007                                 strcpy(s,keywords[i+1]+1);
2008                                 k=j;j=i+2;
2009                                 while (j<=k) {
2010                                         strcat(s," ");
2011                                         strcat(s,keywords[j]);
2012                                 }
2013                                 if ((t=strchr(s,'"'))) *t='\0';
2014                                 /* FIXME: segmented pointer? */
2015                                 U.ovlywindowParams.lpstrText = s;
2016                                 i=k+1;
2017                                 continue;
2018                         }
2019                         FLAG1("stretch",MCI_OVLY_WINDOW_ENABLE_STRETCH);
2020                         break;
2021                 }
2022                 i++;
2023         }
2024         _MCI_CALL_DRIVER( MCI_WINDOW, MAKE_SEGPTR(&U) );
2025         if (s) free(s);
2026         return res;
2027 }
2028
2029 struct  _MCISTR_cmdtable {
2030         char    *cmd;
2031         DWORD   (*fun)(_MCISTR_PROTO_);
2032 } MCISTR_cmdtable[]={
2033         {"break",       MCISTR_Break},
2034         {"capability",  MCISTR_Capability},
2035         {"close",       MCISTR_Close},
2036         {"cue",         MCISTR_Cue},
2037         {"delete",      MCISTR_Delete},
2038         {"escape",      MCISTR_Escape},
2039         {"freeze",      MCISTR_Freeze},
2040         {"info",        MCISTR_Info},
2041         {"load",        MCISTR_Load},
2042         {"open",        MCISTR_Open},
2043         {"pause",       MCISTR_Pause},
2044         {"play",        MCISTR_Play},
2045         {"put",         MCISTR_Put},
2046         {"realize",     MCISTR_Realize},
2047         {"record",      MCISTR_Record},
2048         {"resume",      MCISTR_Resume},
2049         {"save",        MCISTR_Save},
2050         {"seek",        MCISTR_Seek},
2051         {"set",         MCISTR_Set},
2052         {"spin",        MCISTR_Spin},
2053         {"status",      MCISTR_Status},
2054         {"step",        MCISTR_Step},
2055         {"stop",        MCISTR_Stop},
2056         {"sysinfo",     MCISTR_Sysinfo},
2057         {"unfreeze",    MCISTR_Unfreeze},
2058         {"update",      MCISTR_Update},
2059         {"where",       MCISTR_Where},
2060         {"window",      MCISTR_Window},
2061         {NULL,          NULL}
2062 };
2063 /**************************************************************************
2064 *                               mciSendString                   [MMSYSTEM.702]
2065 */
2066 /* The usercode sends a string with a command (and flags) expressed in 
2067  * words in it... We do our best to call aprobiate drivers,
2068  * and return a errorcode AND a readable string (if lpstrRS!=NULL)
2069  * Info gathered by watching cool134.exe and from Borland's mcistrwh.hlp
2070  */
2071 /* FIXME: "all" is a valid devicetype and we should access all devices if
2072  * it is used. (imagine "close all"). Not implemented yet.
2073  */
2074 DWORD mciSendString (LPCSTR lpstrCommand, LPSTR lpstrReturnString, 
2075         UINT uReturnLength, HWND hwndCallback)
2076 {
2077         char    *cmd,*dev,*args,**keywords;
2078         WORD    uDevTyp=0,wDevID=0;
2079         DWORD   dwFlags;
2080         int     res=0,i,nrofkeywords;
2081
2082         dprintf_mci(stddeb,"mciSendString('%s', %p, %d, %X)\n", lpstrCommand, 
2083                 lpstrReturnString, uReturnLength, hwndCallback
2084         );
2085         /* format is <command> <device> <optargs> */
2086         cmd=strdup(lpstrCommand);
2087         dev=strchr(cmd,' ');
2088         if (dev==NULL) {
2089                 free(cmd);
2090                 return MCIERR_MISSING_DEVICE_NAME;
2091         }
2092         *dev++='\0';
2093         args=strchr(dev,' ');
2094         if (args!=NULL) *args++='\0';
2095         AnsiUpper(dev);
2096         if (args!=NULL) {
2097                 char    *s;
2098                 i=1;/* nrofkeywords = nrofspaces+1 */
2099                 s=args;
2100                 while ((s=strchr(s,' '))!=NULL) i++,s++;
2101                 keywords=(char**)xmalloc(sizeof(char*)*(i+2));
2102                 nrofkeywords=i;
2103                 s=args;i=0;
2104                 while (s && i<nrofkeywords) {
2105                         keywords[i++]=s;
2106                         s=strchr(s,' ');
2107                         if (s) *s++='\0';
2108                 }
2109                 keywords[i]=NULL;
2110         } else {
2111                 nrofkeywords=0;
2112                 keywords=(char**)xmalloc(sizeof(char*));
2113         }
2114         dwFlags = 0; /* default flags */
2115         for (i=0;i<nrofkeywords;) {
2116                 if (!STRCMP(keywords[i],"wait")) {
2117                         dwFlags |= MCI_WAIT;
2118                         memcpy(keywords+i,keywords+(i+1),nrofkeywords-i-1);
2119                         nrofkeywords--;
2120                         continue;
2121                 }
2122                 if (!STRCMP(keywords[i],"notify")) {
2123                         /* how should we callback?  I don't know. */
2124                         /*dwFlags |= MCI_NOTIFY;*/
2125                         memcpy(keywords+i,keywords+(i+1),nrofkeywords-i-1);
2126                         nrofkeywords--;
2127                         continue;
2128                 }
2129                 i++;
2130         }
2131
2132         /* determine wDevID and uDevTyp for all commands except "open" */
2133         if (STRCMP(cmd,"open")!=0) {
2134                 wDevID=0;
2135                 while (1) {
2136                         SEGPTR  dname;
2137
2138                         dname=(SEGPTR)mciOpenDrv[wDevID].lpstrAlias;
2139                         if (dname==NULL) 
2140                                 dname=(SEGPTR)mciOpenDrv[wDevID].lpstrDeviceType;
2141                         if ((dname!=NULL)&&(!STRCMP(PTR_SEG_TO_LIN(dname),dev)))
2142                                 break;
2143                         if (++wDevID >= MAXMCIDRIVERS) {
2144                                 dprintf_mci(stddeb, __FILE__":mciSendString:MAXMCIDRIVERS reached!\n");
2145                                 free(keywords);free(cmd);
2146                                 return MCIERR_INTERNAL;
2147                         }
2148                 }
2149                 uDevTyp=mciDrv[wDevID].wType;
2150         }
2151
2152         for (i=0;MCISTR_cmdtable[i].cmd!=NULL;i++) {
2153                 if (!STRCMP(MCISTR_cmdtable[i].cmd,cmd)) {
2154                         res=MCISTR_cmdtable[i].fun(
2155                                 wDevID,uDevTyp,lpstrReturnString,
2156                                 uReturnLength,dev,(LPSTR*)keywords,nrofkeywords,
2157                                 dwFlags
2158                         );
2159                         break;
2160                 }
2161         }
2162         if (MCISTR_cmdtable[i].cmd!=NULL) {
2163                 free(keywords);free(cmd);
2164                 return  res;
2165         }
2166         fprintf(stdnimp,"mciSendString('%s', %p, %u, %X) // unimplemented, please report.\n", lpstrCommand, 
2167                 lpstrReturnString, uReturnLength, hwndCallback
2168         );
2169         free(keywords);free(cmd);
2170         return MCIERR_MISSING_COMMAND_STRING;
2171 }
2172 #endif