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