/*****************************************************************************
 *
 *  Valid.c
 *
 *  Copyright (c) 1996 Microsoft Corporation.  All Rights Reserved.
 *
 *  Abstract:
 *
 *      Validate services.  On a validation error that would not have
 *      been caught in retail, we throw an exception.
 *
 *  Contents:
 *
 *      fFullValidPhwnd
 *      fFullValidPpdw
 *      fFullValidPpfn
 *      fFullValidReadPx
 *      fFullValidWritePx
 *
 *****************************************************************************/

#include "dinputpr.h"

/*****************************************************************************
 *
 *  @doc    INTERNAL
 *
 *  @func   HRESULT | hresFullValidHwnd |
 *
 *          Validate a window handle completely.
 *
 *  @parm   HWND | hwnd |
 *
 *          Window handle to validate.
 *
 *  @parm   LPCSTR | s_szProc |
 *
 *          Name of calling procedure.
 *
 *  @parm   int | iarg |
 *
 *          Parameter index.  (First parameter is 1.)
 *
 *  @returns
 *
 *          <c S_OK> if the parameter is valid.
 *
 *          <c E_HANDLE> if the parameter is invalid.
 *
 *****************************************************************************/

STDMETHODIMP
hresFullValidHwnd_(HWND hwnd, LPCSTR s_szProc, int iarg)
{
    HRESULT hres;
    if (IsWindow(hwnd)) {
        hres = S_OK;
    } else {
        RPF("ERROR %s: arg %d: not a window handle", s_szProc, iarg);
        hres = E_HANDLE;
    }
    return hres;
}

/*****************************************************************************
 *
 *  @doc    INTERNAL
 *
 *  @func   HRESULT | hresFullValidPvCb_ |
 *
 *          Validate that a buffer is readable or writeable.
 *
 *  @parm   PV | pv |
 *
 *          Buffer address.
 *
 *  @parm   UINT | cb |
 *
 *          Size of buffer in bytes.
 *
 *  @parm   PFNBAD | pfnBad |
 *
 *          Function that determines whether the buffer is bad.
 *          Should be <f IsBadReadPtr> or <f IsBadWritePtr>.
 *
 *  @parm   LPCSTR | s_szProc |
 *
 *          Name of calling procedure.
 *
 *  @parm   int | iarg |
 *
 *          Parameter index.  (First parameter is 1.)
 *          High word indicates how many bytes should not be
 *          scrambled.
 *
 *  @returns
 *
 *          <c S_OK> if the parameter is valid.
 *
 *          <c E_POINTER> if the parameter is invalid.
 *
 *****************************************************************************/

typedef BOOL (WINAPI *PFNBAD)(PCV pv, UINT_PTR cb);

#ifndef XDEBUG

#define hresFullValidPvCb_(pv, cb, pfnBad, z, i)                    \
       _hresFullValidPvCb_(pv, cb, pfnBad)                          \

#endif

STDMETHODIMP
hresFullValidPvCb_(PCV pv, UINT cb, PFNBAD pfnBad, LPCSTR s_szProc, int iarg)
{
    HRESULT hres;
#if DIRECTINPUT_VERSION < 0x0400
    if (pfnBad(pv, cb)) {
#else
    if (!pfnBad(pv, LOWORD(cb))) {
#endif
        hres = S_OK;
    } else {
        RPF("ERROR %s: arg %d: invalid pointer", s_szProc, LOWORD(iarg));
        hres = E_POINTER;
    }
    return hres;
}

/*****************************************************************************
 *
 *  @doc    INTERNAL
 *
 *  @func   HRESULT | hresFullValidWritePvCb_ |
 *
 *          Validate that a buffer is writeable.  Also scrambles it
 *          if special goo doesn't need to be done.
 *
 *  @parm   PV | pv |
 *
 *          Buffer address.
 *
 *  @parm   UINT | cb |
 *
 *          Size of buffer in bytes.
 *
 *  @parm   LPCSTR | s_szProc |
 *
 *          Name of calling procedure.
 *
 *  @parm   int | iarg |
 *
 *          Parameter index.  (First parameter is 1.)
 *
 *  @returns
 *
 *          <c S_OK> if the parameter is valid.
 *
 *          <c E_POINTER> if the parameter is invalid.
 *
 *****************************************************************************/

STDMETHODIMP
hresFullValidWritePvCb_(PV pv, UINT cb, LPCSTR s_szProc, int iarg)
{
    HRESULT hres;
    hres = hresFullValidPvCb_(pv, cb, (PFNBAD)IsBadWritePtr, s_szProc, iarg);
#ifdef XDEBUG
    if (SUCCEEDED(hres) && HIWORD(iarg) == 0) {
        ScrambleBuf(pv, cb);
    }
#endif
    return hres;
}


/*****************************************************************************
 *
 *  @doc    INTERNAL
 *
 *  @func   HRESULT | hresFullValidReadPvCb_ |
 *
 *          Validate that a buffer is readable.
 *
 *  @parm   PV | pv |
 *
 *          Buffer address.
 *
 *  @parm   UINT | cb |
 *
 *          Size of buffer in bytes.
 *
 *  @parm   LPCSTR | s_szProc |
 *
 *          Name of calling procedure.
 *
 *  @parm   int | iarg |
 *
 *          Parameter index.  (First parameter is 1.)
 *
 *  @returns
 *
 *          <c S_OK> if the parameter is valid.
 *
 *          <c E_POINTER> if the parameter is invalid.
 *
 *****************************************************************************/

STDMETHODIMP
hresFullValidReadPvCb_(PCV pv, UINT cb, LPCSTR s_szProc, int iarg)
{
    return hresFullValidPvCb_(pv, cb, (PFNBAD)IsBadReadPtr, s_szProc, iarg);
}

/*****************************************************************************
 *
 *  @doc    INTERNAL
 *
 *  @func   HRESULT | hresFullValidPxCb_ |
 *
 *          Validate that a sized structure is readable or writeable.
 *
 *  @parm   PCV | pv |
 *
 *          Structure address.  The first field of the structure must
 *          be a <p dwSize>.  If the structure is being validated for
 *          writing, then all fields beyond the <p dwSize> are scrambled
 *          in XDEBUG.
 *
 *  @parm   UINT | cbHiLo |
 *
 *          Expected sizes of the structure.  One valid size is in the
 *          low word.  An optional alternate valid size is in the high
 *          word.  (The alternate valid size is needed because some
 *          structures changed size between DirectX 3 and DirectX 5.)
 *
 *  @parm   STRUCTPROC | pfnStruct |
 *
 *          Function which validates that a structure is readable or writable.
 *
 *  @parm   LPCSTR | s_szProc |
 *
 *          Name of calling procedure.
 *
 *  @parm   int | iarg |
 *
 *          Parameter index.  (First parameter is 1.)
 *
 *  @returns
 *
 *          <c S_OK> if the parameter is valid.
 *
 *          <c E_POINTER> if the buffer is not readable or writeable.
 *
 *          <c E_INVALIDARG> if the buffer size is incorrect.
 *
 *****************************************************************************/

typedef STDMETHOD(STRUCTPROC)(PCV pv, UINT cb
                                   RD(comma LPCSTR s_szProc comma int iarg));

#ifndef XDEBUG

#define hresFullValidPxCb_(pv, cbHiLo, pfnStruct, z, i)             \
       _hresFullValidPxCb_(pv, cbHiLo, pfnStruct)                   \

#endif

STDMETHODIMP
hresFullValidPxCb_(PCV pv, UINT cbHiLo, STRUCTPROC pfnStruct,
                   LPCSTR s_szProc, int iarg)
{
    HRESULT hres;

    /*
     *  Raymond frequently suffers a brain lapse and passes
     *  a cbX(LPMUMBLE) instead of a cbX(MUMBLE).
     */
    AssertF(LOWORD(cbHiLo) != cbX(DWORD));
    AssertF(HIWORD(cbHiLo) != cbX(DWORD));

    if (!IsBadReadPtr(pv, cbX(DWORD))) {

        DWORD cbIn = *(LPDWORD)pv;

        /*
         *  The leading "cbIn &&" prevents the HIWORD(cbHiLo)==0 case from
         *  accidentally allowing a size of zero to sneak past.
         */

        if (cbIn && (cbIn == LOWORD(cbHiLo) || cbIn == HIWORD(cbHiLo))) {

            hres = pfnStruct(pv, cbIn RD(comma s_szProc comma iarg));
            if (SUCCEEDED(hres)) {
                if (HIWORD(iarg)) {
                    ScrambleBuf(pvAddPvCb(pv, HIWORD(iarg)),
                                cbIn - HIWORD(iarg));
                }
            }
        } else {
            RPF("ERROR %s: arg %d: invalid dwSize", s_szProc, LOWORD(iarg));
            hres = E_INVALIDARG;
        }
    } else {
        RPF("ERROR %s: arg %d: invalid pointer", s_szProc, LOWORD(iarg));
        hres = E_POINTER;
    }

    return hres;
}


/*****************************************************************************
 *
 *  @doc    INTERNAL
 *
 *  @func   HRESULT | hresFullValidWritePxCb_ |
 *
 *          Validate that a sized structure is writeable.  The contents
 *          of the structure are scrambled before returning.
 *
 *  @parm   PV | pv |
 *
 *          Structure address.  The first field of the structure must
 *          be a <p dwSize>.
 *
 *  @parm   UINT | cbHiLo |
 *
 *          Expected sizes of the structure.  One valid size is in the
 *          low word.  An optional alternate valid size is in the high
 *          word.  (The alternate valid size is needed because some
 *          structures changed size between DirectX 3 and DirectX 5.)
 *
 *  @parm   LPCSTR | s_szProc |
 *
 *          Name of calling procedure.
 *
 *  @parm   int | iarg |
 *
 *          Parameter index.  (First parameter is 1.)
 *
 *  @returns
 *
 *          <c S_OK> if the parameter is valid.
 *
 *          <c E_POINTER> if the buffer is not writeable.
 *
 *          <c E_INVALIDARG> if the buffer size is incorrect.
 *
 *****************************************************************************/

STDMETHODIMP
hresFullValidWritePxCb_(PV pv, UINT cb, LPCSTR s_szProc, int iarg)
{
    /*
     *  We need to distinguish hresFullValidWritePvCb_ and
     *  _hresFullValidWritePvCb_ manually, because the preprocessor
     *  gets confused.
     *
     *  We also need to put a cbX(DWORD) into the high word of the iarg
     *  so that the size field won't get demolished.
     */
#ifdef XDEBUG
    return hresFullValidPxCb_(pv, cb, (STRUCTPROC)hresFullValidWritePvCb_,
                                      s_szProc, MAKELONG(iarg, cbX(DWORD)));
#else
    return hresFullValidPxCb_(pv, cb, (STRUCTPROC)_hresFullValidWritePvCb_,
                                      s_szProc, iarg);
#endif
}

#ifdef XDEBUG

/*****************************************************************************
 *
 *  @doc    INTERNAL
 *
 *  @func   HRESULT | hresFullValidWriteNoScramblePxCb_ |
 *
 *          Validate that a sized structure is writeable.  The contents
 *          of the structure are not scrambled.
 *
 *  @parm   PV | pv |
 *
 *          Structure address.  The first field of the structure must
 *          be a <p dwSize>.
 *
 *  @parm   UINT | cbHiLo |
 *
 *          Expected sizes of the structure.  One valid size is in the
 *          low word.  An optional alternate valid size is in the high
 *          word.  (The alternate valid size is needed because some
 *          structures changed size between DirectX 3 and DirectX 5.)
 *
 *  @parm   LPCSTR | s_szProc |
 *
 *          Name of calling procedure.
 *
 *  @parm   int | iarg |
 *
 *          Parameter index.  (First parameter is 1.)
 *
 *  @returns
 *
 *          <c S_OK> if the parameter is valid.
 *
 *          <c E_POINTER> if the buffer is not writeable.
 *
 *          <c E_INVALIDARG> if the buffer size is incorrect.
 *
 *****************************************************************************/

STDMETHODIMP
hresFullValidWriteNoScramblePxCb_(PV pv, UINT cb, LPCSTR s_szProc, int iarg)
{
    return hresFullValidPxCb_(pv, cb, (STRUCTPROC)hresFullValidWritePvCb_,
                                      s_szProc, MAKELONG(iarg, cb));
}
#endif


/*****************************************************************************
 *
 *  @doc    INTERNAL
 *
 *  @func   HRESULT | hresFullValidReadPxCb_ |
 *
 *          Validate that a sized structure is readable.
 *
 *  @parm   PV | pv |
 *
 *          Structure address.  The first field of the structure must
 *          be a <p dwSize>.
 *
 *  @parm   UINT | cbHiLo |
 *
 *          Expected sizes of the structure.  One valid size is in the
 *          low word.  An optional alternate valid size is in the high
 *          word.  (The alternate valid size is needed because some
 *          structures changed size between DirectX 3 and DirectX 5.)
 *
 *  @parm   LPCSTR | s_szProc |
 *
 *          Name of calling procedure.
 *
 *  @parm   int | iarg |
 *
 *          Parameter index.  (First parameter is 1.)
 *
 *  @returns
 *
 *          <c S_OK> if the parameter is valid.
 *
 *          <c E_POINTER> if the buffer is not readable.
 *
 *          <c E_INVALIDARG> if the buffer size is incorrect.
 *
 *****************************************************************************/

STDMETHODIMP
hresFullValidReadPxCb_(PCV pv, UINT cb, LPCSTR s_szProc, int iarg)
{
    /*
     *  We need to distinguish hresFullValidReadPvCb_ and
     *  _hresFullValidReadPvCb_ manually, because the preprocessor
     *  gets confused.
     */
#ifdef XDEBUG
    return hresFullValidPxCb_(pv, cb, hresFullValidReadPvCb_, s_szProc, iarg);
#else
    return hresFullValidPxCb_(pv, cb, _hresFullValidReadPvCb_, s_szProc, iarg);
#endif
}

/*****************************************************************************
 *
 *  @doc    INTERNAL
 *
 *  @func   HRESULT | hresFullValidFl_ |
 *
 *          Validate that no invalid flags are passed.
 *
 *  @parm   DWORD | fl |
 *
 *          Flags passed by the caller.
 *
 *  @parm   DWORD | flV |
 *
 *          Flags which are valid.
 *
 *  @parm   LPCSTR | s_szProc |
 *
 *          Name of calling procedure.
 *
 *  @parm   int | iarg |
 *
 *          Parameter index.  (First parameter is 1.)
 *
 *  @returns
 *
 *          <c S_OK> if the parameter is valid.
 *
 *          <c E_INVALIDARG> if the parameter is invalid.
 *
 *****************************************************************************/

STDMETHODIMP
hresFullValidFl_(DWORD fl, DWORD flV, LPCSTR s_szProc, int iarg)
{
    HRESULT hres;
    if ((fl & ~flV) == 0) {
        hres = S_OK;
    } else {
        RPF("ERROR %s: arg %d: invalid flags", s_szProc, iarg);
        hres = E_INVALIDARG;
    }
    return hres;
}

/*****************************************************************************
 *
 *  @doc    INTERNAL
 *
 *  @func   HRESULT | hresFullValidPfn_ |
 *
 *          Validate that the parameter is a valid code pointer.
 *
 *          Actually, <f IsValidCodePtr> on Win32 is broken, but
 *          tough.
 *
 *  @parm   FARPROC | pfn |
 *
 *          Procedure to "validate".
 *
 *  @parm   LPCSTR | s_szProc |
 *
 *          Name of calling procedure.
 *
 *  @parm   int | iarg |
 *
 *          Parameter index.  (First parameter is 1.)
 *
 *  @returns
 *
 *          <c S_OK> if the parameter is valid.
 *
 *          <c E_INVALIDARG> if the parameter is invalid.
 *
 *****************************************************************************/

STDMETHODIMP
hresFullValidPfn_(FARPROC pfn, LPCSTR s_szProc, int iarg)
{
    HRESULT hres;
    if (!IsBadCodePtr(pfn)) {
        hres = S_OK;
    } else {
        RPF("ERROR %s: arg %d: invalid callback address", s_szProc, iarg);
        hres = E_INVALIDARG;
    }
    return hres;
}

/*****************************************************************************
 *
 *  @doc    INTERNAL
 *
 *  @func   HRESULT | hresFullValidPitf_ |
 *
 *          Validate that the parameter is an interface pointer.
 *
 *          We don't look at it very hard.
 *
 *  @parm   PUNK | punk |
 *
 *          <i IUnknown> to "validate".
 *
 *  @parm   LPCSTR | s_szProc |
 *
 *          Name of calling procedure.
 *
 *  @parm   int | iarg |
 *
 *          Parameter index.  (First parameter is 1.)
 *
 *  @returns
 *
 *          <c S_OK> if the parameter is valid.
 *
 *          <c E_POINTER> if the pointer itself is bogus.
 *
 *          <c E_INVALIDARG> if something inside the pointer is bogus.
 *
 *****************************************************************************/

STDMETHODIMP
hresFullValidPitf_(PUNK punk, LPCSTR s_szProc, int iarg)
{
    HRESULT hres;

    if (!IsBadReadPtr(punk, cbX(*punk))) {
        IUnknownVtbl *pvtbl = punk->lpVtbl;
        if (!IsBadReadPtr(pvtbl, cbX(*pvtbl))) {
            if (!IsBadCodePtr((FARPROC)pvtbl->QueryInterface) &&
                !IsBadCodePtr((FARPROC)pvtbl->AddRef) &&
                !IsBadCodePtr((FARPROC)pvtbl->Release)) {
                hres = S_OK;
            } else {
                RPF("ERROR %s: arg %d: invalid pointer", s_szProc, iarg);
                hres = E_INVALIDARG;
            }
        } else {
            RPF("ERROR %s: arg %d: invalid pointer", s_szProc, iarg);
            hres = E_INVALIDARG;
        }
    } else {
        RPF("ERROR %s: arg %d: invalid pointer", s_szProc, iarg);
        hres = E_POINTER;
    }

    return hres;
}

/*****************************************************************************
 *
 *  @doc    INTERNAL
 *
 *  @func   HRESULT | hresFullValidPcbOut_ |
 *
 *          Validate that the parameter is a valid place to stick an
 *          output result.  We also smas it to zero.
 *
 *  @parm   PV | pcb |
 *
 *          Pointer to "validate".
 *
 *  @parm   UINT | cb |
 *
 *          Size of data pcb points to.
 *
 *  @parm   LPCSTR | s_szProc |
 *
 *          Name of calling procedure.
 *
 *  @parm   int | iarg |
 *
 *          Parameter index.  (First parameter is 1.)
 *
 *  @returns
 *
 *          <c S_OK> if the parameter is valid.
 *
 *          <c E_POINTER> if the pointer itself is bogus.
 *
 *****************************************************************************/

STDMETHODIMP
hresFullValidPcbOut_(PV pcb, UINT cb, LPCSTR s_szProc, int iarg)
{
    HRESULT hres;
    if (!IsBadWritePtr(pcb, cb)) {
        memset(pcb,0,cb);
        hres = S_OK;
    } else {
        RPF("ERROR %s: arg %d: invalid pointer", s_szProc, iarg);
        hres = E_POINTER;
    }
    return hres;
}


/*****************************************************************************
 *
 *  @doc    INTERNAL
 *
 *  @func   HRESULT | hresFullValidReadStrA_ |
 *
 *          Validate that the parameter is a valid readable
 *          ANSI string of maximum length <p cch>.
 *
 *          Note that we cannot use <f IsBadStringPtr> because
 *          <f IsBadStringPtr> handles the "string too long"
 *          case incorrectly.  Instead, we use <f lstrlenA>.
 *
 *  @parm   LPCSTR | psz |
 *
 *          String to "validate".
 *
 *  @parm   UINT | cch |
 *
 *          Maximum string length, including null terminator.
 *
 *  @parm   LPCSTR | s_szProc |
 *
 *          Name of calling procedure.
 *
 *  @parm   int | iarg |
 *
 *          Parameter index.  (First parameter is 1.)
 *
 *  @returns
 *
 *          <c S_OK> if the parameter is valid.
 *
 *          <c E_INVALIDARG> if the pointer itself is bogus.
 *
 *****************************************************************************/

STDMETHODIMP
hresFullValidReadStrA_(LPCSTR psz, UINT cch, LPCSTR s_szProc, int iarg)
{
    HRESULT hres;
    UINT cchT;

    /*
     *  lstrlenA returns 0 if the parameter is invalid.
     *  It also returns 0 if the string is null.
     */
    cchT = (UINT)lstrlenA(psz);

    if (cchT == 0) {
        /*
         *  The ambiguous case.  See if it's really a null string.
         */
        if (IsBadReadPtr(psz, cbCch(1)) || psz[0]) {
            RPF("ERROR %s: arg %d: invalid ANSI string", s_szProc, iarg);
            hres = E_INVALIDARG;
        } else {
            hres = S_OK;
        }
    } else if (cchT < cch) {
        hres = S_OK;
    } else {
        RPF("ERROR %s: arg %d: invalid ANSI string", s_szProc, iarg);
        hres = E_INVALIDARG;
    }
    return hres;
}

/*****************************************************************************
 *
 *  @doc    INTERNAL
 *
 *  @func   HRESULT | hresFullValidReadStrW_ |
 *
 *          Validate that the parameter is a valid readable
 *          UNICODE string of maximum length <p cwch>.
 *
 *          Note that we cannot use <f IsBadStringPtr> because
 *          <f IsBadStringPtr> handles the "string too long"
 *          case incorrectly.  Instead, we use <f lstrlenW>.
 *
 *  @parm   LPCWSTR | pwsz |
 *
 *          String to "validate".
 *
 *  @parm   UINT | cwch |
 *
 *          Maximum string length, including null terminator.
 *
 *  @parm   LPCSTR | s_szProc |
 *
 *          Name of calling procedure.
 *
 *  @parm   int | iarg |
 *
 *          Parameter index.  (First parameter is 1.)
 *
 *  @returns
 *
 *          <c S_OK> if the parameter is valid.
 *
 *          <c E_INVALIDARG> if the pointer itself is bogus.
 *
 *****************************************************************************/

STDMETHODIMP
hresFullValidReadStrW_(LPCWSTR pwsz, UINT cwch, LPCSTR s_szProc, int iarg)
{
    HRESULT hres;
    UINT cwchT;

    hres = E_INVALIDARG;
    /*
     *  lstrlenW returns 0 if the parameter is invalid.
     *  It also returns 0 if the string is null.
     */
    cwchT = (UINT)lstrlenW(pwsz);

    if (cwchT == 0) {
        /*
         *  The ambiguous case.  See if it's really a null string.
         */
        if (IsBadReadPtr(pwsz, cbCwch(1)) || pwsz[0]) {
            RPF("ERROR %s: arg %d: invalid UNICODE string", s_szProc, iarg);
            hres = E_INVALIDARG;
        } else {
            hres = S_OK;
        }
    } else if (cwchT < cwch) {
        hres = S_OK;
    } else {
        RPF("ERROR %s: arg %d: invalid UNICODE string", s_szProc, iarg);
        hres = E_INVALIDARG;
    }
    return hres;
}

/*****************************************************************************
 *
 *  @doc    INTERNAL
 *
 *  @func   HRESULT | hresFullValidPesc_ |
 *
 *          Validate that the parameter is a valid <t DIEFFESCAPE>
 *          structure.
 *
 *          This is merely a wrapper around other validation methods.
 *
 *  @parm   LPDIEFFESCAPE | pesc |
 *
 *          Structure to "validate".
 *
 *  @returns
 *
 *          <c S_OK> if the parameter is valid.
 *
 *          <c E_INVALIDARG> if the pointer itself is bogus.
 *
 *****************************************************************************/

STDMETHODIMP
hresFullValidPesc_(LPDIEFFESCAPE pesc, LPCSTR s_szProc, int iarg)
{
    HRESULT hres;

    if (SUCCEEDED(hres = hresFullValidWriteNoScramblePxCb(pesc, DIEFFESCAPE,
                                                          iarg)) &&
        SUCCEEDED(hres = hresFullValidReadPvCb(pesc->lpvInBuffer,
                                               pesc->cbInBuffer, iarg)) &&
        SUCCEEDED(hres = hresFullValidWriteNoScramblePvCb(pesc->lpvOutBuffer,
                                                pesc->cbOutBuffer, iarg))) {
    } else {
    }

    return hres;
}