d3d11: Use wined3d_device_context_draw().
[wine.git] / dlls / dmloader / loader.c
blobb90584b09a063c2380009d043844d0207cb6e50d
1 /*
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 */
41 struct cache_entry {
42 struct list entry;
43 DMUS_OBJECTDESC Desc;
44 IDirectMusicObject *pObject;
45 BOOL bInvalidDefaultDLS; /* workaround for enabling caching of "faulty" default dls collection */
48 typedef struct IDirectMusicLoaderImpl {
49 IDirectMusicLoader8 IDirectMusicLoader8_iface;
50 LONG ref;
51 WCHAR *search_paths[ARRAY_SIZE(classes)];
52 unsigned int cache_class;
53 struct list cache;
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)
64 int i;
66 for (i = 0; i < ARRAY_SIZE(classes); i++)
67 if (IsEqualGUID(class, classes[i]))
68 return i;
70 return -1;
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);
87 else
88 path[0] = 0;
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) lstrcpyW (pDst->wszName, pSrc->wszName);
102 if (pSrc->dwValidData & DMUS_OBJ_CATEGORY) lstrcpyW (pDst->wszCategory, pSrc->wszCategory);
103 if (pSrc->dwValidData & DMUS_OBJ_FILENAME) lstrcpyW (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;
109 /* set flags */
110 pDst->dwValidData |= pSrc->dwValidData;
111 return S_OK;
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);
128 *ppobj = This;
129 return S_OK;
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);
143 return 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);
153 if (!ref) {
154 unsigned int i;
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);
160 unlock_module();
163 return ref;
166 static struct cache_entry *find_cache_object(IDirectMusicLoaderImpl *This, DMUS_OBJECTDESC *desc)
168 struct cache_entry *existing;
171 * The Object is looked up in the following order.
172 * 1. DMUS_OBJ_OBJECT
173 * 2. DMUS_OBJ_STREAM
174 * 3. DMUS_OBJ_MEMORY
175 * 4. DMUS_OBJ_FILENAME and DMUS_OBJ_FULLPATH
176 * 5. DMUS_OBJ_NAME and DMUS_OBJ_CATEGORY
177 * 6. DMUS_OBJ_NAME
178 * 7. DMUS_OBJ_FILENAME
181 if (desc->dwValidData & DMUS_OBJ_OBJECT) {
182 LIST_FOR_EACH_ENTRY(existing, &This->cache, struct cache_entry, entry) {
183 if (existing->Desc.dwValidData & DMUS_OBJ_OBJECT &&
184 IsEqualGUID(&desc->guidObject, &existing->Desc.guidObject) ) {
185 TRACE("Found by DMUS_OBJ_OBJECT\n");
186 return existing;
191 if (desc->dwValidData & DMUS_OBJ_STREAM)
192 FIXME("Finding DMUS_OBJ_STREAM cached objects currently not supported.\n");
194 if (desc->dwValidData & DMUS_OBJ_MEMORY) {
195 LIST_FOR_EACH_ENTRY(existing, &This->cache, struct cache_entry, entry) {
196 if (existing->Desc.dwValidData & DMUS_OBJ_MEMORY &&
197 desc->llMemLength == existing->Desc.llMemLength &&
198 (desc->pbMemData == existing->Desc.pbMemData ||
199 !memcmp(desc->pbMemData, existing->Desc.pbMemData, desc->llMemLength)) ) {
200 TRACE("Found by DMUS_OBJ_MEMORY (%d)\n",
201 desc->pbMemData == existing->Desc.pbMemData);
202 return existing;
207 if ((desc->dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH)) ==
208 (DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH)) {
209 LIST_FOR_EACH_ENTRY(existing, &This->cache, struct cache_entry, entry) {
210 if ((existing->Desc.dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH)) ==
211 (DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH) &&
212 !wcsncmp(desc->wszFileName, existing->Desc.wszFileName, DMUS_MAX_FILENAME)) {
213 TRACE("Found by DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH\n");
214 return existing;
219 if ((desc->dwValidData & (DMUS_OBJ_NAME | DMUS_OBJ_CATEGORY)) ==
220 (DMUS_OBJ_NAME | DMUS_OBJ_CATEGORY)) {
221 LIST_FOR_EACH_ENTRY(existing, &This->cache, struct cache_entry, entry) {
222 if ((existing->Desc.dwValidData & (DMUS_OBJ_NAME | DMUS_OBJ_CATEGORY)) ==
223 (DMUS_OBJ_NAME | DMUS_OBJ_CATEGORY) &&
224 !wcsncmp(desc->wszName, existing->Desc.wszName, DMUS_MAX_NAME) &&
225 !wcsncmp(desc->wszCategory, existing->Desc.wszCategory, DMUS_MAX_CATEGORY)) {
226 TRACE("Found by DMUS_OBJ_NAME | DMUS_OBJ_CATEGORY\n");
227 return existing;
232 if ((desc->dwValidData & DMUS_OBJ_NAME) == DMUS_OBJ_NAME) {
233 LIST_FOR_EACH_ENTRY(existing, &This->cache, struct cache_entry, entry) {
234 if ((existing->Desc.dwValidData & DMUS_OBJ_NAME) == DMUS_OBJ_NAME &&
235 !wcsncmp(desc->wszName, existing->Desc.wszName, DMUS_MAX_NAME)) {
236 TRACE("Found by DMUS_OBJ_NAME\n");
237 return existing;
242 if ((desc->dwValidData & DMUS_OBJ_FILENAME) == DMUS_OBJ_FILENAME) {
243 LIST_FOR_EACH_ENTRY(existing, &This->cache, struct cache_entry, entry) {
244 if (((existing->Desc.dwValidData & DMUS_OBJ_FILENAME) == DMUS_OBJ_FILENAME) &&
245 !wcsncmp(desc->wszFileName, existing->Desc.wszFileName, DMUS_MAX_FILENAME)) {
246 TRACE("Found by DMUS_OBJ_FILENAME\n");
247 return existing;
252 return NULL;
255 static HRESULT WINAPI IDirectMusicLoaderImpl_GetObject(IDirectMusicLoader8 *iface, DMUS_OBJECTDESC *pDesc, REFIID riid, void **ppv)
257 IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface);
258 HRESULT result = S_OK;
259 HRESULT ret = S_OK; /* used at the end of function, to determine whether everything went OK */
260 struct cache_entry *pExistingEntry, *pObjectEntry = NULL;
261 LPSTREAM pStream;
262 IPersistStream* pPersistStream = NULL;
264 LPDIRECTMUSICOBJECT pObject;
265 DMUS_OBJECTDESC GotDesc;
266 BOOL bCache;
268 TRACE("(%p)->(%p, %s, %p)\n", This, pDesc, debugstr_dmguid(riid), ppv);
270 if (TRACE_ON(dmloader))
271 dump_DMUS_OBJECTDESC(pDesc);
273 /* sometimes it happens that guidClass is missing... which is a BadThingTM */
274 if (!(pDesc->dwValidData & DMUS_OBJ_CLASS)) {
275 ERR(": guidClass not valid but needed\n");
276 *ppv = NULL;
277 return DMUS_E_LOADER_NOCLASSID;
280 /* Iterate through the list of objects we know about; these are either loaded
281 * (GetObject, LoadObjectFromFile) or set via SetObject; */
282 TRACE(": looking if we have object in the cache or if it can be found via alias\n");
283 pExistingEntry = find_cache_object(This, pDesc);
284 if (pExistingEntry) {
286 if (pExistingEntry->bInvalidDefaultDLS) {
287 TRACE(": found faulty default DLS collection... enabling M$ compliant behaviour\n");
288 return DMUS_E_LOADER_NOFILENAME;
291 if (pExistingEntry->Desc.dwValidData & DMUS_OBJ_LOADED) {
292 TRACE(": already loaded\n");
293 return IDirectMusicObject_QueryInterface(pExistingEntry->pObject, riid, ppv);
296 TRACE(": not loaded yet\n");
297 pObjectEntry = pExistingEntry;
300 /* basically, if we found alias, we use its descriptor to load...
301 else we use info we were given */
302 if (pObjectEntry) {
303 TRACE(": found alias entry for requested object... using stored info\n");
304 /* I think in certain cases it can happen that entry's descriptor lacks info about
305 where to load from (e.g.: if we loaded from stream and then released object
306 from cache; then only its CLSID, GUID and perhaps name are left); so just
307 overwrite whatever info the entry has (since it ought to be 100% correct) */
308 DMUSIC_CopyDescriptor (pDesc, &pObjectEntry->Desc);
309 /*pDesc = &pObjectEntry->Desc; */ /* FIXME: is this OK? */
310 } else {
311 TRACE(": no cache/alias entry found for requested object\n");
314 if (pDesc->dwValidData & DMUS_OBJ_URL) {
315 TRACE(": loading from URLs not supported yet\n");
316 return DMUS_E_LOADER_FORMATNOTSUPPORTED;
318 else if (pDesc->dwValidData & DMUS_OBJ_FILENAME) {
319 /* load object from file */
320 /* generate filename; if it's full path, don't add search
321 directory path, otherwise do */
322 WCHAR wszFileName[MAX_PATH];
324 if (pDesc->dwValidData & DMUS_OBJ_FULLPATH) {
325 lstrcpyW(wszFileName, pDesc->wszFileName);
326 } else {
327 WCHAR *p;
328 get_search_path(This, &pDesc->guidClass, wszFileName);
329 p = wszFileName + lstrlenW(wszFileName);
330 if (p > wszFileName && p[-1] != '\\') *p++ = '\\';
331 lstrcpyW(p, pDesc->wszFileName);
333 TRACE(": loading from file (%s)\n", debugstr_w(wszFileName));
334 /* create stream and associate it with file */
335 result = DMUSIC_CreateDirectMusicLoaderFileStream ((LPVOID*)&pStream);
336 if (FAILED(result)) {
337 ERR(": could not create file stream\n");
338 return result;
340 result = IDirectMusicLoaderFileStream_Attach (pStream, wszFileName, iface);
341 if (FAILED(result)) {
342 ERR(": could not attach stream to file\n");
343 IStream_Release (pStream);
344 return result;
347 else if (pDesc->dwValidData & DMUS_OBJ_MEMORY) {
348 /* load object from resource */
349 TRACE(": loading from resource\n");
350 /* create stream and associate it with given resource */
351 result = DMUSIC_CreateDirectMusicLoaderResourceStream ((LPVOID*)&pStream);
352 if (FAILED(result)) {
353 ERR(": could not create resource stream\n");
354 return result;
356 result = IDirectMusicLoaderResourceStream_Attach (pStream, pDesc->pbMemData, pDesc->llMemLength, 0, iface);
357 if (FAILED(result)) {
358 ERR(": could not attach stream to resource\n");
359 IStream_Release (pStream);
360 return result;
363 else if (pDesc->dwValidData & DMUS_OBJ_STREAM) {
364 /* load object from stream */
365 TRACE(": loading from stream\n");
366 /* create universal stream and associate it with given one */
367 result = DMUSIC_CreateDirectMusicLoaderGenericStream ((LPVOID*)&pStream);
368 if (FAILED(result)) {
369 ERR(": could not create generic stream\n");
370 return result;
372 result = IDirectMusicLoaderGenericStream_Attach (pStream, pDesc->pStream, iface);
373 if (FAILED(result)) {
374 ERR(": failed to attach stream\n");
375 IStream_Release (pStream);
376 return result;
378 } else {
379 /* nowhere to load from */
380 FIXME(": unknown/unsupported way of loading\n");
381 return DMUS_E_LOADER_NOFILENAME; /* test shows this is returned */
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 (%08x)\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) {
430 if (!pObjectEntry) {
431 pObjectEntry = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof(*pObjectEntry));
432 DM_STRUCT_INIT(&pObjectEntry->Desc);
433 DMUSIC_CopyDescriptor (&pObjectEntry->Desc, &GotDesc);
434 pObjectEntry->pObject = pObject;
435 pObjectEntry->bInvalidDefaultDLS = FALSE;
436 list_add_head(&This->cache, &pObjectEntry->entry);
437 } else {
438 DMUSIC_CopyDescriptor (&pObjectEntry->Desc, &GotDesc);
439 pObjectEntry->pObject = pObject;
440 pObjectEntry->bInvalidDefaultDLS = FALSE;
442 TRACE(": filled in cache entry\n");
443 } else TRACE(": caching disabled\n");
445 result = IDirectMusicObject_QueryInterface (pObject, riid, ppv);
446 if (!bCache) IDirectMusicObject_Release (pObject); /* since loader's reference is not needed */
447 /* if there was trouble with loading, and if no other error occurred,
448 we should return DMUS_S_PARTIALLOAD; else, error is returned */
449 if (result == S_OK)
450 return ret;
451 else
452 return result;
455 static HRESULT WINAPI IDirectMusicLoaderImpl_SetObject(IDirectMusicLoader8 *iface, DMUS_OBJECTDESC *pDesc)
457 IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface);
458 LPSTREAM pStream;
459 LPDIRECTMUSICOBJECT pObject;
460 DMUS_OBJECTDESC Desc;
461 struct cache_entry *pObjectEntry, *pNewEntry;
462 HRESULT hr;
464 TRACE("(%p)->(%p)\n", This, pDesc);
466 if (TRACE_ON(dmloader))
467 dump_DMUS_OBJECTDESC(pDesc);
469 /* create stream and load additional info from it */
470 if (pDesc->dwValidData & DMUS_OBJ_FILENAME) {
471 /* generate filename; if it's full path, don't add search
472 directory path, otherwise do */
473 WCHAR wszFileName[MAX_PATH];
475 if (pDesc->dwValidData & DMUS_OBJ_FULLPATH) {
476 lstrcpyW(wszFileName, pDesc->wszFileName);
477 } else {
478 WCHAR *p;
479 get_search_path(This, &pDesc->guidClass, wszFileName);
480 p = wszFileName + lstrlenW(wszFileName);
481 if (p > wszFileName && p[-1] != '\\') *p++ = '\\';
482 lstrcpyW(p, pDesc->wszFileName);
484 /* create stream */
485 hr = DMUSIC_CreateDirectMusicLoaderFileStream ((LPVOID*)&pStream);
486 if (FAILED(hr)) {
487 ERR(": could not create file stream\n");
488 return DMUS_E_LOADER_FAILEDOPEN;
490 /* attach stream */
491 hr = IDirectMusicLoaderFileStream_Attach (pStream, wszFileName, iface);
492 if (FAILED(hr)) {
493 ERR(": could not attach stream to file %s, make sure it exists\n", debugstr_w(wszFileName));
494 IStream_Release (pStream);
495 return DMUS_E_LOADER_FAILEDOPEN;
498 else if (pDesc->dwValidData & DMUS_OBJ_STREAM) {
499 /* create stream */
500 hr = DMUSIC_CreateDirectMusicLoaderGenericStream ((LPVOID*)&pStream);
501 if (FAILED(hr)) {
502 ERR(": could not create generic stream\n");
503 return DMUS_E_LOADER_FAILEDOPEN;
505 /* attach stream */
506 hr = IDirectMusicLoaderGenericStream_Attach (pStream, pDesc->pStream, iface);
507 if (FAILED(hr)) {
508 ERR(": could not attach stream\n");
509 IStream_Release (pStream);
510 return DMUS_E_LOADER_FAILEDOPEN;
513 else if (pDesc->dwValidData & DMUS_OBJ_MEMORY) {
514 /* create stream */
515 hr = DMUSIC_CreateDirectMusicLoaderResourceStream ((LPVOID*)&pStream);
516 if (FAILED(hr)) {
517 ERR(": could not create resource stream\n");
518 return DMUS_E_LOADER_FAILEDOPEN;
520 /* attach stream */
521 hr = IDirectMusicLoaderResourceStream_Attach (pStream, pDesc->pbMemData, pDesc->llMemLength, 0, iface);
522 if (FAILED(hr)) {
523 ERR(": could not attach stream to resource\n");
524 IStream_Release (pStream);
525 return DMUS_E_LOADER_FAILEDOPEN;
528 else {
529 ERR(": no way to get additional info\n");
530 return DMUS_E_LOADER_FAILEDOPEN;
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 0x%08x\n", debugstr_guid(&pDesc->guidClass),hr);
538 return DMUS_E_LOADER_FAILEDOPEN;
541 /* *sigh*... some ms objects have lousy implementation of ParseDescriptor that clears input descriptor :( */
542 #ifdef NOW_WE_ARE_FREE
543 /* parse descriptor: we actually use input descriptor; fields that aren't available from stream remain,
544 otherwise real info is set */
545 IDirectMusicObject_ParseDescriptor (pObject, pStream, pDesc);
546 #endif
547 /* hmph... due to some trouble I had with certain tests, we store current position and then set it back */
548 DM_STRUCT_INIT(&Desc);
549 if (FAILED(IDirectMusicObject_ParseDescriptor (pObject, pStream, &Desc))) {
550 IStream_Release(pStream);
551 IDirectMusicObject_Release(pObject);
552 ERR(": couldn't parse descriptor\n");
553 return DMUS_E_LOADER_FORMATNOTSUPPORTED;
556 /* copy elements from parsed descriptor into input descriptor; this sets new info, overwriting if necessary,
557 but leaves info that's provided by input and not available from stream */
558 DMUSIC_CopyDescriptor (pDesc, &Desc);
560 /* release everything */
561 IDirectMusicObject_Release (pObject);
562 IStream_Release (pStream);
564 /* sometimes it happens that twisted programs call SetObject for same object twice...
565 in such cases, native loader returns S_OK and does nothing... a sound plan */
566 LIST_FOR_EACH_ENTRY(pObjectEntry, &This->cache, struct cache_entry, entry) {
567 if (!memcmp (&pObjectEntry->Desc, pDesc, sizeof(DMUS_OBJECTDESC))) {
568 TRACE(": exactly same entry already exists\n");
569 return S_OK;
573 /* add new entry */
574 TRACE(": adding alias entry with following info:\n");
575 if (TRACE_ON(dmloader))
576 dump_DMUS_OBJECTDESC(pDesc);
577 pNewEntry = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof(*pNewEntry));
578 /* use this function instead of pure memcpy due to streams (memcpy just copies pointer),
579 which is basically used further by app that called SetDescriptor... better safety than exception */
580 DMUSIC_CopyDescriptor (&pNewEntry->Desc, pDesc);
581 list_add_head(&This->cache, &pNewEntry->entry);
583 return S_OK;
586 static HRESULT WINAPI IDirectMusicLoaderImpl_SetSearchDirectory(IDirectMusicLoader8 *iface,
587 REFGUID class, WCHAR *path, BOOL clear)
589 IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface);
590 int index = index_from_class(class);
591 DWORD attr;
593 TRACE("(%p, %s, %s, %d)\n", This, debugstr_dmguid(class), debugstr_w(path), clear);
595 if (!path)
596 return E_POINTER;
598 if (path[0]) {
599 attr = GetFileAttributesW(path);
600 if (attr == INVALID_FILE_ATTRIBUTES || !(attr & FILE_ATTRIBUTE_DIRECTORY))
601 return DMUS_E_LOADER_BADPATH;
604 if (clear)
605 FIXME("clear flag ignored\n");
607 /* Ignore invalid GUIDs */
608 if (index < 0)
609 return S_OK;
611 if (!This->search_paths[index])
612 This->search_paths[index] = HeapAlloc(GetProcessHeap(), 0, MAX_PATH);
613 else if (!wcsncmp(This->search_paths[index], path, MAX_PATH))
614 return S_FALSE;
616 lstrcpynW(This->search_paths[index], path, MAX_PATH);
618 return S_OK;
621 static HRESULT WINAPI IDirectMusicLoaderImpl_ScanDirectory(IDirectMusicLoader8 *iface, REFGUID rguidClass, WCHAR *pwzFileExtension, WCHAR *pwzScanFileName)
623 IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface);
624 WIN32_FIND_DATAW FileData;
625 HANDLE hSearch;
626 WCHAR wszSearchString[MAX_PATH];
627 WCHAR *p;
628 HRESULT result;
629 TRACE("(%p, %s, %s, %s)\n", This, debugstr_dmguid(rguidClass), debugstr_w(pwzFileExtension),
630 debugstr_w(pwzScanFileName));
631 if (index_from_class(rguidClass) <= 0) {
632 ERR(": rguidClass invalid CLSID\n");
633 return REGDB_E_CLASSNOTREG;
636 if (!pwzFileExtension)
637 return S_FALSE;
639 /* get search path for given class */
640 get_search_path(This, rguidClass, wszSearchString);
642 p = wszSearchString + lstrlenW(wszSearchString);
643 if (p > wszSearchString && p[-1] != '\\') *p++ = '\\';
644 *p++ = '*'; /* any file */
645 if (lstrcmpW (pwzFileExtension, L"*"))
646 *p++ = '.'; /* if we have actual extension, put a dot */
647 lstrcpyW (p, pwzFileExtension);
649 TRACE(": search string: %s\n", debugstr_w(wszSearchString));
651 hSearch = FindFirstFileW (wszSearchString, &FileData);
652 if (hSearch == INVALID_HANDLE_VALUE) {
653 TRACE(": no files found\n");
654 return S_FALSE;
657 do {
658 DMUS_OBJECTDESC Desc;
659 DM_STRUCT_INIT(&Desc);
660 Desc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME | DMUS_OBJ_DATE;
661 Desc.guidClass = *rguidClass;
662 lstrcpyW (Desc.wszFileName, FileData.cFileName);
663 FileTimeToLocalFileTime (&FileData.ftCreationTime, &Desc.ftDate);
664 IDirectMusicLoader8_SetObject (iface, &Desc);
666 if (!FindNextFileW (hSearch, &FileData)) {
667 if (GetLastError () == ERROR_NO_MORE_FILES) {
668 TRACE(": search completed\n");
669 result = S_OK;
670 } else {
671 ERR(": could not get next file\n");
672 result = E_FAIL;
674 FindClose (hSearch);
675 return result;
677 } while (1);
680 static HRESULT WINAPI IDirectMusicLoaderImpl_CacheObject(IDirectMusicLoader8 *iface,
681 IDirectMusicObject *object)
683 IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface);
684 DMUS_OBJECTDESC desc;
685 struct cache_entry *entry;
687 TRACE("(%p, %p)\n", This, object);
689 DM_STRUCT_INIT(&desc);
690 IDirectMusicObject_GetDescriptor(object, &desc);
692 /* Iterate through the list and check if we have an alias (without object), corresponding
693 to the descriptor of the input object */
694 entry = find_cache_object(This, &desc);
695 if (entry) {
696 if ((entry->Desc.dwValidData & DMUS_OBJ_LOADED) && entry->pObject) {
697 TRACE("Object already loaded.\n");
698 return S_FALSE;
701 entry->Desc.dwValidData |= DMUS_OBJ_LOADED;
702 entry->pObject = object;
703 IDirectMusicObject_AddRef(entry->pObject);
704 return S_OK;
707 return DMUS_E_LOADER_OBJECTNOTFOUND;
710 static HRESULT WINAPI IDirectMusicLoaderImpl_ReleaseObject(IDirectMusicLoader8 *iface,
711 IDirectMusicObject *object)
713 IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface);
714 DMUS_OBJECTDESC desc;
715 struct cache_entry *entry;
717 TRACE("(%p, %p)\n", This, object);
719 if (!object)
720 return E_POINTER;
722 DM_STRUCT_INIT(&desc);
723 IDirectMusicObject_GetDescriptor(object, &desc);
725 TRACE("Looking for the object in cache\n");
726 entry = find_cache_object(This, &desc);
727 if (entry) {
728 dump_DMUS_OBJECTDESC(&entry->Desc);
730 if (entry->pObject && entry->Desc.dwValidData & DMUS_OBJ_LOADED) {
731 IDirectMusicObject_Release(entry->pObject);
732 entry->pObject = NULL;
733 entry->Desc.dwValidData &= ~DMUS_OBJ_LOADED;
734 return S_OK;
738 return S_FALSE;
741 static HRESULT WINAPI IDirectMusicLoaderImpl_ClearCache(IDirectMusicLoader8 *iface, REFGUID class)
743 IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface);
744 struct cache_entry *obj, *obj2;
746 TRACE("(%p, %s)\n", This, debugstr_dmguid(class));
748 LIST_FOR_EACH_ENTRY_SAFE(obj, obj2, &This->cache, struct cache_entry, entry) {
749 if ((IsEqualGUID(class, &GUID_DirectMusicAllTypes) || IsEqualGUID(class, &obj->Desc.guidClass)) &&
750 (obj->Desc.dwValidData & DMUS_OBJ_LOADED)) {
751 /* basically, wrap to ReleaseObject for each object found */
752 IDirectMusicLoader8_ReleaseObject(iface, obj->pObject);
753 list_remove(&obj->entry);
754 HeapFree(GetProcessHeap(), 0, obj);
758 return S_OK;
761 static HRESULT WINAPI IDirectMusicLoaderImpl_EnableCache(IDirectMusicLoader8 *iface, REFGUID class,
762 BOOL enable)
764 IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface);
765 BOOL current;
767 TRACE("(%p, %s, %d)\n", This, debugstr_dmguid(class), enable);
769 current = is_cache_enabled(This, class);
771 if (IsEqualGUID(class, &GUID_DirectMusicAllTypes))
772 This->cache_class = enable ? ~0 : 0;
773 else {
774 int idx = index_from_class(class);
775 if (idx == -1) return S_FALSE;
776 if (enable)
777 This->cache_class |= 1 << idx;
778 else
779 This->cache_class &= ~(1 << idx);
782 if (!enable)
783 IDirectMusicLoader8_ClearCache(iface, class);
785 if (current == enable)
786 return S_FALSE;
788 return S_OK;
791 static HRESULT WINAPI IDirectMusicLoaderImpl_EnumObject(IDirectMusicLoader8 *iface, REFGUID rguidClass, DWORD dwIndex, DMUS_OBJECTDESC *pDesc)
793 IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface);
794 DWORD dwCount = 0;
795 struct cache_entry *pObjectEntry;
796 TRACE("(%p, %s, %d, %p)\n", This, debugstr_dmguid(rguidClass), dwIndex, pDesc);
798 DM_STRUCT_INIT(pDesc);
800 LIST_FOR_EACH_ENTRY(pObjectEntry, &This->cache, struct cache_entry, entry) {
801 if (IsEqualGUID (rguidClass, &GUID_DirectMusicAllTypes) || IsEqualGUID (rguidClass, &pObjectEntry->Desc.guidClass)) {
802 if (dwCount == dwIndex) {
803 *pDesc = pObjectEntry->Desc;
804 /* we aren't supposed to reveal this info */
805 pDesc->dwValidData &= ~(DMUS_OBJ_MEMORY | DMUS_OBJ_STREAM);
806 pDesc->pbMemData = NULL;
807 pDesc->llMemLength = 0;
808 pDesc->pStream = NULL;
809 return S_OK;
811 dwCount++;
815 TRACE(": not found\n");
816 return S_FALSE;
819 static void WINAPI IDirectMusicLoaderImpl_CollectGarbage(IDirectMusicLoader8 *iface)
821 FIXME("(%p)->(): stub\n", iface);
824 static HRESULT WINAPI IDirectMusicLoaderImpl_ReleaseObjectByUnknown(IDirectMusicLoader8 *iface, IUnknown *pObject)
826 IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface);
827 HRESULT result;
828 LPDIRECTMUSICOBJECT pObjectInterface;
830 TRACE("(%p, %p)\n", This, pObject);
832 if (IsBadReadPtr (pObject, sizeof(*pObject))) {
833 ERR(": pObject bad write pointer\n");
834 return E_POINTER;
836 /* we simply get IDirectMusicObject interface */
837 result = IUnknown_QueryInterface (pObject, &IID_IDirectMusicObject, (LPVOID*)&pObjectInterface);
838 if (FAILED(result)) return result;
839 /* and release it in old-fashioned way */
840 result = IDirectMusicLoader8_ReleaseObject (iface, pObjectInterface);
841 IDirectMusicObject_Release (pObjectInterface);
843 return result;
846 static HRESULT WINAPI IDirectMusicLoaderImpl_LoadObjectFromFile(IDirectMusicLoader8 *iface, REFGUID rguidClassID, REFIID iidInterfaceID, WCHAR *pwzFilePath, void **ppObject)
848 IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface);
849 DMUS_OBJECTDESC ObjDesc;
850 WCHAR wszLoaderSearchPath[MAX_PATH];
852 TRACE("(%p, %s, %s, %s, %p): wrapping to IDirectMusicLoaderImpl_GetObject\n", This, debugstr_dmguid(rguidClassID), debugstr_dmguid(iidInterfaceID), debugstr_w(pwzFilePath), ppObject);
854 DM_STRUCT_INIT(&ObjDesc);
855 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 */
856 ObjDesc.guidClass = *rguidClassID;
857 /* OK, MSDN says that search order is the following:
858 - current directory (DONE)
859 - windows search path (FIXME: how do I get that?)
860 - loader's search path (DONE)
862 get_search_path(This, rguidClassID, wszLoaderSearchPath);
863 /* search in current directory */
864 if (!SearchPathW(NULL, pwzFilePath, NULL, ARRAY_SIZE(ObjDesc.wszFileName), ObjDesc.wszFileName, NULL) &&
865 /* search in loader's search path */
866 !SearchPathW(wszLoaderSearchPath, pwzFilePath, NULL, ARRAY_SIZE(ObjDesc.wszFileName), ObjDesc.wszFileName, NULL)) {
867 /* cannot find file */
868 TRACE(": cannot find file\n");
869 return DMUS_E_LOADER_FAILEDOPEN;
872 TRACE(": full file path = %s\n", debugstr_w (ObjDesc.wszFileName));
874 return IDirectMusicLoader_GetObject(iface, &ObjDesc, iidInterfaceID, ppObject);
877 static const IDirectMusicLoader8Vtbl DirectMusicLoader_Loader_Vtbl = {
878 IDirectMusicLoaderImpl_QueryInterface,
879 IDirectMusicLoaderImpl_AddRef,
880 IDirectMusicLoaderImpl_Release,
881 IDirectMusicLoaderImpl_GetObject,
882 IDirectMusicLoaderImpl_SetObject,
883 IDirectMusicLoaderImpl_SetSearchDirectory,
884 IDirectMusicLoaderImpl_ScanDirectory,
885 IDirectMusicLoaderImpl_CacheObject,
886 IDirectMusicLoaderImpl_ReleaseObject,
887 IDirectMusicLoaderImpl_ClearCache,
888 IDirectMusicLoaderImpl_EnableCache,
889 IDirectMusicLoaderImpl_EnumObject,
890 IDirectMusicLoaderImpl_CollectGarbage,
891 IDirectMusicLoaderImpl_ReleaseObjectByUnknown,
892 IDirectMusicLoaderImpl_LoadObjectFromFile
895 /* help function for DMUSIC_SetDefaultDLS */
896 static HRESULT DMUSIC_GetDefaultGMPath (WCHAR wszPath[MAX_PATH]) {
897 HKEY hkDM;
898 DWORD returnType, sizeOfReturnBuffer = MAX_PATH;
899 char szPath[MAX_PATH];
901 if ((RegOpenKeyExA (HKEY_LOCAL_MACHINE, "Software\\Microsoft\\DirectMusic" , 0, KEY_READ, &hkDM) != ERROR_SUCCESS) ||
902 (RegQueryValueExA (hkDM, "GMFilePath", NULL, &returnType, (LPBYTE) szPath, &sizeOfReturnBuffer) != ERROR_SUCCESS)) {
903 WARN(": registry entry missing\n" );
904 return E_FAIL;
906 /* FIXME: Check return types to ensure we're interpreting data right */
907 MultiByteToWideChar (CP_ACP, 0, szPath, -1, wszPath, MAX_PATH);
909 return S_OK;
912 /* for ClassFactory */
913 HRESULT WINAPI create_dmloader(REFIID lpcGUID, void **ppobj)
915 IDirectMusicLoaderImpl *obj;
916 DMUS_OBJECTDESC Desc;
917 struct cache_entry *dls;
918 struct list *pEntry;
920 TRACE("(%s, %p)\n", debugstr_dmguid(lpcGUID), ppobj);
921 obj = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IDirectMusicLoaderImpl));
922 if (NULL == obj) {
923 *ppobj = NULL;
924 return E_OUTOFMEMORY;
926 obj->IDirectMusicLoader8_iface.lpVtbl = &DirectMusicLoader_Loader_Vtbl;
927 obj->ref = 0; /* Will be inited with QueryInterface */
928 list_init(&obj->cache);
929 /* Caching is enabled by default for all classes */
930 obj->cache_class = ~0;
932 /* set default DLS collection (via SetObject... so that loading via DMUS_OBJ_OBJECT is possible) */
933 DM_STRUCT_INIT(&Desc);
934 Desc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH | DMUS_OBJ_OBJECT;
935 Desc.guidClass = CLSID_DirectMusicCollection;
936 Desc.guidObject = GUID_DefaultGMCollection;
937 DMUSIC_GetDefaultGMPath (Desc.wszFileName);
938 IDirectMusicLoader_SetObject(&obj->IDirectMusicLoader8_iface, &Desc);
939 /* and now the workaroundTM for "invalid" default DLS; basically,
940 my tests showed that if GUID chunk is present in default DLS
941 collection, loader treats it as "invalid" and returns
942 DMUS_E_LOADER_NOFILENAME for all requests for it; basically, we check
943 if out input guidObject was overwritten */
944 pEntry = list_head(&obj->cache);
945 dls = LIST_ENTRY(pEntry, struct cache_entry, entry);
946 if (!IsEqualGUID(&Desc.guidObject, &GUID_DefaultGMCollection)) {
947 dls->bInvalidDefaultDLS = TRUE;
950 lock_module();
952 return IDirectMusicLoader_QueryInterface(&obj->IDirectMusicLoader8_iface, lpcGUID, ppobj);