include/mscvpdb.h: Use flexible array members for the rest of structures.
[wine.git] / dlls / dmloader / loader.c
blob2ebbbe7ffca05cacb7c6c0e8d1d6547334ce0090
1 /*
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)
31 UINT i;
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));
42 return S_OK;
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 */
65 struct cache_entry {
66 struct list entry;
67 DMUS_OBJECTDESC Desc;
68 IDirectMusicObject *pObject;
71 struct loader
73 IDirectMusicLoader8 IDirectMusicLoader8_iface;
74 LONG ref;
75 WCHAR *search_paths[ARRAY_SIZE(classes)];
76 unsigned int cache_class;
77 struct list cache;
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)
87 int i;
89 for (i = 0; i < ARRAY_SIZE(classes); i++)
90 if (IsEqualGUID(class, classes[i]))
91 return i;
93 return -1;
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);
110 else
111 path[0] = 0;
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;
132 /* set flags */
133 pDst->dwValidData |= pSrc->dwValidData;
134 return S_OK;
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);
146 *ppobj = This;
147 return S_OK;
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);
161 return 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);
171 if (!ref) {
172 unsigned int i;
174 IDirectMusicLoader8_ClearCache(iface, &GUID_DirectMusicAllTypes);
175 for (i = 0; i < ARRAY_SIZE(classes); i++)
176 free(This->search_paths[i]);
177 free(This);
180 return ref;
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.
189 * 1. DMUS_OBJ_OBJECT
190 * 2. DMUS_OBJ_STREAM
191 * 3. DMUS_OBJ_MEMORY
192 * 4. DMUS_OBJ_FILENAME and DMUS_OBJ_FULLPATH
193 * 5. DMUS_OBJ_NAME and DMUS_OBJ_CATEGORY
194 * 6. DMUS_OBJ_NAME
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");
203 return existing;
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);
219 return existing;
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");
231 return existing;
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");
244 return existing;
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");
254 return existing;
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");
264 return existing;
269 return NULL;
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;
279 LPSTREAM pStream;
280 IPersistStream* pPersistStream = NULL;
282 LPDIRECTMUSICOBJECT pObject;
283 DMUS_OBJECTDESC GotDesc;
284 BOOL bCache;
285 IStream *loader_stream;
286 HRESULT hr;
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");
296 *ppv = NULL;
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 */
316 if (pObjectEntry) {
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? */
324 } else {
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);
339 else
341 WCHAR *p;
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");
358 return result;
360 result = IDirectMusicLoaderResourceStream_Attach(pStream, pDesc->pbMemData, pDesc->llMemLength, 0);
361 if (FAILED(result))
363 ERR(": could not attach stream to resource\n");
364 IStream_Release (pStream);
365 return result;
368 else if (pDesc->dwValidData & DMUS_OBJ_STREAM)
370 pStream = pDesc->pStream;
371 IStream_AddRef(pStream);
373 else
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)))
380 return hr;
381 IStream_Release(pStream);
382 pStream = loader_stream;
384 /* create object */
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");
389 return result;
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");
397 return result;
399 /* load */
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);
406 return result;
408 /* get descriptor */
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");
421 return result;
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");
433 bCache = FALSE;
435 else
437 if (!pObjectEntry)
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);
445 else
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 */
457 if (result == S_OK)
458 return ret;
459 else
460 return result;
463 static HRESULT WINAPI loader_SetObject(IDirectMusicLoader8 *iface, DMUS_OBJECTDESC *pDesc)
465 struct loader *This = impl_from_IDirectMusicLoader8(iface);
466 LPSTREAM pStream;
467 LPDIRECTMUSICOBJECT pObject;
468 DMUS_OBJECTDESC Desc;
469 struct cache_entry *pObjectEntry, *pNewEntry;
470 IStream *loader_stream;
471 HRESULT hr;
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);
484 else
486 WCHAR *p;
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) {
508 /* create stream */
509 hr = DMUSIC_CreateDirectMusicLoaderResourceStream ((LPVOID*)&pStream);
510 if (FAILED(hr)) {
511 ERR(": could not create resource stream\n");
512 return DMUS_E_LOADER_FAILEDOPEN;
514 /* attach stream */
515 hr = IDirectMusicLoaderResourceStream_Attach(pStream, pDesc->pbMemData, pDesc->llMemLength, 0);
516 if (FAILED(hr))
518 ERR(": could not attach stream to resource\n");
519 IStream_Release (pStream);
520 return DMUS_E_LOADER_FAILEDOPEN;
523 else {
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)))
529 return hr;
530 IStream_Release(pStream);
531 pStream = loader_stream;
533 /* create object */
534 hr = CoCreateInstance (&pDesc->guidClass, NULL, CLSCTX_INPROC_SERVER, &IID_IDirectMusicObject, (LPVOID*)&pObject);
535 if (FAILED(hr)) {
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");
562 return S_OK;
566 /* add new entry */
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);
576 return S_OK;
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);
584 DWORD attr;
586 TRACE("(%p, %s, %s, %d)\n", This, debugstr_dmguid(class), debugstr_w(path), clear);
588 if (!path)
589 return E_POINTER;
591 if (path[0]) {
592 attr = GetFileAttributesW(path);
593 if (attr == INVALID_FILE_ATTRIBUTES || !(attr & FILE_ATTRIBUTE_DIRECTORY))
594 return DMUS_E_LOADER_BADPATH;
597 if (clear)
598 FIXME("clear flag ignored\n");
600 /* Ignore invalid GUIDs */
601 if (index < 0)
602 return S_OK;
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))
607 return S_FALSE;
609 lstrcpynW(This->search_paths[index], path, MAX_PATH);
611 return S_OK;
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;
618 HANDLE hSearch;
619 WCHAR wszSearchString[MAX_PATH];
620 WCHAR *p;
621 HRESULT result;
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)
630 return S_FALSE;
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");
647 return S_FALSE;
650 do {
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");
662 result = S_OK;
663 } else {
664 ERR(": could not get next file\n");
665 result = E_FAIL;
667 FindClose (hSearch);
668 return result;
670 } while (1);
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);
688 if (entry) {
689 if ((entry->Desc.dwValidData & DMUS_OBJ_LOADED) && entry->pObject) {
690 TRACE("Object already loaded.\n");
691 return S_FALSE;
694 entry->Desc.dwValidData |= DMUS_OBJ_LOADED;
695 entry->pObject = object;
696 IDirectMusicObject_AddRef(entry->pObject);
697 return S_OK;
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);
712 if (!object)
713 return E_POINTER;
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);
720 if (entry) {
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;
727 return S_OK;
731 return S_FALSE;
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);
747 free(obj);
751 return S_OK;
754 static HRESULT WINAPI loader_EnableCache(IDirectMusicLoader8 *iface, REFGUID class,
755 BOOL enable)
757 struct loader *This = impl_from_IDirectMusicLoader8(iface);
758 BOOL current;
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;
766 else {
767 int idx = index_from_class(class);
768 if (idx == -1) return S_FALSE;
769 if (enable)
770 This->cache_class |= 1 << idx;
771 else
772 This->cache_class &= ~(1 << idx);
775 if (!enable)
776 IDirectMusicLoader8_ClearCache(iface, class);
778 if (current == enable)
779 return S_FALSE;
781 return S_OK;
784 static HRESULT WINAPI loader_EnumObject(IDirectMusicLoader8 *iface, REFGUID rguidClass, DWORD dwIndex, DMUS_OBJECTDESC *pDesc)
786 struct loader *This = impl_from_IDirectMusicLoader8(iface);
787 DWORD dwCount = 0;
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;
802 return S_OK;
804 dwCount++;
808 TRACE(": not found\n");
809 return S_FALSE;
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);
820 HRESULT result;
821 LPDIRECTMUSICOBJECT pObjectInterface;
823 TRACE("(%p, %p)\n", This, pObject);
825 if (IsBadReadPtr (pObject, sizeof(*pObject))) {
826 ERR(": pObject bad write pointer\n");
827 return E_POINTER;
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);
836 return result;
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,
873 loader_AddRef,
874 loader_Release,
875 loader_GetObject,
876 loader_SetObject,
877 loader_SetSearchDirectory,
878 loader_ScanDirectory,
879 loader_CacheObject,
880 loader_ReleaseObject,
881 loader_ClearCache,
882 loader_EnableCache,
883 loader_EnumObject,
884 loader_CollectGarbage,
885 loader_ReleaseObjectByUnknown,
886 loader_LoadObjectFromFile,
889 static HRESULT get_default_gm_path(WCHAR *path, DWORD max_len)
891 DWORD ret;
892 HKEY hkey;
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);
898 RegCloseKey(hkey);
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)
912 struct loader *obj;
913 DMUS_OBJECTDESC Desc;
914 HRESULT hr;
916 TRACE("(%s, %p)\n", debugstr_dmguid(lpcGUID), ppobj);
918 *ppobj = NULL;
919 if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY;
920 obj->IDirectMusicLoader8_iface.lpVtbl = &loader_vtbl;
921 obj->ref = 1;
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);
937 return hr;