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