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