dxdiag: Remove dead increment (Clang).
[wine] / programs / dxdiag / output.c
1 /*
2  * DxDiag file information output
3  *
4  * Copyright 2011 Andrew Nguyen
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
21 #define COBJMACROS
22 #include <initguid.h>
23 #include <windows.h>
24 #include <msxml2.h>
25 #include <assert.h>
26 #include <stdio.h>
27
28 #include "wine/debug.h"
29 #include "wine/unicode.h"
30
31 #include "dxdiag_private.h"
32
33 WINE_DEFAULT_DEBUG_CHANNEL(dxdiag);
34
35 static char output_buffer[1024];
36 static const char crlf[2] = "\r\n";
37
38 static const WCHAR DxDiag[] = {'D','x','D','i','a','g',0};
39
40 static const WCHAR SystemInformation[] = {'S','y','s','t','e','m','I','n','f','o','r','m','a','t','i','o','n',0};
41 static const WCHAR Time[] = {'T','i','m','e',0};
42 static const WCHAR MachineName[] = {'M','a','c','h','i','n','e','N','a','m','e',0};
43 static const WCHAR OperatingSystem[] = {'O','p','e','r','a','t','i','n','g','S','y','s','t','e','m',0};
44 static const WCHAR Language[] = {'L','a','n','g','u','a','g','e',0};
45 static const WCHAR SystemManufacturer[] = {'S','y','s','t','e','m','M','a','n','u','f','a','c','t','u','r','e','r',0};
46 static const WCHAR SystemModel[] = {'S','y','s','t','e','m','M','o','d','e','l',0};
47 static const WCHAR BIOS[] = {'B','I','O','S',0};
48 static const WCHAR Processor[] = {'P','r','o','c','e','s','s','o','r',0};
49 static const WCHAR Memory[] = {'M','e','m','o','r','y',0};
50 static const WCHAR PageFile[] = {'P','a','g','e','F','i','l','e',0};
51 static const WCHAR WindowsDir[] = {'W','i','n','d','o','w','s','D','i','r',0};
52 static const WCHAR DirectXVersion[] = {'D','i','r','e','c','t','X','V','e','r','s','i','o','n',0};
53 static const WCHAR DXSetupParameters[] = {'D','X','S','e','t','u','p','P','a','r','a','m','e','t','e','r','s',0};
54 static const WCHAR DxDiagVersion[] = {'D','x','D','i','a','g','V','e','r','s','i','o','n',0};
55 static const WCHAR DxDiagUnicode[] = {'D','x','D','i','a','g','U','n','i','c','o','d','e',0};
56 static const WCHAR DxDiag64Bit[] = {'D','x','D','i','a','g','6','4','B','i','t',0};
57
58 struct text_information_field
59 {
60     const char *field_name;
61     const WCHAR *value;
62 };
63
64 struct xml_information_field
65 {
66     const WCHAR *tag_name;
67     const WCHAR *value;
68 };
69
70 static BOOL output_text_header(HANDLE hFile, const char *caption)
71 {
72     DWORD len = strlen(caption);
73     DWORD total_len = 3 * (len + sizeof(crlf));
74     char *ptr = output_buffer;
75
76     assert(total_len <= sizeof(output_buffer));
77
78     memset(ptr, '-', len);
79     ptr += len;
80
81     memcpy(ptr, crlf, sizeof(crlf));
82     ptr += sizeof(crlf);
83
84     memcpy(ptr, caption, len);
85     ptr += len;
86
87     memcpy(ptr, crlf, sizeof(crlf));
88     ptr += sizeof(crlf);
89
90     memset(ptr, '-', len);
91     ptr += len;
92
93     memcpy(ptr, crlf, sizeof(crlf));
94
95     return WriteFile(hFile, output_buffer, total_len, NULL, NULL);
96 }
97
98 static BOOL output_text_field(HANDLE hFile, const char *field_name, DWORD field_width, const WCHAR *value)
99 {
100     DWORD value_lenW = strlenW(value);
101     DWORD value_lenA = WideCharToMultiByte(CP_ACP, 0, value, value_lenW, NULL, 0, NULL, NULL);
102     DWORD total_len = field_width + sizeof(": ") - 1 + value_lenA + sizeof(crlf);
103     char sprintf_fmt[1 + 10 + 3 + 1];
104     char *ptr = output_buffer;
105
106     assert(total_len <= sizeof(output_buffer));
107
108     sprintf(sprintf_fmt, "%%%us: ", field_width);
109     ptr += sprintf(ptr, sprintf_fmt, field_name);
110
111     ptr += WideCharToMultiByte(CP_ACP, 0, value, value_lenW, ptr, value_lenA, NULL, NULL);
112
113     memcpy(ptr, crlf, sizeof(crlf));
114     ptr += sizeof(crlf);
115
116     return WriteFile(hFile, output_buffer, total_len, NULL, NULL);
117 }
118
119 static BOOL output_crlf(HANDLE hFile)
120 {
121     return WriteFile(hFile, crlf, sizeof(crlf), NULL, NULL);
122 }
123
124 static inline void fill_system_text_output_table(struct dxdiag_information *dxdiag_info, struct text_information_field *fields)
125 {
126     fields[0].field_name = "Time of this report";
127     fields[0].value = dxdiag_info->system_info.szTimeEnglish;
128     fields[1].field_name = "Machine name";
129     fields[1].value = dxdiag_info->system_info.szMachineNameEnglish;
130     fields[2].field_name = "Operating System";
131     fields[2].value = dxdiag_info->system_info.szOSExLongEnglish;
132     fields[3].field_name = "Language";
133     fields[3].value = dxdiag_info->system_info.szLanguagesEnglish;
134     fields[4].field_name = "System Manufacturer";
135     fields[4].value = dxdiag_info->system_info.szSystemManufacturerEnglish;
136     fields[5].field_name = "System Model";
137     fields[5].value = dxdiag_info->system_info.szSystemModelEnglish;
138     fields[6].field_name = "BIOS";
139     fields[6].value = dxdiag_info->system_info.szBIOSEnglish;
140     fields[7].field_name = "Processor";
141     fields[7].value = dxdiag_info->system_info.szProcessorEnglish;
142     fields[8].field_name = "Memory";
143     fields[8].value = dxdiag_info->system_info.szPhysicalMemoryEnglish;
144     fields[9].field_name = "Page File";
145     fields[9].value = dxdiag_info->system_info.szPageFileEnglish;
146     fields[10].field_name = "Windows Dir";
147     fields[10].value = dxdiag_info->system_info.szWindowsDir;
148     fields[11].field_name = "DirectX Version";
149     fields[11].value = dxdiag_info->system_info.szDirectXVersionLongEnglish;
150     fields[12].field_name = "DX Setup Parameters";
151     fields[12].value = dxdiag_info->system_info.szSetupParamEnglish;
152     fields[13].field_name = "DxDiag Version";
153     fields[13].value = dxdiag_info->system_info.szDxDiagVersion;
154 }
155
156 static BOOL output_text_information(struct dxdiag_information *dxdiag_info, const WCHAR *filename)
157 {
158     struct information_block
159     {
160         const char *caption;
161         const size_t field_width;
162         struct text_information_field fields[50];
163     } output_table[] =
164     {
165         {"System Information", 19},
166     };
167
168     HANDLE hFile;
169     size_t i;
170
171     fill_system_text_output_table(dxdiag_info, output_table[0].fields);
172
173     hFile = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
174                         NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
175     if (hFile == INVALID_HANDLE_VALUE)
176     {
177         WINE_ERR("File creation failed, last error %u\n", GetLastError());
178         return FALSE;
179     }
180
181     for (i = 0; i < sizeof(output_table)/sizeof(output_table[0]); i++)
182     {
183         const struct text_information_field *fields = output_table[i].fields;
184         unsigned int j;
185
186         output_text_header(hFile, output_table[i].caption);
187         for (j = 0; fields[j].field_name; j++)
188             output_text_field(hFile, fields[j].field_name, output_table[i].field_width, fields[j].value);
189         output_crlf(hFile);
190     }
191
192     CloseHandle(hFile);
193     return FALSE;
194 }
195
196 static IXMLDOMElement *xml_create_element(IXMLDOMDocument *xmldoc, const WCHAR *name)
197 {
198     BSTR bstr = SysAllocString(name);
199     IXMLDOMElement *ret;
200     HRESULT hr;
201
202     if (!bstr)
203         return NULL;
204
205     hr = IXMLDOMDocument_createElement(xmldoc, bstr, &ret);
206     SysFreeString(bstr);
207
208     return SUCCEEDED(hr) ? ret : NULL;
209 }
210
211 static HRESULT xml_put_element_text(IXMLDOMElement *element, const WCHAR *text)
212 {
213     BSTR bstr = SysAllocString(text);
214     HRESULT hr;
215
216     if (!bstr)
217         return E_OUTOFMEMORY;
218
219     hr = IXMLDOMElement_put_text(element, bstr);
220     SysFreeString(bstr);
221
222     return hr;
223 }
224
225 static HRESULT save_xml_document(IXMLDOMDocument *xmldoc, const WCHAR *filename)
226 {
227     BSTR bstr = SysAllocString(filename);
228     VARIANT destVar;
229     HRESULT hr;
230
231     if (!bstr)
232         return E_OUTOFMEMORY;
233
234     V_VT(&destVar) = VT_BSTR;
235     V_BSTR(&destVar) = bstr;
236
237     hr = IXMLDOMDocument_save(xmldoc, destVar);
238     VariantClear(&destVar);
239
240     return hr;
241 }
242
243 static inline void fill_system_xml_output_table(struct dxdiag_information *dxdiag_info, struct xml_information_field *fields)
244 {
245     static const WCHAR zeroW[] = {'0',0};
246     static const WCHAR oneW[] = {'1',0};
247
248     fields[0].tag_name = Time;
249     fields[0].value = dxdiag_info->system_info.szTimeEnglish;
250     fields[1].tag_name = MachineName;
251     fields[1].value = dxdiag_info->system_info.szMachineNameEnglish;
252     fields[2].tag_name = OperatingSystem;
253     fields[2].value = dxdiag_info->system_info.szOSExLongEnglish;
254     fields[3].tag_name = Language;
255     fields[3].value = dxdiag_info->system_info.szLanguagesEnglish;
256     fields[4].tag_name = SystemManufacturer;
257     fields[4].value = dxdiag_info->system_info.szSystemManufacturerEnglish;
258     fields[5].tag_name = SystemModel;
259     fields[5].value = dxdiag_info->system_info.szSystemModelEnglish;
260     fields[6].tag_name = BIOS;
261     fields[6].value = dxdiag_info->system_info.szBIOSEnglish;
262     fields[7].tag_name = Processor;
263     fields[7].value = dxdiag_info->system_info.szProcessorEnglish;
264     fields[8].tag_name = Memory;
265     fields[8].value = dxdiag_info->system_info.szPhysicalMemoryEnglish;
266     fields[9].tag_name = PageFile;
267     fields[9].value = dxdiag_info->system_info.szPageFileEnglish;
268     fields[10].tag_name = WindowsDir;
269     fields[10].value = dxdiag_info->system_info.szWindowsDir;
270     fields[11].tag_name = DirectXVersion;
271     fields[11].value = dxdiag_info->system_info.szDirectXVersionLongEnglish;
272     fields[12].tag_name = DXSetupParameters;
273     fields[12].value = dxdiag_info->system_info.szSetupParamEnglish;
274     fields[13].tag_name = DxDiagVersion;
275     fields[13].value = dxdiag_info->system_info.szDxDiagVersion;
276     fields[14].tag_name = DxDiagUnicode;
277     fields[14].value = oneW;
278     fields[15].tag_name = DxDiag64Bit;
279     fields[15].value = dxdiag_info->system_info.win64 ? oneW : zeroW;
280 }
281
282 static BOOL output_xml_information(struct dxdiag_information *dxdiag_info, const WCHAR *filename)
283 {
284     struct information_block
285     {
286         const WCHAR *tag_name;
287         struct xml_information_field fields[50];
288     } output_table[] =
289     {
290         {SystemInformation},
291     };
292
293     IXMLDOMDocument *xmldoc = NULL;
294     IXMLDOMElement *dxdiag_element = NULL;
295     HRESULT hr;
296     size_t i;
297
298     fill_system_xml_output_table(dxdiag_info, output_table[0].fields);
299
300     hr = CoCreateInstance(&CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER,
301                           &IID_IXMLDOMDocument, (void **)&xmldoc);
302     if (FAILED(hr))
303     {
304         WINE_ERR("IXMLDOMDocument instance creation failed with 0x%08x\n", hr);
305         goto error;
306     }
307
308     if (!(dxdiag_element = xml_create_element(xmldoc, DxDiag)))
309         goto error;
310
311     hr = IXMLDOMDocument_appendChild(xmldoc, (IXMLDOMNode *)dxdiag_element, NULL);
312     if (FAILED(hr))
313         goto error;
314
315     for (i = 0; i < sizeof(output_table)/sizeof(output_table[0]); i++)
316     {
317         IXMLDOMElement *info_element = xml_create_element(xmldoc, output_table[i].tag_name);
318         const struct xml_information_field *fields = output_table[i].fields;
319         unsigned int j = 0;
320
321         if (!info_element)
322             goto error;
323
324         hr = IXMLDOMElement_appendChild(dxdiag_element, (IXMLDOMNode *)info_element, NULL);
325         if (FAILED(hr))
326         {
327             IXMLDOMElement_Release(info_element);
328             goto error;
329         }
330
331         for (j = 0; fields[j].tag_name; j++)
332         {
333             IXMLDOMElement *field_element = xml_create_element(xmldoc, fields[j].tag_name);
334
335             if (!field_element)
336             {
337                 IXMLDOMElement_Release(info_element);
338                 goto error;
339             }
340
341             hr = xml_put_element_text(field_element, fields[j].value);
342             if (FAILED(hr))
343             {
344                 IXMLDOMElement_Release(field_element);
345                 IXMLDOMElement_Release(info_element);
346                 goto error;
347             }
348
349             hr = IXMLDOMElement_appendChild(info_element, (IXMLDOMNode *)field_element, NULL);
350             if (FAILED(hr))
351             {
352                 IXMLDOMElement_Release(field_element);
353                 IXMLDOMElement_Release(info_element);
354                 goto error;
355             }
356
357             IXMLDOMElement_Release(field_element);
358         }
359
360         IXMLDOMElement_Release(info_element);
361     }
362
363     hr = save_xml_document(xmldoc, filename);
364     if (FAILED(hr))
365         goto error;
366
367     IXMLDOMElement_Release(dxdiag_element);
368     IXMLDOMDocument_Release(xmldoc);
369     return TRUE;
370 error:
371     if (dxdiag_element) IXMLDOMElement_Release(dxdiag_element);
372     if (xmldoc) IXMLDOMDocument_Release(xmldoc);
373     return FALSE;
374 }
375
376 static struct output_backend
377 {
378     const WCHAR filename_ext[5];
379     BOOL (*output_handler)(struct dxdiag_information *, const WCHAR *filename);
380 } output_backends[] =
381 {
382     /* OUTPUT_TEXT */
383     {
384         {'.','t','x','t',0},
385         output_text_information,
386     },
387     /* OUTPUT_XML */
388     {
389         {'.','x','m','l',0},
390         output_xml_information,
391     },
392 };
393
394 const WCHAR *get_output_extension(enum output_type type)
395 {
396     assert(type > OUTPUT_NONE && type <= sizeof(output_backends)/sizeof(output_backends[0]));
397
398     return output_backends[type - 1].filename_ext;
399 }
400
401 BOOL output_dxdiag_information(struct dxdiag_information *dxdiag_info, const WCHAR *filename, enum output_type type)
402 {
403     assert(type > OUTPUT_NONE && type <= sizeof(output_backends)/sizeof(output_backends[0]));
404
405     return output_backends[type - 1].output_handler(dxdiag_info, filename);
406 }