--- /dev/null
+; 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
--- /dev/null
+#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>
--- /dev/null
+<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>
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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
+};
--- /dev/null
+ 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
--- /dev/null
+<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>
--- /dev/null
+/*
+ * 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");
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ *
+ * 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;
+ }
+
--- /dev/null
+/*
+ *
+ * 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 */
--- /dev/null
+/*
+ *
+ * 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;
+}
--- /dev/null
+/* -*- 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 */
--- /dev/null
+/*
+ *
+ * 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
--- /dev/null
+; $Id$
+;
+; mmdrv.def
+;
+; ReactOS Operating System
+;
+LIBRARY mmdrv.dll
+EXPORTS
+DriverProc@20
+widMessage@20
+wodMessage@20
+midMessage@20
+modMessage@20
+auxMessage@20
--- /dev/null
+/*
+ *
+ * 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
--- /dev/null
+<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>
--- /dev/null
+/*
+ *
+ * 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;
+}
--- /dev/null
+/*
+ *
+ * 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;
+ }
+}
+
--- /dev/null
+
+// 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;
--- /dev/null
+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
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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"
--- /dev/null
+# 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)
--- /dev/null
+<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>
--- /dev/null
+/*
+ * 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
+}
--- /dev/null
+/*
+ * 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
+}
--- /dev/null
+/*
+ * 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
+}
--- /dev/null
+/*
+ * 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
+}
--- /dev/null
+/*
+ * 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
+}
--- /dev/null
+/*
+ * 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
+}
--- /dev/null
+/*
+ * 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
+}
--- /dev/null
+/*
+ * 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"
+}
--- /dev/null
+/*
+ * 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
+}
--- /dev/null
+/*
+ * 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
+}
--- /dev/null
+/*
+ * 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"
+}
--- /dev/null
+/*
+ * 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
+}
--- /dev/null
+/*
+ * 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
+}
--- /dev/null
+/*
+ * 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
+}
--- /dev/null
+/*
+ * 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
+}
--- /dev/null
+/*
+ * 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
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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"
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+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:
--- /dev/null
+/* -*- 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;
+}
--- /dev/null
+/* -*- 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;
+}
--- /dev/null
+/* -*- 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;
+}
--- /dev/null
+TOPSRCDIR = @top_srcdir@
+TOPOBJDIR = ../../..
+SRCDIR = @srcdir@
+VPATH = @srcdir@
+MODULE = imaadp32.acm
+IMPORTS = winmm user32 kernel32
+
+C_SRCS = imaadp32.c
+
+@MAKE_DLL_RULES@
+
+### Dependencies:
--- /dev/null
+@ stdcall DriverProc (long long long long long) ADPCM_DriverProc
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/* -*- 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;
+}
--- /dev/null
+/*
+ * 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"
--- /dev/null
+ 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
--- /dev/null
+@ 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
--- /dev/null
+<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>
--- /dev/null
+/* -*- 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;
+}
--- /dev/null
+/*\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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*\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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
+
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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.
+ */
+
+
--- /dev/null
+TOPSRCDIR = @top_srcdir@
+TOPOBJDIR = ../../..
+SRCDIR = @srcdir@
+VPATH = @srcdir@
+MODULE = msadp32.acm
+IMPORTS = winmm user32 kernel32
+
+C_SRCS = msadp32.c
+
+@MAKE_DLL_RULES@
+
+### Dependencies:
--- /dev/null
+@ stdcall DriverProc (long long long long long) ADPCM_DriverProc
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/* -*- 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;
+}
--- /dev/null
+/* -*- 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;
+}
--- /dev/null
+/* -*- 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+
+/*
+ * 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 */
+
+
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+; MSAFD.DLL - Ancillary Function Driver DLL
+
+LIBRARY msafd.dll
+
+EXPORTS
+WSPStartup@76
+
+; EOF
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+#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>
--- /dev/null
+<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>
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+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
--- /dev/null
+#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 */
--- /dev/null
+#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
+}
+
--- /dev/null
+<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>
--- /dev/null
+#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 */
--- /dev/null
+/* $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;
+}
+
--- /dev/null
+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
--- /dev/null
+/*
+ * 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},
+};
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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, ®Type, 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/* 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;
+}
+
--- /dev/null
+/* 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
+
+
+
+
+
--- /dev/null
+%{
+
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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 );
+ }
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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"
--- /dev/null
+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()
--- /dev/null
+<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>
--- /dev/null
+/*
+ * 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"
+}
--- /dev/null
+/*
+ * 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."
+}
--- /dev/null
+/*
+ * 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"
+}
--- /dev/null
+/*
+ * 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"
+}
--- /dev/null
+/*
+ * 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"
+}
--- /dev/null
+/*
+ * 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"
+}
--- /dev/null
+/*\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
--- /dev/null
+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[\83N \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¢"
+}
--- /dev/null
+/*
+ * 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¸¦ Æ÷ÇÔÇÏ´Â Æú´õ ¼±ÅÃ"
+}
--- /dev/null
+/*
+ * 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"
+}
--- /dev/null
+/*
+ * 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'."
+}
--- /dev/null
+/*
+ * 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"
+}
--- /dev/null
+/*
+ * 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"
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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 );
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/* 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;
+}
+
--- /dev/null
+/* 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
+
+
+
+
+
--- /dev/null
+%{
+
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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, §ion_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, §ion_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], §ion_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;
+}
--- /dev/null
+/*
+ * 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 );
+ }
+}
--- /dev/null
+/*
+** 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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"
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+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:
--- /dev/null
+@ 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()
--- /dev/null
+<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>
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/* $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 */
--- /dev/null
+; $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
--- /dev/null
+#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>
--- /dev/null
+<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>
--- /dev/null
+#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 */
--- /dev/null
+/*
+ * 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 );
+}
--- /dev/null
+; 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
--- /dev/null
+#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>
--- /dev/null
+<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>
--- /dev/null
+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:
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+#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>
--- /dev/null
+# 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
--- /dev/null
+<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>
--- /dev/null
+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
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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 = ®_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);
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/* $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 */
--- /dev/null
+; $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
--- /dev/null
+/* $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>
--- /dev/null
+<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>
--- /dev/null
+/* $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");
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/* 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);
+ }
+}
--- /dev/null
+/* 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__ */
--- /dev/null
+/* 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);
+ }
+}
--- /dev/null
+/* 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 */
--- /dev/null
+/* 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);
+}
--- /dev/null
+/* 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;
+}
--- /dev/null
+@ 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
--- /dev/null
+<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>
--- /dev/null
+/*
+ * 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
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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__ */
--- /dev/null
+/* 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+LIBRARY netcfgx.dll
+
+EXPORTS
+NetClassInstaller@12
+
+;EOF
\ No newline at end of file
--- /dev/null
+#include <windows.h>
+#include <setupapi.h>
+
+ULONG DbgPrint(PCH Format,...);
+
--- /dev/null
+<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>
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+// 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
--- /dev/null
+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
+
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+#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)
--- /dev/null
+#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"
--- /dev/null
+@ 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
--- /dev/null
+<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>
--- /dev/null
+#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 */
--- /dev/null
+/*
+ * 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;
+}