Revert shell32 version to Win2K SP4 level.
[wine] / dlls / msacm / format.c
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2
3 /*
4  *      MSACM32 library
5  *
6  *      Copyright 1998  Patrik Stridvall
7  *                2000  Eric Pouech
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23
24 #include <stdarg.h>
25 #include <string.h>
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winnls.h"
29 #include "winerror.h"
30 #include "wingdi.h"
31 #include "winuser.h"
32 #include "wine/unicode.h"
33 #include "wine/debug.h"
34 #include "mmsystem.h"
35 #include "mmreg.h"
36 #include "msacm.h"
37 #include "msacmdrv.h"
38 #include "wineacm.h"
39
40 WINE_DEFAULT_DEBUG_CHANNEL(msacm);
41
42 static  PACMFORMATCHOOSEA       afc;
43
44 struct MSACM_FillFormatData {
45     HWND                hWnd;
46 #define WINE_ACMFF_TAG          0
47 #define WINE_ACMFF_FORMAT       1
48 #define WINE_ACMFF_WFX          2
49     int                 mode;
50     char                szFormatTag[ACMFORMATTAGDETAILS_FORMATTAG_CHARS];
51     PACMFORMATCHOOSEA   afc;
52     DWORD               ret;
53 };
54
55 static BOOL CALLBACK MSACM_FillFormatTagsCB(HACMDRIVERID hadid,
56                                             PACMFORMATTAGDETAILSA paftd,
57                                             DWORD dwInstance, DWORD fdwSupport)
58 {
59     struct MSACM_FillFormatData*        affd = (struct MSACM_FillFormatData*)dwInstance;
60
61     switch (affd->mode) {
62     case WINE_ACMFF_TAG:
63         if (SendDlgItemMessageA(affd->hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMATTAG,
64                                 CB_FINDSTRINGEXACT,
65                                 (WPARAM)-1, (LPARAM)paftd->szFormatTag) == CB_ERR)
66             SendDlgItemMessageA(affd->hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMATTAG,
67                                 CB_ADDSTRING, 0, (DWORD)paftd->szFormatTag);
68         break;
69     case WINE_ACMFF_FORMAT:
70         if (strcmp(affd->szFormatTag, paftd->szFormatTag) == 0) {
71             HACMDRIVER          had;
72
73             if (acmDriverOpen(&had, hadid, 0) == MMSYSERR_NOERROR) {
74                 ACMFORMATDETAILSA       afd;
75                 unsigned int            i, idx;
76                 MMRESULT                mmr;
77                 char                    buffer[ACMFORMATDETAILS_FORMAT_CHARS+16];
78
79                 afd.cbStruct = sizeof(afd);
80                 afd.dwFormatTag = paftd->dwFormatTag;
81                 afd.pwfx = HeapAlloc(MSACM_hHeap, 0, paftd->cbFormatSize);
82                 if (!afd.pwfx) return FALSE;
83                 afd.pwfx->wFormatTag = paftd->dwFormatTag;
84                 afd.pwfx->cbSize = paftd->cbFormatSize;
85                 afd.cbwfx = paftd->cbFormatSize;
86
87                 for (i = 0; i < paftd->cStandardFormats; i++) {
88                     afd.dwFormatIndex = i;
89                     mmr = acmFormatDetailsA(had, &afd, ACM_FORMATDETAILSF_INDEX);
90                     if (mmr == MMSYSERR_NOERROR) {
91                         strncpy(buffer, afd.szFormat, ACMFORMATTAGDETAILS_FORMATTAG_CHARS);
92                         for (idx = strlen(buffer);
93                              idx < ACMFORMATTAGDETAILS_FORMATTAG_CHARS; idx++)
94                             buffer[idx] = ' ';
95                         wsprintfA(buffer + ACMFORMATTAGDETAILS_FORMATTAG_CHARS,
96                                   "%d Ko/s",
97                                   (afd.pwfx->nAvgBytesPerSec + 512) / 1024);
98                         SendDlgItemMessageA(affd->hWnd,
99                                             IDD_ACMFORMATCHOOSE_CMB_FORMAT,
100                                             CB_ADDSTRING, 0, (DWORD)buffer);
101                     }
102                 }
103                 acmDriverClose(had, 0);
104                 SendDlgItemMessageA(affd->hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMAT,
105                                     CB_SETCURSEL, 0, 0);
106                 HeapFree(MSACM_hHeap, 0, afd.pwfx);
107             }
108         }
109         break;
110     case WINE_ACMFF_WFX:
111         if (strcmp(affd->szFormatTag, paftd->szFormatTag) == 0) {
112             HACMDRIVER          had;
113
114             if (acmDriverOpen(&had, hadid, 0) == MMSYSERR_NOERROR) {
115                 ACMFORMATDETAILSA       afd;
116
117                 afd.cbStruct = sizeof(afd);
118                 afd.dwFormatTag = paftd->dwFormatTag;
119                 afd.pwfx = affd->afc->pwfx;
120                 afd.cbwfx = affd->afc->cbwfx;
121
122                 afd.dwFormatIndex = SendDlgItemMessageA(affd->hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMAT,
123                                                         CB_GETCURSEL, 0, 0);
124                 affd->ret = acmFormatDetailsA(had, &afd, ACM_FORMATDETAILSF_INDEX);
125                 acmDriverClose(had, 0);
126                 return TRUE;
127             }
128         }
129         break;
130     default:
131         FIXME("Unknown mode (%d)\n", affd->mode);
132         break;
133     }
134     return TRUE;
135 }
136
137 static BOOL MSACM_FillFormatTags(HWND hWnd)
138 {
139     ACMFORMATTAGDETAILSA        aftd;
140     struct MSACM_FillFormatData affd;
141
142     memset(&aftd, 0, sizeof(aftd));
143     aftd.cbStruct = sizeof(aftd);
144
145     affd.hWnd = hWnd;
146     affd.mode = WINE_ACMFF_TAG;
147
148     acmFormatTagEnumA(NULL, &aftd, MSACM_FillFormatTagsCB, (DWORD)&affd, 0);
149     SendDlgItemMessageA(hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMATTAG, CB_SETCURSEL, 0, 0);
150     return TRUE;
151 }
152
153 static BOOL MSACM_FillFormat(HWND hWnd)
154 {
155     ACMFORMATTAGDETAILSA        aftd;
156     struct MSACM_FillFormatData affd;
157
158     SendDlgItemMessageA(hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMAT, CB_RESETCONTENT, 0, 0);
159
160     memset(&aftd, 0, sizeof(aftd));
161     aftd.cbStruct = sizeof(aftd);
162
163     affd.hWnd = hWnd;
164     affd.mode = WINE_ACMFF_FORMAT;
165     SendDlgItemMessageA(hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMATTAG,
166                         CB_GETLBTEXT,
167                         SendDlgItemMessageA(hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMATTAG,
168                                             CB_GETCURSEL, 0, 0),
169                         (DWORD)affd.szFormatTag);
170
171     acmFormatTagEnumA(NULL, &aftd, MSACM_FillFormatTagsCB, (DWORD)&affd, 0);
172     SendDlgItemMessageA(hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMAT, CB_SETCURSEL, 0, 0);
173     return TRUE;
174 }
175
176 static MMRESULT MSACM_GetWFX(HWND hWnd, PACMFORMATCHOOSEA afc)
177 {
178     ACMFORMATTAGDETAILSA        aftd;
179     struct MSACM_FillFormatData affd;
180
181     memset(&aftd, 0, sizeof(aftd));
182     aftd.cbStruct = sizeof(aftd);
183
184     affd.hWnd = hWnd;
185     affd.mode = WINE_ACMFF_WFX;
186     affd.afc = afc;
187     affd.ret = MMSYSERR_NOERROR;
188     SendDlgItemMessageA(hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMATTAG,
189                         CB_GETLBTEXT,
190                         SendDlgItemMessageA(hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMATTAG,
191                                             CB_GETCURSEL, 0, 0),
192                         (DWORD)affd.szFormatTag);
193
194     acmFormatTagEnumA(NULL, &aftd, MSACM_FillFormatTagsCB, (DWORD)&affd, 0);
195     return affd.ret;
196 }
197
198 static INT_PTR CALLBACK FormatChooseDlgProc(HWND hWnd, UINT msg,
199                                        WPARAM wParam, LPARAM lParam)
200 {
201
202     TRACE("hwnd=%p msg=%i 0x%08x 0x%08lx\n", hWnd,  msg, wParam, lParam );
203
204     switch (msg) {
205     case WM_INITDIALOG:
206         afc = (PACMFORMATCHOOSEA)lParam;
207         MSACM_FillFormatTags(hWnd);
208         MSACM_FillFormat(hWnd);
209         if ((afc->fdwStyle & ~(ACMFORMATCHOOSE_STYLEF_CONTEXTHELP|
210                                ACMFORMATCHOOSE_STYLEF_SHOWHELP)) != 0)
211             FIXME("Unsupported style %08lx\n", ((PACMFORMATCHOOSEA)lParam)->fdwStyle);
212         if (!(afc->fdwStyle & ACMFORMATCHOOSE_STYLEF_SHOWHELP))
213             ShowWindow(GetDlgItem(hWnd, IDD_ACMFORMATCHOOSE_BTN_HELP), SW_HIDE);
214         return TRUE;
215
216     case WM_COMMAND:
217         switch (LOWORD(wParam)) {
218         case IDOK:
219             EndDialog(hWnd, MSACM_GetWFX(hWnd, afc));
220             return TRUE;
221         case IDCANCEL:
222             EndDialog(hWnd, ACMERR_CANCELED);
223             return TRUE;
224         case IDD_ACMFORMATCHOOSE_CMB_FORMATTAG:
225             switch (HIWORD(wParam)) {
226             case CBN_SELCHANGE:
227                 MSACM_FillFormat(hWnd);
228                 break;
229             default:
230                 TRACE("Dropped dlgNotif (fmtTag): 0x%08x 0x%08lx\n",
231                       HIWORD(wParam), lParam);
232                 break;
233             }
234             break;
235         case IDD_ACMFORMATCHOOSE_BTN_HELP:
236             if (afc->fdwStyle & ACMFORMATCHOOSE_STYLEF_SHOWHELP)
237                 SendMessageA(afc->hwndOwner,
238                              RegisterWindowMessageA(ACMHELPMSGSTRINGA), 0L, 0L);
239             break;
240
241         default:
242             TRACE("Dropped dlgCmd: ctl=%d ntf=0x%04x 0x%08lx\n",
243                   LOWORD(wParam), HIWORD(wParam), lParam);
244             break;
245         }
246         break;
247     case WM_CONTEXTMENU:
248         if (afc->fdwStyle & ACMFORMATCHOOSE_STYLEF_CONTEXTHELP)
249             SendMessageA(afc->hwndOwner,
250                          RegisterWindowMessageA(ACMHELPMSGCONTEXTMENUA),
251                          wParam, lParam);
252         break;
253 #if defined(WM_CONTEXTHELP)
254     case WM_CONTEXTHELP:
255         if (afc->fdwStyle & ACMFORMATCHOOSE_STYLEF_CONTEXTHELP)
256             SendMessageA(afc->hwndOwner,
257                          RegisterWindowMessageA(ACMHELPMSGCONTEXTHELPA),
258                          wParam, lParam);
259         break;
260 #endif
261     default:
262         TRACE("Dropped dlgMsg: hwnd=%p msg=%i 0x%08x 0x%08lx\n",
263               hWnd,  msg, wParam, lParam );
264         break;
265     }
266     return FALSE;
267 }
268
269 /***********************************************************************
270  *           acmFormatChooseA (MSACM32.@)
271  */
272 MMRESULT WINAPI acmFormatChooseA(PACMFORMATCHOOSEA pafmtc)
273 {
274     return DialogBoxParamA(MSACM_hInstance32, MAKEINTRESOURCEA(DLG_ACMFORMATCHOOSE_ID),
275                            pafmtc->hwndOwner, FormatChooseDlgProc, (INT)pafmtc);
276 }
277
278 /***********************************************************************
279  *           acmFormatChooseW (MSACM32.@)
280  */
281 MMRESULT WINAPI acmFormatChooseW(PACMFORMATCHOOSEW pafmtc)
282 {
283     FIXME("(%p): stub\n", pafmtc);
284     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
285     return MMSYSERR_ERROR;
286 }
287
288 /***********************************************************************
289  *           acmFormatDetailsA (MSACM32.@)
290  */
291 MMRESULT WINAPI acmFormatDetailsA(HACMDRIVER had, PACMFORMATDETAILSA pafd,
292                                   DWORD fdwDetails)
293 {
294     ACMFORMATDETAILSW   afdw;
295     MMRESULT            mmr;
296
297     memset(&afdw, 0, sizeof(afdw));
298     afdw.cbStruct = sizeof(afdw);
299     afdw.dwFormatIndex = pafd->dwFormatIndex;
300     afdw.dwFormatTag = pafd->dwFormatTag;
301     afdw.pwfx = pafd->pwfx;
302     afdw.cbwfx = pafd->cbwfx;
303
304     mmr = acmFormatDetailsW(had, &afdw, fdwDetails);
305     if (mmr == MMSYSERR_NOERROR) {
306         pafd->dwFormatTag = afdw.dwFormatTag;
307         pafd->fdwSupport = afdw.fdwSupport;
308         WideCharToMultiByte( CP_ACP, 0, afdw.szFormat, -1,
309                              pafd->szFormat, sizeof(pafd->szFormat), NULL, NULL );
310     }
311     return mmr;
312 }
313
314 /***********************************************************************
315  *           acmFormatDetailsW (MSACM32.@)
316  */
317 MMRESULT WINAPI acmFormatDetailsW(HACMDRIVER had, PACMFORMATDETAILSW pafd, DWORD fdwDetails)
318 {
319     MMRESULT                    mmr;
320     static const WCHAR          fmt1[] = {'%','d',' ','H','z',0};
321     static const WCHAR          fmt2[] = {';',' ','%','d',' ','b','i','t','s',0};
322     ACMFORMATTAGDETAILSA        aftd;
323
324     TRACE("(%p, %p, %ld)\n", had, pafd, fdwDetails);
325
326     memset(&aftd, 0, sizeof(aftd));
327     aftd.cbStruct = sizeof(aftd);
328
329     if (pafd->cbStruct < sizeof(*pafd)) return MMSYSERR_INVALPARAM;
330
331     switch (fdwDetails) {
332     case ACM_FORMATDETAILSF_FORMAT:
333         if (pafd->dwFormatTag != pafd->pwfx->wFormatTag) {
334             mmr = MMSYSERR_INVALPARAM;
335             break;
336         }
337         if (had == NULL) {
338             PWINE_ACMDRIVERID           padid;
339
340             mmr = ACMERR_NOTPOSSIBLE;
341             for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) {
342                 /* should check for codec only */
343                 if (!(padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) &&
344                     acmDriverOpen(&had, (HACMDRIVERID)padid, 0) == 0) {
345                     mmr = MSACM_Message(had, ACMDM_FORMAT_DETAILS, (LPARAM)pafd, fdwDetails);
346                     acmDriverClose(had, 0);
347                     if (mmr == MMSYSERR_NOERROR) break;
348                 }
349             }
350         } else {
351             mmr = MSACM_Message(had, ACMDM_FORMAT_DETAILS, (LPARAM)pafd, fdwDetails);
352         }
353         break;
354     case ACM_FORMATDETAILSF_INDEX:
355         /* should check pafd->dwFormatIndex < aftd->cStandardFormats */
356         mmr = MSACM_Message(had, ACMDM_FORMAT_DETAILS, (LPARAM)pafd, fdwDetails);
357         break;
358     default:
359         WARN("Unknown fdwDetails %08lx\n", fdwDetails);
360         mmr = MMSYSERR_INVALFLAG;
361         break;
362     }
363
364     if (mmr == MMSYSERR_NOERROR && pafd->szFormat[0] == (WCHAR)0) {
365         wsprintfW(pafd->szFormat, fmt1, pafd->pwfx->nSamplesPerSec);
366         if (pafd->pwfx->wBitsPerSample) {
367             wsprintfW(pafd->szFormat + lstrlenW(pafd->szFormat), fmt2,
368                       pafd->pwfx->wBitsPerSample);
369         }
370         MultiByteToWideChar( CP_ACP, 0, (pafd->pwfx->nChannels == 1) ? "; Mono" : "; Stereo", -1,
371                              pafd->szFormat + strlenW(pafd->szFormat),
372                              sizeof(pafd->szFormat)/sizeof(WCHAR) - strlenW(pafd->szFormat) );
373     }
374
375     TRACE("=> %d\n", mmr);
376     return mmr;
377 }
378
379 struct MSACM_FormatEnumWtoA_Instance {
380     PACMFORMATDETAILSA  pafda;
381     DWORD               dwInstance;
382     ACMFORMATENUMCBA    fnCallback;
383 };
384
385 static BOOL CALLBACK MSACM_FormatEnumCallbackWtoA(HACMDRIVERID hadid,
386                                                   PACMFORMATDETAILSW pafdw,
387                                                   DWORD dwInstance,
388                                                   DWORD fdwSupport)
389 {
390     struct MSACM_FormatEnumWtoA_Instance* pafei;
391
392     pafei = (struct MSACM_FormatEnumWtoA_Instance*)dwInstance;
393
394     pafei->pafda->dwFormatIndex = pafdw->dwFormatIndex;
395     pafei->pafda->dwFormatTag = pafdw->dwFormatTag;
396     pafei->pafda->fdwSupport = pafdw->fdwSupport;
397     WideCharToMultiByte( CP_ACP, 0, pafdw->szFormat, -1,
398                          pafei->pafda->szFormat, sizeof(pafei->pafda->szFormat), NULL, NULL );
399
400     return (pafei->fnCallback)(hadid, pafei->pafda,
401                                pafei->dwInstance, fdwSupport);
402 }
403
404 /***********************************************************************
405  *           acmFormatEnumA (MSACM32.@)
406  */
407 MMRESULT WINAPI acmFormatEnumA(HACMDRIVER had, PACMFORMATDETAILSA pafda,
408                                ACMFORMATENUMCBA fnCallback, DWORD dwInstance,
409                                DWORD fdwEnum)
410 {
411     ACMFORMATDETAILSW           afdw;
412     struct MSACM_FormatEnumWtoA_Instance afei;
413
414     if (!pafda)
415         return MMSYSERR_INVALPARAM;
416
417     if (pafda->cbStruct < sizeof(*pafda))
418         return MMSYSERR_INVALPARAM;
419
420     memset(&afdw, 0, sizeof(afdw));
421     afdw.cbStruct = sizeof(afdw);
422     afdw.dwFormatIndex = pafda->dwFormatIndex;
423     afdw.dwFormatTag = pafda->dwFormatTag;
424     afdw.pwfx = pafda->pwfx;
425     afdw.cbwfx = pafda->cbwfx;
426
427     afei.pafda = pafda;
428     afei.dwInstance = dwInstance;
429     afei.fnCallback = fnCallback;
430
431     return acmFormatEnumW(had, &afdw, MSACM_FormatEnumCallbackWtoA,
432                           (DWORD)&afei, fdwEnum);
433 }
434
435 /***********************************************************************
436  *           acmFormatEnumW (MSACM32.@)
437  */
438 static BOOL MSACM_FormatEnumHelper(PWINE_ACMDRIVERID padid, HACMDRIVER had,
439                                    PACMFORMATDETAILSW pafd, PWAVEFORMATEX pwfxRef,
440                                    ACMFORMATENUMCBW fnCallback, DWORD dwInstance,
441                                    DWORD fdwEnum)
442 {
443     ACMFORMATTAGDETAILSW        aftd;
444     unsigned int                        i, j;
445
446     for (i = 0; i < padid->cFormatTags; i++) {
447         memset(&aftd, 0, sizeof(aftd));
448         aftd.cbStruct = sizeof(aftd);
449         aftd.dwFormatTagIndex = i;
450         if (acmFormatTagDetailsW(had, &aftd, ACM_FORMATTAGDETAILSF_INDEX) != MMSYSERR_NOERROR)
451             continue;
452
453         if ((fdwEnum & ACM_FORMATENUMF_WFORMATTAG) && aftd.dwFormatTag != pwfxRef->wFormatTag)
454             continue;
455
456         for (j = 0; j < aftd.cStandardFormats; j++) {
457             pafd->dwFormatIndex = j;
458             pafd->dwFormatTag = aftd.dwFormatTag;
459             if (acmFormatDetailsW(had, pafd, ACM_FORMATDETAILSF_INDEX) != MMSYSERR_NOERROR)
460                 continue;
461
462             if ((fdwEnum & ACM_FORMATENUMF_NCHANNELS) &&
463                 pafd->pwfx->nChannels != pwfxRef->nChannels)
464                 continue;
465             if ((fdwEnum & ACM_FORMATENUMF_NSAMPLESPERSEC) &&
466                 pafd->pwfx->nSamplesPerSec != pwfxRef->nSamplesPerSec)
467                 continue;
468             if ((fdwEnum & ACM_FORMATENUMF_WBITSPERSAMPLE) &&
469                 pafd->pwfx->wBitsPerSample != pwfxRef->wBitsPerSample)
470                 continue;
471             if ((fdwEnum & ACM_FORMATENUMF_HARDWARE) &&
472                 !(pafd->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_HARDWARE))
473                 continue;
474
475             /* more checks to be done on fdwEnum */
476
477             if (!(fnCallback)((HACMDRIVERID)padid, pafd, dwInstance, padid->fdwSupport))
478                 return FALSE;
479         }
480         /* the "formats" used by the filters are also reported */
481     }
482     return TRUE;
483 }
484
485 /**********************************************************************/
486
487 MMRESULT WINAPI acmFormatEnumW(HACMDRIVER had, PACMFORMATDETAILSW pafd,
488                                ACMFORMATENUMCBW fnCallback, DWORD dwInstance,
489                                DWORD fdwEnum)
490 {
491     PWINE_ACMDRIVERID           padid;
492     WAVEFORMATEX                wfxRef;
493     BOOL                        ret;
494
495     TRACE("(%p, %p, %p, %ld, %ld)\n",
496           had, pafd, fnCallback, dwInstance, fdwEnum);
497
498     if (!pafd)
499         return MMSYSERR_INVALPARAM;
500
501     if (pafd->cbStruct < sizeof(*pafd))
502         return MMSYSERR_INVALPARAM;
503
504     if (fdwEnum & (ACM_FORMATENUMF_WFORMATTAG|ACM_FORMATENUMF_NCHANNELS|
505                    ACM_FORMATENUMF_NSAMPLESPERSEC|ACM_FORMATENUMF_WBITSPERSAMPLE|
506                    ACM_FORMATENUMF_CONVERT|ACM_FORMATENUMF_SUGGEST))
507         wfxRef = *pafd->pwfx;
508
509     if ((fdwEnum & ACM_FORMATENUMF_HARDWARE) &&
510         !(fdwEnum & (ACM_FORMATENUMF_INPUT|ACM_FORMATENUMF_OUTPUT)))
511         return MMSYSERR_INVALPARAM;
512
513     if ((fdwEnum & ACM_FORMATENUMF_WFORMATTAG) &&
514         (pafd->dwFormatTag != pafd->pwfx->wFormatTag))
515         return MMSYSERR_INVALPARAM;
516
517     if (fdwEnum & (ACM_FORMATENUMF_CONVERT|ACM_FORMATENUMF_SUGGEST|
518                    ACM_FORMATENUMF_INPUT|ACM_FORMATENUMF_OUTPUT))
519         FIXME("Unsupported fdwEnum values %08lx\n", fdwEnum);
520
521     if (had) {
522         HACMDRIVERID    hadid;
523
524         if (acmDriverID((HACMOBJ)had, &hadid, 0) != MMSYSERR_NOERROR)
525             return MMSYSERR_INVALHANDLE;
526         MSACM_FormatEnumHelper(MSACM_GetDriverID(hadid), had, pafd, &wfxRef,
527                                fnCallback, dwInstance, fdwEnum);
528         return MMSYSERR_NOERROR;
529     }
530     for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) {
531             /* should check for codec only */
532             if ((padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) ||
533                 acmDriverOpen(&had, (HACMDRIVERID)padid, 0) != MMSYSERR_NOERROR)
534                 continue;
535             ret = MSACM_FormatEnumHelper(padid, had, pafd, &wfxRef,
536                                          fnCallback, dwInstance, fdwEnum);
537             acmDriverClose(had, 0);
538             if (!ret) break;
539     }
540     return MMSYSERR_NOERROR;
541 }
542
543 /***********************************************************************
544  *           acmFormatSuggest (MSACM32.@)
545  */
546 MMRESULT WINAPI acmFormatSuggest(HACMDRIVER had, PWAVEFORMATEX pwfxSrc,
547                                  PWAVEFORMATEX pwfxDst, DWORD cbwfxDst, DWORD fdwSuggest)
548 {
549     ACMDRVFORMATSUGGEST adfg;
550     MMRESULT            mmr;
551
552     TRACE("(%p, %p, %p, %ld, %ld)\n",
553           had, pwfxSrc, pwfxDst, cbwfxDst, fdwSuggest);
554
555     if (fdwSuggest & ~(ACM_FORMATSUGGESTF_NCHANNELS|ACM_FORMATSUGGESTF_NSAMPLESPERSEC|
556                        ACM_FORMATSUGGESTF_WBITSPERSAMPLE|ACM_FORMATSUGGESTF_WFORMATTAG))
557         return MMSYSERR_INVALFLAG;
558
559     adfg.cbStruct = sizeof(adfg);
560     adfg.fdwSuggest = fdwSuggest;
561     adfg.pwfxSrc = pwfxSrc;
562     adfg.cbwfxSrc = (pwfxSrc->wFormatTag == WAVE_FORMAT_PCM) ?
563         sizeof(WAVEFORMATEX) : (sizeof(WAVEFORMATEX) + pwfxSrc->cbSize);
564     adfg.pwfxDst = pwfxDst;
565     adfg.cbwfxDst = cbwfxDst;
566
567     if (had == NULL) {
568         PWINE_ACMDRIVERID       padid;
569
570         /* MS doc says: ACM finds the best suggestion.
571          * Well, first found will be the "best"
572          */
573         mmr = ACMERR_NOTPOSSIBLE;
574         for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) {
575             /* should check for codec only */
576             if ((padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) ||
577                 acmDriverOpen(&had, (HACMDRIVERID)padid, 0) != MMSYSERR_NOERROR)
578                 continue;
579
580             if (MSACM_Message(had, ACMDM_FORMAT_SUGGEST, (LPARAM)&adfg, 0L) == MMSYSERR_NOERROR) {
581                 mmr = MMSYSERR_NOERROR;
582                 break;
583             }
584             acmDriverClose(had, 0);
585         }
586     } else {
587         mmr = MSACM_Message(had, ACMDM_FORMAT_SUGGEST, (LPARAM)&adfg, 0L);
588     }
589     return mmr;
590 }
591
592 /***********************************************************************
593  *           acmFormatTagDetailsA (MSACM32.@)
594  */
595 MMRESULT WINAPI acmFormatTagDetailsA(HACMDRIVER had, PACMFORMATTAGDETAILSA paftda,
596                                      DWORD fdwDetails)
597 {
598     ACMFORMATTAGDETAILSW        aftdw;
599     MMRESULT                    mmr;
600
601     memset(&aftdw, 0, sizeof(aftdw));
602     aftdw.cbStruct = sizeof(aftdw);
603     aftdw.dwFormatTagIndex = paftda->dwFormatTagIndex;
604     aftdw.dwFormatTag = paftda->dwFormatTag;
605
606     mmr = acmFormatTagDetailsW(had, &aftdw, fdwDetails);
607     if (mmr == MMSYSERR_NOERROR) {
608         paftda->dwFormatTag = aftdw.dwFormatTag;
609         paftda->dwFormatTagIndex = aftdw.dwFormatTagIndex;
610         paftda->cbFormatSize = aftdw.cbFormatSize;
611         paftda->fdwSupport = aftdw.fdwSupport;
612         paftda->cStandardFormats = aftdw.cStandardFormats;
613         WideCharToMultiByte( CP_ACP, 0, aftdw.szFormatTag, -1, paftda->szFormatTag,
614                              sizeof(paftda->szFormatTag), NULL, NULL );
615     }
616     return mmr;
617 }
618
619 /***********************************************************************
620  *           acmFormatTagDetailsW (MSACM32.@)
621  */
622 MMRESULT WINAPI acmFormatTagDetailsW(HACMDRIVER had, PACMFORMATTAGDETAILSW paftd,
623                                      DWORD fdwDetails)
624 {
625     PWINE_ACMDRIVERID   padid;
626     MMRESULT            mmr = ACMERR_NOTPOSSIBLE;
627
628     TRACE("(%p, %p, %ld)\n", had, paftd, fdwDetails);
629
630     if (fdwDetails & ~(ACM_FORMATTAGDETAILSF_FORMATTAG|ACM_FORMATTAGDETAILSF_INDEX|
631                        ACM_FORMATTAGDETAILSF_LARGESTSIZE))
632         return MMSYSERR_INVALFLAG;
633
634     switch (fdwDetails) {
635     case ACM_FORMATTAGDETAILSF_FORMATTAG:
636         if (had == NULL) {
637             for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) {
638                 /* should check for codec only */
639                 if (!(padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) &&
640                     MSACM_FindFormatTagInCache(padid, paftd->dwFormatTag, NULL) &&
641                     acmDriverOpen(&had, (HACMDRIVERID)padid, 0) == 0) {
642                     mmr = MSACM_Message(had, ACMDM_FORMATTAG_DETAILS, (LPARAM)paftd, fdwDetails);
643                     acmDriverClose(had, 0);
644                     if (mmr == MMSYSERR_NOERROR) break;
645                 }
646             }
647         } else {
648             PWINE_ACMDRIVER     pad = MSACM_GetDriver(had);
649
650             if (pad && MSACM_FindFormatTagInCache(pad->obj.pACMDriverID, paftd->dwFormatTag, NULL))
651                 mmr = MSACM_Message(had, ACMDM_FORMATTAG_DETAILS, (LPARAM)paftd, fdwDetails);
652         }
653         break;
654
655     case ACM_FORMATTAGDETAILSF_INDEX:
656         if (had != NULL) {
657             PWINE_ACMDRIVER     pad = MSACM_GetDriver(had);
658
659             if (pad && paftd->dwFormatTagIndex < pad->obj.pACMDriverID->cFormatTags)
660                 mmr = MSACM_Message(had, ACMDM_FORMATTAG_DETAILS, (LPARAM)paftd, fdwDetails);
661         }
662         break;
663
664     case ACM_FORMATTAGDETAILSF_LARGESTSIZE:
665         if (had == NULL) {
666             ACMFORMATTAGDETAILSW        tmp;
667             DWORD                       ft = paftd->dwFormatTag;
668
669             for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) {
670                 /* should check for codec only */
671                 if (!(padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) &&
672                     acmDriverOpen(&had, (HACMDRIVERID)padid, 0) == 0) {
673
674                     memset(&tmp, 0, sizeof(tmp));
675                     tmp.cbStruct = sizeof(tmp);
676                     tmp.dwFormatTag = ft;
677
678                     if (MSACM_Message(had, ACMDM_FORMATTAG_DETAILS,
679                                       (LPARAM)&tmp, fdwDetails) == MMSYSERR_NOERROR) {
680                         if (mmr == ACMERR_NOTPOSSIBLE ||
681                             paftd->cbFormatSize < tmp.cbFormatSize) {
682                             *paftd = tmp;
683                             mmr = MMSYSERR_NOERROR;
684                         }
685                     }
686                     acmDriverClose(had, 0);
687                 }
688             }
689         } else {
690             mmr = MSACM_Message(had, ACMDM_FORMATTAG_DETAILS, (LPARAM)paftd, fdwDetails);
691         }
692         break;
693
694     default:
695         WARN("Unsupported fdwDetails=%08lx\n", fdwDetails);
696         mmr = MMSYSERR_ERROR;
697     }
698
699     if (mmr == MMSYSERR_NOERROR &&
700         paftd->dwFormatTag == WAVE_FORMAT_PCM && paftd->szFormatTag[0] == 0)
701         MultiByteToWideChar( CP_ACP, 0, "PCM", -1, paftd->szFormatTag,
702                              sizeof(paftd->szFormatTag)/sizeof(WCHAR) );
703
704     return mmr;
705 }
706
707 struct MSACM_FormatTagEnumWtoA_Instance {
708     PACMFORMATTAGDETAILSA       paftda;
709     DWORD                       dwInstance;
710     ACMFORMATTAGENUMCBA         fnCallback;
711 };
712
713 static BOOL CALLBACK MSACM_FormatTagEnumCallbackWtoA(HACMDRIVERID hadid,
714                                                      PACMFORMATTAGDETAILSW paftdw,
715                                                      DWORD dwInstance,
716                                                      DWORD fdwSupport)
717 {
718     struct MSACM_FormatTagEnumWtoA_Instance* paftei;
719
720     paftei = (struct MSACM_FormatTagEnumWtoA_Instance*)dwInstance;
721
722     paftei->paftda->dwFormatTagIndex = paftdw->dwFormatTagIndex;
723     paftei->paftda->dwFormatTag = paftdw->dwFormatTag;
724     paftei->paftda->cbFormatSize = paftdw->cbFormatSize;
725     paftei->paftda->fdwSupport = paftdw->fdwSupport;
726     paftei->paftda->cStandardFormats = paftdw->cStandardFormats;
727     WideCharToMultiByte( CP_ACP, 0, paftdw->szFormatTag, -1, paftei->paftda->szFormatTag,
728                          sizeof(paftei->paftda->szFormatTag), NULL, NULL );
729
730     return (paftei->fnCallback)(hadid, paftei->paftda,
731                                 paftei->dwInstance, fdwSupport);
732 }
733
734 /***********************************************************************
735  *           acmFormatTagEnumA (MSACM32.@)
736  */
737 MMRESULT WINAPI acmFormatTagEnumA(HACMDRIVER had, PACMFORMATTAGDETAILSA paftda,
738                                   ACMFORMATTAGENUMCBA fnCallback, DWORD dwInstance,
739                                   DWORD fdwEnum)
740 {
741     ACMFORMATTAGDETAILSW        aftdw;
742     struct MSACM_FormatTagEnumWtoA_Instance aftei;
743
744     if (!paftda)
745         return MMSYSERR_INVALPARAM;
746
747     if (paftda->cbStruct < sizeof(*paftda))
748         return MMSYSERR_INVALPARAM;
749
750     if (fdwEnum != 0)
751         return MMSYSERR_INVALFLAG;
752
753     memset(&aftdw, 0, sizeof(aftdw));
754     aftdw.cbStruct = sizeof(aftdw);
755     aftdw.dwFormatTagIndex = paftda->dwFormatTagIndex;
756     aftdw.dwFormatTag = paftda->dwFormatTag;
757
758     aftei.paftda = paftda;
759     aftei.dwInstance = dwInstance;
760     aftei.fnCallback = fnCallback;
761
762     return acmFormatTagEnumW(had, &aftdw, MSACM_FormatTagEnumCallbackWtoA,
763                              (DWORD)&aftei, fdwEnum);
764 }
765
766 /***********************************************************************
767  *           acmFormatTagEnumW (MSACM32.@)
768  */
769 MMRESULT WINAPI acmFormatTagEnumW(HACMDRIVER had, PACMFORMATTAGDETAILSW paftd,
770                                   ACMFORMATTAGENUMCBW fnCallback, DWORD dwInstance,
771                                   DWORD fdwEnum)
772 {
773     PWINE_ACMDRIVERID           padid;
774     unsigned int                        i;
775     BOOL                        bPcmDone = FALSE;
776
777     TRACE("(%p, %p, %p, %ld, %ld)\n",
778           had, paftd, fnCallback, dwInstance, fdwEnum);
779
780     if (!paftd)
781         return MMSYSERR_INVALPARAM;
782
783     if (paftd->cbStruct < sizeof(*paftd))
784         return MMSYSERR_INVALPARAM;
785
786     if (fdwEnum != 0)
787         return MMSYSERR_INVALFLAG;
788
789     /* (WS) MSDN info page says that if had != 0, then we should find
790      * the specific driver to get its tags from. Therefore I'm removing
791      * the FIXME call and adding a search block below. It also seems
792      * that the lack of this functionality was the responsible for 
793      * codecs to be multiply and incorrectly listed. 
794      */
795
796     /* if (had) FIXME("had != NULL, not supported\n"); */
797
798     if (had) {
799
800        if (acmDriverID((HACMOBJ)had, (HACMDRIVERID *)&padid, 0) != MMSYSERR_NOERROR)
801           return MMSYSERR_INVALHANDLE;
802
803        for (i = 0; i < padid->cFormatTags; i++) {
804           paftd->dwFormatTagIndex = i;
805           if (MSACM_Message(had, ACMDM_FORMATTAG_DETAILS,
806           (LPARAM)paftd, ACM_FORMATTAGDETAILSF_INDEX) == MMSYSERR_NOERROR) {
807              if (paftd->dwFormatTag == WAVE_FORMAT_PCM) {
808                 if (paftd->szFormatTag[0] == 0)
809                    MultiByteToWideChar( CP_ACP, 0, "PCM", -1, paftd->szFormatTag,
810                          sizeof(paftd->szFormatTag)/sizeof(WCHAR) );
811                 /* (WS) I'm preserving this PCM hack since it seems to be
812                  * correct. Please notice this block was borrowed from
813                  * below.
814                  */
815                 if (bPcmDone) continue;
816                    bPcmDone = TRUE;
817              }
818              if (!(fnCallback)((HACMDRIVERID)padid, paftd, dwInstance, padid->fdwSupport)) 
819                 return MMSYSERR_NOERROR;
820           }
821        }
822
823     }
824
825     /* if had==0 then search for the first suitable driver */
826     else {
827        for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) {
828           /* should check for codec only */
829           if (!(padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) &&
830              acmDriverOpen(&had, (HACMDRIVERID)padid, 0) == MMSYSERR_NOERROR) {
831              for (i = 0; i < padid->cFormatTags; i++) {
832                 paftd->dwFormatTagIndex = i;
833                 if (MSACM_Message(had, ACMDM_FORMATTAG_DETAILS,
834                    (LPARAM)paftd, ACM_FORMATTAGDETAILSF_INDEX) == MMSYSERR_NOERROR) {
835                    if (paftd->dwFormatTag == WAVE_FORMAT_PCM) {
836                       if (paftd->szFormatTag[0] == 0)
837                          MultiByteToWideChar( CP_ACP, 0, "PCM", -1, paftd->szFormatTag,
838                                  sizeof(paftd->szFormatTag)/sizeof(WCHAR) );
839                       /* FIXME (EPP): I'm not sure this is the correct
840                        * algorithm (should make more sense to apply the same
841                        * for all already loaded formats, but this will do
842                        * for now
843                        */
844                       if (bPcmDone) continue;
845                          bPcmDone = TRUE;
846                    }
847                    if (!(fnCallback)((HACMDRIVERID)padid, paftd, dwInstance, padid->fdwSupport)) {
848                       acmDriverClose(had, 0);
849                       return MMSYSERR_NOERROR;
850                    }
851                 }
852              }
853           }
854           acmDriverClose(had, 0);
855        }
856     }
857     return MMSYSERR_NOERROR;
858 }