/*
 * CLIPFILE.C - Windows Clipboard File I/O Routines
 *
 *  Copyright 1985-92, Microsoft Corporation
 */

/* NOTE:
 *   When saving the contents of the clipboard we SetClipboardData(fmt, NULL)
 *   to free up the memory associated with each clipboard format.  Then
 *   after we are done saving we take over as the clipboard owner.  This
 *   causes OWNERDRAW formats to be lost in the save process.
 */


/*
*
*    Touched by    :    Anas Jarrah
*    On Date        :    May 11/1992.
*    Revision remarks by Anas Jarrah ext #15201
*    This file has been changed to comply with the Unicode standard
*    Following is a quick overview of what I have done.
*
*    Was            Changed it into        Remark
*    ===            ===============        ======
*    CHAR              TCHAR             if it refers to a text elements
*    LPCHAR & LPSTR    LPTSTR            if it refers to text.
*    LPCHAR & LPSTR    LPBYTE            if it does not refer to text
*    "..."             TEXT("...")       compile time macro resolves it.
*    '...'             TEXT('...')       same
*    strlen            CharStrLen        compile time macro resolves it.
*    strcpy            CharStrCpy        compile time macro resolves it.
*    strcmp            CharStrCmp        compile time macro resolves it.
*    strcat            CharStrCat        compile time macro resolves it.
*    LoadResource      LoadResource(A/W) NT compiles resource strings into
*                                         Unicode binaries
*    MOpenFile()       MapOpenFile/MOpenFile   Depending on whether Unicode is defined or not.
*                                              This is a temporary cluge, That has to be taken care of.
*
*
*/

#include "clipbrd.h"
#include "dib.h"

#define ATTRDIRLIST 0xC010

// Windows 3.1 programs were packed on byte boundaries.
#pragma pack(1)
// Windows 3.1 BITMAP struct - used to save Win 3.1 .CLP files
typedef struct {
   WORD bmType;
   WORD bmWidth;
   WORD bmHeight;
   WORD bmWidthBytes;
   BYTE bmPlanes;
   BYTE bmBitsPixel;
   LPVOID bmBits;
   } WIN31BITMAP;
#pragma pack()

/* EXTERN data */
extern BOOL fAnythingToRender;

/* FORWARD procs */
HBITMAP PASCAL BitmapToBitmap(HBITMAP, WORD, WORD);

TCHAR       szFileSpecifier[] = TEXT("*.CLP");
TCHAR       szFileName[MAX_PATH];
BOOL        fNTReadFileFormat;
#ifdef JAPAN
extern TCHAR szCaptionName[];
#endif

/* ofSaveStruct is required; Otherwise, the following bug will occur
 *   When the contents of the clipboard are loaded from a file, the
 *   ofStruct is used to open the file and the data handles assigned to
 *   the clipboard are NULL because of DELAYED RENDERING; Delayed
 *   rendering will be done by reading from the file (ofStruct). But,
 *   now, if the contents are to be saved into another file, it should
 *   use the same ofStruct, because data has to be obtained from this
 *   file using delayed rendering; So, a temporary ofSaveStruct is used
 *   to hold the saved file's info; Once the save is successful, the
 *   contents of ofSaveStruct are copied onto ofStruct
 */
/* the ofStruct and ofSaveStruct are replaced by szFileName and
 * szSaveFileName strings.
 */
TCHAR       szSaveFileName[MAX_PATH];
BOOL        fNTSaveFileFormat;


/*
 *  IsWriteable()
 *
 * Test if a clipboard format is writeable(i.e. if it makes sense to write it)
 * OWNERDRAW and others can't be written because we (CLIPBRD) will become the
 * owner when the files are reopened.
 */

BOOL NEAR PASCAL IsWriteable(UINT Format)

{
  /* Are the PRIVATEFIRST and PRIVATELAST things right? */
  if ((Format >= CF_PRIVATEFIRST && Format <= CF_PRIVATELAST) || Format == CF_OWNERDISPLAY)
      return(FALSE);
  if (!fNTSaveFileFormat &&
      (Format == CF_UNICODETEXT || Format == CF_ENHMETAFILE || Format == CF_DSPENHMETAFILE))
      return(FALSE);
  return(TRUE);
}


/*
 *  ReadClipboardFromFile()
 *
 * Read in a clipboard file and register all the formats in delayed mode.
 * to render things for real reopen the file specified by szFileName (was ofStruct).
 *
 * NOTE:
 *    This makes us the clipboard owner.
 *
 * Bug 14564:  Changed return value to a short integer noting why the
 * reading failed.
 * Return Value:  0  Success
 *                1  Improper format
 *                2  OpenClipboard failed
 */
#define READFILE_SUCCESS         0
#define READFILE_IMPROPERFORMAT  1
#define READFILE_OPENCLIPBRDFAIL 2

short NEAR PASCAL ReadClipboardFromFile(HWND hwnd,INT fh)

{
register WORD i;
FILEHEADER    FileHeader;
FORMATHEADER  FormatHeader;

    /* Read the File Header */
    _lread(fh, (LPBYTE)&FileHeader, sizeof(FILEHEADER));

    /* Sanity check, make sure this is one of ours. */
#ifndef JAPAN
    if (FileHeader.magic == CLP_NT_ID)
        fNTReadFileFormat = TRUE;
    else if (FileHeader.magic == CLP_ID)
        fNTReadFileFormat = FALSE;
    else
        return(READFILE_IMPROPERFORMAT);

    if (FileHeader.FormatCount > 100)
        return(READFILE_IMPROPERFORMAT);
#else
    if (FileHeader.magic == CLP_NT_ID)
        fNTReadFileFormat = TRUE;
    else if (FileHeader.magic == CLP_ID)
        fNTReadFileFormat = FALSE;
    else
        goto improperformat;

    if (FileHeader.FormatCount > 100)
    {

improperformat:
    TCHAR szOutMessage[BUFFERLEN];
    TCHAR szCapBuffer[SMALLBUFFERLEN];

    LoadStringW(hInst, IDS_NAME, szCapBuffer, SMALLBUFFERLEN);
    LoadStringW(hInst, IDS_ENOTVALIDFILE, szOutMessage, BUFFERLEN);
    MessageBox(hwnd, szOutMessage,szCapBuffer, MB_OK | MB_ICONEXCLAMATION);
        return(READFILE_IMPROPERFORMAT);
    }
#endif

    /* We become the clipboard owner here! */
    if (!OpenClipboard(hwnd))
        return(READFILE_OPENCLIPBRDFAIL);

    EmptyClipboard();

    for (i=0; i < FileHeader.FormatCount; i++)
        {

        if (fNTReadFileFormat)
            _lread(fh, (LPBYTE)&(FormatHeader.FormatID), sizeof(FormatHeader.FormatID));
        else {
            FormatHeader.FormatID = 0;  /* initialize the high WORD */
            _lread(fh, (LPBYTE)&(FormatHeader.FormatID), sizeof(WORD));
        }
        _lread(fh, (LPBYTE)&(FormatHeader.DataLen), sizeof(FormatHeader.DataLen));
        _lread(fh, (LPBYTE)&(FormatHeader.DataOffset), sizeof(FormatHeader.DataOffset));
        _lread(fh, (LPBYTE)&(FormatHeader.Name), sizeof(FormatHeader.Name));

        if (PRIVATE_FORMAT(FormatHeader.FormatID))
            FormatHeader.FormatID = (UINT)RegisterClipboardFormat(FormatHeader.Name);

        /* Delayed Render. */
        SetClipboardData(FormatHeader.FormatID, NULL);
        }

    /* Now, clipbrd viewer has something to render */
    if (FileHeader.FormatCount > 0)
        fAnythingToRender = TRUE;

    CloseClipboard();
    return(READFILE_SUCCESS);
}


/*
 *  OpenClipboardFile()
 */

void NEAR PASCAL OpenClipboardFile(HWND hwnd)

{
INT     fh;

   lstrcpy(szFileName, TEXT(""));
   OFN.lpstrTitle = szOpenCaption;
   OFN.lpstrFile  = szFileName;
   /* Added OFN_FILEMUSTEXIST.  4 March 1991   clarkc   */
   /* Added OFN_HIDEREADONLY. Happy now, Patrick? :) 1 Oct 1992 a-mgates.*/
   OFN.Flags       = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;

    /* All long pointers should be defined immediately before the call.
     * L.Raman - 2/12/91
     */
   OFN.lpstrDefExt       = (LPTSTR)szDefExt;
   OFN.lpstrFilter       = (LPTSTR)szFilterSpec;
   OFN.lpstrCustomFilter = (LPTSTR)szCustFilterSpec;

   fh = GetOpenFileName ((LPOPENFILENAME) &OFN);
   if (fh)
      {
      fh = (INT)CreateFile((LPCTSTR)szFileName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
            NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

      if (fh > 0)
         {
         short nReadError;

         if (ClearClipboard(hwnd))
            {
            if (nReadError = ReadClipboardFromFile(hwnd, fh))
               {
               TCHAR szErr[MSGMAX];

               LoadString (hInst, IDS_READERR + nReadError, szErr, MSGMAX);
               MessageBox(hwnd,szErr,szAppName,MB_OK | MB_ICONEXCLAMATION);
               }
           }
        _lclose(fh);
        }
     }

  /* On GetOpenFileName failure CommDlgExtendedError will return a value.*/
  if(CommDlgExtendedError())
     {
     MemErrorMessage();
     }

}


/*
 *  WriteFormatBlock() -
 */
DWORD NEAR PASCAL WriteFormatBlock(INT fh,DWORD offset,DWORD DataOffset,
                                    DWORD DataLen,UINT Format,LPSTR szName)
{
FORMATHEADER    FormatHeader;
DWORD           dwBytesWritten = 0;

    FormatHeader.FormatID = Format;
    FormatHeader.DataLen = DataLen;
    FormatHeader.DataOffset = DataOffset;
    lstrcpy(FormatHeader.Name, szName);
    _llseek(fh, offset, 0);

    if (fNTSaveFileFormat)
        dwBytesWritten += _lwrite(fh, (LPSTR)&(FormatHeader.FormatID), sizeof(FormatHeader.FormatID));
    else
        dwBytesWritten += _lwrite(fh, (LPSTR)&(FormatHeader.FormatID), sizeof(WORD));
    dwBytesWritten += _lwrite(fh, (LPSTR)&(FormatHeader.DataLen), sizeof(FormatHeader.DataLen));
    dwBytesWritten += _lwrite(fh, (LPSTR)&(FormatHeader.DataOffset), sizeof(FormatHeader.DataOffset));
    dwBytesWritten += _lwrite(fh, (LPSTR)&(FormatHeader.Name), sizeof(FormatHeader.Name));
    return(dwBytesWritten);
}


/*
 *  lread() - Good ol' _lread that will handle >64k
 */
#define MAXREAD   ((LONG)  (60L * 1024L))

DWORD APIENTRY lread(INT hFile,void FAR *pBuffer,DWORD dwBytes)
{
DWORD   dwByteCount = dwBytes;
#ifdef WIN16
BYTE    huge *hpBuffer = pBuffer;

    while (dwByteCount > MAXREAD)
        {
    if (_lread(hFile, hpBuffer, (WORD)MAXREAD) != MAXREAD)
            return(0);

        dwByteCount -= MAXREAD;
        hpBuffer += MAXREAD;
        }
#else
BYTE    FAR  *hpBuffer = pBuffer;
#endif
    if ((DWORD)_lread(hFile, hpBuffer, dwByteCount) != dwByteCount)
        return(0);

    return(dwBytes);
}


/*
 *  lwrite() -  Good ol' _lwrite that will handle >64k
 */
DWORD APIENTRY lwrite(INT hFile,void FAR *pBuffer,DWORD dwBytes)

{
DWORD   dwByteCount=dwBytes;
#ifdef WIN16
BYTE    huge *hpBuffer = pBuffer;

    while (dwByteCount > MAXREAD)
        {
    if (_lwrite(hFile, (LPSTR)hpBuffer, (WORD)MAXREAD) != MAXREAD)
            return(0);
        dwByteCount -= MAXREAD;
        hpBuffer += MAXREAD;
        }

#else
BYTE    FAR  *hpBuffer = pBuffer;
#endif

    if ((DWORD)_lwrite(hFile, (LPSTR)hpBuffer, dwByteCount) != dwByteCount)
        return(0);

    return(dwBytes);
}


/*
 *  WriteDataBlock() -
 *
 * Returns:
 *    # of bytes written to the output file
 *
 * NOTE: Write saves the name of a temp file in the clipboard for it's
 * own internal clipboard format.  This file goes aways when Write
 * (or windows?) shuts down.  Thus saving Write clipboards won't work
 * (should we special case hack this?)
 *
 */
DWORD NEAR PASCAL WriteDataBlock(register INT hFile,LONG offset,UINT Format)
{
WORD    wPalEntries;
LPBYTE   lpData;
DWORD   dwSize;
BITMAP  bitmap;
HANDLE  hMF;
HANDLE  hBitmap;
HANDLE  hLogPalette;
register HANDLE hData;
LPLOGPALETTE    lpLogPalette;
LPMETAFILEPICT  lpMFP;
HENHMETAFILE    hEMF;
WIN31BITMAP     bmWin31;

    if (!(hData = GetClipboardData(Format)))
        return(0);

    if(_llseek(hFile, offset, 0) != (int)offset)
        return(0);

    /* We have to special case a few common formats but most things
     * get handled in the default case.
     */
    switch (Format)
        {
        case CF_ENHMETAFILE:
            hEMF = hData;
            dwSize = (DWORD) GetEnhMetaFileBits(hEMF, 0, NULL); /* Get data size */
            if (!(hData = GlobalAlloc(GHND, dwSize)))   /* allocate mem for EMF bits */
                return(0);
            if (!(lpData = GlobalLock(hData)))
                return(0);
            if (!GetEnhMetaFileBits(hEMF, dwSize, (LPBYTE)lpData))
                return(0);
            dwSize = lwrite(hFile, lpData, dwSize);
            GlobalUnlock(hData);
            GlobalFree(hData);
            break;


        case CF_METAFILEPICT:
            if (!(lpMFP = (LPMETAFILEPICT)GlobalLock(hData))) /* get header */
                return(0);

            if (fNTSaveFileFormat)
                _lwrite(hFile, (LPBYTE)lpMFP, sizeof(METAFILEPICT)); /* write header */
            else {
                /* If we save the metafile in the Windows 3.1 .CLP file format
                   we have to save the METAFILEPICT structure as a 16bit METAFILEPICT
                   structure. This may cause loss of information if the
                   hight half of the METAFILEPICT structure's fields are used.
                   [pierrej 5/27/92]                                        */

                _lwrite(hFile, (LPBYTE)&(lpMFP->mm), sizeof(WORD));
                _lwrite(hFile, (LPBYTE)&(lpMFP->xExt), sizeof(WORD));
                _lwrite(hFile, (LPBYTE)&(lpMFP->yExt), sizeof(WORD));
                _lwrite(hFile, (LPBYTE)&(lpMFP->hMF), sizeof(WORD));
            }

            hMF = lpMFP->hMF;

            GlobalUnlock(hData);            /* unlock the header */

            /* A-MGates 9/15/92 - Converted this block to use */
            /* GetMetaFileBitsEx                              */

            /* Figure out how big a block we need */
            dwSize = GetMetaFileBitsEx(hMF, 0, NULL);
            if (0 == dwSize)
               {
               return(0);
               }

            hData = GlobalAlloc(GMEM_MOVEABLE, dwSize);
            if (!(lpData = GlobalLock(hData)))
               {
               return(0);
               }

            if (dwSize != GetMetaFileBitsEx(hMF, dwSize, lpData))
               {
               GlobalUnlock(hData);
               GlobalFree(hData);
               return(0);
               }

            dwSize = lwrite(hFile, lpData, dwSize); /* spit them out */

            GlobalUnlock(hData);
            GlobalFree(hData);

            if(dwSize)
                if (fNTSaveFileFormat)
                    dwSize += sizeof(METAFILEPICT);     /* we wrote this much data */
                else
                    dwSize += SIZE_OF_WIN31_METAFILEPICT_STRUCT;
            break;

        case CF_BITMAP:
            // hBitmap = hData;

            /* Writing DDBs to disk is bad. Therefore, we */
            /* write an intelligent CF_DIB block instead.    */
            /* A-MGATES 9/29/92                              */

            Format = CF_DIB;

            hBitmap = DibFromBitmap((HBITMAP)hData, BI_RGB, 4, NULL);

            lpData = GlobalLock(hBitmap);

            // dwSize might be too big, but we can live with that.
            dwSize = GlobalSize(lpData);

            _lwrite(hFile, lpData, dwSize);

            GlobalUnlock(hBitmap);
            GlobalFree(hBitmap);

            break;

#ifdef ICKYOLDCODE

            if (!fNTSaveFileFormat)
                hBitmap = BitmapToBitmap(hBitmap, 4, 1);

            GetObject(hBitmap, sizeof(BITMAP), (LPBYTE) &bitmap);
            dwSize = (DWORD)bitmap.bmWidthBytes * bitmap.bmHeight * bitmap.bmPlanes;

            if (!fNTSaveFileFormat)
               {
               // Round up to the nearest TWO bytes when saving to Win 3.1,
               // not the nearest four, which is what GetObject gives you.
               // Note: The WidthBytes calculation does not include
               // bmPlanes in the multiplication because it seems to represent
               // bytes in a given plane.
               bitmap.bmWidthBytes =
                     ((bitmap.bmWidth * bitmap.bmBitsPixel)+ 15 ) >> 3;
                      // ">> 3" == " / 8", except cheaper.

               if (bitmap.bmWidthBytes & 1)
                  {
                  bitmap.bmWidthBytes++;
                  }
               }

            if (!(hData = GlobalAlloc(GHND, dwSize)))
                return(0);

            if (!(lpData = GlobalLock(hData)))
                {
                GlobalFree(hData);
                return(0);
                }

            GetBitmapBits(hBitmap, dwSize, lpData);

            if (fNTSaveFileFormat)
                _lwrite(hFile, (LPBYTE) & bitmap, sizeof(BITMAP));
            else {
                /* If we save the bitmap in the Windows 3.1 .CLP file format
                   we have to save the BITMAP structure as a 16bit BITMAP
                   structure. This may cause loss of information if the
                   hight half of the BITMAP structure's fields are used.
                   [pierrej 5/27/92]                                        */

                bmWin31.bmType = bitmap.bmType;
                bmWin31.bmWidth = bitmap.bmWidth;
                bmWin31.bmHeight = bitmap.bmHeight;
                bmWin31.bmWidthBytes = bitmap.bmWidthBytes;
                bmWin31.bmPlanes = bitmap.bmPlanes;
                bmWin31.bmBitsPixel = bitmap.bmBitsPixel;
                bmWin31.bmBits = bitmap.bmBits;

                _lwrite(hFile, (LPBYTE) &bmWin31, sizeof(WIN31BITMAP));
            }
            dwSize = lwrite(hFile, lpData, dwSize);

            GlobalUnlock(hData);
            GlobalFree(hData);
            if(dwSize)
                if (fNTSaveFileFormat)
                    dwSize += sizeof(BITMAP);
                else
                    dwSize += sizeof(WIN31BITMAP);
            break;

#endif

        case CF_PALETTE:
            /* Get the number of palette entries */
            GetObject(hData, sizeof(WORD), (LPBYTE)&wPalEntries);

            /* Allocate enough place to build the LOGPALETTE struct */
            dwSize = (DWORD)(sizeof(LOGPALETTE) + (LONG)wPalEntries * sizeof(PALETTEENTRY));
            if (!(hLogPalette = GlobalAlloc(GMEM_ZEROINIT | GMEM_MOVEABLE, dwSize)))
                {
                dwSize = 0L;
                goto Palette_Error;
                }

            if (!(lpLogPalette = (LPLOGPALETTE)GlobalLock(hLogPalette)))
                {
                dwSize = 0L;
                goto Palette_Error;
                }

            lpLogPalette->palVersion = 0x300;      /* Windows 3.00 */
            lpLogPalette->palNumEntries = wPalEntries;

        if (GetPaletteEntries(hData, 0, wPalEntries,
              (LPPALETTEENTRY)(lpLogPalette->palPalEntry)) == 0)
                {
                dwSize = 0L;
                goto Palette_Error;
                }

            /* Write the LOGPALETTE structure onto disk */
            dwSize = lwrite(hFile, (LPBYTE)lpLogPalette, dwSize);

Palette_Error:
            if (lpLogPalette)
                GlobalUnlock(hLogPalette);
            if (hLogPalette)
                GlobalFree(hLogPalette);
            break;

        default:
            dwSize = GlobalSize(hData);

            if (0 ==(lpData = GlobalLock(hData)) )
               {
               return 0;
               }

            dwSize = _lwrite(hFile, lpData, dwSize);
            GlobalUnlock(hData);
            break;
        }

    /* Return the number of bytes written. */
    return(dwSize);
}


/* This function will return the number of clipboard formats compatible with
   the Windows 3.1 clipboard, this excludes CF_UNICODETEXT, CF_ENHMETAFILE and
   CF_DSPENHMETAFILE
*/

int Count16BitClipboardFormats(void)
{
    int iCount;

    iCount = CountClipboardFormats();
    if (IsClipboardFormatAvailable(CF_UNICODETEXT))
        iCount--;
    if (IsClipboardFormatAvailable(CF_ENHMETAFILE))
        iCount--;
    if (IsClipboardFormatAvailable(CF_DSPENHMETAFILE))
        iCount--;

    return iCount;
}


/*
 *  SaveClipboardData() - Writes a clipboard file.
 *
 * In:
 *    hwnd        handle of wnd that becomes the clipboard owner
 *    szFileName  file handle to read from
 *
 * NOTE:
 *    When done we call ReadClipboardFromFile(). this makes us the
 *    clipboard owner.
 */

BOOL NEAR PASCAL SaveClipboardData(HWND    hwnd,LPTSTR szLocalFileName)

{
INT    fh;
register UINT Format;
DWORD     HeaderPos;
DWORD     DataPos;
DWORD     datasize;
BOOL      fComplain;
BOOL      fDIBUsed;
HCURSOR   hCursor;
FILEHEADER FileHeader;
TCHAR      szComplaint[BUFFERLEN];
TCHAR      szName[CCHFMTNAMEMAX];
WORD      wHeaderSize;
UINT      uiSizeHeaderToWrite;

    /* First open the clipboard */
    if (!OpenClipboard(hwndMain))
        return(FALSE);

    /* Open the file */

    fh = (INT)CreateFile((LPCTSTR)szLocalFileName, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
                    NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if (fh == -1)
        {
        GetLastError();
        CloseClipboard();
        return(FALSE);
        }
    /* Fill out the file header structure */
    if (fNTSaveFileFormat) {
        FileHeader.magic = CLP_NT_ID;          /* magic number to tag our files */
        uiSizeHeaderToWrite = (sizeof(UINT) + 2*sizeof(DWORD) + CCHFMTNAMEMAX*sizeof(TCHAR));
    } else {
        FileHeader.magic = CLP_ID;          /* magic number to tag our files */
        uiSizeHeaderToWrite = (sizeof(WORD) + 2*sizeof(DWORD) + CCHFMTNAMEMAX*sizeof(TCHAR));
    }
    FileHeader.FormatCount = 0;          /* dummy for now */

    /* Update HeaderPos and DataPos */
    HeaderPos = sizeof(FILEHEADER);

    /* This is the maximum number of formats that will be written.  Potentially
     * some may fail and some space will be wasted.
     * In 32bit the number of bytes written to the disk isn't sizeof(FORMATHEADER)
     * because of DWORD alignment in the FORMATHEADER structure. Instead we write
     * the format headre structure one field at a time to remain compatible with
     * the 16bit Windows versions.
     */

    if (fNTSaveFileFormat)
        DataPos = HeaderPos + (uiSizeHeaderToWrite * CountClipboardFormats());
    else
        DataPos = HeaderPos + (uiSizeHeaderToWrite * Count16BitClipboardFormats());

    /* Now loop throught the data, one format at a time, and write out the data. */
    fComplain = FALSE;

    LoadString(hInst, IDS_FMTNOTSAV, szComplaint, BUFFERLEN);

    hCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
    ShowCursor(TRUE);

    /* Enumerate all formats */
    Format = 0;
    fDIBUsed = FALSE;
    while ((Format = EnumClipboardFormats(Format)) != 0)
        {
        if (IsWriteable(Format))
            {
            // DO NOT write CF_BITMAP to disk. Transform to CF_DIB
            // and write that instead.
            if (CF_BITMAP == Format || CF_DIB == Format)
               {
               if (!fDIBUsed)
                  {
                  fDIBUsed = TRUE;
                  }
               // If I've already done DIB, go on to the next format.
               else continue;
               }

            GetClipboardName((Format == CF_BITMAP ? CF_DIB : Format),
                  szName, sizeof(szName));

            if ((datasize = WriteDataBlock(fh, DataPos, Format)) != 0)
                {
                /* Create a Format header and write it to the file */
                wHeaderSize = (WORD)WriteFormatBlock(fh,
                     HeaderPos, DataPos, datasize,
                     (Format == CF_BITMAP ? CF_DIB : Format),
                     (LPTSTR)szName);
                if(wHeaderSize < uiSizeHeaderToWrite)
                    {
                    fComplain = TRUE;
                    break;
                    }
                HeaderPos += wHeaderSize;

                /* Update the data pos for the next block */
                DataPos += datasize;

                FileHeader.FormatCount++;       /* this format has been written */
                }
            else
                {
                fComplain = TRUE;
                break;
                }
            }
        }

    ShowCursor(FALSE);
    SetCursor(hCursor);

    if (fComplain)
        {
#ifdef JAPAN
        /* Use Japanese message for caption instead of app name */
        MessageBox(hwnd, szComplaint, szCaptionName, MB_OK | MB_ICONEXCLAMATION);
#else
        MessageBox(hwnd, szComplaint, szAppName, MB_OK | MB_ICONEXCLAMATION);
#endif
        CloseClipboard();
        _lclose(fh);
        return(FALSE);
        }

    CloseClipboard();      /* we are done looking at this */

    _llseek(fh, 0L, 0);     /* move back to the start of file */

    /* Write the File Header with the correct number of formats written */
    _lwrite(fh, (LPBYTE) & FileHeader, sizeof(FILEHEADER));

    /* Now we open the clipboard and become the owner.  this places
     * all the things we just saved in the clipboard (and throws out
     * those things we didn't save)
     */
    _llseek(fh, 0L, 0);

    /* ofStruct will be used for reopening the file */
    lstrcpy(szFileName, szSaveFileName);
    fNTReadFileFormat = fNTSaveFileFormat;

    ReadClipboardFromFile(hwndMain, fh);

    _lclose(fh);

    return(TRUE);
}

/*
 *  SaveClipboardToFile() -
 */
void NEAR PASCAL SaveClipboardToFile(HWND hwnd)
{
INT    hFile;

OFN.lpstrTitle = szSaveCaption;
OFN.lpstrFile  = szSaveFileName;
szSaveFileName[0] = 0;
OFN.Flags      = OFN_HIDEREADONLY | OFN_PATHMUSTEXIST |
                 OFN_OVERWRITEPROMPT | OFN_NOREADONLYRETURN;

/* All long pointers should be defined immediately before the call. */
OFN.lpstrDefExt       = (LPTSTR)szDefExt;
OFN.lpstrFilter       = (LPTSTR)szFilterSpec;
OFN.lpstrCustomFilter = NULL;
OFN.lpfnHook          = NULL;
OFN.nFilterIndex      = 1;

hFile = GetSaveFileName ((LPOPENFILENAME) &OFN);
if (hFile)
   {
   // The first filter listed is "NT Clipboard File". The second is
   // "Windows 3.1 Clipboard file".
   fNTSaveFileFormat = (1 == OFN.nFilterIndex);

   hFile = (INT)CreateFile((LPCTSTR)szSaveFileName, GENERIC_READ, FILE_SHARE_READ,
                     NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
   if (hFile != -1)
      /* The file already exists and the user wants to
       * overwrite! Now, we have to check whether this
       * file is the most recently saved clip file;
       * If so, it will be used for delayed rendering; So,
       * before we overwrite this file we must read all data
       * from it thro RENDERALLFORMATS;
       * Fix for Bug #5602 --SANKAR-- 10-16-89
       */
      {
      CloseHandle((HANDLE)hFile);
      if (lstrcmp(szSaveFileName, szFileName) == 0)
         {
         SendMessage(hwndMain, WM_RENDERALLFORMATS, 0, 0L);
         }
      }

   if (!SaveClipboardData(hwnd, (LPTSTR)szSaveFileName))
      {
      /* If Failure, Delete the incomplete file */
      DeleteFile(szSaveFileName);
      }
   }
}


//
// Purpose:
//    Convert an existing bitmap to the given number of planes and
//    bits/pixel.
//
// Parameters:
//    hBitmap - Handle to the existing bitmap.
//    wPlanes - Number of planes in the destination bitmap.
//    wBitCount - Number of bits/pel in the destination bitmap.
//
// Returns:
//    A handle to the converted bitmap.
//
////////////////////////////////////////////////////////////////////////////
HBITMAP PASCAL BitmapToBitmap(HBITMAP hBitmap, WORD wPlanes, WORD wBitCount)
{
    BITMAP  bm;
    BITMAPINFOHEADER    BmpInfoHeader;
    HANDLE  hDib, hBmp;
    LPBYTE  lpDib, lpBits;
    DWORD   dwLength, dwSize;
    int     iColorTable;
    int     iColorUsed;
    HDC     hDC;

    /*
    ** Get the size of the bitmap.  These values are used to setup the memory
    ** requirements for the DIB.
    */
    GetObject(hBitmap,sizeof(BITMAP),(LPSTR)&bm);
    if ((bm.bmBitsPixel == wBitCount) && (bm.bmPlanes == wPlanes))
        return hBitmap;

    #if 0
    dwSize = dwLength = (DWORD)(bm.bmWidthBytes+sizeof(WORD)) * bm.bmHeight * bm.bmPlanes;
    #else
    dwSize = dwLength = (DWORD)(((bm.bmWidth * wBitCount + 7)/8)*2 + 1)/2
         * wPlanes * bm.bmHeight;
    #endif

    switch(bm.bmBitsPixel * bm.bmPlanes)
    {
        case 1:
            iColorTable = sizeof(RGBQUAD) * 2;
            break;

        case 4:
            iColorTable = sizeof(RGBQUAD) * 16;
            break;

        case 8:
            iColorTable = sizeof(RGBQUAD) * 256;
            break;

        case 24:
        default:
            iColorTable = 0;
            break;
    }
    iColorUsed = iColorTable / sizeof(RGBQUAD);
    dwLength   += (sizeof(BITMAPINFOHEADER) + iColorTable);


    /*
    ** Create the DIB.  First, to the size of the bitmap.  We will calculate
    ** the new memory requirements if DIB-Compression is desired.
    */
    if(hDib = GlobalAlloc(GHND,dwLength)) {
        if(lpDib = GlobalLock(hDib))
           {
           ((LPBITMAPINFOHEADER)lpDib)->biSize          = sizeof(BITMAPINFOHEADER);
           ((LPBITMAPINFOHEADER)lpDib)->biWidth         = bm.bmWidth;
           ((LPBITMAPINFOHEADER)lpDib)->biHeight        = bm.bmHeight;
           ((LPBITMAPINFOHEADER)lpDib)->biPlanes        = 1;
           ((LPBITMAPINFOHEADER)lpDib)->biBitCount      = bm.bmBitsPixel*bm.bmPlanes;
           ((LPBITMAPINFOHEADER)lpDib)->biCompression   = BI_RGB;
           ((LPBITMAPINFOHEADER)lpDib)->biSizeImage     = 0;
           ((LPBITMAPINFOHEADER)lpDib)->biXPelsPerMeter = 0;
           ((LPBITMAPINFOHEADER)lpDib)->biYPelsPerMeter = 0;
           ((LPBITMAPINFOHEADER)lpDib)->biClrUsed       = 0;
           ((LPBITMAPINFOHEADER)lpDib)->biClrImportant  = 0;


           // Figure out where the bits go
           lpBits = (LPBYTE)lpDib+sizeof(BITMAPINFOHEADER)+iColorTable;
           hDC = GetDC(hwndMain);
           if (NULL != hDC)
              {
              if (bm.bmHeight == GetDIBits(hDC,hBitmap,0,
                        bm.bmHeight,lpBits,(LPBITMAPINFO)lpDib,
                        // DIB_PAL_INDICES))
                        DIB_RGB_COLORS))
                 {
                 BmpInfoHeader.biSize          = sizeof(BITMAPINFOHEADER);
                 BmpInfoHeader.biWidth         = bm.bmWidth;
                 BmpInfoHeader.biHeight        = bm.bmHeight;
                 BmpInfoHeader.biPlanes        = wPlanes;
                 BmpInfoHeader.biBitCount      = wBitCount;
                 BmpInfoHeader.biCompression   = BI_RGB;
                 BmpInfoHeader.biSizeImage     = dwSize;
                 BmpInfoHeader.biXPelsPerMeter = 0;
                 BmpInfoHeader.biYPelsPerMeter = 0;
                 BmpInfoHeader.biClrUsed       = iColorUsed;
                 BmpInfoHeader.biClrImportant  = iColorUsed;
                 hBmp = CreateDIBitmap(NULL, &BmpInfoHeader, CBM_INIT,
                            lpBits, (LPBITMAPINFO)lpDib, DIB_RGB_COLORS);
                 }
              ReleaseDC(hwndMain, hDC);
              }
           GlobalUnlock(hDib);
           }
        GlobalFree(hDib);
        }

    return(hBmp);
}