5aeb627edc517f02f344348b743144a288139abd
[reactos.git] / dll / cpl / sysdm / smbios.c
1 /*
2 * PROJECT: ReactOS System Control Panel Applet
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: dll/cpl/sysdm/smbios.c
5 * PURPOSE: Retrieve device or motherboard name identifier from DMI/SMBIOS
6 * COPYRIGHT: Copyright 2018 Stanislav Motylkov <x86corez@gmail.com>
7 *
8 */
9
10 #include "precomp.h"
11
12 #include <strsafe.h>
13 #include <udmihelp.h>
14 #include <dmilib.h>
15
16 typedef struct GENERIC_NAME
17 {
18 PCWSTR pwName;
19 BOOL bCaseSensitive;
20 } GENERIC_NAME;
21
22 typedef struct VENDOR_LONG_NAME
23 {
24 PCWSTR pwLongName;
25 PCWSTR pwShortName;
26 } VENDOR_LONG_NAME;
27
28 typedef struct REDUNDANT_WORD
29 {
30 PCWSTR pwStr;
31 BOOL bReplaceFirstWord;
32 } REDUNDANT_WORD;
33
34 static
35 BOOL
36 IsPunctuation(
37 _In_ WCHAR chr)
38 {
39 return (chr <= L' ' || chr == L'.' || chr == L',');
40 }
41
42 /*
43 * Trim redundant characters
44 */
45 static
46 VOID
47 TrimPunctuation(
48 _Inout_ PWSTR pStr)
49 {
50 SIZE_T Length;
51 UINT i = 0;
52
53 if (!pStr)
54 return;
55
56 Length = wcslen(pStr);
57 if (Length == 0)
58 return;
59
60 /* Trim leading characters */
61 while (i < Length && IsPunctuation(pStr[i]))
62 {
63 i++;
64 }
65
66 if (i > 0)
67 {
68 Length -= i;
69 memmove(pStr, pStr + i, (Length + 1) * sizeof(WCHAR));
70 }
71
72 /* Trim trailing characters */
73 while (Length && IsPunctuation(pStr[Length-1]))
74 {
75 pStr[Length-1] = L'\0';
76 --Length;
77 }
78 }
79
80 /*
81 * Case insensitive variant of wcsstr
82 */
83 static
84 wchar_t * wcsistr(const wchar_t *s, const wchar_t *b)
85 {
86 wchar_t *x;
87 wchar_t *y;
88 wchar_t *c;
89 x = (wchar_t *)s;
90 while (*x)
91 {
92 if (towlower(*x) == towlower(*b))
93 {
94 y = x;
95 c = (wchar_t *)b;
96 while (*y && *c && towlower(*y) == towlower(*c))
97 {
98 c++;
99 y++;
100 }
101 if (!*c)
102 return x;
103 }
104 x++;
105 }
106 return NULL;
107 }
108
109 static
110 wchar_t * wcsistr_plus(const wchar_t *s, wchar_t *b)
111 {
112 wchar_t * result = wcsistr(s, b);
113 UINT len = wcslen(b);
114 // workarounds
115 if (!result && b[len - 1] == L' ' && wcschr(s, L',') != NULL)
116 {
117 b[len - 1] = L',';
118 result = wcsistr(s, b);
119 b[len - 1] = L' ';
120 if (!result)
121 {
122 b[0] = L',';
123 result = wcsistr(s, b);
124 b[0] = L' ';
125 }
126 }
127 if (!result && b[len - 1] == L' ' && wcschr(s, L'(') != NULL)
128 {
129 b[len - 1] = L'(';
130 result = wcsistr(s, b);
131 b[len - 1] = L' ';
132 }
133 if (!result && b[len - 1] == L' ' && wcschr(s, L'_') != NULL)
134 {
135 b[0] = L'_';
136 result = wcsistr(s, b);
137 b[0] = L' ';
138 }
139 if (!result && b[0] == L' ' && b[len - 1] == L' ' && wcschr(s, L')') != NULL)
140 {
141 b[0] = L')';
142 result = wcsistr(s, b);
143 b[0] = L' ';
144 }
145 return result;
146 }
147
148 /*
149 * Replaces full word with another shorter word
150 */
151 static
152 VOID wcsrep(
153 _Inout_ PWSTR pwStr,
154 _In_ PCWSTR pwFind,
155 _In_ PCWSTR pwReplace,
156 _In_ BOOL bReplaceFirstWord)
157 {
158 PWSTR pwsStr, pwsFind, pwsReplace, pwsBuf = NULL;
159 SIZE_T lenStr;
160 SIZE_T lenFind;
161 SIZE_T lenReplace;
162
163 if (!pwStr || !pwFind || !pwReplace ||
164 wcslen(pwStr) == 0 ||
165 wcslen(pwFind) == 0 ||
166 wcslen(pwFind) < wcslen(pwReplace))
167 {
168 return;
169 }
170 lenStr = wcslen(pwStr) + 2 + 1;
171 lenFind = wcslen(pwFind) + 2 + 1;
172 lenReplace = wcslen(pwReplace) + 2 + 1;
173
174 pwsStr = HeapAlloc(GetProcessHeap(), 0, lenStr * sizeof(WCHAR));
175 if (!pwsStr)
176 {
177 return;
178 }
179 StringCchCopyW(pwsStr, lenStr, L" ");
180 StringCchCatW(pwsStr, lenStr, pwStr);
181 StringCchCatW(pwsStr, lenStr, L" ");
182
183 pwsFind = HeapAlloc(GetProcessHeap(), 0, lenFind * sizeof(WCHAR));
184 if (!pwsFind)
185 {
186 goto freeStr;
187 }
188 StringCchCopyW(pwsFind, lenFind, L" ");
189 StringCchCatW(pwsFind, lenFind, pwFind);
190 StringCchCatW(pwsFind, lenFind, L" ");
191
192 if (!(pwsBuf = wcsistr_plus(pwsStr, pwsFind)))
193 {
194 goto freeFind;
195 }
196 if (!bReplaceFirstWord && pwsBuf - pwsStr < 2)
197 {
198 goto freeFind;
199 }
200
201 pwsReplace = HeapAlloc(GetProcessHeap(), 0, lenReplace * sizeof(WCHAR));
202 if (!pwsReplace)
203 {
204 goto freeFind;
205 }
206 StringCchCopyW(pwsReplace, lenReplace, L" ");
207 StringCchCatW(pwsReplace, lenReplace, pwReplace);
208 StringCchCatW(pwsReplace, lenReplace, L" ");
209
210 do
211 {
212 // replace substring
213 memmove(pwsBuf, pwsReplace, (lenReplace - 1) * sizeof(WCHAR));
214 // shift characters
215 memmove(pwsBuf + lenReplace - (wcslen(pwReplace) > 0 ? 1 : 2), pwsBuf + lenFind - 1, (lenStr - lenFind - (pwsBuf - pwsStr) + 1) * sizeof(WCHAR));
216 }
217 while ((pwsBuf = wcsistr_plus(pwsStr, pwsFind)) != NULL);
218
219 TrimDmiStringW(pwsStr);
220 StringCchCopyW(pwStr, wcslen(pwStr), pwsStr);
221
222 HeapFree(GetProcessHeap(), 0, pwsReplace);
223 freeFind:
224 HeapFree(GetProcessHeap(), 0, pwsFind);
225 freeStr:
226 HeapFree(GetProcessHeap(), 0, pwsStr);
227 }
228
229 static
230 BOOL IsGenericSystemName(PCWSTR ven, PCWSTR dev)
231 {
232 static const GENERIC_NAME Vendors[] =
233 {
234 { L"To Be Filled By O.E.M.", FALSE }, // some ASUS boards
235 { L"System manufacturer", TRUE }, // some ASUS boards
236 { L"Default string", TRUE }, // some Gigabyte boards
237 { L"LTD Delovoy Office", TRUE }, // some Gigabyte boards
238 { L"O.E.M", TRUE }, // some AMD boards
239 { L"DEPO Computers", TRUE }, // various boards
240 };
241 static const GENERIC_NAME Devices[] =
242 {
243 { L"To Be Filled By O.E.M.", FALSE }, // some ASUS boards
244 { L"All Series", TRUE }, // some ASUS boards
245 { L"System Product Name", TRUE }, // some ASUS boards
246 { L"Default string", TRUE }, // some Gigabyte boards
247 { L"Please change product name", TRUE }, // some MSI boards
248 { L"Computer", TRUE }, // some Intel boards
249 { L"ChiefRiver Platform", TRUE }, // some Intel boards
250 { L"SharkBay Platform", TRUE }, // some Intel boards
251 { L"HuronRiver Platform", TRUE }, // some Intel boards
252 { L"SandyBridge Platform", TRUE }, // some Intel boards
253 { L"Broadwell Platform", TRUE }, // some LG boards
254 { L"Sabine Platform", TRUE }, // some AMD boards
255 { L"O.E.M", TRUE }, // some AMD boards
256 { L"*", TRUE }, // various boards
257 { L"GEG", TRUE }, // various boards
258 { L"OEM", TRUE }, // various boards
259 { L"DEPO Computers", TRUE }, // various boards
260 { L"Aquarius Pro, Std, Elt Series", TRUE }, // some Foxconn boards
261 { L"Aquarius Server", TRUE }, // some ASUS server boards
262 { L"Aquarius Server G2", TRUE }, // some ASUS server boards
263 { L"Super Server", TRUE }, // some Supermicro server boards
264 { L"POSITIVO MOBILE", FALSE }, // some Positivo devices
265 };
266 BOOL bMatch;
267 UINT i;
268
269 for (i = 0; i < _countof(Vendors); i++)
270 {
271 if (!ven)
272 {
273 break;
274 }
275 if (Vendors[i].bCaseSensitive)
276 {
277 bMatch = !wcscmp(ven, Vendors[i].pwName);
278 }
279 else
280 {
281 bMatch = !wcsicmp(ven, Vendors[i].pwName);
282 }
283 if (bMatch)
284 {
285 return TRUE;
286 }
287 }
288
289 for (i = 0; i < _countof(Devices); i++)
290 {
291 if (!dev)
292 {
293 break;
294 }
295 if (Devices[i].bCaseSensitive)
296 {
297 bMatch = !wcscmp(dev, Devices[i].pwName);
298 }
299 else
300 {
301 bMatch = !wcsicmp(dev, Devices[i].pwName);
302 }
303 if (bMatch)
304 {
305 return TRUE;
306 }
307 }
308 return FALSE;
309 }
310
311 static
312 void AppendSystemFamily(PWSTR pBuf, SIZE_T cchBuf, PCHAR * DmiStrings, PWSTR dev)
313 {
314 static const PCSTR KnownFamilies[] =
315 {
316 "Eee PC", // ASUS
317 "IdeaPad", // Lenovo
318 "IdeaCentre", // Lenovo
319 };
320 static const PCWSTR Aliases[] =
321 {
322 NULL,
323 NULL,
324 L"IdeaCenter",
325 };
326 UINT i;
327 WCHAR wideStr[128];
328
329 for (i = 0; i < _countof(KnownFamilies); i++)
330 {
331 StringCchPrintfW(wideStr, _countof(wideStr), L"%S", KnownFamilies[i]);
332
333 if (wcsistr(dev, wideStr) == NULL &&
334 (!Aliases[i] || wcsistr(dev, Aliases[i]) == NULL) &&
335 !stricmp(DmiStrings[SYS_FAMILY], KnownFamilies[i]))
336 {
337 if (wcslen(pBuf) > 0 && wcslen(dev) > 0)
338 {
339 StringCchCatW(pBuf, cchBuf, L" ");
340 }
341 StringCchCatW(pBuf, cchBuf, wideStr);
342 }
343 }
344 }
345
346 BOOL GetSystemName(PWSTR pBuf, SIZE_T cchBuf)
347 {
348 static const VENDOR_LONG_NAME LongNames[] =
349 {
350 { L"ASUSTeK", L"ASUS" },
351 { L"First International Computer", L"FIC" },
352 { L"Hewlett-Packard", L"HP" },
353 { L"MICRO-STAR", L"MSI" },
354 { L"SGI.COM", L"SGI" },
355 { L"Silicon Graphics International", L"SGI" },
356 { L"InformationComputerSystems", L"ICS" },
357 { L"CHUWI INNOVATION AND TECHNOLOGY", L"CHUWI" },
358 { L"http://www.abit.com.tw/", L"ABIT" },
359 { L"www.abit.com.tw", L"ABIT" },
360 { L"Colorful Technology And Development", L"Colorful" },
361 { L"HaierComputer", L"Haier" },
362 };
363 static const REDUNDANT_WORD RedundantWords[] =
364 {
365 { L"Corporation", FALSE },
366 { L"Computer", FALSE },
367 { L"Computers", FALSE },
368 { L"Group", FALSE },
369 { L"Cloud", FALSE },
370 { L"Center", FALSE },
371 { L"Systems", FALSE },
372 { L"Microsystems", FALSE },
373 { L"Infosystems", FALSE },
374 { L"Electronics", FALSE },
375 { L"Electric", FALSE },
376 { L"Software", FALSE },
377 { L"International", FALSE },
378 { L"Interantonal", FALSE }, // on purpose (some MSI boards)
379 { L"Industrial", FALSE },
380 { L"Information", FALSE },
381 { L"Technology", FALSE },
382 { L"Tecohnology", FALSE }, // on purpose (some Gigabyte boards)
383 { L"Technologies", FALSE },
384 { L"Limited", FALSE },
385 { L"Int", FALSE },
386 { L"Inc", FALSE },
387 { L"Co", FALSE },
388 { L"Corp", FALSE },
389 { L"Crop", FALSE },
390 { L"Ltd", FALSE },
391 { L"GmbH", FALSE },
392 { L"S.p.A", FALSE },
393 { L"S.A", FALSE },
394 { L"SA", FALSE },
395 { L"SAS", FALSE },
396 { L"BV", FALSE },
397 { L"AG", FALSE },
398 { L"OOO", TRUE },
399 { L"CJSC", FALSE },
400 { L"INT'L", FALSE },
401 { L"plc", FALSE },
402 };
403 PVOID SMBiosBuf;
404 PCHAR DmiStrings[ID_STRINGS_MAX] = { 0 };
405 WCHAR ven[512], dev[512];
406 BOOL bGenericName;
407 UINT i;
408 PWCHAR j;
409
410 SMBiosBuf = LoadSMBiosData(DmiStrings);
411 if (!SMBiosBuf)
412 {
413 return FALSE;
414 }
415
416 GetSMBiosStringW(DmiStrings[SYS_VENDOR], ven, _countof(ven), TRUE);
417 GetSMBiosStringW(DmiStrings[SYS_PRODUCT], dev, _countof(dev), TRUE);
418 bGenericName = IsGenericSystemName(ven, dev);
419
420 if (wcslen(dev) == 0 ||
421 !wcscmp(dev, ven) ||
422 bGenericName)
423 {
424 // system strings are unusable, use board strings
425 if (DmiStrings[BOARD_VENDOR] != NULL || !bGenericName)
426 {
427 if ((DmiStrings[BOARD_VENDOR] &&
428 strlen(DmiStrings[BOARD_VENDOR]) >= 2 &&
429 strstr(DmiStrings[BOARD_VENDOR], " ") != DmiStrings[BOARD_VENDOR]) ||
430 IsGenericSystemName(ven, NULL))
431 {
432 GetSMBiosStringW(DmiStrings[BOARD_VENDOR], ven, _countof(ven), TRUE);
433 }
434 GetSMBiosStringW(DmiStrings[BOARD_NAME], dev, _countof(dev), TRUE);
435
436 if (IsGenericSystemName(ven, NULL))
437 {
438 *ven = 0;
439 }
440 if (IsGenericSystemName(NULL, dev))
441 {
442 *dev = 0;
443 }
444 if (wcslen(dev) == 0 &&
445 DmiStrings[SYS_VERSION] != NULL)
446 {
447 GetSMBiosStringW(DmiStrings[SYS_VERSION], dev, _countof(dev), TRUE);
448
449 if (IsGenericSystemName(NULL, dev))
450 {
451 *dev = 0;
452 }
453 }
454 if (wcslen(dev) == 0 &&
455 DmiStrings[BOARD_VERSION] != NULL)
456 {
457 GetSMBiosStringW(DmiStrings[BOARD_VERSION], dev, _countof(dev), TRUE);
458
459 if (IsGenericSystemName(NULL, dev))
460 {
461 *dev = 0;
462 }
463 }
464 }
465
466 if (wcslen(ven) == 0 && wcslen(dev) == 0)
467 {
468 // board strings are empty, use BIOS vendor string
469 GetSMBiosStringW(DmiStrings[BIOS_VENDOR], ven, _countof(ven), TRUE);
470 }
471 }
472 else
473 {
474 if (wcslen(ven) < 2)
475 {
476 GetSMBiosStringW(DmiStrings[BOARD_VENDOR], ven, _countof(ven), TRUE);
477
478 if (IsGenericSystemName(ven, NULL))
479 {
480 *ven = 0;
481 }
482 }
483 }
484
485 // workaround for LORD ELECTRONICS
486 if (((j = wcsstr(ven, L" ")) != NULL) && (j - ven > 2))
487 {
488 i = j - ven;
489 if (!wcsncmp(ven + wcslen(ven) - i, ven, i))
490 {
491 ven[wcslen(ven) - i] = L'\0';
492 }
493 }
494
495 // make vendor strings shorter
496 for (i = 0; i < _countof(LongNames); i++)
497 {
498 wcsrep(ven, LongNames[i].pwLongName, LongNames[i].pwShortName, TRUE);
499 }
500
501 // remove redundant words
502 for (i = 0; i < _countof(RedundantWords); i++)
503 {
504 wcsrep(ven, RedundantWords[i].pwStr, L"", RedundantWords[i].bReplaceFirstWord);
505 }
506 for (i = 0; i < _countof(RedundantWords); i++)
507 {
508 StringCchCopyW(pBuf, cchBuf, RedundantWords[i].pwStr);
509 StringCchCatW(pBuf, cchBuf, L".");
510 wcsrep(ven, pBuf, L"", RedundantWords[i].bReplaceFirstWord);
511 }
512
513 // workaround for LENOVO notebooks
514 if (!wcscmp(ven, L"LENOVO"))
515 {
516 StringCchCopyW(ven, _countof(ven), L"Lenovo");
517
518 if (stricmp(DmiStrings[SYS_VERSION], "Lenovo") &&
519 stricmp(DmiStrings[SYS_VERSION], "Lenovo Product") &&
520 stricmp(DmiStrings[SYS_VERSION], " ") &&
521 _strnicmp(DmiStrings[SYS_VERSION], " ", 3) &&
522 wcsistr(dev, L"IdeaPad ") == NULL &&
523 wcsistr(dev, L"ThinkServer ") == NULL)
524 {
525 GetSMBiosStringW(DmiStrings[SYS_VERSION], dev, _countof(dev), TRUE);
526 }
527
528 if (wcsstr(dev, L"Lenovo-") == dev)
529 {
530 // replace "-" with space
531 dev[6] = L' ';
532 }
533
534 if (!wcscmp(dev, L"Lenovo"))
535 {
536 GetSMBiosStringW(DmiStrings[BOARD_NAME], dev, _countof(dev), TRUE);
537 }
538 }
539 if (!wcscmp(ven, L"IBM") &&
540 DmiStrings[SYS_VERSION] != NULL &&
541 strstr(DmiStrings[SYS_VERSION], "ThinkPad ") != NULL)
542 {
543 GetSMBiosStringW(DmiStrings[SYS_VERSION], dev, _countof(dev), TRUE);
544 }
545
546 // workaround for DEXP
547 if (!wcscmp(ven, L"DEXP"))
548 {
549 if (!stricmp(DmiStrings[SYS_PRODUCT], "Tablet PC")
550 && DmiStrings[SYS_VERSION] != NULL)
551 {
552 GetSMBiosStringW(DmiStrings[SYS_VERSION], dev, _countof(dev), TRUE);
553 }
554 }
555
556 // workaround for Razer Blade
557 if (!wcscmp(ven, L"Razer") && !wcscmp(dev, L"Blade"))
558 {
559 if (DmiStrings[SYS_VERSION] != NULL)
560 {
561 StringCchCopyW(ven, _countof(ven), L"Razer Blade");
562 GetSMBiosStringW(DmiStrings[SYS_VERSION], dev, _countof(dev), TRUE);
563 }
564 }
565
566 // workaround for MSI motherboards
567 if (!wcscmp(ven, L"MSI") &&
568 wcsstr(dev, L"MS-") != NULL &&
569 DmiStrings[BOARD_NAME] != NULL &&
570 strstr(DmiStrings[BOARD_NAME], "(MS-") != NULL)
571 {
572 GetSMBiosStringW(DmiStrings[BOARD_NAME], dev, _countof(dev), TRUE);
573 }
574 if (wcslen(ven) == 0 &&
575 wcsstr(dev, L"MS-") == dev)
576 {
577 StringCchCopyW(ven, _countof(ven), L"MSI");
578 }
579
580 // trim redundant characters
581 TrimPunctuation(ven);
582 TrimPunctuation(dev);
583
584 if (wcsistr(dev, ven) == dev ||
585 (!wcscmp(ven, L"ASUS") && wcsstr(dev, L"ASUS") != NULL) ||
586 (!wcscmp(ven, L"HP") && wcsstr(dev, L" by HP") != NULL))
587 {
588 // device string contains vendor string, use second only
589 StringCchCopyW(pBuf, cchBuf, dev);
590 }
591 else
592 {
593 StringCchCopyW(pBuf, cchBuf, ven);
594 AppendSystemFamily(pBuf, cchBuf, DmiStrings, dev);
595 if (wcslen(pBuf) > 0 && wcslen(dev) > 0)
596 {
597 StringCchCatW(pBuf, cchBuf, L" ");
598 }
599 StringCchCatW(pBuf, cchBuf, dev);
600 }
601
602 FreeSMBiosData(SMBiosBuf);
603
604 return (wcslen(pBuf) > 0);
605 }