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