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;
+}