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