3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: Conversion functions from xml -> db
5 * COPYRIGHT: Copyright 2016,2017 Mark Jansen (mark.jansen@reactos.org)
14 using tinyxml2::XMLText
;
16 static const GUID GUID_NULL
= { 0 };
17 static const char szCompilerVersion
[] = "1.6.0.0";
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
ReadDWordNode(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<DWORD
>(strtoul(value
.c_str(), NULL
, base
));
103 unsigned char char2byte(char hexChar
, bool* success
= NULL
)
105 if (hexChar
>= '0' && hexChar
<= '9')
106 return hexChar
- '0';
107 if (hexChar
>= 'A' && hexChar
<= 'F')
108 return hexChar
- 'A' + 10;
109 if (hexChar
>= 'a' && hexChar
<= 'f')
110 return hexChar
- 'a' + 10;
117 // adapted from wine's ntdll\rtlstr.c rev 1.45
118 static bool StringToGuid(const std::string
& str
, GUID
& guid
)
120 const char *lpszGUID
= str
.c_str();
121 BYTE
* lpOut
= (BYTE
*)&guid
;
123 bool expectBrace
= true;
129 if (*lpszGUID
!= '{')
141 if (*lpszGUID
!= '-')
146 return expectBrace
== (*lpszGUID
== '}');
150 CHAR ch
= *lpszGUID
, ch2
= lpszGUID
[1];
152 bool converted
= true;
154 byte
= char2byte(ch
, &converted
) << 4 | char2byte(ch2
, &converted
);
160 #ifndef WORDS_BIGENDIAN
161 /* For Big Endian machines, we store the data such that the
162 * dword/word members can be read as DWORDS and WORDS correctly. */
195 lpszGUID
++; /* Skip 2nd character of byte */
206 bool ReadGuidNode(XMLHandle dbNode
, const char* nodeName
, GUID
& guid
)
208 std::string value
= ReadStringNode(dbNode
, nodeName
);
209 if (!StringToGuid(value
, guid
))
211 memset(&guid
, 0, sizeof(GUID
));
217 bool ReadBinaryNode(XMLHandle dbNode
, const char* nodeName
, std::vector
<BYTE
>& data
)
219 std::string value
= ReadStringNode(dbNode
, nodeName
);
220 value
.erase(std::remove_if(value
.begin(), value
.end(), ::isspace
), value
.end());
222 size_t length
= value
.size() / 2;
223 if (length
* 2 != value
.size())
227 for (size_t n
= 0; n
< length
; ++n
)
229 data
[n
] = (BYTE
)(char2byte(value
[n
* 2]) << 4 | char2byte(value
[(n
* 2) + 1]));
235 /***********************************************************************
239 bool InExclude::fromXml(XMLHandle dbNode
)
241 Module
= ReadStringNode(dbNode
, "MODULE");
242 // Special module names: '$' and '*'
245 Include
= ToNodeName(dbNode
) == "INCLUDE";
251 bool InExclude::toSdb(PDB pdb
, Database
& db
)
253 TAGID tagid
= db
.BeginWriteListTag(pdb
, TAG_INEXCLUD
);
254 db
.WriteString(pdb
, TAG_MODULE
, Module
, true);
256 SdbWriteNULLTag(pdb
, TAG_INCLUDE
);
257 return !!db
.EndWriteListTag(pdb
, tagid
);
262 void ReadGeneric(XMLHandle dbNode
, std::list
<T
>& result
, const char* nodeName
)
264 XMLHandle node
= dbNode
.FirstChildElement(nodeName
);
265 while (node
.ToNode())
268 if (object
.fromXml(node
))
269 result
.push_back(object
);
271 node
= node
.NextSiblingElement(nodeName
);
276 bool WriteGeneric(PDB pdb
, std::list
<T
>& data
, Database
& db
)
278 for (typename
std::list
<T
>::iterator it
= data
.begin(); it
!= data
.end(); ++it
)
280 if (!it
->toSdb(pdb
, db
))
287 /***********************************************************************
291 bool ShimRef::fromXml(XMLHandle dbNode
)
293 Name
= ReadStringNode(dbNode
, "NAME");
294 CommandLine
= ReadStringNode(dbNode
, "COMMAND_LINE");
295 ReadGeneric(dbNode
, InExcludes
, "INCLUDE");
296 ReadGeneric(dbNode
, InExcludes
, "EXCLUDE");
297 return !Name
.empty();
300 bool ShimRef::toSdb(PDB pdb
, Database
& db
)
302 TAGID tagid
= db
.BeginWriteListTag(pdb
, TAG_SHIM_REF
);
303 db
.WriteString(pdb
, TAG_NAME
, Name
, true);
304 db
.WriteString(pdb
, TAG_COMMAND_LINE
, CommandLine
);
307 ShimTagid
= db
.FindShimTagid(Name
);
308 SdbWriteDWORDTag(pdb
, TAG_SHIM_TAGID
, ShimTagid
);
309 return !!db
.EndWriteListTag(pdb
, tagid
);
313 /***********************************************************************
317 bool Shim::fromXml(XMLHandle dbNode
)
319 Name
= ReadStringNode(dbNode
, "NAME");
320 DllFile
= ReadStringNode(dbNode
, "DLLFILE");
321 ReadGuidNode(dbNode
, "FIX_ID", FixID
);
324 ReadGeneric(dbNode
, InExcludes
, "INCLUDE");
325 ReadGeneric(dbNode
, InExcludes
, "EXCLUDE");
326 return !Name
.empty() && !DllFile
.empty();
329 bool Shim::toSdb(PDB pdb
, Database
& db
)
331 Tagid
= db
.BeginWriteListTag(pdb
, TAG_SHIM
);
332 db
.InsertShimTagid(Name
, Tagid
);
333 db
.WriteString(pdb
, TAG_NAME
, Name
);
334 db
.WriteString(pdb
, TAG_DLLFILE
, DllFile
);
335 if (IsEmptyGuid(FixID
))
337 db
.WriteBinary(pdb
, TAG_FIX_ID
, FixID
);
338 if (!WriteGeneric(pdb
, InExcludes
, db
))
340 return !!db
.EndWriteListTag(pdb
, Tagid
);
344 /***********************************************************************
348 bool Layer::fromXml(XMLHandle dbNode
)
350 Name
= ReadStringNode(dbNode
, "NAME");
351 ReadGeneric(dbNode
, ShimRefs
, "SHIM_REF");
355 bool Layer::toSdb(PDB pdb
, Database
& db
)
357 Tagid
= db
.BeginWriteListTag(pdb
, TAG_LAYER
);
358 db
.WriteString(pdb
, TAG_NAME
, Name
, true);
359 if (!WriteGeneric(pdb
, ShimRefs
, db
))
361 return !!db
.EndWriteListTag(pdb
, Tagid
);
365 /***********************************************************************
369 bool MatchingFile::fromXml(XMLHandle dbNode
)
371 Name
= ReadStringNode(dbNode
, "NAME");
372 Size
= ReadDWordNode(dbNode
, "SIZE");
373 Checksum
= ReadDWordNode(dbNode
, "CHECKSUM");
374 CompanyName
= ReadStringNode(dbNode
, "COMPANY_NAME");
375 InternalName
= ReadStringNode(dbNode
, "INTERNAL_NAME");
376 ProductName
= ReadStringNode(dbNode
, "PRODUCT_NAME");
377 ProductVersion
= ReadStringNode(dbNode
, "PRODUCT_VERSION");
378 FileVersion
= ReadStringNode(dbNode
, "FILE_VERSION");
379 BinFileVersion
= ReadStringNode(dbNode
, "BIN_FILE_VERSION");
380 LinkDate
= ReadStringNode(dbNode
, "LINK_DATE");
381 VerLanguage
= ReadStringNode(dbNode
, "VER_LANGUAGE");
382 FileDescription
= ReadStringNode(dbNode
, "FILE_DESCRIPTION");
383 OriginalFilename
= ReadStringNode(dbNode
, "ORIGINAL_FILENAME");
384 UptoBinFileVersion
= ReadStringNode(dbNode
, "UPTO_BIN_FILE_VERSION");
385 LinkerVersion
= ReadStringNode(dbNode
, "LINKER_VERSION");
389 bool MatchingFile::toSdb(PDB pdb
, Database
& db
)
391 TAGID tagid
= db
.BeginWriteListTag(pdb
, TAG_MATCHING_FILE
);
393 db
.WriteString(pdb
, TAG_NAME
, Name
, true);
394 db
.WriteDWord(pdb
, TAG_SIZE
, Size
);
395 db
.WriteDWord(pdb
, TAG_CHECKSUM
, Checksum
);
396 db
.WriteString(pdb
, TAG_COMPANY_NAME
, CompanyName
);
397 db
.WriteString(pdb
, TAG_INTERNAL_NAME
, InternalName
);
398 db
.WriteString(pdb
, TAG_PRODUCT_NAME
, ProductName
);
399 db
.WriteString(pdb
, TAG_PRODUCT_VERSION
, ProductVersion
);
400 db
.WriteString(pdb
, TAG_FILE_VERSION
, FileVersion
);
401 if (!BinFileVersion
.empty())
402 SHIM_ERR("TAG_BIN_FILE_VERSION Unimplemented\n"); //db.WriteQWord(pdb, TAG_BIN_FILE_VERSION, BinFileVersion);
403 if (!LinkDate
.empty())
404 SHIM_ERR("TAG_LINK_DATE Unimplemented\n"); //db.WriteDWord(pdb, TAG_LINK_DATE, LinkDate);
405 if (!VerLanguage
.empty())
406 SHIM_ERR("TAG_VER_LANGUAGE Unimplemented\n"); //db.WriteDWord(pdb, TAG_VER_LANGUAGE, VerLanguage);
407 db
.WriteString(pdb
, TAG_FILE_DESCRIPTION
, FileDescription
);
408 db
.WriteString(pdb
, TAG_ORIGINAL_FILENAME
, OriginalFilename
);
409 if (!UptoBinFileVersion
.empty())
410 SHIM_ERR("TAG_UPTO_BIN_FILE_VERSION Unimplemented\n"); //db.WriteQWord(pdb, TAG_UPTO_BIN_FILE_VERSION, UptoBinFileVersion);
411 if (!LinkerVersion
.empty())
412 SHIM_ERR("TAG_LINKER_VERSION Unimplemented\n"); //db.WriteDWord(pdb, TAG_LINKER_VERSION, LinkerVersion);
415 return !!db
.EndWriteListTag(pdb
, tagid
);
419 /***********************************************************************
423 bool Exe::fromXml(XMLHandle dbNode
)
425 Name
= ReadStringNode(dbNode
, "NAME");
426 ReadGuidNode(dbNode
, "EXE_ID", ExeID
);
427 AppName
= ReadStringNode(dbNode
, "APP_NAME");
428 Vendor
= ReadStringNode(dbNode
, "VENDOR");
430 ReadGeneric(dbNode
, MatchingFiles
, "MATCHING_FILE");
432 ReadGeneric(dbNode
, ShimRefs
, "SHIM_REF");
434 return !Name
.empty();
437 bool Exe::toSdb(PDB pdb
, Database
& db
)
439 Tagid
= db
.BeginWriteListTag(pdb
, TAG_EXE
);
441 db
.WriteString(pdb
, TAG_NAME
, Name
, true);
442 if (IsEmptyGuid(ExeID
))
444 db
.WriteBinary(pdb
, TAG_EXE_ID
, ExeID
);
447 db
.WriteString(pdb
, TAG_APP_NAME
, AppName
);
448 db
.WriteString(pdb
, TAG_VENDOR
, Vendor
);
450 if (!WriteGeneric(pdb
, MatchingFiles
, db
))
452 if (!WriteGeneric(pdb
, ShimRefs
, db
))
455 return !!db
.EndWriteListTag(pdb
, Tagid
);
459 /***********************************************************************
463 void Database::WriteBinary(PDB pdb
, TAG tag
, const GUID
& guid
, bool always
)
465 if (always
|| !IsEmptyGuid(guid
))
466 SdbWriteBinaryTag(pdb
, tag
, (BYTE
*)&guid
, sizeof(GUID
));
469 void Database::WriteBinary(PDB pdb
, TAG tag
, const std::vector
<BYTE
>& data
, bool always
)
471 if (always
|| !data
.empty())
472 SdbWriteBinaryTag(pdb
, tag
, data
.data(), data
.size());
475 void Database::WriteString(PDB pdb
, TAG tag
, const sdbstring
& str
, bool always
)
477 if (always
|| !str
.empty())
478 SdbWriteStringTag(pdb
, tag
, (LPCWSTR
)str
.c_str());
481 void Database::WriteString(PDB pdb
, TAG tag
, const std::string
& str
, bool always
)
483 WriteString(pdb
, tag
, sdbstring(str
.begin(), str
.end()), always
);
486 void Database::WriteDWord(PDB pdb
, TAG tag
, DWORD value
, bool always
)
489 SdbWriteDWORDTag(pdb
, tag
, value
);
492 TAGID
Database::BeginWriteListTag(PDB pdb
, TAG tag
)
494 return SdbBeginWriteListTag(pdb
, tag
);
497 BOOL
Database::EndWriteListTag(PDB pdb
, TAGID tagid
)
499 return SdbEndWriteListTag(pdb
, tagid
);
502 bool Database::fromXml(XMLHandle dbNode
)
504 Name
= ReadStringNode(dbNode
, "NAME");
505 ReadGuidNode(dbNode
, "DATABASE_ID", ID
);
507 XMLHandle libChild
= dbNode
.FirstChildElement("LIBRARY").FirstChild();
508 while (libChild
.ToNode())
510 std::string NodeName
= ToNodeName(libChild
);
511 if (NodeName
== "SHIM")
514 if (shim
.fromXml(libChild
))
515 Library
.Shims
.push_back(shim
);
517 else if (NodeName
== "FLAG")
519 SHIM_ERR("Unhanled FLAG type\n");
521 else if (NodeName
== "INCLUDE" || NodeName
== "EXCLUDE")
524 if (inex
.fromXml(libChild
))
525 Library
.InExcludes
.push_back(inex
);
527 libChild
= libChild
.NextSibling();
530 ReadGeneric(dbNode
, Layers
, "LAYER");
531 ReadGeneric(dbNode
, Exes
, "EXE");
535 bool Database::fromXml(const char* fileName
)
537 tinyxml2::XMLDocument doc
;
538 tinyxml2::XMLError err
= doc
.LoadFile(fileName
);
539 XMLHandle dbHandle
= tinyxml2::XMLHandle(&doc
).FirstChildElement("SDB").FirstChildElement("DATABASE");
540 return fromXml(dbHandle
);
543 bool Database::toSdb(LPCWSTR path
)
545 PDB pdb
= SdbCreateDatabase(path
, DOS_PATH
);
546 TAGID tidDatabase
= BeginWriteListTag(pdb
, TAG_DATABASE
);
547 LARGE_INTEGER li
= { 0 };
548 RtlSecondsSince1970ToTime(time(0), &li
);
549 SdbWriteQWORDTag(pdb
, TAG_TIME
, li
.QuadPart
);
550 WriteString(pdb
, TAG_COMPILER_VERSION
, szCompilerVersion
);
551 SdbWriteDWORDTag(pdb
, TAG_OS_PLATFORM
, 1);
552 WriteString(pdb
, TAG_NAME
, Name
, true);
555 SHIM_WARN("DB has empty ID!\n");
558 WriteBinary(pdb
, TAG_DATABASE_ID
, ID
);
559 TAGID tidLibrary
= BeginWriteListTag(pdb
, TAG_LIBRARY
);
560 if (!WriteGeneric(pdb
, Library
.InExcludes
, *this))
562 if (!WriteGeneric(pdb
, Library
.Shims
, *this))
564 EndWriteListTag(pdb
, tidLibrary
);
565 if (!WriteGeneric(pdb
, Layers
, *this))
567 if (!WriteGeneric(pdb
, Exes
, *this))
569 EndWriteListTag(pdb
, tidDatabase
);
571 SdbCloseDatabaseWrite(pdb
);
575 static void InsertTagid(const sdbstring
& name
, TAGID tagid
, std::map
<sdbstring
, TAGID
>& lookup
, const char* type
)
577 sdbstring nameLower
= name
;
578 std::transform(nameLower
.begin(), nameLower
.end(), nameLower
.begin(), ::tolower
);
579 if (lookup
.find(nameLower
) != lookup
.end())
581 std::string
nameA(name
.begin(), name
.end());
582 SHIM_WARN("%s '%s' redefined\n", type
, nameA
.c_str());
585 lookup
[nameLower
] = tagid
;
588 static TAGID
FindTagid(const sdbstring
& name
, const std::map
<sdbstring
, TAGID
>& lookup
)
590 sdbstring nameLower
= name
;
591 std::transform(nameLower
.begin(), nameLower
.end(), nameLower
.begin(), ::tolower
);
592 std::map
<sdbstring
, TAGID
>::const_iterator it
= lookup
.find(nameLower
);
593 if (it
== lookup
.end())
598 void Database::InsertShimTagid(const sdbstring
& name
, TAGID tagid
)
600 InsertTagid(name
, tagid
, KnownShims
, "Shim");
603 TAGID
Database::FindShimTagid(const sdbstring
& name
)
605 return FindTagid(name
, KnownShims
);
608 void Database::InsertPatchTagid(const sdbstring
& name
, TAGID tagid
)
610 InsertTagid(name
, tagid
, KnownPatches
, "Patch");
613 TAGID
Database::FindPatchTagid(const sdbstring
& name
)
615 return FindTagid(name
, KnownPatches
);
620 bool xml_2_db(const char* xml
, const WCHAR
* sdb
)
625 return db
.toSdb((LPCWSTR
)sdb
);