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