http.sys: Keep connection sockets open after sending a 400 response.
[wine.git] / dlls / gameux / gamestatistics.c
blob984dfc4ee9fb2d922e2d6726270f148ccd5ea8b0
1 /*
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
20 #define COBJMACROS
23 #include "ole2.h"
24 #include "winreg.h"
25 #include "msxml2.h"
26 #include "shlwapi.h"
27 #include "shlobj.h"
29 #include "gameux.h"
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 /*******************************************************************************
48 * struct GAMEUX_STATS
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];
62 struct GAMEUX_STATS
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
72 * Parameters
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)
79 HRESULT hr;
80 WCHAR lpDirectoryPath[MAX_PATH];
81 LPCWSTR lpEnd;
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))
90 hr = S_FALSE;
92 return hr;
94 /*******************************************************************
95 * GAMEUX_updateStatisticsFile
97 * Helper function updating data stored in statistics file
99 * Parameters:
100 * data [I] pointer to struct containing
101 * statistics data
103 static HRESULT GAMEUX_updateStatisticsFile(struct GAMEUX_STATS *stats)
105 HRESULT hr = S_OK;
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;
112 int i, j;
114 TRACE("(%p)\n", stats);
116 V_VT(&vStatsFilePath) = VT_BSTR;
117 V_BSTR(&vStatsFilePath) = SysAllocString(stats->sStatsFile);
118 if(!V_BSTR(&vStatsFilePath))
119 hr = E_OUTOFMEMORY;
121 if(SUCCEEDED(hr))
122 hr = CoCreateInstance(&CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER,
123 &IID_IXMLDOMDocument, (void**)&document);
125 if(SUCCEEDED(hr))
127 bstrStatistics = SysAllocString(L"Statistics");
128 if(!bstrStatistics)
129 hr = E_OUTOFMEMORY;
132 if(SUCCEEDED(hr))
133 hr = IXMLDOMDocument_createElement(document, bstrStatistics, &root);
135 if(SUCCEEDED(hr))
137 bstrCategory = SysAllocString(L"Category");
138 if(!bstrCategory)
139 hr = E_OUTOFMEMORY;
142 if(SUCCEEDED(hr))
144 bstrIndex = SysAllocString(L"Index");
145 if(!bstrIndex)
146 hr = E_OUTOFMEMORY;
149 if(SUCCEEDED(hr))
151 bstrStatistic = SysAllocString(L"Statistic");
152 if(!bstrStatistic)
153 hr = E_OUTOFMEMORY;
156 if(SUCCEEDED(hr))
158 bstrName = SysAllocString(L"Name");
159 if(!bstrName)
160 hr = E_OUTOFMEMORY;
163 if(SUCCEEDED(hr))
165 bstrValue = SysAllocString(L"Value");
166 if(!bstrValue)
167 hr = E_OUTOFMEMORY;
170 if(SUCCEEDED(hr))
171 for(i=0; i<MAX_CATEGORIES; ++i)
173 IXMLDOMElement *categoryElement = NULL;
175 if(!stats->categories[i].sName[0])
176 continue;
178 V_VT(&vValue) = VT_INT;
179 V_INT(&vValue) = NODE_ELEMENT;
181 hr = IXMLDOMDocument_createNode(document, vValue, bstrCategory, NULL, &categoryNode);
183 if(SUCCEEDED(hr))
184 hr = IXMLDOMNode_QueryInterface(categoryNode, &IID_IXMLDOMElement, (void**)&categoryElement);
186 V_INT(&vValue) = i;
187 if(SUCCEEDED(hr))
188 hr = IXMLDOMElement_setAttribute(categoryElement, bstrIndex, vValue);
190 if(SUCCEEDED(hr))
192 V_VT(&vValue) = VT_BSTR;
193 V_BSTR(&vValue) = SysAllocString(stats->categories[i].sName);
194 if(!V_BSTR(&vValue))
195 hr = E_OUTOFMEMORY;
198 if(SUCCEEDED(hr))
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));
208 if(SUCCEEDED(hr))
210 for(j=0; j<MAX_STATS_PER_CATEGORY; ++j)
212 if(!stats->categories[i].stats[j].sName[0])
213 continue;
215 V_VT(&vValue) = VT_INT;
216 V_INT(&vValue) = NODE_ELEMENT;
218 hr = IXMLDOMDocument_createNode(document, vValue, bstrStatistic, NULL, &statisticsNode);
220 if(SUCCEEDED(hr))
221 hr = IXMLDOMNode_QueryInterface(statisticsNode, &IID_IXMLDOMElement, (LPVOID*)&statisticsElement);
223 V_INT(&vValue) = j;
224 if(SUCCEEDED(hr))
225 hr = IXMLDOMElement_setAttribute(statisticsElement, bstrIndex, vValue);
227 if(SUCCEEDED(hr))
229 V_VT(&vValue) = VT_BSTR;
230 V_BSTR(&vValue) = SysAllocString(stats->categories[i].stats[j].sName);
231 if(!V_BSTR(&vValue))
232 hr = E_OUTOFMEMORY;
235 if(SUCCEEDED(hr))
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));
242 if(SUCCEEDED(hr))
244 V_VT(&vValue) = VT_BSTR;
245 V_BSTR(&vValue) = SysAllocString(stats->categories[i].stats[j].sValue);
246 if(!V_BSTR(&vValue))
247 hr = E_OUTOFMEMORY;
250 if(SUCCEEDED(hr))
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));
257 if(SUCCEEDED(hr))
258 hr = IXMLDOMNode_appendChild(categoryNode, statisticsNode, NULL);
260 if (statisticsElement) IXMLDOMElement_Release(statisticsElement);
261 if (statisticsNode) IXMLDOMNode_Release(statisticsNode);
265 if(SUCCEEDED(hr))
266 hr = IXMLDOMElement_appendChild(root, categoryNode, &categoryNode);
268 if (categoryNode)
269 IXMLDOMNode_Release(categoryNode);
271 if(FAILED(hr))
272 break;
275 if(SUCCEEDED(hr))
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));
281 if(SUCCEEDED(hr))
282 hr = GAMEUX_createStatsDirectory(stats->sStatsFile);
284 if(SUCCEEDED(hr))
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);
297 return hr;
299 /*******************************************************************************
300 * GAMEUX_buildStatisticsFilePath
301 * Creates path to file containing statistics of game with given id.
303 * Parameters:
304 * lpApplicationId [I] application id of game,
305 * as string
306 * lpStatisticsFile [O] array where path will be
307 * stored. Its size must be
308 * at least MAX_PATH
310 static HRESULT GAMEUX_buildStatisticsFilePath(
311 LPCWSTR lpApplicationId,
312 LPWSTR lpStatisticsFile)
314 HRESULT hr;
316 hr = SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, lpStatisticsFile);
318 if(SUCCEEDED(hr))
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");
327 return hr;
329 /*******************************************************************************
330 * GAMEUX_getAppIdFromGDFPath
332 * Loads application identifier associated with given GDF binary.
333 * Routine reads identifier from registry, so will fail if game
334 * is not registered.
336 * Parameters:
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)
346 HRESULT hr;
347 GAME_INSTALL_SCOPE installScope;
348 GUID instanceId;
349 LPWSTR lpRegistryPath = NULL;
350 HKEY hKey;
351 DWORD dwLength = 49*sizeof(WCHAR);/* place for GUID */
353 TRACE("(%s, %p)\n", debugstr_w(GDFBinaryPath), lpApplicationId);
355 if(!GDFBinaryPath)
356 return E_INVALIDARG;
358 installScope = GIS_CURRENT_USER;
359 hr = GAMEUX_FindGameInstanceId(GDFBinaryPath, installScope, &instanceId);
361 if(hr == S_FALSE)
363 installScope = GIS_ALL_USERS;
364 hr = GAMEUX_FindGameInstanceId(GDFBinaryPath, installScope, &instanceId);
367 if(hr == S_FALSE)
368 /* game not registered, so statistics cannot be used */
369 hr = E_FAIL;
371 if(SUCCEEDED(hr))
372 /* game is registered, let's read its application id from registry */
373 hr = GAMEUX_buildGameRegistryPath(installScope, &instanceId, &lpRegistryPath);
375 if(SUCCEEDED(hr)) {
376 hr = HRESULT_FROM_WIN32(RegOpenKeyExW(HKEY_LOCAL_MACHINE,
377 lpRegistryPath, 0, KEY_READ | KEY_WOW64_64KEY, &hKey));
378 if(SUCCEEDED(hr)) {
379 hr = HRESULT_FROM_WIN32(RegGetValueW(hKey, NULL, L"ApplicationId", RRF_RT_REG_SZ,
380 NULL, lpApplicationId, &dwLength));
381 RegCloseKey(hKey);
385 free(lpRegistryPath);
387 TRACE("found app id: %s, return: %#lx\n", debugstr_w(lpApplicationId), hr);
388 return hr;
390 /*******************************************************************
391 * GAMEUX_loadGameStatisticsFromFile
392 * Helper function, loads game statistics from file and stores them
393 * in the structure.
395 * Parameters:
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)
401 HRESULT hr = S_OK;
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;
410 int i, j;
412 TRACE("(%p)\n", data);
414 V_VT(&vStatsFilePath) = VT_BSTR;
415 V_BSTR(&vStatsFilePath) = SysAllocString(data->sStatsFile);
416 if(!V_BSTR(&vStatsFilePath))
417 hr = E_OUTOFMEMORY;
419 if(SUCCEEDED(hr))
420 hr = CoCreateInstance(&CLSID_DOMDocument30, NULL, CLSCTX_INPROC_SERVER, &IID_IXMLDOMDocument, (void**)&document);
422 if(SUCCEEDED(hr))
424 bstrStatistics = SysAllocString(L"Statistics");
425 if(!bstrStatistics)
426 hr = E_OUTOFMEMORY;
429 if(SUCCEEDED(hr))
431 bstrCategory = SysAllocString(L"Category");
432 if(!bstrCategory)
433 hr = E_OUTOFMEMORY;
436 if(SUCCEEDED(hr))
438 bstrIndex = SysAllocString(L"Index");
439 if(!bstrIndex)
440 hr = E_OUTOFMEMORY;
443 if(SUCCEEDED(hr))
445 bstrStatistic = SysAllocString(L"Statistic");
446 if(!bstrStatistic)
447 hr = E_OUTOFMEMORY;
450 if(SUCCEEDED(hr))
452 bstrName = SysAllocString(L"Name");
453 if(!bstrName)
454 hr = E_OUTOFMEMORY;
457 if(SUCCEEDED(hr))
459 bstrValue = SysAllocString(L"Value");
460 if(!bstrValue)
461 hr = E_OUTOFMEMORY;
464 if(SUCCEEDED(hr))
465 hr = IXMLDOMDocument_load(document, vStatsFilePath, &isSuccessful);
467 if(hr == S_OK && isSuccessful != VARIANT_TRUE)
468 hr = S_FALSE;
470 if( hr == S_OK )
471 hr = IXMLDOMDocument_get_documentElement(document, &root);
473 if(hr == S_OK)
474 hr = IXMLDOMElement_get_childNodes(root, &rootChildren);
476 if(hr == S_OK)
478 hr = S_OK;
479 while(hr == S_OK)
481 hr = IXMLDOMNodeList_nextNode(rootChildren, &categoryNode);
483 if(hr == S_OK)
485 hr = IXMLDOMNode_QueryInterface(categoryNode, &IID_IXMLDOMElement, (LPVOID*)&categoryElement);
487 if(SUCCEEDED(hr))
489 hr = IXMLDOMElement_getAttribute(categoryElement, bstrIndex, &vValue);
490 if( hr == S_OK && V_VT(&vValue) != VT_BSTR)
491 hr = E_FAIL;
493 if(SUCCEEDED(hr))
495 i = StrToIntW(V_BSTR(&vValue));
496 hr = IXMLDOMElement_getAttribute(categoryElement, bstrName, &vValue);
497 if( hr == S_OK && V_VT(&vValue) != VT_BSTR)
498 hr = E_FAIL;
501 if(SUCCEEDED(hr))
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);
508 if(SUCCEEDED(hr))
510 hr = S_OK;
511 while(hr == S_OK)
513 hr = IXMLDOMNodeList_nextNode(categoryChildren, &statisticNode);
515 if(hr == S_OK)
517 hr = IXMLDOMNode_QueryInterface(statisticNode, &IID_IXMLDOMElement, (LPVOID*)&statisticElement);
519 if(SUCCEEDED(hr))
521 hr = IXMLDOMElement_getAttribute(statisticElement, bstrIndex, &vValue);
522 if( hr == S_OK && V_VT(&vValue) != VT_BSTR)
523 hr = E_FAIL;
525 if(SUCCEEDED(hr))
527 j = StrToIntW(V_BSTR(&vValue));
528 hr = IXMLDOMElement_getAttribute(statisticElement, bstrName, &vValue);
529 if( hr == S_OK && V_VT(&vValue) != VT_BSTR)
530 hr = E_FAIL;
533 if(SUCCEEDED(hr))
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)
538 hr = E_FAIL;
541 if(SUCCEEDED(hr))
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);
557 if(SUCCEEDED(hr))
558 hr = S_OK;
560 if (categoryElement) IXMLDOMElement_Release(categoryElement);
563 if (categoryNode) IXMLDOMNode_Release(categoryNode);
566 if(SUCCEEDED(hr))
567 hr = S_OK;
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));
581 return hr;
583 /*******************************************************************
584 * GAMEUX_loadGameStatistics
586 * Helper function which loads game statistics associated with game
587 * into interface's internal structures
589 * Parameters:
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,
598 LPWSTR sGameId,
599 GAMESTATS_OPEN_TYPE openType,
600 GAMESTATS_OPEN_RESULT* pOpenResult)
602 HRESULT hr;
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);
610 if(hr == S_OK)
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;
619 hr = S_OK;
621 else
622 hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
624 TRACE("openResult=%#x ret=%#lx\n", *pOpenResult, hr);
625 return hr;
627 /*******************************************************************
628 * IGameStatistics implementation
630 typedef struct _GameStatisticsImpl
632 IGameStatistics IGameStatistics_iface;
633 LONG ref;
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,
644 REFIID riid,
645 void **ppvObject)
647 GameStatisticsImpl *This = impl_from_IGameStatistics( iface );
649 TRACE("%p %s %p\n", This, debugstr_guid( riid ), ppvObject );
651 *ppvObject = NULL;
653 if ( IsEqualGUID( riid, &IID_IUnknown ) ||
654 IsEqualGUID( riid, &IID_IGameStatistics ) )
656 *ppvObject = iface;
658 else
660 FIXME("interface %s not implemented\n", debugstr_guid(riid));
661 return E_NOINTERFACE;
664 IGameStatistics_AddRef( iface );
665 return S_OK;
668 static ULONG WINAPI GameStatisticsImpl_AddRef(IGameStatistics *iface)
670 GameStatisticsImpl *This = impl_from_IGameStatistics( iface );
671 LONG ref;
673 ref = InterlockedIncrement(&This->ref);
675 TRACE("(%p): ref=%ld\n", This, ref);
676 return ref;
679 static ULONG WINAPI GameStatisticsImpl_Release(IGameStatistics *iface)
681 GameStatisticsImpl *This = impl_from_IGameStatistics( iface );
682 LONG ref;
684 ref = InterlockedDecrement( &This->ref );
685 TRACE("(%p): ref=%ld\n", This, ref);
687 if ( ref == 0 )
689 TRACE("freeing IGameStatistics\n");
690 free( This );
693 return ref;
696 static HRESULT WINAPI GameStatisticsImpl_GetMaxCategoryLength(
697 IGameStatistics *iface,
698 UINT *cch)
700 TRACE("(%p, %p)\n", iface, cch);
701 if(!cch)
702 return E_INVALIDARG;
704 *cch = MAX_CATEGORY_LENGTH;
705 return S_OK;
708 static HRESULT WINAPI GameStatisticsImpl_GetMaxNameLength(
709 IGameStatistics *iface,
710 UINT *cch)
712 TRACE("(%p, %p)\n", iface, cch);
713 if(!cch)
714 return E_INVALIDARG;
716 *cch = MAX_NAME_LENGTH;
717 return S_OK;
720 static HRESULT WINAPI GameStatisticsImpl_GetMaxValueLength(
721 IGameStatistics *iface,
722 UINT *cch)
724 TRACE("(%p, %p)\n", iface, cch);
725 if(!cch)
726 return E_INVALIDARG;
728 *cch = MAX_VALUE_LENGTH;
729 return S_OK;
732 static HRESULT WINAPI GameStatisticsImpl_GetMaxCategories(
733 IGameStatistics *iface,
734 WORD *pMax)
736 TRACE("(%p, %p)\n", iface, pMax);
737 if(!pMax)
738 return E_INVALIDARG;
740 *pMax = MAX_CATEGORIES;
741 return S_OK;
744 static HRESULT WINAPI GameStatisticsImpl_GetMaxStatsPerCategory(
745 IGameStatistics *iface,
746 WORD *pMax)
748 TRACE("(%p, %p)\n", iface, pMax);
749 if(!pMax)
750 return E_INVALIDARG;
752 *pMax = MAX_STATS_PER_CATEGORY;
753 return S_OK;
756 static HRESULT WINAPI GameStatisticsImpl_SetCategoryTitle(
757 IGameStatistics *iface,
758 WORD categoryIndex,
759 LPCWSTR title)
761 HRESULT hr = S_OK;
762 DWORD dwLength;
763 GameStatisticsImpl *This = impl_from_IGameStatistics(iface);
765 TRACE("(%p, %d, %s)\n", This, categoryIndex, debugstr_w(title));
767 if(!title || categoryIndex >= MAX_CATEGORIES)
768 return E_INVALIDARG;
770 dwLength = lstrlenW(title);
772 if(dwLength > MAX_CATEGORY_LENGTH)
774 hr = S_FALSE;
775 dwLength = MAX_CATEGORY_LENGTH;
778 lstrcpynW(This->stats.categories[categoryIndex].sName,
779 title, dwLength+1);
781 return hr;
784 static HRESULT WINAPI GameStatisticsImpl_GetCategoryTitle(
785 IGameStatistics *iface,
786 WORD categoryIndex,
787 LPWSTR *pTitle)
789 HRESULT hr = S_OK;
790 LONG nLength;
791 GameStatisticsImpl *This = impl_from_IGameStatistics(iface);
793 TRACE("%p, %d, %p\n", This, categoryIndex, pTitle);
795 if(!pTitle)
796 return E_INVALIDARG;
797 *pTitle = NULL;
799 if (categoryIndex >= MAX_CATEGORIES)
800 hr = E_INVALIDARG;
802 if(SUCCEEDED(hr))
804 nLength = lstrlenW(This->stats.categories[categoryIndex].sName);
805 if(nLength != 0)
807 *pTitle = CoTaskMemAlloc(sizeof(WCHAR)*(nLength+1));
808 lstrcpyW(*pTitle, This->stats.categories[categoryIndex].sName);
812 return hr;
815 static HRESULT WINAPI GameStatisticsImpl_GetStatistic(
816 IGameStatistics *iface,
817 WORD categoryIndex,
818 WORD statIndex,
819 LPWSTR *pName,
820 LPWSTR *pValue)
822 HRESULT hr = S_OK;
823 LONG nLength;
824 GameStatisticsImpl *This = impl_from_IGameStatistics(iface);
826 TRACE("%p, %d,%d, %p, %p\n", This, categoryIndex, statIndex, pName, pValue);
828 if(!pName || !pValue)
829 return E_INVALIDARG;
831 *pName = NULL;
832 *pValue = NULL;
834 if(categoryIndex >= MAX_CATEGORIES || statIndex >= MAX_STATS_PER_CATEGORY)
835 hr = E_INVALIDARG;
837 if(SUCCEEDED(hr))
839 nLength = lstrlenW(This->stats.categories[categoryIndex].stats[statIndex].sName);
840 if(nLength != 0)
842 *pName = CoTaskMemAlloc(sizeof(WCHAR)*(nLength+1));
843 if(!(*pName))
844 hr = E_OUTOFMEMORY;
845 else
846 lstrcpyW(*pName, This->stats.categories[categoryIndex].stats[statIndex].sName);
850 if(SUCCEEDED(hr))
852 nLength = lstrlenW(This->stats.categories[categoryIndex].stats[statIndex].sValue);
853 if(nLength != 0)
855 *pValue = CoTaskMemAlloc(sizeof(WCHAR)*(nLength+1));
856 if(!(*pValue))
857 hr = E_OUTOFMEMORY;
858 else
859 lstrcpyW(*pValue, This->stats.categories[categoryIndex].stats[statIndex].sValue);
863 TRACE("returning pair; %s => %s\n", debugstr_w(*pName), debugstr_w(*pValue));
864 return hr;
867 static HRESULT WINAPI GameStatisticsImpl_SetStatistic(
868 IGameStatistics *iface,
869 WORD categoryIndex,
870 WORD statIndex,
871 LPCWSTR name,
872 LPCWSTR value)
874 HRESULT hr = S_OK;
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));
881 if(!name)
882 return S_FALSE;
884 if(categoryIndex >= MAX_CATEGORIES || statIndex >= MAX_STATS_PER_CATEGORY)
885 return E_INVALIDARG;
887 dwNameLen = lstrlenW(name);
889 if(dwNameLen > MAX_NAME_LENGTH)
891 hr = S_FALSE;
892 dwNameLen = MAX_NAME_LENGTH;
895 lstrcpynW(This->stats.categories[categoryIndex].stats[statIndex].sName,
896 name, dwNameLen+1);
898 if(value)
900 dwValueLen = lstrlenW(value);
902 if(dwValueLen > MAX_VALUE_LENGTH)
904 hr = S_FALSE;
905 dwValueLen = MAX_VALUE_LENGTH;
908 lstrcpynW(This->stats.categories[categoryIndex].stats[statIndex].sValue,
909 value, dwValueLen+1);
911 else
912 /* Windows allows passing NULL as value */
913 This->stats.categories[categoryIndex].stats[statIndex].sValue[0] = 0;
915 return hr;
918 static HRESULT WINAPI GameStatisticsImpl_Save(
919 IGameStatistics *iface,
920 BOOL trackChanges)
922 GameStatisticsImpl *This = impl_from_IGameStatistics(iface);
924 TRACE("(%p, %d)\n", This, trackChanges);
926 if(trackChanges)
927 FIXME("tracking changes not yet implemented\n");
929 return GAMEUX_updateStatisticsFile(&This->stats);
932 static HRESULT WINAPI GameStatisticsImpl_SetLastPlayedCategory(
933 IGameStatistics *iface,
934 UINT categoryIndex)
936 FIXME("stub\n");
937 return E_NOTIMPL;
940 static HRESULT WINAPI GameStatisticsImpl_GetLastPlayedCategory(
941 IGameStatistics *iface,
942 UINT *pCategoryIndex)
944 FIXME("stub\n");
945 return E_NOTIMPL;
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 = calloc(1, sizeof(**ppStats));
973 if(!(*ppStats))
974 return E_OUTOFMEMORY;
976 (*ppStats)->IGameStatistics_iface.lpVtbl = &GameStatisticsImplVtbl;
977 (*ppStats)->ref = 1;
979 TRACE("returning coclass: %p\n", *ppStats);
980 return S_OK;
983 /*******************************************************************************
984 * IGameStatisticsMgr implementation
986 typedef struct _GameStatisticsMgrImpl
988 IGameStatisticsMgr IGameStatisticsMgr_iface;
989 LONG ref;
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,
1000 REFIID riid,
1001 void **ppvObject)
1003 GameStatisticsMgrImpl *This = impl_from_IGameStatisticsMgr( iface );
1005 TRACE("%p %s %p\n", This, debugstr_guid( riid ), ppvObject );
1007 *ppvObject = NULL;
1009 if(IsEqualGUID(riid, &IID_IUnknown) ||
1010 IsEqualGUID(riid, &IID_IGameStatisticsMgr) )
1012 *ppvObject = iface;
1014 else
1016 FIXME("interface %s not implemented\n", debugstr_guid(riid));
1017 return E_NOINTERFACE;
1020 IGameStatisticsMgr_AddRef( iface );
1021 return S_OK;
1024 static ULONG WINAPI GameStatisticsMgrImpl_AddRef(IGameStatisticsMgr *iface)
1026 GameStatisticsMgrImpl *This = impl_from_IGameStatisticsMgr( iface );
1027 LONG ref;
1029 ref = InterlockedIncrement(&This->ref);
1031 TRACE("(%p): ref=%ld\n", This, ref);
1032 return ref;
1035 static ULONG WINAPI GameStatisticsMgrImpl_Release(IGameStatisticsMgr *iface)
1037 GameStatisticsMgrImpl *This = impl_from_IGameStatisticsMgr( iface );
1038 LONG ref;
1040 ref = InterlockedDecrement(&This->ref);
1041 TRACE("(%p): ref=%ld\n", This, ref);
1043 if ( ref == 0 )
1045 TRACE("freeing GameStatistics object\n");
1046 free(This);
1049 return ref;
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)
1059 HRESULT hr;
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);
1068 if(SUCCEEDED(hr))
1069 hr = create_IGameStatistics(&statisticsImpl);
1071 if(SUCCEEDED(hr))
1073 output_iface = &statisticsImpl->IGameStatistics_iface;
1074 hr = GAMEUX_buildStatisticsFilePath(lpApplicationId, statisticsImpl->stats.sStatsFile);
1077 if(SUCCEEDED(hr))
1078 hr = GAMEUX_loadGameStatistics(&statisticsImpl->stats, lpApplicationId, openType, pOpenResult);
1080 if(SUCCEEDED(hr))
1081 *ppiStats = output_iface;
1082 else
1084 free(statisticsImpl);
1085 *ppiStats = NULL;
1088 return hr;
1091 static HRESULT STDMETHODCALLTYPE GameStatisticsMgrImpl_RemoveGameStatistics(
1092 IGameStatisticsMgr* iface,
1093 LPCWSTR GDFBinaryPath)
1095 HRESULT hr;
1096 WCHAR lpApplicationId[49];
1097 WCHAR sStatsFile[MAX_PATH];
1099 TRACE("(%p, %s)\n", iface, debugstr_w(GDFBinaryPath));
1101 hr = GAMEUX_getAppIdFromGDFPath(GDFBinaryPath, lpApplicationId);
1103 if(SUCCEEDED(hr))
1104 hr = GAMEUX_buildStatisticsFilePath(lpApplicationId, sStatsFile);
1106 if(SUCCEEDED(hr))
1107 hr = DeleteFileW(sStatsFile) ? S_OK : HRESULT_FROM_WIN32(GetLastError());
1109 return hr;
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,
1123 IUnknown **ppObj)
1125 GameStatisticsMgrImpl *pGameStatistics;
1127 TRACE("(%p, %p)\n", pUnkOuter, ppObj);
1129 pGameStatistics = malloc(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);
1140 return S_OK;