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
);
179 for(i
=0; i
<MAX_CATEGORIES
; ++i
)
181 if(lstrlenW(stats
->categories
[i
].sName
)==0)
184 V_VT(&vValue
) = VT_INT
;
185 V_INT(&vValue
) = NODE_ELEMENT
;
187 hr
= IXMLDOMDocument_createNode(document
, vValue
, bstrCategory
, NULL
, &categoryNode
);
190 hr
= IXMLDOMNode_QueryInterface(categoryNode
, &IID_IXMLDOMElement
, (LPVOID
*)&categoryElement
);
194 hr
= IXMLDOMElement_setAttribute(categoryElement
, bstrIndex
, vValue
);
198 V_VT(&vValue
) = VT_BSTR
;
199 V_BSTR(&vValue
) = SysAllocString(stats
->categories
[i
].sName
);
206 TRACE("storing category %d: %s\n", i
, debugstr_w(V_BSTR(&vValue
)));
207 hr
= IXMLDOMElement_setAttribute(categoryElement
, bstrName
, vValue
);
210 SysFreeString(V_BSTR(&vValue
));
214 for(j
=0; j
<MAX_STATS_PER_CATEGORY
; ++j
)
216 if(lstrlenW(stats
->categories
[i
].stats
[j
].sName
)==0)
219 V_VT(&vValue
) = VT_INT
;
220 V_INT(&vValue
) = NODE_ELEMENT
;
222 hr
= IXMLDOMDocument_createNode(document
, vValue
, bstrStatistic
, NULL
, &statisticsNode
);
225 hr
= IXMLDOMNode_QueryInterface(statisticsNode
, &IID_IXMLDOMElement
, (LPVOID
*)&statisticsElement
);
229 hr
= IXMLDOMElement_setAttribute(statisticsElement
, bstrIndex
, vValue
);
233 V_VT(&vValue
) = VT_BSTR
;
234 V_BSTR(&vValue
) = SysAllocString(stats
->categories
[i
].stats
[j
].sName
);
241 TRACE(" storing statistic %d: name: %s\n", j
, debugstr_w(V_BSTR(&vValue
)));
242 hr
= IXMLDOMElement_setAttribute(statisticsElement
, bstrName
, vValue
);
245 SysFreeString(V_BSTR(&vValue
));
249 V_VT(&vValue
) = VT_BSTR
;
250 V_BSTR(&vValue
) = SysAllocString(stats
->categories
[i
].stats
[j
].sValue
);
257 TRACE(" storing statistic %d: name: %s\n", j
, debugstr_w(V_BSTR(&vValue
)));
258 hr
= IXMLDOMElement_setAttribute(statisticsElement
, bstrValue
, vValue
);
261 SysFreeString(V_BSTR(&vValue
));
264 hr
= IXMLDOMElement_appendChild(categoryNode
, statisticsNode
, &statisticsNode
);
266 IXMLDOMElement_Release(statisticsElement
);
267 IXMLDOMNode_Release(statisticsNode
);
272 hr
= IXMLDOMElement_appendChild(root
, categoryNode
, &categoryNode
);
274 IXMLDOMElement_Release(categoryElement
);
275 IXMLDOMNode_Release(categoryNode
);
282 hr
= IXMLDOMDocument_putref_documentElement(document
, root
);
284 IXMLDOMElement_Release(root
);
286 TRACE("saving game statistics in %s file\n", debugstr_w(stats
->sStatsFile
));
288 hr
= GAMEUX_createStatsDirectory(stats
->sStatsFile
);
291 hr
= IXMLDOMDocument_save(document
, vStatsFilePath
);
293 IXMLDOMDocument_Release(document
);
295 SysFreeString(bstrValue
);
296 SysFreeString(bstrName
);
297 SysFreeString(bstrStatistic
);
298 SysFreeString(bstrIndex
);
299 SysFreeString(bstrCategory
);
300 SysFreeString(bstrStatistics
);
301 SysFreeString(V_BSTR(&vStatsFilePath
));
302 TRACE("ret=0x%x\n", hr
);
305 /*******************************************************************************
306 * GAMEUX_buildStatisticsFilePath
307 * Creates path to file containing statistics of game with given id.
310 * lpApplicationId [I] application id of game,
312 * lpStatisticsFile [O] array where path will be
313 * stored. Its size must be
316 static HRESULT
GAMEUX_buildStatisticsFilePath(
317 LPCWSTR lpApplicationId
,
318 LPWSTR lpStatisticsFile
)
320 static const WCHAR sBackslash
[] = {'\\',0};
321 static const WCHAR sStatisticsDir
[] = {'\\','M','i','c','r','o','s','o','f','t',
322 '\\','W','i','n','d','o','w','s','\\','G','a','m','e','E','x','p',
323 'l','o','r','e','r','\\','G','a','m','e','S','t','a','t','i','s',
324 't','i','c','s','\\',0};
325 static const WCHAR sDotGamestats
[] = {'.','g','a','m','e','s','t','a','t','s',0};
329 hr
= SHGetFolderPathW(NULL
, CSIDL_LOCAL_APPDATA
, NULL
, SHGFP_TYPE_CURRENT
, lpStatisticsFile
);
333 lstrcatW(lpStatisticsFile
, sStatisticsDir
);
334 lstrcatW(lpStatisticsFile
, lpApplicationId
);
335 lstrcatW(lpStatisticsFile
, sBackslash
);
336 lstrcatW(lpStatisticsFile
, lpApplicationId
);
337 lstrcatW(lpStatisticsFile
, sDotGamestats
);
342 /*******************************************************************************
343 * GAMEUX_getAppIdFromGDFPath
345 * Loads application identifier associated with given GDF binary.
346 * Routine reads identifier from registry, so will fail if game
350 * GDFBinaryPath [I] path to gdf binary
351 * lpApplicationId [O] place to store application id.
352 * must be at least 49 characters
353 * to store guid and termination 0
355 static HRESULT
GAMEUX_getAppIdFromGDFPath(
356 LPCWSTR GDFBinaryPath
,
357 LPWSTR lpApplicationId
)
359 static const WCHAR sApplicationId
[] =
360 {'A','p','p','l','i','c','a','t','i','o','n','I','d',0};
363 GAME_INSTALL_SCOPE installScope
;
365 LPWSTR lpRegistryPath
= NULL
;
367 DWORD dwLength
= 49*sizeof(WCHAR
);/* place for GUID */
369 TRACE("(%s, %p)\n", debugstr_w(GDFBinaryPath
), lpApplicationId
);
374 installScope
= GIS_CURRENT_USER
;
375 hr
= GAMEUX_FindGameInstanceId(GDFBinaryPath
, installScope
, &instanceId
);
379 installScope
= GIS_ALL_USERS
;
380 hr
= GAMEUX_FindGameInstanceId(GDFBinaryPath
, installScope
, &instanceId
);
384 /* game not registered, so statistics cannot be used */
388 /* game is registered, let's read it's application id from registry */
389 hr
= GAMEUX_buildGameRegistryPath(installScope
, &instanceId
, &lpRegistryPath
);
392 hr
= HRESULT_FROM_WIN32(RegOpenKeyExW(HKEY_LOCAL_MACHINE
,
393 lpRegistryPath
, 0, KEY_READ
| KEY_WOW64_64KEY
, &hKey
));
395 hr
= HRESULT_FROM_WIN32(RegGetValueW(hKey
,
396 NULL
, sApplicationId
, RRF_RT_REG_SZ
,
397 NULL
, lpApplicationId
, &dwLength
));
402 HeapFree(GetProcessHeap(), 0, lpRegistryPath
);
404 TRACE("found app id: %s, return: %#x\n", debugstr_w(lpApplicationId
), hr
);
407 /*******************************************************************
408 * GAMEUX_loadGameStatisticsFromFile
409 * Helper function, loads game statistics from file and stores them
413 * data [I/O] structure containing file name to
414 * load and data fields to store data in
416 static HRESULT
GAMEUX_loadStatisticsFromFile(struct GAMEUX_STATS
*data
)
418 static const WCHAR sStatistics
[] = {'S','t','a','t','i','s','t','i','c','s',0};
419 static const WCHAR sCategory
[] = {'C','a','t','e','g','o','r','y',0};
420 static const WCHAR sIndex
[] = {'I','n','d','e','x',0};
421 static const WCHAR sStatistic
[] = {'S','t','a','t','i','s','t','i','c',0};
422 static const WCHAR sName
[] = {'N','a','m','e',0};
423 static const WCHAR sValue
[] = {'V','a','l','u','e',0};
426 IXMLDOMDocument
*document
= NULL
;
427 IXMLDOMElement
*root
= NULL
, *categoryElement
, *statisticElement
;
428 IXMLDOMNode
*categoryNode
, *statisticNode
;
429 IXMLDOMNodeList
*rootChildren
= NULL
, *categoryChildren
;
430 VARIANT vStatsFilePath
, vValue
;
431 BSTR bstrStatistics
= NULL
, bstrCategory
= NULL
, bstrIndex
= NULL
,
432 bstrStatistic
= NULL
, bstrName
= NULL
, bstrValue
= NULL
;
433 VARIANT_BOOL isSuccessful
= VARIANT_FALSE
;
436 TRACE("(%p)\n", data
);
438 V_VT(&vStatsFilePath
) = VT_BSTR
;
439 V_BSTR(&vStatsFilePath
) = SysAllocString(data
->sStatsFile
);
440 if(!V_BSTR(&vStatsFilePath
))
444 hr
= CoCreateInstance(&CLSID_DOMDocument30
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IXMLDOMDocument
, (void**)&document
);
448 bstrStatistics
= SysAllocString(sStatistics
);
455 bstrCategory
= SysAllocString(sCategory
);
462 bstrIndex
= SysAllocString(sIndex
);
469 bstrStatistic
= SysAllocString(sStatistic
);
476 bstrName
= SysAllocString(sName
);
483 bstrValue
= SysAllocString(sValue
);
489 hr
= IXMLDOMDocument_load(document
, vStatsFilePath
, &isSuccessful
);
491 if(hr
== S_OK
&& isSuccessful
!= VARIANT_TRUE
)
495 hr
= IXMLDOMDocument_get_documentElement(document
, &root
);
498 hr
= IXMLDOMElement_get_childNodes(root
, &rootChildren
);
505 hr
= IXMLDOMNodeList_nextNode(rootChildren
, &categoryNode
);
509 hr
= IXMLDOMNode_QueryInterface(categoryNode
, &IID_IXMLDOMElement
, (LPVOID
*)&categoryElement
);
513 hr
= IXMLDOMElement_getAttribute(categoryElement
, bstrIndex
, &vValue
);
514 if( hr
== S_OK
&& V_VT(&vValue
) != VT_BSTR
)
519 i
= StrToIntW(V_BSTR(&vValue
));
520 hr
= IXMLDOMElement_getAttribute(categoryElement
, bstrName
, &vValue
);
521 if( hr
== S_OK
&& V_VT(&vValue
) != VT_BSTR
)
527 lstrcpynW(data
->categories
[i
].sName
, V_BSTR(&vValue
), MAX_CATEGORY_LENGTH
);
528 TRACE("category %d name %s\n", i
, debugstr_w(data
->categories
[i
].sName
));
529 hr
= IXMLDOMElement_get_childNodes(categoryElement
, &categoryChildren
);
537 hr
= IXMLDOMNodeList_nextNode(categoryChildren
, &statisticNode
);
541 hr
= IXMLDOMNode_QueryInterface(statisticNode
, &IID_IXMLDOMElement
, (LPVOID
*)&statisticElement
);
545 hr
= IXMLDOMElement_getAttribute(statisticElement
, bstrIndex
, &vValue
);
546 if( hr
== S_OK
&& V_VT(&vValue
) != VT_BSTR
)
551 j
= StrToIntW(V_BSTR(&vValue
));
552 hr
= IXMLDOMElement_getAttribute(statisticElement
, bstrName
, &vValue
);
553 if( hr
== S_OK
&& V_VT(&vValue
) != VT_BSTR
)
559 lstrcpynW(data
->categories
[i
].stats
[j
].sName
, V_BSTR(&vValue
), MAX_NAME_LENGTH
);
560 hr
= IXMLDOMElement_getAttribute(statisticElement
, bstrValue
, &vValue
);
561 if( hr
== S_OK
&& V_VT(&vValue
) != VT_BSTR
)
567 lstrcpynW(data
->categories
[i
].stats
[j
].sValue
, V_BSTR(&vValue
), MAX_VALUE_LENGTH
);
568 TRACE("statistic %d name %s value %s\n", j
,
569 debugstr_w(data
->categories
[i
].stats
[j
].sName
),
570 debugstr_w(data
->categories
[i
].stats
[j
].sValue
));
572 IXMLDOMElement_Release(statisticElement
);
575 IXMLDOMNode_Release(statisticNode
);
582 IXMLDOMElement_Release(categoryElement
);
585 IXMLDOMNode_Release(categoryNode
);
592 if(rootChildren
) IXMLDOMNodeList_Release(rootChildren
);
593 if(root
) IXMLDOMElement_Release(root
);
594 if(document
) IXMLDOMDocument_Release(document
);
596 SysFreeString(bstrValue
);
597 SysFreeString(bstrName
);
598 SysFreeString(bstrStatistic
);
599 SysFreeString(bstrIndex
);
600 SysFreeString(bstrCategory
);
601 SysFreeString(bstrStatistics
);
602 SysFreeString(V_BSTR(&vStatsFilePath
));
605 /*******************************************************************
606 * GAMEUX_loadGameStatistics
608 * Helper function which loads game statistics associated with game
609 * into interface's internal structures
612 * pStats [O] structure which will receive data
613 * sGameId [I] application instance Id, stored as string
614 * to avoid additional conversions
615 * openType [I] allowed ways of opening statistics
616 * pOpenResult [O] way used to open statistics
619 static HRESULT
GAMEUX_loadGameStatistics(struct GAMEUX_STATS
*pStats
,
621 GAMESTATS_OPEN_TYPE openType
,
622 GAMESTATS_OPEN_RESULT
* pOpenResult
)
625 TRACE("(%p, %s, %d, %p)\n", pStats
, debugstr_w(sGameId
), openType
, pOpenResult
);
627 hr
= GAMEUX_buildStatisticsFilePath(sGameId
, pStats
->sStatsFile
);
629 hr
= GAMEUX_loadStatisticsFromFile(pStats
);
630 TRACE("ldstats finished, res: %#x\n", hr
);
633 *pOpenResult
= GAMESTATS_OPEN_OPENED
;
635 else if(hr
== S_FALSE
&& openType
== GAMESTATS_OPEN_OPENORCREATE
) /* file does not exist */
637 /* create new statistics, not yet connected with file */
638 ZeroMemory(pStats
->categories
, sizeof(pStats
->categories
));
639 *pOpenResult
= GAMESTATS_OPEN_CREATED
;
643 hr
= HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND
);
645 TRACE("openResult=%#x ret=%#x\n", *pOpenResult
, hr
);
648 /*******************************************************************
649 * IGameStatistics implementation
651 typedef struct _GameStatisticsImpl
653 IGameStatistics IGameStatistics_iface
;
655 struct GAMEUX_STATS stats
;
656 } GameStatisticsImpl
;
658 static inline GameStatisticsImpl
*impl_from_IGameStatistics( IGameStatistics
*iface
)
660 return CONTAINING_RECORD(iface
, GameStatisticsImpl
, IGameStatistics_iface
);
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
);
820 if (categoryIndex
>= MAX_CATEGORIES
)
825 nLength
= lstrlenW(This
->stats
.categories
[categoryIndex
].sName
);
828 *pTitle
= CoTaskMemAlloc(sizeof(WCHAR
)*(nLength
+1));
829 lstrcpyW(*pTitle
, This
->stats
.categories
[categoryIndex
].sName
);
836 static HRESULT WINAPI
GameStatisticsImpl_GetStatistic(
837 IGameStatistics
*iface
,
845 GameStatisticsImpl
*This
= impl_from_IGameStatistics(iface
);
847 TRACE("%p, %d,%d, %p, %p\n", This
, categoryIndex
, statIndex
, pName
, pValue
);
849 if(!pName
|| !pValue
)
855 if(categoryIndex
>= MAX_CATEGORIES
|| statIndex
>= MAX_STATS_PER_CATEGORY
)
860 nLength
= lstrlenW(This
->stats
.categories
[categoryIndex
].stats
[statIndex
].sName
);
863 *pName
= CoTaskMemAlloc(sizeof(WCHAR
)*(nLength
+1));
867 lstrcpyW(*pName
, This
->stats
.categories
[categoryIndex
].stats
[statIndex
].sName
);
873 nLength
= lstrlenW(This
->stats
.categories
[categoryIndex
].stats
[statIndex
].sValue
);
876 *pValue
= CoTaskMemAlloc(sizeof(WCHAR
)*(nLength
+1));
880 lstrcpyW(*pValue
, This
->stats
.categories
[categoryIndex
].stats
[statIndex
].sValue
);
884 TRACE("returning pair; %s => %s\n", debugstr_w(*pName
), debugstr_w(*pValue
));
888 static HRESULT WINAPI
GameStatisticsImpl_SetStatistic(
889 IGameStatistics
*iface
,
896 DWORD dwNameLen
, dwValueLen
;
897 GameStatisticsImpl
*This
= impl_from_IGameStatistics(iface
);
899 TRACE("(%p, %d, %d, %s, %s)\n", This
, categoryIndex
, statIndex
,
900 debugstr_w(name
), debugstr_w(value
));
905 if(categoryIndex
>= MAX_CATEGORIES
|| statIndex
>= MAX_STATS_PER_CATEGORY
)
908 dwNameLen
= lstrlenW(name
);
910 if(dwNameLen
> MAX_NAME_LENGTH
)
913 dwNameLen
= MAX_NAME_LENGTH
;
916 lstrcpynW(This
->stats
.categories
[categoryIndex
].stats
[statIndex
].sName
,
921 dwValueLen
= lstrlenW(value
);
923 if(dwValueLen
> MAX_VALUE_LENGTH
)
926 dwValueLen
= MAX_VALUE_LENGTH
;
929 lstrcpynW(This
->stats
.categories
[categoryIndex
].stats
[statIndex
].sValue
,
930 value
, dwValueLen
+1);
933 /* Windows allows passing NULL as value */
934 This
->stats
.categories
[categoryIndex
].stats
[statIndex
].sValue
[0] = 0;
939 static HRESULT WINAPI
GameStatisticsImpl_Save(
940 IGameStatistics
*iface
,
943 GameStatisticsImpl
*This
= impl_from_IGameStatistics(iface
);
946 TRACE("(%p, %d)\n", This
, trackChanges
);
949 FIXME("tracking changes not yet implemented\n");
951 hr
= GAMEUX_updateStatisticsFile(&This
->stats
);
956 static HRESULT WINAPI
GameStatisticsImpl_SetLastPlayedCategory(
957 IGameStatistics
*iface
,
964 static HRESULT WINAPI
GameStatisticsImpl_GetLastPlayedCategory(
965 IGameStatistics
*iface
,
966 UINT
*pCategoryIndex
)
972 static const struct IGameStatisticsVtbl GameStatisticsImplVtbl
=
974 GameStatisticsImpl_QueryInterface
,
975 GameStatisticsImpl_AddRef
,
976 GameStatisticsImpl_Release
,
977 GameStatisticsImpl_GetMaxCategoryLength
,
978 GameStatisticsImpl_GetMaxNameLength
,
979 GameStatisticsImpl_GetMaxValueLength
,
980 GameStatisticsImpl_GetMaxCategories
,
981 GameStatisticsImpl_GetMaxStatsPerCategory
,
982 GameStatisticsImpl_SetCategoryTitle
,
983 GameStatisticsImpl_GetCategoryTitle
,
984 GameStatisticsImpl_GetStatistic
,
985 GameStatisticsImpl_SetStatistic
,
986 GameStatisticsImpl_Save
,
987 GameStatisticsImpl_SetLastPlayedCategory
,
988 GameStatisticsImpl_GetLastPlayedCategory
992 static HRESULT
create_IGameStatistics(GameStatisticsImpl
** ppStats
)
994 TRACE("(%p)\n", ppStats
);
996 *ppStats
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(**ppStats
));
998 return E_OUTOFMEMORY
;
1000 (*ppStats
)->IGameStatistics_iface
.lpVtbl
= &GameStatisticsImplVtbl
;
1001 (*ppStats
)->ref
= 1;
1003 TRACE("returning coclass: %p\n", *ppStats
);
1007 /*******************************************************************************
1008 * IGameStatisticsMgr implementation
1010 typedef struct _GameStatisticsMgrImpl
1012 IGameStatisticsMgr IGameStatisticsMgr_iface
;
1014 } GameStatisticsMgrImpl
;
1016 static inline GameStatisticsMgrImpl
*impl_from_IGameStatisticsMgr( IGameStatisticsMgr
*iface
)
1018 return CONTAINING_RECORD(iface
, GameStatisticsMgrImpl
, IGameStatisticsMgr_iface
);
1022 static HRESULT WINAPI
GameStatisticsMgrImpl_QueryInterface(
1023 IGameStatisticsMgr
*iface
,
1027 GameStatisticsMgrImpl
*This
= impl_from_IGameStatisticsMgr( iface
);
1029 TRACE("%p %s %p\n", This
, debugstr_guid( riid
), ppvObject
);
1033 if(IsEqualGUID(riid
, &IID_IUnknown
) ||
1034 IsEqualGUID(riid
, &IID_IGameStatisticsMgr
) )
1040 FIXME("interface %s not implemented\n", debugstr_guid(riid
));
1041 return E_NOINTERFACE
;
1044 IGameStatisticsMgr_AddRef( iface
);
1048 static ULONG WINAPI
GameStatisticsMgrImpl_AddRef(IGameStatisticsMgr
*iface
)
1050 GameStatisticsMgrImpl
*This
= impl_from_IGameStatisticsMgr( iface
);
1053 ref
= InterlockedIncrement(&This
->ref
);
1055 TRACE("(%p): ref=%d\n", This
, ref
);
1059 static ULONG WINAPI
GameStatisticsMgrImpl_Release(IGameStatisticsMgr
*iface
)
1061 GameStatisticsMgrImpl
*This
= impl_from_IGameStatisticsMgr( iface
);
1064 ref
= InterlockedDecrement(&This
->ref
);
1065 TRACE("(%p): ref=%d\n", This
, ref
);
1069 TRACE("freeing GameStatistics object\n");
1070 HeapFree( GetProcessHeap(), 0, This
);
1076 static HRESULT STDMETHODCALLTYPE
GameStatisticsMgrImpl_GetGameStatistics(
1077 IGameStatisticsMgr
* iface
,
1078 LPCWSTR GDFBinaryPath
,
1079 GAMESTATS_OPEN_TYPE openType
,
1080 GAMESTATS_OPEN_RESULT
*pOpenResult
,
1081 IGameStatistics
**ppiStats
)
1084 WCHAR lpApplicationId
[49];
1085 GameStatisticsImpl
*statisticsImpl
= NULL
;
1086 IGameStatistics
*output_iface
;
1088 TRACE("(%p, %s, 0x%x, %p, %p)\n", iface
, debugstr_w(GDFBinaryPath
), openType
, pOpenResult
, ppiStats
);
1090 hr
= GAMEUX_getAppIdFromGDFPath(GDFBinaryPath
, lpApplicationId
);
1093 hr
= create_IGameStatistics(&statisticsImpl
);
1097 output_iface
= &statisticsImpl
->IGameStatistics_iface
;
1098 hr
= GAMEUX_buildStatisticsFilePath(lpApplicationId
, statisticsImpl
->stats
.sStatsFile
);
1102 hr
= GAMEUX_loadGameStatistics(&statisticsImpl
->stats
, lpApplicationId
, openType
, pOpenResult
);
1105 *ppiStats
= output_iface
;
1108 HeapFree(GetProcessHeap(), 0, statisticsImpl
);
1115 static HRESULT STDMETHODCALLTYPE
GameStatisticsMgrImpl_RemoveGameStatistics(
1116 IGameStatisticsMgr
* iface
,
1117 LPCWSTR GDFBinaryPath
)
1120 WCHAR lpApplicationId
[49];
1121 WCHAR sStatsFile
[MAX_PATH
];
1123 TRACE("(%p, %s)\n", iface
, debugstr_w(GDFBinaryPath
));
1125 hr
= GAMEUX_getAppIdFromGDFPath(GDFBinaryPath
, lpApplicationId
);
1128 hr
= GAMEUX_buildStatisticsFilePath(lpApplicationId
, sStatsFile
);
1131 hr
= (DeleteFileW(sStatsFile
)==TRUE
? S_OK
: HRESULT_FROM_WIN32(GetLastError()));
1136 static const struct IGameStatisticsMgrVtbl GameStatisticsMgrImplVtbl
=
1138 GameStatisticsMgrImpl_QueryInterface
,
1139 GameStatisticsMgrImpl_AddRef
,
1140 GameStatisticsMgrImpl_Release
,
1141 GameStatisticsMgrImpl_GetGameStatistics
,
1142 GameStatisticsMgrImpl_RemoveGameStatistics
,
1145 HRESULT
GameStatistics_create(
1146 IUnknown
*pUnkOuter
,
1149 GameStatisticsMgrImpl
*pGameStatistics
;
1151 TRACE("(%p, %p)\n", pUnkOuter
, ppObj
);
1153 pGameStatistics
= HeapAlloc( GetProcessHeap(), 0, sizeof (*pGameStatistics
) );
1155 if( !pGameStatistics
)
1156 return E_OUTOFMEMORY
;
1158 pGameStatistics
->IGameStatisticsMgr_iface
.lpVtbl
= &GameStatisticsMgrImplVtbl
;
1159 pGameStatistics
->ref
= 1;
1161 *ppObj
= (IUnknown
*)&pGameStatistics
->IGameStatisticsMgr_iface
;
1163 TRACE("returning iface %p\n", *ppObj
);