[APPHELP][APPHELP_APITEST] Implement most of sdbread and sdbwrite, mainly the work...
authorMark Jansen <mark.jansen@reactos.org>
Sun, 22 May 2016 20:27:02 +0000 (20:27 +0000)
committerMark Jansen <mark.jansen@reactos.org>
Sun, 22 May 2016 20:27:02 +0000 (20:27 +0000)
svn path=/trunk/; revision=71380

reactos/dll/appcompat/apphelp/CMakeLists.txt
reactos/dll/appcompat/apphelp/apphelp.h
reactos/dll/appcompat/apphelp/apphelp.spec
reactos/dll/appcompat/apphelp/sdbapi.c
reactos/dll/appcompat/apphelp/sdbread.c [new file with mode: 0644]
reactos/dll/appcompat/apphelp/sdbwrite.c [new file with mode: 0644]
rostests/apitests/apphelp/CMakeLists.txt
rostests/apitests/apphelp/db.c [new file with mode: 0644]
rostests/apitests/apphelp/testlist.c

index 6f40d1c..d53668f 100644 (file)
@@ -5,6 +5,8 @@ list(APPEND SOURCE
     apphelp.c
     layer.c
     sdbapi.c
+    sdbread.c
+    sdbwrite.c
     sdbfileattr.c
     apphelp.spec
     apphelp.h
index f9f2500..4d7531d 100644 (file)
@@ -32,6 +32,19 @@ typedef UINT64 QWORD;
 #define TAGREF_NULL (0)
 #define TAGREF_ROOT (0)
 
+typedef struct _DB {
+    HANDLE file;
+    DWORD size;
+    PBYTE data;
+    TAGID stringtable;
+    DWORD write_iter;
+    GUID database_id;
+} DB, *PDB;
+
+/* Flags for SdbInitDatabase */
+#define HID_DOS_PATHS 0x1
+#define HID_DATABASE_FULLPATH 0x2
+#define HID_NO_DATABASE 0x4
 #define HID_DATABASE_TYPE_MASK 0xF00F0000
 #define SDB_DATABASE_MAIN_MSI 0x80020000
 #define SDB_DATABASE_MAIN_SHIM 0x80030000
@@ -47,6 +60,11 @@ typedef struct tagATTRINFO {
   };
 } ATTRINFO, *PATTRINFO;
 
+typedef enum _PATH_TYPE {
+    DOS_PATH,
+    NT_PATH
+} PATH_TYPE;
+
 typedef enum _SHIM_LOG_LEVEL {
     SHIM_ERR = 1,
     SHIM_WARN = 2,
@@ -99,6 +117,18 @@ BOOL WINAPI SdbpOpenMemMappedFile(LPCWSTR path, PMEMMAPPED mapping);
 void WINAPI SdbpCloseMemMappedFile(PMEMMAPPED mapping);
 DWORD SdbpStrlen(LPCWSTR string);
 PWSTR SdbpStrDup(LPCWSTR string);
+BOOL WINAPI SdbpCheckTagType(TAG tag, WORD type);
+BOOL WINAPI SdbpCheckTagIDType(PDB db, TAGID tagid, WORD type);
+PDB WINAPI SdbpCreate(void);
+PDB WINAPI SdbOpenDatabase(LPCWSTR path, PATH_TYPE type);
+void WINAPI SdbCloseDatabase(PDB);
+BOOL WINAPI SdbIsNullGUID(CONST GUID *Guid);
+
+/* sdbread.c */
+BOOL WINAPI SdbpReadData(PDB db, PVOID dest, DWORD offset, DWORD num);
+TAG WINAPI SdbGetTagFromTagID(PDB db, TAGID tagid);
+TAGID WINAPI SdbFindFirstTag(PDB db, TAGID parent, TAG tag);
+BOOL WINAPI SdbGetDatabaseID(PDB db, GUID* Guid);
 
 
 /* layer.c */
index fe00e30..37d8854 100644 (file)
 @ stub SdbAddLayerTagRefToQuery
 @ stub SdbApphelpNotify
 @ stub SdbApphelpNotifyExSdbApphelpNotifyEx
-@ stub SdbBeginWriteListTag
+@ stdcall SdbBeginWriteListTag(ptr long)
 @ stub SdbBuildCompatEnvVariables
 @ stub SdbCloseApphelpInformation
-@ stub SdbCloseDatabase
-@ stub SdbCloseDatabaseWrite
+@ stdcall SdbCloseDatabase(ptr)
+@ stdcall SdbCloseDatabaseWrite(ptr)
 @ stub SdbCloseLocalDatabase
 @ stub SdbCommitIndexes
-@ stub SdbCreateDatabase
+@ stdcall SdbCreateDatabase(wstr long)
 @ stub SdbCreateHelpCenterURL
 @ stub SdbCreateMsiTransformFile
 @ stub SdbDeclareIndex
 @ stub SdbDumpSearchPathPartCaches
 @ stub SdbEnumMsiTransforms
-@ stub SdbEndWriteListTag
+@ stdcall SdbEndWriteListTag(ptr long)
 @ stub SdbEscapeApphelpURL
 @ stub SdbFindFirstDWORDIndexedTag
 @ stub SdbFindFirstMsiPackage
 @ stub SdbFindFirstMsiPackage_Str
 @ stub SdbFindFirstNamedTag
 @ stub SdbFindFirstStringIndexedTag
-@ stub SdbFindFirstTag
+@ stdcall SdbFindFirstTag(ptr long long)
 @ stub SdbFindFirstTagRef
 @ stub SdbFindNextDWORDIndexedTag
 @ stub SdbFindNextMsiPackage
 @ stub SdbFindNextStringIndexedTag
-@ stub SdbFindNextTag
+@ stdcall SdbFindNextTag(ptr long long)
 @ stub SdbFindNextTagRef
 @ stub SdbFreeDatabaseInformation
 @ stdcall SdbFreeFileAttributes(ptr)
@@ -53,8 +53,8 @@
 @ stub SdbFreeFlagInfo
 @ stub SdbGetAppCompatDataSize
 @ stub SdbGetAppPatchDir
-@ stub SdbGetBinaryTagData
-@ stub SdbGetDatabaseID
+@ stdcall SdbGetBinaryTagData(ptr long)
+@ stdcall SdbGetDatabaseID(ptr ptr)
 @ stub SdbGetDatabaseInformation
 @ stub SdbGetDatabaseInformationByName
 @ stub SdbGetDatabaseMatch
@@ -65,7 +65,7 @@
 @ stub SdbGetFileImageType
 @ stub SdbGetFileImageTypeEx
 @ stub SdbGetFileInfo
-@ stub SdbGetFirstChild
+@ stdcall SdbGetFirstChild(ptr long)
 @ stub SdbGetIndex
 @ stub SdbGetItemFromItemRef
 @ stub SdbGetLayerName
 @ stub SdbGetMatchingExe
 @ stub SdbGetMsiPackageInformation
 @ stub SdbGetNamedLayer
-@ stub SdbGetNextChild
+@ stdcall SdbGetNextChild(ptr long long)
 @ stub SdbGetNthUserSdb
 @ stdcall SdbGetPermLayerKeys(wstr wstr ptr long)
 @ stub SdbGetShowDebugInfoOption
 @ stub SdbGetShowDebugInfoOptionValue
 @ stdcall SdbGetStandardDatabaseGUID(long ptr)
-@ stub SdbGetStringTagPtr
-@ stub SdbGetTagDataSize
-@ stub SdbGetTagFromTagID
+@ stdcall SdbGetStringTagPtr(ptr long)
+@ stdcall SdbGetTagDataSize(ptr long)
+@ stdcall SdbGetTagFromTagID(ptr long)
 @ stub SdbGrabMatchingInfo
 @ stub SdbGrabMatchingInfoEx
 @ stdcall SdbGUIDFromString(wstr ptr)
 @ stub SdbOpenApphelpInformation
 @ stub SdbOpenApphelpInformationByID
 @ stub SdbOpenApphelpResourceFile
-@ stub SdbOpenDatabase
+@ stdcall SdbOpenDatabase(wstr long)
 @ stub SdbOpenDbFromGuid
 @ stub SdbOpenLocalDatabase
 @ stub SdbPackAppCompatData
 @ stub SdbQueryReinstallUpgrade
 @ stub SdbReadApphelpData
 @ stub SdbReadApphelpDetailsData
-@ stub SdbReadBinaryTag
+@ stdcall SdbReadBinaryTag(ptr long ptr long)
 @ stub SdbReadBYTETag
-@ stub SdbReadDWORDTag
+@ stdcall SdbReadDWORDTag(ptr long long)
 @ stub SdbReadDWORDTagRef
 @ stub SdbReadEntryInformation
 @ stub SdbReadMsiTransformInfo
 @ stub SdbReadPatchBits
-@ stub SdbReadQWORDTag
+@ stdcall SdbReadQWORDTag(ptr long int64)
 @ stub SdbReadQWORDTagRef
-@ stub SdbReadStringTag
+@ stdcall SdbReadStringTag(ptr long wstr long)
 @ stub SdbReadStringTagRef
-@ stub SdbReadWORDTag
+@ stdcall SdbReadWORDTag(ptr long long)
 @ stub SdbReadWORDTagRef
 @ stub SdbRegisterDatabase
 @ stub SdbReleaseDatabase
 @ stub SdbTagRefToTagID
 @ stdcall SdbTagToString(long)
 @ stub SdbUnregisterDatabase
-@ stub SdbWriteBinaryTag
-@ stub SdbWriteBinaryTagFromFile
+@ stdcall SdbWriteBinaryTag(ptr long ptr long)
+@ stdcall SdbWriteBinaryTagFromFile(ptr long wstr)
 @ stub SdbWriteBYTETag
-@ stub SdbWriteDWORDTag
-@ stub SdbWriteNULLTag
-@ stub SdbWriteQWORDTag
-@ stub SdbWriteStringRefTag
-@ stub SdbWriteStringTag
+@ stdcall SdbWriteDWORDTag(ptr long long)
+@ stdcall SdbWriteNULLTag(ptr long)
+@ stdcall SdbWriteQWORDTag(ptr long int64)
+@ stdcall SdbWriteStringRefTag(ptr long long)
+@ stdcall SdbWriteStringTag(ptr long wstr)
 @ stub SdbWriteStringTagDirect
-@ stub SdbWriteWORDTag
+@ stdcall SdbWriteWORDTag(ptr long long)
 @ stub SE_DllLoaded
 @ stub SE_DllUnloaded
 @ stub SE_GetHookAPIs
index feb5fc1..1994468 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Copyright 2011 André Hentschel
  * Copyright 2013 Mislav Blažević
- * Copyright 2015 Mark Jansen
+ * Copyright 2015,2016 Mark Jansen
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -202,6 +202,12 @@ void SdbpFree(LPVOID mem
     HeapFree(SdbpHeap(), 0, mem);
 }
 
+PDB WINAPI SdbpCreate(void)
+{
+    PDB db = (PDB)SdbAlloc(sizeof(DB));
+    /* SdbAlloc zeroes the memory. */
+    return db;
+}
 DWORD SdbpStrlen(PCWSTR string)
 {
     return (lstrlenW(string) + 1) * sizeof(WCHAR);
@@ -292,6 +298,119 @@ void WINAPI SdbpCloseMemMappedFile(PMEMMAPPED mapping)
     RtlZeroMemory(mapping, sizeof(*mapping));
 }
 
+BOOL WINAPI SdbpCheckTagType(TAG tag, WORD type)
+{
+    if ((tag & TAG_TYPE_MASK) != type)
+        return FALSE;
+    return TRUE;
+}
+
+BOOL WINAPI SdbpCheckTagIDType(PDB db, TAGID tagid, WORD type)
+{
+    TAG tag = SdbGetTagFromTagID(db, tagid);
+    if (tag == TAG_NULL)
+        return FALSE;
+    return SdbpCheckTagType(tag, type);
+}
+
+/**
+ * Opens specified shim database file.
+ *
+ * @param [in]  path    Path to the shim database.
+ * @param [in]  type    Type of path. Either DOS_PATH or NT_PATH.
+ *
+ * @return  Success: Handle to the shim database, NULL otherwise.
+ */
+PDB WINAPI SdbOpenDatabase(LPCWSTR path, PATH_TYPE type)
+{
+    NTSTATUS Status;
+    IO_STATUS_BLOCK io;
+    OBJECT_ATTRIBUTES attr;
+    UNICODE_STRING str;
+    PDB db;
+    BYTE header[12];
+    DWORD dwRead = 0;
+
+    if (type == DOS_PATH)
+    {
+        if (!RtlDosPathNameToNtPathName_U(path, &str, NULL, NULL))
+            return NULL;
+    }
+    else
+        RtlInitUnicodeString(&str, path);
+
+    db = SdbpCreate();
+    if (!db)
+    {
+        SHIM_ERR("Failed to allocate memory for shim database\n");
+        return NULL;
+    }
+
+    InitializeObjectAttributes(&attr, &str, OBJ_CASE_INSENSITIVE, NULL, NULL);
+
+    Status = NtCreateFile(&db->file, FILE_GENERIC_READ | SYNCHRONIZE,
+                          &attr, &io, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ,
+                          FILE_OPEN, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
+
+    if (type == DOS_PATH)
+        RtlFreeUnicodeString(&str);
+
+    if (!NT_SUCCESS(Status))
+    {
+        SdbCloseDatabase(db);
+        SHIM_ERR("Failed to open shim database file: 0x%lx\n", Status);
+        return NULL;
+    }
+
+    db->size = GetFileSize(db->file, NULL);
+    db->data = SdbAlloc(db->size);
+    ReadFile(db->file, db->data, db->size, &dwRead, NULL);
+
+    if (!SdbpReadData(db, &header, 0, 12))
+    {
+        SdbCloseDatabase(db);
+        SHIM_ERR("Failed to read shim database header\n");
+        return NULL;
+    }
+
+    if (memcmp(&header[8], "sdbf", 4) != 0)
+    {
+        SdbCloseDatabase(db);
+        SHIM_ERR("Shim database header is invalid\n");
+        return NULL;
+    }
+
+    if (*(DWORD*)&header[0] != (DWORD)2)
+    {
+        SdbCloseDatabase(db);
+        SHIM_ERR("Invalid shim database version\n");
+        return NULL;
+    }
+
+    db->stringtable = SdbFindFirstTag(db, TAGID_ROOT, TAG_STRINGTABLE);
+    if(!SdbGetDatabaseID(db, &db->database_id))
+    {
+        SHIM_INFO("Failed to get the database id\n");
+    }
+    return db;
+}
+
+/**
+ * Closes specified database and frees its memory.
+ *
+ * @param [in]  db  Handle to the shim database.
+ */
+void WINAPI SdbCloseDatabase(PDB db)
+{
+    if (!db)
+        return;
+
+    if (db->file)
+        NtClose(db->file);
+    SdbFree(db->data);
+    SdbFree(db);
+}
+
 /**
  * Parses a string to retrieve a GUID.
  *
@@ -380,6 +499,8 @@ BOOL WINAPI SdbGetStandardDatabaseGUID(DWORD Flags, GUID* Guid)
  * @param [in]  tag The tag which will be converted to a string.
  *
  * @return  Success: Pointer to the string matching specified tag, or L"InvalidTag" on failure.
+ *
+ * @todo: Convert this into a lookup table, this is wasting alot of space.
  */
 LPCWSTR WINAPI SdbTagToString(TAG tag)
 {
diff --git a/reactos/dll/appcompat/apphelp/sdbread.c b/reactos/dll/appcompat/apphelp/sdbread.c
new file mode 100644 (file)
index 0000000..707b5e1
--- /dev/null
@@ -0,0 +1,439 @@
+/*
+ * Copyright 2011 André Hentschel
+ * Copyright 2013 Mislav Bla\9eevic
+ * Copyright 2015,2016 Mark Jansen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "windef.h"
+#include "winbase.h"
+#include "apphelp.h"
+
+#include "wine/unicode.h"
+
+
+DWORD WINAPI SdbGetTagDataSize(PDB db, TAGID tagid);
+
+
+BOOL WINAPI SdbpReadData(PDB db, PVOID dest, DWORD offset, DWORD num)
+{
+    DWORD size = offset + num;
+
+    /* Either overflow or no data to read */
+    if (size <= offset)
+        return FALSE;
+
+    /* Overflow */
+    if (db->size < size)
+        return FALSE;
+
+    memcpy(dest, db->data + offset, num);
+    return TRUE;
+}
+
+static DWORD WINAPI SdbpGetTagSize(PDB db, TAGID tagid)
+{
+    WORD type;
+    DWORD size;
+
+    type = SdbGetTagFromTagID(db, tagid) & TAG_TYPE_MASK;
+    if (type == TAG_NULL)
+        return 0;
+
+    size = SdbGetTagDataSize(db, tagid);
+    if (type <= TAG_TYPE_STRINGREF)
+        return size += sizeof(TAG);
+    else size += (sizeof(TAG) + sizeof(DWORD));
+
+    return size;
+}
+
+static LPWSTR WINAPI SdbpGetString(PDB db, TAGID tagid, PDWORD size)
+{
+    TAG tag;
+    TAGID offset;
+
+    tag = SdbGetTagFromTagID(db, tagid);
+    if (tag == TAG_NULL)
+        return NULL;
+
+    if ((tag & TAG_TYPE_MASK) == TAG_TYPE_STRINGREF)
+    {
+        /* No stringtable; all references are invalid */
+        if (db->stringtable == TAGID_NULL)
+            return NULL;
+
+        /* TAG_TYPE_STRINGREF contains offset of string relative to stringtable */
+        if (!SdbpReadData(db, &tagid, tagid + sizeof(TAG), sizeof(TAGID)))
+            return NULL;
+
+        offset = db->stringtable + tagid + sizeof(TAG) + sizeof(TAGID);
+    }
+    else if ((tag & TAG_TYPE_MASK) == TAG_TYPE_STRING)
+    {
+        offset = tagid + sizeof(TAG) + sizeof(TAGID);
+    }
+    else
+    {
+        SHIM_ERR("Tag 0x%u at tagid %u is neither a string or reference to string\n", tag, tagid);
+        return NULL;
+    }
+
+    /* Optionally read string size */
+    if (size && !SdbpReadData(db, size, tagid + sizeof(TAG), sizeof(*size)))
+        return FALSE;
+
+    return (LPWSTR)(&db->data[offset]);
+}
+
+/**
+ * Searches shim database for the tag associated with specified tagid.
+ *
+ * @param [in]  db      Handle to the shim database.
+ * @param [in]  tagid   The TAGID of the tag.
+ *
+ * @return  Success: The tag associated with specified tagid, Failure: TAG_NULL.
+ */
+TAG WINAPI SdbGetTagFromTagID(PDB db, TAGID tagid)
+{
+    TAG data;
+    if (!SdbpReadData(db, &data, tagid, sizeof(data)))
+        return TAG_NULL;
+    return data;
+}
+
+/**
+ * Retrieves size of data at specified tagid.
+ *
+ * @param [in]  db      Handle to the shim database.
+ * @param [in]  tagid   Tagid of tag whose size is queried.
+ *
+ * @return  Success: Size of data at specified tagid, Failure: 0.
+ */
+DWORD WINAPI SdbGetTagDataSize(PDB db, TAGID tagid)
+{
+    /* sizes of data types with fixed size */
+    static const SIZE_T sizes[6] = {
+        0, /* NULL  */ 1, /* BYTE      */
+        2, /* WORD  */ 4, /* DWORD     */
+        8, /* QWORD */ 4  /* STRINGREF */
+    };
+    WORD type;
+    DWORD size;
+
+    type = SdbGetTagFromTagID(db, tagid) & TAG_TYPE_MASK;
+    if (type == TAG_NULL)
+        return 0;
+
+    if (type <= TAG_TYPE_STRINGREF)
+        return sizes[(type >> 12) - 1];
+
+    /* tag with dynamic size (e.g. list): must read size */
+    if (!SdbpReadData(db, &size, tagid + sizeof(TAG), sizeof(size)))
+        return 0;
+
+    return size;
+}
+
+/**
+ * Searches shim database for a child of specified parent tag.
+ *
+ * @param [in]  db      Handle to the shim database.
+ * @param [in]  parent  TAGID of parent.
+ *
+ * @return  Success: TAGID of child tag, Failure: TAGID_NULL.
+ */
+TAGID WINAPI SdbGetFirstChild(PDB db, TAGID parent)
+{
+    /* if we are at beginning of database */
+    if (parent == TAGID_ROOT)
+    {
+        /* header only database: no tags */
+        if (db->size <= _TAGID_ROOT)
+            return TAGID_NULL;
+        /* return *real* root tagid */
+        else return _TAGID_ROOT;
+    }
+
+    /* only list tag can have children */
+    if ((SdbGetTagFromTagID(db, parent) & TAG_TYPE_MASK) != TAG_TYPE_LIST)
+        return TAGID_NULL;
+
+    /* first child is sizeof(TAG) + sizeof(DWORD) bytes after beginning of list */
+    return parent + sizeof(TAG) + sizeof(DWORD);
+}
+
+/**
+ * Searches shim database for next child of specified parent tag.
+ *
+ * @param [in]  db          Handle to the shim database.
+ * @param [in]  parent      TAGID of parent.
+ * @param [in]  prev_child  TAGID of previous child.
+ *
+ * @return  Success: TAGID of next child tag, Failure: TAGID_NULL.
+ */
+TAGID WINAPI SdbGetNextChild(PDB db, TAGID parent, TAGID prev_child)
+{
+    TAGID next_child;
+    DWORD prev_child_size, parent_size;
+
+    prev_child_size = SdbpGetTagSize(db, prev_child);
+    if (prev_child_size == 0)
+        return TAGID_NULL;
+
+    /* Bound check */
+    next_child = prev_child + prev_child_size;
+    if (next_child >= db->size)
+        return TAGID_NULL;
+
+    if (parent == TAGID_ROOT)
+        return next_child;
+
+    parent_size = SdbpGetTagSize(db, parent);
+    if (parent_size == 0)
+        return TAGID_NULL;
+
+    /* Specified parent has no more children */
+    if (next_child >= parent + parent_size)
+        return TAGID_NULL;
+
+    return next_child;
+}
+
+/**
+ * Searches shim database for a tag within specified domain.
+ *
+ * @param [in]  db      Handle to the shim database.
+ * @param [in]  parent  TAGID of parent.
+ * @param [in]  tag     TAG to be located.
+ *
+ * @return  Success: TAGID of first matching tag, Failure: TAGID_NULL.
+ */
+TAGID WINAPI SdbFindFirstTag(PDB db, TAGID parent, TAG tag)
+{
+    TAGID iter;
+
+    iter = SdbGetFirstChild(db, parent);
+    while (iter != TAGID_NULL)
+    {
+        if (SdbGetTagFromTagID(db, iter) == tag)
+            return iter;
+        iter = SdbGetNextChild(db, parent, iter);
+    }
+    return TAGID_NULL;
+}
+
+/**
+ * Searches shim database for a next tag which matches prev_child within parent's domain.
+ *
+ * @param [in]  db          Handle to the shim database.
+ * @param [in]  parent      TAGID of parent.
+ * @param [in]  prev_child  TAGID of previous match.
+ *
+ * @return  Success: TAGID of next match, Failure: TAGID_NULL.
+ */
+TAGID WINAPI SdbFindNextTag(PDB db, TAGID parent, TAGID prev_child)
+{
+    TAG tag;
+    TAGID iter;
+
+    tag = SdbGetTagFromTagID(db, prev_child);
+    iter = SdbGetNextChild(db, parent, prev_child);
+
+    while (iter != TAGID_NULL)
+    {
+        if (SdbGetTagFromTagID(db, iter) == tag)
+            return iter;
+        iter = SdbGetNextChild(db, parent, iter);
+    }
+    return TAGID_NULL;
+}
+
+/**
+ * Searches shim database for string associated with specified tagid and copies string into a
+ * buffer.
+ * 
+ * If size parameter is less than number of characters in string, this function shall fail and
+ * no data shall be copied.
+ *
+ * @param [in]  db      Handle to the shim database.
+ * @param [in]  tagid   TAGID of string or stringref associated with the string.
+ * @param [out] buffer  Buffer in which string will be copied.
+ * @param [in]  size    Number of characters to copy.
+ *
+ * @return  TRUE if string was successfully copied to the buffer FALSE if string was not copied
+ *          to the buffer.
+ */
+BOOL WINAPI SdbReadStringTag(PDB db, TAGID tagid, LPWSTR buffer, DWORD size)
+{
+    LPWSTR string;
+    DWORD string_size;
+
+    string = SdbpGetString(db, tagid, &string_size);
+    if (!string)
+        return FALSE;
+
+    /* Check if buffer is too small */
+    if (size * sizeof(WCHAR) < string_size)
+        return FALSE;
+
+    memcpy(buffer, string, string_size);
+    return TRUE;
+}
+
+/**
+ * Reads WORD value at specified tagid.
+ *
+ * @param [in]  db      Handle to the shim database.
+ * @param [in]  tagid   TAGID of WORD value.
+ * @param [in]  ret     Default return value in case function fails.
+ *
+ * @return  Success: WORD value at specified tagid, or ret on failure.
+ */
+WORD WINAPI SdbReadWORDTag(PDB db, TAGID tagid, WORD ret)
+{
+    if (SdbpCheckTagIDType(db, tagid, TAG_TYPE_WORD))
+        SdbpReadData(db, &ret, tagid + 2, sizeof(WORD));
+    return ret;
+}
+
+/**
+ * Reads DWORD value at specified tagid.
+ *
+ * @param [in]  db      Handle to the shim database.
+ * @param [in]  tagid   TAGID of DWORD value.
+ * @param [in]  ret     Default return value in case function fails.
+ *
+ * @return  Success: DWORD value at specified tagid, otherwise ret.
+ */
+DWORD WINAPI SdbReadDWORDTag(PDB db, TAGID tagid, DWORD ret)
+{
+    if (SdbpCheckTagIDType(db, tagid, TAG_TYPE_DWORD))
+        SdbpReadData(db, &ret, tagid + 2, sizeof(DWORD));
+    return ret;
+}
+
+/**
+ * Reads QWORD value at specified tagid.
+ *
+ * @param [in]  db      Handle to the shim database.
+ * @param [in]  tagid   TAGID of QWORD value.
+ * @param [in]  ret     Default return value in case function fails.
+ *
+ * @return  Success: QWORD value at specified tagid, otherwise ret.
+ */
+QWORD WINAPI SdbReadQWORDTag(PDB db, TAGID tagid, QWORD ret)
+{
+    if (SdbpCheckTagIDType(db, tagid, TAG_TYPE_QWORD))
+        SdbpReadData(db, &ret, tagid + sizeof(TAG), sizeof(QWORD));
+    return ret;
+}
+
+/**
+ * Reads binary data at specified tagid.
+ *
+ * @param [in]  db      Handle to the shim database.
+ * @param [in]  tagid   TAGID of binary data.
+ * @param [out] buffer  Buffer in which data will be copied.
+ * @param [in]  size    Size of the buffer.
+ *
+ * @return  TRUE if data was successfully written, or FALSE otherwise.
+ */
+BOOL WINAPI SdbReadBinaryTag(PDB db, TAGID tagid, PBYTE buffer, DWORD size)
+{
+    DWORD data_size = 0;
+
+    if (SdbpCheckTagIDType(db, tagid, TAG_TYPE_BINARY))
+    {
+        SdbpReadData(db, &data_size, tagid + sizeof(TAG), sizeof(data_size));
+        if (size >= data_size)
+            return SdbpReadData(db, buffer, tagid + sizeof(TAG) + sizeof(data_size), data_size);
+    }
+
+    return FALSE;
+}
+
+/**
+ * Retrieves binary data at specified tagid.
+ *
+ * @param [in]  db      Handle to the shim database.
+ * @param [in]  tagid   TAGID of binary data.
+ *
+ * @return  Success: Pointer to binary data at specified tagid, or NULL on failure.
+ */
+PVOID WINAPI SdbGetBinaryTagData(PDB db, TAGID tagid)
+{
+    if (!SdbpCheckTagIDType(db, tagid, TAG_TYPE_BINARY))
+        return NULL;
+    return &db->data[tagid + sizeof(TAG) + sizeof(DWORD)];
+}
+
+/**
+ * Searches shim database for string associated with specified tagid.
+ *
+ * @param [in]  db      Handle to the shim database.
+ * @param [in]  tagid   TAGID of string or stringref associated with the string.
+ *
+ * @return  the LPWSTR associated with specified tagid, or NULL on failure.
+ */
+LPWSTR WINAPI SdbGetStringTagPtr(PDB db, TAGID tagid)
+{
+    return SdbpGetString(db, tagid, NULL);
+}
+
+/**
+ * Reads binary data at specified tagid.
+ *
+ * @param [in]  db      Handle to the shim database.
+ * @param [out] Guid    Database ID.
+ *
+ * @return  true if the ID was found FALSE otherwise.
+ */
+BOOL WINAPI SdbGetDatabaseID(PDB db, GUID* Guid)
+{
+    if(SdbIsNullGUID(&db->database_id))
+    {
+        TAGID root = SdbFindFirstTag(db, TAGID_ROOT, TAG_DATABASE);
+        if(root != TAGID_NULL)
+        {
+            TAGID id = SdbFindFirstTag(db, root, TAG_DATABASE_ID);
+            if(id != TAGID_NULL)
+            {
+                if(!SdbReadBinaryTag(db, id, (PBYTE)&db->database_id, sizeof(db->database_id)))
+                {
+                    memset(&db->database_id, 0, sizeof(db->database_id));
+                }
+            }
+            else
+            {
+                /* Should we silence this if we are opening a system db? */
+                SHIM_ERR("Failed to get the database id\n");
+            }
+        }
+        else
+        {
+            /* Should we silence this if we are opening a system db? */
+            SHIM_ERR("Failed to get root tag\n");
+        }
+    }
+    if(!SdbIsNullGUID(&db->database_id))
+    {
+        memcpy(Guid, &db->database_id, sizeof(db->database_id));
+        return TRUE;
+    }
+    return FALSE;
+}
+
diff --git a/reactos/dll/appcompat/apphelp/sdbwrite.c b/reactos/dll/appcompat/apphelp/sdbwrite.c
new file mode 100644 (file)
index 0000000..903d8cf
--- /dev/null
@@ -0,0 +1,328 @@
+/*
+ * Copyright 2011 André Hentschel
+ * Copyright 2013 Mislav Bla\9eevic
+ * Copyright 2015,2016 Mark Jansen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#define WIN32_NO_STATUS
+#include "windows.h"
+#include "ntndk.h"
+#include "apphelp.h"
+
+#include "wine/unicode.h"
+
+
+static void WINAPI SdbpFlush(PDB db)
+{
+    IO_STATUS_BLOCK io;
+    NTSTATUS Status = NtWriteFile(db->file, NULL, NULL, NULL, &io,
+        db->data, db->write_iter, NULL, NULL);
+    if( !NT_SUCCESS(Status))
+        SHIM_WARN("failed with 0x%lx\n", Status);
+}
+
+static void WINAPI SdbpWrite(PDB db, LPCVOID data, DWORD size)
+{
+    if (db->write_iter + size > db->size)
+    {
+        /* Round to powers of two to prevent too many reallocations */
+        while (db->size < db->write_iter + size) db->size <<= 1;
+        db->data = SdbReAlloc(db->data, db->size);
+    }
+
+    memcpy(db->data + db->write_iter, data, size);
+    db->write_iter += size;
+}
+
+/**
+ * Creates new shim database file
+ * 
+ * If a file already exists on specified path, that file shall be overwritten.
+ * 
+ * @note Use SdbCloseDatabasWrite to close the database opened with this function.
+ *
+ * @param [in]  path    Path to the new shim database.
+ * @param [in]  type    Type of path. Either DOS_PATH or NT_PATH.
+ *
+ * @return  Success: Handle to the newly created shim database, NULL otherwise.
+ */
+PDB WINAPI SdbCreateDatabase(LPCWSTR path, PATH_TYPE type)
+{
+    static const DWORD version_major = 2, version_minor = 1;
+    static const char* magic = "sdbf";
+    NTSTATUS Status;
+    IO_STATUS_BLOCK io;
+    OBJECT_ATTRIBUTES attr;
+    UNICODE_STRING str;
+    PDB db;
+
+    if (type == DOS_PATH)
+    {
+        if (!RtlDosPathNameToNtPathName_U(path, &str, NULL, NULL))
+            return NULL;
+    }
+    else
+        RtlInitUnicodeString(&str, path);
+
+    db = SdbpCreate();
+    if (!db)
+    {
+        SHIM_ERR("Failed to allocate memory for shim database\n");
+        return NULL;
+    }
+
+    InitializeObjectAttributes(&attr, &str, OBJ_CASE_INSENSITIVE, NULL, NULL);
+
+    Status = NtCreateFile(&db->file, FILE_GENERIC_WRITE | SYNCHRONIZE,
+                          &attr, &io, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ,
+                          FILE_SUPERSEDE, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
+
+    if (type == DOS_PATH)
+        RtlFreeUnicodeString(&str);
+
+    if (!NT_SUCCESS(Status))
+    {
+        SdbCloseDatabase(db);
+        SHIM_ERR("Failed to create shim database file: %lx\n", Status);
+        return NULL;
+    }
+
+    db->size = sizeof(DWORD) + sizeof(DWORD) + strlen(magic);
+    db->data = SdbAlloc(db->size);
+
+    SdbpWrite(db, &version_major, sizeof(DWORD));
+    SdbpWrite(db, &version_minor, sizeof(DWORD));
+    SdbpWrite(db, magic, strlen(magic));
+
+    return db;
+}
+
+/**
+ * Closes specified database and writes data to file.
+ *
+ * @param [in]  db  Handle to the shim database.
+ */
+void WINAPI SdbCloseDatabaseWrite(PDB db)
+{
+    SdbpFlush(db);
+    SdbCloseDatabase(db);
+}
+
+/**
+ * Writes a tag-only (NULL) entry to the specified shim database.
+ *
+ * @param [in]  db  Handle to the shim database.
+ * @param [in]  tag A tag for the entry.
+ *
+ * @return  TRUE if it succeeds, FALSE if it fails.
+ */
+BOOL WINAPI SdbWriteNULLTag(PDB db, TAG tag)
+{
+    if (!SdbpCheckTagType(tag, TAG_TYPE_NULL))
+        return FALSE;
+
+    SdbpWrite(db, &tag, sizeof(TAG));
+    return TRUE;
+}
+
+/**
+ * Writes a WORD entry to the specified shim database.
+ *
+ * @param [in]  db      Handle to the shim database.
+ * @param [in]  tag     A tag for the entry.
+ * @param [in]  data    WORD entry which will be written to the database.
+ *
+ * @return  TRUE if it succeeds, FALSE if it fails.
+ */
+BOOL WINAPI SdbWriteWORDTag(PDB db, TAG tag, WORD data)
+{
+    if (!SdbpCheckTagType(tag, TAG_TYPE_WORD))
+        return FALSE;
+
+    SdbpWrite(db, &tag, sizeof(TAG));
+    SdbpWrite(db, &data, sizeof(data));
+    return TRUE;
+}
+
+/**
+ * Writes a DWORD entry to the specified shim database.
+ *
+ * @param [in]  db      Handle to the shim database.
+ * @param [in]  tag     A tag for the entry.
+ * @param [in]  data    DWORD entry which will be written to the database.
+ *
+ * @return  TRUE if it succeeds, FALSE if it fails.
+ */
+BOOL WINAPI SdbWriteDWORDTag(PDB db, TAG tag, DWORD data)
+{
+    if (!SdbpCheckTagType(tag, TAG_TYPE_DWORD))
+        return FALSE;
+
+    SdbpWrite(db, &tag, sizeof(TAG));
+    SdbpWrite(db, &data, sizeof(data));
+    return TRUE;
+}
+
+/**
+ * Writes a DWORD entry to the specified shim database.
+ *
+ * @param [in]  db      Handle to the shim database.
+ * @param [in]  tag     A tag for the entry.
+ * @param [in]  data    QWORD entry which will be written to the database.
+ *
+ * @return  TRUE if it succeeds, FALSE if it fails.
+ */
+BOOL WINAPI SdbWriteQWORDTag(PDB db, TAG tag, QWORD data)
+{
+    if (!SdbpCheckTagType(tag, TAG_TYPE_QWORD))
+        return FALSE;
+
+    SdbpWrite(db, &tag, sizeof(TAG));
+    SdbpWrite(db, &data, sizeof(data));
+    return TRUE;
+}
+
+/**
+ * Writes a wide string entry to the specified shim database.
+ *
+ * @param [in]  db      Handle to the shim database.
+ * @param [in]  tag     A tag for the entry.
+ * @param [in]  string  Wide string entry which will be written to the database.
+ *
+ * @return  TRUE if it succeeds, FALSE if it fails.
+ */
+BOOL WINAPI SdbWriteStringTag(PDB db, TAG tag, LPCWSTR string)
+{
+    DWORD size;
+
+    if (!SdbpCheckTagType(tag, TAG_TYPE_STRING))
+        return FALSE;
+
+    size = SdbpStrlen(string);
+    SdbpWrite(db, &tag, sizeof(TAG));
+    SdbpWrite(db, &size, sizeof(size));
+    SdbpWrite(db, string, size);
+    return TRUE;
+}
+
+/**
+ * Writes a stringref tag to specified database
+ * @note Reference (tagid) is not checked for validity.
+ *
+ * @param [in]  db      Handle to the shim database.
+ * @param [in]  tag     TAG which will be written.
+ * @param [in]  tagid   TAGID of the string tag refers to.
+ *
+ * @return  TRUE if it succeeds, FALSE if it fails.
+ */
+BOOL WINAPI SdbWriteStringRefTag(PDB db, TAG tag, TAGID tagid)
+{
+    if (!SdbpCheckTagType(tag, TAG_TYPE_STRINGREF))
+        return FALSE;
+
+    SdbpWrite(db, &tag, sizeof(TAG));
+    SdbpWrite(db, &tagid, sizeof(tagid));
+    return TRUE;
+}
+
+/**
+ * Writes data the specified shim database.
+ *
+ * @param [in]  db      Handle to the shim database.
+ * @param [in]  tag     A tag for the entry.
+ * @param [in]  data    Pointer to data.
+ * @param [in]  size    Number of bytes to write.
+ *
+ * @return  TRUE if it succeeds, FALSE if it fails.
+ */
+BOOL WINAPI SdbWriteBinaryTag(PDB db, TAG tag, PBYTE data, DWORD size)
+{
+    if (!SdbpCheckTagType(tag, TAG_TYPE_BINARY))
+        return FALSE;
+
+    SdbpWrite(db, &tag, sizeof(TAG));
+    SdbpWrite(db, &size, sizeof(size));
+    SdbpWrite(db, data, size);
+    return TRUE;
+}
+
+/**
+ * Writes data from a file to the specified shim database.
+ *
+ * @param [in]  db      Handle to the shim database.
+ * @param [in]  tag     A tag for the entry.
+ * @param [in]  path    Path of the input file.
+ *
+ * @return  TRUE if it succeeds, FALSE if it fails.
+ */
+BOOL WINAPI SdbWriteBinaryTagFromFile(PDB db, TAG tag, LPCWSTR path)
+{
+    MEMMAPPED mapped;
+
+    if (!SdbpCheckTagType(tag, TAG_TYPE_BINARY))
+        return FALSE;
+
+    if (!SdbpOpenMemMappedFile(path, &mapped))
+        return FALSE;
+
+    SdbWriteBinaryTag(db, tag, mapped.view, mapped.size);
+    SdbpCloseMemMappedFile(&mapped);
+    return TRUE;
+}
+
+/**
+ * Writes a list tag to specified database All subsequent SdbWrite* functions shall write to
+ * newly created list untill TAGID of that list is passed to SdbEndWriteListTag.
+ *
+ * @param [in]  db  Handle to the shim database.
+ * @param [in]  tag TAG for the list
+ *                  
+ *                  RETURNS Success: TAGID of the newly created list, or TAGID_NULL on failure.
+ *
+ * @return  A TAGID.
+ */
+TAGID WINAPI SdbBeginWriteListTag(PDB db, TAG tag)
+{
+    TAGID list_id;
+
+    if (!SdbpCheckTagType(tag, TAG_TYPE_LIST))
+        return TAGID_NULL;
+
+    list_id = db->write_iter;
+    SdbpWrite(db, &tag, sizeof(TAG));
+    db->write_iter += sizeof(DWORD); /* reserve some memory for storing list size */
+    return list_id;
+}
+
+/**
+ * Marks end of the specified list.
+ *
+ * @param [in]  db      Handle to the shim database.
+ * @param [in]  tagid   TAGID of the list.
+ *
+ * @return  TRUE if it succeeds, FALSE if it fails.
+ */
+BOOL WINAPI SdbEndWriteListTag(PDB db, TAGID tagid)
+{
+    if (!SdbpCheckTagIDType(db, tagid, TAG_TYPE_LIST))
+        return FALSE;
+
+    /* Write size of list to list tag header */
+    *(DWORD*)&db->data[tagid + sizeof(TAG)] = db->write_iter - tagid - sizeof(TAG);
+    return TRUE;
+}
+
index cd1376d..c37c942 100644 (file)
@@ -4,6 +4,7 @@ add_definitions(-D__ROS_LONG64__)
 list(APPEND SOURCE
     apphelp.c
     data.c
+    db.c
     layerapi.c
     testlist.c)
 
diff --git a/rostests/apitests/apphelp/db.c b/rostests/apitests/apphelp/db.c
new file mode 100644 (file)
index 0000000..4dc635a
--- /dev/null
@@ -0,0 +1,283 @@
+/*
+ * Copyright 2012 Detlef Riekenberg
+ * Copyright 2013 Mislav Blažević
+ * Copyright 2015,2016 Mark Jansen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+
+#include <ntstatus.h>
+#define WIN32_NO_STATUS
+#include <windows.h>
+#include <shlwapi.h>
+#include <winnt.h>
+#ifdef __REACTOS__
+#include <ntndk.h>
+#else
+#include <winternl.h>
+#endif
+
+#include <winerror.h>
+#include <stdio.h>
+#include <initguid.h>
+
+#include "wine/test.h"
+
+typedef WORD TAG;
+typedef DWORD TAGID;
+typedef DWORD TAGREF;
+typedef UINT64 QWORD;
+typedef VOID* PDB;
+typedef INT PATH_TYPE;
+#define DOS_PATH 0
+
+#define TAGID_NULL 0x0
+#define TAGID_ROOT 0x0
+#define _TAGID_ROOT 12
+
+
+#define TAG_TYPE_MASK 0xF000
+
+#define TAG_TYPE_NULL 0x1000
+#define TAG_TYPE_BYTE 0x2000
+#define TAG_TYPE_WORD 0x3000
+#define TAG_TYPE_DWORD 0x4000
+#define TAG_TYPE_QWORD 0x5000
+#define TAG_TYPE_STRINGREF 0x6000
+#define TAG_TYPE_LIST 0x7000
+#define TAG_TYPE_STRING 0x8000
+#define TAG_TYPE_BINARY 0x9000
+#define TAG_NULL 0x0
+#define TAG_SIZE (0x1 | TAG_TYPE_DWORD)
+
+#define TAG_MATCH_MODE (0x1 | TAG_TYPE_WORD)
+
+#define TAG_FLAG_LUA (0x10 | TAG_TYPE_QWORD)
+
+#define TAG_STRINGTABLE (0x801 | TAG_TYPE_LIST)
+
+#define TAG_NAME (0x1 | TAG_TYPE_STRINGREF)
+#define TAG_STRINGTABLE_ITEM (0x801 | TAG_TYPE_STRING)
+
+
+#define TAG_GENERAL (0x2 | TAG_TYPE_NULL)
+
+#define TAG_DATA_BITS (0x5 | TAG_TYPE_BINARY)
+
+
+
+static HMODULE hdll;
+static LPCWSTR (WINAPI *pSdbTagToString)(TAG);
+static PDB (WINAPI *pSdbOpenDatabase)(LPCWSTR, PATH_TYPE);
+static PDB (WINAPI *pSdbCreateDatabase)(LPCWSTR, PATH_TYPE);
+static void (WINAPI *pSdbCloseDatabase)(PDB);
+static void (WINAPI *pSdbCloseDatabaseWrite)(PDB);
+static TAG (WINAPI *pSdbGetTagFromTagID)(PDB, TAGID);
+static BOOL (WINAPI *pSdbWriteNULLTag)(PDB, TAG);
+static BOOL (WINAPI *pSdbWriteWORDTag)(PDB, TAG, WORD);
+static BOOL (WINAPI *pSdbWriteDWORDTag)(PDB, TAG, DWORD);
+static BOOL (WINAPI *pSdbWriteQWORDTag)(PDB, TAG, QWORD);
+static BOOL (WINAPI *pSdbWriteBinaryTagFromFile)(PDB, TAG, LPCWSTR);
+static BOOL (WINAPI *pSdbWriteStringTag)(PDB, TAG, LPCWSTR);
+static BOOL (WINAPI *pSdbWriteStringRefTag)(PDB, TAG, TAGID);
+static TAGID (WINAPI *pSdbBeginWriteListTag)(PDB, TAG);
+static BOOL (WINAPI *pSdbEndWriteListTag)(PDB, TAGID);
+static WORD (WINAPI *pSdbReadWORDTag)(PDB, TAGID, WORD);
+static DWORD (WINAPI *pSdbReadDWORDTag)(PDB, TAGID, DWORD);
+static QWORD (WINAPI *pSdbReadQWORDTag)(PDB, TAGID, QWORD);
+static BOOL (WINAPI *pSdbReadBinaryTag)(PDB, TAGID, PBYTE, DWORD);
+static BOOL (WINAPI *pSdbReadStringTag)(PDB, TAGID, LPWSTR, DWORD);
+static DWORD (WINAPI *pSdbGetTagDataSize)(PDB, TAGID);
+static PVOID (WINAPI *pSdbGetBinaryTagData)(PDB, TAGID);
+static LPWSTR (WINAPI *pSdbGetStringTagPtr)(PDB, TAGID);
+static TAGID (WINAPI *pSdbGetFirstChild)(PDB, TAGID);
+static TAGID (WINAPI *pSdbGetNextChild)(PDB, TAGID, TAGID);
+
+static void Write(HANDLE file, LPCVOID buffer, DWORD size)
+{
+    DWORD dwWritten = 0;
+    WriteFile(file, buffer, size, &dwWritten, NULL);
+}
+
+static void test_Sdb(void)
+{
+    static const WCHAR path[] = {'t','e','m','p',0};
+    static const WCHAR path2[] = {'t','e','m','p','2',0};
+    static const WCHAR tag_size_string[] = {'S','I','Z','E',0};
+    static const WCHAR tag_flag_lua_string[] = {'F','L','A','G','_','L','U','A',0};
+    static const TAG tags[5] = {
+        TAG_SIZE, TAG_FLAG_LUA, TAG_NAME,
+        TAG_STRINGTABLE, TAG_STRINGTABLE_ITEM
+    };
+    WCHAR buffer[6] = {0};
+    PDB pdb;
+    QWORD qword;
+    DWORD dword;
+    WORD word;
+    BOOL ret;
+    HANDLE file; /* temp file created for testing purpose */
+    TAG tag;
+    TAGID tagid, ptagid, stringref = 6;
+    LPCWSTR string;
+    PBYTE binary;
+
+    pdb = pSdbCreateDatabase(path, DOS_PATH);
+    ok (pdb != NULL, "failed to create database\n");
+    if(pdb != NULL)
+    {
+        ret = pSdbWriteDWORDTag(pdb, tags[0], 0xDEADBEEF);
+        ok (ret, "failed to write DWORD tag\n");
+        ret = pSdbWriteQWORDTag(pdb, tags[1], 0xDEADBEEFBABE);
+        ok (ret, "failed to write QWORD tag\n");
+        ret = pSdbWriteStringRefTag(pdb, tags[2], stringref);
+        ok (ret, "failed to write stringref tag\n");
+        tagid = pSdbBeginWriteListTag(pdb, tags[3]);
+        ok (tagid != TAGID_NULL, "unexpected NULL tagid\n");
+        ret = pSdbWriteStringTag(pdb, tags[4], path);
+        ok (ret, "failed to write string tag\n");
+        ret = pSdbWriteNULLTag(pdb, TAG_GENERAL);
+        ok (ret, "failed to write NULL tag\n");
+        ret = pSdbWriteWORDTag(pdb, TAG_MATCH_MODE, 0xACE);
+        ok (ret, "failed to write WORD tag\n");
+        ret = pSdbEndWriteListTag(pdb, tagid);
+        ok (ret, "failed to update list size\n");
+        /* [Err ][SdbCloseDatabase    ] Failed to close the file. */
+        pSdbCloseDatabaseWrite(pdb);
+    }
+
+    /* [Err ][SdbGetDatabaseID    ] Failed to get root tag */
+    pdb = pSdbOpenDatabase(path, DOS_PATH);
+    ok(pdb != NULL, "unexpected NULL handle\n");
+
+    if(pdb)
+    {
+        tagid = pSdbGetFirstChild(pdb, TAGID_ROOT);
+        ok(tagid == _TAGID_ROOT, "unexpected tagid %u, expected %u\n", tagid, _TAGID_ROOT);
+
+        tag = pSdbGetTagFromTagID(pdb, tagid);
+        ok(tag == TAG_SIZE, "unexpected tag 0x%x, expected 0x%x\n", tag, TAG_SIZE);
+
+        string = pSdbTagToString(tag);
+        ok(lstrcmpW(string, tag_size_string) == 0, "unexpected string %s, expected %s\n",
+           wine_dbgstr_w(string), wine_dbgstr_w(tag_size_string));
+
+        dword = pSdbReadDWORDTag(pdb, tagid, 0);
+        ok(dword == 0xDEADBEEF, "unexpected value %u, expected 0xDEADBEEF\n", dword);
+
+        tagid = pSdbGetNextChild(pdb, TAGID_ROOT, tagid);
+        ok(tagid == _TAGID_ROOT + sizeof(TAG) + sizeof(DWORD), "unexpected tagid %u, expected %u\n",
+           tagid, _TAGID_ROOT + sizeof(TAG) + sizeof(DWORD));
+
+        tag = pSdbGetTagFromTagID(pdb, tagid);
+        ok (tag == TAG_FLAG_LUA, "unexpected tag 0x%x, expected 0x%x\n", tag, TAG_FLAG_LUA);
+
+        string = pSdbTagToString(tag);
+        ok(lstrcmpW(string, tag_flag_lua_string) == 0, "unexpected string %s, expected %s\n",
+           wine_dbgstr_w(string), wine_dbgstr_w(tag_flag_lua_string));
+
+        qword = pSdbReadQWORDTag(pdb, tagid, 0);
+        ok(qword == 0xDEADBEEFBABE, "unexpected value 0x%I64x, expected 0xDEADBEEFBABE\n", qword);
+
+        tagid = pSdbGetNextChild(pdb, TAGID_ROOT, tagid);
+        string = pSdbGetStringTagPtr(pdb, tagid);
+        ok (string && (lstrcmpW(string, path) == 0), "unexpected string %s, expected %s\n",
+            wine_dbgstr_w(string), wine_dbgstr_w(path));
+
+        ptagid = pSdbGetNextChild(pdb, TAGID_ROOT, tagid);
+        tagid = pSdbGetFirstChild(pdb, ptagid);
+
+        string = pSdbGetStringTagPtr(pdb, tagid);
+        ok (string && (lstrcmpW(string, path) == 0), "unexpected string %s, expected %s\n",
+            wine_dbgstr_w(string), wine_dbgstr_w(path));
+
+        ok (pSdbReadStringTag(pdb, tagid, buffer, 6), "failed to write string to buffer\n");
+        /* [Err ][SdbpReadTagData     ] Buffer too small. Avail: 6, Need: 10. */
+        ok (!pSdbReadStringTag(pdb, tagid, buffer, 3), "string was written to buffer, but failure was expected");
+        ok (pSdbGetTagDataSize(pdb, tagid) == 5 * sizeof(WCHAR), "string has unexpected size\n");
+
+        tagid = pSdbGetNextChild(pdb, ptagid, tagid);
+        tag = pSdbGetTagFromTagID(pdb, tagid);
+        ok (tag == TAG_GENERAL, "unexpected tag 0x%x, expected 0x%x\n", tag, TAG_GENERAL);
+        ok (pSdbGetTagDataSize(pdb, tagid) == 0, "null tag with size > 0\n");
+
+        tagid = pSdbGetNextChild(pdb, ptagid, tagid);
+        word = pSdbReadWORDTag(pdb, tagid, 0);
+        ok (word == 0xACE, "unexpected value 0x%x, expected 0x%x\n", word, 0xACE);
+
+        pSdbCloseDatabase(pdb);
+    }
+    DeleteFileW(path);
+
+    file = CreateFileW(path2, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+    ok (file != INVALID_HANDLE_VALUE, "failed to open file\n");
+    Write(file, &qword, 8);
+    CloseHandle(file);
+
+    pdb = pSdbCreateDatabase(path, DOS_PATH);
+    ok(pdb != NULL, "unexpected NULL handle\n");
+
+    if(pdb)
+    {
+        ret = pSdbWriteBinaryTagFromFile(pdb, TAG_DATA_BITS, path2);
+        ok(ret, "failed to write tag from binary file\n");
+        pSdbCloseDatabaseWrite(pdb);     /* [Err ][SdbCloseDatabase    ] Failed to close the file. */
+        DeleteFileW(path2);
+
+        pdb = pSdbOpenDatabase(path, DOS_PATH);
+        ok(pdb != NULL, "unexpected NULL handle\n");
+        binary = pSdbGetBinaryTagData(pdb, _TAGID_ROOT);
+        ok(memcmp(binary, &qword, 8) == 0, "binary data is corrupt\n");
+        ret = pSdbReadBinaryTag(pdb, _TAGID_ROOT, (PBYTE)buffer, 12);
+        ok(ret, "failed to read binary tag\n");
+        ok(memcmp(buffer, &qword, 8) == 0, "binary data is corrupt\n");
+        pSdbCloseDatabase(pdb);
+    }
+    DeleteFileW(path);
+}
+
+START_TEST(db)
+{
+    //SetEnvironmentVariable("SHIM_DEBUG_LEVEL", "4");
+    //SetEnvironmentVariable("DEBUGCHANNEL", "+apphelp");
+    hdll = LoadLibraryA("apphelp.dll");
+    pSdbTagToString = (void *) GetProcAddress(hdll, "SdbTagToString");
+    pSdbOpenDatabase = (void *) GetProcAddress(hdll, "SdbOpenDatabase");
+    pSdbCreateDatabase = (void *) GetProcAddress(hdll, "SdbCreateDatabase");
+    pSdbCloseDatabase = (void *) GetProcAddress(hdll, "SdbCloseDatabase");
+    pSdbCloseDatabaseWrite = (void *) GetProcAddress(hdll, "SdbCloseDatabaseWrite");
+    pSdbGetTagFromTagID = (void *) GetProcAddress(hdll, "SdbGetTagFromTagID");
+    pSdbWriteNULLTag = (void *) GetProcAddress(hdll, "SdbWriteNULLTag");
+    pSdbWriteWORDTag = (void *) GetProcAddress(hdll, "SdbWriteWORDTag");
+    pSdbWriteDWORDTag = (void *) GetProcAddress(hdll, "SdbWriteDWORDTag");
+    pSdbWriteQWORDTag = (void *) GetProcAddress(hdll, "SdbWriteQWORDTag");
+    pSdbWriteBinaryTagFromFile = (void *) GetProcAddress(hdll, "SdbWriteBinaryTagFromFile");
+    pSdbWriteStringTag = (void *) GetProcAddress(hdll, "SdbWriteStringTag");
+    pSdbWriteStringRefTag = (void *) GetProcAddress(hdll, "SdbWriteStringRefTag");
+    pSdbBeginWriteListTag = (void *)GetProcAddress(hdll, "SdbBeginWriteListTag");
+    pSdbEndWriteListTag = (void *) GetProcAddress(hdll, "SdbEndWriteListTag");
+    pSdbReadWORDTag = (void *) GetProcAddress(hdll, "SdbReadWORDTag");
+    pSdbReadDWORDTag = (void *) GetProcAddress(hdll, "SdbReadDWORDTag");
+    pSdbReadQWORDTag = (void *) GetProcAddress(hdll, "SdbReadQWORDTag");
+    pSdbReadBinaryTag = (void *) GetProcAddress(hdll, "SdbReadBinaryTag");
+    pSdbReadStringTag = (void *) GetProcAddress(hdll, "SdbReadStringTag");
+    pSdbGetTagDataSize = (void *) GetProcAddress(hdll, "SdbGetTagDataSize");
+    pSdbGetBinaryTagData = (void *) GetProcAddress(hdll, "SdbGetBinaryTagData");
+    pSdbGetStringTagPtr = (void *) GetProcAddress(hdll, "SdbGetStringTagPtr");
+    pSdbGetFirstChild = (void *) GetProcAddress(hdll, "SdbGetFirstChild");
+    pSdbGetNextChild = (void *) GetProcAddress(hdll, "SdbGetNextChild");
+
+    test_Sdb();
+}
index a050482..167bbb3 100644 (file)
@@ -4,11 +4,13 @@
 #include <wine/test.h>
 
 extern void func_apphelp(void);
+extern void func_db(void);
 extern void func_layerapi(void);
 
 const struct test winetest_testlist[] =
 {
     { "apphelp", func_apphelp },
+    { "db", func_db },
     { "layerapi", func_layerapi },
     { 0, 0 }
 };