mshtml: Don't use PRInt32 in property getters implementations.
[wine] / dlls / gameux / gamestatistics.c
1 /*
2  *    Gameux library coclass GameStatistics implementation
3  *
4  * Copyright (C) 2010 Mariusz PluciƄski
5  *
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.
10  *
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.
15  *
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
19  */
20 #define COBJMACROS
21
22 #include "config.h"
23
24 #include "ole2.h"
25 #include "winreg.h"
26 #include "msxml2.h"
27 #include "shlwapi.h"
28 #include "shlobj.h"
29
30 #include "gameux.h"
31 #include "gameux_private.h"
32
33 #include "wine/debug.h"
34
35 WINE_DEFAULT_DEBUG_CHANNEL(gameux);
36
37 /*
38  * constant definitions
39  */
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
47  */
48 /*******************************************************************************
49  * struct GAMEUX_STATS
50  *
51  * set of structures for containing game's data
52  */
53 struct GAMEUX_STATS_STAT
54 {
55     WCHAR sName[MAX_NAME_LENGTH+1];
56     WCHAR sValue[MAX_VALUE_LENGTH+1];
57 };
58 struct GAMEUX_STATS_CATEGORY
59 {
60     WCHAR sName[MAX_CATEGORY_LENGTH+1];
61     struct GAMEUX_STATS_STAT stats[MAX_STATS_PER_CATEGORY];
62 };
63 struct GAMEUX_STATS
64 {
65     WCHAR sStatsFile[MAX_PATH];
66     struct GAMEUX_STATS_CATEGORY categories[MAX_CATEGORIES];
67 };
68 /*******************************************************************************
69  * GAMEUX_createStatsDirectory
70  *
71  * Helper function, creates directory to store game statistics
72  *
73  * Parameters
74  *  path                [I]     path to game statistics file.
75  *                              base directory of this file will
76  *                              be created if it doesn't exists
77  */
78 static HRESULT GAMEUX_createStatsDirectory(LPCWSTR lpFilePath)
79 {
80     HRESULT hr;
81     WCHAR lpDirectoryPath[MAX_PATH];
82     LPCWSTR lpEnd;
83
84     lpEnd = StrRChrW(lpFilePath, NULL, '\\');
85     lstrcpynW(lpDirectoryPath, lpFilePath, lpEnd-lpFilePath+1);
86
87     hr = HRESULT_FROM_WIN32(SHCreateDirectoryExW(NULL, lpDirectoryPath, NULL));
88
89     if(hr == HRESULT_FROM_WIN32(ERROR_FILE_EXISTS) ||
90        hr == HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS))
91         hr = S_FALSE;
92
93     return hr;
94 }
95 /*******************************************************************
96  * GAMEUX_updateStatisticsFile
97  *
98  * Helper function updating data stored in statistics file
99  *
100  * Parameters:
101  *  data                [I]     pointer to struct containing
102  *                              statistics data
103  */
104 static HRESULT GAMEUX_updateStatisticsFile(struct GAMEUX_STATS *stats)
105 {
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};
112
113     HRESULT hr = S_OK;
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;
120     int i, j;
121
122     TRACE("(%p)\n", stats);
123
124     V_VT(&vStatsFilePath) = VT_BSTR;
125     V_BSTR(&vStatsFilePath) = SysAllocString(stats->sStatsFile);
126     if(!V_BSTR(&vStatsFilePath))
127         hr = E_OUTOFMEMORY;
128
129     if(SUCCEEDED(hr))
130         hr = CoCreateInstance(&CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER,
131                               &IID_IXMLDOMDocument, (void**)&document);
132
133     if(SUCCEEDED(hr))
134     {
135         bstrStatistics = SysAllocString(sStatistics);
136         if(!bstrStatistics)
137             hr = E_OUTOFMEMORY;
138     }
139
140     if(SUCCEEDED(hr))
141         hr = IXMLDOMDocument_createElement(document, bstrStatistics, &root);
142
143     if(SUCCEEDED(hr))
144     {
145         bstrCategory = SysAllocString(sCategory);
146         if(!bstrCategory)
147             hr = E_OUTOFMEMORY;
148     }
149
150     if(SUCCEEDED(hr))
151     {
152         bstrIndex = SysAllocString(sIndex);
153         if(!bstrIndex)
154             hr = E_OUTOFMEMORY;
155     }
156
157     if(SUCCEEDED(hr))
158     {
159         bstrStatistic = SysAllocString(sStatistic);
160         if(!bstrStatistic)
161             hr = E_OUTOFMEMORY;
162     }
163
164     if(SUCCEEDED(hr))
165     {
166         bstrName = SysAllocString(sName);
167         if(!bstrName)
168             hr = E_OUTOFMEMORY;
169     }
170
171     if(SUCCEEDED(hr))
172     {
173         bstrValue = SysAllocString(sValue);
174         if(!bstrValue)
175             hr = E_OUTOFMEMORY;
176     }
177
178     if(SUCCEEDED(hr))
179         for(i=0; i<MAX_CATEGORIES; ++i)
180         {
181             if(lstrlenW(stats->categories[i].sName)==0)
182                 continue;
183
184             V_VT(&vValue) = VT_INT;
185             V_INT(&vValue) = NODE_ELEMENT;
186
187             hr = IXMLDOMDocument_createNode(document, vValue, bstrCategory, NULL, &categoryNode);
188
189             if(SUCCEEDED(hr))
190                 hr = IXMLDOMNode_QueryInterface(categoryNode, &IID_IXMLDOMElement, (LPVOID*)&categoryElement);
191
192             V_INT(&vValue) = i;
193             if(SUCCEEDED(hr))
194                 hr = IXMLDOMElement_setAttribute(categoryElement, bstrIndex, vValue);
195
196             if(SUCCEEDED(hr))
197             {
198                 V_VT(&vValue) = VT_BSTR;
199                 V_BSTR(&vValue) = SysAllocString(stats->categories[i].sName);
200                 if(!V_BSTR(&vValue))
201                     hr = E_OUTOFMEMORY;
202             }
203
204             if(SUCCEEDED(hr))
205             {
206                 TRACE("storing category %d: %s\n", i, debugstr_w(V_BSTR(&vValue)));
207                 hr = IXMLDOMElement_setAttribute(categoryElement, bstrName, vValue);
208             }
209
210             SysFreeString(V_BSTR(&vValue));
211
212             if(SUCCEEDED(hr))
213             {
214                 for(j=0; j<MAX_STATS_PER_CATEGORY; ++j)
215                 {
216                     if(lstrlenW(stats->categories[i].stats[j].sName)==0)
217                         continue;
218
219                     V_VT(&vValue) = VT_INT;
220                     V_INT(&vValue) = NODE_ELEMENT;
221
222                     hr = IXMLDOMDocument_createNode(document, vValue, bstrStatistic, NULL, &statisticsNode);
223
224                     if(SUCCEEDED(hr))
225                         hr = IXMLDOMNode_QueryInterface(statisticsNode, &IID_IXMLDOMElement, (LPVOID*)&statisticsElement);
226
227                     V_INT(&vValue) = j;
228                     if(SUCCEEDED(hr))
229                         hr = IXMLDOMElement_setAttribute(statisticsElement, bstrIndex, vValue);
230
231                     if(SUCCEEDED(hr))
232                     {
233                         V_VT(&vValue) = VT_BSTR;
234                         V_BSTR(&vValue) = SysAllocString(stats->categories[i].stats[j].sName);
235                         if(!V_BSTR(&vValue))
236                             hr = E_OUTOFMEMORY;
237                     }
238
239                     if(SUCCEEDED(hr))
240                     {
241                         TRACE("    storing statistic %d: name: %s\n", j, debugstr_w(V_BSTR(&vValue)));
242                         hr = IXMLDOMElement_setAttribute(statisticsElement, bstrName, vValue);
243                         SysFreeString(V_BSTR(&vValue));
244                     }
245
246                     if(SUCCEEDED(hr))
247                     {
248                         V_VT(&vValue) = VT_BSTR;
249                         V_BSTR(&vValue) = SysAllocString(stats->categories[i].stats[j].sValue);
250                         if(!V_BSTR(&vValue))
251                             hr = E_OUTOFMEMORY;
252                     }
253
254                     if(SUCCEEDED(hr))
255                     {
256                         TRACE("    storing statistic %d: name: %s\n", j, debugstr_w(V_BSTR(&vValue)));
257                         hr = IXMLDOMElement_setAttribute(statisticsElement, bstrValue, vValue);
258                         SysFreeString(V_BSTR(&vValue));
259                     }
260
261                     if(SUCCEEDED(hr))
262                         hr = IXMLDOMNode_appendChild(categoryNode, statisticsNode, &statisticsNode);
263
264                     IXMLDOMElement_Release(statisticsElement);
265                     IXMLDOMNode_Release(statisticsNode);
266                 }
267             }
268
269             if(SUCCEEDED(hr))
270                 hr = IXMLDOMElement_appendChild(root, categoryNode, &categoryNode);
271
272             IXMLDOMElement_Release(categoryElement);
273             IXMLDOMNode_Release(categoryNode);
274
275             if(FAILED(hr))
276                 break;
277         }
278
279     if(SUCCEEDED(hr))
280         hr = IXMLDOMDocument_putref_documentElement(document, root);
281
282     IXMLDOMElement_Release(root);
283
284     TRACE("saving game statistics in %s file\n", debugstr_w(stats->sStatsFile));
285     if(SUCCEEDED(hr))
286         hr = GAMEUX_createStatsDirectory(stats->sStatsFile);
287
288     if(SUCCEEDED(hr))
289         hr = IXMLDOMDocument_save(document, vStatsFilePath);
290
291     IXMLDOMDocument_Release(document);
292
293     SysFreeString(bstrValue);
294     SysFreeString(bstrName);
295     SysFreeString(bstrStatistic);
296     SysFreeString(bstrIndex);
297     SysFreeString(bstrCategory);
298     SysFreeString(bstrStatistics);
299     SysFreeString(V_BSTR(&vStatsFilePath));
300     TRACE("ret=0x%x\n", hr);
301     return hr;
302 }
303 /*******************************************************************************
304  * GAMEUX_buildStatisticsFilePath
305  * Creates path to file containing statistics of game with given id.
306  *
307  * Parameters:
308  *  lpApplicationId                         [I]     application id of game,
309  *                                                  as string
310  *  lpStatisticsFile                        [O]     array where path will be
311  *                                                  stored. Its size must be
312  *                                                  at least MAX_PATH
313  */
314 static HRESULT GAMEUX_buildStatisticsFilePath(
315         LPCWSTR lpApplicationId,
316         LPWSTR lpStatisticsFile)
317 {
318     static const WCHAR sBackslash[] = {'\\',0};
319     static const WCHAR sStatisticsDir[] = {'\\','M','i','c','r','o','s','o','f','t',
320             '\\','W','i','n','d','o','w','s','\\','G','a','m','e','E','x','p',
321             'l','o','r','e','r','\\','G','a','m','e','S','t','a','t','i','s',
322             't','i','c','s','\\',0};
323     static const WCHAR sDotGamestats[] = {'.','g','a','m','e','s','t','a','t','s',0};
324
325     HRESULT hr;
326
327     hr = SHGetFolderPathW(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, lpStatisticsFile);
328
329     if(SUCCEEDED(hr))
330     {
331         lstrcatW(lpStatisticsFile, sStatisticsDir);
332         lstrcatW(lpStatisticsFile, lpApplicationId);
333         lstrcatW(lpStatisticsFile, sBackslash);
334         lstrcatW(lpStatisticsFile, lpApplicationId);
335         lstrcatW(lpStatisticsFile, sDotGamestats);
336     }
337
338     return hr;
339 }
340 /*******************************************************************************
341  * GAMEUX_getAppIdFromGDFPath
342  *
343  * Loads application identifier associated with given GDF binary.
344  * Routine reads identifier from registry, so will fail if game
345  * is not registered.
346  *
347  * Parameters:
348  *  GDFBinaryPath                       [I]     path to gdf binary
349  *  lpApplicationId                     [O]     place to store application id.
350  *                                              must be at least 49 characters
351  *                                              to store guid and termination 0
352  */
353 static HRESULT GAMEUX_getAppIdFromGDFPath(
354         LPCWSTR GDFBinaryPath,
355         LPWSTR lpApplicationId)
356 {
357     static const WCHAR sApplicationId[] =
358             {'A','p','p','l','i','c','a','t','i','o','n','I','d',0};
359
360     HRESULT hr;
361     GAME_INSTALL_SCOPE installScope;
362     GUID instanceId;
363     LPWSTR lpRegistryPath = NULL;
364     HKEY hKey;
365     DWORD dwLength = 49*sizeof(WCHAR);/* place for GUID */
366
367     TRACE("(%s, %p)\n", debugstr_w(GDFBinaryPath), lpApplicationId);
368
369     if(!GDFBinaryPath)
370         return E_INVALIDARG;
371
372     installScope = GIS_CURRENT_USER;
373     hr = GAMEUX_FindGameInstanceId(GDFBinaryPath, installScope, &instanceId);
374
375     if(hr == S_FALSE)
376     {
377         installScope = GIS_ALL_USERS;
378         hr = GAMEUX_FindGameInstanceId(GDFBinaryPath, installScope, &instanceId);
379     }
380
381     if(hr == S_FALSE)
382         /* game not registered, so statistics cannot be used */
383         hr = E_FAIL;
384
385     if(SUCCEEDED(hr))
386         /* game is registered, let's read it's application id from registry */
387         hr = GAMEUX_buildGameRegistryPath(installScope, &instanceId, &lpRegistryPath);
388
389     if(SUCCEEDED(hr)) {
390         hr = HRESULT_FROM_WIN32(RegOpenKeyExW(HKEY_LOCAL_MACHINE,
391                 lpRegistryPath, 0, KEY_READ | KEY_WOW64_64KEY, &hKey));
392         if(SUCCEEDED(hr)) {
393             hr = HRESULT_FROM_WIN32(RegGetValueW(hKey,
394                     NULL, sApplicationId, RRF_RT_REG_SZ,
395                     NULL, lpApplicationId, &dwLength));
396             RegCloseKey(hKey);
397         }
398     }
399
400     HeapFree(GetProcessHeap(), 0, lpRegistryPath);
401
402     TRACE("found app id: %s, return: %#x\n", debugstr_w(lpApplicationId), hr);
403     return hr;
404 }
405 /*******************************************************************
406  * GAMEUX_loadGameStatisticsFromFile
407  * Helper function, loads game statistics from file and stores them
408  * in the structure.
409  *
410  * Parameters:
411  *  data                [I/O]   structure containing file name to
412  *                              load and data fields to store data in
413  */
414 static HRESULT GAMEUX_loadStatisticsFromFile(struct GAMEUX_STATS *data)
415 {
416     static const WCHAR sStatistics[] = {'S','t','a','t','i','s','t','i','c','s',0};
417     static const WCHAR sCategory[] = {'C','a','t','e','g','o','r','y',0};
418     static const WCHAR sIndex[] = {'I','n','d','e','x',0};
419     static const WCHAR sStatistic[] = {'S','t','a','t','i','s','t','i','c',0};
420     static const WCHAR sName[] = {'N','a','m','e',0};
421     static const WCHAR sValue[] = {'V','a','l','u','e',0};
422
423     HRESULT hr = S_OK;
424     IXMLDOMDocument *document = NULL;
425     IXMLDOMElement *root = NULL, *categoryElement, *statisticElement;
426     IXMLDOMNode *categoryNode, *statisticNode;
427     IXMLDOMNodeList *rootChildren = NULL, *categoryChildren;
428     VARIANT vStatsFilePath, vValue;
429     BSTR bstrStatistics = NULL, bstrCategory = NULL, bstrIndex = NULL,
430         bstrStatistic = NULL, bstrName = NULL, bstrValue = NULL;
431     VARIANT_BOOL isSuccessful =  VARIANT_FALSE;
432     int i, j;
433
434     TRACE("(%p)\n", data);
435
436     V_VT(&vStatsFilePath) = VT_BSTR;
437     V_BSTR(&vStatsFilePath) = SysAllocString(data->sStatsFile);
438     if(!V_BSTR(&vStatsFilePath))
439         hr = E_OUTOFMEMORY;
440
441     if(SUCCEEDED(hr))
442         hr = CoCreateInstance(&CLSID_DOMDocument30, NULL, CLSCTX_INPROC_SERVER, &IID_IXMLDOMDocument, (void**)&document);
443
444     if(SUCCEEDED(hr))
445     {
446         bstrStatistics = SysAllocString(sStatistics);
447         if(!bstrStatistics)
448             hr = E_OUTOFMEMORY;
449     }
450
451     if(SUCCEEDED(hr))
452     {
453         bstrCategory = SysAllocString(sCategory);
454         if(!bstrCategory)
455             hr = E_OUTOFMEMORY;
456     }
457
458     if(SUCCEEDED(hr))
459     {
460         bstrIndex = SysAllocString(sIndex);
461         if(!bstrIndex)
462             hr = E_OUTOFMEMORY;
463     }
464
465     if(SUCCEEDED(hr))
466     {
467         bstrStatistic = SysAllocString(sStatistic);
468         if(!bstrStatistic)
469             hr = E_OUTOFMEMORY;
470     }
471
472     if(SUCCEEDED(hr))
473     {
474         bstrName = SysAllocString(sName);
475         if(!bstrName)
476             hr = E_OUTOFMEMORY;
477     }
478
479     if(SUCCEEDED(hr))
480     {
481         bstrValue = SysAllocString(sValue);
482         if(!bstrValue)
483             hr = E_OUTOFMEMORY;
484     }
485
486     if(SUCCEEDED(hr))
487         hr = IXMLDOMDocument_load(document, vStatsFilePath, &isSuccessful);
488
489     if(hr == S_OK && isSuccessful != VARIANT_TRUE)
490         hr = S_FALSE;
491
492     if( hr == S_OK )
493         hr = IXMLDOMDocument_get_documentElement(document, &root);
494
495     if(hr == S_OK)
496         hr = IXMLDOMElement_get_childNodes(root, &rootChildren);
497
498     if(hr == S_OK)
499     {
500         hr = S_OK;
501         while(hr == S_OK)
502         {
503             hr = IXMLDOMNodeList_nextNode(rootChildren, &categoryNode);
504
505             if(hr == S_OK)
506             {
507                 hr = IXMLDOMNode_QueryInterface(categoryNode, &IID_IXMLDOMElement, (LPVOID*)&categoryElement);
508
509                 if(SUCCEEDED(hr))
510                 {
511                     hr = IXMLDOMElement_getAttribute(categoryElement, bstrIndex, &vValue);
512                     if( hr == S_OK && V_VT(&vValue) != VT_BSTR)
513                         hr = E_FAIL;
514
515                     if(SUCCEEDED(hr))
516                     {
517                         i = StrToIntW(V_BSTR(&vValue));
518                         hr = IXMLDOMElement_getAttribute(categoryElement, bstrName, &vValue);
519                         if( hr == S_OK && V_VT(&vValue) != VT_BSTR)
520                             hr = E_FAIL;
521                     }
522
523                     if(SUCCEEDED(hr))
524                     {
525                         lstrcpynW(data->categories[i].sName, V_BSTR(&vValue), MAX_CATEGORY_LENGTH);
526                         TRACE("category %d name %s\n", i, debugstr_w(data->categories[i].sName));
527                         hr = IXMLDOMElement_get_childNodes(categoryElement, &categoryChildren);
528                     }
529
530                     if(SUCCEEDED(hr))
531                     {
532                         hr = S_OK;
533                         while(hr == S_OK)
534                         {
535                             hr = IXMLDOMNodeList_nextNode(categoryChildren, &statisticNode);
536
537                             if(hr == S_OK)
538                             {
539                                 hr = IXMLDOMNode_QueryInterface(statisticNode, &IID_IXMLDOMElement, (LPVOID*)&statisticElement);
540
541                                 if(SUCCEEDED(hr))
542                                 {
543                                     hr = IXMLDOMElement_getAttribute(statisticElement, bstrIndex, &vValue);
544                                     if( hr == S_OK && V_VT(&vValue) != VT_BSTR)
545                                         hr = E_FAIL;
546
547                                     if(SUCCEEDED(hr))
548                                     {
549                                         j = StrToIntW(V_BSTR(&vValue));
550                                         hr = IXMLDOMElement_getAttribute(statisticElement, bstrName, &vValue);
551                                         if( hr == S_OK && V_VT(&vValue) != VT_BSTR)
552                                             hr = E_FAIL;
553                                     }
554
555                                     if(SUCCEEDED(hr))
556                                     {
557                                         lstrcpynW(data->categories[i].stats[j].sName, V_BSTR(&vValue), MAX_NAME_LENGTH);
558                                         hr = IXMLDOMElement_getAttribute(statisticElement, bstrValue, &vValue);
559                                         if( hr == S_OK && V_VT(&vValue) != VT_BSTR)
560                                             hr = E_FAIL;
561                                     }
562
563                                     if(SUCCEEDED(hr))
564                                     {
565                                         lstrcpynW(data->categories[i].stats[j].sValue, V_BSTR(&vValue), MAX_VALUE_LENGTH);
566                                         TRACE("statistic %d name %s value %s\n", j,
567                                               debugstr_w(data->categories[i].stats[j].sName),
568                                               debugstr_w(data->categories[i].stats[j].sValue));
569                                     }
570                                     IXMLDOMElement_Release(statisticElement);
571                                 }
572
573                                 IXMLDOMNode_Release(statisticNode);
574                             }
575                         }
576
577                         if(SUCCEEDED(hr))
578                             hr = S_OK;
579                     }
580                     IXMLDOMElement_Release(categoryElement);
581                 }
582
583                 IXMLDOMNode_Release(categoryNode);
584             }
585         }
586         if(SUCCEEDED(hr))
587             hr = S_OK;
588     }
589
590     if(rootChildren) IXMLDOMNodeList_Release(rootChildren);
591     if(root) IXMLDOMElement_Release(root);
592     if(document) IXMLDOMDocument_Release(document);
593
594     SysFreeString(bstrValue);
595     SysFreeString(bstrName);
596     SysFreeString(bstrStatistic);
597     SysFreeString(bstrIndex);
598     SysFreeString(bstrCategory);
599     SysFreeString(bstrStatistics);
600     SysFreeString(V_BSTR(&vStatsFilePath));
601     return hr;
602 }
603 /*******************************************************************
604  * GAMEUX_loadGameStatistics
605  *
606  * Helper function which loads game statistics associated with game
607  * into interface's internal structures
608  *
609  * Parameters:
610  *  pStats              [O]     structure which will receive data
611  *  sGameId             [I]     application instance Id, stored as string
612  *                              to avoid additional conversions
613  *  openType            [I]     allowed ways of opening statistics
614  *  pOpenResult         [O]     way used to open statistics
615  *
616  */
617 static HRESULT GAMEUX_loadGameStatistics(struct GAMEUX_STATS *pStats,
618         LPWSTR sGameId,
619         GAMESTATS_OPEN_TYPE openType,
620         GAMESTATS_OPEN_RESULT* pOpenResult)
621 {
622     HRESULT hr;
623     TRACE("(%p, %s, %d, %p)\n", pStats, debugstr_w(sGameId), openType, pOpenResult);
624
625     hr = GAMEUX_buildStatisticsFilePath(sGameId, pStats->sStatsFile);
626
627     hr = GAMEUX_loadStatisticsFromFile(pStats);
628     TRACE("ldstats finished, res: %#x\n", hr);
629     if(hr == S_OK)
630     {
631         *pOpenResult = GAMESTATS_OPEN_OPENED;
632     }
633     else if(hr == S_FALSE && openType == GAMESTATS_OPEN_OPENORCREATE) /* file does not exist */
634     {
635         /* create new statistics, not yet connected with file */
636         ZeroMemory(pStats->categories, sizeof(pStats->categories));
637         *pOpenResult = GAMESTATS_OPEN_CREATED;
638         hr = S_OK;
639     }
640     else
641         hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
642
643     TRACE("openResult=%#x ret=%#x\n", *pOpenResult, hr);
644     return hr;
645 }
646  /*******************************************************************
647  * IGameStatistics implementation
648  */
649 typedef struct _GameStatisticsImpl
650 {
651     IGameStatistics IGameStatistics_iface;
652     LONG ref;
653     struct GAMEUX_STATS stats;
654 } GameStatisticsImpl;
655
656 static inline GameStatisticsImpl *impl_from_IGameStatistics( IGameStatistics *iface )
657 {
658     return CONTAINING_RECORD(iface, GameStatisticsImpl, IGameStatistics_iface);
659 }
660
661 static HRESULT WINAPI GameStatisticsImpl_QueryInterface(
662         IGameStatistics *iface,
663         REFIID riid,
664         void **ppvObject)
665 {
666     GameStatisticsImpl *This = impl_from_IGameStatistics( iface );
667
668     TRACE("%p %s %p\n", This, debugstr_guid( riid ), ppvObject );
669
670     *ppvObject = NULL;
671
672     if ( IsEqualGUID( riid, &IID_IUnknown ) ||
673          IsEqualGUID( riid, &IID_IGameStatistics ) )
674     {
675         *ppvObject = iface;
676     }
677     else
678     {
679         FIXME("interface %s not implemented\n", debugstr_guid(riid));
680         return E_NOINTERFACE;
681     }
682
683     IGameStatistics_AddRef( iface );
684     return S_OK;
685 }
686
687 static ULONG WINAPI GameStatisticsImpl_AddRef(IGameStatistics *iface)
688 {
689     GameStatisticsImpl *This = impl_from_IGameStatistics( iface );
690     LONG ref;
691
692     ref = InterlockedIncrement(&This->ref);
693
694     TRACE("(%p): ref=%d\n", This, ref);
695     return ref;
696 }
697
698 static ULONG WINAPI GameStatisticsImpl_Release(IGameStatistics *iface)
699 {
700     GameStatisticsImpl *This = impl_from_IGameStatistics( iface );
701     LONG ref;
702
703     ref = InterlockedDecrement( &This->ref );
704     TRACE("(%p): ref=%d\n", This, ref);
705
706     if ( ref == 0 )
707     {
708         TRACE("freeing IGameStatistics\n");
709         HeapFree( GetProcessHeap(), 0, This );
710     }
711
712     return ref;
713 }
714
715 static HRESULT WINAPI GameStatisticsImpl_GetMaxCategoryLength(
716     IGameStatistics *iface,
717     UINT *cch)
718 {
719     TRACE("(%p, %p)\n", iface, cch);
720     if(!cch)
721         return E_INVALIDARG;
722
723     *cch = MAX_CATEGORY_LENGTH;
724     return S_OK;
725 }
726
727 static HRESULT WINAPI GameStatisticsImpl_GetMaxNameLength(
728     IGameStatistics *iface,
729     UINT *cch)
730 {
731     TRACE("(%p, %p)\n", iface, cch);
732     if(!cch)
733         return E_INVALIDARG;
734
735     *cch = MAX_NAME_LENGTH;
736     return S_OK;
737 }
738
739 static HRESULT WINAPI GameStatisticsImpl_GetMaxValueLength(
740     IGameStatistics *iface,
741     UINT *cch)
742 {
743     TRACE("(%p, %p)\n", iface, cch);
744     if(!cch)
745         return E_INVALIDARG;
746
747     *cch = MAX_VALUE_LENGTH;
748     return S_OK;
749 }
750
751 static HRESULT WINAPI GameStatisticsImpl_GetMaxCategories(
752     IGameStatistics *iface,
753     WORD *pMax)
754 {
755     TRACE("(%p, %p)\n", iface, pMax);
756     if(!pMax)
757         return E_INVALIDARG;
758
759     *pMax = MAX_CATEGORIES;
760     return S_OK;
761 }
762
763 static HRESULT WINAPI GameStatisticsImpl_GetMaxStatsPerCategory(
764     IGameStatistics *iface,
765     WORD *pMax)
766 {
767     TRACE("(%p, %p)\n", iface, pMax);
768     if(!pMax)
769         return E_INVALIDARG;
770
771     *pMax = MAX_STATS_PER_CATEGORY;
772     return S_OK;
773 }
774
775 static HRESULT WINAPI GameStatisticsImpl_SetCategoryTitle(
776     IGameStatistics *iface,
777     WORD categoryIndex,
778     LPCWSTR title)
779 {
780     HRESULT hr = S_OK;
781     DWORD dwLength;
782     GameStatisticsImpl *This = impl_from_IGameStatistics(iface);
783
784     TRACE("(%p, %d, %s)\n", This, categoryIndex, debugstr_w(title));
785
786     if(!title || categoryIndex >= MAX_CATEGORIES)
787         return E_INVALIDARG;
788
789     dwLength = lstrlenW(title);
790
791     if(dwLength > MAX_CATEGORY_LENGTH)
792     {
793         hr = S_FALSE;
794         dwLength = MAX_CATEGORY_LENGTH;
795     }
796
797     lstrcpynW(This->stats.categories[categoryIndex].sName,
798               title, dwLength+1);
799
800     return hr;
801 }
802
803 static HRESULT WINAPI GameStatisticsImpl_GetCategoryTitle(
804     IGameStatistics *iface,
805     WORD categoryIndex,
806     LPWSTR *pTitle)
807 {
808     HRESULT hr = S_OK;
809     LONG nLength;
810     GameStatisticsImpl *This = impl_from_IGameStatistics(iface);
811
812     TRACE("%p, %d, %p\n", This, categoryIndex, pTitle);
813
814     if(!pTitle)
815         return E_INVALIDARG;
816     *pTitle = NULL;
817
818     if (categoryIndex >= MAX_CATEGORIES)
819         hr = E_INVALIDARG;
820
821     if(SUCCEEDED(hr))
822     {
823         nLength = lstrlenW(This->stats.categories[categoryIndex].sName);
824         if(nLength != 0)
825         {
826             *pTitle = CoTaskMemAlloc(sizeof(WCHAR)*(nLength+1));
827             lstrcpyW(*pTitle, This->stats.categories[categoryIndex].sName);
828         }
829     }
830
831     return hr;
832 }
833
834 static HRESULT WINAPI GameStatisticsImpl_GetStatistic(
835     IGameStatistics *iface,
836     WORD categoryIndex,
837     WORD statIndex,
838     LPWSTR *pName,
839     LPWSTR *pValue)
840 {
841     HRESULT hr = S_OK;
842     LONG nLength;
843     GameStatisticsImpl *This = impl_from_IGameStatistics(iface);
844
845     TRACE("%p, %d,%d, %p, %p\n", This, categoryIndex, statIndex, pName, pValue);
846
847     if(!pName || !pValue)
848         return E_INVALIDARG;
849
850     *pName = NULL;
851     *pValue = NULL;
852
853     if(categoryIndex >= MAX_CATEGORIES || statIndex >= MAX_STATS_PER_CATEGORY)
854         hr = E_INVALIDARG;
855
856     if(SUCCEEDED(hr))
857     {
858         nLength = lstrlenW(This->stats.categories[categoryIndex].stats[statIndex].sName);
859         if(nLength != 0)
860         {
861             *pName = CoTaskMemAlloc(sizeof(WCHAR)*(nLength+1));
862             if(!(*pName))
863                 hr = E_OUTOFMEMORY;
864             else
865                 lstrcpyW(*pName, This->stats.categories[categoryIndex].stats[statIndex].sName);
866         }
867     }
868
869     if(SUCCEEDED(hr))
870     {
871         nLength = lstrlenW(This->stats.categories[categoryIndex].stats[statIndex].sValue);
872         if(nLength != 0)
873         {
874             *pValue = CoTaskMemAlloc(sizeof(WCHAR)*(nLength+1));
875             if(!(*pValue))
876                 hr = E_OUTOFMEMORY;
877             else
878                 lstrcpyW(*pValue, This->stats.categories[categoryIndex].stats[statIndex].sValue);
879         }
880     }
881
882     TRACE("returning pair; %s => %s\n", debugstr_w(*pName), debugstr_w(*pValue));
883     return hr;
884 }
885
886 static HRESULT WINAPI GameStatisticsImpl_SetStatistic(
887     IGameStatistics *iface,
888     WORD categoryIndex,
889     WORD statIndex,
890     LPCWSTR name,
891     LPCWSTR value)
892 {
893     HRESULT hr = S_OK;
894     DWORD dwNameLen, dwValueLen;
895     GameStatisticsImpl *This = impl_from_IGameStatistics(iface);
896
897     TRACE("(%p, %d, %d, %s, %s)\n", This, categoryIndex, statIndex,
898           debugstr_w(name), debugstr_w(value));
899
900     if(!name)
901         return S_FALSE;
902
903     if(categoryIndex >= MAX_CATEGORIES || statIndex >= MAX_STATS_PER_CATEGORY)
904         return E_INVALIDARG;
905
906     dwNameLen = lstrlenW(name);
907
908     if(dwNameLen > MAX_NAME_LENGTH)
909     {
910         hr = S_FALSE;
911         dwNameLen = MAX_NAME_LENGTH;
912     }
913
914     lstrcpynW(This->stats.categories[categoryIndex].stats[statIndex].sName,
915               name, dwNameLen+1);
916
917     if(value)
918     {
919         dwValueLen = lstrlenW(value);
920
921         if(dwValueLen > MAX_VALUE_LENGTH)
922         {
923             hr = S_FALSE;
924             dwValueLen = MAX_VALUE_LENGTH;
925         }
926
927         lstrcpynW(This->stats.categories[categoryIndex].stats[statIndex].sValue,
928                   value, dwValueLen+1);
929     }
930     else
931         /* Windows allows passing NULL as value */
932         This->stats.categories[categoryIndex].stats[statIndex].sValue[0] = 0;
933
934     return hr;
935 }
936
937 static HRESULT WINAPI GameStatisticsImpl_Save(
938     IGameStatistics *iface,
939     BOOL trackChanges)
940 {
941     GameStatisticsImpl *This = impl_from_IGameStatistics(iface);
942     HRESULT hr = S_OK;
943
944     TRACE("(%p, %d)\n", This, trackChanges);
945
946     if(trackChanges)
947         FIXME("tracking changes not yet implemented\n");
948
949     hr = GAMEUX_updateStatisticsFile(&This->stats);
950
951     return hr;
952 }
953
954 static HRESULT WINAPI GameStatisticsImpl_SetLastPlayedCategory(
955     IGameStatistics *iface,
956     UINT categoryIndex)
957 {
958     FIXME("stub\n");
959     return E_NOTIMPL;
960 }
961
962 static HRESULT WINAPI GameStatisticsImpl_GetLastPlayedCategory(
963     IGameStatistics *iface,
964     UINT *pCategoryIndex)
965 {
966     FIXME("stub\n");
967     return E_NOTIMPL;
968 }
969
970 static const struct IGameStatisticsVtbl GameStatisticsImplVtbl =
971 {
972     GameStatisticsImpl_QueryInterface,
973     GameStatisticsImpl_AddRef,
974     GameStatisticsImpl_Release,
975     GameStatisticsImpl_GetMaxCategoryLength,
976     GameStatisticsImpl_GetMaxNameLength,
977     GameStatisticsImpl_GetMaxValueLength,
978     GameStatisticsImpl_GetMaxCategories,
979     GameStatisticsImpl_GetMaxStatsPerCategory,
980     GameStatisticsImpl_SetCategoryTitle,
981     GameStatisticsImpl_GetCategoryTitle,
982     GameStatisticsImpl_GetStatistic,
983     GameStatisticsImpl_SetStatistic,
984     GameStatisticsImpl_Save,
985     GameStatisticsImpl_SetLastPlayedCategory,
986     GameStatisticsImpl_GetLastPlayedCategory
987 };
988
989
990 static HRESULT create_IGameStatistics(GameStatisticsImpl** ppStats)
991 {
992     TRACE("(%p)\n", ppStats);
993
994     *ppStats = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(**ppStats));
995     if(!(*ppStats))
996         return E_OUTOFMEMORY;
997
998     (*ppStats)->IGameStatistics_iface.lpVtbl = &GameStatisticsImplVtbl;
999     (*ppStats)->ref = 1;
1000
1001     TRACE("returning coclass: %p\n", *ppStats);
1002     return S_OK;
1003 }
1004
1005 /*******************************************************************************
1006  * IGameStatisticsMgr implementation
1007  */
1008 typedef struct _GameStatisticsMgrImpl
1009 {
1010     IGameStatisticsMgr IGameStatisticsMgr_iface;
1011     LONG ref;
1012 } GameStatisticsMgrImpl;
1013
1014 static inline GameStatisticsMgrImpl *impl_from_IGameStatisticsMgr( IGameStatisticsMgr *iface )
1015 {
1016     return CONTAINING_RECORD(iface, GameStatisticsMgrImpl, IGameStatisticsMgr_iface);
1017 }
1018
1019
1020 static HRESULT WINAPI GameStatisticsMgrImpl_QueryInterface(
1021         IGameStatisticsMgr *iface,
1022         REFIID riid,
1023         void **ppvObject)
1024 {
1025     GameStatisticsMgrImpl *This = impl_from_IGameStatisticsMgr( iface );
1026
1027     TRACE("%p %s %p\n", This, debugstr_guid( riid ), ppvObject );
1028
1029     *ppvObject = NULL;
1030
1031     if(IsEqualGUID(riid, &IID_IUnknown) ||
1032        IsEqualGUID(riid, &IID_IGameStatisticsMgr) )
1033     {
1034         *ppvObject = iface;
1035     }
1036     else
1037     {
1038         FIXME("interface %s not implemented\n", debugstr_guid(riid));
1039         return E_NOINTERFACE;
1040     }
1041
1042     IGameStatisticsMgr_AddRef( iface );
1043     return S_OK;
1044 }
1045
1046 static ULONG WINAPI GameStatisticsMgrImpl_AddRef(IGameStatisticsMgr *iface)
1047 {
1048     GameStatisticsMgrImpl *This = impl_from_IGameStatisticsMgr( iface );
1049     LONG ref;
1050
1051     ref = InterlockedIncrement(&This->ref);
1052
1053     TRACE("(%p): ref=%d\n", This, ref);
1054     return ref;
1055 }
1056
1057 static ULONG WINAPI GameStatisticsMgrImpl_Release(IGameStatisticsMgr *iface)
1058 {
1059     GameStatisticsMgrImpl *This = impl_from_IGameStatisticsMgr( iface );
1060     LONG ref;
1061
1062     ref = InterlockedDecrement(&This->ref);
1063     TRACE("(%p): ref=%d\n", This, ref);
1064
1065     if ( ref == 0 )
1066     {
1067         TRACE("freeing GameStatistics object\n");
1068         HeapFree( GetProcessHeap(), 0, This);
1069     }
1070
1071     return ref;
1072 }
1073
1074 static HRESULT STDMETHODCALLTYPE GameStatisticsMgrImpl_GetGameStatistics(
1075         IGameStatisticsMgr* iface,
1076         LPCWSTR GDFBinaryPath,
1077         GAMESTATS_OPEN_TYPE openType,
1078         GAMESTATS_OPEN_RESULT *pOpenResult,
1079         IGameStatistics **ppiStats)
1080 {
1081     HRESULT hr;
1082     WCHAR lpApplicationId[49];
1083     GameStatisticsImpl *statisticsImpl = NULL;
1084     IGameStatistics *output_iface;
1085
1086     TRACE("(%p, %s, 0x%x, %p, %p)\n", iface, debugstr_w(GDFBinaryPath), openType, pOpenResult, ppiStats);
1087
1088     hr = GAMEUX_getAppIdFromGDFPath(GDFBinaryPath, lpApplicationId);
1089
1090     if(SUCCEEDED(hr))
1091         hr = create_IGameStatistics(&statisticsImpl);
1092
1093     if(SUCCEEDED(hr))
1094     {
1095         output_iface = &statisticsImpl->IGameStatistics_iface;
1096         hr = GAMEUX_buildStatisticsFilePath(lpApplicationId, statisticsImpl->stats.sStatsFile);
1097     }
1098
1099     if(SUCCEEDED(hr))
1100         hr = GAMEUX_loadGameStatistics(&statisticsImpl->stats, lpApplicationId, openType, pOpenResult);
1101
1102     if(SUCCEEDED(hr))
1103         *ppiStats = output_iface;
1104     else
1105     {
1106         HeapFree(GetProcessHeap(), 0, statisticsImpl);
1107         *ppiStats = NULL;
1108     }
1109
1110     return hr;
1111 }
1112
1113 static HRESULT STDMETHODCALLTYPE GameStatisticsMgrImpl_RemoveGameStatistics(
1114         IGameStatisticsMgr* iface,
1115         LPCWSTR GDFBinaryPath)
1116 {
1117     HRESULT hr;
1118     WCHAR lpApplicationId[49];
1119     WCHAR sStatsFile[MAX_PATH];
1120
1121     TRACE("(%p, %s)\n", iface, debugstr_w(GDFBinaryPath));
1122
1123     hr = GAMEUX_getAppIdFromGDFPath(GDFBinaryPath, lpApplicationId);
1124
1125     if(SUCCEEDED(hr))
1126         hr = GAMEUX_buildStatisticsFilePath(lpApplicationId, sStatsFile);
1127
1128     if(SUCCEEDED(hr))
1129         hr = (DeleteFileW(sStatsFile)==TRUE ? S_OK : HRESULT_FROM_WIN32(GetLastError()));
1130
1131     return hr;
1132 }
1133
1134 static const struct IGameStatisticsMgrVtbl GameStatisticsMgrImplVtbl =
1135 {
1136     GameStatisticsMgrImpl_QueryInterface,
1137     GameStatisticsMgrImpl_AddRef,
1138     GameStatisticsMgrImpl_Release,
1139     GameStatisticsMgrImpl_GetGameStatistics,
1140     GameStatisticsMgrImpl_RemoveGameStatistics,
1141 };
1142
1143 HRESULT GameStatistics_create(
1144         IUnknown *pUnkOuter,
1145         IUnknown **ppObj)
1146 {
1147     GameStatisticsMgrImpl *pGameStatistics;
1148
1149     TRACE("(%p, %p)\n", pUnkOuter, ppObj);
1150
1151     pGameStatistics = HeapAlloc( GetProcessHeap(), 0, sizeof (*pGameStatistics) );
1152
1153     if( !pGameStatistics )
1154         return E_OUTOFMEMORY;
1155
1156     pGameStatistics->IGameStatisticsMgr_iface.lpVtbl = &GameStatisticsMgrImplVtbl;
1157     pGameStatistics->ref = 1;
1158
1159     *ppObj = (IUnknown*)&pGameStatistics->IGameStatisticsMgr_iface;
1160
1161     TRACE("returning iface %p\n", *ppObj);
1162     return S_OK;
1163 }