1 /* IDirectMusicLoaderImpl
3 * Copyright (C) 2003-2004 Rok Mandeljc
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 #include "dmloader_private.h"
22 WINE_DEFAULT_DEBUG_CHANNEL(dmloader);
24 static inline IDirectMusicLoaderImpl* impl_from_IDirectMusicLoader8(IDirectMusicLoader8 *iface)
26 return CONTAINING_RECORD(iface, IDirectMusicLoaderImpl, IDirectMusicLoader8_iface);
29 static HRESULT DMUSIC_InitLoaderSettings(IDirectMusicLoader8 *iface);
30 static HRESULT DMUSIC_GetLoaderSettings(IDirectMusicLoader8 *iface, REFGUID class_id, WCHAR *search_path, BOOL *cache);
31 static HRESULT DMUSIC_SetLoaderSettings(IDirectMusicLoader8 *iface, REFGUID class_id, WCHAR *search_path, BOOL *cache);
33 static HRESULT DMUSIC_CopyDescriptor(DMUS_OBJECTDESC *pDst, DMUS_OBJECTDESC *pSrc)
35 TRACE(": copy\n%s\n", debugstr_DMUS_OBJECTDESC(pSrc));
36 /* copy field by field */
37 if (pSrc->dwValidData & DMUS_OBJ_CLASS) pDst->guidClass = pSrc->guidClass;
38 if (pSrc->dwValidData & DMUS_OBJ_OBJECT) pDst->guidObject = pSrc->guidObject;
39 if (pSrc->dwValidData & DMUS_OBJ_DATE) pDst->ftDate = pSrc->ftDate;
40 if (pSrc->dwValidData & DMUS_OBJ_VERSION) pDst->vVersion = pSrc->vVersion;
41 if (pSrc->dwValidData & DMUS_OBJ_NAME) strcpyW (pDst->wszName, pSrc->wszName);
42 if (pSrc->dwValidData & DMUS_OBJ_CATEGORY) strcpyW (pDst->wszCategory, pSrc->wszCategory);
43 if (pSrc->dwValidData & DMUS_OBJ_FILENAME) strcpyW (pDst->wszFileName, pSrc->wszFileName);
44 if (pSrc->dwValidData & DMUS_OBJ_STREAM) IStream_Clone (pSrc->pStream, &pDst->pStream);
45 if (pSrc->dwValidData & DMUS_OBJ_MEMORY) {
46 pDst->pbMemData = pSrc->pbMemData;
47 pDst->llMemLength = pSrc->llMemLength;
50 pDst->dwValidData |= pSrc->dwValidData;
55 static BOOL DMUSIC_IsValidLoadableClass (REFCLSID pClassID) {
56 if (IsEqualCLSID(pClassID, &CLSID_DirectMusicAudioPathConfig) ||
57 IsEqualCLSID(pClassID, &CLSID_DirectMusicBand) ||
58 IsEqualCLSID(pClassID, &CLSID_DirectMusicContainer) ||
59 IsEqualCLSID(pClassID, &CLSID_DirectMusicCollection) ||
60 IsEqualCLSID(pClassID, &CLSID_DirectMusicChordMap) ||
61 IsEqualCLSID(pClassID, &CLSID_DirectMusicSegment) ||
62 IsEqualCLSID(pClassID, &CLSID_DirectMusicScript) ||
63 IsEqualCLSID(pClassID, &CLSID_DirectMusicSong) ||
64 IsEqualCLSID(pClassID, &CLSID_DirectMusicStyle) ||
65 IsEqualCLSID(pClassID, &CLSID_DirectMusicGraph) ||
66 IsEqualCLSID(pClassID, &CLSID_DirectSoundWave) ||
67 IsEqualCLSID(pClassID, &GUID_DirectMusicAllTypes))
73 /*****************************************************************************
74 * IDirectMusicLoaderImpl implementation
76 /* IUnknown/IDirectMusicLoader(8) part: */
78 static HRESULT WINAPI IDirectMusicLoaderImpl_QueryInterface(IDirectMusicLoader8 *iface, REFIID riid, void **ppobj)
80 IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface);
82 TRACE("(%p, %s, %p)\n",This, debugstr_dmguid(riid), ppobj);
83 if (IsEqualIID (riid, &IID_IUnknown) ||
84 IsEqualIID (riid, &IID_IDirectMusicLoader) ||
85 IsEqualIID (riid, &IID_IDirectMusicLoader8)) {
86 IDirectMusicLoader_AddRef (iface);
91 WARN(": not found\n");
95 static ULONG WINAPI IDirectMusicLoaderImpl_AddRef(IDirectMusicLoader8 *iface)
97 IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface);
98 ULONG ref = InterlockedIncrement(&This->ref);
100 TRACE("(%p)->(): new ref = %u\n", iface, ref);
105 static ULONG WINAPI IDirectMusicLoaderImpl_Release(IDirectMusicLoader8 *iface)
107 IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface);
108 ULONG ref = InterlockedDecrement(&This->ref);
110 TRACE("(%p)->(): new ref = %u\n", iface, ref);
113 /* Firstly, release the cache */
114 IDirectMusicLoader8_ClearCache(iface, &GUID_DirectMusicAllTypes);
115 /* FIXME: Release all allocated entries */
116 HeapFree(GetProcessHeap(), 0, This);
123 static HRESULT WINAPI IDirectMusicLoaderImpl_GetObject(IDirectMusicLoader8 *iface, DMUS_OBJECTDESC *pDesc, REFIID riid, void **ppv)
125 IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface);
126 HRESULT result = S_OK;
127 HRESULT ret = S_OK; /* used at the end of function, to determine whether everything went OK */
130 LPWINE_LOADER_ENTRY pObjectEntry = NULL;
132 IPersistStream* pPersistStream = NULL;
134 LPDIRECTMUSICOBJECT pObject;
135 DMUS_OBJECTDESC GotDesc;
138 TRACE("(%p, %p, %s, %p): pDesc:\n%s\n", This, pDesc, debugstr_dmguid(riid), ppv, debugstr_DMUS_OBJECTDESC(pDesc));
140 /* sometimes it happens that guidClass is missing... which is a BadThingTM */
141 if (!(pDesc->dwValidData & DMUS_OBJ_CLASS)) {
142 ERR(": guidClass not valid but needed\n");
144 return DMUS_E_LOADER_NOCLASSID;
147 /* OK, first we iterate through the list of objects we know about; these are either loaded (GetObject, LoadObjectFromFile)
148 or set via SetObject; */
149 TRACE(": looking if we have object in the cache or if it can be found via alias\n");
150 LIST_FOR_EACH(pEntry, This->pObjects) {
151 LPWINE_LOADER_ENTRY pExistingEntry = LIST_ENTRY(pEntry, WINE_LOADER_ENTRY, entry);
152 if ((pDesc->dwValidData & DMUS_OBJ_OBJECT) &&
153 (pExistingEntry->Desc.dwValidData & DMUS_OBJ_OBJECT) &&
154 IsEqualGUID (&pDesc->guidObject, &pExistingEntry->Desc.guidObject)) {
155 TRACE(": found it by object GUID\n");
156 /* I suppose such stuff can happen only when GUID for object is given (GUID_DefaultGMCollection) */
157 if (pExistingEntry->bInvalidDefaultDLS) {
158 TRACE(": found faulty default DLS collection... enabling M$ compliant behaviour\n");
159 return DMUS_E_LOADER_NOFILENAME;
161 if (pExistingEntry->Desc.dwValidData & DMUS_OBJ_LOADED) {
162 TRACE(": already loaded\n");
163 return IDirectMusicObject_QueryInterface (pExistingEntry->pObject, riid, ppv);
165 TRACE(": not loaded yet\n");
166 pObjectEntry = pExistingEntry;
169 else if ((pDesc->dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH)) &&
170 (pExistingEntry->Desc.dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH)) &&
171 !strncmpW (pDesc->wszFileName, pExistingEntry->Desc.wszFileName, DMUS_MAX_FILENAME)) {
172 TRACE(": found it by fullpath filename\n");
173 if (pExistingEntry->Desc.dwValidData & DMUS_OBJ_LOADED) {
174 TRACE(": already loaded\n");
175 return IDirectMusicObject_QueryInterface (pExistingEntry->pObject, riid, ppv);
177 TRACE(": not loaded yet\n");
178 pObjectEntry = pExistingEntry;
181 else if ((pDesc->dwValidData & (DMUS_OBJ_NAME | DMUS_OBJ_CATEGORY)) &&
182 (pExistingEntry->Desc.dwValidData & (DMUS_OBJ_NAME | DMUS_OBJ_CATEGORY)) &&
183 !strncmpW (pDesc->wszName, pExistingEntry->Desc.wszName, DMUS_MAX_NAME) &&
184 !strncmpW (pDesc->wszCategory, pExistingEntry->Desc.wszCategory, DMUS_MAX_CATEGORY)) {
185 TRACE(": found it by name and category\n");
186 if (pExistingEntry->Desc.dwValidData & DMUS_OBJ_LOADED) {
187 TRACE(": already loaded\n");
188 return IDirectMusicObject_QueryInterface (pExistingEntry->pObject, riid, ppv);
190 TRACE(": not loaded yet\n");
191 pObjectEntry = pExistingEntry;
194 else if ((pDesc->dwValidData & DMUS_OBJ_NAME) &&
195 (pExistingEntry->Desc.dwValidData & DMUS_OBJ_NAME) &&
196 !strncmpW (pDesc->wszName, pExistingEntry->Desc.wszName, DMUS_MAX_NAME)) {
197 TRACE(": found it by name\n");
198 if (pExistingEntry->Desc.dwValidData & DMUS_OBJ_LOADED) {
199 TRACE(": already loaded\n");
200 return IDirectMusicObject_QueryInterface (pExistingEntry->pObject, riid, ppv);
202 TRACE(": not loaded yet\n");
203 pObjectEntry = pExistingEntry;
206 else if ((pDesc->dwValidData & DMUS_OBJ_FILENAME) &&
207 (pExistingEntry->Desc.dwValidData & DMUS_OBJ_FILENAME) &&
208 !strncmpW (pDesc->wszFileName, pExistingEntry->Desc.wszFileName, DMUS_MAX_FILENAME)) {
209 TRACE(": found it by filename\n");
210 if (pExistingEntry->Desc.dwValidData & DMUS_OBJ_LOADED) {
211 TRACE(": already loaded\n");
212 return IDirectMusicObject_QueryInterface (pExistingEntry->pObject, riid, ppv);
214 TRACE(": not loaded yet\n");
215 pObjectEntry = pExistingEntry;
220 /* basically, if we found alias, we use its descriptor to load...
221 else we use info we were given */
223 TRACE(": found alias entry for requested object... using stored info\n");
224 /* I think in certain cases it can happen that entry's descriptor lacks info about
225 where to load from (e.g.: if we loaded from stream and then released object
226 from cache; then only its CLSID, GUID and perhaps name are left); so just
227 overwrite whatever info the entry has (since it ought to be 100% correct) */
228 DMUSIC_CopyDescriptor (pDesc, &pObjectEntry->Desc);
229 /*pDesc = &pObjectEntry->Desc; */ /* FIXME: is this OK? */
231 TRACE(": no cache/alias entry found for requested object\n");
234 if (pDesc->dwValidData & DMUS_OBJ_URL) {
235 TRACE(": loading from URLs not supported yet\n");
236 return DMUS_E_LOADER_FORMATNOTSUPPORTED;
238 else if (pDesc->dwValidData & DMUS_OBJ_FILENAME) {
239 /* load object from file */
240 /* generate filename; if it's full path, don't add search
241 directory path, otherwise do */
242 WCHAR wszFileName[MAX_PATH];
244 if (pDesc->dwValidData & DMUS_OBJ_FULLPATH) {
245 lstrcpyW(wszFileName, pDesc->wszFileName);
247 WCHAR *p, wszSearchPath[MAX_PATH];
248 DMUSIC_GetLoaderSettings (iface, &pDesc->guidClass, wszSearchPath, NULL);
249 lstrcpyW(wszFileName, wszSearchPath);
250 p = wszFileName + lstrlenW(wszFileName);
251 if (p > wszFileName && p[-1] != '\\') *p++ = '\\';
252 strcpyW(p, pDesc->wszFileName);
254 TRACE(": loading from file (%s)\n", debugstr_w(wszFileName));
255 /* create stream and associate it with file */
256 result = DMUSIC_CreateDirectMusicLoaderFileStream ((LPVOID*)&pStream);
257 if (FAILED(result)) {
258 ERR(": could not create file stream\n");
261 result = IDirectMusicLoaderFileStream_Attach (pStream, wszFileName, iface);
262 if (FAILED(result)) {
263 ERR(": could not attach stream to file\n");
264 IStream_Release (pStream);
268 else if (pDesc->dwValidData & DMUS_OBJ_MEMORY) {
269 /* load object from resource */
270 TRACE(": loading from resource\n");
271 /* create stream and associate it with given resource */
272 result = DMUSIC_CreateDirectMusicLoaderResourceStream ((LPVOID*)&pStream);
273 if (FAILED(result)) {
274 ERR(": could not create resource stream\n");
277 result = IDirectMusicLoaderResourceStream_Attach (pStream, pDesc->pbMemData, pDesc->llMemLength, 0, iface);
278 if (FAILED(result)) {
279 ERR(": could not attach stream to resource\n");
280 IStream_Release (pStream);
284 else if (pDesc->dwValidData & DMUS_OBJ_STREAM) {
285 /* load object from stream */
286 TRACE(": loading from stream\n");
287 /* create universal stream and associate it with given one */
288 result = DMUSIC_CreateDirectMusicLoaderGenericStream ((LPVOID*)&pStream);
289 if (FAILED(result)) {
290 ERR(": could not create generic stream\n");
293 result = IDirectMusicLoaderGenericStream_Attach (pStream, pDesc->pStream, iface);
294 if (FAILED(result)) {
295 ERR(": failed to attach stream\n");
296 IStream_Release (pStream);
300 /* nowhere to load from */
301 FIXME(": unknown/unsupported way of loading\n");
302 return DMUS_E_LOADER_NOFILENAME; /* test shows this is returned */
306 result = CoCreateInstance (&pDesc->guidClass, NULL, CLSCTX_INPROC_SERVER, &IID_IDirectMusicObject, (LPVOID*)&pObject);
307 if (FAILED(result)) {
308 ERR(": could not create object\n");
311 /* acquire PersistStream interface */
312 result = IDirectMusicObject_QueryInterface (pObject, &IID_IPersistStream, (LPVOID*)&pPersistStream);
313 if (FAILED(result)) {
314 ERR("failed to Query\n");
318 result = IPersistStream_Load (pPersistStream, pStream);
319 if (result != S_OK) {
320 WARN(": failed to (completely) load object (%s)\n", debugstr_dmreturn(result));
324 DM_STRUCT_INIT(&GotDesc);
325 result = IDirectMusicObject_GetDescriptor (pObject, &GotDesc);
326 /* set filename (if we loaded via filename) */
327 if (pDesc->dwValidData & DMUS_OBJ_FILENAME) {
328 GotDesc.dwValidData |= (pDesc->dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH));
329 strcpyW (GotDesc.wszFileName, pDesc->wszFileName);
331 if (FAILED(result)) {
332 ERR(": failed to get descriptor\n");
335 /* release all loading related stuff */
336 IStream_Release (pStream);
337 IPersistStream_Release (pPersistStream);
339 /* add object to cache/overwrite existing info (if cache is enabled) */
340 DMUSIC_GetLoaderSettings (iface, &pDesc->guidClass, NULL, &bCache);
343 pObjectEntry = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof(WINE_LOADER_ENTRY));
344 DM_STRUCT_INIT(&pObjectEntry->Desc);
345 DMUSIC_CopyDescriptor (&pObjectEntry->Desc, &GotDesc);
346 pObjectEntry->pObject = pObject;
347 pObjectEntry->bInvalidDefaultDLS = FALSE;
348 list_add_head (This->pObjects, &pObjectEntry->entry);
350 DMUSIC_CopyDescriptor (&pObjectEntry->Desc, &GotDesc);
351 pObjectEntry->pObject = pObject;
352 pObjectEntry->bInvalidDefaultDLS = FALSE;
354 TRACE(": filled in cache entry\n");
355 } else TRACE(": caching disabled\n");
358 /* for debug purposes (e.g. to check if all files are cached) */
359 TRACE("*** Loader's cache ***\n");
361 LIST_FOR_EACH (pEntry, This->pObjects) {
363 pObjectEntry = LIST_ENTRY(pEntry, WINE_LOADER_ENTRY, entry);
364 TRACE(": entry nr. %i:\n%s\n - bInvalidDefaultDLS = %i\n - pObject = %p\n", i, debugstr_DMUS_OBJECTDESC(&pObjectEntry->Desc), pObjectEntry->bInvalidDefaultDLS, pObjectEntry->pObject);
368 result = IDirectMusicObject_QueryInterface (pObject, riid, ppv);
369 if (!bCache) IDirectMusicObject_Release (pObject); /* since loader's reference is not needed */
370 /* if there was trouble with loading, and if no other error occurred,
371 we should return DMUS_S_PARTIALLOAD; else, error is returned */
378 static HRESULT WINAPI IDirectMusicLoaderImpl_SetObject(IDirectMusicLoader8 *iface, DMUS_OBJECTDESC *pDesc)
380 IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface);
382 LPDIRECTMUSICOBJECT pObject;
383 DMUS_OBJECTDESC Desc;
385 LPWINE_LOADER_ENTRY pObjectEntry, pNewEntry;
388 TRACE("(%p, %p): pDesc:\n%s\n", This, pDesc, debugstr_DMUS_OBJECTDESC(pDesc));
390 /* create stream and load additional info from it */
391 if (pDesc->dwValidData & DMUS_OBJ_FILENAME) {
392 /* generate filename; if it's full path, don't add search
393 directory path, otherwise do */
394 WCHAR wszFileName[MAX_PATH];
396 if (pDesc->dwValidData & DMUS_OBJ_FULLPATH) {
397 lstrcpyW(wszFileName, pDesc->wszFileName);
400 WCHAR wszSearchPath[MAX_PATH];
401 DMUSIC_GetLoaderSettings (iface, &pDesc->guidClass, wszSearchPath, NULL);
402 lstrcpyW(wszFileName, wszSearchPath);
403 p = wszFileName + lstrlenW(wszFileName);
404 if (p > wszFileName && p[-1] != '\\') *p++ = '\\';
405 strcpyW(p, pDesc->wszFileName);
408 hr = DMUSIC_CreateDirectMusicLoaderFileStream ((LPVOID*)&pStream);
410 ERR(": could not create file stream\n");
411 return DMUS_E_LOADER_FAILEDOPEN;
414 hr = IDirectMusicLoaderFileStream_Attach (pStream, wszFileName, iface);
416 ERR(": could not attach stream to file\n");
417 IStream_Release (pStream);
418 return DMUS_E_LOADER_FAILEDOPEN;
421 else if (pDesc->dwValidData & DMUS_OBJ_STREAM) {
423 hr = DMUSIC_CreateDirectMusicLoaderGenericStream ((LPVOID*)&pStream);
425 ERR(": could not create generic stream\n");
426 return DMUS_E_LOADER_FAILEDOPEN;
429 hr = IDirectMusicLoaderGenericStream_Attach (pStream, pDesc->pStream, iface);
431 ERR(": could not attach stream\n");
432 IStream_Release (pStream);
433 return DMUS_E_LOADER_FAILEDOPEN;
436 else if (pDesc->dwValidData & DMUS_OBJ_MEMORY) {
438 hr = DMUSIC_CreateDirectMusicLoaderResourceStream ((LPVOID*)&pStream);
440 ERR(": could not create resource stream\n");
441 return DMUS_E_LOADER_FAILEDOPEN;
444 hr = IDirectMusicLoaderResourceStream_Attach (pStream, pDesc->pbMemData, pDesc->llMemLength, 0, iface);
446 ERR(": could not attach stream to resource\n");
447 IStream_Release (pStream);
448 return DMUS_E_LOADER_FAILEDOPEN;
452 ERR(": no way to get additional info\n");
453 return DMUS_E_LOADER_FAILEDOPEN;
457 CoCreateInstance (&pDesc->guidClass, NULL, CLSCTX_INPROC_SERVER, &IID_IDirectMusicObject, (LPVOID*)&pObject);
459 /* *sigh*... some ms objects have lousy implementation of ParseDescriptor that clears input descriptor :( */
460 #ifdef NOW_WE_ARE_FREE
461 /* parse descriptor: we actually use input descriptor; fields that aren't available from stream remain,
462 otherwise real info is set */
463 IDirectMusicObject_ParseDescriptor (pObject, pStream, pDesc);
465 /* hmph... due to some trouble I had with certain tests, we store current position and then set it back */
466 DM_STRUCT_INIT(&Desc);
467 if (FAILED(IDirectMusicObject_ParseDescriptor (pObject, pStream, &Desc))) {
468 ERR(": couldn't parse descriptor\n");
469 return DMUS_E_LOADER_FORMATNOTSUPPORTED;
472 /* copy elements from parsed descriptor into input descriptor; this sets new info, overwriting if necessary,
473 but leaves info that's provided by input and not available from stream */
474 DMUSIC_CopyDescriptor (pDesc, &Desc);
476 /* release everything */
477 IDirectMusicObject_Release (pObject);
478 IStream_Release (pStream);
480 /* sometimes it happens that twisted programs call SetObject for same object twice...
481 in such cases, native loader returns S_OK and does nothing... a sound plan */
482 LIST_FOR_EACH (pEntry, This->pObjects) {
483 pObjectEntry = LIST_ENTRY (pEntry, WINE_LOADER_ENTRY, entry);
484 if (!memcmp (&pObjectEntry->Desc, pDesc, sizeof(DMUS_OBJECTDESC))) {
485 TRACE(": exactly same entry already exists\n");
491 TRACE(": adding alias entry with following info:\n%s\n", debugstr_DMUS_OBJECTDESC(pDesc));
492 pNewEntry = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof(WINE_LOADER_ENTRY));
493 /* use this function instead of pure memcpy due to streams (memcpy just copies pointer),
494 which is basically used further by app that called SetDescriptor... better safety than exception */
495 DMUSIC_CopyDescriptor (&pNewEntry->Desc, pDesc);
496 list_add_head (This->pObjects, &pNewEntry->entry);
501 static HRESULT WINAPI IDirectMusicLoaderImpl_SetSearchDirectory(IDirectMusicLoader8 *iface, REFGUID rguidClass, WCHAR *pwzPath, BOOL fClear)
503 IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface);
504 WCHAR wszCurrentPath[MAX_PATH];
505 TRACE("(%p, %s, %s, %d)\n", This, debugstr_dmguid(rguidClass), debugstr_w(pwzPath), fClear);
506 FIXME(": fClear ignored\n");
507 DMUSIC_GetLoaderSettings (iface, rguidClass, wszCurrentPath, NULL);
508 if (!strncmpW(wszCurrentPath, pwzPath, MAX_PATH)) {
511 /* FIXME: check if path is valid; else return DMUS_E_LOADER_BADPATH */
512 return DMUSIC_SetLoaderSettings (iface, rguidClass, pwzPath, NULL);
515 static HRESULT WINAPI IDirectMusicLoaderImpl_ScanDirectory(IDirectMusicLoader8 *iface, REFGUID rguidClass, WCHAR *pwzFileExtension, WCHAR *pwzScanFileName)
517 IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface);
518 static const WCHAR wszAny[] = {'*',0};
519 WIN32_FIND_DATAW FileData;
521 WCHAR wszSearchString[MAX_PATH];
524 TRACE("(%p, %s, %p, %p)\n", This, debugstr_dmguid(rguidClass), pwzFileExtension, pwzScanFileName);
525 if (IsEqualGUID (rguidClass, &GUID_DirectMusicAllTypes) || !DMUSIC_IsValidLoadableClass(rguidClass)) {
526 ERR(": rguidClass invalid CLSID\n");
527 return REGDB_E_CLASSNOTREG;
530 /* get search path for given class */
531 DMUSIC_GetLoaderSettings (iface, rguidClass, wszSearchString, NULL);
533 p = wszSearchString + lstrlenW(wszSearchString);
534 if (p > wszSearchString && p[-1] != '\\') *p++ = '\\';
535 *p++ = '*'; /* any file */
536 if (strcmpW (pwzFileExtension, wszAny)) *p++ = '.'; /* if we have actual extension, put a dot */
537 strcpyW (p, pwzFileExtension);
539 TRACE(": search string: %s\n", debugstr_w(wszSearchString));
541 hSearch = FindFirstFileW (wszSearchString, &FileData);
542 if (hSearch == INVALID_HANDLE_VALUE) {
543 TRACE(": no files found\n");
548 DMUS_OBJECTDESC Desc;
549 DM_STRUCT_INIT(&Desc);
550 Desc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH | DMUS_OBJ_DATE;
551 Desc.guidClass = *rguidClass;
552 strcpyW (Desc.wszFileName, FileData.cFileName);
553 FileTimeToLocalFileTime (&FileData.ftCreationTime, &Desc.ftDate);
554 IDirectMusicLoader8_SetObject (iface, &Desc);
556 if (!FindNextFileW (hSearch, &FileData)) {
557 if (GetLastError () == ERROR_NO_MORE_FILES) {
558 TRACE(": search completed\n");
561 ERR(": could not get next file\n");
570 static HRESULT WINAPI IDirectMusicLoaderImpl_CacheObject(IDirectMusicLoader8 *iface, IDirectMusicObject *pObject)
572 IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface);
573 DMUS_OBJECTDESC Desc;
574 HRESULT result = DMUS_E_LOADER_OBJECTNOTFOUND;
576 LPWINE_LOADER_ENTRY pObjectEntry = NULL;
578 TRACE("(%p, %p)\n", This, pObject);
581 DM_STRUCT_INIT(&Desc);
582 IDirectMusicObject_GetDescriptor (pObject, &Desc);
584 /* now iterate through the list and check if we have an alias (without object), corresponding
585 to the descriptor of the input object */
586 LIST_FOR_EACH(pEntry, This->pObjects) {
587 pObjectEntry = LIST_ENTRY(pEntry, WINE_LOADER_ENTRY, entry);
588 if ((Desc.dwValidData & DMUS_OBJ_OBJECT) &&
589 (pObjectEntry->Desc.dwValidData & DMUS_OBJ_OBJECT) &&
590 IsEqualGUID (&Desc.guidObject, &pObjectEntry->Desc.guidObject)) {
591 TRACE(": found it by object GUID\n");
592 if ((pObjectEntry->Desc.dwValidData & DMUS_OBJ_LOADED) && pObjectEntry->pObject)
598 else if ((Desc.dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH)) &&
599 (pObjectEntry->Desc.dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH)) &&
600 !strncmpW (Desc.wszFileName, pObjectEntry->Desc.wszFileName, DMUS_MAX_FILENAME)) {
601 TRACE(": found it by fullpath filename\n");
602 if ((pObjectEntry->Desc.dwValidData & DMUS_OBJ_LOADED) && pObjectEntry->pObject)
608 else if ((Desc.dwValidData & (DMUS_OBJ_NAME | DMUS_OBJ_CATEGORY)) &&
609 (pObjectEntry->Desc.dwValidData & (DMUS_OBJ_NAME | DMUS_OBJ_CATEGORY)) &&
610 !strncmpW (Desc.wszName, pObjectEntry->Desc.wszName, DMUS_MAX_NAME) &&
611 !strncmpW (Desc.wszCategory, pObjectEntry->Desc.wszCategory, DMUS_MAX_CATEGORY)) {
612 TRACE(": found it by name and category\n");
613 if ((pObjectEntry->Desc.dwValidData & DMUS_OBJ_LOADED) && pObjectEntry->pObject)
619 else if ((Desc.dwValidData & DMUS_OBJ_NAME) &&
620 (pObjectEntry->Desc.dwValidData & DMUS_OBJ_NAME) &&
621 !strncmpW (Desc.wszName, pObjectEntry->Desc.wszName, DMUS_MAX_NAME)) {
622 TRACE(": found it by name\n");
623 if ((pObjectEntry->Desc.dwValidData & DMUS_OBJ_LOADED) && pObjectEntry->pObject)
629 else if ((Desc.dwValidData & DMUS_OBJ_FILENAME) &&
630 (pObjectEntry->Desc.dwValidData & DMUS_OBJ_FILENAME) &&
631 !strncmpW (Desc.wszFileName, pObjectEntry->Desc.wszFileName, DMUS_MAX_FILENAME)) {
632 TRACE(": found it by filename\n");
633 if ((pObjectEntry->Desc.dwValidData & DMUS_OBJ_LOADED) && pObjectEntry->pObject)
641 /* if we found such alias, then set everything */
642 if (result == S_OK) {
643 pObjectEntry->Desc.dwValidData &= DMUS_OBJ_LOADED;
644 pObjectEntry->pObject = pObject;
645 IDirectMusicObject_AddRef (pObjectEntry->pObject);
651 static HRESULT WINAPI IDirectMusicLoaderImpl_ReleaseObject(IDirectMusicLoader8 *iface, IDirectMusicObject *pObject)
653 IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface);
654 DMUS_OBJECTDESC Desc;
656 LPWINE_LOADER_ENTRY pObjectEntry = NULL;
657 HRESULT result = S_FALSE;
659 TRACE("(%p, %p)\n", This, pObject);
661 if(!pObject) return E_POINTER;
664 DM_STRUCT_INIT(&Desc);
665 IDirectMusicObject_GetDescriptor (pObject, &Desc);
667 /* iterate through the list of objects we know about; check only those with DMUS_OBJ_LOADED */
668 TRACE(": looking for the object in cache\n");
669 LIST_FOR_EACH(pEntry, This->pObjects) {
670 pObjectEntry = LIST_ENTRY(pEntry, WINE_LOADER_ENTRY, entry);
671 if ((Desc.dwValidData & DMUS_OBJ_OBJECT) &&
672 (pObjectEntry->Desc.dwValidData & (DMUS_OBJ_OBJECT | DMUS_OBJ_LOADED)) &&
673 IsEqualGUID (&Desc.guidObject, &pObjectEntry->Desc.guidObject)) {
674 TRACE(": found it by object GUID\n%s\n", debugstr_DMUS_OBJECTDESC(&pObjectEntry->Desc));
678 else if ((Desc.dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH)) &&
679 (pObjectEntry->Desc.dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH | DMUS_OBJ_LOADED)) &&
680 !strncmpW (Desc.wszFileName, pObjectEntry->Desc.wszFileName, DMUS_MAX_FILENAME)) {
681 TRACE(": found it by fullpath filename\n");
685 else if ((Desc.dwValidData & (DMUS_OBJ_NAME | DMUS_OBJ_CATEGORY)) &&
686 (pObjectEntry->Desc.dwValidData & (DMUS_OBJ_NAME | DMUS_OBJ_CATEGORY | DMUS_OBJ_LOADED)) &&
687 !strncmpW (Desc.wszName, pObjectEntry->Desc.wszName, DMUS_MAX_NAME) &&
688 !strncmpW (Desc.wszCategory, pObjectEntry->Desc.wszCategory, DMUS_MAX_CATEGORY)) {
689 TRACE(": found it by name and category\n");
693 else if ((Desc.dwValidData & DMUS_OBJ_NAME) &&
694 (pObjectEntry->Desc.dwValidData & (DMUS_OBJ_NAME | DMUS_OBJ_LOADED)) &&
695 !strncmpW (Desc.wszName, pObjectEntry->Desc.wszName, DMUS_MAX_NAME)) {
696 TRACE(": found it by name\n");
700 else if ((Desc.dwValidData & DMUS_OBJ_FILENAME) &&
701 (pObjectEntry->Desc.dwValidData & (DMUS_OBJ_FILENAME | DMUS_OBJ_LOADED)) &&
702 !strncmpW (Desc.wszFileName, pObjectEntry->Desc.wszFileName, DMUS_MAX_FILENAME)) {
703 TRACE(": found it by filename\n");
708 if (result == S_OK) {
709 /*TRACE(": releasing:\n%s - bInvalidDefaultDLS = %i\n - pObject = %p\n", debugstr_DMUS_OBJECTDESC(&pObjectEntry->Desc), pObjectEntry->bInvalidDefaultDLS, pObjectEntry->pObject); */
710 IDirectMusicObject_Release (pObjectEntry->pObject);
711 pObjectEntry->pObject = NULL;
712 pObjectEntry->Desc.dwValidData &= ~DMUS_OBJ_LOADED;
717 static HRESULT WINAPI IDirectMusicLoaderImpl_ClearCache(IDirectMusicLoader8 *iface, REFGUID rguidClass)
719 IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface);
721 LPWINE_LOADER_ENTRY pObjectEntry;
722 TRACE("(%p, %s)\n", This, debugstr_dmguid(rguidClass));
724 LIST_FOR_EACH (pEntry, This->pObjects) {
725 pObjectEntry = LIST_ENTRY (pEntry, WINE_LOADER_ENTRY, entry);
727 if ((IsEqualGUID (rguidClass, &GUID_DirectMusicAllTypes) || IsEqualGUID (rguidClass, &pObjectEntry->Desc.guidClass)) &&
728 (pObjectEntry->Desc.dwValidData & DMUS_OBJ_LOADED)) {
729 /* basically, wrap to ReleaseObject for each object found */
730 IDirectMusicLoader8_ReleaseObject (iface, pObjectEntry->pObject);
737 static HRESULT WINAPI IDirectMusicLoaderImpl_EnableCache(IDirectMusicLoader8 *iface, REFGUID rguidClass, BOOL fEnable)
739 IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface);
741 TRACE("(%p, %s, %d)\n", This, debugstr_dmguid(rguidClass), fEnable);
742 DMUSIC_GetLoaderSettings (iface, rguidClass, NULL, &bCurrent);
743 if (bCurrent == fEnable)
746 return DMUSIC_SetLoaderSettings (iface, rguidClass, NULL, &fEnable);
749 static HRESULT WINAPI IDirectMusicLoaderImpl_EnumObject(IDirectMusicLoader8 *iface, REFGUID rguidClass, DWORD dwIndex, DMUS_OBJECTDESC *pDesc)
751 IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface);
754 LPWINE_LOADER_ENTRY pObjectEntry;
755 TRACE("(%p, %s, %d, %p)\n", This, debugstr_dmguid(rguidClass), dwIndex, pDesc);
757 DM_STRUCT_INIT(pDesc);
759 LIST_FOR_EACH (pEntry, This->pObjects) {
760 pObjectEntry = LIST_ENTRY (pEntry, WINE_LOADER_ENTRY, entry);
762 if (IsEqualGUID (rguidClass, &GUID_DirectMusicAllTypes) || IsEqualGUID (rguidClass, &pObjectEntry->Desc.guidClass)) {
763 if (dwCount == dwIndex) {
764 *pDesc = pObjectEntry->Desc;
765 /* we aren't supposed to reveal this info */
766 pDesc->dwValidData &= ~(DMUS_OBJ_MEMORY | DMUS_OBJ_STREAM);
767 pDesc->pbMemData = NULL;
768 pDesc->llMemLength = 0;
769 pDesc->pStream = NULL;
776 TRACE(": not found\n");
780 static void WINAPI IDirectMusicLoaderImpl_CollectGarbage(IDirectMusicLoader8 *iface)
782 FIXME("(%p)->(): stub\n", iface);
785 static HRESULT WINAPI IDirectMusicLoaderImpl_ReleaseObjectByUnknown(IDirectMusicLoader8 *iface, IUnknown *pObject)
787 IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface);
789 LPDIRECTMUSICOBJECT pObjectInterface;
791 TRACE("(%p, %p)\n", This, pObject);
793 if (IsBadReadPtr (pObject, sizeof(*pObject))) {
794 ERR(": pObject bad write pointer\n");
797 /* we simply get IDirectMusicObject interface */
798 result = IUnknown_QueryInterface (pObject, &IID_IDirectMusicObject, (LPVOID*)&pObjectInterface);
799 if (FAILED(result)) return result;
800 /* and release it in old-fashioned way */
801 result = IDirectMusicLoader8_ReleaseObject (iface, pObjectInterface);
802 IDirectMusicObject_Release (pObjectInterface);
807 static HRESULT WINAPI IDirectMusicLoaderImpl_LoadObjectFromFile(IDirectMusicLoader8 *iface, REFGUID rguidClassID, REFIID iidInterfaceID, WCHAR *pwzFilePath, void **ppObject)
809 IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface);
810 DMUS_OBJECTDESC ObjDesc;
811 WCHAR wszLoaderSearchPath[MAX_PATH];
813 TRACE("(%p, %s, %s, %s, %p): wrapping to IDirectMusicLoaderImpl_GetObject\n", This, debugstr_dmguid(rguidClassID), debugstr_dmguid(iidInterfaceID), debugstr_w(pwzFilePath), ppObject);
815 DM_STRUCT_INIT(&ObjDesc);
816 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 */
817 ObjDesc.guidClass = *rguidClassID;
818 /* OK, MSDN says that search order is the following:
819 - current directory (DONE)
820 - windows search path (FIXME: how do I get that?)
821 - loader's search path (DONE)
823 DMUSIC_GetLoaderSettings (iface, rguidClassID, wszLoaderSearchPath, NULL);
824 /* search in current directory */
825 if (!SearchPathW (NULL, pwzFilePath, NULL, sizeof(ObjDesc.wszFileName)/sizeof(WCHAR), ObjDesc.wszFileName, NULL) &&
826 /* search in loader's search path */
827 !SearchPathW (wszLoaderSearchPath, pwzFilePath, NULL, sizeof(ObjDesc.wszFileName)/sizeof(WCHAR), ObjDesc.wszFileName, NULL)) {
828 /* cannot find file */
829 TRACE(": cannot find file\n");
830 return DMUS_E_LOADER_FAILEDOPEN;
833 TRACE(": full file path = %s\n", debugstr_w (ObjDesc.wszFileName));
835 return IDirectMusicLoader_GetObject(iface, &ObjDesc, iidInterfaceID, ppObject);
838 static const IDirectMusicLoader8Vtbl DirectMusicLoader_Loader_Vtbl = {
839 IDirectMusicLoaderImpl_QueryInterface,
840 IDirectMusicLoaderImpl_AddRef,
841 IDirectMusicLoaderImpl_Release,
842 IDirectMusicLoaderImpl_GetObject,
843 IDirectMusicLoaderImpl_SetObject,
844 IDirectMusicLoaderImpl_SetSearchDirectory,
845 IDirectMusicLoaderImpl_ScanDirectory,
846 IDirectMusicLoaderImpl_CacheObject,
847 IDirectMusicLoaderImpl_ReleaseObject,
848 IDirectMusicLoaderImpl_ClearCache,
849 IDirectMusicLoaderImpl_EnableCache,
850 IDirectMusicLoaderImpl_EnumObject,
851 IDirectMusicLoaderImpl_CollectGarbage,
852 IDirectMusicLoaderImpl_ReleaseObjectByUnknown,
853 IDirectMusicLoaderImpl_LoadObjectFromFile
856 /* help function for DMUSIC_SetDefaultDLS */
857 static HRESULT DMUSIC_GetDefaultGMPath (WCHAR wszPath[MAX_PATH]) {
859 DWORD returnType, sizeOfReturnBuffer = MAX_PATH;
860 char szPath[MAX_PATH];
862 if ((RegOpenKeyExA (HKEY_LOCAL_MACHINE, "Software\\Microsoft\\DirectMusic" , 0, KEY_READ, &hkDM) != ERROR_SUCCESS) ||
863 (RegQueryValueExA (hkDM, "GMFilePath", NULL, &returnType, (LPBYTE) szPath, &sizeOfReturnBuffer) != ERROR_SUCCESS)) {
864 WARN(": registry entry missing\n" );
867 /* FIXME: Check return types to ensure we're interpreting data right */
868 MultiByteToWideChar (CP_ACP, 0, szPath, -1, wszPath, MAX_PATH);
873 /* for ClassFactory */
874 HRESULT WINAPI DMUSIC_CreateDirectMusicLoaderImpl(const GUID *lpcGUID, void **ppobj, IUnknown *pUnkOuter)
876 IDirectMusicLoaderImpl *obj;
877 DMUS_OBJECTDESC Desc;
878 LPWINE_LOADER_ENTRY pDefaultDLSEntry;
881 TRACE("(%s, %p, %p)\n", debugstr_dmguid(lpcGUID), ppobj, pUnkOuter);
882 obj = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IDirectMusicLoaderImpl));
885 return E_OUTOFMEMORY;
887 obj->IDirectMusicLoader8_iface.lpVtbl = &DirectMusicLoader_Loader_Vtbl;
888 obj->ref = 0; /* Will be inited with QueryInterface */
889 /* init cache/alias list */
890 obj->pObjects = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof(struct list));
891 list_init (obj->pObjects);
893 obj->pClassSettings = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof(struct list));
894 list_init (obj->pClassSettings);
895 DMUSIC_InitLoaderSettings(&obj->IDirectMusicLoader8_iface);
897 /* set default DLS collection (via SetObject... so that loading via DMUS_OBJ_OBJECT is possible) */
898 DM_STRUCT_INIT(&Desc);
899 Desc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH | DMUS_OBJ_OBJECT;
900 Desc.guidClass = CLSID_DirectMusicCollection;
901 Desc.guidObject = GUID_DefaultGMCollection;
902 DMUSIC_GetDefaultGMPath (Desc.wszFileName);
903 IDirectMusicLoader_SetObject(&obj->IDirectMusicLoader8_iface, &Desc);
904 /* and now the workaroundTM for "invalid" default DLS; basically,
905 my tests showed that if GUID chunk is present in default DLS
906 collection, loader treats it as "invalid" and returns
907 DMUS_E_LOADER_NOFILENAME for all requests for it; basically, we check
908 if out input guidObject was overwritten */
909 pEntry = list_head (obj->pObjects);
910 pDefaultDLSEntry = LIST_ENTRY (pEntry, WINE_LOADER_ENTRY, entry);
911 if (!IsEqualGUID(&Desc.guidObject, &GUID_DefaultGMCollection)) {
912 pDefaultDLSEntry->bInvalidDefaultDLS = TRUE;
917 return IDirectMusicLoader_QueryInterface(&obj->IDirectMusicLoader8_iface, lpcGUID, ppobj);
920 /* help function for retrieval of search path and caching option for certain class */
921 static HRESULT DMUSIC_GetLoaderSettings(IDirectMusicLoader8 *iface, REFGUID pClassID, WCHAR *wszSearchPath, BOOL *pbCache)
923 IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface);
925 TRACE(": (%p, %s, %p, %p)\n", This, debugstr_dmguid(pClassID), wszSearchPath, pbCache);
927 LIST_FOR_EACH(pEntry, This->pClassSettings) {
928 LPWINE_LOADER_OPTION pOptionEntry = LIST_ENTRY(pEntry, WINE_LOADER_OPTION, entry);
929 if (IsEqualCLSID (pClassID, &pOptionEntry->guidClass)) {
931 strcpyW(wszSearchPath, pOptionEntry->wszSearchPath);
933 *pbCache = pOptionEntry->bCache;
940 /* help function for setting search path and caching option for certain class */
941 static HRESULT DMUSIC_SetLoaderSettings(IDirectMusicLoader8 *iface, REFGUID pClassID, WCHAR *wszSearchPath, BOOL *pbCache)
943 IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface);
945 HRESULT result = S_FALSE; /* in case pClassID != GUID_DirectMusicAllTypes and not a valid CLSID */
946 TRACE(": (%p, %s, %p, %p)\n", This, debugstr_dmguid(pClassID), wszSearchPath, pbCache);
948 LIST_FOR_EACH(pEntry, This->pClassSettings) {
949 LPWINE_LOADER_OPTION pOptionEntry = LIST_ENTRY(pEntry, WINE_LOADER_OPTION, entry);
950 /* well, either we have GUID_DirectMusicAllTypes and need to set it to all,
951 or specific CLSID is given and we set it only to it */
952 if (IsEqualGUID (pClassID, &GUID_DirectMusicAllTypes) ||
953 IsEqualCLSID (pClassID, &pOptionEntry->guidClass)) {
955 strcpyW(pOptionEntry->wszSearchPath, wszSearchPath);
957 pOptionEntry->bCache = *pbCache;
965 static HRESULT DMUSIC_InitLoaderSettings(IDirectMusicLoader8 *iface)
967 IDirectMusicLoaderImpl *This = impl_from_IDirectMusicLoader8(iface);
969 /* hard-coded list of classes */
970 static REFCLSID classes[] = {
971 &CLSID_DirectMusicAudioPathConfig,
972 &CLSID_DirectMusicBand,
973 &CLSID_DirectMusicContainer,
974 &CLSID_DirectMusicCollection,
975 &CLSID_DirectMusicChordMap,
976 &CLSID_DirectMusicSegment,
977 &CLSID_DirectMusicScript,
978 &CLSID_DirectMusicSong,
979 &CLSID_DirectMusicStyle,
980 &CLSID_DirectMusicGraph,
981 &CLSID_DirectSoundWave
985 WCHAR wszCurrent[MAX_PATH];
987 TRACE(": (%p)\n", This);
988 GetCurrentDirectoryW (MAX_PATH, wszCurrent);
990 for (i = 0; i < sizeof(classes)/sizeof(REFCLSID); i++) {
991 LPWINE_LOADER_OPTION pNewSetting = HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof(WINE_LOADER_OPTION));
992 pNewSetting->guidClass = *classes[i];
993 strcpyW (pNewSetting->wszSearchPath, wszCurrent);
994 pNewSetting->bCache = TRUE;
995 list_add_tail (This->pClassSettings, &pNewSetting->entry);