- define additional shell paths for CSIDL_... constants
[wine] / dlls / dmloader / loader.c
1 /* IDirectMusicLoader8 Implementation
2  *
3  * Copyright (C) 2003-2004 Rok Mandeljc
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (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
13  * GNU Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 #include "dmloader_private.h"
21
22 WINE_DEFAULT_DEBUG_CHANNEL(dmloader);
23
24 HRESULT WINAPI DMUSIC_GetDefaultGMPath (WCHAR wszPath[MAX_PATH]);
25
26 /* IDirectMusicLoader8 IUnknown part: */
27 HRESULT WINAPI IDirectMusicLoader8Impl_QueryInterface (LPDIRECTMUSICLOADER8 iface, REFIID riid, LPVOID *ppobj) {
28         ICOM_THIS(IDirectMusicLoader8Impl,iface);
29
30         if (IsEqualIID (riid, &IID_IUnknown) || 
31             IsEqualIID (riid, &IID_IDirectMusicLoader) ||
32             IsEqualIID (riid, &IID_IDirectMusicLoader8)) {
33                 IDirectMusicLoader8Impl_AddRef(iface);
34                 *ppobj = This;
35                 return S_OK;
36         }
37         
38         WARN("(%p)->(%s,%p),not found\n",This,debugstr_guid(riid),ppobj);
39         return E_NOINTERFACE;
40 }
41
42 ULONG WINAPI IDirectMusicLoader8Impl_AddRef (LPDIRECTMUSICLOADER8 iface) {
43         ICOM_THIS(IDirectMusicLoader8Impl,iface);
44         TRACE("(%p) : AddRef from %ld\n", This, This->ref);
45         return ++(This->ref);
46 }
47
48 ULONG WINAPI IDirectMusicLoader8Impl_Release (LPDIRECTMUSICLOADER8 iface) {
49         ICOM_THIS(IDirectMusicLoader8Impl,iface);
50         ULONG ref = --This->ref;
51         TRACE("(%p) : ReleaseRef to %ld\n", This, This->ref);
52         if (ref == 0) {
53                 HeapFree(GetProcessHeap(), 0, This);
54         }
55         return ref;
56 }
57
58 /* IDirectMusicLoader8 IDirectMusicLoader part: */
59 HRESULT WINAPI IDirectMusicLoader8Impl_GetObject (LPDIRECTMUSICLOADER8 iface, LPDMUS_OBJECTDESC pDesc, REFIID riid, LPVOID* ppv) {
60         ICOM_THIS(IDirectMusicLoader8Impl,iface);
61         HRESULT result = 0;
62         struct list *listEntry;
63         LPDMUS_PRIVATE_ALIAS_ENTRY aliasEntry;
64         DMUS_PRIVATE_CACHE_ENTRY *cacheEntry;
65         DMUS_OBJECTDESC CacheDesc;
66         IDirectMusicObject* pObject;
67         LPDMUS_PRIVATE_CACHE_ENTRY newEntry;
68
69         TRACE("(%p, %p, %s, %p): pDesc:\n", This, pDesc, debugstr_guid(riid), ppv);
70         if (TRACE_ON(dmloader))
71                 DMUSIC_dump_DMUS_OBJECTDESC(pDesc);
72         
73         /* if I understand correctly, SetObject makes sort of aliases for entries in cache;
74                 therefore I created alias list, which is similiar to cache list, and is used as resolver
75                 (it maps let's say GUID to filename) */
76         TRACE(": looking for alias\n");
77         LIST_FOR_EACH (listEntry, &This->AliasList) {
78                 aliasEntry = LIST_ENTRY(listEntry, DMUS_PRIVATE_ALIAS_ENTRY, entry);
79                 /* for the time being, we support only GUID/name mapping */
80                 if ((aliasEntry->pDesc->dwValidData & DMUS_OBJ_OBJECT) && (pDesc->dwValidData & DMUS_OBJ_OBJECT)
81                         && IsEqualGUID (&aliasEntry->pDesc->guidObject, &pDesc->guidObject)) {
82                         TRACE(": found alias by GUID (%s)... mapping:\n", debugstr_guid(&aliasEntry->pDesc->guidObject));
83                         if ((aliasEntry->pDesc->dwValidData & DMUS_OBJ_FILENAME) && !(pDesc->dwValidData & DMUS_OBJ_FILENAME)) {
84                                 TRACE(":     - to filename (%s)\n", debugstr_w(aliasEntry->pDesc->wszFileName));
85                                 pDesc->dwValidData |= DMUS_OBJ_FILENAME;
86                                 pDesc->dwValidData |= (aliasEntry->pDesc->dwValidData & DMUS_OBJ_FULLPATH);
87                                 strncpyW (pDesc->wszFileName, aliasEntry->pDesc->wszFileName, DMUS_MAX_FILENAME);
88                         }
89                         if ((aliasEntry->pDesc->dwValidData & DMUS_OBJ_NAME) && !(pDesc->dwValidData & DMUS_OBJ_NAME)) {
90                                 TRACE(":     - to name (%s)\n", debugstr_w(aliasEntry->pDesc->wszName));
91                                 pDesc->dwValidData |= DMUS_OBJ_NAME;
92                                 strncpyW (pDesc->wszName, aliasEntry->pDesc->wszName, DMUS_MAX_NAME);
93                         }
94                         if ((aliasEntry->pDesc->dwValidData & DMUS_OBJ_MEMORY) && !(pDesc->dwValidData & DMUS_OBJ_MEMORY)) {
95                                 TRACE(":     - to memory location\n");
96                                 pDesc->dwValidData |= DMUS_OBJ_MEMORY;
97                                 /* FIXME: is this correct? */
98                                 pDesc->pbMemData = aliasEntry->pDesc->pbMemData;
99                                 pDesc->llMemLength = aliasEntry->pDesc->llMemLength;
100                         }
101                         if ((aliasEntry->pDesc->dwValidData & DMUS_OBJ_STREAM) && !(pDesc->dwValidData & DMUS_OBJ_STREAM)) {
102                                 TRACE(":     - to stream\n");
103                                 pDesc->dwValidData |= DMUS_OBJ_STREAM;
104                                 IStream_Clone (aliasEntry->pDesc->pStream, &pDesc->pStream);    
105                         }                                       
106                 }
107                 else if ((aliasEntry->pDesc->dwValidData & DMUS_OBJ_NAME) && (pDesc->dwValidData & DMUS_OBJ_NAME)       
108                         && !strncmpW (aliasEntry->pDesc->wszName, pDesc->wszName, DMUS_MAX_NAME)) {
109                         TRACE(": found alias by name (%s)... mapping:\n", debugstr_w(aliasEntry->pDesc->wszName));      
110                         if ((aliasEntry->pDesc->dwValidData & DMUS_OBJ_FILENAME) && !(pDesc->dwValidData & DMUS_OBJ_FILENAME)) {
111                                 TRACE(":     - to filename (%s)\n", debugstr_w(aliasEntry->pDesc->wszFileName));
112                                 pDesc->dwValidData |= DMUS_OBJ_FILENAME;
113                                 pDesc->dwValidData |= (aliasEntry->pDesc->dwValidData & DMUS_OBJ_FULLPATH);
114                                 strncpyW (pDesc->wszFileName, aliasEntry->pDesc->wszFileName, DMUS_MAX_FILENAME);
115                         }
116                         if ((aliasEntry->pDesc->dwValidData & DMUS_OBJ_OBJECT) && !(pDesc->dwValidData & DMUS_OBJ_OBJECT)) {
117                                 TRACE(":     - to object GUID (%s)\n", debugstr_guid(&aliasEntry->pDesc->guidObject));
118                                 pDesc->dwValidData |= DMUS_OBJ_OBJECT;
119                                 memcpy (&pDesc->guidObject, &aliasEntry->pDesc->guidObject, sizeof(GUID));
120                         }
121                         if ((aliasEntry->pDesc->dwValidData & DMUS_OBJ_MEMORY) && !(pDesc->dwValidData & DMUS_OBJ_MEMORY)) {
122                                 TRACE(":     - to memory location\n");
123                                 pDesc->dwValidData |= DMUS_OBJ_MEMORY;
124                                 /* FIXME: is this correct? */
125                                 pDesc->pbMemData = aliasEntry->pDesc->pbMemData;
126                                 pDesc->llMemLength = aliasEntry->pDesc->llMemLength;
127                         }
128                         if ((aliasEntry->pDesc->dwValidData & DMUS_OBJ_STREAM) && !(pDesc->dwValidData & DMUS_OBJ_STREAM)) {
129                                 TRACE(":     - to stream\n");
130                                 pDesc->dwValidData |= DMUS_OBJ_STREAM;
131                                 IStream_Clone (aliasEntry->pDesc->pStream, &pDesc->pStream);    
132                         }                               
133                 }
134                 /*else FIXME(": implement other types of mapping\n"); */
135         }
136         
137         /* iterate through cache and check whether object has already been loaded */
138         TRACE(": looking up cache...\n");
139         DM_STRUCT_INIT(&CacheDesc);
140         LIST_FOR_EACH (listEntry, &This->CacheList) {
141                 cacheEntry = LIST_ENTRY(listEntry, DMUS_PRIVATE_CACHE_ENTRY, entry);
142                 /* first check whether cached object is "faulty" default dls collection;
143                  *  I don't think it's recongised by object descriptor, since it contains no
144                  *  data; it's not very elegant way, but it works :)
145                  */
146                 if (cacheEntry->bIsFaultyDLS == TRUE) {
147                         if ((pDesc->dwValidData & DMUS_OBJ_OBJECT) && IsEqualGUID (&GUID_DefaultGMCollection, &pDesc->guidObject)) {
148                                 TRACE(": found faulty default DLS collection... enabling M$ compliant behaviour\n");
149                                 return DMUS_E_LOADER_NOFILENAME;                        
150                         }
151                 }
152                 /* I think it shouldn't happen that pObject is NULL, but better be safe */
153                 if (cacheEntry->pObject) {
154                         DM_STRUCT_INIT(&CacheDesc); /* prepare desc for reuse */
155                         IDirectMusicObject_GetDescriptor (cacheEntry->pObject, &CacheDesc);
156                         /* according to MSDN, search order is:
157                                    1. DMUS_OBJ_OBJECT
158                                    2. DMUS_OBJ_MEMORY (FIXME)
159                                    3. DMUS_OBJ_FILENAME & DMUS_OBJ_FULLPATH
160                                    4. DMUS_OBJ_NAME & DMUS_OBJ_CATEGORY
161                                    5. DMUS_OBJ_NAME
162                                    6. DMUS_OBJ_FILENAME */
163                         if ((pDesc->dwValidData & DMUS_OBJ_OBJECT) && (CacheDesc.dwValidData & DMUS_OBJ_OBJECT)
164                                 && IsEqualGUID (&pDesc->guidObject, &CacheDesc.guidObject)) {
165                                         TRACE(": found it by object GUID\n");
166                                         return IDirectMusicObject_QueryInterface (cacheEntry->pObject, riid, ppv);
167                         }
168                         if ((pDesc->dwValidData & DMUS_OBJ_MEMORY) && (CacheDesc.dwValidData & DMUS_OBJ_MEMORY)) {
169                                         FIXME(": DMUS_OBJ_MEMORY not supported yet\n");
170                         }
171                         if ((pDesc->dwValidData & DMUS_OBJ_FILENAME) && (pDesc->dwValidData & DMUS_OBJ_FULLPATH)
172                                 && (CacheDesc.dwValidData & DMUS_OBJ_FILENAME) && (CacheDesc.dwValidData & DMUS_OBJ_FULLPATH)
173                                 && !strncmpW (pDesc->wszFileName, CacheDesc.wszFileName, DMUS_MAX_FILENAME)) {
174                                         TRACE(": found it by fullpath filename\n");
175                                         return IDirectMusicObject_QueryInterface (cacheEntry->pObject, riid, ppv);
176                         }
177                         if ((pDesc->dwValidData & DMUS_OBJ_NAME) && (pDesc->dwValidData & DMUS_OBJ_CATEGORY)
178                                 && (CacheDesc.dwValidData & DMUS_OBJ_NAME) && (CacheDesc.dwValidData & DMUS_OBJ_CATEGORY)
179                                 && !strncmpW (pDesc->wszName, CacheDesc.wszName, DMUS_MAX_NAME)
180                                 && !strncmpW (pDesc->wszCategory, CacheDesc.wszCategory, DMUS_MAX_CATEGORY)) {
181                                         TRACE(": found it by name and category\n");
182                                         return IDirectMusicObject_QueryInterface (cacheEntry->pObject, riid, ppv);
183                         }
184                         if ((pDesc->dwValidData & DMUS_OBJ_NAME) && (CacheDesc.dwValidData & DMUS_OBJ_NAME)
185                                 && !strncmpW (pDesc->wszName, CacheDesc.wszName, DMUS_MAX_NAME)) {
186                                         TRACE(": found it by name\n");
187                                         return IDirectMusicObject_QueryInterface (cacheEntry->pObject, riid, ppv);
188                         }
189                         if ((pDesc->dwValidData & DMUS_OBJ_FILENAME) && (CacheDesc.dwValidData & DMUS_OBJ_FILENAME)
190                                 && !strncmpW (pDesc->wszFileName, CacheDesc.wszFileName, DMUS_MAX_FILENAME)) {
191                                         TRACE(": found it by filename\n");
192                                         return IDirectMusicObject_QueryInterface (cacheEntry->pObject, riid, ppv);
193                         }
194                 }
195         }
196         
197         /* object doesn't exist in cache... guess we'll have to load it */
198         TRACE(": object does not exist in cache\n");
199         
200         /* sometimes it happens that guidClass is missing */
201         if (!(pDesc->dwValidData & DMUS_OBJ_CLASS)) {
202           ERR(": guidClass not valid but needed\n");
203           *ppv = NULL;
204           return DMUS_E_LOADER_NOCLASSID;
205         }
206
207         if (pDesc->dwValidData & DMUS_OBJ_FILENAME) {
208                 /* load object from file */
209                 /* generate filename; if it's full path, don't add search 
210                    directory path, otherwise do */
211                 WCHAR wzFileName[MAX_PATH];
212                 DMUS_OBJECTDESC GotDesc;
213                 LPSTREAM pStream;
214                 IPersistStream* pPersistStream = NULL;
215
216                 if (pDesc->dwValidData & DMUS_OBJ_FULLPATH) {
217                         lstrcpyW(wzFileName, pDesc->wszFileName);
218                 } else {
219                         WCHAR *p;
220                         lstrcpyW(wzFileName, This->wzSearchPath);
221                         p = wzFileName + lstrlenW(wzFileName);
222                         if (p > wzFileName && p[-1] != '\\') *p++ = '\\';
223                         strcpyW(p, pDesc->wszFileName);
224                 }
225                 TRACE(": loading from file (%s)\n", debugstr_w(wzFileName));
226                 /* create stream and associate it with dls collection file */                   
227                 result = DMUSIC_CreateLoaderStream ((LPVOID*)&pStream);
228                 if (FAILED(result)) {
229                                 ERR(": could not create loader stream\n");
230                         return result;
231                 }
232                 result = ILoaderStream_Attach (pStream, wzFileName, (LPDIRECTMUSICLOADER)iface);
233                 if (FAILED(result)) {
234                         ERR(": could not attach stream to file\n");                     
235                         return result;
236                 }
237                 /* create object */
238                 result = CoCreateInstance (&pDesc->guidClass, NULL, CLSCTX_INPROC_SERVER, &IID_IDirectMusicObject, (LPVOID*)&pObject);
239                 if (FAILED(result)) {
240                         ERR(": could not create object\n");
241                         return result;
242                 }
243                 /* acquire PersistStream interface */
244                 result = IDirectMusicObject_QueryInterface (pObject, &IID_IPersistStream, (LPVOID*)&pPersistStream);
245                 if (FAILED(result)) {
246                         ERR("failed to Query\n");
247                         return result;
248                 }
249                 /* load */
250                 result = IPersistStream_Load (pPersistStream, pStream);
251                 if (FAILED(result)) {
252                         ERR(": failed to load object\n");
253                         return result;
254                 }
255                 /* get descriptor */
256                 DM_STRUCT_INIT(&GotDesc);
257                 result = IDirectMusicObject_GetDescriptor (pObject, &GotDesc);
258                 if (FAILED(result)) {
259                         ERR(": failed to get descriptor\n");
260                         return result;
261                 }
262                 /* now set the "missing" info (check comment at "Loading default DLS collection") */
263                 GotDesc.dwValidData |= (DMUS_OBJ_FILENAME | DMUS_OBJ_LOADED); /* this time only these are missing */
264                 strncpyW (GotDesc.wszFileName, pDesc->wszFileName, DMUS_MAX_FILENAME); /* set wszFileName, even if futile */
265                 /* set descriptor */                    
266                 IDirectMusicObject_SetDescriptor (pObject, &GotDesc);           
267                 /* release all loading related stuff */
268                 IStream_Release (pStream);
269                 IPersistStream_Release (pPersistStream);
270         }
271         else if (pDesc->dwValidData & DMUS_OBJ_STREAM) {
272                 LPSTREAM pClonedStream = NULL;
273                 IPersistStream* pPersistStream = NULL;
274                 DMUS_OBJECTDESC GotDesc;
275                 /* load object from stream */
276                 TRACE(": loading from stream\n");
277                 /* clone stream, given in pDesc */
278                 result = IStream_Clone (pDesc->pStream, &pClonedStream);
279                 if (FAILED(result)) {
280                         ERR(": failed to clone stream\n");
281                         return result;
282                 }
283                 /* create object */
284                 result = CoCreateInstance (&pDesc->guidClass, NULL, CLSCTX_INPROC_SERVER, &IID_IDirectMusicObject, (LPVOID*)&pObject);
285                 if (FAILED(result)) {
286                         ERR(": could not create object\n");
287                         return result;
288                 }
289                 /* acquire PersistStream interface */
290                 result = IDirectMusicObject_QueryInterface (pObject, &IID_IPersistStream, (LPVOID*)&pPersistStream);
291                 if (FAILED(result)) {
292                         ERR(": could not acquire IPersistStream\n");
293                         return result;
294                 }
295                 /* load */
296                 result = IPersistStream_Load (pPersistStream, pClonedStream);
297                 if (FAILED(result)) {
298                         ERR(": failed to load object\n");
299                         return result;
300                 }
301                 /* get descriptor */
302                 DM_STRUCT_INIT(&GotDesc);
303                 result = IDirectMusicObject_GetDescriptor (pObject, &GotDesc);
304                 if (FAILED(result)) {
305                         ERR(": failed to get descriptor\n");
306                         return result;
307                 }
308                 /* now set the "missing" info */
309                 GotDesc.dwValidData |= DMUS_OBJ_LOADED; /* only missing data with streams */
310                 /* set descriptor */                    
311                 IDirectMusicObject_SetDescriptor (pObject, &GotDesc);           
312                 /* release all loading-related stuff */
313                 IPersistStream_Release (pPersistStream);
314                 IStream_Release (pClonedStream);
315         }
316         else if (pDesc->dwValidData & DMUS_OBJ_OBJECT) {
317                 /* load object by GUID */
318                 TRACE(": loading by GUID (only default DLS supported)\n");
319                 if (IsEqualGUID (&pDesc->guidObject, &GUID_DefaultGMCollection)) {
320                         /* Loading default DLS collection: *dirty* secret (TM)
321             *  By mixing native and builtin loader and collection and 
322                          *   various .dls files, I found out following undocumented 
323                          *   behaviour:
324                          *    - loader creates two instances of collection object
325                          *    - it calls ParseDescriptor on first, then releases it
326                          *    - then it checks returned descriptor; I'm not sure, but 
327                          *       it seems that DMUS_OBJ_OBJECT is not present if DLS
328                          *       collection is indeed *real* one (gm.dls)
329             *    - if above mentioned flag is not set, it creates another 
330                          *      instance and loads it; it also gets descriptor and adds
331                          *      guidObject and wszFileName (even though this one cannot be
332                          *      set on native collection, or so it seems)
333             *    => it seems to be sort of check whether given 'default
334                          *       DLS collection' is really one shipped with DX before
335                          *       actually loading it
336                          * -cheers, Rok
337                          */                     
338                         WCHAR wzFileName[DMUS_MAX_FILENAME];
339                         LPSTREAM pStream;
340                         LPSTREAM pProbeStream;
341                         IDirectMusicObject *pProbeObject;
342                         DMUS_OBJECTDESC ProbeDesc;
343                         DMUS_OBJECTDESC GotDesc;
344                         IPersistStream *pPersistStream = NULL;
345
346                         /* get the path for default collection */
347                         TRACE(": getting default DLS collection path...\n");
348                         if (FAILED(DMUSIC_GetDefaultGMPath (wzFileName))) {
349                                 ERR(": could not get default collection path\n");
350                                 return E_FAIL;
351                         }
352                         /* create stream and associate it with dls collection file */
353                         TRACE(": creating stream...\n");
354                         result = DMUSIC_CreateLoaderStream ((LPVOID*) &pStream);
355                         if (FAILED(result)) {
356                                 ERR(": could not create loader stream\n");
357                                 return result;
358                         }
359                         TRACE(": attaching stream...\n");
360                         result = ILoaderStream_Attach (pStream, wzFileName, (LPDIRECTMUSICLOADER)iface);
361                         if (FAILED(result)) {
362                                 ERR(": could not attach stream to file\n");
363                                 return result;
364                         }
365                         /* now create a clone of stream for "probe" */
366                         TRACE(": cloning stream (for probing)...\n");
367                         result = IStream_Clone (pStream, &pProbeStream);
368                         if (FAILED(result)) {
369                                 ERR(": could not clone stream\n");
370                                 return result;
371                         }
372                         /* create object for "probing" */
373                         TRACE(": creating IDirectMusicObject (for probing)...\n");
374                         result = CoCreateInstance (&pDesc->guidClass, NULL, CLSCTX_INPROC_SERVER, &IID_IDirectMusicObject, (LPVOID*) &pProbeObject);
375                         if (FAILED(result)) {
376                                 ERR(": could not create object (for probing)\n");
377                                 return result;
378                         }
379                         /* get descriptor from stream */
380                         TRACE(": parsing descriptor on probe stream...\n");
381                         DM_STRUCT_INIT(&ProbeDesc);
382                         result = IDirectMusicObject_ParseDescriptor (pProbeObject, pProbeStream, &ProbeDesc);
383                         if (FAILED(result)) {
384                                 ERR(": could not parse descriptor\n");
385                                 return result;
386                         }
387                         /* release all probing-related stuff */
388                         TRACE(": releasing probing-related stuff...\n");
389                         IStream_Release (pProbeStream);
390                         IDirectMusicObject_Release (pProbeObject);
391                         /* now, if it happens by any chance that dls collection isn't *the one* 
392                          *  TODO: - check if the way below is the appropriate one
393                          */
394                         if (ProbeDesc.dwValidData & DMUS_OBJ_OBJECT) {
395                                 LPDMUS_PRIVATE_CACHE_ENTRY newEntry;
396                                 WARN(": the default DLS collection is not the one shipped with DX\n");
397                                 /* my tests show that we return pointer to something or NULL, depending on + how 
398                                  * input object was defined (therefore we probably don't return anything for object)
399                                  * and DMUS_E_LOADER_NOFILENAME as error code
400                                  * (I'd personally rather return DMUS_S_PARTIALLOAD, but I don't set rules)
401                                  */
402                                 newEntry = (LPDMUS_PRIVATE_CACHE_ENTRY) HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof(DMUS_PRIVATE_CACHE_ENTRY));
403                                 newEntry->pObject = NULL;
404                                 newEntry->bIsFaultyDLS = TRUE; /* so that cache won't try to get descriptor */
405                                 list_add_tail (&This->CacheList, &newEntry->entry);
406                                 TRACE(": filled in cache entry\n");
407                                 return DMUS_E_LOADER_NOFILENAME;
408                         }
409                         /* now the real loading... create object */
410                         TRACE(": creating IDirectMusicObject (for loading)\n");
411                         result = CoCreateInstance (&pDesc->guidClass, NULL, CLSCTX_INPROC_SERVER, &IID_IDirectMusicObject, (LPVOID*) &pObject);
412                         if (FAILED(result)) {
413                                 ERR(": could not create object (for loading)\n");
414                                 return result;
415                         }
416                         /* acquire PersistStream interface */
417                         TRACE(": getting IPersistStream on object...\n");
418                         result = IDirectMusicObject_QueryInterface (pObject, &IID_IPersistStream, (LPVOID*) &pPersistStream);
419                         if (FAILED(result)) {
420                                 ERR(": could not acquire IPersistStream\n");
421                                 return result;
422                         }
423                         /* load */
424                         TRACE(": loading object..\n");
425                         result = IPersistStream_Load (pPersistStream, pStream);
426                         if (FAILED(result)) {
427                                 ERR(": failed to load object\n");
428                                 return result;
429                         }
430                         /* get descriptor */
431                         TRACE(": getting descriptor of loaded object...\n");
432                         DM_STRUCT_INIT(&GotDesc);
433                         result = IDirectMusicObject_GetDescriptor (pObject, &GotDesc);
434                         if (FAILED(result)) {
435                                 ERR(": failed to get descriptor\n");
436                                 return result;
437                         }
438                         /* now set the "missing" info */
439                         TRACE(": adding \"missing\" info...\n");
440                         GotDesc.dwValidData |= (DMUS_OBJ_OBJECT | DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH | DMUS_OBJ_LOADED);
441                         memcpy (&GotDesc.guidObject, &pDesc->guidObject, sizeof(GUID)); /* set guidObject */
442                         strncpyW (GotDesc.wszFileName, wzFileName, DMUS_MAX_FILENAME); /* set wszFileName, even if futile */
443                         /* set descriptor */
444                         TRACE(": setting descriptor\n");
445                         IDirectMusicObject_SetDescriptor (pObject, &GotDesc);
446                         /* release all loading related stuff */
447                         TRACE(": releasing all loading-related stuff\n");
448             IStream_Release (pStream);
449                         IPersistStream_Release (pPersistStream);
450                 } else {
451                         return E_FAIL;
452                 }
453         } else {
454                 /* nowhere to load from */
455                 FIXME(": unknown/unsupported way of loading\n");
456                 return E_FAIL;
457         }
458         
459         /* add object to cache */
460         newEntry = (LPDMUS_PRIVATE_CACHE_ENTRY) HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof(DMUS_PRIVATE_CACHE_ENTRY));
461         if (pObject) {
462                 newEntry->pObject = pObject;
463                 newEntry->bIsFaultyDLS = FALSE;
464         }
465         list_add_tail (&This->CacheList, &newEntry->entry);
466         TRACE(": filled in cache entry\n");
467
468 #if 0
469         /* for debug purposes (e.g. to check if all files are cached) */
470         TRACE("*** Loader's cache ***\n");
471         int i = 0;
472         LIST_FOR_EACH (listEntry, &This->CacheList) {
473                 i++;
474                 TRACE("Entry nr. %i:\n", i);
475                 cacheEntry = LIST_ENTRY(listEntry, DMUS_PRIVATE_CACHE_ENTRY, entry);
476                 if (cacheEntry->bIsFaultyDLS == FALSE) {
477                         DM_STRUCT_INIT(&CacheDesc); /* prepare desc for reuse */
478                         IDirectMusicObject_GetDescriptor (cacheEntry->pObject, &CacheDesc);
479                         DMUSIC_dump_DMUS_OBJECTDESC(&CacheDesc);
480                 } else {
481                         DPRINTF("faulty DLS collection\n");
482                 }
483         }
484 #endif
485         
486         return IDirectMusicObject_QueryInterface (pObject, riid, ppv);
487 }
488
489 HRESULT WINAPI IDirectMusicLoader8Impl_SetObject (LPDIRECTMUSICLOADER8 iface, LPDMUS_OBJECTDESC pDesc) {
490         ICOM_THIS(IDirectMusicLoader8Impl,iface);
491         DMUS_PRIVATE_ALIAS_ENTRY *newEntry;
492         DMUS_OBJECTDESC Desc;
493         
494         TRACE("(%p, %p): pDesc:\n", This, pDesc);
495         if (TRACE_ON(dmloader))
496                 DMUSIC_dump_DMUS_OBJECTDESC(pDesc);
497         
498         /* create stream and load additional info from it */
499         if (pDesc->dwValidData & DMUS_OBJ_FILENAME) {
500                 /* generate filename; if it's full path, don't add search 
501                    directory path, otherwise do */
502                 WCHAR wzFileName[MAX_PATH];
503                 LPSTREAM pStream;
504                 IDirectMusicObject* pObject;
505
506                 if (pDesc->dwValidData & DMUS_OBJ_FULLPATH) {
507                         lstrcpyW(wzFileName, pDesc->wszFileName);
508                 } else {
509                         WCHAR *p;
510                         lstrcpyW(wzFileName, This->wzSearchPath);
511                         p = wzFileName + lstrlenW(wzFileName);
512                         if (p > wzFileName && p[-1] != '\\') *p++ = '\\';
513                         strcpyW(p, pDesc->wszFileName);
514                 }
515                 /* create stream */
516                 DMUSIC_CreateLoaderStream ((LPVOID*) &pStream);
517                 /* attach stream */
518                 ILoaderStream_Attach (pStream, wzFileName, (LPDIRECTMUSICLOADER)iface);
519                 /* create object */
520                 CoCreateInstance (&pDesc->guidClass, NULL, CLSCTX_INPROC_SERVER, &IID_IDirectMusicObject, (LPVOID*)&pObject);
521                 /* parse descriptor */
522                 DM_STRUCT_INIT(&Desc);
523                 IDirectMusicObject_ParseDescriptor (pObject, pStream, &Desc);
524                 /* release everything */
525                 IDirectMusicObject_Release (pObject);
526                 IStream_Release (pStream);
527         }
528         else if (pDesc->dwValidData & DMUS_OBJ_STREAM) {
529                 /* clone stream */
530                 LPSTREAM pStream = NULL;
531                 IDirectMusicObject* pObject;
532
533                 IStream_Clone (pDesc->pStream, &pStream);
534                 /* create object */
535                 CoCreateInstance (&pDesc->guidClass, NULL, CLSCTX_INPROC_SERVER, &IID_IDirectMusicObject, (LPVOID*)&pObject);
536                 /* parse descriptor */
537                 DM_STRUCT_INIT(&Desc);
538                 IDirectMusicObject_ParseDescriptor (pObject, pStream, &Desc);
539                 /* release everything */
540                 IDirectMusicObject_Release (pObject);
541                 IStream_Release (pStream);
542         }
543         else {
544                 WARN(": no way to get additional info\n");
545         }
546         
547         /* now set additional info... my tests show that existing fields should be overwritten */
548         if (Desc.dwValidData & DMUS_OBJ_OBJECT)
549                 memcpy (&pDesc->guidObject, &Desc.guidObject, sizeof(Desc.guidObject));
550         if (Desc.dwValidData & DMUS_OBJ_CLASS)
551                 memcpy (&pDesc->guidClass, &Desc.guidClass, sizeof(Desc.guidClass));            
552         if (Desc.dwValidData & DMUS_OBJ_NAME)
553                 strncpyW (pDesc->wszName, Desc.wszName, DMUS_MAX_NAME);
554         if (Desc.dwValidData & DMUS_OBJ_CATEGORY)
555                 strncpyW (pDesc->wszCategory, Desc.wszCategory, DMUS_MAX_CATEGORY);             
556         if (Desc.dwValidData & DMUS_OBJ_FILENAME)
557                 strncpyW (pDesc->wszFileName, Desc.wszFileName, DMUS_MAX_FILENAME);             
558         if (Desc.dwValidData & DMUS_OBJ_VERSION)
559                 memcpy (&pDesc->vVersion, &Desc.vVersion, sizeof(Desc.vVersion));                               
560         if (Desc.dwValidData & DMUS_OBJ_DATE)
561                 memcpy (&pDesc->ftDate, &Desc.ftDate, sizeof(Desc.ftDate));
562         pDesc->dwValidData |= Desc.dwValidData; /* add new flags */
563         
564         /* add new entry */
565         TRACE(": adding alias entry with following info:\n");
566         if (TRACE_ON(dmloader))
567                 DMUSIC_dump_DMUS_OBJECTDESC(pDesc);
568         newEntry = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof(DMUS_PRIVATE_ALIAS_ENTRY));
569         newEntry->pDesc = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof(DMUS_OBJECTDESC));
570         memcpy (newEntry->pDesc, pDesc, sizeof(DMUS_OBJECTDESC));
571         list_add_tail (&This->AliasList, &newEntry->entry);
572         
573         return S_OK;
574 }
575
576 HRESULT WINAPI IDirectMusicLoader8Impl_SetSearchDirectory (LPDIRECTMUSICLOADER8 iface, REFGUID rguidClass, WCHAR* pwzPath, BOOL fClear) {
577         ICOM_THIS(IDirectMusicLoader8Impl,iface);
578         TRACE("(%p, %s, %s, %d)\n", This, debugstr_guid(rguidClass), debugstr_w(pwzPath), fClear);
579         if (0 == strncmpW(This->wzSearchPath, pwzPath, MAX_PATH)) {
580           return S_FALSE;
581         } 
582         strncpyW(This->wzSearchPath, pwzPath, MAX_PATH);
583         return S_OK;
584 }
585
586 HRESULT WINAPI IDirectMusicLoader8Impl_ScanDirectory (LPDIRECTMUSICLOADER8 iface, REFGUID rguidClass, WCHAR* pwzFileExtension, WCHAR* pwzScanFileName) {
587         ICOM_THIS(IDirectMusicLoader8Impl,iface);
588         FIXME("(%p, %s, %p, %p): stub\n", This, debugstr_guid(rguidClass), pwzFileExtension, pwzScanFileName);
589         return S_OK;
590 }
591
592 HRESULT WINAPI IDirectMusicLoader8Impl_CacheObject (LPDIRECTMUSICLOADER8 iface, IDirectMusicObject* pObject) {
593         ICOM_THIS(IDirectMusicLoader8Impl,iface);
594         FIXME("(%p, %p): stub\n", This, pObject);
595         return S_OK;
596 }
597
598 HRESULT WINAPI IDirectMusicLoader8Impl_ReleaseObject (LPDIRECTMUSICLOADER8 iface, IDirectMusicObject* pObject) {
599         ICOM_THIS(IDirectMusicLoader8Impl,iface);
600         FIXME("(%p, %p): stub\n", This, pObject);
601         return S_OK;
602 }
603
604 HRESULT WINAPI IDirectMusicLoader8Impl_ClearCache (LPDIRECTMUSICLOADER8 iface, REFGUID rguidClass) {
605         ICOM_THIS(IDirectMusicLoader8Impl,iface);
606         FIXME("(%p, %s): stub\n", This, debugstr_guid(rguidClass));
607         return S_OK;
608 }
609
610 HRESULT WINAPI IDirectMusicLoader8Impl_EnableCache (LPDIRECTMUSICLOADER8 iface, REFGUID rguidClass, BOOL fEnable) {
611         ICOM_THIS(IDirectMusicLoader8Impl,iface);
612         FIXME("(%p, %s, %d): stub\n", This, debugstr_guid(rguidClass), fEnable);
613         return S_OK;
614 }
615
616 HRESULT WINAPI IDirectMusicLoader8Impl_EnumObject (LPDIRECTMUSICLOADER8 iface, REFGUID rguidClass, DWORD dwIndex, LPDMUS_OBJECTDESC pDesc) {
617         ICOM_THIS(IDirectMusicLoader8Impl,iface);
618         FIXME("(%p, %s, %ld, %p): stub\n", This, debugstr_guid(rguidClass), dwIndex, pDesc);
619         return S_OK;
620 }
621
622 /* IDirectMusicLoader8 Interface part follow: */
623 void WINAPI IDirectMusicLoader8Impl_CollectGarbage (LPDIRECTMUSICLOADER8 iface) {
624         ICOM_THIS(IDirectMusicLoader8Impl,iface);
625         FIXME("(%p): stub\n", This);
626 }
627
628 HRESULT WINAPI IDirectMusicLoader8Impl_ReleaseObjectByUnknown (LPDIRECTMUSICLOADER8 iface, IUnknown* pObject) {
629         ICOM_THIS(IDirectMusicLoader8Impl,iface);
630         FIXME("(%p, %p): stub\n", This, pObject);
631         return S_OK;
632 }
633
634 HRESULT WINAPI IDirectMusicLoader8Impl_LoadObjectFromFile (LPDIRECTMUSICLOADER8 iface,  
635                                                            REFGUID rguidClassID, 
636                                                            REFIID iidInterfaceID, 
637                                                            WCHAR* pwzFilePath, 
638                                                            void** ppObject) {
639         ICOM_THIS(IDirectMusicLoader8Impl,iface);
640         DMUS_OBJECTDESC ObjDesc;
641         
642         TRACE("(%p, %s, %s, %s, %p): wrapping to IDirectMusicLoader8Impl_GetObject\n", This, debugstr_guid(rguidClassID), debugstr_guid(iidInterfaceID), debugstr_w(pwzFilePath), ppObject);
643         
644         ObjDesc.dwSize = sizeof(DMUS_OBJECTDESC);
645         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 */
646         ObjDesc.guidClass = *rguidClassID;
647         /* OK, MSDN says that search order is the following:
648             - current directory (DONE)
649             - windows search path (FIXME: how do I get that?)
650             - loader's search path (DONE)
651         */
652         /* search in current directory */
653         if (!SearchPathW (NULL, pwzFilePath, NULL,
654                           sizeof(ObjDesc.wszFileName)/sizeof(WCHAR), ObjDesc.wszFileName, NULL) &&
655             /* search in loader's search path */
656             !SearchPathW (This->wzSearchPath, pwzFilePath, NULL,
657                           sizeof(ObjDesc.wszFileName)/sizeof(WCHAR), ObjDesc.wszFileName, NULL))
658         {
659                 /* cannot find file */
660                 TRACE("cannot find file\n");
661                 return DMUS_E_LOADER_FAILEDOPEN;
662         }
663         
664         TRACE("full file path = %s\n", debugstr_w (ObjDesc.wszFileName));
665         
666         return IDirectMusicLoader8Impl_GetObject (iface, &ObjDesc, iidInterfaceID, ppObject);
667 }
668
669 ICOM_VTABLE(IDirectMusicLoader8) DirectMusicLoader8_Vtbl = {
670     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
671         IDirectMusicLoader8Impl_QueryInterface,
672         IDirectMusicLoader8Impl_AddRef,
673         IDirectMusicLoader8Impl_Release,
674         IDirectMusicLoader8Impl_GetObject,
675         IDirectMusicLoader8Impl_SetObject,
676         IDirectMusicLoader8Impl_SetSearchDirectory,
677         IDirectMusicLoader8Impl_ScanDirectory,
678         IDirectMusicLoader8Impl_CacheObject,
679         IDirectMusicLoader8Impl_ReleaseObject,
680         IDirectMusicLoader8Impl_ClearCache,
681         IDirectMusicLoader8Impl_EnableCache,
682         IDirectMusicLoader8Impl_EnumObject,
683         IDirectMusicLoader8Impl_CollectGarbage,
684         IDirectMusicLoader8Impl_ReleaseObjectByUnknown,
685         IDirectMusicLoader8Impl_LoadObjectFromFile
686 };
687
688 /* for ClassFactory */
689 HRESULT WINAPI DMUSIC_CreateDirectMusicLoaderImpl (LPCGUID lpcGUID, LPVOID *ppobj, LPUNKNOWN pUnkOuter) {
690         IDirectMusicLoader8Impl *obj;
691
692         TRACE("(%p,%p,%p)\n",lpcGUID, ppobj, pUnkOuter);
693         obj = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IDirectMusicLoader8Impl));
694         if (NULL == obj) {
695                 *ppobj = (LPDIRECTMUSICLOADER8)NULL;
696                 return E_OUTOFMEMORY;
697         }
698         obj->lpVtbl = &DirectMusicLoader8_Vtbl;
699         obj->ref = 0; /* will be inited with QueryInterface */
700         MultiByteToWideChar (CP_ACP, 0, ".\\", -1, obj->wzSearchPath, MAX_PATH);
701         list_init (&obj->CacheList);
702         list_init (&obj->AliasList);
703
704         return IDirectMusicLoader8Impl_QueryInterface ((LPDIRECTMUSICLOADER8)obj, lpcGUID, ppobj);
705 }
706
707 /* help function for IDirectMusicLoader8Impl_GetObject */
708 HRESULT WINAPI DMUSIC_GetDefaultGMPath (WCHAR wszPath[MAX_PATH]) {
709         HKEY hkDM;
710         DWORD returnType, sizeOfReturnBuffer = MAX_PATH;
711         char szPath[MAX_PATH];
712
713         if ((RegOpenKeyExA (HKEY_LOCAL_MACHINE, "Software\\Microsoft\\DirectMusic" , 0, KEY_READ, &hkDM) != ERROR_SUCCESS) || 
714             (RegQueryValueExA (hkDM, "GMFilePath", NULL, &returnType, szPath, &sizeOfReturnBuffer) != ERROR_SUCCESS)) {
715                 WARN(": registry entry missing\n" );
716                 return E_FAIL;
717         }
718         /* FIXME: Check return types to ensure we're interpreting data right */
719         MultiByteToWideChar (CP_ACP, 0, szPath, -1, wszPath, MAX_PATH);
720         
721         return S_OK;
722 }