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