Implement A->W call for GetNamedSecurityInfo.
[wine] / dlls / winmm / midimap / midimap.c
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2 /*
3  * Wine MIDI mapper driver
4  *
5  * Copyright    1999, 2000, 2001 Eric Pouech
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  * TODO:
22  *      notification has to be implemented
23  *      IDF file loading
24  */
25
26 #include <stdarg.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include "windef.h"
30 #include "winbase.h"
31 #include "wingdi.h"
32 #include "winuser.h"
33 #include "mmddk.h"
34 #include "winreg.h"
35 #include "wine/debug.h"
36
37 /*
38  * Here's how Windows stores the midiOut mapping information.
39  *
40  * Full form (in HKU) is:
41  *
42  * [Software\\Microsoft\\Windows\\CurrentVersion\\Multimedia\\MIDIMap] 988836060
43  * "AutoScheme"=dword:00000000
44  * "ConfigureCount"=dword:00000004
45  * "CurrentInstrument"="Wine OSS midi"
46  * "CurrentScheme"="epp"
47  * "DriverList"=""
48  * "UseScheme"=dword:00000000
49  *
50  * AutoScheme:          ?
51  * CurrentInstrument:   name of midiOut device to use when UseScheme is 0. Wine uses an extension
52  *                      of the form #n to link to n'th midiOut device of the system
53  * CurrentScheme:       when UseScheme is non null, it's the scheme to use (see below)
54  * DriverList:          ?
55  * UseScheme:           trigger for simple/complex mapping
56  *
57  * A scheme is defined (in HKLM) as:
58  *
59  * [System\\CurrentControlSet\\Control\\MediaProperties\\PrivateProperties\\Midi\\Schemes\\<nameScheme>]
60  * <nameScheme>:        one key for each defined scheme (system wide)
61  * under each one of these <nameScheme> keys, there's:
62  * [...\\<nameScheme>\\<idxDevice>]
63  * "Channels"="<bitMask>"
64  * (the default value of this key also refers to the name of the device).
65  *
66  * this defines, for each midiOut device (identified by its index in <idxDevice>), which
67  * channels have to be mapped onto it. The <bitMask> defines the channels (from 0 to 15)
68  * will be mapped (mapping occurs for channel <ch> if bit <ch> is set in <bitMask>
69  *
70  * Further mapping information can also be defined in:
71  * [System\\CurrentControlSet\\Control\\MediaProperties\\PrivateProperties\\Midi\\Ports\\<nameDevice>\\Instruments\\<idx>]
72  * "Definition"="<.idf file>"
73  * "FriendlyName"="#for .idx file#"
74  * "Port"="<idxPort>"
75  *
76  * This last part isn't implemented (.idf file support).
77  */
78
79 WINE_DEFAULT_DEBUG_CHANNEL(msacm);
80
81 typedef struct tagMIDIOUTPORT
82 {
83     char                name[MAXPNAMELEN];
84     int                 loaded;
85     HMIDIOUT            hMidi;
86     unsigned short      uDevID;
87     LPBYTE              lpbPatch;
88     unsigned int        aChn[16];
89 } MIDIOUTPORT;
90
91 typedef struct tagMIDIMAPDATA
92 {
93     struct tagMIDIMAPDATA*      self;
94     MIDIOUTPORT*        ChannelMap[16];
95 } MIDIMAPDATA;
96
97 static  MIDIOUTPORT*    midiOutPorts;
98 static  unsigned        numMidiOutPorts;
99
100 static  BOOL    MIDIMAP_IsBadData(MIDIMAPDATA* mm)
101 {
102     if (!IsBadReadPtr(mm, sizeof(MIDIMAPDATA)) && mm->self == mm)
103         return FALSE;
104     TRACE("Bad midimap data (%p)\n", mm);
105     return TRUE;
106 }
107
108 static BOOL     MIDIMAP_FindPort(const char* name, unsigned* dev)
109 {
110     for (*dev = 0; *dev < numMidiOutPorts; (*dev)++)
111     {
112         TRACE("%s\n", midiOutPorts[*dev].name);
113         if (strcmp(midiOutPorts[*dev].name, name) == 0)
114             return TRUE;
115     }
116     /* try the form #nnn */
117     if (*name == '#' && isdigit(name[1]))
118     {
119         *dev = atoi(name + 1);
120         if (*dev < numMidiOutPorts)
121             return TRUE;
122     }
123     return FALSE;
124 }
125
126 static BOOL     MIDIMAP_LoadSettingsDefault(MIDIMAPDATA* mom, const char* port)
127 {
128     unsigned i, dev = 0;
129
130     if (port != NULL && !MIDIMAP_FindPort(port, &dev))
131     {
132         ERR("Registry glitch: couldn't find midi out (%s)\n", port);
133         dev = 0;
134     }
135
136     /* this is necessary when no midi out ports are present */
137     if (dev >= numMidiOutPorts)
138         return FALSE;
139     /* sets default */
140     for (i = 0; i < 16; i++) mom->ChannelMap[i] = &midiOutPorts[dev];
141
142     return TRUE;
143 }
144
145 static BOOL     MIDIMAP_LoadSettingsScheme(MIDIMAPDATA* mom, const char* scheme)
146 {
147     HKEY        hSchemesKey, hKey, hPortKey;
148     unsigned    i, idx, dev;
149     char        buffer[256], port[256];
150     DWORD       type, size, mask;
151
152     for (i = 0; i < 16; i++)    mom->ChannelMap[i] = NULL;
153
154     if (RegOpenKeyA(HKEY_LOCAL_MACHINE,
155                     "System\\CurrentControlSet\\Control\\MediaProperties\\PrivateProperties\\Midi\\Schemes",
156                     &hSchemesKey))
157     {
158         return FALSE;
159     }
160     if (RegOpenKeyA(hSchemesKey, scheme, &hKey))
161     {
162         RegCloseKey(hSchemesKey);
163         return FALSE;
164     }
165
166     for (idx = 0; !RegEnumKeyA(hKey, idx, buffer, sizeof(buffer)); idx++)
167     {
168         if (RegOpenKeyA(hKey, buffer, &hPortKey)) continue;
169
170         size = sizeof(port);
171         if (RegQueryValueExA(hPortKey, NULL, 0, &type, port, &size)) continue;
172
173         if (!MIDIMAP_FindPort(port, &dev)) continue;
174
175         size = sizeof(mask);
176         if (RegQueryValueExA(hPortKey, "Channels", 0, &type, (void*)&mask, &size))
177             continue;
178
179         for (i = 0; i < 16; i++)
180         {
181             if (mask & (1 << i))
182             {
183                 if (mom->ChannelMap[i])
184                     ERR("Quirks in registry, channel %u is mapped twice\n", i);
185                 mom->ChannelMap[i] = &midiOutPorts[dev];
186             }
187         }
188     }
189
190     RegCloseKey(hSchemesKey);
191     RegCloseKey(hKey);
192
193     return TRUE;
194 }
195
196 static BOOL     MIDIMAP_LoadSettings(MIDIMAPDATA* mom)
197 {
198     HKEY        hKey;
199     BOOL        ret;
200
201     if (RegOpenKeyA(HKEY_CURRENT_USER,
202                     "Software\\Microsoft\\Windows\\CurrentVersion\\Multimedia\\MIDIMap", &hKey))
203     {
204         ret = MIDIMAP_LoadSettingsDefault(mom, NULL);
205     }
206     else
207     {
208         DWORD   type, size, out;
209         char    buffer[256];
210
211         ret = 2;
212         size = sizeof(out);
213         if (!RegQueryValueExA(hKey, "UseScheme", 0, &type, (void*)&out, &size) && out)
214         {
215             size = sizeof(buffer);
216             if (!RegQueryValueExA(hKey, "CurrentScheme", 0, &type, buffer, &size))
217             {
218                 if (!(ret = MIDIMAP_LoadSettingsScheme(mom, buffer)))
219                     ret = MIDIMAP_LoadSettingsDefault(mom, NULL);
220             }
221             else
222             {
223                 ERR("Wrong registry: UseScheme is active, but no CurrentScheme found\n");
224             }
225         }
226         if (ret == 2)
227         {
228             size = sizeof(buffer);
229             if (!RegQueryValueExA(hKey, "CurrentInstrument", 0, &type, buffer, &size) && *buffer)
230             {
231                 ret = MIDIMAP_LoadSettingsDefault(mom, buffer);
232             }
233             else
234             {
235                 ret = MIDIMAP_LoadSettingsDefault(mom, NULL);
236             }
237         }
238     }
239     RegCloseKey(hKey);
240
241     if (ret && TRACE_ON(msacm))
242     {
243         unsigned        i;
244
245         for (i = 0; i < 16; i++)
246         {
247             TRACE("chnMap[%2d] => %d\n",
248                   i, mom->ChannelMap[i] ? mom->ChannelMap[i]->uDevID : -1);
249         }
250     }
251     return ret;
252 }
253
254 static  DWORD   modOpen(LPDWORD lpdwUser, LPMIDIOPENDESC lpDesc, DWORD dwFlags)
255 {
256     MIDIMAPDATA*        mom = HeapAlloc(GetProcessHeap(), 0, sizeof(MIDIMAPDATA));
257
258     TRACE("(%p %p %08lx)\n", lpdwUser, lpDesc, dwFlags);
259
260     if (!mom) return MMSYSERR_NOMEM;
261
262     if (MIDIMAP_LoadSettings(mom))
263     {
264         *lpdwUser = (DWORD)mom;
265         mom->self = mom;
266
267         return MMSYSERR_NOERROR;
268     }
269     HeapFree(GetProcessHeap(), 0, mom);
270     return MIDIERR_INVALIDSETUP;
271 }
272
273 static  DWORD   modClose(MIDIMAPDATA* mom)
274 {
275     UINT        i;
276     DWORD       ret = MMSYSERR_NOERROR;
277
278     if (MIDIMAP_IsBadData(mom))         return MMSYSERR_ERROR;
279
280     for (i = 0; i < 16; i++)
281     {
282         DWORD   t;
283         if (mom->ChannelMap[i] && mom->ChannelMap[i]->loaded > 0)
284         {
285             t = midiOutClose(mom->ChannelMap[i]->hMidi);
286             if (t == MMSYSERR_NOERROR)
287             {
288                 mom->ChannelMap[i]->loaded = 0;
289                 mom->ChannelMap[i]->hMidi = 0;
290             }
291             else if (ret == MMSYSERR_NOERROR)
292                 ret = t;
293         }
294     }
295     if (ret == MMSYSERR_NOERROR)
296         HeapFree(GetProcessHeap(), 0, mom);
297     return ret;
298 }
299
300 static  DWORD   modLongData(MIDIMAPDATA* mom, LPMIDIHDR lpMidiHdr, DWORD dwParam2)
301 {
302     WORD        chn;
303     DWORD       ret = MMSYSERR_NOERROR;
304     MIDIHDR     mh;
305
306     if (MIDIMAP_IsBadData(mom))
307         return MMSYSERR_ERROR;
308
309     mh = *lpMidiHdr;
310     for (chn = 0; chn < 16; chn++)
311     {
312         if (mom->ChannelMap[chn] && mom->ChannelMap[chn]->loaded > 0)
313         {
314             mh.dwFlags = 0;
315             midiOutPrepareHeader(mom->ChannelMap[chn]->hMidi, &mh, sizeof(mh));
316             ret = midiOutLongMsg(mom->ChannelMap[chn]->hMidi, &mh, sizeof(mh));
317             midiOutUnprepareHeader(mom->ChannelMap[chn]->hMidi, &mh, sizeof(mh));
318             if (ret != MMSYSERR_NOERROR) break;
319         }
320     }
321     return ret;
322 }
323
324 static  DWORD   modData(MIDIMAPDATA* mom, DWORD dwParam)
325 {
326     BYTE        lb = LOBYTE(LOWORD(dwParam));
327     WORD        chn = lb & 0x0F;
328     DWORD       ret = MMSYSERR_NOERROR;
329
330     if (MIDIMAP_IsBadData(mom))
331         return MMSYSERR_ERROR;
332
333     if (!mom->ChannelMap[chn]) return MMSYSERR_NOERROR;
334
335     switch (lb & 0xF0)
336     {
337     case 0x80:
338     case 0x90:
339     case 0xA0:
340     case 0xB0:
341     case 0xC0:
342     case 0xD0:
343     case 0xE0:
344         if (mom->ChannelMap[chn]->loaded == 0)
345         {
346             if (midiOutOpen(&mom->ChannelMap[chn]->hMidi, mom->ChannelMap[chn]->uDevID,
347                             0L, 0L, CALLBACK_NULL) == MMSYSERR_NOERROR)
348                 mom->ChannelMap[chn]->loaded = 1;
349             else
350                 mom->ChannelMap[chn]->loaded = -1;
351             /* FIXME: should load here the IDF midi data... and allow channel and
352              * patch mappings
353              */
354         }
355         if (mom->ChannelMap[chn]->loaded > 0)
356         {
357             /* change channel */
358             dwParam &= ~0x0F;
359             dwParam |= mom->ChannelMap[chn]->aChn[chn];
360
361             if ((LOBYTE(LOWORD(dwParam)) & 0xF0) == 0xC0 /* program change */ &&
362                 mom->ChannelMap[chn]->lpbPatch)
363             {
364                 BYTE patch = HIBYTE(LOWORD(dwParam));
365
366                 /* change patch */
367                 dwParam &= ~0x0000FF00;
368                 dwParam |= mom->ChannelMap[chn]->lpbPatch[patch];
369             }
370             ret = midiOutShortMsg(mom->ChannelMap[chn]->hMidi, dwParam);
371         }
372         break;
373     case 0xF0:
374         for (chn = 0; chn < 16; chn++)
375         {
376             if (mom->ChannelMap[chn]->loaded > 0)
377                 ret = midiOutShortMsg(mom->ChannelMap[chn]->hMidi, dwParam);
378         }
379         break;
380     default:
381         FIXME("ooch %lu\n", dwParam);
382     }
383
384     return ret;
385 }
386
387 static  DWORD   modPrepare(MIDIMAPDATA* mom, LPMIDIHDR lpMidiHdr, DWORD dwParam2)
388 {
389     if (MIDIMAP_IsBadData(mom)) return MMSYSERR_ERROR;
390     if (lpMidiHdr->dwFlags & (MHDR_ISSTRM|MHDR_PREPARED))
391         return MMSYSERR_INVALPARAM;
392
393     lpMidiHdr->dwFlags |= MHDR_PREPARED;
394     return MMSYSERR_NOERROR;
395 }
396
397 static  DWORD   modUnprepare(MIDIMAPDATA* mom, LPMIDIHDR lpMidiHdr, DWORD dwParam2)
398 {
399     if (MIDIMAP_IsBadData(mom)) return MMSYSERR_ERROR;
400     if ((lpMidiHdr->dwFlags & MHDR_ISSTRM) || !(lpMidiHdr->dwFlags & MHDR_PREPARED))
401         return MMSYSERR_INVALPARAM;
402
403     lpMidiHdr->dwFlags &= ~MHDR_PREPARED;
404     return MMSYSERR_NOERROR;
405 }
406
407 static  DWORD   modGetDevCaps(UINT wDevID, MIDIMAPDATA* mom, LPMIDIOUTCAPSA lpMidiCaps, DWORD size)
408 {
409     lpMidiCaps->wMid = 0x00FF;
410     lpMidiCaps->wPid = 0x0001;
411     lpMidiCaps->vDriverVersion = 0x0100;
412     strcpy(lpMidiCaps->szPname, "Wine midi out mapper");
413     lpMidiCaps->wTechnology = MOD_MAPPER;
414     lpMidiCaps->wVoices = 0;
415     lpMidiCaps->wNotes = 0;
416     lpMidiCaps->wChannelMask = 0xFFFF;
417     lpMidiCaps->dwSupport = 0L;
418
419     return MMSYSERR_NOERROR;
420 }
421
422 static  DWORD   modReset(MIDIMAPDATA* mom)
423 {
424     WORD        chn;
425     DWORD       ret = MMSYSERR_NOERROR;
426
427     if (MIDIMAP_IsBadData(mom))
428         return MMSYSERR_ERROR;
429
430     for (chn = 0; chn < 16; chn++)
431     {
432         if (mom->ChannelMap[chn] && mom->ChannelMap[chn]->loaded > 0)
433         {
434             ret = midiOutReset(mom->ChannelMap[chn]->hMidi);
435             if (ret != MMSYSERR_NOERROR) break;
436         }
437     }
438     return ret;
439 }
440
441 /**************************************************************************
442  *                              modMessage (MIDIMAP.@)
443  */
444 DWORD WINAPI MIDIMAP_modMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
445                                 DWORD dwParam1, DWORD dwParam2)
446 {
447     TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
448           wDevID, wMsg, dwUser, dwParam1, dwParam2);
449
450     switch (wMsg)
451     {
452     case DRVM_INIT:
453     case DRVM_EXIT:
454     case DRVM_ENABLE:
455     case DRVM_DISABLE:
456         /* FIXME: Pretend this is supported */
457         return 0;
458
459     case MODM_OPEN:             return modOpen          ((LPDWORD)dwUser,      (LPMIDIOPENDESC)dwParam1,dwParam2);
460     case MODM_CLOSE:            return modClose         ((MIDIMAPDATA*)dwUser);
461
462     case MODM_DATA:             return modData          ((MIDIMAPDATA*)dwUser, dwParam1);
463     case MODM_LONGDATA:         return modLongData      ((MIDIMAPDATA*)dwUser, (LPMIDIHDR)dwParam1,     dwParam2);
464     case MODM_PREPARE:          return modPrepare       ((MIDIMAPDATA*)dwUser, (LPMIDIHDR)dwParam1,     dwParam2);
465     case MODM_UNPREPARE:        return modUnprepare     ((MIDIMAPDATA*)dwUser, (LPMIDIHDR)dwParam1,     dwParam2);
466     case MODM_RESET:            return modReset         ((MIDIMAPDATA*)dwUser);
467
468     case MODM_GETDEVCAPS:       return modGetDevCaps    (wDevID, (MIDIMAPDATA*)dwUser, (LPMIDIOUTCAPSA)dwParam1,dwParam2);
469     case MODM_GETNUMDEVS:       return 1;
470     case MODM_GETVOLUME:        return MMSYSERR_NOTSUPPORTED;
471     case MODM_SETVOLUME:        return MMSYSERR_NOTSUPPORTED;
472     default:
473         FIXME("unknown message %d!\n", wMsg);
474     }
475     return MMSYSERR_NOTSUPPORTED;
476 }
477
478 /*======================================================================*
479  *                  Driver part                                         *
480  *======================================================================*/
481
482 /**************************************************************************
483  *                              MIDIMAP_drvOpen                 [internal]
484  */
485 static  DWORD   MIDIMAP_drvOpen(LPSTR str)
486 {
487     MIDIOUTCAPSA        moc;
488     unsigned            dev, i;
489
490     if (midiOutPorts)
491         return 0;
492
493     numMidiOutPorts = midiOutGetNumDevs();
494     midiOutPorts = HeapAlloc(GetProcessHeap(), 0,
495                              numMidiOutPorts * sizeof(MIDIOUTPORT));
496     for (dev = 0; dev < numMidiOutPorts; dev++)
497     {
498         if (midiOutGetDevCapsA(dev, &moc, sizeof(moc)) == 0L)
499         {
500             strcpy(midiOutPorts[dev].name, moc.szPname);
501             midiOutPorts[dev].loaded = 0;
502             midiOutPorts[dev].hMidi = 0;
503             midiOutPorts[dev].uDevID = dev;
504             midiOutPorts[dev].lpbPatch = NULL;
505             for (i = 0; i < 16; i++)
506                 midiOutPorts[dev].aChn[i] = i;
507         }
508         else
509         {
510             midiOutPorts[dev].loaded = -1;
511         }
512     }
513
514     return 1;
515 }
516
517 /**************************************************************************
518  *                              MIDIMAP_drvClose                [internal]
519  */
520 static  DWORD   MIDIMAP_drvClose(DWORD dwDevID)
521 {
522     if (midiOutPorts)
523     {
524         HeapFree(GetProcessHeap(), 0, midiOutPorts);
525         midiOutPorts = NULL;
526         return 1;
527     }
528     return 0;
529 }
530
531 /**************************************************************************
532  *                              DriverProc (MIDIMAP.@)
533  */
534 LONG CALLBACK   MIDIMAP_DriverProc(DWORD dwDevID, HDRVR hDriv, DWORD wMsg,
535                                    DWORD dwParam1, DWORD dwParam2)
536 {
537 /* EPP     TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n",  */
538 /* EPP    dwDevID, hDriv, wMsg, dwParam1, dwParam2); */
539
540     switch (wMsg)
541     {
542     case DRV_LOAD:              return 1;
543     case DRV_FREE:              return 1;
544     case DRV_OPEN:              return MIDIMAP_drvOpen((LPSTR)dwParam1);
545     case DRV_CLOSE:             return MIDIMAP_drvClose(dwDevID);
546     case DRV_ENABLE:            return 1;
547     case DRV_DISABLE:           return 1;
548     case DRV_QUERYCONFIGURE:    return 1;
549     case DRV_CONFIGURE:         MessageBoxA(0, "MIDIMAP MultiMedia Driver !", "OSS Driver", MB_OK);     return 1;
550     case DRV_INSTALL:           return DRVCNF_RESTART;
551     case DRV_REMOVE:            return DRVCNF_RESTART;
552     default:
553         return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
554     }
555 }