msvcrt/tests: Add _strupr tests.
[wine.git] / dlls / dmloader / loader.c
blob10a2151fcb1b072e72e6c032ea7916ad8205ec83
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) strcpyW (pDst->wszName, pSrc->wszName);
102 if (pSrc->dwValidData & DMUS_OBJ_CATEGORY) strcpyW (pDst->wszCategory, pSrc->wszCategory);
103 if (pSrc->dwValidData & DMUS_OBJ_FILENAME) strcpyW (pDst->wszFileName, pSrc->wszFileName);
104 if (pSrc->dwValidData & DMUS_OBJ_STREAM) IStream_Clone (pSrc->pStream, &pDst->pStream);
105 if (pSrc->dwValidData & DMUS_OBJ_MEMORY) {
106 pDst->pbMemData = pSrc->pbMemData;
107 pDst->llMemLength = pSrc->llMemLength;
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 HRESULT WINAPI IDirectMusicLoaderImpl_GetObject(IDirectMusicLoader8 *iface, DMUS_OBJECTDESC *pDesc, REFIID riid, void **ppv)
168 IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface);
169 HRESULT result = S_OK;
170 HRESULT ret = S_OK; /* used at the end of function, to determine whether everything went OK */
171 struct cache_entry *pExistingEntry, *pObjectEntry = NULL;
172 LPSTREAM pStream;
173 IPersistStream* pPersistStream = NULL;
175 LPDIRECTMUSICOBJECT pObject;
176 DMUS_OBJECTDESC GotDesc;
177 BOOL bCache;
179 TRACE("(%p)->(%p, %s, %p)\n", This, pDesc, debugstr_dmguid(riid), ppv);
181 if (TRACE_ON(dmloader))
182 dump_DMUS_OBJECTDESC(pDesc);
184 /* sometimes it happens that guidClass is missing... which is a BadThingTM */
185 if (!(pDesc->dwValidData & DMUS_OBJ_CLASS)) {
186 ERR(": guidClass not valid but needed\n");
187 *ppv = NULL;
188 return DMUS_E_LOADER_NOCLASSID;
191 /* OK, first we iterate through the list of objects we know about; these are either loaded (GetObject, LoadObjectFromFile)
192 or set via SetObject; */
193 TRACE(": looking if we have object in the cache or if it can be found via alias\n");
194 LIST_FOR_EACH_ENTRY(pExistingEntry, &This->cache, struct cache_entry, entry) {
195 if ((pDesc->dwValidData & DMUS_OBJ_OBJECT) &&
196 (pExistingEntry->Desc.dwValidData & DMUS_OBJ_OBJECT) &&
197 IsEqualGUID (&pDesc->guidObject, &pExistingEntry->Desc.guidObject)) {
198 TRACE(": found it by object GUID\n");
199 /* I suppose such stuff can happen only when GUID for object is given (GUID_DefaultGMCollection) */
200 if (pExistingEntry->bInvalidDefaultDLS) {
201 TRACE(": found faulty default DLS collection... enabling M$ compliant behaviour\n");
202 return DMUS_E_LOADER_NOFILENAME;
204 if (pExistingEntry->Desc.dwValidData & DMUS_OBJ_LOADED) {
205 TRACE(": already loaded\n");
206 return IDirectMusicObject_QueryInterface (pExistingEntry->pObject, riid, ppv);
207 } else {
208 TRACE(": not loaded yet\n");
209 pObjectEntry = pExistingEntry;
212 else if ((pDesc->dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH)) &&
213 (pExistingEntry->Desc.dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH)) &&
214 !strncmpW (pDesc->wszFileName, pExistingEntry->Desc.wszFileName, DMUS_MAX_FILENAME)) {
215 TRACE(": found it by fullpath filename\n");
216 if (pExistingEntry->Desc.dwValidData & DMUS_OBJ_LOADED) {
217 TRACE(": already loaded\n");
218 return IDirectMusicObject_QueryInterface (pExistingEntry->pObject, riid, ppv);
219 } else {
220 TRACE(": not loaded yet\n");
221 pObjectEntry = pExistingEntry;
224 else if ((pDesc->dwValidData & (DMUS_OBJ_NAME | DMUS_OBJ_CATEGORY)) &&
225 (pExistingEntry->Desc.dwValidData & (DMUS_OBJ_NAME | DMUS_OBJ_CATEGORY)) &&
226 !strncmpW (pDesc->wszName, pExistingEntry->Desc.wszName, DMUS_MAX_NAME) &&
227 !strncmpW (pDesc->wszCategory, pExistingEntry->Desc.wszCategory, DMUS_MAX_CATEGORY)) {
228 TRACE(": found it by name and category\n");
229 if (pExistingEntry->Desc.dwValidData & DMUS_OBJ_LOADED) {
230 TRACE(": already loaded\n");
231 return IDirectMusicObject_QueryInterface (pExistingEntry->pObject, riid, ppv);
232 } else {
233 TRACE(": not loaded yet\n");
234 pObjectEntry = pExistingEntry;
237 else if ((pDesc->dwValidData & DMUS_OBJ_NAME) &&
238 (pExistingEntry->Desc.dwValidData & DMUS_OBJ_NAME) &&
239 !strncmpW (pDesc->wszName, pExistingEntry->Desc.wszName, DMUS_MAX_NAME)) {
240 TRACE(": found it by name\n");
241 if (pExistingEntry->Desc.dwValidData & DMUS_OBJ_LOADED) {
242 TRACE(": already loaded\n");
243 return IDirectMusicObject_QueryInterface (pExistingEntry->pObject, riid, ppv);
244 } else {
245 TRACE(": not loaded yet\n");
246 pObjectEntry = pExistingEntry;
249 else if ((pDesc->dwValidData & DMUS_OBJ_FILENAME) &&
250 (pExistingEntry->Desc.dwValidData & DMUS_OBJ_FILENAME) &&
251 !strncmpW (pDesc->wszFileName, pExistingEntry->Desc.wszFileName, DMUS_MAX_FILENAME)) {
252 TRACE(": found it by filename\n");
253 if (pExistingEntry->Desc.dwValidData & DMUS_OBJ_LOADED) {
254 TRACE(": already loaded\n");
255 return IDirectMusicObject_QueryInterface (pExistingEntry->pObject, riid, ppv);
256 } else {
257 TRACE(": not loaded yet\n");
258 pObjectEntry = pExistingEntry;
263 /* basically, if we found alias, we use its descriptor to load...
264 else we use info we were given */
265 if (pObjectEntry) {
266 TRACE(": found alias entry for requested object... using stored info\n");
267 /* I think in certain cases it can happen that entry's descriptor lacks info about
268 where to load from (e.g.: if we loaded from stream and then released object
269 from cache; then only its CLSID, GUID and perhaps name are left); so just
270 overwrite whatever info the entry has (since it ought to be 100% correct) */
271 DMUSIC_CopyDescriptor (pDesc, &pObjectEntry->Desc);
272 /*pDesc = &pObjectEntry->Desc; */ /* FIXME: is this OK? */
273 } else {
274 TRACE(": no cache/alias entry found for requested object\n");
277 if (pDesc->dwValidData & DMUS_OBJ_URL) {
278 TRACE(": loading from URLs not supported yet\n");
279 return DMUS_E_LOADER_FORMATNOTSUPPORTED;
281 else if (pDesc->dwValidData & DMUS_OBJ_FILENAME) {
282 /* load object from file */
283 /* generate filename; if it's full path, don't add search
284 directory path, otherwise do */
285 WCHAR wszFileName[MAX_PATH];
287 if (pDesc->dwValidData & DMUS_OBJ_FULLPATH) {
288 lstrcpyW(wszFileName, pDesc->wszFileName);
289 } else {
290 WCHAR *p;
291 get_search_path(This, &pDesc->guidClass, wszFileName);
292 p = wszFileName + lstrlenW(wszFileName);
293 if (p > wszFileName && p[-1] != '\\') *p++ = '\\';
294 strcpyW(p, pDesc->wszFileName);
296 TRACE(": loading from file (%s)\n", debugstr_w(wszFileName));
297 /* create stream and associate it with file */
298 result = DMUSIC_CreateDirectMusicLoaderFileStream ((LPVOID*)&pStream);
299 if (FAILED(result)) {
300 ERR(": could not create file stream\n");
301 return result;
303 result = IDirectMusicLoaderFileStream_Attach (pStream, wszFileName, iface);
304 if (FAILED(result)) {
305 ERR(": could not attach stream to file\n");
306 IStream_Release (pStream);
307 return result;
310 else if (pDesc->dwValidData & DMUS_OBJ_MEMORY) {
311 /* load object from resource */
312 TRACE(": loading from resource\n");
313 /* create stream and associate it with given resource */
314 result = DMUSIC_CreateDirectMusicLoaderResourceStream ((LPVOID*)&pStream);
315 if (FAILED(result)) {
316 ERR(": could not create resource stream\n");
317 return result;
319 result = IDirectMusicLoaderResourceStream_Attach (pStream, pDesc->pbMemData, pDesc->llMemLength, 0, iface);
320 if (FAILED(result)) {
321 ERR(": could not attach stream to resource\n");
322 IStream_Release (pStream);
323 return result;
326 else if (pDesc->dwValidData & DMUS_OBJ_STREAM) {
327 /* load object from stream */
328 TRACE(": loading from stream\n");
329 /* create universal stream and associate it with given one */
330 result = DMUSIC_CreateDirectMusicLoaderGenericStream ((LPVOID*)&pStream);
331 if (FAILED(result)) {
332 ERR(": could not create generic stream\n");
333 return result;
335 result = IDirectMusicLoaderGenericStream_Attach (pStream, pDesc->pStream, iface);
336 if (FAILED(result)) {
337 ERR(": failed to attach stream\n");
338 IStream_Release (pStream);
339 return result;
341 } else {
342 /* nowhere to load from */
343 FIXME(": unknown/unsupported way of loading\n");
344 return DMUS_E_LOADER_NOFILENAME; /* test shows this is returned */
347 /* create object */
348 result = CoCreateInstance (&pDesc->guidClass, NULL, CLSCTX_INPROC_SERVER, &IID_IDirectMusicObject, (LPVOID*)&pObject);
349 if (FAILED(result)) {
350 ERR(": could not create object\n");
351 return result;
353 /* acquire PersistStream interface */
354 result = IDirectMusicObject_QueryInterface (pObject, &IID_IPersistStream, (LPVOID*)&pPersistStream);
355 if (FAILED(result)) {
356 ERR("failed to Query\n");
357 return result;
359 /* load */
360 result = IPersistStream_Load (pPersistStream, pStream);
361 if (result != S_OK) {
362 WARN(": failed to (completely) load object (%s)\n", debugstr_dmreturn(result));
363 return result;
365 /* get descriptor */
366 DM_STRUCT_INIT(&GotDesc);
367 result = IDirectMusicObject_GetDescriptor (pObject, &GotDesc);
368 /* set filename (if we loaded via filename) */
369 if (pDesc->dwValidData & DMUS_OBJ_FILENAME) {
370 GotDesc.dwValidData |= (pDesc->dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH));
371 strcpyW (GotDesc.wszFileName, pDesc->wszFileName);
373 if (FAILED(result)) {
374 ERR(": failed to get descriptor\n");
375 return result;
377 /* release all loading related stuff */
378 IStream_Release (pStream);
379 IPersistStream_Release (pPersistStream);
381 /* add object to cache/overwrite existing info (if cache is enabled) */
382 bCache = is_cache_enabled(This, &pDesc->guidClass);
383 if (bCache) {
384 if (!pObjectEntry) {
385 pObjectEntry = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof(*pObjectEntry));
386 DM_STRUCT_INIT(&pObjectEntry->Desc);
387 DMUSIC_CopyDescriptor (&pObjectEntry->Desc, &GotDesc);
388 pObjectEntry->pObject = pObject;
389 pObjectEntry->bInvalidDefaultDLS = FALSE;
390 list_add_head(&This->cache, &pObjectEntry->entry);
391 } else {
392 DMUSIC_CopyDescriptor (&pObjectEntry->Desc, &GotDesc);
393 pObjectEntry->pObject = pObject;
394 pObjectEntry->bInvalidDefaultDLS = FALSE;
396 TRACE(": filled in cache entry\n");
397 } else TRACE(": caching disabled\n");
399 result = IDirectMusicObject_QueryInterface (pObject, riid, ppv);
400 if (!bCache) IDirectMusicObject_Release (pObject); /* since loader's reference is not needed */
401 /* if there was trouble with loading, and if no other error occurred,
402 we should return DMUS_S_PARTIALLOAD; else, error is returned */
403 if (result == S_OK)
404 return ret;
405 else
406 return result;
409 static HRESULT WINAPI IDirectMusicLoaderImpl_SetObject(IDirectMusicLoader8 *iface, DMUS_OBJECTDESC *pDesc)
411 IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface);
412 LPSTREAM pStream;
413 LPDIRECTMUSICOBJECT pObject;
414 DMUS_OBJECTDESC Desc;
415 struct cache_entry *pObjectEntry, *pNewEntry;
416 HRESULT hr;
418 TRACE("(%p)->(%p)\n", This, pDesc);
420 if (TRACE_ON(dmloader))
421 dump_DMUS_OBJECTDESC(pDesc);
423 /* create stream and load additional info from it */
424 if (pDesc->dwValidData & DMUS_OBJ_FILENAME) {
425 /* generate filename; if it's full path, don't add search
426 directory path, otherwise do */
427 WCHAR wszFileName[MAX_PATH];
429 if (pDesc->dwValidData & DMUS_OBJ_FULLPATH) {
430 lstrcpyW(wszFileName, pDesc->wszFileName);
431 } else {
432 WCHAR *p;
433 get_search_path(This, &pDesc->guidClass, wszFileName);
434 p = wszFileName + lstrlenW(wszFileName);
435 if (p > wszFileName && p[-1] != '\\') *p++ = '\\';
436 strcpyW(p, pDesc->wszFileName);
438 /* create stream */
439 hr = DMUSIC_CreateDirectMusicLoaderFileStream ((LPVOID*)&pStream);
440 if (FAILED(hr)) {
441 ERR(": could not create file stream\n");
442 return DMUS_E_LOADER_FAILEDOPEN;
444 /* attach stream */
445 hr = IDirectMusicLoaderFileStream_Attach (pStream, wszFileName, iface);
446 if (FAILED(hr)) {
447 ERR(": could not attach stream to file %s, make sure it exists\n", debugstr_w(wszFileName));
448 IStream_Release (pStream);
449 return DMUS_E_LOADER_FAILEDOPEN;
452 else if (pDesc->dwValidData & DMUS_OBJ_STREAM) {
453 /* create stream */
454 hr = DMUSIC_CreateDirectMusicLoaderGenericStream ((LPVOID*)&pStream);
455 if (FAILED(hr)) {
456 ERR(": could not create generic stream\n");
457 return DMUS_E_LOADER_FAILEDOPEN;
459 /* attach stream */
460 hr = IDirectMusicLoaderGenericStream_Attach (pStream, pDesc->pStream, iface);
461 if (FAILED(hr)) {
462 ERR(": could not attach stream\n");
463 IStream_Release (pStream);
464 return DMUS_E_LOADER_FAILEDOPEN;
467 else if (pDesc->dwValidData & DMUS_OBJ_MEMORY) {
468 /* create stream */
469 hr = DMUSIC_CreateDirectMusicLoaderResourceStream ((LPVOID*)&pStream);
470 if (FAILED(hr)) {
471 ERR(": could not create resource stream\n");
472 return DMUS_E_LOADER_FAILEDOPEN;
474 /* attach stream */
475 hr = IDirectMusicLoaderResourceStream_Attach (pStream, pDesc->pbMemData, pDesc->llMemLength, 0, iface);
476 if (FAILED(hr)) {
477 ERR(": could not attach stream to resource\n");
478 IStream_Release (pStream);
479 return DMUS_E_LOADER_FAILEDOPEN;
482 else {
483 ERR(": no way to get additional info\n");
484 return DMUS_E_LOADER_FAILEDOPEN;
487 /* create object */
488 hr = CoCreateInstance (&pDesc->guidClass, NULL, CLSCTX_INPROC_SERVER, &IID_IDirectMusicObject, (LPVOID*)&pObject);
489 if (FAILED(hr)) {
490 ERR("Object creation of %s failed 0x%08x\n", debugstr_guid(&pDesc->guidClass),hr);
491 return DMUS_E_LOADER_FAILEDOPEN;
494 /* *sigh*... some ms objects have lousy implementation of ParseDescriptor that clears input descriptor :( */
495 #ifdef NOW_WE_ARE_FREE
496 /* parse descriptor: we actually use input descriptor; fields that aren't available from stream remain,
497 otherwise real info is set */
498 IDirectMusicObject_ParseDescriptor (pObject, pStream, pDesc);
499 #endif
500 /* hmph... due to some trouble I had with certain tests, we store current position and then set it back */
501 DM_STRUCT_INIT(&Desc);
502 if (FAILED(IDirectMusicObject_ParseDescriptor (pObject, pStream, &Desc))) {
503 ERR(": couldn't parse descriptor\n");
504 return DMUS_E_LOADER_FORMATNOTSUPPORTED;
507 /* copy elements from parsed descriptor into input descriptor; this sets new info, overwriting if necessary,
508 but leaves info that's provided by input and not available from stream */
509 DMUSIC_CopyDescriptor (pDesc, &Desc);
511 /* release everything */
512 IDirectMusicObject_Release (pObject);
513 IStream_Release (pStream);
515 /* sometimes it happens that twisted programs call SetObject for same object twice...
516 in such cases, native loader returns S_OK and does nothing... a sound plan */
517 LIST_FOR_EACH_ENTRY(pObjectEntry, &This->cache, struct cache_entry, entry) {
518 if (!memcmp (&pObjectEntry->Desc, pDesc, sizeof(DMUS_OBJECTDESC))) {
519 TRACE(": exactly same entry already exists\n");
520 return S_OK;
524 /* add new entry */
525 TRACE(": adding alias entry with following info:\n");
526 if (TRACE_ON(dmloader))
527 dump_DMUS_OBJECTDESC(pDesc);
528 pNewEntry = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof(*pNewEntry));
529 /* use this function instead of pure memcpy due to streams (memcpy just copies pointer),
530 which is basically used further by app that called SetDescriptor... better safety than exception */
531 DMUSIC_CopyDescriptor (&pNewEntry->Desc, pDesc);
532 list_add_head(&This->cache, &pNewEntry->entry);
534 return S_OK;
537 static HRESULT WINAPI IDirectMusicLoaderImpl_SetSearchDirectory(IDirectMusicLoader8 *iface,
538 REFGUID class, WCHAR *path, BOOL clear)
540 IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface);
541 int index = index_from_class(class);
542 DWORD attr;
544 TRACE("(%p, %s, %s, %d)\n", This, debugstr_dmguid(class), debugstr_w(path), clear);
546 if (!path)
547 return E_POINTER;
549 if (path[0]) {
550 attr = GetFileAttributesW(path);
551 if (attr == INVALID_FILE_ATTRIBUTES || !(attr & FILE_ATTRIBUTE_DIRECTORY))
552 return DMUS_E_LOADER_BADPATH;
555 if (clear)
556 FIXME("clear flag ignored\n");
558 /* Ignore invalid GUIDs */
559 if (index < 0)
560 return S_OK;
562 if (!This->search_paths[index])
563 This->search_paths[index] = HeapAlloc(GetProcessHeap(), 0, MAX_PATH);
564 else if (!strncmpW(This->search_paths[index], path, MAX_PATH))
565 return S_FALSE;
567 lstrcpynW(This->search_paths[index], path, MAX_PATH);
569 return S_OK;
572 static HRESULT WINAPI IDirectMusicLoaderImpl_ScanDirectory(IDirectMusicLoader8 *iface, REFGUID rguidClass, WCHAR *pwzFileExtension, WCHAR *pwzScanFileName)
574 IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface);
575 static const WCHAR wszAny[] = {'*',0};
576 WIN32_FIND_DATAW FileData;
577 HANDLE hSearch;
578 WCHAR wszSearchString[MAX_PATH];
579 WCHAR *p;
580 HRESULT result;
581 TRACE("(%p, %s, %s, %s)\n", This, debugstr_dmguid(rguidClass), debugstr_w(pwzFileExtension),
582 debugstr_w(pwzScanFileName));
583 if (index_from_class(rguidClass) <= 0) {
584 ERR(": rguidClass invalid CLSID\n");
585 return REGDB_E_CLASSNOTREG;
588 if (!pwzFileExtension)
589 return S_FALSE;
591 /* get search path for given class */
592 get_search_path(This, rguidClass, wszSearchString);
594 p = wszSearchString + lstrlenW(wszSearchString);
595 if (p > wszSearchString && p[-1] != '\\') *p++ = '\\';
596 *p++ = '*'; /* any file */
597 if (strcmpW (pwzFileExtension, wszAny)) *p++ = '.'; /* if we have actual extension, put a dot */
598 strcpyW (p, pwzFileExtension);
600 TRACE(": search string: %s\n", debugstr_w(wszSearchString));
602 hSearch = FindFirstFileW (wszSearchString, &FileData);
603 if (hSearch == INVALID_HANDLE_VALUE) {
604 TRACE(": no files found\n");
605 return S_FALSE;
608 do {
609 DMUS_OBJECTDESC Desc;
610 DM_STRUCT_INIT(&Desc);
611 Desc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH | DMUS_OBJ_DATE;
612 Desc.guidClass = *rguidClass;
613 strcpyW (Desc.wszFileName, FileData.cFileName);
614 FileTimeToLocalFileTime (&FileData.ftCreationTime, &Desc.ftDate);
615 IDirectMusicLoader8_SetObject (iface, &Desc);
617 if (!FindNextFileW (hSearch, &FileData)) {
618 if (GetLastError () == ERROR_NO_MORE_FILES) {
619 TRACE(": search completed\n");
620 result = S_OK;
621 } else {
622 ERR(": could not get next file\n");
623 result = E_FAIL;
625 FindClose (hSearch);
626 return result;
628 } while (1);
631 static HRESULT WINAPI IDirectMusicLoaderImpl_CacheObject(IDirectMusicLoader8 *iface, IDirectMusicObject *pObject)
633 IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface);
634 DMUS_OBJECTDESC Desc;
635 HRESULT result = DMUS_E_LOADER_OBJECTNOTFOUND;
636 struct cache_entry *pObjectEntry;
638 TRACE("(%p, %p)\n", This, pObject);
640 /* get descriptor */
641 DM_STRUCT_INIT(&Desc);
642 IDirectMusicObject_GetDescriptor (pObject, &Desc);
644 /* now iterate through the list and check if we have an alias (without object), corresponding
645 to the descriptor of the input object */
646 LIST_FOR_EACH_ENTRY(pObjectEntry, &This->cache, struct cache_entry, entry) {
647 if ((Desc.dwValidData & DMUS_OBJ_OBJECT) &&
648 (pObjectEntry->Desc.dwValidData & DMUS_OBJ_OBJECT) &&
649 IsEqualGUID (&Desc.guidObject, &pObjectEntry->Desc.guidObject)) {
650 TRACE(": found it by object GUID\n");
651 if ((pObjectEntry->Desc.dwValidData & DMUS_OBJ_LOADED) && pObjectEntry->pObject)
652 result = S_FALSE;
653 else
654 result = S_OK;
655 break;
657 else if ((Desc.dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH)) &&
658 (pObjectEntry->Desc.dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH)) &&
659 !strncmpW (Desc.wszFileName, pObjectEntry->Desc.wszFileName, DMUS_MAX_FILENAME)) {
660 TRACE(": found it by fullpath filename\n");
661 if ((pObjectEntry->Desc.dwValidData & DMUS_OBJ_LOADED) && pObjectEntry->pObject)
662 result = S_FALSE;
663 else
664 result = S_OK;
665 break;
667 else if ((Desc.dwValidData & (DMUS_OBJ_NAME | DMUS_OBJ_CATEGORY)) &&
668 (pObjectEntry->Desc.dwValidData & (DMUS_OBJ_NAME | DMUS_OBJ_CATEGORY)) &&
669 !strncmpW (Desc.wszName, pObjectEntry->Desc.wszName, DMUS_MAX_NAME) &&
670 !strncmpW (Desc.wszCategory, pObjectEntry->Desc.wszCategory, DMUS_MAX_CATEGORY)) {
671 TRACE(": found it by name and category\n");
672 if ((pObjectEntry->Desc.dwValidData & DMUS_OBJ_LOADED) && pObjectEntry->pObject)
673 result = S_FALSE;
674 else
675 result = S_OK;
676 break;
678 else if ((Desc.dwValidData & DMUS_OBJ_NAME) &&
679 (pObjectEntry->Desc.dwValidData & DMUS_OBJ_NAME) &&
680 !strncmpW (Desc.wszName, pObjectEntry->Desc.wszName, DMUS_MAX_NAME)) {
681 TRACE(": found it by name\n");
682 if ((pObjectEntry->Desc.dwValidData & DMUS_OBJ_LOADED) && pObjectEntry->pObject)
683 result = S_FALSE;
684 else
685 result = S_OK;
686 break;
688 else if ((Desc.dwValidData & DMUS_OBJ_FILENAME) &&
689 (pObjectEntry->Desc.dwValidData & DMUS_OBJ_FILENAME) &&
690 !strncmpW (Desc.wszFileName, pObjectEntry->Desc.wszFileName, DMUS_MAX_FILENAME)) {
691 TRACE(": found it by filename\n");
692 if ((pObjectEntry->Desc.dwValidData & DMUS_OBJ_LOADED) && pObjectEntry->pObject)
693 result = S_FALSE;
694 else
695 result = S_OK;
696 break;
700 /* if we found such alias, then set everything */
701 if (result == S_OK) {
702 pObjectEntry->Desc.dwValidData &= DMUS_OBJ_LOADED;
703 pObjectEntry->pObject = pObject;
704 IDirectMusicObject_AddRef (pObjectEntry->pObject);
707 return result;
710 static HRESULT WINAPI IDirectMusicLoaderImpl_ReleaseObject(IDirectMusicLoader8 *iface, IDirectMusicObject *pObject)
712 IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface);
713 DMUS_OBJECTDESC Desc;
714 struct cache_entry *pObjectEntry;
715 HRESULT result = S_FALSE;
717 TRACE("(%p, %p)\n", This, pObject);
719 if(!pObject) return E_POINTER;
721 /* get descriptor */
722 DM_STRUCT_INIT(&Desc);
723 IDirectMusicObject_GetDescriptor (pObject, &Desc);
725 /* iterate through the list of objects we know about; check only those with DMUS_OBJ_LOADED */
726 TRACE(": looking for the object in cache\n");
727 LIST_FOR_EACH_ENTRY(pObjectEntry, &This->cache, struct cache_entry, entry) {
728 if ((Desc.dwValidData & DMUS_OBJ_OBJECT) &&
729 (pObjectEntry->Desc.dwValidData & (DMUS_OBJ_OBJECT | DMUS_OBJ_LOADED)) &&
730 IsEqualGUID (&Desc.guidObject, &pObjectEntry->Desc.guidObject)) {
731 TRACE(": found it by object GUID\n");
732 if (TRACE_ON(dmloader))
733 dump_DMUS_OBJECTDESC(&pObjectEntry->Desc);
734 result = S_OK;
735 break;
737 else if ((Desc.dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH)) &&
738 (pObjectEntry->Desc.dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH | DMUS_OBJ_LOADED)) &&
739 !strncmpW (Desc.wszFileName, pObjectEntry->Desc.wszFileName, DMUS_MAX_FILENAME)) {
740 TRACE(": found it by fullpath filename\n");
741 result = S_OK;
742 break;
744 else if ((Desc.dwValidData & (DMUS_OBJ_NAME | DMUS_OBJ_CATEGORY)) &&
745 (pObjectEntry->Desc.dwValidData & (DMUS_OBJ_NAME | DMUS_OBJ_CATEGORY | DMUS_OBJ_LOADED)) &&
746 !strncmpW (Desc.wszName, pObjectEntry->Desc.wszName, DMUS_MAX_NAME) &&
747 !strncmpW (Desc.wszCategory, pObjectEntry->Desc.wszCategory, DMUS_MAX_CATEGORY)) {
748 TRACE(": found it by name and category\n");
749 result = S_OK;
750 break;
752 else if ((Desc.dwValidData & DMUS_OBJ_NAME) &&
753 (pObjectEntry->Desc.dwValidData & (DMUS_OBJ_NAME | DMUS_OBJ_LOADED)) &&
754 !strncmpW (Desc.wszName, pObjectEntry->Desc.wszName, DMUS_MAX_NAME)) {
755 TRACE(": found it by name\n");
756 result = S_OK;
757 break;
759 else if ((Desc.dwValidData & DMUS_OBJ_FILENAME) &&
760 (pObjectEntry->Desc.dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_LOADED)) &&
761 !strncmpW (Desc.wszFileName, pObjectEntry->Desc.wszFileName, DMUS_MAX_FILENAME)) {
762 TRACE(": found it by filename\n");
763 result = S_OK;
764 break;
767 if (result == S_OK) {
768 /*TRACE(": releasing:\n%s - bInvalidDefaultDLS = %i\n - pObject = %p\n", debugstr_DMUS_OBJECTDESC(&pObjectEntry->Desc), pObjectEntry->bInvalidDefaultDLS, pObjectEntry->pObject); */
769 IDirectMusicObject_Release (pObjectEntry->pObject);
770 pObjectEntry->pObject = NULL;
771 pObjectEntry->Desc.dwValidData &= ~DMUS_OBJ_LOADED;
773 return result;
776 static HRESULT WINAPI IDirectMusicLoaderImpl_ClearCache(IDirectMusicLoader8 *iface, REFGUID class)
778 IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface);
779 struct cache_entry *obj, *obj2;
781 TRACE("(%p, %s)\n", This, debugstr_dmguid(class));
783 LIST_FOR_EACH_ENTRY_SAFE(obj, obj2, &This->cache, struct cache_entry, entry) {
784 if ((IsEqualGUID(class, &GUID_DirectMusicAllTypes) || IsEqualGUID(class, &obj->Desc.guidClass)) &&
785 (obj->Desc.dwValidData & DMUS_OBJ_LOADED)) {
786 /* basically, wrap to ReleaseObject for each object found */
787 IDirectMusicLoader8_ReleaseObject(iface, obj->pObject);
788 list_remove(&obj->entry);
789 HeapFree(GetProcessHeap(), 0, obj);
793 return S_OK;
796 static HRESULT WINAPI IDirectMusicLoaderImpl_EnableCache(IDirectMusicLoader8 *iface, REFGUID class,
797 BOOL enable)
799 IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface);
800 BOOL current;
802 TRACE("(%p, %s, %d)\n", This, debugstr_dmguid(class), enable);
804 current = is_cache_enabled(This, class);
806 if (IsEqualGUID(class, &GUID_DirectMusicAllTypes))
807 This->cache_class = enable ? ~0 : 0;
808 else {
809 if (enable)
810 This->cache_class |= 1 << index_from_class(class);
811 else
812 This->cache_class &= ~(1 << index_from_class(class));
815 if (!enable)
816 IDirectMusicLoader8_ClearCache(iface, class);
818 if (current == enable)
819 return S_FALSE;
821 return S_OK;
824 static HRESULT WINAPI IDirectMusicLoaderImpl_EnumObject(IDirectMusicLoader8 *iface, REFGUID rguidClass, DWORD dwIndex, DMUS_OBJECTDESC *pDesc)
826 IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface);
827 DWORD dwCount = 0;
828 struct cache_entry *pObjectEntry;
829 TRACE("(%p, %s, %d, %p)\n", This, debugstr_dmguid(rguidClass), dwIndex, pDesc);
831 DM_STRUCT_INIT(pDesc);
833 LIST_FOR_EACH_ENTRY(pObjectEntry, &This->cache, struct cache_entry, entry) {
834 if (IsEqualGUID (rguidClass, &GUID_DirectMusicAllTypes) || IsEqualGUID (rguidClass, &pObjectEntry->Desc.guidClass)) {
835 if (dwCount == dwIndex) {
836 *pDesc = pObjectEntry->Desc;
837 /* we aren't supposed to reveal this info */
838 pDesc->dwValidData &= ~(DMUS_OBJ_MEMORY | DMUS_OBJ_STREAM);
839 pDesc->pbMemData = NULL;
840 pDesc->llMemLength = 0;
841 pDesc->pStream = NULL;
842 return S_OK;
844 dwCount++;
848 TRACE(": not found\n");
849 return S_FALSE;
852 static void WINAPI IDirectMusicLoaderImpl_CollectGarbage(IDirectMusicLoader8 *iface)
854 FIXME("(%p)->(): stub\n", iface);
857 static HRESULT WINAPI IDirectMusicLoaderImpl_ReleaseObjectByUnknown(IDirectMusicLoader8 *iface, IUnknown *pObject)
859 IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface);
860 HRESULT result;
861 LPDIRECTMUSICOBJECT pObjectInterface;
863 TRACE("(%p, %p)\n", This, pObject);
865 if (IsBadReadPtr (pObject, sizeof(*pObject))) {
866 ERR(": pObject bad write pointer\n");
867 return E_POINTER;
869 /* we simply get IDirectMusicObject interface */
870 result = IUnknown_QueryInterface (pObject, &IID_IDirectMusicObject, (LPVOID*)&pObjectInterface);
871 if (FAILED(result)) return result;
872 /* and release it in old-fashioned way */
873 result = IDirectMusicLoader8_ReleaseObject (iface, pObjectInterface);
874 IDirectMusicObject_Release (pObjectInterface);
876 return result;
879 static HRESULT WINAPI IDirectMusicLoaderImpl_LoadObjectFromFile(IDirectMusicLoader8 *iface, REFGUID rguidClassID, REFIID iidInterfaceID, WCHAR *pwzFilePath, void **ppObject)
881 IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface);
882 DMUS_OBJECTDESC ObjDesc;
883 WCHAR wszLoaderSearchPath[MAX_PATH];
885 TRACE("(%p, %s, %s, %s, %p): wrapping to IDirectMusicLoaderImpl_GetObject\n", This, debugstr_dmguid(rguidClassID), debugstr_dmguid(iidInterfaceID), debugstr_w(pwzFilePath), ppObject);
887 DM_STRUCT_INIT(&ObjDesc);
888 ObjDesc.dwValidData = DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH | DMUS_OBJ_CLASS; /* I believe I've read somewhere in MSDN that this function requires either full path or relative path */
889 ObjDesc.guidClass = *rguidClassID;
890 /* OK, MSDN says that search order is the following:
891 - current directory (DONE)
892 - windows search path (FIXME: how do I get that?)
893 - loader's search path (DONE)
895 get_search_path(This, rguidClassID, wszLoaderSearchPath);
896 /* search in current directory */
897 if (!SearchPathW (NULL, pwzFilePath, NULL, sizeof(ObjDesc.wszFileName)/sizeof(WCHAR), ObjDesc.wszFileName, NULL) &&
898 /* search in loader's search path */
899 !SearchPathW (wszLoaderSearchPath, pwzFilePath, NULL, sizeof(ObjDesc.wszFileName)/sizeof(WCHAR), ObjDesc.wszFileName, NULL)) {
900 /* cannot find file */
901 TRACE(": cannot find file\n");
902 return DMUS_E_LOADER_FAILEDOPEN;
905 TRACE(": full file path = %s\n", debugstr_w (ObjDesc.wszFileName));
907 return IDirectMusicLoader_GetObject(iface, &ObjDesc, iidInterfaceID, ppObject);
910 static const IDirectMusicLoader8Vtbl DirectMusicLoader_Loader_Vtbl = {
911 IDirectMusicLoaderImpl_QueryInterface,
912 IDirectMusicLoaderImpl_AddRef,
913 IDirectMusicLoaderImpl_Release,
914 IDirectMusicLoaderImpl_GetObject,
915 IDirectMusicLoaderImpl_SetObject,
916 IDirectMusicLoaderImpl_SetSearchDirectory,
917 IDirectMusicLoaderImpl_ScanDirectory,
918 IDirectMusicLoaderImpl_CacheObject,
919 IDirectMusicLoaderImpl_ReleaseObject,
920 IDirectMusicLoaderImpl_ClearCache,
921 IDirectMusicLoaderImpl_EnableCache,
922 IDirectMusicLoaderImpl_EnumObject,
923 IDirectMusicLoaderImpl_CollectGarbage,
924 IDirectMusicLoaderImpl_ReleaseObjectByUnknown,
925 IDirectMusicLoaderImpl_LoadObjectFromFile
928 /* help function for DMUSIC_SetDefaultDLS */
929 static HRESULT DMUSIC_GetDefaultGMPath (WCHAR wszPath[MAX_PATH]) {
930 HKEY hkDM;
931 DWORD returnType, sizeOfReturnBuffer = MAX_PATH;
932 char szPath[MAX_PATH];
934 if ((RegOpenKeyExA (HKEY_LOCAL_MACHINE, "Software\\Microsoft\\DirectMusic" , 0, KEY_READ, &hkDM) != ERROR_SUCCESS) ||
935 (RegQueryValueExA (hkDM, "GMFilePath", NULL, &returnType, (LPBYTE) szPath, &sizeOfReturnBuffer) != ERROR_SUCCESS)) {
936 WARN(": registry entry missing\n" );
937 return E_FAIL;
939 /* FIXME: Check return types to ensure we're interpreting data right */
940 MultiByteToWideChar (CP_ACP, 0, szPath, -1, wszPath, MAX_PATH);
942 return S_OK;
945 /* for ClassFactory */
946 HRESULT WINAPI create_dmloader(REFIID lpcGUID, void **ppobj)
948 IDirectMusicLoaderImpl *obj;
949 DMUS_OBJECTDESC Desc;
950 struct cache_entry *dls;
951 struct list *pEntry;
953 TRACE("(%s, %p)\n", debugstr_dmguid(lpcGUID), ppobj);
954 obj = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IDirectMusicLoaderImpl));
955 if (NULL == obj) {
956 *ppobj = NULL;
957 return E_OUTOFMEMORY;
959 obj->IDirectMusicLoader8_iface.lpVtbl = &DirectMusicLoader_Loader_Vtbl;
960 obj->ref = 0; /* Will be inited with QueryInterface */
961 list_init(&obj->cache);
962 /* Caching is enabled by default for all classes */
963 obj->cache_class = ~0;
965 /* set default DLS collection (via SetObject... so that loading via DMUS_OBJ_OBJECT is possible) */
966 DM_STRUCT_INIT(&Desc);
967 Desc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH | DMUS_OBJ_OBJECT;
968 Desc.guidClass = CLSID_DirectMusicCollection;
969 Desc.guidObject = GUID_DefaultGMCollection;
970 DMUSIC_GetDefaultGMPath (Desc.wszFileName);
971 IDirectMusicLoader_SetObject(&obj->IDirectMusicLoader8_iface, &Desc);
972 /* and now the workaroundTM for "invalid" default DLS; basically,
973 my tests showed that if GUID chunk is present in default DLS
974 collection, loader treats it as "invalid" and returns
975 DMUS_E_LOADER_NOFILENAME for all requests for it; basically, we check
976 if out input guidObject was overwritten */
977 pEntry = list_head(&obj->cache);
978 dls = LIST_ENTRY(pEntry, struct cache_entry, entry);
979 if (!IsEqualGUID(&Desc.guidObject, &GUID_DefaultGMCollection)) {
980 dls->bInvalidDefaultDLS = TRUE;
983 lock_module();
985 return IDirectMusicLoader_QueryInterface(&obj->IDirectMusicLoader8_iface, lpcGUID, ppobj);