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)
14 using tinyxml2::XMLText
;
16 static const GUID GUID_NULL
= { 0 };
17 static const char szCompilerVersion
[] = "1.7.0.1";
19 #if !defined(C_ASSERT)
20 #define C_ASSERT(expr) extern char (*c_assert(void)) [(expr) ? 1 : -1]
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);
34 VOID NTAPI
RtlSecondsSince1970ToTime(IN ULONG SecondsSince1970
,
35 OUT PLARGE_INTEGER Time
);
38 /***********************************************************************
43 // Convert utf8 to utf16:
44 // http://stackoverflow.com/a/7154226/4928207
46 bool IsEmptyGuid(const GUID
& g
)
48 return !memcmp(&g
, &GUID_NULL
, sizeof(GUID
));
51 void RandomGuid(GUID
& g
)
54 for (size_t n
= 0; n
< sizeof(GUID
); ++n
)
55 p
[n
] = (BYTE
)(rand() % 0xff);
58 // Given a node, return the node value (safe)
59 std::string
ToString(XMLHandle node
)
61 XMLText
* txtNode
= node
.FirstChild().ToText();
62 const char* txt
= txtNode
? txtNode
->Value() : NULL
;
64 return std::string(txt
);
68 // Given a node, return the node name (safe)
69 std::string
ToNodeName(XMLHandle node
)
71 tinyxml2::XMLNode
* raw
= node
.ToNode();
72 const char* txt
= raw
? raw
->Value() : NULL
;
74 return std::string(txt
);
78 // Read either an attribute, or a child node
79 std::string
ReadStringNode(XMLHandle dbNode
, const char* nodeName
)
81 tinyxml2::XMLElement
* elem
= dbNode
.ToElement();
84 const char* rawVal
= elem
->Attribute(nodeName
);
86 return std::string(rawVal
);
88 return ToString(dbNode
.FirstChildElement(nodeName
));
91 DWORD
ReadQWordNode(XMLHandle dbNode
, const char* nodeName
)
93 std::string value
= ReadStringNode(dbNode
, nodeName
);
95 if (value
.size() > 2 && value
[0] == '0' && value
[1] == 'x')
98 value
= value
.substr(2);
100 return static_cast<QWORD
>(strtoul(value
.c_str(), NULL
, base
));
103 DWORD
ReadDWordNode(XMLHandle dbNode
, const char* nodeName
)
105 return static_cast<DWORD
>(ReadQWordNode(dbNode
, nodeName
));
108 unsigned char char2byte(char hexChar
, bool* success
= NULL
)
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;
122 // adapted from wine's ntdll\rtlstr.c rev 1.45
123 static bool StringToGuid(const std::string
& str
, GUID
& guid
)
125 const char *lpszGUID
= str
.c_str();
126 BYTE
* lpOut
= (BYTE
*)&guid
;
128 bool expectBrace
= true;
134 if (*lpszGUID
!= '{')
146 if (*lpszGUID
!= '-')
151 return expectBrace
== (*lpszGUID
== '}');
155 CHAR ch
= *lpszGUID
, ch2
= lpszGUID
[1];
157 bool converted
= true;
159 byte
= char2byte(ch
, &converted
) << 4 | char2byte(ch2
, &converted
);
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. */
200 lpszGUID
++; /* Skip 2nd character of byte */
211 bool ReadGuidNode(XMLHandle dbNode
, const char* nodeName
, GUID
& guid
)
213 std::string value
= ReadStringNode(dbNode
, nodeName
);
214 if (!StringToGuid(value
, guid
))
216 memset(&guid
, 0, sizeof(GUID
));
222 bool ReadBinaryNode(XMLHandle dbNode
, const char* nodeName
, std::vector
<BYTE
>& data
)
224 std::string value
= ReadStringNode(dbNode
, nodeName
);
225 value
.erase(std::remove_if(value
.begin(), value
.end(), ::isspace
), value
.end());
227 size_t length
= value
.size() / 2;
228 if (length
* 2 != value
.size())
232 for (size_t n
= 0; n
< length
; ++n
)
234 data
[n
] = (BYTE
)(char2byte(value
[n
* 2]) << 4 | char2byte(value
[(n
* 2) + 1]));
240 /***********************************************************************
244 bool InExclude::fromXml(XMLHandle dbNode
)
246 Module
= ReadStringNode(dbNode
, "MODULE");
247 // Special module names: '$' and '*'
250 Include
= ToNodeName(dbNode
) == "INCLUDE";
256 bool InExclude::toSdb(PDB pdb
, Database
& db
)
258 TAGID tagid
= db
.BeginWriteListTag(pdb
, TAG_INEXCLUD
);
259 db
.WriteString(pdb
, TAG_MODULE
, Module
, true);
261 SdbWriteNULLTag(pdb
, TAG_INCLUDE
);
262 return !!db
.EndWriteListTag(pdb
, tagid
);
267 void ReadGeneric(XMLHandle dbNode
, std::list
<T
>& result
, const char* nodeName
)
269 XMLHandle node
= dbNode
.FirstChildElement(nodeName
);
270 while (node
.ToNode())
273 if (object
.fromXml(node
))
274 result
.push_back(object
);
276 node
= node
.NextSiblingElement(nodeName
);
281 bool WriteGeneric(PDB pdb
, std::list
<T
>& data
, Database
& db
)
283 for (typename
std::list
<T
>::iterator it
= data
.begin(); it
!= data
.end(); ++it
)
285 if (!it
->toSdb(pdb
, db
))
292 /***********************************************************************
296 bool ShimRef::fromXml(XMLHandle dbNode
)
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();
305 bool ShimRef::toSdb(PDB pdb
, Database
& db
)
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
);
312 ShimTagid
= db
.FindShimTagid(Name
);
313 SdbWriteDWORDTag(pdb
, TAG_SHIM_TAGID
, ShimTagid
);
314 return !!db
.EndWriteListTag(pdb
, tagid
);
319 /***********************************************************************
323 bool FlagRef::fromXml(XMLHandle dbNode
)
325 Name
= ReadStringNode(dbNode
, "NAME");
326 return !Name
.empty();
329 bool FlagRef::toSdb(PDB pdb
, Database
& db
)
331 TAGID tagid
= db
.BeginWriteListTag(pdb
, TAG_FLAG_REF
);
332 db
.WriteString(pdb
, TAG_NAME
, Name
, true);
335 FlagTagid
= db
.FindFlagTagid(Name
);
336 SdbWriteDWORDTag(pdb
, TAG_FLAG_TAGID
, FlagTagid
);
337 return !!db
.EndWriteListTag(pdb
, tagid
);
341 /***********************************************************************
345 bool Shim::fromXml(XMLHandle dbNode
)
347 Name
= ReadStringNode(dbNode
, "NAME");
348 DllFile
= ReadStringNode(dbNode
, "DLLFILE");
349 ReadGuidNode(dbNode
, "FIX_ID", FixID
);
352 ReadGeneric(dbNode
, InExcludes
, "INCLUDE");
353 ReadGeneric(dbNode
, InExcludes
, "EXCLUDE");
354 return !Name
.empty() && !DllFile
.empty();
357 bool Shim::toSdb(PDB pdb
, Database
& db
)
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
))
365 db
.WriteBinary(pdb
, TAG_FIX_ID
, FixID
);
366 if (!WriteGeneric(pdb
, InExcludes
, db
))
368 return !!db
.EndWriteListTag(pdb
, Tagid
);
372 /***********************************************************************
376 bool Flag::fromXml(XMLHandle dbNode
)
378 Name
= ReadStringNode(dbNode
, "NAME");
380 KernelFlags
= ReadQWordNode(dbNode
, "FLAG_MASK_KERNEL");
381 UserFlags
= ReadQWordNode(dbNode
, "FLAG_MASK_USER");
382 ProcessParamFlags
= ReadQWordNode(dbNode
, "FLAG_PROCESSPARAM");
384 return !Name
.empty();
387 bool Flag::toSdb(PDB pdb
, Database
& db
)
389 Tagid
= db
.BeginWriteListTag(pdb
, TAG_FLAG
);
390 db
.InsertFlagTagid(Name
, Tagid
);
391 db
.WriteString(pdb
, TAG_NAME
, Name
, true);
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
);
397 return !!db
.EndWriteListTag(pdb
, Tagid
);
401 /***********************************************************************
407 //#define REG_BINARY 3
413 bool Data::fromXml(XMLHandle dbNode
)
415 Name
= ReadStringNode(dbNode
, "NAME");
417 StringData
= ReadStringNode(dbNode
, "DATA_STRING");
418 if (!StringData
.empty())
421 return !Name
.empty();
423 DWordData
= ReadDWordNode(dbNode
, "DATA_DWORD");
426 DataType
= REG_DWORD
;
427 return !Name
.empty();
429 QWordData
= ReadQWordNode(dbNode
, "DATA_QWORD");
432 DataType
= REG_QWORD
;
433 return !Name
.empty();
436 SHIM_ERR("Data node (%s) without value!\n", Name
.c_str());
440 bool Data::toSdb(PDB pdb
, Database
& db
)
442 Tagid
= db
.BeginWriteListTag(pdb
, TAG_DATA
);
443 db
.WriteString(pdb
, TAG_NAME
, Name
, true);
444 db
.WriteDWord(pdb
, TAG_DATA_VALUETYPE
, DataType
, true);
448 db
.WriteString(pdb
, TAG_DATA_STRING
, StringData
);
451 db
.WriteDWord(pdb
, TAG_DATA_DWORD
, DWordData
);
454 db
.WriteQWord(pdb
, TAG_DATA_QWORD
, QWordData
);
457 SHIM_ERR("Data node (%s) with unknown type (0x%x)\n", Name
.c_str(), DataType
);
461 return !!db
.EndWriteListTag(pdb
, Tagid
);
464 /***********************************************************************
468 bool Layer::fromXml(XMLHandle dbNode
)
470 Name
= ReadStringNode(dbNode
, "NAME");
471 ReadGeneric(dbNode
, ShimRefs
, "SHIM_REF");
472 ReadGeneric(dbNode
, FlagRefs
, "FLAG_REF");
473 ReadGeneric(dbNode
, Datas
, "DATA");
477 bool Layer::toSdb(PDB pdb
, Database
& db
)
479 Tagid
= db
.BeginWriteListTag(pdb
, TAG_LAYER
);
480 db
.WriteString(pdb
, TAG_NAME
, Name
, true);
481 if (!WriteGeneric(pdb
, ShimRefs
, db
))
483 if (!WriteGeneric(pdb
, FlagRefs
, db
))
485 if (!WriteGeneric(pdb
, Datas
, db
))
487 return !!db
.EndWriteListTag(pdb
, Tagid
);
491 /***********************************************************************
495 bool MatchingFile::fromXml(XMLHandle dbNode
)
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");
515 bool MatchingFile::toSdb(PDB pdb
, Database
& db
)
517 TAGID tagid
= db
.BeginWriteListTag(pdb
, TAG_MATCHING_FILE
);
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
);
538 return !!db
.EndWriteListTag(pdb
, tagid
);
542 /***********************************************************************
546 bool Exe::fromXml(XMLHandle dbNode
)
548 Name
= ReadStringNode(dbNode
, "NAME");
549 ReadGuidNode(dbNode
, "EXE_ID", ExeID
);
550 AppName
= ReadStringNode(dbNode
, "APP_NAME");
551 Vendor
= ReadStringNode(dbNode
, "VENDOR");
553 ReadGeneric(dbNode
, MatchingFiles
, "MATCHING_FILE");
555 ReadGeneric(dbNode
, ShimRefs
, "SHIM_REF");
556 ReadGeneric(dbNode
, FlagRefs
, "FLAG_REF");
558 return !Name
.empty();
561 bool Exe::toSdb(PDB pdb
, Database
& db
)
563 Tagid
= db
.BeginWriteListTag(pdb
, TAG_EXE
);
565 db
.WriteString(pdb
, TAG_NAME
, Name
, true);
566 if (IsEmptyGuid(ExeID
))
568 db
.WriteBinary(pdb
, TAG_EXE_ID
, ExeID
);
571 db
.WriteString(pdb
, TAG_APP_NAME
, AppName
);
572 db
.WriteString(pdb
, TAG_VENDOR
, Vendor
);
574 if (!WriteGeneric(pdb
, MatchingFiles
, db
))
576 if (!WriteGeneric(pdb
, ShimRefs
, db
))
578 if (!WriteGeneric(pdb
, FlagRefs
, db
))
581 return !!db
.EndWriteListTag(pdb
, Tagid
);
585 /***********************************************************************
589 void Database::WriteBinary(PDB pdb
, TAG tag
, const GUID
& guid
, bool always
)
591 if (always
|| !IsEmptyGuid(guid
))
592 SdbWriteBinaryTag(pdb
, tag
, (BYTE
*)&guid
, sizeof(GUID
));
595 void Database::WriteBinary(PDB pdb
, TAG tag
, const std::vector
<BYTE
>& data
, bool always
)
597 if (always
|| !data
.empty())
598 SdbWriteBinaryTag(pdb
, tag
, data
.data(), data
.size());
601 void Database::WriteString(PDB pdb
, TAG tag
, const sdbstring
& str
, bool always
)
603 if (always
|| !str
.empty())
604 SdbWriteStringTag(pdb
, tag
, (LPCWSTR
)str
.c_str());
607 void Database::WriteString(PDB pdb
, TAG tag
, const std::string
& str
, bool always
)
609 WriteString(pdb
, tag
, sdbstring(str
.begin(), str
.end()), always
);
612 void Database::WriteDWord(PDB pdb
, TAG tag
, DWORD value
, bool always
)
615 SdbWriteDWORDTag(pdb
, tag
, value
);
618 void Database::WriteQWord(PDB pdb
, TAG tag
, QWORD value
, bool always
)
621 SdbWriteQWORDTag(pdb
, tag
, value
);
624 TAGID
Database::BeginWriteListTag(PDB pdb
, TAG tag
)
626 return SdbBeginWriteListTag(pdb
, tag
);
629 BOOL
Database::EndWriteListTag(PDB pdb
, TAGID tagid
)
631 return SdbEndWriteListTag(pdb
, tagid
);
634 bool Database::fromXml(XMLHandle dbNode
)
636 Name
= ReadStringNode(dbNode
, "NAME");
637 ReadGuidNode(dbNode
, "DATABASE_ID", ID
);
639 XMLHandle libChild
= dbNode
.FirstChildElement("LIBRARY").FirstChild();
640 while (libChild
.ToNode())
642 std::string NodeName
= ToNodeName(libChild
);
643 if (NodeName
== "SHIM")
646 if (shim
.fromXml(libChild
))
647 Library
.Shims
.push_back(shim
);
649 else if (NodeName
== "FLAG")
652 if (flag
.fromXml(libChild
))
653 Library
.Flags
.push_back(flag
);
655 else if (NodeName
== "INCLUDE" || NodeName
== "EXCLUDE")
658 if (inex
.fromXml(libChild
))
659 Library
.InExcludes
.push_back(inex
);
661 libChild
= libChild
.NextSibling();
664 ReadGeneric(dbNode
, Layers
, "LAYER");
665 ReadGeneric(dbNode
, Exes
, "EXE");
669 bool Database::fromXml(const char* fileName
)
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
);
677 bool Database::toSdb(LPCWSTR path
)
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);
689 SHIM_WARN("DB has empty ID!\n");
692 WriteBinary(pdb
, TAG_DATABASE_ID
, ID
);
693 TAGID tidLibrary
= BeginWriteListTag(pdb
, TAG_LIBRARY
);
694 if (!WriteGeneric(pdb
, Library
.InExcludes
, *this))
696 if (!WriteGeneric(pdb
, Library
.Shims
, *this))
698 if (!WriteGeneric(pdb
, Library
.Flags
, *this))
700 EndWriteListTag(pdb
, tidLibrary
);
701 if (!WriteGeneric(pdb
, Layers
, *this))
703 if (!WriteGeneric(pdb
, Exes
, *this))
705 EndWriteListTag(pdb
, tidDatabase
);
707 SdbCloseDatabaseWrite(pdb
);
711 static void InsertTagid(const sdbstring
& name
, TAGID tagid
, std::map
<sdbstring
, TAGID
>& lookup
, const char* type
)
713 sdbstring nameLower
= name
;
714 std::transform(nameLower
.begin(), nameLower
.end(), nameLower
.begin(), ::tolower
);
715 if (lookup
.find(nameLower
) != lookup
.end())
717 std::string
nameA(name
.begin(), name
.end());
718 SHIM_WARN("%s '%s' redefined\n", type
, nameA
.c_str());
721 lookup
[nameLower
] = tagid
;
724 static TAGID
FindTagid(const sdbstring
& name
, const std::map
<sdbstring
, TAGID
>& lookup
)
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())
734 void Database::InsertShimTagid(const sdbstring
& name
, TAGID tagid
)
736 InsertTagid(name
, tagid
, KnownShims
, "Shim");
739 TAGID
Database::FindShimTagid(const sdbstring
& name
)
741 return FindTagid(name
, KnownShims
);
744 void Database::InsertPatchTagid(const sdbstring
& name
, TAGID tagid
)
746 InsertTagid(name
, tagid
, KnownPatches
, "Patch");
749 TAGID
Database::FindPatchTagid(const sdbstring
& name
)
751 return FindTagid(name
, KnownPatches
);
754 void Database::InsertFlagTagid(const sdbstring
& name
, TAGID tagid
)
756 InsertTagid(name
, tagid
, KnownFlags
, "Flag");
759 TAGID
Database::FindFlagTagid(const sdbstring
& name
)
761 return FindTagid(name
, KnownFlags
);
765 bool xml_2_db(const char* xml
, const WCHAR
* sdb
)
770 return db
.toSdb((LPCWSTR
)sdb
);