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
30 #include "gameux_private.h"
32 #include "wine/debug.h"
34 WINE_DEFAULT_DEBUG_CHANNEL(gameux
);
37 * constant definitions
39 #define MAX_CATEGORY_LENGTH 60
40 #define MAX_NAME_LENGTH 30
41 #define MAX_VALUE_LENGTH 30
42 #define MAX_CATEGORIES 10
43 #define MAX_STATS_PER_CATEGORY 10
44 /*******************************************************************************
45 * Game statistics helper components
47 /*******************************************************************************
50 * set of structures for containing game's data
52 struct GAMEUX_STATS_STAT
54 WCHAR sName
[MAX_NAME_LENGTH
+1];
55 WCHAR sValue
[MAX_VALUE_LENGTH
+1];
57 struct GAMEUX_STATS_CATEGORY
59 WCHAR sName
[MAX_CATEGORY_LENGTH
+1];
60 struct GAMEUX_STATS_STAT stats
[MAX_STATS_PER_CATEGORY
];
64 WCHAR sStatsFile
[MAX_PATH
];
65 struct GAMEUX_STATS_CATEGORY categories
[MAX_CATEGORIES
];
67 /*******************************************************************************
68 * GAMEUX_createStatsDirectory
70 * Helper function, creates directory to store game statistics
73 * path [I] path to game statistics file.
74 * base directory of this file will
75 * be created if it doesn't exists
77 static HRESULT
GAMEUX_createStatsDirectory(LPCWSTR lpFilePath
)
80 WCHAR lpDirectoryPath
[MAX_PATH
];
83 lpEnd
= StrRChrW(lpFilePath
, NULL
, '\\');
84 lstrcpynW(lpDirectoryPath
, lpFilePath
, lpEnd
-lpFilePath
+1);
86 hr
= HRESULT_FROM_WIN32(SHCreateDirectoryExW(NULL
, lpDirectoryPath
, NULL
));
88 if(hr
== HRESULT_FROM_WIN32(ERROR_FILE_EXISTS
) ||
89 hr
== HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS
))
94 /*******************************************************************
95 * GAMEUX_updateStatisticsFile
97 * Helper function updating data stored in statistics file
100 * data [I] pointer to struct containing
103 static HRESULT
GAMEUX_updateStatisticsFile(struct GAMEUX_STATS
*stats
)
106 IXMLDOMDocument
*document
= NULL
;
107 IXMLDOMElement
*root
= NULL
, *statisticsElement
= NULL
;
108 IXMLDOMNode
*categoryNode
= NULL
, *statisticsNode
= NULL
;
109 VARIANT vStatsFilePath
, vValue
;
110 BSTR bstrStatistics
= NULL
, bstrCategory
= NULL
, bstrIndex
= NULL
,
111 bstrStatistic
= NULL
, bstrName
= NULL
, bstrValue
= NULL
;
114 TRACE("(%p)\n", stats
);
116 V_VT(&vStatsFilePath
) = VT_BSTR
;
117 V_BSTR(&vStatsFilePath
) = SysAllocString(stats
->sStatsFile
);
118 if(!V_BSTR(&vStatsFilePath
))
122 hr
= CoCreateInstance(&CLSID_DOMDocument
, NULL
, CLSCTX_INPROC_SERVER
,
123 &IID_IXMLDOMDocument
, (void**)&document
);
127 bstrStatistics
= SysAllocString(L
"Statistics");
133 hr
= IXMLDOMDocument_createElement(document
, bstrStatistics
, &root
);
137 bstrCategory
= SysAllocString(L
"Category");
144 bstrIndex
= SysAllocString(L
"Index");
151 bstrStatistic
= SysAllocString(L
"Statistic");
158 bstrName
= SysAllocString(L
"Name");
165 bstrValue
= SysAllocString(L
"Value");
171 for(i
=0; i
<MAX_CATEGORIES
; ++i
)
173 IXMLDOMElement
*categoryElement
= NULL
;
175 if(!stats
->categories
[i
].sName
[0])
178 V_VT(&vValue
) = VT_INT
;
179 V_INT(&vValue
) = NODE_ELEMENT
;
181 hr
= IXMLDOMDocument_createNode(document
, vValue
, bstrCategory
, NULL
, &categoryNode
);
184 hr
= IXMLDOMNode_QueryInterface(categoryNode
, &IID_IXMLDOMElement
, (void**)&categoryElement
);
188 hr
= IXMLDOMElement_setAttribute(categoryElement
, bstrIndex
, vValue
);
192 V_VT(&vValue
) = VT_BSTR
;
193 V_BSTR(&vValue
) = SysAllocString(stats
->categories
[i
].sName
);
200 TRACE("storing category %d: %s\n", i
, debugstr_w(V_BSTR(&vValue
)));
201 hr
= IXMLDOMElement_setAttribute(categoryElement
, bstrName
, vValue
);
204 if (categoryElement
) IXMLDOMElement_Release(categoryElement
);
206 SysFreeString(V_BSTR(&vValue
));
210 for(j
=0; j
<MAX_STATS_PER_CATEGORY
; ++j
)
212 if(!stats
->categories
[i
].stats
[j
].sName
[0])
215 V_VT(&vValue
) = VT_INT
;
216 V_INT(&vValue
) = NODE_ELEMENT
;
218 hr
= IXMLDOMDocument_createNode(document
, vValue
, bstrStatistic
, NULL
, &statisticsNode
);
221 hr
= IXMLDOMNode_QueryInterface(statisticsNode
, &IID_IXMLDOMElement
, (LPVOID
*)&statisticsElement
);
225 hr
= IXMLDOMElement_setAttribute(statisticsElement
, bstrIndex
, vValue
);
229 V_VT(&vValue
) = VT_BSTR
;
230 V_BSTR(&vValue
) = SysAllocString(stats
->categories
[i
].stats
[j
].sName
);
237 TRACE(" storing statistic %d: name: %s\n", j
, debugstr_w(V_BSTR(&vValue
)));
238 hr
= IXMLDOMElement_setAttribute(statisticsElement
, bstrName
, vValue
);
239 SysFreeString(V_BSTR(&vValue
));
244 V_VT(&vValue
) = VT_BSTR
;
245 V_BSTR(&vValue
) = SysAllocString(stats
->categories
[i
].stats
[j
].sValue
);
252 TRACE(" storing statistic %d: name: %s\n", j
, debugstr_w(V_BSTR(&vValue
)));
253 hr
= IXMLDOMElement_setAttribute(statisticsElement
, bstrValue
, vValue
);
254 SysFreeString(V_BSTR(&vValue
));
258 hr
= IXMLDOMNode_appendChild(categoryNode
, statisticsNode
, NULL
);
260 if (statisticsElement
) IXMLDOMElement_Release(statisticsElement
);
261 if (statisticsNode
) IXMLDOMNode_Release(statisticsNode
);
266 hr
= IXMLDOMElement_appendChild(root
, categoryNode
, &categoryNode
);
269 IXMLDOMNode_Release(categoryNode
);
276 hr
= IXMLDOMDocument_putref_documentElement(document
, root
);
278 if (root
) IXMLDOMElement_Release(root
);
280 TRACE("saving game statistics in %s file\n", debugstr_w(stats
->sStatsFile
));
282 hr
= GAMEUX_createStatsDirectory(stats
->sStatsFile
);
285 hr
= IXMLDOMDocument_save(document
, vStatsFilePath
);
287 if (document
) IXMLDOMDocument_Release(document
);
289 SysFreeString(bstrValue
);
290 SysFreeString(bstrName
);
291 SysFreeString(bstrStatistic
);
292 SysFreeString(bstrIndex
);
293 SysFreeString(bstrCategory
);
294 SysFreeString(bstrStatistics
);
295 SysFreeString(V_BSTR(&vStatsFilePath
));
296 TRACE("ret=0x%lx\n", hr
);
299 /*******************************************************************************
300 * GAMEUX_buildStatisticsFilePath
301 * Creates path to file containing statistics of game with given id.
304 * lpApplicationId [I] application id of game,
306 * lpStatisticsFile [O] array where path will be
307 * stored. Its size must be
310 static HRESULT
GAMEUX_buildStatisticsFilePath(
311 LPCWSTR lpApplicationId
,
312 LPWSTR lpStatisticsFile
)
316 hr
= SHGetFolderPathW(NULL
, CSIDL_LOCAL_APPDATA
, NULL
, SHGFP_TYPE_CURRENT
, lpStatisticsFile
);
320 lstrcatW(lpStatisticsFile
, L
"\\Microsoft\\Windows\\GameExplorer\\GameStatistics\\");
321 lstrcatW(lpStatisticsFile
, lpApplicationId
);
322 lstrcatW(lpStatisticsFile
, L
"\\");
323 lstrcatW(lpStatisticsFile
, lpApplicationId
);
324 lstrcatW(lpStatisticsFile
, L
".gamestats");
329 /*******************************************************************************
330 * GAMEUX_getAppIdFromGDFPath
332 * Loads application identifier associated with given GDF binary.
333 * Routine reads identifier from registry, so will fail if game
337 * GDFBinaryPath [I] path to gdf binary
338 * lpApplicationId [O] place to store application id.
339 * must be at least 49 characters
340 * to store guid and termination 0
342 static HRESULT
GAMEUX_getAppIdFromGDFPath(
343 LPCWSTR GDFBinaryPath
,
344 LPWSTR lpApplicationId
)
347 GAME_INSTALL_SCOPE installScope
;
349 LPWSTR lpRegistryPath
= NULL
;
351 DWORD dwLength
= 49*sizeof(WCHAR
);/* place for GUID */
353 TRACE("(%s, %p)\n", debugstr_w(GDFBinaryPath
), lpApplicationId
);
358 installScope
= GIS_CURRENT_USER
;
359 hr
= GAMEUX_FindGameInstanceId(GDFBinaryPath
, installScope
, &instanceId
);
363 installScope
= GIS_ALL_USERS
;
364 hr
= GAMEUX_FindGameInstanceId(GDFBinaryPath
, installScope
, &instanceId
);
368 /* game not registered, so statistics cannot be used */
372 /* game is registered, let's read its application id from registry */
373 hr
= GAMEUX_buildGameRegistryPath(installScope
, &instanceId
, &lpRegistryPath
);
376 hr
= HRESULT_FROM_WIN32(RegOpenKeyExW(HKEY_LOCAL_MACHINE
,
377 lpRegistryPath
, 0, KEY_READ
| KEY_WOW64_64KEY
, &hKey
));
379 hr
= HRESULT_FROM_WIN32(RegGetValueW(hKey
, NULL
, L
"ApplicationId", RRF_RT_REG_SZ
,
380 NULL
, lpApplicationId
, &dwLength
));
385 HeapFree(GetProcessHeap(), 0, lpRegistryPath
);
387 TRACE("found app id: %s, return: %#lx\n", debugstr_w(lpApplicationId
), hr
);
390 /*******************************************************************
391 * GAMEUX_loadGameStatisticsFromFile
392 * Helper function, loads game statistics from file and stores them
396 * data [I/O] structure containing file name to
397 * load and data fields to store data in
399 static HRESULT
GAMEUX_loadStatisticsFromFile(struct GAMEUX_STATS
*data
)
402 IXMLDOMDocument
*document
= NULL
;
403 IXMLDOMElement
*root
= NULL
, *categoryElement
= NULL
, *statisticElement
= NULL
;
404 IXMLDOMNode
*categoryNode
= NULL
, *statisticNode
= NULL
;
405 IXMLDOMNodeList
*rootChildren
= NULL
, *categoryChildren
= NULL
;
406 VARIANT vStatsFilePath
, vValue
;
407 BSTR bstrStatistics
= NULL
, bstrCategory
= NULL
, bstrIndex
= NULL
,
408 bstrStatistic
= NULL
, bstrName
= NULL
, bstrValue
= NULL
;
409 VARIANT_BOOL isSuccessful
= VARIANT_FALSE
;
412 TRACE("(%p)\n", data
);
414 V_VT(&vStatsFilePath
) = VT_BSTR
;
415 V_BSTR(&vStatsFilePath
) = SysAllocString(data
->sStatsFile
);
416 if(!V_BSTR(&vStatsFilePath
))
420 hr
= CoCreateInstance(&CLSID_DOMDocument30
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IXMLDOMDocument
, (void**)&document
);
424 bstrStatistics
= SysAllocString(L
"Statistics");
431 bstrCategory
= SysAllocString(L
"Category");
438 bstrIndex
= SysAllocString(L
"Index");
445 bstrStatistic
= SysAllocString(L
"Statistic");
452 bstrName
= SysAllocString(L
"Name");
459 bstrValue
= SysAllocString(L
"Value");
465 hr
= IXMLDOMDocument_load(document
, vStatsFilePath
, &isSuccessful
);
467 if(hr
== S_OK
&& isSuccessful
!= VARIANT_TRUE
)
471 hr
= IXMLDOMDocument_get_documentElement(document
, &root
);
474 hr
= IXMLDOMElement_get_childNodes(root
, &rootChildren
);
481 hr
= IXMLDOMNodeList_nextNode(rootChildren
, &categoryNode
);
485 hr
= IXMLDOMNode_QueryInterface(categoryNode
, &IID_IXMLDOMElement
, (LPVOID
*)&categoryElement
);
489 hr
= IXMLDOMElement_getAttribute(categoryElement
, bstrIndex
, &vValue
);
490 if( hr
== S_OK
&& V_VT(&vValue
) != VT_BSTR
)
495 i
= StrToIntW(V_BSTR(&vValue
));
496 hr
= IXMLDOMElement_getAttribute(categoryElement
, bstrName
, &vValue
);
497 if( hr
== S_OK
&& V_VT(&vValue
) != VT_BSTR
)
503 lstrcpynW(data
->categories
[i
].sName
, V_BSTR(&vValue
), MAX_CATEGORY_LENGTH
);
504 TRACE("category %d name %s\n", i
, debugstr_w(data
->categories
[i
].sName
));
505 hr
= IXMLDOMElement_get_childNodes(categoryElement
, &categoryChildren
);
513 hr
= IXMLDOMNodeList_nextNode(categoryChildren
, &statisticNode
);
517 hr
= IXMLDOMNode_QueryInterface(statisticNode
, &IID_IXMLDOMElement
, (LPVOID
*)&statisticElement
);
521 hr
= IXMLDOMElement_getAttribute(statisticElement
, bstrIndex
, &vValue
);
522 if( hr
== S_OK
&& V_VT(&vValue
) != VT_BSTR
)
527 j
= StrToIntW(V_BSTR(&vValue
));
528 hr
= IXMLDOMElement_getAttribute(statisticElement
, bstrName
, &vValue
);
529 if( hr
== S_OK
&& V_VT(&vValue
) != VT_BSTR
)
535 lstrcpynW(data
->categories
[i
].stats
[j
].sName
, V_BSTR(&vValue
), MAX_NAME_LENGTH
);
536 hr
= IXMLDOMElement_getAttribute(statisticElement
, bstrValue
, &vValue
);
537 if( hr
== S_OK
&& V_VT(&vValue
) != VT_BSTR
)
543 lstrcpynW(data
->categories
[i
].stats
[j
].sValue
, V_BSTR(&vValue
), MAX_VALUE_LENGTH
);
544 TRACE("statistic %d name %s value %s\n", j
,
545 debugstr_w(data
->categories
[i
].stats
[j
].sName
),
546 debugstr_w(data
->categories
[i
].stats
[j
].sValue
));
548 if (statisticElement
) IXMLDOMElement_Release(statisticElement
);
551 if (statisticNode
) IXMLDOMNode_Release(statisticNode
);
555 if (categoryChildren
) IXMLDOMNodeList_Release(categoryChildren
);
560 if (categoryElement
) IXMLDOMElement_Release(categoryElement
);
563 if (categoryNode
) IXMLDOMNode_Release(categoryNode
);
570 if(rootChildren
) IXMLDOMNodeList_Release(rootChildren
);
571 if(root
) IXMLDOMElement_Release(root
);
572 if(document
) IXMLDOMDocument_Release(document
);
574 SysFreeString(bstrValue
);
575 SysFreeString(bstrName
);
576 SysFreeString(bstrStatistic
);
577 SysFreeString(bstrIndex
);
578 SysFreeString(bstrCategory
);
579 SysFreeString(bstrStatistics
);
580 SysFreeString(V_BSTR(&vStatsFilePath
));
583 /*******************************************************************
584 * GAMEUX_loadGameStatistics
586 * Helper function which loads game statistics associated with game
587 * into interface's internal structures
590 * pStats [O] structure which will receive data
591 * sGameId [I] application instance Id, stored as string
592 * to avoid additional conversions
593 * openType [I] allowed ways of opening statistics
594 * pOpenResult [O] way used to open statistics
597 static HRESULT
GAMEUX_loadGameStatistics(struct GAMEUX_STATS
*pStats
,
599 GAMESTATS_OPEN_TYPE openType
,
600 GAMESTATS_OPEN_RESULT
* pOpenResult
)
603 TRACE("(%p, %s, %d, %p)\n", pStats
, debugstr_w(sGameId
), openType
, pOpenResult
);
605 hr
= GAMEUX_buildStatisticsFilePath(sGameId
, pStats
->sStatsFile
);
606 if (FAILED(hr
)) return hr
;
608 hr
= GAMEUX_loadStatisticsFromFile(pStats
);
609 TRACE("ldstats finished, res: %#lx\n", hr
);
612 *pOpenResult
= GAMESTATS_OPEN_OPENED
;
614 else if(hr
== S_FALSE
&& openType
== GAMESTATS_OPEN_OPENORCREATE
) /* file does not exist */
616 /* create new statistics, not yet connected with file */
617 ZeroMemory(pStats
->categories
, sizeof(pStats
->categories
));
618 *pOpenResult
= GAMESTATS_OPEN_CREATED
;
622 hr
= HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND
);
624 TRACE("openResult=%#x ret=%#lx\n", *pOpenResult
, hr
);
627 /*******************************************************************
628 * IGameStatistics implementation
630 typedef struct _GameStatisticsImpl
632 IGameStatistics IGameStatistics_iface
;
634 struct GAMEUX_STATS stats
;
635 } GameStatisticsImpl
;
637 static inline GameStatisticsImpl
*impl_from_IGameStatistics( IGameStatistics
*iface
)
639 return CONTAINING_RECORD(iface
, GameStatisticsImpl
, IGameStatistics_iface
);
642 static HRESULT WINAPI
GameStatisticsImpl_QueryInterface(
643 IGameStatistics
*iface
,
647 GameStatisticsImpl
*This
= impl_from_IGameStatistics( iface
);
649 TRACE("%p %s %p\n", This
, debugstr_guid( riid
), ppvObject
);
653 if ( IsEqualGUID( riid
, &IID_IUnknown
) ||
654 IsEqualGUID( riid
, &IID_IGameStatistics
) )
660 FIXME("interface %s not implemented\n", debugstr_guid(riid
));
661 return E_NOINTERFACE
;
664 IGameStatistics_AddRef( iface
);
668 static ULONG WINAPI
GameStatisticsImpl_AddRef(IGameStatistics
*iface
)
670 GameStatisticsImpl
*This
= impl_from_IGameStatistics( iface
);
673 ref
= InterlockedIncrement(&This
->ref
);
675 TRACE("(%p): ref=%ld\n", This
, ref
);
679 static ULONG WINAPI
GameStatisticsImpl_Release(IGameStatistics
*iface
)
681 GameStatisticsImpl
*This
= impl_from_IGameStatistics( iface
);
684 ref
= InterlockedDecrement( &This
->ref
);
685 TRACE("(%p): ref=%ld\n", This
, ref
);
689 TRACE("freeing IGameStatistics\n");
690 HeapFree( GetProcessHeap(), 0, This
);
696 static HRESULT WINAPI
GameStatisticsImpl_GetMaxCategoryLength(
697 IGameStatistics
*iface
,
700 TRACE("(%p, %p)\n", iface
, cch
);
704 *cch
= MAX_CATEGORY_LENGTH
;
708 static HRESULT WINAPI
GameStatisticsImpl_GetMaxNameLength(
709 IGameStatistics
*iface
,
712 TRACE("(%p, %p)\n", iface
, cch
);
716 *cch
= MAX_NAME_LENGTH
;
720 static HRESULT WINAPI
GameStatisticsImpl_GetMaxValueLength(
721 IGameStatistics
*iface
,
724 TRACE("(%p, %p)\n", iface
, cch
);
728 *cch
= MAX_VALUE_LENGTH
;
732 static HRESULT WINAPI
GameStatisticsImpl_GetMaxCategories(
733 IGameStatistics
*iface
,
736 TRACE("(%p, %p)\n", iface
, pMax
);
740 *pMax
= MAX_CATEGORIES
;
744 static HRESULT WINAPI
GameStatisticsImpl_GetMaxStatsPerCategory(
745 IGameStatistics
*iface
,
748 TRACE("(%p, %p)\n", iface
, pMax
);
752 *pMax
= MAX_STATS_PER_CATEGORY
;
756 static HRESULT WINAPI
GameStatisticsImpl_SetCategoryTitle(
757 IGameStatistics
*iface
,
763 GameStatisticsImpl
*This
= impl_from_IGameStatistics(iface
);
765 TRACE("(%p, %d, %s)\n", This
, categoryIndex
, debugstr_w(title
));
767 if(!title
|| categoryIndex
>= MAX_CATEGORIES
)
770 dwLength
= lstrlenW(title
);
772 if(dwLength
> MAX_CATEGORY_LENGTH
)
775 dwLength
= MAX_CATEGORY_LENGTH
;
778 lstrcpynW(This
->stats
.categories
[categoryIndex
].sName
,
784 static HRESULT WINAPI
GameStatisticsImpl_GetCategoryTitle(
785 IGameStatistics
*iface
,
791 GameStatisticsImpl
*This
= impl_from_IGameStatistics(iface
);
793 TRACE("%p, %d, %p\n", This
, categoryIndex
, pTitle
);
799 if (categoryIndex
>= MAX_CATEGORIES
)
804 nLength
= lstrlenW(This
->stats
.categories
[categoryIndex
].sName
);
807 *pTitle
= CoTaskMemAlloc(sizeof(WCHAR
)*(nLength
+1));
808 lstrcpyW(*pTitle
, This
->stats
.categories
[categoryIndex
].sName
);
815 static HRESULT WINAPI
GameStatisticsImpl_GetStatistic(
816 IGameStatistics
*iface
,
824 GameStatisticsImpl
*This
= impl_from_IGameStatistics(iface
);
826 TRACE("%p, %d,%d, %p, %p\n", This
, categoryIndex
, statIndex
, pName
, pValue
);
828 if(!pName
|| !pValue
)
834 if(categoryIndex
>= MAX_CATEGORIES
|| statIndex
>= MAX_STATS_PER_CATEGORY
)
839 nLength
= lstrlenW(This
->stats
.categories
[categoryIndex
].stats
[statIndex
].sName
);
842 *pName
= CoTaskMemAlloc(sizeof(WCHAR
)*(nLength
+1));
846 lstrcpyW(*pName
, This
->stats
.categories
[categoryIndex
].stats
[statIndex
].sName
);
852 nLength
= lstrlenW(This
->stats
.categories
[categoryIndex
].stats
[statIndex
].sValue
);
855 *pValue
= CoTaskMemAlloc(sizeof(WCHAR
)*(nLength
+1));
859 lstrcpyW(*pValue
, This
->stats
.categories
[categoryIndex
].stats
[statIndex
].sValue
);
863 TRACE("returning pair; %s => %s\n", debugstr_w(*pName
), debugstr_w(*pValue
));
867 static HRESULT WINAPI
GameStatisticsImpl_SetStatistic(
868 IGameStatistics
*iface
,
875 DWORD dwNameLen
, dwValueLen
;
876 GameStatisticsImpl
*This
= impl_from_IGameStatistics(iface
);
878 TRACE("(%p, %d, %d, %s, %s)\n", This
, categoryIndex
, statIndex
,
879 debugstr_w(name
), debugstr_w(value
));
884 if(categoryIndex
>= MAX_CATEGORIES
|| statIndex
>= MAX_STATS_PER_CATEGORY
)
887 dwNameLen
= lstrlenW(name
);
889 if(dwNameLen
> MAX_NAME_LENGTH
)
892 dwNameLen
= MAX_NAME_LENGTH
;
895 lstrcpynW(This
->stats
.categories
[categoryIndex
].stats
[statIndex
].sName
,
900 dwValueLen
= lstrlenW(value
);
902 if(dwValueLen
> MAX_VALUE_LENGTH
)
905 dwValueLen
= MAX_VALUE_LENGTH
;
908 lstrcpynW(This
->stats
.categories
[categoryIndex
].stats
[statIndex
].sValue
,
909 value
, dwValueLen
+1);
912 /* Windows allows passing NULL as value */
913 This
->stats
.categories
[categoryIndex
].stats
[statIndex
].sValue
[0] = 0;
918 static HRESULT WINAPI
GameStatisticsImpl_Save(
919 IGameStatistics
*iface
,
922 GameStatisticsImpl
*This
= impl_from_IGameStatistics(iface
);
924 TRACE("(%p, %d)\n", This
, trackChanges
);
927 FIXME("tracking changes not yet implemented\n");
929 return GAMEUX_updateStatisticsFile(&This
->stats
);
932 static HRESULT WINAPI
GameStatisticsImpl_SetLastPlayedCategory(
933 IGameStatistics
*iface
,
940 static HRESULT WINAPI
GameStatisticsImpl_GetLastPlayedCategory(
941 IGameStatistics
*iface
,
942 UINT
*pCategoryIndex
)
948 static const struct IGameStatisticsVtbl GameStatisticsImplVtbl
=
950 GameStatisticsImpl_QueryInterface
,
951 GameStatisticsImpl_AddRef
,
952 GameStatisticsImpl_Release
,
953 GameStatisticsImpl_GetMaxCategoryLength
,
954 GameStatisticsImpl_GetMaxNameLength
,
955 GameStatisticsImpl_GetMaxValueLength
,
956 GameStatisticsImpl_GetMaxCategories
,
957 GameStatisticsImpl_GetMaxStatsPerCategory
,
958 GameStatisticsImpl_SetCategoryTitle
,
959 GameStatisticsImpl_GetCategoryTitle
,
960 GameStatisticsImpl_GetStatistic
,
961 GameStatisticsImpl_SetStatistic
,
962 GameStatisticsImpl_Save
,
963 GameStatisticsImpl_SetLastPlayedCategory
,
964 GameStatisticsImpl_GetLastPlayedCategory
968 static HRESULT
create_IGameStatistics(GameStatisticsImpl
** ppStats
)
970 TRACE("(%p)\n", ppStats
);
972 *ppStats
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(**ppStats
));
974 return E_OUTOFMEMORY
;
976 (*ppStats
)->IGameStatistics_iface
.lpVtbl
= &GameStatisticsImplVtbl
;
979 TRACE("returning coclass: %p\n", *ppStats
);
983 /*******************************************************************************
984 * IGameStatisticsMgr implementation
986 typedef struct _GameStatisticsMgrImpl
988 IGameStatisticsMgr IGameStatisticsMgr_iface
;
990 } GameStatisticsMgrImpl
;
992 static inline GameStatisticsMgrImpl
*impl_from_IGameStatisticsMgr( IGameStatisticsMgr
*iface
)
994 return CONTAINING_RECORD(iface
, GameStatisticsMgrImpl
, IGameStatisticsMgr_iface
);
998 static HRESULT WINAPI
GameStatisticsMgrImpl_QueryInterface(
999 IGameStatisticsMgr
*iface
,
1003 GameStatisticsMgrImpl
*This
= impl_from_IGameStatisticsMgr( iface
);
1005 TRACE("%p %s %p\n", This
, debugstr_guid( riid
), ppvObject
);
1009 if(IsEqualGUID(riid
, &IID_IUnknown
) ||
1010 IsEqualGUID(riid
, &IID_IGameStatisticsMgr
) )
1016 FIXME("interface %s not implemented\n", debugstr_guid(riid
));
1017 return E_NOINTERFACE
;
1020 IGameStatisticsMgr_AddRef( iface
);
1024 static ULONG WINAPI
GameStatisticsMgrImpl_AddRef(IGameStatisticsMgr
*iface
)
1026 GameStatisticsMgrImpl
*This
= impl_from_IGameStatisticsMgr( iface
);
1029 ref
= InterlockedIncrement(&This
->ref
);
1031 TRACE("(%p): ref=%ld\n", This
, ref
);
1035 static ULONG WINAPI
GameStatisticsMgrImpl_Release(IGameStatisticsMgr
*iface
)
1037 GameStatisticsMgrImpl
*This
= impl_from_IGameStatisticsMgr( iface
);
1040 ref
= InterlockedDecrement(&This
->ref
);
1041 TRACE("(%p): ref=%ld\n", This
, ref
);
1045 TRACE("freeing GameStatistics object\n");
1046 HeapFree( GetProcessHeap(), 0, This
);
1052 static HRESULT STDMETHODCALLTYPE
GameStatisticsMgrImpl_GetGameStatistics(
1053 IGameStatisticsMgr
* iface
,
1054 LPCWSTR GDFBinaryPath
,
1055 GAMESTATS_OPEN_TYPE openType
,
1056 GAMESTATS_OPEN_RESULT
*pOpenResult
,
1057 IGameStatistics
**ppiStats
)
1060 WCHAR lpApplicationId
[49];
1061 GameStatisticsImpl
*statisticsImpl
= NULL
;
1062 IGameStatistics
*output_iface
;
1064 TRACE("(%p, %s, 0x%x, %p, %p)\n", iface
, debugstr_w(GDFBinaryPath
), openType
, pOpenResult
, ppiStats
);
1066 hr
= GAMEUX_getAppIdFromGDFPath(GDFBinaryPath
, lpApplicationId
);
1069 hr
= create_IGameStatistics(&statisticsImpl
);
1073 output_iface
= &statisticsImpl
->IGameStatistics_iface
;
1074 hr
= GAMEUX_buildStatisticsFilePath(lpApplicationId
, statisticsImpl
->stats
.sStatsFile
);
1078 hr
= GAMEUX_loadGameStatistics(&statisticsImpl
->stats
, lpApplicationId
, openType
, pOpenResult
);
1081 *ppiStats
= output_iface
;
1084 HeapFree(GetProcessHeap(), 0, statisticsImpl
);
1091 static HRESULT STDMETHODCALLTYPE
GameStatisticsMgrImpl_RemoveGameStatistics(
1092 IGameStatisticsMgr
* iface
,
1093 LPCWSTR GDFBinaryPath
)
1096 WCHAR lpApplicationId
[49];
1097 WCHAR sStatsFile
[MAX_PATH
];
1099 TRACE("(%p, %s)\n", iface
, debugstr_w(GDFBinaryPath
));
1101 hr
= GAMEUX_getAppIdFromGDFPath(GDFBinaryPath
, lpApplicationId
);
1104 hr
= GAMEUX_buildStatisticsFilePath(lpApplicationId
, sStatsFile
);
1107 hr
= DeleteFileW(sStatsFile
) ? S_OK
: HRESULT_FROM_WIN32(GetLastError());
1112 static const struct IGameStatisticsMgrVtbl GameStatisticsMgrImplVtbl
=
1114 GameStatisticsMgrImpl_QueryInterface
,
1115 GameStatisticsMgrImpl_AddRef
,
1116 GameStatisticsMgrImpl_Release
,
1117 GameStatisticsMgrImpl_GetGameStatistics
,
1118 GameStatisticsMgrImpl_RemoveGameStatistics
,
1121 HRESULT
GameStatistics_create(
1122 IUnknown
*pUnkOuter
,
1125 GameStatisticsMgrImpl
*pGameStatistics
;
1127 TRACE("(%p, %p)\n", pUnkOuter
, ppObj
);
1129 pGameStatistics
= HeapAlloc( GetProcessHeap(), 0, sizeof (*pGameStatistics
) );
1131 if( !pGameStatistics
)
1132 return E_OUTOFMEMORY
;
1134 pGameStatistics
->IGameStatisticsMgr_iface
.lpVtbl
= &GameStatisticsMgrImplVtbl
;
1135 pGameStatistics
->ref
= 1;
1137 *ppObj
= (IUnknown
*)&pGameStatistics
->IGameStatisticsMgr_iface
;
1139 TRACE("returning iface %p\n", *ppObj
);