//
// COM.C
// Utility functions
//
// Copyright(c) Microsoft 1997-
//

#include <as16.h>


//
// PostMessageNoFail()
// This makes sure posted messages don't get lost on Win95 due to the fixed
// interrupt queue.  Conveniently, USER exports PostPostedMessages for
// KERNEL to flush the queue, so we call that before calling PostMessage().
//
void PostMessageNoFail(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    PostPostedMessages();
    PostMessage(hwnd, uMsg, wParam, lParam);
}


//
// AnsiToUni()
//
// UniToAnsi() is conveniently exported by krnl386.  However, we need 
// AnsiToUni() conversions also so we can make sure we end up where we
// started.  This one we have to roll our own thunk for
//
int AnsiToUni
(
    LPSTR   lpAnsi,
    int     cchAnsi,
    LPWSTR  lpUni,
    int     cchUni
)
{
    DWORD   dwMask;
    LONG    lReturn;

    DebugEntry(AnsiToUni);

    ASSERT(g_lpfnAnsiToUni);
    ASSERT(SELECTOROF(lpAnsi));
    ASSERT(SELECTOROF(lpUni));

    //
    // Set up the mask.  These are the params:
    //      0   --  CodePage (CP_ACP, which is 0L)
    //      1   --  Flags (0L)
    //      2   --  lpAnsi              POINTER
    //      3   --  cchAnsi
    //      4   --  lpUni               POINTER
    //      5   --  cchUni
    //
    //
    dwMask = (1L << 2) | (1L << 4);

    //
    // Take the win16lock an extra time; this API will release it, and we
    // can't yield in the middle of a GDI call.
    //
    _EnterWin16Lock();
    lReturn = CallProcEx32W(6, dwMask, (DWORD)g_lpfnAnsiToUni, 0L, 0L, lpAnsi,
        (DWORD)(UINT)cchAnsi, lpUni, (DWORD)(UINT)cchUni);
    _LeaveWin16Lock();

    DebugExitDWORD(AnsiToUni, (DWORD)lReturn);
    return((int)lReturn);
}


//
// PATCHING CODE
//


//
// Get32BitOnlyExport16()
//
// This function gets hold of a 16:16 function address that isn't exported
// but is called via a flat thunk from the exported 32-bit version.  We use
// this for GDI and USER functions that are handy.
//
// This code assumes that the 32-bit routine looks like the following:
//      <DEBUG>
//          68 dwStr32          Push string
//          E8 dwOffsetOut      Call trace out
//      <DEBUG AND RETAIL>
//          B1 bIndex           Put offset in thunk table into cl
//        OR
//          66 B9 wIndex        Put offset in thunk table into cx
//          
//          EB bOffset          Jmp to common flat thunk routine
//        OR
//          66 ED wOffset       Jmp word to common flat thunk routine
//        OR
//          prolog of common flat thunk routine
//          
BOOL GetGdi32OnlyExport
(
    LPSTR   lpszExport32,
    UINT    cbJmpOffset,
    FARPROC FAR* lplpfn16
)
{
    BOOL    rc;

    DebugEntry(GetGdi32OnlyExport);

    rc = Get32BitOnlyExport(GetProcAddress32(g_hInstGdi32, lpszExport32),
        cbJmpOffset, FT_GdiFThkThkConnectionData, lplpfn16);

    DebugExitBOOL(GetGdi32OnlyExport, rc);
    return(rc);
}


BOOL GetUser32OnlyExport
(
    LPSTR   lpszExport32,
    FARPROC FAR* lplpfn16
)
{
    BOOL    rc;

    DebugEntry(GetUser32OnlyExport);

    rc = Get32BitOnlyExport(GetProcAddress32(g_hInstUser32, lpszExport32),
        0, FT_UsrFThkThkConnectionData, lplpfn16);

    DebugExitBOOL(GetUser32OnlyExport, rc);
    return(rc);
}



BOOL Get32BitOnlyExport
(
    DWORD   dwfn32,
    UINT    cbJmpOffset,
    LPDWORD lpThunkTable,
    FARPROC FAR* lplpfn16
)
{
    LPBYTE  lpfn32;
    UINT    offsetThunk;

    DebugEntry(Get32BitOnlyExport);

    ASSERT(lplpfn16);
    *lplpfn16 = NULL;

    //
    // The thunk table pointer points to two DWORDs.  The first is a
    // checksum signature.  The second is the pointer to the target
    // function array.
    //
    ASSERT(!IsBadReadPtr(lpThunkTable, 2*sizeof(DWORD)));
    lpThunkTable = (LPDWORD)lpThunkTable[1];
    ASSERT(!IsBadReadPtr(lpThunkTable, sizeof(DWORD)));

    //
    // Get 16:16 pointer to export
    //
    lpfn32 = NULL;

    if (!dwfn32)
    {
        ERROR_OUT(("Missing 32-bit export"));
        DC_QUIT;
    }

    lpfn32 = MapLS((LPVOID)dwfn32);
    if (!SELECTOROF(lpfn32))
    {
        ERROR_OUT(("Out of selectors"));
        DC_QUIT;
    }

    //
    // Was a jmp offset passed in?  If so, decode the instruction there.
    // It should be a jmp <dword offset>.  Figure out what EIP would be
    // if we jumped there. That's the place the flat thunk occurs.  
    // Currently, only PolyPolyline needs this.
    //
    if (cbJmpOffset)
    {
        if (IsBadReadPtr(lpfn32, cbJmpOffset+5) ||
            (lpfn32[cbJmpOffset] != OPCODE32_JUMP4))
        {
            ERROR_OUT(("Can't read 32-bit export"));
            DC_QUIT;
        }

        //
        // Add dword at cbJmpOffset+1, add this number to (lpfn32+cbJmpOffset+
        // 5), which is the EIP of the next instruction after the jump.  This
        // produces the EIP of the real thunk stub.
        //
        dwfn32 += cbJmpOffset + 5 + *(LPDWORD)(lpfn32+cbJmpOffset+1);

        UnMapLS(lpfn32);
        lpfn32 = MapLS((LPVOID)dwfn32);
        if (!SELECTOROF(lpfn32))
        {
            ERROR_OUT(("Out of selectors"));
            DC_QUIT;
        }
    }

    //
    // Verify that we can read 13 bytes.  The reason this will won't go 
    // past the end in a legitimate case is that this thunklet is either
    // followed by the large # of bytes in the common flat thunk routine,
    // or by another thunklet
    //
    if (IsBadReadPtr(lpfn32, 13))
    {
        ERROR_OUT(("Can't read code in 32-bit export"));
        DC_QUIT;
    }

    //
    // Does this have the 10-byte DEBUG prolog?
    //
    if (*lpfn32 == OPCODE32_PUSH)
    {
        // Yes, skip it
        lpfn32 += 5;

        // Make sure that next thing is a call
        if (*lpfn32 != OPCODE32_CALL)
        {
            ERROR_OUT(("Can't read code in 32-bit export"));
            DC_QUIT;
        }

        lpfn32 += 5;
    }

    //
    // This should either be mov cl, byte or mov cx, word
    //
    if (*lpfn32 == OPCODE32_MOVCL)
    {
        offsetThunk = *(lpfn32+1);
    }
    else if (*((LPWORD)lpfn32) == OPCODE32_MOVCX)
    {
        //
        // NOTE:  Even though this is a CX offset, the thunk code only
        // looks at the low BYTE
        //
        offsetThunk = *(lpfn32+2);
    }
    else
    {
        ERROR_OUT(("Can't read code in 32-bit export"));
        DC_QUIT;
    }

    //
    // Now, can we read this value?
    //
    if (IsBadReadPtr(lpThunkTable+offsetThunk, sizeof(DWORD)) ||
        IsBadCodePtr((FARPROC)lpThunkTable[offsetThunk]))
    {
        ERROR_OUT(("Can't read thunk table entry"));
        DC_QUIT;
    }

    *lplpfn16 = (FARPROC)lpThunkTable[offsetThunk];

DC_EXIT_POINT:
    if (SELECTOROF(lpfn32))
    {
        UnMapLS(lpfn32);
    }
    DebugExitBOOL(Get32BitOnlyExport16, (*lplpfn16 != NULL));
    return(*lplpfn16 != NULL);
}




//
// CreateFnPatch()
// This sets things up to be able to quickly patch/unpatch a system routine.
// The patch is not originally enabled.
//
UINT CreateFnPatch
(
    LPVOID      lpfnToPatch,
    LPVOID      lpfnJumpTo,
    LPFN_PATCH  lpbPatch,
    UINT        uCodeAlias
)
{
    SEGINFO     segInfo;
    UINT        ib;

    DebugEntry(CreateFnPatch);

    ASSERT(lpbPatch->lpCodeAlias == NULL);
    ASSERT(lpbPatch->wSegOrg == 0);

    //
    // Do NOT call IsBadReadPtr() here, that will cause the segment, if
    // not present, to get pulled in, and will masks problems in the debug
    // build that will show up in retail.
    //
    // Fortunately, PrestoChangoSelector() will set the linear address and
    // limit and attributes of our read/write selector properly.
    //

    //
    // Call GetCodeInfo() to check out bit-ness of code segment.  If 32-bit
    // we need to use the 16-bit override opcode for a far 16:16 jump.
    //
    segInfo.flags = 0;
    GetCodeInfo(lpfnToPatch, &segInfo);
    if (segInfo.flags & NSUSE32)
    {
        WARNING_OUT(("Patching 32-bit code segment 0x%04x:0x%04x", SELECTOROF(lpfnToPatch), OFFSETOF(lpfnToPatch)));
        lpbPatch->f32Bit = TRUE;
    }

    //
    // We must fix the codeseg in linear memory, or our shadow will end up
    // pointing somewhere if the original moves.  PolyBezier and SetPixel
    // are in moveable code segments for example.
    //
    // So save this away.  We will fix it when the patch is enabled.
    //
    lpbPatch->wSegOrg = SELECTOROF(lpfnToPatch);

    if (uCodeAlias)
    {
        //
        // We are going to share an already allocated selector.  Note that
        // this only works if the code segments of the two patched functions
        // are identical.  We verify this by the base address in an assert
        // down below.
        //
        lpbPatch->fSharedAlias = TRUE;
    }
    else
    {
        //
        // Create a selector with read-write attributes to alias the read-only
        // code function.  Using the original will set the limit of our
        // selector to the same as that of the codeseg, with the same
        // attributes but read-write.
        //
        uCodeAlias = AllocSelector(SELECTOROF(lpfnToPatch));
        if (!uCodeAlias)
        {
            ERROR_OUT(("CreateFnPatch: Unable to create alias selector"));
            DC_QUIT;
        }
        uCodeAlias = PrestoChangoSelector(SELECTOROF(lpfnToPatch), uCodeAlias);
    }

    lpbPatch->lpCodeAlias = MAKELP(uCodeAlias, OFFSETOF(lpfnToPatch));

    //
    // Create the N patch bytes (jmp far16 Seg:Function) of the patch
    //
    ib = 0;
    if (lpbPatch->f32Bit)
    {
        lpbPatch->rgbPatch[ib++] = OPCODE32_16OVERRIDE;
    }
    lpbPatch->rgbPatch[ib++] = OPCODE_FARJUMP16;
    lpbPatch->rgbPatch[ib++] = LOBYTE(OFFSETOF(lpfnJumpTo));
    lpbPatch->rgbPatch[ib++] = HIBYTE(OFFSETOF(lpfnJumpTo));
    lpbPatch->rgbPatch[ib++] = LOBYTE(SELECTOROF(lpfnJumpTo));
    lpbPatch->rgbPatch[ib++] = HIBYTE(SELECTOROF(lpfnJumpTo));

    lpbPatch->fActive  = FALSE;
    lpbPatch->fEnabled = FALSE;
     
DC_EXIT_POINT:
    DebugExitBOOL(CreateFnPatch, uCodeAlias);
    return(uCodeAlias);
}



//
// DestroyFnPatch()
// This frees any resources used when creating a function patch.  The
// alias data selector to the codeseg for writing purposes is it.
//
void DestroyFnPatch(LPFN_PATCH lpbPatch)
{
    DebugEntry(DestroyFnPatch);

    //
    // First, disable the patch if in use
    //
    if (lpbPatch->fActive)
    {
        TRACE_OUT(("Destroying active patch"));
        EnableFnPatch(lpbPatch, PATCH_DEACTIVATE);
    }

    //
    // Second, free the alias selector if we allocated one
    //
    if (lpbPatch->lpCodeAlias)
    {
        if (!lpbPatch->fSharedAlias)
        {
            FreeSelector(SELECTOROF(lpbPatch->lpCodeAlias));
        }
        lpbPatch->lpCodeAlias = NULL;
    }

    //
    // Third, clear this to fine cleanup problems
    //
    lpbPatch->wSegOrg = 0;
    lpbPatch->f32Bit  = FALSE;

    DebugExitVOID(DestroyFnPatch);
}




//
// EnableFnPatch()
// This actually patches the function to jump to our routine using the
// info saved when created.
//
// THIS ROUTINE MAY GET CALLED AT INTERRUPT TIME.  YOU CAN NOT USE ANY
// EXTERNAL FUNCTION, INCLUDING DEBUG TRACING/ASSERTS.
//
#define SAFE_ASSERT(x)       if (!lpbPatch->fInterruptable) { ASSERT(x); }

void EnableFnPatch(LPFN_PATCH lpbPatch, UINT flags)
{
    UINT    ib;
    UINT    cbPatch;

    SAFE_ASSERT(lpbPatch->lpCodeAlias);
    SAFE_ASSERT(lpbPatch->wSegOrg);

    //
    // Make sure the original and the alias are pointing to the same
    // linear memory.  We don't do this when not enabling/disabling for calls,
    // only when starting/stopping the patches
    //

    //
    // If enabling for the first time, fix BEFORE bytes are copied
    //
    if ((flags & ENABLE_MASK) == ENABLE_ON)
    {
        //
        // We need to fix the original code segment so it won't move in
        // linear memory.  Note that we set the selector base of our alias
        // even though several patches (not too many) may share one.  The 
        // extra times are harmless, and this prevents us from having to order
        // patches in our array in such a way that the original precedes the
        // shared ones, and walking our array in opposite orders to enable or
        // disable.
        //
        // And GlobalFix() just bumps up a lock count, so again, it's OK to do
        // this multiple times.
        //

        //
        // WE KNOW THIS CODE DOESN'T EXECUTE AT INTERRUPT TIME.
        //
        ASSERT(!lpbPatch->fEnabled);
        ASSERT(!lpbPatch->fActive);

        if (!lpbPatch->fActive)
        {
            lpbPatch->fActive = TRUE;

            //
            // Make sure this segment gets pulled in if discarded.  GlobalFix()
            // fails if not, and worse we'll fault the second we try to write 
            // to or read from the alias.  We do this by grabbing the 1st word
            // from the original.
            //
            // GlobalFix will prevent the segment from being discarded until
            // GlobalUnfix() happens.
            //
            ib = *(LPUINT)MAKELP(lpbPatch->wSegOrg, OFFSETOF(lpbPatch->lpCodeAlias));
            GlobalFix((HGLOBAL)lpbPatch->wSegOrg);
            SetSelectorBase(SELECTOROF(lpbPatch->lpCodeAlias), GetSelectorBase(lpbPatch->wSegOrg));
        }
    }

    if (lpbPatch->fInterruptable)
    {
        //
        // If this is for starting/stopping the patch, we have to disable
        // interrupts around the byte copying.  Or we could die if the
        // interrupt handler happens in the middle.
        //
        // We don't need to do this when disabling/enabling to call through
        // to the original, since we know that reflected interrupts aren't
        // nested, and the interrupt will complete before we come back to 
        // the interrupted normal app code.
        //
        if (!(flags & ENABLE_FORCALL))
        {
            _asm cli
        }
    }

    SAFE_ASSERT(GetSelectorBase(SELECTOROF(lpbPatch->lpCodeAlias)) == GetSelectorBase(lpbPatch->wSegOrg));

    if (lpbPatch->f32Bit)
    {
        cbPatch = CB_PATCHBYTES32;
    }
    else
    {
        cbPatch = CB_PATCHBYTES16;
    }

    if (flags & ENABLE_ON)
    {
        SAFE_ASSERT(lpbPatch->fActive);
        SAFE_ASSERT(!lpbPatch->fEnabled);

        if (!lpbPatch->fEnabled)
        {
            //
            // Save the function's original first N bytes, and copy the jump far16
            // patch in.
            //
            for (ib = 0; ib < cbPatch; ib++)
            {
                lpbPatch->rgbOrg[ib]        = lpbPatch->lpCodeAlias[ib];
                lpbPatch->lpCodeAlias[ib]   = lpbPatch->rgbPatch[ib];
            }

            lpbPatch->fEnabled = TRUE;
        }
    }
    else
    {
        SAFE_ASSERT(lpbPatch->fActive);
        SAFE_ASSERT(lpbPatch->fEnabled);

        if (lpbPatch->fEnabled)
        {
            //
            // Put back the function's original first N bytes
            //
            for (ib = 0; ib < cbPatch; ib++)
            {
                lpbPatch->lpCodeAlias[ib] = lpbPatch->rgbOrg[ib];
            }

            lpbPatch->fEnabled = FALSE;
        }
    }

    SAFE_ASSERT(GetSelectorBase(SELECTOROF(lpbPatch->lpCodeAlias)) == GetSelectorBase(lpbPatch->wSegOrg));

    if (lpbPatch->fInterruptable)
    {
        //
        // Reenable interrupts
        //
        if (!(flags & ENABLE_FORCALL))
        {
            _asm sti
        }

    }

    //
    // If really stopping, unfix AFTER the bytes have been copied.  This will
    // bump down a lock count, so when all patches are disabled, the original
    // code segment will be able to move.
    //
    if ((flags & ENABLE_MASK) == ENABLE_OFF)
    {
        //
        // WE KNOW THIS CODE DOESN'T EXECUTE AT INTERRUPT TIME.
        //

        ASSERT(!lpbPatch->fEnabled);
        ASSERT(lpbPatch->fActive);

        if (lpbPatch->fActive)
        {
            lpbPatch->fActive = FALSE;
            GlobalUnfix((HGLOBAL)lpbPatch->wSegOrg);
        }
    }
}


//
// LIST MANIPULATION ROUTINES
//

//
// COM_BasedListInsertBefore(...)
//
// See com.h for description.
//
void COM_BasedListInsertBefore(PBASEDLIST pExisting, PBASEDLIST pNew)
{
    PBASEDLIST  pTemp;

    DebugEntry(COM_BasedListInsertBefore);

    //
    // Check for bad parameters.
    //
    ASSERT((pNew != NULL));
    ASSERT((pExisting != NULL));

    //
    // Find the item before pExisting:
    //
    pTemp = COM_BasedPrevListField(pExisting);
    ASSERT((pTemp != NULL));

    TRACE_OUT(("Inserting item at %#lx into list between %#lx and %#lx",
                 pNew, pTemp, pExisting));

    //
    // Set its <next> field to point to the new item
    //
    pTemp->next = PTRBASE_TO_OFFSET(pNew, pTemp);
    pNew->prev  = PTRBASE_TO_OFFSET(pTemp, pNew);

    //
    // Set <prev> field of pExisting to point to new item:
    //
    pExisting->prev = PTRBASE_TO_OFFSET(pNew, pExisting);
    pNew->next      = PTRBASE_TO_OFFSET(pExisting, pNew);

    DebugExitVOID(COM_BasedListInsertBefore);
} // COM_BasedListInsertBefore


//
// COM_BasedListInsertAfter(...)
//
// See com.h for description.
//
void COM_BasedListInsertAfter(PBASEDLIST pExisting, PBASEDLIST pNew)
{
    PBASEDLIST  pTemp;

    DebugEntry(COM_BasedListInsertAfter);

    //
    // Check for bad parameters.
    //
    ASSERT((pNew != NULL));
    ASSERT((pExisting != NULL));

    //
    // Find the item after pExisting:
    //
    pTemp = COM_BasedNextListField(pExisting);
    ASSERT((pTemp != NULL));

    TRACE_OUT(("Inserting item at %#lx into list between %#lx and %#lx",
                 pNew, pExisting, pTemp));

    //
    // Set its <prev> field to point to the new item
    //
    pTemp->prev = PTRBASE_TO_OFFSET(pNew, pTemp);
    pNew->next  = PTRBASE_TO_OFFSET(pTemp, pNew);

    //
    // Set <next> field of pExisting to point to new item:
    //
    pExisting->next = PTRBASE_TO_OFFSET(pNew, pExisting);
    pNew->prev      = PTRBASE_TO_OFFSET(pExisting, pNew);

    DebugExitVOID(COM_BasedListInsertAfter);
} // COM_BasedListInsertAfter


//
// COM_BasedListRemove(...)
//
// See com.h for description.
//
void COM_BasedListRemove(PBASEDLIST pListItem)
{
    PBASEDLIST pNext     = NULL;
    PBASEDLIST pPrev     = NULL;

    DebugEntry(COM_BasedListRemove);

    //
    // Check for bad parameters.
    //
    ASSERT((pListItem != NULL));

    pPrev = COM_BasedPrevListField(pListItem);
    pNext = COM_BasedNextListField(pListItem);

    ASSERT((pPrev != NULL));
    ASSERT((pNext != NULL));

    TRACE_OUT(("Removing item at %#lx from list", pListItem));

    pPrev->next = PTRBASE_TO_OFFSET(pNext, pPrev);
    pNext->prev = PTRBASE_TO_OFFSET(pPrev, pNext);

    DebugExitVOID(COM_BasedListRemove);
} // COM_BasedListRemove


//
// NOTE:
// Because this is small model 16-bit code, NULL (which is 0) gets turned
// into ds:0 when casting to void FAR*.  Therefore we use our own FAR_NULL
// define, which is 0:0.
//
void FAR * COM_BasedListNext( PBASEDLIST pHead, void FAR * pEntry, UINT nOffset )
{
     PBASEDLIST p;

     ASSERT(pHead != NULL);
     ASSERT(pEntry != NULL);

     p = COM_BasedNextListField(COM_BasedStructToField(pEntry, nOffset));
     return ((p == pHead) ? FAR_NULL : COM_BasedFieldToStruct(p, nOffset));
}

void FAR * COM_BasedListPrev ( PBASEDLIST pHead, void FAR * pEntry, UINT nOffset )
{
     PBASEDLIST p;

     ASSERT(pHead != NULL);
     ASSERT(pEntry != NULL);

     p = COM_BasedPrevListField(COM_BasedStructToField(pEntry, nOffset));
     return ((p == pHead) ? FAR_NULL : COM_BasedFieldToStruct(p, nOffset));
}


void FAR * COM_BasedListFirst ( PBASEDLIST pHead, UINT nOffset )
{
    return (COM_BasedListIsEmpty(pHead) ?
            FAR_NULL :
            COM_BasedFieldToStruct(COM_BasedNextListField(pHead), nOffset));
}

void FAR * COM_BasedListLast ( PBASEDLIST pHead, UINT nOffset )
{
    return (COM_BasedListIsEmpty(pHead) ?
            FAR_NULL :
            COM_BasedFieldToStruct(COM_BasedPrevListField(pHead), nOffset));
}