From 9890259605aa0c35db96e3709f7fad6e840aa29c Mon Sep 17 00:00:00 2001
From: Dmitry Timoshkov <dmitry@baikal.ru>
Date: Tue, 26 Jan 2016 15:05:54 +0800
Subject: oleaut32: Implement decoding of SLTG help strings.

Based on the patch by Sebastian Lackner <sebastian@fds-team.de>.
---
 dlls/oleaut32/typelib.c | 130 +++++++++++++++++++++++++++++++++++++++---------
 dlls/oleaut32/typelib.h |   4 +-
 2 files changed, 109 insertions(+), 25 deletions(-)

diff --git a/dlls/oleaut32/typelib.c b/dlls/oleaut32/typelib.c
index 82ea76c..0a5e07c 100644
--- a/dlls/oleaut32/typelib.c
+++ b/dlls/oleaut32/typelib.c
@@ -3703,6 +3703,87 @@ static BOOL TLB_GUIDFromString(const char *str, GUID *guid)
   return TRUE;
 }
 
+struct bitstream
+{
+    const BYTE *buffer;
+    DWORD       length;
+    WORD        current;
+};
+
+static const char *lookup_code(const BYTE *table, DWORD table_size, struct bitstream *bits)
+{
+    const BYTE *p = table;
+
+    while (p < table + table_size && *p == 0x80)
+    {
+        if (p + 2 >= table + table_size) return NULL;
+
+        if (!(bits->current & 0xff))
+        {
+            if (!bits->length) return NULL;
+            bits->current = (*bits->buffer << 8) | 1;
+            bits->buffer++;
+            bits->length--;
+        }
+
+        if (bits->current & 0x8000)
+        {
+            p += 3;
+        }
+        else
+        {
+            p = table + (*(p + 2) | (*(p + 1) << 8));
+        }
+
+        bits->current <<= 1;
+    }
+
+    if (p + 1 < table + table_size && *(p + 1))
+    {
+        /* FIXME: Whats the meaning of *p? */
+        const BYTE *q = p + 1;
+        while (q < table + table_size && *q) q++;
+        return (q < table + table_size) ? (const char *)(p + 1) : NULL;
+    }
+
+    return NULL;
+}
+
+static const TLBString *decode_string(const BYTE *table, const char *stream, DWORD stream_length, ITypeLibImpl *lib)
+{
+    DWORD buf_size, table_size;
+    const char *p;
+    struct bitstream bits;
+    BSTR buf;
+    TLBString *tlbstr;
+
+    if (!stream_length) return NULL;
+
+    bits.buffer = (const BYTE *)stream;
+    bits.length = stream_length;
+    bits.current = 0;
+
+    buf_size = *(const WORD *)table;
+    table += sizeof(WORD);
+    table_size = *(const DWORD *)table;
+    table += sizeof(DWORD);
+
+    buf = SysAllocStringLen(NULL, buf_size);
+    buf[0] = 0;
+
+    while ((p = lookup_code(table, table_size, &bits)))
+    {
+        static const WCHAR spaceW[] = { ' ',0 };
+        if (buf[0]) strcatW(buf, spaceW);
+        MultiByteToWideChar(CP_ACP, 0, p, -1, buf + strlenW(buf), buf_size - strlenW(buf));
+    }
+
+    tlbstr = TLB_append_str(&lib->string_list, buf);
+    SysFreeString(buf);
+
+    return tlbstr;
+}
+
 static WORD SLTG_ReadString(const char *ptr, const TLBString **pStr, ITypeLibImpl *lib)
 {
     WORD bytelen;
@@ -4386,17 +4467,17 @@ static void SLTG_ProcessModule(char *pBlk, ITypeInfoImpl *pTI,
 /* Because SLTG_OtherTypeInfo is such a painful struct, we make a more
    manageable copy of it into this */
 typedef struct {
-  WORD small_no;
   char *index_name;
   char *other_name;
   WORD res1a;
   WORD name_offs;
-  WORD more_bytes;
+  WORD hlpstr_len;
   char *extra;
   WORD res20;
   DWORD helpcontext;
   WORD res26;
   GUID uuid;
+  WORD typekind;
 } SLTG_InternalOtherTypeInfo;
 
 /****************************************************************************
@@ -4415,8 +4496,8 @@ static ITypeLib2* ITypeLib2_Constructor_SLTG(LPVOID pLib, DWORD dwTLBLength)
     LPVOID pBlk, pFirstBlk;
     SLTG_LibBlk *pLibBlk;
     SLTG_InternalOtherTypeInfo *pOtherTypeInfoBlks;
-    char *pAfterOTIBlks = NULL;
     char *pNameTable, *ptr;
+    const BYTE *hlp_strings;
     int i;
     DWORD len, order;
     ITypeInfoImpl **ppTypeInfoImpl;
@@ -4482,53 +4563,55 @@ static ITypeLib2* ITypeLib2_Constructor_SLTG(LPVOID pLib, DWORD dwTLBLength)
     len += 0x40;
 
     /* And now TypeInfoCount of SLTG_OtherTypeInfo */
+    pTypeLibImpl->TypeInfoCount = *(WORD *)((char *)pLibBlk + len);
+    len += sizeof(WORD);
 
     pOtherTypeInfoBlks = heap_alloc_zero(sizeof(*pOtherTypeInfoBlks) * pTypeLibImpl->TypeInfoCount);
 
-
     ptr = (char*)pLibBlk + len;
 
     for(i = 0; i < pTypeLibImpl->TypeInfoCount; i++) {
 	WORD w, extra;
 	len = 0;
 
-	pOtherTypeInfoBlks[i].small_no = *(WORD*)ptr;
-
-	w = *(WORD*)(ptr + 2);
+	w = *(WORD*)ptr;
 	if(w != 0xffff) {
 	    len += w;
 	    pOtherTypeInfoBlks[i].index_name = heap_alloc(w+1);
-	    memcpy(pOtherTypeInfoBlks[i].index_name, ptr + 4, w);
+	    memcpy(pOtherTypeInfoBlks[i].index_name, ptr + 2, w);
 	    pOtherTypeInfoBlks[i].index_name[w] = '\0';
 	}
-	w = *(WORD*)(ptr + 4 + len);
+	w = *(WORD*)(ptr + 2 + len);
 	if(w != 0xffff) {
-	    TRACE_(typelib)("\twith %s\n", debugstr_an(ptr + 6 + len, w));
-	    len += w;
+	    TRACE_(typelib)("\twith %s\n", debugstr_an(ptr + 4 + len, w));
 	    pOtherTypeInfoBlks[i].other_name = heap_alloc(w+1);
-	    memcpy(pOtherTypeInfoBlks[i].other_name, ptr + 6 + len, w);
+	    memcpy(pOtherTypeInfoBlks[i].other_name, ptr + 4 + len, w);
 	    pOtherTypeInfoBlks[i].other_name[w] = '\0';
+	    len += w;
 	}
-	pOtherTypeInfoBlks[i].res1a = *(WORD*)(ptr + len + 6);
-	pOtherTypeInfoBlks[i].name_offs = *(WORD*)(ptr + len + 8);
-	extra = pOtherTypeInfoBlks[i].more_bytes = *(WORD*)(ptr + 10 + len);
+	pOtherTypeInfoBlks[i].res1a = *(WORD*)(ptr + 4 + len);
+	pOtherTypeInfoBlks[i].name_offs = *(WORD*)(ptr + 6 + len);
+	extra = pOtherTypeInfoBlks[i].hlpstr_len = *(WORD*)(ptr + 8 + len);
 	if(extra) {
 	    pOtherTypeInfoBlks[i].extra = heap_alloc(extra);
-	    memcpy(pOtherTypeInfoBlks[i].extra, ptr + 12, extra);
+	    memcpy(pOtherTypeInfoBlks[i].extra, ptr + 10 + len, extra);
 	    len += extra;
 	}
-	pOtherTypeInfoBlks[i].res20 = *(WORD*)(ptr + 12 + len);
-	pOtherTypeInfoBlks[i].helpcontext = *(DWORD*)(ptr + 14 + len);
-	pOtherTypeInfoBlks[i].res26 = *(WORD*)(ptr + 18 + len);
-	memcpy(&pOtherTypeInfoBlks[i].uuid, ptr + 20 + len, sizeof(GUID));
+	pOtherTypeInfoBlks[i].res20 = *(WORD*)(ptr + 10 + len);
+	pOtherTypeInfoBlks[i].helpcontext = *(DWORD*)(ptr + 12 + len);
+	pOtherTypeInfoBlks[i].res26 = *(WORD*)(ptr + 16 + len);
+	memcpy(&pOtherTypeInfoBlks[i].uuid, ptr + 18 + len, sizeof(GUID));
+	pOtherTypeInfoBlks[i].typekind = *(WORD*)(ptr + 18 + sizeof(GUID) + len);
 	len += sizeof(SLTG_OtherTypeInfo);
 	ptr += len;
     }
 
-    pAfterOTIBlks = ptr;
+    /* Get the next DWORD */
+    len = *(DWORD*)ptr;
 
-    /* Skip this WORD and get the next DWORD */
-    len = *(DWORD*)(pAfterOTIBlks + 2);
+    hlp_strings = (const BYTE *)ptr + sizeof(DWORD);
+    TRACE("max help string length %#x, help strings length %#x\n",
+        *(WORD *)hlp_strings, *(DWORD *)(hlp_strings + 2));
 
     /* Now add this to pLibBLk look at what we're pointing at and
        possibly add 0x20, then add 0x216, sprinkle a bit a magic
@@ -4594,6 +4677,7 @@ static ITypeLib2* ITypeLib2_Constructor_SLTG(LPVOID pLib, DWORD dwTLBLength)
       (*ppTypeInfoImpl)->index = i;
       (*ppTypeInfoImpl)->Name = SLTG_ReadName(pNameTable, pOtherTypeInfoBlks[i].name_offs, pTypeLibImpl);
       (*ppTypeInfoImpl)->dwHelpContext = pOtherTypeInfoBlks[i].helpcontext;
+      (*ppTypeInfoImpl)->DocString = decode_string(hlp_strings, pOtherTypeInfoBlks[i].extra, pOtherTypeInfoBlks[i].hlpstr_len, pTypeLibImpl);
       (*ppTypeInfoImpl)->guid = TLB_append_guid(&pTypeLibImpl->guid_list, &pOtherTypeInfoBlks[i].uuid, 2);
       (*ppTypeInfoImpl)->typeattr.typekind = pTIHeader->typekind;
       (*ppTypeInfoImpl)->typeattr.wMajorVerNum = pTIHeader->major_version;
diff --git a/dlls/oleaut32/typelib.h b/dlls/oleaut32/typelib.h
index 31c9ab9..93b96ea 100644
--- a/dlls/oleaut32/typelib.h
+++ b/dlls/oleaut32/typelib.h
@@ -387,18 +387,18 @@ typedef struct {
 /* we then get 0x40 bytes worth of 0xffff or small numbers followed by
    nrOfFileBlks - 2 of these */
 typedef struct {
-	WORD small_no;
 	SLTG_Name index_name; /* This refers to a name in the directory */
 	SLTG_Name other_name; /* Another one of these weird names */
 	WORD res1a;	      /* 0xffff */
 	WORD name_offs;	      /* offset to name in name table */
-	WORD more_bytes;      /* if this is non-zero we get this many
+	WORD hlpstr_len;      /* if this is non-zero we get this many
 				 bytes before the next element, which seem
 				 to reference the docstring of the type ? */
 	WORD res20;	      /* 0xffff */
 	DWORD helpcontext;
 	WORD res26;	      /* 0xffff */
         GUID uuid;
+        WORD typekind;
 } SLTG_OtherTypeInfo;
 
 /* Next we get WORD 0x0003 followed by a DWORD which if we add to
-- 
2.9.0

