/*++
 *
 *  WOW v1.0
 *
 *  Copyright (c) 1991, Microsoft Corporation
 *
 *  WGMETA.C
 *  WOW32 16-bit GDI API support
 *
 *  History:
 *  Created 07-Mar-1991 by Jeff Parsons (jeffpar)
--*/


#include "precomp.h"
#pragma hdrstop

MODNAME(wgmeta.c);

typedef METAHEADER UNALIGNED *PMETAHEADER16;




// WARNING: This function may cause 16-bit memory to move
VOID CopyMetaFile16FromHMF32(HAND16 hMF16, HMETAFILE hMF32)
{
    UINT   cbMF32, cbMF16;
    VPVOID vp;
    PBYTE  pMF16;


    if((vp = GlobalLock16(hMF16, &cbMF16)) && hMF32) {

        GETMISCPTR(vp, pMF16);

        cbMF32 = GetMetaFileBitsEx(hMF32, 0, NULL);
    
        // Verify these are the same size within the 16-bit kernel memory
        // allocation granularity
        WOW32WARNMSGF((abs(cbMF16 - cbMF32) < 32),
                      ("WOW32: Size MF16 = %lu  MF32 = %lu\n", cbMF16, cbMF32));

        // copy the bits from the 32-bit metafile to the 16-bit metafile memory
        cbMF32 = GetMetaFileBitsEx(hMF32, min(cbMF16, cbMF32), pMF16);

        GlobalUnlock16(hMF16);
        FLUSHVDMPTR(vp, cbMF32, pMF16);
        FREEMISCPTR(pMF16);
    }
}




// WARNING: This function may cause 16-bit memory to move
HAND16 WinMetaFileFromHMF(HMETAFILE hmf, BOOL fFreeOriginal)
{
    UINT cbMetaData;
    VPVOID vpMetaData;
    PBYTE pMetaData;
    HAND16 h16;

    /*
     * Under Windows Metafiles were merely Global Handle to memory
     * so we have to mimick that behavior because some apps "operate"
     * on metafile handles directly.  (WinWord and PowerPoint to
     * GlobalSize and GlobalAlloc to size and create metafiles)
     */

    cbMetaData = GetMetaFileBitsEx(hmf, 0, NULL);

    if (!cbMetaData)
       return((HAND16)NULL);

    /*
     * Win 3.1 allocates extra space in MetaFile and OLE2 checks for this.
     * METAHEADER is defined to be the same size as the 16-bit structure.
     */

    cbMetaData += sizeof(METAHEADER);

    vpMetaData = GlobalAllocLock16(GMEM_MOVEABLE | GMEM_DDESHARE, cbMetaData, &h16);

    if (!vpMetaData)
       return((HAND16)NULL);


    GETOPTPTR(vpMetaData, 0, pMetaData);

    if (GetMetaFileBitsEx(hmf, cbMetaData, pMetaData)) {
       GlobalUnlock16(h16);
    } else {
       GlobalUnlockFree16(vpMetaData);
       return((HAND16)NULL);
    }

    if (fFreeOriginal)
        DeleteMetaFile(hmf);

    return(h16);
}

HMETAFILE HMFFromWinMetaFile(HAND16 h16, BOOL fFreeOriginal)
{
    INT cb;
    VPVOID vp;
    HMETAFILE hmf = (HMETAFILE)0;
    PMETAHEADER16 pMFH16;

    vp = GlobalLock16(h16, &cb);

    if (vp) {
        GETMISCPTR(vp, pMFH16);

        hmf = SetMetaFileBitsEx(cb, (LPBYTE)pMFH16);

        if (fFreeOriginal)
            GlobalUnlockFree16(vp);
        else
            GlobalUnlock16(h16);

        FREEMISCPTR(pMFH16);
    }

    return(hmf);
}


ULONG FASTCALL WG32CloseMetaFile(PVDMFRAME pFrame)
{
    HMETAFILE hmf;
    ULONG ulRet = 0;
    register PCLOSEMETAFILE16 parg16;

    GETARGPTR(pFrame, sizeof(CLOSEMETAFILE16), parg16);

    hmf = CloseMetaFile(HDC32(parg16->f1));

    if (hmf)
   ulRet = (ULONG)WinMetaFileFromHMF(hmf, TRUE);
    // WARNING: 16-bit memory may have moved - invalidate flat pointers now
    FREEVDMPTR(pFrame);
    FREEARGPTR(parg16);
    RETURN(ulRet);
}


ULONG FASTCALL WG32CopyMetaFile(PVDMFRAME pFrame)
{
    ULONG ul;
    PSZ psz2;
    HMETAFILE hmfNew;
    HMETAFILE hmf;
    register PCOPYMETAFILE16 parg16;

    GETARGPTR(pFrame, sizeof(COPYMETAFILE16), parg16);
    GETPSZPTR(parg16->f2, psz2);

    if (psz2) {
        hmf = HMFFromWinMetaFile(parg16->f1, FALSE);
        hmfNew = CopyMetaFile(hmf, psz2);
        DeleteMetaFile(hmf);
   ul = (ULONG)WinMetaFileFromHMF(hmfNew, TRUE);
   // WARNING: 16-bit memory may have moved - invalidate flat pointers now
   FREEVDMPTR(pFrame);
   FREEARGPTR(parg16);
   FREEPSZPTR(psz2);
    } else {
        UINT cb;
        VPVOID vp, vpNew;
        PBYTE pMF, pMFNew;
        HAND16 h16New, h16;

        h16 = (HAND16)parg16->f1;

        ul = (ULONG) NULL;

        vp = GlobalLock16(h16, &cb);
        if (vp) {

        /*
         * Windows app such as WinWord uses GlobalSize to determine
         * the size of the metafile.  However, this size can be larger
         * than the true size of a metafile.  We have to make sure that
         * both source and destination sizes are identical so that
         * WinWord doesn't crash.
         */

       vpNew = GlobalAllocLock16(GMEM_MOVEABLE | GMEM_DDESHARE, cb, &h16New);

       // 16-bit memory may have moved - invalidate flat pointers now
       FREEVDMPTR(pFrame);
       FREEARGPTR(parg16);
       FREEPSZPTR(psz2);

            if (vpNew) {
      GETMISCPTR(vp, pMF);
                GETOPTPTR(vpNew, 0, pMFNew);

                RtlCopyMemory(pMFNew, pMF, cb);

                GlobalUnlock16(h16New);
                FLUSHVDMPTR(vpNew, cb, pMFNew);
                FREEOPTPTR(pMFNew);
                ul = h16New;
            }

            GlobalUnlock16(h16);
            FREEMISCPTR(pMF);
        }
    }

    FREEPSZPTR(psz2);
    FREEARGPTR(parg16);
    RETURN(ul);
}


ULONG FASTCALL WG32CreateMetaFile(PVDMFRAME pFrame)
{
    ULONG ul;
    PSZ psz1;
    register PCREATEMETAFILE16 parg16;

    GETARGPTR(pFrame, sizeof(CREATEMETAFILE16), parg16);
    GETPSZPTR(parg16->f1, psz1);

    ul = GETHDC16(CreateMetaFile(psz1));

    FREEPSZPTR(psz1);
    FREEARGPTR(parg16);
    RETURN(ul);
}

//
// This routine does what the 16-bit parameter validation layer would
// normally do for metafile handles, but since it is currently disabled,
// we'll do it here to fix WordPerfect that relies on it. Once true
// win31-style parameter validation has been re-enabled for metafile
// handles, all code within the ifndefs here and in WG32DeleteMetaFile
// can be removed.
//
#ifndef PARAMETER_VALIDATION_16_RE_ENABLED
#define MEMORYMETAFILE 1
#define DISKMETAFILE 2
#define HEADERSIZE          (sizeof(METAHEADER)/sizeof(WORD))
#define METAVERSION         0x0300
#define METAVERSION100      0x0100

BOOL IsValidMetaFile16(PMETAHEADER16 lpMetaData)
{
    BOOL            sts = FALSE;

        sts = (lpMetaData->mtType == MEMORYMETAFILE ||
           lpMetaData->mtType == DISKMETAFILE) &&
              (lpMetaData->mtHeaderSize == HEADERSIZE) &&
              ((lpMetaData->mtVersion ==METAVERSION) ||
           (lpMetaData->mtVersion ==METAVERSION100)) ;
    return sts;
}
#endif

ULONG FASTCALL WG32DeleteMetaFile(PVDMFRAME pFrame)
{
    ULONG ul = FALSE;
    VPVOID vp;
#ifndef PARAMETER_VALIDATION_16_RE_ENABLED
    PMETAHEADER16 lpMetaData;
#endif

    register PDELETEMETAFILE16 parg16;

    GETARGPTR(pFrame, sizeof(DELETEMETAFILE16), parg16);

    if (vp = GlobalLock16(parg16->f1,NULL)) {
#ifdef PARAMETER_VALIDATION_16_RE_ENABLED
        GlobalUnlockFree16(vp);
        ul = TRUE;
#else
        GETVDMPTR(vp, 1, lpMetaData);

        if (IsValidMetaFile16(lpMetaData)) {
            GlobalUnlockFree16(vp);
            ul = TRUE;
        }

        FREEVDMPTR(lpMetaData);
#endif
    }


    // If this metafile was in DDE conversation, then DDE cleanup code
    // needs to free its 32 bit counter part. So give DDE clean up
    // code a chance.
    // ChandanC

    W32DdeFreeHandle16 (parg16->f1);

    FREEARGPTR(parg16);
    RETURN(ul);
}

INT WG32EnumMetaFileCallBack(HDC hdc, LPHANDLETABLE lpht, LPMETARECORD lpMR, LONG nObj, PMETADATA pMetaData )
{
    INT iReturn;
    DWORD nWords;

    // update object table if we have one
    if (pMetaData->parmemp.vpHandleTable)
        PUTHANDLETABLE16(pMetaData->parmemp.vpHandleTable,nObj,lpht);

    // update MetaRecord

    // don't trash the heap with a bogus record, halt the enumeration
    nWords = lpMR->rdSize;
    if (nWords > pMetaData->mtMaxRecordSize) {
        LOGDEBUG(0,("WOW:bad metafile record during enumeration\n"));
        WOW32ASSERT(FALSE); // contact barryb
        return 0;   // all done
    }
    putstr16(pMetaData->parmemp.vpMetaRecord, (LPSZ)lpMR, nWords*sizeof(WORD));

    CallBack16(RET_ENUMMETAFILEPROC, (PPARM16)&pMetaData->parmemp, pMetaData->vpfnEnumMetaFileProc, (PVPVOID)&iReturn);

    // update the metarec in case the app altered it (Approach does)
    getstr16(pMetaData->parmemp.vpMetaRecord, (LPSZ)lpMR, nWords*sizeof(WORD));

    // update object table if we have one
    if (pMetaData->parmemp.vpHandleTable)
        GETHANDLETABLE16(pMetaData->parmemp.vpHandleTable,nObj,lpht);

    return (SHORT)iReturn;

    hdc;    // quiet the compilier; we already know the DC
}

ULONG FASTCALL WG32EnumMetaFile(PVDMFRAME pFrame)
{
    ULONG       ul = 0;
    register    PENUMMETAFILE16 parg16;
    METADATA    metadata;
    VPVOID      vpMetaFile = (VPVOID) NULL;
    PBYTE       pMetaFile;
    HMETAFILE   hmf = (HMETAFILE) 0;
    HAND16      hMetaFile16;
    HDC  hDC = 0;

    GETARGPTR(pFrame, sizeof(ENUMMETAFILE16), parg16);

    hMetaFile16 = parg16->f2;

    metadata.vpfnEnumMetaFileProc = DWORD32(parg16->f3);
    metadata.parmemp.vpData = (VPVOID)DWORD32(parg16->f4);
    metadata.parmemp.vpMetaRecord = (VPVOID) NULL;
    metadata.parmemp.vpHandleTable = (VPVOID) NULL;
    metadata.parmemp.hdc = parg16->f1;

    // WinWord never calls SetMetaFileBits; they peeked and know that
    // a metafile is really a GlobalHandle in Windows so we have
    // to look for that case.

    hmf = HMFFromWinMetaFile(hMetaFile16, FALSE);
    if (!hmf)
        goto EMF_Exit;

    // Get the metafile bits so we can get max record size and number of objects

    vpMetaFile = GlobalLock16(hMetaFile16, NULL);
    FREEARGPTR(parg16);    // memory may have moved
    FREEVDMPTR(pFrame);
    if (!vpMetaFile)
        goto EMF_Exit;

    GETOPTPTR(vpMetaFile, 0, pMetaFile);
    if (!pMetaFile)
        goto EMF_Exit;

    metadata.parmemp.nObjects = ((PMETAHEADER16)pMetaFile)->mtNoObjects;
    metadata.mtMaxRecordSize = ((PMETAHEADER16)pMetaFile)->mtMaxRecord;

    if (metadata.parmemp.nObjects)
    {
   PBYTE pHT;
   DWORD cb = ((PMETAHEADER16)pMetaFile)->mtNoObjects*sizeof(HAND16);

   metadata.parmemp.vpHandleTable = GlobalAllocLock16(GMEM_MOVEABLE, cb, NULL);
   FREEOPTPTR(pMetaFile);   // memory may have moved
   FREEARGPTR(parg16);
   FREEVDMPTR(pFrame);
        if (!metadata.parmemp.vpHandleTable)
            goto EMF_Exit;

        GETOPTPTR(metadata.parmemp.vpHandleTable, 0, pHT);
   RtlZeroMemory(pHT, cb);
    }

    metadata.parmemp.vpMetaRecord = GlobalAllocLock16(GMEM_MOVEABLE, metadata.mtMaxRecordSize*sizeof(WORD), NULL);
    FREEOPTPTR(pMetaFile);  // memory may have moved
    FREEARGPTR(parg16);
    FREEVDMPTR(pFrame);
    if (!metadata.parmemp.vpMetaRecord)
        goto EMF_Exit;

    // Corel Draw passes a NULL hDC, we'll create a dummy to keep GDI32 happy.
    if (CURRENTPTD()->dwWOWCompatFlags & WOWCF_GETDUMMYDC) {
   if ((hDC = HDC32(metadata.parmemp.hdc)) == 0) {
            hDC = CreateMetaFile(NULL);
        }
    }
    else {
   hDC = HDC32(metadata.parmemp.hdc);
    }

    // When processing metafile, access2.0 faults while receiving
    // WM_DEVMODECHANGE so we block that particular message when
    // in EnumMetaFile

    if ( CURRENTPTD()->dwWOWCompatFlagsEx & WOWCFEX_EATDEVMODEMSG) {
         CURRENTPTD()->dwFlags |= TDF_EATDEVMODEMSG;
    }
    ul = GETBOOL16(EnumMetaFile(hDC,
                                hmf,
                                (MFENUMPROC)WG32EnumMetaFileCallBack,
            ((LPARAM)(LPVOID)&metadata)));


    CURRENTPTD()->dwFlags &= ~TDF_EATDEVMODEMSG;
    
    
    // 16-bit memory may have moved - nothing to do as no flat ptrs exist now

    // copy the 32-bit metafile back to 16-bit land (the app may have altered
    // some of the metarecs in its MetaRecCallBackFunc -- Approach does)
    CopyMetaFile16FromHMF32(hMetaFile16, hmf);

    // Cleanup the dummy hDC created for Corel Draw 5.0.
    if (CURRENTPTD()->dwWOWCompatFlags & WOWCF_GETDUMMYDC) {
   if (HDC32(metadata.parmemp.hdc) == 0) {
            DeleteMetaFile(CloseMetaFile(hDC));
        }
    }

EMF_Exit:
    if (vpMetaFile)
        GlobalUnlock16(hMetaFile16);

    if (hmf)
        DeleteMetaFile(hmf);

    if (metadata.parmemp.vpHandleTable)
        GlobalUnlockFree16(metadata.parmemp.vpHandleTable);

    if (metadata.parmemp.vpMetaRecord)
        GlobalUnlockFree16(metadata.parmemp.vpMetaRecord);

    FREEARGPTR(parg16);
    RETURN(ul);
}


ULONG FASTCALL WG32GetMetaFile(PVDMFRAME pFrame)
{
    ULONG ul;
    PSZ psz1;
    HMETAFILE hmf;
    register PGETMETAFILE16 parg16;

    GETARGPTR(pFrame, sizeof(GETMETAFILE16), parg16);
    GETPSZPTR(parg16->f1, psz1);

    hmf = GetMetaFile(psz1);

    if (hmf)
        ul = WinMetaFileFromHMF(hmf, TRUE);
    else
        ul = 0;

    FREEPSZPTR(psz1);
    FREEARGPTR(parg16);
    RETURN(ul);
}


ULONG FASTCALL WG32PlayMetaFile(PVDMFRAME pFrame)
{
    ULONG ul;
    HMETAFILE hmf;
    register PPLAYMETAFILE16 parg16;

    GETARGPTR(pFrame, sizeof(PLAYMETAFILE16), parg16);

    hmf = HMFFromWinMetaFile(parg16->f2, FALSE);

    ul = GETBOOL16(PlayMetaFile(HDC32(parg16->f1), hmf));

    if (hmf)
        DeleteMetaFile(hmf);

    FREEARGPTR(parg16);
    RETURN(ul);
}


ULONG FASTCALL WG32PlayMetaFileRecord(PVDMFRAME pFrame)
{
    ULONG ul = FALSE;
    LPHANDLETABLE pHT = NULL;
    PBYTE pMetaData;
    WORD wHandles;
    VPHANDLETABLE16 vpHT;
    register PPLAYMETAFILERECORD16 parg16;

    GETARGPTR(pFrame, sizeof(PLAYMETAFILERECORD16), parg16);

    wHandles = parg16->f4;
    vpHT     = parg16->f2;
    if (wHandles && vpHT) {
        ALLOCHANDLETABLE16(wHandles, pHT);
        if (!pHT)
            goto PMFR_Exit;

        GETHANDLETABLE16(vpHT, wHandles, pHT);
    }
    GETOPTPTR(parg16->f3, 0, pMetaData);

    ul = (ULONG) PlayMetaFileRecord(HDC32(parg16->f1),
                                    pHT,
                                    (LPMETARECORD)pMetaData,
                                    (UINT)wHandles);


    if (wHandles && vpHT) {
        PUTHANDLETABLE16(vpHT, wHandles, pHT);
        FREEHANDLETABLE16(pHT);
    }
PMFR_Exit:
    FREEARGPTR(parg16);
    RETURN(ul);
}

#if 0  // implemented in gdi.exe

ULONG FASTCALL WG32GetMetaFileBits(PVDMFRAME pFrame)
{
    ULONG ul = 0;
    register PGETMETAFILEBITS16 parg16;

    GETARGPTR(pFrame, sizeof(GETMETAFILEBITS16), parg16);

    if (GlobalLock16(parg16->f1,NULL))
    {
        GlobalUnlock16(parg16->f1);
        ul = parg16->f1;
    }

    FREEARGPTR(parg16);
    RETURN(ul);
}

ULONG FASTCALL WG32SetMetaFileBits(PVDMFRAME pFrame)
{
    ULONG ul;
    register PSETMETAFILEBITS16 parg16;

    GETARGPTR(pFrame, sizeof(SETMETAFILEBITS16), parg16);

    ul = parg16->f1;

    FREEARGPTR(parg16);
    RETURN(ul);
}

#endif