xref: /reactos/dll/cpl/sysdm/smbios.c (revision 09dde2cf)
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-2020 Stanislav Motylkov <x86corez@gmail.com>
7  *
8  */
9 
10 #include "precomp.h"
11 
12 #include <udmihelp.h>
13 
14 typedef struct GENERIC_NAME
15 {
16     PCWSTR pwName;
17     BOOL bCaseSensitive;
18     BOOL bRemove;
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 static
42 BOOL IsDigitStrA(PCHAR DmiString)
43 {
44     PCHAR c = DmiString;
45     if (!c)
46     {
47         return FALSE;
48     }
49     while (*c)
50     {
51         if (*c >= '0' && *c <= '9')
52         {
53             c++;
54         }
55         else
56         {
57             return FALSE;
58         }
59     }
60     return TRUE;
61 }
62 
63 /*
64  * Trim redundant characters
65  */
66 static
67 VOID
68 TrimPunctuation(
69     _Inout_ PWSTR pStr)
70 {
71     SIZE_T Length;
72     UINT i = 0;
73 
74     if (!pStr)
75         return;
76 
77     Length = wcslen(pStr);
78     if (Length == 0)
79         return;
80 
81     /* Trim leading characters */
82     while (i < Length && IsPunctuation(pStr[i]))
83     {
84         i++;
85     }
86 
87     if (i > 0)
88     {
89         Length -= i;
90         memmove(pStr, pStr + i, (Length + 1) * sizeof(WCHAR));
91     }
92 
93     /* Trim trailing characters */
94     while (Length && IsPunctuation(pStr[Length-1]))
95     {
96         pStr[Length-1] = L'\0';
97         --Length;
98     }
99 }
100 
101 /*
102  * Case insensitive variant of wcsstr
103  */
104 static
105 wchar_t * wcsistr(const wchar_t *s, const wchar_t *b)
106 {
107     wchar_t *x;
108     wchar_t *y;
109     wchar_t *c;
110     x = (wchar_t *)s;
111     while (*x)
112     {
113         if (towlower(*x) == towlower(*b))
114         {
115             y = x;
116             c = (wchar_t *)b;
117             while (*y && *c && towlower(*y) == towlower(*c))
118             {
119                 c++;
120                 y++;
121             }
122             if (!*c)
123                 return x;
124         }
125         x++;
126     }
127     return NULL;
128 }
129 
130 static
131 wchar_t * wcsistr_plus(const wchar_t *s, wchar_t *b)
132 {
133     wchar_t * result = wcsistr(s, b);
134     UINT len = wcslen(b);
135     // workarounds
136     if (!result && b[len - 1] == L' ' && wcschr(s, L',') != NULL)
137     {
138         b[len - 1] = L',';
139         result = wcsistr(s, b);
140         b[len - 1] = L' ';
141         if (!result)
142         {
143             b[0] = L',';
144             result = wcsistr(s, b);
145             b[0] = L' ';
146         }
147     }
148     if (!result && b[len - 1] == L' ' && wcschr(s, L'(') != NULL)
149     {
150         b[len - 1] = L'(';
151         result = wcsistr(s, b);
152         b[len - 1] = L' ';
153     }
154     if (!result && b[len - 1] == L' ' && wcschr(s, L'_') != NULL)
155     {
156         b[0] = L'_';
157         result = wcsistr(s, b);
158         b[0] = L' ';
159     }
160     if (!result && b[0] == L' ' && b[len - 1] == L' ' && wcschr(s, L')') != NULL)
161     {
162         b[0] = L')';
163         result = wcsistr(s, b);
164         if (!result && wcschr(s, L'.'))
165         {
166             b[len - 1] = L'.';
167             result = wcsistr(s, b);
168             b[len - 1] = L' ';
169         }
170         b[0] = L' ';
171     }
172     return result;
173 }
174 
175 /*
176  * Replaces full word with another shorter word
177  */
178 static
179 VOID wcsrep(
180     _Inout_ PWSTR pwStr,
181     _In_ PCWSTR pwFind,
182     _In_ PCWSTR pwReplace,
183     _In_ BOOL bReplaceFirstWord)
184 {
185     PWSTR pwsStr, pwsFind, pwsReplace, pwsBuf = NULL;
186     SIZE_T lenStr;
187     SIZE_T lenFind;
188     SIZE_T lenReplace;
189 
190     if (!pwStr || !pwFind || !pwReplace ||
191         wcslen(pwStr) == 0 ||
192         wcslen(pwFind) == 0 ||
193         wcslen(pwFind) < wcslen(pwReplace))
194     {
195         return;
196     }
197     lenStr = wcslen(pwStr) + 2 + 1;
198     lenFind = wcslen(pwFind) + 2 + 1;
199     lenReplace = wcslen(pwReplace) + 2 + 1;
200 
201     pwsStr = HeapAlloc(GetProcessHeap(), 0, lenStr * sizeof(WCHAR));
202     if (!pwsStr)
203     {
204         return;
205     }
206     StringCchCopyW(pwsStr, lenStr, L" ");
207     StringCchCatW(pwsStr, lenStr, pwStr);
208     StringCchCatW(pwsStr, lenStr, L" ");
209 
210     pwsFind = HeapAlloc(GetProcessHeap(), 0, lenFind * sizeof(WCHAR));
211     if (!pwsFind)
212     {
213         goto freeStr;
214     }
215     StringCchCopyW(pwsFind, lenFind, L" ");
216     StringCchCatW(pwsFind, lenFind, pwFind);
217     StringCchCatW(pwsFind, lenFind, L" ");
218 
219     if (!(pwsBuf = wcsistr_plus(pwsStr, pwsFind)))
220     {
221         goto freeFind;
222     }
223     if (!bReplaceFirstWord && pwsBuf - pwsStr < 2)
224     {
225         goto freeFind;
226     }
227 
228     pwsReplace = HeapAlloc(GetProcessHeap(), 0, lenReplace * sizeof(WCHAR));
229     if (!pwsReplace)
230     {
231         goto freeFind;
232     }
233     StringCchCopyW(pwsReplace, lenReplace, L" ");
234     StringCchCatW(pwsReplace, lenReplace, pwReplace);
235     StringCchCatW(pwsReplace, lenReplace, L" ");
236 
237     do
238     {
239         // replace substring
240         memmove(pwsBuf, pwsReplace, (lenReplace - 1) * sizeof(WCHAR));
241         // shift characters
242         memmove(pwsBuf + lenReplace - (wcslen(pwReplace) > 0 ? 1 : 2), pwsBuf + lenFind - 1, (lenStr - lenFind - (pwsBuf - pwsStr) + 1) * sizeof(WCHAR));
243     }
244     while ((pwsBuf = wcsistr_plus(pwsStr, pwsFind)) != NULL);
245 
246     TrimDmiStringW(pwsStr);
247     StringCchCopyW(pwStr, wcslen(pwStr), pwsStr);
248 
249     HeapFree(GetProcessHeap(), 0, pwsReplace);
250 freeFind:
251     HeapFree(GetProcessHeap(), 0, pwsFind);
252 freeStr:
253     HeapFree(GetProcessHeap(), 0, pwsStr);
254 }
255 
256 static
257 BOOL IsGenericSystemName(PCWSTR ven, PCWSTR dev, BOOL * bRemove)
258 {
259     static const GENERIC_NAME Vendors[] =
260     {
261         // some ASUS boards
262         { L"To Be Filled By O.E.M.", FALSE, TRUE },
263         { L"To Be Filled By O.E.M", FALSE, TRUE },
264         { L"System manufacturer", FALSE, TRUE },
265         // some Gigabyte boards
266         { L"Default string", TRUE, TRUE },
267         { L"LTD Delovoy Office", TRUE, FALSE },
268         { L"Motherboard by ZOTAC", TRUE, FALSE },
269         // various boards
270         { L"Type2 - Board Manufacturer", TRUE, TRUE },
271         { L"Type2 - Board Vendor Name1", TRUE, TRUE },
272         { L"BASE_BOARD_MANUFACTURER", TRUE, TRUE },
273         { L"$(DEFAULT_STRING)", TRUE, TRUE },
274         { L"DEPO Computers", TRUE, FALSE },
275         { L"-", TRUE, TRUE },
276         { L"N/A", TRUE, TRUE },
277         { L"OEM", TRUE, TRUE },
278         { L"O.E.M", TRUE, TRUE },
279         { L"empty", TRUE, TRUE },
280         { L"insyde", TRUE, FALSE },
281         { L"Unknow", TRUE, TRUE },
282         { L"Not Applicable", TRUE, TRUE },
283         // distinguish between Oracle and older VirtualBox releases (Sun, etc.)
284         { L"innotek GmbH", TRUE, FALSE },
285     };
286     static const GENERIC_NAME Devices[] =
287     {
288         // some ASUS boards
289         { L"To Be Filled By O.E.M.", FALSE, TRUE },
290         { L"To Be Filled By O.E.M", FALSE, TRUE },
291         { L"All Series", TRUE, TRUE },
292         { L"System Product Name", TRUE, TRUE },
293         { L"System Name", TRUE, TRUE },
294         // some Gigabyte boards
295         { L"Default string", TRUE, TRUE },
296         // some MSI boards
297         { L"Please change product name", TRUE, TRUE },
298         // some Intel boards
299         { L"Computer", TRUE, TRUE },
300         { L"ChiefRiver Platform", TRUE, FALSE },
301         { L"OakTrail Platform", TRUE, FALSE },
302         { L"SharkBay Platform", TRUE, FALSE },
303         { L"HuronRiver Platform", TRUE, FALSE },
304         { L"SandyBridge Platform", TRUE, FALSE },
305         { L"Broadwell Platform", TRUE, FALSE },
306         { L"Kabylake Platform", TRUE, FALSE },
307         { L"Sabine Platform", TRUE, FALSE },
308         // various boards
309         { L"Base Board Product Name", TRUE, TRUE },
310         { L"Base Board Version", TRUE, TRUE },
311         { L"Type2 - Board Product Name1", TRUE, TRUE },
312         { L"Type2 - Board Product Name", TRUE, TRUE },
313         { L"Type2 - Board Version", TRUE, TRUE },
314         { L"MODEL_NAME", TRUE, TRUE },
315         { L"$(DEFAULT_STRING)", TRUE, TRUE },
316         { L"*", TRUE, TRUE },
317         { L"T", TRUE, TRUE },
318         { L"GEG", TRUE, TRUE },
319         { L"N/A", TRUE, TRUE },
320         { L"---", TRUE, TRUE },
321         { L"OEM", TRUE, TRUE },
322         { L"INVA", TRUE, TRUE },
323         { L"O.E.M", TRUE, TRUE },
324         { L"empty", TRUE, TRUE },
325         { L"DNSNB", TRUE, FALSE },
326         { L"12345", TRUE, FALSE },
327         { L"``````", TRUE, TRUE },
328         { L"Uknown", TRUE, TRUE },
329         { L"Desktop", FALSE, TRUE },
330         { L"Invalid", FALSE, TRUE },
331         { L"Reserved", TRUE, TRUE },
332         { L"Not Applicable", TRUE, TRUE },
333         { L"HaierComputer", TRUE, FALSE },
334         { L"DEPO Computers", TRUE, FALSE },
335         { L"InsydeH2O EFI BIOS", TRUE, TRUE },
336         { L"HP All-in-One", TRUE, FALSE },
337         { L"MP Server", TRUE, FALSE },
338         { L"0000000000", TRUE, TRUE },
339         // some Foxconn boards
340         { L"Aquarius Pro, Std, Elt Series", TRUE, FALSE },
341         // some ASUS server boards
342         { L"Aquarius Server", TRUE, FALSE },
343         { L"Aquarius Server G2", TRUE, FALSE },
344         // some Supermicro server boards
345         { L"Super Server", TRUE, FALSE },
346         // some Positivo devices
347         { L"POSITIVO MOBILE", FALSE, FALSE },
348     };
349     BOOL bMatch;
350     UINT i;
351 
352     if (bRemove)
353     {
354         *bRemove = FALSE;
355     }
356     for (i = 0; i < _countof(Vendors); i++)
357     {
358         if (!ven)
359         {
360             break;
361         }
362         if (Vendors[i].bCaseSensitive)
363         {
364             bMatch = !wcscmp(ven, Vendors[i].pwName);
365         }
366         else
367         {
368             bMatch = !wcsicmp(ven, Vendors[i].pwName);
369         }
370         if (bMatch)
371         {
372             if (bRemove)
373             {
374                 *bRemove = Vendors[i].bRemove;
375             }
376             return TRUE;
377         }
378     }
379 
380     for (i = 0; i < _countof(Devices); i++)
381     {
382         if (!dev)
383         {
384             break;
385         }
386         if (Devices[i].bCaseSensitive)
387         {
388             bMatch = !wcscmp(dev, Devices[i].pwName);
389         }
390         else
391         {
392             bMatch = !wcsicmp(dev, Devices[i].pwName);
393         }
394         if (bMatch)
395         {
396             if (bRemove)
397             {
398                 *bRemove = Devices[i].bRemove;
399             }
400             return TRUE;
401         }
402     }
403     return FALSE;
404 }
405 
406 static
407 void AppendSystemFamily(PWSTR pBuf, SIZE_T cchBuf, PCHAR * DmiStrings, PWSTR dev)
408 {
409     static const PCSTR KnownFamilies[] =
410     {
411         "Eee PC",      // ASUS
412         "ThinkPad",    // Lenovo
413         "IdeaPad",     // Lenovo
414         "IdeaCentre",  // Lenovo
415     };
416     static const PCWSTR Aliases[] =
417     {
418         NULL,
419         NULL,
420         NULL,
421         L"IdeaCenter",
422     };
423     UINT i;
424     WCHAR wideStr[128];
425 
426     for (i = 0; i < _countof(KnownFamilies); i++)
427     {
428         StringCchPrintfW(wideStr, _countof(wideStr), L"%S", KnownFamilies[i]);
429 
430         if (wcsistr(dev, wideStr) == NULL &&
431             (!Aliases[i] || wcsistr(dev, Aliases[i]) == NULL) &&
432             DmiStrings[SYS_FAMILY] != NULL &&
433             !stricmp(DmiStrings[SYS_FAMILY], KnownFamilies[i]))
434         {
435             if (wcslen(pBuf) > 0 && wcslen(dev) > 0)
436             {
437                 StringCchCatW(pBuf, cchBuf, L" ");
438             }
439             StringCchCatW(pBuf, cchBuf, wideStr);
440         }
441     }
442 }
443 
444 static
445 BOOL TrimNonPrintable(PCHAR DmiString)
446 {
447     PCHAR c = DmiString;
448     if (!c)
449     {
450         return FALSE;
451     }
452     while (*c)
453     {
454         if (*c >= 0x20 && *c <= 0x7e)
455         {
456             c++;
457         }
458         else
459         {
460             *c = 0;
461             return TRUE;
462         }
463     }
464     return FALSE;
465 }
466 
467 /* TrimNonPrintable function wrapper. It does special preprocessing
468  * so the function returns FALSE in some corner cases, making the parser
469  * use system strings anyway (instead of board strings). */
470 static
471 BOOL TrimNonPrintableProd(PCHAR DmiString)
472 {
473     PCHAR c;
474 
475     if (!DmiString)
476     {
477         return FALSE;
478     }
479 
480     /* Special handling for HP with broken revision */
481     c = strstr(DmiString, "(\xFF\xFF");
482     if (c > DmiString)
483     {
484         *c = 0;
485     }
486 
487     return TrimNonPrintable(DmiString);
488 }
489 
490 BOOL GetSystemName(PWSTR pBuf, SIZE_T cchBuf)
491 {
492     static const VENDOR_LONG_NAME LongNames[] =
493     {
494         { L"ASUSTeK", L"ASUS" },
495         { L"First International Computer", L"FIC" },
496         { L"Hewlett-Packard", L"HP" },
497         { L"MICRO-STAR", L"MSI" },
498         { L"SGI.COM", L"SGI" },
499         { L"Silicon Graphics International", L"SGI" },
500         { L"Intel(R) Client Systems", L"Intel" },
501         { L"InformationComputerSystems", L"ICS" },
502         { L"Bernecker + Rainer  Industrie-Elektronik", L"Bernecker & Rainer" },
503         { L"CHUWI INNOVATION AND TECHNOLOGY", L"CHUWI" },
504         { L"CHUWI INNOVATION LIMITED", L"CHUWI" },
505         { L"CHUWI  INNOVATION  LIMITED", L"CHUWI" },
506         { L"http://www.abit.com.tw/", L"ABIT" },
507         { L"http:\\\\www.abit.com.tw", L"ABIT" },
508         { L"www.abit.com.tw", L"ABIT" },
509         { L"CASPER BILGISAYAR SISTEMLERI A.S", L"Casper" },
510         { L"Colorful Technology And Development", L"Colorful" },
511         { L"Colorful Yu Gong Technology And Development", L"Colorful Yu Gong" },
512         { L"HaierComputer", L"Haier" },
513         { L"Haier Information Technology (Shen Zhen)", L"Haier" },
514         { L"HASEECOMPUTERS", L"Hasee" },
515         { L"HELIOS BUSINESS COMPUTER", L"HELIOS" },
516         { L"Shanghai Zongzhi InfoTech", L"Zongzhi" },
517         { L"TSING HUA TONGFANG CO.,LTD", L"TSINGHUA TONGFANG" },
518         { L"Yeston Digital Technology Co.,LTD", L"Yeston" },
519     };
520     static const REDUNDANT_WORD RedundantWords[] =
521     {
522         { L"Corporation", FALSE },
523         { L"Communication", FALSE },
524         { L"Computer", FALSE },
525         { L"Computers", FALSE },
526         { L"Group", FALSE },
527         { L"Cloud", FALSE },
528         { L"Center", FALSE },
529         { L"Systems", FALSE },
530         { L"Microsystems", FALSE },
531         { L"Infosystems", FALSE },
532         { L"Digital", FALSE },
533         { L"Electronics", FALSE },
534         { L"Electric", FALSE },
535         { L"Elektronik", FALSE },
536         { L"Software", FALSE },
537         { L"Foundation", FALSE },
538         { L"International", FALSE },
539         { L"Interantonal", FALSE }, // on purpose (some MSI boards)
540         { L"INTERANTIONAL", FALSE }, // on purpose (some MSI boards)
541         { L"Industrial", FALSE },
542         { L"Industrie", FALSE },
543         { L"Information", FALSE },
544         { L"Informatica", FALSE },
545         { L"Produkte", FALSE },
546         { L"Technology", FALSE },
547         { L"Tecohnology", FALSE }, // on purpose (some Gigabyte boards)
548         { L"Technologies", FALSE },
549         { L"Tecnologia", FALSE },
550         { L"Limited", FALSE },
551         { L"Int", FALSE },
552         { L"Inc", FALSE },
553         { L"Co", FALSE },
554         { L"Corp", FALSE },
555         { L"Crop", FALSE },
556         { L"LLC", FALSE },
557         { L"Ltd", FALSE },
558         { L"LTDA", FALSE },
559         { L"GmbH", FALSE },
560         { L"S.p.A", FALSE },
561         { L"A.S.", FALSE },
562         { L"S.A", FALSE },
563         { L"S.A.S", FALSE },
564         { L"S/A", FALSE },
565         { L"SA", FALSE },
566         { L"SAS", FALSE },
567         { L"BV", FALSE },
568         { L"AG", FALSE },
569         { L"OOO", TRUE },
570         { L"CJSC", FALSE },
571         { L"INT'L", FALSE },
572         { L"INTL", FALSE },
573         { L"plc", FALSE },
574     };
575     PVOID SMBiosBuf;
576     PCHAR DmiStrings[ID_STRINGS_MAX] = { 0 };
577     WCHAR ven[512], dev[512];
578     CHAR tmpstr[512];
579     BOOL bTrimProduct, bTrimFamily, bGenericName, bRemove;
580     UINT i;
581     PWCHAR j;
582 
583     SMBiosBuf = LoadSMBiosData(DmiStrings);
584     if (!SMBiosBuf)
585     {
586         return FALSE;
587     }
588 
589     TrimNonPrintable(DmiStrings[SYS_VENDOR]);
590     bTrimProduct = TrimNonPrintableProd(DmiStrings[SYS_PRODUCT]);
591     TrimNonPrintable(DmiStrings[SYS_VERSION]);
592     bTrimFamily = TrimNonPrintable(DmiStrings[SYS_FAMILY]);
593     TrimNonPrintable(DmiStrings[BOARD_VENDOR]);
594     TrimNonPrintable(DmiStrings[BOARD_NAME]);
595     TrimNonPrintable(DmiStrings[BOARD_VERSION]);
596 
597     if (bTrimProduct)
598     {
599         if (DmiStrings[SYS_FAMILY] && !bTrimFamily)
600         {
601             DmiStrings[SYS_PRODUCT] = DmiStrings[SYS_FAMILY];
602             bTrimProduct = FALSE;
603         }
604     }
605 
606     GetSMBiosStringW(DmiStrings[SYS_VENDOR], ven, _countof(ven), TRUE);
607     GetSMBiosStringW(DmiStrings[SYS_PRODUCT], dev, _countof(dev), TRUE);
608     bGenericName = IsGenericSystemName(ven, dev, NULL) || bTrimProduct;
609 
610     if (wcslen(dev) == 0 ||
611         !wcscmp(dev, ven) ||
612         bGenericName)
613     {
614         BOOL bGenericVen = FALSE, bRemoveVen = FALSE, bGenericDev = (wcslen(dev) == 0 || !wcscmp(dev, ven) || bTrimProduct);
615 
616         if (bGenericName && IsGenericSystemName(ven, NULL, &bRemove))
617         {
618             if (bRemove)
619             {
620                 *ven = 0;
621             }
622             bGenericVen = TRUE;
623             bRemoveVen = bRemove;
624         }
625         if (bGenericName && IsGenericSystemName(NULL, dev, &bRemove))
626         {
627             if (bRemove)
628             {
629                 *dev = 0;
630             }
631             bGenericDev = TRUE;
632         }
633         // system strings are unusable, use board strings
634         if (DmiStrings[BOARD_VENDOR] != NULL || !bGenericName)
635         {
636             if ((DmiStrings[BOARD_VENDOR] &&
637                 strlen(DmiStrings[BOARD_VENDOR]) >= 2 &&
638                 strstr(DmiStrings[BOARD_VENDOR], "  ") != DmiStrings[BOARD_VENDOR]) ||
639                 bGenericVen)
640             {
641                 GetSMBiosStringW(DmiStrings[BOARD_VENDOR], ven, _countof(ven), TRUE);
642             }
643             GetSMBiosStringW(DmiStrings[BOARD_NAME], dev, _countof(dev), TRUE);
644 
645             if (IsGenericSystemName(ven, NULL, &bRemove) && bRemove)
646             {
647                 *ven = 0;
648 
649                 if (bGenericVen && !bRemoveVen)
650                 {
651                     GetSMBiosStringW(DmiStrings[SYS_VENDOR], ven, _countof(ven), TRUE);
652                 }
653             }
654             if (IsGenericSystemName(NULL, dev, &bRemove) && bRemove)
655             {
656                 *dev = 0;
657 
658                 if (!bGenericDev)
659                 {
660                     GetSMBiosStringW(DmiStrings[SYS_PRODUCT], dev, _countof(dev), TRUE);
661                 }
662             }
663             if (wcslen(dev) == 0 &&
664                 DmiStrings[SYS_VERSION] != NULL)
665             {
666                 GetSMBiosStringW(DmiStrings[SYS_VERSION], dev, _countof(dev), TRUE);
667 
668                 if (IsGenericSystemName(NULL, dev, &bRemove) && bRemove)
669                 {
670                     *dev = 0;
671                 }
672             }
673             if (wcslen(dev) == 0 &&
674                 DmiStrings[BOARD_VERSION] != NULL)
675             {
676                 GetSMBiosStringW(DmiStrings[BOARD_VERSION], dev, _countof(dev), TRUE);
677 
678                 if (IsGenericSystemName(NULL, dev, &bRemove) && bRemove)
679                 {
680                     *dev = 0;
681                 }
682             }
683         }
684         else if (DmiStrings[BOARD_NAME] != NULL)
685         {
686             GetSMBiosStringW(DmiStrings[BOARD_NAME], dev, _countof(dev), TRUE);
687 
688             if (IsGenericSystemName(NULL, dev, &bRemove) && bRemove)
689             {
690                 *dev = 0;
691             }
692         }
693 
694         if (wcslen(ven) == 0 && wcslen(dev) == 0)
695         {
696             // board strings are empty, use BIOS vendor string
697             GetSMBiosStringW(DmiStrings[BIOS_VENDOR], ven, _countof(ven), TRUE);
698         }
699     }
700     else
701     {
702         if (wcslen(ven) < 2)
703         {
704             GetSMBiosStringW(DmiStrings[BOARD_VENDOR], ven, _countof(ven), TRUE);
705 
706             if (IsGenericSystemName(ven, NULL, &bRemove) && bRemove)
707             {
708                 *ven = 0;
709             }
710         }
711     }
712 
713     // workaround for LORD ELECTRONICS
714     if (((j = wcsstr(ven, L" ")) != NULL) && (j - ven > 2))
715     {
716         i = j - ven;
717         if (!wcsncmp(ven + wcslen(ven) - i, ven, i))
718         {
719             ven[wcslen(ven) - i] = L'\0';
720         }
721     }
722 
723     // make vendor strings shorter
724     for (i = 0; i < _countof(LongNames); i++)
725     {
726         if (wcsstr(dev, LongNames[i].pwLongName) == dev)
727         {
728             // swap ven and dev
729             StringCchCopyW(pBuf, cchBuf, ven);
730             StringCchCopyW(ven, _countof(ven), dev);
731             StringCchCopyW(dev, _countof(dev), pBuf);
732         }
733         wcsrep(ven, LongNames[i].pwLongName, LongNames[i].pwShortName, TRUE);
734     }
735 
736     // remove redundant words
737     for (i = 0; i < _countof(RedundantWords); i++)
738     {
739         wcsrep(ven, RedundantWords[i].pwStr, L"", RedundantWords[i].bReplaceFirstWord);
740     }
741     for (i = 0; i < _countof(RedundantWords); i++)
742     {
743         StringCchCopyW(pBuf, cchBuf, RedundantWords[i].pwStr);
744         StringCchCatW(pBuf, cchBuf, L".");
745         wcsrep(ven, pBuf, L"", RedundantWords[i].bReplaceFirstWord);
746     }
747 
748     // workaround for LENOVO notebooks
749     if (!wcsicmp(ven, L"LENOVO"))
750     {
751         StringCchCopyW(ven, _countof(ven), L"Lenovo");
752 
753         if (DmiStrings[SYS_VERSION] != NULL)
754         {
755             if (!strncmp(DmiStrings[SYS_VERSION], "ThinkPad   ", 11))
756             {
757                 DmiStrings[SYS_VERSION][8] = L'\0';
758             }
759             if (wcslen(dev) > 0 &&
760                 (!strcmp(DmiStrings[SYS_VERSION], "IdeaCentre") ||
761                 !strcmp(DmiStrings[SYS_VERSION], "ThinkPad")))
762             {
763                 DmiStrings[SYS_FAMILY] = DmiStrings[SYS_VERSION];
764                 DmiStrings[SYS_VERSION] = NULL;
765             }
766             else
767             {
768                 StringCchCopyA(tmpstr, _countof(tmpstr), DmiStrings[SYS_VERSION]);
769                 _strupr(tmpstr);
770             }
771         }
772 
773         if (DmiStrings[SYS_VERSION] != NULL &&
774             strcmp(tmpstr, " ") &&
775             strcmp(tmpstr, "LENOVO") &&
776             strstr(tmpstr, "LENOVO   ") == NULL &&
777             strstr(tmpstr, "LENOVO PRODUCT") == NULL &&
778             strstr(tmpstr, "LENOVOPRODUCT") == NULL &&
779             strstr(tmpstr, "INVALID") == NULL &&
780             strncmp(tmpstr, "   ", 3) &&
781             (strlen(tmpstr) >= 3 || !IsDigitStrA(tmpstr)) &&
782             strstr(DmiStrings[SYS_VERSION], "Rev ") == NULL &&
783             strstr(DmiStrings[SYS_VERSION], "1.") == NULL &&
784             wcsistr(dev, L"System ") == NULL && // includes System x and ThinkSystem
785             wcsistr(dev, L"IdeaPad ") == NULL &&
786             wcsistr(dev, L"ThinkServer ") == NULL)
787         {
788             GetSMBiosStringW(DmiStrings[SYS_VERSION], dev, _countof(dev), TRUE);
789         }
790 
791         if (wcsstr(dev, L"Lenovo-") == dev)
792         {
793             // replace "-" with space
794             dev[6] = L' ';
795         }
796 
797         if (!wcscmp(dev, L"Lenovo"))
798         {
799             GetSMBiosStringW(DmiStrings[BOARD_NAME], dev, _countof(dev), TRUE);
800         }
801     }
802     if (!wcscmp(ven, L"IBM") &&
803         DmiStrings[SYS_VERSION] != NULL &&
804         (strstr(DmiStrings[SYS_VERSION], "ThinkPad ") != NULL ||
805          strstr(DmiStrings[SYS_VERSION], "ThinkCentre ") != NULL))
806     {
807         GetSMBiosStringW(DmiStrings[SYS_VERSION], dev, _countof(dev), TRUE);
808     }
809 
810     // workaround for DEXP
811     if (!wcscmp(ven, L"DEXP"))
812     {
813         if (DmiStrings[SYS_PRODUCT] != NULL &&
814             DmiStrings[SYS_VERSION] != NULL &&
815             (!stricmp(DmiStrings[SYS_PRODUCT], "Tablet PC") ||
816              !stricmp(DmiStrings[SYS_PRODUCT], "Notebook") ||
817              !stricmp(DmiStrings[SYS_PRODUCT], "Decktop")))
818         {
819             GetSMBiosStringW(DmiStrings[SYS_VERSION], dev, _countof(dev), TRUE);
820         }
821     }
822 
823     // workaround for Razer Blade
824     if (!wcscmp(ven, L"Razer") && !wcscmp(dev, L"Blade"))
825     {
826         if (DmiStrings[SYS_VERSION] != NULL)
827         {
828             StringCchCopyW(ven, _countof(ven), L"Razer Blade");
829             GetSMBiosStringW(DmiStrings[SYS_VERSION], dev, _countof(dev), TRUE);
830         }
831     }
832 
833     // workaround for MSI motherboards
834     if (!wcscmp(ven, L"MSI") &&
835         wcsstr(dev, L"MS-") != NULL &&
836         DmiStrings[BOARD_NAME] != NULL &&
837         strstr(DmiStrings[BOARD_NAME], "(MS-") != NULL)
838     {
839         GetSMBiosStringW(DmiStrings[BOARD_NAME], dev, _countof(dev), TRUE);
840     }
841     if (wcslen(ven) == 0 &&
842         wcsstr(dev, L"MS-") == dev)
843     {
844         StringCchCopyW(ven, _countof(ven), L"MSI");
845     }
846 
847     // trim redundant characters
848     TrimPunctuation(ven);
849     TrimPunctuation(dev);
850 
851     if (wcsistr(dev, ven) == dev ||
852         (!wcscmp(ven, L"ASUS") && wcsstr(dev, L"ASUS") != NULL) ||
853         (!wcscmp(ven, L"HP") && wcsstr(dev, L" by HP") != NULL) ||
854         (!wcscmp(ven, L"INTEL") && wcsstr(dev, L" INTEL") != NULL))
855     {
856         // device string contains vendor string, use second only
857         StringCchCopyW(pBuf, cchBuf, dev);
858     }
859     else
860     {
861         if (wcslen(ven) > 0 && wcslen(dev) > 0 && (j = wcschr(dev, L' ')))
862         {
863             // check if vendor string ends with first word of device string
864             i = j - dev;
865             if (wcslen(ven) > i && !_wcsnicmp(ven + wcslen(ven) - i, dev, i))
866             {
867                 ven[wcslen(ven) - i] = L'\0';
868                 TrimPunctuation(ven);
869             }
870         }
871         StringCchCopyW(pBuf, cchBuf, ven);
872         AppendSystemFamily(pBuf, cchBuf, DmiStrings, dev);
873         if (wcslen(pBuf) > 0 && wcslen(dev) > 0)
874         {
875             StringCchCatW(pBuf, cchBuf, L" ");
876         }
877         StringCchCatW(pBuf, cchBuf, dev);
878     }
879 
880     FreeSMBiosData(SMBiosBuf);
881 
882     return (wcslen(pBuf) > 0);
883 }
884