Authors: Stewart Caie<kyz@uklinux.net>, Jed Wing <jedwin@ugcs.caltech.edu>, Mike McCormack <mike@codeweavers.com>
Added an implemenation of ITSS.DLL.

diff --git a/configure b/configure
index 9a03b3d..2c09cb8 100755
--- a/configure
+++ b/configure
@@ -19955,7 +19955,7 @@
 MAKE_PROG_RULES=programs/Makeprog.rules
 
 
-                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    ac_config_files="$ac_config_files Make.rules dlls/Makedll.rules dlls/Maketest.rules libs/Makelib.rules programs/Makeprog.rules Makefile dlls/Makefile dlls/advapi32/Makefile dlls/advapi32/tests/Makefile dlls/amstream/Makefile dlls/atl/Makefile dlls/avicap32/Makefile dlls/avifil32/Makefile dlls/cabinet/Makefile dlls/capi2032/Makefile dlls/cards/Makefile dlls/cfgmgr32/Makefile dlls/comcat/Makefile dlls/comctl32/Makefile dlls/comctl32/tests/Makefile dlls/commdlg/Makefile dlls/crtdll/Makefile dlls/crypt32/Makefile dlls/ctl3d/Makefile dlls/d3d8/Makefile dlls/d3d9/Makefile dlls/d3dim/Makefile dlls/d3drm/Makefile dlls/d3dx8/Makefile dlls/d3dxof/Makefile dlls/dbghelp/Makefile dlls/dciman32/Makefile dlls/ddraw/Makefile dlls/ddraw/tests/Makefile dlls/devenum/Makefile dlls/dinput/Makefile dlls/dinput8/Makefile dlls/dmband/Makefile dlls/dmcompos/Makefile dlls/dmime/Makefile dlls/dmloader/Makefile dlls/dmscript/Makefile dlls/dmstyle/Makefile dlls/dmsynth/Makefile dlls/dmusic/Makefile dlls/dmusic32/Makefile dlls/dplay/Makefile dlls/dplayx/Makefile dlls/dpnet/Makefile dlls/dpnhpast/Makefile dlls/dsound/Makefile dlls/dsound/tests/Makefile dlls/dswave/Makefile dlls/dxdiagn/Makefile dlls/dxerr8/Makefile dlls/dxerr9/Makefile dlls/dxguid/Makefile dlls/gdi/Makefile dlls/gdi/tests/Makefile dlls/glu32/Makefile dlls/glut32/Makefile dlls/hhctrl.ocx/Makefile dlls/iccvid/Makefile dlls/icmp/Makefile dlls/ifsmgr.vxd/Makefile dlls/imagehlp/Makefile dlls/imm32/Makefile dlls/iphlpapi/Makefile dlls/iphlpapi/tests/Makefile dlls/kernel/Makefile dlls/kernel/tests/Makefile dlls/lzexpand/Makefile dlls/mapi32/Makefile dlls/mapi32/tests/Makefile dlls/mlang/Makefile dlls/mlang/tests/Makefile dlls/mmdevldr.vxd/Makefile dlls/monodebg.vxd/Makefile dlls/mpr/Makefile dlls/msacm/Makefile dlls/msacm/imaadp32/Makefile dlls/msacm/msadp32/Makefile dlls/msacm/msg711/Makefile dlls/msacm/winemp3/Makefile dlls/msacm/tests/Makefile dlls/msdmo/Makefile dlls/mshtml/Makefile dlls/msi/Makefile dlls/msimg32/Makefile dlls/msisys/Makefile dlls/msnet32/Makefile dlls/msrle32/Makefile dlls/msvcrt/Makefile dlls/msvcrt/tests/Makefile dlls/msvcrt20/Makefile dlls/msvcrt40/Makefile dlls/msvcrtd/Makefile dlls/msvcrtd/tests/Makefile dlls/msvidc32/Makefile dlls/msvideo/Makefile dlls/mswsock/Makefile dlls/netapi32/Makefile dlls/netapi32/tests/Makefile dlls/newdev/Makefile dlls/ntdll/Makefile dlls/ntdll/tests/Makefile dlls/odbc32/Makefile dlls/ole32/Makefile dlls/ole32/tests/Makefile dlls/oleacc/Makefile dlls/oleaut32/Makefile dlls/oleaut32/tests/Makefile dlls/olecli/Makefile dlls/oledlg/Makefile dlls/olepro32/Makefile dlls/olesvr/Makefile dlls/opengl32/Makefile dlls/psapi/Makefile dlls/psapi/tests/Makefile dlls/qcap/Makefile dlls/quartz/Makefile dlls/quartz/tests/Makefile dlls/rasapi32/Makefile dlls/richedit/Makefile dlls/rpcrt4/Makefile dlls/rpcrt4/tests/Makefile dlls/rsabase/Makefile dlls/rsabase/tests/Makefile dlls/secur32/Makefile dlls/serialui/Makefile dlls/setupapi/Makefile dlls/shdocvw/Makefile dlls/shell32/Makefile dlls/shell32/tests/Makefile dlls/shfolder/Makefile dlls/shlwapi/Makefile dlls/shlwapi/tests/Makefile dlls/snmpapi/Makefile dlls/sti/Makefile dlls/strmiids/Makefile dlls/tapi32/Makefile dlls/ttydrv/Makefile dlls/twain/Makefile dlls/unicows/Makefile dlls/url/Makefile dlls/urlmon/Makefile dlls/urlmon/tests/Makefile dlls/user/Makefile dlls/user/tests/Makefile dlls/uuid/Makefile dlls/uxtheme/Makefile dlls/vdhcp.vxd/Makefile dlls/vdmdbg/Makefile dlls/version/Makefile dlls/version/tests/Makefile dlls/vmm.vxd/Makefile dlls/vnbt.vxd/Makefile dlls/vnetbios.vxd/Makefile dlls/vtdapi.vxd/Makefile dlls/vwin32.vxd/Makefile dlls/win32s/Makefile dlls/winaspi/Makefile dlls/wined3d/Makefile dlls/winedos/Makefile dlls/wineps/Makefile dlls/wininet/Makefile dlls/wininet/tests/Makefile dlls/winmm/Makefile dlls/winmm/joystick/Makefile dlls/winmm/mcianim/Makefile dlls/winmm/mciavi/Makefile dlls/winmm/mcicda/Makefile dlls/winmm/mciseq/Makefile dlls/winmm/mciwave/Makefile dlls/winmm/midimap/Makefile dlls/winmm/tests/Makefile dlls/winmm/wavemap/Makefile dlls/winmm/winealsa/Makefile dlls/winmm/winearts/Makefile dlls/winmm/wineaudioio/Makefile dlls/winmm/winejack/Makefile dlls/winmm/winenas/Makefile dlls/winmm/wineoss/Makefile dlls/winnls/Makefile dlls/winsock/Makefile dlls/winsock/tests/Makefile dlls/winspool/Makefile dlls/winspool/tests/Makefile dlls/wintab32/Makefile dlls/wintrust/Makefile dlls/wow32/Makefile dlls/wsock32/Makefile dlls/x11drv/Makefile documentation/Makefile fonts/Makefile include/Makefile libs/Makefile libs/port/Makefile libs/unicode/Makefile libs/wine/Makefile libs/wpp/Makefile loader/Makefile programs/Makefile programs/avitools/Makefile programs/clock/Makefile programs/cmdlgtst/Makefile programs/control/Makefile programs/expand/Makefile programs/msiexec/Makefile programs/notepad/Makefile programs/progman/Makefile programs/regedit/Makefile programs/regsvr32/Makefile programs/rpcss/Makefile programs/rundll32/Makefile programs/start/Makefile programs/taskmgr/Makefile programs/uninstaller/Makefile programs/view/Makefile programs/wcmd/Makefile programs/wineboot/Makefile programs/winebrowser/Makefile programs/winecfg/Makefile programs/wineconsole/Makefile programs/winedbg/Makefile programs/winefile/Makefile programs/winemenubuilder/Makefile programs/winemine/Makefile programs/winepath/Makefile programs/winetest/Makefile programs/winevdm/Makefile programs/winhelp/Makefile programs/winver/Makefile server/Makefile tools/Makefile tools/widl/Makefile tools/winapi/Makefile tools/winebuild/Makefile tools/winedump/Makefile tools/winegcc/Makefile tools/wmc/Makefile tools/wrc/Makefile"
+                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              ac_config_files="$ac_config_files Make.rules dlls/Makedll.rules dlls/Maketest.rules libs/Makelib.rules programs/Makeprog.rules Makefile dlls/Makefile dlls/advapi32/Makefile dlls/advapi32/tests/Makefile dlls/amstream/Makefile dlls/atl/Makefile dlls/avicap32/Makefile dlls/avifil32/Makefile dlls/cabinet/Makefile dlls/capi2032/Makefile dlls/cards/Makefile dlls/cfgmgr32/Makefile dlls/comcat/Makefile dlls/comctl32/Makefile dlls/comctl32/tests/Makefile dlls/commdlg/Makefile dlls/crtdll/Makefile dlls/crypt32/Makefile dlls/ctl3d/Makefile dlls/d3d8/Makefile dlls/d3d9/Makefile dlls/d3dim/Makefile dlls/d3drm/Makefile dlls/d3dx8/Makefile dlls/d3dxof/Makefile dlls/dbghelp/Makefile dlls/dciman32/Makefile dlls/ddraw/Makefile dlls/ddraw/tests/Makefile dlls/devenum/Makefile dlls/dinput/Makefile dlls/dinput8/Makefile dlls/dmband/Makefile dlls/dmcompos/Makefile dlls/dmime/Makefile dlls/dmloader/Makefile dlls/dmscript/Makefile dlls/dmstyle/Makefile dlls/dmsynth/Makefile dlls/dmusic/Makefile dlls/dmusic32/Makefile dlls/dplay/Makefile dlls/dplayx/Makefile dlls/dpnet/Makefile dlls/dpnhpast/Makefile dlls/dsound/Makefile dlls/dsound/tests/Makefile dlls/dswave/Makefile dlls/dxdiagn/Makefile dlls/dxerr8/Makefile dlls/dxerr9/Makefile dlls/dxguid/Makefile dlls/gdi/Makefile dlls/gdi/tests/Makefile dlls/glu32/Makefile dlls/glut32/Makefile dlls/hhctrl.ocx/Makefile dlls/iccvid/Makefile dlls/icmp/Makefile dlls/ifsmgr.vxd/Makefile dlls/imagehlp/Makefile dlls/imm32/Makefile dlls/iphlpapi/Makefile dlls/iphlpapi/tests/Makefile dlls/itss/Makefile dlls/kernel/Makefile dlls/kernel/tests/Makefile dlls/lzexpand/Makefile dlls/mapi32/Makefile dlls/mapi32/tests/Makefile dlls/mlang/Makefile dlls/mlang/tests/Makefile dlls/mmdevldr.vxd/Makefile dlls/monodebg.vxd/Makefile dlls/mpr/Makefile dlls/msacm/Makefile dlls/msacm/imaadp32/Makefile dlls/msacm/msadp32/Makefile dlls/msacm/msg711/Makefile dlls/msacm/winemp3/Makefile dlls/msacm/tests/Makefile dlls/msdmo/Makefile dlls/mshtml/Makefile dlls/msi/Makefile dlls/msimg32/Makefile dlls/msisys/Makefile dlls/msnet32/Makefile dlls/msrle32/Makefile dlls/msvcrt/Makefile dlls/msvcrt/tests/Makefile dlls/msvcrt20/Makefile dlls/msvcrt40/Makefile dlls/msvcrtd/Makefile dlls/msvcrtd/tests/Makefile dlls/msvidc32/Makefile dlls/msvideo/Makefile dlls/mswsock/Makefile dlls/netapi32/Makefile dlls/netapi32/tests/Makefile dlls/newdev/Makefile dlls/ntdll/Makefile dlls/ntdll/tests/Makefile dlls/odbc32/Makefile dlls/ole32/Makefile dlls/ole32/tests/Makefile dlls/oleacc/Makefile dlls/oleaut32/Makefile dlls/oleaut32/tests/Makefile dlls/olecli/Makefile dlls/oledlg/Makefile dlls/olepro32/Makefile dlls/olesvr/Makefile dlls/opengl32/Makefile dlls/psapi/Makefile dlls/psapi/tests/Makefile dlls/qcap/Makefile dlls/quartz/Makefile dlls/quartz/tests/Makefile dlls/rasapi32/Makefile dlls/richedit/Makefile dlls/rpcrt4/Makefile dlls/rpcrt4/tests/Makefile dlls/rsabase/Makefile dlls/rsabase/tests/Makefile dlls/secur32/Makefile dlls/serialui/Makefile dlls/setupapi/Makefile dlls/shdocvw/Makefile dlls/shell32/Makefile dlls/shell32/tests/Makefile dlls/shfolder/Makefile dlls/shlwapi/Makefile dlls/shlwapi/tests/Makefile dlls/snmpapi/Makefile dlls/sti/Makefile dlls/strmiids/Makefile dlls/tapi32/Makefile dlls/ttydrv/Makefile dlls/twain/Makefile dlls/unicows/Makefile dlls/url/Makefile dlls/urlmon/Makefile dlls/urlmon/tests/Makefile dlls/user/Makefile dlls/user/tests/Makefile dlls/uuid/Makefile dlls/uxtheme/Makefile dlls/vdhcp.vxd/Makefile dlls/vdmdbg/Makefile dlls/version/Makefile dlls/version/tests/Makefile dlls/vmm.vxd/Makefile dlls/vnbt.vxd/Makefile dlls/vnetbios.vxd/Makefile dlls/vtdapi.vxd/Makefile dlls/vwin32.vxd/Makefile dlls/win32s/Makefile dlls/winaspi/Makefile dlls/wined3d/Makefile dlls/winedos/Makefile dlls/wineps/Makefile dlls/wininet/Makefile dlls/wininet/tests/Makefile dlls/winmm/Makefile dlls/winmm/joystick/Makefile dlls/winmm/mcianim/Makefile dlls/winmm/mciavi/Makefile dlls/winmm/mcicda/Makefile dlls/winmm/mciseq/Makefile dlls/winmm/mciwave/Makefile dlls/winmm/midimap/Makefile dlls/winmm/tests/Makefile dlls/winmm/wavemap/Makefile dlls/winmm/winealsa/Makefile dlls/winmm/winearts/Makefile dlls/winmm/wineaudioio/Makefile dlls/winmm/winejack/Makefile dlls/winmm/winenas/Makefile dlls/winmm/wineoss/Makefile dlls/winnls/Makefile dlls/winsock/Makefile dlls/winsock/tests/Makefile dlls/winspool/Makefile dlls/winspool/tests/Makefile dlls/wintab32/Makefile dlls/wintrust/Makefile dlls/wow32/Makefile dlls/wsock32/Makefile dlls/x11drv/Makefile documentation/Makefile fonts/Makefile include/Makefile libs/Makefile libs/port/Makefile libs/unicode/Makefile libs/wine/Makefile libs/wpp/Makefile loader/Makefile programs/Makefile programs/avitools/Makefile programs/clock/Makefile programs/cmdlgtst/Makefile programs/control/Makefile programs/expand/Makefile programs/msiexec/Makefile programs/notepad/Makefile programs/progman/Makefile programs/regedit/Makefile programs/regsvr32/Makefile programs/rpcss/Makefile programs/rundll32/Makefile programs/start/Makefile programs/taskmgr/Makefile programs/uninstaller/Makefile programs/view/Makefile programs/wcmd/Makefile programs/wineboot/Makefile programs/winebrowser/Makefile programs/winecfg/Makefile programs/wineconsole/Makefile programs/winedbg/Makefile programs/winefile/Makefile programs/winemenubuilder/Makefile programs/winemine/Makefile programs/winepath/Makefile programs/winetest/Makefile programs/winevdm/Makefile programs/winhelp/Makefile programs/winver/Makefile server/Makefile tools/Makefile tools/widl/Makefile tools/winapi/Makefile tools/winebuild/Makefile tools/winedump/Makefile tools/winegcc/Makefile tools/wmc/Makefile tools/wrc/Makefile"
 
 
 cat >confcache <<\_ACEOF
@@ -20555,6 +20555,7 @@
   "dlls/imm32/Makefile" ) CONFIG_FILES="$CONFIG_FILES dlls/imm32/Makefile" ;;
   "dlls/iphlpapi/Makefile" ) CONFIG_FILES="$CONFIG_FILES dlls/iphlpapi/Makefile" ;;
   "dlls/iphlpapi/tests/Makefile" ) CONFIG_FILES="$CONFIG_FILES dlls/iphlpapi/tests/Makefile" ;;
+  "dlls/itss/Makefile" ) CONFIG_FILES="$CONFIG_FILES dlls/itss/Makefile" ;;
   "dlls/kernel/Makefile" ) CONFIG_FILES="$CONFIG_FILES dlls/kernel/Makefile" ;;
   "dlls/kernel/tests/Makefile" ) CONFIG_FILES="$CONFIG_FILES dlls/kernel/tests/Makefile" ;;
   "dlls/lzexpand/Makefile" ) CONFIG_FILES="$CONFIG_FILES dlls/lzexpand/Makefile" ;;
diff --git a/configure.ac b/configure.ac
index 7bc2560..6696841 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1576,6 +1576,7 @@
 dlls/imm32/Makefile
 dlls/iphlpapi/Makefile
 dlls/iphlpapi/tests/Makefile
+dlls/itss/Makefile
 dlls/kernel/Makefile
 dlls/kernel/tests/Makefile
 dlls/lzexpand/Makefile
diff --git a/dlls/Makefile.in b/dlls/Makefile.in
index c9542ad..7bee5fb 100644
--- a/dlls/Makefile.in
+++ b/dlls/Makefile.in
@@ -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
diff --git a/dlls/itss/.cvsignore b/dlls/itss/.cvsignore
new file mode 100644
index 0000000..fe07290
--- /dev/null
+++ b/dlls/itss/.cvsignore
@@ -0,0 +1,3 @@
+Makefile
+itss.dll.dbg.c
+itss.spec.def
diff --git a/dlls/itss/Makefile.in b/dlls/itss/Makefile.in
new file mode 100644
index 0000000..27c0b8b
--- /dev/null
+++ b/dlls/itss/Makefile.in
@@ -0,0 +1,29 @@
+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
+
+IDL_SRCS = \
+	itss.idl
+
+@MAKE_DLL_RULES@
+
+.SUFFIXES: .idl .h
+
+.idl.h:
+	$(WIDL) $(IDLFLAGS) -b -h -H $@ $<
+
+idl: $(IDL_SRCS:.idl=.h)
+
+### Dependencies:
diff --git a/dlls/itss/chm_lib.c b/dlls/itss/chm_lib.c
new file mode 100644
index 0000000..d0daef2
--- /dev/null
+++ b/dlls/itss/chm_lib.c
@@ -0,0 +1,1670 @@
+/* $Id$ */
+/***************************************************************************
+ *             chm_lib.c - CHM archive manipulation routines               *
+ *                           -------------------                           *
+ *                                                                         *
+ *  author:     Jed Wing <jedwin@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 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.                       *
+ *                                                                         *
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ * 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 {                        \
+        LeaveCriticalSection(&(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;
+
+/* 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;
+}
diff --git a/dlls/itss/chm_lib.h b/dlls/itss/chm_lib.h
new file mode 100644
index 0000000..528bc71
--- /dev/null
+++ b/dlls/itss/chm_lib.h
@@ -0,0 +1,116 @@
+/* $Id$ */
+/***************************************************************************
+ *             chm_lib.h - CHM archive manipulation routines               *
+ *                           -------------------                           *
+ *                                                                         *
+ *  author:     Jed Wing <jedwin@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 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.                       *
+ *                                                                         *
+ ***************************************************************************/
+
+#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);
+
+#endif /* INCLUDED_CHMLIB_H */
diff --git a/dlls/itss/itss.c b/dlls/itss/itss.c
new file mode 100644
index 0000000..c9bee3c
--- /dev/null
+++ b/dlls/itss/itss.c
@@ -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;
+}
diff --git a/dlls/itss/itss.h b/dlls/itss/itss.h
new file mode 100644
index 0000000..12bda5c
--- /dev/null
+++ b/dlls/itss/itss.h
@@ -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 */
diff --git a/dlls/itss/itss.idl b/dlls/itss/itss.idl
new file mode 100644
index 0000000..b1c1cb5
--- /dev/null
+++ b/dlls/itss/itss.idl
@@ -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);");
diff --git a/dlls/itss/itss.spec b/dlls/itss/itss.spec
new file mode 100644
index 0000000..fbad928
--- /dev/null
+++ b/dlls/itss/itss.spec
@@ -0,0 +1,5 @@
+@ stdcall -private DllCanUnloadNow() ITSS_DllCanUnloadNow
+@ stdcall -private DllGetClassObject(ptr ptr ptr) ITSS_DllGetClassObject
+@ stub DllInstall
+@ stdcall -private DllRegisterServer() ITSS_DllRegisterServer
+@ stub DllUnregisterServer
diff --git a/dlls/itss/lzx.c b/dlls/itss/lzx.c
new file mode 100644
index 0000000..9d759e8
--- /dev/null
+++ b/dlls/itss/lzx.c
@@ -0,0 +1,825 @@
+/* $Id$ */
+/***************************************************************************
+ *                        lzx.c - LZX decompression routines               *
+ *                           -------------------                           *
+ *                                                                         *
+ *  maintainer: Jed Wing <jedwin@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 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.                       *
+ *                                                                         *
+ ***************************************************************************/
+
+#include "lzx.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* 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
diff --git a/dlls/itss/lzx.h b/dlls/itss/lzx.h
new file mode 100644
index 0000000..04c97c3
--- /dev/null
+++ b/dlls/itss/lzx.h
@@ -0,0 +1,52 @@
+/* $Id$ */
+/***************************************************************************
+ *                        lzx.h - LZX decompression routines               *
+ *                           -------------------                           *
+ *                                                                         *
+ *  maintainer: Jed Wing <jedwin@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.                                                *
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   Copyright(C) Stuart Caie                                              *
+ *                                                                         *
+ *   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.                       *
+ *                                                                         *
+ ***************************************************************************/
+
+#ifndef INCLUDED_LZX_H
+#define INCLUDED_LZX_H
+
+/* 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);
+
+#endif /* INCLUDED_LZX_H */
diff --git a/dlls/itss/moniker.c b/dlls/itss/moniker.c
new file mode 100644
index 0000000..80db9c8
--- /dev/null
+++ b/dlls/itss/moniker.c
@@ -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;
+}
diff --git a/dlls/itss/storage.c b/dlls/itss/storage.c
new file mode 100644
index 0000000..ec8bbfb
--- /dev/null
+++ b/dlls/itss/storage.c
@@ -0,0 +1,798 @@
+/*
+ *    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);
+
+/************************************************************************/
+
+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;
+}