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