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