msvcrt: Add a helper function to flush all buffers.
[wine] / dlls / dmloader / loader.c
1 /* IDirectMusicLoaderImpl
2  *
3  * Copyright (C) 2003-2004 Rok Mandeljc
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18  */
19
20 #include "dmloader_private.h"
21
22 WINE_DEFAULT_DEBUG_CHANNEL(dmloader);
23
24 static HRESULT DMUSIC_InitLoaderSettings (LPDIRECTMUSICLOADER8 iface);
25 static HRESULT DMUSIC_GetLoaderSettings (LPDIRECTMUSICLOADER8 iface, REFGUID pClassID, WCHAR* wszSearchPath, LPBOOL pbCache);
26 static HRESULT DMUSIC_SetLoaderSettings (LPDIRECTMUSICLOADER8 iface, REFGUID pClassID, WCHAR* wszSearchPath, LPBOOL pbCache);
27
28 static HRESULT DMUSIC_CopyDescriptor (LPDMUS_OBJECTDESC pDst, LPDMUS_OBJECTDESC pSrc) {
29         TRACE(": copy\n%s\n", debugstr_DMUS_OBJECTDESC(pSrc));
30         /* copy field by field */
31         if (pSrc->dwValidData & DMUS_OBJ_CLASS) pDst->guidClass = pSrc->guidClass;
32         if (pSrc->dwValidData & DMUS_OBJ_OBJECT) pDst->guidObject = pSrc->guidObject;
33         if (pSrc->dwValidData & DMUS_OBJ_DATE) pDst->ftDate = pSrc->ftDate;
34         if (pSrc->dwValidData & DMUS_OBJ_VERSION) pDst->vVersion = pSrc->vVersion;
35         if (pSrc->dwValidData & DMUS_OBJ_NAME) strcpyW (pDst->wszName, pSrc->wszName);
36         if (pSrc->dwValidData & DMUS_OBJ_CATEGORY) strcpyW (pDst->wszCategory, pSrc->wszCategory);
37         if (pSrc->dwValidData & DMUS_OBJ_FILENAME) strcpyW (pDst->wszFileName, pSrc->wszFileName);
38         if (pSrc->dwValidData & DMUS_OBJ_STREAM) IStream_Clone (pSrc->pStream, &pDst->pStream);
39         if (pSrc->dwValidData & DMUS_OBJ_MEMORY) {
40                 pDst->pbMemData = pSrc->pbMemData;
41                 pDst->llMemLength = pSrc->llMemLength;
42         }
43         /* set flags */
44         pDst->dwValidData |= pSrc->dwValidData;
45         return S_OK;
46 }
47
48
49 static BOOL DMUSIC_IsValidLoadableClass (REFCLSID pClassID) {
50         if (IsEqualCLSID(pClassID, &CLSID_DirectMusicAudioPathConfig) ||
51                 IsEqualCLSID(pClassID, &CLSID_DirectMusicBand) ||
52                 IsEqualCLSID(pClassID, &CLSID_DirectMusicContainer) ||
53                 IsEqualCLSID(pClassID, &CLSID_DirectMusicCollection) ||
54                 IsEqualCLSID(pClassID, &CLSID_DirectMusicChordMap) ||
55                 IsEqualCLSID(pClassID, &CLSID_DirectMusicSegment) ||
56                 IsEqualCLSID(pClassID, &CLSID_DirectMusicScript) ||
57                 IsEqualCLSID(pClassID, &CLSID_DirectMusicSong) ||
58                 IsEqualCLSID(pClassID, &CLSID_DirectMusicStyle) ||
59                 IsEqualCLSID(pClassID, &CLSID_DirectMusicGraph) ||
60                 IsEqualCLSID(pClassID, &CLSID_DirectSoundWave) ||
61                 IsEqualCLSID(pClassID, &GUID_DirectMusicAllTypes))
62                 return TRUE;
63         else
64                 return FALSE;
65 }
66
67 /*****************************************************************************
68  * IDirectMusicLoaderImpl implementation
69  */
70 /* IUnknown/IDirectMusicLoader(8) part: */
71
72 static HRESULT WINAPI IDirectMusicLoaderImpl_IDirectMusicLoader_QueryInterface (LPDIRECTMUSICLOADER8 iface, REFIID riid, LPVOID *ppobj) {
73         ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface);
74
75         TRACE("(%p, %s, %p)\n",This, debugstr_dmguid(riid), ppobj);
76         if (IsEqualIID (riid, &IID_IUnknown) || 
77             IsEqualIID (riid, &IID_IDirectMusicLoader) ||
78             IsEqualIID (riid, &IID_IDirectMusicLoader8)) {
79                 IDirectMusicLoader_AddRef (iface);
80                 *ppobj = This;
81                 return S_OK;
82         }
83         
84         WARN(": not found\n");
85         return E_NOINTERFACE;
86 }
87
88 static ULONG WINAPI IDirectMusicLoaderImpl_IDirectMusicLoader_AddRef (LPDIRECTMUSICLOADER8 iface) {
89         ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface);
90         TRACE("(%p): AddRef from %d\n", This, This->dwRef);
91         return InterlockedIncrement (&This->dwRef);
92 }
93
94 static ULONG WINAPI IDirectMusicLoaderImpl_IDirectMusicLoader_Release (LPDIRECTMUSICLOADER8 iface) {
95         ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface);
96         
97         DWORD dwRef = InterlockedDecrement (&This->dwRef);
98         TRACE("(%p): ReleaseRef to %d\n", This, This->dwRef);
99         if (dwRef == 0) {
100             /* firstly, release the cache */
101             IDirectMusicLoader8_ClearCache (iface, &GUID_DirectMusicAllTypes);
102             /* FIXME: release all allocated entries */
103             HeapFree (GetProcessHeap(), 0, This);
104             unlock_module();
105         }
106
107         return dwRef;
108 }
109
110 static HRESULT WINAPI IDirectMusicLoaderImpl_IDirectMusicLoader_GetObject (LPDIRECTMUSICLOADER8 iface, LPDMUS_OBJECTDESC pDesc, REFIID riid, LPVOID* ppv) {
111         ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface);
112         HRESULT result = S_OK;
113         HRESULT ret = S_OK; /* used at the end of function, to determine whether everything went OK */
114         
115         struct list *pEntry;
116         LPWINE_LOADER_ENTRY pObjectEntry = NULL;
117         LPSTREAM pStream;
118         IPersistStream* pPersistStream = NULL;
119
120         LPDIRECTMUSICOBJECT pObject;
121         DMUS_OBJECTDESC GotDesc;
122         BOOL bCache;
123
124         TRACE("(%p, %p, %s, %p): pDesc:\n%s\n", This, pDesc, debugstr_dmguid(riid), ppv, debugstr_DMUS_OBJECTDESC(pDesc));
125         
126         /* sometimes it happens that guidClass is missing... which is a BadThingTM */
127         if (!(pDesc->dwValidData & DMUS_OBJ_CLASS)) {
128           ERR(": guidClass not valid but needed\n");
129           *ppv = NULL;
130           return DMUS_E_LOADER_NOCLASSID;
131         }
132         
133         /* OK, first we iterate thru the list of objects we know about; these are either loaded (GetObject, LoadObjectFromFile)
134            or set via SetObject; */
135         TRACE(": looking if we have object in the cache or if it can be found via alias\n");
136         LIST_FOR_EACH(pEntry, This->pObjects) {
137                 LPWINE_LOADER_ENTRY pExistingEntry = LIST_ENTRY(pEntry, WINE_LOADER_ENTRY, entry);
138                 if ((pDesc->dwValidData & DMUS_OBJ_OBJECT) &&
139                         (pExistingEntry->Desc.dwValidData & DMUS_OBJ_OBJECT) &&
140                         IsEqualGUID (&pDesc->guidObject, &pExistingEntry->Desc.guidObject)) {
141                         TRACE(": found it by object GUID\n");
142                         /* I suppose such stuff can happen only when GUID for object is given (GUID_DefaultGMCollection) */
143                         if (pExistingEntry->bInvalidDefaultDLS) {
144                                 TRACE(": found faulty default DLS collection... enabling M$ compliant behaviour\n");
145                                 return DMUS_E_LOADER_NOFILENAME;        
146                         }
147                         if (pExistingEntry->Desc.dwValidData & DMUS_OBJ_LOADED) {
148                                 TRACE(": already loaded\n");
149                                 return IDirectMusicObject_QueryInterface (pExistingEntry->pObject, riid, ppv);
150                         } else {
151                                 TRACE(": not loaded yet\n");
152                                 pObjectEntry = pExistingEntry;
153                         }
154                 }
155                 else if ((pDesc->dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH)) &&
156                                 (pExistingEntry->Desc.dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH)) &&
157                                 !strncmpW (pDesc->wszFileName, pExistingEntry->Desc.wszFileName, DMUS_MAX_FILENAME)) {
158                         TRACE(": found it by fullpath filename\n");
159                         if (pExistingEntry->Desc.dwValidData & DMUS_OBJ_LOADED) {
160                                 TRACE(": already loaded\n");
161                                 return IDirectMusicObject_QueryInterface (pExistingEntry->pObject, riid, ppv);
162                         } else {
163                                 TRACE(": not loaded yet\n");
164                                 pObjectEntry = pExistingEntry;
165                         }
166                 }
167                 else if ((pDesc->dwValidData & (DMUS_OBJ_NAME | DMUS_OBJ_CATEGORY)) &&
168                                 (pExistingEntry->Desc.dwValidData & (DMUS_OBJ_NAME | DMUS_OBJ_CATEGORY)) &&
169                                 !strncmpW (pDesc->wszName, pExistingEntry->Desc.wszName, DMUS_MAX_NAME) &&
170                                 !strncmpW (pDesc->wszCategory, pExistingEntry->Desc.wszCategory, DMUS_MAX_CATEGORY)) {
171                         TRACE(": found it by name and category\n");
172                         if (pExistingEntry->Desc.dwValidData & DMUS_OBJ_LOADED) {
173                                 TRACE(": already loaded\n");
174                                 return IDirectMusicObject_QueryInterface (pExistingEntry->pObject, riid, ppv);
175                         } else {
176                                 TRACE(": not loaded yet\n");
177                                 pObjectEntry = pExistingEntry;
178                         }
179                 }
180                 else if ((pDesc->dwValidData & DMUS_OBJ_NAME) &&
181                                 (pExistingEntry->Desc.dwValidData & DMUS_OBJ_NAME) &&
182                                 !strncmpW (pDesc->wszName, pExistingEntry->Desc.wszName, DMUS_MAX_NAME)) {
183                         TRACE(": found it by name\n");
184                         if (pExistingEntry->Desc.dwValidData & DMUS_OBJ_LOADED) {
185                                 TRACE(": already loaded\n");
186                                 return IDirectMusicObject_QueryInterface (pExistingEntry->pObject, riid, ppv);
187                         } else {
188                                 TRACE(": not loaded yet\n");
189                                 pObjectEntry = pExistingEntry;
190                         }
191                 }
192                 else if ((pDesc->dwValidData & DMUS_OBJ_FILENAME) &&
193                                 (pExistingEntry->Desc.dwValidData & DMUS_OBJ_FILENAME) &&
194                                 !strncmpW (pDesc->wszFileName, pExistingEntry->Desc.wszFileName, DMUS_MAX_FILENAME)) {
195                         TRACE(": found it by filename\n");                              
196                         if (pExistingEntry->Desc.dwValidData & DMUS_OBJ_LOADED) {
197                                 TRACE(": already loaded\n");
198                                 return IDirectMusicObject_QueryInterface (pExistingEntry->pObject, riid, ppv);
199                         } else {
200                                 TRACE(": not loaded yet\n");
201                                 pObjectEntry = pExistingEntry;
202                         }
203                 }
204         }
205         
206         /* basically, if we found alias, we use its descriptor to load...
207            else we use info we were given */
208         if (pObjectEntry) {
209                 TRACE(": found alias entry for requested object... using stored info\n");
210                 /* I think in certain cases it can happen that entry's descriptor lacks info about
211                    where to load from (e.g.: if we loaded from stream and then released object
212                    from cache; then only its CLSID, GUID and perhaps name are left); so just
213                    overwrite whatever info the entry has (since it ought to be 100% correct) */
214                 DMUSIC_CopyDescriptor (pDesc, &pObjectEntry->Desc);
215                 /*pDesc = &pObjectEntry->Desc; */ /* FIXME: is this OK? */
216         } else {
217                 TRACE(": no cache/alias entry found for requested object\n");
218         }
219         
220         if (pDesc->dwValidData & DMUS_OBJ_URL) {
221                 TRACE(": loading from URLs not supported yet\n");
222                 return DMUS_E_LOADER_FORMATNOTSUPPORTED;
223         }
224         else if (pDesc->dwValidData & DMUS_OBJ_FILENAME) {
225                 /* load object from file */
226                 /* generate filename; if it's full path, don't add search 
227                    directory path, otherwise do */
228                 WCHAR wszFileName[MAX_PATH];
229
230                 if (pDesc->dwValidData & DMUS_OBJ_FULLPATH) {
231                         lstrcpyW(wszFileName, pDesc->wszFileName);
232                 } else {
233                         WCHAR *p, wszSearchPath[MAX_PATH];
234                         DMUSIC_GetLoaderSettings (iface, &pDesc->guidClass, wszSearchPath, NULL);
235                         lstrcpyW(wszFileName, wszSearchPath);
236                         p = wszFileName + lstrlenW(wszFileName);
237                         if (p > wszFileName && p[-1] != '\\') *p++ = '\\';
238                         strcpyW(p, pDesc->wszFileName);
239                 }
240                 TRACE(": loading from file (%s)\n", debugstr_w(wszFileName));
241                 /* create stream and associate it with file */                  
242                 result = DMUSIC_CreateDirectMusicLoaderFileStream ((LPVOID*)&pStream);
243                 if (FAILED(result)) {
244                         ERR(": could not create file stream\n");
245                         return result;
246                 }
247                 result = IDirectMusicLoaderFileStream_Attach (pStream, wszFileName, iface);
248                 if (FAILED(result)) {
249                         ERR(": could not attach stream to file\n");
250                         IStream_Release (pStream);
251                         return result;
252                 }
253         }
254         else if (pDesc->dwValidData & DMUS_OBJ_MEMORY) {
255                 /* load object from resource */
256                 TRACE(": loading from resource\n");
257                 /* create stream and associate it with given resource */                        
258                 result = DMUSIC_CreateDirectMusicLoaderResourceStream ((LPVOID*)&pStream);
259                 if (FAILED(result)) {
260                         ERR(": could not create resource stream\n");
261                         return result;
262                 }
263                 result = IDirectMusicLoaderResourceStream_Attach (pStream, pDesc->pbMemData, pDesc->llMemLength, 0, iface);
264                 if (FAILED(result)) {
265                         ERR(": could not attach stream to resource\n");
266                         IStream_Release (pStream);
267                         return result;
268                 }
269         }
270         else if (pDesc->dwValidData & DMUS_OBJ_STREAM) {
271                 /* load object from stream */
272                 TRACE(": loading from stream\n");
273                 /* create universal stream and associate it with given one */                   
274                 result = DMUSIC_CreateDirectMusicLoaderGenericStream ((LPVOID*)&pStream);
275                 if (FAILED(result)) {
276                         ERR(": could not create generic stream\n");
277                         return result;
278                 }
279                 result = IDirectMusicLoaderGenericStream_Attach (pStream, pDesc->pStream, iface);
280                 if (FAILED(result)) {
281                         ERR(": failed to attach stream\n");
282                         IStream_Release (pStream);
283                         return result;
284                 }
285         } else {
286                 /* nowhere to load from */
287                 FIXME(": unknown/unsupported way of loading\n");
288                 return DMUS_E_LOADER_NOFILENAME; /* test shows this is returned */
289         }
290
291         /* create object */
292         result = CoCreateInstance (&pDesc->guidClass, NULL, CLSCTX_INPROC_SERVER, &IID_IDirectMusicObject, (LPVOID*)&pObject);
293         if (FAILED(result)) {
294                 ERR(": could not create object\n");
295                 return result;
296         }
297         /* acquire PersistStream interface */
298         result = IDirectMusicObject_QueryInterface (pObject, &IID_IPersistStream, (LPVOID*)&pPersistStream);
299         if (FAILED(result)) {
300                 ERR("failed to Query\n");
301                 return result;
302         }
303         /* load */
304         result = IPersistStream_Load (pPersistStream, pStream);
305         if (result != S_OK) {
306                 WARN(": failed to (completely) load object (%s)\n", debugstr_dmreturn(result));
307                 ret = DMUS_S_PARTIALLOAD /*result*/;
308         }
309         /* get descriptor */
310         DM_STRUCT_INIT(&GotDesc);
311         result = IDirectMusicObject_GetDescriptor (pObject, &GotDesc);
312         /* set filename (if we loaded via filename) */
313         if (pDesc->dwValidData & DMUS_OBJ_FILENAME) {
314                 GotDesc.dwValidData |= (pDesc->dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH));
315                 strcpyW (GotDesc.wszFileName, pDesc->wszFileName);
316         }
317         if (FAILED(result)) {
318                 ERR(": failed to get descriptor\n");
319                 return result;
320         }
321         /* release all loading related stuff */
322         IStream_Release (pStream);
323         IPersistStream_Release (pPersistStream);        
324                 
325         /* add object to cache/overwrite existing info (if cache is enabled) */
326         DMUSIC_GetLoaderSettings (iface, &pDesc->guidClass, NULL, &bCache);
327         if (bCache) {
328                 if (!pObjectEntry) {
329                         pObjectEntry = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof(WINE_LOADER_ENTRY));
330                         DM_STRUCT_INIT(&pObjectEntry->Desc);
331                         DMUSIC_CopyDescriptor (&pObjectEntry->Desc, &GotDesc);
332                         pObjectEntry->pObject = pObject;
333                         pObjectEntry->bInvalidDefaultDLS = FALSE;
334                         list_add_head (This->pObjects, &pObjectEntry->entry);
335                 } else {
336                         DMUSIC_CopyDescriptor (&pObjectEntry->Desc, &GotDesc);
337                         pObjectEntry->pObject = pObject;
338                         pObjectEntry->bInvalidDefaultDLS = FALSE;
339                 }
340                 TRACE(": filled in cache entry\n");
341         } else TRACE(": caching disabled\n");
342
343 #if 0
344         /* for debug purposes (e.g. to check if all files are cached) */
345         TRACE("*** Loader's cache ***\n");
346         int i = 0;
347         LIST_FOR_EACH (pEntry, This->pObjects) {
348                 i++;
349                 pObjectEntry = LIST_ENTRY(pEntry, WINE_LOADER_ENTRY, entry);
350                 TRACE(": entry nr. %i:\n%s\n  - bInvalidDefaultDLS = %i\n  - pObject = %p\n", i, debugstr_DMUS_OBJECTDESC(&pObjectEntry->Desc), pObjectEntry->bInvalidDefaultDLS, pObjectEntry->pObject);
351         }
352 #endif
353         
354         result = IDirectMusicObject_QueryInterface (pObject, riid, ppv);
355         if (!bCache) IDirectMusicObject_Release (pObject); /* since loader's reference is not needed */
356         /* if there was trouble with loading, and if no other error occurred,
357            we should return DMUS_S_PARTIALLOAD; else, error is returned */
358         if (result == S_OK)
359                 return ret;
360         else
361                 return result;
362 }
363
364 static HRESULT WINAPI IDirectMusicLoaderImpl_IDirectMusicLoader_SetObject (LPDIRECTMUSICLOADER8 iface, LPDMUS_OBJECTDESC pDesc) {
365         ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface);
366         LPSTREAM pStream;
367         LPDIRECTMUSICOBJECT pObject;
368         DMUS_OBJECTDESC Desc;
369         struct list *pEntry;
370         LPWINE_LOADER_ENTRY pObjectEntry, pNewEntry;
371         HRESULT hr;
372
373         TRACE("(%p, %p): pDesc:\n%s\n", This, pDesc, debugstr_DMUS_OBJECTDESC(pDesc));
374
375         /* create stream and load additional info from it */
376         if (pDesc->dwValidData & DMUS_OBJ_FILENAME) {
377                 /* generate filename; if it's full path, don't add search 
378                    directory path, otherwise do */
379                 WCHAR wszFileName[MAX_PATH];
380
381                 if (pDesc->dwValidData & DMUS_OBJ_FULLPATH) {
382                         lstrcpyW(wszFileName, pDesc->wszFileName);
383                 } else {
384                         WCHAR *p;
385                         WCHAR wszSearchPath[MAX_PATH];
386                         DMUSIC_GetLoaderSettings (iface, &pDesc->guidClass, wszSearchPath, NULL);
387                         lstrcpyW(wszFileName, wszSearchPath);
388                         p = wszFileName + lstrlenW(wszFileName);
389                         if (p > wszFileName && p[-1] != '\\') *p++ = '\\';
390                         strcpyW(p, pDesc->wszFileName);
391                 }
392                 /* create stream */
393                 hr = DMUSIC_CreateDirectMusicLoaderFileStream ((LPVOID*)&pStream);
394                 if (FAILED(hr)) {
395                         ERR(": could not create file stream\n");
396                         return DMUS_E_LOADER_FAILEDOPEN;
397                 }
398                 /* attach stream */
399                 hr = IDirectMusicLoaderFileStream_Attach (pStream, wszFileName, iface);
400                 if (FAILED(hr)) {
401                         ERR(": could not attach stream to file\n");
402                         IStream_Release (pStream);
403                         return DMUS_E_LOADER_FAILEDOPEN;
404                 }
405         }
406         else if (pDesc->dwValidData & DMUS_OBJ_STREAM) {        
407                 /* create stream */
408                 hr = DMUSIC_CreateDirectMusicLoaderGenericStream ((LPVOID*)&pStream);
409                 if (FAILED(hr)) {
410                         ERR(": could not create generic stream\n");
411                         return DMUS_E_LOADER_FAILEDOPEN;
412                 }
413                 /* attach stream */
414                 hr = IDirectMusicLoaderGenericStream_Attach (pStream, pDesc->pStream, iface);
415                 if (FAILED(hr)) {
416                         ERR(": could not attach stream\n");
417                         IStream_Release (pStream);
418                         return DMUS_E_LOADER_FAILEDOPEN;
419                 }
420         }
421         else if (pDesc->dwValidData & DMUS_OBJ_MEMORY) {
422                 /* create stream */
423                 hr = DMUSIC_CreateDirectMusicLoaderResourceStream ((LPVOID*)&pStream);
424                 if (FAILED(hr)) {
425                         ERR(": could not create resource stream\n");
426                         return DMUS_E_LOADER_FAILEDOPEN;
427                 }
428                 /* attach stream */
429                 hr = IDirectMusicLoaderResourceStream_Attach (pStream, pDesc->pbMemData, pDesc->llMemLength, 0, iface);
430                 if (FAILED(hr)) {
431                         ERR(": could not attach stream to resource\n");
432                         IStream_Release (pStream);
433                         return DMUS_E_LOADER_FAILEDOPEN;
434                 }
435         }
436         else {
437                 ERR(": no way to get additional info\n");
438                 return DMUS_E_LOADER_FAILEDOPEN;
439         }
440
441         /* create object */
442         CoCreateInstance (&pDesc->guidClass, NULL, CLSCTX_INPROC_SERVER, &IID_IDirectMusicObject, (LPVOID*)&pObject);
443
444         /* *sigh*... some ms objects have lousy implementation of ParseDescriptor that clears input descriptor :( */
445 #ifdef NOW_WE_ARE_FREE
446         /* parse descriptor: we actually use input descriptor; fields that aren't available from stream remain,
447            otherwise real info is set */
448          IDirectMusicObject_ParseDescriptor (pObject, pStream, pDesc);
449 #endif
450         /* hmph... due to some trouble I had with certain tests, we store current position and then set it back */
451         DM_STRUCT_INIT(&Desc);
452         if (FAILED(IDirectMusicObject_ParseDescriptor (pObject, pStream, &Desc))) {
453                 ERR(": couldn't parse descriptor\n");
454                 return DMUS_E_LOADER_FORMATNOTSUPPORTED;
455         }
456
457         /* copy elements from parsed descriptor into input descriptor; this sets new info, overwriting if necessary,
458            but leaves info that's provided by input and not available from stream */    
459         DMUSIC_CopyDescriptor (pDesc, &Desc);
460         
461         /* release everything */
462         IDirectMusicObject_Release (pObject);
463         IStream_Release (pStream);      
464         
465         /* sometimes it happens that twisted programs call SetObject for same object twice...
466            in such cases, native loader returns S_OK and does nothing... a sound plan */
467         LIST_FOR_EACH (pEntry, This->pObjects) {
468                 pObjectEntry = LIST_ENTRY (pEntry, WINE_LOADER_ENTRY, entry);
469                 if (!memcmp (&pObjectEntry->Desc, pDesc, sizeof(DMUS_OBJECTDESC))) {
470                         TRACE(": exactly same entry already exists\n");
471                         return S_OK;
472                 }
473         }               
474         
475         /* add new entry */
476         TRACE(": adding alias entry with following info:\n%s\n", debugstr_DMUS_OBJECTDESC(pDesc));
477         pNewEntry = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof(WINE_LOADER_ENTRY));
478         /* use this function instead of pure memcpy due to streams (memcpy just copies pointer), 
479            which is basically used further by app that called SetDescriptor... better safety than exception */
480         DMUSIC_CopyDescriptor (&pNewEntry->Desc, pDesc);
481         list_add_head (This->pObjects, &pNewEntry->entry);
482
483         return S_OK;
484 }
485
486 static HRESULT WINAPI IDirectMusicLoaderImpl_IDirectMusicLoader_SetSearchDirectory (LPDIRECTMUSICLOADER8 iface, REFGUID rguidClass, WCHAR* pwzPath, BOOL fClear) {
487         WCHAR wszCurrentPath[MAX_PATH];
488         ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface);
489         TRACE("(%p, %s, %s, %d)\n", This, debugstr_dmguid(rguidClass), debugstr_w(pwzPath), fClear);
490         FIXME(": fClear ignored\n");
491         DMUSIC_GetLoaderSettings (iface, rguidClass, wszCurrentPath, NULL);
492         if (!strncmpW(wszCurrentPath, pwzPath, MAX_PATH)) {
493           return S_FALSE;
494         }
495         /* FIXME: check if path is valid; else return DMUS_E_LOADER_BADPATH */
496         return DMUSIC_SetLoaderSettings (iface, rguidClass, pwzPath, NULL);
497 }
498
499 static HRESULT WINAPI IDirectMusicLoaderImpl_IDirectMusicLoader_ScanDirectory (LPDIRECTMUSICLOADER8 iface, REFGUID rguidClass, WCHAR* pwzFileExtension, WCHAR* pwzScanFileName) {
500         static const WCHAR wszAny[] = {'*',0};
501         WIN32_FIND_DATAW FileData;
502         HANDLE hSearch;
503         WCHAR wszSearchString[MAX_PATH];
504         WCHAR *p;
505         HRESULT result;
506         ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface);
507         TRACE("(%p, %s, %p, %p)\n", This, debugstr_dmguid(rguidClass), pwzFileExtension, pwzScanFileName);
508         if (IsEqualGUID (rguidClass, &GUID_DirectMusicAllTypes) || !DMUSIC_IsValidLoadableClass(rguidClass)) {
509                 ERR(": rguidClass invalid CLSID\n");
510                 return REGDB_E_CLASSNOTREG;
511         }
512         
513         /* get search path for given class */
514         DMUSIC_GetLoaderSettings (iface, rguidClass, wszSearchString, NULL);
515         
516         p = wszSearchString + lstrlenW(wszSearchString);
517         if (p > wszSearchString && p[-1] != '\\') *p++ = '\\';
518         *p++ = '*'; /* any file */
519         if (strcmpW (pwzFileExtension, wszAny)) *p++ = '.'; /* if we have actual extension, put a dot */
520         strcpyW (p, pwzFileExtension);
521         
522         TRACE(": search string: %s\n", debugstr_w(wszSearchString));
523         
524         hSearch = FindFirstFileW (wszSearchString, &FileData);
525         if (hSearch == INVALID_HANDLE_VALUE) {
526                 TRACE(": no files found\n");
527                 return S_FALSE;
528         }
529         
530         do {
531                 DMUS_OBJECTDESC Desc;
532                 DM_STRUCT_INIT(&Desc);
533                 Desc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH | DMUS_OBJ_DATE;
534                 Desc.guidClass = *rguidClass;
535                 strcpyW (Desc.wszFileName, FileData.cFileName);
536                 FileTimeToLocalFileTime (&FileData.ftCreationTime, &Desc.ftDate);
537                 IDirectMusicLoader8_SetObject (iface, &Desc);
538                 
539                 if (!FindNextFileW (hSearch, &FileData)) {
540                         if (GetLastError () == ERROR_NO_MORE_FILES) {
541                                 TRACE(": search completed\n");
542                                 result = S_OK;
543                         } else {
544                                 ERR(": could not get next file\n");
545                                 result = E_FAIL;
546                         }
547                         FindClose (hSearch);
548                         return result;
549                 }
550         } while (1);
551 }
552
553 static HRESULT WINAPI IDirectMusicLoaderImpl_IDirectMusicLoader_CacheObject (LPDIRECTMUSICLOADER8 iface, IDirectMusicObject* pObject) {
554         DMUS_OBJECTDESC Desc;
555         HRESULT result = DMUS_E_LOADER_OBJECTNOTFOUND;
556         struct list *pEntry;
557         LPWINE_LOADER_ENTRY  pObjectEntry = NULL;
558
559         ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface);
560         TRACE("(%p, %p)\n", This, pObject);
561         
562         /* get descriptor */
563         DM_STRUCT_INIT(&Desc);
564         IDirectMusicObject_GetDescriptor (pObject, &Desc);
565         
566         /* now iterate thru list and check if we have alias (without object), corresponding
567            to descriptor of input object */
568         LIST_FOR_EACH(pEntry, This->pObjects) {
569                 pObjectEntry = LIST_ENTRY(pEntry, WINE_LOADER_ENTRY, entry);
570                 if ((Desc.dwValidData & DMUS_OBJ_OBJECT) &&
571                         (pObjectEntry->Desc.dwValidData & DMUS_OBJ_OBJECT) &&
572                         IsEqualGUID (&Desc.guidObject, &pObjectEntry->Desc.guidObject)) {
573                         TRACE(": found it by object GUID\n");
574                         if ((pObjectEntry->Desc.dwValidData & DMUS_OBJ_LOADED) && pObjectEntry->pObject)
575                                 result = S_FALSE;
576                         else
577                                 result = S_OK;
578                         break;
579                 }
580                 else if ((Desc.dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH)) &&
581                                 (pObjectEntry->Desc.dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH)) &&
582                                 !strncmpW (Desc.wszFileName, pObjectEntry->Desc.wszFileName, DMUS_MAX_FILENAME)) {
583                         TRACE(": found it by fullpath filename\n");
584                         if ((pObjectEntry->Desc.dwValidData & DMUS_OBJ_LOADED) && pObjectEntry->pObject)
585                                 result = S_FALSE;
586                         else
587                                 result = S_OK;
588                         break;
589                 }
590                 else if ((Desc.dwValidData & (DMUS_OBJ_NAME | DMUS_OBJ_CATEGORY)) &&
591                                 (pObjectEntry->Desc.dwValidData & (DMUS_OBJ_NAME | DMUS_OBJ_CATEGORY)) &&
592                                 !strncmpW (Desc.wszName, pObjectEntry->Desc.wszName, DMUS_MAX_NAME) &&
593                                 !strncmpW (Desc.wszCategory, pObjectEntry->Desc.wszCategory, DMUS_MAX_CATEGORY)) {
594                         TRACE(": found it by name and category\n");
595                         if ((pObjectEntry->Desc.dwValidData & DMUS_OBJ_LOADED) && pObjectEntry->pObject)
596                                 result = S_FALSE;
597                         else
598                                 result = S_OK;
599                         break;
600                 }
601                 else if ((Desc.dwValidData & DMUS_OBJ_NAME) &&
602                                 (pObjectEntry->Desc.dwValidData & DMUS_OBJ_NAME) &&
603                                 !strncmpW (Desc.wszName, pObjectEntry->Desc.wszName, DMUS_MAX_NAME)) {
604                         TRACE(": found it by name\n");
605                         if ((pObjectEntry->Desc.dwValidData & DMUS_OBJ_LOADED) && pObjectEntry->pObject)
606                                 result = S_FALSE;
607                         else
608                                 result = S_OK;
609                         break;
610                 }
611                 else if ((Desc.dwValidData & DMUS_OBJ_FILENAME) &&
612                                 (pObjectEntry->Desc.dwValidData & DMUS_OBJ_FILENAME) &&
613                                 !strncmpW (Desc.wszFileName, pObjectEntry->Desc.wszFileName, DMUS_MAX_FILENAME)) {
614                         TRACE(": found it by filename\n");                              
615                         if ((pObjectEntry->Desc.dwValidData & DMUS_OBJ_LOADED) && pObjectEntry->pObject)
616                                 result = S_FALSE;
617                         else
618                                 result = S_OK;
619                         break;
620                 }
621         }
622         
623         /* if we found such alias, then set everything */
624         if (result == S_OK) {
625                 pObjectEntry->Desc.dwValidData &= DMUS_OBJ_LOADED;
626                 pObjectEntry->pObject = pObject;
627                 IDirectMusicObject_AddRef (pObjectEntry->pObject);
628         }
629         
630         return result;
631 }
632
633 static HRESULT WINAPI IDirectMusicLoaderImpl_IDirectMusicLoader_ReleaseObject (LPDIRECTMUSICLOADER8 iface, IDirectMusicObject* pObject) {
634         DMUS_OBJECTDESC Desc;
635         struct list *pEntry;
636         LPWINE_LOADER_ENTRY pObjectEntry = NULL;
637         HRESULT result = S_FALSE;
638
639         ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface);
640         TRACE("(%p, %p)\n", This, pObject);
641         
642         if(!pObject) return E_POINTER;
643
644         /* get descriptor */
645         DM_STRUCT_INIT(&Desc);
646         IDirectMusicObject_GetDescriptor (pObject, &Desc);
647         
648         /* iterate thru the list of objects we know about; check only those with DMUS_OBJ_LOADED */
649         TRACE(": looking for the object in cache\n");
650         LIST_FOR_EACH(pEntry, This->pObjects) {
651                 pObjectEntry = LIST_ENTRY(pEntry, WINE_LOADER_ENTRY, entry);
652                 if ((Desc.dwValidData & DMUS_OBJ_OBJECT) &&
653                         (pObjectEntry->Desc.dwValidData & (DMUS_OBJ_OBJECT | DMUS_OBJ_LOADED)) &&
654                         IsEqualGUID (&Desc.guidObject, &pObjectEntry->Desc.guidObject)) {
655                         TRACE(": found it by object GUID\n%s\n", debugstr_DMUS_OBJECTDESC(&pObjectEntry->Desc));
656                         result = S_OK;
657                         break;
658                 }
659                 else if ((Desc.dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH)) &&
660                                 (pObjectEntry->Desc.dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH | DMUS_OBJ_LOADED)) &&
661                                 !strncmpW (Desc.wszFileName, pObjectEntry->Desc.wszFileName, DMUS_MAX_FILENAME)) {
662                         TRACE(": found it by fullpath filename\n");
663                         result = S_OK;                  
664                         break;
665                 }
666                 else if ((Desc.dwValidData & (DMUS_OBJ_NAME | DMUS_OBJ_CATEGORY)) &&
667                                 (pObjectEntry->Desc.dwValidData & (DMUS_OBJ_NAME | DMUS_OBJ_CATEGORY | DMUS_OBJ_LOADED)) &&
668                                 !strncmpW (Desc.wszName, pObjectEntry->Desc.wszName, DMUS_MAX_NAME) &&
669                                 !strncmpW (Desc.wszCategory, pObjectEntry->Desc.wszCategory, DMUS_MAX_CATEGORY)) {
670                         TRACE(": found it by name and category\n");
671                         result = S_OK;                  
672                         break;
673                 }
674                 else if ((Desc.dwValidData & DMUS_OBJ_NAME) &&
675                                 (pObjectEntry->Desc.dwValidData & (DMUS_OBJ_NAME | DMUS_OBJ_LOADED)) &&
676                                 !strncmpW (Desc.wszName, pObjectEntry->Desc.wszName, DMUS_MAX_NAME)) {
677                         TRACE(": found it by name\n");
678                         result = S_OK;
679                         break;
680                 }
681                 else if ((Desc.dwValidData & DMUS_OBJ_FILENAME) &&
682                                 (pObjectEntry->Desc.dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_LOADED)) &&
683                                 !strncmpW (Desc.wszFileName, pObjectEntry->Desc.wszFileName, DMUS_MAX_FILENAME)) {
684                         TRACE(": found it by filename\n");
685                         result = S_OK;                  
686                         break;
687                 }
688         }
689         if (result == S_OK) {
690                 /*TRACE(": releasing:\n%s  - bInvalidDefaultDLS = %i\n  - pObject = %p\n", debugstr_DMUS_OBJECTDESC(&pObjectEntry->Desc), pObjectEntry->bInvalidDefaultDLS, pObjectEntry->pObject); */
691                 IDirectMusicObject_Release (pObjectEntry->pObject);
692                 pObjectEntry->pObject = NULL;
693                 pObjectEntry->Desc.dwValidData &= ~DMUS_OBJ_LOADED;
694         } 
695         return result;
696 }
697
698 static HRESULT WINAPI IDirectMusicLoaderImpl_IDirectMusicLoader_ClearCache (LPDIRECTMUSICLOADER8 iface, REFGUID rguidClass) {
699         struct list *pEntry;
700         LPWINE_LOADER_ENTRY pObjectEntry;
701         ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface);
702         TRACE("(%p, %s)\n", This, debugstr_dmguid(rguidClass));
703         
704         LIST_FOR_EACH (pEntry, This->pObjects) {
705                 pObjectEntry = LIST_ENTRY (pEntry, WINE_LOADER_ENTRY, entry);
706                 
707                 if ((IsEqualGUID (rguidClass, &GUID_DirectMusicAllTypes) || IsEqualGUID (rguidClass, &pObjectEntry->Desc.guidClass)) &&
708                         (pObjectEntry->Desc.dwValidData & DMUS_OBJ_LOADED)) {
709                         /* basically, wrap to ReleaseObject for each object found */
710                         IDirectMusicLoader8_ReleaseObject (iface, pObjectEntry->pObject);
711                 }
712         }
713         
714         return S_OK;
715 }
716
717 static HRESULT WINAPI IDirectMusicLoaderImpl_IDirectMusicLoader_EnableCache (LPDIRECTMUSICLOADER8 iface, REFGUID rguidClass, BOOL fEnable) {
718         ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface);
719         BOOL bCurrent;
720         TRACE("(%p, %s, %d)\n", This, debugstr_dmguid(rguidClass), fEnable);
721         DMUSIC_GetLoaderSettings (iface, rguidClass, NULL, &bCurrent);
722         if (bCurrent == fEnable)
723                 return S_FALSE;
724         else
725                 return DMUSIC_SetLoaderSettings (iface, rguidClass, NULL, &fEnable);
726 }
727
728 static HRESULT WINAPI IDirectMusicLoaderImpl_IDirectMusicLoader_EnumObject (LPDIRECTMUSICLOADER8 iface, REFGUID rguidClass, DWORD dwIndex, LPDMUS_OBJECTDESC pDesc) {
729         DWORD dwCount = 0;
730         struct list *pEntry;
731         LPWINE_LOADER_ENTRY pObjectEntry;
732         ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface);
733         TRACE("(%p, %s, %d, %p)\n", This, debugstr_dmguid(rguidClass), dwIndex, pDesc);
734
735         DM_STRUCT_INIT(pDesc);
736
737         LIST_FOR_EACH (pEntry, This->pObjects) {
738                 pObjectEntry = LIST_ENTRY (pEntry, WINE_LOADER_ENTRY, entry);
739
740                 if (IsEqualGUID (rguidClass, &GUID_DirectMusicAllTypes) || IsEqualGUID (rguidClass, &pObjectEntry->Desc.guidClass)) {
741                         if (dwCount == dwIndex) {
742                                 *pDesc = pObjectEntry->Desc;
743                                 /* we aren't supposed to reveal this info */
744                                 pDesc->dwValidData &= ~(DMUS_OBJ_MEMORY | DMUS_OBJ_STREAM);
745                                 pDesc->pbMemData = NULL;
746                                 pDesc->llMemLength = 0;
747                                 pDesc->pStream = NULL;
748                                 return S_OK;
749                         }
750                         dwCount++;
751                 }
752         }
753         
754         TRACE(": not found\n"); 
755         return S_FALSE;
756 }
757
758 static void WINAPI IDirectMusicLoaderImpl_IDirectMusicLoader_CollectGarbage (LPDIRECTMUSICLOADER8 iface) {
759         ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface);
760         FIXME("(%p): stub\n", This);
761 }
762
763 static HRESULT WINAPI IDirectMusicLoaderImpl_IDirectMusicLoader_ReleaseObjectByUnknown (LPDIRECTMUSICLOADER8 iface, IUnknown* pObject) {
764         ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface);
765         HRESULT result;
766         LPDIRECTMUSICOBJECT pObjectInterface;
767         
768         TRACE("(%p, %p)\n", This, pObject);
769         
770         if (IsBadReadPtr (pObject, sizeof(LPUNKNOWN))) {
771                 ERR(": pObject bad write pointer\n");
772                 return E_POINTER;
773         }
774         /* we simply get IDirectMusicObject interface */
775         result = IUnknown_QueryInterface (pObject, &IID_IDirectMusicObject, (LPVOID*)&pObjectInterface);
776         if (FAILED(result)) return result;
777         /* and release it in old-fashioned way */
778         result = IDirectMusicLoader8_ReleaseObject (iface, pObjectInterface);
779         IDirectMusicObject_Release (pObjectInterface);
780         
781         return result;
782 }
783
784 static HRESULT WINAPI IDirectMusicLoaderImpl_IDirectMusicLoader_LoadObjectFromFile (LPDIRECTMUSICLOADER8 iface, REFGUID rguidClassID, REFIID iidInterfaceID, WCHAR* pwzFilePath, void** ppObject) {
785         ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface);
786         DMUS_OBJECTDESC ObjDesc;
787         WCHAR wszLoaderSearchPath[MAX_PATH];
788
789         TRACE("(%p, %s, %s, %s, %p): wrapping to IDirectMusicLoaderImpl_GetObject\n", This, debugstr_dmguid(rguidClassID), debugstr_dmguid(iidInterfaceID), debugstr_w(pwzFilePath), ppObject);
790
791         DM_STRUCT_INIT(&ObjDesc);       
792         ObjDesc.dwValidData = DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH | DMUS_OBJ_CLASS; /* I believe I've read somewhere in MSDN that this function requires either full path or relative path */
793         ObjDesc.guidClass = *rguidClassID;
794         /* OK, MSDN says that search order is the following:
795             - current directory (DONE)
796             - windows search path (FIXME: how do I get that?)
797             - loader's search path (DONE)
798         */
799         DMUSIC_GetLoaderSettings (iface, rguidClassID, wszLoaderSearchPath, NULL);
800     /* search in current directory */
801         if (!SearchPathW (NULL, pwzFilePath, NULL, sizeof(ObjDesc.wszFileName)/sizeof(WCHAR), ObjDesc.wszFileName, NULL) &&
802         /* search in loader's search path */
803                 !SearchPathW (wszLoaderSearchPath, pwzFilePath, NULL, sizeof(ObjDesc.wszFileName)/sizeof(WCHAR), ObjDesc.wszFileName, NULL)) {
804                 /* cannot find file */
805                 TRACE(": cannot find file\n");
806                 return DMUS_E_LOADER_FAILEDOPEN;
807         }
808         
809         TRACE(": full file path = %s\n", debugstr_w (ObjDesc.wszFileName));
810         
811         return IDirectMusicLoaderImpl_IDirectMusicLoader_GetObject (iface, &ObjDesc, iidInterfaceID, ppObject);
812 }
813
814 static const IDirectMusicLoader8Vtbl DirectMusicLoader_Loader_Vtbl = {
815         IDirectMusicLoaderImpl_IDirectMusicLoader_QueryInterface,
816         IDirectMusicLoaderImpl_IDirectMusicLoader_AddRef,
817         IDirectMusicLoaderImpl_IDirectMusicLoader_Release,
818         IDirectMusicLoaderImpl_IDirectMusicLoader_GetObject,
819         IDirectMusicLoaderImpl_IDirectMusicLoader_SetObject,
820         IDirectMusicLoaderImpl_IDirectMusicLoader_SetSearchDirectory,
821         IDirectMusicLoaderImpl_IDirectMusicLoader_ScanDirectory,
822         IDirectMusicLoaderImpl_IDirectMusicLoader_CacheObject,
823         IDirectMusicLoaderImpl_IDirectMusicLoader_ReleaseObject,
824         IDirectMusicLoaderImpl_IDirectMusicLoader_ClearCache,
825         IDirectMusicLoaderImpl_IDirectMusicLoader_EnableCache,
826         IDirectMusicLoaderImpl_IDirectMusicLoader_EnumObject,
827         IDirectMusicLoaderImpl_IDirectMusicLoader_CollectGarbage,
828         IDirectMusicLoaderImpl_IDirectMusicLoader_ReleaseObjectByUnknown,
829         IDirectMusicLoaderImpl_IDirectMusicLoader_LoadObjectFromFile
830 };
831
832 /* help function for DMUSIC_SetDefaultDLS */
833 static HRESULT DMUSIC_GetDefaultGMPath (WCHAR wszPath[MAX_PATH]) {
834         HKEY hkDM;
835         DWORD returnType, sizeOfReturnBuffer = MAX_PATH;
836         char szPath[MAX_PATH];
837
838         if ((RegOpenKeyExA (HKEY_LOCAL_MACHINE, "Software\\Microsoft\\DirectMusic" , 0, KEY_READ, &hkDM) != ERROR_SUCCESS) ||
839             (RegQueryValueExA (hkDM, "GMFilePath", NULL, &returnType, (LPBYTE) szPath, &sizeOfReturnBuffer) != ERROR_SUCCESS)) {
840                 WARN(": registry entry missing\n" );
841                 return E_FAIL;
842         }
843         /* FIXME: Check return types to ensure we're interpreting data right */
844         MultiByteToWideChar (CP_ACP, 0, szPath, -1, wszPath, MAX_PATH);
845
846         return S_OK;
847 }
848
849 /* for ClassFactory */
850 HRESULT WINAPI DMUSIC_CreateDirectMusicLoaderImpl (LPCGUID lpcGUID, LPVOID *ppobj, LPUNKNOWN pUnkOuter) {
851         IDirectMusicLoaderImpl *obj;
852         DMUS_OBJECTDESC Desc;
853         LPWINE_LOADER_ENTRY pDefaultDLSEntry;
854         struct list *pEntry;
855
856         TRACE("(%s, %p, %p)\n", debugstr_dmguid(lpcGUID), ppobj, pUnkOuter);
857         obj = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IDirectMusicLoaderImpl));
858         if (NULL == obj) {
859                 *ppobj = NULL;
860                 return E_OUTOFMEMORY;
861         }
862         obj->LoaderVtbl = &DirectMusicLoader_Loader_Vtbl;
863         obj->dwRef = 0; /* will be inited with QueryInterface */
864         /* init cache/alias list */
865         obj->pObjects = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof(struct list));
866         list_init (obj->pObjects);
867         /* init settings */
868         obj->pClassSettings = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof(struct list));
869         list_init (obj->pClassSettings);
870         DMUSIC_InitLoaderSettings ((LPDIRECTMUSICLOADER8)obj);
871
872         /* set default DLS collection (via SetObject... so that loading via DMUS_OBJ_OBJECT is possible) */
873         DM_STRUCT_INIT(&Desc);
874         Desc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH | DMUS_OBJ_OBJECT;
875         Desc.guidClass = CLSID_DirectMusicCollection;
876         Desc.guidObject = GUID_DefaultGMCollection;
877         DMUSIC_GetDefaultGMPath (Desc.wszFileName);
878         IDirectMusicLoader_SetObject ((LPDIRECTMUSICLOADER8)obj, &Desc);
879         /* and now the workaroundTM for "invalid" default DLS; basically, 
880            my tests showed that if GUID chunk is present in default DLS 
881            collection, loader treats it as "invalid" and returns 
882            DMUS_E_LOADER_NOFILENAME for all requests for it; basically, we check 
883            if out input guidObject was overwritten */
884         pEntry = list_head (obj->pObjects);
885         pDefaultDLSEntry = LIST_ENTRY (pEntry, WINE_LOADER_ENTRY, entry);
886         if (!IsEqualGUID(&Desc.guidObject, &GUID_DefaultGMCollection)) {
887                 pDefaultDLSEntry->bInvalidDefaultDLS = TRUE;
888         }
889
890         lock_module();
891
892         return IDirectMusicLoaderImpl_IDirectMusicLoader_QueryInterface ((LPDIRECTMUSICLOADER8)obj, lpcGUID, ppobj);
893 }
894
895 /* help function for retrieval of search path and caching option for certain class */
896 static HRESULT DMUSIC_GetLoaderSettings (LPDIRECTMUSICLOADER8 iface, REFGUID pClassID, WCHAR* wszSearchPath, LPBOOL pbCache) {
897         ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface);
898         struct list *pEntry;
899         TRACE(": (%p, %s, %p, %p)\n", This, debugstr_dmguid(pClassID), wszSearchPath, pbCache);
900         
901         LIST_FOR_EACH(pEntry, This->pClassSettings) {
902                 LPWINE_LOADER_OPTION pOptionEntry = LIST_ENTRY(pEntry, WINE_LOADER_OPTION, entry);
903                 if (IsEqualCLSID (pClassID, &pOptionEntry->guidClass)) {
904                         if (wszSearchPath)
905                                 strcpyW(wszSearchPath, pOptionEntry->wszSearchPath);
906                         if (pbCache)
907                                 *pbCache = pOptionEntry->bCache;
908                         return S_OK;
909                 }
910         }
911         return S_FALSE;
912 }
913
914 /* help function for setting search path and caching option for certain class */
915 static HRESULT DMUSIC_SetLoaderSettings (LPDIRECTMUSICLOADER8 iface, REFGUID pClassID, WCHAR* wszSearchPath, LPBOOL pbCache) {
916         ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface);
917         struct list *pEntry;
918         HRESULT result = S_FALSE; /* in case pClassID != GUID_DirectMusicAllTypes and not a valid CLSID */
919         TRACE(": (%p, %s, %p, %p)\n", This, debugstr_dmguid(pClassID), wszSearchPath, pbCache);
920         
921         LIST_FOR_EACH(pEntry, This->pClassSettings) {
922                 LPWINE_LOADER_OPTION pOptionEntry = LIST_ENTRY(pEntry, WINE_LOADER_OPTION, entry);
923                 /* well, either we have GUID_DirectMusicAllTypes and need to set it to all,
924                    or specific CLSID is given and we set it only to it */
925                 if (IsEqualGUID (pClassID, &GUID_DirectMusicAllTypes) ||
926                         IsEqualCLSID (pClassID, &pOptionEntry->guidClass)) {
927                         if (wszSearchPath)
928                                 strcpyW(pOptionEntry->wszSearchPath, wszSearchPath);
929                         if (pbCache)
930                                 pOptionEntry->bCache = *pbCache;
931                         result = S_OK;
932                 }
933         }
934
935         return result;
936 }
937
938 static HRESULT DMUSIC_InitLoaderSettings (LPDIRECTMUSICLOADER8 iface) {
939         ICOM_THIS_MULTI(IDirectMusicLoaderImpl, LoaderVtbl, iface);
940         
941         /* hard-coded list of classes */
942         static REFCLSID classes[] = {
943                 &CLSID_DirectMusicAudioPathConfig,
944                 &CLSID_DirectMusicBand,
945                 &CLSID_DirectMusicContainer,
946                 &CLSID_DirectMusicCollection,
947                 &CLSID_DirectMusicChordMap,
948                 &CLSID_DirectMusicSegment,
949                 &CLSID_DirectMusicScript,
950                 &CLSID_DirectMusicSong,
951                 &CLSID_DirectMusicStyle,
952                 &CLSID_DirectMusicGraph,
953                 &CLSID_DirectSoundWave
954         };
955         
956         unsigned int i;
957         WCHAR wszCurrent[MAX_PATH];
958
959         TRACE(": (%p)\n", This);
960         GetCurrentDirectoryW (MAX_PATH, wszCurrent);
961
962         for (i = 0; i < sizeof(classes)/sizeof(REFCLSID); i++) {
963                 LPWINE_LOADER_OPTION pNewSetting = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof(WINE_LOADER_OPTION));
964                 pNewSetting->guidClass = *classes[i];
965                 strcpyW (pNewSetting->wszSearchPath, wszCurrent);
966                 pNewSetting->bCache = TRUE;
967                 list_add_tail (This->pClassSettings, &pNewSetting->entry);
968         }
969
970         return S_OK;
971 }