2 * IDirectMusicLoaderImpl
4 * Copyright (C) 2003-2004 Rok Mandeljc
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include "dmloader_private.h"
23 WINE_DEFAULT_DEBUG_CHANNEL(dmloader
);
25 static const GUID
*classes
[] = {
26 &GUID_DirectMusicAllTypes
, /* Keep as first */
27 &CLSID_DirectMusicAudioPathConfig
,
28 &CLSID_DirectMusicBand
,
29 &CLSID_DirectMusicContainer
,
30 &CLSID_DirectMusicCollection
,
31 &CLSID_DirectMusicChordMap
,
32 &CLSID_DirectMusicSegment
,
33 &CLSID_DirectMusicScript
,
34 &CLSID_DirectMusicSong
,
35 &CLSID_DirectMusicStyle
,
36 &CLSID_DirectMusicGraph
,
37 &CLSID_DirectSoundWave
40 /* cache/alias entry */
44 IDirectMusicObject
*pObject
;
45 BOOL bInvalidDefaultDLS
; /* workaround for enabling caching of "faulty" default dls collection */
48 typedef struct IDirectMusicLoaderImpl
{
49 IDirectMusicLoader8 IDirectMusicLoader8_iface
;
51 WCHAR
*search_paths
[ARRAY_SIZE(classes
)];
52 unsigned int cache_class
;
54 } IDirectMusicLoaderImpl
;
57 static inline IDirectMusicLoaderImpl
* impl_from_IDirectMusicLoader8(IDirectMusicLoader8
*iface
)
59 return CONTAINING_RECORD(iface
, IDirectMusicLoaderImpl
, IDirectMusicLoader8_iface
);
62 static int index_from_class(REFCLSID
class)
66 for (i
= 0; i
< ARRAY_SIZE(classes
); i
++)
67 if (IsEqualGUID(class, classes
[i
]))
73 static inline BOOL
is_cache_enabled(IDirectMusicLoaderImpl
*This
, REFCLSID
class)
75 return !!(This
->cache_class
& 1 << index_from_class(class));
78 static void get_search_path(IDirectMusicLoaderImpl
*This
, REFGUID
class, WCHAR
*path
)
80 int index
= index_from_class(class);
82 if (index
< 0 || !This
->search_paths
[index
])
83 index
= 0; /* Default to GUID_DirectMusicAllTypes */
85 if (This
->search_paths
[index
])
86 lstrcpynW(path
, This
->search_paths
[index
], MAX_PATH
);
91 static HRESULT
DMUSIC_CopyDescriptor(DMUS_OBJECTDESC
*pDst
, DMUS_OBJECTDESC
*pSrc
)
93 if (TRACE_ON(dmloader
))
94 dump_DMUS_OBJECTDESC(pSrc
);
96 /* copy field by field */
97 if (pSrc
->dwValidData
& DMUS_OBJ_CLASS
) pDst
->guidClass
= pSrc
->guidClass
;
98 if (pSrc
->dwValidData
& DMUS_OBJ_OBJECT
) pDst
->guidObject
= pSrc
->guidObject
;
99 if (pSrc
->dwValidData
& DMUS_OBJ_DATE
) pDst
->ftDate
= pSrc
->ftDate
;
100 if (pSrc
->dwValidData
& DMUS_OBJ_VERSION
) pDst
->vVersion
= pSrc
->vVersion
;
101 if (pSrc
->dwValidData
& DMUS_OBJ_NAME
) strcpyW (pDst
->wszName
, pSrc
->wszName
);
102 if (pSrc
->dwValidData
& DMUS_OBJ_CATEGORY
) strcpyW (pDst
->wszCategory
, pSrc
->wszCategory
);
103 if (pSrc
->dwValidData
& DMUS_OBJ_FILENAME
) strcpyW (pDst
->wszFileName
, pSrc
->wszFileName
);
104 if (pSrc
->dwValidData
& DMUS_OBJ_STREAM
) IStream_Clone (pSrc
->pStream
, &pDst
->pStream
);
105 if (pSrc
->dwValidData
& DMUS_OBJ_MEMORY
) {
106 pDst
->pbMemData
= pSrc
->pbMemData
;
107 pDst
->llMemLength
= pSrc
->llMemLength
;
110 pDst
->dwValidData
|= pSrc
->dwValidData
;
114 /*****************************************************************************
115 * IDirectMusicLoaderImpl implementation
117 /* IUnknown/IDirectMusicLoader(8) part: */
119 static HRESULT WINAPI
IDirectMusicLoaderImpl_QueryInterface(IDirectMusicLoader8
*iface
, REFIID riid
, void **ppobj
)
121 IDirectMusicLoaderImpl
*This
= impl_from_IDirectMusicLoader8(iface
);
123 TRACE("(%p, %s, %p)\n",This
, debugstr_dmguid(riid
), ppobj
);
124 if (IsEqualIID (riid
, &IID_IUnknown
) ||
125 IsEqualIID (riid
, &IID_IDirectMusicLoader
) ||
126 IsEqualIID (riid
, &IID_IDirectMusicLoader8
)) {
127 IDirectMusicLoader_AddRef (iface
);
132 WARN(": not found\n");
133 return E_NOINTERFACE
;
136 static ULONG WINAPI
IDirectMusicLoaderImpl_AddRef(IDirectMusicLoader8
*iface
)
138 IDirectMusicLoaderImpl
*This
= impl_from_IDirectMusicLoader8(iface
);
139 ULONG ref
= InterlockedIncrement(&This
->ref
);
141 TRACE("(%p)->(): new ref = %u\n", iface
, ref
);
146 static ULONG WINAPI
IDirectMusicLoaderImpl_Release(IDirectMusicLoader8
*iface
)
148 IDirectMusicLoaderImpl
*This
= impl_from_IDirectMusicLoader8(iface
);
149 ULONG ref
= InterlockedDecrement(&This
->ref
);
151 TRACE("(%p)->(): new ref = %u\n", iface
, ref
);
156 IDirectMusicLoader8_ClearCache(iface
, &GUID_DirectMusicAllTypes
);
157 for (i
= 0; i
< ARRAY_SIZE(classes
); i
++)
158 HeapFree(GetProcessHeap(), 0, This
->search_paths
[i
]);
159 HeapFree(GetProcessHeap(), 0, This
);
166 static HRESULT WINAPI
IDirectMusicLoaderImpl_GetObject(IDirectMusicLoader8
*iface
, DMUS_OBJECTDESC
*pDesc
, REFIID riid
, void **ppv
)
168 IDirectMusicLoaderImpl
*This
= impl_from_IDirectMusicLoader8(iface
);
169 HRESULT result
= S_OK
;
170 HRESULT ret
= S_OK
; /* used at the end of function, to determine whether everything went OK */
171 struct cache_entry
*pExistingEntry
, *pObjectEntry
= NULL
;
173 IPersistStream
* pPersistStream
= NULL
;
175 LPDIRECTMUSICOBJECT pObject
;
176 DMUS_OBJECTDESC GotDesc
;
179 TRACE("(%p)->(%p, %s, %p)\n", This
, pDesc
, debugstr_dmguid(riid
), ppv
);
181 if (TRACE_ON(dmloader
))
182 dump_DMUS_OBJECTDESC(pDesc
);
184 /* sometimes it happens that guidClass is missing... which is a BadThingTM */
185 if (!(pDesc
->dwValidData
& DMUS_OBJ_CLASS
)) {
186 ERR(": guidClass not valid but needed\n");
188 return DMUS_E_LOADER_NOCLASSID
;
191 /* OK, first we iterate through the list of objects we know about; these are either loaded (GetObject, LoadObjectFromFile)
192 or set via SetObject; */
193 TRACE(": looking if we have object in the cache or if it can be found via alias\n");
194 LIST_FOR_EACH_ENTRY(pExistingEntry
, &This
->cache
, struct cache_entry
, entry
) {
195 if ((pDesc
->dwValidData
& DMUS_OBJ_OBJECT
) &&
196 (pExistingEntry
->Desc
.dwValidData
& DMUS_OBJ_OBJECT
) &&
197 IsEqualGUID (&pDesc
->guidObject
, &pExistingEntry
->Desc
.guidObject
)) {
198 TRACE(": found it by object GUID\n");
199 /* I suppose such stuff can happen only when GUID for object is given (GUID_DefaultGMCollection) */
200 if (pExistingEntry
->bInvalidDefaultDLS
) {
201 TRACE(": found faulty default DLS collection... enabling M$ compliant behaviour\n");
202 return DMUS_E_LOADER_NOFILENAME
;
204 if (pExistingEntry
->Desc
.dwValidData
& DMUS_OBJ_LOADED
) {
205 TRACE(": already loaded\n");
206 return IDirectMusicObject_QueryInterface (pExistingEntry
->pObject
, riid
, ppv
);
208 TRACE(": not loaded yet\n");
209 pObjectEntry
= pExistingEntry
;
212 else if ((pDesc
->dwValidData
& (DMUS_OBJ_FILENAME
| DMUS_OBJ_FULLPATH
)) &&
213 (pExistingEntry
->Desc
.dwValidData
& (DMUS_OBJ_FILENAME
| DMUS_OBJ_FULLPATH
)) &&
214 !strncmpW (pDesc
->wszFileName
, pExistingEntry
->Desc
.wszFileName
, DMUS_MAX_FILENAME
)) {
215 TRACE(": found it by fullpath filename\n");
216 if (pExistingEntry
->Desc
.dwValidData
& DMUS_OBJ_LOADED
) {
217 TRACE(": already loaded\n");
218 return IDirectMusicObject_QueryInterface (pExistingEntry
->pObject
, riid
, ppv
);
220 TRACE(": not loaded yet\n");
221 pObjectEntry
= pExistingEntry
;
224 else if ((pDesc
->dwValidData
& (DMUS_OBJ_NAME
| DMUS_OBJ_CATEGORY
)) &&
225 (pExistingEntry
->Desc
.dwValidData
& (DMUS_OBJ_NAME
| DMUS_OBJ_CATEGORY
)) &&
226 !strncmpW (pDesc
->wszName
, pExistingEntry
->Desc
.wszName
, DMUS_MAX_NAME
) &&
227 !strncmpW (pDesc
->wszCategory
, pExistingEntry
->Desc
.wszCategory
, DMUS_MAX_CATEGORY
)) {
228 TRACE(": found it by name and category\n");
229 if (pExistingEntry
->Desc
.dwValidData
& DMUS_OBJ_LOADED
) {
230 TRACE(": already loaded\n");
231 return IDirectMusicObject_QueryInterface (pExistingEntry
->pObject
, riid
, ppv
);
233 TRACE(": not loaded yet\n");
234 pObjectEntry
= pExistingEntry
;
237 else if ((pDesc
->dwValidData
& DMUS_OBJ_NAME
) &&
238 (pExistingEntry
->Desc
.dwValidData
& DMUS_OBJ_NAME
) &&
239 !strncmpW (pDesc
->wszName
, pExistingEntry
->Desc
.wszName
, DMUS_MAX_NAME
)) {
240 TRACE(": found it by name\n");
241 if (pExistingEntry
->Desc
.dwValidData
& DMUS_OBJ_LOADED
) {
242 TRACE(": already loaded\n");
243 return IDirectMusicObject_QueryInterface (pExistingEntry
->pObject
, riid
, ppv
);
245 TRACE(": not loaded yet\n");
246 pObjectEntry
= pExistingEntry
;
249 else if ((pDesc
->dwValidData
& DMUS_OBJ_FILENAME
) &&
250 (pExistingEntry
->Desc
.dwValidData
& DMUS_OBJ_FILENAME
) &&
251 !strncmpW (pDesc
->wszFileName
, pExistingEntry
->Desc
.wszFileName
, DMUS_MAX_FILENAME
)) {
252 TRACE(": found it by filename\n");
253 if (pExistingEntry
->Desc
.dwValidData
& DMUS_OBJ_LOADED
) {
254 TRACE(": already loaded\n");
255 return IDirectMusicObject_QueryInterface (pExistingEntry
->pObject
, riid
, ppv
);
257 TRACE(": not loaded yet\n");
258 pObjectEntry
= pExistingEntry
;
263 /* basically, if we found alias, we use its descriptor to load...
264 else we use info we were given */
266 TRACE(": found alias entry for requested object... using stored info\n");
267 /* I think in certain cases it can happen that entry's descriptor lacks info about
268 where to load from (e.g.: if we loaded from stream and then released object
269 from cache; then only its CLSID, GUID and perhaps name are left); so just
270 overwrite whatever info the entry has (since it ought to be 100% correct) */
271 DMUSIC_CopyDescriptor (pDesc
, &pObjectEntry
->Desc
);
272 /*pDesc = &pObjectEntry->Desc; */ /* FIXME: is this OK? */
274 TRACE(": no cache/alias entry found for requested object\n");
277 if (pDesc
->dwValidData
& DMUS_OBJ_URL
) {
278 TRACE(": loading from URLs not supported yet\n");
279 return DMUS_E_LOADER_FORMATNOTSUPPORTED
;
281 else if (pDesc
->dwValidData
& DMUS_OBJ_FILENAME
) {
282 /* load object from file */
283 /* generate filename; if it's full path, don't add search
284 directory path, otherwise do */
285 WCHAR wszFileName
[MAX_PATH
];
287 if (pDesc
->dwValidData
& DMUS_OBJ_FULLPATH
) {
288 lstrcpyW(wszFileName
, pDesc
->wszFileName
);
291 get_search_path(This
, &pDesc
->guidClass
, wszFileName
);
292 p
= wszFileName
+ lstrlenW(wszFileName
);
293 if (p
> wszFileName
&& p
[-1] != '\\') *p
++ = '\\';
294 strcpyW(p
, pDesc
->wszFileName
);
296 TRACE(": loading from file (%s)\n", debugstr_w(wszFileName
));
297 /* create stream and associate it with file */
298 result
= DMUSIC_CreateDirectMusicLoaderFileStream ((LPVOID
*)&pStream
);
299 if (FAILED(result
)) {
300 ERR(": could not create file stream\n");
303 result
= IDirectMusicLoaderFileStream_Attach (pStream
, wszFileName
, iface
);
304 if (FAILED(result
)) {
305 ERR(": could not attach stream to file\n");
306 IStream_Release (pStream
);
310 else if (pDesc
->dwValidData
& DMUS_OBJ_MEMORY
) {
311 /* load object from resource */
312 TRACE(": loading from resource\n");
313 /* create stream and associate it with given resource */
314 result
= DMUSIC_CreateDirectMusicLoaderResourceStream ((LPVOID
*)&pStream
);
315 if (FAILED(result
)) {
316 ERR(": could not create resource stream\n");
319 result
= IDirectMusicLoaderResourceStream_Attach (pStream
, pDesc
->pbMemData
, pDesc
->llMemLength
, 0, iface
);
320 if (FAILED(result
)) {
321 ERR(": could not attach stream to resource\n");
322 IStream_Release (pStream
);
326 else if (pDesc
->dwValidData
& DMUS_OBJ_STREAM
) {
327 /* load object from stream */
328 TRACE(": loading from stream\n");
329 /* create universal stream and associate it with given one */
330 result
= DMUSIC_CreateDirectMusicLoaderGenericStream ((LPVOID
*)&pStream
);
331 if (FAILED(result
)) {
332 ERR(": could not create generic stream\n");
335 result
= IDirectMusicLoaderGenericStream_Attach (pStream
, pDesc
->pStream
, iface
);
336 if (FAILED(result
)) {
337 ERR(": failed to attach stream\n");
338 IStream_Release (pStream
);
342 /* nowhere to load from */
343 FIXME(": unknown/unsupported way of loading\n");
344 return DMUS_E_LOADER_NOFILENAME
; /* test shows this is returned */
348 result
= CoCreateInstance (&pDesc
->guidClass
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IDirectMusicObject
, (LPVOID
*)&pObject
);
349 if (FAILED(result
)) {
350 ERR(": could not create object\n");
353 /* acquire PersistStream interface */
354 result
= IDirectMusicObject_QueryInterface (pObject
, &IID_IPersistStream
, (LPVOID
*)&pPersistStream
);
355 if (FAILED(result
)) {
356 ERR("failed to Query\n");
360 result
= IPersistStream_Load (pPersistStream
, pStream
);
361 if (result
!= S_OK
) {
362 WARN(": failed to (completely) load object (%s)\n", debugstr_dmreturn(result
));
366 DM_STRUCT_INIT(&GotDesc
);
367 result
= IDirectMusicObject_GetDescriptor (pObject
, &GotDesc
);
368 /* set filename (if we loaded via filename) */
369 if (pDesc
->dwValidData
& DMUS_OBJ_FILENAME
) {
370 GotDesc
.dwValidData
|= (pDesc
->dwValidData
& (DMUS_OBJ_FILENAME
| DMUS_OBJ_FULLPATH
));
371 strcpyW (GotDesc
.wszFileName
, pDesc
->wszFileName
);
373 if (FAILED(result
)) {
374 ERR(": failed to get descriptor\n");
377 /* release all loading related stuff */
378 IStream_Release (pStream
);
379 IPersistStream_Release (pPersistStream
);
381 /* add object to cache/overwrite existing info (if cache is enabled) */
382 bCache
= is_cache_enabled(This
, &pDesc
->guidClass
);
385 pObjectEntry
= HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY
, sizeof(*pObjectEntry
));
386 DM_STRUCT_INIT(&pObjectEntry
->Desc
);
387 DMUSIC_CopyDescriptor (&pObjectEntry
->Desc
, &GotDesc
);
388 pObjectEntry
->pObject
= pObject
;
389 pObjectEntry
->bInvalidDefaultDLS
= FALSE
;
390 list_add_head(&This
->cache
, &pObjectEntry
->entry
);
392 DMUSIC_CopyDescriptor (&pObjectEntry
->Desc
, &GotDesc
);
393 pObjectEntry
->pObject
= pObject
;
394 pObjectEntry
->bInvalidDefaultDLS
= FALSE
;
396 TRACE(": filled in cache entry\n");
397 } else TRACE(": caching disabled\n");
399 result
= IDirectMusicObject_QueryInterface (pObject
, riid
, ppv
);
400 if (!bCache
) IDirectMusicObject_Release (pObject
); /* since loader's reference is not needed */
401 /* if there was trouble with loading, and if no other error occurred,
402 we should return DMUS_S_PARTIALLOAD; else, error is returned */
409 static HRESULT WINAPI
IDirectMusicLoaderImpl_SetObject(IDirectMusicLoader8
*iface
, DMUS_OBJECTDESC
*pDesc
)
411 IDirectMusicLoaderImpl
*This
= impl_from_IDirectMusicLoader8(iface
);
413 LPDIRECTMUSICOBJECT pObject
;
414 DMUS_OBJECTDESC Desc
;
415 struct cache_entry
*pObjectEntry
, *pNewEntry
;
418 TRACE("(%p)->(%p)\n", This
, pDesc
);
420 if (TRACE_ON(dmloader
))
421 dump_DMUS_OBJECTDESC(pDesc
);
423 /* create stream and load additional info from it */
424 if (pDesc
->dwValidData
& DMUS_OBJ_FILENAME
) {
425 /* generate filename; if it's full path, don't add search
426 directory path, otherwise do */
427 WCHAR wszFileName
[MAX_PATH
];
429 if (pDesc
->dwValidData
& DMUS_OBJ_FULLPATH
) {
430 lstrcpyW(wszFileName
, pDesc
->wszFileName
);
433 get_search_path(This
, &pDesc
->guidClass
, wszFileName
);
434 p
= wszFileName
+ lstrlenW(wszFileName
);
435 if (p
> wszFileName
&& p
[-1] != '\\') *p
++ = '\\';
436 strcpyW(p
, pDesc
->wszFileName
);
439 hr
= DMUSIC_CreateDirectMusicLoaderFileStream ((LPVOID
*)&pStream
);
441 ERR(": could not create file stream\n");
442 return DMUS_E_LOADER_FAILEDOPEN
;
445 hr
= IDirectMusicLoaderFileStream_Attach (pStream
, wszFileName
, iface
);
447 ERR(": could not attach stream to file %s, make sure it exists\n", debugstr_w(wszFileName
));
448 IStream_Release (pStream
);
449 return DMUS_E_LOADER_FAILEDOPEN
;
452 else if (pDesc
->dwValidData
& DMUS_OBJ_STREAM
) {
454 hr
= DMUSIC_CreateDirectMusicLoaderGenericStream ((LPVOID
*)&pStream
);
456 ERR(": could not create generic stream\n");
457 return DMUS_E_LOADER_FAILEDOPEN
;
460 hr
= IDirectMusicLoaderGenericStream_Attach (pStream
, pDesc
->pStream
, iface
);
462 ERR(": could not attach stream\n");
463 IStream_Release (pStream
);
464 return DMUS_E_LOADER_FAILEDOPEN
;
467 else if (pDesc
->dwValidData
& DMUS_OBJ_MEMORY
) {
469 hr
= DMUSIC_CreateDirectMusicLoaderResourceStream ((LPVOID
*)&pStream
);
471 ERR(": could not create resource stream\n");
472 return DMUS_E_LOADER_FAILEDOPEN
;
475 hr
= IDirectMusicLoaderResourceStream_Attach (pStream
, pDesc
->pbMemData
, pDesc
->llMemLength
, 0, iface
);
477 ERR(": could not attach stream to resource\n");
478 IStream_Release (pStream
);
479 return DMUS_E_LOADER_FAILEDOPEN
;
483 ERR(": no way to get additional info\n");
484 return DMUS_E_LOADER_FAILEDOPEN
;
488 hr
= CoCreateInstance (&pDesc
->guidClass
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IDirectMusicObject
, (LPVOID
*)&pObject
);
490 ERR("Object creation of %s failed 0x%08x\n", debugstr_guid(&pDesc
->guidClass
),hr
);
491 return DMUS_E_LOADER_FAILEDOPEN
;
494 /* *sigh*... some ms objects have lousy implementation of ParseDescriptor that clears input descriptor :( */
495 #ifdef NOW_WE_ARE_FREE
496 /* parse descriptor: we actually use input descriptor; fields that aren't available from stream remain,
497 otherwise real info is set */
498 IDirectMusicObject_ParseDescriptor (pObject
, pStream
, pDesc
);
500 /* hmph... due to some trouble I had with certain tests, we store current position and then set it back */
501 DM_STRUCT_INIT(&Desc
);
502 if (FAILED(IDirectMusicObject_ParseDescriptor (pObject
, pStream
, &Desc
))) {
503 ERR(": couldn't parse descriptor\n");
504 return DMUS_E_LOADER_FORMATNOTSUPPORTED
;
507 /* copy elements from parsed descriptor into input descriptor; this sets new info, overwriting if necessary,
508 but leaves info that's provided by input and not available from stream */
509 DMUSIC_CopyDescriptor (pDesc
, &Desc
);
511 /* release everything */
512 IDirectMusicObject_Release (pObject
);
513 IStream_Release (pStream
);
515 /* sometimes it happens that twisted programs call SetObject for same object twice...
516 in such cases, native loader returns S_OK and does nothing... a sound plan */
517 LIST_FOR_EACH_ENTRY(pObjectEntry
, &This
->cache
, struct cache_entry
, entry
) {
518 if (!memcmp (&pObjectEntry
->Desc
, pDesc
, sizeof(DMUS_OBJECTDESC
))) {
519 TRACE(": exactly same entry already exists\n");
525 TRACE(": adding alias entry with following info:\n");
526 if (TRACE_ON(dmloader
))
527 dump_DMUS_OBJECTDESC(pDesc
);
528 pNewEntry
= HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY
, sizeof(*pNewEntry
));
529 /* use this function instead of pure memcpy due to streams (memcpy just copies pointer),
530 which is basically used further by app that called SetDescriptor... better safety than exception */
531 DMUSIC_CopyDescriptor (&pNewEntry
->Desc
, pDesc
);
532 list_add_head(&This
->cache
, &pNewEntry
->entry
);
537 static HRESULT WINAPI
IDirectMusicLoaderImpl_SetSearchDirectory(IDirectMusicLoader8
*iface
,
538 REFGUID
class, WCHAR
*path
, BOOL clear
)
540 IDirectMusicLoaderImpl
*This
= impl_from_IDirectMusicLoader8(iface
);
541 int index
= index_from_class(class);
544 TRACE("(%p, %s, %s, %d)\n", This
, debugstr_dmguid(class), debugstr_w(path
), clear
);
550 attr
= GetFileAttributesW(path
);
551 if (attr
== INVALID_FILE_ATTRIBUTES
|| !(attr
& FILE_ATTRIBUTE_DIRECTORY
))
552 return DMUS_E_LOADER_BADPATH
;
556 FIXME("clear flag ignored\n");
558 /* Ignore invalid GUIDs */
562 if (!This
->search_paths
[index
])
563 This
->search_paths
[index
] = HeapAlloc(GetProcessHeap(), 0, MAX_PATH
);
564 else if (!strncmpW(This
->search_paths
[index
], path
, MAX_PATH
))
567 lstrcpynW(This
->search_paths
[index
], path
, MAX_PATH
);
572 static HRESULT WINAPI
IDirectMusicLoaderImpl_ScanDirectory(IDirectMusicLoader8
*iface
, REFGUID rguidClass
, WCHAR
*pwzFileExtension
, WCHAR
*pwzScanFileName
)
574 IDirectMusicLoaderImpl
*This
= impl_from_IDirectMusicLoader8(iface
);
575 static const WCHAR wszAny
[] = {'*',0};
576 WIN32_FIND_DATAW FileData
;
578 WCHAR wszSearchString
[MAX_PATH
];
581 TRACE("(%p, %s, %s, %s)\n", This
, debugstr_dmguid(rguidClass
), debugstr_w(pwzFileExtension
),
582 debugstr_w(pwzScanFileName
));
583 if (index_from_class(rguidClass
) <= 0) {
584 ERR(": rguidClass invalid CLSID\n");
585 return REGDB_E_CLASSNOTREG
;
588 if (!pwzFileExtension
)
591 /* get search path for given class */
592 get_search_path(This
, rguidClass
, wszSearchString
);
594 p
= wszSearchString
+ lstrlenW(wszSearchString
);
595 if (p
> wszSearchString
&& p
[-1] != '\\') *p
++ = '\\';
596 *p
++ = '*'; /* any file */
597 if (strcmpW (pwzFileExtension
, wszAny
)) *p
++ = '.'; /* if we have actual extension, put a dot */
598 strcpyW (p
, pwzFileExtension
);
600 TRACE(": search string: %s\n", debugstr_w(wszSearchString
));
602 hSearch
= FindFirstFileW (wszSearchString
, &FileData
);
603 if (hSearch
== INVALID_HANDLE_VALUE
) {
604 TRACE(": no files found\n");
609 DMUS_OBJECTDESC Desc
;
610 DM_STRUCT_INIT(&Desc
);
611 Desc
.dwValidData
= DMUS_OBJ_CLASS
| DMUS_OBJ_FILENAME
| DMUS_OBJ_FULLPATH
| DMUS_OBJ_DATE
;
612 Desc
.guidClass
= *rguidClass
;
613 strcpyW (Desc
.wszFileName
, FileData
.cFileName
);
614 FileTimeToLocalFileTime (&FileData
.ftCreationTime
, &Desc
.ftDate
);
615 IDirectMusicLoader8_SetObject (iface
, &Desc
);
617 if (!FindNextFileW (hSearch
, &FileData
)) {
618 if (GetLastError () == ERROR_NO_MORE_FILES
) {
619 TRACE(": search completed\n");
622 ERR(": could not get next file\n");
631 static HRESULT WINAPI
IDirectMusicLoaderImpl_CacheObject(IDirectMusicLoader8
*iface
, IDirectMusicObject
*pObject
)
633 IDirectMusicLoaderImpl
*This
= impl_from_IDirectMusicLoader8(iface
);
634 DMUS_OBJECTDESC Desc
;
635 HRESULT result
= DMUS_E_LOADER_OBJECTNOTFOUND
;
636 struct cache_entry
*pObjectEntry
;
638 TRACE("(%p, %p)\n", This
, pObject
);
641 DM_STRUCT_INIT(&Desc
);
642 IDirectMusicObject_GetDescriptor (pObject
, &Desc
);
644 /* now iterate through the list and check if we have an alias (without object), corresponding
645 to the descriptor of the input object */
646 LIST_FOR_EACH_ENTRY(pObjectEntry
, &This
->cache
, struct cache_entry
, entry
) {
647 if ((Desc
.dwValidData
& DMUS_OBJ_OBJECT
) &&
648 (pObjectEntry
->Desc
.dwValidData
& DMUS_OBJ_OBJECT
) &&
649 IsEqualGUID (&Desc
.guidObject
, &pObjectEntry
->Desc
.guidObject
)) {
650 TRACE(": found it by object GUID\n");
651 if ((pObjectEntry
->Desc
.dwValidData
& DMUS_OBJ_LOADED
) && pObjectEntry
->pObject
)
657 else if ((Desc
.dwValidData
& (DMUS_OBJ_FILENAME
| DMUS_OBJ_FULLPATH
)) &&
658 (pObjectEntry
->Desc
.dwValidData
& (DMUS_OBJ_FILENAME
| DMUS_OBJ_FULLPATH
)) &&
659 !strncmpW (Desc
.wszFileName
, pObjectEntry
->Desc
.wszFileName
, DMUS_MAX_FILENAME
)) {
660 TRACE(": found it by fullpath filename\n");
661 if ((pObjectEntry
->Desc
.dwValidData
& DMUS_OBJ_LOADED
) && pObjectEntry
->pObject
)
667 else if ((Desc
.dwValidData
& (DMUS_OBJ_NAME
| DMUS_OBJ_CATEGORY
)) &&
668 (pObjectEntry
->Desc
.dwValidData
& (DMUS_OBJ_NAME
| DMUS_OBJ_CATEGORY
)) &&
669 !strncmpW (Desc
.wszName
, pObjectEntry
->Desc
.wszName
, DMUS_MAX_NAME
) &&
670 !strncmpW (Desc
.wszCategory
, pObjectEntry
->Desc
.wszCategory
, DMUS_MAX_CATEGORY
)) {
671 TRACE(": found it by name and category\n");
672 if ((pObjectEntry
->Desc
.dwValidData
& DMUS_OBJ_LOADED
) && pObjectEntry
->pObject
)
678 else if ((Desc
.dwValidData
& DMUS_OBJ_NAME
) &&
679 (pObjectEntry
->Desc
.dwValidData
& DMUS_OBJ_NAME
) &&
680 !strncmpW (Desc
.wszName
, pObjectEntry
->Desc
.wszName
, DMUS_MAX_NAME
)) {
681 TRACE(": found it by name\n");
682 if ((pObjectEntry
->Desc
.dwValidData
& DMUS_OBJ_LOADED
) && pObjectEntry
->pObject
)
688 else if ((Desc
.dwValidData
& DMUS_OBJ_FILENAME
) &&
689 (pObjectEntry
->Desc
.dwValidData
& DMUS_OBJ_FILENAME
) &&
690 !strncmpW (Desc
.wszFileName
, pObjectEntry
->Desc
.wszFileName
, DMUS_MAX_FILENAME
)) {
691 TRACE(": found it by filename\n");
692 if ((pObjectEntry
->Desc
.dwValidData
& DMUS_OBJ_LOADED
) && pObjectEntry
->pObject
)
700 /* if we found such alias, then set everything */
701 if (result
== S_OK
) {
702 pObjectEntry
->Desc
.dwValidData
&= DMUS_OBJ_LOADED
;
703 pObjectEntry
->pObject
= pObject
;
704 IDirectMusicObject_AddRef (pObjectEntry
->pObject
);
710 static HRESULT WINAPI
IDirectMusicLoaderImpl_ReleaseObject(IDirectMusicLoader8
*iface
, IDirectMusicObject
*pObject
)
712 IDirectMusicLoaderImpl
*This
= impl_from_IDirectMusicLoader8(iface
);
713 DMUS_OBJECTDESC Desc
;
714 struct cache_entry
*pObjectEntry
;
715 HRESULT result
= S_FALSE
;
717 TRACE("(%p, %p)\n", This
, pObject
);
719 if(!pObject
) return E_POINTER
;
722 DM_STRUCT_INIT(&Desc
);
723 IDirectMusicObject_GetDescriptor (pObject
, &Desc
);
725 /* iterate through the list of objects we know about; check only those with DMUS_OBJ_LOADED */
726 TRACE(": looking for the object in cache\n");
727 LIST_FOR_EACH_ENTRY(pObjectEntry
, &This
->cache
, struct cache_entry
, entry
) {
728 if ((Desc
.dwValidData
& DMUS_OBJ_OBJECT
) &&
729 (pObjectEntry
->Desc
.dwValidData
& (DMUS_OBJ_OBJECT
| DMUS_OBJ_LOADED
)) &&
730 IsEqualGUID (&Desc
.guidObject
, &pObjectEntry
->Desc
.guidObject
)) {
731 TRACE(": found it by object GUID\n");
732 if (TRACE_ON(dmloader
))
733 dump_DMUS_OBJECTDESC(&pObjectEntry
->Desc
);
737 else if ((Desc
.dwValidData
& (DMUS_OBJ_FILENAME
| DMUS_OBJ_FULLPATH
)) &&
738 (pObjectEntry
->Desc
.dwValidData
& (DMUS_OBJ_FILENAME
| DMUS_OBJ_FULLPATH
| DMUS_OBJ_LOADED
)) &&
739 !strncmpW (Desc
.wszFileName
, pObjectEntry
->Desc
.wszFileName
, DMUS_MAX_FILENAME
)) {
740 TRACE(": found it by fullpath filename\n");
744 else if ((Desc
.dwValidData
& (DMUS_OBJ_NAME
| DMUS_OBJ_CATEGORY
)) &&
745 (pObjectEntry
->Desc
.dwValidData
& (DMUS_OBJ_NAME
| DMUS_OBJ_CATEGORY
| DMUS_OBJ_LOADED
)) &&
746 !strncmpW (Desc
.wszName
, pObjectEntry
->Desc
.wszName
, DMUS_MAX_NAME
) &&
747 !strncmpW (Desc
.wszCategory
, pObjectEntry
->Desc
.wszCategory
, DMUS_MAX_CATEGORY
)) {
748 TRACE(": found it by name and category\n");
752 else if ((Desc
.dwValidData
& DMUS_OBJ_NAME
) &&
753 (pObjectEntry
->Desc
.dwValidData
& (DMUS_OBJ_NAME
| DMUS_OBJ_LOADED
)) &&
754 !strncmpW (Desc
.wszName
, pObjectEntry
->Desc
.wszName
, DMUS_MAX_NAME
)) {
755 TRACE(": found it by name\n");
759 else if ((Desc
.dwValidData
& DMUS_OBJ_FILENAME
) &&
760 (pObjectEntry
->Desc
.dwValidData
& (DMUS_OBJ_FILENAME
| DMUS_OBJ_LOADED
)) &&
761 !strncmpW (Desc
.wszFileName
, pObjectEntry
->Desc
.wszFileName
, DMUS_MAX_FILENAME
)) {
762 TRACE(": found it by filename\n");
767 if (result
== S_OK
) {
768 /*TRACE(": releasing:\n%s - bInvalidDefaultDLS = %i\n - pObject = %p\n", debugstr_DMUS_OBJECTDESC(&pObjectEntry->Desc), pObjectEntry->bInvalidDefaultDLS, pObjectEntry->pObject); */
769 IDirectMusicObject_Release (pObjectEntry
->pObject
);
770 pObjectEntry
->pObject
= NULL
;
771 pObjectEntry
->Desc
.dwValidData
&= ~DMUS_OBJ_LOADED
;
776 static HRESULT WINAPI
IDirectMusicLoaderImpl_ClearCache(IDirectMusicLoader8
*iface
, REFGUID
class)
778 IDirectMusicLoaderImpl
*This
= impl_from_IDirectMusicLoader8(iface
);
779 struct cache_entry
*obj
, *obj2
;
781 TRACE("(%p, %s)\n", This
, debugstr_dmguid(class));
783 LIST_FOR_EACH_ENTRY_SAFE(obj
, obj2
, &This
->cache
, struct cache_entry
, entry
) {
784 if ((IsEqualGUID(class, &GUID_DirectMusicAllTypes
) || IsEqualGUID(class, &obj
->Desc
.guidClass
)) &&
785 (obj
->Desc
.dwValidData
& DMUS_OBJ_LOADED
)) {
786 /* basically, wrap to ReleaseObject for each object found */
787 IDirectMusicLoader8_ReleaseObject(iface
, obj
->pObject
);
788 list_remove(&obj
->entry
);
789 HeapFree(GetProcessHeap(), 0, obj
);
796 static HRESULT WINAPI
IDirectMusicLoaderImpl_EnableCache(IDirectMusicLoader8
*iface
, REFGUID
class,
799 IDirectMusicLoaderImpl
*This
= impl_from_IDirectMusicLoader8(iface
);
802 TRACE("(%p, %s, %d)\n", This
, debugstr_dmguid(class), enable
);
804 current
= is_cache_enabled(This
, class);
806 if (IsEqualGUID(class, &GUID_DirectMusicAllTypes
))
807 This
->cache_class
= enable
? ~0 : 0;
810 This
->cache_class
|= 1 << index_from_class(class);
812 This
->cache_class
&= ~(1 << index_from_class(class));
816 IDirectMusicLoader8_ClearCache(iface
, class);
818 if (current
== enable
)
824 static HRESULT WINAPI
IDirectMusicLoaderImpl_EnumObject(IDirectMusicLoader8
*iface
, REFGUID rguidClass
, DWORD dwIndex
, DMUS_OBJECTDESC
*pDesc
)
826 IDirectMusicLoaderImpl
*This
= impl_from_IDirectMusicLoader8(iface
);
828 struct cache_entry
*pObjectEntry
;
829 TRACE("(%p, %s, %d, %p)\n", This
, debugstr_dmguid(rguidClass
), dwIndex
, pDesc
);
831 DM_STRUCT_INIT(pDesc
);
833 LIST_FOR_EACH_ENTRY(pObjectEntry
, &This
->cache
, struct cache_entry
, entry
) {
834 if (IsEqualGUID (rguidClass
, &GUID_DirectMusicAllTypes
) || IsEqualGUID (rguidClass
, &pObjectEntry
->Desc
.guidClass
)) {
835 if (dwCount
== dwIndex
) {
836 *pDesc
= pObjectEntry
->Desc
;
837 /* we aren't supposed to reveal this info */
838 pDesc
->dwValidData
&= ~(DMUS_OBJ_MEMORY
| DMUS_OBJ_STREAM
);
839 pDesc
->pbMemData
= NULL
;
840 pDesc
->llMemLength
= 0;
841 pDesc
->pStream
= NULL
;
848 TRACE(": not found\n");
852 static void WINAPI
IDirectMusicLoaderImpl_CollectGarbage(IDirectMusicLoader8
*iface
)
854 FIXME("(%p)->(): stub\n", iface
);
857 static HRESULT WINAPI
IDirectMusicLoaderImpl_ReleaseObjectByUnknown(IDirectMusicLoader8
*iface
, IUnknown
*pObject
)
859 IDirectMusicLoaderImpl
*This
= impl_from_IDirectMusicLoader8(iface
);
861 LPDIRECTMUSICOBJECT pObjectInterface
;
863 TRACE("(%p, %p)\n", This
, pObject
);
865 if (IsBadReadPtr (pObject
, sizeof(*pObject
))) {
866 ERR(": pObject bad write pointer\n");
869 /* we simply get IDirectMusicObject interface */
870 result
= IUnknown_QueryInterface (pObject
, &IID_IDirectMusicObject
, (LPVOID
*)&pObjectInterface
);
871 if (FAILED(result
)) return result
;
872 /* and release it in old-fashioned way */
873 result
= IDirectMusicLoader8_ReleaseObject (iface
, pObjectInterface
);
874 IDirectMusicObject_Release (pObjectInterface
);
879 static HRESULT WINAPI
IDirectMusicLoaderImpl_LoadObjectFromFile(IDirectMusicLoader8
*iface
, REFGUID rguidClassID
, REFIID iidInterfaceID
, WCHAR
*pwzFilePath
, void **ppObject
)
881 IDirectMusicLoaderImpl
*This
= impl_from_IDirectMusicLoader8(iface
);
882 DMUS_OBJECTDESC ObjDesc
;
883 WCHAR wszLoaderSearchPath
[MAX_PATH
];
885 TRACE("(%p, %s, %s, %s, %p): wrapping to IDirectMusicLoaderImpl_GetObject\n", This
, debugstr_dmguid(rguidClassID
), debugstr_dmguid(iidInterfaceID
), debugstr_w(pwzFilePath
), ppObject
);
887 DM_STRUCT_INIT(&ObjDesc
);
888 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 */
889 ObjDesc
.guidClass
= *rguidClassID
;
890 /* OK, MSDN says that search order is the following:
891 - current directory (DONE)
892 - windows search path (FIXME: how do I get that?)
893 - loader's search path (DONE)
895 get_search_path(This
, rguidClassID
, wszLoaderSearchPath
);
896 /* search in current directory */
897 if (!SearchPathW(NULL
, pwzFilePath
, NULL
, ARRAY_SIZE(ObjDesc
.wszFileName
), ObjDesc
.wszFileName
, NULL
) &&
898 /* search in loader's search path */
899 !SearchPathW(wszLoaderSearchPath
, pwzFilePath
, NULL
, ARRAY_SIZE(ObjDesc
.wszFileName
), ObjDesc
.wszFileName
, NULL
)) {
900 /* cannot find file */
901 TRACE(": cannot find file\n");
902 return DMUS_E_LOADER_FAILEDOPEN
;
905 TRACE(": full file path = %s\n", debugstr_w (ObjDesc
.wszFileName
));
907 return IDirectMusicLoader_GetObject(iface
, &ObjDesc
, iidInterfaceID
, ppObject
);
910 static const IDirectMusicLoader8Vtbl DirectMusicLoader_Loader_Vtbl
= {
911 IDirectMusicLoaderImpl_QueryInterface
,
912 IDirectMusicLoaderImpl_AddRef
,
913 IDirectMusicLoaderImpl_Release
,
914 IDirectMusicLoaderImpl_GetObject
,
915 IDirectMusicLoaderImpl_SetObject
,
916 IDirectMusicLoaderImpl_SetSearchDirectory
,
917 IDirectMusicLoaderImpl_ScanDirectory
,
918 IDirectMusicLoaderImpl_CacheObject
,
919 IDirectMusicLoaderImpl_ReleaseObject
,
920 IDirectMusicLoaderImpl_ClearCache
,
921 IDirectMusicLoaderImpl_EnableCache
,
922 IDirectMusicLoaderImpl_EnumObject
,
923 IDirectMusicLoaderImpl_CollectGarbage
,
924 IDirectMusicLoaderImpl_ReleaseObjectByUnknown
,
925 IDirectMusicLoaderImpl_LoadObjectFromFile
928 /* help function for DMUSIC_SetDefaultDLS */
929 static HRESULT
DMUSIC_GetDefaultGMPath (WCHAR wszPath
[MAX_PATH
]) {
931 DWORD returnType
, sizeOfReturnBuffer
= MAX_PATH
;
932 char szPath
[MAX_PATH
];
934 if ((RegOpenKeyExA (HKEY_LOCAL_MACHINE
, "Software\\Microsoft\\DirectMusic" , 0, KEY_READ
, &hkDM
) != ERROR_SUCCESS
) ||
935 (RegQueryValueExA (hkDM
, "GMFilePath", NULL
, &returnType
, (LPBYTE
) szPath
, &sizeOfReturnBuffer
) != ERROR_SUCCESS
)) {
936 WARN(": registry entry missing\n" );
939 /* FIXME: Check return types to ensure we're interpreting data right */
940 MultiByteToWideChar (CP_ACP
, 0, szPath
, -1, wszPath
, MAX_PATH
);
945 /* for ClassFactory */
946 HRESULT WINAPI
create_dmloader(REFIID lpcGUID
, void **ppobj
)
948 IDirectMusicLoaderImpl
*obj
;
949 DMUS_OBJECTDESC Desc
;
950 struct cache_entry
*dls
;
953 TRACE("(%s, %p)\n", debugstr_dmguid(lpcGUID
), ppobj
);
954 obj
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(IDirectMusicLoaderImpl
));
957 return E_OUTOFMEMORY
;
959 obj
->IDirectMusicLoader8_iface
.lpVtbl
= &DirectMusicLoader_Loader_Vtbl
;
960 obj
->ref
= 0; /* Will be inited with QueryInterface */
961 list_init(&obj
->cache
);
962 /* Caching is enabled by default for all classes */
963 obj
->cache_class
= ~0;
965 /* set default DLS collection (via SetObject... so that loading via DMUS_OBJ_OBJECT is possible) */
966 DM_STRUCT_INIT(&Desc
);
967 Desc
.dwValidData
= DMUS_OBJ_CLASS
| DMUS_OBJ_FILENAME
| DMUS_OBJ_FULLPATH
| DMUS_OBJ_OBJECT
;
968 Desc
.guidClass
= CLSID_DirectMusicCollection
;
969 Desc
.guidObject
= GUID_DefaultGMCollection
;
970 DMUSIC_GetDefaultGMPath (Desc
.wszFileName
);
971 IDirectMusicLoader_SetObject(&obj
->IDirectMusicLoader8_iface
, &Desc
);
972 /* and now the workaroundTM for "invalid" default DLS; basically,
973 my tests showed that if GUID chunk is present in default DLS
974 collection, loader treats it as "invalid" and returns
975 DMUS_E_LOADER_NOFILENAME for all requests for it; basically, we check
976 if out input guidObject was overwritten */
977 pEntry
= list_head(&obj
->cache
);
978 dls
= LIST_ENTRY(pEntry
, struct cache_entry
, entry
);
979 if (!IsEqualGUID(&Desc
.guidObject
, &GUID_DefaultGMCollection
)) {
980 dls
->bInvalidDefaultDLS
= TRUE
;
985 return IDirectMusicLoader_QueryInterface(&obj
->IDirectMusicLoader8_iface
, lpcGUID
, ppobj
);