| /* |
| * Gameux library coclass GameExplorer implementation |
| * |
| * Copyright (C) 2010 Mariusz PluciĆski |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| */ |
| #define COBJMACROS |
| |
| #include "config.h" |
| |
| #include "ole2.h" |
| #include "sddl.h" |
| |
| #include "gameux.h" |
| #include "gameux_private.h" |
| |
| #include "initguid.h" |
| #include "msxml2.h" |
| |
| #include "wine/debug.h" |
| #include "winreg.h" |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(gameux); |
| |
| /******************************************************************************* |
| * GameUX helper functions |
| */ |
| /******************************************************************************* |
| * GAMEUX_initGameData |
| * |
| * Internal helper function. |
| * Initializes GAME_DATA structure fields with proper values. Should be |
| * called always before first usage of this structure. Implemented in gameexplorer.c |
| * |
| * Parameters: |
| * GameData [I/O] pointer to structure to initialize |
| */ |
| static void GAMEUX_initGameData(struct GAMEUX_GAME_DATA *GameData) |
| { |
| GameData->sGDFBinaryPath = NULL; |
| GameData->sGameInstallDirectory = NULL; |
| GameData->bstrName = NULL; |
| GameData->bstrDescription = NULL; |
| } |
| /******************************************************************************* |
| * GAMEUX_uninitGameData |
| * |
| * Internal helper function. |
| * Properly frees all data stored or pointed by fields of GAME_DATA structure. |
| * Should be called before freeing this structure. Implemented in gameexplorer.c |
| * |
| * Parameters: |
| * GameData [I/O] pointer to structure to uninitialize |
| */ |
| static void GAMEUX_uninitGameData(struct GAMEUX_GAME_DATA *GameData) |
| { |
| HeapFree(GetProcessHeap(), 0, GameData->sGDFBinaryPath); |
| HeapFree(GetProcessHeap(), 0, GameData->sGameInstallDirectory); |
| SysFreeString(GameData->bstrName); |
| SysFreeString(GameData->bstrDescription); |
| } |
| /******************************************************************************* |
| * GAMEUX_buildGameRegistryPath |
| * |
| * Internal helper function. Description available in gameux_private.h file |
| */ |
| HRESULT GAMEUX_buildGameRegistryPath(GAME_INSTALL_SCOPE installScope, |
| LPCGUID gameInstanceId, |
| LPWSTR* lpRegistryPath) |
| { |
| static const WCHAR sGameUxRegistryPath[] = {'S','O','F','T','W','A','R','E','\\', |
| 'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\', |
| 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\','G','a','m','e','U','X',0}; |
| static const WCHAR sGames[] = {'G','a','m','e','s',0}; |
| static const WCHAR sBackslash[] = {'\\',0}; |
| |
| HRESULT hr = S_OK; |
| HANDLE hToken = NULL; |
| PTOKEN_USER pTokenUser = NULL; |
| DWORD dwLength; |
| LPWSTR lpSID = NULL; |
| WCHAR sInstanceId[40]; |
| WCHAR sRegistryPath[8192]; |
| |
| TRACE("(0x%x, %s, %p)\n", installScope, debugstr_guid(gameInstanceId), lpRegistryPath); |
| |
| /* this will make freeing it easier for user */ |
| *lpRegistryPath = NULL; |
| |
| lstrcpyW(sRegistryPath, sGameUxRegistryPath); |
| lstrcatW(sRegistryPath, sBackslash); |
| |
| if(installScope == GIS_CURRENT_USER) |
| { |
| /* build registry path containing user's SID */ |
| if(!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) |
| hr = HRESULT_FROM_WIN32(GetLastError()); |
| |
| if(SUCCEEDED(hr)) |
| { |
| if(!GetTokenInformation(hToken, TokenUser, NULL, 0, &dwLength) && |
| GetLastError()!=ERROR_INSUFFICIENT_BUFFER) |
| hr = HRESULT_FROM_WIN32(GetLastError()); |
| |
| if(SUCCEEDED(hr)) |
| { |
| pTokenUser = HeapAlloc(GetProcessHeap(), 0, dwLength); |
| if(!pTokenUser) |
| hr = E_OUTOFMEMORY; |
| } |
| |
| if(SUCCEEDED(hr)) |
| if(!GetTokenInformation(hToken, TokenUser, (LPVOID)pTokenUser, dwLength, &dwLength)) |
| hr = HRESULT_FROM_WIN32(GetLastError()); |
| |
| if(SUCCEEDED(hr)) |
| if(!ConvertSidToStringSidW(pTokenUser->User.Sid, &lpSID)) |
| hr = HRESULT_FROM_WIN32(GetLastError()); |
| |
| if(SUCCEEDED(hr)) |
| { |
| lstrcatW(sRegistryPath, lpSID); |
| LocalFree(lpSID); |
| } |
| |
| HeapFree(GetProcessHeap(), 0, pTokenUser); |
| CloseHandle(hToken); |
| } |
| } |
| else if(installScope == GIS_ALL_USERS) |
| /* build registry path without SID */ |
| lstrcatW(sRegistryPath, sGames); |
| else |
| hr = E_INVALIDARG; |
| |
| /* put game's instance id on the end of path, only if instance id was given */ |
| if(gameInstanceId) |
| { |
| if(SUCCEEDED(hr)) |
| hr = (StringFromGUID2(gameInstanceId, sInstanceId, sizeof(sInstanceId)/sizeof(sInstanceId[0])) ? S_OK : E_FAIL); |
| |
| if(SUCCEEDED(hr)) |
| { |
| lstrcatW(sRegistryPath, sBackslash); |
| lstrcatW(sRegistryPath, sInstanceId); |
| } |
| } |
| |
| if(SUCCEEDED(hr)) |
| { |
| *lpRegistryPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(sRegistryPath)+1)*sizeof(WCHAR)); |
| if(!*lpRegistryPath) |
| hr = E_OUTOFMEMORY; |
| } |
| |
| if(SUCCEEDED(hr)) |
| lstrcpyW(*lpRegistryPath, sRegistryPath); |
| |
| TRACE("result: 0x%x, path: %s\n", hr, debugstr_w(*lpRegistryPath)); |
| return hr; |
| } |
| /******************************************************************************* |
| * GAMEUX_WriteRegistryRecord |
| * |
| * Helper function, writes data associated with game (stored in GAMEUX_GAME_DATA |
| * structure) into expected place in registry. |
| * |
| * Parameters: |
| * GameData [I] structure with data which will |
| * be written into registry. |
| * Proper values of fields installScope |
| * and guidInstanceId are required |
| * to create registry key. |
| * |
| * Schema of naming registry keys associated with games is available in |
| * description of _buildGameRegistryPath internal function. |
| * |
| * List of registry keys associated with structure fields: |
| * Key Field in GAMEUX_GAME_DATA structure |
| * ApplicationId guidApplicationId |
| * ConfigApplicationPath sGameInstallDirectory |
| * ConfigGDFBinaryPath sGDFBinaryPath |
| * Title bstrName |
| * |
| */ |
| static HRESULT GAMEUX_WriteRegistryRecord(struct GAMEUX_GAME_DATA *GameData) |
| { |
| static const WCHAR sApplicationId[] = |
| {'A','p','p','l','i','c','a','t','i','o','n','I','d',0}; |
| static const WCHAR sConfigApplicationPath[] = |
| {'C','o','n','f','i','g','A','p','p','l','i','c','a','t','i','o','n','P','a','t','h',0}; |
| static const WCHAR sConfigGDFBinaryPath[] = |
| {'C','o','n','f','i','g','G','D','F','B','i','n','a','r','y','P','a','t','h',0}; |
| static const WCHAR sTitle[] = |
| {'T','i','t','l','e',0}; |
| static const WCHAR sDescription[] = |
| {'D','e','s','c','r','i','p','t','i','o','n',0}; |
| |
| HRESULT hr, hr2; |
| LPWSTR lpRegistryKey; |
| HKEY hKey; |
| WCHAR sGameApplicationId[40]; |
| |
| TRACE("(%p)\n", GameData); |
| |
| hr = GAMEUX_buildGameRegistryPath(GameData->installScope, &GameData->guidInstanceId, &lpRegistryKey); |
| |
| if(SUCCEEDED(hr)) |
| hr = (StringFromGUID2(&GameData->guidApplicationId, sGameApplicationId, sizeof(sGameApplicationId)/sizeof(sGameApplicationId[0])) ? S_OK : E_FAIL); |
| |
| if(SUCCEEDED(hr)) |
| hr = HRESULT_FROM_WIN32(RegCreateKeyExW(HKEY_LOCAL_MACHINE, lpRegistryKey, |
| 0, NULL, 0, KEY_ALL_ACCESS | KEY_WOW64_64KEY, NULL, |
| &hKey, NULL)); |
| |
| if(SUCCEEDED(hr)) |
| { |
| /* write game data to registry key */ |
| hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, sConfigApplicationPath, 0, |
| REG_SZ, (LPBYTE)(GameData->sGameInstallDirectory), |
| (lstrlenW(GameData->sGameInstallDirectory)+1)*sizeof(WCHAR))); |
| |
| if(SUCCEEDED(hr)) |
| hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, sConfigGDFBinaryPath, 0, |
| REG_SZ, (LPBYTE)(GameData->sGDFBinaryPath), |
| (lstrlenW(GameData->sGDFBinaryPath)+1)*sizeof(WCHAR))); |
| |
| if(SUCCEEDED(hr)) |
| hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, sApplicationId, 0, |
| REG_SZ, (LPBYTE)(sGameApplicationId), |
| (lstrlenW(sGameApplicationId)+1)*sizeof(WCHAR))); |
| |
| if(SUCCEEDED(hr)) |
| hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, sTitle, 0, |
| REG_SZ, (LPBYTE)(GameData->bstrName), |
| (lstrlenW(GameData->bstrName)+1)*sizeof(WCHAR))); |
| |
| if(SUCCEEDED(hr)) |
| hr = HRESULT_FROM_WIN32(RegSetValueExW(hKey, sDescription, 0, |
| REG_SZ, (LPBYTE)(GameData->bstrDescription ? GameData->bstrDescription : GameData->bstrName), |
| (lstrlenW(GameData->bstrDescription ? GameData->bstrDescription : GameData->bstrName)+1)*sizeof(WCHAR))); |
| |
| RegCloseKey(hKey); |
| |
| if(FAILED(hr)) |
| { |
| /* if something failed, remove whole key */ |
| hr2 = RegDeleteKeyExW(HKEY_LOCAL_MACHINE, lpRegistryKey, KEY_WOW64_64KEY, 0); |
| /* do not overwrite old failure code with new success code */ |
| if(FAILED(hr2)) |
| hr = hr2; |
| } |
| } |
| |
| HeapFree(GetProcessHeap(), 0, lpRegistryKey); |
| TRACE("returning 0x%x\n", hr); |
| return hr; |
| } |
| /******************************************************************************* |
| * GAMEUX_ProcessGameDefinitionElement |
| * |
| * Helper function, parses single element from Game Definition |
| * |
| * Parameters: |
| * lpXMLElement [I] game definition element |
| * GameData [O] structure, where parsed |
| * data will be stored |
| */ |
| static HRESULT GAMEUX_ProcessGameDefinitionElement( |
| IXMLDOMElement *element, |
| struct GAMEUX_GAME_DATA *GameData) |
| { |
| static const WCHAR sName[] = |
| {'N','a','m','e',0}; |
| static const WCHAR sDescription[] = |
| {'D','e','s','c','r','i','p','t','i','o','n',0}; |
| |
| HRESULT hr; |
| BSTR bstrElementName; |
| |
| TRACE("(%p, %p)\n", element, GameData); |
| |
| hr = IXMLDOMElement_get_nodeName(element, &bstrElementName); |
| if(SUCCEEDED(hr)) |
| { |
| /* check element name */ |
| if(lstrcmpW(bstrElementName, sName) == 0) |
| hr = IXMLDOMElement_get_text(element, &GameData->bstrName); |
| |
| else if(lstrcmpW(bstrElementName, sDescription) == 0) |
| hr = IXMLDOMElement_get_text(element, &GameData->bstrDescription); |
| |
| else |
| FIXME("entry %s in Game Definition File not yet supported\n", debugstr_w(bstrElementName)); |
| |
| SysFreeString(bstrElementName); |
| } |
| |
| return hr; |
| } |
| /******************************************************************************* |
| * GAMEUX_ParseGameDefinition |
| * |
| * Helper function, loads data from given XML element into fields of GAME_DATA |
| * structure |
| * |
| * Parameters: |
| * lpXMLGameDefinitionElement [I] Game Definition XML element |
| * GameData [O] structure where data loaded from |
| * XML element will be stored in |
| */ |
| static HRESULT GAMEUX_ParseGameDefinition(IXMLDOMElement *gamedef, struct GAMEUX_GAME_DATA *game_data) |
| { |
| static const WCHAR gameidW[] = {'g','a','m','e','I','D',0}; |
| IXMLDOMNodeList *props; |
| VARIANT var; |
| HRESULT hr; |
| BSTR attr; |
| |
| TRACE("(%p, %p)\n", gamedef, game_data); |
| |
| attr = SysAllocString(gameidW); |
| if (!attr) |
| return E_OUTOFMEMORY; |
| |
| hr = IXMLDOMElement_getAttribute(gamedef, attr, &var); |
| SysFreeString(attr); |
| |
| if (SUCCEEDED(hr)) |
| { |
| hr = CLSIDFromString(V_BSTR(&var), &game_data->guidApplicationId); |
| VariantClear(&var); |
| } |
| |
| if (SUCCEEDED(hr)) |
| hr = IXMLDOMElement_get_childNodes(gamedef, &props); |
| |
| if (FAILED(hr)) |
| return hr; |
| |
| do |
| { |
| IXMLDOMNode *prop; |
| |
| hr = IXMLDOMNodeList_nextNode(props, &prop); |
| if (hr == S_OK) |
| { |
| IXMLDOMElement *element; |
| |
| hr = IXMLDOMNode_QueryInterface(prop, &IID_IXMLDOMElement, (void**)&element); |
| if (hr == S_OK) |
| { |
| hr = GAMEUX_ProcessGameDefinitionElement(element, game_data); |
| IXMLDOMElement_Release(element); |
| } |
| |
| IXMLDOMNode_Release(prop); |
| } |
| } |
| while (hr == S_OK); |
| IXMLDOMNodeList_Release(props); |
| |
| return FAILED(hr) ? hr : S_OK; |
| } |
| |
| struct parse_gdf_thread_param |
| { |
| struct GAMEUX_GAME_DATA *GameData; |
| HRESULT hr; |
| }; |
| |
| /******************************************************************************* |
| * GAMEUX_ParseGDFBinary |
| * |
| * Helper function, loads given binary and parses embed GDF if there's any. |
| * |
| * Parameters: |
| * GameData [I/O] Structure with game's data. Content of field |
| * sGDFBinaryPath defines path to binary, from |
| * which embed GDF will be loaded. Data from |
| * GDF will be stored in other fields of this |
| * structure. |
| */ |
| static DWORD WINAPI GAMEUX_ParseGDFBinary(void *thread_param) |
| { |
| struct parse_gdf_thread_param *ctx = thread_param; |
| struct GAMEUX_GAME_DATA *GameData = ctx->GameData; |
| static const WCHAR sRes[] = {'r','e','s',':','/','/',0}; |
| static const WCHAR sDATA[] = {'D','A','T','A',0}; |
| static const WCHAR sSlash[] = {'/',0}; |
| |
| HRESULT hr = S_OK; |
| WCHAR sResourcePath[MAX_PATH]; |
| VARIANT variant; |
| VARIANT_BOOL isSuccessful; |
| IXMLDOMDocument *document; |
| IXMLDOMNode *gdNode; |
| IXMLDOMElement *root, *gdElement; |
| |
| TRACE("(%p)->sGDFBinaryPath = %s\n", GameData, debugstr_w(GameData->sGDFBinaryPath)); |
| |
| /* prepare path to GDF, using res:// prefix */ |
| lstrcpyW(sResourcePath, sRes); |
| lstrcatW(sResourcePath, GameData->sGDFBinaryPath); |
| lstrcatW(sResourcePath, sSlash); |
| lstrcatW(sResourcePath, sDATA); |
| lstrcatW(sResourcePath, sSlash); |
| lstrcatW(sResourcePath, ID_GDF_XML_STR); |
| |
| CoInitialize(NULL); |
| |
| hr = CoCreateInstance(&CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER, |
| &IID_IXMLDOMDocument, (void**)&document); |
| |
| if(SUCCEEDED(hr)) |
| { |
| /* load GDF into MSXML */ |
| V_VT(&variant) = VT_BSTR; |
| V_BSTR(&variant) = SysAllocString(sResourcePath); |
| if(!V_BSTR(&variant)) |
| hr = E_OUTOFMEMORY; |
| |
| if(SUCCEEDED(hr)) |
| { |
| hr = IXMLDOMDocument_load(document, variant, &isSuccessful); |
| if(hr == S_FALSE || isSuccessful == VARIANT_FALSE) |
| hr = E_FAIL; |
| } |
| |
| SysFreeString(V_BSTR(&variant)); |
| |
| if(SUCCEEDED(hr)) |
| { |
| hr = IXMLDOMDocument_get_documentElement(document, &root); |
| if(hr == S_FALSE) |
| hr = E_FAIL; |
| } |
| |
| if(SUCCEEDED(hr)) |
| { |
| hr = IXMLDOMElement_get_firstChild(root, &gdNode); |
| if(hr == S_FALSE) |
| hr = E_FAIL; |
| |
| if(SUCCEEDED(hr)) |
| { |
| hr = IXMLDOMNode_QueryInterface(gdNode, &IID_IXMLDOMElement, (LPVOID*)&gdElement); |
| if(SUCCEEDED(hr)) |
| { |
| hr = GAMEUX_ParseGameDefinition(gdElement, GameData); |
| IXMLDOMElement_Release(gdElement); |
| } |
| |
| IXMLDOMNode_Release(gdNode); |
| } |
| |
| IXMLDOMElement_Release(root); |
| } |
| |
| IXMLDOMDocument_Release(document); |
| } |
| |
| CoUninitialize(); |
| ctx->hr = hr; |
| return 0; |
| } |
| |
| /******************************************************************* |
| * GAMEUX_RemoveRegistryRecord |
| * |
| * Helper function, removes registry key associated with given game instance |
| */ |
| static HRESULT GAMEUX_RemoveRegistryRecord(GUID* pInstanceID) |
| { |
| HRESULT hr; |
| LPWSTR lpRegistryPath = NULL; |
| TRACE("(%s)\n", debugstr_guid(pInstanceID)); |
| |
| /* first, check is game installed for all users */ |
| hr = GAMEUX_buildGameRegistryPath(GIS_ALL_USERS, pInstanceID, &lpRegistryPath); |
| if(SUCCEEDED(hr)) |
| hr = HRESULT_FROM_WIN32(RegDeleteKeyExW(HKEY_LOCAL_MACHINE, lpRegistryPath, KEY_WOW64_64KEY, 0)); |
| |
| HeapFree(GetProcessHeap(), 0, lpRegistryPath); |
| |
| /* if not, check current user */ |
| if(FAILED(hr)) |
| { |
| hr = GAMEUX_buildGameRegistryPath(GIS_CURRENT_USER, pInstanceID, &lpRegistryPath); |
| if(SUCCEEDED(hr)) |
| hr = HRESULT_FROM_WIN32(RegDeleteKeyExW(HKEY_LOCAL_MACHINE, lpRegistryPath, KEY_WOW64_64KEY, 0)); |
| |
| HeapFree(GetProcessHeap(), 0, lpRegistryPath); |
| } |
| |
| return hr; |
| } |
| /******************************************************************************* |
| * GAMEUX_RegisterGame |
| * |
| * Internal helper function. Registers game associated with given GDF binary in |
| * Game Explorer. Implemented in gameexplorer.c |
| * |
| * Parameters: |
| * sGDFBinaryPath [I] path to binary containing GDF file in |
| * resources |
| * sGameInstallDirectory [I] path to directory, where game installed |
| * its files. |
| * installScope [I] scope of game installation |
| * pInstanceID [I/O] pointer to game instance identifier. |
| * If pointing to GUID_NULL, then new |
| * identifier will be generated automatically |
| * and returned via this parameter |
| */ |
| static HRESULT GAMEUX_RegisterGame(LPCWSTR sGDFBinaryPath, |
| LPCWSTR sGameInstallDirectory, |
| GAME_INSTALL_SCOPE installScope, |
| GUID *pInstanceID) |
| { |
| HRESULT hr = S_OK; |
| struct GAMEUX_GAME_DATA GameData; |
| |
| TRACE("(%s, %s, 0x%x, %s)\n", debugstr_w(sGDFBinaryPath), debugstr_w(sGameInstallDirectory), installScope, debugstr_guid(pInstanceID)); |
| |
| GAMEUX_initGameData(&GameData); |
| GameData.sGDFBinaryPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(sGDFBinaryPath)+1)*sizeof(WCHAR)); |
| lstrcpyW(GameData.sGDFBinaryPath, sGDFBinaryPath); |
| GameData.sGameInstallDirectory = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(sGameInstallDirectory)+1)*sizeof(WCHAR)); |
| lstrcpyW(GameData.sGameInstallDirectory, sGameInstallDirectory); |
| GameData.installScope = installScope; |
| |
| /* generate GUID if it was not provided by user */ |
| if(IsEqualGUID(pInstanceID, &GUID_NULL)) |
| hr = CoCreateGuid(pInstanceID); |
| |
| GameData.guidInstanceId = *pInstanceID; |
| |
| /* load data from GDF binary */ |
| if(SUCCEEDED(hr)) |
| { |
| struct parse_gdf_thread_param thread_param; |
| HANDLE thread; |
| DWORD ret; |
| |
| thread_param.GameData = &GameData; |
| if(!(thread = CreateThread(NULL, 0, GAMEUX_ParseGDFBinary, &thread_param, 0, &ret))) |
| { |
| ERR("Failed to create thread.\n"); |
| hr = E_FAIL; |
| goto done; |
| } |
| ret = WaitForSingleObject(thread, INFINITE); |
| CloseHandle(thread); |
| if(ret != WAIT_OBJECT_0) |
| { |
| ERR("Wait failed (%#x).\n", ret); |
| hr = E_FAIL; |
| goto done; |
| } |
| hr = thread_param.hr; |
| } |
| |
| /* save data to registry */ |
| if(SUCCEEDED(hr)) |
| hr = GAMEUX_WriteRegistryRecord(&GameData); |
| |
| done: |
| GAMEUX_uninitGameData(&GameData); |
| TRACE("returning 0x%08x\n", hr); |
| return hr; |
| } |
| /******************************************************************************* |
| * GAMEUX_IsGameKeyExist |
| * |
| * Helper function, checks if game's registry ath exists in given scope |
| * |
| * Parameters: |
| * installScope [I] scope to search game in |
| * InstanceID [I] game instance identifier |
| * lpRegistryPath [O] place to store address of registry path to |
| * the game. It is filled only if key exists. |
| * It must be freed by HeapFree(GetProcessHeap(), 0, ...) |
| * |
| * Returns: |
| * S_OK key was found properly |
| * S_FALSE key does not exists |
| * |
| */ |
| static HRESULT GAMEUX_IsGameKeyExist(GAME_INSTALL_SCOPE installScope, |
| LPCGUID InstanceID, |
| LPWSTR* lpRegistryPath) { |
| |
| HRESULT hr; |
| HKEY hKey; |
| |
| hr = GAMEUX_buildGameRegistryPath(installScope, InstanceID, lpRegistryPath); |
| |
| if(SUCCEEDED(hr)) |
| hr = HRESULT_FROM_WIN32(RegOpenKeyExW(HKEY_LOCAL_MACHINE, *lpRegistryPath, |
| 0, KEY_WOW64_64KEY, &hKey)); |
| |
| if(hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) |
| hr = S_FALSE; |
| |
| if(hr == S_OK) |
| RegCloseKey(hKey); |
| else |
| { |
| /* if the key does not exist or another error occurred, do not return the path */ |
| HeapFree(GetProcessHeap(), 0, *lpRegistryPath); |
| *lpRegistryPath = NULL; |
| } |
| |
| return hr; |
| } |
| /******************************************************************************* |
| * GAMEUX_LoadRegistryString |
| * |
| * Helper function, loads string from registry value and allocates buffer for it |
| */ |
| static HRESULT GAMEUX_LoadRegistryString(HKEY hRootKey, |
| LPCWSTR lpRegistryKey, |
| LPCWSTR lpRegistryValue, |
| LPWSTR* lpValue) |
| { |
| HRESULT hr; |
| DWORD dwSize; |
| |
| *lpValue = NULL; |
| |
| hr = HRESULT_FROM_WIN32(RegGetValueW(hRootKey, lpRegistryKey, lpRegistryValue, |
| RRF_RT_REG_SZ, NULL, NULL, &dwSize)); |
| |
| if(SUCCEEDED(hr)) |
| { |
| *lpValue = HeapAlloc(GetProcessHeap(), 0, dwSize); |
| if(!*lpValue) |
| hr = E_OUTOFMEMORY; |
| } |
| |
| if(SUCCEEDED(hr)) |
| hr = HRESULT_FROM_WIN32(RegGetValueW(hRootKey, lpRegistryKey, lpRegistryValue, |
| RRF_RT_REG_SZ, NULL, *lpValue, &dwSize)); |
| |
| return hr; |
| } |
| /******************************************************************************* |
| * GAMEUX_UpdateGame |
| * |
| * Helper function, updates stored data about game with given InstanceID |
| */ |
| static HRESULT GAMEUX_UpdateGame(LPGUID InstanceID) { |
| static const WCHAR sConfigGDFBinaryPath[] = {'C','o','n','f','i','g','G','D','F','B','i','n','a','r','y','P','a','t','h',0}; |
| static const WCHAR sConfigApplicationPath[] = {'C','o','n','f','i','g','A','p','p','l','i','c','a','t','i','o','n','P','a','t','h',0}; |
| |
| HRESULT hr; |
| GAME_INSTALL_SCOPE installScope; |
| LPWSTR lpRegistryPath; |
| LPWSTR lpGDFBinaryPath; |
| |
| TRACE("(%s)\n", debugstr_guid(InstanceID)); |
| |
| /* first, check is game exists in CURRENT_USER scope */ |
| installScope = GIS_CURRENT_USER; |
| hr = GAMEUX_IsGameKeyExist(installScope, InstanceID, &lpRegistryPath); |
| |
| if(hr == S_FALSE) |
| { |
| /* game not found in CURRENT_USER scope, let's check in ALL_USERS */ |
| installScope = GIS_ALL_USERS; |
| hr = GAMEUX_IsGameKeyExist(installScope, InstanceID, &lpRegistryPath); |
| } |
| |
| if(hr == S_FALSE) |
| /* still not found? let's inform user that game does not exists */ |
| hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); |
| |
| if(SUCCEEDED(hr)) |
| { |
| WCHAR *lpGameInstallDirectory = NULL; |
| |
| /* game found, its registry path is in lpRegistryPath and install |
| * scope in installScope */ |
| TRACE("game found in registry (path %s), updating\n", debugstr_w(lpRegistryPath)); |
| |
| /* first, read required data about game */ |
| hr = GAMEUX_LoadRegistryString(HKEY_LOCAL_MACHINE, lpRegistryPath, |
| sConfigGDFBinaryPath, &lpGDFBinaryPath); |
| |
| if(SUCCEEDED(hr)) |
| hr = GAMEUX_LoadRegistryString(HKEY_LOCAL_MACHINE, lpRegistryPath, |
| sConfigApplicationPath, &lpGameInstallDirectory); |
| |
| /* now remove currently existing registry key */ |
| if(SUCCEEDED(hr)) |
| hr = GAMEUX_RemoveRegistryRecord(InstanceID); |
| |
| /* and add it again, it will cause in reparsing of whole GDF */ |
| if(SUCCEEDED(hr)) |
| hr = GAMEUX_RegisterGame(lpGDFBinaryPath, lpGameInstallDirectory, |
| installScope, InstanceID); |
| |
| HeapFree(GetProcessHeap(), 0, lpGDFBinaryPath); |
| HeapFree(GetProcessHeap(), 0, lpGameInstallDirectory); |
| } |
| |
| HeapFree(GetProcessHeap(), 0, lpRegistryPath); |
| TRACE("returning 0x%x\n", hr); |
| return hr; |
| } |
| /******************************************************************************* |
| * GAMEUX_FindGameInstanceId |
| * |
| * Internal helper function. Description available in gameux_private.h file |
| */ |
| HRESULT GAMEUX_FindGameInstanceId( |
| LPCWSTR sGDFBinaryPath, |
| GAME_INSTALL_SCOPE installScope, |
| GUID* pInstanceId) |
| { |
| static const WCHAR sConfigGDFBinaryPath[] = |
| {'C','o','n','f','i','g','G','D','F','B','i','n','a','r','y','P','a','t','h',0}; |
| |
| HRESULT hr; |
| BOOL found = FALSE; |
| LPWSTR lpRegistryPath = NULL; |
| HKEY hRootKey; |
| DWORD dwSubKeys, dwSubKeyLen, dwMaxSubKeyLen, i; |
| LPWSTR lpName = NULL, lpValue = NULL; |
| |
| hr = GAMEUX_buildGameRegistryPath(installScope, NULL, &lpRegistryPath); |
| |
| if(SUCCEEDED(hr)) |
| /* enumerate all subkeys of received one and search them for value "ConfigGGDFBinaryPath" */ |
| hr = HRESULT_FROM_WIN32(RegOpenKeyExW(HKEY_LOCAL_MACHINE, |
| lpRegistryPath, 0, KEY_READ | KEY_WOW64_64KEY, &hRootKey)); |
| |
| if(SUCCEEDED(hr)) |
| { |
| hr = HRESULT_FROM_WIN32(RegQueryInfoKeyW(hRootKey, NULL, NULL, NULL, |
| &dwSubKeys, &dwMaxSubKeyLen, NULL, NULL, NULL, NULL, NULL, NULL)); |
| |
| if(SUCCEEDED(hr)) |
| { |
| ++dwMaxSubKeyLen; /* for string terminator */ |
| lpName = HeapAlloc(GetProcessHeap(), 0, dwMaxSubKeyLen*sizeof(WCHAR)); |
| if(!lpName) hr = E_OUTOFMEMORY; |
| } |
| |
| if(SUCCEEDED(hr)) |
| { |
| for(i=0; i<dwSubKeys && !found; ++i) |
| { |
| dwSubKeyLen = dwMaxSubKeyLen; |
| hr = HRESULT_FROM_WIN32(RegEnumKeyExW(hRootKey, i, lpName, &dwSubKeyLen, |
| NULL, NULL, NULL, NULL)); |
| |
| if(SUCCEEDED(hr)) |
| hr = GAMEUX_LoadRegistryString(hRootKey, lpName, |
| sConfigGDFBinaryPath, &lpValue); |
| |
| if(SUCCEEDED(hr)) |
| { |
| if(lstrcmpW(lpValue, sGDFBinaryPath)==0) |
| { |
| /* key found, let's copy instance id and exit */ |
| hr = CLSIDFromString(lpName, pInstanceId); |
| found = TRUE; |
| } |
| HeapFree(GetProcessHeap(), 0, lpValue); |
| } |
| } |
| } |
| |
| HeapFree(GetProcessHeap(), 0, lpName); |
| RegCloseKey(hRootKey); |
| } |
| |
| HeapFree(GetProcessHeap(), 0, lpRegistryPath); |
| |
| if((SUCCEEDED(hr) && !found) || hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) |
| hr = S_FALSE; |
| |
| return hr; |
| } |
| /******************************************************************************* |
| * GameExplorer implementation |
| */ |
| |
| typedef struct _GameExplorerImpl |
| { |
| IGameExplorer IGameExplorer_iface; |
| IGameExplorer2 IGameExplorer2_iface; |
| LONG ref; |
| } GameExplorerImpl; |
| |
| static inline GameExplorerImpl *impl_from_IGameExplorer(IGameExplorer *iface) |
| { |
| return CONTAINING_RECORD(iface, GameExplorerImpl, IGameExplorer_iface); |
| } |
| |
| static inline GameExplorerImpl *impl_from_IGameExplorer2(IGameExplorer2 *iface) |
| { |
| return CONTAINING_RECORD(iface, GameExplorerImpl, IGameExplorer2_iface); |
| } |
| |
| static HRESULT WINAPI GameExplorerImpl_QueryInterface( |
| IGameExplorer *iface, |
| REFIID riid, |
| void **ppvObject) |
| { |
| GameExplorerImpl *This = impl_from_IGameExplorer(iface); |
| |
| TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppvObject); |
| |
| *ppvObject = NULL; |
| |
| if(IsEqualGUID(riid, &IID_IUnknown) || |
| IsEqualGUID(riid, &IID_IGameExplorer)) |
| { |
| *ppvObject = &This->IGameExplorer_iface; |
| } |
| else if(IsEqualGUID(riid, &IID_IGameExplorer2)) |
| { |
| *ppvObject = &This->IGameExplorer2_iface; |
| } |
| else |
| { |
| FIXME("interface %s not implemented\n", debugstr_guid(riid)); |
| return E_NOINTERFACE; |
| } |
| |
| IGameExplorer_AddRef(iface); |
| return S_OK; |
| } |
| |
| static ULONG WINAPI GameExplorerImpl_AddRef(IGameExplorer *iface) |
| { |
| GameExplorerImpl *This = impl_from_IGameExplorer(iface); |
| LONG ref; |
| |
| ref = InterlockedIncrement(&This->ref); |
| |
| TRACE("(%p): ref=%d\n", This, ref); |
| return ref; |
| } |
| |
| static ULONG WINAPI GameExplorerImpl_Release(IGameExplorer *iface) |
| { |
| GameExplorerImpl *This = impl_from_IGameExplorer(iface); |
| LONG ref; |
| |
| ref = InterlockedDecrement(&This->ref); |
| TRACE("(%p): ref=%d\n", This, ref); |
| |
| if(ref == 0) |
| { |
| TRACE("freeing GameExplorer object\n"); |
| HeapFree(GetProcessHeap(), 0, This); |
| } |
| |
| return ref; |
| } |
| |
| static HRESULT WINAPI GameExplorerImpl_AddGame( |
| IGameExplorer *iface, |
| BSTR bstrGDFBinaryPath, |
| BSTR sGameInstallDirectory, |
| GAME_INSTALL_SCOPE installScope, |
| GUID *pInstanceID) |
| { |
| GameExplorerImpl *This = impl_from_IGameExplorer(iface); |
| TRACE("(%p, %s, %s, 0x%x, %s)\n", This, debugstr_w(bstrGDFBinaryPath), debugstr_w(sGameInstallDirectory), installScope, debugstr_guid(pInstanceID)); |
| return GAMEUX_RegisterGame(bstrGDFBinaryPath, sGameInstallDirectory, installScope, pInstanceID); |
| } |
| |
| static HRESULT WINAPI GameExplorerImpl_RemoveGame( |
| IGameExplorer *iface, |
| GUID instanceID) |
| { |
| GameExplorerImpl *This = impl_from_IGameExplorer(iface); |
| |
| TRACE("(%p, %s)\n", This, debugstr_guid(&instanceID)); |
| return GAMEUX_RemoveRegistryRecord(&instanceID); |
| } |
| |
| static HRESULT WINAPI GameExplorerImpl_UpdateGame( |
| IGameExplorer *iface, |
| GUID instanceID) |
| { |
| GameExplorerImpl *This = impl_from_IGameExplorer(iface); |
| |
| TRACE("(%p, %s)\n", This, debugstr_guid(&instanceID)); |
| return GAMEUX_UpdateGame(&instanceID); |
| } |
| |
| static HRESULT WINAPI GameExplorerImpl_VerifyAccess( |
| IGameExplorer *iface, |
| BSTR sGDFBinaryPath, |
| BOOL *pHasAccess) |
| { |
| GameExplorerImpl *This = impl_from_IGameExplorer(iface); |
| |
| FIXME("(%p, %s, %p)\n", This, debugstr_w(sGDFBinaryPath), pHasAccess); |
| *pHasAccess = TRUE; |
| return S_OK; |
| } |
| |
| static const struct IGameExplorerVtbl GameExplorerImplVtbl = |
| { |
| GameExplorerImpl_QueryInterface, |
| GameExplorerImpl_AddRef, |
| GameExplorerImpl_Release, |
| GameExplorerImpl_AddGame, |
| GameExplorerImpl_RemoveGame, |
| GameExplorerImpl_UpdateGame, |
| GameExplorerImpl_VerifyAccess |
| }; |
| |
| |
| static HRESULT WINAPI GameExplorer2Impl_QueryInterface( |
| IGameExplorer2 *iface, |
| REFIID riid, |
| void **ppvObject) |
| { |
| GameExplorerImpl *This = impl_from_IGameExplorer2(iface); |
| return GameExplorerImpl_QueryInterface(&This->IGameExplorer_iface, riid, ppvObject); |
| } |
| |
| static ULONG WINAPI GameExplorer2Impl_AddRef(IGameExplorer2 *iface) |
| { |
| GameExplorerImpl *This = impl_from_IGameExplorer2(iface); |
| return GameExplorerImpl_AddRef(&This->IGameExplorer_iface); |
| } |
| |
| static ULONG WINAPI GameExplorer2Impl_Release(IGameExplorer2 *iface) |
| { |
| GameExplorerImpl *This = impl_from_IGameExplorer2(iface); |
| return GameExplorerImpl_Release(&This->IGameExplorer_iface); |
| } |
| |
| static HRESULT WINAPI GameExplorer2Impl_CheckAccess( |
| IGameExplorer2 *iface, |
| LPCWSTR binaryGDFPath, |
| BOOL *pHasAccess) |
| { |
| GameExplorerImpl *This = impl_from_IGameExplorer2(iface); |
| FIXME("stub (%p, %s, %p)\n", This, debugstr_w(binaryGDFPath), pHasAccess); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI GameExplorer2Impl_InstallGame( |
| IGameExplorer2 *iface, |
| LPCWSTR binaryGDFPath, |
| LPCWSTR installDirectory, |
| GAME_INSTALL_SCOPE installScope) |
| { |
| HRESULT hr; |
| GUID instanceId; |
| GameExplorerImpl *This = impl_from_IGameExplorer2(iface); |
| |
| TRACE("(%p, %s, %s, 0x%x)\n", This, debugstr_w(binaryGDFPath), debugstr_w(installDirectory), installScope); |
| |
| if(!binaryGDFPath) |
| return E_INVALIDARG; |
| |
| hr = GAMEUX_FindGameInstanceId(binaryGDFPath, GIS_CURRENT_USER, &instanceId); |
| |
| if(hr == S_FALSE) |
| hr = GAMEUX_FindGameInstanceId(binaryGDFPath, GIS_ALL_USERS, &instanceId); |
| |
| if(hr == S_FALSE) |
| { |
| /* if game isn't yet registered, then install it */ |
| instanceId = GUID_NULL; |
| hr = GAMEUX_RegisterGame(binaryGDFPath, installDirectory, installScope, &instanceId); |
| } |
| else if(hr == S_OK) |
| /* otherwise, update game */ |
| hr = GAMEUX_UpdateGame(&instanceId); |
| |
| return hr; |
| } |
| |
| static HRESULT WINAPI GameExplorer2Impl_UninstallGame( |
| IGameExplorer2 *iface, |
| LPCWSTR binaryGDFPath) |
| { |
| HRESULT hr; |
| GUID instanceId; |
| GameExplorerImpl *This = impl_from_IGameExplorer2(iface); |
| TRACE("(%p, %s)\n", This, debugstr_w(binaryGDFPath)); |
| |
| if(!binaryGDFPath) |
| return E_INVALIDARG; |
| |
| hr = GAMEUX_FindGameInstanceId(binaryGDFPath, GIS_CURRENT_USER, &instanceId); |
| |
| if(hr == S_FALSE) |
| hr = GAMEUX_FindGameInstanceId(binaryGDFPath, GIS_ALL_USERS, &instanceId); |
| |
| if(hr == S_OK) |
| hr = GAMEUX_RemoveRegistryRecord(&instanceId); |
| |
| return hr; |
| } |
| |
| static const struct IGameExplorer2Vtbl GameExplorer2ImplVtbl = |
| { |
| GameExplorer2Impl_QueryInterface, |
| GameExplorer2Impl_AddRef, |
| GameExplorer2Impl_Release, |
| GameExplorer2Impl_InstallGame, |
| GameExplorer2Impl_UninstallGame, |
| GameExplorer2Impl_CheckAccess |
| }; |
| |
| /* |
| * Construction routine |
| */ |
| HRESULT GameExplorer_create( |
| IUnknown* pUnkOuter, |
| IUnknown** ppObj) |
| { |
| GameExplorerImpl *pGameExplorer; |
| |
| TRACE("(%p, %p)\n", pUnkOuter, ppObj); |
| |
| pGameExplorer = HeapAlloc(GetProcessHeap(), 0, sizeof(*pGameExplorer)); |
| |
| if(!pGameExplorer) |
| return E_OUTOFMEMORY; |
| |
| pGameExplorer->IGameExplorer_iface.lpVtbl = &GameExplorerImplVtbl; |
| pGameExplorer->IGameExplorer2_iface.lpVtbl = &GameExplorer2ImplVtbl; |
| pGameExplorer->ref = 1; |
| |
| *ppObj = (IUnknown*)&pGameExplorer->IGameExplorer_iface; |
| |
| TRACE("returning iface: %p\n", *ppObj); |
| return S_OK; |
| } |