1 /* 2 * PROJECT: ReactOS System Control Panel Applet 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: dll/cpl/sysdm/smbios.c 5 * PURPOSE: Retrieve device or motherboard name identifier from DMI/SMBIOS 6 * COPYRIGHT: Copyright 2018 Stanislav Motylkov <x86corez@gmail.com> 7 * 8 */ 9 10 #include "precomp.h" 11 12 #include <strsafe.h> 13 #include <udmihelp.h> 14 15 typedef struct GENERIC_NAME 16 { 17 PCWSTR pwName; 18 BOOL bCaseSensitive; 19 } GENERIC_NAME; 20 21 typedef struct VENDOR_LONG_NAME 22 { 23 PCWSTR pwLongName; 24 PCWSTR pwShortName; 25 } VENDOR_LONG_NAME; 26 27 typedef struct REDUNDANT_WORD 28 { 29 PCWSTR pwStr; 30 BOOL bReplaceFirstWord; 31 } REDUNDANT_WORD; 32 33 static 34 BOOL 35 IsPunctuation( 36 _In_ WCHAR chr) 37 { 38 return (chr <= L' ' || chr == L'.' || chr == L','); 39 } 40 41 /* 42 * Trim redundant characters 43 */ 44 static 45 VOID 46 TrimPunctuation( 47 _Inout_ PWSTR pStr) 48 { 49 SIZE_T Length; 50 UINT i = 0; 51 52 if (!pStr) 53 return; 54 55 Length = wcslen(pStr); 56 if (Length == 0) 57 return; 58 59 /* Trim leading characters */ 60 while (i < Length && IsPunctuation(pStr[i])) 61 { 62 i++; 63 } 64 65 if (i > 0) 66 { 67 Length -= i; 68 memmove(pStr, pStr + i, (Length + 1) * sizeof(WCHAR)); 69 } 70 71 /* Trim trailing characters */ 72 while (Length && IsPunctuation(pStr[Length-1])) 73 { 74 pStr[Length-1] = L'\0'; 75 --Length; 76 } 77 } 78 79 /* 80 * Case insensitive variant of wcsstr 81 */ 82 static 83 wchar_t * wcsistr(const wchar_t *s, const wchar_t *b) 84 { 85 wchar_t *x; 86 wchar_t *y; 87 wchar_t *c; 88 x = (wchar_t *)s; 89 while (*x) 90 { 91 if (towlower(*x) == towlower(*b)) 92 { 93 y = x; 94 c = (wchar_t *)b; 95 while (*y && *c && towlower(*y) == towlower(*c)) 96 { 97 c++; 98 y++; 99 } 100 if (!*c) 101 return x; 102 } 103 x++; 104 } 105 return NULL; 106 } 107 108 static 109 wchar_t * wcsistr_plus(const wchar_t *s, wchar_t *b) 110 { 111 wchar_t * result = wcsistr(s, b); 112 UINT len = wcslen(b); 113 // workarounds 114 if (!result && b[len - 1] == L' ' && wcschr(s, L',') != NULL) 115 { 116 b[len - 1] = L','; 117 result = wcsistr(s, b); 118 b[len - 1] = L' '; 119 if (!result) 120 { 121 b[0] = L','; 122 result = wcsistr(s, b); 123 b[0] = L' '; 124 } 125 } 126 if (!result && b[len - 1] == L' ' && wcschr(s, L'(') != NULL) 127 { 128 b[len - 1] = L'('; 129 result = wcsistr(s, b); 130 b[len - 1] = L' '; 131 } 132 if (!result && b[len - 1] == L' ' && wcschr(s, L'_') != NULL) 133 { 134 b[0] = L'_'; 135 result = wcsistr(s, b); 136 b[0] = L' '; 137 } 138 if (!result && b[0] == L' ' && b[len - 1] == L' ' && wcschr(s, L')') != NULL) 139 { 140 b[0] = L')'; 141 result = wcsistr(s, b); 142 b[0] = L' '; 143 } 144 return result; 145 } 146 147 /* 148 * Replaces full word with another shorter word 149 */ 150 static 151 VOID wcsrep( 152 _Inout_ PWSTR pwStr, 153 _In_ PCWSTR pwFind, 154 _In_ PCWSTR pwReplace, 155 _In_ BOOL bReplaceFirstWord) 156 { 157 PWSTR pwsStr, pwsFind, pwsReplace, pwsBuf = NULL; 158 SIZE_T lenStr; 159 SIZE_T lenFind; 160 SIZE_T lenReplace; 161 162 if (!pwStr || !pwFind || !pwReplace || 163 wcslen(pwStr) == 0 || 164 wcslen(pwFind) == 0 || 165 wcslen(pwFind) < wcslen(pwReplace)) 166 { 167 return; 168 } 169 lenStr = wcslen(pwStr) + 2 + 1; 170 lenFind = wcslen(pwFind) + 2 + 1; 171 lenReplace = wcslen(pwReplace) + 2 + 1; 172 173 pwsStr = HeapAlloc(GetProcessHeap(), 0, lenStr * sizeof(WCHAR)); 174 if (!pwsStr) 175 { 176 return; 177 } 178 StringCchCopyW(pwsStr, lenStr, L" "); 179 StringCchCatW(pwsStr, lenStr, pwStr); 180 StringCchCatW(pwsStr, lenStr, L" "); 181 182 pwsFind = HeapAlloc(GetProcessHeap(), 0, lenFind * sizeof(WCHAR)); 183 if (!pwsFind) 184 { 185 goto freeStr; 186 } 187 StringCchCopyW(pwsFind, lenFind, L" "); 188 StringCchCatW(pwsFind, lenFind, pwFind); 189 StringCchCatW(pwsFind, lenFind, L" "); 190 191 if (!(pwsBuf = wcsistr_plus(pwsStr, pwsFind))) 192 { 193 goto freeFind; 194 } 195 if (!bReplaceFirstWord && pwsBuf - pwsStr < 2) 196 { 197 goto freeFind; 198 } 199 200 pwsReplace = HeapAlloc(GetProcessHeap(), 0, lenReplace * sizeof(WCHAR)); 201 if (!pwsReplace) 202 { 203 goto freeFind; 204 } 205 StringCchCopyW(pwsReplace, lenReplace, L" "); 206 StringCchCatW(pwsReplace, lenReplace, pwReplace); 207 StringCchCatW(pwsReplace, lenReplace, L" "); 208 209 do 210 { 211 // replace substring 212 memmove(pwsBuf, pwsReplace, (lenReplace - 1) * sizeof(WCHAR)); 213 // shift characters 214 memmove(pwsBuf + lenReplace - (wcslen(pwReplace) > 0 ? 1 : 2), pwsBuf + lenFind - 1, (lenStr - lenFind - (pwsBuf - pwsStr) + 1) * sizeof(WCHAR)); 215 } 216 while ((pwsBuf = wcsistr_plus(pwsStr, pwsFind)) != NULL); 217 218 TrimDmiStringW(pwsStr); 219 StringCchCopyW(pwStr, wcslen(pwStr), pwsStr); 220 221 HeapFree(GetProcessHeap(), 0, pwsReplace); 222 freeFind: 223 HeapFree(GetProcessHeap(), 0, pwsFind); 224 freeStr: 225 HeapFree(GetProcessHeap(), 0, pwsStr); 226 } 227 228 static 229 BOOL IsGenericSystemName(PCWSTR ven, PCWSTR dev) 230 { 231 static const GENERIC_NAME Vendors[] = 232 { 233 { L"To Be Filled By O.E.M.", FALSE }, // some ASUS boards 234 { L"System manufacturer", TRUE }, // some ASUS boards 235 { L"Default string", TRUE }, // some Gigabyte boards 236 { L"LTD Delovoy Office", TRUE }, // some Gigabyte boards 237 { L"O.E.M", TRUE }, // some AMD boards 238 { L"DEPO Computers", TRUE }, // various boards 239 }; 240 static const GENERIC_NAME Devices[] = 241 { 242 { L"To Be Filled By O.E.M.", FALSE }, // some ASUS boards 243 { L"All Series", TRUE }, // some ASUS boards 244 { L"System Product Name", TRUE }, // some ASUS boards 245 { L"Default string", TRUE }, // some Gigabyte boards 246 { L"Please change product name", TRUE }, // some MSI boards 247 { L"Computer", TRUE }, // some Intel boards 248 { L"ChiefRiver Platform", TRUE }, // some Intel boards 249 { L"SharkBay Platform", TRUE }, // some Intel boards 250 { L"HuronRiver Platform", TRUE }, // some Intel boards 251 { L"SandyBridge Platform", TRUE }, // some Intel boards 252 { L"Broadwell Platform", TRUE }, // some LG boards 253 { L"Sabine Platform", TRUE }, // some AMD boards 254 { L"O.E.M", TRUE }, // some AMD boards 255 { L"*", TRUE }, // various boards 256 { L"GEG", TRUE }, // various boards 257 { L"OEM", TRUE }, // various boards 258 { L"DEPO Computers", TRUE }, // various boards 259 { L"Aquarius Pro, Std, Elt Series", TRUE }, // some Foxconn boards 260 { L"Aquarius Server", TRUE }, // some ASUS server boards 261 { L"Aquarius Server G2", TRUE }, // some ASUS server boards 262 { L"Super Server", TRUE }, // some Supermicro server boards 263 { L"POSITIVO MOBILE", FALSE }, // some Positivo devices 264 }; 265 BOOL bMatch; 266 UINT i; 267 268 for (i = 0; i < _countof(Vendors); i++) 269 { 270 if (!ven) 271 { 272 break; 273 } 274 if (Vendors[i].bCaseSensitive) 275 { 276 bMatch = !wcscmp(ven, Vendors[i].pwName); 277 } 278 else 279 { 280 bMatch = !wcsicmp(ven, Vendors[i].pwName); 281 } 282 if (bMatch) 283 { 284 return TRUE; 285 } 286 } 287 288 for (i = 0; i < _countof(Devices); i++) 289 { 290 if (!dev) 291 { 292 break; 293 } 294 if (Devices[i].bCaseSensitive) 295 { 296 bMatch = !wcscmp(dev, Devices[i].pwName); 297 } 298 else 299 { 300 bMatch = !wcsicmp(dev, Devices[i].pwName); 301 } 302 if (bMatch) 303 { 304 return TRUE; 305 } 306 } 307 return FALSE; 308 } 309 310 static 311 void AppendSystemFamily(PWSTR pBuf, SIZE_T cchBuf, PCHAR * DmiStrings, PWSTR dev) 312 { 313 static const PCSTR KnownFamilies[] = 314 { 315 "Eee PC", // ASUS 316 "IdeaPad", // Lenovo 317 "IdeaCentre", // Lenovo 318 }; 319 static const PCWSTR Aliases[] = 320 { 321 NULL, 322 NULL, 323 L"IdeaCenter", 324 }; 325 UINT i; 326 WCHAR wideStr[128]; 327 328 for (i = 0; i < _countof(KnownFamilies); i++) 329 { 330 StringCchPrintfW(wideStr, _countof(wideStr), L"%S", KnownFamilies[i]); 331 332 if (wcsistr(dev, wideStr) == NULL && 333 (!Aliases[i] || wcsistr(dev, Aliases[i]) == NULL) && 334 DmiStrings[SYS_FAMILY] != NULL && 335 !stricmp(DmiStrings[SYS_FAMILY], KnownFamilies[i])) 336 { 337 if (wcslen(pBuf) > 0 && wcslen(dev) > 0) 338 { 339 StringCchCatW(pBuf, cchBuf, L" "); 340 } 341 StringCchCatW(pBuf, cchBuf, wideStr); 342 } 343 } 344 } 345 346 BOOL GetSystemName(PWSTR pBuf, SIZE_T cchBuf) 347 { 348 static const VENDOR_LONG_NAME LongNames[] = 349 { 350 { L"ASUSTeK", L"ASUS" }, 351 { L"First International Computer", L"FIC" }, 352 { L"Hewlett-Packard", L"HP" }, 353 { L"MICRO-STAR", L"MSI" }, 354 { L"SGI.COM", L"SGI" }, 355 { L"Silicon Graphics International", L"SGI" }, 356 { L"InformationComputerSystems", L"ICS" }, 357 { L"CHUWI INNOVATION AND TECHNOLOGY", L"CHUWI" }, 358 { L"http://www.abit.com.tw/", L"ABIT" }, 359 { L"www.abit.com.tw", L"ABIT" }, 360 { L"Colorful Technology And Development", L"Colorful" }, 361 { L"HaierComputer", L"Haier" }, 362 }; 363 static const REDUNDANT_WORD RedundantWords[] = 364 { 365 { L"Corporation", FALSE }, 366 { L"Computer", FALSE }, 367 { L"Computers", FALSE }, 368 { L"Group", FALSE }, 369 { L"Cloud", FALSE }, 370 { L"Center", FALSE }, 371 { L"Systems", FALSE }, 372 { L"Microsystems", FALSE }, 373 { L"Infosystems", FALSE }, 374 { L"Electronics", FALSE }, 375 { L"Electric", FALSE }, 376 { L"Software", FALSE }, 377 { L"International", FALSE }, 378 { L"Interantonal", FALSE }, // on purpose (some MSI boards) 379 { L"Industrial", FALSE }, 380 { L"Information", FALSE }, 381 { L"Technology", FALSE }, 382 { L"Tecohnology", FALSE }, // on purpose (some Gigabyte boards) 383 { L"Technologies", FALSE }, 384 { L"Limited", FALSE }, 385 { L"Int", FALSE }, 386 { L"Inc", FALSE }, 387 { L"Co", FALSE }, 388 { L"Corp", FALSE }, 389 { L"Crop", FALSE }, 390 { L"Ltd", FALSE }, 391 { L"GmbH", FALSE }, 392 { L"S.p.A", FALSE }, 393 { L"S.A", FALSE }, 394 { L"SA", FALSE }, 395 { L"SAS", FALSE }, 396 { L"BV", FALSE }, 397 { L"AG", FALSE }, 398 { L"OOO", TRUE }, 399 { L"CJSC", FALSE }, 400 { L"INT'L", FALSE }, 401 { L"plc", FALSE }, 402 }; 403 PVOID SMBiosBuf; 404 PCHAR DmiStrings[ID_STRINGS_MAX] = { 0 }; 405 WCHAR ven[512], dev[512]; 406 BOOL bGenericName; 407 UINT i; 408 PWCHAR j; 409 410 SMBiosBuf = LoadSMBiosData(DmiStrings); 411 if (!SMBiosBuf) 412 { 413 return FALSE; 414 } 415 416 GetSMBiosStringW(DmiStrings[SYS_VENDOR], ven, _countof(ven), TRUE); 417 GetSMBiosStringW(DmiStrings[SYS_PRODUCT], dev, _countof(dev), TRUE); 418 bGenericName = IsGenericSystemName(ven, dev); 419 420 if (wcslen(dev) == 0 || 421 !wcscmp(dev, ven) || 422 bGenericName) 423 { 424 // system strings are unusable, use board strings 425 if (DmiStrings[BOARD_VENDOR] != NULL || !bGenericName) 426 { 427 if ((DmiStrings[BOARD_VENDOR] && 428 strlen(DmiStrings[BOARD_VENDOR]) >= 2 && 429 strstr(DmiStrings[BOARD_VENDOR], " ") != DmiStrings[BOARD_VENDOR]) || 430 IsGenericSystemName(ven, NULL)) 431 { 432 GetSMBiosStringW(DmiStrings[BOARD_VENDOR], ven, _countof(ven), TRUE); 433 } 434 GetSMBiosStringW(DmiStrings[BOARD_NAME], dev, _countof(dev), TRUE); 435 436 if (IsGenericSystemName(ven, NULL)) 437 { 438 *ven = 0; 439 } 440 if (IsGenericSystemName(NULL, dev)) 441 { 442 *dev = 0; 443 } 444 if (wcslen(dev) == 0 && 445 DmiStrings[SYS_VERSION] != NULL) 446 { 447 GetSMBiosStringW(DmiStrings[SYS_VERSION], dev, _countof(dev), TRUE); 448 449 if (IsGenericSystemName(NULL, dev)) 450 { 451 *dev = 0; 452 } 453 } 454 if (wcslen(dev) == 0 && 455 DmiStrings[BOARD_VERSION] != NULL) 456 { 457 GetSMBiosStringW(DmiStrings[BOARD_VERSION], dev, _countof(dev), TRUE); 458 459 if (IsGenericSystemName(NULL, dev)) 460 { 461 *dev = 0; 462 } 463 } 464 } 465 466 if (wcslen(ven) == 0 && wcslen(dev) == 0) 467 { 468 // board strings are empty, use BIOS vendor string 469 GetSMBiosStringW(DmiStrings[BIOS_VENDOR], ven, _countof(ven), TRUE); 470 } 471 } 472 else 473 { 474 if (wcslen(ven) < 2) 475 { 476 GetSMBiosStringW(DmiStrings[BOARD_VENDOR], ven, _countof(ven), TRUE); 477 478 if (IsGenericSystemName(ven, NULL)) 479 { 480 *ven = 0; 481 } 482 } 483 } 484 485 // workaround for LORD ELECTRONICS 486 if (((j = wcsstr(ven, L" ")) != NULL) && (j - ven > 2)) 487 { 488 i = j - ven; 489 if (!wcsncmp(ven + wcslen(ven) - i, ven, i)) 490 { 491 ven[wcslen(ven) - i] = L'\0'; 492 } 493 } 494 495 // make vendor strings shorter 496 for (i = 0; i < _countof(LongNames); i++) 497 { 498 wcsrep(ven, LongNames[i].pwLongName, LongNames[i].pwShortName, TRUE); 499 } 500 501 // remove redundant words 502 for (i = 0; i < _countof(RedundantWords); i++) 503 { 504 wcsrep(ven, RedundantWords[i].pwStr, L"", RedundantWords[i].bReplaceFirstWord); 505 } 506 for (i = 0; i < _countof(RedundantWords); i++) 507 { 508 StringCchCopyW(pBuf, cchBuf, RedundantWords[i].pwStr); 509 StringCchCatW(pBuf, cchBuf, L"."); 510 wcsrep(ven, pBuf, L"", RedundantWords[i].bReplaceFirstWord); 511 } 512 513 // workaround for LENOVO notebooks 514 if (!wcscmp(ven, L"LENOVO")) 515 { 516 StringCchCopyW(ven, _countof(ven), L"Lenovo"); 517 518 if (DmiStrings[SYS_VERSION] != NULL && 519 stricmp(DmiStrings[SYS_VERSION], "Lenovo") && 520 stricmp(DmiStrings[SYS_VERSION], "Lenovo Product") && 521 stricmp(DmiStrings[SYS_VERSION], " ") && 522 _strnicmp(DmiStrings[SYS_VERSION], " ", 3) && 523 wcsistr(dev, L"IdeaPad ") == NULL && 524 wcsistr(dev, L"ThinkServer ") == NULL) 525 { 526 GetSMBiosStringW(DmiStrings[SYS_VERSION], dev, _countof(dev), TRUE); 527 } 528 529 if (wcsstr(dev, L"Lenovo-") == dev) 530 { 531 // replace "-" with space 532 dev[6] = L' '; 533 } 534 535 if (!wcscmp(dev, L"Lenovo")) 536 { 537 GetSMBiosStringW(DmiStrings[BOARD_NAME], dev, _countof(dev), TRUE); 538 } 539 } 540 if (!wcscmp(ven, L"IBM") && 541 DmiStrings[SYS_VERSION] != NULL && 542 strstr(DmiStrings[SYS_VERSION], "ThinkPad ") != NULL) 543 { 544 GetSMBiosStringW(DmiStrings[SYS_VERSION], dev, _countof(dev), TRUE); 545 } 546 547 // workaround for DEXP 548 if (!wcscmp(ven, L"DEXP")) 549 { 550 if (DmiStrings[SYS_PRODUCT] != NULL && 551 !stricmp(DmiStrings[SYS_PRODUCT], "Tablet PC") && 552 DmiStrings[SYS_VERSION] != NULL) 553 { 554 GetSMBiosStringW(DmiStrings[SYS_VERSION], dev, _countof(dev), TRUE); 555 } 556 } 557 558 // workaround for Razer Blade 559 if (!wcscmp(ven, L"Razer") && !wcscmp(dev, L"Blade")) 560 { 561 if (DmiStrings[SYS_VERSION] != NULL) 562 { 563 StringCchCopyW(ven, _countof(ven), L"Razer Blade"); 564 GetSMBiosStringW(DmiStrings[SYS_VERSION], dev, _countof(dev), TRUE); 565 } 566 } 567 568 // workaround for MSI motherboards 569 if (!wcscmp(ven, L"MSI") && 570 wcsstr(dev, L"MS-") != NULL && 571 DmiStrings[BOARD_NAME] != NULL && 572 strstr(DmiStrings[BOARD_NAME], "(MS-") != NULL) 573 { 574 GetSMBiosStringW(DmiStrings[BOARD_NAME], dev, _countof(dev), TRUE); 575 } 576 if (wcslen(ven) == 0 && 577 wcsstr(dev, L"MS-") == dev) 578 { 579 StringCchCopyW(ven, _countof(ven), L"MSI"); 580 } 581 582 // trim redundant characters 583 TrimPunctuation(ven); 584 TrimPunctuation(dev); 585 586 if (wcsistr(dev, ven) == dev || 587 (!wcscmp(ven, L"ASUS") && wcsstr(dev, L"ASUS") != NULL) || 588 (!wcscmp(ven, L"HP") && wcsstr(dev, L" by HP") != NULL)) 589 { 590 // device string contains vendor string, use second only 591 StringCchCopyW(pBuf, cchBuf, dev); 592 } 593 else 594 { 595 StringCchCopyW(pBuf, cchBuf, ven); 596 AppendSystemFamily(pBuf, cchBuf, DmiStrings, dev); 597 if (wcslen(pBuf) > 0 && wcslen(dev) > 0) 598 { 599 StringCchCatW(pBuf, cchBuf, L" "); 600 } 601 StringCchCatW(pBuf, cchBuf, dev); 602 } 603 604 FreeSMBiosData(SMBiosBuf); 605 606 return (wcslen(pBuf) > 0); 607 } 608