Add support for some MCIWNDF_ styles, indicate that we do not support
[wine] / dlls / dmloader / loader.c
1 /* IDirectMusicLoader8 Implementation
2  *
3  * Copyright (C) 2003 Rok Mandeljc
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
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
13  * GNU Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 #include <stdarg.h>
21
22 #include "windef.h"
23 #include "winbase.h"
24 #include "winuser.h"
25 #include "wingdi.h"
26 #include "wine/debug.h"
27 #include "wine/unicode.h"
28 #include "winreg.h"
29
30 #include "dmloader_private.h"
31
32 WINE_DEFAULT_DEBUG_CHANNEL(dmloader);
33
34 HRESULT WINAPI DMUSIC_GetDefaultGMPath (WCHAR wszPath[MAX_PATH]);
35
36 /* IDirectMusicLoader8 IUnknown part: */
37 HRESULT WINAPI IDirectMusicLoader8Impl_QueryInterface (LPDIRECTMUSICLOADER8 iface, REFIID riid, LPVOID *ppobj)
38 {
39         ICOM_THIS(IDirectMusicLoader8Impl,iface);
40
41         if (IsEqualIID (riid, &IID_IUnknown) || 
42             IsEqualIID (riid, &IID_IDirectMusicLoader) ||
43             IsEqualIID (riid, &IID_IDirectMusicLoader8)) {
44                 IDirectMusicLoader8Impl_AddRef(iface);
45                 *ppobj = This;
46                 return S_OK;
47         }
48         
49         WARN("(%p)->(%s,%p),not found\n",This,debugstr_guid(riid),ppobj);
50         return E_NOINTERFACE;
51 }
52
53 ULONG WINAPI IDirectMusicLoader8Impl_AddRef (LPDIRECTMUSICLOADER8 iface)
54 {
55         ICOM_THIS(IDirectMusicLoader8Impl,iface);
56         TRACE("(%p) : AddRef from %ld\n", This, This->ref);
57         return ++(This->ref);
58 }
59
60 ULONG WINAPI IDirectMusicLoader8Impl_Release (LPDIRECTMUSICLOADER8 iface)
61 {
62         ICOM_THIS(IDirectMusicLoader8Impl,iface);
63         ULONG ref = --This->ref;
64         TRACE("(%p) : ReleaseRef to %ld\n", This, This->ref);
65         if (ref == 0) {
66                 HeapFree(GetProcessHeap(), 0, This);
67         }
68         return ref;
69 }
70
71 /* IDirectMusicLoader8 IDirectMusicLoader part: */
72 HRESULT WINAPI IDirectMusicLoader8Impl_GetObject (LPDIRECTMUSICLOADER8 iface, LPDMUS_OBJECTDESC pDesc, REFIID riid, LPVOID* ppv)
73 {
74         IDirectMusicObject* pObject;
75         ICOM_THIS(IDirectMusicLoader8Impl,iface);
76         HRESULT result;
77         struct list *listEntry;
78         DMUS_PRIVATE_CACHE_ENTRY *cacheEntry;
79         LPDMUS_PRIVATE_CACHE_ENTRY newEntry;
80
81         TRACE("(%p, %p(dwValidData:0x%08lx), %s, %p)\n", This, pDesc, pDesc->dwValidData, debugstr_guid(riid), ppv);
82
83         TRACE("looking up cache...\n");
84
85         LIST_FOR_EACH (listEntry, &This->CacheList) {
86                 cacheEntry = LIST_ENTRY( listEntry, DMUS_PRIVATE_CACHE_ENTRY, entry );
87                 
88                 if ((pDesc->dwValidData & DMUS_OBJ_OBJECT) || (pDesc->dwValidData & DMUS_OBJ_FILENAME)) {
89                         if (pDesc->dwValidData & DMUS_OBJ_OBJECT) {
90                                 if (IsEqualGUID (&cacheEntry->guidObject, &pDesc->guidObject)) {
91                                         TRACE(": found it by GUID\n");
92                                         if (cacheEntry->pObject)
93                                                 return IDirectMusicObject_QueryInterface ((LPDIRECTMUSICOBJECT) cacheEntry->pObject, riid, ppv);
94                                 }
95                         }
96                         if (pDesc->dwValidData & DMUS_OBJ_FILENAME) {
97                                 if (cacheEntry->wzFileName && !strncmpW (pDesc->wszFileName, cacheEntry->wzFileName, MAX_PATH)) {
98                                         TRACE(": found it by FileName\n");
99                                         if (cacheEntry->pObject)
100                                                 return IDirectMusicObject_QueryInterface ((LPDIRECTMUSICOBJECT) cacheEntry->pObject, riid, ppv);
101                                 }
102                         }
103                 } else if (pDesc->dwValidData & DMUS_OBJ_NAME) {
104                   /**
105                    * Usually search by name (for example main procedure name for scripts) after containers loading
106                    * TODO: container loading code
107                    */
108                   TRACE(" comparing %s with cached %s (file:%s)\n", debugstr_w (pDesc->wszName), debugstr_w (cacheEntry->wzName), debugstr_w(cacheEntry->wzFileName));
109                   if (cacheEntry->wzName && !strncmpW (pDesc->wszName, cacheEntry->wzName, 256)) {
110                     TRACE(": found it by Name\n");
111                     if (NULL != cacheEntry->pObject)
112                       return IDirectMusicObject_QueryInterface ((LPDIRECTMUSICOBJECT) cacheEntry->pObject, riid, ppv);
113                   }
114                 }
115         }
116         
117         /* object doesn't exist in cache... guess we'll have to load it */
118         TRACE(": object does not exist in cache\n");
119         if (pDesc->dwValidData & DMUS_OBJ_LOADED) {
120           ERR("Wanted a on-memory (cached) entry, but not found. Active Hack (waiting for Load code)\n");
121           /* ugly hack waiting for Load impl */
122           result = CoCreateInstance (&pDesc->guidClass, NULL, CLSCTX_INPROC_SERVER, &IID_IDirectMusicObject, (LPVOID*) &pObject);
123           if (SUCCEEDED(result)) {
124             /* add object to cache */
125             result = IDirectMusicObject_QueryInterface (pObject, riid, ppv);
126             if (SUCCEEDED(result)) {
127               newEntry = (LPDMUS_PRIVATE_CACHE_ENTRY) HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof(DMUS_PRIVATE_CACHE_ENTRY));
128               if (pDesc->dwValidData & DMUS_OBJ_NAME)
129                 strncpyW (newEntry->wzName, pDesc->wszName, 256);
130               newEntry->pObject = pObject;
131               list_add_tail (&This->CacheList, &newEntry->entry);
132               TRACE(": filled in cache entry\n");
133             } else {
134               IDirectMusicObject_Release(pObject);
135             }
136           }
137           return result;
138           /*
139            * Normal code
140           *ppv = NULL;
141           return E_FAIL;
142           */
143         }
144         if (!(pDesc->dwValidData & DMUS_OBJ_CLASS)) {
145           WARN("guidClass not valid but needed. What they want to do ?\n");
146           *ppv = NULL;
147           return DMUS_E_LOADER_NOCLASSID;
148         }
149         result = CoCreateInstance (&pDesc->guidClass, NULL, CLSCTX_INPROC_SERVER, &IID_IDirectMusicObject, (LPVOID*) &pObject);
150         if (FAILED(result)) return result;
151         if (pDesc->dwValidData & DMUS_OBJ_FILENAME) {
152                 /* load object from file */
153                 WCHAR wzFileName[MAX_PATH];
154                 ILoaderStream* pStream;
155                 IPersistStream* pPersistStream = NULL;
156                 /* if it's full path, don't add search directory path, otherwise do */
157                 if (pDesc->dwValidData & DMUS_OBJ_FULLPATH) {
158                         lstrcpyW(wzFileName, pDesc->wszFileName);
159                 } else {
160                         WCHAR *p;
161                         lstrcpyW(wzFileName, This->wzSearchPath);
162                         p = wzFileName + lstrlenW(wzFileName);
163                         if (p > wzFileName && p[-1] != '\\') *p++ = '\\';
164                         strcpyW(p, pDesc->wszFileName);
165                 }
166                 TRACE(": loading from file (%s)\n", debugstr_w(wzFileName));
167          
168                 result = DMUSIC_CreateLoaderStream ((LPSTREAM*) &pStream);
169                 if (FAILED(result)) return result;
170                 
171                 result = ILoaderStream_Attach (pStream, wzFileName, (LPDIRECTMUSICLOADER) iface);
172                 if (FAILED(result)) return result;
173                         
174                 result = IDirectMusicObject_QueryInterface (pObject, &IID_IPersistStream, (LPVOID*) &pPersistStream);
175                 if (FAILED(result)) return result;
176                         
177                 result = IPersistStream_Load (pPersistStream, (LPSTREAM) pStream);
178                 if (FAILED(result)) return result;
179                         
180                 ILoaderStream_IStream_Release ((LPSTREAM) pStream);
181                 IPersistStream_Release (pPersistStream);
182         } else if (pDesc->dwValidData & DMUS_OBJ_STREAM) {
183                 /* load object from stream */
184                 IStream* pClonedStream = NULL;
185                 IPersistStream* pPersistStream = NULL;
186
187                 TRACE(": loading from stream\n");
188                 result = IDirectMusicObject_QueryInterface (pObject, &IID_IPersistStream, (LPVOID*) &pPersistStream);
189                 if (FAILED(result)) return result;
190                         
191                 result = IStream_Clone (pDesc->pStream, &pClonedStream);
192                 if (FAILED(result)) return result;
193
194                 result = IPersistStream_Load (pPersistStream, pClonedStream);
195                 if (FAILED(result))     return result;
196
197                 IPersistStream_Release (pPersistStream);
198                 IStream_Release (pClonedStream);
199         } else if (pDesc->dwValidData & DMUS_OBJ_OBJECT) {
200                 /* load object by GUID */
201                 TRACE(": loading by GUID (only default DLS supported)\n");
202                 if (IsEqualGUID (&pDesc->guidObject, &GUID_DefaultGMCollection)) {
203                         WCHAR wzFileName[MAX_PATH];
204                         IPersistStream *pPersistStream = NULL;
205                         ILoaderStream* pStream;
206                         if (FAILED(DMUSIC_GetDefaultGMPath (wzFileName)))
207                                 return E_FAIL;
208                         /* load object from file */
209                         result = DMUSIC_CreateLoaderStream ((LPSTREAM*) &pStream);
210                         if (FAILED(result)) return result;
211
212                         result = ILoaderStream_Attach (pStream, wzFileName, (LPDIRECTMUSICLOADER) iface);
213                         if (FAILED(result)) return result;
214
215                         result = IDirectMusicObject_QueryInterface (pObject, &IID_IPersistStream, (LPVOID*) &pPersistStream);
216                         if (FAILED(result)) return result;
217
218                         result = IPersistStream_Load (pPersistStream, (LPSTREAM) pStream);
219                         if (FAILED(result)) return result;
220
221                         ILoaderStream_IStream_Release ((LPSTREAM) pStream);
222                         IPersistStream_Release (pPersistStream);
223                 } else {
224                         return E_FAIL;
225                 }
226         } else {
227                 /* nowhere to load from */
228                 FIXME(": unknown/unsupported way of loading\n");
229                 return E_FAIL;
230         }
231         /* add object to cache */
232         newEntry = (LPDMUS_PRIVATE_CACHE_ENTRY) HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof(DMUS_PRIVATE_CACHE_ENTRY));
233         if (pDesc->dwValidData & DMUS_OBJ_OBJECT)
234                 memcpy (&newEntry->guidObject, &pDesc->guidObject, sizeof (pDesc->guidObject));
235         if (pDesc->dwValidData & DMUS_OBJ_FILENAME)
236                 strncpyW (newEntry->wzFileName, pDesc->wszFileName, MAX_PATH);
237         if (pObject)
238                 newEntry->pObject = pObject;
239         list_add_tail (&This->CacheList, &newEntry->entry);
240         TRACE(": filled in cache entry\n");
241         
242         /* for debug purposes (e.g. to check if all files are cached) */
243 #if 0
244         int i = 0;
245         LIST_FOR_EACH (listEntry, &This->CacheList) {
246                 i++;
247                 cacheEntry = LIST_ENTRY( listEntry, DMUS_PRIVATE_CACHE_ENTRY, entry );
248                 TRACE("Entry nr. %i: GUID = %s, FileName = %s\n", i, debugstr_guid (&cacheEntry->guidObject), debugstr_w (cacheEntry->wzFileName));
249         }
250 #endif
251         
252         return IDirectMusicObject_QueryInterface (pObject, riid, ppv);
253 }
254
255 HRESULT WINAPI IDirectMusicLoader8Impl_SetObject (LPDIRECTMUSICLOADER8 iface, LPDMUS_OBJECTDESC pDesc)
256 {
257         ICOM_THIS(IDirectMusicLoader8Impl,iface);
258
259         FIXME("(%p, %p): stub\n", This, pDesc);
260
261         return S_OK;
262 }
263
264 HRESULT WINAPI IDirectMusicLoader8Impl_SetSearchDirectory (LPDIRECTMUSICLOADER8 iface, REFGUID rguidClass, WCHAR* pwzPath, BOOL fClear)
265 {
266         ICOM_THIS(IDirectMusicLoader8Impl,iface);
267
268         TRACE("(%p, %s, %s, %d)\n", This, debugstr_guid(rguidClass), debugstr_w(pwzPath), fClear);
269         if (0 == strncmpW(This->wzSearchPath, pwzPath, MAX_PATH)) {
270           return S_FALSE;
271         } 
272         strncpyW(This->wzSearchPath, pwzPath, MAX_PATH);
273         
274         return S_OK;
275 }
276
277 HRESULT WINAPI IDirectMusicLoader8Impl_ScanDirectory (LPDIRECTMUSICLOADER8 iface, REFGUID rguidClass, WCHAR* pwzFileExtension, WCHAR* pwzScanFileName)
278 {
279         ICOM_THIS(IDirectMusicLoader8Impl,iface);
280
281         FIXME("(%p, %s, %p, %p): stub\n", This, debugstr_guid(rguidClass), pwzFileExtension, pwzScanFileName);
282
283         return S_OK;
284 }
285
286 HRESULT WINAPI IDirectMusicLoader8Impl_CacheObject (LPDIRECTMUSICLOADER8 iface, IDirectMusicObject* pObject)
287 {
288         ICOM_THIS(IDirectMusicLoader8Impl,iface);
289
290         FIXME("(%p, %p): stub\n", This, pObject);
291
292         return S_OK;
293 }
294
295 HRESULT WINAPI IDirectMusicLoader8Impl_ReleaseObject (LPDIRECTMUSICLOADER8 iface, IDirectMusicObject* pObject)
296 {
297         ICOM_THIS(IDirectMusicLoader8Impl,iface);
298
299         FIXME("(%p, %p): stub\n", This, pObject);
300
301         return S_OK;
302 }
303
304 HRESULT WINAPI IDirectMusicLoader8Impl_ClearCache (LPDIRECTMUSICLOADER8 iface, REFGUID rguidClass)
305 {
306         ICOM_THIS(IDirectMusicLoader8Impl,iface);
307
308         FIXME("(%p, %s): stub\n", This, debugstr_guid(rguidClass));
309
310         return S_OK;
311 }
312
313 HRESULT WINAPI IDirectMusicLoader8Impl_EnableCache (LPDIRECTMUSICLOADER8 iface, REFGUID rguidClass, BOOL fEnable)
314 {
315         ICOM_THIS(IDirectMusicLoader8Impl,iface);
316
317         FIXME("(%p, %s, %d): stub\n", This, debugstr_guid(rguidClass), fEnable);
318
319         return S_OK;
320 }
321
322 HRESULT WINAPI IDirectMusicLoader8Impl_EnumObject (LPDIRECTMUSICLOADER8 iface, REFGUID rguidClass, DWORD dwIndex, LPDMUS_OBJECTDESC pDesc)
323 {
324         ICOM_THIS(IDirectMusicLoader8Impl,iface);
325
326         FIXME("(%p, %s, %ld, %p): stub\n", This, debugstr_guid(rguidClass), dwIndex, pDesc);
327
328         return S_OK;
329 }
330
331 /* IDirectMusicLoader8 Interface part follow: */
332 void WINAPI IDirectMusicLoader8Impl_CollectGarbage (LPDIRECTMUSICLOADER8 iface)
333 {
334         ICOM_THIS(IDirectMusicLoader8Impl,iface);
335
336         FIXME("(%p): stub\n", This);
337 }
338
339 HRESULT WINAPI IDirectMusicLoader8Impl_ReleaseObjectByUnknown (LPDIRECTMUSICLOADER8 iface, IUnknown* pObject)
340 {
341         ICOM_THIS(IDirectMusicLoader8Impl,iface);
342
343         FIXME("(%p, %p): stub\n", This, pObject);
344         
345         return S_OK;
346 }
347
348 HRESULT WINAPI IDirectMusicLoader8Impl_LoadObjectFromFile (LPDIRECTMUSICLOADER8 iface, 
349                                                            REFGUID rguidClassID, 
350                                                            REFIID iidInterfaceID, 
351                                                            WCHAR* pwzFilePath, 
352                                                            void** ppObject)
353 {
354         ICOM_THIS(IDirectMusicLoader8Impl,iface);
355         DMUS_OBJECTDESC ObjDesc;
356         
357         TRACE("(%p, %s, %s, %s, %p): wrapping to IDirectMusicLoader8Impl_GetObject\n", This, debugstr_guid(rguidClassID), debugstr_guid(iidInterfaceID), debugstr_w(pwzFilePath), ppObject);
358         
359         ObjDesc.dwSize = sizeof(DMUS_OBJECTDESC);
360         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 */
361         ObjDesc.guidClass = *rguidClassID;
362         /* OK, MSDN says that search order is the following:
363             - current directory (DONE)
364             - windows search path (FIXME: how do I get that?)
365             - loader's search path (DONE)
366         */
367         /* search in current directory */
368         if (!SearchPathW (NULL, pwzFilePath, NULL,
369                           sizeof(ObjDesc.wszFileName)/sizeof(WCHAR), ObjDesc.wszFileName, NULL) &&
370             /* search in loader's search path */
371             !SearchPathW (This->wzSearchPath, pwzFilePath, NULL,
372                           sizeof(ObjDesc.wszFileName)/sizeof(WCHAR), ObjDesc.wszFileName, NULL))
373         {
374                 /* cannot find file */
375                 TRACE("cannot find file\n");
376                 return DMUS_E_LOADER_FAILEDOPEN;
377         }
378         
379         TRACE("full file path = %s\n", debugstr_w (ObjDesc.wszFileName));
380         
381         return IDirectMusicLoader8Impl_GetObject (iface, &ObjDesc, iidInterfaceID, ppObject);
382 }
383
384 ICOM_VTABLE(IDirectMusicLoader8) DirectMusicLoader8_Vtbl =
385 {
386     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
387         IDirectMusicLoader8Impl_QueryInterface,
388         IDirectMusicLoader8Impl_AddRef,
389         IDirectMusicLoader8Impl_Release,
390         IDirectMusicLoader8Impl_GetObject,
391         IDirectMusicLoader8Impl_SetObject,
392         IDirectMusicLoader8Impl_SetSearchDirectory,
393         IDirectMusicLoader8Impl_ScanDirectory,
394         IDirectMusicLoader8Impl_CacheObject,
395         IDirectMusicLoader8Impl_ReleaseObject,
396         IDirectMusicLoader8Impl_ClearCache,
397         IDirectMusicLoader8Impl_EnableCache,
398         IDirectMusicLoader8Impl_EnumObject,
399         IDirectMusicLoader8Impl_CollectGarbage,
400         IDirectMusicLoader8Impl_ReleaseObjectByUnknown,
401         IDirectMusicLoader8Impl_LoadObjectFromFile
402 };
403
404 /* for ClassFactory */
405 HRESULT WINAPI DMUSIC_CreateDirectMusicLoader (LPCGUID lpcGUID, LPDIRECTMUSICLOADER8 *ppDMLoad, LPUNKNOWN pUnkOuter)
406 {
407         IDirectMusicLoader8Impl *dmloader;
408
409         TRACE("(%p,%p,%p)\n",lpcGUID, ppDMLoad, pUnkOuter);
410         if (IsEqualIID (lpcGUID, &IID_IDirectMusicLoader) || 
411             IsEqualIID (lpcGUID, &IID_IDirectMusicLoader8)) {
412                 dmloader = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IDirectMusicLoader8Impl));
413                 if (NULL == dmloader) {
414                         *ppDMLoad = (LPDIRECTMUSICLOADER8)NULL;
415                         return E_OUTOFMEMORY;
416                 }
417                 dmloader->lpVtbl = &DirectMusicLoader8_Vtbl;
418                 dmloader->ref = 1;
419                 MultiByteToWideChar (CP_ACP, 0, ".\\", -1, dmloader->wzSearchPath, MAX_PATH);
420                 list_init (&dmloader->CacheList);
421                 *ppDMLoad = (LPDIRECTMUSICLOADER8)dmloader;
422                 return S_OK;
423         }
424         
425         WARN("No interface found\n");
426         return E_NOINTERFACE;
427 }
428
429 /* help function for IDirectMusicLoader8Impl_GetObject */
430 HRESULT WINAPI DMUSIC_GetDefaultGMPath (WCHAR wszPath[MAX_PATH])
431 {
432         HKEY hkDM;
433         DWORD returnType, sizeOfReturnBuffer = MAX_PATH;
434         char szPath[MAX_PATH];
435
436         if ((RegOpenKeyExA (HKEY_LOCAL_MACHINE, "Software\\Microsoft\\DirectMusic" , 0, KEY_READ, &hkDM) != ERROR_SUCCESS) || 
437             (RegQueryValueExA (hkDM, "GMFilePath", NULL, &returnType, szPath, &sizeOfReturnBuffer) != ERROR_SUCCESS)) {
438                 WARN(": registry entry missing\n" );
439                 return E_FAIL;
440         }
441         /* FIXME: Check return types to ensure we're interpreting data right */
442         MultiByteToWideChar (CP_ACP, 0, szPath, -1, wszPath, MAX_PATH);
443         
444         return S_OK;
445 }