xref: /reactos/dll/win32/msacm32/format.c (revision 845faec4)
1 /*
2  *      MSACM32 library
3  *
4  *      Copyright 1998  Patrik Stridvall
5  *		  2000	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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21 
22 #include <stdarg.h>
23 #include <string.h>
24 #include "windef.h"
25 #include "winbase.h"
26 #include "winnls.h"
27 #include "winerror.h"
28 #include "wingdi.h"
29 #include "winuser.h"
30 #include "wine/unicode.h"
31 #include "wine/debug.h"
32 #include "mmsystem.h"
33 #include "mmreg.h"
34 #include "msacm.h"
35 #include "msacmdrv.h"
36 #include "wineacm.h"
37 
38 WINE_DEFAULT_DEBUG_CHANNEL(msacm);
39 
40 struct MSACM_FillFormatData {
41     HWND		hWnd;
42 #define WINE_ACMFF_TAG		0
43 #define WINE_ACMFF_FORMAT	1
44 #define WINE_ACMFF_WFX		2
45     int			mode;
46     WCHAR		szFormatTag[ACMFORMATTAGDETAILS_FORMATTAG_CHARS];
47     PACMFORMATCHOOSEW	afc;
48     DWORD		ret;
49 };
50 
51 static BOOL CALLBACK MSACM_FillFormatTagsCB(HACMDRIVERID hadid,
52                                             PACMFORMATTAGDETAILSW paftd,
53                                             DWORD_PTR dwInstance,
54                                             DWORD fdwSupport)
55 {
56     struct MSACM_FillFormatData*	affd = (struct MSACM_FillFormatData*)dwInstance;
57 
58     switch (affd->mode) {
59     case WINE_ACMFF_TAG:
60         if (paftd->cStandardFormats > 0)
61         {
62             if (SendDlgItemMessageW(affd->hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMATTAG,
63                                     CB_FINDSTRINGEXACT, -1,
64                                     (LPARAM)paftd->szFormatTag) == CB_ERR)
65             {
66                 int index = SendDlgItemMessageW(affd->hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMATTAG,
67                                     CB_ADDSTRING, 0, (LPARAM)paftd->szFormatTag);
68                 if (((affd->afc->fdwStyle & ACMFORMATCHOOSE_STYLEF_INITTOWFXSTRUCT) &&
69 		     (paftd->dwFormatTag == affd->afc->pwfx->wFormatTag)) ||
70 		    (!(affd->afc->fdwStyle & ACMFORMATCHOOSE_STYLEF_INITTOWFXSTRUCT) &&
71 		     (paftd->dwFormatTag == WAVE_FORMAT_PCM)))
72                     SendDlgItemMessageW(affd->hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMATTAG,
73                                         CB_SETCURSEL, index, 0);
74             }
75         }
76 	break;
77     case WINE_ACMFF_FORMAT:
78 	if (strcmpW(affd->szFormatTag, paftd->szFormatTag) == 0) {
79 	    HACMDRIVER		had;
80 
81 	    if (acmDriverOpen(&had, hadid, 0) == MMSYSERR_NOERROR) {
82 		ACMFORMATDETAILSW	afd = {0};
83                 unsigned int            i, len;
84 		MMRESULT		mmr;
85 		WCHAR			buffer[ACMFORMATDETAILS_FORMAT_CHARS+16];
86 
87 		afd.cbStruct = sizeof(afd);
88 		afd.dwFormatTag = paftd->dwFormatTag;
89 		afd.pwfx = HeapAlloc(MSACM_hHeap, 0, paftd->cbFormatSize);
90 		if (!afd.pwfx) return FALSE;
91 		afd.pwfx->wFormatTag = paftd->dwFormatTag;
92 		if (paftd->dwFormatTag != WAVE_FORMAT_PCM)
93 		    afd.pwfx->cbSize = paftd->cbFormatSize - sizeof(WAVEFORMATEX);
94 		afd.cbwfx = paftd->cbFormatSize;
95 
96 		for (i = 0; i < paftd->cStandardFormats; i++) {
97                     static const WCHAR fmtW[] = {'%','d',' ','K','o','/','s','\0'};
98                     int j, index;
99 
100 		    afd.dwFormatIndex = i;
101 		    afd.fdwSupport = 0;
102 		    mmr = acmFormatDetailsW(had, &afd, ACM_FORMATDETAILSF_INDEX);
103 		    if (mmr == MMSYSERR_NOERROR) {
104                        lstrcpynW(buffer, afd.szFormat, ACMFORMATTAGDETAILS_FORMATTAG_CHARS + 1);
105                        len = strlenW(buffer);
106                        for (j = len; j < ACMFORMATTAGDETAILS_FORMATTAG_CHARS; j++)
107                            buffer[j] = ' ';
108                        wsprintfW(buffer + ACMFORMATTAGDETAILS_FORMATTAG_CHARS,
109                                  fmtW, (afd.pwfx->nAvgBytesPerSec + 512) / 1024);
110                        index = SendDlgItemMessageW(affd->hWnd,
111                                            IDD_ACMFORMATCHOOSE_CMB_FORMAT,
112                                            CB_ADDSTRING, 0, (LPARAM)buffer);
113                        if ((affd->afc->fdwStyle & ACMFORMATCHOOSE_STYLEF_INITTOWFXSTRUCT) &&
114                            affd->afc->cbwfx >= paftd->cbFormatSize &&
115                            !memcmp(afd.pwfx, affd->afc->pwfx, paftd->cbFormatSize))
116                            SendDlgItemMessageW(affd->hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMAT,
117                                                CB_SETCURSEL, index, 0);
118 		    }
119 		}
120 		acmDriverClose(had, 0);
121 		HeapFree(MSACM_hHeap, 0, afd.pwfx);
122 	    }
123 	}
124 	break;
125     case WINE_ACMFF_WFX:
126 	if (strcmpW(affd->szFormatTag, paftd->szFormatTag) == 0) {
127 	    HACMDRIVER		had;
128 
129 	    if (acmDriverOpen(&had, hadid, 0) == MMSYSERR_NOERROR) {
130 		ACMFORMATDETAILSW	afd = {0};
131 
132 		afd.cbStruct = sizeof(afd);
133 		afd.dwFormatTag = paftd->dwFormatTag;
134 		afd.pwfx = affd->afc->pwfx;
135 		afd.cbwfx = affd->afc->cbwfx;
136 
137 		afd.dwFormatIndex = SendDlgItemMessageW(affd->hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMAT,
138 							CB_GETCURSEL, 0, 0);
139 		affd->ret = acmFormatDetailsW(had, &afd, ACM_FORMATDETAILSF_INDEX);
140 		acmDriverClose(had, 0);
141 		return TRUE;
142 	    }
143 	}
144 	break;
145     default:
146 	FIXME("Unknown mode (%d)\n", affd->mode);
147 	break;
148     }
149     return TRUE;
150 }
151 
152 static BOOL MSACM_FillFormatTags(HWND hWnd, PACMFORMATCHOOSEW afc)
153 {
154     ACMFORMATTAGDETAILSW	aftd;
155     struct MSACM_FillFormatData	affd;
156 
157     memset(&aftd, 0, sizeof(aftd));
158     aftd.cbStruct = sizeof(aftd);
159 
160     affd.hWnd = hWnd;
161     affd.mode = WINE_ACMFF_TAG;
162     affd.afc = afc;
163 
164     acmFormatTagEnumW(NULL, &aftd, MSACM_FillFormatTagsCB, (DWORD_PTR)&affd, 0);
165     if (SendDlgItemMessageW(hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMATTAG, CB_GETCURSEL, 0, 0) == CB_ERR)
166         SendDlgItemMessageW(hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMATTAG, CB_SETCURSEL, 0, 0);
167     return TRUE;
168 }
169 
170 static BOOL MSACM_FillFormat(HWND hWnd, PACMFORMATCHOOSEW afc)
171 {
172     ACMFORMATTAGDETAILSW	aftd;
173     struct MSACM_FillFormatData	affd;
174 
175     SendDlgItemMessageW(hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMAT, CB_RESETCONTENT, 0, 0);
176 
177     memset(&aftd, 0, sizeof(aftd));
178     aftd.cbStruct = sizeof(aftd);
179 
180     affd.hWnd = hWnd;
181     affd.mode = WINE_ACMFF_FORMAT;
182     affd.afc = afc;
183     SendDlgItemMessageW(hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMATTAG,
184 			CB_GETLBTEXT,
185 			SendDlgItemMessageW(hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMATTAG,
186 					    CB_GETCURSEL, 0, 0),
187                         (LPARAM)affd.szFormatTag);
188 
189     acmFormatTagEnumW(NULL, &aftd, MSACM_FillFormatTagsCB, (DWORD_PTR)&affd, 0);
190     if (SendDlgItemMessageW(hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMAT, CB_GETCURSEL, 0, 0) == CB_ERR)
191         SendDlgItemMessageW(hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMAT, CB_SETCURSEL, 0, 0);
192     return TRUE;
193 }
194 
195 static MMRESULT MSACM_GetWFX(HWND hWnd, PACMFORMATCHOOSEW afc)
196 {
197     ACMFORMATTAGDETAILSW	aftd;
198     struct MSACM_FillFormatData	affd;
199 
200     memset(&aftd, 0, sizeof(aftd));
201     aftd.cbStruct = sizeof(aftd);
202 
203     affd.hWnd = hWnd;
204     affd.mode = WINE_ACMFF_WFX;
205     affd.afc = afc;
206     affd.ret = MMSYSERR_NOERROR;
207     SendDlgItemMessageW(hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMATTAG,
208 			CB_GETLBTEXT,
209 			SendDlgItemMessageW(hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMATTAG,
210 					    CB_GETCURSEL, 0, 0),
211                         (LPARAM)affd.szFormatTag);
212 
213     acmFormatTagEnumW(NULL, &aftd, MSACM_FillFormatTagsCB, (DWORD_PTR)&affd, 0);
214     return affd.ret;
215 }
216 
217 static const WCHAR fmt_prop[] = {'a','c','m','p','r','o','p','\0'};
218 
219 static INT_PTR CALLBACK FormatChooseDlgProc(HWND hWnd, UINT msg,
220                                             WPARAM wParam, LPARAM lParam)
221 {
222     PACMFORMATCHOOSEW   afc = (PACMFORMATCHOOSEW)GetPropW(hWnd, fmt_prop);
223 
224     TRACE("hwnd=%p msg=%i 0x%08lx 0x%08lx\n", hWnd, msg, wParam, lParam);
225 
226     switch (msg) {
227     case WM_INITDIALOG:
228 	afc = (PACMFORMATCHOOSEW)lParam;
229 	SetPropW(hWnd, fmt_prop, (HANDLE)afc);
230 	MSACM_FillFormatTags(hWnd, afc);
231 	MSACM_FillFormat(hWnd, afc);
232 	if ((afc->fdwStyle & ~(ACMFORMATCHOOSE_STYLEF_CONTEXTHELP|
233 			       ACMFORMATCHOOSE_STYLEF_SHOWHELP|
234                                ACMFORMATCHOOSE_STYLEF_INITTOWFXSTRUCT|
235                                ACMFORMATCHOOSE_STYLEF_ENABLETEMPLATEHANDLE|
236                                ACMFORMATCHOOSE_STYLEF_ENABLETEMPLATE)) != 0)
237             FIXME("Unsupported style %08x\n", afc->fdwStyle);
238 	if (!(afc->fdwStyle & ACMFORMATCHOOSE_STYLEF_SHOWHELP))
239 	    ShowWindow(GetDlgItem(hWnd, IDD_ACMFORMATCHOOSE_BTN_HELP), SW_HIDE);
240 	return TRUE;
241 
242     case WM_COMMAND:
243 	switch (LOWORD(wParam)) {
244 	case IDOK:
245 	    EndDialog(hWnd, MSACM_GetWFX(hWnd, afc));
246 	    return TRUE;
247 	case IDCANCEL:
248 	    EndDialog(hWnd, ACMERR_CANCELED);
249 	    return TRUE;
250 	case IDD_ACMFORMATCHOOSE_CMB_FORMATTAG:
251 	    switch (HIWORD(wParam)) {
252 	    case CBN_SELCHANGE:
253 		MSACM_FillFormat(hWnd, afc);
254 		break;
255 	    default:
256 		TRACE("Dropped dlgNotif (fmtTag): 0x%08x 0x%08lx\n",
257 		      HIWORD(wParam), lParam);
258 		break;
259 	    }
260 	    break;
261 	case IDD_ACMFORMATCHOOSE_BTN_HELP:
262 	    if (afc->fdwStyle & ACMFORMATCHOOSE_STYLEF_SHOWHELP)
263 		SendMessageW(afc->hwndOwner,
264 			     RegisterWindowMessageW(ACMHELPMSGSTRINGW), 0L, 0L);
265 	    break;
266 
267 	default:
268 	    TRACE("Dropped dlgCmd: ctl=%d ntf=0x%04x 0x%08lx\n",
269 		  LOWORD(wParam), HIWORD(wParam), lParam);
270 	    break;
271 	}
272 	break;
273     case WM_CONTEXTMENU:
274 	if (afc->fdwStyle & ACMFORMATCHOOSE_STYLEF_CONTEXTHELP)
275 	    SendMessageW(afc->hwndOwner,
276 			 RegisterWindowMessageW(ACMHELPMSGCONTEXTMENUW),
277 			 wParam, lParam);
278 	break;
279 #if defined(WM_CONTEXTHELP)
280     case WM_CONTEXTHELP:
281 	if (afc->fdwStyle & ACMFORMATCHOOSE_STYLEF_CONTEXTHELP)
282 	    SendMessageW(afc->hwndOwner,
283 			 RegisterWindowMessageW(ACMHELPMSGCONTEXTHELPW),
284 			 wParam, lParam);
285 	break;
286 #endif
287     default:
288 	TRACE("Dropped dlgMsg: hwnd=%p msg=%i 0x%08lx 0x%08lx\n",
289 	      hWnd,  msg, wParam, lParam );
290 	break;
291     }
292     return FALSE;
293 }
294 
295 /***********************************************************************
296  *           acmFormatChooseA (MSACM32.@)
297  */
298 MMRESULT WINAPI acmFormatChooseA(PACMFORMATCHOOSEA pafmtc)
299 {
300     ACMFORMATCHOOSEW    afcw;
301     MMRESULT            ret;
302     LPWSTR              title = NULL;
303     LPWSTR              name = NULL;
304     LPWSTR              templ = NULL;
305     DWORD               sz;
306 
307     if (pafmtc->cbStruct < sizeof(ACMFORMATCHOOSEA))
308         return MMSYSERR_INVALPARAM;
309 
310     afcw.cbStruct  = sizeof(afcw);
311     afcw.fdwStyle  = pafmtc->fdwStyle;
312     afcw.hwndOwner = pafmtc->hwndOwner;
313     afcw.pwfx      = pafmtc->pwfx;
314     afcw.cbwfx     = pafmtc->cbwfx;
315     if (pafmtc->pszTitle)
316     {
317         sz = MultiByteToWideChar(CP_ACP, 0, pafmtc->pszTitle, -1, NULL, 0);
318         if (!(title = HeapAlloc(GetProcessHeap(), 0, sz * sizeof(WCHAR))))
319         {
320             ret = MMSYSERR_NOMEM;
321             goto done;
322         }
323         MultiByteToWideChar(CP_ACP, 0, pafmtc->pszTitle, -1, title, sz);
324     }
325     afcw.pszTitle  = title;
326     if (pafmtc->pszName)
327     {
328         sz = MultiByteToWideChar(CP_ACP, 0, pafmtc->pszName, -1, NULL, 0);
329         if (!(name = HeapAlloc(GetProcessHeap(), 0, sz * sizeof(WCHAR))))
330         {
331             ret = MMSYSERR_NOMEM;
332             goto done;
333         }
334         MultiByteToWideChar(CP_ACP, 0, pafmtc->pszName, -1, name, sz);
335     }
336     afcw.pszName   = name;
337     afcw.cchName   = pafmtc->cchName;
338     afcw.fdwEnum   = pafmtc->fdwEnum;
339     afcw.pwfxEnum  = pafmtc->pwfxEnum;
340     afcw.hInstance = pafmtc->hInstance;
341     if (pafmtc->pszTemplateName)
342     {
343         sz = MultiByteToWideChar(CP_ACP, 0, pafmtc->pszTemplateName, -1, NULL, 0);
344         if (!(templ = HeapAlloc(GetProcessHeap(), 0, sz * sizeof(WCHAR))))
345         {
346             ret = MMSYSERR_NOMEM;
347             goto done;
348         }
349         MultiByteToWideChar(CP_ACP, 0, pafmtc->pszTemplateName, -1, templ, sz);
350     }
351     afcw.pszTemplateName = templ;
352     /* FIXME: hook procs not supported yet */
353     if (pafmtc->pfnHook)
354     {
355         FIXME("Unsupported hook procs\n");
356         ret = MMSYSERR_NOTSUPPORTED;
357         goto done;
358     }
359     ret = acmFormatChooseW(&afcw);
360     if (ret == MMSYSERR_NOERROR)
361     {
362         WideCharToMultiByte(CP_ACP, 0, afcw.szFormatTag, -1, pafmtc->szFormatTag, sizeof(pafmtc->szFormatTag),
363                             NULL, NULL);
364         WideCharToMultiByte(CP_ACP, 0, afcw.szFormat, -1, pafmtc->szFormat, sizeof(pafmtc->szFormat),
365                             NULL, NULL);
366         if (pafmtc->pszName)
367             WideCharToMultiByte(CP_ACP, 0, afcw.pszName, -1, pafmtc->pszName, pafmtc->cchName, NULL, NULL);
368     }
369 done:
370     HeapFree(GetProcessHeap(), 0, title);
371     HeapFree(GetProcessHeap(), 0, name);
372     HeapFree(GetProcessHeap(), 0, templ);
373     return ret;
374 }
375 
376 /***********************************************************************
377  *           acmFormatChooseW (MSACM32.@)
378  */
379 MMRESULT WINAPI acmFormatChooseW(PACMFORMATCHOOSEW pafmtc)
380 {
381     if (pafmtc->cbStruct < sizeof(ACMFORMATCHOOSEW))
382         return MMSYSERR_INVALPARAM;
383 
384     if (!pafmtc->pwfx)
385         return MMSYSERR_INVALPARAM;
386 
387     if (pafmtc->fdwStyle & ACMFORMATCHOOSE_STYLEF_ENABLETEMPLATEHANDLE)
388         return DialogBoxIndirectParamW(MSACM_hInstance32, (LPCDLGTEMPLATEW)pafmtc->hInstance,
389                                        pafmtc->hwndOwner, FormatChooseDlgProc, (LPARAM)pafmtc);
390 
391     if (pafmtc->fdwStyle & ACMFORMATCHOOSE_STYLEF_ENABLETEMPLATE)
392         return DialogBoxParamW(pafmtc->hInstance, pafmtc->pszTemplateName,
393                                pafmtc->hwndOwner, FormatChooseDlgProc, (LPARAM)pafmtc);
394 
395     return DialogBoxParamW(MSACM_hInstance32, MAKEINTRESOURCEW(DLG_ACMFORMATCHOOSE_ID),
396                            pafmtc->hwndOwner, FormatChooseDlgProc, (LPARAM)pafmtc);
397 }
398 
399 /***********************************************************************
400  *           acmFormatDetailsA (MSACM32.@)
401  */
402 MMRESULT WINAPI acmFormatDetailsA(HACMDRIVER had, PACMFORMATDETAILSA pafd,
403 				  DWORD fdwDetails)
404 {
405     ACMFORMATDETAILSW	afdw;
406     MMRESULT		mmr;
407 
408     if (!pafd)
409         return MMSYSERR_INVALPARAM;
410 
411     memset(&afdw, 0, sizeof(afdw));
412     afdw.cbStruct = sizeof(afdw);
413     afdw.dwFormatIndex = pafd->dwFormatIndex;
414     afdw.dwFormatTag = pafd->dwFormatTag;
415     afdw.fdwSupport = pafd->fdwSupport;
416     afdw.pwfx = pafd->pwfx;
417     afdw.cbwfx = pafd->cbwfx;
418 
419     mmr = acmFormatDetailsW(had, &afdw, fdwDetails);
420     if (mmr == MMSYSERR_NOERROR) {
421 	pafd->dwFormatTag = afdw.dwFormatTag;
422 	pafd->fdwSupport = afdw.fdwSupport;
423         WideCharToMultiByte( CP_ACP, 0, afdw.szFormat, -1,
424                              pafd->szFormat, sizeof(pafd->szFormat), NULL, NULL );
425     }
426     return mmr;
427 }
428 
429 /***********************************************************************
430  *           acmFormatDetailsW (MSACM32.@)
431  */
432 MMRESULT WINAPI acmFormatDetailsW(HACMDRIVER had, PACMFORMATDETAILSW pafd, DWORD fdwDetails)
433 {
434     MMRESULT			mmr;
435     static const WCHAR		fmt1[] = {'%','d',' ','H','z',0};
436     static const WCHAR		fmt2[] = {';',' ','%','d',' ','b','i','t','s',0};
437     ACMFORMATTAGDETAILSW	aftd = {0};
438 
439     TRACE("(%p, %p, %d)\n", had, pafd, fdwDetails);
440 
441     if (!pafd)
442         return MMSYSERR_INVALPARAM;
443 
444     if (pafd->cbStruct < sizeof(*pafd)) return MMSYSERR_INVALPARAM;
445 
446     if (!pafd->pwfx)
447         return MMSYSERR_INVALPARAM;
448 
449     if (pafd->cbwfx < sizeof(PCMWAVEFORMAT))
450         return MMSYSERR_INVALPARAM;
451 
452     if (pafd->fdwSupport)
453         return MMSYSERR_INVALPARAM;
454 
455     if (pafd->dwFormatTag == WAVE_FORMAT_UNKNOWN)
456         return MMSYSERR_INVALPARAM;
457 
458     switch (fdwDetails) {
459     case ACM_FORMATDETAILSF_FORMAT:
460 	if (pafd->dwFormatTag != pafd->pwfx->wFormatTag) {
461 	    mmr = MMSYSERR_INVALPARAM;
462 	    break;
463 	}
464 	if (had == NULL) {
465 	    PWINE_ACMDRIVERID		padid;
466 
467 	    mmr = ACMERR_NOTPOSSIBLE;
468 	    for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) {
469 		/* should check for codec only */
470 		if (!(padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) &&
471 		    acmDriverOpen(&had, (HACMDRIVERID)padid, 0) == 0) {
472 		    mmr = MSACM_Message(had, ACMDM_FORMAT_DETAILS, (LPARAM)pafd, fdwDetails);
473 		    acmDriverClose(had, 0);
474 		    if (mmr == MMSYSERR_NOERROR) break;
475 		}
476 	    }
477 	} else {
478 	    mmr = MSACM_Message(had, ACMDM_FORMAT_DETAILS, (LPARAM)pafd, fdwDetails);
479 	}
480 	break;
481     case ACM_FORMATDETAILSF_INDEX:
482         aftd.cbStruct = sizeof(aftd);
483         aftd.dwFormatTag = pafd->dwFormatTag;
484         mmr = acmFormatTagDetailsW(had, &aftd, ACM_FORMATTAGDETAILSF_FORMATTAG);
485         if (mmr != MMSYSERR_NOERROR)
486             break;
487         if (pafd->dwFormatIndex >= aftd.cStandardFormats)
488         {
489             mmr = MMSYSERR_INVALPARAM;
490             break;
491         }
492 	mmr = MSACM_Message(had, ACMDM_FORMAT_DETAILS, (LPARAM)pafd, fdwDetails);
493 	break;
494     default:
495 	WARN("Unknown fdwDetails %08x\n", fdwDetails);
496 	mmr = MMSYSERR_INVALFLAG;
497 	break;
498     }
499 
500     if (mmr == MMSYSERR_NOERROR && pafd->szFormat[0] == 0) {
501 	wsprintfW(pafd->szFormat, fmt1, pafd->pwfx->nSamplesPerSec);
502 	if (pafd->pwfx->wBitsPerSample) {
503 	    wsprintfW(pafd->szFormat + lstrlenW(pafd->szFormat), fmt2,
504 		      pafd->pwfx->wBitsPerSample);
505 	}
506         MultiByteToWideChar( CP_ACP, 0, (pafd->pwfx->nChannels == 1) ? "; Mono" : "; Stereo", -1,
507                              pafd->szFormat + strlenW(pafd->szFormat),
508                              sizeof(pafd->szFormat)/sizeof(WCHAR) - strlenW(pafd->szFormat) );
509     }
510 
511     TRACE("=> %d\n", mmr);
512     return mmr;
513 }
514 
515 struct MSACM_FormatEnumWtoA_Instance {
516     PACMFORMATDETAILSA pafda;
517     DWORD_PTR          dwInstance;
518     ACMFORMATENUMCBA   fnCallback;
519 };
520 
521 static BOOL CALLBACK MSACM_FormatEnumCallbackWtoA(HACMDRIVERID hadid,
522 						  PACMFORMATDETAILSW pafdw,
523                                                   DWORD_PTR dwInstance,
524 						  DWORD fdwSupport)
525 {
526     struct MSACM_FormatEnumWtoA_Instance* pafei;
527 
528     pafei = (struct MSACM_FormatEnumWtoA_Instance*)dwInstance;
529 
530     pafei->pafda->dwFormatIndex = pafdw->dwFormatIndex;
531     pafei->pafda->dwFormatTag = pafdw->dwFormatTag;
532     pafei->pafda->fdwSupport = pafdw->fdwSupport;
533     WideCharToMultiByte( CP_ACP, 0, pafdw->szFormat, -1,
534                          pafei->pafda->szFormat, sizeof(pafei->pafda->szFormat), NULL, NULL );
535 
536     return (pafei->fnCallback)(hadid, pafei->pafda,
537 			       pafei->dwInstance, fdwSupport);
538 }
539 
540 /***********************************************************************
541  *           acmFormatEnumA (MSACM32.@)
542  */
543 MMRESULT WINAPI acmFormatEnumA(HACMDRIVER had, PACMFORMATDETAILSA pafda,
544                                ACMFORMATENUMCBA fnCallback,
545                                DWORD_PTR dwInstance, DWORD fdwEnum)
546 {
547     ACMFORMATDETAILSW		afdw;
548     struct MSACM_FormatEnumWtoA_Instance afei;
549 
550     if (!pafda)
551         return MMSYSERR_INVALPARAM;
552 
553     if (!fnCallback)
554         return MMSYSERR_INVALPARAM;
555 
556     if (pafda->cbStruct < sizeof(*pafda))
557         return MMSYSERR_INVALPARAM;
558 
559     memset(&afdw, 0, sizeof(afdw));
560     afdw.cbStruct = sizeof(afdw);
561     afdw.dwFormatIndex = pafda->dwFormatIndex;
562     afdw.dwFormatTag = pafda->dwFormatTag;
563     afdw.fdwSupport = pafda->fdwSupport;
564     afdw.pwfx = pafda->pwfx;
565     afdw.cbwfx = pafda->cbwfx;
566 
567     afei.pafda = pafda;
568     afei.dwInstance = dwInstance;
569     afei.fnCallback = fnCallback;
570 
571     return acmFormatEnumW(had, &afdw, MSACM_FormatEnumCallbackWtoA,
572                           (DWORD_PTR)&afei, fdwEnum);
573 }
574 
575 /***********************************************************************
576  *           acmFormatEnumW (MSACM32.@)
577  */
578 static BOOL MSACM_FormatEnumHelper(PWINE_ACMDRIVERID padid, HACMDRIVER had,
579 				   PACMFORMATDETAILSW pafd, PWAVEFORMATEX pwfxRef,
580                                    ACMFORMATENUMCBW fnCallback,
581                                    DWORD_PTR dwInstance, DWORD fdwEnum)
582 {
583     ACMFORMATTAGDETAILSW	aftd;
584     unsigned int			i, j;
585 
586     if (fdwEnum & ACM_FORMATENUMF_SUGGEST) {
587         HDRVR hdrvr;
588         ACMDRVFORMATSUGGEST adfs;
589         pafd->dwFormatIndex = 0;
590         memset(&aftd, 0, sizeof(aftd));
591         aftd.cbStruct = sizeof(aftd);
592         memset(&adfs, 0, sizeof(adfs));
593         adfs.cbStruct = sizeof(adfs);
594 
595         for (i = 0; i < padid->cFormatTags; i++) {
596             aftd.dwFormatTag = i;
597             pafd->dwFormatTag = aftd.dwFormatTag;
598             pafd->pwfx->wFormatTag = pafd->dwFormatTag;
599 
600             if (acmFormatTagDetailsW(had, &aftd, ACM_FORMATTAGDETAILSF_INDEX) != MMSYSERR_NOERROR)
601                 continue;
602 
603             adfs.cbwfxSrc = aftd.cbFormatSize;
604             adfs.cbwfxDst = aftd.cbFormatSize;
605             adfs.pwfxSrc = pwfxRef;
606             adfs.pwfxDst = pafd->pwfx;
607             pafd->fdwSupport = padid->fdwSupport;
608 
609             if ((fdwEnum & ACM_FORMATENUMF_WFORMATTAG) &&
610                 aftd.dwFormatTag != pwfxRef->wFormatTag)
611                 continue;
612 
613             if ((fdwEnum & ACM_FORMATENUMF_HARDWARE) &&
614                 !(pafd->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_HARDWARE))
615                 continue;
616 
617             hdrvr = OpenDriver(padid->pszFileName,0,0);
618             SendDriverMessage(hdrvr,ACMDM_FORMAT_SUGGEST,(LPARAM)&adfs,(fdwEnum & 0x000000FFL));
619 
620             if (acmFormatDetailsW(had, pafd, ACM_FORMATDETAILSF_FORMAT) != MMSYSERR_NOERROR)
621                 continue;
622 
623             pafd->cbwfx = sizeof(*(pafd->pwfx));
624 
625             if (!(fnCallback)((HACMDRIVERID)padid, pafd, dwInstance, padid->fdwSupport))
626                 return FALSE;
627         }
628     } else {
629         for (i = 0; i < padid->cFormatTags; i++) {
630             memset(&aftd, 0, sizeof(aftd));
631             aftd.cbStruct = sizeof(aftd);
632             aftd.dwFormatTagIndex = i;
633             if (acmFormatTagDetailsW(had, &aftd, ACM_FORMATTAGDETAILSF_INDEX) != MMSYSERR_NOERROR)
634                 continue;
635 
636             if ((fdwEnum & ACM_FORMATENUMF_WFORMATTAG) && aftd.dwFormatTag != pwfxRef->wFormatTag)
637                 continue;
638 
639             for (j = 0; j < aftd.cStandardFormats; j++) {
640                 pafd->dwFormatIndex = j;
641                 pafd->dwFormatTag = aftd.dwFormatTag;
642                 pafd->fdwSupport = 0;
643                 if (acmFormatDetailsW(had, pafd, ACM_FORMATDETAILSF_INDEX) != MMSYSERR_NOERROR)
644                     continue;
645 
646                 if ((fdwEnum & ACM_FORMATENUMF_NCHANNELS) &&
647                     pafd->pwfx->nChannels != pwfxRef->nChannels)
648                     continue;
649                 if ((fdwEnum & ACM_FORMATENUMF_NSAMPLESPERSEC) &&
650                     pafd->pwfx->nSamplesPerSec != pwfxRef->nSamplesPerSec)
651                     continue;
652                 if ((fdwEnum & ACM_FORMATENUMF_WBITSPERSAMPLE) &&
653                     pafd->pwfx->wBitsPerSample != pwfxRef->wBitsPerSample)
654                     continue;
655                 if ((fdwEnum & ACM_FORMATENUMF_HARDWARE) &&
656                     !(pafd->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_HARDWARE))
657                     continue;
658 
659             /* more checks to be done on fdwEnum */
660 
661                 if (!(fnCallback)((HACMDRIVERID)padid, pafd, dwInstance, padid->fdwSupport))
662                     return FALSE;
663             }
664         /* the "formats" used by the filters are also reported */
665         }
666     }
667     return TRUE;
668 }
669 
670 /**********************************************************************/
671 
672 MMRESULT WINAPI acmFormatEnumW(HACMDRIVER had, PACMFORMATDETAILSW pafd,
673                                ACMFORMATENUMCBW fnCallback,
674                                DWORD_PTR dwInstance, DWORD fdwEnum)
675 {
676     PWINE_ACMDRIVERID		padid;
677     WAVEFORMATEX		wfxRef;
678     BOOL			ret;
679     DWORD			cbwfxMax;
680     MMRESULT			mmr;
681 
682     TRACE("(%p, %p, %p, %ld, %d)\n",
683 	  had, pafd, fnCallback, dwInstance, fdwEnum);
684 
685     if (!pafd)
686         return MMSYSERR_INVALPARAM;
687 
688     if (!fnCallback)
689         return MMSYSERR_INVALPARAM;
690 
691     if (pafd->cbStruct < sizeof(*pafd))
692         return MMSYSERR_INVALPARAM;
693 
694     if (pafd->fdwSupport)
695         return MMSYSERR_INVALPARAM;
696 
697     if (!pafd->pwfx)
698         return MMSYSERR_INVALPARAM;
699 
700     if (fdwEnum & (ACM_FORMATENUMF_WFORMATTAG|ACM_FORMATENUMF_NCHANNELS|
701 		   ACM_FORMATENUMF_NSAMPLESPERSEC|ACM_FORMATENUMF_WBITSPERSAMPLE|
702 		   ACM_FORMATENUMF_CONVERT|ACM_FORMATENUMF_SUGGEST))
703         wfxRef = *pafd->pwfx;
704 
705     if ((fdwEnum & ACM_FORMATENUMF_HARDWARE) &&
706 	!(fdwEnum & (ACM_FORMATENUMF_INPUT|ACM_FORMATENUMF_OUTPUT)))
707 	return MMSYSERR_INVALPARAM;
708 
709     if ((fdwEnum & ACM_FORMATENUMF_WFORMATTAG) &&
710 	(pafd->dwFormatTag != pafd->pwfx->wFormatTag))
711 	return MMSYSERR_INVALPARAM;
712 
713     if (fdwEnum & (ACM_FORMATENUMF_CONVERT|ACM_FORMATENUMF_INPUT|ACM_FORMATENUMF_OUTPUT))
714 	FIXME("Unsupported fdwEnum values %08x\n", fdwEnum);
715 
716     mmr = acmMetrics((HACMOBJ)had, ACM_METRIC_MAX_SIZE_FORMAT, &cbwfxMax);
717     if (mmr != MMSYSERR_NOERROR)
718         return mmr;
719     if (pafd->cbwfx < cbwfxMax)
720         return MMSYSERR_INVALPARAM;
721 
722     if (had) {
723 	HACMDRIVERID	hadid;
724 
725 	if (acmDriverID((HACMOBJ)had, &hadid, 0) != MMSYSERR_NOERROR)
726 	    return MMSYSERR_INVALHANDLE;
727 	MSACM_FormatEnumHelper(MSACM_GetDriverID(hadid), had, pafd, &wfxRef,
728 			       fnCallback, dwInstance, fdwEnum);
729 	return MMSYSERR_NOERROR;
730     }
731     for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) {
732 	    /* should check for codec only */
733 	    if ((padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) ||
734 		acmDriverOpen(&had, (HACMDRIVERID)padid, 0) != MMSYSERR_NOERROR)
735 		continue;
736 	    ret = MSACM_FormatEnumHelper(padid, had, pafd, &wfxRef,
737 					 fnCallback, dwInstance, fdwEnum);
738 	    acmDriverClose(had, 0);
739 	    if (!ret) break;
740     }
741     return MMSYSERR_NOERROR;
742 }
743 
744 /***********************************************************************
745  *           acmFormatSuggest (MSACM32.@)
746  */
747 MMRESULT WINAPI acmFormatSuggest(HACMDRIVER had, PWAVEFORMATEX pwfxSrc,
748 				 PWAVEFORMATEX pwfxDst, DWORD cbwfxDst, DWORD fdwSuggest)
749 {
750     ACMFORMATTAGDETAILSW aftd = {0};
751     ACMDRVFORMATSUGGEST	adfg;
752     MMRESULT		mmr;
753 
754     TRACE("(%p, %p, %p, %d, %d)\n",
755 	  had, pwfxSrc, pwfxDst, cbwfxDst, fdwSuggest);
756 
757     if (!pwfxSrc || !pwfxDst)
758         return MMSYSERR_INVALPARAM;
759 
760     if (fdwSuggest & ~(ACM_FORMATSUGGESTF_NCHANNELS|ACM_FORMATSUGGESTF_NSAMPLESPERSEC|
761 		       ACM_FORMATSUGGESTF_WBITSPERSAMPLE|ACM_FORMATSUGGESTF_WFORMATTAG))
762 	return MMSYSERR_INVALFLAG;
763 
764     /* if we were given PCM, try to convert to PCM */
765     if (pwfxSrc->wFormatTag == WAVE_FORMAT_PCM && !had &&
766         !(fdwSuggest & ACM_FORMATSUGGESTF_WFORMATTAG))
767     {
768         ACMFORMATDETAILSW afd = {0};
769         afd.cbStruct = sizeof(afd);
770         afd.dwFormatTag = WAVE_FORMAT_PCM;
771         afd.pwfx = pwfxSrc;
772         afd.cbwfx = sizeof(PCMWAVEFORMAT);
773         if (!acmFormatDetailsW(had, &afd, ACM_FORMATDETAILSF_FORMAT))
774         {
775             memcpy(pwfxDst, pwfxSrc, sizeof(PCMWAVEFORMAT));
776             return MMSYSERR_NOERROR;
777         }
778     }
779 
780     aftd.cbStruct = sizeof(aftd);
781     if (fdwSuggest & ACM_FORMATSUGGESTF_WFORMATTAG)
782         aftd.dwFormatTag = pwfxDst->wFormatTag;
783     mmr = acmFormatTagDetailsW(had, &aftd, ACM_FORMATTAGDETAILSF_LARGESTSIZE);
784     if ((fdwSuggest & ACM_FORMATSUGGESTF_WFORMATTAG) && mmr == ACMERR_NOTPOSSIBLE)
785         return ACMERR_NOTPOSSIBLE;
786 
787     if (cbwfxDst < aftd.cbFormatSize)
788         return MMSYSERR_INVALPARAM;
789 
790     adfg.cbStruct = sizeof(adfg);
791     adfg.fdwSuggest = fdwSuggest;
792     adfg.pwfxSrc = pwfxSrc;
793     adfg.cbwfxSrc = (pwfxSrc->wFormatTag == WAVE_FORMAT_PCM) ?
794 	sizeof(WAVEFORMATEX) : (sizeof(WAVEFORMATEX) + pwfxSrc->cbSize);
795     adfg.pwfxDst = pwfxDst;
796     adfg.cbwfxDst = cbwfxDst;
797 
798     if (had == NULL) {
799 	PWINE_ACMDRIVERID	padid;
800 
801 	/* MS doc says: ACM finds the best suggestion.
802 	 * Well, first found will be the "best"
803 	 */
804 	mmr = ACMERR_NOTPOSSIBLE;
805 	for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) {
806 	    /* should check for codec only */
807 	    if ((padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) ||
808 		acmDriverOpen(&had, (HACMDRIVERID)padid, 0) != MMSYSERR_NOERROR)
809 		continue;
810 
811 	    if (MSACM_Message(had, ACMDM_FORMAT_SUGGEST, (LPARAM)&adfg, 0L) == MMSYSERR_NOERROR) {
812 		mmr = MMSYSERR_NOERROR;
813 		break;
814 	    }
815 	    acmDriverClose(had, 0);
816 	}
817     } else {
818 	mmr = MSACM_Message(had, ACMDM_FORMAT_SUGGEST, (LPARAM)&adfg, 0L);
819     }
820     return mmr;
821 }
822 
823 /***********************************************************************
824  *           acmFormatTagDetailsA (MSACM32.@)
825  */
826 MMRESULT WINAPI acmFormatTagDetailsA(HACMDRIVER had, PACMFORMATTAGDETAILSA paftda,
827 				     DWORD fdwDetails)
828 {
829     ACMFORMATTAGDETAILSW	aftdw;
830     MMRESULT			mmr;
831 
832     memset(&aftdw, 0, sizeof(aftdw));
833     aftdw.cbStruct = sizeof(aftdw);
834     aftdw.dwFormatTagIndex = paftda->dwFormatTagIndex;
835     aftdw.dwFormatTag = paftda->dwFormatTag;
836 
837     mmr = acmFormatTagDetailsW(had, &aftdw, fdwDetails);
838     if (mmr == MMSYSERR_NOERROR) {
839 	paftda->dwFormatTag = aftdw.dwFormatTag;
840 	paftda->dwFormatTagIndex = aftdw.dwFormatTagIndex;
841 	paftda->cbFormatSize = aftdw.cbFormatSize;
842 	paftda->fdwSupport = aftdw.fdwSupport;
843 	paftda->cStandardFormats = aftdw.cStandardFormats;
844         WideCharToMultiByte( CP_ACP, 0, aftdw.szFormatTag, -1, paftda->szFormatTag,
845                              sizeof(paftda->szFormatTag), NULL, NULL );
846     }
847     return mmr;
848 }
849 
850 /***********************************************************************
851  *           acmFormatTagDetailsW (MSACM32.@)
852  */
853 MMRESULT WINAPI acmFormatTagDetailsW(HACMDRIVER had, PACMFORMATTAGDETAILSW paftd,
854 				     DWORD fdwDetails)
855 {
856     PWINE_ACMDRIVERID	padid;
857     MMRESULT		mmr = ACMERR_NOTPOSSIBLE;
858 
859     TRACE("(%p, %p, %d)\n", had, paftd, fdwDetails);
860 
861     if (fdwDetails & ~(ACM_FORMATTAGDETAILSF_FORMATTAG|ACM_FORMATTAGDETAILSF_INDEX|
862 		       ACM_FORMATTAGDETAILSF_LARGESTSIZE))
863 	return MMSYSERR_INVALFLAG;
864 
865     switch (fdwDetails) {
866     case ACM_FORMATTAGDETAILSF_FORMATTAG:
867 	if (had == NULL) {
868 	    for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) {
869 		/* should check for codec only */
870 		if (!(padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) &&
871 		    MSACM_FindFormatTagInCache(padid, paftd->dwFormatTag, NULL) &&
872 		    acmDriverOpen(&had, (HACMDRIVERID)padid, 0) == 0) {
873 		    mmr = MSACM_Message(had, ACMDM_FORMATTAG_DETAILS, (LPARAM)paftd, fdwDetails);
874 		    acmDriverClose(had, 0);
875 		    if (mmr == MMSYSERR_NOERROR) break;
876 		}
877 	    }
878 	} else {
879 	    PWINE_ACMDRIVER	pad = MSACM_GetDriver(had);
880 
881 	    if (pad && MSACM_FindFormatTagInCache(pad->obj.pACMDriverID, paftd->dwFormatTag, NULL))
882 		mmr = MSACM_Message(had, ACMDM_FORMATTAG_DETAILS, (LPARAM)paftd, fdwDetails);
883 	}
884 	break;
885 
886     case ACM_FORMATTAGDETAILSF_INDEX:
887 	if (had != NULL) {
888 	    PWINE_ACMDRIVER	pad = MSACM_GetDriver(had);
889 
890 	    if (pad && paftd->dwFormatTagIndex < pad->obj.pACMDriverID->cFormatTags)
891 		mmr = MSACM_Message(had, ACMDM_FORMATTAG_DETAILS, (LPARAM)paftd, fdwDetails);
892 	}
893 	break;
894 
895     case ACM_FORMATTAGDETAILSF_LARGESTSIZE:
896 	if (had == NULL) {
897 	    ACMFORMATTAGDETAILSW	tmp;
898 	    DWORD			ft = paftd->dwFormatTag;
899 
900 	    for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) {
901 		/* should check for codec only */
902 		if (!(padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) &&
903 		    acmDriverOpen(&had, (HACMDRIVERID)padid, 0) == 0) {
904 
905 		    memset(&tmp, 0, sizeof(tmp));
906 		    tmp.cbStruct = sizeof(tmp);
907 		    tmp.dwFormatTag = ft;
908 
909 		    if (MSACM_Message(had, ACMDM_FORMATTAG_DETAILS,
910 				      (LPARAM)&tmp, fdwDetails) == MMSYSERR_NOERROR) {
911 			if (mmr == ACMERR_NOTPOSSIBLE ||
912 			    paftd->cbFormatSize < tmp.cbFormatSize) {
913 			    *paftd = tmp;
914 			    mmr = MMSYSERR_NOERROR;
915 			}
916 		    }
917 		    acmDriverClose(had, 0);
918 		}
919 	    }
920 	} else {
921 	    mmr = MSACM_Message(had, ACMDM_FORMATTAG_DETAILS, (LPARAM)paftd, fdwDetails);
922 	}
923 	break;
924 
925     default:
926 	WARN("Unsupported fdwDetails=%08x\n", fdwDetails);
927 	mmr = MMSYSERR_ERROR;
928     }
929 
930     if (mmr == MMSYSERR_NOERROR &&
931 	paftd->dwFormatTag == WAVE_FORMAT_PCM && paftd->szFormatTag[0] == 0)
932         MultiByteToWideChar( CP_ACP, 0, "PCM", -1, paftd->szFormatTag,
933                              sizeof(paftd->szFormatTag)/sizeof(WCHAR) );
934 
935     return mmr;
936 }
937 
938 struct MSACM_FormatTagEnumWtoA_Instance {
939     PACMFORMATTAGDETAILSA paftda;
940     DWORD_PTR             dwInstance;
941     ACMFORMATTAGENUMCBA   fnCallback;
942 };
943 
944 static BOOL CALLBACK MSACM_FormatTagEnumCallbackWtoA(HACMDRIVERID hadid,
945 						     PACMFORMATTAGDETAILSW paftdw,
946                                                      DWORD_PTR dwInstance,
947 						     DWORD fdwSupport)
948 {
949     struct MSACM_FormatTagEnumWtoA_Instance* paftei;
950 
951     paftei = (struct MSACM_FormatTagEnumWtoA_Instance*)dwInstance;
952 
953     paftei->paftda->dwFormatTagIndex = paftdw->dwFormatTagIndex;
954     paftei->paftda->dwFormatTag = paftdw->dwFormatTag;
955     paftei->paftda->cbFormatSize = paftdw->cbFormatSize;
956     paftei->paftda->fdwSupport = paftdw->fdwSupport;
957     paftei->paftda->cStandardFormats = paftdw->cStandardFormats;
958     WideCharToMultiByte( CP_ACP, 0, paftdw->szFormatTag, -1, paftei->paftda->szFormatTag,
959                          sizeof(paftei->paftda->szFormatTag), NULL, NULL );
960 
961     return (paftei->fnCallback)(hadid, paftei->paftda,
962 				paftei->dwInstance, fdwSupport);
963 }
964 
965 /***********************************************************************
966  *           acmFormatTagEnumA (MSACM32.@)
967  */
968 MMRESULT WINAPI acmFormatTagEnumA(HACMDRIVER had, PACMFORMATTAGDETAILSA paftda,
969                                   ACMFORMATTAGENUMCBA fnCallback,
970                                   DWORD_PTR dwInstance, DWORD fdwEnum)
971 {
972     ACMFORMATTAGDETAILSW	aftdw;
973     struct MSACM_FormatTagEnumWtoA_Instance aftei;
974 
975     if (!paftda)
976         return MMSYSERR_INVALPARAM;
977 
978     if (paftda->cbStruct < sizeof(*paftda))
979         return MMSYSERR_INVALPARAM;
980 
981     if (fdwEnum != 0)
982         return MMSYSERR_INVALFLAG;
983 
984     memset(&aftdw, 0, sizeof(aftdw));
985     aftdw.cbStruct = sizeof(aftdw);
986     aftdw.dwFormatTagIndex = paftda->dwFormatTagIndex;
987     aftdw.dwFormatTag = paftda->dwFormatTag;
988 
989     aftei.paftda = paftda;
990     aftei.dwInstance = dwInstance;
991     aftei.fnCallback = fnCallback;
992 
993     return acmFormatTagEnumW(had, &aftdw, MSACM_FormatTagEnumCallbackWtoA,
994                              (DWORD_PTR)&aftei, fdwEnum);
995 }
996 
997 /***********************************************************************
998  *           acmFormatTagEnumW (MSACM32.@)
999  */
1000 MMRESULT WINAPI acmFormatTagEnumW(HACMDRIVER had, PACMFORMATTAGDETAILSW paftd,
1001                                   ACMFORMATTAGENUMCBW fnCallback,
1002                                   DWORD_PTR dwInstance, DWORD fdwEnum)
1003 {
1004     PWINE_ACMDRIVERID   padid;
1005     unsigned int        i;
1006     BOOL                bPcmDone = FALSE;
1007 
1008     TRACE("(%p, %p, %p, %ld, %d)\n",
1009           had, paftd, fnCallback, dwInstance, fdwEnum);
1010 
1011     if (!paftd)
1012         return MMSYSERR_INVALPARAM;
1013 
1014     if (paftd->cbStruct < sizeof(*paftd))
1015         return MMSYSERR_INVALPARAM;
1016 
1017     if (fdwEnum != 0)
1018         return MMSYSERR_INVALFLAG;
1019 
1020     /* (WS) MSDN info page says that if had != 0, then we should find
1021      * the specific driver to get its tags from. Therefore I'm removing
1022      * the FIXME call and adding a search block below. It also seems
1023      * that the lack of this functionality was the responsible for
1024      * codecs to be multiply and incorrectly listed.
1025      */
1026 
1027     /* if (had) FIXME("had != NULL, not supported\n"); */
1028 
1029     if (had) {
1030         if (acmDriverID((HACMOBJ)had, (HACMDRIVERID *)&padid, 0) != MMSYSERR_NOERROR)
1031         return MMSYSERR_INVALHANDLE;
1032 
1033         for (i = 0; i < padid->cFormatTags; i++) {
1034             paftd->dwFormatTagIndex = i;
1035             if (MSACM_Message(had, ACMDM_FORMATTAG_DETAILS,
1036                 (LPARAM)paftd, ACM_FORMATTAGDETAILSF_INDEX) == MMSYSERR_NOERROR) {
1037                 if (paftd->dwFormatTag == WAVE_FORMAT_PCM) {
1038                     if (paftd->szFormatTag[0] == 0)
1039                         MultiByteToWideChar( CP_ACP, 0, "PCM", -1, paftd->szFormatTag,
1040                                             sizeof(paftd->szFormatTag)/sizeof(WCHAR) );
1041                     /* (WS) I'm preserving this PCM hack since it seems to be
1042                      * correct. Please notice this block was borrowed from
1043                      * below.
1044                      */
1045                     if (bPcmDone) continue;
1046                     bPcmDone = TRUE;
1047                 }
1048                 if (!(fnCallback)((HACMDRIVERID)padid, paftd, dwInstance, padid->fdwSupport))
1049                     return MMSYSERR_NOERROR;
1050             }
1051         }
1052     }
1053     /* if had==0 then search for the first suitable driver */
1054     else {
1055         for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) {
1056             /* should check for codec only */
1057             if (!(padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) &&
1058                 acmDriverOpen(&had, (HACMDRIVERID)padid, 0) == MMSYSERR_NOERROR) {
1059                 for (i = 0; i < padid->cFormatTags; i++) {
1060                     paftd->dwFormatTagIndex = i;
1061                     if (MSACM_Message(had, ACMDM_FORMATTAG_DETAILS,
1062                         (LPARAM)paftd, ACM_FORMATTAGDETAILSF_INDEX) == MMSYSERR_NOERROR) {
1063                         if (paftd->dwFormatTag == WAVE_FORMAT_PCM) {
1064                             if (paftd->szFormatTag[0] == 0)
1065                                 MultiByteToWideChar( CP_ACP, 0, "PCM", -1, paftd->szFormatTag,
1066                                                      sizeof(paftd->szFormatTag)/sizeof(WCHAR) );
1067                             /* FIXME (EPP): I'm not sure this is the correct
1068                              * algorithm (should make more sense to apply the same
1069                              * for all already loaded formats, but this will do
1070                              * for now
1071                              */
1072                             if (bPcmDone) continue;
1073                             bPcmDone = TRUE;
1074                         }
1075                         if (!(fnCallback)((HACMDRIVERID)padid, paftd, dwInstance, padid->fdwSupport)) {
1076                             acmDriverClose(had, 0);
1077                             return MMSYSERR_NOERROR;
1078                         }
1079                     }
1080                 }
1081                 acmDriverClose(had, 0);
1082             }
1083         }
1084     }
1085     return MMSYSERR_NOERROR;
1086 }
1087