move more dlls
authorGed Murphy <gedmurphy@reactos.org>
Fri, 17 Feb 2006 00:04:10 +0000 (00:04 +0000)
committerGed Murphy <gedmurphy@reactos.org>
Fri, 17 Feb 2006 00:04:10 +0000 (00:04 +0000)
svn path=/trunk/; revision=21025

225 files changed:
reactos/dll/lzexpand/lz32.def [new file with mode: 0644]
reactos/dll/lzexpand/lz32.rc [new file with mode: 0644]
reactos/dll/lzexpand/lz32.xml [new file with mode: 0644]
reactos/dll/lzexpand/lzexpand_main.c [new file with mode: 0644]
reactos/dll/mapi32/imalloc.c [new file with mode: 0644]
reactos/dll/mapi32/mapi32.spec [new file with mode: 0644]
reactos/dll/mapi32/mapi32.xml [new file with mode: 0644]
reactos/dll/mapi32/mapi32_main.c [new file with mode: 0644]
reactos/dll/mapi32/prop.c [new file with mode: 0644]
reactos/dll/mapi32/util.c [new file with mode: 0644]
reactos/dll/mmdrv/auxil.c [new file with mode: 0644]
reactos/dll/mmdrv/entry.c [new file with mode: 0644]
reactos/dll/mmdrv/midi.c [new file with mode: 0644]
reactos/dll/mmdrv/mmddk.h [new file with mode: 0644]
reactos/dll/mmdrv/mmdef.h [new file with mode: 0644]
reactos/dll/mmdrv/mmdrv.def [new file with mode: 0644]
reactos/dll/mmdrv/mmdrv.h [new file with mode: 0644]
reactos/dll/mmdrv/mmdrv.xml [new file with mode: 0644]
reactos/dll/mmdrv/utils.c [new file with mode: 0644]
reactos/dll/mmdrv/wave.c [new file with mode: 0644]
reactos/dll/mmdrv/wave.h [new file with mode: 0644]
reactos/dll/mpr/Makefile.in [new file with mode: 0644]
reactos/dll/mpr/auth.c [new file with mode: 0644]
reactos/dll/mpr/mpr.rc [new file with mode: 0644]
reactos/dll/mpr/mpr.spec [new file with mode: 0644]
reactos/dll/mpr/mpr.xml [new file with mode: 0644]
reactos/dll/mpr/mpr_Bg.rc [new file with mode: 0644]
reactos/dll/mpr/mpr_Cs.rc [new file with mode: 0644]
reactos/dll/mpr/mpr_De.rc [new file with mode: 0644]
reactos/dll/mpr/mpr_En.rc [new file with mode: 0644]
reactos/dll/mpr/mpr_Es.rc [new file with mode: 0644]
reactos/dll/mpr/mpr_Fr.rc [new file with mode: 0644]
reactos/dll/mpr/mpr_Hu.rc [new file with mode: 0644]
reactos/dll/mpr/mpr_It.rc [new file with mode: 0644]
reactos/dll/mpr/mpr_Ja.rc [new file with mode: 0644]
reactos/dll/mpr/mpr_Ko.rc [new file with mode: 0644]
reactos/dll/mpr/mpr_Nl.rc [new file with mode: 0644]
reactos/dll/mpr/mpr_No.rc [new file with mode: 0644]
reactos/dll/mpr/mpr_Pt.rc [new file with mode: 0644]
reactos/dll/mpr/mpr_Ru.rc [new file with mode: 0644]
reactos/dll/mpr/mpr_Sv.rc [new file with mode: 0644]
reactos/dll/mpr/mpr_Uk.rc [new file with mode: 0644]
reactos/dll/mpr/mpr_main.c [new file with mode: 0644]
reactos/dll/mpr/mprres.h [new file with mode: 0644]
reactos/dll/mpr/multinet.c [new file with mode: 0644]
reactos/dll/mpr/netspi.h [new file with mode: 0644]
reactos/dll/mpr/nps.c [new file with mode: 0644]
reactos/dll/mpr/pwcache.c [new file with mode: 0644]
reactos/dll/mpr/version.rc [new file with mode: 0644]
reactos/dll/mpr/wnet.c [new file with mode: 0644]
reactos/dll/mpr/wnetpriv.h [new file with mode: 0644]
reactos/dll/msacm/Makefile.in [new file with mode: 0644]
reactos/dll/msacm/driver.c [new file with mode: 0644]
reactos/dll/msacm/filter.c [new file with mode: 0644]
reactos/dll/msacm/format.c [new file with mode: 0644]
reactos/dll/msacm/imaadp32/Makefile.in [new file with mode: 0644]
reactos/dll/msacm/imaadp32/imaadp32.acm.spec [new file with mode: 0644]
reactos/dll/msacm/imaadp32/imaadp32.c [new file with mode: 0644]
reactos/dll/msacm/internal.c [new file with mode: 0644]
reactos/dll/msacm/msacm.rc [new file with mode: 0644]
reactos/dll/msacm/msacm.spec [new file with mode: 0644]
reactos/dll/msacm/msacm.spec.def [new file with mode: 0644]
reactos/dll/msacm/msacm32.spec [new file with mode: 0644]
reactos/dll/msacm/msacm32.xml [new file with mode: 0644]
reactos/dll/msacm/msacm32_main.c [new file with mode: 0644]
reactos/dll/msacm/msacm_De.rc [new file with mode: 0644]
reactos/dll/msacm/msacm_En.rc [new file with mode: 0644]
reactos/dll/msacm/msacm_Es.rc [new file with mode: 0644]
reactos/dll/msacm/msacm_Fr.rc [new file with mode: 0644]
reactos/dll/msacm/msacm_Hu.rc [new file with mode: 0644]
reactos/dll/msacm/msacm_It.rc [new file with mode: 0644]
reactos/dll/msacm/msacm_Ja.rc [new file with mode: 0644]
reactos/dll/msacm/msacm_Nl.rc [new file with mode: 0644]
reactos/dll/msacm/msacm_Pt.rc [new file with mode: 0644]
reactos/dll/msacm/msacm_Ru.rc [new file with mode: 0644]
reactos/dll/msacm/msacm_Sv.rc [new file with mode: 0644]
reactos/dll/msacm/msacm_main.c [new file with mode: 0644]
reactos/dll/msacm/msadp32/Makefile.in [new file with mode: 0644]
reactos/dll/msacm/msadp32/msadp32.acm.spec [new file with mode: 0644]
reactos/dll/msacm/msadp32/msadp32.c [new file with mode: 0644]
reactos/dll/msacm/pcmconverter.c [new file with mode: 0644]
reactos/dll/msacm/stream.c [new file with mode: 0644]
reactos/dll/msacm/wineacm.h [new file with mode: 0644]
reactos/dll/msafd/include/debug.h [new file with mode: 0644]
reactos/dll/msafd/include/helpers.h [new file with mode: 0644]
reactos/dll/msafd/misc/dllmain.c [new file with mode: 0644]
reactos/dll/msafd/misc/event.c [new file with mode: 0644]
reactos/dll/msafd/misc/helpers.c [new file with mode: 0644]
reactos/dll/msafd/misc/sndrcv.c [new file with mode: 0644]
reactos/dll/msafd/misc/stubs.c [new file with mode: 0644]
reactos/dll/msafd/msafd.def [new file with mode: 0644]
reactos/dll/msafd/msafd.h [new file with mode: 0755]
reactos/dll/msafd/msafd.rc [new file with mode: 0644]
reactos/dll/msafd/msafd.xml [new file with mode: 0644]
reactos/dll/msgina/msgina.c [new file with mode: 0644]
reactos/dll/msgina/msgina.def [new file with mode: 0644]
reactos/dll/msgina/msgina.h [new file with mode: 0644]
reactos/dll/msgina/msgina.rc [new file with mode: 0644]
reactos/dll/msgina/msgina.xml [new file with mode: 0644]
reactos/dll/msgina/resource.h [new file with mode: 0644]
reactos/dll/msgina/resources/ico_logoff.ico [new file with mode: 0644]
reactos/dll/msgina/stubs.c [new file with mode: 0644]
reactos/dll/msi/Makefile.in [new file with mode: 0644]
reactos/dll/msi/action.c [new file with mode: 0644]
reactos/dll/msi/action.h [new file with mode: 0644]
reactos/dll/msi/appsearch.c [new file with mode: 0644]
reactos/dll/msi/classes.c [new file with mode: 0644]
reactos/dll/msi/cond.tab.c [new file with mode: 0644]
reactos/dll/msi/cond.tab.h [new file with mode: 0644]
reactos/dll/msi/cond.y [new file with mode: 0644]
reactos/dll/msi/create.c [new file with mode: 0644]
reactos/dll/msi/custom.c [new file with mode: 0644]
reactos/dll/msi/database.c [new file with mode: 0644]
reactos/dll/msi/delete.c [new file with mode: 0644]
reactos/dll/msi/dialog.c [new file with mode: 0644]
reactos/dll/msi/distinct.c [new file with mode: 0644]
reactos/dll/msi/events.c [new file with mode: 0644]
reactos/dll/msi/files.c [new file with mode: 0644]
reactos/dll/msi/format.c [new file with mode: 0644]
reactos/dll/msi/handle.c [new file with mode: 0644]
reactos/dll/msi/helpers.c [new file with mode: 0644]
reactos/dll/msi/insert.c [new file with mode: 0644]
reactos/dll/msi/install.c [new file with mode: 0644]
reactos/dll/msi/msi.c [new file with mode: 0644]
reactos/dll/msi/msi.rc [new file with mode: 0644]
reactos/dll/msi/msi.spec [new file with mode: 0644]
reactos/dll/msi/msi.xml [new file with mode: 0644]
reactos/dll/msi/msi_Bg.rc [new file with mode: 0644]
reactos/dll/msi/msi_De.rc [new file with mode: 0644]
reactos/dll/msi/msi_En.rc [new file with mode: 0644]
reactos/dll/msi/msi_Es.rc [new file with mode: 0644]
reactos/dll/msi/msi_Fi.rc [new file with mode: 0644]
reactos/dll/msi/msi_Fr.rc [new file with mode: 0644]
reactos/dll/msi/msi_Hu.rc [new file with mode: 0644]
reactos/dll/msi/msi_Ja.rc [new file with mode: 0644]
reactos/dll/msi/msi_Ko.rc [new file with mode: 0644]
reactos/dll/msi/msi_Nl.rc [new file with mode: 0644]
reactos/dll/msi/msi_No.rc [new file with mode: 0644]
reactos/dll/msi/msi_Pt.rc [new file with mode: 0644]
reactos/dll/msi/msi_Ru.rc [new file with mode: 0644]
reactos/dll/msi/msipriv.h [new file with mode: 0644]
reactos/dll/msi/msiquery.c [new file with mode: 0644]
reactos/dll/msi/order.c [new file with mode: 0644]
reactos/dll/msi/package.c [new file with mode: 0644]
reactos/dll/msi/preview.c [new file with mode: 0644]
reactos/dll/msi/query.h [new file with mode: 0644]
reactos/dll/msi/record.c [new file with mode: 0644]
reactos/dll/msi/registry.c [new file with mode: 0644]
reactos/dll/msi/regsvr.c [new file with mode: 0644]
reactos/dll/msi/select.c [new file with mode: 0644]
reactos/dll/msi/source.c [new file with mode: 0644]
reactos/dll/msi/sql.tab.c [new file with mode: 0644]
reactos/dll/msi/sql.tab.h [new file with mode: 0644]
reactos/dll/msi/sql.y [new file with mode: 0644]
reactos/dll/msi/string.c [new file with mode: 0644]
reactos/dll/msi/suminfo.c [new file with mode: 0644]
reactos/dll/msi/table.c [new file with mode: 0644]
reactos/dll/msi/tokenize.c [new file with mode: 0644]
reactos/dll/msi/update.c [new file with mode: 0644]
reactos/dll/msi/upgrade.c [new file with mode: 0644]
reactos/dll/msi/version.rc [new file with mode: 0644]
reactos/dll/msi/where.c [new file with mode: 0644]
reactos/dll/msimg32/Makefile.in [new file with mode: 0644]
reactos/dll/msimg32/msimg32.spec [new file with mode: 0644]
reactos/dll/msimg32/msimg32.xml [new file with mode: 0644]
reactos/dll/msimg32/msimg32_main.c [new file with mode: 0644]
reactos/dll/msvcrt/dllmain.c [new file with mode: 0644]
reactos/dll/msvcrt/msvcrt.def [new file with mode: 0644]
reactos/dll/msvcrt/msvcrt.rc [new file with mode: 0644]
reactos/dll/msvcrt/msvcrt.xml [new file with mode: 0644]
reactos/dll/msvcrt/precomp.h [new file with mode: 0644]
reactos/dll/msvcrt20/msvcrt20.c [new file with mode: 0644]
reactos/dll/msvcrt20/msvcrt20.def [new file with mode: 0644]
reactos/dll/msvcrt20/msvcrt20.rc [new file with mode: 0644]
reactos/dll/msvcrt20/msvcrt20.xml [new file with mode: 0644]
reactos/dll/msvideo/Makefile.in [new file with mode: 0644]
reactos/dll/msvideo/drawdib.c [new file with mode: 0644]
reactos/dll/msvideo/mciwnd.c [new file with mode: 0644]
reactos/dll/msvideo/msvfw32.rc [new file with mode: 0644]
reactos/dll/msvideo/msvfw32.spec [new file with mode: 0644]
reactos/dll/msvideo/msvfw32.xml [new file with mode: 0644]
reactos/dll/msvideo/msvideo.spec [new file with mode: 0644]
reactos/dll/msvideo/msvideo16.c [new file with mode: 0644]
reactos/dll/msvideo/msvideo_main.c [new file with mode: 0644]
reactos/dll/msvideo/msvideo_private.h [new file with mode: 0644]
reactos/dll/msvideo/vfw16.h [new file with mode: 0644]
reactos/dll/mswsock/extensions.c [new file with mode: 0644]
reactos/dll/mswsock/mswsock.def [new file with mode: 0644]
reactos/dll/mswsock/mswsock.rc [new file with mode: 0644]
reactos/dll/mswsock/mswsock.xml [new file with mode: 0644]
reactos/dll/mswsock/stubs.c [new file with mode: 0644]
reactos/dll/netapi32/access.c [new file with mode: 0644]
reactos/dll/netapi32/apibuf.c [new file with mode: 0644]
reactos/dll/netapi32/browsr.c [new file with mode: 0644]
reactos/dll/netapi32/ds.c [new file with mode: 0644]
reactos/dll/netapi32/nbcmdqueue.c [new file with mode: 0644]
reactos/dll/netapi32/nbcmdqueue.h [new file with mode: 0644]
reactos/dll/netapi32/nbnamecache.c [new file with mode: 0644]
reactos/dll/netapi32/nbnamecache.h [new file with mode: 0644]
reactos/dll/netapi32/nbt.c [new file with mode: 0644]
reactos/dll/netapi32/netapi32.c [new file with mode: 0644]
reactos/dll/netapi32/netapi32.spec [new file with mode: 0644]
reactos/dll/netapi32/netapi32.xml [new file with mode: 0644]
reactos/dll/netapi32/netapi32_misc.h [new file with mode: 0644]
reactos/dll/netapi32/netbios.c [new file with mode: 0644]
reactos/dll/netapi32/netbios.h [new file with mode: 0644]
reactos/dll/netapi32/wksta.c [new file with mode: 0644]
reactos/dll/netcfgx/netcfgx.c [new file with mode: 0644]
reactos/dll/netcfgx/netcfgx.def [new file with mode: 0644]
reactos/dll/netcfgx/netcfgx.h [new file with mode: 0644]
reactos/dll/netcfgx/netcfgx.xml [new file with mode: 0644]
reactos/dll/newdev/De.rc [new file with mode: 0644]
reactos/dll/newdev/En.rc [new file with mode: 0644]
reactos/dll/newdev/Es.rc [new file with mode: 0644]
reactos/dll/newdev/Hu.rc [new file with mode: 0644]
reactos/dll/newdev/Ru.rc [new file with mode: 0644]
reactos/dll/newdev/newdev.c [new file with mode: 0644]
reactos/dll/newdev/newdev.h [new file with mode: 0644]
reactos/dll/newdev/newdev.rc [new file with mode: 0644]
reactos/dll/newdev/newdev.spec [new file with mode: 0644]
reactos/dll/newdev/newdev.xml [new file with mode: 0644]
reactos/dll/newdev/resource.h [new file with mode: 0644]
reactos/dll/newdev/resources/header.bmp [new file with mode: 0644]
reactos/dll/newdev/resources/watermark.bmp [new file with mode: 0644]
reactos/dll/newdev/stubs.c [new file with mode: 0644]

diff --git a/reactos/dll/lzexpand/lz32.def b/reactos/dll/lzexpand/lz32.def
new file mode 100644 (file)
index 0000000..6ecbbbe
--- /dev/null
@@ -0,0 +1,19 @@
+; File generated automatically from lzexpand/lz32.spec; do not edit!
+
+LIBRARY lz32.dll
+
+EXPORTS
+CopyLZFile@8=KERNEL32.CopyLZFile
+GetExpandedNameA@8=KERNEL32.GetExpandedNameA
+GetExpandedNameW@8=KERNEL32.GetExpandedNameW
+LZClose@4=KERNEL32.LZClose
+;LZCloseFile
+LZCopy@8=KERNEL32.LZCopy
+;LZCreateFileW
+LZDone@0=KERNEL32.LZDone
+LZInit@4=KERNEL32.LZInit
+LZOpenFileA@12=KERNEL32.LZOpenFileA
+LZOpenFileW@12=KERNEL32.LZOpenFileW
+LZRead@12=KERNEL32.LZRead
+LZSeek@12=KERNEL32.LZSeek
+LZStart@0=KERNEL32.LZStart
diff --git a/reactos/dll/lzexpand/lz32.rc b/reactos/dll/lzexpand/lz32.rc
new file mode 100644 (file)
index 0000000..e1a9dac
--- /dev/null
@@ -0,0 +1,5 @@
+#define REACTOS_VERSION_DLL
+#define REACTOS_STR_FILE_DESCRIPTION   "Lempel-Ziv Expander\0"
+#define REACTOS_STR_INTERNAL_NAME      "lz32\0"
+#define REACTOS_STR_ORIGINAL_FILENAME  "lz32.dll\0"
+#include <reactos/version.rc>
diff --git a/reactos/dll/lzexpand/lz32.xml b/reactos/dll/lzexpand/lz32.xml
new file mode 100644 (file)
index 0000000..67cd488
--- /dev/null
@@ -0,0 +1,10 @@
+<module name="lz32" type="win32dll" baseaddress="${BASEADDRESS_LZ32}" installbase="system32" installname="lz32.dll">
+       <importlibrary definition="lz32.def" />
+       <include base="lz32">.</include>
+       <define name="_DISABLE_TIDENTS" />
+       <define name="__USE_W32API" />
+       <library>ntdll</library>
+       <library>kernel32</library>
+       <file>lzexpand_main.c</file>
+       <file>lz32.rc</file>
+</module>
diff --git a/reactos/dll/lzexpand/lzexpand_main.c b/reactos/dll/lzexpand/lzexpand_main.c
new file mode 100644 (file)
index 0000000..0d71cdb
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * LZ Decompression functions
+ *
+ * Copyright 1996 Marcus Meissner
+ *
+ * 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 "windows.h"
+
+BOOL STDCALL
+DllMain(HANDLE hDll,
+       DWORD dwReason,
+       LPVOID lpReserved)
+{
+   return TRUE;
+}
diff --git a/reactos/dll/mapi32/imalloc.c b/reactos/dll/mapi32/imalloc.c
new file mode 100644 (file)
index 0000000..1ea9442
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+ * MAPI Default IMalloc implementation
+ *
+ * Copyright 2004 Jon Griffiths
+ *
+ * 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 <stdarg.h>
+
+#define COBJMACROS
+#define NONAMELESSUNION
+#define NONAMELESSSTRUCT
+#include "windef.h"
+#include "winbase.h"
+#include "winreg.h"
+#include "winuser.h"
+#include "winerror.h"
+#include "winternl.h"
+#include "objbase.h"
+#include "shlwapi.h"
+#include "mapiutil.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(mapi);
+
+static const IMallocVtbl MAPI_IMalloc_vt;
+
+typedef struct
+{
+  const IMallocVtbl *lpVtbl;
+  LONG lRef;
+} MAPI_IMALLOC;
+
+static MAPI_IMALLOC MAPI_IMalloc = { &MAPI_IMalloc_vt, 0u };
+
+extern LONG MAPI_ObjectCount; /* In mapi32_main.c */
+
+/*************************************************************************
+ * MAPIGetDefaultMalloc@0 (MAPI32.59)
+ *
+ * Get the default MAPI IMalloc interface.
+ *
+ * PARAMS
+ *  None.
+ *
+ * RETURNS
+ *  A pointer to the MAPI default allocator.
+ */
+LPMALLOC WINAPI MAPIGetDefaultMalloc(void)
+{
+    TRACE("()\n");
+
+    IMalloc_AddRef((LPMALLOC)&MAPI_IMalloc);
+    return (LPMALLOC)&MAPI_IMalloc;
+}
+
+/**************************************************************************
+ * IMAPIMalloc_QueryInterface
+ */
+static HRESULT WINAPI IMAPIMalloc_fnQueryInterface(LPMALLOC iface, REFIID refiid,
+                                                   LPVOID *ppvObj)
+{
+    TRACE("(%s,%p)\n", debugstr_guid(refiid), ppvObj);
+
+    if (IsEqualIID(refiid, &IID_IUnknown) ||
+        IsEqualIID(refiid, &IID_IMalloc))
+    {
+        *ppvObj = (LPMALLOC) &MAPI_IMalloc;
+        TRACE("Returning IMalloc (%p)\n", *ppvObj);
+        return S_OK;
+    }
+    TRACE("Returning E_NOINTERFACE\n");
+    return E_NOINTERFACE;
+}
+
+/**************************************************************************
+ * IMAPIMalloc_AddRef
+ */
+static ULONG WINAPI IMAPIMalloc_fnAddRef(LPMALLOC iface)
+{
+    TRACE("(%p)\n", iface);
+    InterlockedIncrement(&MAPI_ObjectCount);
+    return 1u;
+}
+
+/**************************************************************************
+ * IMAPIMalloc_Release
+ */
+static ULONG WINAPI IMAPIMalloc_fnRelease(LPMALLOC iface)
+{
+    TRACE("(%p)\n", iface);
+    InterlockedDecrement(&MAPI_ObjectCount);
+    return 1u;
+}
+
+/**************************************************************************
+ * IMAPIMalloc_Alloc
+ */
+static LPVOID WINAPI IMAPIMalloc_fnAlloc(LPMALLOC iface, DWORD cb)
+{
+    TRACE("(%p)->(%ld)\n", iface, cb);
+
+    return LocalAlloc(LMEM_FIXED, cb);
+}
+
+/**************************************************************************
+ * IMAPIMalloc_Realloc
+ */
+static LPVOID WINAPI IMAPIMalloc_fnRealloc(LPMALLOC iface, LPVOID pv, DWORD cb)
+{
+    TRACE("(%p)->(%p, %ld)\n", iface, pv, cb);
+
+    if (!pv)
+        return LocalAlloc(LMEM_FIXED, cb);
+
+    if (cb)
+        return LocalReAlloc((HANDLE) pv, cb, LMEM_MOVEABLE);
+
+    LocalFree((HANDLE) pv);
+    return NULL;
+}
+
+/**************************************************************************
+ * IMAPIMalloc_Free
+ */
+static void WINAPI IMAPIMalloc_fnFree(LPMALLOC iface, LPVOID pv)
+{
+    TRACE("(%p)->(%p)\n", iface, pv);
+    LocalFree((HANDLE) pv);
+}
+
+/**************************************************************************
+ * IMAPIMalloc_GetSize
+ */
+static DWORD WINAPI IMAPIMalloc_fnGetSize(LPMALLOC iface, LPVOID pv)
+{
+    TRACE("(%p)->(%p)\n", iface, pv);
+    return LocalSize((HANDLE) pv);
+}
+
+/**************************************************************************
+ * IMAPIMalloc_DidAlloc
+ */
+static INT WINAPI IMAPIMalloc_fnDidAlloc(LPMALLOC iface, LPVOID pv)
+{
+    TRACE("(%p)->(%p)\n", iface, pv);
+    return -1;
+}
+
+/**************************************************************************
+ * IMAPIMalloc_HeapMinimize
+ */
+static void WINAPI IMAPIMalloc_fnHeapMinimize(LPMALLOC iface)
+{
+    TRACE("(%p)\n", iface);
+}
+
+static const IMallocVtbl MAPI_IMalloc_vt =
+{
+    IMAPIMalloc_fnQueryInterface,
+    IMAPIMalloc_fnAddRef,
+    IMAPIMalloc_fnRelease,
+    IMAPIMalloc_fnAlloc,
+    IMAPIMalloc_fnRealloc,
+    IMAPIMalloc_fnFree,
+    IMAPIMalloc_fnGetSize,
+    IMAPIMalloc_fnDidAlloc,
+    IMAPIMalloc_fnHeapMinimize
+};
diff --git a/reactos/dll/mapi32/mapi32.spec b/reactos/dll/mapi32/mapi32.spec
new file mode 100644 (file)
index 0000000..c94bc12
--- /dev/null
@@ -0,0 +1,191 @@
+  8 stub @
+ 10 stdcall MAPILogonEx(long ptr ptr long ptr)
+ 11 stdcall MAPILogonEx@20(long ptr ptr long ptr) MAPILogonEx
+ 12 stdcall MAPIAllocateBuffer(long ptr)
+ 13 stdcall MAPIAllocateBuffer@8(long ptr) MAPIAllocateBuffer
+ 14 stdcall MAPIAllocateMore(long ptr ptr)
+ 15 stdcall MAPIAllocateMore@12(long ptr ptr) MAPIAllocateMore
+ 16 stdcall MAPIFreeBuffer(ptr)
+ 17 stdcall MAPIFreeBuffer@4(ptr) MAPIFreeBuffer
+ 18 stub MAPIAdminProfiles
+ 19 stub MAPIAdminProfiles@8
+ 20 stdcall MAPIInitialize(ptr)
+ 21 stdcall MAPIInitialize@4(ptr) MAPIInitialize
+ 22 stdcall MAPIUninitialize()
+ 23 stdcall MAPIUninitialize@0() MAPIUninitialize
+ 24 stub PRProviderInit
+ 25 stub LAUNCHWIZARD
+ 26 stub LaunchWizard@20
+ 27 stub DllGetClassObject
+ 28 stdcall -private DllCanUnloadNow()
+ 29 stub MAPIOpenFormMgr
+ 30 stub MAPIOpenFormMgr@8
+ 31 stub MAPIOpenLocalFormContainer
+ 32 stub MAPIOpenLocalFormContainer@4
+ 33 stdcall ScInitMapiUtil@4(long) ScInitMapiUtil
+ 34 stdcall DeinitMapiUtil@0() DeinitMapiUtil
+ 35 stub ScGenerateMuid@4
+ 36 stub HrAllocAdviseSink@12
+ 41 stdcall WrapProgress@20(ptr ptr ptr ptr ptr) WrapProgress
+ 42 stdcall HrThisThreadAdviseSink@8(ptr ptr) HrThisThreadAdviseSink
+ 43 stub ScBinFromHexBounded@12
+ 44 stdcall FBinFromHex@8(ptr ptr) FBinFromHex
+ 45 stdcall HexFromBin@12(ptr long ptr) HexFromBin
+ 46 stub BuildDisplayTable@40
+ 47 stdcall SwapPlong@8(ptr long) SwapPlong
+ 48 stdcall SwapPword@8(ptr long) SwapPword
+ 49 stub MAPIInitIdle@4
+ 50 stub MAPIDeinitIdle@0
+ 51 stub InstallFilterHook@4
+ 52 stub FtgRegisterIdleRoutine@20
+ 53 stub EnableIdleRoutine@8
+ 54 stub DeregisterIdleRoutine@4
+ 55 stub ChangeIdleRoutine@28
+ 59 stdcall MAPIGetDefaultMalloc@0() MAPIGetDefaultMalloc
+ 60 stdcall CreateIProp@24(ptr ptr ptr ptr ptr ptr) CreateIProp
+ 61 stub CreateTable@36
+ 62 stdcall MNLS_lstrlenW@4(wstr) MNLS_lstrlenW
+ 63 stdcall MNLS_lstrcmpW@8(wstr wstr) MNLS_lstrcmpW
+ 64 stdcall MNLS_lstrcpyW@8(ptr wstr) MNLS_lstrcpyW
+ 65 stdcall MNLS_CompareStringW@24(long wstr wstr) MNLS_CompareStringW
+ 66 stdcall MNLS_MultiByteToWideChar@24(long long str long ptr long) kernel32.MultiByteToWideChar
+ 67 stdcall MNLS_WideCharToMultiByte@32(long long wstr long ptr long ptr ptr) kernel32.WideCharToMultiByte
+ 68 stdcall MNLS_IsBadStringPtrW@8(ptr long) kernel32.IsBadStringPtrW
+ 72 stdcall FEqualNames@8(ptr ptr) FEqualNames
+ 73 stub WrapStoreEntryID@24
+ 74 stdcall IsBadBoundedStringPtr@8(ptr long) IsBadBoundedStringPtr
+ 75 stub HrQueryAllRows@24
+ 76 stdcall PropCopyMore@16(ptr ptr ptr ptr) PropCopyMore
+ 77 stdcall UlPropSize@4(ptr) UlPropSize
+ 78 stdcall FPropContainsProp@12(ptr ptr long) FPropContainsProp
+ 79 stdcall FPropCompareProp@12(ptr long ptr) FPropCompareProp
+ 80 stdcall LPropCompareProp@8(ptr ptr) LPropCompareProp
+ 81 stub HrAddColumns@16
+ 82 stub HrAddColumnsEx@20
+121 stdcall -ret64 FtAddFt@16(double double) MAPI32_FtAddFt
+122 stub FtAdcFt@20
+123 stdcall -ret64 FtSubFt@16(double double) MAPI32_FtSubFt
+124 stdcall -ret64 FtMulDw@12(long double) MAPI32_FtMulDw
+125 stdcall -ret64 FtMulDwDw@8(long long) MAPI32_FtMulDwDw
+126 stdcall -ret64 FtNegFt@8(double) MAPI32_FtNegFt
+127 stub FtDivFtBogus@20
+128 stdcall UlAddRef@4(ptr) UlAddRef
+129 stdcall UlRelease@4(ptr) UlRelease
+130 stdcall SzFindCh@8(str long) shlwapi.StrChrA
+131 stdcall SzFindLastCh@8(str str long) shlwapi.StrRChrA
+132 stdcall SzFindSz@8(str str) shlwapi.StrStrA
+133 stdcall UFromSz@4(str) UFromSz
+135 stdcall HrGetOneProp@12(ptr long ptr) HrGetOneProp
+136 stdcall HrSetOneProp@8(ptr ptr) HrSetOneProp
+137 stdcall FPropExists@8(ptr long) FPropExists
+138 stdcall PpropFindProp@12(ptr long long) PpropFindProp
+139 stdcall FreePadrlist@4(ptr) FreePadrlist
+140 stdcall FreeProws@4(ptr) FreeProws
+141 stub HrSzFromEntryID@12
+142 stub HrEntryIDFromSz@12
+143 stub HrComposeEID@28
+144 stub HrDecomposeEID@28
+145 stub HrComposeMsgID@24
+146 stub HrDecomposeMsgID@24
+147 stdcall OpenStreamOnFile@24(ptr ptr ptr ptr ptr ptr) OpenStreamOnFile
+148 stdcall OpenStreamOnFile(ptr ptr ptr ptr ptr ptr)
+149 stub OpenTnefStream@28
+150 stub OpenTnefStream
+151 stub OpenTnefStreamEx@32
+152 stub OpenTnefStreamEx
+153 stub GetTnefStreamCodepage@12
+154 stub GetTnefStreamCodepage
+155 stdcall UlFromSzHex@4(ptr) UlFromSzHex
+156 stub UNKOBJ_ScAllocate@12
+157 stub UNKOBJ_ScAllocateMore@16
+158 stub UNKOBJ_Free@8
+159 stub UNKOBJ_FreeRows@8
+160 stub UNKOBJ_ScCOAllocate@12
+161 stub UNKOBJ_ScCOReallocate@12
+162 stub UNKOBJ_COFree@8
+163 stub UNKOBJ_ScSzFromIdsAlloc@20
+164 stub ScCountNotifications@12
+165 stub ScCopyNotifications@16
+166 stub ScRelocNotifications@20
+170 stdcall ScCountProps@12(long ptr ptr) ScCountProps
+171 stdcall ScCopyProps@16(long ptr ptr ptr) ScCopyProps
+172 stdcall ScRelocProps@20(long ptr ptr ptr ptr) ScRelocProps
+173 stdcall LpValFindProp@12(long long ptr) LpValFindProp
+174 stdcall ScDupPropset@16(long ptr ptr ptr) ScDupPropset
+175 stdcall FBadRglpszA@8(ptr long) FBadRglpszA
+176 stdcall FBadRglpszW@8(ptr long) FBadRglpszW
+177 stdcall FBadRowSet@4(ptr) FBadRowSet
+178 stub FBadRglpNameID@8
+179 stdcall FBadPropTag@4(long) FBadPropTag
+180 stdcall FBadRow@4(ptr) FBadRow
+181 stdcall FBadProp@4(ptr) FBadProp
+182 stdcall FBadColumnSet@4(ptr) FBadColumnSet
+183 stub RTFSync@12
+184 stub RTFSync
+185 stub WrapCompressedRTFStream@12
+186 stub WrapCompressedRTFStream
+187 stub __ValidateParameters@8
+188 stub __CPPValidateParameters@8
+189 stub FBadSortOrderSet@4
+190 stdcall FBadEntryList@4(ptr) FBadEntryList
+191 stub FBadRestriction@4
+192 stub ScUNCFromLocalPath@12
+193 stub ScLocalPathFromUNC@12
+194 stub HrIStorageFromStream@16
+195 stub HrValidateIPMSubtree@20
+196 stub OpenIMsgSession@12
+197 stub CloseIMsgSession@4
+198 stub OpenIMsgOnIStg@44
+199 stub SetAttribIMsgOnIStg@16
+200 stub GetAttribIMsgOnIStg@12
+201 stub MapStorageSCode@4
+202 stub ScMAPIXFromCMC
+203 stub ScMAPIXFromSMAPI
+204 stub EncodeID@12
+205 stub FDecodeID@12
+206 stub CchOfEncoding@4
+207 stdcall CbOfEncoded@4(ptr) CbOfEncoded
+208 stub MAPISendDocuments
+209 stdcall MAPILogon(long ptr ptr long long ptr)
+210 stub MAPILogoff
+211 stub MAPISendMail
+212 stub MAPISaveMail
+213 stub MAPIReadMail
+214 stub MAPIFindNext
+215 stub MAPIDeleteMail
+217 stub MAPIAddress
+218 stub MAPIDetails
+219 stub MAPIResolveName
+220 stub BMAPISendMail
+221 stub BMAPISaveMail
+222 stub BMAPIReadMail
+223 stub BMAPIGetReadMail
+224 stub BMAPIFindNext
+225 stub BMAPIAddress
+226 stub BMAPIGetAddress
+227 stub BMAPIDetails
+228 stub BMAPIResolveName
+229 stub cmc_act_on
+230 stub cmc_free
+231 stub cmc_list
+232 stub cmc_logoff
+233 stub cmc_logon
+234 stub cmc_look_up
+235 stdcall cmc_query_configuration( long long ptr ptr )
+236 stub cmc_read
+237 stub cmc_send
+238 stub cmc_send_documents
+239 stub HrDispatchNotifications@4
+241 stub HrValidateParameters@8
+244 stub ScCreateConversationIndex@16
+246 stub HrGetOmiProvidersFlags
+247 stub HrGetOmiProvidersFlags@8
+248 stub HrSetOmiProvidersFlagsInvalid
+249 stub HrSetOmiProvidersFlagsInvalid@4
+250 stub GetOutlookVersion
+251 stub GetOutlookVersion@0
+252 stub FixMAPI
+253 stub FixMAPI@0
+# This entry point is sometimes used to detect if the mapi dll came from Outlook
+#254 stub FGetComponentPath
+#255 stub FGetComponentPath@20
diff --git a/reactos/dll/mapi32/mapi32.xml b/reactos/dll/mapi32/mapi32.xml
new file mode 100644 (file)
index 0000000..7db672d
--- /dev/null
@@ -0,0 +1,19 @@
+<module name="mapi32" type="win32dll" baseaddress="${BASEADDRESS_MAPI32}" installbase="system32" installname="mapi32.dll">
+       <importlibrary definition="mapi32.spec.def" />
+       <include base="mapi32">.</include>
+       <include base="ReactOS">include/wine</include>
+       <define name="__USE_W32API" />
+       <define name="_WIN32_WINNT">0x501</define>
+       <define name="__WINESRC__" />
+       <library>ntdll</library>
+       <library>kernel32</library>
+       <library>shlwapi</library>
+       <library>wine</library>
+       <library>uuid</library>
+       <library>advapi32</library>
+       <file>mapi32_main.c</file>
+       <file>imalloc.c</file>
+       <file>prop.c</file>
+       <file>util.c</file>
+       <file>mapi32.spec</file>
+</module>
diff --git a/reactos/dll/mapi32/mapi32_main.c b/reactos/dll/mapi32/mapi32_main.c
new file mode 100644 (file)
index 0000000..5fadb5a
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ *             MAPI basics
+ *
+ * Copyright 2001 CodeWeavers Inc.
+ *
+ * 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 <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "objbase.h"
+#include "mapix.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(mapi);
+
+LONG MAPI_ObjectCount = 0;
+
+/***********************************************************************
+ *              DllMain (MAPI32.init)
+ */
+BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID fImpLoad)
+{
+    TRACE("(%p,%ld,%p)\n", hinstDLL, fdwReason, fImpLoad);
+
+    switch (fdwReason)
+    {
+    case DLL_PROCESS_ATTACH:
+        DisableThreadLibraryCalls(hinstDLL);
+        break;
+    case DLL_PROCESS_DETACH:
+       TRACE("DLL_PROCESS_DETACH: %ld objects remaining\n", MAPI_ObjectCount);
+       break;
+    }
+    return TRUE;
+}
+
+/***********************************************************************
+ * DllCanUnloadNow (MAPI32.28)
+ *
+ * Determine if this dll can be unloaded from the callers address space.
+ *
+ * PARAMS
+ *  None.
+ *
+ * RETURNS
+ *  S_OK, if the dll can be unloaded,
+ *  S_FALSE, otherwise.
+ */
+HRESULT WINAPI DllCanUnloadNow(void)
+{
+    return MAPI_ObjectCount == 0 ? S_OK : S_FALSE;
+}
+
+HRESULT WINAPI MAPIInitialize ( LPVOID lpMapiInit )
+{
+    ERR("Stub\n");
+    return MAPI_E_NOT_INITIALIZED;
+}
+
+ULONG WINAPI MAPILogon(ULONG ulUIParam, LPSTR lpszProfileName, LPSTR
+lpszPassword, FLAGS flFlags, ULONG ulReserver, LPLHANDLE lplhSession)
+{
+    ERR("Stub\n");
+    return MAPI_E_LOGON_FAILED;
+}
+
+HRESULT WINAPI MAPILogonEx(ULONG_PTR ulUIParam, LPWSTR lpszProfileName,
+                           LPWSTR lpszPassword, ULONG flFlags,
+                           LPMAPISESSION *lppSession)
+{
+    ERR("Stub\n");
+    return MAPI_E_LOGON_FAILED;
+}
+
+VOID WINAPI MAPIUninitialize(void)
+{
+    ERR("Stub\n");
+}
diff --git a/reactos/dll/mapi32/prop.c b/reactos/dll/mapi32/prop.c
new file mode 100644 (file)
index 0000000..2477b21
--- /dev/null
@@ -0,0 +1,2549 @@
+/*
+ * Property functions
+ *
+ * Copyright 2004 Jon Griffiths
+ *
+ * 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 <stdarg.h>
+#define NONAMELESSUNION
+#define NONAMELESSSTRUCT
+#include "windef.h"
+#include "winbase.h"
+#include "winreg.h"
+#include "winerror.h"
+#include "winternl.h"
+#include "objbase.h"
+#include "shlwapi.h"
+#include "wine/list.h"
+#include "wine/debug.h"
+#include "wine/unicode.h"
+#include "mapival.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(mapi);
+
+BOOL WINAPI FBadRglpszA(LPSTR*,ULONG);
+
+/* Internal: Check if a property value array is invalid */
+static inline ULONG PROP_BadArray(LPSPropValue lpProp, size_t elemSize)
+{
+    return IsBadReadPtr(lpProp->Value.MVi.lpi, lpProp->Value.MVi.cValues * elemSize);
+}
+
+/*************************************************************************
+ * PropCopyMore@16 (MAPI32.76)
+ *
+ * Copy a property value.
+ *
+ * PARAMS
+ *  lpDest [O] Destination for the copied value
+ *  lpSrc  [I] Property value to copy to lpDest
+ *  lpMore [I] Linked memory allocation function (pass MAPIAllocateMore())
+ *  lpOrig [I] Original allocation to which memory will be linked
+ *
+ * RETURNS
+ *  Success: S_OK. lpDest contains a deep copy of lpSrc.
+ *  Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid,
+ *           MAPI_E_NOT_ENOUGH_MEMORY, if memory allocation fails.
+ *
+ * NOTES
+ *  Any elements within the property returned should not be individually
+ *  freed, as they will be freed when lpOrig is.
+ */
+SCODE WINAPI PropCopyMore(LPSPropValue lpDest, LPSPropValue lpSrc,
+                          ALLOCATEMORE *lpMore, LPVOID lpOrig)
+{
+    ULONG ulLen, i;
+    SCODE scode = S_OK;
+
+    TRACE("(%p,%p,%p,%p)\n", lpDest, lpSrc, lpMore, lpOrig);
+
+    if (!lpDest || IsBadWritePtr(lpDest, sizeof(SPropValue)) ||
+        FBadProp(lpSrc) || !lpMore)
+        return MAPI_E_INVALID_PARAMETER;
+
+    /* Shallow copy first, this is sufficient for properties without pointers */
+    *lpDest = *lpSrc;
+
+   switch (PROP_TYPE(lpSrc->ulPropTag))
+    {
+    case PT_CLSID:
+        scode = lpMore(sizeof(GUID), lpOrig, (LPVOID*)&lpDest->Value.lpguid);
+        if (SUCCEEDED(scode))
+            memcpy(lpDest->Value.lpguid, lpSrc->Value.lpguid, sizeof(GUID));
+        break;
+    case PT_STRING8:
+        ulLen = lstrlenA(lpSrc->Value.lpszA) + 1u;
+        scode = lpMore(ulLen, lpOrig, (LPVOID*)&lpDest->Value.lpszA);
+        if (SUCCEEDED(scode))
+            memcpy(lpDest->Value.lpszA, lpSrc->Value.lpszA, ulLen);
+        break;
+    case PT_UNICODE:
+        ulLen = (strlenW(lpSrc->Value.lpszW) + 1u) * sizeof(WCHAR);
+        scode = lpMore(ulLen, lpOrig, (LPVOID*)&lpDest->Value.lpszW);
+        if (SUCCEEDED(scode))
+            memcpy(lpDest->Value.lpszW, lpSrc->Value.lpszW, ulLen);
+        break;
+    case PT_BINARY:
+        scode = lpMore(lpSrc->Value.bin.cb, lpOrig, (LPVOID*)&lpDest->Value.bin.lpb);
+        if (SUCCEEDED(scode))
+            memcpy(lpDest->Value.bin.lpb, lpSrc->Value.bin.lpb, lpSrc->Value.bin.cb);
+        break;
+    default:
+        if (lpSrc->ulPropTag & MV_FLAG)
+        {
+            ulLen = UlPropSize(lpSrc);
+
+            if (PROP_TYPE(lpSrc->ulPropTag) == PT_MV_STRING8 ||
+                PROP_TYPE(lpSrc->ulPropTag) == PT_MV_UNICODE)
+            {
+                /* UlPropSize doesn't account for the string pointers */
+                ulLen += lpSrc->Value.MVszA.cValues * sizeof(char*);
+            }
+            else if (PROP_TYPE(lpSrc->ulPropTag) == PT_MV_BINARY)
+            {
+               /* UlPropSize doesn't account for the SBinary structs */
+               ulLen += lpSrc->Value.MVbin.cValues * sizeof(SBinary);
+            }
+
+            lpDest->Value.MVi.cValues = lpSrc->Value.MVi.cValues;
+            scode = lpMore(ulLen, lpOrig, (LPVOID*)&lpDest->Value.MVi.lpi);
+            if (FAILED(scode))
+                break;
+
+            /* Note that we could allocate the memory for each value in a
+             * multi-value property seperately, however if an allocation failed
+             * we would be left with a bunch of allocated memory, which (while
+             * not really leaked) is unusable until lpOrig is freed. So for
+             * strings and binary arrays we make a single allocation for all
+             * of the data. This is consistent since individual elements can't
+             * be freed anyway.
+             */
+
+            switch (PROP_TYPE(lpSrc->ulPropTag))
+            {
+            case PT_MV_STRING8:
+            {
+                char *lpNextStr = (char*)(lpDest->Value.MVszA.lppszA +
+                                          lpDest->Value.MVszA.cValues);
+
+                for (i = 0; i < lpSrc->Value.MVszA.cValues; i++)
+                {
+                    ULONG ulStrLen = lstrlenA(lpSrc->Value.MVszA.lppszA[i]) + 1u;
+
+                    lpDest->Value.MVszA.lppszA[i] = lpNextStr;
+                    memcpy(lpNextStr, lpSrc->Value.MVszA.lppszA[i], ulStrLen);
+                    lpNextStr += ulStrLen;
+                }
+                break;
+            }
+            case PT_MV_UNICODE:
+            {
+                WCHAR *lpNextStr = (WCHAR*)(lpDest->Value.MVszW.lppszW +
+                                            lpDest->Value.MVszW.cValues);
+
+                for (i = 0; i < lpSrc->Value.MVszW.cValues; i++)
+                {
+                    ULONG ulStrLen = strlenW(lpSrc->Value.MVszW.lppszW[i]) + 1u;
+
+                    lpDest->Value.MVszW.lppszW[i] = lpNextStr;
+                    memcpy(lpNextStr, lpSrc->Value.MVszW.lppszW[i], ulStrLen * sizeof(WCHAR));
+                    lpNextStr += ulStrLen;
+                }
+                break;
+            }
+            case PT_MV_BINARY:
+            {
+                LPBYTE lpNext = (LPBYTE)(lpDest->Value.MVbin.lpbin +
+                                         lpDest->Value.MVbin.cValues);
+
+                for (i = 0; i < lpSrc->Value.MVszW.cValues; i++)
+                {
+                    lpDest->Value.MVbin.lpbin[i].cb = lpSrc->Value.MVbin.lpbin[i].cb;
+                    lpDest->Value.MVbin.lpbin[i].lpb = lpNext;
+                    memcpy(lpNext, lpSrc->Value.MVbin.lpbin[i].lpb, lpDest->Value.MVbin.lpbin[i].cb);
+                    lpNext += lpDest->Value.MVbin.lpbin[i].cb;
+                }
+                break;
+            }
+            default:
+                /* No embedded pointers, just copy the data over */
+                memcpy(lpDest->Value.MVi.lpi, lpSrc->Value.MVi.lpi, ulLen);
+                break;
+            }
+            break;
+        }
+    }
+    return scode;
+}
+
+/*************************************************************************
+ * UlPropSize@4 (MAPI32.77)
+ *
+ * Determine the size of a property in bytes.
+ *
+ * PARAMS
+ *  lpProp [I] Property to determine the size of
+ *
+ * RETURNS
+ *  Success: The size of the value in lpProp.
+ *  Failure: 0, if a multi-value (array) property is invalid or the type of lpProp
+ *           is unknown.
+ *
+ * NOTES
+ *  - The size returned does not include the size of the SPropValue struct
+ *    or the size of the array of pointers for multi-valued properties that
+ *    contain pointers (such as PT_MV_STRING8 or PT-MV_UNICODE).
+ *  - MSDN incorrectly states that this function returns MAPI_E_CALL_FAILED if
+ *    lpProp is invalid. In reality no checking is performed and this function
+ *    will crash if passed an invalid property, or return 0 if the property
+ *    type is PT_OBJECT or is unknown.
+ */
+ULONG WINAPI UlPropSize(LPSPropValue lpProp)
+{
+    ULONG ulRet = 1u, i;
+
+    TRACE("(%p)\n", lpProp);
+
+    switch (PROP_TYPE(lpProp->ulPropTag))
+    {
+    case PT_MV_I2:       ulRet = lpProp->Value.MVi.cValues;
+    case PT_BOOLEAN:
+    case PT_I2:          ulRet *= sizeof(USHORT);
+                         break;
+    case PT_MV_I4:       ulRet = lpProp->Value.MVl.cValues;
+    case PT_ERROR:
+    case PT_I4:          ulRet *= sizeof(LONG);
+                         break;
+    case PT_MV_I8:       ulRet = lpProp->Value.MVli.cValues;
+    case PT_I8:          ulRet *= sizeof(LONG64);
+                         break;
+    case PT_MV_R4:       ulRet = lpProp->Value.MVflt.cValues;
+    case PT_R4:          ulRet *= sizeof(float);
+                         break;
+    case PT_MV_APPTIME:
+    case PT_MV_R8:       ulRet = lpProp->Value.MVdbl.cValues;
+    case PT_APPTIME:
+    case PT_R8:          ulRet *= sizeof(double);
+                         break;
+    case PT_MV_CURRENCY: ulRet = lpProp->Value.MVcur.cValues;
+    case PT_CURRENCY:    ulRet *= sizeof(CY);
+                         break;
+    case PT_MV_SYSTIME:  ulRet = lpProp->Value.MVft.cValues;
+    case PT_SYSTIME:     ulRet *= sizeof(FILETIME);
+                         break;
+    case PT_MV_CLSID:    ulRet = lpProp->Value.MVguid.cValues;
+    case PT_CLSID:       ulRet *= sizeof(GUID);
+                         break;
+    case PT_MV_STRING8:  ulRet = 0u;
+                         for (i = 0; i < lpProp->Value.MVszA.cValues; i++)
+                             ulRet += (lstrlenA(lpProp->Value.MVszA.lppszA[i]) + 1u);
+                         break;
+    case PT_STRING8:     ulRet = lstrlenA(lpProp->Value.lpszA) + 1u;
+                         break;
+    case PT_MV_UNICODE:  ulRet = 0u;
+                         for (i = 0; i < lpProp->Value.MVszW.cValues; i++)
+                             ulRet += (strlenW(lpProp->Value.MVszW.lppszW[i]) + 1u);
+                         ulRet *= sizeof(WCHAR);
+                         break;
+    case PT_UNICODE:     ulRet = (lstrlenW(lpProp->Value.lpszW) + 1u) * sizeof(WCHAR);
+                         break;
+    case PT_MV_BINARY:   ulRet = 0u;
+                         for (i = 0; i < lpProp->Value.MVbin.cValues; i++)
+                             ulRet += lpProp->Value.MVbin.lpbin[i].cb;
+                         break;
+    case PT_BINARY:      ulRet = lpProp->Value.bin.cb;
+                         break;
+    case PT_OBJECT:
+    default:             ulRet = 0u;
+                         break;
+    }
+
+    return ulRet;
+}
+
+/*************************************************************************
+ * FPropContainsProp@12 (MAPI32.78)
+ *
+ * Find a property with a given property tag in a property array.
+ *
+ * PARAMS
+ *  lpHaystack [I] Property to match to
+ *  lpNeedle   [I] Property to find in lpHaystack
+ *  ulFuzzy    [I] Flags controlling match type and strictness (FL_* flags from "mapidefs.h")
+ *
+ * RETURNS
+ *  TRUE, if lpNeedle matches lpHaystack according to the criteria of ulFuzzy.
+ *
+ * NOTES
+ *  Only property types of PT_STRING8 and PT_BINARY are handled by this function.
+ */
+BOOL WINAPI FPropContainsProp(LPSPropValue lpHaystack, LPSPropValue lpNeedle, ULONG ulFuzzy)
+{
+    TRACE("(%p,%p,0x%08lx)\n", lpHaystack, lpNeedle, ulFuzzy);
+
+    if (FBadProp(lpHaystack) || FBadProp(lpNeedle) ||
+        PROP_TYPE(lpHaystack->ulPropTag) != PROP_TYPE(lpNeedle->ulPropTag))
+        return FALSE;
+
+    /* FIXME: Do later versions support Unicode as well? */
+
+    if (PROP_TYPE(lpHaystack->ulPropTag) == PT_STRING8)
+    {
+        DWORD dwFlags = 0, dwNeedleLen, dwHaystackLen;
+
+        if (ulFuzzy & FL_IGNORECASE)
+            dwFlags |= NORM_IGNORECASE;
+        if (ulFuzzy & FL_IGNORENONSPACE)
+            dwFlags |= NORM_IGNORENONSPACE;
+        if (ulFuzzy & FL_LOOSE)
+            dwFlags |= (NORM_IGNORECASE|NORM_IGNORENONSPACE|NORM_IGNORESYMBOLS);
+
+        dwNeedleLen = lstrlenA(lpNeedle->Value.lpszA);
+        dwHaystackLen = lstrlenA(lpHaystack->Value.lpszA);
+
+        if ((ulFuzzy & (FL_SUBSTRING|FL_PREFIX)) == FL_PREFIX)
+        {
+            if (dwNeedleLen <= dwHaystackLen &&
+                CompareStringA(LOCALE_USER_DEFAULT, dwFlags,
+                               lpHaystack->Value.lpszA, dwNeedleLen,
+                               lpNeedle->Value.lpszA, dwNeedleLen) == CSTR_EQUAL)
+                return TRUE; /* needle is a prefix of haystack */
+        }
+        else if ((ulFuzzy & (FL_SUBSTRING|FL_PREFIX)) == FL_SUBSTRING)
+        {
+            LPSTR (WINAPI *pStrChrFn)(LPCSTR,WORD) = StrChrA;
+            LPSTR lpStr = lpHaystack->Value.lpszA;
+
+            if (dwFlags & NORM_IGNORECASE)
+                pStrChrFn = StrChrIA;
+
+            while ((lpStr = pStrChrFn(lpStr, *lpNeedle->Value.lpszA)) != NULL)
+            {
+                dwHaystackLen -= (lpStr - lpHaystack->Value.lpszA);
+                if (dwNeedleLen <= dwHaystackLen &&
+                    CompareStringA(LOCALE_USER_DEFAULT, dwFlags,
+                               lpStr, dwNeedleLen,
+                               lpNeedle->Value.lpszA, dwNeedleLen) == CSTR_EQUAL)
+                    return TRUE; /* needle is a substring of haystack */
+                lpStr++;
+            }
+        }
+        else if (CompareStringA(LOCALE_USER_DEFAULT, dwFlags,
+                                lpHaystack->Value.lpszA, dwHaystackLen,
+                                lpNeedle->Value.lpszA, dwNeedleLen) == CSTR_EQUAL)
+            return TRUE; /* full string match */
+    }
+    else if (PROP_TYPE(lpHaystack->ulPropTag) == PT_BINARY)
+    {
+        if ((ulFuzzy & (FL_SUBSTRING|FL_PREFIX)) == FL_PREFIX)
+        {
+            if (lpNeedle->Value.bin.cb <= lpHaystack->Value.bin.cb &&
+                !memcmp(lpNeedle->Value.bin.lpb, lpHaystack->Value.bin.lpb,
+                        lpNeedle->Value.bin.cb))
+                return TRUE; /* needle is a prefix of haystack */
+        }
+        else if ((ulFuzzy & (FL_SUBSTRING|FL_PREFIX)) == FL_SUBSTRING)
+        {
+            ULONG ulLen = lpHaystack->Value.bin.cb;
+            LPBYTE lpb = lpHaystack->Value.bin.lpb;
+
+            while ((lpb = memchr(lpb, *lpNeedle->Value.bin.lpb, ulLen)) != NULL)
+            {
+                ulLen = lpHaystack->Value.bin.cb - (lpb - lpHaystack->Value.bin.lpb);
+                if (lpNeedle->Value.bin.cb <= ulLen &&
+                    !memcmp(lpNeedle->Value.bin.lpb, lpb, lpNeedle->Value.bin.cb))
+                    return TRUE; /* needle is a substring of haystack */
+                lpb++;
+            }
+        }
+        else if (!LPropCompareProp(lpHaystack, lpNeedle))
+            return TRUE; /* needle is an exact match with haystack */
+
+    }
+    return FALSE;
+}
+
+/*************************************************************************
+ * FPropCompareProp@12 (MAPI32.79)
+ *
+ * Compare two properties.
+ *
+ * PARAMS
+ *  lpPropLeft  [I] Left hand property to compare to lpPropRight
+ *  ulOp        [I] Comparison operator (RELOP_* enum from "mapidefs.h")
+ *  lpPropRight [I] Right hand property to compare to lpPropLeft
+ *
+ * RETURNS
+ *  TRUE, if the comparison is true, FALSE otherwise.
+ */
+BOOL WINAPI FPropCompareProp(LPSPropValue lpPropLeft, ULONG ulOp, LPSPropValue lpPropRight)
+{
+    LONG iCmp;
+
+    TRACE("(%p,%ld,%p)\n", lpPropLeft, ulOp, lpPropRight);
+
+    if (ulOp > RELOP_RE || FBadProp(lpPropLeft) || FBadProp(lpPropRight))
+        return FALSE;
+
+    if (ulOp == RELOP_RE)
+    {
+        FIXME("Comparison operator RELOP_RE not yet implemented!\n");
+        return FALSE;
+    }
+
+    iCmp = LPropCompareProp(lpPropLeft, lpPropRight);
+
+    switch (ulOp)
+    {
+    case RELOP_LT: return iCmp <  0 ? TRUE : FALSE;
+    case RELOP_LE: return iCmp <= 0 ? TRUE : FALSE;
+    case RELOP_GT: return iCmp >  0 ? TRUE : FALSE;
+    case RELOP_GE: return iCmp >= 0 ? TRUE : FALSE;
+    case RELOP_EQ: return iCmp == 0 ? TRUE : FALSE;
+    case RELOP_NE: return iCmp != 0 ? TRUE : FALSE;
+    }
+    return FALSE;
+}
+
+/*************************************************************************
+ * LPropCompareProp@8 (MAPI32.80)
+ *
+ * Compare two properties.
+ *
+ * PARAMS
+ *  lpPropLeft  [I] Left hand property to compare to lpPropRight
+ *  lpPropRight [I] Right hand property to compare to lpPropLeft
+ *
+ * RETURNS
+ *  An integer less than, equal to or greater than 0, indicating that
+ *  lpszStr is less than, the same, or greater than lpszComp.
+ */
+LONG WINAPI LPropCompareProp(LPSPropValue lpPropLeft, LPSPropValue lpPropRight)
+{
+    LONG iRet;
+
+    TRACE("(%p->0x%08lx,%p->0x%08lx)\n", lpPropLeft, lpPropLeft->ulPropTag,
+          lpPropRight, lpPropRight->ulPropTag);
+
+    /* If the properties are not the same, sort by property type */
+    if (PROP_TYPE(lpPropLeft->ulPropTag) != PROP_TYPE(lpPropRight->ulPropTag))
+        return (LONG)PROP_TYPE(lpPropLeft->ulPropTag) - (LONG)PROP_TYPE(lpPropRight->ulPropTag);
+
+    switch (PROP_TYPE(lpPropLeft->ulPropTag))
+    {
+    case PT_UNSPECIFIED:
+    case PT_NULL:
+        return 0; /* NULLs are equal */
+    case PT_I2:
+        return lpPropLeft->Value.i - lpPropRight->Value.i;
+    case PT_I4:
+        return lpPropLeft->Value.l - lpPropRight->Value.l;
+    case PT_I8:
+        if (lpPropLeft->Value.li.QuadPart > lpPropRight->Value.li.QuadPart)
+            return 1;
+        if (lpPropLeft->Value.li.QuadPart == lpPropRight->Value.li.QuadPart)
+            return 0;
+        return -1;
+    case PT_R4:
+        if (lpPropLeft->Value.flt > lpPropRight->Value.flt)
+            return 1;
+        if (lpPropLeft->Value.flt == lpPropRight->Value.flt)
+            return 0;
+        return -1;
+    case PT_APPTIME:
+    case PT_R8:
+        if (lpPropLeft->Value.dbl > lpPropRight->Value.dbl)
+            return 1;
+        if (lpPropLeft->Value.dbl == lpPropRight->Value.dbl)
+            return 0;
+        return -1;
+    case PT_CURRENCY:
+        if (lpPropLeft->Value.cur.int64 > lpPropRight->Value.cur.int64)
+            return 1;
+        if (lpPropLeft->Value.cur.int64 == lpPropRight->Value.cur.int64)
+            return 0;
+        return -1;
+    case PT_SYSTIME:
+        return CompareFileTime(&lpPropLeft->Value.ft, &lpPropRight->Value.ft);
+    case PT_BOOLEAN:
+        return (lpPropLeft->Value.b ? 1 : 0) - (lpPropRight->Value.b ? 1 : 0);
+    case PT_BINARY:
+        if (lpPropLeft->Value.bin.cb == lpPropRight->Value.bin.cb)
+            iRet = memcmp(lpPropLeft->Value.bin.lpb, lpPropRight->Value.bin.lpb,
+                          lpPropLeft->Value.bin.cb);
+        else
+        {
+            iRet = memcmp(lpPropLeft->Value.bin.lpb, lpPropRight->Value.bin.lpb,
+                          min(lpPropLeft->Value.bin.cb, lpPropRight->Value.bin.cb));
+
+            if (!iRet)
+                iRet = lpPropLeft->Value.bin.cb - lpPropRight->Value.bin.cb;
+        }
+        return iRet;
+    case PT_STRING8:
+        return lstrcmpA(lpPropLeft->Value.lpszA, lpPropRight->Value.lpszA);
+    case PT_UNICODE:
+        return strcmpW(lpPropLeft->Value.lpszW, lpPropRight->Value.lpszW);
+    case PT_ERROR:
+        if (lpPropLeft->Value.err > lpPropRight->Value.err)
+            return 1;
+        if (lpPropLeft->Value.err == lpPropRight->Value.err)
+            return 0;
+        return -1;
+    case PT_CLSID:
+        return memcmp(lpPropLeft->Value.lpguid, lpPropRight->Value.lpguid,
+                      sizeof(GUID));
+    }
+    FIXME("Unhandled property type %ld", PROP_TYPE(lpPropLeft->ulPropTag));
+    return 0;
+}
+
+/*************************************************************************
+ * HrGetOneProp@8 (MAPI32.135)
+ *
+ * Get a property value from an IMAPIProp object.
+ *
+ * PARAMS
+ *  lpIProp   [I] IMAPIProp object to get the property value in
+ *  ulPropTag [I] Property tag of the property to get
+ *  lppProp   [O] Destination for the returned property
+ *
+ * RETURNS
+ *  Success: S_OK. *lppProp contains the property value requested.
+ *  Failure: MAPI_E_NOT_FOUND, if no property value has the tag given by ulPropTag.
+ */
+HRESULT WINAPI HrGetOneProp(LPMAPIPROP lpIProp, ULONG ulPropTag, LPSPropValue *lppProp)
+{
+    SPropTagArray pta;
+    ULONG ulCount;
+    HRESULT hRet;
+
+    TRACE("(%p,%ld,%p)\n", lpIProp, ulPropTag, lppProp);
+
+    pta.cValues = 1u;
+    pta.aulPropTag[0] = ulPropTag;
+    hRet = IMAPIProp_GetProps(lpIProp, &pta, 0u, &ulCount, lppProp);
+    if (hRet == MAPI_W_ERRORS_RETURNED)
+    {
+        MAPIFreeBuffer(*lppProp);
+        *lppProp = NULL;
+        hRet = MAPI_E_NOT_FOUND;
+    }
+    return hRet;
+}
+
+/*************************************************************************
+ * HrSetOneProp@8 (MAPI32.136)
+ *
+ * Set a property value in an IMAPIProp object.
+ *
+ * PARAMS
+ *  lpIProp [I] IMAPIProp object to set the property value in
+ *  lpProp  [I] Property value to set
+ *
+ * RETURNS
+ *  Success: S_OK. The value in lpProp is set in lpIProp.
+ *  Failure: An error result from IMAPIProp_SetProps().
+ */
+HRESULT WINAPI HrSetOneProp(LPMAPIPROP lpIProp, LPSPropValue lpProp)
+{
+    TRACE("(%p,%p)\n", lpIProp, lpProp);
+
+    return IMAPIProp_SetProps(lpIProp, 1u, lpProp, NULL);
+}
+
+/*************************************************************************
+ * FPropExists@8 (MAPI32.137)
+ *
+ * Find a property with a given property tag in an IMAPIProp object.
+ *
+ * PARAMS
+ *  lpIProp   [I] IMAPIProp object to find the property tag in
+ *  ulPropTag [I] Property tag to find
+ *
+ * RETURNS
+ *  TRUE, if ulPropTag matches a property held in lpIProp,
+ *  FALSE, otherwise.
+ *
+ * NOTES
+ *  if ulPropTag has a property type of PT_UNSPECIFIED, then only the property
+ *  Ids need to match for a successful match to occur.
+ */
+ BOOL WINAPI FPropExists(LPMAPIPROP lpIProp, ULONG ulPropTag)
+ {
+    BOOL bRet = FALSE;
+
+    TRACE("(%p,%ld)\n", lpIProp, ulPropTag);
+
+    if (lpIProp)
+    {
+        LPSPropTagArray lpTags;
+        ULONG i;
+
+        if (FAILED(IMAPIProp_GetPropList(lpIProp, 0u, &lpTags)))
+            return FALSE;
+
+        for (i = 0; i < lpTags->cValues; i++)
+        {
+            if (!FBadPropTag(lpTags->aulPropTag[i]) &&
+                (lpTags->aulPropTag[i] == ulPropTag ||
+                 (PROP_TYPE(ulPropTag) == PT_UNSPECIFIED &&
+                  PROP_ID(lpTags->aulPropTag[i]) == lpTags->aulPropTag[i])))
+            {
+                bRet = TRUE;
+                break;
+            }
+        }
+        MAPIFreeBuffer(lpTags);
+    }
+    return bRet;
+}
+
+/*************************************************************************
+ * PpropFindProp@12 (MAPI32.138)
+ *
+ * Find a property with a given property tag in a property array.
+ *
+ * PARAMS
+ *  lpProps   [I] Property array to search
+ *  cValues   [I] Number of properties in lpProps
+ *  ulPropTag [I] Property tag to find
+ *
+ * RETURNS
+ *  A pointer to the matching property, or NULL if none was found.
+ *
+ * NOTES
+ *  if ulPropTag has a property type of PT_UNSPECIFIED, then only the property
+ *  Ids need to match for a successful match to occur.
+ */
+LPSPropValue WINAPI PpropFindProp(LPSPropValue lpProps, ULONG cValues, ULONG ulPropTag)
+{
+    TRACE("(%p,%ld,%ld)\n", lpProps, cValues, ulPropTag);
+
+    if (lpProps && cValues)
+    {
+        ULONG i;
+        for (i = 0; i < cValues; i++)
+        {
+            if (!FBadPropTag(lpProps[i].ulPropTag) &&
+                (lpProps[i].ulPropTag == ulPropTag ||
+                 (PROP_TYPE(ulPropTag) == PT_UNSPECIFIED &&
+                  PROP_ID(lpProps[i].ulPropTag) == PROP_ID(ulPropTag))))
+                return &lpProps[i];
+        }
+    }
+    return NULL;
+}
+
+/*************************************************************************
+ * FreePadrlist@4 (MAPI32.139)
+ *
+ * Free the memory used by an address book list.
+ *
+ * PARAMS
+ *  lpAddrs [I] Address book list to free
+ *
+ * RETURNS
+ *  Nothing.
+ */
+VOID WINAPI FreePadrlist(LPADRLIST lpAddrs)
+{
+    TRACE("(%p)\n", lpAddrs);
+
+    /* Structures are binary compatible; use the same implementation */
+    return FreeProws((LPSRowSet)lpAddrs);
+}
+
+/*************************************************************************
+ * FreeProws@4 (MAPI32.140)
+ *
+ * Free the memory used by a row set.
+ *
+ * PARAMS
+ *  lpRowSet [I] Row set to free
+ *
+ * RETURNS
+ *  Nothing.
+ */
+VOID WINAPI FreeProws(LPSRowSet lpRowSet)
+{
+    TRACE("(%p)\n", lpRowSet);
+
+    if (lpRowSet)
+    {
+        ULONG i;
+
+        for (i = 0; i < lpRowSet->cRows; i++)
+            MAPIFreeBuffer(lpRowSet->aRow[i].lpProps);
+
+        MAPIFreeBuffer(lpRowSet);
+    }
+}
+
+/*************************************************************************
+ * ScCountProps@12 (MAPI32.170)
+ *
+ * Validate and determine the length of an array of properties.
+ *
+ * PARAMS
+ *  iCount  [I] Length of the lpProps array
+ *  lpProps [I] Array of properties to validate/size
+ *  pcBytes [O] If non-NULL, destination for the size of the property array
+ *
+ * RETURNS
+ *  Success: S_OK. If pcBytes is non-NULL, it contains the size of the propery array.
+ *  Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid or validation
+ *           of the property array fails.
+ */
+SCODE WINAPI ScCountProps(INT iCount, LPSPropValue lpProps, ULONG *pcBytes)
+{
+    ULONG i, ulCount = iCount, ulBytes = 0;
+
+    TRACE("(%d,%p,%p)\n", iCount, lpProps, pcBytes);
+
+    if (iCount <= 0 || !lpProps ||
+        IsBadReadPtr(lpProps, iCount * sizeof(SPropValue)))
+        return MAPI_E_INVALID_PARAMETER;
+
+    for (i = 0; i < ulCount; i++)
+    {
+        ULONG ulPropSize = 0;
+
+        if (FBadProp(&lpProps[i]) || lpProps[i].ulPropTag == PROP_ID_NULL ||
+            lpProps[i].ulPropTag == PROP_ID_INVALID)
+            return MAPI_E_INVALID_PARAMETER;
+
+            if (PROP_TYPE(lpProps[i].ulPropTag) != PT_OBJECT)
+            {
+                ulPropSize = UlPropSize(&lpProps[i]);
+                if (!ulPropSize)
+                    return MAPI_E_INVALID_PARAMETER;
+            }
+
+            switch (PROP_TYPE(lpProps[i].ulPropTag))
+            {
+            case PT_STRING8:
+            case PT_UNICODE:
+            case PT_CLSID:
+            case PT_BINARY:
+            case PT_MV_I2:
+            case PT_MV_I4:
+            case PT_MV_I8:
+            case PT_MV_R4:
+            case PT_MV_R8:
+            case PT_MV_CURRENCY:
+            case PT_MV_SYSTIME:
+            case PT_MV_APPTIME:
+                ulPropSize += sizeof(SPropValue);
+                break;
+            case PT_MV_CLSID:
+                ulPropSize += lpProps[i].Value.MVszA.cValues * sizeof(char*) + sizeof(SPropValue);
+                break;
+            case PT_MV_STRING8:
+            case PT_MV_UNICODE:
+                ulPropSize += lpProps[i].Value.MVszA.cValues * sizeof(char*) + sizeof(SPropValue);
+                break;
+            case PT_MV_BINARY:
+                ulPropSize += lpProps[i].Value.MVbin.cValues * sizeof(SBinary) + sizeof(SPropValue);
+                break;
+            default:
+                ulPropSize = sizeof(SPropValue);
+                break;
+            }
+            ulBytes += ulPropSize;
+    }
+    if (pcBytes)
+        *pcBytes = ulBytes;
+
+    return S_OK;
+}
+
+/*************************************************************************
+ * ScCopyProps@16 (MAPI32.171)
+ *
+ * Copy an array of property values into a buffer suited for serialisation.
+ *
+ * PARAMS
+ *  cValues   [I] Number of properties in lpProps
+ *  lpProps   [I] Property array to copy
+ *  lpDst     [O] Destination for the serialised data
+ *  lpCount   [O] If non-NULL, destination for the number of bytes of data written to lpDst
+ *
+ * RETURNS
+ *  Success: S_OK. lpDst contains the serialised data from lpProps.
+ *  Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid.
+ *
+ * NOTES
+ *  The resulting property value array is stored in a contiguous block starting at lpDst.
+ */
+SCODE WINAPI ScCopyProps(int cValues, LPSPropValue lpProps, LPVOID lpDst, ULONG *lpCount)
+{
+    LPSPropValue lpDest = (LPSPropValue)lpDst;
+    char *lpDataDest = (char *)(lpDest + cValues);
+    ULONG ulLen, i;
+    int iter;
+
+    TRACE("(%d,%p,%p,%p)\n", cValues, lpProps, lpDst, lpCount);
+
+    if (!lpProps || cValues < 0 || !lpDest)
+        return MAPI_E_INVALID_PARAMETER;
+
+    memcpy(lpDst, lpProps, cValues * sizeof(SPropValue));
+
+    for (iter = 0; iter < cValues; iter++)
+    {
+        switch (PROP_TYPE(lpProps->ulPropTag))
+        {
+        case PT_CLSID:
+            lpDest->Value.lpguid = (LPGUID)lpDataDest;
+            memcpy(lpDest->Value.lpguid, lpProps->Value.lpguid, sizeof(GUID));
+            lpDataDest += sizeof(GUID);
+            break;
+        case PT_STRING8:
+            ulLen = lstrlenA(lpProps->Value.lpszA) + 1u;
+            lpDest->Value.lpszA = lpDataDest;
+            memcpy(lpDest->Value.lpszA, lpProps->Value.lpszA, ulLen);
+            lpDataDest += ulLen;
+            break;
+        case PT_UNICODE:
+            ulLen = (strlenW(lpProps->Value.lpszW) + 1u) * sizeof(WCHAR);
+            lpDest->Value.lpszW = (LPWSTR)lpDataDest;
+            memcpy(lpDest->Value.lpszW, lpProps->Value.lpszW, ulLen);
+            lpDataDest += ulLen;
+            break;
+        case PT_BINARY:
+            lpDest->Value.bin.lpb = (LPBYTE)lpDataDest;
+            memcpy(lpDest->Value.bin.lpb, lpProps->Value.bin.lpb, lpProps->Value.bin.cb);
+            lpDataDest += lpProps->Value.bin.cb;
+            break;
+        default:
+            if (lpProps->ulPropTag & MV_FLAG)
+            {
+                lpDest->Value.MVi.cValues = lpProps->Value.MVi.cValues;
+                /* Note: Assignment uses lppszA but covers all cases by union aliasing */
+                lpDest->Value.MVszA.lppszA = (char**)lpDataDest;
+
+                switch (PROP_TYPE(lpProps->ulPropTag))
+                {
+                case PT_MV_STRING8:
+                {
+                    lpDataDest += lpProps->Value.MVszA.cValues * sizeof(char *);
+
+                    for (i = 0; i < lpProps->Value.MVszA.cValues; i++)
+                    {
+                        ULONG ulStrLen = lstrlenA(lpProps->Value.MVszA.lppszA[i]) + 1u;
+
+                        lpDest->Value.MVszA.lppszA[i] = lpDataDest;
+                        memcpy(lpDataDest, lpProps->Value.MVszA.lppszA[i], ulStrLen);
+                        lpDataDest += ulStrLen;
+                    }
+                    break;
+                }
+                case PT_MV_UNICODE:
+                {
+                    lpDataDest += lpProps->Value.MVszW.cValues * sizeof(WCHAR *);
+
+                    for (i = 0; i < lpProps->Value.MVszW.cValues; i++)
+                    {
+                        ULONG ulStrLen = (strlenW(lpProps->Value.MVszW.lppszW[i]) + 1u) * sizeof(WCHAR);
+
+                        lpDest->Value.MVszW.lppszW[i] = (LPWSTR)lpDataDest;
+                        memcpy(lpDataDest, lpProps->Value.MVszW.lppszW[i], ulStrLen);
+                        lpDataDest += ulStrLen;
+                    }
+                    break;
+                }
+                case PT_MV_BINARY:
+                {
+                    lpDataDest += lpProps->Value.MVszW.cValues * sizeof(SBinary);
+
+                    for (i = 0; i < lpProps->Value.MVszW.cValues; i++)
+                    {
+                        lpDest->Value.MVbin.lpbin[i].cb = lpProps->Value.MVbin.lpbin[i].cb;
+                        lpDest->Value.MVbin.lpbin[i].lpb = (LPBYTE)lpDataDest;
+                        memcpy(lpDataDest, lpProps->Value.MVbin.lpbin[i].lpb, lpDest->Value.MVbin.lpbin[i].cb);
+                        lpDataDest += lpDest->Value.MVbin.lpbin[i].cb;
+                    }
+                    break;
+                }
+                default:
+                    /* No embedded pointers, just copy the data over */
+                    ulLen = UlPropSize(lpProps);
+                    memcpy(lpDest->Value.MVi.lpi, lpProps->Value.MVi.lpi, ulLen);
+                    lpDataDest += ulLen;
+                    break;
+                }
+                break;
+            }
+        }
+        lpDest++;
+        lpProps++;
+    }
+    if (lpCount)
+        *lpCount = lpDataDest - (char *)lpDst;
+
+    return S_OK;
+}
+
+/*************************************************************************
+ * ScRelocProps@20 (MAPI32.172)
+ *
+ * Relocate the pointers in an array of property values after it has been copied.
+ *
+ * PARAMS
+ *  cValues   [I] Number of properties in lpProps
+ *  lpProps   [O] Property array to relocate the pointers in.
+ *  lpOld     [I] Position where the data was copied from
+ *  lpNew     [I] Position where the data was copied to
+ *  lpCount   [O] If non-NULL, destination for the number of bytes of data at lpDst
+ *
+ * RETURNS
+ *  Success: S_OK. Any pointers in lpProps are relocated.
+ *  Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid.
+ *
+ * NOTES
+ *  MSDN states that this function can be used for serialisation by passing
+ *  NULL as either lpOld or lpNew, thus converting any pointers in lpProps
+ *  between offsets and pointers. This does not work in native (it crashes),
+ *  and cannot be made to work in Wine because the original interface design
+ *  is deficient. The only use left for this function is to remap pointers
+ *  in a contiguous property array that has been copied with memcpy() to
+ *  another memory location.
+ */
+SCODE WINAPI ScRelocProps(int cValues, LPSPropValue lpProps, LPVOID lpOld,
+                          LPVOID lpNew, ULONG *lpCount)
+{
+    static const BOOL bBadPtr = TRUE; /* Windows bug - Assumes source is bad */
+    LPSPropValue lpDest = (LPSPropValue)lpProps;
+    ULONG ulCount = cValues * sizeof(SPropValue);
+    ULONG ulLen, i;
+    int iter;
+
+    TRACE("(%d,%p,%p,%p,%p)\n", cValues, lpProps, lpOld, lpNew, lpCount);
+
+    if (!lpProps || cValues < 0 || !lpOld || !lpNew)
+        return MAPI_E_INVALID_PARAMETER;
+
+    /* The reason native doesn't work as MSDN states is that it assumes that
+     * the lpProps pointer contains valid pointers. This is obviously not
+     * true if the array is being read back from serialisation (the pointers
+     * are just offsets). Native can't actually work converting the pointers to
+     * offsets either, because it converts any array pointers to offsets then
+     * _dereferences the offset_ in order to convert the array elements!
+     *
+     * The code below would handle both cases except that the design of this
+     * function makes it impossible to know when the pointers in lpProps are
+     * valid. If both lpOld and lpNew are non-NULL, native reads the pointers
+     * after converting them, so we must do the same. It seems this
+     * functionality was never tested by MS.
+     */
+
+#define RELOC_PTR(p) (((char*)(p)) - (char*)lpOld + (char*)lpNew)
+
+    for (iter = 0; iter < cValues; iter++)
+    {
+        switch (PROP_TYPE(lpDest->ulPropTag))
+        {
+        case PT_CLSID:
+            lpDest->Value.lpguid = (LPGUID)RELOC_PTR(lpDest->Value.lpguid);
+            ulCount += sizeof(GUID);
+            break;
+        case PT_STRING8:
+            ulLen = bBadPtr ? 0 : lstrlenA(lpDest->Value.lpszA) + 1u;
+            lpDest->Value.lpszA = (LPSTR)RELOC_PTR(lpDest->Value.lpszA);
+            if (bBadPtr)
+                ulLen = lstrlenA(lpDest->Value.lpszA) + 1u;
+            ulCount += ulLen;
+            break;
+        case PT_UNICODE:
+            ulLen = bBadPtr ? 0 : (lstrlenW(lpDest->Value.lpszW) + 1u) * sizeof(WCHAR);
+            lpDest->Value.lpszW = (LPWSTR)RELOC_PTR(lpDest->Value.lpszW);
+            if (bBadPtr)
+                ulLen = (strlenW(lpDest->Value.lpszW) + 1u) * sizeof(WCHAR);
+            ulCount += ulLen;
+            break;
+        case PT_BINARY:
+            lpDest->Value.bin.lpb = (LPBYTE)RELOC_PTR(lpDest->Value.bin.lpb);
+            ulCount += lpDest->Value.bin.cb;
+            break;
+        default:
+            if (lpDest->ulPropTag & MV_FLAG)
+            {
+                /* Since we have to access the array elements, don't map the
+                 * array unless it is invalid (otherwise, map it at the end)
+                 */
+                if (bBadPtr)
+                    lpDest->Value.MVszA.lppszA = (LPSTR*)RELOC_PTR(lpDest->Value.MVszA.lppszA);
+
+                switch (PROP_TYPE(lpProps->ulPropTag))
+                {
+                case PT_MV_STRING8:
+                {
+                    ulCount += lpDest->Value.MVszA.cValues * sizeof(char *);
+
+                    for (i = 0; i < lpDest->Value.MVszA.cValues; i++)
+                    {
+                        ULONG ulStrLen = bBadPtr ? 0 : lstrlenA(lpDest->Value.MVszA.lppszA[i]) + 1u;
+
+                        lpDest->Value.MVszA.lppszA[i] = (LPSTR)RELOC_PTR(lpDest->Value.MVszA.lppszA[i]);
+                        if (bBadPtr)
+                            ulStrLen = lstrlenA(lpDest->Value.MVszA.lppszA[i]) + 1u;
+                        ulCount += ulStrLen;
+                    }
+                    break;
+                }
+                case PT_MV_UNICODE:
+                {
+                    ulCount += lpDest->Value.MVszW.cValues * sizeof(WCHAR *);
+
+                    for (i = 0; i < lpDest->Value.MVszW.cValues; i++)
+                    {
+                        ULONG ulStrLen = bBadPtr ? 0 : (strlenW(lpDest->Value.MVszW.lppszW[i]) + 1u) * sizeof(WCHAR);
+
+                        lpDest->Value.MVszW.lppszW[i] = (LPWSTR)RELOC_PTR(lpDest->Value.MVszW.lppszW[i]);
+                        if (bBadPtr)
+                            ulStrLen = (strlenW(lpDest->Value.MVszW.lppszW[i]) + 1u) * sizeof(WCHAR);
+                        ulCount += ulStrLen;
+                    }
+                    break;
+                }
+                case PT_MV_BINARY:
+                {
+                    ulCount += lpDest->Value.MVszW.cValues * sizeof(SBinary);
+
+                    for (i = 0; i < lpDest->Value.MVszW.cValues; i++)
+                    {
+                        lpDest->Value.MVbin.lpbin[i].lpb = (LPBYTE)RELOC_PTR(lpDest->Value.MVbin.lpbin[i].lpb);
+                        ulCount += lpDest->Value.MVbin.lpbin[i].cb;
+                    }
+                    break;
+                }
+                default:
+                    ulCount += UlPropSize(lpDest);
+                    break;
+                }
+                if (!bBadPtr)
+                    lpDest->Value.MVszA.lppszA = (LPSTR*)RELOC_PTR(lpDest->Value.MVszA.lppszA);
+                break;
+            }
+        }
+        lpDest++;
+    }
+    if (lpCount)
+        *lpCount = ulCount;
+
+    return S_OK;
+}
+
+/*************************************************************************
+ * LpValFindProp@12 (MAPI32.173)
+ *
+ * Find a property with a given property id in a property array.
+ *
+ * PARAMS
+ *  ulPropTag [I] Property tag containing property id to find
+ *  cValues   [I] Number of properties in lpProps
+ *  lpProps   [I] Property array to search
+ *
+ * RETURNS
+ *  A pointer to the matching property, or NULL if none was found.
+ *
+ * NOTES
+ *  This function matches only on the property id and does not care if the
+ *  property types differ.
+ */
+LPSPropValue WINAPI LpValFindProp(ULONG ulPropTag, ULONG cValues, LPSPropValue lpProps)
+{
+    TRACE("(%ld,%ld,%p)\n", ulPropTag, cValues, lpProps);
+
+    if (lpProps && cValues)
+    {
+        ULONG i;
+        for (i = 0; i < cValues; i++)
+        {
+            if (PROP_ID(ulPropTag) == PROP_ID(lpProps[i].ulPropTag))
+                return &lpProps[i];
+        }
+    }
+    return NULL;
+}
+
+/*************************************************************************
+ * ScDupPropset@16 (MAPI32.174)
+ *
+ * Duplicate a property value array into a contiguous block of memory.
+ *
+ * PARAMS
+ *  cValues   [I] Number of properties in lpProps
+ *  lpProps   [I] Property array to duplicate
+ *  lpAlloc   [I] Memory allocation function, use MAPIAllocateBuffer()
+ *  lpNewProp [O] Destination for the newly duplicated property value array
+ *
+ * RETURNS
+ *  Success: S_OK. *lpNewProp contains the duplicated array.
+ *  Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid,
+ *           MAPI_E_NOT_ENOUGH_MEMORY, if memory allocation fails.
+ */
+SCODE WINAPI ScDupPropset(int cValues, LPSPropValue lpProps,
+                          LPALLOCATEBUFFER lpAlloc, LPSPropValue *lpNewProp)
+{
+    ULONG ulCount;
+    SCODE sc;
+
+    TRACE("(%d,%p,%p,%p)\n", cValues, lpProps, lpAlloc, lpNewProp);
+
+    sc = ScCountProps(cValues, lpProps, &ulCount);
+    if (SUCCEEDED(sc))
+    {
+        sc = lpAlloc(ulCount, (LPVOID*)lpNewProp);
+        if (SUCCEEDED(sc))
+            sc = ScCopyProps(cValues, lpProps, *lpNewProp, &ulCount);
+    }
+    return sc;
+}
+
+/*************************************************************************
+ * FBadRglpszA@8 (MAPI32.175)
+ *
+ * Determine if an array of strings is invalid
+ *
+ * PARAMS
+ *  lppszStrs [I] Array of strings to check
+ *  ulCount   [I] Number of strings in lppszStrs
+ *
+ * RETURNS
+ *  TRUE, if lppszStrs is invalid, FALSE otherwise.
+ */
+BOOL WINAPI FBadRglpszA(LPSTR *lppszStrs, ULONG ulCount)
+{
+    ULONG i;
+
+    TRACE("(%p,%ld)\n", lppszStrs, ulCount);
+
+    if (!ulCount)
+        return FALSE;
+
+    if (!lppszStrs || IsBadReadPtr(lppszStrs, ulCount * sizeof(LPWSTR)))
+        return TRUE;
+
+    for (i = 0; i < ulCount; i++)
+    {
+        if (!lppszStrs[i] || IsBadStringPtrA(lppszStrs[i], -1))
+            return TRUE;
+    }
+    return FALSE;
+}
+
+/*************************************************************************
+ * FBadRglpszW@8 (MAPI32.176)
+ *
+ * See FBadRglpszA.
+ */
+BOOL WINAPI FBadRglpszW(LPWSTR *lppszStrs, ULONG ulCount)
+{
+    ULONG i;
+
+    TRACE("(%p,%ld)\n", lppszStrs, ulCount);
+
+    if (!ulCount)
+        return FALSE;
+
+    if (!lppszStrs || IsBadReadPtr(lppszStrs, ulCount * sizeof(LPWSTR)))
+        return TRUE;
+
+    for (i = 0; i < ulCount; i++)
+    {
+        if (!lppszStrs[i] || IsBadStringPtrW(lppszStrs[i], -1))
+            return TRUE;
+    }
+    return FALSE;
+}
+
+/*************************************************************************
+ * FBadRowSet@4 (MAPI32.177)
+ *
+ * Determine if a row is invalid
+ *
+ * PARAMS
+ *  lpRow [I] Row to check
+ *
+ * RETURNS
+ *  TRUE, if lpRow is invalid, FALSE otherwise.
+ */
+BOOL WINAPI FBadRowSet(LPSRowSet lpRowSet)
+{
+    ULONG i;
+    TRACE("(%p)\n", lpRowSet);
+
+    if (!lpRowSet || IsBadReadPtr(lpRowSet, CbSRowSet(lpRowSet)))
+        return TRUE;
+
+    for (i = 0; i < lpRowSet->cRows; i++)
+    {
+        if (FBadRow(&lpRowSet->aRow[i]))
+            return TRUE;
+    }
+    return FALSE;
+}
+
+/*************************************************************************
+ * FBadPropTag@4 (MAPI32.179)
+ *
+ * Determine if a property tag is invalid
+ *
+ * PARAMS
+ *  ulPropTag [I] Property tag to check
+ *
+ * RETURNS
+ *  TRUE, if ulPropTag is invalid, FALSE otherwise.
+ */
+ULONG WINAPI FBadPropTag(ULONG ulPropTag)
+{
+    TRACE("(0x%08lx)\n", ulPropTag);
+
+    switch (ulPropTag & (~MV_FLAG & PROP_TYPE_MASK))
+    {
+    case PT_UNSPECIFIED:
+    case PT_NULL:
+    case PT_I2:
+    case PT_LONG:
+    case PT_R4:
+    case PT_DOUBLE:
+    case PT_CURRENCY:
+    case PT_APPTIME:
+    case PT_ERROR:
+    case PT_BOOLEAN:
+    case PT_OBJECT:
+    case PT_I8:
+    case PT_STRING8:
+    case PT_UNICODE:
+    case PT_SYSTIME:
+    case PT_CLSID:
+    case PT_BINARY:
+        return FALSE;
+    }
+    return TRUE;
+}
+
+/*************************************************************************
+ * FBadRow@4 (MAPI32.180)
+ *
+ * Determine if a row is invalid
+ *
+ * PARAMS
+ *  lpRow [I] Row to check
+ *
+ * RETURNS
+ *  TRUE, if lpRow is invalid, FALSE otherwise.
+ */
+ULONG WINAPI FBadRow(LPSRow lpRow)
+{
+    ULONG i;
+    TRACE("(%p)\n", lpRow);
+
+    if (!lpRow || IsBadReadPtr(lpRow, sizeof(SRow)) || !lpRow->lpProps ||
+        IsBadReadPtr(lpRow->lpProps, lpRow->cValues * sizeof(SPropValue)))
+        return TRUE;
+
+    for (i = 0; i < lpRow->cValues; i++)
+    {
+        if (FBadProp(&lpRow->lpProps[i]))
+            return TRUE;
+    }
+    return FALSE;
+}
+
+/*************************************************************************
+ * FBadProp@4 (MAPI32.181)
+ *
+ * Determine if a property is invalid
+ *
+ * PARAMS
+ *  lpProp [I] Property to check
+ *
+ * RETURNS
+ *  TRUE, if lpProp is invalid, FALSE otherwise.
+ */
+ULONG WINAPI FBadProp(LPSPropValue lpProp)
+{
+    if (!lpProp || IsBadReadPtr(lpProp, sizeof(SPropValue)) ||
+        FBadPropTag(lpProp->ulPropTag))
+        return TRUE;
+
+    switch (PROP_TYPE(lpProp->ulPropTag))
+    {
+    /* Single value properties containing pointers */
+    case PT_STRING8:
+        if (!lpProp->Value.lpszA || IsBadStringPtrA(lpProp->Value.lpszA, -1))
+            return TRUE;
+        break;
+    case PT_UNICODE:
+        if (!lpProp->Value.lpszW || IsBadStringPtrW(lpProp->Value.lpszW, -1))
+            return TRUE;
+        break;
+    case PT_BINARY:
+        if (IsBadReadPtr(lpProp->Value.bin.lpb, lpProp->Value.bin.cb))
+            return TRUE;
+        break;
+    case PT_CLSID:
+        if (IsBadReadPtr(lpProp->Value.lpguid, sizeof(GUID)))
+            return TRUE;
+        break;
+
+    /* Multiple value properties (arrays) containing no pointers */
+    case PT_MV_I2:
+        return PROP_BadArray(lpProp, sizeof(SHORT));
+    case PT_MV_LONG:
+        return PROP_BadArray(lpProp, sizeof(LONG));
+    case PT_MV_LONGLONG:
+        return PROP_BadArray(lpProp, sizeof(LONG64));
+    case PT_MV_FLOAT:
+        return PROP_BadArray(lpProp, sizeof(float));
+    case PT_MV_SYSTIME:
+        return PROP_BadArray(lpProp, sizeof(FILETIME));
+    case PT_MV_APPTIME:
+    case PT_MV_DOUBLE:
+        return PROP_BadArray(lpProp, sizeof(double));
+    case PT_MV_CURRENCY:
+        return PROP_BadArray(lpProp, sizeof(CY));
+    case PT_MV_CLSID:
+        return PROP_BadArray(lpProp, sizeof(GUID));
+
+    /* Multiple value properties containing pointers */
+    case PT_MV_STRING8:
+        return FBadRglpszA(lpProp->Value.MVszA.lppszA,
+                           lpProp->Value.MVszA.cValues);
+    case PT_MV_UNICODE:
+        return FBadRglpszW(lpProp->Value.MVszW.lppszW,
+                           lpProp->Value.MVszW.cValues);
+    case PT_MV_BINARY:
+        return FBadEntryList((LPENTRYLIST)&lpProp->Value.MVbin);
+    }
+    return FALSE;
+}
+
+/*************************************************************************
+ * FBadColumnSet@4 (MAPI32.182)
+ *
+ * Determine if an array of property tags is invalid
+ *
+ * PARAMS
+ *  lpCols [I] Property tag array to check
+ *
+ * RETURNS
+ *  TRUE, if lpCols is invalid, FALSE otherwise.
+ */
+ULONG WINAPI FBadColumnSet(LPSPropTagArray lpCols)
+{
+    ULONG ulRet = FALSE, i;
+
+    TRACE("(%p)\n", lpCols);
+
+    if (!lpCols || IsBadReadPtr(lpCols, CbSPropTagArray(lpCols)))
+        ulRet = TRUE;
+    else
+    {
+        for (i = 0; i < lpCols->cValues; i++)
+        {
+            if ((lpCols->aulPropTag[i] & PROP_TYPE_MASK) == PT_ERROR ||
+                FBadPropTag(lpCols->aulPropTag[i]))
+            {
+                ulRet = TRUE;
+                break;
+            }
+        }
+    }
+    TRACE("Returning %s\n", ulRet ? "TRUE" : "FALSE");
+    return ulRet;
+}
+
+
+/**************************************************************************
+ *  IMAPIProp {MAPI32}
+ *
+ * The default Mapi interface for manipulating object properties.
+ *
+ * DESCRIPTION
+ *  This object provides an interface to an objects properties. It is exposed
+ *  by several types of Mapi objects in order to simplify the querying and
+ *  modification of properties.
+ *
+ * METHODS
+ */
+
+/* A single property in a property data collection */
+typedef struct
+{
+  struct list  entry;
+  ULONG        ulAccess; /* The property value access level */
+  LPSPropValue value;    /* The property value */
+} IPropDataItem, *LPIPropDataItem;
+
+ /* The main property data collection structure */
+typedef struct
+{
+    const IPropDataVtbl   *lpVtbl;
+    LONG             lRef;        /* Reference count */
+    ALLOCATEBUFFER  *lpAlloc;     /* Memory allocation routine */
+    ALLOCATEMORE    *lpMore;      /* Linked memory allocation routine */
+    FREEBUFFER      *lpFree;      /* Memory free routine */
+    ULONG            ulObjAccess; /* Object access level */
+    ULONG            ulNumValues; /* Number of items in values list */
+    struct list      values;      /* List of property values */
+    RTL_CRITICAL_SECTION cs;          /* Lock for thread safety */
+} IPropDataImpl;
+
+/* Internal - Get a property value, assumes lock is held */
+static IPropDataItem *IMAPIPROP_GetValue(IPropDataImpl *This, ULONG ulPropTag)
+{
+    struct list *cursor;
+
+    LIST_FOR_EACH(cursor, &This->values)
+    {
+        LPIPropDataItem current = LIST_ENTRY(cursor, IPropDataItem, entry);
+        /* Note that propery types don't have to match, just Id's */
+        if (PROP_ID(current->value->ulPropTag) == PROP_ID(ulPropTag))
+            return current;
+    }
+    return NULL;
+}
+
+/* Internal - Add a new property value, assumes lock is held */
+static IPropDataItem *IMAPIPROP_AddValue(IPropDataImpl *This,
+                                         LPSPropValue lpProp)
+{
+    LPVOID lpMem;
+    LPIPropDataItem lpNew;
+    HRESULT hRet;
+
+    hRet = This->lpAlloc(sizeof(IPropDataItem), &lpMem);
+
+    if (SUCCEEDED(hRet))
+    {
+        lpNew = lpMem;
+        lpNew->ulAccess = IPROP_READWRITE;
+
+        /* Allocate the value seperately so we can update it easily */
+        lpMem = NULL;
+        hRet = This->lpAlloc(sizeof(SPropValue), &lpMem);
+        if (SUCCEEDED(hRet))
+        {
+            lpNew->value = lpMem;
+
+            hRet = PropCopyMore(lpNew->value, lpProp, This->lpMore, lpMem);
+            if (SUCCEEDED(hRet))
+            {
+                list_add_tail(&This->values, &lpNew->entry);
+                This->ulNumValues++;
+                return lpNew;
+            }
+            This->lpFree(lpNew->value);
+        }
+        This->lpFree(lpNew);
+    }
+    return NULL;
+}
+
+/* Internal - Lock an IPropData object */
+static inline void IMAPIPROP_Lock(IPropDataImpl *This)
+{
+    RtlEnterCriticalSection(&This->cs);
+}
+
+/* Internal - Unlock an IPropData object */
+static inline void IMAPIPROP_Unlock(IPropDataImpl *This)
+{
+    RtlLeaveCriticalSection(&This->cs);
+}
+
+/* This one seems to be missing from mapidefs.h */
+#define CbNewSPropProblemArray(c) \
+    (offsetof(SPropProblemArray,aProblem)+(c)*sizeof(SPropProblem))
+
+/**************************************************************************
+ *  IMAPIProp_QueryInterface {MAPI32}
+ *
+ * Inherited method from the IUnknown Interface.
+ * See IUnknown_QueryInterface.
+ *
+ * NOTES
+ * This object exposes the following interfaces:
+ * - IUnknown() : The default interface for all COM-Objects.
+ * - IMAPIProp() : The default Mapi interface for manipulating object properties.
+ */
+static inline HRESULT WINAPI
+IMAPIProp_fnQueryInterface(LPMAPIPROP iface, REFIID riid, LPVOID *ppvObj)
+{
+    IPropDataImpl *This = (IPropDataImpl*)iface;
+
+    TRACE("(%p,%s,%p)\n", This, debugstr_guid(riid), ppvObj);
+
+    if (!ppvObj || !riid)
+        return MAPI_E_INVALID_PARAMETER;
+
+    *ppvObj = NULL;
+
+    if(IsEqualIID(riid, &IID_IUnknown) ||
+       IsEqualIID(riid, &IID_IMAPIProp) ||
+       IsEqualIID(riid, &IID_IMAPIPropData))
+    {
+        *ppvObj = This;
+        IPropData_AddRef(iface);
+        TRACE("returning %p\n", *ppvObj);
+        return S_OK;
+    }
+
+    TRACE("returning E_NOINTERFACE\n");
+    return MAPI_E_INTERFACE_NOT_SUPPORTED;
+}
+
+/**************************************************************************
+ *  IMAPIProp_AddRef {MAPI32}
+ *
+ * Inherited method from the IUnknown Interface.
+ * See IUnknown_AddRef.
+ */
+static inline ULONG WINAPI IMAPIProp_fnAddRef(LPMAPIPROP iface)
+{
+    IPropDataImpl *This = (IPropDataImpl*)iface;
+
+    TRACE("(%p)->(count before=%lu)\n", This, This->lRef);
+
+    return InterlockedIncrement(&This->lRef);
+}
+
+/**************************************************************************
+ *  IMAPIProp_Release {MAPI32}
+ *
+ * Inherited method from the IUnknown Interface.
+ * See IUnknown_Release.
+ */
+static inline ULONG WINAPI IMAPIProp_fnRelease(LPMAPIPROP iface)
+{
+    IPropDataImpl *This = (IPropDataImpl*)iface;
+    LONG lRef;
+
+    TRACE("(%p)->(count before=%lu)\n", This, This->lRef);
+
+    lRef = InterlockedDecrement(&This->lRef);
+    if (!lRef)
+    {
+        TRACE("Destroying IPropData (%p)\n",This);
+
+        /* Note: No need to lock, since no other thread is referencing iface */
+        while (!list_empty(&This->values))
+        {
+            struct list *head = list_head(&This->values);
+            LPIPropDataItem current = LIST_ENTRY(head, IPropDataItem, entry);
+            list_remove(head);
+            This->lpFree(current->value);
+            This->lpFree(current);
+        }
+        RtlDeleteCriticalSection(&This->cs);
+        This->lpFree(This);
+    }
+    return (ULONG)lRef;
+}
+
+/**************************************************************************
+ *  IMAPIProp_GetLastError {MAPI32}
+ *
+ * Get information about the last error that ocurred in an IMAPIProp object.
+ *
+ * PARAMS
+ *  iface    [I] IMAPIProp object that experienced the error
+ *  hRes     [I] Result of the call that returned an error
+ *  ulFlags  [I] 0=return Ascii strings, MAPI_UNICODE=return Unicode strings
+ *  lppError [O] Destination for detailed error information
+ *
+ * RETURNS
+ *  Success: S_OK. *lppError contains details about the last error.
+ *  Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid,
+ *           MAPI_E_NOT_ENOUGH_MEMORY, if memory allocation fails.
+ *
+ * NOTES
+ *  - If this function succeeds, the returned information in *lppError must be
+ *  freed using MAPIFreeBuffer() once the caller is finished with it.
+ *  - It is possible for this function to suceed and set *lppError to NULL,
+ *  if there is no further information to report about hRes.
+ */
+static inline HRESULT WINAPI
+IMAPIProp_fnGetLastError(LPMAPIPROP iface, HRESULT hRes,
+                         ULONG ulFlags, LPMAPIERROR *lppError)
+{
+    TRACE("(%p,0x%08lX,0x%08lX,%p)\n", iface, hRes, ulFlags, lppError);
+
+    if (!lppError  || SUCCEEDED(hRes) || (ulFlags & ~MAPI_UNICODE))
+        return MAPI_E_INVALID_PARAMETER;
+
+    *lppError = NULL;
+    return S_OK;
+}
+
+/**************************************************************************
+ *  IMAPIProp_SaveChanges {MAPI32}
+ *
+ * Update any changes made to a tansactional IMAPIProp object.
+ *
+ * PARAMS
+ *  iface    [I] IMAPIProp object to update
+ *  ulFlags  [I] Flags controlling the update.
+ *
+ * RETURNS
+ *  Success: S_OK. Any outstanding changes are committed to the object.
+ *  Failure: An HRESULT error code describing the error.
+ */
+static inline HRESULT WINAPI
+IMAPIProp_fnSaveChanges(LPMAPIPROP iface, ULONG ulFlags)
+{
+    TRACE("(%p,0x%08lX)\n", iface, ulFlags);
+
+     /* Since this object is not transacted we do not need to implement this */
+     /* FIXME: Should we set the access levels to clean? */
+    return S_OK;
+}
+
+/**************************************************************************
+ *  IMAPIProp_GetProps {MAPI32}
+ *
+ * Get property values from an IMAPIProp object.
+ *
+ * PARAMS
+ *  iface    [I] IMAPIProp object to get the property values from
+ *  lpTags   [I] Property tage of property values to be retrieved
+ *  ulFlags  [I] Return 0=Ascii MAPI_UNICODE=Unicode strings for
+ *                 unspecified types
+ *  lpCount  [O] Destination for number of properties returned
+ *  lppProps [O] Destination for returned property values
+ *
+ * RETURNS
+ *  Success: S_OK. *lppProps and *lpCount are updated.
+ *  Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid.
+ *           MAPI_E_NOT_ENOUGH_MEMORY, if memory allocation fails, or
+ *           MAPI_W_ERRORS_RETURNED if not all properties were retrieved
+ *           successfully.
+ * NOTES
+ *  - If MAPI_W_ERRORS_RETURNED is returned, any properties that could not be
+ *    retrieved from iface are present in lppProps with their type
+ *    changed to PT_ERROR and Id unchanged.
+ */
+static inline HRESULT WINAPI
+IMAPIProp_fnGetProps(LPMAPIPROP iface, LPSPropTagArray lpTags,
+                     ULONG ulFlags, ULONG *lpCount, LPSPropValue *lppProps)
+{
+    ULONG i;
+    HRESULT hRet = S_OK;
+    IPropDataImpl *This = (IPropDataImpl*)iface;
+
+    TRACE("(%p,%p,0x%08lx,%p,%p) stub\n", iface, lpTags, ulFlags,
+          lpCount, lppProps);
+
+    if (!iface || ulFlags & ~MAPI_UNICODE || !lpTags || *lpCount || !lppProps)
+        return MAPI_E_INVALID_PARAMETER;
+
+    FIXME("semi-stub, flags not supported\n");
+
+    *lpCount = lpTags->cValues;
+    *lppProps = NULL;
+
+    if (*lpCount)
+    {
+        hRet = MAPIAllocateBuffer(*lpCount * sizeof(SPropValue), (LPVOID*)lppProps);
+        if (FAILED(hRet))
+            return hRet;
+
+        IMAPIPROP_Lock(This);
+
+        for (i = 0; i < lpTags->cValues; i++)
+        {
+            HRESULT hRetTmp = E_INVALIDARG;
+            LPIPropDataItem item;
+
+            item = IMAPIPROP_GetValue(This, lpTags->aulPropTag[i]);
+
+            if (item)
+                hRetTmp = PropCopyMore(&(*lppProps)[i], item->value,
+                                       This->lpMore, *lppProps);
+            if (FAILED(hRetTmp))
+            {
+                hRet = MAPI_W_ERRORS_RETURNED;
+                (*lppProps)[i].ulPropTag =
+                    CHANGE_PROP_TYPE(lpTags->aulPropTag[i], PT_ERROR);
+            }
+        }
+
+        IMAPIPROP_Unlock(This);
+    }
+    return hRet;
+}
+
+/**************************************************************************
+ *  MAPIProp_GetPropList {MAPI32}
+ *
+ * Get the list of property tags for all values in an IMAPIProp object.
+ *
+ * PARAMS
+ *  iface   [I] IMAPIProp object to get the property tag list from
+ *  ulFlags [I] Return 0=Ascii MAPI_UNICODE=Unicode strings for
+ *              unspecified types
+ *  lppTags [O] Destination for the retrieved peoperty tag list
+ *
+ * RETURNS
+ *  Success: S_OK. *lppTags contains the tags for all available properties.
+ *  Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid.
+ *           MAPI_E_BAD_CHARWIDTH, if Ascii or Unicode strings are requested
+ *           and that type of string is not supported.
+ */
+static inline HRESULT WINAPI
+IMAPIProp_fnGetPropList(LPMAPIPROP iface, ULONG ulFlags,
+                        LPSPropTagArray *lppTags)
+{
+    IPropDataImpl *This = (IPropDataImpl*)iface;
+    ULONG i;
+    HRESULT hRet;
+
+    TRACE("(%p,0x%08lx,%p) stub\n", iface, ulFlags, lppTags);
+
+    if (!iface || ulFlags & ~MAPI_UNICODE || !lppTags)
+        return MAPI_E_INVALID_PARAMETER;
+
+    FIXME("semi-stub, flags not supported\n");
+
+    *lppTags = NULL;
+
+    IMAPIPROP_Lock(This);
+
+    hRet = MAPIAllocateBuffer(CbNewSPropTagArray(This->ulNumValues),
+                              (LPVOID*)lppTags);
+    if (SUCCEEDED(hRet))
+    {
+        struct list *cursor;
+
+        i = 0;
+        LIST_FOR_EACH(cursor, &This->values)
+        {
+            LPIPropDataItem current = LIST_ENTRY(cursor, IPropDataItem, entry);
+            (*lppTags)->aulPropTag[i] = current->value->ulPropTag;
+            i++;
+        }
+        (*lppTags)->cValues = This->ulNumValues;
+    }
+
+    IMAPIPROP_Unlock(This);
+    return hRet;
+}
+
+/**************************************************************************
+ *  IMAPIProp_OpenProperty {MAPI32}
+ *
+ * Not documented at this time.
+ *
+ * RETURNS
+ *  An HRESULT success/failure code.
+ */
+static inline HRESULT WINAPI
+IMAPIProp_fnOpenProperty(LPMAPIPROP iface, ULONG ulPropTag, LPCIID iid,
+                         ULONG ulOpts, ULONG ulFlags, LPUNKNOWN *lpUnk)
+{
+    FIXME("(%p,%lu,%s,%lu,0x%08lx,%p) stub\n", iface, ulPropTag,
+          debugstr_guid(iid), ulOpts, ulFlags, lpUnk);
+    return MAPI_E_NO_SUPPORT;
+}
+
+
+/**************************************************************************
+ *  IMAPIProp_SetProps {MAPI32}
+ *
+ * Add or edit the property values in an IMAPIProp object.
+ *
+ * PARAMS
+ *  iface    [I] IMAPIProp object to get the property tag list from
+ *  ulValues [I] Number of properties in lpProps
+ *  lpProps  [I] Property values to set
+ *  lppProbs [O] Optional destination for any problems that occurred
+ *
+ * RETURNS
+ *  Success: S_OK. The properties in lpProps are added to iface if they don't
+ *           exist, or changed to the values in lpProps if they do
+ *  Failure: An HRESULT error code describing the error
+ */
+static inline HRESULT WINAPI
+IMAPIProp_fnSetProps(LPMAPIPROP iface, ULONG ulValues,
+                     LPSPropValue lpProps, LPSPropProblemArray *lppProbs)
+{
+    IPropDataImpl *This = (IPropDataImpl*)iface;
+    HRESULT hRet = S_OK;
+    ULONG i;
+
+    TRACE("(%p,%lu,%p,%p)\n", iface, ulValues, lpProps, lppProbs);
+
+    if (!iface || !lpProps)
+      return MAPI_E_INVALID_PARAMETER;
+
+    for (i = 0; i < ulValues; i++)
+    {
+        if (FBadProp(&lpProps[i]) ||
+            PROP_TYPE(lpProps[i].ulPropTag) == PT_OBJECT ||
+            PROP_TYPE(lpProps[i].ulPropTag) == PT_NULL)
+          return MAPI_E_INVALID_PARAMETER;
+    }
+
+    IMAPIPROP_Lock(This);
+
+    /* FIXME: Under what circumstances is lpProbs created? */
+    for (i = 0; i < ulValues; i++)
+    {
+        LPIPropDataItem item = IMAPIPROP_GetValue(This, lpProps[i].ulPropTag);
+
+        if (item)
+        {
+            HRESULT hRetTmp;
+            LPVOID lpMem = NULL;
+
+            /* Found, so update the existing value */
+            if (item->value->ulPropTag != lpProps[i].ulPropTag)
+                FIXME("semi-stub, overwriting type (not coercing)\n");
+
+            hRetTmp = This->lpAlloc(sizeof(SPropValue), &lpMem);
+            if (SUCCEEDED(hRetTmp))
+            {
+                hRetTmp = PropCopyMore(lpMem, &lpProps[i], This->lpMore, lpMem);
+                if (SUCCEEDED(hRetTmp))
+                {
+                    This->lpFree(item->value);
+                    item->value = lpMem;
+                    continue;
+                }
+                This->lpFree(lpMem);
+            }
+            hRet = hRetTmp;
+        }
+        else
+        {
+            /* Add new value */
+            if (!(item = IMAPIPROP_AddValue(This, &lpProps[i])))
+                hRet = MAPI_E_NOT_ENOUGH_MEMORY;
+        }
+    }
+
+    IMAPIPROP_Unlock(This);
+    return hRet;
+}
+
+/**************************************************************************
+ *  IMAPIProp_DeleteProps {MAPI32}
+ *
+ * Delete one or more property values from an IMAPIProp object.
+ *
+ * PARAMS
+ *  iface    [I] IMAPIProp object to remove property values from.
+ *  lpTags   [I] Collection of property Id's to remove from iface.
+ *  lppProbs [O] Destination for problems encountered, if any.
+ *
+ * RETURNS
+ *  Success: S_OK. Any properties in iface matching property Id's in lpTags have
+ *           been deleted. If lppProbs is non-NULL it contains details of any
+ *           errors that occurred.
+ *  Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid.
+ *           E_ACCESSDENIED, if this object was created using CreateIProp() and
+ *           a subsequent call to IPropData_SetObjAcess() was made specifying
+ *           IPROP_READONLY as the access type.
+ *
+ * NOTES
+ *  - lppProbs will not be populated for cases where a property Id is present
+ *    in lpTags but not in iface.
+ *  - lppProbs should be deleted with MAPIFreeBuffer() if returned.
+ */
+static inline HRESULT WINAPI
+IMAPIProp_fnDeleteProps(LPMAPIPROP iface, LPSPropTagArray lpTags,
+                        LPSPropProblemArray *lppProbs)
+{
+    IPropDataImpl *This = (IPropDataImpl*)iface;
+    ULONG i, numProbs = 0;
+    HRESULT hRet = S_OK;
+
+    TRACE("(%p,%p,%p)\n", iface, lpTags, lppProbs);
+
+    if (!iface || !lpTags)
+        return MAPI_E_INVALID_PARAMETER;
+
+    if (lppProbs)
+        *lppProbs = NULL;
+
+    for (i = 0; i < lpTags->cValues; i++)
+    {
+        if (FBadPropTag(lpTags->aulPropTag[i]) ||
+            PROP_TYPE(lpTags->aulPropTag[i]) == PT_OBJECT ||
+            PROP_TYPE(lpTags->aulPropTag[i]) == PT_NULL)
+          return MAPI_E_INVALID_PARAMETER;
+    }
+
+    IMAPIPROP_Lock(This);
+
+    if (This->ulObjAccess != IPROP_READWRITE)
+    {
+        IMAPIPROP_Unlock(This);
+        return E_ACCESSDENIED;
+    }
+
+    for (i = 0; i < lpTags->cValues; i++)
+    {
+        LPIPropDataItem item = IMAPIPROP_GetValue(This, lpTags->aulPropTag[i]);
+
+        if (item)
+        {
+            if (item->ulAccess & IPROP_READWRITE)
+            {
+                /* Everything hunky-dory, remove the item */
+                list_remove(&item->entry);
+                This->lpFree(item->value); /* Also frees value pointers */
+                This->lpFree(item);
+                This->ulNumValues--;
+            }
+            else if (lppProbs)
+            {
+                 /* Can't write the value. Create/populate problems array */
+                 if (!*lppProbs)
+                 {
+                     /* Create problems array */
+                     ULONG ulSize = CbNewSPropProblemArray(lpTags->cValues - i);
+                     HRESULT hRetTmp = MAPIAllocateBuffer(ulSize, (LPVOID*)lppProbs);
+                     if (FAILED(hRetTmp))
+                         hRet = hRetTmp;
+                 }
+                 if (*lppProbs)
+                 {
+                     LPSPropProblem lpProb = &(*lppProbs)->aProblem[numProbs];
+                     lpProb->ulIndex = i;
+                     lpProb->ulPropTag = lpTags->aulPropTag[i];
+                     lpProb->scode = E_ACCESSDENIED;
+                     numProbs++;
+                 }
+            }
+        }
+    }
+    if (lppProbs && *lppProbs)
+        (*lppProbs)->cProblem = numProbs;
+
+    IMAPIPROP_Unlock(This);
+    return hRet;
+}
+
+
+/**************************************************************************
+ *  IMAPIProp_CopyTo {MAPI32}
+ *
+ * Not documented at this time.
+ *
+ * RETURNS
+ *  An HRESULT success/failure code.
+ */
+static inline HRESULT WINAPI
+IMAPIProp_fnCopyTo(LPMAPIPROP iface, ULONG niids, LPCIID lpiidExcl,
+                   LPSPropTagArray lpPropsExcl, ULONG ulParam,
+                   LPMAPIPROGRESS lpIProgress, LPCIID lpIfaceIid, LPVOID lpDstObj,
+                   ULONG ulFlags, LPSPropProblemArray *lppProbs)
+{
+    FIXME("(%p,%lu,%p,%p,%lx,%p,%s,%p,0x%08lX,%p) stub\n", iface, niids,
+          lpiidExcl, lpPropsExcl, ulParam, lpIProgress,
+          debugstr_guid(lpIfaceIid), lpDstObj, ulFlags, lppProbs);
+    return MAPI_E_NO_SUPPORT;
+}
+
+/**************************************************************************
+ *  IMAPIProp_CopyProps {MAPI32}
+ *
+ * Not documented at this time.
+ *
+ * RETURNS
+ *  An HRESULT success/failure code.
+ */
+static inline HRESULT WINAPI
+IMAPIProp_fnCopyProps(LPMAPIPROP iface, LPSPropTagArray lpInclProps,
+                      ULONG ulParam, LPMAPIPROGRESS lpIProgress, LPCIID lpIface,
+                      LPVOID lpDstObj, ULONG ulFlags,
+                      LPSPropProblemArray *lppProbs)
+{
+    FIXME("(%p,%p,%lx,%p,%s,%p,0x%08lX,%p) stub\n", iface, lpInclProps,
+          ulParam, lpIProgress, debugstr_guid(lpIface), lpDstObj, ulFlags,
+          lppProbs);
+    return MAPI_E_NO_SUPPORT;
+}
+
+/**************************************************************************
+ *  IMAPIProp_GetNamesFromIDs {MAPI32}
+ *
+ * Get the names of properties from their identifiers.
+ *
+ * PARAMS
+ *  iface       [I]   IMAPIProp object to operate on
+ *  lppPropTags [I/O] Property identifiers to get the names for, or NULL to
+ *                    get all names
+ *  iid         [I]   Property set identifier, or NULL
+ *  ulFlags     [I]   MAPI_NO_IDS=Don't return numeric named properties,
+ *                    or MAPI_NO_STRINGS=Don't return strings
+ *  lpCount     [O]   Destination for number of properties returned
+ *  lpppNames   [O]   Destination for returned names
+ *
+ * RETURNS
+ *  Success: S_OK. *lppPropTags and lpppNames contain the returned
+ *           name/identifiers.
+ *  Failure: MAPI_E_NO_SUPPORT, if the object does not support named properties,
+ *           MAPI_E_NOT_ENOUGH_MEMORY, if memory allocation fails, or
+ *           MAPI_W_ERRORS_RETURNED if not all properties were retrieved
+ *           successfully.
+ */
+static inline HRESULT WINAPI
+IMAPIProp_fnGetNamesFromIDs(LPMAPIPROP iface, LPSPropTagArray *lppPropTags,
+                            LPGUID iid, ULONG ulFlags, ULONG *lpCount,
+                            LPMAPINAMEID **lpppNames)
+{
+    FIXME("(%p,%p,%s,0x%08lX,%p,%p) stub\n", iface, lppPropTags,
+          debugstr_guid(iid), ulFlags, lpCount, lpppNames);
+    return MAPI_E_NO_SUPPORT;
+}
+
+/**************************************************************************
+ *  IMAPIProp_GetIDsFromNames {MAPI32}
+ *
+ * Get property identifiers associated with one or more named properties.
+ *
+ * PARAMS
+ *  iface       [I] IMAPIProp object to operate on
+ *  ulNames     [I] Number of names in lppNames
+ *  lppNames    [I] Names to query or create, or NULL to query all names
+ *  ulFlags     [I] Pass MAPI_CREATE to create new named properties
+ *  lppPropTags [O] Destination for queried or created property identifiers
+ *
+ * RETURNS
+ *  Success: S_OK. *lppPropTags contains the property tags created or requested.
+ *  Failure: MAPI_E_NO_SUPPORT, if the object does not support named properties,
+ *           MAPI_E_TOO_BIG, if the object cannot process the number of
+ *           properties involved.
+ *           MAPI_E_NOT_ENOUGH_MEMORY, if memory allocation fails, or
+ *           MAPI_W_ERRORS_RETURNED if not all properties were retrieved
+ *           successfully.
+ */
+static inline HRESULT WINAPI
+IMAPIProp_fnGetIDsFromNames(LPMAPIPROP iface, ULONG ulNames,
+                            LPMAPINAMEID *lppNames, ULONG ulFlags,
+                            LPSPropTagArray *lppPropTags)
+{
+    FIXME("(%p,%ld,%p,0x%08lX,%p) stub\n",
+          iface, ulNames, lppNames, ulFlags, lppPropTags);
+    return MAPI_E_NO_SUPPORT;
+}
+
+/**************************************************************************
+ *  IPropData {MAPI32}
+ *
+ * A default Mapi interface to provide manipulation of object properties.
+ *
+ * DESCRIPTION
+ *  This object provides a default interface suitable in some cases as an
+ *  implementation of the IMAPIProp interface (which has no default
+ *  implementation). In addition to the IMAPIProp() methods inherited, this
+ *  interface allows read/write control over access to the object and its
+ *  individual properties.
+ *
+ *  To obtain the default implementation of this interface from Mapi, call
+ *  CreateIProp().
+ *
+ * METHODS
+ */
+
+/**************************************************************************
+ *  IPropData_QueryInterface {MAPI32}
+ *
+ * Inherited method from the IMAPIProp Interface.
+ * See IMAPIProp_QueryInterface.
+ */
+static HRESULT WINAPI
+IPropData_fnQueryInterface(LPPROPDATA iface, REFIID riid, LPVOID *ppvObj)
+{
+    return IMAPIProp_fnQueryInterface((LPMAPIPROP)iface, riid, ppvObj);
+}
+
+/**************************************************************************
+ *  IPropData_AddRef {MAPI32}
+ *
+ * Inherited method from the IMAPIProp Interface.
+ * See IMAPIProp_AddRef.
+ */
+static ULONG WINAPI
+IPropData_fnAddRef(LPPROPDATA iface)
+{
+    return IMAPIProp_fnAddRef((LPMAPIPROP)iface);
+}
+
+/**************************************************************************
+ *  IPropData_Release {MAPI32}
+ *
+ * Inherited method from the IMAPIProp Interface.
+ * See IMAPIProp_Release.
+ */
+static ULONG WINAPI
+IPropData_fnRelease(LPPROPDATA iface)
+{
+    return IMAPIProp_fnRelease((LPMAPIPROP)iface);
+}
+
+/**************************************************************************
+ *  IPropData_GetLastError {MAPI32}
+ *
+ * Inherited method from the IMAPIProp Interface.
+ * See IMAPIProp_GetLastError.
+ */
+static HRESULT WINAPI
+IPropData_fnGetLastError(LPPROPDATA iface, HRESULT hRes, ULONG ulFlags,
+                         LPMAPIERROR *lppError)
+{
+    return IMAPIProp_fnGetLastError((LPMAPIPROP)iface, hRes, ulFlags, lppError);
+}
+
+/**************************************************************************
+ *  IPropData_SaveChanges {MAPI32}
+ *
+ * Inherited method from the IMAPIProp Interface.
+ * See IMAPIProp_SaveChanges.
+ */
+static HRESULT WINAPI
+IPropData_fnSaveChanges(LPPROPDATA iface, ULONG ulFlags)
+{
+    return IMAPIProp_fnSaveChanges((LPMAPIPROP)iface, ulFlags);
+}
+
+/**************************************************************************
+ *  IPropData_GetProps {MAPI32}
+ *
+ * Inherited method from the IMAPIProp Interface.
+ * See IMAPIProp_GetProps.
+ */
+static HRESULT WINAPI
+IPropData_fnGetProps(LPPROPDATA iface, LPSPropTagArray lpPropTags,
+                     ULONG ulFlags, ULONG *lpCount, LPSPropValue *lppProps)
+{
+    return IMAPIProp_fnGetProps((LPMAPIPROP)iface, lpPropTags, ulFlags,
+                                lpCount, lppProps);
+}
+
+/**************************************************************************
+ *  IPropData_GetPropList {MAPI32}
+ *
+ * Inherited method from the IMAPIProp Interface.
+ * See IMAPIProp_GetPropList.
+ */
+static HRESULT WINAPI
+IPropData_fnGetPropList(LPPROPDATA iface, ULONG ulFlags,
+                                              LPSPropTagArray *lppPropTags)
+{
+    return IMAPIProp_fnGetPropList((LPMAPIPROP)iface, ulFlags, lppPropTags);
+}
+
+/**************************************************************************
+ *  IPropData_OpenProperty {MAPI32}
+ *
+ * Inherited method from the IMAPIProp Interface.
+ * See IMAPIProp_OpenProperty.
+ */
+static HRESULT WINAPI
+IPropData_fnOpenProperty(LPPROPDATA iface, ULONG ulPropTag, LPCIID iid,
+                         ULONG ulOpts, ULONG ulFlags, LPUNKNOWN *lpUnk)
+{
+    return IMAPIProp_fnOpenProperty((LPMAPIPROP)iface, ulPropTag, iid,
+                                    ulOpts, ulFlags, lpUnk);
+}
+
+/**************************************************************************
+ *  IPropData_SetProps {MAPI32}
+ *
+ * Inherited method from the IMAPIProp Interface.
+ * See IMAPIProp_SetProps.
+ */
+static HRESULT WINAPI
+IPropData_fnSetProps(LPPROPDATA iface, ULONG cValues, LPSPropValue lpProps,
+                     LPSPropProblemArray *lppProbs)
+{
+    return IMAPIProp_fnSetProps((LPMAPIPROP)iface, cValues, lpProps, lppProbs);
+}
+
+/**************************************************************************
+ *  IPropData_DeleteProps {MAPI32}
+ *
+ * Inherited method from the IMAPIProp Interface.
+ * See IMAPIProp_DeleteProps.
+ */
+static HRESULT WINAPI
+IPropData_fnDeleteProps(LPPROPDATA iface, LPSPropTagArray lpPropTags,
+                        LPSPropProblemArray *lppProbs)
+{
+    return IMAPIProp_fnDeleteProps((LPMAPIPROP)iface, lpPropTags, lppProbs);
+}
+
+/**************************************************************************
+ *  IPropData_CopyTo {MAPI32}
+ *
+ * Inherited method from the IMAPIProp Interface.
+ * See IMAPIProp_CopyTo.
+ */
+static HRESULT WINAPI
+IPropData_fnCopyTo(LPPROPDATA iface, ULONG ciidExclude, LPCIID lpIid,
+                   LPSPropTagArray lpProps, ULONG ulParam,
+                   LPMAPIPROGRESS lpProgress, LPCIID lpIface, LPVOID lpDst,
+                   ULONG ulFlags, LPSPropProblemArray *lppProbs)
+{
+    return IMAPIProp_fnCopyTo((LPMAPIPROP)iface, ciidExclude, lpIid, lpProps,
+                              ulParam, lpProgress, lpIface, lpDst,
+                              ulFlags, lppProbs);
+}
+
+/**************************************************************************
+ *  IPropData_CopyProps {MAPI32}
+ *
+ * Inherited method from the IMAPIProp Interface.
+ * See IMAPIProp_CopyProps.
+ */
+static HRESULT WINAPI
+IPropData_fnCopyProps(LPPROPDATA iface, LPSPropTagArray lpProps,
+                      ULONG ulParam, LPMAPIPROGRESS lpProgress, LPCIID lpIface,
+                      LPVOID lpDst, ULONG ulFlags, LPSPropProblemArray *lppProbs)
+{
+    return IMAPIProp_fnCopyProps((LPMAPIPROP)iface, lpProps, ulParam,
+                                 lpProgress, lpIface, lpDst, ulFlags, lppProbs);
+}
+
+/**************************************************************************
+ *  IPropData_GetNamesFromIDs {MAPI32}
+ *
+ * Inherited method from the IMAPIProp Interface.
+ * See IMAPIProp_GetNamesFromIDs.
+ */
+static HRESULT WINAPI
+IPropData_fnGetNamesFromIDs(LPPROPDATA iface, LPSPropTagArray *lppPropTags,
+                            LPGUID iid, ULONG ulFlags, ULONG *lpCount,
+                            LPMAPINAMEID **lpppNames)
+{
+    return IMAPIProp_fnGetNamesFromIDs((LPMAPIPROP)iface, lppPropTags, iid,
+                                       ulFlags, lpCount, lpppNames);
+}
+
+/**************************************************************************
+ *  IPropData_GetIDsFromNames {MAPI32}
+ *
+ * Inherited method from the IMAPIProp Interface.
+ * See IMAPIProp_GetIDsFromNames.
+ */
+static HRESULT WINAPI
+IPropData_fnGetIDsFromNames(LPPROPDATA iface, ULONG ulNames,
+                            LPMAPINAMEID *lppNames, ULONG ulFlags,
+                            LPSPropTagArray *lppPropTags)
+{
+    return IMAPIProp_fnGetIDsFromNames((LPMAPIPROP)iface, ulNames, lppNames,
+                                       ulFlags, lppPropTags);
+}
+
+/**************************************************************************
+ *  IPropData_HrSetObjAccess {MAPI32}
+ *
+ * Set the access level of an IPropData object.
+ *
+ * PARAMS
+ *  iface    [I] IPropData object to set the access on
+ *  ulAccess [I] Either IPROP_READONLY or IPROP_READWRITE for read or
+ *               read/write access respectively.
+ *
+ * RETURNS
+ *  Success: S_OK. The objects access level is changed.
+ *  Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid.
+ */
+static HRESULT WINAPI
+IPropData_fnHrSetObjAccess(LPPROPDATA iface, ULONG ulAccess)
+{
+    IPropDataImpl *This = (IPropDataImpl*)iface;
+
+    TRACE("(%p,%lx)\n", iface, ulAccess);
+
+    if (!iface || ulAccess < IPROP_READONLY || ulAccess > IPROP_READWRITE)
+        return MAPI_E_INVALID_PARAMETER;
+
+    IMAPIPROP_Lock(This);
+
+    This->ulObjAccess = ulAccess;
+
+    IMAPIPROP_Unlock(This);
+    return S_OK;
+}
+
+/* Internal - determine if an access value is bad */
+static inline BOOL PROP_IsBadAccess(ULONG ulAccess)
+{
+    switch (ulAccess)
+    {
+    case IPROP_READONLY|IPROP_CLEAN:
+    case IPROP_READONLY|IPROP_DIRTY:
+    case IPROP_READWRITE|IPROP_CLEAN:
+    case IPROP_READWRITE|IPROP_DIRTY:
+        return FALSE;
+    }
+    return TRUE;
+}
+
+/**************************************************************************
+ *  IPropData_HrSetPropAccess {MAPI32}
+ *
+ * Set the access levels for a group of property values in an IPropData object.
+ *
+ * PARAMS
+ *  iface    [I] IPropData object to set access levels in.
+ *  lpTags   [I] List of property Id's to set access for.
+ *  lpAccess [O] Access level for each property in lpTags.
+ *
+ * RETURNS
+ *  Success: S_OK. The access level of each property value in lpTags that is
+ *           present in iface is changed.
+ *  Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid.
+ *
+ * NOTES
+ *  - Each access level in lpAccess must contain at least one of IPROP_READONLY
+ *    or IPROP_READWRITE, but not both, and also IPROP_CLEAN or IPROP_DIRTY,
+ *    but not both. No other bits should be set.
+ *  - If a property Id in lpTags is not present in iface, it is ignored.
+ */
+static HRESULT WINAPI
+IPropData_fnHrSetPropAccess(LPPROPDATA iface, LPSPropTagArray lpTags,
+                            ULONG *lpAccess)
+{
+    IPropDataImpl *This = (IPropDataImpl*)iface;
+
+    ULONG i;
+
+    TRACE("(%p,%p,%p)\n", iface, lpTags, lpAccess);
+
+    if (!iface || !lpTags || !lpAccess)
+        return MAPI_E_INVALID_PARAMETER;
+
+    for (i = 0; i < lpTags->cValues; i++)
+    {
+        if (FBadPropTag(lpTags->aulPropTag[i]) || PROP_IsBadAccess(lpAccess[i]))
+            return MAPI_E_INVALID_PARAMETER;
+    }
+
+    IMAPIPROP_Lock(This);
+
+    for (i = 0; i < lpTags->cValues; i++)
+    {
+        LPIPropDataItem item = IMAPIPROP_GetValue(This, lpTags->aulPropTag[i]);
+
+        if (item)
+            item->ulAccess = lpAccess[i];
+    }
+
+    IMAPIPROP_Unlock(This);
+    return S_OK;
+}
+
+/**************************************************************************
+ *  IPropData_HrGetPropAccess {MAPI32}
+ *
+ * Get the access levels for a group of property values in an IPropData object.
+ *
+ * PARAMS
+ *  iface     [I] IPropData object to get access levels from.
+ *  lppTags   [O] Destination for the list of property Id's in iface.
+ *  lppAccess [O] Destination for access level for each property in lppTags.
+ *
+ * RETURNS
+ *  Success: S_OK. lppTags and lppAccess contain the property Id's and the
+ *           Access level of each property value in iface.
+ *  Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid, or
+ *           MAPI_E_NOT_ENOUGH_MEMORY if memory allocation fails.
+ *
+ * NOTES
+ *  - *lppTags and *lppAccess should be freed with MAPIFreeBuffer() by the caller.
+ */
+static HRESULT WINAPI
+IPropData_fnHrGetPropAccess(LPPROPDATA iface, LPSPropTagArray *lppTags,
+                            ULONG **lppAccess)
+{
+    IPropDataImpl *This = (IPropDataImpl*)iface;
+    LPVOID lpMem;
+    HRESULT hRet;
+    ULONG i;
+
+    TRACE("(%p,%p,%p) stub\n", iface, lppTags, lppAccess);
+
+    if (!iface || !lppTags || !lppAccess)
+        return MAPI_E_INVALID_PARAMETER;
+
+    *lppTags = NULL;
+    *lppAccess = NULL;
+
+    IMAPIPROP_Lock(This);
+
+    hRet = This->lpAlloc(CbNewSPropTagArray(This->ulNumValues), &lpMem);
+    if (SUCCEEDED(hRet))
+    {
+        *lppTags = lpMem;
+
+        hRet = This->lpAlloc(This->ulNumValues * sizeof(ULONG), &lpMem);
+        if (SUCCEEDED(hRet))
+        {
+            struct list *cursor;
+
+            *lppAccess = lpMem;
+            (*lppTags)->cValues = This->ulNumValues;
+
+            i = 0;
+            LIST_FOR_EACH(cursor, &This->values)
+            {
+                LPIPropDataItem item = LIST_ENTRY(cursor, IPropDataItem, entry);
+                (*lppTags)->aulPropTag[i] = item->value->ulPropTag;
+                (*lppAccess)[i] = item->ulAccess;
+                i++;
+            }
+            IMAPIPROP_Unlock(This);
+            return S_OK;
+        }
+        This->lpFree(*lppTags);
+        *lppTags = 0;
+    }
+    IMAPIPROP_Unlock(This);
+    return MAPI_E_NOT_ENOUGH_MEMORY;
+}
+
+/**************************************************************************
+ *  IPropData_HrAddObjProps {MAPI32}
+ *
+ * Not documented at this time.
+ *
+ * RETURNS
+ *  An HRESULT success/failure code.
+ */
+static HRESULT WINAPI
+IPropData_fnHrAddObjProps(LPPROPDATA iface, LPSPropTagArray lpTags,
+                          LPSPropProblemArray *lppProbs)
+{
+#if 0
+    ULONG i;
+    HRESULT hRet;
+    LPSPropValue lpValues;
+#endif
+
+    FIXME("(%p,%p,%p) stub\n", iface, lpTags, lppProbs);
+
+    if (!iface || !lpTags)
+        return MAPI_E_INVALID_PARAMETER;
+
+    /* FIXME: Below is the obvious implementation, adding all the properties
+     *        in lpTags to the object. However, it doesn't appear that this
+     *        is what this function does.
+     */
+    return S_OK;
+#if 0
+    if (!lpTags->cValues)
+        return S_OK;
+
+    lpValues = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
+                         lpTags->cValues * sizeof(SPropValue));
+    if (!lpValues)
+        return MAPI_E_NOT_ENOUGH_MEMORY;
+
+    for (i = 0; i < lpTags->cValues; i++)
+        lpValues[i].ulPropTag = lpTags->aulPropTag[i];
+
+    hRet = IPropData_SetProps(iface, lpTags->cValues, lpValues, lppProbs);
+    HeapFree(GetProcessHeap(), 0, lpValues);
+    return hRet;
+#endif
+}
+
+static const IPropDataVtbl IPropDataImpl_vtbl =
+{
+    IPropData_fnQueryInterface,
+    IPropData_fnAddRef,
+    IPropData_fnRelease,
+    IPropData_fnGetLastError,
+    IPropData_fnSaveChanges,
+    IPropData_fnGetProps,
+    IPropData_fnGetPropList,
+    IPropData_fnOpenProperty,
+    IPropData_fnSetProps,
+    IPropData_fnDeleteProps,
+    IPropData_fnCopyTo,
+    IPropData_fnCopyProps,
+    IPropData_fnGetNamesFromIDs,
+    IPropData_fnGetIDsFromNames,
+    IPropData_fnHrSetObjAccess,
+    IPropData_fnHrSetPropAccess,
+    IPropData_fnHrGetPropAccess,
+    IPropData_fnHrAddObjProps
+};
+
+/*************************************************************************
+ * CreateIProp@24 (MAPI32.60)
+ *
+ * Create an IPropData object.
+ *
+ * PARAMS
+ *  iid         [I] GUID of the object to create. Use &IID_IMAPIPropData or NULL
+ *  lpAlloc     [I] Memory allocation function. Use MAPIAllocateBuffer()
+ *  lpMore      [I] Linked memory allocation function. Use MAPIAllocateMore()
+ *  lpFree      [I] Memory free function. Use MAPIFreeBuffer()
+ *  lpReserved  [I] Reserved, set to NULL
+ *  lppPropData [O] Destination for created IPropData object
+ *
+ * RETURNS
+ *  Success: S_OK. *lppPropData contains the newly created object.
+ *  Failure: MAPI_E_INTERFACE_NOT_SUPPORTED, if iid is non-NULL and not supported,
+ *           MAPI_E_INVALID_PARAMETER, if any parameter is invalid
+ */
+SCODE WINAPI CreateIProp(LPCIID iid, ALLOCATEBUFFER *lpAlloc,
+                         ALLOCATEMORE *lpMore, FREEBUFFER *lpFree,
+                         LPVOID lpReserved, LPPROPDATA *lppPropData)
+{
+    IPropDataImpl *lpPropData;
+    SCODE scode;
+
+    TRACE("(%s,%p,%p,%p,%p,%p)\n", debugstr_guid(iid), lpAlloc, lpMore, lpFree,
+          lpReserved, lppPropData);
+
+    if (lppPropData)
+        *lppPropData = NULL;
+
+    if (iid && !IsEqualGUID(iid, &IID_IMAPIPropData))
+        return MAPI_E_INTERFACE_NOT_SUPPORTED;
+
+    if (!lpAlloc || !lpMore || !lpFree || lpReserved || !lppPropData)
+        return MAPI_E_INVALID_PARAMETER;
+
+    scode = lpAlloc(sizeof(IPropDataImpl), (LPVOID*)&lpPropData);
+
+    if (SUCCEEDED(scode))
+    {
+        lpPropData->lpVtbl = &IPropDataImpl_vtbl;
+        lpPropData->lRef = 1;
+        lpPropData->lpAlloc = lpAlloc;
+        lpPropData->lpMore = lpMore;
+        lpPropData->lpFree = lpFree;
+        lpPropData->ulObjAccess = IPROP_READWRITE;
+        lpPropData->ulNumValues = 0;
+        list_init(&lpPropData->values);
+        RtlInitializeCriticalSection(&lpPropData->cs);
+        *lppPropData = (LPPROPDATA)lpPropData;
+    }
+    return scode;
+}
diff --git a/reactos/dll/mapi32/util.c b/reactos/dll/mapi32/util.c
new file mode 100644 (file)
index 0000000..3b7fcc2
--- /dev/null
@@ -0,0 +1,823 @@
+/*
+ * MAPI Utility functions
+ *
+ * Copyright 2004 Jon Griffiths
+ *
+ * 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 <stdarg.h>
+
+#define COBJMACROS
+#define NONAMELESSUNION
+#define NONAMELESSSTRUCT
+#include "windef.h"
+#include "winbase.h"
+#include "winreg.h"
+#include "winuser.h"
+#include "winerror.h"
+#include "winternl.h"
+#include "objbase.h"
+#include "shlwapi.h"
+#include "wine/debug.h"
+#include "wine/unicode.h"
+#include "mapival.h"
+#include "xcmc.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(mapi);
+
+static const BYTE digitsToHex[] = {
+  0,1,2,3,4,5,6,7,8,9,0xff,0xff,0xff,0xff,0xff,0xff,0xff,10,11,12,13,14,15,
+  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,10,11,12,13,
+  14,15 };
+
+/**************************************************************************
+ *  ScInitMapiUtil (MAPI32.33)
+ *
+ * Initialise Mapi utility functions.
+ *
+ * PARAMS
+ *  ulReserved [I] Reserved, pass 0.
+ *
+ * RETURNS
+ *  Success: S_OK. Mapi utility functions may be called.
+ *  Failure: MAPI_E_INVALID_PARAMETER, if ulReserved is not 0.
+ *
+ * NOTES
+ *  Your application does not need to call this function unless it does not
+ *  call MAPIInitialize()/MAPIUninitialize().
+ */
+SCODE WINAPI ScInitMapiUtil(ULONG ulReserved)
+{
+    FIXME("(0x%08lx)stub!\n", ulReserved);
+    if (ulReserved)
+        return MAPI_E_INVALID_PARAMETER;
+    return S_OK;
+}
+
+/**************************************************************************
+ *  DeinitMapiUtil (MAPI32.34)
+ *
+ * Uninitialise Mapi utility functions.
+ *
+ * PARAMS
+ *  None.
+ *
+ * RETURNS
+ *  Nothing.
+ *
+ * NOTES
+ *  Your application does not need to call this function unless it does not
+ *  call MAPIInitialize()/MAPIUninitialize().
+ */
+VOID WINAPI DeinitMapiUtil(void)
+{
+    FIXME("()stub!\n");
+}
+
+typedef LPVOID *LPMAPIALLOCBUFFER;
+
+/**************************************************************************
+ *  MAPIAllocateBuffer   (MAPI32.12)
+ *  MAPIAllocateBuffer@8 (MAPI32.13)
+ *
+ * Allocate a block of memory.
+ *
+ * PARAMS
+ *  cbSize    [I] Size of the block to allocate in bytes
+ *  lppBuffer [O] Destination for pointer to allocated memory
+ *
+ * RETURNS
+ *  Success: S_OK. *lppBuffer is filled with a pointer to a memory block of
+ *           length cbSize bytes.
+ *  Failure: MAPI_E_INVALID_PARAMETER, if lppBuffer is NULL.
+ *           MAPI_E_NOT_ENOUGH_MEMORY, if the memory allocation fails.
+ *
+ * NOTES
+ *  Memory allocated with this function should be freed with MAPIFreeBuffer().
+ *  Further allocations of memory may be linked to the pointer returned using
+ *  MAPIAllocateMore(). Linked allocations are freed when the initial pointer
+ *  is feed.
+ */
+SCODE WINAPI MAPIAllocateBuffer(ULONG cbSize, LPVOID *lppBuffer)
+{
+    LPMAPIALLOCBUFFER lpBuff;
+
+    TRACE("(%ld,%p)\n", cbSize, lppBuffer);
+
+    if (!lppBuffer)
+        return E_INVALIDARG;
+
+    lpBuff = HeapAlloc(GetProcessHeap(), 0, cbSize + sizeof(*lpBuff));
+    if (!lpBuff)
+        return MAPI_E_NOT_ENOUGH_MEMORY;
+
+    TRACE("initial allocation:%p, returning %p\n", lpBuff, lpBuff + 1);
+    *lpBuff++ = NULL;
+    *lppBuffer = lpBuff;
+    return S_OK;
+}
+
+/**************************************************************************
+ *  MAPIAllocateMore    (MAPI32.14)
+ *  MAPIAllocateMore@12 (MAPI32.15)
+ *
+ * Allocate a block of memory linked to a previous allocation.
+ *
+ * PARAMS
+ *  cbSize    [I] Size of the block to allocate in bytes
+ *  lpOrig    [I] Initial allocation to link to, from MAPIAllocateBuffer()
+ *  lppBuffer [O] Destination for pointer to allocated memory
+ *
+ * RETURNS
+ *  Success: S_OK. *lppBuffer is filled with a pointer to a memory block of
+ *           length cbSize bytes.
+ *  Failure: MAPI_E_INVALID_PARAMETER, if lpOrig or lppBuffer is invalid.
+ *           MAPI_E_NOT_ENOUGH_MEMORY, if memory allocation fails.
+ *
+ * NOTES
+ *  Memory allocated with this function and stored in *lppBuffer is freed
+ *  when lpOrig is passed to MAPIFreeBuffer(). It should not be freed independently.
+ */
+SCODE WINAPI MAPIAllocateMore(ULONG cbSize, LPVOID lpOrig, LPVOID *lppBuffer)
+{
+    LPMAPIALLOCBUFFER lpBuff = lpOrig;
+
+    TRACE("(%ld,%p,%p)\n", cbSize, lpOrig, lppBuffer);
+
+    if (!lppBuffer || !lpBuff || !--lpBuff)
+        return E_INVALIDARG;
+
+    /* Find the last allocation in the chain */
+    while (*lpBuff)
+    {
+        TRACE("linked:%p->%p\n", lpBuff, *lpBuff);
+        lpBuff = *lpBuff;
+    }
+
+    if (SUCCEEDED(MAPIAllocateBuffer(cbSize, lppBuffer)))
+    {
+        *lpBuff = ((LPMAPIALLOCBUFFER)*lppBuffer) - 1;
+        TRACE("linking %p->%p\n", lpBuff, *lpBuff);
+    }
+    return *lppBuffer ? S_OK : MAPI_E_NOT_ENOUGH_MEMORY;
+}
+
+/**************************************************************************
+ *  MAPIFreeBuffer   (MAPI32.16)
+ *  MAPIFreeBuffer@4 (MAPI32.17)
+ *
+ * Free a block of memory and any linked allocations associated with it.
+ *
+ * PARAMS
+ *  lpBuffer [I] Memory to free, returned from MAPIAllocateBuffer()
+ *
+ * RETURNS
+ *  S_OK.
+ */
+ULONG WINAPI MAPIFreeBuffer(LPVOID lpBuffer)
+{
+    LPMAPIALLOCBUFFER lpBuff = lpBuffer;
+
+    TRACE("(%p)\n", lpBuffer);
+
+    if (lpBuff && --lpBuff)
+    {
+        while (lpBuff)
+        {
+            LPVOID lpFree = lpBuff;
+
+            lpBuff = *lpBuff;
+
+            TRACE("linked:%p->%p, freeing %p\n", lpFree, lpBuff, lpFree);
+            HeapFree(GetProcessHeap(), 0, lpFree);
+        }
+    }
+    return S_OK;
+}
+
+/**************************************************************************
+ *  WrapProgress@20 (MAPI32.41)
+ */
+HRESULT WINAPI WrapProgress(PVOID unk1, PVOID unk2, PVOID unk3, PVOID unk4, PVOID unk5)
+{
+    /* Native does not implement this function */
+    return MAPI_E_NO_SUPPORT;
+}
+
+/*************************************************************************
+ * HrThisThreadAdviseSink@8 (MAPI32.42)
+ *
+ * Ensure that an advise sink is only notified in its originating thread.
+ *
+ * PARAMS
+ *  lpSink     [I] IMAPIAdviseSink interface to be protected
+ *  lppNewSink [I] Destination for wrapper IMAPIAdviseSink interface
+ *
+ * RETURNS
+ * Success: S_OK. *lppNewSink contains a new sink to use in place of lpSink.
+ * Failure: E_INVALIDARG, if any parameter is invalid.
+ */
+HRESULT WINAPI HrThisThreadAdviseSink(LPMAPIADVISESINK lpSink, LPMAPIADVISESINK* lppNewSink)
+{
+    FIXME("(%p,%p)semi-stub\n", lpSink, lppNewSink);
+
+    if (!lpSink || !lppNewSink)
+        return E_INVALIDARG;
+
+    /* Don't wrap the sink for now, just copy it */
+    *lppNewSink = lpSink;
+    IMAPIAdviseSink_AddRef(lpSink);
+    return S_OK;
+}
+
+/*************************************************************************
+ * FBinFromHex (MAPI32.44)
+ *
+ * Create an array of binary data from a string.
+ *
+ * PARAMS
+ *  lpszHex [I] String to convert to binary data
+ *  lpOut   [O] Destination for resulting binary data
+ *
+ * RETURNS
+ *  Success: TRUE. lpOut contains the decoded binary data.
+ *  Failure: FALSE, if lpszHex does not represent a binary string.
+ *
+ * NOTES
+ *  - lpOut must be at least half the length of lpszHex in bytes.
+ *  - Although the Mapi headers prototype this function as both
+ *    Ascii and Unicode, there is only one (Ascii) implementation. This
+ *    means that lpszHex is treated as an Ascii string (i.e. a single NUL
+ *    character in the byte stream terminates the string).
+ */
+BOOL WINAPI FBinFromHex(LPWSTR lpszHex, LPBYTE lpOut)
+{
+    LPSTR lpStr = (LPSTR)lpszHex;
+
+    TRACE("(%p,%p)\n", lpszHex, lpOut);
+
+    while (*lpStr)
+    {
+        if (lpStr[0] < '0' || lpStr[0] > 'f' || digitsToHex[lpStr[0] - '0'] == 0xff ||
+            lpStr[1] < '0' || lpStr[1] > 'f' || digitsToHex[lpStr[1] - '0'] == 0xff)
+            return FALSE;
+
+        *lpOut++ = (digitsToHex[lpStr[0] - '0'] << 4) | digitsToHex[lpStr[1] - '0'];
+        lpStr += 2;
+    }
+    return TRUE;
+}
+
+/*************************************************************************
+ * HexFromBin (MAPI32.45)
+ *
+ * Create a string from an array of binary data.
+ *
+ * PARAMS
+ *  lpHex   [I] Binary data to convert to string
+ *  iCount  [I] Length of lpHex in bytes
+ *  lpszOut [O] Destination for resulting hex string
+ *
+ * RETURNS
+ *  Nothing.
+ *
+ * NOTES
+ *  - lpszOut must be at least 2 * iCount + 1 bytes characters long.
+ *  - Although the Mapi headers prototype this function as both
+ *    Ascii and Unicode, there is only one (Ascii) implementation. This
+ *    means that the resulting string is not properly NUL terminated
+ *    if the caller expects it to be a Unicode string.
+ */
+void WINAPI HexFromBin(LPBYTE lpHex, int iCount, LPWSTR lpszOut)
+{
+    static const char hexDigits[] = { "0123456789ABCDEF" };
+    LPSTR lpStr = (LPSTR)lpszOut;
+
+    TRACE("(%p,%d,%p)\n", lpHex, iCount, lpszOut);
+
+    while (iCount-- > 0)
+    {
+        *lpStr++ = hexDigits[*lpHex >> 4];
+        *lpStr++ = hexDigits[*lpHex & 0xf];
+        lpHex++;
+    }
+    *lpStr = '\0';
+}
+
+/*************************************************************************
+ * SwapPlong@8 (MAPI32.47)
+ *
+ * Swap the bytes in a ULONG array.
+ *
+ * PARAMS
+ *  lpData [O] Array to swap bytes in
+ *  ulLen  [I] Number of ULONG element to swap the bytes of
+ *
+ * RETURNS
+ *  Nothing.
+ */
+VOID WINAPI SwapPlong(PULONG lpData, ULONG ulLen)
+{
+    ULONG i;
+
+    for (i = 0; i < ulLen; i++)
+        lpData[i] = RtlUlongByteSwap(lpData[i]);
+}
+
+/*************************************************************************
+ * SwapPword@8 (MAPI32.48)
+ *
+ * Swap the bytes in a USHORT array.
+ *
+ * PARAMS
+ *  lpData [O] Array to swap bytes in
+ *  ulLen  [I] Number of USHORT element to swap the bytes of
+ *
+ * RETURNS
+ *  Nothing.
+ */
+VOID WINAPI SwapPword(PUSHORT lpData, ULONG ulLen)
+{
+    ULONG i;
+
+    for (i = 0; i < ulLen; i++)
+        lpData[i] = RtlUshortByteSwap(lpData[i]);
+}
+
+/**************************************************************************
+ *  MNLS_lstrlenW@4 (MAPI32.62)
+ *
+ * Calculate the length of a Unicode string.
+ *
+ * PARAMS
+ *  lpszStr [I] String to calculate the length of
+ *
+ * RETURNS
+ *  The length of lpszStr in Unicode characters.
+ */
+ULONG WINAPI MNLS_lstrlenW(LPCWSTR lpszStr)
+{
+    TRACE("(%s)\n", debugstr_w(lpszStr));
+    return strlenW(lpszStr);
+}
+
+/*************************************************************************
+ * MNLS_lstrcmpW@8 (MAPI32.63)
+ *
+ * Compare two Unicode strings.
+ *
+ * PARAMS
+ *  lpszLeft  [I] First string to compare
+ *  lpszRight [I] Second string to compare
+ *
+ * RETURNS
+ *  An integer less than, equal to or greater than 0, indicating that
+ *  lpszLeft is less than, the same, or greater than lpszRight.
+ */
+INT WINAPI MNLS_lstrcmpW(LPCWSTR lpszLeft, LPCWSTR lpszRight)
+{
+    TRACE("(%s,%s)\n", debugstr_w(lpszLeft), debugstr_w(lpszRight));
+    return strcmpW(lpszLeft, lpszRight);
+}
+
+/*************************************************************************
+ * MNLS_lstrcpyW@8 (MAPI32.64)
+ *
+ * Copy a Unicode string to another string.
+ *
+ * PARAMS
+ *  lpszDest [O] Destination string
+ *  lpszSrc  [I] Source string
+ *
+ * RETURNS
+ *  The length lpszDest in Unicode characters.
+ */
+ULONG WINAPI MNLS_lstrcpyW(LPWSTR lpszDest, LPCWSTR lpszSrc)
+{
+    ULONG len;
+
+    TRACE("(%p,%s)\n", lpszDest, debugstr_w(lpszSrc));
+    len = (strlenW(lpszSrc) + 1) * sizeof(WCHAR);
+    memcpy(lpszDest, lpszSrc, len);
+    return len;
+}
+
+/*************************************************************************
+ * MNLS_CompareStringW@12 (MAPI32.65)
+ *
+ * Compare two Unicode strings.
+ *
+ * PARAMS
+ *  dwCp      [I] Code page for the comparison
+ *  lpszLeft  [I] First string to compare
+ *  lpszRight [I] Second string to compare
+ *
+ * RETURNS
+ *  CSTR_LESS_THAN, CSTR_EQUAL or CSTR_GREATER_THAN, indicating that
+ *  lpszLeft is less than, the same, or greater than lpszRight.
+ */
+INT WINAPI MNLS_CompareStringW(DWORD dwCp, LPCWSTR lpszLeft, LPCWSTR lpszRight)
+{
+    INT ret;
+
+    TRACE("0x%08lx,%s,%s\n", dwCp, debugstr_w(lpszLeft), debugstr_w(lpszRight));
+    ret = MNLS_lstrcmpW(lpszLeft, lpszRight);
+    return ret < 0 ? CSTR_LESS_THAN : ret ? CSTR_GREATER_THAN : CSTR_EQUAL;
+}
+
+/**************************************************************************
+ *  FEqualNames@8 (MAPI32.72)
+ *
+ * Compare two Mapi names.
+ *
+ * PARAMS
+ *  lpName1 [I] First name to compare to lpName2
+ *  lpName2 [I] Second name to compare to lpName1
+ *
+ * RETURNS
+ *  TRUE, if the names are the same,
+ *  FALSE, Otherwise.
+ */
+BOOL WINAPI FEqualNames(LPMAPINAMEID lpName1, LPMAPINAMEID lpName2)
+{
+    TRACE("(%p,%p)\n", lpName1, lpName2);
+
+    if (!lpName1 || !lpName2 ||
+        !IsEqualGUID(lpName1->lpguid, lpName2->lpguid) ||
+        lpName1->ulKind != lpName2->ulKind)
+        return FALSE;
+
+    if (lpName1->ulKind == MNID_STRING)
+        return !strcmpW(lpName1->Kind.lpwstrName, lpName2->Kind.lpwstrName);
+
+    return lpName1->Kind.lID == lpName2->Kind.lID ? TRUE : FALSE;
+}
+
+/**************************************************************************
+ *  IsBadBoundedStringPtr@8 (MAPI32.71)
+ *
+ * Determine if a string pointer is valid.
+ *
+ * PARAMS
+ *  lpszStr [I] String to check
+ *  ulLen   [I] Maximum length of lpszStr
+ *
+ * RETURNS
+ *  TRUE, if lpszStr is invalid or longer than ulLen,
+ *  FALSE, otherwise.
+ */
+BOOL WINAPI IsBadBoundedStringPtr(LPCSTR lpszStr, ULONG ulLen)
+{
+    if (!lpszStr || IsBadStringPtrA(lpszStr, -1) || strlen(lpszStr) >= ulLen)
+        return TRUE;
+    return FALSE;
+}
+
+/**************************************************************************
+ *  FtAddFt@16 (MAPI32.121)
+ *
+ * Add two FILETIME's together.
+ *
+ * PARAMS
+ *  ftLeft  [I] FILETIME to add to ftRight
+ *  ftRight [I] FILETIME to add to ftLeft
+ *
+ * RETURNS
+ *  The sum of ftLeft and ftRight
+ */
+LONGLONG WINAPI MAPI32_FtAddFt(FILETIME ftLeft, FILETIME ftRight)
+{
+    LONGLONG *pl = (LONGLONG*)&ftLeft, *pr = (LONGLONG*)&ftRight;
+
+    return *pl + *pr;
+}
+
+/**************************************************************************
+ *  FtSubFt@16 (MAPI32.123)
+ *
+ * Subtract two FILETIME's together.
+ *
+ * PARAMS
+ *  ftLeft  [I] Initial FILETIME
+ *  ftRight [I] FILETIME to subtract from ftLeft
+ *
+ * RETURNS
+ *  The remainder after ftRight is subtracted from ftLeft.
+ */
+LONGLONG WINAPI MAPI32_FtSubFt(FILETIME ftLeft, FILETIME ftRight)
+{
+    LONGLONG *pl = (LONGLONG*)&ftLeft, *pr = (LONGLONG*)&ftRight;
+
+    return *pr - *pl;
+}
+
+/**************************************************************************
+ *  FtMulDw@12 (MAPI32.124)
+ *
+ * Multiply a FILETIME by a DWORD.
+ *
+ * PARAMS
+ *  dwLeft  [I] DWORD to multiply with ftRight
+ *  ftRight [I] FILETIME to multiply with dwLeft
+ *
+ * RETURNS
+ *  The product of dwLeft and ftRight
+ */
+LONGLONG WINAPI MAPI32_FtMulDw(DWORD dwLeft, FILETIME ftRight)
+{
+    LONGLONG *pr = (LONGLONG*)&ftRight;
+
+    return (LONGLONG)dwLeft * (*pr);
+}
+
+/**************************************************************************
+ *  FtMulDwDw@8 (MAPI32.125)
+ *
+ * Multiply two DWORD, giving the result as a FILETIME.
+ *
+ * PARAMS
+ *  dwLeft  [I] DWORD to multiply with dwRight
+ *  dwRight [I] DWORD to multiply with dwLeft
+ *
+ * RETURNS
+ *  The product of ftMultiplier and ftMultiplicand as a FILETIME.
+ */
+LONGLONG WINAPI MAPI32_FtMulDwDw(DWORD dwLeft, DWORD dwRight)
+{
+    return (LONGLONG)dwLeft * (LONGLONG)dwRight;
+}
+
+/**************************************************************************
+ *  FtNegFt@8 (MAPI32.126)
+ *
+ * Negate a FILETIME.
+ *
+ * PARAMS
+ *  ft [I] FILETIME to negate
+ *
+ * RETURNS
+ *  The negation of ft.
+ */
+LONGLONG WINAPI MAPI32_FtNegFt(FILETIME ft)
+{
+    LONGLONG *p = (LONGLONG*)&ft;
+
+    return - *p;
+}
+
+/**************************************************************************
+ *  UlAddRef@4 (MAPI32.128)
+ *
+ * Add a reference to an object.
+ *
+ * PARAMS
+ *  lpUnk [I] Object to add a reference to.
+ *
+ * RETURNS
+ *  The new reference count of the object, or 0 if lpUnk is NULL.
+ *
+ * NOTES
+ * See IUnknown_AddRef.
+ */
+ULONG WINAPI UlAddRef(void *lpUnk)
+{
+    TRACE("(%p)\n", lpUnk);
+
+    if (!lpUnk)
+        return 0UL;
+    return IUnknown_AddRef((LPUNKNOWN)lpUnk);
+}
+
+/**************************************************************************
+ *  UlRelease@4 (MAPI32.129)
+ *
+ * Remove a reference from an object.
+ *
+ * PARAMS
+ *  lpUnk [I] Object to remove reference from.
+ *
+ * RETURNS
+ *  The new reference count of the object, or 0 if lpUnk is NULL. If lpUnk is
+ *  non-NULL and this function returns 0, the object pointed to by lpUnk has
+ *  been released.
+ *
+ * NOTES
+ * See IUnknown_Release.
+ */
+ULONG WINAPI UlRelease(void *lpUnk)
+{
+    TRACE("(%p)\n", lpUnk);
+
+    if (!lpUnk)
+        return 0UL;
+    return IUnknown_Release((LPUNKNOWN)lpUnk);
+}
+
+/**************************************************************************
+ *  UFromSz@4 (MAPI32.133)
+ *
+ * Read an integer from a string
+ *
+ * PARAMS
+ *  lpszStr [I] String to read the integer from.
+ *
+ * RETURNS
+ *  Success: The integer read from lpszStr.
+ *  Failure: 0, if the first character in lpszStr is not 0-9.
+ *
+ * NOTES
+ *  This function does not accept whitespace and stops at the first non-digit
+ *  character.
+ */
+UINT WINAPI UFromSz(LPCSTR lpszStr)
+{
+    ULONG ulRet = 0;
+
+    TRACE("(%s)\n", debugstr_a(lpszStr));
+
+    if (lpszStr)
+    {
+        while (*lpszStr >= '0' && *lpszStr <= '9')
+        {
+            ulRet = ulRet * 10 + (*lpszStr - '0');
+            lpszStr = CharNextA(lpszStr);
+        }
+    }
+    return ulRet;
+}
+
+/*************************************************************************
+ * OpenStreamOnFile@24 (MAPI32.147)
+ *
+ * Create a stream on a file.
+ *
+ * PARAMS
+ *  lpAlloc    [I] Memory allocation function
+ *  lpFree     [I] Memory free function
+ *  ulFlags    [I] Flags controlling the opening process
+ *  lpszPath   [I] Path of file to create stream on
+ *  lpszPrefix [I] Prefix of the temporary file name (if ulFlags includes SOF_UNIQUEFILENAME)
+ *  lppStream  [O] Destination for created stream
+ *
+ * RETURNS
+ * Success: S_OK. lppStream contains the new stream object
+ * Failure: E_INVALIDARG if any parameter is invalid, or an HRESULT error code
+ *          describing the error.
+ */
+HRESULT WINAPI OpenStreamOnFile(LPALLOCATEBUFFER lpAlloc, LPFREEBUFFER lpFree,
+                                ULONG ulFlags, LPWSTR lpszPath, LPWSTR lpszPrefix,
+                                LPSTREAM *lppStream)
+{
+    WCHAR szBuff[MAX_PATH];
+    DWORD dwMode = STGM_READWRITE, dwAttributes = 0;
+    HRESULT hRet;
+
+    TRACE("(%p,%p,0x%08lx,%s,%s,%p)\n", lpAlloc, lpFree, ulFlags,
+          debugstr_a((LPSTR)lpszPath), debugstr_a((LPSTR)lpszPrefix), lppStream);
+
+    if (lppStream)
+        *lppStream = NULL;
+
+    if (ulFlags & SOF_UNIQUEFILENAME)
+    {
+        FIXME("Should generate a temporary name\n");
+        return E_INVALIDARG;
+    }
+
+    if (!lpszPath || !lppStream)
+        return E_INVALIDARG;
+
+    /* FIXME: Should probably munge mode and attributes, and should handle
+     *        Unicode arguments (I assume MAPI_UNICODE is set in ulFlags if
+     *        we are being passed Unicode strings; MSDN doesn't say).
+     *        This implementation is just enough for Outlook97 to start.
+     */
+    MultiByteToWideChar(CP_ACP, 0, (LPSTR)lpszPath, -1, szBuff, MAX_PATH);
+    hRet = SHCreateStreamOnFileEx(szBuff, dwMode, dwAttributes, TRUE,
+                                  NULL, lppStream);
+    return hRet;
+}
+
+/*************************************************************************
+ * UlFromSzHex@4 (MAPI32.155)
+ *
+ * Read an integer from a hexadecimal string.
+ *
+ * PARAMS
+ *  lpSzHex [I] String containing the hexadecimal number to read
+ *
+ * RETURNS
+ * Success: The number represented by lpszHex.
+ * Failure: 0, if lpszHex does not contain a hex string.
+ *
+ * NOTES
+ *  This function does not accept whitespace and stops at the first non-hex
+ *  character.
+ */
+ULONG WINAPI UlFromSzHex(LPCWSTR lpszHex)
+{
+    LPSTR lpStr = (LPSTR)lpszHex;
+    ULONG ulRet = 0;
+
+    TRACE("(%s)\n", debugstr_a(lpStr));
+
+    while (*lpStr)
+    {
+        if (lpStr[0] < '0' || lpStr[0] > 'f' || digitsToHex[lpStr[0] - '0'] == 0xff ||
+            lpStr[1] < '0' || lpStr[1] > 'f' || digitsToHex[lpStr[1] - '0'] == 0xff)
+            break;
+
+        ulRet = ulRet * 16 + ((digitsToHex[lpStr[0] - '0'] << 4) | digitsToHex[lpStr[1] - '0']);
+        lpStr += 2;
+    }
+    return ulRet;
+}
+
+/************************************************************************
+ * FBadEntryList@4 (MAPI32.190)
+ *
+ * Determine is an entry list is invalid.
+ *
+ * PARAMS
+ *  lpEntryList [I] List to check
+ *
+ * RETURNS
+ *  TRUE, if lpEntryList is invalid,
+ *  FALSE, otherwise.
+ */
+BOOL WINAPI FBadEntryList(LPENTRYLIST lpEntryList)
+{
+    ULONG i;
+
+    if (IsBadReadPtr(lpEntryList, sizeof(*lpEntryList)) ||
+        IsBadReadPtr(lpEntryList->lpbin,
+                     lpEntryList->cValues * sizeof(*lpEntryList->lpbin)))
+        return TRUE;
+
+    for (i = 0; i < lpEntryList->cValues; i++)
+        if(IsBadReadPtr(lpEntryList->lpbin[i].lpb, lpEntryList->lpbin[i].cb))
+            return TRUE;
+
+    return FALSE;
+}
+
+/*************************************************************************
+ * CbOfEncoded@4 (MAPI32.207)
+ *
+ * Return the length of an encoded string.
+ *
+ * PARAMS
+ *  lpSzEnc [I] Encoded string to get the length of.
+ *
+ * RETURNS
+ * The length of the encoded string in bytes.
+ */
+ULONG WINAPI CbOfEncoded(LPCSTR lpszEnc)
+{
+    ULONG ulRet = 0;
+
+    TRACE("(%s)\n", debugstr_a(lpszEnc));
+
+    if (lpszEnc)
+        ulRet = (((strlen(lpszEnc) | 3) >> 2) + 1) * 3;
+    return ulRet;
+}
+
+/*************************************************************************
+ * cmc_query_configuration (MAPI32.235)
+ *
+ * Retrieves the configuration information for the installed CMC
+ *
+ * PARAMS
+ *  session          [I]   MAPI session handle
+ *  item             [I]   Enumerated variable that identifies which 
+ *                         configuration information is being requested
+ *  reference        [O]   Buffer where configuration information is written
+ *  config_extensions[I/O] Path of file to create stream on
+ *
+ * RETURNS
+ * A CMD define
+ */
+CMC_return_code WINAPI cmc_query_configuration(
+  CMC_session_id session,
+  CMC_enum item,
+  CMC_buffer reference,
+  CMC_extension  *config_extensions)
+{
+       FIXME("stub");
+       return CMC_E_NOT_SUPPORTED;
+}
diff --git a/reactos/dll/mmdrv/auxil.c b/reactos/dll/mmdrv/auxil.c
new file mode 100644 (file)
index 0000000..b3a7f73
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ *
+ * COPYRIGHT:            See COPYING in the top level directory
+ * PROJECT:              ReactOS Multimedia
+ * FILE:                 lib/mmdrv/auxil.c
+ * PURPOSE:              Multimedia User Mode Driver
+ * PROGRAMMER:           Andrew Greenwood
+ *                       Aleksey Bragin
+ * UPDATE HISTORY:
+ *                       Mar 16, 2004: Created skeleton implementation
+ */
+#include "mmdrv.h"
+#include "wave.h"
+
+
+#define NDEBUG
+#include <debug.h>
+
+APIENTRY DWORD auxMessage(UINT dwId,
+                  UINT uMessage,
+                  DWORD dwUser,
+                  DWORD dwParam1,
+                  DWORD dwParam2)
+
+{
+    MMRESULT Result;
+    AUX_DD_VOLUME Volume;
+
+    DPRINT("auxMessage\n");
+
+
+       // the following cases are documented by DDK
+       switch (uMessage)
+       {
+       case AUXDM_GETDEVCAPS:
+               DPRINT("AUXDM_GETDEVCAPS");
+               return GetDeviceCapabilities(dwId, AuxDevice, (LPBYTE)dwParam1, (DWORD)dwParam2);
+
+       case AUXDM_GETNUMDEVS:
+               DPRINT("AUXDM_GETNUMDEVS");
+               return GetDeviceCount(AuxDevice);
+               
+       case AUXDM_GETVOLUME:
+         DPRINT("AUXDM_GETVOLUME");
+         Result = AuxGetAudio(dwId, (PBYTE) &Volume, sizeof(Volume));
+         
+         if (Result == MMSYSERR_NOERROR) 
+         {
+            *(LPDWORD)dwParam1 = (DWORD)MAKELONG(HIWORD(Volume.Left), HIWORD(Volume.Right));
+         }
+         return Result;
+               
+
+       case AUXDM_SETVOLUME:
+        DPRINT("AUXDM_SETVOLUME");
+         
+        Volume.Right = HIWORD(dwParam1) << 16;
+        Volume.Left = LOWORD(dwParam1) << 16;
+        
+        return AuxSetAudio(dwId, (PBYTE)&Volume, sizeof(Volume));
+
+       }
+
+    return MMSYSERR_NOERROR;
+}
+
+
+DWORD AuxGetAudio(DWORD dwID, PBYTE pVolume, DWORD sizeVolume)
+{
+    HANDLE DeviceHandle;
+    MMRESULT Result;
+    DWORD BytesReturned;
+
+    Result = OpenDevice(AuxDevice, dwID, &DeviceHandle, GENERIC_READ);
+    if (Result != MMSYSERR_NOERROR)
+         return Result;
+
+    
+    Result = DeviceIoControl(DeviceHandle, IOCTL_AUX_GET_VOLUME, NULL, 0, (LPVOID)pVolume, sizeVolume,
+                           &BytesReturned, NULL) ? MMSYSERR_NOERROR : TranslateStatus();
+
+
+    CloseHandle(DeviceHandle);
+
+    return Result;
+ }
+
+DWORD AuxSetAudio(DWORD dwID, PBYTE pVolume, DWORD sizeVolume)
+{
+    HANDLE DeviceHandle;
+    MMRESULT Result;
+    DWORD BytesReturned;
+
+    Result = OpenDevice(AuxDevice, dwID, &DeviceHandle, GENERIC_READ);
+    if (Result != MMSYSERR_NOERROR)
+         return Result;
+    
+    Result = DeviceIoControl(DeviceHandle, IOCTL_AUX_SET_VOLUME, (LPVOID)pVolume, sizeVolume, NULL, 0, 
+                           &BytesReturned, NULL) ? MMSYSERR_NOERROR : TranslateStatus();
+
+
+    CloseHandle(DeviceHandle);
+
+    return Result;
+ }
+
diff --git a/reactos/dll/mmdrv/entry.c b/reactos/dll/mmdrv/entry.c
new file mode 100644 (file)
index 0000000..d2beefe
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ *
+ * COPYRIGHT:            See COPYING in the top level directory
+ * PROJECT:              ReactOS Multimedia
+ * FILE:                 lib/mmdrv/entry.c
+ * PURPOSE:              Multimedia User Mode Driver
+ * PROGRAMMER:           Andrew Greenwood
+ *                       Aleksey Bragin
+ * UPDATE HISTORY:
+ *                       Jan 30, 2004: Imported into ReactOS tree (Greenwood)
+ *                       Mar 16, 2004: Cleaned up a bit (Bragin)
+ */
+
+
+#include "mmdrv.h"
+
+#define NDEBUG
+#include <debug.h>
+
+#define EXPORT __declspec(dllexport)
+
+CRITICAL_SECTION DriverSection;
+
+APIENTRY LONG DriverProc(DWORD DriverID, HANDLE DriverHandle, UINT Message,
+                LONG Param1, LONG Param2)
+{
+    DPRINT("DriverProc\n");
+
+//    HINSTANCE Module;
+
+    switch(Message)
+    {
+        case DRV_LOAD :
+            DPRINT("DRV_LOAD\n");
+            return TRUE; // dont need to do any more
+/*            
+            Module = GetDriverModuleHandle(DriverHandle);
+
+        // Create our process heap
+        Heap = GetProcessHeap();
+        if (Heap == NULL)
+            return FALSE;
+
+        DisableThreadLibraryCalls(Module);
+        InitializeCriticalSection(&CS);
+
+        //
+        // Load our device list
+        //
+
+//        if (sndFindDevices() != MMSYSERR_NOERROR) {
+//            DeleteCriticalSection(&mmDrvCritSec);
+//            return FALSE;
+//        }
+
+    return TRUE;
+*/
+//            return 1L;
+
+        case DRV_FREE :
+            DPRINT("DRV_FREE\n");
+
+//            TerminateMidi();
+//            TerminateWave();
+
+//            DeleteCriticalSection(&CS);
+            return 1L;
+
+        case DRV_OPEN :
+            DPRINT("DRV_OPEN\n");
+            return 1L;
+
+        case DRV_CLOSE :
+            DPRINT("DRV_CLOSE\n");
+            return 1L;
+
+        case DRV_ENABLE :
+            DPRINT("DRV_ENABLE\n");
+            return 1L;
+
+        case DRV_DISABLE :
+            DPRINT("DRV_DISABLE\n");
+            return 1L;
+
+        case DRV_QUERYCONFIGURE :
+            DPRINT("DRV_QUERYCONFIGURE\n");
+            return 0L;
+
+        case DRV_CONFIGURE :
+            DPRINT("DRV_CONFIGURE\n");
+            return 0L;
+
+        case DRV_INSTALL :
+            DPRINT("DRV_INSTALL\n");
+            return DRVCNF_RESTART;
+
+        default :
+            DPRINT("?\n");
+            return DefDriverProc(DriverID, DriverHandle, Message, Param1, Param2);
+    };
+}
+
+
+BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD Reason, LPVOID Reserved)
+{
+    DPRINT("DllMain called!\n");
+
+    if (Reason == DLL_PROCESS_ATTACH)
+    {
+        DisableThreadLibraryCalls(hInstance);
+
+        // Create our heap
+        Heap = HeapCreate(0, 800, 0);
+        if (Heap == NULL)
+            return FALSE;
+
+        InitializeCriticalSection(&CS);
+
+        // OK to do this now??        
+        FindDevices();
+
+    }
+    else if (Reason == DLL_PROCESS_DETACH)
+    {
+        // We need to do cleanup here...
+//        TerminateMidi();
+//        TerminateWave();
+
+        DeleteCriticalSection(&CS);
+        HeapDestroy(Heap);
+    }
+    
+    return TRUE;
+}
+
+/* EOF */
diff --git a/reactos/dll/mmdrv/midi.c b/reactos/dll/mmdrv/midi.c
new file mode 100644 (file)
index 0000000..ac2299a
--- /dev/null
@@ -0,0 +1,357 @@
+/*
+ *
+ * COPYRIGHT:            See COPYING in the top level directory
+ * PROJECT:              ReactOS Multimedia
+ * FILE:                 lib/mmdrv/midi.c
+ * PURPOSE:              Multimedia User Mode Driver
+ * PROGRAMMER:           Andrew Greenwood
+ * UPDATE HISTORY:
+ *                       Jan 30, 2004: Imported into ReactOS tree
+ */
+
+#include "mmdrv.h"
+#include "wave.h"
+
+#define NDEBUG
+#include <debug.h>
+
+// MIDI device instance information
+//
+#define LOCAL_DATA_SIZE 20
+typedef struct _LOCALMIDIHDR {
+    OVERLAPPED          Ovl;
+    DWORD               BytesReturned;
+    struct _LOCALMIDIHDR *lpNext;       
+    BOOL                Done;           
+    PVOID               pClient;        
+  //  MIDI_DD_INPUT_DATA  MidiData;       
+    BYTE                ExtraData[LOCAL_DATA_SIZE - sizeof(ULONG)];
+                                        
+} LOCALMIDIHDR, *PLOCALMIDIHDR;
+
+#define LOCAL_MIDI_BUFFERS 8
+
+typedef struct {
+    
+    BOOL                fMidiInStarted;
+    DWORD               dwMsg;         
+    DWORD               dwCurData;     
+    BYTE                status;        
+    BOOLEAN             fSysex;        
+    BOOLEAN             Bad;           
+    BYTE                bBytesLeft;    
+    BYTE                bBytePos;      
+    DWORD               dwCurTime;     
+    DWORD               dwMsgTime;     
+                                       
+                                       
+    PLOCALMIDIHDR       DeviceQueue;   
+                                       
+    LOCALMIDIHDR                       
+    Bufs[LOCAL_MIDI_BUFFERS];
+                                       
+                                       
+} LOCALMIDIDATA, *PLOCALMIDIDATA;
+
+
+typedef struct tag_MIDIALLOC {
+    struct tag_MIDIALLOC *Next;         // Chain of devices
+    UINT                DeviceNumber;   // Number of device
+    UINT                DeviceType;     // MidiInput or MidiOutput
+    DWORD               dwCallback;     // client's callback
+    DWORD               dwInstance;     // client's instance data
+    HMIDI               hMidi;          // handle for stream
+    HANDLE              DeviceHandle;   // Midi device handle
+    LPMIDIHDR           lpMIQueue;      // Buffers sent to device
+                                        // This is only required so that
+                                        // CLOSE knows when things have
+                                        // really finished.
+                                        // notify.  This is only accessed
+                                        // on the device thread and its
+                                        // apcs so does not need any
+                                        // synchronized access.
+    HANDLE              Event;          // Event for driver syncrhonization
+                                        // and notification of auxiliary
+                                        // task operation completion.
+//    MIDITHREADFUNCTION  AuxFunction;    // Function for thread to perform
+    union {
+        LPMIDIHDR       pHdr;           // Buffer to pass in aux task
+        ULONG           State;          // State to set
+        struct {
+            ULONG       Function;       // IOCTL to use
+            PBYTE       pData;          // Data to set or get
+            ULONG       DataLen;        // Length of data
+        } GetSetData;
+
+    } AuxParam;
+                                        // 0 means terminate task.
+    HANDLE              ThreadHandle;   // Handle for termination ONLY
+    HANDLE              AuxEvent1;      // Aux thread waits on this
+    HANDLE              AuxEvent2;      // Aux thread caller waits on this
+    DWORD               AuxReturnCode;  // Return code from Aux task
+    DWORD               dwFlags;        // Open flags
+    PLOCALMIDIDATA      Mid;            // Extra midi input structures
+    int                 l;              // Helper global for modMidiLength
+
+} MIDIALLOC, *PMIDIALLOC;
+
+PMIDIALLOC MidiHandleList;              // Our chain of wave handles
+
+
+
+static DWORD OpenMidiDevice(UINT DeviceType, DWORD ID, DWORD User, DWORD Param1, DWORD Param2)
+{
+    PMIDIALLOC pClient = NULL;
+    MMRESULT Result = MMSYSERR_NOERROR;
+    
+    // Check ID?
+    DPRINT("OpenMidiDevice()\n");
+    
+    switch(DeviceType)
+    {
+        case MidiOutDevice :
+            pClient = (PMIDIALLOC) HeapAlloc(Heap, 0, sizeof(MIDIALLOC));
+            if ( pClient ) memset(pClient, 0, sizeof(MIDIALLOC));
+            break;
+        
+        case MidiInDevice :
+            pClient = (PMIDIALLOC) HeapAlloc(Heap, 0, sizeof(MIDIALLOC) + sizeof(LOCALMIDIDATA));
+                       if ( pClient ) memset(pClient, 0, sizeof(MIDIALLOC) + sizeof(LOCALMIDIDATA));
+            break;
+    };
+    
+    if ( !pClient )
+        return MMSYSERR_NOMEM;
+    
+       if (DeviceType == MidiInDevice) 
+       {
+        int i;
+        pClient->Mid = (PLOCALMIDIDATA)(pClient + 1);
+        for (i = 0 ;i < LOCAL_MIDI_BUFFERS ; i++) 
+               {
+            pClient->Mid->Bufs[i].pClient = pClient;
+        }
+    }
+
+    pClient->DeviceType = DeviceType;
+    pClient->dwCallback = ((LPMIDIOPENDESC)Param1)->dwCallback;
+    pClient->dwInstance = ((LPMIDIOPENDESC)Param1)->dwInstance;
+    pClient->hMidi = ((LPMIDIOPENDESC)Param1)->hMidi;
+    pClient->dwFlags = Param2;
+    
+    Result = OpenDevice(DeviceType, ID, &pClient->DeviceHandle, (GENERIC_READ | GENERIC_WRITE));
+    
+    if ( Result != MMSYSERR_NOERROR )
+    {
+        // cleanup
+        return Result;
+    }
+    
+    pClient->Event = CreateEvent(NULL, FALSE, FALSE, NULL);
+    
+    if ( !pClient->Event )
+    {
+        // cleanup
+        return MMSYSERR_NOMEM;
+    }
+
+       if (DeviceType == MidiInDevice) 
+       {
+
+        pClient->AuxEvent1 = CreateEvent(NULL, FALSE, FALSE, NULL);
+        if (pClient->AuxEvent1 == NULL) 
+               {
+            // cleanup
+            return MMSYSERR_NOMEM;
+        }
+        
+               pClient->AuxEvent2 = CreateEvent(NULL, FALSE, FALSE, NULL);
+        if (pClient->AuxEvent2 == NULL) 
+               {
+            // cleanup
+            return MMSYSERR_NOMEM;
+        }
+
+        
+        // TaskCreate
+        
+        
+       WaitForSingleObject(pClient->AuxEvent2, INFINITE);
+    }
+
+    PMIDIALLOC *pUserHandle;
+    pUserHandle = (PMIDIALLOC*) User;
+    *pUserHandle = pClient;
+    
+    // callback    
+
+    return MMSYSERR_NOERROR;
+}
+
+
+
+static DWORD WriteMidi(PBYTE pData, ULONG Length, PMIDIALLOC pClient)
+{
+    DWORD BytesReturned;
+
+    DPRINT("IOCTL_MIDI_PLAY == %d [%x]\n", IOCTL_MIDI_PLAY, IOCTL_MIDI_PLAY);
+    
+    if ( !DeviceIoControl(pClient->DeviceHandle, IOCTL_MIDI_PLAY, (PVOID)pData,
+                          Length, NULL, 0, &BytesReturned, NULL))
+        return TranslateStatus();
+
+    return MMSYSERR_NOERROR;
+}
+
+
+static int GetMidiLength(PMIDIALLOC pClient, BYTE b)
+{
+    if (b >= 0xF8)
+    {
+        // Realtime message - leave running status
+        return 1; // Write one byte
+    }
+
+    switch (b)
+    {
+        case 0xF0: case 0xF4: case 0xF5: case 0xF6: case 0xF7:
+            pClient->l = 1;
+            return pClient->l;
+
+        case 0xF1: case 0xF3:
+            pClient->l = 2;
+            return pClient->l;
+
+        case 0xF2:
+            pClient->l = 3;
+            return pClient->l;
+    }
+
+    switch (b & 0xF0)
+    {
+        case 0x80: case 0x90: case 0xA0: case 0xB0: case 0xE0:
+            pClient->l = 3;
+            return pClient->l;
+
+        case 0xC0: case 0xD0:
+            pClient->l = 2;
+            return pClient->l;
+    }
+
+    return (pClient->l - 1); // uses previous value if data byte (running status)
+}
+
+
+
+/* ----------------------------------------------------------------------------
+    Exported functions
+----------------------------------------------------------------------------- */
+
+APIENTRY DWORD midMessage(DWORD dwId, DWORD dwMessage, DWORD dwUser, DWORD dwParam1, DWORD dwParam2)
+{
+    DPRINT("midMessage\n");
+    return MMSYSERR_NOERROR;
+
+    switch (dwMessage) {
+        case MIDM_GETNUMDEVS:
+            DPRINT("MIDM_GETNUMDEVS");
+            return GetDeviceCount(MidiInDevice);
+
+        case MIDM_GETDEVCAPS:
+            DPRINT("MIDM_GETDEVCAPS");
+            return GetDeviceCapabilities(dwId, MidiInDevice, (LPBYTE)dwParam1, (DWORD)dwParam2);
+
+        case MIDM_OPEN:
+            DPRINT("MIDM_OPEN");
+            return MMSYSERR_NOERROR;
+
+        case MIDM_CLOSE:
+            DPRINT("MIDM_CLOSE");
+            return MMSYSERR_NOERROR;
+
+        case MIDM_ADDBUFFER:
+            DPRINT("MIDM_ADDBUFFER");
+            return MMSYSERR_NOERROR;
+
+        case MIDM_STOP:
+            DPRINT("MIDM_PAUSE");
+            return MMSYSERR_NOERROR;
+
+        case MIDM_START:
+            DPRINT("MIDM_RESTART");
+            return MMSYSERR_NOERROR;
+
+        case MIDM_RESET:
+            DPRINT("MIDM_RESET");
+            return MMSYSERR_NOERROR;
+
+        default:
+            return MMSYSERR_NOTSUPPORTED;
+    }
+
+       // the function should never get to this point
+       //FIXME: Would it be wise to assert here?
+    return MMSYSERR_NOTSUPPORTED;
+}
+
+APIENTRY DWORD modMessage(DWORD ID, DWORD Message, DWORD User, DWORD Param1, DWORD Param2)
+{
+    DPRINT("modMessage\n");
+    
+    switch(Message)
+    {
+        case MODM_GETNUMDEVS:
+            DPRINT("MODM_GETNUMDEVS == %d\n", (int)GetDeviceCount(MidiOutDevice));
+            return GetDeviceCount(MidiOutDevice);
+        
+        case MODM_GETDEVCAPS:
+            DPRINT("MODM_GETDEVCAPS");
+            return GetDeviceCapabilities(ID, MidiOutDevice, (LPBYTE)Param1, (DWORD)Param2);
+            
+        case MODM_OPEN :
+            return OpenMidiDevice(MidiOutDevice, ID, User, Param1, Param2);
+
+        case MODM_CLOSE:
+            DPRINT("MODM_CLOSE");
+            return MMSYSERR_NOTSUPPORTED;
+
+        case MODM_DATA:
+            DPRINT("MODM_DATA");
+
+            int i;
+            BYTE b[4];
+            for (i = 0; i < 4; i ++) {
+                b[i] = (BYTE)(Param1 % 256);
+                Param1 /= 256;
+            }
+            return WriteMidi(b, GetMidiLength((PMIDIALLOC)User, b[0]),
+                                (PMIDIALLOC)User);
+
+        case MODM_LONGDATA:
+            DPRINT("MODM_LONGDATA");
+            return MMSYSERR_NOTSUPPORTED;
+
+        case MODM_RESET:
+            DPRINT("MODM_RESET");
+            return MMSYSERR_NOTSUPPORTED;
+
+        case MODM_SETVOLUME:
+            DPRINT("MODM_SETVOLUME");
+            return MMSYSERR_NOTSUPPORTED;
+
+        case MODM_GETVOLUME:
+            DPRINT("MODM_GETVOLUME");
+            return MMSYSERR_NOTSUPPORTED;
+
+        case MODM_CACHEPATCHES:
+            DPRINT("MODM_CACHEPATCHES");
+            return MMSYSERR_NOTSUPPORTED;
+
+        case MODM_CACHEDRUMPATCHES:
+            DPRINT("MODM_CACHEDRUMPATCHES");
+            return MMSYSERR_NOTSUPPORTED;
+            
+    };
+
+    return MMSYSERR_NOTSUPPORTED;
+}
diff --git a/reactos/dll/mmdrv/mmddk.h b/reactos/dll/mmdrv/mmddk.h
new file mode 100644 (file)
index 0000000..2598b08
--- /dev/null
@@ -0,0 +1,471 @@
+/* -*- tab-width: 8; c-basic-offset: 4 -*- */
+
+/*****************************************************************************
+ * Copyright 1998, Luiz Otavio L. Zorzella
+ *           1999, Eric Pouech
+ *
+ * Purpose:   multimedia declarations (external to WINMM & MMSYSTEM DLLs
+ *                                     for other DLLs (MCI, drivers...))
+ *
+ * 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
+ *
+ *****************************************************************************
+ */
+#ifndef __MMDDK_H
+#define __MMDDK_H
+
+#include <mmsystem.h>
+#include <winbase.h>
+
+typedef VOID (TASKCALLBACK) (DWORD dwInst);
+
+typedef TASKCALLBACK FAR *LPTASKCALLBACK;
+
+UINT        APIENTRY mmTaskCreate(LPTASKCALLBACK lpfn, HANDLE FAR * lph, DWORD dwInst);
+VOID        APIENTRY mmTaskBlock(DWORD h);
+BOOL        APIENTRY mmTaskSignal(DWORD h);
+VOID        APIENTRY mmTaskYield(VOID);
+DWORD       APIENTRY mmGetCurrentTask(VOID);
+
+
+#define MAX_MIDIINDRV  (16)
+/* For now I'm making 16 the maximum number of midi devices one can
+ * have. This should be more than enough for everybody. But as a purist,
+ * I intend to make it unbounded in the future, as soon as I figure
+ * a good way to do so.
+ */
+#define MAX_MIDIOUTDRV         (16)
+
+/* ==================================
+ *   Multimedia DDK compatible part
+ * ================================== */
+
+#include <pshpack1.h>
+
+#define DRVM_INIT              100
+#define DRVM_EXIT              101
+#define DRVM_DISABLE           102
+#define DRVM_ENABLE            103
+
+/* messages that have IOCTL format
+ *    dw1 = NULL or handle
+ *    dw2 = NULL or ptr to DRVM_IOCTL_DATA
+ *    return is MMRESULT
+ */
+#define DRVM_IOCTL             0x100
+#define DRVM_ADD_THRU          (DRVM_IOCTL+1)
+#define DRVM_REMOVE_THRU       (DRVM_IOCTL+2)
+#define DRVM_IOCTL_LAST                (DRVM_IOCTL+5)
+typedef struct {
+    DWORD  dwSize;     /* size of this structure */
+    DWORD  dwCmd;      /* IOCTL command code, 0x80000000 and above reserved for system */
+} DRVM_IOCTL_DATA, *LPDRVM_IOCTL_DATA;
+
+/* command code ranges for dwCmd field of DRVM_IOCTL message
+ * - codes from 0 to 0x7FFFFFFF are user defined
+ * - codes from 0x80000000 to 0xFFFFFFFF are reserved for future definition by microsoft
+ */
+#define DRVM_IOCTL_CMD_USER   0x00000000L
+#define DRVM_IOCTL_CMD_SYSTEM 0x80000000L
+
+#define DRVM_MAPPER                    0x2000
+#define DRVM_USER                      0x4000
+#define DRVM_MAPPER_STATUS             (DRVM_MAPPER+0)
+#define DRVM_MAPPER_RECONFIGURE        (DRVM_MAPPER+1)
+#define DRVM_MAPPER_PREFERRED_GET      (DRVM_MAPPER+21)
+#define DRVM_MAPPER_CONSOLEVOICECOM_GET        (DRVM_MAPPER+23)
+
+#define DRV_QUERYDRVENTRY              (DRV_RESERVED + 1)
+#define DRV_QUERYDEVNODE               (DRV_RESERVED + 2)
+#define DRV_QUERYNAME                  (DRV_RESERVED + 3)
+#define DRV_QUERYDRIVERIDS             (DRV_RESERVED + 4)
+#define DRV_QUERYMAPPABLE              (DRV_RESERVED + 5)
+#define DRV_QUERYMODULE                        (DRV_RESERVED + 9)
+#define DRV_PNPINSTALL                 (DRV_RESERVED + 11)
+#define DRV_QUERYDEVICEINTERFACE       (DRV_RESERVED + 12)
+#define DRV_QUERYDEVICEINTERFACESIZE   (DRV_RESERVED + 13)
+#define DRV_QUERYSTRINGID              (DRV_RESERVED + 14)
+#define DRV_QUERYSTRINGIDSIZE          (DRV_RESERVED + 15)
+#define DRV_QUERYIDFROMSTRINGID                (DRV_RESERVED + 16)
+#ifdef __WINESRC__
+#define DRV_QUERYDSOUNDIFACE           (DRV_RESERVED + 20)
+#define DRV_QUERYDSOUNDDESC            (DRV_RESERVED + 21)
+#define DRV_QUERYDSOUNDGUID            (DRV_RESERVED + 22)
+#endif
+
+#define WODM_INIT              DRVM_INIT
+#define WODM_GETNUMDEVS                 3
+#define WODM_GETDEVCAPS                 4
+#define WODM_OPEN               5
+#define WODM_CLOSE              6
+#define WODM_PREPARE            7
+#define WODM_UNPREPARE          8
+#define WODM_WRITE              9
+#define WODM_PAUSE             10
+#define WODM_RESTART           11
+#define WODM_RESET             12
+#define WODM_GETPOS            13
+#define WODM_GETPITCH          14
+#define WODM_SETPITCH          15
+#define WODM_GETVOLUME         16
+#define WODM_SETVOLUME         17
+#define WODM_GETPLAYBACKRATE   18
+#define WODM_SETPLAYBACKRATE   19
+#define WODM_BREAKLOOP         20
+#define WODM_PREFERRED         21
+
+#define WODM_MAPPER_STATUS      (DRVM_MAPPER_STATUS + 0)
+#define WAVEOUT_MAPPER_STATUS_DEVICE    0
+#define WAVEOUT_MAPPER_STATUS_MAPPED    1
+#define WAVEOUT_MAPPER_STATUS_FORMAT    2
+
+#define WODM_BUSY              21
+
+#define WIDM_INIT              DRVM_INIT
+#define WIDM_GETNUMDEVS                50
+#define WIDM_GETDEVCAPS                51
+#define WIDM_OPEN              52
+#define WIDM_CLOSE             53
+#define WIDM_PREPARE           54
+#define WIDM_UNPREPARE         55
+#define WIDM_ADDBUFFER         56
+#define WIDM_START             57
+#define WIDM_STOP              58
+#define WIDM_RESET             59
+#define WIDM_GETPOS            60
+#define WIDM_PREFERRED         61
+#define WIDM_MAPPER_STATUS      (DRVM_MAPPER_STATUS + 0)
+#define WAVEIN_MAPPER_STATUS_DEVICE     0
+#define WAVEIN_MAPPER_STATUS_MAPPED     1
+#define WAVEIN_MAPPER_STATUS_FORMAT     2
+
+#define MODM_INIT              DRVM_INIT
+#define MODM_GETNUMDEVS                1
+#define MODM_GETDEVCAPS                2
+#define MODM_OPEN              3
+#define MODM_CLOSE             4
+#define MODM_PREPARE           5
+#define MODM_UNPREPARE         6
+#define MODM_DATA              7
+#define MODM_LONGDATA          8
+#define MODM_RESET             9
+#define MODM_GETVOLUME         10
+#define MODM_SETVOLUME         11
+#define MODM_CACHEPATCHES      12
+#define MODM_CACHEDRUMPATCHES  13
+
+#define MIDM_INIT              DRVM_INIT
+#define MIDM_GETNUMDEVS        53
+#define MIDM_GETDEVCAPS        54
+#define MIDM_OPEN              55
+#define MIDM_CLOSE             56
+#define MIDM_PREPARE           57
+#define MIDM_UNPREPARE         58
+#define MIDM_ADDBUFFER         59
+#define MIDM_START             60
+#define MIDM_STOP              61
+#define MIDM_RESET             62
+
+
+#define AUXM_INIT              DRVM_INIT
+#define AUXDM_GETNUMDEVS       3
+#define AUXDM_GETDEVCAPS       4
+#define AUXDM_GETVOLUME        5
+#define AUXDM_SETVOLUME        6
+
+#define MXDM_INIT              DRVM_INIT
+#define MXDM_USER               DRVM_USER
+#define MXDM_MAPPER             DRVM_MAPPER
+
+#define        MXDM_GETNUMDEVS         1
+#define        MXDM_GETDEVCAPS         2
+#define        MXDM_OPEN               3
+#define        MXDM_CLOSE              4
+#define        MXDM_GETLINEINFO        5
+#define        MXDM_GETLINECONTROLS    6
+#define        MXDM_GETCONTROLDETAILS  7
+#define        MXDM_SETCONTROLDETAILS  8
+
+/* pre-defined joystick types */
+#define JOY_HW_NONE                    0
+#define JOY_HW_CUSTOM                  1
+#define JOY_HW_2A_2B_GENERIC           2
+#define JOY_HW_2A_4B_GENERIC           3
+#define JOY_HW_2B_GAMEPAD              4
+#define JOY_HW_2B_FLIGHTYOKE           5
+#define JOY_HW_2B_FLIGHTYOKETHROTTLE   6
+#define JOY_HW_3A_2B_GENERIC           7
+#define JOY_HW_3A_4B_GENERIC           8
+#define JOY_HW_4B_GAMEPAD              9
+#define JOY_HW_4B_FLIGHTYOKE           10
+#define JOY_HW_4B_FLIGHTYOKETHROTTLE   11
+#define JOY_HW_LASTENTRY               12
+
+/* calibration flags */
+#define        JOY_ISCAL_XY            0x00000001l     /* XY are calibrated */
+#define        JOY_ISCAL_Z             0x00000002l     /* Z is calibrated */
+#define        JOY_ISCAL_R             0x00000004l     /* R is calibrated */
+#define        JOY_ISCAL_U             0x00000008l     /* U is calibrated */
+#define        JOY_ISCAL_V             0x00000010l     /* V is calibrated */
+#define        JOY_ISCAL_POV           0x00000020l     /* POV is calibrated */
+
+/* point of view constants */
+#define JOY_POV_NUMDIRS          4
+#define JOY_POVVAL_FORWARD       0
+#define JOY_POVVAL_BACKWARD      1
+#define JOY_POVVAL_LEFT          2
+#define JOY_POVVAL_RIGHT         3
+
+/* Specific settings for joystick hardware */
+#define JOY_HWS_HASZ           0x00000001l     /* has Z info? */
+#define JOY_HWS_HASPOV         0x00000002l     /* point of view hat present */
+#define JOY_HWS_POVISBUTTONCOMBOS 0x00000004l  /* pov done through combo of buttons */
+#define JOY_HWS_POVISPOLL      0x00000008l     /* pov done through polling */
+#define JOY_HWS_ISYOKE         0x00000010l     /* joystick is a flight yoke */
+#define JOY_HWS_ISGAMEPAD      0x00000020l     /* joystick is a game pad */
+#define JOY_HWS_ISCARCTRL      0x00000040l     /* joystick is a car controller */
+/* X defaults to J1 X axis */
+#define JOY_HWS_XISJ1Y         0x00000080l     /* X is on J1 Y axis */
+#define JOY_HWS_XISJ2X         0x00000100l     /* X is on J2 X axis */
+#define JOY_HWS_XISJ2Y         0x00000200l     /* X is on J2 Y axis */
+/* Y defaults to J1 Y axis */
+#define JOY_HWS_YISJ1X         0x00000400l     /* Y is on J1 X axis */
+#define JOY_HWS_YISJ2X         0x00000800l     /* Y is on J2 X axis */
+#define JOY_HWS_YISJ2Y         0x00001000l     /* Y is on J2 Y axis */
+/* Z defaults to J2 Y axis */
+#define JOY_HWS_ZISJ1X         0x00002000l     /* Z is on J1 X axis */
+#define JOY_HWS_ZISJ1Y         0x00004000l     /* Z is on J1 Y axis */
+#define JOY_HWS_ZISJ2X         0x00008000l     /* Z is on J2 X axis */
+/* POV defaults to J2 Y axis, if it is not button based */
+#define JOY_HWS_POVISJ1X       0x00010000l     /* pov done through J1 X axis */
+#define JOY_HWS_POVISJ1Y       0x00020000l     /* pov done through J1 Y axis */
+#define JOY_HWS_POVISJ2X       0x00040000l     /* pov done through J2 X axis */
+/* R defaults to J2 X axis */
+#define JOY_HWS_HASR           0x00080000l     /* has R (4th axis) info */
+#define JOY_HWS_RISJ1X         0x00100000l     /* R done through J1 X axis */
+#define JOY_HWS_RISJ1Y         0x00200000l     /* R done through J1 Y axis */
+#define JOY_HWS_RISJ2Y         0x00400000l     /* R done through J2 X axis */
+/* U & V for future hardware */
+#define JOY_HWS_HASU           0x00800000l     /* has U (5th axis) info */
+#define JOY_HWS_HASV           0x01000000l     /* has V (6th axis) info */
+
+/* Usage settings */
+#define JOY_US_HASRUDDER       0x00000001l     /* joystick configured with rudder */
+#define JOY_US_PRESENT         0x00000002l     /* is joystick actually present? */
+#define JOY_US_ISOEM           0x00000004l     /* joystick is an OEM defined type */
+
+
+/* struct for storing x,y, z, and rudder values */
+typedef struct joypos_tag {
+    DWORD      dwX;
+    DWORD      dwY;
+    DWORD      dwZ;
+    DWORD      dwR;
+    DWORD      dwU;
+    DWORD      dwV;
+} JOYPOS, *LPJOYPOS;
+
+/* struct for storing ranges */
+typedef struct joyrange_tag {
+    JOYPOS     jpMin;
+    JOYPOS     jpMax;
+    JOYPOS     jpCenter;
+} JOYRANGE,*LPJOYRANGE;
+
+typedef struct joyreguservalues_tag {
+    DWORD      dwTimeOut;      /* value at which to timeout joystick polling */
+    JOYRANGE   jrvRanges;      /* range of values app wants returned for axes */
+    JOYPOS     jpDeadZone;     /* area around center to be considered
+                                  as "dead". specified as a percentage
+                                  (0-100). Only X & Y handled by system driver */
+} JOYREGUSERVALUES, *LPJOYREGUSERVALUES;
+
+typedef struct joyreghwsettings_tag {
+    DWORD      dwFlags;
+    DWORD      dwNumButtons;           /* number of buttons */
+} JOYREGHWSETTINGS, *LPJOYHWSETTINGS;
+
+/* range of values returned by the hardware (filled in by calibration) */
+typedef struct joyreghwvalues_tag {
+    JOYRANGE   jrvHardware;            /* values returned by hardware */
+    DWORD      dwPOVValues[JOY_POV_NUMDIRS];/* POV values returned by hardware */
+    DWORD      dwCalFlags;             /* what has been calibrated */
+} JOYREGHWVALUES, *LPJOYREGHWVALUES;
+
+/* hardware configuration */
+typedef struct joyreghwconfig_tag {
+    JOYREGHWSETTINGS   hws;            /* hardware settings */
+    DWORD              dwUsageSettings;/* usage settings */
+    JOYREGHWVALUES     hwv;            /* values returned by hardware */
+    DWORD              dwType;         /* type of joystick */
+    DWORD              dwReserved;     /* reserved for OEM drivers */
+} JOYREGHWCONFIG, *LPJOYREGHWCONFIG;
+
+/* joystick calibration info structure */
+typedef struct joycalibrate_tag {
+    UINT    wXbase;
+    UINT    wXdelta;
+    UINT    wYbase;
+    UINT    wYdelta;
+    UINT    wZbase;
+    UINT    wZdelta;
+} JOYCALIBRATE;
+typedef JOYCALIBRATE *LPJOYCALIBRATE;
+
+/* prototype for joystick message function */
+typedef UINT (CALLBACK * JOYDEVMSGPROC)(DWORD dwID, UINT uMessage, LPARAM lParam1, LPARAM lParam2);
+typedef JOYDEVMSGPROC *LPJOYDEVMSGPROC;
+
+/* messages sent to joystick driver's DriverProc() function */
+#define JDD_GETNUMDEVS          (DRV_RESERVED + 0x0001)
+#define JDD_GETDEVCAPS          (DRV_RESERVED + 0x0002)
+#define JDD_GETPOS              (DRV_RESERVED + 0x0101)
+#define JDD_SETCALIBRATION      (DRV_RESERVED + 0x0102)
+#define JDD_CONFIGCHANGED       (DRV_RESERVED + 0x0103)
+#define JDD_GETPOSEX            (DRV_RESERVED + 0x0104)
+
+#define MCI_MAX_DEVICE_TYPE_LENGTH 80
+
+#define MCI_FALSE                       (MCI_STRING_OFFSET + 19)
+#define MCI_TRUE                        (MCI_STRING_OFFSET + 20)
+
+#define MCI_FORMAT_RETURN_BASE          MCI_FORMAT_MILLISECONDS_S
+#define MCI_FORMAT_MILLISECONDS_S       (MCI_STRING_OFFSET + 21)
+#define MCI_FORMAT_HMS_S                (MCI_STRING_OFFSET + 22)
+#define MCI_FORMAT_MSF_S                (MCI_STRING_OFFSET + 23)
+#define MCI_FORMAT_FRAMES_S             (MCI_STRING_OFFSET + 24)
+#define MCI_FORMAT_SMPTE_24_S           (MCI_STRING_OFFSET + 25)
+#define MCI_FORMAT_SMPTE_25_S           (MCI_STRING_OFFSET + 26)
+#define MCI_FORMAT_SMPTE_30_S           (MCI_STRING_OFFSET + 27)
+#define MCI_FORMAT_SMPTE_30DROP_S       (MCI_STRING_OFFSET + 28)
+#define MCI_FORMAT_BYTES_S              (MCI_STRING_OFFSET + 29)
+#define MCI_FORMAT_SAMPLES_S            (MCI_STRING_OFFSET + 30)
+#define MCI_FORMAT_TMSF_S               (MCI_STRING_OFFSET + 31)
+
+#define MCI_VD_FORMAT_TRACK_S           (MCI_VD_OFFSET + 5)
+
+#define WAVE_FORMAT_PCM_S               (MCI_WAVE_OFFSET + 0)
+#define WAVE_MAPPER_S                   (MCI_WAVE_OFFSET + 1)
+
+#define MCI_SEQ_MAPPER_S                (MCI_SEQ_OFFSET + 5)
+#define MCI_SEQ_FILE_S                  (MCI_SEQ_OFFSET + 6)
+#define MCI_SEQ_MIDI_S                  (MCI_SEQ_OFFSET + 7)
+#define MCI_SEQ_SMPTE_S                 (MCI_SEQ_OFFSET + 8)
+#define MCI_SEQ_FORMAT_SONGPTR_S        (MCI_SEQ_OFFSET + 9)
+#define MCI_SEQ_NONE_S                  (MCI_SEQ_OFFSET + 10)
+#define MIDIMAPPER_S                    (MCI_SEQ_OFFSET + 11)
+
+#define MCI_RESOURCE_RETURNED       0x00010000  /* resource ID */
+#define MCI_COLONIZED3_RETURN       0x00020000  /* colonized ID, 3 bytes data */
+#define MCI_COLONIZED4_RETURN       0x00040000  /* colonized ID, 4 bytes data */
+#define MCI_INTEGER_RETURNED        0x00080000  /* integer conversion needed */
+#define MCI_RESOURCE_DRIVER         0x00100000  /* driver owns returned resource */
+
+#define MCI_NO_COMMAND_TABLE    0xFFFF
+
+#define MCI_COMMAND_HEAD        0
+#define MCI_STRING              1
+#define MCI_INTEGER             2
+#define MCI_END_COMMAND         3
+#define MCI_RETURN              4
+#define MCI_FLAG                5
+#define MCI_END_COMMAND_LIST    6
+#define MCI_RECT                7
+#define MCI_CONSTANT            8
+#define MCI_END_CONSTANT        9
+
+#define MAKEMCIRESOURCE(wRet, wRes) MAKELRESULT((wRet), (wRes))
+
+typedef struct {
+       DWORD                   dwCallback;
+       DWORD                   dwInstance;
+       HMIDIOUT                hMidi;
+       DWORD                   dwFlags;
+} PORTALLOC, *LPPORTALLOC;
+
+typedef struct {
+       HWAVE                   hWave;
+       LPWAVEFORMATEX          lpFormat;
+       DWORD                   dwCallback;
+       DWORD                   dwInstance;
+       UINT                    uMappedDeviceID;
+        DWORD                  dnDevNode;
+} WAVEOPENDESC, *LPWAVEOPENDESC;
+
+typedef struct {
+        DWORD                          dwStreamID;
+        WORD                           wDeviceID;
+} MIDIOPENSTRMID;
+
+typedef struct {
+       HMIDI                   hMidi;
+       DWORD                   dwCallback;
+       DWORD                   dwInstance;
+        DWORD                          dnDevNode;
+        DWORD                          cIds;
+        MIDIOPENSTRMID                 rgIds;
+} MIDIOPENDESC, *LPMIDIOPENDESC;
+
+typedef struct tMIXEROPENDESC
+{
+       HMIXEROBJ               hmx;
+        LPVOID                 pReserved0;
+       DWORD                   dwCallback;
+       DWORD                   dwInstance;
+} MIXEROPENDESC, *LPMIXEROPENDESC;
+
+typedef struct {
+       UINT                    wDeviceID;              /* device ID */
+       LPSTR                   lpstrParams;            /* parameter string for entry in SYSTEM.INI */
+       UINT                    wCustomCommandTable;    /* custom command table (0xFFFF if none) * filled in by the driver */
+       UINT                    wType;                  /* driver type (filled in by the driver) */
+} MCI_OPEN_DRIVER_PARMSA, *LPMCI_OPEN_DRIVER_PARMSA;
+
+typedef struct {
+       UINT                    wDeviceID;              /* device ID */
+       LPWSTR                  lpstrParams;            /* parameter string for entry in SYSTEM.INI */
+       UINT                    wCustomCommandTable;    /* custom command table (0xFFFF if none) * filled in by the driver */
+       UINT                    wType;                  /* driver type (filled in by the driver) */
+} MCI_OPEN_DRIVER_PARMSW, *LPMCI_OPEN_DRIVER_PARMSW;
+//DECL_WINELIB_TYPE_AW(MCI_OPEN_DRIVER_PARMS)
+//DECL_WINELIB_TYPE_AW(LPMCI_OPEN_DRIVER_PARMS)
+
+DWORD                  WINAPI  mciGetDriverData(UINT uDeviceID);
+BOOL                   WINAPI  mciSetDriverData(UINT uDeviceID, DWORD dwData);
+UINT                   WINAPI  mciDriverYield(UINT uDeviceID);
+BOOL                   WINAPI  mciDriverNotify(HWND hwndCallback, UINT uDeviceID,
+                                               UINT uStatus);
+UINT                   WINAPI  mciLoadCommandResource(HINSTANCE hInstance,
+                                              LPCWSTR lpResName, UINT uType);
+BOOL                   WINAPI  mciFreeCommandResource(UINT uTable);
+
+#define DCB_NULL               0x0000
+#define DCB_WINDOW             0x0001                  /* dwCallback is a HWND */
+#define DCB_TASK               0x0002                  /* dwCallback is a HTASK */
+#define DCB_FUNCTION           0x0003                  /* dwCallback is a FARPROC */
+#define DCB_EVENT              0x0005                  /* dwCallback is an EVENT Handler */
+#define DCB_TYPEMASK           0x0007
+#define DCB_NOSWITCH           0x0008                  /* don't switch stacks for callback */
+
+BOOL                   WINAPI  DriverCallback(DWORD dwCallBack, UINT uFlags, HDRVR hDev,
+                                              UINT wMsg, DWORD dwUser, DWORD dwParam1, DWORD dwParam2);
+
+#ifdef __WINESRC__
+#define  WAVE_DIRECTSOUND               0x0080
+#endif
+
+#include <poppack.h>
+
+#endif /* __MMDDK_H */
diff --git a/reactos/dll/mmdrv/mmdef.h b/reactos/dll/mmdrv/mmdef.h
new file mode 100644 (file)
index 0000000..acc842e
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ *
+ * COPYRIGHT:            See COPYING in the top level directory
+ * PROJECT:              ReactOS kernel
+ * FILE:                 lib/mmdrv/mmdef.h
+ * PURPOSE:              Multimedia Definitions (for mmdrv.dll)
+ * PROGRAMMER:           Andrew Greenwood
+ *
+ */
+
+#ifndef __INCLUDES_MMDEF_H__
+#define __INCLUDES_MMDEF_H__
+
+//#define UNICODE
+
+#define EXPORT __declspec(dllexport)
+
+
+//#include <stdio.h>
+//#include <windows.h>
+//#include <mmsystem.h>
+//#include <mmddk.h>
+
+// This needs to be done to get winioctl.h to work:
+//typedef unsigned __int64 DWORD64, *PDWORD64;
+
+//#include <winioctl.h>
+//#include "mmddk.h"
+
+
+#define SOUND_MAX_DEVICE_NAME 1024   // GUESSWORK
+#define SOUND_MAX_DEVICES 256       // GUESSWORK
+
+
+// If the root is \Device and the Device type is
+// WaveIn and the device number is 2, the full name is \Device\WaveIn2
+
+#define WAVE_IN_DEVICE_NAME     "\\Device\\WaveIn"
+#define WAVE_IN_DEVICE_NAME_U  L"\\Device\\WaveIn"
+#define WAVE_OUT_DEVICE_NAME    "\\Device\\WaveOut"
+#define WAVE_OUT_DEVICE_NAME_U L"\\Device\\WaveOut"
+
+#define MIDI_IN_DEVICE_NAME     "\\Device\\MidiIn"
+#define MIDI_IN_DEVICE_NAME_U  L"\\Device\\MidiIn"
+#define MIDI_OUT_DEVICE_NAME    "\\Device\\MidiOut"
+#define MIDI_OUT_DEVICE_NAME_U L"\\Device\\MidiOut"
+
+#define AUX_DEVICE_NAME     "\\Device\\MMAux"
+#define AUX_DEVICE_NAME_U  L"\\Device\\MMAux"
+
+
+#define IOCTL_SOUND_BASE    FILE_DEVICE_SOUND
+#define IOCTL_WAVE_BASE     0x0000
+#define IOCTL_MIDI_BASE     0x0080
+#define IOCTL_AUX_BASE      0x0100
+
+// Wave device driver IOCTLs
+
+#define IOCTL_WAVE_QUERY_FORMAT         CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0001, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define IOCTL_WAVE_SET_FORMAT           CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0002, METHOD_BUFFERED, FILE_WRITE_ACCESS)
+#define IOCTL_WAVE_GET_CAPABILITIES     CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0003, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define IOCTL_WAVE_SET_STATE            CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0004, METHOD_BUFFERED, FILE_WRITE_ACCESS)
+#define IOCTL_WAVE_GET_STATE            CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0005, METHOD_BUFFERED, FILE_WRITE_ACCESS)
+#define IOCTL_WAVE_GET_POSITION         CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0006, METHOD_BUFFERED, FILE_WRITE_ACCESS)
+#define IOCTL_WAVE_SET_VOLUME           CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0007, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define IOCTL_WAVE_GET_VOLUME           CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0008, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define IOCTL_WAVE_SET_PITCH            CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0009, METHOD_BUFFERED, FILE_WRITE_ACCESS)
+#define IOCTL_WAVE_GET_PITCH            CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x000A, METHOD_BUFFERED, FILE_WRITE_ACCESS)
+#define IOCTL_WAVE_SET_PLAYBACK_RATE    CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x000B, METHOD_BUFFERED, FILE_WRITE_ACCESS)
+#define IOCTL_WAVE_GET_PLAYBACK_RATE    CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x000C, METHOD_BUFFERED, FILE_WRITE_ACCESS)
+#define IOCTL_WAVE_PLAY                 CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x000D, METHOD_IN_DIRECT, FILE_WRITE_ACCESS)
+#define IOCTL_WAVE_RECORD               CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x000E, METHOD_OUT_DIRECT, FILE_WRITE_ACCESS)
+#define IOCTL_WAVE_BREAK_LOOP           CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x000F, METHOD_BUFFERED, FILE_WRITE_ACCESS)
+#define IOCTL_WAVE_SET_LOW_PRIORITY     CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0010, METHOD_BUFFERED, FILE_WRITE_ACCESS)
+
+// MIDI device driver IOCTLs
+
+#define IOCTL_MIDI_GET_CAPABILITIES   CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0001, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define IOCTL_MIDI_SET_STATE          CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0002, METHOD_BUFFERED, FILE_WRITE_ACCESS)
+#define IOCTL_MIDI_GET_STATE          CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0003, METHOD_BUFFERED, FILE_WRITE_ACCESS)
+#define IOCTL_MIDI_SET_VOLUME         CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0004, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define IOCTL_MIDI_GET_VOLUME         CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0005, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define IOCTL_MIDI_PLAY               CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0006, METHOD_BUFFERED, FILE_WRITE_ACCESS)
+#define IOCTL_MIDI_RECORD             CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0007, METHOD_BUFFERED, FILE_WRITE_ACCESS)
+#define IOCTL_MIDI_CACHE_PATCHES      CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0008, METHOD_BUFFERED, FILE_WRITE_ACCESS)
+#define IOCTL_MIDI_CACHE_DRUM_PATCHES CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0009, METHOD_BUFFERED, FILE_WRITE_ACCESS)
+
+// AUX device driver IOCTLs
+#define IOCTL_AUX_GET_CAPABILITIES      CTL_CODE(IOCTL_SOUND_BASE, IOCTL_AUX_BASE + 0x0001, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define IOCTL_AUX_SET_VOLUME            CTL_CODE(IOCTL_SOUND_BASE, IOCTL_AUX_BASE + 0x0002, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define IOCTL_AUX_GET_VOLUME            CTL_CODE(IOCTL_SOUND_BASE, IOCTL_AUX_BASE + 0x0003, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define IOCTL_SOUND_GET_CHANGED_VOLUME  CTL_CODE(IOCTL_SOUND_BASE, IOCTL_AUX_BASE + 0x0004, METHOD_BUFFERED, FILE_READ_ACCESS)
+
+#endif
diff --git a/reactos/dll/mmdrv/mmdrv.def b/reactos/dll/mmdrv/mmdrv.def
new file mode 100644 (file)
index 0000000..28e0588
--- /dev/null
@@ -0,0 +1,14 @@
+; $Id$
+;
+; mmdrv.def
+;
+; ReactOS Operating System
+;
+LIBRARY mmdrv.dll
+EXPORTS
+DriverProc@20
+widMessage@20
+wodMessage@20
+midMessage@20
+modMessage@20
+auxMessage@20
diff --git a/reactos/dll/mmdrv/mmdrv.h b/reactos/dll/mmdrv/mmdrv.h
new file mode 100644 (file)
index 0000000..f9211d3
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ *
+ * COPYRIGHT:            See COPYING in the top level directory
+ * PROJECT:              ReactOS Multimedia
+ * FILE:                 lib/mmdrv/mmdrv.h
+ * PURPOSE:              Multimedia User Mode Driver (header)
+ * PROGRAMMER:           Andrew Greenwood
+ *                       Aleksey Bragin
+ * UPDATE HISTORY:
+ *                       Jan 30, 2004: Imported into ReactOS tree
+ */
+
+#ifndef __INCLUDES_MMDRV_H__
+#define __INCLUDES_MMDRV_H__
+
+//#define UNICODE
+
+#define EXPORT __declspec(dllexport)
+
+
+#include <stdio.h>
+#include <windows.h>
+#include <mmsystem.h>
+#include <mmddk.h>
+
+// This needs to be done to get winioctl.h to work:
+//typedef unsigned __int64 DWORD64, *PDWORD64;
+
+#include <winioctl.h>
+//#include "mmddk.h"
+
+#include "mmdef.h"
+
+ULONG DbgPrint(PCH Format, ...);
+
+/*
+#define SOUND_MAX_DEVICE_NAME 1024   // GUESSWORK
+#define SOUND_MAX_DEVICES 256       // GUESSWORK
+*/
+
+// If the root is \Device and the Device type is
+// WaveIn and the device number is 2, the full name is \Device\WaveIn2
+
+#define WAVE_IN_DEVICE_NAME     "\\Device\\WaveIn"
+#define WAVE_IN_DEVICE_NAME_U  L"\\Device\\WaveIn"
+#define WAVE_OUT_DEVICE_NAME    "\\Device\\WaveOut"
+#define WAVE_OUT_DEVICE_NAME_U L"\\Device\\WaveOut"
+
+#define MIDI_IN_DEVICE_NAME     "\\Device\\MidiIn"
+#define MIDI_IN_DEVICE_NAME_U  L"\\Device\\MidiIn"
+#define MIDI_OUT_DEVICE_NAME    "\\Device\\MidiOut"
+#define MIDI_OUT_DEVICE_NAME_U L"\\Device\\MidiOut"
+
+#define AUX_DEVICE_NAME     "\\Device\\MMAux"
+#define AUX_DEVICE_NAME_U  L"\\Device\\MMAux"
+
+/*
+#define IOCTL_SOUND_BASE    FILE_DEVICE_SOUND
+#define IOCTL_WAVE_BASE     0x0000
+#define IOCTL_MIDI_BASE     0x0080
+
+// Wave device driver IOCTLs
+
+#define IOCTL_WAVE_QUERY_FORMAT         CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0001, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define IOCTL_WAVE_SET_FORMAT           CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0002, METHOD_BUFFERED, FILE_WRITE_ACCESS)
+#define IOCTL_WAVE_GET_CAPABILITIES     CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0003, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define IOCTL_WAVE_SET_STATE            CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0004, METHOD_BUFFERED, FILE_WRITE_ACCESS)
+#define IOCTL_WAVE_GET_STATE            CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0005, METHOD_BUFFERED, FILE_WRITE_ACCESS)
+#define IOCTL_WAVE_GET_POSITION         CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0006, METHOD_BUFFERED, FILE_WRITE_ACCESS)
+#define IOCTL_WAVE_SET_VOLUME           CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0007, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define IOCTL_WAVE_GET_VOLUME           CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0008, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define IOCTL_WAVE_SET_PITCH            CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0009, METHOD_BUFFERED, FILE_WRITE_ACCESS)
+#define IOCTL_WAVE_GET_PITCH            CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x000A, METHOD_BUFFERED, FILE_WRITE_ACCESS)
+#define IOCTL_WAVE_SET_PLAYBACK_RATE    CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x000B, METHOD_BUFFERED, FILE_WRITE_ACCESS)
+#define IOCTL_WAVE_GET_PLAYBACK_RATE    CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x000C, METHOD_BUFFERED, FILE_WRITE_ACCESS)
+#define IOCTL_WAVE_PLAY                 CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x000D, METHOD_IN_DIRECT, FILE_WRITE_ACCESS)
+#define IOCTL_WAVE_RECORD               CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x000E, METHOD_OUT_DIRECT, FILE_WRITE_ACCESS)
+#define IOCTL_WAVE_BREAK_LOOP           CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x000F, METHOD_BUFFERED, FILE_WRITE_ACCESS)
+#define IOCTL_WAVE_SET_LOW_PRIORITY     CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0010, METHOD_BUFFERED, FILE_WRITE_ACCESS)
+
+// MIDI device driver IOCTLs
+
+#define IOCTL_MIDI_GET_CAPABILITIES   CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0001, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define IOCTL_MIDI_SET_STATE          CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0002, METHOD_BUFFERED, FILE_WRITE_ACCESS)
+#define IOCTL_MIDI_GET_STATE          CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0003, METHOD_BUFFERED, FILE_WRITE_ACCESS)
+#define IOCTL_MIDI_SET_VOLUME         CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0004, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define IOCTL_MIDI_GET_VOLUME         CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0005, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define IOCTL_MIDI_PLAY               CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0006, METHOD_BUFFERED, FILE_WRITE_ACCESS)
+#define IOCTL_MIDI_RECORD             CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0007, METHOD_BUFFERED, FILE_WRITE_ACCESS)
+#define IOCTL_MIDI_CACHE_PATCHES      CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0008, METHOD_BUFFERED, FILE_WRITE_ACCESS)
+#define IOCTL_MIDI_CACHE_DRUM_PATCHES CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0009, METHOD_BUFFERED, FILE_WRITE_ACCESS)
+*/
+
+
+CRITICAL_SECTION CS;  // Serialize access to device lists
+
+HANDLE Heap;
+
+ enum {
+      InvalidDevice,
+      WaveInDevice,
+      WaveOutDevice,
+      MidiInDevice,
+      MidiOutDevice,
+      AuxDevice
+};
+
+MMRESULT OpenDevice(UINT DeviceType, DWORD ID, PHANDLE pDeviceHandle,
+                    DWORD Access);
+
+MMRESULT FindDevices();
+
+DWORD GetDeviceCount(UINT DeviceType);
+
+DWORD TranslateStatus(void);
+
+
+#endif
diff --git a/reactos/dll/mmdrv/mmdrv.xml b/reactos/dll/mmdrv/mmdrv.xml
new file mode 100644 (file)
index 0000000..f41b264
--- /dev/null
@@ -0,0 +1,15 @@
+<module name="mmdrv" type="win32dll" baseaddress="${BASEADDRESS_MMDRV}" installbase="system32" installname="mmdrv.dll">
+       <importlibrary definition="mmdrv.def" />
+       <include base="mmdrv">.</include>
+       <define name="__USE_W32API" />
+       <define name="UNICODE" />
+       <define name="_UNICODE" />
+       <library>ntdll</library>
+       <library>kernel32</library>
+       <library>winmm</library>
+       <file>auxil.c</file>
+       <file>entry.c</file>
+       <file>midi.c</file>
+       <file>utils.c</file>
+       <file>wave.c</file>
+</module>
diff --git a/reactos/dll/mmdrv/utils.c b/reactos/dll/mmdrv/utils.c
new file mode 100644 (file)
index 0000000..fbe8106
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ *
+ * COPYRIGHT:            See COPYING in the top level directory
+ * PROJECT:              ReactOS Multimedia
+ * FILE:                 lib/mmdrv/utils.c
+ * PURPOSE:              Multimedia User Mode Driver (utility functions)
+ * PROGRAMMER:           Andrew Greenwood
+ * UPDATE HISTORY:
+ *                       Jan 30, 2004: Imported into ReactOS tree
+ */
+
+#include "mmdrv.h"
+
+#define NDEBUG
+#include <debug.h>
+
+typedef struct _DEVICE_LIST
+{
+    struct _DEVICE_LIST *Next;
+    DWORD   DeviceType;
+    ULONG   CardIndex;
+    PVOID   DeviceInstanceData;
+    ULONG   DeviceInstanceDataSize;
+    WCHAR   Name[1];
+} DEVICE_LIST, *PDEVICE_LIST;
+
+PDEVICE_LIST DeviceList;
+
+
+DWORD TranslateStatus(void)
+{
+    switch(GetLastError())
+    {
+        case NO_ERROR :
+        case ERROR_IO_PENDING :
+            return MMSYSERR_NOERROR;
+            
+        case ERROR_BUSY :
+            return MMSYSERR_ALLOCATED;
+            
+        case ERROR_NOT_SUPPORTED :
+        case ERROR_INVALID_FUNCTION :
+            return MMSYSERR_NOTSUPPORTED;
+            
+        case ERROR_NOT_ENOUGH_MEMORY :
+            return MMSYSERR_NOMEM;
+            
+        case ERROR_ACCESS_DENIED :
+            return MMSYSERR_BADDEVICEID;
+            
+        case ERROR_INSUFFICIENT_BUFFER :
+            return MMSYSERR_INVALPARAM;
+            
+        default :
+            return MMSYSERR_ERROR;
+    };
+}
+
+
+
+MMRESULT OpenDevice(UINT DeviceType, DWORD ID, PHANDLE pDeviceHandle,
+                    DWORD Access)
+{
+    DPRINT("OpenDevice()\n");
+    WCHAR DeviceName[SOUND_MAX_DEVICE_NAME];
+    *pDeviceHandle = INVALID_HANDLE_VALUE;
+
+    if (ID > SOUND_MAX_DEVICES)
+        return MMSYSERR_BADDEVICEID;
+
+    switch(DeviceType)
+    {
+        case WaveOutDevice :
+            wsprintf(DeviceName, L"\\\\.%ls%d", WAVE_OUT_DEVICE_NAME_U + strlen("\\Device"), ID);
+            break;
+        case WaveInDevice :
+            wsprintf(DeviceName, L"\\\\.%ls%d", WAVE_IN_DEVICE_NAME_U + strlen("\\Device"), ID);
+            break;
+        case MidiOutDevice :
+            wsprintf(DeviceName, L"\\\\.%ls%d", MIDI_OUT_DEVICE_NAME_U + strlen("\\Device"), ID);
+            break;
+        case MidiInDevice :
+            wsprintf(DeviceName, L"\\\\.%ls%d", MIDI_IN_DEVICE_NAME_U + strlen("\\Device"), ID);
+            break;
+               case AuxDevice :
+                        wsprintf(DeviceName, L"\\\\.%ls%d", AUX_DEVICE_NAME_U + strlen("\\Device"), ID);
+                        break;
+        default : 
+            DPRINT("No Auido Device Found");
+            return MMSYSERR_BADDEVICEID; /* Maybe we should change error code */
+    };
+
+    DPRINT("Attempting to open %S\n", DeviceName);
+
+    *pDeviceHandle = CreateFile(DeviceName, Access, FILE_SHARE_WRITE, NULL,
+                                OPEN_EXISTING, Access != GENERIC_READ ? FILE_FLAG_OVERLAPPED : 0,
+                                NULL);
+
+    DPRINT("DeviceHandle == 0x%x\n", (int)*pDeviceHandle);
+
+    if (pDeviceHandle == INVALID_HANDLE_VALUE)
+        return TranslateStatus();
+
+    return MMSYSERR_NOERROR;
+}
+
+
+// DEVICE LIST MANAGEMENT
+
+
+BOOL AddDeviceToList(PDEVICE_LIST* pList, DWORD DeviceType, DWORD CardIndex,
+                     LPWSTR Name)
+{
+    PDEVICE_LIST pNewDevice;
+
+    DPRINT("AddDeviceToList()\n");
+
+    pNewDevice = (PDEVICE_LIST) HeapAlloc(Heap, 0,
+        sizeof(DEVICE_LIST) + lstrlen(Name) * sizeof(WCHAR));
+
+    if ( !pNewDevice )
+    {
+        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+        return FALSE;
+    }
+    
+    pNewDevice->DeviceType = DeviceType;
+    pNewDevice->CardIndex = CardIndex;
+    lstrcpy(pNewDevice->Name, Name);
+    pNewDevice->DeviceInstanceData = NULL;
+    pNewDevice->Next = *pList;
+    *pList = pNewDevice;
+    
+    DPRINT("Success!\n");
+
+    return TRUE;
+}
+
+
+VOID FreeDeviceList()
+{
+    PDEVICE_LIST pDevice;
+
+    DPRINT("FreeDeviceList()\n");
+    
+    while (DeviceList)
+    {
+        pDevice = DeviceList;
+        DeviceList = pDevice->Next;
+
+        if (pDevice->DeviceInstanceData)
+            HeapFree(Heap, 0, (LPVOID)pDevice->DeviceInstanceData);
+
+        HeapFree(Heap, 0, (LPSTR)pDevice);
+    }
+}
+
+
+MMRESULT FindDevices()
+{
+//    DWORD Index;
+//    HKEY DriverKey;
+
+    DPRINT("Finding devices\n");
+    
+//    DriverKey = OpenParametersKey();
+//  see drvutil.c of MS DDK for how this SHOULD be done...
+
+  
+    SHORT i;
+    HANDLE h;
+    WCHAR DeviceName[SOUND_MAX_DEVICE_NAME]; 
+
+    for (i=0; OpenDevice(WaveOutDevice, i, &h, GENERIC_READ) == MMSYSERR_NOERROR; i++) 
+    { 
+        wsprintf(DeviceName, L"WaveOut%d\0", i);
+        CloseHandle(h);
+        AddDeviceToList(&DeviceList, WaveOutDevice, 0, DeviceName);
+    }
+
+    for (i=0; OpenDevice(WaveInDevice, i, &h, GENERIC_READ) == MMSYSERR_NOERROR; i++) 
+    { 
+        wsprintf(DeviceName, L"WaveIn%d\0", i);
+        CloseHandle(h);
+        AddDeviceToList(&DeviceList, WaveInDevice, 0, DeviceName);
+    }
+
+    for (i=0; OpenDevice(MidiOutDevice, i, &h, GENERIC_READ) == MMSYSERR_NOERROR; i++) 
+    { 
+        wsprintf(DeviceName, L"MidiOut%d\0", i);
+        CloseHandle(h);
+        AddDeviceToList(&DeviceList, MidiOutDevice, 0, DeviceName);
+    }
+
+    for (i=0; OpenDevice(MidiInDevice, i, &h, GENERIC_READ) == MMSYSERR_NOERROR; i++) 
+    { 
+        wsprintf(DeviceName, L"MidiIn%d\0", i);
+        CloseHandle(h);
+        AddDeviceToList(&DeviceList, MidiInDevice, 0, DeviceName);
+    }
+
+    for (i=0; OpenDevice(AuxDevice, i, &h, GENERIC_READ) == MMSYSERR_NOERROR; i++) 
+    { 
+        wsprintf(DeviceName, L"Aux%d\0", i);
+        CloseHandle(h);
+        AddDeviceToList(&DeviceList, AuxDevice, 0, DeviceName);
+    }
+
+
+    // MIDI Out 0: MPU-401 UART
+    // AddDeviceToList(&DeviceList, MidiOutDevice, 0, L"MidiOut0");
+    // Wave Out 0: Sound Blaster 16 (ok?)
+    // AddDeviceToList(&DeviceList, WaveOutDevice, 0, L"WaveOut0");
+
+    return MMSYSERR_NOERROR; // ok?
+}
+
+
+
+DWORD GetDeviceCount(UINT DeviceType)
+{
+    int i;
+    PDEVICE_LIST List;
+
+    for (List = DeviceList, i = 0; List != NULL; List = List->Next)
+        if (List->DeviceType == DeviceType)
+            i ++;
+
+    return i;
+}
diff --git a/reactos/dll/mmdrv/wave.c b/reactos/dll/mmdrv/wave.c
new file mode 100644 (file)
index 0000000..fd0292f
--- /dev/null
@@ -0,0 +1,1048 @@
+/*
+ *
+ * COPYRIGHT:            See COPYING in the top level directory
+ * PROJECT:              ReactOS Multimedia
+ * FILE:                 lib/mmdrv/wave.c
+ * PURPOSE:              Multimedia User Mode Driver
+ * PROGRAMMER:           Andrew Greenwood
+ *                       Aleksey Bragin (aleksey at studiocerebral.com)
+ * UPDATE HISTORY:
+ *                       Jan 30, 2004: Imported into ReactOS tree (Greenwood)
+ *                       Mar 16, 2004: Implemented some funcs (Bragin)
+ */
+
+#include "mmdrv.h"
+#include "wave.h"
+
+#define NDEBUG
+#include <debug.h>
+
+#define WHDR_COMPLETE 0x80000000
+#define MAX_BUFFER_SIZE           8192  
+#define MAX_WAVE_BYTES          5*MAX_BUFFER_SIZE  
+
+PWAVEALLOC WaveLists; 
+
+static MMRESULT waveReadWrite(PWAVEALLOC pClient);
+void wavePartialOvl(DWORD dwErrorCode, DWORD BytesTransferred, LPOVERLAPPED pOverlapped);
+void waveOvl(DWORD dwErrorCode, DWORD BytesTransferred, LPOVERLAPPED pOverlapped);
+void waveLoopOvl(DWORD dwErrorCode, DWORD BytesTransferred, LPOVERLAPPED pOverlapped);
+void waveBreakOvl(DWORD dwErrorCode, DWORD BytesTransferred, LPOVERLAPPED pOverlapped);
+static MMRESULT waveSetState(PWAVEALLOC pClient, ULONG State);
+
+/* ============================
+ *  INTERNAL
+ *  functions start here
+ * ============================
+ */
+
+MMRESULT GetDeviceCapabilities(DWORD ID, UINT DeviceType,
+                                      LPBYTE pCaps, DWORD Size)
+{
+    HANDLE DeviceHandle = NULL;
+    MMRESULT Result = MMSYSERR_NOERROR;
+    DWORD BytesReturned = 0;
+
+    // Open the wave device
+
+    Result = OpenDevice(DeviceType, ID, &DeviceHandle, GENERIC_READ);
+    if (Result != MMSYSERR_NOERROR)
+         return Result;
+
+       if ((DeviceType == WaveOutDevice) || (DeviceType == WaveInDevice))
+       {
+               Result = DeviceIoControl(DeviceHandle, IOCTL_WAVE_GET_CAPABILITIES,
+                            NULL, 0, (LPVOID)pCaps, Size,
+                  &BytesReturned, NULL) ? MMSYSERR_NOERROR : TranslateStatus();
+       }
+
+       else if ((DeviceType == MidiInDevice) || (DeviceType == MidiOutDevice))
+       {
+           Result = DeviceIoControl(DeviceHandle, IOCTL_MIDI_GET_CAPABILITIES,
+                            NULL, 0, (LPVOID)pCaps, Size,
+                  &BytesReturned, NULL) ? MMSYSERR_NOERROR : TranslateStatus();
+       }
+
+    else if (DeviceType == AuxDevice)
+       {
+           Result = DeviceIoControl(DeviceHandle, IOCTL_AUX_GET_CAPABILITIES,
+                            NULL, 0, (LPVOID)pCaps, Size,
+                  &BytesReturned, NULL) ? MMSYSERR_NOERROR : TranslateStatus();
+       }
+
+    // Close the handle and return the result code
+    CloseHandle(DeviceHandle);
+
+    return Result;
+}
+
+static DWORD waveThread(LPVOID lpParameter)
+{
+
+    PWAVEALLOC pClient = (PWAVEALLOC)lpParameter;
+    BOOL Terminate = FALSE;
+
+    SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
+    SetEvent(pClient->AuxEvent2);
+    WaitForSingleObject(pClient->AuxEvent1, INFINITE);
+
+    for (;;) 
+    {
+        switch (pClient->AuxFunction) 
+        {
+            case WaveThreadAddBuffer:
+                {
+                 LPWAVEHDR *pHdrSearching;
+
+                 if (pClient->DeviceType == WaveInDevice)             
+                     pClient->AuxParam.pHdr->dwBytesRecorded = 0;
+            
+                 pHdrSearching = &pClient->DeviceQueue;
+                 pClient->AuxParam.pHdr->lpNext = NULL;
+                 
+                 while (*pHdrSearching) 
+                 {
+                    pHdrSearching = &(*pHdrSearching)->lpNext;
+                 }
+                   
+                 if (pClient->NextBuffer == NULL) 
+                 {
+                     pClient->BufferPosition = 0;
+                     pClient->NextBuffer = pClient->AuxParam.pHdr;
+                        
+                 }
+
+                 *pHdrSearching = pClient->AuxParam.pHdr;
+
+                 pClient->AuxReturnCode = waveReadWrite(pClient);
+                }
+                 break;
+                 
+            case WaveThreadSetState:
+                 pClient->AuxReturnCode = waveSetState(pClient, pClient->AuxParam.State);
+
+                 if (pClient->AuxParam.State == WAVE_DD_RESET) 
+                 {
+                    PWAVEHDR pHdr;
+              
+                    pClient->LoopHead = NULL;
+                    pClient->AuxReturnCode = MMSYSERR_NOERROR;                               
+                    for (pHdr = pClient->DeviceQueue; pHdr != NULL; pHdr = pHdr->lpNext) 
+                    {
+                        pHdr->dwFlags |= WHDR_COMPLETE;
+                    }
+        
+                    pClient->BufferPosition = 0;
+                    pClient->NextBuffer = NULL;                    
+                } 
+                else 
+                {
+                    if (pClient->DeviceType == WaveInDevice && pClient->AuxReturnCode == MMSYSERR_NOERROR) 
+                    {
+                        if (pClient->AuxParam.State == WAVE_DD_STOP) 
+                        {                        
+                            if (pClient->DeviceQueue) 
+                            {
+                                while (!(pClient->DeviceQueue->dwFlags & WHDR_COMPLETE) &&
+                                        pClient->BytesOutstanding != 0) 
+                                {
+                                    waveSetState(pClient, WAVE_DD_RECORD);
+                                    pClient->AuxReturnCode = waveSetState(pClient, WAVE_DD_STOP);
+                                    if (pClient->AuxReturnCode != MMSYSERR_NOERROR)                                     
+                                        break;
+                                    
+                                }
+                                if (pClient->AuxReturnCode == MMSYSERR_NOERROR) 
+                                {
+                                    pClient->DeviceQueue->dwFlags |= WHDR_COMPLETE;
+                                    if (pClient->NextBuffer == pClient->DeviceQueue) 
+                                    {
+                                        pClient->NextBuffer = pClient->DeviceQueue->lpNext;
+                                        pClient->BufferPosition = 0;
+                                    }
+                                }
+                            }
+                      } 
+                      else 
+                      {                      
+                        if (pClient->AuxParam.State == WAVE_DD_RECORD) 
+                                pClient->AuxReturnCode = waveReadWrite(pClient);                        
+                      }
+                    }
+                 }
+
+                 break;
+
+            case WaveThreadGetData:                 
+                 {
+                    OVERLAPPED Overlap;
+                    DWORD BytesReturned;
+
+                    // FIXME
+                    // Assert(hDev != NULL);
+
+                    memset(&Overlap, 0, sizeof(Overlap));
+
+                    Overlap.hEvent = pClient->Event;
+
+                    if (!DeviceIoControl(pClient->hDev, pClient->AuxParam.GetSetData.Function, NULL, 0,
+                         pClient->AuxParam.GetSetData.pData, pClient->AuxParam.GetSetData.DataLen,
+                         &BytesReturned, &Overlap)) 
+                    {
+                        DWORD cbTransfer;
+
+                        if (GetLastError() != ERROR_IO_PENDING) 
+                            pClient->AuxReturnCode = TranslateStatus();
+                        else
+                        {
+
+                            if (!GetOverlappedResult(pClient->hDev, &Overlap, &cbTransfer, TRUE))                         
+                                pClient->AuxReturnCode = TranslateStatus();                        
+                        } 
+                    }
+                    else
+                    {
+                        while (SetEvent(pClient->Event) && WaitForSingleObjectEx(pClient->Event, 0, TRUE) == 
+                            WAIT_IO_COMPLETION) {}
+
+                        pClient->AuxReturnCode = MMSYSERR_NOERROR;
+                    }
+                 }
+                 break;
+
+            case WaveThreadSetData:
+                 {
+                    OVERLAPPED Overlap;
+                    DWORD BytesReturned;
+                    memset((PVOID)&Overlap, 0, sizeof(Overlap));
+                    Overlap.hEvent = pClient->Event;
+    
+                    if (!DeviceIoControl(pClient->hDev, pClient->AuxParam.GetSetData.Function, 
+                                      pClient->AuxParam.GetSetData.pData, pClient->AuxParam.GetSetData.DataLen, 
+                                      NULL, 0, &BytesReturned, &Overlap)) 
+                    {
+                        DWORD cbTransfer;
+                        if (GetLastError() == ERROR_IO_PENDING) 
+                        {
+                            if (!GetOverlappedResult(pClient->hDev, &Overlap, &cbTransfer, TRUE))             
+                                pClient->AuxReturnCode = TranslateStatus();             
+                        } 
+                        else              
+                            pClient->AuxReturnCode = TranslateStatus();
+         
+                    }
+                    else
+                    { 
+                        while (SleepEx(0, TRUE) == WAIT_IO_COMPLETION) {}
+
+                        pClient->AuxReturnCode = MMSYSERR_NOERROR;
+                    }
+                 }                
+                 break;
+
+            case WaveThreadBreakLoop:
+                 pClient->AuxReturnCode = MMSYSERR_NOERROR;
+                 if (pClient->LoopHead)                 
+                    pClient->LoopCount = 0;                          
+                 break;
+
+            case WaveThreadClose:
+                 if (pClient->DeviceQueue != NULL)                  
+                    pClient->AuxReturnCode = WAVERR_STILLPLAYING;                 
+                 else                 
+                    pClient->AuxReturnCode = MMSYSERR_NOERROR;                
+                 break;
+
+            case WaveThreadTerminate:
+                 Terminate = TRUE;
+                 break;
+
+            default:
+                 DPRINT("WaveThread Error");
+                 break;
+
+        }
+
+        pClient->AuxFunction = WaveThreadInvalid;
+
+        while (pClient->DeviceQueue && (pClient->DeviceQueue->dwFlags & WHDR_COMPLETE)) 
+        {
+            PWAVEHDR pHdr;        
+            PWAVEALLOC pWav;
+
+            pHdr = pClient->DeviceQueue;        
+            pClient->DeviceQueue = pHdr->lpNext;
+    
+            pHdr->dwFlags &= ~WHDR_COMPLETE;
+            pHdr->dwFlags &= ~WHDR_INQUEUE;
+            pHdr->lpNext = NULL;
+            pHdr->dwFlags |= WHDR_DONE;
+
+            pWav = (PWAVEALLOC)pHdr->reserved;
+                
+            if (pWav->dwCallback)
+            {
+                DriverCallback(pWav->dwCallback, HIWORD(pWav->dwFlags), (HDRVR)pWav->hWave,  
+                           pClient->DeviceType == WaveOutDevice ? WOM_DONE : WIM_DATA, 
+                           pWav->dwInstance, (DWORD)pHdr, 0L); 
+            }
+        }
+
+        waveReadWrite(pClient);
+
+        if (Terminate) return 1; 
+        SetEvent(pClient->AuxEvent2);
+        while (WaitForSingleObjectEx(pClient->AuxEvent1, INFINITE, TRUE) == WAIT_IO_COMPLETION) 
+        {
+           while (pClient->DeviceQueue && (pClient->DeviceQueue->dwFlags & WHDR_COMPLETE)) 
+           {
+                PWAVEHDR pHdr;        
+                PWAVEALLOC pWav;
+
+                pHdr = pClient->DeviceQueue;        
+                pClient->DeviceQueue = pHdr->lpNext;
+    
+                pHdr->dwFlags &= ~WHDR_COMPLETE;
+                pHdr->dwFlags &= ~WHDR_INQUEUE;
+                pHdr->lpNext = NULL;
+                pHdr->dwFlags |= WHDR_DONE;
+
+                pWav = (PWAVEALLOC)pHdr->reserved;
+                
+                if (pWav->dwCallback)
+                {
+                    DriverCallback(pWav->dwCallback, HIWORD(pWav->dwFlags), (HDRVR)pWav->hWave,  
+                           pClient->DeviceType == WaveOutDevice ? WOM_DONE : WIM_DATA, 
+                           pWav->dwInstance, (DWORD)pHdr, 0L); 
+                }
+            }
+
+        waveReadWrite(pClient);
+        }
+    }
+
+
+  return MMSYSERR_NOERROR;
+}
+
+
+static MMRESULT waveReadWrite(PWAVEALLOC pClient)
+{
+    DWORD dwSize;
+    BOOL Result = FALSE;
+
+   
+    while (pClient->NextBuffer) 
+    {
+        PWAVEHDR pHdr;
+
+        pHdr = pClient->NextBuffer;
+        
+        //FIXME
+        //assert(!(pHdr->dwFlags & (WHDR_DONE | WHDR_COMPLETE)));
+        //assert(pClient->DeviceQueue != NULL);
+        
+
+        dwSize = pHdr->dwBufferLength - pClient->BufferPosition;
+        if (dwSize > MAX_BUFFER_SIZE)         
+            dwSize = MAX_BUFFER_SIZE;
+        
+
+        if (dwSize + pClient->BytesOutstanding <= MAX_WAVE_BYTES) 
+        {
+            PWAVEOVL pWaveOvl;
+
+            if (pClient->BufferPosition == 0) 
+            {
+                if (pClient->NextBuffer && (pClient->NextBuffer->dwFlags & WHDR_BEGINLOOP) &&
+                    pClient->NextBuffer != pClient->LoopHead) 
+                {
+                    pClient->LoopCount = pClient->NextBuffer->dwLoops;
+                    pClient->LoopHead = pClient->NextBuffer;                    
+                    if (pClient->LoopCount > 0)                     
+                        pClient->LoopCount--;                    
+                }
+                
+                if (pClient->LoopCount == 0)                 
+                    pClient->LoopHead = NULL;                
+            }
+
+            pWaveOvl = (PWAVEOVL)HeapAlloc(Heap, HEAP_ZERO_MEMORY, sizeof(*pWaveOvl));
+
+            if (pWaveOvl == NULL) 
+                return MMSYSERR_NOMEM;
+            
+            pWaveOvl->WaveHdr = pHdr;
+
+            if (pClient->DeviceType == WaveOutDevice) 
+            {
+                Result =  WriteFileEx(pClient->hDev, 
+                                      (PBYTE)pHdr->lpData + pClient->BufferPosition, 
+                                      dwSize,
+                                     (LPOVERLAPPED)pWaveOvl,
+                                     (LPOVERLAPPED_COMPLETION_ROUTINE)
+                                      (pHdr->dwBufferLength != 
+                                      pClient->BufferPosition + dwSize ? wavePartialOvl : NULL != pClient->LoopHead ?
+                                      waveLoopOvl : waveOvl));
+            } 
+            else if (pClient->DeviceType == WaveInDevice) 
+            {
+                Result =  ReadFileEx(pClient->hDev, (PBYTE)pHdr->lpData + pClient->BufferPosition,
+                                     dwSize, (LPOVERLAPPED)pWaveOvl, 
+                                     (LPOVERLAPPED_COMPLETION_ROUTINE)
+                                     (pHdr->dwBufferLength !=
+                                     pClient->BufferPosition + dwSize ? wavePartialOvl : NULL != pClient->LoopHead ?
+                                     waveLoopOvl :  waveOvl));
+            }
+
+            
+            if (!Result && GetLastError() != ERROR_IO_PENDING) 
+            {
+               HeapFree(Heap, 0, (LPSTR)pWaveOvl);
+               
+                if (pClient->BytesOutstanding == 0) 
+                {
+                    PWAVEHDR pHdr;
+                    for (pHdr = pClient->DeviceQueue; pHdr != NULL; pHdr = pHdr->lpNext) 
+                    {
+                        pHdr->dwFlags |= WHDR_COMPLETE;
+                    }
+        
+                    pClient->NextBuffer = NULL;
+                    pClient->BufferPosition = 0;
+                    
+                }
+                return TranslateStatus();
+
+            } 
+            else 
+            {
+                pClient->BufferPosition += dwSize;
+                pClient->BytesOutstanding += dwSize;                
+                if (pClient->BufferPosition == pHdr->dwBufferLength) 
+                {
+                
+                    if (!pClient->LoopHead || !(pHdr->dwFlags & WHDR_ENDLOOP))                     
+                        pClient->NextBuffer = pHdr->lpNext;                    
+                    else 
+                    {                    
+                        if (pClient->LoopCount != 0) 
+                        {                            
+                            pClient->NextBuffer = pClient->LoopHead;
+                            pClient->LoopCount--;
+                        } 
+                        else 
+                        {
+                            pClient->DummyWaveOvl.WaveHdr = pClient->LoopHead;
+
+                            Result = WriteFileEx(pClient->hDev, (PVOID)pHdr->lpData, 0,
+                                                 &pClient->DummyWaveOvl.Ovl, 
+                                                 (LPOVERLAPPED_COMPLETION_ROUTINE)waveBreakOvl);
+
+                            if (Result || GetLastError() == ERROR_IO_PENDING) 
+                            {
+                                pClient->NextBuffer = pHdr->lpNext;
+                                pClient->LoopHead = NULL;
+                                
+                            }
+                        }
+                    }
+                    pClient->BufferPosition = 0;
+                }
+            }
+            
+
+        } 
+        else                         
+          break;        
+     }
+    return MMSYSERR_NOERROR;
+}
+static MMRESULT waveSetState(PWAVEALLOC pClient, ULONG State)
+{    
+    OVERLAPPED Overlap;
+    DWORD BytesReturned;
+
+    memset((PVOID)&Overlap, 0, sizeof(Overlap));
+
+    Overlap.hEvent = pClient->Event;
+    
+    if (!DeviceIoControl(pClient->hDev, IOCTL_WAVE_SET_STATE, 
+                         &State,  sizeof(State), NULL, 0, &BytesReturned, &Overlap)) 
+    {
+         DWORD cbTransfer;        
+         if (GetLastError() == ERROR_IO_PENDING) 
+         {
+             if (!GetOverlappedResult(pClient->hDev, &Overlap, &cbTransfer, TRUE))             
+                  return TranslateStatus();             
+         } 
+         else              
+             return TranslateStatus();
+         
+    }
+
+    while (SleepEx(0, TRUE) == WAIT_IO_COMPLETION) {}
+    return MMSYSERR_NOERROR;
+}
+
+void wavePartialOvl(DWORD dwErrorCode, DWORD BytesTransferred, LPOVERLAPPED pOverlapped)
+{
+    LPWAVEHDR pHdr;
+    PWAVEALLOC pClient;
+
+    pHdr = ((PWAVEOVL)pOverlapped)->WaveHdr;    
+    pClient = (PWAVEALLOC)pHdr->reserved;
+
+  
+    /* FIXME
+    Assert(pHdr->dwFlags & WHDR_INQUEUE);
+    Assert(!(pHdr->dwFlags & WHDR_COMPLETE));
+    */    
+
+    pClient->BytesOutstanding -= MAX_BUFFER_SIZE;
+
+    if (pClient->DeviceType == WaveInDevice)     
+        pHdr->dwBytesRecorded += BytesTransferred;
+    HeapFree(Heap, 0, (LPSTR)pOverlapped);
+}
+
+void waveBreakOvl(DWORD dwErrorCode, DWORD BytesTransferred, LPOVERLAPPED pOverlapped)
+{
+    ((PWAVEOVL)pOverlapped)->WaveHdr->dwFlags |= WHDR_COMPLETE;
+}
+
+void waveLoopOvl(DWORD dwErrorCode, DWORD BytesTransferred, LPOVERLAPPED pOverlapped)
+{
+    DWORD dwFlags;
+    PWAVEHDR pHdr;
+    
+    pHdr = ((PWAVEOVL)pOverlapped)->WaveHdr;
+    dwFlags = pHdr->dwFlags;
+    waveOvl(dwErrorCode, BytesTransferred, pOverlapped);
+    pHdr->dwFlags = dwFlags;
+}
+
+void waveOvl(DWORD dwErrorCode, DWORD BytesTransferred, LPOVERLAPPED pOverlapped)
+{
+    PWAVEHDR pHdr;
+    PWAVEALLOC pClient;
+
+    pHdr = ((PWAVEOVL)pOverlapped)->WaveHdr;    
+    pClient = (PWAVEALLOC)pHdr->reserved;
+
+    /* FIXME
+       Assert(pHdr->dwFlags & WHDR_INQUEUE);
+       Assert(!(pHdr->dwFlags & WHDR_COMPLETE));
+    */
+   
+    pHdr->dwFlags |= WHDR_COMPLETE;
+
+    if (pHdr->dwFlags & WHDR_BEGINLOOP) 
+    {
+        PWAVEHDR pHdrSearch;
+        for (pHdrSearch = pClient->DeviceQueue ; pHdrSearch != pHdr ; pHdrSearch = pHdrSearch->lpNext) 
+        {
+            //Assert(pHdrSearch != NULL);
+            pHdrSearch->dwFlags |= WHDR_COMPLETE;
+        }
+    }
+   
+    if (pHdr->dwBufferLength)     
+        pClient->BytesOutstanding -= (pHdr->dwBufferLength - 1) % MAX_BUFFER_SIZE + 1;
+    
+    if (pClient->DeviceType == WaveInDevice)     
+        pHdr->dwBytesRecorded += BytesTransferred;
+    
+    HeapFree(Heap, 0, (LPSTR)pOverlapped);
+
+}
+
+
+
+
+
+static MMRESULT OpenWaveDevice(UINT  DeviceType,
+                                                               DWORD id,
+                                                               DWORD dwUser,
+                                                               DWORD dwParam1,
+                                                               DWORD dwParam2)
+{
+       // TODO: Implement
+    PWAVEALLOC     pClient = (PWAVEALLOC)dwUser;  
+    MMRESULT mResult;
+    BOOL Result;
+    DWORD BytesReturned;
+    LPWAVEFORMATEX pFormats;
+    PWAVEALLOC *pUserHandle = NULL;
+    HANDLE hDevice;
+
+    pFormats = (LPWAVEFORMATEX)((LPWAVEOPENDESC)dwParam1)->lpFormat;
+
+    if (dwParam2 & WAVE_FORMAT_QUERY) 
+    {        
+        mResult = OpenDevice(DeviceType, id, &hDevice, GENERIC_READ);
+        if (mResult != MMSYSERR_NOERROR) 
+            return mResult;
+        
+        Result = DeviceIoControl(hDevice, IOCTL_WAVE_QUERY_FORMAT, (PVOID)pFormats,
+                                 pFormats->wFormatTag == WAVE_FORMAT_PCM ?
+                                 sizeof(PCMWAVEFORMAT) : sizeof(WAVEFORMATEX) + pFormats->cbSize,                        
+                                 NULL, 0, &BytesReturned, NULL);
+        CloseHandle(hDevice);
+        return Result ? MMSYSERR_NOERROR : GetLastError() == ERROR_NOT_SUPPORTED ? WAVERR_BADFORMAT : TranslateStatus();
+    }
+
+    EnterCriticalSection(&CS);
+
+    for (pClient = WaveLists; pClient != NULL; pClient = pClient->Next) 
+    {
+        if (pClient->DeviceNumber == id && pClient->DeviceType == DeviceType) 
+        {        
+            if (pClient->hDev != INVALID_HANDLE_VALUE) 
+            {
+                LeaveCriticalSection(&CS);
+                return MMSYSERR_ALLOCATED;
+            }
+            break;
+        }
+    }
+
+    if (pClient == NULL) 
+    {
+        pClient = (PWAVEALLOC)HeapAlloc(Heap, HEAP_ZERO_MEMORY, sizeof(WAVEALLOC));
+        if (pClient == NULL) 
+        {
+            LeaveCriticalSection(&CS);
+            return MMSYSERR_NOMEM;
+        }
+        
+        pClient->DeviceNumber = id;
+        pClient->Next = WaveLists;
+        pClient->DeviceType = DeviceType;
+        
+        WaveLists = pClient;
+    }
+
+    pClient->hWave       = ((LPWAVEOPENDESC)dwParam1)->hWave;
+    pClient->dwInstance  = ((LPWAVEOPENDESC)dwParam1)->dwInstance;
+    pClient->dwFlags     = dwParam2;
+    pClient->dwCallback  = ((LPWAVEOPENDESC)dwParam1)->dwCallback;
+    pClient->hDev = INVALID_HANDLE_VALUE;
+    pClient->NextBuffer  = NULL;
+    pClient->DeviceQueue = NULL;
+    pClient->LoopHead    = NULL;
+    pClient->LoopCount   = 0;
+    pClient->BytesOutstanding = 0;
+    pClient->BufferPosition = 0;
+    
+    
+    
+
+    mResult = OpenDevice(DeviceType, id, &pClient->hDev, (GENERIC_READ | GENERIC_WRITE));
+
+    if (mResult != MMSYSERR_NOERROR) 
+    {    
+        LeaveCriticalSection(&CS);
+        return mResult;
+    }
+
+     if (!DeviceIoControl(pClient->hDev,IOCTL_WAVE_SET_FORMAT, (PVOID)pFormats,
+                             pFormats->wFormatTag == WAVE_FORMAT_PCM ?
+                             sizeof(PCMWAVEFORMAT) : sizeof(WAVEFORMATEX) + pFormats->cbSize,
+                             NULL, 0, &BytesReturned, NULL))
+    {
+        CloseHandle(pClient->hDev);
+        pClient->hDev = INVALID_HANDLE_VALUE;
+        LeaveCriticalSection(&CS);
+        return GetLastError() == ERROR_NOT_SUPPORTED ? WAVERR_BADFORMAT : TranslateStatus();
+    }
+
+    LeaveCriticalSection(&CS);
+
+    if (!pClient->Event) 
+    {
+        pClient->Event = CreateEvent(NULL, FALSE, FALSE, NULL);
+        if (pClient->Event == NULL) 
+        {
+            // Cleanup
+            return MMSYSERR_NOMEM;
+        }
+
+        pClient->AuxEvent1 = CreateEvent(NULL, FALSE, FALSE, NULL);
+        if (!pClient->AuxEvent1) 
+        {
+           // Cleanup
+            return MMSYSERR_NOMEM;
+        }
+
+        pClient->AuxEvent2 = CreateEvent(NULL, FALSE, FALSE, NULL);
+        if (!pClient->AuxEvent2) 
+        {
+            // Cleanup
+            return MMSYSERR_NOMEM;
+        }
+
+
+        mResult = mmTaskCreate((LPTASKCALLBACK)waveThread, &pClient->ThreadHandle, (DWORD)pClient);
+        if ( mResult != MMSYSERR_NOERROR) 
+        {
+            // Cleanup
+            return MMSYSERR_NOMEM;
+        }
+
+        WaitForSingleObject(pClient->AuxEvent2, INFINITE);
+     }
+     
+     *pUserHandle = pClient;
+      pUserHandle = (PWAVEALLOC *)dwUser;
+   
+
+     if (pClient->dwCallback)
+     {
+        DriverCallback(pClient->dwCallback, HIWORD(pClient->dwFlags),
+                       (HDRVR)pClient->hWave,  DeviceType == WaveOutDevice ? WOM_OPEN : WIM_OPEN, 
+                       pClient->dwInstance, 0L, 0L);                 
+     }
+
+       return MMSYSERR_NOERROR;
+}
+
+
+//FIXME: MS-specific code, except for name of the func!
+MMRESULT GetPositionWaveDevice(PWAVEALLOC pClient, LPMMTIME lpmmt, DWORD dwSize)
+{
+       /*
+    WAVE_DD_POSITION PositionData;
+    MMRESULT mErr;
+
+    if (dwSize < sizeof(MMTIME))
+        return MMSYSERR_ERROR;
+
+    //
+    // Get the current position from the driver
+    //
+    mErr = sndGetHandleData(pClient->hDev,
+                            sizeof(PositionData),
+                            &PositionData,
+                            IOCTL_WAVE_GET_POSITION,
+                            pClient->Event);
+
+    if (mErr == MMSYSERR_NOERROR) {
+        if (lpmmt->wType == TIME_BYTES) {
+            lpmmt->u.cb = PositionData.ByteCount;
+        }
+
+        // default is samples.
+        else {
+            lpmmt->wType = TIME_SAMPLES;
+            lpmmt->u.sample = PositionData.SampleCount;
+        }
+    }
+
+    return mErr;*/ return MMSYSERR_NOERROR;
+}
+
+
+
+MMRESULT soundSetData(UINT DeviceType, UINT DeviceId, UINT Length, PBYTE Data,
+                     ULONG Ioctl)
+{
+    HANDLE hDevcie;
+    MMRESULT Result;
+    DWORD BytesReturned;
+
+    Result = OpenDevice(DeviceType, DeviceId, &hDevcie, GENERIC_READ);
+    if (Result != MMSYSERR_NOERROR) 
+         return Result;
+
+    Result = DeviceIoControl(hDevcie, Ioctl, Data, Length, NULL, 0, &BytesReturned, NULL) ? 
+                             MMSYSERR_NOERROR : TranslateStatus();
+    CloseHandle(hDevcie);
+
+       return Result;
+}
+
+MMRESULT soundGetData(UINT DeviceType, UINT DeviceId, UINT Length, PBYTE Data,
+                     ULONG Ioctl)
+{
+       HANDLE hDevice;
+    MMRESULT Result;
+    DWORD BytesReturned;
+
+    Result = OpenDevice(DeviceType, DeviceId, &hDevice, GENERIC_READ);
+    if (Result != MMSYSERR_NOERROR)     
+         return Result;
+        
+    Result = DeviceIoControl(hDevice, Ioctl, NULL, 0, (LPVOID)Data, Length,
+                           &BytesReturned, NULL) ? MMSYSERR_NOERROR : TranslateStatus();
+    CloseHandle(hDevice);
+    return Result;
+}
+
+
+/* ============================
+ *  EXPORT
+ *  functions start here
+ * ============================
+ */
+
+/*
+ * @implemented
+ */
+APIENTRY DWORD wodMessage(DWORD dwId, DWORD dwMessage, DWORD dwUser, DWORD dwParam1, DWORD dwParam2)
+{
+    PWAVEALLOC pTask = (PWAVEALLOC)dwUser;
+
+    switch (dwMessage) {
+        case WODM_GETNUMDEVS:
+            DPRINT("WODM_GETNUMDEVS");
+            return GetDeviceCount(WaveOutDevice);
+
+        case WODM_GETDEVCAPS:
+            DPRINT("WODM_GETDEVCAPS");
+            return GetDeviceCapabilities(dwId, WaveOutDevice, (LPBYTE)dwParam1,
+                                  (DWORD)dwParam2);
+
+        case WODM_OPEN:
+            DPRINT("WODM_OPEN");
+            return OpenWaveDevice(WaveOutDevice, dwId, dwUser, dwParam1, dwParam2);
+
+        case WODM_CLOSE:
+                       {       
+                               DPRINT("WODM_CLOSE");
+
+                               // 1. Check if the task is ready to complete
+                pTask->AuxFunction = WaveThreadClose;
+                SetEvent(pTask->AuxEvent1);
+                WaitForSingleObject(pTask->AuxEvent2, INFINITE);
+                                       
+                               if ( pTask->AuxReturnCode != MMSYSERR_NOERROR) 
+                {
+                                   return pTask->AuxReturnCode;
+                               }
+                               else
+                                       
+                {
+                    if (pTask->dwCallback)
+                    {
+                        DriverCallback(pTask->dwCallback, HIWORD(pTask->dwFlags), (HDRVR)pTask->hWave, 
+                                       WOM_CLOSE, pTask->dwInstance, 0L, 0L);                   
+                    }
+                }
+
+                               
+                               // 2. Close the device
+                               if (pTask->hDev != INVALID_HANDLE_VALUE) {
+                                       CloseHandle(pTask->hDev);
+
+                                       EnterCriticalSection(&CS);
+                                       pTask->hDev = INVALID_HANDLE_VALUE;
+                                       LeaveCriticalSection(&CS);
+                               }
+
+                               return MMSYSERR_NOERROR;
+                       };
+
+        case WODM_WRITE:
+                       {
+                               LPWAVEHDR pWaveHdr = (LPWAVEHDR)dwParam1;
+
+                               DPRINT("WODM_WRITE");
+
+                               if (dwParam1 != 0)
+                                       return MMSYSERR_INVALPARAM;
+
+                               if ((pWaveHdr->dwFlags & ~(WHDR_INQUEUE|WHDR_DONE|WHDR_PREPARED|WHDR_BEGINLOOP|WHDR_ENDLOOP)))
+                                       return MMSYSERR_INVALPARAM;
+
+                               pWaveHdr->dwFlags &= (WHDR_INQUEUE|WHDR_DONE|WHDR_PREPARED|WHDR_BEGINLOOP|WHDR_ENDLOOP);
+
+                               if ((pWaveHdr->dwFlags & WHDR_PREPARED) == 0)
+                                       return MMSYSERR_INVALPARAM;
+
+                               // Check, if the wave header is already prepared
+                               if (!(pWaveHdr->dwFlags & WHDR_PREPARED))
+                                       return WAVERR_UNPREPARED;
+
+                               // If it's already located in the queue, this op is impossible
+                               if (pWaveHdr->dwFlags & WHDR_INQUEUE )
+                                       return ( WAVERR_STILLPLAYING );
+
+                               // save WAVEALLOC pointer in the WaveHeader
+                               pWaveHdr->reserved = dwUser;
+
+                               
+                pWaveHdr->dwFlags |= WHDR_INQUEUE;
+                pWaveHdr->dwFlags &= ~WHDR_DONE;
+                pTask->AuxParam.pHdr = pWaveHdr;
+                
+                pTask->AuxFunction = WaveThreadAddBuffer;
+                SetEvent(pTask->AuxEvent1);
+                WaitForSingleObject(pTask->AuxEvent2, INFINITE);
+                return pTask->AuxReturnCode;
+                       }
+
+        case WODM_PAUSE:
+            DPRINT("WODM_PAUSE");
+            pTask->AuxParam.State = WAVE_DD_STOP;
+           
+            pTask->AuxFunction = WaveThreadSetState;
+            SetEvent(pTask->AuxEvent1);
+            WaitForSingleObject(pTask->AuxEvent2, INFINITE);
+               return pTask->AuxReturnCode;
+
+        case WODM_RESTART:
+            DPRINT("WODM_RESTART");
+            pTask->AuxParam.State = WAVE_DD_PLAY;
+
+            pTask->AuxFunction = WaveThreadSetState;
+            SetEvent(pTask->AuxEvent1);
+            WaitForSingleObject(pTask->AuxEvent2, INFINITE);
+               return pTask->AuxReturnCode;            
+
+        case WODM_RESET:
+            DPRINT("WODM_RESET");
+            pTask->AuxParam.State = WAVE_DD_RESET;
+
+            pTask->AuxFunction = WaveThreadSetState;
+            SetEvent(pTask->AuxEvent1);
+            WaitForSingleObject(pTask->AuxEvent2, INFINITE);
+               return pTask->AuxReturnCode;
+           
+        case WODM_BREAKLOOP:
+            DPRINT("WODM_BREAKLOOP");
+
+            pTask->AuxFunction = WaveThreadBreakLoop;
+            SetEvent(pTask->AuxEvent1);
+            WaitForSingleObject(pTask->AuxEvent2, INFINITE);
+               return pTask->AuxReturnCode;
+
+        case WODM_GETPOS:
+            DPRINT("WODM_GETPOS");
+            return GetPositionWaveDevice(pTask, (LPMMTIME)dwParam1, dwParam2);
+
+        case WODM_SETPITCH:
+            DPRINT("WODM_SETPITCH");
+            pTask->AuxParam.GetSetData.pData = (PBYTE)&dwParam1;
+            pTask->AuxParam.GetSetData.DataLen = sizeof(DWORD);
+            pTask->AuxParam.GetSetData.Function = IOCTL_WAVE_SET_PITCH;
+            
+            pTask->AuxFunction = WaveThreadSetData;
+            SetEvent(pTask->AuxEvent1);
+            WaitForSingleObject(pTask->AuxEvent2, INFINITE);
+               return pTask->AuxReturnCode;
+
+        case WODM_SETVOLUME:
+            DPRINT("WODM_SETVOLUME");
+            {
+                WAVE_DD_VOLUME Vol;
+                Vol.Left = LOWORD(dwParam1) << 16;
+                Vol.Right = HIWORD(dwParam1) << 16;
+
+                return soundSetData(WaveOutDevice, dwId, sizeof(Vol),
+                                  (PBYTE)&Vol, IOCTL_WAVE_SET_VOLUME);
+            }
+
+        case WODM_SETPLAYBACKRATE:
+            DPRINT("WODM_SETPLAYBACKRATE");
+            pTask->AuxParam.GetSetData.pData = (PBYTE)&dwParam1;
+            pTask->AuxParam.GetSetData.DataLen = sizeof(DWORD);
+            pTask->AuxParam.GetSetData.Function = IOCTL_WAVE_SET_PLAYBACK_RATE;
+            
+            pTask->AuxFunction = WaveThreadSetData;
+            SetEvent(pTask->AuxEvent1);
+            WaitForSingleObject(pTask->AuxEvent2, INFINITE);
+               return pTask->AuxReturnCode;
+
+
+        case WODM_GETPITCH:
+            DPRINT("WODM_GETPITCH");
+            pTask->AuxParam.GetSetData.pData = (PBYTE)dwParam1;
+            pTask->AuxParam.GetSetData.DataLen = sizeof(DWORD);
+            pTask->AuxParam.GetSetData.Function = IOCTL_WAVE_GET_PITCH;
+            
+            pTask->AuxFunction = WaveThreadGetData;
+            SetEvent(pTask->AuxEvent1);
+            WaitForSingleObject(pTask->AuxEvent2, INFINITE);
+               return pTask->AuxReturnCode;
+
+        case WODM_GETVOLUME:
+            DPRINT("WODM_GETVOLUME");
+            {
+                WAVE_DD_VOLUME Vol = {};
+                DWORD res;
+
+                res = soundGetData(WaveOutDevice, dwId, sizeof(Vol),
+                                (PBYTE)&Vol, IOCTL_WAVE_GET_VOLUME);
+
+                if (res == MMSYSERR_NOERROR)
+                    *(LPDWORD)dwParam1 = (DWORD)MAKELONG(HIWORD(Vol.Left), HIWORD(Vol.Right));
+                
+                return res;
+            }
+
+        case WODM_GETPLAYBACKRATE:
+            DPRINT("WODM_GETPLAYBACKRATE");
+            pTask->AuxParam.GetSetData.pData = (PBYTE)dwParam1;
+            pTask->AuxParam.GetSetData.DataLen = sizeof(DWORD);
+            pTask->AuxParam.GetSetData.Function = IOCTL_WAVE_GET_PLAYBACK_RATE;
+                         
+            pTask->AuxFunction = WaveThreadGetData;
+            SetEvent(pTask->AuxEvent1);
+            WaitForSingleObject(pTask->AuxEvent2, INFINITE);
+               return pTask->AuxReturnCode;
+
+        default:
+            return MMSYSERR_NOTSUPPORTED;
+    }
+
+       // This point of execution should never be reached
+    return MMSYSERR_NOTSUPPORTED;
+}
+
+
+/*
+ * @implemented
+ */
+APIENTRY DWORD widMessage(DWORD dwId, DWORD dwMessage, DWORD dwUser, DWORD dwParam1, DWORD dwParam2)
+{
+    DPRINT("widMessage\n");
+
+    switch (dwMessage) 
+    {
+        case WIDM_GETNUMDEVS: 
+            DPRINT("WIDM_GETNUMDEVS");
+            return GetDeviceCount(WaveInDevice);
+
+        case WIDM_GETDEVCAPS:
+            DPRINT("WODM_GETDEVCAPS");
+            return GetDeviceCapabilities(dwId, WaveInDevice, (LPBYTE)dwParam1, (DWORD)dwParam2);
+             
+        case WIDM_OPEN:
+            DPRINT("WIDM_OPEN");
+            return OpenWaveDevice(WaveInDevice, dwId, dwUser, dwParam1, dwParam2);
+
+        case WIDM_CLOSE:
+             return MMSYSERR_NOERROR;
+
+        case WIDM_ADDBUFFER:
+             return MMSYSERR_NOERROR;
+
+        case WIDM_STOP:
+             return MMSYSERR_NOERROR;
+
+        case WIDM_START:
+             return MMSYSERR_NOERROR;
+
+        case WIDM_RESET:
+             return MMSYSERR_NOERROR;
+
+        case WIDM_GETPOS:
+             return MMSYSERR_NOERROR;
+
+
+        default :
+             return MMSYSERR_NOTSUPPORTED;
+    }
+}
+
diff --git a/reactos/dll/mmdrv/wave.h b/reactos/dll/mmdrv/wave.h
new file mode 100644 (file)
index 0000000..42daed2
--- /dev/null
@@ -0,0 +1,92 @@
+
+// FIXME: Should be moved somewhere else?
+typedef struct _WAVE_DD_VOLUME {
+    ULONG   Left;
+    ULONG   Right;
+} WAVE_DD_VOLUME, *PWAVE_DD_VOLUME;
+
+// driver
+#define WAVE_DD_STOP        0x0001
+#define WAVE_DD_PLAY        0x0002      // output devices only
+#define WAVE_DD_RECORD      0x0003      // input devices only
+#define WAVE_DD_RESET       0x0004
+
+// ioctl
+#define WAVE_DD_IDLE        0x0000
+#define WAVE_DD_STOPPED     0x0001      // stopped
+#define WAVE_DD_PLAYING     0x0002      // output devices only
+#define WAVE_DD_RECORDING   0x0003      // input devices only
+
+
+
+typedef enum {
+    WaveThreadInvalid,
+    WaveThreadAddBuffer,
+    WaveThreadSetState,
+    WaveThreadSetData,
+    WaveThreadGetData,
+    WaveThreadBreakLoop,
+    WaveThreadClose,
+    WaveThreadTerminate
+} WAVETHREADFUNCTION;
+
+// WARNING: MS code below!!
+typedef struct {
+    OVERLAPPED Ovl;
+    LPWAVEHDR WaveHdr;
+} WAVEOVL, *PWAVEOVL;
+
+// WARNING: MS code below!!
+// per allocation structure for wave
+typedef struct tag_WAVEALLOC {
+    struct tag_WAVEALLOC *Next;         // Chaining
+    UINT                DeviceNumber;   // Which device
+    UINT                DeviceType;     // WaveInput or WaveOutput
+    DWORD               dwCallback;     // client's callback
+    DWORD               dwInstance;     // client's instance data
+    DWORD               dwFlags;        // Open flags
+    HWAVE               hWave;          // handle for stream
+
+    HANDLE              hDev;           // Wave device handle
+    LPWAVEHDR           DeviceQueue;    // Buffers queued by application
+    LPWAVEHDR           NextBuffer;     // Next buffer to send to device
+    DWORD               BufferPosition; // How far we're into a large buffer
+    DWORD               BytesOutstanding;
+                                        // Bytes being processed by device
+    LPWAVEHDR           LoopHead;       // Start of loop if any
+    DWORD               LoopCount;      // Number more loops to go
+
+    WAVEOVL             DummyWaveOvl;   // For break loop
+                                                                                //
+    HANDLE              Event;          // Event for driver syncrhonization
+                                        // and notification of auxiliary
+                                        // task operation completion.
+    WAVETHREADFUNCTION  AuxFunction;    // Function for thread to perform
+    union {
+        LPWAVEHDR       pHdr;           // Buffer to pass in aux task
+        ULONG           State;          // State to set
+        struct {
+            ULONG       Function;       // IOCTL to use
+            PBYTE       pData;          // Data to set or get
+            ULONG       DataLen;        // Length of data
+        } GetSetData;
+
+    } AuxParam;
+                                        // 0 means terminate task.
+    HANDLE              AuxEvent1;      // Aux thread waits on this
+    HANDLE              AuxEvent2;      // Caller of Aux thread waits on this
+    HANDLE              ThreadHandle;   // Handle for thread termination ONLY
+    MMRESULT            AuxReturnCode;  // Return code from Aux task
+}WAVEALLOC, *PWAVEALLOC;
+
+/* Misc should move to own header */
+MMRESULT GetDeviceCapabilities(DWORD ID, UINT DeviceType,
+                                      LPBYTE pCaps, DWORD Size);
+
+DWORD AuxGetAudio(DWORD dwID, PBYTE pVolume, DWORD sizeVolume);
+DWORD AuxSetAudio(DWORD dwID, PBYTE pVolume, DWORD sizeVolume);
+
+typedef struct _AUX_DD_VOLUME {
+        ULONG   Left;
+        ULONG   Right;
+} AUX_DD_VOLUME, *PAUX_DD_VOLUME;
diff --git a/reactos/dll/mpr/Makefile.in b/reactos/dll/mpr/Makefile.in
new file mode 100644 (file)
index 0000000..988a58b
--- /dev/null
@@ -0,0 +1,22 @@
+TOPSRCDIR = @top_srcdir@\r
+TOPOBJDIR = ../..\r
+SRCDIR    = @srcdir@\r
+VPATH     = @srcdir@\r
+MODULE    = mpr.dll\r
+IMPORTLIB = libmpr.$(IMPLIBEXT)\r
+IMPORTS   = user32 advapi32 kernel32\r
+EXTRALIBS = $(LIBUNICODE)\r
+\r
+C_SRCS = \\r
+       auth.c \\r
+       mpr_main.c \\r
+       multinet.c \\r
+       nps.c \\r
+       pwcache.c \\r
+       wnet.c\r
+\r
+RC_SRCS = mpr.rc\r
+\r
+@MAKE_DLL_RULES@\r
+\r
+### Dependencies:\r
diff --git a/reactos/dll/mpr/auth.c b/reactos/dll/mpr/auth.c
new file mode 100644 (file)
index 0000000..531ca8a
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * MPR Authentication and Logon functions
+ *
+ * Copyright 1999 Ulrich Weigand
+ *
+ * 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 <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winnetwk.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(mpr);
+
+
+/*****************************************************************
+ *  WNetLogoffA [MPR.@]
+ */
+DWORD WINAPI WNetLogoffA( LPCSTR lpProvider, HWND hwndOwner )
+{
+    FIXME( "(%s, %p): stub\n", debugstr_a(lpProvider), hwndOwner );
+
+    SetLastError(WN_NO_NETWORK);
+    return WN_NO_NETWORK;
+}
+
+/*****************************************************************
+ *  WNetLogoffW [MPR.@]
+ */
+DWORD WINAPI WNetLogoffW( LPCWSTR lpProvider, HWND hwndOwner )
+{
+    FIXME( "(%s, %p): stub\n", debugstr_w(lpProvider), hwndOwner );
+
+    SetLastError(WN_NO_NETWORK);
+    return WN_NO_NETWORK;
+}
+
+/*****************************************************************
+ *  WNetLogonA [MPR.@]
+ */
+DWORD WINAPI WNetLogonA( LPCSTR lpProvider, HWND hwndOwner )
+{
+    FIXME( "(%s, %p): stub\n", debugstr_a(lpProvider), hwndOwner );
+
+    SetLastError(WN_NO_NETWORK);
+    return WN_NO_NETWORK;
+}
+
+/*****************************************************************
+ *  WNetLogonW [MPR.@]
+ */
+DWORD WINAPI WNetLogonW( LPCWSTR lpProvider, HWND hwndOwner )
+{
+    FIXME( "(%s, %p): stub\n", debugstr_w(lpProvider), hwndOwner );
+
+    SetLastError(WN_NO_NETWORK);
+    return WN_NO_NETWORK;
+}
+
+/*****************************************************************
+ *  WNetVerifyPasswordA [MPR.@]
+ */
+DWORD WINAPI WNetVerifyPasswordA( LPCSTR lpszPassword, BOOL *pfMatch )
+{
+    FIXME( "(%p, %p): stub\n", lpszPassword, pfMatch );
+
+    SetLastError(WN_NO_NETWORK);
+    return WN_NO_NETWORK;
+}
+
+/*****************************************************************
+ *  WNetVerifyPasswordW [MPR.@]
+ */
+DWORD WINAPI WNetVerifyPasswordW( LPCWSTR lpszPassword, BOOL *pfMatch )
+{
+    FIXME( "(%p, %p): stub\n", lpszPassword, pfMatch );
+
+    SetLastError(WN_NO_NETWORK);
+    return WN_NO_NETWORK;
+}
diff --git a/reactos/dll/mpr/mpr.rc b/reactos/dll/mpr/mpr.rc
new file mode 100644 (file)
index 0000000..418d41d
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * MPR dll resources
+ *
+ * Copyright (C) 2004 Juan Lang
+ *
+ * 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 "windef.h"
+#include "winbase.h"
+#include "winuser.h"
+#include "mprres.h"
+
+#include "version.rc"
+
+#include "mpr_Bg.rc"
+#include "mpr_Cs.rc"
+#include "mpr_De.rc"
+#include "mpr_En.rc"
+#include "mpr_Es.rc"
+#include "mpr_Fr.rc"
+#include "mpr_It.rc"
+#include "mpr_Ja.rc"
+#include "mpr_Ko.rc"
+#include "mpr_Nl.rc"
+#include "mpr_No.rc"
+#include "mpr_Pt.rc"
+#include "mpr_Ru.rc"
+#include "mpr_Sv.rc"
+#include "mpr_Hu.rc"
+#include "mpr_Uk.rc"
diff --git a/reactos/dll/mpr/mpr.spec b/reactos/dll/mpr/mpr.spec
new file mode 100644 (file)
index 0000000..c383c75
--- /dev/null
@@ -0,0 +1,114 @@
+# ordinal exports
+ 1 stub @
+ 2 stub @
+ 3 stub @
+ 4 stub @
+ 5 stub @
+ 6 stub @
+ 7 stub @
+ 8 stub @
+ 9 stub @
+12 stub @
+13 stub @
+14 stub @
+15 stub @
+16 stub @
+17 stub @
+18 stub @
+19 stub @
+20 stub @
+21 stub @
+22 stdcall @(long) MPR_Alloc
+23 stdcall @(ptr long) MPR_ReAlloc
+24 stdcall @(ptr) MPR_Free
+25 stdcall @(ptr long) _MPR_25
+
+@ stdcall -private DllCanUnloadNow()
+@ stub DllGetClassObject
+@ stdcall MultinetGetConnectionPerformanceA(ptr ptr)
+@ stdcall MultinetGetConnectionPerformanceW(ptr ptr)
+@ stdcall MultinetGetErrorTextA(long ptr long)
+@ stdcall MultinetGetErrorTextW(long ptr long)
+@ stdcall NPSAuthenticationDialogA(ptr)
+@ stdcall NPSCopyStringA(str ptr ptr)
+@ stdcall NPSDeviceGetNumberA(str ptr ptr)
+@ stdcall NPSDeviceGetStringA(long long ptr long)
+@ stdcall NPSGetProviderHandleA(ptr)
+@ stdcall NPSGetProviderNameA(long ptr)
+@ stdcall NPSGetSectionNameA(long ptr)
+@ stdcall NPSNotifyGetContextA(ptr)
+@ stdcall NPSNotifyRegisterA(long ptr)
+@ stdcall NPSSetCustomTextA(str)
+@ stdcall NPSSetExtendedErrorA(long str)
+@ stub PwdChangePasswordA
+@ stub PwdChangePasswordW
+@ stub PwdGetPasswordStatusA
+@ stub PwdGetPasswordStatusW
+@ stub PwdSetPasswordStatusA
+@ stub PwdSetPasswordStatusW
+@ stdcall WNetAddConnection2A(ptr str str long)
+@ stdcall WNetAddConnection2W(ptr wstr wstr long)
+@ stdcall WNetAddConnection3A(long ptr str str long)
+@ stdcall WNetAddConnection3W(long ptr wstr wstr long)
+@ stdcall WNetAddConnectionA(str str str)
+@ stdcall WNetAddConnectionW(wstr wstr wstr)
+@ stdcall WNetCachePassword(str long str long long long)
+@ stdcall WNetCancelConnection2A(str long long)
+@ stdcall WNetCancelConnection2W(wstr long long)
+@ stdcall WNetCancelConnectionA(str long)
+@ stdcall WNetCancelConnectionW(wstr long)
+@ stdcall WNetCloseEnum(long)
+@ stdcall WNetConnectionDialog1A(ptr)
+@ stdcall WNetConnectionDialog1W(ptr)
+@ stdcall WNetConnectionDialog(long long)
+@ stdcall WNetDisconnectDialog1A(ptr)
+@ stdcall WNetDisconnectDialog1W(ptr)
+@ stdcall WNetDisconnectDialog(long long)
+@ stdcall WNetEnumCachedPasswords(str long long ptr long)
+@ stdcall WNetEnumResourceA(long ptr ptr ptr)
+@ stdcall WNetEnumResourceW(long ptr ptr ptr)
+@ stub WNetFMXEditPerm
+@ stub WNetFMXGetPermCaps
+@ stub WNetFMXGetPermHelp
+@ stub WNetFormatNetworkNameA
+@ stub WNetFormatNetworkNameW
+@ stdcall WNetGetCachedPassword(ptr long ptr ptr long)
+@ stdcall WNetGetConnectionA(str ptr ptr)
+@ stdcall WNetGetConnectionW(wstr ptr ptr)
+@ stub WNetGetDirectoryTypeA
+@ stub WNetGetHomeDirectoryA
+@ stub WNetGetHomeDirectoryW
+@ stdcall WNetGetLastErrorA(ptr ptr long ptr long)
+@ stdcall WNetGetLastErrorW(ptr ptr long ptr long)
+@ stdcall WNetGetNetworkInformationA(str ptr)
+@ stdcall WNetGetNetworkInformationW(wstr ptr)
+@ stub WNetGetPropertyTextA
+@ stdcall WNetGetProviderNameA(long ptr ptr)
+@ stdcall WNetGetProviderNameW(long ptr ptr)
+@ stdcall WNetGetResourceInformationA(ptr ptr ptr ptr)
+@ stdcall WNetGetResourceInformationW(ptr ptr ptr ptr)
+@ stdcall WNetGetResourceParentA(ptr ptr ptr)
+@ stdcall WNetGetResourceParentW(ptr ptr ptr)
+@ stdcall WNetGetUniversalNameA (str long ptr ptr)
+@ stdcall WNetGetUniversalNameW (wstr long ptr ptr)
+@ stdcall WNetGetUserA(str ptr ptr)
+@ stdcall WNetGetUserW(wstr wstr ptr)
+@ stdcall WNetLogoffA(str long)
+@ stdcall WNetLogoffW(wstr long)
+@ stdcall WNetLogonA(str long)
+@ stub WNetLogonNotify
+@ stdcall WNetLogonW(wstr long)
+@ stdcall WNetOpenEnumA(long long long ptr ptr)
+@ stdcall WNetOpenEnumW(long long long ptr ptr)
+@ stub WNetPasswordChangeNotify
+@ stub WNetPropertyDialogA
+@ stdcall WNetRemoveCachedPassword(long long long)
+@ stub WNetRestoreConnection
+@ stdcall WNetRestoreConnectionA(long str)
+@ stdcall WNetRestoreConnectionW(long wstr)
+@ stdcall WNetSetConnectionA(str long ptr)
+@ stdcall WNetSetConnectionW(wstr long ptr)
+@ stdcall WNetUseConnectionA(long ptr str str long str ptr ptr)
+@ stdcall WNetUseConnectionW(long ptr wstr wstr long wstr ptr ptr)
+@ stdcall WNetVerifyPasswordA(str ptr)
+@ stdcall WNetVerifyPasswordW(wstr ptr)
diff --git a/reactos/dll/mpr/mpr.xml b/reactos/dll/mpr/mpr.xml
new file mode 100644 (file)
index 0000000..62c8744
--- /dev/null
@@ -0,0 +1,25 @@
+<module name="mpr" type="win32dll" baseaddress="${BASEADDRESS_MPR}" installbase="system32" installname="mpr.dll" allowwarnings="true">
+       <importlibrary definition="mpr.spec.def" />
+       <include base="mpr">.</include>
+       <include base="ReactOS">include/wine</include>
+       <define name="UNICODE" />
+       <define name="_UNICODE" />
+       <define name="__REACTOS__" />
+       <define name="__USE_W32API" />
+       <define name="_WIN32_IE">0x600</define>
+       <define name="_WIN32_WINNT">0x501</define>
+       <define name="WINVER">0x501</define>
+       <library>wine</library>
+       <library>ntdll</library>
+       <library>kernel32</library>
+       <library>advapi32</library>
+       <library>user32</library>
+       <file>auth.c</file>
+       <file>mpr_main.c</file>
+       <file>multinet.c</file>
+       <file>nps.c</file>
+       <file>pwcache.c</file>
+       <file>wnet.c</file>
+       <file>mpr.rc</file>
+       <file>mpr.spec</file>
+</module>
diff --git a/reactos/dll/mpr/mpr_Bg.rc b/reactos/dll/mpr/mpr_Bg.rc
new file mode 100644 (file)
index 0000000..615e093
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * MPR Bulgarian resource
+ *
+ * Copyright 2005 Milko Krachounov
+ *
+ * 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
+ */
+
+LANGUAGE LANG_BULGARIAN, SUBLANG_DEFAULT
+
+STRINGTABLE DISCARDABLE
+{
+    IDS_ENTIRENETWORK "Öÿëàòà ìðåæà"
+}
+
+IDD_PROXYDLG DIALOG LOADONCALL MOVEABLE DISCARDABLE 36, 24, 250, 154
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Âúâåäåòå ìðåæîâà ïàðîëà"
+FONT 8, "MS Shell Dlg"
+{
+ LTEXT "Âúâåäåòå âàøåòî ïîòðåáèòåëñêî èìå è ïàðîëà:", IDC_EXPLAIN, 40, 6, 150, 15
+ LTEXT "Ïðîêñè", -1, 40, 26, 50, 10
+/* LTEXT "Realm", -1, 40, 46, 50, 10 */
+ LTEXT "Ïîòðåáèòåë", -1, 40, 66, 50, 10
+ LTEXT "Ïàðîëà", -1, 40, 86, 50, 10
+ LTEXT "" IDC_PROXY, 80, 26, 150, 14, 0
+ LTEXT "" IDC_REALM, 80, 46, 150, 14, 0
+ EDITTEXT IDC_USERNAME, 80, 66, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP
+ EDITTEXT IDC_PASSWORD, 80, 86, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP | ES_PASSWORD
+ CHECKBOX "&Save this password (Insecure)", IDC_SAVEPASSWORD,
+           80, 106, 150, 12, BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP
+ PUSHBUTTON "OK", IDOK, 98, 126, 56, 14, WS_GROUP | WS_TABSTOP | BS_DEFPUSHBUTTON
+ PUSHBUTTON "Îòìåíè", IDCANCEL, 158, 126, 56, 14, WS_GROUP | WS_TABSTOP
+}
diff --git a/reactos/dll/mpr/mpr_Cs.rc b/reactos/dll/mpr/mpr_Cs.rc
new file mode 100644 (file)
index 0000000..cda57e3
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Czech MPR dll resources
+ * Copyright (C) 2004 Juan Lang
+ * Copyright (C) 2004, 2005 David Kredba
+ *
+ * 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
+ */
+
+LANGUAGE LANG_CZECH, SUBLANG_DEFAULT
+
+STRINGTABLE DISCARDABLE
+{
+    IDS_ENTIRENETWORK "Celá sí»"
+}
+
+IDD_PROXYDLG DIALOG LOADONCALL MOVEABLE DISCARDABLE 36, 24, 250, 154
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Zadání sí»ového hesla"
+FONT 8, "MS Shell Dlg"
+{
+ LTEXT "Prosím zadejte své u¾ivatelské jméno a heslo:", IDC_EXPLAIN, 40, 6, 150, 15
+ LTEXT "Proxy", -1, 40, 26, 50, 10
+/* LTEXT "Realm", -1, 40, 46, 50, 10 */
+ LTEXT "U¾ivatel", -1, 40, 66, 50, 10
+ LTEXT "Heslo", -1, 40, 86, 50, 10
+ LTEXT "" IDC_PROXY, 80, 26, 150, 14, 0
+ LTEXT "" IDC_REALM, 80, 46, 150, 14, 0
+ EDITTEXT IDC_USERNAME, 80, 66, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP
+ EDITTEXT IDC_PASSWORD, 80, 86, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP | ES_PASSWORD
+ CHECKBOX "&Ulo¾it toto heslo (Není bezpeèné) ?", IDC_SAVEPASSWORD,
+           80, 106, 150, 12, BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP
+ PUSHBUTTON "OK", IDOK, 98, 126, 56, 14, WS_GROUP | WS_TABSTOP | BS_DEFPUSHBUTTON
+ PUSHBUTTON "Zru¹it", IDCANCEL, 158, 126, 56, 14, WS_GROUP | WS_TABSTOP
+}
diff --git a/reactos/dll/mpr/mpr_De.rc b/reactos/dll/mpr/mpr_De.rc
new file mode 100644 (file)
index 0000000..a91f7b3
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * MPR dll resources
+ *
+ * Copyright (C) 2004 Henning Gerhardt
+ *
+ * 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
+ */
+
+LANGUAGE LANG_GERMAN, SUBLANG_DEFAULT
+
+STRINGTABLE DISCARDABLE
+{
+    IDS_ENTIRENETWORK "Gesamtes Netzwerk"
+}
+
+IDD_PROXYDLG DIALOG LOADONCALL MOVEABLE DISCARDABLE 36, 24, 250, 154
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Netzwerkkennung eingeben"
+FONT 8, "MS Shell Dlg"
+{
+ LTEXT "Bitte geben Sie Benutzernamen und Kennwort ein:", IDC_EXPLAIN, 40, 6, 150, 15
+ LTEXT "Proxy", -1, 40, 26, 50, 10
+/* LTEXT "Bereich", -1, 40, 46, 50, 10 */
+ LTEXT "Benutzername", -1, 40, 66, 50, 10
+ LTEXT "Kennwort", -1, 40, 86, 50, 10
+ LTEXT "" IDC_PROXY, 80, 26, 150, 14, 0
+ LTEXT "" IDC_REALM, 80, 46, 150, 14, 0
+ EDITTEXT IDC_USERNAME, 80, 66, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP
+ EDITTEXT IDC_PASSWORD, 80, 86, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP | ES_PASSWORD
+ CHECKBOX "Dieses Kennwort speichern (unsicher)", IDC_SAVEPASSWORD,
+          80, 106, 150, 12, BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP
+ PUSHBUTTON "OK", IDOK, 98, 126, 56, 14, WS_GROUP | WS_TABSTOP | BS_DEFPUSHBUTTON
+ PUSHBUTTON "Abbrechen", IDCANCEL, 158, 126, 56, 14, WS_GROUP | WS_TABSTOP
+}
diff --git a/reactos/dll/mpr/mpr_En.rc b/reactos/dll/mpr/mpr_En.rc
new file mode 100644 (file)
index 0000000..461a539
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * MPR dll resources
+ *
+ * Copyright (C) 2004 Juan Lang
+ *
+ * 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
+ */
+
+LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT
+
+STRINGTABLE DISCARDABLE
+{
+    IDS_ENTIRENETWORK "Entire Network"
+}
+
+IDD_PROXYDLG DIALOG LOADONCALL MOVEABLE DISCARDABLE 36, 24, 250, 154
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Enter Network Password"
+FONT 8, "MS Shell Dlg"
+{
+ LTEXT "Please enter your username and password:", IDC_EXPLAIN, 40, 6, 150, 15
+ LTEXT "Proxy", -1, 40, 26, 50, 10
+/* LTEXT "Realm", -1, 40, 46, 50, 10 */
+ LTEXT "User", -1, 40, 66, 50, 10
+ LTEXT "Password", -1, 40, 86, 50, 10
+ LTEXT "" IDC_PROXY, 80, 26, 150, 14, 0
+ LTEXT "" IDC_REALM, 80, 46, 150, 14, 0
+ EDITTEXT IDC_USERNAME, 80, 66, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP
+ EDITTEXT IDC_PASSWORD, 80, 86, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP | ES_PASSWORD
+ CHECKBOX "&Save this password (Insecure)", IDC_SAVEPASSWORD,
+           80, 106, 150, 12, BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP
+ PUSHBUTTON "OK", IDOK, 98, 126, 56, 14, WS_GROUP | WS_TABSTOP | BS_DEFPUSHBUTTON
+ PUSHBUTTON "Cancel", IDCANCEL, 158, 126, 56, 14, WS_GROUP | WS_TABSTOP
+}
diff --git a/reactos/dll/mpr/mpr_Es.rc b/reactos/dll/mpr/mpr_Es.rc
new file mode 100644 (file)
index 0000000..370e3cf
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * MPR dll Spanish resources
+ *
+ * Copyright (C) 2004, 2005 José Manuel Ferrer Ortiz
+ *
+ * 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
+ */
+
+LANGUAGE LANG_SPANISH, SUBLANG_NEUTRAL
+
+STRINGTABLE DISCARDABLE
+{
+    IDS_ENTIRENETWORK "Toda la red"
+}
+
+IDD_PROXYDLG DIALOG LOADONCALL MOVEABLE DISCARDABLE 36, 24, 250, 154
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Introduzca contraseña de red"
+FONT 8, "MS Shell Dlg"
+{
+ LTEXT "Por favor, introduzca su nombre de usuario y contraseña:", IDC_EXPLAIN, 40, 6, 150, 15
+ LTEXT "Proxy", -1, 40, 26, 50, 10
+/* LTEXT "Realm", -1, 40, 46, 50, 10 */
+ LTEXT "Usuario", -1, 40, 66, 50, 10
+ LTEXT "Contraseña", -1, 40, 86, 50, 10
+ LTEXT "" IDC_PROXY, 80, 26, 150, 14, 0
+ LTEXT "" IDC_REALM, 80, 46, 150, 14, 0
+ EDITTEXT IDC_USERNAME, 80, 66, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP
+ EDITTEXT IDC_PASSWORD, 80, 86, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP | ES_PASSWORD
+ CHECKBOX "&Guardar esta contraseña (Inseguro)", IDC_SAVEPASSWORD,
+           80, 106, 150, 12, BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP
+ PUSHBUTTON "Aceptar", IDOK, 98, 126, 56, 14, WS_GROUP | WS_TABSTOP | BS_DEFPUSHBUTTON
+ PUSHBUTTON "Cancelar", IDCANCEL, 158, 126, 56, 14, WS_GROUP | WS_TABSTOP
+}
diff --git a/reactos/dll/mpr/mpr_Fr.rc b/reactos/dll/mpr/mpr_Fr.rc
new file mode 100644 (file)
index 0000000..5e2d999
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * MPR dll resources
+ * French language support
+ *
+ * Copyright (C) 2005 Jonathan Ernst
+ *
+ * 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
+ */
+
+LANGUAGE LANG_FRENCH, SUBLANG_NEUTRAL
+
+STRINGTABLE DISCARDABLE
+{
+    IDS_ENTIRENETWORK "Le réseau entier"
+}
+
+IDD_PROXYDLG DIALOG LOADONCALL MOVEABLE DISCARDABLE 36, 24, 250, 154
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Entrez le mot de passe réseau"
+FONT 8, "MS Shell Dlg"
+{
+ LTEXT "Veuillez saisir votre nom d'utilisateur et votre mot de passe:", IDC_EXPLAIN, 40, 6, 150, 15
+ LTEXT "Proxy", -1, 40, 26, 50, 10
+/* LTEXT "Realm", -1, 40, 46, 50, 10 */
+ LTEXT "Utilisateur", -1, 40, 66, 50, 10
+ LTEXT "Mot de passe", -1, 40, 86, 50, 10
+ LTEXT "" IDC_PROXY, 80, 26, 150, 14, 0
+ LTEXT "" IDC_REALM, 80, 46, 150, 14, 0
+ EDITTEXT IDC_USERNAME, 80, 66, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP
+ EDITTEXT IDC_PASSWORD, 80, 86, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP | ES_PASSWORD
+ CHECKBOX "&Enregistrer ce mot de passe (risqué)", IDC_SAVEPASSWORD,
+           80, 106, 150, 12, BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP
+ PUSHBUTTON "OK", IDOK, 98, 126, 56, 14, WS_GROUP | WS_TABSTOP | BS_DEFPUSHBUTTON
+ PUSHBUTTON "Annuler", IDCANCEL, 158, 126, 56, 14, WS_GROUP | WS_TABSTOP
+}
diff --git a/reactos/dll/mpr/mpr_Hu.rc b/reactos/dll/mpr/mpr_Hu.rc
new file mode 100644 (file)
index 0000000..df01bb3
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * MPR dll resources
+ *
+ * Copyright (C) 2004 Juan Lang
+ * Copyright (C) 2005 Gergely Risko
+ *
+ * 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
+ */
+
+LANGUAGE LANG_HUNGARIAN, SUBLANG_NEUTRAL
+
+STRINGTABLE DISCARDABLE
+{
+    IDS_ENTIRENETWORK "Teljes álózat"
+}
+
+IDD_PROXYDLG DIALOG LOADONCALL MOVEABLE DISCARDABLE 36, 24, 250, 154
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Hálózati Bejelentkezés"
+FONT 8, "MS Shell Dlg"
+{
+ LTEXT "Írd be a felhasználó nevet és jelszót:", IDC_EXPLAIN, 40, 6, 150, 15
+ LTEXT "Proxy", -1, 40, 26, 50, 10
+/* LTEXT "Realm", -1, 40, 46, 50, 10 */
+ LTEXT "Felhasználó", -1, 40, 66, 50, 10
+ LTEXT "Jelszó", -1, 40, 86, 50, 10
+ LTEXT "" IDC_PROXY, 80, 26, 150, 14, 0
+ LTEXT "" IDC_REALM, 80, 46, 150, 14, 0
+ EDITTEXT IDC_USERNAME, 80, 66, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP
+ EDITTEXT IDC_PASSWORD, 80, 86, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP | ES_PASSWORD
+ CHECKBOX "Jelszó &mentése (Nem biztonságos)", IDC_SAVEPASSWORD,
+           80, 106, 150, 12, BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP
+ PUSHBUTTON "OK", IDOK, 98, 126, 56, 14, WS_GROUP | WS_TABSTOP | BS_DEFPUSHBUTTON
+ PUSHBUTTON "Mégse", IDCANCEL, 158, 126, 56, 14, WS_GROUP | WS_TABSTOP
+}
diff --git a/reactos/dll/mpr/mpr_It.rc b/reactos/dll/mpr/mpr_It.rc
new file mode 100644 (file)
index 0000000..1a73372
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * MPR dll Italian resources
+ *
+ * Copyright 2004 Ivan Leo Puoti
+ *
+ * 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
+ */
+
+LANGUAGE LANG_ITALIAN, SUBLANG_DEFAULT
+
+STRINGTABLE DISCARDABLE
+{
+    IDS_ENTIRENETWORK "Tutta la rete"
+}
diff --git a/reactos/dll/mpr/mpr_Ja.rc b/reactos/dll/mpr/mpr_Ja.rc
new file mode 100644 (file)
index 0000000..9481b09
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * MPR dll resources
+ *
+ * Copyright (C) 2004 Hajime Segawa
+ *
+ * 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
+ */
+
+LANGUAGE LANG_JAPANESE, SUBLANG_NEUTRAL
+
+STRINGTABLE DISCARDABLE
+{
+    IDS_ENTIRENETWORK "\83l\83b\83g\83\8f\81[\83N\91S\91Ì"
+}
+
+IDD_PROXYDLG DIALOG LOADONCALL MOVEABLE DISCARDABLE 36, 24, 250, 154
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "\83l\83b\83g\83\8f\81[\83N\83p\83X\83\8f\81[\83h\82Ì\93ü\97Í"
+FONT 9, "MS UI Gothic"
+{
+ LTEXT "\83\86\81[\83U\81[\96¼\82Æ\83p\83X\83\8f\81[\83h\82ð\93ü\97Í\82µ\82Ä\89º\82³\82¢:", IDC_EXPLAIN, 40, 6, 150, 15
+ LTEXT "\83v\83\8d\83L\83V", -1, 40, 26, 50, 10
+/* LTEXT "Realm", -1, 40, 46, 50, 10 */
+ LTEXT "\83\86\81[\83U\96¼", -1, 40, 66, 50, 10
+ LTEXT "\83p\83X\83\8f\81[\83h", -1, 40, 86, 50, 10
+ LTEXT "" IDC_PROXY, 80, 26, 150, 14, 0
+ LTEXT "" IDC_REALM, 80, 46, 150, 14, 0
+ EDITTEXT IDC_USERNAME, 80, 66, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP
+ EDITTEXT IDC_PASSWORD, 80, 86, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP | ES_PASSWORD
+ CHECKBOX "\83p\83X\83\8f\81[\83h\82ð\95Û\91\82·\82é(&S) (\88À\91S\82Å\82Í\82 \82è\82Ü\82¹\82ñ)", IDC_SAVEPASSWORD,
+           80, 106, 150, 12, BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP
+ PUSHBUTTON "OK", IDOK, 98, 126, 56, 14, WS_GROUP | WS_TABSTOP | BS_DEFPUSHBUTTON
+ PUSHBUTTON "\83L\83\83\83\93\83Z\83\8b", IDCANCEL, 158, 126, 56, 14, WS_GROUP | WS_TABSTOP
+}
diff --git a/reactos/dll/mpr/mpr_Ko.rc b/reactos/dll/mpr/mpr_Ko.rc
new file mode 100644 (file)
index 0000000..d448122
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * MPR dll resources
+ *
+ * Copyright (C) 2004 Juan Lang
+ * Copyright 2005 YunSong Hwang
+ *
+ * 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
+ */
+
+LANGUAGE LANG_KOREAN, SUBLANG_DEFAULT
+
+STRINGTABLE DISCARDABLE
+{
+    IDS_ENTIRENETWORK "Àüü ³×Æ®¿öÅ©"
+}
+
+IDD_PROXYDLG DIALOG LOADONCALL MOVEABLE DISCARDABLE 36, 24, 250, 154
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "³×Æ®¿öÅ© ¾ÏÈ£ ÀÔ·Â"
+FONT 8, "MS Shell Dlg"
+{
+ LTEXT "´ç½ÅÀÇ »ç¿ëÀÚÀ̸§°ú ¾ÏÈ£¸¦ ÀÔ·ÂÇϽÿÀ:", IDC_EXPLAIN, 40, 6, 150, 15
+ LTEXT "ÇÁ·Ï½Ã", -1, 40, 26, 50, 10
+/* LTEXT "Realm", -1, 40, 46, 50, 10 */
+ LTEXT "»ç¿ëÀÚ", -1, 40, 66, 50, 10
+ LTEXT "¾ÏÈ£", -1, 40, 86, 50, 10
+ LTEXT "" IDC_PROXY, 80, 26, 150, 14, 0
+ LTEXT "" IDC_REALM, 80, 46, 150, 14, 0
+ EDITTEXT IDC_USERNAME, 80, 66, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP
+ EDITTEXT IDC_PASSWORD, 80, 86, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP | ES_PASSWORD
+ CHECKBOX "¾ÏÈ£ ÀúÀå(&S) (Insecure)", IDC_SAVEPASSWORD,
+           80, 106, 150, 12, BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP
+ PUSHBUTTON "È®ÀÎ", IDOK, 98, 126, 56, 14, WS_GROUP | WS_TABSTOP | BS_DEFPUSHBUTTON
+ PUSHBUTTON "Ãë¼Ò", IDCANCEL, 158, 126, 56, 14, WS_GROUP | WS_TABSTOP
+}
diff --git a/reactos/dll/mpr/mpr_Nl.rc b/reactos/dll/mpr/mpr_Nl.rc
new file mode 100644 (file)
index 0000000..a0569cc
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Dutch resources for MPR
+ *
+ * Copyright (C) 2004 Hans Leidekker
+ *
+ * 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
+ */
+
+LANGUAGE LANG_DUTCH, SUBLANG_DEFAULT
+
+STRINGTABLE DISCARDABLE
+{
+    IDS_ENTIRENETWORK "Gehele netwerk"
+}
diff --git a/reactos/dll/mpr/mpr_No.rc b/reactos/dll/mpr/mpr_No.rc
new file mode 100644 (file)
index 0000000..60b7acd
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * MPR dll resources for Norwegian Bokmål
+ *
+ * Copyright (C) 2005 Alexander N. Sørnes <alex@thehandofagony.com>
+ *
+ * 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
+ */
+
+LANGUAGE LANG_NORWEGIAN, SUBLANG_NORWEGIAN_BOKMAL
+
+STRINGTABLE DISCARDABLE
+{
+    IDS_ENTIRENETWORK "Hele nettverket"
+}
+
+IDD_PROXYDLG DIALOG LOADONCALL MOVEABLE DISCARDABLE 36, 24, 250, 154
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Skriv inn nettverkspassord"
+FONT 8, "MS Shell Dlg"
+{
+ LTEXT "Skriv inn brukernavnet og passordet ditt:", IDC_EXPLAIN, 40, 6, 150, 15
+ LTEXT "Mellomtjener", -1, 40, 26, 50, 10
+/* LTEXT "Område", -1, 40, 46, 50, 10 */
+ LTEXT "Bruker", -1, 40, 66, 50, 10
+ LTEXT "Passord", -1, 40, 86, 50, 10
+ LTEXT "" IDC_PROXY, 80, 26, 150, 14, 0
+ LTEXT "" IDC_REALM, 80, 46, 150, 14, 0
+ EDITTEXT IDC_USERNAME, 80, 66, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP
+ EDITTEXT IDC_PASSWORD, 80, 86, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP | ES_PASSWORD
+ CHECKBOX "Lagre dette pa&ssordet (usikkert)", IDC_SAVEPASSWORD,
+           80, 106, 150, 12, BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP
+ PUSHBUTTON "OK", IDOK, 98, 126, 56, 14, WS_GROUP | WS_TABSTOP | BS_DEFPUSHBUTTON
+ PUSHBUTTON "Avbryt", IDCANCEL, 158, 126, 56, 14, WS_GROUP | WS_TABSTOP
+}
diff --git a/reactos/dll/mpr/mpr_Pt.rc b/reactos/dll/mpr/mpr_Pt.rc
new file mode 100644 (file)
index 0000000..e22c49c
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * MPR dll resources
+ *
+ * Copyright (C) 2004 Marcelo Duarte
+ *
+ * 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
+ */
+
+LANGUAGE LANG_PORTUGUESE, SUBLANG_DEFAULT
+
+STRINGTABLE DISCARDABLE
+{
+    IDS_ENTIRENETWORK "Toda a rede"
+}
+
+IDD_PROXYDLG DIALOG LOADONCALL MOVEABLE DISCARDABLE 36, 24, 250, 154
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Entre a senha da rede"
+FONT 8, "MS Shell Dlg"
+{
+ LTEXT "Por favor, entre como o nome de usuário e a senha:", IDC_EXPLAIN, 40, 6, 150, 15
+ LTEXT "Proxy", -1, 40, 26, 50, 10
+/* LTEXT "Realm", -1, 40, 46, 50, 10 */
+ LTEXT "Usuário", -1, 40, 66, 50, 10
+ LTEXT "Senha", -1, 40, 86, 50, 10
+ LTEXT "" IDC_PROXY, 80, 26, 150, 14, 0
+ LTEXT "" IDC_REALM, 80, 46, 150, 14, 0
+ EDITTEXT IDC_USERNAME, 80, 66, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP
+ EDITTEXT IDC_PASSWORD, 80, 86, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP | ES_PASSWORD
+ CHECKBOX "&Salvar esta senha (Inseguro)", IDC_SAVEPASSWORD,
+           80, 106, 150, 12, BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP
+ PUSHBUTTON "OK", IDOK, 98, 126, 56, 14, WS_GROUP | WS_TABSTOP | BS_DEFPUSHBUTTON
+ PUSHBUTTON "Cancelar", IDCANCEL, 158, 126, 56, 14, WS_GROUP | WS_TABSTOP
+}
diff --git a/reactos/dll/mpr/mpr_Ru.rc b/reactos/dll/mpr/mpr_Ru.rc
new file mode 100644 (file)
index 0000000..bd821b4
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * MPR dll resources
+ *
+ * Copyright (C) 2005 Mikhail Y. Zvyozdochkin
+ *
+ * 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
+ */
+
+LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT
+
+STRINGTABLE DISCARDABLE
+{
+    IDS_ENTIRENETWORK "Âñÿ ñåòü"
+}
+
+IDD_PROXYDLG DIALOG LOADONCALL MOVEABLE DISCARDABLE 36, 24, 250, 154
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Ââåäèòå Ñåòåâîé Ïàðîëü"
+FONT 8, "MS Shell Dlg"
+{
+ LTEXT "Ïîæàëóéñòà, ââåäèòå Âàøè èìÿ è ïàðîëü:", IDC_EXPLAIN, 40, 6, 150, 15
+ LTEXT "Ïðîêñè-ñåðâåð", -1, 40, 26, 50, 10
+/* LTEXT "Realm", -1, 40, 46, 50, 10 */
+ LTEXT "Èìÿ ïîëüçîâàòåëÿ", -1, 40, 66, 50, 10
+ LTEXT "Ïàðîëü", -1, 40, 86, 50, 10
+ LTEXT "" IDC_PROXY, 80, 26, 150, 14, 0
+ LTEXT "" IDC_REALM, 80, 46, 150, 14, 0
+ EDITTEXT IDC_USERNAME, 80, 66, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP
+ EDITTEXT IDC_PASSWORD, 80, 86, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP | ES_PASSWORD
+ CHECKBOX "&Ñîõðàíèòü ïàðîëü (íåáåçîïàñíî)", IDC_SAVEPASSWORD,
+           80, 106, 150, 12, BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP
+ PUSHBUTTON "OK", IDOK, 98, 126, 56, 14, WS_GROUP | WS_TABSTOP | BS_DEFPUSHBUTTON
+ PUSHBUTTON "Îòìåíà", IDCANCEL, 158, 126, 56, 14, WS_GROUP | WS_TABSTOP
+}
diff --git a/reactos/dll/mpr/mpr_Sv.rc b/reactos/dll/mpr/mpr_Sv.rc
new file mode 100644 (file)
index 0000000..b1ee8da
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * MPR dll Swedish resources
+ *
+ * Copyright (C) 2005 Andreas Bjerkeholt
+ *
+ * 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
+ */
+
+LANGUAGE LANG_SWEDISH, SUBLANG_DEFAULT
+
+STRINGTABLE DISCARDABLE
+{
+    IDS_ENTIRENETWORK "Hela nätverket"
+}
+
+IDD_PROXYDLG DIALOG LOADONCALL MOVEABLE DISCARDABLE 36, 24, 250, 154
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Ange nätverkslösenord"
+FONT 8, "MS Shell Dlg"
+{
+ LTEXT "Ange ditt användarnamn och lösenord:", IDC_EXPLAIN, 40, 6, 150, 15
+ LTEXT "Proxy", -1, 40, 26, 50, 10
+/* LTEXT "Realm", -1, 40, 46, 50, 10 */
+ LTEXT "Användare", -1, 40, 66, 50, 10
+ LTEXT "Lösenord", -1, 40, 86, 50, 10
+ LTEXT "" IDC_PROXY, 80, 26, 150, 14, 0
+ LTEXT "" IDC_REALM, 80, 46, 150, 14, 0
+ EDITTEXT IDC_USERNAME, 80, 66, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP
+ EDITTEXT IDC_PASSWORD, 80, 86, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP | ES_PASSWORD
+ CHECKBOX "&Spara detta lösenord (Osäkert)", IDC_SAVEPASSWORD,
+           80, 106, 150, 12, BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP
+ PUSHBUTTON "OK", IDOK, 98, 126, 56, 14, WS_GROUP | WS_TABSTOP | BS_DEFPUSHBUTTON
+ PUSHBUTTON "Avbryt", IDCANCEL, 158, 126, 56, 14, WS_GROUP | WS_TABSTOP
+}
diff --git a/reactos/dll/mpr/mpr_Uk.rc b/reactos/dll/mpr/mpr_Uk.rc
new file mode 100644 (file)
index 0000000..c5181f1
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * MPR dll resources (Ukrainian)
+ *
+ * Copyright 2006 Artem Reznikov
+ *
+ * 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
+ */
+
+LANGUAGE LANG_UKRAINIAN, SUBLANG_DEFAULT
+
+STRINGTABLE DISCARDABLE
+{
+    IDS_ENTIRENETWORK "Âñÿ Ìåðåæà"
+}
+
+IDD_PROXYDLG DIALOG LOADONCALL MOVEABLE DISCARDABLE 36, 24, 250, 154
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Ââåä³òü Ìåðåæíèé Ïàðîëü"
+FONT 8, "MS Shell Dlg"
+{
+ LTEXT "Áóäü ëàñêà, ââåä³òü Âàø³ ³ì'ÿ òà ïàðîëü:", IDC_EXPLAIN, 40, 6, 150, 15
+ LTEXT "Ïðîêñ³", -1, 40, 26, 50, 10
+/* LTEXT "Realm", -1, 40, 46, 50, 10 */
+ LTEXT "Êîðèñòóâà÷", -1, 40, 66, 50, 10
+ LTEXT "Ïàðîëü", -1, 40, 86, 50, 10
+ LTEXT "" IDC_PROXY, 80, 26, 150, 14, 0
+ LTEXT "" IDC_REALM, 80, 46, 150, 14, 0
+ EDITTEXT IDC_USERNAME, 80, 66, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP
+ EDITTEXT IDC_PASSWORD, 80, 86, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP | ES_PASSWORD
+ CHECKBOX "&Çáåðåãòè öåé ïàðîëü (íåáåçïå÷íî)", IDC_SAVEPASSWORD,
+           80, 106, 150, 12, BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP
+ PUSHBUTTON "OK", IDOK, 98, 126, 56, 14, WS_GROUP | WS_TABSTOP | BS_DEFPUSHBUTTON
+ PUSHBUTTON "Ñêàñóâàòè", IDCANCEL, 158, 126, 56, 14, WS_GROUP | WS_TABSTOP
+}
diff --git a/reactos/dll/mpr/mpr_main.c b/reactos/dll/mpr/mpr_main.c
new file mode 100644 (file)
index 0000000..b944649
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * MPR undocumented functions
+ *
+ * Copyright 1999 Ulrich Weigand
+ *
+ * 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 <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "objbase.h"
+#include "winnetwk.h"
+#include "wine/debug.h"
+#include "wnetpriv.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(mpr);
+
+ /*
+  * FIXME: The following routines should use a private heap ...
+  */
+
+/*****************************************************************
+ *  @  [MPR.22]
+ */
+LPVOID WINAPI MPR_Alloc( DWORD dwSize )
+{
+    return HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwSize );
+}
+
+/*****************************************************************
+ *  @  [MPR.23]
+ */
+LPVOID WINAPI MPR_ReAlloc( LPVOID lpSrc, DWORD dwSize )
+{
+    if ( lpSrc )
+        return HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, lpSrc, dwSize );
+    else
+        return HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwSize );
+}
+
+/*****************************************************************
+ *  @  [MPR.24]
+ */
+BOOL WINAPI MPR_Free( LPVOID lpMem )
+{
+    if ( lpMem )
+        return HeapFree( GetProcessHeap(), 0, lpMem );
+    else
+        return FALSE;
+}
+
+/*****************************************************************
+ *  @  [MPR.25]
+ */
+BOOL WINAPI _MPR_25( LPBYTE lpMem, INT len )
+{
+    FIXME( "(%p, %d): stub\n", lpMem, len );
+
+    return FALSE;
+}
+
+/*****************************************************************
+ *  DllCanUnloadNow  [MPR.@]
+ */
+HRESULT WINAPI DllCanUnloadNow(void)
+{
+    FIXME("Stub\n");
+    return S_OK;
+}
+
+/*****************************************************************
+ *  DllMain  [MPR.init]
+ */
+BOOL WINAPI DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+    switch (fdwReason) {
+        case DLL_PROCESS_ATTACH:
+            DisableThreadLibraryCalls( hinstDLL );
+            wnetInit(hinstDLL);
+            break;
+
+        case DLL_PROCESS_DETACH:
+            wnetFree();
+            break;
+    }
+    return TRUE;
+}
diff --git a/reactos/dll/mpr/mprres.h b/reactos/dll/mpr/mprres.h
new file mode 100644 (file)
index 0000000..fa9cf8b
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2004 Juan Lang
+ *
+ * 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
+ */
+#ifndef __WINE_MPRRES_H__
+#define __WINE_MPRRES_H__
+
+#define IDS_ENTIRENETWORK 1
+
+#define IDD_PROXYDLG     0x400
+
+#define IDC_PROXY        0x401
+#define IDC_REALM        0x402
+#define IDC_USERNAME     0x403
+#define IDC_PASSWORD     0x404
+#define IDC_SAVEPASSWORD 0x405
+#define IDC_EXPLAIN      0x406
+
+#endif /* ndef __WINE_MPRRES_H__ */
diff --git a/reactos/dll/mpr/multinet.c b/reactos/dll/mpr/multinet.c
new file mode 100644 (file)
index 0000000..aa8cfb8
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * MPR Multinet functions
+ *
+ * Copyright 1999 Ulrich Weigand
+ *
+ * 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 <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winnetwk.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(mpr);
+
+
+/*****************************************************************
+ *     MultinetGetConnectionPerformanceA [MPR.@]
+ *
+ * RETURNS
+ *    Success: NO_ERROR
+ *    Failure: ERROR_NOT_SUPPORTED, ERROR_NOT_CONNECTED,
+ *             ERROR_NO_NET_OR_BAD_PATH, ERROR_BAD_DEVICE,
+ *             ERROR_BAD_NET_NAME, ERROR_INVALID_PARAMETER,
+ *             ERROR_NO_NETWORK, ERROR_EXTENDED_ERROR
+ */
+DWORD WINAPI MultinetGetConnectionPerformanceA(
+       LPNETRESOURCEA lpNetResource,
+       LPNETCONNECTINFOSTRUCT lpNetConnectInfoStruct )
+{
+    FIXME( "(%p, %p): stub\n", lpNetResource, lpNetConnectInfoStruct );
+
+    SetLastError(WN_NO_NETWORK);
+    return WN_NO_NETWORK;
+}
+
+/*****************************************************************
+ *     MultinetGetConnectionPerformanceW [MPR.@]
+ */
+DWORD WINAPI MultinetGetConnectionPerformanceW(
+       LPNETRESOURCEW lpNetResource,
+       LPNETCONNECTINFOSTRUCT lpNetConnectInfoStruct )
+{
+    FIXME( "(%p, %p): stub\n", lpNetResource, lpNetConnectInfoStruct );
+
+    SetLastError(WN_NO_NETWORK);
+    return WN_NO_NETWORK;
+}
+
+/*****************************************************************
+ *  MultinetGetErrorTextA [MPR.@]
+ */
+DWORD WINAPI MultinetGetErrorTextA( DWORD x, DWORD y, DWORD z )
+{
+    FIXME( "(%lx, %lx, %lx): stub\n", x, y, z );
+    return 0;
+}
+
+/*****************************************************************
+ *  MultinetGetErrorTextW [MPR.@]
+ */
+DWORD WINAPI MultinetGetErrorTextW( DWORD x, DWORD y, DWORD z )
+{
+    FIXME( "(%lx, %lx, %lx ): stub\n", x, y, z );
+    return 0;
+}
+
diff --git a/reactos/dll/mpr/netspi.h b/reactos/dll/mpr/netspi.h
new file mode 100644 (file)
index 0000000..7695584
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * MPR - Network provider services
+ * Warning: this file apparently existed as part of the Win98 DDK.  Some of
+ * the declarations in it conflict with those in the Platform SDK's npapi.h,
+ * therefore this header was made private.  Don't try to include both headers.
+ *
+ * Copyright (C) 1999 Ulrich Weigand
+ *
+ * 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
+ */
+
+#ifndef _NETSPI_H_
+#define _NETSPI_H_
+
+#include "windef.h"
+#include "winnetwk.h"
+
+/*
+ * Note: The Unicode variants of all these routines/structures
+ *       apparently don't exist, at least not in Win95 ...
+ */
+
+#define HPROVIDER LPVOID
+typedef HPROVIDER *PHPROVIDER;
+
+typedef struct
+{
+    DWORD  cbStructure;
+    HWND   hwndOwner;
+    LPCSTR lpResource;
+    LPSTR  lpUsername;
+    DWORD  cbUsername;
+    LPSTR  lpPassword;
+    DWORD  cbPassword;
+    LPSTR  lpOrgUnit;
+    DWORD  cbOrgUnit;
+    LPCSTR lpOUTitle;
+    LPCSTR lpExplainText;
+    LPCSTR lpDefaultUserName;
+    DWORD  dwFlags;
+
+} AUTHDLGSTRUCTA, *LPAUTHDLGSTRUCTA;
+
+DECL_WINELIB_TYPE_AW(AUTHDLGSTRUCT)
+DECL_WINELIB_TYPE_AW(LPAUTHDLGSTRUCT)
+
+#define AUTHDLG_ENABLECACHE       0x00000001
+#define AUTHDLG_CHECKCACHE        0x00000002
+#define AUTHDLG_CACHEINVALID      0x00000004
+#define AUTHDLG_USE_DEFAULT_NAME  0x00000008
+#define AUTHDLG_CHECKDEFAULT_NAME 0x00000010
+#define AUTHDLG_LOGON             0x00000020
+
+#define AUTHDLG_ENABLECACHE       0x00000001
+#define AUTHDLG_CHECKCACHE        0x00000002
+#define AUTHDLG_CACHEINVALID      0x00000004
+#define AUTHDLG_USE_DEFAULT_NAME  0x00000008
+#define AUTHDLG_CHECKDEFAULT_NAME 0x00000010
+#define AUTHDLG_LOGON             0x00000020
+
+DWORD WINAPI NPSAuthenticationDialogA(LPAUTHDLGSTRUCTA);
+#define      NPSAuthenticationDialog WINELIB_NAME_AW(NPSAuthenticationDialog)
+DWORD WINAPI NPSGetProviderHandleA(PHPROVIDER);
+#define      NPSGetProviderHandle WINELIB_NAME_AW(NPSGetProviderHandle)
+DWORD WINAPI NPSGetProviderNameA(HPROVIDER,LPCSTR *);
+#define      NPSGetProviderName WINELIB_NAME_AW(NPSGetProviderName)
+DWORD WINAPI NPSGetSectionNameA(HPROVIDER,LPCSTR *lpszSectionName);
+#define      NPSGetSectionName WINELIB_NAME_AW(NPSGetSectionName)
+DWORD WINAPI NPSSetExtendedErrorA(DWORD,LPSTR);
+#define      NPSSetExtendedError WINELIB_NAME_AW(NPSSetExtendedError)
+VOID  WINAPI NPSSetCustomTextA(LPSTR);
+#define      NPSSetCustomText WINELIB_NAME_AW(NPSSetCustomText)
+DWORD WINAPI NPSCopyStringA(LPCSTR,LPVOID,LPDWORD);
+#define      NPSCopyString WINELIB_NAME_AW(NPSCopyString)
+DWORD WINAPI NPSDeviceGetNumberA(LPSTR,LPDWORD,LPDWORD);
+#define      NPSDeviceGetNumber WINELIB_NAME_AW(NPSDeviceGetNumber)
+DWORD WINAPI NPSDeviceGetStringA(DWORD,DWORD,LPSTR,LPDWORD);
+#define      NPSDeviceGetString WINELIB_NAME_AW(NPSDeviceGetString)
+
+
+enum NOTIFYTYPE { NotifyAddConnection,
+                  NotifyCancelConnection,
+                  NotifyGetConnectionPerformance };
+
+#define NOTIFY_PRE              0x00
+#define NOTIFY_POST             0x01
+
+typedef struct
+{
+    DWORD   cbStructure;
+    DWORD   dwNotifyStatus;
+    DWORD   dwOperationStatus;
+    LPVOID  lpNPContext;
+
+} NOTIFYINFO, *LPNOTIFYINFO;
+
+typedef struct
+{
+    DWORD        cbStructure;
+    HWND         hwndOwner;
+    NETRESOURCEA NetResource;
+    DWORD        dwAddFlags;
+    LPSTR        lpAccessName;
+    LPDWORD      lpBufferSize;
+    DWORD        dwResult;
+    DWORD        dwAddContext;
+
+} NOTIFYADDA, *LPNOTIFYADDA;
+
+#define CONNECT_CTXT_RESTORE   0x00000001
+#define CONNECT_CTXT_GLOBAL    0x00000002
+#define CONNECT_CTXT_PROVIDER  0x00000004
+#define CONNECT_CTXT_SINGLE    0x00000008
+
+typedef struct
+{
+    DWORD  cbStructure;
+    LPSTR  lpName;
+    LPSTR  lpProvider;
+    DWORD  dwFlags;
+    BOOL   fForce;
+
+} NOTIFYCANCELA, *LPNOTIFYCANCELA;
+
+typedef struct
+{
+    DWORD                  cbStructure;
+    LPSTR                  lpRemoteName;
+    LPSTR                  lpProviderName;
+    LPNETCONNECTINFOSTRUCT lpNetConnectInfo;
+
+} NOTIFYPERFORMANCEA, *LPNOTIFYPERFORMANCEA;
+
+typedef DWORD (CALLBACK *NOTIFYCALLBACK)(LPNOTIFYINFO,LPVOID);
+
+DWORD  WINAPI NPSNotifyRegisterA(enum NOTIFYTYPE,NOTIFYCALLBACK);
+#define       NPSNotifyRegister WINELIB_NAME_AW(NPSNotifyRegister)
+LPVOID WINAPI NPSNotifyGetContextA(NOTIFYCALLBACK);
+#define       NPSNotifyGetContext WINELIB_NAME_AW(NPSNotifyGetContext)
+
+#endif /* _NETSPI_H_ */
diff --git a/reactos/dll/mpr/nps.c b/reactos/dll/mpr/nps.c
new file mode 100644 (file)
index 0000000..7fe1556
--- /dev/null
@@ -0,0 +1,213 @@
+/*
+ * MPR Network Provider Services functions
+ *
+ * Copyright 1999 Ulrich Weigand
+ * Copyright 2004 Mike McCormack for CodeWeavers Inc.
+ *
+ * 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 "windef.h"
+#include "winbase.h"
+#include "winuser.h"
+#include "netspi.h"
+#include "wine/debug.h"
+#include "winerror.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(mpr);
+
+#include "wine/unicode.h"
+
+#include "mprres.h"
+
+/***********************************************************************
+ *         NPS_ProxyPasswordDialog
+ */
+static INT_PTR WINAPI NPS_ProxyPasswordDialog(
+    HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+    HWND hitem;
+    LPAUTHDLGSTRUCTA lpAuthDlgStruct;
+
+    if( uMsg == WM_INITDIALOG )
+    {
+        TRACE("WM_INITDIALOG (%08lx)\n", lParam);
+
+        /* save the parameter list */
+        lpAuthDlgStruct = (LPAUTHDLGSTRUCTA) lParam;
+        SetWindowLongPtrW( hdlg, GWLP_USERDATA, lParam );
+
+        if( lpAuthDlgStruct->lpExplainText )
+        {
+            hitem = GetDlgItem( hdlg, IDC_EXPLAIN );
+            SetWindowTextA( hitem, lpAuthDlgStruct->lpExplainText );
+        }
+
+        /* extract the Realm from the proxy response and show it */
+        if( lpAuthDlgStruct->lpResource )
+        {
+            hitem = GetDlgItem( hdlg, IDC_REALM );
+            SetWindowTextA( hitem, lpAuthDlgStruct->lpResource );
+        }
+
+        return TRUE;
+    }
+
+    lpAuthDlgStruct = (LPAUTHDLGSTRUCTA) GetWindowLongPtrW( hdlg, GWLP_USERDATA );
+
+    switch( uMsg )
+    {
+    case WM_COMMAND:
+        if( wParam == IDOK )
+        {
+            WCHAR username[0x20], password[0x20];
+
+            username[0] = 0;
+            hitem = GetDlgItem( hdlg, IDC_USERNAME );
+            if( hitem )
+                GetWindowTextA( hitem, lpAuthDlgStruct->lpUsername, lpAuthDlgStruct->cbUsername );
+            
+            password[0] = 0;
+            hitem = GetDlgItem( hdlg, IDC_PASSWORD );
+            if( hitem )
+                GetWindowTextA( hitem, lpAuthDlgStruct->lpPassword, lpAuthDlgStruct->cbPassword );
+
+            EndDialog( hdlg, WN_SUCCESS );
+            return TRUE;
+        }
+        if( wParam == IDCANCEL )
+        {
+            EndDialog( hdlg, WN_CANCEL );
+            return TRUE;
+        }
+        break;
+    }
+    return FALSE;
+}
+
+/*****************************************************************
+ *  NPSAuthenticationDialogA [MPR.@]
+ */
+DWORD WINAPI NPSAuthenticationDialogA( LPAUTHDLGSTRUCTA lpAuthDlgStruct )
+{
+    HMODULE hwininet = GetModuleHandleA( "mpr.dll" );
+
+    TRACE("%p\n", lpAuthDlgStruct);
+
+    if( !lpAuthDlgStruct )
+        return WN_BAD_POINTER;
+    if( lpAuthDlgStruct->cbStructure < sizeof *lpAuthDlgStruct )
+        return WN_BAD_POINTER;
+
+    TRACE("%s %s %s\n",lpAuthDlgStruct->lpResource,
+          lpAuthDlgStruct->lpOUTitle, lpAuthDlgStruct->lpExplainText);
+
+    return DialogBoxParamW( hwininet, MAKEINTRESOURCEW( IDD_PROXYDLG ),
+             lpAuthDlgStruct->hwndOwner, NPS_ProxyPasswordDialog, 
+             (LPARAM) lpAuthDlgStruct );
+}
+
+/*****************************************************************
+ *  NPSGetProviderHandleA [MPR.@]
+ */
+DWORD WINAPI NPSGetProviderHandleA( PHPROVIDER phProvider )
+{
+    FIXME( "(%p): stub\n", phProvider );
+    return WN_NOT_SUPPORTED;
+}
+
+/*****************************************************************
+ *  NPSGetProviderNameA [MPR.@]
+ */
+DWORD WINAPI NPSGetProviderNameA( HPROVIDER hProvider, LPCSTR *lpszProviderName )
+{
+    FIXME( "(%p, %p): stub\n", hProvider, lpszProviderName );
+    return WN_NOT_SUPPORTED;
+}
+
+/*****************************************************************
+ *  NPSGetSectionNameA [MPR.@]
+ */
+DWORD WINAPI NPSGetSectionNameA( HPROVIDER hProvider, LPCSTR *lpszSectionName )
+{
+    FIXME( "(%p, %p): stub\n", hProvider, lpszSectionName );
+    return WN_NOT_SUPPORTED;
+}
+
+/*****************************************************************
+ *  NPSSetExtendedErrorA [MPR.@]
+ */
+DWORD WINAPI NPSSetExtendedErrorA( DWORD NetSpecificError, LPSTR lpExtendedErrorText )
+{
+    FIXME( "(%08lx, %s): stub\n", NetSpecificError, debugstr_a(lpExtendedErrorText) );
+    return WN_NOT_SUPPORTED;
+}
+
+/*****************************************************************
+ *  NPSSetCustomTextA [MPR.@]
+ */
+VOID WINAPI NPSSetCustomTextA( LPSTR lpCustomErrorText )
+{
+    FIXME( "(%s): stub\n", debugstr_a(lpCustomErrorText) );
+}
+
+/*****************************************************************
+ *  NPSCopyStringA [MPR.@]
+ */
+DWORD WINAPI NPSCopyStringA( LPCSTR lpString, LPVOID lpBuffer, LPDWORD lpdwBufferSize )
+{
+    FIXME( "(%s, %p, %p): stub\n", debugstr_a(lpString), lpBuffer, lpdwBufferSize );
+    return WN_NOT_SUPPORTED;
+}
+
+/*****************************************************************
+ *  NPSDeviceGetNumberA [MPR.@]
+ */
+DWORD WINAPI NPSDeviceGetNumberA( LPSTR lpLocalName, LPDWORD lpdwNumber, LPDWORD lpdwType )
+{
+    FIXME( "(%s, %p, %p): stub\n", debugstr_a(lpLocalName), lpdwNumber, lpdwType );
+    return WN_NOT_SUPPORTED;
+}
+
+/*****************************************************************
+ *  NPSDeviceGetStringA [MPR.@]
+ */
+DWORD WINAPI NPSDeviceGetStringA( DWORD dwNumber, DWORD dwType, LPSTR lpLocalName, LPDWORD lpdwBufferSize )
+{
+    FIXME( "(%ld, %ld, %p, %p): stub\n", dwNumber, dwType, lpLocalName, lpdwBufferSize );
+    return WN_NOT_SUPPORTED;
+}
+
+/*****************************************************************
+ *  NPSNotifyRegisterA [MPR.@]
+ */
+DWORD WINAPI NPSNotifyRegisterA( enum NOTIFYTYPE NotifyType, NOTIFYCALLBACK pfNotifyCallBack )
+{
+    FIXME( "(%d, %p): stub\n", NotifyType, pfNotifyCallBack );
+    return WN_NOT_SUPPORTED;
+}
+
+/*****************************************************************
+ *  NPSNotifyGetContextA [MPR.@]
+ */
+LPVOID WINAPI NPSNotifyGetContextA( NOTIFYCALLBACK pfNotifyCallBack )
+{
+    FIXME( "(%p): stub\n", pfNotifyCallBack );
+    return NULL;
+}
diff --git a/reactos/dll/mpr/pwcache.c b/reactos/dll/mpr/pwcache.c
new file mode 100644 (file)
index 0000000..682ec4c
--- /dev/null
@@ -0,0 +1,319 @@
+/*
+ * MPR Password Cache functions
+ *
+ * Copyright 1999 Ulrich Weigand
+ * Copyright 2003,2004 Mike McCormack for CodeWeavers
+ *
+ * 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 <stdarg.h>
+#include <stdio.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winnetwk.h"
+#include "winreg.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(mpr);
+
+static const char mpr_key[] = "Software\\Wine\\Wine\\Mpr\\";
+
+static inline BYTE hex( BYTE x )
+{
+    if( x <= 9 )
+        return x + '0';
+    return x + 'A' - 10;
+}
+
+static inline CHAR ctox( CHAR x )
+{
+    if( ( x >= '0' ) && ( x <= '9' ) )
+        return x - '0';
+    if( ( x >= 'A' ) && ( x <= 'F' ) )
+        return x - 'A' + 10;
+    if( ( x >= 'a' ) && ( x <= 'a' ) )
+        return x - 'a' + 10;
+    return -1;
+}
+
+static LPSTR MPR_GetValueName( LPSTR pbResource, WORD cbResource, BYTE nType )
+{
+    LPSTR name;
+    DWORD  i;
+
+    name = HeapAlloc( GetProcessHeap(), 0, 6+cbResource*2 );
+    if( name )
+        sprintf( name, "X-%02X-", nType );
+    for(i=0; i<cbResource; i++)
+    {
+        name[5+i*2]=hex((pbResource[i]&0xf0)>>4);
+        name[6+i*2]=hex(pbResource[i]&0x0f);
+    }
+    name[5+i*2]=0;
+    TRACE( "Value is %s\n", name );
+    return name;
+}
+
+
+/**************************************************************************
+ * WNetCachePassword [MPR.@]  Saves password in cache
+ *
+ * NOTES
+ *     only the parameter count is verifyed
+ *
+ *     ---- everything below this line might be wrong (js) -----
+ * RETURNS
+ *    Success: WN_SUCCESS
+ *    Failure: WN_ACCESS_DENIED, WN_BAD_PASSWORD, WN_BADVALUE, WN_NET_ERROR,
+ *             WN_NOT_SUPPORTED, WN_OUT_OF_MEMORY
+ */
+DWORD WINAPI WNetCachePassword(
+    LPSTR pbResource, /* [in] Name of workgroup, computer, or resource */
+    WORD cbResource,  /* [in] Size of name */
+    LPSTR pbPassword, /* [in] Buffer containing password */
+    WORD cbPassword,  /* [in] Size of password */
+    BYTE nType,       /* [in] Type of password to cache */
+    WORD x)
+
+{
+    HKEY hkey;
+    DWORD r;
+    LPSTR valname;
+
+    WARN( "(%p(%s), %d, %p(%s), %d, %d, 0x%08x): totally insecure\n",
+           pbResource, debugstr_a(pbResource), cbResource,
+          pbPassword, debugstr_a(pbPassword), cbPassword,
+          nType, x );
+
+    /* @@ Wine registry key: HKCU\Software\Wine\Wine\Mpr */
+    r = RegCreateKeyA( HKEY_CURRENT_USER, mpr_key, &hkey );
+    if( r )
+        return WN_ACCESS_DENIED;
+
+    valname = MPR_GetValueName( pbResource, cbResource, nType );
+    if( valname )
+    {
+        r = RegSetValueExA( hkey, valname, 0, REG_BINARY, 
+                            (LPBYTE)pbPassword, cbPassword );
+        if( r )
+            r = WN_CANCEL;
+        else
+            r = WN_SUCCESS;
+        HeapFree( GetProcessHeap(), 0, valname );
+    }
+    else
+        r = WN_OUT_OF_MEMORY;
+
+    RegCloseKey( hkey );
+
+    return r;
+}
+
+/*****************************************************************
+ *  WNetRemoveCachedPassword [MPR.@]
+ */
+UINT WINAPI WNetRemoveCachedPassword(
+      LPSTR pbResource,   /* [in] resource ID to delete */
+      WORD cbResource,    /* [in] number of bytes in the resource ID */
+      BYTE nType )        /* [in] Type of the resource to delete */
+{
+    HKEY hkey;
+    DWORD r;
+    LPSTR valname;
+
+    WARN( "(%p(%s), %d, %d): totally insecure\n",
+           pbResource, debugstr_a(pbResource), cbResource, nType );
+
+    /* @@ Wine registry key: HKCU\Software\Wine\Wine\Mpr */
+    r = RegCreateKeyA( HKEY_CURRENT_USER, mpr_key, &hkey );
+    if( r )
+        return WN_ACCESS_DENIED;
+
+    valname = MPR_GetValueName( pbResource, cbResource, nType );
+    if( valname )
+    {
+        r = RegDeleteValueA( hkey, valname );
+        if( r )
+            r = WN_ACCESS_DENIED;
+        else
+            r = WN_SUCCESS;
+        HeapFree( GetProcessHeap(), 0, valname );
+    }
+    else
+        r = WN_OUT_OF_MEMORY;
+
+    return r;
+}
+
+/*****************************************************************
+ * WNetGetCachedPassword [MPR.@]  Retrieves password from cache
+ *
+ * NOTES
+ *  the stub seems to be wrong:
+ *     arg1:   ptr     0x40xxxxxx -> (no string)
+ *     arg2:   len     36
+ *     arg3:   ptr     0x40xxxxxx -> (no string)
+ *     arg4:   ptr     0x40xxxxxx -> 0xc8
+ *     arg5:   type?   4
+ *
+ *     ---- everything below this line might be wrong (js) -----
+ * RETURNS
+ *    Success: WN_SUCCESS
+ *    Failure: WN_ACCESS_DENIED, WN_BAD_PASSWORD, WN_BAD_VALUE,
+ *             WN_NET_ERROR, WN_NOT_SUPPORTED, WN_OUT_OF_MEMORY
+ */
+DWORD WINAPI WNetGetCachedPassword(
+    LPSTR pbResource,   /* [in]  Name of workgroup, computer, or resource */
+    WORD cbResource,    /* [in]  Size of name */
+    LPSTR pbPassword,   /* [out] Buffer to receive password */
+    LPWORD pcbPassword, /* [out] Receives size of password */
+    BYTE nType)         /* [in]  Type of password to retrieve */
+{
+    HKEY hkey;
+    DWORD r, type = 0, sz;
+    LPSTR valname;
+
+    WARN( "(%p(%s), %d, %p, %p, %d): totally insecure\n",
+           pbResource, debugstr_a(pbResource), cbResource,
+          pbPassword, pcbPassword, nType );
+
+    memset( pbPassword, 0, *pcbPassword);
+
+    /* @@ Wine registry key: HKCU\Software\Wine\Wine\Mpr */
+    r = RegCreateKeyA( HKEY_CURRENT_USER, mpr_key, &hkey );
+    if( r )
+        return WN_ACCESS_DENIED;
+
+    valname = MPR_GetValueName( pbResource, cbResource, nType );
+    if( valname )
+    {
+        sz = *pcbPassword;
+        r = RegQueryValueExA( hkey, valname, 0, &type, (LPBYTE)pbPassword, &sz );
+        *pcbPassword = sz;
+        if( r )
+            r = WN_CANCEL;
+        else
+            r = WN_SUCCESS;
+        HeapFree( GetProcessHeap(), 0, valname );
+    }
+    else
+        r = WN_OUT_OF_MEMORY;
+
+    return r;
+}
+
+/*******************************************************************
+ * WNetEnumCachedPasswords [MPR.@]
+ *
+ * NOTES
+ *     the parameter count is verifyed
+ * 
+ *  This function is a huge security risk, as virii and such can use
+ * it to grab all the passwords in the cache.  It's bad enough to 
+ * store the passwords (insecurely).
+ *
+ *  bpPrefix and cbPrefix are used to filter the returned passwords
+ *   the first cbPrefix bytes of the password resource identifier
+ *   should match the same number of bytes in bpPrefix
+ *
+ * RETURNS
+ *   Success: WN_SUCCESS   (even if no entries were enumerated)
+ *   Failure: WN_ACCESS_DENIED, WN_BAD_PASSWORD, WN_BAD_VALUE,
+ *             WN_NET_ERROR, WN_NOT_SUPPORTED, WN_OUT_OF_MEMORY
+ */
+
+UINT WINAPI WNetEnumCachedPasswords(
+      LPSTR pbPrefix,  /* [in] prefix to filter cache entries */
+      WORD cbPrefix,   /* [in] number of bytes in Prefix substring */
+      BYTE nType,      /* [in] match the Type ID of the entry */
+      ENUMPASSWORDPROC enumPasswordProc,  /* [in] callback function */
+      DWORD param)     /* [in] parameter passed to enum function */
+{
+    HKEY hkey;
+    DWORD r, type, val_sz, data_sz, i, j, size;
+    PASSWORD_CACHE_ENTRY *entry;
+    CHAR val[256], prefix[6];
+
+    WARN( "(%s, %d, %d, %p, 0x%08lx) totally insecure\n",
+           debugstr_an(pbPrefix,cbPrefix), cbPrefix,
+          nType, enumPasswordProc, param );
+
+    /* @@ Wine registry key: HKCU\Software\Wine\Wine\Mpr */
+    r = RegCreateKeyA( HKEY_CURRENT_USER, mpr_key, &hkey );
+    if( r )
+        return WN_ACCESS_DENIED;
+
+    sprintf(prefix, "X-%02X-", nType );
+
+    i = 0;
+    for( i=0;  ; i++ )
+    {
+        val_sz  = sizeof val;
+        data_sz = 0;
+        type    = 0;
+        val[0] = 0;
+        r = RegEnumValueA( hkey, i, val, &val_sz, NULL, &type, NULL, &data_sz );
+        if( r != ERROR_SUCCESS )
+            break;
+        if( type != REG_BINARY )
+            continue;
+
+        /* check the value is in the format we expect */
+        if( val_sz < sizeof prefix )
+            continue;
+        if( memcmp( prefix, val, 5 ) )
+            continue;
+
+        /* decode the value */
+        for(j=5; j<val_sz; j+=2 )
+        {
+            CHAR hi = ctox( val[j] ), lo = ctox( val[j+1] );
+            if( ( hi < 0 ) || ( lo < 0 ) )
+                break;
+            val[(j-5)/2] = (hi<<4) | lo;
+        }
+
+        /* find the decoded length */
+        val_sz = (j - 5)/2;
+        val[val_sz]=0;
+        if( val_sz < cbPrefix )
+            continue;
+
+        /* check the prefix matches */
+        if( memcmp(val, pbPrefix, cbPrefix) )
+            continue;
+
+        /* read the value data */
+        size = sizeof *entry - sizeof entry->abResource[0] + val_sz + data_sz;
+        entry = HeapAlloc( GetProcessHeap(), 0, sizeof *entry + val_sz + data_sz );
+        memcpy( entry->abResource, val, val_sz );
+        entry->cbEntry = size;
+        entry->cbResource = val_sz;
+        entry->cbPassword = data_sz;
+        entry->iEntry = i;
+        entry->nType = nType;
+        r = RegEnumValueA( hkey, i, NULL, &val_sz, NULL, &type, 
+                           &entry->abResource[val_sz], &data_sz );
+        if( r == ERROR_SUCCESS )
+            enumPasswordProc( entry, param );
+        HeapFree( GetProcessHeap(), 0, entry );
+    }
+
+    RegCloseKey( hkey );
+
+    return WN_SUCCESS;
+}
diff --git a/reactos/dll/mpr/version.rc b/reactos/dll/mpr/version.rc
new file mode 100644 (file)
index 0000000..f22cc4d
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * MPR dll version resources
+ *
+ * Copyright (C) 2004 Juan Lang
+ *
+ * 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
+ */
+
+#define WINE_FILEDESCRIPTION_STR "Wine Multiprotocol Router Library"
+#define WINE_FILENAME_STR "mpr.dll"
+#define WINE_FILEVERSION 5,0,2195,6611
+#define WINE_FILEVERSION_STR "5.00.2195.6611"
+#define WINE_PRODUCTVERSION 5,0,2195,6611
+#define WINE_PRODUCTVERSION_STR "5.00.2195.6611"
+
+#include "wine/wine_common_ver.rc"
diff --git a/reactos/dll/mpr/wnet.c b/reactos/dll/mpr/wnet.c
new file mode 100644 (file)
index 0000000..3b585c0
--- /dev/null
@@ -0,0 +1,2038 @@
+/*
+ * MPR WNet functions
+ *
+ * Copyright 1999 Ulrich Weigand
+ * Copyright 2004 Juan Lang
+ *
+ * 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 <stdarg.h>
+#include "windef.h"
+#include "winbase.h"
+#include "winuser.h"
+#include "winnls.h"
+#include "winnetwk.h"
+#include "npapi.h"
+#include "winreg.h"
+#include "winuser.h"
+#include "wine/debug.h"
+#include "wine/unicode.h"
+#include "mprres.h"
+#include "wnetpriv.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(mpr);
+
+/* Data structures representing network service providers.  Assumes only one
+ * thread creates them, and that they are constant for the life of the process
+ * (and therefore doesn't synchronize access).
+ * FIXME: only basic provider data and enumeration-related data are implemented
+ * so far, need to implement the rest too.
+ */
+typedef struct _WNetProvider
+{
+    HMODULE           hLib;
+    PWSTR             name;
+    PF_NPGetCaps      getCaps;
+    DWORD             dwSpecVersion;
+    DWORD             dwNetType;
+    DWORD             dwEnumScopes;
+    PF_NPOpenEnum     openEnum;
+    PF_NPEnumResource enumResource;
+    PF_NPCloseEnum    closeEnum;
+} WNetProvider, *PWNetProvider;
+
+typedef struct _WNetProviderTable
+{
+    LPWSTR           entireNetwork;
+    DWORD            numAllocated;
+    DWORD            numProviders;
+    WNetProvider     table[1];
+} WNetProviderTable, *PWNetProviderTable;
+
+#define WNET_ENUMERATOR_TYPE_NULL     0
+#define WNET_ENUMERATOR_TYPE_GLOBAL   1
+#define WNET_ENUMERATOR_TYPE_PROVIDER 2
+#define WNET_ENUMERATOR_TYPE_CONTEXT  3
+
+/* An WNet enumerator.  Note that the type doesn't correspond to the scope of
+ * the enumeration; it represents one of the following types:
+ * - a 'null' enumeration, one that contains no members
+ * - a global enumeration, one that's executed across all providers
+ * - a provider-specific enumeration, one that's only executed by a single
+ *   provider
+ * - a context enumeration.  I know this contradicts what I just said about
+ *   there being no correspondence between the scope and the type, but it's
+ *   necessary for the special case that a "Entire Network" entry needs to
+ *   be enumerated in an enumeration of the context scope.  Thus an enumeration
+ *   of the context scope results in a context type enumerator, which morphs
+ *   into a global enumeration (so the enumeration continues across all
+ *   providers).
+ */
+typedef struct _WNetEnumerator
+{
+    DWORD          enumType;
+    DWORD          providerIndex;
+    HANDLE         handle;
+    BOOL           providerDone;
+    DWORD          dwScope;
+    DWORD          dwType;
+    DWORD          dwUsage;
+    LPNETRESOURCEW lpNet;
+} WNetEnumerator, *PWNetEnumerator;
+
+#define BAD_PROVIDER_INDEX (DWORD)0xffffffff
+
+/* Returns an index (into the global WNetProviderTable) of the provider with
+ * the given name, or BAD_PROVIDER_INDEX if not found.
+ */
+static DWORD _findProviderIndexW(LPCWSTR lpProvider);
+
+PWNetProviderTable providerTable;
+
+/*
+ * Global provider table functions
+ */
+
+static void _tryLoadProvider(PCWSTR provider)
+{
+    static const WCHAR servicePrefix[] = { 'S','y','s','t','e','m','\\',
+     'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
+     'S','e','r','v','i','c','e','s','\\',0 };
+    static const WCHAR serviceFmt[] = { '%','s','%','s','\\',
+     'N','e','t','w','o','r','k','P','r','o','v','i','d','e','r',0 };
+    WCHAR serviceName[MAX_PATH];
+    HKEY hKey;
+
+    TRACE("%s\n", debugstr_w(provider));
+    snprintfW(serviceName, sizeof(serviceName) / sizeof(WCHAR), serviceFmt,
+     servicePrefix, provider);
+    serviceName[sizeof(serviceName) / sizeof(WCHAR) - 1] = '\0';
+    if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, serviceName, 0, KEY_READ, &hKey) ==
+     ERROR_SUCCESS)
+    {
+        static const WCHAR szProviderPath[] = { 'P','r','o','v','i','d','e','r',
+         'P','a','t','h',0 };
+        WCHAR providerPath[MAX_PATH];
+        DWORD type, size = sizeof(providerPath);
+
+        if (RegQueryValueExW(hKey, szProviderPath, NULL, &type,
+         (LPBYTE)providerPath, &size) == ERROR_SUCCESS && type == REG_SZ)
+        {
+            static const WCHAR szProviderName[] = { 'N','a','m','e',0 };
+            PWSTR name = NULL;
+           
+            size = 0;
+            RegQueryValueExW(hKey, szProviderName, NULL, NULL, NULL, &size);
+            if (size)
+            {
+                name = HeapAlloc(GetProcessHeap(), 0, size);
+                if (RegQueryValueExW(hKey, szProviderName, NULL, &type,
+                 (LPBYTE)name, &size) != ERROR_SUCCESS || type != REG_SZ)
+                {
+                    HeapFree(GetProcessHeap(), 0, name);
+                    name = NULL;
+                }
+            }
+            if (name)
+            {
+                HMODULE hLib = LoadLibraryW(providerPath);
+
+                if (hLib)
+                {
+                    PF_NPGetCaps getCaps = (PF_NPGetCaps)GetProcAddress(hLib,
+                     "NPGetCaps");
+
+                    TRACE("loaded lib %p\n", hLib);
+                    if (getCaps)
+                    {
+                        PWNetProvider provider =
+                         &providerTable->table[providerTable->numProviders];
+
+                        provider->hLib = hLib;
+                        provider->name = name;
+                        TRACE("name is %s\n", debugstr_w(name));
+                        provider->getCaps = getCaps;
+                        provider->dwSpecVersion = getCaps(WNNC_SPEC_VERSION);
+                        provider->dwNetType = getCaps(WNNC_NET_TYPE);
+                        TRACE("net type is 0x%08lx\n", provider->dwNetType);
+                        provider->dwEnumScopes = getCaps(WNNC_ENUMERATION);
+                        if (provider->dwEnumScopes)
+                        {
+                            TRACE("supports enumeration\n");
+                            provider->openEnum = (PF_NPOpenEnum)
+                             GetProcAddress(hLib, "NPOpenEnum");
+                            TRACE("openEnum is %p\n", provider->openEnum);
+                            provider->enumResource = (PF_NPEnumResource)
+                             GetProcAddress(hLib, "NPEnumResource");
+                            TRACE("enumResource is %p\n",
+                             provider->enumResource);
+                            provider->closeEnum = (PF_NPCloseEnum)
+                             GetProcAddress(hLib, "NPCloseEnum");
+                            TRACE("closeEnum is %p\n", provider->closeEnum);
+                            if (!provider->openEnum || !provider->enumResource
+                             || !provider->closeEnum)
+                            {
+                                provider->openEnum = NULL;
+                                provider->enumResource = NULL;
+                                provider->closeEnum = NULL;
+                                provider->dwEnumScopes = 0;
+                                WARN("Couldn't load enumeration functions\n");
+                            }
+                        }
+                        providerTable->numProviders++;
+                    }
+                    else
+                    {
+                        WARN("Provider %s didn't export NPGetCaps\n",
+                         debugstr_w(provider));
+                        HeapFree(GetProcessHeap(), 0, name);
+                        FreeLibrary(hLib);
+                    }
+                }
+                else
+                {
+                    WARN("Couldn't load library %s for provider %s\n",
+                     debugstr_w(providerPath), debugstr_w(provider));
+                    HeapFree(GetProcessHeap(), 0, name);
+                }
+            }
+            else
+            {
+                WARN("Couldn't get provider name for provider %s\n",
+                 debugstr_w(provider));
+            }
+        }
+        else
+            WARN("Couldn't open value %s\n", debugstr_w(szProviderPath));
+        RegCloseKey(hKey);
+    }
+    else
+        WARN("Couldn't open service key for provider %s\n",
+         debugstr_w(provider));
+}
+
+void wnetInit(HINSTANCE hInstDll)
+{
+    static const WCHAR providerOrderKey[] = { 'S','y','s','t','e','m','\\',
+     'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
+     'C','o','n','t','r','o','l','\\',
+     'N','e','t','w','o','r','k','P','r','o','v','i','d','e','r','\\',
+     'O','r','d','e','r',0 };
+     static const WCHAR providerOrder[] = { 'P','r','o','v','i','d','e','r',
+      'O','r','d','e','r',0 };
+    HKEY hKey;
+
+    if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, providerOrderKey, 0, KEY_READ, &hKey)
+     == ERROR_SUCCESS)
+    {
+        DWORD size = 0;
+
+        RegQueryValueExW(hKey, providerOrder, NULL, NULL, NULL, &size);
+        if (size)
+        {
+            PWSTR providers = HeapAlloc(GetProcessHeap(), 0, size);
+
+            if (providers)
+            {
+                DWORD type;
+
+                if (RegQueryValueExW(hKey, providerOrder, NULL, &type,
+                 (LPBYTE)providers, &size) == ERROR_SUCCESS && type == REG_SZ)
+                {
+                    PWSTR ptr;
+                    DWORD numToAllocate;
+
+                    TRACE("provider order is %s\n", debugstr_w(providers));
+                    /* first count commas as a heuristic for how many to
+                     * allocate space for */
+                    for (ptr = providers, numToAllocate = 1; ptr; )
+                    {
+                        ptr = strchrW(ptr, ',');
+                        if (ptr)
+                            numToAllocate++;
+                    }
+                    providerTable =
+                     HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
+                     sizeof(WNetProviderTable)
+                     + (numToAllocate - 1) * sizeof(WNetProvider));
+                    if (providerTable)
+                    {
+                        PWSTR ptrPrev;
+                        int entireNetworkLen;
+
+                        entireNetworkLen = LoadStringW(hInstDll,
+                         IDS_ENTIRENETWORK, NULL, 0);
+                        providerTable->entireNetwork = HeapAlloc(
+                         GetProcessHeap(), 0, (entireNetworkLen + 1) *
+                         sizeof(WCHAR));
+                        if (providerTable->entireNetwork)
+                            LoadStringW(hInstDll, IDS_ENTIRENETWORK,
+                             providerTable->entireNetwork,
+                             entireNetworkLen + 1);
+                        providerTable->numAllocated = numToAllocate;
+                        for (ptr = providers; ptr; )
+                        {
+                            ptrPrev = ptr;
+                            ptr = strchrW(ptr, ',');
+                            if (ptr)
+                                *ptr = '\0';
+                            _tryLoadProvider(ptrPrev);
+                        }
+                    }
+                }
+                HeapFree(GetProcessHeap(), 0, providers);
+            }
+        }
+        RegCloseKey(hKey);
+    }
+}
+
+void wnetFree(void)
+{
+    if (providerTable)
+    {
+        DWORD i;
+
+        for (i = 0; i < providerTable->numProviders; i++)
+        {
+            HeapFree(GetProcessHeap(), 0, providerTable->table[i].name);
+            FreeModule(providerTable->table[i].hLib);
+        }
+        HeapFree(GetProcessHeap(), 0, providerTable->entireNetwork);
+        HeapFree(GetProcessHeap(), 0, providerTable);
+        providerTable = NULL;
+    }
+}
+
+static DWORD _findProviderIndexW(LPCWSTR lpProvider)
+{
+    DWORD ret = BAD_PROVIDER_INDEX;
+
+    if (providerTable && providerTable->numProviders)
+    {
+        DWORD i;
+
+        for (i = 0; i < providerTable->numProviders &&
+         ret == BAD_PROVIDER_INDEX; i++)
+            if (!strcmpW(lpProvider, providerTable->table[i].name))
+                ret = i;
+    }
+    return ret;
+}
+
+/*
+ * Browsing Functions
+ */
+
+static LPNETRESOURCEW _copyNetResourceForEnumW(LPNETRESOURCEW lpNet)
+{
+    LPNETRESOURCEW ret;
+
+    if (lpNet)
+    {
+        ret = HeapAlloc(GetProcessHeap(), 0, sizeof(NETRESOURCEW));
+        if (ret)
+        {
+            size_t len;
+
+            memcpy(ret, lpNet, sizeof(ret));
+            ret->lpLocalName = ret->lpComment = ret->lpProvider = NULL;
+            if (lpNet->lpRemoteName)
+            {
+                len = strlenW(lpNet->lpRemoteName) + 1;
+                ret->lpRemoteName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
+                if (ret->lpRemoteName)
+                    strcpyW(ret->lpRemoteName, lpNet->lpRemoteName);
+            }
+        }
+    }
+    else
+        ret = NULL;
+    return ret;
+}
+
+static void _freeEnumNetResource(LPNETRESOURCEW lpNet)
+{
+    if (lpNet)
+    {
+        HeapFree(GetProcessHeap(), 0, lpNet->lpRemoteName);
+        HeapFree(GetProcessHeap(), 0, lpNet);
+    }
+}
+
+static PWNetEnumerator _createNullEnumerator(void)
+{
+    PWNetEnumerator ret = HeapAlloc(GetProcessHeap(),
+     HEAP_ZERO_MEMORY, sizeof(WNetEnumerator));
+
+    if (ret)
+        ret->enumType = WNET_ENUMERATOR_TYPE_NULL;
+    return ret;
+}
+
+static PWNetEnumerator _createGlobalEnumeratorW(DWORD dwScope, DWORD dwType,
+ DWORD dwUsage, LPNETRESOURCEW lpNet)
+{
+    PWNetEnumerator ret = HeapAlloc(GetProcessHeap(),
+     HEAP_ZERO_MEMORY, sizeof(WNetEnumerator));
+
+    if (ret)
+    {
+        ret->enumType = WNET_ENUMERATOR_TYPE_GLOBAL;
+        ret->dwScope = dwScope;
+        ret->dwType  = dwType;
+        ret->dwUsage = dwUsage;
+        ret->lpNet   = _copyNetResourceForEnumW(lpNet);
+    }
+    return ret;
+}
+
+static PWNetEnumerator _createProviderEnumerator(DWORD dwScope, DWORD dwType,
+ DWORD dwUsage, DWORD index, HANDLE handle)
+{
+    PWNetEnumerator ret;
+
+    if (!providerTable || index >= providerTable->numProviders)
+        ret = NULL;
+    else
+    {
+        ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WNetEnumerator));
+        if (ret)
+        {
+            ret->enumType      = WNET_ENUMERATOR_TYPE_PROVIDER;
+            ret->providerIndex = index;
+            ret->dwScope       = dwScope;
+            ret->dwType        = dwType;
+            ret->dwUsage       = dwUsage;
+            ret->handle        = handle;
+        }
+    }
+    return ret;
+}
+
+static PWNetEnumerator _createContextEnumerator(DWORD dwScope, DWORD dwType,
+ DWORD dwUsage)
+{
+    PWNetEnumerator ret = HeapAlloc(GetProcessHeap(),
+     HEAP_ZERO_MEMORY, sizeof(WNetEnumerator));
+
+    if (ret)
+    {
+        ret->enumType = WNET_ENUMERATOR_TYPE_CONTEXT;
+        ret->dwScope = dwScope;
+        ret->dwType  = dwType;
+        ret->dwUsage = dwUsage;
+    }
+    return ret;
+}
+
+/* Thunks the array of wide-string LPNETRESOURCEs lpNetArrayIn into buffer
+ * lpBuffer, with size *lpBufferSize.  lpNetArrayIn contains *lpcCount entries
+ * to start.  On return, *lpcCount reflects the number thunked into lpBuffer.
+ * Returns WN_SUCCESS on success (all of lpNetArrayIn thunked), WN_MORE_DATA
+ * if not all members of the array could be thunked, and something else on
+ * failure.
+ */
+static DWORD _thunkNetResourceArrayWToA(const LPNETRESOURCEW lpNetArrayIn,
+ LPDWORD lpcCount, LPVOID lpBuffer, LPDWORD lpBufferSize)
+{
+    DWORD i, numToThunk, totalBytes, ret;
+    LPSTR strNext;
+
+    if (!lpNetArrayIn)
+        return WN_BAD_POINTER;
+    if (!lpcCount)
+        return WN_BAD_POINTER;
+    if (*lpcCount == -1)
+        return WN_BAD_VALUE;
+    if (!lpBuffer)
+        return WN_BAD_POINTER;
+    if (!lpBufferSize)
+        return WN_BAD_POINTER;
+
+    for (i = 0, numToThunk = 0, totalBytes = 0; i < *lpcCount; i++)
+    {
+        LPNETRESOURCEW lpNet = lpNetArrayIn + i;
+
+        totalBytes += sizeof(NETRESOURCEA);
+        if (lpNet->lpLocalName)
+            totalBytes += WideCharToMultiByte(CP_ACP, 0, lpNet->lpLocalName,
+             -1, NULL, 0, NULL, NULL);
+        if (lpNet->lpRemoteName)
+            totalBytes += WideCharToMultiByte(CP_ACP, 0, lpNet->lpRemoteName,
+             -1, NULL, 0, NULL, NULL);
+        if (lpNet->lpComment)
+            totalBytes += WideCharToMultiByte(CP_ACP, 0, lpNet->lpComment,
+             -1, NULL, 0, NULL, NULL);
+        if (lpNet->lpProvider)
+            totalBytes += WideCharToMultiByte(CP_ACP, 0, lpNet->lpProvider,
+             -1, NULL, 0, NULL, NULL);
+        if (totalBytes < *lpBufferSize)
+            numToThunk = i + 1;
+    }
+    strNext = (LPSTR)((LPBYTE)lpBuffer + numToThunk * sizeof(NETRESOURCEA));
+    for (i = 0; i < numToThunk; i++)
+    {
+        LPNETRESOURCEA lpNetOut = (LPNETRESOURCEA)lpBuffer + i;
+        LPNETRESOURCEW lpNetIn = lpNetArrayIn + i;
+
+        memcpy(lpNetOut, lpNetIn, sizeof(NETRESOURCEA));
+        /* lie about string lengths, we already verified how many
+         * we have space for above
+         */
+        if (lpNetIn->lpLocalName)
+        {
+            lpNetOut->lpLocalName = strNext;
+            strNext += WideCharToMultiByte(CP_ACP, 0, lpNetIn->lpLocalName, -1,
+             lpNetOut->lpLocalName, *lpBufferSize, NULL, NULL);
+        }
+        if (lpNetIn->lpRemoteName)
+        {
+            lpNetOut->lpRemoteName = strNext;
+            strNext += WideCharToMultiByte(CP_ACP, 0, lpNetIn->lpRemoteName, -1,
+             lpNetOut->lpRemoteName, *lpBufferSize, NULL, NULL);
+        }
+        if (lpNetIn->lpComment)
+        {
+            lpNetOut->lpComment = strNext;
+            strNext += WideCharToMultiByte(CP_ACP, 0, lpNetIn->lpComment, -1,
+             lpNetOut->lpComment, *lpBufferSize, NULL, NULL);
+        }
+        if (lpNetIn->lpProvider)
+        {
+            lpNetOut->lpProvider = strNext;
+            strNext += WideCharToMultiByte(CP_ACP, 0, lpNetIn->lpProvider, -1,
+             lpNetOut->lpProvider, *lpBufferSize, NULL, NULL);
+        }
+    }
+    ret = numToThunk < *lpcCount ? WN_MORE_DATA : WN_SUCCESS;
+    TRACE("numToThunk is %ld, *lpcCount is %ld, returning %ld\n", numToThunk,
+     *lpcCount, ret);
+    return ret;
+}
+
+/* Thunks the array of multibyte-string LPNETRESOURCEs lpNetArrayIn into buffer
+ * lpBuffer, with size *lpBufferSize.  lpNetArrayIn contains *lpcCount entries
+ * to start.  On return, *lpcCount reflects the number thunked into lpBuffer.
+ * Returns WN_SUCCESS on success (all of lpNetArrayIn thunked), WN_MORE_DATA
+ * if not all members of the array could be thunked, and something else on
+ * failure.
+ */
+static DWORD _thunkNetResourceArrayAToW(const LPNETRESOURCEA lpNetArrayIn,
+ LPDWORD lpcCount, LPVOID lpBuffer, LPDWORD lpBufferSize)
+{
+    DWORD i, numToThunk, totalBytes, ret;
+    LPWSTR strNext;
+
+    if (!lpNetArrayIn)
+        return WN_BAD_POINTER;
+    if (!lpcCount)
+        return WN_BAD_POINTER;
+    if (*lpcCount == -1)
+        return WN_BAD_VALUE;
+    if (!lpBuffer)
+        return WN_BAD_POINTER;
+    if (!lpBufferSize)
+        return WN_BAD_POINTER;
+
+    for (i = 0, numToThunk = 0, totalBytes = 0; i < *lpcCount; i++)
+    {
+        LPNETRESOURCEA lpNet = lpNetArrayIn + i;
+
+        totalBytes += sizeof(NETRESOURCEW);
+        if (lpNet->lpLocalName)
+            totalBytes += MultiByteToWideChar(CP_ACP, 0, lpNet->lpLocalName,
+             -1, NULL, 0) * sizeof(WCHAR);
+        if (lpNet->lpRemoteName)
+            totalBytes += MultiByteToWideChar(CP_ACP, 0, lpNet->lpRemoteName,
+             -1, NULL, 0) * sizeof(WCHAR);
+        if (lpNet->lpComment)
+            totalBytes += MultiByteToWideChar(CP_ACP, 0, lpNet->lpComment,
+             -1, NULL, 0) * sizeof(WCHAR);
+        if (lpNet->lpProvider)
+            totalBytes += MultiByteToWideChar(CP_ACP, 0, lpNet->lpProvider,
+             -1, NULL, 0) * sizeof(WCHAR);
+        if (totalBytes < *lpBufferSize)
+            numToThunk = i + 1;
+    }
+    strNext = (LPWSTR)((LPBYTE)lpBuffer + numToThunk * sizeof(NETRESOURCEW));
+    for (i = 0; i < numToThunk; i++)
+    {
+        LPNETRESOURCEW lpNetOut = (LPNETRESOURCEW)lpBuffer + i;
+        LPNETRESOURCEA lpNetIn = lpNetArrayIn + i;
+
+        memcpy(lpNetOut, lpNetIn, sizeof(NETRESOURCEW));
+        /* lie about string lengths, we already verified how many
+         * we have space for above
+         */
+        if (lpNetIn->lpLocalName)
+        {
+            lpNetOut->lpLocalName = strNext;
+            strNext += MultiByteToWideChar(CP_ACP, 0, lpNetIn->lpLocalName,
+             -1, lpNetOut->lpLocalName, *lpBufferSize);
+        }
+        if (lpNetIn->lpRemoteName)
+        {
+            lpNetOut->lpRemoteName = strNext;
+            strNext += MultiByteToWideChar(CP_ACP, 0, lpNetIn->lpRemoteName,
+             -1, lpNetOut->lpRemoteName, *lpBufferSize);
+        }
+        if (lpNetIn->lpComment)
+        {
+            lpNetOut->lpComment = strNext;
+            strNext += MultiByteToWideChar(CP_ACP, 0, lpNetIn->lpComment,
+             -1, lpNetOut->lpComment, *lpBufferSize);
+        }
+        if (lpNetIn->lpProvider)
+        {
+            lpNetOut->lpProvider = strNext;
+            strNext += MultiByteToWideChar(CP_ACP, 0, lpNetIn->lpProvider,
+             -1, lpNetOut->lpProvider, *lpBufferSize);
+        }
+    }
+    ret = numToThunk < *lpcCount ? WN_MORE_DATA : WN_SUCCESS;
+    TRACE("numToThunk is %ld, *lpcCount is %ld, returning %ld\n", numToThunk,
+     *lpcCount, ret);
+    return ret;
+}
+
+/*********************************************************************
+ * WNetOpenEnumA [MPR.@]
+ *
+ * See comments for WNetOpenEnumW.
+ */
+DWORD WINAPI WNetOpenEnumA( DWORD dwScope, DWORD dwType, DWORD dwUsage,
+                            LPNETRESOURCEA lpNet, LPHANDLE lphEnum )
+{
+    DWORD ret;
+
+    TRACE( "(%08lX, %08lX, %08lX, %p, %p)\n",
+           dwScope, dwType, dwUsage, lpNet, lphEnum );
+
+    if (!lphEnum)
+        ret = WN_BAD_POINTER;
+    else if (!providerTable || providerTable->numProviders == 0)
+        ret = WN_NO_NETWORK;
+    else
+    {
+        if (lpNet)
+        {
+            LPNETRESOURCEW lpNetWide = NULL;
+            BYTE buf[1024];
+            DWORD size = sizeof(buf), count = 1;
+            BOOL allocated = FALSE;
+
+            ret = _thunkNetResourceArrayAToW(lpNet, &count, buf, &size);
+            if (ret == WN_MORE_DATA)
+            {
+                lpNetWide = HeapAlloc(GetProcessHeap(), 0,
+                 size);
+                if (lpNetWide)
+                {
+                    ret = _thunkNetResourceArrayAToW(lpNet, &count, lpNetWide,
+                     &size);
+                    allocated = TRUE;
+                }
+                else
+                    ret = WN_OUT_OF_MEMORY;
+            }
+            else if (ret == WN_SUCCESS)
+                lpNetWide = (LPNETRESOURCEW)buf;
+            if (ret == WN_SUCCESS)
+                ret = WNetOpenEnumW(dwScope, dwType, dwUsage, lpNetWide,
+                 lphEnum);
+            if (allocated && lpNetWide)
+                HeapFree(GetProcessHeap(), 0, lpNetWide);
+        }
+        else
+            ret = WNetOpenEnumW(dwScope, dwType, dwUsage, NULL, lphEnum);
+    }
+    if (ret)
+        SetLastError(ret);
+    TRACE("Returning %ld\n", ret);
+    return ret;
+}
+
+/*********************************************************************
+ * WNetOpenEnumW [MPR.@]
+ *
+ * Network enumeration has way too many parameters, so I'm not positive I got
+ * them right.  What I've got so far:
+ *
+ * - If the scope is RESOURCE_GLOBALNET, and no LPNETRESOURCE is passed,
+ *   all the network providers should be enumerated.
+ *
+ * - If the scope is RESOURCE_GLOBALNET, and LPNETRESOURCE is passed, and
+ *   and neither the LPNETRESOURCE's lpRemoteName nor the LPNETRESOURCE's
+ *   lpProvider is set, all the network providers should be enumerated.
+ *   (This means the enumeration is a list of network providers, not that the
+ *   enumeration is passed on to the providers.)
+ *
+ * - If the scope is RESOURCE_GLOBALNET, and LPNETRESOURCE is passed, and the
+ *   resource matches the "Entire Network" resource (no remote name, no
+ *   provider, comment is the "Entire Network" string), a RESOURCE_GLOBALNET
+ *   enumeration is done on every network provider.
+ *
+ * - If the scope is RESOURCE_GLOBALNET, and LPNETRESOURCE is passed, and
+ *   the LPNETRESOURCE's lpProvider is set, enumeration will be passed through
+ *   only to the given network provider.
+ *
+ * - If the scope is RESOURCE_GLOBALNET, and LPNETRESOURCE is passed, and
+ *   no lpProvider is set, enumeration will be tried on every network provider,
+ *   in the order in which they're loaded.
+ *
+ * - The LPNETRESOURCE should be disregarded for scopes besides
+ *   RESOURCE_GLOBALNET.  MSDN states that lpNet must be NULL if dwScope is not
+ *   RESOURCE_GLOBALNET, but Windows doesn't return an error if it isn't NULL.
+ *
+ * - If the scope is RESOURCE_CONTEXT, MS includes an "Entire Network" net
+ *   resource in the enumerated list, as well as any machines in your
+ *   workgroup.  The machines in your workgroup come from doing a
+ *   RESOURCE_CONTEXT enumeration of every Network Provider.
+ */
+DWORD WINAPI WNetOpenEnumW( DWORD dwScope, DWORD dwType, DWORD dwUsage,
+                            LPNETRESOURCEW lpNet, LPHANDLE lphEnum )
+{
+    DWORD ret;
+
+    TRACE( "(%08lX, %08lX, %08lX, %p, %p)\n",
+          dwScope, dwType, dwUsage, lpNet, lphEnum );
+
+    if (!lphEnum)
+        ret = WN_BAD_POINTER;
+    else if (!providerTable || providerTable->numProviders == 0)
+        ret = WN_NO_NETWORK;
+    else
+    {
+        switch (dwScope)
+        {
+            case RESOURCE_GLOBALNET:
+                if (lpNet)
+                {
+                    if (lpNet->lpProvider)
+                    {
+                        DWORD index = _findProviderIndexW(lpNet->lpProvider);
+
+                        if (index != BAD_PROVIDER_INDEX)
+                        {
+                            if (providerTable->table[index].openEnum &&
+                             providerTable->table[index].dwEnumScopes & dwScope)
+                            {
+                                HANDLE handle;
+
+                                ret = providerTable->table[index].openEnum(
+                                 dwScope, dwType, dwUsage, lpNet, &handle);
+                                if (ret == WN_SUCCESS)
+                                {
+                                    *lphEnum =
+                                     (HANDLE)_createProviderEnumerator(
+                                     dwScope, dwType, dwUsage, index, handle);
+                                    ret = *lphEnum ? WN_SUCCESS :
+                                     WN_OUT_OF_MEMORY;
+                                }
+                            }
+                            else
+                                ret = WN_NOT_SUPPORTED;
+                        }
+                        else
+                            ret = WN_BAD_PROVIDER;
+                    }
+                    else if (lpNet->lpRemoteName)
+                    {
+                        *lphEnum = (HANDLE)_createGlobalEnumeratorW(dwScope,
+                         dwType, dwUsage, lpNet);
+                        ret = *lphEnum ? WN_SUCCESS : WN_OUT_OF_MEMORY;
+                    }
+                    else
+                    {
+                        if (lpNet->lpComment && !strcmpW(lpNet->lpComment,
+                         providerTable->entireNetwork))
+                        {
+                            /* comment matches the "Entire Network", enumerate
+                             * global scope of every provider
+                             */
+                            *lphEnum = (HANDLE)_createGlobalEnumeratorW(dwScope,
+                             dwType, dwUsage, lpNet);
+                        }
+                        else
+                        {
+                            /* this is the same as not having passed lpNet */
+                            *lphEnum = (HANDLE)_createGlobalEnumeratorW(dwScope,
+                             dwType, dwUsage, NULL);
+                        }
+                        ret = *lphEnum ? WN_SUCCESS : WN_OUT_OF_MEMORY;
+                    }
+                }
+                else
+                {
+                    *lphEnum = (HANDLE)_createGlobalEnumeratorW(dwScope, dwType,
+                     dwUsage, lpNet);
+                    ret = *lphEnum ? WN_SUCCESS : WN_OUT_OF_MEMORY;
+                }
+                break;
+            case RESOURCE_CONTEXT:
+                *lphEnum = (HANDLE)_createContextEnumerator(dwScope, dwType,
+                 dwUsage);
+                ret = *lphEnum ? WN_SUCCESS : WN_OUT_OF_MEMORY;
+                break;
+            case RESOURCE_REMEMBERED:
+            case RESOURCE_CONNECTED:
+                *lphEnum = (HANDLE)_createNullEnumerator();
+                ret = *lphEnum ? WN_SUCCESS : WN_OUT_OF_MEMORY;
+                break;
+            default:
+                WARN("unknown scope 0x%08lx\n", dwScope);
+                ret = WN_BAD_VALUE;
+        }
+    }
+    if (ret)
+        SetLastError(ret);
+    TRACE("Returning %ld\n", ret);
+    return ret;
+}
+
+/*********************************************************************
+ * WNetEnumResourceA [MPR.@]
+ */
+DWORD WINAPI WNetEnumResourceA( HANDLE hEnum, LPDWORD lpcCount,
+                                LPVOID lpBuffer, LPDWORD lpBufferSize )
+{
+    DWORD ret;
+
+    TRACE( "(%p, %p, %p, %p)\n", hEnum, lpcCount, lpBuffer, lpBufferSize );
+
+    if (!hEnum)
+        ret = WN_BAD_POINTER;
+    else if (!lpcCount)
+        ret = WN_BAD_POINTER;
+    if (!lpBuffer)
+        ret = WN_BAD_POINTER;
+    else if (!lpBufferSize)
+        ret = WN_BAD_POINTER;
+    else if (*lpBufferSize < sizeof(NETRESOURCEA))
+    {
+        *lpBufferSize = sizeof(NETRESOURCEA);
+        ret = WN_MORE_DATA;
+    }
+    else
+    {
+        DWORD localCount = *lpcCount, localSize = *lpBufferSize;
+        LPVOID localBuffer = HeapAlloc(GetProcessHeap(), 0, localSize);
+
+        if (localBuffer)
+        {
+            ret = WNetEnumResourceW(hEnum, &localCount, localBuffer,
+             &localSize);
+            if (ret == WN_SUCCESS || (ret == WN_MORE_DATA && localCount != -1))
+            {
+                /* FIXME: this isn't necessarily going to work in the case of
+                 * WN_MORE_DATA, because our enumerator may have moved on to
+                 * the next provider.  MSDN states that a large (16KB) buffer
+                 * size is the appropriate usage of this function, so
+                 * hopefully it won't be an issue.
+                 */
+                ret = _thunkNetResourceArrayWToA((LPNETRESOURCEW)localBuffer,
+                 &localCount, lpBuffer, lpBufferSize);
+                *lpcCount = localCount;
+            }
+            HeapFree(GetProcessHeap(), 0, localBuffer);
+        }
+        else
+            ret = WN_OUT_OF_MEMORY;
+    }
+    if (ret)
+        SetLastError(ret);
+    TRACE("Returning %ld\n", ret);
+    return ret;
+}
+
+static DWORD _countProviderBytesW(PWNetProvider provider)
+{
+    DWORD ret;
+
+    if (provider)
+    {
+        ret = sizeof(NETRESOURCEW);
+        ret += 2 * (strlenW(provider->name) + 1) * sizeof(WCHAR);
+    }
+    else
+        ret = 0;
+    return ret;
+}
+
+static DWORD _enumerateProvidersW(PWNetEnumerator enumerator, LPDWORD lpcCount,
+ LPVOID lpBuffer, LPDWORD lpBufferSize)
+{
+    DWORD ret;
+
+    if (!enumerator)
+        return WN_BAD_POINTER;
+    if (enumerator->enumType != WNET_ENUMERATOR_TYPE_GLOBAL)
+        return WN_BAD_VALUE;
+    if (!lpcCount)
+        return WN_BAD_POINTER;
+    if (!lpBuffer)
+        return WN_BAD_POINTER;
+    if (!lpBufferSize)
+        return WN_BAD_POINTER;
+    if (*lpBufferSize < sizeof(NETRESOURCEA))
+        return WN_MORE_DATA;
+
+    if (!providerTable || enumerator->providerIndex >= 
+     providerTable->numProviders)
+        ret = WN_NO_MORE_ENTRIES;
+    else
+    {
+        DWORD bytes = 0, count = 0, countLimit, i;
+        LPNETRESOURCEW resource;
+        LPWSTR strNext;
+
+        countLimit = *lpcCount == -1 ?
+         providerTable->numProviders - enumerator->providerIndex : *lpcCount;
+        while (count < countLimit && bytes < *lpBufferSize)
+        {
+            DWORD bytesNext = _countProviderBytesW(
+             &providerTable->table[count + enumerator->providerIndex]);
+
+            if (bytes + bytesNext < *lpBufferSize)
+            {
+                bytes += bytesNext;
+                count++;
+            }
+        }
+        strNext = (LPWSTR)((LPBYTE)lpBuffer + count * sizeof(NETRESOURCEW));
+        for (i = 0, resource = (LPNETRESOURCEW)lpBuffer; i < count;
+         i++, resource++)
+        {
+            resource->dwScope = RESOURCE_GLOBALNET;
+            resource->dwType = RESOURCETYPE_ANY;
+            resource->dwDisplayType = RESOURCEDISPLAYTYPE_NETWORK;
+            resource->dwUsage = RESOURCEUSAGE_CONTAINER |
+             RESOURCEUSAGE_RESERVED;
+            resource->lpLocalName = NULL;
+            resource->lpRemoteName = strNext;
+            strcpyW(resource->lpRemoteName,
+             providerTable->table[i + enumerator->providerIndex].name);
+            strNext += strlenW(resource->lpRemoteName) + 1;
+            resource->lpComment = NULL;
+            resource->lpProvider = strNext;
+            strcpyW(resource->lpProvider,
+             providerTable->table[i + enumerator->providerIndex].name);
+            strNext += strlenW(resource->lpProvider) + 1;
+        }
+        enumerator->providerIndex += count;
+        *lpcCount = count;
+        ret = count > 0 ? WN_SUCCESS : WN_MORE_DATA;
+    }
+    TRACE("Returning %ld\n", ret);
+    return ret;
+}
+
+/* Advances the enumerator (assumed to be a global enumerator) to the next
+ * provider that supports the enumeration scope passed to WNetOpenEnum.  Does
+ * not open a handle with the next provider.
+ * If the existing handle is NULL, may leave the enumerator unchanged, since
+ * the current provider may support the desired scope.
+ * If the existing handle is not NULL, closes it before moving on.
+ * Returns WN_SUCCESS on success, WN_NO_MORE_ENTRIES if there is no available
+ * provider, and another error on failure.
+ */
+static DWORD _globalEnumeratorAdvance(PWNetEnumerator enumerator)
+{
+    if (!enumerator)
+        return WN_BAD_POINTER;
+    if (enumerator->enumType != WNET_ENUMERATOR_TYPE_GLOBAL)
+        return WN_BAD_VALUE;
+    if (!providerTable || enumerator->providerIndex >=
+     providerTable->numProviders)
+        return WN_NO_MORE_ENTRIES;
+
+    if (enumerator->providerDone)
+    {
+        enumerator->providerDone = FALSE;
+        if (enumerator->handle)
+        {
+            providerTable->table[enumerator->providerIndex].closeEnum(
+             enumerator->handle);
+            enumerator->handle = NULL;
+            enumerator->providerIndex++;
+        }
+        for (; enumerator->providerIndex < providerTable->numProviders &&
+         !(enumerator->dwScope & providerTable->table
+         [enumerator->providerIndex].dwEnumScopes);
+         enumerator->providerIndex++)
+            ;
+    }
+    return enumerator->providerIndex < providerTable->numProviders ?
+     WN_SUCCESS : WN_NO_MORE_ENTRIES;
+}
+
+/* "Passes through" call to the next provider that supports the enumeration
+ * type.
+ * FIXME: if one call to a provider's enumerator succeeds while there's still
+ * space in lpBuffer, I don't call to the next provider.  The caller may not
+ * expect that it should call EnumResourceW again with a return value of
+ * WN_SUCCESS (depending what *lpcCount was to begin with).  That means strings
+ * may have to be moved around a bit, ick.
+ */
+static DWORD _enumerateGlobalPassthroughW(PWNetEnumerator enumerator,
+ LPDWORD lpcCount, LPVOID lpBuffer, LPDWORD lpBufferSize)
+{
+    DWORD ret;
+
+    if (!enumerator)
+        return WN_BAD_POINTER;
+    if (enumerator->enumType != WNET_ENUMERATOR_TYPE_GLOBAL)
+        return WN_BAD_VALUE;
+    if (!lpcCount)
+        return WN_BAD_POINTER;
+    if (!lpBuffer)
+        return WN_BAD_POINTER;
+    if (!lpBufferSize)
+        return WN_BAD_POINTER;
+    if (*lpBufferSize < sizeof(NETRESOURCEW))
+        return WN_MORE_DATA;
+
+    ret = _globalEnumeratorAdvance(enumerator);
+    if (ret == WN_SUCCESS)
+    {
+        ret = providerTable->table[enumerator->providerIndex].
+         openEnum(enumerator->dwScope, enumerator->dwType,
+         enumerator->dwUsage, enumerator->lpNet,
+         &enumerator->handle);
+        if (ret == WN_SUCCESS)
+        {
+            ret = providerTable->table[enumerator->providerIndex].
+             enumResource(enumerator->handle, lpcCount, lpBuffer,
+             lpBufferSize);
+            if (ret != WN_MORE_DATA)
+                enumerator->providerDone = TRUE;
+        }
+    }
+    TRACE("Returning %ld\n", ret);
+    return ret;
+}
+
+static DWORD _enumerateGlobalW(PWNetEnumerator enumerator, LPDWORD lpcCount,
+ LPVOID lpBuffer, LPDWORD lpBufferSize)
+{
+    DWORD ret;
+
+    if (!enumerator)
+        return WN_BAD_POINTER;
+    if (enumerator->enumType != WNET_ENUMERATOR_TYPE_GLOBAL)
+        return WN_BAD_VALUE;
+    if (!lpcCount)
+        return WN_BAD_POINTER;
+    if (!lpBuffer)
+        return WN_BAD_POINTER;
+    if (!lpBufferSize)
+        return WN_BAD_POINTER;
+    if (*lpBufferSize < sizeof(NETRESOURCEW))
+        return WN_MORE_DATA;
+    if (!providerTable)
+        return WN_NO_NETWORK;
+
+    switch (enumerator->dwScope)
+    {
+        case RESOURCE_GLOBALNET:
+            if (enumerator->lpNet)
+                ret = _enumerateGlobalPassthroughW(enumerator, lpcCount,
+                 lpBuffer, lpBufferSize);
+            else
+                ret = _enumerateProvidersW(enumerator, lpcCount, lpBuffer,
+                 lpBufferSize);
+            break;
+        case RESOURCE_CONTEXT:
+            ret = _enumerateGlobalPassthroughW(enumerator, lpcCount, lpBuffer,
+             lpBufferSize);
+            break;
+        default:
+            WARN("unexpected scope 0x%08lx\n", enumerator->dwScope);
+            ret = WN_NO_MORE_ENTRIES;
+    }
+    TRACE("Returning %ld\n", ret);
+    return ret;
+}
+
+static DWORD _enumerateProviderW(PWNetEnumerator enumerator, LPDWORD lpcCount,
+ LPVOID lpBuffer, LPDWORD lpBufferSize)
+{
+    if (!enumerator)
+        return WN_BAD_POINTER;
+    if (enumerator->enumType != WNET_ENUMERATOR_TYPE_PROVIDER)
+        return WN_BAD_VALUE;
+    if (!enumerator->handle)
+        return WN_BAD_VALUE;
+    if (!lpcCount)
+        return WN_BAD_POINTER;
+    if (!lpBuffer)
+        return WN_BAD_POINTER;
+    if (!lpBufferSize)
+        return WN_BAD_POINTER;
+    if (!providerTable)
+        return WN_NO_NETWORK;
+    if (enumerator->providerIndex >= providerTable->numProviders)
+        return WN_NO_MORE_ENTRIES;
+    if (!providerTable->table[enumerator->providerIndex].enumResource)
+        return WN_BAD_VALUE;
+    return providerTable->table[enumerator->providerIndex].enumResource(
+     enumerator->handle, lpcCount, lpBuffer, lpBufferSize);
+}
+
+static DWORD _enumerateContextW(PWNetEnumerator enumerator, LPDWORD lpcCount,
+ LPVOID lpBuffer, LPDWORD lpBufferSize)
+{
+    DWORD ret;
+    size_t cchEntireNetworkLen, bytesNeeded;
+
+    if (!enumerator)
+        return WN_BAD_POINTER;
+    if (enumerator->enumType != WNET_ENUMERATOR_TYPE_CONTEXT)
+        return WN_BAD_VALUE;
+    if (!lpcCount)
+        return WN_BAD_POINTER;
+    if (!lpBuffer)
+        return WN_BAD_POINTER;
+    if (!lpBufferSize)
+        return WN_BAD_POINTER;
+    if (!providerTable)
+        return WN_NO_NETWORK;
+
+    cchEntireNetworkLen = strlenW(providerTable->entireNetwork) + 1;
+    bytesNeeded = sizeof(NETRESOURCEW) + cchEntireNetworkLen * sizeof(WCHAR);
+    if (*lpBufferSize < bytesNeeded)
+    {
+        *lpBufferSize = bytesNeeded;
+        ret = WN_MORE_DATA;
+    }
+    else
+    {
+        LPNETRESOURCEW lpNet = (LPNETRESOURCEW)lpBuffer;
+
+        lpNet->dwScope = RESOURCE_GLOBALNET;
+        lpNet->dwType = enumerator->dwType;
+        lpNet->dwDisplayType = RESOURCEDISPLAYTYPE_ROOT;
+        lpNet->dwUsage = RESOURCEUSAGE_CONTAINER;
+        lpNet->lpLocalName = NULL;
+        lpNet->lpRemoteName = NULL;
+        lpNet->lpProvider = NULL;
+        /* odd, but correct: put comment at end of buffer, so it won't get
+         * overwritten by subsequent calls to a provider's enumResource
+         */
+        lpNet->lpComment = (LPWSTR)((LPBYTE)lpBuffer + *lpBufferSize -
+         (cchEntireNetworkLen * sizeof(WCHAR)));
+        strcpyW(lpNet->lpComment, providerTable->entireNetwork);
+        ret = WN_SUCCESS;
+    }
+    if (ret == WN_SUCCESS)
+    {
+        DWORD bufferSize = *lpBufferSize - bytesNeeded;
+
+        /* "Entire Network" entry enumerated--morph this into a global
+         * enumerator.  enumerator->lpNet continues to be NULL, since it has
+         * no meaning when the scope isn't RESOURCE_GLOBALNET.
+         */
+        enumerator->enumType = WNET_ENUMERATOR_TYPE_GLOBAL;
+        ret = _enumerateGlobalW(enumerator, lpcCount,
+         (LPBYTE)lpBuffer + bytesNeeded, &bufferSize);
+        if (ret == WN_SUCCESS)
+        {
+            /* reflect the fact that we already enumerated "Entire Network" */
+            lpcCount++;
+            *lpBufferSize = bufferSize + bytesNeeded;
+        }
+        else
+        {
+            /* the provider enumeration failed, but we already succeeded in
+             * enumerating "Entire Network"--leave type as global to allow a
+             * retry, but indicate success with a count of one.
+             */
+            ret = WN_SUCCESS;
+            *lpcCount = 1;
+            *lpBufferSize = bytesNeeded;
+        }
+    }
+    TRACE("Returning %ld\n", ret);
+    return ret;
+}
+
+/*********************************************************************
+ * WNetEnumResourceW [MPR.@]
+ */
+DWORD WINAPI WNetEnumResourceW( HANDLE hEnum, LPDWORD lpcCount,
+                                LPVOID lpBuffer, LPDWORD lpBufferSize )
+{
+    DWORD ret;
+
+    TRACE( "(%p, %p, %p, %p)\n", hEnum, lpcCount, lpBuffer, lpBufferSize );
+
+    if (!hEnum)
+        ret = WN_BAD_POINTER;
+    else if (!lpcCount)
+        ret = WN_BAD_POINTER;
+    else if (!lpBuffer)
+        ret = WN_BAD_POINTER;
+    else if (!lpBufferSize)
+        ret = WN_BAD_POINTER;
+    else if (*lpBufferSize < sizeof(NETRESOURCEW))
+    {
+        *lpBufferSize = sizeof(NETRESOURCEW);
+        ret = WN_MORE_DATA;
+    }
+    else
+    {
+        PWNetEnumerator enumerator = (PWNetEnumerator)hEnum;
+
+        switch (enumerator->enumType)
+        {
+            case WNET_ENUMERATOR_TYPE_NULL:
+                ret = WN_NO_MORE_ENTRIES;
+                break;
+            case WNET_ENUMERATOR_TYPE_GLOBAL:
+                ret = _enumerateGlobalW(enumerator, lpcCount, lpBuffer,
+                 lpBufferSize);
+                break;
+            case WNET_ENUMERATOR_TYPE_PROVIDER:
+                ret = _enumerateProviderW(enumerator, lpcCount, lpBuffer,
+                 lpBufferSize);
+                break;
+            case WNET_ENUMERATOR_TYPE_CONTEXT:
+                ret = _enumerateContextW(enumerator, lpcCount, lpBuffer,
+                 lpBufferSize);
+                break;
+            default:
+                WARN("bogus enumerator type!\n");
+                ret = WN_NO_NETWORK;
+        }
+    }
+    if (ret)
+        SetLastError(ret);
+    TRACE("Returning %ld\n", ret);
+    return ret;
+}
+
+/*********************************************************************
+ * WNetCloseEnum [MPR.@]
+ */
+DWORD WINAPI WNetCloseEnum( HANDLE hEnum )
+{
+    DWORD ret;
+
+    TRACE( "(%p)\n", hEnum );
+
+    if (hEnum)
+    {
+        PWNetEnumerator enumerator = (PWNetEnumerator)hEnum;
+
+        switch (enumerator->enumType)
+        {
+            case WNET_ENUMERATOR_TYPE_NULL:
+                ret = WN_SUCCESS;
+                break;
+            case WNET_ENUMERATOR_TYPE_GLOBAL:
+                if (enumerator->lpNet)
+                    _freeEnumNetResource(enumerator->lpNet);
+                if (enumerator->handle)
+                    providerTable->table[enumerator->providerIndex].
+                     closeEnum(enumerator->handle);
+                ret = WN_SUCCESS;
+                break;
+            case WNET_ENUMERATOR_TYPE_PROVIDER:
+                if (enumerator->handle)
+                    providerTable->table[enumerator->providerIndex].
+                     closeEnum(enumerator->handle);
+                ret = WN_SUCCESS;
+                break;
+            default:
+                WARN("bogus enumerator type!\n");
+                ret = WN_BAD_HANDLE;
+        }
+        HeapFree(GetProcessHeap(), 0, hEnum);
+    }
+    else
+        ret = WN_BAD_HANDLE;
+    if (ret)
+        SetLastError(ret);
+    TRACE("Returning %ld\n", ret);
+    return ret;
+}
+
+/*********************************************************************
+ * WNetGetResourceInformationA [MPR.@]
+ */
+DWORD WINAPI WNetGetResourceInformationA( LPNETRESOURCEA lpNetResource,
+                                          LPVOID lpBuffer, LPDWORD cbBuffer,
+                                          LPSTR *lplpSystem )
+{
+    FIXME( "(%p, %p, %p, %p): stub\n",
+           lpNetResource, lpBuffer, cbBuffer, lplpSystem );
+
+    SetLastError(WN_NO_NETWORK);
+    return WN_NO_NETWORK;
+}
+
+/*********************************************************************
+ * WNetGetResourceInformationW [MPR.@]
+ */
+DWORD WINAPI WNetGetResourceInformationW( LPNETRESOURCEW lpNetResource,
+                                          LPVOID lpBuffer, LPDWORD cbBuffer,
+                                          LPWSTR *lplpSystem )
+{
+    FIXME( "(%p, %p, %p, %p): stub\n",
+           lpNetResource, lpBuffer, cbBuffer, lplpSystem );
+
+    SetLastError(WN_NO_NETWORK);
+    return WN_NO_NETWORK;
+}
+
+/*********************************************************************
+ * WNetGetResourceParentA [MPR.@]
+ */
+DWORD WINAPI WNetGetResourceParentA( LPNETRESOURCEA lpNetResource,
+                                     LPVOID lpBuffer, LPDWORD lpBufferSize )
+{
+    FIXME( "(%p, %p, %p): stub\n",
+           lpNetResource, lpBuffer, lpBufferSize );
+
+    SetLastError(WN_NO_NETWORK);
+    return WN_NO_NETWORK;
+}
+
+/*********************************************************************
+ * WNetGetResourceParentW [MPR.@]
+ */
+DWORD WINAPI WNetGetResourceParentW( LPNETRESOURCEW lpNetResource,
+                                     LPVOID lpBuffer, LPDWORD lpBufferSize )
+{
+    FIXME( "(%p, %p, %p): stub\n",
+           lpNetResource, lpBuffer, lpBufferSize );
+
+    SetLastError(WN_NO_NETWORK);
+    return WN_NO_NETWORK;
+}
+
+
+
+/*
+ * Connection Functions
+ */
+
+/*********************************************************************
+ *  WNetAddConnectionA [MPR.@]
+ */
+DWORD WINAPI WNetAddConnectionA( LPCSTR lpRemoteName, LPCSTR lpPassword,
+                                 LPCSTR lpLocalName )
+{
+    FIXME( "(%s, %p, %s): stub\n",
+           debugstr_a(lpRemoteName), lpPassword, debugstr_a(lpLocalName) );
+
+    SetLastError(WN_NO_NETWORK);
+    return WN_NO_NETWORK;
+}
+
+/*********************************************************************
+ *  WNetAddConnectionW [MPR.@]
+ */
+DWORD WINAPI WNetAddConnectionW( LPCWSTR lpRemoteName, LPCWSTR lpPassword,
+                                 LPCWSTR lpLocalName )
+{
+    FIXME( "(%s, %p, %s): stub\n",
+           debugstr_w(lpRemoteName), lpPassword, debugstr_w(lpLocalName) );
+
+    SetLastError(WN_NO_NETWORK);
+    return WN_NO_NETWORK;
+}
+
+/*********************************************************************
+ *  WNetAddConnection2A [MPR.@]
+ */
+DWORD WINAPI WNetAddConnection2A( LPNETRESOURCEA lpNetResource,
+                                  LPCSTR lpPassword, LPCSTR lpUserID,
+                                  DWORD dwFlags )
+{
+    FIXME( "(%p, %p, %s, 0x%08lX): stub\n",
+           lpNetResource, lpPassword, debugstr_a(lpUserID), dwFlags );
+
+    SetLastError(WN_NO_NETWORK);
+    return WN_NO_NETWORK;
+}
+
+/*********************************************************************
+ * WNetAddConnection2W [MPR.@]
+ */
+DWORD WINAPI WNetAddConnection2W( LPNETRESOURCEW lpNetResource,
+                                  LPCWSTR lpPassword, LPCWSTR lpUserID,
+                                  DWORD dwFlags )
+{
+    FIXME( "(%p, %p, %s, 0x%08lX): stub\n",
+           lpNetResource, lpPassword, debugstr_w(lpUserID), dwFlags );
+
+    SetLastError(WN_NO_NETWORK);
+    return WN_NO_NETWORK;
+}
+
+/*********************************************************************
+ * WNetAddConnection3A [MPR.@]
+ */
+DWORD WINAPI WNetAddConnection3A( HWND hwndOwner, LPNETRESOURCEA lpNetResource,
+                                  LPCSTR lpPassword, LPCSTR lpUserID,
+                                  DWORD dwFlags )
+{
+    FIXME( "(%p, %p, %p, %s, 0x%08lX), stub\n",
+           hwndOwner, lpNetResource, lpPassword, debugstr_a(lpUserID), dwFlags );
+
+    SetLastError(WN_NO_NETWORK);
+    return WN_NO_NETWORK;
+}
+
+/*********************************************************************
+ * WNetAddConnection3W [MPR.@]
+ */
+DWORD WINAPI WNetAddConnection3W( HWND hwndOwner, LPNETRESOURCEW lpNetResource,
+                                  LPCWSTR lpPassword, LPCWSTR lpUserID,
+                                  DWORD dwFlags )
+{
+    FIXME( "(%p, %p, %p, %s, 0x%08lX), stub\n",
+           hwndOwner, lpNetResource, lpPassword, debugstr_w(lpUserID), dwFlags );
+
+    SetLastError(WN_NO_NETWORK);
+    return WN_NO_NETWORK;
+}
+
+/*****************************************************************
+ *  WNetUseConnectionA [MPR.@]
+ */
+DWORD WINAPI WNetUseConnectionA( HWND hwndOwner, LPNETRESOURCEA lpNetResource,
+                                 LPCSTR lpPassword, LPCSTR lpUserID, DWORD dwFlags,
+                                 LPSTR lpAccessName, LPDWORD lpBufferSize,
+                                 LPDWORD lpResult )
+{
+    FIXME( "(%p, %p, %p, %s, 0x%08lX, %s, %p, %p), stub\n",
+           hwndOwner, lpNetResource, lpPassword, debugstr_a(lpUserID), dwFlags,
+           debugstr_a(lpAccessName), lpBufferSize, lpResult );
+
+    SetLastError(WN_NO_NETWORK);
+    return WN_NO_NETWORK;
+}
+
+/*****************************************************************
+ *  WNetUseConnectionW [MPR.@]
+ */
+DWORD WINAPI WNetUseConnectionW( HWND hwndOwner, LPNETRESOURCEW lpNetResource,
+                                 LPCWSTR lpPassword, LPCWSTR lpUserID, DWORD dwFlags,
+                                 LPWSTR lpAccessName, LPDWORD lpBufferSize,
+                                 LPDWORD lpResult )
+{
+    FIXME( "(%p, %p, %p, %s, 0x%08lX, %s, %p, %p), stub\n",
+           hwndOwner, lpNetResource, lpPassword, debugstr_w(lpUserID), dwFlags,
+           debugstr_w(lpAccessName), lpBufferSize, lpResult );
+
+    SetLastError(WN_NO_NETWORK);
+    return WN_NO_NETWORK;
+}
+
+/*********************************************************************
+ *  WNetCancelConnectionA [MPR.@]
+ */
+DWORD WINAPI WNetCancelConnectionA( LPCSTR lpName, BOOL fForce )
+{
+    FIXME( "(%s, %d), stub\n", debugstr_a(lpName), fForce );
+
+    return WN_SUCCESS;
+}
+
+/*********************************************************************
+ *  WNetCancelConnectionW [MPR.@]
+ */
+DWORD WINAPI WNetCancelConnectionW( LPCWSTR lpName, BOOL fForce )
+{
+    FIXME( "(%s, %d), stub\n", debugstr_w(lpName), fForce );
+
+    return WN_SUCCESS;
+}
+
+/*********************************************************************
+ *  WNetCancelConnection2A [MPR.@]
+ */
+DWORD WINAPI WNetCancelConnection2A( LPCSTR lpName, DWORD dwFlags, BOOL fForce )
+{
+    FIXME( "(%s, %08lX, %d), stub\n", debugstr_a(lpName), dwFlags, fForce );
+
+    return WN_SUCCESS;
+}
+
+/*********************************************************************
+ *  WNetCancelConnection2W [MPR.@]
+ */
+DWORD WINAPI WNetCancelConnection2W( LPCWSTR lpName, DWORD dwFlags, BOOL fForce )
+{
+    FIXME( "(%s, %08lX, %d), stub\n", debugstr_w(lpName), dwFlags, fForce );
+
+    return WN_SUCCESS;
+}
+
+/*****************************************************************
+ *  WNetRestoreConnectionA [MPR.@]
+ */
+DWORD WINAPI WNetRestoreConnectionA( HWND hwndOwner, LPSTR lpszDevice )
+{
+    FIXME( "(%p, %s), stub\n", hwndOwner, debugstr_a(lpszDevice) );
+
+    SetLastError(WN_NO_NETWORK);
+    return WN_NO_NETWORK;
+}
+
+/*****************************************************************
+ *  WNetRestoreConnectionW [MPR.@]
+ */
+DWORD WINAPI WNetRestoreConnectionW( HWND hwndOwner, LPWSTR lpszDevice )
+{
+    FIXME( "(%p, %s), stub\n", hwndOwner, debugstr_w(lpszDevice) );
+
+    SetLastError(WN_NO_NETWORK);
+    return WN_NO_NETWORK;
+}
+
+/**************************************************************************
+ * WNetGetConnectionA [MPR.@]
+ *
+ * RETURNS
+ * - WN_BAD_LOCALNAME     lpLocalName makes no sense
+ * - WN_NOT_CONNECTED     drive is a local drive
+ * - WN_MORE_DATA         buffer isn't big enough
+ * - WN_SUCCESS           success (net path in buffer)
+ *
+ * FIXME: need to test return values under different errors
+ */
+DWORD WINAPI WNetGetConnectionA( LPCSTR lpLocalName,
+                                 LPSTR lpRemoteName, LPDWORD lpBufferSize )
+{
+    DWORD ret;
+
+    if (!lpLocalName)
+        ret = WN_BAD_POINTER;
+    else if (!lpRemoteName)
+        ret = WN_BAD_POINTER;
+    else if (!lpBufferSize)
+        ret = WN_BAD_POINTER;
+    else
+    {
+        int len = MultiByteToWideChar(CP_ACP, 0, lpLocalName, -1, NULL, 0);
+
+        if (len)
+        {
+            PWSTR wideLocalName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
+
+            if (wideLocalName)
+            {
+                WCHAR wideRemoteStatic[MAX_PATH];
+                DWORD wideRemoteSize = sizeof(wideRemoteStatic) / sizeof(WCHAR);
+
+                MultiByteToWideChar(CP_ACP, 0, lpLocalName, -1, wideLocalName, len);
+
+                /* try once without memory allocation */
+                ret = WNetGetConnectionW(wideLocalName, wideRemoteStatic,
+                 &wideRemoteSize);
+                if (ret == WN_SUCCESS)
+                {
+                    int len = WideCharToMultiByte(CP_ACP, 0, wideRemoteStatic,
+                     -1, NULL, 0, NULL, NULL);
+
+                    if (len <= *lpBufferSize)
+                    {
+                        WideCharToMultiByte(CP_ACP, 0, wideRemoteStatic, -1,
+                         lpRemoteName, *lpBufferSize, NULL, NULL);
+                        ret = WN_SUCCESS;
+                    }
+                    else
+                    {
+                        *lpBufferSize = len;
+                        ret = WN_MORE_DATA;
+                    }
+                }
+                else if (ret == WN_MORE_DATA)
+                {
+                    PWSTR wideRemote = HeapAlloc(GetProcessHeap(), 0,
+                     wideRemoteSize * sizeof(WCHAR));
+
+                    if (wideRemote)
+                    {
+                        ret = WNetGetConnectionW(wideLocalName, wideRemote,
+                         &wideRemoteSize);
+                        if (ret == WN_SUCCESS)
+                        {
+                            if (len <= *lpBufferSize)
+                            {
+                                WideCharToMultiByte(CP_ACP, 0, wideRemoteStatic,
+                                 -1, lpRemoteName, *lpBufferSize, NULL, NULL);
+                                ret = WN_SUCCESS;
+                            }
+                            else
+                            {
+                                *lpBufferSize = len;
+                                ret = WN_MORE_DATA;
+                            }
+                        }
+                        HeapFree(GetProcessHeap(), 0, wideRemote);
+                    }
+                    else
+                        ret = WN_OUT_OF_MEMORY;
+                }
+                HeapFree(GetProcessHeap(), 0, wideLocalName);
+            }
+            else
+                ret = WN_OUT_OF_MEMORY;
+        }
+        else
+            ret = WN_BAD_LOCALNAME;
+    }
+    if (ret)
+        SetLastError(ret);
+    TRACE("Returning %ld\n", ret);
+    return ret;
+}
+
+/**************************************************************************
+ * WNetGetConnectionW [MPR.@]
+ *
+ * FIXME: need to test return values under different errors
+ */
+DWORD WINAPI WNetGetConnectionW( LPCWSTR lpLocalName,
+                                 LPWSTR lpRemoteName, LPDWORD lpBufferSize )
+{
+    DWORD ret;
+
+    TRACE("(%s, %p, %p)\n", debugstr_w(lpLocalName), lpRemoteName,
+     lpBufferSize);
+
+    if (!lpLocalName)
+        ret = WN_BAD_POINTER;
+    else if (!lpRemoteName)
+        ret = WN_BAD_POINTER;
+    else if (!lpBufferSize)
+        ret = WN_BAD_POINTER;
+    else if (!lpLocalName[0])
+        ret = WN_BAD_LOCALNAME;
+    else
+    {
+        if (lpLocalName[1] == ':')
+        {
+            switch(GetDriveTypeW(lpLocalName))
+            {
+            case DRIVE_REMOTE:
+            {
+                WCHAR remote[MAX_PATH];
+                if (!QueryDosDeviceW( lpLocalName, remote, MAX_PATH )) remote[0] = 0;
+                if (strlenW(remote) + 1 > *lpBufferSize)
+                {
+                    *lpBufferSize = strlenW(remote) + 1;
+                    ret = WN_MORE_DATA;
+                }
+                else
+                {
+                    strcpyW( lpRemoteName, remote );
+                    *lpBufferSize = strlenW(lpRemoteName) + 1;
+                    ret = WN_SUCCESS;
+                }
+                break;
+            }
+            case DRIVE_REMOVABLE:
+            case DRIVE_FIXED:
+            case DRIVE_CDROM:
+                TRACE("file is local\n");
+                ret = WN_NOT_CONNECTED;
+                break;
+            default:
+                ret = WN_BAD_LOCALNAME;
+            }
+        }
+        else
+            ret = WN_BAD_LOCALNAME;
+    }
+    if (ret)
+        SetLastError(ret);
+    TRACE("Returning %ld\n", ret);
+    return ret;
+}
+
+/**************************************************************************
+ * WNetSetConnectionA [MPR.@]
+ */
+DWORD WINAPI WNetSetConnectionA( LPCSTR lpName, DWORD dwProperty,
+                                 LPVOID pvValue )
+{
+    FIXME( "(%s, %08lX, %p): stub\n", debugstr_a(lpName), dwProperty, pvValue );
+
+    SetLastError(WN_NO_NETWORK);
+    return WN_NO_NETWORK;
+}
+
+/**************************************************************************
+ * WNetSetConnectionW [MPR.@]
+ */
+DWORD WINAPI WNetSetConnectionW( LPCWSTR lpName, DWORD dwProperty,
+                                 LPVOID pvValue )
+{
+    FIXME( "(%s, %08lX, %p): stub\n", debugstr_w(lpName), dwProperty, pvValue );
+
+    SetLastError(WN_NO_NETWORK);
+    return WN_NO_NETWORK;
+}
+
+/*****************************************************************
+ * WNetGetUniversalNameA [MPR.@]
+ */
+DWORD WINAPI WNetGetUniversalNameA ( LPCSTR lpLocalPath, DWORD dwInfoLevel,
+                                     LPVOID lpBuffer, LPDWORD lpBufferSize )
+{
+    FIXME( "(%s, 0x%08lX, %p, %p): stub\n",
+           debugstr_a(lpLocalPath), dwInfoLevel, lpBuffer, lpBufferSize);
+
+    SetLastError(WN_NO_NETWORK);
+    return WN_NO_NETWORK;
+}
+
+/*****************************************************************
+ * WNetGetUniversalNameW [MPR.@]
+ */
+DWORD WINAPI WNetGetUniversalNameW ( LPCWSTR lpLocalPath, DWORD dwInfoLevel,
+                                     LPVOID lpBuffer, LPDWORD lpBufferSize )
+{
+    FIXME( "(%s, 0x%08lX, %p, %p): stub\n",
+           debugstr_w(lpLocalPath), dwInfoLevel, lpBuffer, lpBufferSize);
+
+    SetLastError(WN_NO_NETWORK);
+    return WN_NO_NETWORK;
+}
+
+
+
+/*
+ * Other Functions
+ */
+
+/**************************************************************************
+ * WNetGetUserA [MPR.@]
+ *
+ * FIXME: we should not return ourselves, but the owner of the drive lpName
+ */
+DWORD WINAPI WNetGetUserA( LPCSTR lpName, LPSTR lpUserID, LPDWORD lpBufferSize )
+{
+    if (GetUserNameA( lpUserID, lpBufferSize )) return WN_SUCCESS;
+    return GetLastError();
+}
+
+/*****************************************************************
+ * WNetGetUserW [MPR.@]
+ *
+ * FIXME: we should not return ourselves, but the owner of the drive lpName
+ */
+DWORD WINAPI WNetGetUserW( LPCWSTR lpName, LPWSTR lpUserID, LPDWORD lpBufferSize )
+{
+    if (GetUserNameW( lpUserID, lpBufferSize )) return WN_SUCCESS;
+    return GetLastError();
+}
+
+/*********************************************************************
+ * WNetConnectionDialog [MPR.@]
+ */
+DWORD WINAPI WNetConnectionDialog( HWND hwnd, DWORD dwType )
+{
+    FIXME( "(%p, %08lX): stub\n", hwnd, dwType );
+
+    SetLastError(WN_NO_NETWORK);
+    return WN_NO_NETWORK;
+}
+
+/*********************************************************************
+ * WNetConnectionDialog1A [MPR.@]
+ */
+DWORD WINAPI WNetConnectionDialog1A( LPCONNECTDLGSTRUCTA lpConnDlgStruct )
+{
+    FIXME( "(%p): stub\n", lpConnDlgStruct );
+
+    SetLastError(WN_NO_NETWORK);
+    return WN_NO_NETWORK;
+}
+
+/*********************************************************************
+ * WNetConnectionDialog1W [MPR.@]
+ */
+DWORD WINAPI WNetConnectionDialog1W( LPCONNECTDLGSTRUCTW lpConnDlgStruct )
+{
+    FIXME( "(%p): stub\n", lpConnDlgStruct );
+
+    SetLastError(WN_NO_NETWORK);
+    return WN_NO_NETWORK;
+}
+
+/*********************************************************************
+ * WNetDisconnectDialog [MPR.@]
+ */
+DWORD WINAPI WNetDisconnectDialog( HWND hwnd, DWORD dwType )
+{
+    FIXME( "(%p, %08lX): stub\n", hwnd, dwType );
+
+    SetLastError(WN_NO_NETWORK);
+    return WN_NO_NETWORK;
+}
+
+/*********************************************************************
+ * WNetDisconnectDialog1A [MPR.@]
+ */
+DWORD WINAPI WNetDisconnectDialog1A( LPDISCDLGSTRUCTA lpConnDlgStruct )
+{
+    FIXME( "(%p): stub\n", lpConnDlgStruct );
+
+    SetLastError(WN_NO_NETWORK);
+    return WN_NO_NETWORK;
+}
+
+/*********************************************************************
+ * WNetDisconnectDialog1W [MPR.@]
+ */
+DWORD WINAPI WNetDisconnectDialog1W( LPDISCDLGSTRUCTW lpConnDlgStruct )
+{
+    FIXME( "(%p): stub\n", lpConnDlgStruct );
+
+    SetLastError(WN_NO_NETWORK);
+    return WN_NO_NETWORK;
+}
+
+/*********************************************************************
+ * WNetGetLastErrorA [MPR.@]
+ */
+DWORD WINAPI WNetGetLastErrorA( LPDWORD lpError,
+                                LPSTR lpErrorBuf, DWORD nErrorBufSize,
+                                LPSTR lpNameBuf, DWORD nNameBufSize )
+{
+    FIXME( "(%p, %p, %ld, %p, %ld): stub\n",
+           lpError, lpErrorBuf, nErrorBufSize, lpNameBuf, nNameBufSize );
+
+    SetLastError(WN_NO_NETWORK);
+    return WN_NO_NETWORK;
+}
+
+/*********************************************************************
+ * WNetGetLastErrorW [MPR.@]
+ */
+DWORD WINAPI WNetGetLastErrorW( LPDWORD lpError,
+                                LPWSTR lpErrorBuf, DWORD nErrorBufSize,
+                         LPWSTR lpNameBuf, DWORD nNameBufSize )
+{
+    FIXME( "(%p, %p, %ld, %p, %ld): stub\n",
+           lpError, lpErrorBuf, nErrorBufSize, lpNameBuf, nNameBufSize );
+
+    SetLastError(WN_NO_NETWORK);
+    return WN_NO_NETWORK;
+}
+
+/*********************************************************************
+ * WNetGetNetworkInformationA [MPR.@]
+ */
+DWORD WINAPI WNetGetNetworkInformationA( LPCSTR lpProvider,
+                                         LPNETINFOSTRUCT lpNetInfoStruct )
+{
+    DWORD ret;
+
+    TRACE( "(%s, %p)\n", debugstr_a(lpProvider), lpNetInfoStruct );
+
+    if (!lpProvider)
+        ret = WN_BAD_POINTER;
+    else
+    {
+        int len;
+
+        len = MultiByteToWideChar(CP_ACP, 0, lpProvider, -1, NULL, 0);
+        if (len)
+        {
+            LPWSTR wideProvider = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
+
+            if (wideProvider)
+            {
+                MultiByteToWideChar(CP_ACP, 0, lpProvider, -1, wideProvider,
+                 len);
+                ret = WNetGetNetworkInformationW(wideProvider, lpNetInfoStruct);
+                HeapFree(GetProcessHeap(), 0, wideProvider);
+            }
+            else
+                ret = WN_OUT_OF_MEMORY;
+        }
+        else
+            ret = GetLastError();
+    }
+    if (ret)
+        SetLastError(ret);
+    TRACE("Returning %ld\n", ret);
+    return ret;
+}
+
+/*********************************************************************
+ * WNetGetNetworkInformationW [MPR.@]
+ */
+DWORD WINAPI WNetGetNetworkInformationW( LPCWSTR lpProvider,
+                                         LPNETINFOSTRUCT lpNetInfoStruct )
+{
+    DWORD ret;
+
+    TRACE( "(%s, %p)\n", debugstr_w(lpProvider), lpNetInfoStruct );
+
+    if (!lpProvider)
+        ret = WN_BAD_POINTER;
+    else if (!lpNetInfoStruct)
+        ret = WN_BAD_POINTER;
+    else if (lpNetInfoStruct->cbStructure < sizeof(NETINFOSTRUCT))
+        ret = WN_BAD_VALUE;
+    else
+    {
+        if (providerTable && providerTable->numProviders)
+        {
+            DWORD providerIndex = _findProviderIndexW(lpProvider);
+
+            if (providerIndex != BAD_PROVIDER_INDEX)
+            {
+                lpNetInfoStruct->cbStructure = sizeof(NETINFOSTRUCT);
+                lpNetInfoStruct->dwProviderVersion =
+                 providerTable->table[providerIndex].dwSpecVersion;
+                lpNetInfoStruct->dwStatus = NO_ERROR;
+                lpNetInfoStruct->dwCharacteristics = 0;
+                lpNetInfoStruct->dwHandle = (ULONG_PTR)NULL;
+                lpNetInfoStruct->wNetType =
+                 HIWORD(providerTable->table[providerIndex].dwNetType);
+                lpNetInfoStruct->dwPrinters = -1;
+                lpNetInfoStruct->dwDrives = -1;
+                ret = WN_SUCCESS;
+            }
+            else
+                ret = WN_BAD_PROVIDER;
+        }
+        else
+            ret = WN_NO_NETWORK;
+    }
+    if (ret)
+        SetLastError(ret);
+    TRACE("Returning %ld\n", ret);
+    return ret;
+}
+
+/*****************************************************************
+ *  WNetGetProviderNameA [MPR.@]
+ */
+DWORD WINAPI WNetGetProviderNameA( DWORD dwNetType,
+                                   LPSTR lpProvider, LPDWORD lpBufferSize )
+{
+    DWORD ret;
+
+    TRACE("(0x%08lx, %s, %p)\n", dwNetType, debugstr_a(lpProvider),
+     lpBufferSize);
+
+    if (!lpProvider)
+        ret = WN_BAD_POINTER;
+    else if (!lpBufferSize)
+        ret = WN_BAD_POINTER;
+    else
+    {
+        if (providerTable)
+        {
+            DWORD i;
+
+            ret = WN_NO_NETWORK;
+            for (i = 0; i < providerTable->numProviders &&
+             HIWORD(providerTable->table[i].dwNetType) != HIWORD(dwNetType);
+             i++)
+                ;
+            if (i < providerTable->numProviders)
+            {
+                DWORD sizeNeeded = WideCharToMultiByte(CP_ACP, 0,
+                 providerTable->table[i].name, -1, NULL, 0, NULL, NULL);
+
+                if (*lpBufferSize < sizeNeeded)
+                {
+                    *lpBufferSize = sizeNeeded;
+                    ret = WN_MORE_DATA;
+                }
+                else
+                {
+                    WideCharToMultiByte(CP_ACP, 0, providerTable->table[i].name,
+                     -1, lpProvider, *lpBufferSize, NULL, NULL);
+                    ret = WN_SUCCESS;
+                    /* FIXME: is *lpBufferSize set to the number of characters
+                     * copied? */
+                }
+            }
+        }
+        else
+            ret = WN_NO_NETWORK;
+    }
+    if (ret)
+        SetLastError(ret);
+    TRACE("Returning %ld\n", ret);
+    return ret;
+}
+
+/*****************************************************************
+ *  WNetGetProviderNameW [MPR.@]
+ */
+DWORD WINAPI WNetGetProviderNameW( DWORD dwNetType,
+                                   LPWSTR lpProvider, LPDWORD lpBufferSize )
+{
+    DWORD ret;
+
+    TRACE("(0x%08lx, %s, %p)\n", dwNetType, debugstr_w(lpProvider),
+     lpBufferSize);
+
+    if (!lpProvider)
+        ret = WN_BAD_POINTER;
+    else if (!lpBufferSize)
+        ret = WN_BAD_POINTER;
+    else
+    {
+        if (providerTable)
+        {
+            DWORD i;
+
+            ret = WN_NO_NETWORK;
+            for (i = 0; i < providerTable->numProviders &&
+             HIWORD(providerTable->table[i].dwNetType) != HIWORD(dwNetType);
+             i++)
+                ;
+            if (i < providerTable->numProviders)
+            {
+                DWORD sizeNeeded = strlenW(providerTable->table[i].name) + 1;
+
+                if (*lpBufferSize < sizeNeeded)
+                {
+                    *lpBufferSize = sizeNeeded;
+                    ret = WN_MORE_DATA;
+                }
+                else
+                {
+                    strcpyW(lpProvider, providerTable->table[i].name);
+                    ret = WN_SUCCESS;
+                    /* FIXME: is *lpBufferSize set to the number of characters
+                     * copied? */
+                }
+            }
+        }
+        else
+            ret = WN_NO_NETWORK;
+    }
+    if (ret)
+        SetLastError(ret);
+    TRACE("Returning %ld\n", ret);
+    return ret;
+}
diff --git a/reactos/dll/mpr/wnetpriv.h b/reactos/dll/mpr/wnetpriv.h
new file mode 100644 (file)
index 0000000..7a275de
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * WNet private definitions
+ *
+ * Copyright (C) 2004 Juan Lang
+ *
+ * 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
+ */
+
+#ifndef __WNET_PRIV_H__
+#define __WNET_PRIV_H__
+
+void wnetInit(HINSTANCE hInstDll);
+void wnetFree(void);
+
+#endif /* ndef __WNET_PRIV_H__ */
diff --git a/reactos/dll/msacm/Makefile.in b/reactos/dll/msacm/Makefile.in
new file mode 100644 (file)
index 0000000..247b681
--- /dev/null
@@ -0,0 +1,29 @@
+TOPSRCDIR = @top_srcdir@
+TOPOBJDIR = ../..
+SRCDIR    = @srcdir@
+VPATH     = @srcdir@
+MODULE    = msacm32.dll
+IMPORTS   = winmm user32 advapi32 kernel32
+ALTNAMES  = msacm.dll
+
+SPEC_SRCS16 = $(ALTNAMES:.dll=.spec)
+
+C_SRCS = \
+       driver.c \
+       filter.c \
+       format.c \
+       internal.c \
+       msacm32_main.c \
+       pcmconverter.c \
+       stream.c
+
+C_SRCS16 = \
+       msacm_main.c
+
+RC_SRCS = msacm.rc
+
+SUBDIRS = tests
+
+@MAKE_DLL_RULES@
+
+### Dependencies:
diff --git a/reactos/dll/msacm/driver.c b/reactos/dll/msacm/driver.c
new file mode 100644 (file)
index 0000000..dab014f
--- /dev/null
@@ -0,0 +1,493 @@
+/* -*- tab-width: 8; c-basic-offset: 4 -*- */
+
+/*
+ *      MSACM32 library
+ *
+ *      Copyright 1998  Patrik Stridvall
+ *               1999  Eric Pouech
+ *
+ * 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 "wine/port.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "winnls.h"
+#include "winreg.h"
+#include "mmsystem.h"
+#include "mmreg.h"
+#include "msacm.h"
+#include "msacmdrv.h"
+#include "wineacm.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msacm);
+
+/***********************************************************************
+ *           acmDriverAddA (MSACM32.@)
+ */
+MMRESULT WINAPI acmDriverAddA(PHACMDRIVERID phadid, HINSTANCE hinstModule,
+                             LPARAM lParam, DWORD dwPriority, DWORD fdwAdd)
+{
+    TRACE("(%p, %p, %08lx, %08lx, %08lx)\n",
+          phadid, hinstModule, lParam, dwPriority, fdwAdd);
+
+    if (!phadid) {
+        WARN("invalid parameter\n");
+       return MMSYSERR_INVALPARAM;
+    }
+
+    /* Check if any unknown flags */
+    if (fdwAdd &
+       ~(ACM_DRIVERADDF_FUNCTION|ACM_DRIVERADDF_NOTIFYHWND|
+         ACM_DRIVERADDF_GLOBAL)) {
+        WARN("invalid flag\n");
+       return MMSYSERR_INVALFLAG;
+    }
+
+    /* Check if any incompatible flags */
+    if ((fdwAdd & ACM_DRIVERADDF_FUNCTION) &&
+       (fdwAdd & ACM_DRIVERADDF_NOTIFYHWND)) {
+        WARN("invalid flag\n");
+       return MMSYSERR_INVALFLAG;
+    }
+
+    /* FIXME: in fact, should GetModuleFileName(hinstModule) and do a
+     * LoadDriver on it, to be sure we can call SendDriverMessage on the
+     * hDrvr handle.
+     */
+    *phadid = (HACMDRIVERID) MSACM_RegisterDriver(NULL, NULL, hinstModule);
+
+    /* FIXME: lParam, dwPriority and fdwAdd ignored */
+
+    return MMSYSERR_NOERROR;
+}
+
+/***********************************************************************
+ *           acmDriverAddW (MSACM32.@)
+ * FIXME
+ *   Not implemented
+ */
+MMRESULT WINAPI acmDriverAddW(PHACMDRIVERID phadid, HINSTANCE hinstModule,
+                             LPARAM lParam, DWORD dwPriority, DWORD fdwAdd)
+{
+    FIXME("(%p, %p, %ld, %ld, %ld): stub\n",
+         phadid, hinstModule, lParam, dwPriority, fdwAdd);
+
+    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+    return MMSYSERR_ERROR;
+}
+
+/***********************************************************************
+ *           acmDriverClose (MSACM32.@)
+ */
+MMRESULT WINAPI acmDriverClose(HACMDRIVER had, DWORD fdwClose)
+{
+    PWINE_ACMDRIVER    pad;
+    PWINE_ACMDRIVERID  padid;
+    PWINE_ACMDRIVER*   tpad;
+
+    TRACE("(%p, %08lx)\n", had, fdwClose);
+
+    if (fdwClose) {
+        WARN("invalid flag\n");
+       return MMSYSERR_INVALFLAG;
+    }
+
+    pad = MSACM_GetDriver(had);
+    if (!pad) {
+        WARN("invalid handle\n");
+       return MMSYSERR_INVALHANDLE;
+    }
+
+    padid = pad->obj.pACMDriverID;
+
+    /* remove driver from list */
+    for (tpad = &(padid->pACMDriverList); *tpad; *tpad = (*tpad)->pNextACMDriver) {
+       if (*tpad == pad) {
+           *tpad = (*tpad)->pNextACMDriver;
+           break;
+       }
+    }
+
+    /* close driver if it has been opened */
+    if (pad->hDrvr && !padid->hInstModule)
+       CloseDriver(pad->hDrvr, 0, 0);
+
+    HeapFree(MSACM_hHeap, 0, pad);
+
+    return MMSYSERR_NOERROR;
+}
+
+/***********************************************************************
+ *           acmDriverDetailsA (MSACM32.@)
+ */
+MMRESULT WINAPI acmDriverDetailsA(HACMDRIVERID hadid, PACMDRIVERDETAILSA padd, DWORD fdwDetails)
+{
+    MMRESULT mmr;
+    ACMDRIVERDETAILSW  addw;
+
+    TRACE("(%p, %p, %08lx)\n", hadid, padd, fdwDetails);
+
+    if (!padd) {
+        WARN("invalid parameter\n");
+        return MMSYSERR_INVALPARAM;
+    }
+
+    if (padd->cbStruct < 4) {
+        WARN("invalid parameter\n");
+        return MMSYSERR_INVALPARAM;
+    }
+
+    addw.cbStruct = sizeof(addw);
+    mmr = acmDriverDetailsW(hadid, &addw, fdwDetails);
+    if (mmr == 0) {
+        ACMDRIVERDETAILSA padda;
+
+        padda.fccType = addw.fccType;
+        padda.fccComp = addw.fccComp;
+        padda.wMid = addw.wMid;
+        padda.wPid = addw.wPid;
+        padda.vdwACM = addw.vdwACM;
+        padda.vdwDriver = addw.vdwDriver;
+        padda.fdwSupport = addw.fdwSupport;
+        padda.cFormatTags = addw.cFormatTags;
+        padda.cFilterTags = addw.cFilterTags;
+        padda.hicon = addw.hicon;
+        WideCharToMultiByte( CP_ACP, 0, addw.szShortName, -1, padda.szShortName,
+                             sizeof(padda.szShortName), NULL, NULL );
+        WideCharToMultiByte( CP_ACP, 0, addw.szLongName, -1, padda.szLongName,
+                             sizeof(padda.szLongName), NULL, NULL );
+        WideCharToMultiByte( CP_ACP, 0, addw.szCopyright, -1, padda.szCopyright,
+                             sizeof(padda.szCopyright), NULL, NULL );
+        WideCharToMultiByte( CP_ACP, 0, addw.szLicensing, -1, padda.szLicensing,
+                             sizeof(padda.szLicensing), NULL, NULL );
+        WideCharToMultiByte( CP_ACP, 0, addw.szFeatures, -1, padda.szFeatures,
+                             sizeof(padda.szFeatures), NULL, NULL );
+        memcpy(padd, &padda, min(padd->cbStruct, sizeof(*padd)));
+    }
+    return mmr;
+}
+
+/***********************************************************************
+ *           acmDriverDetailsW (MSACM32.@)
+ */
+MMRESULT WINAPI acmDriverDetailsW(HACMDRIVERID hadid, PACMDRIVERDETAILSW padd, DWORD fdwDetails)
+{
+    HACMDRIVER acmDrvr;
+    MMRESULT mmr;
+
+    TRACE("(%p, %p, %08lx)\n", hadid, padd, fdwDetails);
+
+    if (!padd) {
+        WARN("invalid parameter\n");
+        return MMSYSERR_INVALPARAM;
+    }
+
+    if (padd->cbStruct < 4) {
+        WARN("invalid parameter\n");
+        return MMSYSERR_INVALPARAM;
+    }
+
+    if (fdwDetails) {
+        WARN("invalid flag\n");
+       return MMSYSERR_INVALFLAG;
+    }
+
+    mmr = acmDriverOpen(&acmDrvr, hadid, 0);
+    if (mmr == MMSYSERR_NOERROR) {
+        ACMDRIVERDETAILSW paddw;
+        mmr = (MMRESULT)MSACM_Message(acmDrvr, ACMDM_DRIVER_DETAILS, (LPARAM)&paddw,  0);
+
+       acmDriverClose(acmDrvr, 0);
+        memcpy(padd, &paddw, min(padd->cbStruct, sizeof(*padd)));
+    }
+
+    return mmr;
+}
+
+/***********************************************************************
+ *           acmDriverEnum (MSACM32.@)
+ */
+MMRESULT WINAPI acmDriverEnum(ACMDRIVERENUMCB fnCallback, DWORD dwInstance, DWORD fdwEnum)
+{
+    PWINE_ACMDRIVERID  padid;
+    DWORD              fdwSupport;
+
+    TRACE("(%p, %08lx, %08lx)\n", fnCallback, dwInstance, fdwEnum);
+
+    if (!fnCallback) {
+        WARN("invalid parameter\n");
+        return MMSYSERR_INVALPARAM;
+    }
+
+    if (fdwEnum & ~(ACM_DRIVERENUMF_NOLOCAL|ACM_DRIVERENUMF_DISABLED)) {
+        WARN("invalid flag\n");
+       return MMSYSERR_INVALFLAG;
+    }
+
+    for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) {
+       fdwSupport = padid->fdwSupport;
+
+       if (padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) {
+           if (fdwEnum & ACM_DRIVERENUMF_DISABLED)
+               fdwSupport |= ACMDRIVERDETAILS_SUPPORTF_DISABLED;
+           else
+               continue;
+       }
+       if (!(*fnCallback)((HACMDRIVERID)padid, dwInstance, fdwSupport))
+           break;
+    }
+
+    return MMSYSERR_NOERROR;
+}
+
+/***********************************************************************
+ *           acmDriverID (MSACM32.@)
+ */
+MMRESULT WINAPI acmDriverID(HACMOBJ hao, PHACMDRIVERID phadid, DWORD fdwDriverID)
+{
+    PWINE_ACMOBJ pao;
+
+    TRACE("(%p, %p, %08lx)\n", hao, phadid, fdwDriverID);
+
+    if (fdwDriverID) {
+        WARN("invalid flag\n");
+       return MMSYSERR_INVALFLAG;
+    }
+
+    pao = MSACM_GetObj(hao, WINE_ACMOBJ_DONTCARE);
+    if (!pao) {
+        WARN("invalid handle\n");
+       return MMSYSERR_INVALHANDLE;
+    }
+
+    if (!phadid) {
+        WARN("invalid parameter\n");
+        return MMSYSERR_INVALPARAM;
+    }
+
+    *phadid = (HACMDRIVERID) pao->pACMDriverID;
+
+    return MMSYSERR_NOERROR;
+}
+
+/***********************************************************************
+ *           acmDriverMessage (MSACM32.@)
+ *
+ */
+LRESULT WINAPI acmDriverMessage(HACMDRIVER had, UINT uMsg, LPARAM lParam1, LPARAM lParam2)
+{
+    TRACE("(%p, %04x, %08lx, %08lx\n", had, uMsg, lParam1, lParam2);
+
+    if ((uMsg >= ACMDM_USER && uMsg < ACMDM_RESERVED_LOW) ||
+       uMsg == ACMDM_DRIVER_ABOUT ||
+       uMsg == DRV_QUERYCONFIGURE ||
+       uMsg == DRV_CONFIGURE)
+       return MSACM_Message(had, uMsg, lParam1, lParam2);
+
+    WARN("invalid parameter\n");
+    return MMSYSERR_INVALPARAM;
+}
+
+/***********************************************************************
+ *           acmDriverOpen (MSACM32.@)
+ */
+MMRESULT WINAPI acmDriverOpen(PHACMDRIVER phad, HACMDRIVERID hadid, DWORD fdwOpen)
+{
+    PWINE_ACMDRIVERID  padid;
+    PWINE_ACMDRIVER    pad = NULL;
+    MMRESULT           ret;
+
+    TRACE("(%p, %p, %08lu)\n", phad, hadid, fdwOpen);
+
+    if (!phad) {
+        WARN("invalid parameter\n");
+       return MMSYSERR_INVALPARAM;
+    }
+
+    if (fdwOpen) {
+        WARN("invalid flag\n");
+       return MMSYSERR_INVALFLAG;
+    }
+
+    padid = MSACM_GetDriverID(hadid);
+    if (!padid) {
+        WARN("invalid handle\n");
+       return MMSYSERR_INVALHANDLE;
+    }
+
+    pad = HeapAlloc(MSACM_hHeap, 0, sizeof(WINE_ACMDRIVER));
+    if (!pad) {
+        WARN("no memory\n");
+        return MMSYSERR_NOMEM;
+    }
+
+    pad->obj.dwType = WINE_ACMOBJ_DRIVER;
+    pad->obj.pACMDriverID = padid;
+
+    if (!(pad->hDrvr = (HDRVR)padid->hInstModule))
+    {
+        ACMDRVOPENDESCW        adod;
+        int            len;
+
+       /* this is not an externally added driver... need to actually load it */
+       if (!padid->pszDriverAlias)
+        {
+            ret = MMSYSERR_ERROR;
+            goto gotError;
+        }
+
+        adod.cbStruct = sizeof(adod);
+        adod.fccType = ACMDRIVERDETAILS_FCCTYPE_AUDIOCODEC;
+        adod.fccComp = ACMDRIVERDETAILS_FCCCOMP_UNDEFINED;
+        adod.dwVersion = acmGetVersion();
+        adod.dwFlags = fdwOpen;
+        adod.dwError = 0;
+        len = strlen("Drivers32") + 1;
+        adod.pszSectionName = HeapAlloc(MSACM_hHeap, 0, len * sizeof(WCHAR));
+        MultiByteToWideChar(CP_ACP, 0, "Drivers32", -1, (LPWSTR)adod.pszSectionName, len);
+        adod.pszAliasName = padid->pszDriverAlias;
+        adod.dnDevNode = 0;
+
+        pad->hDrvr = OpenDriver(padid->pszDriverAlias, NULL, (DWORD)&adod);
+
+        HeapFree(MSACM_hHeap, 0, (LPWSTR)adod.pszSectionName);
+        if (!pad->hDrvr)
+        {
+            ret = adod.dwError;
+            goto gotError;
+        }
+    }
+
+    /* insert new pad at beg of list */
+    pad->pNextACMDriver = padid->pACMDriverList;
+    padid->pACMDriverList = pad;
+
+    /* FIXME: Create a WINE_ACMDRIVER32 */
+    *phad = (HACMDRIVER)pad;
+    TRACE("'%s' => %08lx\n", debugstr_w(padid->pszDriverAlias), (DWORD)pad);
+
+    return MMSYSERR_NOERROR;
+ gotError:
+    WARN("failed: ret = %08x\n", ret);
+    if (pad && !pad->hDrvr)
+       HeapFree(MSACM_hHeap, 0, pad);
+    return ret;
+}
+
+/***********************************************************************
+ *           acmDriverPriority (MSACM32.@)
+ */
+MMRESULT WINAPI acmDriverPriority(HACMDRIVERID hadid, DWORD dwPriority, DWORD fdwPriority)
+{
+    PWINE_ACMDRIVERID padid;
+    CHAR szSubKey[17];
+    CHAR szBuffer[256];
+    LONG lBufferLength = sizeof(szBuffer);
+    LONG lError;
+    HKEY hPriorityKey;
+    DWORD dwPriorityCounter;
+
+    TRACE("(%p, %08lx, %08lx)\n", hadid, dwPriority, fdwPriority);
+
+    padid = MSACM_GetDriverID(hadid);
+    if (!padid) {
+        WARN("invalid handle\n");
+       return MMSYSERR_INVALHANDLE;
+    }
+
+    /* Check for unknown flags */
+    if (fdwPriority &
+       ~(ACM_DRIVERPRIORITYF_ENABLE|ACM_DRIVERPRIORITYF_DISABLE|
+         ACM_DRIVERPRIORITYF_BEGIN|ACM_DRIVERPRIORITYF_END)) {
+        WARN("invalid flag\n");
+       return MMSYSERR_INVALFLAG;
+    }
+
+    /* Check for incompatible flags */
+    if ((fdwPriority & ACM_DRIVERPRIORITYF_ENABLE) &&
+       (fdwPriority & ACM_DRIVERPRIORITYF_DISABLE)) {
+        WARN("invalid flag\n");
+       return MMSYSERR_INVALFLAG;
+    }
+
+    /* Check for incompatible flags */
+    if ((fdwPriority & ACM_DRIVERPRIORITYF_BEGIN) &&
+       (fdwPriority & ACM_DRIVERPRIORITYF_END)) {
+        WARN("invalid flag\n");
+       return MMSYSERR_INVALFLAG;
+    }
+
+    lError = RegOpenKeyA(HKEY_CURRENT_USER,
+                        "Software\\Microsoft\\Multimedia\\"
+                        "Audio Compression Manager\\Priority v4.00",
+                        &hPriorityKey
+                        );
+    /* FIXME: Create key */
+    if (lError != ERROR_SUCCESS) {
+        WARN("RegOpenKeyA failed\n");
+       return MMSYSERR_ERROR;
+    }
+
+    for (dwPriorityCounter = 1; ; dwPriorityCounter++) {
+       snprintf(szSubKey, 17, "Priority%ld", dwPriorityCounter);
+       lError = RegQueryValueA(hPriorityKey, szSubKey, szBuffer, &lBufferLength);
+       if (lError != ERROR_SUCCESS)
+           break;
+
+       FIXME("(%p, %ld, %ld): stub (partial)\n",
+             hadid, dwPriority, fdwPriority);
+       break;
+    }
+
+    RegCloseKey(hPriorityKey);
+
+    WARN("RegQueryValueA failed\n");
+    return MMSYSERR_ERROR;
+}
+
+/***********************************************************************
+ *           acmDriverRemove (MSACM32.@)
+ */
+MMRESULT WINAPI acmDriverRemove(HACMDRIVERID hadid, DWORD fdwRemove)
+{
+    PWINE_ACMDRIVERID padid;
+
+    TRACE("(%p, %08lx)\n", hadid, fdwRemove);
+
+    padid = MSACM_GetDriverID(hadid);
+    if (!padid) {
+        WARN("invalid handle\n");
+       return MMSYSERR_INVALHANDLE;
+    }
+
+    if (fdwRemove) {
+        WARN("invalid flag\n");
+       return MMSYSERR_INVALFLAG;
+    }
+
+    MSACM_UnregisterDriver(padid);
+
+    return MMSYSERR_NOERROR;
+}
diff --git a/reactos/dll/msacm/filter.c b/reactos/dll/msacm/filter.c
new file mode 100644 (file)
index 0000000..3bce5c2
--- /dev/null
@@ -0,0 +1,454 @@
+/* -*- tab-width: 8; c-basic-offset: 4 -*- */
+
+/*
+ *      MSACM32 library
+ *
+ *      Copyright 1998  Patrik Stridvall
+ *
+ * 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 <stdarg.h>
+#include <string.h>
+#include "windef.h"
+#include "winbase.h"
+#include "winnls.h"
+#include "winerror.h"
+#include "mmsystem.h"
+#include "mmreg.h"
+#include "msacm.h"
+#include "msacmdrv.h"
+#include "wineacm.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msacm);
+
+/***********************************************************************
+ *           acmFilterChooseA (MSACM32.@)
+ */
+MMRESULT WINAPI acmFilterChooseA(PACMFILTERCHOOSEA pafltrc)
+{
+    FIXME("(%p): stub\n", pafltrc);
+    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+    return MMSYSERR_ERROR;
+}
+
+/***********************************************************************
+ *           acmFilterChooseW (MSACM32.@)
+ */
+MMRESULT WINAPI acmFilterChooseW(PACMFILTERCHOOSEW pafltrc)
+{
+    FIXME("(%p): stub\n", pafltrc);
+    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+    return MMSYSERR_ERROR;
+}
+
+/***********************************************************************
+ *           acmFilterDetailsA (MSACM32.@)
+ */
+MMRESULT WINAPI acmFilterDetailsA(HACMDRIVER had, PACMFILTERDETAILSA pafd,
+                                 DWORD fdwDetails)
+{
+    ACMFILTERDETAILSW  afdw;
+    MMRESULT           mmr;
+
+    memset(&afdw, 0, sizeof(afdw));
+    afdw.cbStruct = sizeof(afdw);
+    afdw.dwFilterIndex = pafd->dwFilterIndex;
+    afdw.dwFilterTag = pafd->dwFilterTag;
+    afdw.pwfltr = pafd->pwfltr;
+    afdw.cbwfltr = pafd->cbwfltr;
+
+    mmr = acmFilterDetailsW(had, &afdw, fdwDetails);
+    if (mmr == MMSYSERR_NOERROR) {
+       pafd->dwFilterTag = afdw.dwFilterTag;
+       pafd->fdwSupport = afdw.fdwSupport;
+        WideCharToMultiByte( CP_ACP, 0, afdw.szFilter, -1, pafd->szFilter,
+                             sizeof(pafd->szFilter), NULL, NULL );
+    }
+    return mmr;
+}
+
+/***********************************************************************
+ *           acmFilterDetailsW (MSACM32.@)
+ */
+MMRESULT WINAPI acmFilterDetailsW(HACMDRIVER had, PACMFILTERDETAILSW pafd,
+                                 DWORD fdwDetails)
+{
+    MMRESULT                   mmr;
+    ACMFILTERTAGDETAILSA       aftd;
+
+    TRACE("(%p, %p, %ld)\n", had, pafd, fdwDetails);
+
+    memset(&aftd, 0, sizeof(aftd));
+    aftd.cbStruct = sizeof(aftd);
+
+    if (pafd->cbStruct < sizeof(*pafd)) return MMSYSERR_INVALPARAM;
+
+    switch (fdwDetails) {
+    case ACM_FILTERDETAILSF_FILTER:
+       if (pafd->dwFilterTag != pafd->pwfltr->dwFilterTag) {
+           mmr = MMSYSERR_INVALPARAM;
+           break;
+       }
+       if (had == NULL) {
+           PWINE_ACMDRIVERID           padid;
+
+           mmr = ACMERR_NOTPOSSIBLE;
+           for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) {
+               /* should check for codec only */
+               if (!(padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) &&
+                   acmDriverOpen(&had, (HACMDRIVERID)padid, 0) == 0) {
+                   mmr = MSACM_Message(had, ACMDM_FILTER_DETAILS,
+                                       (LPARAM)pafd, (LPARAM)fdwDetails);
+                   acmDriverClose(had, 0);
+                   if (mmr == MMSYSERR_NOERROR) break;
+               }
+           }
+       } else {
+           mmr = MSACM_Message(had, ACMDM_FILTER_DETAILS, (LPARAM)pafd, fdwDetails);
+       }
+       break;
+    case ACM_FILTERDETAILSF_INDEX:
+       /* should check pafd->dwFilterIndex < aftd->cStandardFilters */
+       mmr = MSACM_Message(had, ACMDM_FILTER_DETAILS, (LPARAM)pafd, fdwDetails);
+       break;
+    default:
+       WARN("Unknown fdwDetails %08lx\n", fdwDetails);
+       mmr = MMSYSERR_INVALFLAG;
+       break;
+    }
+
+    TRACE("=> %d\n", mmr);
+    return mmr;
+}
+
+struct MSACM_FilterEnumWtoA_Instance {
+    PACMFILTERDETAILSA pafda;
+    DWORD              dwInstance;
+    ACMFILTERENUMCBA   fnCallback;
+};
+
+static BOOL CALLBACK MSACM_FilterEnumCallbackWtoA(HACMDRIVERID hadid,
+                                                 PACMFILTERDETAILSW pafdw,
+                                                 DWORD dwInstance,
+                                                 DWORD fdwSupport)
+{
+    struct MSACM_FilterEnumWtoA_Instance* pafei;
+
+    pafei = (struct MSACM_FilterEnumWtoA_Instance*)dwInstance;
+
+    pafei->pafda->dwFilterIndex = pafdw->dwFilterIndex;
+    pafei->pafda->dwFilterTag = pafdw->dwFilterTag;
+    pafei->pafda->fdwSupport = pafdw->fdwSupport;
+    WideCharToMultiByte( CP_ACP, 0, pafdw->szFilter, -1, pafei->pafda->szFilter,
+                         sizeof(pafei->pafda->szFilter), NULL, NULL );
+
+    return (pafei->fnCallback)(hadid, pafei->pafda,
+                              pafei->dwInstance, fdwSupport);
+}
+
+/***********************************************************************
+ *           acmFilterEnumA (MSACM32.@)
+ */
+MMRESULT WINAPI acmFilterEnumA(HACMDRIVER had, PACMFILTERDETAILSA pafda,
+                              ACMFILTERENUMCBA fnCallback, DWORD dwInstance,
+                              DWORD fdwEnum)
+{
+    ACMFILTERDETAILSW          afdw;
+    struct MSACM_FilterEnumWtoA_Instance afei;
+
+    memset(&afdw, 0, sizeof(afdw));
+    afdw.cbStruct = sizeof(afdw);
+    afdw.dwFilterIndex = pafda->dwFilterIndex;
+    afdw.dwFilterTag = pafda->dwFilterTag;
+    afdw.pwfltr = pafda->pwfltr;
+    afdw.cbwfltr = pafda->cbwfltr;
+
+    afei.pafda = pafda;
+    afei.dwInstance = dwInstance;
+    afei.fnCallback = fnCallback;
+
+    return acmFilterEnumW(had, &afdw, MSACM_FilterEnumCallbackWtoA,
+                         (DWORD)&afei, fdwEnum);
+}
+
+static BOOL MSACM_FilterEnumHelper(PWINE_ACMDRIVERID padid, HACMDRIVER had,
+                                  PACMFILTERDETAILSW pafd,
+                                  ACMFILTERENUMCBW fnCallback, DWORD dwInstance,
+                                  DWORD fdwEnum)
+{
+    ACMFILTERTAGDETAILSW       aftd;
+    int                                i, j;
+
+    for (i = 0; i < padid->cFilterTags; i++) {
+       memset(&aftd, 0, sizeof(aftd));
+       aftd.cbStruct = sizeof(aftd);
+       aftd.dwFilterTagIndex = i;
+       if (acmFilterTagDetailsW(had, &aftd, ACM_FILTERTAGDETAILSF_INDEX) != MMSYSERR_NOERROR)
+           continue;
+
+       if ((fdwEnum & ACM_FILTERENUMF_DWFILTERTAG) &&
+           aftd.dwFilterTag != pafd->pwfltr->dwFilterTag)
+           continue;
+
+       for (j = 0; j < aftd.cStandardFilters; j++) {
+           pafd->dwFilterIndex = j;
+           pafd->dwFilterTag = aftd.dwFilterTag;
+           if (acmFilterDetailsW(had, pafd, ACM_FILTERDETAILSF_INDEX) != MMSYSERR_NOERROR)
+               continue;
+
+           if (!(fnCallback)((HACMDRIVERID)padid, pafd, dwInstance, padid->fdwSupport))
+               return FALSE;
+       }
+    }
+    return TRUE;
+}
+
+/***********************************************************************
+ *           acmFilterEnumW (MSACM32.@)
+ */
+MMRESULT WINAPI acmFilterEnumW(HACMDRIVER had, PACMFILTERDETAILSW pafd,
+                              ACMFILTERENUMCBW fnCallback, DWORD dwInstance,
+                              DWORD fdwEnum)
+{
+    PWINE_ACMDRIVERID          padid;
+    BOOL                       ret;
+
+    TRACE("(%p, %p, %p, %ld, %ld)\n",
+         had, pafd, fnCallback, dwInstance, fdwEnum);
+
+    if (pafd->cbStruct < sizeof(*pafd)) return MMSYSERR_INVALPARAM;
+
+    if (fdwEnum & ~(ACM_FILTERENUMF_DWFILTERTAG))
+       FIXME("Unsupported fdwEnum values\n");
+
+    if (had) {
+       HACMDRIVERID    hadid;
+
+       if (acmDriverID((HACMOBJ)had, &hadid, 0) != MMSYSERR_NOERROR)
+           return MMSYSERR_INVALHANDLE;
+       MSACM_FilterEnumHelper(MSACM_GetDriverID(hadid), had, pafd,
+                              fnCallback, dwInstance, fdwEnum);
+       return MMSYSERR_NOERROR;
+    }
+    for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) {
+           /* should check for codec only */
+           if ((padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) ||
+               acmDriverOpen(&had, (HACMDRIVERID)padid, 0) != MMSYSERR_NOERROR)
+               continue;
+           ret = MSACM_FilterEnumHelper(padid, had, pafd,
+                                        fnCallback, dwInstance, fdwEnum);
+           acmDriverClose(had, 0);
+           if (!ret) break;
+    }
+    return MMSYSERR_NOERROR;
+}
+
+/***********************************************************************
+ *           acmFilterTagDetailsA (MSACM32.@)
+ */
+MMRESULT WINAPI acmFilterTagDetailsA(HACMDRIVER had, PACMFILTERTAGDETAILSA paftda,
+                                    DWORD fdwDetails)
+{
+    ACMFILTERTAGDETAILSW       aftdw;
+    MMRESULT                   mmr;
+
+    memset(&aftdw, 0, sizeof(aftdw));
+    aftdw.cbStruct = sizeof(aftdw);
+    aftdw.dwFilterTagIndex = paftda->dwFilterTagIndex;
+    aftdw.dwFilterTag = paftda->dwFilterTag;
+
+    mmr = acmFilterTagDetailsW(had, &aftdw, fdwDetails);
+    if (mmr == MMSYSERR_NOERROR) {
+       paftda->dwFilterTag = aftdw.dwFilterTag;
+       paftda->dwFilterTagIndex = aftdw.dwFilterTagIndex;
+       paftda->cbFilterSize = aftdw.cbFilterSize;
+       paftda->fdwSupport = aftdw.fdwSupport;
+       paftda->cStandardFilters = aftdw.cStandardFilters;
+        WideCharToMultiByte( CP_ACP, 0, aftdw.szFilterTag, -1, paftda->szFilterTag,
+                             sizeof(paftda->szFilterTag), NULL, NULL );
+    }
+    return mmr;
+}
+
+/***********************************************************************
+ *           acmFilterTagDetailsW (MSACM32.@)
+ */
+MMRESULT WINAPI acmFilterTagDetailsW(HACMDRIVER had, PACMFILTERTAGDETAILSW paftd,
+                                    DWORD fdwDetails)
+{
+    PWINE_ACMDRIVERID  padid;
+    MMRESULT           mmr;
+
+    TRACE("(%p, %p, %ld)\n", had, paftd, fdwDetails);
+
+    if (fdwDetails & ~(ACM_FILTERTAGDETAILSF_FILTERTAG|ACM_FILTERTAGDETAILSF_INDEX|
+                      ACM_FILTERTAGDETAILSF_LARGESTSIZE))
+       return MMSYSERR_INVALFLAG;
+
+    switch (fdwDetails) {
+    case ACM_FILTERTAGDETAILSF_FILTERTAG:
+       if (had == NULL) {
+           mmr = ACMERR_NOTPOSSIBLE;
+           for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) {
+               /* should check for codec only */
+               if (!(padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) &&
+                     acmDriverOpen(&had, (HACMDRIVERID)padid, 0) == 0) {
+                   mmr = MSACM_Message(had, ACMDM_FILTERTAG_DETAILS, (LPARAM)paftd, fdwDetails);
+                   acmDriverClose(had, 0);
+                   if (mmr == MMSYSERR_NOERROR) break;
+               }
+           }
+       } else {
+           mmr = MSACM_Message(had, ACMDM_FILTERTAG_DETAILS, (LPARAM)paftd, fdwDetails);
+       }
+       break;
+
+    case ACM_FILTERTAGDETAILSF_INDEX:
+       /* FIXME should check paftd->dwFilterTagIndex < add.cFilterTags */
+       mmr = MSACM_Message(had, ACMDM_FILTERTAG_DETAILS, (LPARAM)paftd, fdwDetails);
+       break;
+
+    case ACM_FILTERTAGDETAILSF_LARGESTSIZE:
+       if (had == NULL) {
+           ACMFILTERTAGDETAILSW        tmp;
+           DWORD                       ft = paftd->dwFilterTag;
+
+           mmr = ACMERR_NOTPOSSIBLE;
+           for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) {
+               /* should check for codec only */
+               if (!(padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) &&
+                   acmDriverOpen(&had, (HACMDRIVERID)padid, 0) == 0) {
+
+                   memset(&tmp, 0, sizeof(tmp));
+                   tmp.cbStruct = sizeof(tmp);
+                   tmp.dwFilterTag = ft;
+
+                   if (MSACM_Message(had, ACMDM_FILTERTAG_DETAILS,
+                                     (LPARAM)&tmp, fdwDetails) == MMSYSERR_NOERROR) {
+                       if (mmr == ACMERR_NOTPOSSIBLE ||
+                           paftd->cbFilterSize < tmp.cbFilterSize) {
+                           *paftd = tmp;
+                           mmr = MMSYSERR_NOERROR;
+                       }
+                   }
+                   acmDriverClose(had, 0);
+               }
+           }
+       } else {
+           mmr = MSACM_Message(had, ACMDM_FILTERTAG_DETAILS, (LPARAM)paftd, fdwDetails);
+       }
+       break;
+
+    default:
+       WARN("Unsupported fdwDetails=%08lx\n", fdwDetails);
+       mmr = MMSYSERR_ERROR;
+    }
+
+    if (mmr == MMSYSERR_NOERROR &&
+       paftd->dwFilterTag == WAVE_FORMAT_PCM && paftd->szFilterTag[0] == 0)
+        MultiByteToWideChar( CP_ACP, 0, "PCM", -1, paftd->szFilterTag,
+                             sizeof(paftd->szFilterTag)/sizeof(WCHAR) );
+
+    return mmr;
+}
+
+struct MSACM_FilterTagEnumWtoA_Instance {
+    PACMFILTERTAGDETAILSA      paftda;
+    DWORD                      dwInstance;
+    ACMFILTERTAGENUMCBA        fnCallback;
+};
+
+static BOOL CALLBACK MSACM_FilterTagEnumCallbackWtoA(HACMDRIVERID hadid,
+                                                    PACMFILTERTAGDETAILSW paftdw,
+                                                    DWORD dwInstance,
+                                                    DWORD fdwSupport)
+{
+    struct MSACM_FilterTagEnumWtoA_Instance* paftei;
+
+    paftei = (struct MSACM_FilterTagEnumWtoA_Instance*)dwInstance;
+
+    paftei->paftda->dwFilterTagIndex = paftdw->dwFilterTagIndex;
+    paftei->paftda->dwFilterTag = paftdw->dwFilterTag;
+    paftei->paftda->cbFilterSize = paftdw->cbFilterSize;
+    paftei->paftda->fdwSupport = paftdw->fdwSupport;
+    paftei->paftda->cStandardFilters = paftdw->cStandardFilters;
+    WideCharToMultiByte( CP_ACP, 0, paftdw->szFilterTag, -1, paftei->paftda->szFilterTag,
+                         sizeof(paftei->paftda->szFilterTag), NULL, NULL );
+
+    return (paftei->fnCallback)(hadid, paftei->paftda,
+                               paftei->dwInstance, fdwSupport);
+}
+
+/***********************************************************************
+ *           acmFilterTagEnumA (MSACM32.@)
+ */
+MMRESULT WINAPI acmFilterTagEnumA(HACMDRIVER had, PACMFILTERTAGDETAILSA paftda,
+                                 ACMFILTERTAGENUMCBA fnCallback, DWORD dwInstance,
+                                 DWORD fdwEnum)
+{
+    ACMFILTERTAGDETAILSW       aftdw;
+    struct MSACM_FilterTagEnumWtoA_Instance aftei;
+
+    memset(&aftdw, 0, sizeof(aftdw));
+    aftdw.cbStruct = sizeof(aftdw);
+    aftdw.dwFilterTagIndex = paftda->dwFilterTagIndex;
+    aftdw.dwFilterTag = paftda->dwFilterTag;
+
+    aftei.paftda = paftda;
+    aftei.dwInstance = dwInstance;
+    aftei.fnCallback = fnCallback;
+
+    return acmFilterTagEnumW(had, &aftdw, MSACM_FilterTagEnumCallbackWtoA,
+                            (DWORD)&aftei, fdwEnum);
+}
+
+/***********************************************************************
+ *           acmFilterTagEnumW (MSACM32.@)
+ */
+MMRESULT WINAPI acmFilterTagEnumW(HACMDRIVER had, PACMFILTERTAGDETAILSW paftd,
+                                 ACMFILTERTAGENUMCBW fnCallback, DWORD dwInstance,
+                                 DWORD fdwEnum)
+{
+    PWINE_ACMDRIVERID          padid;
+    int                                i;
+
+    TRACE("(%p, %p, %p, %ld, %ld)\n",
+         had, paftd, fnCallback, dwInstance, fdwEnum);
+
+    if (paftd->cbStruct < sizeof(*paftd)) return MMSYSERR_INVALPARAM;
+
+    if (had) FIXME("had != NULL, not supported\n");
+
+    for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) {
+       /* should check for codec only */
+       if (!(padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) &&
+           acmDriverOpen(&had, (HACMDRIVERID)padid, 0) == MMSYSERR_NOERROR) {
+
+           for (i = 0; i < padid->cFilterTags; i++) {
+               paftd->dwFilterTagIndex = i;
+               if (acmFilterTagDetailsW(had, paftd, ACM_FILTERTAGDETAILSF_INDEX) == MMSYSERR_NOERROR) {
+                   if (!(fnCallback)((HACMDRIVERID)padid, paftd, dwInstance, padid->fdwSupport)) {
+                       padid = NULL;
+                       break;
+                   }
+               }
+           }
+       }
+       acmDriverClose(had, 0);
+    }
+    return MMSYSERR_NOERROR;
+}
diff --git a/reactos/dll/msacm/format.c b/reactos/dll/msacm/format.c
new file mode 100644 (file)
index 0000000..b64f8b0
--- /dev/null
@@ -0,0 +1,858 @@
+/* -*- tab-width: 8; c-basic-offset: 4 -*- */
+
+/*
+ *      MSACM32 library
+ *
+ *      Copyright 1998  Patrik Stridvall
+ *               2000  Eric Pouech
+ *
+ * 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 <stdarg.h>
+#include <string.h>
+#include "windef.h"
+#include "winbase.h"
+#include "winnls.h"
+#include "winerror.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "wine/unicode.h"
+#include "wine/debug.h"
+#include "mmsystem.h"
+#include "mmreg.h"
+#include "msacm.h"
+#include "msacmdrv.h"
+#include "wineacm.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msacm);
+
+static PACMFORMATCHOOSEA       afc;
+
+struct MSACM_FillFormatData {
+    HWND               hWnd;
+#define WINE_ACMFF_TAG         0
+#define WINE_ACMFF_FORMAT      1
+#define WINE_ACMFF_WFX         2
+    int                        mode;
+    char               szFormatTag[ACMFORMATTAGDETAILS_FORMATTAG_CHARS];
+    PACMFORMATCHOOSEA  afc;
+    DWORD              ret;
+};
+
+static BOOL CALLBACK MSACM_FillFormatTagsCB(HACMDRIVERID hadid,
+                                           PACMFORMATTAGDETAILSA paftd,
+                                           DWORD dwInstance, DWORD fdwSupport)
+{
+    struct MSACM_FillFormatData*       affd = (struct MSACM_FillFormatData*)dwInstance;
+
+    switch (affd->mode) {
+    case WINE_ACMFF_TAG:
+       if (SendDlgItemMessageA(affd->hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMATTAG,
+                               CB_FINDSTRINGEXACT,
+                               (WPARAM)-1, (LPARAM)paftd->szFormatTag) == CB_ERR)
+           SendDlgItemMessageA(affd->hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMATTAG,
+                               CB_ADDSTRING, 0, (DWORD)paftd->szFormatTag);
+       break;
+    case WINE_ACMFF_FORMAT:
+       if (strcmp(affd->szFormatTag, paftd->szFormatTag) == 0) {
+           HACMDRIVER          had;
+
+           if (acmDriverOpen(&had, hadid, 0) == MMSYSERR_NOERROR) {
+               ACMFORMATDETAILSA       afd;
+               int                     i, idx;
+               MMRESULT                mmr;
+               char                    buffer[ACMFORMATDETAILS_FORMAT_CHARS+16];
+
+               afd.cbStruct = sizeof(afd);
+               afd.dwFormatTag = paftd->dwFormatTag;
+               afd.pwfx = HeapAlloc(MSACM_hHeap, 0, paftd->cbFormatSize);
+               if (!afd.pwfx) return FALSE;
+               afd.pwfx->wFormatTag = paftd->dwFormatTag;
+               afd.pwfx->cbSize = paftd->cbFormatSize;
+               afd.cbwfx = paftd->cbFormatSize;
+
+               for (i = 0; i < paftd->cStandardFormats; i++) {
+                   afd.dwFormatIndex = i;
+                   mmr = acmFormatDetailsA(had, &afd, ACM_FORMATDETAILSF_INDEX);
+                   if (mmr == MMSYSERR_NOERROR) {
+                       strncpy(buffer, afd.szFormat, ACMFORMATTAGDETAILS_FORMATTAG_CHARS);
+                       for (idx = strlen(buffer);
+                            idx < ACMFORMATTAGDETAILS_FORMATTAG_CHARS; idx++)
+                           buffer[idx] = ' ';
+                       wsprintfA(buffer + ACMFORMATTAGDETAILS_FORMATTAG_CHARS,
+                                 "%d Ko/s",
+                                 (afd.pwfx->nAvgBytesPerSec + 512) / 1024);
+                       SendDlgItemMessageA(affd->hWnd,
+                                           IDD_ACMFORMATCHOOSE_CMB_FORMAT,
+                                           CB_ADDSTRING, 0, (DWORD)buffer);
+                   }
+               }
+               acmDriverClose(had, 0);
+               SendDlgItemMessageA(affd->hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMAT,
+                                   CB_SETCURSEL, 0, 0);
+               HeapFree(MSACM_hHeap, 0, afd.pwfx);
+           }
+       }
+       break;
+    case WINE_ACMFF_WFX:
+       if (strcmp(affd->szFormatTag, paftd->szFormatTag) == 0) {
+           HACMDRIVER          had;
+
+           if (acmDriverOpen(&had, hadid, 0) == MMSYSERR_NOERROR) {
+               ACMFORMATDETAILSA       afd;
+
+               afd.cbStruct = sizeof(afd);
+               afd.dwFormatTag = paftd->dwFormatTag;
+               afd.pwfx = affd->afc->pwfx;
+               afd.cbwfx = affd->afc->cbwfx;
+
+               afd.dwFormatIndex = SendDlgItemMessageA(affd->hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMAT,
+                                                       CB_GETCURSEL, 0, 0);
+               affd->ret = acmFormatDetailsA(had, &afd, ACM_FORMATDETAILSF_INDEX);
+               acmDriverClose(had, 0);
+               return TRUE;
+           }
+       }
+       break;
+    default:
+       FIXME("Unknown mode (%d)\n", affd->mode);
+       break;
+    }
+    return TRUE;
+}
+
+static BOOL MSACM_FillFormatTags(HWND hWnd)
+{
+    ACMFORMATTAGDETAILSA       aftd;
+    struct MSACM_FillFormatData        affd;
+
+    memset(&aftd, 0, sizeof(aftd));
+    aftd.cbStruct = sizeof(aftd);
+
+    affd.hWnd = hWnd;
+    affd.mode = WINE_ACMFF_TAG;
+
+    acmFormatTagEnumA(NULL, &aftd, MSACM_FillFormatTagsCB, (DWORD)&affd, 0);
+    SendDlgItemMessageA(hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMATTAG, CB_SETCURSEL, 0, 0);
+    return TRUE;
+}
+
+static BOOL MSACM_FillFormat(HWND hWnd)
+{
+    ACMFORMATTAGDETAILSA       aftd;
+    struct MSACM_FillFormatData        affd;
+
+    SendDlgItemMessageA(hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMAT, CB_RESETCONTENT, 0, 0);
+
+    memset(&aftd, 0, sizeof(aftd));
+    aftd.cbStruct = sizeof(aftd);
+
+    affd.hWnd = hWnd;
+    affd.mode = WINE_ACMFF_FORMAT;
+    SendDlgItemMessageA(hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMATTAG,
+                       CB_GETLBTEXT,
+                       SendDlgItemMessageA(hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMATTAG,
+                                           CB_GETCURSEL, 0, 0),
+                       (DWORD)affd.szFormatTag);
+
+    acmFormatTagEnumA(NULL, &aftd, MSACM_FillFormatTagsCB, (DWORD)&affd, 0);
+    SendDlgItemMessageA(hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMAT, CB_SETCURSEL, 0, 0);
+    return TRUE;
+}
+
+static MMRESULT MSACM_GetWFX(HWND hWnd, PACMFORMATCHOOSEA afc)
+{
+    ACMFORMATTAGDETAILSA       aftd;
+    struct MSACM_FillFormatData        affd;
+
+    memset(&aftd, 0, sizeof(aftd));
+    aftd.cbStruct = sizeof(aftd);
+
+    affd.hWnd = hWnd;
+    affd.mode = WINE_ACMFF_WFX;
+    affd.afc = afc;
+    affd.ret = MMSYSERR_NOERROR;
+    SendDlgItemMessageA(hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMATTAG,
+                       CB_GETLBTEXT,
+                       SendDlgItemMessageA(hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMATTAG,
+                                           CB_GETCURSEL, 0, 0),
+                       (DWORD)affd.szFormatTag);
+
+    acmFormatTagEnumA(NULL, &aftd, MSACM_FillFormatTagsCB, (DWORD)&affd, 0);
+    return affd.ret;
+}
+
+static INT_PTR CALLBACK FormatChooseDlgProc(HWND hWnd, UINT msg,
+                                      WPARAM wParam, LPARAM lParam)
+{
+
+    TRACE("hwnd=%p msg=%i 0x%08x 0x%08lx\n", hWnd,  msg, wParam, lParam );
+
+    switch (msg) {
+    case WM_INITDIALOG:
+       afc = (PACMFORMATCHOOSEA)lParam;
+       MSACM_FillFormatTags(hWnd);
+       MSACM_FillFormat(hWnd);
+       if ((afc->fdwStyle & ~(ACMFORMATCHOOSE_STYLEF_CONTEXTHELP|
+                              ACMFORMATCHOOSE_STYLEF_SHOWHELP)) != 0)
+           FIXME("Unsupported style %08lx\n", ((PACMFORMATCHOOSEA)lParam)->fdwStyle);
+       if (!(afc->fdwStyle & ACMFORMATCHOOSE_STYLEF_SHOWHELP))
+           ShowWindow(GetDlgItem(hWnd, IDD_ACMFORMATCHOOSE_BTN_HELP), SW_HIDE);
+       return TRUE;
+
+    case WM_COMMAND:
+       switch (LOWORD(wParam)) {
+       case IDOK:
+           EndDialog(hWnd, MSACM_GetWFX(hWnd, afc));
+           return TRUE;
+       case IDCANCEL:
+           EndDialog(hWnd, ACMERR_CANCELED);
+           return TRUE;
+       case IDD_ACMFORMATCHOOSE_CMB_FORMATTAG:
+           switch (HIWORD(wParam)) {
+           case CBN_SELCHANGE:
+               MSACM_FillFormat(hWnd);
+               break;
+           default:
+               TRACE("Dropped dlgNotif (fmtTag): 0x%08x 0x%08lx\n",
+                     HIWORD(wParam), lParam);
+               break;
+           }
+           break;
+       case IDD_ACMFORMATCHOOSE_BTN_HELP:
+           if (afc->fdwStyle & ACMFORMATCHOOSE_STYLEF_SHOWHELP)
+               SendMessageA(afc->hwndOwner,
+                            RegisterWindowMessageA(ACMHELPMSGSTRINGA), 0L, 0L);
+           break;
+
+       default:
+           TRACE("Dropped dlgCmd: ctl=%d ntf=0x%04x 0x%08lx\n",
+                 LOWORD(wParam), HIWORD(wParam), lParam);
+           break;
+       }
+       break;
+    case WM_CONTEXTMENU:
+       if (afc->fdwStyle & ACMFORMATCHOOSE_STYLEF_CONTEXTHELP)
+           SendMessageA(afc->hwndOwner,
+                        RegisterWindowMessageA(ACMHELPMSGCONTEXTMENUA),
+                        wParam, lParam);
+       break;
+#if defined(WM_CONTEXTHELP)
+    case WM_CONTEXTHELP:
+       if (afc->fdwStyle & ACMFORMATCHOOSE_STYLEF_CONTEXTHELP)
+           SendMessageA(afc->hwndOwner,
+                        RegisterWindowMessageA(ACMHELPMSGCONTEXTHELPA),
+                        wParam, lParam);
+       break;
+#endif
+    default:
+       TRACE("Dropped dlgMsg: hwnd=%p msg=%i 0x%08x 0x%08lx\n",
+             hWnd,  msg, wParam, lParam );
+       break;
+    }
+    return FALSE;
+}
+
+/***********************************************************************
+ *           acmFormatChooseA (MSACM32.@)
+ */
+MMRESULT WINAPI acmFormatChooseA(PACMFORMATCHOOSEA pafmtc)
+{
+    return DialogBoxParamA(MSACM_hInstance32, MAKEINTRESOURCEA(DLG_ACMFORMATCHOOSE_ID),
+                          pafmtc->hwndOwner, FormatChooseDlgProc, (INT)pafmtc);
+}
+
+/***********************************************************************
+ *           acmFormatChooseW (MSACM32.@)
+ */
+MMRESULT WINAPI acmFormatChooseW(PACMFORMATCHOOSEW pafmtc)
+{
+    FIXME("(%p): stub\n", pafmtc);
+    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+    return MMSYSERR_ERROR;
+}
+
+/***********************************************************************
+ *           acmFormatDetailsA (MSACM32.@)
+ */
+MMRESULT WINAPI acmFormatDetailsA(HACMDRIVER had, PACMFORMATDETAILSA pafd,
+                                 DWORD fdwDetails)
+{
+    ACMFORMATDETAILSW  afdw;
+    MMRESULT           mmr;
+
+    memset(&afdw, 0, sizeof(afdw));
+    afdw.cbStruct = sizeof(afdw);
+    afdw.dwFormatIndex = pafd->dwFormatIndex;
+    afdw.dwFormatTag = pafd->dwFormatTag;
+    afdw.pwfx = pafd->pwfx;
+    afdw.cbwfx = pafd->cbwfx;
+
+    mmr = acmFormatDetailsW(had, &afdw, fdwDetails);
+    if (mmr == MMSYSERR_NOERROR) {
+       pafd->dwFormatTag = afdw.dwFormatTag;
+       pafd->fdwSupport = afdw.fdwSupport;
+        WideCharToMultiByte( CP_ACP, 0, afdw.szFormat, -1,
+                             pafd->szFormat, sizeof(pafd->szFormat), NULL, NULL );
+    }
+    return mmr;
+}
+
+/***********************************************************************
+ *           acmFormatDetailsW (MSACM32.@)
+ */
+MMRESULT WINAPI acmFormatDetailsW(HACMDRIVER had, PACMFORMATDETAILSW pafd, DWORD fdwDetails)
+{
+    MMRESULT                   mmr;
+    static const WCHAR         fmt1[] = {'%','d',' ','H','z',0};
+    static const WCHAR         fmt2[] = {';',' ','%','d',' ','b','i','t','s',0};
+    ACMFORMATTAGDETAILSA       aftd;
+
+    TRACE("(%p, %p, %ld)\n", had, pafd, fdwDetails);
+
+    memset(&aftd, 0, sizeof(aftd));
+    aftd.cbStruct = sizeof(aftd);
+
+    if (pafd->cbStruct < sizeof(*pafd)) return MMSYSERR_INVALPARAM;
+
+    switch (fdwDetails) {
+    case ACM_FORMATDETAILSF_FORMAT:
+       if (pafd->dwFormatTag != pafd->pwfx->wFormatTag) {
+           mmr = MMSYSERR_INVALPARAM;
+           break;
+       }
+       if (had == NULL) {
+           PWINE_ACMDRIVERID           padid;
+
+           mmr = ACMERR_NOTPOSSIBLE;
+           for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) {
+               /* should check for codec only */
+               if (!(padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) &&
+                   acmDriverOpen(&had, (HACMDRIVERID)padid, 0) == 0) {
+                   mmr = MSACM_Message(had, ACMDM_FORMAT_DETAILS, (LPARAM)pafd, fdwDetails);
+                   acmDriverClose(had, 0);
+                   if (mmr == MMSYSERR_NOERROR) break;
+               }
+           }
+       } else {
+           mmr = MSACM_Message(had, ACMDM_FORMAT_DETAILS, (LPARAM)pafd, fdwDetails);
+       }
+       break;
+    case ACM_FORMATDETAILSF_INDEX:
+       /* should check pafd->dwFormatIndex < aftd->cStandardFormats */
+       mmr = MSACM_Message(had, ACMDM_FORMAT_DETAILS, (LPARAM)pafd, fdwDetails);
+       break;
+    default:
+       WARN("Unknown fdwDetails %08lx\n", fdwDetails);
+       mmr = MMSYSERR_INVALFLAG;
+       break;
+    }
+
+    if (mmr == MMSYSERR_NOERROR && pafd->szFormat[0] == (WCHAR)0) {
+       wsprintfW(pafd->szFormat, fmt1, pafd->pwfx->nSamplesPerSec);
+       if (pafd->pwfx->wBitsPerSample) {
+           wsprintfW(pafd->szFormat + lstrlenW(pafd->szFormat), fmt2,
+                     pafd->pwfx->wBitsPerSample);
+       }
+        MultiByteToWideChar( CP_ACP, 0, (pafd->pwfx->nChannels == 1) ? "; Mono" : "; Stereo", -1,
+                             pafd->szFormat + strlenW(pafd->szFormat),
+                             sizeof(pafd->szFormat)/sizeof(WCHAR) - strlenW(pafd->szFormat) );
+    }
+
+    TRACE("=> %d\n", mmr);
+    return mmr;
+}
+
+struct MSACM_FormatEnumWtoA_Instance {
+    PACMFORMATDETAILSA pafda;
+    DWORD              dwInstance;
+    ACMFORMATENUMCBA   fnCallback;
+};
+
+static BOOL CALLBACK MSACM_FormatEnumCallbackWtoA(HACMDRIVERID hadid,
+                                                 PACMFORMATDETAILSW pafdw,
+                                                 DWORD dwInstance,
+                                                 DWORD fdwSupport)
+{
+    struct MSACM_FormatEnumWtoA_Instance* pafei;
+
+    pafei = (struct MSACM_FormatEnumWtoA_Instance*)dwInstance;
+
+    pafei->pafda->dwFormatIndex = pafdw->dwFormatIndex;
+    pafei->pafda->dwFormatTag = pafdw->dwFormatTag;
+    pafei->pafda->fdwSupport = pafdw->fdwSupport;
+    WideCharToMultiByte( CP_ACP, 0, pafdw->szFormat, -1,
+                         pafei->pafda->szFormat, sizeof(pafei->pafda->szFormat), NULL, NULL );
+
+    return (pafei->fnCallback)(hadid, pafei->pafda,
+                              pafei->dwInstance, fdwSupport);
+}
+
+/***********************************************************************
+ *           acmFormatEnumA (MSACM32.@)
+ */
+MMRESULT WINAPI acmFormatEnumA(HACMDRIVER had, PACMFORMATDETAILSA pafda,
+                              ACMFORMATENUMCBA fnCallback, DWORD dwInstance,
+                              DWORD fdwEnum)
+{
+    ACMFORMATDETAILSW          afdw;
+    struct MSACM_FormatEnumWtoA_Instance afei;
+
+    if (!pafda)
+        return MMSYSERR_INVALPARAM;
+
+    if (pafda->cbStruct < sizeof(*pafda))
+        return MMSYSERR_INVALPARAM;
+
+    memset(&afdw, 0, sizeof(afdw));
+    afdw.cbStruct = sizeof(afdw);
+    afdw.dwFormatIndex = pafda->dwFormatIndex;
+    afdw.dwFormatTag = pafda->dwFormatTag;
+    afdw.pwfx = pafda->pwfx;
+    afdw.cbwfx = pafda->cbwfx;
+
+    afei.pafda = pafda;
+    afei.dwInstance = dwInstance;
+    afei.fnCallback = fnCallback;
+
+    return acmFormatEnumW(had, &afdw, MSACM_FormatEnumCallbackWtoA,
+                         (DWORD)&afei, fdwEnum);
+}
+
+/***********************************************************************
+ *           acmFormatEnumW (MSACM32.@)
+ */
+static BOOL MSACM_FormatEnumHelper(PWINE_ACMDRIVERID padid, HACMDRIVER had,
+                                  PACMFORMATDETAILSW pafd, PWAVEFORMATEX pwfxRef,
+                                  ACMFORMATENUMCBW fnCallback, DWORD dwInstance,
+                                  DWORD fdwEnum)
+{
+    ACMFORMATTAGDETAILSW       aftd;
+    int                                i, j;
+
+    for (i = 0; i < padid->cFormatTags; i++) {
+       memset(&aftd, 0, sizeof(aftd));
+       aftd.cbStruct = sizeof(aftd);
+       aftd.dwFormatTagIndex = i;
+       if (acmFormatTagDetailsW(had, &aftd, ACM_FORMATTAGDETAILSF_INDEX) != MMSYSERR_NOERROR)
+           continue;
+
+       if ((fdwEnum & ACM_FORMATENUMF_WFORMATTAG) && aftd.dwFormatTag != pwfxRef->wFormatTag)
+           continue;
+
+       for (j = 0; j < aftd.cStandardFormats; j++) {
+           pafd->dwFormatIndex = j;
+           pafd->dwFormatTag = aftd.dwFormatTag;
+           if (acmFormatDetailsW(had, pafd, ACM_FORMATDETAILSF_INDEX) != MMSYSERR_NOERROR)
+               continue;
+
+           if ((fdwEnum & ACM_FORMATENUMF_NCHANNELS) &&
+               pafd->pwfx->nChannels != pwfxRef->nChannels)
+               continue;
+           if ((fdwEnum & ACM_FORMATENUMF_NSAMPLESPERSEC) &&
+               pafd->pwfx->nSamplesPerSec != pwfxRef->nSamplesPerSec)
+               continue;
+           if ((fdwEnum & ACM_FORMATENUMF_WBITSPERSAMPLE) &&
+               pafd->pwfx->wBitsPerSample != pwfxRef->wBitsPerSample)
+               continue;
+           if ((fdwEnum & ACM_FORMATENUMF_HARDWARE) &&
+               !(pafd->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_HARDWARE))
+               continue;
+
+           /* more checks to be done on fdwEnum */
+
+           if (!(fnCallback)((HACMDRIVERID)padid, pafd, dwInstance, padid->fdwSupport))
+               return FALSE;
+       }
+       /* the "formats" used by the filters are also reported */
+    }
+    return TRUE;
+}
+
+/**********************************************************************/
+
+MMRESULT WINAPI acmFormatEnumW(HACMDRIVER had, PACMFORMATDETAILSW pafd,
+                              ACMFORMATENUMCBW fnCallback, DWORD dwInstance,
+                              DWORD fdwEnum)
+{
+    PWINE_ACMDRIVERID          padid;
+    WAVEFORMATEX               wfxRef;
+    BOOL                       ret;
+
+    TRACE("(%p, %p, %p, %ld, %ld)\n",
+         had, pafd, fnCallback, dwInstance, fdwEnum);
+
+    if (!pafd)
+        return MMSYSERR_INVALPARAM;
+
+    if (pafd->cbStruct < sizeof(*pafd))
+        return MMSYSERR_INVALPARAM;
+
+    if (fdwEnum & (ACM_FORMATENUMF_WFORMATTAG|ACM_FORMATENUMF_NCHANNELS|
+                  ACM_FORMATENUMF_NSAMPLESPERSEC|ACM_FORMATENUMF_WBITSPERSAMPLE|
+                  ACM_FORMATENUMF_CONVERT|ACM_FORMATENUMF_SUGGEST))
+        wfxRef = *pafd->pwfx;
+
+    if ((fdwEnum & ACM_FORMATENUMF_HARDWARE) &&
+       !(fdwEnum & (ACM_FORMATENUMF_INPUT|ACM_FORMATENUMF_OUTPUT)))
+       return MMSYSERR_INVALPARAM;
+
+    if ((fdwEnum & ACM_FORMATENUMF_WFORMATTAG) &&
+       (pafd->dwFormatTag != pafd->pwfx->wFormatTag))
+       return MMSYSERR_INVALPARAM;
+
+    if (fdwEnum & (ACM_FORMATENUMF_CONVERT|ACM_FORMATENUMF_SUGGEST|
+                  ACM_FORMATENUMF_INPUT|ACM_FORMATENUMF_OUTPUT))
+       FIXME("Unsupported fdwEnum values %08lx\n", fdwEnum);
+
+    if (had) {
+       HACMDRIVERID    hadid;
+
+       if (acmDriverID((HACMOBJ)had, &hadid, 0) != MMSYSERR_NOERROR)
+           return MMSYSERR_INVALHANDLE;
+       MSACM_FormatEnumHelper(MSACM_GetDriverID(hadid), had, pafd, &wfxRef,
+                              fnCallback, dwInstance, fdwEnum);
+       return MMSYSERR_NOERROR;
+    }
+    for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) {
+           /* should check for codec only */
+           if ((padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) ||
+               acmDriverOpen(&had, (HACMDRIVERID)padid, 0) != MMSYSERR_NOERROR)
+               continue;
+           ret = MSACM_FormatEnumHelper(padid, had, pafd, &wfxRef,
+                                        fnCallback, dwInstance, fdwEnum);
+           acmDriverClose(had, 0);
+           if (!ret) break;
+    }
+    return MMSYSERR_NOERROR;
+}
+
+/***********************************************************************
+ *           acmFormatSuggest (MSACM32.@)
+ */
+MMRESULT WINAPI acmFormatSuggest(HACMDRIVER had, PWAVEFORMATEX pwfxSrc,
+                                PWAVEFORMATEX pwfxDst, DWORD cbwfxDst, DWORD fdwSuggest)
+{
+    ACMDRVFORMATSUGGEST        adfg;
+    MMRESULT           mmr;
+
+    TRACE("(%p, %p, %p, %ld, %ld)\n",
+         had, pwfxSrc, pwfxDst, cbwfxDst, fdwSuggest);
+
+    if (fdwSuggest & ~(ACM_FORMATSUGGESTF_NCHANNELS|ACM_FORMATSUGGESTF_NSAMPLESPERSEC|
+                      ACM_FORMATSUGGESTF_WBITSPERSAMPLE|ACM_FORMATSUGGESTF_WFORMATTAG))
+       return MMSYSERR_INVALFLAG;
+
+    adfg.cbStruct = sizeof(adfg);
+    adfg.fdwSuggest = fdwSuggest;
+    adfg.pwfxSrc = pwfxSrc;
+    adfg.cbwfxSrc = (pwfxSrc->wFormatTag == WAVE_FORMAT_PCM) ?
+       sizeof(WAVEFORMATEX) : (sizeof(WAVEFORMATEX) + pwfxSrc->cbSize);
+    adfg.pwfxDst = pwfxDst;
+    adfg.cbwfxDst = cbwfxDst;
+
+    if (had == NULL) {
+       PWINE_ACMDRIVERID       padid;
+
+       /* MS doc says: ACM finds the best suggestion.
+        * Well, first found will be the "best"
+        */
+       mmr = ACMERR_NOTPOSSIBLE;
+       for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) {
+           /* should check for codec only */
+           if ((padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) ||
+               acmDriverOpen(&had, (HACMDRIVERID)padid, 0) != MMSYSERR_NOERROR)
+               continue;
+
+           if (MSACM_Message(had, ACMDM_FORMAT_SUGGEST, (LPARAM)&adfg, 0L) == MMSYSERR_NOERROR) {
+               mmr = MMSYSERR_NOERROR;
+               break;
+           }
+           acmDriverClose(had, 0);
+       }
+    } else {
+       mmr = MSACM_Message(had, ACMDM_FORMAT_SUGGEST, (LPARAM)&adfg, 0L);
+    }
+    return mmr;
+}
+
+/***********************************************************************
+ *           acmFormatTagDetailsA (MSACM32.@)
+ */
+MMRESULT WINAPI acmFormatTagDetailsA(HACMDRIVER had, PACMFORMATTAGDETAILSA paftda,
+                                    DWORD fdwDetails)
+{
+    ACMFORMATTAGDETAILSW       aftdw;
+    MMRESULT                   mmr;
+
+    memset(&aftdw, 0, sizeof(aftdw));
+    aftdw.cbStruct = sizeof(aftdw);
+    aftdw.dwFormatTagIndex = paftda->dwFormatTagIndex;
+    aftdw.dwFormatTag = paftda->dwFormatTag;
+
+    mmr = acmFormatTagDetailsW(had, &aftdw, fdwDetails);
+    if (mmr == MMSYSERR_NOERROR) {
+       paftda->dwFormatTag = aftdw.dwFormatTag;
+       paftda->dwFormatTagIndex = aftdw.dwFormatTagIndex;
+       paftda->cbFormatSize = aftdw.cbFormatSize;
+       paftda->fdwSupport = aftdw.fdwSupport;
+       paftda->cStandardFormats = aftdw.cStandardFormats;
+        WideCharToMultiByte( CP_ACP, 0, aftdw.szFormatTag, -1, paftda->szFormatTag,
+                             sizeof(paftda->szFormatTag), NULL, NULL );
+    }
+    return mmr;
+}
+
+/***********************************************************************
+ *           acmFormatTagDetailsW (MSACM32.@)
+ */
+MMRESULT WINAPI acmFormatTagDetailsW(HACMDRIVER had, PACMFORMATTAGDETAILSW paftd,
+                                    DWORD fdwDetails)
+{
+    PWINE_ACMDRIVERID  padid;
+    MMRESULT           mmr = ACMERR_NOTPOSSIBLE;
+
+    TRACE("(%p, %p, %ld)\n", had, paftd, fdwDetails);
+
+    if (fdwDetails & ~(ACM_FORMATTAGDETAILSF_FORMATTAG|ACM_FORMATTAGDETAILSF_INDEX|
+                      ACM_FORMATTAGDETAILSF_LARGESTSIZE))
+       return MMSYSERR_INVALFLAG;
+
+    switch (fdwDetails) {
+    case ACM_FORMATTAGDETAILSF_FORMATTAG:
+       if (had == NULL) {
+           for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) {
+               /* should check for codec only */
+               if (!(padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) &&
+                   MSACM_FindFormatTagInCache(padid, paftd->dwFormatTag, NULL) &&
+                   acmDriverOpen(&had, (HACMDRIVERID)padid, 0) == 0) {
+                   mmr = MSACM_Message(had, ACMDM_FORMATTAG_DETAILS, (LPARAM)paftd, fdwDetails);
+                   acmDriverClose(had, 0);
+                   if (mmr == MMSYSERR_NOERROR) break;
+               }
+           }
+       } else {
+           PWINE_ACMDRIVER     pad = MSACM_GetDriver(had);
+
+           if (pad && MSACM_FindFormatTagInCache(pad->obj.pACMDriverID, paftd->dwFormatTag, NULL))
+               mmr = MSACM_Message(had, ACMDM_FORMATTAG_DETAILS, (LPARAM)paftd, fdwDetails);
+       }
+       break;
+
+    case ACM_FORMATTAGDETAILSF_INDEX:
+       if (had != NULL) {
+           PWINE_ACMDRIVER     pad = MSACM_GetDriver(had);
+
+           if (pad && paftd->dwFormatTagIndex < pad->obj.pACMDriverID->cFormatTags)
+               mmr = MSACM_Message(had, ACMDM_FORMATTAG_DETAILS, (LPARAM)paftd, fdwDetails);
+       }
+       break;
+
+    case ACM_FORMATTAGDETAILSF_LARGESTSIZE:
+       if (had == NULL) {
+           ACMFORMATTAGDETAILSW        tmp;
+           DWORD                       ft = paftd->dwFormatTag;
+
+           for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) {
+               /* should check for codec only */
+               if (!(padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) &&
+                   acmDriverOpen(&had, (HACMDRIVERID)padid, 0) == 0) {
+
+                   memset(&tmp, 0, sizeof(tmp));
+                   tmp.cbStruct = sizeof(tmp);
+                   tmp.dwFormatTag = ft;
+
+                   if (MSACM_Message(had, ACMDM_FORMATTAG_DETAILS,
+                                     (LPARAM)&tmp, fdwDetails) == MMSYSERR_NOERROR) {
+                       if (mmr == ACMERR_NOTPOSSIBLE ||
+                           paftd->cbFormatSize < tmp.cbFormatSize) {
+                           *paftd = tmp;
+                           mmr = MMSYSERR_NOERROR;
+                       }
+                   }
+                   acmDriverClose(had, 0);
+               }
+           }
+       } else {
+           mmr = MSACM_Message(had, ACMDM_FORMATTAG_DETAILS, (LPARAM)paftd, fdwDetails);
+       }
+       break;
+
+    default:
+       WARN("Unsupported fdwDetails=%08lx\n", fdwDetails);
+       mmr = MMSYSERR_ERROR;
+    }
+
+    if (mmr == MMSYSERR_NOERROR &&
+       paftd->dwFormatTag == WAVE_FORMAT_PCM && paftd->szFormatTag[0] == 0)
+        MultiByteToWideChar( CP_ACP, 0, "PCM", -1, paftd->szFormatTag,
+                             sizeof(paftd->szFormatTag)/sizeof(WCHAR) );
+
+    return mmr;
+}
+
+struct MSACM_FormatTagEnumWtoA_Instance {
+    PACMFORMATTAGDETAILSA      paftda;
+    DWORD                      dwInstance;
+    ACMFORMATTAGENUMCBA        fnCallback;
+};
+
+static BOOL CALLBACK MSACM_FormatTagEnumCallbackWtoA(HACMDRIVERID hadid,
+                                                    PACMFORMATTAGDETAILSW paftdw,
+                                                    DWORD dwInstance,
+                                                    DWORD fdwSupport)
+{
+    struct MSACM_FormatTagEnumWtoA_Instance* paftei;
+
+    paftei = (struct MSACM_FormatTagEnumWtoA_Instance*)dwInstance;
+
+    paftei->paftda->dwFormatTagIndex = paftdw->dwFormatTagIndex;
+    paftei->paftda->dwFormatTag = paftdw->dwFormatTag;
+    paftei->paftda->cbFormatSize = paftdw->cbFormatSize;
+    paftei->paftda->fdwSupport = paftdw->fdwSupport;
+    paftei->paftda->cStandardFormats = paftdw->cStandardFormats;
+    WideCharToMultiByte( CP_ACP, 0, paftdw->szFormatTag, -1, paftei->paftda->szFormatTag,
+                         sizeof(paftei->paftda->szFormatTag), NULL, NULL );
+
+    return (paftei->fnCallback)(hadid, paftei->paftda,
+                               paftei->dwInstance, fdwSupport);
+}
+
+/***********************************************************************
+ *           acmFormatTagEnumA (MSACM32.@)
+ */
+MMRESULT WINAPI acmFormatTagEnumA(HACMDRIVER had, PACMFORMATTAGDETAILSA paftda,
+                                 ACMFORMATTAGENUMCBA fnCallback, DWORD dwInstance,
+                                 DWORD fdwEnum)
+{
+    ACMFORMATTAGDETAILSW       aftdw;
+    struct MSACM_FormatTagEnumWtoA_Instance aftei;
+
+    if (!paftda)
+        return MMSYSERR_INVALPARAM;
+
+    if (paftda->cbStruct < sizeof(*paftda))
+        return MMSYSERR_INVALPARAM;
+
+    if (fdwEnum != 0)
+        return MMSYSERR_INVALFLAG;
+
+    memset(&aftdw, 0, sizeof(aftdw));
+    aftdw.cbStruct = sizeof(aftdw);
+    aftdw.dwFormatTagIndex = paftda->dwFormatTagIndex;
+    aftdw.dwFormatTag = paftda->dwFormatTag;
+
+    aftei.paftda = paftda;
+    aftei.dwInstance = dwInstance;
+    aftei.fnCallback = fnCallback;
+
+    return acmFormatTagEnumW(had, &aftdw, MSACM_FormatTagEnumCallbackWtoA,
+                            (DWORD)&aftei, fdwEnum);
+}
+
+/***********************************************************************
+ *           acmFormatTagEnumW (MSACM32.@)
+ */
+MMRESULT WINAPI acmFormatTagEnumW(HACMDRIVER had, PACMFORMATTAGDETAILSW paftd,
+                                 ACMFORMATTAGENUMCBW fnCallback, DWORD dwInstance,
+                                 DWORD fdwEnum)
+{
+    PWINE_ACMDRIVERID          padid;
+    int                                i;
+    BOOL                       bPcmDone = FALSE;
+
+    TRACE("(%p, %p, %p, %ld, %ld)\n",
+         had, paftd, fnCallback, dwInstance, fdwEnum);
+
+    if (!paftd)
+        return MMSYSERR_INVALPARAM;
+
+    if (paftd->cbStruct < sizeof(*paftd))
+        return MMSYSERR_INVALPARAM;
+
+    if (fdwEnum != 0)
+        return MMSYSERR_INVALFLAG;
+
+    /* (WS) MSDN info page says that if had != 0, then we should find
+     * the specific driver to get its tags from. Therefore I'm removing
+     * the FIXME call and adding a search block below. It also seems
+     * that the lack of this functionality was the responsible for 
+     * codecs to be multiply and incorrectly listed. 
+     */
+
+    /* if (had) FIXME("had != NULL, not supported\n"); */
+
+    if (had) {
+
+       if (acmDriverID((HACMOBJ)had, (HACMDRIVERID *)&padid, 0) != MMSYSERR_NOERROR)
+          return MMSYSERR_INVALHANDLE;
+
+       for (i = 0; i < padid->cFormatTags; i++) {
+         paftd->dwFormatTagIndex = i;
+         if (MSACM_Message(had, ACMDM_FORMATTAG_DETAILS,
+         (LPARAM)paftd, ACM_FORMATTAGDETAILSF_INDEX) == MMSYSERR_NOERROR) {
+            if (paftd->dwFormatTag == WAVE_FORMAT_PCM) {
+               if (paftd->szFormatTag[0] == 0)
+                  MultiByteToWideChar( CP_ACP, 0, "PCM", -1, paftd->szFormatTag,
+                        sizeof(paftd->szFormatTag)/sizeof(WCHAR) );
+               /* (WS) I'm preserving this PCM hack since it seems to be
+                * correct. Please notice this block was borrowed from
+                * below.
+                */
+               if (bPcmDone) continue;
+                  bPcmDone = TRUE;
+            }
+            if (!(fnCallback)((HACMDRIVERID)padid, paftd, dwInstance, padid->fdwSupport)) 
+                return MMSYSERR_NOERROR;
+         }
+       }
+
+    }
+
+    /* if had==0 then search for the first suitable driver */
+    else {
+       for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) {
+          /* should check for codec only */
+          if (!(padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) &&
+            acmDriverOpen(&had, (HACMDRIVERID)padid, 0) == MMSYSERR_NOERROR) {
+            for (i = 0; i < padid->cFormatTags; i++) {
+               paftd->dwFormatTagIndex = i;
+               if (MSACM_Message(had, ACMDM_FORMATTAG_DETAILS,
+                  (LPARAM)paftd, ACM_FORMATTAGDETAILSF_INDEX) == MMSYSERR_NOERROR) {
+                  if (paftd->dwFormatTag == WAVE_FORMAT_PCM) {
+                     if (paftd->szFormatTag[0] == 0)
+                        MultiByteToWideChar( CP_ACP, 0, "PCM", -1, paftd->szFormatTag,
+                                sizeof(paftd->szFormatTag)/sizeof(WCHAR) );
+                     /* FIXME (EPP): I'm not sure this is the correct
+                      * algorithm (should make more sense to apply the same
+                      * for all already loaded formats, but this will do
+                      * for now
+                      */
+                     if (bPcmDone) continue;
+                        bPcmDone = TRUE;
+                  }
+                  if (!(fnCallback)((HACMDRIVERID)padid, paftd, dwInstance, padid->fdwSupport)) {
+                      acmDriverClose(had, 0);
+                      return MMSYSERR_NOERROR;
+                  }
+               }
+            }
+          }
+          acmDriverClose(had, 0);
+       }
+    }
+    return MMSYSERR_NOERROR;
+}
diff --git a/reactos/dll/msacm/imaadp32/Makefile.in b/reactos/dll/msacm/imaadp32/Makefile.in
new file mode 100644 (file)
index 0000000..79cc94e
--- /dev/null
@@ -0,0 +1,12 @@
+TOPSRCDIR = @top_srcdir@
+TOPOBJDIR = ../../..
+SRCDIR    = @srcdir@
+VPATH     = @srcdir@
+MODULE    = imaadp32.acm
+IMPORTS   = winmm user32 kernel32
+
+C_SRCS = imaadp32.c
+
+@MAKE_DLL_RULES@
+
+### Dependencies:
diff --git a/reactos/dll/msacm/imaadp32/imaadp32.acm.spec b/reactos/dll/msacm/imaadp32/imaadp32.acm.spec
new file mode 100644 (file)
index 0000000..a0ba1c7
--- /dev/null
@@ -0,0 +1 @@
+@ stdcall DriverProc (long long long long long) ADPCM_DriverProc
diff --git a/reactos/dll/msacm/imaadp32/imaadp32.c b/reactos/dll/msacm/imaadp32/imaadp32.c
new file mode 100644 (file)
index 0000000..61d3d00
--- /dev/null
@@ -0,0 +1,938 @@
+/*
+ * IMA ADPCM handling
+ *
+ *      Copyright (C) 2001,2002                Eric Pouech
+ *
+ *
+ * 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 <assert.h>
+#include <stdarg.h>
+#include <string.h>
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "winnls.h"
+#include "mmsystem.h"
+#include "mmreg.h"
+#include "msacm.h"
+#include "msacmdrv.h"
+#include "wine/debug.h"
+
+/* see http://www.pcisys.net/~melanson/codecs/adpcm.txt for the details */
+
+WINE_DEFAULT_DEBUG_CHANNEL(adpcm);
+
+/***********************************************************************
+ *           ADPCM_drvOpen
+ */
+static DWORD   ADPCM_drvOpen(LPCSTR str)
+{
+    return 1;
+}
+
+/***********************************************************************
+ *           ADPCM_drvClose
+ */
+static DWORD   ADPCM_drvClose(DWORD dwDevID)
+{
+    return 1;
+}
+
+typedef struct tagAcmAdpcmData
+{
+    void (*convert)(PACMDRVSTREAMINSTANCE adsi,
+                   const unsigned char*, LPDWORD, unsigned char*, LPDWORD);
+    /* IMA encoding only */
+    BYTE       stepIndexL;
+    BYTE       stepIndexR;
+    /* short   sample; */
+} AcmAdpcmData;
+
+/* table to list all supported formats... those are the basic ones. this
+ * also helps given a unique index to each of the supported formats
+ */
+typedef        struct
+{
+    int                nChannels;
+    int                nBits;
+    int                rate;
+} Format;
+
+static Format PCM_Formats[] =
+{
+    {1,  8,  8000}, {2,  8,  8000}, {1, 16,  8000}, {2, 16,  8000},
+    {1,  8, 11025}, {2,  8, 11025}, {1, 16, 11025}, {2, 16, 11025},
+    {1,  8, 22050}, {2,  8, 22050}, {1, 16, 22050}, {2, 16, 22050},
+    {1,  8, 44100}, {2,  8, 44100}, {1, 16, 44100}, {2, 16, 44100},
+};
+
+static Format ADPCM_Formats[] =
+{
+    {1,  4,  8000}, {2,        4,  8000},  {1,  4, 11025}, {2,  4, 11025},
+    {1,  4, 22050}, {2,        4, 22050},  {1,  4, 44100}, {2,  4, 44100},
+};
+
+#define        NUM_PCM_FORMATS         (sizeof(PCM_Formats) / sizeof(PCM_Formats[0]))
+#define        NUM_ADPCM_FORMATS       (sizeof(ADPCM_Formats) / sizeof(ADPCM_Formats[0]))
+
+/***********************************************************************
+ *           ADPCM_GetFormatIndex
+ */
+static DWORD   ADPCM_GetFormatIndex(LPWAVEFORMATEX wfx)
+{
+    int        i, hi;
+    Format*    fmts;
+
+    switch (wfx->wFormatTag)
+    {
+    case WAVE_FORMAT_PCM:
+       hi = NUM_PCM_FORMATS;
+       fmts = PCM_Formats;
+       break;
+    case WAVE_FORMAT_IMA_ADPCM:
+       hi = NUM_ADPCM_FORMATS;
+       fmts = ADPCM_Formats;
+       break;
+    default:
+       return 0xFFFFFFFF;
+    }
+
+    for (i = 0; i < hi; i++)
+    {
+       if (wfx->nChannels == fmts[i].nChannels &&
+           wfx->nSamplesPerSec == fmts[i].rate &&
+           wfx->wBitsPerSample == fmts[i].nBits)
+           return i;
+    }
+
+    return 0xFFFFFFFF;
+}
+
+/***********************************************************************
+ *           R16
+ *
+ * Read a 16 bit sample (correctly handles endianess)
+ */
+static inline short  R16(const unsigned char* src)
+{
+    return (short)((unsigned short)src[0] | ((unsigned short)src[1] << 8));
+}
+
+/***********************************************************************
+ *           W16
+ *
+ * Write a 16 bit sample (correctly handles endianess)
+ */
+static inline void  W16(unsigned char* dst, short s)
+{
+    dst[0] = LOBYTE(s);
+    dst[1] = HIBYTE(s);
+}
+
+/* IMA (or DVI) APDCM codec routines */
+
+static const unsigned IMA_StepTable[89] =
+{
+    7, 8, 9, 10, 11, 12, 13, 14,
+    16, 17, 19, 21, 23, 25, 28, 31,
+    34, 37, 41, 45, 50, 55, 60, 66,
+    73, 80, 88, 97, 107, 118, 130, 143,
+    157, 173, 190, 209, 230, 253, 279, 307,
+    337, 371, 408, 449, 494, 544, 598, 658,
+    724, 796, 876, 963, 1060, 1166, 1282, 1411,
+    1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024,
+    3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484,
+    7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
+    15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794,
+    32767
+};
+
+static const int IMA_IndexTable[16] =
+{
+    -1, -1, -1, -1, 2, 4, 6, 8,
+    -1, -1, -1, -1, 2, 4, 6, 8
+};
+
+static inline void clamp_step_index(int* stepIndex)
+{
+    if (*stepIndex < 0 ) *stepIndex = 0;
+    if (*stepIndex > 88) *stepIndex = 88;
+}
+
+static inline void clamp_sample(int* sample)
+{
+    if (*sample < -32768) *sample = -32768;
+    if (*sample >  32767) *sample =  32767;
+}
+
+static inline void process_nibble(unsigned char code, int* stepIndex, int* sample)
+{
+    unsigned step;
+    int diff;
+
+    code &= 0x0F;
+
+    step = IMA_StepTable[*stepIndex];
+    diff = step >> 3;
+    if (code & 1) diff += step >> 2;
+    if (code & 2) diff += step >> 1;
+    if (code & 4) diff += step;
+    if (code & 8)      *sample -= diff;
+    else               *sample += diff;
+    clamp_sample(sample);
+    *stepIndex += IMA_IndexTable[code];
+    clamp_step_index(stepIndex);
+}
+
+static inline unsigned char generate_nibble(int in, int* stepIndex, int* sample)
+{
+    int effdiff, diff = in - *sample;
+    unsigned step;
+    unsigned char code;
+
+    if (diff < 0)
+    {
+        diff = -diff;
+        code = 8;
+    }
+    else
+    {
+        code = 0;
+    }
+
+    step = IMA_StepTable[*stepIndex];
+    effdiff = (step >> 3);
+    if (diff >= step)
+    {
+        code |= 4;
+        diff -= step;
+        effdiff += step;
+    }
+    step >>= 1;
+    if (diff >= step)
+    {
+        code |= 2;
+        diff -= step;
+        effdiff += step;
+    }
+    step >>= 1;
+    if (diff >= step)
+    {
+        code |= 1;
+        effdiff += step;
+    }
+    if (code & 8)       *sample -= effdiff;
+    else                *sample += effdiff;
+    clamp_sample(sample);
+    *stepIndex += IMA_IndexTable[code];
+    clamp_step_index(stepIndex);
+    return code;
+}
+
+static void cvtSSima16K(PACMDRVSTREAMINSTANCE adsi,
+                         const unsigned char* src, LPDWORD nsrc,
+                         unsigned char* dst, LPDWORD ndst)
+{
+    int         i;
+    int         sampleL, sampleR;
+    int                stepIndexL, stepIndexR;
+    int                nsamp_blk = ((LPIMAADPCMWAVEFORMAT)adsi->pwfxSrc)->wSamplesPerBlock;
+    int                nsamp;
+    /* compute the number of entire blocks we can decode...
+     * it's the min of the number of entire blocks in source buffer and the number
+     * of entire blocks in destination buffer
+     */
+    DWORD      nblock = min(*nsrc / adsi->pwfxSrc->nBlockAlign,
+                             *ndst / (nsamp_blk * 2 * 2));
+
+    *nsrc = nblock * adsi->pwfxSrc->nBlockAlign;
+    *ndst = nblock * (nsamp_blk * 2 * 2);
+
+    nsamp_blk--; /* remove the sample in block header */
+    for (; nblock > 0; nblock--)
+    {
+        const unsigned char* in_src = src;
+
+       /* handle headers first */
+       sampleL = R16(src);
+       stepIndexL = (unsigned)*(src + 2);
+        clamp_step_index(&stepIndexL);
+       src += 4;
+       W16(dst, sampleL);      dst += 2;
+
+       sampleR = R16(src);
+       stepIndexR = (unsigned)*(src + 2);
+        clamp_step_index(&stepIndexR);
+       src += 4;
+       W16(dst, sampleR);      dst += 2;
+
+       for (nsamp = nsamp_blk; nsamp > 0; nsamp -= 8)
+        {
+            for (i = 0; i < 4; i++)
+            {
+                process_nibble(*src, &stepIndexL, &sampleL);
+                W16(dst + (2 * i + 0) * 4 + 0, sampleL);
+                process_nibble(*src++ >> 4, &stepIndexL, &sampleL);
+                W16(dst + (2 * i + 1) * 4 + 0, sampleL);
+            }
+            for (i = 0; i < 4; i++)
+            {
+                process_nibble(*src , &stepIndexR, &sampleR);
+                W16(dst + (2 * i + 0) * 4 + 2, sampleR);
+                process_nibble(*src++ >>4, &stepIndexR, &sampleR);
+                W16(dst + (2 * i + 1) * 4 + 2, sampleR);
+            }
+            dst += 32;
+        }
+        /* we have now to realign the source pointer on block */
+        src = in_src + adsi->pwfxSrc->nBlockAlign;
+    }
+}
+
+static void cvtMMima16K(PACMDRVSTREAMINSTANCE adsi,
+                         const unsigned char* src, LPDWORD nsrc,
+                         unsigned char* dst, LPDWORD ndst)
+{
+    int                sample;
+    int                stepIndex;
+    int                nsamp_blk = ((LPIMAADPCMWAVEFORMAT)adsi->pwfxSrc)->wSamplesPerBlock;
+    int                nsamp;
+    /* compute the number of entire blocks we can decode...
+     * it's the min of the number of entire blocks in source buffer and the number
+     * of entire blocks in destination buffer
+     */
+    DWORD      nblock = min(*nsrc / adsi->pwfxSrc->nBlockAlign,
+                             *ndst / (nsamp_blk * 2));
+
+    *nsrc = nblock * adsi->pwfxSrc->nBlockAlign;
+    *ndst = nblock * nsamp_blk * 2;
+
+    nsamp_blk--; /* remove the sample in block header */
+    for (; nblock > 0; nblock--)
+    {
+        const unsigned char*    in_src = src;
+
+       /* handle header first */
+       sample = R16(src);
+       stepIndex = (unsigned)*(src + 2);
+        clamp_step_index(&stepIndex);
+       src += 4;
+       W16(dst, sample);       dst += 2;
+
+       for (nsamp = nsamp_blk; nsamp > 0; nsamp -= 2)
+        {
+            process_nibble(*src, &stepIndex, &sample);
+           W16(dst, sample); dst += 2;
+            process_nibble(*src++ >> 4, &stepIndex, &sample);
+           W16(dst, sample); dst += 2;
+       }
+        /* we have now to realign the source pointer on block */
+        src = in_src + adsi->pwfxSrc->nBlockAlign;
+    }
+}
+
+static void cvtSS16imaK(PACMDRVSTREAMINSTANCE adsi,
+                         const unsigned char* src, LPDWORD nsrc,
+                         unsigned char* dst, LPDWORD ndst)
+{
+    int                stepIndexL, stepIndexR;
+    int                sampleL, sampleR;
+    BYTE       code1, code2;
+    int                nsamp_blk = ((LPIMAADPCMWAVEFORMAT)adsi->pwfxDst)->wSamplesPerBlock;
+    int                i, nsamp;
+    /* compute the number of entire blocks we can decode...
+     * it's the min of the number of entire blocks in source buffer and the number
+     * of entire blocks in destination buffer
+     */
+    DWORD      nblock = min(*nsrc / (nsamp_blk * 2 * 2),
+                             *ndst / adsi->pwfxDst->nBlockAlign);
+
+    *nsrc = nblock * (nsamp_blk * 2 * 2);
+    *ndst = nblock * adsi->pwfxDst->nBlockAlign;
+
+    stepIndexL = ((AcmAdpcmData*)adsi->dwDriver)->stepIndexL;
+    stepIndexR = ((AcmAdpcmData*)adsi->dwDriver)->stepIndexR;
+
+    nsamp_blk--; /* so that we won't count the sample in header while filling the block */
+
+    for (; nblock > 0; nblock--)
+    {
+        char*   in_dst = dst;
+
+        /* generate header */
+       sampleL = R16(src);  src += 2;
+       W16(dst, sampleL); dst += 2;
+       *dst = (unsigned char)(unsigned)stepIndexL;
+       dst += 2;
+
+       sampleR = R16(src); src += 2;
+       W16(dst, sampleR); dst += 2;
+       *dst = (unsigned char)(unsigned)stepIndexR;
+       dst += 2;
+
+       for (nsamp = nsamp_blk; nsamp > 0; nsamp -= 8)
+        {
+            for (i = 0; i < 4; i++)
+            {
+                code1 = generate_nibble(R16(src + (2 * i + 0) * 2 + 0),
+                                        &stepIndexL, &sampleL);
+                code2 = generate_nibble(R16(src + (2 * i + 1) * 2 + 0),
+                                        &stepIndexL, &sampleL);
+                *dst++ = (code1 << 4) | code2;
+            }
+            for (i = 0; i < 4; i++)
+            {
+                code1 = generate_nibble(R16(src + (2 * i + 0) * 2 + 1),
+                                        &stepIndexR, &sampleR);
+                code2 = generate_nibble(R16(src + (2 * i + 1) * 2 + 1),
+                                        &stepIndexR, &sampleR);
+                *dst++ = (code1 << 4) | code2;
+            }
+            src += 32;
+       }
+       dst = in_dst + adsi->pwfxDst->nBlockAlign;
+    }
+    ((AcmAdpcmData*)adsi->dwDriver)->stepIndexL = stepIndexL;
+    ((AcmAdpcmData*)adsi->dwDriver)->stepIndexR = stepIndexR;
+}
+
+static void cvtMM16imaK(PACMDRVSTREAMINSTANCE adsi,
+                         const unsigned char* src, LPDWORD nsrc,
+                         unsigned char* dst, LPDWORD ndst)
+{
+    int                stepIndex;
+    int                sample;
+    BYTE       code1, code2;
+    int                nsamp_blk = ((LPIMAADPCMWAVEFORMAT)adsi->pwfxDst)->wSamplesPerBlock;
+    int                nsamp;
+    /* compute the number of entire blocks we can decode...
+     * it's the min of the number of entire blocks in source buffer and the number
+     * of entire blocks in destination buffer
+     */
+    DWORD      nblock = min(*nsrc / (nsamp_blk * 2),
+                             *ndst / adsi->pwfxDst->nBlockAlign);
+
+    *nsrc = nblock * (nsamp_blk * 2);
+    *ndst = nblock * adsi->pwfxDst->nBlockAlign;
+
+    stepIndex = ((AcmAdpcmData*)adsi->dwDriver)->stepIndexL;
+    nsamp_blk--; /* so that we won't count the sample in header while filling the block */
+
+    for (; nblock > 0; nblock--)
+    {
+        char*   in_dst = dst;
+
+        /* generate header */
+        /* FIXME: what about the last effective sample from previous block ??? */
+        /* perhaps something like:
+         *      sample += R16(src);
+         *      clamp_sample(sample);
+         * and with :
+         *      + saving the sample in adsi->dwDriver when all blocks are done
+         +      + reset should set the field in adsi->dwDriver to 0 too
+         */
+       sample = R16(src); src += 2;
+       W16(dst, sample); dst += 2;
+       *dst = (unsigned char)(unsigned)stepIndex;
+       dst += 2;
+
+       for (nsamp = nsamp_blk; nsamp > 0; nsamp -= 2)
+        {
+            code1 = generate_nibble(R16(src), &stepIndex, &sample);
+            src += 2;
+            code2 = generate_nibble(R16(src), &stepIndex, &sample);
+            src += 2;
+            *dst++ = (code1 << 4) | code2;
+       }
+       dst = in_dst + adsi->pwfxDst->nBlockAlign;
+    }
+    ((AcmAdpcmData*)adsi->dwDriver)->stepIndexL = stepIndex;
+}
+
+/***********************************************************************
+ *           ADPCM_DriverDetails
+ *
+ */
+static LRESULT ADPCM_DriverDetails(PACMDRIVERDETAILSW add)
+{
+    add->fccType = ACMDRIVERDETAILS_FCCTYPE_AUDIOCODEC;
+    add->fccComp = ACMDRIVERDETAILS_FCCCOMP_UNDEFINED;
+    add->wMid = 0xFF;
+    add->wPid = 0x00;
+    add->vdwACM = 0x01000000;
+    add->vdwDriver = 0x01000000;
+    add->fdwSupport = ACMDRIVERDETAILS_SUPPORTF_CODEC;
+    add->cFormatTags = 2; /* PCM, IMA ADPCM */
+    add->cFilterTags = 0;
+    add->hicon = NULL;
+    MultiByteToWideChar( CP_ACP, 0, "WINE-ADPCM", -1,
+                         add->szShortName, sizeof(add->szShortName)/sizeof(WCHAR) );
+    MultiByteToWideChar( CP_ACP, 0, "Wine IMA ADPCM converter", -1,
+                         add->szLongName, sizeof(add->szLongName)/sizeof(WCHAR) );
+    MultiByteToWideChar( CP_ACP, 0, "Brought to you by the Wine team...", -1,
+                         add->szCopyright, sizeof(add->szCopyright)/sizeof(WCHAR) );
+    MultiByteToWideChar( CP_ACP, 0, "Refer to LICENSE file", -1,
+                         add->szLicensing, sizeof(add->szLicensing)/sizeof(WCHAR) );
+    add->szFeatures[0] = 0;
+
+    return MMSYSERR_NOERROR;
+}
+
+/***********************************************************************
+ *           ADPCM_FormatTagDetails
+ *
+ */
+static LRESULT ADPCM_FormatTagDetails(PACMFORMATTAGDETAILSW aftd, DWORD dwQuery)
+{
+    static WCHAR szPcm[]={'P','C','M',0};
+    static WCHAR szImaAdPcm[]={'I','M','A',' ','A','d','P','C','M',0};
+
+    switch (dwQuery)
+    {
+    case ACM_FORMATTAGDETAILSF_INDEX:
+       if (aftd->dwFormatTagIndex >= 2) return ACMERR_NOTPOSSIBLE;
+       break;
+    case ACM_FORMATTAGDETAILSF_LARGESTSIZE:
+       if (aftd->dwFormatTag == WAVE_FORMAT_UNKNOWN)
+        {
+            aftd->dwFormatTagIndex = 1; /* WAVE_FORMAT_IMA_ADPCM is bigger than PCM */
+           break;
+       }
+       /* fall thru */
+    case ACM_FORMATTAGDETAILSF_FORMATTAG:
+       switch (aftd->dwFormatTag)
+        {
+       case WAVE_FORMAT_PCM:           aftd->dwFormatTagIndex = 0; break;
+       case WAVE_FORMAT_IMA_ADPCM:     aftd->dwFormatTagIndex = 1; break;
+       default:                        return ACMERR_NOTPOSSIBLE;
+       }
+       break;
+    default:
+       WARN("Unsupported query %08lx\n", dwQuery);
+       return MMSYSERR_NOTSUPPORTED;
+    }
+
+    aftd->fdwSupport = ACMDRIVERDETAILS_SUPPORTF_CODEC;
+    switch (aftd->dwFormatTagIndex)
+    {
+    case 0:
+       aftd->dwFormatTag = WAVE_FORMAT_PCM;
+       aftd->cbFormatSize = sizeof(PCMWAVEFORMAT);
+       aftd->cStandardFormats = NUM_PCM_FORMATS;
+        lstrcpyW(aftd->szFormatTag, szPcm);
+        break;
+    case 1:
+       aftd->dwFormatTag = WAVE_FORMAT_IMA_ADPCM;
+       aftd->cbFormatSize = sizeof(IMAADPCMWAVEFORMAT);
+       aftd->cStandardFormats = NUM_ADPCM_FORMATS;
+        lstrcpyW(aftd->szFormatTag, szImaAdPcm);
+       break;
+    }
+    return MMSYSERR_NOERROR;
+}
+
+/***********************************************************************
+ *           ADPCM_FormatDetails
+ *
+ */
+static LRESULT ADPCM_FormatDetails(PACMFORMATDETAILSW afd, DWORD dwQuery)
+{
+    switch (dwQuery)
+    {
+    case ACM_FORMATDETAILSF_FORMAT:
+       if (ADPCM_GetFormatIndex(afd->pwfx) == 0xFFFFFFFF) return ACMERR_NOTPOSSIBLE;
+       break;
+    case ACM_FORMATDETAILSF_INDEX:
+       afd->pwfx->wFormatTag = afd->dwFormatTag;
+       switch (afd->dwFormatTag)
+        {
+       case WAVE_FORMAT_PCM:
+           if (afd->dwFormatIndex >= NUM_PCM_FORMATS) return ACMERR_NOTPOSSIBLE;
+           afd->pwfx->nChannels = PCM_Formats[afd->dwFormatIndex].nChannels;
+           afd->pwfx->nSamplesPerSec = PCM_Formats[afd->dwFormatIndex].rate;
+           afd->pwfx->wBitsPerSample = PCM_Formats[afd->dwFormatIndex].nBits;
+           /* native MSACM uses a PCMWAVEFORMAT structure, so cbSize is not accessible
+            * afd->pwfx->cbSize = 0;
+            */
+           afd->pwfx->nBlockAlign =
+               (afd->pwfx->nChannels * afd->pwfx->wBitsPerSample) / 8;
+           afd->pwfx->nAvgBytesPerSec =
+               afd->pwfx->nSamplesPerSec * afd->pwfx->nBlockAlign;
+           break;
+       case WAVE_FORMAT_IMA_ADPCM:
+           if (afd->dwFormatIndex >= NUM_ADPCM_FORMATS) return ACMERR_NOTPOSSIBLE;
+           afd->pwfx->nChannels = ADPCM_Formats[afd->dwFormatIndex].nChannels;
+           afd->pwfx->nSamplesPerSec = ADPCM_Formats[afd->dwFormatIndex].rate;
+           afd->pwfx->wBitsPerSample = ADPCM_Formats[afd->dwFormatIndex].nBits;
+           afd->pwfx->nBlockAlign = 1024;
+           /* we got 4 bits per sample */
+           afd->pwfx->nAvgBytesPerSec =
+               (afd->pwfx->nSamplesPerSec * 4) / 8;
+            if (afd->cbwfx >= sizeof(WAVEFORMATEX))
+                afd->pwfx->cbSize = sizeof(WORD);
+            if (afd->cbwfx >= sizeof(IMAADPCMWAVEFORMAT))
+                ((IMAADPCMWAVEFORMAT*)afd->pwfx)->wSamplesPerBlock = (1024 - 4 * afd->pwfx->nChannels) * (2 / afd->pwfx->nChannels) + 1;
+           break;
+       default:
+           WARN("Unsupported tag %08lx\n", afd->dwFormatTag);
+           return MMSYSERR_INVALPARAM;
+       }
+       break;
+    default:
+       WARN("Unsupported query %08lx\n", dwQuery);
+       return MMSYSERR_NOTSUPPORTED;
+    }
+    afd->fdwSupport = ACMDRIVERDETAILS_SUPPORTF_CODEC;
+    afd->szFormat[0] = 0; /* let MSACM format this for us... */
+
+    return MMSYSERR_NOERROR;
+}
+
+/***********************************************************************
+ *           ADPCM_FormatSuggest
+ *
+ */
+static LRESULT ADPCM_FormatSuggest(PACMDRVFORMATSUGGEST adfs)
+{
+    /* some tests ... */
+    if (adfs->cbwfxSrc < sizeof(PCMWAVEFORMAT) ||
+       adfs->cbwfxDst < sizeof(PCMWAVEFORMAT) ||
+       ADPCM_GetFormatIndex(adfs->pwfxSrc) == 0xFFFFFFFF) return ACMERR_NOTPOSSIBLE;
+    /* FIXME: should do those tests against the real size (according to format tag */
+
+    /* If no suggestion for destination, then copy source value */
+    if (!(adfs->fdwSuggest & ACM_FORMATSUGGESTF_NCHANNELS))
+       adfs->pwfxDst->nChannels = adfs->pwfxSrc->nChannels;
+    if (!(adfs->fdwSuggest & ACM_FORMATSUGGESTF_NSAMPLESPERSEC))
+        adfs->pwfxDst->nSamplesPerSec = adfs->pwfxSrc->nSamplesPerSec;
+
+    if (!(adfs->fdwSuggest & ACM_FORMATSUGGESTF_WBITSPERSAMPLE))
+    {
+       if (adfs->pwfxSrc->wFormatTag == WAVE_FORMAT_PCM)
+            adfs->pwfxDst->wBitsPerSample = 4;
+        else
+            adfs->pwfxDst->wBitsPerSample = 16;
+    }
+    if (!(adfs->fdwSuggest & ACM_FORMATSUGGESTF_WFORMATTAG))
+    {
+       if (adfs->pwfxSrc->wFormatTag == WAVE_FORMAT_PCM)
+            adfs->pwfxDst->wFormatTag = WAVE_FORMAT_IMA_ADPCM;
+        else
+            adfs->pwfxDst->wFormatTag = WAVE_FORMAT_PCM;
+    }
+
+    /* check if result is ok */
+    if (ADPCM_GetFormatIndex(adfs->pwfxDst) == 0xFFFFFFFF) return ACMERR_NOTPOSSIBLE;
+
+    /* recompute other values */
+    switch (adfs->pwfxDst->wFormatTag)
+    {
+    case WAVE_FORMAT_PCM:
+        adfs->pwfxDst->nBlockAlign = (adfs->pwfxDst->nChannels * adfs->pwfxDst->wBitsPerSample) / 8;
+        adfs->pwfxDst->nAvgBytesPerSec = adfs->pwfxDst->nSamplesPerSec * adfs->pwfxDst->nBlockAlign;
+        break;
+    case WAVE_FORMAT_IMA_ADPCM:
+        adfs->pwfxDst->nBlockAlign = 1024;
+        /* FIXME: not handling header overhead */
+        adfs->pwfxDst->nAvgBytesPerSec = ((adfs->pwfxDst->nSamplesPerSec * 4) / 8) * adfs->pwfxSrc->nChannels;
+        ((IMAADPCMWAVEFORMAT*)adfs->pwfxDst)->wSamplesPerBlock = (1024 - 4 * adfs->pwfxSrc->nChannels) * (2 / adfs->pwfxSrc->nChannels) + 1;
+        TRACE("setting spb=%u\n", ((IMAADPCMWAVEFORMAT*)adfs->pwfxDst)->wSamplesPerBlock);
+        break;
+    default:
+        FIXME("\n");
+        break;
+    }
+
+    return MMSYSERR_NOERROR;
+}
+
+/***********************************************************************
+ *           ADPCM_Reset
+ *
+ */
+static void    ADPCM_Reset(PACMDRVSTREAMINSTANCE adsi, AcmAdpcmData* aad)
+{
+    aad->stepIndexL = aad->stepIndexR = 0;
+}
+
+/***********************************************************************
+ *           ADPCM_StreamOpen
+ *
+ */
+static LRESULT ADPCM_StreamOpen(PACMDRVSTREAMINSTANCE adsi)
+{
+    AcmAdpcmData*      aad;
+    unsigned            nspb;
+
+    assert(!(adsi->fdwOpen & ACM_STREAMOPENF_ASYNC));
+
+    if (ADPCM_GetFormatIndex(adsi->pwfxSrc) == 0xFFFFFFFF ||
+       ADPCM_GetFormatIndex(adsi->pwfxDst) == 0xFFFFFFFF)
+       return ACMERR_NOTPOSSIBLE;
+
+    aad = HeapAlloc(GetProcessHeap(), 0, sizeof(AcmAdpcmData));
+    if (aad == 0) return MMSYSERR_NOMEM;
+
+    adsi->dwDriver = (DWORD)aad;
+
+    if (adsi->pwfxSrc->wFormatTag == WAVE_FORMAT_PCM &&
+       adsi->pwfxDst->wFormatTag == WAVE_FORMAT_PCM)
+    {
+       goto theEnd;
+    }
+    else if (adsi->pwfxSrc->wFormatTag == WAVE_FORMAT_IMA_ADPCM &&
+             adsi->pwfxDst->wFormatTag == WAVE_FORMAT_PCM)
+    {
+       /* resampling or mono <=> stereo not available
+         * ADPCM algo only define 16 bit per sample output
+         */
+       if (adsi->pwfxSrc->nSamplesPerSec != adsi->pwfxDst->nSamplesPerSec ||
+           adsi->pwfxSrc->nChannels != adsi->pwfxDst->nChannels ||
+            adsi->pwfxDst->wBitsPerSample != 16)
+           goto theEnd;
+
+        nspb = ((LPIMAADPCMWAVEFORMAT)adsi->pwfxSrc)->wSamplesPerBlock;
+        TRACE("spb=%u\n", nspb);
+
+        /* we check that in a block, after the header, samples are present on
+         * 4-sample packet pattern
+         * we also check that the block alignement is bigger than the expected size
+         */
+        if (((nspb - 1) & 3) != 0) goto theEnd;
+        if ((((nspb - 1) / 2) + 4) * adsi->pwfxSrc->nChannels < adsi->pwfxSrc->nBlockAlign)
+            goto theEnd;
+
+       /* adpcm decoding... */
+       if (adsi->pwfxDst->wBitsPerSample == 16 && adsi->pwfxDst->nChannels == 2)
+           aad->convert = cvtSSima16K;
+       if (adsi->pwfxDst->wBitsPerSample == 16 && adsi->pwfxDst->nChannels == 1)
+           aad->convert = cvtMMima16K;
+    }
+    else if (adsi->pwfxSrc->wFormatTag == WAVE_FORMAT_PCM &&
+             adsi->pwfxDst->wFormatTag == WAVE_FORMAT_IMA_ADPCM)
+    {
+       if (adsi->pwfxSrc->nSamplesPerSec != adsi->pwfxDst->nSamplesPerSec ||
+           adsi->pwfxSrc->nChannels != adsi->pwfxDst->nChannels ||
+            adsi->pwfxSrc->wBitsPerSample != 16)
+           goto theEnd;
+
+        nspb = ((LPIMAADPCMWAVEFORMAT)adsi->pwfxDst)->wSamplesPerBlock;
+        TRACE("spb=%u\n", nspb);
+
+        /* we check that in a block, after the header, samples are present on
+         * 4-sample packet pattern
+         * we also check that the block alignement is bigger than the expected size
+         */
+        if (((nspb - 1) & 3) != 0) goto theEnd;
+        if ((((nspb - 1) / 2) + 4) * adsi->pwfxDst->nChannels < adsi->pwfxDst->nBlockAlign)
+            goto theEnd;
+
+       /* adpcm coding... */
+       if (adsi->pwfxSrc->wBitsPerSample == 16 && adsi->pwfxSrc->nChannels == 2)
+           aad->convert = cvtSS16imaK;
+       if (adsi->pwfxSrc->wBitsPerSample == 16 && adsi->pwfxSrc->nChannels == 1)
+           aad->convert = cvtMM16imaK;
+    }
+    else goto theEnd;
+    ADPCM_Reset(adsi, aad);
+
+    return MMSYSERR_NOERROR;
+
+ theEnd:
+    HeapFree(GetProcessHeap(), 0, aad);
+    adsi->dwDriver = 0L;
+    return MMSYSERR_NOTSUPPORTED;
+}
+
+/***********************************************************************
+ *           ADPCM_StreamClose
+ *
+ */
+static LRESULT ADPCM_StreamClose(PACMDRVSTREAMINSTANCE adsi)
+{
+    HeapFree(GetProcessHeap(), 0, (void*)adsi->dwDriver);
+    return MMSYSERR_NOERROR;
+}
+
+/***********************************************************************
+ *           ADPCM_round
+ *
+ */
+static inline DWORD    ADPCM_round(DWORD a, DWORD b, DWORD c)
+{
+    assert(a && b && c);
+    /* to be sure, always return an entire number of c... */
+    return ((double)a * (double)b + (double)c - 1) / (double)c;
+}
+
+/***********************************************************************
+ *           ADPCM_StreamSize
+ *
+ */
+static LRESULT ADPCM_StreamSize(PACMDRVSTREAMINSTANCE adsi, PACMDRVSTREAMSIZE adss)
+{
+    switch (adss->fdwSize)
+    {
+    case ACM_STREAMSIZEF_DESTINATION:
+       /* cbDstLength => cbSrcLength */
+       if (adsi->pwfxSrc->wFormatTag == WAVE_FORMAT_PCM &&
+           adsi->pwfxDst->wFormatTag == WAVE_FORMAT_IMA_ADPCM)
+        {
+           /* don't take block overhead into account, doesn't matter too much */
+           adss->cbSrcLength = adss->cbDstLength * 4;
+       }
+        else if (adsi->pwfxSrc->wFormatTag == WAVE_FORMAT_IMA_ADPCM &&
+                 adsi->pwfxDst->wFormatTag == WAVE_FORMAT_PCM)
+        {
+           FIXME("misses the block header overhead\n");
+           adss->cbSrcLength = 256 + adss->cbDstLength / 4;
+       }
+        else
+        {
+           return MMSYSERR_NOTSUPPORTED;
+       }
+       break;
+    case ACM_STREAMSIZEF_SOURCE:
+       /* cbSrcLength => cbDstLength */
+       if (adsi->pwfxSrc->wFormatTag == WAVE_FORMAT_PCM &&
+           adsi->pwfxDst->wFormatTag == WAVE_FORMAT_IMA_ADPCM)
+        {
+           FIXME("misses the block header overhead\n");
+           adss->cbDstLength = 256 + adss->cbSrcLength / 4;
+       }
+        else if (adsi->pwfxSrc->wFormatTag == WAVE_FORMAT_IMA_ADPCM &&
+                 adsi->pwfxDst->wFormatTag == WAVE_FORMAT_PCM)
+        {
+           /* don't take block overhead into account, doesn't matter too much */
+           adss->cbDstLength = adss->cbSrcLength * 4;
+       }
+        else
+        {
+           return MMSYSERR_NOTSUPPORTED;
+       }
+       break;
+    default:
+       WARN("Unsupported query %08lx\n", adss->fdwSize);
+       return MMSYSERR_NOTSUPPORTED;
+    }
+    return MMSYSERR_NOERROR;
+}
+
+/***********************************************************************
+ *           ADPCM_StreamConvert
+ *
+ */
+static LRESULT ADPCM_StreamConvert(PACMDRVSTREAMINSTANCE adsi, PACMDRVSTREAMHEADER adsh)
+{
+    AcmAdpcmData*      aad = (AcmAdpcmData*)adsi->dwDriver;
+    DWORD              nsrc = adsh->cbSrcLength;
+    DWORD              ndst = adsh->cbDstLength;
+
+    if (adsh->fdwConvert &
+       ~(ACM_STREAMCONVERTF_BLOCKALIGN|
+         ACM_STREAMCONVERTF_END|
+         ACM_STREAMCONVERTF_START))
+    {
+       FIXME("Unsupported fdwConvert (%08lx), ignoring it\n", adsh->fdwConvert);
+    }
+    /* ACM_STREAMCONVERTF_BLOCKALIGN
+     * currently all conversions are block aligned, so do nothing for this flag
+     * ACM_STREAMCONVERTF_END
+     * no pending data, so do nothing for this flag
+     */
+    if ((adsh->fdwConvert & ACM_STREAMCONVERTF_START))
+    {
+       ADPCM_Reset(adsi, aad);
+    }
+
+    aad->convert(adsi, adsh->pbSrc, &nsrc, adsh->pbDst, &ndst);
+    adsh->cbSrcLengthUsed = nsrc;
+    adsh->cbDstLengthUsed = ndst;
+
+    return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ *                     ADPCM_DriverProc                        [exported]
+ */
+LRESULT CALLBACK       ADPCM_DriverProc(DWORD dwDevID, HDRVR hDriv, UINT wMsg,
+                                        LPARAM dwParam1, LPARAM dwParam2)
+{
+    TRACE("(%08lx %08lx %04x %08lx %08lx);\n",
+         dwDevID, (DWORD)hDriv, wMsg, dwParam1, dwParam2);
+
+    switch (wMsg)
+    {
+    case DRV_LOAD:             return 1;
+    case DRV_FREE:             return 1;
+    case DRV_OPEN:             return ADPCM_drvOpen((LPSTR)dwParam1);
+    case DRV_CLOSE:            return ADPCM_drvClose(dwDevID);
+    case DRV_ENABLE:           return 1;
+    case DRV_DISABLE:          return 1;
+    case DRV_QUERYCONFIGURE:   return 1;
+    case DRV_CONFIGURE:                MessageBoxA(0, "MSACM IMA ADPCM filter !", "Wine Driver", MB_OK); return 1;
+    case DRV_INSTALL:          return DRVCNF_RESTART;
+    case DRV_REMOVE:           return DRVCNF_RESTART;
+
+    case ACMDM_DRIVER_NOTIFY:
+       /* no caching from other ACM drivers is done so far */
+       return MMSYSERR_NOERROR;
+
+    case ACMDM_DRIVER_DETAILS:
+       return ADPCM_DriverDetails((PACMDRIVERDETAILSW)dwParam1);
+
+    case ACMDM_FORMATTAG_DETAILS:
+       return ADPCM_FormatTagDetails((PACMFORMATTAGDETAILSW)dwParam1, dwParam2);
+
+    case ACMDM_FORMAT_DETAILS:
+       return ADPCM_FormatDetails((PACMFORMATDETAILSW)dwParam1, dwParam2);
+
+    case ACMDM_FORMAT_SUGGEST:
+       return ADPCM_FormatSuggest((PACMDRVFORMATSUGGEST)dwParam1);
+
+    case ACMDM_STREAM_OPEN:
+       return ADPCM_StreamOpen((PACMDRVSTREAMINSTANCE)dwParam1);
+
+    case ACMDM_STREAM_CLOSE:
+       return ADPCM_StreamClose((PACMDRVSTREAMINSTANCE)dwParam1);
+
+    case ACMDM_STREAM_SIZE:
+       return ADPCM_StreamSize((PACMDRVSTREAMINSTANCE)dwParam1, (PACMDRVSTREAMSIZE)dwParam2);
+
+    case ACMDM_STREAM_CONVERT:
+       return ADPCM_StreamConvert((PACMDRVSTREAMINSTANCE)dwParam1, (PACMDRVSTREAMHEADER)dwParam2);
+
+    case ACMDM_HARDWARE_WAVE_CAPS_INPUT:
+    case ACMDM_HARDWARE_WAVE_CAPS_OUTPUT:
+       /* this converter is not a hardware driver */
+    case ACMDM_FILTERTAG_DETAILS:
+    case ACMDM_FILTER_DETAILS:
+       /* this converter is not a filter */
+    case ACMDM_STREAM_RESET:
+       /* only needed for asynchronous driver... we aren't, so just say it */
+       return MMSYSERR_NOTSUPPORTED;
+    case ACMDM_STREAM_PREPARE:
+    case ACMDM_STREAM_UNPREPARE:
+       /* nothing special to do here... so don't do anything */
+       return MMSYSERR_NOERROR;
+
+    default:
+       return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
+    }
+    return 0;
+}
diff --git a/reactos/dll/msacm/internal.c b/reactos/dll/msacm/internal.c
new file mode 100644 (file)
index 0000000..9cb8002
--- /dev/null
@@ -0,0 +1,417 @@
+/* -*- tab-width: 8; c-basic-offset: 4 -*- */
+
+/*
+ *      MSACM32 library
+ *
+ *      Copyright 1998  Patrik Stridvall
+ *               1999  Eric Pouech
+ *
+ * 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 <stdarg.h>
+#include <string.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "winerror.h"
+#include "winreg.h"
+#include "mmsystem.h"
+#include "mmreg.h"
+#include "msacm.h"
+#include "msacmdrv.h"
+#include "wineacm.h"
+#include "wine/debug.h"
+#include "wine/unicode.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msacm);
+
+/**********************************************************************/
+
+HANDLE MSACM_hHeap = NULL;
+PWINE_ACMDRIVERID MSACM_pFirstACMDriverID = NULL;
+PWINE_ACMDRIVERID MSACM_pLastACMDriverID = NULL;
+
+#if 0
+/***********************************************************************
+ *           MSACM_DumpCache
+ */
+static void MSACM_DumpCache(PWINE_ACMDRIVERID padid)
+{
+    unsigned   i;
+
+    TRACE("cFilterTags=%lu cFormatTags=%lu fdwSupport=%08lx\n",
+         padid->cFilterTags, padid->cFormatTags, padid->fdwSupport);
+    for (i = 0; i < padid->cache->cFormatTags; i++) {
+       TRACE("\tdwFormatTag=%lu cbwfx=%lu\n",
+             padid->aFormatTag[i].dwFormatTag, padid->aFormatTag[i].cbwfx);
+    }
+}
+#endif
+
+/***********************************************************************
+ *           MSACM_FindFormatTagInCache                [internal]
+ *
+ *     Returns TRUE is the format tag fmtTag is present in the cache.
+ *     If so, idx is set to its index.
+ */
+BOOL MSACM_FindFormatTagInCache(WINE_ACMDRIVERID* padid, DWORD fmtTag, LPDWORD idx)
+{
+    unsigned   i;
+
+    for (i = 0; i < padid->cFormatTags; i++) {
+       if (padid->aFormatTag[i].dwFormatTag == fmtTag) {
+           if (idx) *idx = i;
+           return TRUE;
+       }
+    }
+    return FALSE;
+}
+
+/***********************************************************************
+ *           MSACM_FillCache
+ */
+static BOOL MSACM_FillCache(PWINE_ACMDRIVERID padid)
+{
+    HACMDRIVER                 had = 0;
+    int                                ntag;
+    ACMDRIVERDETAILSW          add;
+    ACMFORMATTAGDETAILSW        aftd;
+
+    if (acmDriverOpen(&had, (HACMDRIVERID)padid, 0) != 0)
+       return FALSE;
+
+    padid->aFormatTag = NULL;
+    add.cbStruct = sizeof(add);
+    if (MSACM_Message(had, ACMDM_DRIVER_DETAILS, (LPARAM)&add,  0))
+       goto errCleanUp;
+
+    if (add.cFormatTags > 0) {
+       padid->aFormatTag = HeapAlloc(MSACM_hHeap, HEAP_ZERO_MEMORY,
+                                     add.cFormatTags * sizeof(padid->aFormatTag[0]));
+       if (!padid->aFormatTag) goto errCleanUp;
+    }
+
+    padid->cFormatTags = add.cFormatTags;
+    padid->cFilterTags = add.cFilterTags;
+    padid->fdwSupport  = add.fdwSupport;
+
+    aftd.cbStruct = sizeof(aftd);
+
+    for (ntag = 0; ntag < add.cFormatTags; ntag++) {
+       aftd.dwFormatTagIndex = ntag;
+       if (MSACM_Message(had, ACMDM_FORMATTAG_DETAILS, (LPARAM)&aftd, ACM_FORMATTAGDETAILSF_INDEX)) {
+           TRACE("IIOs (%s)\n", debugstr_w(padid->pszDriverAlias));
+           goto errCleanUp;
+       }
+       padid->aFormatTag[ntag].dwFormatTag = aftd.dwFormatTag;
+       padid->aFormatTag[ntag].cbwfx = aftd.cbFormatSize;
+    }
+
+    acmDriverClose(had, 0);
+
+    return TRUE;
+
+errCleanUp:
+    if (had) acmDriverClose(had, 0);
+    HeapFree(MSACM_hHeap, 0, padid->aFormatTag);
+    padid->aFormatTag = NULL;
+    return FALSE;
+}
+
+/***********************************************************************
+ *           MSACM_GetRegistryKey
+ */
+static LPWSTR  MSACM_GetRegistryKey(const WINE_ACMDRIVERID* padid)
+{
+    static const WCHAR baseKey[] = {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
+                                     'A','u','d','i','o','C','o','m','p','r','e','s','s','i','o','n','M','a','n','a','g','e','r','\\',
+                                     'D','r','i','v','e','r','C','a','c','h','e','\\','\0'};
+    LPWSTR     ret;
+    int                len;
+
+    if (!padid->pszDriverAlias) {
+       ERR("No alias needed for registry entry\n");
+       return NULL;
+    }
+    len = strlenW(baseKey);
+    ret = HeapAlloc(MSACM_hHeap, 0, (len + strlenW(padid->pszDriverAlias) + 1) * sizeof(WCHAR));
+    if (!ret) return NULL;
+
+    strcpyW(ret, baseKey);
+    strcpyW(ret + len, padid->pszDriverAlias);
+    CharLowerW(ret + len);
+    return ret;
+}
+
+/***********************************************************************
+ *           MSACM_ReadCache
+ */
+static BOOL MSACM_ReadCache(PWINE_ACMDRIVERID padid)
+{
+    LPWSTR     key = MSACM_GetRegistryKey(padid);
+    HKEY       hKey;
+    DWORD      type, size;
+
+    if (!key) return FALSE;
+
+    padid->aFormatTag = NULL;
+
+    if (RegCreateKeyW(HKEY_LOCAL_MACHINE, key, &hKey))
+       goto errCleanUp;
+
+    size = sizeof(padid->cFormatTags);
+    if (RegQueryValueExA(hKey, "cFormatTags", 0, &type, (void*)&padid->cFormatTags, &size))
+       goto errCleanUp;
+    size = sizeof(padid->cFilterTags);
+    if (RegQueryValueExA(hKey, "cFilterTags", 0, &type, (void*)&padid->cFilterTags, &size))
+       goto errCleanUp;
+    size = sizeof(padid->fdwSupport);
+    if (RegQueryValueExA(hKey, "fdwSupport", 0, &type, (void*)&padid->fdwSupport, &size))
+       goto errCleanUp;
+
+    if (padid->cFormatTags > 0) {
+       size = padid->cFormatTags * sizeof(padid->aFormatTag[0]);
+       padid->aFormatTag = HeapAlloc(MSACM_hHeap, HEAP_ZERO_MEMORY, size);
+       if (!padid->aFormatTag) goto errCleanUp;
+       if (RegQueryValueExA(hKey, "aFormatTagCache", 0, &type, (void*)padid->aFormatTag, &size))
+           goto errCleanUp;
+    }
+    HeapFree(MSACM_hHeap, 0, key);
+    return TRUE;
+
+ errCleanUp:
+    HeapFree(MSACM_hHeap, 0, key);
+    HeapFree(MSACM_hHeap, 0, padid->aFormatTag);
+    padid->aFormatTag = NULL;
+    RegCloseKey(hKey);
+    return FALSE;
+}
+
+/***********************************************************************
+ *           MSACM_WriteCache
+ */
+static BOOL MSACM_WriteCache(PWINE_ACMDRIVERID padid)
+{
+    LPWSTR     key = MSACM_GetRegistryKey(padid);
+    HKEY       hKey;
+
+    if (!key) return FALSE;
+
+    if (RegCreateKeyW(HKEY_LOCAL_MACHINE, key, &hKey))
+       goto errCleanUp;
+
+    if (RegSetValueExA(hKey, "cFormatTags", 0, REG_DWORD, (void*)&padid->cFormatTags, sizeof(DWORD)))
+       goto errCleanUp;
+    if (RegSetValueExA(hKey, "cFilterTags", 0, REG_DWORD, (void*)&padid->cFilterTags, sizeof(DWORD)))
+       goto errCleanUp;
+    if (RegSetValueExA(hKey, "fdwSupport", 0, REG_DWORD, (void*)&padid->fdwSupport, sizeof(DWORD)))
+       goto errCleanUp;
+    if (RegSetValueExA(hKey, "aFormatTagCache", 0, REG_BINARY,
+                      (void*)padid->aFormatTag,
+                      padid->cFormatTags * sizeof(padid->aFormatTag[0])))
+       goto errCleanUp;
+    HeapFree(MSACM_hHeap, 0, key);
+    return TRUE;
+
+ errCleanUp:
+    HeapFree(MSACM_hHeap, 0, key);
+    return FALSE;
+}
+
+/***********************************************************************
+ *           MSACM_RegisterDriver()
+ */
+PWINE_ACMDRIVERID MSACM_RegisterDriver(LPCWSTR pszDriverAlias, LPCWSTR pszFileName,
+                                      HINSTANCE hinstModule)
+{
+    PWINE_ACMDRIVERID  padid;
+
+    TRACE("(%s, %s, %p)\n", 
+          debugstr_w(pszDriverAlias), debugstr_w(pszFileName), hinstModule);
+
+    padid = (PWINE_ACMDRIVERID) HeapAlloc(MSACM_hHeap, 0, sizeof(WINE_ACMDRIVERID));
+    padid->obj.dwType = WINE_ACMOBJ_DRIVERID;
+    padid->obj.pACMDriverID = padid;
+    padid->pszDriverAlias = NULL;
+    if (pszDriverAlias)
+    {
+        padid->pszDriverAlias = HeapAlloc( MSACM_hHeap, 0, (strlenW(pszDriverAlias)+1) * sizeof(WCHAR) );
+        strcpyW( padid->pszDriverAlias, pszDriverAlias );
+    }
+    padid->pszFileName = NULL;
+    if (pszFileName)
+    {
+        padid->pszFileName = HeapAlloc( MSACM_hHeap, 0, (strlenW(pszFileName)+1) * sizeof(WCHAR) );
+        strcpyW( padid->pszFileName, pszFileName );
+    }
+    padid->hInstModule = hinstModule;
+
+    padid->pACMDriverList = NULL;
+    padid->pNextACMDriverID = NULL;
+    padid->pPrevACMDriverID = MSACM_pLastACMDriverID;
+    if (MSACM_pLastACMDriverID)
+       MSACM_pLastACMDriverID->pNextACMDriverID = padid;
+    MSACM_pLastACMDriverID = padid;
+    if (!MSACM_pFirstACMDriverID)
+       MSACM_pFirstACMDriverID = padid;
+    /* disable the driver if we cannot load the cache */
+    if (!MSACM_ReadCache(padid) && !MSACM_FillCache(padid)) {
+       WARN("Couldn't load cache for ACM driver (%s)\n", debugstr_w(pszFileName));
+       MSACM_UnregisterDriver(padid);
+       return NULL;
+    }
+    return padid;
+}
+
+/***********************************************************************
+ *           MSACM_RegisterAllDrivers()
+ */
+void MSACM_RegisterAllDrivers(void)
+{
+    static const WCHAR msacm32[] = {'m','s','a','c','m','3','2','.','d','l','l','\0'};
+    static const WCHAR msacmW[] = {'M','S','A','C','M','.'};
+    static const WCHAR drv32[] = {'d','r','i','v','e','r','s','3','2','\0'};
+    static const WCHAR sys[] = {'s','y','s','t','e','m','.','i','n','i','\0'};
+    static const WCHAR drvkey[] = {'S','o','f','t','w','a','r','e','\\',
+                                  'M','i','c','r','o','s','o','f','t','\\',
+                                  'W','i','n','d','o','w','s',' ','N','T','\\',
+                                  'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
+                                  'D','r','i','v','e','r','s','3','2','\0'};
+    DWORD i, cnt = 0, bufLen, lRet;
+    WCHAR buf[2048], *name, *s;
+    FILETIME lastWrite;
+    HKEY hKey;
+
+    /* FIXME: What if the user edits system.ini while the program is running?
+     * Does Windows handle that?  */
+    if (MSACM_pFirstACMDriverID) return;
+
+    lRet = RegOpenKeyExW(HKEY_LOCAL_MACHINE, drvkey, 0, KEY_QUERY_VALUE, &hKey);
+    if (lRet == ERROR_SUCCESS) {
+       RegQueryInfoKeyW( hKey, 0, 0, 0, &cnt, 0, 0, 0, 0, 0, 0, 0);
+       for (i = 0; i < cnt; i++) {
+           bufLen = sizeof(buf) / sizeof(buf[0]);
+           lRet = RegEnumKeyExW(hKey, i, buf, &bufLen, 0, 0, 0, &lastWrite);
+           if (lRet != ERROR_SUCCESS) continue;
+           if (strncmpiW(buf, msacmW, sizeof(msacmW)/sizeof(msacmW[0]))) continue;
+           if (!(name = strchrW(buf, '='))) continue;
+           *name = 0;
+           MSACM_RegisterDriver(buf, name + 1, 0);
+       }
+       RegCloseKey( hKey );
+    }
+
+    if (GetPrivateProfileSectionW(drv32, buf, sizeof(buf)/sizeof(buf[0]), sys))
+    {
+       for(s = buf; *s;  s += strlenW(s) + 1)
+       {
+           if (strncmpiW(s, msacmW, sizeof(msacmW)/sizeof(msacmW[0]))) continue;
+           if (!(name = strchrW(s, '='))) continue;
+           *name = 0;
+           MSACM_RegisterDriver(s, name + 1, 0);
+           *name = '=';
+       }
+    }
+
+    MSACM_RegisterDriver(msacm32, msacm32, 0);
+}
+
+/***********************************************************************
+ *           MSACM_UnregisterDriver()
+ */
+PWINE_ACMDRIVERID MSACM_UnregisterDriver(PWINE_ACMDRIVERID p)
+{
+    PWINE_ACMDRIVERID pNextACMDriverID;
+
+    while (p->pACMDriverList)
+       acmDriverClose((HACMDRIVER) p->pACMDriverList, 0);
+
+    if (p->pszDriverAlias)
+       HeapFree(MSACM_hHeap, 0, p->pszDriverAlias);
+    if (p->pszFileName)
+       HeapFree(MSACM_hHeap, 0, p->pszFileName);
+    HeapFree(MSACM_hHeap, 0, p->aFormatTag);
+
+    if (p == MSACM_pFirstACMDriverID)
+       MSACM_pFirstACMDriverID = p->pNextACMDriverID;
+    if (p == MSACM_pLastACMDriverID)
+       MSACM_pLastACMDriverID = p->pPrevACMDriverID;
+
+    if (p->pPrevACMDriverID)
+       p->pPrevACMDriverID->pNextACMDriverID = p->pNextACMDriverID;
+    if (p->pNextACMDriverID)
+       p->pNextACMDriverID->pPrevACMDriverID = p->pPrevACMDriverID;
+
+    pNextACMDriverID = p->pNextACMDriverID;
+
+    HeapFree(MSACM_hHeap, 0, p);
+
+    return pNextACMDriverID;
+}
+
+/***********************************************************************
+ *           MSACM_UnregisterAllDrivers()
+ */
+void MSACM_UnregisterAllDrivers(void)
+{
+    PWINE_ACMDRIVERID p = MSACM_pFirstACMDriverID;
+
+    while (p) {
+       MSACM_WriteCache(p);
+       p = MSACM_UnregisterDriver(p);
+    }
+}
+
+/***********************************************************************
+ *           MSACM_GetObj()
+ */
+PWINE_ACMOBJ MSACM_GetObj(HACMOBJ hObj, DWORD type)
+{
+    PWINE_ACMOBJ       pao = (PWINE_ACMOBJ)hObj;
+
+    if (pao == NULL || IsBadReadPtr(pao, sizeof(WINE_ACMOBJ)) ||
+       ((type != WINE_ACMOBJ_DONTCARE) && (type != pao->dwType)))
+       return NULL;
+    return pao;
+}
+
+/***********************************************************************
+ *           MSACM_GetDriverID()
+ */
+PWINE_ACMDRIVERID MSACM_GetDriverID(HACMDRIVERID hDriverID)
+{
+    return (PWINE_ACMDRIVERID)MSACM_GetObj((HACMOBJ)hDriverID, WINE_ACMOBJ_DRIVERID);
+}
+
+/***********************************************************************
+ *           MSACM_GetDriver()
+ */
+PWINE_ACMDRIVER MSACM_GetDriver(HACMDRIVER hDriver)
+{
+    return (PWINE_ACMDRIVER)MSACM_GetObj((HACMOBJ)hDriver, WINE_ACMOBJ_DRIVER);
+}
+
+/***********************************************************************
+ *           MSACM_Message()
+ */
+MMRESULT MSACM_Message(HACMDRIVER had, UINT uMsg, LPARAM lParam1, LPARAM lParam2)
+{
+    PWINE_ACMDRIVER    pad = MSACM_GetDriver(had);
+
+    return pad ? SendDriverMessage(pad->hDrvr, uMsg, lParam1, lParam2) : MMSYSERR_INVALHANDLE;
+}
diff --git a/reactos/dll/msacm/msacm.rc b/reactos/dll/msacm/msacm.rc
new file mode 100644 (file)
index 0000000..6071001
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Top level resource file for MS ACM
+ *
+ * Copyright 2000 Eric Pouech
+ *
+ * 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 "windef.h"
+#include "winbase.h"
+#include "winuser.h"
+#include "wineacm.h"
+
+#include "msacm_En.rc"
+#include "msacm_De.rc"
+#include "msacm_Es.rc"
+#include "msacm_Fr.rc"
+#include "msacm_It.rc"
+#include "msacm_Ja.rc"
+#include "msacm_Nl.rc"
+#include "msacm_Pt.rc"
+#include "msacm_Ru.rc"
+#include "msacm_Sv.rc"
+#include "msacm_Hu.rc"
diff --git a/reactos/dll/msacm/msacm.spec b/reactos/dll/msacm/msacm.spec
new file mode 100644 (file)
index 0000000..9f03617
--- /dev/null
@@ -0,0 +1,40 @@
+  1 stub     WEP
+  2 stub     DRIVERPROC
+  3 stub     ___EXPORTEDSTUB
+  7 pascal   acmGetVersion() acmGetVersion16
+  8 pascal -ret16 acmMetrics(word word ptr) acmMetrics16
+ 10 pascal -ret16 acmDriverEnum(ptr long long) acmDriverEnum16
+ 11 pascal -ret16 acmDriverDetails(word ptr long) acmDriverDetails16
+ 12 pascal -ret16 acmDriverAdd(ptr word long long long) acmDriverAdd16
+ 13 pascal -ret16 acmDriverRemove(word long) acmDriverRemove16
+ 14 pascal -ret16 acmDriverOpen(ptr word long) acmDriverOpen16
+ 15 pascal -ret16 acmDriverClose(word long) acmDriverClose16
+ 16 pascal   acmDriverMessage(word word long long) acmDriverMessage16
+ 17 pascal -ret16 acmDriverID(word ptr long) acmDriverID16
+ 18 pascal -ret16 acmDriverPriority(word long long) acmDriverPriority16
+ 30 pascal -ret16 acmFormatTagDetails(word ptr long) acmFormatTagDetails16
+ 31 pascal -ret16 acmFormatTagEnum(word ptr ptr long long) acmFormatTagEnum16
+ 40 pascal -ret16 acmFormatChoose(ptr) acmFormatChoose16
+ 41 pascal -ret16 acmFormatDetails(word ptr long) acmFormatDetails16
+ 42 pascal -ret16 acmFormatEnum(word ptr ptr long long) acmFormatEnum16
+ 45 pascal -ret16 acmFormatSuggest(word ptr ptr long long) acmFormatSuggest16
+ 50 pascal -ret16 acmFilterTagDetails(word ptr long) acmFilterTagDetails16
+ 51 pascal -ret16 acmFilterTagEnum(word ptr ptr long long) acmFilterTagEnum16
+ 60 pascal -ret16 acmFilterChoose(ptr) acmFilterChoose16
+ 61 pascal -ret16 acmFilterDetails(word ptr long) acmFilterDetails16
+ 62 pascal -ret16 acmFilterEnum(word ptr ptr long long) acmFilterEnum16
+ 70 pascal -ret16 acmStreamOpen(ptr word ptr ptr ptr long long long) acmStreamOpen16
+ 71 pascal -ret16 acmStreamClose(word long) acmStreamClose16
+ 72 pascal -ret16 acmStreamSize(word long ptr long) acmStreamSize16
+ 75 pascal -ret16 acmStreamConvert(word ptr long) acmStreamConvert16
+ 76 pascal -ret16 acmStreamReset(word long) acmStreamReset16
+ 77 pascal -ret16 acmStreamPrepareHeader(word ptr long) acmStreamPrepareHeader16
+ 78 pascal -ret16 acmStreamUnprepareHeader(word ptr long) acmStreamUnprepareHeader16
+150 stub     ACMAPPLICATIONEXIT
+175 stub     ACMHUGEPAGELOCK
+176 stub     ACMHUGEPAGEUNLOCK
+200 stub     ACMOPENCONVERSION
+201 stub     ACMCLOSECONVERSION
+202 stub     ACMCONVERT
+203 stub     ACMCHOOSEFORMAT
+255 pascal   DllEntryPoint(long word word word long word) MSACM_DllEntryPoint
diff --git a/reactos/dll/msacm/msacm.spec.def b/reactos/dll/msacm/msacm.spec.def
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/reactos/dll/msacm/msacm32.spec b/reactos/dll/msacm/msacm32.spec
new file mode 100644 (file)
index 0000000..9500b53
--- /dev/null
@@ -0,0 +1,46 @@
+@ stdcall acmDriverAddA(ptr long long long long)
+@ stdcall acmDriverAddW(ptr long long long long)
+@ stdcall acmDriverClose(long long)
+@ stdcall acmDriverDetailsA(long ptr long)
+@ stdcall acmDriverDetailsW(long ptr long)
+@ stdcall acmDriverEnum(ptr long long)
+@ stdcall acmDriverID(long ptr long)
+@ stdcall acmDriverMessage(long long long long)
+@ stdcall acmDriverOpen(ptr long long)
+@ stdcall acmDriverPriority(long long long)
+@ stdcall acmDriverRemove(long long)
+@ stdcall acmFilterChooseA(ptr)
+@ stdcall acmFilterChooseW(ptr)
+@ stdcall acmFilterDetailsA(long ptr long)
+@ stdcall acmFilterDetailsW(long ptr long)
+@ stdcall acmFilterEnumA(long ptr ptr long long)
+@ stdcall acmFilterEnumW(long ptr ptr long long)
+@ stdcall acmFilterTagDetailsA(long ptr long)
+@ stdcall acmFilterTagDetailsW(long ptr long)
+@ stdcall acmFilterTagEnumA(long ptr ptr long long)
+@ stdcall acmFilterTagEnumW(long ptr ptr long long)
+@ stdcall acmFormatChooseA(ptr)
+@ stdcall acmFormatChooseW(ptr)
+@ stdcall acmFormatDetailsA(long ptr long)
+@ stdcall acmFormatDetailsW(long ptr long)
+@ stdcall acmFormatEnumA(long ptr ptr long long)
+@ stdcall acmFormatEnumW(long ptr ptr long long)
+@ stdcall acmFormatSuggest(long ptr ptr long long)
+@ stdcall acmFormatTagDetailsA(long ptr long)
+@ stdcall acmFormatTagDetailsW(long ptr long)
+@ stdcall acmFormatTagEnumA(long ptr ptr long long)
+@ stdcall acmFormatTagEnumW(long ptr ptr long long)
+@ stdcall acmGetVersion()
+@ stub acmMessage32
+@ stdcall acmMetrics(long long ptr)
+@ stdcall acmStreamClose(long long)
+@ stdcall acmStreamConvert(long ptr long)
+@ stdcall acmStreamMessage(long long long long)
+@ stdcall acmStreamOpen(ptr long ptr ptr ptr long long long)
+@ stdcall acmStreamPrepareHeader(long ptr long)
+@ stdcall acmStreamReset(long long)
+@ stdcall acmStreamSize(long long ptr long)
+@ stdcall acmStreamUnprepareHeader(long ptr long)
+
+# this is wine only
+@ stdcall DriverProc(long long long long long) PCM_DriverProc
diff --git a/reactos/dll/msacm/msacm32.xml b/reactos/dll/msacm/msacm32.xml
new file mode 100644 (file)
index 0000000..c9f10c6
--- /dev/null
@@ -0,0 +1,26 @@
+<module name="msacm32" type="win32dll" baseaddress="${BASEADDRESS_MSACM32}" installbase="system32" installname="msacm32.dll">
+       <importlibrary definition="msacm32.spec.def" />
+       <include base="msacm32">.</include>
+       <include base="ReactOS">include/wine</include>
+       <define name="UNICODE" />
+       <define name="_UNICODE" />
+       <define name="__REACTOS__" />
+       <define name="__USE_W32API" />
+       <define name="_WIN32_IE">0x600</define>
+       <define name="_WIN32_WINNT">0x501</define>
+       <define name="WINVER">0x501</define>
+       <library>wine</library>
+       <library>ntdll</library>
+       <library>kernel32</library>
+       <library>advapi32</library>
+       <library>user32</library>
+       <library>winmm</library>
+       <file>driver.c</file>
+       <file>filter.c</file>
+       <file>format.c</file>
+       <file>internal.c</file>
+       <file>msacm32_main.c</file>
+       <file>pcmconverter.c</file>
+       <file>stream.c</file>
+       <file>msacm32.spec</file>
+</module>
diff --git a/reactos/dll/msacm/msacm32_main.c b/reactos/dll/msacm/msacm32_main.c
new file mode 100644 (file)
index 0000000..dde962a
--- /dev/null
@@ -0,0 +1,233 @@
+/* -*- tab-width: 8; c-basic-offset: 4 -*- */
+
+/*
+ *      MSACM32 library
+ *
+ *      Copyright 1998  Patrik Stridvall
+ *               1999  Eric Pouech
+ *
+ * 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 <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "wine/debug.h"
+#include "mmsystem.h"
+#include "mmreg.h"
+#include "msacm.h"
+#include "msacmdrv.h"
+#include "wineacm.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msacm);
+
+/**********************************************************************/
+
+HINSTANCE      MSACM_hInstance32 = 0;
+
+/***********************************************************************
+ *           DllMain (MSACM32.init)
+ */
+BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+    TRACE("%p 0x%lx %p\n", hInstDLL, fdwReason, lpvReserved);
+
+    switch (fdwReason) {
+    case DLL_PROCESS_ATTACH:
+        DisableThreadLibraryCalls(hInstDLL);
+        MSACM_hHeap = HeapCreate(0, 0x10000, 0);
+        MSACM_hInstance32 = hInstDLL;
+        MSACM_RegisterAllDrivers();
+       break;
+    case DLL_PROCESS_DETACH:
+        MSACM_UnregisterAllDrivers();
+        HeapDestroy(MSACM_hHeap);
+        MSACM_hHeap = NULL;
+        MSACM_hInstance32 = NULL;
+       break;
+    default:
+       break;
+    }
+    return TRUE;
+}
+
+/***********************************************************************
+ *           XRegThunkEntry (MSACM32.1)
+ * FIXME
+ *   No documentation found.
+ */
+
+/***********************************************************************
+ *           acmGetVersion (MSACM32.@)
+ */
+DWORD WINAPI acmGetVersion(void)
+{
+    OSVERSIONINFOA version;
+
+    version.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
+    if (!GetVersionExA( &version ))
+       return 0x04030000;
+
+    switch (version.dwPlatformId) {
+    case VER_PLATFORM_WIN32_NT:
+       return 0x04000565; /* 4.0.1381 */
+    default:
+        FIXME("%lx not supported\n", version.dwPlatformId);
+    case VER_PLATFORM_WIN32_WINDOWS:
+       return 0x04030000; /* 4.3.0 */
+    }
+}
+
+/***********************************************************************
+ *           acmMessage32 (MSACM32.35)
+ * FIXME
+ *   No documentation found.
+ */
+
+/***********************************************************************
+ *           acmMetrics (MSACM32.@)
+ */
+MMRESULT WINAPI acmMetrics(HACMOBJ hao, UINT uMetric, LPVOID pMetric)
+{
+    PWINE_ACMOBJ       pao = MSACM_GetObj(hao, WINE_ACMOBJ_DONTCARE);
+    BOOL               bLocal = TRUE;
+    PWINE_ACMDRIVERID  padid;
+    DWORD              val = 0;
+    int                        i;
+    MMRESULT           mmr = MMSYSERR_NOERROR;
+
+    TRACE("(%p, %d, %p);\n", hao, uMetric, pMetric);
+
+#define CheckLocal(padid) (!bLocal || ((padid)->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_LOCAL))
+
+    switch (uMetric) {
+    case ACM_METRIC_COUNT_DRIVERS:
+       bLocal = FALSE;
+       /* fall through */
+    case ACM_METRIC_COUNT_LOCAL_DRIVERS:
+       if (hao) return MMSYSERR_INVALHANDLE;
+        if (!pMetric) return MMSYSERR_INVALPARAM;
+       for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID)
+           if (!(padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) && CheckLocal(padid))
+               val++;
+       *(LPDWORD)pMetric = val;
+       break;
+
+    case ACM_METRIC_COUNT_CODECS:
+       bLocal = FALSE;
+       /* fall through */
+    case ACM_METRIC_COUNT_LOCAL_CODECS:
+       if (hao) return MMSYSERR_INVALHANDLE;
+        if (!pMetric) return MMSYSERR_INVALPARAM;
+       for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID)
+           if (!(padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) &&
+               (padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_CODEC) &&
+               CheckLocal(padid))
+               val++;
+       *(LPDWORD)pMetric = val;
+       break;
+
+    case ACM_METRIC_COUNT_CONVERTERS:
+       bLocal = FALSE;
+       /* fall through */
+    case ACM_METRIC_COUNT_LOCAL_CONVERTERS:
+       if (hao) return MMSYSERR_INVALHANDLE;
+        if (!pMetric) return MMSYSERR_INVALPARAM;
+       for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID)
+           if (!(padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) &&
+                (padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_CONVERTER) &&
+               CheckLocal(padid))
+               val++;
+       *(LPDWORD)pMetric = val;
+       break;
+
+    case ACM_METRIC_COUNT_FILTERS:
+       bLocal = FALSE;
+       /* fall through */
+    case ACM_METRIC_COUNT_LOCAL_FILTERS:
+       if (hao) return MMSYSERR_INVALHANDLE;
+        if (!pMetric) return MMSYSERR_INVALPARAM;
+       for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID)
+           if (!(padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) &&
+               (padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_FILTER) &&
+               CheckLocal(padid))
+               val++;
+       *(LPDWORD)pMetric = val;
+       break;
+
+    case ACM_METRIC_COUNT_DISABLED:
+       bLocal = FALSE;
+       /* fall through */
+    case ACM_METRIC_COUNT_LOCAL_DISABLED:
+       if (hao) return MMSYSERR_INVALHANDLE;
+        if (!pMetric) return MMSYSERR_INVALPARAM;
+       for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID)
+           if ((padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) && CheckLocal(padid))
+               val++;
+       *(LPDWORD)pMetric = val;
+       break;
+
+    case ACM_METRIC_MAX_SIZE_FORMAT:
+       if (hao == NULL) {
+           for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) {
+               if (!(padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED)) {
+                   for (i = 0; i < padid->cFormatTags; i++) {
+                       if (val < padid->aFormatTag[i].cbwfx)
+                           val = padid->aFormatTag[i].cbwfx;
+                   }
+               }
+           }
+       } else if (pao != NULL) {
+           switch (pao->dwType) {
+           case WINE_ACMOBJ_DRIVER:
+           case WINE_ACMOBJ_DRIVERID:
+               padid = pao->pACMDriverID;
+               break;
+           default:
+               return MMSYSERR_INVALHANDLE;
+           }
+           if (!(padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED)) {
+               for (i = 0; i < padid->cFormatTags; i++) {
+                   if (val < padid->aFormatTag[i].cbwfx)
+                       val = padid->aFormatTag[i].cbwfx;
+               }
+           }
+       } else {
+           return MMSYSERR_INVALHANDLE;
+       }
+        if (!pMetric) return MMSYSERR_INVALPARAM;
+       *(LPDWORD)pMetric = val;
+        break;
+
+    case ACM_METRIC_COUNT_HARDWARE:
+        if (hao) return MMSYSERR_INVALHANDLE;
+        if (!pMetric) return MMSYSERR_INVALPARAM;
+        *(LPDWORD)pMetric = 0;
+        FIXME("ACM_METRIC_COUNT_HARDWARE not implemented\n");
+        break;
+
+    case ACM_METRIC_HARDWARE_WAVE_INPUT:
+    case ACM_METRIC_HARDWARE_WAVE_OUTPUT:
+    case ACM_METRIC_MAX_SIZE_FILTER:
+    case ACM_METRIC_DRIVER_SUPPORT:
+    case ACM_METRIC_DRIVER_PRIORITY:
+    default:
+       FIXME("(%p, %d, %p): stub\n", hao, uMetric, pMetric);
+       mmr = MMSYSERR_NOTSUPPORTED;
+    }
+    return mmr;
+}
diff --git a/reactos/dll/msacm/msacm_De.rc b/reactos/dll/msacm/msacm_De.rc
new file mode 100644 (file)
index 0000000..555885b
--- /dev/null
@@ -0,0 +1,56 @@
+/*\r
+ * German resource file for MS ACM\r
+ *\r
+ * by Friedrich Stange (dj_smith_reactos@online.de)\r
+ *\r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ *\r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+ * Lesser General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+ */\r
+\r
+LANGUAGE LANG_GERMAN, SUBLANG_NEUTRAL\r
+\r
+DLG_ACMFORMATCHOOSE_ID DIALOG DISCARDABLE  10, 20, 225, 100\r
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU\r
+CAPTION "Sound auswahl"\r
+FONT 8, "MS Shell Dlg"\r
+BEGIN\r
+\r
+    LTEXT           "&Name:", -1, 5, 5, 115, 8, NOT WS_GROUP\r
+\r
+    COMBOBOX        IDD_ACMFORMATCHOOSE_CMB_CUSTOM, 5, 15, 115, 60,\r
+                    CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP\r
+\r
+    PUSHBUTTON      "&Speichern als...", IDD_ACMFORMATCHOOSE_BTN_SETNAME, 125, 14, 45, 14\r
+    PUSHBUTTON      "&Löschen", IDD_ACMFORMATCHOOSE_BTN_DELNAME, 175, 14, 45, 14\r
+\r
+    LTEXT           "&Format:", -1, 5, 41, 44, 8, NOT WS_GROUP\r
+\r
+    COMBOBOX        IDD_ACMFORMATCHOOSE_CMB_FORMATTAG, 50, 39, 170, 60,\r
+                    CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP\r
+\r
+    LTEXT           "&Eigenschaften:", -1, 5, 59, 44, 8, NOT WS_GROUP\r
+\r
+#if 0\r
+    COMBOBOX        IDD_ACMFORMATCHOOSE_CMB_FORMAT, 50, 57, 170, 60,\r
+                    CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP |\r
+                    CBS_OWNERDRAWFIXED | CBS_HASSTRINGS\r
+#else\r
+    COMBOBOX        IDD_ACMFORMATCHOOSE_CMB_FORMAT, 50, 57, 170, 60,\r
+                    CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP\r
+#endif\r
+    DEFPUSHBUTTON   "OK", IDOK, 48, 80, 40, 14\r
+    PUSHBUTTON      "Abbrechen", IDCANCEL, 92, 80, 40, 14\r
+    PUSHBUTTON      "&Hilfe", IDD_ACMFORMATCHOOSE_BTN_HELP, 136, 80, 40, 14\r
+\r
+END\r
diff --git a/reactos/dll/msacm/msacm_En.rc b/reactos/dll/msacm/msacm_En.rc
new file mode 100644 (file)
index 0000000..acbd0c4
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * English resource file for MS ACM
+ *
+ * Copyright 2000 Eric Pouech
+ *
+ * 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
+ */
+
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+DLG_ACMFORMATCHOOSE_ID DIALOG DISCARDABLE  10, 20, 225, 100
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Sound Selection"
+FONT 8, "MS Shell Dlg"
+BEGIN
+
+    LTEXT           "&Name:", -1, 5, 5, 115, 8, NOT WS_GROUP
+
+    COMBOBOX        IDD_ACMFORMATCHOOSE_CMB_CUSTOM, 5, 15, 115, 60,
+                    CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+
+    PUSHBUTTON      "&Save As...", IDD_ACMFORMATCHOOSE_BTN_SETNAME, 125, 14, 45, 14
+    PUSHBUTTON      "&Remove", IDD_ACMFORMATCHOOSE_BTN_DELNAME, 175, 14, 45, 14
+
+    LTEXT           "&Format:", -1, 5, 41, 44, 8, NOT WS_GROUP
+
+    COMBOBOX        IDD_ACMFORMATCHOOSE_CMB_FORMATTAG, 50, 39, 170, 60,
+                    CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+
+    LTEXT           "&Attributes:", -1, 5, 59, 44, 8, NOT WS_GROUP
+
+#if 0
+    COMBOBOX        IDD_ACMFORMATCHOOSE_CMB_FORMAT, 50, 57, 170, 60,
+                    CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP |
+                    CBS_OWNERDRAWFIXED | CBS_HASSTRINGS
+#else
+    COMBOBOX        IDD_ACMFORMATCHOOSE_CMB_FORMAT, 50, 57, 170, 60,
+                    CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+#endif
+    DEFPUSHBUTTON   "OK", IDOK, 48, 80, 40, 14
+    PUSHBUTTON      "Cancel", IDCANCEL, 92, 80, 40, 14
+    PUSHBUTTON      "&Help", IDD_ACMFORMATCHOOSE_BTN_HELP, 136, 80, 40, 14
+
+END
diff --git a/reactos/dll/msacm/msacm_Es.rc b/reactos/dll/msacm/msacm_Es.rc
new file mode 100644 (file)
index 0000000..3f26252
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Spanish resource file for MS ACM
+ *
+ * Copyright 2004 José Manuel Ferrer Ortiz
+ *
+ * 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
+ */
+
+LANGUAGE LANG_SPANISH, SUBLANG_NEUTRAL
+
+DLG_ACMFORMATCHOOSE_ID DIALOG DISCARDABLE  10, 20, 225, 100
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Selección de sonido"
+FONT 8, "MS Shell Dlg"
+BEGIN
+
+    LTEXT           "&Nombre:", -1, 5, 5, 115, 8, NOT WS_GROUP
+
+    COMBOBOX        IDD_ACMFORMATCHOOSE_CMB_CUSTOM, 5, 15, 115, 60,
+                    CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+
+    PUSHBUTTON      "&Guardar como...", IDD_ACMFORMATCHOOSE_BTN_SETNAME, 125, 14, 45, 14
+    PUSHBUTTON      "&Eliminar", IDD_ACMFORMATCHOOSE_BTN_DELNAME, 175, 14, 45, 14
+
+    LTEXT           "&Formato:", -1, 5, 41, 44, 8, NOT WS_GROUP
+
+    COMBOBOX        IDD_ACMFORMATCHOOSE_CMB_FORMATTAG, 50, 39, 170, 60,
+                    CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+
+    LTEXT           "A&tributos:", -1, 5, 59, 44, 8, NOT WS_GROUP
+
+#if 0
+    COMBOBOX        IDD_ACMFORMATCHOOSE_CMB_FORMAT, 50, 57, 170, 60,
+                    CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP |
+                    CBS_OWNERDRAWFIXED | CBS_HASSTRINGS
+#else
+    COMBOBOX        IDD_ACMFORMATCHOOSE_CMB_FORMAT, 50, 57, 170, 60,
+                    CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+#endif
+    DEFPUSHBUTTON   "Aceptar", IDOK, 48, 80, 40, 14
+    PUSHBUTTON      "Cancelar", IDCANCEL, 92, 80, 40, 14
+    PUSHBUTTON      "&Ayuda", IDD_ACMFORMATCHOOSE_BTN_HELP, 136, 80, 40, 14
+
+END
diff --git a/reactos/dll/msacm/msacm_Fr.rc b/reactos/dll/msacm/msacm_Fr.rc
new file mode 100644 (file)
index 0000000..f9efdba
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * French resource file for MS ACM
+ *
+ * Copyright 2000 Eric Pouech
+ * Copyright 2003 Vincent Béron
+ *
+ * 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
+ */
+
+LANGUAGE LANG_FRENCH, SUBLANG_NEUTRAL
+
+DLG_ACMFORMATCHOOSE_ID DIALOG DISCARDABLE  10, 20, 225, 100
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Sélection du son"
+FONT 8, "MS Shell Dlg"
+BEGIN
+
+    LTEXT           "&Nom :", -1, 5, 5, 115, 8, NOT WS_GROUP
+
+    COMBOBOX        IDD_ACMFORMATCHOOSE_CMB_CUSTOM, 5, 15, 115, 60,
+                    CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+
+    PUSHBUTTON      "&Enregistrer sous...", IDD_ACMFORMATCHOOSE_BTN_SETNAME, 125, 14, 45, 14
+    PUSHBUTTON      "&Retirer", IDD_ACMFORMATCHOOSE_BTN_DELNAME, 175, 14, 45, 14
+
+    LTEXT           "&Format :", -1, 5, 41, 44, 8, NOT WS_GROUP
+
+    COMBOBOX        IDD_ACMFORMATCHOOSE_CMB_FORMATTAG, 50, 39, 170, 60,
+                    CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+
+    LTEXT           "&Attributs :", -1, 5, 59, 44, 8, NOT WS_GROUP
+
+#if 0
+    COMBOBOX        IDD_ACMFORMATCHOOSE_CMB_FORMAT, 50, 57, 170, 60,
+                    CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP |
+                    CBS_OWNERDRAWFIXED | CBS_HASSTRINGS
+#else
+    COMBOBOX        IDD_ACMFORMATCHOOSE_CMB_FORMAT, 50, 57, 170, 60,
+                    CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+#endif
+    DEFPUSHBUTTON   "OK", IDOK, 48, 80, 40, 14
+    PUSHBUTTON      "Annuler", IDCANCEL, 92, 80, 40, 14
+    PUSHBUTTON      "&Aide", IDD_ACMFORMATCHOOSE_BTN_HELP, 136, 80, 40, 14
+
+END
diff --git a/reactos/dll/msacm/msacm_Hu.rc b/reactos/dll/msacm/msacm_Hu.rc
new file mode 100644 (file)
index 0000000..d0baa82
--- /dev/null
@@ -0,0 +1,57 @@
+/*\r
+ * Hungarian resource file for MS ACM\r
+ *\r
+ * Copyright 2000 Eric Pouech\r
+ * Copyright 2005 Gergely Risko\r
+ *\r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ *\r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+ * Lesser General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+ */\r
+\r
+LANGUAGE LANG_HUNGARIAN, SUBLANG_NEUTRAL\r
+\r
+DLG_ACMFORMATCHOOSE_ID DIALOG DISCARDABLE  10, 20, 225, 100\r
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU\r
+CAPTION "Hang Kiválasztása"\r
+FONT 8, "MS Shell Dlg"\r
+BEGIN\r
+\r
+    LTEXT           "&Név:", -1, 5, 5, 115, 8, NOT WS_GROUP\r
+\r
+    COMBOBOX        IDD_ACMFORMATCHOOSE_CMB_CUSTOM, 5, 15, 115, 60,\r
+                    CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP\r
+\r
+    PUSHBUTTON      "&Mentés másként...", IDD_ACMFORMATCHOOSE_BTN_SETNAME, 125, 14, 45, 14\r
+/* NOT TRANSLATED */    PUSHBUTTON      "&Remove", IDD_ACMFORMATCHOOSE_BTN_DELNAME, 175, 14, 45, 14\r
+\r
+    LTEXT           "&Formátum:", -1, 5, 41, 44, 8, NOT WS_GROUP\r
+\r
+    COMBOBOX        IDD_ACMFORMATCHOOSE_CMB_FORMATTAG, 50, 39, 170, 60,\r
+                    CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP\r
+\r
+    LTEXT           "&Attribútumok:", -1, 5, 59, 44, 8, NOT WS_GROUP\r
+\r
+#if 0\r
+    COMBOBOX        IDD_ACMFORMATCHOOSE_CMB_FORMAT, 50, 57, 170, 60,\r
+                    CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP |\r
+                    CBS_OWNERDRAWFIXED | CBS_HASSTRINGS\r
+#else\r
+    COMBOBOX        IDD_ACMFORMATCHOOSE_CMB_FORMAT, 50, 57, 170, 60,\r
+                    CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP\r
+#endif\r
+    DEFPUSHBUTTON   "OK", IDOK, 48, 80, 40, 14\r
+    PUSHBUTTON      "Mégse", IDCANCEL, 92, 80, 40, 14\r
+    PUSHBUTTON      "&Súgó", IDD_ACMFORMATCHOOSE_BTN_HELP, 136, 80, 40, 14\r
+\r
+END\r
diff --git a/reactos/dll/msacm/msacm_It.rc b/reactos/dll/msacm/msacm_It.rc
new file mode 100644 (file)
index 0000000..c01db70
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Italian resource file for MS ACM
+ *
+ * Copyright 2000 Eric Pouech
+ * Copyright 2003 Ivan Leo Puoti
+ *
+ * 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
+ */
+
+LANGUAGE LANG_ITALIAN, SUBLANG_NEUTRAL
+
+DLG_ACMFORMATCHOOSE_ID DIALOG DISCARDABLE  10, 20, 225, 100
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Selezione dell'audio"
+FONT 8, "MS Shell Dlg"
+BEGIN
+
+    LTEXT           "&Nome:", -1, 5, 5, 115, 8, NOT WS_GROUP
+
+    COMBOBOX        IDD_ACMFORMATCHOOSE_CMB_CUSTOM, 5, 15, 115, 60,
+                    CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+
+    PUSHBUTTON      "&Salva con nome...", IDD_ACMFORMATCHOOSE_BTN_SETNAME, 125, 14, 45, 14
+    PUSHBUTTON      "&Rimuovi", IDD_ACMFORMATCHOOSE_BTN_DELNAME, 175, 14, 45, 14
+
+    LTEXT           "&Formato:", -1, 5, 41, 44, 8, NOT WS_GROUP
+
+    COMBOBOX        IDD_ACMFORMATCHOOSE_CMB_FORMATTAG, 50, 39, 170, 60,
+                    CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+
+    LTEXT           "&Attributi:", -1, 5, 59, 44, 8, NOT WS_GROUP
+
+#if 0
+    COMBOBOX        IDD_ACMFORMATCHOOSE_CMB_FORMAT, 50, 57, 170, 60,
+                    CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP |
+                    CBS_OWNERDRAWFIXED | CBS_HASSTRINGS
+#else
+    COMBOBOX        IDD_ACMFORMATCHOOSE_CMB_FORMAT, 50, 57, 170, 60,
+                    CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+#endif
+    DEFPUSHBUTTON   "OK", IDOK, 48, 80, 40, 14
+    PUSHBUTTON      "Anulla", IDCANCEL, 92, 80, 40, 14
+    PUSHBUTTON      "&Aiuto", IDD_ACMFORMATCHOOSE_BTN_HELP, 136, 80, 40, 14
+
+END
diff --git a/reactos/dll/msacm/msacm_Ja.rc b/reactos/dll/msacm/msacm_Ja.rc
new file mode 100644 (file)
index 0000000..01e7ba3
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Japanese resource file for MS ACM
+ *
+ * Copyright 2000 Hajime Segawa
+ *
+ * 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
+ */
+
+LANGUAGE LANG_JAPANESE, SUBLANG_DEFAULT
+
+DLG_ACMFORMATCHOOSE_ID DIALOG DISCARDABLE  10, 20, 225, 100
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "\83T\83E\83\93\83h\82Ì\91I\91ð"
+FONT 9, "MS UI Gothic"
+BEGIN
+
+    LTEXT           "\96¼\91O(&N):", -1, 5, 5, 115, 8, NOT WS_GROUP
+
+    COMBOBOX        IDD_ACMFORMATCHOOSE_CMB_CUSTOM, 5, 15, 115, 60,
+                    CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+
+    PUSHBUTTON      "\96¼\91O\82ð\95t\82¯\82Ä\95Û\91¶(&S)...", IDD_ACMFORMATCHOOSE_BTN_SETNAME, 125, 14, 45, 14
+    PUSHBUTTON      "\8dí\8f\9c(&R)", IDD_ACMFORMATCHOOSE_BTN_DELNAME, 175, 14, 45, 14
+
+    LTEXT           "\83t\83H\81[\83}\83b\83g(&F):", -1, 5, 41, 44, 8, NOT WS_GROUP
+
+    COMBOBOX        IDD_ACMFORMATCHOOSE_CMB_FORMATTAG, 50, 39, 170, 60,
+                    CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+
+    LTEXT           "\91®\90«(&A):", -1, 5, 59, 44, 8, NOT WS_GROUP
+
+#if 0
+    COMBOBOX        IDD_ACMFORMATCHOOSE_CMB_FORMAT, 50, 57, 170, 60,
+                    CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP |
+                    CBS_OWNERDRAWFIXED | CBS_HASSTRINGS
+#else
+    COMBOBOX        IDD_ACMFORMATCHOOSE_CMB_FORMAT, 50, 57, 170, 60,
+                    CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+#endif
+    DEFPUSHBUTTON   "OK", IDOK, 48, 80, 40, 14
+    PUSHBUTTON      "\83L\83\83\83\93\83Z\83\8b", IDCANCEL, 92, 80, 40, 14
+    PUSHBUTTON      "\83w\83\8b\83v(&H)", IDD_ACMFORMATCHOOSE_BTN_HELP, 136, 80, 40, 14
+
+END
diff --git a/reactos/dll/msacm/msacm_Nl.rc b/reactos/dll/msacm/msacm_Nl.rc
new file mode 100644 (file)
index 0000000..1634323
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * msacm (Dutch resources)
+ *
+ * Copyright 2003 Hans Leidekker
+ *
+ * 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
+ */
+
+LANGUAGE LANG_DUTCH, SUBLANG_NEUTRAL
+
+DLG_ACMFORMATCHOOSE_ID DIALOG DISCARDABLE  10, 20, 225, 100
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Geluidskeuze"
+FONT 8, "MS Shell Dlg"
+BEGIN
+
+    LTEXT           "&Naam:", -1, 5, 5, 115, 8, NOT WS_GROUP
+
+    COMBOBOX        IDD_ACMFORMATCHOOSE_CMB_CUSTOM, 5, 15, 115, 60,
+                    CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+
+    PUSHBUTTON      "&Opslaan als...", IDD_ACMFORMATCHOOSE_BTN_SETNAME, 125, 14, 45, 14
+    PUSHBUTTON      "&Verwijderen", IDD_ACMFORMATCHOOSE_BTN_DELNAME, 175, 14, 45, 14
+
+    LTEXT           "&Formaat:", -1, 5, 41, 44, 8, NOT WS_GROUP
+
+    COMBOBOX        IDD_ACMFORMATCHOOSE_CMB_FORMATTAG, 50, 39, 170, 60,
+                    CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+
+    LTEXT           "&Attributen:", -1, 5, 59, 44, 8, NOT WS_GROUP
+
+#if 0
+    COMBOBOX        IDD_ACMFORMATCHOOSE_CMB_FORMAT, 50, 57, 170, 60,
+                    CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP |
+                    CBS_OWNERDRAWFIXED | CBS_HASSTRINGS
+#else
+    COMBOBOX        IDD_ACMFORMATCHOOSE_CMB_FORMAT, 50, 57, 170, 60,
+                    CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+#endif
+    DEFPUSHBUTTON   "OK", IDOK, 48, 80, 40, 14
+    PUSHBUTTON      "Annuleren", IDCANCEL, 92, 80, 40, 14
+    PUSHBUTTON      "&Help", IDD_ACMFORMATCHOOSE_BTN_HELP, 136, 80, 40, 14
+
+END
diff --git a/reactos/dll/msacm/msacm_Pt.rc b/reactos/dll/msacm/msacm_Pt.rc
new file mode 100644 (file)
index 0000000..062742b
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Portuguese resource file for MS ACM
+ *
+ * Copyright 2003 Marcelo Duarte
+ *
+ * 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
+ */
+
+LANGUAGE LANG_PORTUGUESE, SUBLANG_NEUTRAL
+
+DLG_ACMFORMATCHOOSE_ID DIALOG DISCARDABLE  10, 20, 225, 100
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Seleção de som"
+FONT 8, "MS Shell Dlg"
+BEGIN
+
+    LTEXT           "&Nome:", -1, 5, 5, 115, 8, NOT WS_GROUP
+
+    COMBOBOX        IDD_ACMFORMATCHOOSE_CMB_CUSTOM, 5, 15, 115, 60,
+                    CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+
+    PUSHBUTTON      "&Salvar como...", IDD_ACMFORMATCHOOSE_BTN_SETNAME, 125, 14, 45, 14
+    PUSHBUTTON      "&Remover", IDD_ACMFORMATCHOOSE_BTN_DELNAME, 175, 14, 45, 14
+
+    LTEXT           "&Formato:", -1, 5, 41, 44, 8, NOT WS_GROUP
+
+    COMBOBOX        IDD_ACMFORMATCHOOSE_CMB_FORMATTAG, 50, 39, 170, 60,
+                    CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+
+    LTEXT           "&Atributos:", -1, 5, 59, 44, 8, NOT WS_GROUP
+
+#if 0
+    COMBOBOX        IDD_ACMFORMATCHOOSE_CMB_FORMAT, 50, 57, 170, 60,
+                    CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP |
+                    CBS_OWNERDRAWFIXED | CBS_HASSTRINGS
+#else
+    COMBOBOX        IDD_ACMFORMATCHOOSE_CMB_FORMAT, 50, 57, 170, 60,
+                    CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+#endif
+    DEFPUSHBUTTON   "OK", IDOK, 48, 80, 40, 14
+    PUSHBUTTON      "Cancelar", IDCANCEL, 92, 80, 40, 14
+    PUSHBUTTON      "Aj&uda", IDD_ACMFORMATCHOOSE_BTN_HELP, 136, 80, 40, 14
+
+END
diff --git a/reactos/dll/msacm/msacm_Ru.rc b/reactos/dll/msacm/msacm_Ru.rc
new file mode 100644 (file)
index 0000000..d665e72
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Russian resource file for MS ACM
+ *
+ * Copyright 2005 Mikhail Y. Zvyozdochkin
+ *
+ * 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
+ */
+
+LANGUAGE LANG_RUSSIAN, SUBLANG_NEUTRAL
+
+DLG_ACMFORMATCHOOSE_ID DIALOG DISCARDABLE  10, 20, 234, 101
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Âûáîð çâóêà"
+FONT 8, "MS Shell Dlg"
+BEGIN
+
+    LTEXT           "&Èìÿ:", -1, 5, 5, 115, 8, NOT WS_GROUP
+
+    COMBOBOX        IDD_ACMFORMATCHOOSE_CMB_CUSTOM, 5, 15, 115, 60,
+                    CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+
+    PUSHBUTTON      "Ñîõð&àíèòü êàê...", IDD_ACMFORMATCHOOSE_BTN_SETNAME, 125, 14, 58, 14
+    PUSHBUTTON      "&Óäàëèòü", IDD_ACMFORMATCHOOSE_BTN_DELNAME, 187, 14, 38, 14
+
+    LTEXT           "&Ôîðìàò:", -1, 5, 41, 44, 8, NOT WS_GROUP
+
+    COMBOBOX        IDD_ACMFORMATCHOOSE_CMB_FORMATTAG, 50, 39, 177, 60,
+                    CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+
+    LTEXT           "&Ñâîéñòâà:", -1, 5, 59, 44, 8, NOT WS_GROUP
+
+#if 0
+    COMBOBOX        IDD_ACMFORMATCHOOSE_CMB_FORMAT, 50, 57, 177, 60,
+                    CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP |
+                    CBS_OWNERDRAWFIXED | CBS_HASSTRINGS
+#else
+    COMBOBOX        IDD_ACMFORMATCHOOSE_CMB_FORMAT, 50, 57, 177, 60,
+                    CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+#endif
+    DEFPUSHBUTTON   "OK", IDOK, 48, 80, 40, 14
+    PUSHBUTTON      "Îòìåíà", IDCANCEL, 92, 80, 40, 14
+    PUSHBUTTON      "Ñ&ïðàâêà", IDD_ACMFORMATCHOOSE_BTN_HELP, 136, 80, 40, 14
+
+END
+
diff --git a/reactos/dll/msacm/msacm_Sv.rc b/reactos/dll/msacm/msacm_Sv.rc
new file mode 100644 (file)
index 0000000..aa8cb5e
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Swedish resource file for MS ACM
+ *
+ * Copyright 2005 Anders Bergh
+ *
+ * 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
+ */
+
+LANGUAGE LANG_SWEDISH, SUBLANG_NEUTRAL
+
+DLG_ACMFORMATCHOOSE_ID DIALOG DISCARDABLE  10, 20, 225, 100
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Ljudval"
+FONT 8, "MS Shell Dlg"
+BEGIN
+
+    LTEXT           "&Namn:", -1, 5, 5, 115, 8, NOT WS_GROUP
+
+    COMBOBOX        IDD_ACMFORMATCHOOSE_CMB_CUSTOM, 5, 15, 115, 60,
+                    CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+
+    PUSHBUTTON      "&Spara som...", IDD_ACMFORMATCHOOSE_BTN_SETNAME, 125, 14, 45, 14
+    PUSHBUTTON      "&Ta bort", IDD_ACMFORMATCHOOSE_BTN_DELNAME, 175, 14, 45, 14
+
+    LTEXT           "&Format:", -1, 5, 41, 44, 8, NOT WS_GROUP
+
+    COMBOBOX        IDD_ACMFORMATCHOOSE_CMB_FORMATTAG, 50, 39, 170, 60,
+                    CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+
+    LTEXT           "&Attribut:", -1, 5, 59, 44, 8, NOT WS_GROUP
+
+#if 0
+    COMBOBOX        IDD_ACMFORMATCHOOSE_CMB_FORMAT, 50, 57, 170, 60,
+                    CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP |
+                    CBS_OWNERDRAWFIXED | CBS_HASSTRINGS
+#else
+    COMBOBOX        IDD_ACMFORMATCHOOSE_CMB_FORMAT, 50, 57, 170, 60,
+                    CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+#endif
+    DEFPUSHBUTTON   "OK", IDOK, 48, 80, 40, 14
+    PUSHBUTTON      "Avbryt", IDCANCEL, 92, 80, 40, 14
+    PUSHBUTTON      "&Hjälp", IDD_ACMFORMATCHOOSE_BTN_HELP, 136, 80, 40, 14
+
+END
diff --git a/reactos/dll/msacm/msacm_main.c b/reactos/dll/msacm/msacm_main.c
new file mode 100644 (file)
index 0000000..01bfa75
--- /dev/null
@@ -0,0 +1,463 @@
+/*
+ *      MSACM library
+ *
+ *      Copyright 1998  Patrik Stridvall
+ *
+ * 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 <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "mmsystem.h"
+#include "mmreg.h"
+#include "msacm.h"
+#include "msacmdrv.h"
+#include "wineacm.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msacm);
+
+/**************************************************************************
+ *             DllEntryPoint (MSACM.255)
+ *
+ * MSACM DLL entry point
+ *
+ */
+BOOL WINAPI MSACM_DllEntryPoint(DWORD fdwReason, HINSTANCE16 hinstDLL, WORD ds,
+                               WORD wHeapSize, DWORD dwReserved1, WORD wReserved2)
+{
+    static HANDLE      hndl;
+
+    TRACE("0x%x 0x%lx\n", hinstDLL, fdwReason);
+
+    switch (fdwReason) {
+    case DLL_PROCESS_ATTACH:
+        if (!hndl && !(hndl = LoadLibraryA("MSACM32.DLL"))) {
+           ERR("Could not load sibling MsAcm32.dll\n");
+           return FALSE;
+       }
+       break;
+    case DLL_PROCESS_DETACH:
+       FreeLibrary(hndl);
+       hndl = 0;
+       break;
+    case DLL_THREAD_ATTACH:
+    case DLL_THREAD_DETACH:
+       break;
+    }
+    return TRUE;
+}
+
+/***********************************************************************
+ *             acmGetVersion (MSACM.7)
+ */
+DWORD WINAPI acmGetVersion16(void)
+{
+  return acmGetVersion();
+}
+
+/***********************************************************************
+ *             acmMetrics (MSACM.8)
+ */
+
+MMRESULT16 WINAPI acmMetrics16(
+  HACMOBJ16 hao, UINT16 uMetric, LPVOID pMetric)
+{
+  FIXME("(0x%04x, %d, %p): semi-stub\n", hao, uMetric, pMetric);
+
+  if(!hao) return acmMetrics(0, uMetric, pMetric);
+  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+  return MMSYSERR_ERROR;
+}
+
+/***********************************************************************
+ *             acmDriverEnum (MSACM.10)
+ */
+MMRESULT16 WINAPI acmDriverEnum16(
+  ACMDRIVERENUMCB16 fnCallback, DWORD dwInstance, DWORD fdwEnum)
+{
+  FIXME("(%p, %ld, %ld): stub\n",
+    fnCallback, dwInstance, fdwEnum
+  );
+  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+  return MMSYSERR_ERROR;
+}
+
+/***********************************************************************
+ *             acmDriverDetails (MSACM.11)
+ */
+
+MMRESULT16 WINAPI acmDriverDetails16(
+  HACMDRIVERID16 hadid, LPACMDRIVERDETAILS16 padd, DWORD fdwDetails)
+{
+  FIXME("(0x%04x, %p, %ld): stub\n", hadid, padd, fdwDetails);
+  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+  return MMSYSERR_ERROR;
+}
+
+/***********************************************************************
+ *             acmDriverAdd (MSACM.12)
+ */
+MMRESULT16 WINAPI acmDriverAdd16(
+  LPHACMDRIVERID16 phadid, HINSTANCE16 hinstModule,
+  LPARAM lParam, DWORD dwPriority, DWORD fdwAdd)
+{
+  FIXME("(%p, 0x%04x, %ld, %ld, %ld): stub\n",
+    phadid, hinstModule, lParam, dwPriority, fdwAdd
+  );
+  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+  return MMSYSERR_ERROR;
+}
+
+/***********************************************************************
+ *             acmDriverRemove (MSACM.13)
+ */
+MMRESULT16 WINAPI acmDriverRemove16(
+  HACMDRIVERID16 hadid, DWORD fdwRemove)
+{
+  FIXME("(0x%04x, %ld): stub\n", hadid, fdwRemove);
+  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+  return MMSYSERR_ERROR;
+}
+
+/***********************************************************************
+ *             acmDriverOpen (MSACM.14)
+ */
+MMRESULT16 WINAPI acmDriverOpen16(
+  LPHACMDRIVER16 phad, HACMDRIVERID16 hadid, DWORD fdwOpen)
+{
+  FIXME("(%p, 0x%04x, %ld): stub\n", phad, hadid, fdwOpen);
+  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+  return MMSYSERR_ERROR;
+}
+
+/***********************************************************************
+ *             acmDriverClose (MSACM.15)
+ */
+MMRESULT16 WINAPI acmDriverClose16(
+  HACMDRIVER16 had, DWORD fdwClose)
+{
+  FIXME("(0x%04x, %ld): stub\n", had, fdwClose);
+  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+  return MMSYSERR_ERROR;
+}
+
+/***********************************************************************
+ *             acmDriverMessage (MSACM.16)
+ */
+LRESULT WINAPI acmDriverMessage16(
+  HACMDRIVER16 had, UINT16 uMsg, LPARAM lParam1, LPARAM lParam2)
+{
+  FIXME("(0x%04x, %d, %ld, %ld): stub\n",
+    had, uMsg, lParam1, lParam2
+  );
+  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+  return 0;
+}
+
+/***********************************************************************
+ *             acmDriverID (MSACM.17)
+ */
+MMRESULT16 WINAPI acmDriverID16(
+  HACMOBJ16 hao, LPHACMDRIVERID16 phadid, DWORD fdwDriverID)
+{
+  FIXME("(0x%04x, %p, %ld): stub\n", hao, phadid, fdwDriverID);
+  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+  return MMSYSERR_ERROR;
+}
+
+/***********************************************************************
+ *             acmDriverPriority (MSACM.18)
+ */
+MMRESULT16 WINAPI acmDriverPriority16(
+ HACMDRIVERID16 hadid, DWORD dwPriority, DWORD fdwPriority)
+{
+  FIXME("(0x%04x, %ld, %ld): stub\n",
+    hadid, dwPriority, fdwPriority
+  );
+  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+  return MMSYSERR_ERROR;
+}
+
+/***********************************************************************
+ *             acmFormatTagDetails (MSACM.30)
+ */
+MMRESULT16 WINAPI acmFormatTagDetails16(
+  HACMDRIVER16 had, LPACMFORMATTAGDETAILS16 paftd, DWORD fdwDetails)
+{
+  FIXME("(0x%04x, %p, %ld): stub\n", had, paftd, fdwDetails);
+  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+  return MMSYSERR_ERROR;
+}
+
+/***********************************************************************
+ *             acmFormatTagEnum (MSACM.31)
+ */
+MMRESULT16 WINAPI acmFormatTagEnum16(
+  HACMDRIVER16 had, LPACMFORMATTAGDETAILS16 paftd,
+  ACMFORMATTAGENUMCB16 fnCallback, DWORD dwInstance, DWORD fdwEnum)
+{
+  FIXME("(0x%04x, %p, %p, %ld, %ld): stub\n",
+    had, paftd, fnCallback, dwInstance, fdwEnum
+  );
+  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+  return MMSYSERR_ERROR;
+}
+
+/***********************************************************************
+ *             acmFormatChoose (MSACM.40)
+ */
+MMRESULT16 WINAPI acmFormatChoose16(
+  LPACMFORMATCHOOSE16 pafmtc)
+{
+  FIXME("(%p): stub\n", pafmtc);
+  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+  return MMSYSERR_ERROR;
+}
+
+/***********************************************************************
+ *             acmFormatDetails (MSACM.41)
+ */
+MMRESULT16 WINAPI acmFormatDetails16(
+  HACMDRIVER16 had, LPACMFORMATDETAILS16 pafd, DWORD fdwDetails)
+{
+  FIXME("(0x%04x, %p, %ld): stub\n", had, pafd, fdwDetails);
+  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+  return MMSYSERR_ERROR;
+}
+
+/***********************************************************************
+ *             acmFormatEnum (MSACM.42)
+ */
+MMRESULT16 WINAPI acmFormatEnum16(
+  HACMDRIVER16 had, LPACMFORMATDETAILS16 pafd,
+  ACMFORMATENUMCB16 fnCallback, DWORD dwInstance, DWORD fdwEnum)
+{
+  FIXME("(0x%04x, %p, %p, %ld, %ld): stub\n",
+    had, pafd, fnCallback, dwInstance, fdwEnum
+  );
+  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+  return MMSYSERR_ERROR;
+}
+
+/***********************************************************************
+ *             acmFormatSuggest (MSACM.45)
+ */
+MMRESULT16 WINAPI acmFormatSuggest16(
+  HACMDRIVER16 had, LPWAVEFORMATEX pwfxSrc,
+  LPWAVEFORMATEX pwfxDst, DWORD cbwfxDst, DWORD fdwSuggest)
+{
+  FIXME("(0x%04x, %p, %p, %ld, %ld): stub\n",
+    had, pwfxSrc, pwfxDst, cbwfxDst, fdwSuggest
+  );
+  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+  return MMSYSERR_ERROR;
+}
+
+/***********************************************************************
+ *             acmFilterTagDetails (MSACM.50)
+ */
+MMRESULT16 WINAPI acmFilterTagDetails16(
+  HACMDRIVER16 had, LPACMFILTERTAGDETAILS16 paftd, DWORD fdwDetails)
+{
+  FIXME("(0x%04x, %p, %ld): stub\n", had, paftd, fdwDetails);
+  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+  return MMSYSERR_ERROR;
+}
+
+/***********************************************************************
+ *             acmFilterTagEnum (MSACM.51)
+ */
+MMRESULT16 WINAPI acmFilterTagEnum16(
+  HACMDRIVER16 had, LPACMFILTERTAGDETAILS16 paftd,
+  ACMFILTERTAGENUMCB16 fnCallback, DWORD dwInstance, DWORD fdwEnum)
+{
+  FIXME("(0x%04x, %p, %p, %ld, %ld): stub\n",
+    had, paftd, fnCallback, dwInstance, fdwEnum
+  );
+  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+  return MMSYSERR_ERROR;
+}
+
+/***********************************************************************
+ *             acmFilterChoose (MSACM.60)
+ */
+MMRESULT16 WINAPI acmFilterChoose16(
+  LPACMFILTERCHOOSE16 pafltrc)
+{
+  FIXME("(%p): stub\n", pafltrc);
+  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+  return MMSYSERR_ERROR;
+}
+
+/***********************************************************************
+ *             acmFilterDetails (MSACM.61)
+ */
+MMRESULT16 WINAPI acmFilterDetails16(
+  HACMDRIVER16 had, LPACMFILTERDETAILS16 pafd, DWORD fdwDetails)
+{
+  FIXME("(0x%04x, %p, %ld): stub\n", had, pafd, fdwDetails);
+  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+  return MMSYSERR_ERROR;
+}
+
+/***********************************************************************
+ *             acmFilterEnum (MSACM.62)
+ */
+MMRESULT16 WINAPI acmFilterEnum16(
+  HACMDRIVER16 had, LPACMFILTERDETAILS16 pafd,
+  ACMFILTERENUMCB16 fnCallback, DWORD dwInstance, DWORD fdwEnum)
+{
+  FIXME("(0x%04x, %p, %p, %ld, %ld): stub\n",
+    had, pafd, fnCallback, dwInstance, fdwEnum
+  );
+  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+  return MMSYSERR_ERROR;
+}
+
+/***********************************************************************
+ *             acmStreamOpen (MSACM.70)
+ */
+MMRESULT16 WINAPI acmStreamOpen16(
+  LPHACMSTREAM16 phas, HACMDRIVER16 had,
+  LPWAVEFORMATEX pwfxSrc, LPWAVEFORMATEX pwfxDst,
+  LPWAVEFILTER pwfltr, DWORD dwCallback,
+  DWORD dwInstance, DWORD fdwOpen)
+{
+  FIXME("(%p, 0x%04x, %p, %p, %p, %ld, %ld, %ld): stub\n",
+    phas, had, pwfxSrc, pwfxDst, pwfltr,
+    dwCallback, dwInstance, fdwOpen
+  );
+  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+  return MMSYSERR_ERROR;
+}
+
+/***********************************************************************
+ *             acmStreamClose (MSACM.71)
+ */
+MMRESULT16 WINAPI acmStreamClose16(
+  HACMSTREAM16 has, DWORD fdwClose)
+{
+  FIXME("(0x%04x, %ld): stub\n", has, fdwClose);
+  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+  return MMSYSERR_ERROR;
+}
+
+/***********************************************************************
+ *             acmStreamSize (MSACM.72)
+ */
+MMRESULT16 WINAPI acmStreamSize16(
+  HACMSTREAM16 has, DWORD cbInput,
+  LPDWORD pdwOutputBytes, DWORD fdwSize)
+{
+  FIXME("(0x%04x, %ld, %p, %ld): stub\n",
+    has, cbInput, pdwOutputBytes, fdwSize
+  );
+  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+  return MMSYSERR_ERROR;
+}
+
+/***********************************************************************
+ *             acmStreamConvert (MSACM.75)
+ */
+MMRESULT16 WINAPI acmStreamConvert16(
+  HACMSTREAM16 has, LPACMSTREAMHEADER16 pash, DWORD fdwConvert)
+{
+  FIXME("(0x%04x, %p, %ld): stub\n", has, pash, fdwConvert);
+  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+  return MMSYSERR_ERROR;
+}
+
+/***********************************************************************
+ *             acmStreamReset (MSACM.76)
+ */
+MMRESULT16 WINAPI acmStreamReset16(
+  HACMSTREAM16 has, DWORD fdwReset)
+{
+  FIXME("(0x%04x, %ld): stub\n", has, fdwReset);
+  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+  return MMSYSERR_ERROR;
+}
+
+/***********************************************************************
+ *             acmStreamPrepareHeader (MSACM.77)
+ */
+MMRESULT16 WINAPI acmStreamPrepareHeader16(
+  HACMSTREAM16 has, LPACMSTREAMHEADER16 pash, DWORD fdwPrepare)
+{
+  FIXME("(0x%04x, %p, %ld): stub\n", has, pash, fdwPrepare);
+  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+  return MMSYSERR_ERROR;
+}
+
+/***********************************************************************
+ *             acmStreamUnprepareHeader (MSACM.78)
+ */
+MMRESULT16 WINAPI acmStreamUnprepareHeader16(
+  HACMSTREAM16 has, LPACMSTREAMHEADER16 pash, DWORD fdwUnprepare)
+{
+  FIXME("(0x%04x, %p, %ld): stub\n",
+    has, pash, fdwUnprepare
+  );
+  SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+  return MMSYSERR_ERROR;
+}
+
+/***********************************************************************
+ *             ACMAPPLICATIONEXIT (MSACM.150)
+ * FIXME
+ *   No documentation found.
+ */
+
+/***********************************************************************
+ *             ACMHUGEPAGELOCK (MSACM.175)
+ *FIXME
+ *   No documentation found.
+ */
+
+/***********************************************************************
+ *             ACMHUGEPAGEUNLOCK (MSACM.176)
+ * FIXME
+ *   No documentation found.
+ */
+
+/***********************************************************************
+ *             ACMOPENCONVERSION (MSACM.200)
+ * FIXME
+ *   No documentation found.
+ */
+
+/***********************************************************************
+ *             ACMCLOSECONVERSION (MSACM.201)
+ * FIXME
+ *   No documentation found.
+ */
+
+/***********************************************************************
+ *             ACMCONVERT (MSACM.202)
+ * FIXME
+ *   No documentation found.
+ */
+
+/***********************************************************************
+ *             ACMCHOOSEFORMAT (MSACM.203)
+ * FIXME
+ *   No documentation found.
+ */
+
+
diff --git a/reactos/dll/msacm/msadp32/Makefile.in b/reactos/dll/msacm/msadp32/Makefile.in
new file mode 100644 (file)
index 0000000..c331f65
--- /dev/null
@@ -0,0 +1,12 @@
+TOPSRCDIR = @top_srcdir@
+TOPOBJDIR = ../../..
+SRCDIR    = @srcdir@
+VPATH     = @srcdir@
+MODULE    = msadp32.acm
+IMPORTS   = winmm user32 kernel32
+
+C_SRCS = msadp32.c
+
+@MAKE_DLL_RULES@
+
+### Dependencies:
diff --git a/reactos/dll/msacm/msadp32/msadp32.acm.spec b/reactos/dll/msacm/msadp32/msadp32.acm.spec
new file mode 100644 (file)
index 0000000..a0ba1c7
--- /dev/null
@@ -0,0 +1 @@
+@ stdcall DriverProc (long long long long long) ADPCM_DriverProc
diff --git a/reactos/dll/msacm/msadp32/msadp32.c b/reactos/dll/msacm/msadp32/msadp32.c
new file mode 100644 (file)
index 0000000..dae681c
--- /dev/null
@@ -0,0 +1,781 @@
+/*
+ * MS ADPCM handling
+ *
+ *      Copyright (C) 2002             Eric Pouech
+ *
+ *
+ * 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 <assert.h>
+#include <stdarg.h>
+#include <string.h>
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "winnls.h"
+#include "mmsystem.h"
+#include "mmreg.h"
+#include "msacm.h"
+#include "msacmdrv.h"
+#include "wine/debug.h"
+
+/* see http://www.pcisys.net/~melanson/codecs/adpcm.txt for the details */
+
+WINE_DEFAULT_DEBUG_CHANNEL(adpcm);
+
+/***********************************************************************
+ *           ADPCM_drvOpen
+ */
+static DWORD   ADPCM_drvOpen(LPCSTR str)
+{
+    return 1;
+}
+
+/***********************************************************************
+ *           ADPCM_drvClose
+ */
+static DWORD   ADPCM_drvClose(DWORD dwDevID)
+{
+    return 1;
+}
+
+typedef struct tagAcmAdpcmData
+{
+    void (*convert)(PACMDRVSTREAMINSTANCE adsi,
+                   const unsigned char*, LPDWORD, unsigned char*, LPDWORD);
+} AcmAdpcmData;
+
+/* table to list all supported formats... those are the basic ones. this
+ * also helps given a unique index to each of the supported formats
+ */
+typedef        struct
+{
+    int                nChannels;
+    int                nBits;
+    int                rate;
+} Format;
+
+static Format PCM_Formats[] =
+{
+    {1,  8,  8000}, {2,  8,  8000}, {1, 16,  8000}, {2, 16,  8000},
+    {1,  8, 11025}, {2,  8, 11025}, {1, 16, 11025}, {2, 16, 11025},
+    {1,  8, 22050}, {2,  8, 22050}, {1, 16, 22050}, {2, 16, 22050},
+    {1,  8, 44100}, {2,  8, 44100}, {1, 16, 44100}, {2, 16, 44100},
+};
+
+static Format ADPCM_Formats[] =
+{
+    {1,  4,  8000}, {2,        4,  8000},  {1,  4, 11025}, {2,  4, 11025},
+    {1,  4, 22050}, {2,        4, 22050},  {1,  4, 44100}, {2,  4, 44100},
+};
+
+#define        NUM_PCM_FORMATS         (sizeof(PCM_Formats) / sizeof(PCM_Formats[0]))
+#define        NUM_ADPCM_FORMATS       (sizeof(ADPCM_Formats) / sizeof(ADPCM_Formats[0]))
+
+static int MS_Delta[] =
+{
+    230, 230, 230, 230, 307, 409, 512, 614,
+    768, 614, 512, 409, 307, 230, 230, 230
+};
+
+
+static ADPCMCOEFSET MSADPCM_CoeffSet[] =
+{
+    {256, 0}, {512, -256}, {0, 0}, {192, 64}, {240, 0}, {460, -208}, {392, -232}
+};
+
+/***********************************************************************
+ *           ADPCM_GetFormatIndex
+ */
+static DWORD   ADPCM_GetFormatIndex(WAVEFORMATEX* wfx)
+{
+    int        i, hi;
+    Format*    fmts;
+
+    switch (wfx->wFormatTag)
+    {
+    case WAVE_FORMAT_PCM:
+       hi = NUM_PCM_FORMATS;
+       fmts = PCM_Formats;
+       break;
+    case WAVE_FORMAT_ADPCM:
+       hi = NUM_ADPCM_FORMATS;
+       fmts = ADPCM_Formats;
+       break;
+    default:
+       return 0xFFFFFFFF;
+    }
+
+    for (i = 0; i < hi; i++)
+    {
+       if (wfx->nChannels == fmts[i].nChannels &&
+           wfx->nSamplesPerSec == fmts[i].rate &&
+           wfx->wBitsPerSample == fmts[i].nBits)
+           return i;
+    }
+
+    return 0xFFFFFFFF;
+}
+
+static void     init_wfx_adpcm(ADPCMWAVEFORMAT* awfx)
+{
+    register WAVEFORMATEX*      pwfx = &awfx->wfx;
+
+    /* we assume wFormatTag, nChannels, nSamplesPerSec and wBitsPerSample
+     * have been initialized... */
+
+    if (pwfx->wFormatTag != WAVE_FORMAT_ADPCM) {FIXME("wrong FT\n"); return;}
+    if (ADPCM_GetFormatIndex(pwfx) == 0xFFFFFFFF) {FIXME("wrong fmt\n"); return;}
+
+    switch (pwfx->nSamplesPerSec)
+    {
+    case  8000: pwfx->nBlockAlign = 256;   break;
+    case 11025: pwfx->nBlockAlign = 256;   break;
+    case 22050: pwfx->nBlockAlign = 512;   break;
+    default:
+    case 44100: pwfx->nBlockAlign = 1024;  break;
+    }
+    pwfx->cbSize = 2 * sizeof(WORD) + 7 * sizeof(ADPCMCOEFSET);
+    /* 7 is the size of the block head (which contains two samples) */
+
+    awfx->wSamplesPerBlock = (pwfx->nBlockAlign - (7 * pwfx->nChannels)) * (2 / pwfx->nChannels) + 2;
+    pwfx->nAvgBytesPerSec = (pwfx->nSamplesPerSec * pwfx->nBlockAlign) / awfx->wSamplesPerBlock;
+    awfx->wNumCoef = 7;
+    memcpy(awfx->aCoef, MSADPCM_CoeffSet, 7 * sizeof(ADPCMCOEFSET));
+}
+
+/***********************************************************************
+ *           R16
+ *
+ * Read a 16 bit sample (correctly handles endianess)
+ */
+static inline short  R16(const unsigned char* src)
+{
+    return (short)((unsigned short)src[0] | ((unsigned short)src[1] << 8));
+}
+
+/***********************************************************************
+ *           W16
+ *
+ * Write a 16 bit sample (correctly handles endianess)
+ */
+static inline void  W16(unsigned char* dst, short s)
+{
+    dst[0] = LOBYTE(s);
+    dst[1] = HIBYTE(s);
+}
+
+static inline void clamp_sample(int* sample)
+{
+    if (*sample < -32768) *sample = -32768;
+    if (*sample >  32767) *sample =  32767;
+}
+
+static inline void process_nibble(unsigned nibble, int* idelta,
+                                  int* sample1, int* sample2,
+                                  const ADPCMCOEFSET* coeff)
+{
+    int sample;
+    int snibble;
+
+    /* nibble is in fact a signed 4 bit integer => propagate sign if needed */
+    snibble = (nibble & 0x08) ? (nibble - 16) : nibble;
+    sample = ((*sample1 * coeff->iCoef1) + (*sample2 * coeff->iCoef2)) / 256 +
+        snibble * *idelta;
+    clamp_sample(&sample);
+
+    *sample2 = *sample1;
+    *sample1 = sample;
+    *idelta = ((MS_Delta[nibble] * *idelta) / 256);
+    if (*idelta < 16) *idelta = 16;
+}
+
+static void cvtSSms16K(PACMDRVSTREAMINSTANCE adsi,
+                        const unsigned char* src, LPDWORD nsrc,
+                        unsigned char* dst, LPDWORD ndst)
+{
+    int                 ideltaL, ideltaR;
+    int                 sample1L, sample2L;
+    int                 sample1R, sample2R;
+    ADPCMCOEFSET        coeffL, coeffR;
+    int                 nsamp;
+    int                        nsamp_blk = ((ADPCMWAVEFORMAT*)adsi->pwfxSrc)->wSamplesPerBlock;
+    DWORD              nblock = min(*nsrc / adsi->pwfxSrc->nBlockAlign,
+                                     *ndst / (nsamp_blk * 2 * 2));
+
+    *nsrc = nblock * adsi->pwfxSrc->nBlockAlign;
+    *ndst = nblock * nsamp_blk * 2 * 2;
+
+    nsamp_blk -= 2; /* see below for samples from block head */
+    for (; nblock > 0; nblock--)
+    {
+        const unsigned char*    in_src = src;
+
+        assert(*src <= 6);
+        coeffL = MSADPCM_CoeffSet[*src++];
+        assert(*src <= 6);
+        coeffR = MSADPCM_CoeffSet[*src++];
+
+        ideltaL  = R16(src);    src += 2;
+        ideltaR  = R16(src);    src += 2;
+        sample1L = R16(src);    src += 2;
+        sample1R = R16(src);    src += 2;
+        sample2L = R16(src);    src += 2;
+        sample2R = R16(src);    src += 2;
+
+        /* store samples from block head */
+        W16(dst, sample2L);      dst += 2;
+        W16(dst, sample2R);      dst += 2;
+        W16(dst, sample1L);      dst += 2;
+        W16(dst, sample1R);      dst += 2;
+
+        for (nsamp = nsamp_blk; nsamp > 0; nsamp--)
+        {
+            process_nibble(*src >> 4, &ideltaL, &sample1L, &sample2L, &coeffL);
+            W16(dst, sample1L); dst += 2;
+            process_nibble(*src++ & 0x0F, &ideltaR, &sample1R, &sample2R, &coeffR);
+            W16(dst, sample1R); dst += 2;
+        }
+        src = in_src + adsi->pwfxSrc->nBlockAlign;
+    }
+}
+
+static void cvtMMms16K(PACMDRVSTREAMINSTANCE adsi,
+                        const unsigned char* src, LPDWORD nsrc,
+                        unsigned char* dst, LPDWORD ndst)
+{
+    int                 idelta;
+    int                 sample1, sample2;
+    ADPCMCOEFSET        coeff;
+    int                 nsamp;
+    int                        nsamp_blk = ((ADPCMWAVEFORMAT*)adsi->pwfxSrc)->wSamplesPerBlock;
+    DWORD              nblock = min(*nsrc / adsi->pwfxSrc->nBlockAlign,
+                                     *ndst / (nsamp_blk * 2));
+
+    *nsrc = nblock * adsi->pwfxSrc->nBlockAlign;
+    *ndst = nblock * nsamp_blk * 2;
+
+    nsamp_blk -= 2; /* see below for samples from block head */
+    for (; nblock > 0; nblock--)
+    {
+        const unsigned char*    in_src = src;
+
+        assert(*src <= 6);
+        coeff = MSADPCM_CoeffSet[*src++];
+
+        idelta =  R16(src);     src += 2;
+        sample1 = R16(src);     src += 2;
+        sample2 = R16(src);     src += 2;
+
+        /* store samples from block head */
+        W16(dst, sample2);      dst += 2;
+        W16(dst, sample1);      dst += 2;
+
+        for (nsamp = nsamp_blk; nsamp > 0; nsamp -= 2)
+        {
+            process_nibble(*src >> 4, &idelta, &sample1, &sample2, &coeff);
+            W16(dst, sample1); dst += 2;
+            process_nibble(*src++ & 0x0F, &idelta, &sample1, &sample2, &coeff);
+            W16(dst, sample1); dst += 2;
+        }
+        src = in_src + adsi->pwfxSrc->nBlockAlign;
+    }
+}
+
+#if 0
+static void cvtSS16msK(PACMDRVSTREAMINSTANCE adsi,
+                        const unsigned char* src, LPDWORD nsrc,
+                        unsigned char* dst, LPDWORD ndst)
+{
+}
+
+static void cvtMM16msK(PACMDRVSTREAMINSTANCE adsi,
+                        const unsigned char* src, LPDWORD nsrc,
+                        unsigned char* dst, LPDWORD ndst)
+{
+}
+#endif
+
+/***********************************************************************
+ *           ADPCM_DriverDetails
+ *
+ */
+static LRESULT ADPCM_DriverDetails(PACMDRIVERDETAILSW add)
+{
+    add->fccType = ACMDRIVERDETAILS_FCCTYPE_AUDIOCODEC;
+    add->fccComp = ACMDRIVERDETAILS_FCCCOMP_UNDEFINED;
+    add->wMid = 0xFF;
+    add->wPid = 0x00;
+    add->vdwACM = 0x01000000;
+    add->vdwDriver = 0x01000000;
+    add->fdwSupport = ACMDRIVERDETAILS_SUPPORTF_CODEC;
+    add->cFormatTags = 2; /* PCM, MS ADPCM */
+    add->cFilterTags = 0;
+    add->hicon = NULL;
+    MultiByteToWideChar( CP_ACP, 0, "WINE-MS ADPCM", -1,
+                         add->szShortName, sizeof(add->szShortName)/sizeof(WCHAR) );
+    MultiByteToWideChar( CP_ACP, 0, "Wine MS ADPCM converter", -1,
+                         add->szLongName, sizeof(add->szLongName)/sizeof(WCHAR) );
+    MultiByteToWideChar( CP_ACP, 0, "Brought to you by the Wine team...", -1,
+                         add->szCopyright, sizeof(add->szCopyright)/sizeof(WCHAR) );
+    MultiByteToWideChar( CP_ACP, 0, "Refer to LICENSE file", -1,
+                         add->szLicensing, sizeof(add->szLicensing)/sizeof(WCHAR) );
+    add->szFeatures[0] = 0;
+
+    return MMSYSERR_NOERROR;
+}
+
+/***********************************************************************
+ *           ADPCM_FormatTagDetails
+ *
+ */
+static LRESULT ADPCM_FormatTagDetails(PACMFORMATTAGDETAILSW aftd, DWORD dwQuery)
+{
+    static WCHAR szPcm[]={'P','C','M',0};
+    static WCHAR szMsAdPcm[]={'M','S',' ','A','d','P','C','M',0};
+
+    switch (dwQuery)
+    {
+    case ACM_FORMATTAGDETAILSF_INDEX:
+       if (aftd->dwFormatTagIndex >= 2) return ACMERR_NOTPOSSIBLE;
+       break;
+    case ACM_FORMATTAGDETAILSF_LARGESTSIZE:
+       if (aftd->dwFormatTag == WAVE_FORMAT_UNKNOWN)
+        {
+            aftd->dwFormatTagIndex = 1; /* WAVE_FORMAT_ADPCM is bigger than PCM */
+           break;
+       }
+       /* fall thru */
+    case ACM_FORMATTAGDETAILSF_FORMATTAG:
+       switch (aftd->dwFormatTag)
+        {
+       case WAVE_FORMAT_PCM:   aftd->dwFormatTagIndex = 0; break;
+       case WAVE_FORMAT_ADPCM: aftd->dwFormatTagIndex = 1; break;
+       default:                return ACMERR_NOTPOSSIBLE;
+       }
+       break;
+    default:
+       WARN("Unsupported query %08lx\n", dwQuery);
+       return MMSYSERR_NOTSUPPORTED;
+    }
+
+    aftd->fdwSupport = ACMDRIVERDETAILS_SUPPORTF_CODEC;
+    switch (aftd->dwFormatTagIndex)
+    {
+    case 0:
+       aftd->dwFormatTag = WAVE_FORMAT_PCM;
+       aftd->cbFormatSize = sizeof(PCMWAVEFORMAT);
+       aftd->cStandardFormats = NUM_PCM_FORMATS;
+        lstrcpyW(aftd->szFormatTag, szPcm);
+        break;
+    case 1:
+       aftd->dwFormatTag = WAVE_FORMAT_ADPCM;
+       aftd->cbFormatSize = sizeof(ADPCMWAVEFORMAT) + (7 - 1) * sizeof(ADPCMCOEFSET);
+       aftd->cStandardFormats = NUM_ADPCM_FORMATS;
+        lstrcpyW(aftd->szFormatTag, szMsAdPcm);
+       break;
+    }
+    return MMSYSERR_NOERROR;
+}
+
+/***********************************************************************
+ *           ADPCM_FormatDetails
+ *
+ */
+static LRESULT ADPCM_FormatDetails(PACMFORMATDETAILSW afd, DWORD dwQuery)
+{
+    switch (dwQuery)
+    {
+    case ACM_FORMATDETAILSF_FORMAT:
+       if (ADPCM_GetFormatIndex(afd->pwfx) == 0xFFFFFFFF) return ACMERR_NOTPOSSIBLE;
+       break;
+    case ACM_FORMATDETAILSF_INDEX:
+       afd->pwfx->wFormatTag = afd->dwFormatTag;
+       switch (afd->dwFormatTag)
+        {
+       case WAVE_FORMAT_PCM:
+           if (afd->dwFormatIndex >= NUM_PCM_FORMATS) return ACMERR_NOTPOSSIBLE;
+           afd->pwfx->nChannels = PCM_Formats[afd->dwFormatIndex].nChannels;
+           afd->pwfx->nSamplesPerSec = PCM_Formats[afd->dwFormatIndex].rate;
+           afd->pwfx->wBitsPerSample = PCM_Formats[afd->dwFormatIndex].nBits;
+           /* native MSACM uses a PCMWAVEFORMAT structure, so cbSize is not accessible
+            * afd->pwfx->cbSize = 0;
+            */
+           afd->pwfx->nBlockAlign =
+               (afd->pwfx->nChannels * afd->pwfx->wBitsPerSample) / 8;
+           afd->pwfx->nAvgBytesPerSec =
+               afd->pwfx->nSamplesPerSec * afd->pwfx->nBlockAlign;
+           break;
+       case WAVE_FORMAT_ADPCM:
+           if (afd->dwFormatIndex >= NUM_ADPCM_FORMATS) return ACMERR_NOTPOSSIBLE;
+            if (afd->cbwfx < sizeof(ADPCMWAVEFORMAT) + (7 - 1) * sizeof(ADPCMCOEFSET))
+                return ACMERR_NOTPOSSIBLE;
+           afd->pwfx->nChannels = ADPCM_Formats[afd->dwFormatIndex].nChannels;
+           afd->pwfx->nSamplesPerSec = ADPCM_Formats[afd->dwFormatIndex].rate;
+           afd->pwfx->wBitsPerSample = ADPCM_Formats[afd->dwFormatIndex].nBits;
+            init_wfx_adpcm((ADPCMWAVEFORMAT*)afd->pwfx);
+           break;
+       default:
+           WARN("Unsupported tag %08lx\n", afd->dwFormatTag);
+           return MMSYSERR_INVALPARAM;
+       }
+       break;
+    default:
+       WARN("Unsupported query %08lx\n", dwQuery);
+       return MMSYSERR_NOTSUPPORTED;
+    }
+    afd->fdwSupport = ACMDRIVERDETAILS_SUPPORTF_CODEC;
+    afd->szFormat[0] = 0; /* let MSACM format this for us... */
+
+    return MMSYSERR_NOERROR;
+}
+
+/***********************************************************************
+ *           ADPCM_FormatSuggest
+ *
+ */
+static LRESULT ADPCM_FormatSuggest(PACMDRVFORMATSUGGEST adfs)
+{
+    /* some tests ... */
+    if (adfs->cbwfxSrc < sizeof(PCMWAVEFORMAT) ||
+       adfs->cbwfxDst < sizeof(PCMWAVEFORMAT) ||
+       ADPCM_GetFormatIndex(adfs->pwfxSrc) == 0xFFFFFFFF) return ACMERR_NOTPOSSIBLE;
+    /* FIXME: should do those tests against the real size (according to format tag */
+
+    /* If no suggestion for destination, then copy source value */
+    if (!(adfs->fdwSuggest & ACM_FORMATSUGGESTF_NCHANNELS))
+       adfs->pwfxDst->nChannels = adfs->pwfxSrc->nChannels;
+    if (!(adfs->fdwSuggest & ACM_FORMATSUGGESTF_NSAMPLESPERSEC))
+        adfs->pwfxDst->nSamplesPerSec = adfs->pwfxSrc->nSamplesPerSec;
+
+    if (!(adfs->fdwSuggest & ACM_FORMATSUGGESTF_WBITSPERSAMPLE))
+    {
+       if (adfs->pwfxSrc->wFormatTag == WAVE_FORMAT_PCM)
+            adfs->pwfxDst->wBitsPerSample = 4;
+        else
+            adfs->pwfxDst->wBitsPerSample = 16;
+    }
+    if (!(adfs->fdwSuggest & ACM_FORMATSUGGESTF_WFORMATTAG))
+    {
+       if (adfs->pwfxSrc->wFormatTag == WAVE_FORMAT_PCM)
+            adfs->pwfxDst->wFormatTag = WAVE_FORMAT_ADPCM;
+        else
+            adfs->pwfxDst->wFormatTag = WAVE_FORMAT_PCM;
+    }
+
+    /* check if result is ok */
+    if (ADPCM_GetFormatIndex(adfs->pwfxDst) == 0xFFFFFFFF) return ACMERR_NOTPOSSIBLE;
+
+    /* recompute other values */
+    switch (adfs->pwfxDst->wFormatTag)
+    {
+    case WAVE_FORMAT_PCM:
+        adfs->pwfxDst->nBlockAlign = (adfs->pwfxDst->nChannels * adfs->pwfxDst->wBitsPerSample) / 8;
+        adfs->pwfxDst->nAvgBytesPerSec = adfs->pwfxDst->nSamplesPerSec * adfs->pwfxDst->nBlockAlign;
+        break;
+    case WAVE_FORMAT_ADPCM:
+        init_wfx_adpcm((ADPCMWAVEFORMAT*)adfs->pwfxDst);
+        break;
+    default:
+        FIXME("\n");
+        break;
+    }
+
+    return MMSYSERR_NOERROR;
+}
+
+/***********************************************************************
+ *           ADPCM_Reset
+ *
+ */
+static void    ADPCM_Reset(PACMDRVSTREAMINSTANCE adsi, AcmAdpcmData* aad)
+{
+}
+
+/***********************************************************************
+ *           ADPCM_StreamOpen
+ *
+ */
+static LRESULT ADPCM_StreamOpen(PACMDRVSTREAMINSTANCE adsi)
+{
+    AcmAdpcmData*      aad;
+
+    assert(!(adsi->fdwOpen & ACM_STREAMOPENF_ASYNC));
+
+    if (ADPCM_GetFormatIndex(adsi->pwfxSrc) == 0xFFFFFFFF ||
+       ADPCM_GetFormatIndex(adsi->pwfxDst) == 0xFFFFFFFF)
+       return ACMERR_NOTPOSSIBLE;
+
+    aad = HeapAlloc(GetProcessHeap(), 0, sizeof(AcmAdpcmData));
+    if (aad == 0) return MMSYSERR_NOMEM;
+
+    adsi->dwDriver = (DWORD)aad;
+
+    if (adsi->pwfxSrc->wFormatTag == WAVE_FORMAT_PCM &&
+       adsi->pwfxDst->wFormatTag == WAVE_FORMAT_PCM)
+    {
+       goto theEnd;
+    }
+    else if (adsi->pwfxSrc->wFormatTag == WAVE_FORMAT_ADPCM &&
+             adsi->pwfxDst->wFormatTag == WAVE_FORMAT_PCM)
+    {
+       /* resampling or mono <=> stereo not available
+         * ADPCM algo only define 16 bit per sample output
+         */
+       if (adsi->pwfxSrc->nSamplesPerSec != adsi->pwfxDst->nSamplesPerSec ||
+           adsi->pwfxSrc->nChannels != adsi->pwfxDst->nChannels ||
+            adsi->pwfxDst->wBitsPerSample != 16)
+           goto theEnd;
+
+#if 0
+        {
+            unsigned int nspb = ((IMAADPCMWAVEFORMAT*)adsi->pwfxSrc)->wSamplesPerBlock;
+            FIXME("spb=%u\n", nspb);
+
+            /* we check that in a block, after the header, samples are present on
+             * 4-sample packet pattern
+             * we also check that the block alignement is bigger than the expected size
+             */
+            if (((nspb - 1) & 3) != 0) goto theEnd;
+            if ((((nspb - 1) / 2) + 4) * adsi->pwfxSrc->nChannels < adsi->pwfxSrc->nBlockAlign)
+                goto theEnd;
+        }
+#endif
+
+       /* adpcm decoding... */
+       if (adsi->pwfxDst->wBitsPerSample == 16 && adsi->pwfxDst->nChannels == 2)
+           aad->convert = cvtSSms16K;
+       if (adsi->pwfxDst->wBitsPerSample == 16 && adsi->pwfxDst->nChannels == 1)
+           aad->convert = cvtMMms16K;
+    }
+    else if (adsi->pwfxSrc->wFormatTag == WAVE_FORMAT_PCM &&
+             adsi->pwfxDst->wFormatTag == WAVE_FORMAT_ADPCM)
+    {
+       if (adsi->pwfxSrc->nSamplesPerSec != adsi->pwfxDst->nSamplesPerSec ||
+           adsi->pwfxSrc->nChannels != adsi->pwfxDst->nChannels ||
+            adsi->pwfxSrc->wBitsPerSample != 16)
+           goto theEnd;
+#if 0
+        nspb = ((IMAADPCMWAVEFORMAT*)adsi->pwfxDst)->wSamplesPerBlock;
+        FIXME("spb=%u\n", nspb);
+
+        /* we check that in a block, after the header, samples are present on
+         * 4-sample packet pattern
+         * we also check that the block alignement is bigger than the expected size
+         */
+        if (((nspb - 1) & 3) != 0) goto theEnd;
+        if ((((nspb - 1) / 2) + 4) * adsi->pwfxDst->nChannels < adsi->pwfxDst->nBlockAlign)
+            goto theEnd;
+#endif
+#if 0
+       /* adpcm coding... */
+       if (adsi->pwfxSrc->wBitsPerSample == 16 && adsi->pwfxSrc->nChannels == 2)
+           aad->convert = cvtSS16msK;
+       if (adsi->pwfxSrc->wBitsPerSample == 16 && adsi->pwfxSrc->nChannels == 1)
+           aad->convert = cvtMM16msK;
+#endif
+        FIXME("We don't support encoding yet\n");
+        goto theEnd;
+    }
+    else goto theEnd;
+    ADPCM_Reset(adsi, aad);
+
+    return MMSYSERR_NOERROR;
+
+ theEnd:
+    HeapFree(GetProcessHeap(), 0, aad);
+    adsi->dwDriver = 0L;
+    return MMSYSERR_NOTSUPPORTED;
+}
+
+/***********************************************************************
+ *           ADPCM_StreamClose
+ *
+ */
+static LRESULT ADPCM_StreamClose(PACMDRVSTREAMINSTANCE adsi)
+{
+    HeapFree(GetProcessHeap(), 0, (void*)adsi->dwDriver);
+    return MMSYSERR_NOERROR;
+}
+
+/***********************************************************************
+ *           ADPCM_round
+ *
+ */
+static inline DWORD    ADPCM_round(DWORD a, DWORD b, DWORD c)
+{
+    assert(a && b && c);
+    /* to be sure, always return an entire number of c... */
+    return ((double)a * (double)b + (double)c - 1) / (double)c;
+}
+
+/***********************************************************************
+ *           ADPCM_StreamSize
+ *
+ */
+static LRESULT ADPCM_StreamSize(PACMDRVSTREAMINSTANCE adsi, PACMDRVSTREAMSIZE adss)
+{
+    switch (adss->fdwSize)
+    {
+    case ACM_STREAMSIZEF_DESTINATION:
+       /* cbDstLength => cbSrcLength */
+       if (adsi->pwfxSrc->wFormatTag == WAVE_FORMAT_PCM &&
+           adsi->pwfxDst->wFormatTag == WAVE_FORMAT_ADPCM)
+        {
+           /* don't take block overhead into account, doesn't matter too much */
+           adss->cbSrcLength = adss->cbDstLength * 4;
+       }
+        else if (adsi->pwfxSrc->wFormatTag == WAVE_FORMAT_ADPCM &&
+                 adsi->pwfxDst->wFormatTag == WAVE_FORMAT_PCM)
+        {
+           FIXME("misses the block header overhead\n");
+           adss->cbSrcLength = 256 + adss->cbDstLength / 4;
+       }
+        else
+        {
+           return MMSYSERR_NOTSUPPORTED;
+       }
+       break;
+    case ACM_STREAMSIZEF_SOURCE:
+       /* cbSrcLength => cbDstLength */
+       if (adsi->pwfxSrc->wFormatTag == WAVE_FORMAT_PCM &&
+           adsi->pwfxDst->wFormatTag == WAVE_FORMAT_ADPCM)
+        {
+           FIXME("misses the block header overhead\n");
+           adss->cbDstLength = 256 + adss->cbSrcLength / 4;
+       }
+        else if (adsi->pwfxSrc->wFormatTag == WAVE_FORMAT_ADPCM &&
+                 adsi->pwfxDst->wFormatTag == WAVE_FORMAT_PCM)
+        {
+           /* don't take block overhead into account, doesn't matter too much */
+           adss->cbDstLength = adss->cbSrcLength * 4;
+       }
+        else
+        {
+           return MMSYSERR_NOTSUPPORTED;
+       }
+       break;
+    default:
+       WARN("Unsupported query %08lx\n", adss->fdwSize);
+       return MMSYSERR_NOTSUPPORTED;
+    }
+    return MMSYSERR_NOERROR;
+}
+
+/***********************************************************************
+ *           ADPCM_StreamConvert
+ *
+ */
+static LRESULT ADPCM_StreamConvert(PACMDRVSTREAMINSTANCE adsi, PACMDRVSTREAMHEADER adsh)
+{
+    AcmAdpcmData*      aad = (AcmAdpcmData*)adsi->dwDriver;
+    DWORD              nsrc = adsh->cbSrcLength;
+    DWORD              ndst = adsh->cbDstLength;
+
+    if (adsh->fdwConvert &
+       ~(ACM_STREAMCONVERTF_BLOCKALIGN|
+         ACM_STREAMCONVERTF_END|
+         ACM_STREAMCONVERTF_START))
+    {
+       FIXME("Unsupported fdwConvert (%08lx), ignoring it\n", adsh->fdwConvert);
+    }
+    /* ACM_STREAMCONVERTF_BLOCKALIGN
+     * currently all conversions are block aligned, so do nothing for this flag
+     * ACM_STREAMCONVERTF_END
+     * no pending data, so do nothing for this flag
+     */
+    if ((adsh->fdwConvert & ACM_STREAMCONVERTF_START))
+    {
+       ADPCM_Reset(adsi, aad);
+    }
+
+    aad->convert(adsi, adsh->pbSrc, &nsrc, adsh->pbDst, &ndst);
+    adsh->cbSrcLengthUsed = nsrc;
+    adsh->cbDstLengthUsed = ndst;
+
+    return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ *                     ADPCM_DriverProc                        [exported]
+ */
+LRESULT CALLBACK       ADPCM_DriverProc(DWORD dwDevID, HDRVR hDriv, UINT wMsg,
+                                        LPARAM dwParam1, LPARAM dwParam2)
+{
+    TRACE("(%08lx %08lx %04x %08lx %08lx);\n",
+         dwDevID, (DWORD)hDriv, wMsg, dwParam1, dwParam2);
+
+    switch (wMsg)
+    {
+    case DRV_LOAD:             return 1;
+    case DRV_FREE:             return 1;
+    case DRV_OPEN:             return ADPCM_drvOpen((LPSTR)dwParam1);
+    case DRV_CLOSE:            return ADPCM_drvClose(dwDevID);
+    case DRV_ENABLE:           return 1;
+    case DRV_DISABLE:          return 1;
+    case DRV_QUERYCONFIGURE:   return 1;
+    case DRV_CONFIGURE:                MessageBoxA(0, "MSACM MS ADPCM filter !", "Wine Driver", MB_OK); return 1;
+    case DRV_INSTALL:          return DRVCNF_RESTART;
+    case DRV_REMOVE:           return DRVCNF_RESTART;
+
+    case ACMDM_DRIVER_NOTIFY:
+       /* no caching from other ACM drivers is done so far */
+       return MMSYSERR_NOERROR;
+
+    case ACMDM_DRIVER_DETAILS:
+       return ADPCM_DriverDetails((PACMDRIVERDETAILSW)dwParam1);
+
+    case ACMDM_FORMATTAG_DETAILS:
+       return ADPCM_FormatTagDetails((PACMFORMATTAGDETAILSW)dwParam1, dwParam2);
+
+    case ACMDM_FORMAT_DETAILS:
+       return ADPCM_FormatDetails((PACMFORMATDETAILSW)dwParam1, dwParam2);
+
+    case ACMDM_FORMAT_SUGGEST:
+       return ADPCM_FormatSuggest((PACMDRVFORMATSUGGEST)dwParam1);
+
+    case ACMDM_STREAM_OPEN:
+       return ADPCM_StreamOpen((PACMDRVSTREAMINSTANCE)dwParam1);
+
+    case ACMDM_STREAM_CLOSE:
+       return ADPCM_StreamClose((PACMDRVSTREAMINSTANCE)dwParam1);
+
+    case ACMDM_STREAM_SIZE:
+       return ADPCM_StreamSize((PACMDRVSTREAMINSTANCE)dwParam1, (PACMDRVSTREAMSIZE)dwParam2);
+
+    case ACMDM_STREAM_CONVERT:
+       return ADPCM_StreamConvert((PACMDRVSTREAMINSTANCE)dwParam1, (PACMDRVSTREAMHEADER)dwParam2);
+
+    case ACMDM_HARDWARE_WAVE_CAPS_INPUT:
+    case ACMDM_HARDWARE_WAVE_CAPS_OUTPUT:
+       /* this converter is not a hardware driver */
+    case ACMDM_FILTERTAG_DETAILS:
+    case ACMDM_FILTER_DETAILS:
+       /* this converter is not a filter */
+    case ACMDM_STREAM_RESET:
+       /* only needed for asynchronous driver... we aren't, so just say it */
+       return MMSYSERR_NOTSUPPORTED;
+    case ACMDM_STREAM_PREPARE:
+    case ACMDM_STREAM_UNPREPARE:
+       /* nothing special to do here... so don't do anything */
+       return MMSYSERR_NOERROR;
+
+    default:
+       return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
+    }
+    return 0;
+}
diff --git a/reactos/dll/msacm/pcmconverter.c b/reactos/dll/msacm/pcmconverter.c
new file mode 100644 (file)
index 0000000..3f335d0
--- /dev/null
@@ -0,0 +1,1223 @@
+/* -*- tab-width: 8; c-basic-offset: 4 -*- */
+
+/*
+ *      MSACM32 library
+ *
+ *      Copyright 2000         Eric Pouech
+ *
+ * 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
+ *
+ *     FIXME / TODO list
+ *     + most of the computation should be done in fixed point arithmetic
+ *       instead of floating point (16 bits for integral part, and 16 bits
+ *       for fractional part for example)
+ *     + implement PCM_FormatSuggest function
+ *     + get rid of hack for PCM_DriverProc (msacm32.dll shouldn't export
+ *       a DriverProc, but this would require implementing a generic
+ *       embedded driver handling scheme in msacm32.dll which isn't done yet
+ */
+
+#include "config.h"
+
+#include <assert.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "mmsystem.h"
+#include "mmreg.h"
+#include "msacm.h"
+#include "wingdi.h"
+#include "winnls.h"
+#include "winuser.h"
+
+#include "msacmdrv.h"
+#include "wineacm.h"
+
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msacm);
+
+/***********************************************************************
+ *           PCM_drvOpen
+ */
+static DWORD   PCM_drvOpen(LPCSTR str, PACMDRVOPENDESCW adod)
+{
+    TRACE("(%p, %p)\n", str, adod);
+
+    return (adod == NULL) ||
+       (adod->fccType == ACMDRIVERDETAILS_FCCTYPE_AUDIOCODEC &&
+        adod->fccComp == ACMDRIVERDETAILS_FCCCOMP_UNDEFINED);
+}
+
+/***********************************************************************
+ *           PCM_drvClose
+ */
+static DWORD   PCM_drvClose(DWORD dwDevID)
+{
+    TRACE("(%ld)\n", dwDevID);
+
+    return 1;
+}
+
+#define        NUM_PCM_FORMATS (sizeof(PCM_Formats) / sizeof(PCM_Formats[0]))
+#define        NUM_OF(a,b)     (((a)+(b)-1)/(b))
+
+/* flags for fdwDriver */
+#define PCM_RESAMPLE   1
+
+/* data used while converting */
+typedef struct tagAcmPcmData {
+    /* conversion routine, depending if rate conversion is required */
+    union {
+       void (*cvtKeepRate)(const unsigned char*, int, unsigned char*);
+       void (*cvtChangeRate)(struct tagAcmPcmData*, const unsigned char*,
+                             LPDWORD, unsigned char*, LPDWORD);
+    } cvt;
+    /* the following fields are used only with rate conversion) */
+    DWORD      srcPos;         /* position in source stream */
+    double     dstPos;         /* position in destination stream */
+    double     dstIncr;        /* value to increment dst stream when src stream
+                                  is incremented by 1 */
+    /* last source stream value read */
+    union {
+       unsigned char   b;      /*  8 bit value */
+       short           s;      /* 16 bit value */
+    } last[2]; /* two channels max (stereo) */
+} AcmPcmData;
+
+/* table to list all supported formats... those are the basic ones. this
+ * also helps given a unique index to each of the supported formats
+ */
+static struct {
+    int                nChannels;
+    int                nBits;
+    int                rate;
+} PCM_Formats[] = {
+    {1,  8,  8000}, {2,  8,  8000}, {1, 16,  8000}, {2, 16,  8000},
+    {1,  8, 11025}, {2,  8, 11025}, {1, 16, 11025}, {2, 16, 11025},
+    {1,  8, 22050}, {2,  8, 22050}, {1, 16, 22050}, {2, 16, 22050},
+    {1,  8, 44100}, {2,  8, 44100}, {1, 16, 44100}, {2, 16, 44100},
+    {1,  8, 48000}, {2,  8, 48000}, {1, 16, 48000}, {2, 16, 48000},
+    {1,  8, 96000}, {2,  8, 96000}, {1, 16, 96000}, {2, 16, 96000}
+};
+
+/***********************************************************************
+ *           PCM_GetFormatIndex
+ */
+static DWORD PCM_GetFormatIndex(LPWAVEFORMATEX wfx)
+{
+    int i;
+    TRACE("(%p)\n", wfx);
+
+    for (i = 0; i < NUM_PCM_FORMATS; i++) {
+       if (wfx->nChannels == PCM_Formats[i].nChannels &&
+           wfx->nSamplesPerSec == PCM_Formats[i].rate &&
+           wfx->wBitsPerSample == PCM_Formats[i].nBits)
+           return i;
+    }
+    return 0xFFFFFFFF;
+}
+
+/* PCM Conversions:
+ *
+ * parameters:
+ *     + 8 bit unsigned vs 16 bit signed
+ *     + mono vs stereo (1 or 2 channels)
+ *     + sampling rate (8.0, 11.025, 22.05, 44.1 kHz are defined, but algo
+ *       shall work in all cases)
+ *
+ * mono => stereo: copy the same sample on Left & Right channels
+ * stereo =) mono: use the average value of samples from Left & Right channels
+ * resampling; we lookup for each destination sample the two source adjacent
+ *      samples were src <= dst < src+1 (dst is increased by a fractional
+ *      value which is equivalent to the increment by one on src); then we
+ *      use a linear interpolation between src and src+1
+ */
+
+/***********************************************************************
+ *           C816
+ *
+ * Converts a 8 bit sample to a 16 bit one
+ */
+static inline short C816(unsigned char b)
+{
+    return (short)((b+(b << 8))-32768);
+}
+
+/***********************************************************************
+ *           C168
+ *
+ * Converts a 16 bit sample to a 8 bit one (data loss !!)
+ */
+static inline unsigned char C168(short s)
+{
+    return HIBYTE(s) ^ (unsigned char)0x80;
+}
+
+/***********************************************************************
+ *           R16
+ *
+ * Read a 16 bit sample (correctly handles endianess)
+ */
+static inline short  R16(const unsigned char* src)
+{
+    return (short)((unsigned short)src[0] | ((unsigned short)src[1] << 8));
+}
+
+/***********************************************************************
+ *           W16
+ *
+ * Write a 16 bit sample (correctly handles endianess)
+ */
+static inline void  W16(unsigned char* dst, short s)
+{
+    dst[0] = LOBYTE(s);
+    dst[1] = HIBYTE(s);
+}
+
+/***********************************************************************
+ *           M16
+ *
+ * Convert the (l,r) 16 bit stereo sample into a 16 bit mono
+ * (takes the mid-point of the two values)
+ */
+static inline short M16(short l, short r)
+{
+    return (l + r) / 2;
+}
+
+/***********************************************************************
+ *           M8
+ *
+ * Convert the (l,r) 8 bit stereo sample into a 8 bit mono
+ * (takes the mid-point of the two values)
+ */
+static inline unsigned char M8(unsigned char a, unsigned char b)
+{
+    return (unsigned char)((a + b) / 2);
+}
+
+/* the conversion routines without rate conversion are labelled cvt<X><Y><N><M>K
+ * where :
+ * <X> is the (M)ono/(S)tereo configuration of  input channel
+ * <Y> is the (M)ono/(S)tereo configuration of output channel
+ * <N> is the number of bits of  input channel (8 or 16)
+ * <M> is the number of bits of output channel (8 or 16)
+ *
+ * in the parameters, ns is always the number of samples, so the size of input
+ * buffer (resp output buffer) is ns * (<X> == 'Mono' ? 1:2) * (<N> == 8 ? 1:2)
+ */
+
+static void cvtMM88K(const unsigned char* src, int ns, unsigned char* dst)
+{
+    TRACE("(%p, %d, %p)\n", src, ns, dst);
+    memcpy(dst, src, ns);
+}
+
+static void cvtSS88K(const unsigned char* src, int ns, unsigned char* dst)
+{
+    TRACE("(%p, %d, %p)\n", src, ns, dst);
+    memcpy(dst, src, ns * 2);
+}
+
+static void cvtMM1616K(const unsigned char* src, int ns, unsigned char* dst)
+{
+    TRACE("(%p, %d, %p)\n", src, ns, dst);
+    memcpy(dst, src, ns * 2);
+}
+
+static void cvtSS1616K(const unsigned char* src, int ns, unsigned char* dst)
+{
+    TRACE("(%p, %d, %p)\n", src, ns, dst);
+    memcpy(dst, src, ns * 4);
+}
+
+static void cvtMS88K(const unsigned char* src, int ns, unsigned char* dst)
+{
+    TRACE("(%p, %d, %p)\n", src, ns, dst);
+
+    while (ns--) {
+       *dst++ = *src;
+       *dst++ = *src++;
+    }
+}
+
+static void cvtMS816K(const unsigned char* src, int ns, unsigned char* dst)
+{
+    short      v;
+    TRACE("(%p, %d, %p)\n", src, ns, dst);
+
+    while (ns--) {
+       v = C816(*src++);
+       W16(dst, v);            dst += 2;
+       W16(dst, v);            dst += 2;
+    }
+}
+
+static void cvtMS168K(const unsigned char* src, int ns, unsigned char* dst)
+{
+    unsigned char v;
+    TRACE("(%p, %d, %p)\n", src, ns, dst);
+
+    while (ns--) {
+       v = C168(R16(src));             src += 2;
+       *dst++ = v;
+       *dst++ = v;
+    }
+}
+
+static void cvtMS1616K(const unsigned char* src, int ns, unsigned char* dst)
+{
+    short      v;
+    TRACE("(%p, %d, %p)\n", src, ns, dst);
+
+    while (ns--) {
+       v = R16(src);           src += 2;
+       W16(dst, v);            dst += 2;
+       W16(dst, v);            dst += 2;
+    }
+}
+
+static void cvtSM88K(const unsigned char* src, int ns, unsigned char* dst)
+{
+    TRACE("(%p, %d, %p)\n", src, ns, dst);
+
+    while (ns--) {
+       *dst++ = M8(src[0], src[1]);
+       src += 2;
+    }
+}
+
+static void cvtSM816K(const unsigned char* src, int ns, unsigned char* dst)
+{
+    short      v;
+    TRACE("(%p, %d, %p)\n", src, ns, dst);
+
+    while (ns--) {
+       v = M16(C816(src[0]), C816(src[1]));
+       src += 2;
+       W16(dst, v);            dst += 2;
+    }
+}
+
+static void cvtSM168K(const unsigned char* src, int ns, unsigned char* dst)
+{
+    TRACE("(%p, %d, %p)\n", src, ns, dst);
+
+    while (ns--) {
+       *dst++ = C168(M16(R16(src), R16(src + 2)));
+       src += 4;
+    }
+}
+
+static void cvtSM1616K(const unsigned char* src, int ns, unsigned char* dst)
+{
+    TRACE("(%p, %d, %p)\n", src, ns, dst);
+
+    while (ns--) {
+       W16(dst, M16(R16(src),R16(src+2)));     dst += 2;
+       src += 4;
+    }
+}
+
+static void cvtMM816K(const unsigned char* src, int ns, unsigned char* dst)
+{
+    TRACE("(%p, %d, %p)\n", src, ns, dst);
+
+    while (ns--) {
+       W16(dst, C816(*src++));         dst += 2;
+    }
+}
+
+static void cvtSS816K(const unsigned char* src, int ns, unsigned char* dst)
+{
+    TRACE("(%p, %d, %p)\n", src, ns, dst);
+
+    while (ns--) {
+       W16(dst, C816(*src++)); dst += 2;
+       W16(dst, C816(*src++)); dst += 2;
+    }
+}
+
+static void cvtMM168K(const unsigned char* src, int ns, unsigned char* dst)
+{
+    TRACE("(%p, %d, %p)\n", src, ns, dst);
+
+    while (ns--) {
+       *dst++ = C168(R16(src));        src += 2;
+    }
+}
+
+static void cvtSS168K(const unsigned char* src, int ns, unsigned char* dst)
+{
+    TRACE("(%p, %d, %p)\n", src, ns, dst);
+
+    while (ns--) {
+       *dst++ = C168(R16(src));        src += 2;
+       *dst++ = C168(R16(src));        src += 2;
+    }
+}
+
+static void (*PCM_ConvertKeepRate[16])(const unsigned char*, int, unsigned char*) = {
+    cvtSS88K,  cvtSM88K,   cvtMS88K,   cvtMM88K,
+    cvtSS816K, cvtSM816K,  cvtMS816K,  cvtMM816K,
+    cvtSS168K, cvtSM168K,  cvtMS168K,  cvtMM168K,
+    cvtSS1616K, cvtSM1616K, cvtMS1616K, cvtMM1616K,
+};
+
+/***********************************************************************
+ *           I
+ *
+ * Interpolate the value at r (r in ]0, 1]) between the two points v1 and v2
+ * Linear interpolation is used
+ */
+static inline double   I(double v1, double v2, double r)
+{
+    if (0.0 >= r || r > 1.0) FIXME("r!! %f\n", r);
+    return (1.0 - r) * v1 + r * v2;
+}
+
+static void cvtSS88C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
+                     unsigned char* dst, LPDWORD ndst)
+{
+    double                     r;
+    TRACE("(%p, %p, %p, %p, %p)\n", apd, src, nsrc, dst, ndst);
+
+    while (*nsrc != 0 && *ndst != 0) {
+       while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
+           if (*nsrc == 0) return;
+           apd->last[0].b = *src++;
+           apd->last[1].b = *src++;
+           apd->srcPos++;
+           (*nsrc)--;
+       }
+       /* now do the interpolation */
+       *dst++ = I(apd->last[0].b, src[0], r);
+       *dst++ = I(apd->last[1].b, src[1], r);
+       apd->dstPos += apd->dstIncr;
+       (*ndst)--;
+    }
+}
+
+/* the conversion routines with rate conversion are labelled cvt<X><Y><N><M>C
+ * where :
+ * <X> is the (M)ono/(S)tereo configuration of  input channel
+ * <Y> is the (M)ono/(S)tereo configuration of output channel
+ * <N> is the number of bits of  input channel (8 or 16)
+ * <M> is the number of bits of output channel (8 or 16)
+ *
+ */
+static void cvtSM88C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
+                     unsigned char* dst, LPDWORD ndst)
+{
+    double     r;
+    TRACE("(%p, %p, %p, %p, %p)\n", apd, src, nsrc, dst, ndst);
+
+    while (*nsrc != 0 && *ndst != 0) {
+       while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
+           if (*nsrc == 0) return;
+           apd->last[0].b = *src++;
+           apd->last[1].b = *src++;
+           apd->srcPos++;
+           (*nsrc)--;
+       }
+       /* now do the interpolation */
+        if (*nsrc)     /* don't go off end of data */
+            *dst++ = I(M8(apd->last[0].b, apd->last[1].b), M8(src[0], src[1]), r);
+        else
+            *dst++ = M8(apd->last[0].b, apd->last[1].b);
+       apd->dstPos += apd->dstIncr;
+       (*ndst)--;
+    }
+}
+
+static void cvtMS88C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
+                     unsigned char* dst, LPDWORD ndst)
+{
+    double     r;
+    TRACE("(%p, %p, %p, %p, %p)\n", apd, src, nsrc, dst, ndst);
+
+    while (*nsrc != 0 && *ndst != 0) {
+       while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
+           if (*nsrc == 0) return;
+           apd->last[0].b = *src++;
+           apd->srcPos++;
+           (*nsrc)--;
+       }
+       /* now do the interpolation */
+        if (*nsrc)     /* don't go off end of data */
+            dst[0] = dst[1] = I(apd->last[0].b, src[0], r);
+        else
+            dst[0] = dst[1] = apd->last[0].b;
+       dst += 2;
+       apd->dstPos += apd->dstIncr;
+       (*ndst)--;
+    }
+}
+
+static void cvtMM88C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
+                     unsigned char* dst, LPDWORD ndst)
+{
+    double             r;
+    TRACE("(%p, %p, %p, %p, %p)\n", apd, src, nsrc, dst, ndst);
+
+    while (*nsrc != 0 && *ndst != 0) {
+       while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
+           if (*nsrc == 0) return;
+           apd->last[0].b = *src++;
+           apd->srcPos++;
+           (*nsrc)--;
+       }
+       /* now do the interpolation */
+        if (*nsrc)     /* don't go off end of data */
+            *dst++ = I(apd->last[0].b, src[0], r);
+        else
+            *dst++ = apd->last[0].b;
+       apd->dstPos += apd->dstIncr;
+       (*ndst)--;
+    }
+}
+
+static void cvtSS816C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
+                      unsigned char* dst, LPDWORD ndst)
+{
+    double     r;
+    TRACE("(%p, %p, %p, %p, %p)\n", apd, src, nsrc, dst, ndst);
+
+    while (*nsrc != 0 && *ndst != 0) {
+       while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
+           if (*nsrc == 0) return;
+           apd->last[0].b = *src++;
+           apd->last[1].b = *src++;
+           apd->srcPos++;
+           (*nsrc)--;
+       }
+       /* now do the interpolation */
+        if (*nsrc)     /* don't go off end of data */
+            W16(dst, I(C816(apd->last[0].b), C816(src[0]), r));
+        else
+            W16(dst, C816(apd->last[0].b));
+        dst += 2;
+        if (*nsrc)     /* don't go off end of data */
+           W16(dst, I(C816(apd->last[1].b), C816(src[1]), r));
+        else
+           W16(dst, C816(apd->last[1].b));
+        dst += 2;
+       apd->dstPos += apd->dstIncr;
+       (*ndst)--;
+    }
+}
+
+static void cvtSM816C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
+                       unsigned char* dst, LPDWORD ndst)
+{
+    double             r;
+    TRACE("(%p, %p, %p, %p, %p)\n", apd, src, nsrc, dst, ndst);
+
+    while (*nsrc != 0 && *ndst != 0) {
+       while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
+           if (*nsrc == 0) return;
+           apd->last[0].b = *src++;
+           apd->last[1].b = *src++;
+           apd->srcPos++;
+           (*nsrc)--;
+       }
+       /* now do the interpolation */
+        if (*nsrc)     /* don't go off end of data */
+            W16(dst, I(M16(C816(apd->last[0].b), C816(apd->last[1].b)),
+                       M16(C816(src[0]), C816(src[1])), r));
+        else
+            W16(dst, M16(C816(apd->last[0].b), C816(apd->last[1].b)));
+       dst += 2;
+       apd->dstPos += apd->dstIncr;
+       (*ndst)--;
+    }
+}
+
+static void cvtMS816C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
+                       unsigned char* dst, LPDWORD ndst)
+{
+    double             r;
+    short      v;
+    TRACE("(%p, %p, %p->(%ld), %p, %p->(%ld))\n", apd, src, nsrc, *nsrc, dst, ndst, *ndst);
+
+    while (*nsrc != 0 && *ndst != 0) {
+       while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
+           if (*nsrc == 0) return;
+           apd->last[0].b = *src++;
+           apd->srcPos++;
+           (*nsrc)--;
+       }
+       /* now do the interpolation */
+        if (*nsrc)     /* don't go off end of data */
+           v = I(C816(apd->last[0].b), C816(src[0]), r);
+        else
+            v = C816(apd->last[0].b);
+       W16(dst, v);            dst += 2;
+       W16(dst, v);            dst += 2;
+       apd->dstPos += apd->dstIncr;
+       (*ndst)--;
+    }
+}
+
+static void cvtMM816C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
+                       unsigned char* dst, LPDWORD ndst)
+{
+    double             r;
+    TRACE("(%p, %p, %p, %p, %p)\n", apd, src, nsrc, dst, ndst);
+
+    while (*nsrc != 0 && *ndst != 0) {
+       while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
+           if (*nsrc == 0) return;
+           apd->last[0].b = *src++;
+           apd->srcPos++;
+           (*nsrc)--;
+       }
+       /* now do the interpolation */
+        if (*nsrc)     /* don't go off end of data */
+           W16(dst, I(C816(apd->last[0].b), C816(src[0]), r));
+        else
+            W16(dst, C816(apd->last[0].b));
+       dst += 2;
+       apd->dstPos += apd->dstIncr;
+       (*ndst)--;
+    }
+}
+
+static void cvtSS168C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
+                       unsigned char* dst, LPDWORD ndst)
+{
+    double             r;
+    TRACE("(%p, %p, %p, %p, %p)\n", apd, src, nsrc, dst, ndst);
+
+    while (*nsrc != 0 && *ndst != 0) {
+       while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
+           if (*nsrc == 0) return;
+           apd->last[0].s = R16(src);  src += 2;
+           apd->last[1].s = R16(src);  src += 2;
+           apd->srcPos++;
+           (*nsrc)--;
+       }
+       /* now do the interpolation */
+        if (*nsrc) {   /* don't go off end of data */
+           *dst++ = C168(I(apd->last[0].s, R16(src)  , r));
+           *dst++ = C168(I(apd->last[1].s, R16(src+2), r));
+        } else {
+           *dst++ = C168(apd->last[0].s);
+           *dst++ = C168(apd->last[1].s);
+        }
+       apd->dstPos += apd->dstIncr;
+       (*ndst)--;
+    }
+}
+
+static void cvtSM168C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
+                       unsigned char* dst, LPDWORD ndst)
+{
+    double             r;
+    TRACE("(%p, %p, %p, %p, %p)\n", apd, src, nsrc, dst, ndst);
+
+    while (*nsrc != 0 && *ndst != 0) {
+       while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
+           if (*nsrc == 0) return;
+           apd->last[0].s = R16(src);  src += 2;
+           apd->last[1].s = R16(src);  src += 2;
+           apd->srcPos++;
+           (*nsrc)--;
+       }
+       /* now do the interpolation */
+        if (*nsrc)     /* don't go off end of data */
+           *dst++ = C168(I(M16(apd->last[0].s, apd->last[1].s),
+                           M16(R16(src), R16(src + 2)), r));
+        else
+           *dst++ = C168(M16(apd->last[0].s, apd->last[1].s));
+       apd->dstPos += apd->dstIncr;
+       (*ndst)--;
+    }
+}
+
+
+static void cvtMS168C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
+                       unsigned char* dst, LPDWORD ndst)
+{
+    double             r;
+    TRACE("(%p, %p, %p, %p, %p)\n", apd, src, nsrc, dst, ndst);
+
+    while (*nsrc != 0 && *ndst != 0) {
+       while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
+           if (*nsrc == 0) return;
+           apd->last[0].s = R16(src);  src += 2;
+           apd->srcPos++;
+           (*nsrc)--;
+       }
+       /* now do the interpolation */
+        if (*nsrc)     /* don't go off end of data */
+           dst[0] = dst[1] = C168(I(apd->last[0].s, R16(src), r));
+        else
+           dst[0] = dst[1] = C168(apd->last[0].s);
+        dst += 2;
+       apd->dstPos += apd->dstIncr;
+       (*ndst)--;
+    }
+}
+
+
+static void cvtMM168C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
+                       unsigned char* dst, LPDWORD ndst)
+{
+    double             r;
+    TRACE("(%p, %p, %p, %p, %p)\n", apd, src, nsrc, dst, ndst);
+
+    while (*nsrc != 0 && *ndst != 0) {
+       while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
+           if (*nsrc == 0) return;
+           apd->last[0].s = R16(src);  src += 2;
+           apd->srcPos++;
+           (*nsrc)--;
+       }
+       /* now do the interpolation */
+        if (*nsrc)     /* don't go off end of data */
+           *dst++ = C168(I(apd->last[0].s, R16(src), r));
+        else
+           *dst++ = C168(apd->last[0].s);
+       apd->dstPos += apd->dstIncr;
+       (*ndst)--;
+    }
+}
+
+static void cvtSS1616C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
+                       unsigned char* dst, LPDWORD ndst)
+{
+    double             r;
+    TRACE("(%p, %p, %p, %p, %p)\n", apd, src, nsrc, dst, ndst);
+
+    while (*nsrc != 0 && *ndst != 0) {
+       while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
+           if (*nsrc == 0) return;
+           apd->last[0].s = R16(src);  src += 2;
+           apd->last[1].s = R16(src);  src += 2;
+           apd->srcPos++;
+           (*nsrc)--;
+       }
+       /* now do the interpolation */
+        if (*nsrc)     /* don't go off end of data */
+           W16(dst, I(apd->last[0].s, R16(src), r));
+        else
+           W16(dst, apd->last[0].s);
+        dst += 2;
+        if (*nsrc)     /* don't go off end of data */
+           W16(dst, I(apd->last[1].s, R16(src+2), r));
+        else
+           W16(dst, apd->last[1].s);
+        dst += 2;
+       apd->dstPos += apd->dstIncr;
+       (*ndst)--;
+    }
+}
+
+static void cvtSM1616C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
+                       unsigned char* dst, LPDWORD ndst)
+{
+    double             r;
+    TRACE("(%p, %p, %p, %p, %p)\n", apd, src, nsrc, dst, ndst);
+
+    while (*nsrc != 0 && *ndst != 0) {
+       while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
+           if (*nsrc == 0) return;
+           apd->last[0].s = R16(src);  src += 2;
+           apd->last[1].s = R16(src);  src += 2;
+           apd->srcPos++;
+           (*nsrc)--;
+       }
+       /* now do the interpolation */
+        if (*nsrc)     /* don't go off end of data */
+            W16(dst, I(M16(apd->last[0].s, apd->last[1].s),
+                      M16(R16(src), R16(src+2)), r));
+        else
+            W16(dst, M16(apd->last[0].s, apd->last[1].s));
+       dst += 2;
+       apd->dstPos += apd->dstIncr;
+       (*ndst)--;
+    }
+}
+
+static void cvtMS1616C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
+                       unsigned char* dst, LPDWORD ndst)
+{
+    double             r;
+    short      v;
+    TRACE("(%p, %p, %p, %p, %p)\n", apd, src, nsrc, dst, ndst);
+
+    while (*nsrc != 0 && *ndst != 0) {
+       while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
+           if (*nsrc == 0) return;
+           apd->last[0].s = R16(src);  src += 2;
+           apd->srcPos++;
+           (*nsrc)--;
+       }
+       /* now do the interpolation */
+        if (*nsrc)     /* don't go off end of data */
+           v = I(apd->last[0].s, R16(src), r);
+        else
+           v = apd->last[0].s;
+       W16(dst, v);            dst += 2;
+       W16(dst, v);            dst += 2;
+       apd->dstPos += apd->dstIncr;
+       (*ndst)--;
+    }
+}
+
+static void cvtMM1616C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc,
+                       unsigned char* dst, LPDWORD ndst)
+{
+    double             r;
+    TRACE("(%p, %p, %p, %p, %p)\n", apd, src, nsrc, dst, ndst);
+
+    while (*nsrc != 0 && *ndst != 0) {
+       while ((r = (double)apd->srcPos - apd->dstPos) <= 0) {
+           if (*nsrc == 0) return;
+           apd->last[0].s = R16(src);  src += 2;
+           apd->srcPos++;
+           (*nsrc)--;
+       }
+       /* now do the interpolation */
+        if (*nsrc)     /* don't go off end of data */
+           W16(dst, I(apd->last[0].s, R16(src), r));
+        else
+           W16(dst, apd->last[0].s);
+        dst += 2;
+       apd->dstPos += apd->dstIncr;
+       (*ndst)--;
+    }
+}
+
+static void (*PCM_ConvertChangeRate[16])(AcmPcmData* apd,
+                                         const unsigned char* src, LPDWORD nsrc,
+                                         unsigned char* dst, LPDWORD ndst) = {
+    cvtSS88C,   cvtSM88C,   cvtMS88C,   cvtMM88C,
+    cvtSS816C, cvtSM816C,  cvtMS816C,  cvtMM816C,
+    cvtSS168C, cvtSM168C,  cvtMS168C,  cvtMM168C,
+    cvtSS1616C, cvtSM1616C, cvtMS1616C, cvtMM1616C,
+};
+
+/***********************************************************************
+ *           PCM_DriverDetails
+ *
+ */
+static LRESULT PCM_DriverDetails(PACMDRIVERDETAILSW add)
+{
+    TRACE("(%p)\n", add);
+
+    add->fccType = ACMDRIVERDETAILS_FCCTYPE_AUDIOCODEC;
+    add->fccComp = ACMDRIVERDETAILS_FCCCOMP_UNDEFINED;
+    add->wMid = 0xFF;
+    add->wPid = 0x00;
+    add->vdwACM = 0x01000000;
+    add->vdwDriver = 0x01000000;
+    add->fdwSupport = ACMDRIVERDETAILS_SUPPORTF_CONVERTER;
+    add->cFormatTags = 1;
+    add->cFilterTags = 0;
+    add->hicon = NULL;
+    MultiByteToWideChar( CP_ACP, 0, "WINE-PCM", -1,
+                         add->szShortName, sizeof(add->szShortName)/sizeof(WCHAR) );
+    MultiByteToWideChar( CP_ACP, 0, "Wine PCM converter", -1,
+                         add->szLongName, sizeof(add->szLongName)/sizeof(WCHAR) );
+    MultiByteToWideChar( CP_ACP, 0, "Brought to you by the Wine team...", -1,
+                         add->szCopyright, sizeof(add->szCopyright)/sizeof(WCHAR) );
+    MultiByteToWideChar( CP_ACP, 0, "Refer to LICENSE file", -1,
+                         add->szLicensing, sizeof(add->szLicensing)/sizeof(WCHAR) );
+    add->szFeatures[0] = 0;
+
+    return MMSYSERR_NOERROR;
+}
+
+/***********************************************************************
+ *           PCM_FormatTagDetails
+ *
+ */
+static LRESULT PCM_FormatTagDetails(PACMFORMATTAGDETAILSW aftd, DWORD dwQuery)
+{
+    TRACE("(%p, %08lx)\n", aftd, dwQuery);
+
+    switch (dwQuery) {
+    case ACM_FORMATTAGDETAILSF_INDEX:
+       if (aftd->dwFormatTagIndex != 0) {
+            WARN("not possible\n");
+            return ACMERR_NOTPOSSIBLE;
+        }
+       break;
+    case ACM_FORMATTAGDETAILSF_FORMATTAG:
+       if (aftd->dwFormatTag != WAVE_FORMAT_PCM) {
+            WARN("not possible\n");
+            return ACMERR_NOTPOSSIBLE;
+        }
+       break;
+    case ACM_FORMATTAGDETAILSF_LARGESTSIZE:
+       if (aftd->dwFormatTag != WAVE_FORMAT_UNKNOWN &&
+           aftd->dwFormatTag != WAVE_FORMAT_PCM) {
+            WARN("not possible\n");
+           return ACMERR_NOTPOSSIBLE;
+        }
+       break;
+    default:
+       WARN("Unsupported query %08lx\n", dwQuery);
+       return MMSYSERR_NOTSUPPORTED;
+    }
+
+    aftd->dwFormatTagIndex = 0;
+    aftd->dwFormatTag = WAVE_FORMAT_PCM;
+    aftd->cbFormatSize = sizeof(PCMWAVEFORMAT);
+    aftd->fdwSupport = ACMDRIVERDETAILS_SUPPORTF_CONVERTER;
+    aftd->cStandardFormats = NUM_PCM_FORMATS;
+    aftd->szFormatTag[0] = 0;
+
+    return MMSYSERR_NOERROR;
+}
+
+/***********************************************************************
+ *           PCM_FormatDetails
+ *
+ */
+static LRESULT PCM_FormatDetails(PACMFORMATDETAILSW afd, DWORD dwQuery)
+{
+    TRACE("(%p, %08lx)\n", afd, dwQuery);
+
+    switch (dwQuery) {
+    case ACM_FORMATDETAILSF_FORMAT:
+       if (PCM_GetFormatIndex(afd->pwfx) == 0xFFFFFFFF) {
+            return ACMERR_NOTPOSSIBLE;
+            WARN("not possible\n");
+        }
+       break;
+    case ACM_FORMATDETAILSF_INDEX:
+       assert(afd->dwFormatIndex < NUM_PCM_FORMATS);
+       afd->pwfx->wFormatTag = WAVE_FORMAT_PCM;
+       afd->pwfx->nChannels = PCM_Formats[afd->dwFormatIndex].nChannels;
+       afd->pwfx->nSamplesPerSec = PCM_Formats[afd->dwFormatIndex].rate;
+       afd->pwfx->wBitsPerSample = PCM_Formats[afd->dwFormatIndex].nBits;
+       /* native MSACM uses a PCMWAVEFORMAT structure, so cbSize is not
+        * accessible afd->pwfx->cbSize = 0;
+        */
+       afd->pwfx->nBlockAlign =
+           (afd->pwfx->nChannels * afd->pwfx->wBitsPerSample) / 8;
+       afd->pwfx->nAvgBytesPerSec =
+           afd->pwfx->nSamplesPerSec * afd->pwfx->nBlockAlign;
+       break;
+    default:
+       WARN("Unsupported query %08lx\n", dwQuery);
+       return MMSYSERR_NOTSUPPORTED;
+    }
+
+    afd->dwFormatTag = WAVE_FORMAT_PCM;
+    afd->fdwSupport = ACMDRIVERDETAILS_SUPPORTF_CONVERTER;
+    afd->szFormat[0] = 0; /* let MSACM format this for us... */
+    afd->cbwfx = sizeof(PCMWAVEFORMAT);
+
+    return MMSYSERR_NOERROR;
+}
+
+/***********************************************************************
+ *           PCM_FormatSuggest
+ *
+ */
+static LRESULT PCM_FormatSuggest(PACMDRVFORMATSUGGEST adfs)
+{
+    TRACE("(%p)\n", adfs);
+
+    /* some tests ... */
+    if (adfs->cbwfxSrc < sizeof(PCMWAVEFORMAT) ||
+       adfs->cbwfxDst < sizeof(PCMWAVEFORMAT) ||
+       PCM_GetFormatIndex(adfs->pwfxSrc) == 0xFFFFFFFF) {
+            WARN("not possible\n");
+            return ACMERR_NOTPOSSIBLE;
+       }
+
+    /* is no suggestion for destination, then copy source value */
+    if (!(adfs->fdwSuggest & ACM_FORMATSUGGESTF_NCHANNELS)) {
+       adfs->pwfxDst->nChannels = adfs->pwfxSrc->nChannels;
+    }
+    if (!(adfs->fdwSuggest & ACM_FORMATSUGGESTF_NSAMPLESPERSEC)) {
+       adfs->pwfxDst->nSamplesPerSec = adfs->pwfxSrc->nSamplesPerSec;
+    }
+    if (!(adfs->fdwSuggest & ACM_FORMATSUGGESTF_WBITSPERSAMPLE)) {
+       adfs->pwfxDst->wBitsPerSample = adfs->pwfxSrc->wBitsPerSample;
+    }
+    if (!(adfs->fdwSuggest & ACM_FORMATSUGGESTF_WFORMATTAG)) {
+       if (adfs->pwfxSrc->wFormatTag != WAVE_FORMAT_PCM) {
+            WARN("not possible\n");
+            return ACMERR_NOTPOSSIBLE;
+        }
+       adfs->pwfxDst->wFormatTag = adfs->pwfxSrc->wFormatTag;
+    }
+    /* check if result is ok */
+    if (PCM_GetFormatIndex(adfs->pwfxDst) == 0xFFFFFFFF) {
+        WARN("not possible\n");
+        return ACMERR_NOTPOSSIBLE;
+    }
+
+    /* recompute other values */
+    adfs->pwfxDst->nBlockAlign = (adfs->pwfxDst->nChannels * adfs->pwfxDst->wBitsPerSample) / 8;
+    adfs->pwfxDst->nAvgBytesPerSec = adfs->pwfxDst->nSamplesPerSec * adfs->pwfxDst->nBlockAlign;
+
+    return MMSYSERR_NOERROR;
+}
+
+/***********************************************************************
+ *           PCM_Reset
+ *
+ */
+static void    PCM_Reset(AcmPcmData* apd, int srcNumBits)
+{
+    TRACE("(%p, %d)\n", apd, srcNumBits);
+
+    apd->srcPos = 0;
+    apd->dstPos = 0;
+    /* initialize with neutral value */
+    if (srcNumBits == 16) {
+       apd->last[0].s = 0;
+       apd->last[1].s = 0;
+    } else {
+       apd->last[0].b = (BYTE)0x80;
+       apd->last[1].b = (BYTE)0x80;
+    }
+}
+
+/***********************************************************************
+ *           PCM_StreamOpen
+ *
+ */
+static LRESULT PCM_StreamOpen(PACMDRVSTREAMINSTANCE adsi)
+{
+    AcmPcmData*        apd;
+    int                idx = 0;
+
+    TRACE("(%p)\n", adsi);
+
+    assert(!(adsi->fdwOpen & ACM_STREAMOPENF_ASYNC));
+
+    if (PCM_GetFormatIndex(adsi->pwfxSrc) == 0xFFFFFFFF ||
+       PCM_GetFormatIndex(adsi->pwfxDst) == 0xFFFFFFFF) {
+        WARN("not possible\n");
+       return ACMERR_NOTPOSSIBLE;
+    }
+
+    apd = HeapAlloc(GetProcessHeap(), 0, sizeof(AcmPcmData));
+    if (apd == 0) {
+        WARN("no memory\n");
+        return MMSYSERR_NOMEM;
+    }
+
+    adsi->dwDriver = (DWORD)apd;
+    adsi->fdwDriver = 0;
+
+    if (adsi->pwfxSrc->wBitsPerSample == 16) idx += 8;
+    if (adsi->pwfxDst->wBitsPerSample == 16) idx += 4;
+    if (adsi->pwfxSrc->nChannels      == 1)  idx += 2;
+    if (adsi->pwfxDst->nChannels      == 1)  idx += 1;
+
+    if (adsi->pwfxSrc->nSamplesPerSec == adsi->pwfxDst->nSamplesPerSec) {
+       apd->cvt.cvtKeepRate = PCM_ConvertKeepRate[idx];
+    } else {
+       adsi->fdwDriver |= PCM_RESAMPLE;
+       apd->dstIncr = (double)(adsi->pwfxSrc->nSamplesPerSec) /
+           (double)(adsi->pwfxDst->nSamplesPerSec);
+       PCM_Reset(apd, adsi->pwfxSrc->wBitsPerSample);
+       apd->cvt.cvtChangeRate = PCM_ConvertChangeRate[idx];
+    }
+
+    return MMSYSERR_NOERROR;
+}
+
+/***********************************************************************
+ *           PCM_StreamClose
+ *
+ */
+static LRESULT PCM_StreamClose(PACMDRVSTREAMINSTANCE adsi)
+{
+    TRACE("(%p)\n", adsi);
+
+    HeapFree(GetProcessHeap(), 0, (void*)adsi->dwDriver);
+    return MMSYSERR_NOERROR;
+}
+
+/***********************************************************************
+ *           PCM_round
+ *
+ */
+static inline DWORD    PCM_round(DWORD a, DWORD b, DWORD c)
+{
+    assert(c);
+    /* to be sure, always return an entire number of c... */
+    return ((double)a * (double)b + (double)c - 1) / (double)c;
+}
+
+/***********************************************************************
+ *           PCM_StreamSize
+ *
+ */
+static LRESULT PCM_StreamSize(PACMDRVSTREAMINSTANCE adsi, PACMDRVSTREAMSIZE adss)
+{
+    DWORD      srcMask = ~(adsi->pwfxSrc->nBlockAlign - 1);
+    DWORD      dstMask = ~(adsi->pwfxDst->nBlockAlign - 1);
+
+    TRACE("(%p, %p)\n", adsi, adss);
+
+    switch (adss->fdwSize) {
+    case ACM_STREAMSIZEF_DESTINATION:
+       /* cbDstLength => cbSrcLength */
+       adss->cbSrcLength = PCM_round(adss->cbDstLength & dstMask,
+                                     adsi->pwfxSrc->nAvgBytesPerSec,
+                                     adsi->pwfxDst->nAvgBytesPerSec) & srcMask;
+       break;
+    case ACM_STREAMSIZEF_SOURCE:
+       /* cbSrcLength => cbDstLength */
+       adss->cbDstLength =  PCM_round(adss->cbSrcLength & srcMask,
+                                      adsi->pwfxDst->nAvgBytesPerSec,
+                                      adsi->pwfxSrc->nAvgBytesPerSec) & dstMask;
+       break;
+    default:
+       WARN("Unsupported query %08lx\n", adss->fdwSize);
+       return MMSYSERR_NOTSUPPORTED;
+    }
+    return MMSYSERR_NOERROR;
+}
+
+/***********************************************************************
+ *           PCM_StreamConvert
+ *
+ */
+static LRESULT PCM_StreamConvert(PACMDRVSTREAMINSTANCE adsi, PACMDRVSTREAMHEADER adsh)
+{
+    AcmPcmData*        apd = (AcmPcmData*)adsi->dwDriver;
+    DWORD      nsrc = NUM_OF(adsh->cbSrcLength, adsi->pwfxSrc->nBlockAlign);
+    DWORD      ndst = NUM_OF(adsh->cbDstLength, adsi->pwfxDst->nBlockAlign);
+
+    TRACE("(%p, %p)\n", adsi, adsh);
+
+    TRACE("nsrc=%ld,adsh->cbSrcLength=%ld\n", nsrc, adsh->cbSrcLength);
+    TRACE("ndst=%ld,adsh->cbDstLength=%ld\n", ndst, adsh->cbDstLength);
+    TRACE("src [wFormatTag=%u, nChannels=%u, nSamplesPerSec=%lu, nAvgBytesPerSec=%lu, nBlockAlign=%u, wBitsPerSample=%u, cbSize=%u]\n",
+          adsi->pwfxSrc->wFormatTag, adsi->pwfxSrc->nChannels, adsi->pwfxSrc->nSamplesPerSec, adsi->pwfxSrc->nAvgBytesPerSec,
+          adsi->pwfxSrc->nBlockAlign, adsi->pwfxSrc->wBitsPerSample, adsi->pwfxSrc->cbSize);
+    TRACE("dst [wFormatTag=%u, nChannels=%u, nSamplesPerSec=%lu, nAvgBytesPerSec=%lu, nBlockAlign=%u, wBitsPerSample=%u, cbSize=%u]\n",
+          adsi->pwfxDst->wFormatTag, adsi->pwfxDst->nChannels, adsi->pwfxDst->nSamplesPerSec, adsi->pwfxDst->nAvgBytesPerSec,
+          adsi->pwfxDst->nBlockAlign, adsi->pwfxDst->wBitsPerSample, adsi->pwfxDst->cbSize);
+
+    if (adsh->fdwConvert &
+       ~(ACM_STREAMCONVERTF_BLOCKALIGN|
+         ACM_STREAMCONVERTF_END|
+         ACM_STREAMCONVERTF_START)) {
+       FIXME("Unsupported fdwConvert (%08lx), ignoring it\n", adsh->fdwConvert);
+    }
+    /* ACM_STREAMCONVERTF_BLOCKALIGN
+     * currently all conversions are block aligned, so do nothing for this flag
+     * ACM_STREAMCONVERTF_END
+     * no pending data, so do nothing for this flag
+     */
+    if ((adsh->fdwConvert & ACM_STREAMCONVERTF_START) &&
+       (adsi->fdwDriver & PCM_RESAMPLE)) {
+       PCM_Reset(apd, adsi->pwfxSrc->wBitsPerSample);
+    }
+
+    /* do the job */
+    if (adsi->fdwDriver & PCM_RESAMPLE) {
+       DWORD   nsrc2 = nsrc;
+       DWORD   ndst2 = ndst;
+
+       apd->cvt.cvtChangeRate(apd, adsh->pbSrc, &nsrc2, adsh->pbDst, &ndst2);
+       nsrc -= nsrc2;
+       ndst -= ndst2;
+    } else {
+       if (nsrc < ndst) ndst = nsrc; else nsrc = ndst;
+
+       /* nsrc is now equal to ndst */
+       apd->cvt.cvtKeepRate(adsh->pbSrc, nsrc, adsh->pbDst);
+    }
+
+    adsh->cbSrcLengthUsed = nsrc * adsi->pwfxSrc->nBlockAlign;
+    adsh->cbDstLengthUsed = ndst * adsi->pwfxDst->nBlockAlign;
+
+    return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ *                     DriverProc (MSACM32.@)
+ */
+LRESULT CALLBACK       PCM_DriverProc(DWORD dwDevID, HDRVR hDriv, UINT wMsg,
+                                      LPARAM dwParam1, LPARAM dwParam2)
+{
+    TRACE("(%08lx %08lx %u %08lx %08lx);\n",
+         dwDevID, (DWORD)hDriv, wMsg, dwParam1, dwParam2);
+
+    switch (wMsg) {
+    case DRV_LOAD:             return 1;
+    case DRV_FREE:             return 1;
+    case DRV_OPEN:             return PCM_drvOpen((LPSTR)dwParam1, (PACMDRVOPENDESCW)dwParam2);
+    case DRV_CLOSE:            return PCM_drvClose(dwDevID);
+    case DRV_ENABLE:           return 1;
+    case DRV_DISABLE:          return 1;
+    case DRV_QUERYCONFIGURE:   return 1;
+    case DRV_CONFIGURE:                MessageBoxA(0, "MSACM PCM filter !", "Wine Driver", MB_OK); return 1;
+    case DRV_INSTALL:          return DRVCNF_RESTART;
+    case DRV_REMOVE:           return DRVCNF_RESTART;
+
+    case ACMDM_DRIVER_NOTIFY:
+       /* no caching from other ACM drivers is done so far */
+       return MMSYSERR_NOERROR;
+
+    case ACMDM_DRIVER_DETAILS:
+       return PCM_DriverDetails((PACMDRIVERDETAILSW)dwParam1);
+
+    case ACMDM_FORMATTAG_DETAILS:
+       return PCM_FormatTagDetails((PACMFORMATTAGDETAILSW)dwParam1, dwParam2);
+
+    case ACMDM_FORMAT_DETAILS:
+       return PCM_FormatDetails((PACMFORMATDETAILSW)dwParam1, dwParam2);
+
+    case ACMDM_FORMAT_SUGGEST:
+       return PCM_FormatSuggest((PACMDRVFORMATSUGGEST)dwParam1);
+
+    case ACMDM_STREAM_OPEN:
+       return PCM_StreamOpen((PACMDRVSTREAMINSTANCE)dwParam1);
+
+    case ACMDM_STREAM_CLOSE:
+       return PCM_StreamClose((PACMDRVSTREAMINSTANCE)dwParam1);
+
+    case ACMDM_STREAM_SIZE:
+       return PCM_StreamSize((PACMDRVSTREAMINSTANCE)dwParam1, (PACMDRVSTREAMSIZE)dwParam2);
+
+    case ACMDM_STREAM_CONVERT:
+       return PCM_StreamConvert((PACMDRVSTREAMINSTANCE)dwParam1, (PACMDRVSTREAMHEADER)dwParam2);
+
+    case ACMDM_HARDWARE_WAVE_CAPS_INPUT:
+    case ACMDM_HARDWARE_WAVE_CAPS_OUTPUT:
+       /* this converter is not a hardware driver */
+    case ACMDM_FILTERTAG_DETAILS:
+    case ACMDM_FILTER_DETAILS:
+       /* this converter is not a filter */
+    case ACMDM_STREAM_RESET:
+       /* only needed for asynchronous driver... we aren't, so just say it */
+    case ACMDM_STREAM_PREPARE:
+    case ACMDM_STREAM_UNPREPARE:
+       /* nothing special to do here... so don't do anything */
+       return MMSYSERR_NOTSUPPORTED;
+
+    default:
+       return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
+    }
+    return 0;
+}
diff --git a/reactos/dll/msacm/stream.c b/reactos/dll/msacm/stream.c
new file mode 100644 (file)
index 0000000..5c427ec
--- /dev/null
@@ -0,0 +1,478 @@
+/* -*- tab-width: 8; c-basic-offset: 4 -*- */
+
+/*
+ *      MSACM32 library
+ *
+ *      Copyright 1998  Patrik Stridvall
+ *               1999  Eric Pouech
+ *
+ * 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
+ */
+
+/* TODO
+ *     + asynchronous conversion is not implemented
+ *     + callback/notification
+ *     * acmStreamMessage
+ *     + properly close ACM streams
+ */
+
+#include <stdarg.h>
+#include <string.h>
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "wine/debug.h"
+#include "mmsystem.h"
+#include "mmreg.h"
+#include "msacm.h"
+#include "msacmdrv.h"
+#include "wineacm.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msacm);
+
+static PWINE_ACMSTREAM ACM_GetStream(HACMSTREAM has)
+{
+    TRACE("(%p)\n", has);
+
+    return (PWINE_ACMSTREAM)has;
+}
+
+/***********************************************************************
+ *           acmStreamClose (MSACM32.@)
+ */
+MMRESULT WINAPI acmStreamClose(HACMSTREAM has, DWORD fdwClose)
+{
+    PWINE_ACMSTREAM    was;
+    MMRESULT           ret;
+
+    TRACE("(%p, %ld)\n", has, fdwClose);
+
+    if ((was = ACM_GetStream(has)) == NULL) {
+        WARN("invalid handle\n");
+       return MMSYSERR_INVALHANDLE;
+    }
+    ret = SendDriverMessage(was->pDrv->hDrvr, ACMDM_STREAM_CLOSE, (DWORD)&was->drvInst, 0);
+    if (ret == MMSYSERR_NOERROR) {
+       if (was->hAcmDriver)
+           acmDriverClose(was->hAcmDriver, 0L);
+       HeapFree(MSACM_hHeap, 0, was);
+    }
+    TRACE("=> (%d)\n", ret);
+    return ret;
+}
+
+/***********************************************************************
+ *           acmStreamConvert (MSACM32.@)
+ */
+MMRESULT WINAPI acmStreamConvert(HACMSTREAM has, PACMSTREAMHEADER pash,
+                                DWORD fdwConvert)
+{
+    PWINE_ACMSTREAM    was;
+    MMRESULT           ret = MMSYSERR_NOERROR;
+    PACMDRVSTREAMHEADER        padsh;
+
+    TRACE("(%p, %p, %ld)\n", has, pash, fdwConvert);
+
+    if ((was = ACM_GetStream(has)) == NULL) {
+        WARN("invalid handle\n");
+       return MMSYSERR_INVALHANDLE;
+    }
+    if (!pash || pash->cbStruct < sizeof(ACMSTREAMHEADER)) {
+        WARN("invalid parameter\n");
+       return MMSYSERR_INVALPARAM;
+    }
+    if (!(pash->fdwStatus & ACMSTREAMHEADER_STATUSF_PREPARED)) {
+        WARN("unprepared header\n");
+       return ACMERR_UNPREPARED;
+    }
+
+    /* Note: the ACMSTREAMHEADER and ACMDRVSTREAMHEADER structs are of same
+     * size. some fields are private to msacm internals, and are exposed
+     * in ACMSTREAMHEADER in the dwReservedDriver array
+     */
+    padsh = (PACMDRVSTREAMHEADER)pash;
+
+    /* check that pointers have not been modified */
+    if (padsh->pbPreparedSrc != padsh->pbSrc ||
+       padsh->cbPreparedSrcLength < padsh->cbSrcLength ||
+       padsh->pbPreparedDst != padsh->pbDst ||
+       padsh->cbPreparedDstLength < padsh->cbDstLength) {
+        WARN("invalid parameter\n");
+       return MMSYSERR_INVALPARAM;
+    }
+
+    padsh->fdwConvert = fdwConvert;
+
+    ret = SendDriverMessage(was->pDrv->hDrvr, ACMDM_STREAM_CONVERT, (DWORD)&was->drvInst, (DWORD)padsh);
+    if (ret == MMSYSERR_NOERROR) {
+       padsh->fdwStatus |= ACMSTREAMHEADER_STATUSF_DONE;
+    }
+    TRACE("=> (%d)\n", ret);
+    return ret;
+}
+
+/***********************************************************************
+ *           acmStreamMessage (MSACM32.@)
+ */
+MMRESULT WINAPI acmStreamMessage(HACMSTREAM has, UINT uMsg, LPARAM lParam1,
+                                LPARAM lParam2)
+{
+    FIXME("(%p, %u, %ld, %ld): stub\n", has, uMsg, lParam1, lParam2);
+    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+    return MMSYSERR_ERROR;
+}
+
+/***********************************************************************
+ *           acmStreamOpen (MSACM32.@)
+ */
+MMRESULT WINAPI acmStreamOpen(PHACMSTREAM phas, HACMDRIVER had, PWAVEFORMATEX pwfxSrc,
+                             PWAVEFORMATEX pwfxDst, PWAVEFILTER pwfltr, DWORD dwCallback,
+                             DWORD dwInstance, DWORD fdwOpen)
+{
+    PWINE_ACMSTREAM    was;
+    PWINE_ACMDRIVER    wad;
+    MMRESULT           ret;
+    int                        wfxSrcSize;
+    int                        wfxDstSize;
+    WAVEFORMATEX       wfxSrc, wfxDst;
+
+    TRACE("(%p, %p, %p, %p, %p, %ld, %ld, %ld)\n",
+         phas, had, pwfxSrc, pwfxDst, pwfltr, dwCallback, dwInstance, fdwOpen);
+
+    /* NOTE: pwfxSrc and/or pwfxDst can point to a structure smaller than
+     * WAVEFORMATEX so don't use them directly when not sure */
+    if (pwfxSrc->wFormatTag == WAVE_FORMAT_PCM) {
+        memcpy(&wfxSrc, pwfxSrc, sizeof(PCMWAVEFORMAT));
+        wfxSrc.wBitsPerSample = pwfxSrc->wBitsPerSample;
+        wfxSrc.cbSize = 0;
+        pwfxSrc = &wfxSrc;
+    }
+
+    if (pwfxDst->wFormatTag == WAVE_FORMAT_PCM) {
+        memcpy(&wfxDst, pwfxDst, sizeof(PCMWAVEFORMAT));
+        wfxDst.wBitsPerSample = pwfxDst->wBitsPerSample;
+        wfxDst.cbSize = 0;
+        pwfxDst = &wfxDst;
+    }
+
+    TRACE("src [wFormatTag=%u, nChannels=%u, nSamplesPerSec=%lu, nAvgBytesPerSec=%lu, nBlockAlign=%u, wBitsPerSample=%u, cbSize=%u]\n",
+         pwfxSrc->wFormatTag, pwfxSrc->nChannels, pwfxSrc->nSamplesPerSec, pwfxSrc->nAvgBytesPerSec,
+         pwfxSrc->nBlockAlign, pwfxSrc->wBitsPerSample, pwfxSrc->cbSize);
+
+    TRACE("dst [wFormatTag=%u, nChannels=%u, nSamplesPerSec=%lu, nAvgBytesPerSec=%lu, nBlockAlign=%u, wBitsPerSample=%u, cbSize=%u]\n",
+         pwfxDst->wFormatTag, pwfxDst->nChannels, pwfxDst->nSamplesPerSec, pwfxDst->nAvgBytesPerSec,
+         pwfxDst->nBlockAlign, pwfxDst->wBitsPerSample, pwfxDst->cbSize);
+
+    /* (WS) In query mode, phas should be NULL. If it is not, then instead
+     * of returning an error we are making sure it is NULL, preventing some
+     * applications that pass garbage for phas from crashing.
+     */
+    if (fdwOpen & ACM_STREAMOPENF_QUERY) phas = NULL;
+
+    if (pwfltr && (pwfxSrc->wFormatTag != pwfxDst->wFormatTag)) {
+        WARN("invalid parameter\n");
+        return MMSYSERR_INVALPARAM;
+    }
+
+    wfxSrcSize = wfxDstSize = sizeof(WAVEFORMATEX);
+    if (pwfxSrc->wFormatTag != WAVE_FORMAT_PCM) wfxSrcSize += pwfxSrc->cbSize;
+    if (pwfxDst->wFormatTag != WAVE_FORMAT_PCM) wfxDstSize += pwfxDst->cbSize;
+
+    was = HeapAlloc(MSACM_hHeap, 0, sizeof(*was) + wfxSrcSize + wfxDstSize +
+                   ((pwfltr) ? sizeof(WAVEFILTER) : 0));
+    if (was == NULL) {
+        WARN("no memory\n");
+       return MMSYSERR_NOMEM;
+    }
+
+    was->drvInst.cbStruct = sizeof(was->drvInst);
+    was->drvInst.pwfxSrc = (PWAVEFORMATEX)((LPSTR)was + sizeof(*was));
+    memcpy(was->drvInst.pwfxSrc, pwfxSrc, wfxSrcSize);
+    was->drvInst.pwfxDst = (PWAVEFORMATEX)((LPSTR)was + sizeof(*was) + wfxSrcSize);
+    memcpy(was->drvInst.pwfxDst, pwfxDst, wfxDstSize);
+    if (pwfltr) {
+       was->drvInst.pwfltr = (PWAVEFILTER)((LPSTR)was + sizeof(*was) + wfxSrcSize + wfxDstSize);
+       memcpy(was->drvInst.pwfltr, pwfltr, sizeof(WAVEFILTER));
+    } else {
+       was->drvInst.pwfltr = NULL;
+    }
+    was->drvInst.dwCallback = dwCallback;
+    was->drvInst.dwInstance = dwInstance;
+    was->drvInst.fdwOpen = fdwOpen;
+    was->drvInst.fdwDriver = 0L;
+    was->drvInst.dwDriver = 0L;
+    /* real value will be stored once ACMDM_STREAM_OPEN succeeds */
+    was->drvInst.has = 0L;
+
+    if (had) {
+       if (!(wad = MSACM_GetDriver(had))) {
+           ret = MMSYSERR_INVALPARAM;
+           goto errCleanUp;
+       }
+
+       was->obj.dwType = WINE_ACMOBJ_STREAM;
+       was->obj.pACMDriverID = wad->obj.pACMDriverID;
+       was->pDrv = wad;
+       was->hAcmDriver = 0; /* not to close it in acmStreamClose */
+
+       ret = SendDriverMessage(wad->hDrvr, ACMDM_STREAM_OPEN, (DWORD)&was->drvInst, 0L);
+       if (ret != MMSYSERR_NOERROR)
+           goto errCleanUp;
+    } else {
+       PWINE_ACMDRIVERID wadi;
+
+       ret = ACMERR_NOTPOSSIBLE;
+       for (wadi = MSACM_pFirstACMDriverID; wadi; wadi = wadi->pNextACMDriverID) {
+           if ((wadi->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) ||
+               !MSACM_FindFormatTagInCache(wadi, pwfxSrc->wFormatTag, NULL) ||
+               !MSACM_FindFormatTagInCache(wadi, pwfxDst->wFormatTag, NULL))
+               continue;
+           ret = acmDriverOpen(&had, (HACMDRIVERID)wadi, 0L);
+           if (ret != MMSYSERR_NOERROR)
+               continue;
+           if ((wad = MSACM_GetDriver(had)) != 0) {
+               was->obj.dwType = WINE_ACMOBJ_STREAM;
+               was->obj.pACMDriverID = wad->obj.pACMDriverID;
+               was->pDrv = wad;
+               was->hAcmDriver = had;
+
+               ret = SendDriverMessage(wad->hDrvr, ACMDM_STREAM_OPEN, (DWORD)&was->drvInst, 0L);
+               TRACE("%s => %08x\n", debugstr_w(wadi->pszDriverAlias), ret);
+               if (ret == MMSYSERR_NOERROR) {
+                   if (fdwOpen & ACM_STREAMOPENF_QUERY) {
+                       acmDriverClose(had, 0L);
+                   }
+                   break;
+               }
+           }
+           /* no match, close this acm driver and try next one */
+           acmDriverClose(had, 0L);
+       }
+       if (ret != MMSYSERR_NOERROR) {
+           ret = ACMERR_NOTPOSSIBLE;
+           goto errCleanUp;
+       }
+    }
+    ret = MMSYSERR_NOERROR;
+    was->drvInst.has = (HACMSTREAM)was;
+    if (!(fdwOpen & ACM_STREAMOPENF_QUERY)) {
+       if (phas)
+           *phas = (HACMSTREAM)was;
+       TRACE("=> (%d)\n", ret);
+       return ret;
+    }
+errCleanUp:
+    if (phas)
+       *phas = NULL;
+    HeapFree(MSACM_hHeap, 0, was);
+    TRACE("=> (%d)\n", ret);
+    return ret;
+}
+
+
+/***********************************************************************
+ *           acmStreamPrepareHeader (MSACM32.@)
+ */
+MMRESULT WINAPI acmStreamPrepareHeader(HACMSTREAM has, PACMSTREAMHEADER pash,
+                                      DWORD fdwPrepare)
+{
+    PWINE_ACMSTREAM    was;
+    MMRESULT           ret = MMSYSERR_NOERROR;
+    PACMDRVSTREAMHEADER        padsh;
+
+    TRACE("(%p, %p, %ld)\n", has, pash, fdwPrepare);
+
+    if ((was = ACM_GetStream(has)) == NULL) {
+        WARN("invalid handle\n");
+       return MMSYSERR_INVALHANDLE;
+    }
+    if (!pash || pash->cbStruct < sizeof(ACMSTREAMHEADER)) {
+        WARN("invalid parameter\n");
+       return MMSYSERR_INVALPARAM;
+    }
+    if (fdwPrepare)
+       ret = MMSYSERR_INVALFLAG;
+
+    if (pash->fdwStatus & ACMSTREAMHEADER_STATUSF_DONE)
+       return MMSYSERR_NOERROR;
+
+    /* Note: the ACMSTREAMHEADER and ACMDRVSTREAMHEADER structs are of same
+     * size. some fields are private to msacm internals, and are exposed
+     * in ACMSTREAMHEADER in the dwReservedDriver array
+     */
+    padsh = (PACMDRVSTREAMHEADER)pash;
+
+    padsh->fdwConvert = fdwPrepare;
+    padsh->padshNext = NULL;
+    padsh->fdwDriver = padsh->dwDriver = 0L;
+
+    padsh->fdwPrepared = 0;
+    padsh->dwPrepared = 0;
+    padsh->pbPreparedSrc = 0;
+    padsh->cbPreparedSrcLength = 0;
+    padsh->pbPreparedDst = 0;
+    padsh->cbPreparedDstLength = 0;
+
+    ret = SendDriverMessage(was->pDrv->hDrvr, ACMDM_STREAM_PREPARE, (DWORD)&was->drvInst, (DWORD)padsh);
+    if (ret == MMSYSERR_NOERROR || ret == MMSYSERR_NOTSUPPORTED) {
+       ret = MMSYSERR_NOERROR;
+       padsh->fdwStatus &= ~(ACMSTREAMHEADER_STATUSF_DONE|ACMSTREAMHEADER_STATUSF_INQUEUE);
+       padsh->fdwStatus |= ACMSTREAMHEADER_STATUSF_PREPARED;
+       padsh->fdwPrepared = padsh->fdwStatus;
+       padsh->dwPrepared = 0;
+       padsh->pbPreparedSrc = padsh->pbSrc;
+       padsh->cbPreparedSrcLength = padsh->cbSrcLength;
+       padsh->pbPreparedDst = padsh->pbDst;
+       padsh->cbPreparedDstLength = padsh->cbDstLength;
+    } else {
+       padsh->fdwPrepared = 0;
+       padsh->dwPrepared = 0;
+       padsh->pbPreparedSrc = 0;
+       padsh->cbPreparedSrcLength = 0;
+       padsh->pbPreparedDst = 0;
+       padsh->cbPreparedDstLength = 0;
+    }
+    TRACE("=> (%d)\n", ret);
+    return ret;
+}
+
+/***********************************************************************
+ *           acmStreamReset (MSACM32.@)
+ */
+MMRESULT WINAPI acmStreamReset(HACMSTREAM has, DWORD fdwReset)
+{
+    PWINE_ACMSTREAM    was;
+    MMRESULT           ret = MMSYSERR_NOERROR;
+
+    TRACE("(%p, %ld)\n", has, fdwReset);
+
+    if (fdwReset) {
+        WARN("invalid flag\n");
+       ret = MMSYSERR_INVALFLAG;
+    } else if ((was = ACM_GetStream(has)) == NULL) {
+        WARN("invalid handle\n");
+       return MMSYSERR_INVALHANDLE;
+    } else if (was->drvInst.fdwOpen & ACM_STREAMOPENF_ASYNC) {
+       ret = SendDriverMessage(was->pDrv->hDrvr, ACMDM_STREAM_RESET, (DWORD)&was->drvInst, 0);
+    }
+    TRACE("=> (%d)\n", ret);
+    return ret;
+}
+
+/***********************************************************************
+ *           acmStreamSize (MSACM32.@)
+ */
+MMRESULT WINAPI acmStreamSize(HACMSTREAM has, DWORD cbInput,
+                             LPDWORD pdwOutputBytes, DWORD fdwSize)
+{
+    PWINE_ACMSTREAM    was;
+    ACMDRVSTREAMSIZE   adss;
+    MMRESULT           ret;
+
+    TRACE("(%p, %ld, %p, %ld)\n", has, cbInput, pdwOutputBytes, fdwSize);
+
+    if ((was = ACM_GetStream(has)) == NULL) {
+        WARN("invalid handle\n");
+       return MMSYSERR_INVALHANDLE;
+    }
+    if ((fdwSize & ~ACM_STREAMSIZEF_QUERYMASK) != 0) {
+        WARN("invalid flag\n");
+       return MMSYSERR_INVALFLAG;
+    }
+
+    *pdwOutputBytes = 0L;
+
+    switch (fdwSize & ACM_STREAMSIZEF_QUERYMASK) {
+    case ACM_STREAMSIZEF_DESTINATION:
+       adss.cbDstLength = cbInput;
+       adss.cbSrcLength = 0;
+       break;
+    case ACM_STREAMSIZEF_SOURCE:
+       adss.cbSrcLength = cbInput;
+       adss.cbDstLength = 0;
+       break;
+    default:
+        WARN("invalid flag\n");
+       return MMSYSERR_INVALFLAG;
+    }
+
+    adss.cbStruct = sizeof(adss);
+    adss.fdwSize = fdwSize;
+    ret = SendDriverMessage(was->pDrv->hDrvr, ACMDM_STREAM_SIZE,
+                           (DWORD)&was->drvInst, (DWORD)&adss);
+    if (ret == MMSYSERR_NOERROR) {
+       switch (fdwSize & ACM_STREAMSIZEF_QUERYMASK) {
+       case ACM_STREAMSIZEF_DESTINATION:
+           *pdwOutputBytes = adss.cbSrcLength;
+           break;
+       case ACM_STREAMSIZEF_SOURCE:
+           *pdwOutputBytes = adss.cbDstLength;
+           break;
+       }
+    }
+    TRACE("=> (%d) [%lu]\n", ret, *pdwOutputBytes);
+    return ret;
+}
+
+/***********************************************************************
+ *           acmStreamUnprepareHeader (MSACM32.@)
+ */
+MMRESULT WINAPI acmStreamUnprepareHeader(HACMSTREAM has, PACMSTREAMHEADER pash,
+                                        DWORD fdwUnprepare)
+{
+    PWINE_ACMSTREAM    was;
+    MMRESULT           ret = MMSYSERR_NOERROR;
+    PACMDRVSTREAMHEADER        padsh;
+
+    TRACE("(%p, %p, %ld)\n", has, pash, fdwUnprepare);
+
+    if ((was = ACM_GetStream(has)) == NULL) {
+        WARN("invalid handle\n");
+       return MMSYSERR_INVALHANDLE;
+    }
+    if (!pash || pash->cbStruct < sizeof(ACMSTREAMHEADER)) {
+        WARN("invalid parameter\n");
+       return MMSYSERR_INVALPARAM;
+    }
+    if (!(pash->fdwStatus & ACMSTREAMHEADER_STATUSF_PREPARED)) {
+        WARN("unprepared header\n");
+       return ACMERR_UNPREPARED;
+    }
+
+    /* Note: the ACMSTREAMHEADER and ACMDRVSTREAMHEADER structs are of same
+     * size. some fields are private to msacm internals, and are exposed
+     * in ACMSTREAMHEADER in the dwReservedDriver array
+     */
+    padsh = (PACMDRVSTREAMHEADER)pash;
+
+    /* check that pointers have not been modified */
+    if (padsh->pbPreparedSrc != padsh->pbSrc ||
+       padsh->cbPreparedSrcLength < padsh->cbSrcLength ||
+       padsh->pbPreparedDst != padsh->pbDst ||
+       padsh->cbPreparedDstLength < padsh->cbDstLength) {
+        WARN("invalid parameter\n");
+       return MMSYSERR_INVALPARAM;
+    }
+
+    padsh->fdwConvert = fdwUnprepare;
+
+    ret = SendDriverMessage(was->pDrv->hDrvr, ACMDM_STREAM_UNPREPARE, (DWORD)&was->drvInst, (DWORD)padsh);
+    if (ret == MMSYSERR_NOERROR || ret == MMSYSERR_NOTSUPPORTED) {
+       ret = MMSYSERR_NOERROR;
+       padsh->fdwStatus &= ~(ACMSTREAMHEADER_STATUSF_DONE|ACMSTREAMHEADER_STATUSF_INQUEUE|ACMSTREAMHEADER_STATUSF_PREPARED);
+    }
+    TRACE("=> (%d)\n", ret);
+    return ret;
+}
diff --git a/reactos/dll/msacm/wineacm.h b/reactos/dll/msacm/wineacm.h
new file mode 100644 (file)
index 0000000..5244bfb
--- /dev/null
@@ -0,0 +1,363 @@
+/* -*- tab-width: 8; c-basic-offset: 4 -*- */
+/*
+ * Copyright 2000 Eric Pouech
+ *
+ * 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
+ */
+
+#ifndef __WINE_WINEACM_H
+#define __WINE_WINEACM_H
+
+#ifndef __REACTOS__
+#include "wine/windef16.h"
+//#include "wine/mmsystem16.h"
+
+/***********************************************************************
+ * Win16 definitions
+ */
+typedef BOOL16 (CALLBACK *ACMDRIVERENUMCB16)(
+  HACMDRIVERID16 hadid, DWORD dwInstance, DWORD fdwSupport
+);
+typedef UINT (CALLBACK *ACMFILTERCHOOSEHOOKPROC16)(
+  HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam
+);
+typedef UINT16 (CALLBACK *ACMFORMATCHOOSEHOOKPROC16)(
+  HWND16 hwnd, UINT16 uMsg, WPARAM16 wParam, LPARAM lParam
+);
+
+typedef struct _ACMDRIVERDETAILS16
+{
+  DWORD   cbStruct;
+
+  FOURCC  fccType;
+  FOURCC  fccComp;
+
+  WORD    wMid;
+  WORD    wPid;
+
+  DWORD   vdwACM;
+  DWORD   vdwDriver;
+
+  DWORD   fdwSupport;
+  DWORD   cFormatTags;
+  DWORD   cFilterTags;
+
+  HICON16 hicon;
+
+  CHAR    szShortName[ACMDRIVERDETAILS_SHORTNAME_CHARS];
+  CHAR    szLongName[ACMDRIVERDETAILS_LONGNAME_CHARS];
+  CHAR    szCopyright[ACMDRIVERDETAILS_COPYRIGHT_CHARS];
+  CHAR    szLicensing[ACMDRIVERDETAILS_LICENSING_CHARS];
+  CHAR    szFeatures[ACMDRIVERDETAILS_FEATURES_CHARS];
+} ACMDRIVERDETAILS16, *NPACMDRIVERDETAILS16, *LPACMDRIVERDETAILS16;
+
+typedef struct _ACMFILTERCHOOSE16
+{
+  DWORD          cbStruct;
+  DWORD          fdwStyle;
+
+  HWND16         hwndOwner;
+
+  LPWAVEFILTER   pwfltr;
+  DWORD          cbwfltr;
+
+  LPCSTR         pszTitle;
+
+  char           szFilterTag[ACMFILTERTAGDETAILS_FILTERTAG_CHARS];
+  char           szFilter[ACMFILTERDETAILS_FILTER_CHARS];
+  LPSTR          pszName;
+  DWORD          cchName;
+
+  DWORD          fdwEnum;
+  LPWAVEFILTER   pwfltrEnum;
+
+  HINSTANCE16    hInstance;
+  LPCSTR         pszTemplateName;
+  LPARAM         lCustData;
+  ACMFILTERCHOOSEHOOKPROC16 pfnHook;
+} ACMFILTERCHOOSE16, *NPACMFILTERCHOOSE16, *LPACMFILTERCHOOSE16;
+
+typedef struct _ACMFILTERDETAILS16
+{
+  DWORD          cbStruct;
+  DWORD          dwFilterIndex;
+  DWORD          dwFilterTag;
+  DWORD          fdwSupport;
+  LPWAVEFILTER   pwfltr;
+  DWORD          cbwfltr;
+  CHAR           szFilter[ACMFILTERDETAILS_FILTER_CHARS];
+} ACMFILTERDETAILS16, *NPACMFILTERDETAILS16, *LPACMFILTERDETAILS16;
+
+typedef struct _ACMFILTERTAGDETAILS16
+{
+  DWORD cbStruct;
+  DWORD dwFilterTagIndex;
+  DWORD dwFilterTag;
+  DWORD cbFilterSize;
+  DWORD fdwSupport;
+  DWORD cStandardFilters;
+  CHAR  szFilterTag[ACMFILTERTAGDETAILS_FILTERTAG_CHARS];
+} ACMFILTERTAGDETAILS16, *NPACMFILTERTAGDETAILS16, *LPACMFILTERTAGDETAILS16;
+
+typedef struct _ACMFORMATCHOOSE16
+{
+  DWORD            cbStruct;
+  DWORD            fdwStyle;
+
+  HWND16           hwndOwner;
+
+  LPWAVEFORMATEX   pwfx;
+  DWORD            cbwfx;
+  LPCSTR           pszTitle;
+
+  CHAR             szFormatTag[ACMFORMATTAGDETAILS_FORMATTAG_CHARS];
+  CHAR             szFormat[ACMFORMATDETAILS_FORMAT_CHARS];
+
+  LPSTR            pszName;
+  DWORD            cchName;
+
+  DWORD            fdwEnum;
+  LPWAVEFORMATEX   pwfxEnum;
+
+  HINSTANCE16      hInstance;
+  LPCSTR           pszTemplateName;
+  LPARAM           lCustData;
+  ACMFORMATCHOOSEHOOKPROC16 pfnHook;
+} ACMFORMATCHOOSE16, *NPACMFORMATCHOOSE16, *LPACMFORMATCHOOSE16;
+
+typedef struct _ACMFORMATDETAILS16
+{
+    DWORD            cbStruct;
+    DWORD            dwFormatIndex;
+    DWORD            dwFormatTag;
+    DWORD            fdwSupport;
+    LPWAVEFORMATEX   pwfx;
+    DWORD            cbwfx;
+    CHAR             szFormat[ACMFORMATDETAILS_FORMAT_CHARS];
+} ACMFORMATDETAILS16, *NPACMFORMATDETAILS16, *LPACMFORMATDETAILS16;
+
+typedef struct _ACMFORMATTAGDETAILS16
+{
+  DWORD cbStruct;
+  DWORD dwFormatTagIndex;
+  DWORD dwFormatTag;
+  DWORD cbFormatSize;
+  DWORD fdwSupport;
+  DWORD cStandardFormats;
+  CHAR  szFormatTag[ACMFORMATTAGDETAILS_FORMATTAG_CHARS];
+} ACMFORMATTAGDETAILS16, *NPACMFORMATTAGDETAILS16, *LPACMFORMATTAGDETAILS16;
+
+typedef ACMSTREAMHEADER ACMSTREAMHEADER16, *NPACMSTREAMHEADER16, *LPACMSTREAMHEADER16;
+
+typedef BOOL16 (CALLBACK *ACMFILTERENUMCB16)(
+ HACMDRIVERID16 hadid, LPACMFILTERDETAILS16 pafd,
+ DWORD dwInstance, DWORD fdwSupport
+);
+
+typedef BOOL16 (CALLBACK *ACMFILTERTAGENUMCB16)(
+  HACMDRIVERID16 hadid, LPACMFILTERTAGDETAILS16 paftd,
+  DWORD dwInstance, DWORD fdwSupport
+);
+
+typedef BOOL16 (CALLBACK *ACMFORMATENUMCB16)(
+  HACMDRIVERID16 hadid, LPACMFORMATDETAILS16 pafd,
+  DWORD dwInstance, DWORD fdwSupport
+);
+
+typedef BOOL16 (CALLBACK *ACMFORMATTAGENUMCB16)(
+  HACMDRIVERID16 hadid, LPACMFORMATTAGDETAILS16 paftd,
+  DWORD dwInstance, DWORD fdwSupport
+);
+
+/***********************************************************************
+ * Functions - Win16
+ */
+
+DWORD WINAPI acmGetVersion16(
+);
+MMRESULT16 WINAPI acmMetrics16(
+  HACMOBJ16 hao, UINT16 uMetric, LPVOID pMetric
+);
+MMRESULT16 WINAPI acmDriverEnum16(
+  ACMDRIVERENUMCB16 fnCallback, DWORD dwInstance, DWORD fdwEnum
+);
+MMRESULT16 WINAPI acmDriverDetails16(
+  HACMDRIVERID16 hadid, LPACMDRIVERDETAILS16 padd, DWORD fdwDetails
+);
+MMRESULT16 WINAPI acmDriverAdd16(
+  LPHACMDRIVERID16 phadid, HINSTANCE16 hinstModule,
+  LPARAM lParam, DWORD dwPriority, DWORD fdwAdd
+);
+MMRESULT16 WINAPI acmDriverRemove16(
+  HACMDRIVERID16 hadid, DWORD fdwRemove
+);
+MMRESULT16 WINAPI acmDriverOpen16(
+  LPHACMDRIVER16 phad, HACMDRIVERID16 hadid, DWORD fdwOpen
+);
+MMRESULT16 WINAPI acmDriverClose16(
+  HACMDRIVER16 had, DWORD fdwClose
+);
+LRESULT WINAPI acmDriverMessage16(
+  HACMDRIVER16 had, UINT16 uMsg, LPARAM lParam1, LPARAM lParam2
+);
+MMRESULT16 WINAPI acmDriverID16(
+  HACMOBJ16 hao, LPHACMDRIVERID16 phadid, DWORD fdwDriverID
+);
+MMRESULT16 WINAPI acmDriverPriority16(
+ HACMDRIVERID16 hadid, DWORD dwPriority, DWORD fdwPriority
+);
+MMRESULT16 WINAPI acmFormatTagDetails16(
+  HACMDRIVER16 had, LPACMFORMATTAGDETAILS16 paftd, DWORD fdwDetails
+);
+MMRESULT16 WINAPI acmFormatTagEnum16(
+  HACMDRIVER16 had, LPACMFORMATTAGDETAILS16 paftd,
+  ACMFORMATTAGENUMCB16 fnCallback, DWORD dwInstance, DWORD fdwEnum
+);
+MMRESULT16 WINAPI acmFormatChoose16(
+  LPACMFORMATCHOOSE16 pafmtc
+);
+MMRESULT16 WINAPI acmFormatDetails16(
+  HACMDRIVER16 had, LPACMFORMATDETAILS16 pafd, DWORD fdwDetails
+);
+MMRESULT16 WINAPI acmFormatEnum16(
+  HACMDRIVER16 had, LPACMFORMATDETAILS16 pafd,
+  ACMFORMATENUMCB16 fnCallback, DWORD dwInstance, DWORD fdwEnum
+);
+MMRESULT16 WINAPI acmFormatSuggest16(
+  HACMDRIVER16 had, LPWAVEFORMATEX pwfxSrc,
+  LPWAVEFORMATEX pwfxDst, DWORD cbwfxDst, DWORD fdwSuggest
+);
+MMRESULT16 WINAPI acmFilterTagDetails16(
+  HACMDRIVER16 had, LPACMFILTERTAGDETAILS16 paftd, DWORD fdwDetails
+);
+MMRESULT16 WINAPI acmFilterTagEnum16(
+  HACMDRIVER16 had, LPACMFILTERTAGDETAILS16 paftd,
+  ACMFILTERTAGENUMCB16 fnCallback, DWORD dwInstance, DWORD fdwEnum
+);
+MMRESULT16 WINAPI acmFilterChoose16(
+  LPACMFILTERCHOOSE16 pafltrc
+);
+MMRESULT16 WINAPI acmFilterDetails16(
+  HACMDRIVER16 had, LPACMFILTERDETAILS16 pafd, DWORD fdwDetails
+);
+MMRESULT16 WINAPI acmFilterEnum16(
+  HACMDRIVER16 had, LPACMFILTERDETAILS16 pafd,
+  ACMFILTERENUMCB16 fnCallback, DWORD dwInstance, DWORD fdwEnum
+);
+MMRESULT16 WINAPI acmStreamOpen16(
+  LPHACMSTREAM16 phas, HACMDRIVER16 had,
+  LPWAVEFORMATEX pwfxSrc, LPWAVEFORMATEX pwfxDst,
+  LPWAVEFILTER pwfltr, DWORD dwCallback,
+  DWORD dwInstance, DWORD fdwOpen
+);
+MMRESULT16 WINAPI acmStreamClose16(
+  HACMSTREAM16 has, DWORD fdwClose
+);
+MMRESULT16 WINAPI acmStreamSize16(
+  HACMSTREAM16 has, DWORD cbInput,
+  LPDWORD pdwOutputBytes, DWORD fdwSize
+);
+MMRESULT16 WINAPI acmStreamConvert16(
+  HACMSTREAM16 has, LPACMSTREAMHEADER16 pash, DWORD fdwConvert
+);
+MMRESULT16 WINAPI acmStreamReset16(
+  HACMSTREAM16 has, DWORD fdwReset
+);
+MMRESULT16 WINAPI acmStreamPrepareHeader16(
+  HACMSTREAM16 has, LPACMSTREAMHEADER16 pash, DWORD fdwPrepare
+);
+MMRESULT16 WINAPI acmStreamUnprepareHeader16(
+  HACMSTREAM16 has, LPACMSTREAMHEADER16 pash, DWORD fdwUnprepare
+);
+#endif
+
+/***********************************************************************
+ * Wine specific - Win32
+ */
+typedef struct _WINE_ACMDRIVERID *PWINE_ACMDRIVERID;
+typedef struct _WINE_ACMDRIVER   *PWINE_ACMDRIVER;
+
+#define WINE_ACMOBJ_DONTCARE   0x5EED0000
+#define WINE_ACMOBJ_DRIVERID   0x5EED0001
+#define WINE_ACMOBJ_DRIVER     0x5EED0002
+#define WINE_ACMOBJ_STREAM     0x5EED0003
+
+typedef struct _WINE_ACMOBJ
+{
+    DWORD              dwType;
+    PWINE_ACMDRIVERID  pACMDriverID;
+} WINE_ACMOBJ, *PWINE_ACMOBJ;
+
+typedef struct _WINE_ACMDRIVER
+{
+    WINE_ACMOBJ                obj;
+    HDRVR                      hDrvr;
+    PWINE_ACMDRIVER    pNextACMDriver;
+} WINE_ACMDRIVER;
+
+typedef struct _WINE_ACMSTREAM
+{
+    WINE_ACMOBJ                obj;
+    PWINE_ACMDRIVER    pDrv;
+    ACMDRVSTREAMINSTANCE drvInst;
+    HACMDRIVER         hAcmDriver;
+} WINE_ACMSTREAM, *PWINE_ACMSTREAM;
+
+typedef struct _WINE_ACMDRIVERID
+{
+    WINE_ACMOBJ                obj;
+    LPWSTR             pszDriverAlias;
+    LPWSTR              pszFileName;
+    HINSTANCE          hInstModule;          /* NULL if global */
+    PWINE_ACMDRIVER     pACMDriverList;
+    PWINE_ACMDRIVERID   pNextACMDriverID;
+    PWINE_ACMDRIVERID  pPrevACMDriverID;
+    /* information about the driver itself, either gotten from registry or driver itself */
+    DWORD              cFilterTags;
+    DWORD              cFormatTags;
+    DWORD              fdwSupport;
+    struct {
+       DWORD                   dwFormatTag;
+       DWORD                   cbwfx;
+    }*                         aFormatTag;
+} WINE_ACMDRIVERID;
+
+/* From internal.c */
+extern HANDLE MSACM_hHeap;
+extern PWINE_ACMDRIVERID MSACM_pFirstACMDriverID;
+extern PWINE_ACMDRIVERID MSACM_pLastACMDriverID;
+extern PWINE_ACMDRIVERID MSACM_RegisterDriver(LPCWSTR pszDriverAlias, LPCWSTR pszFileName,
+                                             HINSTANCE hinstModule);
+extern void MSACM_RegisterAllDrivers(void);
+extern PWINE_ACMDRIVERID MSACM_UnregisterDriver(PWINE_ACMDRIVERID p);
+extern void MSACM_UnregisterAllDrivers(void);
+extern PWINE_ACMDRIVERID MSACM_GetDriverID(HACMDRIVERID hDriverID);
+extern PWINE_ACMDRIVER MSACM_GetDriver(HACMDRIVER hDriver);
+extern PWINE_ACMOBJ MSACM_GetObj(HACMOBJ hObj, DWORD type);
+
+extern MMRESULT MSACM_Message(HACMDRIVER, UINT, LPARAM, LPARAM);
+extern BOOL MSACM_FindFormatTagInCache(WINE_ACMDRIVERID*, DWORD, LPDWORD);
+
+/* From msacm32.c */
+extern HINSTANCE MSACM_hInstance32;
+
+/* From pcmcnvtr.c */
+LRESULT CALLBACK       PCM_DriverProc(DWORD dwDevID, HDRVR hDriv, UINT wMsg,
+                                      LPARAM dwParam1, LPARAM dwParam2);
+
+/* Dialog box templates */
+#include "msacmdlg.h"
+
+#endif /* __WINE_WINEACM_H */
diff --git a/reactos/dll/msafd/include/debug.h b/reactos/dll/msafd/include/debug.h
new file mode 100644 (file)
index 0000000..a4eb8fe
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * COPYRIGHT:   See COPYING in the top level directory
+ * PROJECT:     ReactOS Ancillary Function Driver DLL
+ * FILE:        include/debug.h
+ * PURPOSE:     Debugging support macros
+ * DEFINES:     DBG     - Enable debug output
+ *              NASSERT - Disable assertions
+ */
+#ifndef __DEBUG_H
+#define __DEBUG_H
+
+#define NORMAL_MASK    0x000000FF
+#define SPECIAL_MASK   0xFFFFFF00
+#define MIN_TRACE      0x00000001
+#define MID_TRACE      0x00000002
+#define MAX_TRACE      0x00000003
+
+#define DEBUG_CHECK    0x00000100
+#define DEBUG_ULTRA    0xFFFFFFFF
+
+#ifdef ASSERT
+#undef ASSERT
+#endif
+
+#ifdef DBG
+
+extern DWORD DebugTraceLevel;
+
+#define AFD_DbgPrint(_t_, _x_) \
+    if (((DebugTraceLevel & NORMAL_MASK) >= _t_) || \
+        ((DebugTraceLevel & _t_) > NORMAL_MASK)) { \
+        DbgPrint("(%hS:%d)(%hS) ", __FILE__, __LINE__, __FUNCTION__); \
+               DbgPrint _x_; \
+    }
+
+#ifdef NASSERT
+#define ASSERT(x)
+#else /* NASSERT */
+#define ASSERT(x) if (!(x)) { AFD_DbgPrint(MIN_TRACE, ("Assertion "#x" failed at %s:%d\n", __FILE__, __LINE__)); ExitProcess(0); }
+#endif /* NASSERT */
+
+#else /* DBG */
+
+#define AFD_DbgPrint(_t_, _x_)
+
+#define ASSERT_IRQL(x)
+#define ASSERT(x)
+
+#endif /* DBG */
+
+#ifdef assert
+#undef assert
+#endif
+#define assert(x) ASSERT(x)
+
+
+#define UNIMPLEMENTED \
+    AFD_DbgPrint(MIN_TRACE, ("is unimplemented, please try again later.\n"));
+
+#define CHECKPOINT \
+    AFD_DbgPrint(DEBUG_CHECK, ("\n"));
+
+#define CP CHECKPOINT
+
+#endif /* __DEBUG_H */
+
+/* EOF */
diff --git a/reactos/dll/msafd/include/helpers.h b/reactos/dll/msafd/include/helpers.h
new file mode 100644 (file)
index 0000000..1b73c66
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * COPYRIGHT:   See COPYING in the top level directory
+ * PROJECT:     ReactOS Ancillary Function Driver DLL
+ * FILE:        include/helpers.h
+ * PURPOSE:     Definitions for helper DLL management
+ */
+#ifndef __HELPERS_H
+#define __HELPERS_H
+
+//#include <msafd.h>
+
+typedef struct _HELPER_DATA {
+    LIST_ENTRY                                         Helpers;
+       LONG                                                    RefCount;
+    HANDLE                                                     hInstance;
+    INT                                                                MinWSAddressLength;
+    INT                                                                MaxWSAddressLength;
+    INT                                                                MinTDIAddressLength;
+    INT                                                                MaxTDIAddressLength;
+    BOOLEAN                                                    UseDelayedAcceptance;
+    PWINSOCK_MAPPING                           Mapping;
+       PWSH_OPEN_SOCKET                                WSHOpenSocket;
+       PWSH_OPEN_SOCKET2                               WSHOpenSocket2;
+       PWSH_JOIN_LEAF                                  WSHJoinLeaf;
+       PWSH_NOTIFY                                             WSHNotify;
+       PWSH_GET_SOCKET_INFORMATION             WSHGetSocketInformation;
+       PWSH_SET_SOCKET_INFORMATION             WSHSetSocketInformation;
+       PWSH_GET_SOCKADDR_TYPE                  WSHGetSockaddrType;
+       PWSH_GET_WILDCARD_SOCKEADDR             WSHGetWildcardSockaddr;
+       PWSH_GET_BROADCAST_SOCKADDR             WSHGetBroadcastSockaddr;
+       PWSH_ADDRESS_TO_STRING                  WSHAddressToString;
+       PWSH_STRING_TO_ADDRESS                  WSHStringToAddress;
+       PWSH_IOCTL                                              WSHIoctl;
+    WCHAR                                                      TransportName[1];
+} HELPER_DATA, *PHELPER_DATA;
+
+int SockLoadHelperDll(
+       PWSTR TransportName, 
+       PWINSOCK_MAPPING Mapping, 
+       PHELPER_DATA *HelperDllData
+);
+
+int SockLoadTransportMapping(
+       PWSTR TransportName, 
+       PWINSOCK_MAPPING *Mapping
+);
+
+int SockLoadTransportList(
+       PWSTR *TransportList
+);
+
+BOOL SockIsTripleInMapping(
+       PWINSOCK_MAPPING Mapping, 
+       INT AddressFamily, 
+       INT SocketType, 
+       INT Protocol
+);
+
+int SockGetTdiName(
+       PINT AddressFamily, 
+       PINT SocketType, 
+       PINT Protocol, 
+       GROUP Group, 
+       DWORD Flags, 
+       PUNICODE_STRING TransportName, 
+       PVOID *HelperDllContext, 
+       PHELPER_DATA *HelperDllData, 
+       PDWORD Events
+);
+
+#endif /* __HELPERS_H */
+
+/* EOF */
diff --git a/reactos/dll/msafd/misc/dllmain.c b/reactos/dll/msafd/misc/dllmain.c
new file mode 100644 (file)
index 0000000..8cb8eb0
--- /dev/null
@@ -0,0 +1,2310 @@
+
+/*
+ * COPYRIGHT:   See COPYING in the top level directory
+ * PROJECT:     ReactOS Ancillary Function Driver DLL
+ * FILE:        misc/dllmain.c
+ * PURPOSE:     DLL entry point
+ * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
+ *                             Alex Ionescu (alex@relsoft.net)
+ * REVISIONS:
+ *   CSH 01/09-2000 Created
+ *      Alex 16/07/2004 - Complete Rewrite
+ */
+#include <msafd.h>
+
+#include <debug.h>
+
+#ifdef DBG
+//DWORD DebugTraceLevel = DEBUG_ULTRA;
+DWORD DebugTraceLevel = 0;
+#endif /* DBG */
+
+HANDLE GlobalHeap;
+WSPUPCALLTABLE Upcalls;
+LPWPUCOMPLETEOVERLAPPEDREQUEST lpWPUCompleteOverlappedRequest;
+ULONG SocketCount = 0;
+PSOCKET_INFORMATION *Sockets = NULL;
+LIST_ENTRY SockHelpersListHead = {NULL};
+ULONG SockAsyncThreadRefCount;
+HANDLE SockAsyncHelperAfdHandle;
+HANDLE SockAsyncCompletionPort;
+BOOLEAN SockAsyncSelectCalled;
+
+SOCKET 
+WSPAPI 
+WSPSocket(
+       int AddressFamily, 
+       int SocketType, 
+       int Protocol, 
+       LPWSAPROTOCOL_INFOW lpProtocolInfo, 
+       GROUP g, 
+       DWORD dwFlags, 
+       LPINT lpErrno)  
+/*
+ * FUNCTION: Creates a new socket
+ * ARGUMENTS:
+ *     af             = Address family
+ *     type           = Socket type
+ *     protocol       = Protocol type
+ *     lpProtocolInfo = Pointer to protocol information
+ *     g              = Reserved
+ *     dwFlags        = Socket flags
+ *     lpErrno        = Address of buffer for error information
+ * RETURNS:
+ *     Created socket, or INVALID_SOCKET if it could not be created
+ */
+{
+       OBJECT_ATTRIBUTES                       Object;
+       IO_STATUS_BLOCK                         IOSB;
+       USHORT                                          SizeOfPacket;
+       ULONG                                           SizeOfEA;
+       PAFD_CREATE_PACKET                      AfdPacket;
+       HANDLE                                          Sock;
+       PSOCKET_INFORMATION                     Socket = NULL, PrevSocket = NULL;
+    PFILE_FULL_EA_INFORMATION  EABuffer = NULL;
+       PHELPER_DATA                            HelperData;
+       PVOID                                           HelperDLLContext;
+       DWORD                                           HelperEvents;
+       UNICODE_STRING                          TransportName;
+       UNICODE_STRING                          DevName;
+       LARGE_INTEGER                           GroupData;
+       INT                         Status;
+
+       AFD_DbgPrint(MAX_TRACE, ("Creating Socket, getting TDI Name\n"));
+       AFD_DbgPrint(MAX_TRACE, ("AddressFamily (%d)  SocketType (%d)  Protocol (%d).\n",
+    AddressFamily, SocketType, Protocol));
+
+       /* Get Helper Data and Transport */
+       Status = SockGetTdiName (&AddressFamily,
+                                &SocketType,
+                                &Protocol,
+                                g,
+                                dwFlags,
+                                &TransportName,
+                                &HelperDLLContext,
+                                &HelperData,
+                                &HelperEvents);
+
+       /* Check for error */
+       if (Status != NO_ERROR) {
+           AFD_DbgPrint(MID_TRACE,("SockGetTdiName: Status %x\n", Status));
+           goto error;
+       }
+
+       /* AFD Device Name */
+       RtlInitUnicodeString(&DevName, L"\\Device\\Afd\\Endpoint");
+
+       /* Set Socket Data */
+       Socket = HeapAlloc(GlobalHeap, 0, sizeof(*Socket));
+       RtlZeroMemory(Socket, sizeof(*Socket));
+       Socket->RefCount = 2;
+       Socket->Handle = -1;
+       Socket->SharedData.State = SocketOpen;
+       Socket->SharedData.AddressFamily = AddressFamily;
+       Socket->SharedData.SocketType = SocketType;
+       Socket->SharedData.Protocol = Protocol;
+       Socket->HelperContext = HelperDLLContext;
+       Socket->HelperData = HelperData;
+       Socket->HelperEvents = HelperEvents;
+       Socket->LocalAddress = &Socket->WSLocalAddress;
+       Socket->SharedData.SizeOfLocalAddress = HelperData->MaxWSAddressLength;
+       Socket->RemoteAddress = &Socket->WSRemoteAddress;
+       Socket->SharedData.SizeOfRemoteAddress = HelperData->MaxWSAddressLength;
+       Socket->SharedData.UseDelayedAcceptance = HelperData->UseDelayedAcceptance;
+       Socket->SharedData.CreateFlags = dwFlags;
+       Socket->SharedData.CatalogEntryId = lpProtocolInfo->dwCatalogEntryId;
+       Socket->SharedData.ServiceFlags1 = lpProtocolInfo->dwServiceFlags1;
+       Socket->SharedData.ProviderFlags = lpProtocolInfo->dwProviderFlags;
+       Socket->SharedData.GroupID = g;
+       Socket->SharedData.GroupType = 0;
+       Socket->SharedData.UseSAN = FALSE;
+        Socket->SharedData.NonBlocking = FALSE; /* Sockets start blocking */
+       Socket->SanData = NULL;
+
+       /* Ask alex about this */
+       if( Socket->SharedData.SocketType == SOCK_DGRAM ||
+            Socket->SharedData.SocketType == SOCK_RAW ) {
+            AFD_DbgPrint(MID_TRACE,("Connectionless socket\n"));
+           Socket->SharedData.ServiceFlags1 |= XP1_CONNECTIONLESS;
+        }
+
+       /* Packet Size */
+       SizeOfPacket = TransportName.Length + sizeof(AFD_CREATE_PACKET) + sizeof(WCHAR);
+
+       /* EA Size */
+       SizeOfEA = SizeOfPacket + sizeof(FILE_FULL_EA_INFORMATION) + AFD_PACKET_COMMAND_LENGTH;
+
+       /* Set up EA Buffer */
+       EABuffer = HeapAlloc(GlobalHeap, 0, SizeOfEA);
+        RtlZeroMemory(EABuffer, SizeOfEA);
+       EABuffer->NextEntryOffset = 0;
+       EABuffer->Flags = 0;
+       EABuffer->EaNameLength = AFD_PACKET_COMMAND_LENGTH;
+       RtlCopyMemory (EABuffer->EaName, 
+                                       AfdCommand, 
+                                       AFD_PACKET_COMMAND_LENGTH + 1);
+       EABuffer->EaValueLength = SizeOfPacket;
+       
+       /* Set up AFD Packet */
+       AfdPacket = (PAFD_CREATE_PACKET)(EABuffer->EaName + EABuffer->EaNameLength + 1);
+       AfdPacket->SizeOfTransportName = TransportName.Length;
+       RtlCopyMemory (AfdPacket->TransportName,
+                                       TransportName.Buffer, 
+                                       TransportName.Length + sizeof(WCHAR));
+       AfdPacket->GroupID = g;
+
+       /* Set up Endpoint Flags */
+       if ((Socket->SharedData.ServiceFlags1 & XP1_CONNECTIONLESS) != 0) {
+               if ((SocketType != SOCK_DGRAM) && (SocketType != SOCK_RAW)) {
+                       goto error;                     /* Only RAW or UDP can be Connectionless */
+               }
+               AfdPacket->EndpointFlags |= AFD_ENDPOINT_CONNECTIONLESS;
+       }
+       
+       if ((Socket->SharedData.ServiceFlags1 & XP1_MESSAGE_ORIENTED) != 0) { 
+               if (SocketType == SOCK_STREAM) {
+                       if ((Socket->SharedData.ServiceFlags1 & XP1_PSEUDO_STREAM) == 0) {
+                               goto error;             /* The Provider doesn't actually support Message Oriented Streams */
+                       }
+               }
+                       AfdPacket->EndpointFlags |= AFD_ENDPOINT_MESSAGE_ORIENTED;
+       }
+
+       if (SocketType == SOCK_RAW) AfdPacket->EndpointFlags |= AFD_ENDPOINT_RAW;
+
+       if (dwFlags & (WSA_FLAG_MULTIPOINT_C_ROOT | 
+               WSA_FLAG_MULTIPOINT_C_LEAF | 
+               WSA_FLAG_MULTIPOINT_D_ROOT | 
+               WSA_FLAG_MULTIPOINT_D_LEAF)) {
+               if ((Socket->SharedData.ServiceFlags1 & XP1_SUPPORT_MULTIPOINT) == 0) {
+                       goto error;                     /* The Provider doesn't actually support Multipoint */
+               }
+               AfdPacket->EndpointFlags |= AFD_ENDPOINT_MULTIPOINT;
+               if (dwFlags & WSA_FLAG_MULTIPOINT_C_ROOT) {
+                       if (((Socket->SharedData.ServiceFlags1 & XP1_MULTIPOINT_CONTROL_PLANE) == 0) 
+                               || ((dwFlags & WSA_FLAG_MULTIPOINT_C_LEAF) != 0)) {
+                               goto error;             /* The Provider doesn't support Control Planes, or you already gave a leaf */
+                       }
+                       AfdPacket->EndpointFlags |= AFD_ENDPOINT_C_ROOT;
+               }
+               if (dwFlags & WSA_FLAG_MULTIPOINT_D_ROOT) {
+                       if (((Socket->SharedData.ServiceFlags1 & XP1_MULTIPOINT_DATA_PLANE) == 0) 
+                               || ((dwFlags & WSA_FLAG_MULTIPOINT_D_LEAF) != 0)) {
+                               goto error;             /* The Provider doesn't support Data Planes, or you already gave a leaf */
+                       }
+                       AfdPacket->EndpointFlags |= AFD_ENDPOINT_D_ROOT;
+               }
+       }
+
+    /* Set up Object Attributes */
+    InitializeObjectAttributes (&Object,
+                                                               &DevName, 
+                                                               OBJ_CASE_INSENSITIVE | OBJ_INHERIT, 
+                                                               0, 
+                                                               0);
+
+       /* Create the Socket as asynchronous. That means we have to block
+          ourselves after every call to NtDeviceIoControlFile. This is
+          because the kernel doesn't support overlapping synchronous I/O
+          requests (made from multiple threads) at this time (Sep 2005) */
+       ZwCreateFile(&Sock,
+                       GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
+                       &Object,
+                       &IOSB,
+                       NULL,
+                       0,
+                       FILE_SHARE_READ | FILE_SHARE_WRITE,
+                       FILE_OPEN_IF,
+                       0,
+                       EABuffer,
+                       SizeOfEA);
+
+       /* Save Handle */
+       Socket->Handle = (SOCKET)Sock;
+
+        /* XXX See if there's a structure we can reuse -- We need to do this
+         * more properly. */
+        PrevSocket = GetSocketStructure( (SOCKET)Sock );
+
+        if( PrevSocket ) {
+            RtlCopyMemory( PrevSocket, Socket, sizeof(*Socket) );
+            RtlFreeHeap( GlobalHeap, 0, Socket );
+            Socket = PrevSocket;
+        }
+
+       /* Save Group Info */
+       if (g != 0) {
+               GetSocketInformation(Socket, AFD_INFO_GROUP_ID_TYPE, 0, &GroupData);
+
+               Socket->SharedData.GroupID = GroupData.u.LowPart;
+               Socket->SharedData.GroupType = GroupData.u.HighPart;
+       }
+
+       /* Get Window Sizes and Save them */
+       GetSocketInformation (Socket,
+                              AFD_INFO_SEND_WINDOW_SIZE, 
+                              &Socket->SharedData.SizeOfSendBuffer, 
+                              NULL);
+       GetSocketInformation (Socket, 
+                              AFD_INFO_RECEIVE_WINDOW_SIZE, 
+                              &Socket->SharedData.SizeOfRecvBuffer, 
+                              NULL);
+
+       /* Save in Process Sockets List */
+       Sockets[SocketCount] = Socket;
+       SocketCount ++;
+
+       /* Create the Socket Context */
+       CreateContext(Socket);
+
+       /* Notify Winsock */
+       Upcalls.lpWPUModifyIFSHandle(1, (SOCKET)Sock, lpErrno);
+
+       /* Return Socket Handle */
+       AFD_DbgPrint(MID_TRACE,("Success %x\n", Sock));
+       return (SOCKET)Sock;
+
+error:
+       AFD_DbgPrint(MID_TRACE,("Ending %x\n", Status));
+
+        if( lpErrno ) *lpErrno = Status;
+
+       return INVALID_SOCKET;
+}
+
+
+DWORD MsafdReturnWithErrno( NTSTATUS Status, LPINT Errno, DWORD Received,
+                           LPDWORD ReturnedBytes ) {
+    if( ReturnedBytes ) *ReturnedBytes = 0; 
+    if( Errno ) { 
+        switch (Status) {
+        case STATUS_CANT_WAIT: *Errno = WSAEWOULDBLOCK; break;
+        case STATUS_TIMEOUT:
+        case STATUS_SUCCESS: 
+            /* Return Number of bytes Read */
+            if( ReturnedBytes ) *ReturnedBytes = Received; break;
+        case STATUS_END_OF_FILE: *Errno = WSAESHUTDOWN; *ReturnedBytes = 0; break;
+        case STATUS_PENDING: *Errno = WSA_IO_PENDING; break;
+        case STATUS_BUFFER_OVERFLOW: *Errno = WSAEMSGSIZE; break;
+        default: {
+            DbgPrint("MSAFD: Error %x is unknown\n", Status);
+            *Errno = WSAEINVAL; break;
+        } break;
+        }
+    }
+
+    /* Success */
+    return Status == STATUS_SUCCESS ? 0 : SOCKET_ERROR;
+}
+
+
+INT
+WSPAPI
+WSPCloseSocket(
+  IN  SOCKET Handle,
+  OUT  LPINT lpErrno)
+/*
+ * FUNCTION: Closes an open socket
+ * ARGUMENTS:
+ *     s       = Socket descriptor
+ *     lpErrno = Address of buffer for error information
+ * RETURNS:
+ *     NO_ERROR, or SOCKET_ERROR if the socket could not be closed
+ */
+{
+    IO_STATUS_BLOCK IoStatusBlock;
+    PSOCKET_INFORMATION Socket = NULL;
+    NTSTATUS Status;
+    HANDLE SockEvent;
+    AFD_DISCONNECT_INFO DisconnectInfo;
+    SOCKET_STATE OldState;
+
+    /* Create the Wait Event */
+    Status = NtCreateEvent(&SockEvent,
+                           GENERIC_READ | GENERIC_WRITE,
+                           NULL,
+                           1,
+                           FALSE);    
+
+    if(!NT_SUCCESS(Status)) return SOCKET_ERROR;
+
+    /* Get the Socket Structure associate to this Socket*/
+    Socket = GetSocketStructure(Handle);
+
+    /* If a Close is already in Process, give up */
+    if (Socket->SharedData.State == SocketClosed) {
+        *lpErrno = WSAENOTSOCK;
+        return SOCKET_ERROR;
+    }
+
+    /* Set the state to close */
+    OldState = Socket->SharedData.State;
+    Socket->SharedData.State = SocketClosed;
+    
+    /* If SO_LINGER is ON and the Socket is connected, we need to disconnect */
+    /* FIXME: Should we do this on Datagram Sockets too? */
+    if ((OldState == SocketConnected) && (Socket->SharedData.LingerData.l_onoff)) {
+        ULONG LingerWait;
+        ULONG SendsInProgress;
+        ULONG SleepWait;
+        
+        /* We need to respect the timeout */
+        SleepWait = 100;
+        LingerWait = Socket->SharedData.LingerData.l_linger * 1000;
+        
+        /* Loop until no more sends are pending, within the timeout */
+        while (LingerWait) {
+            
+            /* Find out how many Sends are in Progress */
+            if (GetSocketInformation(Socket, 
+                                     AFD_INFO_SENDS_IN_PROGRESS,
+                                     &SendsInProgress,
+                                     NULL)) {
+                /* Bail out if anything but NO_ERROR */
+                LingerWait = 0;
+                break;
+            }
+
+            /* Bail out if no more sends are pending */
+            if (!SendsInProgress) break;
+            
+            /* 
+             * We have to execute a sleep, so it's kind of like
+             * a block. If the socket is Nonblock, we cannot
+             * go on since asyncronous operation is expected
+             * and we cannot offer it
+             */
+            if (Socket->SharedData.NonBlocking) {
+                Socket->SharedData.State = OldState;
+                *lpErrno = WSAEWOULDBLOCK;
+                return SOCKET_ERROR;
+            }
+            
+            /* Now we can sleep, and decrement the linger wait */
+            /* 
+             * FIXME: It seems Windows does some funky acceleration
+             * since the waiting seems to be longer and longer. I
+             * don't think this improves performance so much, so we
+             * wait a fixed time instead.
+             */
+            Sleep(SleepWait);
+            LingerWait -= SleepWait;
+        }
+        
+        /*
+         * We have reached the timeout or sends are over.
+         * Disconnect if the timeout has been reached. 
+         */
+        if (LingerWait <= 0) {
+        
+            DisconnectInfo.Timeout = RtlConvertLongToLargeInteger(0);
+            DisconnectInfo.DisconnectType = AFD_DISCONNECT_ABORT;
+            
+            /* Send IOCTL */
+            Status = NtDeviceIoControlFile((HANDLE)Handle,
+                                           SockEvent,
+                                           NULL,
+                                           NULL,
+                                           &IoStatusBlock,
+                                           IOCTL_AFD_DISCONNECT,
+                                           &DisconnectInfo,
+                                           sizeof(DisconnectInfo),
+                                           NULL,
+                                           0);
+        
+            /* Wait for return */
+            if (Status == STATUS_PENDING) {
+                WaitForSingleObject(SockEvent, INFINITE);
+            }         
+        }
+    }
+    
+    /* FIXME: We should notify the Helper DLL of WSH_NOTIFY_CLOSE */
+    
+    /* Cleanup Time! */
+    Socket->HelperContext = NULL;
+    Socket->SharedData.AsyncDisabledEvents = -1;
+    NtClose(Socket->TdiAddressHandle);
+    Socket->TdiAddressHandle = NULL;
+    NtClose(Socket->TdiConnectionHandle);
+    Socket->TdiConnectionHandle = NULL;
+
+    /* Close the handle */
+    NtClose((HANDLE)Handle);
+
+    return NO_ERROR;
+}
+
+INT
+WSPAPI
+WSPBind(
+       SOCKET Handle, 
+       const struct sockaddr *SocketAddress, 
+       int SocketAddressLength, 
+       LPINT lpErrno)
+/*
+ * FUNCTION: Associates a local address with a socket
+ * ARGUMENTS:
+ *     s       = Socket descriptor
+ *     name    = Pointer to local address
+ *     namelen = Length of name
+ *     lpErrno = Address of buffer for error information
+ * RETURNS:
+ *     0, or SOCKET_ERROR if the socket could not be bound
+ */
+{
+       IO_STATUS_BLOCK                         IOSB;
+       PAFD_BIND_DATA                          BindData;
+       PSOCKET_INFORMATION                     Socket = NULL;
+       NTSTATUS                                        Status;
+       UCHAR                                           BindBuffer[0x1A];
+       SOCKADDR_INFO                           SocketInfo;
+       HANDLE                                  SockEvent;
+
+       Status = NtCreateEvent( &SockEvent, GENERIC_READ | GENERIC_WRITE,
+                               NULL, 1, FALSE );
+
+       if( !NT_SUCCESS(Status) ) return -1;
+
+       /* Get the Socket Structure associate to this Socket*/
+       Socket = GetSocketStructure(Handle);
+
+       /* Dynamic Structure...ugh */
+       BindData = (PAFD_BIND_DATA)BindBuffer;
+
+       /* Set up Address in TDI Format */
+       BindData->Address.TAAddressCount = 1;
+       BindData->Address.Address[0].AddressLength = SocketAddressLength - sizeof(SocketAddress->sa_family);
+       BindData->Address.Address[0].AddressType = SocketAddress->sa_family;
+       RtlCopyMemory (BindData->Address.Address[0].Address,
+                                       SocketAddress->sa_data,
+                                       SocketAddressLength - sizeof(SocketAddress->sa_family));
+       
+       /* Get Address Information */
+       Socket->HelperData->WSHGetSockaddrType ((PSOCKADDR)SocketAddress, 
+                                                                                       SocketAddressLength, 
+                                                                                       &SocketInfo);
+
+       /* Set the Share Type */
+       if (Socket->SharedData.ExclusiveAddressUse) {
+               BindData->ShareType = AFD_SHARE_EXCLUSIVE;
+       }
+       else if (SocketInfo.EndpointInfo == SockaddrEndpointInfoWildcard) {
+               BindData->ShareType = AFD_SHARE_WILDCARD;
+       }
+       else if (Socket->SharedData.ReuseAddresses) {
+               BindData->ShareType = AFD_SHARE_REUSE;
+       } else {
+               BindData->ShareType = AFD_SHARE_UNIQUE;
+       }
+
+       /* Send IOCTL */
+       Status = NtDeviceIoControlFile( (HANDLE)Socket->Handle,
+                                       SockEvent,
+                                       NULL,
+                                       NULL,
+                                       &IOSB,
+                                       IOCTL_AFD_BIND,
+                                       BindData,
+                                       0xA + Socket->SharedData.SizeOfLocalAddress, /* Can't figure out a way to calculate this in C*/
+                                       BindData,
+                                       0xA + Socket->SharedData.SizeOfLocalAddress); /* Can't figure out a way to calculate this C */
+        
+       /* Wait for return */
+       if (Status == STATUS_PENDING) {
+               WaitForSingleObject(SockEvent, INFINITE);
+       }         
+       
+       /* Set up Socket Data */
+       Socket->SharedData.State = SocketBound;
+       Socket->TdiAddressHandle = (HANDLE)IOSB.Information;
+
+       NtClose( SockEvent );
+
+       return MsafdReturnWithErrno
+           ( IOSB.Status, lpErrno, IOSB.Information, NULL );
+}
+
+int 
+WSPAPI
+WSPListen(
+       SOCKET Handle, 
+       int Backlog, 
+       LPINT lpErrno)
+{
+       IO_STATUS_BLOCK                         IOSB;
+       AFD_LISTEN_DATA                         ListenData;
+       PSOCKET_INFORMATION                     Socket = NULL;
+       HANDLE                                  SockEvent;
+       NTSTATUS                                Status;
+
+       Status = NtCreateEvent( &SockEvent, GENERIC_READ | GENERIC_WRITE,
+                               NULL, 1, FALSE );
+
+       if( !NT_SUCCESS(Status) ) return -1;
+
+       /* Get the Socket Structure associate to this Socket*/
+       Socket = GetSocketStructure(Handle);
+
+       /* Set Up Listen Structure */
+       ListenData.UseSAN = FALSE;
+       ListenData.UseDelayedAcceptance = Socket->SharedData.UseDelayedAcceptance;
+       ListenData.Backlog = Backlog;
+
+       /* Send IOCTL */
+       Status = NtDeviceIoControlFile( (HANDLE)Socket->Handle,
+                                       SockEvent,
+                                       NULL,
+                                       NULL,
+                                       &IOSB,
+                                       IOCTL_AFD_START_LISTEN,
+                                       &ListenData,
+                                       sizeof(ListenData),
+                                       NULL,
+                                       0);
+        
+       /* Wait for return */
+       if (Status == STATUS_PENDING) {
+               WaitForSingleObject(SockEvent, INFINITE);
+       }         
+
+       /* Set to Listening */
+       Socket->SharedData.Listening = TRUE;
+
+       NtClose( SockEvent );
+
+       return MsafdReturnWithErrno
+           ( IOSB.Status, lpErrno, IOSB.Information, NULL );
+}
+
+
+int
+WSPAPI 
+WSPSelect(
+       int nfds, 
+       fd_set *readfds,
+       fd_set *writefds, 
+       fd_set *exceptfds, 
+       struct timeval *timeout, 
+       LPINT lpErrno)
+{
+    IO_STATUS_BLOCK                    IOSB;
+    PAFD_POLL_INFO                     PollInfo;
+    NTSTATUS                           Status;
+    ULONG                              HandleCount, OutCount = 0;
+    ULONG                              PollBufferSize;
+    PVOID                              PollBuffer;
+    ULONG                              i, j = 0, x;
+    HANDLE                              SockEvent;
+    BOOL                                HandleCounted;
+    
+    Status = NtCreateEvent( &SockEvent, GENERIC_READ | GENERIC_WRITE,
+                           NULL, 1, FALSE );
+    
+    if( !NT_SUCCESS(Status) ) return SOCKET_ERROR;
+    
+    /* Find out how many sockets we have, and how large the buffer needs 
+     * to be */
+    
+    HandleCount = 
+       ( readfds ? readfds->fd_count : 0 ) + 
+       ( writefds ? writefds->fd_count : 0 ) + 
+       ( exceptfds ? exceptfds->fd_count : 0 );
+
+    if( HandleCount < 0 || nfds != 0 ) HandleCount = nfds * 3;
+
+    PollBufferSize = sizeof(*PollInfo) + 
+       (HandleCount * sizeof(AFD_HANDLE));
+    
+    AFD_DbgPrint(MID_TRACE,("HandleCount: %d BufferSize: %d\n", 
+                            HandleCount, PollBufferSize));
+
+    /* Allocate */
+    PollBuffer = HeapAlloc(GlobalHeap, 0, PollBufferSize);
+    PollInfo = (PAFD_POLL_INFO)PollBuffer;
+    
+    RtlZeroMemory( PollInfo, PollBufferSize );
+
+    /* Convert Timeout to NT Format */
+    if (timeout == NULL) {
+       PollInfo->Timeout.u.LowPart = -1;
+       PollInfo->Timeout.u.HighPart = 0x7FFFFFFF;
+    } else {
+       PollInfo->Timeout = RtlEnlargedIntegerMultiply
+           ((timeout->tv_sec * 1000) + timeout->tv_usec, -10000);
+    }
+    
+    /* Number of handles for AFD to Check */
+    PollInfo->HandleCount = HandleCount;
+    PollInfo->Exclusive = FALSE;
+    
+    if (readfds != NULL) {
+       for (i = 0; i < readfds->fd_count; i++, j++) {
+           PollInfo->Handles[j].Handle = readfds->fd_array[i];
+           PollInfo->Handles[j].Events = AFD_EVENT_RECEIVE | 
+                                         AFD_EVENT_DISCONNECT |
+                                         AFD_EVENT_ABORT |
+                                         AFD_EVENT_ACCEPT;
+       } 
+    }
+    if (writefds != NULL) {
+       for (i = 0; i < writefds->fd_count; i++, j++) {
+           PollInfo->Handles[j].Handle = writefds->fd_array[i];
+           PollInfo->Handles[j].Events = AFD_EVENT_SEND |
+                                         AFD_EVENT_CONNECT;
+       }       
+    }
+    if (exceptfds != NULL) {
+       for (i = 0; i < exceptfds->fd_count; i++, j++) {
+           PollInfo->Handles[j].Handle = exceptfds->fd_array[i];
+           PollInfo->Handles[j].Events = AFD_EVENT_OOB_RECEIVE |
+                                         AFD_EVENT_CONNECT_FAIL;
+       }
+    }
+    
+    /* Send IOCTL */
+    Status = NtDeviceIoControlFile( (HANDLE)PollInfo->Handles[0].Handle,
+                                   SockEvent,
+                                   NULL,
+                                   NULL,
+                                   &IOSB,
+                                   IOCTL_AFD_SELECT,
+                                   PollInfo,
+                                   PollBufferSize,
+                                   PollInfo,
+                                   PollBufferSize);
+
+    AFD_DbgPrint(MID_TRACE,("DeviceIoControlFile => %x\n", Status));
+    
+    /* Wait for Completition */
+    if (Status == STATUS_PENDING) {
+       WaitForSingleObject(SockEvent, INFINITE);
+    }
+    
+    /* Clear the Structures */
+    if( readfds ) FD_ZERO(readfds);
+    if( writefds ) FD_ZERO(writefds);
+    if( exceptfds ) FD_ZERO(exceptfds);
+    
+    /* Loop through return structure */
+    HandleCount = PollInfo->HandleCount;
+    
+    /* Return in FDSET Format */
+    for (i = 0; i < HandleCount; i++) {
+       HandleCounted = FALSE;
+       for(x = 1; x; x<<=1) {
+           switch (PollInfo->Handles[i].Events & x) {
+           case AFD_EVENT_RECEIVE: 
+           case AFD_EVENT_DISCONNECT: 
+           case AFD_EVENT_ABORT: 
+           case AFD_EVENT_ACCEPT: 
+           case AFD_EVENT_CLOSE:
+               AFD_DbgPrint(MID_TRACE,("Event %x on handle %x\n",
+                                       PollInfo->Handles[i].Events,
+                                       PollInfo->Handles[i].Handle));
+               if (! HandleCounted) {
+                   OutCount++;
+                   HandleCounted = TRUE;
+               }
+               if( readfds ) FD_SET(PollInfo->Handles[i].Handle, readfds);
+               break;
+
+           case AFD_EVENT_SEND: case AFD_EVENT_CONNECT:
+               AFD_DbgPrint(MID_TRACE,("Event %x on handle %x\n",
+                                       PollInfo->Handles[i].Events,
+                                       PollInfo->Handles[i].Handle));
+               if (! HandleCounted) {
+                   OutCount++;
+                   HandleCounted = TRUE;
+               }
+               if( writefds ) FD_SET(PollInfo->Handles[i].Handle, writefds);
+               break;
+               
+           case AFD_EVENT_OOB_RECEIVE: case AFD_EVENT_CONNECT_FAIL:
+               AFD_DbgPrint(MID_TRACE,("Event %x on handle %x\n",
+                                       PollInfo->Handles[i].Events,
+                                       PollInfo->Handles[i].Handle));
+               if (! HandleCounted) {
+                   OutCount++;
+                   HandleCounted = TRUE;
+               }
+               if( exceptfds ) FD_SET(PollInfo->Handles[i].Handle, exceptfds);
+               break;
+           }
+       }
+    }
+
+    NtClose( SockEvent );
+
+    AFD_DbgPrint(MID_TRACE,("lpErrno = %x\n", lpErrno));
+
+    if( lpErrno ) {
+       switch( IOSB.Status ) {
+       case STATUS_SUCCESS: 
+       case STATUS_TIMEOUT: *lpErrno = 0; break;
+       default: *lpErrno = WSAEINVAL; break;
+       }
+    }
+
+    AFD_DbgPrint(MID_TRACE,("%d events\n", OutCount));
+    
+    return OutCount;
+}
+
+SOCKET
+WSPAPI 
+WSPAccept(
+       SOCKET Handle, 
+       struct sockaddr *SocketAddress, 
+       int *SocketAddressLength, 
+       LPCONDITIONPROC lpfnCondition, 
+       DWORD_PTR dwCallbackData, 
+       LPINT lpErrno)
+{
+       IO_STATUS_BLOCK                         IOSB;
+       PAFD_RECEIVED_ACCEPT_DATA       ListenReceiveData;
+       AFD_ACCEPT_DATA                         AcceptData;
+       AFD_DEFER_ACCEPT_DATA           DeferData;
+       AFD_PENDING_ACCEPT_DATA         PendingAcceptData;
+       PSOCKET_INFORMATION                     Socket = NULL;
+       NTSTATUS                                        Status;
+       struct fd_set                           ReadSet;
+       struct timeval                          Timeout;
+       PVOID                                           PendingData = NULL;
+       ULONG                                           PendingDataLength = 0;
+       PVOID                                           CalleeDataBuffer;
+       WSABUF                                          CallerData, CalleeID, CallerID, CalleeData;
+       PSOCKADDR                                       RemoteAddress =  NULL;
+       GROUP                                           GroupID = 0;
+       ULONG                                           CallBack;
+       WSAPROTOCOL_INFOW                       ProtocolInfo;
+       SOCKET                                          AcceptSocket;
+       UCHAR                                           ReceiveBuffer[0x1A];
+       HANDLE                                  SockEvent;
+
+       Status = NtCreateEvent( &SockEvent, GENERIC_READ | GENERIC_WRITE,
+                               NULL, 1, FALSE );
+
+       if( !NT_SUCCESS(Status) ) {
+               MsafdReturnWithErrno( Status, lpErrno, 0, NULL );
+               return INVALID_SOCKET;
+       }
+
+       /* Dynamic Structure...ugh */
+       ListenReceiveData = (PAFD_RECEIVED_ACCEPT_DATA)ReceiveBuffer;
+
+       /* Get the Socket Structure associate to this Socket*/
+       Socket = GetSocketStructure(Handle);
+
+       /* If this is non-blocking, make sure there's something for us to accept */
+       FD_ZERO(&ReadSet);
+       FD_SET(Socket->Handle, &ReadSet);
+       Timeout.tv_sec=0;
+       Timeout.tv_usec=0;
+
+       WSPSelect(0, &ReadSet, NULL, NULL, &Timeout, NULL);
+
+       if (ReadSet.fd_array[0] != Socket->Handle) return 0;
+
+       /* Send IOCTL */
+       Status = NtDeviceIoControlFile( (HANDLE)Socket->Handle,
+                                       SockEvent,
+                                       NULL,
+                                       NULL,
+                                       &IOSB,
+                                       IOCTL_AFD_WAIT_FOR_LISTEN,
+                                       NULL,
+                                       0,
+                                       ListenReceiveData,
+                                       0xA + sizeof(*ListenReceiveData));
+       
+       /* Wait for return */
+       if (Status == STATUS_PENDING) {
+               WaitForSingleObject(SockEvent, INFINITE);
+               Status = IOSB.Status;
+       }
+
+       if (!NT_SUCCESS(Status)) {
+               NtClose( SockEvent );
+               MsafdReturnWithErrno( Status, lpErrno, 0, NULL );
+               return INVALID_SOCKET;
+       }
+
+       if (lpfnCondition != NULL) {
+               if ((Socket->SharedData.ServiceFlags1 & XP1_CONNECT_DATA) != 0) {
+                       /* Find out how much data is pending */
+                       PendingAcceptData.SequenceNumber = ListenReceiveData->SequenceNumber;
+                       PendingAcceptData.ReturnSize = TRUE;
+
+                       /* Send IOCTL */
+                       Status = NtDeviceIoControlFile( (HANDLE)Socket->Handle,
+                                                       SockEvent,
+                                                       NULL,
+                                                       NULL,
+                                                       &IOSB,
+                                                       IOCTL_AFD_GET_PENDING_CONNECT_DATA,
+                                                       &PendingAcceptData,
+                                                       sizeof(PendingAcceptData),
+                                                       &PendingAcceptData,
+                                                       sizeof(PendingAcceptData));
+        
+                       /* Wait for return */
+                       if (Status == STATUS_PENDING) {
+                               WaitForSingleObject(SockEvent, INFINITE);
+                               Status = IOSB.Status;
+                       }
+
+                       if (!NT_SUCCESS(Status)) {
+                               NtClose( SockEvent );
+                               MsafdReturnWithErrno( Status, lpErrno, 0, NULL );
+                               return INVALID_SOCKET;
+                       }
+
+                       /* How much data to allocate */
+                       PendingDataLength = IOSB.Information;
+
+                       if (PendingDataLength) {
+                               /* Allocate needed space */
+                               PendingData = HeapAlloc(GlobalHeap, 0, PendingDataLength);
+
+                               /* We want the data now */
+                               PendingAcceptData.ReturnSize = FALSE;
+
+                               /* Send IOCTL */
+                               Status = NtDeviceIoControlFile( (HANDLE)Socket->Handle,
+                                                               SockEvent,
+                                                               NULL,
+                                                               NULL,
+                                                               &IOSB,
+                                                               IOCTL_AFD_GET_PENDING_CONNECT_DATA,
+                                                               &PendingAcceptData,
+                                                               sizeof(PendingAcceptData),
+                                                               PendingData,
+                                                               PendingDataLength);
+
+                               /* Wait for return */
+                               if (Status == STATUS_PENDING) {
+                                       WaitForSingleObject(SockEvent, INFINITE);
+                                       Status = IOSB.Status;
+                               }
+
+                               if (!NT_SUCCESS(Status)) {
+                                       NtClose( SockEvent );
+                                       MsafdReturnWithErrno( Status, lpErrno, 0, NULL );
+                                       return INVALID_SOCKET;
+                               }
+                       }
+               }
+
+               if ((Socket->SharedData.ServiceFlags1 & XP1_QOS_SUPPORTED) != 0) {
+                       /* I don't support this yet */
+               }
+               
+               /* Build Callee ID */
+               CalleeID.buf = (PVOID)Socket->LocalAddress;
+               CalleeID.len = Socket->SharedData.SizeOfLocalAddress;
+
+               /* Set up Address in SOCKADDR Format */
+               RtlCopyMemory (RemoteAddress, 
+                               &ListenReceiveData->Address.Address[0].AddressType, 
+                               sizeof(*RemoteAddress));
+
+               /* Build Caller ID */
+               CallerID.buf = (PVOID)RemoteAddress;
+               CallerID.len = sizeof(*RemoteAddress);
+
+               /* Build Caller Data */
+               CallerData.buf = PendingData;
+               CallerData.len = PendingDataLength;
+
+               /* Check if socket supports Conditional Accept */
+               if (Socket->SharedData.UseDelayedAcceptance != 0) {
+                       /* Allocate Buffer for Callee Data */
+                       CalleeDataBuffer = HeapAlloc(GlobalHeap, 0, 4096);
+                       CalleeData.buf = CalleeDataBuffer;
+                       CalleeData.len = 4096;
+               } else {
+                       /* Nothing */
+                       CalleeData.buf = 0;
+                       CalleeData.len = 0;
+               }
+       
+               /* Call the Condition Function */
+               CallBack = (lpfnCondition)( &CallerID,
+                                               CallerData.buf == NULL
+                                               ? NULL
+                                               : & CallerData,
+                                               NULL,
+                                               NULL,
+                                               &CalleeID,
+                                               CalleeData.buf == NULL
+                                               ? NULL
+                                               : & CalleeData,
+                                               &GroupID,
+                                               dwCallbackData);
+
+               if (((CallBack == CF_ACCEPT) && GroupID) != 0) {
+                       /* TBD: Check for Validity */
+               }
+
+               if (CallBack == CF_ACCEPT) {
+
+                       if ((Socket->SharedData.ServiceFlags1 & XP1_QOS_SUPPORTED) != 0) {
+                               /* I don't support this yet */
+                       }
+
+                       if (CalleeData.buf) {
+                               // SockSetConnectData Sockets(SocketID), IOCTL_AFD_SET_CONNECT_DATA, CalleeData.Buffer, CalleeData.BuffSize, 0
+                       }
+               
+               } else {
+                       /* Callback rejected. Build Defer Structure */
+                       DeferData.SequenceNumber = ListenReceiveData->SequenceNumber;
+                       DeferData.RejectConnection = (CallBack == CF_REJECT);
+
+                       /* Send IOCTL */
+                       Status = NtDeviceIoControlFile( (HANDLE)Socket->Handle,
+                                                       SockEvent,
+                                                       NULL,
+                                                       NULL,
+                                                       &IOSB,
+                                                       IOCTL_AFD_DEFER_ACCEPT,
+                                                       &DeferData,
+                                                       sizeof(DeferData),
+                                                       NULL,
+                                                       0);
+
+                       /* Wait for return */
+                       if (Status == STATUS_PENDING) {
+                               WaitForSingleObject(SockEvent, INFINITE);
+                               Status = IOSB.Status;
+                       }
+
+                       NtClose( SockEvent );
+
+                       if (!NT_SUCCESS(Status)) {
+                               MsafdReturnWithErrno( Status, lpErrno, 0, NULL );
+                               return INVALID_SOCKET;
+                       }
+
+                       if (CallBack == CF_REJECT ) {
+                               *lpErrno = WSAECONNREFUSED;
+                               return INVALID_SOCKET;
+                       } else {
+                               *lpErrno = WSAECONNREFUSED;
+                               return INVALID_SOCKET;
+                       }
+               }
+       }
+       
+       /* Create a new Socket */
+       ProtocolInfo.dwCatalogEntryId = Socket->SharedData.CatalogEntryId;
+       ProtocolInfo.dwServiceFlags1 = Socket->SharedData.ServiceFlags1;
+       ProtocolInfo.dwProviderFlags = Socket->SharedData.ProviderFlags;
+
+       AcceptSocket = WSPSocket (Socket->SharedData.AddressFamily,
+                                 Socket->SharedData.SocketType, 
+                                 Socket->SharedData.Protocol, 
+                                 &ProtocolInfo,
+                                 GroupID, 
+                                 Socket->SharedData.CreateFlags, 
+                                 NULL);
+
+       /* Set up the Accept Structure */
+       AcceptData.ListenHandle = AcceptSocket;
+       AcceptData.SequenceNumber = ListenReceiveData->SequenceNumber;
+    
+       /* Send IOCTL to Accept */
+       Status = NtDeviceIoControlFile( (HANDLE)Socket->Handle,
+                                       SockEvent,
+                                       NULL,
+                                       NULL,
+                                       &IOSB,
+                                       IOCTL_AFD_ACCEPT,
+                                       &AcceptData,
+                                       sizeof(AcceptData),
+                                       NULL,
+                                       0);
+
+       /* Wait for return */
+       if (Status == STATUS_PENDING) {
+               WaitForSingleObject(SockEvent, INFINITE);
+               Status = IOSB.Status;
+       }
+       
+       if (!NT_SUCCESS(Status)) {
+               WSPCloseSocket( AcceptSocket, lpErrno );
+               MsafdReturnWithErrno( Status, lpErrno, 0, NULL );
+               return INVALID_SOCKET;
+       }
+
+       /* Return Address in SOCKADDR FORMAT */
+        if( SocketAddress ) {
+            RtlCopyMemory (SocketAddress, 
+                           &ListenReceiveData->Address.Address[0].AddressType, 
+                           sizeof(*RemoteAddress));
+            if( SocketAddressLength )
+                *SocketAddressLength = 
+                    ListenReceiveData->Address.Address[0].AddressLength;
+        }
+
+       NtClose( SockEvent );
+    /* Re-enable Async Event */
+    SockReenableAsyncSelectEvent(Socket, FD_ACCEPT);
+
+    AFD_DbgPrint(MID_TRACE,("Socket %x\n", AcceptSocket));
+    
+    *lpErrno = 0;
+    
+    /* Return Socket */
+    return AcceptSocket;
+}
+
+int
+WSPAPI 
+WSPConnect(
+       SOCKET Handle, 
+       const struct sockaddr * SocketAddress, 
+       int SocketAddressLength, 
+       LPWSABUF lpCallerData, 
+       LPWSABUF lpCalleeData, 
+       LPQOS lpSQOS, 
+       LPQOS lpGQOS, 
+       LPINT lpErrno)
+{
+       IO_STATUS_BLOCK                         IOSB;
+       PAFD_CONNECT_INFO                       ConnectInfo;
+       PSOCKET_INFORMATION                     Socket = NULL;
+       NTSTATUS                                        Status;
+       UCHAR                                           ConnectBuffer[0x22];
+       ULONG                                           ConnectDataLength;
+       ULONG                                           InConnectDataLength;
+       INT                                             BindAddressLength;
+       PSOCKADDR                                       BindAddress;
+       HANDLE                                  SockEvent;
+
+       Status = NtCreateEvent( &SockEvent, GENERIC_READ | GENERIC_WRITE,
+                               NULL, 1, FALSE );
+
+       if( !NT_SUCCESS(Status) ) return -1;
+
+       AFD_DbgPrint(MID_TRACE,("Called\n"));
+
+       /* Get the Socket Structure associate to this Socket*/
+       Socket = GetSocketStructure(Handle);
+
+       /* Bind us First */
+       if (Socket->SharedData.State == SocketOpen) {
+               
+               /* Get the Wildcard Address */
+               BindAddressLength = Socket->HelperData->MaxWSAddressLength;
+               BindAddress = HeapAlloc(GetProcessHeap(), 0, BindAddressLength);
+               Socket->HelperData->WSHGetWildcardSockaddr (Socket->HelperContext, 
+                                                                                                       BindAddress, 
+                                                                                                       &BindAddressLength);
+
+               /* Bind it */
+               WSPBind(Handle, BindAddress, BindAddressLength, NULL);
+       }
+
+       /* Set the Connect Data */
+       if (lpCallerData != NULL) {
+               ConnectDataLength = lpCallerData->len;
+               Status = NtDeviceIoControlFile((HANDLE)Handle,
+                                                                               SockEvent,
+                                                                               NULL,
+                                                                               NULL,
+                                                                               &IOSB,
+                                                                               IOCTL_AFD_SET_CONNECT_DATA,
+                                                                               lpCallerData->buf,
+                                                                               ConnectDataLength,
+                                                                               NULL,
+                                                                               0);
+               /* Wait for return */
+               if (Status == STATUS_PENDING) {
+                       WaitForSingleObject(SockEvent, INFINITE);
+                       Status = IOSB.Status;
+               }
+       }
+
+       /* Dynamic Structure...ugh */
+       ConnectInfo = (PAFD_CONNECT_INFO)ConnectBuffer;
+
+       /* Set up Address in TDI Format */
+       ConnectInfo->RemoteAddress.TAAddressCount = 1;
+       ConnectInfo->RemoteAddress.Address[0].AddressLength = SocketAddressLength - sizeof(SocketAddress->sa_family);
+       ConnectInfo->RemoteAddress.Address[0].AddressType = SocketAddress->sa_family;
+       RtlCopyMemory (ConnectInfo->RemoteAddress.Address[0].Address, 
+                                       SocketAddress->sa_data, 
+                                       SocketAddressLength - sizeof(SocketAddress->sa_family));
+
+    /* 
+     * Disable FD_WRITE and FD_CONNECT 
+     * The latter fixes a race condition where the FD_CONNECT is re-enabled
+     * at the end of this function right after the Async Thread disables it.
+     * This should only happen at the *next* WSPConnect
+     */
+    if (Socket->SharedData.AsyncEvents & FD_CONNECT) {
+        Socket->SharedData.AsyncDisabledEvents |= FD_CONNECT | FD_WRITE;
+    }
+    
+       /* Tell AFD that we want Connection Data back, have it allocate a buffer */
+       if (lpCalleeData != NULL) {
+               InConnectDataLength = lpCalleeData->len;
+               Status = NtDeviceIoControlFile((HANDLE)Handle,
+                                                                               SockEvent,
+                                                                               NULL,
+                                                                               NULL,
+                                                                               &IOSB,
+                                                                               IOCTL_AFD_SET_CONNECT_DATA_SIZE,
+                                                                               &InConnectDataLength,
+                                                                               sizeof(InConnectDataLength),
+                                                                               NULL,
+                                                                               0);
+
+               /* Wait for return */
+               if (Status == STATUS_PENDING) {
+                       WaitForSingleObject(SockEvent, INFINITE);
+                       Status = IOSB.Status;
+               }
+       }
+
+       /* AFD doesn't seem to care if these are invalid, but let's 0 them anyways */
+       ConnectInfo->Root = 0;
+       ConnectInfo->UseSAN = FALSE;
+       ConnectInfo->Unknown = 0;
+
+    /* FIXME: Handle Async Connect */
+    if (Socket->SharedData.NonBlocking) {
+        AFD_DbgPrint(MIN_TRACE, ("Async Connect UNIMPLEMENTED!\n"));
+    }
+               
+       /* Send IOCTL */
+       Status = NtDeviceIoControlFile((HANDLE)Handle,
+                                                                       SockEvent,
+                                                                       NULL,
+                                                                       NULL,
+                                                                       &IOSB,
+                                                                       IOCTL_AFD_CONNECT,
+                                                                       ConnectInfo,
+                                                                       0x22,
+                                                                       NULL,
+                                                                       0);
+       /* Wait for return */
+       if (Status == STATUS_PENDING) {
+               WaitForSingleObject(SockEvent, INFINITE);
+               Status = IOSB.Status;
+       }
+
+       /* Get any pending connect data */
+        if (lpCalleeData != NULL) {
+               Status = NtDeviceIoControlFile((HANDLE)Handle,
+                                                                               SockEvent,
+                                                                               NULL,
+                                                                               NULL,
+                                                                               &IOSB,
+                                                                               IOCTL_AFD_GET_CONNECT_DATA,
+                                                                               NULL,
+                                                                               0,
+                                                                               lpCalleeData->buf,
+                                                                               lpCalleeData->len);
+               /* Wait for return */
+               if (Status == STATUS_PENDING) {
+                       WaitForSingleObject(SockEvent, INFINITE);
+                       Status = IOSB.Status;
+               }
+        }
+
+    /* Re-enable Async Event */
+    SockReenableAsyncSelectEvent(Socket, FD_WRITE);
+    
+    /* FIXME: THIS IS NOT RIGHT!!! HACK HACK HACK! */
+    SockReenableAsyncSelectEvent(Socket, FD_CONNECT);
+    
+       AFD_DbgPrint(MID_TRACE,("Ending\n"));
+
+       NtClose( SockEvent );
+    
+    return MsafdReturnWithErrno( IOSB.Status, lpErrno, 0, NULL );
+}
+int
+WSPAPI 
+WSPShutdown(
+       SOCKET Handle, 
+       int HowTo, 
+       LPINT lpErrno)
+
+{
+       IO_STATUS_BLOCK                         IOSB;
+       AFD_DISCONNECT_INFO                     DisconnectInfo;
+       PSOCKET_INFORMATION                     Socket = NULL;
+       NTSTATUS                                        Status;
+       HANDLE                                  SockEvent;
+
+       Status = NtCreateEvent( &SockEvent, GENERIC_READ | GENERIC_WRITE,
+                               NULL, 1, FALSE );
+
+       if( !NT_SUCCESS(Status) ) return -1;
+
+       AFD_DbgPrint(MID_TRACE,("Called\n"));
+
+       /* Get the Socket Structure associate to this Socket*/
+       Socket = GetSocketStructure(Handle);
+
+       /* Set AFD Disconnect Type */
+    switch (HowTo) {
+
+               case SD_RECEIVE:
+                       DisconnectInfo.DisconnectType = AFD_DISCONNECT_RECV;
+                       Socket->SharedData.ReceiveShutdown = TRUE;
+                       break;
+               
+               case SD_SEND:
+               DisconnectInfo.DisconnectType= AFD_DISCONNECT_SEND;
+                       Socket->SharedData.SendShutdown = TRUE;
+                       break;
+
+               case SD_BOTH:
+                       DisconnectInfo.DisconnectType = AFD_DISCONNECT_RECV | AFD_DISCONNECT_SEND;
+                       Socket->SharedData.ReceiveShutdown = TRUE;
+                       Socket->SharedData.SendShutdown = TRUE;
+                       break;
+    }
+
+    DisconnectInfo.Timeout = RtlConvertLongToLargeInteger(-1);
+
+       /* Send IOCTL */
+       Status = NtDeviceIoControlFile((HANDLE)Handle,
+                                                                       SockEvent,
+                                                                       NULL,
+                                                                       NULL,
+                                                                       &IOSB,
+                                                                       IOCTL_AFD_DISCONNECT,
+                                                                       &DisconnectInfo,
+                                                                       sizeof(DisconnectInfo),
+                                                                       NULL,
+                                                                       0);
+
+       /* Wait for return */
+       if (Status == STATUS_PENDING) {
+               WaitForSingleObject(SockEvent, INFINITE);
+       }
+
+       AFD_DbgPrint(MID_TRACE,("Ending\n"));
+
+       NtClose( SockEvent );
+
+    return MsafdReturnWithErrno( IOSB.Status, lpErrno, 0, NULL );
+}
+
+
+INT
+WSPAPI
+WSPGetSockName(
+    IN      SOCKET Handle,
+    OUT     LPSOCKADDR Name,
+    IN OUT  LPINT NameLength,
+    OUT     LPINT lpErrno)
+{
+       IO_STATUS_BLOCK                         IOSB;
+       ULONG                                   TdiAddressSize;
+       PTDI_ADDRESS_INFO                       TdiAddress;
+       PTRANSPORT_ADDRESS                      SocketAddress;
+       PSOCKET_INFORMATION                     Socket = NULL;
+       NTSTATUS                                Status;
+       HANDLE                                  SockEvent;
+
+       Status = NtCreateEvent( &SockEvent, GENERIC_READ | GENERIC_WRITE,
+                               NULL, 1, FALSE );
+
+       if( !NT_SUCCESS(Status) ) return SOCKET_ERROR;
+
+       /* Get the Socket Structure associate to this Socket*/
+       Socket = GetSocketStructure(Handle);
+
+       /* Allocate a buffer for the address */
+       TdiAddressSize = FIELD_OFFSET(TDI_ADDRESS_INFO,
+                                     Address.Address[0].Address) +
+                        Socket->SharedData.SizeOfLocalAddress;
+       TdiAddress = HeapAlloc(GlobalHeap, 0, TdiAddressSize);
+
+       if ( TdiAddress == NULL ) {
+               NtClose( SockEvent );
+               *lpErrno = WSAENOBUFS;
+               return SOCKET_ERROR;
+       }
+
+       SocketAddress = &TdiAddress->Address;
+
+       /* Send IOCTL */
+       Status = NtDeviceIoControlFile( (HANDLE)Socket->Handle,
+                                       SockEvent,
+                                       NULL,
+                                       NULL,
+                                       &IOSB,
+                                       IOCTL_AFD_GET_SOCK_NAME,
+                                       NULL,
+                                       0,
+                                       TdiAddress,
+                                       TdiAddressSize);
+       
+       /* Wait for return */
+       if (Status == STATUS_PENDING) {
+               WaitForSingleObject(SockEvent, INFINITE);
+               Status = IOSB.Status;
+       }
+
+       NtClose( SockEvent );
+
+       if (NT_SUCCESS(Status)) {
+               if (*NameLength >= SocketAddress->Address[0].AddressLength) {
+                       Name->sa_family = SocketAddress->Address[0].AddressType;
+                       RtlCopyMemory (Name->sa_data,
+                                      SocketAddress->Address[0].Address, 
+                                      SocketAddress->Address[0].AddressLength);
+                       *NameLength = 2 + SocketAddress->Address[0].AddressLength;
+                        AFD_DbgPrint
+                            (MID_TRACE,
+                             ("NameLength %d Address: %x Port %x\n",
+                              *NameLength, 
+                              ((struct sockaddr_in *)Name)->sin_addr.s_addr,
+                              ((struct sockaddr_in *)Name)->sin_port));
+                       HeapFree(GlobalHeap, 0, TdiAddress);
+                       return 0;
+               } else {
+                       HeapFree(GlobalHeap, 0, TdiAddress);
+                       *lpErrno = WSAEFAULT;
+                       return SOCKET_ERROR;
+               }
+       }
+
+       return MsafdReturnWithErrno
+           ( IOSB.Status, lpErrno, 0, NULL );
+}
+
+
+INT
+WSPAPI
+WSPGetPeerName(
+    IN      SOCKET s, 
+    OUT     LPSOCKADDR Name, 
+    IN OUT  LPINT NameLength, 
+    OUT     LPINT lpErrno)
+{
+       IO_STATUS_BLOCK                         IOSB;
+       ULONG                                   TdiAddressSize;
+       PTDI_ADDRESS_INFO                       TdiAddress;
+       PTRANSPORT_ADDRESS                      SocketAddress;
+       PSOCKET_INFORMATION                     Socket = NULL;
+       NTSTATUS                                Status;
+       HANDLE                                  SockEvent;
+
+       Status = NtCreateEvent( &SockEvent, GENERIC_READ | GENERIC_WRITE,
+                               NULL, 1, FALSE );
+
+       if( !NT_SUCCESS(Status) ) return SOCKET_ERROR;
+
+       /* Get the Socket Structure associate to this Socket*/
+       Socket = GetSocketStructure(s);
+
+       /* Allocate a buffer for the address */
+       TdiAddressSize = FIELD_OFFSET(TDI_ADDRESS_INFO,
+                                     Address.Address[0].Address) +
+                        Socket->SharedData.SizeOfLocalAddress;
+       TdiAddress = HeapAlloc(GlobalHeap, 0, TdiAddressSize);
+
+       if ( TdiAddress == NULL ) {
+               NtClose( SockEvent );
+               *lpErrno = WSAENOBUFS;
+               return SOCKET_ERROR;
+       }
+
+       SocketAddress = &TdiAddress->Address;
+
+       /* Send IOCTL */
+       Status = NtDeviceIoControlFile( (HANDLE)Socket->Handle,
+                                       SockEvent,
+                                       NULL,
+                                       NULL,
+                                       &IOSB,
+                                       IOCTL_AFD_GET_PEER_NAME,
+                                       NULL,
+                                       0,
+                                       TdiAddress,
+                                       TdiAddressSize);
+       
+       /* Wait for return */
+       if (Status == STATUS_PENDING) {
+               WaitForSingleObject(SockEvent, INFINITE);
+               Status = IOSB.Status;
+       }
+
+       NtClose( SockEvent );
+
+       if (NT_SUCCESS(Status)) {
+               if (*NameLength >= SocketAddress->Address[0].AddressLength) {
+                       Name->sa_family = SocketAddress->Address[0].AddressType;
+                       RtlCopyMemory (Name->sa_data,
+                                      SocketAddress->Address[0].Address, 
+                                      SocketAddress->Address[0].AddressLength);
+                       *NameLength = 2 + SocketAddress->Address[0].AddressLength;
+                        AFD_DbgPrint
+                            (MID_TRACE,
+                             ("NameLength %d Address: %s Port %x\n",
+                              *NameLength, 
+                              ((struct sockaddr_in *)Name)->sin_addr.s_addr,
+                              ((struct sockaddr_in *)Name)->sin_port));
+                       HeapFree(GlobalHeap, 0, TdiAddress);
+                       return 0;
+               } else {
+                       HeapFree(GlobalHeap, 0, TdiAddress);
+                       *lpErrno = WSAEFAULT;
+                       return SOCKET_ERROR;
+               }
+       }
+
+       return MsafdReturnWithErrno
+           ( IOSB.Status, lpErrno, 0, NULL );    
+}
+
+INT
+WSPAPI
+WSPIoctl(
+    IN  SOCKET Handle,
+    IN  DWORD dwIoControlCode,
+    IN  LPVOID lpvInBuffer,
+    IN  DWORD cbInBuffer,
+    OUT LPVOID lpvOutBuffer,
+    IN  DWORD cbOutBuffer,
+    OUT LPDWORD lpcbBytesReturned,
+    IN  LPWSAOVERLAPPED lpOverlapped,
+    IN  LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,
+    IN  LPWSATHREADID lpThreadId,
+    OUT LPINT lpErrno)
+{
+    PSOCKET_INFORMATION                        Socket = NULL;
+
+    /* Get the Socket Structure associate to this Socket*/
+    Socket = GetSocketStructure(Handle);
+
+    switch( dwIoControlCode ) {
+    case FIONBIO:
+       if( cbInBuffer < sizeof(INT) ) return SOCKET_ERROR;
+       Socket->SharedData.NonBlocking = *((PINT)lpvInBuffer) ? 1 : 0;
+       AFD_DbgPrint(MID_TRACE,("[%x] Set nonblocking %d\n",
+                               Handle, Socket->SharedData.NonBlocking));
+       return 0;
+
+    default:
+       *lpErrno = WSAEINVAL;
+       return SOCKET_ERROR;
+    }
+}
+
+
+INT
+WSPAPI
+WSPGetSockOpt(
+    IN      SOCKET Handle,
+    IN      INT Level,
+    IN      INT OptionName,
+    OUT            CHAR FAR* OptionValue,
+    IN OUT  LPINT OptionLength,
+    OUT     LPINT lpErrno)
+{
+    PSOCKET_INFORMATION Socket = NULL;
+    PVOID Buffer;
+    INT BufferSize;
+    BOOLEAN BoolBuffer;
+
+    /* Get the Socket Structure associate to this Socket*/
+    Socket = GetSocketStructure(Handle);
+    if (Socket == NULL)
+    {
+        *lpErrno = WSAENOTSOCK;
+        return SOCKET_ERROR;
+    }
+
+    AFD_DbgPrint(MID_TRACE, ("Called\n"));
+
+    switch (Level)
+    {
+        case SOL_SOCKET:
+            switch (OptionName)
+            {
+                case SO_TYPE:
+                    Buffer = &Socket->SharedData.SocketType;
+                    BufferSize = sizeof(INT);
+                    break;
+
+                case SO_RCVBUF:
+                    Buffer = &Socket->SharedData.SizeOfRecvBuffer;
+                    BufferSize = sizeof(INT);
+                    break;
+
+                case SO_SNDBUF:
+                    Buffer = &Socket->SharedData.SizeOfSendBuffer;
+                    BufferSize = sizeof(INT);
+                    break;
+
+                case SO_ACCEPTCONN:
+                    BoolBuffer = Socket->SharedData.Listening;
+                    Buffer = &BoolBuffer;
+                    BufferSize = sizeof(BOOLEAN);
+                    break;
+
+                case SO_BROADCAST:
+                    BoolBuffer = Socket->SharedData.Broadcast;
+                    Buffer = &BoolBuffer;
+                    BufferSize = sizeof(BOOLEAN);
+                    break;
+
+                case SO_DEBUG:
+                    BoolBuffer = Socket->SharedData.Debug;
+                    Buffer = &BoolBuffer;
+                    BufferSize = sizeof(BOOLEAN);
+                    break;
+
+                /* case SO_CONDITIONAL_ACCEPT: */
+                case SO_DONTLINGER:
+                case SO_DONTROUTE:
+                case SO_ERROR:
+                case SO_GROUP_ID:
+                case SO_GROUP_PRIORITY:
+                case SO_KEEPALIVE:
+                case SO_LINGER:
+                case SO_MAX_MSG_SIZE:
+                case SO_OOBINLINE:
+                case SO_PROTOCOL_INFO:
+                case SO_REUSEADDR:
+                    AFD_DbgPrint(MID_TRACE, ("Unimplemented option (%x)\n",
+                                 OptionName));
+
+                default:
+                    *lpErrno = WSAEINVAL;
+                    return SOCKET_ERROR;
+            }
+            
+            if (*OptionLength < BufferSize)
+            {
+                *lpErrno = WSAEFAULT;
+                *OptionLength = BufferSize;
+                return SOCKET_ERROR;
+            }
+            RtlCopyMemory(OptionValue, Buffer, BufferSize);
+
+            return 0;
+
+        case IPPROTO_TCP: /* FIXME */
+        default:
+            *lpErrno = WSAEINVAL;
+            return SOCKET_ERROR;
+    }
+}
+
+
+INT
+WSPAPI
+WSPStartup(
+  IN  WORD wVersionRequested,
+  OUT LPWSPDATA lpWSPData,
+  IN  LPWSAPROTOCOL_INFOW lpProtocolInfo,
+  IN  WSPUPCALLTABLE UpcallTable,
+  OUT LPWSPPROC_TABLE lpProcTable)
+/*
+ * FUNCTION: Initialize service provider for a client
+ * ARGUMENTS:
+ *     wVersionRequested = Highest WinSock SPI version that the caller can use
+ *     lpWSPData         = Address of WSPDATA structure to initialize
+ *     lpProtocolInfo    = Pointer to structure that defines the desired protocol
+ *     UpcallTable       = Pointer to upcall table of the WinSock DLL
+ *     lpProcTable       = Address of procedure table to initialize
+ * RETURNS:
+ *     Status of operation
+ */
+{
+  NTSTATUS Status;
+
+  AFD_DbgPrint(MAX_TRACE, ("wVersionRequested (0x%X) \n", wVersionRequested));
+
+  Status = NO_ERROR;
+
+  Upcalls = UpcallTable;
+
+  if (Status == NO_ERROR) {
+    lpProcTable->lpWSPAccept = WSPAccept;
+    lpProcTable->lpWSPAddressToString = WSPAddressToString;
+    lpProcTable->lpWSPAsyncSelect = WSPAsyncSelect;
+    lpProcTable->lpWSPBind = WSPBind;
+    lpProcTable->lpWSPCancelBlockingCall = WSPCancelBlockingCall;
+    lpProcTable->lpWSPCleanup = WSPCleanup;
+    lpProcTable->lpWSPCloseSocket = WSPCloseSocket;
+    lpProcTable->lpWSPConnect = WSPConnect;
+    lpProcTable->lpWSPDuplicateSocket = WSPDuplicateSocket;
+    lpProcTable->lpWSPEnumNetworkEvents = WSPEnumNetworkEvents;
+    lpProcTable->lpWSPEventSelect = WSPEventSelect;
+    lpProcTable->lpWSPGetOverlappedResult = WSPGetOverlappedResult;
+    lpProcTable->lpWSPGetPeerName = WSPGetPeerName;
+    lpProcTable->lpWSPGetSockName = WSPGetSockName;
+    lpProcTable->lpWSPGetSockOpt = WSPGetSockOpt;
+    lpProcTable->lpWSPGetQOSByName = WSPGetQOSByName;
+    lpProcTable->lpWSPIoctl = WSPIoctl;
+    lpProcTable->lpWSPJoinLeaf = WSPJoinLeaf;
+    lpProcTable->lpWSPListen = WSPListen;
+    lpProcTable->lpWSPRecv = WSPRecv;
+    lpProcTable->lpWSPRecvDisconnect = WSPRecvDisconnect;
+    lpProcTable->lpWSPRecvFrom = WSPRecvFrom;
+    lpProcTable->lpWSPSelect = WSPSelect;
+    lpProcTable->lpWSPSend = WSPSend;
+    lpProcTable->lpWSPSendDisconnect = WSPSendDisconnect;
+    lpProcTable->lpWSPSendTo = WSPSendTo;
+    lpProcTable->lpWSPSetSockOpt = WSPSetSockOpt;
+    lpProcTable->lpWSPShutdown = WSPShutdown;
+    lpProcTable->lpWSPSocket = WSPSocket;
+    lpProcTable->lpWSPStringToAddress = WSPStringToAddress;
+
+    lpWSPData->wVersion     = MAKEWORD(2, 2);
+    lpWSPData->wHighVersion = MAKEWORD(2, 2);
+  }
+
+  AFD_DbgPrint(MAX_TRACE, ("Status (%d).\n", Status));
+
+  return Status;
+}
+
+
+INT
+WSPAPI
+WSPCleanup(
+  OUT LPINT lpErrno)
+/*
+ * FUNCTION: Cleans up service provider for a client
+ * ARGUMENTS:
+ *     lpErrno = Address of buffer for error information
+ * RETURNS:
+ *     0 if successful, or SOCKET_ERROR if not
+ */
+{
+  AFD_DbgPrint(MAX_TRACE, ("\n"));
+
+  AFD_DbgPrint(MAX_TRACE, ("Leaving.\n"));
+
+  *lpErrno = NO_ERROR;
+
+  return 0;
+}
+
+
+
+int 
+GetSocketInformation(
+       PSOCKET_INFORMATION Socket, 
+       ULONG AfdInformationClass, 
+       PULONG Ulong OPTIONAL, 
+       PLARGE_INTEGER LargeInteger OPTIONAL)
+{
+       IO_STATUS_BLOCK                         IOSB;
+       AFD_INFO                                        InfoData;
+       NTSTATUS                                        Status;
+       HANDLE                                  SockEvent;
+
+       Status = NtCreateEvent( &SockEvent, GENERIC_READ | GENERIC_WRITE,
+                               NULL, 1, FALSE );
+
+       if( !NT_SUCCESS(Status) ) return -1;
+
+       /* Set Info Class */
+       InfoData.InformationClass = AfdInformationClass;
+
+       /* Send IOCTL */
+       Status = NtDeviceIoControlFile( (HANDLE)Socket->Handle,
+                                       SockEvent,
+                                       NULL,
+                                       NULL,
+                                       &IOSB,
+                                       IOCTL_AFD_GET_INFO,
+                                       &InfoData,
+                                       sizeof(InfoData),
+                                       &InfoData,
+                                       sizeof(InfoData));
+
+       /* Wait for return */
+       if (Status == STATUS_PENDING) {
+               WaitForSingleObject(SockEvent, INFINITE);
+       }
+
+       /* Return Information */
+       *Ulong = InfoData.Information.Ulong;
+       if (LargeInteger != NULL) {
+               *LargeInteger = InfoData.Information.LargeInteger;
+       }
+
+       NtClose( SockEvent );
+
+       return 0;
+
+}
+
+
+int 
+SetSocketInformation(
+       PSOCKET_INFORMATION Socket, 
+       ULONG AfdInformationClass, 
+       PULONG Ulong OPTIONAL, 
+       PLARGE_INTEGER LargeInteger OPTIONAL)
+{
+       IO_STATUS_BLOCK                         IOSB;
+       AFD_INFO                                        InfoData;
+       NTSTATUS                                        Status;
+       HANDLE                                  SockEvent;
+
+       Status = NtCreateEvent( &SockEvent, GENERIC_READ | GENERIC_WRITE,
+                               NULL, 1, FALSE );
+
+       if( !NT_SUCCESS(Status) ) return -1;
+
+       /* Set Info Class */
+       InfoData.InformationClass = AfdInformationClass;
+
+       /* Set Information */
+       InfoData.Information.Ulong = *Ulong;
+       if (LargeInteger != NULL) {
+               InfoData.Information.LargeInteger = *LargeInteger;
+       }
+
+        AFD_DbgPrint(MID_TRACE,("XXX Info %x (Data %x)\n",
+                                AfdInformationClass, *Ulong));
+
+       /* Send IOCTL */
+       Status = NtDeviceIoControlFile( (HANDLE)Socket->Handle,
+                                       SockEvent,
+                                       NULL,
+                                       NULL,
+                                       &IOSB,
+                                       IOCTL_AFD_GET_INFO,
+                                       &InfoData,
+                                       sizeof(InfoData),
+                                       NULL,
+                                       0);
+
+       /* Wait for return */
+       if (Status == STATUS_PENDING) {
+               WaitForSingleObject(SockEvent, INFINITE);
+       }
+
+       NtClose( SockEvent );
+
+       return 0;
+
+}
+
+PSOCKET_INFORMATION 
+GetSocketStructure(
+       SOCKET Handle)
+{
+       ULONG   i;
+
+       for (i=0; i<SocketCount; i++) {
+               if (Sockets[i]->Handle == Handle) {
+                       return Sockets[i];
+               }
+       }
+       return 0;
+}
+
+int CreateContext(PSOCKET_INFORMATION Socket)
+{
+       IO_STATUS_BLOCK                         IOSB;
+       SOCKET_CONTEXT                          ContextData;
+       NTSTATUS                                Status;
+       HANDLE                                  SockEvent;
+
+       Status = NtCreateEvent( &SockEvent, GENERIC_READ | GENERIC_WRITE,
+                               NULL, 1, FALSE );
+
+       if( !NT_SUCCESS(Status) ) return -1;
+
+       /* Create Context */
+       ContextData.SharedData = Socket->SharedData;
+       ContextData.SizeOfHelperData = 0;
+       RtlCopyMemory (&ContextData.LocalAddress, 
+                                       Socket->LocalAddress, 
+                                       Socket->SharedData.SizeOfLocalAddress);
+       RtlCopyMemory (&ContextData.RemoteAddress, 
+                                       Socket->RemoteAddress, 
+                                       Socket->SharedData.SizeOfRemoteAddress);
+
+       /* Send IOCTL */
+       Status = NtDeviceIoControlFile( (HANDLE)Socket->Handle,
+                                       SockEvent,
+                                       NULL,
+                                       NULL,
+                                       &IOSB,
+                                       IOCTL_AFD_SET_CONTEXT,
+                                       &ContextData,
+                                       sizeof(ContextData),
+                                       NULL,
+                                       0);
+
+       /* Wait for Completition */
+       if (Status == STATUS_PENDING) {
+               WaitForSingleObject(SockEvent, INFINITE);
+       }
+       
+       NtClose( SockEvent );
+
+       return 0;
+}
+
+BOOLEAN SockCreateOrReferenceAsyncThread(VOID)
+{
+       HANDLE hAsyncThread;
+       DWORD AsyncThreadId;
+       HANDLE AsyncEvent;
+       OBJECT_HANDLE_ATTRIBUTE_INFORMATION HandleFlags;
+       NTSTATUS Status;
+
+       /* Check if the Thread Already Exists */
+       if (SockAsyncThreadRefCount) {
+               return TRUE;
+       }
+       
+       /* Create the Completion Port */
+       if (!SockAsyncCompletionPort) {
+               Status = NtCreateIoCompletion(&SockAsyncCompletionPort,
+                                             IO_COMPLETION_ALL_ACCESS,
+                                             NULL,
+                                             2); // Allow 2 threads only
+       
+               /* Protect Handle */    
+               HandleFlags.ProtectFromClose = TRUE;
+               HandleFlags.Inherit = FALSE;
+               Status = NtSetInformationObject(SockAsyncCompletionPort,
+                                               ObjectHandleInformation,
+                                               &HandleFlags,
+                                               sizeof(HandleFlags));
+       }
+    
+       /* Create the Async Event */
+       Status = NtCreateEvent(&AsyncEvent,
+                              EVENT_ALL_ACCESS,
+                              NULL,
+                              NotificationEvent,
+                              FALSE);
+    
+       /* Create the Async Thread */
+       hAsyncThread = CreateThread(NULL,
+                                   0,
+                                   (LPTHREAD_START_ROUTINE)SockAsyncThread,
+                                   NULL,
+                                   0,
+                                   &AsyncThreadId);
+
+       /* Close the Handle */
+       NtClose(hAsyncThread);
+
+       /* Increase the Reference Count */
+       SockAsyncThreadRefCount++;
+       return TRUE;
+}
+
+int SockAsyncThread(PVOID ThreadParam)
+{
+       PVOID AsyncContext;
+       PASYNC_COMPLETION_ROUTINE AsyncCompletionRoutine;
+       IO_STATUS_BLOCK IOSB;
+       NTSTATUS Status;
+                          
+       /* Make the Thread Higher Priority */
+       SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
+       
+       /* Do a KQUEUE/WorkItem Style Loop, thanks to IoCompletion Ports */
+       do {
+               Status =  NtRemoveIoCompletion (SockAsyncCompletionPort,
+                                               (PVOID*)&AsyncCompletionRoutine,
+                                               &AsyncContext,
+                                               &IOSB,
+                                               NULL);
+                                               
+               /* Call the Async Function */
+               if (NT_SUCCESS(Status)) {
+                       (*AsyncCompletionRoutine)(AsyncContext, &IOSB);
+               } else {
+                       /* It Failed, sleep for a second */
+                       Sleep(1000);
+               }
+       } while ((Status != STATUS_TIMEOUT));
+
+    /* The Thread has Ended */
+    return 0;
+}
+
+BOOLEAN SockGetAsyncSelectHelperAfdHandle(VOID)
+{
+       UNICODE_STRING AfdHelper;
+       OBJECT_ATTRIBUTES ObjectAttributes;
+       IO_STATUS_BLOCK IoSb;
+       NTSTATUS Status;
+       FILE_COMPLETION_INFORMATION CompletionInfo;
+       OBJECT_HANDLE_ATTRIBUTE_INFORMATION HandleFlags;
+
+       /* First, make sure we're not already intialized */
+       if (SockAsyncHelperAfdHandle) {
+               return TRUE;
+       }
+
+       /* Set up Handle Name and Object */
+       RtlInitUnicodeString(&AfdHelper, L"\\Device\\Afd\\AsyncSelectHlp" );
+       InitializeObjectAttributes(&ObjectAttributes,
+                                  &AfdHelper,
+                                  OBJ_INHERIT | OBJ_CASE_INSENSITIVE,
+                                  NULL,
+                                  NULL);
+
+       /* Open the Handle to AFD */
+       Status = NtCreateFile(&SockAsyncHelperAfdHandle,
+                             GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
+                             &ObjectAttributes,
+                             &IoSb,
+                             NULL,
+                             0,
+                             FILE_SHARE_READ | FILE_SHARE_WRITE,
+                             FILE_OPEN_IF,
+                             0,
+                             NULL,
+                             0);
+
+       /* 
+        * Now Set up the Completion Port Information 
+        * This means that whenever a Poll is finished, the routine will be executed
+        */
+       CompletionInfo.Port = SockAsyncCompletionPort;
+       CompletionInfo.Key = SockAsyncSelectCompletionRoutine;
+       Status = NtSetInformationFile(SockAsyncHelperAfdHandle,
+                                     &IoSb,
+                                     &CompletionInfo,
+                                     sizeof(CompletionInfo),
+                                     FileCompletionInformation);
+                                     
+                                     
+       /* Protect the Handle */
+       HandleFlags.ProtectFromClose = TRUE;
+       HandleFlags.Inherit = FALSE;
+       Status = NtSetInformationObject(SockAsyncCompletionPort,
+                                       ObjectHandleInformation,
+                                       &HandleFlags,
+                                       sizeof(HandleFlags));
+
+
+       /* Set this variable to true so that Send/Recv/Accept will know wether to renable disabled events */
+       SockAsyncSelectCalled = TRUE;
+       return TRUE;
+}
+
+VOID SockAsyncSelectCompletionRoutine(PVOID Context, PIO_STATUS_BLOCK IoStatusBlock)
+{
+
+       PASYNC_DATA AsyncData = Context;
+       PSOCKET_INFORMATION Socket;
+       ULONG x;
+    
+       /* Get the Socket */
+       Socket = AsyncData->ParentSocket;
+       
+       /* Check if the Sequence  Number Changed behind our back */
+       if (AsyncData->SequenceNumber != Socket->SharedData.SequenceNumber ){
+               return;
+       }
+
+       /* Check we were manually called b/c of a failure */
+       if (!NT_SUCCESS(IoStatusBlock->Status)) {
+               /* FIXME: Perform Upcall */
+               return;
+       }
+
+       for (x = 1; x; x<<=1) {
+               switch (AsyncData->AsyncSelectInfo.Handles[0].Events & x) {
+                       case AFD_EVENT_RECEIVE:
+                               if (0 != (Socket->SharedData.AsyncEvents & FD_READ) && 0 == (Socket->SharedData.AsyncDisabledEvents & FD_READ)) {
+                                       /* Make the Notifcation */
+                                       (Upcalls.lpWPUPostMessage)(Socket->SharedData.hWnd,
+                                                                  Socket->SharedData.wMsg,
+                                                                  Socket->Handle,
+                                                                  WSAMAKESELECTREPLY(FD_READ, 0));
+                                       /* Disable this event until the next read(); */
+                                       Socket->SharedData.AsyncDisabledEvents |= FD_READ;
+                               }
+                               break;
+                       
+                       case AFD_EVENT_OOB_RECEIVE:
+                               if (0 != (Socket->SharedData.AsyncEvents & FD_OOB) && 0 == (Socket->SharedData.AsyncDisabledEvents & FD_OOB)) {
+                                       /* Make the Notifcation */
+                                       (Upcalls.lpWPUPostMessage)(Socket->SharedData.hWnd,
+                                                                  Socket->SharedData.wMsg,
+                                                                  Socket->Handle,
+                                                                  WSAMAKESELECTREPLY(FD_OOB, 0));
+                                       /* Disable this event until the next read(); */
+                                       Socket->SharedData.AsyncDisabledEvents |= FD_OOB;
+                               }
+                               break;
+               
+                       case AFD_EVENT_SEND:
+                               if (0 != (Socket->SharedData.AsyncEvents & FD_WRITE) && 0 == (Socket->SharedData.AsyncDisabledEvents & FD_WRITE)) {
+                                       /* Make the Notifcation */
+                                       (Upcalls.lpWPUPostMessage)(Socket->SharedData.hWnd,
+                                                                  Socket->SharedData.wMsg,
+                                                                  Socket->Handle,
+                                                                  WSAMAKESELECTREPLY(FD_WRITE, 0));
+                                       /* Disable this event until the next write(); */
+                                       Socket->SharedData.AsyncDisabledEvents |= FD_WRITE;
+                               }
+                               break;
+
+            /* FIXME: THIS IS NOT RIGHT!!! HACK HACK HACK! */
+                       case AFD_EVENT_CONNECT:
+                               if (0 != (Socket->SharedData.AsyncEvents & FD_CONNECT) && 0 == (Socket->SharedData.AsyncDisabledEvents & FD_CONNECT)) {
+                                       /* Make the Notifcation */
+                                       (Upcalls.lpWPUPostMessage)(Socket->SharedData.hWnd,
+                                                                  Socket->SharedData.wMsg,
+                                                                  Socket->Handle,
+                                                                  WSAMAKESELECTREPLY(FD_CONNECT, 0));
+                                       /* Disable this event forever; */
+                                       Socket->SharedData.AsyncDisabledEvents |= FD_CONNECT;
+                               }
+                               break;
+                               
+                       case AFD_EVENT_ACCEPT:
+                               if (0 != (Socket->SharedData.AsyncEvents & FD_ACCEPT) && 0 == (Socket->SharedData.AsyncDisabledEvents & FD_ACCEPT)) {
+                                       /* Make the Notifcation */
+                                       (Upcalls.lpWPUPostMessage)(Socket->SharedData.hWnd,
+                                                                  Socket->SharedData.wMsg,
+                                                                  Socket->Handle,
+                                                                  WSAMAKESELECTREPLY(FD_ACCEPT, 0));
+                                       /* Disable this event until the next accept(); */
+                                       Socket->SharedData.AsyncDisabledEvents |= FD_ACCEPT;
+                               }
+                               break;
+
+                       case AFD_EVENT_DISCONNECT:
+                       case AFD_EVENT_ABORT:
+                       case AFD_EVENT_CLOSE:
+                               if (0 != (Socket->SharedData.AsyncEvents & FD_CLOSE) && 0 == (Socket->SharedData.AsyncDisabledEvents & FD_CLOSE)) {
+                                       /* Make the Notifcation */
+                                       (Upcalls.lpWPUPostMessage)(Socket->SharedData.hWnd,
+                                                                  Socket->SharedData.wMsg,
+                                                                  Socket->Handle,
+                                                                  WSAMAKESELECTREPLY(FD_CLOSE, 0));
+                                       /* Disable this event forever; */
+                                       Socket->SharedData.AsyncDisabledEvents |= FD_CLOSE;
+                               }
+                               break;
+
+                       /* FIXME: Support QOS */
+               }
+       }
+       
+       /* Check if there are any events left for us to check */
+       if ((Socket->SharedData.AsyncEvents & (~Socket->SharedData.AsyncDisabledEvents)) == 0 ) {
+               return;
+       }
+
+       /* Keep Polling */
+       SockProcessAsyncSelect(Socket, AsyncData);
+       return;
+}
+
+VOID SockProcessAsyncSelect(PSOCKET_INFORMATION Socket, PASYNC_DATA AsyncData)
+{
+
+       ULONG lNetworkEvents;
+       NTSTATUS Status;
+
+       /* Set up the Async Data Event Info */
+       AsyncData->AsyncSelectInfo.Timeout.HighPart = 0x7FFFFFFF;
+       AsyncData->AsyncSelectInfo.Timeout.LowPart = 0xFFFFFFFF;
+       AsyncData->AsyncSelectInfo.HandleCount = 1;
+       AsyncData->AsyncSelectInfo.Exclusive = TRUE;
+       AsyncData->AsyncSelectInfo.Handles[0].Handle = Socket->Handle;
+       AsyncData->AsyncSelectInfo.Handles[0].Events = 0;
+
+       /* Remove unwanted events */
+       lNetworkEvents = Socket->SharedData.AsyncEvents & (~Socket->SharedData.AsyncDisabledEvents);
+
+       /* Set Events to wait for */
+       if (lNetworkEvents & FD_READ) {
+               AsyncData->AsyncSelectInfo.Handles[0].Events |= AFD_EVENT_RECEIVE;
+       }
+
+       if (lNetworkEvents & FD_WRITE) {
+               AsyncData->AsyncSelectInfo.Handles[0].Events |= AFD_EVENT_SEND;
+       }
+
+       if (lNetworkEvents & FD_OOB) {
+               AsyncData->AsyncSelectInfo.Handles[0].Events |= AFD_EVENT_OOB_RECEIVE;
+       }
+
+       if (lNetworkEvents & FD_ACCEPT) {
+               AsyncData->AsyncSelectInfo.Handles[0].Events |= AFD_EVENT_ACCEPT;
+       }
+
+     /* FIXME: THIS IS NOT RIGHT!!! HACK HACK HACK! */
+       if (lNetworkEvents & FD_CONNECT) {
+               AsyncData->AsyncSelectInfo.Handles[0].Events |= AFD_EVENT_CONNECT | AFD_EVENT_CONNECT_FAIL;
+       }
+
+       if (lNetworkEvents & FD_CLOSE) {
+               AsyncData->AsyncSelectInfo.Handles[0].Events |= AFD_EVENT_DISCONNECT | AFD_EVENT_ABORT;
+       }
+
+       if (lNetworkEvents & FD_QOS) {
+               AsyncData->AsyncSelectInfo.Handles[0].Events |= AFD_EVENT_QOS;
+       }
+
+       if (lNetworkEvents & FD_GROUP_QOS) {
+               AsyncData->AsyncSelectInfo.Handles[0].Events |= AFD_EVENT_GROUP_QOS;
+       }
+       
+       /* Send IOCTL */
+       Status = NtDeviceIoControlFile (SockAsyncHelperAfdHandle,
+                                       NULL,
+                                       NULL,
+                                       AsyncData,
+                                       &AsyncData->IoStatusBlock,
+                                       IOCTL_AFD_SELECT,
+                                       &AsyncData->AsyncSelectInfo,
+                                       sizeof(AsyncData->AsyncSelectInfo),
+                                       &AsyncData->AsyncSelectInfo,
+                                       sizeof(AsyncData->AsyncSelectInfo));
+
+       /* I/O Manager Won't call the completion routine, let's do it manually */
+       if (NT_SUCCESS(Status)) {
+               return;
+       } else {
+               AsyncData->IoStatusBlock.Status = Status;
+               SockAsyncSelectCompletionRoutine(AsyncData, &AsyncData->IoStatusBlock);
+       }
+}
+
+VOID SockProcessQueuedAsyncSelect(PVOID Context, PIO_STATUS_BLOCK IoStatusBlock)
+{
+       PASYNC_DATA AsyncData = Context;
+       BOOL FreeContext = TRUE;
+       PSOCKET_INFORMATION Socket;
+
+       /* Get the Socket */    
+       Socket = AsyncData->ParentSocket;
+
+       /* If someone closed it, stop the function */
+       if (Socket->SharedData.State != SocketClosed) {
+               /* Check if the Sequence Number changed by now, in which case quit */
+               if (AsyncData->SequenceNumber == Socket->SharedData.SequenceNumber) {
+                       /* Do the actuall select, if needed */
+                       if ((Socket->SharedData.AsyncEvents & (~Socket->SharedData.AsyncDisabledEvents))) {
+                               SockProcessAsyncSelect(Socket, AsyncData);
+                               FreeContext = FALSE;
+                       }
+               }
+       }
+       
+       /* Free the Context */
+       if (FreeContext) {
+               HeapFree(GetProcessHeap(), 0, AsyncData);
+       }
+
+       return;
+}
+
+VOID
+SockReenableAsyncSelectEvent (
+    IN PSOCKET_INFORMATION Socket,
+    IN ULONG Event
+    )
+{
+    PASYNC_DATA AsyncData;
+
+    /* Make sure the event is actually disabled */
+    if (!(Socket->SharedData.AsyncDisabledEvents & Event)) {
+        return;
+    }
+
+    /* Re-enable it */
+    Socket->SharedData.AsyncDisabledEvents &= ~Event;
+
+    /* Return if no more events are being polled */
+    if ((Socket->SharedData.AsyncEvents & (~Socket->SharedData.AsyncDisabledEvents)) == 0 ) {
+        return;
+    }
+
+    /* Wait on new events */
+    AsyncData = HeapAlloc(GetProcessHeap(), 0, sizeof(ASYNC_DATA));
+
+    /* Create the Asynch Thread if Needed */  
+    SockCreateOrReferenceAsyncThread();
+
+    /* Increase the sequence number to stop anything else */
+    Socket->SharedData.SequenceNumber++;
+    
+    /* Set up the Async Data */
+    AsyncData->ParentSocket = Socket;
+    AsyncData->SequenceNumber = Socket->SharedData.SequenceNumber;
+
+    /* Begin Async Select by using I/O Completion */
+    NtSetIoCompletion(SockAsyncCompletionPort,
+                      (PVOID)&SockProcessQueuedAsyncSelect,
+                      AsyncData,
+                      0,
+                      0);
+    
+    /* All done */
+    return;
+}
+
+BOOL
+STDCALL
+DllMain(HANDLE hInstDll,
+        ULONG dwReason,
+        PVOID Reserved)
+{
+  
+    switch (dwReason) {
+    case DLL_PROCESS_ATTACH:
+
+               AFD_DbgPrint(MAX_TRACE, ("Loading MSAFD.DLL \n"));
+
+        /* Don't need thread attach notifications
+           so disable them to improve performance */
+        DisableThreadLibraryCalls(hInstDll);
+
+               /* List of DLL Helpers */
+        InitializeListHead(&SockHelpersListHead);
+
+               /* Heap to use when allocating */
+        GlobalHeap = GetProcessHeap();
+
+               /* Allocate Heap for 1024 Sockets, can be expanded later */
+               Sockets = HeapAlloc(GetProcessHeap(), 0, sizeof(PSOCKET_INFORMATION) * 1024);
+
+               AFD_DbgPrint(MAX_TRACE, ("MSAFD.DLL has been loaded\n"));
+
+        break;
+
+    case DLL_THREAD_ATTACH:
+        break;
+
+    case DLL_THREAD_DETACH:
+        break;
+
+    case DLL_PROCESS_DETACH:
+        break;
+    }
+
+    AFD_DbgPrint(MAX_TRACE, ("DllMain of msafd.dll (leaving)\n"));
+
+    return TRUE;
+}
+
+/* EOF */
+
+
diff --git a/reactos/dll/msafd/misc/event.c b/reactos/dll/msafd/misc/event.c
new file mode 100644 (file)
index 0000000..9719258
--- /dev/null
@@ -0,0 +1,229 @@
+/*
+ * COPYRIGHT:   See COPYING in the top level directory
+ * PROJECT:     ReactOS Ancillary Function Driver DLL
+ * FILE:        misc/event.c
+ * PURPOSE:     Event handling
+ * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
+ *                             Alex Ionescu (alex@relsoft.net)
+ * REVISIONS:
+ *   CSH 15/06-2001 Created
+ *      Alex 16/07/2004 - Complete Rewrite
+ */
+
+#include <msafd.h>
+
+#include <debug.h>
+
+int 
+WSPAPI 
+WSPEventSelect(
+       SOCKET Handle, 
+       WSAEVENT hEventObject, 
+       long lNetworkEvents, 
+       LPINT lpErrno)
+{
+       IO_STATUS_BLOCK                         IOSB;
+       AFD_EVENT_SELECT_INFO           EventSelectInfo;
+       PSOCKET_INFORMATION                     Socket = NULL;
+       NTSTATUS                                        Status;
+       ULONG                                           BlockMode;
+       HANDLE                                  SockEvent;
+
+       Status = NtCreateEvent( &SockEvent, GENERIC_READ | GENERIC_WRITE,
+                               NULL, 1, FALSE );
+
+       if( !NT_SUCCESS(Status) ) return -1;
+
+       /* Get the Socket Structure associate to this Socket*/
+       Socket = GetSocketStructure(Handle);
+
+       /* Set Socket to Non-Blocking */
+       BlockMode = 1;
+       SetSocketInformation(Socket, AFD_INFO_BLOCKING_MODE, &BlockMode, NULL);
+       Socket->SharedData.NonBlocking = TRUE;
+
+       /* Deactivate Async Select if there is one */
+       if (Socket->EventObject) {
+               Socket->SharedData.hWnd = NULL;
+               Socket->SharedData.wMsg = 0;
+               Socket->SharedData.AsyncEvents = 0;
+               Socket->SharedData.SequenceNumber++; // This will kill Async Select after the next completion
+       }
+
+       /* Set Structure Info */
+       EventSelectInfo.EventObject = hEventObject;
+       EventSelectInfo.Events = 0;
+
+       /* Set Events to wait for */
+       if (lNetworkEvents & FD_READ) {
+               EventSelectInfo.Events |= AFD_EVENT_RECEIVE;
+    }
+
+    if (lNetworkEvents & FD_WRITE) {
+       EventSelectInfo.Events |= AFD_EVENT_SEND;
+    }
+
+    if (lNetworkEvents & FD_OOB) {
+        EventSelectInfo.Events |= AFD_EVENT_OOB_RECEIVE;
+    }
+
+    if (lNetworkEvents & FD_ACCEPT) {
+       EventSelectInfo.Events |= AFD_EVENT_ACCEPT;
+    }
+
+    if (lNetworkEvents & FD_CONNECT) {
+        EventSelectInfo.Events |= AFD_EVENT_CONNECT | AFD_EVENT_CONNECT_FAIL;
+    }
+
+    if (lNetworkEvents & FD_CLOSE) {
+       EventSelectInfo.Events |= AFD_EVENT_DISCONNECT | AFD_EVENT_ABORT;
+    }
+
+    if (lNetworkEvents & FD_QOS) {
+       EventSelectInfo.Events |= AFD_EVENT_QOS;
+    }
+
+    if (lNetworkEvents & FD_GROUP_QOS) {
+       EventSelectInfo.Events |= AFD_EVENT_GROUP_QOS;
+    }
+
+    /* Send IOCTL */
+    Status = NtDeviceIoControlFile((HANDLE)Handle,
+                                  SockEvent,
+                                  NULL,
+                                  NULL,
+                                  &IOSB,
+                                  IOCTL_AFD_EVENT_SELECT,
+                                  &EventSelectInfo,
+                                  sizeof(EventSelectInfo),
+                                  NULL,
+                                  0);
+
+    AFD_DbgPrint(MID_TRACE,("AFD: %x\n", Status));
+
+    /* Wait for return */
+    if (Status == STATUS_PENDING) {
+       WaitForSingleObject(SockEvent, INFINITE);
+    }
+
+    AFD_DbgPrint(MID_TRACE,("Waited\n"));
+
+    NtClose( SockEvent );
+
+    AFD_DbgPrint(MID_TRACE,("Closed event\n"));
+
+    /* Set Socket Data*/
+    Socket->EventObject = hEventObject;
+    Socket->NetworkEvents = lNetworkEvents;
+
+    AFD_DbgPrint(MID_TRACE,("Leaving\n"));
+
+    return 0;
+}
+
+
+INT
+WSPAPI
+WSPEnumNetworkEvents(
+  IN  SOCKET Handle, 
+  IN  WSAEVENT hEventObject, 
+  OUT LPWSANETWORKEVENTS lpNetworkEvents, 
+  OUT LPINT lpErrno)
+{
+    AFD_ENUM_NETWORK_EVENTS_INFO EnumReq;
+    IO_STATUS_BLOCK                            IOSB;
+    PSOCKET_INFORMATION                        Socket = NULL;
+    NTSTATUS                                   Status;
+    HANDLE                                  SockEvent;
+
+    AFD_DbgPrint(MID_TRACE,("Called (lpNetworkEvents %x)\n", lpNetworkEvents));
+
+    Status = NtCreateEvent( &SockEvent, GENERIC_READ | GENERIC_WRITE,
+                           NULL, 1, FALSE );
+
+    if( !NT_SUCCESS(Status) ) {
+       AFD_DbgPrint(MID_TRACE,("Could not make an event %x\n", Status));
+       return -1;
+    }
+
+    /* Get the Socket Structure associate to this Socket*/
+    Socket = GetSocketStructure(Handle);
+
+    EnumReq.Event = hEventObject;
+
+    /* Send IOCTL */
+    Status = NtDeviceIoControlFile((HANDLE)Handle,
+                                  SockEvent,
+                                  NULL,
+                                  NULL,
+                                  &IOSB,
+                                  IOCTL_AFD_ENUM_NETWORK_EVENTS,
+                                  &EnumReq,
+                                  sizeof(EnumReq),
+                                  NULL,
+                                  0);
+
+    AFD_DbgPrint(MID_TRACE,("AFD: %x\n", Status));
+
+    /* Wait for return */
+    if (Status == STATUS_PENDING) {
+       WaitForSingleObject(SockEvent, INFINITE);
+       Status = STATUS_SUCCESS;
+    }
+
+    AFD_DbgPrint(MID_TRACE,("Waited\n"));
+
+    NtClose( SockEvent );
+
+    AFD_DbgPrint(MID_TRACE,("Closed event\n"));
+    AFD_DbgPrint(MID_TRACE,("About to touch struct at %x (%d)\n", 
+                           lpNetworkEvents, sizeof(*lpNetworkEvents)));
+
+    lpNetworkEvents->lNetworkEvents = 0;
+
+    AFD_DbgPrint(MID_TRACE,("Zeroed struct\n"));
+
+    /* Set Events to wait for */
+    if (EnumReq.PollEvents & AFD_EVENT_RECEIVE) {
+       lpNetworkEvents->lNetworkEvents |= FD_READ;
+    }
+
+    if (EnumReq.PollEvents & AFD_EVENT_SEND) {
+       lpNetworkEvents->lNetworkEvents |= FD_WRITE;
+    }
+
+    if (EnumReq.PollEvents & AFD_EVENT_OOB_RECEIVE) {
+        lpNetworkEvents->lNetworkEvents |= FD_OOB;
+    }
+
+    if (EnumReq.PollEvents & AFD_EVENT_ACCEPT) {
+       lpNetworkEvents->lNetworkEvents |= FD_ACCEPT;
+    }
+
+    if (EnumReq.PollEvents & 
+       (AFD_EVENT_CONNECT | AFD_EVENT_CONNECT_FAIL)) {
+        lpNetworkEvents->lNetworkEvents |= FD_CONNECT;
+    }
+
+    if (EnumReq.PollEvents & 
+       (AFD_EVENT_DISCONNECT | AFD_EVENT_ABORT)) {
+       lpNetworkEvents->lNetworkEvents |= FD_CLOSE;
+    }
+
+    if (EnumReq.PollEvents & AFD_EVENT_QOS) {
+       lpNetworkEvents->lNetworkEvents |= FD_QOS;
+    }
+
+    if (EnumReq.PollEvents & AFD_EVENT_GROUP_QOS) {
+       lpNetworkEvents->lNetworkEvents |= FD_GROUP_QOS;
+    }
+
+    if( NT_SUCCESS(Status) ) *lpErrno = 0;
+    else *lpErrno = WSAEINVAL;
+
+    AFD_DbgPrint(MID_TRACE,("Leaving\n"));
+
+    return 0;
+}
+
+/* EOF */
diff --git a/reactos/dll/msafd/misc/helpers.c b/reactos/dll/msafd/misc/helpers.c
new file mode 100644 (file)
index 0000000..f0bb6a9
--- /dev/null
@@ -0,0 +1,520 @@
+/*
+ * COPYRIGHT:   See COPYING in the top level directory
+ * PROJECT:     ReactOS Ancillary Function Driver DLL
+ * FILE:        misc/helpers.c
+ * PURPOSE:     Helper DLL management
+ * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
+ *                             Alex Ionescu (alex@relsoft.net)
+ * REVISIONS:
+ *   CSH 01/09-2000 Created
+ *      Alex 16/07/2004 - Complete Rewrite
+ */
+#include <msafd.h>
+
+#include <debug.h>
+
+CRITICAL_SECTION HelperDLLDatabaseLock;
+LIST_ENTRY HelperDLLDatabaseListHead;
+
+
+INT 
+SockGetTdiName(
+    PINT AddressFamily, 
+    PINT SocketType, 
+    PINT Protocol, 
+    GROUP Group, 
+    DWORD Flags, 
+    PUNICODE_STRING TransportName, 
+    PVOID *HelperDllContext, 
+    PHELPER_DATA *HelperDllData, 
+    PDWORD Events)
+{
+    PHELPER_DATA        HelperData;
+    PWSTR               Transports;
+    PWSTR               Transport;
+    PWINSOCK_MAPPING   Mapping;
+    PLIST_ENTRY                Helpers;
+    INT                 Status;
+
+    AFD_DbgPrint(MID_TRACE,("Called\n"));
+
+    /* Check in our Current Loaded Helpers */
+    for (Helpers = SockHelpersListHead.Flink;
+         Helpers != &SockHelpersListHead; 
+         Helpers = Helpers->Flink ) {
+
+        HelperData = CONTAINING_RECORD(Helpers, HELPER_DATA, Helpers);
+
+        /* See if this Mapping works for us */
+        if (SockIsTripleInMapping (HelperData->Mapping, 
+                                   *AddressFamily, 
+                                   *SocketType, 
+                                   *Protocol)) {
+
+            /* Call the Helper Dll function get the Transport Name */
+            if (HelperData->WSHOpenSocket2 == NULL ) {
+
+                /* DLL Doesn't support WSHOpenSocket2, call the old one */
+                HelperData->WSHOpenSocket(AddressFamily,
+                                          SocketType,
+                                          Protocol,
+                                          TransportName,
+                                          HelperDllContext,
+                                          Events
+                                          );
+            } else {
+                HelperData->WSHOpenSocket2(AddressFamily,
+                                           SocketType,
+                                           Protocol,
+                                           Group,
+                                           Flags,
+                                           TransportName,
+                                           HelperDllContext,
+                                           Events
+                                           );
+            }
+
+            /* Return the Helper Pointers */
+            *HelperDllData = HelperData;
+            return NO_ERROR;
+        }
+    }
+
+    /* Get the Transports available */
+    Status = SockLoadTransportList(&Transports);
+
+    /* Check for error */
+    if (Status) {
+        AFD_DbgPrint(MIN_TRACE, ("Can't get transport list\n"));
+        return Status;
+    }
+    
+    /* Loop through each transport until we find one that can satisfy us */
+    for (Transport = Transports; 
+         *Transports != 0; 
+         Transport += wcslen(Transport) + 1) {
+       AFD_DbgPrint(MID_TRACE, ("Transport: %S\n", Transports));
+
+        /* See what mapping this Transport supports */
+        Status = SockLoadTransportMapping(Transport, &Mapping);
+        
+        /* Check for error */
+        if (Status) {
+            AFD_DbgPrint(MIN_TRACE, ("Can't get mapping\n"));
+            HeapFree(GlobalHeap, 0, Transports);
+            return Status;
+        }
+
+        /* See if this Mapping works for us */
+        if (SockIsTripleInMapping(Mapping, *AddressFamily, *SocketType, *Protocol)) {
+
+            /* It does, so load the DLL associated with it */
+            Status = SockLoadHelperDll(Transport, Mapping, &HelperData);
+
+            /* Check for error */
+            if (Status) {
+                AFD_DbgPrint(MIN_TRACE, ("Can't load helper DLL\n"));
+                HeapFree(GlobalHeap, 0, Transports);
+                HeapFree(GlobalHeap, 0, Mapping);
+                return Status;
+            }
+
+            /* Call the Helper Dll function get the Transport Name */
+            if (HelperData->WSHOpenSocket2 == NULL) {
+                /* DLL Doesn't support WSHOpenSocket2, call the old one */
+                HelperData->WSHOpenSocket(AddressFamily,
+                                          SocketType,
+                                          Protocol,
+                                          TransportName,
+                                          HelperDllContext,
+                                          Events
+                                          );
+            } else {
+                HelperData->WSHOpenSocket2(AddressFamily,
+                                           SocketType,
+                                           Protocol,
+                                           Group,
+                                           Flags,
+                                           TransportName,
+                                           HelperDllContext,
+                                           Events
+                                           );
+            }
+
+            /* Return the Helper Pointers */
+            *HelperDllData = HelperData;
+           /* We actually cache these ... the can't be freed yet */
+            /*HeapFree(GlobalHeap, 0, Transports);*/
+            /*HeapFree(GlobalHeap, 0, Mapping);*/
+            return NO_ERROR;
+        }
+        
+        HeapFree(GlobalHeap, 0, Mapping);
+    }
+    HeapFree(GlobalHeap, 0, Transports);
+    return WSAEINVAL;
+}
+
+INT
+SockLoadTransportMapping(
+    PWSTR TransportName, 
+    PWINSOCK_MAPPING *Mapping)
+{
+    PWSTR               TransportKey;
+    HKEY                KeyHandle;
+    ULONG               MappingSize;
+    LONG                Status;
+
+    AFD_DbgPrint(MID_TRACE,("Called: TransportName %ws\n", TransportName));
+
+    /* Allocate a Buffer */
+    TransportKey = HeapAlloc(GlobalHeap, 0, (54 + wcslen(TransportName)) * sizeof(WCHAR));
+
+    /* Check for error */
+    if (TransportKey == NULL) {
+        AFD_DbgPrint(MIN_TRACE, ("Buffer allocation failed\n"));
+        return WSAEINVAL;
+    }
+
+    /* Generate the right key name */
+    wcscpy(TransportKey, L"System\\CurrentControlSet\\Services\\");
+    wcscat(TransportKey, TransportName);
+    wcscat(TransportKey, L"\\Parameters\\Winsock");
+
+    /* Open the Key */
+    Status = RegOpenKeyExW(HKEY_LOCAL_MACHINE, TransportKey, 0, KEY_READ, &KeyHandle);
+
+    /* We don't need the Transport Key anymore */
+    HeapFree(GlobalHeap, 0, TransportKey);
+
+    /* Check for error */
+    if (Status) {
+        AFD_DbgPrint(MIN_TRACE, ("Error reading transport mapping registry\n"));
+        return WSAEINVAL;
+    }
+
+    /* Find out how much space we need for the Mapping */
+    Status = RegQueryValueExW(KeyHandle, L"Mapping", NULL, NULL, NULL, &MappingSize);
+
+    /* Check for error */
+    if (Status) {
+        AFD_DbgPrint(MIN_TRACE, ("Error reading transport mapping registry\n"));
+        return WSAEINVAL;
+    }
+
+    /* Allocate Memory for the Mapping */
+    *Mapping = HeapAlloc(GlobalHeap, 0, MappingSize);
+
+    /* Check for error */
+    if (*Mapping == NULL) {
+        AFD_DbgPrint(MIN_TRACE, ("Buffer allocation failed\n"));
+        return WSAEINVAL;
+    }
+
+    /* Read the Mapping */
+    Status = RegQueryValueExW(KeyHandle, L"Mapping", NULL, NULL, (LPBYTE)*Mapping, &MappingSize);
+    
+    /* Check for error */
+    if (Status) {
+        AFD_DbgPrint(MIN_TRACE, ("Error reading transport mapping registry\n"));
+        HeapFree(GlobalHeap, 0, *Mapping);
+        return WSAEINVAL;
+    }
+
+    /* Close key and return */
+    RegCloseKey(KeyHandle);
+    return 0;
+}
+
+INT 
+SockLoadTransportList(
+    PWSTR *TransportList)
+{
+    ULONG      TransportListSize;
+    HKEY       KeyHandle;
+    LONG       Status;
+
+    AFD_DbgPrint(MID_TRACE,("Called\n"));
+    
+    /* Open the Transports Key */
+    Status = RegOpenKeyExW (HKEY_LOCAL_MACHINE,
+                            L"SYSTEM\\CurrentControlSet\\Services\\Winsock\\Parameters",  
+                            0, 
+                            KEY_READ, 
+                            &KeyHandle);
+
+    /* Check for error */
+    if (Status) {
+        AFD_DbgPrint(MIN_TRACE, ("Error reading transport list registry\n"));
+        return WSAEINVAL;
+    }
+    
+    /* Get the Transport List Size */
+    Status = RegQueryValueExW(KeyHandle,
+                              L"Transports",
+                              NULL,
+                              NULL,
+                              NULL,
+                              &TransportListSize);
+
+    /* Check for error */
+    if (Status) {
+        AFD_DbgPrint(MIN_TRACE, ("Error reading transport list registry\n"));
+        return WSAEINVAL;
+    }
+
+    /* Allocate Memory for the Transport List */
+    *TransportList = HeapAlloc(GlobalHeap, 0, TransportListSize);
+
+    /* Check for error */
+    if (*TransportList == NULL) {
+        AFD_DbgPrint(MIN_TRACE, ("Buffer allocation failed\n"));
+        return WSAEINVAL;
+    }
+
+    /* Get the Transports */
+    Status = RegQueryValueExW (KeyHandle, 
+                               L"Transports", 
+                               NULL, 
+                               NULL, 
+                               (LPBYTE)*TransportList, 
+                               &TransportListSize);
+
+    /* Check for error */
+    if (Status) {
+        AFD_DbgPrint(MIN_TRACE, ("Error reading transport list registry\n"));
+        HeapFree(GlobalHeap, 0, *TransportList);
+        return WSAEINVAL;
+    }
+
+    /* Close key and return */
+    RegCloseKey(KeyHandle);
+    return 0;
+}
+
+INT
+SockLoadHelperDll(
+    PWSTR TransportName, 
+    PWINSOCK_MAPPING Mapping, 
+    PHELPER_DATA *HelperDllData)
+{
+    PHELPER_DATA       HelperData;
+    PWSTR               HelperDllName;
+    PWSTR               FullHelperDllName;
+    ULONG               HelperDllNameSize;
+    PWSTR               HelperKey;
+    HKEY                KeyHandle;
+    ULONG               DataSize;
+    LONG                Status;
+    
+    /* Allocate space for the Helper Structure and TransportName */
+    HelperData = HeapAlloc(GlobalHeap, 0, sizeof(*HelperData) + (wcslen(TransportName) + 1) * sizeof(WCHAR));
+
+    /* Check for error */
+    if (HelperData == NULL) {
+        AFD_DbgPrint(MIN_TRACE, ("Buffer allocation failed\n"));
+        return WSAEINVAL;
+    }
+    
+    /* Allocate Space for the Helper DLL Key */
+    HelperKey = HeapAlloc(GlobalHeap, 0, (54 + wcslen(TransportName)) * sizeof(WCHAR));
+       
+    /* Check for error */
+    if (HelperKey == NULL) {
+        AFD_DbgPrint(MIN_TRACE, ("Buffer allocation failed\n"));
+        HeapFree(GlobalHeap, 0, HelperData);
+        return WSAEINVAL;
+    }
+
+    /* Generate the right key name */
+    wcscpy(HelperKey, L"System\\CurrentControlSet\\Services\\");
+    wcscat(HelperKey, TransportName);
+    wcscat(HelperKey, L"\\Parameters\\Winsock");
+
+    /* Open the Key */
+    Status = RegOpenKeyExW(HKEY_LOCAL_MACHINE, HelperKey, 0, KEY_READ, &KeyHandle);
+
+    HeapFree(GlobalHeap, 0, HelperKey);
+
+    /* Check for error */
+    if (Status) {
+        AFD_DbgPrint(MIN_TRACE, ("Error reading helper DLL parameters\n"));
+        HeapFree(GlobalHeap, 0, HelperData);
+        return WSAEINVAL;
+    }
+    
+    /* Read Size of SockAddr Structures */
+    DataSize = sizeof(HelperData->MinWSAddressLength);
+    HelperData->MinWSAddressLength = 16;
+    RegQueryValueExW (KeyHandle, 
+                      L"MinSockaddrLength", 
+                      NULL, 
+                      NULL, 
+                      (LPBYTE)&HelperData->MinWSAddressLength, 
+                      &DataSize);
+    DataSize = sizeof(HelperData->MinWSAddressLength);
+    HelperData->MaxWSAddressLength = 16;
+    RegQueryValueExW (KeyHandle, 
+                      L"MaxSockaddrLength", 
+                      NULL, 
+                      NULL, 
+                      (LPBYTE)&HelperData->MaxWSAddressLength, 
+                      &DataSize);
+
+    /* Size of TDI Structures */
+    HelperData->MinTDIAddressLength = HelperData->MinWSAddressLength + 6;
+    HelperData->MaxTDIAddressLength = HelperData->MaxWSAddressLength + 6;
+    
+    /* Read Delayed Acceptance Setting */
+    DataSize = sizeof(DWORD);
+    HelperData->UseDelayedAcceptance = FALSE;
+    RegQueryValueExW (KeyHandle, 
+                      L"UseDelayedAcceptance", 
+                      NULL, 
+                      NULL, 
+                      (LPBYTE)&HelperData->UseDelayedAcceptance, 
+                      &DataSize);
+
+    /* Allocate Space for the Helper DLL Names */
+    HelperDllName = HeapAlloc(GlobalHeap, 0, 512);
+
+    /* Check for error */
+    if (HelperDllName == NULL) {
+        AFD_DbgPrint(MIN_TRACE, ("Buffer allocation failed\n"));
+        HeapFree(GlobalHeap, 0, HelperData);
+        return WSAEINVAL;
+    }
+
+    FullHelperDllName = HeapAlloc(GlobalHeap, 0, 512);
+       
+    /* Check for error */
+    if (FullHelperDllName == NULL) {
+        AFD_DbgPrint(MIN_TRACE, ("Buffer allocation failed\n"));
+        HeapFree(GlobalHeap, 0, HelperDllName);
+        HeapFree(GlobalHeap, 0, HelperData);
+        return WSAEINVAL;
+    }
+
+    /* Get the name of the Helper DLL*/
+    DataSize = 512;
+    Status = RegQueryValueExW (KeyHandle, 
+                               L"HelperDllName", 
+                               NULL, 
+                               NULL, 
+                               (LPBYTE)HelperDllName, 
+                               &DataSize);
+
+    /* Check for error */
+    if (Status) {
+        AFD_DbgPrint(MIN_TRACE, ("Error reading helper DLL parameters\n"));
+        HeapFree(GlobalHeap, 0, FullHelperDllName);
+        HeapFree(GlobalHeap, 0, HelperDllName);
+        HeapFree(GlobalHeap, 0, HelperData);
+        return WSAEINVAL;
+    }
+
+    /* Get the Full name, expanding Environment Strings */
+    HelperDllNameSize = ExpandEnvironmentStringsW (HelperDllName,
+                                                   FullHelperDllName, 
+                                                   256);
+
+    /* Load the DLL */
+    HelperData->hInstance = LoadLibraryW(FullHelperDllName);
+
+    HeapFree(GlobalHeap, 0, HelperDllName);
+    HeapFree(GlobalHeap, 0, FullHelperDllName);
+
+    if (HelperData->hInstance == NULL) {
+        AFD_DbgPrint(MIN_TRACE, ("Error loading helper DLL\n"));
+        HeapFree(GlobalHeap, 0, HelperData);
+        return WSAEINVAL;
+    }
+
+    /* Close Key */
+    RegCloseKey(KeyHandle);
+
+    /* Get the Pointers to the Helper Routines */
+    HelperData->WSHOpenSocket =        (PWSH_OPEN_SOCKET)
+                                                                       GetProcAddress(HelperData->hInstance,
+                                                                       "WSHOpenSocket");
+    HelperData->WSHOpenSocket2 = (PWSH_OPEN_SOCKET2)
+                                                                       GetProcAddress(HelperData->hInstance,
+                                                                       "WSHOpenSocket2");
+    HelperData->WSHJoinLeaf = (PWSH_JOIN_LEAF)
+                                                               GetProcAddress(HelperData->hInstance,
+                                                               "WSHJoinLeaf");
+    HelperData->WSHNotify = (PWSH_NOTIFY)
+                                                               GetProcAddress(HelperData->hInstance, "WSHNotify");
+    HelperData->WSHGetSocketInformation = (PWSH_GET_SOCKET_INFORMATION)
+                                                                                       GetProcAddress(HelperData->hInstance, 
+                                                                                       "WSHGetSocketInformation");
+    HelperData->WSHSetSocketInformation = (PWSH_SET_SOCKET_INFORMATION)
+                                                                                       GetProcAddress(HelperData->hInstance,
+                                                                                       "WSHSetSocketInformation");
+    HelperData->WSHGetSockaddrType = (PWSH_GET_SOCKADDR_TYPE)
+                                                                               GetProcAddress(HelperData->hInstance,
+                                                                               "WSHGetSockaddrType");
+    HelperData->WSHGetWildcardSockaddr = (PWSH_GET_WILDCARD_SOCKEADDR)
+                                                                                       GetProcAddress(HelperData->hInstance,
+                                                                                       "WSHGetWildcardSockaddr");
+    HelperData->WSHGetBroadcastSockaddr = (PWSH_GET_BROADCAST_SOCKADDR)
+                                                                                       GetProcAddress(HelperData->hInstance,
+                                                                                       "WSHGetBroadcastSockaddr");
+    HelperData->WSHAddressToString = (PWSH_ADDRESS_TO_STRING)
+                                                                               GetProcAddress(HelperData->hInstance,
+                                                                               "WSHAddressToString");
+    HelperData->WSHStringToAddress = (PWSH_STRING_TO_ADDRESS)
+                                                                               GetProcAddress(HelperData->hInstance,
+                                                                               "WSHStringToAddress");
+    HelperData->WSHIoctl = (PWSH_IOCTL)
+                                                       GetProcAddress(HelperData->hInstance,
+                                                       "WSHIoctl");
+
+    /* Save the Mapping Structure and transport name */
+    HelperData->Mapping = Mapping;
+    wcscpy(HelperData->TransportName, TransportName);
+
+    /* Increment Reference Count */
+    HelperData->RefCount = 1;
+
+    /* Add it to our list */
+    InsertHeadList(&SockHelpersListHead, &HelperData->Helpers);
+
+    /* Return Pointers */
+    *HelperDllData = HelperData;
+    return 0;
+}
+
+BOOL
+SockIsTripleInMapping(
+    PWINSOCK_MAPPING Mapping, 
+    INT AddressFamily, 
+    INT SocketType, 
+    INT Protocol)
+{
+    /* The Windows version returns more detailed information on which of the 3 parameters failed...we should do this later */
+    ULONG    Row;
+    
+    AFD_DbgPrint(MID_TRACE,("Called, Mapping rows = %d\n", Mapping->Rows));
+
+    /* Loop through Mapping to Find a matching one */
+    for (Row = 0; Row < Mapping->Rows; Row++) {
+       AFD_DbgPrint(MID_TRACE,("Examining: row %d: AF %d type %d proto %d\n",
+                               Row,
+                               (INT)Mapping->Mapping[Row].AddressFamily,
+                               (INT)Mapping->Mapping[Row].SocketType,
+                               (INT)Mapping->Mapping[Row].Protocol));
+
+        /* Check of all three values Match */
+        if (((INT)Mapping->Mapping[Row].AddressFamily == AddressFamily) && 
+            ((INT)Mapping->Mapping[Row].SocketType == SocketType) && 
+            ((INT)Mapping->Mapping[Row].Protocol == Protocol)) {
+           AFD_DbgPrint(MID_TRACE,("Found\n"));
+            return TRUE;
+        }
+    }
+    AFD_DbgPrint(MID_TRACE,("Not found\n"));
+    return FALSE;
+}
+
+/* EOF */
diff --git a/reactos/dll/msafd/misc/sndrcv.c b/reactos/dll/msafd/misc/sndrcv.c
new file mode 100644 (file)
index 0000000..62afc4a
--- /dev/null
@@ -0,0 +1,620 @@
+/*
+ * COPYRIGHT:   See COPYING in the top level directory
+ * PROJECT:     ReactOS Ancillary Function Driver DLL
+ * FILE:        misc/sndrcv.c
+ * PURPOSE:     Send/receive routines
+ * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
+ *                             Alex Ionescu (alex@relsoft.net)
+ * REVISIONS:
+ *   CSH 01/09-2000 Created
+ *      Alex 16/07/2004 - Complete Rewrite
+ */
+
+#include <msafd.h>
+
+#include <debug.h>
+
+INT
+WSPAPI
+WSPAsyncSelect(
+    IN  SOCKET Handle, 
+    IN  HWND hWnd, 
+    IN  UINT wMsg, 
+    IN  LONG lEvent, 
+    OUT LPINT lpErrno)
+{
+       PSOCKET_INFORMATION Socket = NULL;
+       PASYNC_DATA AsyncData;
+       NTSTATUS Status;
+       ULONG BlockMode;
+
+       /* Get the Socket Structure associated to this Socket */
+       Socket = GetSocketStructure(Handle);
+
+       /* Allocate the Async Data Structure to pass on to the Thread later */
+       AsyncData = HeapAlloc(GetProcessHeap(), 0, sizeof(*AsyncData));
+
+       /* Change the Socket to Non Blocking */
+       BlockMode = 1;
+       SetSocketInformation(Socket, AFD_INFO_BLOCKING_MODE, &BlockMode, NULL);
+       Socket->SharedData.NonBlocking = TRUE;
+
+       /* Deactive WSPEventSelect */
+       if (Socket->SharedData.AsyncEvents) {
+               WSPEventSelect(Handle, NULL, 0, NULL);
+       }
+
+       /* Create the Asynch Thread if Needed */  
+       SockCreateOrReferenceAsyncThread();
+       
+       /* Open a Handle to AFD's Async Helper */
+       SockGetAsyncSelectHelperAfdHandle();
+
+       /* Store Socket Data */
+       Socket->SharedData.hWnd = hWnd;
+       Socket->SharedData.wMsg = wMsg;
+       Socket->SharedData.AsyncEvents = lEvent;
+       Socket->SharedData.AsyncDisabledEvents = 0;
+       Socket->SharedData.SequenceNumber++;
+
+    /* Return if there are no more Events */
+       if ((Socket->SharedData.AsyncEvents & (~Socket->SharedData.AsyncDisabledEvents)) == 0) {
+               HeapFree(GetProcessHeap(), 0, AsyncData);
+               return 0;
+       }
+
+       /* Set up the Async Data */
+       AsyncData->ParentSocket = Socket;
+       AsyncData->SequenceNumber = Socket->SharedData.SequenceNumber;
+
+       /* Begin Async Select by using I/O Completion */
+       Status = NtSetIoCompletion(SockAsyncCompletionPort,
+                                 (PVOID)&SockProcessQueuedAsyncSelect,
+                                 AsyncData,
+                                 0,
+                                 0);
+
+       /* Return */
+       return ERROR_SUCCESS;
+}
+
+
+int 
+WSPAPI
+WSPRecv(
+       SOCKET Handle, 
+       LPWSABUF lpBuffers, 
+       DWORD dwBufferCount, 
+       LPDWORD lpNumberOfBytesRead, 
+       LPDWORD ReceiveFlags, 
+       LPWSAOVERLAPPED lpOverlapped, 
+       LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine, 
+       LPWSATHREADID lpThreadId, 
+       LPINT lpErrno)
+{
+       PIO_STATUS_BLOCK                        IOSB;
+       IO_STATUS_BLOCK                         DummyIOSB;
+       AFD_RECV_INFO                           RecvInfo;
+       NTSTATUS                                        Status;
+       PVOID                                           APCContext;
+       PVOID                                           APCFunction;
+       HANDLE                                          Event;
+       HANDLE                                  SockEvent;
+       PSOCKET_INFORMATION                     Socket;
+
+       AFD_DbgPrint(MID_TRACE,("Called (%x)\n", Handle));
+
+       /* Get the Socket Structure associate to this Socket*/
+       Socket = GetSocketStructure(Handle);
+
+       Status = NtCreateEvent( &SockEvent, GENERIC_READ | GENERIC_WRITE,
+                               NULL, 1, FALSE );
+
+       if( !NT_SUCCESS(Status) ) return -1;
+
+    /* Set up the Receive Structure */
+    RecvInfo.BufferArray = (PAFD_WSABUF)lpBuffers;
+    RecvInfo.BufferCount = dwBufferCount;
+       RecvInfo.TdiFlags = 0;
+       RecvInfo.AfdFlags = Socket->SharedData.NonBlocking ? AFD_IMMEDIATE : 0;
+
+       /* Set the TDI Flags */
+       if (*ReceiveFlags == 0) {
+               RecvInfo.TdiFlags |= TDI_RECEIVE_NORMAL;
+       
+       } else {
+       
+               if (*ReceiveFlags & MSG_OOB) {
+                       RecvInfo.TdiFlags |= TDI_RECEIVE_EXPEDITED;
+               } else {
+                       RecvInfo.TdiFlags |= TDI_RECEIVE_NORMAL;
+               }
+
+               if (*ReceiveFlags & MSG_PEEK) {
+                       RecvInfo.TdiFlags |= TDI_RECEIVE_PEEK;
+               }
+
+               if (*ReceiveFlags & MSG_PARTIAL) {
+                       RecvInfo.TdiFlags |= TDI_RECEIVE_NORMAL;
+               }
+       }
+
+       /* Verifiy if we should use APC */
+
+       if (lpOverlapped == NULL) {
+
+               /* Not using Overlapped structure, so use normal blocking on event */
+               APCContext = NULL;
+               APCFunction = NULL;
+               Event = SockEvent;
+               IOSB = &DummyIOSB;
+
+       } else {
+
+               if (lpCompletionRoutine == NULL) {
+
+                       /* Using Overlapped Structure, but no Completition Routine, so no need for APC */
+                       APCContext = lpOverlapped;
+                       APCFunction = NULL;
+                       Event = lpOverlapped->hEvent;
+               
+               } else {
+
+                       /* Using Overlapped Structure and a Completition Routine, so use an APC */
+                       APCFunction = NULL; // should be a private io completition function inside us
+                       APCContext = lpCompletionRoutine;
+                       RecvInfo.AfdFlags = AFD_SKIP_FIO;
+               }
+
+               IOSB = (PIO_STATUS_BLOCK)&lpOverlapped->Internal;
+               RecvInfo.AfdFlags |= AFD_OVERLAPPED;
+       }
+
+       IOSB->Status = STATUS_PENDING;
+
+       /* Send IOCTL */
+       Status = NtDeviceIoControlFile((HANDLE)Handle,
+                                       SockEvent,
+                                       APCFunction,
+                                       APCContext,
+                                       IOSB,
+                                       IOCTL_AFD_RECV,
+                                       &RecvInfo,
+                                       sizeof(RecvInfo),
+                                       NULL,
+                                       0);
+
+       /* Wait for completition of not overlapped */
+       if (Status == STATUS_PENDING && lpOverlapped == NULL) {
+           /* It's up to the protocol to time out recv.  We must wait
+            * until the protocol decides it's had enough. */
+           WaitForSingleObject(SockEvent, INFINITE);
+           Status = IOSB->Status;
+       }
+
+    NtClose( SockEvent );
+
+    AFD_DbgPrint(MID_TRACE,("Status %x Information %d\n", 
+                           Status, IOSB->Information));
+
+    /* Return the Flags */
+    *ReceiveFlags = 0;
+
+    switch (Status) {
+    case STATUS_RECEIVE_EXPEDITED: *ReceiveFlags = MSG_OOB; break;
+    case STATUS_RECEIVE_PARTIAL_EXPEDITED: 
+       *ReceiveFlags = MSG_PARTIAL | MSG_OOB; break;
+    case STATUS_RECEIVE_PARTIAL: *ReceiveFlags = MSG_PARTIAL; break;
+    }
+    
+    /* Re-enable Async Event */
+    if (*ReceiveFlags == MSG_OOB) {
+        SockReenableAsyncSelectEvent(Socket, FD_OOB);
+    } else {
+        SockReenableAsyncSelectEvent(Socket, FD_READ);
+    }
+
+    return MsafdReturnWithErrno
+       ( Status, lpErrno, IOSB->Information, lpNumberOfBytesRead );
+}
+
+int 
+WSPAPI 
+WSPRecvFrom(
+       SOCKET Handle, 
+       LPWSABUF lpBuffers, 
+       DWORD dwBufferCount, 
+       LPDWORD lpNumberOfBytesRead, 
+       LPDWORD ReceiveFlags, 
+       struct sockaddr *SocketAddress, 
+       int *SocketAddressLength, 
+       LPWSAOVERLAPPED lpOverlapped, 
+       LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine, 
+       LPWSATHREADID lpThreadId, 
+       LPINT lpErrno)
+{
+       PIO_STATUS_BLOCK                        IOSB;
+       IO_STATUS_BLOCK                         DummyIOSB;
+       AFD_RECV_INFO_UDP                       RecvInfo;
+       NTSTATUS                                        Status;
+       PVOID                                           APCContext;
+       PVOID                                           APCFunction;
+       HANDLE                                          Event;
+       HANDLE                                  SockEvent;
+       PSOCKET_INFORMATION                     Socket;
+
+       /* Get the Socket Structure associate to this Socket*/
+       Socket = GetSocketStructure(Handle);
+
+       Status = NtCreateEvent( &SockEvent, GENERIC_READ | GENERIC_WRITE,
+                               NULL, 1, FALSE );
+
+       if( !NT_SUCCESS(Status) ) return -1;
+
+    /* Set up the Receive Structure */
+    RecvInfo.BufferArray = (PAFD_WSABUF)lpBuffers;
+    RecvInfo.BufferCount = dwBufferCount;
+       RecvInfo.TdiFlags = 0;
+       RecvInfo.AfdFlags = Socket->SharedData.NonBlocking ? AFD_IMMEDIATE : 0;
+       RecvInfo.AddressLength = SocketAddressLength;
+       RecvInfo.Address = SocketAddress;
+
+       /* Set the TDI Flags */
+       if (*ReceiveFlags == 0) {
+               RecvInfo.TdiFlags |= TDI_RECEIVE_NORMAL;
+       
+       } else {
+       
+               if (*ReceiveFlags & MSG_OOB) {
+                       RecvInfo.TdiFlags |= TDI_RECEIVE_EXPEDITED;
+               } else {
+                       RecvInfo.TdiFlags |= TDI_RECEIVE_NORMAL;
+               }
+
+               if (*ReceiveFlags & MSG_PEEK) {
+                       RecvInfo.TdiFlags |= TDI_RECEIVE_PEEK;
+               }
+
+               if (*ReceiveFlags & MSG_PARTIAL) {
+                       RecvInfo.TdiFlags |= TDI_RECEIVE_NORMAL;
+               }
+       }
+
+       /* Verifiy if we should use APC */
+
+       if (lpOverlapped == NULL) {
+
+               /* Not using Overlapped structure, so use normal blocking on event */
+               APCContext = NULL;
+               APCFunction = NULL;
+               Event = SockEvent;
+               IOSB = &DummyIOSB;
+
+       } else {
+
+               if (lpCompletionRoutine == NULL) {
+
+                       /* Using Overlapped Structure, but no Completition Routine, so no need for APC */
+                       APCContext = lpOverlapped;
+                       APCFunction = NULL;
+                       Event = lpOverlapped->hEvent;
+               
+               } else {
+
+                       /* Using Overlapped Structure and a Completition Routine, so use an APC */
+                       APCFunction = NULL; // should be a private io completition function inside us
+                       APCContext = lpCompletionRoutine;
+                       RecvInfo.AfdFlags = AFD_SKIP_FIO;
+               }
+
+               IOSB = (PIO_STATUS_BLOCK)&lpOverlapped->Internal;
+               RecvInfo.AfdFlags |= AFD_OVERLAPPED;
+       }
+
+       IOSB->Status = STATUS_PENDING;
+
+       /* Send IOCTL */
+       Status = NtDeviceIoControlFile((HANDLE)Handle,
+                                       SockEvent,
+                                       APCFunction,
+                                       APCContext,
+                                       IOSB,
+                                       IOCTL_AFD_RECV_DATAGRAM,
+                                       &RecvInfo,
+                                       sizeof(RecvInfo),
+                                       NULL,
+                                       0);
+
+       /* Wait for completition of not overlapped */
+       if (Status == STATUS_PENDING && lpOverlapped == NULL) {
+               WaitForSingleObject(SockEvent, INFINITE); // BUGBUG, shouldn wait infintely for receive...
+               Status = IOSB->Status;
+       }
+
+    NtClose( SockEvent );
+
+    /* Return the Flags */
+    *ReceiveFlags = 0;
+
+    switch (Status) {
+    case STATUS_RECEIVE_EXPEDITED: *ReceiveFlags = MSG_OOB; break;
+    case STATUS_RECEIVE_PARTIAL_EXPEDITED: 
+       *ReceiveFlags = MSG_PARTIAL | MSG_OOB; break;
+    case STATUS_RECEIVE_PARTIAL: *ReceiveFlags = MSG_PARTIAL; break;
+    }
+
+    /* Re-enable Async Event */
+    SockReenableAsyncSelectEvent(Socket, FD_READ);
+    
+    return MsafdReturnWithErrno
+       ( Status, lpErrno, IOSB->Information, lpNumberOfBytesRead );
+}
+
+
+int
+WSPAPI 
+WSPSend(
+       SOCKET Handle, 
+       LPWSABUF lpBuffers, 
+       DWORD dwBufferCount, 
+       LPDWORD lpNumberOfBytesSent, 
+       DWORD iFlags, 
+       LPWSAOVERLAPPED lpOverlapped, 
+       LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine, 
+       LPWSATHREADID lpThreadId, 
+       LPINT lpErrno)
+{
+       PIO_STATUS_BLOCK                        IOSB;
+       IO_STATUS_BLOCK                         DummyIOSB;
+       AFD_SEND_INFO                           SendInfo;
+       NTSTATUS                                        Status;
+       PVOID                                           APCContext;
+       PVOID                                           APCFunction;
+       HANDLE                                          Event;
+       HANDLE                                  SockEvent;
+       PSOCKET_INFORMATION                     Socket;
+
+       /* Get the Socket Structure associate to this Socket*/
+       Socket = GetSocketStructure(Handle);
+
+       Status = NtCreateEvent( &SockEvent, GENERIC_READ | GENERIC_WRITE,
+                               NULL, 1, FALSE );
+
+       if( !NT_SUCCESS(Status) ) return -1;
+
+       AFD_DbgPrint(MID_TRACE,("Called\n"));
+
+    /* Set up the Send Structure */
+    SendInfo.BufferArray = (PAFD_WSABUF)lpBuffers;
+    SendInfo.BufferCount = dwBufferCount;
+       SendInfo.TdiFlags = 0;
+       SendInfo.AfdFlags = Socket->SharedData.NonBlocking ? AFD_IMMEDIATE : 0;
+
+       /* Set the TDI Flags */
+       if (iFlags) {
+               if (iFlags & MSG_OOB) {
+                       SendInfo.TdiFlags |= TDI_SEND_EXPEDITED;
+        }
+        if (iFlags & MSG_PARTIAL) {
+            SendInfo.TdiFlags |= TDI_SEND_PARTIAL;
+        }
+       }
+
+       /* Verifiy if we should use APC */
+       if (lpOverlapped == NULL) {
+
+               /* Not using Overlapped structure, so use normal blocking on event */
+               APCContext = NULL;
+               APCFunction = NULL;
+               Event = SockEvent;
+               IOSB = &DummyIOSB;
+
+       } else {
+
+               if (lpCompletionRoutine == NULL) {
+
+                       /* Using Overlapped Structure, but no Completition Routine, so no need for APC */
+                       APCContext = lpOverlapped;
+                       APCFunction = NULL;
+                       Event = lpOverlapped->hEvent;
+               
+               } else {
+
+                       /* Using Overlapped Structure and a Completition Routine, so use an APC */
+                       APCFunction = NULL; // should be a private io completition function inside us
+                       APCContext = lpCompletionRoutine;
+                       SendInfo.AfdFlags = AFD_SKIP_FIO;
+               }
+
+               IOSB = (PIO_STATUS_BLOCK)&lpOverlapped->Internal;
+               SendInfo.AfdFlags |= AFD_OVERLAPPED;
+       }
+
+       IOSB->Status = STATUS_PENDING;
+
+       /* Send IOCTL */
+       Status = NtDeviceIoControlFile((HANDLE)Handle,
+                                       SockEvent,
+                                       APCFunction,
+                                       APCContext,
+                                       IOSB,
+                                       IOCTL_AFD_SEND,
+                                       &SendInfo,
+                                       sizeof(SendInfo),
+                                       NULL,
+                                       0);
+
+       /* Wait for completition of not overlapped */
+       if (Status == STATUS_PENDING && lpOverlapped == NULL) {
+               WaitForSingleObject(SockEvent, INFINITE); // BUGBUG, shouldn wait infintely for send...
+               Status = IOSB->Status;
+       }
+
+       NtClose( SockEvent );
+
+       if (Status == STATUS_PENDING) {
+           AFD_DbgPrint(MID_TRACE,("Leaving (Pending)\n"));
+           return WSA_IO_PENDING;
+       }
+
+     /* Re-enable Async Event */
+    SockReenableAsyncSelectEvent(Socket, FD_WRITE);
+    
+       AFD_DbgPrint(MID_TRACE,("Leaving (Success, %d)\n", IOSB->Information));
+
+    return MsafdReturnWithErrno
+       ( Status, lpErrno, IOSB->Information, lpNumberOfBytesSent );
+}
+
+int 
+WSPAPI
+WSPSendTo(
+       SOCKET Handle, 
+       LPWSABUF lpBuffers, 
+       DWORD dwBufferCount, 
+       LPDWORD lpNumberOfBytesSent, 
+       DWORD iFlags, 
+       const struct sockaddr *SocketAddress, 
+       int SocketAddressLength, 
+       LPWSAOVERLAPPED lpOverlapped, 
+       LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine, 
+       LPWSATHREADID lpThreadId, 
+       LPINT lpErrno)
+{
+       PIO_STATUS_BLOCK                        IOSB;
+       IO_STATUS_BLOCK                         DummyIOSB;
+       AFD_SEND_INFO_UDP                       SendInfo;
+       NTSTATUS                                        Status;
+       PVOID                                           APCContext;
+       PVOID                                           APCFunction;
+       HANDLE                                          Event;
+       PTRANSPORT_ADDRESS                      RemoteAddress;
+       UCHAR                                           TdiBuffer[0x16];
+       PSOCKADDR                                       BindAddress;
+       INT                                                     BindAddressLength;
+       HANDLE                                  SockEvent;
+       PSOCKET_INFORMATION                     Socket;
+       
+
+       /* Get the Socket Structure associate to this Socket*/
+       Socket = GetSocketStructure(Handle);
+
+       Status = NtCreateEvent( &SockEvent, GENERIC_READ | GENERIC_WRITE,
+                               NULL, 1, FALSE );
+
+       if( !NT_SUCCESS(Status) ) return -1;
+
+       /* Bind us First */
+       if (Socket->SharedData.State == SocketOpen) {
+               
+               /* Get the Wildcard Address */
+               BindAddressLength = Socket->HelperData->MaxWSAddressLength;
+               BindAddress = HeapAlloc(GlobalHeap, 0, BindAddressLength);
+               Socket->HelperData->WSHGetWildcardSockaddr (Socket->HelperContext, 
+                                                                                                       BindAddress, 
+                                                                                                       &BindAddressLength);
+
+               /* Bind it */
+               WSPBind(Handle, BindAddress, BindAddressLength, NULL);
+       }
+
+       /* Set up Address in TDI Format */
+       RemoteAddress = (PTRANSPORT_ADDRESS)TdiBuffer;
+       RemoteAddress->TAAddressCount = 1;
+       RemoteAddress->Address[0].AddressLength = SocketAddressLength - sizeof(SocketAddress->sa_family);
+       RtlCopyMemory(&RemoteAddress->Address[0].AddressType, SocketAddress, SocketAddressLength);
+
+       /* Set up Structure */
+       SendInfo.BufferArray = (PAFD_WSABUF)lpBuffers;
+       SendInfo.AfdFlags = Socket->SharedData.NonBlocking ? AFD_IMMEDIATE : 0;
+       SendInfo.BufferCount = dwBufferCount;
+       SendInfo.RemoteAddress = RemoteAddress;
+       SendInfo.SizeOfRemoteAddress = Socket->HelperData->MaxTDIAddressLength;
+
+       /* Verifiy if we should use APC */
+       if (lpOverlapped == NULL) {
+
+               /* Not using Overlapped structure, so use normal blocking on event */
+               APCContext = NULL;
+               APCFunction = NULL;
+               Event = SockEvent;
+               IOSB = &DummyIOSB;
+
+       } else {
+
+               if (lpCompletionRoutine == NULL) {
+
+                       /* Using Overlapped Structure, but no Completition Routine, so no need for APC */
+                       APCContext = lpOverlapped;
+                       APCFunction = NULL;
+                       Event = lpOverlapped->hEvent;
+               
+               } else {
+
+                       /* Using Overlapped Structure and a Completition Routine, so use an APC */
+                       APCFunction = NULL; // should be a private io completition function inside us
+                       APCContext = lpCompletionRoutine;
+                       SendInfo.AfdFlags = AFD_SKIP_FIO;
+               }
+
+               IOSB = (PIO_STATUS_BLOCK)&lpOverlapped->Internal;
+               SendInfo.AfdFlags |= AFD_OVERLAPPED;
+       }
+
+       /* Send IOCTL */
+       Status = NtDeviceIoControlFile((HANDLE)Handle,
+                                       SockEvent,
+                                       APCFunction,
+                                       APCContext,
+                                       IOSB,
+                                       IOCTL_AFD_SEND_DATAGRAM,
+                                       &SendInfo,
+                                       sizeof(SendInfo),
+                                       NULL,
+                                       0);
+
+       /* Wait for completition of not overlapped */
+       if (Status == STATUS_PENDING && lpOverlapped == NULL) {
+               WaitForSingleObject(SockEvent, INFINITE); // BUGBUG, shouldn wait infintely for send...
+               Status = IOSB->Status;
+       }
+
+       NtClose( SockEvent );
+
+       if (Status == STATUS_PENDING) {
+        return WSA_IO_PENDING;
+       }
+    /* Re-enable Async Event */
+    SockReenableAsyncSelectEvent(Socket, FD_WRITE);
+
+    return MsafdReturnWithErrno
+       ( Status, lpErrno, IOSB->Information, lpNumberOfBytesSent );
+}
+INT
+WSPAPI
+WSPRecvDisconnect(
+    IN  SOCKET s,
+    OUT LPWSABUF lpInboundDisconnectData,
+    OUT LPINT lpErrno)
+{
+  UNIMPLEMENTED
+
+  return 0;
+}
+
+
+
+INT
+WSPAPI
+WSPSendDisconnect(
+    IN  SOCKET s,
+    IN  LPWSABUF lpOutboundDisconnectData,
+    OUT LPINT lpErrno)
+{
+  UNIMPLEMENTED
+
+  return 0;
+}
+
+/* EOF */
diff --git a/reactos/dll/msafd/misc/stubs.c b/reactos/dll/msafd/misc/stubs.c
new file mode 100644 (file)
index 0000000..6de00a6
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * COPYRIGHT:   See COPYING in the top level directory
+ * PROJECT:     ReactOS Ancillary Function Driver DLL
+ * FILE:        misc/stubs.c
+ * PURPOSE:     Stubs
+ * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
+ * REVISIONS:
+ *   CSH 01/09-2000 Created
+ */
+#include <msafd.h>
+
+#include <debug.h>
+
+INT
+WSPAPI
+WSPAddressToString(
+    IN      LPSOCKADDR lpsaAddress,
+    IN      DWORD dwAddressLength,
+    IN      LPWSAPROTOCOL_INFOW lpProtocolInfo,
+    OUT     LPWSTR lpszAddressString,
+    IN OUT  LPDWORD lpdwAddressStringLength,
+    OUT     LPINT lpErrno)
+{
+    UNIMPLEMENTED
+
+    return 0;
+}
+
+
+INT
+WSPAPI
+WSPCancelBlockingCall(
+    OUT LPINT lpErrno)
+{
+    UNIMPLEMENTED
+
+    return 0;
+}
+
+
+INT
+WSPAPI
+WSPDuplicateSocket(
+    IN  SOCKET s,
+    IN  DWORD dwProcessId,
+    OUT LPWSAPROTOCOL_INFOW lpProtocolInfo,
+    OUT LPINT lpErrno)
+{
+    UNIMPLEMENTED
+
+    return 0;
+}
+
+
+BOOL
+WSPAPI
+WSPGetOverlappedResult(
+    IN  SOCKET s,
+    IN  LPWSAOVERLAPPED lpOverlapped,
+    OUT LPDWORD lpcbTransfer,
+    IN  BOOL fWait,
+    OUT LPDWORD lpdwFlags,
+    OUT LPINT lpErrno)
+{
+    UNIMPLEMENTED
+
+    return FALSE;
+}
+
+
+BOOL
+WSPAPI
+WSPGetQOSByName(
+    IN      SOCKET s, 
+    IN OUT  LPWSABUF lpQOSName, 
+    OUT     LPQOS lpQOS, 
+    OUT     LPINT lpErrno)
+{
+    UNIMPLEMENTED
+
+    return FALSE;
+}
+
+
+SOCKET
+WSPAPI
+WSPJoinLeaf(
+    IN  SOCKET s,
+    IN  CONST SOCKADDR *name,
+    IN  INT namelen,
+    IN  LPWSABUF lpCallerData,
+    OUT LPWSABUF lpCalleeData,
+    IN  LPQOS lpSQOS,
+    IN  LPQOS lpGQOS,
+    IN  DWORD dwFlags,
+    OUT LPINT lpErrno)
+{
+    UNIMPLEMENTED
+
+    return (SOCKET)0;
+}
+
+
+INT
+WSPAPI
+WSPSetSockOpt(
+    IN  SOCKET s,
+    IN  INT level,
+    IN  INT optname,
+    IN  CONST CHAR FAR* optval,
+    IN  INT optlen,
+    OUT LPINT lpErrno)
+{
+    UNIMPLEMENTED
+
+    return 0;
+}
+
+INT
+WSPAPI
+WSPStringToAddress(
+    IN      LPWSTR AddressString,
+    IN      INT AddressFamily,
+    IN      LPWSAPROTOCOL_INFOW lpProtocolInfo,
+    OUT     LPSOCKADDR lpAddress,
+    IN OUT  LPINT lpAddressLength,
+    OUT     LPINT lpErrno)
+{
+    UNIMPLEMENTED
+
+    return 0;
+}
+
+/* EOF */
diff --git a/reactos/dll/msafd/msafd.def b/reactos/dll/msafd/msafd.def
new file mode 100644 (file)
index 0000000..80ae18c
--- /dev/null
@@ -0,0 +1,8 @@
+; MSAFD.DLL - Ancillary Function Driver DLL
+
+LIBRARY msafd.dll
+
+EXPORTS
+WSPStartup@76
+
+; EOF
diff --git a/reactos/dll/msafd/msafd.h b/reactos/dll/msafd/msafd.h
new file mode 100755 (executable)
index 0000000..48ef099
--- /dev/null
@@ -0,0 +1,475 @@
+/*
+ * COPYRIGHT:   See COPYING in the top level directory
+ * PROJECT:     ReactOS Ancillary Function Driver DLL
+ * FILE:        include/msafd.h
+ * PURPOSE:     Ancillary Function Driver DLL header
+ */
+#ifndef __MSAFD_H
+#define __MSAFD_H
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define WIN32_NO_STATUS
+#include <windows.h>
+#include <winsock2.h>
+#include <ws2spi.h>
+#define NTOS_MODE_USER
+#include <ndk/ntndk.h>
+
+/* This includes ntsecapi.h so it needs to come after the NDK */
+#include <wsahelp.h>
+#include <tdi.h>
+#include <afd/shared.h>
+#include <helpers.h>
+
+extern HANDLE GlobalHeap;
+extern WSPUPCALLTABLE Upcalls;
+extern LPWPUCOMPLETEOVERLAPPEDREQUEST lpWPUCompleteOverlappedRequest;
+extern LIST_ENTRY SockHelpersListHead;
+extern HANDLE SockEvent;
+extern HANDLE SockAsyncCompletionPort;
+extern BOOLEAN SockAsyncSelectCalled;
+
+typedef enum _SOCKET_STATE {
+    SocketOpen,
+    SocketBound,
+    SocketBoundUdp,
+    SocketConnected,
+    SocketClosed
+} SOCKET_STATE, *PSOCKET_STATE;
+
+typedef struct _SOCK_SHARED_INFO {
+    SOCKET_STATE                               State;
+    INT                                                        AddressFamily;
+    INT                                                        SocketType;
+    INT                                                        Protocol;
+    INT                                                        SizeOfLocalAddress;
+    INT                                                        SizeOfRemoteAddress;
+    struct linger                              LingerData;
+    ULONG                                              SendTimeout;
+    ULONG                                              RecvTimeout;
+    ULONG                                              SizeOfRecvBuffer;
+    ULONG                                              SizeOfSendBuffer;
+    struct {
+        BOOLEAN                                        Listening:1;
+        BOOLEAN                                        Broadcast:1;
+        BOOLEAN                                        Debug:1;
+        BOOLEAN                                        OobInline:1;
+        BOOLEAN                                        ReuseAddresses:1;
+        BOOLEAN                                        ExclusiveAddressUse:1;
+        BOOLEAN                                        NonBlocking:1;
+        BOOLEAN                                        DontUseWildcard:1;
+        BOOLEAN                                        ReceiveShutdown:1;
+        BOOLEAN                                        SendShutdown:1;
+        BOOLEAN                                        UseDelayedAcceptance:1;
+               BOOLEAN                                 UseSAN:1;
+    }; // Flags
+    DWORD                                              CreateFlags;
+    DWORD                                              CatalogEntryId;
+    DWORD                                              ServiceFlags1;
+    DWORD                                              ProviderFlags;
+    GROUP                                              GroupID;
+    DWORD                                              GroupType;
+    INT                                                        GroupPriority;
+    INT                                                        SocketLastError;
+    HWND                                               hWnd;
+    LONG                                               Unknown;
+    DWORD                                              SequenceNumber;
+    UINT                                               wMsg;
+    LONG                                               AsyncEvents;
+    LONG                                               AsyncDisabledEvents;
+} SOCK_SHARED_INFO, *PSOCK_SHARED_INFO;
+
+typedef struct _SOCKET_INFORMATION {
+       ULONG RefCount;
+       SOCKET Handle;
+       SOCK_SHARED_INFO SharedData;
+       DWORD HelperEvents;
+       PHELPER_DATA HelperData;
+       PVOID HelperContext;
+       PSOCKADDR LocalAddress;
+       PSOCKADDR RemoteAddress;
+       HANDLE TdiAddressHandle;
+       HANDLE TdiConnectionHandle;
+       PVOID AsyncData;
+       HANDLE EventObject;
+       LONG NetworkEvents;
+       CRITICAL_SECTION Lock;
+       PVOID SanData;
+       BOOL TrySAN;
+       SOCKADDR WSLocalAddress;
+       SOCKADDR WSRemoteAddress;
+} SOCKET_INFORMATION, *PSOCKET_INFORMATION;
+
+
+typedef struct _SOCKET_CONTEXT {
+       SOCK_SHARED_INFO SharedData;
+       ULONG SizeOfHelperData;
+       ULONG Padding;
+       SOCKADDR LocalAddress;
+       SOCKADDR RemoteAddress;
+       /* Plus Helper Data */
+} SOCKET_CONTEXT, *PSOCKET_CONTEXT;
+
+typedef struct _ASYNC_DATA {
+       PSOCKET_INFORMATION ParentSocket;
+       DWORD SequenceNumber;
+       IO_STATUS_BLOCK IoStatusBlock;
+       AFD_POLL_INFO AsyncSelectInfo;
+} ASYNC_DATA, *PASYNC_DATA;
+
+SOCKET
+WSPAPI
+WSPAccept(
+    IN      SOCKET s,
+    OUT     LPSOCKADDR addr,
+    IN OUT  LPINT addrlen,
+    IN      LPCONDITIONPROC lpfnCondition,
+    IN      DWORD dwCallbackData,
+    OUT     LPINT lpErrno);
+
+INT
+WSPAPI
+WSPAddressToString(
+    IN      LPSOCKADDR lpsaAddress,
+    IN      DWORD dwAddressLength,
+    IN      LPWSAPROTOCOL_INFOW lpProtocolInfo,
+    OUT     LPWSTR lpszAddressString,
+    IN OUT  LPDWORD lpdwAddressStringLength,
+    OUT     LPINT lpErrno);
+
+INT
+WSPAPI
+WSPAsyncSelect(
+    IN  SOCKET s, 
+    IN  HWND hWnd, 
+    IN  UINT wMsg, 
+    IN  LONG lEvent, 
+    OUT LPINT lpErrno);
+
+INT
+WSPAPI WSPBind(
+    IN  SOCKET s,
+    IN  CONST SOCKADDR *name, 
+    IN  INT namelen, 
+    OUT LPINT lpErrno);
+
+INT
+WSPAPI
+WSPCancelBlockingCall(
+    OUT LPINT lpErrno);
+
+INT
+WSPAPI
+WSPCleanup(
+    OUT LPINT lpErrno);
+
+INT
+WSPAPI
+WSPCloseSocket(
+    IN SOCKET s,
+    OUT        LPINT lpErrno);
+
+INT
+WSPAPI
+WSPConnect(
+    IN  SOCKET s,
+    IN  CONST SOCKADDR *name,
+    IN  INT namelen,
+    IN  LPWSABUF lpCallerData,
+    OUT LPWSABUF lpCalleeData,
+    IN  LPQOS lpSQOS,
+    IN  LPQOS lpGQOS,
+    OUT LPINT lpErrno);
+
+INT
+WSPAPI
+WSPDuplicateSocket(
+    IN  SOCKET s,
+    IN  DWORD dwProcessId,
+    OUT LPWSAPROTOCOL_INFOW lpProtocolInfo,
+    OUT LPINT lpErrno);
+
+INT
+WSPAPI
+WSPEnumNetworkEvents(
+    IN  SOCKET s, 
+    IN  WSAEVENT hEventObject, 
+    OUT LPWSANETWORKEVENTS lpNetworkEvents, 
+    OUT LPINT lpErrno);
+
+INT
+WSPAPI
+WSPEventSelect(
+    IN  SOCKET s,
+    IN  WSAEVENT hEventObject,
+    IN  LONG lNetworkEvents,
+    OUT LPINT lpErrno);
+
+BOOL
+WSPAPI
+WSPGetOverlappedResult(
+    IN  SOCKET s,
+    IN  LPWSAOVERLAPPED lpOverlapped,
+    OUT LPDWORD lpcbTransfer,
+    IN  BOOL fWait,
+    OUT LPDWORD lpdwFlags,
+    OUT LPINT lpErrno);
+
+INT
+WSPAPI
+WSPGetPeerName(
+    IN      SOCKET s, 
+    OUT     LPSOCKADDR name, 
+    IN OUT  LPINT namelen, 
+    OUT     LPINT lpErrno);
+
+BOOL
+WSPAPI
+WSPGetQOSByName(
+    IN      SOCKET s, 
+    IN OUT  LPWSABUF lpQOSName, 
+    OUT     LPQOS lpQOS, 
+    OUT     LPINT lpErrno);
+
+INT
+WSPAPI
+WSPGetSockName(
+    IN      SOCKET s,
+    OUT     LPSOCKADDR name,
+    IN OUT  LPINT namelen,
+    OUT     LPINT lpErrno);
+
+INT
+WSPAPI
+WSPGetSockOpt(
+    IN      SOCKET s,
+    IN      INT level,
+    IN      INT optname,
+    OUT            CHAR FAR* optval,
+    IN OUT  LPINT optlen,
+    OUT     LPINT lpErrno);
+
+INT
+WSPAPI
+WSPIoctl(
+    IN  SOCKET s,
+    IN  DWORD dwIoControlCode,
+    IN  LPVOID lpvInBuffer,
+    IN  DWORD cbInBuffer,
+    OUT LPVOID lpvOutBuffer,
+    IN  DWORD cbOutBuffer,
+    OUT LPDWORD lpcbBytesReturned,
+    IN  LPWSAOVERLAPPED lpOverlapped,
+    IN  LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,
+    IN  LPWSATHREADID lpThreadId,
+    OUT LPINT lpErrno);
+
+SOCKET
+WSPAPI
+WSPJoinLeaf(
+    IN  SOCKET s,
+    IN  CONST SOCKADDR *name,
+    IN  INT namelen,
+    IN  LPWSABUF lpCallerData,
+    OUT LPWSABUF lpCalleeData,
+    IN  LPQOS lpSQOS,
+    IN  LPQOS lpGQOS,
+    IN  DWORD dwFlags,
+    OUT LPINT lpErrno);
+
+INT
+WSPAPI
+WSPListen(
+    IN  SOCKET s,
+    IN  INT backlog,
+    OUT LPINT lpErrno);
+
+INT
+WSPAPI
+WSPRecv(
+    IN      SOCKET s,
+    IN OUT  LPWSABUF lpBuffers,
+    IN      DWORD dwBufferCount,
+    OUT     LPDWORD lpNumberOfBytesRecvd,
+    IN OUT  LPDWORD lpFlags,
+    IN      LPWSAOVERLAPPED lpOverlapped,
+    IN      LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,
+    IN      LPWSATHREADID lpThreadId,
+    OUT     LPINT lpErrno);
+
+INT
+WSPAPI
+WSPRecvDisconnect(
+    IN  SOCKET s,
+    OUT LPWSABUF lpInboundDisconnectData,
+    OUT LPINT lpErrno);
+
+INT
+WSPAPI
+WSPRecvFrom(
+    IN      SOCKET s,
+    IN OUT  LPWSABUF lpBuffers,
+    IN      DWORD dwBufferCount,
+    OUT     LPDWORD lpNumberOfBytesRecvd,
+    IN OUT  LPDWORD lpFlags,
+    OUT     LPSOCKADDR lpFrom,
+    IN OUT  LPINT lpFromlen,
+    IN      LPWSAOVERLAPPED lpOverlapped,
+    IN      LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,
+    IN      LPWSATHREADID lpThreadId,
+    OUT     LPINT lpErrno);
+
+INT
+WSPAPI
+WSPSelect(
+    IN      INT nfds,
+    IN OUT  LPFD_SET readfds,
+    IN OUT  LPFD_SET writefds,
+    IN OUT  LPFD_SET exceptfds,
+    IN      CONST LPTIMEVAL timeout,
+    OUT     LPINT lpErrno);
+
+INT
+WSPAPI
+WSPSend(
+    IN  SOCKET s,
+    IN  LPWSABUF lpBuffers,
+    IN  DWORD dwBufferCount,
+    OUT LPDWORD lpNumberOfBytesSent,
+    IN  DWORD dwFlags,
+    IN  LPWSAOVERLAPPED lpOverlapped,
+    IN  LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,
+    IN  LPWSATHREADID lpThreadId,
+    OUT LPINT lpErrno);
+
+INT
+WSPAPI
+WSPSendDisconnect(
+    IN  SOCKET s,
+    IN  LPWSABUF lpOutboundDisconnectData,
+    OUT LPINT lpErrno);
+
+INT
+WSPAPI
+WSPSendTo(
+    IN  SOCKET s,
+    IN  LPWSABUF lpBuffers,
+    IN  DWORD dwBufferCount,
+    OUT LPDWORD lpNumberOfBytesSent,
+    IN  DWORD dwFlags,
+    IN  CONST SOCKADDR *lpTo,
+    IN  INT iTolen,
+    IN  LPWSAOVERLAPPED lpOverlapped,
+    IN  LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,
+    IN  LPWSATHREADID lpThreadId,
+    OUT LPINT lpErrno);
+
+INT
+WSPAPI
+WSPSetSockOpt(
+    IN  SOCKET s,
+    IN  INT level,
+    IN  INT optname,
+    IN  CONST CHAR FAR* optval,
+    IN  INT optlen,
+    OUT LPINT lpErrno);
+
+INT
+WSPAPI
+WSPShutdown(
+    IN  SOCKET s,
+    IN  INT how,
+    OUT LPINT lpErrno);
+
+SOCKET
+WSPAPI
+WSPSocket(
+    IN  INT af,
+    IN  INT type,
+    IN  INT protocol,
+    IN  LPWSAPROTOCOL_INFOW lpProtocolInfo,
+    IN  GROUP g,
+    IN  DWORD dwFlags,
+    OUT LPINT lpErrno);
+
+INT
+WSPAPI
+WSPStringToAddress(
+    IN      LPWSTR AddressString,
+    IN      INT AddressFamily,
+    IN      LPWSAPROTOCOL_INFOW lpProtocolInfo,
+    OUT     LPSOCKADDR lpAddress,
+    IN OUT  LPINT lpAddressLength,
+    OUT     LPINT lpErrno);
+
+
+PSOCKET_INFORMATION GetSocketStructure(
+       SOCKET Handle
+);
+
+VOID DeleteSocketStructure( SOCKET Handle );
+
+int GetSocketInformation(
+       PSOCKET_INFORMATION Socket,
+       ULONG                           AfdInformationClass,
+       PULONG Ulong            OPTIONAL,
+       PLARGE_INTEGER          LargeInteger OPTIONAL
+);
+
+int SetSocketInformation(
+       PSOCKET_INFORMATION Socket,
+       ULONG                           AfdInformationClass,
+       PULONG                          Ulong           OPTIONAL,
+       PLARGE_INTEGER          LargeInteger OPTIONAL
+);
+
+int CreateContext(
+       PSOCKET_INFORMATION Socket
+);
+
+int SockAsyncThread(
+       PVOID ThreadParam
+);
+
+VOID 
+SockProcessAsyncSelect(
+       PSOCKET_INFORMATION Socket,
+       PASYNC_DATA AsyncData
+);
+
+VOID
+SockAsyncSelectCompletionRoutine(
+       PVOID Context,
+       PIO_STATUS_BLOCK IoStatusBlock
+);
+
+BOOLEAN
+SockCreateOrReferenceAsyncThread(
+       VOID
+);
+
+BOOLEAN SockGetAsyncSelectHelperAfdHandle(
+       VOID
+);
+
+VOID SockProcessQueuedAsyncSelect(
+       PVOID Context,
+       PIO_STATUS_BLOCK IoStatusBlock
+);
+
+VOID
+SockReenableAsyncSelectEvent (
+    IN PSOCKET_INFORMATION Socket,
+    IN ULONG Event
+    );
+    
+DWORD MsafdReturnWithErrno( NTSTATUS Status, LPINT Errno, DWORD Received,
+                           LPDWORD ReturnedBytes );
+
+typedef VOID (*PASYNC_COMPLETION_ROUTINE)(PVOID Context, PIO_STATUS_BLOCK IoStatusBlock);
+
+#endif /* __MSAFD_H */
+
+/* EOF */
diff --git a/reactos/dll/msafd/msafd.rc b/reactos/dll/msafd/msafd.rc
new file mode 100644 (file)
index 0000000..c0e0d4a
--- /dev/null
@@ -0,0 +1,5 @@
+#define REACTOS_VERSION_DLL
+#define REACTOS_STR_FILE_DESCRIPTION   "Ancillary Function Driver DLL\0"
+#define REACTOS_STR_INTERNAL_NAME      "msafd\0"
+#define REACTOS_STR_ORIGINAL_FILENAME  "msafd.dll\0"
+#include <reactos/version.rc>
diff --git a/reactos/dll/msafd/msafd.xml b/reactos/dll/msafd/msafd.xml
new file mode 100644 (file)
index 0000000..d2f6ba6
--- /dev/null
@@ -0,0 +1,20 @@
+<module name="msafd" type="win32dll" baseaddress="${BASEADDRESS_MSAFD}" installbase="system32" installname="msafd.dll">
+       <importlibrary definition="msafd.def" />
+       <include base="msafd">include</include>
+       <define name="UNICODE" />
+       <define name="_UNICODE" />
+       <define name="__REACTOS__" />
+       <define name="__USE_W32API" />
+       <pch>msafd.h</pch>
+       <library>ntdll</library>
+       <library>kernel32</library>
+       <library>advapi32</library>
+       <directory name="misc">
+               <file>dllmain.c</file>
+               <file>event.c</file>
+               <file>helpers.c</file>
+               <file>sndrcv.c</file>
+               <file>stubs.c</file>
+       </directory>
+       <file>msafd.rc</file>
+</module>
diff --git a/reactos/dll/msgina/msgina.c b/reactos/dll/msgina/msgina.c
new file mode 100644 (file)
index 0000000..3659e52
--- /dev/null
@@ -0,0 +1,562 @@
+/*
+ *  ReactOS kernel
+ *  Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+/* $Id$
+ *
+ * PROJECT:         ReactOS msgina.dll
+ * FILE:            lib/msgina/msgina.c
+ * PURPOSE:         ReactOS Logon GINA DLL
+ * PROGRAMMER:      Thomas Weidenmueller (w3seek@users.sourceforge.net)
+ * UPDATE HISTORY:
+ *      24-11-2003  Created
+ */
+#include <windows.h>
+#include <winwlx.h>
+#include "msgina.h"
+#include "resource.h"
+
+#include <wine/debug.h>
+
+extern HINSTANCE hDllInstance;
+
+typedef struct _DISPLAYSTATUSMSG
+{
+  PGINA_CONTEXT Context;
+  HDESK hDesktop;
+  DWORD dwOptions;
+  PWSTR pTitle;
+  PWSTR pMessage;
+  HANDLE StartupEvent;
+} DISPLAYSTATUSMSG, *PDISPLAYSTATUSMSG;
+
+INT_PTR CALLBACK
+LoggedOnDlgProc(
+  HWND hwndDlg,
+  UINT uMsg,
+  WPARAM wParam,
+  LPARAM lParam
+)
+{
+  switch(uMsg)
+  {
+    case WM_COMMAND:
+    {
+      switch(LOWORD(wParam))
+      {
+        case IDYES:
+        case IDNO:
+        {
+          EndDialog(hwndDlg, LOWORD(wParam));
+          break;
+        }
+      }
+      return FALSE;
+    }
+    case WM_INITDIALOG:
+    {
+      SetFocus(GetDlgItem(hwndDlg, IDNO));
+      break;
+    }
+    case WM_CLOSE:
+    {
+      EndDialog(hwndDlg, IDNO);
+      return TRUE;
+    }
+  }
+  return FALSE;
+}
+
+
+/*
+ * @implemented
+ */
+BOOL WINAPI
+WlxNegotiate(
+       DWORD  dwWinlogonVersion,
+       PDWORD pdwDllVersion)
+{
+  if(!pdwDllVersion || (dwWinlogonVersion < GINA_VERSION))
+    return FALSE;
+  
+  *pdwDllVersion = GINA_VERSION;
+  
+  return TRUE;
+}
+
+
+/*
+ * @implemented
+ */
+BOOL WINAPI
+WlxInitialize(
+       LPWSTR lpWinsta,
+       HANDLE hWlx,
+       PVOID  pvReserved,
+       PVOID  pWinlogonFunctions,
+       PVOID  *pWlxContext)
+{
+  PGINA_CONTEXT pgContext;
+  
+  pgContext = (PGINA_CONTEXT)LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, sizeof(GINA_CONTEXT));
+  if(!pgContext)
+    return FALSE;
+  
+  /* return the context to winlogon */
+  *pWlxContext = (PVOID)pgContext;
+  
+  pgContext->hDllInstance = hDllInstance;
+  
+  /* save pointer to dispatch table */
+  pgContext->pWlxFuncs = (PWLX_DISPATCH_VERSION)pWinlogonFunctions;
+  
+  /* save the winlogon handle used to call the dispatch functions */
+  pgContext->hWlx = hWlx;
+  
+  /* save window station */
+  pgContext->station = lpWinsta;
+  
+  /* clear status window handle */
+  pgContext->hStatusWindow = 0;
+  
+  /* notify winlogon that we will use the default SAS */
+  pgContext->pWlxFuncs->WlxUseCtrlAltDel(hWlx);
+  
+  return TRUE;
+}
+
+
+/*
+ * @implemented
+ */
+BOOL WINAPI
+WlxStartApplication(
+       PVOID pWlxContext,
+       PWSTR pszDesktopName,
+       PVOID pEnvironment,
+       PWSTR pszCmdLine)
+{
+  PGINA_CONTEXT pgContext = (PGINA_CONTEXT)pWlxContext;
+  STARTUPINFO si;
+  PROCESS_INFORMATION pi;
+  BOOL Ret;
+  
+  si.cb = sizeof(STARTUPINFO);
+  si.lpReserved = NULL;
+  si.lpTitle = pszCmdLine;
+  si.dwX = si.dwY = si.dwXSize = si.dwYSize = 0L;
+  si.dwFlags = 0;
+  si.wShowWindow = SW_SHOW;  
+  si.lpReserved2 = NULL;
+  si.cbReserved2 = 0;
+  si.lpDesktop = pszDesktopName;
+  
+  Ret = CreateProcessAsUser(pgContext->UserToken,
+                            NULL,
+                            pszCmdLine,
+                            NULL,
+                            NULL,
+                            FALSE,
+                            CREATE_UNICODE_ENVIRONMENT,
+                            pEnvironment,
+                            NULL,
+                            &si,
+                            &pi);
+  
+  VirtualFree(pEnvironment, 0, MEM_RELEASE);
+  return Ret;
+}
+
+
+/*
+ * @implemented
+ */
+BOOL WINAPI
+WlxActivateUserShell(
+       PVOID pWlxContext,
+       PWSTR pszDesktopName,
+       PWSTR pszMprLogonScript,
+       PVOID pEnvironment)
+{
+  PGINA_CONTEXT pgContext = (PGINA_CONTEXT)pWlxContext;
+  STARTUPINFO si;
+  PROCESS_INFORMATION pi;
+  HKEY hKey;
+  DWORD BufSize, ValueType;
+  WCHAR pszUserInitApp[MAX_PATH];
+  WCHAR pszExpUserInitApp[MAX_PATH];
+  BOOL Ret;
+  
+  /* get the path of userinit */
+  if(RegOpenKeyExW(HKEY_LOCAL_MACHINE, 
+                  L"SOFTWARE\\ReactOS\\Windows NT\\CurrentVersion\\Winlogon", 
+                  0, KEY_QUERY_VALUE, &hKey) != ERROR_SUCCESS)
+  {ERR("GINA: Failed: 1\n");
+    VirtualFree(pEnvironment, 0, MEM_RELEASE);
+    return FALSE;
+  }
+  BufSize = MAX_PATH * sizeof(WCHAR);
+  if((RegQueryValueEx(hKey, L"Userinit", NULL, &ValueType, (LPBYTE)pszUserInitApp, 
+                     &BufSize) != ERROR_SUCCESS) || 
+                     !((ValueType == REG_SZ) || (ValueType == REG_EXPAND_SZ)))
+  {ERR("GINA: Failed: 2\n");
+    RegCloseKey(hKey);
+    VirtualFree(pEnvironment, 0, MEM_RELEASE);
+    return FALSE;
+  }
+  RegCloseKey(hKey);
+  
+  /* start userinit */
+  /* FIXME - allow to start more applications that are comma-separated */
+  si.cb = sizeof(STARTUPINFO);
+  si.lpReserved = NULL;
+  si.lpTitle = L"userinit";
+  si.dwX = si.dwY = si.dwXSize = si.dwYSize = 0;
+  si.dwFlags = 0;
+  si.wShowWindow = SW_SHOW;  
+  si.lpReserved2 = NULL;
+  si.cbReserved2 = 0;
+  si.lpDesktop = pszDesktopName;
+  
+  ExpandEnvironmentStrings(pszUserInitApp, pszExpUserInitApp, MAX_PATH);
+  
+  Ret = CreateProcessAsUser(pgContext->UserToken,
+                            pszExpUserInitApp,
+                            NULL,
+                            NULL,
+                            NULL,
+                            FALSE,
+                            CREATE_UNICODE_ENVIRONMENT,
+                            pEnvironment,
+                            NULL,
+                            &si,
+                            &pi);
+  if(!Ret) ERR("GINA: Failed: 3\n");
+  VirtualFree(pEnvironment, 0, MEM_RELEASE);
+  return Ret;
+}
+
+
+/*
+ * @implemented
+ */
+int WINAPI
+WlxLoggedOnSAS(
+       PVOID pWlxContext,
+       DWORD dwSasType,
+       PVOID pReserved)
+{
+  PGINA_CONTEXT pgContext = (PGINA_CONTEXT)pWlxContext;
+  int SasAction = WLX_SAS_ACTION_NONE;
+  
+  switch(dwSasType)
+  {
+    case WLX_SAS_TYPE_CTRL_ALT_DEL:
+    {
+      int Result;
+      /* display "Are you sure you want to log off?" dialog */
+      Result = pgContext->pWlxFuncs->WlxDialogBoxParam(pgContext->hWlx,
+                                                       pgContext->hDllInstance,
+                                                       (LPTSTR)IDD_LOGOFF_DLG,
+                                                       NULL,
+                                                       LoggedOnDlgProc,
+                                                       (LPARAM)pgContext);
+      if(Result == IDOK)
+      {
+        SasAction = WLX_SAS_ACTION_LOCK_WKSTA;
+      }
+      break;
+    }
+    case WLX_SAS_TYPE_SC_INSERT:
+    {
+      FIXME("WlxLoggedOnSAS: SasType WLX_SAS_TYPE_SC_INSERT not supported!\n");
+      break;
+    }
+    case WLX_SAS_TYPE_SC_REMOVE:
+    {
+      FIXME("WlxLoggedOnSAS: SasType WLX_SAS_TYPE_SC_REMOVE not supported!\n");
+      break;
+    }
+    case WLX_SAS_TYPE_TIMEOUT:
+    {
+      FIXME("WlxLoggedOnSAS: SasType WLX_SAS_TYPE_TIMEOUT not supported!\n");
+      break;
+    }
+    default:
+    {
+      WARN("WlxLoggedOnSAS: Unknown SasType: 0x%x\n", dwSasType);
+      break;
+    }
+  }
+  
+  return SasAction;
+}
+
+
+BOOL 
+CALLBACK 
+StatusMessageWindowProc(
+  HWND hwndDlg,
+  UINT uMsg,
+  WPARAM wParam,
+  LPARAM lParam
+)
+{
+  switch(uMsg)
+  {
+    case WM_INITDIALOG:
+    {
+      PDISPLAYSTATUSMSG msg = (PDISPLAYSTATUSMSG)lParam;
+      if(!msg)
+        return FALSE;
+      
+      msg->Context->hStatusWindow = hwndDlg;
+      
+      if(msg->pTitle)
+        SetWindowText(hwndDlg, msg->pTitle);
+      SetDlgItemText(hwndDlg, IDC_STATUSLABEL, msg->pMessage);
+      if(!msg->Context->SignaledStatusWindowCreated)
+      {
+        msg->Context->SignaledStatusWindowCreated = TRUE;
+        SetEvent(msg->StartupEvent);
+      }
+      break;
+    }
+  }
+  return FALSE;
+}
+
+
+DWORD WINAPI
+StartupWindowThread(LPVOID lpParam)
+{
+  HDESK OldDesk;
+  PDISPLAYSTATUSMSG msg = (PDISPLAYSTATUSMSG)lpParam;
+  
+  OldDesk = GetThreadDesktop(GetCurrentThreadId());
+  
+  if(!SetThreadDesktop(msg->hDesktop))
+  {
+    HeapFree(GetProcessHeap(), 0, lpParam);
+    return FALSE;
+  }
+  DialogBoxParam(hDllInstance, 
+                 MAKEINTRESOURCE(IDD_STATUSWINDOW),
+                 0,
+                 StatusMessageWindowProc,
+                 (LPARAM)lpParam);
+  SetThreadDesktop(OldDesk);
+  
+  msg->Context->hStatusWindow = 0;
+  msg->Context->SignaledStatusWindowCreated = FALSE;
+  
+  HeapFree(GetProcessHeap(), 0, lpParam);
+  return TRUE;
+}
+
+
+/*
+ * @implemented
+ */
+BOOL WINAPI
+WlxDisplayStatusMessage(
+       PVOID pWlxContext,
+       HDESK hDesktop,
+       DWORD dwOptions,
+       PWSTR pTitle,
+       PWSTR pMessage)
+{
+  PDISPLAYSTATUSMSG msg;
+  HANDLE Thread;
+  DWORD ThreadId;
+  PGINA_CONTEXT pgContext = (PGINA_CONTEXT)pWlxContext;
+  
+  if(!pgContext->hStatusWindow)
+  {
+    msg = (PDISPLAYSTATUSMSG)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DISPLAYSTATUSMSG));
+    if(!msg)
+      return FALSE;
+    
+    msg->Context = pgContext;
+    msg->dwOptions = dwOptions;
+    msg->pTitle = pTitle;
+    msg->pMessage = pMessage;
+    msg->hDesktop = hDesktop;
+    
+    msg->StartupEvent = CreateEvent(NULL,
+                                    TRUE,
+                                    FALSE,
+                                    NULL);
+    
+    if(!msg->StartupEvent)
+      return FALSE;
+    
+    Thread = CreateThread(NULL,
+                          0,
+                          StartupWindowThread,
+                          (PVOID)msg,
+                          0,
+                          &ThreadId);
+    if(Thread)
+    {
+      CloseHandle(Thread);
+      WaitForSingleObject(msg->StartupEvent, INFINITE);
+      CloseHandle(msg->StartupEvent);
+      return TRUE;
+    }
+    
+    return FALSE;
+  }
+  
+  if(pTitle)
+    SetWindowText(pgContext->hStatusWindow, pTitle);
+  
+  SetDlgItemText(pgContext->hStatusWindow, IDC_STATUSLABEL, pMessage);
+  
+  return TRUE;
+}
+
+
+/*
+ * @implemented
+ */
+BOOL WINAPI
+WlxRemoveStatusMessage(
+       PVOID pWlxContext)
+{
+  PGINA_CONTEXT pgContext = (PGINA_CONTEXT)pWlxContext;
+  if(pgContext->hStatusWindow)
+  {
+    EndDialog(pgContext->hStatusWindow, 0);
+    pgContext->hStatusWindow = 0;
+  }
+  
+  return TRUE;
+}
+
+
+/*
+ * @implemented
+ */
+VOID WINAPI
+WlxDisplaySASNotice(
+       PVOID pWlxContext)
+{
+  PGINA_CONTEXT pgContext = (PGINA_CONTEXT)pWlxContext;
+  pgContext->pWlxFuncs->WlxSasNotify(pgContext->hWlx, WLX_SAS_TYPE_CTRL_ALT_DEL);
+}
+
+
+static PWSTR
+DuplicationString(PWSTR Str)
+{
+  DWORD cb;
+  PWSTR NewStr;
+
+  cb = (wcslen(Str) + 1) * sizeof(WCHAR);
+  if((NewStr = LocalAlloc(LMEM_FIXED, cb)))
+  {
+    memcpy(NewStr, Str, cb);
+  }
+  return NewStr;
+}
+
+
+/*
+ * @unimplemented
+ */
+int WINAPI
+WlxLoggedOutSAS(
+       PVOID                pWlxContext,
+       DWORD                dwSasType,
+       PLUID                pAuthenticationId,
+       PSID                 pLogonSid,
+       PDWORD               pdwOptions,
+       PHANDLE              phToken,
+       PWLX_MPR_NOTIFY_INFO pNprNotifyInfo,
+       PVOID                *pProfile)
+{
+  PGINA_CONTEXT pgContext = (PGINA_CONTEXT)pWlxContext;
+  TOKEN_STATISTICS Stats;
+  DWORD cbStats;
+
+  if(!phToken)
+  {
+    WARN("msgina: phToken == NULL!\n");
+    return WLX_SAS_ACTION_NONE;
+  }
+
+  if(!LogonUser(L"Administrator", NULL, L"Secrect",
+                LOGON32_LOGON_INTERACTIVE, /* FIXME - use LOGON32_LOGON_UNLOCK instead! */
+                LOGON32_PROVIDER_DEFAULT,
+                phToken))
+  {
+    WARN("msgina: Logonuser() failed\n");
+    return WLX_SAS_ACTION_NONE;
+  }
+  
+  if(!(*phToken))
+  {
+    WARN("msgina: *phToken == NULL!\n");
+    return WLX_SAS_ACTION_NONE;
+  }
+
+  pgContext->UserToken =*phToken;
+  
+  *pdwOptions = 0;
+  *pProfile =NULL; 
+  
+  if(!GetTokenInformation(*phToken,
+                          TokenStatistics,
+                          (PVOID)&Stats,
+                          sizeof(TOKEN_STATISTICS),
+                          &cbStats))
+  {
+    WARN("msgina: Couldn't get Autentication id from user token!\n");
+    return WLX_SAS_ACTION_NONE;
+  }
+  *pAuthenticationId = Stats.AuthenticationId; 
+  pNprNotifyInfo->pszUserName = DuplicationString(L"Administrator");
+  pNprNotifyInfo->pszDomain = NULL;
+  pNprNotifyInfo->pszPassword = DuplicationString(L"Secret");
+  pNprNotifyInfo->pszOldPassword = NULL;
+
+  return WLX_SAS_ACTION_LOGON;
+}
+
+
+BOOL STDCALL
+DllMain(
+       HINSTANCE hinstDLL,
+       DWORD     dwReason,
+       LPVOID    lpvReserved)
+{
+  switch (dwReason)
+  {
+    case DLL_PROCESS_ATTACH:
+      /* fall through */
+    case DLL_THREAD_ATTACH:
+      hDllInstance = hinstDLL;
+      break;
+    case DLL_THREAD_DETACH:
+      break;
+    case DLL_PROCESS_DETACH:
+      break;
+  }
+  return TRUE;
+}
+
diff --git a/reactos/dll/msgina/msgina.def b/reactos/dll/msgina/msgina.def
new file mode 100644 (file)
index 0000000..063c4f3
--- /dev/null
@@ -0,0 +1,55 @@
+LIBRARY msgina.dll
+
+EXPORTS
+;MSGINA.1@0
+;MSGINA.2@0
+;MSGINA.3@0
+;MSGINA.4@0
+;MSGINA.5@0
+;MSGINA.6@0
+;MSGINA.7@0
+;MSGINA.8@0
+;MSGINA.9@0
+;MSGINA.10@0
+;MSGINA.11@0
+;MSGINA.12@0
+;MSGINA.13@0
+;MSGINA.14@0
+;MSGINA.15@0
+;MSGINA.16@0
+;MSGINA.17@0
+;MSGINA.18@0
+;MSGINA.19@0
+;MSGINA.20@0
+;MSGINA.21@0
+;MSGINA.22@0
+;MSGINA.23@0
+;MSGINA.24@0
+;MSGINA.25@0
+;MSGINA.26@0
+;MSGINA.27@0
+;MSGINA.28@0
+ShellShutdownDialog@12
+WlxActivateUserShell@16
+WlxDisplayLockedNotice@4
+WlxDisplaySASNotice@4
+WlxInitialize@20
+WlxIsLockOk@4
+WlxIsLogoffOk@4
+WlxLoggedOnSAS@12
+WlxLoggedOutSAS@32
+WlxLogoff@4
+WlxNegotiate@8
+WlxShutdown@8
+WlxWkstaLockedSAS@8
+WlxScreenSaverNotify@8
+WlxStartApplication@16
+WlxDisplayStatusMessage@20
+WlxGetStatusMessage@16
+WlxNetworkProviderLoad@8
+;WlxReconnectNotify@0
+WlxRemoveStatusMessage@4
+WlxDisconnectNotify@4
+WlxGetConsoleSwitchCredentials@8
+
+; EOF
diff --git a/reactos/dll/msgina/msgina.h b/reactos/dll/msgina/msgina.h
new file mode 100644 (file)
index 0000000..71046d8
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef __MSGINA_H
+#define __MSGINA_H
+
+#define GINA_VERSION (WLX_VERSION_1_3)
+
+#define PWLX_DISPATCH_VERSION PWLX_DISPATCH_VERSION_1_3
+
+typedef struct {
+  HANDLE hWlx;
+  LPWSTR station;
+  PWLX_DISPATCH_VERSION pWlxFuncs;
+  HANDLE hDllInstance;
+  HANDLE UserToken;
+  HWND hStatusWindow;
+  BOOL SignaledStatusWindowCreated;
+} GINA_CONTEXT, *PGINA_CONTEXT;
+
+HINSTANCE hDllInstance;
+
+#endif /* __MSGINA_H */
+
+/* EOF */
diff --git a/reactos/dll/msgina/msgina.rc b/reactos/dll/msgina/msgina.rc
new file mode 100644 (file)
index 0000000..d442474
--- /dev/null
@@ -0,0 +1,34 @@
+#include <windows.h>
+#include "resource.h"
+
+#define REACTOS_VERSION_DLL
+#define REACTOS_STR_FILE_DESCRIPTION   "ReactOS Logon GINA DLL\0"
+#define REACTOS_STR_INTERNAL_NAME      "msgina\0"
+#define REACTOS_STR_ORIGINAL_FILENAME  "msgina.dll\0"
+#include <reactos/version.rc>
+
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+IDI_LOGOFFICON ICON "resources/ico_logoff.ico"
+
+IDD_LOGOFF_DLG DIALOG 0, 0, 188, 60
+STYLE DS_FIXEDSYS | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Log Off ReactOS"
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+FONT 8, "MS Sans Serif"
+{
+   ICON       IDI_LOGOFFICON, IDC_LOGOFFICON, 7, 7, 21, 21, SS_ICON | WS_CHILD | WS_VISIBLE
+   LTEXT      "Are you sure you want to log off?", -1, 35, 16, 146, 8, SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP
+   PUSHBUTTON "&Log Off", 1, 41, 39, 50, 14, BS_DEFPUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP
+   PUSHBUTTON "&No", 2, 95, 39, 50, 14, BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP
+}
+
+IDD_STATUSWINDOW DIALOG 0, 0, 274, 26
+STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION
+CAPTION "Please wait..."
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+FONT 8, "MS Sans Serif"
+{
+    LTEXT "", IDC_STATUSLABEL, 7, 8, 234, 12, SS_ENDELLIPSIS
+}
+
diff --git a/reactos/dll/msgina/msgina.xml b/reactos/dll/msgina/msgina.xml
new file mode 100644 (file)
index 0000000..25eddd2
--- /dev/null
@@ -0,0 +1,16 @@
+<module name="msgina" type="win32dll" baseaddress="${BASEADDRESS_MSGINA}" installbase="system32" installname="msgina.dll">
+       <importlibrary definition="msgina.def" />
+       <include base="msgina">.</include>
+       <include base="msgina">include</include>
+       <define name="UNICODE" />
+       <define name="_UNICODE" />
+       <define name="__REACTOS__" />
+       <define name="__USE_W32API" />
+       <library>ntdll</library>
+       <library>kernel32</library>
+       <library>advapi32</library>
+       <library>user32</library>
+       <file>msgina.c</file>
+       <file>stubs.c</file>
+       <file>msgina.rc</file>
+</module>
diff --git a/reactos/dll/msgina/resource.h b/reactos/dll/msgina/resource.h
new file mode 100644 (file)
index 0000000..7c7f0b0
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef __MSGINA_RESOURCE_H
+#define __MSGINA_RESOURCE_H
+
+#define IDD_LOGOFF_DLG  2250
+#define IDD_STATUSWINDOW 2450
+
+#define IDC_LOGOFFICON  21
+#define IDC_STATUSLABEL  2451
+
+#define IDI_LOGOFFICON  2251
+
+#endif /* __MSGINA_RESOURCE_H */
+
+/* EOF */
diff --git a/reactos/dll/msgina/resources/ico_logoff.ico b/reactos/dll/msgina/resources/ico_logoff.ico
new file mode 100644 (file)
index 0000000..7bb5488
Binary files /dev/null and b/reactos/dll/msgina/resources/ico_logoff.ico differ
diff --git a/reactos/dll/msgina/stubs.c b/reactos/dll/msgina/stubs.c
new file mode 100644 (file)
index 0000000..e36fd15
--- /dev/null
@@ -0,0 +1,177 @@
+/* $Id$
+ *
+ * COPYRIGHT:       See COPYING in the top level directory
+ * PROJECT:         ReactOS msgina.dll
+ * FILE:            lib/msgina/stubs.c
+ * PURPOSE:         msgina.dll stubs
+ * PROGRAMMER:      Thomas Weidenmueller (w3seek@users.sourceforge.net)
+ * NOTES:           If you implement a function, remove it from this file
+ * UPDATE HISTORY:
+ *      24-11-2003  Created
+ */
+#include <windows.h>
+#include <winwlx.h>
+
+#include <wine/debug.h>
+
+/*
+ * @unimplemented
+ */
+DWORD WINAPI
+ShellShutdownDialog(
+    HWND  hParent,
+    DWORD Unknown,
+    BOOL  bHideLogoff)
+{
+  /* Return values:
+   * 0x00: Cancelled/Help
+   * 0x01: Log off user
+   * 0x02: Shutdown
+   * 0x04: Reboot
+   * 0x10: Standby
+   * 0x40: Hibernate
+   */
+  UNIMPLEMENTED;
+  return FALSE;
+}
+
+
+/*
+ * @unimplemented
+ */
+VOID WINAPI
+WlxDisplayLockedNotice(
+       PVOID pWlxContext)
+{
+  UNIMPLEMENTED;
+  return;
+}
+
+
+/*
+ * @unimplemented
+ */
+BOOL WINAPI
+WlxIsLockOk(
+       PVOID pWlxContext)
+{
+  UNIMPLEMENTED;
+  return FALSE;
+}
+
+
+/*
+ * @unimplemented
+ */
+BOOL WINAPI
+WlxIsLogoffOk(
+       PVOID pWlxContext)
+{
+  UNIMPLEMENTED;
+  return FALSE;
+}
+
+
+/*
+ * @unimplemented
+ */
+VOID WINAPI
+WlxLogoff(
+       PVOID pWlxContext)
+{
+  UNIMPLEMENTED;
+  return;
+}
+
+
+/*
+ * @unimplemented
+ */
+VOID WINAPI
+WlxShutdown(
+       PVOID pWlxContext,
+       DWORD ShutdownType)
+{
+  UNIMPLEMENTED;
+  return;
+}
+
+
+/*
+ * @unimplemented
+ */
+int WINAPI
+WlxWkstaLockedSAS(
+       PVOID pWlxContext,
+       DWORD dwSasType)
+{
+  UNIMPLEMENTED;
+  return 0;
+}
+
+
+/*
+ * @unimplemented
+ */
+BOOL WINAPI
+WlxScreenSaverNotify(
+       PVOID pWlxContext,
+       BOOL  *pSecure)
+{
+  UNIMPLEMENTED;
+  return FALSE;
+}
+
+
+/*
+ * @unimplemented
+ */
+BOOL WINAPI
+WlxGetStatusMessage(
+       PVOID pWlxContext,
+       DWORD *pdwOptions,
+       PWSTR pMessage,
+       DWORD dwBufferSize)
+{
+  UNIMPLEMENTED;
+  return FALSE;
+}
+
+
+/*
+ * @unimplemented
+ */
+BOOL WINAPI
+WlxNetworkProviderLoad(
+       PVOID                pWlxContext,
+       PWLX_MPR_NOTIFY_INFO pNprNotifyInfo)
+{
+  UNIMPLEMENTED;
+  return FALSE;
+}
+
+
+/*
+ * @unimplemented
+ */
+VOID WINAPI
+WlxDisconnectNotify(
+       PVOID pWlxContext)
+{
+  UNIMPLEMENTED;
+  return;
+}
+
+
+/*
+ * @unimplemented
+ */
+BOOL WINAPI
+WlxGetConsoleSwitchCredentials(
+       PVOID pWlxContext,
+       PVOID pCredInfo)
+{
+  UNIMPLEMENTED;
+  return FALSE;
+}
+
diff --git a/reactos/dll/msi/Makefile.in b/reactos/dll/msi/Makefile.in
new file mode 100644 (file)
index 0000000..e1216ae
--- /dev/null
@@ -0,0 +1,68 @@
+TOPSRCDIR = @top_srcdir@\r
+TOPOBJDIR = ../..\r
+SRCDIR    = @srcdir@\r
+VPATH     = @srcdir@\r
+MODULE    = msi.dll\r
+IMPORTLIB = libmsi.$(IMPLIBEXT)\r
+IMPORTS   = shell32 shlwapi cabinet oleaut32 ole32 version user32 gdi32 advapi32 kernel32\r
+EXTRALIBS = -luuid $(LIBUNICODE)\r
+\r
+C_SRCS = \\r
+       action.c \\r
+       appsearch.c \\r
+       classes.c \\r
+       create.c \\r
+       custom.c \\r
+       database.c \\r
+       delete.c \\r
+       dialog.c \\r
+       distinct.c \\r
+       events.c \\r
+       files.c \\r
+       format.c \\r
+       handle.c \\r
+       helpers.c \\r
+       insert.c \\r
+       install.c \\r
+       msi.c \\r
+       msiquery.c \\r
+       order.c \\r
+       package.c \\r
+       preview.c \\r
+       record.c \\r
+       registry.c \\r
+       regsvr.c \\r
+       select.c \\r
+       source.c \\r
+       string.c \\r
+       suminfo.c \\r
+       table.c \\r
+       tokenize.c \\r
+       update.c \\r
+       upgrade.c \\r
+       where.c\r
+\r
+RC_SRCS = msi.rc\r
+\r
+EXTRA_SRCS = sql.y cond.y\r
+EXTRA_OBJS = sql.tab.o cond.tab.o\r
+\r
+SUBDIRS = tests\r
+\r
+@MAKE_DLL_RULES@\r
+\r
+sql.tab.c sql.tab.h: sql.y\r
+       $(BISON) -p SQL_ -d $(SRCDIR)/sql.y -o sql.tab.c\r
+\r
+cond.tab.c cond.tab.h: cond.y\r
+       $(BISON) -p COND_ -d $(SRCDIR)/cond.y -o cond.tab.c\r
+\r
+# hack to allow parallel make\r
+sql.tab.h: sql.tab.c\r
+sql.tab.o: sql.tab.h\r
+cond.tab.h: cond.tab.c\r
+cond.tab.o: cond.tab.h\r
+\r
+tokenize.o: sql.tab.h\r
+\r
+### Dependencies:\r
diff --git a/reactos/dll/msi/action.c b/reactos/dll/msi/action.c
new file mode 100644 (file)
index 0000000..ed32e96
--- /dev/null
@@ -0,0 +1,4327 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2004,2005 Aric Stewart for CodeWeavers
+ *
+ * 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
+ */
+
+/*
+ * Pages I need
+ *
+http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/installexecutesequence_table.asp
+
+http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/standard_actions_reference.asp
+ */
+
+#include <stdarg.h>
+
+#define COBJMACROS
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "winreg.h"
+#include "wine/debug.h"
+#include "msidefs.h"
+#include "msipriv.h"
+#include "winuser.h"
+#include "shlobj.h"
+#include "wine/unicode.h"
+#include "winver.h"
+#include "action.h"
+
+#define REG_PROGRESS_VALUE 13200
+#define COMPONENT_PROGRESS_VALUE 24000
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+/*
+ * Prototypes
+ */
+static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran);
+static UINT ACTION_ProcessUISequence(MSIPACKAGE *package);
+static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI);
+
+/*
+ * consts and values used
+ */
+static const WCHAR c_colon[] = {'C',':','\\',0};
+
+static const WCHAR szCreateFolders[] =
+    {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
+static const WCHAR szCostFinalize[] =
+    {'C','o','s','t','F','i','n','a','l','i','z','e',0};
+const WCHAR szInstallFiles[] =
+    {'I','n','s','t','a','l','l','F','i','l','e','s',0};
+const WCHAR szDuplicateFiles[] =
+    {'D','u','p','l','i','c','a','t','e','F','i','l','e','s',0};
+static const WCHAR szWriteRegistryValues[] =
+    {'W','r','i','t','e','R','e','g','i','s','t','r','y',
+            'V','a','l','u','e','s',0};
+static const WCHAR szCostInitialize[] =
+    {'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0};
+static const WCHAR szFileCost[] = 
+    {'F','i','l','e','C','o','s','t',0};
+static const WCHAR szInstallInitialize[] = 
+    {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
+static const WCHAR szInstallValidate[] = 
+    {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
+static const WCHAR szLaunchConditions[] = 
+    {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
+static const WCHAR szProcessComponents[] = 
+    {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
+static const WCHAR szRegisterTypeLibraries[] = 
+    {'R','e','g','i','s','t','e','r','T','y','p','e',
+            'L','i','b','r','a','r','i','e','s',0};
+const WCHAR szRegisterClassInfo[] = 
+    {'R','e','g','i','s','t','e','r','C','l','a','s','s','I','n','f','o',0};
+const WCHAR szRegisterProgIdInfo[] = 
+    {'R','e','g','i','s','t','e','r','P','r','o','g','I','d','I','n','f','o',0};
+static const WCHAR szCreateShortcuts[] = 
+    {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
+static const WCHAR szPublishProduct[] = 
+    {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
+static const WCHAR szWriteIniValues[] = 
+    {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
+static const WCHAR szSelfRegModules[] = 
+    {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
+static const WCHAR szPublishFeatures[] = 
+    {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
+static const WCHAR szRegisterProduct[] = 
+    {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
+static const WCHAR szInstallExecute[] = 
+    {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
+static const WCHAR szInstallExecuteAgain[] = 
+    {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',
+            'A','g','a','i','n',0};
+static const WCHAR szInstallFinalize[] = 
+    {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
+static const WCHAR szForceReboot[] = 
+    {'F','o','r','c','e','R','e','b','o','o','t',0};
+static const WCHAR szResolveSource[] =
+    {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
+const WCHAR szAppSearch[] = 
+    {'A','p','p','S','e','a','r','c','h',0};
+static const WCHAR szAllocateRegistrySpace[] = 
+    {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y',
+            'S','p','a','c','e',0};
+static const WCHAR szBindImage[] = 
+    {'B','i','n','d','I','m','a','g','e',0};
+static const WCHAR szCCPSearch[] = 
+    {'C','C','P','S','e','a','r','c','h',0};
+static const WCHAR szDeleteServices[] = 
+    {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
+static const WCHAR szDisableRollback[] = 
+    {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
+static const WCHAR szExecuteAction[] = 
+    {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
+const WCHAR szFindRelatedProducts[] = 
+    {'F','i','n','d','R','e','l','a','t','e','d',
+            'P','r','o','d','u','c','t','s',0};
+static const WCHAR szInstallAdminPackage[] = 
+    {'I','n','s','t','a','l','l','A','d','m','i','n',
+            'P','a','c','k','a','g','e',0};
+static const WCHAR szInstallSFPCatalogFile[] = 
+    {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g',
+            'F','i','l','e',0};
+static const WCHAR szIsolateComponents[] = 
+    {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
+const WCHAR szMigrateFeatureStates[] = 
+    {'M','i','g','r','a','t','e','F','e','a','t','u','r','e',
+            'S','t','a','t','e','s',0};
+const WCHAR szMoveFiles[] = 
+    {'M','o','v','e','F','i','l','e','s',0};
+static const WCHAR szMsiPublishAssemblies[] = 
+    {'M','s','i','P','u','b','l','i','s','h',
+            'A','s','s','e','m','b','l','i','e','s',0};
+static const WCHAR szMsiUnpublishAssemblies[] = 
+    {'M','s','i','U','n','p','u','b','l','i','s','h',
+            'A','s','s','e','m','b','l','i','e','s',0};
+static const WCHAR szInstallODBC[] = 
+    {'I','n','s','t','a','l','l','O','D','B','C',0};
+static const WCHAR szInstallServices[] = 
+    {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
+const WCHAR szPatchFiles[] = 
+    {'P','a','t','c','h','F','i','l','e','s',0};
+static const WCHAR szPublishComponents[] = 
+    {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
+static const WCHAR szRegisterComPlus[] =
+    {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
+const WCHAR szRegisterExtensionInfo[] =
+    {'R','e','g','i','s','t','e','r','E','x','t','e','n','s','i','o','n',
+            'I','n','f','o',0};
+static const WCHAR szRegisterFonts[] =
+    {'R','e','g','i','s','t','e','r','F','o','n','t','s',0};
+const WCHAR szRegisterMIMEInfo[] =
+    {'R','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
+static const WCHAR szRegisterUser[] =
+    {'R','e','g','i','s','t','e','r','U','s','e','r',0};
+const WCHAR szRemoveDuplicateFiles[] =
+    {'R','e','m','o','v','e','D','u','p','l','i','c','a','t','e',
+            'F','i','l','e','s',0};
+static const WCHAR szRemoveEnvironmentStrings[] =
+    {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t',
+            'S','t','r','i','n','g','s',0};
+const WCHAR szRemoveExistingProducts[] =
+    {'R','e','m','o','v','e','E','x','i','s','t','i','n','g',
+            'P','r','o','d','u','c','t','s',0};
+const WCHAR szRemoveFiles[] =
+    {'R','e','m','o','v','e','F','i','l','e','s',0};
+static const WCHAR szRemoveFolders[] =
+    {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
+static const WCHAR szRemoveIniValues[] =
+    {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
+static const WCHAR szRemoveODBC[] =
+    {'R','e','m','o','v','e','O','D','B','C',0};
+static const WCHAR szRemoveRegistryValues[] =
+    {'R','e','m','o','v','e','R','e','g','i','s','t','r','y',
+            'V','a','l','u','e','s',0};
+static const WCHAR szRemoveShortcuts[] =
+    {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
+static const WCHAR szRMCCPSearch[] =
+    {'R','M','C','C','P','S','e','a','r','c','h',0};
+static const WCHAR szScheduleReboot[] =
+    {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
+static const WCHAR szSelfUnregModules[] =
+    {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
+static const WCHAR szSetODBCFolders[] =
+    {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
+static const WCHAR szStartServices[] =
+    {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
+static const WCHAR szStopServices[] =
+    {'S','t','o','p','S','e','r','v','i','c','e','s',0};
+static const WCHAR szUnpublishComponents[] =
+    {'U','n','p','u','b','l','i','s','h',
+            'C','o','m','p','o','n','e','n','t','s',0};
+static const WCHAR szUnpublishFeatures[] =
+    {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
+const WCHAR szUnregisterClassInfo[] =
+    {'U','n','r','e','g','i','s','t','e','r','C','l','a','s','s',
+            'I','n','f','o',0};
+static const WCHAR szUnregisterComPlus[] =
+    {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
+const WCHAR szUnregisterExtensionInfo[] =
+    {'U','n','r','e','g','i','s','t','e','r',
+            'E','x','t','e','n','s','i','o','n','I','n','f','o',0};
+static const WCHAR szUnregisterFonts[] =
+    {'U','n','r','e','g','i','s','t','e','r','F','o','n','t','s',0};
+const WCHAR szUnregisterMIMEInfo[] =
+    {'U','n','r','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
+const WCHAR szUnregisterProgIdInfo[] =
+    {'U','n','r','e','g','i','s','t','e','r','P','r','o','g','I','d',
+            'I','n','f','o',0};
+static const WCHAR szUnregisterTypeLibraries[] =
+    {'U','n','r','e','g','i','s','t','e','r','T','y','p','e',
+            'L','i','b','r','a','r','i','e','s',0};
+static const WCHAR szValidateProductID[] =
+    {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
+static const WCHAR szWriteEnvironmentStrings[] =
+    {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t',
+            'S','t','r','i','n','g','s',0};
+
+/* action handlers */
+typedef UINT (*STANDARDACTIONHANDLER)(MSIPACKAGE*);
+
+struct _actions {
+    LPCWSTR action;
+    STANDARDACTIONHANDLER handler;
+};
+
+static struct _actions StandardActions[];
+
+
+/********************************************************
+ * helper functions
+ ********************************************************/
+
+static void ce_actiontext(MSIPACKAGE* package, LPCWSTR action)
+{
+    static const WCHAR szActionText[] = 
+        {'A','c','t','i','o','n','T','e','x','t',0};
+    MSIRECORD *row;
+
+    row = MSI_CreateRecord(1);
+    MSI_RecordSetStringW(row,1,action);
+    ControlEvent_FireSubscribedEvent(package,szActionText, row);
+    msiobj_release(&row->hdr);
+}
+
+static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
+{
+    static const WCHAR template_s[]=
+        {'A','c','t','i','o','n',' ','%','s',':',' ','%','s','.',' ', '%','s',
+         '.',0};
+    static const WCHAR format[] = 
+        {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
+    static const WCHAR Query_t[] = 
+        {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
+         '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
+         'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=', 
+         ' ','\'','%','s','\'',0};
+    WCHAR message[1024];
+    WCHAR timet[0x100];
+    MSIRECORD * row = 0;
+    LPCWSTR ActionText;
+    LPWSTR deformated;
+
+    GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
+
+    row = MSI_QueryGetRecord( package->db, Query_t, action );
+    if (!row)
+        return;
+
+    ActionText = MSI_RecordGetString(row,2);
+    deformat_string(package, ActionText, &deformated);
+
+    sprintfW(message,template_s,timet,action,deformated);
+    ce_actiontext(package, deformated);
+    msiobj_release(&row->hdr);
+
+    row = MSI_CreateRecord(1);
+    MSI_RecordSetStringW(row,1,message);
+    MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
+    msiobj_release(&row->hdr);
+    msi_free(deformated);
+}
+
+static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start, 
+                          UINT rc)
+{
+    MSIRECORD * row;
+    static const WCHAR template_s[]=
+        {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
+         '%','s', '.',0};
+    static const WCHAR template_e[]=
+        {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
+         '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
+         '%','i','.',0};
+    static const WCHAR format[] = 
+        {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
+    WCHAR message[1024];
+    WCHAR timet[0x100];
+
+    GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
+    if (start)
+        sprintfW(message,template_s,timet,action);
+    else
+        sprintfW(message,template_e,timet,action,rc);
+    
+    row = MSI_CreateRecord(1);
+    MSI_RecordSetStringW(row,1,message);
+    MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
+    msiobj_release(&row->hdr);
+}
+
+static int msi_get_property_int( MSIPACKAGE *package, LPCWSTR prop, int def )
+{
+    LPWSTR str = msi_dup_property( package, prop );
+    int val = str ? atoiW( str ) : def;
+    msi_free( str );
+    return val;
+}
+
+static UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine )
+{
+    LPCWSTR ptr,ptr2;
+    BOOL quote;
+    DWORD len;
+    LPWSTR prop = NULL, val = NULL;
+
+    if (!szCommandLine)
+        return ERROR_SUCCESS;
+
+    ptr = szCommandLine;
+       
+    while (*ptr)
+    {
+        if (*ptr==' ')
+        {
+            ptr++;
+            continue;
+        }
+
+        TRACE("Looking at %s\n",debugstr_w(ptr));
+
+        ptr2 = strchrW(ptr,'=');
+        if (!ptr2)
+        {
+            ERR("command line contains unknown string : %s\n", debugstr_w(ptr));
+            break;
+        }
+        quote = FALSE;
+
+        len = ptr2-ptr;
+        prop = msi_alloc((len+1)*sizeof(WCHAR));
+        memcpy(prop,ptr,len*sizeof(WCHAR));
+        prop[len]=0;
+        ptr2++;
+       
+        len = 0; 
+        ptr = ptr2; 
+        while (*ptr && (quote || (!quote && *ptr!=' ')))
+        {
+            if (*ptr == '"')
+                quote = !quote;
+            ptr++;
+            len++;
+        }
+       
+        if (*ptr2=='"')
+        {
+            ptr2++;
+            len -= 2;
+        }
+        val = msi_alloc((len+1)*sizeof(WCHAR));
+        memcpy(val,ptr2,len*sizeof(WCHAR));
+        val[len] = 0;
+
+        if (lstrlenW(prop) > 0)
+        {
+            TRACE("Found commandline property (%s) = (%s)\n", 
+                   debugstr_w(prop), debugstr_w(val));
+            MSI_SetPropertyW(package,prop,val);
+        }
+        msi_free(val);
+        msi_free(prop);
+    }
+
+    return ERROR_SUCCESS;
+}
+
+
+static LPWSTR* msi_split_string( LPCWSTR str, WCHAR sep )
+{
+    LPWSTR p, *ret = NULL;
+    UINT count = 0;
+
+    if (!str)
+        return ret;
+
+    /* count the number of substrings */
+    for ( p = (LPWSTR)str, count = 0; p; count++ )
+    {
+        p = strchrW( p, sep );
+        if (p)
+            p++;
+    }
+
+    /* allocate space for an array of substring pointers and the substrings */
+    ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
+                     (lstrlenW(str)+1) * sizeof(WCHAR) );
+    if (!ret)
+        return ret;
+
+    /* copy the string and set the pointers */
+    p = (LPWSTR) &ret[count+1];
+    lstrcpyW( p, str );
+    for( count = 0; (ret[count] = p); count++ )
+    {
+        p = strchrW( p, sep );
+        if (p)
+            *p++ = 0;
+    }
+
+    return ret;
+}
+
+static UINT msi_apply_substorage_transform( MSIPACKAGE *package,
+                                 MSIDATABASE *patch_db, LPCWSTR name )
+{
+    UINT ret = ERROR_FUNCTION_FAILED;
+    IStorage *stg = NULL;
+    HRESULT r;
+
+    TRACE("%p %s\n", package, debugstr_w(name) );
+
+    if (*name++ != ':')
+    {
+        ERR("expected a colon in %s\n", debugstr_w(name));
+        return ERROR_FUNCTION_FAILED;
+    }
+
+    r = IStorage_OpenStorage( patch_db->storage, name, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
+    if (SUCCEEDED(r))
+    {
+        ret = msi_table_apply_transform( package->db, stg );
+        IStorage_Release( stg );
+        ret = ERROR_SUCCESS;
+    }
+    else
+        ERR("failed to open substorage %s\n", debugstr_w(name));
+
+    return ret;
+}
+
+static UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
+{
+    static const WCHAR szProdID[] = { 'P','r','o','d','u','c','t','I','D',0 };
+    LPWSTR guid_list, *guids, product_id;
+    UINT i, ret = ERROR_FUNCTION_FAILED;
+
+    product_id = msi_dup_property( package, szProdID );
+    if (!product_id)
+    {
+        /* FIXME: the property ProductID should be written into the DB somewhere */
+        ERR("no product ID to check\n");
+        return ERROR_SUCCESS;
+    }
+
+    guid_list = msi_suminfo_dup_string( si, PID_TEMPLATE );
+    guids = msi_split_string( guid_list, ';' );
+    for ( i = 0; guids[i] && ret != ERROR_SUCCESS; i++ )
+    {
+        if (!lstrcmpW( guids[i], product_id ))
+            ret = ERROR_SUCCESS;
+    }
+    msi_free( guids );
+    msi_free( guid_list );
+    msi_free( product_id );
+
+    return ret;
+}
+
+static UINT msi_parse_patch_summary( MSIPACKAGE *package, MSIDATABASE *patch_db )
+{
+    MSISUMMARYINFO *si;
+    LPWSTR str, *substorage;
+    UINT i, r = ERROR_SUCCESS;
+
+    si = MSI_GetSummaryInformationW( patch_db, 0 );
+    if (!si)
+        return ERROR_FUNCTION_FAILED;
+
+    msi_check_patch_applicable( package, si );
+
+    /* enumerate the substorage */
+    str = msi_suminfo_dup_string( si, PID_LASTAUTHOR );
+    substorage = msi_split_string( str, ';' );
+    for ( i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++ )
+        r = msi_apply_substorage_transform( package, patch_db, substorage[i] );
+    msi_free( substorage );
+    msi_free( str );
+
+    /* FIXME: parse the sources in PID_REVNUMBER and do something with them... */
+
+    msiobj_release( &si->hdr );
+
+    return r;
+}
+
+static UINT msi_apply_patch_package( MSIPACKAGE *package, LPCWSTR file )
+{
+    MSIDATABASE *patch_db = NULL;
+    UINT r;
+
+    TRACE("%p %s\n", package, debugstr_w( file ) );
+
+    /* FIXME:
+     *  We probably want to make sure we only open a patch collection here.
+     *  Patch collections (.msp) and databases (.msi) have different GUIDs
+     *  but currently MSI_OpenDatabaseW will accept both.
+     */
+    r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY, &patch_db );
+    if ( r != ERROR_SUCCESS )
+    {
+        ERR("failed to open patch collection %s\n", debugstr_w( file ) );
+        return r;
+    }
+
+    msi_parse_patch_summary( package, patch_db );
+    msiobj_release( &patch_db->hdr );
+
+    return ERROR_SUCCESS;
+}
+
+/* get the PATCH property, and apply all the patches it specifies */
+static UINT msi_apply_patches( MSIPACKAGE *package )
+{
+    static const WCHAR szPatch[] = { 'P','A','T','C','H',0 };
+    LPWSTR patch_list, *patches;
+    UINT i, r = ERROR_SUCCESS;
+
+    patch_list = msi_dup_property( package, szPatch );
+
+    TRACE("patches to be applied: %s\n", debugstr_w( patch_list ) );
+
+    patches = msi_split_string( patch_list, ';' );
+    for( i=0; patches && patches[i] && r == ERROR_SUCCESS; i++ )
+        r = msi_apply_patch_package( package, patches[i] );
+
+    msi_free( patches );
+    msi_free( patch_list );
+
+    return r;
+}
+
+static UINT msi_apply_transforms( MSIPACKAGE *package )
+{
+    static const WCHAR szTransforms[] = {
+        'T','R','A','N','S','F','O','R','M','S',0 };
+    LPWSTR xform_list, *xforms;
+    UINT i, r = ERROR_SUCCESS;
+
+    xform_list = msi_dup_property( package, szTransforms );
+    xforms = msi_split_string( xform_list, ';' );
+
+    for( i=0; xforms && xforms[i] && r == ERROR_SUCCESS; i++ )
+    {
+        if (xforms[i][0] == ':')
+            r = msi_apply_substorage_transform( package, package->db, &xforms[i][1] );
+        else
+            r = MSI_DatabaseApplyTransformW( package->db, xforms[i], 0 );
+    }
+
+    msi_free( xforms );
+    msi_free( xform_list );
+
+    return r;
+}
+
+/****************************************************
+ * TOP level entry points 
+ *****************************************************/
+
+UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
+                         LPCWSTR szCommandLine )
+{
+    UINT rc;
+    BOOL ui = FALSE;
+    static const WCHAR szUILevel[] = {'U','I','L','e','v','e','l',0};
+    static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
+    static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
+
+    MSI_SetPropertyW(package, szAction, szInstall);
+
+    package->script = msi_alloc(sizeof(MSISCRIPT));
+    memset(package->script,0,sizeof(MSISCRIPT));
+
+    package->script->InWhatSequence = SEQUENCE_INSTALL;
+
+    if (szPackagePath)   
+    {
+        LPWSTR p, check, path;
+        package->PackagePath = strdupW(szPackagePath);
+        path = strdupW(szPackagePath);
+        p = strrchrW(path,'\\');    
+        if (p)
+        {
+            p++;
+            *p=0;
+        }
+        else
+        {
+            msi_free(path);
+            path = msi_alloc(MAX_PATH*sizeof(WCHAR));
+            GetCurrentDirectoryW(MAX_PATH,path);
+            strcatW(path,cszbs);
+        }
+
+        check = msi_dup_property( package, cszSourceDir );
+        if (!check)
+            MSI_SetPropertyW(package, cszSourceDir, path);
+        msi_free(check);
+        msi_free(path);
+    }
+
+    msi_parse_command_line( package, szCommandLine );
+
+    msi_apply_transforms( package );
+    msi_apply_patches( package );
+
+    if ( msi_get_property_int(package, szUILevel, 0) >= INSTALLUILEVEL_REDUCED )
+    {
+        package->script->InWhatSequence |= SEQUENCE_UI;
+        rc = ACTION_ProcessUISequence(package);
+        ui = TRUE;
+        if (rc == ERROR_SUCCESS)
+        {
+            package->script->InWhatSequence |= SEQUENCE_EXEC;
+            rc = ACTION_ProcessExecSequence(package,TRUE);
+        }
+    }
+    else
+        rc = ACTION_ProcessExecSequence(package,FALSE);
+    
+    if (rc == -1)
+    {
+        /* install was halted but should be considered a success */
+        rc = ERROR_SUCCESS;
+    }
+
+    package->script->CurrentlyScripting= FALSE;
+
+    /* process the ending type action */
+    if (rc == ERROR_SUCCESS)
+        ACTION_PerformActionSequence(package,-1,ui);
+    else if (rc == ERROR_INSTALL_USEREXIT) 
+        ACTION_PerformActionSequence(package,-2,ui);
+    else if (rc == ERROR_INSTALL_SUSPEND) 
+        ACTION_PerformActionSequence(package,-4,ui);
+    else  /* failed */
+        ACTION_PerformActionSequence(package,-3,ui);
+
+    /* finish up running custom actions */
+    ACTION_FinishCustomActions(package);
+    
+    return rc;
+}
+
+static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI)
+{
+    UINT rc = ERROR_SUCCESS;
+    MSIRECORD * row = 0;
+    static const WCHAR ExecSeqQuery[] =
+        {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
+         '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
+         'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
+         '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
+
+    static const WCHAR UISeqQuery[] =
+        {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
+     '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
+     '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
+        ' ', '=',' ','%','i',0};
+
+    if (UI)
+        row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
+    else
+        row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
+
+    if (row)
+    {
+        LPCWSTR action, cond;
+
+        TRACE("Running the actions\n"); 
+
+        /* check conditions */
+        cond = MSI_RecordGetString(row,2);
+        if (cond)
+        {
+            /* this is a hack to skip errors in the condition code */
+            if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
+                goto end;
+        }
+
+        action = MSI_RecordGetString(row,1);
+        if (!action)
+        {
+            ERR("failed to fetch action\n");
+            rc = ERROR_FUNCTION_FAILED;
+            goto end;
+        }
+
+        if (UI)
+            rc = ACTION_PerformUIAction(package,action);
+        else
+            rc = ACTION_PerformAction(package,action,FALSE);
+end:
+        msiobj_release(&row->hdr);
+    }
+    else
+        rc = ERROR_SUCCESS;
+
+    return rc;
+}
+
+typedef struct {
+    MSIPACKAGE* package;
+    BOOL UI;
+} iterate_action_param;
+
+static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
+{
+    iterate_action_param *iap= (iterate_action_param*)param;
+    UINT rc;
+    LPCWSTR cond, action;
+
+    action = MSI_RecordGetString(row,1);
+    if (!action)
+    {
+        ERR("Error is retrieving action name\n");
+        return  ERROR_FUNCTION_FAILED;
+    }
+
+    /* check conditions */
+    cond = MSI_RecordGetString(row,2);
+    if (cond)
+    {
+        /* this is a hack to skip errors in the condition code */
+        if (MSI_EvaluateConditionW(iap->package, cond) == MSICONDITION_FALSE)
+        {
+            TRACE("Skipping action: %s (condition is false)\n",
+                            debugstr_w(action));
+            return ERROR_SUCCESS;
+        }
+    }
+
+    if (iap->UI)
+        rc = ACTION_PerformUIAction(iap->package,action);
+    else
+        rc = ACTION_PerformAction(iap->package,action,FALSE);
+
+    msi_dialog_check_messages( NULL );
+
+    if (iap->package->CurrentInstallState != ERROR_SUCCESS )
+        rc = iap->package->CurrentInstallState;
+
+    if (rc == ERROR_FUNCTION_NOT_CALLED)
+        rc = ERROR_SUCCESS;
+
+    if (rc != ERROR_SUCCESS)
+        ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
+
+    return rc;
+}
+
+UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
+{
+    MSIQUERY * view;
+    UINT r;
+    static const WCHAR query[] =
+        {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
+         '`','%','s','`',
+         ' ','W','H','E','R','E',' ', 
+         '`','S','e','q','u','e','n','c','e','`',' ',
+         '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
+         '`','S','e','q','u','e','n','c','e','`',0};
+    iterate_action_param iap;
+
+    /*
+     * FIXME: probably should be checking UILevel in the
+     *       ACTION_PerformUIAction/ACTION_PerformAction
+     *       rather than saving the UI level here. Those
+     *       two functions can be merged too.
+     */
+    iap.package = package;
+    iap.UI = TRUE;
+
+    TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
+
+    r = MSI_OpenQuery( package->db, &view, query, szTable );
+    if (r == ERROR_SUCCESS)
+    {
+        r = MSI_IterateRecords( view, NULL, ITERATE_Actions, &iap );
+        msiobj_release(&view->hdr);
+    }
+
+    return r;
+}
+
+static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
+{
+    MSIQUERY * view;
+    UINT rc;
+    static const WCHAR ExecSeqQuery[] =
+        {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
+         '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
+         'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
+         '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
+         'O','R','D','E','R',' ', 'B','Y',' ',
+         '`','S','e','q','u','e','n','c','e','`',0 };
+    MSIRECORD * row = 0;
+    static const WCHAR IVQuery[] =
+        {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
+         ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
+         'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
+         'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
+         ' ','\'', 'I','n','s','t','a','l','l',
+         'V','a','l','i','d','a','t','e','\'', 0};
+    INT seq = 0;
+    iterate_action_param iap;
+
+    iap.package = package;
+    iap.UI = FALSE;
+
+    if (package->script->ExecuteSequenceRun)
+    {
+        TRACE("Execute Sequence already Run\n");
+        return ERROR_SUCCESS;
+    }
+
+    package->script->ExecuteSequenceRun = TRUE;
+
+    /* get the sequence number */
+    if (UIran)
+    {
+        row = MSI_QueryGetRecord(package->db, IVQuery);
+        if( !row )
+            return ERROR_FUNCTION_FAILED;
+        seq = MSI_RecordGetInteger(row,1);
+        msiobj_release(&row->hdr);
+    }
+
+    rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
+    if (rc == ERROR_SUCCESS)
+    {
+        TRACE("Running the actions\n");
+
+        rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
+        msiobj_release(&view->hdr);
+    }
+
+    return rc;
+}
+
+static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
+{
+    MSIQUERY * view;
+    UINT rc;
+    static const WCHAR ExecSeqQuery [] =
+        {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
+         '`','I','n','s','t','a','l','l',
+         'U','I','S','e','q','u','e','n','c','e','`',
+         ' ','W','H','E','R','E',' ', 
+         '`','S','e','q','u','e','n','c','e','`',' ',
+         '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
+         '`','S','e','q','u','e','n','c','e','`',0};
+    iterate_action_param iap;
+
+    iap.package = package;
+    iap.UI = TRUE;
+
+    rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
+    
+    if (rc == ERROR_SUCCESS)
+    {
+        TRACE("Running the actions\n"); 
+
+        rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
+        msiobj_release(&view->hdr);
+    }
+
+    return rc;
+}
+
+/********************************************************
+ * ACTION helper functions and functions that perform the actions
+ *******************************************************/
+static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action, 
+                                        UINT* rc, BOOL force )
+{
+    BOOL ret = FALSE; 
+    BOOL run = force;
+    int i;
+
+    if (!package)
+    {
+        ERR("package was null!\n");
+        return FALSE;
+    }
+
+    if (!run && !package->script->CurrentlyScripting)
+        run = TRUE;
+   
+    if (!run)
+    {
+        if (strcmpW(action,szInstallFinalize) == 0 ||
+            strcmpW(action,szInstallExecute) == 0 ||
+            strcmpW(action,szInstallExecuteAgain) == 0) 
+                run = TRUE;
+    }
+    
+    i = 0;
+    while (StandardActions[i].action != NULL)
+    {
+        if (strcmpW(StandardActions[i].action, action)==0)
+        {
+            if (!run)
+            {
+                ui_actioninfo(package, action, TRUE, 0);
+                *rc = schedule_action(package,INSTALL_SCRIPT,action);
+                ui_actioninfo(package, action, FALSE, *rc);
+            }
+            else
+            {
+                ui_actionstart(package, action);
+                if (StandardActions[i].handler)
+                {
+                    *rc = StandardActions[i].handler(package);
+                }
+                else
+                {
+                    FIXME("unhandled standard action %s\n",debugstr_w(action));
+                    *rc = ERROR_SUCCESS;
+                }
+            }
+            ret = TRUE;
+            break;
+        }
+        i++;
+    }
+    return ret;
+}
+
+static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
+                                       UINT* rc, BOOL force )
+{
+    BOOL ret=FALSE;
+    UINT arc;
+
+    arc = ACTION_CustomAction(package,action, force);
+
+    if (arc != ERROR_CALL_NOT_IMPLEMENTED)
+    {
+        *rc = arc;
+        ret = TRUE;
+    }
+    return ret;
+}
+
+/* 
+ * A lot of actions are really important even if they don't do anything
+ * explicit... Lots of properties are set at the beginning of the installation
+ * CostFinalize does a bunch of work to translate the directories and such
+ * 
+ * But until I get write access to the database that is hard, so I am going to
+ * hack it to see if I can get something to run.
+ */
+UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, BOOL force)
+{
+    UINT rc = ERROR_SUCCESS; 
+    BOOL handled;
+
+    TRACE("Performing action (%s)\n",debugstr_w(action));
+
+    handled = ACTION_HandleStandardAction(package, action, &rc, force);
+
+    if (!handled)
+        handled = ACTION_HandleCustomAction(package, action, &rc, force);
+
+    if (!handled)
+    {
+        FIXME("unhandled msi action %s\n",debugstr_w(action));
+        rc = ERROR_FUNCTION_NOT_CALLED;
+    }
+
+    return rc;
+}
+
+UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action)
+{
+    UINT rc = ERROR_SUCCESS;
+    BOOL handled = FALSE;
+
+    TRACE("Performing action (%s)\n",debugstr_w(action));
+
+    handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
+
+    if (!handled)
+        handled = ACTION_HandleCustomAction(package, action, &rc, FALSE);
+
+    if( !handled && ACTION_DialogBox(package,action) == ERROR_SUCCESS )
+        handled = TRUE;
+
+    if (!handled)
+    {
+        FIXME("unhandled msi action %s\n",debugstr_w(action));
+        rc = ERROR_FUNCTION_NOT_CALLED;
+    }
+
+    return rc;
+}
+
+
+/*
+ * Actual Action Handlers
+ */
+
+static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
+{
+    MSIPACKAGE *package = (MSIPACKAGE*)param;
+    LPCWSTR dir;
+    LPWSTR full_path;
+    MSIRECORD *uirow;
+    MSIFOLDER *folder;
+
+    dir = MSI_RecordGetString(row,1);
+    if (!dir)
+    {
+        ERR("Unable to get folder id\n");
+        return ERROR_SUCCESS;
+    }
+
+    full_path = resolve_folder(package,dir,FALSE,FALSE,&folder);
+    if (!full_path)
+    {
+        ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
+        return ERROR_SUCCESS;
+    }
+
+    TRACE("Folder is %s\n",debugstr_w(full_path));
+
+    /* UI stuff */
+    uirow = MSI_CreateRecord(1);
+    MSI_RecordSetStringW(uirow,1,full_path);
+    ui_actiondata(package,szCreateFolders,uirow);
+    msiobj_release( &uirow->hdr );
+
+    if (folder->State == 0)
+        create_full_pathW(full_path);
+
+    folder->State = 3;
+
+    msi_free(full_path);
+    return ERROR_SUCCESS;
+}
+
+/* FIXME: probably should merge this with the above function */
+static UINT msi_create_directory( MSIPACKAGE* package, LPCWSTR dir )
+{
+    UINT rc = ERROR_SUCCESS;
+    MSIFOLDER *folder;
+    LPWSTR install_path;
+
+    install_path = resolve_folder(package, dir, FALSE, FALSE, &folder);
+    if (!install_path)
+        return ERROR_FUNCTION_FAILED; 
+
+    /* create the path */
+    if (folder->State == 0)
+    {
+        create_full_pathW(install_path);
+        folder->State = 2;
+    }
+    msi_free(install_path);
+
+    return rc;
+}
+
+UINT msi_create_component_directories( MSIPACKAGE *package )
+{
+    MSICOMPONENT *comp;
+
+    /* create all the folders required by the components are going to install */
+    LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
+    {
+        if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
+            continue;
+        msi_create_directory( package, comp->Directory );
+    }
+
+    return ERROR_SUCCESS;
+}
+
+/*
+ * Also we cannot enable/disable components either, so for now I am just going 
+ * to do all the directories for all the components.
+ */
+static UINT ACTION_CreateFolders(MSIPACKAGE *package)
+{
+    static const WCHAR ExecSeqQuery[] =
+        {'S','E','L','E','C','T',' ',
+         '`','D','i','r','e','c','t','o','r','y','_','`',
+         ' ','F','R','O','M',' ',
+         '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
+    UINT rc;
+    MSIQUERY *view;
+
+    /* create all the empty folders specified in the CreateFolder table */
+    rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
+    if (rc != ERROR_SUCCESS)
+        return ERROR_SUCCESS;
+
+    rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
+    msiobj_release(&view->hdr);
+
+    msi_create_component_directories( package );
+
+    return rc;
+}
+
+static MSICOMPONENT* load_component( MSIRECORD * row )
+{
+    MSICOMPONENT *comp;
+
+    comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
+    if (!comp)
+        return comp;
+
+    /* fill in the data */
+    comp->Component = msi_dup_record_field( row, 1 );
+
+    TRACE("Loading Component %s\n", debugstr_w(comp->Component));
+
+    comp->ComponentId = msi_dup_record_field( row, 2 );
+    comp->Directory = msi_dup_record_field( row, 3 );
+    comp->Attributes = MSI_RecordGetInteger(row,4);
+    comp->Condition = msi_dup_record_field( row, 5 );
+    comp->KeyPath = msi_dup_record_field( row, 6 );
+
+    comp->Installed = INSTALLSTATE_ABSENT;
+    comp->Action = INSTALLSTATE_UNKNOWN;
+    comp->ActionRequest = INSTALLSTATE_UNKNOWN;
+
+    comp->Enabled = TRUE;
+
+    return comp;
+}
+
+typedef struct {
+    MSIPACKAGE *package;
+    MSIFEATURE *feature;
+} _ilfs;
+
+static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
+{
+    ComponentList *cl;
+
+    cl = msi_alloc( sizeof (*cl) );
+    if ( !cl )
+        return ERROR_NOT_ENOUGH_MEMORY;
+    cl->component = comp;
+    list_add_tail( &feature->Components, &cl->entry );
+
+    return ERROR_SUCCESS;
+}
+
+static UINT iterate_component_check( MSIRECORD *row, LPVOID param )
+{
+    _ilfs* ilfs= (_ilfs*)param;
+    MSIPACKAGE *package = ilfs->package;
+    MSIFEATURE *feature = ilfs->feature;
+    MSICOMPONENT *comp;
+
+    comp = load_component( row );
+    if (!comp)
+        return ERROR_FUNCTION_FAILED;
+
+    list_add_tail( &package->components, &comp->entry );
+    add_feature_component( feature, comp );
+
+    TRACE("Loaded new component %p\n", comp);
+
+    return ERROR_SUCCESS;
+}
+
+static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
+{
+    _ilfs* ilfs= (_ilfs*)param;
+    LPCWSTR component;
+    DWORD rc;
+    MSICOMPONENT *comp;
+    MSIQUERY * view;
+    static const WCHAR Query[] = 
+        {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ', 
+         '`','C','o','m','p','o','n','e','n','t','`',' ',
+         'W','H','E','R','E',' ', 
+         '`','C','o','m','p','o','n','e','n','t','`',' ',
+         '=','\'','%','s','\'',0};
+
+    component = MSI_RecordGetString(row,1);
+
+    /* check to see if the component is already loaded */
+    comp = get_loaded_component( ilfs->package, component );
+    if (comp)
+    {
+        TRACE("Component %s already loaded\n", debugstr_w(component) );
+        add_feature_component( ilfs->feature, comp );
+        return ERROR_SUCCESS;
+    }
+
+    rc = MSI_OpenQuery(ilfs->package->db, &view, Query, component);
+    if (rc != ERROR_SUCCESS)
+        return ERROR_SUCCESS;
+
+    rc = MSI_IterateRecords(view, NULL, iterate_component_check, ilfs);
+    msiobj_release( &view->hdr );
+
+    return ERROR_SUCCESS;
+}
+
+static UINT load_feature(MSIRECORD * row, LPVOID param)
+{
+    MSIPACKAGE* package = (MSIPACKAGE*)param;
+    MSIFEATURE* feature;
+    static const WCHAR Query1[] = 
+        {'S','E','L','E','C','T',' ',
+         '`','C','o','m','p','o','n','e','n','t','_','`',
+         ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
+         'C','o','m','p','o','n','e','n','t','s','`',' ',
+         'W','H','E','R','E',' ',
+         '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
+    MSIQUERY * view;
+    UINT    rc;
+    _ilfs ilfs;
+
+    /* fill in the data */
+
+    feature = msi_alloc_zero( sizeof (MSIFEATURE) );
+    if (!feature)
+        return ERROR_NOT_ENOUGH_MEMORY;
+
+    list_init( &feature->Components );
+    
+    feature->Feature = msi_dup_record_field( row, 1 );
+
+    TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
+
+    feature->Feature_Parent = msi_dup_record_field( row, 2 );
+    feature->Title = msi_dup_record_field( row, 3 );
+    feature->Description = msi_dup_record_field( row, 4 );
+
+    if (!MSI_RecordIsNull(row,5))
+        feature->Display = MSI_RecordGetInteger(row,5);
+  
+    feature->Level= MSI_RecordGetInteger(row,6);
+    feature->Directory = msi_dup_record_field( row, 7 );
+    feature->Attributes = MSI_RecordGetInteger(row,8);
+
+    feature->Installed = INSTALLSTATE_ABSENT;
+    feature->Action = INSTALLSTATE_UNKNOWN;
+    feature->ActionRequest = INSTALLSTATE_UNKNOWN;
+
+    list_add_tail( &package->features, &feature->entry );
+
+    /* load feature components */
+
+    rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
+    if (rc != ERROR_SUCCESS)
+        return ERROR_SUCCESS;
+
+    ilfs.package = package;
+    ilfs.feature = feature;
+
+    MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
+    msiobj_release(&view->hdr);
+
+    return ERROR_SUCCESS;
+}
+
+static UINT load_file(MSIRECORD *row, LPVOID param)
+{
+    MSIPACKAGE* package = (MSIPACKAGE*)param;
+    LPCWSTR component;
+    MSIFILE *file;
+
+    /* fill in the data */
+
+    file = msi_alloc_zero( sizeof (MSIFILE) );
+    if (!file)
+        return ERROR_NOT_ENOUGH_MEMORY;
+    file->File = msi_dup_record_field( row, 1 );
+
+    component = MSI_RecordGetString( row, 2 );
+    file->Component = get_loaded_component( package, component );
+
+    if (!file->Component)
+        ERR("Unfound Component %s\n",debugstr_w(component));
+
+    file->FileName = msi_dup_record_field( row, 3 );
+    reduce_to_longfilename( file->FileName );
+
+    file->ShortName = msi_dup_record_field( row, 3 );
+    reduce_to_shortfilename( file->ShortName );
+    
+    file->FileSize = MSI_RecordGetInteger( row, 4 );
+    file->Version = msi_dup_record_field( row, 5 );
+    file->Language = msi_dup_record_field( row, 6 );
+    file->Attributes = MSI_RecordGetInteger( row, 7 );
+    file->Sequence = MSI_RecordGetInteger( row, 8 );
+
+    file->state = msifs_invalid;
+
+    TRACE("File Loaded (%s)\n",debugstr_w(file->File));  
+
+    list_add_tail( &package->files, &file->entry );
+    return ERROR_SUCCESS;
+}
+
+static UINT load_all_files(MSIPACKAGE *package)
+{
+    MSIQUERY * view;
+    UINT rc;
+    static const WCHAR Query[] =
+        {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
+         '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
+         '`','S','e','q','u','e','n','c','e','`', 0};
+
+    if (!package)
+        return ERROR_INVALID_HANDLE;
+
+    rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
+    if (rc != ERROR_SUCCESS)
+        return ERROR_SUCCESS;
+
+    rc = MSI_IterateRecords(view, NULL, load_file, package);
+    msiobj_release(&view->hdr);
+
+    return ERROR_SUCCESS;
+}
+
+
+/*
+ * I am not doing any of the costing functionality yet. 
+ * Mostly looking at doing the Component and Feature loading
+ *
+ * The native MSI does A LOT of modification to tables here. Mostly adding
+ * a lot of temporary columns to the Feature and Component tables. 
+ *
+ *    note: Native msi also tracks the short filename. But I am only going to
+ *          track the long ones.  Also looking at this directory table
+ *          it appears that the directory table does not get the parents
+ *          resolved base on property only based on their entries in the 
+ *          directory table.
+ */
+static UINT ACTION_CostInitialize(MSIPACKAGE *package)
+{
+    MSIQUERY * view;
+    UINT rc;
+    static const WCHAR Query_all[] =
+        {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
+         '`','F','e','a','t','u','r','e','`',0};
+    static const WCHAR szCosting[] =
+        {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
+    static const WCHAR szZero[] = { '0', 0 };
+
+    if ( 1 == msi_get_property_int( package, szCosting, 0 ) )
+        return ERROR_SUCCESS;
+    
+    MSI_SetPropertyW(package, szCosting, szZero);
+    MSI_SetPropertyW(package, cszRootDrive , c_colon);
+
+    rc = MSI_DatabaseOpenViewW(package->db,Query_all,&view);
+    if (rc != ERROR_SUCCESS)
+        return rc;
+
+    rc = MSI_IterateRecords(view, NULL, load_feature, package);
+    msiobj_release(&view->hdr);
+
+    load_all_files(package);
+
+    return ERROR_SUCCESS;
+}
+
+static UINT execute_script(MSIPACKAGE *package, UINT script )
+{
+    int i;
+    UINT rc = ERROR_SUCCESS;
+
+    TRACE("Executing Script %i\n",script);
+
+    for (i = 0; i < package->script->ActionCount[script]; i++)
+    {
+        LPWSTR action;
+        action = package->script->Actions[script][i];
+        ui_actionstart(package, action);
+        TRACE("Executing Action (%s)\n",debugstr_w(action));
+        rc = ACTION_PerformAction(package, action, TRUE);
+        msi_free(package->script->Actions[script][i]);
+        if (rc != ERROR_SUCCESS)
+            break;
+    }
+    msi_free(package->script->Actions[script]);
+
+    package->script->ActionCount[script] = 0;
+    package->script->Actions[script] = NULL;
+    return rc;
+}
+
+static UINT ACTION_FileCost(MSIPACKAGE *package)
+{
+    return ERROR_SUCCESS;
+}
+
+
+static MSIFOLDER *load_folder( MSIPACKAGE *package, LPCWSTR dir )
+{
+    static const WCHAR Query[] =
+        {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
+         '`','D','i','r','e','c', 't','o','r','y','`',' ',
+         'W','H','E','R','E',' ', '`', 'D','i','r','e','c','t', 'o','r','y','`',
+         ' ','=',' ','\'','%','s','\'',
+         0};
+    LPWSTR ptargetdir, targetdir, srcdir;
+    LPCWSTR parent;
+    LPWSTR shortname = NULL;
+    MSIRECORD * row = 0;
+    MSIFOLDER *folder;
+
+    TRACE("Looking for dir %s\n",debugstr_w(dir));
+
+    folder = get_loaded_folder( package, dir );
+    if (folder)
+        return folder;
+
+    TRACE("Working to load %s\n",debugstr_w(dir));
+
+    folder = msi_alloc_zero( sizeof (MSIFOLDER) );
+    if (!folder)
+        return NULL;
+
+    folder->Directory = strdupW(dir);
+
+    row = MSI_QueryGetRecord(package->db, Query, dir);
+    if (!row)
+        return NULL;
+
+    ptargetdir = targetdir = msi_dup_record_field(row,3);
+
+    /* split src and target dir */
+    if (strchrW(targetdir,':'))
+    {
+        srcdir=strchrW(targetdir,':');
+        *srcdir=0;
+        srcdir ++;
+    }
+    else
+        srcdir=NULL;
+
+    /* for now only pick long filename versions */
+    if (strchrW(targetdir,'|'))
+    {
+        shortname = targetdir;
+        targetdir = strchrW(targetdir,'|'); 
+        *targetdir = 0;
+        targetdir ++;
+    }
+    /* for the sourcedir pick the short filename */
+    if (srcdir && strchrW(srcdir,'|'))
+    {
+        LPWSTR p = strchrW(srcdir,'|'); 
+        *p = 0;
+    }
+
+    /* now check for root dirs */
+    if (targetdir[0] == '.' && targetdir[1] == 0)
+        targetdir = NULL;
+        
+    if (targetdir)
+    {
+        TRACE("   TargetDefault = %s\n",debugstr_w(targetdir));
+        msi_free( folder->TargetDefault);
+        folder->TargetDefault = strdupW(targetdir);
+    }
+
+    if (srcdir)
+        folder->SourceDefault = strdupW(srcdir);
+    else if (shortname)
+        folder->SourceDefault = strdupW(shortname);
+    else if (targetdir)
+        folder->SourceDefault = strdupW(targetdir);
+    msi_free(ptargetdir);
+        TRACE("   SourceDefault = %s\n", debugstr_w( folder->SourceDefault ));
+
+    parent = MSI_RecordGetString(row,2);
+    if (parent) 
+    {
+        folder->Parent = load_folder( package, parent );
+        if ( folder->Parent )
+            TRACE("loaded parent %p %s\n", folder->Parent,
+                  debugstr_w(folder->Parent->Directory));
+        else
+            ERR("failed to load parent folder %s\n", debugstr_w(parent));
+    }
+
+    folder->Property = msi_dup_property( package, dir );
+
+    msiobj_release(&row->hdr);
+
+    list_add_tail( &package->folders, &folder->entry );
+
+    TRACE("%s returning %p\n",debugstr_w(dir),folder);
+
+    return folder;
+}
+
+/* scan for and update current install states */
+static void ACTION_UpdateInstallStates(MSIPACKAGE *package)
+{
+    MSICOMPONENT *comp;
+    MSIFEATURE *feature;
+
+    LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
+    {
+        INSTALLSTATE res;
+        res = MsiGetComponentPathW( package->ProductCode, 
+                                    comp->ComponentId, NULL, NULL);
+        if (res < 0)
+            res = INSTALLSTATE_ABSENT;
+        comp->Installed = res;
+    }
+
+    LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
+    {
+        ComponentList *cl;
+        INSTALLSTATE res = -10;
+
+        LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
+        {
+            comp= cl->component;
+
+            if (res == -10)
+                res = comp->Installed;
+            else
+            {
+                if (res == comp->Installed)
+                    continue;
+
+                if (res != comp->Installed)
+                        res = INSTALLSTATE_INCOMPLETE;
+            }
+        }
+        feature->Installed = res;
+    }
+}
+
+static BOOL process_state_property (MSIPACKAGE* package, LPCWSTR property, 
+                                    INSTALLSTATE state)
+{
+    static const WCHAR all[]={'A','L','L',0};
+    LPWSTR override;
+    MSIFEATURE *feature;
+
+    override = msi_dup_property( package, property );
+    if (!override)
+        return FALSE;
+    LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
+    {
+        if (strcmpiW(override,all)==0)
+        {
+            feature->ActionRequest= state;
+            feature->Action = state;
+        }
+        else
+        {
+            LPWSTR ptr = override;
+            LPWSTR ptr2 = strchrW(override,',');
+
+            while (ptr)
+            {
+                if ((ptr2 && strncmpW(ptr,feature->Feature, ptr2-ptr)==0)
+                    || (!ptr2 && strcmpW(ptr,feature->Feature)==0))
+                {
+                    feature->ActionRequest= state;
+                    feature->Action = state;
+                    break;
+                }
+                if (ptr2)
+                {
+                    ptr=ptr2+1;
+                    ptr2 = strchrW(ptr,',');
+                }
+                else
+                    break;
+            }
+        }
+    } 
+    msi_free(override);
+
+    return TRUE;
+}
+
+static UINT SetFeatureStates(MSIPACKAGE *package)
+{
+    int install_level;
+    static const WCHAR szlevel[] =
+        {'I','N','S','T','A','L','L','L','E','V','E','L',0};
+    static const WCHAR szAddLocal[] =
+        {'A','D','D','L','O','C','A','L',0};
+    static const WCHAR szRemove[] =
+        {'R','E','M','O','V','E',0};
+    static const WCHAR szReinstall[] =
+        {'R','E','I','N','S','T','A','L','L',0};
+    BOOL override = FALSE;
+    MSICOMPONENT* component;
+    MSIFEATURE *feature;
+
+
+    /* I do not know if this is where it should happen.. but */
+
+    TRACE("Checking Install Level\n");
+
+    install_level = msi_get_property_int( package, szlevel, 1 );
+
+    /* ok hereis the _real_ rub
+     * all these activation/deactivation things happen in order and things
+     * later on the list override things earlier on the list.
+     * 1) INSTALLLEVEL processing
+     * 2) ADDLOCAL
+     * 3) REMOVE
+     * 4) ADDSOURCE
+     * 5) ADDDEFAULT
+     * 6) REINSTALL
+     * 7) COMPADDLOCAL
+     * 8) COMPADDSOURCE
+     * 9) FILEADDLOCAL
+     * 10) FILEADDSOURCE
+     * 11) FILEADDDEFAULT
+     * I have confirmed that if ADDLOCAL is stated then the INSTALLLEVEL is
+     * ignored for all the features. seems strange, especially since it is not
+     * documented anywhere, but it is how it works. 
+     *
+     * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
+     * REMOVE are the big ones, since we don't handle administrative installs
+     * yet anyway.
+     */
+    override |= process_state_property(package,szAddLocal,INSTALLSTATE_LOCAL);
+    override |= process_state_property(package,szRemove,INSTALLSTATE_ABSENT);
+    override |= process_state_property(package,szReinstall,INSTALLSTATE_LOCAL);
+
+    if (!override)
+    {
+        LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
+        {
+            BOOL feature_state = ((feature->Level > 0) &&
+                             (feature->Level <= install_level));
+
+            if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
+            {
+                if (feature->Attributes & msidbFeatureAttributesFavorSource)
+                {
+                    feature->ActionRequest = INSTALLSTATE_SOURCE;
+                    feature->Action = INSTALLSTATE_SOURCE;
+                }
+                else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
+                {
+                    feature->ActionRequest = INSTALLSTATE_ADVERTISED;
+                    feature->Action = INSTALLSTATE_ADVERTISED;
+                }
+                else
+                {
+                    feature->ActionRequest = INSTALLSTATE_LOCAL;
+                    feature->Action = INSTALLSTATE_LOCAL;
+                }
+            }
+        }
+    }
+    else
+    {
+        /* set the Preselected Property */
+        static const WCHAR szPreselected[] = {'P','r','e','s','e','l','e','c','t','e','d',0};
+        static const WCHAR szOne[] = { '1', 0 };
+
+        MSI_SetPropertyW(package,szPreselected,szOne);
+    }
+
+    /*
+     * now we want to enable or disable components base on feature 
+    */
+
+    LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
+    {
+        ComponentList *cl;
+
+        TRACE("Examining Feature %s (Installed %i, Action %i, Request %i)\n",
+            debugstr_w(feature->Feature), feature->Installed, feature->Action,
+            feature->ActionRequest);
+
+        LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
+        {
+            component = cl->component;
+
+            if (!component->Enabled)
+            {
+                component->Action = INSTALLSTATE_UNKNOWN;
+                component->ActionRequest = INSTALLSTATE_UNKNOWN;
+            }
+            else
+            {
+                if (feature->Action == INSTALLSTATE_LOCAL)
+                {
+                    component->Action = INSTALLSTATE_LOCAL;
+                    component->ActionRequest = INSTALLSTATE_LOCAL;
+                }
+                else if (feature->ActionRequest == INSTALLSTATE_SOURCE)
+                {
+                    if ((component->Action == INSTALLSTATE_UNKNOWN) ||
+                        (component->Action == INSTALLSTATE_ABSENT) ||
+                        (component->Action == INSTALLSTATE_ADVERTISED))
+                           
+                    {
+                        component->Action = INSTALLSTATE_SOURCE;
+                        component->ActionRequest = INSTALLSTATE_SOURCE;
+                    }
+                }
+                else if (feature->ActionRequest == INSTALLSTATE_ADVERTISED)
+                {
+                    if ((component->Action == INSTALLSTATE_UNKNOWN) ||
+                        (component->Action == INSTALLSTATE_ABSENT))
+                           
+                    {
+                        component->Action = INSTALLSTATE_ADVERTISED;
+                        component->ActionRequest = INSTALLSTATE_ADVERTISED;
+                    }
+                }
+                else if (feature->ActionRequest == INSTALLSTATE_ABSENT)
+                {
+                    if (component->Action == INSTALLSTATE_UNKNOWN)
+                    {
+                        component->Action = INSTALLSTATE_ABSENT;
+                        component->ActionRequest = INSTALLSTATE_ABSENT;
+                    }
+                }
+            }
+        }
+    } 
+
+    LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
+    {
+        TRACE("Result: Component %s (Installed %i, Action %i, Request %i)\n",
+            debugstr_w(component->Component), component->Installed, 
+            component->Action, component->ActionRequest);
+    }
+
+
+    return ERROR_SUCCESS;
+}
+
+static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
+{
+    MSIPACKAGE *package = (MSIPACKAGE*)param;
+    LPCWSTR name;
+    LPWSTR path;
+
+    name = MSI_RecordGetString(row,1);
+
+    /* This helper function now does ALL the work */
+    TRACE("Dir %s ...\n",debugstr_w(name));
+    load_folder(package,name);
+    path = resolve_folder(package,name,FALSE,TRUE,NULL);
+    TRACE("resolves to %s\n",debugstr_w(path));
+    msi_free(path);
+
+    return ERROR_SUCCESS;
+}
+
+static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
+{
+    MSIPACKAGE *package = (MSIPACKAGE*)param;
+    LPCWSTR name;
+    MSIFEATURE *feature;
+
+    name = MSI_RecordGetString( row, 1 );
+
+    feature = get_loaded_feature( package, name );
+    if (!feature)
+        ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
+    else
+    {
+        LPCWSTR Condition;
+        Condition = MSI_RecordGetString(row,3);
+
+        if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
+        {
+            int level = MSI_RecordGetInteger(row,2);
+            TRACE("Reseting feature %s to level %i\n", debugstr_w(name), level);
+            feature->Level = level;
+        }
+    }
+    return ERROR_SUCCESS;
+}
+
+
+/* 
+ * A lot is done in this function aside from just the costing.
+ * The costing needs to be implemented at some point but for now I am going
+ * to focus on the directory building
+ *
+ */
+static UINT ACTION_CostFinalize(MSIPACKAGE *package)
+{
+    static const WCHAR ExecSeqQuery[] =
+        {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
+         '`','D','i','r','e','c','t','o','r','y','`',0};
+    static const WCHAR ConditionQuery[] =
+        {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
+         '`','C','o','n','d','i','t','i','o','n','`',0};
+    static const WCHAR szCosting[] =
+        {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
+    static const WCHAR szlevel[] =
+        {'I','N','S','T','A','L','L','L','E','V','E','L',0};
+    static const WCHAR szOne[] = { '1', 0 };
+    MSICOMPONENT *comp;
+    MSIFILE *file;
+    UINT rc;
+    MSIQUERY * view;
+    LPWSTR level;
+
+    if ( 1 == msi_get_property_int( package, szCosting, 0 ) )
+        return ERROR_SUCCESS;
+    
+    TRACE("Building Directory properties\n");
+
+    rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
+    if (rc == ERROR_SUCCESS)
+    {
+        rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
+                        package);
+        msiobj_release(&view->hdr);
+    }
+
+    TRACE("File calculations\n");
+
+    LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
+    {
+        MSICOMPONENT* comp = file->Component;
+        LPWSTR p;
+
+        if (!comp)
+            continue;
+
+        /* calculate target */
+        p = resolve_folder(package, comp->Directory, FALSE, FALSE, NULL);
+
+        msi_free(file->TargetPath);
+
+        TRACE("file %s is named %s\n",
+               debugstr_w(file->File),debugstr_w(file->FileName));       
+
+        file->TargetPath = build_directory_name(2, p, file->FileName);
+
+        msi_free(p);
+
+        TRACE("file %s resolves to %s\n",
+               debugstr_w(file->File),debugstr_w(file->TargetPath));       
+
+        if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
+        {
+            file->state = msifs_missing;
+            comp->Cost += file->FileSize;
+            continue;
+        }
+
+        if (file->Version)
+        {
+            DWORD handle;
+            DWORD versize;
+            UINT sz;
+            LPVOID version;
+            static WCHAR name[] = {'\\',0};
+            static const WCHAR name_fmt[] = 
+                {'%','u','.','%','u','.','%','u','.','%','u',0};
+            WCHAR filever[0x100];
+            VS_FIXEDFILEINFO *lpVer;
+
+            TRACE("Version comparison..\n");
+            versize = GetFileVersionInfoSizeW(file->TargetPath,&handle);
+            version = msi_alloc(versize);
+            GetFileVersionInfoW(file->TargetPath, 0, versize, version);
+
+            VerQueryValueW(version, (LPWSTR)name, (LPVOID*)&lpVer, &sz);
+
+            sprintfW(filever,name_fmt,
+                HIWORD(lpVer->dwFileVersionMS),
+                LOWORD(lpVer->dwFileVersionMS),
+                HIWORD(lpVer->dwFileVersionLS),
+                LOWORD(lpVer->dwFileVersionLS));
+
+            TRACE("new %s old %s\n", debugstr_w(file->Version),
+                  debugstr_w(filever));
+            if (strcmpiW(filever,file->Version)<0)
+            {
+                file->state = msifs_overwrite;
+                /* FIXME: cost should be diff in size */
+                comp->Cost += file->FileSize;
+            }
+            else
+                file->state = msifs_present;
+            msi_free(version);
+        }
+        else
+            file->state = msifs_present;
+    }
+
+    TRACE("Evaluating Condition Table\n");
+
+    rc = MSI_DatabaseOpenViewW(package->db, ConditionQuery, &view);
+    if (rc == ERROR_SUCCESS)
+    {
+        rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeConditions,
+                    package);
+        msiobj_release(&view->hdr);
+    }
+
+    TRACE("Enabling or Disabling Components\n");
+    LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
+    {
+        if (comp->Condition)
+        {
+            if (MSI_EvaluateConditionW(package,
+                comp->Condition) == MSICONDITION_FALSE)
+            {
+                TRACE("Disabling component %s\n", debugstr_w(comp->Component));
+                comp->Enabled = FALSE;
+            }
+        }
+    }
+
+    MSI_SetPropertyW(package,szCosting,szOne);
+    /* set default run level if not set */
+    level = msi_dup_property( package, szlevel );
+    if (!level)
+        MSI_SetPropertyW(package,szlevel, szOne);
+    msi_free(level);
+
+    ACTION_UpdateInstallStates(package);
+
+    return SetFeatureStates(package);
+}
+
+/* OK this value is "interpreted" and then formatted based on the 
+   first few characters */
+static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type, 
+                         DWORD *size)
+{
+    LPSTR data = NULL;
+    if (value[0]=='#' && value[1]!='#' && value[1]!='%')
+    {
+        if (value[1]=='x')
+        {
+            LPWSTR ptr;
+            CHAR byte[5];
+            LPWSTR deformated = NULL;
+            int count;
+
+            deformat_string(package, &value[2], &deformated);
+
+            /* binary value type */
+            ptr = deformated;
+            *type = REG_BINARY;
+            if (strlenW(ptr)%2)
+                *size = (strlenW(ptr)/2)+1;
+            else
+                *size = strlenW(ptr)/2;
+
+            data = msi_alloc(*size);
+
+            byte[0] = '0'; 
+            byte[1] = 'x'; 
+            byte[4] = 0; 
+            count = 0;
+            /* if uneven pad with a zero in front */
+            if (strlenW(ptr)%2)
+            {
+                byte[2]= '0';
+                byte[3]= *ptr;
+                ptr++;
+                data[count] = (BYTE)strtol(byte,NULL,0);
+                count ++;
+                TRACE("Uneven byte count\n");
+            }
+            while (*ptr)
+            {
+                byte[2]= *ptr;
+                ptr++;
+                byte[3]= *ptr;
+                ptr++;
+                data[count] = (BYTE)strtol(byte,NULL,0);
+                count ++;
+            }
+            msi_free(deformated);
+
+            TRACE("Data %li bytes(%i)\n",*size,count);
+        }
+        else
+        {
+            LPWSTR deformated;
+            LPWSTR p;
+            DWORD d = 0;
+            deformat_string(package, &value[1], &deformated);
+
+            *type=REG_DWORD; 
+            *size = sizeof(DWORD);
+            data = msi_alloc(*size);
+            p = deformated;
+            if (*p == '-')
+                p++;
+            while (*p)
+            {
+                if ( (*p < '0') || (*p > '9') )
+                    break;
+                d *= 10;
+                d += (*p - '0');
+                p++;
+            }
+            if (deformated[0] == '-')
+                d = -d;
+            *(LPDWORD)data = d;
+            TRACE("DWORD %li\n",*(LPDWORD)data);
+
+            msi_free(deformated);
+        }
+    }
+    else
+    {
+        static const WCHAR szMulti[] = {'[','~',']',0};
+        LPCWSTR ptr;
+        *type=REG_SZ;
+
+        if (value[0]=='#')
+        {
+            if (value[1]=='%')
+            {
+                ptr = &value[2];
+                *type=REG_EXPAND_SZ;
+            }
+            else
+                ptr = &value[1];
+         }
+         else
+            ptr=value;
+
+        if (strstrW(value,szMulti))
+            *type = REG_MULTI_SZ;
+
+        *size = deformat_string(package, ptr,(LPWSTR*)&data);
+    }
+    return data;
+}
+
+static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
+{
+    MSIPACKAGE *package = (MSIPACKAGE*)param;
+    static const WCHAR szHCR[] = 
+        {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
+         'R','O','O','T','\\',0};
+    static const WCHAR szHCU[] =
+        {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
+         'U','S','E','R','\\',0};
+    static const WCHAR szHLM[] =
+        {'H','K','E','Y','_','L','O','C','A','L','_',
+         'M','A','C','H','I','N','E','\\',0};
+    static const WCHAR szHU[] =
+        {'H','K','E','Y','_','U','S','E','R','S','\\',0};
+
+    LPSTR value_data = NULL;
+    HKEY  root_key, hkey;
+    DWORD type,size;
+    LPWSTR  deformated;
+    LPCWSTR szRoot, component, name, key, value;
+    MSICOMPONENT *comp;
+    MSIRECORD * uirow;
+    LPWSTR uikey;
+    INT   root;
+    BOOL check_first = FALSE;
+    UINT rc;
+
+    ui_progress(package,2,0,0,0);
+
+    value = NULL;
+    key = NULL;
+    uikey = NULL;
+    name = NULL;
+
+    component = MSI_RecordGetString(row, 6);
+    comp = get_loaded_component(package,component);
+    if (!comp)
+        return ERROR_SUCCESS;
+
+    if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
+    {
+        TRACE("Skipping write due to disabled component %s\n",
+                        debugstr_w(component));
+
+        comp->Action = comp->Installed;
+
+        return ERROR_SUCCESS;
+    }
+
+    comp->Action = INSTALLSTATE_LOCAL;
+
+    name = MSI_RecordGetString(row, 4);
+    if( MSI_RecordIsNull(row,5) && name )
+    {
+        /* null values can have special meanings */
+        if (name[0]=='-' && name[1] == 0)
+                return ERROR_SUCCESS;
+        else if ((name[0]=='+' && name[1] == 0) || 
+                 (name[0] == '*' && name[1] == 0))
+                name = NULL;
+        check_first = TRUE;
+    }
+
+    root = MSI_RecordGetInteger(row,2);
+    key = MSI_RecordGetString(row, 3);
+
+    /* get the root key */
+    switch (root)
+    {
+        case -1: 
+            {
+                static const WCHAR szALLUSER[] = {'A','L','L','U','S','E','R','S',0};
+                LPWSTR all_users = msi_dup_property( package, szALLUSER );
+                if (all_users && all_users[0] == '1')
+                {
+                    root_key = HKEY_LOCAL_MACHINE;
+                    szRoot = szHLM;
+                }
+                else
+                {
+                    root_key = HKEY_CURRENT_USER;
+                    szRoot = szHCU;
+                }
+                msi_free(all_users);
+            }
+                 break;
+        case 0:  root_key = HKEY_CLASSES_ROOT; 
+                 szRoot = szHCR;
+                 break;
+        case 1:  root_key = HKEY_CURRENT_USER;
+                 szRoot = szHCU;
+                 break;
+        case 2:  root_key = HKEY_LOCAL_MACHINE;
+                 szRoot = szHLM;
+                 break;
+        case 3:  root_key = HKEY_USERS; 
+                 szRoot = szHU;
+                 break;
+        default:
+                 ERR("Unknown root %i\n",root);
+                 root_key=NULL;
+                 szRoot = NULL;
+                 break;
+    }
+    if (!root_key)
+        return ERROR_SUCCESS;
+
+    deformat_string(package, key , &deformated);
+    size = strlenW(deformated) + strlenW(szRoot) + 1;
+    uikey = msi_alloc(size*sizeof(WCHAR));
+    strcpyW(uikey,szRoot);
+    strcatW(uikey,deformated);
+
+    if (RegCreateKeyW( root_key, deformated, &hkey))
+    {
+        ERR("Could not create key %s\n",debugstr_w(deformated));
+        msi_free(deformated);
+        msi_free(uikey);
+        return ERROR_SUCCESS;
+    }
+    msi_free(deformated);
+
+    value = MSI_RecordGetString(row,5);
+    if (value)
+        value_data = parse_value(package, value, &type, &size); 
+    else
+    {
+        static const WCHAR szEmpty[] = {0};
+        value_data = (LPSTR)strdupW(szEmpty);
+        size = 0;
+        type = REG_SZ;
+    }
+
+    deformat_string(package, name, &deformated);
+
+    /* get the double nulls to terminate SZ_MULTI */
+    if (type == REG_MULTI_SZ)
+        size +=sizeof(WCHAR);
+
+    if (!check_first)
+    {
+        TRACE("Setting value %s of %s\n",debugstr_w(deformated),
+                        debugstr_w(uikey));
+        RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
+    }
+    else
+    {
+        DWORD sz = 0;
+        rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
+        if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
+        {
+            TRACE("value %s of %s checked already exists\n",
+                            debugstr_w(deformated), debugstr_w(uikey));
+        }
+        else
+        {
+            TRACE("Checked and setting value %s of %s\n",
+                            debugstr_w(deformated), debugstr_w(uikey));
+            if (deformated || size)
+                RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
+        }
+    }
+    RegCloseKey(hkey);
+
+    uirow = MSI_CreateRecord(3);
+    MSI_RecordSetStringW(uirow,2,deformated);
+    MSI_RecordSetStringW(uirow,1,uikey);
+
+    if (type == REG_SZ)
+        MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
+    else
+        MSI_RecordSetStringW(uirow,3,value);
+
+    ui_actiondata(package,szWriteRegistryValues,uirow);
+    msiobj_release( &uirow->hdr );
+
+    msi_free(value_data);
+    msi_free(deformated);
+    msi_free(uikey);
+
+    return ERROR_SUCCESS;
+}
+
+static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
+{
+    UINT rc;
+    MSIQUERY * view;
+    static const WCHAR ExecSeqQuery[] =
+        {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
+         '`','R','e','g','i','s','t','r','y','`',0 };
+
+    if (!package)
+        return ERROR_INVALID_HANDLE;
+
+    rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
+    if (rc != ERROR_SUCCESS)
+        return ERROR_SUCCESS;
+
+    /* increment progress bar each time action data is sent */
+    ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
+
+    rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
+
+    msiobj_release(&view->hdr);
+    return rc;
+}
+
+static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
+{
+    package->script->CurrentlyScripting = TRUE;
+
+    return ERROR_SUCCESS;
+}
+
+
+static UINT ACTION_InstallValidate(MSIPACKAGE *package)
+{
+    MSICOMPONENT *comp;
+    DWORD progress = 0;
+    DWORD total = 0;
+    static const WCHAR q1[]=
+        {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
+         '`','R','e','g','i','s','t','r','y','`',0};
+    UINT rc;
+    MSIQUERY * view;
+    MSIFEATURE *feature;
+    MSIFILE *file;
+
+    TRACE("InstallValidate\n");
+
+    rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
+    if (rc == ERROR_SUCCESS)
+    {
+        MSI_IterateRecords( view, &progress, NULL, package );
+        msiobj_release( &view->hdr );
+        total += progress * REG_PROGRESS_VALUE;
+    }
+
+    LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
+        total += COMPONENT_PROGRESS_VALUE;
+
+    LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
+        total += file->FileSize;
+
+    ui_progress(package,0,total,0,0);
+
+    LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
+    {
+        TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
+            debugstr_w(feature->Feature), feature->Installed, feature->Action,
+            feature->ActionRequest);
+    }
+    
+    return ERROR_SUCCESS;
+}
+
+static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
+{
+    MSIPACKAGE* package = (MSIPACKAGE*)param;
+    LPCWSTR cond = NULL; 
+    LPCWSTR message = NULL;
+    static const WCHAR title[]=
+        {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
+
+    cond = MSI_RecordGetString(row,1);
+
+    if (MSI_EvaluateConditionW(package,cond) != MSICONDITION_TRUE)
+    {
+        LPWSTR deformated;
+        message = MSI_RecordGetString(row,2);
+        deformat_string(package,message,&deformated); 
+        MessageBoxW(NULL,deformated,title,MB_OK);
+        msi_free(deformated);
+        return ERROR_FUNCTION_FAILED;
+    }
+
+    return ERROR_SUCCESS;
+}
+
+static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
+{
+    UINT rc;
+    MSIQUERY * view = NULL;
+    static const WCHAR ExecSeqQuery[] =
+        {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
+         '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
+
+    TRACE("Checking launch conditions\n");
+
+    rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
+    if (rc != ERROR_SUCCESS)
+        return ERROR_SUCCESS;
+
+    rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
+    msiobj_release(&view->hdr);
+
+    return rc;
+}
+
+static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
+{
+
+    if (!cmp->KeyPath)
+        return resolve_folder(package,cmp->Directory,FALSE,FALSE,NULL);
+
+    if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
+    {
+        MSIRECORD * row = 0;
+        UINT root,len;
+        LPWSTR deformated,buffer,deformated_name;
+        LPCWSTR key,name;
+        static const WCHAR ExecSeqQuery[] =
+            {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
+             '`','R','e','g','i','s','t','r','y','`',' ',
+             'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
+             ' ','=',' ' ,'\'','%','s','\'',0 };
+        static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
+        static const WCHAR fmt2[]=
+            {'%','0','2','i',':','\\','%','s','\\','%','s',0};
+
+        row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
+        if (!row)
+            return NULL;
+
+        root = MSI_RecordGetInteger(row,2);
+        key = MSI_RecordGetString(row, 3);
+        name = MSI_RecordGetString(row, 4);
+        deformat_string(package, key , &deformated);
+        deformat_string(package, name, &deformated_name);
+
+        len = strlenW(deformated) + 6;
+        if (deformated_name)
+            len+=strlenW(deformated_name);
+
+        buffer = msi_alloc( len *sizeof(WCHAR));
+
+        if (deformated_name)
+            sprintfW(buffer,fmt2,root,deformated,deformated_name);
+        else
+            sprintfW(buffer,fmt,root,deformated);
+
+        msi_free(deformated);
+        msi_free(deformated_name);
+        msiobj_release(&row->hdr);
+
+        return buffer;
+    }
+    else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
+    {
+        FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
+        return NULL;
+    }
+    else
+    {
+        MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
+
+        if (file)
+            return strdupW( file->TargetPath );
+    }
+    return NULL;
+}
+
+static HKEY openSharedDLLsKey(void)
+{
+    HKEY hkey=0;
+    static const WCHAR path[] =
+        {'S','o','f','t','w','a','r','e','\\',
+         'M','i','c','r','o','s','o','f','t','\\',
+         'W','i','n','d','o','w','s','\\',
+         'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
+         'S','h','a','r','e','d','D','L','L','s',0};
+
+    RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
+    return hkey;
+}
+
+static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
+{
+    HKEY hkey;
+    DWORD count=0;
+    DWORD type;
+    DWORD sz = sizeof(count);
+    DWORD rc;
+    
+    hkey = openSharedDLLsKey();
+    rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
+    if (rc != ERROR_SUCCESS)
+        count = 0;
+    RegCloseKey(hkey);
+    return count;
+}
+
+static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
+{
+    HKEY hkey;
+
+    hkey = openSharedDLLsKey();
+    if (count > 0)
+        msi_reg_set_val_dword( hkey, path, count );
+    else
+        RegDeleteValueW(hkey,path);
+    RegCloseKey(hkey);
+    return count;
+}
+
+/*
+ * Return TRUE if the count should be written out and FALSE if not
+ */
+static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
+{
+    MSIFEATURE *feature;
+    INT count = 0;
+    BOOL write = FALSE;
+
+    /* only refcount DLLs */
+    if (comp->KeyPath == NULL || 
+        comp->Attributes & msidbComponentAttributesRegistryKeyPath || 
+        comp->Attributes & msidbComponentAttributesODBCDataSource)
+        write = FALSE;
+    else
+    {
+        count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
+        write = (count > 0);
+
+        if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
+            write = TRUE;
+    }
+
+    /* increment counts */
+    LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
+    {
+        ComponentList *cl;
+
+        if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ))
+            continue;
+
+        LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
+        {
+            if ( cl->component == comp )
+                count++;
+        }
+    }
+
+    /* decrement counts */
+    LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
+    {
+        ComponentList *cl;
+
+        if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ABSENT ))
+            continue;
+
+        LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
+        {
+            if ( cl->component == comp )
+                count--;
+        }
+    }
+
+    /* ref count all the files in the component */
+    if (write)
+    {
+        MSIFILE *file;
+
+        LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
+        {
+            if (file->Component == comp)
+                ACTION_WriteSharedDLLsCount( file->TargetPath, count );
+        }
+    }
+    
+    /* add a count for permenent */
+    if (comp->Attributes & msidbComponentAttributesPermanent)
+        count ++;
+    
+    comp->RefCount = count;
+
+    if (write)
+        ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
+}
+
+/*
+ * Ok further analysis makes me think that this work is
+ * actually done in the PublishComponents and PublishFeatures
+ * step, and not here.  It appears like the keypath and all that is
+ * resolved in this step, however actually written in the Publish steps.
+ * But we will leave it here for now because it is unclear
+ */
+static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
+{
+    WCHAR squished_pc[GUID_SIZE];
+    WCHAR squished_cc[GUID_SIZE];
+    UINT rc;
+    MSICOMPONENT *comp;
+    HKEY hkey=0,hkey2=0;
+
+    /* writes the Component and Features values to the registry */
+
+    rc = MSIREG_OpenComponents(&hkey);
+    if (rc != ERROR_SUCCESS)
+        goto end;
+      
+    squash_guid(package->ProductCode,squished_pc);
+    ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
+
+    LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
+    {
+        ui_progress(package,2,0,0,0);
+        if (comp->ComponentId)
+        {
+            MSIRECORD * uirow;
+
+            squash_guid(comp->ComponentId,squished_cc);
+           
+            msi_free(comp->FullKeypath);
+            comp->FullKeypath = resolve_keypath( package, comp );
+
+            /* do the refcounting */
+            ACTION_RefCountComponent( package, comp );
+
+            TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n", 
+                            debugstr_w(comp->Component),
+                            debugstr_w(squished_cc),
+                            debugstr_w(comp->FullKeypath), 
+                            comp->RefCount);
+            /*
+            * Write the keypath out if the component is to be registered
+            * and delete the key if the component is to be deregistered
+            */
+            if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
+            {
+                rc = RegCreateKeyW(hkey,squished_cc,&hkey2);
+                if (rc != ERROR_SUCCESS)
+                    continue;
+
+                if (comp->FullKeypath)
+                {
+                    msi_reg_set_val_str( hkey2, squished_pc, comp->FullKeypath );
+
+                    if (comp->Attributes & msidbComponentAttributesPermanent)
+                    {
+                        static const WCHAR szPermKey[] =
+                            { '0','0','0','0','0','0','0','0','0','0','0','0',
+                              '0','0','0','0','0','0','0','0','0','0','0','0',
+                              '0','0','0','0','0','0','0','0',0};
+
+                        msi_reg_set_val_str( hkey2, szPermKey, comp->FullKeypath );
+                    }
+                    
+                    RegCloseKey(hkey2);
+        
+                    /* UI stuff */
+                    uirow = MSI_CreateRecord(3);
+                    MSI_RecordSetStringW(uirow,1,package->ProductCode);
+                    MSI_RecordSetStringW(uirow,2,comp->ComponentId);
+                    MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
+                    ui_actiondata(package,szProcessComponents,uirow);
+                    msiobj_release( &uirow->hdr );
+               }
+            }
+            else if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ABSENT))
+            {
+                DWORD res;
+
+                rc = RegOpenKeyW(hkey,squished_cc,&hkey2);
+                if (rc != ERROR_SUCCESS)
+                    continue;
+
+                RegDeleteValueW(hkey2,squished_pc);
+
+                /* if the key is empty delete it */
+                res = RegEnumKeyExW(hkey2,0,NULL,0,0,NULL,0,NULL);
+                RegCloseKey(hkey2);
+                if (res == ERROR_NO_MORE_ITEMS)
+                    RegDeleteKeyW(hkey,squished_cc);
+        
+                /* UI stuff */
+                uirow = MSI_CreateRecord(2);
+                MSI_RecordSetStringW(uirow,1,package->ProductCode);
+                MSI_RecordSetStringW(uirow,2,comp->ComponentId);
+                ui_actiondata(package,szProcessComponents,uirow);
+                msiobj_release( &uirow->hdr );
+            }
+        }
+    } 
+end:
+    RegCloseKey(hkey);
+    return rc;
+}
+
+typedef struct {
+    CLSID       clsid;
+    LPWSTR      source;
+
+    LPWSTR      path;
+    ITypeLib    *ptLib;
+} typelib_struct;
+
+static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType, 
+                                       LPWSTR lpszName, LONG_PTR lParam)
+{
+    TLIBATTR *attr;
+    typelib_struct *tl_struct = (typelib_struct*) lParam;
+    static const WCHAR fmt[] = {'%','s','\\','%','i',0};
+    int sz; 
+    HRESULT res;
+
+    if (!IS_INTRESOURCE(lpszName))
+    {
+        ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
+        return TRUE;
+    }
+
+    sz = strlenW(tl_struct->source)+4;
+    sz *= sizeof(WCHAR);
+
+    if ((INT)lpszName == 1)
+        tl_struct->path = strdupW(tl_struct->source);
+    else
+    {
+        tl_struct->path = msi_alloc(sz);
+        sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
+    }
+
+    TRACE("trying %s\n", debugstr_w(tl_struct->path));
+    res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
+    if (!SUCCEEDED(res))
+    {
+        msi_free(tl_struct->path);
+        tl_struct->path = NULL;
+
+        return TRUE;
+    }
+
+    ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
+    if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
+    {
+        ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
+        return FALSE;
+    }
+
+    msi_free(tl_struct->path);
+    tl_struct->path = NULL;
+
+    ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
+    ITypeLib_Release(tl_struct->ptLib);
+
+    return TRUE;
+}
+
+static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
+{
+    MSIPACKAGE* package = (MSIPACKAGE*)param;
+    LPCWSTR component;
+    MSICOMPONENT *comp;
+    MSIFILE *file;
+    typelib_struct tl_struct;
+    HMODULE module;
+    static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0};
+
+    component = MSI_RecordGetString(row,3);
+    comp = get_loaded_component(package,component);
+    if (!comp)
+        return ERROR_SUCCESS;
+
+    if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
+    {
+        TRACE("Skipping typelib reg due to disabled component\n");
+
+        comp->Action = comp->Installed;
+
+        return ERROR_SUCCESS;
+    }
+
+    comp->Action = INSTALLSTATE_LOCAL;
+
+    file = get_loaded_file( package, comp->KeyPath ); 
+    if (!file)
+        return ERROR_SUCCESS;
+
+    module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
+    if (module)
+    {
+        LPCWSTR guid;
+        guid = MSI_RecordGetString(row,1);
+        CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
+        tl_struct.source = strdupW( file->TargetPath );
+        tl_struct.path = NULL;
+
+        EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
+                        (LONG_PTR)&tl_struct);
+
+        if (tl_struct.path)
+        {
+            LPWSTR help = NULL;
+            LPCWSTR helpid;
+            HRESULT res;
+
+            helpid = MSI_RecordGetString(row,6);
+
+            if (helpid)
+                help = resolve_folder(package,helpid,FALSE,FALSE,NULL);
+            res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
+            msi_free(help);
+
+            if (!SUCCEEDED(res))
+                ERR("Failed to register type library %s\n",
+                        debugstr_w(tl_struct.path));
+            else
+            {
+                ui_actiondata(package,szRegisterTypeLibraries,row);
+
+                TRACE("Registered %s\n", debugstr_w(tl_struct.path));
+            }
+
+            ITypeLib_Release(tl_struct.ptLib);
+            msi_free(tl_struct.path);
+        }
+        else
+            ERR("Failed to load type library %s\n",
+                    debugstr_w(tl_struct.source));
+
+        FreeLibrary(module);
+        msi_free(tl_struct.source);
+    }
+    else
+        ERR("Could not load file! %s\n", debugstr_w(file->TargetPath));
+
+    return ERROR_SUCCESS;
+}
+
+static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
+{
+    /* 
+     * OK this is a bit confusing.. I am given a _Component key and I believe
+     * that the file that is being registered as a type library is the "key file
+     * of that component" which I interpret to mean "The file in the KeyPath of
+     * that component".
+     */
+    UINT rc;
+    MSIQUERY * view;
+    static const WCHAR Query[] =
+        {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
+         '`','T','y','p','e','L','i','b','`',0};
+
+    rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
+    if (rc != ERROR_SUCCESS)
+        return ERROR_SUCCESS;
+
+    rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
+    msiobj_release(&view->hdr);
+    return rc;
+}
+
+static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
+{
+    MSIPACKAGE *package = (MSIPACKAGE*)param;
+    LPWSTR target_file, target_folder;
+    LPCWSTR buffer;
+    WCHAR filename[0x100];
+    DWORD sz;
+    MSICOMPONENT *comp;
+    static const WCHAR szlnk[]={'.','l','n','k',0};
+    IShellLinkW *sl;
+    IPersistFile *pf;
+    HRESULT res;
+
+    buffer = MSI_RecordGetString(row,4);
+    comp = get_loaded_component(package,buffer);
+    if (!comp)
+        return ERROR_SUCCESS;
+
+    if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
+    {
+        TRACE("Skipping shortcut creation due to disabled component\n");
+
+        comp->Action = comp->Installed;
+
+        return ERROR_SUCCESS;
+    }
+
+    comp->Action = INSTALLSTATE_LOCAL;
+
+    ui_actiondata(package,szCreateShortcuts,row);
+
+    res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
+                    &IID_IShellLinkW, (LPVOID *) &sl );
+
+    if (FAILED(res))
+    {
+        ERR("Is IID_IShellLink\n");
+        return ERROR_SUCCESS;
+    }
+
+    res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
+    if( FAILED( res ) )
+    {
+        ERR("Is IID_IPersistFile\n");
+        return ERROR_SUCCESS;
+    }
+
+    buffer = MSI_RecordGetString(row,2);
+    target_folder = resolve_folder(package, buffer,FALSE,FALSE,NULL);
+
+    /* may be needed because of a bug somehwere else */
+    create_full_pathW(target_folder);
+
+    sz = 0x100;
+    MSI_RecordGetStringW(row,3,filename,&sz);
+    reduce_to_longfilename(filename);
+    if (!strchrW(filename,'.') || strcmpiW(strchrW(filename,'.'),szlnk))
+        strcatW(filename,szlnk);
+    target_file = build_directory_name(2, target_folder, filename);
+    msi_free(target_folder);
+
+    buffer = MSI_RecordGetString(row,5);
+    if (strchrW(buffer,'['))
+    {
+        LPWSTR deformated;
+        deformat_string(package,buffer,&deformated);
+        IShellLinkW_SetPath(sl,deformated);
+        msi_free(deformated);
+    }
+    else
+    {
+        FIXME("poorly handled shortcut format, advertised shortcut\n");
+        IShellLinkW_SetPath(sl,comp->FullKeypath);
+    }
+
+    if (!MSI_RecordIsNull(row,6))
+    {
+        LPWSTR deformated;
+        buffer = MSI_RecordGetString(row,6);
+        deformat_string(package,buffer,&deformated);
+        IShellLinkW_SetArguments(sl,deformated);
+        msi_free(deformated);
+    }
+
+    if (!MSI_RecordIsNull(row,7))
+    {
+        buffer = MSI_RecordGetString(row,7);
+        IShellLinkW_SetDescription(sl,buffer);
+    }
+
+    if (!MSI_RecordIsNull(row,8))
+        IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
+
+    if (!MSI_RecordIsNull(row,9))
+    {
+        LPWSTR Path;
+        INT index; 
+
+        buffer = MSI_RecordGetString(row,9);
+
+        Path = build_icon_path(package,buffer);
+        index = MSI_RecordGetInteger(row,10);
+
+        IShellLinkW_SetIconLocation(sl,Path,index);
+        msi_free(Path);
+    }
+
+    if (!MSI_RecordIsNull(row,11))
+        IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
+
+    if (!MSI_RecordIsNull(row,12))
+    {
+        LPWSTR Path;
+        buffer = MSI_RecordGetString(row,12);
+        Path = resolve_folder(package, buffer, FALSE, FALSE, NULL);
+        IShellLinkW_SetWorkingDirectory(sl,Path);
+        msi_free(Path);
+    }
+
+    TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
+    IPersistFile_Save(pf,target_file,FALSE);
+
+    msi_free(target_file);    
+
+    IPersistFile_Release( pf );
+    IShellLinkW_Release( sl );
+
+    return ERROR_SUCCESS;
+}
+
+static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
+{
+    UINT rc;
+    HRESULT res;
+    MSIQUERY * view;
+    static const WCHAR Query[] =
+        {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
+         '`','S','h','o','r','t','c','u','t','`',0};
+
+    rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
+    if (rc != ERROR_SUCCESS)
+        return ERROR_SUCCESS;
+
+    res = CoInitialize( NULL );
+    if (FAILED (res))
+    {
+        ERR("CoInitialize failed\n");
+        return ERROR_FUNCTION_FAILED;
+    }
+
+    rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
+    msiobj_release(&view->hdr);
+
+    CoUninitialize();
+
+    return rc;
+}
+
+static UINT ITERATE_PublishProduct(MSIRECORD *row, LPVOID param)
+{
+    MSIPACKAGE* package = (MSIPACKAGE*)param;
+    HANDLE the_file;
+    LPWSTR FilePath;
+    LPCWSTR FileName;
+    CHAR buffer[1024];
+    DWORD sz;
+    UINT rc;
+
+    FileName = MSI_RecordGetString(row,1);
+    if (!FileName)
+    {
+        ERR("Unable to get FileName\n");
+        return ERROR_SUCCESS;
+    }
+
+    FilePath = build_icon_path(package,FileName);
+
+    TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
+
+    the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
+                        FILE_ATTRIBUTE_NORMAL, NULL);
+
+    if (the_file == INVALID_HANDLE_VALUE)
+    {
+        ERR("Unable to create file %s\n",debugstr_w(FilePath));
+        msi_free(FilePath);
+        return ERROR_SUCCESS;
+    }
+
+    do 
+    {
+        DWORD write;
+        sz = 1024;
+        rc = MSI_RecordReadStream(row,2,buffer,&sz);
+        if (rc != ERROR_SUCCESS)
+        {
+            ERR("Failed to get stream\n");
+            CloseHandle(the_file);  
+            DeleteFileW(FilePath);
+            break;
+        }
+        WriteFile(the_file,buffer,sz,&write,NULL);
+    } while (sz == 1024);
+
+    msi_free(FilePath);
+
+    CloseHandle(the_file);
+    return ERROR_SUCCESS;
+}
+
+/*
+ * 99% of the work done here is only done for 
+ * advertised installs. However this is where the
+ * Icon table is processed and written out
+ * so that is what I am going to do here.
+ */
+static UINT ACTION_PublishProduct(MSIPACKAGE *package)
+{
+    UINT rc;
+    MSIQUERY * view;
+    static const WCHAR Query[]=
+        {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
+         '`','I','c','o','n','`',0};
+    /* for registry stuff */
+    HKEY hkey=0;
+    HKEY hukey=0;
+    static const WCHAR szProductLanguage[] =
+        {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
+    static const WCHAR szARPProductIcon[] =
+        {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
+    static const WCHAR szProductVersion[] =
+        {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
+    DWORD langid;
+    LPWSTR buffer;
+    DWORD size;
+    MSIHANDLE hDb, hSumInfo;
+
+    /* write out icon files */
+
+    rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
+    if (rc == ERROR_SUCCESS)
+    {
+        MSI_IterateRecords(view, NULL, ITERATE_PublishProduct, package);
+        msiobj_release(&view->hdr);
+    }
+
+    /* ok there is a lot more done here but i need to figure out what */
+
+    rc = MSIREG_OpenProductsKey(package->ProductCode,&hkey,TRUE);
+    if (rc != ERROR_SUCCESS)
+        goto end;
+
+    rc = MSIREG_OpenUserProductsKey(package->ProductCode,&hukey,TRUE);
+    if (rc != ERROR_SUCCESS)
+        goto end;
+
+
+    buffer = msi_dup_property( package, INSTALLPROPERTY_PRODUCTNAMEW );
+    msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTNAMEW, buffer );
+    msi_free(buffer);
+
+    langid = msi_get_property_int( package, szProductLanguage, 0 );
+    msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
+
+    buffer = msi_dup_property( package, szARPProductIcon );
+    if (buffer)
+    {
+        LPWSTR path = build_icon_path(package,buffer);
+        msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTICONW, path );
+        msi_free( path );
+    }
+    msi_free(buffer);
+
+    buffer = msi_dup_property( package, szProductVersion );
+    if (buffer)
+    {
+        DWORD verdword = build_version_dword(buffer);
+        msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
+    }
+    msi_free(buffer);
+    
+    /* FIXME: Need to write more keys to the user registry */
+  
+    hDb= alloc_msihandle( &package->db->hdr );
+    rc = MsiGetSummaryInformationW(hDb, NULL, 0, &hSumInfo); 
+    MsiCloseHandle(hDb);
+    if (rc == ERROR_SUCCESS)
+    {
+        WCHAR guidbuffer[0x200];
+        size = 0x200;
+        rc = MsiSummaryInfoGetPropertyW(hSumInfo, 9, NULL, NULL, NULL,
+                                        guidbuffer, &size);
+        if (rc == ERROR_SUCCESS)
+        {
+            WCHAR squashed[GUID_SIZE];
+            /* for now we only care about the first guid */
+            LPWSTR ptr = strchrW(guidbuffer,';');
+            if (ptr) *ptr = 0;
+            squash_guid(guidbuffer,squashed);
+            msi_reg_set_val_str( hukey, INSTALLPROPERTY_PACKAGECODEW, squashed );
+        }
+        else
+        {
+            ERR("Unable to query Revision_Number...\n");
+            rc = ERROR_SUCCESS;
+        }
+        MsiCloseHandle(hSumInfo);
+    }
+    else
+    {
+        ERR("Unable to open Summary Information\n");
+        rc = ERROR_SUCCESS;
+    }
+
+end:
+
+    RegCloseKey(hkey);
+    RegCloseKey(hukey);
+
+    return rc;
+}
+
+static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
+{
+    MSIPACKAGE *package = (MSIPACKAGE*)param;
+    LPCWSTR component,section,key,value,identifier,filename,dirproperty;
+    LPWSTR deformated_section, deformated_key, deformated_value;
+    LPWSTR folder, fullname = NULL;
+    MSIRECORD * uirow;
+    INT action;
+    MSICOMPONENT *comp;
+    static const WCHAR szWindowsFolder[] =
+          {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
+
+    component = MSI_RecordGetString(row, 8);
+    comp = get_loaded_component(package,component);
+
+    if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
+    {
+        TRACE("Skipping ini file due to disabled component %s\n",
+                        debugstr_w(component));
+
+        comp->Action = comp->Installed;
+
+        return ERROR_SUCCESS;
+    }
+
+    comp->Action = INSTALLSTATE_LOCAL;
+
+    identifier = MSI_RecordGetString(row,1); 
+    filename = MSI_RecordGetString(row,2);
+    dirproperty = MSI_RecordGetString(row,3);
+    section = MSI_RecordGetString(row,4);
+    key = MSI_RecordGetString(row,5);
+    value = MSI_RecordGetString(row,6);
+    action = MSI_RecordGetInteger(row,7);
+
+    deformat_string(package,section,&deformated_section);
+    deformat_string(package,key,&deformated_key);
+    deformat_string(package,value,&deformated_value);
+
+    if (dirproperty)
+    {
+        folder = resolve_folder(package, dirproperty, FALSE, FALSE, NULL);
+        if (!folder)
+            folder = msi_dup_property( package, dirproperty );
+    }
+    else
+        folder = msi_dup_property( package, szWindowsFolder );
+
+    if (!folder)
+    {
+        ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
+        goto cleanup;
+    }
+
+    fullname = build_directory_name(2, folder, filename);
+
+    if (action == 0)
+    {
+        TRACE("Adding value %s to section %s in %s\n",
+                debugstr_w(deformated_key), debugstr_w(deformated_section),
+                debugstr_w(fullname));
+        WritePrivateProfileStringW(deformated_section, deformated_key,
+                                   deformated_value, fullname);
+    }
+    else if (action == 1)
+    {
+        WCHAR returned[10];
+        GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
+                                 returned, 10, fullname);
+        if (returned[0] == 0)
+        {
+            TRACE("Adding value %s to section %s in %s\n",
+                    debugstr_w(deformated_key), debugstr_w(deformated_section),
+                    debugstr_w(fullname));
+
+            WritePrivateProfileStringW(deformated_section, deformated_key,
+                                       deformated_value, fullname);
+        }
+    }
+    else if (action == 3)
+        FIXME("Append to existing section not yet implemented\n");
+
+    uirow = MSI_CreateRecord(4);
+    MSI_RecordSetStringW(uirow,1,identifier);
+    MSI_RecordSetStringW(uirow,2,deformated_section);
+    MSI_RecordSetStringW(uirow,3,deformated_key);
+    MSI_RecordSetStringW(uirow,4,deformated_value);
+    ui_actiondata(package,szWriteIniValues,uirow);
+    msiobj_release( &uirow->hdr );
+cleanup:
+    msi_free(fullname);
+    msi_free(folder);
+    msi_free(deformated_key);
+    msi_free(deformated_value);
+    msi_free(deformated_section);
+    return ERROR_SUCCESS;
+}
+
+static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
+{
+    UINT rc;
+    MSIQUERY * view;
+    static const WCHAR ExecSeqQuery[] = 
+        {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
+         '`','I','n','i','F','i','l','e','`',0};
+
+    rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
+    if (rc != ERROR_SUCCESS)
+    {
+        TRACE("no IniFile table\n");
+        return ERROR_SUCCESS;
+    }
+
+    rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
+    msiobj_release(&view->hdr);
+    return rc;
+}
+
+static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
+{
+    MSIPACKAGE *package = (MSIPACKAGE*)param;
+    LPCWSTR filename;
+    LPWSTR FullName;
+    MSIFILE *file;
+    DWORD len;
+    static const WCHAR ExeStr[] =
+        {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
+    static const WCHAR close[] =  {'\"',0};
+    STARTUPINFOW si;
+    PROCESS_INFORMATION info;
+    BOOL brc;
+
+    memset(&si,0,sizeof(STARTUPINFOW));
+
+    filename = MSI_RecordGetString(row,1);
+    file = get_loaded_file( package, filename );
+
+    if (!file)
+    {
+        ERR("Unable to find file id %s\n",debugstr_w(filename));
+        return ERROR_SUCCESS;
+    }
+
+    len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
+
+    FullName = msi_alloc(len*sizeof(WCHAR));
+    strcpyW(FullName,ExeStr);
+    strcatW( FullName, file->TargetPath );
+    strcatW(FullName,close);
+
+    TRACE("Registering %s\n",debugstr_w(FullName));
+    brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
+                    &si, &info);
+
+    if (brc)
+        msi_dialog_check_messages(info.hProcess);
+
+    msi_free(FullName);
+    return ERROR_SUCCESS;
+}
+
+static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
+{
+    UINT rc;
+    MSIQUERY * view;
+    static const WCHAR ExecSeqQuery[] = 
+        {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
+         '`','S','e','l','f','R','e','g','`',0};
+
+    rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
+    if (rc != ERROR_SUCCESS)
+    {
+        TRACE("no SelfReg table\n");
+        return ERROR_SUCCESS;
+    }
+
+    MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
+    msiobj_release(&view->hdr);
+
+    return ERROR_SUCCESS;
+}
+
+static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
+{
+    MSIFEATURE *feature;
+    UINT rc;
+    HKEY hkey=0;
+    HKEY hukey=0;
+    
+    rc = MSIREG_OpenFeaturesKey(package->ProductCode,&hkey,TRUE);
+    if (rc != ERROR_SUCCESS)
+        goto end;
+
+    rc = MSIREG_OpenUserFeaturesKey(package->ProductCode,&hukey,TRUE);
+    if (rc != ERROR_SUCCESS)
+        goto end;
+
+    /* here the guids are base 85 encoded */
+    LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
+    {
+        ComponentList *cl;
+        LPWSTR data = NULL;
+        GUID clsid;
+        INT size;
+        BOOL absent = FALSE;
+
+        if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
+            !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) &&
+            !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
+            absent = TRUE;
+
+        size = 1;
+        LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
+        {
+            size += 21;
+        }
+        if (feature->Feature_Parent)
+            size += strlenW( feature->Feature_Parent )+2;
+
+        data = msi_alloc(size * sizeof(WCHAR));
+
+        data[0] = 0;
+        LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
+        {
+            MSICOMPONENT* component = cl->component;
+            WCHAR buf[21];
+
+            memset(buf,0,sizeof(buf));
+            if (component->ComponentId)
+            {
+                TRACE("From %s\n",debugstr_w(component->ComponentId));
+                CLSIDFromString(component->ComponentId, &clsid);
+                encode_base85_guid(&clsid,buf);
+                TRACE("to %s\n",debugstr_w(buf));
+                strcatW(data,buf);
+            }
+        }
+        if (feature->Feature_Parent)
+        {
+            static const WCHAR sep[] = {'\2',0};
+            strcatW(data,sep);
+            strcatW(data,feature->Feature_Parent);
+        }
+
+        msi_reg_set_val_str( hkey, feature->Feature, data );
+        msi_free(data);
+
+        size = 0;
+        if (feature->Feature_Parent)
+            size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
+        if (!absent)
+        {
+            RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
+                       (LPBYTE)feature->Feature_Parent,size);
+        }
+        else
+        {
+            size += 2*sizeof(WCHAR);
+            data = msi_alloc(size);
+            data[0] = 0x6;
+            data[1] = 0;
+            if (feature->Feature_Parent)
+                strcpyW( &data[1], feature->Feature_Parent );
+            RegSetValueExW(hukey,feature->Feature,0,REG_SZ,
+                       (LPBYTE)data,size);
+            msi_free(data);
+        }
+    }
+
+end:
+    RegCloseKey(hkey);
+    RegCloseKey(hukey);
+    return rc;
+}
+
+static UINT msi_make_package_local( MSIPACKAGE *package, HKEY hkey )
+{
+    static const WCHAR installerPathFmt[] = {
+        '%','s','\\','I','n','s','t','a','l','l','e','r','\\',0};
+    static const WCHAR fmt[] = {
+        '%','s','\\',
+        'I','n','s','t','a','l','l','e','r','\\',
+        '%','x','.','m','s','i',0};
+    static const WCHAR szOriginalDatabase[] =
+        {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
+    WCHAR windir[MAX_PATH], path[MAX_PATH], packagefile[MAX_PATH];
+    INT num, start;
+    LPWSTR msiFilePath;
+    BOOL r;
+
+    /* copy the package locally */
+    num = GetTickCount() & 0xffff;
+    if (!num) 
+        num = 1;
+    start = num;
+    GetWindowsDirectoryW( windir, MAX_PATH );
+    snprintfW( packagefile, MAX_PATH, fmt, windir, num );
+    do 
+    {
+        HANDLE handle = CreateFileW(packagefile,GENERIC_WRITE, 0, NULL,
+                                  CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
+        if (handle != INVALID_HANDLE_VALUE)
+        {
+            CloseHandle(handle);
+            break;
+        }
+        if (GetLastError() != ERROR_FILE_EXISTS &&
+            GetLastError() != ERROR_SHARING_VIOLATION)
+            break;
+        if (!(++num & 0xffff)) num = 1;
+        sprintfW(packagefile,fmt,num);
+    } while (num != start);
+
+    snprintfW( path, MAX_PATH, installerPathFmt, windir );
+    create_full_pathW(path);
+
+    TRACE("Copying to local package %s\n",debugstr_w(packagefile));
+
+    msiFilePath = msi_dup_property( package, szOriginalDatabase );
+    r = CopyFileW( msiFilePath, packagefile, FALSE);
+    msi_free( msiFilePath );
+
+    if (!r)
+    {
+        ERR("Unable to copy package (%s -> %s) (error %ld)\n",
+            debugstr_w(msiFilePath), debugstr_w(packagefile), GetLastError());
+        return ERROR_FUNCTION_FAILED;
+    }
+
+    /* FIXME: maybe set this key in ACTION_RegisterProduct instead */
+    msi_reg_set_val_str( hkey, INSTALLPROPERTY_LOCALPACKAGEW, packagefile );
+    return ERROR_SUCCESS;
+}
+
+static UINT msi_write_uninstall_property_vals( MSIPACKAGE *package, HKEY hkey )
+{
+    LPWSTR prop, val, key;
+    static const LPCSTR propval[] = {
+        "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
+        "ARPCONTACT",             "Contact",
+        "ARPCOMMENTS",            "Comments",
+        "ProductName",            "DisplayName",
+        "ProductVersion",         "DisplayVersion",
+        "ARPHELPLINK",            "HelpLink",
+        "ARPHELPTELEPHONE",       "HelpTelephone",
+        "ARPINSTALLLOCATION",     "InstallLocation",
+        "SourceDir",              "InstallSource",
+        "Manufacturer",           "Publisher",
+        "ARPREADME",              "Readme",
+        "ARPSIZE",                "Size",
+        "ARPURLINFOABOUT",        "URLInfoAbout",
+        "ARPURLUPDATEINFO",       "URLUpdateInfo",
+        NULL,
+    };
+    const LPCSTR *p = propval;
+
+    while( *p )
+    {
+        prop = strdupAtoW( *p++ );
+        key = strdupAtoW( *p++ );
+        val = msi_dup_property( package, prop );
+        msi_reg_set_val_str( hkey, key, val );
+        msi_free(val);
+        msi_free(key);
+        msi_free(prop);
+    }
+    return ERROR_SUCCESS;
+}
+
+static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
+{
+    HKEY hkey=0;
+    LPWSTR buffer = NULL;
+    UINT rc;
+    DWORD size, langid;
+    static const WCHAR szWindowsInstaller[] = 
+        {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
+    static const WCHAR szUpgradeCode[] = 
+        {'U','p','g','r','a','d','e','C','o','d','e',0};
+    static const WCHAR modpath_fmt[] = 
+        {'M','s','i','E','x','e','c','.','e','x','e',' ',
+         '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
+    static const WCHAR szModifyPath[] = 
+        {'M','o','d','i','f','y','P','a','t','h',0};
+    static const WCHAR szUninstallString[] = 
+        {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
+    static const WCHAR szEstimatedSize[] = 
+        {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
+    static const WCHAR szProductLanguage[] =
+        {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
+    static const WCHAR szProductVersion[] =
+        {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
+
+    SYSTEMTIME systime;
+    static const WCHAR date_fmt[] = {'%','i','%','i','%','i',0};
+    LPWSTR upgrade_code;
+    WCHAR szDate[9]; 
+
+    rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
+    if (rc != ERROR_SUCCESS)
+        return rc;
+
+    /* dump all the info i can grab */
+    /* FIXME: Flesh out more information */
+
+    msi_write_uninstall_property_vals( package, hkey );
+
+    msi_reg_set_val_dword( hkey, szWindowsInstaller, 1 );
+    
+    msi_make_package_local( package, hkey );
+
+    /* do ModifyPath and UninstallString */
+    size = deformat_string(package,modpath_fmt,&buffer);
+    RegSetValueExW(hkey,szModifyPath,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
+    RegSetValueExW(hkey,szUninstallString,0,REG_EXPAND_SZ,(LPBYTE)buffer,size);
+    msi_free(buffer);
+
+    /* FIXME: Write real Estimated Size when we have it */
+    msi_reg_set_val_dword( hkey, szEstimatedSize, 0 );
+   
+    GetLocalTime(&systime);
+    sprintfW(szDate,date_fmt,systime.wYear,systime.wMonth,systime.wDay);
+    msi_reg_set_val_str( hkey, INSTALLPROPERTY_INSTALLDATEW, szDate );
+   
+    langid = msi_get_property_int( package, szProductLanguage, 0 );
+    msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid );
+
+    buffer = msi_dup_property( package, szProductVersion );
+    if (buffer)
+    {
+        DWORD verdword = build_version_dword(buffer);
+
+        msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword );
+        msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword>>24 );
+        msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword>>16)&0x00FF );
+    }
+    msi_free(buffer);
+    
+    /* Handle Upgrade Codes */
+    upgrade_code = msi_dup_property( package, szUpgradeCode );
+    if (upgrade_code)
+    {
+        HKEY hkey2;
+        WCHAR squashed[33];
+        MSIREG_OpenUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
+        squash_guid(package->ProductCode,squashed);
+        msi_reg_set_val_str( hkey2, squashed, NULL );
+        RegCloseKey(hkey2);
+        MSIREG_OpenUserUpgradeCodesKey(upgrade_code, &hkey2, TRUE);
+        squash_guid(package->ProductCode,squashed);
+        msi_reg_set_val_str( hkey2, squashed, NULL );
+        RegCloseKey(hkey2);
+
+        msi_free(upgrade_code);
+    }
+    
+    RegCloseKey(hkey);
+
+    return ERROR_SUCCESS;
+}
+
+static UINT ACTION_InstallExecute(MSIPACKAGE *package)
+{
+    return execute_script(package,INSTALL_SCRIPT);
+}
+
+static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
+{
+    UINT rc;
+
+    /* turn off scheduleing */
+    package->script->CurrentlyScripting= FALSE;
+
+    /* first do the same as an InstallExecute */
+    rc = ACTION_InstallExecute(package);
+    if (rc != ERROR_SUCCESS)
+        return rc;
+
+    /* then handle Commit Actions */
+    rc = execute_script(package,COMMIT_SCRIPT);
+
+    return rc;
+}
+
+static UINT ACTION_ForceReboot(MSIPACKAGE *package)
+{
+    static const WCHAR RunOnce[] = {
+    'S','o','f','t','w','a','r','e','\\',
+    'M','i','c','r','o','s','o','f','t','\\',
+    'W','i','n','d','o','w','s','\\',
+    'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
+    'R','u','n','O','n','c','e',0};
+    static const WCHAR InstallRunOnce[] = {
+    'S','o','f','t','w','a','r','e','\\',
+    'M','i','c','r','o','s','o','f','t','\\',
+    'W','i','n','d','o','w','s','\\',
+    'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
+    'I','n','s','t','a','l','l','e','r','\\',
+    'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
+
+    static const WCHAR msiexec_fmt[] = {
+    '%','s',
+    '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
+    '\"','%','s','\"',0};
+    static const WCHAR install_fmt[] = {
+    '/','I',' ','\"','%','s','\"',' ',
+    'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
+    'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
+    WCHAR buffer[256], sysdir[MAX_PATH];
+    HKEY hkey;
+    WCHAR squished_pc[100];
+
+    squash_guid(package->ProductCode,squished_pc);
+
+    GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
+    RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
+    snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
+     squished_pc);
+
+    msi_reg_set_val_str( hkey, squished_pc, buffer );
+    RegCloseKey(hkey);
+
+    TRACE("Reboot command %s\n",debugstr_w(buffer));
+
+    RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
+    sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
+
+    msi_reg_set_val_str( hkey, squished_pc, buffer );
+    RegCloseKey(hkey);
+
+    return ERROR_INSTALL_SUSPEND;
+}
+
+UINT ACTION_ResolveSource(MSIPACKAGE* package)
+{
+    DWORD attrib;
+    UINT rc;
+    /*
+     * we are currently doing what should be done here in the top level Install
+     * however for Adminastrative and uninstalls this step will be needed
+     */
+    if (!package->PackagePath)
+        return ERROR_SUCCESS;
+
+    attrib = GetFileAttributesW(package->PackagePath);
+    if (attrib == INVALID_FILE_ATTRIBUTES)
+    {
+        LPWSTR prompt;
+        LPWSTR msg;
+        DWORD size = 0;
+
+        rc = MsiSourceListGetInfoW(package->ProductCode, NULL, 
+                MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT,
+                INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
+        if (rc == ERROR_MORE_DATA)
+        {
+            prompt = msi_alloc(size * sizeof(WCHAR));
+            MsiSourceListGetInfoW(package->ProductCode, NULL, 
+                    MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT,
+                    INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
+        }
+        else
+            prompt = strdupW(package->PackagePath);
+
+        msg = generate_error_string(package,1302,1,prompt);
+        while(attrib == INVALID_FILE_ATTRIBUTES)
+        {
+            rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
+            if (rc == IDCANCEL)
+            {
+                rc = ERROR_INSTALL_USEREXIT;
+                break;
+            }
+            attrib = GetFileAttributesW(package->PackagePath);
+        }
+        msi_free(prompt);
+        rc = ERROR_SUCCESS;
+    }
+    else
+        return ERROR_SUCCESS;
+
+    return rc;
+}
+
+static UINT ACTION_RegisterUser(MSIPACKAGE *package)
+{
+    HKEY hkey=0;
+    LPWSTR buffer;
+    LPWSTR productid;
+    UINT rc,i;
+
+    static const WCHAR szPropKeys[][80] = 
+    {
+        {'P','r','o','d','u','c','t','I','D',0},
+        {'U','S','E','R','N','A','M','E',0},
+        {'C','O','M','P','A','N','Y','N','A','M','E',0},
+        {0},
+    };
+
+    static const WCHAR szRegKeys[][80] = 
+    {
+        {'P','r','o','d','u','c','t','I','D',0},
+        {'R','e','g','O','w','n','e','r',0},
+        {'R','e','g','C','o','m','p','a','n','y',0},
+        {0},
+    };
+
+    productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
+    if (!productid)
+        return ERROR_SUCCESS;
+
+    rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE);
+    if (rc != ERROR_SUCCESS)
+        goto end;
+
+    for( i = 0; szPropKeys[i][0]; i++ )
+    {
+        buffer = msi_dup_property( package, szPropKeys[i] );
+        msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
+        msi_free( buffer );
+    }
+
+end:
+    msi_free(productid);
+    RegCloseKey(hkey);
+
+    return ERROR_SUCCESS;
+}
+
+
+static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
+{
+    UINT rc;
+
+    package->script->InWhatSequence |= SEQUENCE_EXEC;
+    rc = ACTION_ProcessExecSequence(package,FALSE);
+    return rc;
+}
+
+
+/*
+ * Code based off of code located here
+ * http://www.codeproject.com/gdi/fontnamefromfile.asp
+ *
+ * Using string index 4 (full font name) instead of 1 (family name)
+ */
+static LPWSTR load_ttfname_from(LPCWSTR filename)
+{
+    HANDLE handle;
+    LPWSTR ret = NULL;
+    int i;
+
+    typedef struct _tagTT_OFFSET_TABLE{
+        USHORT uMajorVersion;
+        USHORT uMinorVersion;
+        USHORT uNumOfTables;
+        USHORT uSearchRange;
+        USHORT uEntrySelector;
+        USHORT uRangeShift;
+    }TT_OFFSET_TABLE;
+
+    typedef struct _tagTT_TABLE_DIRECTORY{
+        char szTag[4]; /* table name */
+        ULONG uCheckSum; /* Check sum */
+        ULONG uOffset; /* Offset from beginning of file */
+        ULONG uLength; /* length of the table in bytes */
+    }TT_TABLE_DIRECTORY;
+
+    typedef struct _tagTT_NAME_TABLE_HEADER{
+    USHORT uFSelector; /* format selector. Always 0 */
+    USHORT uNRCount; /* Name Records count */
+    USHORT uStorageOffset; /* Offset for strings storage, 
+                            * from start of the table */
+    }TT_NAME_TABLE_HEADER;
+   
+    typedef struct _tagTT_NAME_RECORD{
+        USHORT uPlatformID;
+        USHORT uEncodingID;
+        USHORT uLanguageID;
+        USHORT uNameID;
+        USHORT uStringLength;
+        USHORT uStringOffset; /* from start of storage area */
+    }TT_NAME_RECORD;
+
+#define SWAPWORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
+#define SWAPLONG(x) MAKELONG(SWAPWORD(HIWORD(x)), SWAPWORD(LOWORD(x)))
+
+    handle = CreateFileW(filename ,GENERIC_READ, 0, NULL, OPEN_EXISTING,
+                    FILE_ATTRIBUTE_NORMAL, 0 );
+    if (handle != INVALID_HANDLE_VALUE)
+    {
+        TT_TABLE_DIRECTORY tblDir;
+        BOOL bFound = FALSE;
+        TT_OFFSET_TABLE ttOffsetTable;
+        DWORD dwRead;
+
+        ReadFile(handle,&ttOffsetTable, sizeof(TT_OFFSET_TABLE),&dwRead,NULL);
+        ttOffsetTable.uNumOfTables = SWAPWORD(ttOffsetTable.uNumOfTables);
+        ttOffsetTable.uMajorVersion = SWAPWORD(ttOffsetTable.uMajorVersion);
+        ttOffsetTable.uMinorVersion = SWAPWORD(ttOffsetTable.uMinorVersion);
+        
+        if (ttOffsetTable.uMajorVersion != 1 || 
+                        ttOffsetTable.uMinorVersion != 0)
+            return NULL;
+
+        for (i=0; i< ttOffsetTable.uNumOfTables; i++)
+        {
+            ReadFile(handle,&tblDir, sizeof(TT_TABLE_DIRECTORY),&dwRead,NULL);
+            if (strncmp(tblDir.szTag,"name",4)==0)
+            {
+                bFound = TRUE;
+                tblDir.uLength = SWAPLONG(tblDir.uLength);
+                tblDir.uOffset = SWAPLONG(tblDir.uOffset);
+                break;
+            }
+        }
+
+        if (bFound)
+        {
+            TT_NAME_TABLE_HEADER ttNTHeader;
+            TT_NAME_RECORD ttRecord;
+
+            SetFilePointer(handle, tblDir.uOffset, NULL, FILE_BEGIN);
+            ReadFile(handle,&ttNTHeader, sizeof(TT_NAME_TABLE_HEADER),
+                            &dwRead,NULL);
+
+            ttNTHeader.uNRCount = SWAPWORD(ttNTHeader.uNRCount);
+            ttNTHeader.uStorageOffset = SWAPWORD(ttNTHeader.uStorageOffset);
+            bFound = FALSE;
+            for(i=0; i<ttNTHeader.uNRCount; i++)
+            {
+                ReadFile(handle,&ttRecord, sizeof(TT_NAME_RECORD),&dwRead,NULL);
+                ttRecord.uNameID = SWAPWORD(ttRecord.uNameID);
+                /* 4 is the Full Font Name */
+                if(ttRecord.uNameID == 4)
+                {
+                    int nPos;
+                    LPSTR buf;
+                    static LPCSTR tt = " (TrueType)";
+
+                    ttRecord.uStringLength = SWAPWORD(ttRecord.uStringLength);
+                    ttRecord.uStringOffset = SWAPWORD(ttRecord.uStringOffset);
+                    nPos = SetFilePointer(handle, 0, NULL, FILE_CURRENT);
+                    SetFilePointer(handle, tblDir.uOffset + 
+                                    ttRecord.uStringOffset + 
+                                    ttNTHeader.uStorageOffset,
+                                    NULL, FILE_BEGIN);
+                    buf = msi_alloc( ttRecord.uStringLength + 1 + strlen(tt) );
+                    memset(buf, 0, ttRecord.uStringLength + 1 + strlen(tt));
+                    ReadFile(handle, buf, ttRecord.uStringLength, &dwRead, NULL);
+                    if (strlen(buf) > 0)
+                    {
+                        strcat(buf,tt);
+                        ret = strdupAtoW(buf);
+                        msi_free(buf);
+                        break;
+                    }
+
+                    msi_free(buf);
+                    SetFilePointer(handle,nPos, NULL, FILE_BEGIN);
+                }
+            }
+        }
+        CloseHandle(handle);
+    }
+    else
+        ERR("Unable to open font file %s\n", debugstr_w(filename));
+
+    TRACE("Returning fontname %s\n",debugstr_w(ret));
+    return ret;
+}
+
+static UINT ITERATE_RegisterFonts(MSIRECORD *row, LPVOID param)
+{
+    MSIPACKAGE *package = (MSIPACKAGE*)param;
+    LPWSTR name;
+    LPCWSTR filename;
+    MSIFILE *file;
+    static const WCHAR regfont1[] =
+        {'S','o','f','t','w','a','r','e','\\',
+         'M','i','c','r','o','s','o','f','t','\\',
+         'W','i','n','d','o','w','s',' ','N','T','\\',
+         'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
+         'F','o','n','t','s',0};
+    static const WCHAR regfont2[] =
+        {'S','o','f','t','w','a','r','e','\\',
+         'M','i','c','r','o','s','o','f','t','\\',
+         'W','i','n','d','o','w','s','\\',
+         'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
+         'F','o','n','t','s',0};
+    HKEY hkey1;
+    HKEY hkey2;
+
+    filename = MSI_RecordGetString( row, 1 );
+    file = get_loaded_file( package, filename );
+    if (!file)
+    {
+        ERR("Unable to load file\n");
+        return ERROR_SUCCESS;
+    }
+
+    /* check to make sure that component is installed */
+    if (!ACTION_VerifyComponentForAction( file->Component, INSTALLSTATE_LOCAL))
+    {
+        TRACE("Skipping: Component not scheduled for install\n");
+        return ERROR_SUCCESS;
+    }
+
+    RegCreateKeyW(HKEY_LOCAL_MACHINE,regfont1,&hkey1);
+    RegCreateKeyW(HKEY_LOCAL_MACHINE,regfont2,&hkey2);
+
+    if (MSI_RecordIsNull(row,2))
+        name = load_ttfname_from( file->TargetPath );
+    else
+        name = msi_dup_record_field(row,2);
+
+    if (name)
+    {
+        msi_reg_set_val_str( hkey1, name, file->FileName );
+        msi_reg_set_val_str( hkey2, name, file->FileName );
+    }
+
+    msi_free(name);
+    RegCloseKey(hkey1);
+    RegCloseKey(hkey2);
+    return ERROR_SUCCESS;
+}
+
+static UINT ACTION_RegisterFonts(MSIPACKAGE *package)
+{
+    UINT rc;
+    MSIQUERY * view;
+    static const WCHAR ExecSeqQuery[] =
+        {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
+         '`','F','o','n','t','`',0};
+
+    rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
+    if (rc != ERROR_SUCCESS)
+    {
+        TRACE("MSI_DatabaseOpenViewW failed: %d\n", rc);
+        return ERROR_SUCCESS;
+    }
+
+    MSI_IterateRecords(view, NULL, ITERATE_RegisterFonts, package);
+    msiobj_release(&view->hdr);
+
+    return ERROR_SUCCESS;
+}
+
+static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
+{
+    MSIPACKAGE *package = (MSIPACKAGE*)param;
+    LPCWSTR compgroupid=NULL;
+    LPCWSTR feature=NULL;
+    LPCWSTR text = NULL;
+    LPCWSTR qualifier = NULL;
+    LPCWSTR component = NULL;
+    LPWSTR advertise = NULL;
+    LPWSTR output = NULL;
+    HKEY hkey;
+    UINT rc = ERROR_SUCCESS;
+    MSICOMPONENT *comp;
+    DWORD sz = 0;
+
+    component = MSI_RecordGetString(rec,3);
+    comp = get_loaded_component(package,component);
+
+    if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) && 
+       !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
+       !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
+    {
+        TRACE("Skipping: Component %s not scheduled for install\n",
+                        debugstr_w(component));
+
+        return ERROR_SUCCESS;
+    }
+
+    compgroupid = MSI_RecordGetString(rec,1);
+
+    rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
+    if (rc != ERROR_SUCCESS)
+        goto end;
+    
+    text = MSI_RecordGetString(rec,4);
+    qualifier = MSI_RecordGetString(rec,2);
+    feature = MSI_RecordGetString(rec,5);
+  
+    advertise = create_component_advertise_string(package, comp, feature);
+
+    sz = strlenW(advertise);
+
+    if (text)
+        sz += lstrlenW(text);
+
+    sz+=3;
+    sz *= sizeof(WCHAR);
+           
+    output = msi_alloc(sz);
+    memset(output,0,sz);
+    strcpyW(output,advertise);
+    msi_free(advertise);
+
+    if (text)
+        strcatW(output,text);
+
+    msi_reg_set_val_multi_str( hkey, qualifier, output );
+    
+end:
+    RegCloseKey(hkey);
+    msi_free(output);
+    
+    return rc;
+}
+
+/*
+ * At present I am ignorning the advertised components part of this and only
+ * focusing on the qualified component sets
+ */
+static UINT ACTION_PublishComponents(MSIPACKAGE *package)
+{
+    UINT rc;
+    MSIQUERY * view;
+    static const WCHAR ExecSeqQuery[] =
+        {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
+         '`','P','u','b','l','i','s','h',
+         'C','o','m','p','o','n','e','n','t','`',0};
+    
+    rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
+    if (rc != ERROR_SUCCESS)
+        return ERROR_SUCCESS;
+
+    rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
+    msiobj_release(&view->hdr);
+
+    return rc;
+}
+
+static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
+                                           LPCSTR action, LPCWSTR table )
+{
+    static const WCHAR query[] = {
+        'S','E','L','E','C','T',' ','*',' ',
+        'F','R','O','M',' ','`','%','s','`',0 };
+    MSIQUERY *view = NULL;
+    DWORD count = 0;
+    UINT r;
+    
+    r = MSI_OpenQuery( package->db, &view, query, table );
+    if (r == ERROR_SUCCESS)
+    {
+        r = MSI_IterateRecords(view, &count, NULL, package);
+        msiobj_release(&view->hdr);
+    }
+
+    if (count)
+        FIXME("%s -> %lu ignored %s table values\n",
+              action, count, debugstr_w(table));
+
+    return ERROR_SUCCESS;
+}
+
+static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
+{
+    TRACE("%p\n", package);
+    return ERROR_SUCCESS;
+}
+
+static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
+{
+    static const WCHAR table[] =
+         {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
+    return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
+}
+
+static UINT ACTION_MoveFiles( MSIPACKAGE *package )
+{
+    static const WCHAR table[] = { 'M','o','v','e','F','i','l','e',0 };
+    return msi_unimplemented_action_stub( package, "MoveFiles", table );
+}
+
+static UINT ACTION_PatchFiles( MSIPACKAGE *package )
+{
+    static const WCHAR table[] = { 'P','a','t','c','h',0 };
+    return msi_unimplemented_action_stub( package, "PatchFiles", table );
+}
+
+static UINT ACTION_BindImage( MSIPACKAGE *package )
+{
+    static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
+    return msi_unimplemented_action_stub( package, "BindImage", table );
+}
+
+static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
+{
+    static const WCHAR table[] = {
+        'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
+    return msi_unimplemented_action_stub( package, "IsolateComponents", table );
+}
+
+static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
+{
+    static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
+    return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
+}
+
+static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
+{
+    static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
+    return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
+}
+
+static UINT ACTION_InstallServices( MSIPACKAGE *package )
+{
+    static const WCHAR table[] = {
+        'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0 };
+    return msi_unimplemented_action_stub( package, "InstallServices", table );
+}
+
+static UINT ACTION_StartServices( MSIPACKAGE *package )
+{
+    static const WCHAR table[] = {
+        'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
+    return msi_unimplemented_action_stub( package, "StartServices", table );
+}
+
+static UINT ACTION_StopServices( MSIPACKAGE *package )
+{
+    static const WCHAR table[] = {
+        'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
+    return msi_unimplemented_action_stub( package, "StopServices", table );
+}
+
+static UINT ACTION_DeleteServices( MSIPACKAGE *package )
+{
+    static const WCHAR table[] = {
+        'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
+    return msi_unimplemented_action_stub( package, "DeleteServices", table );
+}
+
+static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
+{
+    static const WCHAR table[] = {
+        'E','n','v','i','r','o','n','m','e','n','t',0 };
+    return msi_unimplemented_action_stub( package, "WriteEnvironmentStrings", table );
+}
+
+static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
+{
+    static const WCHAR table[] = {
+        'E','n','v','i','r','o','n','m','e','n','t',0 };
+    return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
+}
+
+static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
+{
+    static const WCHAR table[] = {
+        'M','s','i','A','s','s','e','m','b','l','y',0 };
+    return msi_unimplemented_action_stub( package, "MsiPublishAssemblies", table );
+}
+
+static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
+{
+    static const WCHAR table[] = {
+        'M','s','i','A','s','s','e','m','b','l','y',0 };
+    return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
+}
+
+static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
+{
+    static const WCHAR table[] = { 'F','o','n','t',0 };
+    return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
+}
+
+static UINT ACTION_CCPSearch( MSIPACKAGE *package )
+{
+    static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
+    return msi_unimplemented_action_stub( package, "CCPSearch", table );
+}
+
+static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
+{
+    static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
+    return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
+}
+
+static struct _actions StandardActions[] = {
+    { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
+    { szAppSearch, ACTION_AppSearch },
+    { szBindImage, ACTION_BindImage },
+    { szCCPSearch, ACTION_CCPSearch},
+    { szCostFinalize, ACTION_CostFinalize },
+    { szCostInitialize, ACTION_CostInitialize },
+    { szCreateFolders, ACTION_CreateFolders },
+    { szCreateShortcuts, ACTION_CreateShortcuts },
+    { szDeleteServices, ACTION_DeleteServices },
+    { szDisableRollback, NULL},
+    { szDuplicateFiles, ACTION_DuplicateFiles },
+    { szExecuteAction, ACTION_ExecuteAction },
+    { szFileCost, ACTION_FileCost },
+    { szFindRelatedProducts, ACTION_FindRelatedProducts },
+    { szForceReboot, ACTION_ForceReboot },
+    { szInstallAdminPackage, NULL},
+    { szInstallExecute, ACTION_InstallExecute },
+    { szInstallExecuteAgain, ACTION_InstallExecute },
+    { szInstallFiles, ACTION_InstallFiles},
+    { szInstallFinalize, ACTION_InstallFinalize },
+    { szInstallInitialize, ACTION_InstallInitialize },
+    { szInstallSFPCatalogFile, NULL},
+    { szInstallValidate, ACTION_InstallValidate },
+    { szIsolateComponents, ACTION_IsolateComponents },
+    { szLaunchConditions, ACTION_LaunchConditions },
+    { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
+    { szMoveFiles, ACTION_MoveFiles },
+    { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
+    { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
+    { szInstallODBC, NULL},
+    { szInstallServices, ACTION_InstallServices },
+    { szPatchFiles, ACTION_PatchFiles },
+    { szProcessComponents, ACTION_ProcessComponents },
+    { szPublishComponents, ACTION_PublishComponents },
+    { szPublishFeatures, ACTION_PublishFeatures },
+    { szPublishProduct, ACTION_PublishProduct },
+    { szRegisterClassInfo, ACTION_RegisterClassInfo },
+    { szRegisterComPlus, NULL},
+    { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
+    { szRegisterFonts, ACTION_RegisterFonts },
+    { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
+    { szRegisterProduct, ACTION_RegisterProduct },
+    { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
+    { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
+    { szRegisterUser, ACTION_RegisterUser},
+    { szRemoveDuplicateFiles, NULL},
+    { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
+    { szRemoveExistingProducts, NULL},
+    { szRemoveFiles, ACTION_RemoveFiles},
+    { szRemoveFolders, NULL},
+    { szRemoveIniValues, ACTION_RemoveIniValues },
+    { szRemoveODBC, NULL},
+    { szRemoveRegistryValues, NULL},
+    { szRemoveShortcuts, NULL},
+    { szResolveSource, ACTION_ResolveSource},
+    { szRMCCPSearch, ACTION_RMCCPSearch},
+    { szScheduleReboot, NULL},
+    { szSelfRegModules, ACTION_SelfRegModules },
+    { szSelfUnregModules, ACTION_SelfUnregModules },
+    { szSetODBCFolders, NULL},
+    { szStartServices, ACTION_StartServices },
+    { szStopServices, ACTION_StopServices },
+    { szUnpublishComponents, NULL},
+    { szUnpublishFeatures, NULL},
+    { szUnregisterClassInfo, NULL},
+    { szUnregisterComPlus, NULL},
+    { szUnregisterExtensionInfo, NULL},
+    { szUnregisterFonts, ACTION_UnregisterFonts },
+    { szUnregisterMIMEInfo, NULL},
+    { szUnregisterProgIdInfo, NULL},
+    { szUnregisterTypeLibraries, NULL},
+    { szValidateProductID, NULL},
+    { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
+    { szWriteIniValues, ACTION_WriteIniValues },
+    { szWriteRegistryValues, ACTION_WriteRegistryValues},
+    { NULL, NULL},
+};
diff --git a/reactos/dll/msi/action.h b/reactos/dll/msi/action.h
new file mode 100644 (file)
index 0000000..9b3e58d
--- /dev/null
@@ -0,0 +1,302 @@
+/*
+ * Common prototypes for Action handlers
+ *
+ * Copyright 2005 Aric Stewart for CodeWeavers
+ *
+ * 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
+ */
+
+#ifndef __MSI_ACTION_H__
+#define __MSI_ACTION_H__
+
+#include "wine/list.h"
+
+typedef struct tagMSIFEATURE
+{
+    struct list entry;
+    LPWSTR Feature;
+    LPWSTR Feature_Parent;
+    LPWSTR Title;
+    LPWSTR Description;
+    INT Display;
+    INT Level;
+    LPWSTR Directory;
+    INT Attributes;
+    
+    INSTALLSTATE Installed;
+    INSTALLSTATE ActionRequest;
+    INSTALLSTATE Action;
+
+    struct list Components;
+    
+    INT Cost;
+} MSIFEATURE;
+
+typedef struct tagMSICOMPONENT
+{
+    struct list entry;
+    DWORD magic;
+    LPWSTR Component;
+    LPWSTR ComponentId;
+    LPWSTR Directory;
+    INT Attributes;
+    LPWSTR Condition;
+    LPWSTR KeyPath;
+
+    INSTALLSTATE Installed;
+    INSTALLSTATE ActionRequest;
+    INSTALLSTATE Action;
+
+    BOOL Enabled;
+    INT  Cost;
+    INT  RefCount;
+
+    LPWSTR FullKeypath;
+    LPWSTR AdvertiseString;
+} MSICOMPONENT;
+
+typedef struct tagComponentList
+{
+    struct list entry;
+    MSICOMPONENT *component;
+} ComponentList;
+
+typedef struct tagMSIFOLDER
+{
+    struct list entry;
+    LPWSTR Directory;
+    LPWSTR TargetDefault;
+    LPWSTR SourceDefault;
+
+    LPWSTR ResolvedTarget;
+    LPWSTR ResolvedSource;
+    LPWSTR Property;   /* initially set property */
+    struct tagMSIFOLDER *Parent;
+    INT   State;
+        /* 0 = uninitialized */
+        /* 1 = existing */
+        /* 2 = created remove if empty */
+        /* 3 = created persist if empty */
+    INT   Cost;
+    INT   Space;
+} MSIFOLDER;
+
+typedef enum _msi_file_state {
+    msifs_invalid,
+    msifs_missing,
+    msifs_overwrite,
+    msifs_present,
+    msifs_installed,
+    msifs_skipped,
+} msi_file_state;
+
+typedef struct tagMSIFILE
+{
+    struct list entry;
+    LPWSTR File;
+    MSICOMPONENT *Component;
+    LPWSTR FileName;
+    LPWSTR ShortName;
+    INT FileSize;
+    LPWSTR Version;
+    LPWSTR Language;
+    INT Attributes;
+    INT Sequence;   
+    msi_file_state state;
+    LPWSTR  SourcePath;
+    LPWSTR  TargetPath;
+} MSIFILE;
+
+typedef struct tagMSITEMPFILE
+{
+    struct list entry;
+    LPWSTR File;
+    LPWSTR Path;
+} MSITEMPFILE;
+
+typedef struct tagMSIAPPID
+{
+    struct list entry;
+    LPWSTR AppID; /* Primary key */
+    LPWSTR RemoteServerName;
+    LPWSTR LocalServer;
+    LPWSTR ServiceParameters;
+    LPWSTR DllSurrogate;
+    BOOL ActivateAtStorage;
+    BOOL RunAsInteractiveUser;
+} MSIAPPID;
+
+typedef struct tagMSIPROGID MSIPROGID;
+
+typedef struct tagMSICLASS
+{
+    struct list entry;
+    LPWSTR clsid;     /* Primary Key */
+    LPWSTR Context;   /* Primary Key */
+    MSICOMPONENT *Component;
+    MSIPROGID *ProgID;
+    LPWSTR ProgIDText;
+    LPWSTR Description;
+    MSIAPPID *AppID;
+    LPWSTR FileTypeMask;
+    LPWSTR IconPath;
+    LPWSTR DefInprocHandler;
+    LPWSTR DefInprocHandler32;
+    LPWSTR Argument;
+    MSIFEATURE *Feature;
+    INT Attributes;
+    /* not in the table, set during installation */
+    BOOL Installed;
+} MSICLASS;
+
+typedef struct tagMSIMIME MSIMIME;
+
+typedef struct tagMSIEXTENSION
+{
+    struct list entry;
+    LPWSTR Extension;  /* Primary Key */
+    MSICOMPONENT *Component;
+    MSIPROGID *ProgID;
+    LPWSTR ProgIDText;
+    MSIMIME *Mime;
+    MSIFEATURE *Feature;
+    /* not in the table, set during installation */
+    BOOL Installed;
+    struct list verbs;
+} MSIEXTENSION;
+
+struct tagMSIPROGID
+{
+    struct list entry;
+    LPWSTR ProgID;  /* Primary Key */
+    MSIPROGID *Parent;
+    MSICLASS *Class;
+    LPWSTR Description;
+    LPWSTR IconPath;
+    /* not in the table, set during installation */
+    BOOL InstallMe;
+    MSIPROGID *CurVer;
+    MSIPROGID *VersionInd;
+};
+
+typedef struct tagMSIVERB
+{
+    struct list entry;
+    LPWSTR Verb;
+    INT Sequence;
+    LPWSTR Command;
+    LPWSTR Argument;
+} MSIVERB;
+
+struct tagMSIMIME
+{
+    struct list entry;
+    LPWSTR ContentType;  /* Primary Key */
+    MSIEXTENSION *Extension;
+    LPWSTR clsid;
+    MSICLASS *Class;
+    /* not in the table, set during installation */
+    BOOL InstallMe;
+};
+
+enum SCRIPTS {
+        INSTALL_SCRIPT = 0,
+        COMMIT_SCRIPT = 1,
+        ROLLBACK_SCRIPT = 2,
+        TOTAL_SCRIPTS = 3
+};
+
+#define SEQUENCE_UI       0x1
+#define SEQUENCE_EXEC     0x2
+#define SEQUENCE_INSTALL  0x10
+
+typedef struct tagMSISCRIPT
+{
+    LPWSTR  *Actions[TOTAL_SCRIPTS];
+    UINT    ActionCount[TOTAL_SCRIPTS];
+    BOOL    ExecuteSequenceRun;
+    BOOL    CurrentlyScripting;
+    UINT    InWhatSequence;
+    LPWSTR  *UniqueActions;
+    UINT    UniqueActionsCount;
+} MSISCRIPT;
+
+
+extern UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, BOOL force);
+extern UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action);
+extern void ACTION_FinishCustomActions( MSIPACKAGE* package);
+extern UINT ACTION_CustomAction(MSIPACKAGE *package,const WCHAR *action, BOOL execute);
+
+/* actions in other modules */
+extern UINT ACTION_AppSearch(MSIPACKAGE *package);
+extern UINT ACTION_FindRelatedProducts(MSIPACKAGE *package);
+extern UINT ACTION_InstallFiles(MSIPACKAGE *package);
+extern UINT ACTION_RemoveFiles(MSIPACKAGE *package);
+extern UINT ACTION_DuplicateFiles(MSIPACKAGE *package);
+extern UINT ACTION_RegisterClassInfo(MSIPACKAGE *package);
+extern UINT ACTION_RegisterProgIdInfo(MSIPACKAGE *package);
+extern UINT ACTION_RegisterExtensionInfo(MSIPACKAGE *package);
+extern UINT ACTION_RegisterMIMEInfo(MSIPACKAGE *package);
+
+
+/* Helpers */
+extern DWORD deformat_string(MSIPACKAGE *package, LPCWSTR ptr, WCHAR** data );
+extern LPWSTR msi_dup_record_field(MSIRECORD *row, INT index);
+extern LPWSTR msi_dup_property(MSIPACKAGE *package, LPCWSTR prop);
+extern LPWSTR resolve_folder(MSIPACKAGE *package, LPCWSTR name, BOOL source, 
+                      BOOL set_prop, MSIFOLDER **folder);
+extern MSICOMPONENT *get_loaded_component( MSIPACKAGE* package, LPCWSTR Component );
+extern MSIFEATURE *get_loaded_feature( MSIPACKAGE* package, LPCWSTR Feature );
+extern MSIFILE *get_loaded_file( MSIPACKAGE* package, LPCWSTR file );
+extern MSIFOLDER *get_loaded_folder( MSIPACKAGE *package, LPCWSTR dir );
+extern int track_tempfile(MSIPACKAGE *package, LPCWSTR name, LPCWSTR path);
+extern UINT schedule_action(MSIPACKAGE *package, UINT script, LPCWSTR action);
+extern LPWSTR build_icon_path(MSIPACKAGE *, LPCWSTR);
+extern DWORD build_version_dword(LPCWSTR);
+extern LPWSTR build_directory_name(DWORD , ...);
+extern BOOL create_full_pathW(const WCHAR *path);
+extern BOOL ACTION_VerifyComponentForAction(MSICOMPONENT*, INSTALLSTATE);
+extern BOOL ACTION_VerifyFeatureForAction(MSIFEATURE*, INSTALLSTATE);
+extern void reduce_to_longfilename(WCHAR*);
+extern void reduce_to_shortfilename(WCHAR*);
+extern LPWSTR create_component_advertise_string(MSIPACKAGE*, MSICOMPONENT*, LPCWSTR);
+extern void ACTION_UpdateComponentStates(MSIPACKAGE *package, LPCWSTR szFeature);
+extern UINT register_unique_action(MSIPACKAGE *, LPCWSTR);
+extern BOOL check_unique_action(MSIPACKAGE *, LPCWSTR);
+extern WCHAR* generate_error_string(MSIPACKAGE *, UINT, DWORD, ... );
+extern UINT msi_create_component_directories( MSIPACKAGE *package );
+
+
+/* control event stuff */
+extern VOID ControlEvent_FireSubscribedEvent(MSIPACKAGE *package, LPCWSTR event,
+                                      MSIRECORD *data);
+extern VOID ControlEvent_CleanupSubscriptions(MSIPACKAGE *package);
+extern VOID ControlEvent_SubscribeToEvent(MSIPACKAGE *package, LPCWSTR event,
+                                   LPCWSTR control, LPCWSTR attribute);
+extern VOID ControlEvent_UnSubscribeToEvent( MSIPACKAGE *package, LPCWSTR event,
+                                      LPCWSTR control, LPCWSTR attribute );
+
+/* User Interface messages from the actions */
+extern void ui_progress(MSIPACKAGE *, int, int, int, int);
+extern void ui_actiondata(MSIPACKAGE *, LPCWSTR, MSIRECORD *);
+
+
+/* string consts use a number of places  and defined in helpers.c*/
+extern const WCHAR cszSourceDir[];
+extern const WCHAR szProductCode[];
+extern const WCHAR cszRootDrive[];
+extern const WCHAR cszbs[];
+
+#endif /* __MSI_ACTION_H__ */
diff --git a/reactos/dll/msi/appsearch.c b/reactos/dll/msi/appsearch.c
new file mode 100644 (file)
index 0000000..4c59c1a
--- /dev/null
@@ -0,0 +1,976 @@
+/*
+ * Implementation of the AppSearch action of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2005 Juan Lang
+ *
+ * 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 <stdarg.h>
+
+#define COBJMACROS
+
+#include "windef.h"
+#include "winbase.h"
+#include "winreg.h"
+#include "msi.h"
+#include "msiquery.h"
+#include "msidefs.h"
+#include "winver.h"
+#include "wine/unicode.h"
+#include "wine/debug.h"
+#include "msipriv.h"
+#include "action.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+typedef struct tagMSISIGNATURE
+{
+    LPWSTR   Name;     /* NOT owned by this structure */
+    LPWSTR   Property; /* NOT owned by this structure */
+    LPWSTR   File;
+    DWORD    MinVersionMS;
+    DWORD    MinVersionLS;
+    DWORD    MaxVersionMS;
+    DWORD    MaxVersionLS;
+    DWORD    MinSize;
+    DWORD    MaxSize;
+    FILETIME MinTime;
+    FILETIME MaxTime;
+    LPWSTR   Languages;
+}MSISIGNATURE;
+
+static void ACTION_VerStrToInteger(LPCWSTR verStr, PDWORD ms, PDWORD ls)
+{
+    const WCHAR *ptr;
+    int x1 = 0, x2 = 0, x3 = 0, x4 = 0;
+
+    x1 = atoiW(verStr);
+    ptr = strchrW(verStr, '.');
+    if (ptr)
+    {
+        x2 = atoiW(ptr + 1);
+        ptr = strchrW(ptr + 1, '.');
+    }
+    if (ptr)
+    {
+        x3 = atoiW(ptr + 1);
+        ptr = strchrW(ptr + 1, '.');
+    }
+    if (ptr)
+        x4 = atoiW(ptr + 1);
+    /* FIXME: byte-order dependent? */
+    *ms = x1 << 16 | x2;
+    *ls = x3 << 16 | x4;
+}
+
+/* Fills in sig with the the values from the Signature table, where name is the
+ * signature to find.  Upon return, sig->File will be NULL if the record is not
+ * found, and not NULL if it is found.
+ * Warning: clears all fields in sig!
+ * Returns ERROR_SUCCESS upon success (where not finding the record counts as
+ * success), something else on error.
+ */
+static UINT ACTION_AppSearchGetSignature(MSIPACKAGE *package, MSISIGNATURE *sig,
+ LPCWSTR name)
+{
+    MSIQUERY *view;
+    UINT rc;
+    static const WCHAR ExecSeqQuery[] =  {
+   's','e','l','e','c','t',' ','*',' ',
+   'f','r','o','m',' ',
+   'S','i','g','n','a','t','u','r','e',' ',
+   'w','h','e','r','e',' ','S','i','g','n','a','t','u','r','e',' ','=',' ',
+   '\'','%','s','\'',0};
+
+    TRACE("(package %p, sig %p)\n", package, sig);
+    memset(sig, 0, sizeof(*sig));
+    rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, name);
+    if (rc == ERROR_SUCCESS)
+    {
+        MSIRECORD *row = 0;
+        DWORD time;
+        WCHAR *minVersion, *maxVersion;
+
+        rc = MSI_ViewExecute(view, 0);
+        if (rc != ERROR_SUCCESS)
+        {
+            TRACE("MSI_ViewExecute returned %d\n", rc);
+            goto end;
+        }
+        rc = MSI_ViewFetch(view,&row);
+        if (rc != ERROR_SUCCESS)
+        {
+            TRACE("MSI_ViewFetch returned %d\n", rc);
+            rc = ERROR_SUCCESS;
+            goto end;
+        }
+
+        /* get properties */
+        sig->File = msi_dup_record_field(row,2);
+        minVersion = msi_dup_record_field(row,3);
+        if (minVersion)
+        {
+            ACTION_VerStrToInteger(minVersion, &sig->MinVersionMS,
+             &sig->MinVersionLS);
+            msi_free( minVersion);
+        }
+        maxVersion = msi_dup_record_field(row,4);
+        if (maxVersion)
+        {
+            ACTION_VerStrToInteger(maxVersion, &sig->MaxVersionMS,
+             &sig->MaxVersionLS);
+            msi_free( maxVersion);
+        }
+        sig->MinSize = MSI_RecordGetInteger(row,5);
+        if (sig->MinSize == MSI_NULL_INTEGER)
+            sig->MinSize = 0;
+        sig->MaxSize = MSI_RecordGetInteger(row,6);
+        if (sig->MaxSize == MSI_NULL_INTEGER)
+            sig->MaxSize = 0;
+        sig->Languages = msi_dup_record_field(row,9);
+        time = MSI_RecordGetInteger(row,7);
+        if (time != MSI_NULL_INTEGER)
+            DosDateTimeToFileTime(HIWORD(time), LOWORD(time), &sig->MinTime);
+        time = MSI_RecordGetInteger(row,8);
+        if (time != MSI_NULL_INTEGER)
+            DosDateTimeToFileTime(HIWORD(time), LOWORD(time), &sig->MaxTime);
+        TRACE("Found file name %s for Signature_ %s;\n",
+         debugstr_w(sig->File), debugstr_w(name));
+        TRACE("MinVersion is %d.%d.%d.%d\n", HIWORD(sig->MinVersionMS),
+         LOWORD(sig->MinVersionMS), HIWORD(sig->MinVersionLS),
+         LOWORD(sig->MinVersionLS));
+        TRACE("MaxVersion is %d.%d.%d.%d\n", HIWORD(sig->MaxVersionMS),
+         LOWORD(sig->MaxVersionMS), HIWORD(sig->MaxVersionLS),
+         LOWORD(sig->MaxVersionLS));
+        TRACE("MinSize is %ld, MaxSize is %ld;\n", sig->MinSize, sig->MaxSize);
+        TRACE("Languages is %s\n", debugstr_w(sig->Languages));
+
+end:
+        msiobj_release(&row->hdr);
+        MSI_ViewClose(view);
+        msiobj_release(&view->hdr);
+    }
+    else
+    {
+        TRACE("MSI_OpenQuery returned %d\n", rc);
+        rc = ERROR_SUCCESS;
+    }
+
+    TRACE("returning %d\n", rc);
+    return rc;
+}
+
+static UINT ACTION_AppSearchComponents(MSIPACKAGE *package, BOOL *appFound,
+ MSISIGNATURE *sig)
+{
+    MSIQUERY *view;
+    UINT rc;
+    static const WCHAR ExecSeqQuery[] =  {
+   's','e','l','e','c','t',' ','*',' ',
+   'f','r','o','m',' ',
+   'C','o','m','p','L','o','c','a','t','o','r',' ',
+   'w','h','e','r','e',' ','S','i','g','n','a','t','u','r','e','_',' ','=',' ',
+   '\'','%','s','\'',0};
+
+    TRACE("(package %p, appFound %p, sig %p)\n", package, appFound, sig);
+    *appFound = FALSE;
+    rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, sig->Name);
+    if (rc == ERROR_SUCCESS)
+    {
+        MSIRECORD *row = 0;
+        WCHAR guid[50];
+        DWORD sz;
+
+        rc = MSI_ViewExecute(view, 0);
+        if (rc != ERROR_SUCCESS)
+        {
+            TRACE("MSI_ViewExecute returned %d\n", rc);
+            goto end;
+        }
+        rc = MSI_ViewFetch(view,&row);
+        if (rc != ERROR_SUCCESS)
+        {
+            TRACE("MSI_ViewFetch returned %d\n", rc);
+            rc = ERROR_SUCCESS;
+            goto end;
+        }
+
+        /* get GUID */
+        guid[0] = 0;
+        sz=sizeof(guid)/sizeof(guid[0]);
+        rc = MSI_RecordGetStringW(row,2,guid,&sz);
+        if (rc != ERROR_SUCCESS)
+        {
+            ERR("Error is %x\n",rc);
+            goto end;
+        }
+        FIXME("AppSearch unimplemented for CompLocator table (GUID %s)\n",
+         debugstr_w(guid));
+
+end:
+        msiobj_release(&row->hdr);
+        MSI_ViewClose(view);
+        msiobj_release(&view->hdr);
+    }
+    else
+    {
+        TRACE("MSI_OpenQuery returned %d\n", rc);
+        rc = ERROR_SUCCESS;
+    }
+
+    TRACE("returning %d\n", rc);
+    return rc;
+}
+
+static UINT ACTION_AppSearchReg(MSIPACKAGE *package, BOOL *appFound,
+ MSISIGNATURE *sig)
+{
+    MSIQUERY *view;
+    UINT rc;
+    static const WCHAR ExecSeqQuery[] =  {
+   's','e','l','e','c','t',' ','*',' ',
+   'f','r','o','m',' ',
+   'R','e','g','L','o','c','a','t','o','r',' ',
+   'w','h','e','r','e',' ','S','i','g','n','a','t','u','r','e','_',' ','=',' ',
+   '\'','%','s','\'',0};
+    static const WCHAR dwordFmt[] = { '#','%','d','\0' };
+    static const WCHAR expandSzFmt[] = { '#','%','%','%','s','\0' };
+    static const WCHAR binFmt[] = { '#','x','%','x','\0' };
+
+    TRACE("(package %p, appFound %p, sig %p)\n", package, appFound, sig);
+    *appFound = FALSE;
+    rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, sig->Name);
+    if (rc == ERROR_SUCCESS)
+    {
+        MSIRECORD *row = 0;
+        LPWSTR keyPath = NULL, valueName = NULL, propertyValue = NULL;
+        int root, type;
+        HKEY rootKey, key = NULL;
+        DWORD sz = 0, regType, i;
+        LPBYTE value = NULL;
+
+        rc = MSI_ViewExecute(view, 0);
+        if (rc != ERROR_SUCCESS)
+        {
+            TRACE("MSI_ViewExecute returned %d\n", rc);
+            goto end;
+        }
+        rc = MSI_ViewFetch(view,&row);
+        if (rc != ERROR_SUCCESS)
+        {
+            TRACE("MSI_ViewFetch returned %d\n", rc);
+            rc = ERROR_SUCCESS;
+            goto end;
+        }
+
+        root = MSI_RecordGetInteger(row,2);
+        keyPath = msi_dup_record_field(row,3);
+        /* FIXME: keyPath needs to be expanded for properties */
+        valueName = msi_dup_record_field(row,4);
+        /* FIXME: valueName probably does too */
+        type = MSI_RecordGetInteger(row,5);
+
+        if ((type & 0x0f) != msidbLocatorTypeRawValue)
+        {
+            FIXME("AppSearch unimplemented for type %d (key path %s, value %s)\n",
+             type, debugstr_w(keyPath), debugstr_w(valueName));
+            goto end;
+        }
+
+        switch (root)
+        {
+            case msidbRegistryRootClassesRoot:
+                rootKey = HKEY_CLASSES_ROOT;
+                break;
+            case msidbRegistryRootCurrentUser:
+                rootKey = HKEY_CURRENT_USER;
+                break;
+            case msidbRegistryRootLocalMachine:
+                rootKey = HKEY_LOCAL_MACHINE;
+                break;
+            case msidbRegistryRootUsers:
+                rootKey = HKEY_USERS;
+                break;
+            default:
+                WARN("Unknown root key %d\n", root);
+                goto end;
+        }
+
+        rc = RegCreateKeyW(rootKey, keyPath, &key);
+        if (rc)
+        {
+            TRACE("RegCreateKeyW returned %d\n", rc);
+            rc = ERROR_SUCCESS;
+            goto end;
+        }
+        rc = RegQueryValueExW(key, valueName, NULL, NULL, NULL, &sz);
+        if (rc)
+        {
+            TRACE("RegQueryValueExW returned %d\n", rc);
+            rc = ERROR_SUCCESS;
+            goto end;
+        }
+        /* FIXME: sanity-check sz before allocating (is there an upper-limit
+         * on the value of a property?)
+         */
+        value = msi_alloc( sz);
+        rc = RegQueryValueExW(key, valueName, NULL, &regType, value, &sz);
+        if (rc)
+        {
+            TRACE("RegQueryValueExW returned %d\n", rc);
+            rc = ERROR_SUCCESS;
+            goto end;
+        }
+
+        /* bail out if the registry key is empty */
+        if (sz == 0)
+        {
+            rc = ERROR_SUCCESS;
+            goto end;
+        }
+        
+        switch (regType)
+        {
+            case REG_SZ:
+                if (*(LPWSTR)value == '#')
+                {
+                    /* escape leading pound with another */
+                    propertyValue = msi_alloc( sz + sizeof(WCHAR));
+                    propertyValue[0] = '#';
+                    strcpyW(propertyValue + 1, (LPWSTR)value);
+                }
+                else
+                {
+                    propertyValue = msi_alloc( sz);
+                    strcpyW(propertyValue, (LPWSTR)value);
+                }
+                break;
+            case REG_DWORD:
+                /* 7 chars for digits, 1 for NULL, 1 for #, and 1 for sign
+                 * char if needed
+                 */
+                propertyValue = msi_alloc( 10 * sizeof(WCHAR));
+                sprintfW(propertyValue, dwordFmt, *(DWORD *)value);
+                break;
+            case REG_EXPAND_SZ:
+                /* space for extra #% characters in front */
+                propertyValue = msi_alloc( sz + 2 * sizeof(WCHAR));
+                sprintfW(propertyValue, expandSzFmt, (LPWSTR)value);
+                break;
+            case REG_BINARY:
+                /* 3 == length of "#x<nibble>" */
+                propertyValue = msi_alloc( (sz * 3 + 1) * sizeof(WCHAR));
+                for (i = 0; i < sz; i++)
+                    sprintfW(propertyValue + i * 3, binFmt, value[i]);
+                break;
+            default:
+                WARN("unimplemented for values of type %ld\n", regType);
+                goto end;
+        }
+
+        TRACE("found registry value, setting %s to %s\n",
+         debugstr_w(sig->Property), debugstr_w(propertyValue));
+        rc = MSI_SetPropertyW(package, sig->Property, propertyValue);
+        *appFound = TRUE;
+
+end:
+        msi_free( propertyValue);
+        msi_free( value);
+        RegCloseKey(key);
+
+        msi_free( keyPath);
+        msi_free( valueName);
+
+        msiobj_release(&row->hdr);
+        MSI_ViewClose(view);
+        msiobj_release(&view->hdr);
+    }
+    else
+    {
+        TRACE("MSI_OpenQuery returned %d\n", rc);
+        rc = ERROR_SUCCESS;
+    }
+
+    TRACE("returning %d\n", rc);
+    return rc;
+}
+
+static UINT ACTION_AppSearchIni(MSIPACKAGE *package, BOOL *appFound,
+ MSISIGNATURE *sig)
+{
+    MSIQUERY *view;
+    UINT rc;
+    static const WCHAR ExecSeqQuery[] =  {
+   's','e','l','e','c','t',' ','*',' ',
+   'f','r','o','m',' ',
+   'I','n','i','L','o','c','a','t','o','r',' ',
+   'w','h','e','r','e',' ','S','i','g','n','a','t','u','r','e','_',' ','=',' ',
+   '\'','%','s','\'',0};
+
+    TRACE("(package %p, appFound %p, sig %p)\n", package, appFound, sig);
+    *appFound = FALSE;
+    rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, sig->Name);
+    if (rc == ERROR_SUCCESS)
+    {
+        MSIRECORD *row = 0;
+        LPWSTR fileName;
+
+        rc = MSI_ViewExecute(view, 0);
+        if (rc != ERROR_SUCCESS)
+        {
+            TRACE("MSI_ViewExecute returned %d\n", rc);
+            goto end;
+        }
+        rc = MSI_ViewFetch(view,&row);
+        if (rc != ERROR_SUCCESS)
+        {
+            TRACE("MSI_ViewFetch returned %d\n", rc);
+            rc = ERROR_SUCCESS;
+            goto end;
+        }
+
+        /* get file name */
+        fileName = msi_dup_record_field(row,2);
+        FIXME("AppSearch unimplemented for IniLocator (ini file name %s)\n",
+         debugstr_w(fileName));
+        msi_free( fileName);
+
+end:
+        msiobj_release(&row->hdr);
+        MSI_ViewClose(view);
+        msiobj_release(&view->hdr);
+    }
+    else
+    {
+        TRACE("MSI_OpenQuery returned %d\n", rc);
+        rc = ERROR_SUCCESS;
+    }
+
+
+    TRACE("returning %d\n", rc);
+    return rc;
+}
+
+/* Expands the value in src into a path without property names and only
+ * containing long path names into dst.  Replaces at most len characters of dst,
+ * and always NULL-terminates dst if dst is not NULL and len >= 1.
+ * May modify src.
+ * Assumes src and dst are non-overlapping.
+ * FIXME: return code probably needed:
+ * - what does AppSearch return if the table values are invalid?
+ * - what if dst is too small?
+ */
+static void ACTION_ExpandAnyPath(MSIPACKAGE *package, WCHAR *src, WCHAR *dst,
+ size_t len)
+{
+    WCHAR *ptr;
+    size_t copied = 0;
+
+    if (!src || !dst || !len)
+        return;
+
+    /* Ignore the short portion of the path, don't think we can use it anyway */
+    if ((ptr = strchrW(src, '|')))
+        ptr++;
+    else
+        ptr = src;
+    while (*ptr && copied < len - 1)
+    {
+        WCHAR *prop = strchrW(ptr, '[');
+
+        if (prop)
+        {
+            WCHAR *propEnd = strchrW(prop + 1, ']');
+
+            if (!propEnd)
+            {
+                WARN("Unterminated property name in AnyPath: %s\n",
+                 debugstr_w(prop));
+                break;
+            }
+            else
+            {
+                DWORD propLen;
+
+                *propEnd = 0;
+                propLen = len - copied - 1;
+                MSI_GetPropertyW(package, prop + 1, dst + copied, &propLen);
+                ptr = propEnd + 1;
+                copied += propLen;
+            }
+        }
+        else
+        {
+            size_t toCopy = min(strlenW(ptr) + 1, len - copied - 1);
+
+            memcpy(dst + copied, ptr, toCopy * sizeof(WCHAR));
+            ptr += toCopy;
+            copied += toCopy;
+        }
+    }
+    *(dst + copied) = '\0';
+}
+
+/* Sets *matches to whether the file (whose path is filePath) matches the
+ * versions set in sig.
+ * Return ERROR_SUCCESS in case of success (whether or not the file matches),
+ * something else if an install-halting error occurs.
+ */
+static UINT ACTION_FileVersionMatches(MSISIGNATURE *sig, LPCWSTR filePath,
+ BOOL *matches)
+{
+    UINT rc = ERROR_SUCCESS;
+
+    *matches = FALSE;
+    if (sig->Languages)
+    {
+        FIXME(": need to check version for languages %s\n",
+         debugstr_w(sig->Languages));
+    }
+    else
+    {
+        DWORD zero, size = GetFileVersionInfoSizeW((LPWSTR)filePath, &zero);
+
+        if (size)
+        {
+            LPVOID buf = msi_alloc( size);
+
+            if (buf)
+            {
+                static WCHAR rootW[] = { '\\',0 };
+                UINT versionLen;
+                LPVOID subBlock = NULL;
+
+                if (GetFileVersionInfoW((LPWSTR)filePath, 0, size, buf))
+                    VerQueryValueW(buf, (LPWSTR)rootW, &subBlock, &versionLen);
+                if (subBlock)
+                {
+                    VS_FIXEDFILEINFO *info =
+                     (VS_FIXEDFILEINFO *)subBlock;
+
+                    TRACE("Comparing file version %d.%d.%d.%d:\n",
+                     HIWORD(info->dwFileVersionMS),
+                     LOWORD(info->dwFileVersionMS),
+                     HIWORD(info->dwFileVersionLS),
+                     LOWORD(info->dwFileVersionLS));
+                    if (info->dwFileVersionMS < sig->MinVersionMS
+                     || (info->dwFileVersionMS == sig->MinVersionMS &&
+                     info->dwFileVersionLS < sig->MinVersionLS))
+                    {
+                        TRACE("Less than minimum version %d.%d.%d.%d\n",
+                         HIWORD(sig->MinVersionMS),
+                         LOWORD(sig->MinVersionMS),
+                         HIWORD(sig->MinVersionLS),
+                         LOWORD(sig->MinVersionLS));
+                    }
+                    else if (info->dwFileVersionMS < sig->MinVersionMS
+                     || (info->dwFileVersionMS == sig->MinVersionMS &&
+                     info->dwFileVersionLS < sig->MinVersionLS))
+                    {
+                        TRACE("Greater than minimum version %d.%d.%d.%d\n",
+                         HIWORD(sig->MaxVersionMS),
+                         LOWORD(sig->MaxVersionMS),
+                         HIWORD(sig->MaxVersionLS),
+                         LOWORD(sig->MaxVersionLS));
+                    }
+                    else
+                        *matches = TRUE;
+                }
+                msi_free( buf);
+            }
+            else
+                rc = ERROR_OUTOFMEMORY;
+        }
+    }
+    return rc;
+}
+
+/* Sets *matches to whether the file in findData matches that in sig.
+ * fullFilePath is assumed to be the full path of the file specified in
+ * findData, which may be necessary to compare the version.
+ * Return ERROR_SUCCESS in case of success (whether or not the file matches),
+ * something else if an install-halting error occurs.
+ */
+static UINT ACTION_FileMatchesSig(MSISIGNATURE *sig,
+ LPWIN32_FIND_DATAW findData, LPCWSTR fullFilePath, BOOL *matches)
+{
+    UINT rc = ERROR_SUCCESS;
+
+    *matches = TRUE;
+    /* assumes the caller has already ensured the filenames match, so check
+     * the other fields..
+     */
+    if (sig->MinTime.dwLowDateTime || sig->MinTime.dwHighDateTime)
+    {
+        if (findData->ftCreationTime.dwHighDateTime <
+         sig->MinTime.dwHighDateTime ||
+         (findData->ftCreationTime.dwHighDateTime == sig->MinTime.dwHighDateTime
+         && findData->ftCreationTime.dwLowDateTime <
+         sig->MinTime.dwLowDateTime))
+            *matches = FALSE;
+    }
+    if (*matches && (sig->MaxTime.dwLowDateTime || sig->MaxTime.dwHighDateTime))
+    {
+        if (findData->ftCreationTime.dwHighDateTime >
+         sig->MaxTime.dwHighDateTime ||
+         (findData->ftCreationTime.dwHighDateTime == sig->MaxTime.dwHighDateTime
+         && findData->ftCreationTime.dwLowDateTime >
+         sig->MaxTime.dwLowDateTime))
+            *matches = FALSE;
+    }
+    if (*matches && sig->MinSize && findData->nFileSizeLow < sig->MinSize)
+        *matches = FALSE;
+    if (*matches && sig->MaxSize && findData->nFileSizeLow > sig->MaxSize)
+        *matches = FALSE;
+    if (*matches && (sig->MinVersionMS || sig->MinVersionLS ||
+     sig->MaxVersionMS || sig->MaxVersionLS))
+        rc = ACTION_FileVersionMatches(sig, fullFilePath, matches);
+    return rc;
+}
+
+/* Recursively searches the directory dir for files that match the signature
+ * sig, up to (depth + 1) levels deep.  That is, if depth is 0, it searches dir
+ * (and only dir).  If depth is 1, searches dir and its immediate
+ * subdirectories.
+ * Assumes sig->File is not NULL.
+ * Returns ERROR_SUCCESS on success (which may include non-critical errors),
+ * something else on failures which should halt the install.
+ */
+static UINT ACTION_RecurseSearchDirectory(MSIPACKAGE *package, BOOL *appFound,
+ MSISIGNATURE *sig, LPCWSTR dir, int depth)
+{
+    static const WCHAR starDotStarW[] = { '*','.','*',0 };
+    UINT rc = ERROR_SUCCESS;
+    size_t dirLen = lstrlenW(dir), fileLen = lstrlenW(sig->File);
+    WCHAR *buf;
+
+    TRACE("Searching directory %s for file %s, depth %d\n", debugstr_w(dir),
+     debugstr_w(sig->File), depth);
+
+    if (depth < 0)
+        return ERROR_INVALID_PARAMETER;
+
+    *appFound = FALSE;
+    /* We need the buffer in both paths below, so go ahead and allocate it
+     * here.  Add two because we might need to add a backslash if the dir name
+     * isn't backslash-terminated.
+     */
+    buf = msi_alloc( (dirLen + max(fileLen, lstrlenW(starDotStarW)) + 2) * sizeof(WCHAR));
+    if (buf)
+    {
+        /* a depth of 0 implies we should search dir, so go ahead and search */
+        HANDLE hFind;
+        WIN32_FIND_DATAW findData;
+
+        memcpy(buf, dir, dirLen * sizeof(WCHAR));
+        if (buf[dirLen - 1] != '\\')
+            buf[dirLen++ - 1] = '\\';
+        memcpy(buf + dirLen, sig->File, (fileLen + 1) * sizeof(WCHAR));
+        hFind = FindFirstFileW(buf, &findData);
+        if (hFind != INVALID_HANDLE_VALUE)
+        {
+            BOOL matches;
+
+            /* assuming Signature can't contain wildcards for the file name,
+             * so don't bother with FindNextFileW here.
+             */
+            if (!(rc = ACTION_FileMatchesSig(sig, &findData, buf, &matches))
+             && matches)
+            {
+                TRACE("found file, setting %s to %s\n",
+                 debugstr_w(sig->Property), debugstr_w(buf));
+                rc = MSI_SetPropertyW(package, sig->Property, buf);
+                *appFound = TRUE;
+            }
+            FindClose(hFind);
+        }
+        if (rc == ERROR_SUCCESS && !*appFound && depth > 0)
+        {
+            HANDLE hFind;
+            WIN32_FIND_DATAW findData;
+
+            memcpy(buf, dir, dirLen * sizeof(WCHAR));
+            if (buf[dirLen - 1] != '\\')
+                buf[dirLen++ - 1] = '\\';
+            lstrcpyW(buf + dirLen, starDotStarW);
+            hFind = FindFirstFileW(buf, &findData);
+            if (hFind != INVALID_HANDLE_VALUE)
+            {
+                if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+                    rc = ACTION_RecurseSearchDirectory(package, appFound,
+                     sig, findData.cFileName, depth - 1);
+                while (rc == ERROR_SUCCESS && !*appFound &&
+                 FindNextFileW(hFind, &findData) != 0)
+                {
+                    if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+                        rc = ACTION_RecurseSearchDirectory(package, appFound,
+                         sig, findData.cFileName, depth - 1);
+                }
+                FindClose(hFind);
+            }
+        }
+        msi_free(buf);
+    }
+    else
+        rc = ERROR_OUTOFMEMORY;
+
+    return rc;
+}
+
+static UINT ACTION_CheckDirectory(MSIPACKAGE *package, MSISIGNATURE *sig,
+ LPCWSTR dir)
+{
+    UINT rc = ERROR_SUCCESS;
+
+    if (GetFileAttributesW(dir) & FILE_ATTRIBUTE_DIRECTORY)
+    {
+        TRACE("directory exists, setting %s to %s\n",
+         debugstr_w(sig->Property), debugstr_w(dir));
+        rc = MSI_SetPropertyW(package, sig->Property, dir);
+    }
+    return rc;
+}
+
+static BOOL ACTION_IsFullPath(LPCWSTR path)
+{
+    WCHAR first = toupperW(path[0]);
+    BOOL ret;
+
+    if (first >= 'A' && first <= 'Z' && path[1] == ':')
+        ret = TRUE;
+    else if (path[0] == '\\' && path[1] == '\\')
+        ret = TRUE;
+    else
+        ret = FALSE;
+    return ret;
+}
+
+static UINT ACTION_SearchDirectory(MSIPACKAGE *package, MSISIGNATURE *sig,
+ LPCWSTR expanded, int depth)
+{
+    UINT rc;
+    BOOL found;
+
+    TRACE("%p, %p, %s, %d\n", package, sig, debugstr_w(expanded), depth);
+    if (ACTION_IsFullPath(expanded))
+    {
+        if (sig->File)
+            rc = ACTION_RecurseSearchDirectory(package, &found, sig,
+             expanded, depth);
+        else
+        {
+            /* Recursively searching a directory makes no sense when the
+             * directory to search is the thing you're trying to find.
+             */
+            rc = ACTION_CheckDirectory(package, sig, expanded);
+        }
+    }
+    else
+    {
+        WCHAR pathWithDrive[MAX_PATH] = { 'C',':','\\',0 };
+        DWORD drives = GetLogicalDrives();
+        int i;
+
+        rc = ERROR_SUCCESS;
+        found = FALSE;
+        for (i = 0; rc == ERROR_SUCCESS && !found && i < 26; i++)
+            if (drives & (1 << drives))
+            {
+                pathWithDrive[0] = 'A' + i;
+                if (GetDriveTypeW(pathWithDrive) == DRIVE_FIXED)
+                {
+                    lstrcpynW(pathWithDrive + 3, expanded,
+                              sizeof(pathWithDrive) / sizeof(pathWithDrive[0]) - 3);
+                    if (sig->File)
+                        rc = ACTION_RecurseSearchDirectory(package, &found, sig,
+                         pathWithDrive, depth);
+                    else
+                        rc = ACTION_CheckDirectory(package, sig, pathWithDrive);
+                }
+            }
+    }
+    TRACE("returning %d\n", rc);
+    return rc;
+}
+
+static UINT ACTION_AppSearchDr(MSIPACKAGE *package, MSISIGNATURE *sig)
+{
+    MSIQUERY *view;
+    UINT rc;
+    static const WCHAR ExecSeqQuery[] =  {
+   's','e','l','e','c','t',' ','*',' ',
+   'f','r','o','m',' ',
+   'D','r','L','o','c','a','t','o','r',' ',
+   'w','h','e','r','e',' ','S','i','g','n','a','t','u','r','e','_',' ','=',' ',
+   '\'','%','s','\'',0};
+
+    TRACE("(package %p, sig %p)\n", package, sig);
+    rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, sig->Name);
+    if (rc == ERROR_SUCCESS)
+    {
+        MSIRECORD *row = 0;
+        WCHAR buffer[MAX_PATH], expanded[MAX_PATH];
+        DWORD sz;
+        int depth;
+
+        rc = MSI_ViewExecute(view, 0);
+        if (rc != ERROR_SUCCESS)
+        {
+            TRACE("MSI_ViewExecute returned %d\n", rc);
+            goto end;
+        }
+        rc = MSI_ViewFetch(view,&row);
+        if (rc != ERROR_SUCCESS)
+        {
+            TRACE("MSI_ViewFetch returned %d\n", rc);
+            rc = ERROR_SUCCESS;
+            goto end;
+        }
+
+        /* check whether parent is set */
+        buffer[0] = 0;
+        sz=sizeof(buffer)/sizeof(buffer[0]);
+        rc = MSI_RecordGetStringW(row,2,buffer,&sz);
+        if (rc != ERROR_SUCCESS)
+        {
+            ERR("Error is %x\n",rc);
+            goto end;
+        }
+        else if (buffer[0])
+        {
+            FIXME(": searching parent (%s) unimplemented\n",
+             debugstr_w(buffer));
+            goto end;
+        }
+        /* no parent, now look for path */
+        buffer[0] = 0;
+        sz=sizeof(buffer)/sizeof(buffer[0]);
+        rc = MSI_RecordGetStringW(row,3,buffer,&sz);
+        if (rc != ERROR_SUCCESS)
+        {
+            ERR("Error is %x\n",rc);
+            goto end;
+        }
+        if (MSI_RecordIsNull(row,4))
+            depth = 0;
+        else
+            depth = MSI_RecordGetInteger(row,4);
+        ACTION_ExpandAnyPath(package, buffer, expanded,
+         sizeof(expanded) / sizeof(expanded[0]));
+        rc = ACTION_SearchDirectory(package, sig, expanded, depth);
+
+end:
+        msiobj_release(&row->hdr);
+        MSI_ViewClose(view);
+        msiobj_release(&view->hdr);
+    }
+    else
+    {
+        TRACE("MSI_OpenQuery returned %d\n", rc);
+        rc = ERROR_SUCCESS;
+    }
+
+
+    TRACE("returning %d\n", rc);
+    return rc;
+}
+
+/* http://msdn.microsoft.com/library/en-us/msi/setup/appsearch_table.asp
+ * is the best reference for the AppSearch table and how it's used.
+ */
+UINT ACTION_AppSearch(MSIPACKAGE *package)
+{
+    MSIQUERY *view;
+    UINT rc;
+    static const WCHAR ExecSeqQuery[] =  {
+   's','e','l','e','c','t',' ','*',' ',
+   'f','r','o','m',' ',
+   'A','p','p','S','e','a','r','c','h',0};
+
+    rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery);
+    if (rc == ERROR_SUCCESS)
+    {
+        MSIRECORD *row = 0;
+        WCHAR propBuf[0x100], sigBuf[0x100];
+        DWORD sz;
+        MSISIGNATURE sig;
+        BOOL appFound = FALSE;
+
+        rc = MSI_ViewExecute(view, 0);
+        if (rc != ERROR_SUCCESS)
+            goto end;
+
+        while (!rc)
+        {
+            rc = MSI_ViewFetch(view,&row);
+            if (rc != ERROR_SUCCESS)
+            {
+                rc = ERROR_SUCCESS;
+                break;
+            }
+
+            /* get property and signature */
+            propBuf[0] = 0;
+            sz=sizeof(propBuf)/sizeof(propBuf[0]);
+            rc = MSI_RecordGetStringW(row,1,propBuf,&sz);
+            if (rc != ERROR_SUCCESS)
+            {
+                ERR("Error is %x\n",rc);
+                msiobj_release(&row->hdr);
+                break;
+            }
+            sigBuf[0] = 0;
+            sz=sizeof(sigBuf)/sizeof(sigBuf[0]);
+            rc = MSI_RecordGetStringW(row,2,sigBuf,&sz);
+            if (rc != ERROR_SUCCESS)
+            {
+                ERR("Error is %x\n",rc);
+                msiobj_release(&row->hdr);
+                break;
+            }
+            TRACE("Searching for Property %s, Signature_ %s\n",
+             debugstr_w(propBuf), debugstr_w(sigBuf));
+            /* This clears all the fields, so set Name and Property afterward */
+            rc = ACTION_AppSearchGetSignature(package, &sig, sigBuf);
+            sig.Name = sigBuf;
+            sig.Property = propBuf;
+            if (rc == ERROR_SUCCESS)
+            {
+                rc = ACTION_AppSearchComponents(package, &appFound, &sig);
+                if (rc == ERROR_SUCCESS && !appFound)
+                {
+                    rc = ACTION_AppSearchReg(package, &appFound, &sig);
+                    if (rc == ERROR_SUCCESS && !appFound)
+                    {
+                        rc = ACTION_AppSearchIni(package, &appFound, &sig);
+                        if (rc == ERROR_SUCCESS && !appFound)
+                            rc = ACTION_AppSearchDr(package, &sig);
+                    }
+                }
+            }
+            msi_free( sig.File);
+            msi_free( sig.Languages);
+            msiobj_release(&row->hdr);
+        }
+
+end:
+        MSI_ViewClose(view);
+        msiobj_release(&view->hdr);
+    }
+    else
+        rc = ERROR_SUCCESS;
+
+    return rc;
+}
diff --git a/reactos/dll/msi/classes.c b/reactos/dll/msi/classes.c
new file mode 100644 (file)
index 0000000..b7799f3
--- /dev/null
@@ -0,0 +1,1368 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2005 Aric Stewart for CodeWeavers
+ *
+ * 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
+ */
+
+/* actions handled in this module
+ * RegisterClassInfo
+ * RegisterProgIdInfo
+ * RegisterExtensionInfo
+ * RegisterMIMEInfo
+ * UnRegisterClassInfo (TODO)
+ * UnRegisterProgIdInfo (TODO)
+ * UnRegisterExtensionInfo (TODO)
+ * UnRegisterMIMEInfo (TODO)
+ */
+
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "winreg.h"
+#include "wine/debug.h"
+#include "msipriv.h"
+#include "winuser.h"
+#include "wine/unicode.h"
+#include "action.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+
+extern const WCHAR szRegisterClassInfo[];
+extern const WCHAR szRegisterProgIdInfo[];
+extern const WCHAR szRegisterExtensionInfo[];
+extern const WCHAR szRegisterMIMEInfo[];
+
+extern const WCHAR szUnregisterClassInfo[];
+extern const WCHAR szUnregisterExtensionInfo[];
+extern const WCHAR szUnregisterMIMEInfo[];
+extern const WCHAR szUnregisterProgIdInfo[];
+
+static MSIAPPID *load_appid( MSIPACKAGE* package, MSIRECORD *row )
+{
+    LPCWSTR buffer;
+    MSIAPPID *appid;
+
+    /* fill in the data */
+
+    appid = msi_alloc_zero( sizeof(MSIAPPID) );
+    if (!appid)
+        return NULL;
+    
+    appid->AppID = msi_dup_record_field( row, 1 );
+    TRACE("loading appid %s\n", debugstr_w( appid->AppID ));
+
+    buffer = MSI_RecordGetString(row,2);
+    deformat_string( package, buffer, &appid->RemoteServerName );
+
+    appid->LocalServer = msi_dup_record_field(row,3);
+    appid->ServiceParameters = msi_dup_record_field(row,4);
+    appid->DllSurrogate = msi_dup_record_field(row,5);
+
+    appid->ActivateAtStorage = !MSI_RecordIsNull(row,6);
+    appid->RunAsInteractiveUser = !MSI_RecordIsNull(row,7);
+
+    list_add_tail( &package->appids, &appid->entry );
+    
+    return appid;
+}
+
+static MSIAPPID *load_given_appid( MSIPACKAGE *package, LPCWSTR name )
+{
+    MSIRECORD *row;
+    MSIAPPID *appid;
+    static const WCHAR ExecSeqQuery[] =
+        {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
+         '`','A','p','p','I','d','`',' ','W','H','E','R','E',' ',
+         '`','A','p','p','I','d','`',' ','=',' ','\'','%','s','\'',0};
+
+    if (!name)
+        return NULL;
+
+    /* check for appids already loaded */
+    LIST_FOR_EACH_ENTRY( appid, &package->appids, MSIAPPID, entry )
+    {
+        if (lstrcmpiW( appid->AppID, name )==0)
+        {
+            TRACE("found appid %s %p\n", debugstr_w(name), appid);
+            return appid;
+        }
+    }
+    
+    row = MSI_QueryGetRecord(package->db, ExecSeqQuery, name);
+    if (!row)
+        return NULL;
+
+    appid = load_appid(package, row);
+    msiobj_release(&row->hdr);
+
+    return appid;
+}
+
+static MSIPROGID *load_given_progid(MSIPACKAGE *package, LPCWSTR progid);
+static MSICLASS *load_given_class( MSIPACKAGE *package, LPCWSTR classid );
+
+static MSIPROGID *load_progid( MSIPACKAGE* package, MSIRECORD *row )
+{
+    MSIPROGID *progid;
+    LPCWSTR buffer;
+
+    /* fill in the data */
+
+    progid = msi_alloc_zero( sizeof(MSIPROGID) );
+    if (!progid)
+        return NULL;
+
+    list_add_tail( &package->progids, &progid->entry );
+
+    progid->ProgID = msi_dup_record_field(row,1);
+    TRACE("loading progid %s\n",debugstr_w(progid->ProgID));
+
+    buffer = MSI_RecordGetString(row,2);
+    progid->Parent = load_given_progid(package,buffer);
+    if (progid->Parent == NULL && buffer)
+        FIXME("Unknown parent ProgID %s\n",debugstr_w(buffer));
+
+    buffer = MSI_RecordGetString(row,3);
+    progid->Class = load_given_class(package,buffer);
+    if (progid->Class == NULL && buffer)
+        FIXME("Unknown class %s\n",debugstr_w(buffer));
+
+    progid->Description = msi_dup_record_field(row,4);
+
+    if (!MSI_RecordIsNull(row,6))
+    {
+        INT icon_index = MSI_RecordGetInteger(row,6); 
+        LPCWSTR FileName = MSI_RecordGetString(row,5);
+        LPWSTR FilePath;
+        static const WCHAR fmt[] = {'%','s',',','%','i',0};
+
+        FilePath = build_icon_path(package,FileName);
+       
+        progid->IconPath = msi_alloc( (strlenW(FilePath)+10)* sizeof(WCHAR) );
+
+        sprintfW(progid->IconPath,fmt,FilePath,icon_index);
+
+        msi_free(FilePath);
+    }
+    else
+    {
+        buffer = MSI_RecordGetString(row,5);
+        if (buffer)
+            progid->IconPath = build_icon_path(package,buffer);
+    }
+
+    progid->CurVer = NULL;
+    progid->VersionInd = NULL;
+
+    /* if we have a parent then we may be that parents CurVer */
+    if (progid->Parent && progid->Parent != progid)
+    {
+        MSIPROGID *parent = progid->Parent;
+
+        while (parent->Parent && parent->Parent != parent)
+            parent = parent->Parent;
+
+        /* FIXME: need to determing if we are really the CurVer */
+
+        progid->CurVer = parent;
+        parent->VersionInd = progid;
+    }
+    
+    return progid;
+}
+
+static MSIPROGID *load_given_progid(MSIPACKAGE *package, LPCWSTR name)
+{
+    MSIPROGID *progid;
+    MSIRECORD *row;
+    static const WCHAR ExecSeqQuery[] =
+        {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
+         '`','P','r','o','g','I','d','`',' ','W','H','E','R','E',' ',
+         '`','P','r','o','g','I','d','`',' ','=',' ','\'','%','s','\'',0};
+
+    if (!name)
+        return NULL;
+
+    /* check for progids already loaded */
+    LIST_FOR_EACH_ENTRY( progid, &package->progids, MSIPROGID, entry )
+    {
+        if (strcmpiW( progid->ProgID,name )==0)
+        {
+            TRACE("found progid %s (%p)\n",debugstr_w(name), progid );
+            return progid;
+        }
+    }
+    
+    row = MSI_QueryGetRecord( package->db, ExecSeqQuery, name );
+    if (!row)
+        return NULL;
+
+    progid = load_progid(package, row);
+    msiobj_release(&row->hdr);
+
+    return progid;
+}
+
+static MSICLASS *load_class( MSIPACKAGE* package, MSIRECORD *row )
+{
+    MSICLASS *cls;
+    DWORD i;
+    LPCWSTR buffer;
+
+    /* fill in the data */
+
+    cls = msi_alloc_zero( sizeof(MSICLASS) );
+    if (!cls)
+        return NULL;
+
+    list_add_tail( &package->classes, &cls->entry );
+
+    cls->clsid = msi_dup_record_field( row, 1 );
+    TRACE("loading class %s\n",debugstr_w(cls->clsid));
+    cls->Context = msi_dup_record_field( row, 2 );
+    buffer = MSI_RecordGetString(row,3);
+    cls->Component = get_loaded_component(package, buffer);
+
+    cls->ProgIDText = msi_dup_record_field(row,4);
+    cls->ProgID = load_given_progid(package, cls->ProgIDText);
+
+    cls->Description = msi_dup_record_field(row,5);
+
+    buffer = MSI_RecordGetString(row,6);
+    if (buffer)
+        cls->AppID = load_given_appid(package, buffer);
+
+    cls->FileTypeMask = msi_dup_record_field(row,7);
+
+    if (!MSI_RecordIsNull(row,9))
+    {
+
+        INT icon_index = MSI_RecordGetInteger(row,9); 
+        LPCWSTR FileName = MSI_RecordGetString(row,8);
+        LPWSTR FilePath;
+        static const WCHAR fmt[] = {'%','s',',','%','i',0};
+
+        FilePath = build_icon_path(package,FileName);
+       
+        cls->IconPath = msi_alloc( (strlenW(FilePath)+5)* sizeof(WCHAR) );
+
+        sprintfW(cls->IconPath,fmt,FilePath,icon_index);
+
+        msi_free(FilePath);
+    }
+    else
+    {
+        buffer = MSI_RecordGetString(row,8);
+        if (buffer)
+            cls->IconPath = build_icon_path(package,buffer);
+    }
+
+    if (!MSI_RecordIsNull(row,10))
+    {
+        i = MSI_RecordGetInteger(row,10);
+        if (i != MSI_NULL_INTEGER && i > 0 &&  i < 4)
+        {
+            static const WCHAR ole2[] = {'o','l','e','2','.','d','l','l',0};
+            static const WCHAR ole32[] = {'o','l','e','3','2','.','d','l','l',0};
+
+            switch(i)
+            {
+                case 1:
+                    cls->DefInprocHandler = strdupW(ole2);
+                    break;
+                case 2:
+                    cls->DefInprocHandler32 = strdupW(ole32);
+                    break;
+                case 3:
+                    cls->DefInprocHandler = strdupW(ole2);
+                    cls->DefInprocHandler32 = strdupW(ole32);
+                    break;
+            }
+        }
+        else
+        {
+            cls->DefInprocHandler32 = msi_dup_record_field( row, 10);
+            reduce_to_longfilename(cls->DefInprocHandler32);
+        }
+    }
+    buffer = MSI_RecordGetString(row,11);
+    deformat_string(package,buffer,&cls->Argument);
+
+    buffer = MSI_RecordGetString(row,12);
+    cls->Feature = get_loaded_feature(package,buffer);
+
+    cls->Attributes = MSI_RecordGetInteger(row,13);
+    
+    return cls;
+}
+
+/*
+ * the Class table has 3 primary keys. Generally it is only 
+ * referenced through the first CLSID key. However when loading
+ * all of the classes we need to make sure we do not ignore rows
+ * with other Context and ComponentIndexs 
+ */
+static MSICLASS *load_given_class(MSIPACKAGE *package, LPCWSTR classid)
+{
+    MSICLASS *cls;
+    MSIRECORD *row;
+    static const WCHAR ExecSeqQuery[] =
+        {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
+         '`','C','l','a','s','s','`',' ','W','H','E','R','E',' ',
+         '`','C','L','S','I','D','`',' ','=',' ','\'','%','s','\'',0};
+
+    if (!classid)
+        return NULL;
+    
+    /* check for classes already loaded */
+    LIST_FOR_EACH_ENTRY( cls, &package->classes, MSICLASS, entry )
+    {
+        if (lstrcmpiW( cls->clsid, classid )==0)
+        {
+            TRACE("found class %s (%p)\n",debugstr_w(classid), cls);
+            return cls;
+        }
+    }
+
+    row = MSI_QueryGetRecord(package->db, ExecSeqQuery, classid);
+    if (!row)
+        return NULL;
+
+    cls = load_class(package, row);
+    msiobj_release(&row->hdr);
+
+    return cls;
+}
+
+static MSIEXTENSION *load_given_extension( MSIPACKAGE *package, LPCWSTR extension );
+
+static MSIMIME *load_mime( MSIPACKAGE* package, MSIRECORD *row )
+{
+    LPCWSTR buffer;
+    MSIMIME *mt;
+
+    /* fill in the data */
+
+    mt = msi_alloc_zero( sizeof(MSIMIME) );
+    if (!mt)
+        return mt;
+
+    mt->ContentType = msi_dup_record_field( row, 1 ); 
+    TRACE("loading mime %s\n", debugstr_w(mt->ContentType));
+
+    buffer = MSI_RecordGetString( row, 2 );
+    mt->Extension = load_given_extension( package, buffer );
+
+    mt->clsid = msi_dup_record_field( row, 3 );
+    mt->Class = load_given_class( package, mt->clsid );
+
+    list_add_tail( &package->mimes, &mt->entry );
+
+    return mt;
+}
+
+static MSIMIME *load_given_mime( MSIPACKAGE *package, LPCWSTR mime )
+{
+    MSIRECORD *row;
+    static const WCHAR ExecSeqQuery[] =
+        {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
+         '`','M','I','M','E','`',' ','W','H','E','R','E',' ',
+         '`','C','o','n','t','e','n','t','T','y','p','e','`',' ','=',' ',
+         '\'','%','s','\'',0};
+    MSIMIME *mt;
+
+    if (!mime)
+        return NULL;
+    
+    /* check for mime already loaded */
+    LIST_FOR_EACH_ENTRY( mt, &package->mimes, MSIMIME, entry )
+    {
+        if (strcmpiW(mt->ContentType,mime)==0)
+        {
+            TRACE("found mime %s (%p)\n",debugstr_w(mime), mt);
+            return mt;
+        }
+    }
+    
+    row = MSI_QueryGetRecord(package->db, ExecSeqQuery, mime);
+    if (!row)
+        return NULL;
+
+    mt = load_mime(package, row);
+    msiobj_release(&row->hdr);
+
+    return mt;
+}
+
+static MSIEXTENSION *load_extension( MSIPACKAGE* package, MSIRECORD *row )
+{
+    MSIEXTENSION *ext;
+    LPCWSTR buffer;
+
+    /* fill in the data */
+
+    ext = msi_alloc_zero( sizeof(MSIEXTENSION) );
+    if (!ext)
+        return NULL;
+
+    list_init( &ext->verbs );
+
+    list_add_tail( &package->extensions, &ext->entry );
+
+    ext->Extension = msi_dup_record_field( row, 1 );
+    TRACE("loading extension %s\n", debugstr_w(ext->Extension));
+
+    buffer = MSI_RecordGetString( row, 2 );
+    ext->Component = get_loaded_component( package,buffer );
+
+    ext->ProgIDText = msi_dup_record_field( row, 3 );
+    ext->ProgID = load_given_progid( package, ext->ProgIDText );
+
+    buffer = MSI_RecordGetString( row, 4 );
+    ext->Mime = load_given_mime( package, buffer );
+
+    buffer = MSI_RecordGetString(row,5);
+    ext->Feature = get_loaded_feature( package, buffer );
+
+    return ext;
+}
+
+/*
+ * While the extension table has 2 primary keys, this function is only looking
+ * at the Extension key which is what is referenced as a forign key 
+ */
+static MSIEXTENSION *load_given_extension( MSIPACKAGE *package, LPCWSTR name )
+{
+    MSIRECORD *row;
+    MSIEXTENSION *ext;
+    static const WCHAR ExecSeqQuery[] =
+        {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
+         '`','E','x','t','e','n','s','i','o','n','`',' ',
+         'W','H','E','R','E',' ',
+         '`','E','x','t','e','n','s','i','o','n','`',' ','=',' ',
+         '\'','%','s','\'',0};
+
+    if (!name)
+        return NULL;
+
+    /* check for extensions already loaded */
+    LIST_FOR_EACH_ENTRY( ext, &package->extensions, MSIEXTENSION, entry )
+    {
+        if (strcmpiW( ext->Extension, name )==0)
+        {
+            TRACE("extension %s already loaded %p\n", debugstr_w(name), ext);
+            return ext;
+        }
+    }
+    
+    row = MSI_QueryGetRecord( package->db, ExecSeqQuery, name );
+    if (!row)
+        return NULL;
+
+    ext = load_extension(package, row);
+    msiobj_release(&row->hdr);
+
+    return ext;
+}
+
+static UINT iterate_load_verb(MSIRECORD *row, LPVOID param)
+{
+    MSIPACKAGE* package = (MSIPACKAGE*)param;
+    MSIVERB *verb;
+    LPCWSTR buffer;
+    MSIEXTENSION *extension;
+
+    buffer = MSI_RecordGetString(row,1);
+    extension = load_given_extension( package, buffer );
+    if (!extension)
+    {
+        ERR("Verb unable to find loaded extension %s\n", debugstr_w(buffer));
+        return ERROR_SUCCESS;
+    }
+
+    /* fill in the data */
+
+    verb = msi_alloc_zero( sizeof(MSIVERB) );
+    if (!verb)
+        return ERROR_OUTOFMEMORY;
+
+    verb->Verb = msi_dup_record_field(row,2);
+    TRACE("loading verb %s\n",debugstr_w(verb->Verb));
+    verb->Sequence = MSI_RecordGetInteger(row,3);
+
+    buffer = MSI_RecordGetString(row,4);
+    deformat_string(package,buffer,&verb->Command);
+
+    buffer = MSI_RecordGetString(row,5);
+    deformat_string(package,buffer,&verb->Argument);
+
+    /* assosiate the verb with the correct extension */
+    list_add_tail( &extension->verbs, &verb->entry );
+    
+    return ERROR_SUCCESS;
+}
+
+static UINT iterate_all_classes(MSIRECORD *rec, LPVOID param)
+{
+    MSICOMPONENT *comp;
+    LPCWSTR clsid;
+    LPCWSTR context;
+    LPCWSTR buffer;
+    MSIPACKAGE* package =(MSIPACKAGE*)param;
+    MSICLASS *cls;
+    BOOL match = FALSE;
+
+    clsid = MSI_RecordGetString(rec,1);
+    context = MSI_RecordGetString(rec,2);
+    buffer = MSI_RecordGetString(rec,3);
+    comp = get_loaded_component(package,buffer);
+
+    LIST_FOR_EACH_ENTRY( cls, &package->classes, MSICLASS, entry )
+    {
+        if (strcmpiW( clsid, cls->clsid ))
+            continue;
+        if (strcmpW( context, cls->Context ))
+            continue;
+        if (comp == cls->Component)
+        {
+            match = TRUE;
+            break;
+        }
+    }
+    
+    if (!match)
+        load_class(package, rec);
+
+    return ERROR_SUCCESS;
+}
+
+static VOID load_all_classes(MSIPACKAGE *package)
+{
+    UINT rc = ERROR_SUCCESS;
+    MSIQUERY *view;
+
+    static const WCHAR ExecSeqQuery[] =
+        {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
+         '`','C','l','a','s','s','`',0};
+
+    rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
+    if (rc != ERROR_SUCCESS)
+        return;
+
+    rc = MSI_IterateRecords(view, NULL, iterate_all_classes, package);
+    msiobj_release(&view->hdr);
+}
+
+static UINT iterate_all_extensions(MSIRECORD *rec, LPVOID param)
+{
+    MSICOMPONENT *comp;
+    LPCWSTR buffer;
+    LPCWSTR extension;
+    MSIPACKAGE* package =(MSIPACKAGE*)param;
+    BOOL match = FALSE;
+    MSIEXTENSION *ext;
+
+    extension = MSI_RecordGetString(rec,1);
+    buffer = MSI_RecordGetString(rec,2);
+    comp = get_loaded_component(package,buffer);
+
+    LIST_FOR_EACH_ENTRY( ext, &package->extensions, MSIEXTENSION, entry )
+    {
+        if (strcmpiW(extension,ext->Extension))
+            continue;
+        if (comp == ext->Component)
+        {
+            match = TRUE;
+            break;
+        }
+    }
+
+    if (!match)
+        load_extension(package, rec);
+
+    return ERROR_SUCCESS;
+}
+
+static VOID load_all_extensions(MSIPACKAGE *package)
+{
+    UINT rc = ERROR_SUCCESS;
+    MSIQUERY *view;
+
+    static const WCHAR ExecSeqQuery[] =
+        {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
+         '`','E','x','t','e','n','s','i','o','n','`',0};
+
+    rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
+    if (rc != ERROR_SUCCESS)
+        return;
+
+    rc = MSI_IterateRecords(view, NULL, iterate_all_extensions, package);
+    msiobj_release(&view->hdr);
+}
+
+static UINT iterate_all_progids(MSIRECORD *rec, LPVOID param)
+{
+    LPCWSTR buffer;
+    MSIPACKAGE* package =(MSIPACKAGE*)param;
+
+    buffer = MSI_RecordGetString(rec,1);
+    load_given_progid(package,buffer);
+    return ERROR_SUCCESS;
+}
+
+static VOID load_all_progids(MSIPACKAGE *package)
+{
+    UINT rc = ERROR_SUCCESS;
+    MSIQUERY *view;
+
+    static const WCHAR ExecSeqQuery[] =
+        {'S','E','L','E','C','T',' ','`','P','r','o','g','I','d','`',' ',
+         'F','R','O','M',' ', '`','P','r','o','g','I','d','`',0};
+
+    rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
+    if (rc != ERROR_SUCCESS)
+        return;
+
+    rc = MSI_IterateRecords(view, NULL, iterate_all_progids, package);
+    msiobj_release(&view->hdr);
+}
+
+static VOID load_all_verbs(MSIPACKAGE *package)
+{
+    UINT rc = ERROR_SUCCESS;
+    MSIQUERY *view;
+
+    static const WCHAR ExecSeqQuery[] =
+        {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
+         '`','V','e','r','b','`',0};
+
+    rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
+    if (rc != ERROR_SUCCESS)
+        return;
+
+    rc = MSI_IterateRecords(view, NULL, iterate_load_verb, package);
+    msiobj_release(&view->hdr);
+}
+
+static UINT iterate_all_mimes(MSIRECORD *rec, LPVOID param)
+{
+    LPCWSTR buffer;
+    MSIPACKAGE* package =(MSIPACKAGE*)param;
+
+    buffer = MSI_RecordGetString(rec,1);
+    load_given_mime(package,buffer);
+    return ERROR_SUCCESS;
+}
+
+static VOID load_all_mimes(MSIPACKAGE *package)
+{
+    UINT rc = ERROR_SUCCESS;
+    MSIQUERY *view;
+
+    static const WCHAR ExecSeqQuery[] =
+        {'S','E','L','E','C','T',' ',
+         '`','C','o','n','t','e','n','t','T','y','p','e','`',
+         ' ','F','R','O','M',' ',
+         '`','M','I','M','E','`',0};
+
+    rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
+    if (rc != ERROR_SUCCESS)
+        return;
+
+    rc = MSI_IterateRecords(view, NULL, iterate_all_mimes, package);
+    msiobj_release(&view->hdr);
+}
+
+static void load_classes_and_such(MSIPACKAGE *package)
+{
+    TRACE("Loading all the class info and related tables\n");
+
+    /* check if already loaded */
+    if (!list_empty( &package->classes ) ||
+        !list_empty( &package->mimes ) ||
+        !list_empty( &package->extensions ) ||
+        !list_empty( &package->progids ) )
+        return;
+
+    load_all_classes(package);
+    load_all_extensions(package);
+    load_all_progids(package);
+    /* these loads must come after the other loads */
+    load_all_verbs(package);
+    load_all_mimes(package);
+}
+
+static void mark_progid_for_install( MSIPACKAGE* package, MSIPROGID *progid )
+{
+    MSIPROGID *child;
+
+    if (!progid)
+        return;
+
+    if (progid->InstallMe == TRUE)
+        return;
+
+    progid->InstallMe = TRUE;
+
+    /* all children if this is a parent also install */
+    LIST_FOR_EACH_ENTRY( child, &package->progids, MSIPROGID, entry )
+    {
+        if (child->Parent == progid)
+            mark_progid_for_install( package, child );
+    }
+}
+
+static void mark_mime_for_install( MSIMIME *mime )
+{
+    if (!mime)
+        return;
+    mime->InstallMe = TRUE;
+}
+
+LONG msi_reg_set_val_str( HKEY hkey, LPCWSTR name, LPCWSTR value )
+{
+    DWORD len = value ? (lstrlenW(value) + 1) * sizeof (WCHAR) : 0;
+    return RegSetValueExW( hkey, name, 0, REG_SZ, (LPBYTE)value, len );
+}
+
+LONG msi_reg_set_val_multi_str( HKEY hkey, LPCWSTR name, LPCWSTR value )
+{
+    LPCWSTR p = value;
+    while (*p) p += lstrlenW(p) + 1;
+    return RegSetValueExW( hkey, name, 0, REG_MULTI_SZ,
+                           (LPBYTE)value, (p + 1 - value) * sizeof(WCHAR) );
+}
+
+LONG msi_reg_set_val_dword( HKEY hkey, LPCWSTR name, DWORD val )
+{
+    return RegSetValueExW( hkey, name, 0, REG_DWORD, (LPBYTE)&val, sizeof (DWORD) );
+}
+
+LONG msi_reg_set_subkey_val( HKEY hkey, LPCWSTR path, LPCWSTR name, LPCWSTR val )
+{
+    HKEY hsubkey = 0;
+    LONG r;
+
+    r = RegCreateKeyW( hkey, path, &hsubkey );
+    if (r != ERROR_SUCCESS)
+        return r;
+    r = msi_reg_set_val_str( hsubkey, name, val );
+    RegCloseKey( hsubkey );
+    return r;
+}
+
+static UINT register_appid(MSIAPPID *appid, LPCWSTR app )
+{
+    static const WCHAR szAppID[] = { 'A','p','p','I','D',0 };
+    static const WCHAR szRemoteServerName[] =
+         {'R','e','m','o','t','e','S','e','r','v','e','r','N','a','m','e',0};
+    static const WCHAR szLocalService[] =
+         {'L','o','c','a','l','S','e','r','v','i','c','e',0};
+    static const WCHAR szService[] =
+         {'S','e','r','v','i','c','e','P','a','r','a','m','e','t','e','r','s',0};
+    static const WCHAR szDLL[] =
+         {'D','l','l','S','u','r','r','o','g','a','t','e',0};
+    static const WCHAR szActivate[] =
+         {'A','c','t','i','v','a','t','e','A','s','S','t','o','r','a','g','e',0};
+    static const WCHAR szY[] = {'Y',0};
+    static const WCHAR szRunAs[] = {'R','u','n','A','s',0};
+    static const WCHAR szUser[] = 
+         {'I','n','t','e','r','a','c','t','i','v','e',' ','U','s','e','r',0};
+
+    HKEY hkey2,hkey3;
+
+    RegCreateKeyW(HKEY_CLASSES_ROOT,szAppID,&hkey2);
+    RegCreateKeyW( hkey2, appid->AppID, &hkey3 );
+    RegCloseKey(hkey2);
+    msi_reg_set_val_str( hkey3, NULL, app );
+
+    if (appid->RemoteServerName)
+        msi_reg_set_val_str( hkey3, szRemoteServerName, appid->RemoteServerName );
+
+    if (appid->LocalServer)
+        msi_reg_set_val_str( hkey3, szLocalService, appid->LocalServer );
+
+    if (appid->ServiceParameters)
+        msi_reg_set_val_str( hkey3, szService, appid->ServiceParameters );
+
+    if (appid->DllSurrogate)
+        msi_reg_set_val_str( hkey3, szDLL, appid->DllSurrogate );
+
+    if (appid->ActivateAtStorage)
+        msi_reg_set_val_str( hkey3, szActivate, szY );
+
+    if (appid->RunAsInteractiveUser)
+        msi_reg_set_val_str( hkey3, szRunAs, szUser );
+
+    RegCloseKey(hkey3);
+    return ERROR_SUCCESS;
+}
+
+UINT ACTION_RegisterClassInfo(MSIPACKAGE *package)
+{
+    /* 
+     * Again I am assuming the words, "Whose key file represents" when referring
+     * to a Component as to meaning that Components KeyPath file
+     */
+    
+    UINT rc;
+    MSIRECORD *uirow;
+    static const WCHAR szCLSID[] = { 'C','L','S','I','D',0 };
+    static const WCHAR szProgID[] = { 'P','r','o','g','I','D',0 };
+    static const WCHAR szVIProgID[] = { 'V','e','r','s','i','o','n','I','n','d','e','p','e','n','d','e','n','t','P','r','o','g','I','D',0 };
+    static const WCHAR szAppID[] = { 'A','p','p','I','D',0 };
+    static const WCHAR szSpace[] = {' ',0};
+    static const WCHAR szInprocServer32[] = {'I','n','p','r','o','c','S','e','r','v','e','r','3','2',0};
+    static const WCHAR szFileType_fmt[] = {'F','i','l','e','T','y','p','e','\\','%','s','\\','%','i',0};
+    HKEY hkey,hkey2,hkey3;
+    BOOL install_on_demand = FALSE;
+    MSICLASS *cls;
+
+    load_classes_and_such(package);
+    rc = RegCreateKeyW(HKEY_CLASSES_ROOT,szCLSID,&hkey);
+    if (rc != ERROR_SUCCESS)
+        return ERROR_FUNCTION_FAILED;
+
+    /* install_on_demand should be set if OLE supports install on demand OLE
+     * servers. For now i am defaulting to FALSE because i do not know how to
+     * check, and i am told our builtin OLE does not support it
+     */
+    
+    LIST_FOR_EACH_ENTRY( cls, &package->classes, MSICLASS, entry )
+    {
+        MSICOMPONENT *comp;
+        MSIFILE *file;
+        DWORD size, sz;
+        LPWSTR argument;
+        MSIFEATURE *feature;
+
+        comp = cls->Component;
+        if ( !comp )
+            continue;
+
+        feature = cls->Feature;
+
+        /* 
+         * yes. MSDN says that these are based on _Feature_ not on
+         * Component.  So verify the feature is to be installed
+         */
+        if ((!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL )) &&
+             !(install_on_demand &&
+               ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED )))
+        {
+            TRACE("Skipping class %s reg due to disabled feature %s\n", 
+                            debugstr_w(cls->clsid), 
+                            debugstr_w(feature->Feature));
+
+            continue;
+        }
+
+        TRACE("Registering class %s (%p)\n", debugstr_w(cls->clsid), cls);
+
+        cls->Installed = TRUE;
+        mark_progid_for_install( package, cls->ProgID );
+
+        RegCreateKeyW( hkey, cls->clsid, &hkey2 );
+
+        if (cls->Description)
+            msi_reg_set_val_str( hkey2, NULL, cls->Description );
+
+        RegCreateKeyW( hkey2, cls->Context, &hkey3 );
+        file = get_loaded_file( package, comp->KeyPath );
+
+
+        /* the context server is a short path name 
+         * except for if it is InprocServer32... 
+         */
+        if (strcmpiW( cls->Context, szInprocServer32 )!=0)
+        {
+            sz = GetShortPathNameW( file->TargetPath, NULL, 0 );
+            if (sz == 0)
+            {
+                ERR("Unable to find short path for CLSID COM Server\n");
+                argument = NULL;
+            }
+            else
+            {
+                size = sz * sizeof(WCHAR);
+
+                if (cls->Argument)
+                {
+                    size += strlenW(cls->Argument) * sizeof(WCHAR);
+                    size += sizeof(WCHAR);
+                }
+
+                argument = msi_alloc( size + sizeof(WCHAR));
+                GetShortPathNameW( file->TargetPath, argument, sz );
+
+                if (cls->Argument)
+                {
+                    strcatW(argument,szSpace);
+                    strcatW( argument, cls->Argument );
+                }
+            }
+        }
+        else
+        {
+            size = lstrlenW( file->TargetPath ) * sizeof(WCHAR);
+
+            if (cls->Argument)
+            {
+                size += strlenW(cls->Argument) * sizeof(WCHAR);
+                size += sizeof(WCHAR);
+            }
+
+            argument = msi_alloc( size + sizeof(WCHAR));
+            strcpyW( argument, file->TargetPath );
+
+            if (cls->Argument)
+            {
+                strcatW(argument,szSpace);
+                strcatW( argument, cls->Argument );
+            }
+        }
+
+        if (argument)
+        {
+            msi_reg_set_val_str( hkey3, NULL, argument );
+            msi_free(argument);
+        }
+
+        RegCloseKey(hkey3);
+
+        if (cls->ProgID || cls->ProgIDText)
+        {
+            LPCWSTR progid;
+
+            if (cls->ProgID)
+                progid = cls->ProgID->ProgID;
+            else
+                progid = cls->ProgIDText;
+
+            msi_reg_set_subkey_val( hkey2, szProgID, NULL, progid );
+
+            if (cls->ProgID && cls->ProgID->VersionInd)
+            {
+                msi_reg_set_subkey_val( hkey2, szVIProgID, NULL, 
+                                        cls->ProgID->VersionInd->ProgID );
+            }
+        }
+
+        if (cls->AppID)
+        { 
+            MSIAPPID *appid = cls->AppID;
+
+            msi_reg_set_val_str( hkey2, szAppID, appid->AppID );
+
+            register_appid( appid, cls->Description );
+        }
+
+        if (cls->IconPath)
+        {
+            static const WCHAR szDefaultIcon[] = 
+                {'D','e','f','a','u','l','t','I','c','o','n',0};
+
+            msi_reg_set_subkey_val( hkey2, szDefaultIcon, NULL, cls->IconPath );
+        }
+
+        if (cls->DefInprocHandler)
+        {
+            static const WCHAR szInproc[] =
+                {'I','n','p','r','o','c','H','a','n','d','l','e','r',0};
+
+            msi_reg_set_subkey_val( hkey2, szInproc, NULL, cls->DefInprocHandler );
+        }
+
+        if (cls->DefInprocHandler32)
+        {
+            static const WCHAR szInproc32[] =
+                {'I','n','p','r','o','c','H','a','n','d','l','e','r','3','2',0};
+
+            msi_reg_set_subkey_val( hkey2, szInproc32, NULL, cls->DefInprocHandler32 );
+        }
+        
+        RegCloseKey(hkey2);
+
+        /* if there is a FileTypeMask, register the FileType */
+        if (cls->FileTypeMask)
+        {
+            LPWSTR ptr, ptr2;
+            LPWSTR keyname;
+            INT index = 0;
+            ptr = cls->FileTypeMask;
+            while (ptr && *ptr)
+            {
+                ptr2 = strchrW(ptr,';');
+                if (ptr2)
+                    *ptr2 = 0;
+                keyname = msi_alloc( (strlenW(szFileType_fmt) + strlenW(cls->clsid) + 4) * sizeof(WCHAR));
+                sprintfW( keyname, szFileType_fmt, cls->clsid, index );
+
+                msi_reg_set_subkey_val( HKEY_CLASSES_ROOT, keyname, NULL, ptr );
+                msi_free(keyname);
+
+                if (ptr2)
+                    ptr = ptr2+1;
+                else
+                    ptr = NULL;
+
+                index ++;
+            }
+        }
+        
+        uirow = MSI_CreateRecord(1);
+
+        MSI_RecordSetStringW( uirow, 1, cls->clsid );
+        ui_actiondata(package,szRegisterClassInfo,uirow);
+        msiobj_release(&uirow->hdr);
+    }
+
+    RegCloseKey(hkey);
+    return rc;
+}
+
+static LPCWSTR get_clsid_of_progid( MSIPROGID *progid )
+{
+    while (progid)
+    {
+        if (progid->Class)
+            return progid->Class->clsid;
+        progid = progid->Parent;
+    }
+    return NULL;
+}
+
+static UINT register_progid( MSIPROGID* progid )
+{
+    static const WCHAR szCLSID[] = { 'C','L','S','I','D',0 };
+    static const WCHAR szDefaultIcon[] =
+        {'D','e','f','a','u','l','t','I','c','o','n',0};
+    static const WCHAR szCurVer[] =
+        {'C','u','r','V','e','r',0};
+    HKEY hkey = 0;
+    UINT rc;
+
+    rc = RegCreateKeyW( HKEY_CLASSES_ROOT, progid->ProgID, &hkey );
+    if (rc == ERROR_SUCCESS)
+    {
+        LPCWSTR clsid = get_clsid_of_progid( progid );
+
+        if (clsid)
+            msi_reg_set_subkey_val( hkey, szCLSID, NULL, clsid );
+        else
+            ERR("%s has no class\n", debugstr_w( progid->ProgID ) );
+
+        if (progid->Description)
+            msi_reg_set_val_str( hkey, NULL, progid->Description );
+
+        if (progid->IconPath)
+            msi_reg_set_subkey_val( hkey, szDefaultIcon, NULL, progid->IconPath );
+
+        /* write out the current version */
+        if (progid->CurVer)
+            msi_reg_set_subkey_val( hkey, szCurVer, NULL, progid->CurVer->ProgID );
+
+        RegCloseKey(hkey);
+    }
+    else
+        ERR("failed to create key %s\n", debugstr_w( progid->ProgID ) );
+
+    return rc;
+}
+
+UINT ACTION_RegisterProgIdInfo(MSIPACKAGE *package)
+{
+    MSIPROGID *progid;
+    MSIRECORD *uirow;
+
+    load_classes_and_such(package);
+
+    LIST_FOR_EACH_ENTRY( progid, &package->progids, MSIPROGID, entry )
+    {
+        /* check if this progid is to be installed */
+        if (progid->Class && progid->Class->Installed)
+            progid->InstallMe = TRUE;
+
+        if (!progid->InstallMe)
+        {
+            TRACE("progid %s not scheduled to be installed\n",
+                             debugstr_w(progid->ProgID));
+            continue;
+        }
+       
+        TRACE("Registering progid %s\n", debugstr_w(progid->ProgID));
+
+        register_progid( progid );
+
+        uirow = MSI_CreateRecord( 1 );
+        MSI_RecordSetStringW( uirow, 1, progid->ProgID );
+        ui_actiondata( package, szRegisterProgIdInfo, uirow );
+        msiobj_release( &uirow->hdr );
+    }
+
+    return ERROR_SUCCESS;
+}
+
+static UINT register_verb(MSIPACKAGE *package, LPCWSTR progid, 
+                MSICOMPONENT* component, MSIEXTENSION* extension,
+                MSIVERB* verb, INT* Sequence )
+{
+    LPWSTR keyname;
+    HKEY key;
+    static const WCHAR szShell[] = {'s','h','e','l','l',0};
+    static const WCHAR szCommand[] = {'c','o','m','m','a','n','d',0};
+    static const WCHAR fmt[] = {'\"','%','s','\"',' ','%','s',0};
+    static const WCHAR fmt2[] = {'\"','%','s','\"',0};
+    LPWSTR command;
+    DWORD size;
+    LPWSTR advertise;
+
+    keyname = build_directory_name(4, progid, szShell, verb->Verb, szCommand);
+
+    TRACE("Making Key %s\n",debugstr_w(keyname));
+    RegCreateKeyW(HKEY_CLASSES_ROOT, keyname, &key);
+    size = strlenW(component->FullKeypath);
+    if (verb->Argument)
+        size += strlenW(verb->Argument);
+     size += 4;
+
+     command = msi_alloc(size * sizeof (WCHAR));
+     if (verb->Argument)
+        sprintfW(command, fmt, component->FullKeypath, verb->Argument);
+     else
+        sprintfW(command, fmt2, component->FullKeypath);
+
+     msi_reg_set_val_str( key, NULL, command );
+     msi_free(command);
+
+     advertise = create_component_advertise_string(package, component, 
+                                                   extension->Feature->Feature);
+
+     size = strlenW(advertise);
+
+     if (verb->Argument)
+         size += strlenW(verb->Argument);
+     size += 4;
+
+     command = msi_alloc(size * sizeof (WCHAR));
+     memset(command,0,size*sizeof(WCHAR));
+
+     strcpyW(command,advertise);
+     if (verb->Argument)
+     {
+         static const WCHAR szSpace[] = {' ',0};
+         strcatW(command,szSpace);
+         strcatW(command,verb->Argument);
+     }
+
+     msi_reg_set_val_multi_str( key, szCommand, command );
+     
+     RegCloseKey(key);
+     msi_free(keyname);
+     msi_free(advertise);
+     msi_free(command);
+
+     if (verb->Command)
+     {
+        keyname = build_directory_name(3, progid, szShell, verb->Verb);
+        msi_reg_set_subkey_val( HKEY_CLASSES_ROOT, keyname, NULL, verb->Command );
+        msi_free(keyname);
+     }
+
+     if (verb->Sequence != MSI_NULL_INTEGER)
+     {
+        if (*Sequence == MSI_NULL_INTEGER || verb->Sequence < *Sequence)
+        {
+            *Sequence = verb->Sequence;
+            keyname = build_directory_name(2, progid, szShell);
+            msi_reg_set_subkey_val( HKEY_CLASSES_ROOT, keyname, NULL, verb->Verb );
+            msi_free(keyname);
+        }
+    }
+    return ERROR_SUCCESS;
+}
+
+UINT ACTION_RegisterExtensionInfo(MSIPACKAGE *package)
+{
+    static const WCHAR szContentType[] = 
+        {'C','o','n','t','e','n','t',' ','T','y','p','e',0 };
+    HKEY hkey;
+    MSIEXTENSION *ext;
+    MSIRECORD *uirow;
+    BOOL install_on_demand = TRUE;
+
+    load_classes_and_such(package);
+
+    /* We need to set install_on_demand based on if the shell handles advertised
+     * shortcuts and the like. Because Mike McCormack is working on this i am
+     * going to default to TRUE
+     */
+    
+    LIST_FOR_EACH_ENTRY( ext, &package->extensions, MSIEXTENSION, entry )
+    {
+        LPWSTR extension;
+        MSIFEATURE *feature;
+     
+        if (!ext->Component)
+            continue;
+
+        feature = ext->Feature;
+
+        /* 
+         * yes. MSDN says that these are based on _Feature_ not on
+         * Component.  So verify the feature is to be installed
+         */
+        if ((!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL )) &&
+             !(install_on_demand &&
+               ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED )))
+        {
+            TRACE("Skipping extension %s reg due to disabled feature %s\n",
+                   debugstr_w(ext->Extension), debugstr_w(feature->Feature));
+
+            continue;
+        }
+
+        TRACE("Registering extension %s (%p)\n", debugstr_w(ext->Extension), ext);
+
+        ext->Installed = TRUE;
+
+        /* this is only registered if the extension has at least 1 verb
+         * according to MSDN
+         */
+        if (ext->ProgID && !list_empty( &ext->verbs ) )
+            mark_progid_for_install( package, ext->ProgID );
+
+        mark_mime_for_install(ext->Mime);
+
+        extension = msi_alloc( (lstrlenW( ext->Extension ) + 2)*sizeof(WCHAR) );
+        extension[0] = '.';
+        lstrcpyW(extension+1,ext->Extension);
+
+        RegCreateKeyW(HKEY_CLASSES_ROOT,extension,&hkey);
+        msi_free( extension );
+
+        if (ext->Mime)
+            msi_reg_set_val_str( hkey, szContentType, ext->Mime->ContentType );
+
+        if (ext->ProgID || ext->ProgIDText)
+        {
+            static const WCHAR szSN[] = 
+                {'\\','S','h','e','l','l','N','e','w',0};
+            HKEY hkey2;
+            LPWSTR newkey;
+            LPCWSTR progid;
+            MSIVERB *verb;
+            INT Sequence = MSI_NULL_INTEGER;
+            
+            if (ext->ProgID)
+                progid = ext->ProgID->ProgID;
+            else
+                progid = ext->ProgIDText;
+
+            msi_reg_set_val_str( hkey, NULL, progid );
+
+            newkey = msi_alloc( (strlenW(progid)+strlenW(szSN)+1) * sizeof(WCHAR)); 
+
+            strcpyW(newkey,progid);
+            strcatW(newkey,szSN);
+            RegCreateKeyW(hkey,newkey,&hkey2);
+            RegCloseKey(hkey2);
+
+            msi_free(newkey);
+
+            /* do all the verbs */
+            LIST_FOR_EACH_ENTRY( verb, &ext->verbs, MSIVERB, entry )
+            {
+                register_verb( package, progid, ext->Component,
+                               ext, verb, &Sequence);
+            }
+        }
+        
+        RegCloseKey(hkey);
+
+        uirow = MSI_CreateRecord(1);
+        MSI_RecordSetStringW( uirow, 1, ext->Extension );
+        ui_actiondata(package,szRegisterExtensionInfo,uirow);
+        msiobj_release(&uirow->hdr);
+    }
+
+    return ERROR_SUCCESS;
+}
+
+UINT ACTION_RegisterMIMEInfo(MSIPACKAGE *package)
+{
+    static const WCHAR szExten[] = 
+        {'E','x','t','e','n','s','i','o','n',0 };
+    MSIRECORD *uirow;
+    MSIMIME *mt;
+
+    load_classes_and_such(package);
+
+    LIST_FOR_EACH_ENTRY( mt, &package->mimes, MSIMIME, entry )
+    {
+        LPWSTR extension;
+        LPCWSTR exten;
+        LPCWSTR mime;
+        static const WCHAR fmt[] = 
+            {'M','I','M','E','\\','D','a','t','a','b','a','s','e','\\',
+             'C','o','n','t','e','n','t',' ','T','y','p','e','\\', '%','s',0};
+        LPWSTR key;
+
+        /* 
+         * check if the MIME is to be installed. Either as requesed by an
+         * extension or Class
+         */
+        mt->InstallMe = (mt->InstallMe ||
+              (mt->Class && mt->Class->Installed) ||
+              (mt->Extension && mt->Extension->Installed));
+
+        if (!mt->InstallMe)
+        {
+            TRACE("MIME %s not scheduled to be installed\n",
+                             debugstr_w(mt->ContentType));
+            continue;
+        }
+        
+        mime = mt->ContentType;
+        exten = mt->Extension->Extension;
+
+        extension = msi_alloc( (lstrlenW( exten ) + 2)*sizeof(WCHAR) );
+        extension[0] = '.';
+        lstrcpyW(extension+1,exten);
+
+        key = msi_alloc( (strlenW(mime)+strlenW(fmt)+1) * sizeof(WCHAR) );
+        sprintfW(key,fmt,mime);
+        msi_reg_set_subkey_val( HKEY_CLASSES_ROOT, key, szExten, extension );
+
+        msi_free(extension);
+        msi_free(key);
+
+        if (mt->clsid)
+            FIXME("Handle non null for field 3\n");
+
+        uirow = MSI_CreateRecord(2);
+        MSI_RecordSetStringW(uirow,1,mt->ContentType);
+        MSI_RecordSetStringW(uirow,2,exten);
+        ui_actiondata(package,szRegisterMIMEInfo,uirow);
+        msiobj_release(&uirow->hdr);
+    }
+
+    return ERROR_SUCCESS;
+}
diff --git a/reactos/dll/msi/cond.tab.c b/reactos/dll/msi/cond.tab.c
new file mode 100644 (file)
index 0000000..3cbed28
--- /dev/null
@@ -0,0 +1,2239 @@
+/* A Bison parser, made by GNU Bison 1.875c.  */
+
+/* Skeleton parser for Yacc-like parsing with Bison,
+   Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program 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 General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, when this file is copied by Bison into a
+   Bison output file, you may use that output file without restriction.
+   This special exception was added by the Free Software Foundation
+   in version 1.24 of Bison.  */
+
+/* Written by Richard Stallman by simplifying the original so called
+   ``semantic'' parser.  */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+   infringing on user name space.  This should be done even for local
+   variables, as they might otherwise be expanded by user macros.
+   There are some unavoidable exceptions within include files to
+   define necessary library symbols; they are noted "INFRINGES ON
+   USER NAME SPACE" below.  */
+
+/* Identify Bison output.  */
+#define YYBISON 1
+
+/* Skeleton name.  */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers.  */
+#define YYPURE 1
+
+/* Using locations.  */
+#define YYLSP_NEEDED 0
+
+/* If NAME_PREFIX is specified substitute the variables and functions
+   names.  */
+#define yyparse COND_parse
+#define yylex   COND_lex
+#define yyerror COND_error
+#define yylval  COND_lval
+#define yychar  COND_char
+#define yydebug COND_debug
+#define yynerrs COND_nerrs
+
+
+/* Tokens.  */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+   /* Put the tokens into the symbol table, so that GDB and other debuggers
+      know about them.  */
+   enum yytokentype {
+     COND_SPACE = 258,
+     COND_EOF = 259,
+     COND_OR = 260,
+     COND_AND = 261,
+     COND_NOT = 262,
+     COND_LT = 263,
+     COND_GT = 264,
+     COND_EQ = 265,
+     COND_LPAR = 266,
+     COND_RPAR = 267,
+     COND_TILDA = 268,
+     COND_PERCENT = 269,
+     COND_DOLLARS = 270,
+     COND_QUESTION = 271,
+     COND_AMPER = 272,
+     COND_EXCLAM = 273,
+     COND_IDENT = 274,
+     COND_NUMBER = 275,
+     COND_LITER = 276,
+     COND_ERROR = 277
+   };
+#endif
+#define COND_SPACE 258
+#define COND_EOF 259
+#define COND_OR 260
+#define COND_AND 261
+#define COND_NOT 262
+#define COND_LT 263
+#define COND_GT 264
+#define COND_EQ 265
+#define COND_LPAR 266
+#define COND_RPAR 267
+#define COND_TILDA 268
+#define COND_PERCENT 269
+#define COND_DOLLARS 270
+#define COND_QUESTION 271
+#define COND_AMPER 272
+#define COND_EXCLAM 273
+#define COND_IDENT 274
+#define COND_NUMBER 275
+#define COND_LITER 276
+#define COND_ERROR 277
+
+
+
+
+/* Copy the first part of user declarations.  */
+#line 1 "./cond.y"
+
+
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2003 Mike McCormack for CodeWeavers
+ *
+ * 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 <stdlib.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wine/debug.h"
+#include "wine/unicode.h"
+
+#include "msi.h"
+#include "msiquery.h"
+#include "msipriv.h"
+
+#define YYLEX_PARAM info
+#define YYPARSE_PARAM info
+
+static int COND_error(const char *str);
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+typedef struct tag_yyinput
+{
+    MSIPACKAGE *package;
+    LPCWSTR str;
+    INT    n;
+    MSICONDITION result;
+} COND_input;
+
+struct cond_str {
+    LPCWSTR data;
+    INT len;
+};
+
+static LPWSTR COND_GetString( struct cond_str *str );
+static LPWSTR COND_GetLiteral( struct cond_str *str );
+static int COND_lex( void *COND_lval, COND_input *info);
+
+typedef INT (*comp_int)(INT a, INT b);
+typedef INT (*comp_str)(LPWSTR a, LPWSTR b, BOOL caseless);
+typedef INT (*comp_m1)(LPWSTR a,int b);
+typedef INT (*comp_m2)(int a,LPWSTR b);
+
+static INT comp_lt_i(INT a, INT b);
+static INT comp_gt_i(INT a, INT b);
+static INT comp_le_i(INT a, INT b);
+static INT comp_ge_i(INT a, INT b);
+static INT comp_eq_i(INT a, INT b);
+static INT comp_ne_i(INT a, INT b);
+static INT comp_bitand(INT a, INT b);
+static INT comp_highcomp(INT a, INT b);
+static INT comp_lowcomp(INT a, INT b);
+
+static INT comp_eq_s(LPWSTR a, LPWSTR b, BOOL casless);
+static INT comp_ne_s(LPWSTR a, LPWSTR b, BOOL casless);
+static INT comp_lt_s(LPWSTR a, LPWSTR b, BOOL casless);
+static INT comp_gt_s(LPWSTR a, LPWSTR b, BOOL casless);
+static INT comp_le_s(LPWSTR a, LPWSTR b, BOOL casless);
+static INT comp_ge_s(LPWSTR a, LPWSTR b, BOOL casless);
+static INT comp_substring(LPWSTR a, LPWSTR b, BOOL casless);
+static INT comp_start(LPWSTR a, LPWSTR b, BOOL casless);
+static INT comp_end(LPWSTR a, LPWSTR b, BOOL casless);
+
+static INT comp_eq_m1(LPWSTR a, INT b);
+static INT comp_ne_m1(LPWSTR a, INT b);
+static INT comp_lt_m1(LPWSTR a, INT b);
+static INT comp_gt_m1(LPWSTR a, INT b);
+static INT comp_le_m1(LPWSTR a, INT b);
+static INT comp_ge_m1(LPWSTR a, INT b);
+
+static INT comp_eq_m2(INT a, LPWSTR b);
+static INT comp_ne_m2(INT a, LPWSTR b);
+static INT comp_lt_m2(INT a, LPWSTR b);
+static INT comp_gt_m2(INT a, LPWSTR b);
+static INT comp_le_m2(INT a, LPWSTR b);
+static INT comp_ge_m2(INT a, LPWSTR b);
+
+
+
+/* Enabling traces.  */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+/* Enabling verbose error messages.  */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+#if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED)
+#line 106 "./cond.y"
+typedef union YYSTYPE {
+    struct cond_str str;
+    LPWSTR    string;
+    INT       value;
+    comp_int  fn_comp_int;
+    comp_str  fn_comp_str;
+    comp_m1   fn_comp_m1;
+    comp_m2   fn_comp_m2;
+} YYSTYPE;
+/* Line 191 of yacc.c.  */
+#line 241 "cond.tab.c"
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+
+
+/* Copy the second part of user declarations.  */
+
+
+/* Line 214 of yacc.c.  */
+#line 253 "cond.tab.c"
+
+#if ! defined (yyoverflow) || YYERROR_VERBOSE
+
+# ifndef YYFREE
+#  define YYFREE free
+# endif
+# ifndef YYMALLOC
+#  define YYMALLOC malloc
+# endif
+
+/* The parser invokes alloca or malloc; define the necessary symbols.  */
+
+# ifdef YYSTACK_USE_ALLOCA
+#  if YYSTACK_USE_ALLOCA
+#   define YYSTACK_ALLOC alloca
+#  endif
+# else
+#  if defined (alloca) || defined (_ALLOCA_H)
+#   define YYSTACK_ALLOC alloca
+#  else
+#   ifdef __GNUC__
+#    define YYSTACK_ALLOC __builtin_alloca
+#   endif
+#  endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+   /* Pacify GCC's `empty if-body' warning. */
+#  define YYSTACK_FREE(Ptr) do { /* empty */; } while (0)
+# else
+#  if defined (__STDC__) || defined (__cplusplus)
+#   include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+#   define YYSIZE_T size_t
+#  endif
+#  define YYSTACK_ALLOC YYMALLOC
+#  define YYSTACK_FREE YYFREE
+# endif
+#endif /* ! defined (yyoverflow) || YYERROR_VERBOSE */
+
+
+#if (! defined (yyoverflow) \
+     && (! defined (__cplusplus) \
+        || (defined (YYSTYPE_IS_TRIVIAL) && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member.  */
+union yyalloc
+{
+  short yyss;
+  YYSTYPE yyvs;
+  };
+
+/* The size of the maximum gap between one aligned stack and the next.  */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+   N elements.  */
+# define YYSTACK_BYTES(N) \
+     ((N) * (sizeof (short) + sizeof (YYSTYPE))                                \
+      + YYSTACK_GAP_MAXIMUM)
+
+/* Copy COUNT objects from FROM to TO.  The source and destination do
+   not overlap.  */
+# ifndef YYCOPY
+#  if defined (__GNUC__) && 1 < __GNUC__
+#   define YYCOPY(To, From, Count) \
+      __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+#  else
+#   define YYCOPY(To, From, Count)             \
+      do                                       \
+       {                                       \
+         register YYSIZE_T yyi;                \
+         for (yyi = 0; yyi < (Count); yyi++)   \
+           (To)[yyi] = (From)[yyi];            \
+       }                                       \
+      while (0)
+#  endif
+# endif
+
+/* Relocate STACK from its old location to the new one.  The
+   local variables YYSIZE and YYSTACKSIZE give the old and new number of
+   elements in the stack, and YYPTR gives the new location of the
+   stack.  Advance YYPTR to a properly aligned location for the next
+   stack.  */
+# define YYSTACK_RELOCATE(Stack)                                       \
+    do                                                                 \
+      {                                                                        \
+       YYSIZE_T yynewbytes;                                            \
+       YYCOPY (&yyptr->Stack, Stack, yysize);                          \
+       Stack = &yyptr->Stack;                                          \
+       yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+       yyptr += yynewbytes / sizeof (*yyptr);                          \
+      }                                                                        \
+    while (0)
+
+#endif
+
+#if defined (__STDC__) || defined (__cplusplus)
+   typedef signed char yysigned_char;
+#else
+   typedef short yysigned_char;
+#endif
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL  30
+/* YYLAST -- Last index in YYTABLE.  */
+#define YYLAST   146
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS  23
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS  17
+/* YYNRULES -- Number of rules. */
+#define YYNRULES  65
+/* YYNRULES -- Number of states. */
+#define YYNSTATES  74
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX.  */
+#define YYUNDEFTOK  2
+#define YYMAXUTOK   277
+
+#define YYTRANSLATE(YYX)                                               \
+  ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX.  */
+static const unsigned char yytranslate[] =
+{
+       0,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     1,     2,     3,     4,
+       5,     6,     7,     8,     9,    10,    11,    12,    13,    14,
+      15,    16,    17,    18,    19,    20,    21,    22
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+   YYRHS.  */
+static const unsigned char yyprhs[] =
+{
+       0,     0,     3,     5,     7,    11,    13,    17,    19,    22,
+      24,    26,    30,    34,    39,    43,    47,    51,    53,    56,
+      58,    60,    63,    66,    69,    72,    75,    77,    80,    82,
+      84,    87,    90,    93,    96,    99,   101,   104,   106,   108,
+     111,   114,   117,   120,   123,   125,   128,   130,   132,   135,
+     138,   141,   144,   147,   149,   151,   153,   155,   157,   160,
+     163,   166,   169,   171,   174,   176
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS. */
+static const yysigned_char yyrhs[] =
+{
+      24,     0,    -1,    25,    -1,    26,    -1,    26,     5,    25,
+      -1,    27,    -1,    26,     6,    27,    -1,    28,    -1,     7,
+      28,    -1,    33,    -1,    34,    -1,    33,    29,    33,    -1,
+      34,    30,    34,    -1,    34,    13,    30,    34,    -1,    34,
+      31,    33,    -1,    33,    32,    34,    -1,    11,    25,    12,
+      -1,    10,    -1,     8,     9,    -1,     8,    -1,     9,    -1,
+       8,    10,    -1,     9,    10,    -1,     9,     8,    -1,     8,
+       8,    -1,     9,     9,    -1,    10,    -1,     8,     9,    -1,
+       8,    -1,     9,    -1,     8,    10,    -1,     9,    10,    -1,
+       9,     8,    -1,     8,     8,    -1,     9,     9,    -1,    10,
+      -1,     8,     9,    -1,     8,    -1,     9,    -1,     8,    10,
+      -1,     9,    10,    -1,     9,     8,    -1,     8,     8,    -1,
+       9,     9,    -1,    10,    -1,     8,     9,    -1,     8,    -1,
+       9,    -1,     8,    10,    -1,     9,    10,    -1,     9,     8,
+      -1,     8,     8,    -1,     9,     9,    -1,    36,    -1,    39,
+      -1,    37,    -1,    35,    -1,    21,    -1,    15,    38,    -1,
+      16,    38,    -1,    17,    38,    -1,    18,    38,    -1,    38,
+      -1,    14,    38,    -1,    19,    -1,    20,    -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined.  */
+static const unsigned short yyrline[] =
+{
+       0,   136,   136,   144,   148,   155,   159,   166,   170,   178,
+     182,   187,   191,   197,   203,   208,   213,   221,   225,   229,
+     233,   237,   241,   246,   250,   254,   262,   266,   270,   274,
+     278,   282,   287,   291,   295,   303,   307,   311,   315,   319,
+     323,   328,   332,   336,   344,   348,   352,   356,   360,   364,
+     369,   373,   377,   384,   388,   395,   399,   406,   415,   424,
+     433,   442,   454,   477,   491,   500
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE
+/* YYTNME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+   First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+static const char *const yytname[] =
+{
+  "$end", "error", "$undefined", "COND_SPACE", "COND_EOF", "COND_OR",
+  "COND_AND", "COND_NOT", "COND_LT", "COND_GT", "COND_EQ", "COND_LPAR",
+  "COND_RPAR", "COND_TILDA", "COND_PERCENT", "COND_DOLLARS",
+  "COND_QUESTION", "COND_AMPER", "COND_EXCLAM", "COND_IDENT",
+  "COND_NUMBER", "COND_LITER", "COND_ERROR", "$accept", "condition",
+  "expression", "boolean_term", "boolean_factor", "term", "comp_op_i",
+  "comp_op_s", "comp_op_m1", "comp_op_m2", "value_i", "value_s", "literal",
+  "symbol_i", "symbol_s", "identifier", "integer", 0
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+   token YYLEX-NUM.  */
+static const unsigned short yytoknum[] =
+{
+       0,   256,   257,   258,   259,   260,   261,   262,   263,   264,
+     265,   266,   267,   268,   269,   270,   271,   272,   273,   274,
+     275,   276,   277
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives.  */
+static const unsigned char yyr1[] =
+{
+       0,    23,    24,    25,    25,    26,    26,    27,    27,    28,
+      28,    28,    28,    28,    28,    28,    28,    29,    29,    29,
+      29,    29,    29,    29,    29,    29,    30,    30,    30,    30,
+      30,    30,    30,    30,    30,    31,    31,    31,    31,    31,
+      31,    31,    31,    31,    32,    32,    32,    32,    32,    32,
+      32,    32,    32,    33,    33,    34,    34,    35,    36,    36,
+      36,    36,    37,    37,    38,    39
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN.  */
+static const unsigned char yyr2[] =
+{
+       0,     2,     1,     1,     3,     1,     3,     1,     2,     1,
+       1,     3,     3,     4,     3,     3,     3,     1,     2,     1,
+       1,     2,     2,     2,     2,     2,     1,     2,     1,     1,
+       2,     2,     2,     2,     2,     1,     2,     1,     1,     2,
+       2,     2,     2,     2,     1,     2,     1,     1,     2,     2,
+       2,     2,     2,     1,     1,     1,     1,     1,     2,     2,
+       2,     2,     1,     2,     1,     1
+};
+
+/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
+   STATE-NUM when YYTABLE doesn't specify something else to do.  Zero
+   means the default is an error.  */
+static const unsigned char yydefact[] =
+{
+       0,     0,     0,     0,     0,     0,     0,     0,    64,    65,
+      57,     0,     2,     3,     5,     7,     9,    10,    56,    53,
+      55,    62,    54,     8,     0,    63,    58,    59,    60,    61,
+       1,     0,     0,    19,    20,    17,     0,     0,    37,    38,
+      35,     0,     0,     0,    16,     4,     6,    24,    18,    21,
+      23,    25,    22,    11,    15,    42,    36,    39,    41,    43,
+      40,    28,    29,    26,     0,    12,    14,    33,    27,    30,
+      32,    34,    31,    13
+};
+
+/* YYDEFGOTO[NTERM-NUM]. */
+static const yysigned_char yydefgoto[] =
+{
+      -1,    11,    12,    13,    14,    15,    36,    42,    43,    37,
+      16,    17,    18,    19,    20,    21,    22
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+   STATE-NUM.  */
+#define YYPACT_NINF -37
+static const short yypact[] =
+{
+      -4,    51,    -4,   -11,   -11,   -11,   -11,   -11,   -37,   -37,
+     -37,    18,   -37,    -1,   -37,   -37,    49,    14,   -37,   -37,
+     -37,   -37,   -37,   -37,    19,   -37,   -37,   -37,   -37,   -37,
+     -37,    -4,    -4,    11,    25,    34,   111,    59,    28,    42,
+      60,   130,    59,   111,   -37,   -37,   -37,    63,    69,    72,
+      73,    81,    82,   -37,   -37,    85,    91,    94,    95,   103,
+     104,   133,   136,   -37,    59,   -37,   -37,   -37,   -37,   -37,
+     -37,   -37,   -37,   -37
+};
+
+/* YYPGOTO[NTERM-NUM].  */
+static const short yypgoto[] =
+{
+     -37,   -37,    -2,   -37,    -6,    39,   -37,     0,   -37,   -37,
+     -34,   -36,   -37,   -37,   -37,   129,   -37
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]].  What to do in state STATE-NUM.  If
+   positive, shift that token.  If negative, reduce the rule which
+   number is the opposite.  If zero, do what YYDEFACT says.
+   If YYTABLE_NINF, syntax error.  */
+#define YYTABLE_NINF -53
+static const yysigned_char yytable[] =
+{
+      24,    54,    53,     1,    31,    32,    65,     2,     8,    66,
+       3,     4,     5,     6,     7,     8,     9,    10,    30,    47,
+      48,    49,    38,    39,    40,   -46,    46,    41,    73,    45,
+     -46,    44,   -46,    50,    51,    52,    55,    56,    57,   -47,
+      23,    64,   -28,     0,   -47,     0,   -47,   -28,   -44,   -28,
+      58,    59,    60,   -44,     0,   -44,   -29,    33,    34,    35,
+       0,   -29,     2,   -29,     0,     3,     4,     5,     6,     7,
+       8,     9,    10,     3,   -26,     0,     0,   -51,     8,   -26,
+      10,   -26,   -51,   -45,   -51,     0,   -48,   -50,   -45,     0,
+     -45,   -48,   -50,   -48,   -50,   -52,   -49,     0,     0,   -33,
+     -52,   -49,   -52,   -49,   -33,   -27,   -33,     0,   -30,   -32,
+     -27,     0,   -27,   -30,   -32,   -30,   -32,   -34,   -31,     0,
+       0,     0,   -34,   -31,   -34,   -31,     4,     5,     6,     7,
+       0,     9,    25,    26,    27,    28,    29,     0,    61,    62,
+      63,    67,    68,    69,    70,    71,    72
+};
+
+static const yysigned_char yycheck[] =
+{
+       2,    37,    36,     7,     5,     6,    42,    11,    19,    43,
+      14,    15,    16,    17,    18,    19,    20,    21,     0,     8,
+       9,    10,     8,     9,    10,    14,    32,    13,    64,    31,
+      19,    12,    21,     8,     9,    10,     8,     9,    10,    14,
+       1,    41,    14,    -1,    19,    -1,    21,    19,    14,    21,
+       8,     9,    10,    19,    -1,    21,    14,     8,     9,    10,
+      -1,    19,    11,    21,    -1,    14,    15,    16,    17,    18,
+      19,    20,    21,    14,    14,    -1,    -1,    14,    19,    19,
+      21,    21,    19,    14,    21,    -1,    14,    14,    19,    -1,
+      21,    19,    19,    21,    21,    14,    14,    -1,    -1,    14,
+      19,    19,    21,    21,    19,    14,    21,    -1,    14,    14,
+      19,    -1,    21,    19,    19,    21,    21,    14,    14,    -1,
+      -1,    -1,    19,    19,    21,    21,    15,    16,    17,    18,
+      -1,    20,     3,     4,     5,     6,     7,    -1,     8,     9,
+      10,     8,     9,    10,     8,     9,    10
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+   symbol of state STATE-NUM.  */
+static const unsigned char yystos[] =
+{
+       0,     7,    11,    14,    15,    16,    17,    18,    19,    20,
+      21,    24,    25,    26,    27,    28,    33,    34,    35,    36,
+      37,    38,    39,    28,    25,    38,    38,    38,    38,    38,
+       0,     5,     6,     8,     9,    10,    29,    32,     8,     9,
+      10,    13,    30,    31,    12,    25,    27,     8,     9,    10,
+       8,     9,    10,    33,    34,     8,     9,    10,     8,     9,
+      10,     8,     9,    10,    30,    34,    33,     8,     9,    10,
+       8,     9,    10,    34
+};
+
+#if ! defined (YYSIZE_T) && defined (__SIZE_TYPE__)
+# define YYSIZE_T __SIZE_TYPE__
+#endif
+#if ! defined (YYSIZE_T) && defined (size_t)
+# define YYSIZE_T size_t
+#endif
+#if ! defined (YYSIZE_T)
+# if defined (__STDC__) || defined (__cplusplus)
+#  include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYSIZE_T size_t
+# endif
+#endif
+#if ! defined (YYSIZE_T)
+# define YYSIZE_T unsigned int
+#endif
+
+#define yyerrok                (yyerrstatus = 0)
+#define yyclearin      (yychar = YYEMPTY)
+#define YYEMPTY                (-2)
+#define YYEOF          0
+
+#define YYACCEPT       goto yyacceptlab
+#define YYABORT                goto yyabortlab
+#define YYERROR                goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror.  This remains here temporarily
+   to ease the transition to the new meaning of YYERROR, for GCC.
+   Once GCC version 2 has supplanted version 1, this can go.  */
+
+#define YYFAIL         goto yyerrlab
+
+#define YYRECOVERING()  (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value)                                 \
+do                                                             \
+  if (yychar == YYEMPTY && yylen == 1)                         \
+    {                                                          \
+      yychar = (Token);                                                \
+      yylval = (Value);                                                \
+      yytoken = YYTRANSLATE (yychar);                          \
+      YYPOPSTACK;                                              \
+      goto yybackup;                                           \
+    }                                                          \
+  else                                                         \
+    {                                                          \
+      yyerror ("syntax error: cannot back up");\
+      YYERROR;                                                 \
+    }                                                          \
+while (0)
+
+#define YYTERROR       1
+#define YYERRCODE      256
+
+/* YYLLOC_DEFAULT -- Compute the default location (before the actions
+   are run).  */
+
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N)               \
+   ((Current).first_line   = (Rhs)[1].first_line,      \
+    (Current).first_column = (Rhs)[1].first_column,    \
+    (Current).last_line    = (Rhs)[N].last_line,       \
+    (Current).last_column  = (Rhs)[N].last_column)
+#endif
+
+/* YYLEX -- calling `yylex' with the right arguments.  */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (&yylval, YYLEX_PARAM)
+#else
+# define YYLEX yylex (&yylval)
+#endif
+
+/* Enable debugging if requested.  */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+#  include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args)                       \
+do {                                           \
+  if (yydebug)                                 \
+    YYFPRINTF Args;                            \
+} while (0)
+
+# define YYDSYMPRINT(Args)                     \
+do {                                           \
+  if (yydebug)                                 \
+    yysymprint Args;                           \
+} while (0)
+
+# define YYDSYMPRINTF(Title, Token, Value, Location)           \
+do {                                                           \
+  if (yydebug)                                                 \
+    {                                                          \
+      YYFPRINTF (stderr, "%s ", Title);                                \
+      yysymprint (stderr,                                      \
+                  Token, Value);       \
+      YYFPRINTF (stderr, "\n");                                        \
+    }                                                          \
+} while (0)
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included).                                                   |
+`------------------------------------------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yy_stack_print (short *bottom, short *top)
+#else
+static void
+yy_stack_print (bottom, top)
+    short *bottom;
+    short *top;
+#endif
+{
+  YYFPRINTF (stderr, "Stack now");
+  for (/* Nothing. */; bottom <= top; ++bottom)
+    YYFPRINTF (stderr, " %d", *bottom);
+  YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top)                           \
+do {                                                           \
+  if (yydebug)                                                 \
+    yy_stack_print ((Bottom), (Top));                          \
+} while (0)
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced.  |
+`------------------------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yy_reduce_print (int yyrule)
+#else
+static void
+yy_reduce_print (yyrule)
+    int yyrule;
+#endif
+{
+  int yyi;
+  unsigned int yylno = yyrline[yyrule];
+  YYFPRINTF (stderr, "Reducing stack by rule %d (line %u), ",
+             yyrule - 1, yylno);
+  /* Print the symbols being reduced, and their result.  */
+  for (yyi = yyprhs[yyrule]; 0 <= yyrhs[yyi]; yyi++)
+    YYFPRINTF (stderr, "%s ", yytname [yyrhs[yyi]]);
+  YYFPRINTF (stderr, "-> %s\n", yytname [yyr1[yyrule]]);
+}
+
+# define YY_REDUCE_PRINT(Rule)         \
+do {                                   \
+  if (yydebug)                         \
+    yy_reduce_print (Rule);            \
+} while (0)
+
+/* Nonzero means print parse trace.  It is left uninitialized so that
+   multiple parsers can coexist.  */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YYDSYMPRINT(Args)
+# define YYDSYMPRINTF(Title, Token, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks.  */
+#ifndef        YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+   if the built-in stack extension method is used).
+
+   Do not make this value too large; the results are undefined if
+   SIZE_MAX < YYSTACK_BYTES (YYMAXDEPTH)
+   evaluated with infinite-precision integer arithmetic.  */
+
+#if defined (YYMAXDEPTH) && YYMAXDEPTH == 0
+# undef YYMAXDEPTH
+#endif
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+\f
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+#  if defined (__GLIBC__) && defined (_STRING_H)
+#   define yystrlen strlen
+#  else
+/* Return the length of YYSTR.  */
+static YYSIZE_T
+#   if defined (__STDC__) || defined (__cplusplus)
+yystrlen (const char *yystr)
+#   else
+yystrlen (yystr)
+     const char *yystr;
+#   endif
+{
+  register const char *yys = yystr;
+
+  while (*yys++ != '\0')
+    continue;
+
+  return yys - yystr - 1;
+}
+#  endif
+# endif
+
+# ifndef yystpcpy
+#  if defined (__GLIBC__) && defined (_STRING_H) && defined (_GNU_SOURCE)
+#   define yystpcpy stpcpy
+#  else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+   YYDEST.  */
+static char *
+#   if defined (__STDC__) || defined (__cplusplus)
+yystpcpy (char *yydest, const char *yysrc)
+#   else
+yystpcpy (yydest, yysrc)
+     char *yydest;
+     const char *yysrc;
+#   endif
+{
+  register char *yyd = yydest;
+  register const char *yys = yysrc;
+
+  while ((*yyd++ = *yys++) != '\0')
+    continue;
+
+  return yyd - 1;
+}
+#  endif
+# endif
+
+#endif /* !YYERROR_VERBOSE */
+
+\f
+
+#if YYDEBUG
+/*--------------------------------.
+| Print this symbol on YYOUTPUT.  |
+`--------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yysymprint (FILE *yyoutput, int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yysymprint (yyoutput, yytype, yyvaluep)
+    FILE *yyoutput;
+    int yytype;
+    YYSTYPE *yyvaluep;
+#endif
+{
+  /* Pacify ``unused variable'' warnings.  */
+  (void) yyvaluep;
+
+  if (yytype < YYNTOKENS)
+    {
+      YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+# ifdef YYPRINT
+      YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# endif
+    }
+  else
+    YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+  switch (yytype)
+    {
+      default:
+        break;
+    }
+  YYFPRINTF (yyoutput, ")");
+}
+
+#endif /* ! YYDEBUG */
+/*-----------------------------------------------.
+| Release the memory associated to this symbol.  |
+`-----------------------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yydestruct (int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yydestruct (yytype, yyvaluep)
+    int yytype;
+    YYSTYPE *yyvaluep;
+#endif
+{
+  /* Pacify ``unused variable'' warnings.  */
+  (void) yyvaluep;
+
+  switch (yytype)
+    {
+
+      default:
+        break;
+    }
+}
+\f
+
+/* Prevent warnings from -Wmissing-prototypes.  */
+
+#ifdef YYPARSE_PARAM
+# if defined (__STDC__) || defined (__cplusplus)
+int yyparse (void *YYPARSE_PARAM);
+# else
+int yyparse ();
+# endif
+#else /* ! YYPARSE_PARAM */
+#if defined (__STDC__) || defined (__cplusplus)
+int yyparse (void);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+
+
+
+
+
+/*----------.
+| yyparse.  |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+# if defined (__STDC__) || defined (__cplusplus)
+int yyparse (void *YYPARSE_PARAM)
+# else
+int yyparse (YYPARSE_PARAM)
+  void *YYPARSE_PARAM;
+# endif
+#else /* ! YYPARSE_PARAM */
+#if defined (__STDC__) || defined (__cplusplus)
+int
+yyparse (void)
+#else
+int
+yyparse ()
+
+#endif
+#endif
+{
+  /* The lookahead symbol.  */
+int yychar;
+
+/* The semantic value of the lookahead symbol.  */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far.  */
+int yynerrs;
+
+  register int yystate;
+  register int yyn;
+  int yyresult;
+  /* Number of tokens to shift before error messages enabled.  */
+  int yyerrstatus;
+  /* Lookahead token as an internal (translated) token number.  */
+  int yytoken = 0;
+
+  /* Three stacks and their tools:
+     `yyss': related to states,
+     `yyvs': related to semantic values,
+     `yyls': related to locations.
+
+     Refer to the stacks thru separate pointers, to allow yyoverflow
+     to reallocate them elsewhere.  */
+
+  /* The state stack.  */
+  short        yyssa[YYINITDEPTH];
+  short *yyss = yyssa;
+  register short *yyssp;
+
+  /* The semantic value stack.  */
+  YYSTYPE yyvsa[YYINITDEPTH];
+  YYSTYPE *yyvs = yyvsa;
+  register YYSTYPE *yyvsp;
+
+
+
+#define YYPOPSTACK   (yyvsp--, yyssp--)
+
+  YYSIZE_T yystacksize = YYINITDEPTH;
+
+  /* The variables used to return semantic value and location from the
+     action routines.  */
+  YYSTYPE yyval;
+
+
+  /* When reducing, the number of symbols on the RHS of the reduced
+     rule.  */
+  int yylen;
+
+  YYDPRINTF ((stderr, "Starting parse\n"));
+
+  yystate = 0;
+  yyerrstatus = 0;
+  yynerrs = 0;
+  yychar = YYEMPTY;            /* Cause a token to be read.  */
+
+  /* Initialize stack pointers.
+     Waste one element of value and location stack
+     so that they stay on the same level as the state stack.
+     The wasted elements are never initialized.  */
+
+  yyssp = yyss;
+  yyvsp = yyvs;
+
+  goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate.  |
+`------------------------------------------------------------*/
+ yynewstate:
+  /* In all cases, when you get here, the value and location stacks
+     have just been pushed. so pushing a state here evens the stacks.
+     */
+  yyssp++;
+
+ yysetstate:
+  *yyssp = yystate;
+
+  if (yyss + yystacksize - 1 <= yyssp)
+    {
+      /* Get the current used size of the three stacks, in elements.  */
+      YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+      {
+       /* Give user a chance to reallocate the stack. Use copies of
+          these so that the &'s don't force the real ones into
+          memory.  */
+       YYSTYPE *yyvs1 = yyvs;
+       short *yyss1 = yyss;
+
+
+       /* Each stack pointer address is followed by the size of the
+          data in use in that stack, in bytes.  This used to be a
+          conditional around just the two extra args, but that might
+          be undefined if yyoverflow is a macro.  */
+       yyoverflow ("parser stack overflow",
+                   &yyss1, yysize * sizeof (*yyssp),
+                   &yyvs1, yysize * sizeof (*yyvsp),
+
+                   &yystacksize);
+
+       yyss = yyss1;
+       yyvs = yyvs1;
+      }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+      goto yyoverflowlab;
+# else
+      /* Extend the stack our own way.  */
+      if (YYMAXDEPTH <= yystacksize)
+       goto yyoverflowlab;
+      yystacksize *= 2;
+      if (YYMAXDEPTH < yystacksize)
+       yystacksize = YYMAXDEPTH;
+
+      {
+       short *yyss1 = yyss;
+       union yyalloc *yyptr =
+         (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+       if (! yyptr)
+         goto yyoverflowlab;
+       YYSTACK_RELOCATE (yyss);
+       YYSTACK_RELOCATE (yyvs);
+
+#  undef YYSTACK_RELOCATE
+       if (yyss1 != yyssa)
+         YYSTACK_FREE (yyss1);
+      }
+# endif
+#endif /* no yyoverflow */
+
+      yyssp = yyss + yysize - 1;
+      yyvsp = yyvs + yysize - 1;
+
+
+      YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+                 (unsigned long int) yystacksize));
+
+      if (yyss + yystacksize - 1 <= yyssp)
+       YYABORT;
+    }
+
+  YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+  goto yybackup;
+
+/*-----------.
+| yybackup.  |
+`-----------*/
+yybackup:
+
+/* Do appropriate processing given the current state.  */
+/* Read a lookahead token if we need one and don't already have one.  */
+/* yyresume: */
+
+  /* First try to decide what to do without reference to lookahead token.  */
+
+  yyn = yypact[yystate];
+  if (yyn == YYPACT_NINF)
+    goto yydefault;
+
+  /* Not known => get a lookahead token if don't already have one.  */
+
+  /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol.  */
+  if (yychar == YYEMPTY)
+    {
+      YYDPRINTF ((stderr, "Reading a token: "));
+      yychar = YYLEX;
+    }
+
+  if (yychar <= YYEOF)
+    {
+      yychar = yytoken = YYEOF;
+      YYDPRINTF ((stderr, "Now at end of input.\n"));
+    }
+  else
+    {
+      yytoken = YYTRANSLATE (yychar);
+      YYDSYMPRINTF ("Next token is", yytoken, &yylval, &yylloc);
+    }
+
+  /* If the proper action on seeing token YYTOKEN is to reduce or to
+     detect an error, take that action.  */
+  yyn += yytoken;
+  if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+    goto yydefault;
+  yyn = yytable[yyn];
+  if (yyn <= 0)
+    {
+      if (yyn == 0 || yyn == YYTABLE_NINF)
+       goto yyerrlab;
+      yyn = -yyn;
+      goto yyreduce;
+    }
+
+  if (yyn == YYFINAL)
+    YYACCEPT;
+
+  /* Shift the lookahead token.  */
+  YYDPRINTF ((stderr, "Shifting token %s, ", yytname[yytoken]));
+
+  /* Discard the token being shifted unless it is eof.  */
+  if (yychar != YYEOF)
+    yychar = YYEMPTY;
+
+  *++yyvsp = yylval;
+
+
+  /* Count tokens shifted since error; after three, turn off error
+     status.  */
+  if (yyerrstatus)
+    yyerrstatus--;
+
+  yystate = yyn;
+  goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state.  |
+`-----------------------------------------------------------*/
+yydefault:
+  yyn = yydefact[yystate];
+  if (yyn == 0)
+    goto yyerrlab;
+  goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction.  |
+`-----------------------------*/
+yyreduce:
+  /* yyn is the number of a rule to reduce with.  */
+  yylen = yyr2[yyn];
+
+  /* If YYLEN is nonzero, implement the default value of the action:
+     `$$ = $1'.
+
+     Otherwise, the following line sets YYVAL to garbage.
+     This behavior is undocumented and Bison
+     users should not rely upon it.  Assigning to YYVAL
+     unconditionally makes the parser a bit smaller, and it avoids a
+     GCC warning that YYVAL may be used uninitialized.  */
+  yyval = yyvsp[1-yylen];
+
+
+  YY_REDUCE_PRINT (yyn);
+  switch (yyn)
+    {
+        case 2:
+#line 137 "./cond.y"
+    {
+            COND_input* cond = (COND_input*) info;
+            cond->result = yyvsp[0].value;
+        ;}
+    break;
+
+  case 3:
+#line 145 "./cond.y"
+    {
+            yyval.value = yyvsp[0].value;
+        ;}
+    break;
+
+  case 4:
+#line 149 "./cond.y"
+    {
+            yyval.value = yyvsp[-2].value || yyvsp[0].value;
+        ;}
+    break;
+
+  case 5:
+#line 156 "./cond.y"
+    {
+            yyval.value = yyvsp[0].value;
+        ;}
+    break;
+
+  case 6:
+#line 160 "./cond.y"
+    {
+            yyval.value = yyvsp[-2].value && yyvsp[0].value;
+        ;}
+    break;
+
+  case 7:
+#line 167 "./cond.y"
+    {
+            yyval.value = yyvsp[0].value;
+        ;}
+    break;
+
+  case 8:
+#line 171 "./cond.y"
+    {
+            yyval.value = ! yyvsp[0].value;
+        ;}
+    break;
+
+  case 9:
+#line 179 "./cond.y"
+    {
+            yyval.value = yyvsp[0].value;
+        ;}
+    break;
+
+  case 10:
+#line 183 "./cond.y"
+    {
+            yyval.value = (yyvsp[0].string && yyvsp[0].string[0]) ? MSICONDITION_TRUE : MSICONDITION_FALSE;
+            msi_free( yyvsp[0].string );
+        ;}
+    break;
+
+  case 11:
+#line 188 "./cond.y"
+    {
+            yyval.value = yyvsp[-1].fn_comp_int( yyvsp[-2].value, yyvsp[0].value );
+        ;}
+    break;
+
+  case 12:
+#line 192 "./cond.y"
+    {
+            yyval.value = yyvsp[-1].fn_comp_str( yyvsp[-2].string, yyvsp[0].string, FALSE );
+            msi_free( yyvsp[-2].string );
+            msi_free( yyvsp[0].string );
+        ;}
+    break;
+
+  case 13:
+#line 198 "./cond.y"
+    {
+            yyval.value = yyvsp[-1].fn_comp_str( yyvsp[-3].string, yyvsp[0].string, TRUE );
+            msi_free( yyvsp[-3].string );
+            msi_free( yyvsp[0].string );
+        ;}
+    break;
+
+  case 14:
+#line 204 "./cond.y"
+    {
+            yyval.value = yyvsp[-1].fn_comp_m1( yyvsp[-2].string, yyvsp[0].value );
+            msi_free( yyvsp[-2].string );
+        ;}
+    break;
+
+  case 15:
+#line 209 "./cond.y"
+    {
+            yyval.value = yyvsp[-1].fn_comp_m2( yyvsp[-2].value, yyvsp[0].string );
+            msi_free( yyvsp[0].string );
+        ;}
+    break;
+
+  case 16:
+#line 214 "./cond.y"
+    {
+            yyval.value = yyvsp[-1].value;
+        ;}
+    break;
+
+  case 17:
+#line 222 "./cond.y"
+    {
+            yyval.fn_comp_int = comp_eq_i;
+        ;}
+    break;
+
+  case 18:
+#line 226 "./cond.y"
+    {
+            yyval.fn_comp_int = comp_ne_i;
+        ;}
+    break;
+
+  case 19:
+#line 230 "./cond.y"
+    {
+            yyval.fn_comp_int = comp_lt_i;
+        ;}
+    break;
+
+  case 20:
+#line 234 "./cond.y"
+    {
+            yyval.fn_comp_int = comp_gt_i;
+        ;}
+    break;
+
+  case 21:
+#line 238 "./cond.y"
+    {
+            yyval.fn_comp_int = comp_le_i;
+        ;}
+    break;
+
+  case 22:
+#line 242 "./cond.y"
+    {
+            yyval.fn_comp_int = comp_ge_i;
+        ;}
+    break;
+
+  case 23:
+#line 247 "./cond.y"
+    {
+            yyval.fn_comp_int = comp_bitand;
+        ;}
+    break;
+
+  case 24:
+#line 251 "./cond.y"
+    {
+            yyval.fn_comp_int = comp_highcomp;
+        ;}
+    break;
+
+  case 25:
+#line 255 "./cond.y"
+    {
+            yyval.fn_comp_int = comp_lowcomp;
+        ;}
+    break;
+
+  case 26:
+#line 263 "./cond.y"
+    {
+            yyval.fn_comp_str = comp_eq_s;
+        ;}
+    break;
+
+  case 27:
+#line 267 "./cond.y"
+    {
+            yyval.fn_comp_str = comp_ne_s;
+        ;}
+    break;
+
+  case 28:
+#line 271 "./cond.y"
+    {
+            yyval.fn_comp_str = comp_lt_s;
+        ;}
+    break;
+
+  case 29:
+#line 275 "./cond.y"
+    {
+            yyval.fn_comp_str = comp_gt_s;
+        ;}
+    break;
+
+  case 30:
+#line 279 "./cond.y"
+    {
+            yyval.fn_comp_str = comp_le_s;
+        ;}
+    break;
+
+  case 31:
+#line 283 "./cond.y"
+    {
+            yyval.fn_comp_str = comp_ge_s;
+        ;}
+    break;
+
+  case 32:
+#line 288 "./cond.y"
+    {
+            yyval.fn_comp_str = comp_substring;
+        ;}
+    break;
+
+  case 33:
+#line 292 "./cond.y"
+    {
+            yyval.fn_comp_str = comp_start;
+        ;}
+    break;
+
+  case 34:
+#line 296 "./cond.y"
+    {
+            yyval.fn_comp_str = comp_end;
+        ;}
+    break;
+
+  case 35:
+#line 304 "./cond.y"
+    {
+            yyval.fn_comp_m1 = comp_eq_m1;
+        ;}
+    break;
+
+  case 36:
+#line 308 "./cond.y"
+    {
+            yyval.fn_comp_m1 = comp_ne_m1;
+        ;}
+    break;
+
+  case 37:
+#line 312 "./cond.y"
+    {
+            yyval.fn_comp_m1 = comp_lt_m1;
+        ;}
+    break;
+
+  case 38:
+#line 316 "./cond.y"
+    {
+            yyval.fn_comp_m1 = comp_gt_m1;
+        ;}
+    break;
+
+  case 39:
+#line 320 "./cond.y"
+    {
+            yyval.fn_comp_m1 = comp_le_m1;
+        ;}
+    break;
+
+  case 40:
+#line 324 "./cond.y"
+    {
+            yyval.fn_comp_m1 = comp_ge_m1;
+        ;}
+    break;
+
+  case 41:
+#line 329 "./cond.y"
+    {
+            yyval.fn_comp_m1 = 0;
+        ;}
+    break;
+
+  case 42:
+#line 333 "./cond.y"
+    {
+            yyval.fn_comp_m1 = 0;
+        ;}
+    break;
+
+  case 43:
+#line 337 "./cond.y"
+    {
+            yyval.fn_comp_m1 = 0;
+        ;}
+    break;
+
+  case 44:
+#line 345 "./cond.y"
+    {
+            yyval.fn_comp_m2 = comp_eq_m2;
+        ;}
+    break;
+
+  case 45:
+#line 349 "./cond.y"
+    {
+            yyval.fn_comp_m2 = comp_ne_m2;
+        ;}
+    break;
+
+  case 46:
+#line 353 "./cond.y"
+    {
+            yyval.fn_comp_m2 = comp_lt_m2;
+        ;}
+    break;
+
+  case 47:
+#line 357 "./cond.y"
+    {
+            yyval.fn_comp_m2 = comp_gt_m2;
+        ;}
+    break;
+
+  case 48:
+#line 361 "./cond.y"
+    {
+            yyval.fn_comp_m2 = comp_le_m2;
+        ;}
+    break;
+
+  case 49:
+#line 365 "./cond.y"
+    {
+            yyval.fn_comp_m2 = comp_ge_m2;
+        ;}
+    break;
+
+  case 50:
+#line 370 "./cond.y"
+    {
+            yyval.fn_comp_m2 = 0;
+        ;}
+    break;
+
+  case 51:
+#line 374 "./cond.y"
+    {
+            yyval.fn_comp_m2 = 0;
+        ;}
+    break;
+
+  case 52:
+#line 378 "./cond.y"
+    {
+            yyval.fn_comp_m2 = 0;
+        ;}
+    break;
+
+  case 53:
+#line 385 "./cond.y"
+    {
+            yyval.value = yyvsp[0].value;
+        ;}
+    break;
+
+  case 54:
+#line 389 "./cond.y"
+    {
+            yyval.value = yyvsp[0].value;
+        ;}
+    break;
+
+  case 55:
+#line 396 "./cond.y"
+    {
+        yyval.string = yyvsp[0].string;
+    ;}
+    break;
+
+  case 56:
+#line 400 "./cond.y"
+    {
+        yyval.string = yyvsp[0].string;
+    ;}
+    break;
+
+  case 57:
+#line 407 "./cond.y"
+    {
+            yyval.string = COND_GetLiteral(&yyvsp[0].str);
+            if( !yyval.string )
+                YYABORT;
+        ;}
+    break;
+
+  case 58:
+#line 416 "./cond.y"
+    {
+            COND_input* cond = (COND_input*) info;
+            INSTALLSTATE install = INSTALLSTATE_UNKNOWN, action = INSTALLSTATE_UNKNOWN;
+      
+            MSI_GetComponentStateW(cond->package, yyvsp[0].string, &install, &action );
+            yyval.value = action;
+            msi_free( yyvsp[0].string );
+        ;}
+    break;
+
+  case 59:
+#line 425 "./cond.y"
+    {
+            COND_input* cond = (COND_input*) info;
+            INSTALLSTATE install = INSTALLSTATE_UNKNOWN, action = INSTALLSTATE_UNKNOWN;
+      
+            MSI_GetComponentStateW(cond->package, yyvsp[0].string, &install, &action );
+            yyval.value = install;
+            msi_free( yyvsp[0].string );
+        ;}
+    break;
+
+  case 60:
+#line 434 "./cond.y"
+    {
+            COND_input* cond = (COND_input*) info;
+            INSTALLSTATE install = INSTALLSTATE_UNKNOWN, action = INSTALLSTATE_UNKNOWN;
+      
+            MSI_GetFeatureStateW(cond->package, yyvsp[0].string, &install, &action );
+            yyval.value = action;
+            msi_free( yyvsp[0].string );
+        ;}
+    break;
+
+  case 61:
+#line 443 "./cond.y"
+    {
+            COND_input* cond = (COND_input*) info;
+            INSTALLSTATE install = INSTALLSTATE_UNKNOWN, action = INSTALLSTATE_UNKNOWN;
+      
+            MSI_GetFeatureStateW(cond->package, yyvsp[0].string, &install, &action );
+            yyval.value = install;
+            msi_free( yyvsp[0].string );
+        ;}
+    break;
+
+  case 62:
+#line 455 "./cond.y"
+    {
+            DWORD sz;
+            COND_input* cond = (COND_input*) info;
+
+            sz = 0;
+            MSI_GetPropertyW(cond->package, yyvsp[0].string, NULL, &sz);
+            if (sz == 0)
+            {
+                yyval.string = msi_alloc( sizeof(WCHAR));
+                yyval.string[0] = 0;
+            }
+            else
+            {
+                sz ++;
+                yyval.string = msi_alloc( sz*sizeof (WCHAR) );
+
+                /* Lookup the identifier */
+
+                MSI_GetPropertyW(cond->package,yyvsp[0].string,yyval.string,&sz);
+            }
+            msi_free( yyvsp[0].string );
+        ;}
+    break;
+
+  case 63:
+#line 478 "./cond.y"
+    {
+            UINT len = GetEnvironmentVariableW( yyvsp[0].string, NULL, 0 );
+            if( len++ )
+            {
+                yyval.string = msi_alloc( len*sizeof (WCHAR) );
+                if( yyval.string )
+                    GetEnvironmentVariableW( yyvsp[0].string, yyval.string, len );
+            }
+            msi_free( yyvsp[0].string );
+        ;}
+    break;
+
+  case 64:
+#line 492 "./cond.y"
+    {
+            yyval.string = COND_GetString(&yyvsp[0].str);
+            if( !yyval.string )
+                YYABORT;
+        ;}
+    break;
+
+  case 65:
+#line 501 "./cond.y"
+    {
+            LPWSTR szNum = COND_GetString(&yyvsp[0].str);
+            if( !szNum )
+                YYABORT;
+            yyval.value = atoiW( szNum );
+            msi_free( szNum );
+        ;}
+    break;
+
+
+    }
+
+/* Line 1000 of yacc.c.  */
+#line 1733 "cond.tab.c"
+\f
+  yyvsp -= yylen;
+  yyssp -= yylen;
+
+
+  YY_STACK_PRINT (yyss, yyssp);
+
+  *++yyvsp = yyval;
+
+
+  /* Now `shift' the result of the reduction.  Determine what state
+     that goes to, based on the state we popped back to and the rule
+     number reduced by.  */
+
+  yyn = yyr1[yyn];
+
+  yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+  if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+    yystate = yytable[yystate];
+  else
+    yystate = yydefgoto[yyn - YYNTOKENS];
+
+  goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+  /* If not already recovering from an error, report this error.  */
+  if (!yyerrstatus)
+    {
+      ++yynerrs;
+#if YYERROR_VERBOSE
+      yyn = yypact[yystate];
+
+      if (YYPACT_NINF < yyn && yyn < YYLAST)
+       {
+         YYSIZE_T yysize = 0;
+         int yytype = YYTRANSLATE (yychar);
+         const char* yyprefix;
+         char *yymsg;
+         int yyx;
+
+         /* Start YYX at -YYN if negative to avoid negative indexes in
+            YYCHECK.  */
+         int yyxbegin = yyn < 0 ? -yyn : 0;
+
+         /* Stay within bounds of both yycheck and yytname.  */
+         int yychecklim = YYLAST - yyn;
+         int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+         int yycount = 0;
+
+         yyprefix = ", expecting ";
+         for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+           if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+             {
+               yysize += yystrlen (yyprefix) + yystrlen (yytname [yyx]);
+               yycount += 1;
+               if (yycount == 5)
+                 {
+                   yysize = 0;
+                   break;
+                 }
+             }
+         yysize += (sizeof ("syntax error, unexpected ")
+                    + yystrlen (yytname[yytype]));
+         yymsg = (char *) YYSTACK_ALLOC (yysize);
+         if (yymsg != 0)
+           {
+             char *yyp = yystpcpy (yymsg, "syntax error, unexpected ");
+             yyp = yystpcpy (yyp, yytname[yytype]);
+
+             if (yycount < 5)
+               {
+                 yyprefix = ", expecting ";
+                 for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+                   if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+                     {
+                       yyp = yystpcpy (yyp, yyprefix);
+                       yyp = yystpcpy (yyp, yytname[yyx]);
+                       yyprefix = " or ";
+                     }
+               }
+             yyerror (yymsg);
+             YYSTACK_FREE (yymsg);
+           }
+         else
+           yyerror ("syntax error; also virtual memory exhausted");
+       }
+      else
+#endif /* YYERROR_VERBOSE */
+       yyerror ("syntax error");
+    }
+
+
+
+  if (yyerrstatus == 3)
+    {
+      /* If just tried and failed to reuse lookahead token after an
+        error, discard it.  */
+
+      if (yychar <= YYEOF)
+        {
+          /* If at end of input, pop the error token,
+            then the rest of the stack, then return failure.  */
+         if (yychar == YYEOF)
+            for (;;)
+              {
+                YYPOPSTACK;
+                if (yyssp == yyss)
+                  YYABORT;
+                YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp);
+                yydestruct (yystos[*yyssp], yyvsp);
+              }
+        }
+      else
+       {
+         YYDSYMPRINTF ("Error: discarding", yytoken, &yylval, &yylloc);
+         yydestruct (yytoken, &yylval);
+         yychar = YYEMPTY;
+
+       }
+    }
+
+  /* Else will try to reuse lookahead token after shifting the error
+     token.  */
+  goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR.  |
+`---------------------------------------------------*/
+yyerrorlab:
+
+#ifdef __GNUC__
+  /* Pacify GCC when the user code never invokes YYERROR and the label
+     yyerrorlab therefore never appears in user code.  */
+  if (0)
+     goto yyerrorlab;
+#endif
+
+  yyvsp -= yylen;
+  yyssp -= yylen;
+  yystate = *yyssp;
+  goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR.  |
+`-------------------------------------------------------------*/
+yyerrlab1:
+  yyerrstatus = 3;     /* Each real token shifted decrements this.  */
+
+  for (;;)
+    {
+      yyn = yypact[yystate];
+      if (yyn != YYPACT_NINF)
+       {
+         yyn += YYTERROR;
+         if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+           {
+             yyn = yytable[yyn];
+             if (0 < yyn)
+               break;
+           }
+       }
+
+      /* Pop the current state because it cannot handle the error token.  */
+      if (yyssp == yyss)
+       YYABORT;
+
+      YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp);
+      yydestruct (yystos[yystate], yyvsp);
+      YYPOPSTACK;
+      yystate = *yyssp;
+      YY_STACK_PRINT (yyss, yyssp);
+    }
+
+  if (yyn == YYFINAL)
+    YYACCEPT;
+
+  YYDPRINTF ((stderr, "Shifting error token, "));
+
+  *++yyvsp = yylval;
+
+
+  yystate = yyn;
+  goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here.  |
+`-------------------------------------*/
+yyacceptlab:
+  yyresult = 0;
+  goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here.  |
+`-----------------------------------*/
+yyabortlab:
+  yyresult = 1;
+  goto yyreturn;
+
+#ifndef yyoverflow
+/*----------------------------------------------.
+| yyoverflowlab -- parser overflow comes here.  |
+`----------------------------------------------*/
+yyoverflowlab:
+  yyerror ("parser stack overflow");
+  yyresult = 2;
+  /* Fall through.  */
+#endif
+
+yyreturn:
+#ifndef yyoverflow
+  if (yyss != yyssa)
+    YYSTACK_FREE (yyss);
+#endif
+  return yyresult;
+}
+
+
+#line 510 "./cond.y"
+
+
+
+static int COND_IsAlpha( WCHAR x )
+{
+    return( ( ( x >= 'A' ) && ( x <= 'Z' ) ) ||
+            ( ( x >= 'a' ) && ( x <= 'z' ) ) ||
+            ( ( x == '_' ) ) );
+}
+
+static int COND_IsNumber( WCHAR x )
+{
+    return( (( x >= '0' ) && ( x <= '9' ))  || (x =='-') || (x =='.') );
+}
+
+
+/* the mess of comparison functions */
+
+static INT comp_lt_i(INT a, INT b)
+{ return (a < b); }
+static INT comp_gt_i(INT a, INT b)
+{ return (a > b); }
+static INT comp_le_i(INT a, INT b)
+{ return (a <= b); }
+static INT comp_ge_i(INT a, INT b)
+{ return (a >= b); }
+static INT comp_eq_i(INT a, INT b)
+{ return (a == b); }
+static INT comp_ne_i(INT a, INT b)
+{ return (a != b); }
+static INT comp_bitand(INT a, INT b)
+{ return a & b;}
+static INT comp_highcomp(INT a, INT b)
+{ return HIWORD(a)==b; }
+static INT comp_lowcomp(INT a, INT b)
+{ return LOWORD(a)==b; }
+
+static INT comp_eq_s(LPWSTR a, LPWSTR b, BOOL casless)
+{ if (casless) return !strcmpiW(a,b); else return !strcmpW(a,b);}
+static INT comp_ne_s(LPWSTR a, LPWSTR b, BOOL casless)
+{ if (casless) return strcmpiW(a,b); else  return strcmpW(a,b);}
+static INT comp_lt_s(LPWSTR a, LPWSTR b, BOOL casless)
+{ if (casless) return strcmpiW(a,b)<0; else return strcmpW(a,b)<0;}
+static INT comp_gt_s(LPWSTR a, LPWSTR b, BOOL casless)
+{ if (casless) return strcmpiW(a,b)>0; else return strcmpW(a,b)>0;}
+static INT comp_le_s(LPWSTR a, LPWSTR b, BOOL casless)
+{ if (casless) return strcmpiW(a,b)<=0; else return strcmpW(a,b)<=0;}
+static INT comp_ge_s(LPWSTR a, LPWSTR b, BOOL casless)
+{ if (casless) return strcmpiW(a,b)>=0; else return  strcmpW(a,b)>=0;}
+static INT comp_substring(LPWSTR a, LPWSTR b, BOOL casless)
+/* ERROR NOT WORKING REWRITE */
+{ if (casless) return strstrW(a,b)!=NULL; else return strstrW(a,b)!=NULL;}
+static INT comp_start(LPWSTR a, LPWSTR b, BOOL casless)
+{ if (casless) return strncmpiW(a,b,strlenW(b))==0; 
+  else return strncmpW(a,b,strlenW(b))==0;}
+static INT comp_end(LPWSTR a, LPWSTR b, BOOL casless)
+{ 
+    int i = strlenW(a); 
+    int j = strlenW(b); 
+    if (j>i)
+        return 0;
+    if (casless) return (!strcmpiW(&a[i-j-1],b));
+    else  return (!strcmpW(&a[i-j-1],b));
+}
+
+
+static INT comp_eq_m1(LPWSTR a, INT b)
+{ if (COND_IsNumber(a[0])) return atoiW(a)==b; else return 0;}
+static INT comp_ne_m1(LPWSTR a, INT b)
+{ if (COND_IsNumber(a[0])) return atoiW(a)!=b; else return 1;}
+static INT comp_lt_m1(LPWSTR a, INT b)
+{ if (COND_IsNumber(a[0])) return atoiW(a)<b; else return 0;}
+static INT comp_gt_m1(LPWSTR a, INT b)
+{ if (COND_IsNumber(a[0])) return atoiW(a)>b; else return 0;}
+static INT comp_le_m1(LPWSTR a, INT b)
+{ if (COND_IsNumber(a[0])) return atoiW(a)<=b; else return 0;}
+static INT comp_ge_m1(LPWSTR a, INT b)
+{ if (COND_IsNumber(a[0])) return atoiW(a)>=b; else return 0;}
+
+static INT comp_eq_m2(INT a, LPWSTR b)
+{ if (COND_IsNumber(b[0])) return a == atoiW(b); else return 0;}
+static INT comp_ne_m2(INT a, LPWSTR b)
+{ if (COND_IsNumber(b[0])) return a != atoiW(b); else return 1;}
+static INT comp_lt_m2(INT a, LPWSTR b)
+{ if (COND_IsNumber(b[0])) return a < atoiW(b); else return 0;}
+static INT comp_gt_m2(INT a, LPWSTR b)
+{ if (COND_IsNumber(b[0])) return a > atoiW(b); else return 0;}
+static INT comp_le_m2(INT a, LPWSTR b)
+{ if (COND_IsNumber(b[0])) return a <= atoiW(b); else return 0;}
+static INT comp_ge_m2(INT a, LPWSTR b)
+{ if (COND_IsNumber(b[0])) return a >= atoiW(b); else return 0;}
+
+
+
+static int COND_IsIdent( WCHAR x )
+{
+    return( COND_IsAlpha( x ) || COND_IsNumber( x ) || ( x == '_' ) 
+            || ( x == '#' ) || (x == '.') );
+}
+
+static int COND_GetOne( struct cond_str *str, COND_input *cond )
+{
+    static const WCHAR szNot[] = {'N','O','T',0};
+    static const WCHAR szAnd[] = {'A','N','D',0};
+    static const WCHAR szOr[] = {'O','R',0};
+    WCHAR ch;
+    int rc, len = 1;
+
+    str->data = &cond->str[cond->n];
+
+    ch = str->data[0];
+    switch( ch )
+    {
+    case 0: return 0;
+    case '(': rc = COND_LPAR; break;
+    case ')': rc = COND_RPAR; break;
+    case '&': rc = COND_AMPER; break;
+    case '!': rc = COND_EXCLAM; break;
+    case '$': rc = COND_DOLLARS; break;
+    case '?': rc = COND_QUESTION; break;
+    case '%': rc = COND_PERCENT; break;
+    case ' ': rc = COND_SPACE; break;
+    case '=': rc = COND_EQ; break;
+    case '~': rc = COND_TILDA; break;
+    case '<': rc = COND_LT; break;
+    case '>': rc = COND_GT; break;
+    case '"':
+       {
+           const WCHAR *ch2 = str->data + 1;
+
+
+           while ( *ch2 && *ch2 != '"' )
+               ++ch2;
+           if (*ch2 == '"')
+           {
+               len = ch2 - str->data + 1;
+               rc = COND_LITER;
+               break;
+           }
+       }
+       ERR("Unterminated string\n");
+       rc = COND_ERROR;
+       break;
+    default: 
+        if( COND_IsAlpha( ch ) )
+        {
+            while( COND_IsIdent( str->data[len] ) )
+                len++;
+            rc = COND_IDENT;
+            break;
+        }
+
+        if( COND_IsNumber( ch ) )
+        {
+            while( COND_IsNumber( str->data[len] ) )
+                len++;
+            rc = COND_NUMBER;
+            break;
+        }
+
+        ERR("Got unknown character %c(%x)\n",ch,ch);
+        rc = COND_ERROR;
+        break;
+    }
+
+    /* keyword identifiers */
+    if( rc == COND_IDENT )
+    {
+        if( (len==3) && (strncmpiW(str->data,szNot,len)==0) )
+            rc = COND_NOT;
+        else if( (len==3) && (strncmpiW(str->data,szAnd,len)==0) )
+            rc = COND_AND;
+        else if( (len==2) && (strncmpiW(str->data,szOr,len)==0) )
+            rc = COND_OR;
+    }
+
+    cond->n += len;
+    str->len = len;
+
+    return rc;
+}
+
+static int COND_lex( void *COND_lval, COND_input *cond )
+{
+    int rc;
+    struct cond_str *str = COND_lval;
+
+    do {
+        rc = COND_GetOne( str, cond );
+    } while (rc == COND_SPACE);
+    
+    return rc;
+}
+
+static LPWSTR COND_GetString( struct cond_str *str )
+{
+    LPWSTR ret;
+
+    ret = msi_alloc( (str->len+1) * sizeof (WCHAR) );
+    if( ret )
+    {
+        memcpy( ret, str->data, str->len * sizeof(WCHAR));
+        ret[str->len]=0;
+    }
+    TRACE("Got identifier %s\n",debugstr_w(ret));
+    return ret;
+}
+
+static LPWSTR COND_GetLiteral( struct cond_str *str )
+{
+    LPWSTR ret;
+
+    ret = msi_alloc( (str->len-1) * sizeof (WCHAR) );
+    if( ret )
+    {
+        memcpy( ret, str->data+1, (str->len-2) * sizeof(WCHAR) );
+        ret[str->len - 2]=0;
+    }
+    TRACE("Got literal %s\n",debugstr_w(ret));
+    return ret;
+}
+
+static int COND_error(const char *str)
+{
+    return 0;
+}
+
+MSICONDITION MSI_EvaluateConditionW( MSIPACKAGE *package, LPCWSTR szCondition )
+{
+    COND_input cond;
+    MSICONDITION r;
+
+    cond.package = package;
+    cond.str   = szCondition;
+    cond.n     = 0;
+    cond.result = -1;
+    
+    TRACE("Evaluating %s\n",debugstr_w(szCondition));    
+
+    if ( szCondition == NULL || szCondition[0] == 0)
+       r = MSICONDITION_NONE;
+    else if ( !COND_parse( &cond ) )
+        r = cond.result;
+    else
+        r = MSICONDITION_ERROR;
+
+    TRACE("Evaluates to %i\n",r);
+    return r;
+}
+
+MSICONDITION WINAPI MsiEvaluateConditionW( MSIHANDLE hInstall, LPCWSTR szCondition )
+{
+    MSIPACKAGE *package;
+    UINT ret;
+
+    package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE);
+    if( !package)
+        return ERROR_INVALID_HANDLE;
+    ret = MSI_EvaluateConditionW( package, szCondition );
+    msiobj_release( &package->hdr );
+    return ret;
+}
+
+MSICONDITION WINAPI MsiEvaluateConditionA( MSIHANDLE hInstall, LPCSTR szCondition )
+{
+    LPWSTR szwCond = NULL;
+    MSICONDITION r;
+
+    if( szCondition )
+    {
+        UINT len = MultiByteToWideChar( CP_ACP, 0, szCondition, -1, NULL, 0 );
+        szwCond = msi_alloc( len * sizeof (WCHAR) );
+        MultiByteToWideChar( CP_ACP, 0, szCondition, -1, szwCond, len );
+    }
+
+    r = MsiEvaluateConditionW( hInstall, szwCond );
+
+    msi_free( szwCond );
+
+    return r;
+}
+
diff --git a/reactos/dll/msi/cond.tab.h b/reactos/dll/msi/cond.tab.h
new file mode 100644 (file)
index 0000000..f37273b
--- /dev/null
@@ -0,0 +1,99 @@
+/* A Bison parser, made by GNU Bison 1.875c.  */
+
+/* Skeleton parser for Yacc-like parsing with Bison,
+   Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program 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 General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, when this file is copied by Bison into a
+   Bison output file, you may use that output file without restriction.
+   This special exception was added by the Free Software Foundation
+   in version 1.24 of Bison.  */
+
+/* Tokens.  */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+   /* Put the tokens into the symbol table, so that GDB and other debuggers
+      know about them.  */
+   enum yytokentype {
+     COND_SPACE = 258,
+     COND_EOF = 259,
+     COND_OR = 260,
+     COND_AND = 261,
+     COND_NOT = 262,
+     COND_LT = 263,
+     COND_GT = 264,
+     COND_EQ = 265,
+     COND_LPAR = 266,
+     COND_RPAR = 267,
+     COND_TILDA = 268,
+     COND_PERCENT = 269,
+     COND_DOLLARS = 270,
+     COND_QUESTION = 271,
+     COND_AMPER = 272,
+     COND_EXCLAM = 273,
+     COND_IDENT = 274,
+     COND_NUMBER = 275,
+     COND_LITER = 276,
+     COND_ERROR = 277
+   };
+#endif
+#define COND_SPACE 258
+#define COND_EOF 259
+#define COND_OR 260
+#define COND_AND 261
+#define COND_NOT 262
+#define COND_LT 263
+#define COND_GT 264
+#define COND_EQ 265
+#define COND_LPAR 266
+#define COND_RPAR 267
+#define COND_TILDA 268
+#define COND_PERCENT 269
+#define COND_DOLLARS 270
+#define COND_QUESTION 271
+#define COND_AMPER 272
+#define COND_EXCLAM 273
+#define COND_IDENT 274
+#define COND_NUMBER 275
+#define COND_LITER 276
+#define COND_ERROR 277
+
+
+
+
+#if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED)
+#line 106 "./cond.y"
+typedef union YYSTYPE {
+    struct cond_str str;
+    LPWSTR    string;
+    INT       value;
+    comp_int  fn_comp_int;
+    comp_str  fn_comp_str;
+    comp_m1   fn_comp_m1;
+    comp_m2   fn_comp_m2;
+} YYSTYPE;
+/* Line 1275 of yacc.c.  */
+#line 91 "cond.tab.h"
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+
+
+
+
diff --git a/reactos/dll/msi/cond.y b/reactos/dll/msi/cond.y
new file mode 100644 (file)
index 0000000..7abf2d3
--- /dev/null
@@ -0,0 +1,717 @@
+%{
+
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2003 Mike McCormack for CodeWeavers
+ *
+ * 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 <stdlib.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winuser.h"
+#include "wine/debug.h"
+#include "wine/unicode.h"
+
+#include "msi.h"
+#include "msiquery.h"
+#include "msipriv.h"
+#include "action.h"
+
+#define YYLEX_PARAM info
+#define YYPARSE_PARAM info
+
+static int COND_error(const char *str);
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+typedef struct tag_yyinput
+{
+    MSIPACKAGE *package;
+    LPCWSTR str;
+    INT    n;
+    MSICONDITION result;
+} COND_input;
+
+struct cond_str {
+    LPCWSTR data;
+    INT len;
+};
+
+static LPWSTR COND_GetString( struct cond_str *str );
+static LPWSTR COND_GetLiteral( struct cond_str *str );
+static int COND_lex( void *COND_lval, COND_input *info);
+static const WCHAR szEmpty[] = { 0 };
+
+static INT compare_int( INT a, INT operator, INT b );
+static INT compare_string( LPCWSTR a, INT operator, LPCWSTR b );
+
+static INT compare_and_free_strings( LPWSTR a, INT op, LPWSTR b )
+{
+    INT r;
+
+    r = compare_string( a, op, b );
+    msi_free( a );
+    msi_free( b );
+    return r;
+}
+
+static BOOL num_from_prop( LPCWSTR p, INT *val )
+{
+    INT ret = 0, sign = 1;
+
+    if (!p)
+        return FALSE;
+    if (*p == '-')
+    {
+        sign = -1;
+        p++;
+    }
+    if (!*p)
+        return FALSE;
+    while (*p)
+    {
+        if( *p < '0' || *p > '9' )
+            return FALSE;
+        ret = ret*10 + (*p - '0');
+        p++;
+    }
+    *val = ret*sign;
+    return TRUE;
+}
+
+%}
+
+%pure-parser
+
+%union
+{
+    struct cond_str str;
+    LPWSTR    string;
+    INT       value;
+}
+
+%token COND_SPACE COND_EOF COND_SPACE
+%token COND_OR COND_AND COND_NOT COND_XOR COND_IMP COND_EQV
+%token COND_LT COND_GT COND_EQ COND_NE COND_GE COND_LE
+%token COND_ILT COND_IGT COND_IEQ COND_INE COND_IGE COND_ILE
+%token COND_LPAR COND_RPAR COND_TILDA COND_SS COND_ISS
+%token COND_ILHS COND_IRHS COND_LHS COND_RHS
+%token COND_PERCENT COND_DOLLARS COND_QUESTION COND_AMPER COND_EXCLAM
+%token <str> COND_IDENT <str> COND_NUMBER <str> COND_LITER
+
+%nonassoc COND_ERROR COND_EOF
+
+%type <value> expression boolean_term boolean_factor 
+%type <value> value_i integer operator
+%type <string> identifier symbol_s value_s literal
+
+%%
+
+condition:
+    expression 
+        {
+            COND_input* cond = (COND_input*) info;
+            cond->result = $1;
+        }
+  | /* empty */
+        {
+            COND_input* cond = (COND_input*) info;
+            cond->result = MSICONDITION_NONE;
+        }
+    ;
+
+expression:
+    boolean_term 
+        {
+            $$ = $1;
+        }
+  | expression COND_OR boolean_term
+        {
+            $$ = $1 || $3;
+        }
+  | expression COND_IMP boolean_term
+        {
+            $$ = !$1 || $3;
+        }
+  | expression COND_XOR boolean_term
+        {
+            $$ = ( $1 || $3 ) && !( $1 && $3 );
+        }
+  | expression COND_EQV boolean_term
+        {
+            $$ = ( $1 && $3 ) || ( !$1 && !$3 );
+        }
+    ;
+
+boolean_term:
+    boolean_factor
+        {
+            $$ = $1;
+        }
+  | boolean_term COND_AND boolean_factor
+        {
+            $$ = $1 && $3;
+        }
+    ;
+
+boolean_factor:
+    COND_NOT boolean_factor
+        {
+            $$ = $2 ? 0 : 1;
+        }
+  | value_i
+        {
+            $$ = $1 ? 1 : 0;
+        }
+  | value_s
+        {
+            $$ = ($1 && $1[0]) ? 1 : 0;
+        }
+  | value_i operator value_i
+        {
+            $$ = compare_int( $1, $2, $3 );
+        }
+  | symbol_s operator value_i
+        {
+            int num;
+            if (num_from_prop( $1, &num ))
+                $$ = compare_int( num, $2, $3 );
+            else 
+                $$ = ($2 == COND_NE || $2 == COND_INE );
+        }
+  | value_i operator symbol_s
+        {
+            int num;
+            if (num_from_prop( $3, &num ))
+                $$ = compare_int( $1, $2, num );
+            else 
+                $$ = ($2 == COND_NE || $2 == COND_INE );
+        }
+  | symbol_s operator symbol_s
+        {
+            $$ = compare_and_free_strings( $1, $2, $3 );
+        }
+  | symbol_s operator literal
+        {
+            $$ = compare_and_free_strings( $1, $2, $3 );
+        }
+  | literal operator symbol_s
+        {
+            $$ = compare_and_free_strings( $1, $2, $3 );
+        }
+  | literal operator literal
+        {
+            $$ = compare_and_free_strings( $1, $2, $3 );
+        }
+  | literal operator value_i
+        {
+            $$ = 0;
+        }
+  | value_i operator literal
+        {
+            $$ = 0;
+        }
+  | COND_LPAR expression COND_RPAR
+        {
+            $$ = $2;
+        }
+    ;
+
+operator:
+    /* common functions */
+    COND_EQ { $$ = COND_EQ; }
+  | COND_NE { $$ = COND_NE; }
+  | COND_LT { $$ = COND_LT; }
+  | COND_GT { $$ = COND_GT; }
+  | COND_LE { $$ = COND_LE; }
+  | COND_GE { $$ = COND_GE; }
+  | COND_SS { $$ = COND_SS; }
+  | COND_IEQ { $$ = COND_IEQ; }
+  | COND_INE { $$ = COND_INE; }
+  | COND_ILT { $$ = COND_ILT; }
+  | COND_IGT { $$ = COND_IGT; }
+  | COND_ILE { $$ = COND_ILE; }
+  | COND_IGE { $$ = COND_IGE; }
+  | COND_ISS { $$ = COND_ISS; }
+  | COND_LHS { $$ = COND_LHS; }
+  | COND_RHS { $$ = COND_RHS; }
+  | COND_ILHS { $$ = COND_ILHS; }
+  | COND_IRHS { $$ = COND_IRHS; }
+    ;
+
+value_s:
+    symbol_s
+    {
+        $$ = $1;
+    } 
+  | literal
+    {
+        $$ = $1;
+    }
+    ;
+
+literal:
+    COND_LITER
+        {
+            $$ = COND_GetLiteral(&$1);
+            if( !$$ )
+                YYABORT;
+        }
+    ;
+
+value_i:
+    integer
+        {
+            $$ = $1;
+        }
+  | COND_DOLLARS identifier
+        {
+            COND_input* cond = (COND_input*) info;
+            INSTALLSTATE install = INSTALLSTATE_UNKNOWN, action = INSTALLSTATE_UNKNOWN;
+      
+            MSI_GetComponentStateW(cond->package, $2, &install, &action );
+            $$ = action;
+            msi_free( $2 );
+        }
+  | COND_QUESTION identifier
+        {
+            COND_input* cond = (COND_input*) info;
+            INSTALLSTATE install = INSTALLSTATE_UNKNOWN, action = INSTALLSTATE_UNKNOWN;
+      
+            MSI_GetComponentStateW(cond->package, $2, &install, &action );
+            $$ = install;
+            msi_free( $2 );
+        }
+  | COND_AMPER identifier
+        {
+            COND_input* cond = (COND_input*) info;
+            INSTALLSTATE install = INSTALLSTATE_UNKNOWN, action = INSTALLSTATE_UNKNOWN;
+      
+            MSI_GetFeatureStateW(cond->package, $2, &install, &action );
+            $$ = action;
+            msi_free( $2 );
+        }
+  | COND_EXCLAM identifier
+        {
+            COND_input* cond = (COND_input*) info;
+            INSTALLSTATE install = INSTALLSTATE_UNKNOWN, action = INSTALLSTATE_UNKNOWN;
+      
+            MSI_GetFeatureStateW(cond->package, $2, &install, &action );
+            $$ = install;
+            msi_free( $2 );
+        }
+    ;
+
+symbol_s:
+    identifier
+        {
+            COND_input* cond = (COND_input*) info;
+
+            $$ = msi_dup_property( cond->package, $1 );
+            msi_free( $1 );
+        }
+    | COND_PERCENT identifier
+        {
+            UINT len = GetEnvironmentVariableW( $2, NULL, 0 );
+            $$ = NULL;
+            if (len++)
+            {
+                $$ = msi_alloc( len*sizeof (WCHAR) );
+                GetEnvironmentVariableW( $2, $$, len );
+            }
+            msi_free( $2 );
+        }
+    ;
+
+identifier:
+    COND_IDENT
+        {
+            $$ = COND_GetString(&$1);
+            if( !$$ )
+                YYABORT;
+        }
+    ;
+
+integer:
+    COND_NUMBER
+        {
+            LPWSTR szNum = COND_GetString(&$1);
+            if( !szNum )
+                YYABORT;
+            $$ = atoiW( szNum );
+            msi_free( szNum );
+        }
+    ;
+
+%%
+
+
+static int COND_IsAlpha( WCHAR x )
+{
+    return( ( ( x >= 'A' ) && ( x <= 'Z' ) ) ||
+            ( ( x >= 'a' ) && ( x <= 'z' ) ) ||
+            ( ( x == '_' ) ) );
+}
+
+static int COND_IsNumber( WCHAR x )
+{
+    return( (( x >= '0' ) && ( x <= '9' ))  || (x =='-') || (x =='.') );
+}
+
+static WCHAR *strstriW( const WCHAR *str, const WCHAR *sub )
+{
+    LPWSTR strlower, sublower, r;
+    strlower = CharLowerW( strdupW( str ) );
+    sublower = CharLowerW( strdupW( sub ) );
+    r = strstrW( strlower, sublower );
+    if (r)
+        r = (LPWSTR)str + (r - strlower);
+    msi_free( strlower );
+    msi_free( sublower );
+    return r;
+}
+
+static INT compare_string( LPCWSTR a, INT operator, LPCWSTR b )
+{
+    /* null and empty string are equivalent */
+    if (!a) a = szEmpty;
+    if (!b) b = szEmpty;
+
+    /* a or b may be NULL */
+    switch (operator)
+    {
+    case COND_LT:
+        return -1 == lstrcmpW( a, b );
+    case COND_GT:
+        return  1 == lstrcmpW( a, b );
+    case COND_EQ:
+        return  0 == lstrcmpW( a, b );
+    case COND_NE:
+        return  0 != lstrcmpW( a, b );
+    case COND_GE:
+        return -1 != lstrcmpW( a, b );
+    case COND_LE:
+        return  1 != lstrcmpW( a, b );
+    case COND_SS: /* substring */
+        return strstrW( a, b ) ? 1 : 0;
+    case COND_ILT:
+        return -1 == lstrcmpiW( a, b );
+    case COND_IGT:
+        return  1 == lstrcmpiW( a, b );
+    case COND_IEQ:
+        return  0 == lstrcmpiW( a, b );
+    case COND_INE:
+        return  0 != lstrcmpiW( a, b );
+    case COND_IGE:
+        return -1 != lstrcmpiW( a, b );
+    case COND_ILE:
+        return  1 != lstrcmpiW( a, b );
+    case COND_ISS:
+        return strstriW( a, b ) ? 1 : 0;
+    case COND_LHS:
+    case COND_RHS:
+    case COND_ILHS:
+    case COND_IRHS:
+        ERR("unimplemented string comparison\n");
+        break;
+    default:
+        ERR("invalid integer operator\n");
+        return 0;
+    }
+    return 0;
+}
+
+
+static INT compare_int( INT a, INT operator, INT b )
+{
+    switch (operator)
+    {
+    case COND_LT:
+    case COND_ILT:
+        return a < b;
+    case COND_GT:
+    case COND_IGT:
+        return a > b;
+    case COND_EQ:
+    case COND_IEQ:
+        return a == b;
+    case COND_NE:
+    case COND_INE:
+        return a != b;
+    case COND_GE:
+    case COND_IGE:
+        return a >= b;
+    case COND_LE:
+    case COND_ILE:
+        return a >= b;
+    case COND_SS:
+    case COND_ISS:
+        return ( a & b ) ? 1 : 0;
+    case COND_RHS:
+        return ( ( a & 0xffff ) == b ) ? 1 : 0;
+    case COND_LHS:
+        return ( ( (a>>16) & 0xffff ) == b ) ? 1 : 0;
+    default:
+        ERR("invalid integer operator\n");
+        return 0;
+    }
+    return 0;
+}
+
+
+static int COND_IsIdent( WCHAR x )
+{
+    return( COND_IsAlpha( x ) || COND_IsNumber( x ) || ( x == '_' ) 
+            || ( x == '#' ) || (x == '.') );
+}
+
+static int COND_GetOperator( COND_input *cond )
+{
+    static const struct {
+        const WCHAR str[4];
+        int id;
+    } table[] = {
+        { {'~','=',0},     COND_IEQ },
+        { {'~','>','=',0}, COND_ILE },
+        { {'~','>','<',0}, COND_ISS },
+        { {'~','>','>',0}, COND_IRHS },
+        { {'~','>',0},     COND_ILT },
+        { {'~','<','>',0}, COND_INE },
+        { {'~','<','=',0}, COND_IGE },
+        { {'~','<','<',0}, COND_ILHS },
+        { {'~','<',0},     COND_IGT },
+        { {'>','=',0},     COND_GE  },
+        { {'>','<',0},     COND_SS  },
+        { {'>','>',0},     COND_LHS },
+        { {'>',0},         COND_GT  },
+        { {'<','>',0},     COND_NE  },
+        { {'<','=',0},     COND_LE  },
+        { {'<','<',0},     COND_RHS },
+        { {'<',0},         COND_LT  },
+        { {0},             0        }
+    };
+    LPCWSTR p = &cond->str[cond->n];
+    int i = 0, len;
+
+    while ( 1 )
+    {
+        len = lstrlenW( table[i].str );
+        if ( !len || 0 == strncmpW( table[i].str, p, len ) )
+            break;
+        i++;
+    }
+    cond->n += len;
+    return table[i].id;
+}
+
+static int COND_GetOne( struct cond_str *str, COND_input *cond )
+{
+    int rc, len = 1;
+    WCHAR ch;
+
+    str->data = &cond->str[cond->n];
+
+    ch = str->data[0];
+
+    switch( ch )
+    {
+    case 0: return 0;
+    case '(': rc = COND_LPAR; break;
+    case ')': rc = COND_RPAR; break;
+    case '&': rc = COND_AMPER; break;
+    case '!': rc = COND_EXCLAM; break;
+    case '$': rc = COND_DOLLARS; break;
+    case '?': rc = COND_QUESTION; break;
+    case '%': rc = COND_PERCENT; break;
+    case ' ': rc = COND_SPACE; break;
+    case '=': rc = COND_EQ; break;
+        break;
+
+    case '~':
+    case '<':
+    case '>':
+        rc = COND_GetOperator( cond );
+        if (!rc)
+            rc = COND_ERROR;
+        return rc;
+    default:
+        rc = 0;
+    }
+
+    if ( rc )
+    {
+        cond->n += len;
+        return rc;
+    }
+
+    if (ch == '"' )
+    {
+        LPCWSTR p = strchrW( str->data + 1, '"' );
+       if (!p)
+            return COND_ERROR;
+        len = p - str->data + 1;
+        rc = COND_LITER;
+    }
+    else if( COND_IsAlpha( ch ) )
+    {
+        static const WCHAR szNot[] = {'N','O','T',0};
+        static const WCHAR szAnd[] = {'A','N','D',0};
+        static const WCHAR szXor[] = {'X','O','R',0};
+        static const WCHAR szEqv[] = {'E','Q','V',0};
+        static const WCHAR szImp[] = {'I','M','P',0};
+        static const WCHAR szOr[] = {'O','R',0};
+
+        while( COND_IsIdent( str->data[len] ) )
+            len++;
+        rc = COND_IDENT;
+
+        if ( len == 3 )
+        {
+            if ( !strncmpiW( str->data, szNot, len ) )
+                rc = COND_NOT;
+            else if( !strncmpiW( str->data, szAnd, len ) )
+                rc = COND_AND;
+            else if( !strncmpiW( str->data, szXor, len ) )
+                rc = COND_XOR;
+            else if( !strncmpiW( str->data, szEqv, len ) )
+                rc = COND_EQV;
+            else if( !strncmpiW( str->data, szImp, len ) )
+                rc = COND_IMP;
+        }
+        else if( (len == 2) && !strncmpiW( str->data, szOr, len ) )
+            rc = COND_OR;
+    }
+    else if( COND_IsNumber( ch ) )
+    {
+        while( COND_IsNumber( str->data[len] ) )
+            len++;
+        rc = COND_NUMBER;
+    }
+    else
+    {
+        ERR("Got unknown character %c(%x)\n",ch,ch);
+        return COND_ERROR;
+    }
+
+    cond->n += len;
+    str->len = len;
+
+    return rc;
+}
+
+static int COND_lex( void *COND_lval, COND_input *cond )
+{
+    int rc;
+    struct cond_str *str = COND_lval;
+
+    do {
+        rc = COND_GetOne( str, cond );
+    } while (rc == COND_SPACE);
+    
+    return rc;
+}
+
+static LPWSTR COND_GetString( struct cond_str *str )
+{
+    LPWSTR ret;
+
+    ret = msi_alloc( (str->len+1) * sizeof (WCHAR) );
+    if( ret )
+    {
+        memcpy( ret, str->data, str->len * sizeof(WCHAR));
+        ret[str->len]=0;
+    }
+    TRACE("Got identifier %s\n",debugstr_w(ret));
+    return ret;
+}
+
+static LPWSTR COND_GetLiteral( struct cond_str *str )
+{
+    LPWSTR ret;
+
+    ret = msi_alloc( (str->len-1) * sizeof (WCHAR) );
+    if( ret )
+    {
+        memcpy( ret, str->data+1, (str->len-2) * sizeof(WCHAR) );
+        ret[str->len - 2]=0;
+    }
+    TRACE("Got literal %s\n",debugstr_w(ret));
+    return ret;
+}
+
+static int COND_error(const char *str)
+{
+    TRACE("%s\n", str );
+    return 0;
+}
+
+MSICONDITION MSI_EvaluateConditionW( MSIPACKAGE *package, LPCWSTR szCondition )
+{
+    COND_input cond;
+    MSICONDITION r;
+
+    TRACE("%s\n", debugstr_w( szCondition ) );
+
+    if ( szCondition == NULL )
+       return MSICONDITION_NONE;
+
+    cond.package = package;
+    cond.str   = szCondition;
+    cond.n     = 0;
+    cond.result = MSICONDITION_ERROR;
+    
+    if ( !COND_parse( &cond ) )
+        r = cond.result;
+    else
+        r = MSICONDITION_ERROR;
+
+    TRACE("%i <- %s\n", r, debugstr_w(szCondition));
+    return r;
+}
+
+MSICONDITION WINAPI MsiEvaluateConditionW( MSIHANDLE hInstall, LPCWSTR szCondition )
+{
+    MSIPACKAGE *package;
+    UINT ret;
+
+    package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE);
+    if( !package)
+        return MSICONDITION_ERROR;
+    ret = MSI_EvaluateConditionW( package, szCondition );
+    msiobj_release( &package->hdr );
+    return ret;
+}
+
+MSICONDITION WINAPI MsiEvaluateConditionA( MSIHANDLE hInstall, LPCSTR szCondition )
+{
+    LPWSTR szwCond = NULL;
+    MSICONDITION r;
+
+    szwCond = strdupAtoW( szCondition );
+    if( szCondition && !szwCond )
+        return MSICONDITION_ERROR;
+
+    r = MsiEvaluateConditionW( hInstall, szwCond );
+    msi_free( szwCond );
+    return r;
+}
diff --git a/reactos/dll/msi/create.c b/reactos/dll/msi/create.c
new file mode 100644 (file)
index 0000000..fb93c62
--- /dev/null
@@ -0,0 +1,245 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2002-2004 Mike McCormack for CodeWeavers
+ *
+ * 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 <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "wine/debug.h"
+#include "msi.h"
+#include "msiquery.h"
+#include "objbase.h"
+#include "objidl.h"
+#include "msipriv.h"
+#include "winnls.h"
+
+#include "query.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msidb);
+
+
+/* below is the query interface to a table */
+
+typedef struct tagMSICREATEVIEW
+{
+    MSIVIEW          view;
+    MSIDATABASE     *db;
+    LPWSTR           name;
+    BOOL             bIsTemp;
+    column_info     *col_info;
+} MSICREATEVIEW;
+
+static UINT CREATE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
+{
+    MSICREATEVIEW *cv = (MSICREATEVIEW*)view;
+
+    TRACE("%p %d %d %p\n", cv, row, col, val );
+
+    return ERROR_FUNCTION_FAILED;
+}
+
+static UINT CREATE_execute( struct tagMSIVIEW *view, MSIRECORD *record )
+{
+    MSICREATEVIEW *cv = (MSICREATEVIEW*)view;
+    column_info *col;
+    UINT r, nField;
+    static const WCHAR szTables[] =  { '_','T','a','b','l','e','s',0 };
+    static const WCHAR szColumns[] = { '_','C','o','l','u','m','n','s',0 };
+    MSIVIEW *tv = NULL;
+    MSIRECORD *rec = NULL;
+
+    TRACE("%p Table %s (%s)\n", cv, debugstr_w(cv->name), 
+          cv->bIsTemp?"temporary":"permanent");
+
+    /* only add tables that don't exist already */
+    if( TABLE_Exists(cv->db, cv->name ) )
+        return ERROR_BAD_QUERY_SYNTAX;
+
+    r = TABLE_CreateView( cv->db, szTables, &tv );
+    TRACE("CreateView returned %x\n", r);
+    if( r )
+        return r;
+
+    r = tv->ops->execute( tv, 0 );
+    TRACE("tv execute returned %x\n", r);
+    if( r )
+        goto err;
+
+    rec = MSI_CreateRecord( 1 );
+    if( !rec )
+        goto err;
+
+    r = MSI_RecordSetStringW( rec, 1, cv->name );
+    if( r )
+        goto err;
+
+    r = tv->ops->insert_row( tv, rec );
+    TRACE("insert_row returned %x\n", r);
+    if( r )
+        goto err;
+
+    tv->ops->delete( tv );
+    tv = NULL;
+
+    msiobj_release( &rec->hdr );
+
+    /* add each column to the _Columns table */
+    r = TABLE_CreateView( cv->db, szColumns, &tv );
+    if( r )
+        return r;
+
+    r = tv->ops->execute( tv, 0 );
+    TRACE("tv execute returned %x\n", r);
+    if( r )
+        goto err;
+
+    rec = MSI_CreateRecord( 4 );
+    if( !rec )
+        goto err;
+
+    r = MSI_RecordSetStringW( rec, 1, cv->name );
+    if( r )
+        goto err;
+
+    /*
+     * need to set the table, column number, col name and type
+     * for each column we enter in the table
+     */
+    nField = 1;
+    for( col = cv->col_info; col; col = col->next )
+    {
+        r = MSI_RecordSetInteger( rec, 2, nField );
+        if( r )
+            goto err;
+
+        r = MSI_RecordSetStringW( rec, 3, col->column );
+        if( r )
+            goto err;
+
+        r = MSI_RecordSetInteger( rec, 4, col->type );
+        if( r )
+            goto err;
+
+        r = tv->ops->insert_row( tv, rec );
+        if( r )
+            goto err;
+
+        nField++;
+    }
+    if( !col )
+        r = ERROR_SUCCESS;
+
+err:
+    if (rec)
+        msiobj_release( &rec->hdr );
+    /* FIXME: remove values from the string table on error */
+    if( tv )
+        tv->ops->delete( tv );
+    return r;
+}
+
+static UINT CREATE_close( struct tagMSIVIEW *view )
+{
+    MSICREATEVIEW *cv = (MSICREATEVIEW*)view;
+
+    TRACE("%p\n", cv);
+
+    return ERROR_SUCCESS;
+}
+
+static UINT CREATE_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols )
+{
+    MSICREATEVIEW *cv = (MSICREATEVIEW*)view;
+
+    TRACE("%p %p %p\n", cv, rows, cols );
+
+    return ERROR_FUNCTION_FAILED;
+}
+
+static UINT CREATE_get_column_info( struct tagMSIVIEW *view,
+                UINT n, LPWSTR *name, UINT *type )
+{
+    MSICREATEVIEW *cv = (MSICREATEVIEW*)view;
+
+    TRACE("%p %d %p %p\n", cv, n, name, type );
+
+    return ERROR_FUNCTION_FAILED;
+}
+
+static UINT CREATE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode,
+                MSIRECORD *rec)
+{
+    MSICREATEVIEW *cv = (MSICREATEVIEW*)view;
+
+    TRACE("%p %d %p\n", cv, eModifyMode, rec );
+
+    return ERROR_FUNCTION_FAILED;
+}
+
+static UINT CREATE_delete( struct tagMSIVIEW *view )
+{
+    MSICREATEVIEW *cv = (MSICREATEVIEW*)view;
+
+    TRACE("%p\n", cv );
+
+    msiobj_release( &cv->db->hdr );
+    msi_free( cv );
+
+    return ERROR_SUCCESS;
+}
+
+
+MSIVIEWOPS create_ops =
+{
+    CREATE_fetch_int,
+    NULL,
+    NULL,
+    NULL,
+    CREATE_execute,
+    CREATE_close,
+    CREATE_get_dimensions,
+    CREATE_get_column_info,
+    CREATE_modify,
+    CREATE_delete
+};
+
+UINT CREATE_CreateView( MSIDATABASE *db, MSIVIEW **view, LPWSTR table,
+                        column_info *col_info, BOOL temp )
+{
+    MSICREATEVIEW *cv = NULL;
+
+    TRACE("%p\n", cv );
+
+    cv = msi_alloc_zero( sizeof *cv );
+    if( !cv )
+        return ERROR_FUNCTION_FAILED;
+    
+    /* fill the structure */
+    cv->view.ops = &create_ops;
+    msiobj_addref( &db->hdr );
+    cv->db = db;
+    cv->name = table;
+    cv->col_info = col_info;
+    cv->bIsTemp = temp;
+    *view = (MSIVIEW*) cv;
+
+    return ERROR_SUCCESS;
+}
diff --git a/reactos/dll/msi/custom.c b/reactos/dll/msi/custom.c
new file mode 100644 (file)
index 0000000..0238066
--- /dev/null
@@ -0,0 +1,783 @@
+/*
+ * Custom Action processing for the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2005 Aric Stewart for CodeWeavers
+ *
+ * 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
+ */
+
+/*
+ * Pages I need
+ *
+http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/summary_list_of_all_custom_action_types.asp
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#define COBJMACROS
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "winreg.h"
+#include "wine/debug.h"
+#include "fdi.h"
+#include "msi.h"
+#include "msidefs.h"
+#include "msiquery.h"
+#include "fcntl.h"
+#include "objbase.h"
+#include "objidl.h"
+#include "msipriv.h"
+#include "winnls.h"
+#include "winuser.h"
+#include "shlobj.h"
+#include "wine/unicode.h"
+#include "winver.h"
+#include "action.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+#define CUSTOM_ACTION_TYPE_MASK 0x3F
+static const WCHAR c_collen[] = {'C',':','\\',0};
+static const WCHAR cszTempFolder[]= {'T','e','m','p','F','o','l','d','e','r',0};
+
+typedef struct tagMSIRUNNINGACTION
+{
+    struct list entry;
+    HANDLE handle;
+    BOOL   process;
+    LPWSTR name;
+} MSIRUNNINGACTION;
+
+static UINT HANDLE_CustomType1(MSIPACKAGE *package, LPCWSTR source,
+                               LPCWSTR target, const INT type, LPCWSTR action);
+static UINT HANDLE_CustomType2(MSIPACKAGE *package, LPCWSTR source,
+                               LPCWSTR target, const INT type, LPCWSTR action);
+static UINT HANDLE_CustomType18(MSIPACKAGE *package, LPCWSTR source,
+                                LPCWSTR target, const INT type, LPCWSTR action);
+static UINT HANDLE_CustomType19(MSIPACKAGE *package, LPCWSTR source,
+                                LPCWSTR target, const INT type, LPCWSTR action);
+static UINT HANDLE_CustomType50(MSIPACKAGE *package, LPCWSTR source,
+                                LPCWSTR target, const INT type, LPCWSTR action);
+static UINT HANDLE_CustomType34(MSIPACKAGE *package, LPCWSTR source,
+                                LPCWSTR target, const INT type, LPCWSTR action);
+
+
+static BOOL check_execution_scheduling_options(MSIPACKAGE *package, LPCWSTR action, UINT options)
+{
+    if (!package->script)
+        return TRUE;
+
+    if ((options & msidbCustomActionTypeClientRepeat) == 
+            msidbCustomActionTypeClientRepeat)
+    {
+        if (!(package->script->InWhatSequence & SEQUENCE_UI &&
+            package->script->InWhatSequence & SEQUENCE_EXEC))
+        {
+            TRACE("Skipping action due to dbCustomActionTypeClientRepeat option.\n");
+            return FALSE;
+        }
+    }
+    else if (options & msidbCustomActionTypeFirstSequence)
+    {
+        if (package->script->InWhatSequence & SEQUENCE_UI &&
+            package->script->InWhatSequence & SEQUENCE_EXEC )
+        {
+            TRACE("Skipping action due to msidbCustomActionTypeFirstSequence option.\n");
+            return FALSE;
+        }
+    }
+    else if (options & msidbCustomActionTypeOncePerProcess)
+    {
+        if (check_unique_action(package,action))
+        {
+            TRACE("Skipping action due to msidbCustomActionTypeOncePerProcess option.\n");
+            return FALSE;
+        }
+        else
+            register_unique_action(package,action);
+    }
+
+    return TRUE;
+}
+
+UINT ACTION_CustomAction(MSIPACKAGE *package,LPCWSTR action, BOOL execute)
+{
+    UINT rc = ERROR_SUCCESS;
+    MSIRECORD * row = 0;
+    static const WCHAR ExecSeqQuery[] =
+    {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
+     '`','C','u','s','t','o' ,'m','A','c','t','i','o','n','`',
+     ' ','W','H','E','R','E',' ','`','A','c','t','i' ,'o','n','`',' ',
+     '=',' ','\'','%','s','\'',0};
+    UINT type;
+    LPCWSTR source, target;
+    WCHAR *deformated=NULL;
+
+    row = MSI_QueryGetRecord( package->db, ExecSeqQuery, action );
+    if (!row)
+        return ERROR_CALL_NOT_IMPLEMENTED;
+
+    type = MSI_RecordGetInteger(row,2);
+
+    source = MSI_RecordGetString(row,3);
+    target = MSI_RecordGetString(row,4);
+
+    TRACE("Handling custom action %s (%x %s %s)\n",debugstr_w(action),type,
+          debugstr_w(source), debugstr_w(target));
+
+    /* handle some of the deferred actions */
+    if (type & msidbCustomActionTypeTSAware)
+        FIXME("msidbCustomActionTypeTSAware not handled\n");
+
+    if (type & msidbCustomActionTypeInScript)
+    {
+        if (type & msidbCustomActionTypeNoImpersonate)
+            FIXME("msidbCustomActionTypeNoImpersonate not handled\n");
+
+        if (type & msidbCustomActionTypeRollback)
+        {
+            FIXME("Rollback only action... rollbacks not supported yet\n");
+            schedule_action(package, ROLLBACK_SCRIPT, action);
+            rc = ERROR_SUCCESS;
+            goto end;
+        }
+        if (!execute)
+        {
+            if (type & msidbCustomActionTypeCommit)
+            {
+                TRACE("Deferring Commit Action!\n");
+                schedule_action(package, COMMIT_SCRIPT, action);
+            }
+            else
+            {
+                TRACE("Deferring Action!\n");
+                schedule_action(package, INSTALL_SCRIPT, action);
+            }
+
+            rc = ERROR_SUCCESS;
+            goto end;
+        }
+        else
+        {
+            /*Set ActionData*/
+
+            static const WCHAR szActionData[] = {
+            'C','u','s','t','o','m','A','c','t','i','o','n','D','a','t','a',0};
+            static const WCHAR szBlank[] = {0};
+            LPWSTR actiondata = msi_dup_property( package, action );
+            if (actiondata)
+                MSI_SetPropertyW(package,szActionData,actiondata);
+            else
+                MSI_SetPropertyW(package,szActionData,szBlank);
+            msi_free(actiondata);
+        }
+    }
+    else if (!check_execution_scheduling_options(package,action,type))
+    {
+        rc = ERROR_SUCCESS;
+        goto end;
+    }
+
+    switch (type & CUSTOM_ACTION_TYPE_MASK)
+    {
+        case 1: /* DLL file stored in a Binary table stream */
+            rc = HANDLE_CustomType1(package,source,target,type,action);
+            break;
+        case 2: /* EXE file stored in a Binary table strem */
+            rc = HANDLE_CustomType2(package,source,target,type,action);
+            break;
+        case 18: /*EXE file installed with package */
+            rc = HANDLE_CustomType18(package,source,target,type,action);
+            break;
+        case 19: /* Error that halts install */
+            rc = HANDLE_CustomType19(package,source,target,type,action);
+            break;
+        case 50: /*EXE file specified by a property value */
+            rc = HANDLE_CustomType50(package,source,target,type,action);
+            break;
+        case 34: /*EXE to be run in specified directory */
+            rc = HANDLE_CustomType34(package,source,target,type,action);
+            break;
+        case 35: /* Directory set with formatted text. */
+            deformat_string(package,target,&deformated);
+            MSI_SetTargetPathW(package, source, deformated);
+            msi_free(deformated);
+            break;
+        case 51: /* Property set with formatted text. */
+            deformat_string(package,target,&deformated);
+            rc = MSI_SetPropertyW(package,source,deformated);
+            msi_free(deformated);
+            break;
+        default:
+            FIXME("UNHANDLED ACTION TYPE %i (%s %s)\n",
+             type & CUSTOM_ACTION_TYPE_MASK, debugstr_w(source),
+             debugstr_w(target));
+    }
+
+end:
+    msiobj_release(&row->hdr);
+    return rc;
+}
+
+
+static UINT store_binary_to_temp(MSIPACKAGE *package, LPCWSTR source, 
+                                LPWSTR tmp_file)
+{
+    DWORD sz=MAX_PATH;
+    static const WCHAR f1[] = {'m','s','i',0};
+    WCHAR fmt[MAX_PATH];
+
+    if (MSI_GetPropertyW(package, cszTempFolder, fmt, &sz) != ERROR_SUCCESS)
+        GetTempPathW(MAX_PATH,fmt);
+
+    if (GetTempFileNameW(fmt,f1,0,tmp_file) == 0)
+    {
+        TRACE("Unable to create file\n");
+        return ERROR_FUNCTION_FAILED;
+    }
+    else
+    {
+        /* write out the file */
+        UINT rc;
+        MSIRECORD * row = 0;
+        static const WCHAR fmt[] =
+        {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
+         '`','B','i' ,'n','a','r','y','`',' ','W','H','E','R','E',
+         ' ','`','N','a','m','e','`',' ','=',' ','\'','%','s','\'',0};
+        HANDLE the_file;
+        CHAR buffer[1024];
+
+        the_file = CreateFileW(tmp_file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
+                           FILE_ATTRIBUTE_NORMAL, NULL);
+    
+        if (the_file == INVALID_HANDLE_VALUE)
+            return ERROR_FUNCTION_FAILED;
+
+        row = MSI_QueryGetRecord(package->db, fmt, source);
+        if (!row)
+            return ERROR_FUNCTION_FAILED;
+
+        do 
+        {
+            DWORD write;
+            sz = 1024;
+            rc = MSI_RecordReadStream(row,2,buffer,&sz);
+            if (rc != ERROR_SUCCESS)
+            {
+                ERR("Failed to get stream\n");
+                CloseHandle(the_file);  
+                DeleteFileW(tmp_file);
+                break;
+            }
+            WriteFile(the_file,buffer,sz,&write,NULL);
+        } while (sz == 1024);
+
+        CloseHandle(the_file);
+
+        msiobj_release(&row->hdr);
+    }
+
+    return ERROR_SUCCESS;
+}
+
+static void file_running_action(MSIPACKAGE* package, HANDLE Handle, 
+                                BOOL process, LPCWSTR name)
+{
+    MSIRUNNINGACTION *action;
+
+    action = msi_alloc( sizeof(MSIRUNNINGACTION) );
+
+    action->handle = Handle;
+    action->process = process;
+    action->name = strdupW(name);
+
+    list_add_tail( &package->RunningActions, &action->entry );
+}
+
+static UINT process_action_return_value(UINT type, HANDLE ThreadHandle)
+{
+    DWORD rc=0;
+    
+    if (type == 2)
+    {
+        GetExitCodeProcess(ThreadHandle,&rc);
+    
+        if (rc == 0)
+            return ERROR_SUCCESS;
+        else
+            return ERROR_FUNCTION_FAILED;
+    }
+
+    GetExitCodeThread(ThreadHandle,&rc);
+
+    switch (rc)
+    {
+    case ERROR_FUNCTION_NOT_CALLED:
+    case ERROR_SUCCESS:
+    case ERROR_INSTALL_USEREXIT:
+    case ERROR_INSTALL_FAILURE:
+        return rc;
+    case ERROR_NO_MORE_ITEMS:
+        return ERROR_SUCCESS;
+    default:
+        ERR("Invalid Return Code %ld\n",rc);
+        return ERROR_INSTALL_FAILURE;
+    }
+}
+
+static UINT process_handle(MSIPACKAGE* package, UINT type, 
+                           HANDLE ThreadHandle, HANDLE ProcessHandle,
+                           LPCWSTR Name, BOOL *finished)
+{
+    UINT rc = ERROR_SUCCESS;
+
+    if (!(type & msidbCustomActionTypeAsync))
+    {
+        /* synchronous */
+        TRACE("Synchronous Execution of action %s\n",debugstr_w(Name));
+        if (ProcessHandle)
+            msi_dialog_check_messages(ProcessHandle);
+        else
+            msi_dialog_check_messages(ThreadHandle);
+
+        if (!(type & msidbCustomActionTypeContinue))
+        {
+            if (ProcessHandle)
+                rc = process_action_return_value(2,ProcessHandle);
+            else
+                rc = process_action_return_value(1,ThreadHandle);
+        }
+
+        CloseHandle(ThreadHandle);
+        if (ProcessHandle)
+            CloseHandle(ProcessHandle);
+        if (finished)
+            *finished = TRUE;
+    }
+    else 
+    {
+        TRACE("Asynchronous Execution of action %s\n",debugstr_w(Name));
+        /* asynchronous */
+        if (type & msidbCustomActionTypeContinue)
+        {
+            if (ProcessHandle)
+            {
+                file_running_action(package, ProcessHandle, TRUE, Name);
+                CloseHandle(ThreadHandle);
+            }
+            else
+            file_running_action(package, ThreadHandle, FALSE, Name);
+        }
+        else
+        {
+            CloseHandle(ThreadHandle);
+            if (ProcessHandle)
+                CloseHandle(ProcessHandle);
+        }
+        if (finished)
+            *finished = FALSE;
+    }
+
+    return rc;
+}
+
+
+typedef UINT __stdcall CustomEntry(MSIHANDLE);
+
+typedef struct 
+{
+        MSIPACKAGE *package;
+        WCHAR *target;
+        WCHAR *source;
+} thread_struct;
+
+static DWORD WINAPI ACTION_CallDllFunction(thread_struct *stuff)
+{
+    HANDLE hModule;
+    LPSTR proc;
+    CustomEntry *fn;
+    DWORD rc = ERROR_SUCCESS;
+
+    TRACE("calling function (%s, %s)\n", debugstr_w(stuff->source),
+          debugstr_w(stuff->target));
+
+    hModule = LoadLibraryW(stuff->source);
+    if (hModule)
+    {
+        proc = strdupWtoA( stuff->target );
+        fn = (CustomEntry*)GetProcAddress(hModule,proc);
+        if (fn)
+        {
+            MSIHANDLE hPackage;
+            MSIPACKAGE *package = stuff->package;
+
+            TRACE("Calling function %s\n", proc);
+            hPackage = alloc_msihandle( &package->hdr );
+            if (hPackage)
+            {
+                rc = fn( hPackage );
+                MsiCloseHandle( hPackage );
+            }
+            else
+                ERR("Handle for object %p not found\n", package );
+        }
+        else
+            ERR("Cannot load functon\n");
+
+        msi_free(proc);
+        FreeLibrary(hModule);
+    }
+    else
+        ERR("Unable to load library\n");
+    msiobj_release( &stuff->package->hdr );
+    msi_free(stuff->source);
+    msi_free(stuff->target);
+    msi_free(stuff);
+    return rc;
+}
+
+static DWORD WINAPI DllThread(LPVOID info)
+{
+    thread_struct *stuff;
+    DWORD rc = 0;
+  
+    TRACE("MSI Thread (0x%lx) started for custom action\n",
+                        GetCurrentThreadId());
+    
+    stuff = (thread_struct*)info;
+    rc = ACTION_CallDllFunction(stuff);
+
+    TRACE("MSI Thread (0x%lx) finished (rc %li)\n",GetCurrentThreadId(), rc);
+    /* clse all handles for this thread */
+    MsiCloseAllHandles();
+    return rc;
+}
+
+static UINT HANDLE_CustomType1(MSIPACKAGE *package, LPCWSTR source, 
+                               LPCWSTR target, const INT type, LPCWSTR action)
+{
+    WCHAR tmp_file[MAX_PATH];
+    thread_struct *info;
+    DWORD ThreadId;
+    HANDLE ThreadHandle;
+    UINT rc = ERROR_SUCCESS;
+    BOOL finished = FALSE;
+
+    store_binary_to_temp(package, source, tmp_file);
+
+    TRACE("Calling function %s from %s\n",debugstr_w(target),
+          debugstr_w(tmp_file));
+
+    if (!strchrW(tmp_file,'.'))
+    {
+        static const WCHAR dot[]={'.',0};
+        strcatW(tmp_file,dot);
+    } 
+
+    info = msi_alloc( sizeof(*info) );
+    msiobj_addref( &package->hdr );
+    info->package = package;
+    info->target = strdupW(target);
+    info->source = strdupW(tmp_file);
+
+    ThreadHandle = CreateThread(NULL,0,DllThread,(LPVOID)info,0,&ThreadId);
+
+    rc = process_handle(package, type, ThreadHandle, NULL, action, &finished );
+
+    if (!finished)
+        track_tempfile(package, tmp_file, tmp_file);
+    else
+        DeleteFileW(tmp_file);
+    return rc;
+}
+
+static UINT HANDLE_CustomType2(MSIPACKAGE *package, LPCWSTR source, 
+                               LPCWSTR target, const INT type, LPCWSTR action)
+{
+    WCHAR tmp_file[MAX_PATH];
+    STARTUPINFOW si;
+    PROCESS_INFORMATION info;
+    BOOL rc;
+    INT len;
+    WCHAR *deformated;
+    WCHAR *cmd;
+    static const WCHAR spc[] = {' ',0};
+    UINT prc = ERROR_SUCCESS;
+    BOOL finished = FALSE;
+
+    memset(&si,0,sizeof(STARTUPINFOW));
+
+    store_binary_to_temp(package, source, tmp_file);
+
+    deformat_string(package,target,&deformated);
+
+    len = strlenW(tmp_file)+2;
+
+    if (deformated)
+        len += strlenW(deformated);
+   
+    cmd = msi_alloc(sizeof(WCHAR)*len);
+
+    strcpyW(cmd,tmp_file);
+    if (deformated)
+    {
+        strcatW(cmd,spc);
+        strcatW(cmd,deformated);
+
+        msi_free(deformated);
+    }
+
+    TRACE("executing exe %s\n", debugstr_w(cmd));
+
+    rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL,
+                  c_collen, &si, &info);
+
+
+    if ( !rc )
+    {
+        ERR("Unable to execute command %s\n", debugstr_w(cmd));
+        msi_free(cmd);
+        return ERROR_SUCCESS;
+    }
+    msi_free(cmd);
+
+    prc = process_handle(package, type, info.hThread, info.hProcess, action, 
+                          &finished);
+
+    if (!finished)
+        track_tempfile(package, tmp_file, tmp_file);
+    else
+        DeleteFileW(tmp_file);
+    
+    return prc;
+}
+
+static UINT HANDLE_CustomType18(MSIPACKAGE *package, LPCWSTR source,
+                                LPCWSTR target, const INT type, LPCWSTR action)
+{
+    STARTUPINFOW si;
+    PROCESS_INFORMATION info;
+    BOOL rc;
+    WCHAR *deformated;
+    WCHAR *cmd;
+    INT len;
+    static const WCHAR spc[] = {' ',0};
+    MSIFILE *file;
+    UINT prc;
+
+    memset(&si,0,sizeof(STARTUPINFOW));
+
+    file = get_loaded_file(package,source);
+    if( !file )
+        return ERROR_FUNCTION_FAILED;
+
+    len = lstrlenW( file->TargetPath );
+
+    deformat_string(package,target,&deformated);
+    if (deformated)
+        len += strlenW(deformated);
+    len += 2;
+
+    cmd = msi_alloc(len * sizeof(WCHAR));
+
+    lstrcpyW( cmd, file->TargetPath);
+    if (deformated)
+    {
+        strcatW(cmd, spc);
+        strcatW(cmd, deformated);
+
+        msi_free(deformated);
+    }
+
+    TRACE("executing exe %s\n", debugstr_w(cmd));
+
+    rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL,
+                  c_collen, &si, &info);
+
+    
+    if ( !rc )
+    {
+        ERR("Unable to execute command %s\n", debugstr_w(cmd));
+        msi_free(cmd);
+        return ERROR_SUCCESS;
+    }
+    msi_free(cmd);
+
+    prc = process_handle(package, type, info.hThread, info.hProcess, action, 
+                         NULL);
+
+    return prc;
+}
+
+static UINT HANDLE_CustomType19(MSIPACKAGE *package, LPCWSTR source,
+                                LPCWSTR target, const INT type, LPCWSTR action)
+{
+    static const WCHAR query[] = {
+      'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ',
+      'F','R','O','M',' ','`','E','r','r','o','r','`',' ',
+      'W','H','E','R','E',' ','`','E','r','r','o','r','`',' ','=',' ',
+      '%','s',0
+    };
+    MSIRECORD *row = 0;
+    LPWSTR deformated = NULL;
+
+    deformat_string( package, target, &deformated );
+
+    /* first try treat the error as a number */
+    row = MSI_QueryGetRecord( package->db, query, deformated );
+    if( row )
+    {
+        LPCWSTR error = MSI_RecordGetString( row, 1 );
+        MessageBoxW( NULL, error, NULL, MB_OK );
+        msiobj_release( &row->hdr );
+    }
+    else
+        MessageBoxW( NULL, deformated, NULL, MB_OK );
+
+    msi_free( deformated );
+
+    return ERROR_FUNCTION_FAILED;
+}
+
+static UINT HANDLE_CustomType50(MSIPACKAGE *package, LPCWSTR source,
+                                LPCWSTR target, const INT type, LPCWSTR action)
+{
+    STARTUPINFOW si;
+    PROCESS_INFORMATION info;
+    WCHAR *prop;
+    BOOL rc;
+    WCHAR *deformated;
+    WCHAR *cmd;
+    INT len;
+    static const WCHAR spc[] = {' ',0};
+
+    memset(&si,0,sizeof(STARTUPINFOW));
+    memset(&info,0,sizeof(PROCESS_INFORMATION));
+
+    prop = msi_dup_property( package, source );
+    if (!prop)
+        return ERROR_SUCCESS;
+
+    deformat_string(package,target,&deformated);
+    len = strlenW(prop) + 2;
+    if (deformated)
+         len += strlenW(deformated);
+
+    cmd = msi_alloc(sizeof(WCHAR)*len);
+
+    strcpyW(cmd,prop);
+    if (deformated)
+    {
+        strcatW(cmd,spc);
+        strcatW(cmd,deformated);
+
+        msi_free(deformated);
+    }
+    msi_free(prop);
+
+    TRACE("executing exe %s\n", debugstr_w(cmd));
+
+    rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL,
+                  c_collen, &si, &info);
+
+    
+    if ( !rc )
+    {
+        ERR("Unable to execute command %s\n", debugstr_w(cmd));
+        msi_free(cmd);
+        return ERROR_SUCCESS;
+    }
+    msi_free(cmd);
+
+    return process_handle(package, type, info.hThread, info.hProcess, action, NULL);
+}
+
+static UINT HANDLE_CustomType34(MSIPACKAGE *package, LPCWSTR source,
+                                LPCWSTR target, const INT type, LPCWSTR action)
+{
+    LPWSTR filename, deformated;
+    STARTUPINFOW si;
+    PROCESS_INFORMATION info;
+    BOOL rc;
+    UINT prc;
+
+    memset(&si,0,sizeof(STARTUPINFOW));
+
+    filename = resolve_folder(package, source, FALSE, FALSE, NULL);
+
+    if (!filename)
+        return ERROR_FUNCTION_FAILED;
+
+    SetCurrentDirectoryW(filename);
+    msi_free(filename);
+
+    deformat_string(package,target,&deformated);
+
+    if (!deformated)
+        return ERROR_FUNCTION_FAILED;
+
+    TRACE("executing exe %s\n", debugstr_w(deformated));
+
+    rc = CreateProcessW(NULL, deformated, NULL, NULL, FALSE, 0, NULL,
+                  c_collen, &si, &info);
+
+    if ( !rc )
+    {
+        ERR("Unable to execute command %s\n", debugstr_w(deformated));
+        msi_free(deformated);
+        return ERROR_SUCCESS;
+    }
+    msi_free(deformated);
+
+    prc = process_handle(package, type, info.hThread, info.hProcess, action,
+                         NULL);
+
+    return prc;
+}
+
+
+void ACTION_FinishCustomActions(MSIPACKAGE* package)
+{
+    struct list *item, *cursor;
+    DWORD rc;
+
+    LIST_FOR_EACH_SAFE( item, cursor, &package->RunningActions )
+    {
+        MSIRUNNINGACTION *action = LIST_ENTRY( item, MSIRUNNINGACTION, entry );
+
+        TRACE("Checking on action %s\n", debugstr_w(action->name));
+
+        list_remove( &action->entry );
+
+        if (action->process)
+            GetExitCodeProcess( action->handle, &rc );
+        else
+            GetExitCodeThread( action->handle, &rc );
+
+        if (rc == STILL_ACTIVE)
+        {
+            TRACE("Waiting on action %s\n", debugstr_w( action->name) );
+            msi_dialog_check_messages( action->handle );
+        }
+
+        CloseHandle( action->handle );
+        msi_free( action->name );
+        msi_free( action );
+    }
+}
diff --git a/reactos/dll/msi/database.c b/reactos/dll/msi/database.c
new file mode 100644 (file)
index 0000000..4da5338
--- /dev/null
@@ -0,0 +1,346 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2002,2003,2004,2005 Mike McCormack for CodeWeavers
+ *
+ * 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 <stdarg.h>
+
+#define COBJMACROS
+#define NONAMELESSUNION
+
+#include "windef.h"
+#include "winbase.h"
+#include "winreg.h"
+#include "winnls.h"
+#include "wine/debug.h"
+#include "msi.h"
+#include "msiquery.h"
+#include "msipriv.h"
+#include "objidl.h"
+#include "objbase.h"
+
+#include "initguid.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+DEFINE_GUID( CLSID_MsiDatabase, 0x000c1084, 0x0000, 0x0000,
+             0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
+DEFINE_GUID( CLSID_MsiPatch, 0x000c1086, 0x0000, 0x0000,
+             0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
+
+/*
+ *  .MSI  file format
+ *
+ *  An .msi file is a structured storage file.
+ *  It contains a number of streams.
+ *  A stream for each table in the database.
+ *  Two streams for the string table in the database.
+ *  Any binary data in a table is a reference to a stream.
+ */
+
+static VOID MSI_CloseDatabase( MSIOBJECTHDR *arg )
+{
+    MSIDATABASE *db = (MSIDATABASE *) arg;
+    DWORD r;
+
+    free_cached_tables( db );
+    msi_free_transforms( db );
+    msi_destroy_stringtable( db->strings );
+    r = IStorage_Release( db->storage );
+    if( r )
+        ERR("database reference count was not zero (%ld)\n", r);
+}
+
+UINT MSI_OpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIDATABASE **pdb)
+{
+    IStorage *stg = NULL;
+    HRESULT r;
+    MSIDATABASE *db = NULL;
+    UINT ret = ERROR_FUNCTION_FAILED;
+    LPCWSTR szMode;
+    STATSTG stat;
+
+    TRACE("%s %s\n",debugstr_w(szDBPath),debugstr_w(szPersist) );
+
+    if( !pdb )
+        return ERROR_INVALID_PARAMETER;
+
+    szMode = szPersist;
+    if( HIWORD( szPersist ) )
+    {
+        /* UINT len = lstrlenW( szPerist ) + 1; */
+        FIXME("don't support persist files yet\b");
+        return ERROR_INVALID_PARAMETER;
+        /* szMode = msi_alloc( len * sizeof (DWORD) ); */
+    }
+    else if( szPersist == MSIDBOPEN_READONLY )
+    {
+        r = StgOpenStorage( szDBPath, NULL,
+              STGM_DIRECT|STGM_READ|STGM_SHARE_DENY_WRITE, NULL, 0, &stg);
+    }
+    else if( szPersist == MSIDBOPEN_CREATE )
+    {
+        r = StgCreateDocfile( szDBPath, 
+              STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, 0, &stg);
+        if( r == ERROR_SUCCESS )
+        {
+            IStorage_SetClass( stg, &CLSID_MsiDatabase );
+            r = init_string_table( stg );
+        }
+    }
+    else if( szPersist == MSIDBOPEN_TRANSACT )
+    {
+        r = StgOpenStorage( szDBPath, NULL,
+              STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL, 0, &stg);
+    }
+    else
+    {
+        ERR("unknown flag %p\n",szPersist);
+        return ERROR_INVALID_PARAMETER;
+    }
+
+    if( FAILED( r ) )
+    {
+        FIXME("open failed r = %08lx!\n",r);
+        return ERROR_FUNCTION_FAILED;
+    }
+
+    r = IStorage_Stat( stg, &stat, STATFLAG_NONAME );
+    if( FAILED( r ) )
+    {
+        FIXME("Failed to stat storage\n");
+        goto end;
+    }
+
+    if ( !IsEqualGUID( &stat.clsid, &CLSID_MsiDatabase ) &&
+         !IsEqualGUID( &stat.clsid, &CLSID_MsiPatch ) ) 
+    {
+        ERR("storage GUID is not a MSI database GUID %s\n",
+             debugstr_guid(&stat.clsid) );
+        goto end;
+    }
+
+    db = alloc_msiobject( MSIHANDLETYPE_DATABASE, sizeof (MSIDATABASE),
+                              MSI_CloseDatabase );
+    if( !db )
+    {
+        FIXME("Failed to allocate a handle\n");
+        goto end;
+    }
+
+    if( TRACE_ON( msi ) )
+        enum_stream_names( stg );
+
+    db->storage = stg;
+    db->mode = szMode;
+    list_init( &db->tables );
+    list_init( &db->transforms );
+
+    db->strings = load_string_table( stg );
+    if( !db->strings )
+        goto end;
+
+    ret = ERROR_SUCCESS;
+
+    msiobj_addref( &db->hdr );
+    IStorage_AddRef( stg );
+    *pdb = db;
+
+end:
+    if( db )
+        msiobj_release( &db->hdr );
+    if( stg )
+        IStorage_Release( stg );
+
+    return ret;
+}
+
+UINT WINAPI MsiOpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIHANDLE *phDB)
+{
+    MSIDATABASE *db;
+    UINT ret;
+
+    TRACE("%s %s %p\n",debugstr_w(szDBPath),debugstr_w(szPersist), phDB);
+
+    ret = MSI_OpenDatabaseW( szDBPath, szPersist, &db );
+    if( ret == ERROR_SUCCESS )
+    {
+        *phDB = alloc_msihandle( &db->hdr );
+        msiobj_release( &db->hdr );
+    }
+
+    return ret;
+}
+
+UINT WINAPI MsiOpenDatabaseA(LPCSTR szDBPath, LPCSTR szPersist, MSIHANDLE *phDB)
+{
+    HRESULT r = ERROR_FUNCTION_FAILED;
+    LPWSTR szwDBPath = NULL, szwPersist = NULL;
+
+    TRACE("%s %s %p\n", debugstr_a(szDBPath), debugstr_a(szPersist), phDB);
+
+    if( szDBPath )
+    {
+        szwDBPath = strdupAtoW( szDBPath );
+        if( !szwDBPath )
+            goto end;
+    }
+
+    if( HIWORD(szPersist) )
+    {
+        szwPersist = strdupAtoW( szPersist );
+        if( !szwPersist )
+            goto end;
+    }
+    else
+        szwPersist = (LPWSTR)(DWORD)szPersist;
+
+    r = MsiOpenDatabaseW( szwDBPath, szwPersist, phDB );
+
+end:
+    if( HIWORD(szPersist) )
+        msi_free( szwPersist );
+    msi_free( szwDBPath );
+
+    return r;
+}
+
+UINT MSI_DatabaseImport( MSIDATABASE *db, LPCWSTR folder, LPCWSTR file )
+{
+    FIXME("%p %s %s\n", db, debugstr_w(folder), debugstr_w(file) );
+
+    if( folder == NULL || file == NULL )
+        return ERROR_INVALID_PARAMETER;
+   
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiDatabaseImportW(MSIHANDLE handle, LPCWSTR szFolder, LPCWSTR szFilename)
+{
+    MSIDATABASE *db;
+    UINT r;
+
+    TRACE("%lx %s %s\n",handle,debugstr_w(szFolder), debugstr_w(szFilename));
+
+    db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
+    if( !db )
+        return ERROR_INVALID_HANDLE;
+    r = MSI_DatabaseImport( db, szFolder, szFilename );
+    msiobj_release( &db->hdr );
+    return r;
+}
+
+UINT WINAPI MsiDatabaseImportA( MSIHANDLE handle,
+               LPCSTR szFolder, LPCSTR szFilename )
+{
+    LPWSTR path = NULL, file = NULL;
+    UINT r = ERROR_OUTOFMEMORY;
+
+    TRACE("%lx %s %s\n", handle, debugstr_a(szFolder), debugstr_a(szFilename));
+
+    if( szFolder )
+    {
+        path = strdupAtoW( szFolder );
+        if( !path )
+            goto end;
+    }
+
+    if( szFilename )
+    {
+        file = strdupAtoW( szFilename );
+        if( !file )
+            goto end;
+    }
+
+    r = MsiDatabaseImportW( handle, path, file );
+
+end:
+    msi_free( path );
+    msi_free( file );
+
+    return r;
+}
+
+UINT MSI_DatabaseExport( MSIDATABASE *db, LPCWSTR table,
+               LPCWSTR folder, LPCWSTR file )
+{
+    FIXME("%p %s %s %s\n", db, debugstr_w(table),
+          debugstr_w(folder), debugstr_w(file) );
+
+    if( folder == NULL || file == NULL )
+        return ERROR_INVALID_PARAMETER;
+   
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiDatabaseExportW( MSIHANDLE handle, LPCWSTR szTable,
+               LPCWSTR szFolder, LPCWSTR szFilename )
+{
+    MSIDATABASE *db;
+    UINT r;
+
+    TRACE("%lx %s %s %s\n", handle, debugstr_w(szTable),
+          debugstr_w(szFolder), debugstr_w(szFilename));
+
+    db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
+    if( !db )
+        return ERROR_INVALID_HANDLE;
+    r = MSI_DatabaseExport( db, szTable, szFolder, szFilename );
+    msiobj_release( &db->hdr );
+    return r;
+}
+
+UINT WINAPI MsiDatabaseExportA( MSIHANDLE handle, LPCSTR szTable,
+               LPCSTR szFolder, LPCSTR szFilename )
+{
+    LPWSTR path = NULL, file = NULL, table = NULL;
+    UINT r = ERROR_OUTOFMEMORY;
+
+    TRACE("%lx %s %s %s\n", handle, debugstr_a(szTable),
+          debugstr_a(szFolder), debugstr_a(szFilename));
+
+    if( szTable )
+    {
+        table = strdupAtoW( szTable );
+        if( !table )
+            goto end;
+    }
+
+    if( szFolder )
+    {
+        path = strdupAtoW( szFolder );
+        if( !path )
+            goto end;
+    }
+
+    if( szFilename )
+    {
+        file = strdupAtoW( szFilename );
+        if( !file )
+            goto end;
+    }
+
+    r = MsiDatabaseExportW( handle, table, path, file );
+
+end:
+    msi_free( table );
+    msi_free( path );
+    msi_free( file );
+
+    return r;
+}
diff --git a/reactos/dll/msi/delete.c b/reactos/dll/msi/delete.c
new file mode 100644 (file)
index 0000000..7f6675f
--- /dev/null
@@ -0,0 +1,218 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2002-2005 Mike McCormack for CodeWeavers
+ *
+ * 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 <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "wine/debug.h"
+#include "msi.h"
+#include "msiquery.h"
+#include "objbase.h"
+#include "objidl.h"
+#include "msipriv.h"
+#include "winnls.h"
+
+#include "query.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msidb);
+
+
+/*
+ * Code to delete rows from a table.
+ *
+ * We delete rows by blanking them out rather than trying to remove the row.
+ * This appears to be what the native MSI does (or tries to do). For the query:
+ *
+ * delete from Property
+ *
+ * some non-zero entries are left in the table by native MSI.  I'm not sure if
+ * that's a bug in the way I'm running the query, or a just a bug.
+ */
+
+typedef struct tagMSIDELETEVIEW
+{
+    MSIVIEW        view;
+    MSIDATABASE   *db;
+    MSIVIEW       *table;
+} MSIDELETEVIEW;
+
+static UINT DELETE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
+{
+    MSIDELETEVIEW *dv = (MSIDELETEVIEW*)view;
+
+    TRACE("%p %d %d %p\n", dv, row, col, val );
+
+    return ERROR_FUNCTION_FAILED;
+}
+
+static UINT DELETE_fetch_stream( struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm)
+{
+    MSIDELETEVIEW *dv = (MSIDELETEVIEW*)view;
+
+    TRACE("%p %d %d %p\n", dv, row, col, stm );
+
+    return ERROR_FUNCTION_FAILED;
+}
+
+static UINT DELETE_set_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT val )
+{
+    MSIDELETEVIEW *dv = (MSIDELETEVIEW*)view;
+
+    TRACE("%p %d %d %04x\n", dv, row, col, val );
+
+    return ERROR_FUNCTION_FAILED;
+}
+
+static UINT DELETE_insert_row( struct tagMSIVIEW *view, MSIRECORD *record )
+{
+    MSIDELETEVIEW *dv = (MSIDELETEVIEW*)view;
+
+    TRACE("%p %p\n", dv, record );
+
+    return ERROR_FUNCTION_FAILED;
+}
+
+static UINT DELETE_execute( struct tagMSIVIEW *view, MSIRECORD *record )
+{
+    MSIDELETEVIEW *dv = (MSIDELETEVIEW*)view;
+    UINT r, i, j, rows = 0, cols = 0;
+
+    TRACE("%p %p\n", dv, record);
+
+    if( !dv->table )
+         return ERROR_FUNCTION_FAILED;
+
+    r = dv->table->ops->execute( dv->table, record );
+    if( r != ERROR_SUCCESS )
+        return r;
+
+    r = dv->table->ops->get_dimensions( dv->table, &rows, &cols );
+    if( r != ERROR_SUCCESS )
+        return r;
+
+    TRACE("blanking %d rows\n", rows); 
+
+    /* blank out all the rows that match */
+    for( i=0; i<rows; i++ )
+        for( j=1; j<=cols; j++ )
+            dv->table->ops->set_int( dv->table, i, j, 0 );
+
+    return ERROR_SUCCESS;
+}
+
+static UINT DELETE_close( struct tagMSIVIEW *view )
+{
+    MSIDELETEVIEW *dv = (MSIDELETEVIEW*)view;
+
+    TRACE("%p\n", dv );
+
+    if( !dv->table )
+         return ERROR_FUNCTION_FAILED;
+
+    return dv->table->ops->close( dv->table );
+}
+
+static UINT DELETE_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols )
+{
+    MSIDELETEVIEW *dv = (MSIDELETEVIEW*)view;
+
+    TRACE("%p %p %p\n", dv, rows, cols );
+
+    if( !dv->table )
+         return ERROR_FUNCTION_FAILED;
+
+    *rows = 0;
+
+    return dv->table->ops->get_dimensions( dv->table, NULL, cols );
+}
+
+static UINT DELETE_get_column_info( struct tagMSIVIEW *view,
+                UINT n, LPWSTR *name, UINT *type )
+{
+    MSIDELETEVIEW *dv = (MSIDELETEVIEW*)view;
+
+    TRACE("%p %d %p %p\n", dv, n, name, type );
+
+    if( !dv->table )
+         return ERROR_FUNCTION_FAILED;
+
+    return dv->table->ops->get_column_info( dv->table, n, name, type );
+}
+
+static UINT DELETE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode,
+                MSIRECORD *rec )
+{
+    MSIDELETEVIEW *dv = (MSIDELETEVIEW*)view;
+
+    TRACE("%p %d %p\n", dv, eModifyMode, rec );
+
+    return ERROR_FUNCTION_FAILED;
+}
+
+static UINT DELETE_delete( struct tagMSIVIEW *view )
+{
+    MSIDELETEVIEW *dv = (MSIDELETEVIEW*)view;
+
+    TRACE("%p\n", dv );
+
+    if( dv->table )
+        dv->table->ops->delete( dv->table );
+
+    msi_free( dv );
+
+    return ERROR_SUCCESS;
+}
+
+
+MSIVIEWOPS delete_ops =
+{
+    DELETE_fetch_int,
+    DELETE_fetch_stream,
+    DELETE_set_int,
+    DELETE_insert_row,
+    DELETE_execute,
+    DELETE_close,
+    DELETE_get_dimensions,
+    DELETE_get_column_info,
+    DELETE_modify,
+    DELETE_delete
+};
+
+UINT DELETE_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table )
+{
+    MSIDELETEVIEW *dv = NULL;
+
+    TRACE("%p\n", dv );
+
+    dv = msi_alloc_zero( sizeof *dv );
+    if( !dv )
+        return ERROR_FUNCTION_FAILED;
+    
+    /* fill the structure */
+    dv->view.ops = &delete_ops;
+    dv->db = db;
+    dv->table = table;
+
+    *view = &dv->view;
+
+    return ERROR_SUCCESS;
+}
diff --git a/reactos/dll/msi/dialog.c b/reactos/dll/msi/dialog.c
new file mode 100644 (file)
index 0000000..1384e22
--- /dev/null
@@ -0,0 +1,2259 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2005 Mike McCormack for CodeWeavers
+ *
+ * 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
+ */
+
+#define COBJMACROS
+#define NONAMELESSUNION
+#define NONAMELESSSTRUCT
+
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winuser.h"
+#include "winnls.h"
+#include "wingdi.h"
+#include "msi.h"
+#include "msipriv.h"
+#include "msidefs.h"
+#include "ocidl.h"
+#include "olectl.h"
+#include "richedit.h"
+#include "commctrl.h"
+
+#include "wine/debug.h"
+#include "wine/unicode.h"
+
+#include "action.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+
+struct msi_control_tag;
+typedef struct msi_control_tag msi_control;
+typedef UINT (*msi_handler)( msi_dialog *, msi_control *, WPARAM );
+
+struct msi_control_tag
+{
+    struct list entry;
+    HWND hwnd;
+    msi_handler handler;
+    LPWSTR property;
+    LPWSTR value;
+    HBITMAP hBitmap;
+    HICON hIcon;
+    LPWSTR tabnext;
+    WCHAR name[1];
+};
+
+typedef struct msi_font_tag
+{
+    struct msi_font_tag *next;
+    HFONT hfont;
+    WCHAR name[1];
+} msi_font;
+
+struct msi_dialog_tag
+{
+    MSIPACKAGE *package;
+    msi_dialog_event_handler event_handler;
+    BOOL finished;
+    INT scale;
+    DWORD attributes;
+    HWND hwnd;
+    LPWSTR default_font;
+    msi_font *font_list;
+    struct list controls;
+    HWND hWndFocus;
+    LPWSTR control_default;
+    LPWSTR control_cancel;
+    WCHAR name[1];
+};
+
+typedef UINT (*msi_dialog_control_func)( msi_dialog *dialog, MSIRECORD *rec );
+struct control_handler 
+{
+    LPCWSTR control_type;
+    msi_dialog_control_func func;
+};
+
+typedef struct
+{
+    msi_dialog* dialog;
+    msi_control *parent;
+    DWORD       attributes;
+} radio_button_group_descr;
+
+const WCHAR szMsiDialogClass[] = {
+    'M','s','i','D','i','a','l','o','g','C','l','o','s','e','C','l','a','s','s',0
+};
+const WCHAR szMsiHiddenWindow[] = {
+    'M','s','i','H','i','d','d','e','n','W','i','n','d','o','w',0 };
+static const WCHAR szStatic[] = { 'S','t','a','t','i','c',0 };
+static const WCHAR szButton[] = { 'B','U','T','T','O','N', 0 };
+static const WCHAR szButtonData[] = { 'M','S','I','D','A','T','A',0 };
+static const WCHAR szText[] = { 'T','e','x','t',0 };
+static const WCHAR szPushButton[] = { 'P','u','s','h','B','u','t','t','o','n',0 };
+static const WCHAR szLine[] = { 'L','i','n','e',0 };
+static const WCHAR szBitmap[] = { 'B','i','t','m','a','p',0 };
+static const WCHAR szCheckBox[] = { 'C','h','e','c','k','B','o','x',0 };
+static const WCHAR szScrollableText[] = {
+    'S','c','r','o','l','l','a','b','l','e','T','e','x','t',0 };
+static const WCHAR szComboBox[] = { 'C','o','m','b','o','B','o','x',0 };
+static const WCHAR szEdit[] = { 'E','d','i','t',0 };
+static const WCHAR szMaskedEdit[] = { 'M','a','s','k','e','d','E','d','i','t',0 };
+static const WCHAR szPathEdit[] = { 'P','a','t','h','E','d','i','t',0 };
+static const WCHAR szProgressBar[] = {
+     'P','r','o','g','r','e','s','s','B','a','r',0 };
+static const WCHAR szRadioButtonGroup[] = { 
+    'R','a','d','i','o','B','u','t','t','o','n','G','r','o','u','p',0 };
+static const WCHAR szIcon[] = { 'I','c','o','n',0 };
+static const WCHAR szSelectionTree[] = {
+    'S','e','l','e','c','t','i','o','n','T','r','e','e',0 };
+
+static UINT msi_dialog_checkbox_handler( msi_dialog *, msi_control *, WPARAM );
+static void msi_dialog_checkbox_sync_state( msi_dialog *, msi_control * );
+static UINT msi_dialog_button_handler( msi_dialog *, msi_control *, WPARAM );
+static UINT msi_dialog_edit_handler( msi_dialog *, msi_control *, WPARAM );
+static UINT msi_dialog_radiogroup_handler( msi_dialog *, msi_control *, WPARAM param );
+static UINT msi_dialog_evaluate_control_conditions( msi_dialog *dialog );
+static LRESULT WINAPI MSIRadioGroup_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
+
+
+/* dialog sequencing */
+
+#define WM_MSI_DIALOG_CREATE  (WM_USER+0x100)
+#define WM_MSI_DIALOG_DESTROY (WM_USER+0x101)
+
+static DWORD uiThreadId;
+static HWND hMsiHiddenWindow;
+
+static INT msi_dialog_scale_unit( msi_dialog *dialog, INT val )
+{
+    return (dialog->scale * val + 5) / 10;
+}
+
+static msi_control *msi_dialog_find_control( msi_dialog *dialog, LPCWSTR name )
+{
+    msi_control *control;
+
+    if( !name )
+        return NULL;
+    LIST_FOR_EACH_ENTRY( control, &dialog->controls, msi_control, entry )
+        if( !strcmpW( control->name, name ) ) /* FIXME: case sensitive? */
+            return control;
+    return NULL;
+}
+
+static msi_control *msi_dialog_find_control_by_hwnd( msi_dialog *dialog, HWND hwnd )
+{
+    msi_control *control;
+
+    LIST_FOR_EACH_ENTRY( control, &dialog->controls, msi_control, entry )
+        if( hwnd == control->hwnd )
+            return control;
+    return NULL;
+}
+
+static LPWSTR msi_get_deformatted_field( MSIPACKAGE *package, MSIRECORD *rec, int field )
+{
+    LPCWSTR str = MSI_RecordGetString( rec, field );
+    LPWSTR ret = NULL;
+
+    if (str)
+        deformat_string( package, str, &ret );
+    return ret;
+}
+
+/*
+ * msi_dialog_get_style
+ *
+ * Extract the {\style} string from the front of the text to display and
+ *  update the pointer.
+ */
+static LPWSTR msi_dialog_get_style( LPCWSTR p, LPCWSTR *rest )
+{
+    LPWSTR ret = NULL;
+    LPCWSTR q, i;
+    DWORD len;
+
+    *rest = p;
+    if( !p )
+        return ret;
+    if( *p++ != '{' )
+        return ret;
+    q = strchrW( p, '}' );
+    if( !q )
+        return ret;
+    if( *p == '\\' || *p == '&' )
+        p++;
+
+    /* little bit of sanity checking to stop us getting confused with RTF */
+    for( i=p; i<q; i++ )
+        if( *i == '}' || *i == '\\' )
+            return ret;
+    
+    *rest = ++q;
+    len = q - p;
+
+    ret = msi_alloc( len*sizeof(WCHAR) );
+    if( !ret )
+        return ret;
+    memcpy( ret, p, len*sizeof(WCHAR) );
+    ret[len-1] = 0;
+    return ret;
+}
+
+static UINT msi_dialog_add_font( MSIRECORD *rec, LPVOID param )
+{
+    msi_dialog *dialog = param;
+    msi_font *font;
+    LPCWSTR face, name;
+    LOGFONTW lf;
+    INT style;
+    HDC hdc;
+
+    /* create a font and add it to the list */
+    name = MSI_RecordGetString( rec, 1 );
+    font = msi_alloc( sizeof *font + strlenW( name )*sizeof (WCHAR) );
+    strcpyW( font->name, name );
+    font->next = dialog->font_list;
+    dialog->font_list = font;
+
+    memset( &lf, 0, sizeof lf );
+    face = MSI_RecordGetString( rec, 2 );
+    lf.lfHeight = MSI_RecordGetInteger( rec, 3 );
+    style = MSI_RecordGetInteger( rec, 5 );
+    if( style & msidbTextStyleStyleBitsBold )
+        lf.lfWeight = FW_BOLD;
+    if( style & msidbTextStyleStyleBitsItalic )
+        lf.lfItalic = TRUE;
+    if( style & msidbTextStyleStyleBitsUnderline )
+        lf.lfUnderline = TRUE;
+    if( style & msidbTextStyleStyleBitsStrike )
+        lf.lfStrikeOut = TRUE;
+    lstrcpynW( lf.lfFaceName, face, LF_FACESIZE );
+
+    /* adjust the height */
+    hdc = GetDC( dialog->hwnd );
+    if (hdc)
+    {
+        lf.lfHeight = -MulDiv(lf.lfHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72);
+        ReleaseDC( dialog->hwnd, hdc );
+    }
+
+    font->hfont = CreateFontIndirectW( &lf );
+
+    TRACE("Adding font style %s\n", debugstr_w(font->name) );
+
+    return ERROR_SUCCESS;
+}
+
+static msi_font *msi_dialog_find_font( msi_dialog *dialog, LPCWSTR name )
+{
+    msi_font *font;
+
+    for( font = dialog->font_list; font; font = font->next )
+        if( !strcmpW( font->name, name ) )  /* FIXME: case sensitive? */
+            break;
+
+    return font;
+}
+
+static UINT msi_dialog_set_font( msi_dialog *dialog, HWND hwnd, LPCWSTR name )
+{
+    msi_font *font;
+
+    font = msi_dialog_find_font( dialog, name );
+    if( font )
+        SendMessageW( hwnd, WM_SETFONT, (WPARAM) font->hfont, TRUE );
+    else
+        ERR("No font entry for %s\n", debugstr_w(name));
+    return ERROR_SUCCESS;
+}
+
+static UINT msi_dialog_build_font_list( msi_dialog *dialog )
+{
+    static const WCHAR query[] = {
+      'S','E','L','E','C','T',' ','*',' ',
+      'F','R','O','M',' ','`','T','e','x','t','S','t','y','l','e','`',' ',0
+    };
+    UINT r;
+    MSIQUERY *view = NULL;
+
+    TRACE("dialog %p\n", dialog );
+
+    r = MSI_OpenQuery( dialog->package->db, &view, query );
+    if( r != ERROR_SUCCESS )
+        return r;
+
+    r = MSI_IterateRecords( view, NULL, msi_dialog_add_font, dialog );
+    msiobj_release( &view->hdr );
+
+    return r;
+}
+
+static msi_control *msi_dialog_create_window( msi_dialog *dialog,
+                MSIRECORD *rec, LPCWSTR szCls, LPCWSTR name, LPCWSTR text,
+                DWORD style, HWND parent )
+{
+    DWORD x, y, width, height;
+    LPWSTR font = NULL, title_font = NULL;
+    LPCWSTR title = NULL;
+    msi_control *control;
+
+    style |= WS_CHILD;
+
+    control = msi_alloc( sizeof *control + strlenW(name)*sizeof(WCHAR) );
+    strcpyW( control->name, name );
+    list_add_head( &dialog->controls, &control->entry );
+    control->handler = NULL;
+    control->property = NULL;
+    control->value = NULL;
+    control->hBitmap = NULL;
+    control->hIcon = NULL;
+    control->tabnext = strdupW( MSI_RecordGetString( rec, 11) );
+
+    x = MSI_RecordGetInteger( rec, 4 );
+    y = MSI_RecordGetInteger( rec, 5 );
+    width = MSI_RecordGetInteger( rec, 6 );
+    height = MSI_RecordGetInteger( rec, 7 );
+
+    x = msi_dialog_scale_unit( dialog, x );
+    y = msi_dialog_scale_unit( dialog, y );
+    width = msi_dialog_scale_unit( dialog, width );
+    height = msi_dialog_scale_unit( dialog, height );
+
+    if( text )
+    {
+        deformat_string( dialog->package, text, &title_font );
+        font = msi_dialog_get_style( title_font, &title );
+    }
+
+    control->hwnd = CreateWindowW( szCls, title, style,
+                          x, y, width, height, parent, NULL, NULL, NULL );
+
+    TRACE("Dialog %s control %s hwnd %p\n",
+           debugstr_w(dialog->name), debugstr_w(text), control->hwnd );
+
+    msi_dialog_set_font( dialog, control->hwnd,
+                         font ? font : dialog->default_font );
+
+    msi_free( title_font );
+    msi_free( font );
+
+    return control;
+}
+
+static MSIRECORD *msi_get_binary_record( MSIDATABASE *db, LPCWSTR name )
+{
+    static const WCHAR query[] = {
+        's','e','l','e','c','t',' ','*',' ',
+        'f','r','o','m',' ','B','i','n','a','r','y',' ',
+        'w','h','e','r','e',' ',
+            '`','N','a','m','e','`',' ','=',' ','\'','%','s','\'',0
+    };
+
+    return MSI_QueryGetRecord( db, query, name );
+}
+
+static LPWSTR msi_create_tmp_path(void)
+{
+    WCHAR tmp[MAX_PATH];
+    LPWSTR path = NULL;
+    static const WCHAR prefix[] = { 'm','s','i',0 };
+    DWORD len, r;
+
+    r = GetTempPathW( MAX_PATH, tmp );
+    if( !r )
+        return path;
+    len = lstrlenW( tmp ) + 20;
+    path = msi_alloc( len * sizeof (WCHAR) );
+    if( path )
+    {
+        r = GetTempFileNameW( tmp, prefix, 0, path );
+        if (!r)
+        {
+            msi_free( path );
+            path = NULL;
+        }
+    }
+    return path;
+}
+
+
+static HANDLE msi_load_image( MSIDATABASE *db, LPCWSTR name, UINT type,
+                              UINT cx, UINT cy, UINT flags )
+{
+    MSIRECORD *rec = NULL;
+    HANDLE himage = NULL;
+    LPWSTR tmp;
+    UINT r;
+
+    TRACE("%p %s %u %u %08x\n", db, debugstr_w(name), cx, cy, flags);
+
+    tmp = msi_create_tmp_path();
+    if( !tmp )
+        return himage;
+
+    rec = msi_get_binary_record( db, name );
+    if( rec )
+    {
+        r = MSI_RecordStreamToFile( rec, 2, tmp );
+        if( r == ERROR_SUCCESS )
+        {
+            himage = LoadImageW( 0, tmp, type, cx, cy, flags );
+            DeleteFileW( tmp );
+        }
+        msiobj_release( &rec->hdr );
+    }
+
+    msi_free( tmp );
+    return himage;
+}
+
+static HICON msi_load_icon( MSIDATABASE *db, LPCWSTR text, UINT attributes )
+{
+    DWORD cx = 0, cy = 0, flags;
+
+    flags = LR_LOADFROMFILE | LR_DEFAULTSIZE;
+    if( attributes & msidbControlAttributesFixedSize )
+    {
+        flags &= ~LR_DEFAULTSIZE;
+        if( attributes & msidbControlAttributesIconSize16 )
+        {
+            cx += 16;
+            cy += 16;
+        }
+        if( attributes & msidbControlAttributesIconSize32 )
+        {
+            cx += 32;
+            cy += 32;
+        }
+        /* msidbControlAttributesIconSize48 handled by above logic */
+    }
+    return msi_load_image( db, text, IMAGE_ICON, cx, cy, flags );
+}
+
+
+/* called from the Control Event subscription code */
+void msi_dialog_handle_event( msi_dialog* dialog, LPCWSTR control, 
+                              LPCWSTR attribute, MSIRECORD *rec )
+{
+    msi_control* ctrl;
+    LPCWSTR text;
+
+    ctrl = msi_dialog_find_control( dialog, control );
+    if (!ctrl)
+        return;
+    if( lstrcmpW(attribute, szText) )
+    {
+        ERR("Attribute %s\n", debugstr_w(attribute));
+        return;
+    }
+    text = MSI_RecordGetString( rec , 1 );
+    SetWindowTextW( ctrl->hwnd, text );
+    msi_dialog_check_messages( NULL );
+}
+
+static void msi_dialog_map_events(msi_dialog* dialog, LPCWSTR control)
+{
+    static WCHAR Query[] = {
+        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
+         '`','E','v','e','n','t','M','a','p','p','i','n','g','`',' ',
+        'W','H','E','R','E',' ',
+         '`','D','i','a','l','o','g','_','`',' ','=',' ','\'','%','s','\'',' ',
+        'A','N','D',' ',
+         '`','C','o','n','t','r','o','l','_','`',' ','=',' ','\'','%','s','\'',0
+    };
+    MSIRECORD *row;
+    LPCWSTR event, attribute;
+
+    row = MSI_QueryGetRecord( dialog->package->db, Query, dialog->name, control );
+    if (!row)
+        return;
+
+    event = MSI_RecordGetString( row, 3 );
+    attribute = MSI_RecordGetString( row, 4 );
+    ControlEvent_SubscribeToEvent( dialog->package, event, control, attribute );
+    msiobj_release( &row->hdr );
+}
+
+/* everything except radio buttons */
+static msi_control *msi_dialog_add_control( msi_dialog *dialog,
+                MSIRECORD *rec, LPCWSTR szCls, DWORD style )
+{
+    DWORD attributes;
+    LPCWSTR text, name;
+
+    name = MSI_RecordGetString( rec, 2 );
+    attributes = MSI_RecordGetInteger( rec, 8 );
+    text = MSI_RecordGetString( rec, 10 );
+    if( attributes & msidbControlAttributesVisible )
+        style |= WS_VISIBLE;
+    if( ~attributes & msidbControlAttributesEnabled )
+        style |= WS_DISABLED;
+
+    msi_dialog_map_events(dialog, name);
+
+    return msi_dialog_create_window( dialog, rec, szCls, name, text,
+                                     style, dialog->hwnd );
+}
+
+struct msi_text_info
+{
+    WNDPROC oldproc;
+    DWORD attributes;
+};
+
+/*
+ * we don't erase our own background,
+ * so we have to make sure that the parent window redraws first
+ */
+static void msi_text_on_settext( HWND hWnd )
+{
+    HWND hParent;
+    RECT rc;
+
+    hParent = GetParent( hWnd );
+    GetWindowRect( hWnd, &rc );
+    MapWindowPoints( NULL, hParent, (LPPOINT) &rc, 2 );
+    InvalidateRect( hParent, &rc, TRUE );
+}
+
+static LRESULT WINAPI
+MSIText_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+    struct msi_text_info *info;
+    LRESULT r = 0;
+
+    TRACE("%p %04x %08x %08lx\n", hWnd, msg, wParam, lParam);
+
+    info = GetPropW(hWnd, szButtonData);
+
+    if( msg == WM_CTLCOLORSTATIC &&
+       ( info->attributes & msidbControlAttributesTransparent ) )
+    {
+        SetBkMode( (HDC)wParam, TRANSPARENT );
+        return (LRESULT) GetStockObject(NULL_BRUSH);
+    }
+
+    r = CallWindowProcW(info->oldproc, hWnd, msg, wParam, lParam);
+
+    switch( msg )
+    {
+    case WM_SETTEXT:
+        msi_text_on_settext( hWnd );
+        break;
+    case WM_NCDESTROY:
+        msi_free( info );
+        RemovePropW( hWnd, szButtonData );
+        break;
+    }
+
+    return r;
+}
+
+static UINT msi_dialog_text_control( msi_dialog *dialog, MSIRECORD *rec )
+{
+    msi_control *control;
+    struct msi_text_info *info;
+
+    TRACE("%p %p\n", dialog, rec);
+
+    control = msi_dialog_add_control( dialog, rec, szStatic, SS_LEFT | WS_GROUP );
+    if( !control )
+        return ERROR_FUNCTION_FAILED;
+
+    info = msi_alloc( sizeof *info );
+    if( !info )
+        return ERROR_SUCCESS;
+
+    info->attributes = MSI_RecordGetInteger( rec, 8 );
+    if( info->attributes & msidbControlAttributesTransparent )
+        SetWindowLongPtrW( control->hwnd, GWL_EXSTYLE, WS_EX_TRANSPARENT );
+
+    info->oldproc = (WNDPROC) SetWindowLongPtrW( control->hwnd, GWLP_WNDPROC,
+                                          (LONG_PTR)MSIText_WndProc );
+    SetPropW( control->hwnd, szButtonData, info );
+
+    return ERROR_SUCCESS;
+}
+
+static UINT msi_dialog_button_control( msi_dialog *dialog, MSIRECORD *rec )
+{
+    msi_control *control;
+    UINT attributes, style;
+    LPWSTR text;
+
+    TRACE("%p %p\n", dialog, rec);
+
+    style = WS_TABSTOP;
+    attributes = MSI_RecordGetInteger( rec, 8 );
+    if( attributes & msidbControlAttributesIcon )
+        style |= BS_ICON;
+
+    control = msi_dialog_add_control( dialog, rec, szButton, style );
+    if( !control )
+        return ERROR_FUNCTION_FAILED;
+
+    control->handler = msi_dialog_button_handler;
+
+    /* set the icon */
+    text = msi_get_deformatted_field( dialog->package, rec, 10 );
+    control->hIcon = msi_load_icon( dialog->package->db, text, attributes );
+    if( attributes & msidbControlAttributesIcon )
+        SendMessageW( control->hwnd, BM_SETIMAGE, IMAGE_ICON, (LPARAM) control->hIcon );
+    msi_free( text );
+
+    return ERROR_SUCCESS;
+}
+
+static LPWSTR msi_get_checkbox_value( msi_dialog *dialog, LPCWSTR prop )
+{
+    static const WCHAR query[] = {
+        'S','E','L','E','C','T',' ','*',' ',
+        'F','R','O','M',' ','`','C','h','e','c','k','B','o','x',' ','`',
+        'W','H','E','R','E',' ',
+        '`','P','r','o','p','e','r','t','y','`',' ','=',' ',
+        '\'','%','s','\'',0
+    };
+    MSIRECORD *rec = NULL;
+    LPWSTR ret = NULL;
+
+    /* find if there is a value associated with the checkbox */
+    rec = MSI_QueryGetRecord( dialog->package->db, query, prop );
+    if (!rec)
+        return ret;
+
+    ret = msi_get_deformatted_field( dialog->package, rec, 2 );
+    if( ret && !ret[0] )
+    {
+        msi_free( ret );
+        ret = NULL;
+    }
+    msiobj_release( &rec->hdr );
+    if (ret)
+        return ret;
+
+    ret = msi_dup_property( dialog->package, prop );
+    if( ret && !ret[0] )
+    {
+        msi_free( ret );
+        ret = NULL;
+    }
+
+    return ret;
+}
+
+static UINT msi_dialog_checkbox_control( msi_dialog *dialog, MSIRECORD *rec )
+{
+    msi_control *control;
+    LPCWSTR prop;
+
+    TRACE("%p %p\n", dialog, rec);
+
+    control = msi_dialog_add_control( dialog, rec, szButton,
+                                BS_CHECKBOX | BS_MULTILINE | WS_TABSTOP );
+    control->handler = msi_dialog_checkbox_handler;
+    prop = MSI_RecordGetString( rec, 9 );
+    if( prop )
+    {
+        control->property = strdupW( prop );
+        control->value = msi_get_checkbox_value( dialog, prop );
+        TRACE("control %s value %s\n", debugstr_w(control->property),
+              debugstr_w(control->value));
+    }
+    msi_dialog_checkbox_sync_state( dialog, control );
+
+    return ERROR_SUCCESS;
+}
+
+static UINT msi_dialog_line_control( msi_dialog *dialog, MSIRECORD *rec )
+{
+    TRACE("%p %p\n", dialog, rec);
+
+    msi_dialog_add_control( dialog, rec, szStatic, SS_ETCHEDHORZ | SS_SUNKEN );
+    return ERROR_SUCCESS;
+}
+
+/******************** Scroll Text ********************************************/
+
+struct msi_scrolltext_info
+{
+    msi_dialog *dialog;
+    msi_control *control;
+    WNDPROC oldproc;
+    HMODULE hRichedit;
+};
+
+static LRESULT WINAPI
+MSIScrollText_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+    struct msi_scrolltext_info *info;
+    HRESULT r;
+
+    TRACE("%p %04x %08x %08lx\n", hWnd, msg, wParam, lParam);
+
+    info = GetPropW( hWnd, szButtonData );
+
+    r = CallWindowProcW( info->oldproc, hWnd, msg, wParam, lParam );
+
+    switch( msg )
+    {
+    case WM_NCDESTROY:
+        FreeLibrary( info->hRichedit );
+        msi_free( info );
+        RemovePropW( hWnd, szButtonData );
+        break;
+    case WM_VSCROLL:
+        msi_dialog_button_handler( info->dialog, info->control, BN_CLICKED );
+        break;
+    }
+    return r;
+}
+
+struct msi_streamin_info
+{
+    LPSTR string;
+    DWORD offset;
+    DWORD length;
+};
+
+static DWORD CALLBACK
+msi_richedit_stream_in( DWORD_PTR arg, LPBYTE buffer, LONG count, LONG *pcb )
+{
+    struct msi_streamin_info *info = (struct msi_streamin_info*) arg;
+
+    if( (count + info->offset) > info->length )
+        count = info->length - info->offset;
+    memcpy( buffer, &info->string[ info->offset ], count );
+    *pcb = count;
+    info->offset += count;
+
+    TRACE("%ld/%ld\n", info->offset, info->length);
+
+    return 0;
+}
+
+static void msi_scrolltext_add_text( msi_control *control, LPCWSTR text )
+{
+    struct msi_streamin_info info;
+    EDITSTREAM es;
+
+    info.string = strdupWtoA( text );
+    info.offset = 0;
+    info.length = lstrlenA( info.string ) + 1;
+
+    es.dwCookie = (DWORD_PTR) &info;
+    es.dwError = 0;
+    es.pfnCallback = msi_richedit_stream_in;
+
+    SendMessageW( control->hwnd, EM_STREAMIN, SF_RTF, (LPARAM) &es );
+
+    msi_free( info.string );
+}
+
+static UINT msi_dialog_scrolltext_control( msi_dialog *dialog, MSIRECORD *rec )
+{
+    static const WCHAR szRichEdit20W[] = {
+       'R','i','c','h','E','d','i','t','2','0','W',0
+    };
+    struct msi_scrolltext_info *info;
+    msi_control *control;
+    DWORD style;
+
+    info = msi_alloc( sizeof *info );
+    if (!info)
+        return ERROR_FUNCTION_FAILED;
+
+    info->hRichedit = LoadLibraryA("riched20");
+
+    style = WS_BORDER | ES_MULTILINE | WS_VSCROLL |
+            ES_READONLY | ES_AUTOVSCROLL | WS_TABSTOP;
+    control = msi_dialog_add_control( dialog, rec, szRichEdit20W, style );
+    if (!control)
+    {
+        FreeLibrary( info->hRichedit );
+        msi_free( info );
+        return ERROR_FUNCTION_FAILED;
+    }
+
+    info->dialog = dialog;
+    info->control = control;
+
+    /* subclass the static control */
+    info->oldproc = (WNDPROC) SetWindowLongPtrW( control->hwnd, GWLP_WNDPROC,
+                                          (LONG_PTR)MSIScrollText_WndProc );
+    SetPropW( control->hwnd, szButtonData, info );
+
+    /* add the text into the richedit */
+    msi_scrolltext_add_text( control, MSI_RecordGetString( rec, 10 ) );
+
+    return ERROR_SUCCESS;
+}
+
+static HBITMAP msi_load_picture( MSIDATABASE *db, LPCWSTR name,
+                                 INT cx, INT cy, DWORD flags )
+{
+    HBITMAP hOleBitmap = 0, hBitmap = 0, hOldSrcBitmap, hOldDestBitmap;
+    MSIRECORD *rec = NULL;
+    IStream *stm = NULL;
+    IPicture *pic = NULL;
+    HDC srcdc, destdc;
+    BITMAP bm;
+    UINT r;
+
+    rec = msi_get_binary_record( db, name );
+    if( !rec )
+        goto end;
+
+    r = MSI_RecordGetIStream( rec, 2, &stm );
+    msiobj_release( &rec->hdr );
+    if( r != ERROR_SUCCESS )
+        goto end;
+
+    r = OleLoadPicture( stm, 0, TRUE, &IID_IPicture, (LPVOID*) &pic );
+    IStream_Release( stm );
+    if( FAILED( r ) )
+    {
+        ERR("failed to load picture\n");
+        goto end;
+    }
+
+    r = IPicture_get_Handle( pic, (OLE_HANDLE*) &hOleBitmap );
+    if( FAILED( r ) )
+    {
+        ERR("failed to get bitmap handle\n");
+        goto end;
+    }
+    /* make the bitmap the desired size */
+    r = GetObjectW( hOleBitmap, sizeof bm, &bm );
+    if (r != sizeof bm )
+    {
+        ERR("failed to get bitmap size\n");
+        goto end;
+    }
+
+    if (flags & LR_DEFAULTSIZE)
+    {
+        cx = bm.bmWidth;
+        cy = bm.bmHeight;
+    }
+
+    srcdc = CreateCompatibleDC( NULL );
+    hOldSrcBitmap = SelectObject( srcdc, hOleBitmap );
+    destdc = CreateCompatibleDC( NULL );
+    hBitmap = CreateCompatibleBitmap( srcdc, cx, cy );
+    hOldDestBitmap = SelectObject( destdc, hBitmap );
+    StretchBlt( destdc, 0, 0, cx, cy,
+                srcdc, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY);
+    SelectObject( srcdc, hOldSrcBitmap );
+    SelectObject( destdc, hOldDestBitmap );
+    DeleteDC( srcdc );
+    DeleteDC( destdc );
+
+end:
+    if ( pic )
+        IPicture_Release( pic );
+    return hBitmap;
+}
+
+static UINT msi_dialog_bitmap_control( msi_dialog *dialog, MSIRECORD *rec )
+{
+    UINT cx, cy, flags, style, attributes;
+    msi_control *control;
+    LPWSTR text;
+
+    flags = LR_LOADFROMFILE;
+    style = SS_BITMAP | SS_LEFT | WS_GROUP;
+
+    attributes = MSI_RecordGetInteger( rec, 8 );
+    if( attributes & msidbControlAttributesFixedSize )
+    {
+        flags |= LR_DEFAULTSIZE;
+        style |= SS_CENTERIMAGE;
+    }
+
+    control = msi_dialog_add_control( dialog, rec, szStatic, style );
+    cx = MSI_RecordGetInteger( rec, 6 );
+    cy = MSI_RecordGetInteger( rec, 7 );
+    cx = msi_dialog_scale_unit( dialog, cx );
+    cy = msi_dialog_scale_unit( dialog, cy );
+
+    text = msi_get_deformatted_field( dialog->package, rec, 10 );
+    control->hBitmap = msi_load_picture( dialog->package->db, text, cx, cy, flags );
+    if( control->hBitmap )
+        SendMessageW( control->hwnd, STM_SETIMAGE,
+                      IMAGE_BITMAP, (LPARAM) control->hBitmap );
+    else
+        ERR("Failed to load bitmap %s\n", debugstr_w(text));
+
+    msi_free( text );
+    
+    return ERROR_SUCCESS;
+}
+
+static UINT msi_dialog_icon_control( msi_dialog *dialog, MSIRECORD *rec )
+{
+    msi_control *control;
+    DWORD attributes;
+    LPWSTR text;
+
+    TRACE("\n");
+
+    control = msi_dialog_add_control( dialog, rec, szStatic,
+                            SS_ICON | SS_CENTERIMAGE | WS_GROUP );
+            
+    attributes = MSI_RecordGetInteger( rec, 8 );
+    text = msi_get_deformatted_field( dialog->package, rec, 10 );
+    control->hIcon = msi_load_icon( dialog->package->db, text, attributes );
+    if( control->hIcon )
+        SendMessageW( control->hwnd, STM_SETICON, (WPARAM) control->hIcon, 0 );
+    else
+        ERR("Failed to load bitmap %s\n", debugstr_w(text));
+    msi_free( text );
+    return ERROR_SUCCESS;
+}
+
+static UINT msi_dialog_combo_control( msi_dialog *dialog, MSIRECORD *rec )
+{
+    static const WCHAR szCombo[] = { 'C','O','M','B','O','B','O','X',0 };
+
+    msi_dialog_add_control( dialog, rec, szCombo,
+                            SS_BITMAP | SS_LEFT | SS_CENTERIMAGE );
+    return ERROR_SUCCESS;
+}
+
+static UINT msi_dialog_edit_control( msi_dialog *dialog, MSIRECORD *rec )
+{
+    msi_control *control;
+    LPCWSTR prop;
+    LPWSTR val;
+
+    control = msi_dialog_add_control( dialog, rec, szEdit,
+                                      WS_BORDER | WS_TABSTOP );
+    control->handler = msi_dialog_edit_handler;
+    prop = MSI_RecordGetString( rec, 9 );
+    if( prop )
+        control->property = strdupW( prop );
+    val = msi_dup_property( dialog->package, control->property );
+    SetWindowTextW( control->hwnd, val );
+    msi_free( val );
+    return ERROR_SUCCESS;
+}
+
+/******************** Masked Edit ********************************************/
+
+#define MASK_MAX_GROUPS 10
+
+struct msi_mask_group
+{
+    UINT len;
+    UINT ofs;
+    WCHAR type;
+    HWND hwnd;
+};
+
+struct msi_maskedit_info
+{
+    msi_dialog *dialog;
+    WNDPROC oldproc;
+    HWND hwnd;
+    LPWSTR prop;
+    UINT num_chars;
+    UINT num_groups;
+    struct msi_mask_group group[MASK_MAX_GROUPS];
+};
+
+static BOOL msi_mask_editable( WCHAR type )
+{
+    switch (type)
+    {
+    case '%':
+    case '#':
+    case '&':
+    case '`':
+    case '?':
+    case '^':
+        return TRUE;
+    }
+    return FALSE;
+}
+
+static void msi_mask_control_change( struct msi_maskedit_info *info )
+{
+    LPWSTR val;
+    UINT i, n, r;
+
+    val = msi_alloc( (info->num_chars+1)*sizeof(WCHAR) );
+    for( i=0, n=0; i<info->num_groups; i++ )
+    {
+        if( (info->group[i].len + n) > info->num_chars )
+        {
+            ERR("can't fit control %d text into template\n",i);
+            break;
+        }
+        if (!msi_mask_editable(info->group[i].type))
+        {
+            for(r=0; r<info->group[i].len; r++)
+                val[n+r] = info->group[i].type;
+            val[n+r] = 0;
+        }
+        else
+        {
+            r = GetWindowTextW( info->group[i].hwnd, &val[n], info->group[i].len+1 );
+            if( r != info->group[i].len )
+                break;
+        }
+        n += r;
+    }
+
+    TRACE("%d/%d controls were good\n", i, info->num_groups);
+
+    if( i == info->num_groups )
+    {
+        TRACE("Set property %s to %s\n",
+              debugstr_w(info->prop), debugstr_w(val) );
+        CharUpperBuffW( val, info->num_chars );
+        MSI_SetPropertyW( info->dialog->package, info->prop, val );
+        msi_dialog_evaluate_control_conditions( info->dialog );
+    }
+    msi_free( val );
+}
+
+/* now move to the next control if necessary */
+static VOID msi_mask_next_control( struct msi_maskedit_info *info, HWND hWnd )
+{
+    HWND hWndNext;
+    UINT len, i;
+
+    for( i=0; i<info->num_groups; i++ )
+        if( info->group[i].hwnd == hWnd )
+            break;
+
+    /* don't move from the last control */
+    if( i >= (info->num_groups-1) )
+        return;
+
+    len = SendMessageW( hWnd, WM_GETTEXTLENGTH, 0, 0 );
+    if( len < info->group[i].len )
+        return;
+
+    hWndNext = GetNextDlgTabItem( GetParent( hWnd ), hWnd, FALSE );
+    SetFocus( hWndNext );
+}
+
+static LRESULT WINAPI
+MSIMaskedEdit_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+    struct msi_maskedit_info *info;
+    HRESULT r;
+
+    TRACE("%p %04x %08x %08lx\n", hWnd, msg, wParam, lParam);
+
+    info = GetPropW(hWnd, szButtonData);
+
+    r = CallWindowProcW(info->oldproc, hWnd, msg, wParam, lParam);
+
+    switch( msg )
+    {
+    case WM_COMMAND:
+        if (HIWORD(wParam) == EN_CHANGE)
+        {
+            msi_mask_control_change( info );
+            msi_mask_next_control( info, (HWND) lParam );
+        }
+        break;
+    case WM_NCDESTROY:
+        msi_free( info->prop );
+        msi_free( info );
+        RemovePropW( hWnd, szButtonData );
+        break;
+    }
+
+    return r;
+}
+
+/* fish the various bits of the property out and put them in the control */
+static void
+msi_maskedit_set_text( struct msi_maskedit_info *info, LPCWSTR text )
+{
+    LPCWSTR p;
+    UINT i;
+
+    p = text;
+    for( i = 0; i < info->num_groups; i++ )
+    {
+        if( info->group[i].len < lstrlenW( p ) )
+        {
+            LPWSTR chunk = strdupW( p );
+            chunk[ info->group[i].len ] = 0;
+            SetWindowTextW( info->group[i].hwnd, chunk );
+            msi_free( chunk );
+        }
+        else
+        {
+            SetWindowTextW( info->group[i].hwnd, p );
+            break;
+        }
+        p += info->group[i].len;
+    }
+}
+
+static struct msi_maskedit_info * msi_dialog_parse_groups( LPCWSTR mask )
+{
+    struct msi_maskedit_info * info = NULL;
+    int i = 0, n = 0, total = 0;
+    LPCWSTR p;
+
+    TRACE("masked control, template %s\n", debugstr_w(mask));
+
+    if( !mask )
+        return info;
+
+    info = msi_alloc_zero( sizeof *info );
+    if( !info )
+        return info;
+
+    p = strchrW(mask, '<');
+    if( p )
+        p++;
+    else
+        p = mask;
+
+    for( i=0; i<MASK_MAX_GROUPS; i++ )
+    {
+        /* stop at the end of the string */
+        if( p[0] == 0 || p[0] == '>' )
+            break;
+
+        /* count the number of the same identifier */
+        for( n=0; p[n] == p[0]; n++ )
+            ;
+        info->group[i].ofs = total;
+        info->group[i].type = p[0];
+        if( p[n] == '=' )
+        {
+            n++;
+            total++; /* an extra not part of the group */
+        }
+        info->group[i].len = n;
+        total += n;
+        p += n;
+    }
+
+    TRACE("%d characters in %d groups\n", total, i );
+    if( i == MASK_MAX_GROUPS )
+        ERR("too many groups in PIDTemplate %s\n", debugstr_w(mask));
+
+    info->num_chars = total;
+    info->num_groups = i;
+
+    return info;
+}
+
+static void
+msi_maskedit_create_children( struct msi_maskedit_info *info, LPCWSTR font )
+{
+    DWORD width, height, style, wx, ww;
+    RECT rect;
+    HWND hwnd;
+    UINT i;
+
+    style = WS_CHILD | WS_BORDER | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL;
+
+    GetClientRect( info->hwnd, &rect );
+
+    width = rect.right - rect.left;
+    height = rect.bottom - rect.top;
+
+    for( i = 0; i < info->num_groups; i++ )
+    {
+        if (!msi_mask_editable( info->group[i].type ))
+            continue;
+        wx = (info->group[i].ofs * width) / info->num_chars;
+        ww = (info->group[i].len * width) / info->num_chars;
+
+        hwnd = CreateWindowW( szEdit, NULL, style, wx, 0, ww, height,
+                              info->hwnd, NULL, NULL, NULL );
+        if( !hwnd )
+        {
+            ERR("failed to create mask edit sub window\n");
+            break;
+        }
+
+        SendMessageW( hwnd, EM_LIMITTEXT, info->group[i].len, 0 );
+
+        msi_dialog_set_font( info->dialog, hwnd,
+                             font?font:info->dialog->default_font );
+        info->group[i].hwnd = hwnd;
+    }
+}
+
+/*
+ * office 2003 uses "73931<````=````=````=````=`````>@@@@@"
+ * delphi 7 uses "<????-??????-??????-????>" and "<???-???>"
+ * filemaker pro 7 uses "<^^^^=^^^^=^^^^=^^^^=^^^^=^^^^=^^^^^>"
+ */
+static UINT msi_dialog_maskedit_control( msi_dialog *dialog, MSIRECORD *rec )
+{
+    LPWSTR font_mask, val = NULL, font;
+    struct msi_maskedit_info *info = NULL;
+    UINT ret = ERROR_SUCCESS;
+    msi_control *control;
+    LPCWSTR prop, mask;
+
+    TRACE("\n");
+
+    font_mask = msi_get_deformatted_field( dialog->package, rec, 10 );
+    font = msi_dialog_get_style( font_mask, &mask );
+    if( !mask )
+    {
+        ERR("mask template is empty\n");
+        goto end;
+    }
+
+    info = msi_dialog_parse_groups( mask );
+    if( !info )
+    {
+        ERR("template %s is invalid\n", debugstr_w(mask));
+        goto end;
+    }
+
+    info->dialog = dialog;
+
+    control = msi_dialog_add_control( dialog, rec, szStatic,
+                   SS_OWNERDRAW | WS_GROUP | WS_VISIBLE );
+    if( !control )
+    {
+        ERR("Failed to create maskedit container\n");
+        ret = ERROR_FUNCTION_FAILED;
+        goto end;
+    }
+    SetWindowLongPtrW( control->hwnd, GWL_EXSTYLE, WS_EX_CONTROLPARENT );
+
+    info->hwnd = control->hwnd;
+
+    /* subclass the static control */
+    info->oldproc = (WNDPROC) SetWindowLongPtrW( info->hwnd, GWLP_WNDPROC,
+                                          (LONG_PTR)MSIMaskedEdit_WndProc );
+    SetPropW( control->hwnd, szButtonData, info );
+
+    prop = MSI_RecordGetString( rec, 9 );
+    if( prop )
+        info->prop = strdupW( prop );
+
+    msi_maskedit_create_children( info, font );
+
+    if( prop )
+    {
+        val = msi_dup_property( dialog->package, prop );
+        if( val )
+        {
+            msi_maskedit_set_text( info, val );
+            msi_free( val );
+        }
+    }
+
+end:
+    if( ret != ERROR_SUCCESS )
+        msi_free( info );
+    msi_free( font_mask );
+    msi_free( font );
+    return ret;
+}
+
+/******************** Progress Bar *****************************************/
+
+static UINT msi_dialog_progress_bar( msi_dialog *dialog, MSIRECORD *rec )
+{
+    msi_dialog_add_control( dialog, rec, PROGRESS_CLASSW, WS_VISIBLE );
+    return ERROR_SUCCESS;
+}
+
+/******************** Path Edit ********************************************/
+
+static UINT msi_dialog_pathedit_control( msi_dialog *dialog, MSIRECORD *rec )
+{
+    FIXME("not implemented properly\n");
+    return msi_dialog_edit_control( dialog, rec );
+}
+
+/* radio buttons are a bit different from normal controls */
+static UINT msi_dialog_create_radiobutton( MSIRECORD *rec, LPVOID param )
+{
+    radio_button_group_descr *group = (radio_button_group_descr *)param;
+    msi_dialog *dialog = group->dialog;
+    msi_control *control;
+    LPCWSTR prop, text, name;
+    DWORD style, attributes = group->attributes;
+
+    style = WS_CHILD | BS_AUTORADIOBUTTON | BS_MULTILINE | WS_TABSTOP;
+    name = MSI_RecordGetString( rec, 3 );
+    text = MSI_RecordGetString( rec, 8 );
+    if( attributes & 1 )
+        style |= WS_VISIBLE;
+    if( ~attributes & 2 )
+        style |= WS_DISABLED;
+
+    control = msi_dialog_create_window( dialog, rec, szButton, name, text,
+                                        style, group->parent->hwnd );
+    if (!control)
+        return ERROR_FUNCTION_FAILED;
+    control->handler = msi_dialog_radiogroup_handler;
+
+    prop = MSI_RecordGetString( rec, 1 );
+    if( prop )
+        control->property = strdupW( prop );
+
+    return ERROR_SUCCESS;
+}
+
+static UINT msi_dialog_radiogroup_control( msi_dialog *dialog, MSIRECORD *rec )
+{
+    static const WCHAR query[] = {
+        'S','E','L','E','C','T',' ','*',' ',
+        'F','R','O','M',' ','R','a','d','i','o','B','u','t','t','o','n',' ',
+        'W','H','E','R','E',' ',
+           '`','P','r','o','p','e','r','t','y','`',' ','=',' ','\'','%','s','\'',0};
+    UINT r;
+    LPCWSTR prop;
+    msi_control *control;
+    MSIQUERY *view = NULL;
+    radio_button_group_descr group;
+    MSIPACKAGE *package = dialog->package;
+    WNDPROC oldproc;
+
+    prop = MSI_RecordGetString( rec, 9 );
+
+    TRACE("%p %p %s\n", dialog, rec, debugstr_w( prop ));
+
+    /* Create parent group box to hold radio buttons */
+    control = msi_dialog_add_control( dialog, rec, szButton, BS_OWNERDRAW|WS_GROUP );
+    if( !control )
+        return ERROR_FUNCTION_FAILED;
+
+    oldproc = (WNDPROC) SetWindowLongPtrW( control->hwnd, GWLP_WNDPROC,
+                                           (LONG_PTR)MSIRadioGroup_WndProc );
+    SetPropW(control->hwnd, szButtonData, oldproc);
+    SetWindowLongPtrW( control->hwnd, GWL_EXSTYLE, WS_EX_CONTROLPARENT );
+
+    if( prop )
+        control->property = strdupW( prop );
+
+    /* query the Radio Button table for all control in this group */
+    r = MSI_OpenQuery( package->db, &view, query, prop );
+    if( r != ERROR_SUCCESS )
+    {
+        ERR("query failed for dialog %s radio group %s\n", 
+            debugstr_w(dialog->name), debugstr_w(prop));
+        return ERROR_INVALID_PARAMETER;
+    }
+
+    group.dialog = dialog;
+    group.parent = control;
+    group.attributes = MSI_RecordGetInteger( rec, 8 );
+
+    r = MSI_IterateRecords( view, 0, msi_dialog_create_radiobutton, &group );
+    msiobj_release( &view->hdr );
+
+    return r;
+}
+
+/******************** Selection Tree ***************************************/
+
+static void
+msi_dialog_tv_add_child_features( MSIPACKAGE *package, HWND hwnd,
+                                  LPCWSTR parent, HTREEITEM hParent )
+{
+    MSIFEATURE *feature;
+    TVINSERTSTRUCTW tvis;
+    HTREEITEM hitem;
+
+    LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
+    {
+        if ( lstrcmpW( parent, feature->Feature_Parent ) )
+            continue;
+
+        if ( !feature->Title )
+            continue;
+
+        memset( &tvis, 0, sizeof tvis );
+        tvis.hParent = hParent;
+        tvis.hInsertAfter = TVI_SORT;
+        if (feature->Title)
+        {
+            tvis.u.item.mask = TVIF_TEXT;
+            tvis.u.item.pszText = feature->Title;
+        }
+        tvis.u.item.lParam = (LPARAM) feature;
+        hitem = (HTREEITEM) SendMessageW( hwnd, TVM_INSERTITEMW, 0, (LPARAM) &tvis );
+        if (!hitem)
+            continue;
+
+        msi_dialog_tv_add_child_features( package, hwnd,
+                                          feature->Feature, hitem );
+    }
+}
+
+static UINT msi_dialog_selection_tree( msi_dialog *dialog, MSIRECORD *rec )
+{
+    msi_control *control;
+    LPCWSTR prop;
+    LPWSTR val;
+    MSIPACKAGE *package = dialog->package;
+
+    prop = MSI_RecordGetString( rec, 9 );
+    val = msi_dup_property( package, prop );
+    control = msi_dialog_add_control( dialog, rec, WC_TREEVIEWW,
+                                      TVS_HASBUTTONS | WS_GROUP | WS_VSCROLL );
+    if (!control)
+        return ERROR_FUNCTION_FAILED;
+
+    msi_dialog_tv_add_child_features( package, control->hwnd, NULL, NULL );
+
+    msi_free( val );
+
+    return ERROR_SUCCESS;
+}
+
+struct control_handler msi_dialog_handler[] =
+{
+    { szText, msi_dialog_text_control },
+    { szPushButton, msi_dialog_button_control },
+    { szLine, msi_dialog_line_control },
+    { szBitmap, msi_dialog_bitmap_control },
+    { szCheckBox, msi_dialog_checkbox_control },
+    { szScrollableText, msi_dialog_scrolltext_control },
+    { szComboBox, msi_dialog_combo_control },
+    { szEdit, msi_dialog_edit_control },
+    { szMaskedEdit, msi_dialog_maskedit_control },
+    { szPathEdit, msi_dialog_pathedit_control },
+    { szProgressBar, msi_dialog_progress_bar },
+    { szRadioButtonGroup, msi_dialog_radiogroup_control },
+    { szIcon, msi_dialog_icon_control },
+    { szSelectionTree, msi_dialog_selection_tree },
+};
+
+#define NUM_CONTROL_TYPES (sizeof msi_dialog_handler/sizeof msi_dialog_handler[0])
+
+static UINT msi_dialog_create_controls( MSIRECORD *rec, LPVOID param )
+{
+    msi_dialog *dialog = param;
+    LPCWSTR control_type;
+    UINT i;
+
+    /* find and call the function that can create this type of control */
+    control_type = MSI_RecordGetString( rec, 3 );
+    for( i=0; i<NUM_CONTROL_TYPES; i++ )
+        if (!strcmpiW( msi_dialog_handler[i].control_type, control_type ))
+            break;
+    if( i != NUM_CONTROL_TYPES )
+        msi_dialog_handler[i].func( dialog, rec );
+    else
+        ERR("no handler for element type %s\n", debugstr_w(control_type));
+
+    return ERROR_SUCCESS;
+}
+
+static UINT msi_dialog_fill_controls( msi_dialog *dialog )
+{
+    static const WCHAR query[] = {
+        'S','E','L','E','C','T',' ','*',' ',
+        'F','R','O','M',' ','C','o','n','t','r','o','l',' ',
+        'W','H','E','R','E',' ',
+           '`','D','i','a','l','o','g','_','`',' ','=',' ','\'','%','s','\'',0};
+    UINT r;
+    MSIQUERY *view = NULL;
+    MSIPACKAGE *package = dialog->package;
+
+    TRACE("%p %s\n", dialog, debugstr_w(dialog->name) );
+
+    /* query the Control table for all the elements of the control */
+    r = MSI_OpenQuery( package->db, &view, query, dialog->name );
+    if( r != ERROR_SUCCESS )
+    {
+        ERR("query failed for dialog %s\n", debugstr_w(dialog->name));
+        return ERROR_INVALID_PARAMETER;
+    }
+
+    r = MSI_IterateRecords( view, 0, msi_dialog_create_controls, dialog );
+    msiobj_release( &view->hdr );
+
+    return r;
+}
+
+static UINT msi_dialog_set_control_condition( MSIRECORD *rec, LPVOID param )
+{
+    static const WCHAR szHide[] = { 'H','i','d','e',0 };
+    static const WCHAR szShow[] = { 'S','h','o','w',0 };
+    static const WCHAR szDisable[] = { 'D','i','s','a','b','l','e',0 };
+    static const WCHAR szEnable[] = { 'E','n','a','b','l','e',0 };
+    msi_dialog *dialog = param;
+    msi_control *control;
+    LPCWSTR name, action, condition;
+    UINT r;
+
+    name = MSI_RecordGetString( rec, 2 );
+    action = MSI_RecordGetString( rec, 3 );
+    condition = MSI_RecordGetString( rec, 4 );
+    r = MSI_EvaluateConditionW( dialog->package, condition );
+    control = msi_dialog_find_control( dialog, name );
+    if( r == MSICONDITION_TRUE && control )
+    {
+        TRACE("%s control %s\n", debugstr_w(action), debugstr_w(name));
+
+        /* FIXME: case sensitive? */
+        if(!lstrcmpW(action, szHide))
+            ShowWindow(control->hwnd, SW_HIDE);
+        else if(!strcmpW(action, szShow))
+            ShowWindow(control->hwnd, SW_SHOW);
+        else if(!strcmpW(action, szDisable))
+            EnableWindow(control->hwnd, FALSE);
+        else if(!strcmpW(action, szEnable))
+            EnableWindow(control->hwnd, TRUE);
+        else
+            FIXME("Unhandled action %s\n", debugstr_w(action));
+    }
+
+    return ERROR_SUCCESS;
+}
+
+static UINT msi_dialog_evaluate_control_conditions( msi_dialog *dialog )
+{
+    static const WCHAR query[] = {
+      'S','E','L','E','C','T',' ','*',' ',
+      'F','R','O','M',' ',
+        'C','o','n','t','r','o','l','C','o','n','d','i','t','i','o','n',' ',
+      'W','H','E','R','E',' ',
+        '`','D','i','a','l','o','g','_','`',' ','=',' ','\'','%','s','\'',0
+    };
+    UINT r;
+    MSIQUERY *view = NULL;
+    MSIPACKAGE *package = dialog->package;
+
+    TRACE("%p %s\n", dialog, debugstr_w(dialog->name) );
+
+    /* query the Control table for all the elements of the control */
+    r = MSI_OpenQuery( package->db, &view, query, dialog->name );
+    if( r != ERROR_SUCCESS )
+    {
+        ERR("query failed for dialog %s\n", debugstr_w(dialog->name));
+        return ERROR_INVALID_PARAMETER;
+    }
+
+    r = MSI_IterateRecords( view, 0, msi_dialog_set_control_condition, dialog );
+    msiobj_release( &view->hdr );
+
+    return r;
+}
+
+UINT msi_dialog_reset( msi_dialog *dialog )
+{
+    /* FIXME: should restore the original values of any properties we changed */
+    return msi_dialog_evaluate_control_conditions( dialog );
+}
+
+/* figure out the height of 10 point MS Sans Serif */
+static INT msi_dialog_get_sans_serif_height( HWND hwnd )
+{
+    static const WCHAR szSansSerif[] = {
+        'M','S',' ','S','a','n','s',' ','S','e','r','i','f',0 };
+    LOGFONTW lf;
+    TEXTMETRICW tm;
+    BOOL r;
+    LONG height = 0;
+    HFONT hFont, hOldFont;
+    HDC hdc;
+
+    hdc = GetDC( hwnd );
+    if (hdc)
+    {
+        memset( &lf, 0, sizeof lf );
+        lf.lfHeight = MulDiv(10, GetDeviceCaps(hdc, LOGPIXELSY), 72);
+        strcpyW( lf.lfFaceName, szSansSerif );
+        hFont = CreateFontIndirectW(&lf);
+        if (hFont)
+        {
+            hOldFont = SelectObject( hdc, hFont );
+            r = GetTextMetricsW( hdc, &tm );
+            if (r)
+                height = tm.tmHeight;
+            SelectObject( hdc, hOldFont );
+            DeleteObject( hFont );
+        }
+        ReleaseDC( hwnd, hdc );
+    }
+    return height;
+}
+
+/* fetch the associated record from the Dialog table */
+static MSIRECORD *msi_get_dialog_record( msi_dialog *dialog )
+{
+    static const WCHAR query[] = {
+        'S','E','L','E','C','T',' ','*',' ',
+        'F','R','O','M',' ','D','i','a','l','o','g',' ',
+        'W','H','E','R','E',' ',
+           '`','D','i','a','l','o','g','`',' ','=',' ','\'','%','s','\'',0};
+    MSIPACKAGE *package = dialog->package;
+    MSIRECORD *rec = NULL;
+
+    TRACE("%p %s\n", dialog, debugstr_w(dialog->name) );
+
+    rec = MSI_QueryGetRecord( package->db, query, dialog->name );
+    if( !rec )
+        ERR("query failed for dialog %s\n", debugstr_w(dialog->name));
+
+    return rec;
+}
+
+static void msi_dialog_adjust_dialog_size( msi_dialog *dialog, LPSIZE sz )
+{
+    RECT rect;
+    LONG style;
+
+    /* turn the client size into the window rectangle */
+    rect.left = 0;
+    rect.top = 0;
+    rect.right = msi_dialog_scale_unit( dialog, sz->cx );
+    rect.bottom = msi_dialog_scale_unit( dialog, sz->cy );
+    style = GetWindowLongPtrW( dialog->hwnd, GWL_STYLE );
+    AdjustWindowRect( &rect, style, FALSE );
+    sz->cx = rect.right - rect.left;
+    sz->cy = rect.bottom - rect.top;
+}
+
+static BOOL msi_control_set_next( msi_control *control, msi_control *next )
+{
+    return SetWindowPos( next->hwnd, control->hwnd, 0, 0, 0, 0,
+                         SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOREDRAW |
+                         SWP_NOREPOSITION | SWP_NOSENDCHANGING | SWP_NOSIZE );
+}
+
+static UINT msi_dialog_set_tab_order( msi_dialog *dialog )
+{
+    msi_control *control, *tab_next;
+
+    LIST_FOR_EACH_ENTRY( control, &dialog->controls, msi_control, entry )
+    {
+        tab_next = msi_dialog_find_control( dialog, control->tabnext );
+        if( !tab_next )
+            continue;
+        msi_control_set_next( control, tab_next );
+    }
+
+    return ERROR_SUCCESS;
+}
+
+static void msi_dialog_set_first_control( msi_dialog* dialog, LPCWSTR name )
+{
+    msi_control *control;
+
+    control = msi_dialog_find_control( dialog, name );
+    if( control )
+        dialog->hWndFocus = control->hwnd;
+    else
+        dialog->hWndFocus = NULL;
+}
+
+static LRESULT msi_dialog_oncreate( HWND hwnd, LPCREATESTRUCTW cs )
+{
+    static const WCHAR df[] = {
+        'D','e','f','a','u','l','t','U','I','F','o','n','t',0 };
+    static const WCHAR dfv[] = {
+        'M','S',' ','S','h','e','l','l',' ','D','l','g',0 };
+    msi_dialog *dialog = (msi_dialog*) cs->lpCreateParams;
+    MSIRECORD *rec = NULL;
+    LPWSTR title = NULL;
+    SIZE size;
+
+    TRACE("%p %p\n", dialog, dialog->package);
+
+    dialog->hwnd = hwnd;
+    SetWindowLongPtrW( hwnd, GWLP_USERDATA, (LONG_PTR) dialog );
+
+    rec = msi_get_dialog_record( dialog );
+    if( !rec )
+    {
+        TRACE("No record found for dialog %s\n", debugstr_w(dialog->name));
+        return -1;
+    }
+
+    dialog->scale = msi_dialog_get_sans_serif_height(dialog->hwnd);
+
+    size.cx = MSI_RecordGetInteger( rec, 4 );
+    size.cy = MSI_RecordGetInteger( rec, 5 );
+    msi_dialog_adjust_dialog_size( dialog, &size );
+
+    dialog->attributes = MSI_RecordGetInteger( rec, 6 );
+
+    dialog->default_font = msi_dup_property( dialog->package, df );
+    if (!dialog->default_font)
+    {
+        dialog->default_font = strdupW(dfv);
+        if (!dialog->default_font) return -1;
+    }
+
+    title = msi_get_deformatted_field( dialog->package, rec, 7 );
+    SetWindowTextW( hwnd, title );
+    msi_free( title );
+
+    SetWindowPos( hwnd, 0, 0, 0, size.cx, size.cy,
+                  SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOREDRAW );
+
+
+    msi_dialog_build_font_list( dialog );
+    msi_dialog_fill_controls( dialog );
+    msi_dialog_evaluate_control_conditions( dialog );
+    msi_dialog_set_tab_order( dialog );
+    msi_dialog_set_first_control( dialog, MSI_RecordGetString( rec, 8 ) );
+    msiobj_release( &rec->hdr );
+
+    return 0;
+}
+
+static UINT msi_dialog_send_event( msi_dialog *dialog, LPCWSTR event, LPCWSTR arg )
+{
+    LPWSTR event_fmt = NULL, arg_fmt = NULL;
+
+    TRACE("Sending control event %s %s\n", debugstr_w(event), debugstr_w(arg));
+
+    deformat_string( dialog->package, event, &event_fmt );
+    deformat_string( dialog->package, arg, &arg_fmt );
+
+    dialog->event_handler( dialog->package, event_fmt, arg_fmt, dialog );
+
+    msi_free( event_fmt );
+    msi_free( arg_fmt );
+
+    return ERROR_SUCCESS;
+}
+
+static UINT msi_dialog_set_property( msi_dialog *dialog, LPCWSTR event, LPCWSTR arg )
+{
+    static const WCHAR szNullArg[] = { '{','}',0 };
+    LPWSTR p, prop, arg_fmt = NULL;
+    UINT len;
+
+    len = strlenW(event);
+    prop = msi_alloc( len*sizeof(WCHAR));
+    strcpyW( prop, &event[1] );
+    p = strchrW( prop, ']' );
+    if( p && p[1] == 0 )
+    {
+        *p = 0;
+        if( strcmpW( szNullArg, arg ) )
+            deformat_string( dialog->package, arg, &arg_fmt );
+        MSI_SetPropertyW( dialog->package, prop, arg_fmt );
+        msi_free( arg_fmt );
+    }
+    else
+        ERR("Badly formatted property string - what happens?\n");
+    msi_free( prop );
+    return ERROR_SUCCESS;
+}
+
+static UINT msi_dialog_control_event( MSIRECORD *rec, LPVOID param )
+{
+    msi_dialog *dialog = param;
+    LPCWSTR condition, event, arg;
+    UINT r;
+
+    condition = MSI_RecordGetString( rec, 5 );
+    r = MSI_EvaluateConditionW( dialog->package, condition );
+    if( r == MSICONDITION_TRUE )
+    {
+        event = MSI_RecordGetString( rec, 3 );
+        arg = MSI_RecordGetString( rec, 4 );
+        if( event[0] == '[' )
+            msi_dialog_set_property( dialog, event, arg );
+        else
+            msi_dialog_send_event( dialog, event, arg );
+    }
+
+    return ERROR_SUCCESS;
+}
+
+static UINT msi_dialog_button_handler( msi_dialog *dialog,
+                                       msi_control *control, WPARAM param )
+{
+    static const WCHAR query[] = {
+      'S','E','L','E','C','T',' ','*',' ',
+      'F','R','O','M',' ','C','o','n','t','r','o','l','E','v','e','n','t',' ',
+      'W','H','E','R','E',' ',
+         '`','D','i','a','l','o','g','_','`',' ','=',' ','\'','%','s','\'',' ',
+      'A','N','D',' ',
+         '`','C','o','n','t','r','o','l','_','`',' ','=',' ','\'','%','s','\'',' ',
+      'O','R','D','E','R',' ','B','Y',' ','`','O','r','d','e','r','i','n','g','`',0
+    };
+    MSIQUERY *view = NULL;
+    UINT r;
+
+    if( HIWORD(param) != BN_CLICKED )
+        return ERROR_SUCCESS;
+
+    r = MSI_OpenQuery( dialog->package->db, &view, query,
+                       dialog->name, control->name );
+    if( r != ERROR_SUCCESS )
+    {
+        ERR("query failed\n");
+        return 0;
+    }
+
+    r = MSI_IterateRecords( view, 0, msi_dialog_control_event, dialog );
+    msiobj_release( &view->hdr );
+
+    return r;
+}
+
+static UINT msi_dialog_get_checkbox_state( msi_dialog *dialog,
+                msi_control *control )
+{
+    WCHAR state[2] = { 0 };
+    DWORD sz = 2;
+
+    MSI_GetPropertyW( dialog->package, control->property, state, &sz );
+    return state[0] ? 1 : 0;
+}
+
+static void msi_dialog_set_checkbox_state( msi_dialog *dialog,
+                msi_control *control, UINT state )
+{
+    static const WCHAR szState[] = { '1', 0 };
+    LPCWSTR val;
+
+    /* if uncheck then the property is set to NULL */
+    if (!state)
+    {
+        MSI_SetPropertyW( dialog->package, control->property, NULL );
+        return;
+    }
+
+    /* check for a custom state */
+    if (control->value && control->value[0])
+        val = control->value;
+    else
+        val = szState;
+
+    MSI_SetPropertyW( dialog->package, control->property, val );
+}
+
+static void msi_dialog_checkbox_sync_state( msi_dialog *dialog,
+                msi_control *control )
+{
+    UINT state;
+
+    state = msi_dialog_get_checkbox_state( dialog, control );
+    SendMessageW( control->hwnd, BM_SETCHECK,
+                  state ? BST_CHECKED : BST_UNCHECKED, 0 );
+}
+
+static UINT msi_dialog_checkbox_handler( msi_dialog *dialog,
+                msi_control *control, WPARAM param )
+{
+    UINT state;
+
+    if( HIWORD(param) != BN_CLICKED )
+        return ERROR_SUCCESS;
+
+    TRACE("clicked checkbox %s, set %s\n", debugstr_w(control->name),
+          debugstr_w(control->property));
+
+    state = msi_dialog_get_checkbox_state( dialog, control );
+    state = state ? 0 : 1;
+    msi_dialog_set_checkbox_state( dialog, control, state );
+    msi_dialog_checkbox_sync_state( dialog, control );
+
+    return msi_dialog_button_handler( dialog, control, param );
+}
+
+static UINT msi_dialog_edit_handler( msi_dialog *dialog,
+                msi_control *control, WPARAM param )
+{
+    UINT sz, r;
+    LPWSTR buf;
+
+    if( HIWORD(param) != EN_CHANGE )
+        return ERROR_SUCCESS;
+
+    TRACE("edit %s contents changed, set %s\n", debugstr_w(control->name),
+          debugstr_w(control->property));
+
+    sz = 0x20;
+    buf = msi_alloc( sz*sizeof(WCHAR) );
+    while( buf )
+    {
+        r = GetWindowTextW( control->hwnd, buf, sz );
+        if( r < (sz-1) )
+            break;
+        sz *= 2;
+        buf = msi_realloc( buf, sz*sizeof(WCHAR) );
+    }
+
+    MSI_SetPropertyW( dialog->package, control->property, buf );
+
+    msi_free( buf );
+
+    return ERROR_SUCCESS;
+}
+
+static UINT msi_dialog_radiogroup_handler( msi_dialog *dialog,
+                msi_control *control, WPARAM param )
+{
+    if( HIWORD(param) != BN_CLICKED )
+        return ERROR_SUCCESS;
+
+    TRACE("clicked radio button %s, set %s\n", debugstr_w(control->name),
+          debugstr_w(control->property));
+
+    MSI_SetPropertyW( dialog->package, control->property, control->name );
+
+    return msi_dialog_button_handler( dialog, control, param );
+}
+
+static LRESULT msi_dialog_oncommand( msi_dialog *dialog, WPARAM param, HWND hwnd )
+{
+    msi_control *control = NULL;
+
+    TRACE("%p %p %08x\n", dialog, hwnd, param);
+
+    switch (param)
+    {
+    case 1: /* enter */
+        control = msi_dialog_find_control( dialog, dialog->control_default );
+        break;
+    case 2: /* escape */
+        control = msi_dialog_find_control( dialog, dialog->control_cancel );
+        break;
+    default: 
+        control = msi_dialog_find_control_by_hwnd( dialog, hwnd );
+    }
+
+    if( control )
+    {
+        if( control->handler )
+        {
+            control->handler( dialog, control, param );
+            msi_dialog_evaluate_control_conditions( dialog );
+        }
+    }
+    else
+        ERR("button click from nowhere %p %d %p\n", dialog, param, hwnd);
+    return 0;
+}
+
+static void msi_dialog_setfocus( msi_dialog *dialog )
+{
+    HWND hwnd = dialog->hWndFocus;
+
+    hwnd = GetNextDlgTabItem( dialog->hwnd, hwnd, TRUE);
+    hwnd = GetNextDlgTabItem( dialog->hwnd, hwnd, FALSE);
+    SetFocus( hwnd );
+    dialog->hWndFocus = hwnd;
+}
+
+static LRESULT WINAPI MSIDialog_WndProc( HWND hwnd, UINT msg,
+                WPARAM wParam, LPARAM lParam )
+{
+    msi_dialog *dialog = (LPVOID) GetWindowLongPtrW( hwnd, GWLP_USERDATA );
+
+    TRACE("0x%04x\n", msg);
+
+    switch (msg)
+    {
+    case WM_CREATE:
+        return msi_dialog_oncreate( hwnd, (LPCREATESTRUCTW)lParam );
+
+    case WM_COMMAND:
+        return msi_dialog_oncommand( dialog, wParam, (HWND)lParam );
+
+    case WM_ACTIVATE:
+        if( LOWORD(wParam) == WA_INACTIVE )
+            dialog->hWndFocus = GetFocus();
+        else
+            msi_dialog_setfocus( dialog );
+        return 0;
+
+    case WM_SETFOCUS:
+        msi_dialog_setfocus( dialog );
+        return 0;
+
+    /* bounce back to our subclassed static control */
+    case WM_CTLCOLORSTATIC:
+        return SendMessageW( (HWND) lParam, WM_CTLCOLORSTATIC, wParam, lParam );
+
+    case WM_DESTROY:
+        dialog->hwnd = NULL;
+        return 0;
+    }
+    return DefWindowProcW(hwnd, msg, wParam, lParam);
+}
+
+static LRESULT WINAPI MSIRadioGroup_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+    WNDPROC oldproc = (WNDPROC) GetPropW(hWnd, szButtonData);
+
+    TRACE("hWnd %p msg %04x wParam 0x%08x lParam 0x%08lx\n", hWnd, msg, wParam, lParam);
+
+    if (msg == WM_COMMAND) /* Forward notifications to dialog */
+        SendMessageW(GetParent(hWnd), msg, wParam, lParam);
+
+    return CallWindowProcW(oldproc, hWnd, msg, wParam, lParam);
+}
+
+static LRESULT WINAPI MSIHiddenWindowProc( HWND hwnd, UINT msg,
+                WPARAM wParam, LPARAM lParam )
+{
+    msi_dialog *dialog = (msi_dialog*) lParam;
+
+    TRACE("%d %p\n", msg, dialog);
+
+    switch (msg)
+    {
+    case WM_MSI_DIALOG_CREATE:
+        return msi_dialog_run_message_loop( dialog );
+    case WM_MSI_DIALOG_DESTROY:
+        msi_dialog_destroy( dialog );
+        return 0;
+    }
+    return DefWindowProcW( hwnd, msg, wParam, lParam );
+}
+
+/* functions that interface to other modules within MSI */
+
+msi_dialog *msi_dialog_create( MSIPACKAGE* package, LPCWSTR szDialogName,
+                                msi_dialog_event_handler event_handler )
+{
+    MSIRECORD *rec = NULL;
+    msi_dialog *dialog;
+
+    TRACE("%p %s\n", package, debugstr_w(szDialogName));
+
+    /* allocate the structure for the dialog to use */
+    dialog = msi_alloc_zero( sizeof *dialog + sizeof(WCHAR)*strlenW(szDialogName) );
+    if( !dialog )
+        return NULL;
+    strcpyW( dialog->name, szDialogName );
+    msiobj_addref( &package->hdr );
+    dialog->package = package;
+    dialog->event_handler = event_handler;
+    dialog->finished = 0;
+    list_init( &dialog->controls );
+
+    /* verify that the dialog exists */
+    rec = msi_get_dialog_record( dialog );
+    if( !rec )
+    {
+        msiobj_release( &package->hdr );
+        msi_free( dialog );
+        return NULL;
+    }
+    dialog->attributes = MSI_RecordGetInteger( rec, 6 );
+    dialog->control_default = strdupW( MSI_RecordGetString( rec, 9 ) );
+    dialog->control_cancel = strdupW( MSI_RecordGetString( rec, 10 ) );
+    msiobj_release( &rec->hdr );
+
+    return dialog;
+}
+
+static void msi_process_pending_messages( HWND hdlg )
+{
+    MSG msg;
+
+    while( PeekMessageW( &msg, 0, 0, 0, PM_REMOVE ) )
+    {
+        if( hdlg && IsDialogMessageW( hdlg, &msg ))
+            continue;
+        TranslateMessage( &msg );
+        DispatchMessageW( &msg );
+    }
+}
+
+void msi_dialog_end_dialog( msi_dialog *dialog )
+{
+    TRACE("%p\n", dialog);
+    dialog->finished = 1;
+    PostMessageW(dialog->hwnd, WM_NULL, 0, 0);
+}
+
+void msi_dialog_check_messages( HANDLE handle )
+{
+    DWORD r;
+
+    /* in threads other than the UI thread, block */
+    if( uiThreadId != GetCurrentThreadId() )
+    {
+        if( handle )
+            WaitForSingleObject( handle, INFINITE );
+        return;
+    }
+
+    /* there's two choices for the UI thread */
+    while (1)
+    {
+        msi_process_pending_messages( NULL );
+
+        if( !handle )
+            break;
+
+        /*
+         * block here until somebody creates a new dialog or
+         * the handle we're waiting on becomes ready
+         */
+        r = MsgWaitForMultipleObjects( 1, &handle, 0, INFINITE, QS_ALLINPUT );
+        if( r == WAIT_OBJECT_0 )
+            break;
+    }
+}
+
+UINT msi_dialog_run_message_loop( msi_dialog *dialog )
+{
+    HWND hwnd;
+
+    if( !(dialog->attributes & msidbDialogAttributesVisible) )
+        return ERROR_SUCCESS;
+
+    if( uiThreadId != GetCurrentThreadId() )
+        return SendMessageW( hMsiHiddenWindow, WM_MSI_DIALOG_CREATE, 0, (LPARAM) dialog );
+
+    /* create the dialog window, don't show it yet */
+    hwnd = CreateWindowW( szMsiDialogClass, dialog->name, WS_OVERLAPPEDWINDOW,
+                     CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
+                     NULL, NULL, NULL, dialog );
+    if( !hwnd )
+    {
+        ERR("Failed to create dialog %s\n", debugstr_w( dialog->name ));
+        return ERROR_FUNCTION_FAILED;
+    }
+
+    ShowWindow( hwnd, SW_SHOW );
+    /* UpdateWindow( hwnd ); - and causes the transparent static controls not to paint */
+
+    if( dialog->attributes & msidbDialogAttributesModal )
+    {
+        while( !dialog->finished )
+        {
+            MsgWaitForMultipleObjects( 0, NULL, 0, INFINITE, QS_ALLEVENTS );
+            msi_process_pending_messages( dialog->hwnd );
+        }
+    }
+    else
+        return ERROR_IO_PENDING;
+
+    return ERROR_SUCCESS;
+}
+
+void msi_dialog_do_preview( msi_dialog *dialog )
+{
+    TRACE("\n");
+    dialog->attributes |= msidbDialogAttributesVisible;
+    dialog->attributes &= ~msidbDialogAttributesModal;
+    msi_dialog_run_message_loop( dialog );
+}
+
+void msi_dialog_destroy( msi_dialog *dialog )
+{
+    if( uiThreadId != GetCurrentThreadId() )
+    {
+        SendMessageW( hMsiHiddenWindow, WM_MSI_DIALOG_DESTROY, 0, (LPARAM) dialog );
+        return;
+    }
+
+    if( dialog->hwnd )
+        ShowWindow( dialog->hwnd, SW_HIDE );
+    
+    if( dialog->hwnd )
+        DestroyWindow( dialog->hwnd );
+
+    /* destroy the list of controls */
+    while( !list_empty( &dialog->controls ) )
+    {
+        msi_control *t = LIST_ENTRY( list_head( &dialog->controls ),
+                                     msi_control, entry );
+        list_remove( &t->entry );
+        /* leave dialog->hwnd - destroying parent destroys child windows */
+        msi_free( t->property );
+        msi_free( t->value );
+        if( t->hBitmap )
+            DeleteObject( t->hBitmap );
+        if( t->hIcon )
+            DestroyIcon( t->hIcon );
+        msi_free( t->tabnext );
+        msi_free( t );
+    }
+
+    /* destroy the list of fonts */
+    while( dialog->font_list )
+    {
+        msi_font *t = dialog->font_list;
+        dialog->font_list = t->next;
+        DeleteObject( t->hfont );
+        msi_free( t );
+    }
+    msi_free( dialog->default_font );
+
+    msi_free( dialog->control_default );
+    msi_free( dialog->control_cancel );
+    msiobj_release( &dialog->package->hdr );
+    dialog->package = NULL;
+    msi_free( dialog );
+}
+
+BOOL msi_dialog_register_class( void )
+{
+    WNDCLASSW cls;
+
+    ZeroMemory( &cls, sizeof cls );
+    cls.lpfnWndProc   = MSIDialog_WndProc;
+    cls.hInstance     = NULL;
+    cls.hIcon         = LoadIconW(0, (LPWSTR)IDI_APPLICATION);
+    cls.hCursor       = LoadCursorW(0, (LPWSTR)IDC_ARROW);
+    cls.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);
+    cls.lpszMenuName  = NULL;
+    cls.lpszClassName = szMsiDialogClass;
+
+    if( !RegisterClassW( &cls ) )
+        return FALSE;
+
+    cls.lpfnWndProc   = MSIHiddenWindowProc;
+    cls.lpszClassName = szMsiHiddenWindow;
+
+    if( !RegisterClassW( &cls ) )
+        return FALSE;
+
+    uiThreadId = GetCurrentThreadId();
+
+    hMsiHiddenWindow = CreateWindowW( szMsiHiddenWindow, NULL, WS_OVERLAPPED,
+                                   0, 0, 100, 100, NULL, NULL, NULL, NULL );
+    if( !hMsiHiddenWindow )
+        return FALSE;
+
+    return TRUE;
+}
+
+void msi_dialog_unregister_class( void )
+{
+    DestroyWindow( hMsiHiddenWindow );
+    hMsiHiddenWindow = NULL;
+    UnregisterClassW( szMsiDialogClass, NULL );
+    UnregisterClassW( szMsiHiddenWindow, NULL );
+    uiThreadId = 0;
+}
diff --git a/reactos/dll/msi/distinct.c b/reactos/dll/msi/distinct.c
new file mode 100644 (file)
index 0000000..f5b6bd2
--- /dev/null
@@ -0,0 +1,292 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2002 Mike McCormack for CodeWeavers
+ *
+ * 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 <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "wine/debug.h"
+#include "msi.h"
+#include "msiquery.h"
+#include "objbase.h"
+#include "objidl.h"
+#include "msipriv.h"
+#include "winnls.h"
+
+#include "query.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msidb);
+
+typedef struct tagDISTINCTSET
+{
+    UINT val;
+    UINT count;
+    UINT row;
+    struct tagDISTINCTSET *nextrow;
+    struct tagDISTINCTSET *nextcol;
+} DISTINCTSET;
+
+typedef struct tagMSIDISTINCTVIEW
+{
+    MSIVIEW        view;
+    MSIDATABASE   *db;
+    MSIVIEW       *table;
+    UINT           row_count;
+    UINT          *translation;
+} MSIDISTINCTVIEW;
+
+static DISTINCTSET ** distinct_insert( DISTINCTSET **x, UINT val, UINT row )
+{
+    /* horrible O(n) find */
+    while( *x )
+    {
+        if( (*x)->val == val )
+        {
+            (*x)->count++;
+            return x;
+        }
+        x = &(*x)->nextrow;
+    }
+
+    /* nothing found, so add one */
+    *x = msi_alloc( sizeof (DISTINCTSET) );
+    if( *x )
+    {
+        (*x)->val = val;
+        (*x)->count = 1;
+        (*x)->row = row;
+        (*x)->nextrow = NULL;
+        (*x)->nextcol = NULL;
+    }
+    return x;
+}
+
+static void distinct_free( DISTINCTSET *x )
+{
+    while( x )
+    {
+        DISTINCTSET *next = x->nextrow;
+        distinct_free( x->nextcol );
+        msi_free( x );
+        x = next;
+    }
+}
+
+static UINT DISTINCT_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
+{
+    MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view;
+
+    TRACE("%p %d %d %p\n", dv, row, col, val );
+
+    if( !dv->table )
+        return ERROR_FUNCTION_FAILED;
+
+    if( row >= dv->row_count )
+        return ERROR_INVALID_PARAMETER;
+
+    row = dv->translation[ row ];
+
+    return dv->table->ops->fetch_int( dv->table, row, col, val );
+}
+
+static UINT DISTINCT_execute( struct tagMSIVIEW *view, MSIRECORD *record )
+{
+    MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view;
+    UINT r, i, j, r_count, c_count;
+    DISTINCTSET *rowset = NULL;
+
+    TRACE("%p %p\n", dv, record);
+
+    if( !dv->table )
+         return ERROR_FUNCTION_FAILED;
+
+    r = dv->table->ops->execute( dv->table, record );
+    if( r != ERROR_SUCCESS )
+        return r;
+
+    r = dv->table->ops->get_dimensions( dv->table, &r_count, &c_count );
+    if( r != ERROR_SUCCESS )
+        return r;
+
+    dv->translation = msi_alloc( r_count*sizeof(UINT) );
+    if( !dv->translation )
+        return ERROR_FUNCTION_FAILED;
+
+    /* build it */
+    for( i=0; i<r_count; i++ )
+    {
+        DISTINCTSET **x = &rowset;
+
+        for( j=1; j<=c_count; j++ )
+        {
+            UINT val = 0;
+            r = dv->table->ops->fetch_int( dv->table, i, j, &val );
+            if( r != ERROR_SUCCESS )
+            {
+                ERR("Failed to fetch int at %d %d\n", i, j );
+                distinct_free( rowset );
+                return r;
+            }
+            x = distinct_insert( x, val, i );
+            if( !*x )
+            {
+                ERR("Failed to insert at %d %d\n", i, j );
+                distinct_free( rowset );
+                return ERROR_FUNCTION_FAILED;
+            }
+            if( j != c_count )
+                x = &(*x)->nextcol;
+        }
+
+        /* check if it was distinct and if so, include it */
+        if( (*x)->row == i )
+        {
+            TRACE("Row %d -> %d\n", dv->row_count, i);
+            dv->translation[dv->row_count++] = i;
+        }
+    }
+
+    distinct_free( rowset );
+
+    return ERROR_SUCCESS;
+}
+
+static UINT DISTINCT_close( struct tagMSIVIEW *view )
+{
+    MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view;
+
+    TRACE("%p\n", dv );
+
+    if( !dv->table )
+         return ERROR_FUNCTION_FAILED;
+
+    msi_free( dv->translation );
+    dv->translation = NULL;
+    dv->row_count = 0;
+
+    return dv->table->ops->close( dv->table );
+}
+
+static UINT DISTINCT_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols )
+{
+    MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view;
+
+    TRACE("%p %p %p\n", dv, rows, cols );
+
+    if( !dv->table )
+        return ERROR_FUNCTION_FAILED;
+
+    if( rows )
+    {
+        if( !dv->translation )
+            return ERROR_FUNCTION_FAILED;
+        *rows = dv->row_count;
+    }
+
+    return dv->table->ops->get_dimensions( dv->table, NULL, cols );
+}
+
+static UINT DISTINCT_get_column_info( struct tagMSIVIEW *view,
+                UINT n, LPWSTR *name, UINT *type )
+{
+    MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view;
+
+    TRACE("%p %d %p %p\n", dv, n, name, type );
+
+    if( !dv->table )
+         return ERROR_FUNCTION_FAILED;
+
+    return dv->table->ops->get_column_info( dv->table, n, name, type );
+}
+
+static UINT DISTINCT_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode,
+                MSIRECORD *rec )
+{
+    MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view;
+
+    TRACE("%p %d %p\n", dv, eModifyMode, rec );
+
+    if( !dv->table )
+         return ERROR_FUNCTION_FAILED;
+
+    return dv->table->ops->modify( dv->table, eModifyMode, rec );
+}
+
+static UINT DISTINCT_delete( struct tagMSIVIEW *view )
+{
+    MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view;
+
+    TRACE("%p\n", dv );
+
+    if( dv->table )
+        dv->table->ops->delete( dv->table );
+
+    msi_free( dv->translation );
+    msiobj_release( &dv->db->hdr );
+    msi_free( dv );
+
+    return ERROR_SUCCESS;
+}
+
+
+MSIVIEWOPS distinct_ops =
+{
+    DISTINCT_fetch_int,
+    NULL,
+    NULL,
+    NULL,
+    DISTINCT_execute,
+    DISTINCT_close,
+    DISTINCT_get_dimensions,
+    DISTINCT_get_column_info,
+    DISTINCT_modify,
+    DISTINCT_delete
+};
+
+UINT DISTINCT_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table )
+{
+    MSIDISTINCTVIEW *dv = NULL;
+    UINT count = 0, r;
+
+    TRACE("%p\n", dv );
+
+    r = table->ops->get_dimensions( table, NULL, &count );
+    if( r != ERROR_SUCCESS )
+    {
+        ERR("can't get table dimensions\n");
+        return r;
+    }
+
+    dv = msi_alloc_zero( sizeof *dv );
+    if( !dv )
+        return ERROR_FUNCTION_FAILED;
+    
+    /* fill the structure */
+    dv->view.ops = &distinct_ops;
+    msiobj_addref( &db->hdr );
+    dv->db = db;
+    dv->table = table;
+    dv->translation = NULL;
+    dv->row_count = 0;
+    *view = (MSIVIEW*) dv;
+
+    return ERROR_SUCCESS;
+}
diff --git a/reactos/dll/msi/events.c b/reactos/dll/msi/events.c
new file mode 100644 (file)
index 0000000..393154a
--- /dev/null
@@ -0,0 +1,410 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2005 Aric Stewart for CodeWeavers
+ *
+ * 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
+ */
+
+
+/*
+http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/controlevent_overview.asp
+*/
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "winreg.h"
+#include "msi.h"
+#include "msipriv.h"
+#include "action.h"
+
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+typedef UINT (*EVENTHANDLER)(MSIPACKAGE*,LPCWSTR,msi_dialog *);
+
+struct _events {
+    LPCSTR event;
+    EVENTHANDLER handler;
+};
+
+struct subscriber {
+    struct list entry;
+    LPWSTR event;
+    LPWSTR control;
+    LPWSTR attribute;
+};
+
+UINT ControlEvent_HandleControlEvent(MSIPACKAGE *, LPCWSTR, LPCWSTR, msi_dialog*);
+
+/*
+ * Create a dialog box and run it if it's modal
+ */
+static UINT event_do_dialog( MSIPACKAGE *package, LPCWSTR name, BOOL destroy_modeless )
+{
+    msi_dialog *dialog;
+    UINT r;
+
+    /* create a new dialog */
+    dialog = msi_dialog_create( package, name,
+                                ControlEvent_HandleControlEvent );
+    if( dialog )
+    {
+        /* kill the current modeless dialog */
+        if( destroy_modeless && package->dialog )
+        {
+            msi_dialog_destroy( package->dialog );
+            package->dialog = NULL;
+        }
+
+        /* modeless dialogs return an error message */
+        r = msi_dialog_run_message_loop( dialog );
+        if( r == ERROR_SUCCESS )
+            msi_dialog_destroy( dialog );
+        else
+            package->dialog = dialog;
+    }
+    else
+        r = ERROR_FUNCTION_FAILED;
+
+    return r;
+}
+
+
+/*
+ * End a modal dialog box
+ */
+static UINT ControlEvent_EndDialog(MSIPACKAGE* package, LPCWSTR argument, 
+                                   msi_dialog* dialog)
+{
+    static const WCHAR szExit[] = {
+    'E','x','i','t',0};
+    static const WCHAR szRetry[] = {
+    'R','e','t','r','y',0};
+    static const WCHAR szIgnore[] = {
+    'I','g','n','o','r','e',0};
+    static const WCHAR szReturn[] = {
+    'R','e','t','u','r','n',0};
+
+    if (lstrcmpW(argument,szExit)==0)
+        package->CurrentInstallState = ERROR_INSTALL_USEREXIT;
+    else if (lstrcmpW(argument, szRetry) == 0)
+        package->CurrentInstallState = ERROR_INSTALL_SUSPEND;
+    else if (lstrcmpW(argument, szIgnore) == 0)
+        package->CurrentInstallState = -1;
+    else if (lstrcmpW(argument, szReturn) == 0)
+        package->CurrentInstallState = ERROR_SUCCESS;
+    else
+    {
+        ERR("Unknown argument string %s\n",debugstr_w(argument));
+        package->CurrentInstallState = ERROR_FUNCTION_FAILED;
+    }
+
+    ControlEvent_CleanupSubscriptions(package);
+    msi_dialog_end_dialog( dialog );
+    return ERROR_SUCCESS;
+}
+
+/*
+ * transition from one modal dialog to another modal dialog
+ */
+static UINT ControlEvent_NewDialog(MSIPACKAGE* package, LPCWSTR argument, 
+                                   msi_dialog *dialog)
+{
+    /* store the name of the next dialog, and signal this one to end */
+    package->next_dialog = strdupW(argument);
+    ControlEvent_CleanupSubscriptions(package);
+    msi_dialog_end_dialog( dialog );
+    return ERROR_SUCCESS;
+}
+
+/*
+ * Create a new child dialog of an existing modal dialog
+ */
+static UINT ControlEvent_SpawnDialog(MSIPACKAGE* package, LPCWSTR argument, 
+                              msi_dialog *dialog)
+{
+    /* don't destroy a modeless dialogs that might be our parent */
+    event_do_dialog( package, argument, FALSE );
+    if( package->CurrentInstallState != ERROR_SUCCESS )
+        msi_dialog_end_dialog( dialog );
+    return ERROR_SUCCESS;
+}
+
+/*
+ * Creates a dialog that remains up for a period of time
+ * based on a condition
+ */
+static UINT ControlEvent_SpawnWaitDialog(MSIPACKAGE* package, LPCWSTR argument, 
+                                  msi_dialog* dialog)
+{
+    FIXME("Doing Nothing\n");
+    return ERROR_SUCCESS;
+}
+
+static UINT ControlEvent_DoAction(MSIPACKAGE* package, LPCWSTR argument, 
+                                  msi_dialog* dialog)
+{
+    ACTION_PerformAction(package,argument,TRUE);
+    return ERROR_SUCCESS;
+}
+
+static UINT ControlEvent_AddLocal(MSIPACKAGE* package, LPCWSTR argument, 
+                                  msi_dialog* dialog)
+{
+    static const WCHAR szAll[] = {'A','L','L',0};
+    MSIFEATURE *feature = NULL;
+
+    if (lstrcmpW(szAll,argument))
+    {
+        MSI_SetFeatureStateW(package,argument,INSTALLSTATE_LOCAL);
+    }
+    else
+    {
+        LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
+        {
+            feature->ActionRequest = INSTALLSTATE_LOCAL;
+            feature->Action = INSTALLSTATE_LOCAL;
+        }
+        ACTION_UpdateComponentStates(package,argument);
+    }
+    return ERROR_SUCCESS;
+}
+
+static UINT ControlEvent_Remove(MSIPACKAGE* package, LPCWSTR argument, 
+                                msi_dialog* dialog)
+{
+    static const WCHAR szAll[] = {'A','L','L',0};
+    MSIFEATURE *feature = NULL;
+
+    if (lstrcmpW(szAll,argument))
+    {
+        MSI_SetFeatureStateW(package,argument,INSTALLSTATE_ABSENT);
+    }
+    else
+    {
+        LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
+        {
+            feature->ActionRequest = INSTALLSTATE_ABSENT;
+            feature->Action= INSTALLSTATE_ABSENT;
+        }
+        ACTION_UpdateComponentStates(package,argument);
+    }
+    return ERROR_SUCCESS;
+}
+
+static UINT ControlEvent_AddSource(MSIPACKAGE* package, LPCWSTR argument, 
+                                   msi_dialog* dialog)
+{
+    static const WCHAR szAll[] = {'A','L','L',0};
+    MSIFEATURE *feature = NULL;
+
+    if (lstrcmpW(szAll,argument))
+    {
+        MSI_SetFeatureStateW(package,argument,INSTALLSTATE_SOURCE);
+    }
+    else
+    {
+        LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
+        {
+            feature->ActionRequest = INSTALLSTATE_SOURCE;
+            feature->Action = INSTALLSTATE_SOURCE;
+        }
+        ACTION_UpdateComponentStates(package,argument);
+    }
+    return ERROR_SUCCESS;
+}
+
+static UINT ControlEvent_SetTargetPath(MSIPACKAGE* package, LPCWSTR argument, 
+                                   msi_dialog* dialog)
+{
+    LPWSTR path = msi_dup_property( package, argument );
+    UINT r;
+    /* failure to set the path halts the executing of control events */
+    r = MSI_SetTargetPathW(package, argument, path);
+    msi_free(path);
+    return r;
+}
+
+static UINT ControlEvent_Reset(MSIPACKAGE* package, LPCWSTR argument, 
+                                   msi_dialog* dialog)
+{
+    msi_dialog_reset(dialog);
+    return ERROR_SUCCESS;
+}
+
+/*
+ * Subscribed events
+ */
+static void free_subscriber( struct subscriber *sub )
+{
+    msi_free(sub->event);
+    msi_free(sub->control);
+    msi_free(sub->attribute);
+    msi_free(sub);
+}
+
+VOID ControlEvent_SubscribeToEvent( MSIPACKAGE *package, LPCWSTR event,
+                                    LPCWSTR control, LPCWSTR attribute )
+{
+    struct subscriber *sub;
+
+    sub = msi_alloc(sizeof (*sub));
+    if( !sub )
+        return;
+    sub->event = strdupW(event);
+    sub->control = strdupW(control);
+    sub->attribute = strdupW(attribute);
+    list_add_tail( &package->subscriptions, &sub->entry );
+}
+
+VOID ControlEvent_UnSubscribeToEvent( MSIPACKAGE *package, LPCWSTR event,
+                                      LPCWSTR control, LPCWSTR attribute )
+{
+    struct list *i, *t;
+    struct subscriber *sub;
+
+    LIST_FOR_EACH_SAFE( i, t, &package->subscriptions )
+    {
+        sub = LIST_ENTRY( i, struct subscriber, entry );
+
+        if( lstrcmpiW(sub->control,control) )
+            continue;
+        if( lstrcmpiW(sub->attribute,attribute) )
+            continue;
+        if( lstrcmpiW(sub->event,event) )
+            continue;
+        list_remove( &sub->entry );
+        free_subscriber( sub );
+    }
+}
+
+VOID ControlEvent_FireSubscribedEvent( MSIPACKAGE *package, LPCWSTR event, 
+                                       MSIRECORD *rec )
+{
+    struct subscriber *sub;
+
+    TRACE("Firing Event %s\n",debugstr_w(event));
+
+    if (!package->dialog)
+        return;
+
+    LIST_FOR_EACH_ENTRY( sub, &package->subscriptions, struct subscriber, entry )
+    {
+        if (lstrcmpiW(sub->event, event))
+            continue;
+        msi_dialog_handle_event( package->dialog, sub->control,
+                                 sub->attribute, rec );
+    }
+}
+
+VOID ControlEvent_CleanupSubscriptions(MSIPACKAGE *package)
+{
+    struct list *i, *t;
+    struct subscriber *sub;
+
+    LIST_FOR_EACH_SAFE( i, t, &package->subscriptions )
+    {
+        sub = LIST_ENTRY( i, struct subscriber, entry );
+
+        list_remove( &sub->entry );
+        free_subscriber( sub );
+    }
+}
+
+/*
+ * ACTION_DialogBox()
+ *
+ * Return ERROR_SUCCESS if dialog is process and ERROR_FUNCTION_FAILED
+ * if the given parameter is not a dialog box
+ */
+UINT ACTION_DialogBox( MSIPACKAGE* package, LPCWSTR szDialogName )
+{
+    UINT r = ERROR_SUCCESS;
+
+    if( package->next_dialog )
+        ERR("Already a next dialog... ignoring it\n");
+    package->next_dialog = NULL;
+
+    /*
+     * Dialogs are chained by filling in the next_dialog member
+     *  of the package structure, then terminating the current dialog.
+     *  The code below sees the next_dialog member set, and runs the
+     *  next dialog.
+     * We fall out of the loop below if we come across a modeless
+     *  dialog, as it returns ERROR_IO_PENDING when we try to run
+     *  its message loop.
+     */
+    r = event_do_dialog( package, szDialogName, TRUE );
+    while( r == ERROR_SUCCESS && package->next_dialog )
+    {
+        LPWSTR name = package->next_dialog;
+
+        package->next_dialog = NULL;
+        r = event_do_dialog( package, name, TRUE );
+        msi_free( name );
+    }
+
+    if( r == ERROR_IO_PENDING )
+        r = ERROR_SUCCESS;
+
+    return r;
+}
+
+struct _events Events[] = {
+    { "EndDialog",ControlEvent_EndDialog },
+    { "NewDialog",ControlEvent_NewDialog },
+    { "SpawnDialog",ControlEvent_SpawnDialog },
+    { "SpawnWaitDialog",ControlEvent_SpawnWaitDialog },
+    { "DoAction",ControlEvent_DoAction },
+    { "AddLocal",ControlEvent_AddLocal },
+    { "Remove",ControlEvent_Remove },
+    { "AddSource",ControlEvent_AddSource },
+    { "SetTargetPath",ControlEvent_SetTargetPath },
+    { "Reset",ControlEvent_Reset },
+    { NULL,NULL },
+};
+
+UINT ControlEvent_HandleControlEvent(MSIPACKAGE *package, LPCWSTR event,
+                                     LPCWSTR argument, msi_dialog* dialog)
+{
+    int i = 0;
+    UINT rc = ERROR_SUCCESS;
+
+    TRACE("Handling Control Event %s\n",debugstr_w(event));
+    if (!event)
+        return rc;
+
+    while( Events[i].event != NULL)
+    {
+        LPWSTR wevent = strdupAtoW(Events[i].event);
+        if (lstrcmpW(wevent,event)==0)
+        {
+            msi_free(wevent);
+            rc = Events[i].handler(package,argument,dialog);
+            return rc;
+        }
+        msi_free(wevent);
+        i++;
+    }
+    FIXME("unhandled control event %s arg(%s)\n",
+          debugstr_w(event), debugstr_w(argument));
+    return rc;
+}
diff --git a/reactos/dll/msi/files.c b/reactos/dll/msi/files.c
new file mode 100644 (file)
index 0000000..694f6d6
--- /dev/null
@@ -0,0 +1,887 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2005 Aric Stewart for CodeWeavers
+ *
+ * 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
+ */
+
+
+/*
+ * Actions dealing with files These are
+ *
+ * InstallFiles
+ * DuplicateFiles
+ * MoveFiles (TODO)
+ * PatchFiles (TODO)
+ * RemoveDuplicateFiles(TODO)
+ * RemoveFiles(TODO)
+ */
+
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "wine/debug.h"
+#include "fdi.h"
+#include "msi.h"
+#include "msidefs.h"
+#include "msvcrt/fcntl.h"
+#include "msipriv.h"
+#include "winuser.h"
+#include "wine/unicode.h"
+#include "action.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+extern const WCHAR szInstallFiles[];
+extern const WCHAR szDuplicateFiles[];
+extern const WCHAR szMoveFiles[];
+extern const WCHAR szPatchFiles[];
+extern const WCHAR szRemoveDuplicateFiles[];
+extern const WCHAR szRemoveFiles[];
+
+static const WCHAR cszTempFolder[]= {'T','e','m','p','F','o','l','d','e','r',0};
+
+
+/*
+ * This is a helper function for handling embedded cabinet media
+ */
+static UINT writeout_cabinet_stream(MSIPACKAGE *package, LPCWSTR stream_name,
+                                    WCHAR* source)
+{
+    UINT rc;
+    USHORT* data;
+    UINT    size;
+    DWORD   write;
+    HANDLE  the_file;
+    WCHAR tmp[MAX_PATH];
+
+    rc = read_raw_stream_data(package->db,stream_name,&data,&size); 
+    if (rc != ERROR_SUCCESS)
+        return rc;
+
+    write = MAX_PATH;
+    if (MSI_GetPropertyW(package, cszTempFolder, tmp, &write))
+        GetTempPathW(MAX_PATH,tmp);
+
+    GetTempFileNameW(tmp,stream_name,0,source);
+
+    track_tempfile(package,strrchrW(source,'\\'), source);
+    the_file = CreateFileW(source, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
+                           FILE_ATTRIBUTE_NORMAL, NULL);
+
+    if (the_file == INVALID_HANDLE_VALUE)
+    {
+        ERR("Unable to create file %s\n",debugstr_w(source));
+        rc = ERROR_FUNCTION_FAILED;
+        goto end;
+    }
+
+    WriteFile(the_file,data,size,&write,NULL);
+    CloseHandle(the_file);
+    TRACE("wrote %li bytes to %s\n",write,debugstr_w(source));
+end:
+    msi_free(data);
+    return rc;
+}
+
+
+/* Support functions for FDI functions */
+typedef struct
+{
+    MSIPACKAGE* package;
+    LPCSTR cab_path;
+} CabData;
+
+static void * cabinet_alloc(ULONG cb)
+{
+    return msi_alloc(cb);
+}
+
+static void cabinet_free(void *pv)
+{
+    msi_free(pv);
+}
+
+static INT_PTR cabinet_open(char *pszFile, int oflag, int pmode)
+{
+    HANDLE handle;
+    DWORD dwAccess = 0;
+    DWORD dwShareMode = 0;
+    DWORD dwCreateDisposition = OPEN_EXISTING;
+    switch (oflag & _O_ACCMODE)
+    {
+    case _O_RDONLY:
+        dwAccess = GENERIC_READ;
+        dwShareMode = FILE_SHARE_READ | FILE_SHARE_DELETE;
+        break;
+    case _O_WRONLY:
+        dwAccess = GENERIC_WRITE;
+        dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
+        break;
+    case _O_RDWR:
+        dwAccess = GENERIC_READ | GENERIC_WRITE;
+        dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
+        break;
+    }
+    if ((oflag & (_O_CREAT | _O_EXCL)) == (_O_CREAT | _O_EXCL))
+        dwCreateDisposition = CREATE_NEW;
+    else if (oflag & _O_CREAT)
+        dwCreateDisposition = CREATE_ALWAYS;
+    handle = CreateFileA( pszFile, dwAccess, dwShareMode, NULL, 
+                          dwCreateDisposition, 0, NULL );
+    if (handle == INVALID_HANDLE_VALUE)
+        return 0;
+    return (INT_PTR) handle;
+}
+
+static UINT cabinet_read(INT_PTR hf, void *pv, UINT cb)
+{
+    HANDLE handle = (HANDLE) hf;
+    DWORD dwRead;
+    if (ReadFile(handle, pv, cb, &dwRead, NULL))
+        return dwRead;
+    return 0;
+}
+
+static UINT cabinet_write(INT_PTR hf, void *pv, UINT cb)
+{
+    HANDLE handle = (HANDLE) hf;
+    DWORD dwWritten;
+    if (WriteFile(handle, pv, cb, &dwWritten, NULL))
+        return dwWritten;
+    return 0;
+}
+
+static int cabinet_close(INT_PTR hf)
+{
+    HANDLE handle = (HANDLE) hf;
+    return CloseHandle(handle) ? 0 : -1;
+}
+
+static long cabinet_seek(INT_PTR hf, long dist, int seektype)
+{
+    HANDLE handle = (HANDLE) hf;
+    /* flags are compatible and so are passed straight through */
+    return SetFilePointer(handle, dist, NULL, seektype);
+}
+
+static void msi_file_update_ui( MSIPACKAGE *package, MSIFILE *f )
+{
+    MSIRECORD *uirow;
+    LPWSTR uipath, p;
+
+    /* the UI chunk */
+    uirow = MSI_CreateRecord( 9 );
+    MSI_RecordSetStringW( uirow, 1, f->FileName );
+    uipath = strdupW( f->TargetPath );
+    p = strrchrW(uipath,'\\');
+    if (p)
+        p[1]=0;
+    MSI_RecordSetStringW( uirow, 9, uipath);
+    MSI_RecordSetInteger( uirow, 6, f->FileSize );
+    ui_actiondata( package, szInstallFiles, uirow);
+    msiobj_release( &uirow->hdr );
+    msi_free( uipath );
+    ui_progress( package, 2, f->FileSize, 0, 0);
+}
+
+static INT_PTR cabinet_notify(FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin)
+{
+    switch (fdint)
+    {
+    case fdintCOPY_FILE:
+    {
+        CabData *data = (CabData*) pfdin->pv;
+        HANDLE handle;
+        LPWSTR file;
+        MSIFILE *f;
+
+        file = strdupAtoW(pfdin->psz1);
+        f = get_loaded_file(data->package, file);
+        msi_free(file);
+
+        if (!f)
+        {
+            ERR("Unknown File in Cabinet (%s)\n",debugstr_a(pfdin->psz1));
+            return 0;
+        }
+
+        if (f->state != msifs_missing && f->state != msifs_overwrite)
+        {
+            TRACE("Skipping extraction of %s\n",debugstr_a(pfdin->psz1));
+            return 0;
+        }
+
+        msi_file_update_ui( data->package, f );
+
+        TRACE("extracting %s\n", debugstr_w(f->TargetPath) );
+
+        handle = CreateFileW( f->TargetPath, GENERIC_READ | GENERIC_WRITE, 0,
+                              NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL );
+        if ( handle == INVALID_HANDLE_VALUE )
+        {
+            ERR("failed to create %s (error %ld)\n",
+                debugstr_w( f->TargetPath ), GetLastError() );
+            return 0;
+        }
+
+        f->state = msifs_installed;
+        return (INT_PTR) handle;
+    }
+    case fdintCLOSE_FILE_INFO:
+    {
+        FILETIME ft;
+        FILETIME ftLocal;
+        HANDLE handle = (HANDLE) pfdin->hf;
+
+        if (!DosDateTimeToFileTime(pfdin->date, pfdin->time, &ft))
+            return -1;
+        if (!LocalFileTimeToFileTime(&ft, &ftLocal))
+            return -1;
+        if (!SetFileTime(handle, &ftLocal, 0, &ftLocal))
+            return -1;
+        CloseHandle(handle);
+        return 1;
+    }
+    default:
+        return 0;
+    }
+}
+
+/***********************************************************************
+ *            extract_cabinet_file
+ *
+ * Extract files from a cab file.
+ */
+static BOOL extract_cabinet_file(MSIPACKAGE* package, LPCWSTR source, 
+                                 LPCWSTR path)
+{
+    HFDI hfdi;
+    ERF erf;
+    BOOL ret;
+    char *cabinet;
+    char *cab_path;
+    CabData data;
+
+    TRACE("Extracting %s to %s\n",debugstr_w(source), debugstr_w(path));
+
+    hfdi = FDICreate(cabinet_alloc,
+                     cabinet_free,
+                     cabinet_open,
+                     cabinet_read,
+                     cabinet_write,
+                     cabinet_close,
+                     cabinet_seek,
+                     0,
+                     &erf);
+    if (!hfdi)
+    {
+        ERR("FDICreate failed\n");
+        return FALSE;
+    }
+
+    if (!(cabinet = strdupWtoA( source )))
+    {
+        FDIDestroy(hfdi);
+        return FALSE;
+    }
+    if (!(cab_path = strdupWtoA( path )))
+    {
+        FDIDestroy(hfdi);
+        msi_free(cabinet);
+        return FALSE;
+    }
+
+    data.package = package;
+    data.cab_path = cab_path;
+
+    ret = FDICopy(hfdi, cabinet, "", 0, cabinet_notify, NULL, &data);
+
+    if (!ret)
+        ERR("FDICopy failed\n");
+
+    FDIDestroy(hfdi);
+
+    msi_free(cabinet);
+    msi_free(cab_path);
+
+    return ret;
+}
+
+static VOID set_file_source(MSIPACKAGE* package, MSIFILE* file, MSICOMPONENT*
+        comp, LPCWSTR path)
+{
+    if (file->Attributes & msidbFileAttributesNoncompressed)
+    {
+        LPWSTR p;
+        p = resolve_folder(package, comp->Directory, TRUE, FALSE, NULL);
+        file->SourcePath = build_directory_name(2, p, file->ShortName);
+        msi_free(p);
+    }
+    else
+        file->SourcePath = build_directory_name(2, path, file->File);
+}
+
+static BOOL check_volume(LPCWSTR path, LPCWSTR want_volume, LPWSTR volume, 
+        UINT *intype)
+{
+    WCHAR drive[4];
+    WCHAR name[MAX_PATH];
+    UINT type;
+
+    if (!(path[0] && path[1] == ':'))
+        return TRUE;
+
+    drive[0] = path[0];
+    drive[1] = path[1];
+    drive[2] = '\\';
+    drive[3] = 0;
+    TRACE("Checking volume %s .. (%s)\n",debugstr_w(drive), debugstr_w(want_volume));
+    type = GetDriveTypeW(drive);
+    TRACE("drive is of type %x\n",type);
+
+    if (type == DRIVE_UNKNOWN || type == DRIVE_NO_ROOT_DIR || 
+            type == DRIVE_FIXED || type == DRIVE_RAMDISK)
+        return TRUE;
+
+    GetVolumeInformationW(drive, name, MAX_PATH, NULL, NULL, NULL, NULL, 0);
+    TRACE("Drive contains %s\n", debugstr_w(name));
+    volume = strdupW(name);
+    if (*intype)
+        *intype=type;
+    return (strcmpiW(want_volume,name)==0);
+}
+
+static BOOL check_for_sourcefile(LPCWSTR source)
+{
+    DWORD attrib = GetFileAttributesW(source);
+    return (!(attrib == INVALID_FILE_ATTRIBUTES));
+}
+
+static UINT ready_volume(MSIPACKAGE* package, LPCWSTR path, LPWSTR last_volume, 
+                         MSIRECORD *row,UINT *type )
+{
+    LPWSTR volume = NULL; 
+    LPCWSTR want_volume = MSI_RecordGetString(row, 5);
+    BOOL ok = check_volume(path, want_volume, volume, type);
+
+    TRACE("Readying Volume for %s (%s, %s)\n", debugstr_w(path),
+          debugstr_w(want_volume), debugstr_w(last_volume));
+
+    if (check_for_sourcefile(path) && !ok)
+    {
+        FIXME("Found the Sourcefile but not on the correct volume.(%s,%s,%s)\n",
+                debugstr_w(path),debugstr_w(want_volume), debugstr_w(volume));
+        return ERROR_SUCCESS;
+    }
+
+    while (!ok)
+    {
+        INT rc;
+        LPCWSTR prompt;
+        LPWSTR msg;
+      
+        prompt = MSI_RecordGetString(row,3);
+        msg = generate_error_string(package, 1302, 1, prompt);
+        rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
+        msi_free(volume);
+        msi_free(msg);
+        if (rc == IDOK)
+            ok = check_for_sourcefile(path);
+        else
+            return ERROR_INSTALL_USEREXIT;
+    }
+
+    msi_free(last_volume);
+    last_volume = strdupW(volume);
+    return ERROR_SUCCESS;
+}
+
+struct media_info {
+    UINT last_sequence; 
+    LPWSTR last_volume;
+    LPWSTR last_path;
+    DWORD count;
+    WCHAR source[MAX_PATH];
+};
+
+static struct media_info *create_media_info( void )
+{
+    struct media_info *mi;
+
+    mi = msi_alloc( sizeof *mi  );
+    if (mi)
+    {
+        mi->last_sequence = 0; 
+        mi->last_volume = NULL;
+        mi->last_path = NULL;
+        mi->count = 0;
+        mi->source[0] = 0;
+    }
+
+    return mi;
+}
+
+static void free_media_info( struct media_info *mi )
+{
+    msi_free( mi->last_path );
+    msi_free( mi );
+}
+
+static UINT ready_media_for_file( MSIPACKAGE *package, struct media_info *mi,
+                                  MSIFILE *file )
+{
+    UINT rc = ERROR_SUCCESS;
+    MSIRECORD * row = 0;
+    static const WCHAR ExecSeqQuery[] =
+        {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
+         '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
+         '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=',
+         ' ','%', 'i',' ','O','R','D','E','R',' ','B','Y',' ',
+         '`','L','a','s','t','S','e','q','u','e','n','c','e','`',0};
+    LPCWSTR cab, volume;
+    DWORD sz;
+    INT seq;
+    UINT type;
+    LPCWSTR prompt;
+    MSICOMPONENT *comp = file->Component;
+
+    if (file->Sequence <= mi->last_sequence)
+    {
+        set_file_source(package,file,comp,mi->last_path);
+        TRACE("Media already ready (%u, %u)\n",file->Sequence,mi->last_sequence);
+        return ERROR_SUCCESS;
+    }
+
+    mi->count ++;
+    row = MSI_QueryGetRecord(package->db, ExecSeqQuery, file->Sequence);
+    if (!row)
+    {
+        TRACE("Unable to query row\n");
+        return ERROR_FUNCTION_FAILED;
+    }
+
+    seq = MSI_RecordGetInteger(row,2);
+    mi->last_sequence = seq;
+
+    volume = MSI_RecordGetString(row, 5);
+    prompt = MSI_RecordGetString(row, 3);
+
+    msi_free(mi->last_path);
+    mi->last_path = NULL;
+
+    if (file->Attributes & msidbFileAttributesNoncompressed)
+    {
+        mi->last_path = resolve_folder(package, comp->Directory, TRUE, FALSE, NULL);
+        set_file_source(package,file,comp,mi->last_path);
+        rc = ready_volume(package, file->SourcePath, mi->last_volume, row,&type);
+
+        MsiSourceListAddMediaDiskW(package->ProductCode, NULL, 
+            MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT, mi->count, volume,
+            prompt);
+
+        if (type == DRIVE_REMOVABLE || type == DRIVE_CDROM || 
+                type == DRIVE_RAMDISK)
+            MsiSourceListSetInfoW(package->ProductCode, NULL, 
+                MSIINSTALLCONTEXT_USERMANAGED, 
+                MSICODE_PRODUCT|MSISOURCETYPE_MEDIA,
+                INSTALLPROPERTY_LASTUSEDSOURCEW, mi->last_path);
+        else
+            MsiSourceListSetInfoW(package->ProductCode, NULL, 
+                MSIINSTALLCONTEXT_USERMANAGED, 
+                MSICODE_PRODUCT|MSISOURCETYPE_NETWORK,
+                INSTALLPROPERTY_LASTUSEDSOURCEW, mi->last_path);
+        msiobj_release(&row->hdr);
+        return rc;
+    }
+
+    cab = MSI_RecordGetString(row,4);
+    if (cab)
+    {
+        TRACE("Source is CAB %s\n",debugstr_w(cab));
+        /* the stream does not contain the # character */
+        if (cab[0]=='#')
+        {
+            LPWSTR path;
+
+            writeout_cabinet_stream(package,&cab[1],mi->source);
+            mi->last_path = strdupW(mi->source);
+            *(strrchrW(mi->last_path,'\\')+1)=0;
+
+            path = msi_dup_property( package, cszSourceDir );
+
+            MsiSourceListAddMediaDiskW(package->ProductCode, NULL, 
+                MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT, mi->count,
+                volume, prompt);
+
+            MsiSourceListSetInfoW(package->ProductCode, NULL,
+                MSIINSTALLCONTEXT_USERMANAGED,
+                MSICODE_PRODUCT|MSISOURCETYPE_NETWORK,
+                INSTALLPROPERTY_LASTUSEDSOURCEW, path);
+
+            msi_free(path);
+        }
+        else
+        {
+            sz = MAX_PATH;
+            mi->last_path = msi_alloc(MAX_PATH*sizeof(WCHAR));
+            if (MSI_GetPropertyW(package, cszSourceDir, mi->source, &sz))
+            {
+                ERR("No Source dir defined\n");
+                rc = ERROR_FUNCTION_FAILED;
+            }
+            else
+            {
+                strcpyW(mi->last_path,mi->source);
+                strcatW(mi->source,cab);
+
+                rc = ready_volume(package, mi->source, mi->last_volume, row, &type);
+                if (type == DRIVE_REMOVABLE || type == DRIVE_CDROM || 
+                        type == DRIVE_RAMDISK)
+                    MsiSourceListSetInfoW(package->ProductCode, NULL,
+                            MSIINSTALLCONTEXT_USERMANAGED,
+                            MSICODE_PRODUCT|MSISOURCETYPE_MEDIA,
+                            INSTALLPROPERTY_LASTUSEDSOURCEW, mi->last_path);
+                else
+                    MsiSourceListSetInfoW(package->ProductCode, NULL,
+                            MSIINSTALLCONTEXT_USERMANAGED,
+                            MSICODE_PRODUCT|MSISOURCETYPE_NETWORK,
+                            INSTALLPROPERTY_LASTUSEDSOURCEW, mi->last_path);
+
+                /* extract the cab file into a folder in the temp folder */
+                sz = MAX_PATH;
+                if (MSI_GetPropertyW(package, cszTempFolder,mi->last_path, &sz) 
+                                    != ERROR_SUCCESS)
+                    GetTempPathW(MAX_PATH,mi->last_path);
+            }
+        }
+        rc = !extract_cabinet_file(package, mi->source, mi->last_path);
+    }
+    else
+    {
+        sz = MAX_PATH;
+        mi->last_path = msi_alloc(MAX_PATH*sizeof(WCHAR));
+        MSI_GetPropertyW(package,cszSourceDir,mi->source,&sz);
+        strcpyW(mi->last_path,mi->source);
+        rc = ready_volume(package, mi->last_path, mi->last_volume, row, &type);
+
+        if (type == DRIVE_REMOVABLE || type == DRIVE_CDROM || 
+                type == DRIVE_RAMDISK)
+            MsiSourceListSetInfoW(package->ProductCode, NULL,
+                    MSIINSTALLCONTEXT_USERMANAGED,
+                    MSICODE_PRODUCT|MSISOURCETYPE_MEDIA,
+                    INSTALLPROPERTY_LASTUSEDSOURCEW, mi->last_path);
+        else
+            MsiSourceListSetInfoW(package->ProductCode, NULL,
+                    MSIINSTALLCONTEXT_USERMANAGED,
+                    MSICODE_PRODUCT|MSISOURCETYPE_NETWORK,
+                    INSTALLPROPERTY_LASTUSEDSOURCEW, mi->last_path);
+    }
+    set_file_source(package, file, comp, mi->last_path);
+
+    MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
+            MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT, mi->count, volume,
+            prompt);
+
+    msiobj_release(&row->hdr);
+
+    return rc;
+}
+
+static UINT get_file_target(MSIPACKAGE *package, LPCWSTR file_key, 
+                                   LPWSTR* file_source)
+{
+    MSIFILE *file;
+
+    LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
+    {
+        if (lstrcmpW( file_key, file->File )==0)
+        {
+            if (file->state >= msifs_overwrite)
+            {
+                *file_source = strdupW( file->TargetPath );
+                return ERROR_SUCCESS;
+            }
+            else
+                return ERROR_FILE_NOT_FOUND;
+        }
+    }
+
+    return ERROR_FUNCTION_FAILED;
+}
+
+/*
+ * ACTION_InstallFiles()
+ * 
+ * For efficiency, this is done in two passes:
+ * 1) Correct all the TargetPaths and determine what files are to be installed.
+ * 2) Extract Cabinets and copy files.
+ */
+UINT ACTION_InstallFiles(MSIPACKAGE *package)
+{
+    struct media_info *mi;
+    UINT rc = ERROR_SUCCESS;
+    LPWSTR ptr;
+    MSIFILE *file;
+
+    /* increment progress bar each time action data is sent */
+    ui_progress(package,1,1,0,0);
+
+    /* handle the keys for the SourceList */
+    ptr = strrchrW(package->PackagePath,'\\');
+    if (ptr)
+    {
+        ptr ++;
+        MsiSourceListSetInfoW(package->ProductCode, NULL,
+                MSIINSTALLCONTEXT_USERMANAGED,
+                MSICODE_PRODUCT,
+                INSTALLPROPERTY_PACKAGENAMEW, ptr);
+    }
+    /* FIXME("Write DiskPrompt\n"); */
+    
+    /* Pass 1 */
+    LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
+    {
+        if (!ACTION_VerifyComponentForAction( file->Component, INSTALLSTATE_LOCAL ))
+        {
+            ui_progress(package,2,file->FileSize,0,0);
+            TRACE("File %s is not scheduled for install\n",
+                   debugstr_w(file->File));
+
+            file->state = msifs_skipped;
+        }
+    }
+
+    /*
+     * Despite MSDN specifying that the CreateFolders action
+     * should be called before InstallFiles, some installers don't
+     * do that, and they seem to work correctly.  We need to create
+     * directories here to make sure that the files can be copied.
+     */
+    msi_create_component_directories( package );
+
+    mi = create_media_info();
+
+    /* Pass 2 */
+    LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
+    {
+        if (file->state != msifs_missing && file->state != msifs_overwrite)
+            continue;
+
+        TRACE("Pass 2: %s\n",debugstr_w(file->File));
+
+        rc = ready_media_for_file( package, mi, file );
+        if (rc != ERROR_SUCCESS)
+        {
+            ERR("Unable to ready media\n");
+            rc = ERROR_FUNCTION_FAILED;
+            break;
+        }
+
+        TRACE("file paths %s to %s\n",debugstr_w(file->SourcePath),
+              debugstr_w(file->TargetPath));
+
+        if (file->state != msifs_missing && file->state != msifs_overwrite)
+            continue;
+
+        /* compressed files are extracted in ready_media_for_file */
+        if (~file->Attributes & msidbFileAttributesNoncompressed)
+        {
+            if (INVALID_FILE_ATTRIBUTES == GetFileAttributesW(file->TargetPath))
+                ERR("compressed file wasn't extracted (%s)\n",
+                    debugstr_w(file->TargetPath));
+            continue;
+        }
+
+        rc = CopyFileW(file->SourcePath,file->TargetPath,FALSE);
+        if (!rc)
+        {
+            rc = GetLastError();
+            ERR("Unable to copy file (%s -> %s) (error %d)\n",
+                debugstr_w(file->SourcePath), debugstr_w(file->TargetPath), rc);
+            if (rc == ERROR_ALREADY_EXISTS && file->state == msifs_overwrite)
+            {
+                rc = 0;
+            }
+            else if (rc == ERROR_FILE_NOT_FOUND)
+            {
+                ERR("Source File Not Found!  Continuing\n");
+                rc = 0;
+            }
+            else if (file->Attributes & msidbFileAttributesVital)
+            {
+                ERR("Ignoring Error and continuing (nonvital file)...\n");
+                rc = 0;
+            }
+        }
+        else
+        {
+            file->state = msifs_installed;
+            rc = ERROR_SUCCESS;
+        }
+    }
+
+    /* cleanup */
+    free_media_info( mi );
+    return rc;
+}
+
+static UINT ITERATE_DuplicateFiles(MSIRECORD *row, LPVOID param)
+{
+    MSIPACKAGE *package = (MSIPACKAGE*)param;
+    WCHAR *file_source = NULL;
+    WCHAR dest_name[0x100];
+    LPWSTR dest_path, dest;
+    LPCWSTR file_key, component;
+    DWORD sz;
+    DWORD rc;
+    MSICOMPONENT *comp;
+
+    component = MSI_RecordGetString(row,2);
+    comp = get_loaded_component(package,component);
+
+    if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
+    {
+        TRACE("Skipping copy due to disabled component %s\n",
+                        debugstr_w(component));
+
+        /* the action taken was the same as the current install state */        
+        comp->Action = comp->Installed;
+
+        return ERROR_SUCCESS;
+    }
+
+    comp->Action = INSTALLSTATE_LOCAL;
+
+    file_key = MSI_RecordGetString(row,3);
+    if (!file_key)
+    {
+        ERR("Unable to get file key\n");
+        return ERROR_FUNCTION_FAILED;
+    }
+
+    rc = get_file_target(package,file_key,&file_source);
+
+    if (rc != ERROR_SUCCESS)
+    {
+        ERR("Original file unknown %s\n",debugstr_w(file_key));
+        msi_free(file_source);
+        return ERROR_SUCCESS;
+    }
+
+    if (MSI_RecordIsNull(row,4))
+        strcpyW(dest_name,strrchrW(file_source,'\\')+1);
+    else
+    {
+        sz=0x100;
+        MSI_RecordGetStringW(row,4,dest_name,&sz);
+        reduce_to_longfilename(dest_name);
+    }
+
+    if (MSI_RecordIsNull(row,5))
+    {
+        LPWSTR p;
+        dest_path = strdupW(file_source);
+        p = strrchrW(dest_path,'\\');
+        if (p)
+            *p=0;
+    }
+    else
+    {
+        LPCWSTR destkey;
+        destkey = MSI_RecordGetString(row,5);
+        dest_path = resolve_folder(package, destkey, FALSE,FALSE,NULL);
+        if (!dest_path)
+        {
+            /* try a Property */
+            dest_path = msi_dup_property( package, destkey );
+            if (!dest_path)
+            {
+                FIXME("Unable to get destination folder, try AppSearch properties\n");
+                msi_free(file_source);
+                return ERROR_SUCCESS;
+            }
+        }
+    }
+
+    dest = build_directory_name(2, dest_path, dest_name);
+
+    TRACE("Duplicating file %s to %s\n",debugstr_w(file_source),
+                    debugstr_w(dest)); 
+
+    if (strcmpW(file_source,dest))
+        rc = !CopyFileW(file_source,dest,TRUE);
+    else
+        rc = ERROR_SUCCESS;
+
+    if (rc != ERROR_SUCCESS)
+        ERR("Failed to copy file %s -> %s, last error %ld\n",
+            debugstr_w(file_source), debugstr_w(dest_path), GetLastError());
+
+    FIXME("We should track these duplicate files as well\n");   
+
+    msi_free(dest_path);
+    msi_free(dest);
+    msi_free(file_source);
+
+    return ERROR_SUCCESS;
+}
+
+UINT ACTION_DuplicateFiles(MSIPACKAGE *package)
+{
+    UINT rc;
+    MSIQUERY * view;
+    static const WCHAR ExecSeqQuery[] =
+        {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
+         '`','D','u','p','l','i','c','a','t','e','F','i','l','e','`',0};
+
+    rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
+    if (rc != ERROR_SUCCESS)
+        return ERROR_SUCCESS;
+
+    rc = MSI_IterateRecords(view, NULL, ITERATE_DuplicateFiles, package);
+    msiobj_release(&view->hdr);
+
+    return rc;
+}
+
+UINT ACTION_RemoveFiles( MSIPACKAGE *package )
+{
+    MSIFILE *file;
+
+    LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
+    {
+        if ( !file->Component )
+            continue;
+        if ( file->Component->Installed == INSTALLSTATE_LOCAL )
+            continue;
+
+        if ( file->state == msifs_installed )
+            ERR("removing installed file %s\n", debugstr_w(file->TargetPath));
+
+        if ( file->state != msifs_present )
+            continue;
+
+        TRACE("removing %s\n", debugstr_w(file->File) );
+        if ( !DeleteFileW( file->TargetPath ) )
+            ERR("failed to delete %s\n",  debugstr_w(file->TargetPath) );
+        file->state = msifs_missing;
+    }
+
+    return ERROR_SUCCESS;
+}
diff --git a/reactos/dll/msi/format.c b/reactos/dll/msi/format.c
new file mode 100644 (file)
index 0000000..85f9028
--- /dev/null
@@ -0,0 +1,735 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2005 Mike McCormack for CodeWeavers
+ * Copyright 2005 Aric Stewart for CodeWeavers
+ *
+ * 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
+ */
+
+/*
+http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/msiformatrecord.asp 
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#define COBJMACROS
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "wine/debug.h"
+#include "msi.h"
+#include "msipriv.h"
+#include "winnls.h"
+#include "wine/unicode.h"
+#include "action.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+
+static DWORD deformat_string_internal(MSIPACKAGE *package, LPCWSTR ptr, 
+                                     WCHAR** data, DWORD len, MSIRECORD* record,
+                                     BOOL* in_group);
+
+
+static LPWSTR build_default_format(MSIRECORD* record)
+{
+    int i;  
+    int count;
+    LPWSTR rc;
+    static const WCHAR fmt[] = {'%','i',':',' ','[','%','i',']',' ',0};
+    WCHAR buf[11];
+
+    count = MSI_RecordGetFieldCount(record);
+
+    rc = msi_alloc((11*count)*sizeof(WCHAR));
+    rc[0] = 0;
+    for (i = 1; i <= count; i++)
+    {
+        sprintfW(buf,fmt,i,i); 
+        strcatW(rc,buf);
+    }
+    return rc;
+}
+
+static const WCHAR* scanW(LPCWSTR buf, WCHAR token, DWORD len)
+{
+    DWORD i;
+    for (i = 0; i < len; i++)
+        if (buf[i] == token)
+            return &buf[i];
+    return NULL;
+}
+
+/* break out helper functions for deformating */
+static LPWSTR deformat_component(MSIPACKAGE* package, LPCWSTR key, DWORD* sz)
+{
+    LPWSTR value = NULL;
+    MSICOMPONENT *comp;
+
+    *sz = 0;
+    if (!package)
+        return NULL;
+
+    FIXME("component key %s\n", debugstr_w(key));
+    comp = get_loaded_component(package,key);
+    if (comp)
+    {
+        value = resolve_folder(package, comp->Directory, FALSE, FALSE, NULL);
+        *sz = (strlenW(value)) * sizeof(WCHAR);
+    }
+
+    return value;
+}
+
+static LPWSTR deformat_file(MSIPACKAGE* package, LPCWSTR key, DWORD* sz, 
+                            BOOL shortname)
+{
+    LPWSTR value = NULL;
+    MSIFILE *file;
+
+    *sz = 0;
+
+    if (!package)
+        return NULL;
+
+    file = get_loaded_file( package, key );
+    if (file)
+    {
+        if (!shortname)
+        {
+            value = strdupW( file->TargetPath );
+            *sz = (strlenW(value)) * sizeof(WCHAR);
+        }
+        else
+        {
+            DWORD size = 0;
+            size = GetShortPathNameW( file->TargetPath, NULL, 0 );
+
+            if (size > 0)
+            {
+                *sz = (size-1) * sizeof (WCHAR);
+                size ++;
+                value = msi_alloc(size * sizeof(WCHAR));
+                GetShortPathNameW( file->TargetPath, value, size );
+            }
+            else
+            {
+                ERR("Unable to get ShortPath size (%s)\n",
+                    debugstr_w( file->TargetPath) );
+                value = NULL;
+                *sz = 0;
+            }
+        }
+    }
+
+    return value;
+}
+
+static LPWSTR deformat_environment(MSIPACKAGE* package, LPCWSTR key, 
+                                   DWORD* chunk)
+{
+    LPWSTR value = NULL;
+    DWORD sz;
+
+    sz  = GetEnvironmentVariableW(key,NULL,0);
+    if (sz > 0)
+    {
+        sz++;
+        value = msi_alloc(sz * sizeof(WCHAR));
+        GetEnvironmentVariableW(key,value,sz);
+        *chunk = (strlenW(value)) * sizeof(WCHAR);
+    }
+    else
+    {
+        ERR("Unknown environment variable %s\n", debugstr_w(key));
+        *chunk = 0;
+        value = NULL;
+    }
+    return value;
+}
+
+static LPWSTR deformat_NULL(DWORD* chunk)
+{
+    LPWSTR value;
+
+    value = msi_alloc(sizeof(WCHAR)*2);
+    value[0] =  0;
+    *chunk = sizeof(WCHAR);
+    return value;
+}
+
+static LPWSTR deformat_escape(LPCWSTR key, DWORD* chunk)
+{
+    LPWSTR value;
+
+    value = msi_alloc(sizeof(WCHAR)*2);
+    value[0] =  key[0];
+    *chunk = sizeof(WCHAR);
+
+    return value;
+}
+
+
+static BOOL is_key_number(LPCWSTR key)
+{
+    INT index = 0;
+    if (key[0] == 0)
+        return FALSE;
+
+    while (isdigitW(key[index])) index++;
+    if (key[index] == 0)
+        return TRUE;
+    else
+        return FALSE;
+}
+
+static LPWSTR deformat_index(MSIRECORD* record, LPCWSTR key, DWORD* chunk )
+{
+    INT index;
+    LPWSTR value; 
+
+    index = atoiW(key);
+    TRACE("record index %i\n",index);
+    value = msi_dup_record_field(record,index);
+    if (value)
+        *chunk = strlenW(value) * sizeof(WCHAR);
+    else
+    {
+        value = NULL;
+        *chunk = 0;
+    }
+    return value;
+}
+
+static LPWSTR deformat_property(MSIPACKAGE* package, LPCWSTR key, DWORD* chunk)
+{
+    LPWSTR value;
+
+    if (!package)
+        return NULL;
+
+    value = msi_dup_property( package, key );
+
+    if (value)
+        *chunk = (strlenW(value)) * sizeof(WCHAR);
+
+    return value;
+}
+
+/*
+ * Groups cannot be nested. They are just treated as from { to next } 
+ */
+static BOOL find_next_group(LPCWSTR source, DWORD len_remaining,
+                                    LPWSTR *group, LPCWSTR *mark, 
+                                    LPCWSTR* mark2)
+{
+    int i;
+    BOOL found = FALSE;
+
+    *mark = scanW(source,'{',len_remaining);
+    if (!*mark)
+        return FALSE;
+
+    for (i = 1; (*mark - source) + i < len_remaining; i++)
+    {
+        if ((*mark)[i] == '}')
+        {
+            found = TRUE;
+            break;
+        }
+    }
+    if (! found)
+        return FALSE;
+
+    *mark2 = &(*mark)[i]; 
+
+    i = *mark2 - *mark;
+    *group = msi_alloc(i*sizeof(WCHAR));
+
+    i -= 1;
+    memcpy(*group,&(*mark)[1],i*sizeof(WCHAR));
+    (*group)[i] = 0;
+
+    TRACE("Found group %s\n",debugstr_w(*group));
+    return TRUE;
+}
+
+
+static BOOL find_next_outermost_key(LPCWSTR source, DWORD len_remaining,
+                                    LPWSTR *key, LPCWSTR *mark, LPCWSTR* mark2, 
+                                    BOOL *nested)
+{
+    INT count = 0;
+    INT total_count = 0;
+    int i;
+
+    *mark = scanW(source,'[',len_remaining);
+    if (!*mark)
+        return FALSE;
+
+    count = 1;
+    total_count = 1;
+    *nested = FALSE;
+    for (i = 1; (*mark - source) + i < len_remaining && count > 0; i++)
+    {
+        if ((*mark)[i] == '[' && (*mark)[i-1] != '\\')
+        {
+            count ++;
+            total_count ++;
+            *nested = TRUE;
+        }
+        else if ((*mark)[i] == ']' && (*mark)[i-1] != '\\')
+        {
+            count --;
+        }
+    }
+
+    if (count > 0)
+        return FALSE;
+
+    *mark2 = &(*mark)[i-1]; 
+
+    i = *mark2 - *mark;
+    *key = msi_alloc(i*sizeof(WCHAR));
+    /* do not have the [] in the key */
+    i -= 1;
+    memcpy(*key,&(*mark)[1],i*sizeof(WCHAR));
+    (*key)[i] = 0;
+
+    TRACE("Found Key %s\n",debugstr_w(*key));
+    return TRUE;
+}
+
+static LPWSTR deformat_group(MSIPACKAGE* package, LPWSTR group, DWORD len, 
+                      MSIRECORD* record, DWORD* size)
+{
+    LPWSTR value = NULL;
+    LPCWSTR mark, mark2;
+    LPWSTR key;
+    BOOL nested;
+    INT failcount;
+    static const WCHAR fmt[] = {'{','%','s','}',0};
+    UINT sz;
+
+    if (!group || group[0] == 0) 
+    {
+        *size = 0;
+        return NULL;
+    }
+    /* if no [] then group is returned as is */
+
+     if (!find_next_outermost_key(group, len, &key, &mark, &mark2, &nested))
+     {
+         *size = (len+2)*sizeof(WCHAR);
+         value = msi_alloc(*size);
+         sprintfW(value,fmt,group);
+         /* do not return size of the null at the end */
+         *size = (len+1)*sizeof(WCHAR);
+         return value;
+     }
+
+     msi_free(key);
+     failcount = 0;
+     sz = deformat_string_internal(package, group, &value, strlenW(group),
+                                     record, &failcount);
+     if (failcount==0)
+     {
+        *size = sz * sizeof(WCHAR);
+        return value;
+     }
+     else if (failcount < 0)
+     {
+         LPWSTR v2;
+
+         v2 = msi_alloc((sz+2)*sizeof(WCHAR));
+         v2[0] = '{';
+         memcpy(&v2[1],value,sz*sizeof(WCHAR));
+         v2[sz+1]='}';
+         msi_free(value);
+
+         *size = (sz+2)*sizeof(WCHAR);
+         return v2;
+     }
+     else
+     {
+         msi_free(value);
+         *size = 0;
+         return NULL;
+     }
+}
+
+
+/*
+ * len is in WCHARs
+ * return is also in WCHARs
+ */
+static DWORD deformat_string_internal(MSIPACKAGE *package, LPCWSTR ptr, 
+                                     WCHAR** data, DWORD len, MSIRECORD* record,
+                                     INT* failcount)
+{
+    LPCWSTR mark = NULL;
+    LPCWSTR mark2 = NULL;
+    DWORD size=0;
+    DWORD chunk=0;
+    LPWSTR key;
+    LPWSTR value = NULL;
+    DWORD sz;
+    LPBYTE newdata = NULL;
+    const WCHAR* progress = NULL;
+    BOOL nested;
+
+    if (ptr==NULL)
+    {
+        TRACE("Deformatting NULL string\n");
+        *data = NULL;
+        return 0;
+    }
+
+    TRACE("Starting with %s\n",debugstr_wn(ptr,len));
+
+    /* scan for special characters... fast exit */
+    if ((!scanW(ptr,'[',len) || (scanW(ptr,'[',len) && !scanW(ptr,']',len))) && 
+        (scanW(ptr,'{',len) && !scanW(ptr,'}',len)))
+    {
+        /* not formatted */
+        *data = msi_alloc((len*sizeof(WCHAR)));
+        memcpy(*data,ptr,len*sizeof(WCHAR));
+        TRACE("Returning %s\n",debugstr_wn(*data,len));
+        return len;
+    }
+  
+    progress = ptr;
+
+    while (progress - ptr < len)
+    {
+        /* seek out first group if existing */
+        if (find_next_group(progress, len - (progress - ptr), &key,
+                                &mark, &mark2))
+        {
+            value = deformat_group(package, key, strlenW(key)+1, record, 
+                            &chunk);
+            msi_free( key );
+            key = NULL;
+            nested = FALSE;
+        }
+        /* formatted string located */
+        else if (!find_next_outermost_key(progress, len - (progress - ptr), 
+                                &key, &mark, &mark2, &nested))
+        {
+            LPBYTE nd2;
+
+            TRACE("after value %s\n", debugstr_wn((LPWSTR)newdata,
+                                    size/sizeof(WCHAR)));
+            chunk = (len - (progress - ptr)) * sizeof(WCHAR);
+            TRACE("after chunk is %li + %li\n",size,chunk);
+            if (size)
+                nd2 = msi_realloc(newdata,(size+chunk));
+            else
+                nd2 = msi_alloc(chunk);
+
+            newdata = nd2;
+            memcpy(&newdata[size],progress,chunk);
+            size+=chunk;
+            break;
+        }
+
+        if (mark != progress)
+        {
+            LPBYTE tgt;
+            DWORD old_size = size;
+            INT cnt = (mark - progress);
+            TRACE("%i  (%i) characters before marker\n",cnt,(mark-progress));
+            size += cnt * sizeof(WCHAR);
+            if (!old_size)
+                tgt = msi_alloc(size);
+            else
+                tgt = msi_realloc(newdata,size);
+            newdata  = tgt;
+            memcpy(&newdata[old_size],progress,(cnt * sizeof(WCHAR)));  
+        }
+
+        progress = mark;
+
+        if (nested)
+        {
+            TRACE("Nested key... %s\n",debugstr_w(key));
+            deformat_string_internal(package, key, &value, strlenW(key)+1,
+                                     record, failcount);
+
+            msi_free(key);
+            key = value;
+        }
+
+        TRACE("Current %s .. %s\n",debugstr_wn((LPWSTR)newdata, 
+                                size/sizeof(WCHAR)),debugstr_w(key));
+
+        if (!package)
+        {
+            /* only deformat number indexs */
+            if (key && is_key_number(key))
+            {
+                value = deformat_index(record,key,&chunk);  
+                if (!chunk && failcount && *failcount >= 0)
+                    (*failcount)++;
+            }
+            else
+            {
+                if (failcount)
+                    *failcount = -1;
+                if(key)
+                {
+                    DWORD keylen = strlenW(key);
+                    chunk = (keylen + 2)*sizeof(WCHAR);
+                    value = msi_alloc(chunk);
+                    value[0] = '[';
+                    memcpy(&value[1],key,keylen*sizeof(WCHAR));
+                    value[1+keylen] = ']';
+                }
+            }
+        }
+        else
+        {
+            sz = 0;
+            if (key) switch (key[0])
+            {
+                case '~':
+                    value = deformat_NULL(&chunk);
+                break;
+                case '$':
+                    value = deformat_component(package,&key[1],&chunk);
+                break;
+                case '#':
+                    value = deformat_file(package,&key[1], &chunk, FALSE);
+                break;
+                case '!': /* should be short path */
+                    value = deformat_file(package,&key[1], &chunk, TRUE);
+                break;
+                case '\\':
+                    value = deformat_escape(&key[1],&chunk);
+                break;
+                case '%':
+                    value = deformat_environment(package,&key[1],&chunk);
+                break;
+                default:
+                    /* index keys cannot be nested */
+                    if (is_key_number(key))
+                        if (!nested)
+                            value = deformat_index(record,key,&chunk);
+                        else
+                        {
+                            static const WCHAR fmt[] = {'[','%','s',']',0};
+                            value = msi_alloc(10);
+                            sprintfW(value,fmt,key);
+                            chunk = strlenW(value)*sizeof(WCHAR);
+                        }
+                    else
+                        value = deformat_property(package,key,&chunk);
+                break;      
+            }
+        }
+
+        msi_free(key);
+
+        if (value!=NULL)
+        {
+            LPBYTE nd2;
+            TRACE("value %s, chunk %li size %li\n",debugstr_w((LPWSTR)value),
+                    chunk, size);
+            if (size)
+                nd2= msi_realloc(newdata,(size + chunk));
+            else
+                nd2= msi_alloc(chunk);
+            newdata = nd2;
+            memcpy(&newdata[size],value,chunk);
+            size+=chunk;   
+            msi_free(value);
+        }
+        else if (failcount && *failcount >=0 )
+            (*failcount)++;
+
+        progress = mark2+1;
+    }
+
+    TRACE("after everything %s\n",debugstr_wn((LPWSTR)newdata, 
+                            size/sizeof(WCHAR)));
+
+    *data = (LPWSTR)newdata;
+    return size / sizeof(WCHAR);
+}
+
+
+UINT MSI_FormatRecordW( MSIPACKAGE* package, MSIRECORD* record, LPWSTR buffer,
+                        DWORD *size )
+{
+    LPWSTR deformated;
+    LPWSTR rec;
+    DWORD len;
+    UINT rc = ERROR_INVALID_PARAMETER;
+
+    TRACE("%p %p %p %li\n",package, record ,buffer, *size);
+
+    rec = msi_dup_record_field(record,0);
+    if (!rec)
+        rec = build_default_format(record);
+
+    TRACE("(%s)\n",debugstr_w(rec));
+
+    len = deformat_string_internal(package,rec,&deformated,strlenW(rec),
+                                   record, NULL);
+
+    if (buffer)
+    {
+        if (*size>len)
+        {
+            memcpy(buffer,deformated,len*sizeof(WCHAR));
+            rc = ERROR_SUCCESS;
+            buffer[len] = 0;
+        }
+        else
+        {
+            if (*size > 0)
+            {
+                memcpy(buffer,deformated,(*size)*sizeof(WCHAR));
+                buffer[(*size)-1] = 0;
+            }
+            rc = ERROR_MORE_DATA;
+        }
+    }
+    else
+        rc = ERROR_SUCCESS;
+
+    *size = len;
+
+    msi_free(rec);
+    msi_free(deformated);
+    return rc;
+}
+
+UINT MSI_FormatRecordA( MSIPACKAGE* package, MSIRECORD* record, LPSTR buffer,
+                        DWORD *size )
+{
+    LPWSTR deformated;
+    LPWSTR rec;
+    DWORD len,lenA;
+    UINT rc = ERROR_INVALID_PARAMETER;
+
+    TRACE("%p %p %p %li\n",package, record ,buffer, *size);
+
+    rec = msi_dup_record_field(record,0);
+    if (!rec)
+        rec = build_default_format(record);
+
+    TRACE("(%s)\n",debugstr_w(rec));
+
+    len = deformat_string_internal(package,rec,&deformated,strlenW(rec),
+                                   record, NULL);
+    /* If len is zero then WideCharToMultiByte will return 0 indicating 
+     * failure, but that will do just as well since we are ignoring
+     * possible errors.
+     */
+    lenA = WideCharToMultiByte(CP_ACP,0,deformated,len,NULL,0,NULL,NULL);
+
+    if (buffer)
+    {
+        /* Ditto above */
+        WideCharToMultiByte(CP_ACP,0,deformated,len,buffer,*size,NULL, NULL);
+        if (*size>lenA)
+        {
+            rc = ERROR_SUCCESS;
+            buffer[lenA] = 0;
+        }
+        else
+        {
+            rc = ERROR_MORE_DATA;
+            if (*size)
+                buffer[(*size)-1] = 0;
+        }
+    }
+    else
+        rc = ERROR_SUCCESS;
+
+    *size = lenA;
+
+    msi_free(rec);
+    msi_free(deformated);
+    return rc;
+}
+
+
+UINT WINAPI MsiFormatRecordW( MSIHANDLE hInstall, MSIHANDLE hRecord, 
+                              LPWSTR szResult, DWORD *sz )
+{
+    UINT r = ERROR_INVALID_HANDLE;
+    MSIPACKAGE *package;
+    MSIRECORD *record;
+
+    TRACE("%ld %ld %p %p\n", hInstall, hRecord, szResult, sz);
+
+    record = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD );
+
+    if (!record)
+        return ERROR_INVALID_HANDLE;
+    if (!sz)
+    {
+        msiobj_release( &record->hdr );
+        if (szResult)
+            return ERROR_INVALID_PARAMETER;
+        else
+            return ERROR_SUCCESS;
+    }
+
+    package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE );
+
+    r = MSI_FormatRecordW( package, record, szResult, sz );
+    msiobj_release( &record->hdr );
+    if (package)
+        msiobj_release( &package->hdr );
+    return r;
+}
+
+UINT WINAPI MsiFormatRecordA( MSIHANDLE hInstall, MSIHANDLE hRecord,
+                              LPSTR szResult, DWORD *sz )
+{
+    UINT r = ERROR_INVALID_HANDLE;
+    MSIPACKAGE *package = NULL;
+    MSIRECORD *record = NULL;
+
+    TRACE("%ld %ld %p %p\n", hInstall, hRecord, szResult, sz);
+
+    record = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD );
+
+    if (!record)
+        return ERROR_INVALID_HANDLE;
+    if (!sz)
+    {
+        msiobj_release( &record->hdr );
+        if (szResult)
+            return ERROR_INVALID_PARAMETER;
+        else
+            return ERROR_SUCCESS;
+    }
+
+    package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE );
+
+    r = MSI_FormatRecordA( package, record, szResult, sz );
+    msiobj_release( &record->hdr );
+    if (package)
+        msiobj_release( &package->hdr );
+    return r;
+}
diff --git a/reactos/dll/msi/handle.c b/reactos/dll/msi/handle.c
new file mode 100644 (file)
index 0000000..52c2439
--- /dev/null
@@ -0,0 +1,240 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2002-2004 Mike McCormack for CodeWeavers
+ *
+ * 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 <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winreg.h"
+#include "shlwapi.h"
+#include "wine/debug.h"
+#include "msi.h"
+#include "msiquery.h"
+#include "msipriv.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+static CRITICAL_SECTION MSI_handle_cs;
+static CRITICAL_SECTION_DEBUG MSI_handle_cs_debug =
+{
+    0, 0, &MSI_handle_cs,
+    { &MSI_handle_cs_debug.ProcessLocksList, 
+      &MSI_handle_cs_debug.ProcessLocksList },
+      0, 0, { (DWORD_PTR)(__FILE__ ": MSI_handle_cs") }
+};
+static CRITICAL_SECTION MSI_handle_cs = { &MSI_handle_cs_debug, -1, 0, 0, 0, 0 };
+
+static CRITICAL_SECTION MSI_object_cs;
+static CRITICAL_SECTION_DEBUG MSI_object_cs_debug =
+{
+    0, 0, &MSI_object_cs,
+    { &MSI_object_cs_debug.ProcessLocksList, 
+      &MSI_object_cs_debug.ProcessLocksList },
+      0, 0, { (DWORD_PTR)(__FILE__ ": MSI_object_cs") }
+};
+static CRITICAL_SECTION MSI_object_cs = { &MSI_object_cs_debug, -1, 0, 0, 0, 0 };
+
+typedef struct msi_handle_info_t
+{
+    MSIOBJECTHDR *obj;
+    DWORD dwThreadId;
+} msi_handle_info;
+
+static msi_handle_info msihandletable[MSIMAXHANDLES];
+
+MSIHANDLE alloc_msihandle( MSIOBJECTHDR *obj )
+{
+    MSIHANDLE ret = 0;
+    UINT i;
+
+    EnterCriticalSection( &MSI_handle_cs );
+
+    /* find a slot */
+    for(i=0; i<MSIMAXHANDLES; i++)
+        if( !msihandletable[i].obj )
+            break;
+    if( (i>=MSIMAXHANDLES) || msihandletable[i].obj )
+        goto out;
+
+    msiobj_addref( obj );
+    msihandletable[i].obj = obj;
+    msihandletable[i].dwThreadId = GetCurrentThreadId();
+    ret = (MSIHANDLE) (i+1);
+out:
+    TRACE("%p -> %ld\n", obj, ret );
+
+    LeaveCriticalSection( &MSI_handle_cs );
+    return ret;
+}
+
+void *msihandle2msiinfo(MSIHANDLE handle, UINT type)
+{
+    MSIOBJECTHDR *ret = NULL;
+
+    EnterCriticalSection( &MSI_handle_cs );
+    handle--;
+    if( handle<0 )
+        goto out;
+    if( handle>=MSIMAXHANDLES )
+        goto out;
+    if( !msihandletable[handle].obj )
+        goto out;
+    if( msihandletable[handle].obj->magic != MSIHANDLE_MAGIC )
+        goto out;
+    if( type && (msihandletable[handle].obj->type != type) )
+        goto out;
+    ret = msihandletable[handle].obj;
+    msiobj_addref( ret );
+    
+out:
+    LeaveCriticalSection( &MSI_handle_cs );
+
+    return (void*) ret;
+}
+
+void *alloc_msiobject(UINT type, UINT size, msihandledestructor destroy )
+{
+    MSIOBJECTHDR *info;
+
+    info = msi_alloc_zero( size );
+    if( info )
+    {
+        info->magic = MSIHANDLE_MAGIC;
+        info->type = type;
+        info->refcount = 1;
+        info->destructor = destroy;
+    }
+
+    return info;
+}
+
+void msiobj_addref( MSIOBJECTHDR *info )
+{
+    TRACE("%p\n", info);
+
+    if( !info )
+        return;
+
+    if( info->magic != MSIHANDLE_MAGIC )
+    {
+        ERR("Invalid handle!\n");
+        return;
+    }
+
+    InterlockedIncrement(&info->refcount);
+}
+
+void msiobj_lock( MSIOBJECTHDR *info )
+{
+    EnterCriticalSection( &MSI_object_cs );
+}
+
+void msiobj_unlock( MSIOBJECTHDR *info )
+{
+    LeaveCriticalSection( &MSI_object_cs );
+}
+
+int msiobj_release( MSIOBJECTHDR *info )
+{
+    int ret;
+
+    TRACE("%p\n",info);
+
+    if( !info )
+        return -1;
+
+    if( info->magic != MSIHANDLE_MAGIC )
+    {
+        ERR("Invalid handle!\n");
+        return -1;
+    }
+
+    ret = InterlockedDecrement( &info->refcount );
+    if( ret==0 )
+    {
+        if( info->destructor )
+            info->destructor( info );
+        msi_free( info );
+        TRACE("object %p destroyed\n", info);
+    }
+
+    return ret;
+}
+
+/***********************************************************
+ *   MsiCloseHandle   [MSI.@]
+ */
+UINT WINAPI MsiCloseHandle(MSIHANDLE handle)
+{
+    MSIOBJECTHDR *info;
+    UINT ret = ERROR_INVALID_HANDLE;
+
+    TRACE("%lx\n",handle);
+
+    EnterCriticalSection( &MSI_handle_cs );
+
+    info = msihandle2msiinfo(handle, 0);
+    if( !info )
+        goto out;
+
+    if( info->magic != MSIHANDLE_MAGIC )
+    {
+        ERR("Invalid handle!\n");
+        goto out;
+    }
+
+    msiobj_release( info );
+    msihandletable[handle-1].obj = NULL;
+    ret = ERROR_SUCCESS;
+
+    TRACE("handle %lx Destroyed\n", handle);
+out:
+    LeaveCriticalSection( &MSI_handle_cs );
+    if( info )
+        msiobj_release( info );
+
+    return ret;
+}
+
+/***********************************************************
+ *   MsiCloseAllHandles   [MSI.@]
+ *
+ *  Closes all handles owned by the current thread
+ *
+ *  RETURNS:
+ *   The number of handles closed
+ */
+UINT WINAPI MsiCloseAllHandles(void)
+{
+    UINT i, n=0;
+
+    TRACE("\n");
+
+    for(i=0; i<MSIMAXHANDLES; i++)
+    {
+        if(msihandletable[i].dwThreadId == GetCurrentThreadId())
+        {
+            MsiCloseHandle( i+1 );
+            n++;
+        }
+    }
+
+    return n;
+}
diff --git a/reactos/dll/msi/helpers.c b/reactos/dll/msi/helpers.c
new file mode 100644 (file)
index 0000000..2b2ba06
--- /dev/null
@@ -0,0 +1,1002 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2005 Aric Stewart for CodeWeavers
+ *
+ * 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
+ */
+
+/*
+ * Here are helper functions formally in action.c that are used by a variaty of
+ * actions and functions.
+ */
+
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "wine/debug.h"
+#include "msipriv.h"
+#include "winuser.h"
+#include "wine/unicode.h"
+#include "action.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+static const WCHAR cszTargetDir[] = {'T','A','R','G','E','T','D','I','R',0};
+static const WCHAR cszDatabase[]={'D','A','T','A','B','A','S','E',0};
+
+const WCHAR cszSourceDir[] = {'S','o','u','r','c','e','D','i','r',0};
+const WCHAR cszRootDrive[] = {'R','O','O','T','D','R','I','V','E',0};
+const WCHAR cszbs[]={'\\',0};
+
+DWORD build_version_dword(LPCWSTR version_string)
+{
+    SHORT major,minor;
+    WORD build;
+    DWORD rc = 0x00000000;
+    LPCWSTR ptr1;
+
+    ptr1 = version_string;
+
+    if (!ptr1)
+        return rc;
+    else
+        major = atoiW(ptr1);
+
+
+    if(ptr1)
+        ptr1 = strchrW(ptr1,'.');
+    if (ptr1)
+    {
+        ptr1++;
+        minor = atoiW(ptr1);
+    }
+    else
+        minor = 0;
+
+    if (ptr1)
+        ptr1 = strchrW(ptr1,'.');
+
+    if (ptr1)
+    {
+        ptr1++;
+        build = atoiW(ptr1);
+    }
+    else
+        build = 0;
+
+    rc = MAKELONG(build,MAKEWORD(minor,major));
+    TRACE("%s -> 0x%lx\n",debugstr_w(version_string),rc);
+    return rc;
+}
+
+LPWSTR build_icon_path(MSIPACKAGE *package, LPCWSTR icon_name )
+{
+    LPWSTR SystemFolder, dest, FilePath;
+
+    static const WCHAR szInstaller[] = 
+        {'M','i','c','r','o','s','o','f','t','\\',
+         'I','n','s','t','a','l','l','e','r','\\',0};
+    static const WCHAR szFolder[] =
+        {'A','p','p','D','a','t','a','F','o','l','d','e','r',0};
+
+    SystemFolder = msi_dup_property( package, szFolder );
+
+    dest = build_directory_name(3, SystemFolder, szInstaller, package->ProductCode);
+
+    create_full_pathW(dest);
+
+    FilePath = build_directory_name(2, dest, icon_name);
+
+    msi_free(SystemFolder);
+    msi_free(dest);
+    return FilePath;
+}
+
+LPWSTR msi_dup_record_field( MSIRECORD *row, INT index )
+{
+    return strdupW( MSI_RecordGetString(row,index) );
+}
+
+LPWSTR msi_dup_property(MSIPACKAGE *package, LPCWSTR prop)
+{
+    DWORD sz = 0;
+    LPWSTR str;
+    UINT r;
+
+    r = MSI_GetPropertyW(package, prop, NULL, &sz);
+    if (r != ERROR_SUCCESS && r != ERROR_MORE_DATA)
+        return NULL;
+
+    sz++;
+    str = msi_alloc(sz*sizeof(WCHAR));
+    r = MSI_GetPropertyW(package, prop, str, &sz);
+    if (r != ERROR_SUCCESS)
+    {
+        msi_free(str);
+        str = NULL;
+    }
+    return str;
+}
+
+MSICOMPONENT* get_loaded_component( MSIPACKAGE* package, LPCWSTR Component )
+{
+    MSICOMPONENT *comp;
+
+    LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
+    {
+        if (lstrcmpW(Component,comp->Component)==0)
+            return comp;
+    }
+    return NULL;
+}
+
+MSIFEATURE* get_loaded_feature(MSIPACKAGE* package, LPCWSTR Feature )
+{
+    MSIFEATURE *feature;
+
+    LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
+    {
+        if (lstrcmpW( Feature, feature->Feature )==0)
+            return feature;
+    }
+    return NULL;
+}
+
+MSIFILE* get_loaded_file( MSIPACKAGE* package, LPCWSTR key )
+{
+    MSIFILE *file;
+
+    LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
+    {
+        if (lstrcmpW( key, file->File )==0)
+            return file;
+    }
+    return NULL;
+}
+
+int track_tempfile( MSIPACKAGE *package, LPCWSTR name, LPCWSTR path )
+{
+    MSITEMPFILE *temp;
+
+    LIST_FOR_EACH_ENTRY( temp, &package->tempfiles, MSITEMPFILE, entry )
+    {
+        if (lstrcmpW( name, temp->File )==0)
+        {
+            TRACE("tempfile %s already exists with path %s\n",
+                debugstr_w(temp->File), debugstr_w(temp->Path));
+            return -1;
+        }
+    }
+
+    temp = msi_alloc_zero( sizeof (MSITEMPFILE) );
+    if (!temp)
+        return -1;
+
+    list_add_head( &package->tempfiles, &temp->entry );
+
+    temp->File = strdupW( name );
+    temp->Path = strdupW( path );
+
+    TRACE("adding tempfile %s with path %s\n",
+           debugstr_w(temp->File), debugstr_w(temp->Path));
+
+    return 0;
+}
+
+MSIFOLDER *get_loaded_folder( MSIPACKAGE *package, LPCWSTR dir )
+{
+    MSIFOLDER *folder;
+    
+    LIST_FOR_EACH_ENTRY( folder, &package->folders, MSIFOLDER, entry )
+    {
+        if (lstrcmpW( dir, folder->Directory )==0)
+            return folder;
+    }
+    return NULL;
+}
+
+static LPWSTR get_source_root( MSIPACKAGE *package )
+{
+    LPWSTR path, p;
+
+    path = msi_dup_property( package, cszSourceDir );
+    if (path)
+        return path;
+
+    path = msi_dup_property( package, cszDatabase );
+    if (path)
+    {
+        p = strrchrW(path,'\\');
+        if (p)
+            *(p+1) = 0;
+    }
+    return path;
+}
+
+LPWSTR resolve_folder(MSIPACKAGE *package, LPCWSTR name, BOOL source, 
+                      BOOL set_prop, MSIFOLDER **folder)
+{
+    MSIFOLDER *f;
+    LPWSTR p, path = NULL;
+
+    TRACE("Working to resolve %s\n",debugstr_w(name));
+
+    if (!name)
+        return NULL;
+
+    /* special resolving for Target and Source root dir */
+    if (strcmpW(name,cszTargetDir)==0 || strcmpW(name,cszSourceDir)==0)
+    {
+        if (!source)
+        {
+            LPWSTR check_path;
+            check_path = msi_dup_property( package, cszTargetDir );
+            if (!check_path)
+            {
+                check_path = msi_dup_property( package, cszRootDrive );
+                if (set_prop)
+                    MSI_SetPropertyW(package,cszTargetDir,check_path);
+            }
+
+            /* correct misbuilt target dir */
+            path = build_directory_name(2, check_path, NULL);
+            if (strcmpiW(path,check_path)!=0)
+                MSI_SetPropertyW(package,cszTargetDir,path);
+            msi_free(check_path);
+        }
+        else
+            path = get_source_root( package );
+        if (folder)
+            *folder = get_loaded_folder( package, name );
+        return path;
+    }
+
+    f = get_loaded_folder( package, name );
+    if (!f)
+        return NULL;
+
+    if (folder)
+        *folder = f;
+
+    if (!source && f->ResolvedTarget)
+    {
+        path = strdupW( f->ResolvedTarget );
+        TRACE("   already resolved to %s\n",debugstr_w(path));
+        return path;
+    }
+    else if (source && f->ResolvedSource)
+    {
+        path = strdupW( f->ResolvedSource );
+        TRACE("   (source)already resolved to %s\n",debugstr_w(path));
+        return path;
+    }
+    else if (!source && f->Property)
+    {
+        path = build_directory_name( 2, f->Property, NULL );
+                    
+        TRACE("   internally set to %s\n",debugstr_w(path));
+        if (set_prop)
+            MSI_SetPropertyW( package, name, path );
+        return path;
+    }
+
+    if (f->Parent)
+    {
+        LPWSTR parent = f->Parent->Directory;
+
+        TRACE(" ! Parent is %s\n", debugstr_w(parent));
+
+        p = resolve_folder(package, parent, source, set_prop, NULL);
+        if (!source)
+        {
+            TRACE("   TargetDefault = %s\n", debugstr_w(f->TargetDefault));
+
+            path = build_directory_name( 3, p, f->TargetDefault, NULL );
+            f->ResolvedTarget = strdupW( path );
+            TRACE("target -> %s\n", debugstr_w(path));
+            if (set_prop)
+                MSI_SetPropertyW(package,name,path);
+        }
+        else 
+        {
+            if (f->SourceDefault && f->SourceDefault[0]!='.')
+                path = build_directory_name( 3, p, f->SourceDefault, NULL );
+            else
+                path = strdupW(p);
+            TRACE("source -> %s\n", debugstr_w(path));
+
+            /* if the directory doesn't exist, use the root */
+            if (INVALID_FILE_ATTRIBUTES == GetFileAttributesW( path ))
+            {
+                msi_free( path );
+                path = get_source_root( package );
+                TRACE("defaulting to %s\n", debugstr_w(path));
+            }
+            else
+                f->ResolvedSource = strdupW( path );
+        }
+        msi_free(p);
+    }
+    return path;
+}
+
+/* wrapper to resist a need for a full rewrite right now */
+DWORD deformat_string(MSIPACKAGE *package, LPCWSTR ptr, WCHAR** data )
+{
+    if (ptr)
+    {
+        MSIRECORD *rec = MSI_CreateRecord(1);
+        DWORD size = 0;
+
+        MSI_RecordSetStringW(rec,0,ptr);
+        MSI_FormatRecordW(package,rec,NULL,&size);
+        if (size >= 0)
+        {
+            size++;
+            *data = msi_alloc(size*sizeof(WCHAR));
+            if (size > 1)
+                MSI_FormatRecordW(package,rec,*data,&size);
+            else
+                *data[0] = 0;
+            msiobj_release( &rec->hdr );
+            return sizeof(WCHAR)*size;
+        }
+        msiobj_release( &rec->hdr );
+    }
+
+    *data = NULL;
+    return 0;
+}
+
+UINT schedule_action(MSIPACKAGE *package, UINT script, LPCWSTR action)
+{
+    UINT count;
+    LPWSTR *newbuf = NULL;
+    if (script >= TOTAL_SCRIPTS)
+    {
+        FIXME("Unknown script requested %i\n",script);
+        return ERROR_FUNCTION_FAILED;
+    }
+    TRACE("Scheduling Action %s in script %i\n",debugstr_w(action), script);
+    
+    count = package->script->ActionCount[script];
+    package->script->ActionCount[script]++;
+    if (count != 0)
+        newbuf = msi_realloc( package->script->Actions[script],
+                        package->script->ActionCount[script]* sizeof(LPWSTR));
+    else
+        newbuf = msi_alloc( sizeof(LPWSTR));
+
+    newbuf[count] = strdupW(action);
+    package->script->Actions[script] = newbuf;
+
+   return ERROR_SUCCESS;
+}
+
+static void remove_tracked_tempfiles(MSIPACKAGE* package)
+{
+    struct list *item, *cursor;
+
+    LIST_FOR_EACH_SAFE( item, cursor, &package->tempfiles )
+    {
+        MSITEMPFILE *temp = LIST_ENTRY( item, MSITEMPFILE, entry );
+
+        list_remove( &temp->entry );
+        TRACE("deleting temp file %s\n", debugstr_w( temp->Path ));
+        DeleteFileW( temp->Path );
+        msi_free( temp->File );
+        msi_free( temp->Path );
+        msi_free( temp );
+    }
+}
+
+static void free_feature( MSIFEATURE *feature )
+{
+    struct list *item, *cursor;
+
+    LIST_FOR_EACH_SAFE( item, cursor, &feature->Components )
+    {
+        ComponentList *cl = LIST_ENTRY( item, ComponentList, entry );
+        list_remove( &cl->entry );
+        msi_free( cl );
+    }
+    msi_free( feature->Feature );
+    msi_free( feature->Feature_Parent );
+    msi_free( feature->Directory );
+    msi_free( feature->Description );
+    msi_free( feature->Title );
+    msi_free( feature );
+}
+
+void free_extension( MSIEXTENSION *ext )
+{
+    struct list *item, *cursor;
+
+    LIST_FOR_EACH_SAFE( item, cursor, &ext->verbs )
+    {
+        MSIVERB *verb = LIST_ENTRY( item, MSIVERB, entry );
+
+        list_remove( &verb->entry );
+        msi_free( verb->Verb );
+        msi_free( verb->Command );
+        msi_free( verb->Argument );
+        msi_free( verb );
+    }
+
+    msi_free( ext->Extension );
+    msi_free( ext->ProgIDText );
+    msi_free( ext );
+}
+
+/* Called when the package is being closed */
+void ACTION_free_package_structures( MSIPACKAGE* package)
+{
+    INT i;
+    struct list *item, *cursor;
+    
+    TRACE("Freeing package action data\n");
+
+    remove_tracked_tempfiles(package);
+
+    LIST_FOR_EACH_SAFE( item, cursor, &package->features )
+    {
+        MSIFEATURE *feature = LIST_ENTRY( item, MSIFEATURE, entry );
+        list_remove( &feature->entry );
+        free_feature( feature );
+    }
+
+    LIST_FOR_EACH_SAFE( item, cursor, &package->folders )
+    {
+        MSIFOLDER *folder = LIST_ENTRY( item, MSIFOLDER, entry );
+
+        list_remove( &folder->entry );
+        msi_free( folder->Directory );
+        msi_free( folder->TargetDefault );
+        msi_free( folder->SourceDefault );
+        msi_free( folder->ResolvedTarget );
+        msi_free( folder->ResolvedSource );
+        msi_free( folder->Property );
+        msi_free( folder );
+    }
+
+    LIST_FOR_EACH_SAFE( item, cursor, &package->components )
+    {
+        MSICOMPONENT *comp = LIST_ENTRY( item, MSICOMPONENT, entry );
+        
+        list_remove( &comp->entry );
+        msi_free( comp->Component );
+        msi_free( comp->ComponentId );
+        msi_free( comp->Directory );
+        msi_free( comp->Condition );
+        msi_free( comp->KeyPath );
+        msi_free( comp->FullKeypath );
+        msi_free( comp );
+    }
+
+    LIST_FOR_EACH_SAFE( item, cursor, &package->files )
+    {
+        MSIFILE *file = LIST_ENTRY( item, MSIFILE, entry );
+
+        list_remove( &file->entry );
+        msi_free( file->File );
+        msi_free( file->FileName );
+        msi_free( file->ShortName );
+        msi_free( file->Version );
+        msi_free( file->Language );
+        msi_free( file->SourcePath );
+        msi_free( file->TargetPath );
+        msi_free( file );
+    }
+
+    /* clean up extension, progid, class and verb structures */
+    LIST_FOR_EACH_SAFE( item, cursor, &package->classes )
+    {
+        MSICLASS *cls = LIST_ENTRY( item, MSICLASS, entry );
+
+        list_remove( &cls->entry );
+        msi_free( cls->clsid );
+        msi_free( cls->Context );
+        msi_free( cls->Description );
+        msi_free( cls->FileTypeMask );
+        msi_free( cls->IconPath );
+        msi_free( cls->DefInprocHandler );
+        msi_free( cls->DefInprocHandler32 );
+        msi_free( cls->Argument );
+        msi_free( cls->ProgIDText );
+        msi_free( cls );
+    }
+
+    LIST_FOR_EACH_SAFE( item, cursor, &package->extensions )
+    {
+        MSIEXTENSION *ext = LIST_ENTRY( item, MSIEXTENSION, entry );
+
+        list_remove( &ext->entry );
+        free_extension( ext );
+    }
+
+    LIST_FOR_EACH_SAFE( item, cursor, &package->progids )
+    {
+        MSIPROGID *progid = LIST_ENTRY( item, MSIPROGID, entry );
+
+        list_remove( &progid->entry );
+        msi_free( progid->ProgID );
+        msi_free( progid->Description );
+        msi_free( progid->IconPath );
+        msi_free( progid );
+    }
+
+    LIST_FOR_EACH_SAFE( item, cursor, &package->mimes )
+    {
+        MSIMIME *mt = LIST_ENTRY( item, MSIMIME, entry );
+
+        list_remove( &mt->entry );
+        msi_free( mt->clsid );
+        msi_free( mt->ContentType );
+        msi_free( mt );
+    }
+
+    LIST_FOR_EACH_SAFE( item, cursor, &package->appids )
+    {
+        MSIAPPID *appid = LIST_ENTRY( item, MSIAPPID, entry );
+
+        list_remove( &appid->entry );
+        msi_free( appid->AppID );
+        msi_free( appid->RemoteServerName );
+        msi_free( appid->LocalServer );
+        msi_free( appid->ServiceParameters );
+        msi_free( appid->DllSurrogate );
+        msi_free( appid );
+    }
+
+    if (package->script)
+    {
+        for (i = 0; i < TOTAL_SCRIPTS; i++)
+        {
+            int j;
+            for (j = 0; j < package->script->ActionCount[i]; j++)
+                msi_free(package->script->Actions[i][j]);
+        
+            msi_free(package->script->Actions[i]);
+        }
+
+        for (i = 0; i < package->script->UniqueActionsCount; i++)
+            msi_free(package->script->UniqueActions[i]);
+
+        msi_free(package->script->UniqueActions);
+        msi_free(package->script);
+    }
+
+    msi_free(package->PackagePath);
+    msi_free(package->ProductCode);
+    msi_free(package->ActionFormat);
+    msi_free(package->LastAction);
+
+    /* cleanup control event subscriptions */
+    ControlEvent_CleanupSubscriptions(package);
+}
+
+/*
+ *  build_directory_name()
+ *
+ *  This function is to save messing round with directory names
+ *  It handles adding backslashes between path segments, 
+ *   and can add \ at the end of the directory name if told to.
+ *
+ *  It takes a variable number of arguments.
+ *  It always allocates a new string for the result, so make sure
+ *   to free the return value when finished with it.
+ *
+ *  The first arg is the number of path segments that follow.
+ *  The arguments following count are a list of path segments.
+ *  A path segment may be NULL.
+ *
+ *  Path segments will be added with a \ separating them.
+ *  A \ will not be added after the last segment, however if the
+ *    last segment is NULL, then the last character will be a \
+ * 
+ */
+LPWSTR build_directory_name(DWORD count, ...)
+{
+    DWORD sz = 1, i;
+    LPWSTR dir;
+    va_list va;
+
+    va_start(va,count);
+    for(i=0; i<count; i++)
+    {
+        LPCWSTR str = va_arg(va,LPCWSTR);
+        if (str)
+            sz += strlenW(str) + 1;
+    }
+    va_end(va);
+
+    dir = msi_alloc(sz*sizeof(WCHAR));
+    dir[0]=0;
+
+    va_start(va,count);
+    for(i=0; i<count; i++)
+    {
+        LPCWSTR str = va_arg(va,LPCWSTR);
+        if (!str)
+            continue;
+        strcatW(dir, str);
+        if( ((i+1)!=count) && dir[strlenW(dir)-1]!='\\')
+            strcatW(dir, cszbs);
+    }
+    return dir;
+}
+
+/***********************************************************************
+ *            create_full_pathW
+ *
+ * Recursively create all directories in the path.
+ *
+ * shamelessly stolen from setupapi/queue.c
+ */
+BOOL create_full_pathW(const WCHAR *path)
+{
+    BOOL ret = TRUE;
+    int len;
+    WCHAR *new_path;
+
+    new_path = msi_alloc( (strlenW(path) + 1) * sizeof(WCHAR));
+
+    strcpyW(new_path, path);
+
+    while((len = strlenW(new_path)) && new_path[len - 1] == '\\')
+    new_path[len - 1] = 0;
+
+    while(!CreateDirectoryW(new_path, NULL))
+    {
+        WCHAR *slash;
+        DWORD last_error = GetLastError();
+        if(last_error == ERROR_ALREADY_EXISTS)
+            break;
+
+        if(last_error != ERROR_PATH_NOT_FOUND)
+        {
+            ret = FALSE;
+            break;
+        }
+
+        if(!(slash = strrchrW(new_path, '\\')))
+        {
+            ret = FALSE;
+            break;
+        }
+
+        len = slash - new_path;
+        new_path[len] = 0;
+        if(!create_full_pathW(new_path))
+        {
+            ret = FALSE;
+            break;
+        }
+        new_path[len] = '\\';
+    }
+
+    msi_free(new_path);
+    return ret;
+}
+
+void ui_progress(MSIPACKAGE *package, int a, int b, int c, int d )
+{
+    MSIRECORD * row;
+
+    row = MSI_CreateRecord(4);
+    MSI_RecordSetInteger(row,1,a);
+    MSI_RecordSetInteger(row,2,b);
+    MSI_RecordSetInteger(row,3,c);
+    MSI_RecordSetInteger(row,4,d);
+    MSI_ProcessMessage(package, INSTALLMESSAGE_PROGRESS, row);
+    msiobj_release(&row->hdr);
+
+    msi_dialog_check_messages(NULL);
+}
+
+void ui_actiondata(MSIPACKAGE *package, LPCWSTR action, MSIRECORD * record)
+{
+    static const WCHAR Query_t[] = 
+        {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
+         '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
+         'W','H','E','R','E',' ', '`','A','c','t','i','o','n','`',' ','=', 
+         ' ','\'','%','s','\'',0};
+    WCHAR message[1024];
+    MSIRECORD * row = 0;
+    DWORD size;
+    static const WCHAR szActionData[] = 
+        {'A','c','t','i','o','n','D','a','t','a',0};
+
+    if (!package->LastAction || strcmpW(package->LastAction,action))
+    {
+        row = MSI_QueryGetRecord(package->db, Query_t, action);
+        if (!row)
+            return;
+
+        if (MSI_RecordIsNull(row,3))
+        {
+            msiobj_release(&row->hdr);
+            return;
+        }
+
+        /* update the cached actionformat */
+        msi_free(package->ActionFormat);
+        package->ActionFormat = msi_dup_record_field(row,3);
+
+        msi_free(package->LastAction);
+        package->LastAction = strdupW(action);
+
+        msiobj_release(&row->hdr);
+    }
+
+    MSI_RecordSetStringW(record,0,package->ActionFormat);
+    size = 1024;
+    MSI_FormatRecordW(package,record,message,&size);
+
+    row = MSI_CreateRecord(1);
+    MSI_RecordSetStringW(row,1,message);
+    MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, row);
+
+    ControlEvent_FireSubscribedEvent(package,szActionData, row);
+
+    msiobj_release(&row->hdr);
+}
+
+BOOL ACTION_VerifyComponentForAction( MSICOMPONENT* comp, INSTALLSTATE check )
+{
+    if (!comp)
+        return FALSE;
+
+    if (comp->Installed == check)
+        return FALSE;
+
+    if (comp->ActionRequest == check)
+        return TRUE;
+    else
+        return FALSE;
+}
+
+BOOL ACTION_VerifyFeatureForAction( MSIFEATURE* feature, INSTALLSTATE check )
+{
+    if (!feature)
+        return FALSE;
+
+    if (feature->Installed == check)
+        return FALSE;
+
+    if (feature->ActionRequest == check)
+        return TRUE;
+    else
+        return FALSE;
+}
+
+void reduce_to_longfilename(WCHAR* filename)
+{
+    LPWSTR p = strchrW(filename,'|');
+    if (p)
+        memmove(filename, p+1, (strlenW(p+1)+1)*sizeof(WCHAR));
+}
+
+void reduce_to_shortfilename(WCHAR* filename)
+{
+    LPWSTR p = strchrW(filename,'|');
+    if (p)
+        *p = 0;
+}
+
+LPWSTR create_component_advertise_string(MSIPACKAGE* package, 
+                MSICOMPONENT* component, LPCWSTR feature)
+{
+    GUID clsid;
+    WCHAR productid_85[21];
+    WCHAR component_85[21];
+    /*
+     * I have a fair bit of confusion as to when a < is used and when a > is
+     * used. I do not think i have it right...
+     *
+     * Ok it appears that the > is used if there is a guid for the compoenent
+     * and the < is used if not.
+     */
+    static WCHAR fmt1[] = {'%','s','%','s','<',0,0};
+    static WCHAR fmt2[] = {'%','s','%','s','>','%','s',0,0};
+    LPWSTR output = NULL;
+    DWORD sz = 0;
+
+    memset(productid_85,0,sizeof(productid_85));
+    memset(component_85,0,sizeof(component_85));
+
+    CLSIDFromString(package->ProductCode, &clsid);
+    
+    encode_base85_guid(&clsid,productid_85);
+
+    CLSIDFromString(component->ComponentId, &clsid);
+    encode_base85_guid(&clsid,component_85);
+
+    TRACE("Doing something with this... %s %s %s\n", 
+            debugstr_w(productid_85), debugstr_w(feature),
+            debugstr_w(component_85));
+    sz = lstrlenW(productid_85) + lstrlenW(feature);
+    if (component)
+        sz += lstrlenW(component_85);
+
+    sz+=3;
+    sz *= sizeof(WCHAR);
+           
+    output = msi_alloc(sz);
+    memset(output,0,sz);
+
+    if (component)
+        sprintfW(output,fmt2,productid_85,feature,component_85);
+    else
+        sprintfW(output,fmt1,productid_85,feature);
+    
+    return output;
+}
+
+/* update compoennt state based on a feature change */
+void ACTION_UpdateComponentStates(MSIPACKAGE *package, LPCWSTR szFeature)
+{
+    INSTALLSTATE newstate;
+    MSIFEATURE *feature;
+    ComponentList *cl;
+
+    feature = get_loaded_feature(package,szFeature);
+    if (!feature)
+        return;
+
+    newstate = feature->ActionRequest;
+
+    LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
+    {
+        MSICOMPONENT* component = cl->component;
+    
+        TRACE("MODIFYING(%i): Component %s (Installed %i, Action %i, Request %i)\n",
+            newstate, debugstr_w(component->Component), component->Installed, 
+            component->Action, component->ActionRequest);
+        
+        if (!component->Enabled)
+            continue;
+        if (newstate == INSTALLSTATE_LOCAL)
+        {
+            component->ActionRequest = INSTALLSTATE_LOCAL;
+            component->Action = INSTALLSTATE_LOCAL;
+        }
+        else 
+        {
+            ComponentList *clist;
+            MSIFEATURE *f;
+
+            component->ActionRequest = newstate;
+            component->Action = newstate;
+
+            /*if any other feature wants is local we need to set it local*/
+            LIST_FOR_EACH_ENTRY( f, &package->features, MSIFEATURE, entry )
+            {
+                if ( component->ActionRequest != INSTALLSTATE_LOCAL )
+                    break;
+
+                LIST_FOR_EACH_ENTRY( clist, &f->Components, ComponentList, entry )
+                {
+                    if ( clist->component == component )
+                    {
+                        if (f->ActionRequest == INSTALLSTATE_LOCAL)
+                        {
+                            TRACE("Saved by %s\n", debugstr_w(f->Feature));
+                            component->ActionRequest = INSTALLSTATE_LOCAL;
+                            component->Action = INSTALLSTATE_LOCAL;
+                        }
+                        break;
+                    }
+                }
+            }
+        }
+        TRACE("Result (%i): Component %s (Installed %i, Action %i, Request %i)\n",
+            newstate, debugstr_w(component->Component), component->Installed, 
+            component->Action, component->ActionRequest);
+    } 
+}
+
+UINT register_unique_action(MSIPACKAGE *package, LPCWSTR action)
+{
+    UINT count;
+    LPWSTR *newbuf = NULL;
+
+    if (!package->script)
+        return FALSE;
+
+    TRACE("Registering Action %s as having fun\n",debugstr_w(action));
+    
+    count = package->script->UniqueActionsCount;
+    package->script->UniqueActionsCount++;
+    if (count != 0)
+        newbuf = msi_realloc( package->script->UniqueActions,
+                        package->script->UniqueActionsCount* sizeof(LPWSTR));
+    else
+        newbuf = msi_alloc( sizeof(LPWSTR));
+
+    newbuf[count] = strdupW(action);
+    package->script->UniqueActions = newbuf;
+
+    return ERROR_SUCCESS;
+}
+
+BOOL check_unique_action(MSIPACKAGE *package, LPCWSTR action)
+{
+    INT i;
+
+    if (!package->script)
+        return FALSE;
+
+    for (i = 0; i < package->script->UniqueActionsCount; i++)
+        if (!strcmpW(package->script->UniqueActions[i],action))
+            return TRUE;
+
+    return FALSE;
+}
+
+WCHAR* generate_error_string(MSIPACKAGE *package, UINT error, DWORD count, ... )
+{
+    static const WCHAR query[] = {'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ','F','R','O','M',' ','`','E','r','r','o','r','`',' ','W','H','E','R','E',' ','`','E','r','r','o','r','`',' ','=',' ','%','i',0};
+
+    MSIRECORD *rec;
+    MSIRECORD *row;
+    DWORD size = 0;
+    DWORD i;
+    va_list va;
+    LPCWSTR str;
+    LPWSTR data;
+
+    row = MSI_QueryGetRecord(package->db, query, error);
+    if (!row)
+        return 0;
+
+    rec = MSI_CreateRecord(count+2);
+
+    str = MSI_RecordGetString(row,1);
+    MSI_RecordSetStringW(rec,0,str);
+    msiobj_release( &row->hdr );
+    MSI_RecordSetInteger(rec,1,error);
+
+    va_start(va,count);
+    for (i = 0; i < count; i++)
+    {
+        str = va_arg(va,LPCWSTR);
+        MSI_RecordSetStringW(rec,(i+2),str);
+    }
+    va_end(va);
+
+    MSI_FormatRecordW(package,rec,NULL,&size);
+    if (size >= 0)
+    {
+        size++;
+        data = msi_alloc(size*sizeof(WCHAR));
+        if (size > 1)
+            MSI_FormatRecordW(package,rec,data,&size);
+        else
+            data[0] = 0;
+        msiobj_release( &rec->hdr );
+        return data;
+    }
+
+    msiobj_release( &rec->hdr );
+    data = NULL;
+    return data;
+}
diff --git a/reactos/dll/msi/insert.c b/reactos/dll/msi/insert.c
new file mode 100644 (file)
index 0000000..536daea
--- /dev/null
@@ -0,0 +1,267 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2004 Mike McCormack for CodeWeavers
+ *
+ * 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 <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "wine/debug.h"
+#include "msi.h"
+#include "msiquery.h"
+#include "objbase.h"
+#include "objidl.h"
+#include "msipriv.h"
+#include "winnls.h"
+
+#include "query.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msidb);
+
+
+/* below is the query interface to a table */
+
+typedef struct tagMSIINSERTVIEW
+{
+    MSIVIEW          view;
+    MSIDATABASE     *db;
+    BOOL             bIsTemp;
+    MSIVIEW         *sv;
+    column_info     *vals;
+} MSIINSERTVIEW;
+
+static UINT INSERT_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
+{
+    MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view;
+
+    TRACE("%p %d %d %p\n", iv, row, col, val );
+
+    return ERROR_FUNCTION_FAILED;
+}
+
+/*
+ * INSERT_merge_record
+ *
+ * Merge a value_list and a record to create a second record.
+ * Replace wildcard entries in the valuelist with values from the record
+ */
+static MSIRECORD *INSERT_merge_record( UINT fields, column_info *vl, MSIRECORD *rec )
+{
+    MSIRECORD *merged;
+    DWORD wildcard_count = 1, i;
+    const WCHAR *str;
+
+    merged = MSI_CreateRecord( fields );
+    for( i=1; i <= fields; i++ )
+    {
+        if( !vl )
+        {
+            TRACE("Not enough elements in the list to insert\n");
+            goto err;
+        }
+        switch( vl->val->type )
+        {
+        case EXPR_SVAL:
+            TRACE("field %ld -> %s\n", i, debugstr_w(vl->val->u.sval));
+            MSI_RecordSetStringW( merged, i, vl->val->u.sval );
+            break;
+        case EXPR_IVAL:
+            MSI_RecordSetInteger( merged, i, vl->val->u.ival );
+            break;
+        case EXPR_WILDCARD:
+            if( !rec )
+                goto err;
+            if( MSI_RecordIsNull( rec, wildcard_count ) )
+                goto err;
+            str = MSI_RecordGetString( rec, wildcard_count );
+            MSI_RecordSetStringW( merged, i, str );
+            wildcard_count++;
+            break;
+        default:
+            ERR("Unknown expression type %d\n", vl->val->type);
+        }
+        vl = vl->next;
+    }
+
+    return merged;
+err:
+    msiobj_release( &merged->hdr );
+    return NULL;
+}
+
+static UINT INSERT_execute( struct tagMSIVIEW *view, MSIRECORD *record )
+{
+    MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view;
+    UINT r, col_count = 0;
+    MSIVIEW *sv;
+    MSIRECORD *values = NULL;
+
+    TRACE("%p %p\n", iv, record );
+
+    sv = iv->sv;
+    if( !sv )
+        return ERROR_FUNCTION_FAILED;
+
+    r = sv->ops->execute( sv, 0 );
+    TRACE("tv execute returned %x\n", r);
+    if( r )
+        return r;
+
+    r = sv->ops->get_dimensions( sv, NULL, &col_count );
+    if( r )
+        goto err;
+
+    /*
+     * Merge the wildcard values into the list of values provided
+     * in the query, and create a record containing both.
+     */
+    values = INSERT_merge_record( col_count, iv->vals, record );
+    if( !values )
+        goto err;
+
+    r = sv->ops->insert_row( sv, values );
+
+err:
+    if( values )
+        msiobj_release( &values->hdr );
+
+    return r;
+}
+
+
+static UINT INSERT_close( struct tagMSIVIEW *view )
+{
+    MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view;
+    MSIVIEW *sv;
+
+    TRACE("%p\n", iv);
+
+    sv = iv->sv;
+    if( !sv )
+        return ERROR_FUNCTION_FAILED;
+
+    return sv->ops->close( sv );
+}
+
+static UINT INSERT_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols )
+{
+    MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view;
+    MSIVIEW *sv;
+
+    TRACE("%p %p %p\n", iv, rows, cols );
+
+    sv = iv->sv;
+    if( !sv )
+        return ERROR_FUNCTION_FAILED;
+
+    return sv->ops->get_dimensions( sv, rows, cols );
+}
+
+static UINT INSERT_get_column_info( struct tagMSIVIEW *view,
+                UINT n, LPWSTR *name, UINT *type )
+{
+    MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view;
+    MSIVIEW *sv;
+
+    TRACE("%p %d %p %p\n", iv, n, name, type );
+
+    sv = iv->sv;
+    if( !sv )
+        return ERROR_FUNCTION_FAILED;
+
+    return sv->ops->get_column_info( sv, n, name, type );
+}
+
+static UINT INSERT_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIRECORD *rec)
+{
+    MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view;
+
+    TRACE("%p %d %p\n", iv, eModifyMode, rec );
+
+    return ERROR_FUNCTION_FAILED;
+}
+
+static UINT INSERT_delete( struct tagMSIVIEW *view )
+{
+    MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view;
+    MSIVIEW *sv;
+
+    TRACE("%p\n", iv );
+
+    sv = iv->sv;
+    if( sv )
+        sv->ops->delete( sv );
+    msiobj_release( &iv->db->hdr );
+    msi_free( iv );
+
+    return ERROR_SUCCESS;
+}
+
+
+MSIVIEWOPS insert_ops =
+{
+    INSERT_fetch_int,
+    NULL,
+    NULL,
+    NULL,
+    INSERT_execute,
+    INSERT_close,
+    INSERT_get_dimensions,
+    INSERT_get_column_info,
+    INSERT_modify,
+    INSERT_delete
+};
+
+UINT INSERT_CreateView( MSIDATABASE *db, MSIVIEW **view, LPWSTR table,
+                        column_info *columns, column_info *values, BOOL temp )
+{
+    MSIINSERTVIEW *iv = NULL;
+    UINT r;
+    MSIVIEW *tv = NULL, *sv = NULL;
+
+    TRACE("%p\n", iv );
+
+    r = TABLE_CreateView( db, table, &tv );
+    if( r != ERROR_SUCCESS )
+        return r;
+
+    r = SELECT_CreateView( db, &sv, tv, columns );
+    if( r != ERROR_SUCCESS )
+    {
+        if( tv )
+            tv->ops->delete( tv );
+        return r;
+    }
+    
+    iv = msi_alloc_zero( sizeof *iv );
+    if( !iv )
+        return ERROR_FUNCTION_FAILED;
+
+    /* fill the structure */
+    iv->view.ops = &insert_ops;
+    msiobj_addref( &db->hdr );
+    iv->db = db;
+    iv->vals = values;
+    iv->bIsTemp = temp;
+    iv->sv = sv;
+    *view = (MSIVIEW*) iv;
+
+    return ERROR_SUCCESS;
+}
diff --git a/reactos/dll/msi/install.c b/reactos/dll/msi/install.c
new file mode 100644 (file)
index 0000000..08b5f70
--- /dev/null
@@ -0,0 +1,764 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2005 Aric Stewart for CodeWeavers
+ *
+ * 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
+ */
+
+/* Msi top level apis directly related to installs */
+
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "wine/debug.h"
+#include "msi.h"
+#include "msidefs.h"
+#include "msipriv.h"
+#include "winuser.h"
+#include "wine/unicode.h"
+#include "action.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+/***********************************************************************
+ * MsiDoActionA       (MSI.@)
+ */
+UINT WINAPI MsiDoActionA( MSIHANDLE hInstall, LPCSTR szAction )
+{
+    LPWSTR szwAction;
+    UINT ret;
+
+    TRACE("%s\n", debugstr_a(szAction));
+
+    szwAction = strdupAtoW(szAction);
+    if (szAction && !szwAction)
+        return ERROR_FUNCTION_FAILED; 
+
+    ret = MsiDoActionW( hInstall, szwAction );
+    msi_free( szwAction );
+    return ret;
+}
+
+/***********************************************************************
+ * MsiDoActionW       (MSI.@)
+ */
+UINT WINAPI MsiDoActionW( MSIHANDLE hInstall, LPCWSTR szAction )
+{
+    MSIPACKAGE *package;
+    UINT ret;
+
+    TRACE("%s\n",debugstr_w(szAction));
+
+    if (!szAction)
+        return ERROR_INVALID_PARAMETER;
+
+    package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE );
+    if (!package)
+        return ERROR_INVALID_HANDLE;
+    ret = ACTION_PerformUIAction( package, szAction );
+    msiobj_release( &package->hdr );
+
+    return ret;
+}
+
+/***********************************************************************
+ * MsiSequenceA       (MSI.@)
+ */
+UINT WINAPI MsiSequenceA( MSIHANDLE hInstall, LPCSTR szTable, INT iSequenceMode )
+{
+    LPWSTR szwTable;
+    UINT ret;
+
+    TRACE("%s\n", debugstr_a(szTable));
+
+    szwTable = strdupAtoW(szTable);
+    if (szTable && !szwTable)
+        return ERROR_FUNCTION_FAILED; 
+
+    ret = MsiSequenceW( hInstall, szwTable, iSequenceMode );
+    msi_free( szwTable );
+    return ret;
+}
+
+/***********************************************************************
+ * MsiSequenceW       (MSI.@)
+ */
+UINT WINAPI MsiSequenceW( MSIHANDLE hInstall, LPCWSTR szTable, INT iSequenceMode )
+{
+    MSIPACKAGE *package;
+    UINT ret;
+
+    TRACE("%s\n", debugstr_w(szTable));
+
+    package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE );
+    if (!package)
+        return ERROR_INVALID_HANDLE;
+
+    ret = MSI_Sequence( package, szTable, iSequenceMode );
+    msiobj_release( &package->hdr );
+    return ret;
+}
+
+UINT msi_strcpy_to_awstring( LPCWSTR str, awstring *awbuf, DWORD *sz )
+{
+    UINT len, r = ERROR_SUCCESS;
+
+    if (awbuf->str.w && !sz )
+        return ERROR_INVALID_PARAMETER;
+
+    if (!sz)
+        return r;
+    if (awbuf->unicode)
+    {
+        len = lstrlenW( str );
+        if (awbuf->str.w) 
+            lstrcpynW( awbuf->str.w, str, *sz );
+    }
+    else
+    {
+        len = WideCharToMultiByte( CP_ACP, 0, str, -1, NULL, 0, NULL, NULL );
+        if (len)
+            len--;
+        WideCharToMultiByte( CP_ACP, 0, str, -1, awbuf->str.a, *sz, NULL, NULL );
+        if ( *sz && (len >= *sz) )
+            awbuf->str.a[*sz - 1] = 0;
+    }
+
+    if (awbuf->str.w && len >= *sz)
+        r = ERROR_MORE_DATA;
+    *sz = len;
+    return r;
+}
+
+/***********************************************************************
+ * MsiGetTargetPath   (internal)
+ */
+UINT WINAPI MSI_GetTargetPath( MSIHANDLE hInstall, LPCWSTR szFolder,
+                               awstring *szPathBuf, DWORD* pcchPathBuf )
+{
+    MSIPACKAGE *package;
+    LPWSTR path;
+    UINT r;
+
+    if (!szFolder)
+        return ERROR_INVALID_PARAMETER;
+
+    package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE );
+    if (!package)
+        return ERROR_INVALID_HANDLE;
+
+    path = resolve_folder( package, szFolder, FALSE, FALSE, NULL );
+    msiobj_release( &package->hdr );
+
+    if (!path)
+        return ERROR_DIRECTORY;
+
+    r = msi_strcpy_to_awstring( path, szPathBuf, pcchPathBuf );
+    msi_free( path );
+    return r;
+}
+
+/***********************************************************************
+ * MsiGetTargetPathA        (MSI.@)
+ */
+UINT WINAPI MsiGetTargetPathA( MSIHANDLE hInstall, LPCSTR szFolder, 
+                               LPSTR szPathBuf, DWORD* pcchPathBuf )
+{
+    LPWSTR szwFolder;
+    awstring path;
+    UINT r;
+
+    TRACE("%s %p %p\n", debugstr_a(szFolder), szPathBuf, pcchPathBuf);
+
+    szwFolder = strdupAtoW(szFolder);
+    if (szFolder && !szwFolder)
+        return ERROR_FUNCTION_FAILED; 
+
+    path.unicode = FALSE;
+    path.str.a = szPathBuf;
+
+    r = MSI_GetTargetPath( hInstall, szwFolder, &path, pcchPathBuf );
+
+    msi_free( szwFolder );
+
+    return r;
+}
+
+/***********************************************************************
+ * MsiGetTargetPathW        (MSI.@)
+ */
+UINT WINAPI MsiGetTargetPathW( MSIHANDLE hInstall, LPCWSTR szFolder,
+                               LPWSTR szPathBuf, DWORD* pcchPathBuf )
+{
+    awstring path;
+
+    TRACE("%s %p %p\n", debugstr_w(szFolder), szPathBuf, pcchPathBuf);
+
+    path.unicode = TRUE;
+    path.str.w = szPathBuf;
+
+    return MSI_GetTargetPath( hInstall, szFolder, &path, pcchPathBuf );
+}
+
+/***********************************************************************
+ * MsiGetSourcePath   (internal)
+ */
+static UINT MSI_GetSourcePath( MSIHANDLE hInstall, LPCWSTR szFolder,
+                               awstring *szPathBuf, DWORD* pcchPathBuf )
+{
+    MSIPACKAGE *package;
+    LPWSTR path;
+    UINT r;
+
+    TRACE("%s %p %p\n", debugstr_w(szFolder), szPathBuf, pcchPathBuf );
+
+    if (!szFolder)
+        return ERROR_INVALID_PARAMETER;
+
+    package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE);
+    if (!package)
+        return ERROR_INVALID_HANDLE;
+
+    if (szPathBuf->str.w && !pcchPathBuf )
+    {
+        msiobj_release( &package->hdr );
+        return ERROR_INVALID_PARAMETER;
+    }
+
+    path = resolve_folder(package, szFolder, TRUE, FALSE, NULL);
+    msiobj_release( &package->hdr );
+
+    TRACE("path = %s\n",debugstr_w(path));
+    if (!path)
+        return ERROR_DIRECTORY;
+
+    r = msi_strcpy_to_awstring( path, szPathBuf, pcchPathBuf );
+    msi_free( path );
+    return r;
+}
+
+/***********************************************************************
+ * MsiGetSourcePathA     (MSI.@)
+ */
+UINT WINAPI MsiGetSourcePathA( MSIHANDLE hInstall, LPCSTR szFolder, 
+                               LPSTR szPathBuf, DWORD* pcchPathBuf )
+{
+    LPWSTR folder;
+    awstring str;
+    UINT r;
+
+    TRACE("%s %p %p\n", szFolder, debugstr_a(szPathBuf), pcchPathBuf);
+
+    str.unicode = FALSE;
+    str.str.a = szPathBuf;
+
+    folder = strdupAtoW( szFolder );
+    r = MSI_GetSourcePath( hInstall, folder, &str, pcchPathBuf );
+    msi_free( folder );
+
+    return r;
+}
+
+/***********************************************************************
+ * MsiGetSourcePathW     (MSI.@)
+ */
+UINT WINAPI MsiGetSourcePathW( MSIHANDLE hInstall, LPCWSTR szFolder,
+                               LPWSTR szPathBuf, DWORD* pcchPathBuf )
+{
+    awstring str;
+
+    TRACE("%s %p %p\n", debugstr_w(szFolder), szPathBuf, pcchPathBuf );
+
+    str.unicode = TRUE;
+    str.str.w = szPathBuf;
+
+    return MSI_GetSourcePath( hInstall, szFolder, &str, pcchPathBuf );
+}
+
+/***********************************************************************
+ * MsiSetTargetPathA  (MSI.@)
+ */
+UINT WINAPI MsiSetTargetPathA( MSIHANDLE hInstall, LPCSTR szFolder,
+                               LPCSTR szFolderPath )
+{
+    LPWSTR szwFolder = NULL, szwFolderPath = NULL;
+    UINT rc = ERROR_OUTOFMEMORY;
+
+    if ( !szFolder || !szFolderPath )
+        return ERROR_INVALID_PARAMETER;
+
+    szwFolder = strdupAtoW(szFolder);
+    szwFolderPath = strdupAtoW(szFolderPath);
+    if (!szwFolder || !szwFolderPath)
+        goto end;
+
+    rc = MsiSetTargetPathW( hInstall, szwFolder, szwFolderPath );
+
+end:
+    msi_free(szwFolder);
+    msi_free(szwFolderPath);
+
+    return rc;
+}
+
+/*
+ * Ok my original interpretation of this was wrong. And it looks like msdn has
+ * changed a bit also. The given folder path does not have to actually already
+ * exist, it just cannot be read only and must be a legal folder path.
+ */
+UINT MSI_SetTargetPathW(MSIPACKAGE *package, LPCWSTR szFolder, 
+                             LPCWSTR szFolderPath)
+{
+    DWORD attrib;
+    LPWSTR path = NULL;
+    LPWSTR path2 = NULL;
+    MSIFOLDER *folder;
+
+    TRACE("(%p %s %s)\n",package, debugstr_w(szFolder),debugstr_w(szFolderPath));
+
+    attrib = GetFileAttributesW(szFolderPath);
+    if ( attrib != INVALID_FILE_ATTRIBUTES &&
+          (!(attrib & FILE_ATTRIBUTE_DIRECTORY) ||
+           attrib & FILE_ATTRIBUTE_OFFLINE ||
+           attrib & FILE_ATTRIBUTE_READONLY))
+        return ERROR_FUNCTION_FAILED;
+
+    path = resolve_folder(package,szFolder,FALSE,FALSE,&folder);
+    if (!path)
+        return ERROR_DIRECTORY;
+
+    if (attrib == INVALID_FILE_ATTRIBUTES)
+    {
+        if (!CreateDirectoryW(szFolderPath,NULL))
+        {
+            msi_free( path );
+            return ERROR_FUNCTION_FAILED;
+        }
+        RemoveDirectoryW(szFolderPath);
+    }
+
+    msi_free(folder->Property);
+    folder->Property = build_directory_name(2, szFolderPath, NULL);
+
+    if (lstrcmpiW(path, folder->Property) == 0)
+    {
+        /*
+         *  Resolved Target has not really changed, so just 
+         *  set this folder and do not recalculate everything.
+         */
+        msi_free(folder->ResolvedTarget);
+        folder->ResolvedTarget = NULL;
+        path2 = resolve_folder(package,szFolder,FALSE,TRUE,NULL);
+        msi_free(path2);
+    }
+    else
+    {
+        MSIFOLDER *f;
+
+        LIST_FOR_EACH_ENTRY( f, &package->folders, MSIFOLDER, entry )
+        {
+            msi_free(f->ResolvedTarget);
+            f->ResolvedTarget=NULL;
+        }
+
+        LIST_FOR_EACH_ENTRY( f, &package->folders, MSIFOLDER, entry )
+        {
+            path2 = resolve_folder(package, f->Directory, FALSE, TRUE, NULL);
+            msi_free(path2);
+        }
+    }
+    msi_free(path);
+
+    return ERROR_SUCCESS;
+}
+
+/***********************************************************************
+ * MsiSetTargetPathW  (MSI.@)
+ */
+UINT WINAPI MsiSetTargetPathW(MSIHANDLE hInstall, LPCWSTR szFolder, 
+                             LPCWSTR szFolderPath)
+{
+    MSIPACKAGE *package;
+    UINT ret;
+
+    TRACE("(%s %s)\n",debugstr_w(szFolder),debugstr_w(szFolderPath));
+
+    if ( !szFolder || !szFolderPath )
+        return ERROR_INVALID_PARAMETER;
+
+    package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE);
+    if (!package)
+        return ERROR_INVALID_HANDLE;
+
+    ret = MSI_SetTargetPathW( package, szFolder, szFolderPath );
+    msiobj_release( &package->hdr );
+    return ret;
+}
+
+/***********************************************************************
+ *           MsiGetMode    (MSI.@)
+ *
+ * Returns an internal installer state (if it is running in a mode iRunMode)
+ *
+ * PARAMS
+ *   hInstall    [I]  Handle to the installation
+ *   hRunMode    [I]  Checking run mode
+ *        MSIRUNMODE_ADMIN             Administrative mode
+ *        MSIRUNMODE_ADVERTISE         Advertisement mode
+ *        MSIRUNMODE_MAINTENANCE       Maintenance mode
+ *        MSIRUNMODE_ROLLBACKENABLED   Rollback is enabled
+ *        MSIRUNMODE_LOGENABLED        Log file is writing
+ *        MSIRUNMODE_OPERATIONS        Operations in progress??
+ *        MSIRUNMODE_REBOOTATEND       We need to reboot after installation completed
+ *        MSIRUNMODE_REBOOTNOW         We need to reboot to continue the installation
+ *        MSIRUNMODE_CABINET           Files from cabinet are installed
+ *        MSIRUNMODE_SOURCESHORTNAMES  Long names in source files is suppressed
+ *        MSIRUNMODE_TARGETSHORTNAMES  Long names in destination files is suppressed
+ *        MSIRUNMODE_RESERVED11        Reserved
+ *        MSIRUNMODE_WINDOWS9X         Running under Windows95/98
+ *        MSIRUNMODE_ZAWENABLED        Demand installation is supported
+ *        MSIRUNMODE_RESERVED14        Reserved
+ *        MSIRUNMODE_RESERVED15        Reserved
+ *        MSIRUNMODE_SCHEDULED         called from install script
+ *        MSIRUNMODE_ROLLBACK          called from rollback script
+ *        MSIRUNMODE_COMMIT            called from commit script
+ *
+ * RETURNS
+ *    In the state: TRUE
+ *    Not in the state: FALSE
+ *
+ */
+
+BOOL WINAPI MsiGetMode(MSIHANDLE hInstall, MSIRUNMODE iRunMode)
+{
+    BOOL r = FALSE;
+
+    switch (iRunMode)
+    {
+    case MSIRUNMODE_WINDOWS9X:
+        if (GetVersion() & 0x80000000)
+            r = TRUE;
+        break;
+
+    case MSIRUNMODE_RESERVED11:
+    case MSIRUNMODE_RESERVED14:
+    case MSIRUNMODE_RESERVED15:
+        break;
+
+    case MSIRUNMODE_SCHEDULED:
+    case MSIRUNMODE_ROLLBACK:
+    case MSIRUNMODE_COMMIT:
+        break;
+
+    default:
+        FIXME("%ld %d\n", hInstall, iRunMode);
+        r = TRUE;
+    }
+
+    return r;
+}
+
+/***********************************************************************
+ *           MsiSetMode    (MSI.@)
+ */
+BOOL WINAPI MsiSetMode(MSIHANDLE hInstall, MSIRUNMODE iRunMode, BOOL fState)
+{
+    switch (iRunMode)
+    {
+    case MSIRUNMODE_RESERVED11:
+    case MSIRUNMODE_WINDOWS9X:
+    case MSIRUNMODE_RESERVED14:
+    case MSIRUNMODE_RESERVED15:
+        return FALSE;
+    default:
+        FIXME("%ld %d %d\n", hInstall, iRunMode, fState);
+    }
+    return TRUE;
+}
+
+/***********************************************************************
+ * MsiSetFeatureStateA (MSI.@)
+ *
+ * According to the docs, when this is called it immediately recalculates
+ * all the component states as well
+ */
+UINT WINAPI MsiSetFeatureStateA(MSIHANDLE hInstall, LPCSTR szFeature,
+                                INSTALLSTATE iState)
+{
+    LPWSTR szwFeature = NULL;
+    UINT rc;
+
+    szwFeature = strdupAtoW(szFeature);
+
+    if (!szwFeature)
+        return ERROR_FUNCTION_FAILED;
+   
+    rc = MsiSetFeatureStateW(hInstall,szwFeature, iState); 
+
+    msi_free(szwFeature);
+
+    return rc;
+}
+
+
+
+UINT WINAPI MSI_SetFeatureStateW(MSIPACKAGE* package, LPCWSTR szFeature,
+                                INSTALLSTATE iState)
+{
+    UINT rc = ERROR_SUCCESS;
+    MSIFEATURE *feature, *child;
+
+    TRACE(" %s to %i\n",debugstr_w(szFeature), iState);
+
+    feature = get_loaded_feature(package,szFeature);
+    if (!feature)
+        return ERROR_UNKNOWN_FEATURE;
+
+    if (iState == INSTALLSTATE_ADVERTISED && 
+        feature->Attributes & msidbFeatureAttributesDisallowAdvertise)
+        return ERROR_FUNCTION_FAILED;
+
+    feature->ActionRequest = iState;
+    feature->Action = iState;
+
+    ACTION_UpdateComponentStates(package,szFeature);
+
+    /* update all the features that are children of this feature */
+    LIST_FOR_EACH_ENTRY( child, &package->features, MSIFEATURE, entry )
+    {
+        if (lstrcmpW(szFeature, child->Feature_Parent) == 0)
+            MSI_SetFeatureStateW(package, child->Feature, iState);
+    }
+    
+    return rc;
+}
+
+/***********************************************************************
+ * MsiSetFeatureStateW (MSI.@)
+ */
+UINT WINAPI MsiSetFeatureStateW(MSIHANDLE hInstall, LPCWSTR szFeature,
+                                INSTALLSTATE iState)
+{
+    MSIPACKAGE* package;
+    UINT rc = ERROR_SUCCESS;
+
+    TRACE(" %s to %i\n",debugstr_w(szFeature), iState);
+
+    package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE);
+    if (!package)
+        return ERROR_INVALID_HANDLE;
+
+    rc = MSI_SetFeatureStateW(package,szFeature,iState);
+
+    msiobj_release( &package->hdr );
+    return rc;
+}
+
+/***********************************************************************
+* MsiGetFeatureStateA   (MSI.@)
+*/
+UINT WINAPI MsiGetFeatureStateA(MSIHANDLE hInstall, LPSTR szFeature,
+                  INSTALLSTATE *piInstalled, INSTALLSTATE *piAction)
+{
+    LPWSTR szwFeature = NULL;
+    UINT rc;
+    
+    szwFeature = strdupAtoW(szFeature);
+
+    rc = MsiGetFeatureStateW(hInstall,szwFeature,piInstalled, piAction);
+
+    msi_free( szwFeature);
+
+    return rc;
+}
+
+UINT MSI_GetFeatureStateW(MSIPACKAGE *package, LPWSTR szFeature,
+                  INSTALLSTATE *piInstalled, INSTALLSTATE *piAction)
+{
+    MSIFEATURE *feature;
+
+    feature = get_loaded_feature(package,szFeature);
+    if (!feature)
+        return ERROR_UNKNOWN_FEATURE;
+
+    if (piInstalled)
+        *piInstalled = feature->Installed;
+
+    if (piAction)
+        *piAction = feature->Action;
+
+    TRACE("returning %i %i\n", feature->Installed, feature->Action);
+
+    return ERROR_SUCCESS;
+}
+
+/***********************************************************************
+* MsiGetFeatureStateW   (MSI.@)
+*/
+UINT WINAPI MsiGetFeatureStateW(MSIHANDLE hInstall, LPWSTR szFeature,
+                  INSTALLSTATE *piInstalled, INSTALLSTATE *piAction)
+{
+    MSIPACKAGE* package;
+    UINT ret;
+
+    TRACE("%ld %s %p %p\n", hInstall, debugstr_w(szFeature), piInstalled,
+piAction);
+
+    package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE);
+    if (!package)
+        return ERROR_INVALID_HANDLE;
+    ret = MSI_GetFeatureStateW(package, szFeature, piInstalled, piAction);
+    msiobj_release( &package->hdr );
+    return ret;
+}
+
+/***********************************************************************
+ * MsiSetComponentStateA (MSI.@)
+ */
+UINT WINAPI MsiSetComponentStateA(MSIHANDLE hInstall, LPCSTR szComponent,
+                                  INSTALLSTATE iState)
+{
+    UINT rc;
+    LPWSTR szwComponent = strdupAtoW(szComponent);
+
+    rc = MsiSetComponentStateW(hInstall, szwComponent, iState);
+
+    msi_free(szwComponent);
+
+    return rc;
+}
+
+/***********************************************************************
+ * MsiGetComponentStateA (MSI.@)
+ */
+UINT WINAPI MsiGetComponentStateA(MSIHANDLE hInstall, LPSTR szComponent,
+                  INSTALLSTATE *piInstalled, INSTALLSTATE *piAction)
+{
+    LPWSTR szwComponent= NULL;
+    UINT rc;
+    
+    szwComponent= strdupAtoW(szComponent);
+
+    rc = MsiGetComponentStateW(hInstall,szwComponent,piInstalled, piAction);
+
+    msi_free( szwComponent);
+
+    return rc;
+}
+
+static UINT MSI_SetComponentStateW(MSIPACKAGE *package, LPCWSTR szComponent,
+                                   INSTALLSTATE iState)
+{
+    MSICOMPONENT *comp;
+
+    TRACE("%p %s %d\n", package, debugstr_w(szComponent), iState);
+
+    comp = get_loaded_component(package, szComponent);
+    if (!comp)
+        return ERROR_UNKNOWN_COMPONENT;
+
+    comp->Installed = iState;
+
+    return ERROR_SUCCESS;
+}
+
+UINT MSI_GetComponentStateW(MSIPACKAGE *package, LPWSTR szComponent,
+                  INSTALLSTATE *piInstalled, INSTALLSTATE *piAction)
+{
+    MSICOMPONENT *comp;
+
+    TRACE("%p %s %p %p\n", package, debugstr_w(szComponent),
+           piInstalled, piAction);
+
+    comp = get_loaded_component(package,szComponent);
+    if (!comp)
+        return ERROR_UNKNOWN_COMPONENT;
+
+    if (piInstalled)
+        *piInstalled = comp->Installed;
+
+    if (piAction)
+        *piAction = comp->Action;
+
+    TRACE("states (%i, %i)\n", comp->Installed, comp->Action );
+
+    return ERROR_SUCCESS;
+}
+
+/***********************************************************************
+ * MsiSetComponentStateW (MSI.@)
+ */
+UINT WINAPI MsiSetComponentStateW(MSIHANDLE hInstall, LPCWSTR szComponent,
+                                  INSTALLSTATE iState)
+{
+    MSIPACKAGE* package;
+    UINT ret;
+
+    package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE);
+    if (!package)
+        return ERROR_INVALID_HANDLE;
+    ret = MSI_SetComponentStateW(package, szComponent, iState);
+    msiobj_release(&package->hdr);
+    return ret;
+}
+
+/***********************************************************************
+ * MsiGetComponentStateW (MSI.@)
+ */
+UINT WINAPI MsiGetComponentStateW(MSIHANDLE hInstall, LPWSTR szComponent,
+                  INSTALLSTATE *piInstalled, INSTALLSTATE *piAction)
+{
+    MSIPACKAGE* package;
+    UINT ret;
+
+    TRACE("%ld %s %p %p\n", hInstall, debugstr_w(szComponent),
+           piInstalled, piAction);
+
+    package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE);
+    if (!package)
+        return ERROR_INVALID_HANDLE;
+    ret = MSI_GetComponentStateW( package, szComponent, piInstalled, piAction);
+    msiobj_release( &package->hdr );
+    return ret;
+}
+
+/***********************************************************************
+ * MsiGetLanguage (MSI.@)
+ */
+LANGID WINAPI MsiGetLanguage(MSIHANDLE hInstall)
+{
+    MSIPACKAGE* package;
+    LANGID langid;
+    LPWSTR buffer;
+    static const WCHAR szProductLanguage[] =
+        {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
+    
+    package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE);
+    if (!package)
+        return ERROR_INVALID_HANDLE;
+
+    buffer = msi_dup_property( package, szProductLanguage );
+    langid = atoiW(buffer);
+
+    msi_free(buffer);
+    msiobj_release (&package->hdr);
+    return langid;
+}
diff --git a/reactos/dll/msi/msi.c b/reactos/dll/msi/msi.c
new file mode 100644 (file)
index 0000000..404bdf6
--- /dev/null
@@ -0,0 +1,2038 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2002,2003,2004,2005 Mike McCormack for CodeWeavers
+ *
+ * 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 <stdarg.h>
+
+#define COBJMACROS
+#define NONAMELESSUNION
+
+#include "windef.h"
+#include "winbase.h"
+#include "winreg.h"
+#include "winnls.h"
+#include "shlwapi.h"
+#include "wine/debug.h"
+#include "msi.h"
+#include "msiquery.h"
+#include "msipriv.h"
+#include "wincrypt.h"
+#include "winver.h"
+#include "winuser.h"
+#include "shlobj.h"
+#include "shobjidl.h"
+#include "objidl.h"
+#include "wine/unicode.h"
+#include "action.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+/* the UI level */
+INSTALLUILEVEL gUILevel = INSTALLUILEVEL_BASIC;
+HWND           gUIhwnd = 0;
+INSTALLUI_HANDLERA gUIHandlerA = NULL;
+INSTALLUI_HANDLERW gUIHandlerW = NULL;
+DWORD gUIFilter = 0;
+LPVOID gUIContext = NULL;
+WCHAR gszLogFile[MAX_PATH];
+HINSTANCE msi_hInstance;
+
+static LONG dll_count;
+
+static const WCHAR installerW[] = {'\\','I','n','s','t','a','l','l','e','r',0};
+
+/*
+ * Dll lifetime tracking declaration
+ */
+static void LockModule(void)
+{
+    InterlockedIncrement(&dll_count);
+}
+
+static void UnlockModule(void)
+{
+    InterlockedDecrement(&dll_count);
+}
+
+
+UINT WINAPI MsiOpenProductA(LPCSTR szProduct, MSIHANDLE *phProduct)
+{
+    UINT r;
+    LPWSTR szwProd = NULL;
+
+    TRACE("%s %p\n",debugstr_a(szProduct), phProduct);
+
+    if( szProduct )
+    {
+        szwProd = strdupAtoW( szProduct );
+        if( !szwProd )
+            return ERROR_OUTOFMEMORY;
+    }
+
+    r = MsiOpenProductW( szwProd, phProduct );
+
+    msi_free( szwProd );
+
+    return r;
+}
+
+static UINT MSI_OpenProductW( LPCWSTR szProduct, MSIPACKAGE **ppackage )
+{
+    LPWSTR path = NULL;
+    UINT r;
+    HKEY hKeyProduct = NULL;
+    DWORD count, type;
+
+    TRACE("%s %p\n", debugstr_w(szProduct), ppackage );
+
+    r = MSIREG_OpenUninstallKey(szProduct,&hKeyProduct,FALSE);
+    if( r != ERROR_SUCCESS )
+    {
+        r = ERROR_UNKNOWN_PRODUCT;
+        goto end;
+    }
+
+    /* find the size of the path */
+    type = count = 0;
+    r = RegQueryValueExW( hKeyProduct, INSTALLPROPERTY_LOCALPACKAGEW,
+                          NULL, &type, NULL, &count );
+    if( r != ERROR_SUCCESS )
+    {
+        r = ERROR_UNKNOWN_PRODUCT;
+        goto end;
+    }
+
+    /* now alloc and fetch the path of the database to open */
+    path = msi_alloc( count );
+    if( !path )
+        goto end;
+
+    r = RegQueryValueExW( hKeyProduct, INSTALLPROPERTY_LOCALPACKAGEW,
+                          NULL, &type, (LPBYTE) path, &count );
+    if( r != ERROR_SUCCESS )
+    {
+        r = ERROR_UNKNOWN_PRODUCT;
+        goto end;
+    }
+
+    r = MSI_OpenPackageW( path, ppackage );
+
+end:
+    msi_free( path );
+    if( hKeyProduct )
+        RegCloseKey( hKeyProduct );
+
+    return r;
+}
+
+UINT WINAPI MsiOpenProductW( LPCWSTR szProduct, MSIHANDLE *phProduct )
+{
+   MSIPACKAGE *package = NULL;
+   UINT r;
+
+   r = MSI_OpenProductW( szProduct, &package );
+   if( r == ERROR_SUCCESS )
+   {
+       *phProduct = alloc_msihandle( &package->hdr );
+       msiobj_release( &package->hdr );
+   }
+   return r;
+}
+
+UINT WINAPI MsiAdvertiseProductA(LPCSTR szPackagePath, LPCSTR szScriptfilePath,
+                LPCSTR szTransforms, LANGID lgidLanguage)
+{
+    FIXME("%s %s %s %08x\n",debugstr_a(szPackagePath),
+          debugstr_a(szScriptfilePath), debugstr_a(szTransforms), lgidLanguage);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiAdvertiseProductW(LPCWSTR szPackagePath, LPCWSTR szScriptfilePath,
+                LPCWSTR szTransforms, LANGID lgidLanguage)
+{
+    FIXME("%s %s %s %08x\n",debugstr_w(szPackagePath),
+          debugstr_w(szScriptfilePath), debugstr_w(szTransforms), lgidLanguage);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiAdvertiseProductExA(LPCSTR szPackagePath, LPCSTR szScriptfilePath,
+      LPCSTR szTransforms, LANGID lgidLanguage, DWORD dwPlatform, DWORD dwOptions)
+{
+    FIXME("%s %s %s %08x %08lx %08lx\n", debugstr_a(szPackagePath),
+          debugstr_a(szScriptfilePath), debugstr_a(szTransforms),
+          lgidLanguage, dwPlatform, dwOptions);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiAdvertiseProductExW( LPCWSTR szPackagePath, LPCWSTR szScriptfilePath,
+      LPCWSTR szTransforms, LANGID lgidLanguage, DWORD dwPlatform, DWORD dwOptions)
+{
+    FIXME("%s %s %s %08x %08lx %08lx\n", debugstr_w(szPackagePath),
+          debugstr_w(szScriptfilePath), debugstr_w(szTransforms),
+          lgidLanguage, dwPlatform, dwOptions);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiInstallProductA(LPCSTR szPackagePath, LPCSTR szCommandLine)
+{
+    LPWSTR szwPath = NULL, szwCommand = NULL;
+    UINT r = ERROR_OUTOFMEMORY;
+
+    TRACE("%s %s\n",debugstr_a(szPackagePath), debugstr_a(szCommandLine));
+
+    if( szPackagePath )
+    {
+        szwPath = strdupAtoW( szPackagePath );
+        if( !szwPath )
+            goto end;
+    }
+
+    if( szCommandLine )
+    {
+        szwCommand = strdupAtoW( szCommandLine );
+        if( !szwCommand )
+            goto end;
+    }
+
+    r = MsiInstallProductW( szwPath, szwCommand );
+
+end:
+    msi_free( szwPath );
+    msi_free( szwCommand );
+
+    return r;
+}
+
+UINT WINAPI MsiInstallProductW(LPCWSTR szPackagePath, LPCWSTR szCommandLine)
+{
+    MSIPACKAGE *package = NULL;
+    UINT r;
+
+    FIXME("%s %s\n",debugstr_w(szPackagePath), debugstr_w(szCommandLine));
+
+    r = MSI_OpenPackageW( szPackagePath, &package );
+    if (r == ERROR_SUCCESS)
+    {
+        r = MSI_InstallPackage( package, szPackagePath, szCommandLine );
+        msiobj_release( &package->hdr );
+    }
+
+    return r;
+}
+
+UINT WINAPI MsiReinstallProductA(LPCSTR szProduct, DWORD dwReinstallMode)
+{
+    FIXME("%s %08lx\n", debugstr_a(szProduct), dwReinstallMode);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiReinstallProductW(LPCWSTR szProduct, DWORD dwReinstallMode)
+{
+    FIXME("%s %08lx\n", debugstr_w(szProduct), dwReinstallMode);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiApplyPatchA(LPCSTR szPatchPackage, LPCSTR szInstallPackage,
+        INSTALLTYPE eInstallType, LPCSTR szCommandLine)
+{
+    FIXME("%s %s %d %s\n", debugstr_a(szPatchPackage), debugstr_a(szInstallPackage),
+          eInstallType, debugstr_a(szCommandLine));
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiApplyPatchW(LPCWSTR szPatchPackage, LPCWSTR szInstallPackage,
+         INSTALLTYPE eInstallType, LPCWSTR szCommandLine)
+{
+    FIXME("%s %s %d %s\n", debugstr_w(szPatchPackage), debugstr_w(szInstallPackage),
+          eInstallType, debugstr_w(szCommandLine));
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiConfigureProductExW(LPCWSTR szProduct, int iInstallLevel,
+                        INSTALLSTATE eInstallState, LPCWSTR szCommandLine)
+{
+    MSIPACKAGE* package = NULL;
+    UINT r;
+    DWORD sz;
+    WCHAR sourcepath[MAX_PATH];
+    WCHAR filename[MAX_PATH];
+    static const WCHAR szInstalled[] = {
+        ' ','I','n','s','t','a','l','l','e','d','=','1',0};
+    LPWSTR commandline;
+
+    FIXME("%s %d %d %s\n",debugstr_w(szProduct), iInstallLevel, eInstallState,
+          debugstr_w(szCommandLine));
+
+    if (eInstallState != INSTALLSTATE_LOCAL &&
+        eInstallState != INSTALLSTATE_DEFAULT)
+    {
+        FIXME("Not implemented for anything other than local installs\n");
+        return ERROR_CALL_NOT_IMPLEMENTED;
+    }
+
+    sz = sizeof(sourcepath);
+    MsiSourceListGetInfoW(szProduct, NULL, MSIINSTALLCONTEXT_USERMANAGED, 
+            MSICODE_PRODUCT, INSTALLPROPERTY_LASTUSEDSOURCEW, sourcepath,
+            &sz);
+
+    sz = sizeof(filename);
+    MsiSourceListGetInfoW(szProduct, NULL, MSIINSTALLCONTEXT_USERMANAGED, 
+            MSICODE_PRODUCT, INSTALLPROPERTY_PACKAGENAMEW, filename, &sz);
+
+    lstrcatW(sourcepath,filename);
+
+    /*
+     * ok 1, we need to find the msi file for this product.
+     *    2, find the source dir for the files
+     *    3, do the configure/install.
+     *    4, cleanupany runonce entry.
+     */
+
+    r = MSI_OpenProductW( szProduct, &package );
+    if (r != ERROR_SUCCESS)
+        return r;
+
+    sz = lstrlenW(szInstalled) + 1;
+
+    if (szCommandLine)
+        sz += lstrlenW(szCommandLine);
+
+    commandline = msi_alloc(sz * sizeof(WCHAR));
+    if (!commandline )
+    {
+        r = ERROR_OUTOFMEMORY;
+        goto end;
+    }
+
+    commandline[0] = 0;
+    if (szCommandLine)
+        lstrcpyW(commandline,szCommandLine);
+
+    if (MsiQueryProductStateW(szProduct) != INSTALLSTATE_UNKNOWN)
+        lstrcatW(commandline,szInstalled);
+
+    r = MSI_InstallPackage( package, sourcepath, commandline );
+
+    msi_free(commandline);
+
+end:
+    msiobj_release( &package->hdr );
+
+    return r;
+}
+
+UINT WINAPI MsiConfigureProductExA(LPCSTR szProduct, int iInstallLevel,
+                        INSTALLSTATE eInstallState, LPCSTR szCommandLine)
+{
+    LPWSTR szwProduct = NULL;
+    LPWSTR szwCommandLine = NULL;
+    UINT r = ERROR_OUTOFMEMORY;
+
+    if( szProduct )
+    {
+        szwProduct = strdupAtoW( szProduct );
+        if( !szwProduct )
+            goto end;
+    }
+
+    if( szCommandLine)
+    {
+        szwCommandLine = strdupAtoW( szCommandLine );
+        if( !szwCommandLine)
+            goto end;
+    }
+
+    r = MsiConfigureProductExW( szwProduct, iInstallLevel, eInstallState,
+                                szwCommandLine );
+end:
+    msi_free( szwProduct );
+    msi_free( szwCommandLine);
+
+    return r;
+}
+
+UINT WINAPI MsiConfigureProductA(LPCSTR szProduct, int iInstallLevel,
+                                 INSTALLSTATE eInstallState)
+{
+    LPWSTR szwProduct = NULL;
+    UINT r;
+
+    TRACE("%s %d %d\n",debugstr_a(szProduct), iInstallLevel, eInstallState);
+
+    if( szProduct )
+    {
+        szwProduct = strdupAtoW( szProduct );
+        if( !szwProduct )
+            return ERROR_OUTOFMEMORY;
+    }
+
+    r = MsiConfigureProductW( szwProduct, iInstallLevel, eInstallState );
+    msi_free( szwProduct );
+
+    return r;
+}
+
+UINT WINAPI MsiConfigureProductW(LPCWSTR szProduct, int iInstallLevel,
+                                 INSTALLSTATE eInstallState)
+{
+    FIXME("%s %d %d\n", debugstr_w(szProduct), iInstallLevel, eInstallState);
+
+    return MsiConfigureProductExW(szProduct, iInstallLevel, eInstallState, NULL);
+}
+
+UINT WINAPI MsiGetProductCodeA(LPCSTR szComponent, LPSTR szBuffer)
+{
+    LPWSTR szwComponent = NULL;
+    UINT r;
+    WCHAR szwBuffer[GUID_SIZE];
+
+    TRACE("%s %s\n",debugstr_a(szComponent), debugstr_a(szBuffer));
+
+    if( szComponent )
+    {
+        szwComponent = strdupAtoW( szComponent );
+        if( !szwComponent )
+            return ERROR_OUTOFMEMORY;
+    }
+
+    r = MsiGetProductCodeW( szwComponent, szwBuffer );
+
+    if( ERROR_SUCCESS == r )
+        WideCharToMultiByte(CP_ACP, 0, szwBuffer, -1, szBuffer, GUID_SIZE, NULL, NULL);
+
+    msi_free( szwComponent );
+
+    return r;
+}
+
+UINT WINAPI MsiGetProductCodeW(LPCWSTR szComponent, LPWSTR szBuffer)
+{
+    UINT rc;
+    HKEY hkey;
+    WCHAR szSquished[GUID_SIZE];
+    DWORD sz = GUID_SIZE;
+    static const WCHAR szPermKey[] =
+        { '0','0','0','0','0','0','0','0','0','0','0','0',
+          '0','0','0','0','0','0','0','0','0','0','0','0',
+          '0','0','0','0','0','0','0','0',0};
+
+    TRACE("%s %p\n",debugstr_w(szComponent), szBuffer);
+
+    if (NULL == szComponent)
+        return ERROR_INVALID_PARAMETER;
+
+    rc = MSIREG_OpenComponentsKey( szComponent, &hkey, FALSE);
+    if (rc != ERROR_SUCCESS)
+        return ERROR_UNKNOWN_COMPONENT;
+
+    rc = RegEnumValueW(hkey, 0, szSquished, &sz, NULL, NULL, NULL, NULL);
+    if (rc == ERROR_SUCCESS && strcmpW(szSquished,szPermKey)==0)
+    {
+        sz = GUID_SIZE;
+        rc = RegEnumValueW(hkey, 1, szSquished, &sz, NULL, NULL, NULL, NULL);
+    }
+
+    RegCloseKey(hkey);
+
+    if (rc != ERROR_SUCCESS)
+        return ERROR_INSTALL_FAILURE;
+
+    unsquash_guid(szSquished, szBuffer);
+    return ERROR_SUCCESS;
+}
+
+UINT WINAPI MsiGetProductInfoA(LPCSTR szProduct, LPCSTR szAttribute,
+                 LPSTR szBuffer, DWORD *pcchValueBuf)
+{
+    LPWSTR szwProduct = NULL, szwAttribute = NULL, szwBuffer = NULL;
+    UINT r = ERROR_OUTOFMEMORY;
+    DWORD pcchwValueBuf = 0;
+
+    TRACE("%s %s %p %p\n", debugstr_a(szProduct), debugstr_a(szAttribute),
+          szBuffer, pcchValueBuf);
+
+    if( szProduct )
+    {
+        szwProduct = strdupAtoW( szProduct );
+        if( !szwProduct )
+            goto end;
+    }
+    
+    if( szAttribute )
+    {
+        szwAttribute = strdupAtoW( szAttribute );
+        if( !szwAttribute )
+            goto end;
+    }
+
+    if( szBuffer )
+    {
+        szwBuffer = msi_alloc( (*pcchValueBuf) * sizeof(WCHAR) );
+        pcchwValueBuf = *pcchValueBuf;
+        if( !szwBuffer )     
+            goto end;
+    }
+
+    r = MsiGetProductInfoW( szwProduct, szwAttribute, szwBuffer, 
+                            &pcchwValueBuf );
+
+    if( ERROR_SUCCESS == r )
+    {
+        INT old_len = *pcchValueBuf;
+        *pcchValueBuf = WideCharToMultiByte(CP_ACP, 0, szwBuffer, pcchwValueBuf,
+                        szBuffer, *pcchValueBuf, NULL, NULL);
+        if (old_len > *pcchValueBuf)
+            szBuffer[*pcchValueBuf]=0;
+    }
+
+end:
+    msi_free( szwProduct );
+    msi_free( szwAttribute );
+    msi_free( szwBuffer );
+
+    return r;
+}
+
+UINT WINAPI MsiGetProductInfoW(LPCWSTR szProduct, LPCWSTR szAttribute,
+                LPWSTR szBuffer, DWORD *pcchValueBuf)
+{
+    MSIHANDLE hProduct;
+    UINT r;
+    static const WCHAR szProductVersion[] =
+        {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
+    static const WCHAR szProductLanguage[] =
+        {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
+
+    FIXME("%s %s %p %p\n",debugstr_w(szProduct), debugstr_w(szAttribute),
+          szBuffer, pcchValueBuf);
+
+    if (NULL != szBuffer && NULL == pcchValueBuf)
+        return ERROR_INVALID_PARAMETER;
+    if (NULL == szProduct || NULL == szAttribute)
+        return ERROR_INVALID_PARAMETER;
+    
+    /* check for special properties */
+    if (strcmpW(szAttribute, INSTALLPROPERTY_PACKAGECODEW)==0)
+    {
+        HKEY hkey;
+        WCHAR squished[GUID_SIZE];
+        WCHAR package[200];
+        DWORD sz = sizeof(squished);
+
+        r = MSIREG_OpenUserProductsKey(szProduct, &hkey, FALSE);
+        if (r != ERROR_SUCCESS)
+            return ERROR_UNKNOWN_PRODUCT;
+
+        r = RegQueryValueExW(hkey, INSTALLPROPERTY_PACKAGECODEW, NULL, NULL, 
+                        (LPBYTE)squished, &sz);
+        if (r != ERROR_SUCCESS)
+        {
+            RegCloseKey(hkey);
+            return ERROR_UNKNOWN_PRODUCT;
+        }
+
+        unsquash_guid(squished, package);
+        *pcchValueBuf = strlenW(package);
+        if (strlenW(package) > *pcchValueBuf)
+        {
+            RegCloseKey(hkey);
+            return ERROR_MORE_DATA;
+        }
+        else
+            strcpyW(szBuffer, package);
+
+        RegCloseKey(hkey);
+        r = ERROR_SUCCESS;
+    }
+    else if (strcmpW(szAttribute, INSTALLPROPERTY_VERSIONSTRINGW)==0)
+    {
+        r = MsiOpenProductW(szProduct, &hProduct);
+        if (ERROR_SUCCESS != r)
+            return r;
+
+        r = MsiGetPropertyW(hProduct, szProductVersion, szBuffer, pcchValueBuf);
+        MsiCloseHandle(hProduct);
+    }
+    else if (strcmpW(szAttribute, INSTALLPROPERTY_ASSIGNMENTTYPEW)==0)
+    {
+        FIXME("0 (zero) if advertised or per user , 1(one) if per machine.\n");
+        if (szBuffer)
+        {
+            szBuffer[0] = '1';
+            szBuffer[1] = 0;
+        }
+        if (pcchValueBuf)
+            *pcchValueBuf = 1;
+        r = ERROR_SUCCESS;
+    }
+    else if (strcmpW(szAttribute, INSTALLPROPERTY_LANGUAGEW)==0)
+    {
+        r = MsiOpenProductW(szProduct, &hProduct);
+        if (ERROR_SUCCESS != r)
+            return r;
+
+        r = MsiGetPropertyW(hProduct, szProductLanguage, szBuffer, pcchValueBuf);
+        MsiCloseHandle(hProduct);
+    }
+    else
+    {
+        r = MsiOpenProductW(szProduct, &hProduct);
+        if (ERROR_SUCCESS != r)
+            return r;
+
+        r = MsiGetPropertyW(hProduct, szAttribute, szBuffer, pcchValueBuf);
+        MsiCloseHandle(hProduct);
+    }
+
+    return r;
+}
+
+UINT WINAPI MsiEnableLogA(DWORD dwLogMode, LPCSTR szLogFile, DWORD attributes)
+{
+    LPWSTR szwLogFile = NULL;
+    UINT r;
+
+    TRACE("%08lx %s %08lx\n", dwLogMode, debugstr_a(szLogFile), attributes);
+
+    if( szLogFile )
+    {
+        szwLogFile = strdupAtoW( szLogFile );
+        if( !szwLogFile )
+            return ERROR_OUTOFMEMORY;
+    }
+    r = MsiEnableLogW( dwLogMode, szwLogFile, attributes );
+    msi_free( szwLogFile );
+    return r;
+}
+
+UINT WINAPI MsiEnableLogW(DWORD dwLogMode, LPCWSTR szLogFile, DWORD attributes)
+{
+    HANDLE file = INVALID_HANDLE_VALUE;
+
+    TRACE("%08lx %s %08lx\n", dwLogMode, debugstr_w(szLogFile), attributes);
+
+    lstrcpyW(gszLogFile,szLogFile);
+    if (!(attributes & INSTALLLOGATTRIBUTES_APPEND))
+        DeleteFileW(szLogFile);
+    file = CreateFileW(szLogFile, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS,
+                           FILE_ATTRIBUTE_NORMAL, NULL);
+    if (file != INVALID_HANDLE_VALUE)
+        CloseHandle(file);
+    else
+        ERR("Unable to enable log %s\n",debugstr_w(szLogFile));
+
+    return ERROR_SUCCESS;
+}
+
+INSTALLSTATE WINAPI MsiQueryProductStateA(LPCSTR szProduct)
+{
+    LPWSTR szwProduct = NULL;
+    INSTALLSTATE r;
+
+    if( szProduct )
+    {
+         szwProduct = strdupAtoW( szProduct );
+         if( !szwProduct )
+             return ERROR_OUTOFMEMORY;
+    }
+    r = MsiQueryProductStateW( szwProduct );
+    msi_free( szwProduct );
+    return r;
+}
+
+INSTALLSTATE WINAPI MsiQueryProductStateW(LPCWSTR szProduct)
+{
+    UINT rc;
+    INSTALLSTATE rrc = INSTALLSTATE_UNKNOWN;
+    HKEY hkey = 0;
+    static const WCHAR szWindowsInstaller[] = {
+         'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0 };
+    DWORD sz;
+
+    TRACE("%s\n", debugstr_w(szProduct));
+
+    rc = MSIREG_OpenUserProductsKey(szProduct,&hkey,FALSE);
+    if (rc != ERROR_SUCCESS)
+        goto end;
+
+    RegCloseKey(hkey);
+
+    rc = MSIREG_OpenUninstallKey(szProduct,&hkey,FALSE);
+    if (rc != ERROR_SUCCESS)
+        goto end;
+
+    sz = sizeof(rrc);
+    rc = RegQueryValueExW(hkey,szWindowsInstaller,NULL,NULL,(LPVOID)&rrc, &sz);
+    if (rc != ERROR_SUCCESS)
+        goto end;
+
+    switch (rrc)
+    {
+    case 1:
+        /* default */
+        rrc = INSTALLSTATE_DEFAULT;
+        break;
+    default:
+        FIXME("Unknown install state read from registry (%i)\n",rrc);
+        rrc = INSTALLSTATE_UNKNOWN;
+        break;
+    }
+end:
+    RegCloseKey(hkey);
+    return rrc;
+}
+
+INSTALLUILEVEL WINAPI MsiSetInternalUI(INSTALLUILEVEL dwUILevel, HWND *phWnd)
+{
+    INSTALLUILEVEL old = gUILevel;
+    HWND oldwnd = gUIhwnd;
+
+    TRACE("%08x %p\n", dwUILevel, phWnd);
+
+    gUILevel = dwUILevel;
+    if (phWnd)
+    {
+        gUIhwnd = *phWnd;
+        *phWnd = oldwnd;
+    }
+    return old;
+}
+
+INSTALLUI_HANDLERA WINAPI MsiSetExternalUIA(INSTALLUI_HANDLERA puiHandler,
+                                  DWORD dwMessageFilter, LPVOID pvContext)
+{
+    INSTALLUI_HANDLERA prev = gUIHandlerA;
+
+    TRACE("%p %lx %p\n",puiHandler, dwMessageFilter,pvContext);
+    gUIHandlerA = puiHandler;
+    gUIFilter = dwMessageFilter;
+    gUIContext = pvContext;
+
+    return prev;
+}
+
+INSTALLUI_HANDLERW WINAPI MsiSetExternalUIW(INSTALLUI_HANDLERW puiHandler,
+                                  DWORD dwMessageFilter, LPVOID pvContext)
+{
+    INSTALLUI_HANDLERW prev = gUIHandlerW;
+
+    TRACE("%p %lx %p\n",puiHandler,dwMessageFilter,pvContext);
+    gUIHandlerW = puiHandler;
+    gUIFilter = dwMessageFilter;
+    gUIContext = pvContext;
+
+    return prev;
+}
+
+/******************************************************************
+ *  MsiLoadStringW            [MSI.@]
+ *
+ * Loads a string from MSI's string resources.
+ *
+ * PARAMS
+ *
+ *   handle        [I]  only -1 is handled currently
+ *   id            [I]  id of the string to be loaded
+ *   lpBuffer      [O]  buffer for the string to be written to
+ *   nBufferMax    [I]  maximum size of the buffer in characters
+ *   lang          [I]  the preferred language for the string
+ *
+ * RETURNS
+ *
+ *   If successful, this function returns the language id of the string loaded
+ *   If the function fails, the function returns zero.
+ *
+ * NOTES
+ *
+ *   The type of the first parameter is unknown.  LoadString's prototype
+ *  suggests that it might be a module handle.  I have made it an MSI handle
+ *  for starters, as -1 is an invalid MSI handle, but not an invalid module
+ *  handle.  Maybe strings can be stored in an MSI database somehow.
+ */
+LANGID WINAPI MsiLoadStringW( MSIHANDLE handle, UINT id, LPWSTR lpBuffer,
+                int nBufferMax, LANGID lang )
+{
+    HRSRC hres;
+    HGLOBAL hResData;
+    LPWSTR p;
+    DWORD i, len;
+
+    TRACE("%ld %u %p %d %d\n", handle, id, lpBuffer, nBufferMax, lang);
+
+    if( handle != -1 )
+        FIXME("don't know how to deal with handle = %08lx\n", handle);
+
+    if( !lang )
+        lang = GetUserDefaultLangID();
+
+    hres = FindResourceExW( msi_hInstance, (LPCWSTR) RT_STRING,
+                            (LPWSTR)1, lang );
+    if( !hres )
+        return 0;
+    hResData = LoadResource( msi_hInstance, hres );
+    if( !hResData )
+        return 0;
+    p = LockResource( hResData );
+    if( !p )
+        return 0;
+
+    for (i = 0; i < (id&0xf); i++)
+       p += *p + 1;
+    len = *p;
+
+    if( nBufferMax <= len )
+        return 0;
+
+    memcpy( lpBuffer, p+1, len * sizeof(WCHAR));
+    lpBuffer[ len ] = 0;
+
+    TRACE("found -> %s\n", debugstr_w(lpBuffer));
+
+    return lang;
+}
+
+LANGID WINAPI MsiLoadStringA( MSIHANDLE handle, UINT id, LPSTR lpBuffer,
+                int nBufferMax, LANGID lang )
+{
+    LPWSTR bufW;
+    LANGID r;
+    DWORD len;
+
+    bufW = msi_alloc(nBufferMax*sizeof(WCHAR));
+    r = MsiLoadStringW(handle, id, bufW, nBufferMax, lang);
+    if( r )
+    {
+        len = WideCharToMultiByte(CP_ACP, 0, bufW, -1, NULL, 0, NULL, NULL );
+        if( len <= nBufferMax )
+            WideCharToMultiByte( CP_ACP, 0, bufW, -1,
+                                 lpBuffer, nBufferMax, NULL, NULL );
+        else
+            r = 0;
+    }
+    msi_free(bufW);
+    return r;
+}
+
+INSTALLSTATE WINAPI MsiLocateComponentA(LPCSTR szComponent, LPSTR lpPathBuf,
+                DWORD *pcchBuf)
+{
+    /* This FIXME will crash some installer 
+        *  FIXME("%s %p %p\n", debugstr_a(szComponent), lpPathBuf, pcchBuf);
+        */
+    return INSTALLSTATE_UNKNOWN;
+}
+
+INSTALLSTATE WINAPI MsiLocateComponentW(LPCWSTR szComponent, LPWSTR lpPathBuf,
+                DWORD *pcchBuf)
+{
+    FIXME("%s %p %p\n", debugstr_w(szComponent), lpPathBuf, pcchBuf);
+    return INSTALLSTATE_UNKNOWN;
+}
+
+UINT WINAPI MsiMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType,
+                WORD wLanguageId, DWORD f)
+{
+    FIXME("%p %s %s %u %08x %08lx\n",hWnd,debugstr_a(lpText),debugstr_a(lpCaption),
+          uType,wLanguageId,f);
+    return MessageBoxExA(hWnd,lpText,lpCaption,uType,wLanguageId); 
+}
+
+UINT WINAPI MsiMessageBoxW(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType,
+                WORD wLanguageId, DWORD f)
+{
+    FIXME("%p %s %s %u %08x %08lx\n",hWnd,debugstr_w(lpText),debugstr_w(lpCaption),
+          uType,wLanguageId,f);
+    return MessageBoxExW(hWnd,lpText,lpCaption,uType,wLanguageId); 
+}
+
+UINT WINAPI MsiProvideAssemblyA( LPCSTR szAssemblyName, LPCSTR szAppContext,
+                DWORD dwInstallMode, DWORD dwAssemblyInfo, LPSTR lpPathBuf,
+                DWORD* pcchPathBuf )
+{
+    FIXME("%s %s %08lx %08lx %p %p\n", debugstr_a(szAssemblyName),
+          debugstr_a(szAppContext), dwInstallMode, dwAssemblyInfo, lpPathBuf,
+          pcchPathBuf);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiProvideAssemblyW( LPCWSTR szAssemblyName, LPCWSTR szAppContext,
+                DWORD dwInstallMode, DWORD dwAssemblyInfo, LPWSTR lpPathBuf,
+                DWORD* pcchPathBuf )
+{
+    FIXME("%s %s %08lx %08lx %p %p\n", debugstr_w(szAssemblyName),
+          debugstr_w(szAppContext), dwInstallMode, dwAssemblyInfo, lpPathBuf,
+          pcchPathBuf);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiProvideComponentFromDescriptorA( LPCSTR szDescriptor,
+                LPSTR szPath, DWORD *pcchPath, DWORD *pcchArgs )
+{
+    FIXME("%s %p %p %p\n", debugstr_a(szDescriptor), szPath, pcchPath, pcchArgs );
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiProvideComponentFromDescriptorW( LPCWSTR szDescriptor,
+                LPWSTR szPath, DWORD *pcchPath, DWORD *pcchArgs )
+{
+    FIXME("%s %p %p %p\n", debugstr_w(szDescriptor), szPath, pcchPath, pcchArgs );
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+HRESULT WINAPI MsiGetFileSignatureInformationA( LPCSTR szSignedObjectPath,
+                DWORD dwFlags, PCCERT_CONTEXT* ppcCertContext, BYTE* pbHashData,
+                DWORD* pcbHashData)
+{
+    FIXME("%s %08lx %p %p %p\n", debugstr_a(szSignedObjectPath), dwFlags,
+          ppcCertContext, pbHashData, pcbHashData);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+HRESULT WINAPI MsiGetFileSignatureInformationW( LPCWSTR szSignedObjectPath,
+                DWORD dwFlags, PCCERT_CONTEXT* ppcCertContext, BYTE* pbHashData,
+                DWORD* pcbHashData)
+{
+    FIXME("%s %08lx %p %p %p\n", debugstr_w(szSignedObjectPath), dwFlags,
+          ppcCertContext, pbHashData, pcbHashData);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiGetProductPropertyA( MSIHANDLE hProduct, LPCSTR szProperty,
+                                    LPSTR szValue, DWORD *pccbValue )
+{
+    FIXME("%ld %s %p %p\n", hProduct, debugstr_a(szProperty), szValue, pccbValue);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiGetProductPropertyW( MSIHANDLE hProduct, LPCWSTR szProperty,
+                                    LPWSTR szValue, DWORD *pccbValue )
+{
+    FIXME("%ld %s %p %p\n", hProduct, debugstr_w(szProperty), szValue, pccbValue);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiVerifyPackageA( LPCSTR szPackage )
+{
+    UINT r;
+    LPWSTR szPack = NULL;
+
+    TRACE("%s\n", debugstr_a(szPackage) );
+
+    if( szPackage )
+    {
+        szPack = strdupAtoW( szPackage );
+        if( !szPack )
+            return ERROR_OUTOFMEMORY;
+    }
+
+    r = MsiVerifyPackageW( szPack );
+
+    msi_free( szPack );
+
+    return r;
+}
+
+UINT WINAPI MsiVerifyPackageW( LPCWSTR szPackage )
+{
+    MSIHANDLE handle;
+    UINT r;
+
+    TRACE("%s\n", debugstr_w(szPackage) );
+
+    r = MsiOpenDatabaseW( szPackage, MSIDBOPEN_READONLY, &handle );
+    MsiCloseHandle( handle );
+
+    return r;
+}
+
+INSTALLSTATE WINAPI MsiGetComponentPathA(LPCSTR szProduct, LPCSTR szComponent,
+                                         LPSTR lpPathBuf, DWORD* pcchBuf)
+{
+    LPWSTR szwProduct = NULL, szwComponent = NULL, lpwPathBuf= NULL;
+    INSTALLSTATE rc;
+    UINT incoming_len;
+
+    if( szProduct )
+    {
+        szwProduct = strdupAtoW( szProduct );
+        if( !szwProduct)
+            return ERROR_OUTOFMEMORY;
+    }
+
+    if( szComponent )
+    {
+        szwComponent = strdupAtoW( szComponent );
+        if( !szwComponent )
+        {
+            msi_free( szwProduct);
+            return ERROR_OUTOFMEMORY;
+        }
+    }
+
+    if( pcchBuf && *pcchBuf > 0 )
+    {
+        lpwPathBuf = msi_alloc( *pcchBuf * sizeof(WCHAR));
+        incoming_len = *pcchBuf;
+    }
+    else
+    {
+        lpwPathBuf = NULL;
+        incoming_len = 0;
+    }
+
+    rc = MsiGetComponentPathW(szwProduct, szwComponent, lpwPathBuf, pcchBuf);
+
+    msi_free( szwProduct);
+    msi_free( szwComponent);
+    if (lpwPathBuf)
+    {
+        if (rc != INSTALLSTATE_UNKNOWN)
+            WideCharToMultiByte(CP_ACP, 0, lpwPathBuf, incoming_len,
+                            lpPathBuf, incoming_len, NULL, NULL);
+        msi_free( lpwPathBuf);
+    }
+
+    return rc;
+}
+
+INSTALLSTATE WINAPI MsiGetComponentPathW(LPCWSTR szProduct, LPCWSTR szComponent,
+                                         LPWSTR lpPathBuf, DWORD* pcchBuf)
+{
+    WCHAR squished_pc[GUID_SIZE];
+    UINT rc;
+    INSTALLSTATE rrc = INSTALLSTATE_UNKNOWN;
+    HKEY hkey = 0;
+    LPWSTR path = NULL;
+    DWORD sz, type;
+
+    TRACE("%s %s %p %p\n", debugstr_w(szProduct),
+           debugstr_w(szComponent), lpPathBuf, pcchBuf);
+
+    if( !szComponent )
+        return INSTALLSTATE_INVALIDARG;
+    if( lpPathBuf && !pcchBuf )
+        return INSTALLSTATE_INVALIDARG;
+
+    squash_guid(szProduct,squished_pc);
+
+    rc = MSIREG_OpenProductsKey( szProduct, &hkey, FALSE);
+    if( rc != ERROR_SUCCESS )
+        goto end;
+
+    RegCloseKey(hkey);
+
+    rc = MSIREG_OpenComponentsKey( szComponent, &hkey, FALSE);
+    if( rc != ERROR_SUCCESS )
+        goto end;
+
+    sz = 0;
+    type = 0;
+    rc = RegQueryValueExW( hkey, squished_pc, NULL, &type, NULL, &sz );
+    if( rc != ERROR_SUCCESS )
+        goto end;
+    if( type != REG_SZ )
+        goto end;
+
+    sz += sizeof(WCHAR);
+    path = msi_alloc( sz );
+    if( !path )
+        goto end;
+
+    rc = RegQueryValueExW( hkey, squished_pc, NULL, NULL, (LPVOID) path, &sz );
+    if( rc != ERROR_SUCCESS )
+        goto end;
+
+    TRACE("found path of (%s:%s)(%s)\n", debugstr_w(szComponent),
+           debugstr_w(szProduct), debugstr_w(path));
+
+    if (path[0]=='0')
+    {
+        FIXME("Registry entry.. check entry\n");
+        rrc = INSTALLSTATE_LOCAL;
+    }
+    else
+    {
+        /* PROBABLY a file */
+        if ( GetFileAttributesW(path) != INVALID_FILE_ATTRIBUTES )
+            rrc = INSTALLSTATE_LOCAL;
+        else
+            rrc = INSTALLSTATE_ABSENT;
+    }
+
+    if( pcchBuf )
+    {
+        sz = sz / sizeof(WCHAR);
+        if( *pcchBuf >= sz )
+            lstrcpyW( lpPathBuf, path );
+        *pcchBuf = sz;
+    }
+
+end:
+    msi_free(path );
+    RegCloseKey(hkey);
+    return rrc;
+}
+
+/******************************************************************
+ * MsiQueryFeatureStateA      [MSI.@]
+ */
+INSTALLSTATE WINAPI MsiQueryFeatureStateA(LPCSTR szProduct, LPCSTR szFeature)
+{
+    LPWSTR szwProduct = NULL, szwFeature= NULL;
+    INSTALLSTATE rc = INSTALLSTATE_UNKNOWN;
+
+    szwProduct = strdupAtoW( szProduct );
+    if ( szProduct && !szwProduct )
+        goto end;
+
+    szwFeature = strdupAtoW( szFeature );
+    if ( szFeature && !szwFeature )
+        goto end;
+
+    rc = MsiQueryFeatureStateW(szwProduct, szwFeature);
+
+end:
+    msi_free( szwProduct);
+    msi_free( szwFeature);
+
+    return rc;
+}
+
+/******************************************************************
+ * MsiQueryFeatureStateW      [MSI.@]
+ *
+ * This does not verify that the Feature is functional. So i am only going to
+ * check the existence of the key in the registry. This should tell me if it is
+ * installed.
+ */
+INSTALLSTATE WINAPI MsiQueryFeatureStateW(LPCWSTR szProduct, LPCWSTR szFeature)
+{
+    WCHAR squishProduct[GUID_SIZE];
+    UINT rc;
+    DWORD sz = 0;
+    HKEY hkey;
+
+    TRACE("%s %s\n", debugstr_w(szProduct), debugstr_w(szFeature));
+
+    if (!szProduct || !szFeature)
+        return INSTALLSTATE_INVALIDARG;
+
+    if (!squash_guid( szProduct, squishProduct ))
+        return INSTALLSTATE_INVALIDARG;
+
+    rc = MSIREG_OpenFeaturesKey(szProduct, &hkey, FALSE);
+    if (rc != ERROR_SUCCESS)
+        return INSTALLSTATE_UNKNOWN;
+
+    rc = RegQueryValueExW( hkey, szFeature, NULL, NULL, NULL, &sz);
+    RegCloseKey(hkey);
+
+    if (rc == ERROR_SUCCESS)
+        return INSTALLSTATE_LOCAL;
+
+    return INSTALLSTATE_UNKNOWN;
+}
+
+/******************************************************************
+ * MsiGetFileVersionA         [MSI.@]
+ */
+UINT WINAPI MsiGetFileVersionA(LPCSTR szFilePath, LPSTR lpVersionBuf,
+                DWORD* pcchVersionBuf, LPSTR lpLangBuf, DWORD* pcchLangBuf)
+{
+    LPWSTR szwFilePath = NULL, lpwVersionBuff = NULL, lpwLangBuff = NULL;
+    UINT ret = ERROR_OUTOFMEMORY;
+
+    if( szFilePath )
+    {
+        szwFilePath = strdupAtoW( szFilePath );
+        if( !szwFilePath )
+            goto end;
+    }
+
+    if( lpVersionBuf && pcchVersionBuf && *pcchVersionBuf )
+    {
+        lpwVersionBuff = msi_alloc(*pcchVersionBuf*sizeof(WCHAR));
+        if( !lpwVersionBuff )
+            goto end;
+    }
+
+    if( lpLangBuf && pcchLangBuf && *pcchLangBuf )
+    {
+        lpwLangBuff = msi_alloc(*pcchVersionBuf*sizeof(WCHAR));
+        if( !lpwLangBuff )
+            goto end;
+    }
+
+    ret = MsiGetFileVersionW(szwFilePath, lpwVersionBuff, pcchVersionBuf,
+                             lpwLangBuff, pcchLangBuf);
+
+    if( lpwVersionBuff )
+        WideCharToMultiByte(CP_ACP, 0, lpwVersionBuff, -1,
+                            lpVersionBuf, *pcchVersionBuf, NULL, NULL);
+    if( lpwLangBuff )
+        WideCharToMultiByte(CP_ACP, 0, lpwLangBuff, -1,
+                            lpLangBuf, *pcchLangBuf, NULL, NULL);
+
+end:
+    msi_free(szwFilePath);
+    msi_free(lpwVersionBuff);
+    msi_free(lpwLangBuff);
+
+    return ret;
+}
+
+/******************************************************************
+ * MsiGetFileVersionW         [MSI.@]
+ */
+UINT WINAPI MsiGetFileVersionW(LPCWSTR szFilePath, LPWSTR lpVersionBuf,
+                DWORD* pcchVersionBuf, LPWSTR lpLangBuf, DWORD* pcchLangBuf)
+{
+    static WCHAR szVersionResource[] = {'\\',0};
+    static const WCHAR szVersionFormat[] = {
+        '%','d','.','%','d','.','%','d','.','%','d',0};
+    static const WCHAR szLangFormat[] = {'%','d',0};
+    UINT ret = 0;
+    DWORD dwVerLen;
+    LPVOID lpVer = NULL;
+    VS_FIXEDFILEINFO *ffi;
+    UINT puLen;
+    WCHAR tmp[32];
+
+    TRACE("%s %p %ld %p %ld\n", debugstr_w(szFilePath),
+          lpVersionBuf, pcchVersionBuf?*pcchVersionBuf:0,
+          lpLangBuf, pcchLangBuf?*pcchLangBuf:0);
+
+    dwVerLen = GetFileVersionInfoSizeW(szFilePath, NULL);
+    if( !dwVerLen )
+        return GetLastError();
+
+    lpVer = msi_alloc(dwVerLen);
+    if( !lpVer )
+    {
+        ret = ERROR_OUTOFMEMORY;
+        goto end;
+    }
+
+    if( !GetFileVersionInfoW(szFilePath, 0, dwVerLen, lpVer) )
+    {
+        ret = GetLastError();
+        goto end;
+    }
+    if( lpVersionBuf && pcchVersionBuf && *pcchVersionBuf )
+    {
+        if( VerQueryValueW(lpVer, szVersionResource, (LPVOID*)&ffi, &puLen) &&
+            (puLen > 0) )
+        {
+            wsprintfW(tmp, szVersionFormat,
+                  HIWORD(ffi->dwFileVersionMS), LOWORD(ffi->dwFileVersionMS),
+                  HIWORD(ffi->dwFileVersionLS), LOWORD(ffi->dwFileVersionLS));
+            lstrcpynW(lpVersionBuf, tmp, *pcchVersionBuf);
+            *pcchVersionBuf = lstrlenW(lpVersionBuf);
+        }
+        else
+        {
+            *lpVersionBuf = 0;
+            *pcchVersionBuf = 0;
+        }
+    }
+
+    if( lpLangBuf && pcchLangBuf && *pcchLangBuf )
+    {
+        DWORD lang = GetUserDefaultLangID();
+
+        FIXME("Retrieve language from file\n");
+        wsprintfW(tmp, szLangFormat, lang);
+        lstrcpynW(lpLangBuf, tmp, *pcchLangBuf);
+        *pcchLangBuf = lstrlenW(lpLangBuf);
+    }
+
+end:
+    msi_free(lpVer);
+    return ret;
+}
+
+
+/******************************************************************
+ *     DllMain
+ */
+BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+    switch(fdwReason)
+    {
+    case DLL_PROCESS_ATTACH:
+        msi_hInstance = hinstDLL;
+        DisableThreadLibraryCalls(hinstDLL);
+        msi_dialog_register_class();
+        break;
+    case DLL_PROCESS_DETACH:
+        msi_dialog_unregister_class();
+        /* FIXME: Cleanup */
+        break;
+    }
+    return TRUE;
+}
+
+typedef struct tagIClassFactoryImpl
+{
+    const IClassFactoryVtbl *lpVtbl;
+} IClassFactoryImpl;
+
+static HRESULT WINAPI MsiCF_QueryInterface(LPCLASSFACTORY iface,
+                REFIID riid,LPVOID *ppobj)
+{
+    IClassFactoryImpl *This = (IClassFactoryImpl *)iface;
+    FIXME("%p %s %p\n",This,debugstr_guid(riid),ppobj);
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI MsiCF_AddRef(LPCLASSFACTORY iface)
+{
+    LockModule();
+    return 2;
+}
+
+static ULONG WINAPI MsiCF_Release(LPCLASSFACTORY iface)
+{
+    UnlockModule();
+    return 1;
+}
+
+static HRESULT WINAPI MsiCF_CreateInstance(LPCLASSFACTORY iface,
+    LPUNKNOWN pOuter, REFIID riid, LPVOID *ppobj)
+{
+    IClassFactoryImpl *This = (IClassFactoryImpl *)iface;
+
+    FIXME("%p %p %s %p\n", This, pOuter, debugstr_guid(riid), ppobj);
+    return E_FAIL;
+}
+
+static HRESULT WINAPI MsiCF_LockServer(LPCLASSFACTORY iface, BOOL dolock)
+{
+    TRACE("(%p)->(%d)\n", iface, dolock);
+
+    if(dolock)
+        LockModule();
+    else
+        UnlockModule();
+
+    return S_OK;
+}
+
+static const IClassFactoryVtbl MsiCF_Vtbl =
+{
+    MsiCF_QueryInterface,
+    MsiCF_AddRef,
+    MsiCF_Release,
+    MsiCF_CreateInstance,
+    MsiCF_LockServer
+};
+
+static IClassFactoryImpl Msi_CF = { &MsiCF_Vtbl };
+
+/******************************************************************
+ * DllGetClassObject          [MSI.@]
+ */
+HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
+{
+    TRACE("%s %s %p\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
+
+    if( IsEqualCLSID (rclsid, &CLSID_IMsiServer) ||
+        IsEqualCLSID (rclsid, &CLSID_IMsiServerMessage) ||
+        IsEqualCLSID (rclsid, &CLSID_IMsiServerX1) ||
+        IsEqualCLSID (rclsid, &CLSID_IMsiServerX2) ||
+        IsEqualCLSID (rclsid, &CLSID_IMsiServerX3) )
+    {
+        *ppv = (LPVOID) &Msi_CF;
+        return S_OK;
+    }
+    return CLASS_E_CLASSNOTAVAILABLE;
+}
+
+/******************************************************************
+ * DllGetVersion              [MSI.@]
+ */
+HRESULT WINAPI DllGetVersion(DLLVERSIONINFO *pdvi)
+{
+    TRACE("%p\n",pdvi);
+
+    if (pdvi->cbSize != sizeof(DLLVERSIONINFO))
+        return E_INVALIDARG;
+
+    pdvi->dwMajorVersion = MSI_MAJORVERSION;
+    pdvi->dwMinorVersion = MSI_MINORVERSION;
+    pdvi->dwBuildNumber = MSI_BUILDNUMBER;
+    pdvi->dwPlatformID = 1;
+
+    return S_OK;
+}
+
+/******************************************************************
+ * DllCanUnloadNow            [MSI.@]
+ */
+HRESULT WINAPI DllCanUnloadNow(void)
+{
+    return dll_count == 0 ? S_OK : S_FALSE;
+}
+
+/***********************************************************************
+ * MsiGetFeatureUsageW           [MSI.@]
+ */
+UINT WINAPI MsiGetFeatureUsageW( LPCWSTR szProduct, LPCWSTR szFeature,
+                                 DWORD* pdwUseCount, WORD* pwDateUsed )
+{
+    FIXME("%s %s %p %p\n",debugstr_w(szProduct), debugstr_w(szFeature),
+          pdwUseCount, pwDateUsed);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+/***********************************************************************
+ * MsiGetFeatureUsageA           [MSI.@]
+ */
+UINT WINAPI MsiGetFeatureUsageA( LPCSTR szProduct, LPCSTR szFeature,
+                                 DWORD* pdwUseCount, WORD* pwDateUsed )
+{
+    LPWSTR prod = NULL, feat = NULL;
+    UINT ret = ERROR_OUTOFMEMORY;
+
+    TRACE("%s %s %p %p\n", debugstr_a(szProduct), debugstr_a(szFeature),
+          pdwUseCount, pwDateUsed);
+
+    prod = strdupAtoW( szProduct );
+    if (szProduct && !prod)
+        goto end;
+
+    feat = strdupAtoW( szFeature );
+    if (szFeature && !feat)
+        goto end;
+
+    ret = MsiGetFeatureUsageW( prod, feat, pdwUseCount, pwDateUsed );
+
+end:
+    msi_free( prod );
+    msi_free( feat );
+
+    return ret;
+}
+
+/***********************************************************************
+ * MsiUseFeatureExW           [MSI.@]
+ */
+INSTALLSTATE WINAPI MsiUseFeatureExW( LPCWSTR szProduct, LPCWSTR szFeature,
+                                      DWORD dwInstallMode, DWORD dwReserved )
+{
+    INSTALLSTATE state;
+
+    TRACE("%s %s %li %li\n", debugstr_w(szProduct), debugstr_w(szFeature),
+          dwInstallMode, dwReserved);
+
+    state = MsiQueryFeatureStateW( szProduct, szFeature );
+
+    if (dwReserved)
+        return INSTALLSTATE_INVALIDARG;
+
+    if (state == INSTALLSTATE_LOCAL && dwInstallMode != INSTALLMODE_NODETECTION)
+    {
+        FIXME("mark product %s feature %s as used\n",
+              debugstr_w(szProduct), debugstr_w(szFeature) );
+    }
+
+    return state;
+}
+
+/***********************************************************************
+ * MsiUseFeatureExA           [MSI.@]
+ */
+INSTALLSTATE WINAPI MsiUseFeatureExA( LPCSTR szProduct, LPCSTR szFeature,
+                                      DWORD dwInstallMode, DWORD dwReserved )
+{
+    INSTALLSTATE ret = INSTALLSTATE_UNKNOWN;
+    LPWSTR prod = NULL, feat = NULL;
+
+    TRACE("%s %s %li %li\n", debugstr_a(szProduct), debugstr_a(szFeature),
+          dwInstallMode, dwReserved);
+
+    prod = strdupAtoW( szProduct );
+    if (szProduct && !prod)
+        goto end;
+
+    feat = strdupAtoW( szFeature );
+    if (szFeature && !feat)
+        goto end;
+
+    ret = MsiUseFeatureExW( prod, feat, dwInstallMode, dwReserved );
+
+end:
+    msi_free( prod );
+    msi_free( feat );
+
+    return ret;
+}
+
+INSTALLSTATE WINAPI MsiUseFeatureW( LPCWSTR szProduct, LPCWSTR szFeature )
+{
+    FIXME("%s %s\n", debugstr_w(szProduct), debugstr_w(szFeature));
+
+    return INSTALLSTATE_LOCAL;
+}
+
+INSTALLSTATE WINAPI MsiUseFeatureA( LPCSTR szProduct, LPCSTR szFeature )
+{
+    INSTALLSTATE ret = INSTALLSTATE_UNKNOWN;
+    LPWSTR prod = NULL, feat = NULL;
+
+    TRACE("%s %s\n", debugstr_a(szProduct), debugstr_a(szFeature) );
+
+    prod = strdupAtoW( szProduct );
+    if (szProduct && !prod)
+        goto end;
+
+    feat = strdupAtoW( szFeature );
+    if (szFeature && !feat)
+        goto end;
+
+    ret = MsiUseFeatureW( prod, feat );
+
+end:
+    msi_free( prod );
+    msi_free( feat );
+
+    return ret;
+}
+
+/***********************************************************************
+ * MsiProvideQualifiedComponentExW [MSI.@]
+ */
+UINT WINAPI MsiProvideQualifiedComponentExW(LPCWSTR szComponent,
+                LPCWSTR szQualifier, DWORD dwInstallMode, LPWSTR szProduct,
+                DWORD Unused1, DWORD Unused2, LPWSTR lpPathBuf,
+                DWORD* pcchPathBuf)
+{
+    HKEY hkey;
+    UINT rc;
+    LPWSTR info;
+    DWORD sz;
+    WCHAR product[MAX_FEATURE_CHARS+1];
+    WCHAR component[MAX_FEATURE_CHARS+1];
+    WCHAR feature[MAX_FEATURE_CHARS+1];
+
+    TRACE("%s %s %li %s %li %li %p %p\n", debugstr_w(szComponent),
+          debugstr_w(szQualifier), dwInstallMode, debugstr_w(szProduct),
+          Unused1, Unused2, lpPathBuf, pcchPathBuf);
+   
+    rc = MSIREG_OpenUserComponentsKey(szComponent, &hkey, FALSE);
+    if (rc != ERROR_SUCCESS)
+        return ERROR_INDEX_ABSENT;
+
+    sz = 0;
+    rc = RegQueryValueExW( hkey, szQualifier, NULL, NULL, NULL, &sz);
+    if (sz <= 0)
+    {
+        RegCloseKey(hkey);
+        return ERROR_INDEX_ABSENT;
+    }
+
+    info = msi_alloc(sz);
+    rc = RegQueryValueExW( hkey, szQualifier, NULL, NULL, (LPBYTE)info, &sz);
+    if (rc != ERROR_SUCCESS)
+    {
+        RegCloseKey(hkey);
+        msi_free(info);
+        return ERROR_INDEX_ABSENT;
+    }
+
+    MsiDecomposeDescriptorW(info, product, feature, component, &sz);
+    
+    if (!szProduct)
+        rc = MsiGetComponentPathW(product, component, lpPathBuf, pcchPathBuf);
+    else
+        rc = MsiGetComponentPathW(szProduct, component, lpPathBuf, pcchPathBuf);
+   
+    RegCloseKey(hkey);
+    msi_free(info);
+
+    if (rc == INSTALLSTATE_LOCAL)
+        return ERROR_SUCCESS;
+    else 
+        return ERROR_FILE_NOT_FOUND;
+}
+
+/***********************************************************************
+ * MsiProvideQualifiedComponentW [MSI.@]
+ */
+UINT WINAPI MsiProvideQualifiedComponentW( LPCWSTR szComponent,
+                LPCWSTR szQualifier, DWORD dwInstallMode, LPWSTR lpPathBuf,
+                DWORD* pcchPathBuf)
+{
+    return MsiProvideQualifiedComponentExW(szComponent, szQualifier, 
+                    dwInstallMode, NULL, 0, 0, lpPathBuf, pcchPathBuf);
+}
+
+/***********************************************************************
+ * MsiProvideQualifiedComponentA [MSI.@]
+ */
+UINT WINAPI MsiProvideQualifiedComponentA( LPCSTR szComponent,
+                LPCSTR szQualifier, DWORD dwInstallMode, LPSTR lpPathBuf,
+                DWORD* pcchPathBuf)
+{
+    LPWSTR szwComponent, szwQualifier, lpwPathBuf;
+    DWORD pcchwPathBuf;
+    UINT rc;
+
+    TRACE("%s %s %li %p %p\n",szComponent, szQualifier,
+                    dwInstallMode, lpPathBuf, pcchPathBuf);
+
+    szwComponent= strdupAtoW( szComponent);
+    szwQualifier= strdupAtoW( szQualifier);
+
+    lpwPathBuf = msi_alloc(*pcchPathBuf * sizeof(WCHAR));
+
+    pcchwPathBuf = *pcchPathBuf;
+
+    rc = MsiProvideQualifiedComponentW(szwComponent, szwQualifier, 
+                    dwInstallMode, lpwPathBuf, &pcchwPathBuf);
+
+    msi_free(szwComponent);
+    msi_free(szwQualifier);
+    *pcchPathBuf = WideCharToMultiByte(CP_ACP, 0, lpwPathBuf, pcchwPathBuf,
+                    lpPathBuf, *pcchPathBuf, NULL, NULL);
+
+    msi_free(lpwPathBuf);
+    return rc;
+}
+
+USERINFOSTATE WINAPI MsiGetUserInfoW(LPCWSTR szProduct, LPWSTR lpUserNameBuf,
+                DWORD* pcchUserNameBuf, LPWSTR lpOrgNameBuf,
+                DWORD* pcchOrgNameBuf, LPWSTR lpSerialBuf, DWORD* pcchSerialBuf)
+{
+    HKEY hkey;
+    DWORD sz;
+    UINT rc = ERROR_SUCCESS,rc2 = ERROR_SUCCESS;
+
+    TRACE("%s %p %p %p %p %p %p\n",debugstr_w(szProduct), lpUserNameBuf,
+          pcchUserNameBuf, lpOrgNameBuf, pcchOrgNameBuf, lpSerialBuf,
+          pcchSerialBuf);
+
+    rc = MSIREG_OpenUninstallKey(szProduct, &hkey, FALSE);
+    if (rc != ERROR_SUCCESS)
+        return USERINFOSTATE_UNKNOWN;
+
+    if (lpUserNameBuf)
+    {
+        sz = *lpUserNameBuf * sizeof(WCHAR);
+        rc = RegQueryValueExW( hkey, INSTALLPROPERTY_REGOWNERW, NULL,
+                NULL, (LPBYTE)lpUserNameBuf,
+                               &sz);
+    }
+    if (!lpUserNameBuf && pcchUserNameBuf)
+    {
+        sz = 0;
+        rc = RegQueryValueExW( hkey, INSTALLPROPERTY_REGOWNERW, NULL,
+                NULL, NULL, &sz);
+    }
+
+    if (pcchUserNameBuf)
+        *pcchUserNameBuf = sz / sizeof(WCHAR);
+
+    if (lpOrgNameBuf)
+    {
+        sz = *pcchOrgNameBuf * sizeof(WCHAR);
+        rc2 = RegQueryValueExW( hkey, INSTALLPROPERTY_REGCOMPANYW, NULL,
+                NULL, (LPBYTE)lpOrgNameBuf, &sz);
+    }
+    if (!lpOrgNameBuf && pcchOrgNameBuf)
+    {
+        sz = 0;
+        rc2 = RegQueryValueExW( hkey, INSTALLPROPERTY_REGCOMPANYW, NULL,
+                NULL, NULL, &sz);
+    }
+
+    if (pcchOrgNameBuf)
+        *pcchOrgNameBuf = sz / sizeof(WCHAR);
+
+    if (rc != ERROR_SUCCESS && rc != ERROR_MORE_DATA && 
+        rc2 != ERROR_SUCCESS && rc2 != ERROR_MORE_DATA)
+    {
+        RegCloseKey(hkey);
+        return USERINFOSTATE_ABSENT;
+    }
+
+    if (lpSerialBuf)
+    {
+        sz = *pcchSerialBuf * sizeof(WCHAR);
+        RegQueryValueExW( hkey, INSTALLPROPERTY_PRODUCTIDW, NULL, NULL,
+                (LPBYTE)lpSerialBuf, &sz);
+    }
+    if (!lpSerialBuf && pcchSerialBuf)
+    {
+        sz = 0;
+        rc = RegQueryValueExW( hkey, INSTALLPROPERTY_PRODUCTIDW, NULL,
+                NULL, NULL, &sz);
+    }
+    if (pcchSerialBuf)
+        *pcchSerialBuf = sz / sizeof(WCHAR);
+    
+    RegCloseKey(hkey);
+    return USERINFOSTATE_PRESENT;
+}
+
+USERINFOSTATE WINAPI MsiGetUserInfoA(LPCSTR szProduct, LPSTR lpUserNameBuf,
+                DWORD* pcchUserNameBuf, LPSTR lpOrgNameBuf,
+                DWORD* pcchOrgNameBuf, LPSTR lpSerialBuf, DWORD* pcchSerialBuf)
+{
+    FIXME("%s %p %p %p %p %p %p\n",debugstr_a(szProduct), lpUserNameBuf,
+          pcchUserNameBuf, lpOrgNameBuf, pcchOrgNameBuf, lpSerialBuf,
+          pcchSerialBuf);
+
+    return USERINFOSTATE_UNKNOWN;
+}
+
+UINT WINAPI MsiCollectUserInfoW(LPCWSTR szProduct)
+{
+    MSIHANDLE handle;
+    UINT rc;
+    MSIPACKAGE *package;
+    static const WCHAR szFirstRun[] = {'F','i','r','s','t','R','u','n',0};
+
+    TRACE("(%s)\n",debugstr_w(szProduct));
+
+    rc = MsiOpenProductW(szProduct,&handle);
+    if (rc != ERROR_SUCCESS)
+        return ERROR_INVALID_PARAMETER;
+
+    package = msihandle2msiinfo(handle, MSIHANDLETYPE_PACKAGE);
+    rc = ACTION_PerformUIAction(package, szFirstRun);
+    msiobj_release( &package->hdr );
+
+    MsiCloseHandle(handle);
+
+    return rc;
+}
+
+UINT WINAPI MsiCollectUserInfoA(LPCSTR szProduct)
+{
+    MSIHANDLE handle;
+    UINT rc;
+    MSIPACKAGE *package;
+    static const WCHAR szFirstRun[] = {'F','i','r','s','t','R','u','n',0};
+
+    TRACE("(%s)\n",debugstr_a(szProduct));
+
+    rc = MsiOpenProductA(szProduct,&handle);
+    if (rc != ERROR_SUCCESS)
+        return ERROR_INVALID_PARAMETER;
+
+    package = msihandle2msiinfo(handle, MSIHANDLETYPE_PACKAGE);
+    rc = ACTION_PerformUIAction(package, szFirstRun);
+    msiobj_release( &package->hdr );
+
+    MsiCloseHandle(handle);
+
+    return rc;
+}
+
+/***********************************************************************
+ * MsiConfigureFeatureA            [MSI.@]
+ */
+UINT WINAPI MsiConfigureFeatureA(LPCSTR szProduct, LPCSTR szFeature, INSTALLSTATE eInstallState)
+{
+    FIXME("%s %s %i\n", debugstr_a(szProduct), debugstr_a(szFeature), eInstallState);
+    return ERROR_SUCCESS;
+}
+
+/***********************************************************************
+ * MsiConfigureFeatureW            [MSI.@]
+ */
+UINT WINAPI MsiConfigureFeatureW(LPCWSTR szProduct, LPCWSTR szFeature, INSTALLSTATE eInstallState)
+{
+    FIXME("%s %s %i\n", debugstr_w(szProduct), debugstr_w(szFeature), eInstallState);
+    return ERROR_SUCCESS;
+}
+
+UINT WINAPI MsiCreateAndVerifyInstallerDirectory(DWORD dwReserved)
+{
+    WCHAR path[MAX_PATH];
+
+    if(dwReserved) {
+        FIXME("Don't know how to handle argument %ld\n", dwReserved);
+        return ERROR_CALL_NOT_IMPLEMENTED;
+    }
+
+   if(!GetWindowsDirectoryW(path, MAX_PATH)) {
+        FIXME("GetWindowsDirectory failed unexpected! Error %ld\n",
+              GetLastError());
+        return ERROR_CALL_NOT_IMPLEMENTED;
+   }
+
+   strcatW(path, installerW);
+
+   CreateDirectoryW(path, NULL);
+
+   return 0;
+}
+
+/***********************************************************************
+ * MsiGetShortcutTargetA           [MSI.@]
+ */
+UINT WINAPI MsiGetShortcutTargetA( LPCSTR szShortcutTarget,
+                                   LPSTR szProductCode, LPSTR szFeatureId,
+                                   LPSTR szComponentCode )
+{
+    LPWSTR target;
+    const int len = MAX_FEATURE_CHARS+1;
+    WCHAR product[MAX_FEATURE_CHARS+1], feature[MAX_FEATURE_CHARS+1], component[MAX_FEATURE_CHARS+1];
+    UINT r;
+
+    target = strdupAtoW( szShortcutTarget );
+    if (szShortcutTarget && !target )
+        return ERROR_OUTOFMEMORY;
+    product[0] = 0;
+    feature[0] = 0;
+    component[0] = 0;
+    r = MsiGetShortcutTargetW( target, product, feature, component );
+    msi_free( target );
+    if (r == ERROR_SUCCESS)
+    {
+        WideCharToMultiByte( CP_ACP, 0, product, -1, szProductCode, len, NULL, NULL );
+        WideCharToMultiByte( CP_ACP, 0, feature, -1, szFeatureId, len, NULL, NULL );
+        WideCharToMultiByte( CP_ACP, 0, component, -1, szComponentCode, len, NULL, NULL );
+    }
+    return r;
+}
+
+/***********************************************************************
+ * MsiGetShortcutTargetW           [MSI.@]
+ */
+UINT WINAPI MsiGetShortcutTargetW( LPCWSTR szShortcutTarget,
+                                   LPWSTR szProductCode, LPWSTR szFeatureId,
+                                   LPWSTR szComponentCode )
+{
+    IShellLinkDataList *dl = NULL;
+    IPersistFile *pf = NULL;
+    LPEXP_DARWIN_LINK darwin = NULL;
+    HRESULT r, init;
+
+    TRACE("%s %p %p %p\n", debugstr_w(szShortcutTarget),
+          szProductCode, szFeatureId, szComponentCode );
+
+    init = CoInitialize(NULL);
+
+    r = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
+                          &IID_IPersistFile, (LPVOID*) &pf );
+    if( SUCCEEDED( r ) )
+    {
+        r = IPersistFile_Load( pf, szShortcutTarget,
+                               STGM_READ | STGM_SHARE_DENY_WRITE );
+        if( SUCCEEDED( r ) )
+        {
+            r = IPersistFile_QueryInterface( pf, &IID_IShellLinkDataList,
+                                             (LPVOID*) &dl );
+            if( SUCCEEDED( r ) )
+            {
+                IShellLinkDataList_CopyDataBlock( dl, EXP_DARWIN_ID_SIG,
+                                                  (LPVOID) &darwin );
+                IShellLinkDataList_Release( dl );
+            }
+        }
+        IPersistFile_Release( pf );
+    }
+
+    if (SUCCEEDED(init))
+        CoUninitialize();
+
+    TRACE("darwin = %p\n", darwin);
+
+    if (darwin)
+    {
+        DWORD sz;
+        UINT ret;
+
+        ret = MsiDecomposeDescriptorW( darwin->szwDarwinID,
+                  szProductCode, szFeatureId, szComponentCode, &sz );
+        LocalFree( darwin );
+        return ret;
+    }
+
+    return ERROR_FUNCTION_FAILED;
+}
+
+UINT WINAPI MsiReinstallFeatureW( LPCWSTR szProduct, LPCWSTR szFeature,
+                                  DWORD dwReinstallMode )
+{
+    MSIPACKAGE* package = NULL;
+    UINT r;
+    DWORD sz;
+    WCHAR sourcepath[MAX_PATH];
+    WCHAR filename[MAX_PATH];
+    static const WCHAR szInstalled[] = {
+        ' ','L','O','G','V','E','R','B','O','S','E','=','1',' ','I','n','s','t','a','l','l','e','d','=','1',0};
+    static const WCHAR fmt[] = {'R','E','I','N','S','T','A','L','L','=','%','s',0};
+    static const WCHAR REINSTALLMODE[] = {'R','E','I','N','S','T','A','L','L','M','O','D','E',0};
+    WCHAR reinstallmode[11];
+    LPWSTR ptr;
+    LPWSTR commandline;
+
+    FIXME("%s %s %li\n", debugstr_w(szProduct), debugstr_w(szFeature),
+                           dwReinstallMode);
+
+    memset(reinstallmode,0,sizeof(reinstallmode));
+    ptr = reinstallmode;
+
+    if (dwReinstallMode & REINSTALLMODE_FILEMISSING)
+        { *ptr = 'p'; ptr++; }
+    if (dwReinstallMode & REINSTALLMODE_FILEOLDERVERSION)
+        { *ptr = 'o'; ptr++; }
+    if (dwReinstallMode & REINSTALLMODE_FILEEQUALVERSION)
+        { *ptr = 'w'; ptr++; }
+    if (dwReinstallMode & REINSTALLMODE_FILEEXACT)
+        { *ptr = 'd'; ptr++; }
+    if (dwReinstallMode & REINSTALLMODE_FILEVERIFY)
+        { *ptr = 'c'; ptr++; }
+    if (dwReinstallMode & REINSTALLMODE_FILEREPLACE)
+        { *ptr = 'a'; ptr++; }
+    if (dwReinstallMode & REINSTALLMODE_USERDATA)
+        { *ptr = 'u'; ptr++; }
+    if (dwReinstallMode & REINSTALLMODE_MACHINEDATA)
+        { *ptr = 'm'; ptr++; }
+    if (dwReinstallMode & REINSTALLMODE_SHORTCUT)
+        { *ptr = 's'; ptr++; }
+    if (dwReinstallMode & REINSTALLMODE_PACKAGE)
+        { *ptr = 'v'; ptr++; }
+    
+    sz = sizeof(sourcepath);
+    MsiSourceListGetInfoW(szProduct, NULL, MSIINSTALLCONTEXT_USERMANAGED, 
+            MSICODE_PRODUCT, INSTALLPROPERTY_LASTUSEDSOURCEW, sourcepath,
+            &sz);
+
+    sz = sizeof(filename);
+    MsiSourceListGetInfoW(szProduct, NULL, MSIINSTALLCONTEXT_USERMANAGED, 
+            MSICODE_PRODUCT, INSTALLPROPERTY_PACKAGENAMEW, filename, &sz);
+
+    strcatW(sourcepath,filename);
+
+    if (dwReinstallMode & REINSTALLMODE_PACKAGE)
+        r = MSI_OpenPackageW( sourcepath, &package );
+    else
+        r = MSI_OpenProductW( szProduct, &package );
+
+    if (r != ERROR_SUCCESS)
+        return r;
+
+    MSI_SetPropertyW(package,REINSTALLMODE,reinstallmode);
+    
+    sz = lstrlenW(szInstalled);
+    sz += lstrlenW(fmt);
+    sz += lstrlenW(szFeature);
+
+    commandline = msi_alloc(sz * sizeof(WCHAR));
+
+    sprintfW(commandline,fmt,szFeature);
+    lstrcatW(commandline,szInstalled);
+
+    r = MSI_InstallPackage( package, sourcepath, commandline );
+
+    msiobj_release( &package->hdr );
+
+    msi_free(commandline);
+
+    return r;
+}
+
+UINT WINAPI MsiReinstallFeatureA( LPCSTR szProduct, LPCSTR szFeature,
+                                  DWORD dwReinstallMode )
+{
+    LPWSTR wszProduct;
+    LPWSTR wszFeature;
+    UINT rc;
+
+    TRACE("%s %s %li\n", debugstr_a(szProduct), debugstr_a(szFeature),
+                           dwReinstallMode);
+
+    wszProduct = strdupAtoW(szProduct);
+    wszFeature = strdupAtoW(szFeature);
+
+    rc = MsiReinstallFeatureW(wszProduct, wszFeature, dwReinstallMode);
+    
+    msi_free(wszProduct);
+    msi_free(wszFeature);
+    return rc;
+}
+
+/***********************************************************************
+ * MsiEnumPatchesA            [MSI.@]
+ */
+UINT WINAPI MsiEnumPatchesA( LPCSTR szProduct, DWORD iPatchIndex, 
+        LPSTR lpPatchBuf, LPSTR lpTransformsBuf, DWORD* pcchTransformsBuf)
+{
+    FIXME("%s %ld %p %p %p\n", debugstr_a(szProduct),
+          iPatchIndex, lpPatchBuf, lpTransformsBuf, pcchTransformsBuf);
+    return ERROR_NO_MORE_ITEMS;
+}
+
+/***********************************************************************
+ * MsiEnumPatchesW            [MSI.@]
+ */
+UINT WINAPI MsiEnumPatchesW( LPCWSTR szProduct, DWORD iPatchIndex, 
+        LPWSTR lpPatchBuf, LPWSTR lpTransformsBuf, DWORD* pcchTransformsBuf)
+{
+    FIXME("%s %ld %p %p %p\n", debugstr_w(szProduct),
+          iPatchIndex, lpPatchBuf, lpTransformsBuf, pcchTransformsBuf);
+    return ERROR_NO_MORE_ITEMS;
+}
+
+/***********************************************************************
+ * MsiGetFileHashW            [MSI.@]
+ */
+UINT WINAPI MsiGetFileHashW( LPCWSTR szFilePath, DWORD dwOptions,
+                             PMSIFILEHASHINFO pHash )
+{
+    FIXME("%s %08lx %p\n", debugstr_w(szFilePath), dwOptions, pHash );
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+/***********************************************************************
+ * MsiGetFileHashA            [MSI.@]
+ */
+UINT WINAPI MsiGetFileHashA( LPCSTR szFilePath, DWORD dwOptions,
+                             PMSIFILEHASHINFO pHash )
+{
+    FIXME("%s %08lx %p\n", debugstr_a(szFilePath), dwOptions, pHash );
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+/***********************************************************************
+ * MsiAdvertiseScriptW        [MSI.@]
+ */
+UINT WINAPI MsiAdvertiseScriptW( LPCWSTR szScriptFile, DWORD dwFlags,
+                                 PHKEY phRegData, BOOL fRemoveItems )
+{
+    FIXME("%s %08lx %p %d\n",
+          debugstr_w( szScriptFile ), dwFlags, phRegData, fRemoveItems );
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+/***********************************************************************
+ * MsiAdvertiseScriptA        [MSI.@]
+ */
+UINT WINAPI MsiAdvertiseScriptA( LPCSTR szScriptFile, DWORD dwFlags,
+                                 PHKEY phRegData, BOOL fRemoveItems )
+{
+    FIXME("%s %08lx %p %d\n",
+          debugstr_a( szScriptFile ), dwFlags, phRegData, fRemoveItems );
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
diff --git a/reactos/dll/msi/msi.rc b/reactos/dll/msi/msi.rc
new file mode 100644 (file)
index 0000000..7f57ef1
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Resources for MSI
+ *
+ * Copyright 2005 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
+ */
+
+#include "windef.h"
+#include "winbase.h"
+#include "winuser.h"
+#include "winnls.h"
+
+LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
+
+#include "version.rc"
+
+#include "msi_Bg.rc"
+#include "msi_De.rc"
+#include "msi_En.rc"
+#include "msi_Ja.rc" 
+#include "msi_Es.rc"
+#include "msi_Fi.rc"
+#include "msi_Fr.rc"
+#include "msi_Ko.rc"
+#include "msi_Nl.rc"
+#include "msi_No.rc"
+#include "msi_Pt.rc"
+#include "msi_Ru.rc"
+#include "msi_Hu.rc"
diff --git a/reactos/dll/msi/msi.spec b/reactos/dll/msi/msi.spec
new file mode 100644 (file)
index 0000000..802adef
--- /dev/null
@@ -0,0 +1,237 @@
+5 stdcall MsiAdvertiseProductA(str str str long)
+6 stdcall MsiAdvertiseProductW(wstr wstr wstr long)
+7 stdcall MsiCloseAllHandles()
+8 stdcall MsiCloseHandle(long)
+9 stdcall MsiCollectUserInfoA(str)
+10 stdcall MsiCollectUserInfoW(wstr)
+11 stdcall MsiConfigureFeatureA(str str long)
+12 stub MsiConfigureFeatureFromDescriptorA
+13 stub MsiConfigureFeatureFromDescriptorW
+14 stdcall MsiConfigureFeatureW(wstr wstr ptr)
+15 stdcall MsiConfigureProductA(str long long)
+16 stdcall MsiConfigureProductW(wstr long long)
+17 stdcall MsiCreateRecord(long)
+18 stdcall MsiDatabaseApplyTransformA(long str long)
+19 stdcall MsiDatabaseApplyTransformW(long wstr long)
+20 stdcall MsiDatabaseCommit(long)
+21 stdcall MsiDatabaseExportA(long str str str)
+22 stdcall MsiDatabaseExportW(long wstr wstr wstr)
+23 stdcall MsiDatabaseGenerateTransformA(long long str long long)
+24 stdcall MsiDatabaseGenerateTransformW(long long wstr long long)
+25 stdcall MsiDatabaseGetPrimaryKeysA(long str ptr)
+26 stdcall MsiDatabaseGetPrimaryKeysW(long wstr ptr)
+27 stdcall MsiDatabaseImportA(str str long)
+28 stdcall MsiDatabaseImportW(wstr wstr long)
+29 stub MsiDatabaseMergeA
+30 stub MsiDatabaseMergeW
+31 stdcall MsiDatabaseOpenViewA(long str ptr)
+32 stdcall MsiDatabaseOpenViewW(long wstr ptr)
+33 stdcall MsiDoActionA(long str)
+34 stdcall MsiDoActionW(long wstr)
+35 stdcall MsiEnableUIPreview(long ptr)
+36 stdcall MsiEnumClientsA(str long ptr)
+37 stdcall MsiEnumClientsW(wstr long ptr)
+38 stdcall MsiEnumComponentQualifiersA(str long ptr ptr ptr ptr)
+39 stdcall MsiEnumComponentQualifiersW(wstr long ptr ptr ptr ptr)
+40 stdcall MsiEnumComponentsA(long ptr)
+41 stdcall MsiEnumComponentsW(long ptr)
+42 stdcall MsiEnumFeaturesA(str long ptr ptr)
+43 stdcall MsiEnumFeaturesW(wstr long ptr ptr)
+44 stdcall MsiEnumProductsA(long ptr)
+45 stdcall MsiEnumProductsW(long ptr)
+46 stdcall MsiEvaluateConditionA(long str)
+47 stdcall MsiEvaluateConditionW(long wstr)
+48 stdcall MsiGetLastErrorRecord()
+49 stdcall MsiGetActiveDatabase(long)
+50 stdcall MsiGetComponentStateA(long str ptr ptr)
+51 stdcall MsiGetComponentStateW(long wstr ptr ptr)
+52 stub MsiGetDatabaseState
+53 stub MsiGetFeatureCostA
+54 stub MsiGetFeatureCostW
+55 stub MsiGetFeatureInfoA
+56 stub MsiGetFeatureInfoW
+57 stdcall MsiGetFeatureStateA(long str ptr ptr)
+58 stdcall MsiGetFeatureStateW(long wstr ptr ptr)
+59 stdcall MsiGetFeatureUsageA(str str ptr ptr)
+60 stdcall MsiGetFeatureUsageW(wstr wstr ptr ptr)
+61 stub MsiGetFeatureValidStatesA
+62 stub MsiGetFeatureValidStatesW
+63 stdcall MsiGetLanguage(long)
+64 stdcall MsiGetMode(long long)
+65 stdcall MsiGetProductCodeA(str str)
+66 stdcall MsiGetProductCodeW(wstr wstr)
+67 stdcall MsiGetProductInfoA(str str str long)
+68 stub MsiGetProductInfoFromScriptA
+69 stub MsiGetProductInfoFromScriptW
+70 stdcall MsiGetProductInfoW(wstr wstr wstr long)
+71 stdcall MsiGetProductPropertyA(long str ptr ptr)
+72 stdcall MsiGetProductPropertyW(long wstr ptr ptr)
+73 stdcall MsiGetPropertyA(ptr str ptr ptr)
+74 stdcall MsiGetPropertyW(ptr wstr ptr ptr)
+75 stdcall MsiGetSourcePathA(long str ptr ptr)
+76 stdcall MsiGetSourcePathW(long wstr ptr ptr)
+77 stdcall MsiGetSummaryInformationA(long str long ptr)
+78 stdcall MsiGetSummaryInformationW(long wstr long ptr)
+79 stdcall MsiGetTargetPathA(long str ptr ptr)
+80 stdcall MsiGetTargetPathW(long wstr ptr ptr)
+81 stdcall MsiGetUserInfoA(str ptr ptr ptr ptr ptr ptr)
+82 stdcall MsiGetUserInfoW(wstr ptr ptr ptr ptr ptr ptr)
+83 stub MsiInstallMissingComponentA
+84 stub MsiInstallMissingComponentW
+85 stub MsiInstallMissingFileA
+86 stub MsiInstallMissingFileW
+87 stdcall MsiInstallProductA(str str)
+88 stdcall MsiInstallProductW(wstr wstr)
+89 stdcall MsiLocateComponentA(str ptr long)
+90 stdcall MsiLocateComponentW(wstr ptr long)
+91 stdcall MsiOpenDatabaseA(str str ptr)
+92 stdcall MsiOpenDatabaseW(wstr wstr ptr)
+93 stdcall MsiOpenPackageA(str ptr)
+94 stdcall MsiOpenPackageW(wstr ptr)
+95 stdcall MsiOpenProductA(str ptr)
+96 stdcall MsiOpenProductW(wstr ptr)
+97 stdcall MsiPreviewBillboardA(long str str)
+98 stdcall MsiPreviewBillboardW(long wstr wstr)
+99 stdcall MsiPreviewDialogA(long str)
+100 stdcall MsiPreviewDialogW(long wstr)
+101 stub MsiProcessAdvertiseScriptA
+102 stub MsiProcessAdvertiseScriptW
+103 stdcall MsiProcessMessage(long long long)
+104 stub MsiProvideComponentA
+105 stdcall MsiProvideComponentFromDescriptorA(str ptr ptr ptr)
+106 stdcall MsiProvideComponentFromDescriptorW(wstr ptr ptr ptr)
+107 stub MsiProvideComponentW
+108 stdcall MsiProvideQualifiedComponentA(str str long ptr ptr)
+109 stdcall MsiProvideQualifiedComponentW(wstr wstr long ptr ptr)
+110 stdcall MsiQueryFeatureStateA(str str)
+111 stdcall MsiQueryFeatureStateW(wstr wstr)
+112 stdcall MsiQueryProductStateA(str)
+113 stdcall MsiQueryProductStateW(wstr)
+114 stdcall MsiRecordDataSize(long long)
+115 stdcall MsiRecordGetFieldCount(long)
+116 stdcall MsiRecordGetInteger(long long)
+117 stdcall MsiRecordGetStringA(long long ptr ptr)
+118 stdcall MsiRecordGetStringW(long long ptr ptr)
+119 stdcall MsiRecordIsNull(long long)
+120 stdcall MsiRecordReadStream(long long ptr ptr)
+121 stdcall MsiRecordSetInteger(long long long)
+122 stdcall MsiRecordSetStreamA(long long str)
+123 stdcall MsiRecordSetStreamW(long long wstr)
+124 stdcall MsiRecordSetStringA(long long str)
+125 stdcall MsiRecordSetStringW(long long wstr)
+126 stdcall MsiReinstallFeatureA(str str long)
+127 stub MsiReinstallFeatureFromDescriptorA
+128 stub MsiReinstallFeatureFromDescriptorW
+129 stdcall MsiReinstallFeatureW(wstr wstr long)
+130 stdcall MsiReinstallProductA(str long)
+131 stdcall MsiReinstallProductW(wstr long)
+132 stdcall MsiSequenceA(long str long)
+133 stdcall MsiSequenceW(long wstr long)
+134 stdcall MsiSetComponentStateA(long str long)
+135 stdcall MsiSetComponentStateW(long wstr long)
+136 stdcall MsiSetExternalUIA(ptr long ptr)
+137 stdcall MsiSetExternalUIW(ptr long ptr)
+138 stdcall MsiSetFeatureStateA(long str long)
+139 stdcall MsiSetFeatureStateW(long wstr long)
+140 stub MsiSetInstallLevel
+141 stdcall MsiSetInternalUI(long ptr)
+142 stub MsiVerifyDiskSpace
+143 stdcall MsiSetMode(long long long)
+144 stdcall MsiSetPropertyA(long str str)
+145 stdcall MsiSetPropertyW(long wstr wstr)
+146 stdcall MsiSetTargetPathA(long str str)
+147 stdcall MsiSetTargetPathW(long wstr wstr)
+148 stdcall MsiSummaryInfoGetPropertyA(long long ptr ptr ptr ptr ptr)
+149 stdcall MsiSummaryInfoGetPropertyCount(long ptr)
+150 stdcall MsiSummaryInfoGetPropertyW(long long ptr ptr ptr ptr ptr)
+151 stdcall MsiSummaryInfoPersist(long)
+152 stdcall MsiSummaryInfoSetPropertyA(long long long long ptr str)
+153 stdcall MsiSummaryInfoSetPropertyW(long long long long ptr wstr)
+154 stdcall MsiUseFeatureA(str str)
+155 stdcall MsiUseFeatureW(wstr wstr)
+156 stdcall MsiVerifyPackageA(str)
+157 stdcall MsiVerifyPackageW(wstr)
+158 stdcall MsiViewClose(long)
+159 stdcall MsiViewExecute(long long)
+160 stdcall MsiViewFetch(long ptr)
+161 stdcall MsiViewGetErrorA(long ptr ptr)
+162 stdcall MsiViewGetErrorW(long ptr ptr)
+163 stdcall MsiViewModify(long long long)
+164 stdcall MsiDatabaseIsTablePersistentA(long str)
+165 stdcall MsiDatabaseIsTablePersistentW(long wstr)
+166 stdcall MsiViewGetColumnInfo(long long ptr)
+167 stdcall MsiRecordClearData(long)
+168 stdcall MsiEnableLogA(long str long)
+169 stdcall MsiEnableLogW(long wstr long)
+170 stdcall MsiFormatRecordA(long long ptr ptr)
+171 stdcall MsiFormatRecordW(long long ptr ptr)
+172 stdcall MsiGetComponentPathA(str str ptr ptr)
+173 stdcall MsiGetComponentPathW(wstr wstr ptr ptr)
+174 stdcall MsiApplyPatchA(str str long str)
+175 stdcall MsiApplyPatchW(wstr wstr long wstr)
+176 stdcall MsiAdvertiseScriptA(str long ptr long)
+177 stdcall MsiAdvertiseScriptW(wstr long ptr long)
+178 stub MsiGetPatchInfoA
+179 stub MsiGetPatchInfoW
+180 stdcall MsiEnumPatchesA(str long ptr ptr ptr)
+181 stdcall MsiEnumPatchesW(str long ptr ptr ptr)
+182 stdcall -private DllGetVersion(ptr)
+183 stub MsiGetProductCodeFromPackageCodeA
+184 stub MsiGetProductCodeFromPackageCodeW
+185 stub MsiCreateTransformSummaryInfoA
+186 stub MsiCreateTransformSummaryInfoW
+187 stub MsiQueryFeatureStateFromDescriptorA
+188 stub MsiQueryFeatureStateFromDescriptorW
+189 stdcall MsiConfigureProductExA(str long long str)
+190 stdcall MsiConfigureProductExW(wstr long long wstr)
+191 stub MsiInvalidateFeatureCache
+192 stdcall MsiUseFeatureExA(str str long long)
+193 stdcall MsiUseFeatureExW(wstr wstr long long)
+194 stdcall MsiGetFileVersionA(str str ptr str ptr)
+195 stdcall MsiGetFileVersionW(wstr wstr ptr wstr ptr)
+196 stdcall MsiLoadStringA(long long long long long)
+197 stdcall MsiLoadStringW(long long long long long)
+198 stdcall MsiMessageBoxA(long long long long long long)
+199 stdcall MsiMessageBoxW(long long long long long long)
+200 stdcall MsiDecomposeDescriptorA(str ptr ptr ptr ptr)
+201 stdcall MsiDecomposeDescriptorW(wstr ptr ptr ptr ptr)
+202 stub MsiProvideQualifiedComponentExA
+203 stdcall MsiProvideQualifiedComponentExW(wstr wstr long wstr long long ptr ptr)
+204 stdcall MsiEnumRelatedProductsA(str long long ptr)
+205 stdcall MsiEnumRelatedProductsW(wstr long long ptr)
+206 stub MsiSetFeatureAttributesA
+207 stub MsiSetFeatureAttributesW
+208 stub MsiSourceListClearAllA
+209 stub MsiSourceListClearAllW
+210 stub MsiSourceListAddSourceA
+211 stub MsiSourceListAddSourceW
+212 stub MsiSourceListForceResolutionA
+213 stub MsiSourceListForceResolutionW
+214 stub MsiIsProductElevatedA
+215 stub MsiIsProductElevatedW
+216 stdcall MsiGetShortcutTargetA(str ptr ptr ptr)
+217 stdcall MsiGetShortcutTargetW(wstr ptr ptr ptr)
+218 stdcall MsiGetFileHashA(str long ptr)
+219 stdcall MsiGetFileHashW(wstr long ptr)
+220 stub MsiEnumComponentCostsA
+221 stub MsiEnumComponentCostsW
+222 stdcall MsiCreateAndVerifyInstallerDirectory(long)
+223 stdcall MsiGetFileSignatureInformationA(str long ptr ptr ptr)
+224 stdcall MsiGetFileSignatureInformationW(wstr long ptr ptr ptr)
+225 stdcall MsiProvideAssemblyA(str str long long str ptr)
+226 stdcall MsiProvideAssemblyW(wstr wstr long long wstr ptr)
+227 stdcall MsiAdvertiseProductExA(str str str long long long)
+228 stdcall MsiAdvertiseProductExW(wstr wstr wstr long long long)
+229 stub MsiNotifySidChangeA
+230 stub MsiNotifySidChangeW
+231 stdcall MsiOpenPackageExA(str long ptr)
+232 stdcall MsiOpenPackageExW(wstr long ptr)
+233 stub MsiDeleteUserDataA
+234 stub MsiDeleteUserDataW
+235 stub Migrate10CachedPackagesA
+236 stub Migrate10CachedPackagesW
+
+@ stdcall -private DllCanUnloadNow()
+@ stdcall -private DllGetClassObject(ptr ptr ptr)
+@ stdcall -private DllRegisterServer()
+@ stdcall -private DllUnregisterServer()
diff --git a/reactos/dll/msi/msi.xml b/reactos/dll/msi/msi.xml
new file mode 100644 (file)
index 0000000..3e745ad
--- /dev/null
@@ -0,0 +1,62 @@
+<module name="msi" type="win32dll" baseaddress="${BASEADDRESS_MSI}" installbase="system32" installname="msi.dll" allowwarnings="true">
+       <autoregister infsection="OleControlDlls" type="DllRegisterServer" />
+       <importlibrary definition="msi.spec.def" />
+       <include base="msi">.</include>
+       <include base="ReactOS">include/wine</include>
+       <define name="__REACTOS__" />
+       <define name="__USE_W32API" />
+       <define name="_WIN32_IE">0x600</define>
+       <define name="_WIN32_WINNT">0x501</define>
+       <define name="WINVER">0x501</define>
+       <library>wine</library>
+       <library>uuid</library>
+       <library>ntdll</library>
+       <library>kernel32</library>
+       <library>user32</library>
+       <library>gdi32</library>
+       <library>advapi32</library>
+       <library>shell32</library>
+       <library>shlwapi</library>
+       <library>winmm</library>
+       <library>cabinet</library>
+       <library>ole32</library>
+       <library>oleaut32</library>
+       <library>version</library>
+       <file>action.c</file>
+       <file>appsearch.c</file>
+       <file>classes.c</file>
+       <file>cond.tab.c</file>
+       <file>create.c</file>
+       <file>custom.c</file>
+       <file>database.c</file>
+       <file>delete.c</file>
+       <file>dialog.c</file>
+       <file>distinct.c</file>
+       <file>events.c</file>
+       <file>files.c</file>
+       <file>format.c</file>
+       <file>handle.c</file>
+       <file>helpers.c</file>
+       <file>insert.c</file>
+       <file>install.c</file>
+       <file>msi.c</file>
+       <file>msiquery.c</file>
+       <file>order.c</file>
+       <file>package.c</file>
+       <file>preview.c</file>
+       <file>record.c</file>
+       <file>registry.c</file>
+       <file>regsvr.c</file>
+       <file>select.c</file>
+       <file>source.c</file>
+       <file>sql.tab.c</file>
+       <file>string.c</file>
+       <file>suminfo.c</file>
+       <file>table.c</file>
+       <file>tokenize.c</file>
+       <file>update.c</file>
+       <file>upgrade.c</file>
+       <file>where.c</file>
+       <file>msi.spec</file>
+       <file>msi.rc</file>
+</module>
diff --git a/reactos/dll/msi/msi_Bg.rc b/reactos/dll/msi/msi_Bg.rc
new file mode 100644 (file)
index 0000000..8a0ead5
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Bulgarian resources for MSI
+ *
+ * Copyright 2005 Milko Krachounov
+ *
+ * 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
+ */
+
+LANGUAGE LANG_BULGARIAN, SUBLANG_DEFAULT
+
+STRINGTABLE DISCARDABLE
+{
+       5 "ïúòÿò %s íå å íàìåðåí"
+       9 "ïîñòàâåòå äèñê %s"
+       10 "íåêîðåêòíè ïàðàìåòðè"
+       11 "âúâåäåòå ïàïêàòà, êîÿòî ñúäúðæà %s"
+       12 "èçòî÷íèêà çà èíñòàëàöèÿ íà ôóíêöèîíàëíîñòòà ëèïñâà"
+       13 "ìðåæîâîòî óñòðîéñòâà íóæíî çà ôóíêöèîíàëíîñòòà ëèïñâà "
+       14 "ôóíêöèîíàëíîñò îò:"
+       15 "èçáåðåòå ïàïêàòà, êîÿòî ñúäúðæà %s"
+}
diff --git a/reactos/dll/msi/msi_De.rc b/reactos/dll/msi/msi_De.rc
new file mode 100644 (file)
index 0000000..0b15276
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * German resources for MSI
+ *
+ * Copyright 2005 Henning Gerhardt
+ *
+ * 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
+ */
+
+LANGUAGE LANG_GERMAN, SUBLANG_DEFAULT
+
+STRINGTABLE DISCARDABLE
+{
+       5 "Der Pfad %s wurde nicht gefunden."
+       9 "Bitte Disk %s einlegen."
+       10 "schlechte Parameter"
+       11 "Geben Sie das Verzeichnis ein, dass %s enthält."
+       12 "Die Installationsquelle für das Feature fehlt."
+       13 "Das Netzwerklaufwerk für das Feature fehlt."
+       14 "Feature von:"
+       15 "Wählen Sie das Verzeichnis aus, dass %s enthält."
+}
diff --git a/reactos/dll/msi/msi_En.rc b/reactos/dll/msi/msi_En.rc
new file mode 100644 (file)
index 0000000..f3330c8
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * English resources for MSI
+ *
+ * Copyright 2005 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
+ */
+
+LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT
+
+STRINGTABLE DISCARDABLE
+{
+       5 "path %s not found"
+       9 "insert disk %s"
+       10 "bad parameters"
+       11 "enter which folder contains %s"
+       12 "install source for feature missing"
+       13 "network drive for feature missing"
+       14 "feature from:"
+       15 "choose which folder contains %s"
+}
diff --git a/reactos/dll/msi/msi_Es.rc b/reactos/dll/msi/msi_Es.rc
new file mode 100644 (file)
index 0000000..3d36ff7
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Spanish resources for MSI
+ *
+ * Copyright 2005 José Manuel Ferrer Ortiz
+ *
+ * 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
+ */
+
+LANGUAGE LANG_SPANISH, SUBLANG_NEUTRAL
+
+STRINGTABLE DISCARDABLE
+{
+       5 "ruta %s no encontrada"
+       9 "inserte el disco %s"
+       10 "parámetros incorrectos"
+       11 "introduzca qué carpeta contiene %s"
+       12 "instalar fuente para característica ausente"
+       13 "unidad de red para característica ausente"
+       14 "característica de:"
+       15 "elija qué carpeta contiene %s"
+}
diff --git a/reactos/dll/msi/msi_Fi.rc b/reactos/dll/msi/msi_Fi.rc
new file mode 100644 (file)
index 0000000..5de0be8
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Finnish resources for MSI
+ *
+ * Copyright 2005 Kimmo Myllyvirta
+ *
+ * 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
+ */
+
+LANGUAGE LANG_FINNISH, SUBLANG_DEFAULT
+
+STRINGTABLE DISCARDABLE
+{
+       5 "Polkua %s ei löydy."
+       9 "Anna levy %s"
+       10 "Virheelliset parametrit."
+       11 "Anna kansio, joka sisältää %s"
+       12 "Ominaisuuden asennuslähde puuttuu."
+       13 "Ominaisuuden verkkolevy puuttuu."
+       14 "Ominaisuus:"
+       15 "Valitse kansio, joka sisältää %s"
+}
diff --git a/reactos/dll/msi/msi_Fr.rc b/reactos/dll/msi/msi_Fr.rc
new file mode 100644 (file)
index 0000000..6ed1034
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * French resources for MSI
+ *
+ * Copyright 2005 Jonathan Ernst
+ *
+ * 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
+ */
+
+LANGUAGE LANG_FRENCH, SUBLANG_NEUTRAL
+
+STRINGTABLE DISCARDABLE
+{
+       5 "Le chemin %s est introuvable"
+       9 "insérez le disque %s"
+       10 "mauvais paramètres"
+       11 "saisissez le nom du dossier contenant %s"
+       12 "source d'installation pour la fonctionnalité manquante"
+       13 "lecteur réseau pour la fonctionnalité manquant"
+       14 "fonctionnalité depuis:"
+       15 "sélectionnez le dossier contenant %s"
+}
diff --git a/reactos/dll/msi/msi_Hu.rc b/reactos/dll/msi/msi_Hu.rc
new file mode 100644 (file)
index 0000000..7532831
--- /dev/null
@@ -0,0 +1,34 @@
+/*\r
+ * Hungarian resources for MSI\r
+ *\r
+ * Copyright 2005 Mike McCormack\r
+ * Copyright 2005 Gergely Risko\r
+ *\r
+ * This library is free software; you can redistribute it and/or\r
+ * modify it under the terms of the GNU Lesser General Public\r
+ * License as published by the Free Software Foundation; either\r
+ * version 2.1 of the License, or (at your option) any later version.\r
+ *\r
+ * This library is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+ * Lesser General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU Lesser General Public\r
+ * License along with this library; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+ */\r
+\r
+LANGUAGE LANG_HUNGARIAN, SUBLANG_NEUTRAL\r
+\r
+STRINGTABLE DISCARDABLE\r
+{\r
+       5 "Az elérési út: %s nem található"\r
+       9 "Tedd be a következõ lemezt: %s"\r
+       10 "Rossz paraméterek"\r
+       11 "Add meg melyik mappa tartalmazza a következõt: %s"\r
+       12 "Az összetevõ forrása hiányzik"\r
+       13 "Hiányzik a hálózati meghajtó az össszetevõhöz"\r
+/* NOT TRANSLATED */   14 "feature from:"\r
+       15 "Válaszd ki melyik mappa tartalmazza a következõt: %s"\r
+}\r
diff --git a/reactos/dll/msi/msi_Ja.rc b/reactos/dll/msi/msi_Ja.rc
new file mode 100644 (file)
index 0000000..84424c2
--- /dev/null
@@ -0,0 +1,13 @@
+LANGUAGE LANG_JAPANESE, SUBLANG_NEUTRAL
+
+STRINGTABLE DISCARDABLE
+{
+       5 "\83p\83X %s \82ª\82Ý\82Â\82©\82è\82Ü\82¹\82ñ\82Å\82µ\82½"
+       9 "\83f\83B\83X\83N %s \82ð\91}\93ü\82µ\82Ä\82­\82¾\82³\82¢"
+       10 "\96³\8cø\82È\83p\83\89\83\81\81[\83^\82Å\82·"
+       11 "%s \82Ì\82 \82é\83t\83H\83\8b\83_\82ð\93ü\97Í\82µ\82Ä\82­\82¾\82³\82¢"
+       12 "\8b@\94\\\82Ì\83C\83\93\83X\83g\81[\83\8b\8c³\82ª\82 \82è\82Ü\82¹\82ñ"
+       13 "\8b@\94\\\82ª\82 \82é\83l\83b\83g\83\8f\81[\83\83h\83\89\83C\83u\82ª\82 \82è\82Ü\82¹\82ñ"
+       14 "\8b@\94\\\82Ì\8fê\8f\8a:"
+       15 "%s \82Ì\82 \82é\83t\83H\83\8b\83_\82ð\91I\91ð\82µ\82Ä\82­\82¾\82³\82¢"
+}
diff --git a/reactos/dll/msi/msi_Ko.rc b/reactos/dll/msi/msi_Ko.rc
new file mode 100644 (file)
index 0000000..fb4be6b
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Korean resources for MSI
+ *
+ * Copyright 2005 YunSong Hwang
+ *
+ * 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
+ */
+
+LANGUAGE LANG_KOREAN, SUBLANG_DEFAULT
+
+STRINGTABLE DISCARDABLE
+{
+       5 "%s  °æ·Î¸¦ Ã£À»¼ö ¾ø½À´Ï´Ù"
+       9 "µð½ºÅ© %s »ðÀÔ"
+       10 "Àý¸øµÈ ¸Å°³º¯¼ö"
+       11 "%s¸¦ Æ÷ÇÔÇϴ Æú´õ¸¦ ÀÔ·ÂÇϼ¼¿©"
+       12 "ºüÁø ºÎºÐ(feature)À» À§ÇÑ ¼³Ä¡ ¿øº»"
+       13 "ºüÁø ºÎºÐ(feature)À» À§ÇÑ ³×Æ®¿öÅ© µå¶óÀ̺ê"
+       14 "ºÎºÐ(feature)¿¡¼­:"
+       15 " %s¸¦ Æ÷ÇÔÇϴ Æú´õ ¼±ÅÃ"
+}
diff --git a/reactos/dll/msi/msi_Nl.rc b/reactos/dll/msi/msi_Nl.rc
new file mode 100644 (file)
index 0000000..aff08e7
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Durch resources for MSI
+ *
+ * Copyright 2005 Hans Leidekker
+ *
+ * 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
+ */
+
+LANGUAGE LANG_DUTCH, SUBLANG_DEFAULT
+
+STRINGTABLE DISCARDABLE
+{
+       5 "Pad %s niet gevonden"
+       9 "Plaats disk %s"
+       10 "Ongeldige parameters"
+       11 "Voer de map in die %s bevat"
+       12 "De installatiebron van het feature ontbreekt"
+       13 "De netwerkschijf met het feature ontbreekt"
+       14 "Feature van:"
+       15 "Kies de map die %s bevat"
+}
diff --git a/reactos/dll/msi/msi_No.rc b/reactos/dll/msi/msi_No.rc
new file mode 100644 (file)
index 0000000..fbed9fb
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Norwegian Bokmål resources for MSI
+ *
+ * Copyright 2005 Alexander N. Sørnes <alex@thehandofagony.com>
+ *
+ * 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
+ */
+
+LANGUAGE LANG_NORWEGIAN, SUBLANG_NORWEGIAN_BOKMAL
+
+STRINGTABLE DISCARDABLE
+{
+       5 "Fant ikke stien '%s'."
+       9 "Sett i disk '%s'"
+       10 "Gale parametere."
+       11 "Oppgi katalogen som inneholder '%s'."
+       12 "Egenskapens installasjonskilde mangler."
+       13 "Egenskapens nettverksstasjon mangler."
+       14 "Egenskap fra::"
+       15 "Velg katalogen som inneholder '%s'."
+}
diff --git a/reactos/dll/msi/msi_Pt.rc b/reactos/dll/msi/msi_Pt.rc
new file mode 100644 (file)
index 0000000..aa87ad2
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Portuguese resources for MSI
+ *
+ * Copyright 2005 Marcelo Duarte
+ *
+ * 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
+ */
+
+LANGUAGE LANG_PORTUGUESE, SUBLANG_DEFAULT
+
+STRINGTABLE DISCARDABLE
+{
+       5 "caminho %s não encontrado"
+       9 "insira disco %s"
+       10 "parâmetros inválidos"
+       11 "entre qual pasta contém %s"
+       12 "instalar fonte para característica faltando"
+       13 "drive de rede para  característica faltando"
+       14 "característica de:"
+       15 "escolha qual pasta contém %s"
+}
diff --git a/reactos/dll/msi/msi_Ru.rc b/reactos/dll/msi/msi_Ru.rc
new file mode 100644 (file)
index 0000000..b9f41d7
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Russian resources for MSI
+ *
+ * Copyright 2005 Mikhail Y. Zvyozdochkin
+ *                Aleksey Bragin
+ *
+ * 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
+ */
+
+LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT
+
+STRINGTABLE DISCARDABLE
+{
+       5 "ïóòü %s íå íàéäåí"
+       9 "âñòàâüòå äèñê %s"
+       10 "íåâåðíûå ïàðàìåòðû"
+       11 "ââåäèòå, êàêàÿ ïàïêà ñîäåðæèò %s"
+       12 "íåäîñòóïåí èñòî÷íèê óñòàíîâêè (ñúåìíûé èëè êîìïàêò-äèñê íå âñòàâëåí â äèñêîâîä)"
+       13 "íåäîñòóïåí ñåòåâîé äèñê, ñîäåðæàùèé íåîáõîäèìûé ôàéë"
+       14 "ôóíêöèîíàëüíîñòü èç:"
+       15 "âûáåðèòå, êàêàÿ ïàïêà ñîäåðæèò %s"
+}
diff --git a/reactos/dll/msi/msipriv.h b/reactos/dll/msi/msipriv.h
new file mode 100644 (file)
index 0000000..575d121
--- /dev/null
@@ -0,0 +1,507 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2002-2005 Mike McCormack for CodeWeavers
+ *
+ * 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
+ */
+
+#ifndef __WINE_MSI_PRIVATE__
+#define __WINE_MSI_PRIVATE__
+
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "msi.h"
+#include "msiquery.h"
+#include "objbase.h"
+#include "objidl.h"
+#include "winnls.h"
+#include "wine/list.h"
+
+#define MSI_DATASIZEMASK 0x00ff
+#define MSITYPE_VALID    0x0100
+#define MSITYPE_LOCALIZABLE 0x200
+#define MSITYPE_STRING   0x0800
+#define MSITYPE_NULLABLE 0x1000
+#define MSITYPE_KEY      0x2000
+
+#define MSITYPE_IS_BINARY(type) (((type) & ~MSITYPE_NULLABLE) == (MSITYPE_STRING|MSITYPE_VALID))
+
+struct tagMSITABLE;
+typedef struct tagMSITABLE MSITABLE;
+
+struct string_table;
+typedef struct string_table string_table;
+
+struct tagMSIOBJECTHDR;
+typedef struct tagMSIOBJECTHDR MSIOBJECTHDR;
+
+typedef VOID (*msihandledestructor)( MSIOBJECTHDR * );
+
+struct tagMSIOBJECTHDR
+{
+    UINT magic;
+    UINT type;
+    LONG refcount;
+    msihandledestructor destructor;
+};
+
+typedef struct tagMSIDATABASE
+{
+    MSIOBJECTHDR hdr;
+    IStorage *storage;
+    string_table *strings;
+    LPCWSTR mode;
+    struct list tables;
+    struct list transforms;
+} MSIDATABASE;
+
+typedef struct tagMSIVIEW MSIVIEW;
+
+typedef struct tagMSIQUERY
+{
+    MSIOBJECTHDR hdr;
+    MSIVIEW *view;
+    UINT row;
+    MSIDATABASE *db;
+    struct list mem;
+} MSIQUERY;
+
+/* maybe we can use a Variant instead of doing it ourselves? */
+typedef struct tagMSIFIELD
+{
+    UINT type;
+    union
+    {
+        INT iVal;
+        LPWSTR szwVal;
+        IStream *stream;
+    } u;
+} MSIFIELD;
+
+typedef struct tagMSIRECORD
+{
+    MSIOBJECTHDR hdr;
+    UINT count;       /* as passed to MsiCreateRecord */
+    MSIFIELD fields[1]; /* nb. array size is count+1 */
+} MSIRECORD;
+
+typedef struct tagMSIVIEWOPS
+{
+    /*
+     * fetch_int - reads one integer from {row,col} in the table
+     *
+     *  This function should be called after the execute method.
+     *  Data returned by the function should not change until 
+     *   close or delete is called.
+     *  To get a string value, query the database's string table with
+     *   the integer value returned from this function.
+     */
+    UINT (*fetch_int)( struct tagMSIVIEW *, UINT row, UINT col, UINT *val );
+
+    /*
+     * fetch_int - reads one integer from {row,col} in the table
+     *
+     *  This function is similar to fetch_int, except fetches a
+     *    stream instead of an integer.
+     */
+    UINT (*fetch_stream)( struct tagMSIVIEW *, UINT row, UINT col, IStream **stm );
+
+    /*
+     * get_int - sets one integer at {row,col} in the table
+     *
+     *  Similar semantics to fetch_int
+     */
+    UINT (*set_int)( struct tagMSIVIEW *, UINT row, UINT col, UINT val );
+
+    /*
+     * Inserts a new row into the database from the records contents
+     */
+    UINT (*insert_row)( struct tagMSIVIEW *, MSIRECORD * );
+
+    /*
+     * execute - loads the underlying data into memory so it can be read
+     */
+    UINT (*execute)( struct tagMSIVIEW *, MSIRECORD * );
+
+    /*
+     * close - clears the data read by execute from memory
+     */
+    UINT (*close)( struct tagMSIVIEW * );
+
+    /*
+     * get_dimensions - returns the number of rows or columns in a table.
+     *
+     *  The number of rows can only be queried after the execute method
+     *   is called. The number of columns can be queried at any time.
+     */
+    UINT (*get_dimensions)( struct tagMSIVIEW *, UINT *rows, UINT *cols );
+
+    /*
+     * get_column_info - returns the name and type of a specific column
+     *
+     *  The name is HeapAlloc'ed by this function and should be freed by
+     *   the caller.
+     *  The column information can be queried at any time.
+     */
+    UINT (*get_column_info)( struct tagMSIVIEW *, UINT n, LPWSTR *name, UINT *type );
+
+    /*
+     * modify - not yet implemented properly
+     */
+    UINT (*modify)( struct tagMSIVIEW *, MSIMODIFY, MSIRECORD * );
+
+    /*
+     * delete - destroys the structure completely
+     */
+    UINT (*delete)( struct tagMSIVIEW * );
+
+} MSIVIEWOPS;
+
+struct tagMSIVIEW
+{
+    MSIOBJECTHDR hdr;
+    MSIVIEWOPS   *ops;
+};
+
+struct msi_dialog_tag;
+typedef struct msi_dialog_tag msi_dialog;
+
+typedef struct tagMSIPACKAGE
+{
+    MSIOBJECTHDR hdr;
+    MSIDATABASE *db;
+    struct list components;
+    struct list features;
+    struct list files;
+    struct list tempfiles;
+    struct list folders;
+    LPWSTR ActionFormat;
+    LPWSTR LastAction;
+
+    struct list classes;
+    struct list extensions;
+    struct list progids;
+    struct list mimes;
+    struct list appids;
+    
+    struct tagMSISCRIPT *script;
+
+    struct list RunningActions;
+
+    LPWSTR PackagePath;
+    LPWSTR ProductCode;
+
+    UINT CurrentInstallState;
+    msi_dialog *dialog;
+    LPWSTR next_dialog;
+    
+    struct list subscriptions;
+} MSIPACKAGE;
+
+typedef struct tagMSIPREVIEW
+{
+    MSIOBJECTHDR hdr;
+    MSIPACKAGE *package;
+    msi_dialog *dialog;
+} MSIPREVIEW;
+
+#define MSI_MAX_PROPS 20
+
+typedef struct tagMSISUMMARYINFO
+{
+    MSIOBJECTHDR hdr;
+    MSIDATABASE *db;
+    DWORD update_count;
+    PROPVARIANT property[MSI_MAX_PROPS];
+} MSISUMMARYINFO;
+
+#define MSIHANDLETYPE_ANY 0
+#define MSIHANDLETYPE_DATABASE 1
+#define MSIHANDLETYPE_SUMMARYINFO 2
+#define MSIHANDLETYPE_VIEW 3
+#define MSIHANDLETYPE_RECORD 4
+#define MSIHANDLETYPE_PACKAGE 5
+#define MSIHANDLETYPE_PREVIEW 6
+
+#define MSI_MAJORVERSION 2
+#define MSI_MINORVERSION 0
+#define MSI_BUILDNUMBER 2600
+
+#define GUID_SIZE 39
+
+#define MSIHANDLE_MAGIC 0x4d434923
+#define MSIMAXHANDLES 0xf0
+
+DEFINE_GUID(CLSID_IMsiServer,   0x000C101C,0x0000,0x0000,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
+DEFINE_GUID(CLSID_IMsiServerX1, 0x000C103E,0x0000,0x0000,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
+DEFINE_GUID(CLSID_IMsiServerX2, 0x000C1090,0x0000,0x0000,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
+DEFINE_GUID(CLSID_IMsiServerX3, 0x000C1094,0x0000,0x0000,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
+
+DEFINE_GUID(CLSID_IMsiServerMessage, 0x000C101D,0x0000,0x0000,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
+
+/* handle unicode/ascii output in the Msi* API functions */
+typedef struct {
+    BOOL unicode;
+    union {
+       LPSTR a;
+       LPWSTR w;
+    } str;
+} awstring;
+
+typedef struct {
+    BOOL unicode;
+    union {
+       LPCSTR a;
+       LPCWSTR w;
+    } str;
+} awcstring;
+
+UINT msi_strcpy_to_awstring( LPCWSTR str, awstring *awbuf, DWORD *sz );
+
+/* handle functions */
+extern void *msihandle2msiinfo(MSIHANDLE handle, UINT type);
+extern MSIHANDLE alloc_msihandle( MSIOBJECTHDR * );
+extern void *alloc_msiobject(UINT type, UINT size, msihandledestructor destroy );
+extern void msiobj_addref(MSIOBJECTHDR *);
+extern int msiobj_release(MSIOBJECTHDR *);
+extern void msiobj_lock(MSIOBJECTHDR *);
+extern void msiobj_unlock(MSIOBJECTHDR *);
+
+extern void free_cached_tables( MSIDATABASE *db );
+extern void msi_free_transforms( MSIDATABASE *db );
+extern string_table *load_string_table( IStorage *stg );
+extern UINT MSI_CommitTables( MSIDATABASE *db );
+extern HRESULT init_string_table( IStorage *stg );
+
+
+/* string table functions */
+extern BOOL msi_addstring( string_table *st, UINT string_no, const CHAR *data, int len, UINT refcount );
+extern BOOL msi_addstringW( string_table *st, UINT string_no, const WCHAR *data, int len, UINT refcount );
+extern UINT msi_id2stringW( string_table *st, UINT string_no, LPWSTR buffer, UINT *sz );
+extern UINT msi_id2stringA( string_table *st, UINT string_no, LPSTR buffer, UINT *sz );
+
+extern LPWSTR MSI_makestring( MSIDATABASE *db, UINT stringid);
+extern UINT msi_string2idW( string_table *st, LPCWSTR buffer, UINT *id );
+extern UINT msi_string2idA( string_table *st, LPCSTR str, UINT *id );
+extern string_table *msi_init_stringtable( int entries, UINT codepage );
+extern VOID msi_destroy_stringtable( string_table *st );
+extern UINT msi_string_count( string_table *st );
+extern UINT msi_id_refcount( string_table *st, UINT i );
+extern UINT msi_string_totalsize( string_table *st, UINT *last );
+extern UINT msi_strcmp( string_table *st, UINT lval, UINT rval, UINT *res );
+extern const WCHAR *msi_string_lookup_id( string_table *st, UINT id );
+extern UINT msi_string_get_codepage( string_table *st );
+
+
+extern UINT VIEW_find_column( MSIVIEW *view, LPCWSTR name, UINT *n );
+
+extern BOOL TABLE_Exists( MSIDATABASE *db, LPWSTR name );
+
+extern UINT read_raw_stream_data( MSIDATABASE*, LPCWSTR stname,
+                              USHORT **pdata, UINT *psz );
+
+/* transform functions */
+extern UINT msi_table_apply_transform( MSIDATABASE *db, IStorage *stg );
+extern UINT MSI_DatabaseApplyTransformW( MSIDATABASE *db, 
+                 LPCWSTR szTransformFile, int iErrorCond );
+
+/* action internals */
+extern UINT MSI_InstallPackage( MSIPACKAGE *, LPCWSTR, LPCWSTR );
+extern void ACTION_free_package_structures( MSIPACKAGE* );
+extern UINT ACTION_DialogBox( MSIPACKAGE*, LPCWSTR);
+extern UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode );
+
+/* record internals */
+extern UINT MSI_RecordSetIStream( MSIRECORD *, unsigned int, IStream *);
+extern UINT MSI_RecordGetIStream( MSIRECORD *, unsigned int, IStream **);
+extern const WCHAR *MSI_RecordGetString( MSIRECORD *, unsigned int );
+extern MSIRECORD *MSI_CreateRecord( unsigned int );
+extern UINT MSI_RecordSetInteger( MSIRECORD *, unsigned int, int );
+extern UINT MSI_RecordSetStringW( MSIRECORD *, unsigned int, LPCWSTR );
+extern UINT MSI_RecordSetStringA( MSIRECORD *, unsigned int, LPCSTR );
+extern BOOL MSI_RecordIsNull( MSIRECORD *, unsigned int );
+extern UINT MSI_RecordGetStringW( MSIRECORD * , unsigned int, LPWSTR, DWORD *);
+extern UINT MSI_RecordGetStringA( MSIRECORD *, unsigned int, LPSTR, DWORD *);
+extern int MSI_RecordGetInteger( MSIRECORD *, unsigned int );
+extern UINT MSI_RecordReadStream( MSIRECORD *, unsigned int, char *, DWORD *);
+extern unsigned int MSI_RecordGetFieldCount( MSIRECORD *rec );
+extern UINT MSI_RecordSetStreamW( MSIRECORD *, unsigned int, LPCWSTR );
+extern UINT MSI_RecordSetStreamA( MSIRECORD *, unsigned int, LPCSTR );
+extern UINT MSI_RecordDataSize( MSIRECORD *, unsigned int );
+extern UINT MSI_RecordStreamToFile( MSIRECORD *, unsigned int, LPCWSTR );
+
+/* stream internals */
+extern UINT get_raw_stream( MSIHANDLE hdb, LPCWSTR stname, IStream **stm );
+extern UINT db_get_raw_stream( MSIDATABASE *db, LPCWSTR stname, IStream **stm );
+extern void enum_stream_names( IStorage *stg );
+
+/* database internals */
+extern UINT MSI_OpenDatabaseW( LPCWSTR, LPCWSTR, MSIDATABASE ** );
+extern UINT MSI_DatabaseOpenViewW(MSIDATABASE *, LPCWSTR, MSIQUERY ** );
+extern UINT MSI_OpenQuery( MSIDATABASE *, MSIQUERY **, LPCWSTR, ... );
+typedef UINT (*record_func)( MSIRECORD *, LPVOID );
+extern UINT MSI_IterateRecords( MSIQUERY *, DWORD *, record_func, LPVOID );
+extern MSIRECORD *MSI_QueryGetRecord( MSIDATABASE *db, LPCWSTR query, ... );
+extern UINT MSI_DatabaseImport( MSIDATABASE *, LPCWSTR, LPCWSTR );
+extern UINT MSI_DatabaseExport( MSIDATABASE *, LPCWSTR, LPCWSTR, LPCWSTR );
+extern UINT MSI_DatabaseGetPrimaryKeys( MSIDATABASE *, LPCWSTR, MSIRECORD ** );
+
+/* view internals */
+extern UINT MSI_ViewExecute( MSIQUERY*, MSIRECORD * );
+extern UINT MSI_ViewFetch( MSIQUERY*, MSIRECORD ** );
+extern UINT MSI_ViewClose( MSIQUERY* );
+
+/* package internals */
+extern MSIPACKAGE *MSI_CreatePackage( MSIDATABASE * );
+extern UINT MSI_OpenPackageW( LPCWSTR szPackage, MSIPACKAGE ** );
+extern UINT MSI_SetTargetPathW( MSIPACKAGE *, LPCWSTR, LPCWSTR );
+extern UINT MSI_SetPropertyW( MSIPACKAGE *, LPCWSTR, LPCWSTR );
+extern INT MSI_ProcessMessage( MSIPACKAGE *, INSTALLMESSAGE, MSIRECORD * );
+extern UINT MSI_GetPropertyW( MSIPACKAGE *, LPCWSTR, LPWSTR, DWORD * );
+extern UINT MSI_GetPropertyA(MSIPACKAGE *, LPCSTR, LPSTR, DWORD* );
+extern MSICONDITION MSI_EvaluateConditionW( MSIPACKAGE *, LPCWSTR );
+extern UINT MSI_GetComponentStateW( MSIPACKAGE *, LPWSTR, INSTALLSTATE *, INSTALLSTATE * );
+extern UINT MSI_GetFeatureStateW( MSIPACKAGE *, LPWSTR, INSTALLSTATE *, INSTALLSTATE * );
+extern UINT WINAPI MSI_SetFeatureStateW(MSIPACKAGE*, LPCWSTR, INSTALLSTATE );
+
+/* for deformating */
+extern UINT MSI_FormatRecordW( MSIPACKAGE *, MSIRECORD *, LPWSTR, DWORD * );
+extern UINT MSI_FormatRecordA( MSIPACKAGE *, MSIRECORD *, LPSTR, DWORD * );
+    
+/* registry data encoding/decoding functions */
+extern BOOL unsquash_guid(LPCWSTR in, LPWSTR out);
+extern BOOL squash_guid(LPCWSTR in, LPWSTR out);
+extern BOOL encode_base85_guid(GUID *,LPWSTR);
+extern BOOL decode_base85_guid(LPCWSTR,GUID*);
+extern UINT MSIREG_OpenUninstallKey(LPCWSTR szProduct, HKEY* key, BOOL create);
+extern UINT MSIREG_OpenUserProductsKey(LPCWSTR szProduct, HKEY* key, BOOL create);
+extern UINT MSIREG_OpenFeatures(HKEY* key);
+extern UINT MSIREG_OpenFeaturesKey(LPCWSTR szProduct, HKEY* key, BOOL create);
+extern UINT MSIREG_OpenComponents(HKEY* key);
+extern UINT MSIREG_OpenUserComponentsKey(LPCWSTR szComponent, HKEY* key, BOOL create);
+extern UINT MSIREG_OpenComponentsKey(LPCWSTR szComponent, HKEY* key, BOOL create);
+extern UINT MSIREG_OpenProductsKey(LPCWSTR szProduct, HKEY* key, BOOL create);
+extern UINT MSIREG_OpenUserFeaturesKey(LPCWSTR szProduct, HKEY* key, BOOL create);
+extern UINT MSIREG_OpenUserComponentsKey(LPCWSTR szComponent, HKEY* key, BOOL create);
+extern UINT MSIREG_OpenUpgradeCodesKey(LPCWSTR szProduct, HKEY* key, BOOL create);
+extern UINT MSIREG_OpenUserUpgradeCodesKey(LPCWSTR szProduct, HKEY* key, BOOL create);
+
+extern LONG msi_reg_set_val_str( HKEY hkey, LPCWSTR name, LPCWSTR value );
+extern LONG msi_reg_set_val_multi_str( HKEY hkey, LPCWSTR name, LPCWSTR value );
+extern LONG msi_reg_set_val_dword( HKEY hkey, LPCWSTR name, DWORD val );
+
+/* msi dialog interface */
+typedef UINT (*msi_dialog_event_handler)( MSIPACKAGE*, LPCWSTR, LPCWSTR, msi_dialog* );
+extern msi_dialog *msi_dialog_create( MSIPACKAGE*, LPCWSTR, msi_dialog_event_handler );
+extern UINT msi_dialog_run_message_loop( msi_dialog* );
+extern void msi_dialog_end_dialog( msi_dialog* );
+extern void msi_dialog_check_messages( HANDLE );
+extern void msi_dialog_do_preview( msi_dialog* );
+extern void msi_dialog_destroy( msi_dialog* );
+extern BOOL msi_dialog_register_class( void );
+extern void msi_dialog_unregister_class( void );
+extern void msi_dialog_handle_event( msi_dialog*, LPCWSTR, LPCWSTR, MSIRECORD * );
+extern UINT msi_dialog_reset( msi_dialog *dialog );
+
+/* preview */
+extern MSIPREVIEW *MSI_EnableUIPreview( MSIDATABASE * );
+extern UINT MSI_PreviewDialogW( MSIPREVIEW *, LPCWSTR );
+
+/* summary information */
+extern MSISUMMARYINFO *MSI_GetSummaryInformationW( MSIDATABASE *db, UINT uiUpdateCount );
+extern LPWSTR msi_suminfo_dup_string( MSISUMMARYINFO *si, UINT uiProperty );
+
+/* undocumented functions */
+UINT WINAPI MsiCreateAndVerifyInstallerDirectory( DWORD );
+UINT WINAPI MsiDecomposeDescriptorW( LPCWSTR, LPWSTR, LPWSTR, LPWSTR, DWORD * );
+UINT WINAPI MsiDecomposeDescriptorA( LPCSTR, LPSTR, LPSTR, LPSTR, DWORD * );
+LANGID WINAPI MsiLoadStringW( MSIHANDLE, UINT, LPWSTR, int, LANGID );
+LANGID WINAPI MsiLoadStringA( MSIHANDLE, UINT, LPSTR, int, LANGID );
+
+/* UI globals */
+extern INSTALLUILEVEL gUILevel;
+extern HWND gUIhwnd;
+extern INSTALLUI_HANDLERA gUIHandlerA;
+extern INSTALLUI_HANDLERW gUIHandlerW;
+extern DWORD gUIFilter;
+extern LPVOID gUIContext;
+extern WCHAR gszLogFile[MAX_PATH];
+
+/* memory allocation macro functions */
+static inline void *msi_alloc( size_t len )
+{
+    return HeapAlloc( GetProcessHeap(), 0, len );
+}
+
+static inline void *msi_alloc_zero( size_t len )
+{
+    return HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, len );
+}
+
+static inline void *msi_realloc( void *mem, size_t len )
+{
+    return HeapReAlloc( GetProcessHeap(), 0, mem, len );
+}
+
+static inline void *msi_realloc_zero( void *mem, size_t len )
+{
+    return HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, mem, len );
+}
+
+static inline BOOL msi_free( void *mem )
+{
+    return HeapFree( GetProcessHeap(), 0, mem );
+}
+
+inline static char *strdupWtoA( LPCWSTR str )
+{
+    LPSTR ret = NULL;
+    DWORD len;
+
+    if (!str) return ret;
+    len = WideCharToMultiByte( CP_ACP, 0, str, -1, NULL, 0, NULL, NULL);
+    ret = msi_alloc( len );
+    if (ret)
+        WideCharToMultiByte( CP_ACP, 0, str, -1, ret, len, NULL, NULL );
+    return ret;
+}
+
+inline static LPWSTR strdupAtoW( LPCSTR str )
+{
+    LPWSTR ret = NULL;
+    DWORD len;
+
+    if (!str) return ret;
+    len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
+    ret = msi_alloc( len * sizeof(WCHAR) );
+    if (ret)
+        MultiByteToWideChar( CP_ACP, 0, str, -1, ret, len );
+    return ret;
+}
+
+inline static LPWSTR strdupW( LPCWSTR src )
+{
+    LPWSTR dest;
+    if (!src) return NULL;
+    dest = msi_alloc( (lstrlenW(src)+1)*sizeof(WCHAR) );
+    if (dest)
+        lstrcpyW(dest, src);
+    return dest;
+}
+
+#endif /* __WINE_MSI_PRIVATE__ */
diff --git a/reactos/dll/msi/msiquery.c b/reactos/dll/msi/msiquery.c
new file mode 100644 (file)
index 0000000..4824d87
--- /dev/null
@@ -0,0 +1,868 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2002-2005 Mike McCormack for CodeWeavers
+ *
+ * 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 <stdarg.h>
+
+#define COBJMACROS
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "wine/debug.h"
+#include "wine/unicode.h"
+#include "msi.h"
+#include "msiquery.h"
+#include "objbase.h"
+#include "objidl.h"
+#include "msipriv.h"
+#include "winnls.h"
+
+#include "query.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+static void MSI_CloseView( MSIOBJECTHDR *arg )
+{
+    MSIQUERY *query = (MSIQUERY*) arg;
+    struct list *ptr, *t;
+
+    if( query->view && query->view->ops->delete )
+        query->view->ops->delete( query->view );
+    msiobj_release( &query->db->hdr );
+
+    LIST_FOR_EACH_SAFE( ptr, t, &query->mem )
+    {
+        msi_free( ptr );
+    }
+}
+
+UINT VIEW_find_column( MSIVIEW *table, LPCWSTR name, UINT *n )
+{
+    LPWSTR col_name;
+    UINT i, count, r;
+
+    r = table->ops->get_dimensions( table, NULL, &count );
+    if( r != ERROR_SUCCESS )
+        return r;
+
+    for( i=1; i<=count; i++ )
+    {
+        INT x;
+
+        col_name = NULL;
+        r = table->ops->get_column_info( table, i, &col_name, NULL );
+        if( r != ERROR_SUCCESS )
+            return r;
+        x = lstrcmpW( name, col_name );
+        msi_free( col_name );
+        if( !x )
+        {
+            *n = i;
+            return ERROR_SUCCESS;
+        }
+    }
+
+    return ERROR_INVALID_PARAMETER;
+}
+
+UINT WINAPI MsiDatabaseOpenViewA(MSIHANDLE hdb,
+              LPCSTR szQuery, MSIHANDLE *phView)
+{
+    UINT r;
+    LPWSTR szwQuery;
+
+    TRACE("%ld %s %p\n", hdb, debugstr_a(szQuery), phView);
+
+    if( szQuery )
+    {
+        szwQuery = strdupAtoW( szQuery );
+        if( !szwQuery )
+            return ERROR_FUNCTION_FAILED;
+    }
+    else
+        szwQuery = NULL;
+
+    r = MsiDatabaseOpenViewW( hdb, szwQuery, phView);
+
+    msi_free( szwQuery );
+    return r;
+}
+
+UINT MSI_DatabaseOpenViewW(MSIDATABASE *db,
+              LPCWSTR szQuery, MSIQUERY **pView)
+{
+    MSIQUERY *query;
+    UINT r;
+
+    TRACE("%s %p\n", debugstr_w(szQuery), pView);
+
+    if( !szQuery)
+        return ERROR_INVALID_PARAMETER;
+
+    /* pre allocate a handle to hold a pointer to the view */
+    query = alloc_msiobject( MSIHANDLETYPE_VIEW, sizeof (MSIQUERY),
+                              MSI_CloseView );
+    if( !query )
+        return ERROR_FUNCTION_FAILED;
+
+    msiobj_addref( &db->hdr );
+    query->row = 0;
+    query->db = db;
+    query->view = NULL;
+    list_init( &query->mem );
+
+    r = MSI_ParseSQL( db, szQuery, &query->view, &query->mem );
+    if( r == ERROR_SUCCESS )
+    {
+        msiobj_addref( &query->hdr );
+        *pView = query;
+    }
+
+    msiobj_release( &query->hdr );
+    return r;
+}
+
+UINT MSI_OpenQuery( MSIDATABASE *db, MSIQUERY **view, LPCWSTR fmt, ... )
+{
+    UINT r;
+    int size = 100, res;
+    LPWSTR query;
+
+    /* construct the string */
+    for (;;)
+    {
+        va_list va;
+        query = msi_alloc( size*sizeof(WCHAR) );
+        va_start(va, fmt);
+        res = vsnprintfW(query, size, fmt, va);
+        va_end(va);
+        if (res == -1) size *= 2;
+        else if (res >= size) size = res + 1;
+        else break;
+        msi_free( query );
+    }
+    /* perform the query */
+    r = MSI_DatabaseOpenViewW(db, query, view);
+    msi_free(query);
+    return r;
+}
+
+UINT MSI_IterateRecords( MSIQUERY *view, DWORD *count,
+                         record_func func, LPVOID param )
+{
+    MSIRECORD *rec = NULL;
+    UINT r, n = 0, max = 0;
+
+    r = MSI_ViewExecute( view, NULL );
+    if( r != ERROR_SUCCESS )
+        return r;
+
+    if( count )
+        max = *count;
+
+    /* iterate a query */
+    for( n = 0; (max == 0) || (n < max); n++ )
+    {
+        r = MSI_ViewFetch( view, &rec );
+        if( r != ERROR_SUCCESS )
+            break;
+        if (func)
+            r = func( rec, param );
+        msiobj_release( &rec->hdr );
+        if( r != ERROR_SUCCESS )
+            break;
+    }
+
+    MSI_ViewClose( view );
+
+    if( count )
+        *count = n;
+
+    if( r == ERROR_NO_MORE_ITEMS )
+        r = ERROR_SUCCESS;
+
+    return r;
+}
+
+/* return a single record from a query */
+MSIRECORD *MSI_QueryGetRecord( MSIDATABASE *db, LPCWSTR fmt, ... )
+{
+    MSIRECORD *rec = NULL;
+    MSIQUERY *view = NULL;
+    UINT r;
+    int size = 100, res;
+    LPWSTR query;
+
+    /* construct the string */
+    for (;;)
+    {
+        va_list va;
+        query = msi_alloc( size*sizeof(WCHAR) );
+        va_start(va, fmt);
+        res = vsnprintfW(query, size, fmt, va);
+        va_end(va);
+        if (res == -1) size *= 2;
+        else if (res >= size) size = res + 1;
+        else break;
+        msi_free( query );
+    }
+    /* perform the query */
+    r = MSI_DatabaseOpenViewW(db, query, &view);
+    msi_free(query);
+
+    if( r == ERROR_SUCCESS )
+    {
+        MSI_ViewExecute( view, NULL );
+        MSI_ViewFetch( view, &rec );
+        MSI_ViewClose( view );
+        msiobj_release( &view->hdr );
+    }
+    return rec;
+}
+
+UINT WINAPI MsiDatabaseOpenViewW(MSIHANDLE hdb,
+              LPCWSTR szQuery, MSIHANDLE *phView)
+{
+    MSIDATABASE *db;
+    MSIQUERY *query = NULL;
+    UINT ret;
+
+    TRACE("%s %p\n", debugstr_w(szQuery), phView);
+
+    db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
+    if( !db )
+        return ERROR_INVALID_HANDLE;
+
+    ret = MSI_DatabaseOpenViewW( db, szQuery, &query );
+    if( ret == ERROR_SUCCESS )
+    {
+        *phView = alloc_msihandle( &query->hdr );
+        msiobj_release( &query->hdr );
+    }
+    msiobj_release( &db->hdr );
+
+    return ret;
+}
+
+UINT MSI_ViewFetch(MSIQUERY *query, MSIRECORD **prec)
+{
+    MSIVIEW *view;
+    MSIRECORD *rec;
+    UINT row_count = 0, col_count = 0, i, ival, ret, type;
+
+    TRACE("%p %p\n", query, prec );
+
+    view = query->view;
+    if( !view )
+        return ERROR_FUNCTION_FAILED;
+
+    ret = view->ops->get_dimensions( view, &row_count, &col_count );
+    if( ret )
+        return ret;
+    if( !col_count )
+        return ERROR_INVALID_PARAMETER;
+
+    if( query->row >= row_count )
+        return ERROR_NO_MORE_ITEMS;
+
+    rec = MSI_CreateRecord( col_count );
+    if( !rec )
+        return ERROR_FUNCTION_FAILED;
+
+    for( i=1; i<=col_count; i++ )
+    {
+        ret = view->ops->get_column_info( view, i, NULL, &type );
+        if( ret )
+        {
+            ERR("Error getting column type for %d\n", i );
+            continue;
+        }
+        if (!MSITYPE_IS_BINARY(type))
+        {
+            ret = view->ops->fetch_int( view, query->row, i, &ival );
+            if( ret )
+            {
+                ERR("Error fetching data for %d\n", i );
+                continue;
+            }
+            if( ! (type & MSITYPE_VALID ) )
+                ERR("Invalid type!\n");
+
+            /* check if it's nul (0) - if so, don't set anything */
+            if( !ival )
+                continue;
+
+            if( type & MSITYPE_STRING )
+            {
+                LPWSTR sval;
+
+                sval = MSI_makestring( query->db, ival );
+                MSI_RecordSetStringW( rec, i, sval );
+                msi_free( sval );
+            }
+            else
+            {
+                if( (type & MSI_DATASIZEMASK) == 2 )
+                    MSI_RecordSetInteger( rec, i, ival - (1<<15) );
+                else
+                    MSI_RecordSetInteger( rec, i, ival - (1<<31) );
+            }
+        }
+        else
+        {
+            IStream *stm = NULL;
+
+            ret = view->ops->fetch_stream( view, query->row, i, &stm );
+            if( ( ret == ERROR_SUCCESS ) && stm )
+            {
+                MSI_RecordSetIStream( rec, i, stm );
+                IStream_Release( stm );
+            }
+            else
+                ERR("failed to get stream\n");
+        }
+    }
+    query->row ++;
+
+    *prec = rec;
+
+    return ERROR_SUCCESS;
+}
+
+UINT WINAPI MsiViewFetch(MSIHANDLE hView, MSIHANDLE *record)
+{
+    MSIQUERY *query;
+    MSIRECORD *rec = NULL;
+    UINT ret;
+
+    TRACE("%ld %p\n", hView, record);
+
+    query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
+    if( !query )
+        return ERROR_INVALID_HANDLE;
+    ret = MSI_ViewFetch( query, &rec );
+    if( ret == ERROR_SUCCESS )
+    {
+        *record = alloc_msihandle( &rec->hdr );
+        msiobj_release( &rec->hdr );
+    }
+    msiobj_release( &query->hdr );
+    return ret;
+}
+
+UINT MSI_ViewClose(MSIQUERY *query)
+{
+    MSIVIEW *view;
+
+    TRACE("%p\n", query );
+
+    view = query->view;
+    if( !view )
+        return ERROR_FUNCTION_FAILED;
+    if( !view->ops->close )
+        return ERROR_FUNCTION_FAILED;
+
+    return view->ops->close( view );
+}
+
+UINT WINAPI MsiViewClose(MSIHANDLE hView)
+{
+    MSIQUERY *query;
+    UINT ret;
+
+    TRACE("%ld\n", hView );
+
+    query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
+    if( !query )
+        return ERROR_INVALID_HANDLE;
+
+    ret = MSI_ViewClose( query );
+    msiobj_release( &query->hdr );
+    return ret;
+}
+
+UINT MSI_ViewExecute(MSIQUERY *query, MSIRECORD *rec )
+{
+    MSIVIEW *view;
+
+    TRACE("%p %p\n", query, rec);
+
+    view = query->view;
+    if( !view )
+        return ERROR_FUNCTION_FAILED;
+    if( !view->ops->execute )
+        return ERROR_FUNCTION_FAILED;
+    query->row = 0;
+
+    return view->ops->execute( view, rec );
+}
+
+UINT WINAPI MsiViewExecute(MSIHANDLE hView, MSIHANDLE hRec)
+{
+    MSIQUERY *query;
+    MSIRECORD *rec = NULL;
+    UINT ret;
+    
+    TRACE("%ld %ld\n", hView, hRec);
+
+    query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
+    if( !query )
+        return ERROR_INVALID_HANDLE;
+
+    if( hRec )
+    {
+        rec = msihandle2msiinfo( hRec, MSIHANDLETYPE_RECORD );
+        if( !rec )
+        {
+            ret = ERROR_INVALID_HANDLE;
+            goto out;
+        }
+    }
+
+    msiobj_lock( &rec->hdr );
+    ret = MSI_ViewExecute( query, rec );
+    msiobj_unlock( &rec->hdr );
+
+out:
+    msiobj_release( &query->hdr );
+    if( rec )
+        msiobj_release( &rec->hdr );
+
+    return ret;
+}
+
+static UINT msi_set_record_type_string( MSIRECORD *rec, UINT field, UINT type )
+{
+    static const WCHAR fmt[] = { '%','d',0 };
+    WCHAR szType[0x10];
+
+    if (MSITYPE_IS_BINARY(type))
+        szType[0] = 'v';
+    else if (type & MSITYPE_LOCALIZABLE)
+        szType[0] = 'l';
+    else if (type & MSITYPE_STRING)
+        szType[0] = 's';
+    else
+        szType[0] = 'i';
+    if (type & MSITYPE_NULLABLE)
+        szType[0] &= ~0x20;
+
+    sprintfW( &szType[1], fmt, (type&0xff) );
+
+    TRACE("type %04x -> %s\n", type, debugstr_w(szType) );
+
+    return MSI_RecordSetStringW( rec, field, szType );
+}
+
+UINT WINAPI MsiViewGetColumnInfo(MSIHANDLE hView, MSICOLINFO info, MSIHANDLE *hRec)
+{
+    MSIVIEW *view = NULL;
+    MSIQUERY *query = NULL;
+    MSIRECORD *rec = NULL;
+    UINT r = ERROR_FUNCTION_FAILED, i, count = 0, type;
+    LPWSTR name;
+
+    TRACE("%ld %d %p\n", hView, info, hRec);
+
+    if( !hRec )
+        return ERROR_INVALID_PARAMETER;
+
+    if( info != MSICOLINFO_NAMES && info != MSICOLINFO_TYPES )
+        return ERROR_INVALID_PARAMETER;
+
+    query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
+    if( !query )
+        return ERROR_INVALID_HANDLE;
+
+    view = query->view;
+    if( !view )
+        goto out;
+
+    if( !view->ops->get_dimensions )
+        goto out;
+
+    r = view->ops->get_dimensions( view, NULL, &count );
+    if( r )
+        goto out;
+    if( !count )
+    {
+        r = ERROR_INVALID_PARAMETER;
+        goto out;
+    }
+
+    rec = MSI_CreateRecord( count );
+    if( !rec )
+    {
+        r = ERROR_FUNCTION_FAILED;
+        goto out;
+    }
+
+    for( i=0; i<count; i++ )
+    {
+        name = NULL;
+        r = view->ops->get_column_info( view, i+1, &name, &type );
+        if( r != ERROR_SUCCESS )
+            continue;
+        if (info == MSICOLINFO_NAMES)
+            MSI_RecordSetStringW( rec, i+1, name );
+        else
+            msi_set_record_type_string( rec, i+1, type);
+        msi_free( name );
+    }
+
+    *hRec = alloc_msihandle( &rec->hdr );
+
+out:
+    msiobj_release( &query->hdr );
+    if( rec )
+        msiobj_release( &rec->hdr );
+
+    return r;
+}
+
+UINT WINAPI MsiViewModify( MSIHANDLE hView, MSIMODIFY eModifyMode,
+                MSIHANDLE hRecord)
+{
+    MSIVIEW *view = NULL;
+    MSIQUERY *query = NULL;
+    MSIRECORD *rec = NULL;
+    UINT r = ERROR_FUNCTION_FAILED;
+
+    TRACE("%ld %x %ld\n", hView, eModifyMode, hRecord);
+
+    query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
+    if( !query )
+        return ERROR_INVALID_HANDLE;
+
+    view = query->view;
+    if( !view )
+        goto out;
+
+    if( !view->ops->modify )
+        goto out;
+
+    rec = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD );
+    if( !rec )
+    {
+        r = ERROR_INVALID_HANDLE;
+        goto out;
+    }
+
+    r = view->ops->modify( view, eModifyMode, rec );
+
+out:
+    msiobj_release( &query->hdr );
+    if( rec )
+        msiobj_release( &rec->hdr );
+
+    return r;
+}
+
+MSIDBERROR WINAPI MsiViewGetErrorW( MSIHANDLE handle, LPWSTR szColumnNameBuffer,
+                              DWORD *pcchBuf )
+{
+    MSIQUERY *query = NULL;
+    static const WCHAR szError[] = { 0 };
+    MSIDBERROR r = MSIDBERROR_NOERROR;
+    int len;
+
+    FIXME("%ld %p %p - returns empty error string\n",
+          handle, szColumnNameBuffer, pcchBuf );
+
+    if( !pcchBuf )
+        return MSIDBERROR_INVALIDARG;
+
+    query = msihandle2msiinfo( handle, MSIHANDLETYPE_VIEW );
+    if( !query )
+        return MSIDBERROR_INVALIDARG;
+
+    len = lstrlenW( szError );
+    if( szColumnNameBuffer )
+    {
+        if( *pcchBuf > len )
+            lstrcpyW( szColumnNameBuffer, szError );
+        else
+            r = MSIDBERROR_MOREDATA;
+    }
+    *pcchBuf = len;
+
+    msiobj_release( &query->hdr );
+    return r;
+}
+
+MSIDBERROR WINAPI MsiViewGetErrorA( MSIHANDLE handle, LPSTR szColumnNameBuffer,
+                              DWORD *pcchBuf )
+{
+    static const CHAR szError[] = { 0 };
+    MSIQUERY *query = NULL;
+    MSIDBERROR r = MSIDBERROR_NOERROR;
+    int len;
+
+    FIXME("%ld %p %p - returns empty error string\n",
+          handle, szColumnNameBuffer, pcchBuf );
+
+    if( !pcchBuf )
+        return MSIDBERROR_INVALIDARG;
+
+    query = msihandle2msiinfo( handle, MSIHANDLETYPE_VIEW );
+    if( !query )
+        return MSIDBERROR_INVALIDARG;
+
+    len = lstrlenA( szError );
+    if( szColumnNameBuffer )
+    {
+        if( *pcchBuf > len )
+            lstrcpyA( szColumnNameBuffer, szError );
+        else
+            r = MSIDBERROR_MOREDATA;
+    }
+    *pcchBuf = len;
+
+    msiobj_release( &query->hdr );
+    return r;
+}
+
+MSIHANDLE WINAPI MsiGetLastErrorRecord( void )
+{
+    FIXME("\n");
+    return 0;
+}
+
+DEFINE_GUID( CLSID_MsiTransform, 0x000c1082, 0x0000, 0x0000, 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
+
+UINT MSI_DatabaseApplyTransformW( MSIDATABASE *db, 
+                 LPCWSTR szTransformFile, int iErrorCond )
+{
+    UINT r;
+    IStorage *stg = NULL;
+    TRACE("%p %s %d\n", db, debugstr_w(szTransformFile), iErrorCond);
+
+    r = StgOpenStorage( szTransformFile, NULL,
+           STGM_DIRECT|STGM_READ|STGM_SHARE_DENY_WRITE, NULL, 0, &stg);
+    if( r )
+        return r;
+
+    if( TRACE_ON( msi ) )
+        enum_stream_names( stg );
+
+    r = msi_table_apply_transform( db, stg );
+
+    IStorage_Release( stg );
+
+    return r;
+}
+
+UINT WINAPI MsiDatabaseApplyTransformW( MSIHANDLE hdb, 
+                 LPCWSTR szTransformFile, int iErrorCond)
+{
+    MSIDATABASE *db;
+    UINT r;
+
+    db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
+    if( !db )
+        return ERROR_INVALID_HANDLE;
+
+    r = MSI_DatabaseApplyTransformW( db, szTransformFile, iErrorCond );
+    msiobj_release( &db->hdr );
+    return r;
+}
+
+UINT WINAPI MsiDatabaseApplyTransformA( MSIHANDLE hdb, 
+                 LPCSTR szTransformFile, int iErrorCond)
+{
+    LPWSTR wstr;
+    UINT ret;
+
+    TRACE("%ld %s %d\n", hdb, debugstr_a(szTransformFile), iErrorCond);
+
+    wstr = strdupAtoW( szTransformFile );
+    if( szTransformFile && !wstr )
+        return ERROR_NOT_ENOUGH_MEMORY;
+
+    ret = MsiDatabaseApplyTransformW( hdb, wstr, iErrorCond);
+
+    msi_free( wstr );
+
+    return ret;
+}
+
+UINT WINAPI MsiDatabaseGenerateTransformA( MSIHANDLE hdb, MSIHANDLE hdbref,
+                 LPCSTR szTransformFile, int iReserved1, int iReserved2 )
+{
+    FIXME("%ld %ld %s %d %d\n", hdb, hdbref, 
+           debugstr_a(szTransformFile), iReserved1, iReserved2);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiDatabaseGenerateTransformW( MSIHANDLE hdb, MSIHANDLE hdbref,
+                 LPCWSTR szTransformFile, int iReserved1, int iReserved2 )
+{
+    FIXME("%ld %ld %s %d %d\n", hdb, hdbref, 
+           debugstr_w(szTransformFile), iReserved1, iReserved2);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiDatabaseCommit( MSIHANDLE hdb )
+{
+    MSIDATABASE *db;
+    UINT r;
+
+    TRACE("%ld\n", hdb);
+
+    db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
+    if( !db )
+        return ERROR_INVALID_HANDLE;
+
+    /* FIXME: lock the database */
+
+    r = MSI_CommitTables( db );
+
+    /* FIXME: unlock the database */
+
+    msiobj_release( &db->hdr );
+
+    return r;
+}
+
+struct msi_primary_key_record_info
+{
+    DWORD n;
+    MSIRECORD *rec;
+};
+
+static UINT msi_primary_key_iterator( MSIRECORD *rec, LPVOID param )
+{
+    struct msi_primary_key_record_info *info = param;
+    LPCWSTR name;
+    DWORD type;
+
+    type = MSI_RecordGetInteger( rec, 4 );
+    if( type & MSITYPE_KEY )
+    {
+        info->n++;
+        if( info->rec )
+        {
+            name = MSI_RecordGetString( rec, 3 );
+            MSI_RecordSetStringW( info->rec, info->n, name );
+        }
+    }
+
+    return ERROR_SUCCESS;
+}
+
+UINT MSI_DatabaseGetPrimaryKeys( MSIDATABASE *db,
+                LPCWSTR table, MSIRECORD **prec )
+{
+    static const WCHAR sql[] = {
+        's','e','l','e','c','t',' ','*',' ',
+        'f','r','o','m',' ','`','_','C','o','l','u','m','n','s','`',' ',
+        'w','h','e','r','e',' ',
+        '`','T','a','b','l','e','`',' ','=',' ','\'','%','s','\'',0 };
+    struct msi_primary_key_record_info info;
+    MSIQUERY *query = NULL;
+    MSIVIEW *view;
+    UINT r;
+    
+    r = MSI_OpenQuery( db, &query, sql, table );
+    if( r != ERROR_SUCCESS )
+        return r;
+
+    view = query->view;
+
+    /* count the number of primary key records */
+    info.n = 0;
+    info.rec = 0;
+    r = MSI_IterateRecords( query, 0, msi_primary_key_iterator, &info );
+    if( r == ERROR_SUCCESS )
+    {
+        TRACE("Found %ld primary keys\n", info.n );
+
+        /* allocate a record and fill in the names of the tables */
+        info.rec = MSI_CreateRecord( info.n );
+        info.n = 0;
+        r = MSI_IterateRecords( query, 0, msi_primary_key_iterator, &info );
+        if( r == ERROR_SUCCESS )
+            *prec = info.rec;
+        else
+            msiobj_release( &info.rec->hdr );
+    }
+    msiobj_release( &query->hdr );
+
+    return r;
+}
+
+UINT WINAPI MsiDatabaseGetPrimaryKeysW( MSIHANDLE hdb,
+                    LPCWSTR table, MSIHANDLE* phRec )
+{
+    MSIRECORD *rec = NULL;
+    MSIDATABASE *db;
+    UINT r;
+
+    TRACE("%ld %s %p\n", hdb, debugstr_w(table), phRec);
+
+    db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
+    if( !db )
+        return ERROR_INVALID_HANDLE;
+
+    r = MSI_DatabaseGetPrimaryKeys( db, table, &rec );
+    if( r == ERROR_SUCCESS )
+    {
+        *phRec = alloc_msihandle( &rec->hdr );
+        msiobj_release( &rec->hdr );
+    }
+    msiobj_release( &db->hdr );
+
+    return r;
+}
+
+UINT WINAPI MsiDatabaseGetPrimaryKeysA(MSIHANDLE hdb, 
+                    LPCSTR table, MSIHANDLE* phRec)
+{
+    LPWSTR szwTable = NULL;
+    UINT r;
+
+    TRACE("%ld %s %p\n", hdb, debugstr_a(table), phRec);
+
+    if( table )
+    {
+        szwTable = strdupAtoW( table );
+        if( !szwTable )
+            return ERROR_OUTOFMEMORY;
+    }
+    r = MsiDatabaseGetPrimaryKeysW( hdb, szwTable, phRec );
+    msi_free( szwTable );
+
+    return r;
+}
+
+UINT WINAPI MsiDatabaseIsTablePersistentA(
+              MSIHANDLE hDatabase, LPSTR szTableName)
+{
+    FIXME("%lx %s\n", hDatabase, debugstr_a(szTableName));
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiDatabaseIsTablePersistentW(
+              MSIHANDLE hDatabase, LPWSTR szTableName)
+{
+    FIXME("%lx %s\n", hDatabase, debugstr_w(szTableName));
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
diff --git a/reactos/dll/msi/order.c b/reactos/dll/msi/order.c
new file mode 100644 (file)
index 0000000..27da7c6
--- /dev/null
@@ -0,0 +1,337 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2002 Mike McCormack for CodeWeavers
+ *
+ * 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 <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "wine/debug.h"
+#include "msi.h"
+#include "msiquery.h"
+#include "objbase.h"
+#include "objidl.h"
+#include "msipriv.h"
+#include "winnls.h"
+
+#include "query.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msidb);
+
+
+/* below is the query interface to a table */
+
+typedef struct tagMSIORDERVIEW
+{
+    MSIVIEW        view;
+    MSIDATABASE   *db;
+    MSIVIEW       *table;
+    UINT          *reorder;
+    UINT           num_cols;
+    UINT           cols[1];
+} MSIORDERVIEW;
+
+static UINT ORDER_compare( MSIORDERVIEW *ov, UINT a, UINT b, UINT *swap )
+{
+    UINT r, i, a_val = 0, b_val = 0;
+
+    *swap = 0;
+    for( i=0; i<ov->num_cols; i++ )
+    {
+        r = ov->table->ops->fetch_int( ov->table, a, ov->cols[i], &a_val );
+        if( r != ERROR_SUCCESS )
+            return r;
+
+        r = ov->table->ops->fetch_int( ov->table, b, ov->cols[i], &b_val );
+        if( r != ERROR_SUCCESS )
+            return r;
+
+        if( a_val != b_val )
+        {
+            if( a_val > b_val )
+                *swap = 1;
+            break;
+        }
+    }
+
+    return ERROR_SUCCESS;
+}
+
+static UINT ORDER_mergesort( MSIORDERVIEW *ov, UINT left, UINT right )
+{
+    UINT r, centre = (left + right)/2, temp, swap = 0, i, j;
+    UINT *array = ov->reorder;
+
+    if( left == right )
+        return ERROR_SUCCESS;
+
+    /* sort the left half */
+    r = ORDER_mergesort( ov, left, centre );
+    if( r != ERROR_SUCCESS )
+        return r;
+
+    /* sort the right half */
+    r = ORDER_mergesort( ov, centre+1, right );
+    if( r != ERROR_SUCCESS )
+        return r;
+
+    for( i=left, j=centre+1; (i<=centre) && (j<=right); i++ )
+    {
+        r = ORDER_compare( ov, array[i], array[j], &swap );
+        if( r != ERROR_SUCCESS )
+            return r;
+        if( swap )
+        { 
+            temp = array[j];
+            memmove( &array[i+1], &array[i], (j-i)*sizeof (UINT) );
+            array[i] = temp;
+            j++;
+            centre++;
+        }
+    }
+
+    return ERROR_SUCCESS;
+}
+
+static UINT ORDER_verify( MSIORDERVIEW *ov, UINT num_rows )
+{
+    UINT i, swap, r;
+
+    for( i=1; i<num_rows; i++ )
+    {
+        r = ORDER_compare( ov, ov->reorder[i-1], ov->reorder[i], &swap );
+        if( r != ERROR_SUCCESS )
+            return r;
+        if( !swap )
+            continue;
+        ERR("Bad order! %d\n", i);
+        return ERROR_FUNCTION_FAILED;
+    }
+
+    return ERROR_SUCCESS;
+}
+
+static UINT ORDER_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
+{
+    MSIORDERVIEW *ov = (MSIORDERVIEW*)view;
+
+    TRACE("%p %d %d %p\n", ov, row, col, val );
+
+    if( !ov->table )
+         return ERROR_FUNCTION_FAILED;
+
+    row = ov->reorder[ row ];
+
+    return ov->table->ops->fetch_int( ov->table, row, col, val );
+}
+
+static UINT ORDER_execute( struct tagMSIVIEW *view, MSIRECORD *record )
+{
+    MSIORDERVIEW *ov = (MSIORDERVIEW*)view;
+    UINT r, num_rows = 0, i;
+
+    TRACE("%p %p\n", ov, record);
+
+    if( !ov->table )
+         return ERROR_FUNCTION_FAILED;
+
+    r = ov->table->ops->execute( ov->table, record );
+    if( r != ERROR_SUCCESS )
+        return r;
+
+    r = ov->table->ops->get_dimensions( ov->table, &num_rows, NULL );
+    if( r != ERROR_SUCCESS )
+        return r;
+
+    ov->reorder = msi_alloc( num_rows*sizeof(UINT) );
+    if( !ov->reorder )
+        return ERROR_FUNCTION_FAILED;
+
+    for( i=0; i<num_rows; i++ )
+        ov->reorder[i] = i;
+
+    r = ORDER_mergesort( ov, 0, num_rows - 1 );
+    if( r != ERROR_SUCCESS )
+        return r;
+
+    r = ORDER_verify( ov, num_rows );
+    if( r != ERROR_SUCCESS )
+        return r;
+
+    return ERROR_SUCCESS;
+}
+
+static UINT ORDER_close( struct tagMSIVIEW *view )
+{
+    MSIORDERVIEW *ov = (MSIORDERVIEW*)view;
+
+    TRACE("%p\n", ov );
+
+    if( !ov->table )
+         return ERROR_FUNCTION_FAILED;
+
+    msi_free( ov->reorder );
+    ov->reorder = NULL;
+
+    return ov->table->ops->close( ov->table );
+}
+
+static UINT ORDER_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols )
+{
+    MSIORDERVIEW *ov = (MSIORDERVIEW*)view;
+
+    TRACE("%p %p %p\n", ov, rows, cols );
+
+    if( !ov->table )
+         return ERROR_FUNCTION_FAILED;
+
+    return ov->table->ops->get_dimensions( ov->table, rows, cols );
+}
+
+static UINT ORDER_get_column_info( struct tagMSIVIEW *view,
+                UINT n, LPWSTR *name, UINT *type )
+{
+    MSIORDERVIEW *ov = (MSIORDERVIEW*)view;
+
+    TRACE("%p %d %p %p\n", ov, n, name, type );
+
+    if( !ov->table )
+         return ERROR_FUNCTION_FAILED;
+
+    return ov->table->ops->get_column_info( ov->table, n, name, type );
+}
+
+static UINT ORDER_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode,
+                MSIRECORD *rec )
+{
+    MSIORDERVIEW *ov = (MSIORDERVIEW*)view;
+
+    TRACE("%p %d %p\n", ov, eModifyMode, rec );
+
+    if( !ov->table )
+         return ERROR_FUNCTION_FAILED;
+
+    return ov->table->ops->modify( ov->table, eModifyMode, rec );
+}
+
+static UINT ORDER_delete( struct tagMSIVIEW *view )
+{
+    MSIORDERVIEW *ov = (MSIORDERVIEW*)view;
+
+    TRACE("%p\n", ov );
+
+    if( ov->table )
+        ov->table->ops->delete( ov->table );
+
+    msi_free( ov->reorder );
+    ov->reorder = NULL;
+
+    msiobj_release( &ov->db->hdr );
+    msi_free( ov );
+
+    return ERROR_SUCCESS;
+}
+
+
+MSIVIEWOPS order_ops =
+{
+    ORDER_fetch_int,
+    NULL,
+    NULL,
+    NULL,
+    ORDER_execute,
+    ORDER_close,
+    ORDER_get_dimensions,
+    ORDER_get_column_info,
+    ORDER_modify,
+    ORDER_delete
+};
+
+static UINT ORDER_AddColumn( MSIORDERVIEW *ov, LPCWSTR name )
+{
+    UINT n, count, r;
+    MSIVIEW *table;
+
+    TRACE("%p adding %s\n", ov, debugstr_w( name ) );
+
+    if( ov->view.ops != &order_ops )
+        return ERROR_FUNCTION_FAILED;
+
+    table = ov->table;
+    if( !table )
+        return ERROR_FUNCTION_FAILED;
+    if( !table->ops->get_dimensions )
+        return ERROR_FUNCTION_FAILED;
+    if( !table->ops->get_column_info )
+        return ERROR_FUNCTION_FAILED;
+
+    r = table->ops->get_dimensions( table, NULL, &count );
+    if( r != ERROR_SUCCESS )
+        return r;
+
+    if( ov->num_cols >= count )
+        return ERROR_FUNCTION_FAILED;
+
+    r = VIEW_find_column( table, name, &n );
+    if( r != ERROR_SUCCESS )
+        return r;
+
+    ov->cols[ov->num_cols] = n;
+    TRACE("Ordering by column %s (%d)\n", debugstr_w( name ), n);
+
+    ov->num_cols++;
+
+    return ERROR_SUCCESS;
+}
+
+UINT ORDER_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table,
+                       column_info *columns )
+{
+    MSIORDERVIEW *ov = NULL;
+    UINT count = 0, r;
+    column_info *x;
+
+    TRACE("%p\n", ov );
+
+    r = table->ops->get_dimensions( table, NULL, &count );
+    if( r != ERROR_SUCCESS )
+    {
+        ERR("can't get table dimensions\n");
+        return r;
+    }
+
+    ov = msi_alloc_zero( sizeof *ov + sizeof (UINT) * count );
+    if( !ov )
+        return ERROR_FUNCTION_FAILED;
+    
+    /* fill the structure */
+    ov->view.ops = &order_ops;
+    msiobj_addref( &db->hdr );
+    ov->db = db;
+    ov->table = table;
+    ov->reorder = NULL;
+    ov->num_cols = 0;
+    *view = (MSIVIEW*) ov;
+
+    for( x = columns; x ; x = x->next )
+        ORDER_AddColumn( ov, x->column );
+
+    return ERROR_SUCCESS;
+}
diff --git a/reactos/dll/msi/package.c b/reactos/dll/msi/package.c
new file mode 100644 (file)
index 0000000..a77f853
--- /dev/null
@@ -0,0 +1,904 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2004 Aric Stewart for CodeWeavers
+ *
+ * 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
+ */
+
+#define NONAMELESSUNION
+
+#include <stdarg.h>
+#include <stdio.h>
+#include "windef.h"
+#include "winbase.h"
+#include "winreg.h"
+#include "winnls.h"
+#include "shlwapi.h"
+#include "wingdi.h"
+#include "wine/debug.h"
+#include "msi.h"
+#include "msiquery.h"
+#include "objidl.h"
+#include "wincrypt.h"
+#include "winuser.h"
+#include "shlobj.h"
+#include "wine/unicode.h"
+#include "objbase.h"
+
+#include "msipriv.h"
+#include "action.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+static void MSI_FreePackage( MSIOBJECTHDR *arg)
+{
+    MSIPACKAGE *package= (MSIPACKAGE*) arg;
+
+    if( package->dialog )
+        msi_dialog_destroy( package->dialog );
+    ACTION_free_package_structures(package);
+
+    msiobj_release( &package->db->hdr );
+}
+
+static UINT clone_properties(MSIDATABASE *db)
+{
+    MSIQUERY * view = NULL;
+    UINT rc;
+    static const WCHAR CreateSql[] = {
+       'C','R','E','A','T','E',' ','T','A','B','L','E',' ','`','_','P','r','o',
+       'p','e','r','t','y','`',' ','(',' ','`','_','P','r','o','p','e','r','t',
+       'y','`',' ','C','H','A','R','(','5','6',')',' ','N','O','T',' ','N','U',
+       'L','L',',',' ','`','V','a','l','u','e','`',' ','C','H','A','R','(','9',
+       '8',')',' ','N','O','T',' ','N','U','L','L',' ','P','R','I','M','A','R',
+       'Y',' ','K','E','Y',' ','`','_','P','r','o','p','e','r','t','y','`',')',0};
+    static const WCHAR Query[] = {
+       'S','E','L','E','C','T',' ','*',' ',
+       'F','R','O','M',' ','`','P','r','o','p','e','r','t','y','`',0};
+    static const WCHAR Insert[] = {
+       'I','N','S','E','R','T',' ','i','n','t','o',' ',
+       '`','_','P','r','o','p','e','r','t','y','`',' ',
+       '(','`','_','P','r','o','p','e','r','t','y','`',',',
+       '`','V','a','l','u','e','`',')',' ',
+       'V','A','L','U','E','S',' ','(','?',',','?',')',0};
+
+    /* create the temporary properties table */
+    rc = MSI_DatabaseOpenViewW(db, CreateSql, &view);
+    if (rc != ERROR_SUCCESS)
+        return rc;
+    rc = MSI_ViewExecute(view,0);   
+    MSI_ViewClose(view);
+    msiobj_release(&view->hdr); 
+    if (rc != ERROR_SUCCESS)
+        return rc;
+
+    /* clone the existing properties */
+    rc = MSI_DatabaseOpenViewW(db, Query, &view);
+    if (rc != ERROR_SUCCESS)
+        return rc;
+
+    rc = MSI_ViewExecute(view, 0);
+    if (rc != ERROR_SUCCESS)
+    {
+        MSI_ViewClose(view);
+        msiobj_release(&view->hdr); 
+        return rc;
+    }
+    while (1)
+    {
+        MSIRECORD * row;
+        MSIQUERY * view2;
+
+        rc = MSI_ViewFetch(view,&row);
+        if (rc != ERROR_SUCCESS)
+            break;
+
+        rc = MSI_DatabaseOpenViewW(db,Insert,&view2);  
+        if (rc!= ERROR_SUCCESS)
+            continue;
+        rc = MSI_ViewExecute(view2,row);
+        MSI_ViewClose(view2);
+        msiobj_release(&view2->hdr);
+        if (rc == ERROR_SUCCESS) 
+            msiobj_release(&row->hdr); 
+    }
+    MSI_ViewClose(view);
+    msiobj_release(&view->hdr);
+    
+    return rc;
+}
+
+/*
+ * set_installed_prop
+ *
+ * Sets the "Installed" property to indicate that
+ *  the product is installed for the current user.
+ */
+static UINT set_installed_prop( MSIPACKAGE *package )
+{
+    static const WCHAR szInstalled[] = {
+        'I','n','s','t','a','l','l','e','d',0 };
+    WCHAR val[2] = { '1', 0 };
+    HKEY hkey = 0;
+    UINT r;
+
+    r = MSIREG_OpenUninstallKey( package->ProductCode, &hkey, FALSE );
+    if (r == ERROR_SUCCESS)
+    {
+        RegCloseKey( hkey );
+        MSI_SetPropertyW( package, szInstalled, val );
+    }
+
+    return r;
+}
+
+/*
+ * There are a whole slew of these we need to set
+ *
+ *
+http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/properties.asp
+ */
+static VOID set_installer_properties(MSIPACKAGE *package)
+{
+    WCHAR pth[MAX_PATH];
+    WCHAR *ptr;
+    OSVERSIONINFOA OSVersion;
+    MEMORYSTATUSEX msex;
+    DWORD verval;
+    WCHAR verstr[10], bufstr[20];
+    HDC dc;
+
+    static const WCHAR cszbs[]={'\\',0};
+    static const WCHAR CFF[] = 
+{'C','o','m','m','o','n','F','i','l','e','s','F','o','l','d','e','r',0};
+    static const WCHAR PFF[] = 
+{'P','r','o','g','r','a','m','F','i','l','e','s','F','o','l','d','e','r',0};
+    static const WCHAR CADF[] = 
+{'C','o','m','m','o','n','A','p','p','D','a','t','a','F','o','l','d','e','r',0};
+    static const WCHAR FaF[] = 
+{'F','a','v','o','r','i','t','e','s','F','o','l','d','e','r',0};
+    static const WCHAR FoF[] = 
+{'F','o','n','t','s','F','o','l','d','e','r',0};
+    static const WCHAR SendTF[] = 
+{'S','e','n','d','T','o','F','o','l','d','e','r',0};
+    static const WCHAR SMF[] = 
+{'S','t','a','r','t','M','e','n','u','F','o','l','d','e','r',0};
+    static const WCHAR StF[] = 
+{'S','t','a','r','t','u','p','F','o','l','d','e','r',0};
+    static const WCHAR TemplF[] = 
+{'T','e','m','p','l','a','t','e','F','o','l','d','e','r',0};
+    static const WCHAR DF[] = 
+{'D','e','s','k','t','o','p','F','o','l','d','e','r',0};
+    static const WCHAR PMF[] = 
+{'P','r','o','g','r','a','m','M','e','n','u','F','o','l','d','e','r',0};
+    static const WCHAR ATF[] = 
+{'A','d','m','i','n','T','o','o','l','s','F','o','l','d','e','r',0};
+    static const WCHAR ADF[] = 
+{'A','p','p','D','a','t','a','F','o','l','d','e','r',0};
+    static const WCHAR SF[] = 
+{'S','y','s','t','e','m','F','o','l','d','e','r',0};
+    static const WCHAR SF16[] = 
+{'S','y','s','t','e','m','1','6','F','o','l','d','e','r',0};
+    static const WCHAR LADF[] = 
+{'L','o','c','a','l','A','p','p','D','a','t','a','F','o','l','d','e','r',0};
+    static const WCHAR MPF[] = 
+{'M','y','P','i','c','t','u','r','e','s','F','o','l','d','e','r',0};
+    static const WCHAR PF[] = 
+{'P','e','r','s','o','n','a','l','F','o','l','d','e','r',0};
+    static const WCHAR WF[] = 
+{'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
+    static const WCHAR WV[] = 
+{'W','i','n','d','o','w','s','V','o','l','u','m','e',0};
+    static const WCHAR TF[]=
+{'T','e','m','p','F','o','l','d','e','r',0};
+    static const WCHAR szAdminUser[] =
+{'A','d','m','i','n','U','s','e','r',0};
+    static const WCHAR szPriv[] =
+{'P','r','i','v','i','l','e','g','e','d',0};
+    static const WCHAR szOne[] =
+{'1',0};
+    static const WCHAR v9x[] = { 'V','e','r','s','i','o','n','9','X',0 };
+    static const WCHAR vNT[] = { 'V','e','r','s','i','o','n','N','T',0 };
+    static const WCHAR szFormat[] = {'%','l','i',0};
+    static const WCHAR szWinBuild[] =
+{'W','i','n','d','o','w','s','B','u','i','l','d', 0 };
+    static const WCHAR szSPL[] = 
+{'S','e','r','v','i','c','e','P','a','c','k','L','e','v','e','l',0 };
+    static const WCHAR szSix[] = {'6',0 };
+
+    static const WCHAR szVersionMsi[] = { 'V','e','r','s','i','o','n','M','s','i',0 };
+    static const WCHAR szPhysicalMemory[] = { 'P','h','y','s','i','c','a','l','M','e','m','o','r','y',0 };
+    static const WCHAR szFormat2[] = {'%','l','i','.','%','l','i',0};
+/* Screen properties */
+    static const WCHAR szScreenX[] = {'S','c','r','e','e','n','X',0};
+    static const WCHAR szScreenY[] = {'S','c','r','e','e','n','Y',0};
+    static const WCHAR szColorBits[] = {'C','o','l','o','r','B','i','t','s',0};
+    static const WCHAR szScreenFormat[] = {'%','d',0};
+    static const WCHAR szIntel[] = { 'I','n','t','e','l',0 };
+    SYSTEM_INFO sys_info;
+
+    /*
+     * Other things that probably should be set:
+     *
+     * SystemLanguageID ComputerName UserLanguageID LogonUser VirtualMemory
+     * Intel ShellAdvSupport DefaultUIFont VersionDatabase PackagecodeChanging
+     * ProductState CaptionHeight BorderTop BorderSide TextHeight
+     * RedirectedDllSupport Time Date Privileged
+     */
+
+    SHGetFolderPathW(NULL,CSIDL_PROGRAM_FILES_COMMON,NULL,0,pth);
+    strcatW(pth,cszbs);
+    MSI_SetPropertyW(package, CFF, pth);
+
+    SHGetFolderPathW(NULL,CSIDL_PROGRAM_FILES,NULL,0,pth);
+    strcatW(pth,cszbs);
+    MSI_SetPropertyW(package, PFF, pth);
+
+    SHGetFolderPathW(NULL,CSIDL_COMMON_APPDATA,NULL,0,pth);
+    strcatW(pth,cszbs);
+    MSI_SetPropertyW(package, CADF, pth);
+
+    SHGetFolderPathW(NULL,CSIDL_FAVORITES,NULL,0,pth);
+    strcatW(pth,cszbs);
+    MSI_SetPropertyW(package, FaF, pth);
+
+    SHGetFolderPathW(NULL,CSIDL_FONTS,NULL,0,pth);
+    strcatW(pth,cszbs);
+    MSI_SetPropertyW(package, FoF, pth);
+
+    SHGetFolderPathW(NULL,CSIDL_SENDTO,NULL,0,pth);
+    strcatW(pth,cszbs);
+    MSI_SetPropertyW(package, SendTF, pth);
+
+    SHGetFolderPathW(NULL,CSIDL_STARTMENU,NULL,0,pth);
+    strcatW(pth,cszbs);
+    MSI_SetPropertyW(package, SMF, pth);
+
+    SHGetFolderPathW(NULL,CSIDL_STARTUP,NULL,0,pth);
+    strcatW(pth,cszbs);
+    MSI_SetPropertyW(package, StF, pth);
+
+    SHGetFolderPathW(NULL,CSIDL_TEMPLATES,NULL,0,pth);
+    strcatW(pth,cszbs);
+    MSI_SetPropertyW(package, TemplF, pth);
+
+    SHGetFolderPathW(NULL,CSIDL_DESKTOP,NULL,0,pth);
+    strcatW(pth,cszbs);
+    MSI_SetPropertyW(package, DF, pth);
+
+    SHGetFolderPathW(NULL,CSIDL_PROGRAMS,NULL,0,pth);
+    strcatW(pth,cszbs);
+    MSI_SetPropertyW(package, PMF, pth);
+
+    SHGetFolderPathW(NULL,CSIDL_ADMINTOOLS,NULL,0,pth);
+    strcatW(pth,cszbs);
+    MSI_SetPropertyW(package, ATF, pth);
+
+    SHGetFolderPathW(NULL,CSIDL_APPDATA,NULL,0,pth);
+    strcatW(pth,cszbs);
+    MSI_SetPropertyW(package, ADF, pth);
+
+    SHGetFolderPathW(NULL,CSIDL_SYSTEM,NULL,0,pth);
+    strcatW(pth,cszbs);
+    MSI_SetPropertyW(package, SF, pth);
+    MSI_SetPropertyW(package, SF16, pth);
+
+    SHGetFolderPathW(NULL,CSIDL_LOCAL_APPDATA,NULL,0,pth);
+    strcatW(pth,cszbs);
+    MSI_SetPropertyW(package, LADF, pth);
+
+    SHGetFolderPathW(NULL,CSIDL_MYPICTURES,NULL,0,pth);
+    strcatW(pth,cszbs);
+    MSI_SetPropertyW(package, MPF, pth);
+
+    SHGetFolderPathW(NULL,CSIDL_PERSONAL,NULL,0,pth);
+    strcatW(pth,cszbs);
+    MSI_SetPropertyW(package, PF, pth);
+
+    SHGetFolderPathW(NULL,CSIDL_WINDOWS,NULL,0,pth);
+    strcatW(pth,cszbs);
+    MSI_SetPropertyW(package, WF, pth);
+    
+    /* Physical Memory is specified in MB. Using total amount. */
+    msex.dwLength = sizeof(msex);
+    GlobalMemoryStatusEx( &msex );
+    sprintfW( bufstr, szScreenFormat, (int)(msex.ullTotalPhys/1024/1024));
+    MSI_SetPropertyW(package, szPhysicalMemory, bufstr);
+
+    SHGetFolderPathW(NULL,CSIDL_WINDOWS,NULL,0,pth);
+    ptr = strchrW(pth,'\\');
+    if (ptr)
+       *(ptr+1) = 0;
+    MSI_SetPropertyW(package, WV, pth);
+    
+    GetTempPathW(MAX_PATH,pth);
+    MSI_SetPropertyW(package, TF, pth);
+
+
+    /* in a wine environment the user is always admin and privileged */
+    MSI_SetPropertyW(package,szAdminUser,szOne);
+    MSI_SetPropertyW(package,szPriv,szOne);
+
+    /* set the os things */
+    OSVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
+    GetVersionExA(&OSVersion);
+    verval = OSVersion.dwMinorVersion+OSVersion.dwMajorVersion*100;
+    sprintfW(verstr,szFormat,verval);
+    switch (OSVersion.dwPlatformId)
+    {
+        case VER_PLATFORM_WIN32_WINDOWS:    
+            MSI_SetPropertyW(package,v9x,verstr);
+            break;
+        case VER_PLATFORM_WIN32_NT:
+            MSI_SetPropertyW(package,vNT,verstr);
+            break;
+    }
+    sprintfW(verstr,szFormat,OSVersion.dwBuildNumber);
+    MSI_SetPropertyW(package,szWinBuild,verstr);
+    /* just fudge this */
+    MSI_SetPropertyW(package,szSPL,szSix);
+
+    sprintfW( bufstr, szFormat2, MSI_MAJORVERSION, MSI_MINORVERSION);
+    MSI_SetPropertyW( package, szVersionMsi, bufstr );
+
+    GetSystemInfo( &sys_info );
+    if (sys_info.u.s.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL)
+    {
+        sprintfW( bufstr, szScreenFormat, sys_info.wProcessorLevel );
+        MSI_SetPropertyW( package, szIntel, bufstr );
+    }
+
+    /* Screen properties. */
+    dc = GetDC(0);
+    sprintfW( bufstr, szScreenFormat, GetDeviceCaps( dc, HORZRES ) );
+    MSI_SetPropertyW( package, szScreenX, bufstr );
+    sprintfW( bufstr, szScreenFormat, GetDeviceCaps( dc, VERTRES ));
+    MSI_SetPropertyW( package, szScreenY, bufstr );
+    sprintfW( bufstr, szScreenFormat, GetDeviceCaps( dc, BITSPIXEL ));
+    MSI_SetPropertyW( package, szColorBits, bufstr );
+    ReleaseDC(0, dc);
+}
+
+MSIPACKAGE *MSI_CreatePackage( MSIDATABASE *db )
+{
+    static const WCHAR szLevel[] = { 'U','I','L','e','v','e','l',0 };
+    static const WCHAR szpi[] = {'%','i',0};
+    static const WCHAR szProductCode[] = {
+        'P','r','o','d','u','c','t','C','o','d','e',0};
+    MSIPACKAGE *package = NULL;
+    WCHAR uilevel[10];
+
+    TRACE("%p\n", db);
+
+    package = alloc_msiobject( MSIHANDLETYPE_PACKAGE, sizeof (MSIPACKAGE),
+                               MSI_FreePackage );
+    if( package )
+    {
+        msiobj_addref( &db->hdr );
+
+        package->db = db;
+        list_init( &package->components );
+        list_init( &package->features );
+        list_init( &package->files );
+        list_init( &package->tempfiles );
+        list_init( &package->folders );
+        package->ActionFormat = NULL;
+        package->LastAction = NULL;
+        package->dialog = NULL;
+        package->next_dialog = NULL;
+        list_init( &package->subscriptions );
+        list_init( &package->appids );
+        list_init( &package->classes );
+        list_init( &package->mimes );
+        list_init( &package->extensions );
+        list_init( &package->progids );
+        list_init( &package->RunningActions );
+
+        /* OK, here is where we do a slew of things to the database to 
+         * prep for all that is to come as a package */
+
+        clone_properties(db);
+        set_installer_properties(package);
+        sprintfW(uilevel,szpi,gUILevel);
+        MSI_SetPropertyW(package, szLevel, uilevel);
+
+        package->ProductCode = msi_dup_property( package, szProductCode );
+        set_installed_prop( package );
+    }
+
+    return package;
+}
+
+/*
+ * copy_package_to_temp   [internal]
+ *
+ * copy the msi file to a temp file to prevent locking a CD
+ * with a multi disc install 
+ *
+ * FIXME: I think this is wrong, and instead of copying the package,
+ *        we should read all the tables to memory, then open the
+ *        database to read binary streams on demand.
+ */ 
+static LPCWSTR copy_package_to_temp( LPCWSTR szPackage, LPWSTR filename )
+{
+    WCHAR path[MAX_PATH];
+    static const WCHAR szMSI[] = {'M','S','I',0};
+
+    GetTempPathW( MAX_PATH, path );
+    GetTempFileNameW( path, szMSI, 0, filename );
+
+    if( !CopyFileW( szPackage, filename, FALSE ) )
+    {
+        ERR("failed to copy package to temp path %s\n", debugstr_w(filename) );
+        return szPackage;
+    }
+
+    TRACE("Opening relocated package %s\n", debugstr_w( filename ));
+    return filename;
+}
+
+UINT MSI_OpenPackageW(LPCWSTR szPackage, MSIPACKAGE **pPackage)
+{
+    MSIDATABASE *db = NULL;
+    MSIPACKAGE *package;
+    MSIHANDLE handle;
+    UINT r;
+
+    TRACE("%s %p\n", debugstr_w(szPackage), pPackage);
+
+    if( szPackage[0] == '#' )
+    {
+        handle = atoiW(&szPackage[1]);
+        db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
+        if( !db )
+            return ERROR_INVALID_HANDLE;
+    }
+    else
+    {
+        WCHAR temppath[MAX_PATH];
+        LPCWSTR file = copy_package_to_temp( szPackage, temppath );
+
+        r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY, &db );
+
+        if (file != szPackage)
+            DeleteFileW( file );
+
+        if( r != ERROR_SUCCESS )
+            return r;
+    }
+
+    package = MSI_CreatePackage( db );
+    msiobj_release( &db->hdr );
+    if( !package )
+        return ERROR_FUNCTION_FAILED;
+
+    /* 
+     * FIXME:  I don't think this is right.  Maybe we should be storing the
+     * name of the database in the MSIDATABASE structure and fetching this
+     * info from there, or maybe this is only relevant to cached databases.
+     */
+    if( szPackage[0] != '#' )
+    {
+        static const WCHAR OriginalDatabase[] =
+          {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
+        static const WCHAR Database[] = {'D','A','T','A','B','A','S','E',0};
+
+        MSI_SetPropertyW( package, OriginalDatabase, szPackage );
+        MSI_SetPropertyW( package, Database, szPackage );
+    }
+
+    *pPackage = package;
+
+    return ERROR_SUCCESS;
+}
+
+UINT WINAPI MsiOpenPackageExW(LPCWSTR szPackage, DWORD dwOptions, MSIHANDLE *phPackage)
+{
+    MSIPACKAGE *package = NULL;
+    UINT ret;
+
+    TRACE("%s %08lx %p\n", debugstr_w(szPackage), dwOptions, phPackage );
+
+    if( dwOptions )
+        FIXME("dwOptions %08lx not supported\n", dwOptions);
+
+    ret = MSI_OpenPackageW( szPackage, &package );
+    if( ret == ERROR_SUCCESS )
+    {
+        *phPackage = alloc_msihandle( &package->hdr );
+        msiobj_release( &package->hdr );
+    }
+
+    return ret;
+}
+
+UINT WINAPI MsiOpenPackageW(LPCWSTR szPackage, MSIHANDLE *phPackage)
+{
+    return MsiOpenPackageExW( szPackage, 0, phPackage );
+}
+
+UINT WINAPI MsiOpenPackageExA(LPCSTR szPackage, DWORD dwOptions, MSIHANDLE *phPackage)
+{
+    LPWSTR szwPack = NULL;
+    UINT ret;
+
+    if( szPackage )
+    {
+        szwPack = strdupAtoW( szPackage );
+        if( !szwPack )
+            return ERROR_OUTOFMEMORY;
+    }
+
+    ret = MsiOpenPackageExW( szwPack, dwOptions, phPackage );
+
+    msi_free( szwPack );
+
+    return ret;
+}
+
+UINT WINAPI MsiOpenPackageA(LPCSTR szPackage, MSIHANDLE *phPackage)
+{
+    return MsiOpenPackageExA( szPackage, 0, phPackage );
+}
+
+MSIHANDLE WINAPI MsiGetActiveDatabase(MSIHANDLE hInstall)
+{
+    MSIPACKAGE *package;
+    MSIHANDLE handle = 0;
+
+    TRACE("(%ld)\n",hInstall);
+
+    package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE);
+    if( package)
+    {
+        handle = alloc_msihandle( &package->db->hdr );
+        msiobj_release( &package->hdr );
+    }
+
+    return handle;
+}
+
+INT MSI_ProcessMessage( MSIPACKAGE *package, INSTALLMESSAGE eMessageType,
+                               MSIRECORD *record)
+{
+    DWORD log_type = 0;
+    LPWSTR message;
+    DWORD sz;
+    DWORD total_size = 0;
+    INT msg_field=1;
+    INT i;
+    INT rc;
+    char *msg;
+    int len;
+
+    TRACE("%x\n", eMessageType);
+    rc = 0;
+
+    if ((eMessageType & 0xff000000) == INSTALLMESSAGE_ERROR)
+        log_type |= INSTALLLOGMODE_ERROR;
+    if ((eMessageType & 0xff000000) == INSTALLMESSAGE_WARNING)
+        log_type |= INSTALLLOGMODE_WARNING;
+    if ((eMessageType & 0xff000000) == INSTALLMESSAGE_USER)
+        log_type |= INSTALLLOGMODE_USER;
+    if ((eMessageType & 0xff000000) == INSTALLMESSAGE_INFO)
+        log_type |= INSTALLLOGMODE_INFO;
+    if ((eMessageType & 0xff000000) == INSTALLMESSAGE_COMMONDATA)
+        log_type |= INSTALLLOGMODE_COMMONDATA;
+    if ((eMessageType & 0xff000000) == INSTALLMESSAGE_ACTIONSTART)
+        log_type |= INSTALLLOGMODE_ACTIONSTART;
+    if ((eMessageType & 0xff000000) == INSTALLMESSAGE_ACTIONDATA)
+        log_type |= INSTALLLOGMODE_ACTIONDATA;
+    /* just a guess */
+    if ((eMessageType & 0xff000000) == INSTALLMESSAGE_PROGRESS)
+        log_type |= 0x800;
+
+    message = msi_alloc(1*sizeof (WCHAR));
+    message[0]=0;
+    msg_field = MSI_RecordGetFieldCount(record);
+    for (i = 1; i <= msg_field; i++)
+    {
+        LPWSTR tmp;
+        WCHAR number[3];
+        static const WCHAR format[] = { '%','i',':',' ',0};
+        static const WCHAR space[] = { ' ',0};
+        sz = 0;
+        MSI_RecordGetStringW(record,i,NULL,&sz);
+        sz+=4;
+        total_size+=sz*sizeof(WCHAR);
+        tmp = msi_alloc(sz*sizeof(WCHAR));
+        message = msi_realloc(message,total_size*sizeof (WCHAR));
+
+        MSI_RecordGetStringW(record,i,tmp,&sz);
+
+        if (msg_field > 1)
+        {
+            sprintfW(number,format,i);
+            strcatW(message,number);
+        }
+        strcatW(message,tmp);
+        if (msg_field > 1)
+            strcatW(message,space);
+
+        msi_free(tmp);
+    }
+
+    TRACE("(%p %lx %lx %s)\n",gUIHandlerA, gUIFilter, log_type,
+                             debugstr_w(message));
+
+    /* convert it to ASCII */
+    len = WideCharToMultiByte( CP_ACP, 0, message, -1,
+                               NULL, 0, NULL, NULL );
+    msg = msi_alloc( len );
+    WideCharToMultiByte( CP_ACP, 0, message, -1,
+                         msg, len, NULL, NULL );
+
+    if (gUIHandlerA && (gUIFilter & log_type))
+    {
+        rc = gUIHandlerA(gUIContext,eMessageType,msg);
+    }
+
+    if ((!rc) && (gszLogFile[0]) && !((eMessageType & 0xff000000) ==
+                                      INSTALLMESSAGE_PROGRESS))
+    {
+        DWORD write;
+        HANDLE log_file = CreateFileW(gszLogFile,GENERIC_WRITE, 0, NULL,
+                                  OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+
+        if (log_file != INVALID_HANDLE_VALUE)
+        {
+            SetFilePointer(log_file,0, NULL, FILE_END);
+            WriteFile(log_file,msg,strlen(msg),&write,NULL);
+            WriteFile(log_file,"\n",1,&write,NULL);
+            CloseHandle(log_file);
+        }
+    }
+    msi_free( msg );
+    
+    msi_free( message);
+    return ERROR_SUCCESS;
+}
+
+INT WINAPI MsiProcessMessage( MSIHANDLE hInstall, INSTALLMESSAGE eMessageType,
+                              MSIHANDLE hRecord)
+{
+    UINT ret = ERROR_INVALID_HANDLE;
+    MSIPACKAGE *package = NULL;
+    MSIRECORD *record = NULL;
+
+    package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE );
+    if( !package )
+        return ERROR_INVALID_HANDLE;
+
+    record = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD );
+    if( !record )
+        goto out;
+
+    ret = MSI_ProcessMessage( package, eMessageType, record );
+
+out:
+    msiobj_release( &package->hdr );
+    if( record )
+        msiobj_release( &record->hdr );
+
+    return ret;
+}
+
+/* property code */
+UINT WINAPI MsiSetPropertyA( MSIHANDLE hInstall, LPCSTR szName, LPCSTR szValue )
+{
+    LPWSTR szwName = NULL, szwValue = NULL;
+    UINT r = ERROR_OUTOFMEMORY;
+
+    szwName = strdupAtoW( szName );
+    if( szName && !szwName )
+        goto end;
+
+    szwValue = strdupAtoW( szValue );
+    if( szValue && !szwValue )
+        goto end;
+
+    r = MsiSetPropertyW( hInstall, szwName, szwValue);
+
+end:
+    msi_free( szwName );
+    msi_free( szwValue );
+
+    return r;
+}
+
+UINT MSI_SetPropertyW( MSIPACKAGE *package, LPCWSTR szName, LPCWSTR szValue)
+{
+    MSIQUERY *view;
+    MSIRECORD *row;
+    UINT rc;
+    DWORD sz = 0;
+    static const WCHAR Insert[]=
+     {'I','N','S','E','R','T',' ','i','n','t','o',' ','`','_','P','r','o','p'
+,'e','r','t','y','`',' ','(','`','_','P','r','o','p','e','r','t','y','`'
+,',','`','V','a','l','u','e','`',')',' ','V','A','L','U','E','S'
+,' ','(','?',',','?',')',0};
+    static const WCHAR Update[]=
+     {'U','P','D','A','T','E',' ','_','P','r','o','p','e'
+,'r','t','y',' ','s','e','t',' ','`','V','a','l','u','e','`',' ','='
+,' ','?',' ','w','h','e','r','e',' ','`','_','P','r','o','p'
+,'e','r','t','y','`',' ','=',' ','\'','%','s','\'',0};
+    WCHAR Query[1024];
+
+    TRACE("%p %s %s\n", package, debugstr_w(szName), debugstr_w(szValue));
+
+    if (!szName)
+        return ERROR_INVALID_PARAMETER;
+
+    /* this one is weird... */
+    if (!szName[0])
+        return szValue ? ERROR_FUNCTION_FAILED : ERROR_SUCCESS;
+
+    rc = MSI_GetPropertyW(package,szName,0,&sz);
+    if (rc==ERROR_MORE_DATA || rc == ERROR_SUCCESS)
+    {
+        sprintfW(Query,Update,szName);
+
+        row = MSI_CreateRecord(1);
+        MSI_RecordSetStringW(row,1,szValue);
+
+    }
+    else
+    {
+        strcpyW(Query,Insert);
+
+        row = MSI_CreateRecord(2);
+        MSI_RecordSetStringW(row,1,szName);
+        MSI_RecordSetStringW(row,2,szValue);
+    }
+
+    rc = MSI_DatabaseOpenViewW(package->db,Query,&view);
+    if (rc == ERROR_SUCCESS)
+    {
+        rc = MSI_ViewExecute(view,row);
+
+        MSI_ViewClose(view);
+        msiobj_release(&view->hdr);
+    }
+    msiobj_release(&row->hdr);
+
+    return rc;
+}
+
+UINT WINAPI MsiSetPropertyW( MSIHANDLE hInstall, LPCWSTR szName, LPCWSTR szValue)
+{
+    MSIPACKAGE *package;
+    UINT ret;
+
+    package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE);
+    if( !package )
+        return ERROR_INVALID_HANDLE;
+    ret = MSI_SetPropertyW( package, szName, szValue);
+    msiobj_release( &package->hdr );
+    return ret;
+}
+
+static MSIRECORD *MSI_GetPropertyRow( MSIPACKAGE *package, LPCWSTR name )
+{
+    static const WCHAR query[]=
+    {'S','E','L','E','C','T',' ','`','V','a','l','u','e','`',' ',
+     'F','R','O','M',' ' ,'`','_','P','r','o','p','e','r','t','y','`',
+     ' ','W','H','E','R','E',' ' ,'`','_','P','r','o','p','e','r','t','y','`',
+     '=','\'','%','s','\'',0};
+
+    if (!name || !name[0])
+        return NULL;
+
+    return MSI_QueryGetRecord( package->db, query, name );
+}
+/* internal function, not compatible with MsiGetPropertyW */
+UINT MSI_GetPropertyW( MSIPACKAGE *package, LPCWSTR szName, 
+                       LPWSTR szValueBuf, DWORD* pchValueBuf )
+{
+    MSIRECORD *row;
+    UINT rc = ERROR_FUNCTION_FAILED;
+
+    row = MSI_GetPropertyRow( package, szName );
+
+    if (*pchValueBuf > 0)
+        szValueBuf[0] = 0;
+
+    if (row)
+    {
+        rc = MSI_RecordGetStringW(row,1,szValueBuf,pchValueBuf);
+        msiobj_release(&row->hdr);
+    }
+
+    if (rc == ERROR_SUCCESS)
+        TRACE("returning %s for property %s\n", debugstr_w(szValueBuf),
+            debugstr_w(szName));
+    else if (rc == ERROR_MORE_DATA)
+        TRACE("need %li sized buffer for %s\n", *pchValueBuf,
+            debugstr_w(szName));
+    else
+    {
+        *pchValueBuf = 0;
+        TRACE("property %s not found\n", debugstr_w(szName));
+    }
+
+    return rc;
+}
+
+static UINT MSI_GetProperty( MSIHANDLE handle, LPCWSTR name, 
+                             awstring *szValueBuf, DWORD* pchValueBuf )
+{
+    static const WCHAR empty[] = {0};
+    MSIPACKAGE *package;
+    MSIRECORD *row = NULL;
+    UINT r;
+    LPCWSTR val = NULL;
+
+    TRACE("%lu %s %p %p\n", handle, debugstr_w(name),
+          szValueBuf->str.w, pchValueBuf );
+
+    if (!name)
+        return ERROR_INVALID_PARAMETER;
+
+    package = msihandle2msiinfo( handle, MSIHANDLETYPE_PACKAGE );
+    if (!package)
+        return ERROR_INVALID_HANDLE;
+
+    row = MSI_GetPropertyRow( package, name );
+    if (row)
+        val = MSI_RecordGetString( row, 1 );
+
+    if (!val)
+        val = empty;
+
+    r = msi_strcpy_to_awstring( val, szValueBuf, pchValueBuf );
+
+    if (row)
+        msiobj_release( &row->hdr );
+    msiobj_release( &package->hdr );
+
+    return r;
+}
+
+UINT WINAPI MsiGetPropertyA( MSIHANDLE hInstall, LPCSTR szName,
+                             LPSTR szValueBuf, DWORD* pchValueBuf )
+{
+    awstring val;
+    LPWSTR name;
+    UINT r;
+
+    val.unicode = FALSE;
+    val.str.a = szValueBuf;
+
+    name = strdupAtoW( szName );
+    if (szName && !name)
+        return ERROR_OUTOFMEMORY;
+
+    r = MSI_GetProperty( hInstall, name, &val, pchValueBuf );
+    msi_free( name );
+    return r;
+}
+  
+UINT WINAPI MsiGetPropertyW( MSIHANDLE hInstall, LPCWSTR szName,
+                             LPWSTR szValueBuf, DWORD* pchValueBuf )
+{
+    awstring val;
+
+    val.unicode = TRUE;
+    val.str.w = szValueBuf;
+
+    return MSI_GetProperty( hInstall, szName, &val, pchValueBuf );
+}
diff --git a/reactos/dll/msi/preview.c b/reactos/dll/msi/preview.c
new file mode 100644 (file)
index 0000000..4c7c62b
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2005 Mike McCormack for CodeWeavers
+ *
+ * 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 <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winnls.h"
+#include "msi.h"
+#include "msipriv.h"
+
+#include "wine/debug.h"
+#include "wine/unicode.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+static void MSI_ClosePreview( MSIOBJECTHDR *arg )
+{
+    MSIPREVIEW *preview = (MSIPREVIEW *) arg;
+
+    msiobj_release( &preview->package->hdr );
+}
+
+MSIPREVIEW *MSI_EnableUIPreview( MSIDATABASE *db )
+{
+    MSIPREVIEW *preview = NULL;
+    MSIPACKAGE *package;
+
+    package = MSI_CreatePackage( db );
+    if( package )
+    {
+        preview = alloc_msiobject( MSIHANDLETYPE_PREVIEW, sizeof (MSIPREVIEW),
+                               MSI_ClosePreview );
+        if( preview )
+        {
+            preview->package = package;
+            preview->dialog = 0;
+            msiobj_addref( &package->hdr );
+        }
+        msiobj_release( &package->hdr );
+    }
+    return preview;
+}
+
+UINT WINAPI MsiEnableUIPreview( MSIHANDLE hdb, MSIHANDLE* phPreview )
+{
+    MSIDATABASE *db;
+    MSIPREVIEW *preview;
+    UINT r = ERROR_FUNCTION_FAILED;
+
+    TRACE("%ld %p\n", hdb, phPreview);
+
+    db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
+    if( !db )
+        return ERROR_INVALID_HANDLE;
+    preview = MSI_EnableUIPreview( db );
+    if( preview )
+    {
+        *phPreview = alloc_msihandle( &preview->hdr );
+        msiobj_release( &preview->hdr );
+        r = ERROR_SUCCESS;
+    }
+    msiobj_release( &db->hdr );
+
+    return r;
+}
+
+static UINT preview_event_handler( MSIPACKAGE *package, LPCWSTR event,
+                                   LPCWSTR argument, msi_dialog *dialog )
+{
+    MESSAGE("Preview dialog event '%s' (arg='%s')\n",
+            debugstr_w( event ), debugstr_w( argument ));
+    return ERROR_SUCCESS;
+}
+
+UINT MSI_PreviewDialogW( MSIPREVIEW *preview, LPCWSTR szDialogName )
+{
+    msi_dialog *dialog = NULL;
+    UINT r = ERROR_SUCCESS;
+
+    if( preview->dialog )
+        msi_dialog_destroy( preview->dialog );
+
+    /* an empty name means we should just destroy the current preview dialog */
+    if( szDialogName )
+    {
+        dialog = msi_dialog_create( preview->package, szDialogName,
+                                    preview_event_handler );
+        if( dialog )
+            msi_dialog_do_preview( dialog );
+        else
+            r = ERROR_FUNCTION_FAILED;
+    }
+    preview->dialog = dialog;
+
+    return r;
+}
+
+UINT WINAPI MsiPreviewDialogW( MSIHANDLE hPreview, LPCWSTR szDialogName )
+{
+    MSIPREVIEW *preview;
+    UINT r;
+
+    TRACE("%ld %s\n", hPreview, debugstr_w(szDialogName));
+
+    preview = msihandle2msiinfo( hPreview, MSIHANDLETYPE_PREVIEW );
+    if( !preview )
+        return ERROR_INVALID_HANDLE;
+
+    r = MSI_PreviewDialogW( preview, szDialogName );
+
+    msiobj_release( &preview->hdr );
+
+    return r;
+}
+
+UINT WINAPI MsiPreviewDialogA( MSIHANDLE hPreview, LPCSTR szDialogName )
+{
+    UINT r;
+    LPWSTR strW = NULL;
+
+    TRACE("%ld %s\n", hPreview, debugstr_a(szDialogName));
+
+    if( szDialogName )
+    {
+        strW = strdupAtoW( szDialogName );
+        if( !strW )
+            return ERROR_OUTOFMEMORY;
+    }
+    r = MsiPreviewDialogW( hPreview, strW );
+    msi_free( strW );
+    return r;
+}
+
+UINT WINAPI MsiPreviewBillboardW( MSIHANDLE hPreview, LPCWSTR szControlName,
+                                  LPCWSTR szBillboard)
+{
+    FIXME("%ld %s %s\n", hPreview, debugstr_w(szControlName),
+          debugstr_w(szBillboard));
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiPreviewBillboardA( MSIHANDLE hPreview, LPCSTR szControlName,
+                                  LPCSTR szBillboard)
+{
+    FIXME("%ld %s %s\n", hPreview, debugstr_a(szControlName),
+          debugstr_a(szBillboard));
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
diff --git a/reactos/dll/msi/query.h b/reactos/dll/msi/query.h
new file mode 100644 (file)
index 0000000..e1fa104
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2002 Mike McCormack for CodeWeavers
+ *
+ * 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
+ */
+
+#ifndef __WINE_MSI_QUERY_H
+#define __WINE_MSI_QUERY_H
+
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "objbase.h"
+#include "objidl.h"
+#include "msi.h"
+#include "msiquery.h"
+#include "msipriv.h"
+#include "wine/list.h"
+
+
+#define OP_EQ       1
+#define OP_AND      2
+#define OP_OR       3
+#define OP_GT       4
+#define OP_LT       5
+#define OP_LE       6
+#define OP_GE       7
+#define OP_NE       8
+#define OP_ISNULL   9
+#define OP_NOTNULL  10
+
+#define EXPR_COMPLEX  1
+#define EXPR_COLUMN   2
+#define EXPR_COL_NUMBER 3
+#define EXPR_IVAL     4
+#define EXPR_SVAL     5
+#define EXPR_UVAL     6
+#define EXPR_STRCMP   7
+#define EXPR_WILDCARD 9
+#define EXPR_COL_NUMBER_STRING 10
+
+struct sql_str {
+    LPCWSTR data;
+    INT len;
+};
+
+typedef struct _column_info
+{
+    LPCWSTR table;
+    LPCWSTR column;
+    UINT   type;
+    struct expr *val;
+    struct _column_info *next;
+} column_info;
+
+struct complex_expr
+{
+    UINT op;
+    struct expr *left;
+    struct expr *right;
+};
+
+struct expr
+{
+    int type;
+    union
+    {
+        struct complex_expr expr;
+        INT   ival;
+        UINT  uval;
+        LPCWSTR sval;
+        LPCWSTR column;
+        UINT col_number;
+    } u;
+};
+
+UINT MSI_ParseSQL( MSIDATABASE *db, LPCWSTR command, MSIVIEW **phview,
+                   struct list *mem );
+
+UINT TABLE_CreateView( MSIDATABASE *db, LPCWSTR name, MSIVIEW **view );
+
+UINT SELECT_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table,
+                        column_info *columns );
+
+UINT DISTINCT_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table );
+
+UINT ORDER_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table,
+                       column_info *columns );
+
+UINT WHERE_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table,
+                       struct expr *cond );
+
+UINT CREATE_CreateView( MSIDATABASE *db, MSIVIEW **view, LPWSTR table,
+                        column_info *col_info, BOOL temp );
+
+UINT INSERT_CreateView( MSIDATABASE *db, MSIVIEW **view, LPWSTR table,
+                        column_info *columns, column_info *values, BOOL temp );
+
+UINT UPDATE_CreateView( MSIDATABASE *db, MSIVIEW **, LPWSTR table,
+                        column_info *list, struct expr *expr );
+
+UINT DELETE_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table );
+
+int sqliteGetToken(const WCHAR *z, int *tokenType);
+
+#endif /* __WINE_MSI_QUERY_H */
diff --git a/reactos/dll/msi/record.c b/reactos/dll/msi/record.c
new file mode 100644 (file)
index 0000000..c1f6d1f
--- /dev/null
@@ -0,0 +1,832 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2002-2004 Mike McCormack for CodeWeavers
+ *
+ * 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 <stdarg.h>
+
+#define COBJMACROS
+
+#include "windef.h"
+#include "winbase.h"
+#include "winuser.h"
+#include "winerror.h"
+#include "wine/debug.h"
+#include "msi.h"
+#include "msiquery.h"
+#include "msipriv.h"
+#include "objidl.h"
+#include "winnls.h"
+#include "ole2.h"
+
+#include "winreg.h"
+#include "shlwapi.h"
+
+#include "query.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msidb);
+
+#define MSIFIELD_NULL   0
+#define MSIFIELD_INT    1
+#define MSIFIELD_STR    2
+#define MSIFIELD_WSTR   3
+#define MSIFIELD_STREAM 4
+
+static void MSI_FreeField( MSIFIELD *field )
+{
+    switch( field->type )
+    {
+    case MSIFIELD_NULL:
+    case MSIFIELD_INT:
+        break;
+    case MSIFIELD_WSTR:
+        msi_free( field->u.szwVal);
+        break;
+    case MSIFIELD_STREAM:
+        IStream_Release( field->u.stream );
+        break;
+    default:
+        ERR("Invalid field type %d\n", field->type);
+    }
+}
+
+static void MSI_CloseRecord( MSIOBJECTHDR *arg )
+{
+    MSIRECORD *rec = (MSIRECORD *) arg;
+    UINT i;
+
+    for( i=0; i<=rec->count; i++ )
+        MSI_FreeField( &rec->fields[i] );
+}
+
+MSIRECORD *MSI_CreateRecord( unsigned int cParams )
+{
+    MSIRECORD *rec;
+    UINT len;
+
+    TRACE("%d\n", cParams);
+
+    if( cParams>65535 )
+        return NULL;
+
+    len = sizeof (MSIRECORD) + sizeof (MSIFIELD)*cParams;
+    rec = alloc_msiobject( MSIHANDLETYPE_RECORD, len, MSI_CloseRecord );
+    if( rec )
+        rec->count = cParams;
+    return rec;
+}
+
+MSIHANDLE WINAPI MsiCreateRecord( unsigned int cParams )
+{
+    MSIRECORD *rec;
+    MSIHANDLE ret = 0;
+
+    TRACE("%d\n", cParams);
+
+    rec = MSI_CreateRecord( cParams );
+    if( rec )
+        ret = alloc_msihandle( &rec->hdr );
+    msiobj_release( &rec->hdr );
+    return ret;
+}
+
+unsigned int MSI_RecordGetFieldCount( MSIRECORD *rec )
+{
+    return rec->count;
+}
+
+unsigned int WINAPI MsiRecordGetFieldCount( MSIHANDLE handle )
+{
+    MSIRECORD *rec;
+    UINT ret;
+
+    TRACE("%ld\n", handle );
+
+    rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+    if( !rec )
+        return -1;
+
+    msiobj_lock( &rec->hdr );
+    ret = MSI_RecordGetFieldCount( rec );
+    msiobj_unlock( &rec->hdr );
+    msiobj_release( &rec->hdr );
+
+    return ret;
+}
+
+static BOOL string2intW( LPCWSTR str, int *out )
+{
+    int x = 0;
+    LPCWSTR p = str;
+
+    if( *p == '-' ) /* skip the minus sign */
+        p++;
+    while ( *p )
+    {
+        if( (*p < '0') || (*p > '9') )
+            return FALSE;
+        x *= 10;
+        x += (*p - '0');
+        p++;
+    }
+
+    if( str[0] == '-' ) /* check if it's negative */
+        x = -x;
+    *out = x; 
+
+    return TRUE;
+}
+
+int MSI_RecordGetInteger( MSIRECORD *rec, unsigned int iField)
+{
+    int ret = 0;
+
+    TRACE("%p %d\n", rec, iField );
+
+    if( iField > rec->count )
+        return MSI_NULL_INTEGER;
+
+    switch( rec->fields[iField].type )
+    {
+    case MSIFIELD_INT:
+        return rec->fields[iField].u.iVal;
+    case MSIFIELD_WSTR:
+        if( string2intW( rec->fields[iField].u.szwVal, &ret ) )
+            return ret;
+        return MSI_NULL_INTEGER;
+    default:
+        break;
+    }
+
+    return MSI_NULL_INTEGER;
+}
+
+int WINAPI MsiRecordGetInteger( MSIHANDLE handle, unsigned int iField)
+{
+    MSIRECORD *rec;
+    UINT ret;
+
+    TRACE("%ld %d\n", handle, iField );
+
+    rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+    if( !rec )
+        return MSI_NULL_INTEGER;
+
+    msiobj_lock( &rec->hdr );
+    ret = MSI_RecordGetInteger( rec, iField );
+    msiobj_unlock( &rec->hdr );
+    msiobj_release( &rec->hdr );
+
+    return ret;
+}
+
+UINT WINAPI MsiRecordClearData( MSIHANDLE handle )
+{
+    MSIRECORD *rec;
+    UINT i;
+
+    TRACE("%ld\n", handle );
+
+    rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+    if( !rec )
+        return ERROR_INVALID_HANDLE;
+
+    msiobj_lock( &rec->hdr );
+    for( i=0; i<=rec->count; i++)
+    {
+        MSI_FreeField( &rec->fields[i] );
+        rec->fields[i].type = MSIFIELD_NULL;
+        rec->fields[i].u.iVal = 0;
+    }
+    msiobj_unlock( &rec->hdr );
+    msiobj_release( &rec->hdr );
+
+    return ERROR_SUCCESS;
+}
+
+UINT MSI_RecordSetInteger( MSIRECORD *rec, unsigned int iField, int iVal )
+{
+    TRACE("%p %u %d\n", rec, iField, iVal);
+
+    if( iField > rec->count )
+        return ERROR_INVALID_PARAMETER;
+    MSI_FreeField( &rec->fields[iField] );
+    rec->fields[iField].type = MSIFIELD_INT;
+    rec->fields[iField].u.iVal = iVal;
+
+    return ERROR_SUCCESS;
+}
+
+UINT WINAPI MsiRecordSetInteger( MSIHANDLE handle, unsigned int iField, int iVal )
+{
+    MSIRECORD *rec;
+    UINT ret;
+
+    TRACE("%ld %u %d\n", handle, iField, iVal);
+
+    rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+    if( !rec )
+        return ERROR_INVALID_HANDLE;
+
+    msiobj_lock( &rec->hdr );
+    ret = MSI_RecordSetInteger( rec, iField, iVal );
+    msiobj_unlock( &rec->hdr );
+    msiobj_release( &rec->hdr );
+    return ret;
+}
+
+BOOL MSI_RecordIsNull( MSIRECORD *rec, unsigned int iField )
+{
+    BOOL r = TRUE;
+
+    TRACE("%p %d\n", rec, iField );
+
+    r = ( iField > rec->count ) ||
+        ( rec->fields[iField].type == MSIFIELD_NULL );
+
+    return r;
+}
+
+BOOL WINAPI MsiRecordIsNull( MSIHANDLE handle, unsigned int iField )
+{
+    MSIRECORD *rec;
+    UINT ret;
+
+    TRACE("%ld %d\n", handle, iField );
+
+    rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+    if( !rec )
+        return 0;
+    msiobj_lock( &rec->hdr );
+    ret = MSI_RecordIsNull( rec, iField );
+    msiobj_unlock( &rec->hdr );
+    msiobj_release( &rec->hdr );
+    return ret;
+
+}
+
+UINT MSI_RecordGetStringA(MSIRECORD *rec, unsigned int iField, 
+               LPSTR szValue, DWORD *pcchValue)
+{
+    UINT len=0, ret;
+    CHAR buffer[16];
+
+    TRACE("%p %d %p %p\n", rec, iField, szValue, pcchValue);
+
+    if( iField > rec->count )
+        return ERROR_INVALID_PARAMETER;
+
+    ret = ERROR_SUCCESS;
+    switch( rec->fields[iField].type )
+    {
+    case MSIFIELD_INT:
+        wsprintfA(buffer, "%d", rec->fields[iField].u.iVal);
+        len = lstrlenA( buffer );
+        lstrcpynA(szValue, buffer, *pcchValue);
+        break;
+    case MSIFIELD_WSTR:
+        len = WideCharToMultiByte( CP_ACP, 0, rec->fields[iField].u.szwVal, -1,
+                             NULL, 0 , NULL, NULL);
+        WideCharToMultiByte( CP_ACP, 0, rec->fields[iField].u.szwVal, -1,
+                             szValue, *pcchValue, NULL, NULL);
+        if( *pcchValue && len>*pcchValue )
+            szValue[*pcchValue-1] = 0;
+        if( len )
+            len--;
+        break;
+    case MSIFIELD_NULL:
+        if( *pcchValue > 0 )
+            szValue[0] = 0;
+        break;
+    default:
+        ret = ERROR_INVALID_PARAMETER;
+        break;
+    }
+
+    if( *pcchValue < len )
+        ret = ERROR_MORE_DATA;
+    *pcchValue = len;
+
+    return ret;
+}
+
+UINT WINAPI MsiRecordGetStringA(MSIHANDLE handle, unsigned int iField, 
+               LPSTR szValue, DWORD *pcchValue)
+{
+    MSIRECORD *rec;
+    UINT ret;
+
+    TRACE("%ld %d %p %p\n", handle, iField, szValue, pcchValue);
+
+    rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+    if( !rec )
+        return ERROR_INVALID_HANDLE;
+    msiobj_lock( &rec->hdr );
+    ret = MSI_RecordGetStringA( rec, iField, szValue, pcchValue);
+    msiobj_unlock( &rec->hdr );
+    msiobj_release( &rec->hdr );
+    return ret;
+}
+
+const WCHAR *MSI_RecordGetString( MSIRECORD *rec, unsigned int iField )
+{
+    if( iField > rec->count )
+        return NULL;
+
+    if( rec->fields[iField].type != MSIFIELD_WSTR )
+        return NULL;
+
+    return rec->fields[iField].u.szwVal;
+}
+
+UINT MSI_RecordGetStringW(MSIRECORD *rec, unsigned int iField,
+               LPWSTR szValue, DWORD *pcchValue)
+{
+    UINT len=0, ret;
+    WCHAR buffer[16];
+    static const WCHAR szFormat[] = { '%','d',0 };
+
+    TRACE("%p %d %p %p\n", rec, iField, szValue, pcchValue);
+
+    if( iField > rec->count )
+        return ERROR_INVALID_PARAMETER;
+
+    ret = ERROR_SUCCESS;
+    switch( rec->fields[iField].type )
+    {
+    case MSIFIELD_INT:
+        wsprintfW(buffer, szFormat, rec->fields[iField].u.iVal);
+        len = lstrlenW( buffer );
+        lstrcpynW(szValue, buffer, *pcchValue);
+        break;
+    case MSIFIELD_WSTR:
+        len = lstrlenW( rec->fields[iField].u.szwVal );
+        lstrcpynW(szValue, rec->fields[iField].u.szwVal, *pcchValue);
+        break;
+    case MSIFIELD_NULL:
+        len = 1;
+        if( *pcchValue > 0 )
+            szValue[0] = 0;
+    default:
+        break;
+    }
+
+    if( *pcchValue < len )
+        ret = ERROR_MORE_DATA;
+    *pcchValue = len;
+
+    return ret;
+}
+
+UINT WINAPI MsiRecordGetStringW(MSIHANDLE handle, unsigned int iField,
+               LPWSTR szValue, DWORD *pcchValue)
+{
+    MSIRECORD *rec;
+    UINT ret;
+
+    TRACE("%ld %d %p %p\n", handle, iField, szValue, pcchValue);
+
+    rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+    if( !rec )
+        return ERROR_INVALID_HANDLE;
+
+    msiobj_lock( &rec->hdr );
+    ret = MSI_RecordGetStringW( rec, iField, szValue, pcchValue );
+    msiobj_unlock( &rec->hdr );
+    msiobj_release( &rec->hdr );
+    return ret;
+}
+
+static UINT msi_get_stream_size( IStream *stm )
+{
+    STATSTG stat;
+    HRESULT r;
+
+    r = IStream_Stat( stm, &stat, STATFLAG_NONAME );
+    if( FAILED(r) )
+        return 0;
+    return stat.cbSize.QuadPart;
+}
+
+UINT MSI_RecordDataSize(MSIRECORD *rec, unsigned int iField)
+{
+    TRACE("%p %d\n", rec, iField);
+
+    if( iField > rec->count )
+        return 0;
+
+    switch( rec->fields[iField].type )
+    {
+    case MSIFIELD_INT:
+        return sizeof (INT);
+    case MSIFIELD_WSTR:
+        return lstrlenW( rec->fields[iField].u.szwVal );
+    case MSIFIELD_NULL:
+        break;
+    case MSIFIELD_STREAM:
+        return msi_get_stream_size( rec->fields[iField].u.stream );
+    }
+    return 0;
+}
+
+UINT WINAPI MsiRecordDataSize(MSIHANDLE handle, unsigned int iField)
+{
+    MSIRECORD *rec;
+    UINT ret;
+
+    TRACE("%ld %d\n", handle, iField);
+
+    rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+    if( !rec )
+        return 0;
+    msiobj_lock( &rec->hdr );
+    ret = MSI_RecordDataSize( rec, iField);
+    msiobj_unlock( &rec->hdr );
+    msiobj_release( &rec->hdr );
+    return ret;
+}
+
+UINT MSI_RecordSetStringA( MSIRECORD *rec, unsigned int iField, LPCSTR szValue )
+{
+    LPWSTR str;
+
+    TRACE("%p %d %s\n", rec, iField, debugstr_a(szValue));
+
+    if( iField > rec->count )
+        return ERROR_INVALID_FIELD;
+
+    MSI_FreeField( &rec->fields[iField] );
+    if( szValue && szValue[0] )
+    {
+        str = strdupAtoW( szValue );
+        rec->fields[iField].type = MSIFIELD_WSTR;
+        rec->fields[iField].u.szwVal = str;
+    }
+    else
+    {
+        rec->fields[iField].type = MSIFIELD_NULL;
+        rec->fields[iField].u.szwVal = NULL;
+    }
+
+    return 0;
+}
+
+UINT WINAPI MsiRecordSetStringA( MSIHANDLE handle, unsigned int iField, LPCSTR szValue )
+{
+    MSIRECORD *rec;
+    UINT ret;
+
+    TRACE("%ld %d %s\n", handle, iField, debugstr_a(szValue));
+
+    rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+    if( !rec )
+        return ERROR_INVALID_HANDLE;
+    msiobj_lock( &rec->hdr );
+    ret = MSI_RecordSetStringA( rec, iField, szValue );
+    msiobj_unlock( &rec->hdr );
+    msiobj_release( &rec->hdr );
+    return ret;
+}
+
+UINT MSI_RecordSetStringW( MSIRECORD *rec, unsigned int iField, LPCWSTR szValue )
+{
+    LPWSTR str;
+
+    TRACE("%p %d %s\n", rec, iField, debugstr_w(szValue));
+
+    if( iField > rec->count )
+        return ERROR_INVALID_FIELD;
+
+    MSI_FreeField( &rec->fields[iField] );
+
+    if( szValue && szValue[0] )
+    {
+        str = strdupW( szValue );
+        rec->fields[iField].type = MSIFIELD_WSTR;
+        rec->fields[iField].u.szwVal = str;
+    }
+    else
+    {
+        rec->fields[iField].type = MSIFIELD_NULL;
+        rec->fields[iField].u.szwVal = NULL;
+    }
+
+    return 0;
+}
+
+UINT WINAPI MsiRecordSetStringW( MSIHANDLE handle, unsigned int iField, LPCWSTR szValue )
+{
+    MSIRECORD *rec;
+    UINT ret;
+
+    TRACE("%ld %d %s\n", handle, iField, debugstr_w(szValue));
+
+    rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+    if( !rec )
+        return ERROR_INVALID_HANDLE;
+
+    msiobj_lock( &rec->hdr );
+    ret = MSI_RecordSetStringW( rec, iField, szValue );
+    msiobj_unlock( &rec->hdr );
+    msiobj_release( &rec->hdr );
+    return ret;
+}
+
+/* read the data in a file into an IStream */
+static UINT RECORD_StreamFromFile(LPCWSTR szFile, IStream **pstm)
+{
+    DWORD sz, szHighWord = 0, read;
+    HANDLE handle;
+    HGLOBAL hGlob = 0;
+    HRESULT hr;
+    ULARGE_INTEGER ulSize;
+
+    TRACE("reading %s\n", debugstr_w(szFile));
+
+    /* read the file into memory */
+    handle = CreateFileW(szFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
+    if( handle == INVALID_HANDLE_VALUE )
+        return GetLastError();
+    sz = GetFileSize(handle, &szHighWord);
+    if( sz != INVALID_FILE_SIZE && szHighWord == 0 )
+    {
+        hGlob = GlobalAlloc(GMEM_FIXED, sz);
+        if( hGlob )
+        {
+            BOOL r = ReadFile(handle, hGlob, sz, &read, NULL);
+            if( !r )
+            {
+                GlobalFree(hGlob);
+                hGlob = 0;
+            }
+        }
+    }
+    CloseHandle(handle);
+    if( !hGlob )
+        return ERROR_FUNCTION_FAILED;
+
+    /* make a stream out of it, and set the correct file size */
+    hr = CreateStreamOnHGlobal(hGlob, TRUE, pstm);
+    if( FAILED( hr ) )
+    {
+        GlobalFree(hGlob);
+        return ERROR_FUNCTION_FAILED;
+    }
+
+    /* set the correct size - CreateStreamOnHGlobal screws it up */
+    ulSize.QuadPart = sz;
+    IStream_SetSize(*pstm, ulSize);
+
+    TRACE("read %s, %ld bytes into IStream %p\n", debugstr_w(szFile), sz, *pstm);
+
+    return ERROR_SUCCESS;
+}
+
+UINT MSI_RecordSetStreamW(MSIRECORD *rec, unsigned int iField, LPCWSTR szFilename)
+{
+    IStream *stm = NULL;
+    HRESULT r;
+
+    if( (iField == 0) || (iField > rec->count) )
+        return ERROR_INVALID_PARAMETER;
+
+    /* no filename means we should seek back to the start of the stream */
+    if( !szFilename )
+    {
+        LARGE_INTEGER ofs;
+        ULARGE_INTEGER cur;
+
+        if( rec->fields[iField].type != MSIFIELD_STREAM )
+            return ERROR_INVALID_FIELD;
+
+        stm = rec->fields[iField].u.stream;
+        if( !stm )
+            return ERROR_INVALID_FIELD;
+
+        ofs.QuadPart = 0;
+        r = IStream_Seek( stm, ofs, STREAM_SEEK_SET, &cur );
+        if( FAILED( r ) )
+            return ERROR_FUNCTION_FAILED;
+    }
+    else
+    {
+        /* read the file into a stream and save the stream in the record */
+        r = RECORD_StreamFromFile(szFilename, &stm);
+        if( r != ERROR_SUCCESS )
+            return r;
+
+        /* if all's good, store it in the record */
+        MSI_FreeField( &rec->fields[iField] );
+        rec->fields[iField].type = MSIFIELD_STREAM;
+        rec->fields[iField].u.stream = stm;
+    }
+
+    return ERROR_SUCCESS;
+}
+
+UINT WINAPI MsiRecordSetStreamA(MSIHANDLE hRecord, unsigned int iField, LPCSTR szFilename)
+{
+    LPWSTR wstr = NULL;
+    UINT ret;
+
+    TRACE("%ld %d %s\n", hRecord, iField, debugstr_a(szFilename));
+
+    if( szFilename )
+    {
+        wstr = strdupAtoW( szFilename );
+        if( !wstr )
+             return ERROR_OUTOFMEMORY;
+    }
+    ret = MsiRecordSetStreamW(hRecord, iField, wstr);
+    msi_free(wstr);
+
+    return ret;
+}
+
+UINT WINAPI MsiRecordSetStreamW(MSIHANDLE handle, unsigned int iField, LPCWSTR szFilename)
+{
+    MSIRECORD *rec;
+    UINT ret;
+
+    TRACE("%ld %d %s\n", handle, iField, debugstr_w(szFilename));
+
+    rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+    if( !rec )
+        return ERROR_INVALID_HANDLE;
+
+    msiobj_lock( &rec->hdr );
+    ret = MSI_RecordSetStreamW( rec, iField, szFilename );
+    msiobj_unlock( &rec->hdr );
+    msiobj_release( &rec->hdr );
+    return ret;
+}
+
+UINT MSI_RecordReadStream(MSIRECORD *rec, unsigned int iField, char *buf, DWORD *sz)
+{
+    ULONG count;
+    HRESULT r;
+    IStream *stm;
+
+    TRACE("%p %d %p %p\n", rec, iField, buf, sz);
+
+    if( !sz )
+        return ERROR_INVALID_PARAMETER;
+
+    if( iField > rec->count)
+        return ERROR_INVALID_PARAMETER;
+
+    if( rec->fields[iField].type != MSIFIELD_STREAM )
+        return ERROR_INVALID_DATATYPE;
+
+    stm = rec->fields[iField].u.stream;
+    if( !stm )
+        return ERROR_INVALID_PARAMETER;
+
+    /* if there's no buffer pointer, calculate the length to the end */
+    if( !buf )
+    {
+        LARGE_INTEGER ofs;
+        ULARGE_INTEGER end, cur;
+
+        ofs.QuadPart = cur.QuadPart = 0;
+        end.QuadPart = 0;
+        r = IStream_Seek( stm, ofs, STREAM_SEEK_SET, &cur );
+        IStream_Seek( stm, ofs, STREAM_SEEK_END, &end );
+        ofs.QuadPart = cur.QuadPart;
+        IStream_Seek( stm, ofs, STREAM_SEEK_SET, &cur );
+        *sz = end.QuadPart - cur.QuadPart;
+
+        return ERROR_SUCCESS;
+    }
+
+    /* read the data */
+    count = 0;
+    r = IStream_Read( stm, buf, *sz, &count );
+    if( FAILED( r ) )
+    {
+        *sz = 0;
+        return ERROR_FUNCTION_FAILED;
+    }
+
+    *sz = count;
+
+    return ERROR_SUCCESS;
+}
+
+UINT WINAPI MsiRecordReadStream(MSIHANDLE handle, unsigned int iField, char *buf, DWORD *sz)
+{
+    MSIRECORD *rec;
+    UINT ret;
+
+    TRACE("%ld %d %p %p\n", handle, iField, buf, sz);
+
+    rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+    if( !rec )
+        return ERROR_INVALID_HANDLE;
+    msiobj_lock( &rec->hdr );
+    ret = MSI_RecordReadStream( rec, iField, buf, sz );
+    msiobj_unlock( &rec->hdr );
+    msiobj_release( &rec->hdr );
+    return ret;
+}
+
+UINT MSI_RecordSetIStream( MSIRECORD *rec, unsigned int iField, IStream *stm )
+{
+    TRACE("%p %d %p\n", rec, iField, stm);
+
+    if( iField > rec->count )
+        return ERROR_INVALID_FIELD;
+
+    MSI_FreeField( &rec->fields[iField] );
+
+    rec->fields[iField].type = MSIFIELD_STREAM;
+    rec->fields[iField].u.stream = stm;
+    IStream_AddRef( stm );
+
+    return ERROR_SUCCESS;
+}
+
+UINT MSI_RecordGetIStream( MSIRECORD *rec, unsigned int iField, IStream **pstm)
+{
+    TRACE("%p %d %p\n", rec, iField, pstm);
+
+    if( iField > rec->count )
+        return ERROR_INVALID_FIELD;
+
+    if( rec->fields[iField].type != MSIFIELD_STREAM )
+        return ERROR_INVALID_FIELD;
+
+    *pstm = rec->fields[iField].u.stream;
+    IStream_AddRef( *pstm );
+
+    return ERROR_SUCCESS;
+}
+
+static UINT msi_dump_stream_to_file( IStream *stm, LPCWSTR name )
+{
+    ULARGE_INTEGER size;
+    LARGE_INTEGER pos;
+    IStream *out;
+    DWORD stgm;
+    HRESULT r;
+
+    stgm = STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_FAILIFTHERE;
+    r = SHCreateStreamOnFileW( name, stgm, &out );
+    if( FAILED( r ) )
+        return ERROR_FUNCTION_FAILED;
+
+    pos.QuadPart = 0;
+    r = IStream_Seek( stm, pos, STREAM_SEEK_END, &size );
+    if( FAILED( r ) )
+        goto end;
+
+    pos.QuadPart = 0;
+    r = IStream_Seek( stm, pos, STREAM_SEEK_SET, NULL );
+    if( FAILED( r ) )
+        goto end;
+
+    r = IStream_CopyTo( stm, out, size, NULL, NULL );
+
+end:
+    IStream_Release( out );
+    if( FAILED( r ) )
+        return ERROR_FUNCTION_FAILED;
+    return ERROR_SUCCESS;
+}
+
+UINT MSI_RecordStreamToFile( MSIRECORD *rec, unsigned int iField, LPCWSTR name )
+{
+    IStream *stm = NULL;
+    UINT r;
+
+    TRACE("%p %u %s\n", rec, iField, debugstr_w(name));
+
+    msiobj_lock( &rec->hdr );
+
+    r = MSI_RecordGetIStream( rec, iField, &stm );
+    if( r == ERROR_SUCCESS )
+    {
+        r = msi_dump_stream_to_file( stm, name );
+        IStream_Release( stm );
+    }
+
+    msiobj_unlock( &rec->hdr );
+
+    return r;
+}
diff --git a/reactos/dll/msi/registry.c b/reactos/dll/msi/registry.c
new file mode 100644 (file)
index 0000000..528070e
--- /dev/null
@@ -0,0 +1,980 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2005 Mike McCormack for CodeWeavers
+ * Copyright 2005 Aric Stewart for CodeWeavers
+ *
+ * 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 <stdarg.h>
+
+#define COBJMACROS
+#define NONAMELESSUNION
+
+#include "windef.h"
+#include "winbase.h"
+#include "winreg.h"
+#include "winnls.h"
+#include "shlwapi.h"
+#include "wine/debug.h"
+#include "msi.h"
+#include "msipriv.h"
+#include "wincrypt.h"
+#include "wine/unicode.h"
+#include "winver.h"
+#include "winuser.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+
+/* 
+ * This module will be all the helper functions for registry access by the
+ * installer bits. 
+ */
+static const WCHAR szUserFeatures_fmt[] = {
+'S','o','f','t','w','a','r','e','\\',
+'M','i','c','r','o','s','o','f','t','\\',
+'I','n','s','t','a','l','l','e','r','\\',
+'F','e','a','t','u','r','e','s','\\',
+'%','s',0};
+
+static const WCHAR szInstaller_Features[] = {
+'S','o','f','t','w','a','r','e','\\',
+'M','i','c','r','o','s','o','f','t','\\',
+'W','i','n','d','o','w','s','\\',
+'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
+'I','n','s','t','a','l','l','e','r','\\',
+'F','e','a','t','u','r','e','s',0 };
+
+static const WCHAR szInstaller_Features_fmt[] = {
+'S','o','f','t','w','a','r','e','\\',
+'M','i','c','r','o','s','o','f','t','\\',
+'W','i','n','d','o','w','s','\\',
+'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
+'I','n','s','t','a','l','l','e','r','\\',
+'F','e','a','t','u','r','e','s','\\',
+'%','s',0};
+
+static const WCHAR szInstaller_Components[] = {
+'S','o','f','t','w','a','r','e','\\',
+'M','i','c','r','o','s','o','f','t','\\',
+'W','i','n','d','o','w','s','\\',
+'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
+'I','n','s','t','a','l','l','e','r','\\',
+'C','o','m','p','o','n','e','n','t','s',0 };
+
+static const WCHAR szInstaller_Components_fmt[] = {
+'S','o','f','t','w','a','r','e','\\',
+'M','i','c','r','o','s','o','f','t','\\',
+'W','i','n','d','o','w','s','\\',
+'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
+'I','n','s','t','a','l','l','e','r','\\',
+'C','o','m','p','o','n','e','n','t','s','\\',
+'%','s',0};
+
+static const WCHAR szUser_Components_fmt[] = {
+'S','o','f','t','w','a','r','e','\\',
+'M','i','c','r','o','s','o','f','t','\\',
+'I','n','s','t','a','l','l','e','r','\\',
+'C','o','m','p','o','n','e','n','t','s','\\',
+'%','s',0};
+
+static const WCHAR szUninstall_fmt[] = {
+'S','o','f','t','w','a','r','e','\\',
+'M','i','c','r','o','s','o','f','t','\\',
+'W','i','n','d','o','w','s','\\',
+'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
+'U','n','i','n','s','t','a','l','l','\\',
+'%','s',0 };
+
+static const WCHAR szUserProduct_fmt[] = {
+'S','o','f','t','w','a','r','e','\\',
+'M','i','c','r','o','s','o','f','t','\\',
+'I','n','s','t','a','l','l','e','r','\\',
+'P','r','o','d','u','c','t','s','\\',
+'%','s',0};
+
+static const WCHAR szInstaller_Products[] = {
+'S','o','f','t','w','a','r','e','\\',
+'M','i','c','r','o','s','o','f','t','\\',
+'W','i','n','d','o','w','s','\\',
+'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
+'I','n','s','t','a','l','l','e','r','\\',
+'P','r','o','d','u','c','t','s',0};
+
+static const WCHAR szInstaller_Products_fmt[] = {
+'S','o','f','t','w','a','r','e','\\',
+'M','i','c','r','o','s','o','f','t','\\',
+'W','i','n','d','o','w','s','\\',
+'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
+'I','n','s','t','a','l','l','e','r','\\',
+'P','r','o','d','u','c','t','s','\\',
+'%','s',0};
+
+static const WCHAR szInstaller_UpgradeCodes[] = {
+'S','o','f','t','w','a','r','e','\\',
+'M','i','c','r','o','s','o','f','t','\\',
+'W','i','n','d','o','w','s','\\',
+'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
+'I','n','s','t','a','l','l','e','r','\\',
+'U','p','g','r','a','d','e','C','o','d','e','s',0};
+
+static const WCHAR szInstaller_UpgradeCodes_fmt[] = {
+'S','o','f','t','w','a','r','e','\\',
+'M','i','c','r','o','s','o','f','t','\\',
+'W','i','n','d','o','w','s','\\',
+'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
+'I','n','s','t','a','l','l','e','r','\\',
+'U','p','g','r','a','d','e','C','o','d','e','s','\\',
+'%','s',0};
+
+static const WCHAR szInstaller_UserUpgradeCodes[] = {
+'S','o','f','t','w','a','r','e','\\',
+'M','i','c','r','o','s','o','f','t','\\',
+'I','n','s','t','a','l','l','e','r','\\',
+'U','p','g','r','a','d','e','C','o','d','e','s',0};
+
+static const WCHAR szInstaller_UserUpgradeCodes_fmt[] = {
+'S','o','f','t','w','a','r','e','\\',
+'M','i','c','r','o','s','o','f','t','\\',
+'I','n','s','t','a','l','l','e','r','\\',
+'U','p','g','r','a','d','e','C','o','d','e','s','\\',
+'%','s',0};
+
+
+#define SQUISH_GUID_SIZE 33
+
+BOOL unsquash_guid(LPCWSTR in, LPWSTR out)
+{
+    DWORD i,n=0;
+
+    out[n++]='{';
+    for(i=0; i<8; i++)
+        out[n++] = in[7-i];
+    out[n++]='-';
+    for(i=0; i<4; i++)
+        out[n++] = in[11-i];
+    out[n++]='-';
+    for(i=0; i<4; i++)
+        out[n++] = in[15-i];
+    out[n++]='-';
+    for(i=0; i<2; i++)
+    {
+        out[n++] = in[17+i*2];
+        out[n++] = in[16+i*2];
+    }
+    out[n++]='-';
+    for( ; i<8; i++)
+    {
+        out[n++] = in[17+i*2];
+        out[n++] = in[16+i*2];
+    }
+    out[n++]='}';
+    out[n]=0;
+    return TRUE;
+}
+
+BOOL squash_guid(LPCWSTR in, LPWSTR out)
+{
+    DWORD i,n=0;
+
+    if(in[n++] != '{')
+        return FALSE;
+    for(i=0; i<8; i++)
+        out[7-i] = in[n++];
+    if(in[n++] != '-')
+        return FALSE;
+    for(i=0; i<4; i++)
+        out[11-i] = in[n++];
+    if(in[n++] != '-')
+        return FALSE;
+    for(i=0; i<4; i++)
+        out[15-i] = in[n++];
+    if(in[n++] != '-')
+        return FALSE;
+    for(i=0; i<2; i++)
+    {
+        out[17+i*2] = in[n++];
+        out[16+i*2] = in[n++];
+    }
+    if(in[n++] != '-')
+        return FALSE;
+    for( ; i<8; i++)
+    {
+        out[17+i*2] = in[n++];
+        out[16+i*2] = in[n++];
+    }
+    out[32]=0;
+    if(in[n++] != '}')
+        return FALSE;
+    if(in[n])
+        return FALSE;
+    return TRUE;
+}
+
+
+/* tables for encoding and decoding base85 */
+static const unsigned char table_dec85[0x80] = {
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0x00,0xff,0xff,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0xff,
+0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0xff,0xff,0xff,0x16,0xff,0x17,
+0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,
+0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x30,0x31,0x32,0x33,0xff,0x34,0x35,0x36,
+0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x40,0x41,0x42,0x43,0x44,0x45,0x46,
+0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,0x50,0x51,0x52,0xff,0x53,0x54,0xff,
+};
+
+static const char table_enc85[] =
+"!$%&'()*+,-.0123456789=?@ABCDEFGHIJKLMNO"
+"PQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwx"
+"yz{}~";
+
+/*
+ *  Converts a base85 encoded guid into a GUID pointer
+ *  Base85 encoded GUIDs should be 20 characters long.
+ *
+ *  returns TRUE if successful, FALSE if not
+ */
+BOOL decode_base85_guid( LPCWSTR str, GUID *guid )
+{
+    DWORD i, val = 0, base = 1, *p;
+
+    if (!str)
+        return FALSE;
+
+    p = (DWORD*) guid;
+    for( i=0; i<20; i++ )
+    {
+        if( (i%5) == 0 )
+        {
+            val = 0;
+            base = 1;
+        }
+        val += table_dec85[str[i]] * base;
+        if( str[i] >= 0x80 )
+            return FALSE;
+        if( table_dec85[str[i]] == 0xff )
+            return FALSE;
+        if( (i%5) == 4 )
+            p[i/5] = val;
+        base *= 85;
+    }
+    return TRUE;
+}
+
+/*
+ *  Encodes a base85 guid given a GUID pointer
+ *  Caller should provide a 21 character buffer for the encoded string.
+ *
+ *  returns TRUE if successful, FALSE if not
+ */
+BOOL encode_base85_guid( GUID *guid, LPWSTR str )
+{
+    unsigned int x, *p, i;
+
+    p = (unsigned int*) guid;
+    for( i=0; i<4; i++ )
+    {
+        x = p[i];
+        *str++ = table_enc85[x%85];
+        x = x/85;
+        *str++ = table_enc85[x%85];
+        x = x/85;
+        *str++ = table_enc85[x%85];
+        x = x/85;
+        *str++ = table_enc85[x%85];
+        x = x/85;
+        *str++ = table_enc85[x%85];
+    }
+    *str = 0;
+
+    return TRUE;
+}
+
+
+UINT MSIREG_OpenUninstallKey(LPCWSTR szProduct, HKEY* key, BOOL create)
+{
+    UINT rc;
+    WCHAR keypath[0x200];
+    TRACE("%s\n",debugstr_w(szProduct));
+
+    sprintfW(keypath,szUninstall_fmt,szProduct);
+
+    if (create)
+        rc = RegCreateKeyW(HKEY_LOCAL_MACHINE, keypath, key);
+    else
+        rc = RegOpenKeyW(HKEY_LOCAL_MACHINE, keypath, key);
+
+    return rc;
+}
+
+UINT MSIREG_OpenUserProductsKey(LPCWSTR szProduct, HKEY* key, BOOL create)
+{
+    UINT rc;
+    WCHAR squished_pc[GUID_SIZE];
+    WCHAR keypath[0x200];
+
+    TRACE("%s\n",debugstr_w(szProduct));
+    squash_guid(szProduct,squished_pc);
+    TRACE("squished (%s)\n", debugstr_w(squished_pc));
+
+    sprintfW(keypath,szUserProduct_fmt,squished_pc);
+
+    if (create)
+        rc = RegCreateKeyW(HKEY_CURRENT_USER,keypath,key);
+    else
+        rc = RegOpenKeyW(HKEY_CURRENT_USER,keypath,key);
+
+    return rc;
+}
+
+UINT MSIREG_OpenUserFeaturesKey(LPCWSTR szProduct, HKEY* key, BOOL create)
+{
+    UINT rc;
+    WCHAR squished_pc[GUID_SIZE];
+    WCHAR keypath[0x200];
+
+    TRACE("%s\n",debugstr_w(szProduct));
+    squash_guid(szProduct,squished_pc);
+    TRACE("squished (%s)\n", debugstr_w(squished_pc));
+
+    sprintfW(keypath,szUserFeatures_fmt,squished_pc);
+
+    if (create)
+        rc = RegCreateKeyW(HKEY_CURRENT_USER,keypath,key);
+    else
+        rc = RegOpenKeyW(HKEY_CURRENT_USER,keypath,key);
+
+    return rc;
+}
+
+UINT MSIREG_OpenFeatures(HKEY* key)
+{
+    return RegCreateKeyW(HKEY_LOCAL_MACHINE,szInstaller_Features,key);
+}
+
+UINT MSIREG_OpenFeaturesKey(LPCWSTR szProduct, HKEY* key, BOOL create)
+{
+    UINT rc;
+    WCHAR squished_pc[GUID_SIZE];
+    WCHAR keypath[0x200];
+
+    TRACE("%s\n",debugstr_w(szProduct));
+    squash_guid(szProduct,squished_pc);
+    TRACE("squished (%s)\n", debugstr_w(squished_pc));
+
+    sprintfW(keypath,szInstaller_Features_fmt,squished_pc);
+
+    if (create)
+        rc = RegCreateKeyW(HKEY_LOCAL_MACHINE,keypath,key);
+    else
+        rc = RegOpenKeyW(HKEY_LOCAL_MACHINE,keypath,key);
+
+    return rc;
+}
+
+UINT MSIREG_OpenComponents(HKEY* key)
+{
+    return RegCreateKeyW(HKEY_LOCAL_MACHINE,szInstaller_Components,key);
+}
+
+UINT MSIREG_OpenComponentsKey(LPCWSTR szComponent, HKEY* key, BOOL create)
+{
+    UINT rc;
+    WCHAR squished_cc[GUID_SIZE];
+    WCHAR keypath[0x200];
+
+    TRACE("%s\n",debugstr_w(szComponent));
+    squash_guid(szComponent,squished_cc);
+    TRACE("squished (%s)\n", debugstr_w(squished_cc));
+
+    sprintfW(keypath,szInstaller_Components_fmt,squished_cc);
+
+    if (create)
+        rc = RegCreateKeyW(HKEY_LOCAL_MACHINE,keypath,key);
+    else
+        rc = RegOpenKeyW(HKEY_LOCAL_MACHINE,keypath,key);
+
+    return rc;
+}
+
+UINT MSIREG_OpenUserComponentsKey(LPCWSTR szComponent, HKEY* key, BOOL create)
+{
+    UINT rc;
+    WCHAR squished_cc[GUID_SIZE];
+    WCHAR keypath[0x200];
+
+    TRACE("%s\n",debugstr_w(szComponent));
+    squash_guid(szComponent,squished_cc);
+    TRACE("squished (%s)\n", debugstr_w(squished_cc));
+
+    sprintfW(keypath,szUser_Components_fmt,squished_cc);
+
+    if (create)
+        rc = RegCreateKeyW(HKEY_CURRENT_USER,keypath,key);
+    else
+        rc = RegOpenKeyW(HKEY_CURRENT_USER,keypath,key);
+
+    return rc;
+}
+
+UINT MSIREG_OpenProductsKey(LPCWSTR szProduct, HKEY* key, BOOL create)
+{
+    UINT rc;
+    WCHAR squished_pc[GUID_SIZE];
+    WCHAR keypath[0x200];
+
+    TRACE("%s\n",debugstr_w(szProduct));
+    squash_guid(szProduct,squished_pc);
+    TRACE("squished (%s)\n", debugstr_w(squished_pc));
+
+    sprintfW(keypath,szInstaller_Products_fmt,squished_pc);
+
+    if (create)
+        rc = RegCreateKeyW(HKEY_LOCAL_MACHINE,keypath,key);
+    else
+        rc = RegOpenKeyW(HKEY_LOCAL_MACHINE,keypath,key);
+
+    return rc;
+}
+
+UINT MSIREG_OpenUpgradeCodesKey(LPCWSTR szUpgradeCode, HKEY* key, BOOL create)
+{
+    UINT rc;
+    WCHAR squished_pc[GUID_SIZE];
+    WCHAR keypath[0x200];
+
+    TRACE("%s\n",debugstr_w(szUpgradeCode));
+    squash_guid(szUpgradeCode,squished_pc);
+    TRACE("squished (%s)\n", debugstr_w(squished_pc));
+
+    sprintfW(keypath,szInstaller_UpgradeCodes_fmt,squished_pc);
+
+    if (create)
+        rc = RegCreateKeyW(HKEY_LOCAL_MACHINE,keypath,key);
+    else
+        rc = RegOpenKeyW(HKEY_LOCAL_MACHINE,keypath,key);
+
+    return rc;
+}
+
+UINT MSIREG_OpenUserUpgradeCodesKey(LPCWSTR szUpgradeCode, HKEY* key, BOOL create)
+{
+    UINT rc;
+    WCHAR squished_pc[GUID_SIZE];
+    WCHAR keypath[0x200];
+
+    TRACE("%s\n",debugstr_w(szUpgradeCode));
+    squash_guid(szUpgradeCode,squished_pc);
+    TRACE("squished (%s)\n", debugstr_w(squished_pc));
+
+    sprintfW(keypath,szInstaller_UserUpgradeCodes_fmt,squished_pc);
+
+    if (create)
+        rc = RegCreateKeyW(HKEY_CURRENT_USER,keypath,key);
+    else
+        rc = RegOpenKeyW(HKEY_CURRENT_USER,keypath,key);
+
+    return rc;
+}
+
+
+/*************************************************************************
+ *  MsiDecomposeDescriptorW   [MSI.@]
+ *
+ * Decomposes an MSI descriptor into product, feature and component parts.
+ * An MSI descriptor is a string of the form:
+ *   [base 85 guid] [feature code] '>' [base 85 guid]
+ *
+ * PARAMS
+ *   szDescriptor  [I]  the descriptor to decompose
+ *   szProduct     [O]  buffer of MAX_FEATURE_CHARS+1 for the product guid
+ *   szFeature     [O]  buffer of MAX_FEATURE_CHARS+1 for the feature code
+ *   szComponent   [O]  buffer of MAX_FEATURE_CHARS+1 for the component guid
+ *   pUsed         [O]  the length of the descriptor
+ *
+ * RETURNS
+ *   ERROR_SUCCESS             if everything worked correctly
+ *   ERROR_INVALID_PARAMETER   if the descriptor was invalid
+ *
+ */
+UINT WINAPI MsiDecomposeDescriptorW( LPCWSTR szDescriptor, LPWSTR szProduct,
+                LPWSTR szFeature, LPWSTR szComponent, DWORD *pUsed )
+{
+    UINT r, len;
+    LPWSTR p;
+    GUID product, component;
+
+    TRACE("%s %p %p %p %p\n", debugstr_w(szDescriptor), szProduct,
+          szFeature, szComponent, pUsed);
+
+    r = decode_base85_guid( szDescriptor, &product );
+    if( !r )
+        return ERROR_INVALID_PARAMETER;
+
+    TRACE("product %s\n", debugstr_guid( &product ));
+
+    p = strchrW(&szDescriptor[20],'>');
+    if( !p )
+        return ERROR_INVALID_PARAMETER;
+
+    len = (p - &szDescriptor[20]);
+    if( len > MAX_FEATURE_CHARS )
+        return ERROR_INVALID_PARAMETER;
+    if (szFeature)
+    {
+        memcpy( szFeature, &szDescriptor[20], len*sizeof(WCHAR) );
+        szFeature[len] = 0;
+    }
+
+    TRACE("feature %s\n", debugstr_w( &szDescriptor[20] ));
+
+    r = decode_base85_guid( p+1, &component );
+    if( !r )
+        return ERROR_INVALID_PARAMETER;
+
+    TRACE("component %s\n", debugstr_guid( &component ));
+
+    if (szProduct)
+        StringFromGUID2( &product, szProduct, MAX_FEATURE_CHARS+1 );
+    if (szComponent)
+        StringFromGUID2( &component, szComponent, MAX_FEATURE_CHARS+1 );
+    len = ( &p[21] - szDescriptor );
+
+    TRACE("length = %d\n", len);
+    *pUsed = len;
+
+    return ERROR_SUCCESS;
+}
+
+UINT WINAPI MsiDecomposeDescriptorA( LPCSTR szDescriptor, LPSTR szProduct,
+                LPSTR szFeature, LPSTR szComponent, DWORD *pUsed )
+{
+    WCHAR product[MAX_FEATURE_CHARS+1];
+    WCHAR feature[MAX_FEATURE_CHARS+1];
+    WCHAR component[MAX_FEATURE_CHARS+1];
+    LPWSTR str = NULL, p = NULL, f = NULL, c = NULL;
+    UINT r;
+
+    TRACE("%s %p %p %p %p\n", debugstr_a(szDescriptor), szProduct,
+          szFeature, szComponent, pUsed);
+
+    str = strdupAtoW( szDescriptor );
+    if( szDescriptor && !str )
+        return ERROR_OUTOFMEMORY;
+
+    if (szProduct)
+        p = product;
+    if (szFeature)
+        f = feature;
+    if (szComponent)
+        c = component;
+
+    r = MsiDecomposeDescriptorW( str, p, f, c, pUsed );
+
+    WideCharToMultiByte( CP_ACP, 0, p, MAX_FEATURE_CHARS+1,
+                         szProduct, MAX_FEATURE_CHARS+1, NULL, NULL );
+    WideCharToMultiByte( CP_ACP, 0, f, MAX_FEATURE_CHARS+1,
+                         szFeature, MAX_FEATURE_CHARS+1, NULL, NULL );
+    WideCharToMultiByte( CP_ACP, 0, c, MAX_FEATURE_CHARS+1,
+                         szComponent, MAX_FEATURE_CHARS+1, NULL, NULL );
+
+    msi_free( str );
+
+    return r;
+}
+
+UINT WINAPI MsiEnumProductsA(DWORD index, LPSTR lpguid)
+{
+    DWORD r;
+    WCHAR szwGuid[GUID_SIZE];
+
+    TRACE("%ld %p\n",index,lpguid);
+    
+    if (NULL == lpguid)
+        return ERROR_INVALID_PARAMETER;
+    r = MsiEnumProductsW(index, szwGuid);
+    if( r == ERROR_SUCCESS )
+        WideCharToMultiByte(CP_ACP, 0, szwGuid, -1, lpguid, GUID_SIZE, NULL, NULL);
+
+    return r;
+}
+
+UINT WINAPI MsiEnumProductsW(DWORD index, LPWSTR lpguid)
+{
+    HKEY hkeyFeatures = 0;
+    DWORD r;
+    WCHAR szKeyName[SQUISH_GUID_SIZE];
+
+    TRACE("%ld %p\n",index,lpguid);
+
+    if (NULL == lpguid)
+        return ERROR_INVALID_PARAMETER;
+
+    r = MSIREG_OpenFeatures(&hkeyFeatures);
+    if( r != ERROR_SUCCESS )
+        return ERROR_NO_MORE_ITEMS;
+
+    r = RegEnumKeyW(hkeyFeatures, index, szKeyName, SQUISH_GUID_SIZE);
+    if( r == ERROR_SUCCESS )
+        unsquash_guid(szKeyName, lpguid);
+    RegCloseKey(hkeyFeatures);
+
+    return r;
+}
+
+UINT WINAPI MsiEnumFeaturesA(LPCSTR szProduct, DWORD index, 
+      LPSTR szFeature, LPSTR szParent)
+{
+    DWORD r;
+    WCHAR szwFeature[GUID_SIZE], szwParent[GUID_SIZE];
+    LPWSTR szwProduct = NULL;
+
+    TRACE("%s %ld %p %p\n",debugstr_a(szProduct),index,szFeature,szParent);
+
+    if( szProduct )
+    {
+        szwProduct = strdupAtoW( szProduct );
+        if( !szwProduct )
+            return ERROR_OUTOFMEMORY;
+    }
+
+    r = MsiEnumFeaturesW(szwProduct, index, szwFeature, szwParent);
+    if( r == ERROR_SUCCESS )
+    {
+        WideCharToMultiByte(CP_ACP, 0, szwFeature, -1,
+                            szFeature, GUID_SIZE, NULL, NULL);
+        WideCharToMultiByte(CP_ACP, 0, szwParent, -1,
+                            szParent, GUID_SIZE, NULL, NULL);
+    }
+
+    msi_free( szwProduct);
+
+    return r;
+}
+
+UINT WINAPI MsiEnumFeaturesW(LPCWSTR szProduct, DWORD index, 
+      LPWSTR szFeature, LPWSTR szParent)
+{
+    HKEY hkeyProduct = 0;
+    DWORD r, sz;
+
+    TRACE("%s %ld %p %p\n",debugstr_w(szProduct),index,szFeature,szParent);
+
+    r = MSIREG_OpenFeaturesKey(szProduct,&hkeyProduct,FALSE);
+    if( r != ERROR_SUCCESS )
+        return ERROR_NO_MORE_ITEMS;
+
+    sz = GUID_SIZE;
+    r = RegEnumValueW(hkeyProduct, index, szFeature, &sz, NULL, NULL, NULL, NULL);
+    RegCloseKey(hkeyProduct);
+
+    return r;
+}
+
+UINT WINAPI MsiEnumComponentsA(DWORD index, LPSTR lpguid)
+{
+    DWORD r;
+    WCHAR szwGuid[GUID_SIZE];
+
+    TRACE("%ld %p\n",index,lpguid);
+
+    r = MsiEnumComponentsW(index, szwGuid);
+    if( r == ERROR_SUCCESS )
+        WideCharToMultiByte(CP_ACP, 0, szwGuid, -1, lpguid, GUID_SIZE, NULL, NULL);
+
+    return r;
+}
+
+UINT WINAPI MsiEnumComponentsW(DWORD index, LPWSTR lpguid)
+{
+    HKEY hkeyComponents = 0;
+    DWORD r;
+    WCHAR szKeyName[SQUISH_GUID_SIZE];
+
+    TRACE("%ld %p\n",index,lpguid);
+
+    r = MSIREG_OpenComponents(&hkeyComponents);
+    if( r != ERROR_SUCCESS )
+        return ERROR_NO_MORE_ITEMS;
+
+    r = RegEnumKeyW(hkeyComponents, index, szKeyName, SQUISH_GUID_SIZE);
+    if( r == ERROR_SUCCESS )
+        unsquash_guid(szKeyName, lpguid);
+    RegCloseKey(hkeyComponents);
+
+    return r;
+}
+
+UINT WINAPI MsiEnumClientsA(LPCSTR szComponent, DWORD index, LPSTR szProduct)
+{
+    DWORD r;
+    WCHAR szwProduct[GUID_SIZE];
+    LPWSTR szwComponent = NULL;
+
+    TRACE("%s %ld %p\n",debugstr_a(szComponent),index,szProduct);
+
+    if( szComponent )
+    {
+        szwComponent = strdupAtoW( szComponent );
+        if( !szwComponent )
+            return ERROR_OUTOFMEMORY;
+    }
+
+    r = MsiEnumClientsW(szComponent?szwComponent:NULL, index, szwProduct);
+    if( r == ERROR_SUCCESS )
+    {
+        WideCharToMultiByte(CP_ACP, 0, szwProduct, -1,
+                            szProduct, GUID_SIZE, NULL, NULL);
+    }
+
+    msi_free( szwComponent);
+
+    return r;
+}
+
+UINT WINAPI MsiEnumClientsW(LPCWSTR szComponent, DWORD index, LPWSTR szProduct)
+{
+    HKEY hkeyComp = 0;
+    DWORD r, sz;
+    WCHAR szValName[SQUISH_GUID_SIZE];
+
+    TRACE("%s %ld %p\n",debugstr_w(szComponent),index,szProduct);
+
+    r = MSIREG_OpenComponentsKey(szComponent,&hkeyComp,FALSE);
+    if( r != ERROR_SUCCESS )
+        return ERROR_NO_MORE_ITEMS;
+
+    sz = SQUISH_GUID_SIZE;
+    r = RegEnumValueW(hkeyComp, index, szValName, &sz, NULL, NULL, NULL, NULL);
+    if( r == ERROR_SUCCESS )
+        unsquash_guid(szValName, szProduct);
+
+    RegCloseKey(hkeyComp);
+
+    return r;
+}
+
+UINT WINAPI MSI_EnumComponentQualifiers( LPCWSTR szComponent, DWORD iIndex,
+                awstring *lpQualBuf, DWORD* pcchQual,
+                awstring *lpAppBuf, DWORD* pcchAppBuf )
+{
+    DWORD name_sz, val_sz, name_max, val_max, type, ofs;
+    LPWSTR name = NULL, val = NULL;
+    UINT r, r2;
+    HKEY key;
+
+    TRACE("%s %08lx %p %p %p %p\n", debugstr_w(szComponent), iIndex,
+          lpQualBuf, pcchQual, lpAppBuf, pcchAppBuf);
+
+    if (!szComponent)
+        return ERROR_INVALID_PARAMETER;
+
+    r = MSIREG_OpenUserComponentsKey( szComponent, &key, FALSE );
+    if (r != ERROR_SUCCESS)
+        return ERROR_UNKNOWN_COMPONENT;
+
+    /* figure out how big the name is we want to return */
+    name_max = 0x10;
+    r = ERROR_OUTOFMEMORY;
+    name = msi_alloc( name_max * sizeof(WCHAR) );
+    if (!name)
+        goto end;
+
+    val_max = 0x10;
+    r = ERROR_OUTOFMEMORY;
+    val = msi_alloc( val_max );
+    if (!val)
+        goto end;
+
+    /* loop until we allocate enough memory */
+    while (1)
+    {
+        name_sz = name_max;
+        val_sz = val_max;
+        r = RegEnumValueW( key, iIndex, name, &name_sz,
+                           NULL, &type, (LPBYTE)val, &val_sz );
+        if (r == ERROR_SUCCESS)
+            break;
+        if (r != ERROR_MORE_DATA)
+            goto end;
+        if (type != REG_MULTI_SZ)
+        {
+            ERR("component data has wrong type (%ld)\n", type);
+            goto end;
+        }
+
+        r = ERROR_OUTOFMEMORY;
+        if ((name_sz+1) >= name_max)
+        {
+            name_max *= 2;
+            msi_free( name );
+            name = msi_alloc( name_max * sizeof (WCHAR) );
+            if (!name)
+                goto end;
+            continue;
+        }
+        if (val_sz > val_max)
+        {
+            val_max = val_sz + sizeof (WCHAR);
+            val = msi_alloc( val_max * sizeof (WCHAR) );
+            if (!val)
+                goto end;
+            continue;
+        }
+        ERR("should be enough data, but isn't %ld %ld\n", name_sz, val_sz );
+        goto end;
+    }
+
+    ofs = 0;
+    r = MsiDecomposeDescriptorW( val, NULL, NULL, NULL, &ofs );
+    if (r != ERROR_SUCCESS)
+        goto end;
+
+    TRACE("Providing %s and %s\n", debugstr_w(name), debugstr_w(val+ofs));
+
+    r = msi_strcpy_to_awstring( name, lpQualBuf, pcchQual );
+    r2 = msi_strcpy_to_awstring( val+ofs, lpAppBuf, pcchAppBuf );
+
+    if (r2 != ERROR_SUCCESS)
+        r = r2;
+
+end:
+    msi_free(val);
+    msi_free(name);
+    RegCloseKey(key);
+
+    return r;
+}
+
+/*************************************************************************
+ *  MsiEnumComponentQualifiersA [MSI.@]
+ */
+UINT WINAPI MsiEnumComponentQualifiersA( LPCSTR szComponent, DWORD iIndex,
+                LPSTR lpQualifierBuf, DWORD* pcchQualifierBuf,
+                LPSTR lpApplicationDataBuf, DWORD* pcchApplicationDataBuf )
+{
+    awstring qual, appdata;
+    LPWSTR comp;
+    UINT r;
+
+    TRACE("%s %08lx %p %p %p %p\n", debugstr_a(szComponent), iIndex,
+          lpQualifierBuf, pcchQualifierBuf, lpApplicationDataBuf,
+          pcchApplicationDataBuf);
+
+    comp = strdupAtoW( szComponent );
+    if (szComponent && !comp)
+        return ERROR_OUTOFMEMORY;
+
+    qual.unicode = FALSE;
+    qual.str.a = lpQualifierBuf;
+
+    appdata.unicode = FALSE;
+    appdata.str.a = lpApplicationDataBuf;
+
+    r = MSI_EnumComponentQualifiers( comp, iIndex,
+              &qual, pcchQualifierBuf, &appdata, pcchApplicationDataBuf );
+    msi_free( comp );
+    return r;
+}
+
+/*************************************************************************
+ *  MsiEnumComponentQualifiersW [MSI.@]
+ */
+UINT WINAPI MsiEnumComponentQualifiersW( LPCWSTR szComponent, DWORD iIndex,
+                LPWSTR lpQualifierBuf, DWORD* pcchQualifierBuf,
+                LPWSTR lpApplicationDataBuf, DWORD* pcchApplicationDataBuf )
+{
+    awstring qual, appdata;
+
+    TRACE("%s %08lx %p %p %p %p\n", debugstr_w(szComponent), iIndex,
+          lpQualifierBuf, pcchQualifierBuf, lpApplicationDataBuf,
+          pcchApplicationDataBuf);
+
+    qual.unicode = TRUE;
+    qual.str.w = lpQualifierBuf;
+
+    appdata.unicode = TRUE;
+    appdata.str.w = lpApplicationDataBuf;
+
+    return MSI_EnumComponentQualifiers( szComponent, iIndex,
+                 &qual, pcchQualifierBuf, &appdata, pcchApplicationDataBuf );
+}
+
+/*************************************************************************
+ *  MsiEnumRelatedProductsW   [MSI.@]
+ *
+ */
+UINT WINAPI MsiEnumRelatedProductsW(LPCWSTR szUpgradeCode, DWORD dwReserved,
+                                    DWORD iProductIndex, LPWSTR lpProductBuf)
+{
+    UINT r;
+    HKEY hkey;
+    WCHAR szKeyName[SQUISH_GUID_SIZE];
+
+    TRACE("%s %lu %lu %p\n", debugstr_w(szUpgradeCode), dwReserved,
+          iProductIndex, lpProductBuf);
+
+    if (NULL == szUpgradeCode)
+        return ERROR_INVALID_PARAMETER;
+    if (NULL == lpProductBuf)
+        return ERROR_INVALID_PARAMETER;
+
+    r = MSIREG_OpenUpgradeCodesKey(szUpgradeCode, &hkey, FALSE);
+    if (r != ERROR_SUCCESS)
+        return ERROR_NO_MORE_ITEMS;
+
+    r = RegEnumKeyW(hkey, iProductIndex, szKeyName, SQUISH_GUID_SIZE);
+    if( r == ERROR_SUCCESS )
+        unsquash_guid(szKeyName, lpProductBuf);
+    RegCloseKey(hkey);
+
+    return r;
+}
+
+/*************************************************************************
+ *  MsiEnumRelatedProductsA   [MSI.@]
+ *
+ */
+UINT WINAPI MsiEnumRelatedProductsA(LPCSTR szUpgradeCode, DWORD dwReserved,
+                                    DWORD iProductIndex, LPSTR lpProductBuf)
+{
+    LPWSTR szwUpgradeCode = NULL;
+    WCHAR productW[GUID_SIZE];
+    UINT r;
+
+    TRACE("%s %lu %lu %p\n", debugstr_a(szUpgradeCode), dwReserved,
+          iProductIndex, lpProductBuf);
+
+    if (szUpgradeCode)
+    {
+        szwUpgradeCode = strdupAtoW( szUpgradeCode );
+        if( !szwUpgradeCode )
+            return ERROR_OUTOFMEMORY;
+    }
+
+    r = MsiEnumRelatedProductsW( szwUpgradeCode, dwReserved,
+                                 iProductIndex, productW );
+    if (r == ERROR_SUCCESS)
+    {
+        WideCharToMultiByte( CP_ACP, 0, productW, GUID_SIZE,
+                             lpProductBuf, GUID_SIZE, NULL, NULL );
+    }
+    msi_free( szwUpgradeCode);
+    return r;
+}
diff --git a/reactos/dll/msi/regsvr.c b/reactos/dll/msi/regsvr.c
new file mode 100644 (file)
index 0000000..f49b78a
--- /dev/null
@@ -0,0 +1,626 @@
+/*
+ *     self-registerable dll functions for msi.dll
+ *
+ * Copyright (C) 2004 Raphael Junqueira
+ *
+ * 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 <string.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winuser.h"
+#include "winreg.h"
+#include "winerror.h"
+
+#include "ole2.h"
+#include "olectl.h"
+
+#include "wine/debug.h"
+
+#include "msi.h"
+#include "initguid.h"
+#include "msipriv.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+/*
+ * Near the bottom of this file are the exported DllRegisterServer and
+ * DllUnregisterServer, which make all this worthwhile.
+ */
+
+/***********************************************************************
+ *             interface for self-registering
+ */
+struct regsvr_interface {
+    IID const *iid;            /* NULL for end of list */
+    LPCSTR name;               /* can be NULL to omit */
+    IID const *base_iid;       /* can be NULL to omit */
+    int num_methods;           /* can be <0 to omit */
+    CLSID const *ps_clsid;     /* can be NULL to omit */
+    CLSID const *ps_clsid32;   /* can be NULL to omit */
+};
+
+static HRESULT register_interfaces(struct regsvr_interface const *list);
+static HRESULT unregister_interfaces(struct regsvr_interface const *list);
+
+/**
+ * @todo: maybe adding typelibs support here
+ * [Software\\Classes\\CLSID\\{000C1090-0000-0000-C000-000000000046}\\TypeLib] 1080380217
+ * @="{000C1092-0000-0000-C000-000000000046}"
+ */
+struct regsvr_coclass {
+    CLSID const *clsid;                /* NULL for end of list */
+    LPCSTR name;               /* can be NULL to omit */
+    LPCSTR iph32;              /* can be NULL to omit */
+    LPCSTR ips;                        /* can be NULL to omit */
+    LPCSTR ips32;              /* can be NULL to omit */
+    LPCSTR ips32_tmodel;       /* can be NULL to omit, if apartment, iph32 must be set */
+    LPCSTR progid;             /* can be NULL to omit */
+    LPCSTR viprogid;           /* can be NULL to omit */
+    LPCSTR progid_extra;       /* can be NULL to omit */
+};
+
+static HRESULT register_coclasses(struct regsvr_coclass const *list);
+static HRESULT unregister_coclasses(struct regsvr_coclass const *list);
+
+/***********************************************************************
+ *             static string constants
+ */
+static WCHAR const interface_keyname[10] = {
+    'I', 'n', 't', 'e', 'r', 'f', 'a', 'c', 'e', 0 };
+static WCHAR const base_ifa_keyname[14] = {
+    'B', 'a', 's', 'e', 'I', 'n', 't', 'e', 'r', 'f', 'a', 'c',
+    'e', 0 };
+static WCHAR const num_methods_keyname[11] = {
+    'N', 'u', 'm', 'M', 'e', 't', 'h', 'o', 'd', 's', 0 };
+static WCHAR const ps_clsid_keyname[15] = {
+    'P', 'r', 'o', 'x', 'y', 'S', 't', 'u', 'b', 'C', 'l', 's',
+    'i', 'd', 0 };
+static WCHAR const ps_clsid32_keyname[17] = {
+    'P', 'r', 'o', 'x', 'y', 'S', 't', 'u', 'b', 'C', 'l', 's',
+    'i', 'd', '3', '2', 0 };
+static WCHAR const clsid_keyname[6] = {
+    'C', 'L', 'S', 'I', 'D', 0 };
+static WCHAR const curver_keyname[7] = {
+    'C', 'u', 'r', 'V', 'e', 'r', 0 };
+static WCHAR const iph32_keyname[] = {
+    'I', 'n', 'P', 'r', 'o', 'c', 'H', 'a', 'n', 'd', 'l', 'e', 'r',
+    '3', '2', 0 };
+static WCHAR const ips_keyname[13] = {
+    'I', 'n', 'P', 'r', 'o', 'c', 'S', 'e', 'r', 'v', 'e', 'r',
+    0 };
+static WCHAR const ips32_keyname[15] = {
+    'I', 'n', 'P', 'r', 'o', 'c', 'S', 'e', 'r', 'v', 'e', 'r',
+    '3', '2', 0 };
+static WCHAR const progid_keyname[7] = {
+    'P', 'r', 'o', 'g', 'I', 'D', 0 };
+static WCHAR const viprogid_keyname[25] = {
+    'V', 'e', 'r', 's', 'i', 'o', 'n', 'I', 'n', 'd', 'e', 'p',
+    'e', 'n', 'd', 'e', 'n', 't', 'P', 'r', 'o', 'g', 'I', 'D',
+    0 };
+static char const tmodel_valuename[] = "ThreadingModel";
+
+/***********************************************************************
+ *             static helper functions
+ */
+static LONG register_key_guid(HKEY base, WCHAR const *name, GUID const *guid);
+static LONG register_key_defvalueW(HKEY base, WCHAR const *name,
+                                  WCHAR const *value);
+static LONG register_key_defvalueA(HKEY base, WCHAR const *name,
+                                  char const *value);
+static LONG register_progid(WCHAR const *clsid,
+                           char const *progid, char const *curver_progid,
+                           char const *name, char const *extra);
+static LONG recursive_delete_key(HKEY key);
+static LONG recursive_delete_keyA(HKEY base, char const *name);
+static LONG recursive_delete_keyW(HKEY base, WCHAR const *name);
+
+/***********************************************************************
+ *             register_interfaces
+ */
+static HRESULT register_interfaces(struct regsvr_interface const *list) {
+    LONG res = ERROR_SUCCESS;
+    HKEY interface_key;
+
+    res = RegCreateKeyExW(HKEY_CLASSES_ROOT, interface_keyname, 0, NULL, 0,
+                         KEY_READ | KEY_WRITE, NULL, &interface_key, NULL);
+    if (res != ERROR_SUCCESS) goto error_return;
+
+    for (; res == ERROR_SUCCESS && list->iid; ++list) {
+       WCHAR buf[39];
+       HKEY iid_key;
+
+       StringFromGUID2(list->iid, buf, 39);
+       res = RegCreateKeyExW(interface_key, buf, 0, NULL, 0,
+                             KEY_READ | KEY_WRITE, NULL, &iid_key, NULL);
+       if (res != ERROR_SUCCESS) goto error_close_interface_key;
+
+       if (list->name) {
+           res = RegSetValueExA(iid_key, NULL, 0, REG_SZ,
+                                (CONST BYTE*)(list->name),
+                                strlen(list->name) + 1);
+           if (res != ERROR_SUCCESS) goto error_close_iid_key;
+       }
+
+       if (list->base_iid) {
+           register_key_guid(iid_key, base_ifa_keyname, list->base_iid);
+           if (res != ERROR_SUCCESS) goto error_close_iid_key;
+       }
+
+       if (0 <= list->num_methods) {
+           static WCHAR const fmt[3] = { '%', 'd', 0 };
+           HKEY key;
+
+           res = RegCreateKeyExW(iid_key, num_methods_keyname, 0, NULL, 0,
+                                 KEY_READ | KEY_WRITE, NULL, &key, NULL);
+           if (res != ERROR_SUCCESS) goto error_close_iid_key;
+
+           wsprintfW(buf, fmt, list->num_methods);
+           res = RegSetValueExW(key, NULL, 0, REG_SZ,
+                                (CONST BYTE*)buf,
+                                (lstrlenW(buf) + 1) * sizeof(WCHAR));
+           RegCloseKey(key);
+
+           if (res != ERROR_SUCCESS) goto error_close_iid_key;
+       }
+
+       if (list->ps_clsid) {
+           register_key_guid(iid_key, ps_clsid_keyname, list->ps_clsid);
+           if (res != ERROR_SUCCESS) goto error_close_iid_key;
+       }
+
+       if (list->ps_clsid32) {
+           register_key_guid(iid_key, ps_clsid32_keyname, list->ps_clsid32);
+           if (res != ERROR_SUCCESS) goto error_close_iid_key;
+       }
+
+    error_close_iid_key:
+       RegCloseKey(iid_key);
+    }
+
+error_close_interface_key:
+    RegCloseKey(interface_key);
+error_return:
+    return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK;
+}
+
+/***********************************************************************
+ *             unregister_interfaces
+ */
+static HRESULT unregister_interfaces(struct regsvr_interface const *list) {
+    LONG res = ERROR_SUCCESS;
+    HKEY interface_key;
+
+    res = RegOpenKeyExW(HKEY_CLASSES_ROOT, interface_keyname, 0,
+                       KEY_READ | KEY_WRITE, &interface_key);
+    if (res == ERROR_FILE_NOT_FOUND) return S_OK;
+    if (res != ERROR_SUCCESS) goto error_return;
+
+    for (; res == ERROR_SUCCESS && list->iid; ++list) {
+       WCHAR buf[39];
+
+       StringFromGUID2(list->iid, buf, 39);
+       res = recursive_delete_keyW(interface_key, buf);
+    }
+
+    RegCloseKey(interface_key);
+error_return:
+    return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK;
+}
+
+/***********************************************************************
+ *             register_coclasses
+ */
+static HRESULT register_coclasses(struct regsvr_coclass const *list) {
+    LONG res = ERROR_SUCCESS;
+    HKEY coclass_key;
+
+    res = RegCreateKeyExW(HKEY_CLASSES_ROOT, clsid_keyname, 0, NULL, 0,
+                         KEY_READ | KEY_WRITE, NULL, &coclass_key, NULL);
+    if (res != ERROR_SUCCESS) goto error_return;
+
+    for (; res == ERROR_SUCCESS && list->clsid; ++list) {
+       WCHAR buf[39];
+       HKEY clsid_key;
+
+       StringFromGUID2(list->clsid, buf, 39);
+       res = RegCreateKeyExW(coclass_key, buf, 0, NULL, 0,
+                             KEY_READ | KEY_WRITE, NULL, &clsid_key, NULL);
+       if (res != ERROR_SUCCESS) goto error_close_coclass_key;
+
+       if (list->name) {
+           res = RegSetValueExA(clsid_key, NULL, 0, REG_SZ,
+                                (CONST BYTE*)(list->name),
+                                strlen(list->name) + 1);
+           if (res != ERROR_SUCCESS) goto error_close_clsid_key;
+       }
+
+       if (list->iph32) {
+           HKEY iph32_key;
+
+           res = RegCreateKeyExW(clsid_key, iph32_keyname, 0, NULL, 0,
+                                 KEY_READ | KEY_WRITE, NULL,
+                                 &iph32_key, NULL);
+           if (res != ERROR_SUCCESS) goto error_close_clsid_key;
+
+           res = RegSetValueExA(iph32_key, NULL, 0, REG_SZ,
+                                (CONST BYTE*)list->iph32,
+                                lstrlenA(list->iph32) + 1);
+           RegCloseKey(iph32_key);
+           if (res != ERROR_SUCCESS) goto error_close_clsid_key;
+       }
+
+       if (list->ips) {
+           res = register_key_defvalueA(clsid_key, ips_keyname, list->ips);
+           if (res != ERROR_SUCCESS) goto error_close_clsid_key;
+       }
+
+       if (list->ips32) {
+           HKEY ips32_key;
+
+           res = RegCreateKeyExW(clsid_key, ips32_keyname, 0, NULL, 0,
+                                 KEY_READ | KEY_WRITE, NULL,
+                                 &ips32_key, NULL);
+           if (res != ERROR_SUCCESS) goto error_close_clsid_key;
+
+           res = RegSetValueExA(ips32_key, NULL, 0, REG_SZ,
+                                (CONST BYTE*)list->ips32,
+                                lstrlenA(list->ips32) + 1);
+           if (res == ERROR_SUCCESS && list->ips32_tmodel)
+               res = RegSetValueExA(ips32_key, tmodel_valuename, 0, REG_SZ,
+                                    (CONST BYTE*)list->ips32_tmodel,
+                                    strlen(list->ips32_tmodel) + 1);
+           RegCloseKey(ips32_key);
+           if (res != ERROR_SUCCESS) goto error_close_clsid_key;
+       }
+
+       if (list->progid) {
+           res = register_key_defvalueA(clsid_key, progid_keyname,
+                                        list->progid);
+           if (res != ERROR_SUCCESS) goto error_close_clsid_key;
+
+           res = register_progid(buf, list->progid, NULL,
+                                 list->name, list->progid_extra);
+           if (res != ERROR_SUCCESS) goto error_close_clsid_key;
+       }
+
+       if (list->viprogid) {
+           res = register_key_defvalueA(clsid_key, viprogid_keyname,
+                                        list->viprogid);
+           if (res != ERROR_SUCCESS) goto error_close_clsid_key;
+
+           res = register_progid(buf, list->viprogid, list->progid,
+                                 list->name, list->progid_extra);
+           if (res != ERROR_SUCCESS) goto error_close_clsid_key;
+       }
+
+    error_close_clsid_key:
+       RegCloseKey(clsid_key);
+    }
+
+error_close_coclass_key:
+    RegCloseKey(coclass_key);
+error_return:
+    return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK;
+}
+
+/***********************************************************************
+ *             unregister_coclasses
+ */
+static HRESULT unregister_coclasses(struct regsvr_coclass const *list) {
+    LONG res = ERROR_SUCCESS;
+    HKEY coclass_key;
+
+    res = RegOpenKeyExW(HKEY_CLASSES_ROOT, clsid_keyname, 0,
+                       KEY_READ | KEY_WRITE, &coclass_key);
+    if (res == ERROR_FILE_NOT_FOUND) return S_OK;
+    if (res != ERROR_SUCCESS) goto error_return;
+
+    for (; res == ERROR_SUCCESS && list->clsid; ++list) {
+       WCHAR buf[39];
+
+       StringFromGUID2(list->clsid, buf, 39);
+       res = recursive_delete_keyW(coclass_key, buf);
+       if (res != ERROR_SUCCESS) goto error_close_coclass_key;
+
+       if (list->progid) {
+           res = recursive_delete_keyA(HKEY_CLASSES_ROOT, list->progid);
+           if (res != ERROR_SUCCESS) goto error_close_coclass_key;
+       }
+
+       if (list->viprogid) {
+           res = recursive_delete_keyA(HKEY_CLASSES_ROOT, list->viprogid);
+           if (res != ERROR_SUCCESS) goto error_close_coclass_key;
+       }
+    }
+
+error_close_coclass_key:
+    RegCloseKey(coclass_key);
+error_return:
+    return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK;
+}
+
+/***********************************************************************
+ *             regsvr_key_guid
+ */
+static LONG register_key_guid(HKEY base, WCHAR const *name, GUID const *guid) {
+    WCHAR buf[39];
+
+    StringFromGUID2(guid, buf, 39);
+    return register_key_defvalueW(base, name, buf);
+}
+
+/***********************************************************************
+ *             regsvr_key_defvalueW
+ */
+static LONG register_key_defvalueW(
+    HKEY base,
+    WCHAR const *name,
+    WCHAR const *value) {
+    LONG res;
+    HKEY key;
+
+    res = RegCreateKeyExW(base, name, 0, NULL, 0,
+                         KEY_READ | KEY_WRITE, NULL, &key, NULL);
+    if (res != ERROR_SUCCESS) return res;
+    res = RegSetValueExW(key, NULL, 0, REG_SZ, (CONST BYTE*)value,
+                        (lstrlenW(value) + 1) * sizeof(WCHAR));
+    RegCloseKey(key);
+    return res;
+}
+
+/***********************************************************************
+ *             regsvr_key_defvalueA
+ */
+static LONG register_key_defvalueA(
+    HKEY base,
+    WCHAR const *name,
+    char const *value) {
+    LONG res;
+    HKEY key;
+
+    res = RegCreateKeyExW(base, name, 0, NULL, 0,
+                         KEY_READ | KEY_WRITE, NULL, &key, NULL);
+    if (res != ERROR_SUCCESS) return res;
+    res = RegSetValueExA(key, NULL, 0, REG_SZ, (CONST BYTE*)value,
+                        lstrlenA(value) + 1);
+    RegCloseKey(key);
+    return res;
+}
+
+/***********************************************************************
+ *             regsvr_progid
+ */
+static LONG register_progid(
+    WCHAR const *clsid,
+    char const *progid,
+    char const *curver_progid,
+    char const *name,
+    char const *extra) {
+    LONG res;
+    HKEY progid_key;
+
+    res = RegCreateKeyExA(HKEY_CLASSES_ROOT, progid, 0,
+                         NULL, 0, KEY_READ | KEY_WRITE, NULL,
+                         &progid_key, NULL);
+    if (res != ERROR_SUCCESS) return res;
+
+    if (name) {
+       res = RegSetValueExA(progid_key, NULL, 0, REG_SZ,
+                            (CONST BYTE*)name, strlen(name) + 1);
+       if (res != ERROR_SUCCESS) goto error_close_progid_key;
+    }
+
+    if (clsid) {
+       res = register_key_defvalueW(progid_key, clsid_keyname, clsid);
+       if (res != ERROR_SUCCESS) goto error_close_progid_key;
+    }
+
+    if (curver_progid) {
+       res = register_key_defvalueA(progid_key, curver_keyname,
+                                    curver_progid);
+       if (res != ERROR_SUCCESS) goto error_close_progid_key;
+    }
+
+    if (extra) {
+       HKEY extra_key;
+
+       res = RegCreateKeyExA(progid_key, extra, 0,
+                             NULL, 0, KEY_READ | KEY_WRITE, NULL,
+                             &extra_key, NULL);
+       if (res == ERROR_SUCCESS)
+           RegCloseKey(extra_key);
+    }
+
+error_close_progid_key:
+    RegCloseKey(progid_key);
+    return res;
+}
+
+/***********************************************************************
+ *             recursive_delete_key
+ */
+static LONG recursive_delete_key(HKEY key) {
+    LONG res;
+    WCHAR subkey_name[MAX_PATH];
+    DWORD cName;
+    HKEY subkey;
+
+    for (;;) {
+       cName = sizeof(subkey_name) / sizeof(WCHAR);
+       res = RegEnumKeyExW(key, 0, subkey_name, &cName,
+                           NULL, NULL, NULL, NULL);
+       if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA) {
+           res = ERROR_SUCCESS; /* presumably we're done enumerating */
+           break;
+       }
+       res = RegOpenKeyExW(key, subkey_name, 0,
+                           KEY_READ | KEY_WRITE, &subkey);
+       if (res == ERROR_FILE_NOT_FOUND) continue;
+       if (res != ERROR_SUCCESS) break;
+
+       res = recursive_delete_key(subkey);
+       RegCloseKey(subkey);
+       if (res != ERROR_SUCCESS) break;
+    }
+
+    if (res == ERROR_SUCCESS) res = RegDeleteKeyW(key, 0);
+    return res;
+}
+
+/***********************************************************************
+ *             recursive_delete_keyA
+ */
+static LONG recursive_delete_keyA(HKEY base, char const *name) {
+    LONG res;
+    HKEY key;
+
+    res = RegOpenKeyExA(base, name, 0, KEY_READ | KEY_WRITE, &key);
+    if (res == ERROR_FILE_NOT_FOUND) return ERROR_SUCCESS;
+    if (res != ERROR_SUCCESS) return res;
+    res = recursive_delete_key(key);
+    RegCloseKey(key);
+    return res;
+}
+
+/***********************************************************************
+ *             recursive_delete_keyW
+ */
+static LONG recursive_delete_keyW(HKEY base, WCHAR const *name) {
+    LONG res;
+    HKEY key;
+
+    res = RegOpenKeyExW(base, name, 0, KEY_READ | KEY_WRITE, &key);
+    if (res == ERROR_FILE_NOT_FOUND) return ERROR_SUCCESS;
+    if (res != ERROR_SUCCESS) return res;
+    res = recursive_delete_key(key);
+    RegCloseKey(key);
+    return res;
+}
+
+/***********************************************************************
+ *             coclass list
+ */
+static struct regsvr_coclass const coclass_list[] = {
+    {     
+        &CLSID_IMsiServer,
+       "Msi install server",
+       "ole32.dll",
+       NULL,
+       "msi.dll",
+       "Apartment",
+       "WindowsInstaller.Installer",
+       NULL
+    },    
+    {     
+        &CLSID_IMsiServerMessage,
+       "Wine Installer Message RPC",
+       NULL,
+       NULL,
+       "msi.dll",
+       NULL,
+       "WindowsInstaller.Message",
+       NULL
+    },
+    {     
+        &CLSID_IMsiServerX1,
+       "Msi install server",
+       "ole32.dll",
+       NULL,
+       "msi.dll",
+       "Apartment",
+       "WindowsInstaller.Installer",
+       NULL
+    },
+    {     
+        &CLSID_IMsiServerX2,
+       "Msi install server",
+       "ole32.dll",
+       NULL,
+       "msi.dll",
+       "Apartment",
+       "WindowsInstaller.Installer",
+       NULL
+    },
+    {     
+        &CLSID_IMsiServerX3,
+       "Msi install server",
+       "ole32.dll",
+       NULL,
+       "msi.dll",
+       "Apartment",
+       "WindowsInstaller.Installer",
+        NULL
+    },
+    { NULL }                   /* list terminator */
+};
+
+/***********************************************************************
+ *             interface list
+ */
+/*
+ * we should declare: (@see ole32/regsvr.c for examples)
+ [-HKEY_CLASSES_ROOT\Interface\{000C101C-0000-0000-C000-000000000046}] 
+ [-HKEY_CLASSES_ROOT\Interface\{000C101D-0000-0000-C000-000000000046}] 
+ [-HKEY_CLASSES_ROOT\Interface\{000C1025-0000-0000-C000-000000000046}] 
+ [-HKEY_CLASSES_ROOT\Interface\{000C1033-0000-0000-C000-000000000046}] 
+ [-HKEY_CLASSES_ROOT\Interface\{000C1090-0000-0000-C000-000000000046}] 
+ [-HKEY_CLASSES_ROOT\Interface\{000C1093-0000-0000-C000-000000000046}] 
+ [-HKEY_CLASSES_ROOT\Interface\{000C1095-0000-0000-C000-000000000046}] 
+ [-HKEY_CLASSES_ROOT\Interface\{000C109A-0000-0000-C000-000000000046}] 
+ [-HKEY_CLASSES_ROOT\Interface\{000C109B-0000-0000-C000-000000000046}] 
+ [-HKEY_CLASSES_ROOT\Interface\{000C109C-0000-0000-C000-000000000046}] 
+ [-HKEY_CLASSES_ROOT\Interface\{000C109D-0000-0000-C000-000000000046}] 
+ [-HKEY_CLASSES_ROOT\Interface\{000C109E-0000-0000-C000-000000000046}] 
+ [-HKEY_CLASSES_ROOT\Interface\{000C109F-0000-0000-C000-000000000046}]
+*/
+static struct regsvr_interface const interface_list[] = {
+    { NULL }                   /* list terminator */
+};
+
+/***********************************************************************
+ *             DllRegisterServer (MSI.@)
+ */
+HRESULT WINAPI DllRegisterServer(void)
+{
+    HRESULT hr;
+
+    TRACE("\n");
+
+    hr = register_coclasses(coclass_list);
+    if (SUCCEEDED(hr))
+       hr = register_interfaces(interface_list);
+    return hr;
+}
+
+/***********************************************************************
+ *             DllUnregisterServer (MSI.@)
+ */
+HRESULT WINAPI DllUnregisterServer(void)
+{
+    HRESULT hr;
+
+    TRACE("\n");
+
+    hr = unregister_coclasses(coclass_list);
+    if (SUCCEEDED(hr))
+       hr = unregister_interfaces(interface_list);
+    return hr;
+}
diff --git a/reactos/dll/msi/select.c b/reactos/dll/msi/select.c
new file mode 100644 (file)
index 0000000..9bc2b96
--- /dev/null
@@ -0,0 +1,288 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2002-2004 Mike McCormack for CodeWeavers
+ *
+ * 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 <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "wine/debug.h"
+#include "msi.h"
+#include "msiquery.h"
+#include "objbase.h"
+#include "objidl.h"
+#include "msipriv.h"
+#include "winnls.h"
+
+#include "query.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msidb);
+
+
+/* below is the query interface to a table */
+
+typedef struct tagMSISELECTVIEW
+{
+    MSIVIEW        view;
+    MSIDATABASE   *db;
+    MSIVIEW       *table;
+    UINT           num_cols;
+    UINT           max_cols;
+    UINT           cols[1];
+} MSISELECTVIEW;
+
+static UINT SELECT_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
+{
+    MSISELECTVIEW *sv = (MSISELECTVIEW*)view;
+
+    TRACE("%p %d %d %p\n", sv, row, col, val );
+
+    if( !sv->table )
+         return ERROR_FUNCTION_FAILED;
+
+    if( (col==0) || (col>sv->num_cols) )
+         return ERROR_FUNCTION_FAILED;
+
+    col = sv->cols[ col - 1 ];
+
+    return sv->table->ops->fetch_int( sv->table, row, col, val );
+}
+
+static UINT SELECT_fetch_stream( struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm)
+{
+    MSISELECTVIEW *sv = (MSISELECTVIEW*)view;
+
+    TRACE("%p %d %d %p\n", sv, row, col, stm );
+
+    if( !sv->table )
+         return ERROR_FUNCTION_FAILED;
+
+    if( (col==0) || (col>sv->num_cols) )
+         return ERROR_FUNCTION_FAILED;
+
+    col = sv->cols[ col - 1 ];
+
+    return sv->table->ops->fetch_stream( sv->table, row, col, stm );
+}
+
+static UINT SELECT_set_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT val )
+{
+    MSISELECTVIEW *sv = (MSISELECTVIEW*)view;
+
+    TRACE("%p %d %d %04x\n", sv, row, col, val );
+
+    if( !sv->table )
+         return ERROR_FUNCTION_FAILED;
+
+    if( (col==0) || (col>sv->num_cols) )
+         return ERROR_FUNCTION_FAILED;
+
+    col = sv->cols[ col - 1 ];
+
+    return sv->table->ops->set_int( sv->table, row, col, val );
+}
+
+static UINT SELECT_insert_row( struct tagMSIVIEW *view, MSIRECORD *record )
+{
+    MSISELECTVIEW *sv = (MSISELECTVIEW*)view;
+
+    TRACE("%p %p\n", sv, record );
+
+    if( !sv->table )
+         return ERROR_FUNCTION_FAILED;
+
+    return sv->table->ops->insert_row( sv->table, record );
+}
+
+static UINT SELECT_execute( struct tagMSIVIEW *view, MSIRECORD *record )
+{
+    MSISELECTVIEW *sv = (MSISELECTVIEW*)view;
+
+    TRACE("%p %p\n", sv, record);
+
+    if( !sv->table )
+         return ERROR_FUNCTION_FAILED;
+
+    return sv->table->ops->execute( sv->table, record );
+}
+
+static UINT SELECT_close( struct tagMSIVIEW *view )
+{
+    MSISELECTVIEW *sv = (MSISELECTVIEW*)view;
+
+    TRACE("%p\n", sv );
+
+    if( !sv->table )
+         return ERROR_FUNCTION_FAILED;
+
+    return sv->table->ops->close( sv->table );
+}
+
+static UINT SELECT_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols )
+{
+    MSISELECTVIEW *sv = (MSISELECTVIEW*)view;
+
+    TRACE("%p %p %p\n", sv, rows, cols );
+
+    if( !sv->table )
+         return ERROR_FUNCTION_FAILED;
+
+    if( cols )
+        *cols = sv->num_cols;
+
+    return sv->table->ops->get_dimensions( sv->table, rows, NULL );
+}
+
+static UINT SELECT_get_column_info( struct tagMSIVIEW *view,
+                UINT n, LPWSTR *name, UINT *type )
+{
+    MSISELECTVIEW *sv = (MSISELECTVIEW*)view;
+
+    TRACE("%p %d %p %p\n", sv, n, name, type );
+
+    if( !sv->table )
+         return ERROR_FUNCTION_FAILED;
+
+    if( (n==0) || (n>sv->num_cols) )
+         return ERROR_FUNCTION_FAILED;
+
+    n = sv->cols[ n - 1 ];
+
+    return sv->table->ops->get_column_info( sv->table, n, name, type );
+}
+
+static UINT SELECT_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode,
+                MSIRECORD *rec )
+{
+    MSISELECTVIEW *sv = (MSISELECTVIEW*)view;
+
+    TRACE("%p %d %p\n", sv, eModifyMode, rec );
+
+    if( !sv->table )
+         return ERROR_FUNCTION_FAILED;
+
+    return sv->table->ops->modify( sv->table, eModifyMode, rec );
+}
+
+static UINT SELECT_delete( struct tagMSIVIEW *view )
+{
+    MSISELECTVIEW *sv = (MSISELECTVIEW*)view;
+
+    TRACE("%p\n", sv );
+
+    if( sv->table )
+        sv->table->ops->delete( sv->table );
+    sv->table = NULL;
+
+    msi_free( sv );
+
+    return ERROR_SUCCESS;
+}
+
+
+MSIVIEWOPS select_ops =
+{
+    SELECT_fetch_int,
+    SELECT_fetch_stream,
+    SELECT_set_int,
+    SELECT_insert_row,
+    SELECT_execute,
+    SELECT_close,
+    SELECT_get_dimensions,
+    SELECT_get_column_info,
+    SELECT_modify,
+    SELECT_delete
+};
+
+static UINT SELECT_AddColumn( MSISELECTVIEW *sv, LPCWSTR name )
+{
+    UINT r, n=0;
+    MSIVIEW *table;
+
+    TRACE("%p adding %s\n", sv, debugstr_w( name ) );
+
+    if( sv->view.ops != &select_ops )
+        return ERROR_FUNCTION_FAILED;
+
+    table = sv->table;
+    if( !table )
+        return ERROR_FUNCTION_FAILED;
+    if( !table->ops->get_dimensions )
+        return ERROR_FUNCTION_FAILED;
+    if( !table->ops->get_column_info )
+        return ERROR_FUNCTION_FAILED;
+
+    if( sv->num_cols >= sv->max_cols )
+        return ERROR_FUNCTION_FAILED;
+
+    r = VIEW_find_column( table, name, &n );
+    if( r != ERROR_SUCCESS )
+        return r;
+
+    sv->cols[sv->num_cols] = n;
+    TRACE("Translating column %s from %d -> %d\n", 
+          debugstr_w( name ), sv->num_cols, n);
+
+    sv->num_cols++;
+
+    return ERROR_SUCCESS;
+}
+
+UINT SELECT_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table,
+                        column_info *columns )
+{
+    MSISELECTVIEW *sv = NULL;
+    UINT count = 0, r;
+
+    TRACE("%p\n", sv );
+
+    r = table->ops->get_dimensions( table, NULL, &count );
+    if( r != ERROR_SUCCESS )
+    {
+        ERR("can't get table dimensions\n");
+        return r;
+    }
+
+    sv = msi_alloc_zero( sizeof *sv + count*sizeof (UINT) );
+    if( !sv )
+        return ERROR_FUNCTION_FAILED;
+    
+    /* fill the structure */
+    sv->view.ops = &select_ops;
+    sv->db = db;
+    sv->table = table;
+    sv->num_cols = 0;
+    sv->max_cols = count;
+
+    while( columns )
+    {
+        r = SELECT_AddColumn( sv, columns->column );
+        if( r )
+            break;
+        columns = columns->next;
+    }
+
+    if( r == ERROR_SUCCESS )
+        *view = &sv->view;
+    else
+        msi_free( sv );
+
+    return r;
+}
diff --git a/reactos/dll/msi/source.c b/reactos/dll/msi/source.c
new file mode 100644 (file)
index 0000000..4f67e18
--- /dev/null
@@ -0,0 +1,560 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2005 Aric Stewart for CodeWeavers
+ *
+ * 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 <stdarg.h>
+
+#define COBJMACROS
+#define NONAMELESSUNION
+
+#include "windef.h"
+#include "winbase.h"
+#include "winreg.h"
+#include "winnls.h"
+#include "shlwapi.h"
+#include "wine/debug.h"
+#include "msi.h"
+#include "msiquery.h"
+#include "msipriv.h"
+#include "wincrypt.h"
+#include "winver.h"
+#include "winuser.h"
+#include "wine/unicode.h"
+#include "action.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+/*
+ * These apis are defined in MSI 3.0
+ */
+
+typedef struct tagMediaInfo
+{
+    LPWSTR  path;
+    WCHAR   szIndex[10];
+    WCHAR   type;
+} media_info;
+
+static UINT OpenSourceKey(LPCWSTR szProduct, HKEY* key, BOOL user, BOOL create)
+{
+    HKEY rootkey = 0; 
+    UINT rc; 
+    static const WCHAR szSourceList[] = {'S','o','u','r','c','e','L','i','s','t',0};
+
+    if (user)
+        rc = MSIREG_OpenUserProductsKey(szProduct, &rootkey, create);
+    else
+        rc = MSIREG_OpenProductsKey(szProduct, &rootkey, create);
+
+    if (rc)
+        return rc;
+
+    if (create)
+        rc = RegCreateKeyW(rootkey, szSourceList, key);
+    else
+        rc = RegOpenKeyW(rootkey,szSourceList, key); 
+
+    return rc;
+}
+
+static UINT OpenMediaSubkey(HKEY rootkey, HKEY *key, BOOL create)
+{
+    UINT rc;
+    static const WCHAR media[] = {'M','e','d','i','a',0};
+
+    if (create)
+        rc = RegCreateKeyW(rootkey, media, key);
+    else
+        rc = RegOpenKeyW(rootkey,media, key); 
+
+    return rc;
+}
+
+static UINT OpenNetworkSubkey(HKEY rootkey, HKEY *key, BOOL create)
+{
+    UINT rc;
+    static const WCHAR net[] = {'N','e','t',0};
+
+    if (create)
+        rc = RegCreateKeyW(rootkey, net, key);
+    else
+        rc = RegOpenKeyW(rootkey, net, key); 
+
+    return rc;
+}
+
+static UINT OpenURLSubkey(HKEY rootkey, HKEY *key, BOOL create)
+{
+    UINT rc;
+    static const WCHAR URL[] = {'U','R','L',0};
+
+    if (create)
+        rc = RegCreateKeyW(rootkey, URL, key);
+    else
+        rc = RegOpenKeyW(rootkey, URL, key); 
+
+    return rc;
+}
+
+
+static UINT find_given_source(HKEY key, LPCWSTR szSource, media_info *ss)
+{
+    DWORD index = 0;
+    WCHAR szIndex[10];
+    DWORD size;
+    DWORD val_size;
+    LPWSTR val;
+    UINT rc = ERROR_SUCCESS;
+
+    while (rc == ERROR_SUCCESS)
+    {
+        val = NULL;
+        val_size = 0;
+        rc = RegEnumValueW(key, index, szIndex, &size, NULL, NULL, NULL, &val_size);
+        if (rc != ERROR_NO_MORE_ITEMS)
+        {
+            val = msi_alloc(val_size);
+            RegEnumValueW(key, index, szIndex, &size, NULL, NULL, (LPBYTE)val, 
+                &val_size);
+            if (lstrcmpiW(szSource,val)==0)
+            {
+                ss->path = val;
+                strcpyW(ss->szIndex,szIndex);
+                break;
+            }
+            else
+                strcpyW(ss->szIndex,szIndex);
+
+            msi_free(val);
+            index ++;
+        }
+    }
+    return rc;
+}
+
+/******************************************************************
+ *  MsiSourceListGetInfoW   (MSI.@)
+ */
+UINT WINAPI MsiSourceListGetInfoW( LPCWSTR szProduct, LPCWSTR szUserSid,
+                                   MSIINSTALLCONTEXT dwContext, DWORD dwOptions,
+                                   LPCWSTR szProperty, LPWSTR szValue, 
+                                   LPDWORD pcchValue) 
+{
+    HKEY sourcekey;
+    UINT rc;
+
+    TRACE("%s %s\n", debugstr_w(szProduct), debugstr_w(szProperty));
+
+    if (!szProduct || lstrlenW(szProduct) > 39)
+        return ERROR_INVALID_PARAMETER;
+
+    if (szValue && !pcchValue)
+        return ERROR_INVALID_PARAMETER;
+    
+    if (dwOptions == MSICODE_PATCH)
+    {
+        FIXME("Unhandled options MSICODE_PATCH\n");
+        return ERROR_FUNCTION_FAILED;
+    }
+    
+    if (szUserSid)
+        FIXME("Unhandled UserSid %s\n",debugstr_w(szUserSid));
+
+    if (dwContext == MSIINSTALLCONTEXT_USERUNMANAGED)
+        FIXME("Unknown context MSIINSTALLCONTEXT_USERUNMANAGED\n");
+
+    if (dwContext == MSIINSTALLCONTEXT_MACHINE)
+        rc = OpenSourceKey(szProduct, &sourcekey, FALSE, FALSE);
+    else
+        rc = OpenSourceKey(szProduct, &sourcekey, TRUE, FALSE);
+
+    if (rc != ERROR_SUCCESS)
+        return ERROR_UNKNOWN_PRODUCT;
+
+    if (strcmpW(szProperty, INSTALLPROPERTY_MEDIAPACKAGEPATHW) == 0)
+    {
+        HKEY key;
+        rc = OpenMediaSubkey(sourcekey, &key, FALSE);
+        if (rc == ERROR_SUCCESS)
+            rc = RegQueryValueExW(key, INSTALLPROPERTY_MEDIAPACKAGEPATHW,
+                    0, 0, (LPBYTE)szValue, pcchValue);
+        if (rc != ERROR_SUCCESS && rc != ERROR_MORE_DATA)
+            rc = ERROR_UNKNOWN_PROPERTY;
+        RegCloseKey(key);
+    }
+    else if (strcmpW(szProperty, INSTALLPROPERTY_DISKPROMPTW) ==0)
+    {
+        HKEY key;
+        rc = OpenMediaSubkey(sourcekey, &key, FALSE);
+        if (rc == ERROR_SUCCESS)
+            rc = RegQueryValueExW(key, INSTALLPROPERTY_DISKPROMPTW, 0, 0,
+                    (LPBYTE)szValue, pcchValue);
+        if (rc != ERROR_SUCCESS && rc != ERROR_MORE_DATA)
+            rc = ERROR_UNKNOWN_PROPERTY;
+        RegCloseKey(key);
+    }
+    else if (strcmpW(szProperty, INSTALLPROPERTY_LASTUSEDSOURCEW)==0)
+    {
+        LPWSTR buffer;
+        DWORD size = 0;
+
+        RegQueryValueExW(sourcekey, INSTALLPROPERTY_LASTUSEDSOURCEW, 0, 0,
+                NULL, &size);
+        if (size == 0)
+            rc = ERROR_UNKNOWN_PROPERTY;
+        else
+        {
+            LPWSTR ptr;
+            buffer = msi_alloc(size);
+            rc = RegQueryValueExW(sourcekey, INSTALLPROPERTY_LASTUSEDSOURCEW,
+                    0, 0, (LPBYTE)buffer,&size); 
+            ptr = strchrW(buffer,';');
+            if (ptr) ptr = strchrW(ptr+1,';');
+            if (!ptr)
+                rc = ERROR_UNKNOWN_PROPERTY;
+            else
+            {
+                ptr ++;
+                lstrcpynW(szValue, ptr, *pcchValue);
+                if (lstrlenW(ptr) > *pcchValue)
+                {
+                    *pcchValue = lstrlenW(ptr)+1;
+                    rc = ERROR_MORE_DATA;
+                }
+                else
+                    rc = ERROR_SUCCESS;
+            }
+            msi_free(buffer);
+        }
+    }
+    else if (strcmpW(INSTALLPROPERTY_LASTUSEDTYPEW, szProperty)==0)
+    {
+        LPWSTR buffer;
+        DWORD size = 0;
+
+        RegQueryValueExW(sourcekey, INSTALLPROPERTY_LASTUSEDSOURCEW, 0, 0,
+                NULL, &size);
+        if (size == 0)
+            rc = ERROR_UNKNOWN_PROPERTY;
+        else
+        {
+            buffer = msi_alloc(size);
+            rc = RegQueryValueExW(sourcekey, INSTALLPROPERTY_LASTUSEDSOURCEW,
+                    0, 0, (LPBYTE)buffer,&size); 
+            if (*pcchValue < 1)
+            {
+                rc = ERROR_MORE_DATA;
+                *pcchValue = 1;
+            }
+            else
+            {
+                szValue[0] = buffer[0];
+                rc = ERROR_SUCCESS;
+            }
+            msi_free(buffer);
+        }
+    }
+    else if (strcmpW(INSTALLPROPERTY_PACKAGENAMEW, szProperty)==0)
+    {
+        rc = RegQueryValueExW(sourcekey, INSTALLPROPERTY_PACKAGENAMEW, 0, 0, 
+                (LPBYTE)szValue, pcchValue);
+        if (rc != ERROR_SUCCESS && rc != ERROR_MORE_DATA)
+            rc = ERROR_UNKNOWN_PROPERTY;
+    }
+    else
+    {
+        FIXME("Unknown property %s\n",debugstr_w(szProperty));
+        rc = ERROR_UNKNOWN_PROPERTY;
+    }
+
+    RegCloseKey(sourcekey);
+    return rc;
+}
+
+/******************************************************************
+ *  MsiSourceListSetInfoW   (MSI.@)
+ */
+UINT WINAPI MsiSourceListSetInfoW( LPCWSTR szProduct, LPCWSTR szUserSid,
+                                   MSIINSTALLCONTEXT dwContext, DWORD dwOptions,
+                                   LPCWSTR szProperty, LPCWSTR szValue)
+{
+    HKEY sourcekey;
+    UINT rc;
+
+    TRACE("%s %s %x %lx %s %s\n", debugstr_w(szProduct), debugstr_w(szUserSid), 
+            dwContext, dwOptions, debugstr_w(szProperty), debugstr_w(szValue));
+
+    if (!szProduct || lstrlenW(szProduct) > 39)
+        return ERROR_INVALID_PARAMETER;
+
+    if (dwOptions & MSICODE_PATCH)
+    {
+        FIXME("Unhandled options MSICODE_PATCH\n");
+        return ERROR_FUNCTION_FAILED;
+    }
+    
+    if (szUserSid)
+        FIXME("Unhandled UserSid %s\n",debugstr_w(szUserSid));
+
+    if (dwContext == MSIINSTALLCONTEXT_USERUNMANAGED)
+        FIXME("Unknown context MSIINSTALLCONTEXT_USERUNMANAGED\n");
+
+    if (dwContext == MSIINSTALLCONTEXT_MACHINE)
+        rc = OpenSourceKey(szProduct, &sourcekey, FALSE, TRUE);
+    else
+        rc = OpenSourceKey(szProduct, &sourcekey, TRUE, TRUE);
+
+    if (rc != ERROR_SUCCESS)
+        return ERROR_UNKNOWN_PRODUCT;
+
+
+    if (strcmpW(szProperty, INSTALLPROPERTY_MEDIAPACKAGEPATHW) == 0)
+    {
+        HKEY key;
+        DWORD size = lstrlenW(szValue)*sizeof(WCHAR);
+        rc = OpenMediaSubkey(sourcekey, &key, FALSE);
+        if (rc == ERROR_SUCCESS)
+            rc = RegSetValueExW(key, INSTALLPROPERTY_MEDIAPACKAGEPATHW, 0,
+                    REG_SZ, (LPBYTE)szValue, size);
+        if (rc != ERROR_SUCCESS)
+            rc = ERROR_UNKNOWN_PROPERTY;
+        RegCloseKey(key);
+    }
+    else if (strcmpW(szProperty, INSTALLPROPERTY_DISKPROMPTW) == 0)
+    {
+        HKEY key;
+        DWORD size = lstrlenW(szValue)*sizeof(WCHAR);
+        rc = OpenMediaSubkey(sourcekey, &key, FALSE);
+        if (rc == ERROR_SUCCESS)
+            rc = RegSetValueExW(key, INSTALLPROPERTY_DISKPROMPTW, 0,
+                    REG_SZ, (LPBYTE)szValue, size);
+        if (rc != ERROR_SUCCESS)
+            rc = ERROR_UNKNOWN_PROPERTY;
+        RegCloseKey(key);
+    }
+    else if (strcmpW(szProperty, INSTALLPROPERTY_LASTUSEDSOURCEW)==0)
+    {
+        LPWSTR buffer = NULL;
+        DWORD size;
+        WCHAR typechar = 'n';
+        static const WCHAR LastUsedSource_Fmt[] = {'%','c',';','%','i',';','%','s',0};
+
+        /* make sure the source is registered */
+        MsiSourceListAddSourceExW(szProduct, szUserSid, dwContext, 
+                dwOptions, szValue, 0); 
+
+        if (dwOptions & MSISOURCETYPE_NETWORK)
+            typechar = 'n';
+        else if (dwOptions & MSISOURCETYPE_URL)
+            typechar = 'u';
+        else if (dwOptions & MSISOURCETYPE_MEDIA)
+            typechar = 'm';
+        else 
+            ERR("Unknown source type! 0x%lx\n",dwOptions);
+        
+        size = (lstrlenW(szValue)+5)*sizeof(WCHAR);
+        buffer = msi_alloc(size);
+        sprintfW(buffer, LastUsedSource_Fmt, typechar, 1, szValue);
+        rc = RegSetValueExW(sourcekey, INSTALLPROPERTY_LASTUSEDSOURCEW, 0, 
+                REG_EXPAND_SZ, (LPBYTE)buffer, size);
+        if (rc != ERROR_SUCCESS)
+            rc = ERROR_UNKNOWN_PROPERTY;
+        msi_free( buffer );
+    }
+    else if (strcmpW(INSTALLPROPERTY_PACKAGENAMEW, szProperty)==0)
+    {
+        DWORD size = lstrlenW(szValue)*sizeof(WCHAR);
+        rc = RegSetValueExW(sourcekey, INSTALLPROPERTY_PACKAGENAMEW, 0,
+                REG_SZ, (LPBYTE)szValue, size);
+        if (rc != ERROR_SUCCESS)
+            rc = ERROR_UNKNOWN_PROPERTY;
+    }
+    else
+    {
+        FIXME("Unknown property %s\n",debugstr_w(szProperty));
+        rc = ERROR_UNKNOWN_PROPERTY;
+    }
+
+    RegCloseKey(sourcekey);
+    return rc;
+
+}
+
+/******************************************************************
+ *  MsiSourceListAddSourceExW (MSI.@)
+ */
+UINT WINAPI MsiSourceListAddSourceExW( LPCWSTR szProduct, LPCWSTR szUserSid,
+        MSIINSTALLCONTEXT dwContext, DWORD dwOptions, LPCWSTR szSource, 
+        DWORD dwIndex)
+{
+    HKEY sourcekey;
+    HKEY typekey;
+    UINT rc;
+    media_info source_struct;
+   
+    TRACE("%s, %s, %x, %lx, %s, %li\n", debugstr_w(szProduct), 
+            debugstr_w(szUserSid), dwContext, dwOptions, debugstr_w(szSource), 
+            dwIndex);
+    
+    if (!szProduct)
+        return ERROR_INVALID_PARAMETER;
+
+    if (!szSource)
+        return ERROR_INVALID_PARAMETER;
+
+    if (dwOptions & MSICODE_PATCH)
+    {
+        FIXME("Unhandled options MSICODE_PATCH\n");
+        return ERROR_FUNCTION_FAILED;
+    }
+
+    if (szUserSid)
+        FIXME("Unhandled UserSid %s\n",debugstr_w(szUserSid));
+
+    if (dwContext == MSIINSTALLCONTEXT_USERUNMANAGED)
+        FIXME("Unknown context MSIINSTALLCONTEXT_USERUNMANAGED\n");
+
+    if (dwContext == MSIINSTALLCONTEXT_MACHINE)
+        rc = OpenSourceKey(szProduct, &sourcekey, FALSE, TRUE);
+    else
+        rc = OpenSourceKey(szProduct, &sourcekey, TRUE, TRUE);
+
+    if (rc != ERROR_SUCCESS)
+        return ERROR_UNKNOWN_PRODUCT;
+
+    if (dwOptions & MSISOURCETYPE_NETWORK)
+        rc = OpenNetworkSubkey(sourcekey, &typekey, TRUE);
+    else if (dwOptions & MSISOURCETYPE_URL)
+        rc = OpenURLSubkey(sourcekey, &typekey, TRUE);
+    else
+    {
+        ERR("unknown media type: %08lx\n", dwOptions);
+        RegCloseKey(sourcekey);
+        return ERROR_FUNCTION_FAILED;
+    }
+
+    source_struct.szIndex[0] = 0;
+    if (find_given_source(typekey, szSource, &source_struct)==ERROR_SUCCESS)
+    {
+        DWORD current_index = atoiW(source_struct.szIndex);
+        /* found the source */
+        if (dwIndex > 0 && current_index != dwIndex)
+            FIXME("Need to reorder the sources!\n");
+    }
+    else
+    {
+        DWORD current_index = 0;
+        static const WCHAR fmt[] = {'%','i',0};
+        DWORD size = lstrlenW(szSource)*sizeof(WCHAR);
+
+        if (source_struct.szIndex[0])
+            current_index = atoiW(source_struct.szIndex);
+        /* new source */
+        if (dwIndex > 0 && dwIndex < current_index)
+            FIXME("Need to reorder the sources!\n");
+
+        current_index ++;
+        sprintfW(source_struct.szIndex,fmt,current_index);
+        rc = RegSetValueExW(typekey, source_struct.szIndex, 0, REG_EXPAND_SZ, 
+                (LPBYTE)szSource, size);
+    }
+
+    RegCloseKey(typekey);
+    RegCloseKey(sourcekey);
+    return rc;
+}
+
+/******************************************************************
+ *  MsiSourceListAddMediaDisk(MSI.@)
+ */
+UINT WINAPI MsiSourceListAddMediaDiskW(LPCWSTR szProduct, LPCWSTR szUserSid, 
+        MSIINSTALLCONTEXT dwContext, DWORD dwOptions, DWORD dwDiskId, 
+        LPCWSTR szVolumeLabel, LPCWSTR szDiskPrompt)
+{
+    HKEY sourcekey;
+    HKEY mediakey;
+    UINT rc;
+    WCHAR szIndex[10];
+    static const WCHAR fmt[] = {'%','i',0};
+    static const WCHAR disk_fmt[] = {'%','s',';','%','s',0};
+    static const WCHAR empty[1] = {0};
+    LPCWSTR pt1,pt2;
+    LPWSTR buffer;
+    DWORD size;
+
+    TRACE("%s %s %x %lx %li %s %s\n", debugstr_w(szProduct), 
+            debugstr_w(szUserSid), dwContext, dwOptions, dwDiskId, 
+            debugstr_w(szVolumeLabel), debugstr_w(szDiskPrompt)); 
+
+    if (!szProduct || lstrlenW(szProduct) > 39)
+        return ERROR_INVALID_PARAMETER;
+
+    if (dwOptions & MSICODE_PATCH)
+    {
+        FIXME("Unhandled options MSICODE_PATCH\n");
+        return ERROR_FUNCTION_FAILED;
+    }
+    
+    if (szUserSid)
+        FIXME("Unhandled UserSid %s\n",debugstr_w(szUserSid));
+
+    if (dwContext == MSIINSTALLCONTEXT_USERUNMANAGED)
+        FIXME("Unknown context MSIINSTALLCONTEXT_USERUNMANAGED\n");
+
+    if (dwContext == MSIINSTALLCONTEXT_MACHINE)
+        rc = OpenSourceKey(szProduct, &sourcekey, FALSE, TRUE);
+    else
+        rc = OpenSourceKey(szProduct, &sourcekey, TRUE, TRUE);
+
+    if (rc != ERROR_SUCCESS)
+        return ERROR_UNKNOWN_PRODUCT;
+
+    OpenMediaSubkey(sourcekey,&mediakey,TRUE);
+
+    sprintfW(szIndex,fmt,dwDiskId);
+
+    size = 2;
+    if (szVolumeLabel)
+    {
+        size +=lstrlenW(szVolumeLabel);
+        pt1 = szVolumeLabel;
+    }
+    else
+        pt1 = empty;
+    if (szDiskPrompt)
+    {
+        size +=lstrlenW(szDiskPrompt);
+        pt2 = szDiskPrompt;
+    }
+    else
+        pt2 = empty;
+
+    size *=sizeof(WCHAR);
+
+    buffer = msi_alloc(size);
+    sprintfW(buffer,disk_fmt,pt1,pt2);
+
+    RegSetValueExW(mediakey, szIndex, 0, REG_SZ, (LPBYTE)buffer, size);
+    msi_free( buffer );
+
+    RegCloseKey(sourcekey);
+    RegCloseKey(mediakey);
+
+    return ERROR_SUCCESS;
+}
diff --git a/reactos/dll/msi/sql.tab.c b/reactos/dll/msi/sql.tab.c
new file mode 100644 (file)
index 0000000..f464395
--- /dev/null
@@ -0,0 +1,2484 @@
+/* A Bison parser, made by GNU Bison 1.875c.  */
+
+/* Skeleton parser for Yacc-like parsing with Bison,
+   Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program 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 General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, when this file is copied by Bison into a
+   Bison output file, you may use that output file without restriction.
+   This special exception was added by the Free Software Foundation
+   in version 1.24 of Bison.  */
+
+/* Written by Richard Stallman by simplifying the original so called
+   ``semantic'' parser.  */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+   infringing on user name space.  This should be done even for local
+   variables, as they might otherwise be expanded by user macros.
+   There are some unavoidable exceptions within include files to
+   define necessary library symbols; they are noted "INFRINGES ON
+   USER NAME SPACE" below.  */
+
+/* Identify Bison output.  */
+#define YYBISON 1
+
+/* Skeleton name.  */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers.  */
+#define YYPURE 1
+
+/* Using locations.  */
+#define YYLSP_NEEDED 0
+
+/* If NAME_PREFIX is specified substitute the variables and functions
+   names.  */
+#define yyparse SQL_parse
+#define yylex   SQL_lex
+#define yyerror SQL_error
+#define yylval  SQL_lval
+#define yychar  SQL_char
+#define yydebug SQL_debug
+#define yynerrs SQL_nerrs
+
+
+/* Tokens.  */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+   /* Put the tokens into the symbol table, so that GDB and other debuggers
+      know about them.  */
+   enum yytokentype {
+     TK_ABORT = 258,
+     TK_AFTER = 259,
+     TK_AGG_FUNCTION = 260,
+     TK_ALL = 261,
+     TK_AND = 262,
+     TK_AS = 263,
+     TK_ASC = 264,
+     TK_BEFORE = 265,
+     TK_BEGIN = 266,
+     TK_BETWEEN = 267,
+     TK_BITAND = 268,
+     TK_BITNOT = 269,
+     TK_BITOR = 270,
+     TK_BY = 271,
+     TK_CASCADE = 272,
+     TK_CASE = 273,
+     TK_CHAR = 274,
+     TK_CHECK = 275,
+     TK_CLUSTER = 276,
+     TK_COLLATE = 277,
+     TK_COLUMN = 278,
+     TK_COMMA = 279,
+     TK_COMMENT = 280,
+     TK_COMMIT = 281,
+     TK_CONCAT = 282,
+     TK_CONFLICT = 283,
+     TK_CONSTRAINT = 284,
+     TK_COPY = 285,
+     TK_CREATE = 286,
+     TK_DEFAULT = 287,
+     TK_DEFERRABLE = 288,
+     TK_DEFERRED = 289,
+     TK_DELETE = 290,
+     TK_DELIMITERS = 291,
+     TK_DESC = 292,
+     TK_DISTINCT = 293,
+     TK_DOT = 294,
+     TK_DROP = 295,
+     TK_EACH = 296,
+     TK_ELSE = 297,
+     TK_END = 298,
+     TK_END_OF_FILE = 299,
+     TK_EQ = 300,
+     TK_EXCEPT = 301,
+     TK_EXPLAIN = 302,
+     TK_FAIL = 303,
+     TK_FLOAT = 304,
+     TK_FOR = 305,
+     TK_FOREIGN = 306,
+     TK_FROM = 307,
+     TK_FUNCTION = 308,
+     TK_GE = 309,
+     TK_GLOB = 310,
+     TK_GROUP = 311,
+     TK_GT = 312,
+     TK_HAVING = 313,
+     TK_HOLD = 314,
+     TK_IGNORE = 315,
+     TK_ILLEGAL = 316,
+     TK_IMMEDIATE = 317,
+     TK_IN = 318,
+     TK_INDEX = 319,
+     TK_INITIALLY = 320,
+     TK_ID = 321,
+     TK_INSERT = 322,
+     TK_INSTEAD = 323,
+     TK_INT = 324,
+     TK_INTEGER = 325,
+     TK_INTERSECT = 326,
+     TK_INTO = 327,
+     TK_IS = 328,
+     TK_ISNULL = 329,
+     TK_JOIN = 330,
+     TK_JOIN_KW = 331,
+     TK_KEY = 332,
+     TK_LE = 333,
+     TK_LIKE = 334,
+     TK_LIMIT = 335,
+     TK_LONG = 336,
+     TK_LONGCHAR = 337,
+     TK_LP = 338,
+     TK_LSHIFT = 339,
+     TK_LT = 340,
+     TK_LOCALIZABLE = 341,
+     TK_MATCH = 342,
+     TK_MINUS = 343,
+     TK_NE = 344,
+     TK_NOT = 345,
+     TK_NOTNULL = 346,
+     TK_NULL = 347,
+     TK_OBJECT = 348,
+     TK_OF = 349,
+     TK_OFFSET = 350,
+     TK_ON = 351,
+     TK_OR = 352,
+     TK_ORACLE_OUTER_JOIN = 353,
+     TK_ORDER = 354,
+     TK_PLUS = 355,
+     TK_PRAGMA = 356,
+     TK_PRIMARY = 357,
+     TK_RAISE = 358,
+     TK_REFERENCES = 359,
+     TK_REM = 360,
+     TK_REPLACE = 361,
+     TK_RESTRICT = 362,
+     TK_ROLLBACK = 363,
+     TK_ROW = 364,
+     TK_RP = 365,
+     TK_RSHIFT = 366,
+     TK_SELECT = 367,
+     TK_SEMI = 368,
+     TK_SET = 369,
+     TK_SHORT = 370,
+     TK_SLASH = 371,
+     TK_SPACE = 372,
+     TK_STAR = 373,
+     TK_STATEMENT = 374,
+     TK_STRING = 375,
+     TK_TABLE = 376,
+     TK_TEMP = 377,
+     TK_THEN = 378,
+     TK_TRANSACTION = 379,
+     TK_TRIGGER = 380,
+     TK_UMINUS = 381,
+     TK_UNCLOSED_STRING = 382,
+     TK_UNION = 383,
+     TK_UNIQUE = 384,
+     TK_UPDATE = 385,
+     TK_UPLUS = 386,
+     TK_USING = 387,
+     TK_VACUUM = 388,
+     TK_VALUES = 389,
+     TK_VIEW = 390,
+     TK_WHEN = 391,
+     TK_WHERE = 392,
+     TK_WILDCARD = 393,
+     COLUMN = 395,
+     FUNCTION = 396,
+     COMMENT = 397,
+     UNCLOSED_STRING = 398,
+     SPACE = 399,
+     ILLEGAL = 400,
+     END_OF_FILE = 401
+   };
+#endif
+#define TK_ABORT 258
+#define TK_AFTER 259
+#define TK_AGG_FUNCTION 260
+#define TK_ALL 261
+#define TK_AND 262
+#define TK_AS 263
+#define TK_ASC 264
+#define TK_BEFORE 265
+#define TK_BEGIN 266
+#define TK_BETWEEN 267
+#define TK_BITAND 268
+#define TK_BITNOT 269
+#define TK_BITOR 270
+#define TK_BY 271
+#define TK_CASCADE 272
+#define TK_CASE 273
+#define TK_CHAR 274
+#define TK_CHECK 275
+#define TK_CLUSTER 276
+#define TK_COLLATE 277
+#define TK_COLUMN 278
+#define TK_COMMA 279
+#define TK_COMMENT 280
+#define TK_COMMIT 281
+#define TK_CONCAT 282
+#define TK_CONFLICT 283
+#define TK_CONSTRAINT 284
+#define TK_COPY 285
+#define TK_CREATE 286
+#define TK_DEFAULT 287
+#define TK_DEFERRABLE 288
+#define TK_DEFERRED 289
+#define TK_DELETE 290
+#define TK_DELIMITERS 291
+#define TK_DESC 292
+#define TK_DISTINCT 293
+#define TK_DOT 294
+#define TK_DROP 295
+#define TK_EACH 296
+#define TK_ELSE 297
+#define TK_END 298
+#define TK_END_OF_FILE 299
+#define TK_EQ 300
+#define TK_EXCEPT 301
+#define TK_EXPLAIN 302
+#define TK_FAIL 303
+#define TK_FLOAT 304
+#define TK_FOR 305
+#define TK_FOREIGN 306
+#define TK_FROM 307
+#define TK_FUNCTION 308
+#define TK_GE 309
+#define TK_GLOB 310
+#define TK_GROUP 311
+#define TK_GT 312
+#define TK_HAVING 313
+#define TK_HOLD 314
+#define TK_IGNORE 315
+#define TK_ILLEGAL 316
+#define TK_IMMEDIATE 317
+#define TK_IN 318
+#define TK_INDEX 319
+#define TK_INITIALLY 320
+#define TK_ID 321
+#define TK_INSERT 322
+#define TK_INSTEAD 323
+#define TK_INT 324
+#define TK_INTEGER 325
+#define TK_INTERSECT 326
+#define TK_INTO 327
+#define TK_IS 328
+#define TK_ISNULL 329
+#define TK_JOIN 330
+#define TK_JOIN_KW 331
+#define TK_KEY 332
+#define TK_LE 333
+#define TK_LIKE 334
+#define TK_LIMIT 335
+#define TK_LONG 336
+#define TK_LONGCHAR 337
+#define TK_LP 338
+#define TK_LSHIFT 339
+#define TK_LT 340
+#define TK_LOCALIZABLE 341
+#define TK_MATCH 342
+#define TK_MINUS 343
+#define TK_NE 344
+#define TK_NOT 345
+#define TK_NOTNULL 346
+#define TK_NULL 347
+#define TK_OBJECT 348
+#define TK_OF 349
+#define TK_OFFSET 350
+#define TK_ON 351
+#define TK_OR 352
+#define TK_ORACLE_OUTER_JOIN 353
+#define TK_ORDER 354
+#define TK_PLUS 355
+#define TK_PRAGMA 356
+#define TK_PRIMARY 357
+#define TK_RAISE 358
+#define TK_REFERENCES 359
+#define TK_REM 360
+#define TK_REPLACE 361
+#define TK_RESTRICT 362
+#define TK_ROLLBACK 363
+#define TK_ROW 364
+#define TK_RP 365
+#define TK_RSHIFT 366
+#define TK_SELECT 367
+#define TK_SEMI 368
+#define TK_SET 369
+#define TK_SHORT 370
+#define TK_SLASH 371
+#define TK_SPACE 372
+#define TK_STAR 373
+#define TK_STATEMENT 374
+#define TK_STRING 375
+#define TK_TABLE 376
+#define TK_TEMP 377
+#define TK_THEN 378
+#define TK_TRANSACTION 379
+#define TK_TRIGGER 380
+#define TK_UMINUS 381
+#define TK_UNCLOSED_STRING 382
+#define TK_UNION 383
+#define TK_UNIQUE 384
+#define TK_UPDATE 385
+#define TK_UPLUS 386
+#define TK_USING 387
+#define TK_VACUUM 388
+#define TK_VALUES 389
+#define TK_VIEW 390
+#define TK_WHEN 391
+#define TK_WHERE 392
+#define TK_WILDCARD 393
+#define COLUMN 395
+#define FUNCTION 396
+#define COMMENT 397
+#define UNCLOSED_STRING 398
+#define SPACE 399
+#define ILLEGAL 400
+#define END_OF_FILE 401
+
+
+
+
+/* Copy the first part of user declarations.  */
+#line 1 "./sql.y"
+
+
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2002-2004 Mike McCormack for CodeWeavers
+ *
+ * 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 <stdlib.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "query.h"
+#include "wine/list.h"
+#include "wine/debug.h"
+
+#define YYLEX_PARAM info
+#define YYPARSE_PARAM info
+
+extern int SQL_error(const char *str);
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+typedef struct tag_SQL_input
+{
+    MSIDATABASE *db;
+    LPCWSTR command;
+    DWORD n, len;
+    MSIVIEW **view;  /* view structure for the resulting query */
+    struct list *mem;
+} SQL_input;
+
+static LPWSTR SQL_getstring( void *info, struct sql_str *str );
+static INT SQL_getint( void *info );
+static int SQL_lex( void *SQL_lval, SQL_input *info );
+
+static void *parser_alloc( void *info, unsigned int sz );
+static column_info *parser_alloc_column( void *info, LPCWSTR table, LPCWSTR column );
+
+static BOOL SQL_MarkPrimaryKeys( column_info *cols, column_info *keys);
+
+static struct expr * EXPR_complex( void *info, struct expr *l, UINT op, struct expr *r );
+static struct expr * EXPR_column( void *info, column_info *column );
+static struct expr * EXPR_ival( void *info, int val );
+static struct expr * EXPR_sval( void *info, struct sql_str * );
+static struct expr * EXPR_wildcard( void *info );
+
+
+
+/* Enabling traces.  */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+/* Enabling verbose error messages.  */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+#if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED)
+#line 72 "./sql.y"
+typedef union YYSTYPE {
+    struct sql_str str;
+    LPWSTR string;
+    column_info *column_list;
+    MSIVIEW *query;
+    struct expr *expr;
+    USHORT column_type;
+    int integer;
+} YYSTYPE;
+/* Line 191 of yacc.c.  */
+#line 453 "sql.tab.c"
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+
+
+/* Copy the second part of user declarations.  */
+
+
+/* Line 214 of yacc.c.  */
+#line 465 "sql.tab.c"
+
+#if ! defined (yyoverflow) || YYERROR_VERBOSE
+
+# ifndef YYFREE
+#  define YYFREE free
+# endif
+# ifndef YYMALLOC
+#  define YYMALLOC malloc
+# endif
+
+/* The parser invokes alloca or malloc; define the necessary symbols.  */
+
+# ifdef YYSTACK_USE_ALLOCA
+#  if YYSTACK_USE_ALLOCA
+#   define YYSTACK_ALLOC alloca
+#  endif
+# else
+#  if defined (alloca) || defined (_ALLOCA_H)
+#   define YYSTACK_ALLOC alloca
+#  else
+#   ifdef __GNUC__
+#    define YYSTACK_ALLOC __builtin_alloca
+#   endif
+#  endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+   /* Pacify GCC's `empty if-body' warning. */
+#  define YYSTACK_FREE(Ptr) do { /* empty */; } while (0)
+# else
+#  if defined (__STDC__) || defined (__cplusplus)
+#   include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+#   define YYSIZE_T size_t
+#  endif
+#  define YYSTACK_ALLOC YYMALLOC
+#  define YYSTACK_FREE YYFREE
+# endif
+#endif /* ! defined (yyoverflow) || YYERROR_VERBOSE */
+
+
+#if (! defined (yyoverflow) \
+     && (! defined (__cplusplus) \
+        || (defined (YYSTYPE_IS_TRIVIAL) && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member.  */
+union yyalloc
+{
+  short yyss;
+  YYSTYPE yyvs;
+  };
+
+/* The size of the maximum gap between one aligned stack and the next.  */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+   N elements.  */
+# define YYSTACK_BYTES(N) \
+     ((N) * (sizeof (short) + sizeof (YYSTYPE))                                \
+      + YYSTACK_GAP_MAXIMUM)
+
+/* Copy COUNT objects from FROM to TO.  The source and destination do
+   not overlap.  */
+# ifndef YYCOPY
+#  if defined (__GNUC__) && 1 < __GNUC__
+#   define YYCOPY(To, From, Count) \
+      __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+#  else
+#   define YYCOPY(To, From, Count)             \
+      do                                       \
+       {                                       \
+         register YYSIZE_T yyi;                \
+         for (yyi = 0; yyi < (Count); yyi++)   \
+           (To)[yyi] = (From)[yyi];            \
+       }                                       \
+      while (0)
+#  endif
+# endif
+
+/* Relocate STACK from its old location to the new one.  The
+   local variables YYSIZE and YYSTACKSIZE give the old and new number of
+   elements in the stack, and YYPTR gives the new location of the
+   stack.  Advance YYPTR to a properly aligned location for the next
+   stack.  */
+# define YYSTACK_RELOCATE(Stack)                                       \
+    do                                                                 \
+      {                                                                        \
+       YYSIZE_T yynewbytes;                                            \
+       YYCOPY (&yyptr->Stack, Stack, yysize);                          \
+       Stack = &yyptr->Stack;                                          \
+       yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+       yyptr += yynewbytes / sizeof (*yyptr);                          \
+      }                                                                        \
+    while (0)
+
+#endif
+
+#if defined (__STDC__) || defined (__cplusplus)
+   typedef signed char yysigned_char;
+#else
+   typedef short yysigned_char;
+#endif
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL  29
+/* YYLAST -- Last index in YYTABLE.  */
+#define YYLAST   124
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS  147
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS  31
+/* YYNRULES -- Number of rules. */
+#define YYNRULES  69
+/* YYNRULES -- Number of states. */
+#define YYNSTATES  127
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX.  */
+#define YYUNDEFTOK  2
+#define YYMAXUTOK   401
+
+#define YYTRANSLATE(YYX)                                               \
+  ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX.  */
+static const unsigned char yytranslate[] =
+{
+       0,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     1,     2,     3,     4,
+       5,     6,     7,     8,     9,    10,    11,    12,    13,    14,
+      15,    16,    17,    18,    19,    20,    21,    22,    23,    24,
+      25,    26,    27,    28,    29,    30,    31,    32,    33,    34,
+      35,    36,    37,    38,    39,    40,    41,    42,    43,    44,
+      45,    46,    47,    48,    49,    50,    51,    52,    53,    54,
+      55,    56,    57,    58,    59,    60,    61,    62,    63,    64,
+      65,    66,    67,    68,    69,    70,    71,    72,    73,    74,
+      75,    76,    77,    78,    79,    80,    81,    82,    83,    84,
+      85,    86,    87,    88,    89,    90,    91,    92,    93,    94,
+      95,    96,    97,    98,    99,   100,   101,   102,   103,   104,
+     105,   106,   107,   108,   109,   110,   111,   112,   113,   114,
+     115,   116,   117,   118,   119,   120,   121,   122,   123,   124,
+     125,   126,   127,   128,   129,   130,   131,   132,   133,   134,
+     135,   136,   137,   138,   139,   140,   141,   142,   143,   144,
+     145,   146
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+   YYRHS.  */
+static const unsigned char yyprhs[] =
+{
+       0,     0,     3,     5,     7,     9,    11,    13,    15,    26,
+      38,    45,    53,    60,    63,    68,    72,    74,    77,    79,
+      82,    84,    88,    90,    95,    97,    99,   101,   103,   105,
+     107,   112,   114,   117,   121,   124,   126,   130,   132,   134,
+     138,   141,   145,   149,   153,   157,   161,   165,   169,   173,
+     177,   181,   185,   190,   192,   194,   196,   200,   202,   206,
+     210,   212,   215,   217,   219,   221,   225,   227,   229,   231
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS. */
+static const short yyrhs[] =
+{
+     148,     0,    -1,   149,    -1,   161,    -1,   151,    -1,   150,
+      -1,   152,    -1,   153,    -1,    67,    72,   175,    83,   164,
+     110,   134,    83,   169,   110,    -1,    67,    72,   175,    83,
+     164,   110,   134,    83,   169,   110,   122,    -1,    31,   121,
+     175,    83,   154,   110,    -1,    31,   121,   175,    83,   154,
+     110,    59,    -1,   130,   175,   114,   170,   137,   167,    -1,
+      35,   165,    -1,   155,   102,    77,   164,    -1,   155,    24,
+     156,    -1,   156,    -1,   174,   157,    -1,   158,    -1,   158,
+      86,    -1,   159,    -1,   159,    90,    92,    -1,    19,    -1,
+      19,    83,   160,   110,    -1,    82,    -1,   115,    -1,    69,
+      -1,    81,    -1,    93,    -1,   177,    -1,   162,    99,    16,
+     164,    -1,   162,    -1,   112,   163,    -1,   112,    38,   163,
+      -1,   164,   165,    -1,   174,    -1,   174,    24,   164,    -1,
+     118,    -1,   166,    -1,   166,   137,   167,    -1,    52,   175,
+      -1,    83,   167,   110,    -1,   173,    45,   173,    -1,   167,
+       7,   167,    -1,   167,    97,   167,    -1,   173,    45,   168,
+      -1,   173,    57,   168,    -1,   173,    85,   168,    -1,   173,
+      78,   168,    -1,   173,    54,   168,    -1,   173,    89,   168,
+      -1,   173,    73,    92,    -1,   173,    73,    90,    92,    -1,
+     173,    -1,   172,    -1,   172,    -1,   172,    24,   169,    -1,
+     171,    -1,   171,    24,   170,    -1,   174,    45,   172,    -1,
+     177,    -1,    88,   177,    -1,   120,    -1,   138,    -1,   174,
+      -1,   175,    39,   176,    -1,   176,    -1,   176,    -1,    66,
+      -1,    70,    -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined.  */
+static const unsigned short yyrline[] =
+{
+       0,   138,   138,   146,   147,   148,   149,   150,   154,   165,
+     178,   190,   205,   218,   231,   241,   251,   258,   266,   270,
+     277,   281,   288,   292,   296,   300,   304,   308,   312,   319,
+     328,   340,   344,   348,   364,   385,   386,   390,   397,   398,
+     414,   427,   433,   439,   445,   451,   457,   463,   469,   475,
+     481,   487,   493,   502,   503,   507,   514,   525,   526,   534,
+     542,   548,   554,   560,   569,   578,   584,   593,   600,   609
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE
+/* YYTNME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+   First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+static const char *const yytname[] =
+{
+  "$end", "error", "$undefined", "TK_ABORT", "TK_AFTER",
+  "TK_AGG_FUNCTION", "TK_ALL", "TK_AND", "TK_AS", "TK_ASC", "TK_BEFORE",
+  "TK_BEGIN", "TK_BETWEEN", "TK_BITAND", "TK_BITNOT", "TK_BITOR", "TK_BY",
+  "TK_CASCADE", "TK_CASE", "TK_CHAR", "TK_CHECK", "TK_CLUSTER",
+  "TK_COLLATE", "TK_COLUMN", "TK_COMMA", "TK_COMMENT", "TK_COMMIT",
+  "TK_CONCAT", "TK_CONFLICT", "TK_CONSTRAINT", "TK_COPY", "TK_CREATE",
+  "TK_DEFAULT", "TK_DEFERRABLE", "TK_DEFERRED", "TK_DELETE",
+  "TK_DELIMITERS", "TK_DESC", "TK_DISTINCT", "TK_DOT", "TK_DROP",
+  "TK_EACH", "TK_ELSE", "TK_END", "TK_END_OF_FILE", "TK_EQ", "TK_EXCEPT",
+  "TK_EXPLAIN", "TK_FAIL", "TK_FLOAT", "TK_FOR", "TK_FOREIGN", "TK_FROM",
+  "TK_FUNCTION", "TK_GE", "TK_GLOB", "TK_GROUP", "TK_GT", "TK_HAVING",
+  "TK_HOLD", "TK_IGNORE", "TK_ILLEGAL", "TK_IMMEDIATE", "TK_IN",
+  "TK_INDEX", "TK_INITIALLY", "TK_ID", "TK_INSERT", "TK_INSTEAD", "TK_INT",
+  "TK_INTEGER", "TK_INTERSECT", "TK_INTO", "TK_IS", "TK_ISNULL", "TK_JOIN",
+  "TK_JOIN_KW", "TK_KEY", "TK_LE", "TK_LIKE", "TK_LIMIT", "TK_LONG",
+  "TK_LONGCHAR", "TK_LP", "TK_LSHIFT", "TK_LT", "TK_LOCALIZABLE",
+  "TK_MATCH", "TK_MINUS", "TK_NE", "TK_NOT", "TK_NOTNULL", "TK_NULL",
+  "TK_OBJECT", "TK_OF", "TK_OFFSET", "TK_ON", "TK_OR",
+  "TK_ORACLE_OUTER_JOIN", "TK_ORDER", "TK_PLUS", "TK_PRAGMA", "TK_PRIMARY",
+  "TK_RAISE", "TK_REFERENCES", "TK_REM", "TK_REPLACE", "TK_RESTRICT",
+  "TK_ROLLBACK", "TK_ROW", "TK_RP", "TK_RSHIFT", "TK_SELECT", "TK_SEMI",
+  "TK_SET", "TK_SHORT", "TK_SLASH", "TK_SPACE", "TK_STAR", "TK_STATEMENT",
+  "TK_STRING", "TK_TABLE", "TK_TEMP", "TK_THEN", "TK_TRANSACTION",
+  "TK_TRIGGER", "TK_UMINUS", "TK_UNCLOSED_STRING", "TK_UNION", "TK_UNIQUE",
+  "TK_UPDATE", "TK_UPLUS", "TK_USING", "TK_VACUUM", "TK_VALUES", "TK_VIEW",
+  "TK_WHEN", "TK_WHERE", "TK_WILDCARD", "AGG_FUNCTION.", "COLUMN",
+  "FUNCTION", "COMMENT", "UNCLOSED_STRING", "SPACE", "ILLEGAL",
+  "END_OF_FILE", "$accept", "query", "onequery", "oneinsert", "onecreate",
+  "oneupdate", "onedelete", "table_def", "column_def", "column_and_type",
+  "column_type", "data_type_l", "data_type", "data_count", "oneselect",
+  "unorderedsel", "selectfrom", "selcollist", "from", "fromtable", "expr",
+  "val", "constlist", "update_assign_list", "column_assignment",
+  "const_val", "column_val", "column", "table", "id", "number", 0
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+   token YYLEX-NUM.  */
+static const unsigned short yytoknum[] =
+{
+       0,   256,   257,   258,   259,   260,   261,   262,   263,   264,
+     265,   266,   267,   268,   269,   270,   271,   272,   273,   274,
+     275,   276,   277,   278,   279,   280,   281,   282,   283,   284,
+     285,   286,   287,   288,   289,   290,   291,   292,   293,   294,
+     295,   296,   297,   298,   299,   300,   301,   302,   303,   304,
+     305,   306,   307,   308,   309,   310,   311,   312,   313,   314,
+     315,   316,   317,   318,   319,   320,   321,   322,   323,   324,
+     325,   326,   327,   328,   329,   330,   331,   332,   333,   334,
+     335,   336,   337,   338,   339,   340,   341,   342,   343,   344,
+     345,   346,   347,   348,   349,   350,   351,   352,   353,   354,
+     355,   356,   357,   358,   359,   360,   361,   362,   363,   364,
+     365,   366,   367,   368,   369,   370,   371,   372,   373,   374,
+     375,   376,   377,   378,   379,   380,   381,   382,   383,   384,
+     385,   386,   387,   388,   389,   390,   391,   392,   393,   394,
+     395,   396,   397,   398,   399,   400,   401
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives.  */
+static const unsigned char yyr1[] =
+{
+       0,   147,   148,   149,   149,   149,   149,   149,   150,   150,
+     151,   151,   152,   153,   154,   155,   155,   156,   157,   157,
+     158,   158,   159,   159,   159,   159,   159,   159,   159,   160,
+     161,   161,   162,   162,   163,   164,   164,   164,   165,   165,
+     166,   167,   167,   167,   167,   167,   167,   167,   167,   167,
+     167,   167,   167,   168,   168,   169,   169,   170,   170,   171,
+     172,   172,   172,   172,   173,   174,   174,   175,   176,   177
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN.  */
+static const unsigned char yyr2[] =
+{
+       0,     2,     1,     1,     1,     1,     1,     1,    10,    11,
+       6,     7,     6,     2,     4,     3,     1,     2,     1,     2,
+       1,     3,     1,     4,     1,     1,     1,     1,     1,     1,
+       4,     1,     2,     3,     2,     1,     3,     1,     1,     3,
+       2,     3,     3,     3,     3,     3,     3,     3,     3,     3,
+       3,     3,     4,     1,     1,     1,     3,     1,     3,     3,
+       1,     2,     1,     1,     1,     3,     1,     1,     1,     1
+};
+
+/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
+   STATE-NUM when YYTABLE doesn't specify something else to do.  Zero
+   means the default is an error.  */
+static const unsigned char yydefact[] =
+{
+       0,     0,     0,     0,     0,     0,     0,     2,     5,     4,
+       6,     7,     3,    31,     0,     0,    13,    38,     0,     0,
+      68,    37,    32,     0,    35,     0,    66,     0,    67,     1,
+       0,     0,    40,     0,     0,    33,    34,     0,     0,     0,
+       0,     0,     0,    39,     0,    64,     0,    36,    65,     0,
+      57,     0,    30,     0,     0,    16,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,    10,     0,     0,    22,    26,    27,    24,    28,    25,
+      17,    18,    20,    41,    43,    44,    69,     0,    62,    63,
+      45,    54,    42,    60,    49,    53,    46,     0,    51,    48,
+      47,    50,     0,    12,    58,    59,    11,    15,     0,     0,
+      19,     0,    61,    52,     0,    14,     0,    29,    21,     0,
+      23,     0,    55,     8,     0,     9,    56
+};
+
+/* YYDEFGOTO[NTERM-NUM]. */
+static const yysigned_char yydefgoto[] =
+{
+      -1,     6,     7,     8,     9,    10,    11,    53,    54,    55,
+      80,    81,    82,   116,    12,    13,    22,    23,    16,    17,
+      43,    90,   121,    49,    50,    91,    44,    45,    25,    26,
+      93
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+   STATE-NUM.  */
+#define YYPACT_NINF -111
+static const yysigned_char yypact[] =
+{
+     -28,  -110,   -38,   -46,   -32,   -42,    29,  -111,  -111,  -111,
+    -111,  -111,  -111,   -67,   -42,   -42,  -111,   -99,   -42,   -48,
+    -111,  -111,  -111,   -38,    17,     7,    11,   -62,  -111,  -111,
+      37,   -22,  -111,   -43,   -16,  -111,  -111,   -48,   -42,   -42,
+     -48,   -42,   -43,    -3,   -29,  -111,   -48,  -111,  -111,   -68,
+      48,    28,  -111,   -36,   -19,  -111,   -18,    -5,   -43,   -43,
+     -58,   -58,   -58,   -70,   -58,   -58,   -58,   -34,   -43,   -42,
+     -61,    20,   -42,     5,     2,  -111,  -111,  -111,  -111,  -111,
+    -111,     1,    -2,  -111,    -3,    -3,  -111,    19,  -111,  -111,
+    -111,  -111,  -111,  -111,  -111,  -111,  -111,    -1,  -111,  -111,
+    -111,  -111,   -41,    -3,  -111,  -111,  -111,  -111,   -48,    19,
+    -111,     3,  -111,  -111,    13,  -111,   -12,  -111,  -111,   -61,
+    -111,    -9,    91,   -13,   -61,  -111,  -111
+};
+
+/* YYPGOTO[NTERM-NUM].  */
+static const yysigned_char yypgoto[] =
+{
+    -111,  -111,  -111,  -111,  -111,  -111,  -111,  -111,  -111,    47,
+    -111,  -111,  -111,  -111,  -111,  -111,   101,   -27,    98,  -111,
+     -11,    52,     0,    53,  -111,   -53,    46,    -4,    85,    40,
+     -66
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]].  What to do in state STATE-NUM.  If
+   positive, shift that token.  If negative, reduce the rule which
+   number is the opposite.  If zero, do what YYDEFACT says.
+   If YYTABLE_NINF, syntax error.  */
+#define YYTABLE_NINF -68
+static const yysigned_char yytable[] =
+{
+      24,    74,    58,     1,    58,    72,    19,     2,    20,    86,
+      47,    14,    86,    52,    15,    24,    60,   105,    20,    67,
+      97,   112,    98,    20,    20,    61,    18,    87,    62,    29,
+      87,    57,    30,    24,    20,    51,    24,    56,    33,     3,
+      42,    37,    24,   117,    63,    28,    38,    84,    85,    64,
+     -67,    75,    39,    40,    28,    28,    65,   103,    28,    88,
+      66,    41,    88,    76,    77,    51,   122,    46,    56,    68,
+      21,   122,    69,    70,    71,    78,   102,    89,    48,   106,
+      89,   115,   108,    73,     4,   109,    21,   110,   111,    86,
+      27,   113,    59,   114,    59,   118,   119,    79,   120,    31,
+      32,   123,     5,    34,    24,    83,    92,    95,    95,   125,
+      95,    95,    95,    94,    96,   124,    99,   100,   101,   107,
+      35,    36,   104,     0,   126
+};
+
+static const short yycheck[] =
+{
+       4,    19,     7,    31,     7,    24,    38,    35,    66,    70,
+      37,   121,    70,    40,    52,    19,    45,    70,    66,    46,
+      90,    87,    92,    66,    66,    54,    72,    88,    57,     0,
+      88,    42,    99,    37,    66,    39,    40,    41,   137,    67,
+      83,    24,    46,   109,    73,     5,    39,    58,    59,    78,
+      39,    69,   114,    16,    14,    15,    85,    68,    18,   120,
+      89,    83,   120,    81,    82,    69,   119,    83,    72,   137,
+     118,   124,    24,    45,   110,    93,   110,   138,    38,    59,
+     138,   108,    77,   102,   112,    83,   118,    86,    90,    70,
+       5,    92,    97,   134,    97,    92,    83,   115,   110,    14,
+      15,   110,   130,    18,   108,   110,    60,    61,    62,   122,
+      64,    65,    66,    61,    62,    24,    64,    65,    66,    72,
+      19,    23,    69,    -1,   124
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+   symbol of state STATE-NUM.  */
+static const unsigned char yystos[] =
+{
+       0,    31,    35,    67,   112,   130,   148,   149,   150,   151,
+     152,   153,   161,   162,   121,    52,   165,   166,    72,    38,
+      66,   118,   163,   164,   174,   175,   176,   175,   176,     0,
+      99,   175,   175,   137,   175,   163,   165,    24,    39,   114,
+      16,    83,    83,   167,   173,   174,    83,   164,   176,   170,
+     171,   174,   164,   154,   155,   156,   174,   167,     7,    97,
+      45,    54,    57,    73,    78,    85,    89,   164,   137,    24,
+      45,   110,    24,   102,    19,    69,    81,    82,    93,   115,
+     157,   158,   159,   110,   167,   167,    70,    88,   120,   138,
+     168,   172,   173,   177,   168,   173,   168,    90,    92,   168,
+     168,   168,   110,   167,   170,   172,    59,   156,    77,    83,
+      86,    90,   177,    92,   134,   164,   160,   177,    92,    83,
+     110,   169,   172,   110,    24,   122,   169
+};
+
+#if ! defined (YYSIZE_T) && defined (__SIZE_TYPE__)
+# define YYSIZE_T __SIZE_TYPE__
+#endif
+#if ! defined (YYSIZE_T) && defined (size_t)
+# define YYSIZE_T size_t
+#endif
+#if ! defined (YYSIZE_T)
+# if defined (__STDC__) || defined (__cplusplus)
+#  include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYSIZE_T size_t
+# endif
+#endif
+#if ! defined (YYSIZE_T)
+# define YYSIZE_T unsigned int
+#endif
+
+#define yyerrok                (yyerrstatus = 0)
+#define yyclearin      (yychar = YYEMPTY)
+#define YYEMPTY                (-2)
+#define YYEOF          0
+
+#define YYACCEPT       goto yyacceptlab
+#define YYABORT                goto yyabortlab
+#define YYERROR                goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror.  This remains here temporarily
+   to ease the transition to the new meaning of YYERROR, for GCC.
+   Once GCC version 2 has supplanted version 1, this can go.  */
+
+#define YYFAIL         goto yyerrlab
+
+#define YYRECOVERING()  (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value)                                 \
+do                                                             \
+  if (yychar == YYEMPTY && yylen == 1)                         \
+    {                                                          \
+      yychar = (Token);                                                \
+      yylval = (Value);                                                \
+      yytoken = YYTRANSLATE (yychar);                          \
+      YYPOPSTACK;                                              \
+      goto yybackup;                                           \
+    }                                                          \
+  else                                                         \
+    {                                                          \
+      yyerror ("syntax error: cannot back up");\
+      YYERROR;                                                 \
+    }                                                          \
+while (0)
+
+#define YYTERROR       1
+#define YYERRCODE      256
+
+/* YYLLOC_DEFAULT -- Compute the default location (before the actions
+   are run).  */
+
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N)               \
+   ((Current).first_line   = (Rhs)[1].first_line,      \
+    (Current).first_column = (Rhs)[1].first_column,    \
+    (Current).last_line    = (Rhs)[N].last_line,       \
+    (Current).last_column  = (Rhs)[N].last_column)
+#endif
+
+/* YYLEX -- calling `yylex' with the right arguments.  */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (&yylval, YYLEX_PARAM)
+#else
+# define YYLEX yylex (&yylval)
+#endif
+
+/* Enable debugging if requested.  */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+#  include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args)                       \
+do {                                           \
+  if (yydebug)                                 \
+    YYFPRINTF Args;                            \
+} while (0)
+
+# define YYDSYMPRINT(Args)                     \
+do {                                           \
+  if (yydebug)                                 \
+    yysymprint Args;                           \
+} while (0)
+
+# define YYDSYMPRINTF(Title, Token, Value, Location)           \
+do {                                                           \
+  if (yydebug)                                                 \
+    {                                                          \
+      YYFPRINTF (stderr, "%s ", Title);                                \
+      yysymprint (stderr,                                      \
+                  Token, Value);       \
+      YYFPRINTF (stderr, "\n");                                        \
+    }                                                          \
+} while (0)
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included).                                                   |
+`------------------------------------------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yy_stack_print (short *bottom, short *top)
+#else
+static void
+yy_stack_print (bottom, top)
+    short *bottom;
+    short *top;
+#endif
+{
+  YYFPRINTF (stderr, "Stack now");
+  for (/* Nothing. */; bottom <= top; ++bottom)
+    YYFPRINTF (stderr, " %d", *bottom);
+  YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top)                           \
+do {                                                           \
+  if (yydebug)                                                 \
+    yy_stack_print ((Bottom), (Top));                          \
+} while (0)
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced.  |
+`------------------------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yy_reduce_print (int yyrule)
+#else
+static void
+yy_reduce_print (yyrule)
+    int yyrule;
+#endif
+{
+  int yyi;
+  unsigned int yylno = yyrline[yyrule];
+  YYFPRINTF (stderr, "Reducing stack by rule %d (line %u), ",
+             yyrule - 1, yylno);
+  /* Print the symbols being reduced, and their result.  */
+  for (yyi = yyprhs[yyrule]; 0 <= yyrhs[yyi]; yyi++)
+    YYFPRINTF (stderr, "%s ", yytname [yyrhs[yyi]]);
+  YYFPRINTF (stderr, "-> %s\n", yytname [yyr1[yyrule]]);
+}
+
+# define YY_REDUCE_PRINT(Rule)         \
+do {                                   \
+  if (yydebug)                         \
+    yy_reduce_print (Rule);            \
+} while (0)
+
+/* Nonzero means print parse trace.  It is left uninitialized so that
+   multiple parsers can coexist.  */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YYDSYMPRINT(Args)
+# define YYDSYMPRINTF(Title, Token, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks.  */
+#ifndef        YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+   if the built-in stack extension method is used).
+
+   Do not make this value too large; the results are undefined if
+   SIZE_MAX < YYSTACK_BYTES (YYMAXDEPTH)
+   evaluated with infinite-precision integer arithmetic.  */
+
+#if defined (YYMAXDEPTH) && YYMAXDEPTH == 0
+# undef YYMAXDEPTH
+#endif
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+\f
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+#  if defined (__GLIBC__) && defined (_STRING_H)
+#   define yystrlen strlen
+#  else
+/* Return the length of YYSTR.  */
+static YYSIZE_T
+#   if defined (__STDC__) || defined (__cplusplus)
+yystrlen (const char *yystr)
+#   else
+yystrlen (yystr)
+     const char *yystr;
+#   endif
+{
+  register const char *yys = yystr;
+
+  while (*yys++ != '\0')
+    continue;
+
+  return yys - yystr - 1;
+}
+#  endif
+# endif
+
+# ifndef yystpcpy
+#  if defined (__GLIBC__) && defined (_STRING_H) && defined (_GNU_SOURCE)
+#   define yystpcpy stpcpy
+#  else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+   YYDEST.  */
+static char *
+#   if defined (__STDC__) || defined (__cplusplus)
+yystpcpy (char *yydest, const char *yysrc)
+#   else
+yystpcpy (yydest, yysrc)
+     char *yydest;
+     const char *yysrc;
+#   endif
+{
+  register char *yyd = yydest;
+  register const char *yys = yysrc;
+
+  while ((*yyd++ = *yys++) != '\0')
+    continue;
+
+  return yyd - 1;
+}
+#  endif
+# endif
+
+#endif /* !YYERROR_VERBOSE */
+
+\f
+
+#if YYDEBUG
+/*--------------------------------.
+| Print this symbol on YYOUTPUT.  |
+`--------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yysymprint (FILE *yyoutput, int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yysymprint (yyoutput, yytype, yyvaluep)
+    FILE *yyoutput;
+    int yytype;
+    YYSTYPE *yyvaluep;
+#endif
+{
+  /* Pacify ``unused variable'' warnings.  */
+  (void) yyvaluep;
+
+  if (yytype < YYNTOKENS)
+    {
+      YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+# ifdef YYPRINT
+      YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# endif
+    }
+  else
+    YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+  switch (yytype)
+    {
+      default:
+        break;
+    }
+  YYFPRINTF (yyoutput, ")");
+}
+
+#endif /* ! YYDEBUG */
+/*-----------------------------------------------.
+| Release the memory associated to this symbol.  |
+`-----------------------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yydestruct (int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yydestruct (yytype, yyvaluep)
+    int yytype;
+    YYSTYPE *yyvaluep;
+#endif
+{
+  /* Pacify ``unused variable'' warnings.  */
+  (void) yyvaluep;
+
+  switch (yytype)
+    {
+
+      default:
+        break;
+    }
+}
+\f
+
+/* Prevent warnings from -Wmissing-prototypes.  */
+
+#ifdef YYPARSE_PARAM
+# if defined (__STDC__) || defined (__cplusplus)
+int yyparse (void *YYPARSE_PARAM);
+# else
+int yyparse ();
+# endif
+#else /* ! YYPARSE_PARAM */
+#if defined (__STDC__) || defined (__cplusplus)
+int yyparse (void);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+
+
+
+
+
+/*----------.
+| yyparse.  |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+# if defined (__STDC__) || defined (__cplusplus)
+int yyparse (void *YYPARSE_PARAM)
+# else
+int yyparse (YYPARSE_PARAM)
+  void *YYPARSE_PARAM;
+# endif
+#else /* ! YYPARSE_PARAM */
+#if defined (__STDC__) || defined (__cplusplus)
+int
+yyparse (void)
+#else
+int
+yyparse ()
+
+#endif
+#endif
+{
+  /* The lookahead symbol.  */
+int yychar;
+
+/* The semantic value of the lookahead symbol.  */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far.  */
+int yynerrs;
+
+  register int yystate;
+  register int yyn;
+  int yyresult;
+  /* Number of tokens to shift before error messages enabled.  */
+  int yyerrstatus;
+  /* Lookahead token as an internal (translated) token number.  */
+  int yytoken = 0;
+
+  /* Three stacks and their tools:
+     `yyss': related to states,
+     `yyvs': related to semantic values,
+     `yyls': related to locations.
+
+     Refer to the stacks thru separate pointers, to allow yyoverflow
+     to reallocate them elsewhere.  */
+
+  /* The state stack.  */
+  short        yyssa[YYINITDEPTH];
+  short *yyss = yyssa;
+  register short *yyssp;
+
+  /* The semantic value stack.  */
+  YYSTYPE yyvsa[YYINITDEPTH];
+  YYSTYPE *yyvs = yyvsa;
+  register YYSTYPE *yyvsp;
+
+
+
+#define YYPOPSTACK   (yyvsp--, yyssp--)
+
+  YYSIZE_T yystacksize = YYINITDEPTH;
+
+  /* The variables used to return semantic value and location from the
+     action routines.  */
+  YYSTYPE yyval;
+
+
+  /* When reducing, the number of symbols on the RHS of the reduced
+     rule.  */
+  int yylen;
+
+  YYDPRINTF ((stderr, "Starting parse\n"));
+
+  yystate = 0;
+  yyerrstatus = 0;
+  yynerrs = 0;
+  yychar = YYEMPTY;            /* Cause a token to be read.  */
+
+  /* Initialize stack pointers.
+     Waste one element of value and location stack
+     so that they stay on the same level as the state stack.
+     The wasted elements are never initialized.  */
+
+  yyssp = yyss;
+  yyvsp = yyvs;
+
+  goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate.  |
+`------------------------------------------------------------*/
+ yynewstate:
+  /* In all cases, when you get here, the value and location stacks
+     have just been pushed. so pushing a state here evens the stacks.
+     */
+  yyssp++;
+
+ yysetstate:
+  *yyssp = yystate;
+
+  if (yyss + yystacksize - 1 <= yyssp)
+    {
+      /* Get the current used size of the three stacks, in elements.  */
+      YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+      {
+       /* Give user a chance to reallocate the stack. Use copies of
+          these so that the &'s don't force the real ones into
+          memory.  */
+       YYSTYPE *yyvs1 = yyvs;
+       short *yyss1 = yyss;
+
+
+       /* Each stack pointer address is followed by the size of the
+          data in use in that stack, in bytes.  This used to be a
+          conditional around just the two extra args, but that might
+          be undefined if yyoverflow is a macro.  */
+       yyoverflow ("parser stack overflow",
+                   &yyss1, yysize * sizeof (*yyssp),
+                   &yyvs1, yysize * sizeof (*yyvsp),
+
+                   &yystacksize);
+
+       yyss = yyss1;
+       yyvs = yyvs1;
+      }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+      goto yyoverflowlab;
+# else
+      /* Extend the stack our own way.  */
+      if (YYMAXDEPTH <= yystacksize)
+       goto yyoverflowlab;
+      yystacksize *= 2;
+      if (YYMAXDEPTH < yystacksize)
+       yystacksize = YYMAXDEPTH;
+
+      {
+       short *yyss1 = yyss;
+       union yyalloc *yyptr =
+         (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+       if (! yyptr)
+         goto yyoverflowlab;
+       YYSTACK_RELOCATE (yyss);
+       YYSTACK_RELOCATE (yyvs);
+
+#  undef YYSTACK_RELOCATE
+       if (yyss1 != yyssa)
+         YYSTACK_FREE (yyss1);
+      }
+# endif
+#endif /* no yyoverflow */
+
+      yyssp = yyss + yysize - 1;
+      yyvsp = yyvs + yysize - 1;
+
+
+      YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+                 (unsigned long int) yystacksize));
+
+      if (yyss + yystacksize - 1 <= yyssp)
+       YYABORT;
+    }
+
+  YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+  goto yybackup;
+
+/*-----------.
+| yybackup.  |
+`-----------*/
+yybackup:
+
+/* Do appropriate processing given the current state.  */
+/* Read a lookahead token if we need one and don't already have one.  */
+/* yyresume: */
+
+  /* First try to decide what to do without reference to lookahead token.  */
+
+  yyn = yypact[yystate];
+  if (yyn == YYPACT_NINF)
+    goto yydefault;
+
+  /* Not known => get a lookahead token if don't already have one.  */
+
+  /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol.  */
+  if (yychar == YYEMPTY)
+    {
+      YYDPRINTF ((stderr, "Reading a token: "));
+      yychar = YYLEX;
+    }
+
+  if (yychar <= YYEOF)
+    {
+      yychar = yytoken = YYEOF;
+      YYDPRINTF ((stderr, "Now at end of input.\n"));
+    }
+  else
+    {
+      yytoken = YYTRANSLATE (yychar);
+      YYDSYMPRINTF ("Next token is", yytoken, &yylval, &yylloc);
+    }
+
+  /* If the proper action on seeing token YYTOKEN is to reduce or to
+     detect an error, take that action.  */
+  yyn += yytoken;
+  if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+    goto yydefault;
+  yyn = yytable[yyn];
+  if (yyn <= 0)
+    {
+      if (yyn == 0 || yyn == YYTABLE_NINF)
+       goto yyerrlab;
+      yyn = -yyn;
+      goto yyreduce;
+    }
+
+  if (yyn == YYFINAL)
+    YYACCEPT;
+
+  /* Shift the lookahead token.  */
+  YYDPRINTF ((stderr, "Shifting token %s, ", yytname[yytoken]));
+
+  /* Discard the token being shifted unless it is eof.  */
+  if (yychar != YYEOF)
+    yychar = YYEMPTY;
+
+  *++yyvsp = yylval;
+
+
+  /* Count tokens shifted since error; after three, turn off error
+     status.  */
+  if (yyerrstatus)
+    yyerrstatus--;
+
+  yystate = yyn;
+  goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state.  |
+`-----------------------------------------------------------*/
+yydefault:
+  yyn = yydefact[yystate];
+  if (yyn == 0)
+    goto yyerrlab;
+  goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction.  |
+`-----------------------------*/
+yyreduce:
+  /* yyn is the number of a rule to reduce with.  */
+  yylen = yyr2[yyn];
+
+  /* If YYLEN is nonzero, implement the default value of the action:
+     `$$ = $1'.
+
+     Otherwise, the following line sets YYVAL to garbage.
+     This behavior is undocumented and Bison
+     users should not rely upon it.  Assigning to YYVAL
+     unconditionally makes the parser a bit smaller, and it avoids a
+     GCC warning that YYVAL may be used uninitialized.  */
+  yyval = yyvsp[1-yylen];
+
+
+  YY_REDUCE_PRINT (yyn);
+  switch (yyn)
+    {
+        case 2:
+#line 139 "./sql.y"
+    {
+        SQL_input* sql = (SQL_input*) info;
+        *sql->view = yyvsp[0].query;
+    ;}
+    break;
+
+  case 8:
+#line 155 "./sql.y"
+    {
+            SQL_input *sql = (SQL_input*) info;
+            MSIVIEW *insert = NULL; 
+            UINT r;
+
+            r = INSERT_CreateView( sql->db, &insert, yyvsp[-7].string, yyvsp[-5].column_list, yyvsp[-1].column_list, FALSE ); 
+            if( !insert )
+                YYABORT;
+            yyval.query = insert;
+        ;}
+    break;
+
+  case 9:
+#line 166 "./sql.y"
+    {
+            SQL_input *sql = (SQL_input*) info;
+            MSIVIEW *insert = NULL; 
+
+            INSERT_CreateView( sql->db, &insert, yyvsp[-8].string, yyvsp[-6].column_list, yyvsp[-2].column_list, TRUE ); 
+            if( !insert )
+                YYABORT;
+            yyval.query = insert;
+        ;}
+    break;
+
+  case 10:
+#line 179 "./sql.y"
+    {
+            SQL_input* sql = (SQL_input*) info;
+            MSIVIEW *create = NULL; 
+
+            if( !yyvsp[-1].column_list )
+                YYABORT;
+            CREATE_CreateView( sql->db, &create, yyvsp[-3].string, yyvsp[-1].column_list, FALSE );
+            if( !create )
+                YYABORT;
+            yyval.query = create;
+        ;}
+    break;
+
+  case 11:
+#line 191 "./sql.y"
+    {
+            SQL_input* sql = (SQL_input*) info;
+            MSIVIEW *create = NULL; 
+
+            if( !yyvsp[-2].column_list )
+                YYABORT;
+            CREATE_CreateView( sql->db, &create, yyvsp[-4].string, yyvsp[-2].column_list, TRUE );
+            if( !create )
+                YYABORT;
+            yyval.query = create;
+        ;}
+    break;
+
+  case 12:
+#line 206 "./sql.y"
+    {
+            SQL_input* sql = (SQL_input*) info;
+            MSIVIEW *update = NULL; 
+
+            UPDATE_CreateView( sql->db, &update, yyvsp[-4].string, yyvsp[-2].column_list, yyvsp[0].expr );
+            if( !update )
+                YYABORT;
+            yyval.query = update;
+        ;}
+    break;
+
+  case 13:
+#line 219 "./sql.y"
+    {
+            SQL_input* sql = (SQL_input*) info;
+            MSIVIEW *delete = NULL; 
+
+            DELETE_CreateView( sql->db, &delete, yyvsp[0].query );
+            if( !delete )
+                YYABORT;
+            yyval.query = delete;
+        ;}
+    break;
+
+  case 14:
+#line 232 "./sql.y"
+    {
+            if( SQL_MarkPrimaryKeys( yyvsp[-3].column_list, yyvsp[0].column_list ) )
+                yyval.column_list = yyvsp[-3].column_list;
+            else
+                yyval.column_list = NULL;
+        ;}
+    break;
+
+  case 15:
+#line 242 "./sql.y"
+    {
+            column_info *ci;
+
+            for( ci = yyvsp[-2].column_list; ci->next; ci = ci->next )
+                ;
+
+            ci->next = yyvsp[0].column_list;
+            yyval.column_list = yyvsp[-2].column_list;
+        ;}
+    break;
+
+  case 16:
+#line 252 "./sql.y"
+    {
+            yyval.column_list = yyvsp[0].column_list;
+        ;}
+    break;
+
+  case 17:
+#line 259 "./sql.y"
+    {
+            yyval.column_list = yyvsp[-1].column_list;
+            yyval.column_list->type = yyvsp[0].column_type | MSITYPE_VALID;
+        ;}
+    break;
+
+  case 18:
+#line 267 "./sql.y"
+    {
+            yyval.column_type = yyvsp[0].column_type;
+        ;}
+    break;
+
+  case 19:
+#line 271 "./sql.y"
+    {
+            yyval.column_type = yyvsp[-1].column_type | MSITYPE_LOCALIZABLE;
+        ;}
+    break;
+
+  case 20:
+#line 278 "./sql.y"
+    {
+            yyval.column_type |= MSITYPE_NULLABLE;
+        ;}
+    break;
+
+  case 21:
+#line 282 "./sql.y"
+    {
+            yyval.column_type = yyvsp[-2].column_type;
+        ;}
+    break;
+
+  case 22:
+#line 289 "./sql.y"
+    {
+            yyval.column_type = MSITYPE_STRING | 1;
+        ;}
+    break;
+
+  case 23:
+#line 293 "./sql.y"
+    {
+            yyval.column_type = MSITYPE_STRING | 0x400 | yyvsp[-1].column_type;
+        ;}
+    break;
+
+  case 24:
+#line 297 "./sql.y"
+    {
+            yyval.column_type = 2;
+        ;}
+    break;
+
+  case 25:
+#line 301 "./sql.y"
+    {
+            yyval.column_type = 2;
+        ;}
+    break;
+
+  case 26:
+#line 305 "./sql.y"
+    {
+            yyval.column_type = 2;
+        ;}
+    break;
+
+  case 27:
+#line 309 "./sql.y"
+    {
+            yyval.column_type = 4;
+        ;}
+    break;
+
+  case 28:
+#line 313 "./sql.y"
+    {
+            yyval.column_type = MSITYPE_STRING | MSITYPE_VALID;
+        ;}
+    break;
+
+  case 29:
+#line 320 "./sql.y"
+    {
+            if( ( yyvsp[0].integer > 255 ) || ( yyvsp[0].integer < 0 ) )
+                YYABORT;
+            yyval.column_type = yyvsp[0].integer;
+        ;}
+    break;
+
+  case 30:
+#line 329 "./sql.y"
+    {
+            SQL_input* sql = (SQL_input*) info;
+
+            yyval.query = NULL;
+            if( yyvsp[0].column_list )
+                ORDER_CreateView( sql->db, &yyval.query, yyvsp[-3].query, yyvsp[0].column_list );
+            else
+                yyval.query = yyvsp[-3].query;
+            if( !yyval.query )
+                YYABORT;
+        ;}
+    break;
+
+  case 32:
+#line 345 "./sql.y"
+    {
+            yyval.query = yyvsp[0].query;
+        ;}
+    break;
+
+  case 33:
+#line 349 "./sql.y"
+    {
+            SQL_input* sql = (SQL_input*) info;
+            UINT r;
+
+            yyval.query = NULL;
+            r = DISTINCT_CreateView( sql->db, &yyval.query, yyvsp[0].query );
+            if (r != ERROR_SUCCESS)
+            {
+                yyvsp[0].query->ops->delete(yyvsp[0].query);
+                YYABORT;
+            }
+        ;}
+    break;
+
+  case 34:
+#line 365 "./sql.y"
+    {
+            SQL_input* sql = (SQL_input*) info;
+            UINT r;
+
+            yyval.query = NULL;
+            if( yyvsp[-1].column_list )
+            {
+                r = SELECT_CreateView( sql->db, &yyval.query, yyvsp[0].query, yyvsp[-1].column_list );
+                if (r != ERROR_SUCCESS)
+                {
+                    yyvsp[0].query->ops->delete(yyvsp[0].query);
+                    YYABORT;
+                }
+            }
+            else
+                yyval.query = yyvsp[0].query;
+        ;}
+    break;
+
+  case 36:
+#line 387 "./sql.y"
+    { 
+            yyvsp[-2].column_list->next = yyvsp[0].column_list;
+        ;}
+    break;
+
+  case 37:
+#line 391 "./sql.y"
+    {
+            yyval.column_list = NULL;
+        ;}
+    break;
+
+  case 39:
+#line 399 "./sql.y"
+    { 
+            SQL_input* sql = (SQL_input*) info;
+            UINT r;
+
+            yyval.query = NULL;
+            r = WHERE_CreateView( sql->db, &yyval.query, yyvsp[-2].query, yyvsp[0].expr );
+            if( r != ERROR_SUCCESS )
+            {
+                yyvsp[-2].query->ops->delete( yyvsp[-2].query );
+                YYABORT;
+            }
+        ;}
+    break;
+
+  case 40:
+#line 415 "./sql.y"
+    {
+            SQL_input* sql = (SQL_input*) info;
+            UINT r;
+
+            yyval.query = NULL;
+            r = TABLE_CreateView( sql->db, yyvsp[0].string, &yyval.query );
+            if( r != ERROR_SUCCESS || !yyval.query )
+                YYABORT;
+        ;}
+    break;
+
+  case 41:
+#line 428 "./sql.y"
+    {
+            yyval.expr = yyvsp[-1].expr;
+            if( !yyval.expr )
+                YYABORT;
+        ;}
+    break;
+
+  case 42:
+#line 434 "./sql.y"
+    {
+            yyval.expr = EXPR_complex( info, yyvsp[-2].expr, OP_EQ, yyvsp[0].expr );
+            if( !yyval.expr )
+                YYABORT;
+        ;}
+    break;
+
+  case 43:
+#line 440 "./sql.y"
+    {
+            yyval.expr = EXPR_complex( info, yyvsp[-2].expr, OP_AND, yyvsp[0].expr );
+            if( !yyval.expr )
+                YYABORT;
+        ;}
+    break;
+
+  case 44:
+#line 446 "./sql.y"
+    {
+            yyval.expr = EXPR_complex( info, yyvsp[-2].expr, OP_OR, yyvsp[0].expr );
+            if( !yyval.expr )
+                YYABORT;
+        ;}
+    break;
+
+  case 45:
+#line 452 "./sql.y"
+    {
+            yyval.expr = EXPR_complex( info, yyvsp[-2].expr, OP_EQ, yyvsp[0].expr );
+            if( !yyval.expr )
+                YYABORT;
+        ;}
+    break;
+
+  case 46:
+#line 458 "./sql.y"
+    {
+            yyval.expr = EXPR_complex( info, yyvsp[-2].expr, OP_GT, yyvsp[0].expr );
+            if( !yyval.expr )
+                YYABORT;
+        ;}
+    break;
+
+  case 47:
+#line 464 "./sql.y"
+    {
+            yyval.expr = EXPR_complex( info, yyvsp[-2].expr, OP_LT, yyvsp[0].expr );
+            if( !yyval.expr )
+                YYABORT;
+        ;}
+    break;
+
+  case 48:
+#line 470 "./sql.y"
+    {
+            yyval.expr = EXPR_complex( info, yyvsp[-2].expr, OP_LE, yyvsp[0].expr );
+            if( !yyval.expr )
+                YYABORT;
+        ;}
+    break;
+
+  case 49:
+#line 476 "./sql.y"
+    {
+            yyval.expr = EXPR_complex( info, yyvsp[-2].expr, OP_GE, yyvsp[0].expr );
+            if( !yyval.expr )
+                YYABORT;
+        ;}
+    break;
+
+  case 50:
+#line 482 "./sql.y"
+    {
+            yyval.expr = EXPR_complex( info, yyvsp[-2].expr, OP_NE, yyvsp[0].expr );
+            if( !yyval.expr )
+                YYABORT;
+        ;}
+    break;
+
+  case 51:
+#line 488 "./sql.y"
+    {
+            yyval.expr = EXPR_complex( info, yyvsp[-2].expr, OP_ISNULL, NULL );
+            if( !yyval.expr )
+                YYABORT;
+        ;}
+    break;
+
+  case 52:
+#line 494 "./sql.y"
+    {
+            yyval.expr = EXPR_complex( info, yyvsp[-3].expr, OP_NOTNULL, NULL );
+            if( !yyval.expr )
+                YYABORT;
+        ;}
+    break;
+
+  case 55:
+#line 508 "./sql.y"
+    {
+            yyval.column_list = parser_alloc_column( info, NULL, NULL );
+            if( !yyval.column_list )
+                YYABORT;
+            yyval.column_list->val = yyvsp[0].expr;
+        ;}
+    break;
+
+  case 56:
+#line 515 "./sql.y"
+    {
+            yyval.column_list = parser_alloc_column( info, NULL, NULL );
+            if( !yyval.column_list )
+                YYABORT;
+            yyval.column_list->val = yyvsp[-2].expr;
+            yyval.column_list->next = yyvsp[0].column_list;
+        ;}
+    break;
+
+  case 58:
+#line 527 "./sql.y"
+    {
+            yyval.column_list = yyvsp[-2].column_list;
+            yyval.column_list->next = yyvsp[0].column_list;
+        ;}
+    break;
+
+  case 59:
+#line 535 "./sql.y"
+    {
+            yyval.column_list = yyvsp[-2].column_list;
+            yyval.column_list->val = yyvsp[0].expr;
+        ;}
+    break;
+
+  case 60:
+#line 543 "./sql.y"
+    {
+            yyval.expr = EXPR_ival( info, yyvsp[0].integer );
+            if( !yyval.expr )
+                YYABORT;
+        ;}
+    break;
+
+  case 61:
+#line 549 "./sql.y"
+    {
+            yyval.expr = EXPR_ival( info, -yyvsp[0].integer );
+            if( !yyval.expr )
+                YYABORT;
+        ;}
+    break;
+
+  case 62:
+#line 555 "./sql.y"
+    {
+            yyval.expr = EXPR_sval( info, &yyvsp[0].str );
+            if( !yyval.expr )
+                YYABORT;
+        ;}
+    break;
+
+  case 63:
+#line 561 "./sql.y"
+    {
+            yyval.expr = EXPR_wildcard( info );
+            if( !yyval.expr )
+                YYABORT;
+        ;}
+    break;
+
+  case 64:
+#line 570 "./sql.y"
+    {
+            yyval.expr = EXPR_column( info, yyvsp[0].column_list );
+            if( !yyval.expr )
+                YYABORT;
+        ;}
+    break;
+
+  case 65:
+#line 579 "./sql.y"
+    {
+            yyval.column_list = parser_alloc_column( info, yyvsp[-2].string, yyvsp[0].string );
+            if( !yyval.column_list )
+                YYABORT;
+        ;}
+    break;
+
+  case 66:
+#line 585 "./sql.y"
+    {
+            yyval.column_list = parser_alloc_column( info, NULL, yyvsp[0].string );
+            if( !yyval.column_list )
+                YYABORT;
+        ;}
+    break;
+
+  case 67:
+#line 594 "./sql.y"
+    {
+            yyval.string = yyvsp[0].string;
+        ;}
+    break;
+
+  case 68:
+#line 601 "./sql.y"
+    {
+            yyval.string = SQL_getstring( info, &yyvsp[0].str );
+            if( !yyval.string )
+                YYABORT;
+        ;}
+    break;
+
+  case 69:
+#line 610 "./sql.y"
+    {
+            yyval.integer = SQL_getint( info );
+        ;}
+    break;
+
+
+    }
+
+/* Line 1000 of yacc.c.  */
+#line 2054 "sql.tab.c"
+\f
+  yyvsp -= yylen;
+  yyssp -= yylen;
+
+
+  YY_STACK_PRINT (yyss, yyssp);
+
+  *++yyvsp = yyval;
+
+
+  /* Now `shift' the result of the reduction.  Determine what state
+     that goes to, based on the state we popped back to and the rule
+     number reduced by.  */
+
+  yyn = yyr1[yyn];
+
+  yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+  if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+    yystate = yytable[yystate];
+  else
+    yystate = yydefgoto[yyn - YYNTOKENS];
+
+  goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+  /* If not already recovering from an error, report this error.  */
+  if (!yyerrstatus)
+    {
+      ++yynerrs;
+#if YYERROR_VERBOSE
+      yyn = yypact[yystate];
+
+      if (YYPACT_NINF < yyn && yyn < YYLAST)
+       {
+         YYSIZE_T yysize = 0;
+         int yytype = YYTRANSLATE (yychar);
+         const char* yyprefix;
+         char *yymsg;
+         int yyx;
+
+         /* Start YYX at -YYN if negative to avoid negative indexes in
+            YYCHECK.  */
+         int yyxbegin = yyn < 0 ? -yyn : 0;
+
+         /* Stay within bounds of both yycheck and yytname.  */
+         int yychecklim = YYLAST - yyn;
+         int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+         int yycount = 0;
+
+         yyprefix = ", expecting ";
+         for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+           if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+             {
+               yysize += yystrlen (yyprefix) + yystrlen (yytname [yyx]);
+               yycount += 1;
+               if (yycount == 5)
+                 {
+                   yysize = 0;
+                   break;
+                 }
+             }
+         yysize += (sizeof ("syntax error, unexpected ")
+                    + yystrlen (yytname[yytype]));
+         yymsg = (char *) YYSTACK_ALLOC (yysize);
+         if (yymsg != 0)
+           {
+             char *yyp = yystpcpy (yymsg, "syntax error, unexpected ");
+             yyp = yystpcpy (yyp, yytname[yytype]);
+
+             if (yycount < 5)
+               {
+                 yyprefix = ", expecting ";
+                 for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+                   if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+                     {
+                       yyp = yystpcpy (yyp, yyprefix);
+                       yyp = yystpcpy (yyp, yytname[yyx]);
+                       yyprefix = " or ";
+                     }
+               }
+             yyerror (yymsg);
+             YYSTACK_FREE (yymsg);
+           }
+         else
+           yyerror ("syntax error; also virtual memory exhausted");
+       }
+      else
+#endif /* YYERROR_VERBOSE */
+       yyerror ("syntax error");
+    }
+
+
+
+  if (yyerrstatus == 3)
+    {
+      /* If just tried and failed to reuse lookahead token after an
+        error, discard it.  */
+
+      if (yychar <= YYEOF)
+        {
+          /* If at end of input, pop the error token,
+            then the rest of the stack, then return failure.  */
+         if (yychar == YYEOF)
+            for (;;)
+              {
+                YYPOPSTACK;
+                if (yyssp == yyss)
+                  YYABORT;
+                YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp);
+                yydestruct (yystos[*yyssp], yyvsp);
+              }
+        }
+      else
+       {
+         YYDSYMPRINTF ("Error: discarding", yytoken, &yylval, &yylloc);
+         yydestruct (yytoken, &yylval);
+         yychar = YYEMPTY;
+
+       }
+    }
+
+  /* Else will try to reuse lookahead token after shifting the error
+     token.  */
+  goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR.  |
+`---------------------------------------------------*/
+yyerrorlab:
+
+#ifdef __GNUC__
+  /* Pacify GCC when the user code never invokes YYERROR and the label
+     yyerrorlab therefore never appears in user code.  */
+  if (0)
+     goto yyerrorlab;
+#endif
+
+  yyvsp -= yylen;
+  yyssp -= yylen;
+  yystate = *yyssp;
+  goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR.  |
+`-------------------------------------------------------------*/
+yyerrlab1:
+  yyerrstatus = 3;     /* Each real token shifted decrements this.  */
+
+  for (;;)
+    {
+      yyn = yypact[yystate];
+      if (yyn != YYPACT_NINF)
+       {
+         yyn += YYTERROR;
+         if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+           {
+             yyn = yytable[yyn];
+             if (0 < yyn)
+               break;
+           }
+       }
+
+      /* Pop the current state because it cannot handle the error token.  */
+      if (yyssp == yyss)
+       YYABORT;
+
+      YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp);
+      yydestruct (yystos[yystate], yyvsp);
+      YYPOPSTACK;
+      yystate = *yyssp;
+      YY_STACK_PRINT (yyss, yyssp);
+    }
+
+  if (yyn == YYFINAL)
+    YYACCEPT;
+
+  YYDPRINTF ((stderr, "Shifting error token, "));
+
+  *++yyvsp = yylval;
+
+
+  yystate = yyn;
+  goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here.  |
+`-------------------------------------*/
+yyacceptlab:
+  yyresult = 0;
+  goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here.  |
+`-----------------------------------*/
+yyabortlab:
+  yyresult = 1;
+  goto yyreturn;
+
+#ifndef yyoverflow
+/*----------------------------------------------.
+| yyoverflowlab -- parser overflow comes here.  |
+`----------------------------------------------*/
+yyoverflowlab:
+  yyerror ("parser stack overflow");
+  yyresult = 2;
+  /* Fall through.  */
+#endif
+
+yyreturn:
+#ifndef yyoverflow
+  if (yyss != yyssa)
+    YYSTACK_FREE (yyss);
+#endif
+  return yyresult;
+}
+
+
+#line 615 "./sql.y"
+
+
+static void *parser_alloc( void *info, unsigned int sz )
+{
+    SQL_input* sql = (SQL_input*) info;
+    struct list *mem;
+
+    mem = msi_alloc( sizeof (struct list) + sz );
+    list_add_tail( sql->mem, mem );
+    return &mem[1];
+}
+
+static column_info *parser_alloc_column( void *info, LPCWSTR table, LPCWSTR column )
+{
+    column_info *col;
+
+    col = parser_alloc( info, sizeof (*col) );
+    if( col )
+    {
+        col->table = table;
+        col->column = column;
+        col->val = NULL;
+        col->type = 0;
+        col->next = NULL;
+    }
+
+    return col;
+}
+
+int SQL_lex( void *SQL_lval, SQL_input *sql )
+{
+    int token;
+    struct sql_str * str = SQL_lval;
+
+    do
+    {
+        sql->n += sql->len;
+        if( ! sql->command[sql->n] )
+            return 0;  /* end of input */
+
+        /* TRACE("string : %s\n", debugstr_w(&sql->command[sql->n])); */
+        sql->len = sqliteGetToken( &sql->command[sql->n], &token );
+        if( sql->len==0 )
+            break;
+        str->data = &sql->command[sql->n];
+        str->len = sql->len;
+    }
+    while( token == TK_SPACE );
+
+    /* TRACE("token : %d (%s)\n", token, debugstr_wn(&sql->command[sql->n], sql->len)); */
+    
+    return token;
+}
+
+LPWSTR SQL_getstring( void *info, struct sql_str *strdata )
+{
+    LPCWSTR p = strdata->data;
+    UINT len = strdata->len;
+    LPWSTR str;
+
+    /* if there's quotes, remove them */
+    if( ( (p[0]=='`') && (p[len-1]=='`') ) || 
+        ( (p[0]=='\'') && (p[len-1]=='\'') ) )
+    {
+        p++;
+        len -= 2;
+    }
+    str = parser_alloc( info, (len + 1)*sizeof(WCHAR) );
+    if( !str )
+        return str;
+    memcpy( str, p, len*sizeof(WCHAR) );
+    str[len]=0;
+
+    return str;
+}
+
+INT SQL_getint( void *info )
+{
+    SQL_input* sql = (SQL_input*) info;
+    LPCWSTR p = &sql->command[sql->n];
+    INT i, r = 0;
+
+    for( i=0; i<sql->len; i++ )
+    {
+        if( '0' > p[i] || '9' < p[i] )
+        {
+            ERR("should only be numbers here!\n");
+            break;
+        }
+        r = (p[i]-'0') + r*10;
+    }
+
+    return r;
+}
+
+int SQL_error( const char *str )
+{
+    return 0;
+}
+
+static struct expr * EXPR_wildcard( void *info )
+{
+    struct expr *e = parser_alloc( info, sizeof *e );
+    if( e )
+    {
+        e->type = EXPR_WILDCARD;
+    }
+    return e;
+}
+
+static struct expr * EXPR_complex( void *info, struct expr *l, UINT op, struct expr *r )
+{
+    struct expr *e = parser_alloc( info, sizeof *e );
+    if( e )
+    {
+        e->type = EXPR_COMPLEX;
+        e->u.expr.left = l;
+        e->u.expr.op = op;
+        e->u.expr.right = r;
+    }
+    return e;
+}
+
+static struct expr * EXPR_column( void *info, column_info *column )
+{
+    struct expr *e = parser_alloc( info, sizeof *e );
+    if( e )
+    {
+        e->type = EXPR_COLUMN;
+        e->u.sval = column->column;
+    }
+    return e;
+}
+
+static struct expr * EXPR_ival( void *info, int val )
+{
+    struct expr *e = parser_alloc( info, sizeof *e );
+    if( e )
+    {
+        e->type = EXPR_IVAL;
+        e->u.ival = val;
+    }
+    return e;
+}
+
+static struct expr * EXPR_sval( void *info, struct sql_str *str )
+{
+    struct expr *e = parser_alloc( info, sizeof *e );
+    if( e )
+    {
+        e->type = EXPR_SVAL;
+        e->u.sval = SQL_getstring( info, str );
+    }
+    return e;
+}
+
+static BOOL SQL_MarkPrimaryKeys( column_info *cols,
+                                 column_info *keys )
+{
+    column_info *k;
+    BOOL found = TRUE;
+
+    for( k = keys; k && found; k = k->next )
+    {
+        column_info *c;
+
+        found = FALSE;
+        for( c = cols; c && !found; c = c->next )
+        {
+             if( lstrcmpW( k->column, c->column ) )
+                 continue;
+             c->type |= MSITYPE_KEY;
+             found = TRUE;
+        }
+    }
+
+    return found;
+}
+
+UINT MSI_ParseSQL( MSIDATABASE *db, LPCWSTR command, MSIVIEW **phview,
+                   struct list *mem )
+{
+    SQL_input sql;
+    int r;
+
+    *phview = NULL;
+
+    sql.db = db;
+    sql.command = command;
+    sql.n = 0;
+    sql.len = 0;
+    sql.view = phview;
+    sql.mem = mem;
+
+    r = SQL_parse(&sql);
+
+    TRACE("Parse returned %d\n", r);
+    if( r )
+    {
+        *sql.view = NULL;
+        return ERROR_BAD_QUERY_SYNTAX;
+    }
+
+    return ERROR_SUCCESS;
+}
+
diff --git a/reactos/dll/msi/sql.tab.h b/reactos/dll/msi/sql.tab.h
new file mode 100644 (file)
index 0000000..4726cbd
--- /dev/null
@@ -0,0 +1,345 @@
+/* A Bison parser, made by GNU Bison 1.875c.  */
+
+/* Skeleton parser for Yacc-like parsing with Bison,
+   Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program 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 General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, when this file is copied by Bison into a
+   Bison output file, you may use that output file without restriction.
+   This special exception was added by the Free Software Foundation
+   in version 1.24 of Bison.  */
+
+/* Tokens.  */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+   /* Put the tokens into the symbol table, so that GDB and other debuggers
+      know about them.  */
+   enum yytokentype {
+     TK_ABORT = 258,
+     TK_AFTER = 259,
+     TK_AGG_FUNCTION = 260,
+     TK_ALL = 261,
+     TK_AND = 262,
+     TK_AS = 263,
+     TK_ASC = 264,
+     TK_BEFORE = 265,
+     TK_BEGIN = 266,
+     TK_BETWEEN = 267,
+     TK_BITAND = 268,
+     TK_BITNOT = 269,
+     TK_BITOR = 270,
+     TK_BY = 271,
+     TK_CASCADE = 272,
+     TK_CASE = 273,
+     TK_CHAR = 274,
+     TK_CHECK = 275,
+     TK_CLUSTER = 276,
+     TK_COLLATE = 277,
+     TK_COLUMN = 278,
+     TK_COMMA = 279,
+     TK_COMMENT = 280,
+     TK_COMMIT = 281,
+     TK_CONCAT = 282,
+     TK_CONFLICT = 283,
+     TK_CONSTRAINT = 284,
+     TK_COPY = 285,
+     TK_CREATE = 286,
+     TK_DEFAULT = 287,
+     TK_DEFERRABLE = 288,
+     TK_DEFERRED = 289,
+     TK_DELETE = 290,
+     TK_DELIMITERS = 291,
+     TK_DESC = 292,
+     TK_DISTINCT = 293,
+     TK_DOT = 294,
+     TK_DROP = 295,
+     TK_EACH = 296,
+     TK_ELSE = 297,
+     TK_END = 298,
+     TK_END_OF_FILE = 299,
+     TK_EQ = 300,
+     TK_EXCEPT = 301,
+     TK_EXPLAIN = 302,
+     TK_FAIL = 303,
+     TK_FLOAT = 304,
+     TK_FOR = 305,
+     TK_FOREIGN = 306,
+     TK_FROM = 307,
+     TK_FUNCTION = 308,
+     TK_GE = 309,
+     TK_GLOB = 310,
+     TK_GROUP = 311,
+     TK_GT = 312,
+     TK_HAVING = 313,
+     TK_HOLD = 314,
+     TK_IGNORE = 315,
+     TK_ILLEGAL = 316,
+     TK_IMMEDIATE = 317,
+     TK_IN = 318,
+     TK_INDEX = 319,
+     TK_INITIALLY = 320,
+     TK_ID = 321,
+     TK_INSERT = 322,
+     TK_INSTEAD = 323,
+     TK_INT = 324,
+     TK_INTEGER = 325,
+     TK_INTERSECT = 326,
+     TK_INTO = 327,
+     TK_IS = 328,
+     TK_ISNULL = 329,
+     TK_JOIN = 330,
+     TK_JOIN_KW = 331,
+     TK_KEY = 332,
+     TK_LE = 333,
+     TK_LIKE = 334,
+     TK_LIMIT = 335,
+     TK_LONG = 336,
+     TK_LONGCHAR = 337,
+     TK_LP = 338,
+     TK_LSHIFT = 339,
+     TK_LT = 340,
+     TK_LOCALIZABLE = 341,
+     TK_MATCH = 342,
+     TK_MINUS = 343,
+     TK_NE = 344,
+     TK_NOT = 345,
+     TK_NOTNULL = 346,
+     TK_NULL = 347,
+     TK_OBJECT = 348,
+     TK_OF = 349,
+     TK_OFFSET = 350,
+     TK_ON = 351,
+     TK_OR = 352,
+     TK_ORACLE_OUTER_JOIN = 353,
+     TK_ORDER = 354,
+     TK_PLUS = 355,
+     TK_PRAGMA = 356,
+     TK_PRIMARY = 357,
+     TK_RAISE = 358,
+     TK_REFERENCES = 359,
+     TK_REM = 360,
+     TK_REPLACE = 361,
+     TK_RESTRICT = 362,
+     TK_ROLLBACK = 363,
+     TK_ROW = 364,
+     TK_RP = 365,
+     TK_RSHIFT = 366,
+     TK_SELECT = 367,
+     TK_SEMI = 368,
+     TK_SET = 369,
+     TK_SHORT = 370,
+     TK_SLASH = 371,
+     TK_SPACE = 372,
+     TK_STAR = 373,
+     TK_STATEMENT = 374,
+     TK_STRING = 375,
+     TK_TABLE = 376,
+     TK_TEMP = 377,
+     TK_THEN = 378,
+     TK_TRANSACTION = 379,
+     TK_TRIGGER = 380,
+     TK_UMINUS = 381,
+     TK_UNCLOSED_STRING = 382,
+     TK_UNION = 383,
+     TK_UNIQUE = 384,
+     TK_UPDATE = 385,
+     TK_UPLUS = 386,
+     TK_USING = 387,
+     TK_VACUUM = 388,
+     TK_VALUES = 389,
+     TK_VIEW = 390,
+     TK_WHEN = 391,
+     TK_WHERE = 392,
+     TK_WILDCARD = 393,
+     COLUMN = 395,
+     FUNCTION = 396,
+     COMMENT = 397,
+     UNCLOSED_STRING = 398,
+     SPACE = 399,
+     ILLEGAL = 400,
+     END_OF_FILE = 401
+   };
+#endif
+#define TK_ABORT 258
+#define TK_AFTER 259
+#define TK_AGG_FUNCTION 260
+#define TK_ALL 261
+#define TK_AND 262
+#define TK_AS 263
+#define TK_ASC 264
+#define TK_BEFORE 265
+#define TK_BEGIN 266
+#define TK_BETWEEN 267
+#define TK_BITAND 268
+#define TK_BITNOT 269
+#define TK_BITOR 270
+#define TK_BY 271
+#define TK_CASCADE 272
+#define TK_CASE 273
+#define TK_CHAR 274
+#define TK_CHECK 275
+#define TK_CLUSTER 276
+#define TK_COLLATE 277
+#define TK_COLUMN 278
+#define TK_COMMA 279
+#define TK_COMMENT 280
+#define TK_COMMIT 281
+#define TK_CONCAT 282
+#define TK_CONFLICT 283
+#define TK_CONSTRAINT 284
+#define TK_COPY 285
+#define TK_CREATE 286
+#define TK_DEFAULT 287
+#define TK_DEFERRABLE 288
+#define TK_DEFERRED 289
+#define TK_DELETE 290
+#define TK_DELIMITERS 291
+#define TK_DESC 292
+#define TK_DISTINCT 293
+#define TK_DOT 294
+#define TK_DROP 295
+#define TK_EACH 296
+#define TK_ELSE 297
+#define TK_END 298
+#define TK_END_OF_FILE 299
+#define TK_EQ 300
+#define TK_EXCEPT 301
+#define TK_EXPLAIN 302
+#define TK_FAIL 303
+#define TK_FLOAT 304
+#define TK_FOR 305
+#define TK_FOREIGN 306
+#define TK_FROM 307
+#define TK_FUNCTION 308
+#define TK_GE 309
+#define TK_GLOB 310
+#define TK_GROUP 311
+#define TK_GT 312
+#define TK_HAVING 313
+#define TK_HOLD 314
+#define TK_IGNORE 315
+#define TK_ILLEGAL 316
+#define TK_IMMEDIATE 317
+#define TK_IN 318
+#define TK_INDEX 319
+#define TK_INITIALLY 320
+#define TK_ID 321
+#define TK_INSERT 322
+#define TK_INSTEAD 323
+#define TK_INT 324
+#define TK_INTEGER 325
+#define TK_INTERSECT 326
+#define TK_INTO 327
+#define TK_IS 328
+#define TK_ISNULL 329
+#define TK_JOIN 330
+#define TK_JOIN_KW 331
+#define TK_KEY 332
+#define TK_LE 333
+#define TK_LIKE 334
+#define TK_LIMIT 335
+#define TK_LONG 336
+#define TK_LONGCHAR 337
+#define TK_LP 338
+#define TK_LSHIFT 339
+#define TK_LT 340
+#define TK_LOCALIZABLE 341
+#define TK_MATCH 342
+#define TK_MINUS 343
+#define TK_NE 344
+#define TK_NOT 345
+#define TK_NOTNULL 346
+#define TK_NULL 347
+#define TK_OBJECT 348
+#define TK_OF 349
+#define TK_OFFSET 350
+#define TK_ON 351
+#define TK_OR 352
+#define TK_ORACLE_OUTER_JOIN 353
+#define TK_ORDER 354
+#define TK_PLUS 355
+#define TK_PRAGMA 356
+#define TK_PRIMARY 357
+#define TK_RAISE 358
+#define TK_REFERENCES 359
+#define TK_REM 360
+#define TK_REPLACE 361
+#define TK_RESTRICT 362
+#define TK_ROLLBACK 363
+#define TK_ROW 364
+#define TK_RP 365
+#define TK_RSHIFT 366
+#define TK_SELECT 367
+#define TK_SEMI 368
+#define TK_SET 369
+#define TK_SHORT 370
+#define TK_SLASH 371
+#define TK_SPACE 372
+#define TK_STAR 373
+#define TK_STATEMENT 374
+#define TK_STRING 375
+#define TK_TABLE 376
+#define TK_TEMP 377
+#define TK_THEN 378
+#define TK_TRANSACTION 379
+#define TK_TRIGGER 380
+#define TK_UMINUS 381
+#define TK_UNCLOSED_STRING 382
+#define TK_UNION 383
+#define TK_UNIQUE 384
+#define TK_UPDATE 385
+#define TK_UPLUS 386
+#define TK_USING 387
+#define TK_VACUUM 388
+#define TK_VALUES 389
+#define TK_VIEW 390
+#define TK_WHEN 391
+#define TK_WHERE 392
+#define TK_WILDCARD 393
+#define COLUMN 395
+#define FUNCTION 396
+#define COMMENT 397
+#define UNCLOSED_STRING 398
+#define SPACE 399
+#define ILLEGAL 400
+#define END_OF_FILE 401
+
+
+
+
+#if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED)
+#line 72 "./sql.y"
+typedef union YYSTYPE {
+    struct sql_str str;
+    LPWSTR string;
+    column_info *column_list;
+    MSIVIEW *query;
+    struct expr *expr;
+    USHORT column_type;
+    int integer;
+} YYSTYPE;
+/* Line 1275 of yacc.c.  */
+#line 337 "sql.tab.h"
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+
+
+
+
diff --git a/reactos/dll/msi/sql.y b/reactos/dll/msi/sql.y
new file mode 100644 (file)
index 0000000..6669441
--- /dev/null
@@ -0,0 +1,819 @@
+%{
+
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2002-2004 Mike McCormack for CodeWeavers
+ *
+ * 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 <stdlib.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "query.h"
+#include "wine/list.h"
+#include "wine/debug.h"
+
+#define YYLEX_PARAM info
+#define YYPARSE_PARAM info
+
+extern int SQL_error(const char *str);
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+typedef struct tag_SQL_input
+{
+    MSIDATABASE *db;
+    LPCWSTR command;
+    DWORD n, len;
+    MSIVIEW **view;  /* view structure for the resulting query */
+    struct list *mem;
+} SQL_input;
+
+static LPWSTR SQL_getstring( void *info, struct sql_str *str );
+static INT SQL_getint( void *info );
+static int SQL_lex( void *SQL_lval, SQL_input *info );
+
+static void *parser_alloc( void *info, unsigned int sz );
+static column_info *parser_alloc_column( void *info, LPCWSTR table, LPCWSTR column );
+
+static BOOL SQL_MarkPrimaryKeys( column_info *cols, column_info *keys);
+
+static struct expr * EXPR_complex( void *info, struct expr *l, UINT op, struct expr *r );
+static struct expr * EXPR_column( void *info, column_info *column );
+static struct expr * EXPR_ival( void *info, int val );
+static struct expr * EXPR_sval( void *info, struct sql_str * );
+static struct expr * EXPR_wildcard( void *info );
+
+%}
+
+%pure-parser
+
+%union
+{
+    struct sql_str str;
+    LPWSTR string;
+    column_info *column_list;
+    MSIVIEW *query;
+    struct expr *expr;
+    USHORT column_type;
+    int integer;
+}
+
+%token TK_ABORT TK_AFTER TK_AGG_FUNCTION TK_ALL TK_AND TK_AS TK_ASC
+%token TK_BEFORE TK_BEGIN TK_BETWEEN TK_BITAND TK_BITNOT TK_BITOR TK_BY
+%token TK_CASCADE TK_CASE TK_CHAR TK_CHECK TK_CLUSTER TK_COLLATE TK_COLUMN
+%token TK_COMMA TK_COMMENT TK_COMMIT TK_CONCAT TK_CONFLICT 
+%token TK_CONSTRAINT TK_COPY TK_CREATE
+%token TK_DEFAULT TK_DEFERRABLE TK_DEFERRED TK_DELETE TK_DELIMITERS TK_DESC
+%token TK_DISTINCT TK_DOT TK_DROP TK_EACH
+%token TK_ELSE TK_END TK_END_OF_FILE TK_EQ TK_EXCEPT TK_EXPLAIN
+%token TK_FAIL TK_FLOAT TK_FOR TK_FOREIGN TK_FROM TK_FUNCTION
+%token TK_GE TK_GLOB TK_GROUP TK_GT
+%token TK_HAVING TK_HOLD
+%token TK_IGNORE TK_ILLEGAL TK_IMMEDIATE TK_IN TK_INDEX TK_INITIALLY
+%token <str> TK_ID 
+%token TK_INSERT TK_INSTEAD TK_INT 
+%token <str> TK_INTEGER
+%token TK_INTERSECT TK_INTO TK_IS
+%token TK_ISNULL
+%token TK_JOIN TK_JOIN_KW
+%token TK_KEY
+%token TK_LE TK_LIKE TK_LIMIT TK_LONG TK_LONGCHAR TK_LP TK_LSHIFT TK_LT
+%token TK_LOCALIZABLE
+%token TK_MATCH TK_MINUS
+%token TK_NE TK_NOT TK_NOTNULL TK_NULL
+%token TK_OBJECT TK_OF TK_OFFSET TK_ON TK_OR TK_ORACLE_OUTER_JOIN TK_ORDER
+%token TK_PLUS TK_PRAGMA TK_PRIMARY
+%token TK_RAISE TK_REFERENCES TK_REM TK_REPLACE TK_RESTRICT TK_ROLLBACK
+%token TK_ROW TK_RP TK_RSHIFT
+%token TK_SELECT TK_SEMI TK_SET TK_SHORT TK_SLASH TK_SPACE TK_STAR TK_STATEMENT 
+%token <str> TK_STRING
+%token TK_TABLE TK_TEMP TK_THEN TK_TRANSACTION TK_TRIGGER
+%token TK_UMINUS TK_UNCLOSED_STRING TK_UNION TK_UNIQUE
+%token TK_UPDATE TK_UPLUS TK_USING
+%token TK_VACUUM TK_VALUES TK_VIEW
+%token TK_WHEN TK_WHERE TK_WILDCARD
+
+/*
+ * These are extra tokens used by the lexer but never seen by the
+ * parser.  We put them in a rule so that the parser generator will
+ * add them to the parse.h output file.
+ *
+ */
+%nonassoc END_OF_FILE ILLEGAL SPACE UNCLOSED_STRING COMMENT FUNCTION
+          COLUMN AGG_FUNCTION.
+
+%type <string> table id
+%type <column_list> selcollist column column_and_type column_def table_def
+%type <column_list> column_assignment update_assign_list constlist
+%type <query> query from fromtable selectfrom unorderedsel
+%type <query> oneupdate onedelete oneselect onequery onecreate oneinsert
+%type <expr> expr val column_val const_val
+%type <column_type> column_type data_type data_type_l data_count
+%type <integer> number
+
+%%
+
+query:
+    onequery
+    {
+        SQL_input* sql = (SQL_input*) info;
+        *sql->view = $1;
+    }
+    ;
+
+onequery:
+    oneselect
+  | onecreate
+  | oneinsert
+  | oneupdate
+  | onedelete
+    ;
+
+oneinsert:
+    TK_INSERT TK_INTO table TK_LP selcollist TK_RP TK_VALUES TK_LP constlist TK_RP
+        {
+            SQL_input *sql = (SQL_input*) info;
+            MSIVIEW *insert = NULL; 
+            UINT r;
+
+            r = INSERT_CreateView( sql->db, &insert, $3, $5, $9, FALSE ); 
+            if( !insert )
+                YYABORT;
+            $$ = insert;
+        }
+  | TK_INSERT TK_INTO table TK_LP selcollist TK_RP TK_VALUES TK_LP constlist TK_RP TK_TEMP
+        {
+            SQL_input *sql = (SQL_input*) info;
+            MSIVIEW *insert = NULL; 
+
+            INSERT_CreateView( sql->db, &insert, $3, $5, $9, TRUE ); 
+            if( !insert )
+                YYABORT;
+            $$ = insert;
+        }
+    ;
+
+onecreate:
+    TK_CREATE TK_TABLE table TK_LP table_def TK_RP
+        {
+            SQL_input* sql = (SQL_input*) info;
+            MSIVIEW *create = NULL; 
+
+            if( !$5 )
+                YYABORT;
+            CREATE_CreateView( sql->db, &create, $3, $5, FALSE );
+            if( !create )
+                YYABORT;
+            $$ = create;
+        }
+  | TK_CREATE TK_TABLE table TK_LP table_def TK_RP TK_HOLD
+        {
+            SQL_input* sql = (SQL_input*) info;
+            MSIVIEW *create = NULL; 
+
+            if( !$5 )
+                YYABORT;
+            CREATE_CreateView( sql->db, &create, $3, $5, TRUE );
+            if( !create )
+                YYABORT;
+            $$ = create;
+        }
+    ;
+
+oneupdate:
+    TK_UPDATE table TK_SET update_assign_list TK_WHERE expr
+        {
+            SQL_input* sql = (SQL_input*) info;
+            MSIVIEW *update = NULL; 
+
+            UPDATE_CreateView( sql->db, &update, $2, $4, $6 );
+            if( !update )
+                YYABORT;
+            $$ = update;
+        }
+    ;
+
+onedelete:
+    TK_DELETE from
+        {
+            SQL_input* sql = (SQL_input*) info;
+            MSIVIEW *delete = NULL; 
+
+            DELETE_CreateView( sql->db, &delete, $2 );
+            if( !delete )
+                YYABORT;
+            $$ = delete;
+        }
+    ;
+
+table_def:
+    column_def TK_PRIMARY TK_KEY selcollist
+        {
+            if( SQL_MarkPrimaryKeys( $1, $4 ) )
+                $$ = $1;
+            else
+                $$ = NULL;
+        }
+    ;
+
+column_def:
+    column_def TK_COMMA column_and_type
+        {
+            column_info *ci;
+
+            for( ci = $1; ci->next; ci = ci->next )
+                ;
+
+            ci->next = $3;
+            $$ = $1;
+        }
+  | column_and_type
+        {
+            $$ = $1;
+        }
+    ;
+
+column_and_type:
+    column column_type
+        {
+            $$ = $1;
+            $$->type = $2 | MSITYPE_VALID;
+        }
+    ;
+
+column_type:
+    data_type_l
+        {
+            $$ = $1;
+        }
+  | data_type_l TK_LOCALIZABLE
+        {
+            $$ = $1 | MSITYPE_LOCALIZABLE;
+        }
+    ;
+
+data_type_l:
+    data_type
+        {
+            $$ |= MSITYPE_NULLABLE;
+        }
+  | data_type TK_NOT TK_NULL
+        {
+            $$ = $1;
+        }
+    ;
+
+data_type:
+    TK_CHAR
+        {
+            $$ = MSITYPE_STRING | 1;
+        }
+  | TK_CHAR TK_LP data_count TK_RP
+        {
+            $$ = MSITYPE_STRING | 0x400 | $3;
+        }
+  | TK_LONGCHAR
+        {
+            $$ = 2;
+        }
+  | TK_SHORT
+        {
+            $$ = 2;
+        }
+  | TK_INT
+        {
+            $$ = 2;
+        }
+  | TK_LONG
+        {
+            $$ = 4;
+        }
+  | TK_OBJECT
+        {
+            $$ = MSITYPE_STRING | MSITYPE_VALID;
+        }
+    ;
+
+data_count:
+    number
+        {
+            if( ( $1 > 255 ) || ( $1 < 0 ) )
+                YYABORT;
+            $$ = $1;
+        }
+    ;
+
+oneselect:
+    unorderedsel TK_ORDER TK_BY selcollist
+        {
+            SQL_input* sql = (SQL_input*) info;
+
+            $$ = NULL;
+            if( $4 )
+                ORDER_CreateView( sql->db, &$$, $1, $4 );
+            else
+                $$ = $1;
+            if( !$$ )
+                YYABORT;
+        }
+  | unorderedsel
+    ;
+
+unorderedsel:
+    TK_SELECT selectfrom
+        {
+            $$ = $2;
+        }
+  | TK_SELECT TK_DISTINCT selectfrom
+        {
+            SQL_input* sql = (SQL_input*) info;
+            UINT r;
+
+            $$ = NULL;
+            r = DISTINCT_CreateView( sql->db, &$$, $3 );
+            if (r != ERROR_SUCCESS)
+            {
+                $3->ops->delete($3);
+                YYABORT;
+            }
+        }
+    ;
+
+selectfrom:
+    selcollist from 
+        {
+            SQL_input* sql = (SQL_input*) info;
+            UINT r;
+
+            $$ = NULL;
+            if( $1 )
+            {
+                r = SELECT_CreateView( sql->db, &$$, $2, $1 );
+                if (r != ERROR_SUCCESS)
+                {
+                    $2->ops->delete($2);
+                    YYABORT;
+                }
+            }
+            else
+                $$ = $2;
+        }
+    ;
+
+selcollist:
+    column 
+  | column TK_COMMA selcollist
+        { 
+            $1->next = $3;
+        }
+  | TK_STAR
+        {
+            $$ = NULL;
+        }
+    ;
+
+from:
+    fromtable
+  | fromtable TK_WHERE expr
+        { 
+            SQL_input* sql = (SQL_input*) info;
+            UINT r;
+
+            $$ = NULL;
+            r = WHERE_CreateView( sql->db, &$$, $1, $3 );
+            if( r != ERROR_SUCCESS )
+            {
+                $1->ops->delete( $1 );
+                YYABORT;
+            }
+        }
+    ;
+
+fromtable:
+    TK_FROM table
+        {
+            SQL_input* sql = (SQL_input*) info;
+            UINT r;
+
+            $$ = NULL;
+            r = TABLE_CreateView( sql->db, $2, &$$ );
+            if( r != ERROR_SUCCESS || !$$ )
+                YYABORT;
+        }
+    ;
+
+expr:
+    TK_LP expr TK_RP
+        {
+            $$ = $2;
+            if( !$$ )
+                YYABORT;
+        }
+  | column_val TK_EQ column_val
+        {
+            $$ = EXPR_complex( info, $1, OP_EQ, $3 );
+            if( !$$ )
+                YYABORT;
+        }
+  | expr TK_AND expr
+        {
+            $$ = EXPR_complex( info, $1, OP_AND, $3 );
+            if( !$$ )
+                YYABORT;
+        }
+  | expr TK_OR expr
+        {
+            $$ = EXPR_complex( info, $1, OP_OR, $3 );
+            if( !$$ )
+                YYABORT;
+        }
+  | column_val TK_EQ val
+        {
+            $$ = EXPR_complex( info, $1, OP_EQ, $3 );
+            if( !$$ )
+                YYABORT;
+        }
+  | column_val TK_GT val
+        {
+            $$ = EXPR_complex( info, $1, OP_GT, $3 );
+            if( !$$ )
+                YYABORT;
+        }
+  | column_val TK_LT val
+        {
+            $$ = EXPR_complex( info, $1, OP_LT, $3 );
+            if( !$$ )
+                YYABORT;
+        }
+  | column_val TK_LE val
+        {
+            $$ = EXPR_complex( info, $1, OP_LE, $3 );
+            if( !$$ )
+                YYABORT;
+        }
+  | column_val TK_GE val
+        {
+            $$ = EXPR_complex( info, $1, OP_GE, $3 );
+            if( !$$ )
+                YYABORT;
+        }
+  | column_val TK_NE val
+        {
+            $$ = EXPR_complex( info, $1, OP_NE, $3 );
+            if( !$$ )
+                YYABORT;
+        }
+  | column_val TK_IS TK_NULL
+        {
+            $$ = EXPR_complex( info, $1, OP_ISNULL, NULL );
+            if( !$$ )
+                YYABORT;
+        }
+  | column_val TK_IS TK_NOT TK_NULL
+        {
+            $$ = EXPR_complex( info, $1, OP_NOTNULL, NULL );
+            if( !$$ )
+                YYABORT;
+        }
+    ;
+
+val:
+    column_val
+  | const_val
+    ;
+
+constlist:
+    const_val
+        {
+            $$ = parser_alloc_column( info, NULL, NULL );
+            if( !$$ )
+                YYABORT;
+            $$->val = $1;
+        }
+  | const_val TK_COMMA constlist
+        {
+            $$ = parser_alloc_column( info, NULL, NULL );
+            if( !$$ )
+                YYABORT;
+            $$->val = $1;
+            $$->next = $3;
+        }
+    ;
+
+update_assign_list:
+    column_assignment
+  | column_assignment TK_COMMA update_assign_list
+        {
+            $$ = $1;
+            $$->next = $3;
+        }
+    ;
+
+column_assignment:
+    column TK_EQ const_val
+        {
+            $$ = $1;
+            $$->val = $3;
+        }
+    ;
+
+const_val:
+    number
+        {
+            $$ = EXPR_ival( info, $1 );
+            if( !$$ )
+                YYABORT;
+        }
+  | TK_MINUS number
+        {
+            $$ = EXPR_ival( info, -$2 );
+            if( !$$ )
+                YYABORT;
+        }
+  | TK_STRING
+        {
+            $$ = EXPR_sval( info, &$1 );
+            if( !$$ )
+                YYABORT;
+        }
+  | TK_WILDCARD
+        {
+            $$ = EXPR_wildcard( info );
+            if( !$$ )
+                YYABORT;
+        }
+    ;
+
+column_val:
+    column 
+        {
+            $$ = EXPR_column( info, $1 );
+            if( !$$ )
+                YYABORT;
+        }
+    ;
+
+column:
+    table TK_DOT id
+        {
+            $$ = parser_alloc_column( info, $1, $3 );
+            if( !$$ )
+                YYABORT;
+        }
+  | id
+        {
+            $$ = parser_alloc_column( info, NULL, $1 );
+            if( !$$ )
+                YYABORT;
+        }
+    ;
+
+table:
+    id
+        {
+            $$ = $1;
+        }
+    ;
+
+id:
+    TK_ID
+        {
+            $$ = SQL_getstring( info, &$1 );
+            if( !$$ )
+                YYABORT;
+        }
+    ;
+
+number:
+    TK_INTEGER
+        {
+            $$ = SQL_getint( info );
+        }
+    ;
+
+%%
+
+static void *parser_alloc( void *info, unsigned int sz )
+{
+    SQL_input* sql = (SQL_input*) info;
+    struct list *mem;
+
+    mem = msi_alloc( sizeof (struct list) + sz );
+    list_add_tail( sql->mem, mem );
+    return &mem[1];
+}
+
+static column_info *parser_alloc_column( void *info, LPCWSTR table, LPCWSTR column )
+{
+    column_info *col;
+
+    col = parser_alloc( info, sizeof (*col) );
+    if( col )
+    {
+        col->table = table;
+        col->column = column;
+        col->val = NULL;
+        col->type = 0;
+        col->next = NULL;
+    }
+
+    return col;
+}
+
+int SQL_lex( void *SQL_lval, SQL_input *sql )
+{
+    int token;
+    struct sql_str * str = SQL_lval;
+
+    do
+    {
+        sql->n += sql->len;
+        if( ! sql->command[sql->n] )
+            return 0;  /* end of input */
+
+        /* TRACE("string : %s\n", debugstr_w(&sql->command[sql->n])); */
+        sql->len = sqliteGetToken( &sql->command[sql->n], &token );
+        if( sql->len==0 )
+            break;
+        str->data = &sql->command[sql->n];
+        str->len = sql->len;
+    }
+    while( token == TK_SPACE );
+
+    /* TRACE("token : %d (%s)\n", token, debugstr_wn(&sql->command[sql->n], sql->len)); */
+    
+    return token;
+}
+
+LPWSTR SQL_getstring( void *info, struct sql_str *strdata )
+{
+    LPCWSTR p = strdata->data;
+    UINT len = strdata->len;
+    LPWSTR str;
+
+    /* if there's quotes, remove them */
+    if( ( (p[0]=='`') && (p[len-1]=='`') ) || 
+        ( (p[0]=='\'') && (p[len-1]=='\'') ) )
+    {
+        p++;
+        len -= 2;
+    }
+    str = parser_alloc( info, (len + 1)*sizeof(WCHAR) );
+    if( !str )
+        return str;
+    memcpy( str, p, len*sizeof(WCHAR) );
+    str[len]=0;
+
+    return str;
+}
+
+INT SQL_getint( void *info )
+{
+    SQL_input* sql = (SQL_input*) info;
+    LPCWSTR p = &sql->command[sql->n];
+    INT i, r = 0;
+
+    for( i=0; i<sql->len; i++ )
+    {
+        if( '0' > p[i] || '9' < p[i] )
+        {
+            ERR("should only be numbers here!\n");
+            break;
+        }
+        r = (p[i]-'0') + r*10;
+    }
+
+    return r;
+}
+
+int SQL_error( const char *str )
+{
+    return 0;
+}
+
+static struct expr * EXPR_wildcard( void *info )
+{
+    struct expr *e = parser_alloc( info, sizeof *e );
+    if( e )
+    {
+        e->type = EXPR_WILDCARD;
+    }
+    return e;
+}
+
+static struct expr * EXPR_complex( void *info, struct expr *l, UINT op, struct expr *r )
+{
+    struct expr *e = parser_alloc( info, sizeof *e );
+    if( e )
+    {
+        e->type = EXPR_COMPLEX;
+        e->u.expr.left = l;
+        e->u.expr.op = op;
+        e->u.expr.right = r;
+    }
+    return e;
+}
+
+static struct expr * EXPR_column( void *info, column_info *column )
+{
+    struct expr *e = parser_alloc( info, sizeof *e );
+    if( e )
+    {
+        e->type = EXPR_COLUMN;
+        e->u.sval = column->column;
+    }
+    return e;
+}
+
+static struct expr * EXPR_ival( void *info, int val )
+{
+    struct expr *e = parser_alloc( info, sizeof *e );
+    if( e )
+    {
+        e->type = EXPR_IVAL;
+        e->u.ival = val;
+    }
+    return e;
+}
+
+static struct expr * EXPR_sval( void *info, struct sql_str *str )
+{
+    struct expr *e = parser_alloc( info, sizeof *e );
+    if( e )
+    {
+        e->type = EXPR_SVAL;
+        e->u.sval = SQL_getstring( info, str );
+    }
+    return e;
+}
+
+static BOOL SQL_MarkPrimaryKeys( column_info *cols,
+                                 column_info *keys )
+{
+    column_info *k;
+    BOOL found = TRUE;
+
+    for( k = keys; k && found; k = k->next )
+    {
+        column_info *c;
+
+        found = FALSE;
+        for( c = cols; c && !found; c = c->next )
+        {
+             if( lstrcmpW( k->column, c->column ) )
+                 continue;
+             c->type |= MSITYPE_KEY;
+             found = TRUE;
+        }
+    }
+
+    return found;
+}
+
+UINT MSI_ParseSQL( MSIDATABASE *db, LPCWSTR command, MSIVIEW **phview,
+                   struct list *mem )
+{
+    SQL_input sql;
+    int r;
+
+    *phview = NULL;
+
+    sql.db = db;
+    sql.command = command;
+    sql.n = 0;
+    sql.len = 0;
+    sql.view = phview;
+    sql.mem = mem;
+
+    r = SQL_parse(&sql);
+
+    TRACE("Parse returned %d\n", r);
+    if( r )
+    {
+        *sql.view = NULL;
+        return ERROR_BAD_QUERY_SYNTAX;
+    }
+
+    return ERROR_SUCCESS;
+}
diff --git a/reactos/dll/msi/string.c b/reactos/dll/msi/string.c
new file mode 100644 (file)
index 0000000..a5ec138
--- /dev/null
@@ -0,0 +1,456 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2002-2004, Mike McCormack for CodeWeavers
+ *
+ * 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 <stdarg.h>
+#include <assert.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "wine/debug.h"
+#include "wine/unicode.h"
+#include "msi.h"
+#include "msiquery.h"
+#include "objbase.h"
+#include "objidl.h"
+#include "msipriv.h"
+#include "winnls.h"
+
+#include "query.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msidb);
+
+typedef struct _msistring
+{
+    UINT hash;
+    UINT refcount;
+    LPWSTR str;
+} msistring;
+
+struct string_table
+{
+    UINT maxcount;         /* the number of strings */
+    UINT freeslot;
+    UINT codepage;
+    msistring *strings; /* an array of strings (in the tree) */
+};
+
+static UINT msistring_makehash( const WCHAR *str )
+{
+    UINT hash = 0;
+
+    if (str==NULL)
+        return hash;
+
+    while( *str )
+    {
+        hash ^= *str++;
+        hash *= 53;
+        hash = (hash<<5) | (hash>>27);
+    }
+    return hash;
+}
+
+string_table *msi_init_stringtable( int entries, UINT codepage )
+{
+    string_table *st;
+
+    st = msi_alloc( sizeof (string_table) );
+    if( !st )
+        return NULL;    
+    if( entries < 1 )
+        entries = 1;
+    st->strings = msi_alloc_zero( sizeof (msistring) * entries );
+    if( !st )
+    {
+        msi_free( st );
+        return NULL;    
+    }
+    st->maxcount = entries;
+    st->freeslot = 1;
+    st->codepage = codepage;
+
+    return st;
+}
+
+VOID msi_destroy_stringtable( string_table *st )
+{
+    UINT i;
+
+    for( i=0; i<st->maxcount; i++ )
+    {
+        if( st->strings[i].refcount )
+            msi_free( st->strings[i].str );
+    }
+    msi_free( st->strings );
+    msi_free( st );
+}
+
+static int st_find_free_entry( string_table *st )
+{
+    UINT i, sz;
+    msistring *p;
+
+    TRACE("%p\n", st);
+
+    if( st->freeslot )
+    {
+        for( i = st->freeslot; i < st->maxcount; i++ )
+            if( !st->strings[i].refcount )
+                return i;
+    }
+    for( i = 1; i < st->maxcount; i++ )
+        if( !st->strings[i].refcount )
+            return i;
+
+    /* dynamically resize */
+    sz = st->maxcount + 1 + st->maxcount/2;
+    p = msi_realloc_zero( st->strings, sz*sizeof(msistring) );
+    if( !p )
+        return -1;
+    st->strings = p;
+    st->freeslot = st->maxcount;
+    st->maxcount = sz;
+    if( st->strings[st->freeslot].refcount )
+        ERR("oops. expected freeslot to be free...\n");
+    return st->freeslot;
+}
+
+static void st_mark_entry_used( string_table *st, UINT n )
+{
+    if( n >= st->maxcount )
+        return;
+    st->freeslot = n + 1;
+}
+
+int msi_addstring( string_table *st, UINT n, const CHAR *data, int len, UINT refcount )
+{
+    int sz;
+
+    if( !data )
+        return 0;
+    if( !data[0] )
+        return 0;
+    if( n > 0 )
+    {
+        if( st->strings[n].refcount )
+            return -1;
+    }
+    else
+    {
+        if( ERROR_SUCCESS == msi_string2idA( st, data, &n ) )
+        {
+            st->strings[n].refcount++;
+            return n;
+        }
+        n = st_find_free_entry( st );
+        if( n < 0 )
+            return -1;
+    }
+
+    if( n < 1 )
+    {
+        ERR("invalid index adding %s (%d)\n", debugstr_a( data ), n );
+        return -1;
+    }
+
+    /* allocate a new string */
+    if( len < 0 )
+        len = strlen(data);
+    sz = MultiByteToWideChar( st->codepage, 0, data, len, NULL, 0 );
+    st->strings[n].str = msi_alloc( (sz+1)*sizeof(WCHAR) );
+    if( !st->strings[n].str )
+        return -1;
+    MultiByteToWideChar( st->codepage, 0, data, len, st->strings[n].str, sz );
+    st->strings[n].str[sz] = 0;
+    st->strings[n].refcount = 1;
+    st->strings[n].hash = msistring_makehash( st->strings[n].str );
+
+    st_mark_entry_used( st, n );
+
+    return n;
+}
+
+int msi_addstringW( string_table *st, UINT n, const WCHAR *data, int len, UINT refcount )
+{
+    /* TRACE("[%2d] = %s\n", string_no, debugstr_an(data,len) ); */
+
+    if( !data )
+        return 0;
+    if( !data[0] )
+        return 0;
+    if( n > 0 )
+    {
+        if( st->strings[n].refcount )
+            return -1;
+    }
+    else
+    {
+        if( ERROR_SUCCESS == msi_string2idW( st, data, &n ) )
+        {
+            st->strings[n].refcount++;
+            return n;
+        }
+        n = st_find_free_entry( st );
+        if( n < 0 )
+            return -1;
+    }
+
+    if( n < 1 )
+    {
+        ERR("invalid index adding %s (%d)\n", debugstr_w( data ), n );
+        return -1;
+    }
+
+    /* allocate a new string */
+    if(len<0)
+        len = strlenW(data);
+    TRACE("%s, n = %d len = %d\n", debugstr_w(data), n, len );
+
+    st->strings[n].str = msi_alloc( (len+1)*sizeof(WCHAR) );
+    if( !st->strings[n].str )
+        return -1;
+    TRACE("%d\n",__LINE__);
+    memcpy( st->strings[n].str, data, len*sizeof(WCHAR) );
+    st->strings[n].str[len] = 0;
+    st->strings[n].refcount = 1;
+    st->strings[n].hash = msistring_makehash( st->strings[n].str );
+
+    st_mark_entry_used( st, n );
+
+    return n;
+}
+
+/* find the string identified by an id - return null if there's none */
+const WCHAR *msi_string_lookup_id( string_table *st, UINT id )
+{
+    static const WCHAR zero[] = { 0 };
+    if( id == 0 )
+        return zero;
+
+    if( id >= st->maxcount )
+        return NULL;
+
+    if( id && !st->strings[id].refcount )
+        return NULL;
+
+    return st->strings[id].str;
+}
+
+/*
+ *  msi_id2stringW
+ *
+ *  [in] st         - pointer to the string table
+ *  [in] id  - id of the string to retrieve
+ *  [out] buffer    - destination of the string
+ *  [in/out] sz     - number of bytes available in the buffer on input
+ *                    number of bytes used on output
+ *
+ *   The size includes the terminating nul character.  Short buffers
+ *  will be filled, but not nul terminated.
+ */
+UINT msi_id2stringW( string_table *st, UINT id, LPWSTR buffer, UINT *sz )
+{
+    UINT len;
+    const WCHAR *str;
+
+    TRACE("Finding string %d of %d\n", id, st->maxcount);
+
+    str = msi_string_lookup_id( st, id );
+    if( !str )
+        return ERROR_FUNCTION_FAILED;
+
+    len = strlenW( str ) + 1;
+
+    if( !buffer )
+    {
+        *sz = len;
+        return ERROR_SUCCESS;
+    }
+
+    if( *sz < len )
+        *sz = len;
+    memcpy( buffer, str, (*sz)*sizeof(WCHAR) ); 
+    *sz = len;
+
+    return ERROR_SUCCESS;
+}
+
+/*
+ *  msi_id2stringA
+ *
+ *  [in] st         - pointer to the string table
+ *  [in] id         - id of the string to retrieve
+ *  [out] buffer    - destination of the UTF8 string
+ *  [in/out] sz     - number of bytes available in the buffer on input
+ *                    number of bytes used on output
+ *
+ *   The size includes the terminating nul character.  Short buffers
+ *  will be filled, but not nul terminated.
+ */
+UINT msi_id2stringA( string_table *st, UINT id, LPSTR buffer, UINT *sz )
+{
+    UINT len;
+    const WCHAR *str;
+    int n;
+
+    TRACE("Finding string %d of %d\n", id, st->maxcount);
+
+    str = msi_string_lookup_id( st, id );
+    if( !str )
+        return ERROR_FUNCTION_FAILED;
+
+    len = WideCharToMultiByte( st->codepage, 0, str, -1, NULL, 0, NULL, NULL );
+
+    if( !buffer )
+    {
+        *sz = len;
+        return ERROR_SUCCESS;
+    }
+
+    if( len > *sz )
+    {
+        n = strlenW( str ) + 1;
+        while( n && (len > *sz) )
+            len = WideCharToMultiByte( st->codepage, 0, 
+                           str, --n, NULL, 0, NULL, NULL );
+    }
+    else
+        n = -1;
+
+    *sz = WideCharToMultiByte( st->codepage, 0, str, n, buffer, len, NULL, NULL );
+
+    return ERROR_SUCCESS;
+}
+
+/*
+ *  msi_string2idW
+ *
+ *  [in] st         - pointer to the string table
+ *  [in] str        - string to find in the string table
+ *  [out] id        - id of the string, if found
+ */
+UINT msi_string2idW( string_table *st, LPCWSTR str, UINT *id )
+{
+    UINT hash;
+    UINT i, r = ERROR_INVALID_PARAMETER;
+
+    hash = msistring_makehash( str );
+    for( i=0; i<st->maxcount; i++ )
+    {
+        if ( (str == NULL && st->strings[i].str == NULL) || 
+            ( ( st->strings[i].hash == hash ) &&
+            !strcmpW( st->strings[i].str, str ) ))
+        {
+            r = ERROR_SUCCESS;
+            *id = i;
+            break;
+        }
+    }
+
+    return r;
+}
+
+UINT msi_string2idA( string_table *st, LPCSTR buffer, UINT *id )
+{
+    DWORD sz;
+    UINT r = ERROR_INVALID_PARAMETER;
+    LPWSTR str;
+
+    TRACE("Finding string %s in string table\n", debugstr_a(buffer) );
+
+    if( buffer[0] == 0 )
+    {
+        *id = 0;
+        return ERROR_SUCCESS;
+    }
+
+    sz = MultiByteToWideChar( st->codepage, 0, buffer, -1, NULL, 0 );
+    if( sz <= 0 )
+        return r;
+    str = msi_alloc( sz*sizeof(WCHAR) );
+    if( !str )
+        return ERROR_NOT_ENOUGH_MEMORY;
+    MultiByteToWideChar( st->codepage, 0, buffer, -1, str, sz );
+
+    r = msi_string2idW( st, str, id );
+    msi_free( str );
+
+    return r;
+}
+
+UINT msi_strcmp( string_table *st, UINT lval, UINT rval, UINT *res )
+{
+    const WCHAR *l_str, *r_str;
+
+    l_str = msi_string_lookup_id( st, lval );
+    if( !l_str )
+        return ERROR_INVALID_PARAMETER;
+    
+    r_str = msi_string_lookup_id( st, rval );
+    if( !r_str )
+        return ERROR_INVALID_PARAMETER;
+
+    /* does this do the right thing for all UTF-8 strings? */
+    *res = strcmpW( l_str, r_str );
+
+    return ERROR_SUCCESS;
+}
+
+UINT msi_string_count( string_table *st )
+{
+    return st->maxcount;
+}
+
+UINT msi_id_refcount( string_table *st, UINT i )
+{
+    if( i >= st->maxcount )
+        return 0;
+    return st->strings[i].refcount;
+}
+
+UINT msi_string_totalsize( string_table *st, UINT *total )
+{
+    UINT size = 0, i, len;
+
+    if( st->strings[0].str || st->strings[0].refcount )
+        ERR("oops. element 0 has a string\n");
+    *total = 0;
+    for( i=1; i<st->maxcount; i++ )
+    {
+        if( st->strings[i].str )
+        {
+            TRACE("[%u] = %s\n", i, debugstr_w(st->strings[i].str));
+            len = WideCharToMultiByte( st->codepage, 0,
+                     st->strings[i].str, -1, NULL, 0, NULL, NULL);
+            if( len )
+                len--;
+            size += len;
+            *total = (i+1);
+        }
+    }
+    TRACE("%u/%u strings %u bytes codepage %x\n", *total, st->maxcount, size, st->codepage );
+    return size;
+}
+
+UINT msi_string_get_codepage( string_table *st )
+{
+    return st->codepage;
+}
diff --git a/reactos/dll/msi/suminfo.c b/reactos/dll/msi/suminfo.c
new file mode 100644 (file)
index 0000000..8a5d973
--- /dev/null
@@ -0,0 +1,761 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2002, 2005 Mike McCormack for CodeWeavers
+ *
+ * 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 <stdarg.h>
+
+#define COBJMACROS
+#define NONAMELESSUNION
+
+#define PRSPEC_PROPID (1)
+
+#include "windef.h"
+#include "winbase.h"
+#include "winreg.h"
+#include "winnls.h"
+#include "shlwapi.h"
+#include "wine/debug.h"
+#include "msi.h"
+#include "msiquery.h"
+#include "msidefs.h"
+#include "msipriv.h"
+#include "objidl.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+#include "pshpack1.h"
+
+typedef struct { 
+    WORD wByteOrder;
+    WORD wFormat;
+    DWORD dwOSVer;
+    CLSID clsID;
+    DWORD reserved;
+} PROPERTYSETHEADER;
+
+typedef struct { 
+    FMTID fmtid;
+    DWORD dwOffset;
+} FORMATIDOFFSET;
+
+typedef struct { 
+    DWORD cbSection;
+    DWORD cProperties;
+} PROPERTYSECTIONHEADER; 
+typedef struct { 
+    DWORD propid;
+    DWORD dwOffset;
+} PROPERTYIDOFFSET; 
+
+typedef struct {
+    DWORD type;
+    union {
+        INT i4;
+        SHORT i2;
+        FILETIME ft;
+        struct {
+            DWORD len;
+            BYTE str[1];
+        } str;
+    } u;
+} PROPERTY_DATA;
+#include "poppack.h"
+
+#define SECT_HDR_SIZE (sizeof(PROPERTYSECTIONHEADER))
+
+static const WCHAR szSumInfo[] = { 5 ,'S','u','m','m','a','r','y',
+                       'I','n','f','o','r','m','a','t','i','o','n',0 };
+
+static void free_prop( PROPVARIANT *prop )
+{
+    if (prop->vt == VT_LPSTR )
+        msi_free( prop->u.pszVal );
+    prop->vt = VT_EMPTY;
+}
+
+static void MSI_CloseSummaryInfo( MSIOBJECTHDR *arg )
+{
+    MSISUMMARYINFO *si = (MSISUMMARYINFO *) arg;
+    DWORD i;
+
+    for( i = 0; i < MSI_MAX_PROPS; i++ )
+        free_prop( &si->property[i] );
+    msiobj_release( &si->db->hdr );
+}
+
+static UINT get_type( UINT uiProperty )
+{
+    switch( uiProperty )
+    {
+    case PID_CODEPAGE:
+         return VT_I2;
+    
+    case PID_SUBJECT:
+    case PID_AUTHOR:
+    case PID_KEYWORDS:
+    case PID_COMMENTS:
+    case PID_TEMPLATE:
+    case PID_LASTAUTHOR:
+    case PID_REVNUMBER:
+    case PID_APPNAME:
+    case PID_TITLE:
+         return VT_LPSTR;
+
+    case PID_LASTPRINTED:
+    case PID_CREATE_DTM:
+    case PID_LASTSAVE_DTM:
+         return VT_FILETIME;
+
+    case PID_WORDCOUNT:
+    case PID_CHARCOUNT:
+    case PID_SECURITY:
+    case PID_PAGECOUNT:
+         return VT_I4;
+    }
+    return VT_EMPTY;
+}
+
+static UINT get_property_count( PROPVARIANT *property )
+{
+    UINT i, n = 0;
+
+    if( !property )
+        return n;
+    for( i = 0; i < MSI_MAX_PROPS; i++ )
+        if( property[i].vt != VT_EMPTY )
+            n++;
+    return n;
+}
+
+/* FIXME: doesn't deal with endian conversion */
+static void read_properties_from_data( PROPVARIANT *prop, LPBYTE data, DWORD sz )
+{
+    UINT type;
+    DWORD i;
+    int size;
+    PROPERTY_DATA *propdata;
+    PROPVARIANT *property;
+    PROPERTYIDOFFSET *idofs;
+    PROPERTYSECTIONHEADER *section_hdr;
+
+    section_hdr = (PROPERTYSECTIONHEADER*) &data[0];
+    idofs = (PROPERTYIDOFFSET*) &data[SECT_HDR_SIZE];
+
+    /* now set all the properties */
+    for( i = 0; i < section_hdr->cProperties; i++ )
+    {
+        type = get_type( idofs[i].propid );
+        if( type == VT_EMPTY )
+        {
+            ERR("propid %ld has unknown type\n", idofs[i].propid);
+            break;
+        }
+
+        propdata = (PROPERTY_DATA*) &data[ idofs[i].dwOffset ];
+
+        /* check the type is the same as we expect */
+        if( type != propdata->type )
+        {
+            ERR("wrong type %d != %ld\n", type, propdata->type);
+            break;
+        }
+
+        /* check we don't run off the end of the data */
+        size = sz - idofs[i].dwOffset - sizeof(DWORD);
+        if( sizeof(DWORD) > size ||
+            ( type == VT_FILETIME && sizeof(FILETIME) > size ) ||
+            ( type == VT_LPSTR && (propdata->u.str.len + sizeof(DWORD)) > size ) )
+        {
+            ERR("not enough data\n");
+            break;
+        }
+
+        if( idofs[i].propid >= MSI_MAX_PROPS )
+        {
+            ERR("Unknown property ID %ld\n", idofs[i].propid );
+            break;
+        }
+
+        property = &prop[ idofs[i].propid ];
+        property->vt = type;
+
+        if( type == VT_LPSTR )
+        {
+            LPSTR str = msi_alloc( propdata->u.str.len );
+            memcpy( str, propdata->u.str.str, propdata->u.str.len );
+            str[ propdata->u.str.len - 1 ] = 0;
+            property->u.pszVal = str;
+        }
+        else if( type == VT_FILETIME )
+            property->u.filetime = propdata->u.ft;
+        else if( type == VT_I2 )
+            property->u.iVal = propdata->u.i2;
+        else if( type == VT_I4 )
+            property->u.lVal = propdata->u.i4;
+    }
+}
+
+static UINT load_summary_info( MSISUMMARYINFO *si, IStream *stm )
+{
+    UINT ret = ERROR_FUNCTION_FAILED;
+    PROPERTYSETHEADER set_hdr;
+    FORMATIDOFFSET format_hdr;
+    PROPERTYSECTIONHEADER section_hdr;
+    LPBYTE data = NULL;
+    LARGE_INTEGER ofs;
+    ULONG count, sz;
+    HRESULT r;
+
+    TRACE("%p %p\n", si, stm);
+
+    /* read the header */
+    sz = sizeof set_hdr;
+    r = IStream_Read( stm, &set_hdr, sz, &count );
+    if( FAILED(r) || count != sz )
+        return ret;
+
+    if( set_hdr.wByteOrder != 0xfffe )
+    {
+        ERR("property set not big-endian %04X\n", set_hdr.wByteOrder);
+        return ret;
+    }
+
+    sz = sizeof format_hdr;
+    r = IStream_Read( stm, &format_hdr, sz, &count );
+    if( FAILED(r) || count != sz )
+        return ret;
+
+    /* check the format id is correct */
+    if( !IsEqualGUID( &FMTID_SummaryInformation, &format_hdr.fmtid ) )
+        return ret;
+
+    /* seek to the location of the section */
+    ofs.QuadPart = format_hdr.dwOffset;
+    r = IStream_Seek( stm, ofs, STREAM_SEEK_SET, NULL );
+    if( FAILED(r) )
+        return ret;
+
+    /* read the section itself */
+    sz = SECT_HDR_SIZE;
+    r = IStream_Read( stm, &section_hdr, sz, &count );
+    if( FAILED(r) || count != sz )
+        return ret;
+
+    if( section_hdr.cProperties > MSI_MAX_PROPS )
+    {
+        ERR("too many properties %ld\n", section_hdr.cProperties);
+        return ret;
+    }
+
+    data = msi_alloc( section_hdr.cbSection);
+    if( !data )
+        return ret;
+
+    memcpy( data, &section_hdr, SECT_HDR_SIZE );
+
+    /* read all the data in one go */
+    sz = section_hdr.cbSection - SECT_HDR_SIZE;
+    r = IStream_Read( stm, &data[ SECT_HDR_SIZE ], sz, &count );
+    if( SUCCEEDED(r) && count == sz )
+        read_properties_from_data( si->property, data, sz + SECT_HDR_SIZE );
+    else
+        ERR("failed to read properties %ld %ld\n", count, sz);
+
+    msi_free( data );
+    return ret;
+}
+
+static DWORD write_dword( LPBYTE data, DWORD ofs, DWORD val )
+{
+    if( data )
+    {
+        data[ofs++] = val&0xff;
+        data[ofs++] = (val>>8)&0xff;
+        data[ofs++] = (val>>16)&0xff;
+        data[ofs++] = (val>>24)&0xff;
+    }
+    return 4;
+}
+
+static DWORD write_filetime( LPBYTE data, DWORD ofs, LPFILETIME ft )
+{
+    write_dword( data, ofs, ft->dwLowDateTime );
+    write_dword( data, ofs + 4, ft->dwHighDateTime );
+    return 8;
+}
+
+static DWORD write_string( LPBYTE data, DWORD ofs, LPCSTR str )
+{
+    DWORD len = lstrlenA( str ) + 1;
+    write_dword( data, ofs, len );
+    if( data )
+        memcpy( &data[ofs + 4], str, len );
+    return (7 + len) & ~3;
+}
+
+static UINT write_property_to_data( PROPVARIANT *prop, LPBYTE data )
+{
+    DWORD sz = 0;
+
+    if( prop->vt == VT_EMPTY )
+        return sz;
+
+    /* add the type */
+    sz += write_dword( data, sz, prop->vt );
+    switch( prop->vt )
+    {
+    case VT_I2:
+        sz += write_dword( data, sz, prop->u.iVal );
+        break;
+    case VT_I4:
+        sz += write_dword( data, sz, prop->u.lVal );
+        break;
+    case VT_FILETIME:
+        sz += write_filetime( data, sz, &prop->u.filetime );
+        break;
+    case VT_LPSTR:
+        sz += write_string( data, sz, prop->u.pszVal );
+        break;
+    }
+    return sz;
+}
+
+static UINT save_summary_info( MSISUMMARYINFO * si, IStream *stm )
+{
+    UINT ret = ERROR_FUNCTION_FAILED;
+    PROPERTYSETHEADER set_hdr;
+    FORMATIDOFFSET format_hdr;
+    PROPERTYSECTIONHEADER section_hdr;
+    PROPERTYIDOFFSET idofs[MSI_MAX_PROPS];
+    LPBYTE data = NULL;
+    ULONG count, sz;
+    HRESULT r;
+    int i, n;
+
+    /* write the header */
+    sz = sizeof set_hdr;
+    memset( &set_hdr, 0, sz );
+    set_hdr.wByteOrder = 0xfffe;
+    set_hdr.wFormat = 0;
+    set_hdr.dwOSVer = 0x00020005; /* build 5, platform id 2 */
+    /* set_hdr.clsID is {00000000-0000-0000-0000-000000000000} */
+    set_hdr.reserved = 1;
+    r = IStream_Write( stm, &set_hdr, sz, &count );
+    if( FAILED(r) || count != sz )
+        return ret;
+
+    /* write the format header */
+    sz = sizeof format_hdr;
+    memcpy( &format_hdr.fmtid, &FMTID_SummaryInformation, sizeof (FMTID) );
+    format_hdr.dwOffset = sizeof format_hdr + sizeof set_hdr;
+    r = IStream_Write( stm, &format_hdr, sz, &count );
+    if( FAILED(r) || count != sz )
+        return ret;
+
+    /* add up how much space the data will take and calculate the offsets */
+    section_hdr.cbSection = sizeof section_hdr;
+    section_hdr.cbSection += (get_property_count( si->property ) * sizeof idofs[0]);
+    section_hdr.cProperties = 0;
+    n = 0;
+    for( i = 0; i < MSI_MAX_PROPS; i++ )
+    {
+        sz = write_property_to_data( &si->property[i], NULL );
+        if( !sz )
+            continue;
+        idofs[ section_hdr.cProperties ].propid = i;
+        idofs[ section_hdr.cProperties ].dwOffset = section_hdr.cbSection;
+        section_hdr.cProperties++;
+        section_hdr.cbSection += sz;
+    }
+
+    data = msi_alloc_zero( section_hdr.cbSection );
+
+    sz = 0;
+    memcpy( &data[sz], &section_hdr, sizeof section_hdr );
+    sz += sizeof section_hdr;
+
+    memcpy( &data[sz], idofs, section_hdr.cProperties * sizeof idofs[0] );
+    sz += section_hdr.cProperties * sizeof idofs[0];
+
+    /* write out the data */
+    for( i = 0; i < MSI_MAX_PROPS; i++ )
+        sz += write_property_to_data( &si->property[i], &data[sz] );
+
+    r = IStream_Write( stm, data, sz, &count );
+    msi_free( data );
+    if( FAILED(r) || count != sz )
+        return ret;
+
+    return ERROR_SUCCESS;
+}
+
+MSISUMMARYINFO *MSI_GetSummaryInformationW( MSIDATABASE *db, UINT uiUpdateCount )
+{
+    IStream *stm = NULL;
+    MSISUMMARYINFO *si;
+    DWORD grfMode;
+    HRESULT r;
+
+    TRACE("%p %d\n", db, uiUpdateCount );
+
+    si = alloc_msiobject( MSIHANDLETYPE_SUMMARYINFO, 
+                  sizeof (MSISUMMARYINFO), MSI_CloseSummaryInfo );
+    if( !si )
+        return si;
+
+    msiobj_addref( &db->hdr );
+    si->db = db;
+    memset( &si->property, 0, sizeof si->property );
+    si->update_count = uiUpdateCount;
+
+    /* read the stream... if we fail, we'll start with an empty property set */
+    grfMode = STGM_READ | STGM_SHARE_EXCLUSIVE;
+    r = IStorage_OpenStream( si->db->storage, szSumInfo, 0, grfMode, 0, &stm );
+    if( SUCCEEDED(r) )
+    {
+        load_summary_info( si, stm );
+        IStream_Release( stm );
+    }
+
+    return si;
+}
+
+UINT WINAPI MsiGetSummaryInformationW( MSIHANDLE hDatabase, 
+              LPCWSTR szDatabase, UINT uiUpdateCount, MSIHANDLE *pHandle )
+{
+    MSISUMMARYINFO *si;
+    MSIDATABASE *db;
+    UINT ret = ERROR_FUNCTION_FAILED;
+
+    TRACE("%ld %s %d %p\n", hDatabase, debugstr_w(szDatabase),
+           uiUpdateCount, pHandle);
+
+    if( !pHandle )
+        return ERROR_INVALID_PARAMETER;
+
+    if( szDatabase )
+    {
+        ret = MSI_OpenDatabaseW( szDatabase, NULL, &db );
+        if( ret != ERROR_SUCCESS )
+            return ret;
+    }
+    else
+    {
+        db = msihandle2msiinfo( hDatabase, MSIHANDLETYPE_DATABASE );
+        if( !db )
+            return ERROR_INVALID_PARAMETER;
+    }
+
+    si = MSI_GetSummaryInformationW( db, uiUpdateCount );
+    if (si)
+    {
+        *pHandle = alloc_msihandle( &si->hdr );
+        if( *pHandle )
+            ret = ERROR_SUCCESS;
+        msiobj_release( &si->hdr );
+    }
+
+    if( db )
+        msiobj_release( &db->hdr );
+
+    return ret;
+}
+
+UINT WINAPI MsiGetSummaryInformationA(MSIHANDLE hDatabase, 
+              LPCSTR szDatabase, UINT uiUpdateCount, MSIHANDLE *pHandle)
+{
+    LPWSTR szwDatabase = NULL;
+    UINT ret;
+
+    TRACE("%ld %s %d %p\n", hDatabase, debugstr_a(szDatabase), 
+          uiUpdateCount, pHandle);
+
+    if( szDatabase )
+    {
+        szwDatabase = strdupAtoW( szDatabase );
+        if( !szwDatabase )
+            return ERROR_FUNCTION_FAILED;
+    }
+
+    ret = MsiGetSummaryInformationW(hDatabase, szwDatabase, uiUpdateCount, pHandle);
+
+    msi_free( szwDatabase );
+
+    return ret;
+}
+
+UINT WINAPI MsiSummaryInfoGetPropertyCount(MSIHANDLE hSummaryInfo, UINT *pCount)
+{
+    MSISUMMARYINFO *si;
+
+    TRACE("%ld %p\n",hSummaryInfo, pCount);
+
+    si = msihandle2msiinfo( hSummaryInfo, MSIHANDLETYPE_SUMMARYINFO );
+    if( !si )
+        return ERROR_INVALID_HANDLE;
+
+    if( pCount )
+        *pCount = get_property_count( si->property );
+    msiobj_release( &si->hdr );
+
+    return ERROR_SUCCESS;
+}
+
+static UINT get_prop( MSIHANDLE handle, UINT uiProperty, UINT *puiDataType,
+          INT *piValue, FILETIME *pftValue, awstring *str, DWORD *pcchValueBuf)
+{
+    MSISUMMARYINFO *si;
+    PROPVARIANT *prop;
+    UINT ret = ERROR_SUCCESS;
+
+    TRACE("%ld %d %p %p %p %p %p\n", handle, uiProperty, puiDataType,
+          piValue, pftValue, str, pcchValueBuf);
+
+    si = msihandle2msiinfo( handle, MSIHANDLETYPE_SUMMARYINFO );
+    if( !si )
+        return ERROR_INVALID_HANDLE;
+
+    if ( uiProperty >= MSI_MAX_PROPS )
+    {
+        *puiDataType = VT_EMPTY;
+        return ret;
+    }
+
+    prop = &si->property[uiProperty];
+
+    if( puiDataType )
+        *puiDataType = prop->vt;
+
+    switch( prop->vt )
+    {
+    case VT_I2:
+        if( piValue )
+            *piValue = prop->u.iVal;
+        break;
+    case VT_I4:
+        if( piValue )
+            *piValue = prop->u.lVal;
+        break;
+    case VT_LPSTR:
+        if( pcchValueBuf )
+        {
+            DWORD len = 0;
+
+            if( str->unicode )
+            {
+                len = MultiByteToWideChar( CP_ACP, 0, prop->u.pszVal, -1,
+                                           str->str.w, *pcchValueBuf );
+                len--;
+            }
+            else
+            {
+                len = lstrlenA( prop->u.pszVal );
+                if( str->str.a )
+                    lstrcpynA(str->str.a, prop->u.pszVal, *pcchValueBuf );
+            }
+            if (len >= *pcchValueBuf)
+                ret = ERROR_MORE_DATA;
+            *pcchValueBuf = len;
+        }
+        break;
+    case VT_FILETIME:
+        if( pftValue )
+            memcpy(pftValue, &prop->u.filetime, sizeof (FILETIME) );
+        break;
+    case VT_EMPTY:
+        break;
+    default:
+        FIXME("Unknown property variant type\n");
+        break;
+    }
+    msiobj_release( &si->hdr );
+    return ret;
+}
+
+LPWSTR msi_suminfo_dup_string( MSISUMMARYINFO *si, UINT uiProperty )
+{
+    PROPVARIANT *prop;
+
+    if ( uiProperty >= MSI_MAX_PROPS )
+        return NULL;
+    prop = &si->property[uiProperty];
+    if( prop->vt != VT_LPSTR )
+        return NULL;
+    return strdupAtoW( prop->u.pszVal );
+}
+
+UINT WINAPI MsiSummaryInfoGetPropertyA(
+      MSIHANDLE handle, UINT uiProperty, UINT *puiDataType, INT *piValue,
+      FILETIME *pftValue, LPSTR szValueBuf, DWORD *pcchValueBuf)
+{
+    awstring str;
+
+    TRACE("%ld %d %p %p %p %p %p\n", handle, uiProperty, puiDataType,
+          piValue, pftValue, szValueBuf, pcchValueBuf );
+
+    str.unicode = FALSE;
+    str.str.a = szValueBuf;
+
+    return get_prop( handle, uiProperty, puiDataType, piValue,
+                     pftValue, &str, pcchValueBuf );
+}
+
+UINT WINAPI MsiSummaryInfoGetPropertyW(
+      MSIHANDLE handle, UINT uiProperty, UINT *puiDataType, INT *piValue,
+      FILETIME *pftValue, LPWSTR szValueBuf, DWORD *pcchValueBuf)
+{
+    awstring str;
+
+    TRACE("%ld %d %p %p %p %p %p\n", handle, uiProperty, puiDataType,
+          piValue, pftValue, szValueBuf, pcchValueBuf );
+
+    str.unicode = TRUE;
+    str.str.w = szValueBuf;
+
+    return get_prop( handle, uiProperty, puiDataType, piValue,
+                     pftValue, &str, pcchValueBuf );
+}
+
+static UINT set_prop( MSIHANDLE handle, UINT uiProperty, UINT uiDataType,
+               INT iValue, FILETIME* pftValue, awcstring *str )
+{
+    MSISUMMARYINFO *si;
+    PROPVARIANT *prop;
+    UINT type, len, ret = ERROR_SUCCESS;
+
+    TRACE("%ld %u %u %i %p %p\n", handle, uiProperty, uiDataType,
+          iValue, pftValue, str );
+
+    type = get_type( uiProperty );
+    if( type == VT_EMPTY || type != uiDataType )
+        return ERROR_DATATYPE_MISMATCH;
+
+    if( uiDataType == VT_LPSTR && !str->str.w )
+        return ERROR_INVALID_PARAMETER;
+
+    if( uiDataType == VT_FILETIME && !pftValue )
+        return ERROR_INVALID_PARAMETER;
+
+    si = msihandle2msiinfo( handle, MSIHANDLETYPE_SUMMARYINFO );
+    if( !si )
+        return ERROR_INVALID_HANDLE;
+
+    prop = &si->property[uiProperty];
+
+    if( prop->vt == VT_EMPTY )
+    {
+        if( !si->update_count )
+        {
+            ret = ERROR_FUNCTION_FAILED;
+            goto end;
+        }
+        si->update_count--;
+    }
+    else if( prop->vt != type )
+        goto end;
+
+    free_prop( prop );
+    prop->vt = type;
+    switch( type )
+    {
+    case VT_I4:
+        prop->u.lVal = iValue;
+        break;
+    case VT_I2:
+        prop->u.iVal = iValue;
+        break;
+    case VT_FILETIME:
+        memcpy( &prop->u.filetime, pftValue, sizeof prop->u.filetime );
+        break;
+    case VT_LPSTR:
+        if( str->unicode )
+        {
+            len = WideCharToMultiByte( CP_ACP, 0, str->str.w, -1,
+                                       NULL, 0, NULL, NULL );
+            prop->u.pszVal = msi_alloc( len );
+            WideCharToMultiByte( CP_ACP, 0, str->str.w, -1,
+                                 prop->u.pszVal, len, NULL, NULL );
+        }
+        else
+        {
+            len = lstrlenA( str->str.a ) + 1;
+            prop->u.pszVal = msi_alloc( len );
+            lstrcpyA( prop->u.pszVal, str->str.a );
+        }
+        break;
+    }
+
+end:
+    msiobj_release( &si->hdr );
+    return ret;
+}
+
+UINT WINAPI MsiSummaryInfoSetPropertyW( MSIHANDLE handle, UINT uiProperty,
+               UINT uiDataType, INT iValue, FILETIME* pftValue, LPCWSTR szValue )
+{
+    awcstring str;
+
+    TRACE("%ld %u %u %i %p %s\n", handle, uiProperty, uiDataType,
+          iValue, pftValue, debugstr_w(szValue) );
+
+    str.unicode = TRUE;
+    str.str.w = szValue;
+    return set_prop( handle, uiProperty, uiDataType, iValue, pftValue, &str );
+}
+
+UINT WINAPI MsiSummaryInfoSetPropertyA( MSIHANDLE handle, UINT uiProperty,
+               UINT uiDataType, INT iValue, FILETIME* pftValue, LPCSTR szValue )
+{
+    awcstring str;
+
+    TRACE("%ld %u %u %i %p %s\n", handle, uiProperty, uiDataType,
+          iValue, pftValue, debugstr_a(szValue) );
+
+    str.unicode = FALSE;
+    str.str.a = szValue;
+    return set_prop( handle, uiProperty, uiDataType, iValue, pftValue, &str );
+}
+
+UINT WINAPI MsiSummaryInfoPersist( MSIHANDLE handle )
+{
+    IStream *stm = NULL;
+    MSISUMMARYINFO *si;
+    DWORD grfMode;
+    HRESULT r;
+    UINT ret = ERROR_FUNCTION_FAILED;
+
+    TRACE("%ld\n", handle );
+
+    si = msihandle2msiinfo( handle, MSIHANDLETYPE_SUMMARYINFO );
+    if( !si )
+        return ERROR_INVALID_HANDLE;
+
+    grfMode = STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE;
+    r = IStorage_CreateStream( si->db->storage, szSumInfo, grfMode, 0, 0, &stm );
+    if( SUCCEEDED(r) )
+    {
+        ret = save_summary_info( si, stm );
+        IStream_Release( stm );
+    }
+    msiobj_release( &si->hdr );
+
+    return ret;
+}
diff --git a/reactos/dll/msi/table.c b/reactos/dll/msi/table.c
new file mode 100644 (file)
index 0000000..a3becc6
--- /dev/null
@@ -0,0 +1,1861 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2002-2005 Mike McCormack for CodeWeavers
+ *
+ * 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 <stdarg.h>
+
+#define COBJMACROS
+#define NONAMELESSUNION
+#define NONAMELESSSTRUCT
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "wine/debug.h"
+#include "msi.h"
+#include "msiquery.h"
+#include "objbase.h"
+#include "objidl.h"
+#include "msipriv.h"
+#include "winnls.h"
+
+#include "query.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msidb);
+
+typedef struct tagMSICOLUMNINFO
+{
+    LPCWSTR tablename;
+    UINT   number;
+    LPCWSTR colname;
+    UINT   type;
+    UINT   offset;
+} MSICOLUMNINFO;
+
+struct tagMSITABLE
+{
+    USHORT **data;
+    UINT row_count;
+    struct list entry;
+    WCHAR name[1];
+};
+
+typedef struct tagMSITRANSFORM {
+    struct list entry;
+    IStorage *stg;
+} MSITRANSFORM;
+
+#define MAX_STREAM_NAME 0x1f
+
+static UINT table_get_column_info( MSIDATABASE *db, LPCWSTR name,
+       MSICOLUMNINFO **pcols, UINT *pcount );
+static UINT get_tablecolumns( MSIDATABASE *db, 
+       LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz);
+static void msi_free_colinfo( MSICOLUMNINFO *colinfo, UINT count );
+
+static inline UINT bytes_per_column( const MSICOLUMNINFO *col )
+{
+    if( col->type & MSITYPE_STRING )
+        return 2;
+    if( (col->type & 0xff) > 4 )
+        ERR("Invalid column size!\n");
+    return col->type & 0xff;
+}
+
+static int utf2mime(int x)
+{
+    if( (x>='0') && (x<='9') )
+        return x-'0';
+    if( (x>='A') && (x<='Z') )
+        return x-'A'+10;
+    if( (x>='a') && (x<='z') )
+        return x-'a'+10+26;
+    if( x=='.' )
+        return 10+26+26;
+    if( x=='_' )
+        return 10+26+26+1;
+    return -1;
+}
+
+static LPWSTR encode_streamname(BOOL bTable, LPCWSTR in)
+{
+    DWORD count = MAX_STREAM_NAME;
+    DWORD ch, next;
+    LPWSTR out, p;
+
+    if( !bTable )
+        count = lstrlenW( in )+2;
+    out = msi_alloc( count*sizeof(WCHAR) );
+    p = out;
+
+    if( bTable )
+    {
+         *p++ = 0x4840;
+         count --;
+    }
+    while( count -- ) 
+    {
+        ch = *in++;
+        if( !ch )
+        {
+            *p = ch;
+            return out;
+        }
+        if( ( ch < 0x80 ) && ( utf2mime(ch) >= 0 ) )
+        {
+            ch = utf2mime(ch) + 0x4800;
+            next = *in;
+            if( next && (next<0x80) )
+            {
+                next = utf2mime(next);
+                if( next >= 0  )
+                {
+                     next += 0x3ffffc0;
+                     ch += (next<<6);
+                     in++;
+                }
+            }
+        }
+        *p++ = ch;
+    }
+    ERR("Failed to encode stream name (%s)\n",debugstr_w(in));
+    msi_free( out );
+    return NULL;
+}
+
+static int mime2utf(int x)
+{
+    if( x<10 )
+        return x + '0';
+    if( x<(10+26))
+        return x - 10 + 'A';
+    if( x<(10+26+26))
+        return x - 10 - 26 + 'a';
+    if( x == (10+26+26) )
+        return '.';
+    return '_';
+}
+
+static BOOL decode_streamname(LPWSTR in, LPWSTR out)
+{
+    WCHAR ch;
+    DWORD count = 0;
+
+    while ( (ch = *in++) )
+    {
+        if( (ch >= 0x3800 ) && (ch < 0x4840 ) )
+        {
+            if( ch >= 0x4800 )
+                ch = mime2utf(ch-0x4800);
+            else
+            {
+                ch -= 0x3800;
+                *out++ = mime2utf(ch&0x3f);
+                count++;
+                ch = mime2utf((ch>>6)&0x3f);
+            }
+        }
+        *out++ = ch;
+        count++;
+    }
+    *out = 0;
+    return count;
+}
+
+void enum_stream_names( IStorage *stg )
+{
+    IEnumSTATSTG *stgenum = NULL;
+    HRESULT r;
+    STATSTG stat;
+    ULONG n, count;
+    WCHAR name[0x40];
+
+    r = IStorage_EnumElements( stg, 0, NULL, 0, &stgenum );
+    if( FAILED( r ) )
+        return;
+
+    n = 0;
+    while( 1 )
+    {
+        count = 0;
+        r = IEnumSTATSTG_Next( stgenum, 1, &stat, &count );
+        if( FAILED( r ) || !count )
+            break;
+        decode_streamname( stat.pwcsName, name );
+        TRACE("stream %2ld -> %s %s\n", n, 
+              debugstr_w(stat.pwcsName), debugstr_w(name) );
+        n++;
+    }
+
+    IEnumSTATSTG_Release( stgenum );
+}
+
+static UINT read_stream_data( IStorage *stg, LPCWSTR stname,
+                              USHORT **pdata, UINT *psz )
+{
+    HRESULT r;
+    UINT ret = ERROR_FUNCTION_FAILED;
+    VOID *data;
+    ULONG sz, count;
+    IStream *stm = NULL;
+    STATSTG stat;
+    LPWSTR encname;
+
+    encname = encode_streamname(TRUE, stname);
+
+    TRACE("%s -> %s\n",debugstr_w(stname),debugstr_w(encname));
+
+    r = IStorage_OpenStream(stg, encname, NULL, 
+            STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm);
+    msi_free( encname );
+    if( FAILED( r ) )
+    {
+        WARN("open stream failed r = %08lx - empty table?\n",r);
+        return ret;
+    }
+
+    r = IStream_Stat(stm, &stat, STATFLAG_NONAME );
+    if( FAILED( r ) )
+    {
+        WARN("open stream failed r = %08lx!\n",r);
+        goto end;
+    }
+
+    if( stat.cbSize.QuadPart >> 32 )
+    {
+        WARN("Too big!\n");
+        goto end;
+    }
+        
+    sz = stat.cbSize.QuadPart;
+    data = msi_alloc( sz );
+    if( !data )
+    {
+        WARN("couldn't allocate memory r=%08lx!\n",r);
+        ret = ERROR_NOT_ENOUGH_MEMORY;
+        goto end;
+    }
+        
+    r = IStream_Read(stm, data, sz, &count );
+    if( FAILED( r ) || ( count != sz ) )
+    {
+        msi_free( data );
+        WARN("read stream failed r = %08lx!\n",r);
+        goto end;
+    }
+
+    *pdata = data;
+    *psz = sz;
+    ret = ERROR_SUCCESS;
+
+end:
+    IStream_Release( stm );
+
+    return ret;
+}
+
+UINT db_get_raw_stream( MSIDATABASE *db, LPCWSTR stname, IStream **stm )
+{
+    LPWSTR encname;
+    HRESULT r;
+
+    encname = encode_streamname(FALSE, stname);
+
+    TRACE("%s -> %s\n",debugstr_w(stname),debugstr_w(encname));
+
+    r = IStorage_OpenStream(db->storage, encname, NULL, 
+            STGM_READ | STGM_SHARE_EXCLUSIVE, 0, stm);
+    if( FAILED( r ) )
+    {
+        MSITRANSFORM *transform;
+
+        LIST_FOR_EACH_ENTRY( transform, &db->transforms, MSITRANSFORM, entry )
+        {
+            TRACE("looking for %s in transform storage\n", debugstr_w(stname) );
+            r = IStorage_OpenStream( transform->stg, encname, NULL, 
+                    STGM_READ | STGM_SHARE_EXCLUSIVE, 0, stm );
+            if (SUCCEEDED(r))
+                break;
+        }
+    }
+
+    msi_free( encname );
+
+    return SUCCEEDED(r) ? ERROR_SUCCESS : ERROR_FUNCTION_FAILED;
+}
+
+UINT read_raw_stream_data( MSIDATABASE *db, LPCWSTR stname,
+                              USHORT **pdata, UINT *psz )
+{
+    HRESULT r;
+    UINT ret = ERROR_FUNCTION_FAILED;
+    VOID *data;
+    ULONG sz, count;
+    IStream *stm = NULL;
+    STATSTG stat;
+
+    r = db_get_raw_stream( db, stname, &stm );
+    if( r != ERROR_SUCCESS)
+        return ret;
+    r = IStream_Stat(stm, &stat, STATFLAG_NONAME );
+    if( FAILED( r ) )
+    {
+        WARN("open stream failed r = %08lx!\n",r);
+        goto end;
+    }
+
+    if( stat.cbSize.QuadPart >> 32 )
+    {
+        WARN("Too big!\n");
+        goto end;
+    }
+        
+    sz = stat.cbSize.QuadPart;
+    data = msi_alloc( sz );
+    if( !data )
+    {
+        WARN("couldn't allocate memory r=%08lx!\n",r);
+        ret = ERROR_NOT_ENOUGH_MEMORY;
+        goto end;
+    }
+        
+    r = IStream_Read(stm, data, sz, &count );
+    if( FAILED( r ) || ( count != sz ) )
+    {
+        msi_free( data );
+        WARN("read stream failed r = %08lx!\n",r);
+        goto end;
+    }
+
+    *pdata = data;
+    *psz = sz;
+    ret = ERROR_SUCCESS;
+
+end:
+    IStream_Release( stm );
+
+    return ret;
+}
+
+static UINT write_stream_data( IStorage *stg, LPCWSTR stname,
+                               LPVOID data, UINT sz )
+{
+    HRESULT r;
+    UINT ret = ERROR_FUNCTION_FAILED;
+    ULONG count;
+    IStream *stm = NULL;
+    ULARGE_INTEGER size;
+    LARGE_INTEGER pos;
+    LPWSTR encname;
+
+    encname = encode_streamname(TRUE, stname );
+    r = IStorage_OpenStream( stg, encname, NULL, 
+            STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &stm);
+    if( FAILED(r) )
+    {
+        r = IStorage_CreateStream( stg, encname,
+                STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm);
+    }
+    msi_free( encname );
+    if( FAILED( r ) )
+    {
+        WARN("open stream failed r = %08lx\n",r);
+        return ret;
+    }
+
+    size.QuadPart = sz;
+    r = IStream_SetSize( stm, size );
+    if( FAILED( r ) )
+    {
+        WARN("Failed to SetSize\n");
+        goto end;
+    }
+
+    pos.QuadPart = 0;
+    r = IStream_Seek( stm, pos, STREAM_SEEK_SET, NULL );
+    if( FAILED( r ) )
+    {
+        WARN("Failed to Seek\n");
+        goto end;
+    }
+
+    r = IStream_Write(stm, data, sz, &count );
+    if( FAILED( r ) || ( count != sz ) )
+    {
+        WARN("Failed to Write\n");
+        goto end;
+    }
+
+    ret = ERROR_SUCCESS;
+
+end:
+    IStream_Release( stm );
+
+    return ret;
+}
+
+static void free_table( MSITABLE *table )
+{
+    int i;
+    for( i=0; i<table->row_count; i++ )
+        msi_free( table->data[i] );
+    msi_free( table->data );
+    msi_free( table );
+}
+
+static UINT msi_table_get_row_size( const MSICOLUMNINFO *cols, UINT count )
+{
+    const MSICOLUMNINFO *last_col = &cols[count-1];
+    if (!count)
+        return 0;
+    return last_col->offset + bytes_per_column( last_col );
+}
+
+/* add this table to the list of cached tables in the database */
+static MSITABLE *read_table_from_storage( IStorage *stg, LPCWSTR name,
+                                    const MSICOLUMNINFO *cols, UINT num_cols )
+{
+    MSITABLE *t;
+    USHORT *rawdata = NULL;
+    UINT rawsize = 0, i, j, row_size = 0;
+
+    TRACE("%s\n",debugstr_w(name));
+
+    /* nonexistent tables should be interpreted as empty tables */
+    t = msi_alloc( sizeof (MSITABLE) + lstrlenW(name)*sizeof (WCHAR) );
+    if( !t )
+        return t;
+
+    row_size = msi_table_get_row_size( cols, num_cols );
+
+    t->row_count = 0;
+    t->data = NULL;
+    lstrcpyW( t->name, name );
+
+    /* if we can't read the table, just assume that it's empty */
+    read_stream_data( stg, name, &rawdata, &rawsize );
+    if( !rawdata )
+        return t;
+
+    TRACE("Read %d bytes\n", rawsize );
+
+    if( rawsize % row_size )
+    {
+        WARN("Table size is invalid %d/%d\n", rawsize, row_size );
+        goto err;
+    }
+
+    t->row_count = rawsize / row_size;
+    t->data = msi_alloc_zero( t->row_count * sizeof (USHORT*) );
+    if( !t->data )
+        goto err;
+
+    /* transpose all the data */
+    TRACE("Transposing data from %d columns\n", t->row_count );
+    for( i=0; i<t->row_count; i++ )
+    {
+        t->data[i] = msi_alloc( row_size );
+        if( !t->data[i] )
+            goto err;
+
+        for( j=0; j<num_cols; j++ )
+        {
+            UINT ofs = cols[j].offset/2;
+            UINT n = bytes_per_column( &cols[j] );
+
+            switch( n )
+            {
+            case 2:
+                t->data[i][ofs] = rawdata[ofs*t->row_count + i ];
+                break;
+            case 4:
+                t->data[i][ofs] = rawdata[ofs*t->row_count + i*2 ];
+                t->data[i][ofs+1] = rawdata[ofs*t->row_count + i*2 + 1];
+                break;
+            default:
+                ERR("oops - unknown column width %d\n", n);
+                goto err;
+            }
+        }
+    }
+
+    msi_free( rawdata );
+    return t;
+err:
+    msi_free( rawdata );
+    free_table( t );
+    return NULL;
+}
+
+void free_cached_tables( MSIDATABASE *db )
+{
+    while( !list_empty( &db->tables ) )
+    {
+        MSITABLE *t = LIST_ENTRY( list_head( &db->tables ), MSITABLE, entry );
+
+        list_remove( &t->entry );
+        free_table( t );
+    }
+}
+
+static MSITABLE *find_cached_table( MSIDATABASE *db, LPCWSTR name )
+{
+    MSITABLE *t;
+
+    LIST_FOR_EACH_ENTRY( t, &db->tables, MSITABLE, entry )
+        if( !lstrcmpW( name, t->name ) )
+            return t;
+
+    return NULL;
+}
+
+static UINT table_get_column_info( MSIDATABASE *db, LPCWSTR name, MSICOLUMNINFO **pcols, UINT *pcount )
+{
+    UINT r, column_count = 0;
+    MSICOLUMNINFO *columns;
+
+    /* get the number of columns in this table */
+    column_count = 0;
+    r = get_tablecolumns( db, name, NULL, &column_count );
+    if( r != ERROR_SUCCESS )
+        return r;
+
+    /* if there's no columns, there's no table */
+    if( column_count == 0 )
+        return ERROR_INVALID_PARAMETER;
+
+    TRACE("Table %s found\n", debugstr_w(name) );
+
+    columns = msi_alloc( column_count*sizeof (MSICOLUMNINFO) );
+    if( !columns )
+        return ERROR_FUNCTION_FAILED;
+
+    r = get_tablecolumns( db, name, columns, &column_count );
+    if( r != ERROR_SUCCESS )
+    {
+        msi_free( columns );
+        return ERROR_FUNCTION_FAILED;
+    }
+
+    *pcols = columns;
+    *pcount = column_count;
+
+    return r;
+}
+
+static MSITABLE *get_table( MSIDATABASE *db, LPCWSTR name,
+                            const MSICOLUMNINFO *cols, UINT num_cols )
+{
+    MSITABLE *table;
+
+    /* first, see if the table is cached */
+    table = find_cached_table( db, name );
+    if( table )
+        return table;
+
+    table = read_table_from_storage( db->storage, name, cols, num_cols );
+    if( table )
+        list_add_head( &db->tables, &table->entry );
+
+    return table;
+}
+
+static UINT save_table( MSIDATABASE *db, MSITABLE *t )
+{
+    USHORT *rawdata = NULL, *p;
+    UINT rawsize, r, i, j, row_size, num_cols = 0;
+    MSICOLUMNINFO *cols = NULL;
+
+    TRACE("Saving %s\n", debugstr_w( t->name ) );
+
+    r = table_get_column_info( db, t->name, &cols, &num_cols );
+    if( r != ERROR_SUCCESS )
+        return r;
+    
+    row_size = msi_table_get_row_size( cols, num_cols );
+
+    rawsize = t->row_count * row_size;
+    rawdata = msi_alloc_zero( rawsize );
+    if( !rawdata )
+    {
+        r = ERROR_NOT_ENOUGH_MEMORY;
+        goto err;
+    }
+
+    p = rawdata;
+    for( i=0; i<num_cols; i++ )
+    {
+        for( j=0; j<t->row_count; j++ )
+        {
+            UINT offset = cols[i].offset;
+
+            *p++ = t->data[j][offset/2];
+            if( 4 == bytes_per_column( &cols[i] ) )
+                *p++ = t->data[j][offset/2+1];
+        }
+    }
+
+    TRACE("writing %d bytes\n", rawsize);
+    r = write_stream_data( db->storage, t->name, rawdata, rawsize );
+
+err:
+    msi_free_colinfo( cols, num_cols );
+    msi_free( cols );
+    msi_free( rawdata );
+
+    return r;
+}
+
+HRESULT init_string_table( IStorage *stg )
+{
+    HRESULT r;
+    static const WCHAR szStringData[] = {
+        '_','S','t','r','i','n','g','D','a','t','a',0 };
+    static const WCHAR szStringPool[] = {
+        '_','S','t','r','i','n','g','P','o','o','l',0 };
+    USHORT zero[2] = { 0, 0 };
+    ULONG count = 0;
+    IStream *stm = NULL;
+    LPWSTR encname;
+
+    encname = encode_streamname(TRUE, szStringPool );
+
+    /* create the StringPool stream... add the zero string to it*/
+    r = IStorage_CreateStream( stg, encname,
+            STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm);
+    msi_free( encname );
+    if( r ) 
+    {
+        TRACE("Failed\n");
+        return r;
+    }
+
+    r = IStream_Write(stm, zero, sizeof zero, &count );
+    IStream_Release( stm );
+
+    if( FAILED( r ) || ( count != sizeof zero ) )
+    {
+        TRACE("Failed\n");
+        return E_FAIL;
+    }
+
+    /* create the StringData stream... make it zero length */
+    encname = encode_streamname(TRUE, szStringData );
+    r = IStorage_CreateStream( stg, encname,
+            STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm);
+    msi_free( encname );
+    if( r ) 
+    {
+        TRACE("Failed\n");
+        return E_FAIL;
+    }
+    IStream_Release( stm );
+
+    return r;
+}
+
+string_table *load_string_table( IStorage *stg )
+{
+    string_table *st = NULL;
+    CHAR *data;
+    USHORT *pool;
+    UINT r, datasize = 0, poolsize = 0, codepage;
+    DWORD i, count, offset, len, n;
+    static const WCHAR szStringData[] = {
+        '_','S','t','r','i','n','g','D','a','t','a',0 };
+    static const WCHAR szStringPool[] = {
+        '_','S','t','r','i','n','g','P','o','o','l',0 };
+
+    r = read_stream_data( stg, szStringPool, &pool, &poolsize );
+    if( r != ERROR_SUCCESS)
+        goto end;
+    r = read_stream_data( stg, szStringData, (USHORT**)&data, &datasize );
+    if( r != ERROR_SUCCESS)
+        goto end;
+
+    count = poolsize/4;
+    if( poolsize > 4 )
+        codepage = pool[0] | ( pool[1] << 16 );
+    else
+        codepage = CP_ACP;
+    st = msi_init_stringtable( count, codepage );
+
+    offset = 0;
+    n = 1;
+    for( i=1; i<count; i++ )
+    {
+        len = pool[i*2];
+
+        /*
+         * If a string is over 64k, the previous string entry is made null
+         * and its the high word of the length is inserted in the null string's
+         * reference count field.
+         */
+        if( pool[i*2-2] == 0 )
+            len += pool[i*2-1] * 0x10000;
+
+        if( (offset + len) > datasize )
+        {
+            ERR("string table corrupt?\n");
+            break;
+        }
+
+        /* don't add the high word of a string's length as a string */
+        if ( len || !pool[i*2+1] )
+        {
+            r = msi_addstring( st, n, data+offset, len, pool[i*2+1] );
+            if( r != n )
+                ERR("Failed to add string %ld\n", n );
+            n++;
+        }
+
+        offset += len;
+    }
+
+    if ( datasize != offset )
+        ERR("string table load failed! (%08x != %08lx)\n", datasize, offset );
+
+    TRACE("Loaded %ld strings\n", count);
+
+end:
+    msi_free( pool );
+    msi_free( data );
+
+    return st;
+}
+
+static UINT save_string_table( MSIDATABASE *db )
+{
+    UINT i, count, datasize, poolsize, sz, used, r, codepage;
+    UINT ret = ERROR_FUNCTION_FAILED;
+    static const WCHAR szStringData[] = {
+        '_','S','t','r','i','n','g','D','a','t','a',0 };
+    static const WCHAR szStringPool[] = {
+        '_','S','t','r','i','n','g','P','o','o','l',0 };
+    CHAR *data = NULL;
+    USHORT *pool = NULL;
+
+    TRACE("\n");
+
+    /* construct the new table in memory first */
+    datasize = msi_string_totalsize( db->strings, &count );
+    poolsize = count*2*sizeof(USHORT);
+
+    pool = msi_alloc( poolsize );
+    if( ! pool )
+    {
+        WARN("Failed to alloc pool %d bytes\n", poolsize );
+        goto err;
+    }
+    data = msi_alloc( datasize );
+    if( ! data )
+    {
+        WARN("Failed to alloc data %d bytes\n", poolsize );
+        goto err;
+    }
+
+    used = 0;
+    codepage = msi_string_get_codepage( db->strings );
+    pool[0]=codepage&0xffff;
+    pool[1]=(codepage>>16);
+    for( i=1; i<count; i++ )
+    {
+        sz = datasize - used;
+        r = msi_id2stringA( db->strings, i, data+used, &sz );
+        if( r != ERROR_SUCCESS )
+        {
+            ERR("failed to fetch string\n");
+            sz = 0;
+        }
+        if( sz && (sz < (datasize - used ) ) )
+            sz--;
+        TRACE("adding %u bytes %s\n", sz, debugstr_a(data+used) );
+        pool[ i*2 ] = sz;
+        pool[ i*2 + 1 ] = msi_id_refcount( db->strings, i );
+        used += sz;
+        if( used > datasize  )
+        {
+            ERR("oops overran %d >= %d\n", used, datasize);
+            goto err;
+        }
+    }
+
+    if( used != datasize )
+    {
+        ERR("oops used %d != datasize %d\n", used, datasize);
+        goto err;
+    }
+
+    /* write the streams */
+    r = write_stream_data( db->storage, szStringData, data, datasize );
+    TRACE("Wrote StringData r=%08x\n", r);
+    if( r )
+        goto err;
+    r = write_stream_data( db->storage, szStringPool, pool, poolsize );
+    TRACE("Wrote StringPool r=%08x\n", r);
+    if( r )
+        goto err;
+
+    ret = ERROR_SUCCESS;
+
+err:
+    msi_free( data );
+    msi_free( pool );
+
+    return ret;
+}
+
+/* information for default tables */
+static const WCHAR szTables[]  = { '_','T','a','b','l','e','s',0 };
+static const WCHAR szTable[]  = { 'T','a','b','l','e',0 };
+static const WCHAR szName[]    = { 'N','a','m','e',0 };
+static const WCHAR szColumns[] = { '_','C','o','l','u','m','n','s',0 };
+static const WCHAR szColumn[]  = { 'C','o','l','u','m','n',0 };
+static const WCHAR szNumber[]  = { 'N','u','m','b','e','r',0 };
+static const WCHAR szType[]    = { 'T','y','p','e',0 };
+
+static const MSICOLUMNINFO _Columns_cols[4] = {
+    { szColumns, 1, szTable,  MSITYPE_VALID | MSITYPE_STRING | 64, 0 },
+    { szColumns, 2, szNumber, MSITYPE_VALID | 2,                   2 },
+    { szColumns, 3, szName,   MSITYPE_VALID | MSITYPE_STRING | 64, 4 },
+    { szColumns, 4, szType,   MSITYPE_VALID | 2,                   6 },
+};
+static const MSICOLUMNINFO _Tables_cols[1] = {
+    { szTables,  1, szName,   MSITYPE_VALID | MSITYPE_STRING | 64, 0 },
+};
+
+static UINT get_defaulttablecolumns( LPCWSTR name, MSICOLUMNINFO *colinfo, UINT *sz)
+{
+    const MSICOLUMNINFO *p;
+    DWORD i, n;
+
+    TRACE("%s\n", debugstr_w(name));
+
+    if (!lstrcmpW( name, szTables ))
+    {
+        p = _Tables_cols;
+        n = 1;
+    }
+    else if (!lstrcmpW( name, szColumns ))
+    {
+        p = _Columns_cols;
+        n = 4;
+    }
+    else
+        return ERROR_FUNCTION_FAILED;
+
+    /* duplicate the string data so we can free it in msi_free_colinfo */
+    for (i=0; i<n; i++)
+    {
+        if (colinfo && (i < *sz) )
+        {
+            memcpy( &colinfo[i], &p[i], sizeof(MSICOLUMNINFO) );
+            colinfo[i].tablename = strdupW( p[i].tablename );
+            colinfo[i].colname = strdupW( p[i].colname );
+        }
+        if( colinfo && (i >= *sz) )
+            break;
+    }
+    *sz = n;
+    return ERROR_SUCCESS;
+}
+
+static void msi_free_colinfo( MSICOLUMNINFO *colinfo, UINT count )
+{
+    UINT i;
+
+    for( i=0; i<count; i++ )
+    {
+        msi_free( (LPWSTR) colinfo[i].tablename );
+        msi_free( (LPWSTR) colinfo[i].colname );
+    }
+}
+
+LPWSTR MSI_makestring( MSIDATABASE *db, UINT stringid)
+{
+    UINT sz=0, r;
+    LPWSTR str;
+
+    r = msi_id2stringW( db->strings, stringid, NULL, &sz );
+    if( r != ERROR_SUCCESS )
+        return NULL;
+    str = msi_alloc( sz*sizeof (WCHAR) );
+    if( !str )
+        return str;
+    r = msi_id2stringW( db->strings, stringid, str, &sz );
+    if( r == ERROR_SUCCESS )
+        return str;
+    msi_free( str );
+    return NULL;
+}
+
+static UINT get_tablecolumns( MSIDATABASE *db, 
+       LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz)
+{
+    UINT r, i, n=0, table_id, count, maxcount = *sz;
+    MSITABLE *table = NULL;
+
+    /* first check if there is a default table with that name */
+    r = get_defaulttablecolumns( szTableName, colinfo, sz );
+    if( ( r == ERROR_SUCCESS ) && *sz )
+        return r;
+
+    table = get_table( db, szColumns, _Columns_cols, 4 );
+    if( !table )
+    {
+        ERR("couldn't load _Columns table\n");
+        return ERROR_FUNCTION_FAILED;
+    }
+
+    /* convert table and column names to IDs from the string table */
+    r = msi_string2idW( db->strings, szTableName, &table_id );
+    if( r != ERROR_SUCCESS )
+    {
+        WARN("Couldn't find id for %s\n", debugstr_w(szTableName));
+        return r;
+    }
+
+    TRACE("Table id is %d\n", table_id);
+
+    count = table->row_count;
+    for( i=0; i<count; i++ )
+    {
+        if( table->data[ i ][ 0 ] != table_id )
+            continue;
+        if( colinfo )
+        {
+            UINT id = table->data[ i ] [ 2 ];
+            colinfo[n].tablename = MSI_makestring( db, table_id );
+            colinfo[n].number = table->data[ i ][ 1 ] - (1<<15);
+            colinfo[n].colname = MSI_makestring( db, id );
+            colinfo[n].type = table->data[ i ] [ 3 ] ^ 0x8000;
+            /* this assumes that columns are in order in the table */
+            if( n )
+                colinfo[n].offset = colinfo[n-1].offset
+                                  + bytes_per_column( &colinfo[n-1] );
+            else
+                colinfo[n].offset = 0;
+            TRACE("table %s column %d is [%s] (%d) with type %08x "
+                  "offset %d at row %d\n", debugstr_w(szTableName),
+                   colinfo[n].number, debugstr_w(colinfo[n].colname),
+                   id, colinfo[n].type, colinfo[n].offset, i);
+            if( n != (colinfo[n].number-1) )
+            {
+                ERR("oops. data in the _Columns table isn't in the right "
+                    "order for table %s\n", debugstr_w(szTableName));
+                return ERROR_FUNCTION_FAILED;
+            }
+        }
+        n++;
+        if( colinfo && ( n >= maxcount ) )
+            break;
+    }
+    *sz = n;
+
+    return ERROR_SUCCESS;
+}
+
+/* try to find the table name in the _Tables table */
+BOOL TABLE_Exists( MSIDATABASE *db, LPWSTR name )
+{
+    UINT r, table_id = 0, i, count;
+    MSITABLE *table = NULL;
+
+    if( !lstrcmpW( name, szTables ) )
+        return TRUE;
+    if( !lstrcmpW( name, szColumns ) )
+        return TRUE;
+
+    r = msi_string2idW( db->strings, name, &table_id );
+    if( r != ERROR_SUCCESS )
+    {
+        TRACE("Couldn't find id for %s\n", debugstr_w(name));
+        return FALSE;
+    }
+
+    table = get_table( db, szTables, _Tables_cols, 1 );
+    if( !table )
+    {
+        TRACE("table %s not available\n", debugstr_w(szTables));
+        return FALSE;
+    }
+
+    /* count = table->size/2; */
+    count = table->row_count;
+    for( i=0; i<count; i++ )
+        if( table->data[ i ][ 0 ] == table_id )
+            break;
+
+    if (i!=count)
+        return TRUE;
+
+    TRACE("Searched %d tables, but %d was not found\n", count, table_id );
+
+    return FALSE;
+}
+
+/* below is the query interface to a table */
+
+typedef struct tagMSITABLEVIEW
+{
+    MSIVIEW        view;
+    MSIDATABASE   *db;
+    MSITABLE      *table;
+    MSICOLUMNINFO *columns;
+    UINT           num_cols;
+    UINT           row_size;
+    WCHAR          name[1];
+} MSITABLEVIEW;
+
+static UINT TABLE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
+{
+    MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
+    UINT offset, num_rows, n;
+
+    if( !tv->table )
+        return ERROR_INVALID_PARAMETER;
+
+    if( (col==0) || (col>tv->num_cols) )
+        return ERROR_INVALID_PARAMETER;
+
+    /* how many rows are there ? */
+    num_rows = tv->table->row_count;
+    if( row >= num_rows )
+        return ERROR_NO_MORE_ITEMS;
+
+    if( tv->columns[col-1].offset >= tv->row_size )
+    {
+        ERR("Stuffed up %d >= %d\n", tv->columns[col-1].offset, tv->row_size );
+        ERR("%p %p\n", tv, tv->columns );
+        return ERROR_FUNCTION_FAILED;
+    }
+
+    offset = row + (tv->columns[col-1].offset/2) * num_rows;
+    n = bytes_per_column( &tv->columns[col-1] );
+    switch( n )
+    {
+    case 4:
+        offset = tv->columns[col-1].offset/2;
+        *val = tv->table->data[row][offset] + 
+               (tv->table->data[row][offset + 1] << 16);
+        break;
+    case 2:
+        offset = tv->columns[col-1].offset/2;
+        *val = tv->table->data[row][offset];
+        break;
+    default:
+        ERR("oops! what is %d bytes per column?\n", n );
+        return ERROR_FUNCTION_FAILED;
+    }
+
+    /* TRACE("Data [%d][%d] = %d\n", row, col, *val ); */
+
+    return ERROR_SUCCESS;
+}
+
+/*
+ * We need a special case for streams, as we need to reference column with
+ * the name of the stream in the same table, and the table name
+ * which may not be available at higher levels of the query
+ */
+static UINT TABLE_fetch_stream( struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm )
+{
+    MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
+    UINT ival = 0, refcol = 0, r;
+    LPWSTR sval;
+    LPWSTR full_name;
+    DWORD len;
+    static const WCHAR szDot[] = { '.', 0 };
+
+    if( !view->ops->fetch_int )
+        return ERROR_INVALID_PARAMETER;
+
+    /*
+     * The column marked with the type stream data seems to have a single number
+     * which references the column containing the name of the stream data
+     *
+     * Fetch the column to reference first.
+     */
+    r = view->ops->fetch_int( view, row, col, &ival );
+    if( r != ERROR_SUCCESS )
+        return r;
+
+    /* now get the column with the name of the stream */
+    r = view->ops->fetch_int( view, row, ival, &refcol );
+    if( r != ERROR_SUCCESS )
+        return r;
+
+    /* lookup the string value from the string table */
+    sval = MSI_makestring( tv->db, refcol );
+    if( !sval )
+        return ERROR_INVALID_PARAMETER;
+
+    len = lstrlenW( tv->name ) + 2 + lstrlenW( sval );
+    full_name = msi_alloc( len*sizeof(WCHAR) );
+    lstrcpyW( full_name, tv->name );
+    lstrcatW( full_name, szDot );
+    lstrcatW( full_name, sval );
+
+    r = db_get_raw_stream( tv->db, full_name, stm );
+    if( r )
+        ERR("fetching stream %s, error = %d\n",debugstr_w(full_name), r);
+    msi_free( full_name );
+    msi_free( sval );
+
+    return r;
+}
+
+static UINT TABLE_set_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT val )
+{
+    MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
+    UINT offset, n;
+
+    if( !tv->table )
+        return ERROR_INVALID_PARAMETER;
+
+    if( (col==0) || (col>tv->num_cols) )
+        return ERROR_INVALID_PARAMETER;
+
+    if( tv->columns[col-1].offset >= tv->row_size )
+    {
+        ERR("Stuffed up %d >= %d\n", tv->columns[col-1].offset, tv->row_size );
+        ERR("%p %p\n", tv, tv->columns );
+        return ERROR_FUNCTION_FAILED;
+    }
+
+    n = bytes_per_column( &tv->columns[col-1] );
+    switch( n )
+    {
+    case 4:
+        offset = tv->columns[col-1].offset/2;
+        tv->table->data[row][offset]     = val & 0xffff;
+        tv->table->data[row][offset + 1] = (val>>16)&0xffff;
+        break;
+    case 2:
+        offset = tv->columns[col-1].offset/2;
+        tv->table->data[row][offset] = val;
+        break;
+    default:
+        ERR("oops! what is %d bytes per column?\n", n );
+        return ERROR_FUNCTION_FAILED;
+    }
+    return ERROR_SUCCESS;
+}
+
+static UINT table_create_new_row( struct tagMSIVIEW *view, UINT *num )
+{
+    MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
+    USHORT **p, *row;
+    UINT sz;
+
+    TRACE("%p\n", view);
+
+    if( !tv->table )
+        return ERROR_INVALID_PARAMETER;
+
+    row = msi_alloc_zero( tv->row_size );
+    if( !row )
+        return ERROR_NOT_ENOUGH_MEMORY;
+
+    sz = (tv->table->row_count + 1) * sizeof (UINT*);
+    if( tv->table->data )
+        p = msi_realloc( tv->table->data, sz );
+    else
+        p = msi_alloc( sz );
+    if( !p )
+    {
+        msi_free( row );
+        return ERROR_NOT_ENOUGH_MEMORY;
+    }
+
+    tv->table->data = p;
+    tv->table->data[tv->table->row_count] = row;
+    *num = tv->table->row_count;
+    tv->table->row_count++;
+
+    return ERROR_SUCCESS;
+}
+
+static UINT TABLE_execute( struct tagMSIVIEW *view, MSIRECORD *record )
+{
+    MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
+
+    TRACE("%p %p\n", tv, record);
+
+    TRACE("There are %d columns\n", tv->num_cols );
+    tv->table = get_table( tv->db, tv->name, tv->columns, tv->num_cols );
+    if( !tv->table )
+        return ERROR_FUNCTION_FAILED;
+
+    return ERROR_SUCCESS;
+}
+
+static UINT TABLE_close( struct tagMSIVIEW *view )
+{
+    MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
+
+    TRACE("%p\n", view );
+
+    if( !tv->table )
+        return ERROR_FUNCTION_FAILED;
+
+    tv->table = NULL;
+    
+    return ERROR_SUCCESS;
+}
+
+static UINT TABLE_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols)
+{
+    MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
+
+    TRACE("%p %p %p\n", view, rows, cols );
+
+    if( cols )
+        *cols = tv->num_cols;
+    if( rows )
+    {
+        if( !tv->table )
+            return ERROR_INVALID_PARAMETER;
+        *rows = tv->table->row_count;
+    }
+
+    return ERROR_SUCCESS;
+}
+
+static UINT TABLE_get_column_info( struct tagMSIVIEW *view,
+                UINT n, LPWSTR *name, UINT *type )
+{
+    MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
+
+    TRACE("%p %d %p %p\n", tv, n, name, type );
+
+    if( ( n == 0 ) || ( n > tv->num_cols ) )
+        return ERROR_INVALID_PARAMETER;
+
+    if( name )
+    {
+        *name = strdupW( tv->columns[n-1].colname );
+        if( !*name )
+            return ERROR_FUNCTION_FAILED;
+    }
+    if( type )
+        *type = tv->columns[n-1].type;
+
+    return ERROR_SUCCESS;
+}
+
+static UINT msi_table_find_row( MSITABLEVIEW *tv, MSIRECORD *rec, UINT *row );
+
+static UINT table_validate_new( MSITABLEVIEW *tv, MSIRECORD *rec )
+{
+    UINT r, row;
+
+    r = msi_table_find_row( tv, rec, &row );
+    if (r != ERROR_SUCCESS)
+        return ERROR_SUCCESS;
+    return ERROR_INVALID_DATA;
+}
+
+static UINT msi_table_modify_row( MSITABLEVIEW *tv, MSIRECORD *rec,
+                                  UINT row, UINT mask )
+{
+    UINT i, val, r = ERROR_SUCCESS;
+
+    TRACE("%p %p %u %08x\n", tv, rec, row, mask );
+
+    for( i = 0; i < tv->num_cols; i++ )
+    {
+        /* set keys or values specified in the mask */
+        if( (~tv->columns[i].type & MSITYPE_KEY) && (~mask & (1<<i)) )
+            continue;
+
+        if( (tv->columns[i].type & MSITYPE_STRING) &&
+            ! MSITYPE_IS_BINARY(tv->columns[i].type) )
+        {
+            const WCHAR *str = MSI_RecordGetString( rec, i+1 );
+            val = msi_addstringW( tv->db->strings, 0, str, -1, 1 );
+        }
+        else
+        {
+            val = MSI_RecordGetInteger( rec, i+1 );
+            if ( 2 == bytes_per_column( &tv->columns[i] ) )
+                val ^= 0x8000;
+        }
+        r = TABLE_set_int( &tv->view, row, i+1, val );
+        if( r )
+            break;
+    }
+
+    return r;
+}
+
+static UINT TABLE_insert_row( struct tagMSIVIEW *view, MSIRECORD *rec )
+{
+    MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
+    UINT r, row = -1;
+
+    TRACE("%p %p\n", tv, rec );
+
+    r = table_create_new_row( view, &row );
+    TRACE("insert_row returned %08x\n", r);
+    if( r != ERROR_SUCCESS )
+        return r;
+
+    return msi_table_modify_row( tv, rec, row, ~0 );
+}
+
+static UINT TABLE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode,
+                MSIRECORD *rec)
+{
+    MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
+    UINT r;
+
+    TRACE("%p %d %p\n", view, eModifyMode, rec );
+
+    if (!tv->table)
+    {
+        r = TABLE_execute( view, NULL );
+        if( r )
+            return r;
+    }
+
+    switch (eModifyMode)
+    {
+    case MSIMODIFY_VALIDATE_NEW:
+        r = table_validate_new( tv, rec );
+        break;
+
+    case MSIMODIFY_INSERT_TEMPORARY:
+        r = table_validate_new( tv, rec );
+        if (r != ERROR_SUCCESS)
+            break;
+        r = TABLE_insert_row( view, rec );
+        break;
+
+    case MSIMODIFY_REFRESH:
+    case MSIMODIFY_INSERT:
+    case MSIMODIFY_UPDATE:
+    case MSIMODIFY_ASSIGN:
+    case MSIMODIFY_REPLACE:
+    case MSIMODIFY_MERGE:
+    case MSIMODIFY_DELETE:
+    case MSIMODIFY_VALIDATE:
+    case MSIMODIFY_VALIDATE_FIELD:
+    case MSIMODIFY_VALIDATE_DELETE:
+        FIXME("%p %d %p - mode not implemented\n", view, eModifyMode, rec );
+        r = ERROR_CALL_NOT_IMPLEMENTED;
+        break;
+
+    default:
+        r = ERROR_INVALID_DATA;
+    }
+
+    return r;
+}
+
+static UINT TABLE_delete( struct tagMSIVIEW *view )
+{
+    MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
+
+    TRACE("%p\n", view );
+
+    tv->table = NULL;
+
+    if( tv->columns )
+    {
+        msi_free_colinfo( tv->columns, tv->num_cols );
+        msi_free( tv->columns );
+    }
+    tv->columns = NULL;
+
+    msi_free( tv );
+
+    return ERROR_SUCCESS;
+}
+
+
+MSIVIEWOPS table_ops =
+{
+    TABLE_fetch_int,
+    TABLE_fetch_stream,
+    TABLE_set_int,
+    TABLE_insert_row,
+    TABLE_execute,
+    TABLE_close,
+    TABLE_get_dimensions,
+    TABLE_get_column_info,
+    TABLE_modify,
+    TABLE_delete
+};
+
+UINT TABLE_CreateView( MSIDATABASE *db, LPCWSTR name, MSIVIEW **view )
+{
+    MSITABLEVIEW *tv ;
+    UINT r, sz, column_count;
+    MSICOLUMNINFO *columns;
+
+    TRACE("%p %s %p\n", db, debugstr_w(name), view );
+
+    /* get the number of columns in this table */
+    column_count = 0;
+    r = get_tablecolumns( db, name, NULL, &column_count );
+    if( r != ERROR_SUCCESS )
+        return r;
+
+    /* if there's no columns, there's no table */
+    if( column_count == 0 )
+        return ERROR_INVALID_PARAMETER;
+
+    TRACE("Table found\n");
+
+    sz = sizeof *tv + lstrlenW(name)*sizeof name[0] ;
+    tv = msi_alloc_zero( sz );
+    if( !tv )
+        return ERROR_FUNCTION_FAILED;
+    
+    columns = msi_alloc( column_count*sizeof (MSICOLUMNINFO));
+    if( !columns )
+    {
+        msi_free( tv );
+        return ERROR_FUNCTION_FAILED;
+    }
+
+    r = get_tablecolumns( db, name, columns, &column_count );
+    if( r != ERROR_SUCCESS )
+    {
+        msi_free( columns );
+        msi_free( tv );
+        return ERROR_FUNCTION_FAILED;
+    }
+
+    TRACE("Table has %d columns\n", column_count);
+
+    /* fill the structure */
+    tv->view.ops = &table_ops;
+    tv->db = db;
+    tv->columns = columns;
+    tv->num_cols = column_count;
+    tv->table = NULL;
+    tv->row_size = msi_table_get_row_size( columns, column_count );
+
+    TRACE("%s one row is %d bytes\n", debugstr_w(name), tv->row_size );
+
+    *view = (MSIVIEW*) tv;
+    lstrcpyW( tv->name, name );
+
+    return ERROR_SUCCESS;
+}
+
+UINT MSI_CommitTables( MSIDATABASE *db )
+{
+    UINT r;
+    MSITABLE *table = NULL;
+
+    TRACE("%p\n",db);
+
+    r = save_string_table( db );
+    if( r != ERROR_SUCCESS )
+    {
+        WARN("failed to save string table r=%08x\n",r);
+        return r;
+    }
+
+    LIST_FOR_EACH_ENTRY( table, &db->tables, MSITABLE, entry )
+    {
+        r = save_table( db, table );
+        if( r != ERROR_SUCCESS )
+        {
+            WARN("failed to save table %s (r=%08x)\n",
+                  debugstr_w(table->name), r);
+            return r;
+        }
+    }
+
+    /* force everything to reload next time */
+    free_cached_tables( db );
+
+    return ERROR_SUCCESS;
+}
+
+static MSIRECORD *msi_get_transform_record( MSITABLEVIEW *tv, string_table *st, USHORT *rawdata )
+{
+    UINT i, val, ofs = 0;
+    USHORT mask = *rawdata++;
+    MSICOLUMNINFO *columns = tv->columns;
+    MSIRECORD *rec;
+    const int debug_transform = 0;
+
+    rec = MSI_CreateRecord( tv->num_cols );
+    if( !rec )
+        return rec;
+
+    if( debug_transform ) MESSAGE("row -> ");
+    for( i=0; i<tv->num_cols; i++ )
+    {
+        UINT n = bytes_per_column( &columns[i] );
+
+        if ( (mask&1) && (i>=(mask>>8)) )
+            break;
+        /* all keys must be present */
+        if ( (~mask&1) && (~columns[i].type & MSITYPE_KEY) && ((1<<i) & ~mask) )
+            continue;
+
+        switch( n )
+        {
+        case 2:
+            val = rawdata[ofs];
+            if( (columns[i].type & MSITYPE_STRING) &&
+                ! MSITYPE_IS_BINARY(tv->columns[i].type) )
+            {
+                LPCWSTR sval = msi_string_lookup_id( st, val );
+                MSI_RecordSetStringW( rec, i+1, sval );
+                if( debug_transform ) MESSAGE("[%s]", debugstr_w(sval));
+            }
+            else
+            {
+                val ^= 0x8000;
+                MSI_RecordSetInteger( rec, i+1, val );
+                if( debug_transform) MESSAGE("[0x%04x]", val );
+            }
+            break;
+        case 4:
+            val = rawdata[ofs] + (rawdata[ofs + 1]<<16);
+            /* val ^= 0x80000000; */
+            MSI_RecordSetInteger( rec, i+1, val );
+            if( debug_transform ) MESSAGE("[0x%08x]", val );
+            break;
+        default:
+            ERR("oops - unknown column width %d\n", n);
+            break;
+        }
+        ofs += n/2;
+    }
+    if( debug_transform) MESSAGE("\n");
+    return rec;
+}
+
+static void dump_record( MSIRECORD *rec )
+{
+    UINT i, n;
+
+    MESSAGE("row -> ");
+    n = MSI_RecordGetFieldCount( rec );
+    for( i=1; i<=n; i++ )
+    {
+        LPCWSTR sval = MSI_RecordGetString( rec, i );
+
+        if( MSI_RecordIsNull( rec, i ) )
+            MESSAGE("[]");
+        else if( (sval = MSI_RecordGetString( rec, i )) )
+            MESSAGE("[%s]", debugstr_w(sval));
+        else
+            MESSAGE("[0x%08x]", MSI_RecordGetInteger( rec, i ) );
+    }
+    MESSAGE("\n");
+}
+
+static void dump_table( string_table *st, USHORT *rawdata, UINT rawsize )
+{
+    LPCWSTR sval;
+    UINT i;
+
+    for( i=0; i<(rawsize/2); i++ )
+    {
+        sval = msi_string_lookup_id( st, rawdata[i] );
+        if( !sval ) sval = (WCHAR[]) {0};
+        MESSAGE(" %04x %s\n", rawdata[i], debugstr_w(sval) );
+    }
+}
+
+static UINT* msi_record_to_row( MSITABLEVIEW *tv, MSIRECORD *rec )
+{
+    LPCWSTR str;
+    UINT i, r, *data;
+
+    data = msi_alloc( tv->num_cols *sizeof (UINT) );
+    for( i=0; i<tv->num_cols; i++ )
+    {
+        data[i] = 0;
+
+        if ( ~tv->columns[i].type & MSITYPE_KEY )
+            continue;
+
+        /* turn the transform column value into a row value */
+        if ( ( tv->columns[i].type & MSITYPE_STRING ) &&
+             ! MSITYPE_IS_BINARY(tv->columns[i].type) )
+        {
+            str = MSI_RecordGetString( rec, i+1 );
+            r = msi_string2idW( tv->db->strings, str, &data[i] );
+
+            /* if there's no matching string in the string table,
+               these keys can't match any record, so fail now. */
+            if( ERROR_SUCCESS != r )
+            {
+                msi_free( data );
+                return NULL;
+            }
+        }
+        else
+            data[i] = MSI_RecordGetInteger( rec, i+1 );
+    }
+    return data;
+}
+
+static UINT msi_row_matches( MSITABLEVIEW *tv, UINT row, UINT *data )
+{
+    UINT i, r, x, ret = ERROR_FUNCTION_FAILED;
+
+    for( i=0; i<tv->num_cols; i++ )
+    {
+        if ( ~tv->columns[i].type & MSITYPE_KEY )
+            continue;
+
+        /* turn the transform column value into a row value */
+        r = TABLE_fetch_int( &tv->view, row, i+1, &x );
+        if ( r != ERROR_SUCCESS )
+        {
+            ERR("TABLE_fetch_int shouldn't fail here\n");
+            break;
+        }
+
+        /* if this key matches, move to the next column */
+        if ( x != data[i] )
+        {
+            ret = ERROR_FUNCTION_FAILED;
+            break;
+        }
+
+        ret = ERROR_SUCCESS;
+    }
+
+    return ret;
+}
+
+static UINT msi_table_find_row( MSITABLEVIEW *tv, MSIRECORD *rec, UINT *row )
+{
+    UINT i, r = ERROR_FUNCTION_FAILED, *data;
+
+    data = msi_record_to_row( tv, rec );
+    if( !data )
+        return r;
+    for( i=0; i<tv->table->row_count; i++ )
+    {
+        r = msi_row_matches( tv, i, data );
+        if( r == ERROR_SUCCESS )
+        {
+            *row = i;
+            break;
+        }
+    }
+    msi_free( data );
+    return r;
+}
+
+static UINT msi_delete_row( MSITABLEVIEW *tv, UINT row )
+{
+    UINT i;
+    for( i=1; i<=tv->num_cols; i++ )
+        tv->view.ops->set_int( &tv->view, row, i, 0 );
+    return ERROR_SUCCESS;
+}
+
+static UINT msi_table_load_transform( MSIDATABASE *db, IStorage *stg,
+                                      string_table *st, LPCWSTR name )
+{
+    UINT rawsize = 0;
+    USHORT *rawdata = NULL;
+    MSITABLEVIEW *tv = NULL;
+    UINT r, n, sz, i, mask;
+    MSIRECORD *rec = NULL;
+    const int debug_transform = 0;
+
+    TRACE("%p %p %p %s\n", db, stg, st, debugstr_w(name) );
+
+    /* create a table view */
+    r = TABLE_CreateView( db, name, (MSIVIEW**) &tv );
+    if( r != ERROR_SUCCESS )
+        goto err;
+
+    r = tv->view.ops->execute( &tv->view, NULL );
+    if( r != ERROR_SUCCESS )
+        goto err;
+
+    /* read the transform data */
+    r = ERROR_FUNCTION_FAILED;
+    read_stream_data( stg, name, &rawdata, &rawsize );
+    if( !rawdata || (rawsize < 2) )
+    {
+        ERR("odd sized transform for table %s\n", debugstr_w(name));
+        goto err;
+    }
+
+    TRACE("name = %s columns = %u row_size = %u raw size = %u\n",
+          debugstr_w(name), tv->num_cols, tv->row_size, rawsize );
+
+    /* interpret the data */
+    r = ERROR_SUCCESS;
+    for( n=0; n < (rawsize/2);  )
+    {
+        mask = rawdata[n];
+
+        if (mask&1)
+        {
+            /*
+             * if the low bit is set, columns are continuous and
+             * the number of columns is specified in the high byte
+             */
+            sz = 2 + tv->row_size;
+        }
+        else
+        {
+            /*
+             * If the low bit is not set, rowdata[n] is a bitmask.
+             * Excepting for key fields, which are always present,
+             *  each bit indicates that a field is present in the transform record.
+             *
+             * rawdata[n] == 0 is a special case ... only the keys will be present
+             * and it means that this row should be deleted.
+             */
+            sz = 2;
+            for( i=0; i<tv->num_cols; i++ )
+            {
+                if( (tv->columns[i].type & MSITYPE_KEY) || ((1<<i)&mask))
+                    sz += bytes_per_column( &tv->columns[i] );
+            }
+        }
+
+        /* check we didn't run of the end of the table */
+        if ( (n+sz) > rawsize )
+        {
+            ERR("borked.\n");
+            dump_table( st, rawdata, rawsize );
+            break;
+        }
+
+        rec = msi_get_transform_record( tv, st, &rawdata[n] );
+        if (rec)
+        {
+            UINT row = 0;
+
+            r = msi_table_find_row( tv, rec, &row );
+
+            if( rawdata[n] & 1)
+            {
+                if( debug_transform ) MESSAGE("insert [%d]: ", row);
+                TABLE_insert_row( &tv->view, rec );
+            }
+            else if( mask & 0xff )
+            {
+                if( debug_transform ) MESSAGE("modify [%d]: ", row);
+                msi_table_modify_row( tv, rec, row, mask );
+            }
+            else
+            {
+                if( debug_transform ) MESSAGE("delete [%d]: ", row);
+                msi_delete_row( tv, row );
+            }
+            if( debug_transform ) dump_record( rec );
+            msiobj_release( &rec->hdr );
+        }
+
+        n += sz/2;
+        
+    }
+
+err:
+    /* no need to free the table, it's associated with the database */
+    msi_free( rawdata );
+    if( tv )
+        tv->view.ops->delete( &tv->view );
+
+    return ERROR_SUCCESS;
+}
+
+/*
+ * msi_table_apply_transform
+ *
+ * Enumerate the table transforms in a transform storage and apply each one.
+ */
+UINT msi_table_apply_transform( MSIDATABASE *db, IStorage *stg )
+{
+    IEnumSTATSTG *stgenum = NULL;
+    HRESULT r;
+    STATSTG stat;
+    ULONG n, count;
+    WCHAR name[0x40];
+    string_table *strings;
+    UINT ret = ERROR_FUNCTION_FAILED;
+
+    TRACE("%p %p\n", db, stg );
+
+    strings = load_string_table( stg );
+    if( !strings )
+        goto end;
+
+    r = IStorage_EnumElements( stg, 0, NULL, 0, &stgenum );
+    if( FAILED( r ) )
+        goto end;
+
+    n = 0;
+    ret = ERROR_SUCCESS;
+
+    while( r == ERROR_SUCCESS )
+    {
+        count = 0;
+        r = IEnumSTATSTG_Next( stgenum, 1, &stat, &count );
+        if( FAILED( r ) || !count )
+            break;
+        decode_streamname( stat.pwcsName, name );
+        if( ( name[0] == 0x4840 ) && ( name[1] != '_' ) )
+            ret = msi_table_load_transform( db, stg, strings, name+1 );
+        else
+            TRACE("transform contains stream %s\n", debugstr_w(name));
+        n++;
+    }
+
+    if ( ret == ERROR_SUCCESS )
+    {
+        MSITRANSFORM *t;
+
+        t = msi_alloc( sizeof *t );
+        t->stg = stg;
+        IStorage_AddRef( stg );
+        list_add_tail( &db->transforms, &t->entry );
+    }
+
+end:
+    if ( stgenum )
+        IEnumSTATSTG_Release( stgenum );
+    if ( strings )
+        msi_destroy_stringtable( strings );
+
+    return ret;
+}
+
+void msi_free_transforms( MSIDATABASE *db )
+{
+    while( !list_empty( &db->transforms ) )
+    {
+        MSITRANSFORM *t = LIST_ENTRY( list_head( &db->transforms ),
+                                      MSITRANSFORM, entry );
+        list_remove( &t->entry );
+        IStorage_Release( t->stg );
+        msi_free( t );
+    }
+}
diff --git a/reactos/dll/msi/tokenize.c b/reactos/dll/msi/tokenize.c
new file mode 100644 (file)
index 0000000..05c3cda
--- /dev/null
@@ -0,0 +1,400 @@
+/*
+** 2001 September 15
+**
+** The author disclaims copyright to this source code.  In place of
+** a legal notice, here is a blessing:
+**
+**    May you do good and not evil.
+**    May you find forgiveness for yourself and forgive others.
+**    May you share freely, never taking more than you give.
+**
+*************************************************************************
+** An tokenizer for SQL
+**
+** This file contains C code that splits an SQL input string up into
+** individual tokens and sends those tokens one-by-one over to the
+** parser for analysis.
+*/
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdlib.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wine/debug.h"
+#include "winnls.h"
+#include "query.h"
+#include "sql.tab.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+/*
+** All the keywords of the SQL language are stored as in a hash
+** table composed of instances of the following structure.
+*/
+typedef struct Keyword Keyword;
+struct Keyword {
+  const char *zName;             /* The keyword name */
+  int tokenType;           /* The token value for this keyword */
+};
+
+/*
+** These are the keywords
+*/
+static const Keyword aKeywordTable[] = {
+  { "ABORT", TK_ABORT },
+  { "AFTER", TK_AFTER },
+  { "ALL", TK_ALL },
+  { "AND", TK_AND },
+  { "AS", TK_AS },
+  { "ASC", TK_ASC },
+  { "BEFORE", TK_BEFORE },
+  { "BEGIN", TK_BEGIN },
+  { "BETWEEN", TK_BETWEEN },
+  { "BY", TK_BY },
+  { "CASCADE", TK_CASCADE },
+  { "CASE", TK_CASE },
+  { "CHAR", TK_CHAR },
+  { "CHARACTER", TK_CHAR },
+  { "CHECK", TK_CHECK },
+  { "CLUSTER", TK_CLUSTER },
+  { "COLLATE", TK_COLLATE },
+  { "COMMIT", TK_COMMIT },
+  { "CONFLICT", TK_CONFLICT },
+  { "CONSTRAINT", TK_CONSTRAINT },
+  { "COPY", TK_COPY },
+  { "CREATE", TK_CREATE },
+  { "CROSS", TK_JOIN_KW },
+  { "DEFAULT", TK_DEFAULT },
+  { "DEFERRED", TK_DEFERRED },
+  { "DEFERRABLE", TK_DEFERRABLE },
+  { "DELETE", TK_DELETE },
+  { "DELIMITERS", TK_DELIMITERS },
+  { "DESC", TK_DESC },
+  { "DISTINCT", TK_DISTINCT },
+  { "DROP", TK_DROP },
+  { "END", TK_END },
+  { "EACH", TK_EACH },
+  { "ELSE", TK_ELSE },
+  { "EXCEPT", TK_EXCEPT },
+  { "EXPLAIN", TK_EXPLAIN },
+  { "FAIL", TK_FAIL },
+  { "FOR", TK_FOR },
+  { "FOREIGN", TK_FOREIGN },
+  { "FROM", TK_FROM },
+  { "FULL", TK_JOIN_KW },
+  { "GLOB", TK_GLOB },
+  { "GROUP", TK_GROUP },
+  { "HAVING", TK_HAVING },
+  { "HOLD", TK_HOLD },
+  { "IGNORE", TK_IGNORE },
+  { "IMMEDIATE", TK_IMMEDIATE },
+  { "IN", TK_IN },
+  { "INDEX", TK_INDEX },
+  { "INITIALLY", TK_INITIALLY },
+  { "INNER", TK_JOIN_KW },
+  { "INSERT", TK_INSERT },
+  { "INSTEAD", TK_INSTEAD },
+  { "INT", TK_INT },
+  { "INTERSECT", TK_INTERSECT },
+  { "INTO", TK_INTO },
+  { "IS", TK_IS },
+  { "ISNULL", TK_ISNULL },
+  { "JOIN", TK_JOIN },
+  { "KEY", TK_KEY },
+  { "LEFT", TK_JOIN_KW },
+  { "LIKE", TK_LIKE },
+  { "LIMIT", TK_LIMIT },
+  { "LOCALIZABLE", TK_LOCALIZABLE },
+  { "LONG", TK_LONG },
+  { "LONGCHAR", TK_LONGCHAR },
+  { "MATCH", TK_MATCH },
+  { "NATURAL", TK_JOIN_KW },
+  { "NOT", TK_NOT },
+  { "NOTNULL", TK_NOTNULL },
+  { "NULL", TK_NULL },
+  { "OBJECT", TK_OBJECT },
+  { "OF", TK_OF },
+  { "OFFSET", TK_OFFSET },
+  { "ON", TK_ON },
+  { "OR", TK_OR },
+  { "ORDER", TK_ORDER },
+  { "OUTER", TK_JOIN_KW },
+  { "PRAGMA", TK_PRAGMA },
+  { "PRIMARY", TK_PRIMARY },
+  { "RAISE", TK_RAISE },
+  { "REFERENCES", TK_REFERENCES },
+  { "REPLACE", TK_REPLACE },
+  { "RESTRICT", TK_RESTRICT },
+  { "RIGHT", TK_JOIN_KW },
+  { "ROLLBACK", TK_ROLLBACK },
+  { "ROW", TK_ROW },
+  { "SELECT", TK_SELECT },
+  { "SET", TK_SET },
+  { "SHORT", TK_SHORT },
+  { "STATEMENT", TK_STATEMENT },
+  { "TABLE", TK_TABLE },
+  { "TEMP", TK_TEMP },
+  { "TEMPORARY", TK_TEMP },
+  { "THEN", TK_THEN },
+  { "TRANSACTION", TK_TRANSACTION },
+  { "TRIGGER", TK_TRIGGER },
+  { "UNION", TK_UNION },
+  { "UNIQUE", TK_UNIQUE },
+  { "UPDATE", TK_UPDATE },
+  { "USING", TK_USING },
+  { "VACUUM", TK_VACUUM },
+  { "VALUES", TK_VALUES },
+  { "VIEW", TK_VIEW },
+  { "WHEN", TK_WHEN },
+  { "WHERE", TK_WHERE },
+};
+
+#define KEYWORD_COUNT ( sizeof aKeywordTable/sizeof (Keyword) )
+
+/*
+** This function looks up an identifier to determine if it is a
+** keyword.  If it is a keyword, the token code of that keyword is 
+** returned.  If the input is not a keyword, TK_ID is returned.
+*/
+static int sqliteKeywordCode(const WCHAR *z, int n){
+  UINT i, len;
+  char buffer[0x10];
+
+  len = WideCharToMultiByte( CP_ACP, 0, z, n, buffer, sizeof buffer, NULL, NULL );
+  for(i=0; i<len; i++)
+      buffer[i] = toupper(buffer[i]);
+  for(i=0; i<KEYWORD_COUNT; i++)
+  {
+      if(memcmp(buffer, aKeywordTable[i].zName, len))
+          continue;
+      if(strlen(aKeywordTable[i].zName) == len )
+          return aKeywordTable[i].tokenType;
+  }
+  return TK_ID;
+}
+
+
+/*
+** If X is a character that can be used in an identifier then
+** isIdChar[X] will be 1.  Otherwise isIdChar[X] will be 0.
+**
+** In this implementation, an identifier can be a string of
+** alphabetic characters, digits, and "_" plus any character
+** with the high-order bit set.  The latter rule means that
+** any sequence of UTF-8 characters or characters taken from
+** an extended ISO8859 character set can form an identifier.
+*/
+static const char isIdChar[] = {
+/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 1x */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 2x */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,  /* 3x */
+    0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /* 4x */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,  /* 5x */
+    0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /* 6x */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,  /* 7x */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /* 8x */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /* 9x */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /* Ax */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /* Bx */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /* Cx */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /* Dx */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /* Ex */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /* Fx */
+};
+
+
+/*
+** Return the length of the token that begins at z[0].  Return
+** -1 if the token is (or might be) incomplete.  Store the token
+** type in *tokenType before returning.
+*/
+int sqliteGetToken(const WCHAR *z, int *tokenType){
+  int i;
+  switch( *z ){
+    case ' ': case '\t': case '\n': case '\f': case '\r': {
+      for(i=1; isspace(z[i]); i++){}
+      *tokenType = TK_SPACE;
+      return i;
+    }
+    case '-': {
+      if( z[1]==0 ) return -1;
+      if( z[1]=='-' ){
+        for(i=2; z[i] && z[i]!='\n'; i++){}
+        *tokenType = TK_COMMENT;
+        return i;
+      }
+      *tokenType = TK_MINUS;
+      return 1;
+    }
+    case '(': {
+      if( z[1]=='+' && z[2]==')' ){
+        *tokenType = TK_ORACLE_OUTER_JOIN;
+        return 3;
+      }else{
+        *tokenType = TK_LP;
+        return 1;
+      }
+    }
+    case ')': {
+      *tokenType = TK_RP;
+      return 1;
+    }
+    case ';': {
+      *tokenType = TK_SEMI;
+      return 1;
+    }
+    case '+': {
+      *tokenType = TK_PLUS;
+      return 1;
+    }
+    case '*': {
+      *tokenType = TK_STAR;
+      return 1;
+    }
+    case '/': {
+      if( z[1]!='*' || z[2]==0 ){
+        *tokenType = TK_SLASH;
+        return 1;
+      }
+      for(i=3; z[i] && (z[i]!='/' || z[i-1]!='*'); i++){}
+      if( z[i] ) i++;
+      *tokenType = TK_COMMENT;
+      return i;
+    }
+    case '%': {
+      *tokenType = TK_REM;
+      return 1;
+    }
+    case '=': {
+      *tokenType = TK_EQ;
+      return 1 + (z[1]=='=');
+    }
+    case '<': {
+      if( z[1]=='=' ){
+        *tokenType = TK_LE;
+        return 2;
+      }else if( z[1]=='>' ){
+        *tokenType = TK_NE;
+        return 2;
+      }else if( z[1]=='<' ){
+        *tokenType = TK_LSHIFT;
+        return 2;
+      }else{
+        *tokenType = TK_LT;
+        return 1;
+      }
+    }
+    case '>': {
+      if( z[1]=='=' ){
+        *tokenType = TK_GE;
+        return 2;
+      }else if( z[1]=='>' ){
+        *tokenType = TK_RSHIFT;
+        return 2;
+      }else{
+        *tokenType = TK_GT;
+        return 1;
+      }
+    }
+    case '!': {
+      if( z[1]!='=' ){
+        *tokenType = TK_ILLEGAL;
+        return 2;
+      }else{
+        *tokenType = TK_NE;
+        return 2;
+      }
+    }
+    case '|': {
+      if( z[1]!='|' ){
+        *tokenType = TK_BITOR;
+        return 1;
+      }else{
+        *tokenType = TK_CONCAT;
+        return 2;
+      }
+    }
+    case '?': {
+      *tokenType = TK_WILDCARD;
+      return 1;
+    }
+    case ',': {
+      *tokenType = TK_COMMA;
+      return 1;
+    }
+    case '&': {
+      *tokenType = TK_BITAND;
+      return 1;
+    }
+    case '~': {
+      *tokenType = TK_BITNOT;
+      return 1;
+    }
+    case '`': case '\'': case '"': {
+      int delim = z[0];
+      for(i=1; z[i]; i++){
+        if( z[i]==delim ){
+          if( z[i+1]==delim ){
+            i++;
+          }else{
+            break;
+          }
+        }
+      }
+      if( z[i] ) i++;
+      if( delim == '`' )
+        *tokenType = TK_ID;
+      else
+        *tokenType = TK_STRING;
+      return i;
+    }
+    case '.': {
+      if( !isdigit(z[1]) ){
+        *tokenType = TK_DOT;
+        return 1;
+      }
+      /* Fall thru into the next case */
+    }
+    case '0': case '1': case '2': case '3': case '4':
+    case '5': case '6': case '7': case '8': case '9': {
+      *tokenType = TK_INTEGER;
+      for(i=1; isdigit(z[i]); i++){}
+      if( z[i]=='.' ){
+        i++;
+        while( isdigit(z[i]) ){ i++; }
+        *tokenType = TK_FLOAT;
+      }
+      if( (z[i]=='e' || z[i]=='E') &&
+           ( isdigit(z[i+1]) 
+            || ((z[i+1]=='+' || z[i+1]=='-') && isdigit(z[i+2]))
+           )
+      ){
+        i += 2;
+        while( isdigit(z[i]) ){ i++; }
+        *tokenType = TK_FLOAT;
+      }else if( z[0]=='.' ){
+        *tokenType = TK_FLOAT;
+      }
+      return i;
+    }
+    case '[': {
+      for(i=1; z[i] && z[i-1]!=']'; i++){}
+      *tokenType = TK_ID;
+      return i;
+    }
+    default: {
+      if( !isIdChar[*z] ){
+        break;
+      }
+      for(i=1; isIdChar[z[i]]; i++){}
+      *tokenType = sqliteKeywordCode(z, i);
+      return i;
+    }
+  }
+  *tokenType = TK_ILLEGAL;
+  return 1;
+}
diff --git a/reactos/dll/msi/update.c b/reactos/dll/msi/update.c
new file mode 100644 (file)
index 0000000..a36c519
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2004 Mike McCormack for CodeWeavers
+ *
+ * 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 receuved 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 <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "wine/debug.h"
+#include "msi.h"
+#include "msiquery.h"
+#include "objbase.h"
+#include "objidl.h"
+#include "msipriv.h"
+#include "winnls.h"
+
+#include "query.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msidb);
+
+
+/* below is the query interface to a table */
+
+typedef struct tagMSIUPDATEVIEW
+{
+    MSIVIEW          view;
+    MSIDATABASE     *db;
+    MSIVIEW         *wv;
+    column_info     *vals;
+} MSIUPDATEVIEW;
+
+static UINT UPDATE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
+{
+    MSIUPDATEVIEW *uv = (MSIUPDATEVIEW*)view;
+
+    TRACE("%p %d %d %p\n", uv, row, col, val );
+
+    return ERROR_FUNCTION_FAILED;
+}
+
+static UINT UPDATE_execute( struct tagMSIVIEW *view, MSIRECORD *record )
+{
+    MSIUPDATEVIEW *uv = (MSIUPDATEVIEW*)view;
+    UINT n, type, val, r, row, col_count = 0, row_count = 0;
+    MSIVIEW *wv;
+
+    TRACE("%p %p\n", uv, record );
+
+    if( !record )
+        return ERROR_FUNCTION_FAILED;
+
+    wv = uv->wv;
+    if( !wv )
+        return ERROR_FUNCTION_FAILED;
+
+    r = wv->ops->execute( wv, 0 );
+    TRACE("tv execute returned %x\n", r);
+    if( r )
+        return r;
+
+    r = wv->ops->get_dimensions( wv, &row_count, &col_count );
+    if( r )
+        goto err;
+
+    for( row = 0; row < row_count; row++ )
+    {
+        for( n = 1; n <= col_count; n++ )
+        {
+            r = wv->ops->get_column_info( wv, n, NULL, &type );
+            if( r )
+                break;
+
+            if( type & MSITYPE_STRING )
+            {
+                const WCHAR *str = MSI_RecordGetString( record, n );
+                val = msi_addstringW( uv->db->strings, 0, str, -1, 1 );
+            }
+            else
+            {
+                val = MSI_RecordGetInteger( record, n );
+                val |= 0x8000;
+            }
+            r = wv->ops->set_int( wv, row, n, val );
+            if( r )
+                break;
+        }
+    }
+
+err:
+    return ERROR_SUCCESS;
+}
+
+
+static UINT UPDATE_close( struct tagMSIVIEW *view )
+{
+    MSIUPDATEVIEW *uv = (MSIUPDATEVIEW*)view;
+    MSIVIEW *wv;
+
+    TRACE("%p\n", uv);
+
+    wv = uv->wv;
+    if( !wv )
+        return ERROR_FUNCTION_FAILED;
+
+    return wv->ops->close( wv );
+}
+
+static UINT UPDATE_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols )
+{
+    MSIUPDATEVIEW *uv = (MSIUPDATEVIEW*)view;
+    MSIVIEW *wv;
+
+    TRACE("%p %p %p\n", uv, rows, cols );
+
+    wv = uv->wv;
+    if( !wv )
+        return ERROR_FUNCTION_FAILED;
+
+    return wv->ops->get_dimensions( wv, rows, cols );
+}
+
+static UINT UPDATE_get_column_info( struct tagMSIVIEW *view,
+                UINT n, LPWSTR *name, UINT *type )
+{
+    MSIUPDATEVIEW *uv = (MSIUPDATEVIEW*)view;
+    MSIVIEW *wv;
+
+    TRACE("%p %d %p %p\n", uv, n, name, type );
+
+    wv = uv->wv;
+    if( !wv )
+        return ERROR_FUNCTION_FAILED;
+
+    return wv->ops->get_column_info( wv, n, name, type );
+}
+
+static UINT UPDATE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode,
+                MSIRECORD *rec )
+{
+    MSIUPDATEVIEW *uv = (MSIUPDATEVIEW*)view;
+
+    TRACE("%p %d %p\n", uv, eModifyMode, rec );
+
+    return ERROR_FUNCTION_FAILED;
+}
+
+static UINT UPDATE_delete( struct tagMSIVIEW *view )
+{
+    MSIUPDATEVIEW *uv = (MSIUPDATEVIEW*)view;
+    MSIVIEW *wv;
+
+    TRACE("%p\n", uv );
+
+    wv = uv->wv;
+    if( wv )
+        wv->ops->delete( wv );
+    msiobj_release( &uv->db->hdr );
+    msi_free( uv );
+
+    return ERROR_SUCCESS;
+}
+
+
+static MSIVIEWOPS update_ops =
+{
+    UPDATE_fetch_int,
+    NULL,
+    NULL,
+    NULL,
+    UPDATE_execute,
+    UPDATE_close,
+    UPDATE_get_dimensions,
+    UPDATE_get_column_info,
+    UPDATE_modify,
+    UPDATE_delete
+};
+
+UINT UPDATE_CreateView( MSIDATABASE *db, MSIVIEW **view, LPWSTR table,
+                        column_info *columns, struct expr *expr )
+{
+    MSIUPDATEVIEW *uv = NULL;
+    UINT r;
+    MSIVIEW *tv = NULL, *sv = NULL, *wv = NULL;
+
+    TRACE("%p\n", uv );
+
+    r = TABLE_CreateView( db, table, &tv );
+    if( r != ERROR_SUCCESS )
+        return r;
+
+    /* add conditions first */
+    r = WHERE_CreateView( db, &wv, tv, expr );
+    if( r != ERROR_SUCCESS )
+    {
+        tv->ops->delete( tv );
+        return r;
+    }
+    
+    /* then select the columns we want */
+    r = SELECT_CreateView( db, &sv, wv, columns );
+    if( r != ERROR_SUCCESS )
+    {
+        wv->ops->delete( wv );
+        return r;
+    }
+
+    uv = msi_alloc_zero( sizeof *uv );
+    if( !uv )
+        return ERROR_FUNCTION_FAILED;
+
+    /* fill the structure */
+    uv->view.ops = &update_ops;
+    msiobj_addref( &db->hdr );
+    uv->db = db;
+    uv->vals = columns;
+    uv->wv = sv;
+    *view = (MSIVIEW*) uv;
+
+    return ERROR_SUCCESS;
+}
diff --git a/reactos/dll/msi/upgrade.c b/reactos/dll/msi/upgrade.c
new file mode 100644 (file)
index 0000000..3851abb
--- /dev/null
@@ -0,0 +1,226 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2005 Aric Stewart for CodeWeavers
+ *
+ * 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
+ */
+
+/*
+ * Actions focused on in this module
+ *
+ * FindRelatedProducts
+ * MigrateFeatureStates (TODO)
+ * RemoveExistingProducts (TODO)
+ */
+
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "winreg.h"
+#include "wine/debug.h"
+#include "msidefs.h"
+#include "msipriv.h"
+#include "winuser.h"
+#include "action.h"
+#include "wine/unicode.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+extern const WCHAR szFindRelatedProducts[];
+extern const WCHAR szMigrateFeatureStates[];
+extern const WCHAR szRemoveExistingProducts[];
+
+static BOOL check_language(DWORD lang1, LPCWSTR lang2, DWORD attributes)
+{
+    DWORD langdword;
+
+    if (!lang2 || lang2[0]==0)
+        return TRUE;
+
+    langdword = atoiW(lang2);
+
+    if (attributes & msidbUpgradeAttributesLanguagesExclusive)
+        return (lang1 != langdword);
+    else
+        return (lang1 == langdword);
+}
+
+static void append_productcode(MSIPACKAGE* package, LPCWSTR action_property,
+                               LPCWSTR productid)
+{
+    LPWSTR prop;
+    LPWSTR newprop;
+    DWORD len;
+    static const WCHAR separator[] = {';',0};
+
+    prop = msi_dup_property(package, action_property );
+    if (prop)
+        len = strlenW(prop);
+    else
+        len = 0;
+
+    /*separator*/
+    len ++;
+
+    len += strlenW(productid);
+
+    /*null*/
+    len++;
+
+    newprop = msi_alloc( len*sizeof(WCHAR) );
+
+    if (prop)
+    {
+        strcpyW(newprop,prop);
+        strcatW(newprop,separator);
+    }
+    else
+        newprop[0] = 0;
+    strcatW(newprop,productid);
+
+    MSI_SetPropertyW(package, action_property, newprop);
+    TRACE("Found Related Product... %s now %s\n",debugstr_w(action_property),
+                    debugstr_w(newprop));
+    msi_free( prop );
+    msi_free( newprop );
+}
+
+static UINT ITERATE_FindRelatedProducts(MSIRECORD *rec, LPVOID param)
+{
+    MSIPACKAGE *package = (MSIPACKAGE*)param;
+    WCHAR product[GUID_SIZE];
+    DWORD index = 0;
+    DWORD attributes = 0;
+    DWORD sz = GUID_SIZE;
+    LPCWSTR upgrade_code;
+    HKEY hkey = 0;
+    UINT rc = ERROR_SUCCESS;
+    MSIRECORD *uirow;
+
+    upgrade_code = MSI_RecordGetString(rec,1);
+
+    rc = MSIREG_OpenUpgradeCodesKey(upgrade_code, &hkey, FALSE);
+    if (rc != ERROR_SUCCESS)
+        return ERROR_SUCCESS;
+
+    uirow = MSI_CreateRecord(1);
+    attributes = MSI_RecordGetInteger(rec,5);
+    
+    while (rc == ERROR_SUCCESS)
+    {
+        rc = RegEnumValueW(hkey, index, product, &sz, NULL, NULL, NULL, NULL);
+        TRACE("Looking at (%li) %s\n",index,debugstr_w(product));
+        if (rc == ERROR_SUCCESS)
+        {
+            WCHAR productid[GUID_SIZE];
+            LPCWSTR ver;
+            LPCWSTR language;
+            LPCWSTR action_property;
+            DWORD check = 0x00000000;
+            DWORD comp_ver = 0x00000000;
+            DWORD sz = 0x100;
+            HKEY hukey;
+            INT r;
+
+            unsquash_guid(product,productid);
+            rc = MSIREG_OpenUserProductsKey(productid, &hukey, FALSE);
+            if (rc != ERROR_SUCCESS)
+            {
+                rc = ERROR_SUCCESS;
+                index ++;
+                continue;
+            }
+          
+            sz = sizeof(DWORD);
+            RegQueryValueExW(hukey, INSTALLPROPERTY_VERSIONW, NULL, NULL,
+                    (LPBYTE)&check, &sz);
+            /* check min */
+            ver = MSI_RecordGetString(rec,2);
+            comp_ver = build_version_dword(ver);
+            r = check - comp_ver; 
+            if (r < 0 || (r == 0 && !(attributes &
+                                    msidbUpgradeAttributesVersionMinInclusive)))
+            {
+                RegCloseKey(hukey);
+                index ++;
+                continue;
+            }
+
+            /* check max */
+            ver = MSI_RecordGetString(rec,3);
+            comp_ver = build_version_dword(ver);
+            r = check - comp_ver;
+            if (r > 0 || (r == 0 && !(attributes & 
+                                    msidbUpgradeAttributesVersionMaxInclusive)))
+            {
+                RegCloseKey(hukey);
+                index ++;
+                continue;
+            }
+
+            /* check language*/
+            sz = sizeof(DWORD);
+            RegQueryValueExW(hukey, INSTALLPROPERTY_LANGUAGEW, NULL, NULL,
+                    (LPBYTE)&check, &sz);
+            RegCloseKey(hukey);
+            language = MSI_RecordGetString(rec,4);
+            TRACE("Checking languages 0x%lx and %s\n", check, 
+                            debugstr_w(language));
+            if (!check_language(check, language, attributes))
+            {
+                index ++;
+                continue;
+            }
+
+            action_property = MSI_RecordGetString(rec,7);
+            append_productcode(package,action_property,productid);
+            ui_actiondata(package,szFindRelatedProducts,uirow);
+        }
+        index ++;
+    }
+    RegCloseKey(hkey);
+    msiobj_release( &uirow->hdr);
+    
+    return ERROR_SUCCESS;
+}
+
+UINT ACTION_FindRelatedProducts(MSIPACKAGE *package)
+{
+    static const WCHAR Query[] = 
+        {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',
+         ' ','`','U','p','g','r','a','d','e','`',0};
+    UINT rc = ERROR_SUCCESS;
+    MSIQUERY *view;
+
+    if (check_unique_action(package,szFindRelatedProducts))
+    {
+        TRACE("Skipping FindRelatedProducts action: already done on client side\n");
+        return ERROR_SUCCESS;
+    }
+    else
+        register_unique_action(package,szFindRelatedProducts);
+
+    rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
+    if (rc != ERROR_SUCCESS)
+        return ERROR_SUCCESS;
+    
+    rc = MSI_IterateRecords(view, NULL, ITERATE_FindRelatedProducts, package);
+    msiobj_release(&view->hdr);
+    
+    return rc;
+}
diff --git a/reactos/dll/msi/version.rc b/reactos/dll/msi/version.rc
new file mode 100644 (file)
index 0000000..d5164a4
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2004 Christian Costa
+ *
+ * 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
+ */
+
+#define WINE_FILEDESCRIPTION_STR "Wine MSI dll"
+#define WINE_FILENAME_STR "msi.dll"
+#define WINE_FILEVERSION 3,1,4000,2435
+#define WINE_FILEVERSION_STR "3.1.4000.2435"
+#define WINE_PRODUCTVERSION 3,1,4000,2435
+#define WINE_PRODUCTVERSION_STR "3.1.4000.2435"
+
+#include "wine/wine_common_ver.rc"
diff --git a/reactos/dll/msi/where.c b/reactos/dll/msi/where.c
new file mode 100644 (file)
index 0000000..b11ebe7
--- /dev/null
@@ -0,0 +1,482 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2002 Mike McCormack for CodeWeavers
+ *
+ * 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 <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "wine/debug.h"
+#include "msi.h"
+#include "msiquery.h"
+#include "objbase.h"
+#include "objidl.h"
+#include "msipriv.h"
+#include "winnls.h"
+
+#include "query.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msidb);
+
+
+/* below is the query interface to a table */
+
+typedef struct tagMSIWHEREVIEW
+{
+    MSIVIEW        view;
+    MSIDATABASE   *db;
+    MSIVIEW       *table;
+    UINT           row_count;
+    UINT          *reorder;
+    struct expr   *cond;
+} MSIWHEREVIEW;
+
+static UINT WHERE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
+{
+    MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
+
+    TRACE("%p %d %d %p\n", wv, row, col, val );
+
+    if( !wv->table )
+        return ERROR_FUNCTION_FAILED;
+
+    if( row > wv->row_count )
+        return ERROR_NO_MORE_ITEMS;
+
+    row = wv->reorder[ row ];
+
+    return wv->table->ops->fetch_int( wv->table, row, col, val );
+}
+
+static UINT WHERE_fetch_stream( struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm )
+{
+    MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
+
+    TRACE("%p %d %d %p\n", wv, row, col, stm );
+
+    if( !wv->table )
+        return ERROR_FUNCTION_FAILED;
+
+    if( row > wv->row_count )
+        return ERROR_NO_MORE_ITEMS;
+
+    row = wv->reorder[ row ];
+
+    return wv->table->ops->fetch_stream( wv->table, row, col, stm );
+}
+
+static UINT WHERE_set_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT val )
+{
+    MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
+
+    TRACE("%p %d %d %04x\n", wv, row, col, val );
+
+    if( !wv->table )
+         return ERROR_FUNCTION_FAILED;
+    
+    if( row > wv->row_count )
+        return ERROR_NO_MORE_ITEMS;
+    
+    row = wv->reorder[ row ];
+    
+    return wv->table->ops->set_int( wv->table, row, col, val );
+}
+
+static UINT INT_evaluate( UINT lval, UINT op, UINT rval )
+{
+    switch( op )
+    {
+    case OP_EQ:
+        return ( lval == rval );
+    case OP_AND:
+        return ( lval && rval );
+    case OP_OR:
+        return ( lval || rval );
+    case OP_GT:
+        return ( lval > rval );
+    case OP_LT:
+        return ( lval < rval );
+    case OP_LE:
+        return ( lval <= rval );
+    case OP_GE:
+        return ( lval >= rval );
+    case OP_NE:
+        return ( lval != rval );
+    case OP_ISNULL:
+        return ( !lval );
+    case OP_NOTNULL:
+        return ( lval );
+    default:
+        ERR("Unknown operator %d\n", op );
+    }
+    return 0;
+}
+
+static const WCHAR *STRING_evaluate( string_table *st,
+              MSIVIEW *table, UINT row, struct expr *expr, MSIRECORD *record )
+{
+    UINT val = 0, r;
+
+    switch( expr->type )
+    {
+    case EXPR_COL_NUMBER_STRING:
+        r = table->ops->fetch_int( table, row, expr->u.col_number, &val );
+        if( r != ERROR_SUCCESS )
+            return NULL;
+        return msi_string_lookup_id( st, val );
+
+    case EXPR_SVAL:
+        return expr->u.sval;
+
+    case EXPR_WILDCARD:
+        return MSI_RecordGetString( record, 1 );
+
+    default:
+        ERR("Invalid expression type\n");
+        break;
+    }
+    return NULL;
+}
+
+static UINT STRCMP_Evaluate( string_table *st, MSIVIEW *table, UINT row, 
+                             struct expr *cond, UINT *val, MSIRECORD *record )
+{
+    int sr;
+    const WCHAR *l_str, *r_str;
+
+    l_str = STRING_evaluate( st, table, row, cond->u.expr.left, record );
+    r_str = STRING_evaluate( st, table, row, cond->u.expr.right, record );
+    if( l_str == r_str )
+        sr = 0;
+    else if( l_str && ! r_str )
+        sr = 1;
+    else if( r_str && ! l_str )
+        sr = -1;
+    else
+        sr = lstrcmpW( l_str, r_str );
+
+    *val = ( cond->u.expr.op == OP_EQ && ( sr == 0 ) ) ||
+           ( cond->u.expr.op == OP_LT && ( sr < 0 ) ) ||
+           ( cond->u.expr.op == OP_GT && ( sr > 0 ) );
+
+    return ERROR_SUCCESS;
+}
+
+static UINT WHERE_evaluate( MSIDATABASE *db, MSIVIEW *table, UINT row, 
+                             struct expr *cond, UINT *val, MSIRECORD *record )
+{
+    UINT r, lval, rval;
+
+    if( !cond )
+        return ERROR_SUCCESS;
+
+    switch( cond->type )
+    {
+    case EXPR_COL_NUMBER_STRING:
+    case EXPR_COL_NUMBER:
+        return table->ops->fetch_int( table, row, cond->u.col_number, val );
+
+    case EXPR_UVAL:
+        *val = cond->u.uval;
+        return ERROR_SUCCESS;
+
+    case EXPR_COMPLEX:
+        r = WHERE_evaluate( db, table, row, cond->u.expr.left, &lval, record );
+        if( r != ERROR_SUCCESS )
+            return r;
+        r = WHERE_evaluate( db, table, row, cond->u.expr.right, &rval, record );
+        if( r != ERROR_SUCCESS )
+            return r;
+        *val = INT_evaluate( lval, cond->u.expr.op, rval );
+        return ERROR_SUCCESS;
+
+    case EXPR_STRCMP:
+        return STRCMP_Evaluate( db->strings, table, row, cond, val, record );
+
+    case EXPR_WILDCARD:
+        *val = MSI_RecordGetInteger( record, 1 );
+        return ERROR_SUCCESS;
+
+    default:
+        ERR("Invalid expression type\n");
+        break;
+    } 
+
+    return ERROR_SUCCESS;
+
+}
+
+static UINT WHERE_execute( struct tagMSIVIEW *view, MSIRECORD *record )
+{
+    MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
+    UINT count = 0, r, val, i;
+    MSIVIEW *table = wv->table;
+
+    TRACE("%p %p\n", wv, record);
+
+    if( !table )
+         return ERROR_FUNCTION_FAILED;
+
+    r = table->ops->execute( table, record );
+    if( r != ERROR_SUCCESS )
+        return r;
+
+    r = table->ops->get_dimensions( table, &count, NULL );
+    if( r != ERROR_SUCCESS )
+        return r;
+
+    msi_free( wv->reorder );
+    wv->reorder = msi_alloc( count*sizeof(UINT) );
+    if( !wv->reorder )
+        return ERROR_FUNCTION_FAILED;
+
+    wv->row_count = 0;
+    for( i=0; i<count; i++ )
+    {
+        val = 0;
+        r = WHERE_evaluate( wv->db, table, i, wv->cond, &val, record );
+        if( r != ERROR_SUCCESS )
+            return r;
+        if( val )
+            wv->reorder[ wv->row_count ++ ] = i;
+    }
+
+    return ERROR_SUCCESS;
+}
+
+static UINT WHERE_close( struct tagMSIVIEW *view )
+{
+    MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
+
+    TRACE("%p\n", wv );
+
+    if( !wv->table )
+         return ERROR_FUNCTION_FAILED;
+
+    msi_free( wv->reorder );
+    wv->reorder = NULL;
+
+    return wv->table->ops->close( wv->table );
+}
+
+static UINT WHERE_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols )
+{
+    MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
+
+    TRACE("%p %p %p\n", wv, rows, cols );
+
+    if( !wv->table )
+         return ERROR_FUNCTION_FAILED;
+
+    if( rows )
+    {
+        if( !wv->reorder )
+            return ERROR_FUNCTION_FAILED;
+        *rows = wv->row_count;
+    }
+
+    return wv->table->ops->get_dimensions( wv->table, NULL, cols );
+}
+
+static UINT WHERE_get_column_info( struct tagMSIVIEW *view,
+                UINT n, LPWSTR *name, UINT *type )
+{
+    MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
+
+    TRACE("%p %d %p %p\n", wv, n, name, type );
+
+    if( !wv->table )
+         return ERROR_FUNCTION_FAILED;
+
+    return wv->table->ops->get_column_info( wv->table, n, name, type );
+}
+
+static UINT WHERE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode,
+                MSIRECORD *rec )
+{
+    MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
+
+    TRACE("%p %d %p\n", wv, eModifyMode, rec );
+
+    if( !wv->table )
+         return ERROR_FUNCTION_FAILED;
+
+    return wv->table->ops->modify( wv->table, eModifyMode, rec );
+}
+
+static UINT WHERE_delete( struct tagMSIVIEW *view )
+{
+    MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
+
+    TRACE("%p\n", wv );
+
+    if( wv->table )
+        wv->table->ops->delete( wv->table );
+    wv->table = 0;
+
+    msi_free( wv->reorder );
+    wv->reorder = NULL;
+    wv->row_count = 0;
+
+    msiobj_release( &wv->db->hdr );
+    msi_free( wv );
+
+    return ERROR_SUCCESS;
+}
+
+
+MSIVIEWOPS where_ops =
+{
+    WHERE_fetch_int,
+    WHERE_fetch_stream,
+    WHERE_set_int,
+    NULL,
+    WHERE_execute,
+    WHERE_close,
+    WHERE_get_dimensions,
+    WHERE_get_column_info,
+    WHERE_modify,
+    WHERE_delete
+};
+
+static UINT WHERE_VerifyCondition( MSIDATABASE *db, MSIVIEW *table, struct expr *cond,
+                                   UINT *valid )
+{
+    UINT r, val = 0;
+
+    switch( cond->type )
+    {
+    case EXPR_COLUMN:
+        r = VIEW_find_column( table, cond->u.column, &val );
+        if( r == ERROR_SUCCESS )
+        {
+            UINT type = 0;
+            r = table->ops->get_column_info( table, val, NULL, &type );
+            if( r == ERROR_SUCCESS )
+            {
+                if (type&MSITYPE_STRING)
+                    cond->type = EXPR_COL_NUMBER_STRING;
+                else
+                    cond->type = EXPR_COL_NUMBER;
+                cond->u.col_number = val;
+                *valid = 1;
+            }
+            else
+                *valid = 0;
+        }
+        else
+        {
+            *valid = 0;
+            ERR("Couldn't find column %s\n", debugstr_w( cond->u.column ) );
+        }
+        break;
+    case EXPR_COMPLEX:
+        r = WHERE_VerifyCondition( db, table, cond->u.expr.left, valid );
+        if( r != ERROR_SUCCESS )
+            return r;
+        if( !*valid )
+            return ERROR_SUCCESS;
+        r = WHERE_VerifyCondition( db, table, cond->u.expr.right, valid );
+        if( r != ERROR_SUCCESS )
+            return r;
+
+        /* check the type of the comparison */
+        if( ( cond->u.expr.left->type == EXPR_SVAL ) ||
+            ( cond->u.expr.left->type == EXPR_COL_NUMBER_STRING ) ||
+            ( cond->u.expr.right->type == EXPR_SVAL ) ||
+            ( cond->u.expr.right->type == EXPR_COL_NUMBER_STRING ) )
+        {
+            switch( cond->u.expr.op )
+            {
+            case OP_EQ:
+            case OP_GT:
+            case OP_LT:
+                break;
+            default:
+                *valid = FALSE;
+                return ERROR_INVALID_PARAMETER;
+            }
+
+            /* FIXME: check we're comparing a string to a column */
+
+            cond->type = EXPR_STRCMP;
+        }
+
+        break;
+    case EXPR_IVAL:
+        *valid = 1;
+        cond->type = EXPR_UVAL;
+        cond->u.uval = cond->u.ival + (1<<15);
+        break;
+    case EXPR_WILDCARD:
+        *valid = 1;
+        break;
+    case EXPR_SVAL:
+        *valid = 1;
+        break;
+    default:
+        ERR("Invalid expression type\n");
+        *valid = 0;
+        break;
+    } 
+
+    return ERROR_SUCCESS;
+}
+
+UINT WHERE_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table,
+                       struct expr *cond )
+{
+    MSIWHEREVIEW *wv = NULL;
+    UINT count = 0, r, valid = 0;
+
+    TRACE("%p\n", table );
+
+    r = table->ops->get_dimensions( table, NULL, &count );
+    if( r != ERROR_SUCCESS )
+    {
+        ERR("can't get table dimensions\n");
+        return r;
+    }
+
+    if( cond )
+    {
+        r = WHERE_VerifyCondition( db, table, cond, &valid );
+        if( r != ERROR_SUCCESS )
+            return r;
+        if( !valid )
+            return ERROR_FUNCTION_FAILED;
+    }
+
+    wv = msi_alloc_zero( sizeof *wv );
+    if( !wv )
+        return ERROR_FUNCTION_FAILED;
+    
+    /* fill the structure */
+    wv->view.ops = &where_ops;
+    msiobj_addref( &db->hdr );
+    wv->db = db;
+    wv->table = table;
+    wv->row_count = 0;
+    wv->reorder = NULL;
+    wv->cond = cond;
+    *view = (MSIVIEW*) wv;
+
+    return ERROR_SUCCESS;
+}
diff --git a/reactos/dll/msimg32/Makefile.in b/reactos/dll/msimg32/Makefile.in
new file mode 100644 (file)
index 0000000..3d097d3
--- /dev/null
@@ -0,0 +1,13 @@
+TOPSRCDIR = @top_srcdir@
+TOPOBJDIR = ../..
+SRCDIR    = @srcdir@
+VPATH     = @srcdir@
+MODULE    = msimg32.dll
+IMPORTLIB = libmsimg32.$(IMPLIBEXT)
+IMPORTS   = gdi32 kernel32
+
+C_SRCS = msimg32_main.c
+
+@MAKE_DLL_RULES@
+
+### Dependencies:
diff --git a/reactos/dll/msimg32/msimg32.spec b/reactos/dll/msimg32/msimg32.spec
new file mode 100644 (file)
index 0000000..76b5075
--- /dev/null
@@ -0,0 +1,5 @@
+@ stdcall AlphaBlend(long long long long long long long long long long long) gdi32.GdiAlphaBlend
+@ stdcall -private DllInitialize(long long ptr) DllMain
+@ stdcall GradientFill(long ptr long ptr long long) gdi32.GdiGradientFill
+@ stdcall TransparentBlt(long long long long long long long long long long long) gdi32.GdiTransparentBlt
+@ stdcall vSetDdrawflag()
diff --git a/reactos/dll/msimg32/msimg32.xml b/reactos/dll/msimg32/msimg32.xml
new file mode 100644 (file)
index 0000000..7fd1e79
--- /dev/null
@@ -0,0 +1,12 @@
+<module name="msimg32" type="win32dll" baseaddress="${BASEADDRESS_MSIMG32}" installbase="system32" installname="msimg32.dll" allowwarnings="true">
+       <importlibrary definition="msimg32.spec.def" />
+       <include base="msimg32">.</include>
+       <include base="msimg32">include</include>
+       <define name="__REACTOS__" />
+       <define name="__USE_W32API" />
+       <library>ntdll</library>
+       <library>kernel32</library>
+       <library>gdi32</library>
+       <file>msimg32_main.c</file>
+       <file>msimg32.spec</file>
+</module>
diff --git a/reactos/dll/msimg32/msimg32_main.c b/reactos/dll/msimg32/msimg32_main.c
new file mode 100644 (file)
index 0000000..4586a30
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2002 Uwe Bonnes
+ *
+ * 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 <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winerror.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msimg32);
+
+/***********************************************************************
+ *           DllInitialize (MSIMG32.@)
+ *
+ * MSIMG32 initialisation routine.
+ */
+BOOL WINAPI DllMain( HINSTANCE inst, DWORD reason, LPVOID reserved )
+{
+    if (reason == DLL_PROCESS_ATTACH) DisableThreadLibraryCalls( inst );
+    return TRUE;
+}
+
+
+/******************************************************************************
+ *           vSetDdrawflag   (MSIMG32.@)
+ */
+void WINAPI vSetDdrawflag(void)
+{
+    static unsigned int vDrawflag=1;
+    FIXME("stub: vSetDrawFlag %u\n", vDrawflag);
+    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+}
diff --git a/reactos/dll/msvcrt/dllmain.c b/reactos/dll/msvcrt/dllmain.c
new file mode 100644 (file)
index 0000000..a2b30a7
--- /dev/null
@@ -0,0 +1,142 @@
+/* $Id$
+ *
+ * dllmain.c
+ *
+ * ReactOS MSVCRT.DLL Compatibility Library
+ *
+ *  THIS SOFTWARE IS NOT COPYRIGHTED
+ *
+ *  This source code is offered for use in the public domain. You may
+ *  use, modify or distribute it freely.
+ *
+ *  This code is distributed in the hope that it will be useful but
+ *  WITHOUT ANY WARRANTY. ALL WARRENTIES, EXPRESS OR IMPLIED ARE HEREBY
+ *  DISCLAMED. This includes but is not limited to warrenties of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Revision: 1.24 $
+ * $Author$
+ * $Date$
+ *
+ */
+
+#include <precomp.h>
+#include <internal/wine/msvcrt.h>
+
+#define NDEBUG
+#include <internal/debug.h>
+
+
+/* EXTERNAL PROTOTYPES ********************************************************/
+
+//void __fileno_init(void);
+extern BOOL __fileno_init(void);
+extern int BlockEnvToEnvironA(void);
+extern int BlockEnvToEnvironW(void);
+extern void FreeEnvironment(char **environment);
+extern void _atexit_cleanup(void);
+
+extern unsigned int _osver;
+extern unsigned int _winminor;
+extern unsigned int _winmajor;
+extern unsigned int _winver;
+
+extern char* _acmdln;        /* pointer to ascii command line */
+extern wchar_t* _wcmdln;     /* pointer to wide character command line */
+#undef _environ
+extern char** _environ;      /* pointer to environment block */
+extern char** __initenv;     /* pointer to initial environment block */
+extern wchar_t** _wenviron;  /* pointer to environment block */
+extern wchar_t** __winitenv; /* pointer to initial environment block */
+
+
+/* LIBRARY GLOBAL VARIABLES ***************************************************/
+
+HANDLE hHeap = NULL;        /* handle for heap */
+
+
+/* LIBRARY ENTRY POINT ********************************************************/
+
+BOOL
+STDCALL
+DllMain(PVOID hinstDll, ULONG dwReason, PVOID reserved)
+{
+    switch (dwReason)
+    {
+    case DLL_PROCESS_ATTACH://1
+        /* initialize version info */
+        //DPRINT1("Process Attach %d\n", nAttachCount);
+        //DPRINT1("Process Attach\n");
+        _osver = GetVersion();
+        _winmajor = (_osver >> 8) & 0xFF;
+        _winminor = _osver & 0xFF;
+        _winver = (_winmajor << 8) + _winminor;
+        _osver = (_osver >> 16) & 0xFFFF;
+        hHeap = HeapCreate(0, 100000, 0);
+        if (hHeap == NULL)
+            return FALSE;
+        if (!__fileno_init()) 
+            return FALSE;
+
+        /* create tls stuff */
+        if (!CreateThreadData())
+            return FALSE;
+
+        if (BlockEnvToEnvironA() < 0)
+            return FALSE;
+
+        if (BlockEnvToEnvironW() < 0)
+        {
+            FreeEnvironment(_environ);
+            return FALSE;
+        }
+
+        _acmdln = _strdup(GetCommandLineA());
+        _wcmdln = _wcsdup(GetCommandLineW());
+
+        /* FIXME: more initializations... */
+
+        /* FIXME: Initialization of the WINE code */
+        msvcrt_init_mt_locks();
+
+        DPRINT("Attach done\n");
+        break;
+
+    case DLL_THREAD_ATTACH://2
+        break;
+
+    case DLL_THREAD_DETACH://4
+        FreeThreadData(NULL);
+        break;
+
+    case DLL_PROCESS_DETACH://0
+        //DPRINT1("Detach %d\n", nAttachCount);
+        //DPRINT("Detach\n");
+        /* FIXME: more cleanup... */
+        _fcloseall();
+        _atexit_cleanup();
+
+        /* destroy tls stuff */
+        DestroyThreadData();
+
+       if (__winitenv && __winitenv != _wenviron)
+            FreeEnvironment((char**)__winitenv);
+        if (_wenviron)
+            FreeEnvironment((char**)_wenviron);
+
+       if (__initenv && __initenv != _environ)
+            FreeEnvironment(__initenv);
+        if (_environ)
+            FreeEnvironment(_environ);
+
+        /* destroy heap */
+        HeapDestroy(hHeap);
+
+        DPRINT("Detach done\n");
+        break;
+    }
+
+    return TRUE;
+}
+
+/* EOF */
diff --git a/reactos/dll/msvcrt/msvcrt.def b/reactos/dll/msvcrt/msvcrt.def
new file mode 100644 (file)
index 0000000..a19d38d
--- /dev/null
@@ -0,0 +1,846 @@
+; $Id$
+;
+; ReactOS MSVCRT Compatibility Library
+;
+LIBRARY MSVCRT.DLL
+EXPORTS
+;----------------------------------------------------------------------
+;      C++ Mangled Symbols
+;----------------------------------------------------------------------
+$I10_OUTPUT=MSVCRT_I10_OUTPUT @1
+??0__non_rtti_object@@QAE@ABV0@@Z=__thiscall_MSVCRT___non_rtti_object_copy_ctor @2
+??0__non_rtti_object@@QAE@PBD@Z=__thiscall_MSVCRT___non_rtti_object_ctor @3
+??0bad_cast@@QAE@ABQBD@Z=__thiscall_MSVCRT_bad_cast_ctor @4
+??0bad_cast@@QAE@ABV0@@Z=__thiscall_MSVCRT_bad_cast_copy_ctor @5
+??0bad_typeid@@QAE@ABV0@@Z=__thiscall_MSVCRT_bad_typeid_copy_ctor @6
+??0bad_typeid@@QAE@PBD@Z=__thiscall_MSVCRT_bad_typeid_ctor @7
+??0exception@@QAE@ABQBD@Z=__thiscall_MSVCRT_exception_ctor @8
+??0exception@@QAE@ABV0@@Z=__thiscall_MSVCRT_exception_copy_ctor @9
+??0exception@@QAE@XZ=__thiscall_MSVCRT_exception_default_ctor @10
+??1__non_rtti_object@@UAE@XZ=__thiscall_MSVCRT___non_rtti_object_dtor @11
+??1bad_cast@@UAE@XZ=__thiscall_MSVCRT_bad_cast_dtor @12
+??1bad_typeid@@UAE@XZ=__thiscall_MSVCRT_bad_typeid_dtor @13
+??1exception@@UAE@XZ=__thiscall_MSVCRT_exception_dtor @14
+??1type_info@@UAE@XZ=__thiscall_MSVCRT_type_info_dtor @15
+??2@YAPAXI@Z=MSVCRT_operator_new @16
+??_U@YAPAXI@Z=MSVCRT_operator_new @36
+??3@YAXPAX@Z=MSVCRT_operator_delete @17
+??_V@YAXPAX@Z=MSVCRT_operator_delete @37
+??4__non_rtti_object@@QAEAAV0@ABV0@@Z=__thiscall_MSVCRT___non_rtti_object_opequals @18
+??4bad_cast@@QAEAAV0@ABV0@@Z=__thiscall_MSVCRT_bad_cast_opequals @19
+??4bad_typeid@@QAEAAV0@ABV0@@Z=__thiscall_MSVCRT_bad_typeid_opequals @20
+??4exception@@QAEAAV0@ABV0@@Z=__thiscall_MSVCRT_exception_opequals @21
+??8type_info@@QBEHABV0@@Z=__thiscall_MSVCRT_type_info_opequals_equals @22
+??9type_info@@QBEHABV0@@Z=__thiscall_MSVCRT_type_info_opnot_equals @23
+??_7__non_rtti_object@@6B@=MSVCRT___non_rtti_object_vtable @24 DATA
+??_7bad_cast@@6B@=MSVCRT_bad_cast_vtable @25 DATA
+??_7bad_typeid@@6B@=MSVCRT_bad_typeid_vtable @26 DATA
+??_7exception@@6B@=MSVCRT_exception_vtable @27 DATA
+??_E__non_rtti_object@@UAEPAXI@Z=__thiscall_MSVCRT___non_rtti_object_vector_dtor @28
+??_Ebad_cast@@UAEPAXI@Z=__thiscall_MSVCRT_bad_cast_vector_dtor @29
+??_Ebad_typeid@@UAEPAXI@Z=__thiscall_MSVCRT_bad_typeid_vector_dtor @30
+??_Eexception@@UAEPAXI@Z=__thiscall_MSVCRT_exception_vector_dtor @31
+??_G__non_rtti_object@@UAEPAXI@Z=__thiscall_MSVCRT___non_rtti_object_scalar_dtor @32
+??_Gbad_cast@@UAEPAXI@Z=__thiscall_MSVCRT_bad_cast_scalar_dtor @33
+??_Gbad_typeid@@UAEPAXI@Z=__thiscall_MSVCRT_bad_typeid_scalar_dtor @34
+??_Gexception@@UAEPAXI@Z=__thiscall_MSVCRT_exception_scalar_dtor @35
+?_query_new_handler@@YAP6AHI@ZXZ=MSVCRT__query_new_handler @38
+?_query_new_mode@@YAHXZ=MSVCRT__query_new_mode @39
+?_set_new_handler@@YAP6AHI@ZP6AHI@Z@Z=MSVCRT__set_new_handler @40
+?_set_new_mode@@YAHH@Z=MSVCRT__set_new_mode @41
+?_set_se_translator@@YAP6AXIPAU_EXCEPTION_POINTERS@@@ZP6AXI0@Z@Z=MSVCRT__set_se_translator @42
+?before@type_info@@QBEHABV1@@Z=__thiscall_MSVCRT_type_info_before @43
+?raw_name@type_info@@QBEPBDXZ=__thiscall_MSVCRT_type_info_raw_name @45
+?set_new_handler@@YAP6AXXZP6AXXZ@Z=MSVCRT__set_new_handler @46
+?set_terminate@@YAP6AXXZP6AXXZ@Z=MSVCRT_set_terminate @47
+?set_unexpected@@YAP6AXXZP6AXXZ@Z=MSVCRT_set_unexpected @48
+?terminate@@YAXXZ=MSVCRT_terminate @49
+?unexpected@@YAXXZ=MSVCRT_unexpected @50
+?what@exception@@UBEPBDXZ=__thiscall_MSVCRT_what_exception @51
+??1type_info@@UAE@XZ=MSVCRT_type_info_dtor@4
+
+?name@type_info@@QBEPBDXZ=__thiscall_MSVCRT_type_info_name @44
+;??0bad_cast@@QAE@PBD@Z
+;??0bad_cast@@AAE@PBQBD@Z
+;??_Fbad_cast@@QAEXXZ
+;??_Fbad_typeid@@QAEXXZ
+
+;----------------------------------------------------------------------
+;      C Undecorated Symbols
+;----------------------------------------------------------------------
+_CIacos
+_CIasin
+_CIatan
+_CIatan2
+_CIcos
+_CIcosh
+_CIexp
+_CIfmod
+_CIlog
+_CIlog10
+_CIpow
+_CIsin
+_CIsinh
+_CIsqrt
+_CItan
+_CItanh
+_CxxThrowException
+_EH_prolog
+_Getdays
+_Getmonths
+_Gettnames
+_HUGE DATA
+_Strftime
+_XcptFilter
+__CxxCallUnwindDtor=stub
+__CxxDetectRethrow
+__CxxExceptionFilter=stub
+__CxxFrameHandler
+__CxxLongjmpUnwind=stub
+__CxxQueryExceptionSize
+__CxxRegisterExceptionObject=stub
+__CxxUnregisterExceptionObject=stub
+__DestructExceptionObject=stub
+__RTCastToVoid=MSVCRT___RTCastToVoid
+__RTDynamicCast=MSVCRT___RTDynamicCast
+__RTtypeid=MSVCRT___RTtypeid
+__STRINGTOLD=stub
+__argc DATA
+__argv DATA
+__badioinfo DATA
+__crtCompareStringA
+__crtGetLocaleInfoW
+__crtLCMapStringA
+__dllonexit
+__doserrno
+__fpecode
+__getmainargs
+__initenv DATA
+__isascii=NTDLL.__isascii
+__iscsym=NTDLL.__iscsym
+__iscsymf=NTDLL.__iscsymf
+__lc_codepage
+__lc_collate_cp
+__lc_handle
+__lconv_init
+__mb_cur_max DATA
+__p___argc
+__p___argv
+__p___wargv
+__p___initenv
+__p___mb_cur_max
+__p___winitenv
+__p__acmdln
+__p__amblksiz
+__p__commode
+__p__daylight
+__p__dstbias
+__p__environ
+__p__fileinfo=stub
+__p__fmode
+__p__iob
+__p__mbcasemap=stub
+__p__mbctype=stub
+__p__osver
+__p__pctype
+__p__pgmptr
+__p__pwctype
+__p__timezone
+__p__tzname=stub
+__p__wcmdln
+__p__wenviron
+__p__winmajor
+__p__winminor
+__p__winver
+__p__wpgmptr
+__pioinfo
+__pxcptinfoptrs
+__set_app_type
+__setlc_active DATA
+__setusermatherr
+__threadhandle
+__threadid
+__toascii=NTDLL.__toascii
+__unDName=stub
+__unDNameEx=stub
+__unguarded_readlc_active DATA
+__wargv
+__wgetmainargs
+__winitenv
+___lc_codepage_func=stub
+___lc_handle_func=stub
+___mb_cur_max_func=stub
+___setlc_active_func=stub
+___unguarded_readlc_active_add_func=stub
+__crtCompareStringW=stub
+__crtGetStringTypeW=stub
+__crtLCMapStringW=stub
+__iob_func=stub
+__pctype_func=stub
+__uncaught_exception=stub
+__wcserror=stub
+_abnormal_termination
+_access
+_acmdln DATA
+_adj_fdiv_m16i=stub
+_adj_fdiv_m32 = _adj_fdiv_m32@4
+_adj_fdiv_m32i = _adj_fdiv_m32i@4
+_adj_fdiv_m64 = _adj_fdiv_m64@8
+_adj_fdiv_r
+_adj_fdivr_m16i=stub
+_adj_fdivr_m32 = _adj_fdivr_m32@4
+_adj_fdivr_m32i = _adj_fdivr_m32i@4
+_adj_fdivr_m64 = _adj_fdivr_m64@8
+_adj_fpatan
+_adj_fprem=stub
+_adj_fprem1=stub
+_adj_fptan=stub
+_adjust_fdiv DATA
+_aexit_rtn
+_aligned_free=stub
+_aligned_malloc=stub
+_aligned_offset_malloc=stub
+_aligned_offset_realloc=stub
+_aligned_realloc=stub
+_amsg_exit
+_assert
+_atodbl=stub
+_atoi64=NTDLL._atoi64
+_atoldbl=stub
+_beep
+_beginthread
+_beginthreadex
+_c_exit
+_cabs
+_callnewh=stub
+_cexit
+_cgets
+_cgetws=stub
+_chdir
+_chdrive
+_chgsign
+_chkesp=stub
+_chmod
+_chsize
+_clearfp
+_close
+_commit
+_commode DATA
+_control87
+_controlfp
+_copysign
+_cprintf
+_cputs
+_cputws=stub
+_creat
+_cscanf
+_ctime64=stub
+_ctype DATA
+_cwait
+_cwprintf=stub
+_cwscanf=stub
+_daylight DATA
+_dstbias=stub
+_dup
+_dup2
+_ecvt
+_endthread
+_endthreadex
+_environ=stub
+_eof
+_errno
+_except_handler2
+_except_handler3
+_execl
+_execle
+_execlp
+_execlpe
+_execv
+_execve
+_execvp
+_execvpe
+_exit
+_expand
+_fcloseall
+_fcvt
+_fdopen
+_fgetchar
+_fgetwchar
+_filbuf
+_fileinfo=stub DATA
+_filelength
+_filelengthi64
+_fileno
+_findclose
+_findfirst
+_findfirst64=stub
+_findfirsti64
+_findnext
+_findnext64=stub
+_findnexti64
+_finite
+_flsbuf
+_flushall
+_fmode DATA
+_fpclass
+_fpieee_flt
+_fpreset
+_fputchar
+_fputwchar
+_fsopen
+_fstat
+_fstat64=stub
+_fstati64
+_ftime
+_ftime64=stub
+_ftol=NTDLL._ftol
+_fullpath
+_futime
+_futime64=stub
+_gcvt
+_get_osfhandle
+_get_sbh_threshold=stub
+_getch
+_getche
+_getcwd
+_getdcwd
+_getdiskfree
+_getdllprocaddr
+_getdrive
+_getdrives
+_get_heap_handle=stub
+_getmaxstdio=stub
+_getmbcp=stub
+_getpid
+_getsystime
+_getw
+_getwch=stub
+_getwche=stub
+_getws=stub
+_gmtime64=stub
+_global_unwind2
+_heapadd
+_heapchk
+_heapmin
+_heapset
+_heapused=stub
+_heapwalk
+_hypot
+_i64toa=NTDLL._i64toa
+_i64tow=NTDLL._i64tow
+_initterm
+_inp=stub
+_inpd=stub
+_inpw=stub
+_iob
+_isatty
+_isctype
+_ismbbalnum
+_ismbbalpha
+_ismbbgraph
+_ismbbkalnum
+_ismbbkana
+_ismbbkprint=stub
+_ismbbkpunct
+_ismbblead
+_ismbbprint
+_ismbbpunct
+_ismbbtrail
+_ismbcalnum
+_ismbcalpha
+_ismbcdigit
+_ismbcgraph=stub
+_ismbchira
+_ismbckata
+_ismbcl0
+_ismbcl1
+_ismbcl2
+_ismbclegal
+_ismbclower
+_ismbcprint
+_ismbcpunct=stub
+_ismbcspace
+_ismbcsymbol
+_ismbcupper
+_ismbslead
+_ismbstrail
+_isnan
+_itoa=NTDLL._itoa
+_itow=NTDLL._itow
+_j0
+_j1
+_jn
+_kbhit
+_localtime64=stub
+_lfind
+_loaddll
+_local_unwind2
+_lock
+_locking
+_logb
+_longjmpex=stub
+_lrotl
+_lrotr
+_lsearch
+_lseek
+_lseeki64
+_ltoa=NTDLL._ltoa
+_ltow
+_makepath
+_mbbtombc
+_mbbtype
+_mbcasemap=stub
+_mbccpy
+_mbcjistojms
+_mbcjmstojis
+_mbclen
+_mbctohira
+_mbctokata
+_mbctolower
+_mbctombb
+_mbctoupper
+_mbctype DATA
+_mbsbtype
+_mbscat
+_mbschr
+_mbscmp
+_mbscoll
+_mbscpy
+_mbscspn
+_mbsdec
+_mbsdup
+_mbsicmp
+_mbsicoll
+_mbsinc
+_mbslen
+_mbslwr
+_mbsnbcat
+_mbsnbcmp
+_mbsnbcnt
+_mbsnbcoll
+_mbsnbcpy
+_mbsnbicmp
+_mbsnbicoll
+_mbsnbset
+_mbsncat
+_mbsnccnt
+_mbsncmp
+_mbsncoll
+_mbsncpy
+_mbsnextc
+_mbsnicmp
+_mbsnicoll
+_mbsninc
+_mbsnset
+_mbspbrk
+_mbsrchr
+_mbsrev
+_mbsset
+_mbsspn
+_mbsspnp
+_mbsstr
+_mbstok
+_mbstrlen
+_mbsupr
+_memccpy
+_memicmp=NTDLL._memicmp
+_mkdir
+_mktemp
+_mktime64=stub
+_msize
+_nextafter
+_onexit
+_open
+_open_osfhandle
+_osver DATA
+_osplatform=stub
+_outp=stub
+_outpd=stub
+_outpw=stub
+_pclose
+_pctype DATA
+_pgmptr DATA
+_pipe
+_popen
+_purecall
+_putch
+_putenv
+_putw
+_putwch=stub
+_putws
+_pwctype DATA
+_read
+_resetstkoflw=stub
+_rmdir
+_rmtmp
+_rotl
+_rotr
+_safe_fdiv=stub
+_safe_fdivr=stub
+_safe_fprem=stub
+_safe_fprem1=stub
+_scalb
+_scprintf=stub
+_scwprintf=stub
+_searchenv
+_seh_longjmp_unwind=_seh_longjmp_unwind@4
+_set_error_mode=stub
+_set_SSE2_enable=stub
+_set_sbh_threshold=stub
+_seterrormode
+_setjmp
+_setjmp3
+_setmaxstdio=stub
+_setmbcp
+_setmode
+_setsystime
+_sleep
+_snprintf=crt__snprintf
+_snscanf=stub
+_snwscanf=stub
+_snwprintf=crt__snwprintf
+_sopen
+_spawnl
+_spawnle
+_spawnlp
+_spawnlpe
+_spawnv
+_spawnve
+_spawnvp
+_spawnvpe
+_splitpath=NTDLL._splitpath
+_stat
+_stat64=stub
+_stati64
+_statusfp
+_strcmpi
+_strdate
+_strdup
+_strerror
+_stricmp=NTDLL._stricmp
+_stricoll
+_strlwr=NTDLL._strlwr
+_strncoll
+_strnicmp=NTDLL._strnicmp
+_strnicoll
+_strnset
+_strrev
+_strset
+_strtime
+_strtoi64=stub
+_strtoui64=strtoull
+_strupr
+_swab
+_sys_errlist DATA
+_sys_nerr DATA
+_tell
+_telli64
+_tempnam
+_time64=stub
+_timezone DATA
+_tolower=NTDLL._tolower
+_toupper=NTDLL._toupper
+_tzname DATA
+_tzset
+_ui64toa=NTDLL._i64toa
+_ui64tow=NTDLL._i64tow
+_ultoa=NTDLL._ultoa
+_ultow=NTDLL._ultow
+_umask
+_ungetch
+_unlink
+_unloaddll
+_unlock
+_utime
+_utime64=stub
+_vscprintf=stub
+_vscwprintf=stub
+_vsnprintf
+_vsnwprintf
+_waccess
+_wasctime
+_wchdir
+_wchmod
+_wcmdln DATA
+_wcreat
+_wcsdup
+_wcserror=stub
+_wcsicmp
+_wcsicoll
+_wcslwr
+_wcsncoll
+_wcsnicmp
+_wcsnicoll
+_wcsnset
+_wcsrev
+_wcsset
+_wcstoi64=stub
+_wcstoui64=stub
+_wctime64=stub
+_wcsupr
+_wctime
+_wenviron=stub
+_wexecl
+_wexecle
+_wexeclp
+_wexeclpe
+_wexecv
+_wexecve
+_wexecvp
+_wexecvpe
+_wfdopen
+_wfindfirst
+_wfindfirst64=stub
+_wfindfirsti64
+_wfindnext
+_wfindnext64=stub
+_wfindnexti64
+_wfopen
+_wfreopen
+_wfsopen
+_wfullpath
+_wgetcwd
+_wgetdcwd
+_wgetenv
+_winmajor DATA
+_winminor DATA
+_winver DATA
+_wmakepath
+_wmkdir
+_wmktemp
+_wopen
+_wperror
+_wpgmptr=stub DATA
+_wpopen
+_wputenv
+_wremove
+_wrename
+_write
+_wrmdir
+_wsearchenv
+_wsetlocale=stub
+_wsopen
+_wspawnl
+_wspawnle
+_wspawnlp
+_wspawnlpe
+_wspawnv
+_wspawnve
+_wspawnvp
+_wspawnvpe
+_wsplitpath
+_wstat
+_wstat64=stub
+_wstati64
+_wstrdate
+_wstrtime
+_wsystem=stub
+_wtempnam
+_wtmpnam
+_wtof=stub
+_wtoi=NTDLL._wtoi
+_wtoi64=NTDLL._wtoi64
+_wtol
+_wunlink
+_wutime
+_wutime64=stub
+_y0
+_y1
+_yn
+abort
+abs=NTDLL.abs
+acos
+asctime
+asin
+atan=NTDLL.atan
+atan2
+atexit
+atof
+atoi=NTDLL.atoi
+atol=NTDLL.atol
+bsearch=NTDLL.bsearch
+calloc
+ceil=NTDLL.ceil
+clearerr
+clock
+cos=NTDLL.cos
+cosh
+ctime
+difftime
+div
+exit
+exp
+fabs=NTDLL.fabs
+fclose
+feof
+ferror
+fflush
+fgetc
+fgetpos
+fgets
+fgetwc
+fgetws
+floor=NTDLL.floor
+fmod
+fopen
+fprintf
+fputc
+fputs
+fputwc
+fputws
+fread
+free
+freopen
+frexp
+fscanf
+fseek
+fsetpos
+ftell
+fwprintf
+fwrite
+fwscanf
+getc
+getchar
+getenv
+gets
+getwc
+getwchar
+gmtime
+is_wctype
+isalnum=NTDLL.isalnum
+isalpha=NTDLL.isalpha
+iscntrl=NTDLL.iscntrl
+isdigit=NTDLL.isdigit
+isgraph=NTDLL.isgraph
+isleadbyte
+islower=NTDLL.islower
+isprint=NTDLL.isprint
+ispunct=NTDLL.ispunct
+isspace=NTDLL.isspace
+isupper=NTDLL.isupper
+iswalnum
+iswalpha=NTDLL.iswalpha
+iswascii
+iswcntrl
+iswctype=NTDLL.iswctype
+iswdigit=NTDLL.iswdigit
+iswgraph
+iswlower=NTDLL.iswlower
+iswprint
+iswpunct
+iswspace=NTDLL.iswspace
+iswupper
+iswxdigit=NTDLL.iswxdigit
+isxdigit=NTDLL.isxdigit
+labs=NTDLL.labs
+ldexp
+ldiv
+localeconv
+localtime
+log=NTDLL.log
+log10
+longjmp
+malloc
+mblen
+mbstowcs=NTDLL.mbstowcs
+mbtowc
+memchr
+memcmp
+memcpy=memmove
+memmove
+memset
+mktime
+modf
+perror
+pow=NTDLL.pow
+printf
+putc
+putchar
+puts
+putwc
+putwchar
+qsort=NTDLL.qsort
+raise
+rand
+realloc
+remove
+rename
+rewind
+scanf
+setbuf
+setlocale
+setvbuf
+signal
+sin=NTDLL.sin
+sinh
+sprintf=crt_sprintf
+sqrt=NTDLL.sqrt
+srand
+sscanf=crt_sscanf
+strcat
+strchr
+strcmp=NTDLL.strcmp
+strcoll
+strcpy
+strcspn
+strerror
+strftime
+strlen
+strncat
+strncmp
+strncpy
+strpbrk
+strrchr
+strspn
+strstr
+strtod
+strtok
+strtol=NTDLL.strtol
+strtoul
+strxfrm
+swprintf=crt_swprintf
+swscanf
+system
+tan=NTDLL.tan
+tanh
+time
+tmpfile
+tmpnam
+tolower
+toupper=NTDLL.toupper
+towlower=NTDLL.towlower
+towupper=NTDLL.towupper
+ungetc
+ungetwc
+vfprintf
+vfwprintf
+vprintf
+vsprintf
+vswprintf
+vwprintf
+wcscat
+wcschr
+wcscmp
+wcscoll
+wcscpy
+wcscspn
+wcsftime
+wcslen
+wcsncat
+wcsncmp
+wcsncpy
+wcspbrk
+wcsrchr
+wcsspn
+wcsstr
+wcstod
+wcstok
+wcstol=NTDLL.wcstol
+wcstombs
+wcstoul=NTDLL.wcstoul
+wcsxfrm
+wctomb
+wprintf
+wscanf
+
+; EOF
diff --git a/reactos/dll/msvcrt/msvcrt.rc b/reactos/dll/msvcrt/msvcrt.rc
new file mode 100644 (file)
index 0000000..46b222c
--- /dev/null
@@ -0,0 +1,5 @@
+#define REACTOS_VERSION_DLL
+#define REACTOS_STR_FILE_DESCRIPTION   "MSVCRT.DLL Compatibility Library\0"
+#define REACTOS_STR_INTERNAL_NAME      "msvcrt\0"
+#define REACTOS_STR_ORIGINAL_FILENAME  "msvcrt.dll\0"
+#include <reactos/version.rc>
diff --git a/reactos/dll/msvcrt/msvcrt.xml b/reactos/dll/msvcrt/msvcrt.xml
new file mode 100644 (file)
index 0000000..b2c4e1a
--- /dev/null
@@ -0,0 +1,25 @@
+<module name="msvcrt" type="win32dll" baseaddress="${BASEADDRESS_MSVCRT}" mangledsymbols="true" installbase="system32" installname="msvcrt.dll">
+       <linkerflag>-nostartfiles</linkerflag>
+       <linkerflag>--enable-stdcall-fixup</linkerflag>
+       <linkerflag>-nostdlib</linkerflag>
+       <linkerflag>-lgcc</linkerflag>
+       <importlibrary definition="msvcrt.def" />
+       <include base="msvcrt">.</include>
+       <include base="crt">include</include>
+       <define name="_DISABLE_TIDENTS" />
+       <define name="__USE_W32API" />
+       <define name="_WIN32_IE">0x600</define>
+       <define name="_WIN32_WINNT">0x501</define>
+       <define name="__REACTOS__" />
+       <define name="USE_MSVCRT_PREFIX" />
+       <define name="_MSVCRT_LIB_" />
+       <define name="_MT" />
+       <library>crt</library>
+       <library>string</library>
+       <library>kernel32</library>
+       <library>ntdll</library>
+       <library>wine</library>
+       <pch>precomp.h</pch>
+       <file>dllmain.c</file>
+       <file>msvcrt.rc</file>
+</module>
diff --git a/reactos/dll/msvcrt/precomp.h b/reactos/dll/msvcrt/precomp.h
new file mode 100644 (file)
index 0000000..acfc877
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef _CRT_PRECOMP_H
+#define _CRT_PRECOMP_H
+
+#include <stdio.h>
+#include <internal/tls.h>
+#include <stdlib.h>
+#include <windows.h>
+
+#endif /* _CRT_PRECOMP_H */
diff --git a/reactos/dll/msvcrt20/msvcrt20.c b/reactos/dll/msvcrt20/msvcrt20.c
new file mode 100644 (file)
index 0000000..18693a6
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * msvcrt20 implementation
+ *
+ * Copyright 2002 Alexandre Julliard
+ *
+ * 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 <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+
+extern void __getmainargs(int *argc, char** *argv, char** *envp,
+                          int expand_wildcards, int *new_mode);
+extern void __wgetmainargs(int *argc, WCHAR** *wargv, WCHAR** *wenvp,
+                           int expand_wildcards, int *new_mode);
+
+/*********************************************************************
+ *             __getmainargs (MSVCRT20.@)
+ *
+ * new_mode is not a pointer in msvcrt20.
+ */
+void MSVCRT20__getmainargs( int *argc, char** *argv, char** *envp,
+                            int expand_wildcards, int new_mode )
+{
+    __getmainargs( argc, argv, envp, expand_wildcards, &new_mode );
+}
+
+/*********************************************************************
+ *             __wgetmainargs (MSVCRT20.@)
+ *
+ * new_mode is not a pointer in msvcrt20.
+ */
+void MSVCRT20__wgetmainargs( int *argc, WCHAR** *wargv, WCHAR** *wenvp,
+                             int expand_wildcards, int new_mode )
+{
+    __wgetmainargs( argc, wargv, wenvp, expand_wildcards, &new_mode );
+}
diff --git a/reactos/dll/msvcrt20/msvcrt20.def b/reactos/dll/msvcrt20/msvcrt20.def
new file mode 100644 (file)
index 0000000..0fc2525
--- /dev/null
@@ -0,0 +1,708 @@
+; File generated automatically from msvcrt20/msvcrt20.spec; do not edit!
+
+LIBRARY msvcrt20.dll
+
+EXPORTS
+  ??2@YAPAXI@Z=msvcrt.??2@YAPAXI@Z @80
+  ??3@YAXPAX@Z=msvcrt.??3@YAXPAX@Z @81
+  ?_query_new_handler@@YAP6AHI@ZXZ=msvcrt.?_query_new_handler@@YAP6AHI@ZXZ @227
+  ?_query_new_mode@@YAHXZ=msvcrt.?_query_new_mode@@YAHXZ @228
+  ?_set_new_handler@@YAP6AHI@ZP6AHI@Z@Z=msvcrt.?_set_new_handler@@YAP6AHI@ZP6AHI@Z@Z @229
+  ?_set_new_mode@@YAHH@Z=msvcrt.?_set_new_mode@@YAHH@Z @230
+  ?_set_se_translator@@YAP6AXIPAU_EXCEPTION_POINTERS@@@ZP6AXI0@Z@Z=msvcrt.?_set_se_translator@@YAP6AXIPAU_EXCEPTION_POINTERS@@@ZP6AXI0@Z@Z @231
+  ?set_terminate@@YAP6AXXZP6AXXZ@Z=msvcrt.?set_terminate@@YAP6AXXZP6AXXZ@Z @366
+  ?set_unexpected@@YAP6AXXZP6AXXZ@Z=msvcrt.?set_unexpected@@YAP6AXXZP6AXXZ@Z @367
+  ?terminate@@YAXXZ=msvcrt.?terminate@@YAXXZ @410
+  ?unexpected@@YAXXZ=msvcrt.?unexpected@@YAXXZ @419
+  _CIacos=msvcrt._CIacos @439
+  _CIasin=msvcrt._CIasin @440
+  _CIatan=msvcrt._CIatan @441
+  _CIatan2=msvcrt._CIatan2 @442
+  _CIcos=msvcrt._CIcos @443
+  _CIcosh=msvcrt._CIcosh @444
+  _CIexp=msvcrt._CIexp @445
+  _CIfmod=msvcrt._CIfmod @446
+  _CIlog=msvcrt._CIlog @447
+  _CIlog10=msvcrt._CIlog10 @448
+  _CIpow=msvcrt._CIpow @449
+  _CIsin=msvcrt._CIsin @450
+  _CIsinh=msvcrt._CIsinh @451
+  _CIsqrt=msvcrt._CIsqrt @452
+  _CItan=msvcrt._CItan @453
+  _CItanh=msvcrt._CItanh @454
+  _CxxThrowException=msvcrt._CxxThrowException @455
+  _HUGE=msvcrt._HUGE @456 DATA
+  _XcptFilter=msvcrt._XcptFilter @457
+  __CxxFrameHandler=msvcrt.__CxxFrameHandler @458
+  __CxxLongjmpUnwind@4=msvcrt.__CxxLongjmpUnwind @459
+  __STRINGTOLD=msvcrt.__STRINGTOLD @460
+  __argc=msvcrt.__argc @461 DATA
+  __argv=msvcrt.__argv @462 DATA
+  __dllonexit=msvcrt.__dllonexit @463
+  __doserrno=msvcrt.__doserrno @464
+  __fpecode=msvcrt.__fpecode @465
+  __getmainargs=MSVCRT20__getmainargs @466
+  __initenv=msvcrt.__initenv @467 DATA
+  __isascii=msvcrt.__isascii @468
+  __iscsym=msvcrt.__iscsym @469
+  __iscsymf=msvcrt.__iscsymf @470
+  __lconv_init=msvcrt.__lconv_init @471
+  __mb_cur_max=msvcrt.__mb_cur_max @472 DATA
+  __p___argc=msvcrt.__p___argc @473
+  __p___argv=msvcrt.__p___argv @474
+  __p___initenv=msvcrt.__p___initenv @475
+  __p___mb_cur_max=msvcrt.__p___mb_cur_max @476
+  __p___wargv=msvcrt.__p___wargv @477
+  __p___winitenv=msvcrt.__p___winitenv @478
+  __p__acmdln=msvcrt.__p__acmdln @479
+  __p__amblksiz=msvcrt.__p__amblksiz @480
+  __p__commode=msvcrt.__p__commode @481
+  __p__daylight=msvcrt.__p__daylight @482
+  __p__environ=msvcrt.__p__environ @483
+  __p__fmode=msvcrt.__p__fmode @484
+  __p__iob=msvcrt.__p__iob @485
+  __p__mbctype=msvcrt.__p__mbctype @486
+  __p__osver=msvcrt.__p__osver @487
+  __p__pctype=msvcrt.__p__pctype @488
+  __p__pgmptr=msvcrt.__p__pgmptr @489
+  __p__pwctype=msvcrt.__p__pwctype @490
+  __p__timezone=msvcrt.__p__timezone @491
+  __p__tzname=msvcrt.__p__tzname @492
+  __p__wcmdln=msvcrt.__p__wcmdln @493
+  __p__wenviron=msvcrt.__p__wenviron @494
+  __p__winmajor=msvcrt.__p__winmajor @495
+  __p__winminor=msvcrt.__p__winminor @496
+  __p__winver=msvcrt.__p__winver @497
+  __p__wpgmptr=msvcrt.__p__wpgmptr @498
+  __pxcptinfoptrs=msvcrt.__pxcptinfoptrs @499
+  __threadhandle=msvcrt.__threadhandle @501
+  __threadid=msvcrt.__threadid @502
+  __toascii=msvcrt.__toascii @503
+  __wargv=msvcrt.__wargv @504 DATA
+  __wgetmainargs=MSVCRT20__wgetmainargs @505
+  __winitenv=msvcrt.__winitenv @506 DATA
+  _abnormal_termination=msvcrt._abnormal_termination @507
+  _access=msvcrt._access @508
+  _acmdln=msvcrt._acmdln @509 DATA
+  _adj_fdiv_m16i=msvcrt._adj_fdiv_m16i @510
+  _adj_fdiv_m32=msvcrt._adj_fdiv_m32 @511
+  _adj_fdiv_m32i=msvcrt._adj_fdiv_m32i @512
+  _adj_fdiv_m64=msvcrt._adj_fdiv_m64 @513
+  _adj_fdiv_r=msvcrt._adj_fdiv_r @514
+  _adj_fdivr_m16i=msvcrt._adj_fdivr_m16i @515
+  _adj_fdivr_m32=msvcrt._adj_fdivr_m32 @516
+  _adj_fdivr_m32i=msvcrt._adj_fdivr_m32i @517
+  _adj_fdivr_m64=msvcrt._adj_fdivr_m64 @518
+  _adj_fpatan=msvcrt._adj_fpatan @519
+  _adj_fprem=msvcrt._adj_fprem @520
+  _adj_fprem1=msvcrt._adj_fprem1 @521
+  _adj_fptan=msvcrt._adj_fptan @522
+  _adjust_fdiv=msvcrt._adjust_fdiv @523
+  _aexit_rtn=msvcrt._aexit_rtn @524 DATA
+  _amsg_exit=msvcrt._amsg_exit @525
+  _assert=msvcrt._assert @526
+  _atodbl=msvcrt._atodbl @527
+  _atoi64=msvcrt._atoi64 @528
+  _atoldbl=msvcrt._atoldbl @529
+  _beep=msvcrt._beep @530
+  _beginthread=msvcrt._beginthread @531
+  _beginthreadex=msvcrt._beginthreadex @532
+  _c_exit=msvcrt._c_exit @533
+  _cabs=msvcrt._cabs @534
+  _cexit=msvcrt._cexit @535
+  _cgets=msvcrt._cgets @536
+  _chdir=msvcrt._chdir @537
+  _chdrive=msvcrt._chdrive @538
+  _chgsign=msvcrt._chgsign @539
+  _chmod=msvcrt._chmod @540
+  _chsize=msvcrt._chsize @541
+  _clearfp=msvcrt._clearfp @542
+  _close=msvcrt._close @543
+  _commit=msvcrt._commit @544
+  _commode=msvcrt._commode @545 DATA
+  _control87=msvcrt._control87 @546
+  _controlfp=msvcrt._controlfp @547
+  _copysign=msvcrt._copysign @548
+  _cprintf=msvcrt._cprintf @549
+  _cputs=msvcrt._cputs @550
+  _creat=msvcrt._creat @551
+  _cscanf=msvcrt._cscanf @552
+  _ctype=msvcrt._ctype @553 DATA
+  _cwait=msvcrt._cwait @554
+  _daylight=msvcrt._daylight @555 DATA
+  _dup=msvcrt._dup @556
+  _dup2=msvcrt._dup2 @557
+  _ecvt=msvcrt._ecvt @558
+  _endthread=msvcrt._endthread @559
+  _endthreadex=msvcrt._endthreadex @560
+  _environ=msvcrt._environ @561 DATA
+  _eof=msvcrt._eof @562
+  _errno=msvcrt._errno @563
+  _except_handler2=msvcrt._except_handler2 @564
+  _except_handler3=msvcrt._except_handler3 @565
+  _execl=msvcrt._execl @566
+  _execle=msvcrt._execle @567
+  _execlp=msvcrt._execlp @568
+  _execlpe=msvcrt._execlpe @569
+  _execv=msvcrt._execv @570
+  _execve=msvcrt._execve @571
+  _execvp=msvcrt._execvp @572
+  _execvpe=msvcrt._execvpe @573
+  _exit=msvcrt._exit @574
+  _expand=msvcrt._expand @575
+  _fcloseall=msvcrt._fcloseall @576
+  _fcvt=msvcrt._fcvt @577
+  _fdopen=msvcrt._fdopen @578
+  _fgetchar=msvcrt._fgetchar @579
+  _fgetwchar=msvcrt._fgetwchar @580
+  _filbuf=msvcrt._filbuf @581
+  _fileinfo=msvcrt._fileinfo @582 DATA
+  _filelength=msvcrt._filelength @583
+  _fileno=msvcrt._fileno @584
+  _findclose=msvcrt._findclose @585
+  _findfirst=msvcrt._findfirst @586
+  _findnext=msvcrt._findnext @587
+  _finite=msvcrt._finite @588
+  _flsbuf=msvcrt._flsbuf @589
+  _flushall=msvcrt._flushall @590
+  _fmode=msvcrt._fmode @591 DATA
+  _fpclass=msvcrt._fpclass @592
+  _fpieee_flt=msvcrt._fpieee_flt @593
+  _fpreset=msvcrt._fpreset @594
+  _fputchar=msvcrt._fputchar @595
+  _fputwchar=msvcrt._fputwchar @596
+  _fsopen=msvcrt._fsopen @597
+  _fstat=msvcrt._fstat @598
+  _ftime=msvcrt._ftime @599
+  _ftol=msvcrt._ftol @600
+  _fullpath=msvcrt._fullpath @601
+  _futime=msvcrt._futime @602
+  _gcvt=msvcrt._gcvt @603
+  _get_osfhandle=msvcrt._get_osfhandle @604
+  _getch=msvcrt._getch @605
+  _getche=msvcrt._getche @606
+  _getcwd=msvcrt._getcwd @607
+  _getdcwd=msvcrt._getdcwd @608
+  _getdiskfree=msvcrt._getdiskfree @609
+  _getdllprocaddr=msvcrt._getdllprocaddr @610
+  _getdrive=msvcrt._getdrive @611
+  _getdrives=msvcrt._getdrives @612
+  _getmbcp=msvcrt._getmbcp @613
+  _getpid=msvcrt._getpid @614
+  _getsystime=msvcrt._getsystime @615
+  _getw=msvcrt._getw @616
+  _getws=msvcrt._getws @617
+  _global_unwind2=msvcrt._global_unwind2 @618
+  _heapadd=msvcrt._heapadd @619
+  _heapchk=msvcrt._heapchk @620
+  _heapmin=msvcrt._heapmin @621
+  _heapset=msvcrt._heapset @622
+  _heapused=msvcrt._heapused @623
+  _heapwalk=msvcrt._heapwalk @624
+  _hypot=msvcrt._hypot @625
+  _i64toa=msvcrt._i64toa @626
+  _i64tow=msvcrt._i64tow @627
+  _initterm=msvcrt._initterm @628
+  _iob=msvcrt._iob @629 DATA
+  _isatty=msvcrt._isatty @630
+  _isctype=msvcrt._isctype @631
+  _ismbbalnum=msvcrt._ismbbalnum @632
+  _ismbbalpha=msvcrt._ismbbalpha @633
+  _ismbbgraph=msvcrt._ismbbgraph @634
+  _ismbbkalnum=msvcrt._ismbbkalnum @635
+  _ismbbkana=msvcrt._ismbbkana @636
+  _ismbbkprint=msvcrt._ismbbkprint @637
+  _ismbbkpunct=msvcrt._ismbbkpunct @638
+  _ismbblead=msvcrt._ismbblead @639
+  _ismbbprint=msvcrt._ismbbprint @640
+  _ismbbpunct=msvcrt._ismbbpunct @641
+  _ismbbtrail=msvcrt._ismbbtrail @642
+  _ismbcalnum=msvcrt._ismbcalnum @643
+  _ismbcalpha=msvcrt._ismbcalpha @644
+  _ismbcdigit=msvcrt._ismbcdigit @645
+  _ismbcgraph=msvcrt._ismbcgraph @646
+  _ismbchira=msvcrt._ismbchira @647
+  _ismbckata=msvcrt._ismbckata @648
+  _ismbcl0=msvcrt._ismbcl0 @649
+  _ismbcl1=msvcrt._ismbcl1 @650
+  _ismbcl2=msvcrt._ismbcl2 @651
+  _ismbclegal=msvcrt._ismbclegal @652
+  _ismbclower=msvcrt._ismbclower @653
+  _ismbcprint=msvcrt._ismbcprint @654
+  _ismbcpunct=msvcrt._ismbcpunct @655
+  _ismbcspace=msvcrt._ismbcspace @656
+  _ismbcsymbol=msvcrt._ismbcsymbol @657
+  _ismbcupper=msvcrt._ismbcupper @658
+  _ismbslead=msvcrt._ismbslead @659
+  _ismbstrail=msvcrt._ismbstrail @660
+  _isnan=msvcrt._isnan @661
+  _itoa=msvcrt._itoa @662
+  _itow=msvcrt._itow @663
+  _j0=msvcrt._j0 @664
+  _j1=msvcrt._j1 @665
+  _jn=msvcrt._jn @666
+  _kbhit=msvcrt._kbhit @667
+  _lfind=msvcrt._lfind @668
+  _loaddll=msvcrt._loaddll @669
+  _local_unwind2=msvcrt._local_unwind2 @670
+  _locking=msvcrt._locking @671
+  _logb=msvcrt._logb @672
+  _longjmpex=msvcrt._longjmpex @673
+  _lrotl=msvcrt._lrotl @674
+  _lrotr=msvcrt._lrotr @675
+  _lsearch=msvcrt._lsearch @676
+  _lseek=msvcrt._lseek @677
+  _ltoa=msvcrt._ltoa @678
+  _ltow=msvcrt._ltow @679
+  _makepath=msvcrt._makepath @680
+  _matherr=msvcrt._matherr @681
+  _mbbtombc=msvcrt._mbbtombc @682
+  _mbbtype=msvcrt._mbbtype @683
+  _mbccpy=msvcrt._mbccpy @684
+  _mbcjistojms=msvcrt._mbcjistojms @685
+  _mbcjmstojis=msvcrt._mbcjmstojis @686
+  _mbclen=msvcrt._mbclen @687
+  _mbctohira=msvcrt._mbctohira @688
+  _mbctokata=msvcrt._mbctokata @689
+  _mbctolower=msvcrt._mbctolower @690
+  _mbctombb=msvcrt._mbctombb @691
+  _mbctoupper=msvcrt._mbctoupper @692
+  _mbctype=msvcrt._mbctype @693 DATA
+  _mbsbtype=msvcrt._mbsbtype @694
+  _mbscat=msvcrt._mbscat @695
+  _mbschr=msvcrt._mbschr @696
+  _mbscmp=msvcrt._mbscmp @697
+  _mbscoll=msvcrt._mbscoll @698
+  _mbscpy=msvcrt._mbscpy @699
+  _mbscspn=msvcrt._mbscspn @700
+  _mbsdec=msvcrt._mbsdec @701
+  _mbsdup=msvcrt._mbsdup @702
+  _mbsicmp=msvcrt._mbsicmp @703
+  _mbsicoll=msvcrt._mbsicoll @704
+  _mbsinc=msvcrt._mbsinc @705
+  _mbslen=msvcrt._mbslen @706
+  _mbslwr=msvcrt._mbslwr @707
+  _mbsnbcat=msvcrt._mbsnbcat @708
+  _mbsnbcmp=msvcrt._mbsnbcmp @709
+  _mbsnbcnt=msvcrt._mbsnbcnt @710
+  _mbsnbcoll=msvcrt._mbsnbcoll @711
+  _mbsnbcpy=msvcrt._mbsnbcpy @712
+  _mbsnbicmp=msvcrt._mbsnbicmp @713
+  _mbsnbicoll=msvcrt._mbsnbicoll @714
+  _mbsnbset=msvcrt._mbsnbset @715
+  _mbsncat=msvcrt._mbsncat @716
+  _mbsnccnt=msvcrt._mbsnccnt @717
+  _mbsncmp=msvcrt._mbsncmp @718
+  _mbsncoll=msvcrt._mbsncoll @719
+  _mbsncpy=msvcrt._mbsncpy @720
+  _mbsnextc=msvcrt._mbsnextc @721
+  _mbsnicmp=msvcrt._mbsnicmp @722
+  _mbsnicoll=msvcrt._mbsnicoll @723
+  _mbsninc=msvcrt._mbsninc @724
+  _mbsnset=msvcrt._mbsnset @725
+  _mbspbrk=msvcrt._mbspbrk @726
+  _mbsrchr=msvcrt._mbsrchr @727
+  _mbsrev=msvcrt._mbsrev @728
+  _mbsset=msvcrt._mbsset @729
+  _mbsspn=msvcrt._mbsspn @730
+  _mbsspnp=msvcrt._mbsspnp @731
+  _mbsstr=msvcrt._mbsstr @732
+  _mbstok=msvcrt._mbstok @733
+  _mbstrlen=msvcrt._mbstrlen @734
+  _mbsupr=msvcrt._mbsupr @735
+  _memccpy=msvcrt._memccpy @736
+  _memicmp=msvcrt._memicmp @737
+  _mkdir=msvcrt._mkdir @738
+  _mktemp=msvcrt._mktemp @739
+  _msize=msvcrt._msize @740
+  _nextafter=msvcrt._nextafter @743
+  _onexit=msvcrt._onexit @744
+  _open=msvcrt._open @745
+  _open_osfhandle=msvcrt._open_osfhandle @746
+  _osver=msvcrt._osver @747 DATA
+  _pclose=msvcrt._pclose @748
+  _pctype=msvcrt._pctype @749 DATA
+  _pgmptr=msvcrt._pgmptr @750 DATA
+  _pipe=msvcrt._pipe @751
+  _popen=msvcrt._popen @752
+  _purecall=msvcrt._purecall @753
+  _putch=msvcrt._putch @754
+  _putenv=msvcrt._putenv @755
+  _putw=msvcrt._putw @756
+  _putws=msvcrt._putws @757
+  _pwctype=msvcrt._pwctype @758 DATA
+  _read=msvcrt._read @759
+  _rmdir=msvcrt._rmdir @760
+  _rmtmp=msvcrt._rmtmp @761
+  _rotl=msvcrt._rotl @762
+  _rotr=msvcrt._rotr @763
+  _safe_fdiv=msvcrt._safe_fdiv @764
+  _safe_fdivr=msvcrt._safe_fdivr @765
+  _safe_fprem=msvcrt._safe_fprem @766
+  _safe_fprem1=msvcrt._safe_fprem1 @767
+  _scalb=msvcrt._scalb @768
+  _searchenv=msvcrt._searchenv @769
+  __seh_longjmp_unwind@4=msvcrt._seh_longjmp_unwind @500
+  _seterrormode=msvcrt._seterrormode @770
+  _setjmp=msvcrt._setjmp @771
+  _setjmp3=msvcrt._setjmp3 @772
+  _setmbcp=msvcrt._setmbcp @773
+  _setmode=msvcrt._setmode @774
+  _setsystime=msvcrt._setsystime @775
+  _sleep=msvcrt._sleep @776
+  _snprintf=msvcrt._snprintf @777
+  _snwprintf=msvcrt._snwprintf @778
+  _sopen=msvcrt._sopen @779
+  _spawnl=msvcrt._spawnl @780
+  _spawnle=msvcrt._spawnle @781
+  _spawnlp=msvcrt._spawnlp @782
+  _spawnlpe=msvcrt._spawnlpe @783
+  _spawnv=msvcrt._spawnv @784
+  _spawnve=msvcrt._spawnve @785
+  _spawnvp=msvcrt._spawnvp @786
+  _spawnvpe=msvcrt._spawnvpe @787
+  _splitpath=msvcrt._splitpath @788
+  _stat=msvcrt._stat @789
+  _statusfp=msvcrt._statusfp @790
+  _strcmpi=msvcrt._strcmpi @791
+  _strdate=msvcrt._strdate @792
+  _strdup=msvcrt._strdup @793
+  _strerror=msvcrt._strerror @794
+  _stricmp=msvcrt._stricmp @795
+  _stricoll=msvcrt._stricoll @796
+  _strlwr=msvcrt._strlwr @797
+  _strncoll=msvcrt._strncoll @798
+  _strnicmp=msvcrt._strnicmp @799
+  _strnicoll=msvcrt._strnicoll @800
+  _strnset=msvcrt._strnset @801
+  _strrev=msvcrt._strrev @802
+  _strset=msvcrt._strset @803
+  _strtime=msvcrt._strtime @804
+  _strupr=msvcrt._strupr @805
+  _swab=msvcrt._swab @806
+  _sys_errlist=msvcrt._sys_errlist @807 DATA
+  _sys_nerr=msvcrt._sys_nerr @808 DATA
+  _tccpy=msvcrt._mbccpy @809
+  _tclen=msvcrt._mbclen @810
+  _tcschr=msvcrt._mbschr @811
+  _tcsclen=msvcrt._mbslen @812
+  _tcscmp=msvcrt._mbscmp @813
+  _tcscspn=msvcrt._mbscspn @814
+  _tcsdec=msvcrt._mbsdec @815
+  _tcsicmp=msvcrt._mbsicmp @816
+  _tcsinc=msvcrt._mbsinc @817
+  _tcslwr=msvcrt._mbslwr @818
+  _tcsnbcnt=msvcrt._mbsnbcnt @819
+  _tcsncat=msvcrt._mbsnbcat @820
+  _tcsnccat=msvcrt._mbsncat @821
+  _tcsnccmp=msvcrt._mbsncmp @822
+  _tcsnccnt=msvcrt._mbsnccnt @823
+  _tcsnccpy=msvcrt._mbsncpy @824
+  _tcsncicmp=msvcrt._mbsnicmp @825
+  _tcsncmp=msvcrt._mbsnbcmp @826
+  _tcsncpy=msvcrt._mbsnbcpy @827
+  _tcsncset=msvcrt._mbsnset @828
+  _tcsnextc=msvcrt._mbsnextc @829
+  _tcsnicmp=msvcrt._mbsnbicmp @830
+  _tcsninc=msvcrt._mbsninc @831
+  _tcsnset=msvcrt._mbsnbset @832
+  _tcspbrk=msvcrt._mbspbrk @833
+  _tcsrchr=msvcrt._mbsrchr @834
+  _tcsrev=msvcrt._mbsrev @835
+  _tcsset=msvcrt._mbsset @836
+  _tcsspn=msvcrt._mbsspn @837
+  _tcsspnp=msvcrt._mbsspnp @838
+  _tcsstr=msvcrt._mbsstr @839
+  _tcstok=msvcrt._mbstok @840
+  _tcsupr=msvcrt._mbsupr @841
+  _tell=msvcrt._tell @842
+  _tempnam=msvcrt._tempnam @843
+  _timezone=msvcrt._timezone @844 DATA
+  _tolower=msvcrt._tolower @845
+  _toupper=msvcrt._toupper @846
+  _tzname=msvcrt._tzname @847 DATA
+  _tzset=msvcrt._tzset @848
+  _ui64toa=msvcrt._ui64toa @849
+  _ui64tow=msvcrt._ui64tow @850
+  _ultoa=msvcrt._ultoa @851
+  _ultow=msvcrt._ultow @852
+  _umask=msvcrt._umask @853
+  _ungetch=msvcrt._ungetch @854
+  _unlink=msvcrt._unlink @855
+  _unloaddll=msvcrt._unloaddll @856
+  _utime=msvcrt._utime @857
+  _vsnprintf=msvcrt._vsnprintf @858
+  _vsnwprintf=msvcrt._vsnwprintf @859
+  _waccess=msvcrt._waccess @860
+  _wasctime=msvcrt._wasctime @861
+  _wchdir=msvcrt._wchdir @862
+  _wchmod=msvcrt._wchmod @863
+  _wcmdln=msvcrt._wcmdln @864 DATA
+  _wcreat=msvcrt._wcreat @865
+  _wcsdup=msvcrt._wcsdup @866
+  _wcsicmp=msvcrt._wcsicmp @867
+  _wcsicoll=msvcrt._wcsicoll @868
+  _wcslwr=msvcrt._wcslwr @869
+  _wcsncoll=msvcrt._wcsncoll @870
+  _wcsnicmp=msvcrt._wcsnicmp @871
+  _wcsnicoll=msvcrt._wcsnicoll @872
+  _wcsnset=msvcrt._wcsnset @873
+  _wcsrev=msvcrt._wcsrev @874
+  _wcsset=msvcrt._wcsset @875
+  _wcsupr=msvcrt._wcsupr @876
+  _wctime=msvcrt._wctime @877
+  _wenviron=msvcrt._wenviron @878 DATA
+  _wexecl=msvcrt._wexecl @879
+  _wexecle=msvcrt._wexecle @880
+  _wexeclp=msvcrt._wexeclp @881
+  _wexeclpe=msvcrt._wexeclpe @882
+  _wexecv=msvcrt._wexecv @883
+  _wexecve=msvcrt._wexecve @884
+  _wexecvp=msvcrt._wexecvp @885
+  _wexecvpe=msvcrt._wexecvpe @886
+  _wfdopen=msvcrt._wfdopen @887
+  _wfindfirst=msvcrt._wfindfirst @888
+  _wfindnext=msvcrt._wfindnext @889
+  _wfopen=msvcrt._wfopen @890
+  _wfreopen=msvcrt._wfreopen @891
+  _wfsopen=msvcrt._wfsopen @892
+  _wfullpath=msvcrt._wfullpath @893
+  _wgetcwd=msvcrt._wgetcwd @894
+  _wgetdcwd=msvcrt._wgetdcwd @895
+  _wgetenv=msvcrt._wgetenv @896
+  _winmajor=msvcrt._winmajor @897 DATA
+  _winminor=msvcrt._winminor @898 DATA
+  _winver=msvcrt._winver @899 DATA
+  _wmakepath=msvcrt._wmakepath @900
+  _wmkdir=msvcrt._wmkdir @901
+  _wmktemp=msvcrt._wmktemp @902
+  _wopen=msvcrt._wopen @903
+  _wperror=msvcrt._wperror @904
+  _wpgmptr=msvcrt._wpgmptr @905 DATA
+  _wpopen=msvcrt._wpopen @906
+  _wputenv=msvcrt._wputenv @907
+  _wremove=msvcrt._wremove @908
+  _wrename=msvcrt._wrename @909
+  _write=msvcrt._write @910
+  _wrmdir=msvcrt._wrmdir @911
+  _wsearchenv=msvcrt._wsearchenv @912
+  _wsetlocale=msvcrt._wsetlocale @913
+  _wsopen=msvcrt._wsopen @914
+  _wspawnl=msvcrt._wspawnl @915
+  _wspawnle=msvcrt._wspawnle @916
+  _wspawnlp=msvcrt._wspawnlp @917
+  _wspawnlpe=msvcrt._wspawnlpe @918
+  _wspawnv=msvcrt._wspawnv @919
+  _wspawnve=msvcrt._wspawnve @920
+  _wspawnvp=msvcrt._wspawnvp @921
+  _wspawnvpe=msvcrt._wspawnvpe @922
+  _wsplitpath=msvcrt._wsplitpath @923
+  _wstat=msvcrt._wstat @924
+  _wstrdate=msvcrt._wstrdate @925
+  _wstrtime=msvcrt._wstrtime @926
+  _wsystem=msvcrt._wsystem @927
+  _wtempnam=msvcrt._wtempnam @928
+  _wtmpnam=msvcrt._wtmpnam @929
+  _wtoi=msvcrt._wtoi @930
+  _wtoi64=msvcrt._wtoi64 @931
+  _wtol=msvcrt._wtol @932
+  _wunlink=msvcrt._wunlink @933
+  _wutime=msvcrt._wutime @934
+  _y0=msvcrt._y0 @935
+  _y1=msvcrt._y1 @936
+  _yn=msvcrt._yn @937
+  abort=msvcrt.abort @938
+  abs=msvcrt.abs @939
+  acos=msvcrt.acos @940
+  asctime=msvcrt.asctime @941
+  asin=msvcrt.asin @942
+  atan=msvcrt.atan @943
+  atan2=msvcrt.atan2 @944
+  atexit=msvcrt.atexit @945
+  atof=msvcrt.atof @946
+  atoi=msvcrt.atoi @947
+  atol=msvcrt.atol @948
+  bsearch=msvcrt.bsearch @949
+  calloc=msvcrt.calloc @950
+  ceil=msvcrt.ceil @951
+  clearerr=msvcrt.clearerr @952
+  clock=msvcrt.clock @953
+  cos=msvcrt.cos @954
+  cosh=msvcrt.cosh @955
+  ctime=msvcrt.ctime @956
+  difftime=msvcrt.difftime @957
+  div=msvcrt.div @958
+  exit=msvcrt.exit @959
+  exp=msvcrt.exp @960
+  fabs=msvcrt.fabs @961
+  fclose=msvcrt.fclose @962
+  feof=msvcrt.feof @963
+  ferror=msvcrt.ferror @964
+  fflush=msvcrt.fflush @965
+  fgetc=msvcrt.fgetc @966
+  fgetpos=msvcrt.fgetpos @967
+  fgets=msvcrt.fgets @968
+  fgetwc=msvcrt.fgetwc @969
+  fgetws=msvcrt.fgetws @970
+  floor=msvcrt.floor @971
+  fmod=msvcrt.fmod @972
+  fopen=msvcrt.fopen @973
+  fprintf=msvcrt.fprintf @974
+  fputc=msvcrt.fputc @975
+  fputs=msvcrt.fputs @976
+  fputwc=msvcrt.fputwc @977
+  fputws=msvcrt.fputws @978
+  fread=msvcrt.fread @979
+  free=msvcrt.free @980
+  freopen=msvcrt.freopen @981
+  frexp=msvcrt.frexp @982
+  fscanf=msvcrt.fscanf @983
+  fseek=msvcrt.fseek @984
+  fsetpos=msvcrt.fsetpos @985
+  ftell=msvcrt.ftell @986
+  fwprintf=msvcrt.fwprintf @987
+  fwrite=msvcrt.fwrite @988
+  fwscanf=msvcrt.fwscanf @989
+  getc=msvcrt.getc @990
+  getchar=msvcrt.getchar @991
+  getenv=msvcrt.getenv @992
+  gets=msvcrt.gets @993
+  getwc=msvcrt.getwc @994
+  getwchar=msvcrt.getwchar @995
+  gmtime=msvcrt.gmtime @996
+  is_wctype=msvcrt.is_wctype @997
+  isalnum=msvcrt.isalnum @998
+  isalpha=msvcrt.isalpha @999
+  iscntrl=msvcrt.iscntrl @1000
+  isdigit=msvcrt.isdigit @1001
+  isgraph=msvcrt.isgraph @1002
+  isleadbyte=msvcrt.isleadbyte @1003
+  islower=msvcrt.islower @1004
+  isprint=msvcrt.isprint @1005
+  ispunct=msvcrt.ispunct @1006
+  isspace=msvcrt.isspace @1007
+  isupper=msvcrt.isupper @1008
+  iswalnum=msvcrt.iswalnum @1009
+  iswalpha=msvcrt.iswalpha @1010
+  iswascii=msvcrt.iswascii @1011
+  iswcntrl=msvcrt.iswcntrl @1012
+  iswctype=msvcrt.iswctype @1013
+  iswdigit=msvcrt.iswdigit @1014
+  iswgraph=msvcrt.iswgraph @1015
+  iswlower=msvcrt.iswlower @1016
+  iswprint=msvcrt.iswprint @1017
+  iswpunct=msvcrt.iswpunct @1018
+  iswspace=msvcrt.iswspace @1019
+  iswupper=msvcrt.iswupper @1020
+  iswxdigit=msvcrt.iswxdigit @1021
+  isxdigit=msvcrt.isxdigit @1022
+  labs=msvcrt.labs @1023
+  ldexp=msvcrt.ldexp @1024
+  ldiv=msvcrt.ldiv @1025
+  localeconv=msvcrt.localeconv @1026
+  localtime=msvcrt.localtime @1027
+  log=msvcrt.log @1028
+  log10=msvcrt.log10 @1029
+  longjmp=msvcrt.longjmp @1030
+  malloc=msvcrt.malloc @1031
+  mblen=msvcrt.mblen @1032
+  mbstowcs=msvcrt.mbstowcs @1033
+  mbtowc=msvcrt.mbtowc @1034
+  memchr=msvcrt.memchr @1035
+  memcmp=msvcrt.memcmp @1036
+  memcpy=msvcrt.memcpy @1037
+  memmove=msvcrt.memmove @1038
+  memset=msvcrt.memset @1039
+  mktime=msvcrt.mktime @1040
+  modf=msvcrt.modf @1041
+  perror=msvcrt.perror @1042
+  pow=msvcrt.pow @1043
+  printf=msvcrt.printf @1044
+  putc=msvcrt.putc @1045
+  putchar=msvcrt.putchar @1046
+  puts=msvcrt.puts @1047
+  putwc=msvcrt.putwc @1048
+  putwchar=msvcrt.putwchar @1049
+  qsort=msvcrt.qsort @1050
+  raise=msvcrt.raise @1051
+  rand=msvcrt.rand @1052
+  realloc=msvcrt.realloc @1053
+  remove=msvcrt.remove @1054
+  rename=msvcrt.rename @1055
+  rewind=msvcrt.rewind @1056
+  scanf=msvcrt.scanf @1057
+  setbuf=msvcrt.setbuf @1058
+  setlocale=msvcrt.setlocale @1059
+  setvbuf=msvcrt.setvbuf @1060
+  signal=msvcrt.signal @1061
+  sin=msvcrt.sin @1062
+  sinh=msvcrt.sinh @1063
+  sprintf=msvcrt.sprintf @1064
+  sqrt=msvcrt.sqrt @1065
+  srand=msvcrt.srand @1066
+  sscanf=msvcrt.sscanf @1067
+  strcat=msvcrt.strcat @1068
+  strchr=msvcrt.strchr @1069
+  strcmp=msvcrt.strcmp @1070
+  strcoll=msvcrt.strcoll @1071
+  strcpy=msvcrt.strcpy @1072
+  strcspn=msvcrt.strcspn @1073
+  strerror=msvcrt.strerror @1074
+  strftime=msvcrt.strftime @1075
+  strlen=msvcrt.strlen @1076
+  strncat=msvcrt.strncat @1077
+  strncmp=msvcrt.strncmp @1078
+  strncpy=msvcrt.strncpy @1079
+  strpbrk=msvcrt.strpbrk @1080
+  strrchr=msvcrt.strrchr @1081
+  strspn=msvcrt.strspn @1082
+  strstr=msvcrt.strstr @1083
+  strtod=msvcrt.strtod @1084
+  strtok=msvcrt.strtok @1085
+  strtol=msvcrt.strtol @1086
+  strtoul=msvcrt.strtoul @1087
+  strxfrm=msvcrt.strxfrm @1088
+  swprintf=msvcrt.swprintf @1089
+  swscanf=msvcrt.swscanf @1090
+  system=msvcrt.system @1091
+  tan=msvcrt.tan @1092
+  tanh=msvcrt.tanh @1093
+  time=msvcrt.time @1094
+  tmpfile=msvcrt.tmpfile @1095
+  tmpnam=msvcrt.tmpnam @1096
+  tolower=msvcrt.tolower @1097
+  toupper=msvcrt.toupper @1098
+  towlower=msvcrt.towlower @1099
+  towupper=msvcrt.towupper @1100
+  ungetc=msvcrt.ungetc @1101
+  ungetwc=msvcrt.ungetwc @1102
+  vfprintf=msvcrt.vfprintf @1103
+  vfwprintf=msvcrt.vfwprintf @1104
+  vprintf=msvcrt.vprintf @1105
+  vsprintf=msvcrt.vsprintf @1106
+  vswprintf=msvcrt.vswprintf @1107
+  vwprintf=msvcrt.vwprintf @1108
+  wcscat=msvcrt.wcscat @1109
+  wcschr=msvcrt.wcschr @1110
+  wcscmp=msvcrt.wcscmp @1111
+  wcscoll=msvcrt.wcscoll @1112
+  wcscpy=msvcrt.wcscpy @1113
+  wcscspn=msvcrt.wcscspn @1114
+  wcsftime=msvcrt.wcsftime @1115
+  wcslen=msvcrt.wcslen @1116
+  wcsncat=msvcrt.wcsncat @1117
+  wcsncmp=msvcrt.wcsncmp @1118
+  wcsncpy=msvcrt.wcsncpy @1119
+  wcspbrk=msvcrt.wcspbrk @1120
+  wcsrchr=msvcrt.wcsrchr @1121
+  wcsspn=msvcrt.wcsspn @1122
+  wcsstr=msvcrt.wcsstr @1123
+  wcstod=msvcrt.wcstod @1124
+  wcstok=msvcrt.wcstok @1125
+  wcstol=msvcrt.wcstol @1126
+  wcstombs=msvcrt.wcstombs @1127
+  wcstoul=msvcrt.wcstoul @1128
+  wcsxfrm=msvcrt.wcsxfrm @1129
+  wctomb=msvcrt.wctomb @1130
+  wprintf=msvcrt.wprintf @1131
+  wscanf=msvcrt.wscanf @1132
diff --git a/reactos/dll/msvcrt20/msvcrt20.rc b/reactos/dll/msvcrt20/msvcrt20.rc
new file mode 100644 (file)
index 0000000..514d146
--- /dev/null
@@ -0,0 +1,5 @@
+#define REACTOS_VERSION_DLL
+#define REACTOS_STR_FILE_DESCRIPTION   "MSVCRT20.DLL Compatibility Library\0"
+#define REACTOS_STR_INTERNAL_NAME      "msvcrt20\0"
+#define REACTOS_STR_ORIGINAL_FILENAME  "msvcrt20.dll\0"
+#include <reactos/version.rc>
diff --git a/reactos/dll/msvcrt20/msvcrt20.xml b/reactos/dll/msvcrt20/msvcrt20.xml
new file mode 100644 (file)
index 0000000..5d00b03
--- /dev/null
@@ -0,0 +1,17 @@
+<module name="msvcrt20" type="win32dll" baseaddress="${BASEADDRESS_MSVCRT20}" mangledsymbols="yes" installbase="system32" installname="msvcrt20.dll">
+       <importlibrary definition="msvcrt20.def" />
+       <include base="msvcrt20">.</include>
+       <define name="_DISABLE_TIDENTS" />
+       <define name="__USE_W32API" />
+       <define name="__REACTOS__" />
+       <define name="_WIN32_IE">0x600</define>
+       <define name="_WIN32_WINNT">0x501</define>
+       <define name="USE_MSVCRT_PREFIX" />
+       <define name="_MT" />
+       <library>wine</library>
+       <library>string</library>
+       <library>ntdll</library>
+       <library>kernel32</library>
+       <file>msvcrt20.c</file>
+       <file>msvcrt20.rc</file>
+</module>
diff --git a/reactos/dll/msvideo/Makefile.in b/reactos/dll/msvideo/Makefile.in
new file mode 100644 (file)
index 0000000..b52a912
--- /dev/null
@@ -0,0 +1,22 @@
+TOPSRCDIR = @top_srcdir@
+TOPOBJDIR = ../..
+SRCDIR    = @srcdir@
+VPATH     = @srcdir@
+MODULE    = msvfw32.dll
+IMPORTS   = winmm comctl32 version user32 gdi32 advapi32 kernel32 ntdll
+ALTNAMES  = msvideo.dll
+EXTRALIBS = $(LIBUNICODE)
+
+SPEC_SRCS16 = $(ALTNAMES:.dll=.spec)
+
+C_SRCS = \
+       mciwnd.c \
+       msvideo_main.c \
+       drawdib.c
+
+C_SRCS16 = \
+       msvideo16.c
+
+@MAKE_DLL_RULES@
+
+### Dependencies:
diff --git a/reactos/dll/msvideo/drawdib.c b/reactos/dll/msvideo/drawdib.c
new file mode 100644 (file)
index 0000000..fa4c92f
--- /dev/null
@@ -0,0 +1,463 @@
+/*
+ * Copyright 2000 Bradley Baetz
+ *
+ * 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
+ *
+ * FIXME: Some flags are ignored
+ *
+ * Handle palettes
+ */
+
+#include <string.h>
+#include "msvideo_private.h"
+
+#include "wingdi.h"
+#include "winuser.h"
+
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msvideo);
+
+typedef struct tagWINE_HDD {
+    HDC                 hdc;
+    INT                 dxDst;
+    INT                 dyDst;
+    LPBITMAPINFOHEADER  lpbi;
+    INT                 dxSrc;
+    INT                 dySrc;
+    HPALETTE            hpal;          /* Palette to use for the DIB */
+    BOOL                begun;         /* DrawDibBegin has been called */
+    LPBITMAPINFOHEADER  lpbiOut;       /* Output format */
+    HIC                 hic;           /* HIC for decompression */
+    HDC                 hMemDC;                /* DC for buffering */
+    HBITMAP             hOldDib;       /* Original Dib */
+    HBITMAP             hDib;          /* DibSection */
+    LPVOID              lpvbits;       /* Buffer for holding decompressed dib */
+    HDRAWDIB            hSelf;
+    struct tagWINE_HDD* next;
+} WINE_HDD;
+
+int num_colours(LPBITMAPINFOHEADER lpbi)
+{
+       if(lpbi->biClrUsed)
+               return lpbi->biClrUsed;
+       if(lpbi->biBitCount<=8)
+               return 1<<lpbi->biBitCount;
+       return 0;
+}
+
+static WINE_HDD*        HDD_FirstHdd /* = NULL */;
+
+static WINE_HDD*       MSVIDEO_GetHddPtr(HDRAWDIB hd)
+{
+    WINE_HDD*   hdd;
+
+    for (hdd = HDD_FirstHdd; hdd != NULL && hdd->hSelf != hd; hdd = hdd->next);
+    return hdd;
+}
+
+static DWORD HDD_HandleRef = 1;
+
+/***********************************************************************
+ *             DrawDibOpen             [MSVFW32.@]
+ */
+HDRAWDIB VFWAPI DrawDibOpen(void) 
+{
+    WINE_HDD*   whdd;
+
+    TRACE("(void)\n");
+       
+    whdd = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_HDD));
+    TRACE("=> %p\n", whdd);
+
+    while (MSVIDEO_GetHddPtr((HDRAWDIB)HDD_HandleRef) != NULL) HDD_HandleRef++;
+    whdd->hSelf = (HDRAWDIB)HDD_HandleRef++;
+
+    whdd->next = HDD_FirstHdd;
+    HDD_FirstHdd = whdd;
+
+    return whdd->hSelf;
+}
+
+/***********************************************************************
+ *             DrawDibClose            [MSVFW32.@]
+ */
+BOOL VFWAPI DrawDibClose(HDRAWDIB hdd) 
+{
+    WINE_HDD* whdd = MSVIDEO_GetHddPtr(hdd);
+    WINE_HDD** p;
+
+    TRACE("(%p)\n", hdd);
+
+    if (!whdd) return FALSE;
+
+    if (whdd->begun) DrawDibEnd(hdd);
+
+    for (p = &HDD_FirstHdd; *p != NULL; p = &((*p)->next))
+    {
+        if (*p == whdd)
+        {
+            *p = whdd->next;
+            break;
+        }
+    }
+
+    HeapFree(GetProcessHeap(), 0, whdd);
+
+    return TRUE;
+}
+
+/***********************************************************************
+ *             DrawDibEnd              [MSVFW32.@]
+ */
+BOOL VFWAPI DrawDibEnd(HDRAWDIB hdd) 
+{
+    BOOL ret = TRUE;
+    WINE_HDD *whdd = MSVIDEO_GetHddPtr(hdd);
+
+    TRACE("(%p)\n", hdd);
+
+    whdd->hpal = 0; /* Do not free this */
+    whdd->hdc = 0;
+    if (whdd->lpbi) 
+    {
+        HeapFree(GetProcessHeap(), 0, whdd->lpbi);
+        whdd->lpbi = NULL;
+    }
+    if (whdd->lpbiOut) 
+    {
+        HeapFree(GetProcessHeap(), 0, whdd->lpbiOut);
+        whdd->lpbiOut = NULL;
+    }
+
+    whdd->begun = FALSE;
+
+    /*if (whdd->lpvbits)
+      HeapFree(GetProcessHeap(), 0, whdd->lpvbuf);*/
+
+    if (whdd->hMemDC) 
+    {
+        SelectObject(whdd->hMemDC, whdd->hOldDib);
+        DeleteDC(whdd->hMemDC);
+        whdd->hMemDC = 0;
+    }
+
+    if (whdd->hDib) DeleteObject(whdd->hDib);
+    whdd->hDib = 0;
+
+    if (whdd->hic) 
+    {
+        ICDecompressEnd(whdd->hic);
+        ICClose(whdd->hic);
+        whdd->hic = 0;
+    }
+
+    whdd->lpvbits = NULL;
+
+    return ret;
+}
+
+/***********************************************************************
+ *              DrawDibBegin            [MSVFW32.@]
+ */
+BOOL VFWAPI DrawDibBegin(HDRAWDIB hdd,
+                         HDC      hdc,
+                         INT      dxDst,
+                         INT      dyDst,
+                         LPBITMAPINFOHEADER lpbi,
+                         INT      dxSrc,
+                         INT      dySrc,
+                         UINT     wFlags) 
+{
+    BOOL ret = TRUE;
+    WINE_HDD *whdd;
+
+    TRACE("(%p,%p,%d,%d,%p,%d,%d,0x%08lx)\n",
+          hdd, hdc, dxDst, dyDst, lpbi, dxSrc, dySrc, (DWORD)wFlags);
+
+    TRACE("lpbi: %ld,%ld/%ld,%d,%d,%ld,%ld,%ld,%ld,%ld,%ld\n",
+          lpbi->biSize, lpbi->biWidth, lpbi->biHeight, lpbi->biPlanes,
+          lpbi->biBitCount, lpbi->biCompression, lpbi->biSizeImage,
+          lpbi->biXPelsPerMeter, lpbi->biYPelsPerMeter, lpbi->biClrUsed,
+          lpbi->biClrImportant);
+
+    if (wFlags & ~(DDF_BUFFER))
+        FIXME("wFlags == 0x%08x not handled\n", wFlags & ~(DDF_BUFFER));
+
+    whdd = MSVIDEO_GetHddPtr(hdd);
+    if (!whdd) return FALSE;
+
+    if (whdd->begun) DrawDibEnd(hdd);
+
+    if (lpbi->biCompression) 
+    {
+        DWORD size = 0;
+
+        whdd->hic = ICOpen(ICTYPE_VIDEO, lpbi->biCompression, ICMODE_DECOMPRESS);
+        if (!whdd->hic) 
+        {
+            WARN("Could not open IC. biCompression == 0x%08lx\n", lpbi->biCompression);
+            ret = FALSE;
+        }
+
+        if (ret) 
+        {
+            size = ICDecompressGetFormat(whdd->hic, lpbi, NULL);
+            if (size == ICERR_UNSUPPORTED) 
+            {
+                WARN("Codec doesn't support GetFormat, giving up.\n");
+                ret = FALSE;
+            }
+        }
+
+        if (ret) 
+        {
+            whdd->lpbiOut = HeapAlloc(GetProcessHeap(), 0, size);
+
+            if (ICDecompressGetFormat(whdd->hic, lpbi, whdd->lpbiOut) != ICERR_OK)
+                ret = FALSE;
+        }
+
+        if (ret) 
+        {
+            /* FIXME: Use Ex functions if available? */
+            if (ICDecompressBegin(whdd->hic, lpbi, whdd->lpbiOut) != ICERR_OK)
+                ret = FALSE;
+            
+            TRACE("biSizeImage == %ld\n", whdd->lpbiOut->biSizeImage);
+            TRACE("biCompression == %ld\n", whdd->lpbiOut->biCompression);
+            TRACE("biBitCount == %d\n", whdd->lpbiOut->biBitCount);
+        }
+    }
+    else 
+    {
+        DWORD dwSize;
+        /* No compression */
+        TRACE("Not compressed!\n");
+        dwSize = lpbi->biSize + num_colours(lpbi)*sizeof(RGBQUAD);
+        whdd->lpbiOut = HeapAlloc(GetProcessHeap(), 0, dwSize);
+        memcpy(whdd->lpbiOut, lpbi, dwSize);
+    }
+
+    if (ret) 
+    {
+        /*whdd->lpvbuf = HeapAlloc(GetProcessHeap(), 0, whdd->lpbiOut->biSizeImage);*/
+
+        whdd->hMemDC = CreateCompatibleDC(hdc);
+        TRACE("Creating: %ld, %p\n", whdd->lpbiOut->biSize, whdd->lpvbits);
+        whdd->hDib = CreateDIBSection(whdd->hMemDC, (BITMAPINFO *)whdd->lpbiOut, DIB_RGB_COLORS, &(whdd->lpvbits), 0, 0);
+        if (!whdd->hDib) 
+        {
+            TRACE("Error: %ld\n", GetLastError());
+        }
+        TRACE("Created: %p,%p\n", whdd->hDib, whdd->lpvbits);
+        whdd->hOldDib = SelectObject(whdd->hMemDC, whdd->hDib);
+    }
+
+    if (ret) 
+    {
+        whdd->hdc = hdc;
+        whdd->dxDst = dxDst;
+        whdd->dyDst = dyDst;
+        whdd->lpbi = HeapAlloc(GetProcessHeap(), 0, lpbi->biSize);
+        memcpy(whdd->lpbi, lpbi, lpbi->biSize);
+        whdd->dxSrc = dxSrc;
+        whdd->dySrc = dySrc;
+        whdd->begun = TRUE;
+        whdd->hpal = 0;
+    } 
+    else 
+    {
+        if (whdd->hic)
+            ICClose(whdd->hic);
+        if (whdd->lpbiOut) 
+        {
+            HeapFree(GetProcessHeap(), 0, whdd->lpbiOut);
+            whdd->lpbiOut = NULL;
+        }
+    }
+
+    return ret;
+}
+
+/**********************************************************************
+ *             DrawDibDraw             [MSVFW32.@]
+ */
+BOOL VFWAPI DrawDibDraw(HDRAWDIB hdd, HDC hdc,
+                        INT xDst, INT yDst, INT dxDst, INT dyDst,
+                        LPBITMAPINFOHEADER lpbi,
+                        LPVOID lpBits,
+                        INT xSrc, INT ySrc, INT dxSrc, INT dySrc,
+                        UINT wFlags)
+{
+    WINE_HDD *whdd;
+    BOOL ret = TRUE;
+
+    TRACE("(%p,%p,%d,%d,%d,%d,%p,%p,%d,%d,%d,%d,0x%08lx)\n",
+          hdd, hdc, xDst, yDst, dxDst, dyDst, lpbi, lpBits, xSrc, ySrc, dxSrc, dySrc, (DWORD)wFlags);
+
+    whdd = MSVIDEO_GetHddPtr(hdd);
+    if (!whdd) return FALSE;
+
+    if (wFlags & ~(DDF_SAME_HDC | DDF_SAME_DRAW | DDF_NOTKEYFRAME | DDF_UPDATE | DDF_DONTDRAW))
+        FIXME("wFlags == 0x%08lx not handled\n", (DWORD)wFlags);
+
+    if (!lpBits) 
+    {
+        /* Undocumented? */
+        lpBits = (LPSTR)lpbi + (WORD)(lpbi->biSize) + (WORD)(num_colours(lpbi)*sizeof(RGBQUAD));
+    }
+
+
+#define CHANGED(x) (whdd->x != x)
+
+    if ((!whdd->begun) || 
+        (!(wFlags & DDF_SAME_HDC) && CHANGED(hdc)) || 
+        (!(wFlags & DDF_SAME_DRAW) && (CHANGED(lpbi) || CHANGED(dxSrc) || CHANGED(dySrc) || CHANGED(dxDst) || CHANGED(dyDst)))) 
+    {
+        TRACE("Something changed!\n");
+        ret = DrawDibBegin(hdd, hdc, dxDst, dyDst, lpbi, dxSrc, dySrc, 0);
+    }
+
+#undef CHANGED
+
+    if ((dxDst == -1) && (dyDst == -1)) 
+    {
+        dxDst = dxSrc;
+        dyDst = dySrc;
+    }
+
+    if (!(wFlags & DDF_UPDATE)) 
+    {
+        /* biSizeImage may be set to 0 for BI_RGB (uncompressed) bitmaps */
+        if ((lpbi->biCompression == BI_RGB) && (lpbi->biSizeImage == 0))
+            lpbi->biSizeImage = ((lpbi->biWidth * lpbi->biBitCount + 31) / 32) * 4 * lpbi->biHeight;
+
+        if (lpbi->biCompression) 
+        {
+            DWORD flags = 0;
+
+            TRACE("Compression == 0x%08lx\n", lpbi->biCompression);
+
+            if (wFlags & DDF_NOTKEYFRAME)
+                flags |= ICDECOMPRESS_NOTKEYFRAME;
+
+            ICDecompress(whdd->hic, flags, lpbi, lpBits, whdd->lpbiOut, whdd->lpvbits);
+        }
+        else
+        {
+            memcpy(whdd->lpvbits, lpBits, lpbi->biSizeImage);
+        }
+    }
+    if (!(wFlags & DDF_DONTDRAW) && whdd->hpal)
+        SelectPalette(hdc, whdd->hpal, FALSE);
+
+    if (!(StretchBlt(whdd->hdc, xDst, yDst, dxDst, dyDst, whdd->hMemDC, xSrc, ySrc, dxSrc, dySrc, SRCCOPY)))
+        ret = FALSE;
+    
+    return ret;
+}
+
+/*************************************************************************
+ *             DrawDibStart            [MSVFW32.@]
+ */
+BOOL VFWAPI DrawDibStart(HDRAWDIB hdd, DWORD rate) {
+       FIXME("(%p, %ld), stub\n", hdd, rate);
+       return TRUE;
+}
+
+/*************************************************************************
+ *             DrawDibStop             [MSVFW32.@]
+ */
+BOOL VFWAPI DrawDibStop(HDRAWDIB hdd) {
+       FIXME("(%p), stub\n", hdd);
+       return TRUE;
+}
+
+/***********************************************************************
+ *              DrawDibSetPalette       [MSVFW32.@]
+ */
+BOOL VFWAPI DrawDibSetPalette(HDRAWDIB hdd, HPALETTE hpal) 
+{
+    WINE_HDD *whdd;
+
+    TRACE("(%p, %p)\n", hdd, hpal);
+
+    whdd = MSVIDEO_GetHddPtr(hdd);
+    if (!whdd) return FALSE;
+
+    whdd->hpal = hpal;
+
+    if (whdd->begun) 
+    {
+        SelectPalette(whdd->hdc, hpal, 0);
+        RealizePalette(whdd->hdc);
+    }
+
+    return TRUE;
+}
+
+/***********************************************************************
+ *              DrawDibGetPalette       [MSVFW32.@]
+ */
+HPALETTE VFWAPI DrawDibGetPalette(HDRAWDIB hdd) 
+{
+    WINE_HDD *whdd;
+
+    TRACE("(%p)\n", hdd);
+
+    whdd = MSVIDEO_GetHddPtr(hdd);
+    if (!whdd) return FALSE;
+
+    return whdd->hpal;
+}
+
+/***********************************************************************
+ *              DrawDibRealize          [MSVFW32.@]
+ */
+UINT VFWAPI DrawDibRealize(HDRAWDIB hdd, HDC hdc, BOOL fBackground) 
+{
+    WINE_HDD *whdd;
+    HPALETTE oldPal;
+    UINT ret = 0;
+
+    FIXME("(%p, %p, %d), stub\n", hdd, hdc, fBackground);
+
+    whdd = MSVIDEO_GetHddPtr(hdd);
+    if (!whdd) return FALSE;
+
+    if (!whdd || !(whdd->begun)) 
+    {
+        ret = 0;
+        goto out;
+    }
+
+    if (!whdd->hpal)
+        whdd->hpal = CreateHalftonePalette(hdc);
+
+    oldPal = SelectPalette(hdc, whdd->hpal, fBackground);
+    ret = RealizePalette(hdc);
+
+ out:
+    TRACE("=> %u\n", ret);
+    return ret;
+}
+
+DWORD VFWAPI DrawDibProfileDisplay(LPBITMAPINFOHEADER lpbi)
+{
+    FIXME("(%p) stub\n", lpbi);
+
+    return PD_CAN_DRAW_DIB;
+}
diff --git a/reactos/dll/msvideo/mciwnd.c b/reactos/dll/msvideo/mciwnd.c
new file mode 100644 (file)
index 0000000..9260c01
--- /dev/null
@@ -0,0 +1,1419 @@
+/*
+ * Copyright 2000 Eric Pouech
+ * Copyright 2003 Dmitry Timoshkov
+ *
+ * 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
+ *
+ * FIXME:
+ * Add support for all remaining MCI_ commands and MCIWNDM_ messages.
+ * Add support for MCIWNDF_RECORD.
+ */
+
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winnls.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "winreg.h"
+#include "winternl.h"
+#include "vfw.h"
+#include "digitalv.h"
+#include "commctrl.h"
+#include "wine/unicode.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(mci);
+
+extern HMODULE MSVFW32_hModule;
+static const WCHAR mciWndClassW[] = {'M','C','I','W','n','d','C','l','a','s','s',0};
+
+typedef struct
+{
+    DWORD       dwStyle;
+    MCIDEVICEID mci;
+    HDRVR       hdrv;
+    int         alias;
+    UINT        dev_type;
+    UINT        mode;
+    long        position;
+    SIZE        size; /* size of the original frame rect */
+    int         zoom;
+    LPWSTR      lpName;
+    HWND        hWnd, hwndOwner;
+    UINT        uTimer;
+    MCIERROR    lasterror;
+    WCHAR       return_string[128];
+    WORD        active_timer, inactive_timer;
+} MCIWndInfo;
+
+static LRESULT WINAPI MCIWndProc(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam);
+
+#define CTL_PLAYSTOP    0x3200
+#define CTL_MENU        0x3201
+#define CTL_TRACKBAR    0x3202
+
+/***********************************************************************
+ *                MCIWndRegisterClass                [MSVFW32.@]
+ *
+ * NOTE: Native always uses its own hInstance
+ */
+BOOL VFWAPIV MCIWndRegisterClass(void)
+{
+    WNDCLASSW wc;
+
+    /* Since we are going to register a class belonging to MSVFW32
+     * and later we will create windows with a different hInstance
+     * CS_GLOBALCLASS is needed. And because the second attempt
+     * to register a global class will fail we need to test whether
+     * the class was already registered.
+     */
+    wc.style = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS | CS_OWNDC | CS_GLOBALCLASS;
+    wc.lpfnWndProc = MCIWndProc;
+    wc.cbClsExtra = 0;
+    wc.cbWndExtra = sizeof(MCIWndInfo*);
+    wc.hInstance = MSVFW32_hModule;
+    wc.hIcon = 0;
+    wc.hCursor = LoadCursorW(0, MAKEINTRESOURCEW(IDC_ARROW));
+    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
+    wc.lpszMenuName = NULL;
+    wc.lpszClassName = mciWndClassW;
+
+    if (RegisterClassW(&wc)) return TRUE;
+    if (GetLastError() == ERROR_CLASS_ALREADY_EXISTS) return TRUE;
+
+    return FALSE;
+}
+
+/***********************************************************************
+ *                MCIWndCreateW                                [MSVFW32.@]
+ */
+HWND VFWAPIV MCIWndCreateW(HWND hwndParent, HINSTANCE hInstance,
+                           DWORD dwStyle, LPCWSTR szFile)
+{
+    TRACE("%p %p %lx %s\n", hwndParent, hInstance, dwStyle, debugstr_w(szFile));
+
+    MCIWndRegisterClass();
+
+    if (!hInstance) hInstance = GetModuleHandleW(0);
+
+    if (hwndParent)
+        dwStyle |= WS_VISIBLE | WS_BORDER /*| WS_CHILD*/;
+    else
+        dwStyle |= WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX;
+
+    return CreateWindowExW(0, mciWndClassW, NULL,
+                           dwStyle | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
+                           0, 0, 300, 0,
+                           hwndParent, 0, hInstance, (LPVOID)szFile);
+}
+
+/***********************************************************************
+ *                MCIWndCreate                [MSVFW32.@]
+ *                MCIWndCreateA                [MSVFW32.@]
+ */
+HWND VFWAPIV MCIWndCreateA(HWND hwndParent, HINSTANCE hInstance,
+                           DWORD dwStyle, LPCSTR szFile)
+{
+    HWND ret;
+    UNICODE_STRING fileW;
+
+    if (szFile)
+        RtlCreateUnicodeStringFromAsciiz(&fileW, szFile);
+    else
+        fileW.Buffer = NULL;
+
+    ret = MCIWndCreateW(hwndParent, hInstance, dwStyle, fileW.Buffer);
+
+    RtlFreeUnicodeString(&fileW);
+    return ret;
+}
+
+static inline void MCIWND_notify_mode(MCIWndInfo *mwi)
+{
+    if (mwi->dwStyle & MCIWNDF_NOTIFYMODE)
+    {
+        UINT new_mode = SendMessageW(mwi->hWnd, MCIWNDM_GETMODEW, 0, 0);
+        if (new_mode != mwi->mode)
+        {
+            mwi->mode = new_mode;
+            SendMessageW(mwi->hwndOwner, MCIWNDM_NOTIFYMODE, (WPARAM)mwi->hWnd, new_mode);
+        }
+    }
+}
+
+static inline void MCIWND_notify_pos(MCIWndInfo *mwi)
+{
+    if (mwi->dwStyle & MCIWNDF_NOTIFYPOS)
+    {
+        long new_pos = SendMessageW(mwi->hWnd, MCIWNDM_GETPOSITIONW, 0, 0);
+        if (new_pos != mwi->position)
+        {
+            mwi->position = new_pos;
+            SendMessageW(mwi->hwndOwner, MCIWNDM_NOTIFYPOS, (WPARAM)mwi->hWnd, new_pos);
+        }
+    }
+}
+
+static inline void MCIWND_notify_size(MCIWndInfo *mwi)
+{
+    if (mwi->dwStyle & MCIWNDF_NOTIFYSIZE)
+        SendMessageW(mwi->hwndOwner, MCIWNDM_NOTIFYSIZE, (WPARAM)mwi->hWnd, 0);
+}
+
+static inline void MCIWND_notify_error(MCIWndInfo *mwi)
+{
+    if (mwi->dwStyle & MCIWNDF_NOTIFYERROR)
+        SendMessageW(mwi->hwndOwner, MCIWNDM_NOTIFYERROR, (WPARAM)mwi->hWnd, (LPARAM)mwi->lasterror);
+}
+
+static void MCIWND_UpdateState(MCIWndInfo *mwi)
+{
+    WCHAR buffer[1024];
+
+    if (!mwi->mci)
+    {
+        /* FIXME: get this from resources */
+        static const WCHAR no_deviceW[] = {'N','o',' ','D','e','v','i','c','e',0};
+        SetWindowTextW(mwi->hWnd, no_deviceW);
+        return;
+    }
+
+    MCIWND_notify_pos(mwi);
+
+    if (!(mwi->dwStyle & MCIWNDF_NOPLAYBAR))
+        SendDlgItemMessageW(mwi->hWnd, CTL_TRACKBAR, TBM_SETPOS, TRUE, mwi->position);
+
+    if (!(mwi->dwStyle & MCIWNDF_SHOWALL))
+        return;
+
+    if ((mwi->dwStyle & MCIWNDF_SHOWNAME) && mwi->lpName)
+        strcpyW(buffer, mwi->lpName);
+    else
+        *buffer = 0;
+
+    if (mwi->dwStyle & (MCIWNDF_SHOWPOS|MCIWNDF_SHOWMODE))
+    {
+        static const WCHAR spaceW[] = {' ',0};
+        static const WCHAR l_braceW[] = {'(',0};
+
+        if (*buffer) strcatW(buffer, spaceW);
+        strcatW(buffer, l_braceW);
+    }
+
+    if (mwi->dwStyle & MCIWNDF_SHOWPOS)
+    {
+        WCHAR posW[64];
+
+        posW[0] = 0;
+        SendMessageW(mwi->hWnd, MCIWNDM_GETPOSITIONW, 64, (LPARAM)posW);
+        strcatW(buffer, posW);
+    }
+
+    if ((mwi->dwStyle & (MCIWNDF_SHOWPOS|MCIWNDF_SHOWMODE)) == (MCIWNDF_SHOWPOS|MCIWNDF_SHOWMODE))
+    {
+        static const WCHAR dashW[] = {' ','-',' ',0};
+        strcatW(buffer, dashW);
+    }
+
+    if (mwi->dwStyle & MCIWNDF_SHOWMODE)
+    {
+        WCHAR modeW[64];
+
+        modeW[0] = 0;
+        SendMessageW(mwi->hWnd, MCIWNDM_GETMODEW, 64, (LPARAM)modeW);
+        strcatW(buffer, modeW);
+    }
+
+    if (mwi->dwStyle & (MCIWNDF_SHOWPOS|MCIWNDF_SHOWMODE))
+    {
+        static const WCHAR r_braceW[] = {')',0};
+        strcatW(buffer, r_braceW);
+    }
+
+    TRACE("=> '%s'\n", debugstr_w(buffer));
+    SetWindowTextW(mwi->hWnd, buffer);
+}
+
+static LRESULT MCIWND_Create(HWND hWnd, LPCREATESTRUCTW cs)
+{
+    HWND hChld;
+    MCIWndInfo *mwi;
+    static const WCHAR buttonW[] = {'b','u','t','t','o','n',0};
+
+    mwi = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*mwi));
+    if (!mwi) return -1;
+
+    SetWindowLongW(hWnd, 0, (LPARAM)mwi);
+
+    mwi->dwStyle = cs->style;
+    /* There is no need to show stats if there is no caption */
+    if ((mwi->dwStyle & WS_CAPTION) != WS_CAPTION)
+        mwi->dwStyle &= ~MCIWNDF_SHOWALL;
+
+    mwi->hWnd = hWnd;
+    mwi->hwndOwner = cs->hwndParent;
+    mwi->active_timer = 500;
+    mwi->inactive_timer = 2000;
+    mwi->mode = MCI_MODE_NOT_READY;
+    mwi->position = -1;
+    mwi->zoom = 100;
+
+    if (!(mwi->dwStyle & MCIWNDF_NOMENU))
+    {
+        static const WCHAR menuW[] = {'M','e','n','u',0};
+
+        hChld = CreateWindowExW(0, buttonW, menuW, WS_CHILD|WS_VISIBLE, 32, cs->cy, 32, 32,
+                                hWnd, (HMENU)CTL_MENU, cs->hInstance, 0L);
+        TRACE("Get Button2: %p\n", hChld);
+    }
+
+    if (!(mwi->dwStyle & MCIWNDF_NOPLAYBAR))
+    {
+        INITCOMMONCONTROLSEX init;
+        static const WCHAR playW[] = {'P','l','a','y',0};
+
+        /* adding the other elements: play/stop button, menu button, status */
+        hChld = CreateWindowExW(0, buttonW, playW, WS_CHILD|WS_VISIBLE, 0, cs->cy, 32, 32,
+                                hWnd, (HMENU)CTL_PLAYSTOP, cs->hInstance, 0L);
+        TRACE("Get Button1: %p\n", hChld);
+
+        init.dwSize = sizeof(init);
+        init.dwICC = ICC_BAR_CLASSES;
+        InitCommonControlsEx(&init);
+
+        hChld = CreateWindowExW(0, TRACKBAR_CLASSW, NULL, WS_CHILD|WS_VISIBLE, 64, cs->cy, cs->cx - 64, 32,
+                                hWnd, (HMENU)CTL_TRACKBAR, cs->hInstance, 0L);
+        TRACE("Get status: %p\n", hChld);
+    }
+
+    /* This sets the default window size */
+    SendMessageW(hWnd, MCI_CLOSE, 0, 0);
+
+    if (cs->lpCreateParams)
+    {
+        LPARAM lParam;
+
+        /* MCI wnd class is prepared to be embedded as an MDI child window */
+        if (cs->dwExStyle & WS_EX_MDICHILD)
+        {
+            MDICREATESTRUCTW *mdics = (MDICREATESTRUCTW *)cs->lpCreateParams;
+            lParam = mdics->lParam;
+        }
+        else
+            lParam = (LPARAM)cs->lpCreateParams;
+
+        /* If it's our internal class pointer, file name is a unicode string */
+        if (cs->lpszClass == mciWndClassW)
+            SendMessageW(hWnd, MCIWNDM_OPENW, 0, lParam);
+        else
+        {
+            /* Otherwise let's try to figure out what string format is used */
+            HWND parent = cs->hwndParent;
+            if (!parent) parent = GetWindow(hWnd, GW_OWNER);
+
+            SendMessageW(hWnd, IsWindowUnicode(parent) ? MCIWNDM_OPENW : MCIWNDM_OPENA, 0, lParam);
+        }
+    }
+
+    return 0;
+}
+
+static void MCIWND_ToggleState(MCIWndInfo *mwi)
+{
+    switch (SendMessageW(mwi->hWnd, MCIWNDM_GETMODEW, 0, 0))
+    {
+    case MCI_MODE_NOT_READY:
+    case MCI_MODE_RECORD:
+    case MCI_MODE_SEEK:
+    case MCI_MODE_OPEN:
+        TRACE("Cannot do much...\n");
+        break;
+
+    case MCI_MODE_PAUSE:
+        SendMessageW(mwi->hWnd, MCI_RESUME, 0, 0);
+        break;
+
+    case MCI_MODE_PLAY:
+        SendMessageW(mwi->hWnd, MCI_PAUSE, 0, 0);
+        break;
+
+    case MCI_MODE_STOP:
+        SendMessageW(mwi->hWnd, MCI_STOP, 0, 0);
+        break;
+    }
+}
+
+static LRESULT MCIWND_Command(MCIWndInfo *mwi, WPARAM wParam, LPARAM lParam)
+{
+    switch (LOWORD(wParam))
+    {
+    case CTL_PLAYSTOP: MCIWND_ToggleState(mwi); break;
+    case CTL_MENU:
+    case CTL_TRACKBAR:
+    default:
+        FIXME("support for command %04x not implement yet\n", LOWORD(wParam));
+    }
+    return 0L;
+}
+
+static void MCIWND_notify_media(MCIWndInfo *mwi)
+{
+    if (mwi->dwStyle & (MCIWNDF_NOTIFYMEDIAA | MCIWNDF_NOTIFYMEDIAW))
+    {
+        if (!mwi->lpName)
+        {
+            static const WCHAR empty_str[1];
+            SendMessageW(mwi->hwndOwner, MCIWNDM_NOTIFYMEDIA, (WPARAM)mwi->hWnd, (LPARAM)empty_str);
+        }
+        else
+        {
+            if (mwi->dwStyle & MCIWNDF_NOTIFYANSI)
+            {
+                char *ansi_name;
+                int len;
+
+                len = WideCharToMultiByte(CP_ACP, 0, mwi->lpName, -1, NULL, 0, NULL, NULL);
+                ansi_name = HeapAlloc(GetProcessHeap(), 0, len);
+                WideCharToMultiByte(CP_ACP, 0, mwi->lpName, -1, ansi_name, len, NULL, NULL);
+
+                SendMessageW(mwi->hwndOwner, MCIWNDM_NOTIFYMEDIA, (WPARAM)mwi->hWnd, (LPARAM)ansi_name);
+
+                HeapFree(GetProcessHeap(), 0, ansi_name);
+            }
+            else
+                SendMessageW(mwi->hwndOwner, MCIWNDM_NOTIFYMEDIA, (WPARAM)mwi->hWnd, (LPARAM)mwi->lpName);
+        }
+    }
+}
+
+static MCIERROR mci_generic_command(MCIWndInfo *mwi, UINT cmd)
+{
+    MCI_GENERIC_PARMS mci_generic;
+
+    mci_generic.dwCallback = 0;
+    mwi->lasterror = mciSendCommandW(mwi->mci, cmd, 0, (DWORD_PTR)&mci_generic);
+
+    if (mwi->lasterror)
+        return mwi->lasterror;
+
+    MCIWND_notify_mode(mwi);
+    MCIWND_UpdateState(mwi);
+    return 0;
+}
+
+static LRESULT mci_get_devcaps(MCIWndInfo *mwi, UINT cap)
+{
+    MCI_GETDEVCAPS_PARMS mci_devcaps;
+
+    mci_devcaps.dwItem = cap;
+    mwi->lasterror = mciSendCommandW(mwi->mci, MCI_GETDEVCAPS,
+                                   MCI_GETDEVCAPS_ITEM,
+                                   (DWORD_PTR)&mci_devcaps);
+    if (mwi->lasterror)
+        return 0;
+
+    return mci_devcaps.dwReturn;
+}
+
+static LRESULT WINAPI MCIWndProc(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
+{
+    MCIWndInfo *mwi;
+
+    TRACE("%p %04x %08x %08lx\n", hWnd, wMsg, wParam, lParam);
+
+    mwi = (MCIWndInfo*)GetWindowLongW(hWnd, 0);
+    if (!mwi && wMsg != WM_CREATE)
+        return DefWindowProcW(hWnd, wMsg, wParam, lParam);
+
+    switch (wMsg)
+    {
+    case WM_CREATE:
+        MCIWND_Create(hWnd, (CREATESTRUCTW *)lParam);
+        break;
+
+    case WM_DESTROY:
+        if (mwi->uTimer)
+            KillTimer(hWnd, mwi->uTimer);
+
+        if (mwi->mci)
+            SendMessageW(hWnd, MCI_CLOSE, 0, 0);
+
+        HeapFree(GetProcessHeap(), 0, mwi);
+
+        DestroyWindow(GetDlgItem(hWnd, CTL_MENU));
+        DestroyWindow(GetDlgItem(hWnd, CTL_PLAYSTOP));
+        DestroyWindow(GetDlgItem(hWnd, CTL_TRACKBAR));
+        break;
+
+    case WM_PAINT:
+        {
+            MCI_DGV_UPDATE_PARMS mci_update;
+            PAINTSTRUCT ps;
+
+            mci_update.hDC = (wParam) ? (HDC)wParam : BeginPaint(hWnd, &ps);
+
+            mciSendCommandW(mwi->mci, MCI_UPDATE,
+                            MCI_DGV_UPDATE_HDC | MCI_DGV_UPDATE_PAINT,
+                            (DWORD_PTR)&mci_update);
+
+            if (!wParam) EndPaint(hWnd, &ps);
+            return 1;
+        }
+
+    case WM_COMMAND:
+        return MCIWND_Command(mwi, wParam, lParam);
+
+    case WM_NCACTIVATE:
+        if (mwi->uTimer)
+        {
+            KillTimer(hWnd, mwi->uTimer);
+            mwi->uTimer = SetTimer(hWnd, 1, wParam ? mwi->active_timer : mwi->inactive_timer, NULL);
+        }
+        break;
+
+    case WM_TIMER:
+        MCIWND_UpdateState(mwi);
+        return 0;
+
+    case WM_SIZE:
+        SetWindowPos(GetDlgItem(hWnd, CTL_PLAYSTOP), 0, 0, HIWORD(lParam) - 32, 0, 0, SWP_NOSIZE | SWP_NOACTIVATE);
+        SetWindowPos(GetDlgItem(hWnd, CTL_MENU), 0, 32, HIWORD(lParam) - 32, 0, 0, SWP_NOSIZE | SWP_NOACTIVATE);
+        SetWindowPos(GetDlgItem(hWnd, CTL_TRACKBAR), 0, 64, HIWORD(lParam) - 32, LOWORD(lParam) - 64, 32, SWP_NOACTIVATE);
+
+        if (!(mwi->dwStyle & MCIWNDF_NOAUTOSIZEMOVIE))
+        {
+            RECT rc;
+
+            rc.left = rc.top = 0;
+            rc.right = LOWORD(lParam);
+            rc.bottom = HIWORD(lParam);
+            if (!(mwi->dwStyle & MCIWNDF_NOPLAYBAR))
+                rc.bottom -= 32; /* subtract the height of the playbar */
+            SendMessageW(hWnd, MCIWNDM_PUT_DEST, 0, (LPARAM)&rc);
+        }
+        MCIWND_notify_size(mwi);
+        break;
+
+    case MM_MCINOTIFY:
+        MCIWND_notify_mode(mwi);
+        MCIWND_UpdateState(mwi);
+        return 0;
+
+    case MCIWNDM_OPENA:
+        {
+            UNICODE_STRING nameW;
+            TRACE("MCIWNDM_OPENA %s\n", debugstr_a((LPSTR)lParam));
+            RtlCreateUnicodeStringFromAsciiz(&nameW, (LPCSTR)lParam);
+            lParam = (LPARAM)nameW.Buffer;
+        }
+        /* fall through */
+    case MCIWNDM_OPENW:
+        {
+            RECT rc;
+            HCURSOR hCursor;
+            MCI_OPEN_PARMSW mci_open;
+            MCI_GETDEVCAPS_PARMS mci_devcaps;
+            WCHAR aliasW[64];
+            WCHAR drv_name[MAX_PATH];
+            static const WCHAR formatW[] = {'%','d',0};
+            static const WCHAR mci32W[] = {'m','c','i','3','2',0};
+            static const WCHAR system_iniW[] = {'s','y','s','t','e','m','.','i','n','i',0};
+
+            TRACE("MCIWNDM_OPENW %s\n", debugstr_w((LPWSTR)lParam));
+
+            if (wParam == MCIWNDOPENF_NEW)
+            {
+                SendMessageW(hWnd, MCIWNDM_NEWW, 0, lParam);
+                goto end_of_mci_open;
+            }
+
+            if (mwi->uTimer)
+            {
+                KillTimer(hWnd, mwi->uTimer);
+                mwi->uTimer = 0;
+            }
+
+            hCursor = LoadCursorW(0, (LPWSTR)IDC_WAIT);
+            hCursor = SetCursor(hCursor);
+
+            mci_open.lpstrElementName = (LPWSTR)lParam;
+            wsprintfW(aliasW, formatW, (int)hWnd + 1);
+            mci_open.lpstrAlias = aliasW;
+            mwi->lasterror = mciSendCommandW(mwi->mci, MCI_OPEN,
+                                             MCI_OPEN_ELEMENT | MCI_OPEN_ALIAS | MCI_WAIT,
+                                             (DWORD_PTR)&mci_open);
+            SetCursor(hCursor);
+
+            if (mwi->lasterror && !(mwi->dwStyle & MCIWNDF_NOERRORDLG))
+            {
+                /* FIXME: get the caption from resources */
+                static const WCHAR caption[] = {'M','C','I',' ','E','r','r','o','r',0};
+                WCHAR error_str[MAXERRORLENGTH];
+
+                mciGetErrorStringW(mwi->lasterror, error_str, MAXERRORLENGTH);
+                MessageBoxW(hWnd, error_str, caption, MB_ICONEXCLAMATION | MB_OK);
+                MCIWND_notify_error(mwi);
+                goto end_of_mci_open;
+            }
+
+            mwi->mci = mci_open.wDeviceID;
+            mwi->alias = (int)hWnd + 1;
+
+            mwi->lpName = HeapAlloc(GetProcessHeap(), 0, (strlenW((LPWSTR)lParam) + 1) * sizeof(WCHAR));
+            strcpyW(mwi->lpName, (LPWSTR)lParam);
+
+            MCIWND_UpdateState(mwi);
+
+            mci_devcaps.dwItem = MCI_GETDEVCAPS_DEVICE_TYPE;
+            mwi->lasterror = mciSendCommandW(mwi->mci, MCI_GETDEVCAPS,
+                                             MCI_GETDEVCAPS_ITEM,
+                                             (DWORD_PTR)&mci_devcaps);
+            if (mwi->lasterror)
+            {
+                MCIWND_notify_error(mwi);
+                goto end_of_mci_open;
+            }
+
+            mwi->dev_type = mci_devcaps.dwReturn;
+
+            drv_name[0] = 0;
+            SendMessageW(hWnd, MCIWNDM_GETDEVICEW, 256, (LPARAM)drv_name);
+            if (drv_name[0] && GetPrivateProfileStringW(mci32W, drv_name, NULL,
+                                            drv_name, MAX_PATH, system_iniW))
+                mwi->hdrv = OpenDriver(drv_name, NULL, 0);
+
+            if (mwi->dev_type == MCI_DEVTYPE_DIGITAL_VIDEO)
+            {
+                MCI_DGV_WINDOW_PARMSW mci_window;
+
+                mci_window.hWnd = hWnd;
+                mwi->lasterror = mciSendCommandW(mwi->mci, MCI_WINDOW,
+                                                 MCI_DGV_WINDOW_HWND,
+                                                 (DWORD_PTR)&mci_window);
+                if (mwi->lasterror)
+                {
+                    MCIWND_notify_error(mwi);
+                    goto end_of_mci_open;
+                }
+            }
+
+            if (SendMessageW(hWnd, MCIWNDM_GET_DEST, 0, (LPARAM)&rc) == 0)
+            {
+                mwi->size.cx = rc.right - rc.left;
+                mwi->size.cy = rc.bottom - rc.top;
+
+                rc.right = MulDiv(mwi->size.cx, mwi->zoom, 100);
+                rc.bottom = MulDiv(mwi->size.cy, mwi->zoom, 100);
+                SendMessageW(hWnd, MCIWNDM_PUT_DEST, 0, (LPARAM)&rc);
+            }
+            else
+            {
+                GetClientRect(hWnd, &rc);
+                rc.bottom = rc.top;
+            }
+
+            if (!(mwi->dwStyle & MCIWNDF_NOPLAYBAR))
+                rc.bottom += 32; /* add the height of the playbar */
+            AdjustWindowRect(&rc, GetWindowLongW(hWnd, GWL_STYLE), FALSE);
+            SetWindowPos(hWnd, 0, 0, 0, rc.right - rc.left,
+                         rc.bottom - rc.top, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
+
+            SendDlgItemMessageW(hWnd, CTL_TRACKBAR, TBM_SETRANGEMIN, 0L, 0L);
+            SendDlgItemMessageW(hWnd, CTL_TRACKBAR, TBM_SETRANGEMAX, 1,
+                                SendMessageW(hWnd, MCIWNDM_GETLENGTH, 0, 0));
+            mwi->uTimer = SetTimer(hWnd, 1, mwi->active_timer, NULL);
+
+            MCIWND_notify_media(mwi);
+
+end_of_mci_open:
+            if (wMsg == MCIWNDM_OPENA)
+                HeapFree(GetProcessHeap(), 0, (void *)lParam);
+            return mwi->lasterror;
+        }
+
+    case MCIWNDM_GETDEVICEID:
+        TRACE("MCIWNDM_GETDEVICEID\n");
+        return mwi->mci;
+
+    case MCIWNDM_GETALIAS:
+        TRACE("MCIWNDM_GETALIAS\n");
+        return mwi->alias;
+
+    case MCIWNDM_GET_SOURCE:
+        {
+            MCI_DGV_RECT_PARMS mci_rect;
+
+            mwi->lasterror = mciSendCommandW(mwi->mci, MCI_WHERE,
+                                             MCI_DGV_WHERE_SOURCE,
+                                             (DWORD_PTR)&mci_rect);
+            if (mwi->lasterror)
+            {
+                MCIWND_notify_error(mwi);
+                return mwi->lasterror;
+            }
+            *(RECT *)lParam = mci_rect.rc;
+            TRACE("MCIWNDM_GET_SOURCE: %s\n", wine_dbgstr_rect(&mci_rect.rc));
+            return 0;
+        }
+
+    case MCIWNDM_GET_DEST:
+        {
+            MCI_DGV_RECT_PARMS mci_rect;
+
+            mwi->lasterror = mciSendCommandW(mwi->mci, MCI_WHERE,
+                                             MCI_DGV_WHERE_DESTINATION,
+                                             (DWORD_PTR)&mci_rect);
+            if (mwi->lasterror)
+            {
+                MCIWND_notify_error(mwi);
+                return mwi->lasterror;
+            }
+            *(RECT *)lParam = mci_rect.rc;
+            TRACE("MCIWNDM_GET_DEST: %s\n", wine_dbgstr_rect(&mci_rect.rc));
+            return 0;
+        }
+
+    case MCIWNDM_PUT_SOURCE:
+        {
+            MCI_DGV_PUT_PARMS mci_put;
+
+            mci_put.rc = *(RECT *)lParam;
+            TRACE("MCIWNDM_PUT_SOURCE: %s\n", wine_dbgstr_rect(&mci_put.rc));
+            mwi->lasterror = mciSendCommandW(mwi->mci, MCI_PUT,
+                                             MCI_DGV_PUT_SOURCE,
+                                             (DWORD_PTR)&mci_put);
+            if (mwi->lasterror)
+            {
+                MCIWND_notify_error(mwi);
+                return mwi->lasterror;
+            }
+            return 0;
+        }
+
+    case MCIWNDM_PUT_DEST:
+        {
+            MCI_DGV_PUT_PARMS mci_put;
+
+            mci_put.rc = *(RECT *)lParam;
+            TRACE("MCIWNDM_PUT_DEST: %s\n", wine_dbgstr_rect(&mci_put.rc));
+
+            mwi->lasterror = mciSendCommandW(mwi->mci, MCI_PUT,
+                                             MCI_DGV_PUT_DESTINATION | MCI_DGV_RECT,
+                                             (DWORD_PTR)&mci_put);
+            if (mwi->lasterror)
+            {
+                MCIWND_notify_error(mwi);
+                return mwi->lasterror;
+            }
+            return 0;
+        }
+
+    case MCIWNDM_GETLENGTH:
+        {
+            MCI_STATUS_PARMS mci_status;
+
+            mci_status.dwItem = MCI_STATUS_LENGTH;
+            mwi->lasterror = mciSendCommandW(mwi->mci, MCI_STATUS,
+                                             MCI_STATUS_ITEM,
+                                             (DWORD_PTR)&mci_status);
+            if (mwi->lasterror)
+            {
+                MCIWND_notify_error(mwi);
+                return 0;
+            }
+            TRACE("MCIWNDM_GETLENGTH: %ld\n", mci_status.dwReturn);
+            return mci_status.dwReturn;
+        }
+
+    case MCIWNDM_GETSTART:
+        {
+            MCI_STATUS_PARMS mci_status;
+
+            mci_status.dwItem = MCI_STATUS_POSITION;
+            mwi->lasterror = mciSendCommandW(mwi->mci, MCI_STATUS,
+                                             MCI_STATUS_ITEM | MCI_STATUS_START,
+                                             (DWORD_PTR)&mci_status);
+            if (mwi->lasterror)
+            {
+                MCIWND_notify_error(mwi);
+                return 0;
+            }
+            TRACE("MCIWNDM_GETSTART: %ld\n", mci_status.dwReturn);
+            return mci_status.dwReturn;
+        }
+
+    case MCIWNDM_GETEND:
+        {
+            LRESULT start, length;
+
+            start = SendMessageW(hWnd, MCIWNDM_GETSTART, 0, 0);
+            length = SendMessageW(hWnd, MCIWNDM_GETLENGTH, 0, 0);
+            TRACE("MCIWNDM_GETEND: %ld\n", start + length);
+            return (start + length);
+        }
+
+    case MCIWNDM_GETPOSITIONA:
+    case MCIWNDM_GETPOSITIONW:
+        {
+            MCI_STATUS_PARMS mci_status;
+
+            TRACE("MCIWNDM_GETPOSITION\n");
+
+            /* get position string if requested */
+            if (wParam && lParam)
+            {
+                if (wMsg == MCIWNDM_GETPOSITIONA)
+                {
+                    char cmd[64];
+
+                    wsprintfA(cmd, "status %d position", mwi->alias);
+                    mwi->lasterror = mciSendStringA(cmd, (LPSTR)lParam, wParam, 0);
+                }
+                else
+                {
+
+                    WCHAR cmdW[64];
+                    static const WCHAR formatW[] = {'s','t','a','t','u','s',' ','%','d',' ','p','o','s','i','t','i','o','n',0};
+
+                    wsprintfW(cmdW, formatW, mwi->alias);
+                    mwi->lasterror = mciSendStringW(cmdW, (LPWSTR)lParam, wParam, 0);
+                }
+
+                if (mwi->lasterror)
+                    return 0;
+            }
+
+            mci_status.dwItem = MCI_STATUS_POSITION;
+            mwi->lasterror = mciSendCommandW(mwi->mci, MCI_STATUS,
+                                             MCI_STATUS_ITEM,
+                                             (DWORD_PTR)&mci_status);
+            if (mwi->lasterror)
+                return 0;
+
+            return mci_status.dwReturn;
+        }
+
+    case MCIWNDM_GETMODEA:
+    case MCIWNDM_GETMODEW:
+        {
+            MCI_STATUS_PARMS mci_status;
+
+            TRACE("MCIWNDM_GETMODE\n");
+
+            if (!mwi->mci)
+                return MCI_MODE_NOT_READY;
+
+            /* get mode string if requested */
+            if (wParam && lParam)
+            {
+                if (wMsg == MCIWNDM_GETMODEA)
+                {
+                    char cmd[64];
+
+                    wsprintfA(cmd, "status %d mode", mwi->alias);
+                    mwi->lasterror = mciSendStringA(cmd, (LPSTR)lParam, wParam, 0);
+                }
+                else
+                {
+
+                    WCHAR cmdW[64];
+                    static const WCHAR formatW[] = {'s','t','a','t','u','s',' ','%','d',' ','m','o','d','e',0};
+
+                    wsprintfW(cmdW, formatW, mwi->alias);
+                    mwi->lasterror = mciSendStringW(cmdW, (LPWSTR)lParam, wParam, 0);
+                }
+
+                if (mwi->lasterror)
+                    return MCI_MODE_NOT_READY;
+            }
+
+            mci_status.dwItem = MCI_STATUS_MODE;
+            mwi->lasterror = mciSendCommandW(mwi->mci, MCI_STATUS,
+                                             MCI_STATUS_ITEM,
+                                             (DWORD_PTR)&mci_status);
+            if (mwi->lasterror)
+                return MCI_MODE_NOT_READY;
+
+            return mci_status.dwReturn;
+        }
+
+    case MCIWNDM_PLAYFROM:
+        {
+            MCI_PLAY_PARMS mci_play;
+
+            TRACE("MCIWNDM_PLAYFROM %08lx\n", lParam);
+
+            mci_play.dwCallback = (DWORD_PTR)hWnd;
+            mci_play.dwFrom = lParam;
+            mwi->lasterror = mciSendCommandW(mwi->mci, MCI_PLAY,
+                                             MCI_FROM | MCI_NOTIFY,
+                                             (DWORD_PTR)&mci_play);
+            if (mwi->lasterror)
+            {
+                MCIWND_notify_error(mwi);
+                return mwi->lasterror;
+            }
+
+            MCIWND_notify_mode(mwi);
+            MCIWND_UpdateState(mwi);
+            return 0;
+        }
+
+    case MCIWNDM_PLAYTO:
+        {
+            MCI_PLAY_PARMS mci_play;
+
+            TRACE("MCIWNDM_PLAYTO %08lx\n", lParam);
+
+            mci_play.dwCallback = (DWORD_PTR)hWnd;
+            mci_play.dwTo = lParam;
+            mwi->lasterror = mciSendCommandW(mwi->mci, MCI_PLAY,
+                                             MCI_TO | MCI_NOTIFY,
+                                             (DWORD_PTR)&mci_play);
+            if (mwi->lasterror)
+            {
+                MCIWND_notify_error(mwi);
+                return mwi->lasterror;
+            }
+
+            MCIWND_notify_mode(mwi);
+            MCIWND_UpdateState(mwi);
+            return 0;
+        }
+
+    case MCIWNDM_PLAYREVERSE:
+        {
+            MCI_PLAY_PARMS mci_play;
+            DWORD flags = MCI_NOTIFY;
+
+            TRACE("MCIWNDM_PLAYREVERSE %08lx\n", lParam);
+
+            mci_play.dwCallback = (DWORD_PTR)hWnd;
+            mci_play.dwFrom = lParam;
+            switch (mwi->dev_type)
+            {
+            default:
+            case MCI_DEVTYPE_ANIMATION:
+                flags |= MCI_ANIM_PLAY_REVERSE;
+                break;
+
+            case MCI_DEVTYPE_DIGITAL_VIDEO:
+                flags |= MCI_DGV_PLAY_REVERSE;
+                break;
+
+#ifdef MCI_VCR_PLAY_REVERSE
+            case MCI_DEVTYPE_VCR:
+                flags |= MCI_VCR_PLAY_REVERSE;
+                break;
+#endif
+
+            case MCI_DEVTYPE_VIDEODISC:
+                flags |= MCI_VD_PLAY_REVERSE;
+                break;
+
+            }
+            mwi->lasterror = mciSendCommandW(mwi->mci, MCI_PLAY,
+                                             flags, (DWORD_PTR)&mci_play);
+            if (mwi->lasterror)
+            {
+                MCIWND_notify_error(mwi);
+                return mwi->lasterror;
+            }
+
+            MCIWND_notify_mode(mwi);
+            MCIWND_UpdateState(mwi);
+            return 0;
+        }
+
+    case MCIWNDM_GETERRORA:
+        mciGetErrorStringA(mwi->lasterror, (LPSTR)lParam, wParam);
+        TRACE("MCIWNDM_GETERRORA: %s\n", debugstr_an((LPSTR)lParam, wParam));
+        return mwi->lasterror;
+
+    case MCIWNDM_GETERRORW:
+        mciGetErrorStringW(mwi->lasterror, (LPWSTR)lParam, wParam);
+        TRACE("MCIWNDM_GETERRORW: %s\n", debugstr_wn((LPWSTR)lParam, wParam));
+        return mwi->lasterror;
+
+    case MCIWNDM_SETOWNER:
+        TRACE("MCIWNDM_SETOWNER %p\n", (HWND)wParam);
+        mwi->hwndOwner = (HWND)wParam;
+        return 0;
+
+    case MCIWNDM_SENDSTRINGA:
+        {
+            UNICODE_STRING stringW;
+
+            TRACE("MCIWNDM_SENDSTRINGA %s\n", debugstr_a((LPCSTR)lParam));
+
+            RtlCreateUnicodeStringFromAsciiz(&stringW, (LPCSTR)lParam);
+            lParam = (LPARAM)stringW.Buffer;
+        }
+        /* fall through */
+    case MCIWNDM_SENDSTRINGW:
+        {
+            WCHAR *cmdW, *p;
+
+            TRACE("MCIWNDM_SENDSTRINGW %s\n", debugstr_w((LPCWSTR)lParam));
+
+            p = strchrW((LPCWSTR)lParam, ' ');
+            if (p)
+            {
+                static const WCHAR formatW[] = {'%','d',' ',0};
+                int len, pos;
+
+                pos = p - (WCHAR *)lParam + 1;
+                len = lstrlenW((LPCWSTR)lParam) + 64;
+
+                cmdW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
+
+                memcpy(cmdW, (void *)lParam, pos * sizeof(WCHAR));
+                wsprintfW(cmdW + pos, formatW, mwi->alias);
+                strcatW(cmdW, (WCHAR *)lParam + pos);
+            }
+            else
+                cmdW = (LPWSTR)lParam;
+
+            mwi->lasterror = mciSendStringW(cmdW, mwi->return_string,
+                                            sizeof(mwi->return_string)/sizeof(mwi->return_string[0]),
+                                            0);
+            if (mwi->lasterror)
+                MCIWND_notify_error(mwi);
+
+            if (cmdW != (LPWSTR)lParam)
+                HeapFree(GetProcessHeap(), 0, cmdW);
+
+            if (wMsg == MCIWNDM_SENDSTRINGA)
+                HeapFree(GetProcessHeap(), 0, (void *)lParam);
+
+            MCIWND_UpdateState(mwi);
+            return mwi->lasterror;
+        }
+
+    case MCIWNDM_RETURNSTRINGA:
+        WideCharToMultiByte(CP_ACP, 0, mwi->return_string, -1, (LPSTR)lParam, wParam, NULL, NULL);
+        TRACE("MCIWNDM_RETURNTRINGA %s\n", debugstr_an((LPSTR)lParam, wParam));
+        return mwi->lasterror;
+
+    case MCIWNDM_RETURNSTRINGW:
+        strncpyW((LPWSTR)lParam, mwi->return_string, wParam);
+        TRACE("MCIWNDM_RETURNTRINGW %s\n", debugstr_wn((LPWSTR)lParam, wParam));
+        return mwi->lasterror;
+
+    case MCIWNDM_SETTIMERS:
+        TRACE("MCIWNDM_SETTIMERS active %d ms, inactive %d ms\n", (int)wParam, (int)lParam);
+        mwi->active_timer = (WORD)wParam;
+        mwi->inactive_timer = (WORD)lParam;
+        return 0;
+
+    case MCIWNDM_SETACTIVETIMER:
+        TRACE("MCIWNDM_SETACTIVETIMER %d ms\n", (int)wParam);
+        mwi->active_timer = (WORD)wParam;
+        return 0;
+
+    case MCIWNDM_SETINACTIVETIMER:
+        TRACE("MCIWNDM_SETINACTIVETIMER %d ms\n", (int)wParam);
+        mwi->inactive_timer = (WORD)wParam;
+        return 0;
+
+    case MCIWNDM_GETACTIVETIMER:
+        TRACE("MCIWNDM_GETACTIVETIMER: %d ms\n", mwi->active_timer);
+        return mwi->active_timer;
+
+    case MCIWNDM_GETINACTIVETIMER:
+        TRACE("MCIWNDM_GETINACTIVETIMER: %d ms\n", mwi->inactive_timer);
+        return mwi->inactive_timer;
+
+    case MCIWNDM_CHANGESTYLES:
+        TRACE("MCIWNDM_CHANGESTYLES mask %08x, set %08lx\n", wParam, lParam);
+        /* FIXME: update the visual window state as well:
+         * add/remove trackbar, autosize, etc.
+         */
+        mwi->dwStyle &= ~wParam;
+        mwi->dwStyle |= lParam & wParam;
+        return 0;
+
+    case MCIWNDM_GETSTYLES:
+        TRACE("MCIWNDM_GETSTYLES: %08lx\n", mwi->dwStyle & 0xffff);
+        return mwi->dwStyle & 0xffff;
+
+    case MCIWNDM_GETDEVICEA:
+        {
+            MCI_SYSINFO_PARMSA mci_sysinfo;
+
+            mci_sysinfo.lpstrReturn = (LPSTR)lParam;
+            mci_sysinfo.dwRetSize = wParam;
+            mwi->lasterror = mciSendCommandA(mwi->mci, MCI_SYSINFO,
+                                             MCI_SYSINFO_INSTALLNAME,
+                                             (DWORD_PTR)&mci_sysinfo);
+            TRACE("MCIWNDM_GETDEVICEA: %s\n", debugstr_an((LPSTR)lParam, wParam));
+            return 0;
+        }
+
+    case MCIWNDM_GETDEVICEW:
+        {
+            MCI_SYSINFO_PARMSW mci_sysinfo;
+
+            mci_sysinfo.lpstrReturn = (LPWSTR)lParam;
+            mci_sysinfo.dwRetSize = wParam;
+            mwi->lasterror = mciSendCommandW(mwi->mci, MCI_SYSINFO,
+                                             MCI_SYSINFO_INSTALLNAME,
+                                             (DWORD_PTR)&mci_sysinfo);
+            TRACE("MCIWNDM_GETDEVICEW: %s\n", debugstr_wn((LPWSTR)lParam, wParam));
+            return 0;
+        }
+
+    case MCIWNDM_VALIDATEMEDIA:
+        TRACE("MCIWNDM_VALIDATEMEDIA\n");
+        if (mwi->mci)
+        {
+            SendMessageW(hWnd, MCIWNDM_GETSTART, 0, 0);
+            SendMessageW(hWnd, MCIWNDM_GETLENGTH, 0, 0);
+        }
+        return 0;
+
+    case MCIWNDM_GETFILENAMEA:
+        TRACE("MCIWNDM_GETFILENAMEA: %s\n", debugstr_w(mwi->lpName));
+        if (mwi->lpName)
+            WideCharToMultiByte(CP_ACP, 0, mwi->lpName, -1, (LPSTR)lParam, wParam, NULL, NULL);
+        return 0;
+
+    case MCIWNDM_GETFILENAMEW:
+        TRACE("MCIWNDM_GETFILENAMEW: %s\n", debugstr_w(mwi->lpName));
+        if (mwi->lpName)
+            strncpyW((LPWSTR)lParam, mwi->lpName, wParam);
+        return 0;
+
+    case MCIWNDM_GETTIMEFORMATA:
+    case MCIWNDM_GETTIMEFORMATW:
+        {
+            MCI_STATUS_PARMS mci_status;
+
+            TRACE("MCIWNDM_GETTIMEFORMAT %08x %08lx\n", wParam, lParam);
+
+            /* get format string if requested */
+            if (wParam && lParam)
+            {
+                if (wMsg == MCIWNDM_GETTIMEFORMATA)
+                {
+                    char cmd[64];
+
+                    wsprintfA(cmd, "status %d time format", mwi->alias);
+                    mwi->lasterror = mciSendStringA(cmd, (LPSTR)lParam, wParam, 0);
+                    if (mwi->lasterror)
+                        return 0;
+                }
+                else
+                {
+                    WCHAR cmdW[64];
+                    static const WCHAR formatW[] = {'s','t','a','t','u','s',' ','%','d',' ','t','i','m','e',' ','f','o','r','m','a','t',0};
+
+                    wsprintfW(cmdW, formatW, mwi->alias);
+                    mwi->lasterror = mciSendStringW(cmdW, (LPWSTR)lParam, wParam, 0);
+                    if (mwi->lasterror)
+                        return 0;
+                }
+            }
+
+            mci_status.dwItem = MCI_STATUS_TIME_FORMAT ;
+            mwi->lasterror = mciSendCommandW(mwi->mci, MCI_STATUS,
+                                             MCI_STATUS_ITEM,
+                                             (DWORD_PTR)&mci_status);
+            if (mwi->lasterror)
+                return 0;
+
+            return mci_status.dwReturn;
+        }
+
+    case MCIWNDM_SETTIMEFORMATA:
+        {
+            UNICODE_STRING stringW;
+
+            TRACE("MCIWNDM_SETTIMEFORMATA %s\n", debugstr_a((LPSTR)lParam));
+
+            RtlCreateUnicodeStringFromAsciiz(&stringW, (LPCSTR)lParam);
+            lParam = (LPARAM)stringW.Buffer;
+        }
+        /* fall through */
+    case MCIWNDM_SETTIMEFORMATW:
+        {
+            static const WCHAR formatW[] = {'s','e','t',' ','%','d',' ','t','i','m','e',' ','f','o','r','m','a','t',' ',0};
+            WCHAR *cmdW;
+
+            TRACE("MCIWNDM_SETTIMEFORMATW %s\n", debugstr_w((LPWSTR)lParam));
+
+            if (mwi->mci)
+            {
+                cmdW = HeapAlloc(GetProcessHeap(), 0, (lstrlenW((LPCWSTR)lParam) + 64) * sizeof(WCHAR));
+                wsprintfW(cmdW, formatW, mwi->alias);
+                strcatW(cmdW, (WCHAR *)lParam);
+
+                mwi->lasterror = mciSendStringW(cmdW, NULL, 0, 0);
+
+                /* fix the range tracking according to the new time format */
+                if (!mwi->lasterror)
+                    SendDlgItemMessageW(hWnd, CTL_TRACKBAR, TBM_SETRANGEMAX, 1,
+                                        SendMessageW(hWnd, MCIWNDM_GETLENGTH, 0, 0));
+            }
+
+            if (wMsg == MCIWNDM_SETTIMEFORMATA)
+                HeapFree(GetProcessHeap(), 0, (void *)lParam);
+
+            return 0;
+        }
+
+    case MCIWNDM_CAN_PLAY:
+        TRACE("MCIWNDM_CAN_PLAY\n");
+        if (mwi->mci)
+            return mci_get_devcaps(mwi, MCI_GETDEVCAPS_CAN_PLAY);
+        return 0;
+
+    case MCIWNDM_CAN_RECORD:
+        TRACE("MCIWNDM_CAN_RECORD\n");
+        if (mwi->mci)
+            return mci_get_devcaps(mwi, MCI_GETDEVCAPS_CAN_RECORD);
+        return 0;
+
+    case MCIWNDM_CAN_SAVE:
+        TRACE("MCIWNDM_CAN_SAVE\n");
+        if (mwi->mci)
+            return mci_get_devcaps(mwi, MCI_GETDEVCAPS_CAN_SAVE);
+        return 0;
+
+    case MCIWNDM_CAN_EJECT:
+        TRACE("MCIWNDM_CAN_EJECT\n");
+        if (mwi->mci)
+            return mci_get_devcaps(mwi, MCI_GETDEVCAPS_CAN_EJECT);
+        return 0;
+
+    case MCIWNDM_CAN_WINDOW:
+        TRACE("MCIWNDM_CAN_WINDOW\n");
+        switch (mwi->dev_type)
+        {
+        case MCI_DEVTYPE_ANIMATION:
+        case MCI_DEVTYPE_DIGITAL_VIDEO:
+        case MCI_DEVTYPE_OVERLAY:
+            return 1;
+        }
+        return 0;
+
+    case MCIWNDM_CAN_CONFIG:
+        TRACE("MCIWNDM_CAN_CONFIG\n");
+        if (mwi->hdrv)
+            return SendDriverMessage(mwi->hdrv, DRV_QUERYCONFIGURE, 0, 0);
+        return 0;
+
+    case MCIWNDM_SETZOOM:
+        TRACE("MCIWNDM_SETZOOM %ld\n", lParam);
+        mwi->zoom = lParam;
+
+        if (mwi->mci && !(mwi->dwStyle & MCIWNDF_NOAUTOSIZEWINDOW))
+        {
+            RECT rc;
+
+            rc.left = rc.top = 0;
+            rc.right = MulDiv(mwi->size.cx, mwi->zoom, 100);
+            rc.bottom = MulDiv(mwi->size.cy, mwi->zoom, 100);
+
+            if (!(mwi->dwStyle & MCIWNDF_NOPLAYBAR))
+                rc.bottom += 32; /* add the height of the playbar */
+            AdjustWindowRect(&rc, GetWindowLongW(hWnd, GWL_STYLE), FALSE);
+            SetWindowPos(hWnd, 0, 0, 0, rc.right - rc.left, rc.bottom - rc.top,
+                         SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
+        }
+        return 0;
+
+    case MCIWNDM_GETZOOM:
+        TRACE("MCIWNDM_GETZOOM: %d\n", mwi->zoom);
+        return mwi->zoom;
+
+    case MCIWNDM_EJECT:
+        {
+            MCI_SET_PARMS mci_set;
+
+            TRACE("MCIWNDM_EJECT\n");
+
+            mci_set.dwCallback = (DWORD_PTR)hWnd;
+            mwi->lasterror = mciSendCommandW(mwi->mci, MCI_SET,
+                                             MCI_SET_DOOR_OPEN | MCI_NOTIFY,
+                                             (DWORD_PTR)&mci_set);
+            MCIWND_notify_mode(mwi);
+            MCIWND_UpdateState(mwi);
+            return mwi->lasterror;
+        }
+
+    case MCIWNDM_SETVOLUME:
+    case MCIWNDM_GETVOLUME:
+    case MCIWNDM_SETSPEED:
+    case MCIWNDM_GETSPEED:
+    case MCIWNDM_SETREPEAT:
+    case MCIWNDM_GETREPEAT:
+    case MCIWNDM_REALIZE:
+    case MCIWNDM_GETPALETTE:
+    case MCIWNDM_SETPALETTE:
+    case MCIWNDM_NEWA:
+    case MCIWNDM_NEWW:
+    case MCIWNDM_PALETTEKICK:
+    case MCIWNDM_OPENINTERFACE:
+        FIXME("support for MCIWNDM_ message WM_USER+%d not implemented\n", wMsg - WM_USER);
+        return 0;
+
+    case MCI_PLAY:
+        {
+            LRESULT end = SendMessageW(hWnd, MCIWNDM_GETEND, 0, 0);
+            return SendMessageW(hWnd, MCIWNDM_PLAYTO, 0, end);
+        }
+
+    case MCI_SEEK:
+        {
+            MCI_SEEK_PARMS mci_seek;
+
+            switch (lParam)
+            {
+            case MCIWND_START:
+                lParam = SendMessageW(hWnd, MCIWNDM_GETSTART, 0, 0);
+                break;
+
+            case MCIWND_END:
+                lParam = SendMessageW(hWnd, MCIWNDM_GETEND, 0, 0);
+                break;
+            }
+
+            mci_seek.dwTo = lParam;
+            mwi->lasterror = mciSendCommandW(mwi->mci, MCI_SEEK,
+                                             MCI_TO, (DWORD_PTR)&mci_seek);
+            if (mwi->lasterror)
+            {
+                MCIWND_notify_error(mwi);
+                return mwi->lasterror;
+            }
+            /* update window to reflect the state */
+            InvalidateRect(hWnd, NULL, TRUE);
+            return 0;
+        }
+
+    case MCI_CLOSE:
+        {
+            RECT rc;
+            MCI_GENERIC_PARMS mci_generic;
+
+            if (mwi->hdrv)
+            {
+                CloseDriver(mwi->hdrv, 0, 0);
+                mwi->hdrv = 0;
+            }
+
+            if (mwi->mci)
+            {
+                mci_generic.dwCallback = 0;
+                mwi->lasterror = mciSendCommandW(mwi->mci, MCI_CLOSE,
+                                                 0, (DWORD_PTR)&mci_generic);
+                mwi->mci = 0;
+            }
+
+            mwi->mode = MCI_MODE_NOT_READY;
+            mwi->position = -1;
+
+            if (mwi->lpName)
+            {
+                HeapFree(GetProcessHeap(), 0, mwi->lpName);
+                mwi->lpName = NULL;
+            }
+            MCIWND_UpdateState(mwi);
+
+            GetClientRect(hWnd, &rc);
+            rc.bottom = rc.top;
+            if (!(mwi->dwStyle & MCIWNDF_NOPLAYBAR))
+                rc.bottom += 32; /* add the height of the playbar */
+            AdjustWindowRect(&rc, GetWindowLongW(hWnd, GWL_STYLE), FALSE);
+            SetWindowPos(hWnd, 0, 0, 0, rc.right - rc.left,
+                         rc.bottom - rc.top, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
+
+            MCIWND_notify_media(mwi);
+            return 0;
+        }
+
+    case MCI_PAUSE:
+    case MCI_STEP:
+    case MCI_STOP:
+    case MCI_RESUME:
+        mci_generic_command(mwi, wMsg);
+        if (wMsg == MCI_STEP && !mwi->lasterror)
+        {
+            /* update window to reflect the state */
+            InvalidateRect(hWnd, NULL, TRUE);
+        }
+        return mwi->lasterror;
+
+    case MCI_CONFIGURE:
+        if (mwi->hdrv)
+            SendDriverMessage(mwi->hdrv, DRV_CONFIGURE, (LPARAM)hWnd, 0);
+        return 0;
+
+    case MCI_BREAK:
+    case MCI_CAPTURE:
+    case MCI_COPY:
+    case MCI_CUE:
+    case MCI_CUT:
+    case MCI_DELETE:
+    case MCI_ESCAPE:
+    case MCI_FREEZE:
+    case MCI_GETDEVCAPS:
+    /*case MCI_INDEX:*/
+    case MCI_INFO:
+    case MCI_LIST:
+    case MCI_LOAD:
+    /*case MCI_MARK:*/
+    case MCI_MONITOR:
+    case MCI_OPEN:
+    case MCI_PASTE:
+    case MCI_PUT:
+    case MCI_QUALITY:
+    case MCI_REALIZE:
+    case MCI_RECORD:
+    case MCI_RESERVE:
+    case MCI_RESTORE:
+    case MCI_SAVE:
+    case MCI_SET:
+    case MCI_SETAUDIO:
+    /*case MCI_SETTIMECODE:*/
+    /*case MCI_SETTUNER:*/
+    case MCI_SETVIDEO:
+    case MCI_SIGNAL:
+    case MCI_SPIN:
+    case MCI_STATUS:
+    case MCI_SYSINFO:
+    case MCI_UNDO:
+    case MCI_UNFREEZE:
+    case MCI_UPDATE:
+    case MCI_WHERE:
+    case MCI_WINDOW:
+        FIXME("support for MCI_ command %04x not implemented\n", wMsg);
+        return 0;
+    }
+
+    if (wMsg >= WM_USER)
+    {
+        FIXME("support for MCIWNDM_ message WM_USER+%d not implemented\n", wMsg - WM_USER);
+        return 0;
+    }
+
+    if (GetWindowLongW(hWnd, GWL_EXSTYLE) & WS_EX_MDICHILD)
+        return DefMDIChildProcW(hWnd, wMsg, wParam, lParam);
+
+    return DefWindowProcW(hWnd, wMsg, wParam, lParam);
+}
diff --git a/reactos/dll/msvideo/msvfw32.rc b/reactos/dll/msvideo/msvfw32.rc
new file mode 100644 (file)
index 0000000..75c98f0
--- /dev/null
@@ -0,0 +1,5 @@
+#define REACTOS_VERSION_DLL
+#define REACTOS_STR_FILE_DESCRIPTION   "MSvideo support\0"
+#define REACTOS_STR_INTERNAL_NAME      "msvfw32\0"
+#define REACTOS_STR_ORIGINAL_FILENAME  "msvfw32.dll\0"
+#include <reactos/version.rc>
diff --git a/reactos/dll/msvideo/msvfw32.spec b/reactos/dll/msvideo/msvfw32.spec
new file mode 100644 (file)
index 0000000..9590f91
--- /dev/null
@@ -0,0 +1,51 @@
+# Yes, ICCompress,ICDecompress,MCIWnd* and ICDraw* are cdecl (VFWAPIV).
+# The rest is stdcall (VFWAPI) however. -Marcus Meissner, 990124
+
+2 stdcall VideoForWindowsVersion()
+
+@ stdcall DrawDibBegin(long long long long ptr long long long)
+@ stub    DrawDibChangePalette
+@ stdcall DrawDibClose(long)
+@ stdcall DrawDibDraw(long long long long long long ptr ptr long long long long long)
+@ stdcall DrawDibEnd(long)
+@ stub    DrawDibGetBuffer
+@ stdcall DrawDibGetPalette(long)
+@ stdcall DrawDibOpen()
+@ stdcall DrawDibProfileDisplay(ptr)
+@ stdcall DrawDibRealize(long long long)
+@ stdcall DrawDibSetPalette(long long)
+@ stdcall DrawDibStart(long long)
+@ stdcall DrawDibStop(long)
+@ stub    DrawDibTime
+@ stub    GetOpenFileNamePreview
+@ stdcall GetOpenFileNamePreviewA(ptr)
+@ stdcall GetOpenFileNamePreviewW(ptr)
+@ stdcall GetSaveFileNamePreviewA(ptr)
+@ stdcall GetSaveFileNamePreviewW(ptr)
+@ stdcall ICClose(long)
+@ cdecl   ICCompress(long long ptr ptr ptr ptr ptr ptr long long long ptr ptr)
+@ stdcall ICCompressorChoose(long long ptr ptr ptr ptr)
+@ stdcall ICCompressorFree(ptr)
+@ cdecl   ICDecompress(long long ptr ptr ptr ptr)
+@ cdecl   ICDraw(long long ptr ptr long long)
+@ cdecl   ICDrawBegin(long long long long long long long long long ptr long long long long long long)
+@ stdcall ICGetDisplayFormat(long ptr ptr long long long)
+@ stdcall ICGetInfo(long ptr long)
+@ stdcall ICImageCompress(long long ptr ptr ptr long ptr)
+@ stdcall ICImageDecompress(long long ptr ptr ptr)
+@ stdcall ICInfo(long long ptr)
+@ stdcall ICInstall(long long ptr str long)
+@ stdcall ICLocate(long long ptr ptr long)
+@ stub    ICMThunk
+@ stdcall ICOpen(long long long)
+@ stdcall ICOpenFunction(long long long ptr)
+@ stdcall ICRemove(long long long)
+@ stdcall ICSendMessage(long long long long)
+@ stub    ICSeqCompressFrame
+@ stub    ICSeqCompressFrameEnd
+@ stub    ICSeqCompressFrameStart
+@ cdecl   MCIWndCreate (long long long str) MCIWndCreateA
+@ cdecl   MCIWndCreateA (long long long str)
+@ cdecl   MCIWndCreateW (long long long wstr)
+@ cdecl   MCIWndRegisterClass()
+@ stub    StretchDIB
diff --git a/reactos/dll/msvideo/msvfw32.xml b/reactos/dll/msvideo/msvfw32.xml
new file mode 100644 (file)
index 0000000..d8bc0c9
--- /dev/null
@@ -0,0 +1,20 @@
+<module name="msvfw32" type="win32dll" baseaddress="${BASEADDRESS_MSVFW32}" installbase="system32" installname="msvfw32.dll">
+       <importlibrary definition="msvfw32.spec.def" />
+       <include base="ReactOS">include/wine</include>
+       <define name="__REACTOS__" />
+       <define name="__USE_W32API" />
+       <define name="_WIN32_IE">0x600</define>
+       <define name="_WIN32_WINNT">0x501</define>
+       <define name="WINVER">0x501</define>
+       <library>winmm</library>
+       <library>gdi32</library>
+       <library>comctl32</library>
+       <library>kernel32</library>
+       <library>ntdll</library>
+    <library>wine</library>
+       <file>drawdib.c</file>
+       <file>mciwnd.c</file>
+       <file>msvfw32.rc</file>
+       <file>msvideo_main.c</file>
+       <file>msvfw32.spec</file>
+</module>
diff --git a/reactos/dll/msvideo/msvideo.spec b/reactos/dll/msvideo/msvideo.spec
new file mode 100644 (file)
index 0000000..a6f9a87
--- /dev/null
@@ -0,0 +1,68 @@
+2 pascal VideoForWindowsVersion() VideoForWindowsVersion
+20 stub VIDEOGETNUMDEVS
+21 stub VIDEOGETERRORTEXT
+22 pascal VideoCapDriverDescAndVer(word ptr word ptr word) VideoCapDriverDescAndVer16
+28 stub VIDEOOPEN
+29 stub VIDEOCLOSE
+30 stub VIDEODIALOG
+31 stub VIDEOFRAME
+32 stub VIDEOCONFIGURE
+33 stub VIDEOCONFIGURESTORAGE
+34 stub VIDEOGETCHANNELCAPS
+35 stub VIDEOUPDATE
+40 stub VIDEOSTREAMADDBUFFER
+41 stub VIDEOSTREAMFINI
+42 stub VIDEOSTREAMGETERROR
+43 stub VIDEOSTREAMGETPOSITION
+44 stub VIDEOSTREAMINIT
+46 stub VIDEOSTREAMPREPAREHEADER
+47 stub VIDEOSTREAMRESET
+49 stub VIDEOSTREAMSTART
+50 stub VIDEOSTREAMSTOP
+51 stub VIDEOSTREAMUNPREPAREHEADER
+52 stub VIDEOSTREAMALLOCHDRANDBUFFER
+53 stub VIDEOSTREAMFREEHDRANDBUFFER
+60 stub VIDEOMESSAGE
+102 pascal -ret16 DrawDibOpen() DrawDibOpen16
+103 pascal -ret16 DrawDibClose(word) DrawDibClose16
+104 pascal -ret16 DrawDibBegin(word word s_word s_word ptr s_word s_word word) DrawDibBegin16
+105 pascal -ret16 DrawDibEnd(word) DrawDibEnd16
+106 pascal -ret16 DrawDibDraw(word word s_word s_word s_word s_word ptr ptr s_word s_word s_word s_word word) DrawDibDraw16
+108 pascal -ret16 DrawDibGetPalette(word) DrawDibGetPalette16
+110 pascal -ret16 DrawDibSetPalette(word word) DrawDibSetPalette16
+111 stub DRAWDIBCHANGEPALETTE
+112 pascal -ret16 DrawDibRealize(word word word) DrawDibRealize16
+113 stub DRAWDIBTIME
+114 stub DRAWDIBPROFILEDISPLAY
+115 stub STRETCHDIB
+118 pascal -ret16 DrawDibStart(word long) DrawDibStart16
+119 pascal -ret16 DrawDibStop(word) DrawDibStop16
+120 stub DRAWDIBGETBUFFER
+200 pascal -ret16 ICInfo(long long segptr) ICInfo16
+201 stub ICINSTALL
+202 stub ICREMOVE
+203 pascal -ret16 ICOpen(long long word) ICOpen16
+204 pascal ICClose(word) ICClose16
+205 pascal ICSendMessage(word word long long) ICSendMessage16
+206 pascal -ret16 ICOpenFunction(long long word segptr) ICOpenFunction16
+207 varargs _ICMessage(word word word) ICMessage16
+212 pascal ICGetInfo(word segptr long) ICGetInfo16
+213 pascal -ret16 ICLocate(long long ptr ptr word) ICLocate16
+224 cdecl _ICCompress(word long segptr segptr segptr segptr segptr segptr long long long segptr segptr) ICCompress16
+230 cdecl _ICDecompress(word long segptr segptr segptr segptr) ICDecompress16
+232 cdecl _ICDrawBegin(word long word word word s_word s_word s_word s_word segptr s_word s_word s_word s_word long long) ICDrawBegin16
+234 cdecl _ICDraw(word long segptr segptr long long) ICDraw16
+239 pascal -ret16 ICGetDisplayFormat(word ptr ptr s_word s_word s_word) ICGetDisplayFormat16
+240 stub ICIMAGECOMPRESS
+241 stub ICIMAGEDECOMPRESS
+242 stub ICCOMPRESSORCHOOSE
+243 stub ICCOMPRESSORFREE
+244 stub ICSEQCOMPRESSFRAMESTART
+245 stub ICSEQCOMPRESSFRAMEEND
+246 stub ICSEQCOMPRESSFRAME
+250 stub _MCIWNDCREATE
+251 stub _MCIWNDREGISTERCLASS
+252 stub GETOPENFILENAMEPREVIEW
+253 stub GETSAVEFILENAMEPREVIEW
+
+300 pascal DllEntryPoint(long word word word long word) VIDEO_LibMain
diff --git a/reactos/dll/msvideo/msvideo16.c b/reactos/dll/msvideo/msvideo16.c
new file mode 100644 (file)
index 0000000..2d5596e
--- /dev/null
@@ -0,0 +1,910 @@
+/*
+ * msvideo 16-bit functions
+ *
+ * Copyright 1998 Marcus Meissner
+ * Copyright 2000 Bradley Baetz
+ *
+ * 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
+ */
+
+#define COM_NO_WINDOWS_H
+#include <stdio.h>
+#include <string.h>
+
+#include "msvideo_private.h"
+#include "winver.h"
+#include "winnls.h"
+#include "winreg.h"
+#include "vfw16.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msvideo);
+
+/* Drivers32 settings */
+#define HKLM_DRIVERS32 "Software\\Microsoft\\Windows NT\\CurrentVersion\\Drivers32"
+
+/***********************************************************************
+ *             DrawDibOpen             [MSVIDEO.102]
+ */
+HDRAWDIB16 VFWAPI DrawDibOpen16(void)
+{
+    return HDRAWDIB_16(DrawDibOpen());
+}
+
+/***********************************************************************
+ *             DrawDibClose            [MSVIDEO.103]
+ */
+BOOL16 VFWAPI DrawDibClose16(HDRAWDIB16 hdd)
+{
+    return DrawDibClose(HDRAWDIB_32(hdd));
+}
+
+/************************************************************************
+ *             DrawDibBegin            [MSVIDEO.104]
+ */
+BOOL16 VFWAPI DrawDibBegin16(HDRAWDIB16 hdd, HDC16 hdc, INT16 dxDst,
+                            INT16 dyDst, LPBITMAPINFOHEADER lpbi, INT16 dxSrc,
+                            INT16 dySrc, UINT16 wFlags)
+{
+    return DrawDibBegin(HDRAWDIB_32(hdd), HDC_32(hdc), dxDst, dyDst, lpbi,
+                       dxSrc, dySrc, wFlags);
+}
+
+/***********************************************************************
+ *             DrawDibEnd              [MSVIDEO.105]
+ */
+BOOL16 VFWAPI DrawDibEnd16(HDRAWDIB16 hdd)
+{
+    return DrawDibEnd(HDRAWDIB_32(hdd));
+}
+
+/**********************************************************************
+ *             DrawDibDraw             [MSVIDEO.106]
+ */
+BOOL16 VFWAPI DrawDibDraw16(HDRAWDIB16 hdd, HDC16 hdc, INT16 xDst, INT16 yDst,
+                           INT16 dxDst, INT16 dyDst, LPBITMAPINFOHEADER lpbi,
+                           LPVOID lpBits, INT16 xSrc, INT16 ySrc, INT16 dxSrc,
+                           INT16 dySrc, UINT16 wFlags)
+{
+    return DrawDibDraw(HDRAWDIB_32(hdd), HDC_32(hdc), xDst, yDst, dxDst,
+                      dyDst, lpbi, lpBits, xSrc, ySrc, dxSrc, dySrc, wFlags);
+}
+
+/***********************************************************************
+ *              DrawDibGetPalette       [MSVIDEO.108]
+ */
+HPALETTE16 VFWAPI DrawDibGetPalette16(HDRAWDIB16 hdd)
+{
+    return HPALETTE_16(DrawDibGetPalette(HDRAWDIB_32(hdd)));
+}
+
+/***********************************************************************
+ *              DrawDibSetPalette       [MSVIDEO.110]
+ */
+BOOL16 VFWAPI DrawDibSetPalette16(HDRAWDIB16 hdd, HPALETTE16 hpal)
+{
+    return DrawDibSetPalette(HDRAWDIB_32(hdd), HPALETTE_32(hpal));
+}
+
+/***********************************************************************
+ *              DrawDibRealize          [MSVIDEO.112]
+ */
+UINT16 VFWAPI DrawDibRealize16(HDRAWDIB16 hdd, HDC16 hdc,
+                              BOOL16 fBackground)
+{
+    return (UINT16)DrawDibRealize(HDRAWDIB_32(hdd), HDC_32(hdc), fBackground);
+}
+
+/*************************************************************************
+ *             DrawDibStart            [MSVIDEO.118]
+ */
+BOOL16 VFWAPI DrawDibStart16(HDRAWDIB16 hdd, DWORD rate)
+{
+    return DrawDibStart(HDRAWDIB_32(hdd), rate);
+}
+
+/*************************************************************************
+ *             DrawDibStop             [MSVIDEO.119]
+ */
+BOOL16 DrawDibStop16(HDRAWDIB16 hdd)
+{
+    return DrawDibStop(HDRAWDIB_32(hdd));
+}
+
+/***********************************************************************
+ *             ICOpen                          [MSVIDEO.203]
+ */
+HIC16 VFWAPI ICOpen16(DWORD fccType, DWORD fccHandler, UINT16 wMode)
+{
+    return HIC_16(ICOpen(fccType, fccHandler, wMode));
+}
+
+/***********************************************************************
+ *             ICClose                 [MSVIDEO.204]
+ */
+LRESULT WINAPI ICClose16(HIC16 hic)
+{
+    return ICClose(HIC_32(hic));
+}
+
+/***********************************************************************
+ *             _ICMessage                      [MSVIDEO.207]
+ */
+LRESULT VFWAPIV ICMessage16( HIC16 hic, UINT16 msg, UINT16 cb, VA_LIST16 valist )
+{
+    LPWORD lpData;
+    SEGPTR segData;
+    LRESULT ret;
+    UINT16 i;
+
+    lpData = HeapAlloc(GetProcessHeap(), 0, cb);
+
+    TRACE("0x%08lx, %u, %u, ...)\n", (DWORD) hic, msg, cb);
+
+    for (i = 0; i < cb / sizeof(WORD); i++) 
+    {
+       lpData[i] = VA_ARG16(valist, WORD);
+    }
+
+    segData = MapLS(lpData);
+    ret = ICSendMessage16(hic, msg, segData, (DWORD) cb);
+    UnMapLS(segData);
+    HeapFree(GetProcessHeap(), 0, lpData);
+    return ret;
+}
+
+/***********************************************************************
+ *             ICGetInfo                       [MSVIDEO.212]
+ */
+LRESULT VFWAPI ICGetInfo16(HIC16 hic, ICINFO16 * picinfo, DWORD cb)
+{
+    LRESULT ret;
+
+    TRACE("(0x%08lx,%p,%ld)\n", (DWORD) hic, picinfo, cb);
+    ret = ICSendMessage16(hic, ICM_GETINFO, (DWORD) picinfo, cb);
+    TRACE("    -> 0x%08lx\n", ret);
+    return ret;
+}
+
+/***********************************************************************
+ *             ICLocate                        [MSVIDEO.213]
+ */
+HIC16 VFWAPI ICLocate16(DWORD fccType, DWORD fccHandler,
+                       LPBITMAPINFOHEADER lpbiIn, LPBITMAPINFOHEADER lpbiOut,
+                       WORD wFlags)
+{
+    return HIC_16(ICLocate(fccType, fccHandler, lpbiIn, lpbiOut, wFlags));
+}
+
+/***********************************************************************
+ *             _ICCompress                     [MSVIDEO.224]
+ */
+DWORD VFWAPIV ICCompress16(HIC16 hic, DWORD dwFlags,
+                          LPBITMAPINFOHEADER lpbiOutput, LPVOID lpData,
+                          LPBITMAPINFOHEADER lpbiInput, LPVOID lpBits,
+                          LPDWORD lpckid, LPDWORD lpdwFlags,
+                          LONG lFrameNum, DWORD dwFrameSize,
+                          DWORD dwQuality, LPBITMAPINFOHEADER lpbiPrev,
+                          LPVOID lpPrev)
+{
+    DWORD ret;
+    ICCOMPRESS iccmp;
+    SEGPTR seg_iccmp;
+    
+    TRACE("(0x%08lx,%ld,%p,%p,%p,%p,...)\n", (DWORD) hic, dwFlags,
+         lpbiOutput, lpData, lpbiInput, lpBits);
+
+    iccmp.dwFlags = dwFlags;
+
+    iccmp.lpbiOutput = lpbiOutput;
+    iccmp.lpOutput = lpData;
+    iccmp.lpbiInput = lpbiInput;
+    iccmp.lpInput = lpBits;
+
+    iccmp.lpckid = lpckid;
+    iccmp.lpdwFlags = lpdwFlags;
+    iccmp.lFrameNum = lFrameNum;
+    iccmp.dwFrameSize = dwFrameSize;
+    iccmp.dwQuality = dwQuality;
+    iccmp.lpbiPrev = lpbiPrev;
+    iccmp.lpPrev = lpPrev;
+    seg_iccmp = MapLS(&iccmp);
+    ret = ICSendMessage16(hic, ICM_COMPRESS, seg_iccmp, sizeof(ICCOMPRESS));
+    UnMapLS(seg_iccmp);
+    return ret;
+}
+
+/***********************************************************************
+ *             _ICDecompress                   [MSVIDEO.230]
+ */
+DWORD VFWAPIV ICDecompress16(HIC16 hic, DWORD dwFlags,
+                            LPBITMAPINFOHEADER lpbiFormat, LPVOID lpData,
+                            LPBITMAPINFOHEADER lpbi, LPVOID lpBits)
+{
+    ICDECOMPRESS icd;
+    SEGPTR segptr;
+    DWORD ret;
+
+    TRACE("(0x%08lx,%ld,%p,%p,%p,%p)\n", (DWORD) hic, dwFlags, lpbiFormat,
+         lpData, lpbi, lpBits);
+
+    icd.dwFlags = dwFlags;
+    icd.lpbiInput = lpbiFormat;
+    icd.lpInput = lpData;
+    icd.lpbiOutput = lpbi;
+    icd.lpOutput = lpBits;
+    icd.ckid = 0;
+    segptr = MapLS(&icd);
+    ret = ICSendMessage16(hic, ICM_DECOMPRESS, segptr, sizeof(ICDECOMPRESS));
+    UnMapLS(segptr);
+    return ret;
+}
+
+/***********************************************************************
+ *             _ICDrawBegin            [MSVIDEO.232]
+ */
+DWORD VFWAPIV ICDrawBegin16(HIC16 hic,         /* [in] */
+                           DWORD dwFlags,      /* [in] flags */
+                           HPALETTE16 hpal,    /* [in] palette to draw with */
+                           HWND16 hwnd,        /* [in] window to draw to */
+                           HDC16 hdc,          /* [in] HDC to draw to */
+                           INT16 xDst,         /* [in] destination rectangle */
+                           INT16 yDst,         /* [in] */
+                           INT16 dxDst,        /* [in] */
+                           INT16 dyDst,        /* [in] */
+                           LPBITMAPINFOHEADER lpbi,    /* [in] format of frame to draw NOTE: SEGPTR */
+                           INT16 xSrc,         /* [in] source rectangle */
+                           INT16 ySrc,         /* [in] */
+                           INT16 dxSrc,        /* [in] */
+                           INT16 dySrc,        /* [in] */
+                           DWORD dwRate,       /* [in] frames/second = (dwRate/dwScale) */
+                           DWORD dwScale)      /* [in] */
+{
+    DWORD ret;
+    ICDRAWBEGIN16 icdb;
+    SEGPTR seg_icdb;
+
+    TRACE ("(0x%08lx,%ld,0x%08lx,0x%08lx,0x%08lx,%u,%u,%u,%u,%p,%u,%u,%u,%u,%ld,%ld)\n",
+          (DWORD) hic, dwFlags, (DWORD) hpal, (DWORD) hwnd, (DWORD) hdc,
+          xDst, yDst, dxDst, dyDst, lpbi, xSrc, ySrc, dxSrc, dySrc, dwRate,
+          dwScale);
+
+    icdb.dwFlags = dwFlags;
+    icdb.hpal = hpal;
+    icdb.hwnd = hwnd;
+    icdb.hdc = hdc;
+    icdb.xDst = xDst;
+    icdb.yDst = yDst;
+    icdb.dxDst = dxDst;
+    icdb.dyDst = dyDst;
+    icdb.lpbi = lpbi;          /* Keep this as SEGPTR for the mapping code to deal with */
+    icdb.xSrc = xSrc;
+    icdb.ySrc = ySrc;
+    icdb.dxSrc = dxSrc;
+    icdb.dySrc = dySrc;
+    icdb.dwRate = dwRate;
+    icdb.dwScale = dwScale;
+    seg_icdb = MapLS(&icdb);
+    ret = (DWORD) ICSendMessage16(hic, ICM_DRAW_BEGIN, seg_icdb,
+                                 sizeof(ICDRAWBEGIN16));
+    UnMapLS(seg_icdb);
+    return ret;
+}
+
+/***********************************************************************
+ *             _ICDraw                 [MSVIDEO.234]
+ */
+DWORD VFWAPIV ICDraw16(HIC16 hic, DWORD dwFlags,
+                      LPVOID lpFormat, /* [???] NOTE: SEGPTR */
+                      LPVOID lpData,   /* [???] NOTE: SEGPTR */
+                      DWORD cbData, LONG lTime)
+{
+    DWORD ret;
+    ICDRAW icd;
+    SEGPTR seg_icd;
+
+    TRACE("(0x%08lx,0x%08lx,%p,%p,%ld,%ld)\n", (DWORD) hic, dwFlags,
+         lpFormat, lpData, cbData, lTime);
+    icd.dwFlags = dwFlags;
+    icd.lpFormat = lpFormat;
+    icd.lpData = lpData;
+    icd.cbData = cbData;
+    icd.lTime = lTime;
+    seg_icd = MapLS(&icd);
+    ret = ICSendMessage16(hic, ICM_DRAW, seg_icd, sizeof(ICDRAW));
+    UnMapLS(seg_icd);
+    return ret;
+}
+
+/***********************************************************************
+ *             ICGetDisplayFormat                      [MSVIDEO.239]
+ */
+HIC16 VFWAPI ICGetDisplayFormat16(HIC16 hic, LPBITMAPINFOHEADER lpbiIn,
+                                 LPBITMAPINFOHEADER lpbiOut, INT16 depth,
+                                 INT16 dx, INT16 dy)
+{
+    return HIC_16(ICGetDisplayFormat(HIC_32(hic), lpbiIn, lpbiOut, depth,
+                                    dx, dy));
+}
+
+#define COPY(x,y) (x->y = x##16->y);
+#define COPYPTR(x,y) (x->y = MapSL((SEGPTR)x##16->y));
+
+/******************************************************************
+ *             MSVIDEO_MapICDEX16To32
+ *
+ *
+ */
+static LPVOID MSVIDEO_MapICDEX16To32(LPDWORD lParam) 
+{
+    LPVOID ret;
+
+    ICDECOMPRESSEX *icdx = HeapAlloc(GetProcessHeap(), 0, sizeof(ICDECOMPRESSEX));
+    ICDECOMPRESSEX16 *icdx16 = MapSL(*lParam);
+    ret = icdx16;
+
+    COPY(icdx, dwFlags);
+    COPYPTR(icdx, lpbiSrc);
+    COPYPTR(icdx, lpSrc);
+    COPYPTR(icdx, lpbiDst);
+    COPYPTR(icdx, lpDst);
+    COPY(icdx, xDst);
+    COPY(icdx, yDst);
+    COPY(icdx, dxDst);
+    COPY(icdx, dyDst);
+    COPY(icdx, xSrc);
+    COPY(icdx, ySrc);
+    COPY(icdx, dxSrc);
+    COPY(icdx, dySrc);
+
+    *lParam = (DWORD)(icdx);
+    return ret;
+}
+
+/******************************************************************
+ *             MSVIDEO_MapMsg16To32
+ *
+ *
+ */
+static LPVOID MSVIDEO_MapMsg16To32(UINT msg, LPDWORD lParam1, LPDWORD lParam2)
+{
+    LPVOID ret = 0;
+
+    TRACE("Mapping %d\n", msg);
+
+    switch (msg) 
+    {
+    case DRV_LOAD:
+    case DRV_ENABLE:
+    case DRV_CLOSE:
+    case DRV_DISABLE:
+    case DRV_FREE:
+    case ICM_ABOUT:
+    case ICM_CONFIGURE:
+    case ICM_COMPRESS_END:
+    case ICM_DECOMPRESS_END:
+    case ICM_DECOMPRESSEX_END:
+    case ICM_SETQUALITY:
+    case ICM_DRAW_START_PLAY:
+    case ICM_DRAW_STOP_PLAY:
+    case ICM_DRAW_REALIZE:
+    case ICM_DRAW_RENDERBUFFER:
+    case ICM_DRAW_END:
+        break;
+    case DRV_OPEN:
+    case ICM_GETDEFAULTQUALITY:
+    case ICM_GETQUALITY:
+    case ICM_SETSTATE:
+    case ICM_DRAW_WINDOW:
+    case ICM_GETBUFFERSWANTED:
+        *lParam1 = (DWORD)MapSL(*lParam1);
+        break;
+    case ICM_GETINFO:
+        {
+            ICINFO *ici = HeapAlloc(GetProcessHeap(), 0, sizeof(ICINFO));
+            ICINFO16 *ici16;
+            
+            ici16 = MapSL(*lParam1);
+            ret = ici16;
+            
+            ici->dwSize = sizeof(ICINFO);
+            COPY(ici, fccType);
+            COPY(ici, fccHandler);
+            COPY(ici, dwFlags);
+            COPY(ici, dwVersion);
+            COPY(ici, dwVersionICM);
+            MultiByteToWideChar( CP_ACP, 0, ici16->szName, -1, ici->szName, 16 );
+            MultiByteToWideChar( CP_ACP, 0, ici16->szDescription, -1, ici->szDescription, 128 );
+            MultiByteToWideChar( CP_ACP, 0, ici16->szDriver, -1, ici->szDriver, 128 );
+            *lParam1 = (DWORD)(ici);
+            *lParam2 = sizeof(ICINFO);
+        }
+        break;
+    case ICM_COMPRESS:
+        {
+            ICCOMPRESS *icc = HeapAlloc(GetProcessHeap(), 0, sizeof(ICCOMPRESS));
+            ICCOMPRESS *icc16;
+
+            icc16 = MapSL(*lParam1);
+            ret = icc16;
+
+            COPY(icc, dwFlags);
+            COPYPTR(icc, lpbiOutput);
+            COPYPTR(icc, lpOutput);
+            COPYPTR(icc, lpbiInput);
+            COPYPTR(icc, lpInput);
+            COPYPTR(icc, lpckid);
+            COPYPTR(icc, lpdwFlags);
+            COPY(icc, lFrameNum);
+            COPY(icc, dwFrameSize);
+            COPY(icc, dwQuality);
+            COPYPTR(icc, lpbiPrev);
+            COPYPTR(icc, lpPrev);
+
+            *lParam1 = (DWORD)(icc);
+            *lParam2 = sizeof(ICCOMPRESS);
+        }
+        break;
+    case ICM_DECOMPRESS:
+        {
+            ICDECOMPRESS *icd = HeapAlloc(GetProcessHeap(), 0, sizeof(ICDECOMPRESS));
+            ICDECOMPRESS *icd16; /* Same structure except for the pointers */
+
+            icd16 = MapSL(*lParam1);
+            ret = icd16;
+
+            COPY(icd, dwFlags);
+            COPYPTR(icd, lpbiInput);
+            COPYPTR(icd, lpInput);
+            COPYPTR(icd, lpbiOutput);
+            COPYPTR(icd, lpOutput);
+            COPY(icd, ckid);
+
+            *lParam1 = (DWORD)(icd);
+            *lParam2 = sizeof(ICDECOMPRESS);
+        }
+        break;
+    case ICM_COMPRESS_BEGIN:
+    case ICM_COMPRESS_GET_FORMAT:
+    case ICM_COMPRESS_GET_SIZE:
+    case ICM_COMPRESS_QUERY:
+    case ICM_DECOMPRESS_GET_FORMAT:
+    case ICM_DECOMPRESS_QUERY:
+    case ICM_DECOMPRESS_BEGIN:
+    case ICM_DECOMPRESS_SET_PALETTE:
+    case ICM_DECOMPRESS_GET_PALETTE:
+        *lParam1 = (DWORD)MapSL(*lParam1);
+        *lParam2 = (DWORD)MapSL(*lParam2);
+        break;
+    case ICM_DECOMPRESSEX_QUERY:
+        if ((*lParam2 != sizeof(ICDECOMPRESSEX16)) && (*lParam2 != 0))
+            WARN("*lParam2 has unknown value %p\n", (ICDECOMPRESSEX16*)*lParam2);
+        /* FIXME: *lParm2 is meant to be 0 or an ICDECOMPRESSEX16*, but is sizeof(ICDECOMRPESSEX16)
+         * This is because of ICMessage(). Special case it?
+         {
+         LPVOID* addr = HeapAlloc(GetProcessHeap(), 0, 2*sizeof(LPVOID));
+         addr[0] = MSVIDEO_MapICDEX16To32(lParam1);
+         if (*lParam2)
+         addr[1] = MSVIDEO_MapICDEX16To32(lParam2);
+         else
+         addr[1] = 0;
+         
+         ret = addr;
+         }
+         break;*/
+    case ICM_DECOMPRESSEX_BEGIN:
+    case ICM_DECOMPRESSEX:
+        ret = MSVIDEO_MapICDEX16To32(lParam1);
+        *lParam2 = sizeof(ICDECOMPRESSEX);
+        break;
+    case ICM_DRAW_BEGIN:
+        {
+            ICDRAWBEGIN *icdb = HeapAlloc(GetProcessHeap(), 0, sizeof(ICDRAWBEGIN));
+            ICDRAWBEGIN16 *icdb16 = MapSL(*lParam1);
+            ret = icdb16;
+
+            COPY(icdb, dwFlags);
+            icdb->hpal = HPALETTE_32(icdb16->hpal);
+            icdb->hwnd = HWND_32(icdb16->hwnd);
+            icdb->hdc = HDC_32(icdb16->hdc);
+            COPY(icdb, xDst);
+            COPY(icdb, yDst);
+            COPY(icdb, dxDst);
+            COPY(icdb, dyDst);
+            COPYPTR(icdb, lpbi);
+            COPY(icdb, xSrc);
+            COPY(icdb, ySrc);
+            COPY(icdb, dxSrc);
+            COPY(icdb, dySrc);
+            COPY(icdb, dwRate);
+            COPY(icdb, dwScale);
+
+            *lParam1 = (DWORD)(icdb);
+            *lParam2 = sizeof(ICDRAWBEGIN);
+        }
+        break;
+    case ICM_DRAW_SUGGESTFORMAT:
+        {
+            ICDRAWSUGGEST *icds = HeapAlloc(GetProcessHeap(), 0, sizeof(ICDRAWSUGGEST));
+            ICDRAWSUGGEST16 *icds16 = MapSL(*lParam1);
+
+            ret = icds16;
+
+            COPY(icds, dwFlags);
+            COPYPTR(icds, lpbiIn);
+            COPYPTR(icds, lpbiSuggest);
+            COPY(icds, dxSrc);
+            COPY(icds, dySrc);
+            COPY(icds, dxDst);
+            COPY(icds, dyDst);
+            icds->hicDecompressor = HIC_32(icds16->hicDecompressor);
+
+            *lParam1 = (DWORD)(icds);
+            *lParam2 = sizeof(ICDRAWSUGGEST);
+        }
+        break;
+    case ICM_DRAW:
+        {
+            ICDRAW *icd = HeapAlloc(GetProcessHeap(), 0, sizeof(ICDRAW));
+            ICDRAW *icd16 = MapSL(*lParam1);
+            ret = icd16;
+
+            COPY(icd, dwFlags);
+            COPYPTR(icd, lpFormat);
+            COPYPTR(icd, lpData);
+            COPY(icd, cbData);
+            COPY(icd, lTime);
+
+            *lParam1 = (DWORD)(icd);
+            *lParam2 = sizeof(ICDRAW);
+        }
+        break;
+    case ICM_DRAW_START:
+    case ICM_DRAW_STOP:
+        break;
+    default:
+        FIXME("%d is not yet handled. Expect a crash.\n", msg);
+    }
+    return ret;
+}
+
+#undef COPY
+#undef COPYPTR
+
+/******************************************************************
+ *             MSVIDEO_UnmapMsg16To32
+ *
+ *
+ */
+static void MSVIDEO_UnmapMsg16To32(UINT msg, LPVOID data16, LPDWORD lParam1, LPDWORD lParam2)
+{
+    TRACE("Unmapping %d\n", msg);
+
+#define UNCOPY(x, y) (x##16->y = x->y);
+
+    switch (msg) 
+    {
+    case ICM_GETINFO:
+        {
+            ICINFO *ici = (ICINFO*)(*lParam1);
+            ICINFO16 *ici16 = (ICINFO16*)data16;
+
+            UNCOPY(ici, fccType);
+            UNCOPY(ici, fccHandler);
+            UNCOPY(ici, dwFlags);
+            UNCOPY(ici, dwVersion);
+            UNCOPY(ici, dwVersionICM);
+            WideCharToMultiByte( CP_ACP, 0, ici->szName, -1, ici16->szName, 
+                                 sizeof(ici16->szName), NULL, NULL );
+            ici16->szName[sizeof(ici16->szName)-1] = 0;
+            WideCharToMultiByte( CP_ACP, 0, ici->szDescription, -1, ici16->szDescription, 
+                                 sizeof(ici16->szDescription), NULL, NULL );
+            ici16->szDescription[sizeof(ici16->szDescription)-1] = 0;
+            /* This just gives garbage for some reason - BB
+               lstrcpynWtoA(ici16->szDriver, ici->szDriver, 128);*/
+
+            HeapFree(GetProcessHeap(), 0, ici);
+        }
+        break;
+    case ICM_DECOMPRESS_QUERY:
+        /*{
+          LPVOID* x = data16;
+          HeapFree(GetProcessHeap(), 0, x[0]);
+          if (x[1])
+          HeapFree(GetProcessHeap(), 0, x[1]);
+          }
+          break;*/
+    case ICM_COMPRESS:
+    case ICM_DECOMPRESS:
+    case ICM_DECOMPRESSEX_QUERY:
+    case ICM_DECOMPRESSEX_BEGIN:
+    case ICM_DECOMPRESSEX:
+    case ICM_DRAW_BEGIN:
+    case ICM_DRAW_SUGGESTFORMAT:
+    case ICM_DRAW:
+        HeapFree(GetProcessHeap(), 0, data16);
+        break;
+    default:
+        ERR("Unmapping unmapped msg %d\n", msg);
+    }
+#undef UNCOPY
+}
+
+/***********************************************************************
+ *             ICInfo                          [MSVIDEO.200]
+ */
+BOOL16 VFWAPI ICInfo16(DWORD fccType, DWORD fccHandler, ICINFO16 *lpicinfo)
+{
+    BOOL16 ret;
+    LPVOID lpv;
+    DWORD lParam = (DWORD)lpicinfo;
+    DWORD size = ((ICINFO*)(MapSL((SEGPTR)lpicinfo)))->dwSize;
+    
+    /* Use the mapping functions to map the ICINFO structure */
+    lpv = MSVIDEO_MapMsg16To32(ICM_GETINFO, &lParam, &size);
+
+    ret = ICInfo(fccType, fccHandler, (ICINFO*)lParam);
+
+    MSVIDEO_UnmapMsg16To32(ICM_GETINFO, lpv, &lParam, &size);
+
+    return ret;
+}
+
+/******************************************************************
+ *             IC_Callback3216
+ *
+ *
+ */
+static  LRESULT CALLBACK  IC_Callback3216(HIC hic, HDRVR hdrv, UINT msg, DWORD lp1, DWORD lp2)
+{
+    WINE_HIC*   whic;
+    LRESULT     ret = 0;
+    WORD args[8];
+
+    whic = MSVIDEO_GetHicPtr(hic);
+    if (whic)
+    {
+        switch (msg)
+        {
+        case DRV_OPEN:
+            lp2 = (DWORD)MapLS((void*)lp2);
+            break;
+        }
+        args[7] = HIWORD(hic);
+        args[6] = LOWORD(hic);
+        args[5] = HDRVR_16(whic->hdrv);
+        args[4] = msg;
+        args[3] = HIWORD(lp1);
+        args[2] = LOWORD(lp1);
+        args[1] = HIWORD(lp2);
+        args[0] = LOWORD(lp2);
+        WOWCallback16Ex( (DWORD)whic->driverproc16, WCB16_PASCAL, sizeof(args), args, &ret );
+
+        switch (msg)
+        {
+        case DRV_OPEN:
+            UnMapLS(lp2);
+            break;
+        }
+    }
+    else ret = ICERR_BADHANDLE;
+    return ret;
+}
+
+/***********************************************************************
+ *             ICOpenFunction                  [MSVIDEO.206]
+ */
+HIC16 VFWAPI ICOpenFunction16(DWORD fccType, DWORD fccHandler, UINT16 wMode, FARPROC16 lpfnHandler)
+{
+    HIC         hic32;
+
+    hic32 = MSVIDEO_OpenFunction(fccType, fccHandler, wMode, 
+                                 (DRIVERPROC)IC_Callback3216, (DWORD)lpfnHandler);
+    return HIC_16(hic32);
+}
+
+/***********************************************************************
+ *             ICSendMessage                   [MSVIDEO.205]
+ */
+LRESULT VFWAPI ICSendMessage16(HIC16 hic, UINT16 msg, DWORD lParam1, DWORD lParam2) 
+{
+    LRESULT     ret = ICERR_BADHANDLE;
+    WINE_HIC*   whic;
+
+    whic = MSVIDEO_GetHicPtr(HIC_32(hic));
+    if (whic)
+    {
+        /* we've got a 16 bit driver proc... call it directly */
+        if (whic->driverproc16)
+        {
+            WORD args[8];
+
+            /* FIXME: original code was passing hdrv first and hic second */
+            /* but this doesn't match what IC_Callback3216 does */
+            args[7] = HIWORD(hic);
+            args[6] = LOWORD(hic);
+            args[5] = HDRVR_16(whic->hdrv);
+            args[4] = msg;
+            args[3] = HIWORD(lParam1);
+            args[2] = LOWORD(lParam1);
+            args[1] = HIWORD(lParam2);
+            args[0] = LOWORD(lParam2);
+            WOWCallback16Ex( (DWORD)whic->driverproc16, WCB16_PASCAL, sizeof(args), args, &ret );
+        }
+        else
+        {
+            /* map the message for a 32 bit infrastructure, and pass it along */
+            void*       data16 = MSVIDEO_MapMsg16To32(msg, &lParam1, &lParam2);
+    
+            ret = MSVIDEO_SendMessage(whic, msg, lParam1, lParam2);
+            if (data16)
+                MSVIDEO_UnmapMsg16To32(msg, data16, &lParam1, &lParam2);
+        }
+    }
+    return ret;
+}
+
+/***********************************************************************
+ *             VideoCapDriverDescAndVer        [MSVIDEO.22]
+ */
+DWORD WINAPI VideoCapDriverDescAndVer16(WORD nr, LPSTR buf1, WORD buf1len,
+                                        LPSTR buf2, WORD buf2len)
+{
+    DWORD      verhandle;
+    DWORD      infosize;
+    UINT       subblocklen;
+    char       *s, buf[2048], fn[260];
+    LPBYTE     infobuf;
+    LPVOID     subblock;
+    DWORD      i, cnt = 0, lRet;
+    DWORD      bufLen, fnLen;
+    FILETIME   lastWrite;
+    HKEY       hKey;
+    BOOL        found = FALSE;
+
+    TRACE("(%d,%p,%d,%p,%d)\n", nr, buf1, buf1len, buf2, buf2len);
+    lRet = RegOpenKeyExA(HKEY_LOCAL_MACHINE, HKLM_DRIVERS32, 0, KEY_QUERY_VALUE, &hKey);
+    if (lRet == ERROR_SUCCESS) 
+    {
+       RegQueryInfoKeyA( hKey, 0, 0, 0, &cnt, 0, 0, 0, 0, 0, 0, 0);
+       for (i = 0; i < cnt; i++) 
+       {
+           bufLen = sizeof(buf) / sizeof(buf[0]);
+           lRet = RegEnumKeyExA(hKey, i, buf, &bufLen, 0, 0, 0, &lastWrite);
+           if (lRet != ERROR_SUCCESS) continue;
+           if (strncasecmp(buf, "vid", 3)) continue;
+           if (nr--) continue;
+           fnLen = sizeof(fn);
+           lRet = RegQueryValueExA(hKey, buf, 0, 0, fn, &fnLen);
+           if (lRet == ERROR_SUCCESS) found = TRUE;
+           break;
+       }
+       RegCloseKey( hKey );
+    } 
+
+    /* search system.ini if not found in the registry */
+    if (!found && GetPrivateProfileStringA("drivers32", NULL, NULL, buf, sizeof(buf), "system.ini"))
+    {
+       for (s = buf; *s; s += strlen(s) + 1)
+       {
+           if (strncasecmp(s, "vid", 3)) continue;
+           if (nr--) continue;
+           if (GetPrivateProfileStringA("drivers32", s, NULL, fn, sizeof(fn), "system.ini"))
+               found = TRUE;
+           break;
+       }
+    }
+
+    if (nr || !found) 
+    {
+        TRACE("No more VID* entries found nr=%d\n", nr);
+        return 20;
+    }
+    infosize = GetFileVersionInfoSizeA(fn, &verhandle);
+    if (!infosize) 
+    {
+        TRACE("%s has no fileversioninfo.\n", fn);
+        return 18;
+    }
+    infobuf = HeapAlloc(GetProcessHeap(), 0, infosize);
+    if (GetFileVersionInfoA(fn, verhandle, infosize, infobuf)) 
+    {
+        char   vbuf[200];
+        /* Yes, two space behind : */
+        /* FIXME: test for buflen */
+        sprintf(vbuf, "Version:  %d.%d.%d.%d\n", 
+                ((WORD*)infobuf)[0x0f],
+                ((WORD*)infobuf)[0x0e],
+                ((WORD*)infobuf)[0x11],
+                ((WORD*)infobuf)[0x10]
+           );
+        TRACE("version of %s is %s\n", fn, vbuf);
+        strncpy(buf2, vbuf, buf2len);
+    }
+    else 
+    {
+        TRACE("GetFileVersionInfoA failed for %s.\n", fn);
+        strncpy(buf2, fn, buf2len); /* msvideo.dll appears to copy fn*/
+    }
+    /* FIXME: language problem? */
+    if (VerQueryValueA(        infobuf,
+                        "\\StringFileInfo\\040904E4\\FileDescription",
+                        &subblock,
+                        &subblocklen
+            )) 
+    {
+        TRACE("VQA returned %s\n", (LPCSTR)subblock);
+        strncpy(buf1, subblock, buf1len);
+    }
+    else 
+    {
+        TRACE("VQA did not return on query \\StringFileInfo\\040904E4\\FileDescription?\n");
+        strncpy(buf1, fn, buf1len); /* msvideo.dll appears to copy fn*/
+    }
+    HeapFree(GetProcessHeap(), 0, infobuf);
+    return 0;
+}
+
+/******************************************************************
+ *             IC_CallTo16
+ *
+ *
+ */
+static  LRESULT CALLBACK IC_CallTo16(HDRVR hdrv, HIC hic, UINT msg, LPARAM lp1, LPARAM lp2)
+{
+#if 0
+    WINE_HIC*   whic = IC_GetPtr(hic);
+    LRESULT     ret = 0;
+    
+    
+    if (whic->driverproc) 
+    {
+        ret = whic->driverproc(hic, whic->hdrv, msg, lParam1, lParam2);
+    }
+    else
+    {
+        ret = SendDriverMessage(whic->hdrv, msg, lParam1, lParam2);
+    }
+#else
+    FIXME("No 32=>16 conversion yet\n");
+#endif
+    return 0;
+}
+
+/**************************************************************************
+ *                      DllEntryPoint (MSVIDEO.300)
+ *
+ * MSVIDEO DLL entry point
+ *
+ */
+BOOL WINAPI VIDEO_LibMain(DWORD fdwReason, HINSTANCE hinstDLL, WORD ds,
+                          WORD wHeapSize, DWORD dwReserved1, WORD wReserved2)
+{
+    switch (fdwReason) 
+    {
+    case DLL_PROCESS_ATTACH:
+        /* hook in our 16 bit management functions */
+        pFnCallTo16 = IC_CallTo16;
+        break;
+    case DLL_PROCESS_DETACH:
+        /* remove our 16 bit management functions */
+        pFnCallTo16 = NULL;
+        break;
+    case DLL_THREAD_ATTACH:
+    case DLL_THREAD_DETACH:
+        break;
+    }
+    return TRUE;
+}
diff --git a/reactos/dll/msvideo/msvideo_main.c b/reactos/dll/msvideo/msvideo_main.c
new file mode 100644 (file)
index 0000000..0d918ed
--- /dev/null
@@ -0,0 +1,1184 @@
+/*
+ * Copyright 1998 Marcus Meissner
+ * Copyright 2000 Bradley Baetz
+ * Copyright 2003 Michael Günnewig
+ *
+ * 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
+ *
+ * FIXME: This all assumes 32 bit codecs
+ *             Win95 appears to prefer 32 bit codecs, even from 16 bit code.
+ *             There is the ICOpenFunction16 to worry about still, though.
+ *      
+ * TODO
+ *      - no thread safety
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "msvideo_private.h"
+#include "winnls.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "winreg.h"
+
+#include "windowsx.h"
+
+#include "wine/debug.h"
+
+/* Drivers32 settings */
+#define HKLM_DRIVERS32 "Software\\Microsoft\\Windows NT\\CurrentVersion\\Drivers32"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msvideo);
+
+static inline const char *wine_dbgstr_fcc( DWORD fcc )
+{
+    return wine_dbg_sprintf("%c%c%c%c", 
+                            LOBYTE(LOWORD(fcc)), HIBYTE(LOWORD(fcc)),
+                            LOBYTE(HIWORD(fcc)), HIBYTE(HIWORD(fcc)));
+}
+
+LRESULT (CALLBACK *pFnCallTo16)(HDRVR, HIC, UINT, LPARAM, LPARAM) = NULL;
+
+static WINE_HIC*        MSVIDEO_FirstHic /* = NULL */;
+
+typedef struct _reg_driver reg_driver;
+struct _reg_driver
+{
+    DWORD       fccType;
+    DWORD       fccHandler;
+    DRIVERPROC  proc;
+    LPWSTR      name;
+    reg_driver* next;
+};
+
+static reg_driver* reg_driver_list = NULL;
+
+/* This one is a macro such that it works for both ASCII and Unicode */
+#define fourcc_to_string(str, fcc) do { \
+       (str)[0] = LOBYTE(LOWORD(fcc)); \
+       (str)[1] = HIBYTE(LOWORD(fcc)); \
+       (str)[2] = LOBYTE(HIWORD(fcc)); \
+       (str)[3] = HIBYTE(HIWORD(fcc)); \
+       } while(0)
+
+HMODULE MSVFW32_hModule;
+
+BOOL WINAPI DllMain( HINSTANCE hinst, DWORD reason, LPVOID reserved )
+{
+    TRACE("%p,%lx,%p\n", hinst, reason, reserved);
+
+    switch(reason)
+    {
+        case DLL_PROCESS_ATTACH:
+            DisableThreadLibraryCalls(hinst);
+            MSVFW32_hModule = (HMODULE)hinst;
+            break;
+    }
+    return TRUE;
+}
+
+static int compare_fourcc(DWORD fcc1, DWORD fcc2)
+{
+  char fcc_str1[4];
+  char fcc_str2[4];
+  fourcc_to_string(fcc_str1, fcc1);
+  fourcc_to_string(fcc_str2, fcc2);
+  return strncasecmp(fcc_str1, fcc_str2, 4);
+}
+
+typedef BOOL (*enum_handler_t)(const char*, int, void*);
+
+static BOOL enum_drivers(DWORD fccType, enum_handler_t handler, void* param)
+{
+    CHAR buf[2048], fccTypeStr[5], *s;
+    DWORD i, cnt = 0, bufLen, lRet;
+    BOOL result = FALSE;
+    FILETIME lastWrite;
+    HKEY hKey;
+
+    fourcc_to_string(fccTypeStr, fccType);
+    fccTypeStr[4] = '.';
+
+    /* first, go through the registry entries */
+    lRet = RegOpenKeyExA(HKEY_LOCAL_MACHINE, HKLM_DRIVERS32, 0, KEY_QUERY_VALUE, &hKey);
+    if (lRet == ERROR_SUCCESS) 
+    {
+       RegQueryInfoKeyA( hKey, 0, 0, 0, &cnt, 0, 0, 0, 0, 0, 0, 0);
+       for (i = 0; i < cnt; i++) 
+       {
+           bufLen = sizeof(buf) / sizeof(buf[0]);
+           lRet = RegEnumKeyExA(hKey, i, buf, &bufLen, 0, 0, 0, &lastWrite);
+           if (lRet != ERROR_SUCCESS) continue;
+           if (strncasecmp(buf, fccTypeStr, 5) || buf[9] != '=') continue;
+           if ((result = handler(buf, i, param))) break;
+       }
+       RegCloseKey( hKey );
+    }
+    if (result) return result;
+
+    /* if that didn't work, go through the values in system.ini */
+    if (GetPrivateProfileSectionA("drivers32", buf, sizeof(buf), "system.ini")) 
+    {
+       for (s = buf; *s; cnt++, s += strlen(s) + 1)
+       {
+           if (strncasecmp(s, fccTypeStr, 5) || s[9] != '=') continue;
+           if ((result = handler(s, cnt, param))) break;
+       }
+    }
+
+    return result;
+}
+
+/******************************************************************
+ *             MSVIDEO_GetHicPtr
+ *
+ *
+ */
+WINE_HIC*   MSVIDEO_GetHicPtr(HIC hic)
+{
+    WINE_HIC*   whic;
+
+    for (whic = MSVIDEO_FirstHic; whic && whic->hic != hic; whic = whic->next);
+    return whic;
+}
+
+/***********************************************************************
+ *             VideoForWindowsVersion          [MSVFW32.2]
+ *             VideoForWindowsVersion          [MSVIDEO.2]
+ * Returns the version in major.minor form.
+ * In Windows95 this returns 0x040003b6 (4.950)
+ */
+DWORD WINAPI VideoForWindowsVersion(void) 
+{
+    return 0x040003B6; /* 4.950 */
+}
+
+static BOOL ICInfo_enum_handler(const char *drv, int nr, void *param)
+{
+    ICINFO *lpicinfo = (ICINFO *)param;
+    DWORD fccHandler = mmioStringToFOURCCA(drv + 5, 0);
+
+    /* exact match of fccHandler or nth driver found */
+    if ((lpicinfo->fccHandler != nr) && (lpicinfo->fccHandler != fccHandler))
+       return FALSE;
+
+    lpicinfo->fccType = mmioStringToFOURCCA(drv, 0);
+    lpicinfo->fccHandler = fccHandler;
+    lpicinfo->dwFlags = 0;
+    lpicinfo->dwVersion = 0;
+    lpicinfo->dwVersionICM = 0x104;
+    lpicinfo->szName[0] = 0;
+    lpicinfo->szDescription[0] = 0;
+    MultiByteToWideChar(CP_ACP, 0, drv + 10, -1, lpicinfo->szDriver, 
+                       sizeof(lpicinfo->szDriver)/sizeof(WCHAR));
+
+    return TRUE;
+}
+
+/***********************************************************************
+ *             ICInfo                          [MSVFW32.@]
+ * Get information about an installable compressor. Return TRUE if there
+ * is one.
+ *
+ * PARAMS
+ *   fccType     [I] type of compressor (e.g. 'vidc')
+ *   fccHandler  [I] real fcc for handler or <n>th compressor
+ *   lpicinfo    [O] information about compressor
+ */
+BOOL VFWAPI ICInfo( DWORD fccType, DWORD fccHandler, ICINFO *lpicinfo)
+{
+    TRACE("(%s,%s/%08lx,%p)\n", 
+          wine_dbgstr_fcc(fccType), wine_dbgstr_fcc(fccHandler), fccHandler, lpicinfo);
+
+    lpicinfo->fccHandler = fccHandler;
+    return enum_drivers(fccType, ICInfo_enum_handler, lpicinfo);
+}
+
+static DWORD IC_HandleRef = 1;
+
+/***********************************************************************
+ *             ICInstall                       [MSVFW32.@]
+ */
+BOOL VFWAPI ICInstall(DWORD fccType, DWORD fccHandler, LPARAM lParam, LPSTR szDesc, UINT wFlags) 
+{
+    reg_driver* driver;
+    unsigned len;
+
+    TRACE("(%s,%s,%p,%p,0x%08x)\n", wine_dbgstr_fcc(fccType), wine_dbgstr_fcc(fccHandler), (void*)lParam, szDesc, wFlags);
+
+    /* Check if a driver is already registered */
+    for (driver = reg_driver_list; driver; driver = driver->next)
+    {
+        if (!compare_fourcc(fccType, driver->fccType) &&
+            !compare_fourcc(fccHandler, driver->fccHandler))
+            break;
+    }
+    if (driver) return FALSE;
+
+    /* Register the driver */
+    driver = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(reg_driver));
+    if (!driver) goto oom;
+    driver->fccType = fccType;
+    driver->fccHandler = fccHandler;
+
+    switch(wFlags)
+    {
+    case ICINSTALL_FUNCTION:
+        driver->proc = (DRIVERPROC)lParam;
+       driver->name = NULL;
+        break;
+    case ICINSTALL_DRIVER:
+       driver->proc = NULL;
+        len = MultiByteToWideChar(CP_ACP, 0, (char*)lParam, -1, NULL, 0);
+        driver->name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
+        if (!driver->name) goto oom;
+        MultiByteToWideChar(CP_ACP, 0, (char*)lParam, -1, driver->name, len);
+       break;
+    default:
+       ERR("Invalid flags!\n");
+       HeapFree(GetProcessHeap(), 0, driver);
+       return FALSE;
+   }
+
+   /* Insert our driver in the list*/
+   driver->next = reg_driver_list;
+   reg_driver_list = driver;
+    
+   return TRUE;
+ oom:
+   if (driver) HeapFree(GetProcessHeap(), 0, driver);
+   return FALSE;
+}
+
+/***********************************************************************
+ *             ICRemove                        [MSVFW32.@]
+ */
+BOOL VFWAPI ICRemove(DWORD fccType, DWORD fccHandler, UINT wFlags) 
+{
+    reg_driver** pdriver;
+    
+    TRACE("(%s,%s,0x%08x)\n", wine_dbgstr_fcc(fccType), wine_dbgstr_fcc(fccHandler), wFlags);
+
+    /* Check if a driver is already registered */
+    for (pdriver = &reg_driver_list; *pdriver; pdriver = &(*pdriver)->next)
+    {
+        if (!compare_fourcc(fccType, (*pdriver)->fccType) &&
+            !compare_fourcc(fccHandler, (*pdriver)->fccHandler))
+            break;
+    }
+    if (!*pdriver)
+        return FALSE;
+
+    /* Remove the driver from the list */
+    *pdriver = (*pdriver)->next;
+    if ((*pdriver)->name)
+        HeapFree(GetProcessHeap(), 0, (*pdriver)->name);
+    HeapFree(GetProcessHeap(), 0, *pdriver);
+    
+    return TRUE;  
+}
+
+
+/***********************************************************************
+ *             ICOpen                          [MSVFW32.@]
+ * Opens an installable compressor. Return special handle.
+ */
+HIC VFWAPI ICOpen(DWORD fccType, DWORD fccHandler, UINT wMode) 
+{
+    WCHAR              codecname[10];
+    ICOPEN             icopen;
+    HDRVR              hdrv;
+    WINE_HIC*           whic;
+    BOOL                bIs16;
+    static const WCHAR  drv32W[] = {'d','r','i','v','e','r','s','3','2','\0'};
+    reg_driver*         driver;
+
+    TRACE("(%s,%s,0x%08x)\n", wine_dbgstr_fcc(fccType), wine_dbgstr_fcc(fccHandler), wMode);
+
+    /* Check if there is a registered driver that matches */
+    driver = reg_driver_list;
+    while(driver)
+        if (!compare_fourcc(fccType, driver->fccType) &&
+            !compare_fourcc(fccHandler, driver->fccHandler))
+           break;
+        else
+            driver = driver->next;
+
+    if (driver && driver->proc)
+       /* The driver has been registered at runtime with its driverproc */
+        return MSVIDEO_OpenFunction(fccType, fccHandler, wMode, (DRIVERPROC)driver->proc, (DWORD)NULL);
+  
+    /* Well, lParam2 is in fact a LPVIDEO_OPEN_PARMS, but it has the
+     * same layout as ICOPEN
+     */
+    icopen.dwSize              = sizeof(ICOPEN);
+    icopen.fccType             = fccType;
+    icopen.fccHandler          = fccHandler;
+    icopen.dwVersion            = 0x00001000; /* FIXME */
+    icopen.dwFlags             = wMode;
+    icopen.dwError              = 0;
+    icopen.pV1Reserved          = NULL;
+    icopen.pV2Reserved          = NULL;
+    icopen.dnDevNode            = 0; /* FIXME */
+       
+    if (!driver) {
+        /* The driver is registered in the registry */
+       fourcc_to_string(codecname, fccType);
+        codecname[4] = '.';
+       fourcc_to_string(codecname + 5, fccHandler);
+        codecname[9] = '\0';
+
+        hdrv = OpenDriver(codecname, drv32W, (LPARAM)&icopen);
+        if (!hdrv) 
+        {
+            if (fccType == streamtypeVIDEO) 
+            {
+               codecname[0] = 'v';
+               codecname[1] = 'i';
+               codecname[2] = 'd';
+               codecname[3] = 'c';
+
+               fccType = ICTYPE_VIDEO;
+                hdrv = OpenDriver(codecname, drv32W, (LPARAM)&icopen);
+           }
+            if (!hdrv)
+                return 0;
+       }
+    } else {
+        /* The driver has been registered at runtime with its name */
+        hdrv = OpenDriver(driver->name, NULL, (LPARAM)&icopen);
+        if (!hdrv) 
+            return 0; 
+    }
+    bIs16 = GetDriverFlags(hdrv) & 0x10000000; /* undocumented flag: WINE_GDF_16BIT */
+
+    if (bIs16 && !pFnCallTo16)
+    {
+        FIXME("Got a 16 bit driver, but no 16 bit support in msvfw\n");
+        return 0;
+    }
+    whic = HeapAlloc(GetProcessHeap(), 0, sizeof(WINE_HIC));
+    if (!whic)
+    {
+        CloseDriver(hdrv, 0, 0);
+        return FALSE;
+    }
+    whic->hdrv          = hdrv;
+    /* FIXME: is the signature the real one ? */
+    whic->driverproc    = bIs16 ? (DRIVERPROC)pFnCallTo16 : NULL;
+    whic->driverproc16  = 0;
+    whic->type          = fccType;
+    whic->handler       = fccHandler;
+    while (MSVIDEO_GetHicPtr(HIC_32(IC_HandleRef)) != NULL) IC_HandleRef++;
+    whic->hic           = HIC_32(IC_HandleRef++);
+    whic->next          = MSVIDEO_FirstHic;
+    MSVIDEO_FirstHic = whic;
+
+    TRACE("=> %p\n", whic->hic);
+    return whic->hic;
+}
+
+/***********************************************************************
+ *             MSVIDEO_OpenFunction
+ */
+HIC MSVIDEO_OpenFunction(DWORD fccType, DWORD fccHandler, UINT wMode, 
+                         DRIVERPROC lpfnHandler, DWORD lpfnHandler16) 
+{
+    ICOPEN      icopen;
+    WINE_HIC*   whic;
+
+    TRACE("(%s,%s,%d,%p,%08lx)\n", 
+          wine_dbgstr_fcc(fccType), wine_dbgstr_fcc(fccHandler), wMode, lpfnHandler, lpfnHandler16);
+
+    icopen.dwSize              = sizeof(ICOPEN);
+    icopen.fccType             = fccType;
+    icopen.fccHandler          = fccHandler;
+    icopen.dwVersion            = 0x00001000; /* FIXME */
+    icopen.dwFlags             = wMode;
+    icopen.dwError              = 0;
+    icopen.pV1Reserved          = NULL;
+    icopen.pV2Reserved          = NULL;
+    icopen.dnDevNode            = 0; /* FIXME */
+
+    whic = HeapAlloc(GetProcessHeap(), 0, sizeof(WINE_HIC));
+    if (!whic) return 0;
+
+    whic->driverproc   = lpfnHandler;
+    whic->driverproc16 = lpfnHandler16;
+    while (MSVIDEO_GetHicPtr(HIC_32(IC_HandleRef)) != NULL) IC_HandleRef++;
+    whic->hic          = HIC_32(IC_HandleRef++);
+    whic->next         = MSVIDEO_FirstHic;
+    MSVIDEO_FirstHic = whic;
+
+    /* Now try opening/loading the driver. Taken from DRIVER_AddToList */
+    /* What if the function is used more than once? */
+
+    if (MSVIDEO_SendMessage(whic, DRV_LOAD, 0L, 0L) != DRV_SUCCESS) 
+    {
+        WARN("DRV_LOAD failed for hic %p\n", whic->hic);
+        MSVIDEO_FirstHic = whic->next;
+        HeapFree(GetProcessHeap(), 0, whic);
+        return 0;
+    }
+    /* return value is not checked */
+    MSVIDEO_SendMessage(whic, DRV_ENABLE, 0L, 0L);
+
+    whic->driverId = (DWORD)MSVIDEO_SendMessage(whic, DRV_OPEN, 0, (DWORD)&icopen);
+    /* FIXME: What should we put here? */
+    whic->hdrv = (HDRVR)0;
+    
+    if (whic->driverId == 0) 
+    {
+        WARN("DRV_OPEN failed for hic %p\n", whic->hic);
+        MSVIDEO_FirstHic = whic->next;
+        HeapFree(GetProcessHeap(), 0, whic);
+        return 0;
+    }
+
+    TRACE("=> %p\n", whic->hic);
+    return whic->hic;
+}
+
+/***********************************************************************
+ *             ICOpenFunction                  [MSVFW32.@]
+ */
+HIC VFWAPI ICOpenFunction(DWORD fccType, DWORD fccHandler, UINT wMode, FARPROC lpfnHandler) 
+{
+    return MSVIDEO_OpenFunction(fccType, fccHandler, wMode, (DRIVERPROC)lpfnHandler, 0);
+}
+
+/***********************************************************************
+ *             ICGetInfo                       [MSVFW32.@]
+ */
+LRESULT VFWAPI ICGetInfo(HIC hic, ICINFO *picinfo, DWORD cb) 
+{
+    LRESULT    ret;
+    WINE_HIC*   whic = MSVIDEO_GetHicPtr(hic);
+
+    TRACE("(%p,%p,%ld)\n", hic, picinfo, cb);
+
+    whic = MSVIDEO_GetHicPtr(hic);
+    if (!whic) return ICERR_BADHANDLE;
+    if (!picinfo) return MMSYSERR_INVALPARAM;
+
+    /* (WS) The field szDriver should be initialized because the driver 
+     * is not obliged and often will not do it. Some applications, like
+     * VirtualDub, rely on this field and will occasionally crash if it
+     * goes unitialized.
+     */
+    if (cb >= sizeof(ICINFO)) picinfo->szDriver[0] = '\0';
+
+    ret = ICSendMessage(hic, ICM_GETINFO, (DWORD)picinfo, cb);
+
+    /* (WS) When szDriver was not supplied by the driver itself, apparently 
+     * Windows will set its value equal to the driver file name. This can
+     * be obtained from the registry as we do here.
+     */
+    if (cb >= sizeof(ICINFO) && picinfo->szDriver[0] == 0)
+    {
+        ICINFO  ii;
+
+        memset(&ii, 0, sizeof(ii));
+        ii.dwSize = sizeof(ii);
+        ICInfo(picinfo->fccType, picinfo->fccHandler, &ii);
+        lstrcpyW(picinfo->szDriver, ii.szDriver);
+    }
+
+    TRACE("    -> 0x%08lx\n", ret);
+    return ret;
+}
+
+typedef struct {
+    DWORD fccType;
+    DWORD fccHandler;
+    LPBITMAPINFOHEADER lpbiIn;
+    LPBITMAPINFOHEADER lpbiOut;
+    WORD wMode;
+    DWORD querymsg;
+    HIC hic;
+} driver_info_t;
+
+static HIC try_driver(driver_info_t *info)
+{
+    HIC   hic;
+
+    if ((hic = ICOpen(info->fccType, info->fccHandler, info->wMode))) 
+    {
+       if (!ICSendMessage(hic, info->querymsg, (DWORD)info->lpbiIn, (DWORD)info->lpbiOut))
+           return hic;
+       ICClose(hic);
+    }
+    return 0;
+}
+
+static BOOL ICLocate_enum_handler(const char *drv, int nr, void *param)
+{
+    driver_info_t *info = (driver_info_t *)param;
+    info->fccHandler = mmioStringToFOURCCA(drv + 5, 0);
+    info->hic = try_driver(info);
+    return info->hic != 0;
+}
+
+/***********************************************************************
+ *             ICLocate                        [MSVFW32.@]
+ */
+HIC VFWAPI ICLocate(DWORD fccType, DWORD fccHandler, LPBITMAPINFOHEADER lpbiIn,
+                    LPBITMAPINFOHEADER lpbiOut, WORD wMode)
+{
+    driver_info_t info;
+
+    TRACE("(%s,%s,%p,%p,0x%04x)\n", 
+          wine_dbgstr_fcc(fccType), wine_dbgstr_fcc(fccHandler), lpbiIn, lpbiOut, wMode);
+
+    info.fccType = fccType;
+    info.fccHandler = fccHandler;
+    info.lpbiIn = lpbiIn;
+    info.lpbiOut = lpbiOut;
+    info.wMode = wMode;
+
+    switch (wMode) 
+    {
+    case ICMODE_FASTCOMPRESS:
+    case ICMODE_COMPRESS:
+        info.querymsg = ICM_COMPRESS_QUERY;
+        break;
+    case ICMODE_FASTDECOMPRESS:
+    case ICMODE_DECOMPRESS:
+        info.querymsg = ICM_DECOMPRESS_QUERY;
+        break;
+    case ICMODE_DRAW:
+        info.querymsg = ICM_DRAW_QUERY;
+        break;
+    default:
+        WARN("Unknown mode (%d)\n", wMode);
+        return 0;
+    }
+
+    /* Easy case: handler/type match, we just fire a query and return */
+    info.hic = try_driver(&info);
+    /* If it didn't work, try each driver in turn. 32 bit codecs only. */
+    /* FIXME: Move this to an init routine? */
+    if (!info.hic) enum_drivers(fccType, ICLocate_enum_handler, &info);
+
+    if (info.hic) 
+    {
+        TRACE("=> %p\n", info.hic);
+       return info.hic;
+    }
+
+    if (fccType == streamtypeVIDEO) 
+        return ICLocate(ICTYPE_VIDEO, fccHandler, lpbiIn, lpbiOut, wMode);
+    
+    WARN("(%s,%s,%p,%p,0x%04x) not found!\n",
+         wine_dbgstr_fcc(fccType), wine_dbgstr_fcc(fccHandler), lpbiIn, lpbiOut, wMode);
+    return 0;
+}
+
+/***********************************************************************
+ *             ICGetDisplayFormat                      [MSVFW32.@]
+ */
+HIC VFWAPI ICGetDisplayFormat(
+       HIC hic,LPBITMAPINFOHEADER lpbiIn,LPBITMAPINFOHEADER lpbiOut,
+       INT depth,INT dx,INT dy)
+{
+       HIC     tmphic = hic;
+
+       TRACE("(%p,%p,%p,%d,%d,%d)!\n",hic,lpbiIn,lpbiOut,depth,dx,dy);
+
+       if (!tmphic) {
+               tmphic=ICLocate(ICTYPE_VIDEO,0,lpbiIn,NULL,ICMODE_DECOMPRESS);
+               if (!tmphic)
+                       return tmphic;
+       }
+       if ((dy == lpbiIn->biHeight) && (dx == lpbiIn->biWidth))
+               dy = dx = 0; /* no resize needed */
+
+       /* Can we decompress it ? */
+       if (ICDecompressQuery(tmphic,lpbiIn,NULL) != 0)
+               goto errout; /* no, sorry */
+
+       ICDecompressGetFormat(tmphic,lpbiIn,lpbiOut);
+
+       if (lpbiOut->biCompression != 0) {
+          FIXME("Ooch, how come decompressor outputs compressed data (%ld)??\n",
+                        lpbiOut->biCompression);
+       }
+       if (lpbiOut->biSize < sizeof(*lpbiOut)) {
+          FIXME("Ooch, size of output BIH is too small (%ld)\n",
+                        lpbiOut->biSize);
+          lpbiOut->biSize = sizeof(*lpbiOut);
+       }
+       if (!depth) {
+               HDC     hdc;
+
+               hdc = GetDC(0);
+               depth = GetDeviceCaps(hdc,BITSPIXEL)*GetDeviceCaps(hdc,PLANES);
+               ReleaseDC(0,hdc);
+               if (depth==15)  depth = 16;
+               if (depth<8)    depth =  8;
+       }
+       if (lpbiIn->biBitCount == 8)
+               depth = 8;
+
+       TRACE("=> %p\n", tmphic);
+       return tmphic;
+errout:
+       if (hic!=tmphic)
+               ICClose(tmphic);
+
+       TRACE("=> 0\n");
+       return 0;
+}
+
+/***********************************************************************
+ *             ICCompress                      [MSVFW32.@]
+ */
+DWORD VFWAPIV
+ICCompress(
+       HIC hic,DWORD dwFlags,LPBITMAPINFOHEADER lpbiOutput,LPVOID lpData,
+       LPBITMAPINFOHEADER lpbiInput,LPVOID lpBits,LPDWORD lpckid,
+       LPDWORD lpdwFlags,LONG lFrameNum,DWORD dwFrameSize,DWORD dwQuality,
+       LPBITMAPINFOHEADER lpbiPrev,LPVOID lpPrev)
+{
+       ICCOMPRESS      iccmp;
+
+       TRACE("(%p,%ld,%p,%p,%p,%p,...)\n",hic,dwFlags,lpbiOutput,lpData,lpbiInput,lpBits);
+
+       iccmp.dwFlags           = dwFlags;
+
+       iccmp.lpbiOutput        = lpbiOutput;
+       iccmp.lpOutput          = lpData;
+       iccmp.lpbiInput         = lpbiInput;
+       iccmp.lpInput           = lpBits;
+
+       iccmp.lpckid            = lpckid;
+       iccmp.lpdwFlags         = lpdwFlags;
+       iccmp.lFrameNum         = lFrameNum;
+       iccmp.dwFrameSize       = dwFrameSize;
+       iccmp.dwQuality         = dwQuality;
+       iccmp.lpbiPrev          = lpbiPrev;
+       iccmp.lpPrev            = lpPrev;
+       return ICSendMessage(hic,ICM_COMPRESS,(DWORD)&iccmp,sizeof(iccmp));
+}
+
+/***********************************************************************
+ *             ICDecompress                    [MSVFW32.@]
+ */
+DWORD VFWAPIV  ICDecompress(HIC hic,DWORD dwFlags,LPBITMAPINFOHEADER lpbiFormat,
+                               LPVOID lpData,LPBITMAPINFOHEADER lpbi,LPVOID lpBits)
+{
+       ICDECOMPRESS    icd;
+       DWORD ret;
+
+       TRACE("(%p,%ld,%p,%p,%p,%p)\n",hic,dwFlags,lpbiFormat,lpData,lpbi,lpBits);
+
+       TRACE("lpBits[0] == %lx\n",((LPDWORD)lpBits)[0]);
+
+       icd.dwFlags     = dwFlags;
+       icd.lpbiInput   = lpbiFormat;
+       icd.lpInput     = lpData;
+
+       icd.lpbiOutput  = lpbi;
+       icd.lpOutput    = lpBits;
+       icd.ckid        = 0;
+       ret = ICSendMessage(hic,ICM_DECOMPRESS,(DWORD)&icd,sizeof(ICDECOMPRESS));
+
+       TRACE("lpBits[0] == %lx\n",((LPDWORD)lpBits)[0]);
+
+       TRACE("-> %ld\n",ret);
+
+       return ret;
+}
+
+
+/***********************************************************************
+ *             ICCompressorChoose   [MSVFW32.@]
+ */
+BOOL VFWAPI ICCompressorChoose(HWND hwnd, UINT uiFlags, LPVOID pvIn,
+                               LPVOID lpData, PCOMPVARS pc, LPSTR lpszTitle)
+{
+  FIXME("(%p,0x%X,%p,%p,%p,%s),stub!\n",hwnd,uiFlags,pvIn,lpData,pc,lpszTitle);
+
+  if (pc == NULL || pc->cbSize != sizeof(COMPVARS))
+    return FALSE;
+
+  if ((pc->dwFlags & ICMF_COMPVARS_VALID) == 0) {
+    pc->dwFlags   = 0;
+    pc->fccType   = pc->fccHandler = 0;
+    pc->hic       = NULL;
+    pc->lpbiOut   = NULL;
+    pc->lpBitsOut = pc->lpBitsPrev = pc->lpState = NULL;
+    pc->lQ        = ICQUALITY_DEFAULT;
+    pc->lKey      = -1;
+    pc->lDataRate = 300; /* kB */
+    pc->lpState   = NULL;
+    pc->cbState   = 0;
+  }
+  if (pc->fccType == 0)
+    pc->fccType = ICTYPE_VIDEO;
+
+  /* FIXME */
+  
+  return FALSE;
+}
+
+
+/***********************************************************************
+ *             ICCompressorFree   [MSVFW32.@]
+ */
+void VFWAPI ICCompressorFree(PCOMPVARS pc)
+{
+  TRACE("(%p)\n",pc);
+
+  if (pc != NULL && pc->cbSize == sizeof(COMPVARS)) {
+    if (pc->hic != NULL) {
+      ICClose(pc->hic);
+      pc->hic = NULL;
+    }
+    if (pc->lpbiOut != NULL) {
+      GlobalFreePtr(pc->lpbiOut);
+      pc->lpbiOut = NULL;
+    }
+    if (pc->lpBitsOut != NULL) {
+      GlobalFreePtr(pc->lpBitsOut);
+      pc->lpBitsOut = NULL;
+    }
+    if (pc->lpBitsPrev != NULL) {
+      GlobalFreePtr(pc->lpBitsPrev);
+      pc->lpBitsPrev = NULL;
+    }
+    if (pc->lpState != NULL) {
+      GlobalFreePtr(pc->lpBitsPrev);
+      pc->lpState = NULL;
+    }
+    pc->dwFlags = 0;
+  }
+}
+
+
+/******************************************************************
+ *             MSVIDEO_SendMessage
+ *
+ *
+ */
+LRESULT MSVIDEO_SendMessage(WINE_HIC* whic, UINT msg, DWORD lParam1, DWORD lParam2)
+{
+    LRESULT     ret;
+    
+#define XX(x) case x: TRACE("(%p,"#x",0x%08lx,0x%08lx)\n",whic,lParam1,lParam2); break;
+    
+    switch (msg) {
+        /* DRV_* */
+        XX(DRV_LOAD);
+        XX(DRV_ENABLE);
+        XX(DRV_OPEN);
+        XX(DRV_CLOSE);
+        XX(DRV_DISABLE);
+        XX(DRV_FREE);
+        /* ICM_RESERVED+X */
+        XX(ICM_ABOUT);
+        XX(ICM_CONFIGURE);
+        XX(ICM_GET);
+        XX(ICM_GETINFO);
+        XX(ICM_GETDEFAULTQUALITY);
+        XX(ICM_GETQUALITY);
+        XX(ICM_GETSTATE);
+        XX(ICM_SETQUALITY);
+        XX(ICM_SET);
+        XX(ICM_SETSTATE);
+        /* ICM_USER+X */
+        XX(ICM_COMPRESS_FRAMES_INFO);
+        XX(ICM_COMPRESS_GET_FORMAT);
+        XX(ICM_COMPRESS_GET_SIZE);
+        XX(ICM_COMPRESS_QUERY);
+        XX(ICM_COMPRESS_BEGIN);
+        XX(ICM_COMPRESS);
+        XX(ICM_COMPRESS_END);
+        XX(ICM_DECOMPRESS_GET_FORMAT);
+        XX(ICM_DECOMPRESS_QUERY);
+        XX(ICM_DECOMPRESS_BEGIN);
+        XX(ICM_DECOMPRESS);
+        XX(ICM_DECOMPRESS_END);
+        XX(ICM_DECOMPRESS_SET_PALETTE);
+        XX(ICM_DECOMPRESS_GET_PALETTE);
+        XX(ICM_DRAW_QUERY);
+        XX(ICM_DRAW_BEGIN);
+        XX(ICM_DRAW_GET_PALETTE);
+        XX(ICM_DRAW_START);
+        XX(ICM_DRAW_STOP);
+        XX(ICM_DRAW_END);
+        XX(ICM_DRAW_GETTIME);
+        XX(ICM_DRAW);
+        XX(ICM_DRAW_WINDOW);
+        XX(ICM_DRAW_SETTIME);
+        XX(ICM_DRAW_REALIZE);
+        XX(ICM_DRAW_FLUSH);
+        XX(ICM_DRAW_RENDERBUFFER);
+        XX(ICM_DRAW_START_PLAY);
+        XX(ICM_DRAW_STOP_PLAY);
+        XX(ICM_DRAW_SUGGESTFORMAT);
+        XX(ICM_DRAW_CHANGEPALETTE);
+        XX(ICM_GETBUFFERSWANTED);
+        XX(ICM_GETDEFAULTKEYFRAMERATE);
+        XX(ICM_DECOMPRESSEX_BEGIN);
+        XX(ICM_DECOMPRESSEX_QUERY);
+        XX(ICM_DECOMPRESSEX);
+        XX(ICM_DECOMPRESSEX_END);
+        XX(ICM_SET_STATUS_PROC);
+    default:
+        FIXME("(%p,0x%08lx,0x%08lx,0x%08lx) unknown message\n",whic,(DWORD)msg,lParam1,lParam2);
+    }
+    
+#undef XX
+    
+    if (whic->driverproc) {
+       /* dwDriverId parameter is the value returned by the DRV_OPEN */
+        ret = whic->driverproc(whic->driverId, whic->hdrv, msg, lParam1, lParam2);
+    } else {
+        ret = SendDriverMessage(whic->hdrv, msg, lParam1, lParam2);
+    }
+
+    TRACE("    -> 0x%08lx\n", ret);
+    return ret;
+}
+
+/***********************************************************************
+ *             ICSendMessage                   [MSVFW32.@]
+ */
+LRESULT VFWAPI ICSendMessage(HIC hic, UINT msg, DWORD lParam1, DWORD lParam2) 
+{
+    WINE_HIC*   whic = MSVIDEO_GetHicPtr(hic);
+
+    if (!whic) return ICERR_BADHANDLE;
+    return MSVIDEO_SendMessage(whic, msg, lParam1, lParam2);
+}
+
+/***********************************************************************
+ *             ICDrawBegin             [MSVFW32.@]
+ */
+DWORD VFWAPIV ICDrawBegin(
+       HIC                hic,     /* [in] */
+       DWORD              dwFlags, /* [in] flags */
+       HPALETTE           hpal,    /* [in] palette to draw with */
+       HWND               hwnd,    /* [in] window to draw to */
+       HDC                hdc,     /* [in] HDC to draw to */
+       INT                xDst,    /* [in] destination rectangle */
+       INT                yDst,    /* [in] */
+       INT                dxDst,   /* [in] */
+       INT                dyDst,   /* [in] */
+       LPBITMAPINFOHEADER lpbi,    /* [in] format of frame to draw */
+       INT                xSrc,    /* [in] source rectangle */
+       INT                ySrc,    /* [in] */
+       INT                dxSrc,   /* [in] */
+       INT                dySrc,   /* [in] */
+       DWORD              dwRate,  /* [in] frames/second = (dwRate/dwScale) */
+       DWORD              dwScale) /* [in] */
+{
+
+       ICDRAWBEGIN     icdb;
+
+       TRACE("(%p,%ld,%p,%p,%p,%u,%u,%u,%u,%p,%u,%u,%u,%u,%ld,%ld)\n",
+                 hic, dwFlags, hpal, hwnd, hdc, xDst, yDst, dxDst, dyDst,
+                 lpbi, xSrc, ySrc, dxSrc, dySrc, dwRate, dwScale);
+
+       icdb.dwFlags = dwFlags;
+       icdb.hpal = hpal;
+       icdb.hwnd = hwnd;
+       icdb.hdc = hdc;
+       icdb.xDst = xDst;
+       icdb.yDst = yDst;
+       icdb.dxDst = dxDst;
+       icdb.dyDst = dyDst;
+       icdb.lpbi = lpbi;
+       icdb.xSrc = xSrc;
+       icdb.ySrc = ySrc;
+       icdb.dxSrc = dxSrc;
+       icdb.dySrc = dySrc;
+       icdb.dwRate = dwRate;
+       icdb.dwScale = dwScale;
+       return ICSendMessage(hic,ICM_DRAW_BEGIN,(DWORD)&icdb,sizeof(icdb));
+}
+
+/***********************************************************************
+ *             ICDraw                  [MSVFW32.@]
+ */
+DWORD VFWAPIV ICDraw(HIC hic, DWORD dwFlags, LPVOID lpFormat, LPVOID lpData, DWORD cbData, LONG lTime) {
+       ICDRAW  icd;
+
+       TRACE("(%p,%ld,%p,%p,%ld,%ld)\n",hic,dwFlags,lpFormat,lpData,cbData,lTime);
+
+       icd.dwFlags = dwFlags;
+       icd.lpFormat = lpFormat;
+       icd.lpData = lpData;
+       icd.cbData = cbData;
+       icd.lTime = lTime;
+
+       return ICSendMessage(hic,ICM_DRAW,(DWORD)&icd,sizeof(icd));
+}
+
+/***********************************************************************
+ *             ICClose                 [MSVFW32.@]
+ */
+LRESULT WINAPI ICClose(HIC hic)
+{
+    WINE_HIC* whic = MSVIDEO_GetHicPtr(hic);
+    WINE_HIC** p;
+
+    TRACE("(%p)\n",hic);
+
+    if (!whic) return ICERR_BADHANDLE;
+
+    if (whic->driverproc) 
+    {
+        MSVIDEO_SendMessage(whic, DRV_CLOSE, 0, 0);
+        MSVIDEO_SendMessage(whic, DRV_DISABLE, 0, 0);
+        MSVIDEO_SendMessage(whic, DRV_FREE, 0, 0);
+    }
+    else
+    {
+        CloseDriver(whic->hdrv, 0, 0);
+    }
+
+    /* remove whic from list */
+    for (p = &MSVIDEO_FirstHic; *p != NULL; p = &((*p)->next))
+    {
+        if ((*p) == whic)
+        {
+            *p = whic->next;
+            break;
+        }
+    }
+
+    HeapFree(GetProcessHeap(), 0, whic);
+    return 0;
+}
+
+
+
+/***********************************************************************
+ *             ICImageCompress [MSVFW32.@]
+ */
+HANDLE VFWAPI ICImageCompress(
+       HIC hic, UINT uiFlags,
+       LPBITMAPINFO lpbiIn, LPVOID lpBits,
+       LPBITMAPINFO lpbiOut, LONG lQuality,
+       LONG* plSize)
+{
+       FIXME("(%p,%08x,%p,%p,%p,%ld,%p)\n",
+               hic, uiFlags, lpbiIn, lpBits, lpbiOut, lQuality, plSize);
+
+       return NULL;
+}
+
+/***********************************************************************
+ *             ICImageDecompress       [MSVFW32.@]
+ */
+
+HANDLE VFWAPI ICImageDecompress(
+       HIC hic, UINT uiFlags, LPBITMAPINFO lpbiIn,
+       LPVOID lpBits, LPBITMAPINFO lpbiOut)
+{
+       HGLOBAL hMem = NULL;
+       BYTE*   pMem = NULL;
+       BOOL    bReleaseIC = FALSE;
+       BYTE*   pHdr = NULL;
+       LONG    cbHdr = 0;
+       BOOL    bSucceeded = FALSE;
+       BOOL    bInDecompress = FALSE;
+       DWORD   biSizeImage;
+
+       TRACE("(%p,%08x,%p,%p,%p)\n",
+               hic, uiFlags, lpbiIn, lpBits, lpbiOut);
+
+       if ( hic == NULL )
+       {
+               hic = ICDecompressOpen( ICTYPE_VIDEO, 0, &lpbiIn->bmiHeader, (lpbiOut != NULL) ? &lpbiOut->bmiHeader : NULL );
+               if ( hic == NULL )
+               {
+                       WARN("no handler\n" );
+                       goto err;
+               }
+               bReleaseIC = TRUE;
+       }
+       if ( uiFlags != 0 )
+       {
+               FIXME( "unknown flag %08x\n", uiFlags );
+               goto err;
+       }
+       if ( lpbiIn == NULL || lpBits == NULL )
+       {
+               WARN("invalid argument\n");
+               goto err;
+       }
+
+       if ( lpbiOut != NULL )
+       {
+               if ( lpbiOut->bmiHeader.biSize != sizeof(BITMAPINFOHEADER) )
+                       goto err;
+               cbHdr = sizeof(BITMAPINFOHEADER);
+               if ( lpbiOut->bmiHeader.biCompression == 3 )
+                       cbHdr += sizeof(DWORD)*3;
+               else
+               if ( lpbiOut->bmiHeader.biBitCount <= 8 )
+               {
+                       if ( lpbiOut->bmiHeader.biClrUsed == 0 )
+                               cbHdr += sizeof(RGBQUAD) * (1<<lpbiOut->bmiHeader.biBitCount);
+                       else
+                               cbHdr += sizeof(RGBQUAD) * lpbiOut->bmiHeader.biClrUsed;
+               }
+       }
+       else
+       {
+               TRACE( "get format\n" );
+
+               cbHdr = ICDecompressGetFormatSize(hic,lpbiIn);
+               if ( cbHdr < sizeof(BITMAPINFOHEADER) )
+                       goto err;
+               pHdr = (BYTE*)HeapAlloc(GetProcessHeap(),0,cbHdr+sizeof(RGBQUAD)*256);
+               if ( pHdr == NULL )
+                       goto err;
+               ZeroMemory( pHdr, cbHdr+sizeof(RGBQUAD)*256 );
+               if ( ICDecompressGetFormat( hic, lpbiIn, (BITMAPINFO*)pHdr ) != ICERR_OK )
+                       goto err;
+               lpbiOut = (BITMAPINFO*)pHdr;
+               if ( lpbiOut->bmiHeader.biBitCount <= 8 &&
+                        ICDecompressGetPalette( hic, lpbiIn, lpbiOut ) != ICERR_OK &&
+                        lpbiIn->bmiHeader.biBitCount == lpbiOut->bmiHeader.biBitCount )
+               {
+                       if ( lpbiIn->bmiHeader.biClrUsed == 0 )
+                               memcpy( lpbiOut->bmiColors, lpbiIn->bmiColors, sizeof(RGBQUAD)*(1<<lpbiOut->bmiHeader.biBitCount) );
+                       else
+                               memcpy( lpbiOut->bmiColors, lpbiIn->bmiColors, sizeof(RGBQUAD)*lpbiIn->bmiHeader.biClrUsed );
+               }
+               if ( lpbiOut->bmiHeader.biBitCount <= 8 &&
+                        lpbiOut->bmiHeader.biClrUsed == 0 )
+                       lpbiOut->bmiHeader.biClrUsed = 1<<lpbiOut->bmiHeader.biBitCount;
+
+               lpbiOut->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+               cbHdr = sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*lpbiOut->bmiHeader.biClrUsed;
+       }
+
+       biSizeImage = lpbiOut->bmiHeader.biSizeImage;
+       if ( biSizeImage == 0 )
+               biSizeImage = ((((lpbiOut->bmiHeader.biWidth * lpbiOut->bmiHeader.biBitCount + 7) >> 3) + 3) & (~3)) * abs(lpbiOut->bmiHeader.biHeight);
+
+       TRACE( "call ICDecompressBegin\n" );
+
+       if ( ICDecompressBegin( hic, lpbiIn, lpbiOut ) != ICERR_OK )
+               goto err;
+       bInDecompress = TRUE;
+
+       TRACE( "cbHdr %ld, biSizeImage %ld\n", cbHdr, biSizeImage );
+
+       hMem = GlobalAlloc( GMEM_MOVEABLE|GMEM_ZEROINIT, cbHdr + biSizeImage );
+       if ( hMem == NULL )
+       {
+               WARN( "out of memory\n" );
+               goto err;
+       }
+       pMem = (BYTE*)GlobalLock( hMem );
+       if ( pMem == NULL )
+               goto err;
+       memcpy( pMem, lpbiOut, cbHdr );
+
+       TRACE( "call ICDecompress\n" );
+       if ( ICDecompress( hic, 0, &lpbiIn->bmiHeader, lpBits, &lpbiOut->bmiHeader, pMem+cbHdr ) != ICERR_OK )
+               goto err;
+
+       bSucceeded = TRUE;
+err:
+       if ( bInDecompress )
+               ICDecompressEnd( hic );
+       if ( bReleaseIC )
+               ICClose(hic);
+       if ( pHdr != NULL )
+               HeapFree(GetProcessHeap(),0,pHdr);
+       if ( pMem != NULL )
+               GlobalUnlock( hMem );
+       if ( !bSucceeded && hMem != NULL )
+       {
+               GlobalFree(hMem); hMem = NULL;
+       }
+
+       return (HANDLE)hMem;
+}
+
+static BOOL GetFileNamePreview(LPVOID lpofn,BOOL bSave,BOOL bUnicode)
+{
+  CHAR    szFunctionName[20];
+  BOOL    (*fnGetFileName)(LPVOID);
+  HMODULE hComdlg32;
+  BOOL    ret;
+
+  FIXME("(%p,%d,%d), semi-stub!\n",lpofn,bSave,bUnicode);
+
+  lstrcpyA(szFunctionName, (bSave ? "GetSaveFileName" : "GetOpenFileName"));
+  lstrcatA(szFunctionName, (bUnicode ? "W" : "A"));
+
+  hComdlg32 = LoadLibraryA("COMDLG32.DLL");
+  if (hComdlg32 == NULL)
+    return FALSE;
+
+  fnGetFileName = (LPVOID)GetProcAddress(hComdlg32, szFunctionName);
+  if (fnGetFileName == NULL)
+    return FALSE;
+
+  /* FIXME: need to add OFN_ENABLEHOOK and our own handler */
+  ret = fnGetFileName(lpofn);
+
+  FreeLibrary(hComdlg32);
+  return ret;
+}
+
+/***********************************************************************
+ *             GetOpenFileNamePreviewA [MSVFW32.@]
+ */
+BOOL WINAPI GetOpenFileNamePreviewA(LPOPENFILENAMEA lpofn)
+{
+  FIXME("(%p), semi-stub!\n", lpofn);
+
+  return GetFileNamePreview(lpofn, FALSE, FALSE);
+}
+
+/***********************************************************************
+ *             GetOpenFileNamePreviewW [MSVFW32.@]
+ */
+BOOL WINAPI GetOpenFileNamePreviewW(LPOPENFILENAMEW lpofn)
+{
+  FIXME("(%p), semi-stub!\n", lpofn);
+
+  return GetFileNamePreview(lpofn, FALSE, TRUE);
+}
+
+/***********************************************************************
+ *             GetSaveFileNamePreviewA [MSVFW32.@]
+ */
+BOOL WINAPI GetSaveFileNamePreviewA(LPOPENFILENAMEA lpofn)
+{
+  FIXME("(%p), semi-stub!\n", lpofn);
+
+  return GetFileNamePreview(lpofn, TRUE, FALSE);
+}
+
+/***********************************************************************
+ *             GetSaveFileNamePreviewW [MSVFW32.@]
+ */
+BOOL WINAPI GetSaveFileNamePreviewW(LPOPENFILENAMEW lpofn)
+{
+  FIXME("(%p), semi-stub!\n", lpofn);
+
+  return GetFileNamePreview(lpofn, TRUE, TRUE);
+}
diff --git a/reactos/dll/msvideo/msvideo_private.h b/reactos/dll/msvideo/msvideo_private.h
new file mode 100644 (file)
index 0000000..bf0544f
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright 1999 Marcus Meissner
+ *
+ * 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
+ */
+
+#ifndef __WINE_MSVIDEO_PRIVATE_H
+#define __WINE_MSVIDEO_PRIVATE_H
+
+#define COM_NO_WINDOWS_H
+#include <stdarg.h>
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "commdlg.h"
+#include "vfw.h"
+
+/* HIC struct (same layout as Win95 one) */
+typedef struct tagWINE_HIC {
+    DWORD              magic;          /* 00: 'Smag' */
+    HANDLE             curthread;      /* 04: */
+    DWORD              type;           /* 08: */
+    DWORD              handler;        /* 0C: */
+    HDRVR              hdrv;           /* 10: */
+    DWORD              private;        /* 14:(handled by SendDriverMessage)*/
+    DRIVERPROC         driverproc;     /* 18:(handled by SendDriverMessage)*/
+    DWORD              x1;             /* 1c: name? */
+    WORD               x2;             /* 20: */
+    DWORD              x3;             /* 22: */
+                                       /* 26: */
+    DWORD               driverproc16;   /* Wine specific flags */
+    HIC                 hic;
+    DWORD               driverId;
+    struct tagWINE_HIC* next;
+} WINE_HIC;
+
+HIC             MSVIDEO_OpenFunction(DWORD, DWORD, UINT, DRIVERPROC, DWORD);
+LRESULT         MSVIDEO_SendMessage(WINE_HIC*, UINT, DWORD, DWORD);
+WINE_HIC*       MSVIDEO_GetHicPtr(HIC);
+
+extern LRESULT  (CALLBACK *pFnCallTo16)(HDRVR, HIC, UINT, LPARAM, LPARAM);
+
+/* handle16 --> handle conversions */
+#define HDRAWDIB_32(h16)       ((HDRAWDIB)(ULONG_PTR)(h16))
+#define HIC_32(h16)            ((HIC)(ULONG_PTR)(h16))
+
+/* handle --> handle16 conversions */
+#define HDRVR_16(h32)          (LOWORD(h32))
+#define HDRAWDIB_16(h32)       (LOWORD(h32))
+#define HIC_16(h32)            (LOWORD(h32))
+
+#endif  /* __WINE_MSVIDEO_PRIVATE_H */
diff --git a/reactos/dll/msvideo/vfw16.h b/reactos/dll/msvideo/vfw16.h
new file mode 100644 (file)
index 0000000..3257ed0
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Copyright 1999 Marcus Meissner
+ *
+ * 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
+ */
+
+#ifndef __WINE_VFW16_H
+#define __WINE_VFW16_H
+
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "vfw.h"
+#include "wownt32.h"
+#include "wine/windef16.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif  /* __cplusplus */
+
+typedef HANDLE16 HDRAWDIB16;
+
+#include "pshpack1.h"
+
+typedef struct {
+       DWORD dwSize;
+       DWORD fccType;
+       DWORD fccHandler;
+       DWORD dwFlags;
+       DWORD dwVersion;
+       DWORD dwVersionICM;
+       /*
+        * under Win16, normal chars are used
+        */
+       CHAR szName[16];
+       CHAR szDescription[128];
+       CHAR szDriver[128];
+} ICINFO16;
+
+typedef struct {
+    DWORD              dwFlags;
+    LPBITMAPINFOHEADER lpbiSrc;
+    LPVOID             lpSrc;
+    LPBITMAPINFOHEADER lpbiDst;
+    LPVOID             lpDst;
+
+    INT16              xDst;       /* destination rectangle */
+    INT16              yDst;
+    INT16              dxDst;
+    INT16              dyDst;
+
+    INT16              xSrc;       /* source rectangle */
+    INT16              ySrc;
+    INT16              dxSrc;
+    INT16              dySrc;
+} ICDECOMPRESSEX16;
+
+typedef struct {
+       DWORD           dwFlags;
+       HPALETTE16      hpal;
+       HWND16          hwnd;
+       HDC16           hdc;
+       INT16           xDst;
+       INT16           yDst;
+       INT16           dxDst;
+       INT16           dyDst;
+       LPBITMAPINFOHEADER      lpbi;
+       INT16           xSrc;
+       INT16           ySrc;
+       INT16           dxSrc;
+       INT16           dySrc;
+       DWORD           dwRate;
+       DWORD           dwScale;
+} ICDRAWBEGIN16;
+
+#include "poppack.h"
+
+typedef struct {
+       DWORD dwFlags;
+       LPBITMAPINFOHEADER lpbiIn;
+       LPBITMAPINFOHEADER lpbiSuggest;
+       INT16 dxSrc;
+       INT16 dySrc;
+       INT16 dxDst;
+       INT16 dyDst;
+       HIC16 hicDecompressor;
+} ICDRAWSUGGEST16;
+
+DWORD   VFWAPIV ICDraw16(HIC16,DWORD,LPVOID,LPVOID,DWORD,LONG);
+DWORD   VFWAPIV ICDrawBegin16(HIC16,DWORD,HPALETTE16,HWND16,HDC16,INT16,
+                              INT16,INT16,INT16,LPBITMAPINFOHEADER,
+                              INT16,INT16,INT16,INT16,DWORD,DWORD);
+LRESULT WINAPI  ICClose16(HIC16);
+DWORD   VFWAPIV ICCompress16(HIC16,DWORD,LPBITMAPINFOHEADER,LPVOID,
+                             LPBITMAPINFOHEADER,LPVOID,LPDWORD,
+                             LPDWORD,LONG,DWORD,DWORD,
+                             LPBITMAPINFOHEADER,LPVOID);
+DWORD   VFWAPIV ICDecompress16(HIC16,DWORD,LPBITMAPINFOHEADER,LPVOID,
+                               LPBITMAPINFOHEADER,LPVOID);
+HIC16   VFWAPI  ICGetDisplayFormat16(HIC16,LPBITMAPINFOHEADER,
+                                     LPBITMAPINFOHEADER,INT16,INT16,
+                                     INT16);
+LRESULT VFWAPI  ICGetInfo16(HIC16,ICINFO16 *,DWORD);
+BOOL16  VFWAPI  ICInfo16(DWORD,DWORD,ICINFO16 *);
+HIC16   VFWAPI  ICLocate16(DWORD,DWORD,LPBITMAPINFOHEADER,
+                           LPBITMAPINFOHEADER,WORD);
+LRESULT VFWAPIV ICMessage16( HIC16 hic, UINT16 msg, UINT16 cb, VA_LIST16 valist );
+HIC16   VFWAPI  ICOpen16(DWORD,DWORD,UINT16);
+HIC16   VFWAPI  ICOpenFunction16(DWORD,DWORD,UINT16,FARPROC16);
+LRESULT VFWAPI  ICSendMessage16(HIC16,UINT16,DWORD,DWORD);
+
+#ifdef __cplusplus
+}
+#endif  /* __cplusplus */
+
+#endif  /* __WINE_VFW16_H */
diff --git a/reactos/dll/mswsock/extensions.c b/reactos/dll/mswsock/extensions.c
new file mode 100644 (file)
index 0000000..0e79d5e
--- /dev/null
@@ -0,0 +1,54 @@
+/* $Id: stubs.c 12852 2005-01-06 13:58:04Z mf $
+ *
+ * COPYRIGHT:   See COPYING in the top level directory
+ * PROJECT:     ReactOS WinSock DLL
+ * FILE:        stubs.c
+ * PURPOSE:     WSAIoctl wrappers for Microsoft extensions to Winsock
+ * PROGRAMMERS: KJK::Hyperion <hackbunny@reactos.com>
+ * REVISIONS:
+ */
+
+#include <windows.h>
+#include <winsock2.h>
+#include <mswsock.h>
+
+/*
+ * @implemented
+ */
+BOOL
+STDCALL
+TransmitFile(SOCKET Socket,
+             HANDLE File,
+             DWORD NumberOfBytesToWrite,
+             DWORD NumberOfBytesPerSend,
+             LPOVERLAPPED Overlapped,
+             LPTRANSMIT_FILE_BUFFERS TransmitBuffers,
+             DWORD Flags)
+{
+  static GUID TransmitFileGUID = WSAID_TRANSMITFILE;
+  LPFN_TRANSMITFILE pfnTransmitFile;
+  DWORD cbBytesReturned;
+
+  if (WSAIoctl(Socket,
+               SIO_GET_EXTENSION_FUNCTION_POINTER,
+               &TransmitFileGUID,
+               sizeof(TransmitFileGUID),
+               &pfnTransmitFile,
+               sizeof(pfnTransmitFile),
+               &cbBytesReturned,
+               NULL,
+               NULL) == SOCKET_ERROR)
+  {
+    return FALSE;
+  }
+
+  return pfnTransmitFile(Socket,
+                         File,
+                         NumberOfBytesToWrite,
+                         NumberOfBytesPerSend,
+                         Overlapped,
+                         TransmitBuffers,
+                         Flags);
+}
+
+/* EOF */
diff --git a/reactos/dll/mswsock/mswsock.def b/reactos/dll/mswsock/mswsock.def
new file mode 100644 (file)
index 0000000..3fca871
--- /dev/null
@@ -0,0 +1,39 @@
+; $Id$
+;
+; MSWSOCK.DLL - Windows Sockets 2 DLL
+
+LIBRARY mswsock.dll
+
+EXPORTS
+ServiceMain@8 @0
+SvchostPushServiceGlobals@4 @1
+AcceptEx@32 @2
+EnumProtocolsA@12 @3
+EnumProtocolsW@12 @4
+GetAcceptExSockaddrs@32 @5
+GetAddressByNameA@40 @6
+GetAddressByNameW@40 @7
+GetNameByTypeA@12 @8
+GetNameByTypeW@12 @9
+GetServiceA@28 @10
+GetServiceW@28 @11
+GetTypeByNameA@8 @12
+GetTypeByNameW@8 @13
+MigrateWinsockConfiguration@12 @14
+NPLoadNameSpaces@12 @15
+NSPStartup@8 @16
+SetServiceA@24 @17
+SetServiceW@24 @18
+StartWsdpService@0 @19
+StopWsdpService@0 @20
+TransmitFile@28 @21
+WSARecvEx@16 @22
+WSPStartup@76 @23
+dn_expand@20 @24
+getnetbyname@4 @25
+inet_network@4 @26
+rcmd@24 @27
+rexec@24 @28
+rresvport@4 @29
+s_perror@4 @30
+sethostname@8 @31
diff --git a/reactos/dll/mswsock/mswsock.rc b/reactos/dll/mswsock/mswsock.rc
new file mode 100644 (file)
index 0000000..36b5616
--- /dev/null
@@ -0,0 +1,7 @@
+/* $Id$ */
+
+#define REACTOS_VERSION_DLL
+#define REACTOS_STR_FILE_DESCRIPTION   "Windows Sockets 2 DLL\0"
+#define REACTOS_STR_INTERNAL_NAME      "mswsock\0"
+#define REACTOS_STR_ORIGINAL_FILENAME  "mswsock.dll\0"
+#include <reactos/version.rc>
diff --git a/reactos/dll/mswsock/mswsock.xml b/reactos/dll/mswsock/mswsock.xml
new file mode 100644 (file)
index 0000000..a661f24
--- /dev/null
@@ -0,0 +1,11 @@
+<module name="mswsock" type="win32dll" baseaddress="${BASEADDRESS_MSWSOCK}" installbase="system32" installname="mswsock.dll">
+       <importlibrary definition="mswsock.def" />
+       <define name="UNICODE" />
+       <define name="LE" />
+       <define name="__USE_W32API" />
+       <library>kernel32</library>
+       <library>ws2_32</library>
+       <file>extensions.c</file>
+       <file>stubs.c</file>
+       <file>mswsock.rc</file>
+</module>
diff --git a/reactos/dll/mswsock/stubs.c b/reactos/dll/mswsock/stubs.c
new file mode 100644 (file)
index 0000000..dc91012
--- /dev/null
@@ -0,0 +1,517 @@
+/* $Id$
+ *
+ * COPYRIGHT:   See COPYING in the top level directory
+ * PROJECT:     ReactOS WinSock DLL
+ * FILE:        stubs.c
+ * PURPOSE:     Stub functions
+ * PROGRAMMERS: Ge van Geldorp (ge@gse.nl)
+ * REVISIONS:
+ */
+
+#include <windows.h>
+#include <stdlib.h>
+#include <winsock2.h>
+#include <mswsock.h>
+#include <ws2spi.h>
+#include <nspapi.h>
+
+typedef DWORD (* LPFN_NSPAPI)(VOID);
+typedef struct _NS_ROUTINE {
+    DWORD        dwFunctionCount;
+    LPFN_NSPAPI *alpfnFunctions;
+    DWORD        dwNameSpace;
+    DWORD        dwPriority;
+} NS_ROUTINE, *PNS_ROUTINE, * FAR LPNS_ROUTINE;
+
+/*
+ * @unimplemented
+ */
+BOOL
+STDCALL
+AcceptEx(SOCKET ListenSocket,
+         SOCKET AcceptSocket,
+         PVOID OutputBuffer,
+         DWORD ReceiveDataLength,
+         DWORD LocalAddressLength,
+         DWORD RemoteAddressLength,
+         LPDWORD BytesReceived,
+         LPOVERLAPPED Overlapped)
+{
+  OutputDebugStringW(L"w32sock AcceptEx stub called\n");
+
+  return FALSE;
+}
+
+
+/*
+ * @unimplemented
+ */
+INT
+STDCALL
+EnumProtocolsA(LPINT ProtocolCount,
+               LPVOID ProtocolBuffer,
+               LPDWORD BufferLength)
+{
+  OutputDebugStringW(L"w32sock EnumProtocolsA stub called\n");
+
+  return SOCKET_ERROR;
+}
+
+
+/*
+ * @unimplemented
+ */
+INT
+STDCALL
+EnumProtocolsW(LPINT ProtocolCount,
+               LPVOID ProtocolBuffer,
+               LPDWORD BufferLength)
+{
+  OutputDebugStringW(L"w32sock EnumProtocolsW stub called\n");
+
+  return SOCKET_ERROR;
+}
+
+
+/*
+ * @unimplemented
+ */
+VOID
+STDCALL
+GetAcceptExSockaddrs(PVOID OutputBuffer,
+                     DWORD ReceiveDataLength,
+                     DWORD LocalAddressLength,
+                     DWORD RemoteAddressLength,
+                     LPSOCKADDR* LocalSockaddr,
+                     LPINT LocalSockaddrLength,
+                     LPSOCKADDR* RemoteSockaddr,
+                     LPINT RemoteSockaddrLength)
+{
+  OutputDebugStringW(L"w32sock GetAcceptExSockaddrs stub called\n");
+}
+
+
+/*
+ * @unimplemented
+ */
+INT
+STDCALL
+GetAddressByNameA(DWORD NameSpace,
+                  LPGUID ServiceType,
+                  LPSTR ServiceName,
+                  LPINT Protocols,
+                  DWORD Resolution,
+                  LPSERVICE_ASYNC_INFO ServiceAsyncInfo,
+                  LPVOID CsaddrBuffer,
+                  LPDWORD BufferLength,
+                  LPSTR AliasBuffer,
+                  LPDWORD AliasBufferLength)
+{
+  OutputDebugStringW(L"w32sock GetAddressByNameA stub called\n");
+
+  return SOCKET_ERROR;
+}
+
+
+/*
+ * @unimplemented
+ */
+INT
+STDCALL
+GetAddressByNameW(DWORD NameSpace,
+                  LPGUID ServiceType,
+                  LPWSTR ServiceName,
+                  LPINT Protocols,
+                  DWORD Resolution,
+                  LPSERVICE_ASYNC_INFO ServiceAsyncInfo,
+                  LPVOID CsaddrBuffer,
+                  LPDWORD BufferLength,
+                  LPWSTR AliasBuffer,
+                  LPDWORD AliasBufferLength)
+{
+  OutputDebugStringW(L"w32sock GetAddressByNameW stub called\n");
+
+  return SOCKET_ERROR;
+}
+
+
+/*
+ * @unimplemented
+ */
+INT
+STDCALL
+GetServiceA(DWORD NameSpace,
+            LPGUID Guid,
+            LPSTR ServiceName,
+            DWORD Properties,
+            LPVOID Buffer,
+            LPDWORD BufferSize,
+            LPSERVICE_ASYNC_INFO ServiceAsyncInfo)
+{
+  OutputDebugStringW(L"w32sock GetServiceA stub called\n");
+
+  return SOCKET_ERROR;
+}
+
+
+/*
+ * @unimplemented
+ */
+INT
+STDCALL
+GetServiceW(DWORD NameSpace,
+            LPGUID Guid,
+            LPWSTR ServiceName,
+            DWORD Properties,
+            LPVOID Buffer,
+            LPDWORD BufferSize,
+            LPSERVICE_ASYNC_INFO ServiceAsyncInfo)
+{
+  OutputDebugStringW(L"w32sock GetServiceW stub called\n");
+
+  return SOCKET_ERROR;
+}
+
+
+/*
+ * @unimplemented
+ */
+INT
+STDCALL
+GetTypeByNameA(LPSTR ServiceName,
+               LPGUID ServiceType)
+{
+  OutputDebugStringW(L"w32sock GetTypeByNameA stub called\n");
+
+  return SOCKET_ERROR;
+}
+
+
+/*
+ * @unimplemented
+ */
+INT
+STDCALL
+GetTypeByNameW(LPWSTR ServiceName,
+               LPGUID ServiceType)
+{
+  OutputDebugStringW(L"w32sock GetTypeByNameW stub called\n");
+
+  return SOCKET_ERROR;
+}
+
+
+/*
+ * @unimplemented
+ */
+INT
+STDCALL
+MigrateWinsockConfiguration(DWORD Unknown1,
+                            DWORD Unknown2,
+                            DWORD Unknown3)
+{
+  OutputDebugStringW(L"w32sock MigrateWinsockConfiguration stub called\n");
+
+  return SOCKET_ERROR;
+}
+
+
+/*
+ * @unimplemented
+ */
+INT
+STDCALL
+SetServiceA(DWORD NameSpace,
+            DWORD Operation,
+            DWORD Flags,
+            LPSERVICE_INFOA ServiceInfo,
+            LPSERVICE_ASYNC_INFO ServiceAsyncInfo,
+            LPDWORD dwStatusFlags)
+{
+  OutputDebugStringW(L"w32sock SetServiceA stub called\n");
+
+  return SOCKET_ERROR;
+}
+
+
+/*
+ * @unimplemented
+ */
+INT
+STDCALL
+SetServiceW(DWORD NameSpace,
+            DWORD Operation,
+            DWORD Flags,
+            LPSERVICE_INFOW ServiceInfo,
+            LPSERVICE_ASYNC_INFO ServiceAsyncInfo,
+            LPDWORD dwStatusFlags)
+{
+  OutputDebugStringW(L"w32sock SetServiceW stub called\n");
+
+  return SOCKET_ERROR;
+}
+
+
+/*
+ * @unimplemented
+ */
+int
+STDCALL
+WSARecvEx(SOCKET Sock,
+          char *Buf,
+          int Len,
+          int *Flags)
+{
+  OutputDebugStringW(L"w32sock WSARecvEx stub called\n");
+
+  return SOCKET_ERROR;
+}
+
+
+/*
+ * @unimplemented
+ */
+int
+STDCALL
+dn_expand(unsigned char *MessagePtr,
+          unsigned char *EndofMesOrig,
+          unsigned char *CompDomNam,
+          unsigned char *ExpandDomNam,
+          int Length)
+{
+  OutputDebugStringW(L"w32sock dn_expand stub called\n");
+
+  return SOCKET_ERROR;
+}
+
+
+/*
+ * @unimplemented
+ */
+struct netent *
+STDCALL
+getnetbyname(const char *name)
+{
+  OutputDebugStringW(L"w32sock getnetbyname stub called\n");
+
+  return NULL;
+}
+
+
+/*
+ * @unimplemented
+ */
+UINT
+STDCALL
+inet_network(const char *cp)
+{
+  OutputDebugStringW(L"w32sock inet_network stub called\n");
+
+  return INADDR_NONE;
+}
+
+
+/*
+ * @unimplemented
+ */
+SOCKET
+STDCALL
+rcmd(char **AHost,
+     USHORT InPort,
+     char *LocUser,
+     char *RemUser,
+     char *Cmd,
+     int *Fd2p)
+{
+  OutputDebugStringW(L"w32sock rcmd stub called\n");
+
+  return INVALID_SOCKET;
+}
+
+
+/*
+ * @unimplemented
+ */
+SOCKET
+STDCALL
+rexec(char **AHost,
+      int InPort,
+      char *User,
+      char *Passwd,
+      char *Cmd,
+      int *Fd2p)
+{
+  OutputDebugStringW(L"w32sock rexec stub called\n");
+
+  return INVALID_SOCKET;
+}
+
+
+/*
+ * @unimplemented
+ */
+SOCKET
+STDCALL
+rresvport(int *port)
+{
+  OutputDebugStringW(L"w32sock rresvport stub called\n");
+
+  return INVALID_SOCKET;
+}
+
+
+/*
+ * @unimplemented
+ */
+void
+STDCALL
+s_perror(const char *str)
+{
+  OutputDebugStringW(L"w32sock s_perror stub called\n");
+}
+
+
+/*
+ * @unimplemented
+ */
+int
+STDCALL
+sethostname(char *Name, int NameLen)
+{
+  OutputDebugStringW(L"w32sock sethostname stub called\n");
+
+  return SOCKET_ERROR;
+}
+
+
+/*
+ * @unimplemented
+ */
+BOOL
+STDCALL
+DllMain(HINSTANCE InstDLL,
+        DWORD Reason,
+        LPVOID Reserved)
+{
+  return TRUE;
+}
+
+
+/*
+ * @unimplemented
+ */
+INT
+STDCALL
+GetNameByTypeA(LPGUID lpServiceType,LPSTR lpServiceName,DWORD dwNameLength)
+{
+  OutputDebugStringW(L"w32sock GetNameByTypeA stub called\n");
+  return TRUE;
+}
+
+
+/*
+ * @unimplemented
+ */
+INT
+STDCALL
+GetNameByTypeW(LPGUID lpServiceType,LPWSTR lpServiceName,DWORD dwNameLength)
+{
+  OutputDebugStringW(L"w32sock GetNameByTypeW stub called\n");
+  return TRUE;
+}
+
+
+/*
+ * @unimplemented
+ */
+INT
+STDCALL
+NSPStartup(
+    LPGUID lpProviderId,
+    LPNSP_ROUTINE lpnspRoutines
+    )
+{
+  return TRUE;
+}
+
+
+/*
+ * @unimplemented
+ */
+int
+STDCALL
+WSPStartup(
+    IN WORD wVersionRequested,
+    OUT LPWSPDATA lpWSPData,
+    IN LPWSAPROTOCOL_INFOW lpProtocolInfo,
+    IN WSPUPCALLTABLE UpcallTable,
+    OUT LPWSPPROC_TABLE lpProcTable
+    )
+{
+  return TRUE;
+}
+
+
+/*
+ * @unimplemented
+ */
+INT
+STDCALL
+NPLoadNameSpaces(
+    IN OUT LPDWORD lpdwVersion,
+    IN OUT LPNS_ROUTINE nsrBuffer,
+    IN OUT LPDWORD lpdwBufferLength
+    )
+{
+  OutputDebugStringW(L"mswsock NPLoadNameSpaces stub called\n");
+
+  *lpdwVersion = 1;
+
+  return TRUE;
+}
+
+
+/*
+ * @unimplemented
+ */
+VOID
+STDCALL
+StartWsdpService()
+{
+  OutputDebugStringW(L"mswsock StartWsdpService stub called\n");
+}
+
+
+/*
+ * @unimplemented
+ */
+VOID
+STDCALL
+StopWsdpService()
+{
+  OutputDebugStringW(L"mswsock StopWsdpService stub called\n");
+}
+
+
+/*
+ * @unimplemented
+ */
+DWORD
+STDCALL
+SvchostPushServiceGlobals(DWORD Value)
+{
+  OutputDebugStringW(L"mswsock SvchostPushServiceGlobals stub called\n");
+
+  return 0;
+}
+
+
+/*
+ * @unimplemented
+ */
+VOID
+STDCALL
+ServiceMain(DWORD Unknown1, DWORD Unknown2)
+{
+  OutputDebugStringW(L"mswsock ServiceMain stub called\n");
+}
diff --git a/reactos/dll/netapi32/access.c b/reactos/dll/netapi32/access.c
new file mode 100644 (file)
index 0000000..e3e0a36
--- /dev/null
@@ -0,0 +1,580 @@
+/*
+ * Copyright 2002 Andriy Palamarchuk
+ *
+ * netapi32 access functions
+ *
+ * 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 <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "lmcons.h"
+#include "lmaccess.h"
+#include "lmapibuf.h"
+#include "lmerr.h"
+#include "netapi32_misc.h"
+#include "wine/debug.h"
+#include "wine/unicode.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(netapi32);
+
+static const WCHAR sAdminUserName[] = {'A','d','m','i','n','i','s','t','r','a','t',
+                                'o','r',0};
+static const WCHAR sGuestUserName[] = {'G','u','e','s','t',0};
+
+/************************************************************
+ *                NETAPI_ValidateServername
+ *
+ * Validates server name
+ */
+static NET_API_STATUS NETAPI_ValidateServername(LPCWSTR ServerName)
+{
+    if (ServerName)
+    {
+        if (ServerName[0] == 0)
+            return ERROR_BAD_NETPATH;
+        else if (
+            ((ServerName[0] == '\\') &&
+             (ServerName[1] != '\\'))
+            ||
+            ((ServerName[0] == '\\') &&
+             (ServerName[1] == '\\') &&
+             (ServerName[2] == 0))
+            )
+            return ERROR_INVALID_NAME;
+    }
+    return NERR_Success;
+}
+
+/************************************************************
+ *                NETAPI_IsKnownUser
+ *
+ * Checks whether the user name indicates current user.
+ */
+static BOOL NETAPI_IsKnownUser(LPCWSTR UserName)
+{
+    DWORD dwSize = UNLEN + 1;
+    BOOL Result;
+    LPWSTR buf;
+
+    if (!lstrcmpW(UserName, sAdminUserName) ||
+        !lstrcmpW(UserName, sGuestUserName))
+        return TRUE;
+    NetApiBufferAllocate(dwSize * sizeof(WCHAR), (LPVOID *) &buf);
+    Result = GetUserNameW(buf, &dwSize);
+
+    Result = Result && !lstrcmpW(UserName, buf);
+    NetApiBufferFree(buf);
+
+    return Result;
+}
+
+#define NETAPI_ForceKnownUser(UserName, FailureCode) \
+    if (!NETAPI_IsKnownUser(UserName)) \
+    { \
+        FIXME("Can't find information for user %s\n", \
+              debugstr_w(UserName)); \
+        return FailureCode; \
+    }
+
+/************************************************************
+ *                NetUserAdd (NETAPI32.@)
+ */
+NET_API_STATUS WINAPI NetUserAdd(LPCWSTR servername,
+                  DWORD level, LPBYTE bufptr, LPDWORD parm_err)
+{
+    NET_API_STATUS status;
+    FIXME("(%s, %ld, %p, %p) stub!\n", debugstr_w(servername), level, bufptr, parm_err);
+
+    status = NETAPI_ValidateServername(servername);
+    if (status != NERR_Success)
+        return status;
+    
+    if ((bufptr != NULL) && (level > 0) && (level <= 4))
+    {
+        PUSER_INFO_1 ui = (PUSER_INFO_1) bufptr;
+        TRACE("usri%ld_name: %s\n", level, debugstr_w(ui->usri1_name));
+        TRACE("usri%ld_password: %s\n", level, debugstr_w(ui->usri1_password));
+        TRACE("usri%ld_comment: %s\n", level, debugstr_w(ui->usri1_comment));
+    }
+    return status;
+}
+
+/************************************************************
+ *                NetUserDel  (NETAPI32.@)
+ */
+NET_API_STATUS WINAPI NetUserDel(LPCWSTR servername, LPCWSTR username)
+{
+    NET_API_STATUS status;
+    FIXME("(%s, %s) stub!\n", debugstr_w(servername), debugstr_w(username));
+
+    status = NETAPI_ValidateServername(servername);
+    if (status != NERR_Success)
+        return status;
+
+    if (!NETAPI_IsKnownUser(username))
+        return NERR_UserNotFound;
+
+    /* Delete the user here */
+    return status;
+}
+
+/************************************************************
+ *                NetUserGetInfo  (NETAPI32.@)
+ */
+NET_API_STATUS WINAPI
+NetUserGetInfo(LPCWSTR servername, LPCWSTR username, DWORD level,
+               LPBYTE* bufptr)
+{
+    NET_API_STATUS status;
+    TRACE("(%s, %s, %ld, %p)\n", debugstr_w(servername), debugstr_w(username),
+          level, bufptr);
+    status = NETAPI_ValidateServername(servername);
+    if (status != NERR_Success)
+        return status;
+    NETAPI_ForceLocalComputer(servername, NERR_InvalidComputer);
+    NETAPI_ForceKnownUser(username, NERR_UserNotFound);
+
+    switch (level)
+    {
+    case 0:
+    {
+        PUSER_INFO_0 ui;
+        int name_sz;
+
+        name_sz = lstrlenW(username) + 1;
+
+        /* set up buffer */
+        NetApiBufferAllocate(sizeof(USER_INFO_0) + name_sz * sizeof(WCHAR),
+                             (LPVOID *) bufptr);
+
+        ui = (PUSER_INFO_0) *bufptr;
+        ui->usri0_name = (LPWSTR) (*bufptr + sizeof(USER_INFO_0));
+
+        /* get data */
+        lstrcpyW(ui->usri0_name, username);
+        break;
+    }
+
+    case 10:
+    {
+        PUSER_INFO_10 ui;
+        PUSER_INFO_0 ui0;
+        NET_API_STATUS status;
+        /* sizes of the field buffers in WCHARS */
+        int name_sz, comment_sz, usr_comment_sz, full_name_sz;
+
+        comment_sz = 1;
+        usr_comment_sz = 1;
+        full_name_sz = 1;
+
+        /* get data */
+        status = NetUserGetInfo(servername, username, 0, (LPBYTE *) &ui0);
+        if (status != NERR_Success)
+        {
+            NetApiBufferFree(ui0);
+            return status;
+        }
+        name_sz = lstrlenW(ui0->usri0_name) + 1;
+
+        /* set up buffer */
+        NetApiBufferAllocate(sizeof(USER_INFO_10) +
+                             (name_sz + comment_sz + usr_comment_sz +
+                              full_name_sz) * sizeof(WCHAR),
+                             (LPVOID *) bufptr);
+        ui = (PUSER_INFO_10) *bufptr;
+        ui->usri10_name = (LPWSTR) (*bufptr + sizeof(USER_INFO_10));
+        ui->usri10_comment = (LPWSTR) (
+            ((PBYTE) ui->usri10_name) + name_sz * sizeof(WCHAR));
+        ui->usri10_usr_comment = (LPWSTR) (
+            ((PBYTE) ui->usri10_comment) + comment_sz * sizeof(WCHAR));
+        ui->usri10_full_name = (LPWSTR) (
+            ((PBYTE) ui->usri10_usr_comment) + usr_comment_sz * sizeof(WCHAR));
+
+        /* set data */
+        lstrcpyW(ui->usri10_name, ui0->usri0_name);
+        NetApiBufferFree(ui0);
+        ui->usri10_comment[0] = 0;
+        ui->usri10_usr_comment[0] = 0;
+        ui->usri10_full_name[0] = 0;
+        break;
+    }
+
+    case 1:
+      {
+        static const WCHAR homedirW[] = {'H','O','M','E',0};
+        PUSER_INFO_1 ui;
+        PUSER_INFO_0 ui0;
+        NET_API_STATUS status;
+        /* sizes of the field buffers in WCHARS */
+        int name_sz, password_sz, home_dir_sz, comment_sz, script_path_sz;
+
+        password_sz = 1; /* not filled out for security reasons for NetUserGetInfo*/
+        comment_sz = 1;
+        script_path_sz = 1;
+
+       /* get data */
+        status = NetUserGetInfo(servername, username, 0, (LPBYTE *) &ui0);
+        if (status != NERR_Success)
+        {
+            NetApiBufferFree(ui0);
+            return status;
+        }
+        name_sz = lstrlenW(ui0->usri0_name) + 1;
+        home_dir_sz = GetEnvironmentVariableW(homedirW, NULL,0);
+        /* set up buffer */
+        NetApiBufferAllocate(sizeof(USER_INFO_1) +
+                             (name_sz + password_sz + home_dir_sz +
+                              comment_sz + script_path_sz) * sizeof(WCHAR),
+                             (LPVOID *) bufptr);
+
+        ui = (PUSER_INFO_1) *bufptr;
+        ui->usri1_name = (LPWSTR) (ui + 1);
+        ui->usri1_password = ui->usri1_name + name_sz;
+        ui->usri1_home_dir = ui->usri1_password + password_sz;
+        ui->usri1_comment = ui->usri1_home_dir + home_dir_sz;
+        ui->usri1_script_path = ui->usri1_comment + comment_sz;
+        /* set data */
+        lstrcpyW(ui->usri1_name, ui0->usri0_name);
+        NetApiBufferFree(ui0);
+        ui->usri1_password[0] = 0;
+        ui->usri1_password_age = 0;
+        ui->usri1_priv = 0;
+        GetEnvironmentVariableW(homedirW, ui->usri1_home_dir,home_dir_sz);
+        ui->usri1_comment[0] = 0;
+        ui->usri1_flags = 0;
+        ui->usri1_script_path[0] = 0;
+        break;
+      }
+    case 2:
+    case 3:
+    case 4:
+    case 11:
+    case 20:
+    case 23:
+    case 1003:
+    case 1005:
+    case 1006:
+    case 1007:
+    case 1008:
+    case 1009:
+    case 1010:
+    case 1011:
+    case 1012:
+    case 1013:
+    case 1014:
+    case 1017:
+    case 1018:
+    case 1020:
+    case 1023:
+    case 1024:
+    case 1025:
+    case 1051:
+    case 1052:
+    case 1053:
+    {
+        FIXME("Level %ld is not implemented\n", level);
+        break;
+    }
+    default:
+        ERR("Invalid level %ld is specified\n", level);
+        return ERROR_INVALID_LEVEL;
+    }
+    return NERR_Success;
+}
+
+
+
+/************************************************************
+ *                NetUserEnum  (NETAPI32.@)
+ */
+NET_API_STATUS WINAPI
+NetUserEnum(LPCWSTR servername, DWORD level, DWORD filter, LPBYTE* bufptr,
+           DWORD prefmaxlen, LPDWORD entriesread, LPDWORD totalentries,
+           LPDWORD resume_handle)
+{
+  FIXME("(%s,%ld, 0x%ld,%p,%ld,%p,%p,%p) stub!\n", debugstr_w(servername), level,
+        filter, bufptr, prefmaxlen, entriesread, totalentries, resume_handle);
+
+  return ERROR_ACCESS_DENIED;
+}
+
+/************************************************************
+ *                ACCESS_QueryAdminDisplayInformation
+ *
+ *  Creates a buffer with information for the Admin User
+ */
+static void ACCESS_QueryAdminDisplayInformation(PNET_DISPLAY_USER *buf, PDWORD pdwSize)
+{
+    static const WCHAR sAdminUserName[] = {
+        'A','d','m','i','n','i','s','t','r','a','t','o','r',0};
+
+    /* sizes of the field buffers in WCHARS */
+    int name_sz, comment_sz, full_name_sz;
+    PNET_DISPLAY_USER usr;
+
+    /* set up buffer */
+    name_sz = lstrlenW(sAdminUserName);
+    comment_sz = 1;
+    full_name_sz = 1;
+    
+    *pdwSize = sizeof(NET_DISPLAY_USER);
+    *pdwSize += (name_sz + comment_sz + full_name_sz) * sizeof(WCHAR);
+    NetApiBufferAllocate(*pdwSize, (LPVOID *) buf);
+
+    usr = *buf;
+    usr->usri1_name = (LPWSTR) ((PBYTE) usr + sizeof(NET_DISPLAY_USER));
+    usr->usri1_comment = (LPWSTR) (
+        ((PBYTE) usr->usri1_name) + name_sz * sizeof(WCHAR));
+    usr->usri1_full_name = (LPWSTR) (
+        ((PBYTE) usr->usri1_comment) + comment_sz * sizeof(WCHAR));
+
+    /* set data */
+    lstrcpyW(usr->usri1_name, sAdminUserName);
+    usr->usri1_comment[0] = 0;
+    usr->usri1_flags = UF_SCRIPT | UF_NORMAL_ACCOUNT | UF_DONT_EXPIRE_PASSWD;
+    usr->usri1_full_name[0] = 0;
+    usr->usri1_user_id = 500;
+    usr->usri1_next_index = 0;
+}
+
+/************************************************************
+ *                ACCESS_QueryGuestDisplayInformation
+ *
+ *  Creates a buffer with information for the Guest User
+ */
+static void ACCESS_QueryGuestDisplayInformation(PNET_DISPLAY_USER *buf, PDWORD pdwSize)
+{
+    static const WCHAR sGuestUserName[] = {
+        'G','u','e','s','t',0 };
+
+    /* sizes of the field buffers in WCHARS */
+    int name_sz, comment_sz, full_name_sz;
+    PNET_DISPLAY_USER usr;
+
+    /* set up buffer */
+    name_sz = lstrlenW(sGuestUserName);
+    comment_sz = 1;
+    full_name_sz = 1;
+    
+    *pdwSize = sizeof(NET_DISPLAY_USER);
+    *pdwSize += (name_sz + comment_sz + full_name_sz) * sizeof(WCHAR);
+    NetApiBufferAllocate(*pdwSize, (LPVOID *) buf);
+
+    usr = *buf;
+    usr->usri1_name = (LPWSTR) ((PBYTE) usr + sizeof(NET_DISPLAY_USER));
+    usr->usri1_comment = (LPWSTR) (
+        ((PBYTE) usr->usri1_name) + name_sz * sizeof(WCHAR));
+    usr->usri1_full_name = (LPWSTR) (
+        ((PBYTE) usr->usri1_comment) + comment_sz * sizeof(WCHAR));
+
+    /* set data */
+    lstrcpyW(usr->usri1_name, sGuestUserName);
+    usr->usri1_comment[0] = 0;
+    usr->usri1_flags = UF_ACCOUNTDISABLE | UF_SCRIPT | UF_NORMAL_ACCOUNT |
+        UF_DONT_EXPIRE_PASSWD;
+    usr->usri1_full_name[0] = 0;
+    usr->usri1_user_id = 500;
+    usr->usri1_next_index = 0;
+}
+
+/************************************************************
+ *                NetQueryDisplayInformation  (NETAPI32.@)
+ * Copies NET_DISPLAY_USER record.
+ */
+static void ACCESS_CopyDisplayUser(PNET_DISPLAY_USER dest, LPWSTR *dest_buf,
+                            PNET_DISPLAY_USER src)
+{
+    LPWSTR str = *dest_buf;
+
+    src->usri1_name = str;
+    lstrcpyW(src->usri1_name, dest->usri1_name);
+    str = (LPWSTR) (
+        ((PBYTE) str) + (lstrlenW(str) + 1) * sizeof(WCHAR));
+
+    src->usri1_comment = str;
+    lstrcpyW(src->usri1_comment, dest->usri1_comment);
+    str = (LPWSTR) (
+        ((PBYTE) str) + (lstrlenW(str) + 1) * sizeof(WCHAR));
+
+    src->usri1_flags = dest->usri1_flags;
+
+    src->usri1_full_name = str;
+    lstrcpyW(src->usri1_full_name, dest->usri1_full_name);
+    str = (LPWSTR) (
+        ((PBYTE) str) + (lstrlenW(str) + 1) * sizeof(WCHAR));
+
+    src->usri1_user_id = dest->usri1_user_id;
+    src->usri1_next_index = dest->usri1_next_index;
+    *dest_buf = str;
+}
+
+/************************************************************
+ *                NetQueryDisplayInformation  (NETAPI32.@)
+ *
+ * The buffer structure:
+ * - array of fixed size record of the level type
+ * - strings, referenced by the record of the level type
+ */
+NET_API_STATUS WINAPI
+NetQueryDisplayInformation(
+    LPCWSTR ServerName, DWORD Level, DWORD Index, DWORD EntriesRequested,
+    DWORD PreferredMaximumLength, LPDWORD ReturnedEntryCount,
+    PVOID *SortedBuffer)
+{
+    TRACE("(%s, %ld, %ld, %ld, %ld, %p, %p)\n", debugstr_w(ServerName),
+          Level, Index, EntriesRequested, PreferredMaximumLength,
+          ReturnedEntryCount, SortedBuffer);
+    NETAPI_ForceLocalComputer(ServerName, ERROR_ACCESS_DENIED);
+    switch (Level)
+    {
+    case 1:
+    {
+        /* current record */
+        PNET_DISPLAY_USER inf;
+        /* current available strings buffer */
+        LPWSTR str;
+        PNET_DISPLAY_USER admin, guest;
+        DWORD admin_size, guest_size;
+        LPWSTR name = NULL;
+        DWORD dwSize;
+
+        /* sizes of the field buffers in WCHARS */
+        int name_sz, comment_sz, full_name_sz;
+
+        /* number of the records, returned in SortedBuffer
+           3 - for current user, Administrator and Guest users
+         */
+        int records = 3;
+
+        FIXME("Level %ld partially implemented\n", Level);
+        *ReturnedEntryCount = records;
+        comment_sz = 1;
+        full_name_sz = 1;
+
+        /* get data */
+        dwSize = UNLEN + 1;
+        NetApiBufferAllocate(dwSize, (LPVOID *) &name);
+        if (!GetUserNameW(name, &dwSize))
+        {
+            NetApiBufferFree(name);
+            return ERROR_ACCESS_DENIED;
+        }
+        name_sz = dwSize;
+        ACCESS_QueryAdminDisplayInformation(&admin, &admin_size);
+        ACCESS_QueryGuestDisplayInformation(&guest, &guest_size);
+
+        /* set up buffer */
+        dwSize = sizeof(NET_DISPLAY_USER) * records;
+        dwSize += (name_sz + comment_sz + full_name_sz) * sizeof(WCHAR);
+
+        NetApiBufferAllocate(dwSize +
+                             admin_size - sizeof(NET_DISPLAY_USER) +
+                             guest_size - sizeof(NET_DISPLAY_USER),
+                             (LPVOID *) SortedBuffer);
+        inf = (PNET_DISPLAY_USER) *SortedBuffer;
+        str = (LPWSTR) ((PBYTE) inf + sizeof(NET_DISPLAY_USER) * records);
+        inf->usri1_name = str;
+        str = (LPWSTR) (
+            ((PBYTE) str) + name_sz * sizeof(WCHAR));
+        inf->usri1_comment = str;
+        str = (LPWSTR) (
+            ((PBYTE) str) + comment_sz * sizeof(WCHAR));
+        inf->usri1_full_name = str;
+        str = (LPWSTR) (
+            ((PBYTE) str) + full_name_sz * sizeof(WCHAR));
+
+        /* set data */
+        lstrcpyW(inf->usri1_name, name);
+        NetApiBufferFree(name);
+        inf->usri1_comment[0] = 0;
+        inf->usri1_flags =
+            UF_SCRIPT | UF_NORMAL_ACCOUNT | UF_DONT_EXPIRE_PASSWD;
+        inf->usri1_full_name[0] = 0;
+        inf->usri1_user_id = 0;
+        inf->usri1_next_index = 0;
+
+        inf++;
+        ACCESS_CopyDisplayUser(admin, &str, inf);
+        NetApiBufferFree(admin);
+
+        inf++;
+        ACCESS_CopyDisplayUser(guest, &str, inf);
+        NetApiBufferFree(guest);
+        break;
+    }
+
+    case 2:
+    case 3:
+    {
+        FIXME("Level %ld is not implemented\n", Level);
+        break;
+    }
+
+    default:
+        ERR("Invalid level %ld is specified\n", Level);
+        return ERROR_INVALID_LEVEL;
+    }
+    return NERR_Success;
+}
+
+/************************************************************
+ *                NetGetDCName  (NETAPI32.@)
+ *
+ *  Return the name of the primary domain controller (PDC)
+ */
+
+NET_API_STATUS WINAPI
+NetGetDCName(LPCWSTR servername, LPCWSTR domainname, LPBYTE *bufptr)
+{
+  FIXME("(%s, %s, %p) stub!\n", debugstr_w(servername),
+                 debugstr_w(domainname), bufptr);
+  return NERR_DCNotFound; /* say we can't find a domain controller */  
+}
+
+
+/************************************************************
+ *                NetUserModalsGet  (NETAPI32.@)
+ */
+NET_API_STATUS WINAPI NetUserModalsGet(LPCWSTR szServer, DWORD level, LPBYTE *pbuffer)
+{
+    FIXME("(%s %ld %p) stub!\n", debugstr_w(szServer), level, pbuffer);
+    return NERR_InternalError;
+}
+
+/************************************************************
+ *                NetLocalGroupAdd  (NETAPI32.@)
+ */
+NET_API_STATUS WINAPI NetLocalGroupAdd(LPCWSTR servername, DWORD level,
+                 LPBYTE buf, LPDWORD parm_err)
+{
+    FIXME("(%s %ld %p %p) stub!\n", debugstr_w(servername), level, buf, parm_err);
+    return NERR_Success;
+}
+
+/************************************************************
+ *                NetLocalGroupSetMember (NETAPI32.@)
+ */
+
+NET_API_STATUS WINAPI NetLocalGroupSetMembers(LPCWSTR servername,
+             LPCWSTR groupname, DWORD level, LPBYTE buf, DWORD totalentries)
+{
+    FIXME("(%s %s %ld %p %ld) stub!\n", debugstr_w(servername), 
+            debugstr_w(groupname),level, buf, totalentries);
+    return NERR_Success;
+}
diff --git a/reactos/dll/netapi32/apibuf.c b/reactos/dll/netapi32/apibuf.c
new file mode 100644 (file)
index 0000000..6a8b8db
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2002 Andriy Palamarchuk
+ *
+ * Net API buffer calls
+ *
+ * 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 <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "lmcons.h"
+#include "lmapibuf.h"
+#include "lmerr.h"
+#include "winerror.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(netapi32);
+
+/************************************************************
+ *                NetApiBufferAllocate  (NETAPI32.@)
+ */
+NET_API_STATUS WINAPI NetApiBufferAllocate(DWORD ByteCount, LPVOID* Buffer)
+{
+    TRACE("(%ld, %p)\n", ByteCount, Buffer);
+    *Buffer = HeapAlloc(GetProcessHeap(), 0, ByteCount);
+    if (*Buffer)
+        return NERR_Success;
+    else
+        return GetLastError();
+}
+
+/************************************************************
+ *                NetApiBufferFree  (NETAPI32.@)
+ */
+NET_API_STATUS WINAPI NetApiBufferFree(LPVOID Buffer)
+{
+    TRACE("(%p)\n", Buffer);
+    HeapFree(GetProcessHeap(), 0, Buffer);
+    return NERR_Success;
+}
+
+/************************************************************
+ *                NetApiBufferReallocate  (NETAPI32.@)
+ */
+NET_API_STATUS WINAPI NetApiBufferReallocate(LPVOID OldBuffer, DWORD NewByteCount,
+                                             LPVOID* NewBuffer)
+{
+    TRACE("(%p, %ld, %p)\n", OldBuffer, NewByteCount, NewBuffer);
+    if (NewByteCount) 
+    {
+        if (OldBuffer)
+            *NewBuffer = HeapReAlloc(GetProcessHeap(), 0, OldBuffer, NewByteCount);
+        else
+            *NewBuffer = HeapAlloc(GetProcessHeap(), 0, NewByteCount);
+       return *NewBuffer ? NERR_Success : GetLastError();
+    } 
+    else 
+    {
+       if (!HeapFree(GetProcessHeap(), 0, OldBuffer)) return GetLastError();
+       *NewBuffer = 0;
+       return NERR_Success;
+    }
+}
+
+/************************************************************
+ *                NetApiBufferSize  (NETAPI32.@)
+ */
+NET_API_STATUS WINAPI NetApiBufferSize(LPVOID Buffer, LPDWORD ByteCount)
+{
+    DWORD dw;
+
+    TRACE("(%p, %p)\n", Buffer, ByteCount);
+    if (Buffer == NULL)
+        return ERROR_INVALID_PARAMETER;
+    dw = HeapSize(GetProcessHeap(), 0, Buffer);
+    TRACE("size: %ld\n", dw);
+    if (dw != 0xFFFFFFFF)
+        *ByteCount = dw;
+    else
+        *ByteCount = 0;
+
+    return NERR_Success;
+}
diff --git a/reactos/dll/netapi32/browsr.c b/reactos/dll/netapi32/browsr.c
new file mode 100644 (file)
index 0000000..b0ed893
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2002 Andriy Palamarchuk
+ *
+ * netapi32 browser functions
+ *
+ * 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 <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "lmcons.h"
+#include "lmbrowsr.h"
+#include "lmshare.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(netapi32);
+
+/************************************************************
+ *                I_BrowserSetNetlogonState  (NETAPI32.@)
+ */
+NET_API_STATUS WINAPI I_BrowserSetNetlogonState(
+    LPWSTR ServerName, LPWSTR DomainName, LPWSTR EmulatedServerName,
+    DWORD Role)
+{
+    return ERROR_NOT_SUPPORTED;
+}
+
+/************************************************************
+ *                I_BrowserQueryEmulatedDomains  (NETAPI32.@)
+ */
+NET_API_STATUS WINAPI I_BrowserQueryEmulatedDomains(
+    LPWSTR ServerName, PBROWSER_EMULATED_DOMAIN *EmulatedDomains,
+    LPDWORD EntriesRead)
+{
+    return ERROR_NOT_SUPPORTED;
+}
+
+/************************************************************
+ *                NetShareEnum  (NETAPI32.@)
+ */
+NET_API_STATUS WINAPI NetShareEnum( LPWSTR servername, DWORD level, LPBYTE* bufptr,
+  DWORD prefmaxlen, LPDWORD entriesread, LPDWORD totalentries, LPDWORD resume_handle)
+{
+    FIXME("%s %ld %p %ld %p %p %p\n", debugstr_w(servername), level, bufptr,
+          prefmaxlen, entriesread, totalentries, resume_handle);
+    return ERROR_NOT_SUPPORTED;
+}
diff --git a/reactos/dll/netapi32/ds.c b/reactos/dll/netapi32/ds.c
new file mode 100644 (file)
index 0000000..1558277
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2005 Paul Vriens
+ *
+ * netapi32 directory service functions
+ *
+ * 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 <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "wine/debug.h"
+#include "dsrole.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(ds);
+
+/************************************************************
+ *  DsRoleFreeMemory (NETAPI32.@)
+ *
+ * PARAMS
+ *  Buffer [I] Pointer to the to-be-freed buffer.
+ *
+ * RETURNS
+ *  Nothing
+ */
+VOID WINAPI DsRoleFreeMemory(PVOID Buffer)
+{
+    FIXME("(%p) stub\n", Buffer);
+}
+
+/************************************************************
+ *  DsRoleGetPrimaryDomainInformation  (NETAPI32.@)
+ *
+ * PARAMS
+ *  lpServer  [I] Pointer to UNICODE string with Computername
+ *  InfoLevel [I] Type of data to retrieve     
+ *  Buffer    [O] Pointer to to the requested data
+ *
+ * RETURNS
+ *
+ * NOTES
+ *  When lpServer is NULL, use the local computer
+ */
+DWORD WINAPI DsRoleGetPrimaryDomainInformation(
+    LPCWSTR lpServer, DSROLE_PRIMARY_DOMAIN_INFO_LEVEL InfoLevel,
+    PBYTE* Buffer)
+{
+    FIXME("(%p, %d, %p) stub\n", lpServer, InfoLevel, Buffer);
+
+    /* Check some input parameters */
+
+    if (!Buffer) return ERROR_INVALID_PARAMETER;
+    if ((InfoLevel < DsRolePrimaryDomainInfoBasic) || (InfoLevel > DsRoleOperationState)) return ERROR_INVALID_PARAMETER;
+
+    return E_NOTIMPL;
+}
diff --git a/reactos/dll/netapi32/nbcmdqueue.c b/reactos/dll/netapi32/nbcmdqueue.c
new file mode 100644 (file)
index 0000000..d8948f7
--- /dev/null
@@ -0,0 +1,199 @@
+/* Copyright (c) 2003 Juan Lang
+ *
+ * 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 "wine/debug.h"
+#include "nbcmdqueue.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(netbios);
+
+struct NBCmdQueue
+{
+    HANDLE           heap;
+    CRITICAL_SECTION cs;
+    PNCB             head;
+};
+
+#define CANCEL_EVENT_PTR(ncb) (PHANDLE)((ncb)->ncb_reserve)
+#define NEXT_PTR(ncb) (PNCB *)((ncb)->ncb_reserve + sizeof(HANDLE))
+
+/* The reserved area of an ncb will be used for the following data:
+ * - a cancelled flag (BOOL, 4 bytes??)
+ * - a handle to an event that's set by a cancelled command on completion
+ *   (HANDLE, 4 bytes)
+ * These members are used in the following way
+ * - on cancel, set the event member of the reserved field (with create event)
+ * - NBCmdComplete will delete the ncb from the queue of there's no event;
+ *   otherwise it will set the event and not delete the ncb
+ * - cancel must lock the queue before finding the ncb in it, and can unlock it
+ *   once it's set the event (and the cancelled flag)
+ * - NBCmdComplete must lock the queue before attempting to remove the ncb or
+ *   check the event
+ * - NBCmdQueueCancelAll will lock the queue, and cancel all ncb's in the queue.
+ *   It'll then unlock the queue, and wait on the event in the head of the queue
+ *   until there's no more ncb's in the queue.
+ * Space optimization: use the handle as a boolean.  NULL == 0 => not cancelled.
+ * Non-NULL == valid handle => cancelled.  This allows storing a next pointer
+ * in the ncb's reserved field as well, avoiding a memory alloc for a new
+ * command (cool).
+ */
+
+struct NBCmdQueue *NBCmdQueueCreate(HANDLE heap)
+{
+    struct NBCmdQueue *queue;
+
+    if (heap == NULL)
+        heap = GetProcessHeap();
+    queue = HeapAlloc(heap, 0, sizeof(struct NBCmdQueue));
+    if (queue)
+    {
+        queue->heap = heap;
+        InitializeCriticalSection(&queue->cs);
+        queue->head = NULL;
+    }
+    return queue;
+}
+
+UCHAR NBCmdQueueAdd(struct NBCmdQueue *queue, PNCB ncb)
+{
+    UCHAR ret;
+
+    TRACE(": queue %p, ncb %p\n", queue, ncb);
+
+    if (!queue)
+        return NRC_BADDR;
+    if (!ncb)
+        return NRC_INVADDRESS;
+
+    *CANCEL_EVENT_PTR(ncb) = NULL;
+    EnterCriticalSection(&queue->cs);
+    *NEXT_PTR(ncb) = queue->head;
+    queue->head = ncb;
+    ret = NRC_GOODRET;
+    LeaveCriticalSection(&queue->cs);
+    TRACE("returning 0x%02x\n", ret);
+    return ret;
+}
+
+static PNCB *NBCmdQueueFindNBC(struct NBCmdQueue *queue, PNCB ncb)
+{
+    PNCB *ret;
+
+    if (!queue || !ncb)
+        ret = NULL;
+    else
+    {
+        ret = &queue->head;
+        while (ret && *ret != ncb)
+            ret = NEXT_PTR(*ret);
+    }
+    return ret;
+}
+
+UCHAR NBCmdQueueCancel(struct NBCmdQueue *queue, PNCB ncb)
+{
+    UCHAR ret;
+    PNCB *spot;
+
+    TRACE(": queue %p, ncb %p\n", queue, ncb);
+
+    if (!queue)
+        return NRC_BADDR;
+    if (!ncb)
+        return NRC_INVADDRESS;
+
+    EnterCriticalSection(&queue->cs);
+    spot = NBCmdQueueFindNBC(queue, ncb);
+    if (spot)
+    {
+        *CANCEL_EVENT_PTR(*spot) = CreateEventW(NULL, FALSE, FALSE, NULL);
+        WaitForSingleObject(*CANCEL_EVENT_PTR(*spot), INFINITE);
+        CloseHandle(*CANCEL_EVENT_PTR(*spot));
+        *spot = *NEXT_PTR(*spot);
+        if (ncb->ncb_retcode == NRC_CMDCAN)
+            ret = NRC_CMDCAN;
+        else
+            ret = NRC_CANOCCR;
+    }
+    else
+        ret = NRC_INVADDRESS;
+    LeaveCriticalSection(&queue->cs);
+    TRACE("returning 0x%02x\n", ret);
+    return ret;
+}
+
+UCHAR NBCmdQueueComplete(struct NBCmdQueue *queue, PNCB ncb, UCHAR retcode)
+{
+    UCHAR ret;
+    PNCB *spot;
+
+    TRACE(": queue %p, ncb %p\n", queue, ncb);
+
+    if (!queue)
+        return NRC_BADDR;
+    if (!ncb)
+        return NRC_INVADDRESS;
+
+    EnterCriticalSection(&queue->cs);
+    spot = NBCmdQueueFindNBC(queue, ncb);
+    if (spot)
+    {
+        if (*CANCEL_EVENT_PTR(*spot))
+            SetEvent(*CANCEL_EVENT_PTR(*spot));
+        else
+            *spot = *NEXT_PTR(*spot);
+        ret = NRC_GOODRET;
+    }
+    else
+        ret = NRC_INVADDRESS;
+    LeaveCriticalSection(&queue->cs);
+    TRACE("returning 0x%02x\n", ret);
+    return ret;
+}
+
+UCHAR NBCmdQueueCancelAll(struct NBCmdQueue *queue)
+{
+    UCHAR ret;
+
+    TRACE(": queue %p\n", queue);
+
+    if (!queue)
+        return NRC_BADDR;
+
+    EnterCriticalSection(&queue->cs);
+    while (queue->head)
+    {
+        TRACE(": waiting for ncb %p (command 0x%02x)\n", queue->head,
+         queue->head->ncb_command);
+        NBCmdQueueCancel(queue, queue->head);
+    }
+    LeaveCriticalSection(&queue->cs);
+    ret = NRC_GOODRET;
+    TRACE("returning 0x%02x\n", ret);
+    return ret;
+}
+
+void NBCmdQueueDestroy(struct NBCmdQueue *queue)
+{
+    TRACE(": queue %p\n", queue);
+
+    if (queue)
+    {
+        NBCmdQueueCancelAll(queue);
+        DeleteCriticalSection(&queue->cs);
+        HeapFree(queue->heap, 0, queue);
+    }
+}
diff --git a/reactos/dll/netapi32/nbcmdqueue.h b/reactos/dll/netapi32/nbcmdqueue.h
new file mode 100644 (file)
index 0000000..f06f95b
--- /dev/null
@@ -0,0 +1,66 @@
+/* Copyright (c) 2003 Juan Lang
+ *
+ * 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
+ */
+#ifndef __NBCMDQUEUE_H__
+#define __NBCMDQUEUE_H__
+
+#include <stdarg.h>
+#include "windef.h"
+#include "winbase.h"
+#include "nb30.h"
+
+/* This file defines a queue of pending NetBIOS commands.  The queue operations
+ * are thread safe, with the exception of NBCmdQueueDestroy:  ensure no other
+ * threads are manipulating the queue when calling NBCmdQueueDestroy.
+ */
+
+struct NBCmdQueue;
+
+/* Allocates a new command queue from heap. */
+struct NBCmdQueue *NBCmdQueueCreate(HANDLE heap);
+
+/* Adds ncb to queue.  Assumes queue is not NULL, and ncb is not already in the
+ * queue.  If ncb is already in the queue, returns NRC_TOOMANY.
+ */
+UCHAR NBCmdQueueAdd(struct NBCmdQueue *queue, PNCB ncb);
+
+/* Cancels the given ncb.  Blocks until the command completes.  Implicitly
+ * removes ncb from the queue.  Assumes queue and ncb are not NULL, and that
+ * ncb has been added to queue previously.
+ * Returns NRC_CMDCAN on a successful cancellation, NRC_CMDOCCR if the command
+ * completed before it could be cancelled, and various other return values for
+ * different failures.
+ */
+UCHAR NBCmdQueueCancel(struct NBCmdQueue *queue, PNCB ncb);
+
+/* Sets the return code of the given ncb, and implicitly removes the command
+ * from the queue.  Assumes queue and ncb are not NULL, and that ncb has been
+ * added to queue previously.
+ * Returns NRC_GOODRET on success.
+ */
+UCHAR NBCmdQueueComplete(struct NBCmdQueue *queue, PNCB ncb, UCHAR retcode);
+
+/* Cancels all pending commands in the queue (useful for a RESET or a shutdown).
+ * Returns when all commands have been completed.
+ */
+UCHAR NBCmdQueueCancelAll(struct NBCmdQueue *queue);
+
+/* Frees all memory associated with the queue.  Blocks until all commands
+ * pending in the queue have been completed.
+ */
+void NBCmdQueueDestroy(struct NBCmdQueue *queue);
+
+#endif /* __NBCMDQUEUE_H__ */
diff --git a/reactos/dll/netapi32/nbnamecache.c b/reactos/dll/netapi32/nbnamecache.c
new file mode 100644 (file)
index 0000000..1fdb705
--- /dev/null
@@ -0,0 +1,215 @@
+/* Copyright (c) 2003 Juan Lang
+ *
+ * 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
+ *
+ * This implementation uses a linked list, because I don't have a decent
+ * hash table implementation handy.  This is somewhat inefficient, but it's
+ * rather more efficient than not having a name cache at all.
+ */
+
+#include "config.h"
+#include "wine/port.h"
+#include "wine/debug.h"
+
+#include "nbnamecache.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(netbios);
+
+typedef struct _NBNameCacheNode
+{
+    DWORD expireTime;
+    NBNameCacheEntry *entry;
+    struct _NBNameCacheNode *next;
+} NBNameCacheNode;
+
+struct NBNameCache
+{
+    HANDLE heap;
+    CRITICAL_SECTION cs;
+    DWORD entryExpireTimeMS;
+    NBNameCacheNode *head;
+};
+
+/* Unlinks the node pointed to by *prev, and frees any associated memory.
+ * If that node's next pointed to another node, *prev now points to it.
+ * Assumes the caller owns cache's lock.
+ */
+static void NBNameCacheUnlinkNode(struct NBNameCache *cache,
+ NBNameCacheNode **prev)
+{
+    if (cache && prev && *prev)
+    {
+        NBNameCacheNode *next = (*prev)->next;
+
+        HeapFree(cache->heap, 0, (*prev)->entry);
+        HeapFree(cache->heap, 0, *prev);
+        *prev = next;
+    }
+}
+
+/* Walks the list beginning with cache->head looking for the node with name
+ * name.  If the node is found, returns a pointer to the next pointer of the
+ * node _prior_ to the found node (or head if head points to it).  Thus, if the
+ * node's all you want, dereference the return value twice.  If you want to
+ * modify the list, modify the referent of the return value.
+ * While it's at it, deletes nodes whose time has expired (except the node
+ * you're looking for, of course).
+ * Returns NULL if the node isn't found.
+ * Assumes the caller owns cache's lock.
+ */
+static NBNameCacheNode **NBNameCacheWalk(struct NBNameCache *cache,
+ const char name[NCBNAMSZ])
+{
+    NBNameCacheNode **ret = NULL;
+
+    if (cache && cache->head)
+    {
+        NBNameCacheNode **ptr;
+
+        ptr = &cache->head;
+        while (ptr && *ptr && (*ptr)->entry)
+        {
+            if (!memcmp((*ptr)->entry->name, name, NCBNAMSZ - 1))
+                ret = ptr;
+            else
+            {
+                if (GetTickCount() > (*ptr)->expireTime)
+                    NBNameCacheUnlinkNode(cache, ptr);
+            }
+            if (*ptr)
+                ptr = &(*ptr)->next;
+        }
+    }
+    return ret;
+}
+
+struct NBNameCache *NBNameCacheCreate(HANDLE heap, DWORD entryExpireTimeMS)
+{
+    struct NBNameCache *cache;
+    
+    
+    if (!heap)
+        heap = GetProcessHeap();
+    cache = HeapAlloc(heap, 0, sizeof(struct NBNameCache));
+    if (cache)
+    {
+        cache->heap = heap;
+        InitializeCriticalSection(&cache->cs);
+        cache->entryExpireTimeMS = entryExpireTimeMS;
+        cache->head = NULL;
+    }
+    return cache;
+}
+
+BOOL NBNameCacheAddEntry(struct NBNameCache *cache, NBNameCacheEntry *entry)
+{
+    BOOL ret;
+
+    if (cache && entry)
+    {
+        NBNameCacheNode **node;
+
+        EnterCriticalSection(&cache->cs);
+        node = NBNameCacheWalk(cache, (char*)entry->name);
+        if (node)
+        {
+            (*node)->expireTime = GetTickCount() +
+             cache->entryExpireTimeMS;
+            HeapFree(cache->heap, 0, (*node)->entry);
+            (*node)->entry = entry;
+            ret = TRUE;
+        }
+        else
+        {
+            NBNameCacheNode *newNode = HeapAlloc(cache->heap, 0, sizeof(NBNameCacheNode));
+            if (newNode)
+            {
+                newNode->expireTime = GetTickCount() +
+                 cache->entryExpireTimeMS;
+                newNode->entry = entry;
+                newNode->next = cache->head;
+                cache->head = newNode;
+                ret = TRUE;
+            }
+            else
+                ret = FALSE;
+        }
+        LeaveCriticalSection(&cache->cs);
+    }
+    else
+        ret = FALSE;
+    return ret;
+}
+
+const NBNameCacheEntry *NBNameCacheFindEntry(struct NBNameCache *cache,
+ const UCHAR name[NCBNAMSZ])
+{
+    const NBNameCacheEntry *ret;
+    UCHAR printName[NCBNAMSZ];
+
+    memcpy(printName, name, NCBNAMSZ - 1);
+    printName[NCBNAMSZ - 1] = '\0';
+    if (cache)
+    {
+        NBNameCacheNode **node;
+
+        EnterCriticalSection(&cache->cs);
+        node = NBNameCacheWalk(cache, (char*)name);
+        if (node)
+            ret = (*node)->entry;
+        else
+            ret = NULL;
+        LeaveCriticalSection(&cache->cs);
+    }
+    else
+        ret = NULL;
+    return ret;
+}
+
+BOOL NBNameCacheUpdateNBName(struct NBNameCache *cache,
+ const UCHAR name[NCBNAMSZ], const UCHAR nbname[NCBNAMSZ])
+{
+    BOOL ret;
+
+    if (cache)
+    {
+        NBNameCacheNode **node;
+
+        EnterCriticalSection(&cache->cs);
+        node = NBNameCacheWalk(cache, (char*)name);
+        if (node && *node && (*node)->entry)
+        {
+            memcpy((*node)->entry->nbname, nbname, NCBNAMSZ);
+            ret = TRUE;
+        }
+        else
+            ret = FALSE;
+        LeaveCriticalSection(&cache->cs);
+    }
+    else
+        ret = FALSE;
+    return ret;
+}
+
+void NBNameCacheDestroy(struct NBNameCache *cache)
+{
+    if (cache)
+    {
+        DeleteCriticalSection(&cache->cs);
+        while (cache->head)
+            NBNameCacheUnlinkNode(cache, &cache->head);
+        HeapFree(cache->heap, 0, cache);
+    }
+}
diff --git a/reactos/dll/netapi32/nbnamecache.h b/reactos/dll/netapi32/nbnamecache.h
new file mode 100644 (file)
index 0000000..04e3663
--- /dev/null
@@ -0,0 +1,82 @@
+/* Copyright (c) 2003 Juan Lang
+ *
+ * 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
+ */
+#ifndef __WINE_NBNAMECACHE_H
+#define __WINE_NBNAMECACHE_H
+
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "nb30.h"
+
+struct NBNameCache;
+
+/* Represents an entry in the name cache.  If the NetBIOS name is known, it's
+ * in nbname.  Otherwise, nbname begins with '*'.  numAddresses defines the
+ * number of addresses in addresses.
+ * Notice that it allows multiple addresses per name, but doesn't explicitly
+ * allow group names.  That's because all names so far are unique; if a use for
+ * group names comes up, adding a flag here is simple enough.
+ * Also, only the first NCBNAMSZ - 1 bytes are considered significant.  This is
+ * because a name may have been resolved using DNS, and the suffix byte is
+ * always truncated for DNS lookups.
+ */
+typedef struct _NBNameCacheEntry
+{
+    UCHAR name[NCBNAMSZ];
+    UCHAR nbname[NCBNAMSZ];
+    DWORD numAddresses;
+    DWORD addresses[1];
+} NBNameCacheEntry;
+
+/* Functions that create, manipulate, and destroy a name cache.  Thread-safe,
+ * with the exception of NBNameCacheDestroy--ensure that no other threads are
+ * manipulating the cache before destoying it.
+ */
+
+/* Allocates a new name cache from heap, and sets the expire time on new
+ * entries to entryExpireTimeMS after a cache entry is added.
+ */
+struct NBNameCache *NBNameCacheCreate(HANDLE heap, DWORD entryExpireTimeMS);
+
+/* Adds an entry to the cache.  The entry is assumed to have been allocated
+ * from the same heap as the name cache; the name cache will own the entry
+ * from now on.  The entry's expire time is initialized at this time to
+ * entryExpireTimeMS + the current time in MS.  If an existing entry with the
+ * same name was in the cache, the entry is replaced.  Returns TRUE on success
+ * or FALSE on failure.
+ */
+BOOL NBNameCacheAddEntry(struct NBNameCache *cache, NBNameCacheEntry *entry);
+
+/* Finds the entry with name name in the cache and returns a pointer to it, or
+ * NULL if it isn't found.
+ */
+const NBNameCacheEntry *NBNameCacheFindEntry(struct NBNameCache *cache,
+ const UCHAR name[NCBNAMSZ]);
+
+/* If the entry with name name is in the cache, updates its nbname member to
+ * nbname.  The entry's expire time is implicitly updated to entryExpireTimeMS
+ * + the current time in MS, since getting the NetBIOS name meant validating
+ * the name and address anyway.
+ * Returns TRUE on success or FALSE on failure.
+ */
+BOOL NBNameCacheUpdateNBName(struct NBNameCache *cache,
+ const UCHAR name[NCBNAMSZ], const UCHAR nbname[NCBNAMSZ]);
+
+void NBNameCacheDestroy(struct NBNameCache *cache);
+
+#endif /* ndef __WINE_NBNAMECACHE_H */
diff --git a/reactos/dll/netapi32/nbt.c b/reactos/dll/netapi32/nbt.c
new file mode 100644 (file)
index 0000000..3125df0
--- /dev/null
@@ -0,0 +1,1553 @@
+/* Copyright (c) 2003 Juan Lang
+ *
+ * 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
+ *
+ * I am heavily indebted to Chris Hertel's excellent Implementing CIFS,
+ * http://ubiqx.org/cifs/ , for whatever understanding I have of NBT.
+ * I also stole from Mike McCormack's smb.c and netapi32.c, although little of
+ * that code remains.
+ * Lack of understanding and bugs are my fault.
+ *
+ * FIXME:
+ * - Of the NetBIOS session functions, only client functions are supported, and
+ *   it's likely they'll be the only functions supported.  NBT requires session
+ *   servers to listen on TCP/139.  This requires root privilege, and Samba is
+ *   likely to be listening here already.  This further restricts NetBIOS
+ *   applications, both explicit users and implicit ones:  CreateNamedPipe
+ *   won't actually create a listening pipe, for example, so applications can't
+ *   act as RPC servers using a named pipe protocol binding, DCOM won't be able
+ *   to support callbacks or servers over the named pipe protocol, etc.
+ *
+ * - Datagram support is omitted for the same reason.  To send a NetBIOS
+ *   datagram, you must include the NetBIOS name by which your application is
+ *   known.  This requires you to have registered the name previously, and be
+ *   able to act as a NetBIOS datagram server (listening on UDP/138).
+ *
+ * - Name registration functions are omitted for the same reason--registering a
+ *   name requires you to be able to defend it, and this means listening on
+ *   UDP/137.
+ *   Win98 requires you either use your computer's NetBIOS name (with the NULL
+ *   suffix byte) as the calling name when creating a session, or to register
+ *   a new name before creating one:  it disallows '*' as the calling name.
+ *   Win2K initially starts with an empty name table, and doesn't allow you to
+ *   use the machine's NetBIOS name (with the NULL suffix byte) as the calling
+ *   name.  Although it allows sessions to be created with '*' as the calling
+ *   name, doing so results in timeouts for all receives, because the
+ *   application never gets them.
+ *   So, a well-behaved Netbios application will typically want to register a
+ *   name.  I should probably support a do-nothing name list that allows
+ *   NCBADDNAME to add to it, but doesn't actually register the name, or does
+ *   attempt to register it without being able to defend it.
+ *
+ * - Name lookups may not behave quite as you'd expect/like if you have
+ *   multiple LANAs.  If a name is resolvable through DNS, or if you're using
+ *   WINS, it'll resolve on _any_ LANA.  So, a Call will succeed on any LANA as
+ *   well.
+ *   I'm not sure how Windows behaves in this case.  I could try to force
+ *   lookups to the correct adapter by using one of the GetPreferred*
+ *   functions, but with the possibility of multiple adapters in the same
+ *   same subnet, there's no guarantee that what IpHlpApi thinks is the
+ *   preferred adapter will actually be a LANA.  (It's highly probable because
+ *   this is an unusual configuration, but not guaranteed.)
+ *
+ * See also other FIXMEs in the code.
+ */
+
+#include "config.h"
+#include <stdarg.h>
+
+#include "winsock2.h"
+#include "windef.h"
+#include "winbase.h"
+#include "wine/debug.h"
+#include "winreg.h"
+#include "iphlpapi.h"
+
+#include "netbios.h"
+#include "nbnamecache.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(netbios);
+
+#define PORT_NBNS 137
+#define PORT_NBDG 138
+#define PORT_NBSS 139
+
+#ifndef INADDR_NONE
+#define INADDR_NONE ~0UL
+#endif
+
+#define NBR_ADDWORD(p,word) (*(WORD *)(p)) = htons(word)
+#define NBR_GETWORD(p) ntohs(*(WORD *)(p))
+
+#define MIN_QUERIES         1
+#define MAX_QUERIES         0xffff
+#define MIN_QUERY_TIMEOUT   100
+#define MAX_QUERY_TIMEOUT   0xffffffff
+#define BCAST_QUERIES       3
+#define BCAST_QUERY_TIMEOUT 750
+#define WINS_QUERIES        3
+#define WINS_QUERY_TIMEOUT  750
+#define MAX_WINS_SERVERS    2
+#define MIN_CACHE_TIMEOUT   60000
+#define CACHE_TIMEOUT       360000
+
+#define MAX_NBT_NAME_SZ (NCBNAMSZ * 2 + MAX_DOMAIN_NAME_LEN + 2)
+#define SIMPLE_NAME_QUERY_PKT_SIZE 26 + MAX_NBT_NAME_SZ
+
+#define DEFAULT_NBT_SESSIONS 16
+
+#define NBNS_TYPE_NB             0x0020
+#define NBNS_TYPE_NBSTAT         0x0021
+#define NBNS_CLASS_INTERNET      0x00001
+#define NBNS_HEADER_SIZE         (sizeof(WORD) * 6)
+#define NBNS_RESPONSE_AND_OPCODE 0xf800
+#define NBNS_RESPONSE_AND_QUERY  0x8000
+#define NBNS_REPLYCODE           0x0f
+
+#define NBSS_HDRSIZE 4
+
+#define NBSS_MSG       0x00
+#define NBSS_REQ       0x81
+#define NBSS_ACK       0x82
+#define NBSS_NACK      0x83
+#define NBSS_RETARGET  0x84
+#define NBSS_KEEPALIVE 0x85
+
+#define NBSS_ERR_NOT_LISTENING_ON_NAME    0x80
+#define NBSS_ERR_NOT_LISTENING_FOR_CALLER 0x81
+#define NBSS_ERR_BAD_NAME                 0x82
+#define NBSS_ERR_INSUFFICIENT_RESOURCES   0x83
+
+#define NBSS_EXTENSION 0x01
+
+typedef struct _NetBTSession
+{
+    CRITICAL_SECTION cs;
+    SOCKET           fd;
+    DWORD            bytesPending;
+} NetBTSession;
+
+typedef struct _NetBTAdapter
+{
+    MIB_IPADDRROW       ipr;
+    WORD                nameQueryXID;
+    struct NBNameCache *nameCache;
+    DWORD               xmit_success;
+    DWORD               recv_success;
+} NetBTAdapter;
+
+static ULONG gTransportID;
+static BOOL  gEnableDNS;
+static DWORD gBCastQueries;
+static DWORD gBCastQueryTimeout;
+static DWORD gWINSQueries;
+static DWORD gWINSQueryTimeout;
+static DWORD gWINSServers[MAX_WINS_SERVERS];
+static int   gNumWINSServers;
+static char  gScopeID[MAX_DOMAIN_NAME_LEN];
+static DWORD gCacheTimeout;
+static struct NBNameCache *gNameCache;
+
+/* Converts from a NetBIOS name into a Second Level Encoding-formatted name.
+ * Assumes p is not NULL and is either NULL terminated or has at most NCBNAMSZ
+ * bytes, and buffer has at least MAX_NBT_NAME_SZ bytes.  Pads with space bytes
+ * if p is NULL-terminated.  Returns the number of bytes stored in buffer.
+ */
+static int NetBTNameEncode(const UCHAR *p, UCHAR *buffer)
+{
+    int i,len=0;
+
+    if (!p) return 0;
+    if (!buffer) return 0;
+
+    buffer[len++] = NCBNAMSZ * 2;
+    for (i = 0; p[i] && i < NCBNAMSZ; i++)
+    {
+        buffer[len++] = ((p[i] & 0xf0) >> 4) + 'A';
+        buffer[len++] =  (p[i] & 0x0f) + 'A';
+    }
+    while (len < NCBNAMSZ * 2)
+    {
+        buffer[len++] = 'C';
+        buffer[len++] = 'A';
+    }
+    if (*gScopeID)
+    {
+        int scopeIDLen = strlen(gScopeID);
+
+        memcpy(buffer + len, gScopeID, scopeIDLen);
+        len += scopeIDLen;
+    }
+    buffer[len++] = 0;     /* add second terminator */
+    return len;
+}
+
+/* Creates a NBT name request packet for name in buffer.  If broadcast is true,
+ * creates a broadcast request, otherwise creates a unicast request.
+ * Returns the number of bytes stored in buffer.
+ */
+static DWORD NetBTNameReq(const UCHAR name[NCBNAMSZ], WORD xid, WORD qtype,
+ BOOL broadcast, UCHAR *buffer, int len)
+{
+    int i = 0;
+
+    if (len < SIMPLE_NAME_QUERY_PKT_SIZE) return 0;
+
+    NBR_ADDWORD(&buffer[i],xid);    i+=2; /* transaction */
+    if (broadcast)
+    {
+        NBR_ADDWORD(&buffer[i],0x0110); /* flags: r=req,op=query,rd=1,b=1 */
+        i+=2;
+    }
+    else
+    {
+        NBR_ADDWORD(&buffer[i],0x0100); /* flags: r=req,op=query,rd=1,b=0 */
+        i+=2;
+    }
+    NBR_ADDWORD(&buffer[i],0x0001); i+=2; /* one name query */
+    NBR_ADDWORD(&buffer[i],0x0000); i+=2; /* zero answers */
+    NBR_ADDWORD(&buffer[i],0x0000); i+=2; /* zero authorities */
+    NBR_ADDWORD(&buffer[i],0x0000); i+=2; /* zero additional */
+
+    i += NetBTNameEncode(name, &buffer[i]);
+
+    NBR_ADDWORD(&buffer[i],qtype); i+=2;
+    NBR_ADDWORD(&buffer[i],NBNS_CLASS_INTERNET); i+=2;
+
+    return i;
+}
+
+/* Sends a name query request for name on fd to destAddr.  Sets SO_BROADCAST on
+ * fd if broadcast is TRUE.  Assumes fd is not INVALID_SOCKET, and name is not
+ * NULL.
+ * Returns 0 on success, -1 on failure.
+ */
+static int NetBTSendNameQuery(SOCKET fd, const UCHAR name[NCBNAMSZ], WORD xid,
+ WORD qtype, DWORD destAddr, BOOL broadcast)
+{
+    int ret = 0, on = 1;
+    struct in_addr addr;
+
+    addr.s_addr = destAddr;
+    TRACE("name %s, dest addr %s\n", name, inet_ntoa(addr));
+
+    if (broadcast)
+        ret = setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (const char*)&on, sizeof(on));
+    if(ret == 0)
+    {
+        WSABUF wsaBuf;
+        UCHAR buf[SIMPLE_NAME_QUERY_PKT_SIZE];
+        struct sockaddr_in sin;
+
+        memset(&sin, 0, sizeof(sin));
+        sin.sin_addr.s_addr = destAddr;
+        sin.sin_family      = AF_INET;
+        sin.sin_port        = htons(PORT_NBNS);
+
+        wsaBuf.buf = (CHAR*)buf;
+        wsaBuf.len = NetBTNameReq(name, xid, qtype, broadcast, buf,
+         sizeof(buf));
+        if (wsaBuf.len > 0)
+        {
+            DWORD bytesSent;
+
+            ret = WSASendTo(fd, &wsaBuf, 1, &bytesSent, 0,
+             (struct sockaddr*)&sin, sizeof(sin), NULL, NULL);
+            if (ret < 0 || bytesSent < wsaBuf.len)
+                ret = -1;
+            else
+                ret = 0;
+        }
+        else
+            ret = -1;
+    }
+    return ret;
+}
+
+typedef BOOL (*NetBTAnswerCallback)(void *data, WORD answerCount,
+ WORD answerIndex, PUCHAR rData, WORD rdLength);
+
+/* Waits on fd until GetTickCount() returns a value greater than or equal to
+ * waitUntil for a name service response.  If a name response matching xid
+ * is received, calls answerCallback once for each answer resource record in
+ * the response.  (The callback's answerCount will be the total number of
+ * answers to expect, and answerIndex will be the 0-based index that's being
+ * sent this time.)  Quits parsing if answerCallback returns FALSE.
+ * Returns NRC_GOODRET on timeout or a valid response received, something else
+ * on error.
+ */
+static UCHAR NetBTWaitForNameResponse(NetBTAdapter *adapter, SOCKET fd,
+ DWORD waitUntil, NetBTAnswerCallback answerCallback, void *data)
+{
+    BOOL found = FALSE;
+    DWORD now;
+    UCHAR ret = NRC_GOODRET;
+
+    if (!adapter) return NRC_BADDR;
+    if (fd == INVALID_SOCKET) return NRC_BADDR;
+    if (!answerCallback) return NRC_BADDR;
+
+    while (!found && ret == NRC_GOODRET && (now = GetTickCount()) < waitUntil)
+    {
+        DWORD msToWait = waitUntil - now;
+        struct fd_set fds;
+        struct timeval timeout = { msToWait / 1000, msToWait % 1000 };
+        int r;
+
+        FD_ZERO(&fds);
+        FD_SET(fd, &fds);
+        r = select(fd + 1, &fds, NULL, NULL, &timeout);
+        if (r < 0)
+            ret = NRC_SYSTEM;
+        else if (r == 1)
+        {
+            /* FIXME: magic #, is this always enough? */
+            UCHAR buffer[256];
+            int fromsize;
+            struct sockaddr_in fromaddr;
+            WORD respXID, flags, queryCount, answerCount;
+            WSABUF wsaBuf = { sizeof(buffer), (CHAR*)buffer };
+            DWORD bytesReceived, recvFlags = 0;
+
+            fromsize = sizeof(fromaddr);
+            r = WSARecvFrom(fd, &wsaBuf, 1, &bytesReceived, &recvFlags,
+             (struct sockaddr*)&fromaddr, &fromsize, NULL, NULL);
+            if(r < 0)
+            {
+                ret = NRC_SYSTEM;
+                break;
+            }
+
+            if (bytesReceived < NBNS_HEADER_SIZE)
+                continue;
+
+            respXID = NBR_GETWORD(buffer);
+            if (adapter->nameQueryXID != respXID)
+                continue;
+
+            flags = NBR_GETWORD(buffer + 2);
+            queryCount = NBR_GETWORD(buffer + 4);
+            answerCount = NBR_GETWORD(buffer + 6);
+
+            /* a reply shouldn't contain a query, ignore bad packet */
+            if (queryCount > 0)
+                continue;
+
+            if ((flags & NBNS_RESPONSE_AND_OPCODE) == NBNS_RESPONSE_AND_QUERY)
+            {
+                if ((flags & NBNS_REPLYCODE) != 0)
+                    ret = NRC_NAMERR;
+                else if ((flags & NBNS_REPLYCODE) == 0 && answerCount > 0)
+                {
+                    PUCHAR ptr = buffer + NBNS_HEADER_SIZE;
+                    BOOL shouldContinue = TRUE;
+                    WORD answerIndex = 0;
+
+                    found = TRUE;
+                    /* decode one answer at a time */
+                    while (ret == NRC_GOODRET && answerIndex < answerCount &&
+                     ptr - buffer < bytesReceived && shouldContinue)
+                    {
+                        WORD rLen;
+
+                        /* scan past name */
+                        for (; ptr[0] && ptr - buffer < bytesReceived; )
+                            ptr += ptr[0] + 1;
+                        ptr++;
+                        ptr += 2; /* scan past type */
+                        if (ptr - buffer < bytesReceived && ret == NRC_GOODRET
+                         && NBR_GETWORD(ptr) == NBNS_CLASS_INTERNET)
+                            ptr += sizeof(WORD);
+                        else
+                            ret = NRC_SYSTEM; /* parse error */
+                        ptr += sizeof(DWORD); /* TTL */
+                        rLen = NBR_GETWORD(ptr);
+                        rLen = min(rLen, bytesReceived - (ptr - buffer));
+                        ptr += sizeof(WORD);
+                        shouldContinue = answerCallback(data, answerCount,
+                         answerIndex, ptr, rLen);
+                        ptr += rLen;
+                        answerIndex++;
+                    }
+                }
+            }
+        }
+    }
+    TRACE("returning 0x%02x\n", ret);
+    return ret;
+}
+
+typedef struct _NetBTNameQueryData {
+    NBNameCacheEntry *cacheEntry;
+    UCHAR ret;
+} NetBTNameQueryData;
+
+/* Name query callback function for NetBTWaitForNameResponse, creates a cache
+ * entry on the first answer, adds each address as it's called again (as long
+ * as there's space).  If there's an error that should be propagated as the
+ * NetBIOS error, modifies queryData's ret member to the proper return code.
+ */
+static BOOL NetBTFindNameAnswerCallback(void *pVoid, WORD answerCount,
+ WORD answerIndex, PUCHAR rData, WORD rLen)
+{
+    NetBTNameQueryData *queryData = (NetBTNameQueryData *)pVoid;
+    BOOL ret;
+
+    if (queryData)
+    {
+        if (queryData->cacheEntry == NULL)
+        {
+            queryData->cacheEntry = HeapAlloc(
+             GetProcessHeap(), 0, sizeof(NBNameCacheEntry) +
+             (answerCount - 1) * sizeof(DWORD));
+            if (queryData->cacheEntry)
+                queryData->cacheEntry->numAddresses = 0;
+            else
+            {
+                ret = FALSE;
+                queryData->ret = NRC_OSRESNOTAV;
+            }
+        }
+        if (rLen == 6 && queryData->cacheEntry &&
+         queryData->cacheEntry->numAddresses < answerCount)
+        {
+            queryData->cacheEntry->addresses[queryData->cacheEntry->
+             numAddresses++] = *(PDWORD)(rData + 2);
+            ret = queryData->cacheEntry->numAddresses < answerCount;
+        }
+        else
+            ret = FALSE;
+    }
+    else
+        ret = FALSE;
+    return ret;
+}
+
+/* Workhorse NetBT name lookup function.  Sends a name lookup query for
+ * ncb->ncb_callname to sendTo, as a broadcast if broadcast is TRUE, using
+ * adapter->nameQueryXID as the transaction ID.  Waits up to timeout
+ * milliseconds, and retries up to maxQueries times, waiting for a reply.
+ * If a valid response is received, stores the looked up addresses as a
+ * NBNameCacheEntry in *cacheEntry.
+ * Returns NRC_GOODRET on success, though this may not mean the name was
+ * resolved--check whether *cacheEntry is NULL.
+ */
+static UCHAR NetBTNameWaitLoop(NetBTAdapter *adapter, SOCKET fd, PNCB ncb,
+ DWORD sendTo, BOOL broadcast, DWORD timeout, DWORD maxQueries,
+ NBNameCacheEntry **cacheEntry)
+{
+    unsigned int queries;
+    NetBTNameQueryData queryData;
+
+    if (!adapter) return NRC_BADDR;
+    if (fd == INVALID_SOCKET) return NRC_BADDR;
+    if (!ncb) return NRC_BADDR;
+    if (!cacheEntry) return NRC_BADDR;
+
+    queryData.cacheEntry = NULL;
+    queryData.ret = NRC_GOODRET;
+    for (queries = 0; queryData.cacheEntry == NULL && queries < maxQueries;
+     queries++)
+    {
+        if (!NCB_CANCELLED(ncb))
+        {
+            int r = NetBTSendNameQuery(fd, ncb->ncb_callname,
+             adapter->nameQueryXID, NBNS_TYPE_NB, sendTo, broadcast);
+
+            if (r == 0)
+                queryData.ret = NetBTWaitForNameResponse(adapter, fd,
+                 GetTickCount() + timeout, NetBTFindNameAnswerCallback,
+                 &queryData);
+            else
+                queryData.ret = NRC_SYSTEM;
+        }
+        else
+            queryData.ret = NRC_CMDCAN;
+    }
+    if (queryData.cacheEntry)
+    {
+        memcpy(queryData.cacheEntry->name, ncb->ncb_callname, NCBNAMSZ);
+        memcpy(queryData.cacheEntry->nbname, ncb->ncb_callname, NCBNAMSZ);
+    }
+    *cacheEntry = queryData.cacheEntry;
+    return queryData.ret;
+}
+
+/* Attempts to add cacheEntry to the name cache in *nameCache; if *nameCache
+ * has not yet been created, creates it, using gCacheTimeout as the cache
+ * entry timeout.  If memory allocation fails, or if NBNameCacheAddEntry fails,
+ * frees cacheEntry.
+ * Returns NRC_GOODRET on success, and something else on failure.
+ */
+static UCHAR NetBTStoreCacheEntry(struct NBNameCache **nameCache,
+ NBNameCacheEntry *cacheEntry)
+{
+    UCHAR ret;
+
+    if (!nameCache) return NRC_BADDR;
+    if (!cacheEntry) return NRC_BADDR;
+
+    if (!*nameCache)
+        *nameCache = NBNameCacheCreate(GetProcessHeap(), gCacheTimeout);
+    if (*nameCache)
+        ret = NBNameCacheAddEntry(*nameCache, cacheEntry)
+         ?  NRC_GOODRET : NRC_OSRESNOTAV;
+    else
+    {
+        HeapFree(GetProcessHeap(), 0, cacheEntry);
+        ret = NRC_OSRESNOTAV;
+    }
+    return ret;
+}
+
+/* Attempts to resolve name using inet_addr(), then gethostbyname() if
+ * gEnableDNS is TRUE, if the suffix byte is either <00> or <20>.  If the name
+ * can be looked up, returns 0 and stores the looked up addresses as a
+ * NBNameCacheEntry in *cacheEntry.
+ * Returns NRC_GOODRET on success, though this may not mean the name was
+ * resolved--check whether *cacheEntry is NULL.  Returns something else on
+ * error.
+ */
+static UCHAR NetBTinetResolve(const UCHAR name[NCBNAMSZ],
+ NBNameCacheEntry **cacheEntry)
+{
+    UCHAR ret = NRC_GOODRET;
+
+    TRACE("name %s, cacheEntry %p\n", name, cacheEntry);
+
+    if (!name) return NRC_BADDR;
+    if (!cacheEntry) return NRC_BADDR;
+
+    if (isalnum(name[0]) && (name[NCBNAMSZ - 1] == 0 ||
+     name[NCBNAMSZ - 1] == 0x20))
+    {
+        CHAR toLookup[NCBNAMSZ];
+        unsigned int i;
+
+        for (i = 0; i < NCBNAMSZ - 1 && name[i] && name[i] != ' '; i++)
+            toLookup[i] = name[i];
+        toLookup[i] = '\0';
+
+        if (isdigit(toLookup[0]))
+        {
+            unsigned long addr = inet_addr(toLookup);
+
+            if (addr != INADDR_NONE)
+            {
+                *cacheEntry = HeapAlloc(GetProcessHeap(),
+                 0, sizeof(NBNameCacheEntry));
+                if (*cacheEntry)
+                {
+                    memcpy((*cacheEntry)->name, name, NCBNAMSZ);
+                    memset((*cacheEntry)->nbname, 0, NCBNAMSZ);
+                    (*cacheEntry)->nbname[0] = '*';
+                    (*cacheEntry)->numAddresses = 1;
+                    (*cacheEntry)->addresses[0] = addr;
+                }
+                else
+                    ret = NRC_OSRESNOTAV;
+            }
+        }
+        if (gEnableDNS && ret == NRC_GOODRET && !*cacheEntry)
+        {
+            struct hostent *host;
+
+            if ((host = gethostbyname(toLookup)) != NULL)
+            {
+                for (i = 0; ret == NRC_GOODRET && host->h_addr_list &&
+                 host->h_addr_list[i]; i++)
+                    ;
+                if (host->h_addr_list && host->h_addr_list[0])
+                {
+                    *cacheEntry = HeapAlloc(
+                     GetProcessHeap(), 0, sizeof(NBNameCacheEntry) +
+                     (i - 1) * sizeof(DWORD));
+                    if (*cacheEntry)
+                    {
+                        memcpy((*cacheEntry)->name, name, NCBNAMSZ);
+                        memset((*cacheEntry)->nbname, 0, NCBNAMSZ);
+                        (*cacheEntry)->nbname[0] = '*';
+                        (*cacheEntry)->numAddresses = i;
+                        for (i = 0; i < (*cacheEntry)->numAddresses; i++)
+                            (*cacheEntry)->addresses[i] =
+                             (DWORD)host->h_addr_list[i];
+                    }
+                    else
+                        ret = NRC_OSRESNOTAV;
+                }
+            }
+        }
+    }
+
+    TRACE("returning 0x%02x\n", ret);
+    return ret;
+}
+
+/* Looks up the name in ncb->ncb_callname, first in the name caches (global
+ * and this adapter's), then using gethostbyname(), next by WINS if configured,
+ * and finally using broadcast NetBT name resolution.  In NBT parlance, this
+ * makes this an "H-node".  Stores an entry in the appropriate name cache for a
+ * found node, and returns it as *cacheEntry.
+ * Assumes data, ncb, and cacheEntry are not NULL.
+ * Returns NRC_GOODRET on success--which doesn't mean the name was resolved,
+ * just that all name lookup operations completed successfully--and something
+ * else on failure.  *cacheEntry will be NULL if the name was not found.
+ */
+static UCHAR NetBTInternalFindName(NetBTAdapter *adapter, PNCB ncb,
+ const NBNameCacheEntry **cacheEntry)
+{
+    UCHAR ret = NRC_GOODRET;
+
+    TRACE("adapter %p, ncb %p, cacheEntry %p\n", adapter, ncb, cacheEntry);
+
+    if (!cacheEntry) return NRC_BADDR;
+    *cacheEntry = NULL;
+
+    if (!adapter) return NRC_BADDR;
+    if (!ncb) return NRC_BADDR;
+
+    if (ncb->ncb_callname[0] == '*')
+        ret = NRC_NOWILD;
+    else
+    {
+        *cacheEntry = NBNameCacheFindEntry(gNameCache, ncb->ncb_callname);
+        if (!*cacheEntry)
+            *cacheEntry = NBNameCacheFindEntry(adapter->nameCache,
+             ncb->ncb_callname);
+        if (!*cacheEntry)
+        {
+            NBNameCacheEntry *newEntry = NULL;
+
+            ret = NetBTinetResolve(ncb->ncb_callname, &newEntry);
+            if (ret == NRC_GOODRET && newEntry)
+            {
+                ret = NetBTStoreCacheEntry(&gNameCache, newEntry);
+                if (ret != NRC_GOODRET)
+                    newEntry = NULL;
+            }
+            else
+            {
+                SOCKET fd = WSASocketA(PF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL,
+                 0, WSA_FLAG_OVERLAPPED);
+
+                if(fd == INVALID_SOCKET)
+                    ret = NRC_OSRESNOTAV;
+                else
+                {
+                    int winsNdx;
+
+                    adapter->nameQueryXID++;
+                    for (winsNdx = 0; ret == NRC_GOODRET && *cacheEntry == NULL
+                     && winsNdx < gNumWINSServers; winsNdx++)
+                        ret = NetBTNameWaitLoop(adapter, fd, ncb,
+                         gWINSServers[winsNdx], FALSE, gWINSQueryTimeout,
+                         gWINSQueries, &newEntry);
+                    if (ret == NRC_GOODRET && newEntry)
+                    {
+                        ret = NetBTStoreCacheEntry(&gNameCache, newEntry);
+                        if (ret != NRC_GOODRET)
+                            newEntry = NULL;
+                    }
+                    if (ret == NRC_GOODRET && *cacheEntry == NULL)
+                    {
+                        DWORD bcastAddr =
+                         adapter->ipr.dwAddr & adapter->ipr.dwMask;
+
+                        if (adapter->ipr.dwBCastAddr)
+                            bcastAddr |= ~adapter->ipr.dwMask;
+                        ret = NetBTNameWaitLoop(adapter, fd, ncb, bcastAddr,
+                         TRUE, gBCastQueryTimeout, gBCastQueries, &newEntry);
+                        if (ret == NRC_GOODRET && newEntry)
+                        {
+                            ret = NetBTStoreCacheEntry(&adapter->nameCache,
+                             newEntry);
+                            if (ret != NRC_GOODRET)
+                                newEntry = NULL;
+                        }
+                    }
+                    closesocket(fd);
+                }
+            }
+            *cacheEntry = newEntry;
+        }
+    }
+    TRACE("returning 0x%02x\n", ret);
+    return ret;
+}
+
+typedef struct _NetBTNodeQueryData
+{
+    BOOL gotResponse;
+    PADAPTER_STATUS astat;
+    WORD astatLen;
+} NetBTNodeQueryData;
+
+/* Callback function for NetBTAstatRemote, parses the rData for the node
+ * status and name list of the remote node.  Always returns FALSE, since
+ * there's never more than one answer we care about in a node status response.
+ */
+static BOOL NetBTNodeStatusAnswerCallback(void *pVoid, WORD answerCount,
+ WORD answerIndex, PUCHAR rData, WORD rLen)
+{
+    NetBTNodeQueryData *data = (NetBTNodeQueryData *)pVoid;
+
+    if (data && !data->gotResponse && rData && rLen >= 1)
+    {
+        /* num names is first byte; each name is NCBNAMSZ + 2 bytes */
+        if (rLen >= rData[0] * (NCBNAMSZ + 2))
+        {
+            WORD i;
+            PUCHAR src;
+            PNAME_BUFFER dst;
+
+            data->gotResponse = TRUE;
+            data->astat->name_count = rData[0];
+            for (i = 0, src = rData + 1,
+             dst = (PNAME_BUFFER)((PUCHAR)data->astat +
+              sizeof(ADAPTER_STATUS));
+             i < data->astat->name_count && src - rData < rLen &&
+             (PUCHAR)dst - (PUCHAR)data->astat < data->astatLen;
+             i++, dst++, src += NCBNAMSZ + 2)
+            {
+                UCHAR flags = *(src + NCBNAMSZ);
+
+                memcpy(dst->name, src, NCBNAMSZ);
+                /* we won't actually see a registering name in the returned
+                 * response.  It's useful to see if no other flags are set; if
+                 * none are, then the name is registered. */
+                dst->name_flags = REGISTERING;
+                if (flags & 0x80)
+                    dst->name_flags |= GROUP_NAME;
+                if (flags & 0x10)
+                    dst->name_flags |= DEREGISTERED;
+                if (flags & 0x08)
+                    dst->name_flags |= DUPLICATE;
+                if (dst->name_flags == REGISTERING)
+                    dst->name_flags = REGISTERED;
+            }
+            /* arbitrarily set HW type to Ethernet */
+            data->astat->adapter_type = 0xfe;
+            if (src - rData < rLen)
+                memcpy(data->astat->adapter_address, src,
+                 min(rLen - (src - rData), 6));
+        }
+    }
+    return FALSE;
+}
+
+/* This uses the WINS timeout and query values, as they're the
+ * UCAST_REQ_RETRY_TIMEOUT and UCAST_REQ_RETRY_COUNT according to the RFCs.
+ */
+static UCHAR NetBTAstatRemote(NetBTAdapter *adapter, PNCB ncb)
+{
+    UCHAR ret = NRC_GOODRET;
+    const NBNameCacheEntry *cacheEntry = NULL;
+
+    TRACE("adapter %p, NCB %p\n", adapter, ncb);
+
+    if (!adapter) return NRC_BADDR;
+    if (!ncb) return NRC_INVADDRESS;
+
+    ret = NetBTInternalFindName(adapter, ncb, &cacheEntry);
+    if (ret == NRC_GOODRET && cacheEntry)
+    {
+        if (cacheEntry->numAddresses > 0)
+        {
+            SOCKET fd = WSASocketA(PF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, 0,
+             WSA_FLAG_OVERLAPPED);
+
+            if(fd == INVALID_SOCKET)
+                ret = NRC_OSRESNOTAV;
+            else
+            {
+                NetBTNodeQueryData queryData;
+                DWORD queries;
+                PADAPTER_STATUS astat = (PADAPTER_STATUS)ncb->ncb_buffer;
+
+                adapter->nameQueryXID++;
+                astat->name_count = 0;
+                queryData.gotResponse = FALSE;
+                queryData.astat = astat;
+                queryData.astatLen = ncb->ncb_length;
+                for (queries = 0; !queryData.gotResponse &&
+                 queries < gWINSQueries; queries++)
+                {
+                    if (!NCB_CANCELLED(ncb))
+                    {
+                        int r = NetBTSendNameQuery(fd, ncb->ncb_callname,
+                         adapter->nameQueryXID, NBNS_TYPE_NBSTAT,
+                         cacheEntry->addresses[0], FALSE);
+
+                        if (r == 0)
+                            ret = NetBTWaitForNameResponse(adapter, fd,
+                             GetTickCount() + gWINSQueryTimeout,
+                             NetBTNodeStatusAnswerCallback, &queryData);
+                        else
+                            ret = NRC_SYSTEM;
+                    }
+                    else
+                        ret = NRC_CMDCAN;
+                }
+                closesocket(fd);
+            }
+        }
+        else
+            ret = NRC_CMDTMO;
+    }
+    else if (ret == NRC_CMDCAN)
+        ; /* do nothing, we were cancelled */
+    else
+        ret = NRC_CMDTMO;
+    TRACE("returning 0x%02x\n", ret);
+    return ret;
+}
+
+static UCHAR NetBTAstat(void *adapt, PNCB ncb)
+{
+    NetBTAdapter *adapter = (NetBTAdapter *)adapt;
+    UCHAR ret;
+
+    TRACE("adapt %p, NCB %p\n", adapt, ncb);
+
+    if (!adapter) return NRC_ENVNOTDEF;
+    if (!ncb) return NRC_INVADDRESS;
+    if (!ncb->ncb_buffer) return NRC_BADDR;
+    if (ncb->ncb_length < sizeof(ADAPTER_STATUS)) return NRC_BUFLEN;
+
+    if (ncb->ncb_callname[0] == '*')
+    {
+        DWORD physAddrLen;
+        MIB_IFROW ifRow;
+        PADAPTER_STATUS astat = (PADAPTER_STATUS)ncb->ncb_buffer;
+  
+        memset(astat, 0, sizeof(ADAPTER_STATUS));
+        astat->rev_major = 3;
+        ifRow.dwIndex = adapter->ipr.dwIndex;
+        if (GetIfEntry(&ifRow) != NO_ERROR)
+            ret = NRC_BRIDGE;
+        else
+        {
+            physAddrLen = min(ifRow.dwPhysAddrLen, 6);
+            if (physAddrLen > 0)
+                memcpy(astat->adapter_address, ifRow.bPhysAddr, physAddrLen);
+            /* doubt anyone cares, but why not.. */
+            if (ifRow.dwType == MIB_IF_TYPE_TOKENRING)
+                astat->adapter_type = 0xff;
+            else
+                astat->adapter_type = 0xfe; /* for Ethernet */
+            astat->max_sess_pkt_size = 0xffff;
+            astat->xmit_success = adapter->xmit_success;
+            astat->recv_success = adapter->recv_success;
+        }
+        ret = NRC_GOODRET;
+    }
+    else
+        ret = NetBTAstatRemote(adapter, ncb);
+    TRACE("returning 0x%02x\n", ret);
+    return ret;
+}
+
+static UCHAR NetBTFindName(void *adapt, PNCB ncb)
+{
+    NetBTAdapter *adapter = (NetBTAdapter *)adapt;
+    UCHAR ret;
+    const NBNameCacheEntry *cacheEntry = NULL;
+    PFIND_NAME_HEADER foundName;
+
+    TRACE("adapt %p, NCB %p\n", adapt, ncb);
+
+    if (!adapter) return NRC_ENVNOTDEF;
+    if (!ncb) return NRC_INVADDRESS;
+    if (!ncb->ncb_buffer) return NRC_BADDR;
+    if (ncb->ncb_length < sizeof(FIND_NAME_HEADER)) return NRC_BUFLEN;
+
+    foundName = (PFIND_NAME_HEADER)ncb->ncb_buffer;
+    memset(foundName, 0, sizeof(FIND_NAME_HEADER));
+
+    ret = NetBTInternalFindName(adapter, ncb, &cacheEntry);
+    if (ret == NRC_GOODRET)
+    {
+        if (cacheEntry)
+        {
+            DWORD spaceFor = min((ncb->ncb_length - sizeof(FIND_NAME_HEADER)) /
+             sizeof(FIND_NAME_BUFFER), cacheEntry->numAddresses);
+            DWORD ndx;
+
+            for (ndx = 0; ndx < spaceFor; ndx++)
+            {
+                PFIND_NAME_BUFFER findNameBuffer;
+
+                findNameBuffer =
+                 (PFIND_NAME_BUFFER)((PUCHAR)foundName +
+                 sizeof(FIND_NAME_HEADER) + foundName->node_count *
+                 sizeof(FIND_NAME_BUFFER));
+                memset(findNameBuffer->destination_addr, 0, 2);
+                memcpy(findNameBuffer->destination_addr + 2,
+                 &adapter->ipr.dwAddr, sizeof(DWORD));
+                memset(findNameBuffer->source_addr, 0, 2);
+                memcpy(findNameBuffer->source_addr + 2,
+                 &cacheEntry->addresses[ndx], sizeof(DWORD));
+                foundName->node_count++;
+            }
+            if (spaceFor < cacheEntry->numAddresses)
+                ret = NRC_BUFLEN;
+        }
+        else
+            ret = NRC_CMDTMO;
+    }
+    TRACE("returning 0x%02x\n", ret);
+    return ret;
+}
+
+static UCHAR NetBTSessionReq(SOCKET fd, const UCHAR *calledName,
+ const UCHAR *callingName)
+{
+    UCHAR buffer[NBSS_HDRSIZE + MAX_DOMAIN_NAME_LEN * 2], ret;
+    int r;
+    unsigned int len = 0;
+    DWORD bytesSent, bytesReceived, recvFlags = 0;
+    WSABUF wsaBuf;
+
+    buffer[0] = NBSS_REQ;
+    buffer[1] = 0;
+
+    len += NetBTNameEncode(calledName, &buffer[NBSS_HDRSIZE]);
+    len += NetBTNameEncode(callingName, &buffer[NBSS_HDRSIZE + len]);
+
+    NBR_ADDWORD(&buffer[2], len);
+
+    wsaBuf.len = len + NBSS_HDRSIZE;
+    wsaBuf.buf = (char*)buffer;
+
+    r = WSASend(fd, &wsaBuf, 1, &bytesSent, 0, NULL, NULL);
+    if(r < 0 || bytesSent < len + NBSS_HDRSIZE)
+    {
+        ERR("send failed\n");
+        return NRC_SABORT;
+    }
+
+    /* I've already set the recv timeout on this socket (if it supports it), so
+     * just block.  Hopefully we'll always receive the session acknowledgement
+     * within one timeout.
+     */
+    wsaBuf.len = NBSS_HDRSIZE + 1;
+    r = WSARecv(fd, &wsaBuf, 1, &bytesReceived, &recvFlags, NULL, NULL);
+    if (r < 0 || bytesReceived < NBSS_HDRSIZE)
+        ret = NRC_SABORT;
+    else if (buffer[0] == NBSS_NACK)
+    {
+        if (r == NBSS_HDRSIZE + 1)
+        {
+            switch (buffer[NBSS_HDRSIZE])
+            {
+                case NBSS_ERR_INSUFFICIENT_RESOURCES:
+                    ret = NRC_REMTFUL;
+                    break;
+                default:
+                    ret = NRC_NOCALL;
+            }
+        }
+        else
+            ret = NRC_NOCALL;
+    }
+    else if (buffer[0] == NBSS_RETARGET)
+    {
+        FIXME("Got a session retarget, can't deal\n");
+        ret = NRC_NOCALL;
+    }
+    else if (buffer[0] == NBSS_ACK)
+        ret = NRC_GOODRET;
+    else
+        ret = NRC_SYSTEM;
+
+    TRACE("returning 0x%02x\n", ret);
+    return ret;
+}
+
+static UCHAR NetBTCall(void *adapt, PNCB ncb, void **sess)
+{
+    NetBTAdapter *adapter = (NetBTAdapter *)adapt;
+    UCHAR ret;
+    const NBNameCacheEntry *cacheEntry = NULL;
+
+    TRACE("adapt %p, ncb %p\n", adapt, ncb);
+
+    if (!adapter) return NRC_ENVNOTDEF;
+    if (!ncb) return NRC_INVADDRESS;
+    if (!sess) return NRC_BADDR;
+
+    ret = NetBTInternalFindName(adapter, ncb, &cacheEntry);
+    if (ret == NRC_GOODRET)
+    {
+        if (cacheEntry && cacheEntry->numAddresses > 0)
+        {
+            SOCKET fd;
+
+            fd = WSASocketA(PF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0,
+             WSA_FLAG_OVERLAPPED);
+            if (fd != INVALID_SOCKET)
+            {
+                DWORD timeout;
+                struct sockaddr_in sin;
+
+                if (ncb->ncb_rto > 0)
+                {
+                    timeout = ncb->ncb_rto * 500;
+                    setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout,
+                     sizeof(timeout));
+                }
+                if (ncb->ncb_rto > 0)
+                {
+                    timeout = ncb->ncb_sto * 500;
+                    setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout,
+                     sizeof(timeout));
+                }
+
+                memset(&sin, 0, sizeof(sin));
+                memcpy(&sin.sin_addr, &cacheEntry->addresses[0],
+                 sizeof(sin.sin_addr));
+                sin.sin_family = AF_INET;
+                sin.sin_port   = htons(PORT_NBSS);
+                /* FIXME: use nonblocking mode for the socket, check the
+                 * cancel flag periodically
+                 */
+                if (connect(fd, (struct sockaddr *)&sin, sizeof(sin))
+                 == SOCKET_ERROR)
+                    ret = NRC_CMDTMO;
+                else
+                {
+                    static UCHAR fakedCalledName[] = "*SMBSERVER";
+                    const UCHAR *calledParty = cacheEntry->nbname[0] == '*'
+                     ? fakedCalledName : cacheEntry->nbname;
+
+                    ret = NetBTSessionReq(fd, calledParty, ncb->ncb_name);
+                    if (ret != NRC_GOODRET && calledParty[0] == '*')
+                    {
+                        FIXME("NBT session to \"*SMBSERVER\" refused,\n");
+                        FIXME("should try finding name using ASTAT\n");
+                    }
+                }
+                if (ret != NRC_GOODRET)
+                    closesocket(fd);
+                else
+                {
+                    NetBTSession *session = HeapAlloc(
+                     GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(NetBTSession));
+
+                    if (session)
+                    {
+                        session->fd = fd;
+                        InitializeCriticalSection(&session->cs);
+                        *sess = session;
+                    }
+                    else
+                    {
+                        ret = NRC_OSRESNOTAV;
+                        closesocket(fd);
+                    }
+                }
+            }
+            else
+                ret = NRC_OSRESNOTAV;
+        }
+        else
+            ret = NRC_NAMERR;
+    }
+    TRACE("returning 0x%02x\n", ret);
+    return ret;
+}
+
+/* Notice that I don't protect against multiple thread access to NetBTSend.
+ * This is because I don't update any data in the adapter, and I only make a
+ * single call to WSASend, which I assume to act atomically (not interleaving
+ * data from other threads).
+ * I don't lock, because I only depend on the fd being valid, and this won't be
+ * true until a session setup is completed.
+ */
+static UCHAR NetBTSend(void *adapt, void *sess, PNCB ncb)
+{
+    NetBTAdapter *adapter = (NetBTAdapter *)adapt;
+    NetBTSession *session = (NetBTSession *)sess;
+    UCHAR buffer[NBSS_HDRSIZE], ret;
+    int r;
+    WSABUF wsaBufs[2];
+    DWORD bytesSent;
+
+    TRACE("adapt %p, session %p, NCB %p\n", adapt, session, ncb);
+
+    if (!adapter) return NRC_ENVNOTDEF;
+    if (!ncb) return NRC_INVADDRESS;
+    if (!ncb->ncb_buffer) return NRC_BADDR;
+    if (!session) return NRC_SNUMOUT;
+    if (session->fd == INVALID_SOCKET) return NRC_SNUMOUT;
+
+    buffer[0] = NBSS_MSG;
+    buffer[1] = 0;
+    NBR_ADDWORD(&buffer[2], ncb->ncb_length);
+
+    wsaBufs[0].len = NBSS_HDRSIZE;
+    wsaBufs[0].buf = (char*)buffer;
+    wsaBufs[1].len = ncb->ncb_length;
+    wsaBufs[1].buf = (char*)ncb->ncb_buffer;
+
+    r = WSASend(session->fd, wsaBufs, sizeof(wsaBufs) / sizeof(wsaBufs[0]),
+     &bytesSent, 0, NULL, NULL);
+    if (r == SOCKET_ERROR)
+    {
+        NetBIOSHangupSession(ncb);
+        ret = NRC_SABORT;
+    }
+    else if (bytesSent < NBSS_HDRSIZE + ncb->ncb_length)
+    {
+        FIXME("Only sent %ld bytes (of %d), hanging up session\n", bytesSent,
+         NBSS_HDRSIZE + ncb->ncb_length);
+        NetBIOSHangupSession(ncb);
+        ret = NRC_SABORT;
+    }
+    else
+    {
+        ret = NRC_GOODRET;
+        adapter->xmit_success++;
+    }
+    TRACE("returning 0x%02x\n", ret);
+    return ret;
+}
+
+static UCHAR NetBTRecv(void *adapt, void *sess, PNCB ncb)
+{
+    NetBTAdapter *adapter = (NetBTAdapter *)adapt;
+    NetBTSession *session = (NetBTSession *)sess;
+    UCHAR buffer[NBSS_HDRSIZE], ret;
+    int r;
+    WSABUF wsaBufs[2];
+    DWORD bufferCount, bytesReceived, flags;
+
+    TRACE("adapt %p, session %p, NCB %p\n", adapt, session, ncb);
+
+    if (!adapter) return NRC_ENVNOTDEF;
+    if (!ncb) return NRC_BADDR;
+    if (!ncb->ncb_buffer) return NRC_BADDR;
+    if (!session) return NRC_SNUMOUT;
+    if (session->fd == INVALID_SOCKET) return NRC_SNUMOUT;
+
+    EnterCriticalSection(&session->cs);
+    bufferCount = 0;
+    if (session->bytesPending == 0)
+    {
+        bufferCount++;
+        wsaBufs[0].len = NBSS_HDRSIZE;
+        wsaBufs[0].buf = (char*)buffer;
+    }
+    wsaBufs[bufferCount].len = ncb->ncb_length;
+    wsaBufs[bufferCount].buf = (char*)ncb->ncb_buffer;
+    bufferCount++;
+
+    flags = 0;
+    /* FIXME: should poll a bit so I can check the cancel flag */
+    r = WSARecv(session->fd, wsaBufs, bufferCount, &bytesReceived, &flags,
+     NULL, NULL);
+    if (r == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK)
+    {
+        LeaveCriticalSection(&session->cs);
+        ERR("Receive error, WSAGetLastError() returns %d\n", WSAGetLastError());
+        NetBIOSHangupSession(ncb);
+        ret = NRC_SABORT;
+    }
+    else if (NCB_CANCELLED(ncb))
+    {
+        LeaveCriticalSection(&session->cs);
+        ret = NRC_CMDCAN;
+    }
+    else
+    {
+        if (bufferCount == 2)
+        {
+            if (buffer[0] == NBSS_KEEPALIVE)
+            {
+                LeaveCriticalSection(&session->cs);
+                FIXME("Oops, received a session keepalive and lost my place\n");
+                /* need to read another session header until we get a session
+                 * message header. */
+                NetBIOSHangupSession(ncb);
+                ret = NRC_SABORT;
+            }
+            else if (buffer[0] != NBSS_MSG)
+            {
+                LeaveCriticalSection(&session->cs);
+                FIXME("Received unexpected session msg type %d\n", buffer[0]);
+                NetBIOSHangupSession(ncb);
+                ret = NRC_SABORT;
+            }
+            else
+            {
+                if (buffer[1] & NBSS_EXTENSION)
+                {
+                    LeaveCriticalSection(&session->cs);
+                    FIXME("Received a message that's too long for my taste\n");
+                    NetBIOSHangupSession(ncb);
+                    ret = NRC_SABORT;
+                }
+                else
+                {
+                    session->bytesPending = NBSS_HDRSIZE
+                     + NBR_GETWORD(&buffer[2]) - bytesReceived;
+                    ncb->ncb_length = bytesReceived - NBSS_HDRSIZE;
+                    LeaveCriticalSection(&session->cs);
+                }
+            }
+        }
+        else
+        {
+            if (bytesReceived < session->bytesPending)
+                session->bytesPending -= bytesReceived;
+            else
+                session->bytesPending = 0;
+            LeaveCriticalSection(&session->cs);
+            ncb->ncb_length = bytesReceived;
+        }
+        if (session->bytesPending > 0)
+            ret = NRC_INCOMP;
+        else
+        {
+            ret = NRC_GOODRET;
+            adapter->recv_success++;
+        }
+    }
+    TRACE("returning 0x%02x\n", ret);
+    return ret;
+}
+
+static UCHAR NetBTHangup(void *adapt, void *sess)
+{
+    NetBTSession *session = (NetBTSession *)sess;
+
+    TRACE("adapt %p, session %p\n", adapt, session);
+
+    if (!session) return NRC_SNUMOUT;
+
+    /* I don't lock the session, because NetBTRecv knows not to decrement
+     * past 0, so if a receive completes after this it should still deal.
+     */
+    closesocket(session->fd);
+    session->fd = INVALID_SOCKET;
+    session->bytesPending = 0;
+    DeleteCriticalSection(&session->cs);
+    HeapFree(GetProcessHeap(), 0, session);
+
+    return NRC_GOODRET;
+}
+
+static void NetBTCleanupAdapter(void *adapt)
+{
+    TRACE("adapt %p\n", adapt);
+    if (adapt)
+    {
+        NetBTAdapter *adapter = (NetBTAdapter *)adapt;
+
+        if (adapter->nameCache)
+            NBNameCacheDestroy(adapter->nameCache);
+        HeapFree(GetProcessHeap(), 0, adapt);
+    }
+}
+
+static void NetBTCleanup(void)
+{
+    TRACE("\n");
+    if (gNameCache)
+    {
+        NBNameCacheDestroy(gNameCache);
+        gNameCache = NULL;
+    }
+}
+
+static UCHAR NetBTRegisterAdapter(PMIB_IPADDRROW ipRow)
+{
+    UCHAR ret;
+    NetBTAdapter *adapter;
+    
+    if (!ipRow) return NRC_BADDR;
+
+    adapter = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(NetBTAdapter));
+    if (adapter)
+    {
+        memcpy(&adapter->ipr, ipRow, sizeof(MIB_IPADDRROW));
+        if (!NetBIOSRegisterAdapter(gTransportID, ipRow->dwIndex, adapter))
+        {
+            NetBTCleanupAdapter(adapter);
+            ret = NRC_SYSTEM;
+        }
+        else
+            ret = NRC_GOODRET;
+    }
+    else
+        ret = NRC_OSRESNOTAV;
+    return ret;
+}
+
+/* Callback for NetBIOS adapter enumeration.  Assumes closure is a pointer to
+ * a MIB_IPADDRTABLE containing all the IP adapters needed to be added to the
+ * NetBIOS adapter table.  For each callback, checks if the passed-in adapt
+ * has an entry in the table; if so, this adapter was enumerated previously,
+ * and it's enabled.  As a flag, the table's dwAddr entry is changed to
+ * INADDR_LOOPBACK, since this is an invalid address for a NetBT adapter.
+ * The NetBTEnum function will add any remaining adapters from the
+ * MIB_IPADDRTABLE to the NetBIOS adapter table.
+ */
+static BOOL NetBTEnumCallback(UCHAR totalLANAs, UCHAR lanaIndex,
+ ULONG transport, const NetBIOSAdapterImpl *data, void *closure)
+{
+    BOOL ret;
+    PMIB_IPADDRTABLE table = (PMIB_IPADDRTABLE)closure;
+
+    if (table && data)
+    {
+        DWORD ndx;
+
+        ret = FALSE;
+        for (ndx = 0; !ret && ndx < table->dwNumEntries; ndx++)
+        {
+            const NetBTAdapter *adapter = (const NetBTAdapter *)data->data;
+
+            if (table->table[ndx].dwIndex == adapter->ipr.dwIndex)
+            {
+                NetBIOSEnableAdapter(data->lana);
+                table->table[ndx].dwAddr = INADDR_LOOPBACK;
+                ret = TRUE;
+            }
+        }
+    }
+    else
+        ret = FALSE;
+    return ret;
+}
+
+/* Enumerates adapters by:
+ * - retrieving the IP address table for the local machine
+ * - eliminating loopback addresses from the table
+ * - eliminating redundant addresses, that is, multiple addresses on the same
+ *   subnet
+ * Calls NetBIOSEnumAdapters, passing the resulting table as the callback
+ * data.  The callback reenables each adapter that's already in the NetBIOS
+ * table.  After NetBIOSEnumAdapters returns, this function adds any remaining
+ * adapters to the NetBIOS table.
+ */
+static UCHAR NetBTEnum(void)
+{
+    UCHAR ret;
+    DWORD size = 0;
+
+    TRACE("\n");
+
+    if (GetIpAddrTable(NULL, &size, FALSE) == ERROR_INSUFFICIENT_BUFFER)
+    {
+        PMIB_IPADDRTABLE ipAddrs, coalesceTable = NULL;
+        DWORD numIPAddrs = (size - sizeof(MIB_IPADDRTABLE)) /
+         sizeof(MIB_IPADDRROW) + 1;
+
+        ipAddrs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
+        if (ipAddrs)
+            coalesceTable = HeapAlloc(GetProcessHeap(),
+             HEAP_ZERO_MEMORY, sizeof(MIB_IPADDRTABLE) +
+             (min(numIPAddrs, MAX_LANA + 1) - 1) * sizeof(MIB_IPADDRROW));
+        if (ipAddrs && coalesceTable)
+        {
+            if (GetIpAddrTable(ipAddrs, &size, FALSE) == ERROR_SUCCESS)
+            {
+                DWORD ndx;
+
+                for (ndx = 0; ndx < ipAddrs->dwNumEntries; ndx++)
+                {
+                    if ((ipAddrs->table[ndx].dwAddr &
+                     ipAddrs->table[ndx].dwMask) !=
+                     htonl((INADDR_LOOPBACK & IN_CLASSA_NET)))
+                    {
+                        BOOL newNetwork = TRUE;
+                        DWORD innerIndex;
+
+                        /* make sure we don't have more than one entry
+                         * for a subnet */
+                        for (innerIndex = 0; newNetwork &&
+                         innerIndex < coalesceTable->dwNumEntries; innerIndex++)
+                            if ((ipAddrs->table[ndx].dwAddr &
+                             ipAddrs->table[ndx].dwMask) ==
+                             (coalesceTable->table[innerIndex].dwAddr
+                             & coalesceTable->table[innerIndex].dwMask))
+                                newNetwork = FALSE;
+
+                        if (newNetwork)
+                            memcpy(&coalesceTable->table[
+                             coalesceTable->dwNumEntries++],
+                             &ipAddrs->table[ndx], sizeof(MIB_IPADDRROW));
+                    }
+                }
+
+                NetBIOSEnumAdapters(gTransportID, NetBTEnumCallback,
+                 coalesceTable);
+                ret = NRC_GOODRET;
+                for (ndx = 0; ret == NRC_GOODRET &&
+                 ndx < coalesceTable->dwNumEntries; ndx++)
+                    if (coalesceTable->table[ndx].dwAddr != INADDR_LOOPBACK)
+                        ret = NetBTRegisterAdapter(&coalesceTable->table[ndx]);
+            }
+            else
+                ret = NRC_SYSTEM;
+            HeapFree(GetProcessHeap(), 0, ipAddrs);
+            HeapFree(GetProcessHeap(), 0, coalesceTable);
+        }
+        else
+            ret = NRC_OSRESNOTAV;
+    }
+    else
+        ret = NRC_SYSTEM;
+    TRACE("returning 0x%02x\n", ret);
+    return ret;
+}
+
+static const WCHAR VxD_MSTCPW[] = { 'S','Y','S','T','E','M','\\','C','u','r',
+ 'r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\','S','e','r','v',
+ 'i','c','e','s','\\','V','x','D','\\','M','S','T','C','P','\0' };
+static const WCHAR NetBT_ParametersW[] = { 'S','Y','S','T','E','M','\\','C','u',
+ 'r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\','S','e','r',
+ 'v','i','c','e','s','\\','N','e','t','B','T','\\','P','a','r','a','m','e','t',
+ 'e','r','s','\0' };
+static const WCHAR EnableDNSW[] = { 'E','n','a','b','l','e','D','N','S','\0' };
+static const WCHAR BcastNameQueryCountW[] = { 'B','c','a','s','t','N','a','m',
+ 'e','Q','u','e','r','y','C','o','u','n','t','\0' };
+static const WCHAR BcastNameQueryTimeoutW[] = { 'B','c','a','s','t','N','a','m',
+ 'e','Q','u','e','r','y','T','i','m','e','o','u','t','\0' };
+static const WCHAR NameSrvQueryCountW[] = { 'N','a','m','e','S','r','v',
+ 'Q','u','e','r','y','C','o','u','n','t','\0' };
+static const WCHAR NameSrvQueryTimeoutW[] = { 'N','a','m','e','S','r','v',
+ 'Q','u','e','r','y','T','i','m','e','o','u','t','\0' };
+static const WCHAR ScopeIDW[] = { 'S','c','o','p','e','I','D','\0' };
+static const WCHAR CacheTimeoutW[] = { 'C','a','c','h','e','T','i','m','e','o',
+ 'u','t','\0' };
+static const WCHAR Config_NetworkW[] = { 'S','o','f','t','w','a','r','e','\\',
+                                         'W','i','n','e','\\','N','e','t','w','o','r','k','\0' };
+
+/* Initializes global variables and registers the NetBT transport */
+void NetBTInit(void)
+{
+    HKEY hKey;
+    NetBIOSTransport transport;
+    LONG ret;
+
+    TRACE("\n");
+
+    gEnableDNS = TRUE;
+    gBCastQueries = BCAST_QUERIES;
+    gBCastQueryTimeout = BCAST_QUERY_TIMEOUT;
+    gWINSQueries = WINS_QUERIES;
+    gWINSQueryTimeout = WINS_QUERY_TIMEOUT;
+    gNumWINSServers = 0;
+    memset(gWINSServers, 0, sizeof(gWINSServers));
+    gScopeID[0] = '\0';
+    gCacheTimeout = CACHE_TIMEOUT;
+
+    /* Try to open the Win9x NetBT configuration key */
+    ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, VxD_MSTCPW, 0, KEY_READ, &hKey);
+    /* If that fails, try the WinNT NetBT configuration key */
+    if (ret != ERROR_SUCCESS)
+        ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, NetBT_ParametersW, 0, KEY_READ,
+         &hKey);
+    if (ret == ERROR_SUCCESS)
+    {
+        DWORD dword, size;
+
+        size = sizeof(dword);
+        if (RegQueryValueExW(hKey, EnableDNSW, NULL, NULL,
+         (LPBYTE)&dword, &size) == ERROR_SUCCESS)
+            gEnableDNS = dword;
+        size = sizeof(dword);
+        if (RegQueryValueExW(hKey, BcastNameQueryCountW, NULL, NULL,
+         (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_QUERIES
+         && dword <= MAX_QUERIES)
+            gBCastQueries = dword;
+        size = sizeof(dword);
+        if (RegQueryValueExW(hKey, BcastNameQueryTimeoutW, NULL, NULL,
+         (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_QUERY_TIMEOUT
+         && dword <= MAX_QUERY_TIMEOUT)
+            gBCastQueryTimeout = dword;
+        size = sizeof(dword);
+        if (RegQueryValueExW(hKey, NameSrvQueryCountW, NULL, NULL,
+         (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_QUERIES
+         && dword <= MAX_QUERIES)
+            gWINSQueries = dword;
+        size = sizeof(dword);
+        if (RegQueryValueExW(hKey, NameSrvQueryTimeoutW, NULL, NULL,
+         (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_QUERY_TIMEOUT
+         && dword <= MAX_QUERY_TIMEOUT)
+            gWINSQueryTimeout = dword;
+        size = MAX_DOMAIN_NAME_LEN - 1;
+        if (RegQueryValueExW(hKey, ScopeIDW, NULL, NULL, (LPBYTE)gScopeID + 1, &size)
+         == ERROR_SUCCESS)
+        {
+            /* convert into L2-encoded version, suitable for use by
+               NetBTNameEncode */
+            char *ptr, *lenPtr;
+
+            for (ptr = gScopeID + 1; *ptr &&
+             ptr - gScopeID < MAX_DOMAIN_NAME_LEN; )
+            {
+                for (lenPtr = ptr - 1, *lenPtr = 0; *ptr && *ptr != '.' &&
+                 ptr - gScopeID < MAX_DOMAIN_NAME_LEN; ptr++)
+                    *lenPtr += 1;
+                ptr++;
+            }
+        }
+        if (RegQueryValueExW(hKey, CacheTimeoutW, NULL, NULL,
+         (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_CACHE_TIMEOUT)
+            gCacheTimeout = dword;
+        RegCloseKey(hKey);
+    }
+    /* WINE-specific NetBT registry settings.  Because our adapter naming is
+     * different than MS', we can't do per-adapter WINS configuration in the
+     * same place.  Just do a global WINS configuration instead.
+     */
+    /* @@ Wine registry key: HKCU\Software\Wine\Network */
+    if (RegOpenKeyW(HKEY_CURRENT_USER, Config_NetworkW, &hKey) == ERROR_SUCCESS)
+    {
+        static const char *nsValueNames[] = { "WinsServer", "BackupWinsServer" };
+        char nsString[16];
+        DWORD size, ndx;
+
+        for (ndx = 0; ndx < sizeof(nsValueNames) / sizeof(nsValueNames[0]);
+         ndx++)
+        {
+            size = sizeof(nsString) / sizeof(char);
+            if (RegQueryValueExA(hKey, nsValueNames[ndx], NULL, NULL,
+             (LPBYTE)nsString, &size) == ERROR_SUCCESS)
+            {
+                unsigned long addr = inet_addr(nsString);
+
+                if (addr != INADDR_NONE && gNumWINSServers < MAX_WINS_SERVERS)
+                    gWINSServers[gNumWINSServers++] = addr;
+            }
+        }
+        RegCloseKey(hKey);
+    }
+
+    transport.enumerate      = NetBTEnum;
+    transport.astat          = NetBTAstat;
+    transport.findName       = NetBTFindName;
+    transport.call           = NetBTCall;
+    transport.send           = NetBTSend;
+    transport.recv           = NetBTRecv;
+    transport.hangup         = NetBTHangup;
+    transport.cleanupAdapter = NetBTCleanupAdapter;
+    transport.cleanup        = NetBTCleanup;
+    memcpy(&gTransportID, TRANSPORT_NBT, sizeof(ULONG));
+    NetBIOSRegisterTransport(gTransportID, &transport);
+}
diff --git a/reactos/dll/netapi32/netapi32.c b/reactos/dll/netapi32/netapi32.c
new file mode 100644 (file)
index 0000000..e01b929
--- /dev/null
@@ -0,0 +1,136 @@
+/* Copyright 2001 Mike McCormack
+ * Copyright 2003 Juan Lang
+ *
+ * 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 "wine/debug.h"
+#include "lm.h"
+#include "netbios.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(netbios);
+
+HMODULE NETAPI32_hModule = 0;
+
+BOOL WINAPI DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+    TRACE("%p,%lx,%p\n", hinstDLL, fdwReason, lpvReserved);
+
+    switch (fdwReason) {
+        case DLL_PROCESS_ATTACH:
+        {
+            DisableThreadLibraryCalls(hinstDLL);
+            NETAPI32_hModule = hinstDLL;
+            NetBIOSInit();
+            NetBTInit();
+            break;
+        }
+        case DLL_PROCESS_DETACH:
+        {
+            NetBIOSShutdown();
+            break;
+        }
+    }
+
+    return TRUE;
+}
+
+NET_API_STATUS  WINAPI NetServerEnum(
+  LPCWSTR servername,
+  DWORD level,
+  LPBYTE* bufptr,
+  DWORD prefmaxlen,
+  LPDWORD entriesread,
+  LPDWORD totalentries,
+  DWORD servertype,
+  LPCWSTR domain,
+  LPDWORD resume_handle
+)
+{
+    FIXME("Stub (%s %ld %p %ld %p %p %ld %s %p)\n", debugstr_w(servername),
+     level, bufptr, prefmaxlen, entriesread, totalentries, servertype,
+     debugstr_w(domain), resume_handle);
+
+    return ERROR_NO_BROWSER_SERVERS_FOUND;
+}
+
+
+/************************************************************
+ *                NetServerGetInfo  (NETAPI32.@)
+ */
+NET_API_STATUS WINAPI NetServerGetInfo(LMSTR servername, DWORD level, LPBYTE* bufptr)
+{
+    FIXME("stub (%p, %ld, %p)\n", servername, level, bufptr);
+    return ERROR_ACCESS_DENIED;
+}
+
+
+/************************************************************
+ *                NetStatisticsGet  (NETAPI32.@)
+ */
+NET_API_STATUS WINAPI NetStatisticsGet(LPWSTR server, LPWSTR service,
+                                       DWORD level, DWORD options,
+                                       LPBYTE *bufptr)
+{
+    TRACE("(%p, %p, %ld, %ld, %p)\n", server, service, level, options, bufptr);
+    return NERR_InternalError;
+}
+
+DWORD WINAPI NetpNetBiosStatusToApiStatus(DWORD nrc)
+{
+    DWORD ret;
+
+    switch (nrc)
+    {
+        case NRC_GOODRET:
+            ret = NO_ERROR;
+            break;
+        case NRC_NORES:
+            ret = NERR_NoNetworkResource;
+            break;
+        case NRC_DUPNAME:
+            ret = NERR_AlreadyExists;
+            break;
+        case NRC_NAMTFUL:
+            ret = NERR_TooManyNames;
+            break;
+        case NRC_ACTSES:
+            ret = NERR_DeleteLater;
+            break;
+        case NRC_REMTFUL:
+            ret = ERROR_REM_NOT_LIST;
+            break;
+        case NRC_NOCALL:
+            ret = NERR_NameNotFound;
+            break;
+        case NRC_NOWILD:
+            ret = ERROR_INVALID_PARAMETER;
+            break;
+        case NRC_INUSE:
+            ret = NERR_DuplicateName;
+            break;
+        case NRC_NAMERR:
+            ret = ERROR_INVALID_PARAMETER;
+            break;
+        case NRC_NAMCONF:
+            ret = NERR_DuplicateName;
+            break;
+        default:
+            ret = NERR_NetworkError;
+    }
+    return ret;
+}
diff --git a/reactos/dll/netapi32/netapi32.spec b/reactos/dll/netapi32/netapi32.spec
new file mode 100644 (file)
index 0000000..dbb42c4
--- /dev/null
@@ -0,0 +1,290 @@
+@ stub DsAddressToSiteNames
+@ stub DsAddressToSiteNamesEx
+@ stub DsDeregisterDnsHostRecords
+@ stub DsEnumerateDomainTrusts
+@ stub DsGetDcClose
+@ stub DsGetDcName
+@ stub DsGetDcNext
+@ stub DsGetDcOpen
+@ stub DsGetDcSiteCoverage
+@ stub DsGetForestTrustInformationW
+@ stub DsGetSiteName
+@ stub DsMergeForestTrustInformationW
+@ stdcall DsRoleFreeMemory(ptr)
+@ stdcall DsRoleGetPrimaryDomainInformation(wstr long ptr)
+@ stub DsValidateSubnetName
+@ stub I_BrowserDebugCall
+@ stub I_BrowserDebugTrace
+@ stdcall I_BrowserQueryEmulatedDomains(wstr ptr ptr)
+@ stub I_BrowserQueryOtherDomains
+@ stub I_BrowserQueryStatistics
+@ stub I_BrowserResetNetlogonState
+@ stub I_BrowserResetStatistics
+@ stub I_BrowserServerEnum
+@ stdcall I_BrowserSetNetlogonState(wstr wstr wstr long)
+@ stub I_NetAccountDeltas
+@ stub I_NetAccountSync
+@ stub I_NetDatabaseDeltas
+@ stub I_NetDatabaseRedo
+@ stub I_NetDatabaseSync2
+@ stub I_NetDatabaseSync
+@ stub I_NetDfsCreateExitPoint
+@ stub I_NetDfsCreateLocalPartition
+@ stub I_NetDfsDeleteExitPoint
+@ stub I_NetDfsDeleteLocalPartition
+@ stub I_NetDfsFixLocalVolume
+@ stub I_NetDfsGetVersion
+@ stub I_NetDfsIsThisADomainName
+@ stub I_NetDfsModifyPrefix
+@ stub I_NetDfsSetLocalVolumeState
+@ stub I_NetDfsSetServerInfo
+@ stub I_NetGetDCList
+@ stub I_NetListCanonicalize
+@ stub I_NetListTraverse
+@ stub I_NetLogonControl2
+@ stub I_NetLogonControl
+@ stub I_NetLogonSamLogoff
+@ stub I_NetLogonSamLogon
+@ stub I_NetLogonUasLogoff
+@ stub I_NetLogonUasLogon
+@ stub I_NetNameCanonicalize
+@ stdcall I_NetNameCompare(ptr wstr wstr ptr ptr)
+@ stdcall I_NetNameValidate(ptr wstr ptr ptr)
+@ stub I_NetPathCanonicalize
+@ stub I_NetPathCompare
+@ stub I_NetPathType
+@ stub I_NetServerAuthenticate2
+@ stub I_NetServerAuthenticate
+@ stub I_NetServerPasswordSet
+@ stub I_NetServerReqChallenge
+@ stub I_NetServerSetServiceBits
+@ stub I_NetServerSetServiceBitsEx
+@ stub NetAlertRaise
+@ stub NetAlertRaiseEx
+@ stdcall NetApiBufferAllocate(long ptr)
+@ stdcall NetApiBufferFree(ptr)
+@ stdcall NetApiBufferReallocate(ptr long ptr)
+@ stdcall NetApiBufferSize(ptr ptr)
+@ stub NetAuditClear
+@ stub NetAuditRead
+@ stub NetAuditWrite
+@ stub NetBrowserStatisticsGet
+@ stub NetConfigGet
+@ stub NetConfigGetAll
+@ stub NetConfigSet
+@ stub NetConnectionEnum
+@ stub NetDfsAdd
+@ stub NetDfsEnum
+@ stub NetDfsGetInfo
+@ stub NetDfsManagerGetConfigInfo
+@ stub NetDfsMove
+@ stub NetDfsRemove
+@ stub NetDfsRename
+@ stub NetDfsSetInfo
+@ stub NetEnumerateTrustedDomains
+@ stub NetErrorLogClear
+@ stub NetErrorLogRead
+@ stub NetErrorLogWrite
+@ stub NetFileClose
+@ stub NetFileEnum
+@ stub NetFileGetInfo
+@ stub NetGetAnyDCName
+@ stdcall NetGetDCName(wstr wstr ptr)
+@ stub NetGetDisplayInformationIndex
+@ stdcall NetGetJoinInformation(wstr ptr ptr)
+@ stub NetGroupAdd
+@ stub NetGroupAddUser
+@ stub NetGroupDel
+@ stub NetGroupDelUser
+@ stub NetGroupEnum
+@ stub NetGroupGetInfo
+@ stub NetGroupGetUsers
+@ stub NetGroupSetInfo
+@ stub NetGroupSetUsers
+@ stdcall NetLocalGroupAdd(wstr long ptr ptr)
+@ stub NetLocalGroupAddMember
+@ stub NetLocalGroupAddMembers
+@ stub NetLocalGroupDel
+@ stub NetLocalGroupDelMember
+@ stub NetLocalGroupDelMembers
+@ stub NetLocalGroupEnum
+@ stub NetLocalGroupGetInfo
+@ stub NetLocalGroupGetMembers
+@ stub NetLocalGroupSetInfo
+@ stdcall NetLocalGroupSetMembers(wstr wstr long ptr ptr)
+@ stub NetMessageBufferSend
+@ stub NetMessageNameAdd
+@ stub NetMessageNameDel
+@ stub NetMessageNameEnum
+@ stub NetMessageNameGetInfo
+@ stdcall NetQueryDisplayInformation(wstr long long long long ptr ptr)
+@ stub NetRemoteComputerSupports
+@ stub NetRemoteTOD
+@ stub NetReplExportDirAdd
+@ stub NetReplExportDirDel
+@ stub NetReplExportDirEnum
+@ stub NetReplExportDirGetInfo
+@ stub NetReplExportDirLock
+@ stub NetReplExportDirSetInfo
+@ stub NetReplExportDirUnlock
+@ stub NetReplGetInfo
+@ stub NetReplImportDirAdd
+@ stub NetReplImportDirDel
+@ stub NetReplImportDirEnum
+@ stub NetReplImportDirGetInfo
+@ stub NetReplImportDirLock
+@ stub NetReplImportDirUnlock
+@ stub NetReplSetInfo
+@ stub NetRplAdapterAdd
+@ stub NetRplAdapterDel
+@ stub NetRplAdapterEnum
+@ stub NetRplBootAdd
+@ stub NetRplBootDel
+@ stub NetRplBootEnum
+@ stub NetRplClose
+@ stub NetRplConfigAdd
+@ stub NetRplConfigDel
+@ stub NetRplConfigEnum
+@ stub NetRplGetInfo
+@ stub NetRplOpen
+@ stub NetRplProfileAdd
+@ stub NetRplProfileClone
+@ stub NetRplProfileDel
+@ stub NetRplProfileEnum
+@ stub NetRplProfileGetInfo
+@ stub NetRplProfileSetInfo
+@ stub NetRplSetInfo
+@ stub NetRplSetSecurity
+@ stub NetRplVendorAdd
+@ stub NetRplVendorDel
+@ stub NetRplVendorEnum
+@ stub NetRplWkstaAdd
+@ stub NetRplWkstaClone
+@ stub NetRplWkstaDel
+@ stub NetRplWkstaEnum
+@ stub NetRplWkstaGetInfo
+@ stub NetRplWkstaSetInfo
+@ stub NetScheduleJobAdd
+@ stub NetScheduleJobDel
+@ stub NetScheduleJobEnum
+@ stub NetScheduleJobGetInfo
+@ stub NetServerComputerNameAdd
+@ stub NetServerComputerNameDel
+@ stub NetServerDiskEnum
+@ stdcall NetServerEnum(wstr long ptr long ptr ptr long wstr ptr)
+@ stub NetServerEnumEx
+@ stdcall NetServerGetInfo(wstr long ptr)
+@ stub NetServerSetInfo
+@ stub NetServerTransportAdd
+@ stub NetServerTransportAddEx
+@ stub NetServerTransportDel
+@ stub NetServerTransportEnum
+@ stub NetServiceControl
+@ stub NetServiceEnum
+@ stub NetServiceGetInfo
+@ stub NetServiceInstall
+@ stub NetSessionDel
+@ stub NetSessionEnum
+@ stub NetSessionGetInfo
+@ stub NetShareAdd
+@ stub NetShareCheck
+@ stub NetShareDel
+@ stub NetShareDelSticky
+@ stdcall NetShareEnum(wstr long ptr long ptr ptr ptr)
+@ stub NetShareEnumSticky
+@ stub NetShareGetInfo
+@ stub NetShareSetInfo
+@ stdcall NetStatisticsGet(wstr wstr long long ptr)
+@ stub NetUseAdd
+@ stub NetUseDel
+@ stub NetUseEnum
+@ stub NetUseGetInfo
+@ stdcall NetUserAdd(wstr long ptr ptr)
+@ stub NetUserChangePassword
+@ stdcall NetUserDel(wstr wstr)
+@ stdcall NetUserEnum(wstr long long ptr long ptr ptr ptr)
+@ stub NetUserGetGroups
+@ stdcall NetUserGetInfo(wstr wstr long ptr)
+@ stub NetUserGetLocalGroups
+@ stdcall NetUserModalsGet(wstr long ptr)
+@ stub NetUserModalsSet
+@ stub NetUserSetGroups
+@ stub NetUserSetInfo
+@ stdcall NetWkstaGetInfo(wstr long ptr)
+@ stub NetWkstaSetInfo
+@ stub NetWkstaTransportAdd
+@ stub NetWkstaTransportDel
+@ stdcall NetWkstaTransportEnum (wstr long ptr long ptr ptr ptr)
+@ stub NetWkstaUserEnum
+@ stdcall NetWkstaUserGetInfo(wstr long ptr)
+@ stub NetWkstaUserSetInfo
+@ stdcall NetapipBufferAllocate(long ptr) NetApiBufferAllocate
+@ stdcall Netbios(ptr)
+@ stub NetpAccessCheck
+@ stub NetpAccessCheckAndAudit
+@ stub NetpAllocConfigName
+@ stub NetpAllocStrFromStr
+@ stub NetpAllocStrFromWStr
+@ stub NetpAllocTStrFromString
+@ stub NetpAllocWStrFromStr
+@ stub NetpAllocWStrFromWStr
+@ stub NetpApiStatusToNtStatus
+@ stub NetpAssertFailed
+@ stub NetpCloseConfigData
+@ stub NetpCopyStringToBuffer
+@ stub NetpCreateSecurityObject
+@ stub NetpDbgDisplayServerInfo
+@ stub NetpDbgPrint
+@ stdcall NetpDeleteSecurityObject(long) ntdll.RtlDeleteSecurityObject
+@ stdcall NetpGetComputerName(ptr)
+@ stub NetpGetConfigBool
+@ stub NetpGetConfigDword
+@ stub NetpGetConfigTStrArray
+@ stub NetpGetConfigValue
+@ stub NetpGetDomainName
+@ stub NetpGetFileSecurity
+@ stub NetpGetPrivilege
+@ stub NetpHexDump
+@ stdcall NetpInitOemString(ptr str) ntdll.RtlInitAnsiString
+@ stub NetpIsRemote
+@ stub NetpIsUncComputerNameValid
+@ stub NetpLocalTimeZoneOffset
+@ stub NetpLogonPutUnicodeString
+@ stub NetpNetBiosAddName
+@ stub NetpNetBiosCall
+@ stub NetpNetBiosDelName
+@ stub NetpNetBiosGetAdapterNumbers
+@ stub NetpNetBiosHangup
+@ stub NetpNetBiosReceive
+@ stub NetpNetBiosReset
+@ stub NetpNetBiosSend
+@ stdcall NetpNetBiosStatusToApiStatus(long)
+@ stub NetpNtStatusToApiStatus
+@ stub NetpOpenConfigData
+@ stub NetpPackString
+@ stub NetpReleasePrivilege
+@ stub NetpSetConfigBool
+@ stub NetpSetConfigDword
+@ stub NetpSetConfigTStrArray
+@ stub NetpSetFileSecurity
+@ stub NetpSmbCheck
+@ stub NetpStringToNetBiosName
+@ stub NetpTStrArrayEntryCount
+@ stub NetpwNameCanonicalize
+@ stub NetpwNameCompare
+@ stub NetpwNameValidate
+@ stub NetpwPathCanonicalize
+@ stub NetpwPathCompare
+@ stub NetpwPathType
+@ stub NlBindingAddServerToCache
+@ stub NlBindingRemoveServerFromCache
+@ stub NlBindingSetAuthInfo
+@ stub RxNetAccessAdd
+@ stub RxNetAccessDel
+@ stub RxNetAccessEnum
+@ stub RxNetAccessGetInfo
+@ stub RxNetAccessGetUserPerms
+@ stub RxNetAccessSetInfo
+@ stub RxNetServerEnum
+@ stub RxNetUserPasswordSet
+@ stub RxRemoteApi
diff --git a/reactos/dll/netapi32/netapi32.xml b/reactos/dll/netapi32/netapi32.xml
new file mode 100644 (file)
index 0000000..04796c2
--- /dev/null
@@ -0,0 +1,28 @@
+<module name="netapi32" type="win32dll" baseaddress="${BASEADDRESS_NETAPI32}" installbase="system32" installname="netapi32.dll" allowwarnings="true">
+       <importlibrary definition="netapi32.spec.def" />
+       <include base="netapi32">.</include>
+       <include base="ReactOS">include/wine</include>
+       <define name="__REACTOS__" />
+       <define name="__USE_W32API" />
+       <define name="_WIN32_IE">0x600</define>
+       <define name="_WIN32_WINNT">0x501</define>
+       <define name="WINVER">0x501</define>
+       <define name="_SVRAPI_" />
+       <library>wine</library>
+       <library>ntdll</library>
+       <library>kernel32</library>
+       <library>advapi32</library>
+       <library>ws2_32</library>
+       <library>iphlpapi</library>
+       <file>access.c</file>
+       <file>apibuf.c</file>
+       <file>browsr.c</file>
+       <file>ds.c</file>
+       <file>nbcmdqueue.c</file>
+       <file>nbnamecache.c</file>
+       <file>nbt.c</file>
+       <file>netapi32.c</file>
+       <file>netbios.c</file>
+       <file>wksta.c</file>
+       <file>netapi32.spec</file>
+</module>
diff --git a/reactos/dll/netapi32/netapi32_misc.h b/reactos/dll/netapi32/netapi32_misc.h
new file mode 100644 (file)
index 0000000..92cbc26
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2002 Andriy Palamarchuk
+ *
+ * netapi32 internal functions.
+ *
+ * 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, writ
+e to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef __WINE_NETAPI32_MISC_H
+#define __WINE_NETAPI32_MISC_H
+
+extern BOOL NETAPI_IsLocalComputer(LPCWSTR ServerName);
+
+#define NETAPI_ForceLocalComputer(ServerName, FailureCode) \
+    if (!NETAPI_IsLocalComputer(ServerName)) \
+    { \
+        FIXME("Action Implemented for local computer only. " \
+              "Requested for server %s\n", debugstr_w(ServerName)); \
+        return FailureCode; \
+    }
+
+#endif
diff --git a/reactos/dll/netapi32/netbios.c b/reactos/dll/netapi32/netbios.c
new file mode 100644 (file)
index 0000000..71f503a
--- /dev/null
@@ -0,0 +1,857 @@
+/* Copyright (c) 2003 Juan Lang
+ *
+ * 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 "wine/debug.h"
+#include "nbcmdqueue.h"
+#include "netbios.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(netbios);
+
+/* This file provides a NetBIOS emulator that implements the NetBIOS interface,
+ * including thread safety and asynchronous call support.  The protocol
+ * implementation is separate, with blocking (synchronous) functions.
+ */
+
+#define ADAPTERS_INCR 8
+#define DEFAULT_NUM_SESSIONS 16
+
+typedef struct _NetBIOSTransportTableEntry
+{
+    ULONG            id;
+    NetBIOSTransport transport;
+} NetBIOSTransportTableEntry;
+
+typedef struct _NetBIOSSession
+{
+    BOOL  inUse;
+    UCHAR state;
+    UCHAR local_name[NCBNAMSZ];
+    UCHAR remote_name[NCBNAMSZ];
+    void *data;
+} NetBIOSSession;
+
+/* This struct needs a little explanation, unfortunately.  enabled is only
+ * used by nbInternalEnum (see).  If transport_id is not 0 and transport
+ * is not NULL, the adapter is considered valid.  (transport is a pointer to
+ * an entry in a NetBIOSTransportTableEntry.)  data has data for the callers of
+ * NetBIOSEnumAdapters to be able to see.  The lana is repeated there, even
+ * though I don't use it internally--it's for transports to use reenabling
+ * adapters using NetBIOSEnableAdapter.
+ */
+typedef struct _NetBIOSAdapter
+{
+    BOOL               enabled;
+    BOOL               shuttingDown;
+    LONG               resetting;
+    ULONG              transport_id;
+    NetBIOSTransport  *transport;
+    NetBIOSAdapterImpl impl;
+    struct NBCmdQueue *cmdQueue;
+    CRITICAL_SECTION   cs;
+    DWORD              sessionsLen;
+    NetBIOSSession    *sessions;
+} NetBIOSAdapter;
+
+typedef struct _NetBIOSAdapterTable {
+    CRITICAL_SECTION cs;
+    BOOL             enumerated;
+    BOOL             enumerating;
+    UCHAR            tableSize;
+    NetBIOSAdapter  *table;
+} NetBIOSAdapterTable;
+
+/* Just enough space for NBT right now */
+static NetBIOSTransportTableEntry gTransports[1];
+static UCHAR gNumTransports = 0;
+static NetBIOSAdapterTable gNBTable;
+
+static UCHAR nbResizeAdapterTable(UCHAR newSize)
+{
+    UCHAR ret;
+
+    if (gNBTable.table)
+        gNBTable.table = HeapReAlloc(GetProcessHeap(),
+         HEAP_ZERO_MEMORY, gNBTable.table,
+         newSize * sizeof(NetBIOSAdapter));
+    else
+        gNBTable.table = HeapAlloc(GetProcessHeap(),
+         HEAP_ZERO_MEMORY, newSize * sizeof(NetBIOSAdapter));
+    if (gNBTable.table)
+    {
+        gNBTable.tableSize = newSize;
+        ret = NRC_GOODRET;
+    }
+    else
+        ret = NRC_OSRESNOTAV;
+    return ret;
+}
+
+void NetBIOSInit(void)
+{
+    memset(&gNBTable, 0, sizeof(gNBTable));
+    InitializeCriticalSection(&gNBTable.cs);
+}
+
+void NetBIOSShutdown(void)
+{
+    UCHAR i;
+
+    EnterCriticalSection(&gNBTable.cs);
+    for (i = 0; i < gNBTable.tableSize; i++)
+    {
+        if (gNBTable.table[i].transport &&
+         gNBTable.table[i].transport->cleanupAdapter)
+            gNBTable.table[i].transport->cleanupAdapter(
+             gNBTable.table[i].impl.data);
+    }
+    for (i = 0; i < gNumTransports; i++)
+        if (gTransports[i].transport.cleanup)
+            gTransports[i].transport.cleanup();
+    LeaveCriticalSection(&gNBTable.cs);
+    DeleteCriticalSection(&gNBTable.cs);
+    HeapFree(GetProcessHeap(), 0, gNBTable.table);
+}
+
+BOOL NetBIOSRegisterTransport(ULONG id, NetBIOSTransport *transport)
+{
+    BOOL ret;
+
+    TRACE(": transport 0x%08lx, p %p\n", id, transport);
+    if (!transport)
+        ret = FALSE;
+    else if (gNumTransports >= sizeof(gTransports) / sizeof(gTransports[0]))
+    {
+        FIXME("You tried to add %d transports, but I only have space for %d\n",
+         gNumTransports + 1, sizeof(gTransports) / sizeof(gTransports[0]));
+        ret = FALSE;
+    }
+    else
+    {
+        UCHAR i;
+
+        ret = FALSE;
+        for (i = 0; !ret && i < gNumTransports; i++)
+        {
+            if (gTransports[i].id == id)
+            {
+                WARN("Replacing NetBIOS transport ID %ld\n", id);
+                memcpy(&gTransports[i].transport, transport,
+                 sizeof(NetBIOSTransport));
+                ret = TRUE;
+            }
+        }
+        if (!ret)
+        {
+            gTransports[gNumTransports].id = id;
+            memcpy(&gTransports[gNumTransports].transport, transport,
+             sizeof(NetBIOSTransport));
+            gNumTransports++;
+            ret = TRUE;
+        }
+    }
+    TRACE("returning %d\n", ret);
+    return ret;
+}
+
+/* In this, I acquire the table lock to make sure no one else is modifying it.
+ * This is _probably_ overkill since it should only be called during the
+ * context of a NetBIOSEnum call, but just to be safe..
+ */
+BOOL NetBIOSRegisterAdapter(ULONG transport, DWORD ifIndex, void *data)
+{
+    BOOL ret;
+    UCHAR i;
+
+    TRACE(": transport 0x%08lx, ifIndex 0x%08lx, data %p\n", transport, ifIndex,
+     data);
+    for (i = 0; i < gNumTransports && gTransports[i].id != transport; i++)
+        ;
+    if (gTransports[i].id == transport)
+    {
+        NetBIOSTransport *transportPtr = &gTransports[i].transport;
+
+        TRACE(": found transport %p for id 0x%08lx\n", transportPtr, transport);
+
+        EnterCriticalSection(&gNBTable.cs);
+        ret = FALSE;
+        for (i = 0; i < gNBTable.tableSize &&
+         gNBTable.table[i].transport != 0; i++)
+            ;
+        if (i == gNBTable.tableSize && gNBTable.tableSize < MAX_LANA + 1)
+        {
+            UCHAR newSize;
+
+            if (gNBTable.tableSize < (MAX_LANA + 1) - ADAPTERS_INCR)
+                newSize = gNBTable.tableSize + ADAPTERS_INCR;
+            else
+                newSize = MAX_LANA + 1;
+            nbResizeAdapterTable(newSize);
+        }
+        if (i < gNBTable.tableSize && gNBTable.table[i].transport == 0)
+        {
+            TRACE(": registering as LANA %d\n", i);
+            gNBTable.table[i].transport_id = transport;
+            gNBTable.table[i].transport = transportPtr;
+            gNBTable.table[i].impl.lana = i;
+            gNBTable.table[i].impl.ifIndex = ifIndex;
+            gNBTable.table[i].impl.data = data;
+            gNBTable.table[i].cmdQueue = NBCmdQueueCreate(GetProcessHeap());
+            InitializeCriticalSection(&gNBTable.table[i].cs);
+            gNBTable.table[i].enabled = TRUE;
+            ret = TRUE;
+        }
+        LeaveCriticalSection(&gNBTable.cs);
+    }
+    else
+        ret = FALSE;
+    TRACE("returning %d\n", ret);
+    return ret;
+}
+
+/* In this, I acquire the table lock to make sure no one else is modifying it.
+ * This is _probably_ overkill since it should only be called during the
+ * context of a NetBIOSEnum call, but just to be safe..
+ */
+void NetBIOSEnableAdapter(UCHAR lana)
+{
+    TRACE(": %d\n", lana);
+    if (lana < gNBTable.tableSize)
+    {
+        EnterCriticalSection(&gNBTable.cs);
+        if (gNBTable.table[lana].transport != 0)
+            gNBTable.table[lana].enabled = TRUE;
+        LeaveCriticalSection(&gNBTable.cs);
+    }
+}
+
+static void nbShutdownAdapter(NetBIOSAdapter *adapter)
+{
+    if (adapter)
+    {
+        adapter->shuttingDown = TRUE;
+        NBCmdQueueCancelAll(adapter->cmdQueue);
+        if (adapter->transport->cleanupAdapter)
+            adapter->transport->cleanupAdapter(adapter->impl.data);
+        NBCmdQueueDestroy(adapter->cmdQueue);
+        DeleteCriticalSection(&adapter->cs);
+        memset(adapter, 0, sizeof(NetBIOSAdapter));
+    }
+}
+
+static void nbInternalEnum(void)
+{
+    UCHAR i;
+
+    EnterCriticalSection(&gNBTable.cs);
+    TRACE("before mark\n");
+    /* mark: */
+    for (i = 0; i < gNBTable.tableSize; i++)
+        if (gNBTable.table[i].enabled && gNBTable.table[i].transport != 0)
+            gNBTable.table[i].enabled = FALSE;
+
+    TRACE("marked, before store, %d transports\n", gNumTransports);
+    /* store adapters: */
+    for (i = 0; i < gNumTransports; i++)
+        if (gTransports[i].transport.enumerate)
+            gTransports[i].transport.enumerate();
+
+    TRACE("before sweep\n");
+    /* sweep: */
+    for (i = 0; i < gNBTable.tableSize; i++)
+        if (!gNBTable.table[i].enabled && gNBTable.table[i].transport != 0)
+            nbShutdownAdapter(&gNBTable.table[i]);
+    gNBTable.enumerated = TRUE;
+    LeaveCriticalSection(&gNBTable.cs);
+}
+
+UCHAR NetBIOSNumAdapters(void)
+{
+    UCHAR ret, i;
+
+    if (!gNBTable.enumerated)
+        nbInternalEnum();
+    for (i = 0, ret = 0; i < gNBTable.tableSize; i++)
+        if (gNBTable.table[i].transport != 0)
+            ret++;
+    return ret;
+}
+
+void NetBIOSEnumAdapters(ULONG transport, NetBIOSEnumAdaptersCallback cb,
+ void *closure)
+{
+    TRACE("transport 0x%08lx, callback %p, closure %p\n", transport, cb,
+     closure);
+    if (cb)
+    {
+        BOOL enumAll = memcmp(&transport, ALL_TRANSPORTS, sizeof(ULONG)) == 0;
+        UCHAR i, numLANAs = 0;
+
+        EnterCriticalSection(&gNBTable.cs);
+        if (!gNBTable.enumerating)
+        {
+            gNBTable.enumerating = TRUE;
+            nbInternalEnum();
+            gNBTable.enumerating = FALSE;
+        }
+        for (i = 0; i < gNBTable.tableSize; i++)
+            if (enumAll || gNBTable.table[i].transport_id == transport)
+                numLANAs++;
+        if (numLANAs > 0)
+        {
+            UCHAR lanaIndex = 0;
+
+            for (i = 0; i < gNBTable.tableSize; i++)
+                if (gNBTable.table[i].transport_id != 0 &&
+                 (enumAll || gNBTable.table[i].transport_id == transport))
+                    cb(numLANAs, lanaIndex++, gNBTable.table[i].transport_id,
+                     &gNBTable.table[i].impl, closure);
+        }
+        LeaveCriticalSection(&gNBTable.cs);
+    }
+}
+
+static NetBIOSAdapter *nbGetAdapter(UCHAR lana)
+{
+    NetBIOSAdapter *ret = NULL;
+
+    TRACE(": lana %d, num allocated adapters %d\n", lana, gNBTable.tableSize);
+    if (lana < gNBTable.tableSize && gNBTable.table[lana].transport_id != 0
+     && gNBTable.table[lana].transport)
+        ret = &gNBTable.table[lana];
+    TRACE("returning %p\n", ret);
+    return ret;
+}
+
+static UCHAR nbEnum(PNCB ncb)
+{
+    PLANA_ENUM lanas = (PLANA_ENUM)ncb->ncb_buffer;
+    UCHAR i, ret;
+
+    TRACE(": ncb %p\n", ncb);
+
+    if (!lanas)
+        ret = NRC_BUFLEN;
+    else if (ncb->ncb_length < sizeof(LANA_ENUM))
+        ret = NRC_BUFLEN;
+    else
+    {
+        nbInternalEnum();
+        lanas->length = 0;
+        for (i = 0; i < gNBTable.tableSize; i++)
+            if (gNBTable.table[i].transport)
+            {
+                lanas->length++;
+                lanas->lana[i] = i;
+            }
+        ret = NRC_GOODRET;
+    }
+    TRACE("returning 0x%02x\n", ret);
+    return ret;
+}
+
+static UCHAR nbInternalHangup(NetBIOSAdapter *adapter, NetBIOSSession *session);
+
+static UCHAR nbCancel(NetBIOSAdapter *adapter, PNCB ncb)
+{
+    UCHAR ret;
+
+    TRACE(": adapter %p, ncb %p\n", adapter, ncb);
+
+    if (!adapter) return NRC_BRIDGE;
+    if (!ncb) return NRC_INVADDRESS;
+
+    switch (ncb->ncb_command & 0x7f)
+    {
+        case NCBCANCEL:
+        case NCBADDNAME:
+        case NCBADDGRNAME:
+        case NCBDELNAME:
+        case NCBRESET:
+        case NCBSSTAT:
+            ret = NRC_CANCEL;
+            break;
+
+        /* NCBCALL, NCBCHAINSEND/NCBSEND, NCBHANGUP all close the associated
+         * session if cancelled */
+        case NCBCALL:
+        case NCBSEND:
+        case NCBCHAINSEND:
+        case NCBSENDNA:
+        case NCBCHAINSENDNA:
+        case NCBHANGUP:
+        {
+            if (ncb->ncb_lsn >= adapter->sessionsLen)
+                ret = NRC_SNUMOUT;
+            else if (!adapter->sessions[ncb->ncb_lsn].inUse)
+                ret = NRC_SNUMOUT;
+            else
+            {
+                ret = NBCmdQueueCancel(adapter->cmdQueue, ncb);
+                if (ret == NRC_CMDCAN || ret == NRC_CANOCCR)
+                    nbInternalHangup(adapter, &adapter->sessions[ncb->ncb_lsn]);
+            }
+            break;
+        }
+
+        default:
+            ret = NBCmdQueueCancel(adapter->cmdQueue, ncb);
+    }
+    TRACE("returning 0x%02x\n", ret);
+    return ret;
+}
+
+/* Resizes adapter to contain space for at least sessionsLen sessions.
+ * If allocating more space for sessions, sets the adapter's sessionsLen to
+ * sessionsLen.  If the adapter's sessionsLen was already at least sessionsLen,
+ * does nothing.  Does not modify existing sessions.  Assumes the adapter is
+ * locked.
+ * Returns NRC_GOODRET on success, and something else on failure.
+ */
+static UCHAR nbResizeAdapter(NetBIOSAdapter *adapter, UCHAR sessionsLen)
+{
+    UCHAR ret = NRC_GOODRET;
+
+    if (adapter && adapter->sessionsLen < sessionsLen)
+    {
+        NetBIOSSession *newSessions;
+
+        if (adapter->sessions)
+            newSessions = HeapReAlloc(GetProcessHeap(),
+             HEAP_ZERO_MEMORY, adapter->sessions, sessionsLen *
+             sizeof(NetBIOSSession));
+        else
+            newSessions = HeapAlloc(GetProcessHeap(),
+             HEAP_ZERO_MEMORY, sessionsLen * sizeof(NetBIOSSession));
+        if (newSessions)
+        {
+            adapter->sessions = newSessions;
+            adapter->sessionsLen = sessionsLen;
+        }
+        else
+            ret = NRC_OSRESNOTAV;
+    }
+    return ret;
+}
+
+static UCHAR nbReset(NetBIOSAdapter *adapter, PNCB ncb)
+{
+    UCHAR ret;
+
+    TRACE(": adapter %p, ncb %p\n", adapter, ncb);
+
+    if (!adapter) return NRC_BRIDGE;
+    if (!ncb) return NRC_INVADDRESS;
+
+    if (InterlockedIncrement(&adapter->resetting) == 1)
+    {
+        UCHAR i, resizeTo;
+
+        NBCmdQueueCancelAll(adapter->cmdQueue);
+
+        EnterCriticalSection(&adapter->cs);
+        for (i = 0; i < adapter->sessionsLen; i++)
+            if (adapter->sessions[i].inUse)
+                nbInternalHangup(adapter, &adapter->sessions[i]);
+        if (!ncb->ncb_lsn)
+            resizeTo = ncb->ncb_callname[0] == 0 ? DEFAULT_NUM_SESSIONS :
+             ncb->ncb_callname[0];
+        else if (adapter->sessionsLen == 0)
+            resizeTo = DEFAULT_NUM_SESSIONS;
+        else
+            resizeTo = 0;
+        if (resizeTo > 0)
+            ret = nbResizeAdapter(adapter, resizeTo);
+        else
+            ret = NRC_GOODRET;
+        LeaveCriticalSection(&adapter->cs);
+    }
+    else
+        ret = NRC_TOOMANY;
+    InterlockedDecrement(&adapter->resetting);
+    TRACE("returning 0x%02x\n", ret);
+    return ret;
+}
+
+static UCHAR nbSStat(NetBIOSAdapter *adapter, PNCB ncb)
+{
+    UCHAR ret, i, spaceFor;
+    PSESSION_HEADER sstat;
+
+    TRACE(": adapter %p, NCB %p\n", adapter, ncb);
+
+    if (!adapter) return NRC_BADDR;
+    if (adapter->sessionsLen == 0) return NRC_ENVNOTDEF;
+    if (!ncb) return NRC_INVADDRESS;
+    if (!ncb->ncb_buffer) return NRC_BADDR;
+    if (ncb->ncb_length < sizeof(SESSION_HEADER)) return NRC_BUFLEN;
+
+    sstat = (PSESSION_HEADER)ncb->ncb_buffer;
+    ret = NRC_GOODRET;
+    memset(sstat, 0, sizeof(SESSION_HEADER));
+    spaceFor = (ncb->ncb_length - sizeof(SESSION_HEADER)) /
+     sizeof(SESSION_BUFFER);
+    EnterCriticalSection(&adapter->cs);
+    for (i = 0; ret == NRC_GOODRET && i < adapter->sessionsLen; i++)
+    {
+        if (adapter->sessions[i].inUse && (ncb->ncb_name[0] == '*' ||
+         !memcmp(ncb->ncb_name, adapter->sessions[i].local_name, NCBNAMSZ)))
+        {
+            if (sstat->num_sess < spaceFor)
+            {
+                PSESSION_BUFFER buf;
+               
+                buf = (PSESSION_BUFFER)((PUCHAR)sstat + sizeof(SESSION_HEADER)
+                 + sstat->num_sess * sizeof(SESSION_BUFFER));
+                buf->lsn = i;
+                buf->state = adapter->sessions[i].state;
+                memcpy(buf->local_name, adapter->sessions[i].local_name,
+                 NCBNAMSZ);
+                memcpy(buf->remote_name, adapter->sessions[i].remote_name,
+                 NCBNAMSZ);
+                buf->rcvs_outstanding = buf->sends_outstanding = 0;
+                sstat->num_sess++;
+            }
+            else
+                ret = NRC_BUFLEN;
+        }
+    }
+    LeaveCriticalSection(&adapter->cs);
+
+    TRACE("returning 0x%02x\n", ret);
+    return ret;
+}
+
+static UCHAR nbCall(NetBIOSAdapter *adapter, PNCB ncb)
+{
+    UCHAR ret, i;
+
+    TRACE(": adapter %p, NCB %p\n", adapter, ncb);
+
+    if (!adapter) return NRC_BRIDGE;
+    if (adapter->sessionsLen == 0) return NRC_ENVNOTDEF;
+    if (!adapter->transport->call) return NRC_ILLCMD;
+    if (!ncb) return NRC_INVADDRESS;
+
+    EnterCriticalSection(&adapter->cs);
+    for (i = 0; i < adapter->sessionsLen && adapter->sessions[i].inUse; i++)
+        ;
+    if (i < adapter->sessionsLen)
+    {
+        adapter->sessions[i].inUse = TRUE;
+        adapter->sessions[i].state = CALL_PENDING;
+        memcpy(adapter->sessions[i].local_name, ncb->ncb_name, NCBNAMSZ);
+        memcpy(adapter->sessions[i].remote_name, ncb->ncb_callname, NCBNAMSZ);
+        ret = NRC_GOODRET;
+    }
+    else
+        ret = NRC_LOCTFUL;
+    LeaveCriticalSection(&adapter->cs);
+
+    if (ret == NRC_GOODRET)
+    {
+        ret = adapter->transport->call(adapter->impl.data, ncb,
+         &adapter->sessions[i].data);
+        if (ret == NRC_GOODRET)
+        {
+            ncb->ncb_lsn = i;
+            adapter->sessions[i].state = SESSION_ESTABLISHED;
+        }
+        else
+        {
+            adapter->sessions[i].inUse = FALSE;
+            adapter->sessions[i].state = 0;
+        }
+    }
+    TRACE("returning 0x%02x\n", ret);
+    return ret;
+}
+
+static UCHAR nbSend(NetBIOSAdapter *adapter, PNCB ncb)
+{
+    UCHAR ret;
+    NetBIOSSession *session;
+
+    if (!adapter) return NRC_BRIDGE;
+    if (!adapter->transport->send) return NRC_ILLCMD;
+    if (!ncb) return NRC_INVADDRESS;
+    if (ncb->ncb_lsn >= adapter->sessionsLen) return NRC_SNUMOUT;
+    if (!adapter->sessions[ncb->ncb_lsn].inUse) return NRC_SNUMOUT;
+    if (!ncb->ncb_buffer) return NRC_BADDR;
+
+    session = &adapter->sessions[ncb->ncb_lsn];
+    if (session->state != SESSION_ESTABLISHED)
+        ret = NRC_SNUMOUT;
+    else
+        ret = adapter->transport->send(adapter->impl.data, session->data, ncb);
+    return ret;
+}
+
+static UCHAR nbRecv(NetBIOSAdapter *adapter, PNCB ncb)
+{
+    UCHAR ret;
+    NetBIOSSession *session;
+
+    if (!adapter) return NRC_BRIDGE;
+    if (!adapter->transport->recv) return NRC_ILLCMD;
+    if (!ncb) return NRC_INVADDRESS;
+    if (ncb->ncb_lsn >= adapter->sessionsLen) return NRC_SNUMOUT;
+    if (!adapter->sessions[ncb->ncb_lsn].inUse) return NRC_SNUMOUT;
+    if (!ncb->ncb_buffer) return NRC_BADDR;
+
+    session = &adapter->sessions[ncb->ncb_lsn];
+    if (session->state != SESSION_ESTABLISHED)
+        ret = NRC_SNUMOUT;
+    else
+        ret = adapter->transport->recv(adapter->impl.data, session->data, ncb);
+    return ret;
+}
+
+static UCHAR nbInternalHangup(NetBIOSAdapter *adapter, NetBIOSSession *session)
+{
+    UCHAR ret;
+
+    if (!adapter) return NRC_BRIDGE;
+    if (!session) return NRC_SNUMOUT;
+
+    if (adapter->transport->hangup)
+        ret = adapter->transport->hangup(adapter->impl.data, session->data);
+    else
+        ret = NRC_ILLCMD;
+    EnterCriticalSection(&adapter->cs);
+    memset(session, 0, sizeof(NetBIOSSession));
+    LeaveCriticalSection(&adapter->cs);
+    return NRC_GOODRET;
+}
+
+static UCHAR nbHangup(NetBIOSAdapter *adapter, PNCB ncb)
+{
+    UCHAR ret;
+    NetBIOSSession *session;
+
+    if (!adapter) return NRC_BRIDGE;
+    if (!ncb) return NRC_INVADDRESS;
+    if (ncb->ncb_lsn >= adapter->sessionsLen) return NRC_SNUMOUT;
+    if (!adapter->sessions[ncb->ncb_lsn].inUse) return NRC_SNUMOUT;
+
+    session = &adapter->sessions[ncb->ncb_lsn];
+    if (session->state != SESSION_ESTABLISHED)
+        ret = NRC_SNUMOUT;
+    else
+    {
+        session->state = HANGUP_PENDING;
+        ret = nbInternalHangup(adapter, session);
+    }
+    return ret;
+}
+
+void NetBIOSHangupSession(PNCB ncb)
+{
+    NetBIOSAdapter *adapter;
+
+    if (!ncb) return;
+
+    adapter = nbGetAdapter(ncb->ncb_lana_num);
+    if (adapter)
+    {
+        if (ncb->ncb_lsn < adapter->sessionsLen &&
+         adapter->sessions[ncb->ncb_lsn].inUse)
+            nbHangup(adapter, ncb);
+    }
+}
+
+static UCHAR nbAStat(NetBIOSAdapter *adapter, PNCB ncb)
+{
+    UCHAR ret;
+
+    if (!adapter) return NRC_BRIDGE;
+    if (!adapter->transport->astat) return NRC_ILLCMD;
+    if (!ncb) return NRC_INVADDRESS;
+    if (!ncb->ncb_buffer) return NRC_BADDR;
+    if (ncb->ncb_length < sizeof(ADAPTER_STATUS)) return NRC_BUFLEN;
+
+    ret = adapter->transport->astat(adapter->impl.data, ncb);
+    if (ncb->ncb_callname[0] == '*')
+    {
+        PADAPTER_STATUS astat = (PADAPTER_STATUS)ncb->ncb_buffer;
+
+        astat->max_sess = astat->max_cfg_sess = adapter->sessionsLen;
+    }
+    return ret;
+}
+
+static UCHAR nbDispatch(NetBIOSAdapter *adapter, PNCB ncb)
+{
+    UCHAR ret, cmd;
+
+    TRACE(": adapter %p, ncb %p\n", adapter, ncb);
+
+    if (!adapter) return NRC_BRIDGE;
+    if (!ncb) return NRC_INVADDRESS;
+
+    cmd = ncb->ncb_command & 0x7f;
+    if (cmd == NCBRESET)
+        ret = nbReset(adapter, ncb);
+    else
+    {
+        ret = NBCmdQueueAdd(adapter->cmdQueue, ncb);
+        if (ret == NRC_GOODRET)
+        {
+            switch (cmd)
+            {
+                case NCBCALL:
+                    ret = nbCall(adapter, ncb);
+                    break;
+
+                /* WinNT doesn't chain sends, it always sends immediately.
+                 * Doubt there's any real significance to the NA variants.
+                 */
+                case NCBSEND:
+                case NCBSENDNA:
+                case NCBCHAINSEND:
+                case NCBCHAINSENDNA:
+                    ret = nbSend(adapter, ncb);
+                    break;
+
+                case NCBRECV:
+                    ret = nbRecv(adapter, ncb);
+                    break;
+
+                case NCBHANGUP:
+                    ret = nbHangup(adapter, ncb);
+                    break;
+
+                case NCBASTAT:
+                    ret = nbAStat(adapter, ncb);
+                    break;
+
+                case NCBFINDNAME:
+                    if (adapter->transport->findName)
+                        ret = adapter->transport->findName(adapter->impl.data,
+                         ncb);
+                    else
+                        ret = NRC_ILLCMD;
+                    break;
+
+                default:
+                    FIXME("(%p): command code 0x%02x\n", ncb, ncb->ncb_command);
+                    ret = NRC_ILLCMD;
+            }
+            NBCmdQueueComplete(adapter->cmdQueue, ncb, ret);
+        }
+    }
+    TRACE("returning 0x%02x\n", ret);
+    return ret;
+}
+
+static DWORD WINAPI nbCmdThread(LPVOID lpVoid)
+{
+    PNCB ncb = (PNCB)lpVoid;
+
+    if (ncb)
+    {
+        UCHAR ret;
+        NetBIOSAdapter *adapter = nbGetAdapter(ncb->ncb_lana_num);
+
+        if (adapter)
+            ret = nbDispatch(adapter, ncb);
+        else
+            ret = NRC_BRIDGE;
+        ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret;
+        if (ncb->ncb_post)
+            ncb->ncb_post(ncb);
+        else if (ncb->ncb_event)
+            SetEvent(ncb->ncb_event);
+    }
+    return 0;
+}
+
+UCHAR WINAPI Netbios(PNCB ncb)
+{
+    UCHAR ret, cmd;
+
+    TRACE("ncb = %p\n", ncb);
+
+    if (!ncb) return NRC_INVADDRESS;
+
+    TRACE("ncb_command 0x%02x, ncb_lana_num %d, ncb_buffer %p, ncb_length %d\n",
+     ncb->ncb_command, ncb->ncb_lana_num, ncb->ncb_buffer, ncb->ncb_length);
+    cmd = ncb->ncb_command & 0x7f;
+
+    if (cmd == NCBENUM)
+        ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret = nbEnum(ncb);
+    else if (cmd == NCBADDNAME)
+    {
+        FIXME("NCBADDNAME: stub, returning success");
+        ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret = NRC_GOODRET;
+    }
+    else
+    {
+        NetBIOSAdapter *adapter;
+
+        /* Apps not specifically written for WinNT won't do an NCBENUM first,
+         * so make sure the table has been enumerated at least once
+         */
+        if (!gNBTable.enumerated)
+            nbInternalEnum();
+        adapter = nbGetAdapter(ncb->ncb_lana_num);
+        if (!adapter)
+            ret = NRC_BRIDGE;
+        else
+        {
+            if (adapter->shuttingDown)
+                ret = NRC_IFBUSY;
+            else if (adapter->resetting)
+                ret = NRC_TOOMANY;
+            else
+            {
+                /* non-asynch commands first */
+                if (cmd == NCBCANCEL)
+                    ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret =
+                     nbCancel(adapter, ncb);
+                else if (cmd == NCBSSTAT)
+                    ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret =
+                     nbSStat(adapter, ncb);
+                else
+                {
+                    if (ncb->ncb_command & ASYNCH)
+                    {
+                        HANDLE thread = CreateThread(NULL, 0, nbCmdThread, ncb,
+                         CREATE_SUSPENDED, NULL);
+
+                        if (thread != NULL)
+                        {
+                            ncb->ncb_retcode = ncb->ncb_cmd_cplt = NRC_PENDING;
+                            if (ncb->ncb_event)
+                                ResetEvent(ncb->ncb_event);
+                            ResumeThread(thread);
+                            CloseHandle(thread);
+                            ret = NRC_GOODRET;
+                        }
+                        else
+                        ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret =
+                         NRC_OSRESNOTAV;
+                    }
+                    else
+                        ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret =
+                         nbDispatch(adapter, ncb);
+                }
+            }
+        }
+    }
+    TRACE("returning 0x%02x\n", ret);
+    return ret;
+}
diff --git a/reactos/dll/netapi32/netbios.h b/reactos/dll/netapi32/netbios.h
new file mode 100644 (file)
index 0000000..0130aaf
--- /dev/null
@@ -0,0 +1,183 @@
+/* Copyright (c) 2003 Juan Lang
+ *
+ * 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
+ */
+#ifndef __WINE_NETBIOS_H__
+#define __WINE_NETBIOS_H__
+
+#include <stdarg.h>
+#include "windef.h"
+#include "winbase.h"
+#include "lm.h"
+#include "nb30.h"
+
+/* This file describes the interface WINE's NetBIOS implementation uses to
+ * interact with a transport implementation (where a transport might be
+ * NetBIOS-over-TCP/IP (aka NetBT, NBT), NetBIOS-over-IPX, etc.)
+ */
+
+/**
+ * Public functions
+ */
+
+void NetBIOSInit(void);
+void NetBIOSShutdown(void);
+
+struct _NetBIOSTransport;
+
+/* A transport should register itself during its init function (see below) with
+ * a unique id (the transport_id of ACTION_HEADER, for example) and an
+ * implementation.  Returns TRUE on success, and FALSE on failure.
+ */
+BOOL NetBIOSRegisterTransport(ULONG id, struct _NetBIOSTransport *transport);
+
+/* Registers an adapter with the given transport and ifIndex with NetBIOS.
+ * ifIndex is an interface index usable by the IpHlpApi.  ifIndex is not
+ * required to be unique, but is required so that NetWkstaTransportEnum can use
+ * GetIfEntry to get the name and hardware address of the adapter.
+ * Returns TRUE on success, FALSE on failure.
+ * FIXME: need functions for retrieving the name and hardware index, rather
+ * than assuming a correlation with IpHlpApi.
+ */
+BOOL NetBIOSRegisterAdapter(ULONG transport, DWORD ifIndex, void *adapter);
+
+/* During enumeration, all adapters from your transport are disabled
+ * internally.  If an adapter is still valid, reenable it with this function.
+ * Adapters you don't enable will have their transport's NetBIOSCleanupAdapter
+ * function (see below) called on them, and will be removed from the table.
+ * (This is to deal with lack of plug-and-play--sorry.)
+ */
+void NetBIOSEnableAdapter(UCHAR lana);
+
+/* Gets a quick count of the number of NetBIOS adapters.  Not guaranteed not
+ * to change from one call to the next, depending on what's been enumerated
+ * lately.  See also NetBIOSEnumAdapters.
+ */
+UCHAR NetBIOSNumAdapters(void);
+
+typedef struct _NetBIOSAdapterImpl {
+    UCHAR lana;
+    DWORD ifIndex;
+    void *data;
+} NetBIOSAdapterImpl;
+
+typedef BOOL (*NetBIOSEnumAdaptersCallback)(UCHAR totalLANAs, UCHAR lanaIndex,
+ ULONG transport, const NetBIOSAdapterImpl *data, void *closure);
+
+/* Enumerates all NetBIOS adapters for the transport transport, or for all
+ * transports if transport is ALL_TRANSPORTS.  Your callback will be called
+ * once for every enumerated adapter, with a count of how many adapters have
+ * been enumerated, a 0-based index relative to that count, the adapter's
+ * transport, and its ifIndex.
+ * Your callback should return FALSE if it no longer wishes to be called.
+ */
+void NetBIOSEnumAdapters(ULONG transport, NetBIOSEnumAdaptersCallback cb,
+ void *closure);
+
+/* Hangs up the session identified in the NCB; the NCB need not be a NCBHANGUP.
+ * Will result in the transport's hangup function being called, so release any
+ * locks you own before calling to avoid deadlock.
+ * This function is intended for use by a transport, if the session is closed
+ * by some error in the transport layer.
+ */
+void NetBIOSHangupSession(PNCB ncb);
+
+/**
+ * Functions a transport implementation must implement
+ */
+
+/* This function is called to ask a transport implementation to enumerate any
+ * LANAs into the NetBIOS adapter table by:
+ * - calling NetBIOSRegisterAdapter for any new adapters
+ * - calling NetBIOSEnableAdapter for any existing adapters
+ * NetBIOSEnumAdapters (see) may be of use to determine which adapters already
+ * exist.
+ * A transport can assume no other thread is modifying the NetBIOS adapter
+ * table during the lifetime of its NetBIOSEnum function (and, therefore, that
+ * this function won't be called reentrantly).
+ */
+typedef UCHAR (*NetBIOSEnum)(void);
+
+/* A cleanup function for a transport.  This is the last function called on a
+ * transport.
+ */
+typedef void (*NetBIOSCleanup)(void);
+
+/* Adapter functions */
+
+/* Functions with direct mappings to the Netbios interface.  These functions
+ * are expected to be synchronous, although the first four bytes of the
+ * reserved member of the ncb are a cancel flag.  A long-running function
+ * should check whether this is not FALSE from time to time (see the
+ * NCB_CANCELLED macro), and return NRC_CMDCAN if it's been cancelled.  (The
+ * remainder of the NCB's reserved field is, well, reserved.)
+ */
+
+/* Used to see whether the pointer to an NCB has been cancelled.  The NetBIOS
+ * interface designates certain functions as non-cancellable functions, but I
+ * use this flag for all NCBs.  Support it if you can.
+ * FIXME: this isn't enough, need to support an EVENT or some such, because
+ * some calls (recv) will block indefinitely, so a reset, shutdown, etc. will
+ * never occur.
+ */
+#define NCB_CANCELLED(pncb) *(PBOOL)((pncb)->ncb_reserve)
+
+typedef UCHAR (*NetBIOSAstat)(void *adapter, PNCB ncb);
+typedef UCHAR (*NetBIOSFindName)(void *adapter, PNCB ncb);
+
+/* Functions to support the session service */
+
+/* Implement to support the NCBCALL command.  If you need data stored for the
+ * session, return it in *session.  You can clean it up in your NetBIOSHangup
+ * function (see).
+ */
+typedef UCHAR (*NetBIOSCall)(void *adapter, PNCB ncb, void **session);
+typedef UCHAR (*NetBIOSSend)(void *adapter, void *session, PNCB ncb);
+typedef UCHAR (*NetBIOSRecv)(void *adapter, void *session, PNCB ncb);
+typedef UCHAR (*NetBIOSHangup)(void *adapter, void *session);
+
+/* The last function called on an adapter; it is not called reentrantly, and
+ * no new calls will be made on the adapter once this has been entered.  Clean
+ * up any resources allocated for the adapter here.
+ */
+typedef void (*NetBIOSCleanupAdapter)(void *adapter);
+
+typedef struct _NetBIOSTransport
+{
+    NetBIOSEnum           enumerate;
+    NetBIOSAstat          astat;
+    NetBIOSFindName       findName;
+    NetBIOSCall           call;
+    NetBIOSSend           send;
+    NetBIOSRecv           recv;
+    NetBIOSHangup         hangup;
+    NetBIOSCleanupAdapter cleanupAdapter;
+    NetBIOSCleanup        cleanup;
+} NetBIOSTransport;
+
+/* Transport-specific functions.  When adding a transport, add a call to its
+ * init function in netapi32's DllMain.  The transport can do any global
+ * initialization it needs here.  It should call NetBIOSRegisterTransport to
+ * register itself with NetBIOS.
+ */
+
+/* NetBIOS-over-TCP/IP (NetBT) functions */
+
+/* Not defined by MS, so make my own private define: */
+#define TRANSPORT_NBT "MNBT"
+
+void NetBTInit(void);
+
+#endif /* ndef __WINE_NETBIOS_H__ */
diff --git a/reactos/dll/netapi32/wksta.c b/reactos/dll/netapi32/wksta.c
new file mode 100644 (file)
index 0000000..425a989
--- /dev/null
@@ -0,0 +1,565 @@
+/* Copyright 2002 Andriy Palamarchuk
+ * Copyright (c) 2003 Juan Lang
+ *
+ * netapi32 user functions
+ *
+ * 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 "wine/port.h"
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include "ntstatus.h"
+#define WIN32_NO_STATUS
+#include "windef.h"
+#include "winbase.h"
+#include "winsock2.h"
+#include "nb30.h"
+#include "lmcons.h"
+#include "lmapibuf.h"
+#include "lmerr.h"
+#include "lmwksta.h"
+#include "iphlpapi.h"
+#include "winerror.h"
+#include "winreg.h"
+#include "ntsecapi.h"
+#include "netbios.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(netapi32);
+
+/************************************************************
+ *                NETAPI_IsLocalComputer
+ *
+ * Checks whether the server name indicates local machine.
+ */
+BOOL NETAPI_IsLocalComputer(LPCWSTR ServerName)
+{
+    if (!ServerName)
+    {
+        return TRUE;
+    }
+    else if (ServerName[0] == '\0')
+        return TRUE;
+    else
+    {
+        DWORD dwSize = MAX_COMPUTERNAME_LENGTH + 1;
+        BOOL Result;
+        LPWSTR buf;
+
+        NetApiBufferAllocate(dwSize * sizeof(WCHAR), (LPVOID *) &buf);
+        Result = GetComputerNameW(buf,  &dwSize);
+        if (Result && (ServerName[0] == '\\') && (ServerName[1] == '\\'))
+            ServerName += 2;
+        Result = Result && !lstrcmpW(ServerName, buf);
+        NetApiBufferFree(buf);
+
+        return Result;
+    }
+}
+
+static void wprint_mac(WCHAR* buffer, int len, PMIB_IFROW ifRow)
+{
+    int i;
+    unsigned char val;
+
+    if (!buffer)
+        return;
+    if (len < 1)
+        return;
+    if (!ifRow)
+    {
+        *buffer = '\0';
+        return;
+    }
+
+    for (i = 0; i < ifRow->dwPhysAddrLen && 2 * i < len; i++)
+    {
+        val = ifRow->bPhysAddr[i];
+        if ((val >>4) >9)
+            buffer[2*i] = (WCHAR)((val >>4) + 'A' - 10);
+        else
+            buffer[2*i] = (WCHAR)((val >>4) + '0');
+        if ((val & 0xf ) >9)
+            buffer[2*i+1] = (WCHAR)((val & 0xf) + 'A' - 10);
+        else
+            buffer[2*i+1] = (WCHAR)((val & 0xf) + '0');
+    }
+    buffer[2*i]=(WCHAR)0;
+}
+
+/* Theoretically this could be too short, except that MS defines
+ * MAX_ADAPTER_NAME as 128, and MAX_INTERFACE_NAME_LEN as 256, and both
+ * represent a count of WCHARs, so even with an extroardinarily long header
+ * this will be plenty
+ */
+#define MAX_TRANSPORT_NAME MAX_INTERFACE_NAME_LEN
+#define MAX_TRANSPORT_ADDR 13
+
+#define NBT_TRANSPORT_NAME_HEADER "\\Device\\NetBT_Tcpip_"
+#define UNKNOWN_TRANSPORT_NAME_HEADER "\\Device\\UnknownTransport_"
+
+static void wprint_name(WCHAR *buffer, int len, ULONG transport,
+ PMIB_IFROW ifRow)
+{
+    WCHAR *ptr1, *ptr2;
+    const char *name;
+
+    if (!buffer)
+        return;
+    if (!ifRow)
+    {
+        *buffer = '\0';
+        return;
+    }
+
+    if (!memcmp(&transport, TRANSPORT_NBT, sizeof(ULONG)))
+        name = NBT_TRANSPORT_NAME_HEADER;
+    else
+        name = UNKNOWN_TRANSPORT_NAME_HEADER;
+
+    for (ptr1 = buffer; *name && ptr1 < buffer + len; ptr1++, name++)
+        *ptr1 = *name;
+    for (ptr2 = ifRow->wszName; *ptr2 && ptr1 < buffer + len; ptr1++, ptr2++)
+        *ptr1 = *ptr2;
+    *ptr1 = '\0';
+}
+
+/***********************************************************************
+ *                NetWkstaTransportEnum  (NETAPI32.@)
+ */
+struct WkstaTransportEnumData
+{
+    UCHAR          n_adapt;
+    UCHAR          n_read;
+    DWORD          prefmaxlen;
+    LPBYTE        *pbuf;
+    NET_API_STATUS ret;
+};
+
+/**********************************************************************/
+
+static BOOL WkstaEnumAdaptersCallback(UCHAR totalLANAs, UCHAR lanaIndex,
+ ULONG transport, const NetBIOSAdapterImpl *data, void *closure)
+{
+    BOOL ret;
+    struct WkstaTransportEnumData *enumData = (struct WkstaTransportEnumData *)
+     closure;
+
+    if (enumData && enumData->pbuf)
+    {
+        if (lanaIndex == 0)
+        {
+            DWORD toAllocate;
+
+            enumData->n_adapt = totalLANAs;
+            enumData->n_read = 0;
+
+            toAllocate = totalLANAs * (sizeof(WKSTA_TRANSPORT_INFO_0)
+             + MAX_TRANSPORT_NAME * sizeof(WCHAR) +
+             MAX_TRANSPORT_ADDR * sizeof(WCHAR));
+            if (enumData->prefmaxlen != MAX_PREFERRED_LENGTH)
+                toAllocate = enumData->prefmaxlen;
+            NetApiBufferAllocate(toAllocate, (LPVOID *)enumData->pbuf);
+        }
+        if (*(enumData->pbuf))
+        {
+            UCHAR spaceFor;
+
+            if (enumData->prefmaxlen == MAX_PREFERRED_LENGTH)
+                spaceFor = totalLANAs;
+            else
+                spaceFor = enumData->prefmaxlen /
+                 (sizeof(WKSTA_TRANSPORT_INFO_0) + (MAX_TRANSPORT_NAME +
+                 MAX_TRANSPORT_ADDR) * sizeof(WCHAR));
+            if (enumData->n_read < spaceFor)
+            {
+                PWKSTA_TRANSPORT_INFO_0 ti;
+                LPWSTR transport_name, transport_addr;
+                MIB_IFROW ifRow;
+
+                ti = (PWKSTA_TRANSPORT_INFO_0)(*(enumData->pbuf) +
+                 enumData->n_read * sizeof(WKSTA_TRANSPORT_INFO_0));
+                transport_name = (LPWSTR)(*(enumData->pbuf) +
+                 totalLANAs * sizeof(WKSTA_TRANSPORT_INFO_0) +
+                 enumData->n_read * MAX_TRANSPORT_NAME * sizeof(WCHAR));
+                transport_addr = (LPWSTR)(*(enumData->pbuf) +
+                 totalLANAs * (sizeof(WKSTA_TRANSPORT_INFO_0) +
+                 MAX_TRANSPORT_NAME * sizeof(WCHAR)) +
+                 enumData->n_read * MAX_TRANSPORT_ADDR * sizeof(WCHAR));
+
+                ifRow.dwIndex = data->ifIndex;
+                GetIfEntry(&ifRow);
+                ti->wkti0_quality_of_service = 0;
+                ti->wkti0_number_of_vcs = 0;
+                ti->wkti0_transport_name = transport_name;
+                wprint_name(ti->wkti0_transport_name, MAX_TRANSPORT_NAME,
+                 transport, &ifRow);
+                ti->wkti0_transport_address = transport_addr;
+                wprint_mac(ti->wkti0_transport_address, MAX_TRANSPORT_ADDR,
+                 &ifRow);
+                if (!memcmp(&transport, TRANSPORT_NBT, sizeof(ULONG)))
+                    ti->wkti0_wan_ish = TRUE;
+                else
+                    ti->wkti0_wan_ish = FALSE;
+                TRACE("%d of %d:ti at %p\n", lanaIndex, totalLANAs, ti);
+                TRACE("transport_name at %p %s\n",
+                 ti->wkti0_transport_name,
+                 debugstr_w(ti->wkti0_transport_name));
+                TRACE("transport_address at %p %s\n",
+                 ti->wkti0_transport_address,
+                 debugstr_w(ti->wkti0_transport_address));
+                enumData->n_read++;
+                enumData->ret = NERR_Success;
+                ret = TRUE;
+            }
+            else
+            {
+                enumData->ret = ERROR_MORE_DATA;
+                ret = FALSE;
+            }
+        }
+        else
+        {
+            enumData->ret = ERROR_OUTOFMEMORY;
+            ret = FALSE;
+        }
+    }
+    else
+        ret = FALSE;
+    return ret;
+}
+
+/**********************************************************************/
+
+NET_API_STATUS WINAPI 
+NetWkstaTransportEnum(LPWSTR ServerName, DWORD level, PBYTE* pbuf,
+      DWORD prefmaxlen, LPDWORD read_entries,
+      PDWORD total_entries, PDWORD hresume)
+{
+    NET_API_STATUS ret;
+
+    TRACE(":%s, 0x%08lx, %p, 0x%08lx, %p, %p, %p\n", debugstr_w(ServerName), 
+     level, pbuf, prefmaxlen, read_entries, total_entries,hresume);
+    if (!NETAPI_IsLocalComputer(ServerName))
+    {
+        FIXME(":not implemented for non-local computers\n");
+        ret = ERROR_INVALID_LEVEL;
+    }
+    else
+    {
+        if (hresume && *hresume)
+        {
+          FIXME(":resume handle not implemented\n");
+          return ERROR_INVALID_LEVEL;
+        }
+
+        switch (level)
+        {
+            case 0: /* transport info */
+            {
+                ULONG allTransports;
+                struct WkstaTransportEnumData enumData;
+
+                if (NetBIOSNumAdapters() == 0)
+                  return ERROR_NETWORK_UNREACHABLE;
+                if (!read_entries)
+                  return STATUS_ACCESS_VIOLATION;
+                if (!total_entries || !pbuf)
+                  return RPC_X_NULL_REF_POINTER;
+
+                enumData.prefmaxlen = prefmaxlen;
+                enumData.pbuf = pbuf;
+                memcpy(&allTransports, ALL_TRANSPORTS, sizeof(ULONG));
+                NetBIOSEnumAdapters(allTransports, WkstaEnumAdaptersCallback,
+                 &enumData);
+                *read_entries = enumData.n_read;
+                *total_entries = enumData.n_adapt;
+                if (hresume) *hresume= 0;
+                ret = enumData.ret;
+                break;
+            }
+            default:
+                ERR("Invalid level %ld is specified\n", level);
+                ret = ERROR_INVALID_LEVEL;
+        }
+    }
+    return ret;
+}
+
+
+/************************************************************
+ *                NetWkstaUserGetInfo  (NETAPI32.@)
+ */
+NET_API_STATUS WINAPI NetWkstaUserGetInfo(LPWSTR reserved, DWORD level,
+                                          PBYTE* bufptr)
+{
+    TRACE("(%s, %ld, %p)\n", debugstr_w(reserved), level, bufptr);
+    switch (level)
+    {
+    case 0:
+    {
+        PWKSTA_USER_INFO_0 ui;
+        DWORD dwSize = UNLEN + 1;
+
+        /* set up buffer */
+        NetApiBufferAllocate(sizeof(WKSTA_USER_INFO_0) + dwSize * sizeof(WCHAR),
+                             (LPVOID *) bufptr);
+
+        ui = (PWKSTA_USER_INFO_0) *bufptr;
+        ui->wkui0_username = (LPWSTR) (*bufptr + sizeof(WKSTA_USER_INFO_0));
+
+        /* get data */
+        if (!GetUserNameW(ui->wkui0_username, &dwSize))
+        {
+            NetApiBufferFree(ui);
+            return ERROR_NOT_ENOUGH_MEMORY;
+        }
+        else
+            NetApiBufferReallocate(
+                *bufptr, sizeof(WKSTA_USER_INFO_0) +
+                (lstrlenW(ui->wkui0_username) + 1) * sizeof(WCHAR),
+                (LPVOID *) bufptr);
+        break;
+    }
+
+    case 1:
+    {
+        PWKSTA_USER_INFO_1 ui;
+        PWKSTA_USER_INFO_0 ui0;
+        DWORD dwSize;
+        LSA_OBJECT_ATTRIBUTES ObjectAttributes;
+        LSA_HANDLE PolicyHandle;
+        PPOLICY_ACCOUNT_DOMAIN_INFO DomainInfo;
+        NTSTATUS NtStatus;
+
+        /* sizes of the field buffers in WCHARS */
+        int username_sz, logon_domain_sz, oth_domains_sz, logon_server_sz;
+
+        FIXME("Level 1 processing is partially implemented\n");
+        oth_domains_sz = 1;
+        logon_server_sz = 1;
+
+        /* get some information first to estimate size of the buffer */
+        ui0 = NULL;
+        NetWkstaUserGetInfo(NULL, 0, (PBYTE *) &ui0);
+        username_sz = lstrlenW(ui0->wkui0_username) + 1;
+
+        ZeroMemory(&ObjectAttributes, sizeof(ObjectAttributes));
+        NtStatus = LsaOpenPolicy(NULL, &ObjectAttributes,
+                                 POLICY_VIEW_LOCAL_INFORMATION,
+                                 &PolicyHandle);
+        if (NtStatus != STATUS_SUCCESS)
+        {
+            ERR("LsaOpenPolicyFailed with NT status %lx\n",
+                LsaNtStatusToWinError(NtStatus));
+            NetApiBufferFree(ui0);
+            return ERROR_NOT_ENOUGH_MEMORY;
+        }
+        LsaQueryInformationPolicy(PolicyHandle, PolicyAccountDomainInformation,
+                                  (PVOID*) &DomainInfo);
+        logon_domain_sz = lstrlenW(DomainInfo->DomainName.Buffer) + 1;
+        LsaClose(PolicyHandle);
+
+        /* set up buffer */
+        NetApiBufferAllocate(sizeof(WKSTA_USER_INFO_1) +
+                             (username_sz + logon_domain_sz +
+                              oth_domains_sz + logon_server_sz) * sizeof(WCHAR),
+                             (LPVOID *) bufptr);
+        ui = (WKSTA_USER_INFO_1 *) *bufptr;
+        ui->wkui1_username = (LPWSTR) (*bufptr + sizeof(WKSTA_USER_INFO_1));
+        ui->wkui1_logon_domain = (LPWSTR) (
+            ((PBYTE) ui->wkui1_username) + username_sz * sizeof(WCHAR));
+        ui->wkui1_oth_domains = (LPWSTR) (
+            ((PBYTE) ui->wkui1_logon_domain) +
+            logon_domain_sz * sizeof(WCHAR));
+        ui->wkui1_logon_server = (LPWSTR) (
+            ((PBYTE) ui->wkui1_oth_domains) +
+            oth_domains_sz * sizeof(WCHAR));
+
+        /* get data */
+        dwSize = username_sz;
+        lstrcpyW(ui->wkui1_username, ui0->wkui0_username);
+        NetApiBufferFree(ui0);
+
+        lstrcpynW(ui->wkui1_logon_domain, DomainInfo->DomainName.Buffer,
+                logon_domain_sz);
+        LsaFreeMemory(DomainInfo);
+
+        /* FIXME. Not implemented. Populated with empty strings */
+        ui->wkui1_oth_domains[0] = 0;
+        ui->wkui1_logon_server[0] = 0;
+        break;
+    }
+    case 1101:
+    {
+        PWKSTA_USER_INFO_1101 ui;
+        DWORD dwSize = 1;
+
+        FIXME("Stub. Level 1101 processing is not implemented\n");
+        /* FIXME see also wkui1_oth_domains for level 1 */
+
+        /* set up buffer */
+        NetApiBufferAllocate(sizeof(WKSTA_USER_INFO_1101) + dwSize * sizeof(WCHAR),
+                             (LPVOID *) bufptr);
+
+        ui = (PWKSTA_USER_INFO_1101) *bufptr;
+        ui->wkui1101_oth_domains = (LPWSTR)(ui + 1);
+
+        /* get data */
+        ui->wkui1101_oth_domains[0] = 0;
+        break;
+    }
+    default:
+        ERR("Invalid level %ld is specified\n", level);
+        return ERROR_INVALID_LEVEL;
+    }
+    return NERR_Success;
+}
+
+/************************************************************
+ *                NetpGetComputerName  (NETAPI32.@)
+ */
+NET_API_STATUS WINAPI NetpGetComputerName(LPWSTR *Buffer)
+{
+    DWORD dwSize = MAX_COMPUTERNAME_LENGTH + 1;
+
+    TRACE("(%p)\n", Buffer);
+    NetApiBufferAllocate(dwSize * sizeof(WCHAR), (LPVOID *) Buffer);
+    if (GetComputerNameW(*Buffer,  &dwSize))
+    {
+        NetApiBufferReallocate(
+            *Buffer, dwSize * sizeof(WCHAR),
+            (LPVOID *) Buffer);
+        return NERR_Success;
+    }
+    else
+    {
+        NetApiBufferFree(*Buffer);
+        return ERROR_NOT_ENOUGH_MEMORY;
+    }
+}
+
+NET_API_STATUS WINAPI I_NetNameCompare(LPVOID p1, LPWSTR wkgrp, LPWSTR comp,
+ LPVOID p4, LPVOID p5)
+{
+    FIXME("(%p %s %s %p %p): stub\n", p1, debugstr_w(wkgrp), debugstr_w(comp),
+     p4, p5);
+    return ERROR_INVALID_PARAMETER;
+}
+
+NET_API_STATUS WINAPI I_NetNameValidate(LPVOID p1, LPWSTR wkgrp, LPVOID p3,
+ LPVOID p4)
+{
+    FIXME("(%p %s %p %p): stub\n", p1, debugstr_w(wkgrp), p3, p4);
+    return ERROR_INVALID_PARAMETER;
+}
+
+NET_API_STATUS WINAPI NetWkstaGetInfo( LPWSTR servername, DWORD level,
+                                       LPBYTE* bufptr)
+{
+    NET_API_STATUS ret;
+
+    TRACE("%s %ld %p\n", debugstr_w( servername ), level, bufptr );
+    if (servername)
+    {
+        if (!NETAPI_IsLocalComputer(servername))
+        {
+            FIXME("remote computers not supported\n");
+            return ERROR_INVALID_LEVEL;
+        }
+    }
+    if (!bufptr) return ERROR_INVALID_PARAMETER;
+
+    switch (level)
+    {
+        case 100:
+        {
+            DWORD computerNameLen, domainNameLen, size;
+            WCHAR computerName[MAX_COMPUTERNAME_LENGTH + 1];
+            LSA_OBJECT_ATTRIBUTES ObjectAttributes;
+            LSA_HANDLE PolicyHandle;
+            NTSTATUS NtStatus;
+           
+            computerNameLen = MAX_COMPUTERNAME_LENGTH + 1;
+            GetComputerNameW(computerName, &computerNameLen);
+            computerNameLen++; /* include NULL terminator */
+
+            ZeroMemory(&ObjectAttributes, sizeof(ObjectAttributes));
+            NtStatus = LsaOpenPolicy(NULL, &ObjectAttributes,
+             POLICY_VIEW_LOCAL_INFORMATION, &PolicyHandle);
+            if (NtStatus != STATUS_SUCCESS)
+                ret = LsaNtStatusToWinError(NtStatus);
+            else
+            {
+                PPOLICY_ACCOUNT_DOMAIN_INFO DomainInfo;
+
+                LsaQueryInformationPolicy(PolicyHandle,
+                 PolicyAccountDomainInformation, (PVOID*)&DomainInfo);
+                domainNameLen = lstrlenW(DomainInfo->DomainName.Buffer) + 1;
+                size = sizeof(WKSTA_INFO_100) + computerNameLen * sizeof(WCHAR)
+                 + domainNameLen * sizeof(WCHAR);
+                ret = NetApiBufferAllocate(size, (LPVOID *)bufptr);
+                if (ret == NERR_Success)
+                {
+                    PWKSTA_INFO_100 info = (PWKSTA_INFO_100)*bufptr;
+                    OSVERSIONINFOW verInfo;
+
+                    info->wki100_platform_id = PLATFORM_ID_NT;
+                    info->wki100_computername = (LPWSTR)(*bufptr +
+                     sizeof(WKSTA_INFO_100));
+                    memcpy(info->wki100_computername, computerName,
+                     computerNameLen * sizeof(WCHAR));
+                    info->wki100_langroup = (LPWSTR)(*bufptr +
+                     sizeof(WKSTA_INFO_100) + computerNameLen * sizeof(WCHAR));
+                    memcpy(info->wki100_langroup, DomainInfo->DomainName.Buffer,
+                     domainNameLen * sizeof(WCHAR));
+                    memset(&verInfo, 0, sizeof(verInfo));
+                    verInfo.dwOSVersionInfoSize = sizeof(verInfo);
+                    GetVersionExW(&verInfo);
+                    info->wki100_ver_major = verInfo.dwMajorVersion;
+                    info->wki100_ver_minor = verInfo.dwMinorVersion;
+                }
+                LsaFreeMemory(DomainInfo);
+                LsaClose(PolicyHandle);
+            }
+            break;
+        }
+
+        default:
+            FIXME("level %ld unimplemented\n", level);
+            ret = ERROR_INVALID_LEVEL;
+    }
+    return ret;
+}
+
+/************************************************************
+ *                NetGetJoinInformation (NETAPI32.@)
+ */
+NET_API_STATUS NET_API_FUNCTION NetGetJoinInformation(
+    LPCWSTR Server,
+    LPWSTR *Name,
+    PNETSETUP_JOIN_STATUS type)
+{
+    FIXME("Stub %s %p %p\n", wine_dbgstr_w(Server), Name, type);
+
+    *Name = NULL;
+    *type = NetSetupUnknownStatus;
+
+    return NERR_Success;
+}
diff --git a/reactos/dll/netcfgx/netcfgx.c b/reactos/dll/netcfgx/netcfgx.c
new file mode 100644 (file)
index 0000000..22cb4e0
--- /dev/null
@@ -0,0 +1,376 @@
+/*
+ * COPYRIGHT:       See COPYING in the top level directory
+ * PROJECT:         ReactOS Configuration of networkd devices
+ * FILE:            lib/netcfgx/netcfgx.c
+ * PURPOSE:         Network devices installer
+ *
+ * PROGRAMMERS:     Hervé Poussineau (hpoussin@reactos.org)
+ */
+
+#define NDEBUG
+#include <debug.h>
+
+#include "netcfgx.h"
+
+/* Append a REG_SZ to an existing REG_MULTI_SZ string in the registry.
+ * If the value doesn't exist, create it.
+ * Returns ERROR_SUCCESS if success. Othewise, returns an error code
+ */
+static LONG
+AppendStringToMultiSZ(
+       IN HKEY hKey,
+       IN PCWSTR ValueName,
+       IN PCWSTR ValueToAppend)
+{
+       PWSTR Buffer = NULL;
+       DWORD dwRegType;
+       DWORD dwRequired, dwLength;
+       DWORD dwTmp;
+       LONG rc;
+
+       rc = RegQueryValueExW(
+               hKey,
+               ValueName,
+               NULL,
+               &dwRegType,
+               NULL,
+               &dwRequired);
+       if (rc != ERROR_FILE_NOT_FOUND)
+       {
+               if (rc != ERROR_SUCCESS)
+                       goto cleanup;
+               if (dwRegType != REG_MULTI_SZ)
+               {
+                       rc = ERROR_GEN_FAILURE;
+                       goto cleanup;
+               }
+
+               dwTmp = dwLength = dwRequired + wcslen(ValueToAppend) * sizeof(WCHAR) + sizeof(UNICODE_NULL);
+               Buffer = HeapAlloc(GetProcessHeap(), 0, dwLength);
+               if (!Buffer)
+               {
+                       rc = ERROR_NOT_ENOUGH_MEMORY;
+                       goto cleanup;
+               }
+               rc = RegQueryValueExW(
+                       hKey,
+                       ValueName,
+                       NULL,
+                       NULL,
+                       (BYTE*)Buffer,
+                       &dwTmp);
+               if (rc != ERROR_SUCCESS)
+                       goto cleanup;
+       }
+       else
+       {
+               dwRequired = sizeof(WCHAR);
+               dwLength = wcslen(ValueToAppend) * sizeof(WCHAR) + 2 * sizeof(UNICODE_NULL);
+               Buffer = HeapAlloc(GetProcessHeap(), 0, dwLength);
+               if (!Buffer)
+               {
+                       rc = ERROR_NOT_ENOUGH_MEMORY;
+                       goto cleanup;
+               }
+       }
+
+       /* Append the value */
+       wcscpy(&Buffer[dwRequired / sizeof(WCHAR) - 1], ValueToAppend);
+       /* Terminate the REG_MULTI_SZ string */
+       Buffer[dwLength / sizeof(WCHAR) - 1] = UNICODE_NULL;
+
+       rc = RegSetValueExW(
+               hKey,
+               ValueName,
+               0,
+               REG_MULTI_SZ,
+               (const BYTE*)Buffer,
+               dwLength);
+
+cleanup:
+       HeapFree(GetProcessHeap(), 0, Buffer);
+       return rc;
+}
+
+DWORD WINAPI
+NetClassInstaller(
+       IN DI_FUNCTION InstallFunction,
+       IN HDEVINFO DeviceInfoSet,
+       IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL)
+{
+       RPC_STATUS RpcStatus;
+       UUID Uuid;
+       LPWSTR InstanceId = NULL;
+       LPWSTR UuidRpcString = NULL;
+       LPWSTR UuidString = NULL;
+       LPWSTR DeviceName = NULL;
+       LPWSTR ExportName = NULL;
+       LONG rc;
+       DWORD dwShowIcon, dwLength;
+       HKEY hKey = INVALID_HANDLE_VALUE;
+       HKEY hLinkageKey = INVALID_HANDLE_VALUE;
+       HKEY hNetworkKey = INVALID_HANDLE_VALUE;
+       HKEY hConnectionKey = INVALID_HANDLE_VALUE;
+       
+       if (InstallFunction != DIF_INSTALLDEVICE)
+               return ERROR_DI_DO_DEFAULT;
+
+       DPRINT("%lu %p %p\n", InstallFunction, DeviceInfoSet, DeviceInfoData);
+
+       /* Get Instance ID */
+       if (SetupDiGetDeviceInstanceIdW(DeviceInfoSet, DeviceInfoData, NULL, 0, &dwLength))
+       {
+               DPRINT("SetupDiGetDeviceInstanceIdW() returned TRUE. FALSE expected\n");
+               rc = ERROR_GEN_FAILURE;
+               goto cleanup;
+       }
+       InstanceId = HeapAlloc(GetProcessHeap(), 0, dwLength);
+       if (!InstanceId)
+       {
+               DPRINT("HeapAlloc() failed\n");
+               rc = ERROR_NOT_ENOUGH_MEMORY;
+               goto cleanup;
+       }
+       if (!SetupDiGetDeviceInstanceIdW(DeviceInfoSet, DeviceInfoData, InstanceId, dwLength, NULL))
+       {
+               rc = GetLastError();
+               DPRINT("SetupDiGetDeviceInstanceIdW() failed with error 0x%lx\n", rc);
+               goto cleanup;
+       }
+
+       /* Create a new UUID */
+       RpcStatus = UuidCreate(&Uuid);
+       if (RpcStatus != RPC_S_OK && RpcStatus != RPC_S_UUID_LOCAL_ONLY)
+       {
+               DPRINT("UuidCreate() failed with RPC status 0x%lx\n", RpcStatus);
+               rc = ERROR_GEN_FAILURE;
+               goto cleanup;
+       }
+       RpcStatus = UuidToStringW(&Uuid, &UuidRpcString);
+       if (RpcStatus != RPC_S_OK)
+       {
+               DPRINT("UuidToStringW() failed with RPC status 0x%lx\n", RpcStatus);
+               rc = ERROR_GEN_FAILURE;
+               goto cleanup;
+       }
+
+       /* Add curly braces around Uuid */
+       UuidString = HeapAlloc(GetProcessHeap(), 0, (2 + wcslen(UuidRpcString)) * sizeof(WCHAR) + sizeof(UNICODE_NULL));
+       if (!UuidString)
+       {
+               DPRINT("HeapAlloc() failed\n");
+               rc = ERROR_NOT_ENOUGH_MEMORY;
+               goto cleanup;
+       }
+       wcscpy(UuidString, L"{");
+       wcscat(UuidString, UuidRpcString);
+       wcscat(UuidString, L"}");
+
+       /* Create device name */
+       DeviceName = HeapAlloc(GetProcessHeap(), 0, (wcslen(L"\\Device\\") + wcslen(UuidString)) * sizeof(WCHAR) + sizeof(UNICODE_NULL));
+       if (!DeviceName)
+       {
+               DPRINT("HeapAlloc() failed\n");
+               rc = ERROR_NOT_ENOUGH_MEMORY;
+               goto cleanup;
+       }
+       wcscpy(DeviceName, L"\\Device\\");
+       wcscat(DeviceName, UuidString);
+       
+       /* Create export name */
+       ExportName = HeapAlloc(GetProcessHeap(), 0, (wcslen(L"\\Device\\Tcpip_") + wcslen(UuidString)) * sizeof(WCHAR) + sizeof(UNICODE_NULL));
+       if (!ExportName)
+       {
+               DPRINT("HeapAlloc() failed\n");
+               rc = ERROR_NOT_ENOUGH_MEMORY;
+               goto cleanup;
+       }
+       wcscpy(ExportName, L"\\Device\\Tcpip_");
+       wcscat(ExportName, UuidString);
+
+       /* Write Tcpip parameters in new service Key */
+       rc = RegCreateKeyExW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Services", 0, NULL, REG_OPTION_NON_VOLATILE, 0, NULL, &hKey, NULL);
+       if (rc != ERROR_SUCCESS)
+       {
+               DPRINT("RegCreateKeyExW() failed with error 0x%lx\n", rc);
+               goto cleanup;
+       }
+       rc = RegCreateKeyExW(hKey, UuidString, 0, NULL, REG_OPTION_NON_VOLATILE, 0, NULL, &hNetworkKey, NULL);
+       if (rc != ERROR_SUCCESS)
+       {
+               DPRINT("RegCreateKeyExW() failed with error 0x%lx\n", rc);
+               goto cleanup;
+       }
+       RegCloseKey(hKey);
+       hKey = INVALID_HANDLE_VALUE;
+       rc = RegCreateKeyExW(hNetworkKey, L"Parameters\\Tcpip", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, &hKey, NULL);
+       if (rc != ERROR_SUCCESS)
+       {
+               DPRINT("RegCreateKeyExW() failed with error 0x%lx\n", rc);
+               goto cleanup;
+       }
+       RegCloseKey(hNetworkKey);
+       hNetworkKey = INVALID_HANDLE_VALUE;
+       rc = RegSetValueExW(hKey, L"DefaultGateway", 0, REG_SZ, (const BYTE*)L"0.0.0.0", (wcslen(L"0.0.0.0") + 1) * sizeof(WCHAR));
+       if (rc != ERROR_SUCCESS)
+       {
+               DPRINT("RegSetValueExW() failed with error 0x%lx\n", rc);
+               goto cleanup;
+       }
+       rc = RegSetValueExW(hKey, L"IPAddress", 0, REG_SZ, (const BYTE*)L"0.0.0.0", (wcslen(L"0.0.0.0") + 1) * sizeof(WCHAR));
+       if (rc != ERROR_SUCCESS)
+       {
+               DPRINT("RegSetValueExW() failed with error 0x%lx\n", rc);
+               goto cleanup;
+       }
+       rc = RegSetValueExW(hKey, L"SubnetMask", 0, REG_SZ, (const BYTE*)L"0.0.0.0", (wcslen(L"0.0.0.0") + 1) * sizeof(WCHAR));
+       if (rc != ERROR_SUCCESS)
+       {
+               DPRINT("RegSetValueExW() failed with error 0x%lx\n", rc);
+               goto cleanup;
+       }
+       RegCloseKey(hKey);
+       hKey = INVALID_HANDLE_VALUE;
+
+       /* Write 'Linkage' key in hardware key */
+#if _WIN32_WINNT >= 0x502
+       hKey = SetupDiOpenDevRegKey(DeviceInfoSet, DeviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_READ | KEY_WRITE);
+#else
+       hKey = SetupDiOpenDevRegKey(DeviceInfoSet, DeviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_ALL_ACCESS);
+#endif
+       if (hKey == INVALID_HANDLE_VALUE && GetLastError() == ERROR_FILE_NOT_FOUND)
+               hKey = SetupDiCreateDevRegKeyW(DeviceInfoSet, DeviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DRV, NULL, NULL);
+       if (hKey == INVALID_HANDLE_VALUE)
+       {
+               rc = GetLastError();
+               DPRINT("SetupDiCreateDevRegKeyW() failed with error 0x%lx\n", rc);
+               goto cleanup;
+       }
+       rc = RegSetValueExW(hKey, L"NetCfgInstanceId", 0, REG_SZ, (const BYTE*)UuidString, (wcslen(UuidString) + 1) * sizeof(WCHAR));
+       if (rc != ERROR_SUCCESS)
+       {
+               DPRINT("RegSetValueExW() failed with error 0x%lx\n", rc);
+               goto cleanup;
+       }
+       rc = RegCreateKeyExW(hKey, L"Linkage", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, &hLinkageKey, NULL);
+       if (rc != ERROR_SUCCESS)
+       {
+               DPRINT("RegCreateKeyExW() failed with error 0x%lx\n", rc);
+               goto cleanup;
+       }
+       rc = RegSetValueExW(hLinkageKey, L"Export", 0, REG_SZ, (const BYTE*)DeviceName, (wcslen(DeviceName) + 1) * sizeof(WCHAR));
+       if (rc != ERROR_SUCCESS)
+       {
+               DPRINT("RegSetValueExW() failed with error 0x%lx\n", rc);
+               goto cleanup;
+       }
+       rc = RegSetValueExW(hLinkageKey, L"RootDevice", 0, REG_SZ, (const BYTE*)UuidString, (wcslen(UuidString) + 1) * sizeof(WCHAR));
+       if (rc != ERROR_SUCCESS)
+       {
+               DPRINT("RegSetValueExW() failed with error 0x%lx\n", rc);
+               goto cleanup;
+       }
+       rc = RegSetValueExW(hLinkageKey, L"UpperBind", 0, REG_SZ, (const BYTE*)L"Tcpip", (wcslen(L"Tcpip") + 1) * sizeof(WCHAR));
+       if (rc != ERROR_SUCCESS)
+       {
+               DPRINT("RegSetValueExW() failed with error 0x%lx\n", rc);
+               goto cleanup;
+       }
+       RegCloseKey(hKey);
+       hKey = INVALID_HANDLE_VALUE;
+
+       /* Write connection information in network subkey */
+       rc = RegCreateKeyExW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}", 0, NULL, REG_OPTION_NON_VOLATILE, 0, NULL, &hNetworkKey, NULL);
+       if (rc != ERROR_SUCCESS)
+       {
+               DPRINT("RegCreateKeyExW() failed with error 0x%lx\n", rc);
+               goto cleanup;
+       }
+       rc = RegCreateKeyExW(hNetworkKey, UuidString, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_CREATE_SUB_KEY, NULL, &hKey, NULL);
+       if (rc != ERROR_SUCCESS)
+       {
+               DPRINT("RegCreateKeyExW() failed with error 0x%lx\n", rc);
+               goto cleanup;
+       }
+       rc = RegCreateKeyExW(hKey, L"Connection", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, &hConnectionKey, NULL);
+       RegCloseKey(hKey);
+       hKey = INVALID_HANDLE_VALUE;
+       if (rc != ERROR_SUCCESS)
+       {
+               DPRINT("RegCreateKeyExW() failed with error 0x%lx\n", rc);
+               goto cleanup;
+       }
+       rc = RegSetValueExW(hConnectionKey, L"Name", 0, REG_SZ, (const BYTE*)L"Network connection", (wcslen(L"Network connection") + 1) * sizeof(WCHAR));
+       if (rc != ERROR_SUCCESS)
+       {
+               DPRINT("RegSetValueExW() failed with error 0x%lx\n", rc);
+               goto cleanup;
+       }
+       rc = RegSetValueExW(hConnectionKey, L"PnpInstanceId", 0, REG_SZ, (const BYTE*)InstanceId, (wcslen(InstanceId) + 1) * sizeof(WCHAR));
+       if (rc != ERROR_SUCCESS)
+       {
+               DPRINT("RegSetValueExW() failed with error 0x%lx\n", rc);
+               goto cleanup;
+       }
+       dwShowIcon = 1;
+       rc = RegSetValueExW(hConnectionKey, L"ShowIcon", 0, REG_DWORD, (const BYTE*)&dwShowIcon, sizeof(dwShowIcon));
+       if (rc != ERROR_SUCCESS)
+       {
+               DPRINT("RegSetValueExW() failed with error 0x%lx\n", rc);
+               goto cleanup;
+       }
+
+       /* Write linkage information in Tcpip service */
+       rc = RegCreateKeyExW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Linkage", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_QUERY_VALUE | KEY_SET_VALUE, NULL, &hKey, NULL);
+       if (rc != ERROR_SUCCESS)
+       {
+               DPRINT("RegCreateKeyExW() failed with error 0x%lx\n", rc);
+               goto cleanup;
+       }
+       rc = AppendStringToMultiSZ(hKey, L"Bind", DeviceName);
+       if (rc != ERROR_SUCCESS)
+       {
+               DPRINT("AppendStringToMultiSZ() failed with error 0x%lx\n", rc);
+               goto cleanup;
+       }
+       rc = AppendStringToMultiSZ(hKey, L"Export", ExportName);
+       if (rc != ERROR_SUCCESS)
+       {
+               DPRINT("AppendStringToMultiSZ() failed with error 0x%lx\n", rc);
+               goto cleanup;
+       }
+       rc = AppendStringToMultiSZ(hKey, L"Route", UuidString);
+       if (rc != ERROR_SUCCESS)
+       {
+               DPRINT("AppendStringToMultiSZ() failed with error 0x%lx\n", rc);
+               goto cleanup;
+       }
+
+       /* HACK: hpoussin, Dec 2005. TCP/IP driver is not able to manage devices
+        * which are installed after its startup. So, we have to reboot to take
+        * this new netcard into account.
+        */
+       MessageBox(NULL, TEXT("You need to reboot to finish the installation of your network card."), TEXT("Reboot required"), MB_OK | MB_ICONWARNING);
+       rc = ERROR_SUCCESS;
+
+cleanup:
+       if (UuidRpcString != NULL)
+               RpcStringFreeW(&UuidRpcString);
+       HeapFree(GetProcessHeap(), 0, InstanceId);
+       HeapFree(GetProcessHeap(), 0, UuidString);
+       HeapFree(GetProcessHeap(), 0, DeviceName);
+       HeapFree(GetProcessHeap(), 0, ExportName);
+       if (hKey != INVALID_HANDLE_VALUE)
+               RegCloseKey(hKey);
+       if (hLinkageKey != INVALID_HANDLE_VALUE)
+               RegCloseKey(hLinkageKey);
+       if (hNetworkKey != INVALID_HANDLE_VALUE)
+               RegCloseKey(hNetworkKey);
+       if (hConnectionKey != INVALID_HANDLE_VALUE)
+               RegCloseKey(hConnectionKey);
+
+       if (rc == ERROR_SUCCESS)
+               rc = ERROR_DI_DO_DEFAULT;
+       DPRINT("Returning 0x%lx\n", rc);
+       return rc;
+}
diff --git a/reactos/dll/netcfgx/netcfgx.def b/reactos/dll/netcfgx/netcfgx.def
new file mode 100644 (file)
index 0000000..0f866b5
--- /dev/null
@@ -0,0 +1,6 @@
+LIBRARY netcfgx.dll
+
+EXPORTS
+NetClassInstaller@12
+
+;EOF
\ No newline at end of file
diff --git a/reactos/dll/netcfgx/netcfgx.h b/reactos/dll/netcfgx/netcfgx.h
new file mode 100644 (file)
index 0000000..5181820
--- /dev/null
@@ -0,0 +1,5 @@
+#include <windows.h>
+#include <setupapi.h>
+
+ULONG DbgPrint(PCH Format,...);
+
diff --git a/reactos/dll/netcfgx/netcfgx.xml b/reactos/dll/netcfgx/netcfgx.xml
new file mode 100644 (file)
index 0000000..d974d63
--- /dev/null
@@ -0,0 +1,8 @@
+<module name="netcfgx" type="win32dll" installbase="system32" installname="netcfgx.dll">
+       <importlibrary definition="netcfgx.def" />
+       <define name="__REACTOS__" />
+       <file>netcfgx.c</file>
+       <library>ntdll</library>
+       <library>rpcrt4</library>
+       <library>setupapi</library>
+</module>
diff --git a/reactos/dll/newdev/De.rc b/reactos/dll/newdev/De.rc
new file mode 100644 (file)
index 0000000..dbcf629
--- /dev/null
@@ -0,0 +1,74 @@
+LANGUAGE LANG_GERMAN, SUBLANG_NEUTRAL
+
+IDD_WELCOMEPAGE DIALOG DISCARDABLE  0, 0, 317, 193
+STYLE DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | 
+    WS_SYSMENU
+CAPTION "Hardwareinstallation"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    LTEXT           "Willkommen",IDC_WELCOMETITLE,120,8,195,24
+    LTEXT           "Dieser Assistent installiert einen neuen Gerätetreiber für:",
+                    IDC_STATIC,120,40,195,16
+    LTEXT           "Klicken Sie auf Weiter um fortzufahren.",IDC_STATIC,120,169,195,17
+    LTEXT           "UNKNOWN DEVICE",IDC_DEVICE,134,55,164,11
+    CONTROL         "Treiber automatisch installieren",IDC_RADIO_AUTO,"Button",
+                    BS_AUTORADIOBUTTON,120,112,178,13
+    CONTROL         "Treiber von einer bestimmten Position installieren",IDC_RADIO_MANUAL,
+                    "Button",BS_AUTORADIOBUTTON,120,133,164,14
+END
+
+IDD_NODRIVER DIALOG DISCARDABLE  0, 0, 317, 193
+STYLE DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | 
+    WS_SYSMENU
+CAPTION "Hardwareinstallation"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    LTEXT           "Installation fehlgeschlagen",IDC_FINISHTITLE,120,8,195,24
+    LTEXT           "Der Gerät konnte nicht installiert werden, da kein passender Treiber gefunden werden konnte.",
+                    IDC_STATIC,120,40,195,19
+    LTEXT           "Klicken Sie auf Zurück, wenn Sie eine Treiber CD oder Diskette haben oder den Pfad zum Treiber kennen.",
+                    IDC_STATIC,120,98,181,24
+    CONTROL         "Diesen Dialog nicht mehr anzeigen",IDC_DONOTSHOWDLG,"Button",
+                    BS_AUTOCHECKBOX | WS_TABSTOP,120,170,180,11
+END
+
+IDD_CHSOURCE DIALOG DISCARDABLE  0, 0, 317, 143
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Hardwareinstallation"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    CONTROL         "Diese Pfade durchsuchen",IDC_RADIO_SEARCHHERE,"Button",
+                    BS_AUTORADIOBUTTON,30,11,239,13
+    CONTROL         "Treiber manuell auswählen",IDC_RADIO_CHOOSE,"Button",
+                    BS_AUTORADIOBUTTON,30,98,171,12
+    CONTROL         "Wechseldatenträger durchsuchen",IDC_CHECK_MEDIA,"Button",
+                    BS_AUTOCHECKBOX | WS_TABSTOP,47,33,163,9
+    CONTROL         "Diesen Pfad ebenfalls durchsuchen",IDC_CHECK_PATH,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,50,54,162,10
+    PUSHBUTTON      "Durchsuchen",IDC_BROWSE,248,69,50,14
+    COMBOBOX        IDC_COMBO_PATH,64,71,176,12,CBS_DROPDOWN | CBS_SORT | 
+                    WS_VSCROLL | WS_TABSTOP
+END
+
+IDD_SEARCHDRV DIALOG DISCARDABLE  0, 0, 317, 143
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Hardwareinstallation"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    LTEXT           "UNKNOWN DEVICE",IDC_DEVICE,51,20,169,16
+END
+
+IDD_FINISHPAGE DIALOG DISCARDABLE  0, 0, 317, 193
+STYLE DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | 
+    WS_SYSMENU
+CAPTION "Hardwareinstallation"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    LTEXT           "Installation abgeschlossen",IDC_FINISHTITLE,120,8,195,11
+    LTEXT           "Der Assistent hat die Installation erfolgreich abgeschlossen.",
+                    IDC_STATIC,120,32,195,19
+    LTEXT           "Klicken Sie zum Beenden der Installation auf Fertigstellen.",IDC_STATIC,120,174,
+                    190,11
+    LTEXT           "UNKNOWN DEVICE",IDC_DEVICE,148,53,147,12
+END
+
diff --git a/reactos/dll/newdev/En.rc b/reactos/dll/newdev/En.rc
new file mode 100644 (file)
index 0000000..13839db
--- /dev/null
@@ -0,0 +1,74 @@
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+IDD_WELCOMEPAGE DIALOG DISCARDABLE  0, 0, 317, 193
+STYLE DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | 
+    WS_SYSMENU
+CAPTION "Device installation"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    LTEXT           "Welcome",IDC_WELCOMETITLE,120,8,195,24
+    LTEXT           "This wizard installs a new device driver for:",
+                    IDC_STATIC,120,40,195,16
+    LTEXT           "Click Next to continue.",IDC_STATIC,120,169,195,17
+    LTEXT           "UNKNOWN DEVICE",IDC_DEVICE,134,55,164,11
+    CONTROL         "Install driver automatically",IDC_RADIO_AUTO,"Button",
+                    BS_AUTORADIOBUTTON,120,112,178,13
+    CONTROL         "Install driver from specific location",IDC_RADIO_MANUAL,
+                    "Button",BS_AUTORADIOBUTTON,120,133,164,14
+END
+
+IDD_NODRIVER DIALOG DISCARDABLE  0, 0, 317, 193
+STYLE DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | 
+    WS_SYSMENU
+CAPTION "Device installation"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    LTEXT           "Installation failed",IDC_FINISHTITLE,120,8,195,24
+    LTEXT           "The device could not be installed because the driver could not be found.",
+                    IDC_STATIC,120,40,195,19
+    LTEXT           "Click on Back if you have a driver disk or know the path to the driver.",
+                    IDC_STATIC,120,98,181,24
+    CONTROL         "Do not show this dialog anymore",IDC_DONOTSHOWDLG,"Button",
+                    BS_AUTOCHECKBOX | WS_TABSTOP,120,170,180,11
+END
+
+IDD_CHSOURCE DIALOG DISCARDABLE  0, 0, 317, 143
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Device installation"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    CONTROL         "Search in these locations",IDC_RADIO_SEARCHHERE,"Button",
+                    BS_AUTORADIOBUTTON,27,11,239,13
+    CONTROL         "Choose the driver manually",IDC_RADIO_CHOOSE,"Button",
+                    BS_AUTORADIOBUTTON,27,98,171,12
+    CONTROL         "Search removeable media",IDC_CHECK_MEDIA,"Button",
+                    BS_AUTOCHECKBOX | WS_TABSTOP,47,33,163,9
+    CONTROL         "Include this path",IDC_CHECK_PATH,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,47,54,162,10
+    PUSHBUTTON      "Browse",IDC_BROWSE,248,69,50,14
+    COMBOBOX        IDC_COMBO_PATH,61,71,176,12,CBS_DROPDOWN | CBS_SORT | 
+                    WS_VSCROLL | WS_TABSTOP
+END
+
+IDD_SEARCHDRV DIALOG DISCARDABLE  0, 0, 317, 143
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Device installation"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    LTEXT           "UNKNOWN DEVICE",IDC_DEVICE,51,20,169,16
+END
+
+IDD_FINISHPAGE DIALOG DISCARDABLE  0, 0, 317, 193
+STYLE DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | 
+    WS_SYSMENU
+CAPTION "Device installation"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    LTEXT           "Installation complete",IDC_FINISHTITLE,120,8,195,11
+    LTEXT           "The wizard has finished installing the driver for:",
+                    IDC_STATIC,120,32,195,19
+    LTEXT           "Click Finish to close the wizard.",IDC_STATIC,120,174,
+                    179,11
+    LTEXT           "UNKNOWN DEVICE",IDC_DEVICE,148,53,147,12
+END
+
diff --git a/reactos/dll/newdev/Es.rc b/reactos/dll/newdev/Es.rc
new file mode 100644 (file)
index 0000000..26f6889
--- /dev/null
@@ -0,0 +1,74 @@
+LANGUAGE LANG_SPANISH, SUBLANG_NEUTRAL
+
+IDD_WELCOMEPAGE DIALOG DISCARDABLE  0, 0, 317, 193
+STYLE DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | 
+    WS_SYSMENU
+CAPTION "Instalación de Dispositivos"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    LTEXT           "Bienvenido",IDC_WELCOMETITLE,120,8,195,24
+    LTEXT           "Este asistente lo ayudará a instalar un nuevo controlador de dispositivos para:",
+                    IDC_STATIC,120,40,195,16
+    LTEXT           "Haga click en Siguiente para continuar.",IDC_STATIC,120,169,195,17
+    LTEXT           "DISPOSITIVO DESCONOCIDO",IDC_DEVICE,134,55,164,11
+    CONTROL         "Instalar controlador automáticamente",IDC_RADIO_AUTO,"Button",
+                    BS_AUTORADIOBUTTON,120,112,178,13
+    CONTROL         "Instalar controlador desde una ubicación específica",IDC_RADIO_MANUAL,
+                    "Button",BS_AUTORADIOBUTTON,120,133,164,14
+END
+
+IDD_NODRIVER DIALOG DISCARDABLE  0, 0, 317, 193
+STYLE DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | 
+    WS_SYSMENU
+CAPTION "Instalación de Dispositivos"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    LTEXT           "La instalación ha fallado",IDC_FINISHTITLE,120,8,195,24
+    LTEXT           "El dispositivo no pudo ser instalado porque el controlador no fue encontrado.",
+                    IDC_STATIC,120,40,195,19
+    LTEXT           "Haga click en Atrás si tiene un CDROM del controlador o conoce dónde encontrar el controlador.",
+                    IDC_STATIC,120,98,181,24
+    CONTROL         "No volver a mostrar esta ventana",IDC_DONOTSHOWDLG,"Button",
+                    BS_AUTOCHECKBOX | WS_TABSTOP,120,170,180,11
+END
+
+IDD_CHSOURCE DIALOG DISCARDABLE  0, 0, 317, 143
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Instalación de Dispositivos"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    CONTROL         "Buscar en controlador en estas ubicaciones",IDC_RADIO_SEARCHHERE,"Button",
+                    BS_AUTORADIOBUTTON,27,11,239,13
+    CONTROL         "Seleccionar el controlador manualmente",IDC_RADIO_CHOOSE,"Button",
+                    BS_AUTORADIOBUTTON,27,98,171,12
+    CONTROL         "Buscar en medios extraíbles",IDC_CHECK_MEDIA,"Button",
+                    BS_AUTOCHECKBOX | WS_TABSTOP,47,33,163,9
+    CONTROL         "Incluir esta ubicación de búsqueda",IDC_CHECK_PATH,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,47,54,162,10
+    PUSHBUTTON      "Examinar",IDC_BROWSE,248,69,50,14
+    COMBOBOX        IDC_COMBO_PATH,61,71,176,12,CBS_DROPDOWN | CBS_SORT | 
+                    WS_VSCROLL | WS_TABSTOP
+END
+
+IDD_SEARCHDRV DIALOG DISCARDABLE  0, 0, 317, 143
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Device installation"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    LTEXT           "DISPOSITIVO DESCONOCIDO",IDC_DEVICE,51,20,169,16
+END
+
+IDD_FINISHPAGE DIALOG DISCARDABLE  0, 0, 317, 193
+STYLE DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | 
+    WS_SYSMENU
+CAPTION "Device installation"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    LTEXT           "Instalación completa.",IDC_FINISHTITLE,120,8,195,11
+    LTEXT           "El asistente ha finalizado la instalación del controlador para el dispositivo:",
+                    IDC_STATIC,120,32,195,19
+    LTEXT           "Haga click en Finalizar parar cerrar el asistente.",IDC_STATIC,120,174,
+                    179,11
+    LTEXT           "DISPOSITIVO DESCONOCIDO",IDC_DEVICE,148,53,147,12
+END
+
diff --git a/reactos/dll/newdev/Hu.rc b/reactos/dll/newdev/Hu.rc
new file mode 100644 (file)
index 0000000..9e960d2
--- /dev/null
@@ -0,0 +1,62 @@
+// Hungarian resource file by Robert Horvath - talley at cubeclub.hu\r
+LANGUAGE LANG_HUNGARIAN, SUBLANG_NEUTRAL\r
+\r
+IDD_WELCOMEPAGE DIALOG DISCARDABLE  0, 0, 317, 193\r
+STYLE DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | \r
+    WS_SYSMENU\r
+CAPTION "Hardver telepítõ"\r
+FONT 8, "MS Shell Dlg"\r
+BEGIN\r
+    LTEXT           "welcome??",IDC_WELCOMETITLE,115,8,195,24\r
+    LTEXT           "A telepítõ a következõ hardverhez telepít fel eszközmeghatjtót:",IDC_STATIC,115,40,195,16\r
+    LTEXT           "Kattints a Tovább gombra a folytatáshoz.",IDC_STATIC,115,169,195,17\r
+    LTEXT           "Ismeretlen eszköz!",IDC_DEVICE,129,55,164,11\r
+    CONTROL         "Automatikus telepítés",IDC_RADIO_AUTO,"Button",BS_AUTORADIOBUTTON,115,112,178,13\r
+    CONTROL         "Eszközmeghajtó kiválasztása",IDC_RADIO_MANUAL,"Button",BS_AUTORADIOBUTTON,115,133,164,14\r
+END\r
+\r
+IDD_NODRIVER DIALOG DISCARDABLE  0, 0, 317, 193\r
+STYLE DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | \r
+    WS_SYSMENU\r
+CAPTION "Hardver telepítõ"\r
+FONT 8, "MS Shell Dlg"\r
+BEGIN\r
+    LTEXT           "A telepítés sikertelen.",IDC_FINISHTITLE,115,8,195,24\r
+    LTEXT           "Az eszközmeghajtó nem található.",IDC_STATIC,115,40,195,19\r
+    LTEXT           "Kattints a Vissza gombra, és az Eszközmeghajtó kiválasztása gombra, ha tudod, hol van az eszközmeghajtó.",IDC_STATIC,113,98,181,24\r
+    CONTROL         "Ne mutasd ezt többször",IDC_DONOTSHOWDLG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,114,170,180,11\r
+END\r
+\r
+IDD_CHSOURCE DIALOG DISCARDABLE  0, 0, 317, 143\r
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU\r
+CAPTION "Hardver telepítõ"\r
+FONT 8, "MS Shell Dlg"\r
+BEGIN\r
+    CONTROL         "Keresés ezeken a helyeken",IDC_RADIO_SEARCHHERE,"Button",BS_AUTORADIOBUTTON,27,11,239,13\r
+    CONTROL         "Eszközmeghajtó kiválasztása",IDC_RADIO_CHOOSE,"Button",BS_AUTORADIOBUTTON,27,98,171,12\r
+    CONTROL         "Keresés cserélhetõ lemezeken",IDC_CHECK_MEDIA,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,47,33,163,9\r
+    CONTROL         "A következõ útvonalon keressen",IDC_CHECK_PATH,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,47,54,162,10\r
+    PUSHBUTTON      "Böngészés",IDC_BROWSE,248,69,45,14\r
+    COMBOBOX        IDC_COMBO_PATH,61,71,176,12,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP\r
+END\r
+\r
+IDD_SEARCHDRV DIALOG DISCARDABLE  0, 0, 317, 143\r
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU\r
+CAPTION "Hardver telepítõ"\r
+FONT 8, "MS Shell Dlg"\r
+BEGIN\r
+    LTEXT           "Ismeretlen eszköz!",IDC_DEVICE,51,20,169,16\r
+END\r
+\r
+IDD_FINISHPAGE DIALOG DISCARDABLE  0, 0, 317, 193\r
+STYLE DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | \r
+    WS_SYSMENU\r
+CAPTION "Hardver telepítõ"\r
+FONT 8, "MS Shell Dlg"\r
+BEGIN\r
+    LTEXT           "A telepítés sikeresen befejezõdött",IDC_FINISHTITLE,115,8,195,11\r
+    LTEXT           "A telepítõ feltelepítette az eszközmeghajtókat a következõ eszközhöz:",IDC_STATIC,115,32,195,19\r
+    LTEXT           "A kilépéshez kattints a Befejezés gombra.",IDC_STATIC,115,174,179,11\r
+    LTEXT           "Ismeretlen eszköz!",IDC_DEVICE,148,53,147,12\r
+END\r
+\r
diff --git a/reactos/dll/newdev/Ru.rc b/reactos/dll/newdev/Ru.rc
new file mode 100644 (file)
index 0000000..4125e50
--- /dev/null
@@ -0,0 +1,74 @@
+LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT
+
+IDD_WELCOMEPAGE DIALOG DISCARDABLE  0, 0, 317, 193
+STYLE DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | 
+    WS_SYSMENU
+CAPTION "Óñòàíîâêà îáîðóäîâàíèÿ"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    LTEXT           "Âàñ ïðèâåòñòâóåò ìàñòåð óñòàíîâêè íîâîãî îáîðóäîâàíèÿ",IDC_WELCOMETITLE,120,8,195,24
+    LTEXT           "Ýòîò ìàñòåð óñòàíàâëèâàåò äðàéâåð äëÿ:",
+                    IDC_STATIC,120,40,195,16
+    LTEXT           "Äëÿ ïðîäîëæåíèÿ íàæìèòå êíîïêó ""Äàëåå"".",IDC_STATIC,120,169,195,17
+    LTEXT           "Íåèçâåñòíîå óñòðîéñòâî",IDC_DEVICE,134,55,164,11
+    CONTROL         "Óñòàíîâèòü äðàéâåð àâòîìàòè÷åñêè",IDC_RADIO_AUTO,"Button",
+                    BS_AUTORADIOBUTTON,120,112,178,13
+    CONTROL         "Óñòàíîâèòü äðàéâåð èç óêàçàííîãî ìåñòà",IDC_RADIO_MANUAL,
+                    "Button",BS_AUTORADIOBUTTON,120,133,164,14
+END
+
+IDD_NODRIVER DIALOG DISCARDABLE  0, 0, 317, 193
+STYLE DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | 
+    WS_SYSMENU
+CAPTION "Óñòàíîâêà îáîðóäîâàíèÿ"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    LTEXT           "Óñòàíîâêà íå óäàëàñü",IDC_FINISHTITLE,120,8,195,24
+    LTEXT           "Óñòðîéñòâî íå óñòàíîâëåíî, ïîòîìó ÷òî äðàéâåð íå íàéäåí.",
+                    IDC_STATIC,120,40,195,19
+    LTEXT           "Íàæìèòå êíîïêó ""Íàçàä"", åñëè ó Âàñ åñòü óñòàíîâî÷íûé äèñê ñ äðàéâåðîì, èëè ÷òîáû óêàçàòü ìåñòîïîëîæåíèå äðàéâåðà",
+                    IDC_STATIC,120,98,181,24
+    CONTROL         "Íå ïîêàçûâàòü ýòîò äèàëîã â áóäóùåì",IDC_DONOTSHOWDLG,"Button",
+                    BS_AUTOCHECKBOX | WS_TABSTOP,120,170,180,11
+END
+
+IDD_CHSOURCE DIALOG DISCARDABLE  0, 0, 317, 143
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Óñòàíîâêà îáîðóäîâàíèÿ"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    CONTROL         "Èñêàòü ïî ýòèì ïóòÿì",IDC_RADIO_SEARCHHERE,"Button",
+                    BS_AUTORADIOBUTTON,27,11,239,13
+    CONTROL         "Âûáðàòü äðàéâåð âðó÷íóþ",IDC_RADIO_CHOOSE,"Button",
+                    BS_AUTORADIOBUTTON,27,98,171,12
+    CONTROL         "Èñêàòü íà ñìåííûõ íîñèòåëÿõ",IDC_CHECK_MEDIA,"Button",
+                    BS_AUTOCHECKBOX | WS_TABSTOP,47,33,163,9
+    CONTROL         "Âêëþ÷èòü ýòîò ïóòü",IDC_CHECK_PATH,"Button",BS_AUTOCHECKBOX | 
+                    WS_TABSTOP,47,54,162,10
+    PUSHBUTTON      "Îáçîð",IDC_BROWSE,248,69,50,14
+    COMBOBOX        IDC_COMBO_PATH,61,71,176,12,CBS_DROPDOWN | CBS_SORT | 
+                    WS_VSCROLL | WS_TABSTOP
+END
+
+IDD_SEARCHDRV DIALOG DISCARDABLE  0, 0, 317, 143
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Óñòàíîâêà îáîðóäîâàíèÿ"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    LTEXT           "Íåèçâåñòíîå óñòðîéñòâî",IDC_DEVICE,51,20,169,16
+END
+
+IDD_FINISHPAGE DIALOG DISCARDABLE  0, 0, 317, 193
+STYLE DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | 
+    WS_SYSMENU
+CAPTION "Óñòàíîâêà îáîðóäîâàíèÿ"
+FONT 8, "MS Shell Dlg"
+BEGIN
+    LTEXT           "Óñòàíîâêà çàâåðøåíà",IDC_FINISHTITLE,120,8,195,11
+    LTEXT           "Ìàñòåð çàêîí÷èë óñòàíîâêó äðàéâåðà äëÿ:",
+                    IDC_STATIC,120,32,195,19
+    LTEXT           "Äëÿ âûõîäà èç ìàñòåðà íàæìèòå êíîïêó ""Ãîòîâî"".",IDC_STATIC,120,174,
+                    179,11
+    LTEXT           "Íåèçâåñòíîå óñòðîéñòâî",IDC_DEVICE,148,53,147,12
+END
+
diff --git a/reactos/dll/newdev/newdev.c b/reactos/dll/newdev/newdev.c
new file mode 100644 (file)
index 0000000..7ded017
--- /dev/null
@@ -0,0 +1,1241 @@
+/*
+ * New device installer (newdev.dll)
+ *
+ * Copyright 2005 Hervé Poussineau (hpoussin@reactos.org)
+ *           2005 Christoph von Wittich (Christoph@ActiveVB.de)
+ *
+ * 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
+ */
+
+#define YDEBUG
+#include "newdev.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(newdev);
+
+static BOOL SearchDriver ( PDEVINSTDATA DevInstData, LPCTSTR Path );
+static BOOL InstallDriver ( PDEVINSTDATA DevInstData );
+static DWORD WINAPI FindDriverProc( LPVOID lpParam );
+static BOOL FindDriver ( PDEVINSTDATA DevInstData );
+
+static DEVINSTDATA DevInstData;
+HINSTANCE hDllInstance;
+HANDLE hThread;
+
+static BOOL
+CanDisableDevice(IN DEVINST DevInst,
+                 IN HMACHINE hMachine,
+                 OUT BOOL *CanDisable)
+{
+#if 0
+    /* hpoussin, Dec 2005. I've disabled this code because
+     * ntoskrnl never sets the DN_DISABLEABLE flag.
+     */
+    CONFIGRET cr;
+    ULONG Status, ProblemNumber;
+    BOOL Ret = FALSE;
+
+    cr = CM_Get_DevNode_Status_Ex(&Status,
+                                  &ProblemNumber,
+                                  DevInst,
+                                  0,
+                                  hMachine);
+    if (cr == CR_SUCCESS)
+    {
+        *CanDisable = ((Status & DN_DISABLEABLE) != 0);
+        Ret = TRUE;
+    }
+
+    return Ret;
+#else
+    *CanDisable = TRUE;
+    return TRUE;
+#endif
+}
+
+
+static BOOL
+IsDeviceStarted(IN DEVINST DevInst,
+                IN HMACHINE hMachine,
+                OUT BOOL *IsEnabled)
+{
+    CONFIGRET cr;
+    ULONG Status, ProblemNumber;
+    BOOL Ret = FALSE;
+
+    cr = CM_Get_DevNode_Status_Ex(&Status,
+                                  &ProblemNumber,
+                                  DevInst,
+                                  0,
+                                  hMachine);
+    if (cr == CR_SUCCESS)
+    {
+        *IsEnabled = ((Status & DN_STARTED) != 0);
+        Ret = TRUE;
+    }
+
+    return Ret;
+}
+
+
+static BOOL
+StartDevice(IN HDEVINFO DeviceInfoSet,
+            IN PSP_DEVINFO_DATA DevInfoData  OPTIONAL,
+            IN BOOL bEnable,
+            IN DWORD HardwareProfile  OPTIONAL,
+            OUT BOOL *bNeedReboot  OPTIONAL)
+{
+    SP_PROPCHANGE_PARAMS pcp;
+    SP_DEVINSTALL_PARAMS dp;
+    DWORD LastErr;
+    BOOL Ret = FALSE;
+
+    pcp.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
+    pcp.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE;
+    pcp.HwProfile = HardwareProfile;
+
+    if (bEnable)
+    {
+        /* try to enable the device on the global profile */
+        pcp.StateChange = DICS_ENABLE;
+        pcp.Scope = DICS_FLAG_GLOBAL;
+
+        /* ignore errors */
+        LastErr = GetLastError();
+        if (SetupDiSetClassInstallParams(DeviceInfoSet,
+                                         DevInfoData,
+                                         &pcp.ClassInstallHeader,
+                                         sizeof(SP_PROPCHANGE_PARAMS)))
+        {
+            SetupDiCallClassInstaller(DIF_PROPERTYCHANGE,
+                                      DeviceInfoSet,
+                                      DevInfoData);
+        }
+        SetLastError(LastErr);
+    }
+
+    /* try config-specific */
+    pcp.StateChange = (bEnable ? DICS_ENABLE : DICS_DISABLE);
+    pcp.Scope = DICS_FLAG_CONFIGSPECIFIC;
+
+    if (SetupDiSetClassInstallParams(DeviceInfoSet,
+                                     DevInfoData,
+                                     &pcp.ClassInstallHeader,
+                                     sizeof(SP_PROPCHANGE_PARAMS)) &&
+        SetupDiCallClassInstaller(DIF_PROPERTYCHANGE,
+                                  DeviceInfoSet,
+                                  DevInfoData))
+    {
+        dp.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
+        if (SetupDiGetDeviceInstallParams(DeviceInfoSet,
+                                          DevInfoData,
+                                          &dp))
+        {
+            if (bNeedReboot != NULL)
+            {
+                *bNeedReboot = ((dp.Flags & (DI_NEEDRESTART | DI_NEEDREBOOT)) != 0);
+            }
+
+            Ret = TRUE;
+        }
+    }
+    return Ret;
+}
+
+/*
+* @unimplemented
+*/
+BOOL WINAPI
+UpdateDriverForPlugAndPlayDevicesW(
+            IN HWND hwndParent,
+            IN LPCWSTR HardwareId,
+            IN LPCWSTR FullInfPath,
+            IN DWORD InstallFlags,
+            OUT PBOOL bRebootRequired OPTIONAL)
+{
+    UNIMPLEMENTED;
+    SetLastError(ERROR_GEN_FAILURE);
+    return FALSE;
+}
+
+/*
+* @implemented
+*/
+BOOL WINAPI
+UpdateDriverForPlugAndPlayDevicesA(
+               IN HWND hwndParent,
+               IN LPCSTR HardwareId,
+               IN LPCSTR FullInfPath,
+               IN DWORD InstallFlags,
+               OUT PBOOL bRebootRequired OPTIONAL)
+{
+    BOOL Result;
+    LPWSTR HardwareIdW = NULL;
+    LPWSTR FullInfPathW = NULL;
+
+    int len = MultiByteToWideChar(CP_ACP, 0, HardwareId, -1, NULL, 0);
+    HardwareIdW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
+    if (!HardwareIdW)
+    {
+        SetLastError(ERROR_GEN_FAILURE);
+        return FALSE;
+    }
+    MultiByteToWideChar(CP_ACP, 0, HardwareId, -1, HardwareIdW, len);
+
+    len = MultiByteToWideChar(CP_ACP, 0, FullInfPath, -1, NULL, 0);
+    FullInfPathW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
+    if (!FullInfPathW)
+    {
+        HeapFree(GetProcessHeap(), 0, HardwareIdW);
+        SetLastError(ERROR_GEN_FAILURE);
+        return FALSE;
+    }
+    MultiByteToWideChar(CP_ACP, 0, FullInfPath, -1, FullInfPathW, len);
+
+    Result = UpdateDriverForPlugAndPlayDevicesW(hwndParent,
+        HardwareIdW,
+        FullInfPathW,
+        InstallFlags,
+        bRebootRequired);
+
+    HeapFree(GetProcessHeap(), 0, HardwareIdW);
+    HeapFree(GetProcessHeap(), 0, FullInfPathW);
+
+    return Result;
+}
+
+
+static HFONT
+CreateTitleFont(VOID)
+{
+    NONCLIENTMETRICS ncm;
+    LOGFONT LogFont;
+    HDC hdc;
+    INT FontSize;
+    HFONT hFont;
+
+    ncm.cbSize = sizeof(NONCLIENTMETRICS);
+    SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0);
+
+    LogFont = ncm.lfMessageFont;
+    LogFont.lfWeight = FW_BOLD;
+    _tcscpy(LogFont.lfFaceName, _T("MS Shell Dlg"));
+
+    hdc = GetDC(NULL);
+    FontSize = 12;
+    LogFont.lfHeight = 0 - GetDeviceCaps (hdc, LOGPIXELSY) * FontSize / 72;
+    hFont = CreateFontIndirect(&LogFont);
+    ReleaseDC(NULL, hdc);
+
+    return hFont;
+}
+
+static VOID
+CenterWindow(HWND hWnd)
+{
+    HWND hWndParent;
+    RECT rcParent;
+    RECT rcWindow;
+
+    hWndParent = GetParent(hWnd);
+    if (hWndParent == NULL)
+        hWndParent = GetDesktopWindow();
+
+    GetWindowRect(hWndParent, &rcParent);
+    GetWindowRect(hWnd, &rcWindow);
+
+    SetWindowPos(hWnd,
+        HWND_TOP,
+        ((rcParent.right - rcParent.left) - (rcWindow.right - rcWindow.left)) / 2,
+        ((rcParent.bottom - rcParent.top) - (rcWindow.bottom - rcWindow.top)) / 2,
+        0,
+        0,
+        SWP_NOSIZE);
+}
+
+static INT_PTR CALLBACK
+WelcomeDlgProc(
+               IN HWND hwndDlg,
+               IN UINT uMsg,
+               IN WPARAM wParam,
+               IN LPARAM lParam)
+{
+
+    PDEVINSTDATA DevInstData;
+
+    /* Retrieve pointer to the global setup data */
+    DevInstData = (PDEVINSTDATA)GetWindowLongPtr (hwndDlg, GWL_USERDATA);
+
+    switch (uMsg)
+    {
+    case WM_INITDIALOG:
+        {
+            HWND hwndControl;
+            DWORD dwStyle;
+
+            /* Get pointer to the global setup data */
+            DevInstData = (PDEVINSTDATA)((LPPROPSHEETPAGE)lParam)->lParam;
+            SetWindowLongPtr(hwndDlg, GWL_USERDATA, (DWORD_PTR)DevInstData);
+
+            hwndControl = GetParent(hwndDlg);
+
+            /* Center the wizard window */
+            CenterWindow (hwndControl);
+
+            /* Hide the system menu */
+            dwStyle = GetWindowLong(hwndControl, GWL_STYLE);
+            SetWindowLong(hwndControl, GWL_STYLE, dwStyle & ~WS_SYSMENU);
+
+            /* Set title font */
+            SendDlgItemMessage(hwndDlg,
+                IDC_WELCOMETITLE,
+                WM_SETFONT,
+                (WPARAM)DevInstData->hTitleFont,
+                (LPARAM)TRUE);
+
+            SendDlgItemMessage(hwndDlg,
+                IDC_DEVICE,
+                WM_SETTEXT,
+                0,
+                (LPARAM) DevInstData->buffer);
+
+            SendDlgItemMessage(hwndDlg,
+                IDC_RADIO_AUTO,
+                BM_SETCHECK,
+                (WPARAM) TRUE,
+                (LPARAM) 0);
+
+
+        }
+        break;
+
+
+    case WM_NOTIFY:
+        {
+            LPNMHDR lpnm = (LPNMHDR)lParam;
+
+            switch (lpnm->code)
+            {
+            case PSN_SETACTIVE:
+                /* Enable the Next button */
+                PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_NEXT);
+                break;
+
+            case PSN_WIZNEXT:
+                /* Handle a Next button click, if necessary */
+
+                if (SendDlgItemMessage(hwndDlg, IDC_RADIO_AUTO, BM_GETCHECK, (WPARAM) 0, (LPARAM) 0) == BST_CHECKED)
+                    PropSheet_SetCurSel(GetParent(hwndDlg), 0, IDD_SEARCHDRV);
+
+                break;
+
+            default:
+                break;
+            }
+        }
+        break;
+
+    default:
+        break;
+    }
+
+    return FALSE;
+}
+
+static INT_PTR CALLBACK
+CHSourceDlgProc(
+                IN HWND hwndDlg,
+                IN UINT uMsg,
+                IN WPARAM wParam,
+                IN LPARAM lParam)
+{
+
+    PDEVINSTDATA DevInstData;
+
+    /* Retrieve pointer to the global setup data */
+    DevInstData = (PDEVINSTDATA)GetWindowLongPtr (hwndDlg, GWL_USERDATA);
+
+    switch (uMsg)
+    {
+    case WM_INITDIALOG:
+        {
+            HWND hwndControl;
+            DWORD dwStyle;
+
+            /* Get pointer to the global setup data */
+            DevInstData = (PDEVINSTDATA)((LPPROPSHEETPAGE)lParam)->lParam;
+            SetWindowLongPtr(hwndDlg, GWL_USERDATA, (DWORD_PTR)DevInstData);
+
+            hwndControl = GetParent(hwndDlg);
+
+            /* Center the wizard window */
+            CenterWindow (hwndControl);
+
+            /* Hide the system menu */
+            dwStyle = GetWindowLong(hwndControl, GWL_STYLE);
+            SetWindowLong(hwndControl, GWL_STYLE, dwStyle & ~WS_SYSMENU);
+
+            SendDlgItemMessage(hwndDlg,
+                IDC_RADIO_SEARCHHERE,
+                BM_SETCHECK,
+                (WPARAM) TRUE,
+                (LPARAM) 0);
+
+        }
+        break;
+
+
+    case WM_NOTIFY:
+        {
+            LPNMHDR lpnm = (LPNMHDR)lParam;
+
+            switch (lpnm->code)
+            {
+            case PSN_SETACTIVE:
+                /* Enable the Next and Back buttons */
+                PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_NEXT | PSWIZB_BACK);
+                break;
+
+            case PSN_WIZNEXT:
+                /* Handle a Next button click, if necessary */
+                PropSheet_SetCurSel(GetParent(hwndDlg), 0, 4);
+                break;
+
+            default:
+                break;
+            }
+        }
+        break;
+
+    default:
+        break;
+    }
+
+    return FALSE;
+}
+
+static INT_PTR CALLBACK
+SearchDrvDlgProc(
+                 IN HWND hwndDlg,
+                 IN UINT uMsg,
+                 IN WPARAM wParam,
+                 IN LPARAM lParam)
+{
+
+    PDEVINSTDATA DevInstData;
+    DWORD dwThreadId;
+
+    /* Retrieve pointer to the global setup data */
+    DevInstData = (PDEVINSTDATA)GetWindowLongPtr (hwndDlg, GWL_USERDATA);
+
+    switch (uMsg)
+    {
+    case WM_INITDIALOG:
+        {
+            HWND hwndControl;
+            DWORD dwStyle;
+
+            /* Get pointer to the global setup data */
+            DevInstData = (PDEVINSTDATA)((LPPROPSHEETPAGE)lParam)->lParam;
+            SetWindowLongPtr(hwndDlg, GWL_USERDATA, (DWORD_PTR)DevInstData);
+
+            DevInstData->hDialog = hwndDlg;
+            hwndControl = GetParent(hwndDlg);
+
+            /* Center the wizard window */
+            CenterWindow (hwndControl);
+
+            SendDlgItemMessage(hwndDlg,
+                IDC_DEVICE,
+                WM_SETTEXT,
+                0,
+                (LPARAM) DevInstData->buffer);
+
+            /* Hide the system menu */
+            dwStyle = GetWindowLong(hwndControl, GWL_STYLE);
+            SetWindowLong(hwndControl, GWL_STYLE, dwStyle & ~WS_SYSMENU);
+        }
+        break;
+
+    case WM_SEARCH_FINISHED:
+        {
+            CloseHandle(hThread);
+            hThread = 0;
+            if (wParam == 0)
+                PropSheet_SetCurSel(GetParent(hwndDlg), 0, IDD_NODRIVER);
+            else
+                PropSheet_SetCurSel(GetParent(hwndDlg), 0, IDD_FINISHPAGE);
+            break;
+        }
+    case WM_NOTIFY:
+        {
+            LPNMHDR lpnm = (LPNMHDR)lParam;
+
+            switch (lpnm->code)
+            {
+            case PSN_SETACTIVE:
+                PropSheet_SetWizButtons(GetParent(hwndDlg), !PSWIZB_NEXT | !PSWIZB_BACK);
+                hThread = CreateThread( NULL, 0, FindDriverProc, DevInstData, 0, &dwThreadId);
+                break;
+
+            case PSN_KILLACTIVE:
+                if (hThread != 0)
+                {
+                    SetWindowLong ( hwndDlg, DWL_MSGRESULT, TRUE);
+                    return TRUE;
+                }
+                break;
+            case PSN_WIZNEXT:
+                /* Handle a Next button click, if necessary */
+                break;
+
+            default:
+                break;
+            }
+        }
+        break;
+
+    default:
+        break;
+    }
+
+    return FALSE;
+}
+
+static DWORD WINAPI
+FindDriverProc(
+               IN LPVOID lpParam)
+{
+    TCHAR drive[] = {'?',':',0};
+    size_t nType;
+    DWORD dwDrives;
+    PDEVINSTDATA DevInstData;
+    DWORD config_flags;
+    UINT i = 1;
+
+    DevInstData = (PDEVINSTDATA)lpParam;
+
+    dwDrives = GetLogicalDrives();
+    for (drive[0] = 'A'; drive[0] <= 'Z'; drive[0]++)
+    {
+        if (dwDrives & i)
+        {
+            nType = GetDriveType( drive );
+            if ((nType == DRIVE_CDROM))
+                //if ((nType == DRIVE_CDROM) || (nType == DRIVE_FIXED))
+            {
+                /* search for inf file */
+                if (SearchDriver ( DevInstData, drive ))
+                {
+                    /* if we found a valid driver inf... */
+                    if (FindDriver ( DevInstData ))
+                    {
+                        InstallDriver ( DevInstData );
+                        PostMessage(DevInstData->hDialog, WM_SEARCH_FINISHED, 1, 0);
+                        return 0;
+                    }
+                }
+            }
+        }
+        i <<= 1;
+    }
+
+    /* update device configuration */
+    if(SetupDiGetDeviceRegistryProperty(DevInstData->hDevInfo,
+        &DevInstData->devInfoData,
+        SPDRP_CONFIGFLAGS,
+        NULL,
+        (BYTE *)&config_flags,
+        sizeof(config_flags),
+        NULL))
+    {
+        config_flags |= CONFIGFLAG_FAILEDINSTALL;
+        SetupDiSetDeviceRegistryProperty(
+            DevInstData->hDevInfo,
+            &DevInstData->devInfoData,
+            SPDRP_CONFIGFLAGS,
+            (BYTE *)&config_flags, sizeof(config_flags) );
+    }
+
+    PostMessage(DevInstData->hDialog, WM_SEARCH_FINISHED, 0, 0);
+    return 0;
+}
+
+static INT_PTR CALLBACK
+FinishDlgProc(
+              IN HWND hwndDlg,
+              IN UINT uMsg,
+              IN WPARAM wParam,
+              IN LPARAM lParam)
+{
+
+    PDEVINSTDATA DevInstData;
+
+    /* Retrieve pointer to the global setup data */
+    DevInstData = (PDEVINSTDATA)GetWindowLongPtr (hwndDlg, GWL_USERDATA);
+
+    switch (uMsg)
+    {
+    case WM_INITDIALOG:
+        {
+            HWND hwndControl;
+
+            /* Get pointer to the global setup data */
+            DevInstData = (PDEVINSTDATA)((LPPROPSHEETPAGE)lParam)->lParam;
+            SetWindowLongPtr(hwndDlg, GWL_USERDATA, (DWORD_PTR)DevInstData);
+
+            hwndControl = GetDlgItem(GetParent(hwndDlg), IDCANCEL);
+            ShowWindow (hwndControl, SW_HIDE);
+            EnableWindow (hwndControl, FALSE);
+
+            SendDlgItemMessage(hwndDlg,
+                IDC_DEVICE,
+                WM_SETTEXT,
+                0,
+                (LPARAM) DevInstData->drvInfoData.Description);
+
+            /* Set title font */
+            SendDlgItemMessage(hwndDlg,
+                IDC_FINISHTITLE,
+                WM_SETFONT,
+                (WPARAM)DevInstData->hTitleFont,
+                (LPARAM)TRUE);
+        }
+        break;
+
+    case WM_NOTIFY:
+        {
+            LPNMHDR lpnm = (LPNMHDR)lParam;
+
+            switch (lpnm->code)
+            {
+            case PSN_SETACTIVE:
+                /* Enable the correct buttons on for the active page */
+                PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_FINISH);
+                break;
+
+            case PSN_WIZBACK:
+                /* Handle a Back button click, if necessary */
+                break;
+
+            case PSN_WIZFINISH:
+                /* Handle a Finish button click, if necessary */
+                break;
+
+            default:
+                break;
+            }
+        }
+        break;
+
+    default:
+        break;
+    }
+
+    return FALSE;
+}
+
+static INT_PTR CALLBACK
+InstFailDlgProc(
+                IN HWND hwndDlg,
+                IN UINT uMsg,
+                IN WPARAM wParam,
+                IN LPARAM lParam)
+{
+
+    PDEVINSTDATA DevInstData;
+
+    /* Get pointer to the global setup data */
+    DevInstData = (PDEVINSTDATA)GetWindowLongPtr (hwndDlg, GWL_USERDATA);
+
+    switch (uMsg)
+    {
+    case WM_INITDIALOG:
+        {
+            HWND hwndControl;
+            BOOL DisableableDevice = FALSE;
+
+            DevInstData = (PDEVINSTDATA)((LPPROPSHEETPAGE)lParam)->lParam;
+            SetWindowLongPtr(hwndDlg, GWL_USERDATA, (DWORD_PTR)DevInstData);
+
+            hwndControl = GetDlgItem(GetParent(hwndDlg), IDCANCEL);
+            ShowWindow (hwndControl, SW_HIDE);
+            EnableWindow (hwndControl, FALSE);
+
+            /* Set title font */
+            SendDlgItemMessage(hwndDlg,
+                IDC_FINISHTITLE,
+                WM_SETFONT,
+                (WPARAM)DevInstData->hTitleFont,
+                (LPARAM)TRUE);
+
+            /* disable the "do not show this dialog anymore" checkbox
+               if the device cannot be disabled */
+            CanDisableDevice(DevInstData->devInfoData.DevInst,
+                             NULL,
+                             &DisableableDevice);
+            EnableWindow(GetDlgItem(hwndDlg,
+                                    IDC_DONOTSHOWDLG),
+                         DisableableDevice);
+        }
+        break;
+
+    case WM_NOTIFY:
+        {
+            LPNMHDR lpnm = (LPNMHDR)lParam;
+
+            switch (lpnm->code)
+            {
+            case PSN_SETACTIVE:
+                /* Enable the correct buttons on for the active page */
+                PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK | PSWIZB_FINISH);
+                break;
+
+            case PSN_WIZBACK:
+                PropSheet_SetCurSel(GetParent(hwndDlg), 0, IDD_WELCOMEPAGE);
+                /* Handle a Back button click, if necessary */
+                break;
+
+            case PSN_WIZFINISH:
+            {
+                BOOL DisableableDevice = FALSE;
+                BOOL IsStarted = FALSE;
+
+                if (CanDisableDevice(DevInstData->devInfoData.DevInst,
+                                     NULL,
+                                     &DisableableDevice) &&
+                    DisableableDevice &&
+                    IsDeviceStarted(DevInstData->devInfoData.DevInst,
+                                    NULL,
+                                    &IsStarted) &&
+                    !IsStarted &&
+                    SendDlgItemMessage(hwndDlg, IDC_DONOTSHOWDLG, BM_GETCHECK, (WPARAM) 0, (LPARAM) 0) == BST_CHECKED)
+                {
+                    /* disable the device */
+                    StartDevice(DevInstData->hDevInfo,
+                                &DevInstData->devInfoData,
+                                FALSE,
+                                0,
+                                NULL);
+                }
+                break;
+            }
+
+            default:
+                break;
+            }
+        }
+        break;
+
+    default:
+        break;
+    }
+
+    return FALSE;
+}
+
+
+static BOOL
+FindDriver(
+           IN PDEVINSTDATA DevInstData)
+{
+    SP_DEVINSTALL_PARAMS DevInstallParams = {0,};
+    BOOL ret;
+
+    DevInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
+    if (!SetupDiGetDeviceInstallParams(DevInstData->hDevInfo, &DevInstData->devInfoData, &DevInstallParams))
+    {
+        TRACE("SetupDiGetDeviceInstallParams() failed with error 0x%lx\n", GetLastError());
+        return FALSE;
+    }
+    DevInstallParams.FlagsEx |= DI_FLAGSEX_ALLOWEXCLUDEDDRVS;
+    if (!SetupDiSetDeviceInstallParams(DevInstData->hDevInfo, &DevInstData->devInfoData, &DevInstallParams))
+    {
+        TRACE("SetupDiSetDeviceInstallParams() failed with error 0x%lx\n", GetLastError());
+        return FALSE;
+    }
+
+    ret = SetupDiBuildDriverInfoList(DevInstData->hDevInfo, &DevInstData->devInfoData, SPDIT_COMPATDRIVER);
+    if (!ret)
+    {
+        TRACE("SetupDiBuildDriverInfoList() failed with error 0x%lx\n", GetLastError());
+        return FALSE;
+    }
+
+    DevInstData->drvInfoData.cbSize = sizeof(SP_DRVINFO_DATA);
+    ret = SetupDiEnumDriverInfo(
+        DevInstData->hDevInfo,
+        &DevInstData->devInfoData,
+        SPDIT_COMPATDRIVER,
+        0,
+        &DevInstData->drvInfoData);
+    if (!ret)
+    {
+        if (GetLastError() == ERROR_NO_MORE_ITEMS)
+            return FALSE;
+        TRACE("SetupDiEnumDriverInfo() failed with error 0x%lx\n", GetLastError());
+        return FALSE;
+    }
+    TRACE("Installing driver %S: %S\n", DevInstData->drvInfoData.MfgName, DevInstData->drvInfoData.Description);
+
+    return TRUE;
+}
+
+
+static BOOL
+IsDots(IN LPCTSTR str)
+{
+    if(_tcscmp(str, _T(".")) && _tcscmp(str, _T(".."))) return FALSE;
+    return TRUE;
+}
+
+static LPTSTR
+GetFileExt(IN LPTSTR FileName)
+{
+    if (FileName == 0)
+        return _T("");
+
+    int i = _tcsclen(FileName);
+    while ((i >= 0) && (FileName[i] != _T('.')))
+        i--;
+
+    FileName = _tcslwr(FileName);
+
+    if (i >= 0)
+        return &FileName[i];
+    else
+        return _T("");
+}
+
+static BOOL
+SearchDriver(
+             IN PDEVINSTDATA DevInstData,
+             IN LPCTSTR Path)
+{
+    WIN32_FIND_DATA wfd;
+    SP_DEVINSTALL_PARAMS DevInstallParams;
+    TCHAR DirPath[MAX_PATH];
+    TCHAR FileName[MAX_PATH];
+    TCHAR FullPath[MAX_PATH];
+    TCHAR LastDirPath[MAX_PATH] = _T("");
+    TCHAR PathWithPattern[MAX_PATH];
+    BOOL ok = TRUE;
+    BOOL ret;
+    HANDLE hFindFile;
+
+    _tcscpy(DirPath, Path);
+
+    if (DirPath[_tcsclen(DirPath) - 1] != '\\')
+        _tcscat(DirPath, _T("\\"));
+
+    _tcscpy(PathWithPattern, DirPath);
+    _tcscat(PathWithPattern, _T("\\*"));
+
+    for (hFindFile = FindFirstFile(PathWithPattern, &wfd); ((hFindFile != INVALID_HANDLE_VALUE) && ok); ok = FindNextFile(hFindFile, &wfd))
+    {
+
+        _tcscpy(FileName, wfd.cFileName);
+        if (IsDots(FileName)) continue;
+
+        if((wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
+        {
+            _tcscpy(FullPath, DirPath);
+            _tcscat(FullPath, FileName);
+            if(SearchDriver(DevInstData, FullPath))
+                break;
+        }
+        else
+        {
+            LPCTSTR pszExtension = GetFileExt(FileName);
+
+            if ((_tcscmp(pszExtension, _T(".inf")) == 0) && (_tcscmp(LastDirPath, DirPath) != 0))
+            {
+                _tcscpy(LastDirPath, DirPath);
+                ZeroMemory (&DevInstallParams, sizeof(SP_DEVINSTALL_PARAMS));
+                DevInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
+
+                ret = SetupDiGetDeviceInstallParams(
+                    DevInstData->hDevInfo,
+                    &DevInstData->devInfoData,
+                    &DevInstallParams);
+
+                if (_tcsclen(DirPath) <= MAX_PATH)
+                {
+                    memcpy(DevInstallParams.DriverPath, DirPath, (_tcsclen(DirPath) + 1) *  sizeof(TCHAR));
+                }
+
+                ret = SetupDiSetDeviceInstallParams(
+                    DevInstData->hDevInfo,
+                    &DevInstData->devInfoData,
+                    &DevInstallParams);
+
+                if ( FindDriver ( DevInstData ) )
+                {
+                    if (hFindFile != INVALID_HANDLE_VALUE)
+                        FindClose(hFindFile);
+                    return TRUE;
+                }
+
+            }
+        }
+    }
+
+    if (hFindFile != INVALID_HANDLE_VALUE)
+        FindClose(hFindFile);
+
+    return FALSE;
+}
+
+static BOOL
+InstallDriver(
+              IN PDEVINSTDATA DevInstData)
+{
+
+    BOOL ret;
+
+    ret = SetupDiCallClassInstaller(
+        DIF_SELECTBESTCOMPATDRV,
+        DevInstData->hDevInfo,
+        &DevInstData->devInfoData);
+    if (!ret)
+    {
+        TRACE("SetupDiCallClassInstaller(DIF_SELECTBESTCOMPATDRV) failed with error 0x%lx\n", GetLastError());
+        return FALSE;
+    }
+
+    ret = SetupDiCallClassInstaller(
+        DIF_ALLOW_INSTALL,
+        DevInstData->hDevInfo,
+        &DevInstData->devInfoData);
+    if (!ret)
+    {
+        TRACE("SetupDiCallClassInstaller(DIF_ALLOW_INSTALL) failed with error 0x%lx\n", GetLastError());
+        return FALSE;
+    }
+
+    ret = SetupDiCallClassInstaller(
+        DIF_NEWDEVICEWIZARD_PREANALYZE,
+        DevInstData->hDevInfo,
+        &DevInstData->devInfoData);
+    if (!ret)
+    {
+        TRACE("SetupDiCallClassInstaller(DIF_NEWDEVICEWIZARD_PREANALYZE) failed with error 0x%lx\n", GetLastError());
+        return FALSE;
+    }
+
+    ret = SetupDiCallClassInstaller(
+        DIF_NEWDEVICEWIZARD_POSTANALYZE,
+        DevInstData->hDevInfo,
+        &DevInstData->devInfoData);
+    if (!ret)
+    {
+        TRACE("SetupDiCallClassInstaller(DIF_NEWDEVICEWIZARD_POSTANALYZE) failed with error 0x%lx\n", GetLastError());
+        return FALSE;
+    }
+
+    ret = SetupDiCallClassInstaller(
+        DIF_INSTALLDEVICEFILES,
+        DevInstData->hDevInfo,
+        &DevInstData->devInfoData);
+    if (!ret)
+    {
+        TRACE("SetupDiCallClassInstaller(DIF_INSTALLDEVICEFILES) failed with error 0x%lx\n", GetLastError());
+        return FALSE;
+    }
+
+    ret = SetupDiCallClassInstaller(
+        DIF_REGISTER_COINSTALLERS,
+        DevInstData->hDevInfo,
+        &DevInstData->devInfoData);
+    if (!ret)
+    {
+        TRACE("SetupDiCallClassInstaller(DIF_REGISTER_COINSTALLERS) failed with error 0x%lx\n", GetLastError());
+        return FALSE;
+    }
+
+    ret = SetupDiCallClassInstaller(
+        DIF_INSTALLINTERFACES,
+        DevInstData->hDevInfo,
+        &DevInstData->devInfoData);
+    if (!ret)
+    {
+        TRACE("SetupDiCallClassInstaller(DIF_INSTALLINTERFACES) failed with error 0x%lx\n", GetLastError());
+        return FALSE;
+    }
+
+    ret = SetupDiCallClassInstaller(
+        DIF_INSTALLDEVICE,
+        DevInstData->hDevInfo,
+        &DevInstData->devInfoData);
+    if (!ret)
+    {
+        TRACE("SetupDiCallClassInstaller(DIF_INSTALLDEVICE) failed with error 0x%lx\n", GetLastError());
+        return FALSE;
+    }
+
+    ret = SetupDiCallClassInstaller(
+        DIF_NEWDEVICEWIZARD_FINISHINSTALL,
+        DevInstData->hDevInfo,
+        &DevInstData->devInfoData);
+    if (!ret)
+    {
+        TRACE("SetupDiCallClassInstaller(DIF_NEWDEVICEWIZARD_FINISHINSTALL) failed with error 0x%lx\n", GetLastError());
+        return FALSE;
+    }
+
+    ret = SetupDiCallClassInstaller(
+        DIF_DESTROYPRIVATEDATA,
+        DevInstData->hDevInfo,
+        &DevInstData->devInfoData);
+    if (!ret)
+    {
+        TRACE("SetupDiCallClassInstaller(DIF_DESTROYPRIVATEDATA) failed with error 0x%lx\n", GetLastError());
+        return FALSE;
+    }
+
+    return TRUE;
+
+}
+
+static VOID
+CleanUp(VOID)
+{
+
+    if (DevInstData.devInfoData.cbSize != 0)
+    {
+        if (!SetupDiDestroyDriverInfoList(DevInstData.hDevInfo, &DevInstData.devInfoData, SPDIT_COMPATDRIVER))
+            TRACE("SetupDiDestroyDriverInfoList() failed with error 0x%lx\n", GetLastError());
+    }
+
+    if (DevInstData.hDevInfo != INVALID_HANDLE_VALUE)
+    {
+        if (!SetupDiDestroyDeviceInfoList(DevInstData.hDevInfo))
+            TRACE("SetupDiDestroyDeviceInfoList() failed with error 0x%lx\n", GetLastError());
+    }
+
+    if (DevInstData.buffer)
+        HeapFree(GetProcessHeap(), 0, DevInstData.buffer);
+
+}
+
+/*
+* @implemented
+*/
+BOOL WINAPI
+DevInstallW(
+            IN HWND hWndParent,
+            IN HINSTANCE hInstance,
+            IN LPCWSTR InstanceId,
+            IN INT Show)
+{
+
+    PROPSHEETHEADER psh;
+    HPROPSHEETPAGE ahpsp[5];
+    PROPSHEETPAGE psp;
+    BOOL ret;
+    DWORD config_flags;
+
+    if (!IsUserAdmin())
+    {
+        /* XP kills the process... */
+        ExitProcess(ERROR_ACCESS_DENIED);
+    }
+
+    /* Clear devinst data */
+    ZeroMemory(&DevInstData, sizeof(DEVINSTDATA));
+    DevInstData.devInfoData.cbSize = 0; /* Tell if the devInfoData is valid */
+
+
+    DevInstData.hDevInfo = SetupDiCreateDeviceInfoListExW(NULL, NULL, NULL, NULL);
+    if (DevInstData.hDevInfo == INVALID_HANDLE_VALUE)
+    {
+        TRACE("SetupDiCreateDeviceInfoListExW() failed with error 0x%lx\n", GetLastError());
+        CleanUp();
+        return FALSE;
+    }
+
+    DevInstData.devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
+    ret = SetupDiOpenDeviceInfoW(
+        DevInstData.hDevInfo,
+        InstanceId,
+        NULL,
+        0, /* Open flags */
+        &DevInstData.devInfoData);
+    if (!ret)
+    {
+        TRACE("SetupDiOpenDeviceInfoW() failed with error 0x%lx (InstanceId %S)\n", GetLastError(), InstanceId);
+        DevInstData.devInfoData.cbSize = 0;
+        CleanUp();
+        return FALSE;
+    }
+
+    SetLastError(ERROR_GEN_FAILURE);
+    ret = SetupDiGetDeviceRegistryProperty(
+        DevInstData.hDevInfo,
+        &DevInstData.devInfoData,
+        SPDRP_DEVICEDESC,
+        &DevInstData.regDataType,
+        NULL, 0,
+        &DevInstData.requiredSize);
+
+    if (!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER && DevInstData.regDataType == REG_SZ)
+    {
+        DevInstData.buffer = HeapAlloc(GetProcessHeap(), 0, DevInstData.requiredSize);
+        if (!DevInstData.buffer)
+        {
+            TRACE("HeapAlloc() failed\n");
+            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+        }
+        else
+        {
+            ret = SetupDiGetDeviceRegistryProperty(
+                DevInstData.hDevInfo,
+                &DevInstData.devInfoData,
+                SPDRP_DEVICEDESC,
+                &DevInstData.regDataType,
+                DevInstData.buffer, DevInstData.requiredSize,
+                &DevInstData.requiredSize);
+        }
+    }
+    if (!ret)
+    {
+        TRACE("SetupDiGetDeviceRegistryProperty() failed with error 0x%lx (InstanceId %S)\n", GetLastError(), InstanceId);
+        CleanUp();
+        return FALSE;
+    }
+
+    if(SetupDiGetDeviceRegistryProperty(DevInstData.hDevInfo,
+        &DevInstData.devInfoData,
+        SPDRP_CONFIGFLAGS,
+        NULL,
+        (BYTE *)&config_flags,
+        sizeof(config_flags),
+        NULL))
+    {
+        if (config_flags & CONFIGFLAG_FAILEDINSTALL)
+        {
+            CleanUp();
+            return TRUE;
+        }   
+    }
+    /*
+    else 
+    {
+        swprintf(buf, _T("%ld"), GetLastError()); 
+        MessageBox(0,buf,buf,0);
+    }
+    */
+
+    TRACE("Installing %S (%S)\n", DevInstData.buffer, InstanceId);
+
+    if ((!FindDriver(&DevInstData)) && (Show != SW_HIDE))
+    {
+
+        /* Create the Welcome page */
+        ZeroMemory (&psp, sizeof(PROPSHEETPAGE));
+        psp.dwSize = sizeof(PROPSHEETPAGE);
+        psp.dwFlags = PSP_DEFAULT | PSP_HIDEHEADER;
+        psp.hInstance = hDllInstance;
+        psp.lParam = (LPARAM)&DevInstData;
+        psp.pfnDlgProc = WelcomeDlgProc;
+        psp.pszTemplate = MAKEINTRESOURCE(IDD_WELCOMEPAGE);
+        ahpsp[IDD_WELCOMEPAGE] = CreatePropertySheetPage(&psp);
+
+        /* Create the Select Source page */
+        psp.dwFlags = PSP_DEFAULT | PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE;
+        psp.pfnDlgProc = CHSourceDlgProc;
+        psp.pszTemplate = MAKEINTRESOURCE(IDD_CHSOURCE);
+        ahpsp[IDD_CHSOURCE] = CreatePropertySheetPage(&psp);
+
+        /* Create the Search driver page */
+        psp.dwFlags = PSP_DEFAULT | PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE;
+        psp.pfnDlgProc = SearchDrvDlgProc;
+        psp.pszTemplate = MAKEINTRESOURCE(IDD_SEARCHDRV);
+        ahpsp[IDD_SEARCHDRV] = CreatePropertySheetPage(&psp);
+
+        /* Create the Finish page */
+        psp.dwFlags = PSP_DEFAULT | PSP_HIDEHEADER;
+        psp.pfnDlgProc = FinishDlgProc;
+        psp.pszTemplate = MAKEINTRESOURCE(IDD_FINISHPAGE);
+        ahpsp[IDD_FINISHPAGE] = CreatePropertySheetPage(&psp);
+
+        /* Create the Install failed page */
+        psp.dwFlags = PSP_DEFAULT | PSP_HIDEHEADER;
+        psp.pfnDlgProc = InstFailDlgProc;
+        psp.pszTemplate = MAKEINTRESOURCE(IDD_NODRIVER);
+        ahpsp[IDD_NODRIVER] = CreatePropertySheetPage(&psp);
+
+        /* Create the property sheet */
+        psh.dwSize = sizeof(PROPSHEETHEADER);
+        psh.dwFlags = PSH_WIZARD97 | PSH_WATERMARK | PSH_HEADER;
+        psh.hInstance = hDllInstance;
+        psh.hwndParent = NULL;
+        psh.nPages = 5;
+        psh.nStartPage = 0;
+        psh.phpage = ahpsp;
+        psh.pszbmWatermark = MAKEINTRESOURCE(IDB_WATERMARK);
+        psh.pszbmHeader = MAKEINTRESOURCE(IDB_HEADER);
+
+        /* Create title font */
+        DevInstData.hTitleFont = CreateTitleFont();
+
+        /* Display the wizard */
+        PropertySheet(&psh);
+
+        DeleteObject(DevInstData.hTitleFont);
+
+    }
+    else
+    {
+        InstallDriver ( &DevInstData );
+    }
+
+    CleanUp();
+    return TRUE;
+}
+
+/*
+* @unimplemented
+*/
+BOOL WINAPI
+ClientSideInstallW(IN HWND hWndOwner,
+                   IN DWORD dwUnknownFlags,
+                   IN LPWSTR lpNamedPipeName)
+{
+    /* NOTE: pNamedPipeName is in the format:
+     *       "\\.\pipe\PNP_Device_Install_Pipe_0.{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}"
+     */
+    return FALSE;
+}
+
+BOOL WINAPI
+DllMain(
+        IN HINSTANCE hInstance,
+        IN DWORD dwReason,
+        IN LPVOID lpReserved)
+{
+    if (dwReason == DLL_PROCESS_ATTACH)
+    {
+        INITCOMMONCONTROLSEX InitControls;
+
+        DisableThreadLibraryCalls(hInstance);
+
+        InitControls.dwSize = sizeof(INITCOMMONCONTROLSEX);
+        InitControls.dwICC = ICC_PROGRESS_CLASS;
+        InitCommonControlsEx(&InitControls);
+        hDllInstance = hInstance;
+    }
+
+    return TRUE;
+}
diff --git a/reactos/dll/newdev/newdev.h b/reactos/dll/newdev/newdev.h
new file mode 100644 (file)
index 0000000..b2504ee
--- /dev/null
@@ -0,0 +1,25 @@
+#include <windows.h>
+#include <commctrl.h>
+#include <regstr.h>
+#include <setupapi.h>
+#include <cfgmgr32.h>
+#include <tchar.h>
+#include <wine/debug.h>
+
+#include <stdio.h>
+
+#include "resource.h"
+
+typedef struct _DEVINSTDATA
+{
+       HFONT hTitleFont;
+       PBYTE buffer;
+       DWORD requiredSize;
+       DWORD regDataType;
+       HWND hDialog;
+       HDEVINFO hDevInfo;
+       SP_DEVINFO_DATA devInfoData;
+       SP_DRVINFO_DATA drvInfoData;
+} DEVINSTDATA, *PDEVINSTDATA;
+
+#define WM_SEARCH_FINISHED (WM_USER + 10)
diff --git a/reactos/dll/newdev/newdev.rc b/reactos/dll/newdev/newdev.rc
new file mode 100644 (file)
index 0000000..587778b
--- /dev/null
@@ -0,0 +1,13 @@
+#include <windows.h>
+#include "resource.h"
+
+LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
+
+IDB_WATERMARK BITMAP "resources/watermark.bmp"
+IDB_HEADER    BITMAP "resources/header.bmp"
+
+#include "En.rc"
+#include "Es.rc"
+#include "De.rc"
+#include "Hu.rc"
+#include "Ru.rc"
diff --git a/reactos/dll/newdev/newdev.spec b/reactos/dll/newdev/newdev.spec
new file mode 100644 (file)
index 0000000..1a2c993
--- /dev/null
@@ -0,0 +1,11 @@
+@ stdcall ClientSideInstallW(ptr long wstr)\r
+@ stdcall DevInstallW(ptr ptr wstr long)\r
+@ stub InstallDevInst\r
+@ stub InstallDevInstEx\r
+@ stdcall InstallNewDevice(ptr ptr ptr)\r
+@ stub InstallSelectedDevice\r
+@ stdcall InstallSelectedDriverW(ptr ptr wstr long ptr)\r
+@ stub InstallWindowsUpdateDriver\r
+@ stub RollbackDriver\r
+@ stdcall UpdateDriverForPlugAndPlayDevicesA(ptr str str long ptr)\r
+@ stdcall UpdateDriverForPlugAndPlayDevicesW(ptr wstr wstr long ptr)\r
diff --git a/reactos/dll/newdev/newdev.xml b/reactos/dll/newdev/newdev.xml
new file mode 100644 (file)
index 0000000..45cfeba
--- /dev/null
@@ -0,0 +1,17 @@
+<module name="newdev" type="win32dll" installbase="system32" installname="newdev.dll">
+       <include base="newdev">.</include>
+       <define name="UNICODE" />
+       <define name="_UNICODE" />
+       <importlibrary definition="newdev.spec.def" />
+       <define name="_WIN32_IE">0x0600</define>
+       <define name="_WIN32_WINNT">0x0501</define>
+       <file>newdev.c</file>
+       <file>stubs.c</file>
+       <file>newdev.rc</file>
+       <file>newdev.spec</file>
+       <library>wine</library>
+       <library>gdi32</library>
+       <library>comctl32</library>
+       <library>ntdll</library>
+       <library>setupapi</library>
+</module>
diff --git a/reactos/dll/newdev/resource.h b/reactos/dll/newdev/resource.h
new file mode 100644 (file)
index 0000000..fad5d93
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef RESOURCE_H
+#define RESOURCE_H
+
+#define IDB_WATERMARK                                  100
+#define IDB_HEADER                                             101
+
+#define IDC_STATIC                                             -1
+
+#define IDC_DEVICE                      2000
+#define IDC_RADIO_AUTO                  2001
+#define IDC_RADIO_MANUAL                2002
+#define IDC_DONOTSHOWDLG                2003
+#define IDC_RADIO_SEARCHHERE            2004
+#define IDC_RADIO_CHOOSE                2005
+#define IDC_CHECK_MEDIA                 2006
+#define IDC_CHECK_PATH                  2007
+#define IDC_WELCOMETITLE                               2008
+#define IDC_BROWSE                      2009
+#define IDC_COMBO_PATH                  2010
+#define IDC_FINISHTITLE                 2011
+
+#define IDD_WELCOMEPAGE                                        0
+#define IDD_CHSOURCE                    1
+#define IDD_SEARCHDRV                   2
+#define IDD_FINISHPAGE                  3
+#define IDD_NODRIVER                    4
+
+#endif /* RESOURCE_H */
diff --git a/reactos/dll/newdev/resources/header.bmp b/reactos/dll/newdev/resources/header.bmp
new file mode 100644 (file)
index 0000000..af7bf7a
Binary files /dev/null and b/reactos/dll/newdev/resources/header.bmp differ
diff --git a/reactos/dll/newdev/resources/watermark.bmp b/reactos/dll/newdev/resources/watermark.bmp
new file mode 100644 (file)
index 0000000..1b42e5b
Binary files /dev/null and b/reactos/dll/newdev/resources/watermark.bmp differ
diff --git a/reactos/dll/newdev/stubs.c b/reactos/dll/newdev/stubs.c
new file mode 100644 (file)
index 0000000..226a87e
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * New device installer (newdev.dll)
+ *
+ * Copyright 2005 Hervé Poussineau (hpoussin@reactos.org)
+ *
+ * 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 "newdev.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(newdev);
+
+/*
+* @unimplemented
+*/
+BOOL WINAPI
+InstallNewDevice(
+       IN HWND hwndParent,
+       IN LPGUID ClassGuid OPTIONAL,
+       OUT PDWORD Reboot)
+{
+       UNIMPLEMENTED;
+       SetLastError(ERROR_GEN_FAILURE);
+       return FALSE;
+}
+
+/*
+* @unimplemented
+*/
+BOOL WINAPI
+InstallSelectedDriverW(
+       IN HWND hwndParent,
+       IN HDEVINFO DeviceInfoSet,
+       IN LPCWSTR Reserved,
+       IN BOOL Backup,
+       OUT PDWORD pReboot)
+{
+       UNIMPLEMENTED;
+       SetLastError(ERROR_GEN_FAILURE);
+       return FALSE;
+}