+++ /dev/null
-/*
- * PROJECT: xml2sdb
- * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
- * PURPOSE: Conversion functions from xml -> db
- * COPYRIGHT: Copyright 2016,2017 Mark Jansen (mark.jansen@reactos.org)
- */
-
-#include "xml2sdb.h"
-#include "sdbpapi.h"
-#include "tinyxml2.h"
-#include <time.h>
-#include <algorithm>
-
-using tinyxml2::XMLText;
-
-static const GUID GUID_NULL = { 0 };
-static const char szCompilerVersion[] = "1.6.0.0";
-
-#if !defined(C_ASSERT)
-#define C_ASSERT(expr) extern char (*c_assert(void)) [(expr) ? 1 : -1]
-#endif
-
-
-C_ASSERT(sizeof(GUID) == 16);
-C_ASSERT(sizeof(ULONG) == 4);
-C_ASSERT(sizeof(LARGE_INTEGER) == 8);
-C_ASSERT(sizeof(WCHAR) == 2);
-C_ASSERT(sizeof(wchar_t) == 2);
-C_ASSERT(sizeof(TAG) == 2);
-C_ASSERT(sizeof(TAGID) == 4);
-
-
-extern "C"
-VOID NTAPI RtlSecondsSince1970ToTime(IN ULONG SecondsSince1970,
- OUT PLARGE_INTEGER Time);
-
-
-/***********************************************************************
- * Helper functions
- */
-
-
-// Convert utf8 to utf16:
-// http://stackoverflow.com/a/7154226/4928207
-
-bool IsEmptyGuid(const GUID& g)
-{
- return !memcmp(&g, &GUID_NULL, sizeof(GUID));
-}
-
-void RandomGuid(GUID& g)
-{
- BYTE* p = (BYTE*)&g;
- for (size_t n = 0; n < sizeof(GUID); ++n)
- p[n] = (BYTE)(rand() % 0xff);
-}
-
-// Given a node, return the node value (safe)
-std::string ToString(XMLHandle node)
-{
- XMLText* txtNode = node.FirstChild().ToText();
- const char* txt = txtNode ? txtNode->Value() : NULL;
- if (txt)
- return std::string(txt);
- return std::string();
-}
-
-// Given a node, return the node name (safe)
-std::string ToNodeName(XMLHandle node)
-{
- tinyxml2::XMLNode* raw = node.ToNode();
- const char* txt = raw ? raw->Value() : NULL;
- if (txt)
- return std::string(txt);
- return std::string();
-}
-
-// Read either an attribute, or a child node
-std::string ReadStringNode(XMLHandle dbNode, const char* nodeName)
-{
- tinyxml2::XMLElement* elem = dbNode.ToElement();
- if (elem)
- {
- const char* rawVal = elem->Attribute(nodeName);
- if (rawVal)
- return std::string(rawVal);
- }
- return ToString(dbNode.FirstChildElement(nodeName));
-}
-
-DWORD ReadDWordNode(XMLHandle dbNode, const char* nodeName)
-{
- std::string value = ReadStringNode(dbNode, nodeName);
- int base = 10;
- if (value.size() > 2 && value[0] == '0' && value[1] == 'x')
- {
- base = 16;
- value = value.substr(2);
- }
- return static_cast<DWORD>(strtoul(value.c_str(), NULL, base));
-}
-
-unsigned char char2byte(char hexChar, bool* success = NULL)
-{
- if (hexChar >= '0' && hexChar <= '9')
- return hexChar - '0';
- if (hexChar >= 'A' && hexChar <= 'F')
- return hexChar - 'A' + 10;
- if (hexChar >= 'a' && hexChar <= 'f')
- return hexChar - 'a' + 10;
-
- if (success)
- *success = false;
- return 0;
-}
-
-// adapted from wine's ntdll\rtlstr.c rev 1.45
-static bool StringToGuid(const std::string& str, GUID& guid)
-{
- const char *lpszGUID = str.c_str();
- BYTE* lpOut = (BYTE*)&guid;
- int i = 0;
- bool expectBrace = true;
- while (i <= 37)
- {
- switch (i)
- {
- case 0:
- if (*lpszGUID != '{')
- {
- i++;
- expectBrace = false;
- continue;
- }
- break;
-
- case 9:
- case 14:
- case 19:
- case 24:
- if (*lpszGUID != '-')
- return false;
- break;
-
- case 37:
- return expectBrace == (*lpszGUID == '}');
-
- default:
- {
- CHAR ch = *lpszGUID, ch2 = lpszGUID[1];
- unsigned char byte;
- bool converted = true;
-
- byte = char2byte(ch, &converted) << 4 | char2byte(ch2, &converted);
- if (!converted)
- return false;
-
- switch (i)
- {
-#ifndef WORDS_BIGENDIAN
- /* For Big Endian machines, we store the data such that the
- * dword/word members can be read as DWORDS and WORDS correctly. */
- /* Dword */
- case 1:
- lpOut[3] = byte;
- break;
- case 3:
- lpOut[2] = byte;
- break;
- case 5:
- lpOut[1] = byte;
- break;
- case 7:
- lpOut[0] = byte;
- lpOut += 4;
- break;
- /* Word */
- case 10:
- case 15:
- lpOut[1] = byte;
- break;
- case 12:
- case 17:
- lpOut[0] = byte;
- lpOut += 2;
- break;
-#endif
- /* Byte */
- default:
- lpOut[0] = byte;
- lpOut++;
- break;
- }
-
- lpszGUID++; /* Skip 2nd character of byte */
- i++;
- }
- }
-
- lpszGUID++;
- i++;
- }
- return false;
-}
-
-bool ReadGuidNode(XMLHandle dbNode, const char* nodeName, GUID& guid)
-{
- std::string value = ReadStringNode(dbNode, nodeName);
- if (!StringToGuid(value, guid))
- {
- memset(&guid, 0, sizeof(GUID));
- return false;
- }
- return true;
-}
-
-bool ReadBinaryNode(XMLHandle dbNode, const char* nodeName, std::vector<BYTE>& data)
-{
- std::string value = ReadStringNode(dbNode, nodeName);
- value.erase(std::remove_if(value.begin(), value.end(), ::isspace), value.end());
-
- size_t length = value.size() / 2;
- if (length * 2 != value.size())
- return false;
-
- data.resize(length);
- for (size_t n = 0; n < length; ++n)
- {
- data[n] = (BYTE)(char2byte(value[n * 2]) << 4 | char2byte(value[(n * 2) + 1]));
- }
- return true;
-}
-
-
-/***********************************************************************
- * InExclude
- */
-
-bool InExclude::fromXml(XMLHandle dbNode)
-{
- Module = ReadStringNode(dbNode, "MODULE");
- // Special module names: '$' and '*'
- if (!Module.empty())
- {
- Include = ToNodeName(dbNode) == "INCLUDE";
- return true;
- }
- return false;
-}
-
-bool InExclude::toSdb(PDB pdb, Database& db)
-{
- TAGID tagid = db.BeginWriteListTag(pdb, TAG_INEXCLUD);
- db.WriteString(pdb, TAG_MODULE, Module, true);
- if (Include)
- SdbWriteNULLTag(pdb, TAG_INCLUDE);
- return !!db.EndWriteListTag(pdb, tagid);
-}
-
-
-template<typename T>
-void ReadGeneric(XMLHandle dbNode, std::list<T>& result, const char* nodeName)
-{
- XMLHandle node = dbNode.FirstChildElement(nodeName);
- while (node.ToNode())
- {
- T object;
- if (object.fromXml(node))
- result.push_back(object);
-
- node = node.NextSiblingElement(nodeName);
- }
-}
-
-template<typename T>
-bool WriteGeneric(PDB pdb, std::list<T>& data, Database& db)
-{
- for (typename std::list<T>::iterator it = data.begin(); it != data.end(); ++it)
- {
- if (!it->toSdb(pdb, db))
- return false;
- }
- return true;
-}
-
-
-/***********************************************************************
- * ShimRef
- */
-
-bool ShimRef::fromXml(XMLHandle dbNode)
-{
- Name = ReadStringNode(dbNode, "NAME");
- CommandLine = ReadStringNode(dbNode, "COMMAND_LINE");
- ReadGeneric(dbNode, InExcludes, "INCLUDE");
- ReadGeneric(dbNode, InExcludes, "EXCLUDE");
- return !Name.empty();
-}
-
-bool ShimRef::toSdb(PDB pdb, Database& db)
-{
- TAGID tagid = db.BeginWriteListTag(pdb, TAG_SHIM_REF);
- db.WriteString(pdb, TAG_NAME, Name, true);
- db.WriteString(pdb, TAG_COMMAND_LINE, CommandLine);
-
- if (!ShimTagid)
- ShimTagid = db.FindShimTagid(Name);
- SdbWriteDWORDTag(pdb, TAG_SHIM_TAGID, ShimTagid);
- return !!db.EndWriteListTag(pdb, tagid);
-}
-
-
-/***********************************************************************
- * Shim
- */
-
-bool Shim::fromXml(XMLHandle dbNode)
-{
- Name = ReadStringNode(dbNode, "NAME");
- DllFile = ReadStringNode(dbNode, "DLLFILE");
- ReadGuidNode(dbNode, "FIX_ID", FixID);
- // GENERAL ?
- // DESCRIPTION_RC_ID
- ReadGeneric(dbNode, InExcludes, "INCLUDE");
- ReadGeneric(dbNode, InExcludes, "EXCLUDE");
- return !Name.empty() && !DllFile.empty();
-}
-
-bool Shim::toSdb(PDB pdb, Database& db)
-{
- Tagid = db.BeginWriteListTag(pdb, TAG_SHIM);
- db.InsertShimTagid(Name, Tagid);
- db.WriteString(pdb, TAG_NAME, Name);
- db.WriteString(pdb, TAG_DLLFILE, DllFile);
- if (IsEmptyGuid(FixID))
- RandomGuid(FixID);
- db.WriteBinary(pdb, TAG_FIX_ID, FixID);
- if (!WriteGeneric(pdb, InExcludes, db))
- return false;
- return !!db.EndWriteListTag(pdb, Tagid);
-}
-
-
-/***********************************************************************
- * Layer
- */
-
-bool Layer::fromXml(XMLHandle dbNode)
-{
- Name = ReadStringNode(dbNode, "NAME");
- ReadGeneric(dbNode, ShimRefs, "SHIM_REF");
- return true;
-}
-
-bool Layer::toSdb(PDB pdb, Database& db)
-{
- Tagid = db.BeginWriteListTag(pdb, TAG_LAYER);
- db.WriteString(pdb, TAG_NAME, Name, true);
- if (!WriteGeneric(pdb, ShimRefs, db))
- return false;
- return !!db.EndWriteListTag(pdb, Tagid);
-}
-
-
-/***********************************************************************
- * MatchingFile
- */
-
-bool MatchingFile::fromXml(XMLHandle dbNode)
-{
- Name = ReadStringNode(dbNode, "NAME");
- Size = ReadDWordNode(dbNode, "SIZE");
- Checksum = ReadDWordNode(dbNode, "CHECKSUM");
- CompanyName = ReadStringNode(dbNode, "COMPANY_NAME");
- InternalName = ReadStringNode(dbNode, "INTERNAL_NAME");
- ProductName = ReadStringNode(dbNode, "PRODUCT_NAME");
- ProductVersion = ReadStringNode(dbNode, "PRODUCT_VERSION");
- FileVersion = ReadStringNode(dbNode, "FILE_VERSION");
- BinFileVersion = ReadStringNode(dbNode, "BIN_FILE_VERSION");
- LinkDate = ReadStringNode(dbNode, "LINK_DATE");
- VerLanguage = ReadStringNode(dbNode, "VER_LANGUAGE");
- FileDescription = ReadStringNode(dbNode, "FILE_DESCRIPTION");
- OriginalFilename = ReadStringNode(dbNode, "ORIGINAL_FILENAME");
- UptoBinFileVersion = ReadStringNode(dbNode, "UPTO_BIN_FILE_VERSION");
- LinkerVersion = ReadStringNode(dbNode, "LINKER_VERSION");
- return true;
-}
-
-bool MatchingFile::toSdb(PDB pdb, Database& db)
-{
- TAGID tagid = db.BeginWriteListTag(pdb, TAG_MATCHING_FILE);
-
- db.WriteString(pdb, TAG_NAME, Name, true);
- db.WriteDWord(pdb, TAG_SIZE, Size);
- db.WriteDWord(pdb, TAG_CHECKSUM, Checksum);
- db.WriteString(pdb, TAG_COMPANY_NAME, CompanyName);
- db.WriteString(pdb, TAG_INTERNAL_NAME, InternalName);
- db.WriteString(pdb, TAG_PRODUCT_NAME, ProductName);
- db.WriteString(pdb, TAG_PRODUCT_VERSION, ProductVersion);
- db.WriteString(pdb, TAG_FILE_VERSION, FileVersion);
- if (!BinFileVersion.empty())
- SHIM_ERR("TAG_BIN_FILE_VERSION Unimplemented\n"); //db.WriteQWord(pdb, TAG_BIN_FILE_VERSION, BinFileVersion);
- if (!LinkDate.empty())
- SHIM_ERR("TAG_LINK_DATE Unimplemented\n"); //db.WriteDWord(pdb, TAG_LINK_DATE, LinkDate);
- if (!VerLanguage.empty())
- SHIM_ERR("TAG_VER_LANGUAGE Unimplemented\n"); //db.WriteDWord(pdb, TAG_VER_LANGUAGE, VerLanguage);
- db.WriteString(pdb, TAG_FILE_DESCRIPTION, FileDescription);
- db.WriteString(pdb, TAG_ORIGINAL_FILENAME, OriginalFilename);
- if (!UptoBinFileVersion.empty())
- SHIM_ERR("TAG_UPTO_BIN_FILE_VERSION Unimplemented\n"); //db.WriteQWord(pdb, TAG_UPTO_BIN_FILE_VERSION, UptoBinFileVersion);
- if (!LinkerVersion.empty())
- SHIM_ERR("TAG_LINKER_VERSION Unimplemented\n"); //db.WriteDWord(pdb, TAG_LINKER_VERSION, LinkerVersion);
-
-
- return !!db.EndWriteListTag(pdb, tagid);
-}
-
-
-/***********************************************************************
- * Exe
- */
-
-bool Exe::fromXml(XMLHandle dbNode)
-{
- Name = ReadStringNode(dbNode, "NAME");
- ReadGuidNode(dbNode, "EXE_ID", ExeID);
- AppName = ReadStringNode(dbNode, "APP_NAME");
- Vendor = ReadStringNode(dbNode, "VENDOR");
-
- ReadGeneric(dbNode, MatchingFiles, "MATCHING_FILE");
-
- ReadGeneric(dbNode, ShimRefs, "SHIM_REF");
-
- return !Name.empty();
-}
-
-bool Exe::toSdb(PDB pdb, Database& db)
-{
- Tagid = db.BeginWriteListTag(pdb, TAG_EXE);
-
- db.WriteString(pdb, TAG_NAME, Name, true);
- if (IsEmptyGuid(ExeID))
- RandomGuid(ExeID);
- db.WriteBinary(pdb, TAG_EXE_ID, ExeID);
-
-
- db.WriteString(pdb, TAG_APP_NAME, AppName);
- db.WriteString(pdb, TAG_VENDOR, Vendor);
-
- if (!WriteGeneric(pdb, MatchingFiles, db))
- return false;
- if (!WriteGeneric(pdb, ShimRefs, db))
- return false;
-
- return !!db.EndWriteListTag(pdb, Tagid);
-}
-
-
-/***********************************************************************
- * Database
- */
-
-void Database::WriteBinary(PDB pdb, TAG tag, const GUID& guid, bool always)
-{
- if (always || !IsEmptyGuid(guid))
- SdbWriteBinaryTag(pdb, tag, (BYTE*)&guid, sizeof(GUID));
-}
-
-void Database::WriteBinary(PDB pdb, TAG tag, const std::vector<BYTE>& data, bool always)
-{
- if (always || !data.empty())
- SdbWriteBinaryTag(pdb, tag, data.data(), data.size());
-}
-
-void Database::WriteString(PDB pdb, TAG tag, const sdbstring& str, bool always)
-{
- if (always || !str.empty())
- SdbWriteStringTag(pdb, tag, (LPCWSTR)str.c_str());
-}
-
-void Database::WriteString(PDB pdb, TAG tag, const std::string& str, bool always)
-{
- WriteString(pdb, tag, sdbstring(str.begin(), str.end()), always);
-}
-
-void Database::WriteDWord(PDB pdb, TAG tag, DWORD value, bool always)
-{
- if (always || value)
- SdbWriteDWORDTag(pdb, tag, value);
-}
-
-TAGID Database::BeginWriteListTag(PDB pdb, TAG tag)
-{
- return SdbBeginWriteListTag(pdb, tag);
-}
-
-BOOL Database::EndWriteListTag(PDB pdb, TAGID tagid)
-{
- return SdbEndWriteListTag(pdb, tagid);
-}
-
-bool Database::fromXml(XMLHandle dbNode)
-{
- Name = ReadStringNode(dbNode, "NAME");
- ReadGuidNode(dbNode, "DATABASE_ID", ID);
-
- XMLHandle libChild = dbNode.FirstChildElement("LIBRARY").FirstChild();
- while (libChild.ToNode())
- {
- std::string NodeName = ToNodeName(libChild);
- if (NodeName == "SHIM")
- {
- Shim shim;
- if (shim.fromXml(libChild))
- Library.Shims.push_back(shim);
- }
- else if (NodeName == "FLAG")
- {
- SHIM_ERR("Unhanled FLAG type\n");
- }
- else if (NodeName == "INCLUDE" || NodeName == "EXCLUDE")
- {
- InExclude inex;
- if (inex.fromXml(libChild))
- Library.InExcludes.push_back(inex);
- }
- libChild = libChild.NextSibling();
- }
-
- ReadGeneric(dbNode, Layers, "LAYER");
- ReadGeneric(dbNode, Exes, "EXE");
- return true;
-}
-
-bool Database::fromXml(const char* fileName)
-{
- tinyxml2::XMLDocument doc;
- tinyxml2::XMLError err = doc.LoadFile(fileName);
- XMLHandle dbHandle = tinyxml2::XMLHandle(&doc).FirstChildElement("SDB").FirstChildElement("DATABASE");
- return fromXml(dbHandle);
-}
-
-bool Database::toSdb(LPCWSTR path)
-{
- PDB pdb = SdbCreateDatabase(path, DOS_PATH);
- TAGID tidDatabase = BeginWriteListTag(pdb, TAG_DATABASE);
- LARGE_INTEGER li = { 0 };
- RtlSecondsSince1970ToTime(time(0), &li);
- SdbWriteQWORDTag(pdb, TAG_TIME, li.QuadPart);
- WriteString(pdb, TAG_COMPILER_VERSION, szCompilerVersion);
- SdbWriteDWORDTag(pdb, TAG_OS_PLATFORM, 1);
- WriteString(pdb, TAG_NAME, Name, true);
- if (IsEmptyGuid(ID))
- {
- SHIM_WARN("DB has empty ID!\n");
- RandomGuid(ID);
- }
- WriteBinary(pdb, TAG_DATABASE_ID, ID);
- TAGID tidLibrary = BeginWriteListTag(pdb, TAG_LIBRARY);
- if (!WriteGeneric(pdb, Library.InExcludes, *this))
- return false;
- if (!WriteGeneric(pdb, Library.Shims, *this))
- return false;
- EndWriteListTag(pdb, tidLibrary);
- if (!WriteGeneric(pdb, Layers, *this))
- return false;
- if (!WriteGeneric(pdb, Exes, *this))
- return false;
- EndWriteListTag(pdb, tidDatabase);
-
- SdbCloseDatabaseWrite(pdb);
- return true;
-}
-
-static void InsertTagid(const sdbstring& name, TAGID tagid, std::map<sdbstring, TAGID>& lookup, const char* type)
-{
- sdbstring nameLower = name;
- std::transform(nameLower.begin(), nameLower.end(), nameLower.begin(), ::tolower);
- if (lookup.find(nameLower) != lookup.end())
- {
- std::string nameA(name.begin(), name.end());
- SHIM_WARN("%s '%s' redefined\n", type, nameA.c_str());
- return;
- }
- lookup[nameLower] = tagid;
-}
-
-static TAGID FindTagid(const sdbstring& name, const std::map<sdbstring, TAGID>& lookup)
-{
- sdbstring nameLower = name;
- std::transform(nameLower.begin(), nameLower.end(), nameLower.begin(), ::tolower);
- std::map<sdbstring, TAGID>::const_iterator it = lookup.find(nameLower);
- if (it == lookup.end())
- return 0;
- return it->second;
-}
-
-void Database::InsertShimTagid(const sdbstring& name, TAGID tagid)
-{
- InsertTagid(name, tagid, KnownShims, "Shim");
-}
-
-TAGID Database::FindShimTagid(const sdbstring& name)
-{
- return FindTagid(name, KnownShims);
-}
-
-void Database::InsertPatchTagid(const sdbstring& name, TAGID tagid)
-{
- InsertTagid(name, tagid, KnownPatches, "Patch");
-}
-
-TAGID Database::FindPatchTagid(const sdbstring& name)
-{
- return FindTagid(name, KnownPatches);
-}
-
-
-
-bool xml_2_db(const char* xml, const WCHAR* sdb)
-{
- Database db;
- if (db.fromXml(xml))
- {
- return db.toSdb((LPCWSTR)sdb);
- }
- return false;
-}