implementation of ITSS.DLL

Mike McCormack mike at codeweavers.com
Tue Aug 24 02:06:23 CDT 2004


Hi All,

This is an implementation of the Infotech Storage System (itss.dll) 
based upon chmlib, but hacked up alot to be unicode safe, and use the 
Windows API.

The Infotech Storage system provides an IStorage interface through which 
to read compiled HTML (.chm) files, and an IMoniker interface to parse 
monikers representing documents within the chm file.

Mike


ChangeLog:
Stewart Caie<kyz at uklinux.net>
Jed Wing <jedwin at ugcs.caltech.edu>
Mike McCormack <mike at codeweavers.com>
* add an implemenation of ITSS.DLL

-------------- next part --------------
Index: configure.ac
===================================================================
RCS file: /home/wine/wine/configure.ac,v
retrieving revision 1.302
diff -u -r1.302 configure.ac
--- configure.ac	19 Aug 2004 19:31:20 -0000	1.302
+++ configure.ac	24 Aug 2004 05:30:09 -0000
@@ -1576,6 +1577,7 @@
 dlls/imm32/Makefile
 dlls/iphlpapi/Makefile
 dlls/iphlpapi/tests/Makefile
+dlls/itss/Makefile
 dlls/kernel/Makefile
 dlls/kernel/tests/Makefile
 dlls/lzexpand/Makefile
Index: dlls/Makefile.in
===================================================================
RCS file: /home/wine/wine/dlls/Makefile.in,v
retrieving revision 1.224
diff -u -r1.224 Makefile.in
--- dlls/Makefile.in	19 Aug 2004 19:31:20 -0000	1.224
+++ dlls/Makefile.in	24 Aug 2004 05:30:49 -0000
@@ -63,6 +63,7 @@
 	imagehlp \
 	imm32 \
 	iphlpapi \
+	itss \
 	kernel \
 	lzexpand \
 	mapi32 \
@@ -287,6 +288,7 @@
 	imagehlp.dll.so \
 	imm32.dll.so \
 	iphlpapi.dll.so \
+	itss.dll.so \
 	joystick.drv.so \
 	kernel32.dll.so \
 	libdxerr8.a \
@@ -578,6 +580,9 @@
 iphlpapi.dll.so: iphlpapi/iphlpapi.dll.so
 	$(RM) $@ && $(LN_S) iphlpapi/iphlpapi.dll.so $@
 
+itss.dll.so: itss/itss.dll.so
+	$(RM) $@ && $(LN_S) itss/itss.dll.so $@
+
 joystick.drv.so: winmm/joystick/joystick.drv.so
 	$(RM) $@ && $(LN_S) winmm/joystick/joystick.drv.so $@
 
@@ -1013,6 +1018,7 @@
 	libimagehlp.$(IMPLIBEXT) \
 	libimm32.$(IMPLIBEXT) \
 	libiphlpapi.$(IMPLIBEXT) \
+	libitss.$(IMPLIBEXT) \
 	libkernel32.$(IMPLIBEXT) \
 	liblz32.$(IMPLIBEXT) \
 	libmapi32.$(IMPLIBEXT) \
@@ -1348,6 +1354,11 @@
 libiphlpapi.a: iphlpapi/iphlpapi.spec.def
 	$(DLLTOOL) -k -l $@ -d iphlpapi/iphlpapi.spec.def
 
+libitss.def: itss/itss.spec.def
+	$(RM) $@ && $(LN_S) itss/itss.spec.def $@
+libitss.a: itss/itss.spec.def
+	$(DLLTOOL) -k -l $@ -d itss/itss.spec.def
+
 libkernel32.def: kernel/kernel32.spec.def
 	$(RM) $@ && $(LN_S) kernel/kernel32.spec.def $@
 libkernel32.a: kernel/kernel32.spec.def
@@ -1759,6 +1770,7 @@
 imagehlp/imagehlp.spec.def: $(WINEBUILD)
 imm32/imm32.spec.def: $(WINEBUILD)
 iphlpapi/iphlpapi.spec.def: $(WINEBUILD)
+itss/itss.spec.def: $(WINEBUILD)
 kernel/kernel32.spec.def: $(WINEBUILD)
 lzexpand/lz32.spec.def: $(WINEBUILD)
 mapi32/mapi32.spec.def: $(WINEBUILD)
@@ -1891,6 +1903,7 @@
 imagehlp/imagehlp.dll.so: imagehlp
 imm32/imm32.dll.so: imm32
 iphlpapi/iphlpapi.dll.so: iphlpapi
+itss/itss.dll.so: itss
 winmm/joystick/joystick.drv.so: winmm/joystick
 kernel/kernel32.dll.so: kernel
 lzexpand/lz32.dll.so: lzexpand
--- /dev/null	1994-07-18 08:46:18.000000000 +0900
+++ dlls/itss/Makefile.in	2004-08-24 15:45:01.000000000 +0900
@@ -0,0 +1,22 @@
+TOPSRCDIR = @top_srcdir@
+TOPOBJDIR = ../..
+SRCDIR    = @srcdir@
+VPATH     = @srcdir@
+MODULE    = itss.dll
+IMPORTS   = ole32 user32 advapi32 kernel32 ntdll
+EXTRALIBS = $(LIBUNICODE) -luuid
+EXTRADEFS = -DCOM_NO_WINDOWS_H
+
+C_SRCS = \
+	chm_lib.c \
+	lzx.c \
+	itss.c \
+	moniker.c \
+	storage.c
+
+ at MAKE_DLL_RULES@
+
+itss.h: itss.idl
+	$(WIDL) $(IDLFLAGS) -b -h -H $@ $<
+
+### Dependencies:
--- /dev/null	1994-07-18 08:46:18.000000000 +0900
+++ dlls/itss/chm_lib.c	2004-08-24 15:45:11.000000000 +0900
@@ -0,0 +1,1676 @@
+/* $Id: chm_lib.c,v 1.1 2004/08/23 07:58:20 mike Exp $ */
+/***************************************************************************
+ *             chm_lib.c - CHM archive manipulation routines               *
+ *                           -------------------                           *
+ *                                                                         *
+ *  author:     Jed Wing <jedwin at ugcs.caltech.edu>                         *
+ *  version:    0.3                                                        *
+ *  notes:      These routines are meant for the manipulation of microsoft *
+ *              .chm (compiled html help) files, but may likely be used    *
+ *              for the manipulation of any ITSS archive, if ever ITSS     *
+ *              archives are used for any other purpose.                   *
+ *                                                                         *
+ *              Note also that the section names are statically handled.   *
+ *              To be entirely correct, the section names should be read   *
+ *              from the section names meta-file, and then the various     *
+ *              content sections and the "transforms" to apply to the data *
+ *              they contain should be inferred from the section name and  *
+ *              the meta-files referenced using that name; however, all of *
+ *              the files I've been able to get my hands on appear to have *
+ *              only two sections: Uncompressed and MSCompressed.          *
+ *              Additionally, the ITSS.DLL file included with Windows does *
+ *              not appear to handle any different transforms than the     *
+ *              simple LZX-transform.  Furthermore, the list of transforms *
+ *              to apply is broken, in that only half the required space   *
+ *              is allocated for the list.  (It appears as though the      *
+ *              space is allocated for ASCII strings, but the strings are  *
+ *              written as unicode.  As a result, only the first half of   *
+ *              the string appears.)  So this is probably not too big of   *
+ *              a deal, at least until CHM v4 (MS .lit files), which also  *
+ *              incorporate encryption, of some description.               *
+ *                                                                         *
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program 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.                       *
+ *                                                                         *
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ * Adapted for Wine by Mike McCormack                                      *
+ *                                                                         *
+ ***************************************************************************/
+
+#include "config.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wine/unicode.h"
+
+#include "chm_lib.h"
+#include "lzx.h"
+
+#define CHM_ACQUIRE_LOCK(a) do {                        \
+        EnterCriticalSection(&(a));                     \
+    } while(0)
+#define CHM_RELEASE_LOCK(a) do {                        \
+        EnterCriticalSection(&(a));                     \
+    } while(0)
+
+#define CHM_NULL_FD (INVALID_HANDLE_VALUE)
+#define CHM_CLOSE_FILE(fd) CloseHandle((fd))
+
+/*
+ * defines related to tuning
+ */
+#ifndef CHM_MAX_BLOCKS_CACHED
+#define CHM_MAX_BLOCKS_CACHED 5
+#endif
+
+/*
+ * architecture specific defines
+ *
+ * Note: as soon as C99 is more widespread, the below defines should
+ * probably just use the C99 sized-int types.
+ *
+ * The following settings will probably work for many platforms.  The sizes
+ * don't have to be exactly correct, but the types must accommodate at least as
+ * many bits as they specify.
+ */
+
+/* i386, 32-bit, Windows */
+typedef BYTE   UChar;
+typedef SHORT  Int16;
+typedef USHORT UInt16;
+typedef LONG   Int32;
+typedef DWORD      UInt32;
+typedef LONGLONG   Int64;
+typedef ULONGLONG  UInt64;
+
+#ifdef __GNUC__
+#define memcmp __builtin_memcmp
+#define memcpy __builtin_memcpy
+#define strlen __builtin_strlen
+#endif
+
+/* utilities for unmarshalling data */
+static int _unmarshal_char_array(unsigned char **pData,
+                                 unsigned long *pLenRemain,
+                                 char *dest,
+                                 int count)
+{
+    if (count <= 0  ||  (unsigned int)count > *pLenRemain)
+        return 0;
+    memcpy(dest, (*pData), count);
+    *pData += count;
+    *pLenRemain -= count;
+    return 1;
+}
+
+static int _unmarshal_uchar_array(unsigned char **pData,
+                                  unsigned long *pLenRemain,
+                                  unsigned char *dest,
+                                  int count)
+{
+        if (count <= 0  ||  (unsigned int)count > *pLenRemain)
+        return 0;
+    memcpy(dest, (*pData), count);
+    *pData += count;
+    *pLenRemain -= count;
+    return 1;
+}
+
+static int _unmarshal_int32(unsigned char **pData,
+                            unsigned long *pLenRemain,
+                            Int32 *dest)
+{
+    if (4 > *pLenRemain)
+        return 0;
+    *dest = (*pData)[0] | (*pData)[1]<<8 | (*pData)[2]<<16 | (*pData)[3]<<24;
+    *pData += 4;
+    *pLenRemain -= 4;
+    return 1;
+}
+
+static int _unmarshal_uint32(unsigned char **pData,
+                             unsigned long *pLenRemain,
+                             UInt32 *dest)
+{
+    if (4 > *pLenRemain)
+        return 0;
+    *dest = (*pData)[0] | (*pData)[1]<<8 | (*pData)[2]<<16 | (*pData)[3]<<24;
+    *pData += 4;
+    *pLenRemain -= 4;
+    return 1;
+}
+
+static int _unmarshal_int64(unsigned char **pData,
+                            unsigned long *pLenRemain,
+                            Int64 *dest)
+{
+    Int64 temp;
+    int i;
+    if (8 > *pLenRemain)
+        return 0;
+    temp=0;
+    for(i=8; i>0; i--)
+    {
+        temp <<= 8;
+        temp |= (*pData)[i-1];
+    }
+    *dest = temp;
+    *pData += 8;
+    *pLenRemain -= 8;
+    return 1;
+}
+
+static int _unmarshal_uint64(unsigned char **pData,
+                             unsigned long *pLenRemain,
+                             UInt64 *dest)
+{
+    UInt64 temp;
+    int i;
+    if (8 > *pLenRemain)
+        return 0;
+    temp=0;
+    for(i=8; i>0; i--)
+    {
+        temp <<= 8;
+        temp |= (*pData)[i-1];
+    }
+    *dest = temp;
+    *pData += 8;
+    *pLenRemain -= 8;
+    return 1;
+}
+
+static int _unmarshal_uuid(unsigned char **pData,
+                           unsigned long *pDataLen,
+                           unsigned char *dest)
+{
+    return _unmarshal_uchar_array(pData, pDataLen, dest, 16);
+}
+
+/* names of sections essential to decompression */
+static const WCHAR _CHMU_RESET_TABLE[] = {
+':',':','D','a','t','a','S','p','a','c','e','/',
+        'S','t','o','r','a','g','e','/',
+        'M','S','C','o','m','p','r','e','s','s','e','d','/',
+        'T','r','a','n','s','f','o','r','m','/',
+        '{','7','F','C','2','8','9','4','0','-','9','D','3','1',
+          '-','1','1','D','0','-','9','B','2','7','-',
+          '0','0','A','0','C','9','1','E','9','C','7','C','}','/',
+        'I','n','s','t','a','n','c','e','D','a','t','a','/',
+        'R','e','s','e','t','T','a','b','l','e',0
+};
+static const WCHAR _CHMU_LZXC_CONTROLDATA[] = {
+':',':','D','a','t','a','S','p','a','c','e','/',
+        'S','t','o','r','a','g','e','/',
+        'M','S','C','o','m','p','r','e','s','s','e','d','/',
+        'C','o','n','t','r','o','l','D','a','t','a',0
+};
+static const WCHAR _CHMU_CONTENT[] = {
+':',':','D','a','t','a','S','p','a','c','e','/',
+        'S','t','o','r','a','g','e','/',
+        'M','S','C','o','m','p','r','e','s','s','e','d','/',
+        'C','o','n','t','e','n','t',0
+};
+static const WCHAR _CHMU_SPANINFO[] = {
+':',':','D','a','t','a','S','p','a','c','e','/',
+        'S','t','o','r','a','g','e','/',
+        'M','S','C','o','m','p','r','e','s','s','e','d','/',
+        'S','p','a','n','I','n','f','o',
+};
+
+/*
+ * structures local to this module
+ */
+
+/* structure of ITSF headers */
+#define _CHM_ITSF_V2_LEN (0x58)
+#define _CHM_ITSF_V3_LEN (0x60)
+struct chmItsfHeader
+{
+    char        signature[4];           /*  0 (ITSF) */
+    Int32       version;                /*  4 */
+    Int32       header_len;             /*  8 */
+    Int32       unknown_000c;           /*  c */
+    UInt32      last_modified;          /* 10 */
+    UInt32      lang_id;                /* 14 */
+    UChar       dir_uuid[16];           /* 18 */
+    UChar       stream_uuid[16];        /* 28 */
+    UInt64      unknown_offset;         /* 38 */
+    UInt64      unknown_len;            /* 40 */
+    UInt64      dir_offset;             /* 48 */
+    UInt64      dir_len;                /* 50 */
+    UInt64      data_offset;            /* 58 (Not present before V3) */
+}; /* __attribute__ ((aligned (1))); */
+
+static int _unmarshal_itsf_header(unsigned char **pData,
+                                  unsigned long *pDataLen,
+                                  struct chmItsfHeader *dest)
+{
+    /* we only know how to deal with the 0x58 and 0x60 byte structures */
+    if (*pDataLen != _CHM_ITSF_V2_LEN  &&  *pDataLen != _CHM_ITSF_V3_LEN)
+        return 0;
+
+    /* unmarshal common fields */
+    _unmarshal_char_array(pData, pDataLen,  dest->signature, 4);
+    _unmarshal_int32     (pData, pDataLen, &dest->version);
+    _unmarshal_int32     (pData, pDataLen, &dest->header_len);
+    _unmarshal_int32     (pData, pDataLen, &dest->unknown_000c);
+    _unmarshal_uint32    (pData, pDataLen, &dest->last_modified);
+    _unmarshal_uint32    (pData, pDataLen, &dest->lang_id);
+    _unmarshal_uuid      (pData, pDataLen,  dest->dir_uuid);
+    _unmarshal_uuid      (pData, pDataLen,  dest->stream_uuid);
+    _unmarshal_uint64    (pData, pDataLen, &dest->unknown_offset);
+    _unmarshal_uint64    (pData, pDataLen, &dest->unknown_len);
+    _unmarshal_uint64    (pData, pDataLen, &dest->dir_offset);
+    _unmarshal_uint64    (pData, pDataLen, &dest->dir_len);
+
+    /* error check the data */
+    /* XXX: should also check UUIDs, probably, though with a version 3 file,
+     * current MS tools do not seem to use them.
+     */
+    if (memcmp(dest->signature, "ITSF", 4) != 0)
+        return 0;
+    if (dest->version == 2)
+    {
+        if (dest->header_len < _CHM_ITSF_V2_LEN)
+            return 0;
+    }
+    else if (dest->version == 3)
+    {
+        if (dest->header_len < _CHM_ITSF_V3_LEN)
+            return 0;
+    }
+    else
+        return 0;
+
+    /* now, if we have a V3 structure, unmarshal the rest.
+     * otherwise, compute it
+     */
+    if (dest->version == 3)
+    {
+        if (*pDataLen != 0)
+            _unmarshal_uint64(pData, pDataLen, &dest->data_offset);
+        else
+            return 0;
+    }
+    else
+        dest->data_offset = dest->dir_offset + dest->dir_len;
+
+    return 1;
+}
+
+/* structure of ITSP headers */
+#define _CHM_ITSP_V1_LEN (0x54)
+struct chmItspHeader
+{
+    char        signature[4];           /*  0 (ITSP) */
+    Int32       version;                /*  4 */
+    Int32       header_len;             /*  8 */
+    Int32       unknown_000c;           /*  c */
+    UInt32      block_len;              /* 10 */
+    Int32       blockidx_intvl;         /* 14 */
+    Int32       index_depth;            /* 18 */
+    Int32       index_root;             /* 1c */
+    Int32       index_head;             /* 20 */
+    Int32       unknown_0024;           /* 24 */
+    UInt32      num_blocks;             /* 28 */
+    Int32       unknown_002c;           /* 2c */
+    UInt32      lang_id;                /* 30 */
+    UChar       system_uuid[16];        /* 34 */
+    UChar       unknown_0044[16];       /* 44 */
+}; /* __attribute__ ((aligned (1))); */
+
+static int _unmarshal_itsp_header(unsigned char **pData,
+                                  unsigned long *pDataLen,
+                                  struct chmItspHeader *dest)
+{
+    /* we only know how to deal with a 0x54 byte structures */
+    if (*pDataLen != _CHM_ITSP_V1_LEN)
+        return 0;
+
+    /* unmarshal fields */
+    _unmarshal_char_array(pData, pDataLen,  dest->signature, 4);
+    _unmarshal_int32     (pData, pDataLen, &dest->version);
+    _unmarshal_int32     (pData, pDataLen, &dest->header_len);
+    _unmarshal_int32     (pData, pDataLen, &dest->unknown_000c);
+    _unmarshal_uint32    (pData, pDataLen, &dest->block_len);
+    _unmarshal_int32     (pData, pDataLen, &dest->blockidx_intvl);
+    _unmarshal_int32     (pData, pDataLen, &dest->index_depth);
+    _unmarshal_int32     (pData, pDataLen, &dest->index_root);
+    _unmarshal_int32     (pData, pDataLen, &dest->index_head);
+    _unmarshal_int32     (pData, pDataLen, &dest->unknown_0024);
+    _unmarshal_uint32    (pData, pDataLen, &dest->num_blocks);
+    _unmarshal_int32     (pData, pDataLen, &dest->unknown_002c);
+    _unmarshal_uint32    (pData, pDataLen, &dest->lang_id);
+    _unmarshal_uuid      (pData, pDataLen,  dest->system_uuid);
+    _unmarshal_uchar_array(pData, pDataLen, dest->unknown_0044, 16);
+
+    /* error check the data */
+    if (memcmp(dest->signature, "ITSP", 4) != 0)
+        return 0;
+    if (dest->version != 1)
+        return 0;
+    if (dest->header_len != _CHM_ITSP_V1_LEN)
+        return 0;
+
+    return 1;
+}
+
+/* structure of PMGL headers */
+static const char _chm_pmgl_marker[4] = "PMGL";
+#define _CHM_PMGL_LEN (0x14)
+struct chmPmglHeader
+{
+    char        signature[4];           /*  0 (PMGL) */
+    UInt32      free_space;             /*  4 */
+    UInt32      unknown_0008;           /*  8 */
+    Int32       block_prev;             /*  c */
+    Int32       block_next;             /* 10 */
+}; /* __attribute__ ((aligned (1))); */
+
+static int _unmarshal_pmgl_header(unsigned char **pData,
+                                  unsigned long *pDataLen,
+                                  struct chmPmglHeader *dest)
+{
+    /* we only know how to deal with a 0x14 byte structures */
+    if (*pDataLen != _CHM_PMGL_LEN)
+        return 0;
+
+    /* unmarshal fields */
+    _unmarshal_char_array(pData, pDataLen,  dest->signature, 4);
+    _unmarshal_uint32    (pData, pDataLen, &dest->free_space);
+    _unmarshal_uint32    (pData, pDataLen, &dest->unknown_0008);
+    _unmarshal_int32     (pData, pDataLen, &dest->block_prev);
+    _unmarshal_int32     (pData, pDataLen, &dest->block_next);
+
+    /* check structure */
+    if (memcmp(dest->signature, _chm_pmgl_marker, 4) != 0)
+        return 0;
+
+    return 1;
+}
+
+/* structure of PMGI headers */
+static const char _chm_pmgi_marker[4] = "PMGI";
+#define _CHM_PMGI_LEN (0x08)
+struct chmPmgiHeader
+{
+    char        signature[4];           /*  0 (PMGI) */
+    UInt32      free_space;             /*  4 */
+}; /* __attribute__ ((aligned (1))); */
+
+static int _unmarshal_pmgi_header(unsigned char **pData,
+                                  unsigned long *pDataLen,
+                                  struct chmPmgiHeader *dest)
+{
+    /* we only know how to deal with a 0x8 byte structures */
+    if (*pDataLen != _CHM_PMGI_LEN)
+        return 0;
+
+    /* unmarshal fields */
+    _unmarshal_char_array(pData, pDataLen,  dest->signature, 4);
+    _unmarshal_uint32    (pData, pDataLen, &dest->free_space);
+
+    /* check structure */
+    if (memcmp(dest->signature, _chm_pmgi_marker, 4) != 0)
+        return 0;
+
+    return 1;
+}
+
+/* structure of LZXC reset table */
+#define _CHM_LZXC_RESETTABLE_V1_LEN (0x28)
+struct chmLzxcResetTable
+{
+    UInt32      version;
+    UInt32      block_count;
+    UInt32      unknown;
+    UInt32      table_offset;
+    UInt64      uncompressed_len;
+    UInt64      compressed_len;
+    UInt64      block_len;     
+}; /* __attribute__ ((aligned (1))); */
+
+static int _unmarshal_lzxc_reset_table(unsigned char **pData,
+                                       unsigned long *pDataLen,
+                                       struct chmLzxcResetTable *dest)
+{
+    /* we only know how to deal with a 0x28 byte structures */
+    if (*pDataLen != _CHM_LZXC_RESETTABLE_V1_LEN)
+        return 0;
+
+    /* unmarshal fields */
+    _unmarshal_uint32    (pData, pDataLen, &dest->version);
+    _unmarshal_uint32    (pData, pDataLen, &dest->block_count);
+    _unmarshal_uint32    (pData, pDataLen, &dest->unknown);
+    _unmarshal_uint32    (pData, pDataLen, &dest->table_offset);
+    _unmarshal_uint64    (pData, pDataLen, &dest->uncompressed_len);
+    _unmarshal_uint64    (pData, pDataLen, &dest->compressed_len);
+    _unmarshal_uint64    (pData, pDataLen, &dest->block_len);
+
+    /* check structure */
+    if (dest->version != 2)
+        return 0;
+
+    return 1;
+}
+
+/* structure of LZXC control data block */
+#define _CHM_LZXC_MIN_LEN (0x18)
+#define _CHM_LZXC_V2_LEN (0x1c)
+struct chmLzxcControlData
+{
+    UInt32      size;                   /*  0        */
+    char        signature[4];           /*  4 (LZXC) */
+    UInt32      version;                /*  8        */
+    UInt32      resetInterval;          /*  c        */
+    UInt32      windowSize;             /* 10        */
+    UInt32      windowsPerReset;        /* 14        */
+    UInt32      unknown_18;             /* 18        */
+};
+
+static int _unmarshal_lzxc_control_data(unsigned char **pData,
+                                        unsigned long *pDataLen,
+                                        struct chmLzxcControlData *dest)
+{
+    /* we want at least 0x18 bytes */
+    if (*pDataLen < _CHM_LZXC_MIN_LEN)
+        return 0;
+
+    /* unmarshal fields */
+    _unmarshal_uint32    (pData, pDataLen, &dest->size);
+    _unmarshal_char_array(pData, pDataLen,  dest->signature, 4);
+    _unmarshal_uint32    (pData, pDataLen, &dest->version);
+    _unmarshal_uint32    (pData, pDataLen, &dest->resetInterval);
+    _unmarshal_uint32    (pData, pDataLen, &dest->windowSize);
+    _unmarshal_uint32    (pData, pDataLen, &dest->windowsPerReset);
+
+    if (*pDataLen >= _CHM_LZXC_V2_LEN)
+        _unmarshal_uint32    (pData, pDataLen, &dest->unknown_18);
+    else
+        dest->unknown_18 = 0;
+
+    if (dest->version == 2)
+    {
+        dest->resetInterval *= 0x8000;
+        dest->windowSize *= 0x8000;
+    }
+    if (dest->windowSize == 0  ||  dest->resetInterval == 0)
+        return 0;
+
+    /* for now, only support resetInterval a multiple of windowSize/2 */
+    if (dest->windowSize == 1)
+        return 0;
+    if ((dest->resetInterval % (dest->windowSize/2)) != 0)
+        return 0;
+
+    /* check structure */
+    if (memcmp(dest->signature, "LZXC", 4) != 0)
+        return 0;
+
+    return 1;
+}
+
+/* the structure used for chm file handles */
+struct chmFile
+{
+    HANDLE              fd;
+
+    CRITICAL_SECTION    mutex;
+    CRITICAL_SECTION    lzx_mutex;
+    CRITICAL_SECTION    cache_mutex;
+
+    UInt64              dir_offset;
+    UInt64              dir_len;    
+    UInt64              data_offset;
+    Int32               index_root;
+    Int32               index_head;
+    UInt32              block_len;     
+
+    UInt64              span;
+    struct chmUnitInfo  rt_unit;
+    struct chmUnitInfo  cn_unit;
+    struct chmLzxcResetTable reset_table;
+
+    /* LZX control data */
+    int                 compression_enabled;
+    UInt32              window_size;
+    UInt32              reset_interval;
+    UInt32              reset_blkcount;
+
+    /* decompressor state */
+    struct LZXstate    *lzx_state;
+    int                 lzx_last_block;
+
+    /* cache for decompressed blocks */
+    UChar             **cache_blocks;
+    Int64              *cache_block_indices;
+    Int32               cache_num_blocks;
+};
+
+/*
+ * utility functions local to this module
+ */
+
+/* utility function to handle differences between {pread,read}(64)? */
+static Int64 _chm_fetch_bytes(struct chmFile *h,
+                              UChar *buf,
+                              UInt64 os,
+                              Int64 len)
+{
+    Int64 readLen=0;
+    if (h->fd  ==  CHM_NULL_FD)
+        return readLen;
+
+    CHM_ACQUIRE_LOCK(h->mutex);
+    /* NOTE: this might be better done with CreateFileMapping, et cetera... */
+    {
+        DWORD origOffsetLo=0, origOffsetHi=0;
+        DWORD offsetLo, offsetHi;
+        DWORD actualLen=0;
+
+        /* awkward Win32 Seek/Tell */
+        offsetLo = (unsigned long)(os & 0xffffffffL);
+        offsetHi = (unsigned long)((os >> 32) & 0xffffffffL);
+        origOffsetLo = SetFilePointer(h->fd, 0, &origOffsetHi, FILE_CURRENT);
+        offsetLo = SetFilePointer(h->fd, offsetLo, &offsetHi, FILE_BEGIN);
+
+        /* read the data */
+        if (ReadFile(h->fd,
+                     buf,
+                     (DWORD)len,
+                     &actualLen,
+                     NULL) == TRUE)
+            readLen = actualLen;
+        else
+            readLen = 0;
+
+        /* restore original position */
+        SetFilePointer(h->fd, origOffsetLo, &origOffsetHi, FILE_BEGIN);
+    }
+    CHM_RELEASE_LOCK(h->mutex);
+    return readLen;
+}
+
+/* open an ITS archive */
+struct chmFile *chm_openW(const WCHAR *filename)
+{
+    unsigned char               sbuffer[256];
+    unsigned long               sremain;
+    unsigned char              *sbufpos;
+    struct chmFile             *newHandle=NULL;
+    struct chmItsfHeader        itsfHeader;
+    struct chmItspHeader        itspHeader;
+#if 0
+    struct chmUnitInfo          uiSpan;
+#endif
+    struct chmUnitInfo          uiLzxc;
+    struct chmLzxcControlData   ctlData;
+
+    /* allocate handle */
+    newHandle = (struct chmFile *)malloc(sizeof(struct chmFile));
+    newHandle->fd = CHM_NULL_FD;
+    newHandle->lzx_state = NULL;
+    newHandle->cache_blocks = NULL;
+    newHandle->cache_block_indices = NULL;
+    newHandle->cache_num_blocks = 0;
+
+    /* open file */
+    if ((newHandle->fd=CreateFileW(filename,
+                                   GENERIC_READ,
+                                   0,
+                                   NULL,
+                                   OPEN_EXISTING,
+                                   FILE_ATTRIBUTE_NORMAL,
+                                   NULL)) == CHM_NULL_FD)
+    {
+        free(newHandle);
+        return NULL;
+    }
+
+    /* initialize mutexes, if needed */
+    InitializeCriticalSection(&newHandle->mutex);
+    InitializeCriticalSection(&newHandle->lzx_mutex);
+    InitializeCriticalSection(&newHandle->cache_mutex);
+
+    /* read and verify header */
+    sremain = _CHM_ITSF_V3_LEN;
+    sbufpos = sbuffer;
+    if (_chm_fetch_bytes(newHandle, sbuffer, (UInt64)0, sremain) != sremain    ||
+        !_unmarshal_itsf_header(&sbufpos, &sremain, &itsfHeader))
+    {
+        chm_close(newHandle);
+        return NULL;
+    }
+
+    /* stash important values from header */
+    newHandle->dir_offset  = itsfHeader.dir_offset;
+    newHandle->dir_len     = itsfHeader.dir_len;
+    newHandle->data_offset = itsfHeader.data_offset;
+
+    /* now, read and verify the directory header chunk */
+    sremain = _CHM_ITSP_V1_LEN;
+    sbufpos = sbuffer;
+    if (_chm_fetch_bytes(newHandle, sbuffer,
+                         (UInt64)itsfHeader.dir_offset, sremain) != sremain       ||
+        !_unmarshal_itsp_header(&sbufpos, &sremain, &itspHeader))
+    {
+        chm_close(newHandle);
+        return NULL;
+    }
+
+    /* grab essential information from ITSP header */
+    newHandle->dir_offset += itspHeader.header_len;
+    newHandle->dir_len    -= itspHeader.header_len;
+    newHandle->index_root  = itspHeader.index_root;
+    newHandle->index_head  = itspHeader.index_head;
+    newHandle->block_len   = itspHeader.block_len;
+
+    /* if the index root is -1, this means we don't have any PMGI blocks.
+     * as a result, we must use the sole PMGL block as the index root
+     */
+    if (newHandle->index_root == -1)
+        newHandle->index_root = newHandle->index_head;
+
+    /* By default, compression is enabled. */
+    newHandle->compression_enabled = 1;
+
+/* Jed, Sun Jun 27: 'span' doesn't seem to be used anywhere?! */
+#if 0
+    /* fetch span */
+    if (CHM_RESOLVE_SUCCESS != chm_resolve_object(newHandle,
+                                                  _CHMU_SPANINFO,
+                                                  &uiSpan)                ||
+        uiSpan.space == CHM_COMPRESSED)
+    {
+        chm_close(newHandle);
+        return NULL;
+    }
+
+    /* N.B.: we've already checked that uiSpan is in the uncompressed section,
+     *       so this should not require attempting to decompress, which may
+     *       rely on having a valid "span"
+     */
+    sremain = 8;
+    sbufpos = sbuffer;
+    if (chm_retrieve_object(newHandle, &uiSpan, sbuffer,
+                            0, sremain) != sremain                        ||
+        !_unmarshal_uint64(&sbufpos, &sremain, &newHandle->span))
+    {
+        chm_close(newHandle);
+        return NULL;
+    }
+#endif
+
+    /* prefetch most commonly needed unit infos */
+    if (CHM_RESOLVE_SUCCESS != chm_resolve_object(newHandle,
+                                                  _CHMU_RESET_TABLE,
+                                                  &newHandle->rt_unit)    ||
+        newHandle->rt_unit.space == CHM_COMPRESSED                        ||
+        CHM_RESOLVE_SUCCESS != chm_resolve_object(newHandle,
+                                                  _CHMU_CONTENT,
+                                                  &newHandle->cn_unit)    ||
+        newHandle->cn_unit.space == CHM_COMPRESSED                        ||
+        CHM_RESOLVE_SUCCESS != chm_resolve_object(newHandle,
+                                                  _CHMU_LZXC_CONTROLDATA,
+                                                  &uiLzxc)                ||
+        uiLzxc.space == CHM_COMPRESSED)
+    {
+        newHandle->compression_enabled = 0;
+    }
+
+    /* read reset table info */
+    if (newHandle->compression_enabled)
+    {
+        sremain = _CHM_LZXC_RESETTABLE_V1_LEN;
+        sbufpos = sbuffer;
+        if (chm_retrieve_object(newHandle, &newHandle->rt_unit, sbuffer,
+                                0, sremain) != sremain                        ||
+            !_unmarshal_lzxc_reset_table(&sbufpos, &sremain,
+                                         &newHandle->reset_table))
+        {
+            newHandle->compression_enabled = 0;
+        }
+    }
+
+    /* read control data */
+    if (newHandle->compression_enabled)
+    {
+        sremain = (unsigned long)uiLzxc.length;
+        sbufpos = sbuffer;
+        if (chm_retrieve_object(newHandle, &uiLzxc, sbuffer,
+                                0, sremain) != sremain                       ||
+            !_unmarshal_lzxc_control_data(&sbufpos, &sremain,
+                                          &ctlData))
+        {
+            newHandle->compression_enabled = 0;
+        }
+
+        newHandle->window_size = ctlData.windowSize;
+        newHandle->reset_interval = ctlData.resetInterval;
+
+/* Jed, Mon Jun 28: Experimentally, it appears that the reset block count */
+/*       must be multiplied by this formerly unknown ctrl data field in   */
+/*       order to decompress some files.                                  */
+#if 0
+        newHandle->reset_blkcount = newHandle->reset_interval /
+                    (newHandle->window_size / 2);
+#else
+        newHandle->reset_blkcount = newHandle->reset_interval    /
+                                    (newHandle->window_size / 2) *
+                                    ctlData.windowsPerReset;
+#endif
+    }
+
+    /* initialize cache */
+    chm_set_param(newHandle, CHM_PARAM_MAX_BLOCKS_CACHED,
+                  CHM_MAX_BLOCKS_CACHED);
+
+    return newHandle;
+}
+
+/* close an ITS archive */
+void chm_close(struct chmFile *h)
+{
+    if (h != NULL)
+    {
+        if (h->fd != CHM_NULL_FD)
+            CHM_CLOSE_FILE(h->fd);
+        h->fd = CHM_NULL_FD;
+
+        DeleteCriticalSection(&h->mutex);
+        DeleteCriticalSection(&h->lzx_mutex);
+        DeleteCriticalSection(&h->cache_mutex);
+
+        if (h->lzx_state)
+            LZXteardown(h->lzx_state);
+        h->lzx_state = NULL;
+
+        if (h->cache_blocks)
+        {
+            int i;
+            for (i=0; i<h->cache_num_blocks; i++)
+            {
+                if (h->cache_blocks[i])
+                    free(h->cache_blocks[i]);
+            }
+            free(h->cache_blocks);
+            h->cache_blocks = NULL;
+        }
+
+        if (h->cache_block_indices)
+            free(h->cache_block_indices);
+        h->cache_block_indices = NULL;
+
+        free(h);
+    }
+}
+
+/*
+ * set a parameter on the file handle.
+ * valid parameter types:
+ *          CHM_PARAM_MAX_BLOCKS_CACHED:
+ *                 how many decompressed blocks should be cached?  A simple
+ *                 caching scheme is used, wherein the index of the block is
+ *                 used as a hash value, and hash collision results in the
+ *                 invalidation of the previously cached block.
+ */
+void chm_set_param(struct chmFile *h,
+                   int paramType,
+                   int paramVal)
+{
+    switch (paramType)
+    {
+        case CHM_PARAM_MAX_BLOCKS_CACHED:
+            CHM_ACQUIRE_LOCK(h->cache_mutex);
+            if (paramVal != h->cache_num_blocks)
+            {
+                UChar **newBlocks;
+                UInt64 *newIndices;
+                int     i;
+
+                /* allocate new cached blocks */
+                newBlocks = (UChar **)malloc(paramVal * sizeof (UChar *));
+                newIndices = (UInt64 *)malloc(paramVal * sizeof (UInt64));
+                for (i=0; i<paramVal; i++)
+                {
+                    newBlocks[i] = NULL;
+                    newIndices[i] = 0;
+                }
+
+                /* re-distribute old cached blocks */
+                if (h->cache_blocks)
+                {
+                    for (i=0; i<h->cache_num_blocks; i++)
+                    {
+                        int newSlot = (int)(h->cache_block_indices[i] % paramVal);
+
+                        if (h->cache_blocks[i])
+                        {
+                            /* in case of collision, destroy newcomer */
+                            if (newBlocks[newSlot])
+                            {
+                                free(h->cache_blocks[i]);
+                                h->cache_blocks[i] = NULL;
+                            }
+                            else
+                            {
+                                newBlocks[newSlot] = h->cache_blocks[i];
+                                newIndices[newSlot] =
+                                            h->cache_block_indices[i];
+                            }
+                        }
+                    }
+
+                    free(h->cache_blocks);
+                    free(h->cache_block_indices);
+                }
+
+                /* now, set new values */
+                h->cache_blocks = newBlocks;
+                h->cache_block_indices = newIndices;
+                h->cache_num_blocks = paramVal;
+            }
+            CHM_RELEASE_LOCK(h->cache_mutex);
+            break;
+
+        default:
+            break;
+    }
+}
+
+/*
+ * helper methods for chm_resolve_object
+ */
+
+/* skip a compressed dword */
+static void _chm_skip_cword(UChar **pEntry)
+{
+    while (*(*pEntry)++ >= 0x80)
+        ;
+}
+
+/* skip the data from a PMGL entry */
+static void _chm_skip_PMGL_entry_data(UChar **pEntry)
+{
+    _chm_skip_cword(pEntry);
+    _chm_skip_cword(pEntry);
+    _chm_skip_cword(pEntry);
+}
+
+/* parse a compressed dword */
+static UInt64 _chm_parse_cword(UChar **pEntry)
+{
+    UInt64 accum = 0;
+    UChar temp;
+    while ((temp=*(*pEntry)++) >= 0x80)
+    {
+        accum <<= 7;
+        accum += temp & 0x7f;
+    }
+
+    return (accum << 7) + temp;
+}
+
+/* parse a utf-8 string into an ASCII char buffer */
+static int _chm_parse_UTF8(UChar **pEntry, UInt64 count, WCHAR *path)
+{
+    /* MJM - Modified to return real Unicode strings */ 
+    while (count != 0)
+    {
+        *path++ = (*(*pEntry)++);
+        --count;
+    }
+
+    *path = '\0';
+    return 1;
+}
+
+/* parse a PMGL entry into a chmUnitInfo struct; return 1 on success. */
+static int _chm_parse_PMGL_entry(UChar **pEntry, struct chmUnitInfo *ui)
+{
+    UInt64 strLen;
+
+    /* parse str len */
+    strLen = _chm_parse_cword(pEntry);
+    if (strLen > CHM_MAX_PATHLEN)
+        return 0;
+
+    /* parse path */
+    if (! _chm_parse_UTF8(pEntry, strLen, ui->path))
+        return 0;
+
+    /* parse info */
+    ui->space  = (int)_chm_parse_cword(pEntry);
+    ui->start  = _chm_parse_cword(pEntry);
+    ui->length = _chm_parse_cword(pEntry);
+    return 1;
+}
+
+/* find an exact entry in PMGL; return NULL if we fail */
+static UChar *_chm_find_in_PMGL(UChar *page_buf,
+                         UInt32 block_len,
+                         const WCHAR *objPath)
+{
+    /* XXX: modify this to do a binary search using the nice index structure
+     *      that is provided for us.
+     */
+    struct chmPmglHeader header;
+    UInt32 hremain;
+    UChar *end;
+    UChar *cur;
+    UChar *temp;
+    UInt64 strLen;
+    WCHAR buffer[CHM_MAX_PATHLEN+1];
+
+    /* figure out where to start and end */
+    cur = page_buf;
+    hremain = _CHM_PMGL_LEN;
+    if (! _unmarshal_pmgl_header(&cur, &hremain, &header))
+        return NULL;
+    end = page_buf + block_len - (header.free_space);
+
+    /* now, scan progressively */
+    while (cur < end)
+    {
+        /* grab the name */
+        temp = cur;
+        strLen = _chm_parse_cword(&cur);
+        if (! _chm_parse_UTF8(&cur, strLen, buffer))
+            return NULL;
+
+        /* check if it is the right name */
+        if (! strcmpiW(buffer, objPath))
+            return temp;
+
+        _chm_skip_PMGL_entry_data(&cur);
+    }
+
+    return NULL;
+}
+
+/* find which block should be searched next for the entry; -1 if no block */
+static Int32 _chm_find_in_PMGI(UChar *page_buf,
+                        UInt32 block_len,
+                        const WCHAR *objPath)
+{
+    /* XXX: modify this to do a binary search using the nice index structure
+     *      that is provided for us
+     */
+    struct chmPmgiHeader header;
+    UInt32 hremain;
+    int page=-1;
+    UChar *end;
+    UChar *cur;
+    UInt64 strLen;
+    WCHAR buffer[CHM_MAX_PATHLEN+1];
+
+    /* figure out where to start and end */
+    cur = page_buf;
+    hremain = _CHM_PMGI_LEN;
+    if (! _unmarshal_pmgi_header(&cur, &hremain, &header))
+        return -1;
+    end = page_buf + block_len - (header.free_space);
+
+    /* now, scan progressively */
+    while (cur < end)
+    {
+        /* grab the name */
+        strLen = _chm_parse_cword(&cur);
+        if (! _chm_parse_UTF8(&cur, strLen, buffer))
+            return -1;
+
+        /* check if it is the right name */
+        if (strcmpiW(buffer, objPath) > 0)
+            return page;
+
+        /* load next value for path */
+        page = (int)_chm_parse_cword(&cur);
+    }
+
+    return page;
+}
+
+/* resolve a particular object from the archive */
+int chm_resolve_object(struct chmFile *h,
+                       const WCHAR *objPath,
+                       struct chmUnitInfo *ui)
+{
+    /*
+     * XXX: implement caching scheme for dir pages
+     */
+
+    Int32 curPage;
+
+    /* buffer to hold whatever page we're looking at */
+    UChar *page_buf = HeapAlloc(GetProcessHeap(), 0, h->block_len);
+
+    /* starting page */
+    curPage = h->index_root;
+
+    /* until we have either returned or given up */
+    while (curPage != -1)
+    {
+
+        /* try to fetch the index page */
+        if (_chm_fetch_bytes(h, page_buf,
+                             (UInt64)h->dir_offset + (UInt64)curPage*h->block_len,
+                             h->block_len) != h->block_len)
+	{
+	    HeapFree(GetProcessHeap(), 0, page_buf);
+            return CHM_RESOLVE_FAILURE;
+	}
+
+        /* now, if it is a leaf node: */
+        if (memcmp(page_buf, _chm_pmgl_marker, 4) == 0)
+        {
+            /* scan block */
+            UChar *pEntry = _chm_find_in_PMGL(page_buf,
+                                              h->block_len,
+                                              objPath);
+            if (pEntry == NULL)
+            {
+	        HeapFree(GetProcessHeap(), 0, page_buf);
+                return CHM_RESOLVE_FAILURE;
+            }
+
+            /* parse entry and return */
+            _chm_parse_PMGL_entry(&pEntry, ui);
+	    HeapFree(GetProcessHeap(), 0, page_buf);
+            return CHM_RESOLVE_SUCCESS;
+        }
+
+        /* else, if it is a branch node: */
+        else if (memcmp(page_buf, _chm_pmgi_marker, 4) == 0)
+            curPage = _chm_find_in_PMGI(page_buf, h->block_len, objPath);
+
+        /* else, we are confused.  give up. */
+        else
+        {
+	    HeapFree(GetProcessHeap(), 0, page_buf);
+            return CHM_RESOLVE_FAILURE;
+        }
+    }
+
+    /* didn't find anything.  fail. */
+    HeapFree(GetProcessHeap(), 0, page_buf);
+    return CHM_RESOLVE_FAILURE;
+}
+
+/*
+ * utility methods for dealing with compressed data
+ */
+
+/* get the bounds of a compressed block.  return 0 on failure */
+static int _chm_get_cmpblock_bounds(struct chmFile *h,
+                             UInt64 block,
+                             UInt64 *start,
+                             Int64 *len)
+{
+    UChar buffer[8], *dummy;
+    UInt32 remain;
+
+    /* for all but the last block, use the reset table */
+    if (block < h->reset_table.block_count-1)
+    {
+        /* unpack the start address */
+        dummy = buffer;
+        remain = 8;
+        if (_chm_fetch_bytes(h, buffer,
+                             (UInt64)h->data_offset
+                                + (UInt64)h->rt_unit.start
+                                + (UInt64)h->reset_table.table_offset
+                                + (UInt64)block*8,
+                             remain) != remain                            ||
+            !_unmarshal_uint64(&dummy, &remain, start))
+            return 0;
+
+        /* unpack the end address */
+        dummy = buffer;
+        remain = 8;
+        if (_chm_fetch_bytes(h, buffer,
+                         (UInt64)h->data_offset
+                                + (UInt64)h->rt_unit.start
+                                + (UInt64)h->reset_table.table_offset
+                                + (UInt64)block*8 + 8,
+                         remain) != remain                                ||
+            !_unmarshal_int64(&dummy, &remain, len))
+            return 0;
+    }
+
+    /* for the last block, use the span in addition to the reset table */
+    else
+    {
+        /* unpack the start address */
+        dummy = buffer;
+        remain = 8;
+        if (_chm_fetch_bytes(h, buffer,
+                             (UInt64)h->data_offset
+                                + (UInt64)h->rt_unit.start
+                                + (UInt64)h->reset_table.table_offset
+                                + (UInt64)block*8,
+                             remain) != remain                            ||
+            !_unmarshal_uint64(&dummy, &remain, start))
+            return 0;
+
+        *len = h->reset_table.compressed_len;
+    }
+
+    /* compute the length and absolute start address */
+    *len -= *start;
+    *start += h->data_offset + h->cn_unit.start;
+
+    return 1;
+}
+
+/* decompress the block.  must have lzx_mutex. */
+static Int64 _chm_decompress_block(struct chmFile *h,
+                                   UInt64 block,
+                                   UChar **ubuffer)
+{
+    UChar *cbuffer = HeapAlloc( GetProcessHeap(), 0,
+                              ((unsigned int)h->reset_table.block_len + 6144));
+    UInt64 cmpStart;                                    /* compressed start  */
+    Int64 cmpLen;                                       /* compressed len    */
+    int indexSlot;                                      /* cache index slot  */
+    UChar *lbuffer;                                     /* local buffer ptr  */
+    UInt32 blockAlign = (UInt32)(block % h->reset_blkcount); /* reset intvl. aln. */
+    UInt32 i;                                           /* local loop index  */
+
+    /* let the caching system pull its weight! */
+    if (block - blockAlign <= h->lzx_last_block  &&
+        block              >= h->lzx_last_block)
+        blockAlign = (block - h->lzx_last_block);
+
+    /* check if we need previous blocks */
+    if (blockAlign != 0)
+    {
+        /* fetch all required previous blocks since last reset */
+        for (i = blockAlign; i > 0; i--)
+        {
+            UInt32 curBlockIdx = block - i;
+
+            /* check if we most recently decompressed the previous block */
+            if (h->lzx_last_block != curBlockIdx)
+            {
+                if ((curBlockIdx % h->reset_blkcount) == 0)
+                {
+#ifdef CHM_DEBUG
+                    fprintf(stderr, "***RESET (1)***\n");
+#endif
+                    LZXreset(h->lzx_state);
+                }
+
+                indexSlot = (int)((curBlockIdx) % h->cache_num_blocks);
+                h->cache_block_indices[indexSlot] = curBlockIdx;
+                if (! h->cache_blocks[indexSlot])
+                    h->cache_blocks[indexSlot] = (UChar *)malloc(
+                                                                 (unsigned int)(h->reset_table.block_len));
+                lbuffer = h->cache_blocks[indexSlot];
+
+                /* decompress the previous block */
+#ifdef CHM_DEBUG
+                fprintf(stderr, "Decompressing block #%4d (EXTRA)\n", curBlockIdx);
+#endif
+                if (!_chm_get_cmpblock_bounds(h, curBlockIdx, &cmpStart, &cmpLen) ||
+                    _chm_fetch_bytes(h, cbuffer, cmpStart, cmpLen) != cmpLen      ||
+                    LZXdecompress(h->lzx_state, cbuffer, lbuffer, (int)cmpLen,
+                                  (int)h->reset_table.block_len) != DECR_OK)
+                {
+#ifdef CHM_DEBUG
+                    fprintf(stderr, "   (DECOMPRESS FAILED!)\n");
+#endif
+                    HeapFree(GetProcessHeap(), 0, cbuffer);
+                    return (Int64)0;
+                }
+
+                h->lzx_last_block = (int)curBlockIdx;
+            }
+        }
+    }
+    else
+    {
+        if ((block % h->reset_blkcount) == 0)
+        {
+#ifdef CHM_DEBUG
+            fprintf(stderr, "***RESET (2)***\n");
+#endif
+            LZXreset(h->lzx_state);
+        }
+    }
+
+    /* allocate slot in cache */
+    indexSlot = (int)(block % h->cache_num_blocks);
+    h->cache_block_indices[indexSlot] = block;
+    if (! h->cache_blocks[indexSlot])
+        h->cache_blocks[indexSlot] = (UChar *)malloc(
+                                          ((unsigned int)h->reset_table.block_len));
+    lbuffer = h->cache_blocks[indexSlot];
+    *ubuffer = lbuffer;
+
+    /* decompress the block we actually want */
+#ifdef CHM_DEBUG
+    fprintf(stderr, "Decompressing block #%4d (REAL )\n", block);
+#endif
+    if (! _chm_get_cmpblock_bounds(h, block, &cmpStart, &cmpLen)          ||
+        _chm_fetch_bytes(h, cbuffer, cmpStart, cmpLen) != cmpLen          ||
+        LZXdecompress(h->lzx_state, cbuffer, lbuffer, (int)cmpLen,
+                      (int)h->reset_table.block_len) != DECR_OK)
+    {
+#ifdef CHM_DEBUG
+        fprintf(stderr, "   (DECOMPRESS FAILED!)\n");
+#endif
+        HeapFree(GetProcessHeap(), 0, cbuffer);
+        return (Int64)0;
+    }
+    h->lzx_last_block = (int)block;
+
+    /* XXX: modify LZX routines to return the length of the data they
+     * decompressed and return that instead, for an extra sanity check.
+     */
+    HeapFree(GetProcessHeap(), 0, cbuffer);
+    return h->reset_table.block_len;
+}
+
+/* grab a region from a compressed block */
+static Int64 _chm_decompress_region(struct chmFile *h,
+                                    UChar *buf,
+                                    UInt64 start,
+                                    Int64 len)
+{
+    UInt64 nBlock, nOffset;
+    UInt64 nLen;
+    UInt64 gotLen;
+    UChar *ubuffer;
+
+        if (len <= 0)
+                return (Int64)0;
+
+    /* figure out what we need to read */
+    nBlock = start / h->reset_table.block_len;
+    nOffset = start % h->reset_table.block_len;
+    nLen = len;
+    if (nLen > (h->reset_table.block_len - nOffset))
+        nLen = h->reset_table.block_len - nOffset;
+
+    /* if block is cached, return data from it. */
+    CHM_ACQUIRE_LOCK(h->lzx_mutex);
+    CHM_ACQUIRE_LOCK(h->cache_mutex);
+    if (h->cache_block_indices[nBlock % h->cache_num_blocks] == nBlock    &&
+        h->cache_blocks[nBlock % h->cache_num_blocks] != NULL)
+    {
+        memcpy(buf,
+               h->cache_blocks[nBlock % h->cache_num_blocks] + nOffset,
+               (unsigned int)nLen);
+        CHM_RELEASE_LOCK(h->cache_mutex);
+        CHM_RELEASE_LOCK(h->lzx_mutex);
+        return nLen;
+    }
+    CHM_RELEASE_LOCK(h->cache_mutex);
+
+    /* data request not satisfied, so... start up the decompressor machine */
+    if (! h->lzx_state)
+    {
+        int window_size = ffs(h->window_size) - 1;
+        h->lzx_last_block = -1;
+        h->lzx_state = LZXinit(window_size);
+    }
+
+    /* decompress some data */
+    gotLen = _chm_decompress_block(h, nBlock, &ubuffer);
+    if (gotLen < nLen)
+        nLen = gotLen;
+    memcpy(buf, ubuffer+nOffset, (unsigned int)nLen);
+    CHM_RELEASE_LOCK(h->lzx_mutex);
+    return nLen;
+}
+
+/* retrieve (part of) an object */
+LONGINT64 chm_retrieve_object(struct chmFile *h,
+                               struct chmUnitInfo *ui,
+                               unsigned char *buf,
+                               LONGUINT64 addr,
+                               LONGINT64 len)
+{
+    /* must be valid file handle */
+    if (h == NULL)
+        return (Int64)0;
+
+    /* starting address must be in correct range */
+    if (addr < 0  ||  addr >= ui->length)
+        return (Int64)0;
+
+    /* clip length */
+    if (addr + len > ui->length)
+        len = ui->length - addr;
+
+    /* if the file is uncompressed, it's simple */
+    if (ui->space == CHM_UNCOMPRESSED)
+    {
+        /* read data */
+        return _chm_fetch_bytes(h,
+                                buf,
+                                (UInt64)h->data_offset + (UInt64)ui->start + (UInt64)addr,
+                                len);
+    }
+
+    /* else if the file is compressed, it's a little trickier */
+    else /* ui->space == CHM_COMPRESSED */
+    {
+        Int64 swath=0, total=0;
+
+        /* if compression is not enabled for this file... */
+        if (! h->compression_enabled)
+            return total;
+
+        do {
+
+            /* swill another mouthful */
+            swath = _chm_decompress_region(h, buf, ui->start + addr, len);
+
+            /* if we didn't get any... */
+            if (swath == 0)
+                return total;
+
+            /* update stats */
+            total += swath;
+            len -= swath;
+            addr += swath;
+            buf += swath;
+
+        } while (len != 0);
+
+        return total;
+    }
+}
+
+/* enumerate the objects in the .chm archive */
+int chm_enumerate(struct chmFile *h,
+                  int what,
+                  CHM_ENUMERATOR e,
+                  void *context)
+{
+    Int32 curPage;
+
+    /* buffer to hold whatever page we're looking at */
+    UChar *page_buf = HeapAlloc(GetProcessHeap(), 0, (unsigned int)h->block_len);
+    struct chmPmglHeader header;
+    UChar *end;
+    UChar *cur;
+    unsigned long lenRemain;
+    UInt64 ui_path_len;
+
+    /* the current ui */
+    struct chmUnitInfo ui;
+    int flag;
+
+    /* starting page */
+    curPage = h->index_head;
+
+    /* until we have either returned or given up */
+    while (curPage != -1)
+    {
+
+        /* try to fetch the index page */
+        if (_chm_fetch_bytes(h,
+                             page_buf,
+                             (UInt64)h->dir_offset + (UInt64)curPage*h->block_len,
+                             h->block_len) != h->block_len)
+        {
+            HeapFree(GetProcessHeap(), 0, page_buf);
+            return 0;
+        }
+
+        /* figure out start and end for this page */
+        cur = page_buf;
+        lenRemain = _CHM_PMGL_LEN;
+        if (! _unmarshal_pmgl_header(&cur, &lenRemain, &header))
+        {
+            HeapFree(GetProcessHeap(), 0, page_buf);
+            return 0;
+        }
+        end = page_buf + h->block_len - (header.free_space);
+
+        /* loop over this page */
+        while (cur < end)
+        {
+            if (! _chm_parse_PMGL_entry(&cur, &ui))
+            {
+                HeapFree(GetProcessHeap(), 0, page_buf);
+                return 0;
+            }
+
+            /* get the length of the path */
+            ui_path_len = strlenW(ui.path)-1;
+
+            /* check for DIRS */
+            if (ui.path[ui_path_len] == '/'  &&  !(what & CHM_ENUMERATE_DIRS))
+                continue;
+
+            /* check for FILES */
+            if (ui.path[ui_path_len] != '/'  &&  !(what & CHM_ENUMERATE_FILES))
+                continue;
+
+            /* check for NORMAL vs. META */
+            if (ui.path[0] == '/')
+            {
+
+                /* check for NORMAL vs. SPECIAL */
+                if (ui.path[1] == '#'  ||  ui.path[1] == '$')
+                    flag = CHM_ENUMERATE_SPECIAL;
+                else
+                    flag = CHM_ENUMERATE_NORMAL;
+            }
+            else
+                flag = CHM_ENUMERATE_META;
+            if (! (what & flag))
+                continue;
+
+            /* call the enumerator */
+            {
+                int status = (*e)(h, &ui, context);
+                switch (status)
+                {
+                    case CHM_ENUMERATOR_FAILURE:
+                        HeapFree(GetProcessHeap(), 0, page_buf);
+                        return 0;
+                    case CHM_ENUMERATOR_CONTINUE:
+                        break;
+                    case CHM_ENUMERATOR_SUCCESS:
+                        HeapFree(GetProcessHeap(), 0, page_buf);
+                        return 1;
+                    default:
+                        break;
+                }
+            }
+        }
+
+        /* advance to next page */
+        curPage = header.block_next;
+    }
+
+    HeapFree(GetProcessHeap(), 0, page_buf);
+    return 1;
+}
+
+int chm_enumerate_dir(struct chmFile *h,
+                      const WCHAR *prefix,
+                      int what,
+                      CHM_ENUMERATOR e,
+                      void *context)
+{
+    /*
+     * XXX: do this efficiently (i.e. using the tree index)
+     */
+
+    Int32 curPage;
+
+    /* buffer to hold whatever page we're looking at */
+    UChar *page_buf = HeapAlloc(GetProcessHeap(), 0, (unsigned int)h->block_len);
+    struct chmPmglHeader header;
+    UChar *end;
+    UChar *cur;
+    unsigned long lenRemain;
+
+    /* set to 1 once we've started */
+    int it_has_begun=0;
+
+    /* the current ui */
+    struct chmUnitInfo ui;
+    int flag;
+    UInt64 ui_path_len;
+
+    /* the length of the prefix */
+    WCHAR prefixRectified[CHM_MAX_PATHLEN+1];
+    int prefixLen;
+    WCHAR lastPath[CHM_MAX_PATHLEN];
+    int lastPathLen;
+
+    /* starting page */
+    curPage = h->index_head;
+
+    /* initialize pathname state */
+    strncpyW(prefixRectified, prefix, CHM_MAX_PATHLEN);
+    prefixLen = strlenW(prefixRectified);
+    if (prefixLen != 0)
+    {
+        if (prefixRectified[prefixLen-1] != '/')
+        {
+            prefixRectified[prefixLen] = '/';
+            prefixRectified[prefixLen+1] = '\0';
+            ++prefixLen;
+        }
+    }
+    lastPath[0] = '\0';
+    lastPathLen = -1;
+
+    /* until we have either returned or given up */
+    while (curPage != -1)
+    {
+
+        /* try to fetch the index page */
+        if (_chm_fetch_bytes(h,
+                             page_buf,
+                             (UInt64)h->dir_offset + (UInt64)curPage*h->block_len,
+                             h->block_len) != h->block_len)
+        {
+            HeapFree(GetProcessHeap(), 0, page_buf);
+            return 0;
+        }
+
+        /* figure out start and end for this page */
+        cur = page_buf;
+        lenRemain = _CHM_PMGL_LEN;
+        if (! _unmarshal_pmgl_header(&cur, &lenRemain, &header))
+        {
+            HeapFree(GetProcessHeap(), 0, page_buf);
+            return 0;
+        }
+        end = page_buf + h->block_len - (header.free_space);
+
+        /* loop over this page */
+        while (cur < end)
+        {
+            if (! _chm_parse_PMGL_entry(&cur, &ui))
+            {
+                HeapFree(GetProcessHeap(), 0, page_buf);
+                return 0;
+            }
+
+            /* check if we should start */
+            if (! it_has_begun)
+            {
+                if (ui.length == 0  &&  strncmpiW(ui.path, prefixRectified, prefixLen) == 0)
+                    it_has_begun = 1;
+                else
+                    continue;
+
+                if (ui.path[prefixLen] == '\0')
+                    continue;
+            }
+
+            /* check if we should stop */
+            else
+            {
+                if (strncmpiW(ui.path, prefixRectified, prefixLen) != 0)
+                {
+                    HeapFree(GetProcessHeap(), 0, page_buf);
+                    return 1;
+                }
+            }
+
+            /* check if we should include this path */
+            if (lastPathLen != -1)
+            {
+                if (strncmpiW(ui.path, lastPath, lastPathLen) == 0)
+                    continue;
+            }
+            strcpyW(lastPath, ui.path);
+            lastPathLen = strlenW(lastPath);
+
+            /* get the length of the path */
+            ui_path_len = strlenW(ui.path)-1;
+
+            /* check for DIRS */
+            if (ui.path[ui_path_len] == '/'  &&  !(what & CHM_ENUMERATE_DIRS))
+                continue;
+
+            /* check for FILES */
+            if (ui.path[ui_path_len] != '/'  &&  !(what & CHM_ENUMERATE_FILES))
+                continue;
+
+            /* check for NORMAL vs. META */
+            if (ui.path[0] == '/')
+            {
+
+                /* check for NORMAL vs. SPECIAL */
+                if (ui.path[1] == '#'  ||  ui.path[1] == '$')
+                    flag = CHM_ENUMERATE_SPECIAL;
+                else
+                    flag = CHM_ENUMERATE_NORMAL;
+            }
+            else
+                flag = CHM_ENUMERATE_META;
+            if (! (what & flag))
+                continue;
+
+            /* call the enumerator */
+            {
+                int status = (*e)(h, &ui, context);
+                switch (status)
+                {
+                    case CHM_ENUMERATOR_FAILURE:
+                        HeapFree(GetProcessHeap(), 0, page_buf);
+                        return 0;
+                    case CHM_ENUMERATOR_CONTINUE:
+                        break;
+                    case CHM_ENUMERATOR_SUCCESS:
+                        HeapFree(GetProcessHeap(), 0, page_buf);
+                        return 1;
+                    default:
+                        break;
+                }
+            }
+        }
+
+        /* advance to next page */
+        curPage = header.block_next;
+    }
+
+    HeapFree(GetProcessHeap(), 0, page_buf);
+    return 1;
+}
--- /dev/null	1994-07-18 08:46:18.000000000 +0900
+++ dlls/itss/chm_lib.h	2004-08-24 15:45:11.000000000 +0900
@@ -0,0 +1,120 @@
+/* $Id: chm_lib.h,v 1.1 2004/08/23 07:58:20 mike Exp $ */
+/***************************************************************************
+ *             chm_lib.h - CHM archive manipulation routines               *
+ *                           -------------------                           *
+ *                                                                         *
+ *  author:     Jed Wing <jedwin at ugcs.caltech.edu>                         *
+ *  version:    0.3                                                        *
+ *  notes:      These routines are meant for the manipulation of microsoft *
+ *              .chm (compiled html help) files, but may likely be used    *
+ *              for the manipulation of any ITSS archive, if ever ITSS     *
+ *              archives are used for any other purpose.                   *
+ *                                                                         *
+ *              Note also that the section names are statically handled.   *
+ *              To be entirely correct, the section names should be read   *
+ *              from the section names meta-file, and then the various     *
+ *              content sections and the "transforms" to apply to the data *
+ *              they contain should be inferred from the section name and  *
+ *              the meta-files referenced using that name; however, all of *
+ *              the files I've been able to get my hands on appear to have *
+ *              only two sections: Uncompressed and MSCompressed.          *
+ *              Additionally, the ITSS.DLL file included with Windows does *
+ *              not appear to handle any different transforms than the     *
+ *              simple LZX-transform.  Furthermore, the list of transforms *
+ *              to apply is broken, in that only half the required space   *
+ *              is allocated for the list.  (It appears as though the      *
+ *              space is allocated for ASCII strings, but the strings are  *
+ *              written as unicode.  As a result, only the first half of   *
+ *              the string appears.)  So this is probably not too big of   *
+ *              a deal, at least until CHM v4 (MS .lit files), which also  *
+ *              incorporate encryption, of some description.               *
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program 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.                       *
+ *                                                                         *
+ ***************************************************************************/
+
+#ifndef INCLUDED_CHMLIB_H
+#define INCLUDED_CHMLIB_H
+
+typedef ULONGLONG LONGUINT64;
+typedef LONGLONG  LONGINT64;
+
+/* the two available spaces in a CHM file                      */
+/* N.B.: The format supports arbitrarily many spaces, but only */
+/*       two appear to be used at present.                     */
+#define CHM_UNCOMPRESSED (0)
+#define CHM_COMPRESSED   (1)
+
+/* structure representing an ITS (CHM) file stream             */
+struct chmFile;
+
+/* structure representing an element from an ITS file stream   */
+#define CHM_MAX_PATHLEN  (256)
+struct chmUnitInfo
+{
+    LONGUINT64         start;
+    LONGUINT64         length;
+    int                space;
+    WCHAR              path[CHM_MAX_PATHLEN+1];
+};
+
+struct chmFile* chm_openW(const WCHAR *filename);
+
+/* close an ITS archive */
+void chm_close(struct chmFile *h);
+
+/* methods for ssetting tuning parameters for particular file */
+#define CHM_PARAM_MAX_BLOCKS_CACHED 0
+void chm_set_param(struct chmFile *h,
+                   int paramType,
+                   int paramVal);
+
+/* resolve a particular object from the archive */
+#define CHM_RESOLVE_SUCCESS (0)
+#define CHM_RESOLVE_FAILURE (1)
+int chm_resolve_object(struct chmFile *h,
+                       const WCHAR *objPath,
+                       struct chmUnitInfo *ui);
+
+/* retrieve part of an object from the archive */
+LONGINT64 chm_retrieve_object(struct chmFile *h,
+                              struct chmUnitInfo *ui,
+                              unsigned char *buf,
+                              LONGUINT64 addr,
+                              LONGINT64 len);
+
+/* enumerate the objects in the .chm archive */
+typedef int (*CHM_ENUMERATOR)(struct chmFile *h,
+                              struct chmUnitInfo *ui,
+                              void *context);
+#define CHM_ENUMERATE_NORMAL    (1)
+#define CHM_ENUMERATE_META      (2)
+#define CHM_ENUMERATE_SPECIAL   (4)
+#define CHM_ENUMERATE_FILES     (8)
+#define CHM_ENUMERATE_DIRS      (16)
+#define CHM_ENUMERATE_ALL       (31)
+#define CHM_ENUMERATOR_FAILURE  (0)
+#define CHM_ENUMERATOR_CONTINUE (1)
+#define CHM_ENUMERATOR_SUCCESS  (2)
+int chm_enumerate(struct chmFile *h,
+                  int what,
+                  CHM_ENUMERATOR e,
+                  void *context);
+
+int chm_enumerate_dir(struct chmFile *h,
+                      const WCHAR *prefix,
+                      int what,
+                      CHM_ENUMERATOR e,
+                      void *context);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* INCLUDED_CHMLIB_H */
--- /dev/null	1994-07-18 08:46:18.000000000 +0900
+++ dlls/itss/itss.c	2004-08-24 15:45:18.000000000 +0900
@@ -0,0 +1,409 @@
+/*
+ *    ITSS Class Factory
+ *
+ * Copyright 2002 Lionel Ulmer
+ * Copyright 2004 Mike McCormack
+ *
+ *  see http://bonedaddy.net/pabs3/hhm/#chmspec
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "config.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winuser.h"
+#include "winnls.h"
+#include "winreg.h"
+#include "ole2.h"
+
+#include "itss.h"
+#include "uuids.h"
+
+#include "wine/unicode.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(itss);
+
+#include "initguid.h"
+
+DEFINE_GUID(CLSID_ITStorage,0x5d02926a,0x212e,0x11d0,0x9d,0xf9,0x00,0xa0,0xc9,0x22,0xe6,0xec );
+DEFINE_GUID(CLSID_ITSProtocol,0x9d148290,0xb9c8,0x11d0,0xa4,0xcc,0x00,0x00,0xf8,0x01,0x49,0xf6);
+DEFINE_GUID(IID_IITStorage, 0x88cc31de, 0x27ab, 0x11d0, 0x9d, 0xf9, 0x0, 0xa0, 0xc9, 0x22, 0xe6, 0xec);
+
+static HRESULT ITSS_create(IUnknown *pUnkOuter, LPVOID *ppObj);
+extern HRESULT ITS_IParseDisplayName_create(IUnknown *pUnkOuter, LPVOID *ppObj);
+extern HRESULT ITSS_StgOpenStorage( const WCHAR*, IStorage*, DWORD, SNB, DWORD, IStorage** );
+
+BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpv)
+{
+    switch(fdwReason) {
+    case DLL_PROCESS_ATTACH:
+        DisableThreadLibraryCalls(hInstDLL);
+        break;
+    case DLL_PROCESS_DETACH:
+        break;
+    }
+    return TRUE;
+}
+
+/******************************************************************************
+ * ITSS ClassFactory
+ */
+typedef struct {
+    IClassFactory ITF_IClassFactory;
+
+    DWORD ref;
+    HRESULT (*pfnCreateInstance)(IUnknown *pUnkOuter, LPVOID *ppObj);
+} IClassFactoryImpl;
+
+struct object_creation_info
+{
+    const CLSID *clsid;
+    LPCSTR szClassName;
+    HRESULT (*pfnCreateInstance)(IUnknown *pUnkOuter, LPVOID *ppObj);
+};
+
+static const struct object_creation_info object_creation[] =
+{
+    { &CLSID_ITStorage, "ITStorage", ITSS_create },
+    { &CLSID_ITSProtocol, "ITSProtocol", ITS_IParseDisplayName_create },
+};
+
+static HRESULT WINAPI
+ITSSCF_QueryInterface(LPCLASSFACTORY iface,REFIID riid,LPVOID *ppobj)
+{
+    ICOM_THIS(IClassFactoryImpl,iface);
+
+    if (IsEqualGUID(riid, &IID_IUnknown)
+	|| IsEqualGUID(riid, &IID_IClassFactory))
+    {
+	IClassFactory_AddRef(iface);
+	*ppobj = This;
+	return S_OK;
+    }
+
+    WARN("(%p)->(%s,%p),not found\n",This,debugstr_guid(riid),ppobj);
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI ITSSCF_AddRef(LPCLASSFACTORY iface) {
+    ICOM_THIS(IClassFactoryImpl,iface);
+    return ++(This->ref);
+}
+
+static ULONG WINAPI ITSSCF_Release(LPCLASSFACTORY iface) {
+    ICOM_THIS(IClassFactoryImpl,iface);
+
+    ULONG ref = --This->ref;
+
+    if (ref == 0)
+	HeapFree(GetProcessHeap(), 0, This);
+
+    return ref;
+}
+
+
+static HRESULT WINAPI ITSSCF_CreateInstance(LPCLASSFACTORY iface, LPUNKNOWN pOuter,
+					  REFIID riid, LPVOID *ppobj) {
+    ICOM_THIS(IClassFactoryImpl,iface);
+    HRESULT hres;
+    LPUNKNOWN punk;
+    
+    TRACE("(%p)->(%p,%s,%p)\n",This,pOuter,debugstr_guid(riid),ppobj);
+
+    hres = This->pfnCreateInstance(pOuter, (LPVOID *) &punk);
+    if (FAILED(hres)) {
+        *ppobj = NULL;
+        return hres;
+    }
+    hres = IUnknown_QueryInterface(punk, riid, ppobj);
+    if (FAILED(hres)) {
+        *ppobj = NULL;
+	return hres;
+    }
+    IUnknown_Release(punk);
+    return hres;
+}
+
+static HRESULT WINAPI ITSSCF_LockServer(LPCLASSFACTORY iface,BOOL dolock) {
+    ICOM_THIS(IClassFactoryImpl,iface);
+    FIXME("(%p)->(%d),stub!\n",This,dolock);
+    return S_OK;
+}
+
+static IClassFactoryVtbl ITSSCF_Vtbl =
+{
+    ITSSCF_QueryInterface,
+    ITSSCF_AddRef,
+    ITSSCF_Release,
+    ITSSCF_CreateInstance,
+    ITSSCF_LockServer
+};
+
+
+HRESULT WINAPI ITSS_DllGetClassObject(REFCLSID rclsid, REFIID iid, LPVOID *ppv)
+{
+    int i;
+    IClassFactoryImpl *factory;
+
+    TRACE("%s %s %p\n",debugstr_guid(rclsid), debugstr_guid(iid), ppv);
+    
+    if ( !IsEqualGUID( &IID_IClassFactory, iid )
+	 && ! IsEqualGUID( &IID_IUnknown, iid) )
+	return E_NOINTERFACE;
+
+    for (i=0; i < sizeof(object_creation)/sizeof(object_creation[0]); i++)
+    {
+	if (IsEqualGUID(object_creation[i].clsid, rclsid))
+	    break;
+    }
+
+    if (i == sizeof(object_creation)/sizeof(object_creation[0]))
+    {
+	FIXME("%s: no class found.\n", debugstr_guid(rclsid));
+	return CLASS_E_CLASSNOTAVAILABLE;
+    }
+
+    TRACE("Creating a class factory for %s\n",object_creation[i].szClassName);
+
+    factory = HeapAlloc(GetProcessHeap(), 0, sizeof(*factory));
+    if (factory == NULL) return E_OUTOFMEMORY;
+
+    factory->ITF_IClassFactory.lpVtbl = &ITSSCF_Vtbl;
+    factory->ref = 1;
+
+    factory->pfnCreateInstance = object_creation[i].pfnCreateInstance;
+
+    *ppv = &(factory->ITF_IClassFactory);
+
+    TRACE("(%p) <- %p\n", ppv, &(factory->ITF_IClassFactory) );
+
+    return S_OK;
+}
+
+/*****************************************************************************/
+
+typedef struct {
+    IITStorageVtbl *vtbl_IITStorage;
+    DWORD ref;
+} ITStorageImpl;
+
+
+HRESULT WINAPI ITStorageImpl_QueryInterface(
+    IITStorage* iface,
+    REFIID riid,
+    void** ppvObject)
+{
+    ICOM_THIS(ITStorageImpl,iface);
+    if (IsEqualGUID(riid, &IID_IUnknown)
+	|| IsEqualGUID(riid, &IID_IITStorage))
+    {
+	IClassFactory_AddRef(iface);
+	*ppvObject = This;
+	return S_OK;
+    }
+
+    WARN("(%p)->(%s,%p),not found\n",This,debugstr_guid(riid),ppvObject);
+    return E_NOINTERFACE;
+}
+
+ULONG WINAPI ITStorageImpl_AddRef(
+    IITStorage* iface)
+{
+    ICOM_THIS(ITStorageImpl,iface);
+    TRACE("%p\n", This);
+    return ++(This->ref);
+}
+
+ULONG WINAPI ITStorageImpl_Release(
+    IITStorage* iface)
+{
+    ICOM_THIS(ITStorageImpl,iface);
+    ULONG ref = --This->ref;
+
+    if (ref == 0)
+	HeapFree(GetProcessHeap(), 0, This);
+
+    return ref;
+}
+
+HRESULT WINAPI ITStorageImpl_StgCreateDocfile(
+    IITStorage* iface,
+    const WCHAR* pwcsName,
+    DWORD grfMode,
+    DWORD reserved,
+    IStorage** ppstgOpen)
+{
+    ICOM_THIS(ITStorageImpl,iface);
+
+    TRACE("%p %s %lu %lu %p\n", This,
+          debugstr_w(pwcsName), grfMode, reserved, ppstgOpen );
+
+    return ITSS_StgOpenStorage( pwcsName, NULL, grfMode,
+                                0, reserved, ppstgOpen);
+}
+
+HRESULT WINAPI ITStorageImpl_StgCreateDocfileOnILockBytes(
+    IITStorage* iface,
+    ILockBytes* plkbyt,
+    DWORD grfMode,
+    DWORD reserved,
+    IStorage** ppstgOpen)
+{
+    ICOM_THIS(ITStorageImpl,iface);
+    FIXME("%p\n", This);
+    return E_NOTIMPL;
+}
+
+HRESULT WINAPI ITStorageImpl_StgIsStorageFile(
+    IITStorage* iface,
+    const WCHAR* pwcsName)
+{
+    ICOM_THIS(ITStorageImpl,iface);
+    FIXME("%p\n", This);
+    return E_NOTIMPL;
+}
+
+HRESULT WINAPI ITStorageImpl_StgIsStorageILockBytes(
+    IITStorage* iface,
+    ILockBytes* plkbyt)
+{
+    ICOM_THIS(ITStorageImpl,iface);
+    FIXME("%p\n", This);
+    return E_NOTIMPL;
+}
+
+HRESULT WINAPI ITStorageImpl_StgOpenStorage(
+    IITStorage* iface,
+    const WCHAR* pwcsName,
+    IStorage* pstgPriority,
+    DWORD grfMode,
+    SNB snbExclude,
+    DWORD reserved,
+    IStorage** ppstgOpen)
+{
+    ICOM_THIS(ITStorageImpl,iface);
+
+    TRACE("%p %s %p %ld %p\n", This, debugstr_w( pwcsName ),
+           pstgPriority, grfMode, snbExclude );
+
+    return ITSS_StgOpenStorage( pwcsName, pstgPriority, grfMode,
+                                snbExclude, reserved, ppstgOpen);
+}
+
+HRESULT WINAPI ITStorageImpl_StgOpenStorageOnILockBytes(
+    IITStorage* iface,
+    ILockBytes* plkbyt,
+    IStorage* pStgPriority,
+    DWORD grfMode,
+    SNB snbExclude,
+    DWORD reserved,
+    IStorage** ppstgOpen)
+{
+    ICOM_THIS(ITStorageImpl,iface);
+    FIXME("%p\n", This);
+    return E_NOTIMPL;
+}
+
+HRESULT WINAPI ITStorageImpl_StgSetTimes(
+    IITStorage* iface,
+    WCHAR* lpszName,
+    FILETIME* pctime,
+    FILETIME* patime,
+    FILETIME* pmtime)
+{
+    ICOM_THIS(ITStorageImpl,iface);
+    FIXME("%p\n", This);
+    return E_NOTIMPL;
+}
+
+HRESULT WINAPI ITStorageImpl_SetControlData(
+    IITStorage* iface,
+    PITS_Control_Data pControlData)
+{
+    ICOM_THIS(ITStorageImpl,iface);
+    FIXME("%p\n", This);
+    return E_NOTIMPL;
+}
+
+HRESULT WINAPI ITStorageImpl_DefaultControlData(
+    IITStorage* iface,
+    PITS_Control_Data* ppControlData)
+{
+    ICOM_THIS(ITStorageImpl,iface);
+    FIXME("%p\n", This);
+    return E_NOTIMPL;
+}
+
+HRESULT WINAPI ITStorageImpl_Compact(
+    IITStorage* iface,
+    const WCHAR* pwcsName,
+    ECompactionLev iLev)
+{
+    ICOM_THIS(ITStorageImpl,iface);
+    FIXME("%p\n", This);
+    return E_NOTIMPL;
+}
+
+static IITStorageVtbl ITStorageImpl_Vtbl =
+{
+    ITStorageImpl_QueryInterface,
+    ITStorageImpl_AddRef,
+    ITStorageImpl_Release,
+    ITStorageImpl_StgCreateDocfile,
+    ITStorageImpl_StgCreateDocfileOnILockBytes,
+    ITStorageImpl_StgIsStorageFile,
+    ITStorageImpl_StgIsStorageILockBytes,
+    ITStorageImpl_StgOpenStorage,
+    ITStorageImpl_StgOpenStorageOnILockBytes,
+    ITStorageImpl_StgSetTimes,
+    ITStorageImpl_SetControlData,
+    ITStorageImpl_DefaultControlData,
+    ITStorageImpl_Compact,
+};
+
+static HRESULT ITSS_create(IUnknown *pUnkOuter, LPVOID *ppObj)
+{
+    ITStorageImpl *its;
+
+    its = HeapAlloc( GetProcessHeap(), 0, sizeof(ITStorageImpl) );
+    its->vtbl_IITStorage = &ITStorageImpl_Vtbl;
+    its->ref = 1;
+
+    TRACE("-> %p\n", its);
+    *ppObj = (LPVOID) its;
+
+    return S_OK;
+}
+
+/*****************************************************************************/
+
+HRESULT WINAPI ITSS_DllRegisterServer(void)
+{
+    FIXME("\n");
+    return S_OK;
+}
+
+BOOL WINAPI ITSS_DllCanUnloadNow(void)
+{
+    FIXME("\n");
+
+    return FALSE;
+}
--- /dev/null	1994-07-18 08:46:18.000000000 +0900
+++ dlls/itss/itss.h	2004-08-24 15:51:05.000000000 +0900
@@ -0,0 +1,315 @@
+/*** Autogenerated by WIDL 0.1 from itss.idl - Do not edit ***/
+#include <rpc.h>
+#include <rpcndr.h>
+
+#ifndef __WIDL_ITSS_H
+#define __WIDL_ITSS_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+#include <oaidl.h>
+typedef struct _ITS_Control_Data {
+    UINT cdwControlData;
+    UINT adwControlData[1];
+} ITS_Control_Data, *PITS_Control_Data;
+
+typedef enum ECompactionLev {
+    COMPACT_DATA = 0,
+    COMPACT_DATA_AND_PATH
+} ECompactionLev;
+
+#ifndef __IITStorage_FWD_DEFINED__
+#define __IITStorage_FWD_DEFINED__
+typedef struct IITStorage IITStorage;
+#endif
+
+/*****************************************************************************
+ * IITStorage interface
+ */
+#ifndef __IITStorage_INTERFACE_DEFINED__
+#define __IITStorage_INTERFACE_DEFINED__
+
+#if defined(__cplusplus) && !defined(CINTERFACE)
+struct IITStorage : public IUnknown
+{
+    virtual HRESULT STDMETHODCALLTYPE StgCreateDocfile(
+        const WCHAR* pwcsName,
+        DWORD grfMode,
+        DWORD reserved,
+        IStorage** ppstgOpen) = 0;
+
+    virtual HRESULT STDMETHODCALLTYPE StgCreateDocfileOnILockBytes(
+        ILockBytes* plkbyt,
+        DWORD grfMode,
+        DWORD reserved,
+        IStorage** ppstgOpen) = 0;
+
+    virtual HRESULT STDMETHODCALLTYPE StgIsStorageFile(
+        const WCHAR* pwcsName) = 0;
+
+    virtual HRESULT STDMETHODCALLTYPE StgIsStorageILockBytes(
+        ILockBytes* plkbyt) = 0;
+
+    virtual HRESULT STDMETHODCALLTYPE StgOpenStorage(
+        const WCHAR* pwcsName,
+        IStorage* pstgPriority,
+        DWORD grfMode,
+        SNB snbExclude,
+        DWORD reserved,
+        IStorage** ppstgOpen) = 0;
+
+    virtual HRESULT STDMETHODCALLTYPE StgOpenStorageOnILockBytes(
+        ILockBytes* plkbyt,
+        IStorage* pStgPriority,
+        DWORD grfMode,
+        SNB snbExclude,
+        DWORD reserved,
+        IStorage** ppstgOpen) = 0;
+
+    virtual HRESULT STDMETHODCALLTYPE StgSetTimes(
+        WCHAR* lpszName,
+        FILETIME* pctime,
+        FILETIME* patime,
+        FILETIME* pmtime) = 0;
+
+    virtual HRESULT STDMETHODCALLTYPE SetControlData(
+        PITS_Control_Data pControlData) = 0;
+
+    virtual HRESULT STDMETHODCALLTYPE DefaultControlData(
+        PITS_Control_Data* ppControlData) = 0;
+
+    virtual HRESULT STDMETHODCALLTYPE Compact(
+        const WCHAR* pwcsName,
+        ECompactionLev iLev) = 0;
+
+};
+#else
+typedef struct IITStorageVtbl IITStorageVtbl;
+struct IITStorage {
+    const IITStorageVtbl* lpVtbl;
+};
+struct IITStorageVtbl {
+    BEGIN_INTERFACE
+
+    /*** IUnknown methods ***/
+    HRESULT (STDMETHODCALLTYPE *QueryInterface)(
+        IITStorage* This,
+        REFIID riid,
+        void** ppvObject);
+
+    ULONG (STDMETHODCALLTYPE *AddRef)(
+        IITStorage* This);
+
+    ULONG (STDMETHODCALLTYPE *Release)(
+        IITStorage* This);
+
+    /*** IITStorage methods ***/
+    HRESULT (STDMETHODCALLTYPE *StgCreateDocfile)(
+        IITStorage* This,
+        const WCHAR* pwcsName,
+        DWORD grfMode,
+        DWORD reserved,
+        IStorage** ppstgOpen);
+
+    HRESULT (STDMETHODCALLTYPE *StgCreateDocfileOnILockBytes)(
+        IITStorage* This,
+        ILockBytes* plkbyt,
+        DWORD grfMode,
+        DWORD reserved,
+        IStorage** ppstgOpen);
+
+    HRESULT (STDMETHODCALLTYPE *StgIsStorageFile)(
+        IITStorage* This,
+        const WCHAR* pwcsName);
+
+    HRESULT (STDMETHODCALLTYPE *StgIsStorageILockBytes)(
+        IITStorage* This,
+        ILockBytes* plkbyt);
+
+    HRESULT (STDMETHODCALLTYPE *StgOpenStorage)(
+        IITStorage* This,
+        const WCHAR* pwcsName,
+        IStorage* pstgPriority,
+        DWORD grfMode,
+        SNB snbExclude,
+        DWORD reserved,
+        IStorage** ppstgOpen);
+
+    HRESULT (STDMETHODCALLTYPE *StgOpenStorageOnILockBytes)(
+        IITStorage* This,
+        ILockBytes* plkbyt,
+        IStorage* pStgPriority,
+        DWORD grfMode,
+        SNB snbExclude,
+        DWORD reserved,
+        IStorage** ppstgOpen);
+
+    HRESULT (STDMETHODCALLTYPE *StgSetTimes)(
+        IITStorage* This,
+        WCHAR* lpszName,
+        FILETIME* pctime,
+        FILETIME* patime,
+        FILETIME* pmtime);
+
+    HRESULT (STDMETHODCALLTYPE *SetControlData)(
+        IITStorage* This,
+        PITS_Control_Data pControlData);
+
+    HRESULT (STDMETHODCALLTYPE *DefaultControlData)(
+        IITStorage* This,
+        PITS_Control_Data* ppControlData);
+
+    HRESULT (STDMETHODCALLTYPE *Compact)(
+        IITStorage* This,
+        const WCHAR* pwcsName,
+        ECompactionLev iLev);
+
+    END_INTERFACE
+};
+
+#ifdef COBJMACROS
+/*** IUnknown methods ***/
+#define IITStorage_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b)
+#define IITStorage_AddRef(p) (p)->lpVtbl->AddRef(p)
+#define IITStorage_Release(p) (p)->lpVtbl->Release(p)
+/*** IITStorage methods ***/
+#define IITStorage_StgCreateDocfile(p,a,b,c,d) (p)->lpVtbl->StgCreateDocfile(p,a,b,c,d)
+#define IITStorage_StgCreateDocfileOnILockBytes(p,a,b,c,d) (p)->lpVtbl->StgCreateDocfileOnILockBytes(p,a,b,c,d)
+#define IITStorage_StgIsStorageFile(p,a) (p)->lpVtbl->StgIsStorageFile(p,a)
+#define IITStorage_StgIsStorageILockBytes(p,a) (p)->lpVtbl->StgIsStorageILockBytes(p,a)
+#define IITStorage_StgOpenStorage(p,a,b,c,d,e,f) (p)->lpVtbl->StgOpenStorage(p,a,b,c,d,e,f)
+#define IITStorage_StgOpenStorageOnILockBytes(p,a,b,c,d,e,f) (p)->lpVtbl->StgOpenStorageOnILockBytes(p,a,b,c,d,e,f)
+#define IITStorage_StgSetTimes(p,a,b,c,d) (p)->lpVtbl->StgSetTimes(p,a,b,c,d)
+#define IITStorage_SetControlData(p,a) (p)->lpVtbl->SetControlData(p,a)
+#define IITStorage_DefaultControlData(p,a) (p)->lpVtbl->DefaultControlData(p,a)
+#define IITStorage_Compact(p,a,b) (p)->lpVtbl->Compact(p,a,b)
+#endif
+
+#endif
+
+#define IITStorage_METHODS \
+    /*** IUnknown methods ***/ \
+    STDMETHOD_(HRESULT,QueryInterface)(THIS_ REFIID riid, void** ppvObject) PURE; \
+    STDMETHOD_(ULONG,AddRef)(THIS) PURE; \
+    STDMETHOD_(ULONG,Release)(THIS) PURE; \
+    /*** IITStorage methods ***/ \
+    STDMETHOD_(HRESULT,StgCreateDocfile)(THIS_ const WCHAR* pwcsName, DWORD grfMode, DWORD reserved, IStorage** ppstgOpen) PURE; \
+    STDMETHOD_(HRESULT,StgCreateDocfileOnILockBytes)(THIS_ ILockBytes* plkbyt, DWORD grfMode, DWORD reserved, IStorage** ppstgOpen) PURE; \
+    STDMETHOD_(HRESULT,StgIsStorageFile)(THIS_ const WCHAR* pwcsName) PURE; \
+    STDMETHOD_(HRESULT,StgIsStorageILockBytes)(THIS_ ILockBytes* plkbyt) PURE; \
+    STDMETHOD_(HRESULT,StgOpenStorage)(THIS_ const WCHAR* pwcsName, IStorage* pstgPriority, DWORD grfMode, SNB snbExclude, DWORD reserved, IStorage** ppstgOpen) PURE; \
+    STDMETHOD_(HRESULT,StgOpenStorageOnILockBytes)(THIS_ ILockBytes* plkbyt, IStorage* pStgPriority, DWORD grfMode, SNB snbExclude, DWORD reserved, IStorage** ppstgOpen) PURE; \
+    STDMETHOD_(HRESULT,StgSetTimes)(THIS_ WCHAR* lpszName, FILETIME* pctime, FILETIME* patime, FILETIME* pmtime) PURE; \
+    STDMETHOD_(HRESULT,SetControlData)(THIS_ PITS_Control_Data pControlData) PURE; \
+    STDMETHOD_(HRESULT,DefaultControlData)(THIS_ PITS_Control_Data* ppControlData) PURE; \
+    STDMETHOD_(HRESULT,Compact)(THIS_ const WCHAR* pwcsName, ECompactionLev iLev) PURE;
+
+HRESULT CALLBACK IITStorage_StgCreateDocfile_Proxy(
+    IITStorage* This,
+    const WCHAR* pwcsName,
+    DWORD grfMode,
+    DWORD reserved,
+    IStorage** ppstgOpen);
+void __RPC_STUB IITStorage_StgCreateDocfile_Stub(
+    struct IRpcStubBuffer* This,
+    struct IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+HRESULT CALLBACK IITStorage_StgCreateDocfileOnILockBytes_Proxy(
+    IITStorage* This,
+    ILockBytes* plkbyt,
+    DWORD grfMode,
+    DWORD reserved,
+    IStorage** ppstgOpen);
+void __RPC_STUB IITStorage_StgCreateDocfileOnILockBytes_Stub(
+    struct IRpcStubBuffer* This,
+    struct IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+HRESULT CALLBACK IITStorage_StgIsStorageFile_Proxy(
+    IITStorage* This,
+    const WCHAR* pwcsName);
+void __RPC_STUB IITStorage_StgIsStorageFile_Stub(
+    struct IRpcStubBuffer* This,
+    struct IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+HRESULT CALLBACK IITStorage_StgIsStorageILockBytes_Proxy(
+    IITStorage* This,
+    ILockBytes* plkbyt);
+void __RPC_STUB IITStorage_StgIsStorageILockBytes_Stub(
+    struct IRpcStubBuffer* This,
+    struct IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+HRESULT CALLBACK IITStorage_StgOpenStorage_Proxy(
+    IITStorage* This,
+    const WCHAR* pwcsName,
+    IStorage* pstgPriority,
+    DWORD grfMode,
+    SNB snbExclude,
+    DWORD reserved,
+    IStorage** ppstgOpen);
+void __RPC_STUB IITStorage_StgOpenStorage_Stub(
+    struct IRpcStubBuffer* This,
+    struct IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+HRESULT CALLBACK IITStorage_StgOpenStorageOnILockBytes_Proxy(
+    IITStorage* This,
+    ILockBytes* plkbyt,
+    IStorage* pStgPriority,
+    DWORD grfMode,
+    SNB snbExclude,
+    DWORD reserved,
+    IStorage** ppstgOpen);
+void __RPC_STUB IITStorage_StgOpenStorageOnILockBytes_Stub(
+    struct IRpcStubBuffer* This,
+    struct IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+HRESULT CALLBACK IITStorage_StgSetTimes_Proxy(
+    IITStorage* This,
+    WCHAR* lpszName,
+    FILETIME* pctime,
+    FILETIME* patime,
+    FILETIME* pmtime);
+void __RPC_STUB IITStorage_StgSetTimes_Stub(
+    struct IRpcStubBuffer* This,
+    struct IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+HRESULT CALLBACK IITStorage_SetControlData_Proxy(
+    IITStorage* This,
+    PITS_Control_Data pControlData);
+void __RPC_STUB IITStorage_SetControlData_Stub(
+    struct IRpcStubBuffer* This,
+    struct IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+HRESULT CALLBACK IITStorage_DefaultControlData_Proxy(
+    IITStorage* This,
+    PITS_Control_Data* ppControlData);
+void __RPC_STUB IITStorage_DefaultControlData_Stub(
+    struct IRpcStubBuffer* This,
+    struct IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+HRESULT CALLBACK IITStorage_Compact_Proxy(
+    IITStorage* This,
+    const WCHAR* pwcsName,
+    ECompactionLev iLev);
+void __RPC_STUB IITStorage_Compact_Stub(
+    struct IRpcStubBuffer* This,
+    struct IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+
+#endif  /* __IITStorage_INTERFACE_DEFINED__ */
+
+DEFINE_GUID(CLSID_ITStorage,0x5d02926a,0x212e,0x11d0,0x9d,0xf9,0x00,0xa0,0xc9,0x22,0xe6,0xec );
+DEFINE_GUID(IID_IITStorage, 0x88cc31de, 0x27ab, 0x11d0, 0x9d, 0xf9, 0x0, 0xa0, 0xc9, 0x22, 0xe6, 0xec);
+#ifdef __cplusplus
+}
+#endif
+#endif /* __WIDL_ITSS_H */
--- /dev/null	1994-07-18 08:46:18.000000000 +0900
+++ dlls/itss/itss.idl	2004-08-24 15:45:22.000000000 +0900
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2004 Mike McCormack
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+import "oaidl.idl";
+
+typedef struct _ITS_Control_Data
+{
+    UINT cdwControlData;
+    UINT adwControlData[1];
+
+} ITS_Control_Data, *PITS_Control_Data;
+
+typedef enum ECompactionLev { 
+    COMPACT_DATA = 0,
+    COMPACT_DATA_AND_PATH
+} ECompactionLev;
+
+[
+  object,
+  pointer_default(unique)
+]
+interface IITStorage : IUnknown
+{
+    HRESULT StgCreateDocfile(
+        [in] const WCHAR * pwcsName,
+        [in] DWORD grfMode,
+        [in] DWORD reserved,
+        [out] IStorage ** ppstgOpen);
+
+    HRESULT StgCreateDocfileOnILockBytes(
+        [in] ILockBytes * plkbyt,
+        [in] DWORD grfMode,
+        [in] DWORD reserved, 
+        [out] IStorage ** ppstgOpen);
+
+
+    HRESULT StgIsStorageFile(
+        [in] const WCHAR * pwcsName);
+
+    HRESULT StgIsStorageILockBytes(
+        [in] ILockBytes * plkbyt);
+
+    HRESULT StgOpenStorage(
+        [in] const WCHAR * pwcsName,
+        [in] IStorage * pstgPriority,
+        [in] DWORD grfMode,
+        [in] SNB snbExclude,
+        [in] DWORD reserved,
+        [out] IStorage ** ppstgOpen);
+
+    HRESULT StgOpenStorageOnILockBytes(
+        [in] ILockBytes * plkbyt,
+        [in] IStorage * pStgPriority,
+        [in] DWORD grfMode,
+        [in] SNB snbExclude,
+        [in] DWORD reserved,
+        [out] IStorage ** ppstgOpen);
+
+    HRESULT StgSetTimes(
+        [in] WCHAR const * lpszName,
+        [in] FILETIME const * pctime,
+        [in] FILETIME const * patime,
+        [in] FILETIME const * pmtime);
+
+    HRESULT SetControlData(
+        [in] PITS_Control_Data pControlData);
+    
+    HRESULT DefaultControlData(
+        [out] PITS_Control_Data * ppControlData);
+
+    HRESULT Compact(
+        [in] const WCHAR * pwcsName,
+        [in] ECompactionLev iLev);
+}
+
+cpp_quote("DEFINE_GUID(CLSID_ITStorage,0x5d02926a,0x212e,0x11d0,0x9d,0xf9,0x00,0xa0,0xc9,0x22,0xe6,0xec );");
+cpp_quote("DEFINE_GUID(IID_IITStorage, 0x88cc31de, 0x27ab, 0x11d0, 0x9d, 0xf9, 0x0, 0xa0, 0xc9, 0x22, 0xe6, 0xec);");
--- /dev/null	1994-07-18 08:46:18.000000000 +0900
+++ dlls/itss/itss.spec	2004-08-24 15:50:55.000000000 +0900
@@ -0,0 +1,5 @@
+@ stdcall -private DllCanUnloadNow() ITSS_DllCanUnloadNow
+@ stdcall -private DllGetClassObject(ptr ptr ptr) ITSS_DllGetClassObject
+@ stub DllInstall
+@ stdcall DllRegisterServer() ITSS_DllRegisterServer
+@ stub DllUnregisterServer
--- /dev/null	1994-07-18 08:46:18.000000000 +0900
+++ dlls/itss/lzx.c	2004-08-24 15:45:32.000000000 +0900
@@ -0,0 +1,829 @@
+/* $Id: lzx.c,v 1.2 2004/08/24 05:09:52 mike Exp $ */
+/***************************************************************************
+ *                        lzx.c - LZX decompression routines               *
+ *                           -------------------                           *
+ *                                                                         *
+ *  maintainer: Jed Wing <jedwin at ugcs.caltech.edu>                         *
+ *  source:     modified lzx.c from cabextract v0.5                        *
+ *  notes:      This file was taken from cabextract v0.5, which was,       *
+ *              itself, a modified version of the lzx decompression code   *
+ *              from unlzx.                                                *
+ *                                                                         *
+ *  platforms:  In its current incarnation, this file has been tested on   *
+ *              two different Linux platforms (one, redhat-based, with a   *
+ *              2.1.2 glibc and gcc 2.95.x, and the other, Debian, with    *
+ *              2.2.4 glibc and both gcc 2.95.4 and gcc 3.0.2).  Both were *
+ *              Intel x86 compatible machines.                             *
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   Copyright(C) Stuart Caie                                              *
+ *                                                                         *
+ *   This program 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.                       *
+ *                                                                         *
+ ***************************************************************************/
+
+#include "lzx.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef __GNUC__
+#define memcpy __builtin_memcpy
+#endif
+
+/* sized types */
+typedef unsigned char  UBYTE; /* 8 bits exactly    */
+typedef unsigned short UWORD; /* 16 bits (or more) */
+typedef unsigned int   ULONG; /* 32 bits (or more) */
+typedef   signed int    LONG; /* 32 bits (or more) */
+
+/* some constants defined by the LZX specification */
+#define LZX_MIN_MATCH                (2)
+#define LZX_MAX_MATCH                (257)
+#define LZX_NUM_CHARS                (256)
+#define LZX_BLOCKTYPE_INVALID        (0)   /* also blocktypes 4-7 invalid */
+#define LZX_BLOCKTYPE_VERBATIM       (1)
+#define LZX_BLOCKTYPE_ALIGNED        (2)
+#define LZX_BLOCKTYPE_UNCOMPRESSED   (3)
+#define LZX_PRETREE_NUM_ELEMENTS     (20)
+#define LZX_ALIGNED_NUM_ELEMENTS     (8)   /* aligned offset tree #elements */
+#define LZX_NUM_PRIMARY_LENGTHS      (7)   /* this one missing from spec! */
+#define LZX_NUM_SECONDARY_LENGTHS    (249) /* length tree #elements */
+
+/* LZX huffman defines: tweak tablebits as desired */
+#define LZX_PRETREE_MAXSYMBOLS  (LZX_PRETREE_NUM_ELEMENTS)
+#define LZX_PRETREE_TABLEBITS   (6)
+#define LZX_MAINTREE_MAXSYMBOLS (LZX_NUM_CHARS + 50*8)
+#define LZX_MAINTREE_TABLEBITS  (12)
+#define LZX_LENGTH_MAXSYMBOLS   (LZX_NUM_SECONDARY_LENGTHS+1)
+#define LZX_LENGTH_TABLEBITS    (12)
+#define LZX_ALIGNED_MAXSYMBOLS  (LZX_ALIGNED_NUM_ELEMENTS)
+#define LZX_ALIGNED_TABLEBITS   (7)
+
+#define LZX_LENTABLE_SAFETY (64) /* we allow length table decoding overruns */
+
+#define LZX_DECLARE_TABLE(tbl) \
+  UWORD tbl##_table[(1<<LZX_##tbl##_TABLEBITS) + (LZX_##tbl##_MAXSYMBOLS<<1)];\
+  UBYTE tbl##_len  [LZX_##tbl##_MAXSYMBOLS + LZX_LENTABLE_SAFETY]
+
+struct LZXstate
+{
+    UBYTE *window;         /* the actual decoding window              */
+    ULONG window_size;     /* window size (32Kb through 2Mb)          */
+    ULONG actual_size;     /* window size when it was first allocated */
+    ULONG window_posn;     /* current offset within the window        */
+    ULONG R0, R1, R2;      /* for the LRU offset system               */
+    UWORD main_elements;   /* number of main tree elements            */
+    int   header_read;     /* have we started decoding at all yet?    */
+    UWORD block_type;      /* type of this block                      */
+    ULONG block_length;    /* uncompressed length of this block       */
+    ULONG block_remaining; /* uncompressed bytes still left to decode */
+    ULONG frames_read;     /* the number of CFDATA blocks processed   */
+    LONG  intel_filesize;  /* magic header value used for transform   */
+    LONG  intel_curpos;    /* current offset in transform space       */
+    int   intel_started;   /* have we seen any translatable data yet? */
+
+    LZX_DECLARE_TABLE(PRETREE);
+    LZX_DECLARE_TABLE(MAINTREE);
+    LZX_DECLARE_TABLE(LENGTH);
+    LZX_DECLARE_TABLE(ALIGNED);
+};
+
+/* LZX decruncher */
+
+/* Microsoft's LZX document and their implementation of the
+ * com.ms.util.cab Java package do not concur.
+ *
+ * In the LZX document, there is a table showing the correlation between
+ * window size and the number of position slots. It states that the 1MB
+ * window = 40 slots and the 2MB window = 42 slots. In the implementation,
+ * 1MB = 42 slots, 2MB = 50 slots. The actual calculation is 'find the
+ * first slot whose position base is equal to or more than the required
+ * window size'. This would explain why other tables in the document refer
+ * to 50 slots rather than 42.
+ *
+ * The constant NUM_PRIMARY_LENGTHS used in the decompression pseudocode
+ * is not defined in the specification.
+ *
+ * The LZX document does not state the uncompressed block has an
+ * uncompressed length field. Where does this length field come from, so
+ * we can know how large the block is? The implementation has it as the 24
+ * bits following after the 3 blocktype bits, before the alignment
+ * padding.
+ *
+ * The LZX document states that aligned offset blocks have their aligned
+ * offset huffman tree AFTER the main and length trees. The implementation
+ * suggests that the aligned offset tree is BEFORE the main and length
+ * trees.
+ *
+ * The LZX document decoding algorithm states that, in an aligned offset
+ * block, if an extra_bits value is 1, 2 or 3, then that number of bits
+ * should be read and the result added to the match offset. This is
+ * correct for 1 and 2, but not 3, where just a huffman symbol (using the
+ * aligned tree) should be read.
+ *
+ * Regarding the E8 preprocessing, the LZX document states 'No translation
+ * may be performed on the last 6 bytes of the input block'. This is
+ * correct.  However, the pseudocode provided checks for the *E8 leader*
+ * up to the last 6 bytes. If the leader appears between -10 and -7 bytes
+ * from the end, this would cause the next four bytes to be modified, at
+ * least one of which would be in the last 6 bytes, which is not allowed
+ * according to the spec.
+ *
+ * The specification states that the huffman trees must always contain at
+ * least one element. However, many CAB files contain blocks where the
+ * length tree is completely empty (because there are no matches), and
+ * this is expected to succeed.
+ */
+
+
+/* LZX uses what it calls 'position slots' to represent match offsets.
+ * What this means is that a small 'position slot' number and a small
+ * offset from that slot are encoded instead of one large offset for
+ * every match.
+ * - position_base is an index to the position slot bases
+ * - extra_bits states how many bits of offset-from-base data is needed.
+ */
+static const UBYTE extra_bits[51] = {
+     0,  0,  0,  0,  1,  1,  2,  2,  3,  3,  4,  4,  5,  5,  6,  6,
+     7,  7,  8,  8,  9,  9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14,
+    15, 15, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
+    17, 17, 17
+};
+
+static const ULONG position_base[51] = {
+          0,       1,       2,      3,      4,      6,      8,     12,     16,     24,     32,       48,      64,      96,     128,     192,
+        256,     384,     512,    768,   1024,   1536,   2048,   3072,   4096,   6144,   8192,    12288,   16384,   24576,   32768,   49152,
+      65536,   98304,  131072, 196608, 262144, 393216, 524288, 655360, 786432, 917504, 1048576, 1179648, 1310720, 1441792, 1572864, 1703936,
+    1835008, 1966080, 2097152
+};
+
+struct LZXstate *LZXinit(int window)
+{
+    struct LZXstate *pState=NULL;
+    ULONG wndsize = 1 << window;
+    int i, posn_slots;
+
+    /* LZX supports window sizes of 2^15 (32Kb) through 2^21 (2Mb) */
+    /* if a previously allocated window is big enough, keep it     */
+    if (window < 15 || window > 21) return NULL;
+
+    /* allocate state and associated window */
+    pState = (struct LZXstate *)malloc(sizeof(struct LZXstate));
+    if (!(pState->window = (UBYTE *)malloc(wndsize)))
+    {
+        free(pState);
+        return NULL;
+    }
+    pState->actual_size = wndsize;
+    pState->window_size = wndsize;
+
+    /* calculate required position slots */
+    if (window == 20) posn_slots = 42;
+    else if (window == 21) posn_slots = 50;
+    else posn_slots = window << 1;
+
+    /** alternatively **/
+    /* posn_slots=i=0; while (i < wndsize) i += 1 << extra_bits[posn_slots++]; */
+
+    /* initialize other state */
+    pState->R0  =  pState->R1  = pState->R2 = 1;
+    pState->main_elements   = LZX_NUM_CHARS + (posn_slots << 3);
+    pState->header_read     = 0;
+    pState->frames_read     = 0;
+    pState->block_remaining = 0;
+    pState->block_type      = LZX_BLOCKTYPE_INVALID;
+    pState->intel_curpos    = 0;
+    pState->intel_started   = 0;
+    pState->window_posn     = 0;
+
+    /* initialise tables to 0 (because deltas will be applied to them) */
+    for (i = 0; i < LZX_MAINTREE_MAXSYMBOLS; i++) pState->MAINTREE_len[i] = 0;
+    for (i = 0; i < LZX_LENGTH_MAXSYMBOLS; i++)   pState->LENGTH_len[i]   = 0;
+
+    return pState;
+}
+
+void LZXteardown(struct LZXstate *pState)
+{
+    if (pState)
+    {
+        if (pState->window)
+            free(pState->window);
+        free(pState);
+    }
+}
+
+int LZXreset(struct LZXstate *pState)
+{
+    int i;
+
+    pState->R0  =  pState->R1  = pState->R2 = 1;
+    pState->header_read     = 0;
+    pState->frames_read     = 0;
+    pState->block_remaining = 0;
+    pState->block_type      = LZX_BLOCKTYPE_INVALID;
+    pState->intel_curpos    = 0;
+    pState->intel_started   = 0;
+    pState->window_posn     = 0;
+
+    for (i = 0; i < LZX_MAINTREE_MAXSYMBOLS + LZX_LENTABLE_SAFETY; i++) pState->MAINTREE_len[i] = 0;
+    for (i = 0; i < LZX_LENGTH_MAXSYMBOLS + LZX_LENTABLE_SAFETY; i++)   pState->LENGTH_len[i]   = 0;
+
+    return DECR_OK;
+}
+
+
+/* Bitstream reading macros:
+ *
+ * INIT_BITSTREAM    should be used first to set up the system
+ * READ_BITS(var,n)  takes N bits from the buffer and puts them in var
+ *
+ * ENSURE_BITS(n)    ensures there are at least N bits in the bit buffer
+ * PEEK_BITS(n)      extracts (without removing) N bits from the bit buffer
+ * REMOVE_BITS(n)    removes N bits from the bit buffer
+ *
+ * These bit access routines work by using the area beyond the MSB and the
+ * LSB as a free source of zeroes. This avoids having to mask any bits.
+ * So we have to know the bit width of the bitbuffer variable. This is
+ * sizeof(ULONG) * 8, also defined as ULONG_BITS
+ */
+
+/* number of bits in ULONG. Note: This must be at multiple of 16, and at
+ * least 32 for the bitbuffer code to work (ie, it must be able to ensure
+ * up to 17 bits - that's adding 16 bits when there's one bit left, or
+ * adding 32 bits when there are no bits left. The code should work fine
+ * for machines where ULONG >= 32 bits.
+ */
+#define ULONG_BITS (sizeof(ULONG)<<3)
+
+#define INIT_BITSTREAM do { bitsleft = 0; bitbuf = 0; } while (0)
+
+#define ENSURE_BITS(n)							\
+  while (bitsleft < (n)) {						\
+    bitbuf |= ((inpos[1]<<8)|inpos[0]) << (ULONG_BITS-16 - bitsleft);	\
+    bitsleft += 16; inpos+=2;						\
+  }
+
+#define PEEK_BITS(n)   (bitbuf >> (ULONG_BITS - (n)))
+#define REMOVE_BITS(n) ((bitbuf <<= (n)), (bitsleft -= (n)))
+
+#define READ_BITS(v,n) do {						\
+  ENSURE_BITS(n);							\
+  (v) = PEEK_BITS(n);							\
+  REMOVE_BITS(n);							\
+} while (0)
+
+
+/* Huffman macros */
+
+#define TABLEBITS(tbl)   (LZX_##tbl##_TABLEBITS)
+#define MAXSYMBOLS(tbl)  (LZX_##tbl##_MAXSYMBOLS)
+#define SYMTABLE(tbl)    (pState->tbl##_table)
+#define LENTABLE(tbl)    (pState->tbl##_len)
+
+/* BUILD_TABLE(tablename) builds a huffman lookup table from code lengths.
+ * In reality, it just calls make_decode_table() with the appropriate
+ * values - they're all fixed by some #defines anyway, so there's no point
+ * writing each call out in full by hand.
+ */
+#define BUILD_TABLE(tbl)						\
+  if (make_decode_table(						\
+    MAXSYMBOLS(tbl), TABLEBITS(tbl), LENTABLE(tbl), SYMTABLE(tbl)	\
+  )) { return DECR_ILLEGALDATA; }
+
+
+/* READ_HUFFSYM(tablename, var) decodes one huffman symbol from the
+ * bitstream using the stated table and puts it in var.
+ */
+#define READ_HUFFSYM(tbl,var) do {					\
+  ENSURE_BITS(16);							\
+  hufftbl = SYMTABLE(tbl);						\
+  if ((i = hufftbl[PEEK_BITS(TABLEBITS(tbl))]) >= MAXSYMBOLS(tbl)) {	\
+    j = 1 << (ULONG_BITS - TABLEBITS(tbl));				\
+    do {								\
+      j >>= 1; i <<= 1; i |= (bitbuf & j) ? 1 : 0;			\
+      if (!j) { return DECR_ILLEGALDATA; }	                        \
+    } while ((i = hufftbl[i]) >= MAXSYMBOLS(tbl));			\
+  }									\
+  j = LENTABLE(tbl)[(var) = i];						\
+  REMOVE_BITS(j);							\
+} while (0)
+
+
+/* READ_LENGTHS(tablename, first, last) reads in code lengths for symbols
+ * first to last in the given table. The code lengths are stored in their
+ * own special LZX way.
+ */
+#define READ_LENGTHS(tbl,first,last) do { \
+  lb.bb = bitbuf; lb.bl = bitsleft; lb.ip = inpos; \
+  if (lzx_read_lens(pState, LENTABLE(tbl),(first),(last),&lb)) { \
+    return DECR_ILLEGALDATA; \
+  } \
+  bitbuf = lb.bb; bitsleft = lb.bl; inpos = lb.ip; \
+} while (0)
+
+
+/* make_decode_table(nsyms, nbits, length[], table[])
+ *
+ * This function was coded by David Tritscher. It builds a fast huffman
+ * decoding table out of just a canonical huffman code lengths table.
+ *
+ * nsyms  = total number of symbols in this huffman tree.
+ * nbits  = any symbols with a code length of nbits or less can be decoded
+ *          in one lookup of the table.
+ * length = A table to get code lengths from [0 to syms-1]
+ * table  = The table to fill up with decoded symbols and pointers.
+ *
+ * Returns 0 for OK or 1 for error
+ */
+
+static int make_decode_table(ULONG nsyms, ULONG nbits, UBYTE *length, UWORD *table) {
+    register UWORD sym;
+    register ULONG leaf;
+    register UBYTE bit_num = 1;
+    ULONG fill;
+    ULONG pos         = 0; /* the current position in the decode table */
+    ULONG table_mask  = 1 << nbits;
+    ULONG bit_mask    = table_mask >> 1; /* don't do 0 length codes */
+    ULONG next_symbol = bit_mask; /* base of allocation for long codes */
+
+    /* fill entries for codes short enough for a direct mapping */
+    while (bit_num <= nbits) {
+        for (sym = 0; sym < nsyms; sym++) {
+            if (length[sym] == bit_num) {
+                leaf = pos;
+
+                if((pos += bit_mask) > table_mask) return 1; /* table overrun */
+
+                /* fill all possible lookups of this symbol with the symbol itself */
+                fill = bit_mask;
+                while (fill-- > 0) table[leaf++] = sym;
+            }
+        }
+        bit_mask >>= 1;
+        bit_num++;
+    }
+
+    /* if there are any codes longer than nbits */
+    if (pos != table_mask) {
+        /* clear the remainder of the table */
+        for (sym = pos; sym < table_mask; sym++) table[sym] = 0;
+
+        /* give ourselves room for codes to grow by up to 16 more bits */
+        pos <<= 16;
+        table_mask <<= 16;
+        bit_mask = 1 << 15;
+
+        while (bit_num <= 16) {
+            for (sym = 0; sym < nsyms; sym++) {
+                if (length[sym] == bit_num) {
+                    leaf = pos >> 16;
+                    for (fill = 0; fill < bit_num - nbits; fill++) {
+                        /* if this path hasn't been taken yet, 'allocate' two entries */
+                        if (table[leaf] == 0) {
+                            table[(next_symbol << 1)] = 0;
+                            table[(next_symbol << 1) + 1] = 0;
+                            table[leaf] = next_symbol++;
+                        }
+                        /* follow the path and select either left or right for next bit */
+                        leaf = table[leaf] << 1;
+                        if ((pos >> (15-fill)) & 1) leaf++;
+                    }
+                    table[leaf] = sym;
+
+                    if ((pos += bit_mask) > table_mask) return 1; /* table overflow */
+                }
+            }
+            bit_mask >>= 1;
+            bit_num++;
+        }
+    }
+
+    /* full table? */
+    if (pos == table_mask) return 0;
+
+    /* either erroneous table, or all elements are 0 - let's find out. */
+    for (sym = 0; sym < nsyms; sym++) if (length[sym]) return 1;
+    return 0;
+}
+
+struct lzx_bits {
+  ULONG bb;
+  int bl;
+  UBYTE *ip;
+};
+
+static int lzx_read_lens(struct LZXstate *pState, UBYTE *lens, ULONG first, ULONG last, struct lzx_bits *lb) {
+    ULONG i,j, x,y;
+    int z;
+
+    register ULONG bitbuf = lb->bb;
+    register int bitsleft = lb->bl;
+    UBYTE *inpos = lb->ip;
+    UWORD *hufftbl;
+
+    for (x = 0; x < 20; x++) {
+        READ_BITS(y, 4);
+        LENTABLE(PRETREE)[x] = y;
+    }
+    BUILD_TABLE(PRETREE);
+
+    for (x = first; x < last; ) {
+        READ_HUFFSYM(PRETREE, z);
+        if (z == 17) {
+            READ_BITS(y, 4); y += 4;
+            while (y--) lens[x++] = 0;
+        }
+        else if (z == 18) {
+            READ_BITS(y, 5); y += 20;
+            while (y--) lens[x++] = 0;
+        }
+        else if (z == 19) {
+            READ_BITS(y, 1); y += 4;
+            READ_HUFFSYM(PRETREE, z);
+            z = lens[x] - z; if (z < 0) z += 17;
+            while (y--) lens[x++] = z;
+        }
+        else {
+            z = lens[x] - z; if (z < 0) z += 17;
+            lens[x++] = z;
+        }
+    }
+
+    lb->bb = bitbuf;
+    lb->bl = bitsleft;
+    lb->ip = inpos;
+    return 0;
+}
+
+int LZXdecompress(struct LZXstate *pState, unsigned char *inpos, unsigned char *outpos, int inlen, int outlen) {
+    UBYTE *endinp = inpos + inlen;
+    UBYTE *window = pState->window;
+    UBYTE *runsrc, *rundest;
+    UWORD *hufftbl; /* used in READ_HUFFSYM macro as chosen decoding table */
+
+    ULONG window_posn = pState->window_posn;
+    ULONG window_size = pState->window_size;
+    ULONG R0 = pState->R0;
+    ULONG R1 = pState->R1;
+    ULONG R2 = pState->R2;
+
+    register ULONG bitbuf;
+    register int bitsleft;
+    ULONG match_offset, i,j,k; /* ijk used in READ_HUFFSYM macro */
+    struct lzx_bits lb; /* used in READ_LENGTHS macro */
+
+    int togo = outlen, this_run, main_element, aligned_bits;
+    int match_length, length_footer, extra, verbatim_bits;
+    int copy_length;
+
+    INIT_BITSTREAM;
+
+    /* read header if necessary */
+    if (!pState->header_read) {
+        i = j = 0;
+        READ_BITS(k, 1); if (k) { READ_BITS(i,16); READ_BITS(j,16); }
+        pState->intel_filesize = (i << 16) | j; /* or 0 if not encoded */
+        pState->header_read = 1;
+    }
+
+    /* main decoding loop */
+    while (togo > 0) {
+        /* last block finished, new block expected */
+        if (pState->block_remaining == 0) {
+            if (pState->block_type == LZX_BLOCKTYPE_UNCOMPRESSED) {
+                if (pState->block_length & 1) inpos++; /* realign bitstream to word */
+                INIT_BITSTREAM;
+            }
+
+            READ_BITS(pState->block_type, 3);
+            READ_BITS(i, 16);
+            READ_BITS(j, 8);
+            pState->block_remaining = pState->block_length = (i << 8) | j;
+
+            switch (pState->block_type) {
+                case LZX_BLOCKTYPE_ALIGNED:
+                    for (i = 0; i < 8; i++) { READ_BITS(j, 3); LENTABLE(ALIGNED)[i] = j; }
+                    BUILD_TABLE(ALIGNED);
+                    /* rest of aligned header is same as verbatim */
+
+                case LZX_BLOCKTYPE_VERBATIM:
+                    READ_LENGTHS(MAINTREE, 0, 256);
+                    READ_LENGTHS(MAINTREE, 256, pState->main_elements);
+                    BUILD_TABLE(MAINTREE);
+                    if (LENTABLE(MAINTREE)[0xE8] != 0) pState->intel_started = 1;
+
+                    READ_LENGTHS(LENGTH, 0, LZX_NUM_SECONDARY_LENGTHS);
+                    BUILD_TABLE(LENGTH);
+                    break;
+
+                case LZX_BLOCKTYPE_UNCOMPRESSED:
+                    pState->intel_started = 1; /* because we can't assume otherwise */
+                    ENSURE_BITS(16); /* get up to 16 pad bits into the buffer */
+                    if (bitsleft > 16) inpos -= 2; /* and align the bitstream! */
+                    R0 = inpos[0]|(inpos[1]<<8)|(inpos[2]<<16)|(inpos[3]<<24);inpos+=4;
+                    R1 = inpos[0]|(inpos[1]<<8)|(inpos[2]<<16)|(inpos[3]<<24);inpos+=4;
+                    R2 = inpos[0]|(inpos[1]<<8)|(inpos[2]<<16)|(inpos[3]<<24);inpos+=4;
+                    break;
+
+                default:
+                    return DECR_ILLEGALDATA;
+            }
+        }
+
+        /* buffer exhaustion check */
+        if (inpos > endinp) {
+            /* it's possible to have a file where the next run is less than
+             * 16 bits in size. In this case, the READ_HUFFSYM() macro used
+             * in building the tables will exhaust the buffer, so we should
+             * allow for this, but not allow those accidentally read bits to
+             * be used (so we check that there are at least 16 bits
+             * remaining - in this boundary case they aren't really part of
+             * the compressed data)
+             */
+            if (inpos > (endinp+2) || bitsleft < 16) return DECR_ILLEGALDATA;
+        }
+
+        while ((this_run = pState->block_remaining) > 0 && togo > 0) {
+            if (this_run > togo) this_run = togo;
+            togo -= this_run;
+            pState->block_remaining -= this_run;
+
+            /* apply 2^x-1 mask */
+            window_posn &= window_size - 1;
+            /* runs can't straddle the window wraparound */
+            if ((window_posn + this_run) > window_size)
+                return DECR_DATAFORMAT;
+
+            switch (pState->block_type) {
+
+                case LZX_BLOCKTYPE_VERBATIM:
+                    while (this_run > 0) {
+                        READ_HUFFSYM(MAINTREE, main_element);
+
+                        if (main_element < LZX_NUM_CHARS) {
+                            /* literal: 0 to LZX_NUM_CHARS-1 */
+                            window[window_posn++] = main_element;
+                            this_run--;
+                        }
+                        else {
+                            /* match: LZX_NUM_CHARS + ((slot<<3) | length_header (3 bits)) */
+                            main_element -= LZX_NUM_CHARS;
+
+                            match_length = main_element & LZX_NUM_PRIMARY_LENGTHS;
+                            if (match_length == LZX_NUM_PRIMARY_LENGTHS) {
+                                READ_HUFFSYM(LENGTH, length_footer);
+                                match_length += length_footer;
+                            }
+                            match_length += LZX_MIN_MATCH;
+
+                            match_offset = main_element >> 3;
+
+                            if (match_offset > 2) {
+                                /* not repeated offset */
+                                if (match_offset != 3) {
+                                    extra = extra_bits[match_offset];
+                                    READ_BITS(verbatim_bits, extra);
+                                    match_offset = position_base[match_offset] - 2 + verbatim_bits;
+                                }
+                                else {
+                                    match_offset = 1;
+                                }
+
+                                /* update repeated offset LRU queue */
+                                R2 = R1; R1 = R0; R0 = match_offset;
+                            }
+                            else if (match_offset == 0) {
+                                match_offset = R0;
+                            }
+                            else if (match_offset == 1) {
+                                match_offset = R1;
+                                R1 = R0; R0 = match_offset;
+                            }
+                            else /* match_offset == 2 */ {
+                                match_offset = R2;
+                                R2 = R0; R0 = match_offset;
+                            }
+
+                            rundest = window + window_posn;
+                            this_run -= match_length;
+
+                            /* copy any wrapped around source data */
+                            if (window_posn >= match_offset) {
+                                /* no wrap */
+                                 runsrc = rundest - match_offset;
+                            } else {
+                                runsrc = rundest + (window_size - match_offset);
+                                copy_length = match_offset - window_posn;
+                                if (copy_length < match_length) {
+                                     match_length -= copy_length;
+                                     window_posn += copy_length;
+                                     while (copy_length-- > 0) *rundest++ = *runsrc++;
+                                     runsrc = window;
+                                }
+                            }
+                            window_posn += match_length;
+ 
+                            /* copy match data - no worries about destination wraps */
+                            while (match_length-- > 0) *rundest++ = *runsrc++;
+
+                        }
+                    }
+                    break;
+
+                case LZX_BLOCKTYPE_ALIGNED:
+                    while (this_run > 0) {
+                        READ_HUFFSYM(MAINTREE, main_element);
+
+                        if (main_element < LZX_NUM_CHARS) {
+                            /* literal: 0 to LZX_NUM_CHARS-1 */
+                            window[window_posn++] = main_element;
+                            this_run--;
+                        }
+                        else {
+                            /* match: LZX_NUM_CHARS + ((slot<<3) | length_header (3 bits)) */
+                            main_element -= LZX_NUM_CHARS;
+
+                            match_length = main_element & LZX_NUM_PRIMARY_LENGTHS;
+                            if (match_length == LZX_NUM_PRIMARY_LENGTHS) {
+                                READ_HUFFSYM(LENGTH, length_footer);
+                                match_length += length_footer;
+                            }
+                            match_length += LZX_MIN_MATCH;
+
+                            match_offset = main_element >> 3;
+
+                            if (match_offset > 2) {
+                                /* not repeated offset */
+                                extra = extra_bits[match_offset];
+                                match_offset = position_base[match_offset] - 2;
+                                if (extra > 3) {
+                                    /* verbatim and aligned bits */
+                                    extra -= 3;
+                                    READ_BITS(verbatim_bits, extra);
+                                    match_offset += (verbatim_bits << 3);
+                                    READ_HUFFSYM(ALIGNED, aligned_bits);
+                                    match_offset += aligned_bits;
+                                }
+                                else if (extra == 3) {
+                                    /* aligned bits only */
+                                    READ_HUFFSYM(ALIGNED, aligned_bits);
+                                    match_offset += aligned_bits;
+                                }
+                                else if (extra > 0) { /* extra==1, extra==2 */
+                                    /* verbatim bits only */
+                                    READ_BITS(verbatim_bits, extra);
+                                    match_offset += verbatim_bits;
+                                }
+                                else /* extra == 0 */ {
+                                    /* ??? */
+                                    match_offset = 1;
+                                }
+
+                                /* update repeated offset LRU queue */
+                                R2 = R1; R1 = R0; R0 = match_offset;
+                            }
+                            else if (match_offset == 0) {
+                                match_offset = R0;
+                            }
+                            else if (match_offset == 1) {
+                                match_offset = R1;
+                                R1 = R0; R0 = match_offset;
+                            }
+                            else /* match_offset == 2 */ {
+                                match_offset = R2;
+                                R2 = R0; R0 = match_offset;
+                            }
+
+                            rundest = window + window_posn;
+                            this_run -= match_length;
+
+                            /* copy any wrapped around source data */
+                            if (window_posn >= match_offset) {
+                                /* no wrap */
+                                 runsrc = rundest - match_offset;
+                            } else {
+                                runsrc = rundest + (window_size - match_offset);
+                                copy_length = match_offset - window_posn;
+                                if (copy_length < match_length) {
+                                     match_length -= copy_length;
+                                     window_posn += copy_length;
+                                     while (copy_length-- > 0) *rundest++ = *runsrc++;
+                                     runsrc = window;
+                                }
+                            }
+                            window_posn += match_length;
+ 
+                            /* copy match data - no worries about destination wraps */
+                            while (match_length-- > 0) *rundest++ = *runsrc++;
+
+                        }
+                    }
+                    break;
+
+                case LZX_BLOCKTYPE_UNCOMPRESSED:
+                    if ((inpos + this_run) > endinp) return DECR_ILLEGALDATA;
+                    memcpy(window + window_posn, inpos, (size_t) this_run);
+                    inpos += this_run; window_posn += this_run;
+                    break;
+
+                default:
+                    return DECR_ILLEGALDATA; /* might as well */
+            }
+
+        }
+    }
+
+    if (togo != 0) return DECR_ILLEGALDATA;
+    memcpy(outpos, window + ((!window_posn) ? window_size : window_posn) - outlen, (size_t) outlen);
+
+    pState->window_posn = window_posn;
+    pState->R0 = R0;
+    pState->R1 = R1;
+    pState->R2 = R2;
+
+    /* intel E8 decoding */
+    if ((pState->frames_read++ < 32768) && pState->intel_filesize != 0) {
+        if (outlen <= 6 || !pState->intel_started) {
+            pState->intel_curpos += outlen;
+        }
+        else {
+            UBYTE *data    = outpos;
+            UBYTE *dataend = data + outlen - 10;
+            LONG curpos    = pState->intel_curpos;
+            LONG filesize  = pState->intel_filesize;
+            LONG abs_off, rel_off;
+
+            pState->intel_curpos = curpos + outlen;
+
+            while (data < dataend) {
+                if (*data++ != 0xE8) { curpos++; continue; }
+                abs_off = data[0] | (data[1]<<8) | (data[2]<<16) | (data[3]<<24);
+                if ((abs_off >= -curpos) && (abs_off < filesize)) {
+                    rel_off = (abs_off >= 0) ? abs_off - curpos : abs_off + filesize;
+                    data[0] = (UBYTE) rel_off;
+                    data[1] = (UBYTE) (rel_off >> 8);
+                    data[2] = (UBYTE) (rel_off >> 16);
+                    data[3] = (UBYTE) (rel_off >> 24);
+                }
+                data += 4;
+                curpos += 5;
+            }
+        }
+    }
+    return DECR_OK;
+}
+
+#ifdef LZX_CHM_TESTDRIVER
+int main(int c, char **v)
+{
+    FILE *fin, *fout;
+    struct LZXstate state;
+    UBYTE ibuf[16384];
+    UBYTE obuf[32768];
+    int ilen, olen;
+    int status;
+    int i;
+    int count=0;
+    int w = atoi(v[1]);
+    LZXinit(&state, w);
+    fout = fopen(v[2], "wb");
+    for (i=3; i<c; i++)
+    {
+        fin = fopen(v[i], "rb");
+        ilen = fread(ibuf, 1, 16384, fin);
+        status = LZXdecompress(&state, ibuf, obuf, ilen, 32768);
+        switch (status)
+        {
+            case DECR_OK:
+                printf("ok\n");
+                fwrite(obuf, 1, 32768, fout);
+                break;
+            case DECR_DATAFORMAT:
+                printf("bad format\n");
+                break;
+            case DECR_ILLEGALDATA:
+                printf("illegal data\n");
+                break;
+            case DECR_NOMEMORY:
+                printf("no memory\n");
+                break;
+            default:
+                break;
+        }
+        fclose(fin);
+        if (++count == 2)
+        {
+            count = 0;
+            LZXreset(&state);
+        }
+    }
+    fclose(fout);
+}
+#endif
--- /dev/null	1994-07-18 08:46:18.000000000 +0900
+++ dlls/itss/lzx.h	2004-08-24 15:45:32.000000000 +0900
@@ -0,0 +1,62 @@
+/* $Id: lzx.h,v 1.1 2004/08/23 07:58:20 mike Exp $ */
+/***************************************************************************
+ *                        lzx.h - LZX decompression routines               *
+ *                           -------------------                           *
+ *                                                                         *
+ *  maintainer: Jed Wing <jedwin at ugcs.caltech.edu>                         *
+ *  source:     modified lzx.c from cabextract v0.5                        *
+ *  notes:      This file was taken from cabextract v0.5, which was,       *
+ *              itself, a modified version of the lzx decompression code   *
+ *              from unlzx.                                                *
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.  Note that an exemption to this   *
+ *   license has been granted by Stuart Caie for the purposes of           *
+ *   distribution with chmlib.  This does not, to the best of my           *
+ *   knowledge, constitute a change in the license of this (the LZX) code  *
+ *   in general.                                                           *
+ *                                                                         *
+ ***************************************************************************/
+
+#ifndef INCLUDED_LZX_H
+#define INCLUDED_LZX_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* return codes */
+#define DECR_OK           (0)
+#define DECR_DATAFORMAT   (1)
+#define DECR_ILLEGALDATA  (2)
+#define DECR_NOMEMORY     (3)
+
+/* opaque state structure */
+struct LZXstate;
+
+/* create an lzx state object */
+struct LZXstate *LZXinit(int window);
+
+/* destroy an lzx state object */
+void LZXteardown(struct LZXstate *pState);
+
+/* reset an lzx stream */
+int LZXreset(struct LZXstate *pState);
+
+/* decompress an LZX compressed block */
+int LZXdecompress(struct LZXstate *pState,
+                  unsigned char *inpos,
+                  unsigned char *outpos,
+                  int inlen,
+                  int outlen);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* INCLUDED_LZX_H */
--- /dev/null	1994-07-18 08:46:18.000000000 +0900
+++ dlls/itss/moniker.c	2004-08-24 15:45:39.000000000 +0900
@@ -0,0 +1,449 @@
+/*
+ *    ITSS Moniker implementation
+ *
+ * Copyright 2004 Mike McCormack
+ *
+ *  Implementation of the infamous mk:@MSITStore moniker
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "config.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winuser.h"
+#include "winnls.h"
+#include "winreg.h"
+#include "ole2.h"
+
+#include "itss.h"
+#include "uuids.h"
+
+#include "wine/unicode.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(itss);
+
+/*****************************************************************************/
+
+typedef struct {
+    IMonikerVtbl *vtbl_ITS_IMoniker;
+    DWORD ref;
+    LPWSTR szHtml;
+    WCHAR szFile[1];
+} ITS_IMonikerImpl;
+
+/*** IUnknown methods ***/
+static HRESULT WINAPI ITS_IMonikerImpl_QueryInterface(
+    IMoniker* iface,
+    REFIID riid,
+    void** ppvObject)
+{
+    ICOM_THIS(ITS_IMonikerImpl,iface);
+
+    if (IsEqualGUID(riid, &IID_IUnknown)
+	|| IsEqualGUID(riid, &IID_IParseDisplayName))
+    {
+	IClassFactory_AddRef(iface);
+	*ppvObject = This;
+	return S_OK;
+    }
+
+    WARN("(%p)->(%s,%p),not found\n",This,debugstr_guid(riid),ppvObject);
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI ITS_IMonikerImpl_AddRef(
+    IMoniker* iface)
+{
+    ICOM_THIS(ITS_IMonikerImpl,iface);
+    TRACE("%p\n", This);
+    return ++(This->ref);
+}
+
+static ULONG WINAPI ITS_IMonikerImpl_Release(
+    IMoniker* iface)
+{
+    ICOM_THIS(ITS_IMonikerImpl,iface);
+    ULONG ref = --This->ref;
+
+    if (ref == 0)
+	HeapFree(GetProcessHeap(), 0, This);
+
+    return ref;
+}
+
+/*** IPersist methods ***/
+static HRESULT WINAPI ITS_IMonikerImpl_GetClassID(
+    IMoniker* iface,
+    CLSID* pClassID)
+{
+    ICOM_THIS(ITS_IMonikerImpl,iface);
+
+    TRACE("%p %p\n", This, pClassID);
+    memcpy( pClassID, &CLSID_ITStorage, sizeof (CLSID) );
+    return S_OK;
+}
+
+/*** IPersistStream methods ***/
+static HRESULT WINAPI ITS_IMonikerImpl_IsDirty(
+    IMoniker* iface)
+{
+    FIXME("\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ITS_IMonikerImpl_Load(
+    IMoniker* iface,
+    IStream* pStm)
+{
+    FIXME("\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ITS_IMonikerImpl_Save(
+    IMoniker* iface,
+    IStream* pStm,
+    BOOL fClearDirty)
+{
+    FIXME("\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ITS_IMonikerImpl_GetSizeMax(
+    IMoniker* iface,
+    ULARGE_INTEGER* pcbSize)
+{
+    FIXME("\n");
+    return E_NOTIMPL;
+}
+
+/*** IMoniker methods ***/
+static HRESULT WINAPI ITS_IMonikerImpl_BindToObject(
+    IMoniker* iface,
+    IBindCtx* pbc,
+    IMoniker* pmkToLeft,
+    REFIID riidResult,
+    void** ppvResult)
+{
+    FIXME("\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ITS_IMonikerImpl_BindToStorage(
+    IMoniker* iface,
+    IBindCtx* pbc,
+    IMoniker* pmkToLeft,
+    REFIID riid,
+    void** ppvObj)
+{
+    FIXME("\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ITS_IMonikerImpl_Reduce(
+    IMoniker* iface,
+    IBindCtx* pbc,
+    DWORD dwReduceHowFar,
+    IMoniker** ppmkToLeft,
+    IMoniker** ppmkReduced)
+{
+    FIXME("\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ITS_IMonikerImpl_ComposeWith(
+    IMoniker* iface,
+    IMoniker* pmkRight,
+    BOOL fOnlyIfNotGeneric,
+    IMoniker** ppmkComposite)
+{
+    FIXME("\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ITS_IMonikerImpl_Enum(
+    IMoniker* iface,
+    BOOL fForward,
+    IEnumMoniker** ppenumMoniker)
+{
+    FIXME("\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ITS_IMonikerImpl_IsEqual(
+    IMoniker* iface,
+    IMoniker* pmkOtherMoniker)
+{
+    FIXME("\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ITS_IMonikerImpl_Hash(
+    IMoniker* iface,
+    DWORD* pdwHash)
+{
+    FIXME("\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ITS_IMonikerImpl_IsRunning(
+    IMoniker* iface,
+    IBindCtx* pbc,
+    IMoniker* pmkToLeft,
+    IMoniker* pmkNewlyRunning)
+{
+    FIXME("\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ITS_IMonikerImpl_GetTimeOfLastChange(
+    IMoniker* iface,
+    IBindCtx* pbc,
+    IMoniker* pmkToLeft,
+    FILETIME* pFileTime)
+{
+    FIXME("\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ITS_IMonikerImpl_Inverse(
+    IMoniker* iface,
+    IMoniker** ppmk)
+{
+    FIXME("\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ITS_IMonikerImpl_CommonPrefixWith(
+    IMoniker* iface,
+    IMoniker* pmkOther,
+    IMoniker** ppmkPrefix)
+{
+    FIXME("\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ITS_IMonikerImpl_RelativePathTo(
+    IMoniker* iface,
+    IMoniker* pmkOther,
+    IMoniker** ppmkRelPath)
+{
+    FIXME("\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ITS_IMonikerImpl_GetDisplayName(
+    IMoniker* iface,
+    IBindCtx* pbc,
+    IMoniker* pmkToLeft,
+    LPOLESTR* ppszDisplayName)
+{
+    ICOM_THIS(ITS_IMonikerImpl,iface);
+    static const WCHAR szFormat[] = {
+        'm','s','-','i','t','s',':','%','s',':',':','%','s',0 };
+    DWORD len = sizeof szFormat / sizeof(WCHAR);
+    LPWSTR str;
+
+    TRACE("%p %p %p %p\n", iface, pbc, pmkToLeft, ppszDisplayName);
+
+    len = strlenW( This->szFile ) + strlenW( This->szHtml );
+    str = CoTaskMemAlloc( len*sizeof(WCHAR) );
+    sprintfW( str, szFormat, This->szFile, This->szHtml );
+
+    *ppszDisplayName = str;
+    
+    return S_OK;
+}
+
+static HRESULT WINAPI ITS_IMonikerImpl_ParseDisplayName(
+    IMoniker* iface,
+    IBindCtx* pbc,
+    IMoniker* pmkToLeft,
+    LPOLESTR pszDisplayName,
+    ULONG* pchEaten,
+    IMoniker** ppmkOut)
+{
+    FIXME("\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ITS_IMonikerImpl_IsSystemMoniker(
+    IMoniker* iface,
+    DWORD* pdwMksys)
+{
+    FIXME("\n");
+    return E_NOTIMPL;
+}
+
+static IMonikerVtbl ITS_IMonikerImpl_Vtbl =
+{
+    ITS_IMonikerImpl_QueryInterface,
+    ITS_IMonikerImpl_AddRef,
+    ITS_IMonikerImpl_Release,
+    ITS_IMonikerImpl_GetClassID,
+    ITS_IMonikerImpl_IsDirty,
+    ITS_IMonikerImpl_Load,
+    ITS_IMonikerImpl_Save,
+    ITS_IMonikerImpl_GetSizeMax,
+    ITS_IMonikerImpl_BindToObject,
+    ITS_IMonikerImpl_BindToStorage,
+    ITS_IMonikerImpl_Reduce,
+    ITS_IMonikerImpl_ComposeWith,
+    ITS_IMonikerImpl_Enum,
+    ITS_IMonikerImpl_IsEqual,
+    ITS_IMonikerImpl_Hash,
+    ITS_IMonikerImpl_IsRunning,
+    ITS_IMonikerImpl_GetTimeOfLastChange,
+    ITS_IMonikerImpl_Inverse,
+    ITS_IMonikerImpl_CommonPrefixWith,
+    ITS_IMonikerImpl_RelativePathTo,
+    ITS_IMonikerImpl_GetDisplayName,
+    ITS_IMonikerImpl_ParseDisplayName,
+    ITS_IMonikerImpl_IsSystemMoniker
+};
+
+static HRESULT ITS_IMoniker_create( IMoniker **ppObj, LPWSTR name, DWORD n )
+{
+    ITS_IMonikerImpl *itsmon;
+    DWORD sz;
+
+    /* szFile[1] has space for one character already */
+    sz = sizeof(ITS_IMonikerImpl) + strlenW( name )*sizeof(WCHAR);
+
+    itsmon = HeapAlloc( GetProcessHeap(), 0, sz );
+    itsmon->vtbl_ITS_IMoniker = &ITS_IMonikerImpl_Vtbl;
+    itsmon->ref = 1;
+    strcpyW( itsmon->szFile, name );
+    itsmon->szHtml = &itsmon->szFile[n];
+
+    while( *itsmon->szHtml == ':' )
+        *itsmon->szHtml++ = 0;
+
+    TRACE("-> %p %s %s\n", itsmon,
+          debugstr_w(itsmon->szFile), debugstr_w(itsmon->szHtml) );
+    *ppObj = (IMoniker*) itsmon;
+
+    return S_OK;
+}
+
+/*****************************************************************************/
+
+typedef struct {
+    IParseDisplayNameVtbl *vtbl_ITS_IParseDisplayName;
+    DWORD ref;
+} ITS_IParseDisplayNameImpl;
+
+static HRESULT WINAPI ITS_IParseDisplayNameImpl_QueryInterface(
+    IParseDisplayName* iface,
+    REFIID riid,
+    void** ppvObject)
+{
+    ICOM_THIS(ITS_IParseDisplayNameImpl,iface);
+
+    if (IsEqualGUID(riid, &IID_IUnknown)
+	|| IsEqualGUID(riid, &IID_IParseDisplayName))
+    {
+	IClassFactory_AddRef(iface);
+	*ppvObject = This;
+	return S_OK;
+    }
+
+    WARN("(%p)->(%s,%p),not found\n",This,debugstr_guid(riid),ppvObject);
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI ITS_IParseDisplayNameImpl_AddRef(
+    IParseDisplayName* iface)
+{
+    ICOM_THIS(ITS_IParseDisplayNameImpl,iface);
+    TRACE("%p\n", This);
+    return ++(This->ref);
+}
+
+static ULONG WINAPI ITS_IParseDisplayNameImpl_Release(
+    IParseDisplayName* iface)
+{
+    ICOM_THIS(ITS_IParseDisplayNameImpl,iface);
+    ULONG ref = --This->ref;
+
+    if (ref == 0)
+	HeapFree(GetProcessHeap(), 0, This);
+
+    return ref;
+}
+
+static HRESULT WINAPI ITS_IParseDisplayNameImpl_ParseDisplayName(
+    IParseDisplayName *iface,
+    IBindCtx * pbc,
+    LPOLESTR pszDisplayName, 
+    ULONG * pchEaten,
+    IMoniker ** ppmkOut)
+{
+    static const WCHAR szPrefix[] = { 
+        '@','M','S','I','T','S','t','o','r','e',':',0 };
+    const DWORD prefix_len = (sizeof szPrefix/sizeof szPrefix[0])-1;
+    DWORD n;
+
+    ICOM_THIS(ITS_IParseDisplayNameImpl,iface);
+
+    TRACE("%p %s %p %p\n", This,
+          debugstr_w( pszDisplayName ), pchEaten, ppmkOut );
+
+    if( strncmpW( pszDisplayName, szPrefix, prefix_len ) )
+        return MK_E_SYNTAX;
+
+    /* search backwards for a double colon */
+    for( n = strlenW( pszDisplayName ) - 3; prefix_len <= n; n-- )
+        if( ( pszDisplayName[n] == ':' ) && ( pszDisplayName[n+1] == ':' ) )
+            break;
+
+    if( n < prefix_len )
+        return MK_E_SYNTAX;
+
+    if( !pszDisplayName[n+2] )
+        return MK_E_SYNTAX;
+
+    *pchEaten = strlenW( pszDisplayName ) - n - 3;
+
+    return ITS_IMoniker_create( ppmkOut,
+              &pszDisplayName[prefix_len], n-prefix_len );
+}
+
+static IParseDisplayNameVtbl ITS_IParseDisplayNameImpl_Vtbl =
+{
+    ITS_IParseDisplayNameImpl_QueryInterface,
+    ITS_IParseDisplayNameImpl_AddRef,
+    ITS_IParseDisplayNameImpl_Release,
+    ITS_IParseDisplayNameImpl_ParseDisplayName
+};
+ 
+HRESULT ITS_IParseDisplayName_create(IUnknown *pUnkOuter, LPVOID *ppObj)
+{
+    ITS_IParseDisplayNameImpl *its;
+
+    its = HeapAlloc( GetProcessHeap(), 0, sizeof(ITS_IParseDisplayNameImpl) );
+    its->vtbl_ITS_IParseDisplayName = &ITS_IParseDisplayNameImpl_Vtbl;
+    its->ref = 1;
+
+    TRACE("-> %p\n", its);
+    *ppObj = (LPVOID) its;
+
+    return S_OK;
+}
--- /dev/null	1994-07-18 08:46:18.000000000 +0900
+++ dlls/itss/storage.c	2004-08-24 15:45:46.000000000 +0900
@@ -0,0 +1,800 @@
+/*
+ *    ITSS Storage implementation
+ *
+ * Copyright 2004 Mike McCormack
+ *
+ *  see http://bonedaddy.net/pabs3/hhm/#chmspec
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "config.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winuser.h"
+#include "winnls.h"
+#include "winreg.h"
+#include "ole2.h"
+
+#include "uuids.h"
+
+#include "itss.h"
+#include "chm_lib.h"
+
+#include "wine/unicode.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(itss);
+
+#include "initguid.h"
+
+/************************************************************************/
+
+typedef struct _ITSS_IStorageImpl
+{
+    IStorageVtbl *vtbl_IStorage;
+    DWORD ref;
+    struct chmFile *chmfile;
+    WCHAR dir[1];
+} ITSS_IStorageImpl;
+
+struct enum_info
+{
+    struct enum_info *next, *prev;
+    struct chmUnitInfo ui;
+};
+
+typedef struct _IEnumSTATSTG_Impl
+{
+    IEnumSTATSTGVtbl *vtbl_IEnumSTATSTG;
+    DWORD ref;
+    struct enum_info *first, *last, *current;
+} IEnumSTATSTG_Impl;
+
+typedef struct _IStream_Impl
+{
+    IStreamVtbl *vtbl_IStream;
+    DWORD ref;
+    ITSS_IStorageImpl *stg;
+    ULONGLONG addr;
+    struct chmUnitInfo ui;
+} IStream_Impl;
+
+static HRESULT ITSS_create_chm_storage(
+           struct chmFile *chmfile, const WCHAR *dir, IStorage** ppstgOpen );
+static IStream_Impl* ITSS_create_stream( 
+           ITSS_IStorageImpl *stg, struct chmUnitInfo *ui );
+
+/************************************************************************/
+
+static HRESULT WINAPI ITSS_IEnumSTATSTG_QueryInterface(
+    IEnumSTATSTG* iface,
+    REFIID riid,
+    void** ppvObject)
+{
+    ICOM_THIS(IEnumSTATSTG_Impl,iface);
+
+    if (IsEqualGUID(riid, &IID_IUnknown)
+	|| IsEqualGUID(riid, &IID_IEnumSTATSTG))
+    {
+	IEnumSTATSTG_AddRef(iface);
+	*ppvObject = This;
+	return S_OK;
+    }
+
+    WARN("(%p)->(%s,%p),not found\n",This,debugstr_guid(riid),ppvObject);
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI ITSS_IEnumSTATSTG_AddRef(
+    IEnumSTATSTG* iface)
+{
+    ICOM_THIS(IEnumSTATSTG_Impl,iface);
+    return ++(This->ref);
+}
+
+static ULONG WINAPI ITSS_IEnumSTATSTG_Release(
+    IEnumSTATSTG* iface)
+{
+    ICOM_THIS(IEnumSTATSTG_Impl,iface);
+
+    ULONG ref = --This->ref;
+
+    if (ref == 0)
+    {
+        while( This->first )
+        {
+            struct enum_info *t = This->first->next;
+            HeapFree( GetProcessHeap(), 0, This->first );
+            This->first = t;
+        }
+	HeapFree(GetProcessHeap(), 0, This);
+    }
+
+    return ref;
+}
+
+static HRESULT WINAPI ITSS_IEnumSTATSTG_Next(
+        IEnumSTATSTG* iface,
+        ULONG celt,
+        STATSTG* rgelt,
+        ULONG* pceltFetched)
+{
+    ICOM_THIS(IEnumSTATSTG_Impl,iface);
+    DWORD len, n;
+    struct enum_info *cur;
+
+    TRACE("%p %lu %p %p\n", This, celt, rgelt, pceltFetched );
+
+    cur = This->current;
+    n = 0;
+    while( (n<celt) && cur) 
+    {
+        WCHAR *str;
+
+        memset( rgelt, 0, sizeof *rgelt );
+
+        /* copy the name */
+        str = cur->ui.path;
+        if( *str == '/' )
+            str++;
+        len = strlenW( str ) + 1;
+        rgelt->pwcsName = CoTaskMemAlloc( len*sizeof(WCHAR) );
+        strcpyW( rgelt->pwcsName, str );
+
+        /* determine the type */
+        if( rgelt->pwcsName[len-2] == '/' )
+        {
+            rgelt->pwcsName[len-2] = 0;
+            rgelt->type = STGTY_STORAGE;
+        }
+        else
+            rgelt->type = STGTY_STREAM;
+
+        /* copy the size */
+        rgelt->cbSize.QuadPart = cur->ui.length;
+
+        /* advance to the next item if it exists */
+        n++;
+        cur = cur->next;
+    }
+
+    This->current = cur;
+    *pceltFetched = n;
+
+    if( n < celt )
+        return S_FALSE;
+
+    return S_OK;
+}
+
+static HRESULT WINAPI ITSS_IEnumSTATSTG_Skip(
+        IEnumSTATSTG* iface,
+        ULONG celt)
+{
+    ICOM_THIS(IEnumSTATSTG_Impl,iface);
+    DWORD n;
+    struct enum_info *cur;
+
+    TRACE("%p %lu\n", This, celt );
+
+    cur = This->current;
+    n = 0;
+    while( (n<celt) && cur) 
+    {
+        n++;
+        cur = cur->next;
+    }
+    This->current = cur;
+
+    if( n < celt )
+        return S_FALSE;
+
+    return S_OK;
+}
+
+static HRESULT WINAPI ITSS_IEnumSTATSTG_Reset(
+        IEnumSTATSTG* iface)
+{
+    ICOM_THIS(IEnumSTATSTG_Impl,iface);
+
+    TRACE("%p\n", This );
+
+    This->current = This->first;
+
+    return S_OK;
+}
+
+static HRESULT WINAPI ITSS_IEnumSTATSTG_Clone(
+        IEnumSTATSTG* iface,
+        IEnumSTATSTG** ppenum)
+{
+    FIXME("\n");
+    return E_NOTIMPL;
+}
+
+struct IEnumSTATSTGVtbl IEnumSTATSTG_vtbl =
+{
+    ITSS_IEnumSTATSTG_QueryInterface,
+    ITSS_IEnumSTATSTG_AddRef,
+    ITSS_IEnumSTATSTG_Release,
+    ITSS_IEnumSTATSTG_Next,
+    ITSS_IEnumSTATSTG_Skip,
+    ITSS_IEnumSTATSTG_Reset,
+    ITSS_IEnumSTATSTG_Clone
+};
+
+static IEnumSTATSTG_Impl *ITSS_create_enum( void )
+{
+    IEnumSTATSTG_Impl *stgenum;
+
+    stgenum = HeapAlloc( GetProcessHeap(), 0, sizeof (IEnumSTATSTG_Impl) );
+    stgenum->vtbl_IEnumSTATSTG = &IEnumSTATSTG_vtbl;
+    stgenum->ref = 1;
+    stgenum->first = NULL;
+    stgenum->last = NULL;
+    stgenum->current = NULL;
+
+    TRACE(" -> %p\n", stgenum );
+
+    return stgenum;
+}
+
+/************************************************************************/
+
+HRESULT WINAPI ITSS_IStorageImpl_QueryInterface(
+    IStorage* iface,
+    REFIID riid,
+    void** ppvObject)
+{
+    ICOM_THIS(ITSS_IStorageImpl,iface);
+
+    if (IsEqualGUID(riid, &IID_IUnknown)
+	|| IsEqualGUID(riid, &IID_IStorage))
+    {
+	IStorage_AddRef(iface);
+	*ppvObject = This;
+	return S_OK;
+    }
+
+    WARN("(%p)->(%s,%p),not found\n",This,debugstr_guid(riid),ppvObject);
+    return E_NOINTERFACE;
+}
+
+ULONG WINAPI ITSS_IStorageImpl_AddRef(
+    IStorage* iface)
+{
+    ICOM_THIS(ITSS_IStorageImpl,iface);
+    return ++(This->ref);
+}
+
+ULONG WINAPI ITSS_IStorageImpl_Release(
+    IStorage* iface)
+{
+    ICOM_THIS(ITSS_IStorageImpl,iface);
+
+    ULONG ref = --This->ref;
+
+    if (ref == 0)
+    {
+	HeapFree(GetProcessHeap(), 0, This);
+    }
+
+    return ref;
+}
+
+HRESULT WINAPI ITSS_IStorageImpl_CreateStream(
+    IStorage* iface,
+    LPCOLESTR pwcsName,
+    DWORD grfMode,
+    DWORD reserved1,
+    DWORD reserved2,
+    IStream** ppstm)
+{
+    FIXME("\n");
+    return E_NOTIMPL;
+}
+
+HRESULT WINAPI ITSS_IStorageImpl_OpenStream(
+    IStorage* iface,
+    LPCOLESTR pwcsName,
+    void* reserved1,
+    DWORD grfMode,
+    DWORD reserved2,
+    IStream** ppstm)
+{
+    ICOM_THIS(ITSS_IStorageImpl,iface);
+    IStream_Impl *stm;
+    DWORD len;
+    struct chmUnitInfo ui;
+    int r;
+    WCHAR *path;
+
+    TRACE("%p %s %p %lu %lu %p\n", This, debugstr_w(pwcsName),
+          reserved1, grfMode, reserved2, ppstm );
+
+    len = strlenW( This->dir ) + strlenW( pwcsName ) + 1;
+    path = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
+    strcpyW( path, This->dir );
+    strcatW( path, pwcsName );
+
+    r = chm_resolve_object(This->chmfile, path, &ui);
+    HeapFree( GetProcessHeap(), 0, path );
+
+    if( r != CHM_RESOLVE_SUCCESS )
+        return STG_E_FILENOTFOUND;
+
+    stm = ITSS_create_stream( This, &ui );
+    if( !stm )
+        return E_FAIL;
+
+    *ppstm = (IStream*) stm;
+
+    return S_OK;
+}
+
+HRESULT WINAPI ITSS_IStorageImpl_CreateStorage(
+    IStorage* iface,
+    LPCOLESTR pwcsName,
+    DWORD grfMode,
+    DWORD dwStgFmt,
+    DWORD reserved2,
+    IStorage** ppstg)
+{
+    FIXME("\n");
+    return E_NOTIMPL;
+}
+
+HRESULT WINAPI ITSS_IStorageImpl_OpenStorage(
+    IStorage* iface,
+    LPCOLESTR pwcsName,
+    IStorage* pstgPriority,
+    DWORD grfMode,
+    SNB snbExclude,
+    DWORD reserved,
+    IStorage** ppstg)
+{
+    FIXME("\n");
+    return E_NOTIMPL;
+}
+
+HRESULT WINAPI ITSS_IStorageImpl_CopyTo(
+    IStorage* iface,
+    DWORD ciidExclude,
+    const IID* rgiidExclude,
+    SNB snbExclude,
+    IStorage* pstgDest)
+{
+    FIXME("\n");
+    return E_NOTIMPL;
+}
+
+HRESULT WINAPI ITSS_IStorageImpl_MoveElementTo(
+    IStorage* iface,
+    LPCOLESTR pwcsName,
+    IStorage* pstgDest,
+    LPCOLESTR pwcsNewName,
+    DWORD grfFlags)
+{
+    FIXME("\n");
+    return E_NOTIMPL;
+}
+
+HRESULT WINAPI ITSS_IStorageImpl_Commit(
+    IStorage* iface,
+    DWORD grfCommitFlags)
+{
+    FIXME("\n");
+    return E_NOTIMPL;
+}
+
+HRESULT WINAPI ITSS_IStorageImpl_Revert(
+    IStorage* iface)
+{
+    FIXME("\n");
+    return E_NOTIMPL;
+}
+
+static int ITSS_chm_enumerator(
+    struct chmFile *h,
+    struct chmUnitInfo *ui,
+    void *context)
+{
+    struct enum_info *info;
+    IEnumSTATSTG_Impl* stgenum = context;
+
+    TRACE("adding %s to enumeration\n", debugstr_w(ui->path) );
+
+    info = HeapAlloc( GetProcessHeap(), 0, sizeof (struct enum_info) );
+    memcpy( &info->ui, ui, sizeof info->ui );
+
+    info->next = NULL;
+    info->prev = stgenum->last;
+    if( stgenum->last )
+        stgenum->last->next = info;
+    else
+        stgenum->first = info;
+    stgenum->last = info;
+    
+    return CHM_ENUMERATOR_CONTINUE;
+}
+
+HRESULT WINAPI ITSS_IStorageImpl_EnumElements(
+    IStorage* iface,
+    DWORD reserved1,
+    void* reserved2,
+    DWORD reserved3,
+    IEnumSTATSTG** ppenum)
+{
+    ICOM_THIS(ITSS_IStorageImpl,iface);
+    IEnumSTATSTG_Impl* stgenum;
+
+    TRACE("%p %ld %p %ld %p\n", This, reserved1, reserved2, reserved3, ppenum );
+
+    stgenum = ITSS_create_enum();
+    if( !stgenum )
+        return E_FAIL;
+
+    chm_enumerate_dir(This->chmfile,
+                  This->dir,
+                  CHM_ENUMERATE_ALL,
+                  ITSS_chm_enumerator,
+                  stgenum );
+
+    stgenum->current = stgenum->first;
+
+    *ppenum = (IEnumSTATSTG*) stgenum;
+
+    return S_OK;
+}
+
+HRESULT WINAPI ITSS_IStorageImpl_DestroyElement(
+    IStorage* iface,
+    LPCOLESTR pwcsName)
+{
+    FIXME("\n");
+    return E_NOTIMPL;
+}
+
+HRESULT WINAPI ITSS_IStorageImpl_RenameElement(
+    IStorage* iface,
+    LPCOLESTR pwcsOldName,
+    LPCOLESTR pwcsNewName)
+{
+    FIXME("\n");
+    return E_NOTIMPL;
+}
+
+HRESULT WINAPI ITSS_IStorageImpl_SetElementTimes(
+    IStorage* iface,
+    LPCOLESTR pwcsName,
+    const FILETIME* pctime,
+    const FILETIME* patime,
+    const FILETIME* pmtime)
+{
+    FIXME("\n");
+    return E_NOTIMPL;
+}
+
+HRESULT WINAPI ITSS_IStorageImpl_SetClass(
+    IStorage* iface,
+    REFCLSID clsid)
+{
+    FIXME("\n");
+    return E_NOTIMPL;
+}
+
+HRESULT WINAPI ITSS_IStorageImpl_SetStateBits(
+    IStorage* iface,
+    DWORD grfStateBits,
+    DWORD grfMask)
+{
+    FIXME("\n");
+    return E_NOTIMPL;
+}
+
+HRESULT WINAPI ITSS_IStorageImpl_Stat(
+    IStorage* iface,
+    STATSTG* pstatstg,
+    DWORD grfStatFlag)
+{
+    FIXME("\n");
+    return E_NOTIMPL;
+}
+
+static IStorageVtbl ITSS_IStorageImpl_Vtbl =
+{
+    ITSS_IStorageImpl_QueryInterface,
+    ITSS_IStorageImpl_AddRef,
+    ITSS_IStorageImpl_Release,
+    ITSS_IStorageImpl_CreateStream,
+    ITSS_IStorageImpl_OpenStream,
+    ITSS_IStorageImpl_CreateStorage,
+    ITSS_IStorageImpl_OpenStorage,
+    ITSS_IStorageImpl_CopyTo,
+    ITSS_IStorageImpl_MoveElementTo,
+    ITSS_IStorageImpl_Commit,
+    ITSS_IStorageImpl_Revert,
+    ITSS_IStorageImpl_EnumElements,
+    ITSS_IStorageImpl_DestroyElement,
+    ITSS_IStorageImpl_RenameElement,
+    ITSS_IStorageImpl_SetElementTimes,
+    ITSS_IStorageImpl_SetClass,
+    ITSS_IStorageImpl_SetStateBits,
+    ITSS_IStorageImpl_Stat,
+};
+
+static HRESULT ITSS_create_chm_storage(
+      struct chmFile *chmfile, const WCHAR *dir, IStorage** ppstgOpen )
+{
+    ITSS_IStorageImpl *stg;
+    DWORD len;
+
+    TRACE("%p %s\n", chmfile, debugstr_w( dir ) );
+
+    len = strlenW( dir ) + 1;
+    stg = HeapAlloc( GetProcessHeap(), 0, 
+                     sizeof (ITSS_IStorageImpl) + len*sizeof(WCHAR) );
+    stg->vtbl_IStorage = &ITSS_IStorageImpl_Vtbl;
+    stg->ref = 1;
+    stg->chmfile = chmfile;
+    strcpyW( stg->dir, dir );
+
+    *ppstgOpen = (IStorage*) stg;
+
+    return S_OK;
+}
+
+HRESULT ITSS_StgOpenStorage( 
+    const WCHAR* pwcsName,
+    IStorage* pstgPriority,
+    DWORD grfMode,
+    SNB snbExclude,
+    DWORD reserved,
+    IStorage** ppstgOpen)
+{
+    struct chmFile *chmfile;
+    static const WCHAR szRoot[] = { '/', 0 };
+
+    TRACE("%s\n", debugstr_w(pwcsName) );
+
+    chmfile = chm_openW( pwcsName );
+    if( !chmfile )
+        return E_FAIL;
+
+    return ITSS_create_chm_storage( chmfile, szRoot, ppstgOpen );
+}
+
+/************************************************************************/
+
+static HRESULT WINAPI ITSS_IStream_QueryInterface(
+    IStream* iface,
+    REFIID riid,
+    void** ppvObject)
+{
+    ICOM_THIS(IStream_Impl,iface);
+
+    if (IsEqualGUID(riid, &IID_IUnknown)
+	|| IsEqualGUID(riid, &IID_ISequentialStream)
+	|| IsEqualGUID(riid, &IID_IStream))
+    {
+	IStream_AddRef(iface);
+	*ppvObject = This;
+	return S_OK;
+    }
+
+    WARN("(%p)->(%s,%p),not found\n",This,debugstr_guid(riid),ppvObject);
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI ITSS_IStream_AddRef(
+    IStream* iface)
+{
+    ICOM_THIS(IStream_Impl,iface);
+    return ++(This->ref);
+}
+
+static ULONG WINAPI ITSS_IStream_Release(
+    IStream* iface)
+{
+    ICOM_THIS(IStream_Impl,iface);
+
+    ULONG ref = --This->ref;
+
+    if (ref == 0)
+    {
+        IStorage_Release( (IStorage*) This->stg );
+	HeapFree(GetProcessHeap(), 0, This);
+    }
+
+    return ref;
+}
+
+static HRESULT WINAPI ITSS_IStream_Read(
+        IStream* iface,
+        void* pv,
+        ULONG cb,
+        ULONG* pcbRead)
+{
+    ICOM_THIS(IStream_Impl,iface);
+    ULONG count;
+
+    TRACE("%p %p %lu %p\n", This, pv, cb, pcbRead);
+
+    count = chm_retrieve_object(This->stg->chmfile, 
+                          &This->ui, pv, This->addr, cb);
+    This->addr += count;
+    if( pcbRead )
+        *pcbRead = count;
+    
+    return S_OK;
+}
+
+static HRESULT WINAPI ITSS_IStream_Write(
+        IStream* iface,
+        const void* pv,
+        ULONG cb,
+        ULONG* pcbWritten)
+{
+    FIXME("\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ITSS_IStream_Seek(
+        IStream* iface,
+        LARGE_INTEGER dlibMove,
+        DWORD dwOrigin,
+        ULARGE_INTEGER* plibNewPosition)
+{
+    ICOM_THIS(IStream_Impl,iface);
+    LONGLONG newpos;
+
+    TRACE("%p %s %lu %p\n", This,
+          wine_dbgstr_longlong( dlibMove.QuadPart ), dwOrigin, plibNewPosition );
+
+    newpos = This->addr;
+    switch( dwOrigin )
+    {
+    case STREAM_SEEK_CUR:
+        newpos = This->addr + dlibMove.QuadPart;
+        break;
+    case STREAM_SEEK_SET:
+        newpos = dlibMove.QuadPart;
+        break;
+    case STREAM_SEEK_END:
+        newpos = This->ui.length + dlibMove.QuadPart;
+        break;
+    }
+
+    if( ( newpos < 0 ) || ( newpos > This->ui.length ) )
+        return STG_E_INVALIDPOINTER;
+
+    This->addr = newpos;
+    if( plibNewPosition )
+        plibNewPosition->QuadPart = This->addr;
+
+    return S_OK;
+}
+
+static HRESULT WINAPI ITSS_IStream_SetSize(
+        IStream* iface,
+        ULARGE_INTEGER libNewSize)
+{
+    FIXME("\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ITSS_IStream_CopyTo(
+        IStream* iface,
+        IStream* pstm,
+        ULARGE_INTEGER cb,
+        ULARGE_INTEGER* pcbRead,
+        ULARGE_INTEGER* pcbWritten)
+{
+    FIXME("\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ITSS_IStream_Commit(
+        IStream* iface,
+        DWORD grfCommitFlags)
+{
+    FIXME("\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ITSS_IStream_Revert(
+        IStream* iface)
+{
+    FIXME("\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ITSS_IStream_LockRegion(
+        IStream* iface,
+        ULARGE_INTEGER libOffset,
+        ULARGE_INTEGER cb,
+        DWORD dwLockType)
+{
+    FIXME("\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ITSS_IStream_UnlockRegion(
+        IStream* iface,
+        ULARGE_INTEGER libOffset,
+        ULARGE_INTEGER cb,
+        DWORD dwLockType)
+{
+    FIXME("\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ITSS_IStream_Stat(
+        IStream* iface,
+        STATSTG* pstatstg,
+        DWORD grfStatFlag)
+{
+    FIXME("\n");
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI ITSS_IStream_Clone(
+        IStream* iface,
+        IStream** ppstm)
+{
+    FIXME("\n");
+    return E_NOTIMPL;
+}
+
+struct IStreamVtbl ITSS_IStream_vtbl =
+{
+    ITSS_IStream_QueryInterface,
+    ITSS_IStream_AddRef,
+    ITSS_IStream_Release,
+    ITSS_IStream_Read,
+    ITSS_IStream_Write,
+    ITSS_IStream_Seek,
+    ITSS_IStream_SetSize,
+    ITSS_IStream_CopyTo,
+    ITSS_IStream_Commit,
+    ITSS_IStream_Revert,
+    ITSS_IStream_LockRegion,
+    ITSS_IStream_UnlockRegion,
+    ITSS_IStream_Stat,
+    ITSS_IStream_Clone,
+};
+
+static IStream_Impl *ITSS_create_stream(
+           ITSS_IStorageImpl *stg, struct chmUnitInfo *ui )
+{
+    IStream_Impl *stm;
+
+    stm = HeapAlloc( GetProcessHeap(), 0, sizeof (IStream_Impl) );
+    stm->vtbl_IStream = &ITSS_IStream_vtbl;
+    stm->ref = 1;
+    stm->addr = 0;
+    memcpy( &stm->ui, ui, sizeof stm->ui );
+    stm->stg = stg;
+    IStorage_AddRef( (IStorage*) stg );
+
+    TRACE(" -> %p\n", stm );
+
+    return stm;
+}


More information about the wine-patches mailing list