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