xref: /reactos/dll/cpl/sysdm/smbios.c (revision c3483339)
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 
15 typedef struct GENERIC_NAME
16 {
17     PCWSTR pwName;
18     BOOL bCaseSensitive;
19 } GENERIC_NAME;
20 
21 typedef struct VENDOR_LONG_NAME
22 {
23     PCWSTR pwLongName;
24     PCWSTR pwShortName;
25 } VENDOR_LONG_NAME;
26 
27 typedef struct REDUNDANT_WORD
28 {
29     PCWSTR pwStr;
30     BOOL bReplaceFirstWord;
31 } REDUNDANT_WORD;
32 
33 static
34 BOOL
35 IsPunctuation(
36     _In_ WCHAR chr)
37 {
38     return (chr <= L' ' || chr == L'.' || chr == L',');
39 }
40 
41 /*
42  * Trim redundant characters
43  */
44 static
45 VOID
46 TrimPunctuation(
47     _Inout_ PWSTR pStr)
48 {
49     SIZE_T Length;
50     UINT i = 0;
51 
52     if (!pStr)
53         return;
54 
55     Length = wcslen(pStr);
56     if (Length == 0)
57         return;
58 
59     /* Trim leading characters */
60     while (i < Length && IsPunctuation(pStr[i]))
61     {
62         i++;
63     }
64 
65     if (i > 0)
66     {
67         Length -= i;
68         memmove(pStr, pStr + i, (Length + 1) * sizeof(WCHAR));
69     }
70 
71     /* Trim trailing characters */
72     while (Length && IsPunctuation(pStr[Length-1]))
73     {
74         pStr[Length-1] = L'\0';
75         --Length;
76     }
77 }
78 
79 /*
80  * Case insensitive variant of wcsstr
81  */
82 static
83 wchar_t * wcsistr(const wchar_t *s, const wchar_t *b)
84 {
85     wchar_t *x;
86     wchar_t *y;
87     wchar_t *c;
88     x = (wchar_t *)s;
89     while (*x)
90     {
91         if (towlower(*x) == towlower(*b))
92         {
93             y = x;
94             c = (wchar_t *)b;
95             while (*y && *c && towlower(*y) == towlower(*c))
96             {
97                 c++;
98                 y++;
99             }
100             if (!*c)
101                 return x;
102         }
103         x++;
104     }
105     return NULL;
106 }
107 
108 static
109 wchar_t * wcsistr_plus(const wchar_t *s, wchar_t *b)
110 {
111     wchar_t * result = wcsistr(s, b);
112     UINT len = wcslen(b);
113     // workarounds
114     if (!result && b[len - 1] == L' ' && wcschr(s, L',') != NULL)
115     {
116         b[len - 1] = L',';
117         result = wcsistr(s, b);
118         b[len - 1] = L' ';
119         if (!result)
120         {
121             b[0] = L',';
122             result = wcsistr(s, b);
123             b[0] = L' ';
124         }
125     }
126     if (!result && b[len - 1] == L' ' && wcschr(s, L'(') != NULL)
127     {
128         b[len - 1] = L'(';
129         result = wcsistr(s, b);
130         b[len - 1] = L' ';
131     }
132     if (!result && b[len - 1] == L' ' && wcschr(s, L'_') != NULL)
133     {
134         b[0] = L'_';
135         result = wcsistr(s, b);
136         b[0] = L' ';
137     }
138     if (!result && b[0] == L' ' && b[len - 1] == L' ' && wcschr(s, L')') != NULL)
139     {
140         b[0] = L')';
141         result = wcsistr(s, b);
142         b[0] = L' ';
143     }
144     return result;
145 }
146 
147 /*
148  * Replaces full word with another shorter word
149  */
150 static
151 VOID wcsrep(
152     _Inout_ PWSTR pwStr,
153     _In_ PCWSTR pwFind,
154     _In_ PCWSTR pwReplace,
155     _In_ BOOL bReplaceFirstWord)
156 {
157     PWSTR pwsStr, pwsFind, pwsReplace, pwsBuf = NULL;
158     SIZE_T lenStr;
159     SIZE_T lenFind;
160     SIZE_T lenReplace;
161 
162     if (!pwStr || !pwFind || !pwReplace ||
163         wcslen(pwStr) == 0 ||
164         wcslen(pwFind) == 0 ||
165         wcslen(pwFind) < wcslen(pwReplace))
166     {
167         return;
168     }
169     lenStr = wcslen(pwStr) + 2 + 1;
170     lenFind = wcslen(pwFind) + 2 + 1;
171     lenReplace = wcslen(pwReplace) + 2 + 1;
172 
173     pwsStr = HeapAlloc(GetProcessHeap(), 0, lenStr * sizeof(WCHAR));
174     if (!pwsStr)
175     {
176         return;
177     }
178     StringCchCopyW(pwsStr, lenStr, L" ");
179     StringCchCatW(pwsStr, lenStr, pwStr);
180     StringCchCatW(pwsStr, lenStr, L" ");
181 
182     pwsFind = HeapAlloc(GetProcessHeap(), 0, lenFind * sizeof(WCHAR));
183     if (!pwsFind)
184     {
185         goto freeStr;
186     }
187     StringCchCopyW(pwsFind, lenFind, L" ");
188     StringCchCatW(pwsFind, lenFind, pwFind);
189     StringCchCatW(pwsFind, lenFind, L" ");
190 
191     if (!(pwsBuf = wcsistr_plus(pwsStr, pwsFind)))
192     {
193         goto freeFind;
194     }
195     if (!bReplaceFirstWord && pwsBuf - pwsStr < 2)
196     {
197         goto freeFind;
198     }
199 
200     pwsReplace = HeapAlloc(GetProcessHeap(), 0, lenReplace * sizeof(WCHAR));
201     if (!pwsReplace)
202     {
203         goto freeFind;
204     }
205     StringCchCopyW(pwsReplace, lenReplace, L" ");
206     StringCchCatW(pwsReplace, lenReplace, pwReplace);
207     StringCchCatW(pwsReplace, lenReplace, L" ");
208 
209     do
210     {
211         // replace substring
212         memmove(pwsBuf, pwsReplace, (lenReplace - 1) * sizeof(WCHAR));
213         // shift characters
214         memmove(pwsBuf + lenReplace - (wcslen(pwReplace) > 0 ? 1 : 2), pwsBuf + lenFind - 1, (lenStr - lenFind - (pwsBuf - pwsStr) + 1) * sizeof(WCHAR));
215     }
216     while ((pwsBuf = wcsistr_plus(pwsStr, pwsFind)) != NULL);
217 
218     TrimDmiStringW(pwsStr);
219     StringCchCopyW(pwStr, wcslen(pwStr), pwsStr);
220 
221     HeapFree(GetProcessHeap(), 0, pwsReplace);
222 freeFind:
223     HeapFree(GetProcessHeap(), 0, pwsFind);
224 freeStr:
225     HeapFree(GetProcessHeap(), 0, pwsStr);
226 }
227 
228 static
229 BOOL IsGenericSystemName(PCWSTR ven, PCWSTR dev)
230 {
231     static const GENERIC_NAME Vendors[] =
232     {
233         { L"To Be Filled By O.E.M.", FALSE }, // some ASUS boards
234         { L"System manufacturer", TRUE },     // some ASUS boards
235         { L"Default string", TRUE },          // some Gigabyte boards
236         { L"LTD Delovoy Office", TRUE },      // some Gigabyte boards
237         { L"O.E.M", TRUE },                   // some AMD boards
238         { L"DEPO Computers", TRUE },          // various boards
239     };
240     static const GENERIC_NAME Devices[] =
241     {
242         { L"To Be Filled By O.E.M.", FALSE }, // some ASUS boards
243         { L"All Series", TRUE },              // some ASUS boards
244         { L"System Product Name", TRUE },     // some ASUS boards
245         { L"Default string", TRUE },          // some Gigabyte boards
246         { L"Please change product name", TRUE }, // some MSI boards
247         { L"Computer", TRUE },                // some Intel boards
248         { L"ChiefRiver Platform", TRUE },     // some Intel boards
249         { L"SharkBay Platform", TRUE },       // some Intel boards
250         { L"HuronRiver Platform", TRUE },     // some Intel boards
251         { L"SandyBridge Platform", TRUE },    // some Intel boards
252         { L"Broadwell Platform", TRUE },      // some LG boards
253         { L"Sabine Platform", TRUE },         // some AMD boards
254         { L"O.E.M", TRUE },                   // some AMD boards
255         { L"*", TRUE },                       // various boards
256         { L"GEG", TRUE },                     // various boards
257         { L"OEM", TRUE },                     // various boards
258         { L"DEPO Computers", TRUE },          // various boards
259         { L"Aquarius Pro, Std, Elt Series", TRUE }, // some Foxconn boards
260         { L"Aquarius Server", TRUE },         // some ASUS server boards
261         { L"Aquarius Server G2", TRUE },      // some ASUS server boards
262         { L"Super Server", TRUE },            // some Supermicro server boards
263         { L"POSITIVO MOBILE", FALSE },        // some Positivo devices
264     };
265     BOOL bMatch;
266     UINT i;
267 
268     for (i = 0; i < _countof(Vendors); i++)
269     {
270         if (!ven)
271         {
272             break;
273         }
274         if (Vendors[i].bCaseSensitive)
275         {
276             bMatch = !wcscmp(ven, Vendors[i].pwName);
277         }
278         else
279         {
280             bMatch = !wcsicmp(ven, Vendors[i].pwName);
281         }
282         if (bMatch)
283         {
284             return TRUE;
285         }
286     }
287 
288     for (i = 0; i < _countof(Devices); i++)
289     {
290         if (!dev)
291         {
292             break;
293         }
294         if (Devices[i].bCaseSensitive)
295         {
296             bMatch = !wcscmp(dev, Devices[i].pwName);
297         }
298         else
299         {
300             bMatch = !wcsicmp(dev, Devices[i].pwName);
301         }
302         if (bMatch)
303         {
304             return TRUE;
305         }
306     }
307     return FALSE;
308 }
309 
310 static
311 void AppendSystemFamily(PWSTR pBuf, SIZE_T cchBuf, PCHAR * DmiStrings, PWSTR dev)
312 {
313     static const PCSTR KnownFamilies[] =
314     {
315         "Eee PC",      // ASUS
316         "IdeaPad",     // Lenovo
317         "IdeaCentre",  // Lenovo
318     };
319     static const PCWSTR Aliases[] =
320     {
321         NULL,
322         NULL,
323         L"IdeaCenter",
324     };
325     UINT i;
326     WCHAR wideStr[128];
327 
328     for (i = 0; i < _countof(KnownFamilies); i++)
329     {
330         StringCchPrintfW(wideStr, _countof(wideStr), L"%S", KnownFamilies[i]);
331 
332         if (wcsistr(dev, wideStr) == NULL &&
333             (!Aliases[i] || wcsistr(dev, Aliases[i]) == NULL) &&
334             DmiStrings[SYS_FAMILY] != 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 (DmiStrings[SYS_VERSION] != NULL &&
519             stricmp(DmiStrings[SYS_VERSION], "Lenovo") &&
520             stricmp(DmiStrings[SYS_VERSION], "Lenovo Product") &&
521             stricmp(DmiStrings[SYS_VERSION], " ") &&
522             _strnicmp(DmiStrings[SYS_VERSION], "   ", 3) &&
523             wcsistr(dev, L"IdeaPad ") == NULL &&
524             wcsistr(dev, L"ThinkServer ") == NULL)
525         {
526             GetSMBiosStringW(DmiStrings[SYS_VERSION], dev, _countof(dev), TRUE);
527         }
528 
529         if (wcsstr(dev, L"Lenovo-") == dev)
530         {
531             // replace "-" with space
532             dev[6] = L' ';
533         }
534 
535         if (!wcscmp(dev, L"Lenovo"))
536         {
537             GetSMBiosStringW(DmiStrings[BOARD_NAME], dev, _countof(dev), TRUE);
538         }
539     }
540     if (!wcscmp(ven, L"IBM") &&
541         DmiStrings[SYS_VERSION] != NULL &&
542         strstr(DmiStrings[SYS_VERSION], "ThinkPad ") != NULL)
543     {
544         GetSMBiosStringW(DmiStrings[SYS_VERSION], dev, _countof(dev), TRUE);
545     }
546 
547     // workaround for DEXP
548     if (!wcscmp(ven, L"DEXP"))
549     {
550         if (DmiStrings[SYS_PRODUCT] != NULL &&
551             !stricmp(DmiStrings[SYS_PRODUCT], "Tablet PC") &&
552             DmiStrings[SYS_VERSION] != NULL)
553         {
554             GetSMBiosStringW(DmiStrings[SYS_VERSION], dev, _countof(dev), TRUE);
555         }
556     }
557 
558     // workaround for Razer Blade
559     if (!wcscmp(ven, L"Razer") && !wcscmp(dev, L"Blade"))
560     {
561         if (DmiStrings[SYS_VERSION] != NULL)
562         {
563             StringCchCopyW(ven, _countof(ven), L"Razer Blade");
564             GetSMBiosStringW(DmiStrings[SYS_VERSION], dev, _countof(dev), TRUE);
565         }
566     }
567 
568     // workaround for MSI motherboards
569     if (!wcscmp(ven, L"MSI") &&
570         wcsstr(dev, L"MS-") != NULL &&
571         DmiStrings[BOARD_NAME] != NULL &&
572         strstr(DmiStrings[BOARD_NAME], "(MS-") != NULL)
573     {
574         GetSMBiosStringW(DmiStrings[BOARD_NAME], dev, _countof(dev), TRUE);
575     }
576     if (wcslen(ven) == 0 &&
577         wcsstr(dev, L"MS-") == dev)
578     {
579         StringCchCopyW(ven, _countof(ven), L"MSI");
580     }
581 
582     // trim redundant characters
583     TrimPunctuation(ven);
584     TrimPunctuation(dev);
585 
586     if (wcsistr(dev, ven) == dev ||
587         (!wcscmp(ven, L"ASUS") && wcsstr(dev, L"ASUS") != NULL) ||
588         (!wcscmp(ven, L"HP") && wcsstr(dev, L" by HP") != NULL))
589     {
590         // device string contains vendor string, use second only
591         StringCchCopyW(pBuf, cchBuf, dev);
592     }
593     else
594     {
595         StringCchCopyW(pBuf, cchBuf, ven);
596         AppendSystemFamily(pBuf, cchBuf, DmiStrings, dev);
597         if (wcslen(pBuf) > 0 && wcslen(dev) > 0)
598         {
599             StringCchCatW(pBuf, cchBuf, L" ");
600         }
601         StringCchCatW(pBuf, cchBuf, dev);
602     }
603 
604     FreeSMBiosData(SMBiosBuf);
605 
606     return (wcslen(pBuf) > 0);
607 }
608