NE_LoadFileModule: Free module when NE_LoadDLLs fails.
[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:
9  *      + special commands of device drivers should be handled by those drivers
10  *      + this current implementation does not allow commands like 
11  *        "capability <filename> can play" which is allowed by the MCI standard.
12  *      + return value and their conversion to strings (including error codes)
13  *        is not coherent with MCI standard.
14  *      + digital video interface has not been throughoutfully tested
15  */
16
17 #include <unistd.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <fcntl.h>
21 #include <sys/ioctl.h>
22 #include <assert.h>
23 #include "multimedia.h"
24 #include "digitalv.h"
25 #include "heap.h"
26 #include "ldt.h"
27 #include "user.h"
28 #include "driver.h"
29 #include "callback.h"
30 #include "debug.h"
31 #include "xmalloc.h"
32
33 DEFAULT_DEBUG_CHANNEL(mci)
34
35 /* The reason why I just don't lowercase the keywords array in 
36  * mciSendString is left as an exercise to the reader.
37  * (EP: For the blinds, like me, it's linked to file name handling 
38  * which is case sensitive).
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 static LPSTR _MCISTR_Unquote(LPSTR str)
61 {
62     if (str) {
63         int     i, len = strlen(str);
64         
65         if (len > 1 && str[0] == '"' && str[len-1] == '"') {
66             for (i = 1; i < len-1; i++) {
67                 str[i - 1] = str[i];
68             }
69             str[len-2] = 0;
70         }
71     }
72     return str;
73 }
74
75 /* print a DWORD in the specified timeformat */
76 static void
77 _MCISTR_printtf(char *buf, UINT16 uDevType, DWORD timef, DWORD val) 
78 {
79     *buf = '\0';
80     switch (timef) {
81     case MCI_FORMAT_MILLISECONDS:
82     case MCI_FORMAT_FRAMES:
83     case MCI_FORMAT_BYTES:
84     case MCI_FORMAT_SAMPLES:
85     case MCI_VD_FORMAT_TRACK:
86         /*case MCI_SEQ_FORMAT_SONGPTR: sameas MCI_VD_FORMAT_TRACK */
87         sprintf(buf, "%ld",val);
88         break;
89     case MCI_FORMAT_HMS:
90         /* well, the macros have the same content*/
91         /*FALLTRHOUGH*/
92     case MCI_FORMAT_MSF:
93         sprintf(buf, "%d:%d:%d", 
94                 MCI_HMS_HOUR(val),
95                 MCI_HMS_MINUTE(val),
96                 MCI_HMS_SECOND(val));
97         break;
98     case MCI_FORMAT_TMSF:
99         sprintf(buf, "%d:%d:%d:%d",
100                 MCI_TMSF_TRACK(val),
101                 MCI_TMSF_MINUTE(val),
102                 MCI_TMSF_SECOND(val),
103                 MCI_TMSF_FRAME(val));
104         break;
105     default:
106         FIXME(mci, "missing timeformat for %ld, report.\n", timef);
107         strcpy(buf, "0"); /* hmm */
108         break;
109     }
110     return;
111 }
112 /* possible different return types */
113 #define _MCISTR_int     1
114 #define _MCISTR_time    2
115 #define _MCISTR_bool    3
116 #define _MCISTR_tfname  4
117 #define _MCISTR_mode    5
118 #define _MCISTR_divtype 6
119 #define _MCISTR_seqtype 7
120 #define _MCISTR_vdmtype 8
121 #define _MCISTR_devtype 9
122
123 static void
124 _MCISTR_convreturn(int type, DWORD dwReturn, LPSTR lpstrReturnString,
125                    WORD uReturnLength, WORD uDevTyp, int timef) 
126 {
127     switch (type) {
128     case _MCISTR_vdmtype:
129         switch (dwReturn) {
130         case MCI_VD_MEDIA_CLV:  _MCI_STR("CLV");        break;
131         case MCI_VD_MEDIA_CAV:  _MCI_STR("CAV");        break;
132         default:
133         case MCI_VD_MEDIA_OTHER:_MCI_STR("other");      break;
134         }
135         break;
136     case _MCISTR_seqtype:
137         switch (dwReturn) {
138         case MCI_SEQ_NONE:      _MCI_STR("none");       break;
139         case MCI_SEQ_SMPTE:     _MCI_STR("smpte");      break;
140         case MCI_SEQ_FILE:      _MCI_STR("file");       break;
141         case MCI_SEQ_MIDI:      _MCI_STR("midi");       break;
142         default:FIXME(mci, "missing sequencer mode %ld\n", dwReturn);
143         }
144         break;
145     case _MCISTR_mode:
146         switch (dwReturn) {
147         case MCI_MODE_NOT_READY:_MCI_STR("not ready");  break;
148         case MCI_MODE_STOP:     _MCI_STR("stopped");    break;
149         case MCI_MODE_PLAY:     _MCI_STR("playing");    break;
150         case MCI_MODE_RECORD:   _MCI_STR("recording");  break;
151         case MCI_MODE_SEEK:     _MCI_STR("seeking");    break;
152         case MCI_MODE_PAUSE:    _MCI_STR("paused");     break;
153         case MCI_MODE_OPEN:     _MCI_STR("open");       break;
154         default:break;
155         }
156         break;
157     case _MCISTR_bool:
158         if (dwReturn)
159             _MCI_STR("true");
160         else
161             _MCI_STR("false");
162         break;
163     case _MCISTR_int:{
164         char    buf[16];
165         sprintf(buf, "%ld", dwReturn);
166         _MCI_STR(buf);
167         break;
168     }
169     case _MCISTR_time: {        
170         char    buf[100];
171         _MCISTR_printtf(buf, uDevTyp, timef, dwReturn);
172         _MCI_STR(buf);
173         break;
174     }
175     case _MCISTR_tfname:
176         switch (timef) {
177         case MCI_FORMAT_MILLISECONDS:   _MCI_STR("milliseconds");       break;
178         case MCI_FORMAT_FRAMES:         _MCI_STR("frames");             break;
179         case MCI_FORMAT_BYTES:          _MCI_STR("bytes");              break;
180         case MCI_FORMAT_SAMPLES:        _MCI_STR("samples");            break;
181         case MCI_FORMAT_HMS:            _MCI_STR("hms");                break;
182         case MCI_FORMAT_MSF:            _MCI_STR("msf");                break;
183         case MCI_FORMAT_TMSF:           _MCI_STR("tmsf");               break;
184         default:
185             FIXME(mci, "missing timefmt for %d, report.\n", timef);
186             break;
187         }
188         break;
189     case _MCISTR_divtype:
190         switch (dwReturn) {
191         case MCI_SEQ_DIV_PPQN:          _MCI_STR("PPQN");               break;
192         case MCI_SEQ_DIV_SMPTE_24:      _MCI_STR("SMPTE 24 frame");     break;
193         case MCI_SEQ_DIV_SMPTE_25:      _MCI_STR("SMPTE 25 frame");     break;
194         case MCI_SEQ_DIV_SMPTE_30:      _MCI_STR("SMPTE 30 frame");     break;
195         case MCI_SEQ_DIV_SMPTE_30DROP:  _MCI_STR("SMPTE 30 frame drop");break;
196         }
197     case _MCISTR_devtype:
198         switch (dwReturn) {
199         case MCI_DEVTYPE_VCR:           _MCI_STR("vcr");                break;
200         case MCI_DEVTYPE_VIDEODISC:     _MCI_STR("videodisc");          break;
201         case MCI_DEVTYPE_CD_AUDIO:      _MCI_STR("cd audio");           break;
202         case MCI_DEVTYPE_OVERLAY:       _MCI_STR("overlay");            break;
203         case MCI_DEVTYPE_DAT:           _MCI_STR("dat");                break;
204         case MCI_DEVTYPE_SCANNER:       _MCI_STR("scanner");            break;
205         case MCI_DEVTYPE_ANIMATION:     _MCI_STR("animation");          break;
206         case MCI_DEVTYPE_DIGITAL_VIDEO: _MCI_STR("digital video");      break;
207         case MCI_DEVTYPE_OTHER:         _MCI_STR("other");              break;
208         case MCI_DEVTYPE_WAVEFORM_AUDIO:_MCI_STR("waveform audio");     break;
209         case MCI_DEVTYPE_SEQUENCER:     _MCI_STR("sequencer");          break;
210         default:FIXME(mci, "unknown device type %ld, report.\n",
211                       dwReturn);break;
212         }
213         break;
214     default:
215         FIXME(mci, "unknown resulttype %d, report.\n", type);
216         break;
217     }
218 }
219
220 #define FLAG1(str, flag)                        \
221         if (!STRCMP(keywords[i], str)) {        \
222                 dwFlags |= flag;                \
223                 i++;                            \
224                 continue;                       \
225         }
226
227 #define FLAG2(str1, str2, flag)                 \
228         if (!STRCMP(keywords[i], str1) &&       \
229             (i+1 < nrofkeywords) &&             \
230             !STRCMP(keywords[i+1], str2)) {     \
231                 dwFlags |= flag;                \
232                 i += 2;                         \
233                 continue;                       \
234         }
235
236 /* All known subcommands are implemented in single functions to avoid
237  * bloat and a xxxx lines long mciSendString(). All commands are of the
238  * format MCISTR_Cmd(_MCISTR_PROTO_) where _MCISTR_PROTO_ is the above
239  * defined line of arguments. (This is just for easy enhanceability.)
240  * All functions return the MCIERR_ errorvalue as DWORD. Returnvalues
241  * for the calls are in lpstrReturnString (If I mention return values
242  * in function headers, I mean returnvalues in lpstrReturnString.)
243  * Integers are sprintf("%d")ed integers. Boolean values are 
244  * "true" and "false". 
245  * timeformat depending values are "%d" "%d:%d" "%d:%d:%d" "%d:%d:%d:%d"
246  * FIXME: is above line correct?
247  *
248  * Preceding every function is a list of implemented/known arguments.
249  * Feel free to add missing arguments.
250  *
251  */
252
253 /*
254  * Opens the specified MCI driver. 
255  * Arguments: <name> 
256  * Optional:
257  *      "shareable"
258  *      "alias <aliasname>"
259  *      "element <elementname>"
260  * Additional:
261  * waveform audio:
262  *      "buffer <nrBytesPerSec>"
263  * Animation:
264  *      "nostatic"      increaste nr of nonstatic colours
265  *      "parent <windowhandle>"
266  *      "style <mask>"  bitmask of WS_xxxxx (see windows.h)
267  *      "style child"   WS_CHILD
268  *      "style overlap" WS_OVERLAPPED
269  *      "style popup"   WS_POPUP
270  * Overlay:
271  *      "parent <windowhandle>"
272  *      "style <mask>"  bitmask of WS_xxxxx (see windows.h)
273  *      "style child"   WS_CHILD
274  *      "style overlap" WS_OVERLAPPED
275  *      "style popup"   WS_POPUP
276  * Returns nothing.
277  */
278 static DWORD
279 MCISTR_Open(_MCISTR_PROTO_) 
280 {
281     int                 res, i;
282     char*               s;
283     union U {
284         MCI_OPEN_PARMSA         openParams;
285         MCI_WAVE_OPEN_PARMSA    waveopenParams;
286         MCI_ANIM_OPEN_PARMSA    animopenParams;
287         MCI_OVLY_OPEN_PARMSA    ovlyopenParams;
288         MCI_DGV_OPEN_PARMSA     dgvopenParams;
289     };
290     union U*            pU = xmalloc(sizeof(union U));
291
292     pU->openParams.lpstrElementName = NULL;
293     s = strchr(dev, '!');
294     if (s != NULL) {
295         *s++ = '\0';
296         pU->openParams.lpstrElementName = strdup(s);
297         dwFlags |= MCI_OPEN_ELEMENT;
298     }
299     for (i = 0; i < nrofkeywords; ) {
300         if ((!STRCMP(keywords[i], "type")) && (i < nrofkeywords-1)) {
301             pU->openParams.lpstrElementName = strdup(dev);
302             dwFlags |= MCI_OPEN_ELEMENT;
303             dev = keywords[i+1];
304             /* FIXME: isn't there a memory leak here ? keyword+i ? */
305             memcpy(keywords+i, keywords+(i+2), (nrofkeywords-i-2) * sizeof(char*));
306             nrofkeywords -= 2;
307             i += 2;
308             continue;
309         }
310         i++;
311     }
312     CharUpperA((char*)dev);
313     uDevTyp = MCI_GetDevTypeFromString(dev);
314     if (uDevTyp == 0) {
315         free(pU->openParams.lpstrElementName);
316         free(pU);
317         return MCIERR_INVALID_DEVICE_NAME;
318     }
319
320     pU->openParams.dwCallback   = hwndCallback;
321     pU->openParams.wDeviceID    = wDevID;
322     pU->ovlyopenParams.dwStyle  = 0; 
323     pU->animopenParams.dwStyle  = 0; 
324     pU->openParams.lpstrDeviceType      = strdup(dev);
325     pU->openParams.lpstrAlias   = NULL;
326     dwFlags |= MCI_OPEN_TYPE;
327     for (i = 0; i < nrofkeywords; ) {
328         FLAG1("shareable", MCI_OPEN_SHAREABLE);
329         if (!STRCMP(keywords[i], "alias") && (i+1 < nrofkeywords)) {
330             dwFlags |= MCI_OPEN_ALIAS;
331             pU->openParams.lpstrAlias = strdup(keywords[i+1]);
332             i += 2;
333             continue;
334         }
335         if (!STRCMP(keywords[i], "element") && (i+1 < nrofkeywords)) {
336             dwFlags |= MCI_OPEN_ELEMENT;
337             assert(pU->openParams.lpstrElementName == NULL);
338             pU->openParams.lpstrElementName = strdup(keywords[i+1]);
339             i += 2;
340             continue;
341         }
342         switch (uDevTyp) {
343         case MCI_DEVTYPE_ANIMATION:
344             FLAG1("nostatic", MCI_ANIM_OPEN_NOSTATIC);
345             if (!STRCMP(keywords[i], "parent") && (i+1 < nrofkeywords)) {
346                 dwFlags |= MCI_ANIM_OPEN_PARENT;
347                 sscanf(keywords[i+1], "%u", &(pU->animopenParams.hWndParent));
348                 i += 2;
349                 continue;
350             }
351             if (!STRCMP(keywords[i], "style") && (i+1 < nrofkeywords)) {
352                 DWORD   st;
353                 
354                 dwFlags |= MCI_ANIM_OPEN_WS;
355                 if (!STRCMP(keywords[i+1], "popup")) {
356                     pU->animopenParams.dwStyle |= WS_POPUP; 
357                 } else if (!STRCMP(keywords[i+1], "overlap")) {
358                     pU->animopenParams.dwStyle |= WS_OVERLAPPED; 
359                 } else if (!STRCMP(keywords[i+1], "child")) {
360                     pU->animopenParams.dwStyle |= WS_CHILD; 
361                 } else if (sscanf(keywords[i+1], "%ld", &st)) {
362                     pU->animopenParams.dwStyle |= st; 
363                 } else
364                     FIXME(mci, "unknown 'style' keyword %s, please report.\n", keywords[i+1]);
365                 i += 2;
366                 continue;
367             }
368             break;
369         case MCI_DEVTYPE_DIGITAL_VIDEO:
370             FLAG1("nostatic", MCI_ANIM_OPEN_NOSTATIC);
371             if (!STRCMP(keywords[i], "parent") && (i+1 < nrofkeywords)) {
372                 dwFlags |= MCI_DGV_OPEN_PARENT;
373                 sscanf(keywords[i+1], "%u", &(pU->dgvopenParams.hWndParent));
374                 i += 2;
375                 continue;
376             }
377             if (!STRCMP(keywords[i], "style") && (i+1 < nrofkeywords)) {
378                 DWORD   st;
379                 
380                 dwFlags |= MCI_DGV_OPEN_WS;
381                 if (!STRCMP(keywords[i+1], "popup")) {
382                     pU->dgvopenParams.dwStyle |= WS_POPUP; 
383                 } else if (!STRCMP(keywords[i+1], "overlap")) {
384                     pU->dgvopenParams.dwStyle |= WS_OVERLAPPED; 
385                 } else if (!STRCMP(keywords[i+1], "child")) {
386                     pU->dgvopenParams.dwStyle |= WS_CHILD; 
387                 } else if (sscanf(keywords[i+1], "%ld", &st)) {
388                     pU->dgvopenParams.dwStyle |= st; 
389                 } else
390                     FIXME(mci, "unknown 'style' keyword %s, please report.\n", keywords[i+1]);
391                 i += 2;
392                 continue;
393             }
394             break;
395         case MCI_DEVTYPE_WAVEFORM_AUDIO:
396             if (!STRCMP(keywords[i], "buffer") && (i+1 < nrofkeywords)) {
397                 dwFlags |= MCI_WAVE_OPEN_BUFFER;
398                 sscanf(keywords[i+1], "%ld", &(pU->waveopenParams.dwBufferSeconds));
399             }
400             break;
401         case MCI_DEVTYPE_OVERLAY:
402             /* looks just like anim, but without NOSTATIC */
403             if (!STRCMP(keywords[i], "parent") && (i+1 < nrofkeywords)) {
404                 dwFlags |= MCI_OVLY_OPEN_PARENT;
405                 sscanf(keywords[i+1], "%u", &(pU->ovlyopenParams.hWndParent));
406                 i += 2;
407                 continue;
408             }
409             if (!STRCMP(keywords[i], "style") && (i+1 < nrofkeywords)) {
410                 DWORD   st;
411                 
412                 dwFlags |= MCI_OVLY_OPEN_WS;
413                 if (!STRCMP(keywords[i+1], "popup")) {
414                     pU->ovlyopenParams.dwStyle |= WS_POPUP; 
415                 } else if (!STRCMP(keywords[i+1], "overlap")) {
416                     pU->ovlyopenParams.dwStyle |= WS_OVERLAPPED; 
417                 } else if (!STRCMP(keywords[i+1], "child")) {
418                     pU->ovlyopenParams.dwStyle |= WS_CHILD; 
419                 } else if (sscanf(keywords[i+1], "%ld", &st)) {
420                     pU->ovlyopenParams.dwStyle |= st; 
421                 } else
422                     FIXME(mci, "unknown 'style' keyword %s, please report.\n", keywords[i+1]);
423                 i += 2;
424                 continue;
425             }
426             break;
427         }
428         FIXME(mci, "unknown parameter passed %s, please report.\n",
429               keywords[i]);
430         i++;
431     }
432
433     _MCISTR_Unquote(pU->openParams.lpstrElementName);
434
435     res = mciSendCommandA(0, MCI_OPEN, dwFlags, (DWORD)pU);
436
437     free(pU->openParams.lpstrElementName);
438     free(pU->openParams.lpstrDeviceType);
439     free(pU->openParams.lpstrAlias);
440     free(pU);
441     return res;
442 }
443
444 /* A helper function for a lot of others ... 
445  * for instance status/play/record/seek etc.
446  */
447 DWORD
448 _MCISTR_determine_timeformat(LPCSTR dev, WORD wDevID, WORD uDevTyp, int *timef)
449 {
450     int                 res;
451     DWORD               dwFlags = MCI_STATUS_ITEM;
452     LPMCI_STATUS_PARMS  statusParams = xmalloc(sizeof(MCI_STATUS_PARMS));
453     
454     if (!statusParams) return 0;
455     statusParams->dwItem        = MCI_STATUS_TIME_FORMAT;
456     statusParams->dwReturn      = 0;
457     res = mciSendCommandA(wDevID, MCI_STATUS, dwFlags, (DWORD)statusParams);
458
459     if (res == 0) *timef = statusParams->dwReturn;
460     free(statusParams);
461     return res;
462 }
463
464 /* query status of MCI drivers
465  * Arguments:
466  * Required: 
467  *      "mode"  - returns "not ready" "paused" "playing" "stopped" "open" 
468  *                "parked" "recording" "seeking" ....
469  * Basics:
470  *      "current track" - returns current track as integer
471  *      "length [track <nr>]"   - returns length [of track <nr>] in current 
472  *                              timeformat
473  *      "number of tracks" - returns number of tracks as integer
474  *      "position [track <nr>]" - returns position [in track <nr>] in current 
475  *                              timeformat
476  *      "ready"                 - checks if device is ready to play, -> bool
477  *      "start position"        - returns start position in timeformat
478  *      "time format"           - returns timeformat (list of possible values:
479  *                              "ms" "msf" "milliseconds" "hmsf" "tmsf" "frames"
480  *                              "bytes" "samples" "hms")
481  *      "media present"         - returns if media is present as bool
482  * Animation:
483  *      "forward"               - returns "true" if device is playing forwards
484  *      "speed"                 - returns speed for device
485  *      "palette handle"        - returns palette handle
486  *      "window handle"         - returns window handle
487  *      "stretch"               - returns stretch bool
488  * MIDI sequencer:
489  *      "division type"         - ? returns "PPQN" "SMPTE 24 frame" 
490  *                      "SMPTE 25 frame" "SMPTE 30 frame" "SMPTE 30 drop frame"
491  *      "tempo"                 - current tempo in (PPQN? speed in frames, SMPTE*? speed in hsmf)
492  *      "offset"                - offset in dito.
493  *      "port"                  - midi port as integer
494  *      "slave"                 - slave device ("midi", "file", "none", "smpte")
495  *      "master"                - masterdevice (dito.)
496  * Overlay:
497  *      "window handle"         - see animation
498  *      "stretch"               - dito
499  * Video Disc:
500  *      "speed"                 - speed as integer
501  *      "forward"               - returns bool (when playing forward)
502  *      "side"                  - returns 1 or 2
503  *      "media type"            - returns "CAV" "CLV" "other"
504  *      "disc size"             - returns "8" or "12"
505  * WAVEFORM audio:
506  *      "input"                 - base queries on input set
507  *      "output"                - base queries on output set
508  *      "format tag"            - return integer format tag
509  *      "channels"              - return integer nr of channels
510  *      "bytespersec"           - return average nr of bytes/sec
511  *      "samplespersec"         - return nr of samples per sec
512  *      "bitspersample"         - return bitspersample
513  *      "alignment"             - return block alignment
514  *      "level"                 - return level?
515  * Digitalvideo:
516  *      "audio"
517  *      "audio alignment"
518  *      "audio bitspersample"
519  *      "audio breaks"
520  *      "audio bytespersec"
521  *      "audio input"
522  *      "audio record"
523  *      "audio source"
524  *      "audio samplespersec"
525  *      "audio stream"
526  *      "bass"
527  *      "bitsperpel"
528  *      "brightness"
529  *      "color"
530  *      "contrast"
531  *      "disk space drive"
532  *      "file completion"
533  *      "file format "
534  *      "file mode"
535  *      "forward"
536  *      "frames skipped"
537  *      "gamma"
538  *      "input"
539  *      "left volume"
540  *      "media present"
541  *      "mode"
542  *      "monitor"
543  *      "monitor method"
544  *      "nominal"
545  *      "nominal frame rate"
546  *      "nominal record frame rate"
547  *      "output"
548  *      "palette handle"
549  *      "pause mode"
550  *      "play speed"
551  *      "record frame rate"
552  *      "reference frame"
553  *      "reserved size"
554  *      "right volume"
555  *      "seek exactly"
556  *      "sharpness"
557  *      "smpte"
558  *      "speed"
559  *      "still file format"
560  *      "tint"
561  *      "treble"
562  *      "unsaved"
563  *      "video"
564  *      "video key index"
565  *      "video key color"
566  *      "video record"
567  *      "video source"
568  *      "video source number"
569  *      "video stream"
570  *      "volume"
571  *      "window handle"
572  *      "window visible"
573  *      "window minimized"
574  *      "window maximized"
575  */
576
577 #define ITEM1(str, item, xtype)                 \
578         if (!STRCMP(keywords[i], str)) {        \
579                 pU->statusParams.dwItem = item; \
580                 type = xtype;                   \
581                 i++;                            \
582                 continue;                       \
583         }
584 #define ITEM2(str1, str2, item, xtype)          \
585         if (    !STRCMP(keywords[i], str1) &&   \
586                 (i+1 < nrofkeywords) &&         \
587                 !STRCMP(keywords[i+1], str2)) { \
588                 pU->statusParams.dwItem = item; \
589                 type = xtype;                   \
590                 i += 2;                         \
591                 continue;                       \
592         }
593 #define ITEM3(str1, str2, str3, item, xtype)    \
594         if (    !STRCMP(keywords[i], str1) &&   \
595                 (i+2 < nrofkeywords) &&         \
596                 !STRCMP(keywords[i+1], str2) && \
597                 !STRCMP(keywords[i+2], str3)) { \
598                 pU->statusParams.dwItem = item; \
599                 type = xtype;                   \
600                 i += 3;                         \
601                 continue;                       \
602         }
603 #define ITEM4(str1, str2, str3, str4, item, xtype) \
604         if (    !STRCMP(keywords[i], str1) &&   \
605                 (i+3 < nrofkeywords) &&         \
606                 !STRCMP(keywords[i+1], str2) && \
607                 !STRCMP(keywords[i+2], str3) && \
608                 !STRCMP(keywords[i+3], str4)) { \
609                 pU->statusParams.dwItem = item; \
610                 type = xtype;                   \
611                 i += 4;                         \
612                 continue;                       \
613         }
614
615 static DWORD
616 MCISTR_Status(_MCISTR_PROTO_) {
617     union U {
618         MCI_STATUS_PARMS        statusParams;
619         MCI_DGV_STATUS_PARMSA   dgvstatusParams;
620     };
621     union U*            pU = xmalloc(sizeof(union U));
622     int                 type = 0, i, res, timef;
623     
624     pU->statusParams.dwCallback = hwndCallback;
625     dwFlags     |= MCI_STATUS_ITEM;
626     res = _MCISTR_determine_timeformat(dev, wDevID, uDevTyp, &timef);
627     if (res) return res;
628     
629     pU->statusParams.dwReturn   = 0;
630     pU->statusParams.dwItem     = 0;
631     
632     for (i = 0; i < nrofkeywords; ) {
633         if (!STRCMP(keywords[i], "track") && (i+1 < nrofkeywords)) {
634             sscanf(keywords[i+1], "%ld", &(pU->statusParams.dwTrack));
635             dwFlags |= MCI_TRACK;
636             i += 2;
637             continue;
638         }
639         FLAG1("start", MCI_STATUS_START);
640         /* generic things */
641         ITEM2("current", "track", MCI_STATUS_CURRENT_TRACK, _MCISTR_time);
642         ITEM2("time", "format", MCI_STATUS_TIME_FORMAT, _MCISTR_tfname);
643         ITEM1("ready", MCI_STATUS_READY, _MCISTR_bool);
644         ITEM1("mode", MCI_STATUS_MODE, _MCISTR_mode);
645         ITEM3("number", "of", "tracks", MCI_STATUS_NUMBER_OF_TRACKS, _MCISTR_int);
646         ITEM1("length", MCI_STATUS_LENGTH, _MCISTR_time);
647         ITEM1("position", MCI_STATUS_POSITION, _MCISTR_time);
648         ITEM2("media", "present", MCI_STATUS_MEDIA_PRESENT, _MCISTR_bool);
649         
650         switch (uDevTyp) {
651         case MCI_DEVTYPE_ANIMATION:
652             ITEM2("palette", "handle", MCI_ANIM_STATUS_HPAL, _MCISTR_int);
653             ITEM2("window", "handle", MCI_ANIM_STATUS_HWND, _MCISTR_int);
654             ITEM1("stretch", MCI_ANIM_STATUS_STRETCH, _MCISTR_bool);
655             ITEM1("speed", MCI_ANIM_STATUS_SPEED, _MCISTR_int);
656             ITEM1("forward", MCI_ANIM_STATUS_FORWARD, _MCISTR_bool);
657             break;
658         case MCI_DEVTYPE_SEQUENCER:
659             /* just completing the list, not working correctly */
660             ITEM2("division", "type", MCI_SEQ_STATUS_DIVTYPE, _MCISTR_divtype);
661             /* tempo ... PPQN in frames/second, SMPTE in hmsf */
662             ITEM1("tempo", MCI_SEQ_STATUS_TEMPO, _MCISTR_int);
663             ITEM1("port", MCI_SEQ_STATUS_PORT, _MCISTR_int);
664             ITEM1("slave", MCI_SEQ_STATUS_SLAVE, _MCISTR_seqtype);
665             ITEM1("master", MCI_SEQ_STATUS_SLAVE, _MCISTR_seqtype);
666             /* offset ... PPQN in frames/second, SMPTE in hmsf */
667             ITEM1("offset", MCI_SEQ_STATUS_SLAVE, _MCISTR_time);
668             break;
669         case MCI_DEVTYPE_OVERLAY:
670             ITEM2("window", "handle", MCI_OVLY_STATUS_HWND, _MCISTR_int);
671             ITEM1("stretch", MCI_OVLY_STATUS_STRETCH, _MCISTR_bool);
672             break;
673         case MCI_DEVTYPE_VIDEODISC:
674             ITEM1("speed", MCI_VD_STATUS_SPEED, _MCISTR_int);
675             ITEM1("forward", MCI_VD_STATUS_FORWARD, _MCISTR_bool);
676             ITEM1("side", MCI_VD_STATUS_SIDE, _MCISTR_int);
677             ITEM2("media", "type", MCI_VD_STATUS_SIDE, _MCISTR_vdmtype);
678             /* returns 8 or 12 */
679             ITEM2("disc", "size", MCI_VD_STATUS_DISC_SIZE, _MCISTR_int);
680             break;
681         case MCI_DEVTYPE_WAVEFORM_AUDIO:
682             /* I am not quite sure if foll. 2 lines are right. */
683             FLAG1("input", MCI_WAVE_INPUT);
684             FLAG1("output", MCI_WAVE_OUTPUT);
685             
686             ITEM2("format", "tag", MCI_WAVE_STATUS_FORMATTAG, _MCISTR_int);
687             ITEM1("channels", MCI_WAVE_STATUS_CHANNELS, _MCISTR_int);
688             ITEM1("bytespersec", MCI_WAVE_STATUS_AVGBYTESPERSEC, _MCISTR_int);
689             ITEM1("samplespersec", MCI_WAVE_STATUS_SAMPLESPERSEC, _MCISTR_int);
690             ITEM1("bitspersample", MCI_WAVE_STATUS_BITSPERSAMPLE, _MCISTR_int);
691             ITEM1("alignment", MCI_WAVE_STATUS_BLOCKALIGN, _MCISTR_int);
692             ITEM1("level", MCI_WAVE_STATUS_LEVEL, _MCISTR_int);
693             break;
694         case MCI_DEVTYPE_DIGITAL_VIDEO:
695             ITEM1("audio", MCI_DGV_STATUS_AUDIO, _MCISTR_bool);  /* FIXME? doc says MCI_ON/MCI_OFF */
696             ITEM2("audio", "alignment", MCI_DGV_STATUS_BLOCKALIGN, _MCISTR_int);
697             ITEM2("audio", "bitspersample", MCI_DGV_STATUS_BITSPERSAMPLE, _MCISTR_int);
698 /* EPP      ITEM2("audio", "breaks", MCI_AVI_STATUS_AUDIO_BREAKS, _MCISTR_int); */
699             ITEM2("audio", "bytespersec", MCI_DGV_STATUS_AVGBYTESPERSEC, _MCISTR_int);
700             ITEM2("audio", "input", MCI_DGV_STATUS_AUDIO_INPUT, _MCISTR_int);
701             ITEM2("audio", "record", MCI_DGV_STATUS_AUDIO_RECORD, _MCISTR_bool); /* FIXME? doc says MCI_ON/MCI_OFF */
702             ITEM3("audio", "source", "number", MCI_DGV_STATUS_AUDIO_SOURCE, _MCISTR_int);
703             /* FIXME: ITEM2("audio", "source", MCI_DGV_STATUS_AUDIO_SOURCE, _MCISTR_dgvaudiosource); */
704 /* EPP      ITEM2("audio", "samplespersec", MCI_DGV_STATUS_SAMPLESPERSECOND, _MCISTR_int); */
705             ITEM2("audio", "stream", MCI_DGV_STATUS_AUDIO_STREAM, _MCISTR_int);
706             ITEM1("bass", MCI_DGV_STATUS_BASS, _MCISTR_int);
707             ITEM1("bitsperpel", MCI_DGV_STATUS_BITSPERPEL, _MCISTR_int);
708             ITEM1("brightness", MCI_DGV_STATUS_BRIGHTNESS, _MCISTR_int);
709             ITEM1("color", MCI_DGV_STATUS_COLOR, _MCISTR_int);
710             ITEM1("contrast", MCI_DGV_STATUS_CONTRAST, _MCISTR_int);
711 /* EPP  *       "disk space drive" FIXME */
712             ITEM2("file", "completion", MCI_DGV_STATUS_FILE_COMPLETION, _MCISTR_int);
713 /* EPP      ITEM2("file", "format", MCI_DGV_STATUS_FILEFORMAT, _MCISTR_???); */
714 /* EPP      ITEM2("file", "mode", MCI_DGV_STATUS_FILE_MODE, _MCISTR_gdvfileformat); */
715             ITEM1("forward", MCI_DGV_STATUS_FORWARD, _MCISTR_bool);
716 /* EPP      ITEM2("frames", "skipped", MCI_AVI_STATUS_FRAMES_SKIPPED, _MCISTR_int); */
717             ITEM1("gamma", MCI_DGV_STATUS_GAMMA, _MCISTR_int);
718             ITEM2("input", "bass", MCI_DGV_STATUS_BASS|MCI_DGV_STATUS_INPUT, _MCISTR_int);
719             ITEM2("input", "brightness", MCI_DGV_STATUS_BRIGHTNESS|MCI_DGV_STATUS_INPUT, _MCISTR_int);
720             ITEM2("input", "color", MCI_DGV_STATUS_COLOR|MCI_DGV_STATUS_INPUT, _MCISTR_int);
721             ITEM2("input", "contrast", MCI_DGV_STATUS_CONTRAST|MCI_DGV_STATUS_INPUT, _MCISTR_int);
722             ITEM2("input", "gamma", MCI_DGV_STATUS_GAMMA|MCI_DGV_STATUS_INPUT, _MCISTR_int);
723             ITEM2("input", "sharpness", MCI_DGV_STATUS_SHARPNESS|MCI_DGV_STATUS_INPUT, _MCISTR_int);
724             ITEM2("input", "tint", MCI_DGV_STATUS_TINT|MCI_DGV_STATUS_INPUT, _MCISTR_int);
725             ITEM2("input", "treble", MCI_DGV_STATUS_TREBLE|MCI_DGV_STATUS_INPUT, _MCISTR_int);
726
727             ITEM2("left", "volume", MCI_DGV_STATUS_VOLUME|MCI_DGV_STATUS_LEFT, _MCISTR_int);
728             ITEM2("media", "present", MCI_STATUS_MEDIA_PRESENT, _MCISTR_bool);
729 /* EPP      ITEM2("monitor", "method", MCI_DGV_STATUS_MONITOR_METHOD, _MCISTR_monitor); */
730 /* EPP      ITEM1("monitor", MCI_DGV_STATUS_MONITOR, _MCISTR_monitor2); */
731             ITEM2("nominal", "bass", MCI_DGV_STATUS_BASS|MCI_DGV_STATUS_NOMINAL, _MCISTR_int);
732             ITEM2("nominal", "brightness", MCI_DGV_STATUS_BRIGHTNESS|MCI_DGV_STATUS_NOMINAL, _MCISTR_int);
733             ITEM2("nominal", "color", MCI_DGV_STATUS_COLOR|MCI_DGV_STATUS_NOMINAL, _MCISTR_int);
734             ITEM2("nominal", "contrast", MCI_DGV_STATUS_CONTRAST|MCI_DGV_STATUS_NOMINAL, _MCISTR_int);
735             ITEM2("nominal", "gamma", MCI_DGV_STATUS_GAMMA|MCI_DGV_STATUS_NOMINAL, _MCISTR_int);
736             ITEM2("nominal", "sharpness", MCI_DGV_STATUS_SHARPNESS|MCI_DGV_STATUS_NOMINAL, _MCISTR_int);
737             ITEM2("nominal", "tint", MCI_DGV_STATUS_TINT|MCI_DGV_STATUS_NOMINAL, _MCISTR_int);
738             ITEM2("nominal", "treble", MCI_DGV_STATUS_TREBLE|MCI_DGV_STATUS_NOMINAL, _MCISTR_int);
739             ITEM3("nominal", "frame", "rate", MCI_DGV_STATUS_FRAME_RATE|MCI_DGV_STATUS_NOMINAL, _MCISTR_int);
740             ITEM4("nominal", "record", "frame", "rate", MCI_DGV_STATUS_FRAME_RATE|MCI_DGV_STATUS_RECORD|MCI_DGV_STATUS_NOMINAL, _MCISTR_int);
741             ITEM2("output", "bass", MCI_DGV_STATUS_BASS|MCI_DGV_STATUS_OUTPUT, _MCISTR_int);
742             ITEM2("output", "brightness", MCI_DGV_STATUS_BRIGHTNESS|MCI_DGV_STATUS_OUTPUT, _MCISTR_int);
743             ITEM2("output", "color", MCI_DGV_STATUS_COLOR|MCI_DGV_STATUS_OUTPUT, _MCISTR_int);
744             ITEM2("output", "contrast", MCI_DGV_STATUS_CONTRAST|MCI_DGV_STATUS_OUTPUT, _MCISTR_int);
745             ITEM2("output", "gamma", MCI_DGV_STATUS_GAMMA|MCI_DGV_STATUS_OUTPUT, _MCISTR_int);
746             ITEM2("output", "sharpness", MCI_DGV_STATUS_SHARPNESS|MCI_DGV_STATUS_OUTPUT, _MCISTR_int);
747             ITEM2("output", "tint", MCI_DGV_STATUS_TINT|MCI_DGV_STATUS_OUTPUT, _MCISTR_int);
748             ITEM2("output", "treble", MCI_DGV_STATUS_TREBLE|MCI_DGV_STATUS_OUTPUT, _MCISTR_int);
749             ITEM2("palette", "handle", MCI_DGV_STATUS_HPAL, _MCISTR_int); /* FIXME? */
750             ITEM2("pause", "mode", MCI_DGV_STATUS_PAUSE_MODE, _MCISTR_bool); /* FIXME */
751 /* EPP      ITEM2("play", "speed", MCI_AVI_STATUS_LAST_PLAY_SPEED, _MCISTR_int); */
752             ITEM3("record", "frame", "rate", MCI_DGV_STATUS_FRAME_RATE|MCI_DGV_STATUS_RECORD, _MCISTR_int);
753             ITEM2("reference", "frame", MCI_DGV_STATUS_FRAME_RATE, _MCISTR_int); /* FIXME */
754             ITEM2("reserved", "size", MCI_DGV_STATUS_SIZE, _MCISTR_int);
755             ITEM2("right", "volume", MCI_DGV_STATUS_VOLUME|MCI_DGV_STATUS_RIGHT, _MCISTR_int);
756             ITEM2("seek", "exactly", MCI_DGV_STATUS_SEEK_EXACTLY, _MCISTR_bool);
757             ITEM1("sharpness", MCI_DGV_STATUS_SHARPNESS, _MCISTR_int);
758 /* EPP      ITEM1("smpte", MCI_DGV_STATUS_SMPTE, _MCISTR_smpte); */
759 /* EPP      ITEM1("speed", MCI_DGV_STATUS_SPEED, _MCISTR_speed);  */
760 /* EPP      ITEM3("still", "file", "format", MCI_DGV_STATUS_STILL_FILEFORMAT, _MCISTR_???); */
761             ITEM1("tint", MCI_DGV_STATUS_TINT, _MCISTR_int);
762             ITEM1("treble", MCI_DGV_STATUS_TREBLE, _MCISTR_int);
763             ITEM1("unsaved", MCI_DGV_STATUS_UNSAVED, _MCISTR_bool);
764             ITEM3("video", "key", "index", MCI_DGV_STATUS_KEY_INDEX, _MCISTR_int);
765             ITEM3("video", "key", "color", MCI_DGV_STATUS_KEY_COLOR, _MCISTR_bool);
766             ITEM2("video", "record", MCI_DGV_STATUS_VIDEO_RECORD, _MCISTR_bool);  /* FIXME MCI_ON/OFF */
767             ITEM3("video", "source", "number", MCI_DGV_STATUS_VIDEO_SOURCE, _MCISTR_int);
768 /* EPP      ITEM2("video", "source", MCI_DGV_STATUS_VIDEO_SOURCE, _MCISTR_videosrctype); */
769             ITEM2("video", "stream", MCI_DGV_STATUS_VIDEO_STREAM, _MCISTR_int);
770             ITEM1("video", MCI_DGV_STATUS_VIDEO, _MCISTR_bool); /* FIXME MCI_ON/OFF */
771             ITEM1("volume", MCI_DGV_STATUS_VOLUME, _MCISTR_int);
772             ITEM2("window", "handle", MCI_DGV_STATUS_HWND, _MCISTR_int);
773             ITEM2("window", "visible", MCI_DGV_STATUS_WINDOW_VISIBLE, _MCISTR_bool);
774             ITEM2("window", "minimized", MCI_DGV_STATUS_WINDOW_MINIMIZED, _MCISTR_bool);
775             ITEM2("window", "maximized",MCI_DGV_STATUS_WINDOW_MAXIMIZED , _MCISTR_bool);
776             break;
777         }
778         FIXME(mci, "unknown keyword '%s'\n", keywords[i]);
779         i++;
780     }
781     if (!pU->statusParams.dwItem) 
782         return MCIERR_MISSING_STRING_ARGUMENT;
783     
784     res = mciSendCommandA(wDevID, MCI_STATUS, dwFlags, (DWORD)pU);
785
786     if (res == 0)
787         _MCISTR_convreturn(type, pU->statusParams.dwReturn, lpstrReturnString, uReturnLength, uDevTyp, timef);
788     free(pU);
789     return res;
790 }
791 #undef ITEM1
792 #undef ITEM2
793 #undef ITEM3
794 #undef ITEM4
795
796 /* set specified parameters in respective MCI drivers
797  * Arguments:
798  *      "door open"     eject media or somesuch
799  *      "door close"    load media
800  *      "time format <timeformatname>"  "ms" "milliseconds" "msf" "hmsf" 
801  *                                      "tmsf" "SMPTE 24" "SMPTE 25" "SMPTE 30"
802  *                                      "SMPTE drop 30"
803  *      "audio [all|left|right] [on|off]" sets specified audiochannel on or off
804  *      "video [on|off]"                sets video on/off
805  * Waveform audio:
806  *      "formattag pcm"         sets format to pcm
807  *      "formattag <nr>"        sets integer formattag value
808  *      "any input"             accept input from any known source
809  *      "any output"            output to any known destination
810  *      "input <nr>"            input from source <nr>
811  *      "output <nr>"           output to destination <nr>
812  *      "channels <nr>"         sets nr of channels 
813  *      "bytespersec <nr>"      sets average bytes per second
814  *      "samplespersec <nr>"    sets average samples per second (1 sample can
815  *                              be 2 bytes!) 
816  *      "alignment <nr>"        sets the blockalignment to <nr>
817  *      "bitspersample <nr>"    sets the nr of bits per sample
818  * Sequencer:
819  *      "master [midi|file|smpte|none]" sets the midi master device
820  *      "slave [midi|file|smpte|none]" sets the midi master device
821  *      "port mapper"           midioutput to portmapper
822  *      "port <nr>"             midioutput to specified port
823  *      "tempo <nr>"            tempo of track (depends on timeformat/divtype)
824  *      "offset <nr>"           start offset?
825  */
826 static DWORD
827 MCISTR_Set(_MCISTR_PROTO_) {
828     union U {
829         MCI_SET_PARMS           setParams;
830         MCI_WAVE_SET_PARMS      wavesetParams;
831         MCI_SEQ_SET_PARMS       seqsetParams;
832     };
833     union U*    pU = xmalloc(sizeof(union U));
834     int         i, res;
835     
836     pU->setParams.dwCallback = hwndCallback;
837
838     for (i = 0; i < nrofkeywords; ) {
839         FLAG2("door", "open", MCI_SET_DOOR_OPEN);
840         FLAG2("door", "closed", MCI_SET_DOOR_CLOSED);
841         
842         if (!STRCMP(keywords[i], "time") && (i+2 < nrofkeywords) && !STRCMP(keywords[i+1], "format")) {
843             dwFlags |= MCI_SET_TIME_FORMAT;
844             
845             /* FIXME:is this a shortcut for milliseconds or
846              *   minutes:seconds? */
847             if (!STRCMP(keywords[i+2], "ms"))
848                 pU->setParams.dwTimeFormat = MCI_FORMAT_MILLISECONDS;
849             
850             if (!STRCMP(keywords[i+2], "milliseconds"))
851                 pU->setParams.dwTimeFormat = MCI_FORMAT_MILLISECONDS;
852             if (!STRCMP(keywords[i+2], "msf"))
853                 pU->setParams.dwTimeFormat = MCI_FORMAT_MSF;
854             if (!STRCMP(keywords[i+2], "hms"))
855                 pU->setParams.dwTimeFormat = MCI_FORMAT_HMS;
856             if (!STRCMP(keywords[i+2], "frames"))
857                 pU->setParams.dwTimeFormat = MCI_FORMAT_FRAMES;
858             if (!STRCMP(keywords[i+2], "track"))
859                 pU->setParams.dwTimeFormat = MCI_VD_FORMAT_TRACK;
860             if (!STRCMP(keywords[i+2], "bytes"))
861                 pU->setParams.dwTimeFormat = MCI_FORMAT_BYTES;
862             if (!STRCMP(keywords[i+2], "samples"))
863                 pU->setParams.dwTimeFormat = MCI_FORMAT_SAMPLES;
864             if (!STRCMP(keywords[i+2], "tmsf"))
865                 pU->setParams.dwTimeFormat = MCI_FORMAT_TMSF;
866             if (        !STRCMP(keywords[i+2], "song") && 
867                         (i+3 < nrofkeywords) &&
868                         !STRCMP(keywords[i+3], "pointer")
869                         )
870                 pU->setParams.dwTimeFormat = MCI_SEQ_FORMAT_SONGPTR;
871             if (!STRCMP(keywords[i+2], "smpte") && (i+3 < nrofkeywords)) {
872                 if (!STRCMP(keywords[i+3], "24"))
873                     pU->setParams.dwTimeFormat = MCI_FORMAT_SMPTE_24;
874                 if (!STRCMP(keywords[i+3], "25"))
875                     pU->setParams.dwTimeFormat = MCI_FORMAT_SMPTE_25;
876                 if (!STRCMP(keywords[i+3], "30"))
877                     pU->setParams.dwTimeFormat = MCI_FORMAT_SMPTE_30;
878                 if (!STRCMP(keywords[i+3], "drop") && (i+4 < nrofkeywords) && !STRCMP(keywords[i+4], "30")) {
879                     pU->setParams.dwTimeFormat = MCI_FORMAT_SMPTE_30DROP;
880                     i++;
881                 }
882                 i++;
883                                 /*FALLTHROUGH*/
884             }
885             i += 3;
886             continue;
887         }
888         if (!STRCMP(keywords[i], "audio") && (i+1 < nrofkeywords)) {
889             dwFlags |= MCI_SET_AUDIO;
890             if (!STRCMP(keywords[i+1], "all"))
891                 pU->setParams.dwAudio = MCI_SET_AUDIO_ALL;
892             if (!STRCMP(keywords[i+1], "left"))
893                 pU->setParams.dwAudio = MCI_SET_AUDIO_LEFT;
894             if (!STRCMP(keywords[i+1], "right"))
895                 pU->setParams.dwAudio = MCI_SET_AUDIO_RIGHT;
896             i += 2;
897             continue;
898         }
899         FLAG1("video", MCI_SET_VIDEO);
900         FLAG1("on", MCI_SET_ON);
901         FLAG1("off", MCI_SET_OFF);
902         switch (uDevTyp) {
903         case MCI_DEVTYPE_WAVEFORM_AUDIO:
904             FLAG2("any", "input", MCI_WAVE_SET_ANYINPUT);
905             FLAG2("any", "output", MCI_WAVE_SET_ANYOUTPUT);
906             
907             if (        !STRCMP(keywords[i], "formattag") && 
908                         (i+1 < nrofkeywords) &&
909                         !STRCMP(keywords[i+1], "pcm")
910                         ) {
911                 dwFlags |= MCI_WAVE_SET_FORMATTAG;
912                 pU->wavesetParams.wFormatTag = WAVE_FORMAT_PCM;
913                 i += 2;
914                 continue;
915             }
916             
917             /* <keyword> <integer> */
918 #define WII(str,flag,fmt,element)               \
919     if (!STRCMP(keywords[i], str) &&            \
920         (i+1 < nrofkeywords)) {                 \
921         sscanf(keywords[i+1], fmt,              \
922                &(pU->wavesetParams. element));  \
923         dwFlags |= flag;                        \
924         i += 2;                                 \
925         continue;                               \
926     }
927             WII("formattag", MCI_WAVE_SET_FORMATTAG, "%u", wFormatTag);
928             WII("channels", MCI_WAVE_SET_CHANNELS, "%u", nChannels);
929             WII("bytespersec", MCI_WAVE_SET_AVGBYTESPERSEC, "%lu", nAvgBytesPerSec);
930             WII("samplespersec", MCI_WAVE_SET_SAMPLESPERSEC, "%lu", nSamplesPerSec);
931             WII("alignment", MCI_WAVE_SET_BLOCKALIGN, "%u", nBlockAlign);
932             WII("bitspersample", MCI_WAVE_SET_BITSPERSAMPLE, "%u", wBitsPerSample);
933             WII("input", MCI_WAVE_INPUT, "%u", wInput);
934             WII("output", MCI_WAVE_OUTPUT, "%u", wOutput);
935 #undef WII
936             break;
937         case MCI_DEVTYPE_SEQUENCER:
938             if (!STRCMP(keywords[i], "master") && (i+1 < nrofkeywords)) {
939                 dwFlags |= MCI_SEQ_SET_MASTER;
940                 if (!STRCMP(keywords[i+1], "midi"))
941                     pU->seqsetParams.dwMaster = MCI_SEQ_MIDI;
942                 if (!STRCMP(keywords[i+1], "file"))
943                     pU->seqsetParams.dwMaster = MCI_SEQ_FILE;
944                 if (!STRCMP(keywords[i+1], "smpte"))
945                     pU->seqsetParams.dwMaster = MCI_SEQ_SMPTE;
946                 if (!STRCMP(keywords[i+1], "none"))
947                     pU->seqsetParams.dwMaster = MCI_SEQ_NONE;
948                 i += 2;
949                 continue;
950             }
951             if (!STRCMP(keywords[i], "slave") && (i+1 < nrofkeywords)) {
952                 dwFlags |= MCI_SEQ_SET_SLAVE;
953                 if (!STRCMP(keywords[i+1], "midi"))
954                     pU->seqsetParams.dwMaster = MCI_SEQ_MIDI;
955                 if (!STRCMP(keywords[i+1], "file"))
956                     pU->seqsetParams.dwMaster = MCI_SEQ_FILE;
957                 if (!STRCMP(keywords[i+1], "smpte"))
958                     pU->seqsetParams.dwMaster = MCI_SEQ_SMPTE;
959                 if (!STRCMP(keywords[i+1], "none"))
960                     pU->seqsetParams.dwMaster = MCI_SEQ_NONE;
961                 i += 2;
962                 continue;
963             }
964             if (        !STRCMP(keywords[i], "port") && 
965                         (i+1 < nrofkeywords) &&
966                         !STRCMP(keywords[i+1], "mapper")
967                         ) {
968                 pU->seqsetParams.dwPort=-1;/* FIXME:not sure*/
969                 dwFlags |= MCI_SEQ_SET_PORT;
970                 i += 2;
971                 continue;
972             }
973 #define SII(str,flag,element)                   \
974         if (!STRCMP(keywords[i], str) &&        \
975              (i+1 < nrofkeywords)) {            \
976             sscanf(keywords[i+1], "%ld",        \
977                    &(pU->seqsetParams.element));\
978             dwFlags |= flag;                    \
979             i += 2;                             \
980             continue;                           \
981         }
982             SII("tempo", MCI_SEQ_SET_TEMPO, dwTempo);
983             SII("port", MCI_SEQ_SET_PORT, dwPort);
984             SII("offset", MCI_SEQ_SET_PORT, dwOffset);
985 #undef SII
986         }
987         i++;
988     }
989     if (!dwFlags)
990         return MCIERR_MISSING_STRING_ARGUMENT;
991     res = mciSendCommandA(wDevID, MCI_SET, dwFlags, (DWORD)pU);
992     free(pU);
993     return res;
994 }
995
996 /* specify break key
997  * Arguments: 
998  *      "off"           disable break
999  *      "on <keyid>"    enable break on key with keyid
1000  * (I strongly suspect, that there is another parameter:
1001  *      "window <handle>"       
1002  * but I don't see it mentioned in my documentation.
1003  * Returns nothing.
1004  */
1005 static DWORD
1006 MCISTR_Break(_MCISTR_PROTO_)
1007 {
1008     LPMCI_BREAK_PARMS   breakParams = xmalloc(sizeof(MCI_BREAK_PARMS));
1009     int                 res, i;
1010     
1011     if (!breakParams) return 0;
1012
1013     breakParams->dwCallback = hwndCallback;
1014     /*breakParams.hwndBreak ? */
1015     for (i = 0; i < nrofkeywords; i++) {
1016         FLAG1("off", MCI_BREAK_OFF);
1017         if (!STRCMP(keywords[i], "on") && (nrofkeywords > i+1)) {
1018             dwFlags &= ~MCI_BREAK_OFF;
1019             dwFlags |= MCI_BREAK_KEY;
1020             sscanf(keywords[i+1], "%d", &(breakParams->nVirtKey));
1021             i += 2;
1022             continue;
1023         }
1024     }
1025     res = mciSendCommandA(wDevID, MCI_BREAK, dwFlags, (DWORD)breakParams);
1026     free(breakParams);
1027     return res;
1028 }
1029
1030 #define ITEM1(str, item, xtype)                 \
1031         if (!STRCMP(keywords[i], str)) {        \
1032                 gdcParams->dwItem = item;       \
1033                 type = xtype;                   \
1034                 i++;                            \
1035                 continue;                       \
1036         }
1037 #define ITEM2(str1, str2, item, xtype)          \
1038         if (    !STRCMP(keywords[i], str1) &&   \
1039                 (i+1 < nrofkeywords) &&         \
1040                 !STRCMP(keywords[i+1], str2)) { \
1041                 gdcParams->dwItem = item;       \
1042                 type = xtype;                   \
1043                 i += 2;                         \
1044                 continue;                       \
1045         }
1046 #define ITEM3(str1, str2, str3, item, xtype)    \
1047         if (    !STRCMP(keywords[i], str1) &&   \
1048                 (i+2 < nrofkeywords) &&         \
1049                 !STRCMP(keywords[i+1], str2) && \
1050                 !STRCMP(keywords[i+2], str3)) { \
1051                 gdcParams->dwItem = item;       \
1052                 type = xtype;                   \
1053                 i += 3;                         \
1054                 continue;                       \
1055         }
1056
1057 /* get device capabilities of MCI drivers
1058  * Arguments:
1059  * Generic:
1060  *      "device type"           returns device name as string
1061  *      "has audio"             returns bool
1062  *      "has video"             returns bool
1063  *      "uses files"            returns bool
1064  *      "compound device"       returns bool
1065  *      "can record"            returns bool
1066  *      "can play"              returns bool
1067  *      "can eject"             returns bool
1068  *      "can save"              returns bool
1069  * Animation:
1070  *      "palettes"              returns nr of available palette entries
1071  *      "windows"               returns nr of available windows
1072  *      "can reverse"           returns bool
1073  *      "can stretch"           returns bool
1074  *      "slow play rate"        returns the slow playrate
1075  *      "fast play rate"        returns the fast playrate
1076  *      "normal play rate"      returns the normal playrate
1077  * Digital video
1078  *      "can freeze"            returns bool
1079  *      "can lock"              returns bool
1080  *      "can reverse"           returns bool
1081  *      "can stretch"           returns bool
1082  *      "can stretch input"     returns bool
1083  *      "can test"              returns bool
1084  *      "has still"             returns bool
1085  *      "maximum play rate"     returns the maximum play rate, in fps
1086  *      "minimum play rate"     returns the minimum play rate, in fps
1087  *      "uses files"            returns bool
1088  *      "uses palettes"         returns bool
1089  *      "windows"               returns nr of available windows
1090  * Overlay:
1091  *      "windows"               returns nr of available windows
1092  *      "can stretch"           returns bool
1093  *      "can freeze"            returns bool
1094  * Videodisc:
1095  *      "cav"                   assume CAV discs (default if no disk inserted)
1096  *      "clv"                   assume CLV discs 
1097  *      "can reverse"           returns bool
1098  *      "slow play rate"        returns the slow playrate
1099  *      "fast play rate"        returns the fast playrate
1100  *      "normal play rate"      returns the normal playrate
1101  * Waveform audio:
1102  *      "inputs"                returns nr of inputdevices
1103  *      "outputs"               returns nr of outputdevices
1104  */
1105 static DWORD
1106 MCISTR_Capability(_MCISTR_PROTO_) {
1107     MCI_GETDEVCAPS_PARMS *gdcParams = xmalloc(sizeof(MCI_GETDEVCAPS_PARMS));
1108     int type=0, i, res;
1109     
1110     gdcParams->dwCallback = hwndCallback;
1111     if (!nrofkeywords)
1112         return MCIERR_MISSING_STRING_ARGUMENT;
1113     /* well , thats default */
1114     dwFlags |= MCI_GETDEVCAPS_ITEM;
1115     gdcParams->dwItem = 0;
1116
1117     for (i = 0; i < nrofkeywords; i++) {
1118         ITEM2("device", "type", MCI_GETDEVCAPS_DEVICE_TYPE, _MCISTR_devtype);
1119         ITEM2("has", "audio", MCI_GETDEVCAPS_HAS_AUDIO, _MCISTR_bool);
1120         ITEM2("has", "video", MCI_GETDEVCAPS_HAS_VIDEO, _MCISTR_bool);
1121         ITEM2("uses", "files", MCI_GETDEVCAPS_USES_FILES, _MCISTR_bool);
1122         ITEM2("compound", "device", MCI_GETDEVCAPS_COMPOUND_DEVICE, _MCISTR_bool);
1123         ITEM2("can", "record", MCI_GETDEVCAPS_CAN_RECORD, _MCISTR_bool);
1124         ITEM2("can", "play", MCI_GETDEVCAPS_CAN_PLAY, _MCISTR_bool);
1125         ITEM2("can", "eject", MCI_GETDEVCAPS_CAN_EJECT, _MCISTR_bool);
1126         ITEM2("can", "save", MCI_GETDEVCAPS_CAN_SAVE, _MCISTR_bool);
1127         switch (uDevTyp) {
1128         case MCI_DEVTYPE_ANIMATION:
1129             ITEM1("palettes", MCI_ANIM_GETDEVCAPS_PALETTES, _MCISTR_int);
1130             ITEM1("windows", MCI_ANIM_GETDEVCAPS_MAX_WINDOWS, _MCISTR_int);
1131             ITEM2("can", "reverse", MCI_ANIM_GETDEVCAPS_CAN_REVERSE, _MCISTR_bool);
1132             ITEM2("can", "stretch", MCI_ANIM_GETDEVCAPS_CAN_STRETCH, _MCISTR_bool);
1133             ITEM3("slow", "play", "rate", MCI_ANIM_GETDEVCAPS_SLOW_RATE, _MCISTR_int);
1134             ITEM3("fast", "play", "rate", MCI_ANIM_GETDEVCAPS_FAST_RATE, _MCISTR_int);
1135             ITEM3("normal", "play", "rate", MCI_ANIM_GETDEVCAPS_NORMAL_RATE, _MCISTR_int);
1136             break;
1137         case MCI_DEVTYPE_DIGITAL_VIDEO:
1138             ITEM2("can", "freeze", MCI_DGV_GETDEVCAPS_CAN_FREEZE, _MCISTR_bool);
1139             ITEM2("can", "lock", MCI_DGV_GETDEVCAPS_CAN_LOCK, _MCISTR_bool);
1140             ITEM2("can", "reverse", MCI_DGV_GETDEVCAPS_CAN_REVERSE, _MCISTR_bool);
1141             ITEM3("can", "stretch", "input", MCI_DGV_GETDEVCAPS_CAN_STR_IN, _MCISTR_bool);
1142             ITEM2("can", "stretch", MCI_DGV_GETDEVCAPS_CAN_STRETCH, _MCISTR_bool);
1143             ITEM2("can", "test", MCI_DGV_GETDEVCAPS_CAN_TEST, _MCISTR_bool);
1144             ITEM2("has", "still", MCI_DGV_GETDEVCAPS_HAS_STILL, _MCISTR_bool);
1145             ITEM3("maximum", "play", "rate", MCI_DGV_GETDEVCAPS_MAXIMUM_RATE, _MCISTR_int);
1146             ITEM3("minimum", "play", "rate", MCI_DGV_GETDEVCAPS_MINIMUM_RATE, _MCISTR_int);
1147             ITEM2("uses", "files", MCI_GETDEVCAPS_USES_FILES, _MCISTR_bool);
1148             ITEM2("uses", "palettes", MCI_DGV_GETDEVCAPS_PALETTES, _MCISTR_bool);
1149             ITEM1("windows", MCI_DGV_GETDEVCAPS_MAX_WINDOWS, _MCISTR_int);
1150             break;
1151         case MCI_DEVTYPE_OVERLAY:
1152             ITEM1("windows", MCI_OVLY_GETDEVCAPS_MAX_WINDOWS, _MCISTR_int);
1153             ITEM2("can", "freeze", MCI_OVLY_GETDEVCAPS_CAN_FREEZE, _MCISTR_bool);
1154             ITEM2("can", "stretch", MCI_OVLY_GETDEVCAPS_CAN_STRETCH, _MCISTR_bool);
1155             break;
1156         case MCI_DEVTYPE_VIDEODISC:
1157             FLAG1("cav", MCI_VD_GETDEVCAPS_CAV);
1158             FLAG1("clv", MCI_VD_GETDEVCAPS_CLV);
1159             ITEM2("can", "reverse", MCI_VD_GETDEVCAPS_CAN_REVERSE, _MCISTR_bool);
1160             ITEM3("slow", "play", "rate", MCI_VD_GETDEVCAPS_SLOW_RATE, _MCISTR_int);
1161             ITEM3("fast", "play", "rate", MCI_VD_GETDEVCAPS_FAST_RATE, _MCISTR_int);
1162             ITEM3("normal", "play", "rate", MCI_VD_GETDEVCAPS_NORMAL_RATE, _MCISTR_int);
1163             break;
1164         case MCI_DEVTYPE_WAVEFORM_AUDIO:
1165             ITEM1("inputs", MCI_WAVE_GETDEVCAPS_INPUTS, _MCISTR_int);
1166             ITEM1("outputs", MCI_WAVE_GETDEVCAPS_OUTPUTS, _MCISTR_int);
1167             break;
1168         }
1169     }
1170     res = mciSendCommandA(wDevID, MCI_GETDEVCAPS, dwFlags, (DWORD)gdcParams);
1171
1172     /* no timeformat needed */
1173     if (res == 0)
1174         _MCISTR_convreturn(type, gdcParams->dwReturn, lpstrReturnString,
1175                             uReturnLength, uDevTyp, 0);
1176     free(gdcParams);
1177     return res;
1178 }
1179 #undef ITEM1
1180 #undef ITEM2
1181 #undef ITEM3
1182 /* resumes operation of device. no arguments, no return values */
1183 static DWORD
1184 MCISTR_Resume(_MCISTR_PROTO_)
1185 {
1186     MCI_GENERIC_PARMS*  genParams = xmalloc(sizeof(MCI_GENERIC_PARMS));
1187     int                 res;
1188
1189     genParams->dwCallback = hwndCallback;
1190     res = mciSendCommandA(wDevID, MCI_RESUME, dwFlags, (DWORD)genParams);
1191     free(genParams);
1192     return res;
1193 }
1194
1195 /* pauses operation of device. no arguments, no return values */
1196 static DWORD
1197 MCISTR_Pause(_MCISTR_PROTO_)
1198 {
1199     MCI_GENERIC_PARMS*  genParams = xmalloc(sizeof(MCI_GENERIC_PARMS));
1200     int                 res;
1201
1202     genParams->dwCallback = hwndCallback;
1203     res = mciSendCommandA(wDevID, MCI_PAUSE, dwFlags, (DWORD)genParams);
1204     free(genParams);
1205     return res;
1206 }
1207
1208 /* stops operation of device. no arguments, no return values */
1209 static DWORD
1210 MCISTR_Stop(_MCISTR_PROTO_)
1211 {
1212     MCI_GENERIC_PARMS*  genParams = xmalloc(sizeof(MCI_GENERIC_PARMS));
1213     int                 res;
1214
1215     genParams->dwCallback = hwndCallback;
1216     res = mciSendCommandA(wDevID, MCI_STOP, dwFlags, (DWORD)genParams);
1217     free(genParams);
1218     return res;
1219 }
1220
1221 /* starts recording.
1222  * Arguments:
1223  *      "overwrite"     overwrite existing things
1224  *      "insert"        insert at current position
1225  *      "to <time>"     record up to <time> (specified in timeformat)
1226  *      "from <time>"   record from <time> (specified in timeformat)
1227  */
1228 static DWORD
1229 MCISTR_Record(_MCISTR_PROTO_) {
1230     int                 i, res, timef, nrargs, j, k, a[4];
1231     char                        *parsestr;
1232     MCI_RECORD_PARMS    *recordParams = xmalloc(sizeof(MCI_RECORD_PARMS));
1233     
1234     res = _MCISTR_determine_timeformat(dev, wDevID, uDevTyp, &timef);
1235     if (res) return res;
1236     
1237     switch (timef) {
1238     case MCI_FORMAT_MILLISECONDS:
1239     case MCI_FORMAT_FRAMES:
1240     case MCI_FORMAT_BYTES:
1241     case MCI_FORMAT_SAMPLES:
1242         nrargs = 1;
1243         parsestr = "%d";
1244         break;
1245     case MCI_FORMAT_HMS:
1246     case MCI_FORMAT_MSF:
1247         parsestr="%d:%d:%d";
1248         nrargs=3;
1249         break;
1250     case MCI_FORMAT_TMSF:
1251         parsestr="%d:%d:%d:%d";
1252         nrargs=4;
1253         break;
1254     default:FIXME(mci, "unknown timeformat %d, please report.\n", timef);
1255         parsestr="%d";
1256         nrargs=1;
1257         break;
1258     }
1259     recordParams->dwCallback = hwndCallback;
1260     for (i = 0; i < nrofkeywords; ) {
1261         if (!strcmp(keywords[i], "to") && (i+1 < nrofkeywords)) {
1262             dwFlags |= MCI_TO;
1263             a[0] = a[1] = a[2] = a[3] = 0;
1264             j=sscanf(keywords[i+1], parsestr, &a[0], &a[1], &a[2], &a[3]);
1265             /* add up all integers we got, if we have more 
1266              * shift them. (Well I should use the macros in 
1267              * mmsystem.h, right).
1268              */
1269             recordParams->dwTo = 0;
1270             for (k = 0; k < j; k++)
1271                 recordParams->dwTo += a[k] << (8*(nrargs-k));
1272             i += 2;
1273             continue;
1274         }
1275         if (!strcmp(keywords[i], "from") && (i+1 < nrofkeywords)) {
1276             dwFlags |= MCI_FROM;
1277             a[0] = a[1] = a[2] = a[3] = 0;
1278             j = sscanf(keywords[i+1], parsestr, &a[0], &a[1], &a[2], &a[3]);
1279             /* dito. */
1280             recordParams->dwFrom = 0;
1281             for (k = 0; k < j; k++)
1282                 recordParams->dwFrom += a[k]<<(8*(nrargs-k));
1283             i += 2;
1284             continue;
1285         }
1286         FLAG1("insert", MCI_RECORD_INSERT);
1287         FLAG1("overwrite", MCI_RECORD_OVERWRITE);
1288         i++;
1289     }
1290     res = mciSendCommandA(wDevID, MCI_RECORD, dwFlags, (DWORD)recordParams);
1291     free(recordParams);
1292     return res;
1293 }
1294
1295 /* play media
1296  * Arguments:
1297  *      "to <time>"     play up to <time> (specified in set timeformat)
1298  *      "from <time>"   play from <time> (specified in set timeformat)
1299  * Animation:
1300  *      "slow"          play slow
1301  *      "fast"          play fast 
1302  *      "scan"          play as fast as possible (with audio disabled perhaps)
1303  *      "reverse"       play reverse
1304  *      "speed <fps>"   play with specified frames per second
1305  * Videodisc:
1306  *      "slow"          play slow
1307  *      "fast"          play fast 
1308  *      "scan"          play as fast as possible (with audio disabled perhaps)
1309  *      "reverse"       play reverse
1310  *      "speed <fps>"   play with specified frames per second
1311  * Digitalvideo:
1312  *      "fullscreen"
1313  *      "repeat"
1314  *      "reverse"
1315  *      "window"
1316  */
1317 static DWORD
1318 MCISTR_Play(_MCISTR_PROTO_) {
1319     int                 i, res, timef, nrargs, j, k, a[4];
1320     char                        *parsestr;
1321     union U {
1322         MCI_PLAY_PARMS          playParams;
1323         MCI_VD_PLAY_PARMS       vdplayParams;
1324         MCI_ANIM_PLAY_PARMS     animplayParams;
1325         MCI_DGV_PLAY_PARMS      dgvplayParams;
1326     };
1327     union U *pU = xmalloc(sizeof(union U));
1328     
1329     res = _MCISTR_determine_timeformat(dev, wDevID, uDevTyp, &timef);
1330     if (res) return res;
1331     switch (timef) {
1332     case MCI_FORMAT_MILLISECONDS:
1333     case MCI_FORMAT_FRAMES:
1334     case MCI_FORMAT_BYTES:
1335     case MCI_FORMAT_SAMPLES:
1336         nrargs=1;
1337         parsestr="%d";
1338         break;
1339     case MCI_FORMAT_HMS:
1340     case MCI_FORMAT_MSF:
1341         parsestr="%d:%d:%d";
1342         nrargs=3;
1343         break;
1344     case MCI_FORMAT_TMSF:
1345         parsestr="%d:%d:%d:%d";
1346         nrargs=4;
1347         break;
1348     default:FIXME(mci, "unknown timeformat %d, please report.\n", timef);
1349         parsestr="%d";
1350         nrargs=1;
1351         break;
1352     }
1353     pU->playParams.dwCallback = hwndCallback;
1354     for (i = 0; i < nrofkeywords; ) {
1355         if (!strcmp(keywords[i], "to") && (i+1 < nrofkeywords)) {
1356             dwFlags |= MCI_TO;
1357             a[0]=a[1]=a[2]=a[3]=0;
1358             j=sscanf(keywords[i+1], parsestr, &a[0], &a[1], &a[2], &a[3]);
1359             /* add up all integers we got, if we have more 
1360              * shift them. (Well I should use the macros in 
1361              * mmsystem.h, right).
1362              */
1363             pU->playParams.dwTo=0;
1364             for (k = 0; k < j; k++)
1365                 pU->playParams.dwTo += a[k] << (8*(nrargs-k));
1366             i += 2;
1367             continue;
1368         }
1369         if (!strcmp(keywords[i], "from") && (i+1 < nrofkeywords)) {
1370             dwFlags |= MCI_FROM;
1371             a[0]=a[1]=a[2]=a[3]=0;
1372             j=sscanf(keywords[i+1], parsestr, &a[0], &a[1], &a[2], &a[3]);
1373             /* dito. */
1374             pU->playParams.dwFrom=0;
1375             for (k = 0; k < j; k++)
1376                 pU->playParams.dwFrom += a[k]<<(8*(nrargs-k));
1377             i += 2;
1378             continue;
1379         }
1380         switch (uDevTyp) {
1381         case MCI_DEVTYPE_VIDEODISC:
1382             FLAG1("slow", MCI_VD_PLAY_SLOW);
1383             FLAG1("fast", MCI_VD_PLAY_FAST);
1384             FLAG1("scan", MCI_VD_PLAY_SCAN);
1385             FLAG1("reverse", MCI_VD_PLAY_REVERSE);
1386             if (!STRCMP(keywords[i], "speed") && (i+1 < nrofkeywords)) {
1387                 dwFlags |= MCI_VD_PLAY_SPEED;
1388                 sscanf(keywords[i+1], "%ld", &(pU->vdplayParams.dwSpeed));
1389                 i += 2;
1390                 continue;
1391             }
1392             break;
1393         case MCI_DEVTYPE_ANIMATION:
1394             FLAG1("slow", MCI_ANIM_PLAY_SLOW);
1395             FLAG1("fast", MCI_ANIM_PLAY_FAST);
1396             FLAG1("scan", MCI_ANIM_PLAY_SCAN);
1397             FLAG1("reverse", MCI_ANIM_PLAY_REVERSE);
1398             if (!STRCMP(keywords[i], "speed") && (i+1 < nrofkeywords)) {
1399                 dwFlags |= MCI_ANIM_PLAY_SPEED;
1400                 sscanf(keywords[i+1], "%ld", &(pU->animplayParams.dwSpeed));
1401                 i += 2;
1402                 continue;
1403             }
1404             break;
1405         case MCI_DEVTYPE_DIGITAL_VIDEO:
1406 /* EPP      FLAG1("fullscreen", MCI_MCIAVI_PLAY_FULLSCREEN); */
1407             FLAG1("repeat", MCI_DGV_PLAY_REPEAT);
1408             FLAG1("reverse", MCI_DGV_PLAY_REVERSE);
1409 /* EPP      FLAG1("window", MCI_MCIAVI_PLAY_WINDOW); */
1410             break;
1411         }
1412         i++;
1413     }
1414     res = mciSendCommandA(wDevID, MCI_PLAY, dwFlags, (DWORD)pU);
1415     free(pU);
1416     return res;
1417 }
1418
1419 /* seek to a specified position
1420  * Arguments:
1421  *      "to start"      seek to start of medium
1422  *      "to end"        seek to end of medium
1423  *      "to <time>"     seek to <time> specified in current timeformat
1424  */
1425 static DWORD
1426 MCISTR_Seek(_MCISTR_PROTO_) {
1427     int                 i, res, timef, nrargs, j, k, a[4];
1428     char                *parsestr;
1429     MCI_SEEK_PARMS      *seekParams = xmalloc(sizeof(MCI_SEEK_PARMS));
1430     
1431     res = _MCISTR_determine_timeformat(dev, wDevID, uDevTyp, &timef);
1432     if (res) return res;
1433     switch (timef) {
1434     case MCI_FORMAT_MILLISECONDS:
1435     case MCI_FORMAT_FRAMES:
1436     case MCI_FORMAT_BYTES:
1437     case MCI_FORMAT_SAMPLES:
1438         nrargs=1;
1439         parsestr="%d";
1440         break;
1441     case MCI_FORMAT_HMS:
1442     case MCI_FORMAT_MSF:
1443         parsestr="%d:%d:%d";
1444         nrargs=3;
1445         break;
1446     case MCI_FORMAT_TMSF:
1447         parsestr="%d:%d:%d:%d";
1448         nrargs=4;
1449         break;
1450     default:
1451         FIXME(mci, "unknown timeformat %d, please report.\n", timef);
1452         parsestr="%d";
1453         nrargs=1;
1454         break;
1455     }
1456     seekParams->dwCallback = hwndCallback;
1457     for (i = 0; i < nrofkeywords; ) {
1458         if (!STRCMP(keywords[i], "to") && (i+1 < nrofkeywords)) {
1459             if (!STRCMP(keywords[i+1], "start")) {
1460                 dwFlags |= MCI_SEEK_TO_START;
1461                 seekParams->dwTo = 0;
1462                 i += 2;
1463                 continue;
1464             }
1465             if (!STRCMP(keywords[i+1], "end")) {
1466                 dwFlags |= MCI_SEEK_TO_END;
1467                 seekParams->dwTo = 0;
1468                 i += 2;
1469                 continue;
1470             }
1471             dwFlags |= MCI_TO;
1472             i += 2;
1473             a[0] = a[1] = a[2] = a[3] = 0;
1474             j = sscanf(keywords[i+1], parsestr, &a[0], &a[1], &a[2], &a[3]);
1475             seekParams->dwTo = 0;
1476             for (k = 0; k < j; k++)
1477                 seekParams->dwTo += a[k] << (8*(nrargs-k));
1478             continue;
1479         }
1480         switch (uDevTyp) {
1481         case MCI_DEVTYPE_VIDEODISC:
1482             FLAG1("reverse", MCI_VD_SEEK_REVERSE);
1483             break;
1484         }
1485         i++;
1486     }
1487     res = mciSendCommandA(wDevID, MCI_SEEK, dwFlags, (DWORD)seekParams);
1488     free(seekParams);
1489     return res;
1490 }
1491
1492 /* close media/driver */
1493 static DWORD
1494 MCISTR_Close(_MCISTR_PROTO_)
1495 {
1496     MCI_GENERIC_PARMS*  closeParams = xmalloc(sizeof(MCI_GENERIC_PARMS));
1497     int                 res;
1498     
1499     closeParams->dwCallback = hwndCallback;
1500     res = mciSendCommandA(wDevID, MCI_CLOSE, dwFlags, (DWORD)closeParams);
1501     free(closeParams);
1502     return res;
1503 }
1504
1505 /* return information.
1506  * Arguments:
1507  *      "product"       return product name (human readable)
1508  *      "file"          return filename
1509  * Animation:
1510  *      "text"          returns text?
1511  * Overlay:
1512  *      "text"          returns text?
1513  * Digitalvideo
1514  *      "audio algorithm"
1515  *      "audio quality"
1516  *      "still algorithm"
1517  *      "still quality"
1518  *      "usage"
1519  *      "version"
1520  *      "video algorithm"
1521  *      "video quality"
1522  *      "window text"
1523  */
1524 static DWORD
1525 MCISTR_Info(_MCISTR_PROTO_)
1526 {
1527     union U {
1528         MCI_INFO_PARMSA         infoParams;
1529         MCI_DGV_INFO_PARMSA     dgvinfoParams;
1530     };
1531     union U*            pU = xmalloc(sizeof(union U));
1532     DWORD               sflags;
1533     int                 i, res;
1534     
1535     sflags = dwFlags;
1536     for (i = 0; i < nrofkeywords; i++) {
1537         FLAG1("product", MCI_INFO_PRODUCT);
1538         FLAG1("file", MCI_INFO_FILE);
1539         switch (uDevTyp) {
1540         case MCI_DEVTYPE_ANIMATION:
1541             FLAG2("window", "text", MCI_ANIM_INFO_TEXT);
1542             break;
1543         case MCI_DEVTYPE_OVERLAY:
1544             FLAG2("window", "text", MCI_OVLY_INFO_TEXT);
1545             break;
1546         case MCI_DEVTYPE_DIGITAL_VIDEO:
1547 #define MI1(str, flag)                                  \
1548         if (!STRCMP(keywords[i], str)) {                \
1549                 dwFlags |= MCI_DGV_INFO_ITEM;           \
1550                 pU->dgvinfoParams.dwItem |= flag;       \
1551                 i++;                                    \
1552                 continue;                               \
1553         }
1554 #define MI2(str1, str2, flag)                           \
1555         if (!STRCMP(keywords[i], str1) &&               \
1556             (i+1 < nrofkeywords) &&                     \
1557             !STRCMP(keywords[i+1], str2)) {             \
1558                 dwFlags |= MCI_DGV_INFO_ITEM;           \
1559                 pU->dgvinfoParams.dwItem |= flag;       \
1560                 i += 2;                                 \
1561                 continue;                               \
1562         }
1563             MI2("audio", "algorithm", MCI_DGV_INFO_AUDIO_ALG);
1564             MI2("audio", "quality", MCI_DGV_INFO_AUDIO_QUALITY);
1565             MI2("still", "algorithm", MCI_DGV_INFO_STILL_ALG);
1566             MI2("still", "quality", MCI_DGV_INFO_STILL_QUALITY);
1567             MI1("usage", MCI_DGV_INFO_USAGE);
1568             MI1("version", MCI_INFO_VERSION );
1569             MI2("video", "algorithm", MCI_DGV_INFO_VIDEO_ALG);
1570             MI2("video", "quality", MCI_DGV_INFO_VIDEO_QUALITY);
1571             MI2("window", "text", MCI_DGV_INFO_TEXT);
1572 #undef MI1
1573 #undef MI2
1574         }
1575     }
1576     if (dwFlags == sflags)
1577         return MCIERR_MISSING_STRING_ARGUMENT;
1578     pU->infoParams.dwCallback = hwndCallback;
1579
1580     /* MCI driver will fill in lpstrReturn, dwRetSize.
1581      * FIXME: I don't know if this is correct behaviour
1582      */
1583     res = mciSendCommandA(wDevID, MCI_INFO, dwFlags, (DWORD)pU);
1584     if (res == 0)
1585         _MCI_STR(pU->infoParams.lpstrReturn);
1586     free(pU);
1587     return res;
1588 }
1589
1590 /* query MCI driver itself for information
1591  * Arguments:
1592  *      "installname"   return install name of <device> (system.ini)
1593  *      "quantity"      return nr of installed drivers
1594  *      "open"          open drivers only (additional flag)
1595  *      "name <nr>"     return nr of devices with <devicetyp>
1596  *      "name all"      return nr of all devices
1597  *
1598  */
1599 static DWORD
1600 MCISTR_Sysinfo(_MCISTR_PROTO_) {
1601     MCI_SYSINFO_PARMSA  sysinfoParams;
1602     int                 i, res;
1603     
1604     sysinfoParams.lpstrReturn   = lpstrReturnString;
1605     sysinfoParams.dwRetSize     = uReturnLength;
1606     sysinfoParams.wDeviceType   = uDevTyp;
1607     sysinfoParams.dwCallback    = hwndCallback;
1608
1609     for (i = 0; i < nrofkeywords; i++) {
1610         FLAG1("installname", MCI_SYSINFO_INSTALLNAME);
1611         FLAG1("quantity", MCI_SYSINFO_INSTALLNAME);
1612         FLAG1("open", MCI_SYSINFO_OPEN);
1613         if (!strcmp(keywords[i], "name") && (i+1 < nrofkeywords)) {
1614             sscanf(keywords[i+1], "%ld", &(sysinfoParams.dwNumber));
1615             dwFlags |= MCI_SYSINFO_NAME;
1616             i++;
1617         }
1618     }
1619     res = mciSendCommandA(0, MCI_SYSINFO, dwFlags, (DWORD)&sysinfoParams);
1620
1621     if (dwFlags & MCI_SYSINFO_QUANTITY) {
1622         char    buf[100];
1623         
1624         sprintf(buf, "%ld", *(long*)lpstrReturnString);
1625         _MCI_STR(buf);
1626     }
1627     /* no need to copy anything back, mciSysInfo did it for us */
1628     return res;
1629 }
1630
1631 /* load file
1632  * Argument: "<filename>"
1633  * Overlay: "at <left> <top> <right> <bottom>" additional
1634  */
1635 static DWORD
1636 MCISTR_Load(_MCISTR_PROTO_) {
1637     union U {
1638         MCI_LOAD_PARMSA         loadParams;
1639         MCI_OVLY_LOAD_PARMSA    ovlyloadParams;
1640     };
1641     union       U *pU = xmalloc(sizeof(union U));
1642     int         i, len = 0, res;
1643     char        *s;
1644     
1645     for (i = 0; i < nrofkeywords; ) {
1646         switch (uDevTyp) {
1647         case MCI_DEVTYPE_OVERLAY:
1648             if (!STRCMP(keywords[i], "at") && (i+4 < nrofkeywords)) {
1649                 dwFlags |= MCI_OVLY_RECT;
1650                 sscanf(keywords[i+1], "%d", &(pU->ovlyloadParams.rc.left));
1651                 sscanf(keywords[i+2], "%d", &(pU->ovlyloadParams.rc.top));
1652                 sscanf(keywords[i+3], "%d", &(pU->ovlyloadParams.rc.right));
1653                 sscanf(keywords[i+4], "%d", &(pU->ovlyloadParams.rc.bottom));
1654                 memcpy(keywords+i, keywords+(i+5), nrofkeywords-(i+5));
1655                 continue;
1656             }
1657             break;
1658         }
1659         len += strlen(keywords[i])+1;
1660         i++;
1661     }
1662     s=(char*)xmalloc(len);
1663     *s='\0';
1664     while (i < nrofkeywords) {
1665         strcat(s, keywords[i]);
1666         i++;
1667         if (i < nrofkeywords) strcat(s, " ");
1668     }
1669     pU->loadParams.lpfilename = s;
1670     pU->loadParams.dwCallback = hwndCallback;
1671     dwFlags |= MCI_LOAD_FILE;
1672     res = mciSendCommandA(wDevID, MCI_LOAD, dwFlags, (DWORD)pU);
1673     free(s);
1674     free(pU);
1675     return res;
1676 }
1677
1678 /* save to file
1679  * Argument: "<filename>"
1680  * Overlay: "at <left> <top> <right> <bottom>" additional
1681  */
1682 static DWORD
1683 MCISTR_Save(_MCISTR_PROTO_) {
1684     union U {
1685         MCI_SAVE_PARMS          saveParams;
1686         MCI_OVLY_SAVE_PARMSA    ovlysaveParams;
1687     };
1688     union U*    pU = xmalloc(sizeof(union U));
1689     int         i, len = 0, res;
1690     char*       s;
1691     
1692     for (i = 0; i < nrofkeywords; ) {
1693         switch (uDevTyp) {
1694         case MCI_DEVTYPE_OVERLAY:
1695             if (!STRCMP(keywords[i], "at") && (i+4 < nrofkeywords)) {
1696                 dwFlags |= MCI_OVLY_RECT;
1697                 sscanf(keywords[i+1], "%d", &(pU->ovlysaveParams.rc.left));
1698                 sscanf(keywords[i+2], "%d", &(pU->ovlysaveParams.rc.top));
1699                 sscanf(keywords[i+3], "%d", &(pU->ovlysaveParams.rc.right));
1700                 sscanf(keywords[i+4], "%d", &(pU->ovlysaveParams.rc.bottom));
1701                 memcpy(keywords+i, keywords+(i+5), nrofkeywords-(i+5));
1702                 continue;
1703             }
1704             break;
1705         }
1706         len += strlen(keywords[i])+1;
1707         i++;
1708     }
1709     s = (char*)xmalloc(len);
1710     *s = '\0';
1711     while (i < nrofkeywords) {
1712         strcat(s, keywords[i]);
1713         i++;
1714         if (i < nrofkeywords) strcat(s, " ");
1715     }
1716     pU->saveParams.lpfilename = s;
1717     pU->saveParams.dwCallback = hwndCallback;
1718     dwFlags |= MCI_LOAD_FILE;
1719     res = mciSendCommandA(wDevID, MCI_SAVE, dwFlags, (DWORD)pU);
1720     free(s);
1721     free(pU);
1722     return res;
1723 }
1724
1725 /* prepare device for input/output
1726  * (only applyable to waveform audio)
1727  */
1728 static DWORD
1729 MCISTR_Cue(_MCISTR_PROTO_) {
1730     MCI_GENERIC_PARMS   *cueParams = xmalloc(sizeof(MCI_GENERIC_PARMS));
1731     int                 i, res;
1732     
1733     for (i = 0; i < nrofkeywords; i++) {
1734         switch (uDevTyp) {
1735         case MCI_DEVTYPE_WAVEFORM_AUDIO:
1736             FLAG1("input", MCI_WAVE_INPUT);
1737             FLAG1("output", MCI_WAVE_OUTPUT);
1738             break;
1739         }
1740     }
1741
1742     cueParams->dwCallback = hwndCallback;
1743
1744     res = mciSendCommandA(wDevID, MCI_CUE, dwFlags, (DWORD)cueParams);
1745     free(cueParams);
1746     return res;
1747 }
1748
1749 /* delete information */
1750 static DWORD
1751 MCISTR_Delete(_MCISTR_PROTO_) {
1752     int timef, nrargs, i, j, k, a[4], res;
1753     char        *parsestr;
1754     MCI_WAVE_DELETE_PARMS *deleteParams = xmalloc(sizeof(MCI_WAVE_DELETE_PARMS));
1755     
1756     /* only implemented for waveform audio */
1757     if (uDevTyp != MCI_DEVTYPE_WAVEFORM_AUDIO)
1758         return MCIERR_UNSUPPORTED_FUNCTION; /* well it fits */
1759     res = _MCISTR_determine_timeformat(dev, wDevID, uDevTyp, &timef);
1760     if (res) return res;
1761     switch (timef) {
1762     case MCI_FORMAT_MILLISECONDS:
1763     case MCI_FORMAT_FRAMES:
1764     case MCI_FORMAT_BYTES:
1765     case MCI_FORMAT_SAMPLES:
1766         nrargs=1;
1767         parsestr="%d";
1768         break;
1769     case MCI_FORMAT_HMS:
1770     case MCI_FORMAT_MSF:
1771         parsestr="%d:%d:%d";
1772         nrargs=3;
1773         break;
1774     case MCI_FORMAT_TMSF:
1775         parsestr="%d:%d:%d:%d";
1776         nrargs=4;
1777         break;
1778     default:FIXME(mci, "unknown timeformat %d, please report.\n", timef);
1779         parsestr="%d";
1780         nrargs=1;
1781         break;
1782     }
1783     for (i = 0; i < nrofkeywords; ) {
1784         if (!strcmp(keywords[i], "to") && (i+1 < nrofkeywords)) {
1785             dwFlags |= MCI_TO;
1786             a[0] = a[1] = a[2] = a[3] = 0;
1787             j = sscanf(keywords[i+1], parsestr, &a[0], &a[1], &a[2], &a[3]);
1788             /* add up all integers we got, if we have more 
1789              * shift them. (Well I should use the macros in 
1790              * mmsystem.h, right).
1791              */
1792             deleteParams->dwTo = 0;
1793             for (k = 0;k < j;k++)
1794                 deleteParams->dwTo += a[k]<<(8*(nrargs-k));
1795             i += 2;
1796             continue;
1797         }
1798         if (!strcmp(keywords[i], "from") && (i+1 < nrofkeywords)) {
1799             dwFlags |= MCI_FROM;
1800             a[0] = a[1] = a[2] = a[3] = 0;
1801             j = sscanf(keywords[i+1], parsestr, &a[0], &a[1], &a[2], &a[3]);
1802             /* dito. */
1803             deleteParams->dwFrom = 0;
1804             for (k = 0; k < j; k++)
1805                 deleteParams->dwFrom += a[k]<<(8*(nrargs-k));
1806             i += 2;
1807             continue;
1808         }
1809         i++;
1810     }
1811     deleteParams->dwCallback = hwndCallback;
1812     res = mciSendCommandA(wDevID, MCI_DELETE, dwFlags, (DWORD)deleteParams);
1813     free(deleteParams);
1814     return res;
1815 }
1816
1817 /* send command to device. only applies to videodisc */
1818 static DWORD
1819 MCISTR_Escape(_MCISTR_PROTO_)
1820 {
1821     LPMCI_VD_ESCAPE_PARMSA      escapeParams = xmalloc(sizeof(MCI_VD_ESCAPE_PARMSA));
1822     int                 i, len = 0, res;
1823     char                *s;
1824     
1825     if (uDevTyp != MCI_DEVTYPE_VIDEODISC)
1826         return MCIERR_UNSUPPORTED_FUNCTION;
1827
1828     for (i = 0; i < nrofkeywords; i++) {
1829         len += strlen(keywords[i]) + 1;
1830     }
1831     s = (char*)malloc(len);
1832     *s = '\0';
1833     for (i = 0; i < nrofkeywords; ) {
1834         strcat(s, keywords[i]);
1835         i++;
1836         if (i < nrofkeywords) strcat(s, " ");
1837     }
1838     escapeParams->lpstrCommand = s;
1839     escapeParams->dwCallback = hwndCallback;
1840     dwFlags |= MCI_VD_ESCAPE_STRING;
1841     res = mciSendCommandA(wDevID, MCI_ESCAPE, dwFlags, (DWORD)escapeParams);
1842     free(s);
1843     free(escapeParams);
1844     return res;
1845 }
1846
1847 /* unfreeze [part of] the overlayed video 
1848  * only applyable to Overlay devices
1849  */
1850 static DWORD
1851 MCISTR_Unfreeze(_MCISTR_PROTO_)
1852 {
1853     LPMCI_OVLY_RECT_PARMS       unfreezeParams = xmalloc(sizeof(MCI_OVLY_RECT_PARMS));
1854     int                         i, res;
1855     
1856     if (uDevTyp != MCI_DEVTYPE_OVERLAY)
1857         return MCIERR_UNSUPPORTED_FUNCTION;
1858     for (i = 0; i < nrofkeywords; ) {
1859         if (!STRCMP(keywords[i], "at") && (i+4 < nrofkeywords)) {
1860             sscanf(keywords[i+1], "%d", &(unfreezeParams->rc.left));
1861             sscanf(keywords[i+2], "%d", &(unfreezeParams->rc.top));
1862             sscanf(keywords[i+3], "%d", &(unfreezeParams->rc.right));
1863             sscanf(keywords[i+4], "%d", &(unfreezeParams->rc.bottom));
1864             dwFlags |= MCI_OVLY_RECT;
1865             i += 5;
1866             continue;
1867         }
1868         i++;
1869     }
1870     unfreezeParams->dwCallback = hwndCallback;
1871     res = mciSendCommandA(wDevID, MCI_UNFREEZE, dwFlags, (DWORD)unfreezeParams);
1872     free(unfreezeParams);
1873     return res;
1874 }
1875 /* freeze [part of] the overlayed video 
1876  * only applyable to Overlay devices
1877  */
1878 static DWORD
1879 MCISTR_Freeze(_MCISTR_PROTO_)
1880 {
1881     LPMCI_OVLY_RECT_PARMS       freezeParams = xmalloc(sizeof(MCI_OVLY_RECT_PARMS));
1882     int                         i, res;
1883     
1884     if (uDevTyp != MCI_DEVTYPE_OVERLAY)
1885         return MCIERR_UNSUPPORTED_FUNCTION;
1886     for (i = 0; i < nrofkeywords; ) {
1887         if (!STRCMP(keywords[i], "at") && (i+4 < nrofkeywords)) {
1888             sscanf(keywords[i+1], "%d", &(freezeParams->rc.left));
1889             sscanf(keywords[i+2], "%d", &(freezeParams->rc.top));
1890             sscanf(keywords[i+3], "%d", &(freezeParams->rc.right));
1891             sscanf(keywords[i+4], "%d", &(freezeParams->rc.bottom));
1892             dwFlags |= MCI_OVLY_RECT;
1893             i += 5;
1894             continue;
1895         }
1896         i++;
1897     }
1898     freezeParams->dwCallback = hwndCallback;
1899     res = mciSendCommandA(wDevID, MCI_FREEZE, dwFlags, (DWORD)freezeParams);
1900     free(freezeParams);
1901     return res;
1902 }
1903
1904 /* copy parts of image to somewhere else 
1905  * "source [at <left> <top> <right> <bottom>]"  source is framebuffer [or rect]
1906  * "destination [at <left> <top> <right> <bottom>]"     destination is framebuffer [or rect]
1907  * Overlay:
1908  * "frame [at <left> <top> <right> <bottom>]"   frame is framebuffer [or rect]
1909  *                                              where the video input is placed
1910  * "video [at <left> <top> <right> <bottom>]"   video is whole video [or rect]
1911  *                                              (defining part of input to
1912  *                                              be displayed)
1913  * FIXME: This whole junk is passing multiple rectangles.
1914  *      I don't know how to do that with the present interface.
1915  *      (Means code below is broken)
1916  */
1917 static DWORD
1918 MCISTR_Put(_MCISTR_PROTO_) {
1919     union U {
1920         MCI_OVLY_RECT_PARMS     ovlyputParams;
1921         MCI_ANIM_RECT_PARMS     animputParams;
1922         MCI_DGV_RECT_PARMS      dgvputParams;
1923     };
1924     union U*    pU = xmalloc(sizeof(union U));
1925     int         i, res;
1926     
1927     for (i = 0; i < nrofkeywords; ) {
1928         switch (uDevTyp) {
1929         case MCI_DEVTYPE_ANIMATION:
1930             FLAG1("source", MCI_ANIM_PUT_SOURCE);
1931             FLAG1("destination", MCI_ANIM_PUT_DESTINATION);
1932             if (!STRCMP(keywords[i], "at") && (i+4 < nrofkeywords)) {
1933                 sscanf(keywords[i+1], "%d", &(pU->animputParams.rc.left));
1934                 sscanf(keywords[i+2], "%d", &(pU->animputParams.rc.top));
1935                 sscanf(keywords[i+3], "%d", &(pU->animputParams.rc.right));
1936                 sscanf(keywords[i+4], "%d", &(pU->animputParams.rc.bottom));
1937                 dwFlags |= MCI_ANIM_RECT;
1938                 i += 5;
1939                 continue;
1940             }
1941             break;
1942         case MCI_DEVTYPE_OVERLAY:
1943             FLAG1("source", MCI_OVLY_PUT_SOURCE);
1944             FLAG1("destination", MCI_OVLY_PUT_DESTINATION);
1945             FLAG1("video", MCI_OVLY_PUT_VIDEO);
1946             FLAG1("frame", MCI_OVLY_PUT_FRAME);
1947             if (!STRCMP(keywords[i], "at") && (i+4 < nrofkeywords)) {
1948                 sscanf(keywords[i+1], "%d", &(pU->ovlyputParams.rc.left));
1949                 sscanf(keywords[i+2], "%d", &(pU->ovlyputParams.rc.top));
1950                 sscanf(keywords[i+3], "%d", &(pU->ovlyputParams.rc.right));
1951                 sscanf(keywords[i+4], "%d", &(pU->ovlyputParams.rc.bottom));
1952                 dwFlags |= MCI_OVLY_RECT;
1953                 i += 5;
1954                 continue;
1955             }
1956             break;
1957         case MCI_DEVTYPE_DIGITAL_VIDEO:
1958             FLAG1("source", MCI_DGV_PUT_SOURCE);
1959             FLAG1("destination", MCI_DGV_PUT_DESTINATION);
1960             FLAG1("video", MCI_DGV_PUT_VIDEO);
1961             FLAG1("frame", MCI_DGV_PUT_FRAME);
1962             if (!STRCMP(keywords[i], "at") && (i+4 < nrofkeywords)) {
1963                 sscanf(keywords[i+1], "%d", &(pU->dgvputParams.rc.left));
1964                 sscanf(keywords[i+2], "%d", &(pU->dgvputParams.rc.top));
1965                 sscanf(keywords[i+3], "%d", &(pU->dgvputParams.rc.right));
1966                 sscanf(keywords[i+4], "%d", &(pU->dgvputParams.rc.bottom));
1967                 dwFlags |= MCI_DGV_RECT;
1968                 i += 5;
1969                 continue;
1970             }
1971             break;
1972             break;
1973         }
1974         i++;
1975     }
1976     pU->dgvputParams.dwCallback = hwndCallback;
1977     res = mciSendCommandA(wDevID, MCI_PUT, dwFlags, (DWORD)pU);
1978     free(pU);
1979     return res;
1980 }
1981
1982 /* palette behaviour changing
1983  * (Animation only)
1984  *      "normal"        realize the palette normally
1985  *      "background"    realize the palette as background palette
1986  */
1987 static DWORD
1988 MCISTR_Realize(_MCISTR_PROTO_)
1989 {
1990     MCI_GENERIC_PARMS   *realizeParams = xmalloc(sizeof(MCI_GENERIC_PARMS));
1991     int                 i, res;
1992     
1993     if (uDevTyp != MCI_DEVTYPE_ANIMATION)
1994         return MCIERR_UNSUPPORTED_FUNCTION;
1995     for (i = 0; i < nrofkeywords; i++) {
1996         FLAG1("background", MCI_ANIM_REALIZE_BKGD);
1997         FLAG1("normal", MCI_ANIM_REALIZE_NORM);
1998     }
1999     realizeParams->dwCallback = hwndCallback;
2000     res = mciSendCommandA(wDevID, MCI_REALIZE, dwFlags, (DWORD)realizeParams);
2001     free(realizeParams);
2002     return res;
2003 }
2004
2005 /* Digitalvideo:
2006  *      "algorithm <algorithm>"
2007  *      "alignment to <integer>"
2008  *      "bass to <factor>"
2009  *      "bitspersample to <bit_count>"
2010  *      "bytespersec to <integer>"
2011  *      "clocktime"
2012  *      "input"
2013  *      "left off"
2014  *      "left on"
2015  *      "left volume to <factor>"
2016  *      "off"
2017  *      "on"
2018  *      "output"
2019  *      "over <duration>"
2020  *      "quality <descripto>r"
2021  *      "record off"
2022  *      "record on"
2023  *      "right off"
2024  *      "right on"
2025  *      "right volume to <factor>"
2026  *      "samplespersec to <integer>"
2027  *      "source to <sourcename>"
2028  *      "stream to <number>"
2029  *      "treble to <factor>"
2030  *      "volume to <factor>"
2031  * Vcr:
2032  *      "off"
2033  *      "on"
2034  *      "monitor to type number number"
2035  *      "record off"
2036  *      "record track track_number off"
2037  *      "record on "
2038  *      "record track track_number on "
2039  *      "source to type number number"
2040  *      "track track_number off"
2041  *      "track track_number on"
2042  */
2043 static DWORD
2044 MCISTR_SetAudio(_MCISTR_PROTO_)
2045 {
2046     MCI_DGV_SETAUDIO_PARMSA     setaudioParams;
2047     int                         i, res;
2048
2049     if (uDevTyp != MCI_DEVTYPE_DIGITAL_VIDEO)
2050         return MCIERR_UNSUPPORTED_FUNCTION;
2051
2052     setaudioParams.dwCallback =  hwndCallback;
2053
2054     for (i = 0; i < nrofkeywords; ) {
2055         if (!STRCMP(keywords[i], "algorithm") && (i+1 < nrofkeywords)) {
2056             setaudioParams.lpstrAlgorithm = strdup(keywords[i+1]);
2057             dwFlags |= MCI_DGV_SETAUDIO_ALG;
2058             i += 2;
2059             continue;
2060         }
2061 #define MSAI2(str1, str2, flag)                                                 \
2062         if (  !STRCMP(keywords[i], str1) && (i+2 < nrofkeywords) &&             \
2063               !STRCMP(keywords[i+1], str2)) {                                   \
2064             dwFlags |= MCI_DGV_SETAUDIO_ITEM;                                   \
2065             setaudioParams.dwItem = flag;                                       \
2066             sscanf(keywords[i+2], "%lu", &setaudioParams.dwValue);              \
2067             i += 3;                                                             \
2068             continue;                                                           \
2069         }
2070 #define MSAI3(str1, str2, str3, flag)                                           \
2071         if (  !STRCMP(keywords[i], str1) && (i+3 < nrofkeywords) &&             \
2072               !STRCMP(keywords[i+1], str2) && !STRCMP(keywords[i+2], str3)) {   \
2073             dwFlags |= MCI_DGV_SETAUDIO_ITEM;                                   \
2074             setaudioParams.dwItem = flag;                                       \
2075             sscanf(keywords[i+3], "%lu", &setaudioParams.dwValue);              \
2076             i += 4;                                                             \
2077             continue;                                                           \
2078         }
2079         MSAI2("alignment", "to", MCI_DGV_SETAUDIO_BLOCKALIGN);
2080         MSAI2("bass", "to", MCI_DGV_SETAUDIO_BASS);
2081         MSAI2("bitspersample", "to", MCI_DGV_SETAUDIO_BITSPERSAMPLE);
2082         MSAI2("bytespersec", "to", MCI_DGV_SETAUDIO_AVGBYTESPERSEC);
2083         MSAI2("samplespersec", "to", MCI_DGV_SETAUDIO_SAMPLESPERSEC);
2084         MSAI2("stream", "to", MCI_DGV_SETAUDIO_STREAM);
2085         MSAI2("treble", "to", MCI_DGV_SETAUDIO_TREBLE);
2086         MSAI2("volume", "to", MCI_DGV_SETAUDIO_VOLUME);
2087         MSAI3("input", "bass", "to", MCI_DGV_SETAUDIO_BASS|MCI_DGV_SETAUDIO_INPUT);
2088         MSAI3("input", "treble", "to", MCI_DGV_SETAUDIO_TREBLE|MCI_DGV_SETAUDIO_INPUT);
2089         MSAI3("input", "volume", "to", MCI_DGV_SETAUDIO_VOLUME|MCI_DGV_SETAUDIO_INPUT);
2090         MSAI3("output", "bass", "to", MCI_DGV_SETAUDIO_BASS|MCI_DGV_SETAUDIO_OUTPUT);
2091         MSAI3("output", "treble", "to", MCI_DGV_SETAUDIO_TREBLE|MCI_DGV_SETAUDIO_OUTPUT);
2092         MSAI3("output", "volume", "to", MCI_DGV_SETAUDIO_VOLUME|MCI_DGV_SETAUDIO_OUTPUT);
2093
2094         FLAG1("clocktime", MCI_DGV_SETAUDIO_CLOCKTIME);
2095         FLAG2("left", "off", MCI_DGV_SETAUDIO_LEFT|MCI_SET_OFF);
2096         FLAG2("left", "on", MCI_DGV_SETAUDIO_LEFT|MCI_SET_ON);
2097         if (!STRCMP(keywords[i], "left") && (i+3 < nrofkeywords) && 
2098             !STRCMP(keywords[i+1], "volume") && !STRCMP(keywords[i+2], "to")) {
2099             dwFlags |= MCI_DGV_SETAUDIO_ITEM | MCI_DGV_SETAUDIO_VALUE | MCI_DGV_SETAUDIO_LEFT;
2100             setaudioParams.dwItem = MCI_DGV_SETAUDIO_VOLUME;
2101             sscanf(keywords[i+3], "%lu", &setaudioParams.dwValue);
2102             i += 4;
2103             continue;
2104         }
2105         FLAG1("off", MCI_SET_OFF);
2106         FLAG1("on", MCI_SET_ON);
2107         if (!STRCMP(keywords[i], "over") && (i+1 < nrofkeywords)) {
2108             dwFlags |= MCI_DGV_SETAUDIO_OVER;
2109             sscanf(keywords[i+3], "%lu", &setaudioParams.dwOver);
2110             i += 2;
2111             continue;
2112         }
2113         if (!STRCMP(keywords[i], "quality") && (i+1 < nrofkeywords)) {
2114             setaudioParams.lpstrQuality = strdup(keywords[i+1]);
2115             dwFlags |= MCI_DGV_SETAUDIO_QUALITY;
2116             i += 2;
2117             continue;
2118         }
2119         FLAG2("record", "off", MCI_DGV_SETAUDIO_RECORD|MCI_SET_OFF);
2120         FLAG2("record", "on", MCI_DGV_SETAUDIO_RECORD|MCI_SET_ON);
2121         FLAG2("right", "off", MCI_DGV_SETAUDIO_RIGHT|MCI_SET_OFF);
2122         FLAG2("right", "on", MCI_DGV_SETAUDIO_RIGHT|MCI_SET_ON);
2123         if (!STRCMP(keywords[i], "right") && (i+3 < nrofkeywords) && 
2124             !STRCMP(keywords[i+1], "volume") && !STRCMP(keywords[i+2], "to")) {
2125             dwFlags |= MCI_DGV_SETAUDIO_ITEM | MCI_DGV_SETAUDIO_VALUE | MCI_DGV_SETAUDIO_RIGHT;
2126             setaudioParams.dwItem = MCI_DGV_SETAUDIO_VOLUME;
2127             sscanf(keywords[i+3], "%lu", &setaudioParams.dwValue);
2128             i += 4;
2129             continue;
2130         }
2131         if (!STRCMP(keywords[i], "source") && (i+2 < nrofkeywords) && !STRCMP(keywords[i+1], "to")) {
2132             dwFlags |= MCI_DGV_SETAUDIO_ITEM;
2133             setaudioParams.dwItem |= MCI_DGV_SETAUDIO_SOURCE;
2134             if (!STRCMP(keywords[i+2], "left")) {
2135                 setaudioParams.dwValue |= MCI_DGV_SETAUDIO_SOURCE_LEFT;
2136             } else if (!STRCMP(keywords[i+2], "right")) {
2137                 setaudioParams.dwValue |= MCI_DGV_SETAUDIO_SOURCE_RIGHT;
2138             } else if (!STRCMP(keywords[i+2], "average")) {
2139                 setaudioParams.dwValue |= MCI_DGV_SETAUDIO_SOURCE_AVERAGE;
2140             } else if (!STRCMP(keywords[i+2], "stereo")) {
2141                 setaudioParams.dwValue |= MCI_DGV_SETAUDIO_SOURCE_STEREO;
2142             } else {
2143                 res = MCIERR_UNSUPPORTED_FUNCTION;
2144                 return res;
2145             }
2146             i += 3;
2147             continue;
2148         }
2149     }
2150     res = mciSendCommandA(wDevID, MCI_SETAUDIO, dwFlags, (DWORD)&setaudioParams);
2151     free(setaudioParams.lpstrAlgorithm);
2152     free(setaudioParams.lpstrQuality);
2153     return res;
2154 }
2155
2156 /* videodisc spinning
2157  *      "up"
2158  *      "down"
2159  */
2160 static DWORD
2161 MCISTR_Spin(_MCISTR_PROTO_)
2162 {
2163     MCI_GENERIC_PARMS   *spinParams = xmalloc(sizeof(MCI_GENERIC_PARMS));
2164     int                 i, res;
2165     
2166     if (uDevTyp != MCI_DEVTYPE_VIDEODISC)
2167         return MCIERR_UNSUPPORTED_FUNCTION;
2168     for (i = 0; i < nrofkeywords; i++) {
2169         FLAG1("up", MCI_VD_SPIN_UP);
2170         FLAG1("down", MCI_VD_SPIN_UP);
2171     }
2172     spinParams->dwCallback = hwndCallback;
2173     res = mciSendCommandA(wDevID, MCI_SPIN, dwFlags, (DWORD)spinParams);
2174     free(spinParams);
2175     return res;
2176 }
2177
2178 /* step single frames
2179  *      "reverse"       optional flag
2180  *      "by <nr>"       for <nr> frames
2181  */
2182 static DWORD
2183 MCISTR_Step(_MCISTR_PROTO_) {
2184     union U {
2185         MCI_ANIM_STEP_PARMS     animstepParams;
2186         MCI_VD_STEP_PARMS       vdstepParams;
2187     };
2188     union U *pU = xmalloc(sizeof(union U));
2189     int i, res;
2190     
2191     for (i = 0; i < nrofkeywords; ) {
2192         switch (uDevTyp) {
2193         case MCI_DEVTYPE_ANIMATION:
2194             FLAG1("reverse", MCI_ANIM_STEP_REVERSE);
2195             if (!STRCMP(keywords[i], "by") && (i+1 < nrofkeywords)) {
2196                 sscanf(keywords[i+1], "%ld", &(pU->animstepParams.dwFrames));
2197                 dwFlags |= MCI_ANIM_STEP_FRAMES;
2198                 i += 2;
2199                 continue;
2200             }
2201             break;
2202         case MCI_DEVTYPE_VIDEODISC:
2203             FLAG1("reverse", MCI_VD_STEP_REVERSE);
2204             if (!STRCMP(keywords[i], "by") && (i+1 < nrofkeywords)) {
2205                 sscanf(keywords[i+1], "%ld", &(pU->vdstepParams.dwFrames));
2206                 dwFlags |= MCI_VD_STEP_FRAMES;
2207                 i += 2;
2208                 continue;
2209             }
2210             break;
2211         }
2212         i++;
2213     }
2214     pU->animstepParams.dwCallback = hwndCallback;
2215     res = mciSendCommandA(wDevID, MCI_STEP, dwFlags, (DWORD)pU);
2216     free(pU);
2217     return res;
2218 }
2219
2220 /* update animation window
2221  * Arguments:
2222  *      "at <left> <top> <right> <bottom>" only in this rectangle
2223  *      "hdc"           device context
2224  */
2225 static DWORD
2226 MCISTR_Update(_MCISTR_PROTO_) {
2227     int         i, res;
2228     LPMCI_ANIM_UPDATE_PARMS     updateParams = xmalloc(sizeof(MCI_ANIM_UPDATE_PARMS));
2229     
2230     for (i = 0; i < nrofkeywords; ) {
2231         if (!STRCMP(keywords[i], "at") && (i+4 < nrofkeywords)) {
2232             sscanf(keywords[i+1], "%d", &(updateParams->rc.left));
2233             sscanf(keywords[i+2], "%d", &(updateParams->rc.top));
2234             sscanf(keywords[i+3], "%d", &(updateParams->rc.right));
2235             sscanf(keywords[i+4], "%d", &(updateParams->rc.bottom));
2236             dwFlags |= MCI_ANIM_RECT;
2237             i += 5;
2238             continue;
2239         }
2240         if (!STRCMP(keywords[i], "hdc") && (i+1 < nrofkeywords)) {
2241             dwFlags |= MCI_ANIM_UPDATE_HDC;
2242             sscanf(keywords[i+1], "%d", &(updateParams->hDC));
2243             i += 2;
2244             continue;
2245         }
2246         i++;
2247     }
2248     updateParams->dwCallback = hwndCallback;
2249     res = mciSendCommandA(wDevID, MCI_UPDATE, dwFlags, (DWORD)updateParams);
2250     free(updateParams);
2251     return res;
2252 }
2253
2254 /* where command for animation and overlay drivers.
2255  * just returns the specified rectangle as a string
2256  * Arguments:
2257  *      "source"
2258  *      "destination"
2259  * Overlay special:
2260  *      "video"
2261  *      "frame"
2262  */
2263 static DWORD
2264 MCISTR_Where(_MCISTR_PROTO_) {
2265     union U {
2266         MCI_ANIM_RECT_PARMS     animwhereParams;
2267         MCI_OVLY_RECT_PARMS     ovlywhereParams;
2268     };
2269     union U*    pU = xmalloc(sizeof(union U));
2270     int         i, res;
2271     
2272     for (i = 0; i < nrofkeywords; i++) {
2273         switch (uDevTyp) {
2274         case MCI_DEVTYPE_ANIMATION:
2275             FLAG1("source", MCI_ANIM_WHERE_SOURCE);
2276             FLAG1("destination", MCI_ANIM_WHERE_DESTINATION);
2277             break;
2278         case MCI_DEVTYPE_OVERLAY:
2279             FLAG1("source", MCI_OVLY_WHERE_SOURCE);
2280             FLAG1("destination", MCI_OVLY_WHERE_DESTINATION);
2281             FLAG1("video", MCI_OVLY_WHERE_VIDEO);
2282             FLAG1("frame", MCI_OVLY_WHERE_FRAME);
2283             break;
2284         }
2285     }
2286     pU->animwhereParams.dwCallback = hwndCallback;
2287     res = mciSendCommandA(wDevID, MCI_WHERE, dwFlags, (DWORD)pU);
2288     if (res == 0) {
2289         char    buf[100];
2290         switch (uDevTyp) {
2291         case MCI_DEVTYPE_ANIMATION:
2292             sprintf(buf, "%d %d %d %d",
2293                     pU->animwhereParams.rc.left,
2294                     pU->animwhereParams.rc.top,
2295                     pU->animwhereParams.rc.right,
2296                     pU->animwhereParams.rc.bottom
2297                    );
2298             break;
2299         case MCI_DEVTYPE_OVERLAY:
2300             sprintf(buf, "%d %d %d %d",
2301                     pU->ovlywhereParams.rc.left,
2302                     pU->ovlywhereParams.rc.top,
2303                     pU->ovlywhereParams.rc.right,
2304                     pU->ovlywhereParams.rc.bottom
2305                    );
2306             break;
2307         default:strcpy(buf, "0 0 0 0");break;
2308         }
2309         _MCI_STR(buf);
2310     }
2311     free(pU);
2312     return      res;
2313 }
2314
2315 static DWORD
2316 MCISTR_Window(_MCISTR_PROTO_) {
2317     union U {
2318         MCI_ANIM_WINDOW_PARMSA  animwindowParams;
2319         MCI_OVLY_WINDOW_PARMSA  ovlywindowParams;
2320         MCI_DGV_WINDOW_PARMSA   dgvwindowParams;
2321     };
2322     union U*    pU = xmalloc(sizeof(union U));
2323     int         i, res;
2324     char*       s;
2325     
2326     s = NULL;
2327     for (i = 0; i < nrofkeywords; ) {
2328         switch (uDevTyp) {
2329         case MCI_DEVTYPE_ANIMATION:
2330             if (!STRCMP(keywords[i], "handle") && (i+1 < nrofkeywords)) {
2331                 dwFlags |= MCI_ANIM_WINDOW_HWND;
2332                 if (!STRCMP(keywords[i+1], "default")) 
2333                     pU->animwindowParams.hWnd = MCI_OVLY_WINDOW_DEFAULT;
2334                 else
2335                     sscanf(keywords[i+1], "%d", &(pU->animwindowParams.hWnd));
2336                 i += 2;
2337                 continue;
2338             }
2339             if (!STRCMP(keywords[i], "state") && (i+1 < nrofkeywords)) {
2340                 dwFlags |= MCI_ANIM_WINDOW_STATE;
2341                 if (!STRCMP(keywords[i+1], "hide"))
2342                     pU->animwindowParams.nCmdShow = SW_HIDE;
2343                 if (!STRCMP(keywords[i+1], "iconic"))
2344                     pU->animwindowParams.nCmdShow = SW_SHOWMINNOACTIVE; /* correct? */
2345                 if (!STRCMP(keywords[i+1], "minimized"))
2346                     pU->animwindowParams.nCmdShow = SW_SHOWMINIMIZED;
2347                 if (!STRCMP(keywords[i+1], "maximized"))
2348                     pU->animwindowParams.nCmdShow = SW_SHOWMAXIMIZED;
2349                 if (!STRCMP(keywords[i+1], "minimize"))
2350                     pU->animwindowParams.nCmdShow = SW_MINIMIZE;
2351                 if (!STRCMP(keywords[i+1], "normal"))
2352                     pU->animwindowParams.nCmdShow = SW_NORMAL;
2353                 if (!STRCMP(keywords[i+1], "restore"))
2354                     pU->animwindowParams.nCmdShow = SW_NORMAL;
2355                 if (!STRCMP(keywords[i+1], "show"))
2356                     pU->animwindowParams.nCmdShow = SW_SHOW;
2357                 if (!STRCMP(keywords[i+1], "no") && (i+2 < nrofkeywords)) {
2358                     if (!STRCMP(keywords[i+2], "active"))
2359                         pU->animwindowParams.nCmdShow = SW_SHOWNOACTIVATE;
2360                     if (!STRCMP(keywords[i+2], "action"))
2361                         pU->animwindowParams.nCmdShow = SW_SHOWNA;/* correct?*/
2362                     i++;
2363                 }
2364                 i += 2;
2365                 continue;
2366             }
2367             /* text is enclosed in " ... " as it seems */
2368             if (!STRCMP(keywords[i], "text")) {
2369                 if (keywords[i+1][0] != '"') {
2370                     i++;
2371                     continue;
2372                 }
2373                 dwFlags |= MCI_ANIM_WINDOW_TEXT;
2374                 pU->animwindowParams.lpstrText = _MCISTR_Unquote(keywords[i+1]);
2375                 i += 2;
2376                 continue;
2377             }
2378             FLAG1("stretch", MCI_ANIM_WINDOW_ENABLE_STRETCH);
2379             break;
2380         case MCI_DEVTYPE_DIGITAL_VIDEO:
2381             if (!STRCMP(keywords[i], "handle") && (i+1 < nrofkeywords)) {
2382                 dwFlags |= MCI_DGV_WINDOW_HWND;
2383                 if (!STRCMP(keywords[i+1], "default")) 
2384                     pU->dgvwindowParams.hWnd = MCI_OVLY_WINDOW_DEFAULT;
2385                 else
2386                     sscanf(keywords[i+1], "%d", &(pU->dgvwindowParams.hWnd));
2387                 i += 2;
2388                 continue;
2389             }
2390             if (!STRCMP(keywords[i], "state") && (i+1 < nrofkeywords)) {
2391                 dwFlags |= MCI_DGV_WINDOW_STATE;
2392                 if (!STRCMP(keywords[i+1], "hide"))
2393                     pU->dgvwindowParams.nCmdShow = SW_HIDE;
2394                 if (!STRCMP(keywords[i+1], "iconic"))
2395                     pU->dgvwindowParams.nCmdShow = SW_SHOWMINNOACTIVE; /* correct? */
2396                 if (!STRCMP(keywords[i+1], "minimized"))
2397                     pU->dgvwindowParams.nCmdShow = SW_SHOWMINIMIZED;
2398                 if (!STRCMP(keywords[i+1], "maximized"))
2399                     pU->dgvwindowParams.nCmdShow = SW_SHOWMAXIMIZED;
2400                 if (!STRCMP(keywords[i+1], "minimize"))
2401                     pU->dgvwindowParams.nCmdShow = SW_MINIMIZE;
2402                 if (!STRCMP(keywords[i+1], "normal"))
2403                     pU->dgvwindowParams.nCmdShow = SW_NORMAL;
2404                 if (!STRCMP(keywords[i+1], "restore"))
2405                     pU->dgvwindowParams.nCmdShow = SW_NORMAL;
2406                 if (!STRCMP(keywords[i+1], "show"))
2407                     pU->dgvwindowParams.nCmdShow = SW_SHOW;
2408                 if (!STRCMP(keywords[i+1], "no") && (i+2 < nrofkeywords)) {
2409                     if (!STRCMP(keywords[i+2], "active"))
2410                         pU->dgvwindowParams.nCmdShow = SW_SHOWNOACTIVATE;
2411                     if (!STRCMP(keywords[i+2], "action"))
2412                         pU->dgvwindowParams.nCmdShow = SW_SHOWNA;/* correct?*/
2413                     i++;
2414                 }
2415                 i += 2;
2416                 continue;
2417             }
2418             /* text is enclosed in " ... " as it seems */
2419             if (!STRCMP(keywords[i], "text")) {
2420                 if (keywords[i+1][0] != '"') {
2421                     i++;
2422                     continue;
2423                 }
2424                 dwFlags |= MCI_DGV_WINDOW_TEXT;
2425                 pU->dgvwindowParams.lpstrText = _MCISTR_Unquote(keywords[i+1]);
2426                 i += 2;
2427                 continue;
2428             }
2429             break;
2430
2431         case MCI_DEVTYPE_OVERLAY:
2432             if (!STRCMP(keywords[i], "handle") && (i+1 < nrofkeywords)) {
2433                 dwFlags |= MCI_OVLY_WINDOW_HWND;
2434                 if (!STRCMP(keywords[i+1], "default")) 
2435                     pU->ovlywindowParams.hWnd = MCI_OVLY_WINDOW_DEFAULT;
2436                 else
2437                     sscanf(keywords[i+1], "%d", &(pU->ovlywindowParams.hWnd));
2438                 i += 2;
2439                 continue;
2440             }
2441             if (!STRCMP(keywords[i], "state") && (i+1 < nrofkeywords)) {
2442                 dwFlags |= MCI_OVLY_WINDOW_STATE;
2443                 if (!STRCMP(keywords[i+1], "hide"))
2444                     pU->ovlywindowParams.nCmdShow = SW_HIDE;
2445                 if (!STRCMP(keywords[i+1], "iconic"))
2446                     pU->ovlywindowParams.nCmdShow = SW_SHOWMINNOACTIVE; /* correct? */
2447                 if (!STRCMP(keywords[i+1], "minimized"))
2448                     pU->ovlywindowParams.nCmdShow = SW_SHOWMINIMIZED;
2449                 if (!STRCMP(keywords[i+1], "maximized"))
2450                     pU->ovlywindowParams.nCmdShow = SW_SHOWMAXIMIZED;
2451                 if (!STRCMP(keywords[i+1], "minimize"))
2452                     pU->ovlywindowParams.nCmdShow = SW_MINIMIZE;
2453                 if (!STRCMP(keywords[i+1], "normal"))
2454                     pU->ovlywindowParams.nCmdShow = SW_NORMAL;
2455                 if (!STRCMP(keywords[i+1], "show"))
2456                     pU->ovlywindowParams.nCmdShow = SW_SHOW;
2457                 if (!STRCMP(keywords[i+1], "no") && (i+2 < nrofkeywords)) {
2458                     if (!STRCMP(keywords[i+2], "active"))
2459                         pU->ovlywindowParams.nCmdShow = SW_SHOWNOACTIVATE;
2460                     if (!STRCMP(keywords[i+2], "action"))
2461                         pU->ovlywindowParams.nCmdShow = SW_SHOWNA;/* correct?*/
2462                     i++;
2463                 }
2464                 i += 2;
2465                 continue;
2466             }
2467             /* text is enclosed in " ... " as it seems */
2468             if (!STRCMP(keywords[i], "text")) {
2469                 if (keywords[i+1][0] != '"') {
2470                     i++;
2471                     continue;
2472                 }
2473                 dwFlags |= MCI_OVLY_WINDOW_TEXT;
2474                 pU->ovlywindowParams.lpstrText = _MCISTR_Unquote(keywords[i+1]);
2475                 i += 2;
2476                 continue;
2477             }
2478             FLAG1("stretch", MCI_OVLY_WINDOW_ENABLE_STRETCH);
2479             break;
2480         }
2481         i++;
2482     }
2483     pU->animwindowParams.dwCallback = hwndCallback;
2484     res = mciSendCommandA(wDevID, MCI_WINDOW, dwFlags, (DWORD)pU);
2485     if (s) free(s);
2486     free(pU);
2487     return res;
2488 }
2489
2490 struct  _MCISTR_cmdtable {
2491     char        *cmd;
2492     DWORD       (*fun)(_MCISTR_PROTO_);
2493 } MCISTR_cmdtable[]={
2494     {"break",           MCISTR_Break},
2495     {"capability",      MCISTR_Capability},
2496     {"close",           MCISTR_Close},
2497     {"cue",             MCISTR_Cue},
2498     {"delete",          MCISTR_Delete},
2499     {"escape",          MCISTR_Escape},
2500     {"freeze",          MCISTR_Freeze},
2501     {"info",            MCISTR_Info},
2502     {"load",            MCISTR_Load},
2503     {"open",            MCISTR_Open},
2504     {"pause",           MCISTR_Pause},
2505     {"play",            MCISTR_Play},
2506     {"put",             MCISTR_Put},
2507     {"realize",         MCISTR_Realize},
2508     {"record",          MCISTR_Record},
2509     {"resume",          MCISTR_Resume},
2510     {"save",            MCISTR_Save},
2511     {"seek",            MCISTR_Seek},
2512     {"set",             MCISTR_Set},
2513     {"setaudio",        MCISTR_SetAudio},
2514     {"spin",            MCISTR_Spin},
2515     {"status",          MCISTR_Status},
2516     {"step",            MCISTR_Step},
2517     {"stop",            MCISTR_Stop},
2518     {"sysinfo",         MCISTR_Sysinfo},
2519     {"unfreeze",        MCISTR_Unfreeze},
2520     {"update",          MCISTR_Update},
2521     {"where",           MCISTR_Where},
2522     {"window",          MCISTR_Window},
2523     {NULL,              NULL}
2524 };
2525
2526 /**************************************************************************
2527  *                              mciSendString16                 [MMSYSTEM.702]
2528  */
2529 /* The usercode sends a string with a command (and flags) expressed in 
2530  * words in it... We do our best to call appropriate drivers,
2531  * and return a errorcode AND a readable string (if lpstrRS != NULL)
2532  * Info gathered by watching cool134.exe and from Borland's mcistrwh.hlp
2533  */
2534 /* FIXME: "all" is a valid devicetype and we should access all devices if
2535  * it is used. (imagine "close all"). Not implemented yet.
2536  */
2537 DWORD WINAPI mciSendString16(LPCSTR lpstrCommand, LPSTR lpstrReturnString, 
2538                              UINT16 uReturnLength, HWND16 hwndCallback)
2539 {
2540     char        *cmd, *dev, *args, **keywords;
2541     WORD        uDevTyp = 0, wDevID = 0;
2542     DWORD       dwFlags;
2543     int         res = 0, i, nrofkeywords;
2544     
2545     TRACE(mci, "('%s', %p, %d, %X)\n", 
2546           lpstrCommand, lpstrReturnString, uReturnLength, hwndCallback);
2547
2548     /* format is <command> <device> <optargs> */
2549     cmd = strdup(lpstrCommand);
2550     dev = strchr(cmd, ' ');
2551     if (dev == NULL) {
2552         free(cmd);
2553         return MCIERR_MISSING_DEVICE_NAME;
2554     }
2555     *dev++ = '\0';
2556     args = strchr(dev, ' ');
2557     if (args != NULL) *args = '\0';
2558     while (*++args == ' ');
2559
2560     if (args != NULL) {
2561         char    *s;
2562         i = 1;/* nrofkeywords = nrofspaces+1 */
2563         s = args;
2564         while ((s = strchr(s, ' ')) != NULL) {
2565             i++;
2566             while (*++s == ' ');
2567             /* see if we have a quoted string */
2568             if (*s == '"') {
2569                 s = strchr(s+1, '"');
2570                 if (!s || s[1] != ' ') {
2571                     /* missed closing '"'*/
2572                     free(cmd);
2573                     return MCIERR_NO_CLOSING_QUOTE;
2574                 }
2575             }
2576         }
2577         keywords = (char**)xmalloc(sizeof(char*) * (i + 2));
2578         nrofkeywords = i;
2579         s = args; 
2580         i = 0;
2581         while (s && i < nrofkeywords) {
2582             keywords[i++] = s;
2583             if (*s == '"') {
2584                 if ((s = strchr(s+1, '"')) != NULL) s++;
2585             } else {
2586                 s = strchr(s, ' ');
2587             }
2588             if (s) {
2589                 *s = '\0';
2590                 while (*++s == ' ');
2591             }
2592             TRACE(mci, "[%d] => '%s'\n", i-1, keywords[i-1]);
2593         }
2594         keywords[i] = NULL;
2595     } else {
2596         nrofkeywords = 0;
2597         keywords = (char**)xmalloc(sizeof(char*));
2598         keywords[0] = NULL;
2599     }
2600     dwFlags = 0; /* default flags */
2601     for (i = 0; i < nrofkeywords;) {
2602         if (!STRCMP(keywords[i], "wait")) {
2603             dwFlags |= MCI_WAIT;
2604             memcpy(keywords+i, keywords+(i+1), (nrofkeywords-i-1) * sizeof(char*));
2605             nrofkeywords--;
2606             continue;
2607         }
2608         if (!STRCMP(keywords[i], "notify")) {
2609             dwFlags |= MCI_NOTIFY;
2610             memcpy(keywords+i, keywords+(i+1), (nrofkeywords-i-1) * sizeof(char*));
2611             nrofkeywords--;
2612             continue;
2613         }
2614         i++;
2615     }
2616
2617     /* determine wDevID and uDevTyp for all commands except "open" */
2618     if (STRCMP(cmd, "open") != 0) {
2619         wDevID = mciGetDeviceIDA(dev);
2620         if (wDevID == 0) {
2621             TRACE(mci, "Device '%s' not found!\n", dev);
2622             res = MCIERR_INVALID_DEVICE_NAME;
2623             goto the_end;
2624         }
2625         uDevTyp = MCI_GetDrv(wDevID)->modp.wType;
2626     }
2627
2628     if (lpstrReturnString && uReturnLength > 0)
2629         *lpstrReturnString = 0;
2630
2631     for (i = 0; MCISTR_cmdtable[i].cmd != NULL; i++) {
2632         if (!STRCMP(MCISTR_cmdtable[i].cmd, cmd)) {
2633             res = MCISTR_cmdtable[i].fun(wDevID, uDevTyp, lpstrReturnString,
2634                                          uReturnLength, dev, (LPSTR*)keywords, 
2635                                          nrofkeywords, dwFlags, hwndCallback);
2636             break;
2637         }
2638     }
2639     if (MCISTR_cmdtable[i].cmd == NULL) {
2640         FIXME(mci, "('%s', %p, %u, %X): unimplemented, please report.\n", 
2641               lpstrCommand, lpstrReturnString, uReturnLength, hwndCallback);
2642         res = MCIERR_MISSING_COMMAND_STRING;
2643     }
2644 the_end:
2645     free(keywords); free(cmd);
2646     return res;
2647 }
2648
2649 /**************************************************************************
2650  *                              mciSendStringA          [MMSYSTEM.702][WINMM.51]
2651  */
2652 DWORD WINAPI mciSendStringA(LPCSTR lpstrCommand, LPSTR lpstrReturnString, 
2653                             UINT uReturnLength, HWND hwndCallback)
2654 {
2655     return mciSendString16(lpstrCommand, lpstrReturnString, uReturnLength, hwndCallback);
2656 }
2657
2658 /**************************************************************************
2659  *                              mciSendStringW                  [WINMM.52]
2660  */
2661 DWORD WINAPI mciSendStringW(LPCWSTR lpwstrCommand, LPSTR lpstrReturnString, 
2662                             UINT uReturnLength, HWND hwndCallback)
2663 {
2664     LPSTR       lpstrCommand;
2665     UINT        ret;
2666
2667     /* FIXME: is there something to do with lpstrReturnString ? */
2668     lpstrCommand = HEAP_strdupWtoA(GetProcessHeap(), 0, lpwstrCommand);
2669     ret = mciSendString16(lpstrCommand, lpstrReturnString, uReturnLength, hwndCallback);
2670     HeapFree(GetProcessHeap(), 0, lpstrCommand);
2671     return ret;
2672 }
2673
2674 /**************************************************************************
2675  *                              mciExecute                      [WINMM.38]
2676  */
2677 DWORD WINAPI mciExecute(LPCSTR lpstrCommand)
2678 {
2679     char        strRet[256];
2680     DWORD       ret;
2681
2682     TRACE(mci, "(%s)!\n", lpstrCommand);
2683
2684     ret = mciSendString16(lpstrCommand, strRet, sizeof(strRet), 0);
2685     if (ret != 0) {
2686         if (!mciGetErrorString16(ret, strRet, sizeof(strRet))) {
2687             sprintf(strRet, "Unknown MCI error (%ld)", ret);
2688         }
2689         MessageBoxA(0, strRet, "Error in mciExecute()", MB_OK); 
2690     }
2691     /* FIXME: what shall I return ? */
2692     return 0;
2693 }