xref: /reactos/dll/cpl/sysdm/smbios.c (revision c81af08f)
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-2019 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     BOOL bRemove;
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         if (!result && wcschr(s, L'.'))
144         {
145             b[len - 1] = L'.';
146             result = wcsistr(s, b);
147             b[len - 1] = L' ';
148         }
149         b[0] = L' ';
150     }
151     return result;
152 }
153 
154 /*
155  * Replaces full word with another shorter word
156  */
157 static
158 VOID wcsrep(
159     _Inout_ PWSTR pwStr,
160     _In_ PCWSTR pwFind,
161     _In_ PCWSTR pwReplace,
162     _In_ BOOL bReplaceFirstWord)
163 {
164     PWSTR pwsStr, pwsFind, pwsReplace, pwsBuf = NULL;
165     SIZE_T lenStr;
166     SIZE_T lenFind;
167     SIZE_T lenReplace;
168 
169     if (!pwStr || !pwFind || !pwReplace ||
170         wcslen(pwStr) == 0 ||
171         wcslen(pwFind) == 0 ||
172         wcslen(pwFind) < wcslen(pwReplace))
173     {
174         return;
175     }
176     lenStr = wcslen(pwStr) + 2 + 1;
177     lenFind = wcslen(pwFind) + 2 + 1;
178     lenReplace = wcslen(pwReplace) + 2 + 1;
179 
180     pwsStr = HeapAlloc(GetProcessHeap(), 0, lenStr * sizeof(WCHAR));
181     if (!pwsStr)
182     {
183         return;
184     }
185     StringCchCopyW(pwsStr, lenStr, L" ");
186     StringCchCatW(pwsStr, lenStr, pwStr);
187     StringCchCatW(pwsStr, lenStr, L" ");
188 
189     pwsFind = HeapAlloc(GetProcessHeap(), 0, lenFind * sizeof(WCHAR));
190     if (!pwsFind)
191     {
192         goto freeStr;
193     }
194     StringCchCopyW(pwsFind, lenFind, L" ");
195     StringCchCatW(pwsFind, lenFind, pwFind);
196     StringCchCatW(pwsFind, lenFind, L" ");
197 
198     if (!(pwsBuf = wcsistr_plus(pwsStr, pwsFind)))
199     {
200         goto freeFind;
201     }
202     if (!bReplaceFirstWord && pwsBuf - pwsStr < 2)
203     {
204         goto freeFind;
205     }
206 
207     pwsReplace = HeapAlloc(GetProcessHeap(), 0, lenReplace * sizeof(WCHAR));
208     if (!pwsReplace)
209     {
210         goto freeFind;
211     }
212     StringCchCopyW(pwsReplace, lenReplace, L" ");
213     StringCchCatW(pwsReplace, lenReplace, pwReplace);
214     StringCchCatW(pwsReplace, lenReplace, L" ");
215 
216     do
217     {
218         // replace substring
219         memmove(pwsBuf, pwsReplace, (lenReplace - 1) * sizeof(WCHAR));
220         // shift characters
221         memmove(pwsBuf + lenReplace - (wcslen(pwReplace) > 0 ? 1 : 2), pwsBuf + lenFind - 1, (lenStr - lenFind - (pwsBuf - pwsStr) + 1) * sizeof(WCHAR));
222     }
223     while ((pwsBuf = wcsistr_plus(pwsStr, pwsFind)) != NULL);
224 
225     TrimDmiStringW(pwsStr);
226     StringCchCopyW(pwStr, wcslen(pwStr), pwsStr);
227 
228     HeapFree(GetProcessHeap(), 0, pwsReplace);
229 freeFind:
230     HeapFree(GetProcessHeap(), 0, pwsFind);
231 freeStr:
232     HeapFree(GetProcessHeap(), 0, pwsStr);
233 }
234 
235 static
236 BOOL IsGenericSystemName(PCWSTR ven, PCWSTR dev, BOOL * bRemove)
237 {
238     static const GENERIC_NAME Vendors[] =
239     {
240         // some ASUS boards
241         { L"To Be Filled By O.E.M.", FALSE, TRUE },
242         { L"To Be Filled By O.E.M", FALSE, TRUE },
243         { L"System manufacturer", FALSE, TRUE },
244         // some Gigabyte boards
245         { L"Default string", TRUE, TRUE },
246         { L"LTD Delovoy Office", TRUE, FALSE },
247         { L"Motherboard by ZOTAC", TRUE, FALSE },
248         // various boards
249         { L"Type2 - Board Vendor Name1", TRUE, TRUE },
250         { L"DEPO Computers", TRUE, FALSE },
251         { L"-", TRUE, TRUE },
252         { L"OEM", TRUE, TRUE },
253         { L"O.E.M", TRUE, TRUE },
254         { L"Unknow", TRUE, TRUE },
255         // distinguish between Oracle and older VirtualBox releases (Sun, etc.)
256         { L"innotek GmbH", TRUE, FALSE },
257     };
258     static const GENERIC_NAME Devices[] =
259     {
260         // some ASUS boards
261         { L"To Be Filled By O.E.M.", FALSE, TRUE },
262         { L"To Be Filled By O.E.M", FALSE, TRUE },
263         { L"All Series", TRUE, TRUE },
264         { L"System Product Name", TRUE, TRUE },
265         { L"System Name", TRUE, TRUE },
266         // some Gigabyte boards
267         { L"Default string", TRUE, TRUE },
268         // some MSI boards
269         { L"Please change product name", TRUE, TRUE },
270         // some Intel boards
271         { L"Computer", TRUE, TRUE },
272         { L"ChiefRiver Platform", TRUE, FALSE },
273         { L"OakTrail Platform", TRUE, FALSE },
274         { L"SharkBay Platform", TRUE, FALSE },
275         { L"HuronRiver Platform", TRUE, FALSE },
276         { L"SandyBridge Platform", TRUE, FALSE },
277         { L"Broadwell Platform", TRUE, FALSE },
278         { L"Sabine Platform", TRUE, FALSE },
279         // various boards
280         { L"Base Board Product Name", TRUE, TRUE },
281         { L"Base Board Version", TRUE, TRUE },
282         { L"Type2 - Board Product Name1", TRUE, TRUE },
283         { L"Type2 - Board Version", TRUE, TRUE },
284         { L"*", TRUE, TRUE },
285         { L"T", TRUE, TRUE },
286         { L"GEG", TRUE, TRUE },
287         { L"N/A", TRUE, TRUE },
288         { L"---", TRUE, TRUE },
289         { L"OEM", TRUE, TRUE },
290         { L"INVA", TRUE, TRUE },
291         { L"O.E.M", TRUE, TRUE },
292         { L"DNSNB", TRUE, FALSE },
293         { L"12345", TRUE, FALSE },
294         { L"``````", TRUE, TRUE },
295         { L"Uknown", TRUE, TRUE },
296         { L"Desktop", FALSE, TRUE },
297         { L"Invalid", FALSE, TRUE },
298         { L"Reserved", TRUE, TRUE },
299         { L"HaierComputer", TRUE, FALSE },
300         { L"DEPO Computers", TRUE, FALSE },
301         { L"InsydeH2O EFI BIOS", TRUE, TRUE },
302         { L"HP All-in-One", TRUE, FALSE },
303         { L"MP Server", TRUE, FALSE },
304         { L"0000000000", TRUE, TRUE },
305         // some Foxconn boards
306         { L"Aquarius Pro, Std, Elt Series", TRUE, FALSE },
307         // some ASUS server boards
308         { L"Aquarius Server", TRUE, FALSE },
309         { L"Aquarius Server G2", TRUE, FALSE },
310         // some Supermicro server boards
311         { L"Super Server", TRUE, FALSE },
312         // some Positivo devices
313         { L"POSITIVO MOBILE", FALSE, FALSE },
314     };
315     BOOL bMatch;
316     UINT i;
317 
318     if (bRemove)
319     {
320         *bRemove = FALSE;
321     }
322     for (i = 0; i < _countof(Vendors); i++)
323     {
324         if (!ven)
325         {
326             break;
327         }
328         if (Vendors[i].bCaseSensitive)
329         {
330             bMatch = !wcscmp(ven, Vendors[i].pwName);
331         }
332         else
333         {
334             bMatch = !wcsicmp(ven, Vendors[i].pwName);
335         }
336         if (bMatch)
337         {
338             if (bRemove)
339             {
340                 *bRemove = Vendors[i].bRemove;
341             }
342             return TRUE;
343         }
344     }
345 
346     for (i = 0; i < _countof(Devices); i++)
347     {
348         if (!dev)
349         {
350             break;
351         }
352         if (Devices[i].bCaseSensitive)
353         {
354             bMatch = !wcscmp(dev, Devices[i].pwName);
355         }
356         else
357         {
358             bMatch = !wcsicmp(dev, Devices[i].pwName);
359         }
360         if (bMatch)
361         {
362             if (bRemove)
363             {
364                 *bRemove = Devices[i].bRemove;
365             }
366             return TRUE;
367         }
368     }
369     return FALSE;
370 }
371 
372 static
373 void AppendSystemFamily(PWSTR pBuf, SIZE_T cchBuf, PCHAR * DmiStrings, PWSTR dev)
374 {
375     static const PCSTR KnownFamilies[] =
376     {
377         "Eee PC",      // ASUS
378         "ThinkPad",    // Lenovo
379         "IdeaPad",     // Lenovo
380         "IdeaCentre",  // Lenovo
381     };
382     static const PCWSTR Aliases[] =
383     {
384         NULL,
385         NULL,
386         NULL,
387         L"IdeaCenter",
388     };
389     UINT i;
390     WCHAR wideStr[128];
391 
392     for (i = 0; i < _countof(KnownFamilies); i++)
393     {
394         StringCchPrintfW(wideStr, _countof(wideStr), L"%S", KnownFamilies[i]);
395 
396         if (wcsistr(dev, wideStr) == NULL &&
397             (!Aliases[i] || wcsistr(dev, Aliases[i]) == NULL) &&
398             DmiStrings[SYS_FAMILY] != NULL &&
399             !stricmp(DmiStrings[SYS_FAMILY], KnownFamilies[i]))
400         {
401             if (wcslen(pBuf) > 0 && wcslen(dev) > 0)
402             {
403                 StringCchCatW(pBuf, cchBuf, L" ");
404             }
405             StringCchCatW(pBuf, cchBuf, wideStr);
406         }
407     }
408 }
409 
410 static
411 BOOL TrimNonPrintable(PCHAR DmiString)
412 {
413     PCHAR c = DmiString;
414     if (!c)
415     {
416         return FALSE;
417     }
418     while (*c)
419     {
420         if (*c >= 0x20 && *c <= 0x7e)
421         {
422             c++;
423         }
424         else
425         {
426             *c = 0;
427             return TRUE;
428         }
429     }
430     return FALSE;
431 }
432 
433 BOOL GetSystemName(PWSTR pBuf, SIZE_T cchBuf)
434 {
435     static const VENDOR_LONG_NAME LongNames[] =
436     {
437         { L"ASUSTeK", L"ASUS" },
438         { L"First International Computer", L"FIC" },
439         { L"Hewlett-Packard", L"HP" },
440         { L"MICRO-STAR", L"MSI" },
441         { L"SGI.COM", L"SGI" },
442         { L"Silicon Graphics International", L"SGI" },
443         { L"Intel(R) Client Systems", L"Intel" },
444         { L"InformationComputerSystems", L"ICS" },
445         { L"CHUWI INNOVATION AND TECHNOLOGY", L"CHUWI" },
446         { L"http://www.abit.com.tw/", L"ABIT" },
447         { L"www.abit.com.tw", L"ABIT" },
448         { L"CASPER BILGISAYAR SISTEMLERI A.S", L"Casper" },
449         { L"Colorful Technology And Development", L"Colorful" },
450         { L"Colorful Yu Gong Technology And Development", L"Colorful Yu Gong" },
451         { L"HaierComputer", L"Haier" },
452         { L"HELIOS BUSINESS COMPUTER", L"HELIOS" },
453         { L"Shanghai Zongzhi InfoTech", L"Zongzhi" },
454         { L"TSING HUA TONGFANG CO.,LTD", L"TSINGHUA TONGFANG" },
455         { L"Yeston Digital Technology Co.,LTD", L"Yeston" },
456     };
457     static const REDUNDANT_WORD RedundantWords[] =
458     {
459         { L"Corporation", FALSE },
460         { L"Communication", FALSE },
461         { L"Computer", FALSE },
462         { L"Computers", FALSE },
463         { L"Group", FALSE },
464         { L"Cloud", FALSE },
465         { L"Center", FALSE },
466         { L"Systems", FALSE },
467         { L"Microsystems", FALSE },
468         { L"Infosystems", FALSE },
469         { L"Electronics", FALSE },
470         { L"Electric", FALSE },
471         { L"Software", FALSE },
472         { L"Foundation", FALSE },
473         { L"International", FALSE },
474         { L"Interantonal", FALSE }, // on purpose (some MSI boards)
475         { L"INTERANTIONAL", FALSE }, // on purpose (some MSI boards)
476         { L"Industrial", FALSE },
477         { L"Information", FALSE },
478         { L"Informatica", FALSE },
479         { L"Technology", FALSE },
480         { L"Tecohnology", FALSE }, // on purpose (some Gigabyte boards)
481         { L"Technologies", FALSE },
482         { L"Tecnologia", FALSE },
483         { L"Limited", FALSE },
484         { L"Int", FALSE },
485         { L"Inc", FALSE },
486         { L"Co", FALSE },
487         { L"Corp", FALSE },
488         { L"Crop", FALSE },
489         { L"Ltd", FALSE },
490         { L"LTDA", FALSE },
491         { L"GmbH", FALSE },
492         { L"S.p.A", FALSE },
493         { L"A.S.", FALSE },
494         { L"S.A", FALSE },
495         { L"S.A.S", FALSE },
496         { L"S/A", FALSE },
497         { L"SA", FALSE },
498         { L"SAS", FALSE },
499         { L"BV", FALSE },
500         { L"AG", FALSE },
501         { L"OOO", TRUE },
502         { L"CJSC", FALSE },
503         { L"INT'L", FALSE },
504         { L"plc", FALSE },
505     };
506     PVOID SMBiosBuf;
507     PCHAR DmiStrings[ID_STRINGS_MAX] = { 0 };
508     WCHAR ven[512], dev[512];
509     CHAR tmpstr[512];
510     BOOL bTrimProduct, bTrimFamily, bGenericName, bRemove;
511     UINT i;
512     PWCHAR j;
513 
514     SMBiosBuf = LoadSMBiosData(DmiStrings);
515     if (!SMBiosBuf)
516     {
517         return FALSE;
518     }
519 
520     TrimNonPrintable(DmiStrings[SYS_VENDOR]);
521     bTrimProduct = TrimNonPrintable(DmiStrings[SYS_PRODUCT]);
522     TrimNonPrintable(DmiStrings[SYS_VERSION]);
523     bTrimFamily = TrimNonPrintable(DmiStrings[SYS_FAMILY]);
524     TrimNonPrintable(DmiStrings[BOARD_VENDOR]);
525     TrimNonPrintable(DmiStrings[BOARD_NAME]);
526     TrimNonPrintable(DmiStrings[BOARD_VERSION]);
527 
528     if (bTrimProduct)
529     {
530         if (DmiStrings[SYS_FAMILY] && !bTrimFamily)
531         {
532             DmiStrings[SYS_PRODUCT] = DmiStrings[SYS_FAMILY];
533             bTrimProduct = FALSE;
534         }
535     }
536 
537     GetSMBiosStringW(DmiStrings[SYS_VENDOR], ven, _countof(ven), TRUE);
538     GetSMBiosStringW(DmiStrings[SYS_PRODUCT], dev, _countof(dev), TRUE);
539     bGenericName = IsGenericSystemName(ven, dev, NULL) || bTrimProduct;
540 
541     if (wcslen(dev) == 0 ||
542         !wcscmp(dev, ven) ||
543         bGenericName)
544     {
545         BOOL bGenericVen = FALSE, bGenericDev = (wcslen(dev) == 0 || !wcscmp(dev, ven) || bTrimProduct);
546 
547         if (bGenericName && IsGenericSystemName(ven, NULL, &bRemove))
548         {
549             if (bRemove)
550             {
551                 *ven = 0;
552             }
553             bGenericVen = TRUE;
554         }
555         if (bGenericName && IsGenericSystemName(NULL, dev, &bRemove))
556         {
557             if (bRemove)
558             {
559                 *dev = 0;
560             }
561             bGenericDev = TRUE;
562         }
563         // system strings are unusable, use board strings
564         if (DmiStrings[BOARD_VENDOR] != NULL || !bGenericName)
565         {
566             if ((DmiStrings[BOARD_VENDOR] &&
567                 strlen(DmiStrings[BOARD_VENDOR]) >= 2 &&
568                 strstr(DmiStrings[BOARD_VENDOR], "  ") != DmiStrings[BOARD_VENDOR]) ||
569                 bGenericVen)
570             {
571                 GetSMBiosStringW(DmiStrings[BOARD_VENDOR], ven, _countof(ven), TRUE);
572             }
573             GetSMBiosStringW(DmiStrings[BOARD_NAME], dev, _countof(dev), TRUE);
574 
575             if (IsGenericSystemName(ven, NULL, &bRemove) && bRemove)
576             {
577                 *ven = 0;
578             }
579             if (IsGenericSystemName(NULL, dev, &bRemove) && bRemove)
580             {
581                 *dev = 0;
582 
583                 if (!bGenericDev)
584                 {
585                     GetSMBiosStringW(DmiStrings[SYS_PRODUCT], dev, _countof(dev), TRUE);
586                 }
587             }
588             if (wcslen(dev) == 0 &&
589                 DmiStrings[SYS_VERSION] != NULL)
590             {
591                 GetSMBiosStringW(DmiStrings[SYS_VERSION], dev, _countof(dev), TRUE);
592 
593                 if (IsGenericSystemName(NULL, dev, &bRemove) && bRemove)
594                 {
595                     *dev = 0;
596                 }
597             }
598             if (wcslen(dev) == 0 &&
599                 DmiStrings[BOARD_VERSION] != NULL)
600             {
601                 GetSMBiosStringW(DmiStrings[BOARD_VERSION], dev, _countof(dev), TRUE);
602 
603                 if (IsGenericSystemName(NULL, dev, &bRemove) && bRemove)
604                 {
605                     *dev = 0;
606                 }
607             }
608         }
609 
610         if (wcslen(ven) == 0 && wcslen(dev) == 0)
611         {
612             // board strings are empty, use BIOS vendor string
613             GetSMBiosStringW(DmiStrings[BIOS_VENDOR], ven, _countof(ven), TRUE);
614         }
615     }
616     else
617     {
618         if (wcslen(ven) < 2)
619         {
620             GetSMBiosStringW(DmiStrings[BOARD_VENDOR], ven, _countof(ven), TRUE);
621 
622             if (IsGenericSystemName(ven, NULL, &bRemove) && bRemove)
623             {
624                 *ven = 0;
625             }
626         }
627     }
628 
629     // workaround for LORD ELECTRONICS
630     if (((j = wcsstr(ven, L" ")) != NULL) && (j - ven > 2))
631     {
632         i = j - ven;
633         if (!wcsncmp(ven + wcslen(ven) - i, ven, i))
634         {
635             ven[wcslen(ven) - i] = L'\0';
636         }
637     }
638 
639     // make vendor strings shorter
640     for (i = 0; i < _countof(LongNames); i++)
641     {
642         if (wcsstr(dev, LongNames[i].pwLongName) == dev)
643         {
644             // swap ven and dev
645             StringCchCopyW(pBuf, cchBuf, ven);
646             StringCchCopyW(ven, _countof(ven), dev);
647             StringCchCopyW(dev, _countof(dev), pBuf);
648         }
649         wcsrep(ven, LongNames[i].pwLongName, LongNames[i].pwShortName, TRUE);
650     }
651 
652     // remove redundant words
653     for (i = 0; i < _countof(RedundantWords); i++)
654     {
655         wcsrep(ven, RedundantWords[i].pwStr, L"", RedundantWords[i].bReplaceFirstWord);
656     }
657     for (i = 0; i < _countof(RedundantWords); i++)
658     {
659         StringCchCopyW(pBuf, cchBuf, RedundantWords[i].pwStr);
660         StringCchCatW(pBuf, cchBuf, L".");
661         wcsrep(ven, pBuf, L"", RedundantWords[i].bReplaceFirstWord);
662     }
663 
664     // workaround for LENOVO notebooks
665     if (!wcsicmp(ven, L"LENOVO"))
666     {
667         StringCchCopyW(ven, _countof(ven), L"Lenovo");
668 
669         if (DmiStrings[SYS_VERSION] != NULL)
670         {
671             if (!strncmp(DmiStrings[SYS_VERSION], "ThinkPad   ", 11))
672             {
673                 DmiStrings[SYS_VERSION][8] = L'\0';
674             }
675             if (wcslen(dev) > 0 &&
676                 (!strcmp(DmiStrings[SYS_VERSION], "IdeaCentre") ||
677                 !strcmp(DmiStrings[SYS_VERSION], "ThinkPad")))
678             {
679                 DmiStrings[SYS_FAMILY] = DmiStrings[SYS_VERSION];
680                 DmiStrings[SYS_VERSION] = NULL;
681             }
682             else
683             {
684                 StringCchCopyA(tmpstr, _countof(tmpstr), DmiStrings[SYS_VERSION]);
685                 _strupr(tmpstr);
686             }
687         }
688 
689         if (DmiStrings[SYS_VERSION] != NULL &&
690             strcmp(tmpstr, " ") &&
691             strcmp(tmpstr, "LENOVO") &&
692             strstr(tmpstr, "LENOVO   ") == NULL &&
693             strstr(tmpstr, "LENOVO PRODUCT") == NULL &&
694             strstr(tmpstr, "INVALID") == NULL &&
695             strncmp(tmpstr, "   ", 3) &&
696             strstr(DmiStrings[SYS_VERSION], "Rev ") == NULL &&
697             strstr(DmiStrings[SYS_VERSION], "1.") == NULL &&
698             wcsistr(dev, L"System ") == NULL && // includes System x and ThinkSystem
699             wcsistr(dev, L"IdeaPad ") == NULL &&
700             wcsistr(dev, L"ThinkServer ") == NULL)
701         {
702             GetSMBiosStringW(DmiStrings[SYS_VERSION], dev, _countof(dev), TRUE);
703         }
704 
705         if (wcsstr(dev, L"Lenovo-") == dev)
706         {
707             // replace "-" with space
708             dev[6] = L' ';
709         }
710 
711         if (!wcscmp(dev, L"Lenovo"))
712         {
713             GetSMBiosStringW(DmiStrings[BOARD_NAME], dev, _countof(dev), TRUE);
714         }
715     }
716     if (!wcscmp(ven, L"IBM") &&
717         DmiStrings[SYS_VERSION] != NULL &&
718         (strstr(DmiStrings[SYS_VERSION], "ThinkPad ") != NULL ||
719          strstr(DmiStrings[SYS_VERSION], "ThinkCentre ") != NULL))
720     {
721         GetSMBiosStringW(DmiStrings[SYS_VERSION], dev, _countof(dev), TRUE);
722     }
723 
724     // workaround for DEXP
725     if (!wcscmp(ven, L"DEXP"))
726     {
727         if (DmiStrings[SYS_PRODUCT] != NULL &&
728             DmiStrings[SYS_VERSION] != NULL &&
729             (!stricmp(DmiStrings[SYS_PRODUCT], "Tablet PC") ||
730              !stricmp(DmiStrings[SYS_PRODUCT], "Notebook") ||
731              !stricmp(DmiStrings[SYS_PRODUCT], "Decktop")))
732         {
733             GetSMBiosStringW(DmiStrings[SYS_VERSION], dev, _countof(dev), TRUE);
734         }
735     }
736 
737     // workaround for Razer Blade
738     if (!wcscmp(ven, L"Razer") && !wcscmp(dev, L"Blade"))
739     {
740         if (DmiStrings[SYS_VERSION] != NULL)
741         {
742             StringCchCopyW(ven, _countof(ven), L"Razer Blade");
743             GetSMBiosStringW(DmiStrings[SYS_VERSION], dev, _countof(dev), TRUE);
744         }
745     }
746 
747     // workaround for MSI motherboards
748     if (!wcscmp(ven, L"MSI") &&
749         wcsstr(dev, L"MS-") != NULL &&
750         DmiStrings[BOARD_NAME] != NULL &&
751         strstr(DmiStrings[BOARD_NAME], "(MS-") != NULL)
752     {
753         GetSMBiosStringW(DmiStrings[BOARD_NAME], dev, _countof(dev), TRUE);
754     }
755     if (wcslen(ven) == 0 &&
756         wcsstr(dev, L"MS-") == dev)
757     {
758         StringCchCopyW(ven, _countof(ven), L"MSI");
759     }
760 
761     // trim redundant characters
762     TrimPunctuation(ven);
763     TrimPunctuation(dev);
764 
765     if (wcsistr(dev, ven) == dev ||
766         (!wcscmp(ven, L"ASUS") && wcsstr(dev, L"ASUS") != NULL) ||
767         (!wcscmp(ven, L"HP") && wcsstr(dev, L" by HP") != NULL) ||
768         (!wcscmp(ven, L"INTEL") && wcsstr(dev, L" INTEL") != NULL))
769     {
770         // device string contains vendor string, use second only
771         StringCchCopyW(pBuf, cchBuf, dev);
772     }
773     else
774     {
775         if (wcslen(ven) > 0 && wcslen(dev) > 0 && (j = wcschr(dev, L' ')))
776         {
777             // check if vendor string ends with first word of device string
778             i = j - dev;
779             if (wcslen(ven) > i && !_wcsnicmp(ven + wcslen(ven) - i, dev, i))
780             {
781                 ven[wcslen(ven) - i] = L'\0';
782                 TrimPunctuation(ven);
783             }
784         }
785         StringCchCopyW(pBuf, cchBuf, ven);
786         AppendSystemFamily(pBuf, cchBuf, DmiStrings, dev);
787         if (wcslen(pBuf) > 0 && wcslen(dev) > 0)
788         {
789             StringCchCatW(pBuf, cchBuf, L" ");
790         }
791         StringCchCatW(pBuf, cchBuf, dev);
792     }
793 
794     FreeSMBiosData(SMBiosBuf);
795 
796     return (wcslen(pBuf) > 0);
797 }
798