2 * Copyright (C) 2003-2004 Rok Mandeljc
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 #include "dmloader_private.h"
21 WINE_DEFAULT_DEBUG_CHANNEL(dmloader
);
23 static const WCHAR
*system_default_gm_paths
[] =
25 L
"/usr/share/sounds/sf2/default-GM.sf2",
26 L
"/usr/share/soundfonts/default.sf2",
29 static HRESULT
get_system_default_gm_path(WCHAR
*path
, UINT max_len
)
33 for (i
= 0; i
< ARRAY_SIZE(system_default_gm_paths
); i
++)
35 swprintf(path
, max_len
, L
"\\??\\unix%s", system_default_gm_paths
[i
]);
36 if (GetFileAttributesW(path
) != INVALID_FILE_ATTRIBUTES
) break;
39 if (i
< ARRAY_SIZE(system_default_gm_paths
))
41 WARN("Using system %s for the default collection\n", debugstr_w(path
));
45 ERR("Unable to find system path, default collection will not be available\n");
46 return DMUS_E_LOADER_FAILEDOPEN
;
49 static const GUID
*classes
[] = {
50 &GUID_DirectMusicAllTypes
, /* Keep as first */
51 &CLSID_DirectMusicAudioPathConfig
,
52 &CLSID_DirectMusicBand
,
53 &CLSID_DirectMusicContainer
,
54 &CLSID_DirectMusicCollection
,
55 &CLSID_DirectMusicChordMap
,
56 &CLSID_DirectMusicSegment
,
57 &CLSID_DirectMusicScript
,
58 &CLSID_DirectMusicSong
,
59 &CLSID_DirectMusicStyle
,
60 &CLSID_DirectMusicGraph
,
61 &CLSID_DirectSoundWave
64 /* cache/alias entry */
68 IDirectMusicObject
*pObject
;
73 IDirectMusicLoader8 IDirectMusicLoader8_iface
;
75 WCHAR
*search_paths
[ARRAY_SIZE(classes
)];
76 unsigned int cache_class
;
80 static inline struct loader
*impl_from_IDirectMusicLoader8(IDirectMusicLoader8
*iface
)
82 return CONTAINING_RECORD(iface
, struct loader
, IDirectMusicLoader8_iface
);
85 static int index_from_class(REFCLSID
class)
89 for (i
= 0; i
< ARRAY_SIZE(classes
); i
++)
90 if (IsEqualGUID(class, classes
[i
]))
96 static inline BOOL
is_cache_enabled(struct loader
*This
, REFCLSID
class)
98 return !!(This
->cache_class
& 1 << index_from_class(class));
101 static void get_search_path(struct loader
*This
, REFGUID
class, WCHAR
*path
)
103 int index
= index_from_class(class);
105 if (index
< 0 || !This
->search_paths
[index
])
106 index
= 0; /* Default to GUID_DirectMusicAllTypes */
108 if (This
->search_paths
[index
])
109 lstrcpynW(path
, This
->search_paths
[index
], MAX_PATH
);
114 static HRESULT
DMUSIC_CopyDescriptor(DMUS_OBJECTDESC
*pDst
, DMUS_OBJECTDESC
*pSrc
)
116 if (TRACE_ON(dmloader
))
117 dump_DMUS_OBJECTDESC(pSrc
);
119 /* copy field by field */
120 if (pSrc
->dwValidData
& DMUS_OBJ_CLASS
) pDst
->guidClass
= pSrc
->guidClass
;
121 if (pSrc
->dwValidData
& DMUS_OBJ_OBJECT
) pDst
->guidObject
= pSrc
->guidObject
;
122 if (pSrc
->dwValidData
& DMUS_OBJ_DATE
) pDst
->ftDate
= pSrc
->ftDate
;
123 if (pSrc
->dwValidData
& DMUS_OBJ_VERSION
) pDst
->vVersion
= pSrc
->vVersion
;
124 if (pSrc
->dwValidData
& DMUS_OBJ_NAME
) lstrcpyW (pDst
->wszName
, pSrc
->wszName
);
125 if (pSrc
->dwValidData
& DMUS_OBJ_CATEGORY
) lstrcpyW (pDst
->wszCategory
, pSrc
->wszCategory
);
126 if (pSrc
->dwValidData
& DMUS_OBJ_FILENAME
) lstrcpyW (pDst
->wszFileName
, pSrc
->wszFileName
);
127 if (pSrc
->dwValidData
& DMUS_OBJ_STREAM
) IStream_Clone (pSrc
->pStream
, &pDst
->pStream
);
128 if (pSrc
->dwValidData
& DMUS_OBJ_MEMORY
) {
129 pDst
->pbMemData
= pSrc
->pbMemData
;
130 pDst
->llMemLength
= pSrc
->llMemLength
;
133 pDst
->dwValidData
|= pSrc
->dwValidData
;
137 static HRESULT WINAPI
loader_QueryInterface(IDirectMusicLoader8
*iface
, REFIID riid
, void **ppobj
)
139 struct loader
*This
= impl_from_IDirectMusicLoader8(iface
);
141 TRACE("(%p, %s, %p)\n",This
, debugstr_dmguid(riid
), ppobj
);
142 if (IsEqualIID (riid
, &IID_IUnknown
) ||
143 IsEqualIID (riid
, &IID_IDirectMusicLoader
) ||
144 IsEqualIID (riid
, &IID_IDirectMusicLoader8
)) {
145 IDirectMusicLoader_AddRef (iface
);
150 WARN(": not found\n");
151 return E_NOINTERFACE
;
154 static ULONG WINAPI
loader_AddRef(IDirectMusicLoader8
*iface
)
156 struct loader
*This
= impl_from_IDirectMusicLoader8(iface
);
157 ULONG ref
= InterlockedIncrement(&This
->ref
);
159 TRACE("(%p)->(): new ref = %lu\n", iface
, ref
);
164 static ULONG WINAPI
loader_Release(IDirectMusicLoader8
*iface
)
166 struct loader
*This
= impl_from_IDirectMusicLoader8(iface
);
167 ULONG ref
= InterlockedDecrement(&This
->ref
);
169 TRACE("(%p)->(): new ref = %lu\n", iface
, ref
);
174 IDirectMusicLoader8_ClearCache(iface
, &GUID_DirectMusicAllTypes
);
175 for (i
= 0; i
< ARRAY_SIZE(classes
); i
++)
176 free(This
->search_paths
[i
]);
183 static struct cache_entry
*find_cache_object(struct loader
*This
, DMUS_OBJECTDESC
*desc
)
185 struct cache_entry
*existing
;
188 * The Object is looked up in the following order.
192 * 4. DMUS_OBJ_FILENAME and DMUS_OBJ_FULLPATH
193 * 5. DMUS_OBJ_NAME and DMUS_OBJ_CATEGORY
195 * 7. DMUS_OBJ_FILENAME
198 if (desc
->dwValidData
& DMUS_OBJ_OBJECT
) {
199 LIST_FOR_EACH_ENTRY(existing
, &This
->cache
, struct cache_entry
, entry
) {
200 if (existing
->Desc
.dwValidData
& DMUS_OBJ_OBJECT
&&
201 IsEqualGUID(&desc
->guidObject
, &existing
->Desc
.guidObject
) ) {
202 TRACE("Found by DMUS_OBJ_OBJECT\n");
208 if (desc
->dwValidData
& DMUS_OBJ_STREAM
)
209 FIXME("Finding DMUS_OBJ_STREAM cached objects currently not supported.\n");
211 if (desc
->dwValidData
& DMUS_OBJ_MEMORY
) {
212 LIST_FOR_EACH_ENTRY(existing
, &This
->cache
, struct cache_entry
, entry
) {
213 if (existing
->Desc
.dwValidData
& DMUS_OBJ_MEMORY
&&
214 desc
->llMemLength
== existing
->Desc
.llMemLength
&&
215 (desc
->pbMemData
== existing
->Desc
.pbMemData
||
216 !memcmp(desc
->pbMemData
, existing
->Desc
.pbMemData
, desc
->llMemLength
)) ) {
217 TRACE("Found by DMUS_OBJ_MEMORY (%d)\n",
218 desc
->pbMemData
== existing
->Desc
.pbMemData
);
224 if ((desc
->dwValidData
& (DMUS_OBJ_FILENAME
| DMUS_OBJ_FULLPATH
)) ==
225 (DMUS_OBJ_FILENAME
| DMUS_OBJ_FULLPATH
)) {
226 LIST_FOR_EACH_ENTRY(existing
, &This
->cache
, struct cache_entry
, entry
) {
227 if ((existing
->Desc
.dwValidData
& (DMUS_OBJ_FILENAME
| DMUS_OBJ_FULLPATH
)) ==
228 (DMUS_OBJ_FILENAME
| DMUS_OBJ_FULLPATH
) &&
229 !wcsncmp(desc
->wszFileName
, existing
->Desc
.wszFileName
, DMUS_MAX_FILENAME
)) {
230 TRACE("Found by DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH\n");
236 if ((desc
->dwValidData
& (DMUS_OBJ_NAME
| DMUS_OBJ_CATEGORY
)) ==
237 (DMUS_OBJ_NAME
| DMUS_OBJ_CATEGORY
)) {
238 LIST_FOR_EACH_ENTRY(existing
, &This
->cache
, struct cache_entry
, entry
) {
239 if ((existing
->Desc
.dwValidData
& (DMUS_OBJ_NAME
| DMUS_OBJ_CATEGORY
)) ==
240 (DMUS_OBJ_NAME
| DMUS_OBJ_CATEGORY
) &&
241 !wcsncmp(desc
->wszName
, existing
->Desc
.wszName
, DMUS_MAX_NAME
) &&
242 !wcsncmp(desc
->wszCategory
, existing
->Desc
.wszCategory
, DMUS_MAX_CATEGORY
)) {
243 TRACE("Found by DMUS_OBJ_NAME | DMUS_OBJ_CATEGORY\n");
249 if ((desc
->dwValidData
& DMUS_OBJ_NAME
) == DMUS_OBJ_NAME
) {
250 LIST_FOR_EACH_ENTRY(existing
, &This
->cache
, struct cache_entry
, entry
) {
251 if ((existing
->Desc
.dwValidData
& DMUS_OBJ_NAME
) == DMUS_OBJ_NAME
&&
252 !wcsncmp(desc
->wszName
, existing
->Desc
.wszName
, DMUS_MAX_NAME
)) {
253 TRACE("Found by DMUS_OBJ_NAME\n");
259 if ((desc
->dwValidData
& DMUS_OBJ_FILENAME
) == DMUS_OBJ_FILENAME
) {
260 LIST_FOR_EACH_ENTRY(existing
, &This
->cache
, struct cache_entry
, entry
) {
261 if (((existing
->Desc
.dwValidData
& DMUS_OBJ_FILENAME
) == DMUS_OBJ_FILENAME
) &&
262 !wcsncmp(desc
->wszFileName
, existing
->Desc
.wszFileName
, DMUS_MAX_FILENAME
)) {
263 TRACE("Found by DMUS_OBJ_FILENAME\n");
272 static HRESULT WINAPI
loader_GetObject(IDirectMusicLoader8
*iface
, DMUS_OBJECTDESC
*pDesc
, REFIID riid
, void **ppv
)
274 static const DWORD source_flags
= DMUS_OBJ_STREAM
| DMUS_OBJ_FILENAME
| DMUS_OBJ_URL
| DMUS_OBJ_STREAM
;
275 struct loader
*This
= impl_from_IDirectMusicLoader8(iface
);
276 HRESULT result
= S_OK
;
277 HRESULT ret
= S_OK
; /* used at the end of function, to determine whether everything went OK */
278 struct cache_entry
*pExistingEntry
, *pObjectEntry
= NULL
;
280 IPersistStream
* pPersistStream
= NULL
;
282 LPDIRECTMUSICOBJECT pObject
;
283 DMUS_OBJECTDESC GotDesc
;
285 IStream
*loader_stream
;
288 TRACE("(%p)->(%p, %s, %p)\n", This
, pDesc
, debugstr_dmguid(riid
), ppv
);
290 if (TRACE_ON(dmloader
))
291 dump_DMUS_OBJECTDESC(pDesc
);
293 /* sometimes it happens that guidClass is missing... which is a BadThingTM */
294 if (!(pDesc
->dwValidData
& DMUS_OBJ_CLASS
)) {
295 ERR(": guidClass not valid but needed\n");
297 return DMUS_E_LOADER_NOCLASSID
;
300 /* Iterate through the list of objects we know about; these are either loaded
301 * (GetObject, LoadObjectFromFile) or set via SetObject; */
302 TRACE(": looking if we have object in the cache or if it can be found via alias\n");
303 pExistingEntry
= find_cache_object(This
, pDesc
);
304 if (pExistingEntry
) {
305 if (pExistingEntry
->Desc
.dwValidData
& DMUS_OBJ_LOADED
) {
306 TRACE(": already loaded\n");
307 return IDirectMusicObject_QueryInterface(pExistingEntry
->pObject
, riid
, ppv
);
310 TRACE(": not loaded yet\n");
311 pObjectEntry
= pExistingEntry
;
314 /* basically, if we found alias, we use its descriptor to load...
315 else we use info we were given */
317 TRACE(": found alias entry for requested object... using stored info\n");
318 /* I think in certain cases it can happen that entry's descriptor lacks info about
319 where to load from (e.g.: if we loaded from stream and then released object
320 from cache; then only its CLSID, GUID and perhaps name are left); so just
321 overwrite whatever info the entry has (since it ought to be 100% correct) */
322 DMUSIC_CopyDescriptor (pDesc
, &pObjectEntry
->Desc
);
323 /*pDesc = &pObjectEntry->Desc; */ /* FIXME: is this OK? */
325 TRACE(": no cache/alias entry found for requested object\n");
328 if (pDesc
->dwValidData
& DMUS_OBJ_URL
)
330 TRACE(": loading from URLs not supported yet\n");
331 return DMUS_E_LOADER_FORMATNOTSUPPORTED
;
333 else if (pDesc
->dwValidData
& DMUS_OBJ_FILENAME
)
335 WCHAR file_name
[MAX_PATH
];
337 if (pDesc
->dwValidData
& DMUS_OBJ_FULLPATH
)
338 lstrcpyW(file_name
, pDesc
->wszFileName
);
342 get_search_path(This
, &pDesc
->guidClass
, file_name
);
343 p
= file_name
+ lstrlenW(file_name
);
344 if (p
> file_name
&& p
[-1] != '\\') *p
++ = '\\';
345 lstrcpyW(p
, pDesc
->wszFileName
);
348 TRACE(": loading from file (%s)\n", debugstr_w(file_name
));
349 if (FAILED(hr
= file_stream_create(file_name
, &pStream
))) return hr
;
351 else if (pDesc
->dwValidData
& DMUS_OBJ_MEMORY
) {
352 /* load object from resource */
353 TRACE(": loading from resource\n");
354 /* create stream and associate it with given resource */
355 result
= DMUSIC_CreateDirectMusicLoaderResourceStream ((LPVOID
*)&pStream
);
356 if (FAILED(result
)) {
357 ERR(": could not create resource stream\n");
360 result
= IDirectMusicLoaderResourceStream_Attach(pStream
, pDesc
->pbMemData
, pDesc
->llMemLength
, 0);
363 ERR(": could not attach stream to resource\n");
364 IStream_Release (pStream
);
368 else if (pDesc
->dwValidData
& DMUS_OBJ_STREAM
)
370 pStream
= pDesc
->pStream
;
371 IStream_AddRef(pStream
);
375 FIXME(": unknown/unsupported way of loading\n");
376 return DMUS_E_LOADER_NOFILENAME
;
379 if (FAILED(hr
= loader_stream_create((IDirectMusicLoader
*)iface
, pStream
, &loader_stream
)))
381 IStream_Release(pStream
);
382 pStream
= loader_stream
;
385 result
= CoCreateInstance (&pDesc
->guidClass
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IDirectMusicObject
, (LPVOID
*)&pObject
);
386 if (FAILED(result
)) {
387 IStream_Release(pStream
);
388 ERR(": could not create object\n");
391 /* acquire PersistStream interface */
392 result
= IDirectMusicObject_QueryInterface (pObject
, &IID_IPersistStream
, (LPVOID
*)&pPersistStream
);
393 if (FAILED(result
)) {
394 IStream_Release(pStream
);
395 IDirectMusicObject_Release(pObject
);
396 ERR("failed to Query\n");
400 result
= IPersistStream_Load (pPersistStream
, pStream
);
401 if (result
!= S_OK
) {
402 IStream_Release(pStream
);
403 IPersistStream_Release(pPersistStream
);
404 IDirectMusicObject_Release(pObject
);
405 WARN(": failed to (completely) load object (%#lx)\n", result
);
409 DM_STRUCT_INIT(&GotDesc
);
410 result
= IDirectMusicObject_GetDescriptor (pObject
, &GotDesc
);
411 /* set filename (if we loaded via filename) */
412 if (pDesc
->dwValidData
& DMUS_OBJ_FILENAME
) {
413 GotDesc
.dwValidData
|= (pDesc
->dwValidData
& (DMUS_OBJ_FILENAME
| DMUS_OBJ_FULLPATH
));
414 lstrcpyW (GotDesc
.wszFileName
, pDesc
->wszFileName
);
416 if (FAILED(result
)) {
417 IStream_Release(pStream
);
418 IPersistStream_Release(pPersistStream
);
419 IDirectMusicObject_Release(pObject
);
420 ERR(": failed to get descriptor\n");
423 /* release all loading related stuff */
424 IStream_Release (pStream
);
425 IPersistStream_Release (pPersistStream
);
427 /* add object to cache/overwrite existing info (if cache is enabled) */
428 bCache
= is_cache_enabled(This
, &pDesc
->guidClass
);
429 if (!bCache
) TRACE(": caching disabled\n");
430 else if ((pDesc
->dwValidData
& source_flags
) == DMUS_OBJ_STREAM
)
432 FIXME("Skipping cache for DMUS_OBJ_STREAM object\n");
439 pObjectEntry
= calloc(1, sizeof(*pObjectEntry
));
440 DM_STRUCT_INIT(&pObjectEntry
->Desc
);
441 DMUSIC_CopyDescriptor (&pObjectEntry
->Desc
, &GotDesc
);
442 pObjectEntry
->pObject
= pObject
;
443 list_add_head(&This
->cache
, &pObjectEntry
->entry
);
447 DMUSIC_CopyDescriptor (&pObjectEntry
->Desc
, &GotDesc
);
448 pObjectEntry
->pObject
= pObject
;
450 TRACE(": filled in cache entry\n");
453 result
= IDirectMusicObject_QueryInterface (pObject
, riid
, ppv
);
454 if (!bCache
) IDirectMusicObject_Release (pObject
); /* since loader's reference is not needed */
455 /* if there was trouble with loading, and if no other error occurred,
456 we should return DMUS_S_PARTIALLOAD; else, error is returned */
463 static HRESULT WINAPI
loader_SetObject(IDirectMusicLoader8
*iface
, DMUS_OBJECTDESC
*pDesc
)
465 struct loader
*This
= impl_from_IDirectMusicLoader8(iface
);
467 LPDIRECTMUSICOBJECT pObject
;
468 DMUS_OBJECTDESC Desc
;
469 struct cache_entry
*pObjectEntry
, *pNewEntry
;
470 IStream
*loader_stream
;
473 TRACE("(%p)->(%p)\n", This
, pDesc
);
475 if (TRACE_ON(dmloader
))
476 dump_DMUS_OBJECTDESC(pDesc
);
478 if (pDesc
->dwValidData
& DMUS_OBJ_FILENAME
)
480 WCHAR file_name
[MAX_PATH
];
482 if (pDesc
->dwValidData
& DMUS_OBJ_FULLPATH
)
483 lstrcpyW(file_name
, pDesc
->wszFileName
);
487 get_search_path(This
, &pDesc
->guidClass
, file_name
);
488 p
= file_name
+ lstrlenW(file_name
);
489 if (p
> file_name
&& p
[-1] != '\\') *p
++ = '\\';
490 lstrcpyW(p
, pDesc
->wszFileName
);
493 if (!wcsicmp(file_name
, L
"C:\\windows\\system32\\drivers\\gm.dls")
494 && GetFileAttributesW(file_name
) == INVALID_FILE_ATTRIBUTES
)
496 hr
= get_system_default_gm_path(file_name
, ARRAY_SIZE(file_name
));
497 if (FAILED(hr
)) return hr
;
500 if (FAILED(hr
= file_stream_create(file_name
, &pStream
))) return hr
;
502 else if (pDesc
->dwValidData
& DMUS_OBJ_STREAM
)
504 pStream
= pDesc
->pStream
;
505 IStream_AddRef(pStream
);
507 else if (pDesc
->dwValidData
& DMUS_OBJ_MEMORY
) {
509 hr
= DMUSIC_CreateDirectMusicLoaderResourceStream ((LPVOID
*)&pStream
);
511 ERR(": could not create resource stream\n");
512 return DMUS_E_LOADER_FAILEDOPEN
;
515 hr
= IDirectMusicLoaderResourceStream_Attach(pStream
, pDesc
->pbMemData
, pDesc
->llMemLength
, 0);
518 ERR(": could not attach stream to resource\n");
519 IStream_Release (pStream
);
520 return DMUS_E_LOADER_FAILEDOPEN
;
524 ERR(": no way to get additional info\n");
525 return DMUS_E_LOADER_FAILEDOPEN
;
528 if (FAILED(hr
= loader_stream_create((IDirectMusicLoader
*)iface
, pStream
, &loader_stream
)))
530 IStream_Release(pStream
);
531 pStream
= loader_stream
;
534 hr
= CoCreateInstance (&pDesc
->guidClass
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IDirectMusicObject
, (LPVOID
*)&pObject
);
536 IStream_Release(pStream
);
537 ERR("Object creation of %s failed %#lx\n", debugstr_guid(&pDesc
->guidClass
),hr
);
538 return DMUS_E_LOADER_FAILEDOPEN
;
541 DM_STRUCT_INIT(&Desc
);
542 if (FAILED(IDirectMusicObject_ParseDescriptor (pObject
, pStream
, &Desc
))) {
543 IStream_Release(pStream
);
544 IDirectMusicObject_Release(pObject
);
545 ERR(": couldn't parse descriptor\n");
546 return DMUS_E_LOADER_FORMATNOTSUPPORTED
;
549 /* copy elements from parsed descriptor into input descriptor; this sets new info, overwriting if necessary,
550 but leaves info that's provided by input and not available from stream */
551 DMUSIC_CopyDescriptor (pDesc
, &Desc
);
553 /* release everything */
554 IDirectMusicObject_Release (pObject
);
555 IStream_Release (pStream
);
557 /* sometimes it happens that twisted programs call SetObject for same object twice...
558 in such cases, native loader returns S_OK and does nothing... a sound plan */
559 LIST_FOR_EACH_ENTRY(pObjectEntry
, &This
->cache
, struct cache_entry
, entry
) {
560 if (!memcmp (&pObjectEntry
->Desc
, pDesc
, sizeof(DMUS_OBJECTDESC
))) {
561 TRACE(": exactly same entry already exists\n");
567 TRACE(": adding alias entry with following info:\n");
568 if (TRACE_ON(dmloader
))
569 dump_DMUS_OBJECTDESC(pDesc
);
570 pNewEntry
= calloc(1, sizeof(*pNewEntry
));
571 /* use this function instead of pure memcpy due to streams (memcpy just copies pointer),
572 which is basically used further by app that called SetDescriptor... better safety than exception */
573 DMUSIC_CopyDescriptor (&pNewEntry
->Desc
, pDesc
);
574 list_add_head(&This
->cache
, &pNewEntry
->entry
);
579 static HRESULT WINAPI
loader_SetSearchDirectory(IDirectMusicLoader8
*iface
,
580 REFGUID
class, WCHAR
*path
, BOOL clear
)
582 struct loader
*This
= impl_from_IDirectMusicLoader8(iface
);
583 int index
= index_from_class(class);
586 TRACE("(%p, %s, %s, %d)\n", This
, debugstr_dmguid(class), debugstr_w(path
), clear
);
592 attr
= GetFileAttributesW(path
);
593 if (attr
== INVALID_FILE_ATTRIBUTES
|| !(attr
& FILE_ATTRIBUTE_DIRECTORY
))
594 return DMUS_E_LOADER_BADPATH
;
598 FIXME("clear flag ignored\n");
600 /* Ignore invalid GUIDs */
604 if (!This
->search_paths
[index
])
605 This
->search_paths
[index
] = malloc(MAX_PATH
);
606 else if (!wcsncmp(This
->search_paths
[index
], path
, MAX_PATH
))
609 lstrcpynW(This
->search_paths
[index
], path
, MAX_PATH
);
614 static HRESULT WINAPI
loader_ScanDirectory(IDirectMusicLoader8
*iface
, REFGUID rguidClass
, WCHAR
*pwzFileExtension
, WCHAR
*pwzScanFileName
)
616 struct loader
*This
= impl_from_IDirectMusicLoader8(iface
);
617 WIN32_FIND_DATAW FileData
;
619 WCHAR wszSearchString
[MAX_PATH
];
622 TRACE("(%p, %s, %s, %s)\n", This
, debugstr_dmguid(rguidClass
), debugstr_w(pwzFileExtension
),
623 debugstr_w(pwzScanFileName
));
624 if (index_from_class(rguidClass
) <= 0) {
625 ERR(": rguidClass invalid CLSID\n");
626 return REGDB_E_CLASSNOTREG
;
629 if (!pwzFileExtension
)
632 /* get search path for given class */
633 get_search_path(This
, rguidClass
, wszSearchString
);
635 p
= wszSearchString
+ lstrlenW(wszSearchString
);
636 if (p
> wszSearchString
&& p
[-1] != '\\') *p
++ = '\\';
637 *p
++ = '*'; /* any file */
638 if (lstrcmpW (pwzFileExtension
, L
"*"))
639 *p
++ = '.'; /* if we have actual extension, put a dot */
640 lstrcpyW (p
, pwzFileExtension
);
642 TRACE(": search string: %s\n", debugstr_w(wszSearchString
));
644 hSearch
= FindFirstFileW (wszSearchString
, &FileData
);
645 if (hSearch
== INVALID_HANDLE_VALUE
) {
646 TRACE(": no files found\n");
651 DMUS_OBJECTDESC Desc
;
652 DM_STRUCT_INIT(&Desc
);
653 Desc
.dwValidData
= DMUS_OBJ_CLASS
| DMUS_OBJ_FILENAME
| DMUS_OBJ_DATE
;
654 Desc
.guidClass
= *rguidClass
;
655 lstrcpyW (Desc
.wszFileName
, FileData
.cFileName
);
656 FileTimeToLocalFileTime (&FileData
.ftCreationTime
, &Desc
.ftDate
);
657 IDirectMusicLoader8_SetObject (iface
, &Desc
);
659 if (!FindNextFileW (hSearch
, &FileData
)) {
660 if (GetLastError () == ERROR_NO_MORE_FILES
) {
661 TRACE(": search completed\n");
664 ERR(": could not get next file\n");
673 static HRESULT WINAPI
loader_CacheObject(IDirectMusicLoader8
*iface
,
674 IDirectMusicObject
*object
)
676 struct loader
*This
= impl_from_IDirectMusicLoader8(iface
);
677 DMUS_OBJECTDESC desc
;
678 struct cache_entry
*entry
;
680 TRACE("(%p, %p)\n", This
, object
);
682 DM_STRUCT_INIT(&desc
);
683 IDirectMusicObject_GetDescriptor(object
, &desc
);
685 /* Iterate through the list and check if we have an alias (without object), corresponding
686 to the descriptor of the input object */
687 entry
= find_cache_object(This
, &desc
);
689 if ((entry
->Desc
.dwValidData
& DMUS_OBJ_LOADED
) && entry
->pObject
) {
690 TRACE("Object already loaded.\n");
694 entry
->Desc
.dwValidData
|= DMUS_OBJ_LOADED
;
695 entry
->pObject
= object
;
696 IDirectMusicObject_AddRef(entry
->pObject
);
700 return DMUS_E_LOADER_OBJECTNOTFOUND
;
703 static HRESULT WINAPI
loader_ReleaseObject(IDirectMusicLoader8
*iface
,
704 IDirectMusicObject
*object
)
706 struct loader
*This
= impl_from_IDirectMusicLoader8(iface
);
707 DMUS_OBJECTDESC desc
;
708 struct cache_entry
*entry
;
710 TRACE("(%p, %p)\n", This
, object
);
715 DM_STRUCT_INIT(&desc
);
716 IDirectMusicObject_GetDescriptor(object
, &desc
);
718 TRACE("Looking for the object in cache\n");
719 entry
= find_cache_object(This
, &desc
);
721 dump_DMUS_OBJECTDESC(&entry
->Desc
);
723 if (entry
->pObject
&& entry
->Desc
.dwValidData
& DMUS_OBJ_LOADED
) {
724 IDirectMusicObject_Release(entry
->pObject
);
725 entry
->pObject
= NULL
;
726 entry
->Desc
.dwValidData
&= ~DMUS_OBJ_LOADED
;
734 static HRESULT WINAPI
loader_ClearCache(IDirectMusicLoader8
*iface
, REFGUID
class)
736 struct loader
*This
= impl_from_IDirectMusicLoader8(iface
);
737 struct cache_entry
*obj
, *obj2
;
739 TRACE("(%p, %s)\n", This
, debugstr_dmguid(class));
741 LIST_FOR_EACH_ENTRY_SAFE(obj
, obj2
, &This
->cache
, struct cache_entry
, entry
) {
742 if ((IsEqualGUID(class, &GUID_DirectMusicAllTypes
) || IsEqualGUID(class, &obj
->Desc
.guidClass
)) &&
743 (obj
->Desc
.dwValidData
& DMUS_OBJ_LOADED
)) {
744 /* basically, wrap to ReleaseObject for each object found */
745 IDirectMusicLoader8_ReleaseObject(iface
, obj
->pObject
);
746 list_remove(&obj
->entry
);
754 static HRESULT WINAPI
loader_EnableCache(IDirectMusicLoader8
*iface
, REFGUID
class,
757 struct loader
*This
= impl_from_IDirectMusicLoader8(iface
);
760 TRACE("(%p, %s, %d)\n", This
, debugstr_dmguid(class), enable
);
762 current
= is_cache_enabled(This
, class);
764 if (IsEqualGUID(class, &GUID_DirectMusicAllTypes
))
765 This
->cache_class
= enable
? ~0 : 0;
767 int idx
= index_from_class(class);
768 if (idx
== -1) return S_FALSE
;
770 This
->cache_class
|= 1 << idx
;
772 This
->cache_class
&= ~(1 << idx
);
776 IDirectMusicLoader8_ClearCache(iface
, class);
778 if (current
== enable
)
784 static HRESULT WINAPI
loader_EnumObject(IDirectMusicLoader8
*iface
, REFGUID rguidClass
, DWORD dwIndex
, DMUS_OBJECTDESC
*pDesc
)
786 struct loader
*This
= impl_from_IDirectMusicLoader8(iface
);
788 struct cache_entry
*pObjectEntry
;
789 TRACE("(%p, %s, %ld, %p)\n", This
, debugstr_dmguid(rguidClass
), dwIndex
, pDesc
);
791 DM_STRUCT_INIT(pDesc
);
793 LIST_FOR_EACH_ENTRY(pObjectEntry
, &This
->cache
, struct cache_entry
, entry
) {
794 if (IsEqualGUID (rguidClass
, &GUID_DirectMusicAllTypes
) || IsEqualGUID (rguidClass
, &pObjectEntry
->Desc
.guidClass
)) {
795 if (dwCount
== dwIndex
) {
796 *pDesc
= pObjectEntry
->Desc
;
797 /* we aren't supposed to reveal this info */
798 pDesc
->dwValidData
&= ~(DMUS_OBJ_MEMORY
| DMUS_OBJ_STREAM
);
799 pDesc
->pbMemData
= NULL
;
800 pDesc
->llMemLength
= 0;
801 pDesc
->pStream
= NULL
;
808 TRACE(": not found\n");
812 static void WINAPI
loader_CollectGarbage(IDirectMusicLoader8
*iface
)
814 FIXME("(%p)->(): stub\n", iface
);
817 static HRESULT WINAPI
loader_ReleaseObjectByUnknown(IDirectMusicLoader8
*iface
, IUnknown
*pObject
)
819 struct loader
*This
= impl_from_IDirectMusicLoader8(iface
);
821 LPDIRECTMUSICOBJECT pObjectInterface
;
823 TRACE("(%p, %p)\n", This
, pObject
);
825 if (IsBadReadPtr (pObject
, sizeof(*pObject
))) {
826 ERR(": pObject bad write pointer\n");
829 /* we simply get IDirectMusicObject interface */
830 result
= IUnknown_QueryInterface (pObject
, &IID_IDirectMusicObject
, (LPVOID
*)&pObjectInterface
);
831 if (FAILED(result
)) return result
;
832 /* and release it in old-fashioned way */
833 result
= IDirectMusicLoader8_ReleaseObject (iface
, pObjectInterface
);
834 IDirectMusicObject_Release (pObjectInterface
);
839 static HRESULT WINAPI
loader_LoadObjectFromFile(IDirectMusicLoader8
*iface
, REFGUID rguidClassID
, REFIID iidInterfaceID
, WCHAR
*pwzFilePath
, void **ppObject
)
841 struct loader
*This
= impl_from_IDirectMusicLoader8(iface
);
842 DMUS_OBJECTDESC ObjDesc
;
843 WCHAR wszLoaderSearchPath
[MAX_PATH
];
845 TRACE("(%p, %s, %s, %s, %p): wrapping to loader_GetObject\n", This
, debugstr_dmguid(rguidClassID
), debugstr_dmguid(iidInterfaceID
), debugstr_w(pwzFilePath
), ppObject
);
847 DM_STRUCT_INIT(&ObjDesc
);
848 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 */
849 ObjDesc
.guidClass
= *rguidClassID
;
850 /* OK, MSDN says that search order is the following:
851 - current directory (DONE)
852 - windows search path (FIXME: how do I get that?)
853 - loader's search path (DONE)
855 get_search_path(This
, rguidClassID
, wszLoaderSearchPath
);
856 /* search in current directory */
857 if (!SearchPathW(NULL
, pwzFilePath
, NULL
, ARRAY_SIZE(ObjDesc
.wszFileName
), ObjDesc
.wszFileName
, NULL
) &&
858 /* search in loader's search path */
859 !SearchPathW(wszLoaderSearchPath
, pwzFilePath
, NULL
, ARRAY_SIZE(ObjDesc
.wszFileName
), ObjDesc
.wszFileName
, NULL
)) {
860 /* cannot find file */
861 TRACE(": cannot find file\n");
862 return DMUS_E_LOADER_FAILEDOPEN
;
865 TRACE(": full file path = %s\n", debugstr_w (ObjDesc
.wszFileName
));
867 return IDirectMusicLoader_GetObject(iface
, &ObjDesc
, iidInterfaceID
, ppObject
);
870 static const IDirectMusicLoader8Vtbl loader_vtbl
=
872 loader_QueryInterface
,
877 loader_SetSearchDirectory
,
878 loader_ScanDirectory
,
880 loader_ReleaseObject
,
884 loader_CollectGarbage
,
885 loader_ReleaseObjectByUnknown
,
886 loader_LoadObjectFromFile
,
889 static HRESULT
get_default_gm_path(WCHAR
*path
, DWORD max_len
)
894 if (!(ret
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
, L
"Software\\Microsoft\\DirectMusic" , 0, KEY_READ
, &hkey
)))
896 DWORD type
, size
= max_len
* sizeof(WCHAR
);
897 ret
= RegQueryValueExW(hkey
, L
"GMFilePath", NULL
, &type
, (BYTE
*)path
, &size
);
900 if (!ret
&& GetFileAttributesW(path
) != INVALID_FILE_ATTRIBUTES
) return S_OK
;
903 if (!ret
) WARN("Failed to find %s, using system fallbacks\n", debugstr_w(path
));
904 else WARN("Failed to open GMFilePath registry key, using system fallbacks\n");
906 return get_system_default_gm_path(path
, max_len
);
909 /* for ClassFactory */
910 HRESULT
create_dmloader(REFIID lpcGUID
, void **ppobj
)
913 DMUS_OBJECTDESC Desc
;
916 TRACE("(%s, %p)\n", debugstr_dmguid(lpcGUID
), ppobj
);
919 if (!(obj
= calloc(1, sizeof(*obj
)))) return E_OUTOFMEMORY
;
920 obj
->IDirectMusicLoader8_iface
.lpVtbl
= &loader_vtbl
;
922 list_init(&obj
->cache
);
923 /* Caching is enabled by default for all classes */
924 obj
->cache_class
= ~0;
926 /* set default DLS collection (via SetObject... so that loading via DMUS_OBJ_OBJECT is possible) */
927 DM_STRUCT_INIT(&Desc
);
928 Desc
.dwValidData
= DMUS_OBJ_CLASS
| DMUS_OBJ_FILENAME
| DMUS_OBJ_FULLPATH
| DMUS_OBJ_OBJECT
;
929 Desc
.guidClass
= CLSID_DirectMusicCollection
;
930 Desc
.guidObject
= GUID_DefaultGMCollection
;
931 if (SUCCEEDED(hr
= get_default_gm_path(Desc
.wszFileName
, ARRAY_SIZE(Desc
.wszFileName
))))
932 hr
= IDirectMusicLoader_SetObject(&obj
->IDirectMusicLoader8_iface
, &Desc
);
933 if (FAILED(hr
)) WARN("Failed to load the default collection, hr %#lx\n", hr
);
935 hr
= IDirectMusicLoader_QueryInterface(&obj
->IDirectMusicLoader8_iface
, lpcGUID
, ppobj
);
936 IDirectMusicLoader_Release(&obj
->IDirectMusicLoader8_iface
);