[MKHIVE] Minor improvements.
[reactos.git] / sdk / tools / xml2sdb / xml2sdb.cpp
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.1";
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 * Data
403 */
404
405 #ifndef REG_SZ
406 #define REG_SZ 1
407 //#define REG_BINARY 3
408 #define REG_DWORD 4
409 #define REG_QWORD 11
410 #endif
411
412
413 bool Data::fromXml(XMLHandle dbNode)
414 {
415 Name = ReadStringNode(dbNode, "NAME");
416
417 StringData = ReadStringNode(dbNode, "DATA_STRING");
418 if (!StringData.empty())
419 {
420 DataType = REG_SZ;
421 return !Name.empty();
422 }
423 DWordData = ReadDWordNode(dbNode, "DATA_DWORD");
424 if (DWordData)
425 {
426 DataType = REG_DWORD;
427 return !Name.empty();
428 }
429 QWordData = ReadQWordNode(dbNode, "DATA_QWORD");
430 if (QWordData)
431 {
432 DataType = REG_QWORD;
433 return !Name.empty();
434 }
435
436 SHIM_ERR("Data node (%s) without value!\n", Name.c_str());
437 return false;
438 }
439
440 bool Data::toSdb(PDB pdb, Database& db)
441 {
442 Tagid = db.BeginWriteListTag(pdb, TAG_DATA);
443 db.WriteString(pdb, TAG_NAME, Name, true);
444 db.WriteDWord(pdb, TAG_DATA_VALUETYPE, DataType, true);
445 switch (DataType)
446 {
447 case REG_SZ:
448 db.WriteString(pdb, TAG_DATA_STRING, StringData);
449 break;
450 case REG_DWORD:
451 db.WriteDWord(pdb, TAG_DATA_DWORD, DWordData);
452 break;
453 case REG_QWORD:
454 db.WriteQWord(pdb, TAG_DATA_QWORD, QWordData);
455 break;
456 default:
457 SHIM_ERR("Data node (%s) with unknown type (0x%x)\n", Name.c_str(), DataType);
458 return false;
459 }
460
461 return !!db.EndWriteListTag(pdb, Tagid);
462 }
463
464 /***********************************************************************
465 * Layer
466 */
467
468 bool Layer::fromXml(XMLHandle dbNode)
469 {
470 Name = ReadStringNode(dbNode, "NAME");
471 ReadGeneric(dbNode, ShimRefs, "SHIM_REF");
472 ReadGeneric(dbNode, FlagRefs, "FLAG_REF");
473 ReadGeneric(dbNode, Datas, "DATA");
474 return true;
475 }
476
477 bool Layer::toSdb(PDB pdb, Database& db)
478 {
479 Tagid = db.BeginWriteListTag(pdb, TAG_LAYER);
480 db.WriteString(pdb, TAG_NAME, Name, true);
481 if (!WriteGeneric(pdb, ShimRefs, db))
482 return false;
483 if (!WriteGeneric(pdb, FlagRefs, db))
484 return false;
485 if (!WriteGeneric(pdb, Datas, db))
486 return false;
487 return !!db.EndWriteListTag(pdb, Tagid);
488 }
489
490
491 /***********************************************************************
492 * MatchingFile
493 */
494
495 bool MatchingFile::fromXml(XMLHandle dbNode)
496 {
497 Name = ReadStringNode(dbNode, "NAME");
498 Size = ReadDWordNode(dbNode, "SIZE");
499 Checksum = ReadDWordNode(dbNode, "CHECKSUM");
500 CompanyName = ReadStringNode(dbNode, "COMPANY_NAME");
501 InternalName = ReadStringNode(dbNode, "INTERNAL_NAME");
502 ProductName = ReadStringNode(dbNode, "PRODUCT_NAME");
503 ProductVersion = ReadStringNode(dbNode, "PRODUCT_VERSION");
504 FileVersion = ReadStringNode(dbNode, "FILE_VERSION");
505 BinFileVersion = ReadStringNode(dbNode, "BIN_FILE_VERSION");
506 LinkDate = ReadDWordNode(dbNode, "LINK_DATE");
507 VerLanguage = ReadStringNode(dbNode, "VER_LANGUAGE");
508 FileDescription = ReadStringNode(dbNode, "FILE_DESCRIPTION");
509 OriginalFilename = ReadStringNode(dbNode, "ORIGINAL_FILENAME");
510 UptoBinFileVersion = ReadStringNode(dbNode, "UPTO_BIN_FILE_VERSION");
511 LinkerVersion = ReadDWordNode(dbNode, "LINKER_VERSION");
512 return true;
513 }
514
515 bool MatchingFile::toSdb(PDB pdb, Database& db)
516 {
517 TAGID tagid = db.BeginWriteListTag(pdb, TAG_MATCHING_FILE);
518
519 db.WriteString(pdb, TAG_NAME, Name, true);
520 db.WriteDWord(pdb, TAG_SIZE, Size);
521 db.WriteDWord(pdb, TAG_CHECKSUM, Checksum);
522 db.WriteString(pdb, TAG_COMPANY_NAME, CompanyName);
523 db.WriteString(pdb, TAG_INTERNAL_NAME, InternalName);
524 db.WriteString(pdb, TAG_PRODUCT_NAME, ProductName);
525 db.WriteString(pdb, TAG_PRODUCT_VERSION, ProductVersion);
526 db.WriteString(pdb, TAG_FILE_VERSION, FileVersion);
527 if (!BinFileVersion.empty())
528 SHIM_ERR("TAG_BIN_FILE_VERSION Unimplemented\n"); //db.WriteQWord(pdb, TAG_BIN_FILE_VERSION, BinFileVersion);
529 db.WriteDWord(pdb, TAG_LINK_DATE, LinkDate);
530 if (!VerLanguage.empty())
531 SHIM_ERR("TAG_VER_LANGUAGE Unimplemented\n"); //db.WriteDWord(pdb, TAG_VER_LANGUAGE, VerLanguage);
532 db.WriteString(pdb, TAG_FILE_DESCRIPTION, FileDescription);
533 db.WriteString(pdb, TAG_ORIGINAL_FILENAME, OriginalFilename);
534 if (!UptoBinFileVersion.empty())
535 SHIM_ERR("TAG_UPTO_BIN_FILE_VERSION Unimplemented\n"); //db.WriteQWord(pdb, TAG_UPTO_BIN_FILE_VERSION, UptoBinFileVersion);
536 db.WriteDWord(pdb, TAG_LINKER_VERSION, LinkerVersion);
537
538 return !!db.EndWriteListTag(pdb, tagid);
539 }
540
541
542 /***********************************************************************
543 * Exe
544 */
545
546 bool Exe::fromXml(XMLHandle dbNode)
547 {
548 Name = ReadStringNode(dbNode, "NAME");
549 ReadGuidNode(dbNode, "EXE_ID", ExeID);
550 AppName = ReadStringNode(dbNode, "APP_NAME");
551 Vendor = ReadStringNode(dbNode, "VENDOR");
552
553 ReadGeneric(dbNode, MatchingFiles, "MATCHING_FILE");
554
555 ReadGeneric(dbNode, ShimRefs, "SHIM_REF");
556 ReadGeneric(dbNode, FlagRefs, "FLAG_REF");
557
558 return !Name.empty();
559 }
560
561 bool Exe::toSdb(PDB pdb, Database& db)
562 {
563 Tagid = db.BeginWriteListTag(pdb, TAG_EXE);
564
565 db.WriteString(pdb, TAG_NAME, Name, true);
566 if (IsEmptyGuid(ExeID))
567 RandomGuid(ExeID);
568 db.WriteBinary(pdb, TAG_EXE_ID, ExeID);
569
570
571 db.WriteString(pdb, TAG_APP_NAME, AppName);
572 db.WriteString(pdb, TAG_VENDOR, Vendor);
573
574 if (!WriteGeneric(pdb, MatchingFiles, db))
575 return false;
576 if (!WriteGeneric(pdb, ShimRefs, db))
577 return false;
578 if (!WriteGeneric(pdb, FlagRefs, db))
579 return false;
580
581 return !!db.EndWriteListTag(pdb, Tagid);
582 }
583
584
585 /***********************************************************************
586 * Database
587 */
588
589 void Database::WriteBinary(PDB pdb, TAG tag, const GUID& guid, bool always)
590 {
591 if (always || !IsEmptyGuid(guid))
592 SdbWriteBinaryTag(pdb, tag, (BYTE*)&guid, sizeof(GUID));
593 }
594
595 void Database::WriteBinary(PDB pdb, TAG tag, const std::vector<BYTE>& data, bool always)
596 {
597 if (always || !data.empty())
598 SdbWriteBinaryTag(pdb, tag, data.data(), data.size());
599 }
600
601 void Database::WriteString(PDB pdb, TAG tag, const sdbstring& str, bool always)
602 {
603 if (always || !str.empty())
604 SdbWriteStringTag(pdb, tag, (LPCWSTR)str.c_str());
605 }
606
607 void Database::WriteString(PDB pdb, TAG tag, const std::string& str, bool always)
608 {
609 WriteString(pdb, tag, sdbstring(str.begin(), str.end()), always);
610 }
611
612 void Database::WriteDWord(PDB pdb, TAG tag, DWORD value, bool always)
613 {
614 if (always || value)
615 SdbWriteDWORDTag(pdb, tag, value);
616 }
617
618 void Database::WriteQWord(PDB pdb, TAG tag, QWORD value, bool always)
619 {
620 if (always || value)
621 SdbWriteQWORDTag(pdb, tag, value);
622 }
623
624 TAGID Database::BeginWriteListTag(PDB pdb, TAG tag)
625 {
626 return SdbBeginWriteListTag(pdb, tag);
627 }
628
629 BOOL Database::EndWriteListTag(PDB pdb, TAGID tagid)
630 {
631 return SdbEndWriteListTag(pdb, tagid);
632 }
633
634 bool Database::fromXml(XMLHandle dbNode)
635 {
636 Name = ReadStringNode(dbNode, "NAME");
637 ReadGuidNode(dbNode, "DATABASE_ID", ID);
638
639 XMLHandle libChild = dbNode.FirstChildElement("LIBRARY").FirstChild();
640 while (libChild.ToNode())
641 {
642 std::string NodeName = ToNodeName(libChild);
643 if (NodeName == "SHIM")
644 {
645 Shim shim;
646 if (shim.fromXml(libChild))
647 Library.Shims.push_back(shim);
648 }
649 else if (NodeName == "FLAG")
650 {
651 Flag flag;
652 if (flag.fromXml(libChild))
653 Library.Flags.push_back(flag);
654 }
655 else if (NodeName == "INCLUDE" || NodeName == "EXCLUDE")
656 {
657 InExclude inex;
658 if (inex.fromXml(libChild))
659 Library.InExcludes.push_back(inex);
660 }
661 libChild = libChild.NextSibling();
662 }
663
664 ReadGeneric(dbNode, Layers, "LAYER");
665 ReadGeneric(dbNode, Exes, "EXE");
666 return true;
667 }
668
669 bool Database::fromXml(const char* fileName)
670 {
671 tinyxml2::XMLDocument doc;
672 tinyxml2::XMLError err = doc.LoadFile(fileName);
673 XMLHandle dbHandle = tinyxml2::XMLHandle(&doc).FirstChildElement("SDB").FirstChildElement("DATABASE");
674 return fromXml(dbHandle);
675 }
676
677 bool Database::toSdb(LPCWSTR path)
678 {
679 PDB pdb = SdbCreateDatabase(path, DOS_PATH);
680 TAGID tidDatabase = BeginWriteListTag(pdb, TAG_DATABASE);
681 LARGE_INTEGER li = { 0 };
682 RtlSecondsSince1970ToTime(time(0), &li);
683 SdbWriteQWORDTag(pdb, TAG_TIME, li.QuadPart);
684 WriteString(pdb, TAG_COMPILER_VERSION, szCompilerVersion);
685 SdbWriteDWORDTag(pdb, TAG_OS_PLATFORM, 1);
686 WriteString(pdb, TAG_NAME, Name, true);
687 if (IsEmptyGuid(ID))
688 {
689 SHIM_WARN("DB has empty ID!\n");
690 RandomGuid(ID);
691 }
692 WriteBinary(pdb, TAG_DATABASE_ID, ID);
693 TAGID tidLibrary = BeginWriteListTag(pdb, TAG_LIBRARY);
694 if (!WriteGeneric(pdb, Library.InExcludes, *this))
695 return false;
696 if (!WriteGeneric(pdb, Library.Shims, *this))
697 return false;
698 if (!WriteGeneric(pdb, Library.Flags, *this))
699 return false;
700 EndWriteListTag(pdb, tidLibrary);
701 if (!WriteGeneric(pdb, Layers, *this))
702 return false;
703 if (!WriteGeneric(pdb, Exes, *this))
704 return false;
705 EndWriteListTag(pdb, tidDatabase);
706
707 SdbCloseDatabaseWrite(pdb);
708 return true;
709 }
710
711 static void InsertTagid(const sdbstring& name, TAGID tagid, std::map<sdbstring, TAGID>& lookup, const char* type)
712 {
713 sdbstring nameLower = name;
714 std::transform(nameLower.begin(), nameLower.end(), nameLower.begin(), ::tolower);
715 if (lookup.find(nameLower) != lookup.end())
716 {
717 std::string nameA(name.begin(), name.end());
718 SHIM_WARN("%s '%s' redefined\n", type, nameA.c_str());
719 return;
720 }
721 lookup[nameLower] = tagid;
722 }
723
724 static TAGID FindTagid(const sdbstring& name, const std::map<sdbstring, TAGID>& lookup)
725 {
726 sdbstring nameLower = name;
727 std::transform(nameLower.begin(), nameLower.end(), nameLower.begin(), ::tolower);
728 std::map<sdbstring, TAGID>::const_iterator it = lookup.find(nameLower);
729 if (it == lookup.end())
730 return 0;
731 return it->second;
732 }
733
734 void Database::InsertShimTagid(const sdbstring& name, TAGID tagid)
735 {
736 InsertTagid(name, tagid, KnownShims, "Shim");
737 }
738
739 TAGID Database::FindShimTagid(const sdbstring& name)
740 {
741 return FindTagid(name, KnownShims);
742 }
743
744 void Database::InsertPatchTagid(const sdbstring& name, TAGID tagid)
745 {
746 InsertTagid(name, tagid, KnownPatches, "Patch");
747 }
748
749 TAGID Database::FindPatchTagid(const sdbstring& name)
750 {
751 return FindTagid(name, KnownPatches);
752 }
753
754 void Database::InsertFlagTagid(const sdbstring& name, TAGID tagid)
755 {
756 InsertTagid(name, tagid, KnownFlags, "Flag");
757 }
758
759 TAGID Database::FindFlagTagid(const sdbstring& name)
760 {
761 return FindTagid(name, KnownFlags);
762 }
763
764
765 bool xml_2_db(const char* xml, const WCHAR* sdb)
766 {
767 Database db;
768 if (db.fromXml(xml))
769 {
770 return db.toSdb((LPCWSTR)sdb);
771 }
772 return false;
773 }