2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS xml to sdb converter
4 * FILE: sdk/tools/xml2sdb/xml2sdb.cpp
5 * PURPOSE: Conversion functions from xml -> db
6 * PROGRAMMERS: Mark Jansen
16 using tinyxml2::XMLText
;
18 static const GUID GUID_NULL
= { 0 };
19 static const char szCompilerVersion
[] = "1.5.0.0";
21 #if !defined(C_ASSERT)
22 #define C_ASSERT(expr) extern char (*c_assert(void)) [(expr) ? 1 : -1]
26 C_ASSERT(sizeof(GUID
) == 16);
27 C_ASSERT(sizeof(ULONG
) == 4);
28 C_ASSERT(sizeof(LARGE_INTEGER
) == 8);
29 C_ASSERT(sizeof(WCHAR
) == 2);
30 C_ASSERT(sizeof(wchar_t) == 2);
31 C_ASSERT(sizeof(TAG
) == 2);
32 C_ASSERT(sizeof(TAGID
) == 4);
36 VOID NTAPI
RtlSecondsSince1970ToTime(IN ULONG SecondsSince1970
,
37 OUT PLARGE_INTEGER Time
);
40 /***********************************************************************
45 // Convert utf8 to utf16:
46 // http://stackoverflow.com/a/7154226/4928207
48 bool IsEmptyGuid(const GUID
& g
)
50 return !memcmp(&g
, &GUID_NULL
, sizeof(GUID
));
53 void RandomGuid(GUID
& g
)
56 for (size_t n
= 0; n
< sizeof(GUID
); ++n
)
57 p
[n
] = (BYTE
)(rand() % 0xff);
60 // Given a node, return the node value (safe)
61 std::string
ToString(XMLHandle node
)
63 XMLText
* txtNode
= node
.FirstChild().ToText();
64 const char* txt
= txtNode
? txtNode
->Value() : NULL
;
66 return std::string(txt
);
70 // Given a node, return the node name (safe)
71 std::string
ToNodeName(XMLHandle node
)
73 tinyxml2::XMLNode
* raw
= node
.ToNode();
74 const char* txt
= raw
? raw
->Value() : NULL
;
76 return std::string(txt
);
80 // Read either an attribute, or a child node
81 std::string
ReadStringNode(XMLHandle dbNode
, const char* nodeName
)
83 tinyxml2::XMLElement
* elem
= dbNode
.ToElement();
86 const char* rawVal
= elem
->Attribute(nodeName
);
88 return std::string(rawVal
);
90 return ToString(dbNode
.FirstChildElement(nodeName
));
93 DWORD
ReadDWordNode(XMLHandle dbNode
, const char* nodeName
)
95 std::string value
= ReadStringNode(dbNode
, nodeName
);
97 if (value
.size() > 2 && value
[0] == '0' && value
[1] == 'x')
100 value
= value
.substr(2);
102 return static_cast<DWORD
>(strtoul(value
.c_str(), NULL
, base
));
105 unsigned char char2byte(char hexChar
, bool* success
= NULL
)
107 if (hexChar
>= '0' && hexChar
<= '9')
108 return hexChar
- '0';
109 if (hexChar
>= 'A' && hexChar
<= 'F')
110 return hexChar
- 'A' + 10;
111 if (hexChar
>= 'a' && hexChar
<= 'f')
112 return hexChar
- 'a' + 10;
119 // adapted from wine's ntdll\rtlstr.c rev 1.45
120 static bool StringToGuid(const std::string
& str
, GUID
& guid
)
122 const char *lpszGUID
= str
.c_str();
123 BYTE
* lpOut
= (BYTE
*)&guid
;
125 bool expectBrace
= true;
131 if (*lpszGUID
!= '{')
143 if (*lpszGUID
!= '-')
148 return expectBrace
== (*lpszGUID
== '}');
152 CHAR ch
= *lpszGUID
, ch2
= lpszGUID
[1];
154 bool converted
= true;
156 byte
= char2byte(ch
, &converted
) << 4 | char2byte(ch2
, &converted
);
162 #ifndef WORDS_BIGENDIAN
163 /* For Big Endian machines, we store the data such that the
164 * dword/word members can be read as DWORDS and WORDS correctly. */
197 lpszGUID
++; /* Skip 2nd character of byte */
208 bool ReadGuidNode(XMLHandle dbNode
, const char* nodeName
, GUID
& guid
)
210 std::string value
= ReadStringNode(dbNode
, nodeName
);
211 if (!StringToGuid(value
, guid
))
213 memset(&guid
, 0, sizeof(GUID
));
219 bool ReadBinaryNode(XMLHandle dbNode
, const char* nodeName
, std::vector
<BYTE
>& data
)
221 std::string value
= ReadStringNode(dbNode
, nodeName
);
222 value
.erase(std::remove_if(value
.begin(), value
.end(), ::isspace
), value
.end());
224 size_t length
= value
.size() / 2;
225 if (length
* 2 != value
.size())
229 for (size_t n
= 0; n
< length
; ++n
)
231 data
[n
] = (BYTE
)(char2byte(value
[n
* 2]) << 4 | char2byte(value
[(n
* 2) + 1]));
237 /***********************************************************************
241 bool InExclude::fromXml(XMLHandle dbNode
)
243 Module
= ReadStringNode(dbNode
, "MODULE");
246 Include
= dbNode
.FirstChildElement("INCLUDE").ToNode() != NULL
;
249 tinyxml2::XMLElement
* elem
= dbNode
.ToElement();
252 Include
|= (elem
->Attribute("INCLUDE") != NULL
);
262 bool InExclude::toSdb(PDB pdb
, Database
& db
)
264 TAGID tagid
= db
.BeginWriteListTag(pdb
, TAG_INEXCLUD
);
265 db
.WriteString(pdb
, TAG_MODULE
, Module
, true);
267 SdbWriteNULLTag(pdb
, TAG_INCLUDE
);
268 return !!db
.EndWriteListTag(pdb
, tagid
);
273 void ReadGeneric(XMLHandle dbNode
, std::list
<T
>& result
, const char* nodeName
)
275 XMLHandle node
= dbNode
.FirstChildElement(nodeName
);
276 while (node
.ToNode())
279 if (object
.fromXml(node
))
280 result
.push_back(object
);
282 node
= node
.NextSiblingElement(nodeName
);
287 bool WriteGeneric(PDB pdb
, std::list
<T
>& data
, Database
& db
)
289 for (typename
std::list
<T
>::iterator it
= data
.begin(); it
!= data
.end(); ++it
)
291 if (!it
->toSdb(pdb
, db
))
298 /***********************************************************************
302 bool ShimRef::fromXml(XMLHandle dbNode
)
304 Name
= ReadStringNode(dbNode
, "NAME");
305 CommandLine
= ReadStringNode(dbNode
, "COMMAND_LINE");
306 ReadGeneric(dbNode
, InExcludes
, "INEXCLUDE");
307 return !Name
.empty();
310 bool ShimRef::toSdb(PDB pdb
, Database
& db
)
312 TAGID tagid
= db
.BeginWriteListTag(pdb
, TAG_SHIM_REF
);
313 db
.WriteString(pdb
, TAG_NAME
, Name
, true);
314 db
.WriteString(pdb
, TAG_COMMAND_LINE
, CommandLine
);
317 ShimTagid
= db
.FindShimTagid(Name
);
318 SdbWriteDWORDTag(pdb
, TAG_SHIM_TAGID
, ShimTagid
);
319 return !!db
.EndWriteListTag(pdb
, tagid
);
323 /***********************************************************************
327 bool Shim::fromXml(XMLHandle dbNode
)
329 Name
= ReadStringNode(dbNode
, "NAME");
330 DllFile
= ReadStringNode(dbNode
, "DLLFILE");
331 ReadGuidNode(dbNode
, "FIX_ID", FixID
);
334 ReadGeneric(dbNode
, InExcludes
, "INEXCLUDE");
335 return !Name
.empty() && !DllFile
.empty();
338 bool Shim::toSdb(PDB pdb
, Database
& db
)
340 Tagid
= db
.BeginWriteListTag(pdb
, TAG_SHIM
);
341 db
.InsertShimTagid(Name
, Tagid
);
342 db
.WriteString(pdb
, TAG_NAME
, Name
);
343 db
.WriteString(pdb
, TAG_DLLFILE
, DllFile
);
344 if (IsEmptyGuid(FixID
))
346 db
.WriteBinary(pdb
, TAG_FIX_ID
, FixID
);
347 if (!WriteGeneric(pdb
, InExcludes
, db
))
349 return !!db
.EndWriteListTag(pdb
, Tagid
);
353 /***********************************************************************
357 bool Layer::fromXml(XMLHandle dbNode
)
359 Name
= ReadStringNode(dbNode
, "NAME");
360 ReadGeneric(dbNode
, ShimRefs
, "SHIM_REF");
364 bool Layer::toSdb(PDB pdb
, Database
& db
)
366 Tagid
= db
.BeginWriteListTag(pdb
, TAG_LAYER
);
367 db
.WriteString(pdb
, TAG_NAME
, Name
, true);
368 if (!WriteGeneric(pdb
, ShimRefs
, db
))
370 return !!db
.EndWriteListTag(pdb
, Tagid
);
374 /***********************************************************************
378 bool MatchingFile::fromXml(XMLHandle dbNode
)
380 Name
= ReadStringNode(dbNode
, "NAME");
381 Size
= ReadDWordNode(dbNode
, "SIZE");
382 Checksum
= ReadDWordNode(dbNode
, "CHECKSUM");
383 CompanyName
= ReadStringNode(dbNode
, "COMPANY_NAME");
384 InternalName
= ReadStringNode(dbNode
, "INTERNAL_NAME");
385 ProductName
= ReadStringNode(dbNode
, "PRODUCT_NAME");
386 ProductVersion
= ReadStringNode(dbNode
, "PRODUCT_VERSION");
387 FileVersion
= ReadStringNode(dbNode
, "FILE_VERSION");
388 BinFileVersion
= ReadStringNode(dbNode
, "BIN_FILE_VERSION");
389 LinkDate
= ReadStringNode(dbNode
, "LINK_DATE");
390 VerLanguage
= ReadStringNode(dbNode
, "VER_LANGUAGE");
391 FileDescription
= ReadStringNode(dbNode
, "FILE_DESCRIPTION");
392 OriginalFilename
= ReadStringNode(dbNode
, "ORIGINAL_FILENAME");
393 UptoBinFileVersion
= ReadStringNode(dbNode
, "UPTO_BIN_FILE_VERSION");
394 LinkerVersion
= ReadStringNode(dbNode
, "LINKER_VERSION");
398 bool MatchingFile::toSdb(PDB pdb
, Database
& db
)
400 TAGID tagid
= db
.BeginWriteListTag(pdb
, TAG_MATCHING_FILE
);
402 db
.WriteString(pdb
, TAG_NAME
, Name
, true);
403 db
.WriteDWord(pdb
, TAG_SIZE
, Size
);
404 db
.WriteDWord(pdb
, TAG_CHECKSUM
, Checksum
);
405 db
.WriteString(pdb
, TAG_COMPANY_NAME
, CompanyName
);
406 db
.WriteString(pdb
, TAG_INTERNAL_NAME
, InternalName
);
407 db
.WriteString(pdb
, TAG_PRODUCT_NAME
, ProductName
);
408 db
.WriteString(pdb
, TAG_PRODUCT_VERSION
, ProductVersion
);
409 db
.WriteString(pdb
, TAG_FILE_VERSION
, FileVersion
);
410 if (!BinFileVersion
.empty())
411 SHIM_ERR("TAG_BIN_FILE_VERSION Unimplemented\n"); //db.WriteQWord(pdb, TAG_BIN_FILE_VERSION, BinFileVersion);
412 if (!LinkDate
.empty())
413 SHIM_ERR("TAG_LINK_DATE Unimplemented\n"); //db.WriteDWord(pdb, TAG_LINK_DATE, LinkDate);
414 if (!VerLanguage
.empty())
415 SHIM_ERR("TAG_VER_LANGUAGE Unimplemented\n"); //db.WriteDWord(pdb, TAG_VER_LANGUAGE, VerLanguage);
416 db
.WriteString(pdb
, TAG_FILE_DESCRIPTION
, FileDescription
);
417 db
.WriteString(pdb
, TAG_ORIGINAL_FILENAME
, OriginalFilename
);
418 if (!UptoBinFileVersion
.empty())
419 SHIM_ERR("TAG_UPTO_BIN_FILE_VERSION Unimplemented\n"); //db.WriteQWord(pdb, TAG_UPTO_BIN_FILE_VERSION, UptoBinFileVersion);
420 if (!LinkerVersion
.empty())
421 SHIM_ERR("TAG_LINKER_VERSION Unimplemented\n"); //db.WriteDWord(pdb, TAG_LINKER_VERSION, LinkerVersion);
424 return !!db
.EndWriteListTag(pdb
, tagid
);
428 /***********************************************************************
432 bool Exe::fromXml(XMLHandle dbNode
)
434 Name
= ReadStringNode(dbNode
, "NAME");
435 ReadGuidNode(dbNode
, "EXE_ID", ExeID
);
436 AppName
= ReadStringNode(dbNode
, "APP_NAME");
437 Vendor
= ReadStringNode(dbNode
, "VENDOR");
439 ReadGeneric(dbNode
, MatchingFiles
, "MATCHING_FILE");
441 ReadGeneric(dbNode
, ShimRefs
, "SHIM_REF");
443 return !Name
.empty();
446 bool Exe::toSdb(PDB pdb
, Database
& db
)
448 Tagid
= db
.BeginWriteListTag(pdb
, TAG_EXE
);
450 db
.WriteString(pdb
, TAG_NAME
, Name
, true);
451 if (IsEmptyGuid(ExeID
))
453 db
.WriteBinary(pdb
, TAG_EXE_ID
, ExeID
);
456 db
.WriteString(pdb
, TAG_APP_NAME
, AppName
);
457 db
.WriteString(pdb
, TAG_VENDOR
, Vendor
);
459 if (!WriteGeneric(pdb
, MatchingFiles
, db
))
461 if (!WriteGeneric(pdb
, ShimRefs
, db
))
464 return !!db
.EndWriteListTag(pdb
, Tagid
);
468 /***********************************************************************
472 void Database::WriteBinary(PDB pdb
, TAG tag
, const GUID
& guid
, bool always
)
474 if (always
|| !IsEmptyGuid(guid
))
475 SdbWriteBinaryTag(pdb
, tag
, (BYTE
*)&guid
, sizeof(GUID
));
478 void Database::WriteBinary(PDB pdb
, TAG tag
, const std::vector
<BYTE
>& data
, bool always
)
480 if (always
|| !data
.empty())
481 SdbWriteBinaryTag(pdb
, tag
, data
.data(), data
.size());
484 void Database::WriteString(PDB pdb
, TAG tag
, const sdbstring
& str
, bool always
)
486 if (always
|| !str
.empty())
487 SdbWriteStringTag(pdb
, tag
, (LPCWSTR
)str
.c_str());
490 void Database::WriteString(PDB pdb
, TAG tag
, const std::string
& str
, bool always
)
492 WriteString(pdb
, tag
, sdbstring(str
.begin(), str
.end()), always
);
495 void Database::WriteDWord(PDB pdb
, TAG tag
, DWORD value
, bool always
)
498 SdbWriteDWORDTag(pdb
, tag
, value
);
501 TAGID
Database::BeginWriteListTag(PDB db
, TAG tag
)
503 return SdbBeginWriteListTag(db
, tag
);
506 BOOL
Database::EndWriteListTag(PDB db
, TAGID tagid
)
508 return SdbEndWriteListTag(db
, tagid
);
511 bool Database::fromXml(XMLHandle dbNode
)
513 Name
= ReadStringNode(dbNode
, "NAME");
514 ReadGuidNode(dbNode
, "DATABASE_ID", ID
);
516 XMLHandle libChild
= dbNode
.FirstChildElement("LIBRARY").FirstChild();
517 while (libChild
.ToNode())
519 std::string NodeName
= ToNodeName(libChild
);
520 if (NodeName
== "SHIM")
523 if (shim
.fromXml(libChild
))
524 Library
.Shims
.push_back(shim
);
526 else if (NodeName
== "FLAG")
528 SHIM_ERR("Unhanled FLAG type\n");
530 libChild
= libChild
.NextSibling();
533 ReadGeneric(dbNode
, Layers
, "LAYER");
534 ReadGeneric(dbNode
, Exes
, "EXE");
538 bool Database::fromXml(const char* fileName
)
540 tinyxml2::XMLDocument doc
;
541 tinyxml2::XMLError err
= doc
.LoadFile(fileName
);
542 XMLHandle dbHandle
= tinyxml2::XMLHandle(&doc
).FirstChildElement("SDB").FirstChildElement("DATABASE");
543 return fromXml(dbHandle
);
546 bool Database::toSdb(LPCWSTR path
)
548 PDB pdb
= SdbCreateDatabase(path
, DOS_PATH
);
549 TAGID tidDatabase
= BeginWriteListTag(pdb
, TAG_DATABASE
);
550 LARGE_INTEGER li
= { 0 };
551 RtlSecondsSince1970ToTime(time(0), &li
);
552 SdbWriteQWORDTag(pdb
, TAG_TIME
, li
.QuadPart
);
553 WriteString(pdb
, TAG_COMPILER_VERSION
, szCompilerVersion
);
554 SdbWriteDWORDTag(pdb
, TAG_OS_PLATFORM
, 1);
555 WriteString(pdb
, TAG_NAME
, Name
, true);
558 SHIM_WARN("DB has empty ID!\n");
561 WriteBinary(pdb
, TAG_DATABASE_ID
, ID
);
562 TAGID tidLibrary
= BeginWriteListTag(pdb
, TAG_LIBRARY
);
563 if (!WriteGeneric(pdb
, Library
.Shims
, *this))
565 EndWriteListTag(pdb
, tidLibrary
);
566 if (!WriteGeneric(pdb
, Layers
, *this))
568 if (!WriteGeneric(pdb
, Exes
, *this))
570 EndWriteListTag(pdb
, tidDatabase
);
572 SdbCloseDatabaseWrite(pdb
);
576 static void InsertTagid(const sdbstring
& name
, TAGID tagid
, std::map
<sdbstring
, TAGID
>& lookup
, const char* type
)
578 sdbstring nameLower
= name
;
579 std::transform(nameLower
.begin(), nameLower
.end(), nameLower
.begin(), ::tolower
);
580 if (lookup
.find(nameLower
) != lookup
.end())
582 std::string
nameA(name
.begin(), name
.end());
583 SHIM_WARN("%s '%s' redefined\n", type
, nameA
.c_str());
586 lookup
[nameLower
] = tagid
;
589 static TAGID
FindTagid(const sdbstring
& name
, const std::map
<sdbstring
, TAGID
>& lookup
)
591 sdbstring nameLower
= name
;
592 std::transform(nameLower
.begin(), nameLower
.end(), nameLower
.begin(), ::tolower
);
593 std::map
<sdbstring
, TAGID
>::const_iterator it
= lookup
.find(nameLower
);
594 if (it
== lookup
.end())
599 void Database::InsertShimTagid(const sdbstring
& name
, TAGID tagid
)
601 InsertTagid(name
, tagid
, KnownShims
, "Shim");
604 TAGID
Database::FindShimTagid(const sdbstring
& name
)
606 return FindTagid(name
, KnownShims
);
609 void Database::InsertPatchTagid(const sdbstring
& name
, TAGID tagid
)
611 InsertTagid(name
, tagid
, KnownPatches
, "Patch");
614 TAGID
Database::FindPatchTagid(const sdbstring
& name
)
616 return FindTagid(name
, KnownPatches
);
621 bool xml_2_db(const char* xml
, const WCHAR
* sdb
)
626 return db
.toSdb((LPCWSTR
)sdb
);