xref: /reactos/dll/cpl/sysdm/smbios.c (revision 7abc8be1)
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 }
606