2 * Gameux library coclass GameExplorer implementation
4 * Copyright (C) 2010 Mariusz PluciĆski
6 * This library 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 library 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 library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29 #include "gameux_private.h"
34 #include "wine/debug.h"
37 WINE_DEFAULT_DEBUG_CHANNEL(gameux);
39 /* function from Shell32, not defined in header */
40 extern BOOL WINAPI GUIDFromStringW(LPCWSTR psz, LPGUID pguid);
42 /*******************************************************************************
43 * GameUX helper functions
45 /*******************************************************************************
48 * Internal helper function. Description available in gameux_private.h file
50 void GAMEUX_initGameData(struct GAMEUX_GAME_DATA *GameData)
52 GameData->sGDFBinaryPath = NULL;
53 GameData->sGameInstallDirectory = NULL;
54 GameData->bstrName = NULL;
56 /*******************************************************************************
57 * GAMEUX_uninitGameData
59 * Internal helper function. Description available in gameux_private.h file
61 void GAMEUX_uninitGameData(struct GAMEUX_GAME_DATA *GameData)
63 HeapFree(GetProcessHeap(), 0, GameData->sGDFBinaryPath);
64 HeapFree(GetProcessHeap(), 0, GameData->sGameInstallDirectory);
65 SysFreeString(GameData->bstrName);
67 /*******************************************************************************
68 * GAMEUX_buildGameRegistryPath
70 * Helper function, builds registry path to key, where game's data are stored
73 * installScope [I] the scope which was used in AddGame/InstallGame call
74 * gameInstanceId [I] game instance GUID
75 * lpRegistryPath [O] pointer which will receive address to string
76 * containing expected registry path. Path
77 * is relative to HKLM registry key. It
78 * must be freed by calling HeapFree(GetProcessHeap(), 0, ...)
80 * Name of game's registry key always follows patterns below:
81 * When game is installed for current user only (installScope is GIS_CURRENT_USER):
82 * HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\
83 * GameUX\[user's security ID]\[game instance ID]
85 * When game is installed for all users (installScope is GIS_ALL_USERS):
86 * HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\
87 * GameUX\Games\[game instance ID]
91 static HRESULT GAMEUX_buildGameRegistryPath(GAME_INSTALL_SCOPE installScope,
92 LPCGUID gameInstanceId,
93 LPWSTR* lpRegistryPath)
95 static const WCHAR sGameUxRegistryPath[] = {'S','O','F','T','W','A','R','E','\\',
96 'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
97 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\','G','a','m','e','U','X',0};
98 static const WCHAR sGames[] = {'G','a','m','e','s',0};
99 static const WCHAR sBackslash[] = {'\\',0};
102 HANDLE hToken = NULL;
103 PTOKEN_USER pTokenUser = NULL;
106 WCHAR sInstanceId[40];
107 WCHAR sRegistryPath[8192];
109 TRACE("(0x%x, %s, %p)\n", installScope, debugstr_guid(gameInstanceId), lpRegistryPath);
111 lstrcpyW(sRegistryPath, sGameUxRegistryPath);
112 lstrcatW(sRegistryPath, sBackslash);
114 if(installScope == GIS_CURRENT_USER)
116 /* build registry path containing user's SID */
117 if(!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
118 hr = HRESULT_FROM_WIN32(GetLastError());
122 if(!GetTokenInformation(hToken, TokenUser, NULL, 0, &dwLength) &&
123 GetLastError()!=ERROR_INSUFFICIENT_BUFFER)
124 hr = HRESULT_FROM_WIN32(GetLastError());
128 pTokenUser = HeapAlloc(GetProcessHeap(), 0, dwLength);
134 if(!GetTokenInformation(hToken, TokenUser, (LPVOID)pTokenUser, dwLength, &dwLength))
135 hr = HRESULT_FROM_WIN32(GetLastError());
138 if(!ConvertSidToStringSidW(pTokenUser->User.Sid, &lpSID))
139 hr = HRESULT_FROM_WIN32(GetLastError());
143 lstrcatW(sRegistryPath, lpSID);
147 HeapFree(GetProcessHeap(), 0, pTokenUser);
151 else if(installScope == GIS_ALL_USERS)
152 /* build registry path without SID */
153 lstrcatW(sRegistryPath, sGames);
157 /* put game's instance id on the end of path */
159 hr = (StringFromGUID2(gameInstanceId, sInstanceId, sizeof(sInstanceId)/sizeof(sInstanceId[0])) ? S_OK : E_FAIL);
163 lstrcatW(sRegistryPath, sBackslash);
164 lstrcatW(sRegistryPath, sInstanceId);
169 *lpRegistryPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(sRegistryPath)+1)*sizeof(WCHAR));
175 lstrcpyW(*lpRegistryPath, sRegistryPath);
177 TRACE("result: 0x%x, path: %s\n", hr, debugstr_w(*lpRegistryPath));
180 /*******************************************************************************
181 * GAMEUX_WriteRegistryRecord
183 * Helper function, writes data associated with game (stored in GAMEUX_GAME_DATA
184 * structure) into expected place in registry.
187 * GameData [I] structure with data which will
188 * be written into registry.
189 * Proper values of fields installScope
190 * and guidInstanceId are required
191 * to create registry key.
193 * Schema of naming registry keys associated with games is available in
194 * description of _buildGameRegistryPath internal function.
196 * List of registry keys associated with structure fields:
197 * Key Field in GAMEUX_GAME_DATA structure
198 * ApplicationId guidApplicationId
199 * ConfigApplicationPath sGameInstallDirectory
200 * ConfigGDFBinaryPath sGDFBinaryPath
204 static HRESULT GAMEUX_WriteRegistryRecord(struct GAMEUX_GAME_DATA *GameData)
206 static const WCHAR sApplicationId[] =
207 {'A','p','p','l','i','c','a','t','i','o','n','I','d',0};
208 static const WCHAR sConfigApplicationPath[] =
209 {'C','o','n','f','i','g','A','p','p','l','i','c','a','t','i','o','n','P','a','t','h',0};
210 static const WCHAR sConfigGDFBinaryPath[] =
211 {'C','o','n','f','i','g','G','D','F','B','i','n','a','r','y','P','a','t','h',0};
212 static const WCHAR sTitle[] =
213 {'T','i','t','l','e',0};
216 LPWSTR lpRegistryKey;
218 WCHAR sGameApplicationId[40];
220 TRACE("(%p)\n", GameData);
222 hr = GAMEUX_buildGameRegistryPath(GameData->installScope, &GameData->guidInstanceId, &lpRegistryKey);
225 hr = (StringFromGUID2(&GameData->guidApplicationId, sGameApplicationId, sizeof(sGameApplicationId)/sizeof(sGameApplicationId[0])) ? S_OK : E_FAIL);
228 hr = HRESULT_FROM_WIN32(RegCreateKeyExW(HKEY_LOCAL_MACHINE, lpRegistryKey,
229 0, NULL, 0, KEY_ALL_ACCESS, NULL,
234 /* write game data to registry key */
235 hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, sConfigApplicationPath, 0,
236 REG_SZ, (LPBYTE)(GameData->sGameInstallDirectory),
237 (lstrlenW(GameData->sGameInstallDirectory)+1)*sizeof(WCHAR)));
240 hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, sConfigGDFBinaryPath, 0,
241 REG_SZ, (LPBYTE)(GameData->sGDFBinaryPath),
242 (lstrlenW(GameData->sGDFBinaryPath)+1)*sizeof(WCHAR)));
245 hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, sApplicationId, 0,
246 REG_SZ, (LPBYTE)(sGameApplicationId),
247 (lstrlenW(sGameApplicationId)+1)*sizeof(WCHAR)));
250 hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, sTitle, 0,
251 REG_SZ, (LPBYTE)(GameData->bstrName),
252 (lstrlenW(GameData->bstrName)+1)*sizeof(WCHAR)));
258 /* if something failed, remove whole key */
259 hr2 = RegDeleteKeyExW(HKEY_LOCAL_MACHINE, lpRegistryKey, 0, 0);
260 /* do not overwrite old failure code with new success code */
266 HeapFree(GetProcessHeap(), 0, lpRegistryKey);
267 TRACE("returning 0x%x\n", hr);
270 /*******************************************************************************
271 * GAMEUX_ProcessGameDefinitionElement
273 * Helper function, parses single element from Game Definition
276 * lpXMLElement [I] game definition element
277 * GameData [O] structure, where parsed
278 * data will be stored
280 static HRESULT GAMEUX_ProcessGameDefinitionElement(
281 IXMLDOMElement *element,
282 struct GAMEUX_GAME_DATA *GameData)
284 static const WCHAR sName[] =
288 BSTR bstrElementName;
290 TRACE("(%p, %p)\n", element, GameData);
292 hr = IXMLDOMElement_get_nodeName(element, &bstrElementName);
295 /* check element name */
296 if(lstrcmpW(bstrElementName, sName) == 0)
297 hr = IXMLDOMElement_get_text(element, &GameData->bstrName);
300 FIXME("entry %s in Game Definition File not yet supported\n", debugstr_w(bstrElementName));
302 SysFreeString(bstrElementName);
307 /*******************************************************************************
308 * GAMEUX_ParseGameDefinition
310 * Helper function, loads data from given XML element into fields of GAME_DATA
314 * lpXMLGameDefinitionElement [I] Game Definition XML element
315 * GameData [O] structure where data loaded from
316 * XML element will be stored in
318 static HRESULT GAMEUX_ParseGameDefinition(
319 IXMLDOMElement *gdElement,
320 struct GAMEUX_GAME_DATA *GameData)
322 static const WCHAR sGameId[] = {'g','a','m','e','I','D',0};
327 IXMLDOMNodeList *childrenList;
328 IXMLDOMNode *nextNode;
329 IXMLDOMElement *nextElement;
331 TRACE("(%p, %p)\n", gdElement, GameData);
333 bstrAttribute = SysAllocString(sGameId);
337 hr = IXMLDOMElement_getAttribute(gdElement, bstrAttribute, &variant);
341 hr = ( GUIDFromStringW(V_BSTR(&variant), &GameData->guidApplicationId)==TRUE ? S_OK : E_FAIL);
343 SysFreeString(V_BSTR(&variant));
346 SysFreeString(bstrAttribute);
348 /* browse subnodes */
350 hr = IXMLDOMElement_get_childNodes(gdElement, &childrenList);
356 hr = IXMLDOMNodeList_nextNode(childrenList, &nextNode);
360 hr = IXMLDOMNode_QueryInterface(nextNode, &IID_IXMLDOMElement,
361 (LPVOID*)&nextElement);
365 hr = GAMEUX_ProcessGameDefinitionElement(nextElement, GameData);
366 IXMLDOMElement_Release(nextElement);
369 IXMLDOMElement_Release(nextNode);
375 IXMLDOMNodeList_Release(childrenList);
380 /*******************************************************************************
381 * GAMEUX_ParseGDFBinary
383 * Helper funtion, loads given binary and parses embed GDF if there's any.
386 * GameData [I/O] Structure with game's data. Content of field
387 * sGDFBinaryPath defines path to binary, from
388 * which embed GDF will be loaded. Data from
389 * GDF will be stored in other fields of this
392 static HRESULT GAMEUX_ParseGDFBinary(struct GAMEUX_GAME_DATA *GameData)
394 static const WCHAR sRes[] = {'r','e','s',':','/','/',0};
395 static const WCHAR sDATA[] = {'D','A','T','A',0};
396 static const WCHAR sSlash[] = {'/',0};
399 WCHAR sResourcePath[MAX_PATH];
401 VARIANT_BOOL isSuccessful;
402 IXMLDOMDocument *document;
404 IXMLDOMElement *root, *gdElement;
406 TRACE("(%p)->sGDFBinaryPath = %s\n", GameData, debugstr_w(GameData->sGDFBinaryPath));
408 /* prepare path to GDF, using res:// prefix */
409 lstrcpyW(sResourcePath, sRes);
410 lstrcatW(sResourcePath, GameData->sGDFBinaryPath);
411 lstrcatW(sResourcePath, sSlash);
412 lstrcatW(sResourcePath, sDATA);
413 lstrcatW(sResourcePath, sSlash);
414 lstrcatW(sResourcePath, ID_GDF_XML_STR);
416 hr = CoCreateInstance(&CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER,
417 &IID_IXMLDOMDocument, (void**)&document);
421 /* load GDF into MSXML */
422 V_VT(&variant) = VT_BSTR;
423 V_BSTR(&variant) = SysAllocString(sResourcePath);
424 if(!V_BSTR(&variant))
429 hr = IXMLDOMDocument_load(document, variant, &isSuccessful);
430 if(hr == S_FALSE || isSuccessful == VARIANT_FALSE)
434 SysFreeString(V_BSTR(&variant));
438 hr = IXMLDOMDocument_get_documentElement(document, &root);
445 hr = IXMLDOMElement_get_firstChild(root, &gdNode);
451 hr = IXMLDOMNode_QueryInterface(gdNode, &IID_IXMLDOMElement, (LPVOID*)&gdElement);
454 hr = GAMEUX_ParseGameDefinition(gdElement, GameData);
455 IXMLDOMElement_Release(gdElement);
458 IXMLDOMNode_Release(gdNode);
461 IXMLDOMElement_Release(root);
464 IXMLDOMDocument_Release(document);
469 /*******************************************************************************
470 * GAMEUX_RegisterGame
472 * Internal helper function. Description available in gameux_private.h file
474 HRESULT WINAPI GAMEUX_RegisterGame(LPCWSTR sGDFBinaryPath,
475 LPCWSTR sGameInstallDirectory,
476 GAME_INSTALL_SCOPE installScope,
480 struct GAMEUX_GAME_DATA GameData;
482 TRACE("(%s, %s, 0x%x, %s)\n", debugstr_w(sGDFBinaryPath), debugstr_w(sGameInstallDirectory), installScope, debugstr_guid(pInstanceID));
484 GAMEUX_initGameData(&GameData);
485 GameData.sGDFBinaryPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(sGDFBinaryPath)+1)*sizeof(WCHAR));
486 lstrcpyW(GameData.sGDFBinaryPath, sGDFBinaryPath);
487 GameData.sGameInstallDirectory = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(sGameInstallDirectory)+1)*sizeof(WCHAR));
488 lstrcpyW(GameData.sGameInstallDirectory, sGameInstallDirectory);
489 GameData.installScope = installScope;
491 /* generate GUID if it was not provided by user */
492 if(IsEqualGUID(pInstanceID, &GUID_NULL))
493 hr = CoCreateGuid(pInstanceID);
495 GameData.guidInstanceId = *pInstanceID;
497 /* load data from GDF binary */
499 hr = GAMEUX_ParseGDFBinary(&GameData);
501 /* save data to registry */
503 hr = GAMEUX_WriteRegistryRecord(&GameData);
505 GAMEUX_uninitGameData(&GameData);
506 TRACE("returing 0x%08x\n", hr);
509 /*******************************************************************************
510 * GameExplorer implementation
513 typedef struct _GameExplorerImpl
515 const struct IGameExplorerVtbl *lpGameExplorerVtbl;
516 const struct IGameExplorer2Vtbl *lpGameExplorer2Vtbl;
520 static inline GameExplorerImpl *impl_from_IGameExplorer(IGameExplorer *iface)
522 return (GameExplorerImpl*)((char*)iface - FIELD_OFFSET(GameExplorerImpl, lpGameExplorerVtbl));
525 static inline IGameExplorer* IGameExplorer_from_impl(GameExplorerImpl* This)
527 return (struct IGameExplorer*)&This->lpGameExplorerVtbl;
530 static inline GameExplorerImpl *impl_from_IGameExplorer2(IGameExplorer2 *iface)
532 return (GameExplorerImpl*)((char*)iface - FIELD_OFFSET(GameExplorerImpl, lpGameExplorer2Vtbl));
535 static inline IGameExplorer2* IGameExplorer2_from_impl(GameExplorerImpl* This)
537 return (struct IGameExplorer2*)&This->lpGameExplorer2Vtbl;
540 static HRESULT WINAPI GameExplorerImpl_QueryInterface(
541 IGameExplorer *iface,
545 GameExplorerImpl *This = impl_from_IGameExplorer(iface);
547 TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppvObject);
551 if(IsEqualGUID(riid, &IID_IUnknown) ||
552 IsEqualGUID(riid, &IID_IGameExplorer))
554 *ppvObject = IGameExplorer_from_impl(This);
556 else if(IsEqualGUID(riid, &IID_IGameExplorer2))
558 *ppvObject = IGameExplorer2_from_impl(This);
562 FIXME("interface %s not implemented\n", debugstr_guid(riid));
563 return E_NOINTERFACE;
566 IGameExplorer_AddRef(iface);
570 static ULONG WINAPI GameExplorerImpl_AddRef(IGameExplorer *iface)
572 GameExplorerImpl *This = impl_from_IGameExplorer(iface);
575 ref = InterlockedIncrement(&This->ref);
577 TRACE("(%p): ref=%d\n", This, ref);
581 static ULONG WINAPI GameExplorerImpl_Release(IGameExplorer *iface)
583 GameExplorerImpl *This = impl_from_IGameExplorer(iface);
586 ref = InterlockedDecrement(&This->ref);
587 TRACE("(%p): ref=%d\n", This, ref);
591 TRACE("freeing GameExplorer object\n");
592 HeapFree(GetProcessHeap(), 0, This);
598 static HRESULT WINAPI GameExplorerImpl_AddGame(
599 IGameExplorer *iface,
600 BSTR bstrGDFBinaryPath,
601 BSTR sGameInstallDirectory,
602 GAME_INSTALL_SCOPE installScope,
605 GameExplorerImpl *This = impl_from_IGameExplorer(iface);
606 TRACE("(%p, %s, %s, 0x%x, %s)\n", This, debugstr_w(bstrGDFBinaryPath), debugstr_w(sGameInstallDirectory), installScope, debugstr_guid(pInstanceID));
607 return GAMEUX_RegisterGame(bstrGDFBinaryPath, sGameInstallDirectory, installScope, pInstanceID);
610 static HRESULT WINAPI GameExplorerImpl_RemoveGame(
611 IGameExplorer *iface,
614 GameExplorerImpl *This = impl_from_IGameExplorer(iface);
616 TRACE("(%p, %s)\n", This, debugstr_guid(&instanceID));
621 static HRESULT WINAPI GameExplorerImpl_UpdateGame(
622 IGameExplorer *iface,
625 GameExplorerImpl *This = impl_from_IGameExplorer(iface);
627 TRACE("(%p, %s)\n", This, debugstr_guid(&instanceID));
632 static HRESULT WINAPI GameExplorerImpl_VerifyAccess(
633 IGameExplorer *iface,
637 GameExplorerImpl *This = impl_from_IGameExplorer(iface);
639 TRACE("(%p, %s, %p)\n", This, debugstr_w(sGDFBinaryPath), pHasAccess);
644 static const struct IGameExplorerVtbl GameExplorerImplVtbl =
646 GameExplorerImpl_QueryInterface,
647 GameExplorerImpl_AddRef,
648 GameExplorerImpl_Release,
649 GameExplorerImpl_AddGame,
650 GameExplorerImpl_RemoveGame,
651 GameExplorerImpl_UpdateGame,
652 GameExplorerImpl_VerifyAccess
656 static HRESULT WINAPI GameExplorer2Impl_QueryInterface(
657 IGameExplorer2 *iface,
661 GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
662 return GameExplorerImpl_QueryInterface(IGameExplorer_from_impl(This), riid, ppvObject);
665 static ULONG WINAPI GameExplorer2Impl_AddRef(IGameExplorer2 *iface)
667 GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
668 return GameExplorerImpl_AddRef(IGameExplorer_from_impl(This));
671 static ULONG WINAPI GameExplorer2Impl_Release(IGameExplorer2 *iface)
673 GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
674 return GameExplorerImpl_Release(IGameExplorer_from_impl(This));
677 static HRESULT WINAPI GameExplorer2Impl_CheckAccess(
678 IGameExplorer2 *iface,
679 LPCWSTR binaryGDFPath,
682 GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
683 FIXME("stub (%p, %s, %p)\n", This, debugstr_w(binaryGDFPath), pHasAccess);
687 static HRESULT WINAPI GameExplorer2Impl_InstallGame(
688 IGameExplorer2 *iface,
689 LPCWSTR binaryGDFPath,
690 LPCWSTR installDirectory,
691 GAME_INSTALL_SCOPE installScope)
693 GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
694 FIXME("stub (%p, %s, %s, 0x%x)\n", This, debugstr_w(binaryGDFPath), debugstr_w(installDirectory), installScope);
698 static HRESULT WINAPI GameExplorer2Impl_UninstallGame(
699 IGameExplorer2 *iface,
700 LPCWSTR binaryGDFPath)
702 GameExplorerImpl *This = impl_from_IGameExplorer2(iface);
703 FIXME("stub (%p, %s)\n", This, debugstr_w(binaryGDFPath));
707 static const struct IGameExplorer2Vtbl GameExplorer2ImplVtbl =
709 GameExplorer2Impl_QueryInterface,
710 GameExplorer2Impl_AddRef,
711 GameExplorer2Impl_Release,
712 GameExplorer2Impl_InstallGame,
713 GameExplorer2Impl_UninstallGame,
714 GameExplorer2Impl_CheckAccess
718 * Construction routine
720 HRESULT GameExplorer_create(
724 GameExplorerImpl *pGameExplorer;
726 TRACE("(%p, %p)\n", pUnkOuter, ppObj);
728 pGameExplorer = HeapAlloc(GetProcessHeap(), 0, sizeof(*pGameExplorer));
731 return E_OUTOFMEMORY;
733 pGameExplorer->lpGameExplorerVtbl = &GameExplorerImplVtbl;
734 pGameExplorer->lpGameExplorer2Vtbl = &GameExplorer2ImplVtbl;
735 pGameExplorer->ref = 1;
737 *ppObj = (IUnknown*)(&pGameExplorer->lpGameExplorerVtbl);
739 TRACE("returning iface: %p\n", *ppObj);