2 * Gameux library coclass GameStatistics 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
31 #include "gameux_private.h"
33 #include "wine/debug.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(gameux);
38 * constant definitions
40 #define MAX_CATEGORY_LENGTH 60
41 #define MAX_NAME_LENGTH 30
42 #define MAX_VALUE_LENGTH 30
43 #define MAX_CATEGORIES 10
44 #define MAX_STATS_PER_CATEGORY 10
45 /*******************************************************************************
46 * Game statistics helper components
48 /*******************************************************************************
51 * set of structures for containing game's data
53 struct GAMEUX_STATS_STAT
55 WCHAR sName[MAX_NAME_LENGTH+1];
56 WCHAR sValue[MAX_VALUE_LENGTH+1];
58 struct GAMEUX_STATS_CATEGORY
60 WCHAR sName[MAX_CATEGORY_LENGTH+1];
61 struct GAMEUX_STATS_STAT stats[MAX_STATS_PER_CATEGORY];
65 WCHAR sStatsFile[MAX_PATH];
66 struct GAMEUX_STATS_CATEGORY categories[MAX_CATEGORIES];
68 /*******************************************************************************
69 * GAMEUX_createStatsDirectory
71 * Helper function, creates directory to store game statistics
74 * path [I] path to game statistics file.
75 * base directory of this file will
76 * be created if it doesn't exists
78 static HRESULT GAMEUX_createStatsDirectory(LPCWSTR lpFilePath)
81 WCHAR lpDirectoryPath[MAX_PATH];
84 lpEnd = StrRChrW(lpFilePath, NULL, '\\');
85 lstrcpynW(lpDirectoryPath, lpFilePath, lpEnd-lpFilePath+1);
87 hr = HRESULT_FROM_WIN32(SHCreateDirectoryExW(NULL, lpDirectoryPath, NULL));
89 if(hr == HRESULT_FROM_WIN32(ERROR_FILE_EXISTS) ||
90 hr == HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS))
95 /*******************************************************************
96 * GAMEUX_updateStatisticsFile
98 * Helper function updating data stored in statistics file
101 * data [I] pointer to struct containing
104 static HRESULT GAMEUX_updateStatisticsFile(struct GAMEUX_STATS *stats)
106 static const WCHAR sStatistics[] = {'S','t','a','t','i','s','t','i','c','s',0};
107 static const WCHAR sCategory[] = {'C','a','t','e','g','o','r','y',0};
108 static const WCHAR sIndex[] = {'I','n','d','e','x',0};
109 static const WCHAR sStatistic[] = {'S','t','a','t','i','s','t','i','c',0};
110 static const WCHAR sName[] = {'N','a','m','e',0};
111 static const WCHAR sValue[] = {'V','a','l','u','e',0};
114 IXMLDOMDocument *document;
115 IXMLDOMElement *root, *categoryElement, *statisticsElement;
116 IXMLDOMNode *categoryNode, *statisticsNode;
117 VARIANT vStatsFilePath, vValue;
118 BSTR bstrStatistics = NULL, bstrCategory = NULL, bstrIndex = NULL,
119 bstrStatistic = NULL, bstrName = NULL, bstrValue = NULL;
122 TRACE("(%p)\n", stats);
124 V_VT(&vStatsFilePath) = VT_BSTR;
125 V_BSTR(&vStatsFilePath) = SysAllocString(stats->sStatsFile);
126 if(!V_BSTR(&vStatsFilePath))
130 hr = CoCreateInstance(&CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER,
131 &IID_IXMLDOMDocument, (void**)&document);
135 bstrStatistics = SysAllocString(sStatistics);
141 hr = IXMLDOMDocument_createElement(document, bstrStatistics, &root);
145 bstrCategory = SysAllocString(sCategory);
152 bstrIndex = SysAllocString(sIndex);
159 bstrStatistic = SysAllocString(sStatistic);
166 bstrName = SysAllocString(sName);
173 bstrValue = SysAllocString(sValue);
181 for(i=0; i<MAX_CATEGORIES; ++i)
183 if(lstrlenW(stats->categories[i].sName)==0)
186 V_VT(&vValue) = VT_INT;
187 V_INT(&vValue) = NODE_ELEMENT;
189 hr = IXMLDOMDocument_createNode(document, vValue, bstrCategory, NULL, &categoryNode);
192 hr = IXMLDOMNode_QueryInterface(categoryNode, &IID_IXMLDOMElement, (LPVOID*)&categoryElement);
196 hr = IXMLDOMElement_setAttribute(categoryElement, bstrIndex, vValue);
200 V_VT(&vValue) = VT_BSTR;
201 V_BSTR(&vValue) = SysAllocString(stats->categories[i].sName);
208 TRACE("storing category %d: %s\n", i, debugstr_w(V_BSTR(&vValue)));
209 hr = IXMLDOMElement_setAttribute(categoryElement, bstrName, vValue);
212 SysFreeString(V_BSTR(&vValue));
216 for(j=0; j<MAX_STATS_PER_CATEGORY; ++j)
218 if(lstrlenW(stats->categories[i].stats[j].sName)==0)
221 V_VT(&vValue) = VT_INT;
222 V_INT(&vValue) = NODE_ELEMENT;
224 hr = IXMLDOMDocument_createNode(document, vValue, bstrStatistic, NULL, &statisticsNode);
227 hr = IXMLDOMNode_QueryInterface(statisticsNode, &IID_IXMLDOMElement, (LPVOID*)&statisticsElement);
231 hr = IXMLDOMElement_setAttribute(statisticsElement, bstrIndex, vValue);
235 V_VT(&vValue) = VT_BSTR;
236 V_BSTR(&vValue) = SysAllocString(stats->categories[i].stats[j].sName);
243 TRACE(" storing statistic %d: name: %s\n", j, debugstr_w(V_BSTR(&vValue)));
244 hr = IXMLDOMElement_setAttribute(statisticsElement, bstrName, vValue);
247 SysFreeString(V_BSTR(&vValue));
251 V_VT(&vValue) = VT_BSTR;
252 V_BSTR(&vValue) = SysAllocString(stats->categories[i].stats[j].sValue);
259 TRACE(" storing statistic %d: name: %s\n", j, debugstr_w(V_BSTR(&vValue)));
260 hr = IXMLDOMElement_setAttribute(statisticsElement, bstrValue, vValue);
263 SysFreeString(V_BSTR(&vValue));
266 hr = IXMLDOMElement_appendChild(categoryNode, statisticsNode, &statisticsNode);
268 IXMLDOMElement_Release(statisticsElement);
269 IXMLDOMNode_Release(statisticsNode);
274 hr = IXMLDOMElement_appendChild(root, categoryNode, &categoryNode);
276 IXMLDOMElement_Release(categoryElement);
277 IXMLDOMNode_Release(categoryNode);
284 hr = IXMLDOMDocument_putref_documentElement(document, root);
286 IXMLDOMElement_Release(root);
288 TRACE("saving game statistics in %s file\n", debugstr_w(stats->sStatsFile));
290 hr = GAMEUX_createStatsDirectory(stats->sStatsFile);
293 hr = IXMLDOMDocument_save(document, vStatsFilePath);
295 IXMLDOMDocument_Release(document);
297 SysFreeString(bstrValue);
298 SysFreeString(bstrName);
299 SysFreeString(bstrStatistic);
300 SysFreeString(bstrIndex);
301 SysFreeString(bstrCategory);
302 SysFreeString(bstrStatistics);
303 SysFreeString(V_BSTR(&vStatsFilePath));
304 TRACE("ret=0x%x\n", hr);
307 /*******************************************************************************
308 * GAMEUX_buildStatisticsFilePath
309 * Creates path to file contaning statistics of game with given id.
312 * lpApplicationId [I] application id of game,
314 * lpStatisticsFile [O] array where path will be
315 * stored. It's size must be
318 static HRESULT GAMEUX_buildStatisticsFilePath(
319 LPCWSTR lpApplicationId,
320 LPWSTR lpStatisticsFile)
322 static const WCHAR sBackslash[] = {'\\',0};
323 static const WCHAR sStatisticsDir[] = {'\\','M','i','c','r','o','s','o','f','t',
324 '\\','W','i','n','d','o','w','s','\\','G','a','m','e','E','x','p',
325 'l','o','r','e','r','\\','G','a','m','e','S','t','a','t','i','s',
327 static const WCHAR sDotGamestats[] = {'.','g','a','m','e','s','t','a','t','s',0};
331 hr = SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, lpStatisticsFile);
335 lstrcatW(lpStatisticsFile, sStatisticsDir);
336 lstrcatW(lpStatisticsFile, lpApplicationId);
337 lstrcatW(lpStatisticsFile, sBackslash);
338 lstrcatW(lpStatisticsFile, lpApplicationId);
339 lstrcatW(lpStatisticsFile, sDotGamestats);
344 /*******************************************************************************
345 * GAMEUX_getAppIdFromGDFPath
347 * Loads application identifier associated with given GDF binary.
348 * Routine reads identifier from registry, so will fail if game
352 * GDFBinaryPath [I] path to gdf binary
353 * lpApplicationId [O] place to store application id.
354 * must be at least 49 characters
355 * to store guid and termination 0
357 static HRESULT GAMEUX_getAppIdFromGDFPath(
358 LPCWSTR GDFBinaryPath,
359 LPWSTR lpApplicationId)
361 static const WCHAR sApplicationId[] =
362 {'A','p','p','l','i','c','a','t','i','o','n','I','d',0};
365 GAME_INSTALL_SCOPE installScope;
367 LPWSTR lpRegistryPath;
368 DWORD dwLength = 49*sizeof(WCHAR);/* place for GUID */
370 TRACE("(%s, %p)\n", debugstr_w(GDFBinaryPath), lpApplicationId);
375 installScope = GIS_CURRENT_USER;
376 hr = GAMEUX_FindGameInstanceId(GDFBinaryPath, installScope, &instanceId);
380 installScope = GIS_ALL_USERS;
381 hr = GAMEUX_FindGameInstanceId(GDFBinaryPath, installScope, &instanceId);
385 /* game not registered, so statistics cannot be used */
389 /* game is registered, let's read it's application id from registry */
390 hr = GAMEUX_buildGameRegistryPath(installScope, &instanceId, &lpRegistryPath);
393 hr = HRESULT_FROM_WIN32(RegGetValueW(HKEY_LOCAL_MACHINE,
394 lpRegistryPath, sApplicationId, RRF_RT_REG_SZ,
395 NULL, lpApplicationId, &dwLength));
397 HeapFree(GetProcessHeap(), 0, lpRegistryPath);
399 TRACE("found app id: %s, return: %#x\n", debugstr_w(lpApplicationId), hr);
402 /*******************************************************************
403 * GAMEUX_loadGameStatisticsFromFile
404 * Helper function, loads game statistics from file and stores them
408 * data [I/O] structure containing file name to
409 * load and data fields to store data in
411 static HRESULT GAMEUX_loadStatisticsFromFile(struct GAMEUX_STATS *data)
413 static const WCHAR sStatistics[] = {'S','t','a','t','i','s','t','i','c','s',0};
414 static const WCHAR sCategory[] = {'C','a','t','e','g','o','r','y',0};
415 static const WCHAR sIndex[] = {'I','n','d','e','x',0};
416 static const WCHAR sStatistic[] = {'S','t','a','t','i','s','t','i','c',0};
417 static const WCHAR sName[] = {'N','a','m','e',0};
418 static const WCHAR sValue[] = {'V','a','l','u','e',0};
421 IXMLDOMDocument *document = NULL;
422 IXMLDOMElement *root = NULL, *categoryElement, *statisticElement;
423 IXMLDOMNode *categoryNode, *statisticNode;
424 IXMLDOMNodeList *rootChildren = NULL, *categoryChildren;
425 VARIANT vStatsFilePath, vValue;
426 BSTR bstrStatistics = NULL, bstrCategory = NULL, bstrIndex = NULL,
427 bstrStatistic = NULL, bstrName = NULL, bstrValue = NULL;
428 VARIANT_BOOL isSuccessful = VARIANT_FALSE;
431 TRACE("(%p)\n", data);
433 V_VT(&vStatsFilePath) = VT_BSTR;
434 V_BSTR(&vStatsFilePath) = SysAllocString(data->sStatsFile);
435 if(!V_BSTR(&vStatsFilePath))
439 hr = CoCreateInstance(&CLSID_DOMDocument30, NULL, CLSCTX_INPROC_SERVER, &IID_IXMLDOMDocument, (void**)&document);
443 bstrStatistics = SysAllocString(sStatistics);
450 bstrCategory = SysAllocString(sCategory);
457 bstrIndex = SysAllocString(sIndex);
464 bstrStatistic = SysAllocString(sStatistic);
471 bstrName = SysAllocString(sName);
478 bstrValue = SysAllocString(sValue);
484 hr = IXMLDOMDocument_load(document, vStatsFilePath, &isSuccessful);
486 if(hr == S_OK && isSuccessful != VARIANT_TRUE)
490 hr = IXMLDOMDocument_get_documentElement(document, &root);
493 hr = IXMLDOMElement_get_childNodes(root, &rootChildren);
500 hr = IXMLDOMNodeList_nextNode(rootChildren, &categoryNode);
504 hr = IXMLDOMNode_QueryInterface(categoryNode, &IID_IXMLDOMElement, (LPVOID*)&categoryElement);
508 hr = IXMLDOMElement_getAttribute(categoryElement, bstrIndex, &vValue);
509 if( hr == S_OK && V_VT(&vValue) != VT_BSTR)
514 i = StrToIntW(V_BSTR(&vValue));
515 hr = IXMLDOMElement_getAttribute(categoryElement, bstrName, &vValue);
516 if( hr == S_OK && V_VT(&vValue) != VT_BSTR)
522 lstrcpynW(data->categories[i].sName, V_BSTR(&vValue), MAX_CATEGORY_LENGTH);
523 TRACE("category %d name %s\n", i, debugstr_w(data->categories[i].sName));
524 hr = IXMLDOMElement_get_childNodes(categoryElement, &categoryChildren);
532 hr = IXMLDOMNodeList_nextNode(categoryChildren, &statisticNode);
536 hr = IXMLDOMNode_QueryInterface(statisticNode, &IID_IXMLDOMElement, (LPVOID*)&statisticElement);
540 hr = IXMLDOMElement_getAttribute(statisticElement, bstrIndex, &vValue);
541 if( hr == S_OK && V_VT(&vValue) != VT_BSTR)
546 j = StrToIntW(V_BSTR(&vValue));
547 hr = IXMLDOMElement_getAttribute(statisticElement, bstrName, &vValue);
548 if( hr == S_OK && V_VT(&vValue) != VT_BSTR)
554 lstrcpynW(data->categories[i].stats[j].sName, V_BSTR(&vValue), MAX_NAME_LENGTH);
555 hr = IXMLDOMElement_getAttribute(statisticElement, bstrValue, &vValue);
556 if( hr == S_OK && V_VT(&vValue) != VT_BSTR)
561 lstrcpynW(data->categories[i].stats[j].sValue, V_BSTR(&vValue), MAX_VALUE_LENGTH);
563 TRACE(" statistic %d name %s value %s\n", j,
564 debugstr_w(data->categories[i].stats[j].sName),
565 debugstr_w(data->categories[i].stats[j].sValue));
566 IXMLDOMElement_Release(statisticElement);
569 IXMLDOMNode_Release(statisticNode);
576 IXMLDOMElement_Release(categoryElement);
579 IXMLDOMNode_Release(categoryNode);
586 if(rootChildren) IXMLDOMNodeList_Release(rootChildren);
587 if(root) IXMLDOMElement_Release(root);
588 if(document) IXMLDOMDocument_Release(document);
590 SysFreeString(bstrValue);
591 SysFreeString(bstrName);
592 SysFreeString(bstrStatistic);
593 SysFreeString(bstrIndex);
594 SysFreeString(bstrCategory);
595 SysFreeString(bstrStatistics);
596 SysFreeString(V_BSTR(&vStatsFilePath));
599 /*******************************************************************
600 * GAMEUX_loadGameStatistics
602 * Helper function which loads game statistics associated with game
603 * into interface's internal structures
606 * pStats [O] structure which will receive data
607 * sGameId [I] application instance Id, stored as string
608 * to avoid additional conversions
609 * openType [I] allowed ways of opening statistics
610 * pOpenResult [O] way used to open statistics
613 static HRESULT GAMEUX_loadGameStatistics(struct GAMEUX_STATS *pStats,
615 GAMESTATS_OPEN_TYPE openType,
616 GAMESTATS_OPEN_RESULT* pOpenResult)
619 TRACE("(%p, %s, %d, %p)\n", pStats, debugstr_w(sGameId), openType, pOpenResult);
621 hr = GAMEUX_buildStatisticsFilePath(sGameId, pStats->sStatsFile);
623 hr = GAMEUX_loadStatisticsFromFile(pStats);
624 TRACE("ldstats finished, res: %#x\n", hr);
627 *pOpenResult = GAMESTATS_OPEN_OPENED;
629 else if(hr == S_FALSE && openType == GAMESTATS_OPEN_OPENORCREATE) /* file does not exist */
631 /* create new statitics, not yet connected with file */
632 TRACE("size: %d\n", sizeof(pStats->categories));
633 ZeroMemory(pStats->categories, sizeof(pStats->categories));
634 *pOpenResult = GAMESTATS_OPEN_CREATED;
638 hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
640 TRACE("openResult=%#x ret=%#x\n", *pOpenResult, hr);
643 /*******************************************************************
644 * IGameStatistics implementation
646 typedef struct _GameStatisticsImpl
648 const struct IGameStatisticsVtbl *lpVtbl;
650 struct GAMEUX_STATS stats;
651 } GameStatisticsImpl;
653 static inline GameStatisticsImpl *impl_from_IGameStatistics( IGameStatistics *iface )
655 return (GameStatisticsImpl *)((char*)iface - FIELD_OFFSET(GameStatisticsImpl, lpVtbl));
657 static inline IGameStatistics *IGameStatistics_from_impl( GameStatisticsImpl* This )
659 return (struct IGameStatistics*)&This->lpVtbl;
663 static HRESULT WINAPI GameStatisticsImpl_QueryInterface(
664 IGameStatistics *iface,
668 GameStatisticsImpl *This = impl_from_IGameStatistics( iface );
670 TRACE("%p %s %p\n", This, debugstr_guid( riid ), ppvObject );
674 if ( IsEqualGUID( riid, &IID_IUnknown ) ||
675 IsEqualGUID( riid, &IID_IGameStatistics ) )
681 FIXME("interface %s not implemented\n", debugstr_guid(riid));
682 return E_NOINTERFACE;
685 IGameStatistics_AddRef( iface );
689 static ULONG WINAPI GameStatisticsImpl_AddRef(IGameStatistics *iface)
691 GameStatisticsImpl *This = impl_from_IGameStatistics( iface );
694 ref = InterlockedIncrement(&This->ref);
696 TRACE("(%p): ref=%d\n", This, ref);
700 static ULONG WINAPI GameStatisticsImpl_Release(IGameStatistics *iface)
702 GameStatisticsImpl *This = impl_from_IGameStatistics( iface );
705 ref = InterlockedDecrement( &This->ref );
706 TRACE("(%p): ref=%d\n", This, ref);
710 TRACE("freeing IGameStatistics\n");
711 HeapFree( GetProcessHeap(), 0, This );
717 static HRESULT WINAPI GameStatisticsImpl_GetMaxCategoryLength(
718 IGameStatistics *iface,
721 TRACE("(%p, %p)\n", iface, cch);
725 *cch = MAX_CATEGORY_LENGTH;
729 static HRESULT WINAPI GameStatisticsImpl_GetMaxNameLength(
730 IGameStatistics *iface,
733 TRACE("(%p, %p)\n", iface, cch);
737 *cch = MAX_NAME_LENGTH;
741 static HRESULT WINAPI GameStatisticsImpl_GetMaxValueLength(
742 IGameStatistics *iface,
745 TRACE("(%p, %p)\n", iface, cch);
749 *cch = MAX_VALUE_LENGTH;
753 static HRESULT WINAPI GameStatisticsImpl_GetMaxCategories(
754 IGameStatistics *iface,
757 TRACE("(%p, %p)\n", iface, pMax);
761 *pMax = MAX_CATEGORIES;
765 static HRESULT WINAPI GameStatisticsImpl_GetMaxStatsPerCategory(
766 IGameStatistics *iface,
769 TRACE("(%p, %p)\n", iface, pMax);
773 *pMax = MAX_STATS_PER_CATEGORY;
777 static HRESULT WINAPI GameStatisticsImpl_SetCategoryTitle(
778 IGameStatistics *iface,
784 GameStatisticsImpl *This = impl_from_IGameStatistics(iface);
786 TRACE("(%p, %d, %s)\n", This, categoryIndex, debugstr_w(title));
788 if(!title || categoryIndex >= MAX_CATEGORIES)
791 dwLength = lstrlenW(title);
793 if(dwLength > MAX_CATEGORY_LENGTH)
796 dwLength = MAX_CATEGORY_LENGTH;
799 lstrcpynW(This->stats.categories[categoryIndex].sName,
805 static HRESULT WINAPI GameStatisticsImpl_GetCategoryTitle(
806 IGameStatistics *iface,
812 GameStatisticsImpl *This = impl_from_IGameStatistics(iface);
814 TRACE("%p, %d, %p\n", This, categoryIndex, pTitle);
818 if(!pTitle || categoryIndex >= MAX_CATEGORIES)
824 nLength = lstrlenW(This->stats.categories[categoryIndex].sName);
827 *pTitle = CoTaskMemAlloc(sizeof(WCHAR)*(nLength+1));
828 lstrcpyW(*pTitle, This->stats.categories[categoryIndex].sName);
835 static HRESULT WINAPI GameStatisticsImpl_GetStatistic(
836 IGameStatistics *iface,
844 GameStatisticsImpl *This = impl_from_IGameStatistics(iface);
846 TRACE("%p, %d,%d, %p, %p\n", This, categoryIndex, statIndex, pName, pValue);
848 if(!pName || !pValue)
854 if(categoryIndex >= MAX_CATEGORIES || statIndex >= MAX_STATS_PER_CATEGORY)
859 nLength = lstrlenW(This->stats.categories[categoryIndex].stats[statIndex].sName);
862 *pName = CoTaskMemAlloc(sizeof(WCHAR)*(nLength+1));
866 lstrcpyW(*pName, This->stats.categories[categoryIndex].stats[statIndex].sName);
872 nLength = lstrlenW(This->stats.categories[categoryIndex].stats[statIndex].sValue);
875 *pValue = CoTaskMemAlloc(sizeof(WCHAR)*(nLength+1));
879 lstrcpyW(*pValue, This->stats.categories[categoryIndex].stats[statIndex].sValue);
883 TRACE("returning pair; %s => %s\n", debugstr_w(*pName), debugstr_w(*pValue));
887 static HRESULT WINAPI GameStatisticsImpl_SetStatistic(
888 IGameStatistics *iface,
895 DWORD dwNameLen, dwValueLen;
896 GameStatisticsImpl *This = impl_from_IGameStatistics(iface);
898 TRACE("(%p, %d, %d, %s, %s)\n", This, categoryIndex, statIndex,
899 debugstr_w(name), debugstr_w(value));
904 if(categoryIndex >= MAX_CATEGORIES || statIndex >= MAX_STATS_PER_CATEGORY)
907 dwNameLen = lstrlenW(name);
909 if(dwNameLen > MAX_NAME_LENGTH)
912 dwNameLen = MAX_NAME_LENGTH;
915 lstrcpynW(This->stats.categories[categoryIndex].stats[statIndex].sName,
920 dwValueLen = lstrlenW(value);
922 if(dwValueLen > MAX_VALUE_LENGTH)
925 dwValueLen = MAX_VALUE_LENGTH;
928 lstrcpynW(This->stats.categories[categoryIndex].stats[statIndex].sValue,
929 value, dwValueLen+1);
932 /* Windows allows to pass NULL as value */
933 This->stats.categories[categoryIndex].stats[statIndex].sValue[0] = 0;
938 static HRESULT WINAPI GameStatisticsImpl_Save(
939 IGameStatistics *iface,
942 GameStatisticsImpl *This = impl_from_IGameStatistics(iface);
945 TRACE("(%p, %d)\n", This, trackChanges);
947 if(trackChanges == TRUE)
948 FIXME("tracking changes not yet implemented\n");
950 hr = GAMEUX_updateStatisticsFile(&This->stats);
955 static HRESULT WINAPI GameStatisticsImpl_SetLastPlayedCategory(
956 IGameStatistics *iface,
963 static HRESULT WINAPI GameStatisticsImpl_GetLastPlayedCategory(
964 IGameStatistics *iface,
965 UINT *pCategoryIndex)
971 static const struct IGameStatisticsVtbl GameStatisticsImplVtbl =
973 GameStatisticsImpl_QueryInterface,
974 GameStatisticsImpl_AddRef,
975 GameStatisticsImpl_Release,
976 GameStatisticsImpl_GetMaxCategoryLength,
977 GameStatisticsImpl_GetMaxNameLength,
978 GameStatisticsImpl_GetMaxValueLength,
979 GameStatisticsImpl_GetMaxCategories,
980 GameStatisticsImpl_GetMaxStatsPerCategory,
981 GameStatisticsImpl_SetCategoryTitle,
982 GameStatisticsImpl_GetCategoryTitle,
983 GameStatisticsImpl_GetStatistic,
984 GameStatisticsImpl_SetStatistic,
985 GameStatisticsImpl_Save,
986 GameStatisticsImpl_SetLastPlayedCategory,
987 GameStatisticsImpl_GetLastPlayedCategory
991 HRESULT create_IGameStatistics(GameStatisticsImpl** ppStats)
993 TRACE("(%p)\n", ppStats);
995 *ppStats = HeapAlloc( GetProcessHeap(), 0, sizeof(**ppStats));
997 return E_OUTOFMEMORY;
999 (*ppStats)->lpVtbl = &GameStatisticsImplVtbl;
1000 (*ppStats)->ref = 1;
1002 TRACE("returning coclass: %p\n", *ppStats);
1006 /*******************************************************************************
1007 * IGameStatisticsMgr implementation
1009 typedef struct _GameStatisticsMgrImpl
1011 const struct IGameStatisticsMgrVtbl *lpVtbl;
1013 } GameStatisticsMgrImpl;
1015 static inline GameStatisticsMgrImpl *impl_from_IGameStatisticsMgr( IGameStatisticsMgr *iface )
1017 return (GameStatisticsMgrImpl *)((char*)iface - FIELD_OFFSET(GameStatisticsMgrImpl, lpVtbl));
1021 static HRESULT WINAPI GameStatisticsMgrImpl_QueryInterface(
1022 IGameStatisticsMgr *iface,
1026 GameStatisticsMgrImpl *This = impl_from_IGameStatisticsMgr( iface );
1028 TRACE("%p %s %p\n", This, debugstr_guid( riid ), ppvObject );
1032 if(IsEqualGUID(riid, &IID_IUnknown) ||
1033 IsEqualGUID(riid, &IID_IGameStatisticsMgr) )
1039 FIXME("interface %s not implemented\n", debugstr_guid(riid));
1040 return E_NOINTERFACE;
1043 IGameStatisticsMgr_AddRef( iface );
1047 static ULONG WINAPI GameStatisticsMgrImpl_AddRef(IGameStatisticsMgr *iface)
1049 GameStatisticsMgrImpl *This = impl_from_IGameStatisticsMgr( iface );
1052 ref = InterlockedIncrement(&This->ref);
1054 TRACE("(%p): ref=%d\n", This, ref);
1058 static ULONG WINAPI GameStatisticsMgrImpl_Release(IGameStatisticsMgr *iface)
1060 GameStatisticsMgrImpl *This = impl_from_IGameStatisticsMgr( iface );
1063 ref = InterlockedDecrement(&This->ref);
1064 TRACE("(%p): ref=%d\n", This, ref);
1068 TRACE("freeing GameStatistics object\n");
1069 HeapFree( GetProcessHeap(), 0, This);
1075 static HRESULT STDMETHODCALLTYPE GameStatisticsMgrImpl_GetGameStatistics(
1076 IGameStatisticsMgr* iface,
1077 LPCWSTR GDFBinaryPath,
1078 GAMESTATS_OPEN_TYPE openType,
1079 GAMESTATS_OPEN_RESULT *pOpenResult,
1080 IGameStatistics **ppiStats)
1083 WCHAR lpApplicationId[49];
1084 GameStatisticsImpl *statisticsImpl;
1086 TRACE("(%p, %s, 0x%x, %p, %p)\n", iface, debugstr_w(GDFBinaryPath), openType, pOpenResult, ppiStats);
1088 hr = GAMEUX_getAppIdFromGDFPath(GDFBinaryPath, lpApplicationId);
1091 hr = create_IGameStatistics(&statisticsImpl);
1095 *ppiStats = IGameStatistics_from_impl(statisticsImpl);
1096 hr = GAMEUX_buildStatisticsFilePath(lpApplicationId, statisticsImpl->stats.sStatsFile);
1100 hr = GAMEUX_loadGameStatistics(&statisticsImpl->stats, lpApplicationId, openType, pOpenResult);
1105 static HRESULT STDMETHODCALLTYPE GameStatisticsMgrImpl_RemoveGameStatistics(
1106 IGameStatisticsMgr* iface,
1107 LPCWSTR GDFBinaryPath)
1110 WCHAR lpApplicationId[49];
1111 WCHAR sStatsFile[MAX_PATH];
1113 TRACE("(%p, %s)\n", iface, debugstr_w(GDFBinaryPath));
1115 hr = GAMEUX_getAppIdFromGDFPath(GDFBinaryPath, lpApplicationId);
1118 hr = GAMEUX_buildStatisticsFilePath(lpApplicationId, sStatsFile);
1121 hr = (DeleteFileW(sStatsFile)==TRUE ? S_OK : HRESULT_FROM_WIN32(GetLastError()));
1126 static const struct IGameStatisticsMgrVtbl GameStatisticsMgrImplVtbl =
1128 GameStatisticsMgrImpl_QueryInterface,
1129 GameStatisticsMgrImpl_AddRef,
1130 GameStatisticsMgrImpl_Release,
1131 GameStatisticsMgrImpl_GetGameStatistics,
1132 GameStatisticsMgrImpl_RemoveGameStatistics,
1135 HRESULT GameStatistics_create(
1136 IUnknown *pUnkOuter,
1139 GameStatisticsMgrImpl *pGameStatistics;
1141 TRACE("(%p, %p)\n", pUnkOuter, ppObj);
1143 pGameStatistics = HeapAlloc( GetProcessHeap(), 0, sizeof (*pGameStatistics) );
1145 if( !pGameStatistics )
1146 return E_OUTOFMEMORY;
1148 pGameStatistics->lpVtbl = &GameStatisticsMgrImplVtbl;
1149 pGameStatistics->ref = 1;
1151 *ppObj = (IUnknown*)(&pGameStatistics->lpVtbl);
1153 TRACE("returning iface %p\n", *ppObj);