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