Reactos
1/*
2 * PROJECT: xml2sdb
3 * LICENSE: MIT (https://spdx.org/licenses/MIT)
4 * PURPOSE: Conversion functions from xml -> db
5 * COPYRIGHT: Copyright 2016-2025 Mark Jansen <mark.jansen@reactos.org>
6 */
7
8#include "xml2sdb.h"
9#include "sdbpapi.h"
10#include "tinyxml2.h"
11#include <time.h>
12#include <algorithm>
13#include <sstream>
14
15using tinyxml2::XMLText;
16
17static const GUID GUID_NULL = { 0 };
18static const char szCompilerVersion[] = "1.8.0.0";
19
20#if !defined(C_ASSERT)
21#define C_ASSERT(expr) extern char (*c_assert(void)) [(expr) ? 1 : -1]
22#endif
23
24
25C_ASSERT(sizeof(GUID) == 16);
26C_ASSERT(sizeof(ULONG) == 4);
27C_ASSERT(sizeof(LARGE_INTEGER) == 8);
28C_ASSERT(sizeof(WCHAR) == 2);
29C_ASSERT(sizeof(wchar_t) == 2);
30C_ASSERT(sizeof(TAG) == 2);
31C_ASSERT(sizeof(TAGID) == 4);
32
33
34extern "C"
35VOID NTAPI RtlSecondsSince1970ToTime(IN ULONG SecondsSince1970,
36 OUT PLARGE_INTEGER Time);
37
38
39/***********************************************************************
40 * Helper functions
41 */
42str_to_flag platform_to_flag[] = {
43 {"X86", PLATFORM_X86},
44 {"I386", PLATFORM_X86},
45 {"AMD64", PLATFORM_AMD64},
46 {"ANY", PLATFORM_ANY},
47 {nullptr, 0},
48};
49
50DWORD
51str_to_enum(const std::string& str, const str_to_flag* table)
52{
53 DWORD value = 0;
54 std::istringstream iss(str);
55 std::string item;
56 while (std::getline(iss, item, ','))
57 {
58 std::string trimmedItem = item;
59 trimmedItem.erase(remove_if(trimmedItem.begin(), trimmedItem.end(), isspace), trimmedItem.end());
60 std::transform(trimmedItem.begin(), trimmedItem.end(), trimmedItem.begin(), ::toupper);
61 for (const str_to_flag* p = table; p->name; ++p)
62 {
63 if (trimmedItem == p->name)
64 {
65 value |= p->flag;
66 break;
67 }
68 }
69 }
70 return value;
71}
72
73
74// Convert utf8 to utf16:
75// http://stackoverflow.com/a/7154226/4928207
76
77bool IsEmptyGuid(const GUID& g)
78{
79 return !memcmp(&g, &GUID_NULL, sizeof(GUID));
80}
81
82void RandomGuid(GUID& g)
83{
84 BYTE* p = (BYTE*)&g;
85 for (size_t n = 0; n < sizeof(GUID); ++n)
86 p[n] = (BYTE)(rand() % 0xff);
87}
88
89// Given a node, return the node value (safe)
90std::string ToString(XMLHandle node)
91{
92 XMLText* txtNode = node.FirstChild().ToText();
93 const char* txt = txtNode ? txtNode->Value() : NULL;
94 if (txt)
95 return std::string(txt);
96 return std::string();
97}
98
99// Given a node, return the node name (safe)
100std::string ToNodeName(XMLHandle node)
101{
102 tinyxml2::XMLNode* raw = node.ToNode();
103 const char* txt = raw ? raw->Value() : NULL;
104 if (txt)
105 return std::string(txt);
106 return std::string();
107}
108
109// Read either an attribute, or a child node
110std::string ReadStringNode(XMLHandle dbNode, const char* nodeName)
111{
112 tinyxml2::XMLElement* elem = dbNode.ToElement();
113 if (elem)
114 {
115 const char* rawVal = elem->Attribute(nodeName);
116 if (rawVal)
117 return std::string(rawVal);
118 }
119 return ToString(dbNode.FirstChildElement(nodeName));
120}
121
122QWORD ReadQWordNode(XMLHandle dbNode, const char* nodeName)
123{
124 std::string value = ReadStringNode(dbNode, nodeName);
125 int base = 10;
126 if (value.size() > 2 && value[0] == '0' && value[1] == 'x')
127 {
128 base = 16;
129 value = value.substr(2);
130 }
131 return static_cast<QWORD>(strtoull(value.c_str(), NULL, base));
132}
133
134DWORD ReadDWordNode(XMLHandle dbNode, const char* nodeName)
135{
136 return static_cast<DWORD>(ReadQWordNode(dbNode, nodeName));
137}
138
139PlatformType ReadPlatformNode(XMLHandle dbNode, const char *nodeName)
140{
141 std::string value = ReadStringNode(dbNode, nodeName);
142 if (value.empty())
143 return PLATFORM_ANY;
144 DWORD platform = str_to_enum(value, platform_to_flag);
145 return static_cast<PlatformType>(platform);
146}
147
148unsigned char char2byte(char hexChar, bool* success = NULL)
149{
150 if (hexChar >= '0' && hexChar <= '9')
151 return hexChar - '0';
152 if (hexChar >= 'A' && hexChar <= 'F')
153 return hexChar - 'A' + 10;
154 if (hexChar >= 'a' && hexChar <= 'f')
155 return hexChar - 'a' + 10;
156
157 if (success)
158 *success = false;
159 return 0;
160}
161
162// adapted from wine's ntdll\rtlstr.c rev 1.45
163static bool StringToGuid(const std::string& str, GUID& guid)
164{
165 const char *lpszGUID = str.c_str();
166 BYTE* lpOut = (BYTE*)&guid;
167 int i = 0;
168 bool expectBrace = true;
169 while (i <= 37)
170 {
171 switch (i)
172 {
173 case 0:
174 if (*lpszGUID != '{')
175 {
176 i++;
177 expectBrace = false;
178 continue;
179 }
180 break;
181
182 case 9:
183 case 14:
184 case 19:
185 case 24:
186 if (*lpszGUID != '-')
187 return false;
188 break;
189
190 case 37:
191 return expectBrace == (*lpszGUID == '}');
192
193 default:
194 {
195 CHAR ch = *lpszGUID, ch2 = lpszGUID[1];
196 unsigned char byte;
197 bool converted = true;
198
199 byte = char2byte(ch, &converted) << 4 | char2byte(ch2, &converted);
200 if (!converted)
201 return false;
202
203 switch (i)
204 {
205#ifndef WORDS_BIGENDIAN
206 /* For Big Endian machines, we store the data such that the
207 * dword/word members can be read as DWORDS and WORDS correctly. */
208 /* Dword */
209 case 1:
210 lpOut[3] = byte;
211 break;
212 case 3:
213 lpOut[2] = byte;
214 break;
215 case 5:
216 lpOut[1] = byte;
217 break;
218 case 7:
219 lpOut[0] = byte;
220 lpOut += 4;
221 break;
222 /* Word */
223 case 10:
224 case 15:
225 lpOut[1] = byte;
226 break;
227 case 12:
228 case 17:
229 lpOut[0] = byte;
230 lpOut += 2;
231 break;
232#endif
233 /* Byte */
234 default:
235 lpOut[0] = byte;
236 lpOut++;
237 break;
238 }
239
240 lpszGUID++; /* Skip 2nd character of byte */
241 i++;
242 }
243 }
244
245 lpszGUID++;
246 i++;
247 }
248 return false;
249}
250
251bool ReadGuidNode(XMLHandle dbNode, const char* nodeName, GUID& guid)
252{
253 std::string value = ReadStringNode(dbNode, nodeName);
254 if (!StringToGuid(value, guid))
255 {
256 memset(&guid, 0, sizeof(GUID));
257 return false;
258 }
259 return true;
260}
261
262bool ReadBinaryNode(XMLHandle dbNode, const char* nodeName, std::vector<BYTE>& data)
263{
264 std::string value = ReadStringNode(dbNode, nodeName);
265 value.erase(std::remove_if(value.begin(), value.end(), ::isspace), value.end());
266
267 size_t length = value.size() / 2;
268 if (length * 2 != value.size())
269 return false;
270
271 data.resize(length);
272 for (size_t n = 0; n < length; ++n)
273 {
274 data[n] = (BYTE)(char2byte(value[n * 2]) << 4 | char2byte(value[(n * 2) + 1]));
275 }
276 return true;
277}
278
279
280/***********************************************************************
281 * InExclude
282 */
283
284bool InExclude::fromXml(XMLHandle dbNode)
285{
286 Module = ReadStringNode(dbNode, "MODULE");
287 // Special module names: '$' and '*'
288 if (!Module.empty())
289 {
290 Include = ToNodeName(dbNode) == "INCLUDE";
291 return true;
292 }
293 return false;
294}
295
296bool InExclude::toSdb(Database& db)
297{
298 TAGID tagid = db.BeginWriteListTag(TAG_INEXCLUD);
299 db.WriteString(TAG_MODULE, Module, true);
300 if (Include)
301 db.WriteNull(TAG_INCLUDE);
302 return !!db.EndWriteListTag(tagid);
303}
304
305
306template<typename T>
307void ReadGeneric(XMLHandle dbNode, std::list<T>& result, const char* nodeName)
308{
309 XMLHandle node = dbNode.FirstChildElement(nodeName);
310 while (node.ToNode())
311 {
312 T object;
313 if (object.fromXml(node))
314 result.push_back(object);
315
316 node = node.NextSiblingElement(nodeName);
317 }
318}
319
320template<typename T>
321void ReadGeneric(XMLHandle dbNode, std::list<T>& result, const char* nodeName, PlatformType platform)
322{
323 XMLHandle node = dbNode.FirstChildElement(nodeName);
324 while (node.ToNode())
325 {
326 T object;
327 if (object.fromXml(node) && ((object.Platform & platform) != PLATFORM_NONE))
328 result.push_back(object);
329
330 node = node.NextSiblingElement(nodeName);
331 }
332}
333
334template<typename T>
335bool WriteGeneric(std::list<T>& data, Database& db)
336{
337 for (typename std::list<T>::iterator it = data.begin(); it != data.end(); ++it)
338 {
339 if (!it->toSdb(db))
340 return false;
341 }
342 return true;
343}
344
345
346/***********************************************************************
347 * ShimRef
348 */
349
350bool ShimRef::fromXml(XMLHandle dbNode)
351{
352 Name = ReadStringNode(dbNode, "NAME");
353 CommandLine = ReadStringNode(dbNode, "COMMAND_LINE");
354 ReadGeneric(dbNode, InExcludes, "INCLUDE");
355 ReadGeneric(dbNode, InExcludes, "EXCLUDE");
356 return !Name.empty();
357}
358
359bool ShimRef::toSdb(Database& db)
360{
361 TAGID tagid = db.BeginWriteListTag(TAG_SHIM_REF);
362 db.WriteString(TAG_NAME, Name, true);
363 db.WriteString(TAG_COMMAND_LINE, CommandLine);
364
365 if (!ShimTagid)
366 ShimTagid = db.FindShimTagid(Name);
367 db.WriteDWord(TAG_SHIM_TAGID, ShimTagid);
368 return !!db.EndWriteListTag(tagid);
369}
370
371
372
373/***********************************************************************
374 * FlagRef
375 */
376
377bool FlagRef::fromXml(XMLHandle dbNode)
378{
379 Name = ReadStringNode(dbNode, "NAME");
380 return !Name.empty();
381}
382
383bool FlagRef::toSdb(Database& db)
384{
385 TAGID tagid = db.BeginWriteListTag(TAG_FLAG_REF);
386 db.WriteString(TAG_NAME, Name, true);
387
388 if (!FlagTagid)
389 FlagTagid = db.FindFlagTagid(Name);
390 db.WriteDWord(TAG_FLAG_TAGID, FlagTagid);
391 return !!db.EndWriteListTag(tagid);
392}
393
394
395/***********************************************************************
396 * Shim
397 */
398
399bool Shim::fromXml(XMLHandle dbNode)
400{
401 Name = ReadStringNode(dbNode, "NAME");
402 DllFile = ReadStringNode(dbNode, "DLLFILE");
403 ReadGuidNode(dbNode, "FIX_ID", FixID);
404 // GENERAL ?
405 // DESCRIPTION_RC_ID
406 ReadGeneric(dbNode, InExcludes, "INCLUDE");
407 ReadGeneric(dbNode, InExcludes, "EXCLUDE");
408 return !Name.empty() && !DllFile.empty();
409}
410
411bool Shim::toSdb(Database& db)
412{
413 Tagid = db.BeginWriteListTag(TAG_SHIM);
414 db.InsertShimTagid(Name, Tagid);
415 db.WriteString(TAG_NAME, Name);
416 db.WriteString(TAG_DLLFILE, DllFile);
417 if (IsEmptyGuid(FixID))
418 RandomGuid(FixID);
419 db.WriteBinary(TAG_FIX_ID, FixID);
420 if (!WriteGeneric(InExcludes, db))
421 return false;
422 return !!db.EndWriteListTag(Tagid);
423}
424
425
426/***********************************************************************
427 * Flag
428 */
429
430bool Flag::fromXml(XMLHandle dbNode)
431{
432 Name = ReadStringNode(dbNode, "NAME");
433
434 KernelFlags = ReadQWordNode(dbNode, "FLAG_MASK_KERNEL");
435 UserFlags = ReadQWordNode(dbNode, "FLAG_MASK_USER");
436 ProcessParamFlags = ReadQWordNode(dbNode, "FLAG_PROCESSPARAM");
437
438 return !Name.empty();
439}
440
441bool Flag::toSdb(Database& db)
442{
443 Tagid = db.BeginWriteListTag(TAG_FLAG);
444 db.InsertFlagTagid(Name, Tagid);
445 db.WriteString(TAG_NAME, Name, true);
446
447 db.WriteQWord(TAG_FLAG_MASK_KERNEL, KernelFlags);
448 db.WriteQWord(TAG_FLAG_MASK_USER, UserFlags);
449 db.WriteQWord(TAG_FLAG_PROCESSPARAM, ProcessParamFlags);
450
451 return !!db.EndWriteListTag(Tagid);
452}
453
454
455/***********************************************************************
456 * Data
457 */
458
459#ifndef REG_SZ
460#define REG_SZ 1
461//#define REG_BINARY 3
462#define REG_DWORD 4
463#define REG_QWORD 11
464#endif
465
466
467bool Data::fromXml(XMLHandle dbNode)
468{
469 Name = ReadStringNode(dbNode, "NAME");
470
471 StringData = ReadStringNode(dbNode, "DATA_STRING");
472 if (!StringData.empty())
473 {
474 DataType = REG_SZ;
475 return !Name.empty();
476 }
477 DWordData = ReadDWordNode(dbNode, "DATA_DWORD");
478 if (DWordData)
479 {
480 DataType = REG_DWORD;
481 return !Name.empty();
482 }
483 QWordData = ReadQWordNode(dbNode, "DATA_QWORD");
484 if (QWordData)
485 {
486 DataType = REG_QWORD;
487 return !Name.empty();
488 }
489
490 SHIM_ERR("Data node (%s) without value!\n", Name.c_str());
491 return false;
492}
493
494bool Data::toSdb(Database& db)
495{
496 Tagid = db.BeginWriteListTag(TAG_DATA);
497 db.WriteString(TAG_NAME, Name, true);
498 db.WriteDWord(TAG_DATA_VALUETYPE, DataType, true);
499 switch (DataType)
500 {
501 case REG_SZ:
502 db.WriteString(TAG_DATA_STRING, StringData);
503 break;
504 case REG_DWORD:
505 db.WriteDWord(TAG_DATA_DWORD, DWordData);
506 break;
507 case REG_QWORD:
508 db.WriteQWord(TAG_DATA_QWORD, QWordData);
509 break;
510 default:
511 SHIM_ERR("Data node (%s) with unknown type (0x%x)\n", Name.c_str(), DataType);
512 return false;
513 }
514
515 return !!db.EndWriteListTag(Tagid);
516}
517
518/***********************************************************************
519 * Layer
520 */
521
522bool Layer::fromXml(XMLHandle dbNode)
523{
524 Name = ReadStringNode(dbNode, "NAME");
525 ReadGeneric(dbNode, ShimRefs, "SHIM_REF");
526 ReadGeneric(dbNode, FlagRefs, "FLAG_REF");
527 ReadGeneric(dbNode, Datas, "DATA");
528 return true;
529}
530
531bool Layer::toSdb(Database& db)
532{
533 Tagid = db.BeginWriteListTag(TAG_LAYER);
534 db.WriteString(TAG_NAME, Name, true);
535 if (!WriteGeneric(ShimRefs, db))
536 return false;
537 if (!WriteGeneric(FlagRefs, db))
538 return false;
539 if (!WriteGeneric(Datas, db))
540 return false;
541 return !!db.EndWriteListTag(Tagid);
542}
543
544
545/***********************************************************************
546 * MatchingFile
547 */
548
549bool MatchingFile::fromXml(XMLHandle dbNode)
550{
551 Name = ReadStringNode(dbNode, "NAME");
552 Size = ReadDWordNode(dbNode, "SIZE");
553 Checksum = ReadDWordNode(dbNode, "CHECKSUM");
554 CompanyName = ReadStringNode(dbNode, "COMPANY_NAME");
555 InternalName = ReadStringNode(dbNode, "INTERNAL_NAME");
556 ProductName = ReadStringNode(dbNode, "PRODUCT_NAME");
557 ProductVersion = ReadStringNode(dbNode, "PRODUCT_VERSION");
558 FileVersion = ReadStringNode(dbNode, "FILE_VERSION");
559 BinFileVersion = ReadStringNode(dbNode, "BIN_FILE_VERSION");
560 LinkDate = ReadDWordNode(dbNode, "LINK_DATE");
561 VerLanguage = ReadStringNode(dbNode, "VER_LANGUAGE");
562 FileDescription = ReadStringNode(dbNode, "FILE_DESCRIPTION");
563 OriginalFilename = ReadStringNode(dbNode, "ORIGINAL_FILENAME");
564 UptoBinFileVersion = ReadStringNode(dbNode, "UPTO_BIN_FILE_VERSION");
565 LinkerVersion = ReadDWordNode(dbNode, "LINKER_VERSION");
566 return true;
567}
568
569bool MatchingFile::toSdb(Database& db)
570{
571 TAGID tagid = db.BeginWriteListTag(TAG_MATCHING_FILE);
572
573 db.WriteString(TAG_NAME, Name, true);
574 db.WriteDWord(TAG_SIZE, Size);
575 db.WriteDWord(TAG_CHECKSUM, Checksum);
576 db.WriteString(TAG_COMPANY_NAME, CompanyName);
577 db.WriteString(TAG_INTERNAL_NAME, InternalName);
578 db.WriteString(TAG_PRODUCT_NAME, ProductName);
579 db.WriteString(TAG_PRODUCT_VERSION, ProductVersion);
580 db.WriteString(TAG_FILE_VERSION, FileVersion);
581 if (!BinFileVersion.empty())
582 SHIM_ERR("TAG_BIN_FILE_VERSION Unimplemented\n"); //db.WriteQWord(TAG_BIN_FILE_VERSION, BinFileVersion);
583 db.WriteDWord(TAG_LINK_DATE, LinkDate);
584 if (!VerLanguage.empty())
585 SHIM_ERR("TAG_VER_LANGUAGE Unimplemented\n"); //db.WriteDWord(TAG_VER_LANGUAGE, VerLanguage);
586 db.WriteString(TAG_FILE_DESCRIPTION, FileDescription);
587 db.WriteString(TAG_ORIGINAL_FILENAME, OriginalFilename);
588 if (!UptoBinFileVersion.empty())
589 SHIM_ERR("TAG_UPTO_BIN_FILE_VERSION Unimplemented\n"); //db.WriteQWord(TAG_UPTO_BIN_FILE_VERSION, UptoBinFileVersion);
590 db.WriteDWord(TAG_LINKER_VERSION, LinkerVersion);
591
592 return !!db.EndWriteListTag(tagid);
593}
594
595
596/***********************************************************************
597 * Exe
598 */
599
600bool Exe::fromXml(XMLHandle dbNode)
601{
602 Name = ReadStringNode(dbNode, "NAME");
603 ReadGuidNode(dbNode, "EXE_ID", ExeID);
604 AppName = ReadStringNode(dbNode, "APP_NAME");
605 Vendor = ReadStringNode(dbNode, "VENDOR");
606
607 ReadGeneric(dbNode, MatchingFiles, "MATCHING_FILE");
608
609 ReadGeneric(dbNode, ShimRefs, "SHIM_REF");
610 ReadGeneric(dbNode, FlagRefs, "FLAG_REF");
611
612 Platform = ReadPlatformNode(dbNode, "RUNTIME_PLATFORM");
613
614 return !Name.empty();
615}
616
617bool Exe::toSdb(Database& db)
618{
619 Tagid = db.BeginWriteListTag(TAG_EXE);
620
621 db.WriteString(TAG_NAME, Name, true);
622 if (IsEmptyGuid(ExeID))
623 RandomGuid(ExeID);
624 db.WriteBinary(TAG_EXE_ID, ExeID);
625
626
627 db.WriteString(TAG_APP_NAME, AppName);
628 db.WriteString(TAG_VENDOR, Vendor);
629
630 if (!WriteGeneric(MatchingFiles, db))
631 return false;
632 if (!WriteGeneric(ShimRefs, db))
633 return false;
634 if (!WriteGeneric(FlagRefs, db))
635 return false;
636
637 return !!db.EndWriteListTag(Tagid);
638}
639
640
641/***********************************************************************
642 * Database
643 */
644
645void Database::WriteBinary(TAG tag, const GUID& guid, bool always)
646{
647 if (always || !IsEmptyGuid(guid))
648 SdbWriteBinaryTag(pdb, tag, (BYTE*)&guid, sizeof(GUID));
649}
650
651void Database::WriteBinary(TAG tag, const std::vector<BYTE>& data, bool always)
652{
653 if (always || !data.empty())
654 SdbWriteBinaryTag(pdb, tag, data.data(), data.size());
655}
656
657void Database::WriteString(TAG tag, const sdbstring& str, bool always)
658{
659 if (always || !str.empty())
660 SdbWriteStringTag(pdb, tag, (LPCWSTR)str.c_str());
661}
662
663void Database::WriteString(TAG tag, const std::string& str, bool always)
664{
665 WriteString(tag, sdbstring(str.begin(), str.end()), always);
666}
667
668void Database::WriteDWord(TAG tag, DWORD value, bool always)
669{
670 if (always || value)
671 SdbWriteDWORDTag(pdb, tag, value);
672}
673
674void Database::WriteQWord(TAG tag, QWORD value, bool always)
675{
676 if (always || value)
677 SdbWriteQWORDTag(pdb, tag, value);
678}
679
680void Database::WriteNull(TAG tag)
681{
682 SdbWriteNULLTag(pdb, tag);
683}
684
685TAGID Database::BeginWriteListTag(TAG tag)
686{
687 return SdbBeginWriteListTag(pdb, tag);
688}
689
690BOOL Database::EndWriteListTag(TAGID tagid)
691{
692 return SdbEndWriteListTag(pdb, tagid);
693}
694
695bool Database::fromXml(XMLHandle dbNode)
696{
697 Name = ReadStringNode(dbNode, "NAME");
698 ReadGuidNode(dbNode, "DATABASE_ID", ID);
699
700 XMLHandle libChild = dbNode.FirstChildElement("LIBRARY").FirstChild();
701 while (libChild.ToNode())
702 {
703 std::string NodeName = ToNodeName(libChild);
704 if (NodeName == "SHIM")
705 {
706 Shim shim;
707 if (shim.fromXml(libChild) && ((shim.Platform & platform) != PLATFORM_NONE))
708 Library.Shims.push_back(shim);
709 }
710 else if (NodeName == "FLAG")
711 {
712 Flag flag;
713 if (flag.fromXml(libChild))
714 Library.Flags.push_back(flag);
715 }
716 else if (NodeName == "INCLUDE" || NodeName == "EXCLUDE")
717 {
718 InExclude inex;
719 if (inex.fromXml(libChild))
720 Library.InExcludes.push_back(inex);
721 }
722 libChild = libChild.NextSibling();
723 }
724
725 ReadGeneric(dbNode, Layers, "LAYER", platform);
726 ReadGeneric(dbNode, Exes, "EXE", platform);
727 return true;
728}
729
730bool Database::fromXml(const char* fileName, PlatformType platform_)
731{
732 tinyxml2::XMLDocument doc;
733 tinyxml2::XMLError err = doc.LoadFile(fileName);
734 XMLHandle dbHandle = tinyxml2::XMLHandle(&doc).FirstChildElement("SDB").FirstChildElement("DATABASE");
735 platform = platform_;
736 return fromXml(dbHandle);
737}
738
739bool Database::toSdb(LPCWSTR path)
740{
741 pdb = SdbCreateDatabase(path, DOS_PATH);
742 TAGID tidDatabase = BeginWriteListTag(TAG_DATABASE);
743 LARGE_INTEGER li = { 0 };
744 RtlSecondsSince1970ToTime(time(0), &li);
745 WriteQWord(TAG_TIME, li.QuadPart);
746 WriteString(TAG_COMPILER_VERSION, szCompilerVersion);
747 WriteDWord(TAG_OS_PLATFORM, platform);
748 WriteString(TAG_NAME, Name, true);
749 if (IsEmptyGuid(ID))
750 {
751 SHIM_WARN("DB has empty ID!\n");
752 RandomGuid(ID);
753 }
754 WriteBinary(TAG_DATABASE_ID, ID);
755 TAGID tidLibrary = BeginWriteListTag(TAG_LIBRARY);
756 if (!WriteGeneric(Library.InExcludes, *this))
757 return false;
758 if (!WriteGeneric(Library.Shims, *this))
759 return false;
760 if (!WriteGeneric(Library.Flags, *this))
761 return false;
762 EndWriteListTag(tidLibrary);
763 if (!WriteGeneric(Layers, *this))
764 return false;
765 if (!WriteGeneric(Exes, *this))
766 return false;
767 EndWriteListTag(tidDatabase);
768
769 SdbCloseDatabaseWrite(pdb);
770 pdb = nullptr;
771 return true;
772}
773
774static void InsertTagid(const sdbstring& name, TAGID tagid, std::map<sdbstring, TAGID>& lookup, const char* type)
775{
776 sdbstring nameLower = name;
777 std::transform(nameLower.begin(), nameLower.end(), nameLower.begin(), ::tolower);
778 if (lookup.find(nameLower) != lookup.end())
779 {
780 std::string nameA(name.begin(), name.end());
781 SHIM_WARN("%s '%s' redefined\n", type, nameA.c_str());
782 return;
783 }
784 lookup[nameLower] = tagid;
785}
786
787static TAGID FindTagid(const sdbstring& name, const std::map<sdbstring, TAGID>& lookup)
788{
789 sdbstring nameLower = name;
790 std::transform(nameLower.begin(), nameLower.end(), nameLower.begin(), ::tolower);
791 std::map<sdbstring, TAGID>::const_iterator it = lookup.find(nameLower);
792 if (it == lookup.end())
793 return 0;
794 return it->second;
795}
796
797void Database::InsertShimTagid(const sdbstring& name, TAGID tagid)
798{
799 InsertTagid(name, tagid, KnownShims, "Shim");
800}
801
802TAGID Database::FindShimTagid(const sdbstring& name)
803{
804 return FindTagid(name, KnownShims);
805}
806
807void Database::InsertPatchTagid(const sdbstring& name, TAGID tagid)
808{
809 InsertTagid(name, tagid, KnownPatches, "Patch");
810}
811
812TAGID Database::FindPatchTagid(const sdbstring& name)
813{
814 return FindTagid(name, KnownPatches);
815}
816
817void Database::InsertFlagTagid(const sdbstring& name, TAGID tagid)
818{
819 InsertTagid(name, tagid, KnownFlags, "Flag");
820}
821
822TAGID Database::FindFlagTagid(const sdbstring& name)
823{
824 return FindTagid(name, KnownFlags);
825}