/********************** START OF SPECIFICATIONS *************************/
/*                                                                      */
/* SOURCE FILE NAME: MMOTPROC.C                                         */
/*                                                                      */
/* DESCRIPTIVE NAME: Multi-Media I/O Procedure for M-Motion Video       */
/*                                                                      */
/* COPYRIGHT:                                                           */
/*              Copyright (c) IBM Corporation  1990, 1993               */
/*                        All Rights Reserved                           */
/*                                                                      */
/* STATUS:   MMPM/2 Release 1.10                                        */
/*                                                                      */
/* ABSTRACT: This file contains the MMIO                                */
/*           IOProc for M-Motion Still Video files.                     */
/*                                                                      */
/*           This IOProc is intended to be a sample for how IOprocs     */
/*           can be written, and is most applicable to Image data.      */
/*           Note that there are many areas that are specific to the    */
/*           M-Motion Still Video Image file format.  These sections    */
/*           and structures should be changed as necessary for use      */
/*           with other image file formats.                             */
/*                                                                      */
/*           Note also that this image IOProc uses large buffering      */
/*           schemes, due to the ordering of image data (top-down)      */
/*           in the file, whereas the MMIO function and messages rely   */
/*           on this data being made available in the normal OS/2 PM    */
/*           bottom-up form.  This forces the IOproc to pre-read,       */
/*           buffer and flip image data before it can supply the data   */
/*           to the application.  It must perform the reverse order     */
/*           when writing (get the data, buffer it, flip and write to   */
/*           to disk.                                                   */
/*                                                                      */
/*********************** END OF SPECIFICATIONS **************************/

/************************************************************************/
/* Put all #defines here                                                */
/************************************************************************/

#define INCL_32                         /* force 32 bit compile */
#define INCL_GPIBITMAPS
#define INCL_DOSFILEMGR
#define INCL_WIN
#define INCL_GPI
#define INCL_PM

#define MEMCHECK

/************************************************************************/
/* Put all #includes here                                               */
/************************************************************************/

#include <os2.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <os2medef.h>
#include <mmioos2.h>
#include "mmotproc.h"
#include "immonls.h"
#include <ioprnls.h>

/************************************************************************/
/* MMOT IOProc                                                          */
/*                                                                      */
/* ARGUMENTS:                                                           */
/*                                                                      */
/*     PSZ pmmioStr - pointer to MMIOINFO block                         */
/*     USHORT usMsg - MMIO message being sent                           */
/*     LONG lParam1 - filename or other parameter depending on message  */
/*     LONG lParam2 - used with some messages as values                 */
/*                                                                      */
/*                                                                      */
/*  RETURN:                                                             */
/*                                                                      */
/*      MMIOM_OPEN                                                      */
/*          Success           - MMIO_SUCCESS     (0)                    */
/*          Failure           - MMIO_ERROR      (-1)                    */
/*                                                                      */
/*      MMIOM_READ                                                      */
/*          Success           - Returns the number of bytes actually    */
/*                              read.  Return 0L if no more bytes can   */
/*                              be read.                                */
/*          Failure           - MMIO_ERROR      (-1)                    */
/*                                                                      */
/*      MMIOM_WRITE                                                     */
/*          Success           - Returns the number of bytes actually    */
/*                              written.                                */
/*          Failure           - MMIO_ERROR      (-1)                    */
/*                                                                      */
/*      MMIOM_SEEK                                                      */
/*          Success           - Returns the new file position           */
/*          Failure           - MMIO_ERROR      (-1)                    */
/*                                                                      */
/*      MMIOM_CLOSE                                                     */
/*          Success           - MMIO_SUCCESS     (0)                    */
/*          Failure           - MMIO_ERROR      (-1)                    */
/*          Other             - MMIO_WARNING, file was closed but the   */
/*                              IOProc expected additional data         */
/*                                                                      */
/*      MMIOM_GETFORMATNAME                                             */
/*          Success           - Returns the number of bytes read into   */
/*                              the buffer (size of format name)        */
/*          Failure           - Return 0                                */
/*                                                                      */
/*      MMIOM_GETFORMATINFO                                             */
/*          Success           - MMIO_SUCCESS     (0)                    */
/*          Failure           - MMIO_ERROR      (-1)                    */
/*                                                                      */
/*      MMIOM_QUERYHEADERLENGTH                                         */
/*          Success           - Returns the size of the header in bytes */
/*          Failure           - Return 0                                */
/*                                                                      */
/*      MMIOM_IDENTIFYFILE                                              */
/*          Success           - MMIO_SUCCESS     (0)                    */
/*          Failure           - MMIO_ERROR      (-1)                    */
/*                                                                      */
/*      MMIOM_GETHEADER                                                 */
/*          Success           - Returns number of bytes copied into     */
/*                              the header structure.                   */
/*          Failure           - Return 0                                */
/*          Other             - If length passed in was not large       */
/*                              enough to hold header then,             */
/*                              MMIOERR_INVALID_BUFFER_LENGTH is set    */
/*                              in ulErrorRet.                          */
/*                            - If header is bad,                       */
/*                              MMIOERR_INVALID_STRUCTURE is set in     */
/*                              ulErrorRet                              */
/*                                                                      */
/*      MMIOM_SETHEADER                                                 */
/*          Success           - Returns number of bytes written         */
/*          Failure           - Return 0                                */
/*          Other             - If header is bad,                       */
/*                              MMIOERR_INVALID_STRUCTURE is set in     */
/*                              ulErrorRet                              */
/*                                                                      */
/*  DESCRIPTION:                                                        */
/*                                                                      */
/*      This routine will translate IBM M-Motion YUV Video data into    */
/*      OS/2 2.0 memory bitmap data and back again.  The IOProc is part */
/*      of OS/2 MultiMedia Extentions File Format Conversion Utility.   */
/*                                                                      */
/*  GLOBAL VARS REFERENCED:                                             */
/*                                                                      */
/*      None                                                            */
/*                                                                      */
/*  GLOBAL VARS MODIFIED:                                               */
/*                                                                      */
/*      None                                                            */
/*                                                                      */
/*  NOTES:                                                              */
/*                                                                      */
/*      None                                                            */
/*                                                                      */
/*  SIDE EFFECTS:                                                       */
/*                                                                      */
/*      None                                                            */
/*                                                                      */
/************************************************************************/

LONG EXPENTRY IOProc_Entry (PVOID  pmmioStr,
                             USHORT usMsg,
                             LONG   lParam1,
                             LONG   lParam2)

    {
    PMMIOINFO   pmmioinfo;                      /* MMIOINFO block */

    pmmioinfo = (PMMIOINFO) pmmioStr;

    switch (usMsg)
        {
        /*#############################################################*
         * When Closing the file, perform the following:
         * 1) Setup Variables
         * 2) Process the Image buffer
         * 3) Compress the Image to appropriate format
         *#############################################################*/
        case MMIOM_CLOSE:
            {
            /************************************************************
             * Declare local variables.
             ************************************************************/
            PMMFILESTATUS   pVidInfo;         /* MMotionIOProc instance data */

            ULONG           ulHeight;         /* Image height                */
            USHORT          usBitCount;
            /* Image width, including overflow in 1bpp & 4bpp                */
            ULONG           ulImgPelWidth;
            PBYTE           lpYUVLine;        /* One line of packed YUV      */
            LONG            lYUVBytesPerLine;

            ULONG           ulMaxPelWidth;    /* # pels on 4-pel boundaries  */
            /* # pels on a YUV line in the output file                       */
            ULONG           ulYUVPelWidth;
            ULONG           ulRGBMaxBytesPerLine; /* #bytes on 4-pel bounds  */
            PBYTE           lpRGBLine;        /* One line of 24bit RGB       */

            PBYTE           lpImgBufPtr;      /* Current loc in RGB image buf*/
            LONG            lRGBBytesPerLine; /* #bytes on a line in image   */
            ULONG           ulRowCount;       /* loop counter                */
            LONG            lBytesWritten;    /* #bytes output on a write    */
            LONG            lRetCode;
            USHORT          rc;

            /***********************************************************
             * Check for valid MMIOINFO block.
             ***********************************************************/
            if (!pmmioinfo)
                return (MMIO_ERROR);

            /***********************************************************
             * Set up our working file status variable.
             ***********************************************************/
            pVidInfo = (PMMFILESTATUS)pmmioinfo->pExtraInfoStruct;

            /***********************************************************
             * Assume success for the moment....
             ***********************************************************/
            lRetCode = MMIO_SUCCESS;

            /************************************************************
             * see if we are in Write mode and have a buffer to write out.
             *    We have no image buffer in UNTRANSLATED mode.
             ************************************************************/
            if ((pmmioinfo->ulFlags & MMIO_WRITE) && (pVidInfo->lpImgBuf))
                {
                /*******************************************************
                 * The buffer should be in palettized or 24-bit RGB
                 * We must convert it to YUV to be written to the file.
                 *******************************************************
                 * The buffer should be complete.  If not, then we
                 * should still close, but can flag an error to the
                 * user that the data may be corrupted.  The only way
                 * we can estimate if this is true is to check the final
                 * position.  If not at the end...
                 *******************************************************/
                if (pVidInfo->lImgBytePos !=
                                          (LONG)pVidInfo->ulImgTotalBytes)
                    {
                    lRetCode = MMIO_WARNING;
                    }

                /*******************************************************
                 * Set up width and height of image in the buffer.
                 *******************************************************/
                ulHeight = pVidInfo->mmImgHdr.mmXDIBHeader.BMPInfoHeader2.cy;
                usBitCount =
                    pVidInfo->mmImgHdr.mmXDIBHeader.BMPInfoHeader2.cBitCount;

                /*******************************************************
                 * Get the line width in YUV pels and packed bytes.
                 *******************************************************/
                ulYUVPelWidth = pVidInfo->mmotHeader.mmXlen;
                lYUVBytesPerLine = (LONG)(ulYUVPelWidth * 3) >> 1;


                /*******************************************************
                 * This changes from OS/2 PM bottom-up bitmap form
                 *    to M-Motion's top-down form.  Flip all pad, boundary
                 *    bytes as well
                 *******************************************************/
                ImgBufferFlip ((PBYTE)pVidInfo->lpImgBuf,
                                        pVidInfo->ulImgPaddedBytesPerLine,
                                        ulHeight);

                /*******************************************************
                 * Determine number of POSSIBLE pels on a line, tho some
                 *   may be overflow in 1bpp and 4bpp modes.
                 *
                 * From that, we can calc the number of RGB pels we will
                 *   create to represent this line.
                 *******************************************************/
                ulImgPelWidth = (pVidInfo->ulImgPelBytesPerLine << 3) /
                                             usBitCount;
                lRGBBytesPerLine = ulImgPelWidth * 3;

                /*******************************************************
                 * Ensure the width is on a 4-pel boundary, necessary for
                 *     M-Motion.  We will buffer with black
                 *    *** THIS IS ONLY NECESSARY FOR M-MOTION IMAGES
                 *******************************************************/
                if (ulImgPelWidth % 4)
                    ulMaxPelWidth = (((ulImgPelWidth >> 2) + 1) << 2);
                else
                    ulMaxPelWidth = ulImgPelWidth;

                /* #RGB bytes/line = #pels * 3 bytes/pel */
                ulRGBMaxBytesPerLine = ulMaxPelWidth * 3;


                /*******************************************************
                 * Create a buffer for one line of RGB data, accounting for
                 *   the 4-pel boundary required.  Extra bytes won't be used.
                 *******************************************************/
                if (DosAllocMem ((PPVOID) &lpRGBLine,
                                 ulRGBMaxBytesPerLine,
                                 fALLOC))
                    return (MMIO_ERROR);

                /*******************************************************
                 * Create a buffer for one line of YUV data.
                 *******************************************************/
                if (DosAllocMem ((PPVOID) &lpYUVLine,
                                 lYUVBytesPerLine,
                                 fALLOC))
                    return (MMIO_ERROR);

                /*******************************************************
                 * Zero out RGB buffer to cover for any extra black pels
                 * needed at the end.
                 *******************************************************/
                memset (lpRGBLine, 0, ulRGBMaxBytesPerLine);

                /*******************************************************
                 * Initialize start position of RGB buffer.
                 *******************************************************/
                lpImgBufPtr = pVidInfo->lpImgBuf;

                /*******************************************************
                 * Process Image Buffer - Save to file
                 * Place "your" processing code here, if full image
                 *      buffering is performed.
                 * For MMotion:
                 *    Loop
                 *       1.Convert and copy a line of 1bpp, 4bpp, 8 bpp or
                 *            24bpp data into a temporary 24 bpp RGB line.
                 *            This line may contain overflow garbage
                 *            pels from 1bpp and 4bpp modes (where
                 *            width does not fall on even byte boundaries.)
                 *       2.Convert the temporary RGB line contents into a
                 *           YUV line.  ONLY that data necessary converted.
                 *           Overflow from bitmap data ignored.
                 *       3.Write the YUV line to the file
                 *******************************************************/
                for (ulRowCount = 0;
                     ulRowCount < ulHeight;
                     ulRowCount++)
                    {
                    /***************************************************
                     * Convert 1 line of Image data into RGB data
                     ***************************************************/
                   switch (usBitCount)
                       {
                       case 1:
                           {
                           /* Convert 1bpp padded image buffer into 24-bit */
                           /*    RGB line buffer, w/pads                   */
                           Convert1BitTo24Bit (
                                   (PBYTE)lpImgBufPtr,
                                   (PRGB) lpRGBLine,
                                   (PRGB) &(pVidInfo->rgbPalette),
                                   pVidInfo->ulImgPelBytesPerLine);
                           break;
                           }

                       case 4:
                           {
                           /* Convert data from app buffer into 24-bit and */
                           /*    copy into image buffer                    */
                           Convert4BitTo24Bit (
                                   (PBYTE)lpImgBufPtr,
                                   (PRGB) lpRGBLine,
                                   (PRGB) &(pVidInfo->rgbPalette),
                                   pVidInfo->ulImgPelBytesPerLine);
                           break;
                           }

                       case 8:
                           {
                           /* Convert data from app buffer into 24-bit and */
                           /*    copy into image buffer                    */
                           Convert8BitTo24Bit (
                                   (PBYTE)lpImgBufPtr,
                                   (PRGB) lpRGBLine,
                                   (PRGB) &(pVidInfo->rgbPalette),
                                   pVidInfo->ulImgPelBytesPerLine);
                           break;
                           }

                       case 24:
                           {
                           /* Copy raw RGB data from the image buffer into */
                           /*    the temporary                             */
                           /*   RGB line.  Only copy those pels necessary. */
                           /*    No conversion required                    */
                           memcpy ((PVOID) lpRGBLine,
                                          (PVOID) lpImgBufPtr,
                                          ulYUVPelWidth * 3);
                           break;
                           }

                       }   /* end of Switch for Bit Conversion block */

                    /***************************************************
                     * Convert one line at a time from RGB to YUV.
                     ***************************************************/
                    ConvertOneLineRGBtoYUV (lpRGBLine,
                                            lpYUVLine,
                                            ulYUVPelWidth);

                    /***************************************************
                     * Write out line of YUV data to the file.
                     ***************************************************/
                    lBytesWritten  = mmioWrite (pVidInfo->hmmioSS,
                                                (PVOID) lpYUVLine,
                                                lYUVBytesPerLine);

                    /* Check if error or EOF  */
                    if (lBytesWritten != lYUVBytesPerLine)
                       {
                       lRetCode = lBytesWritten;
                       break;
                       }

                    /***************************************************
                     * Make sure bitmap image buffer pointer is correct
                     *    for next line to be converted.  Move forward ALL
                     *    the bytes in the bitmap line, including overflow
                     *    and pad bytes.
                     ***************************************************/
                    lpImgBufPtr += pVidInfo->ulImgPaddedBytesPerLine;
                    }

                /*******************************************************
                 * Free temp buffers.
                 *******************************************************/
                if (lpRGBLine)
                   {
                   DosFreeMem ((PVOID) lpRGBLine);
                   }

                if (lpYUVLine)
                   {
                   DosFreeMem ((PVOID) lpYUVLine);
                   }
                }  /* end IF WRITE & IMAGE BUFFER block */

            /***********************************************************
             * Free the RGB buffer, if it exists, that was created
             * for the translated READ operations.
             ***********************************************************/
            if (pVidInfo->lpRGBBuf)
               {
               DosFreeMem ((PVOID) pVidInfo->lpRGBBuf);
               }

            /***********************************************************
             * Free the IMG buffer, if it exists, that was created
             * for the translated  WRITE operations.
             ***********************************************************/
            if (pVidInfo->lpImgBuf)
               {
               DosFreeMem ((PVOID) pVidInfo->lpImgBuf);
               }

            /***********************************************************
             * Close the file with mmioClose.
             ***********************************************************/
            rc = mmioClose (pVidInfo->hmmioSS, 0);

            DosFreeMem ((PVOID) pVidInfo);

            if (rc != MMIO_SUCCESS)
               return (rc);

            return (lRetCode);
            }  /* end case of MMIOM_CLOSE */

        /*#############################################################*
         * Get the NLS format Information.
         *#############################################################*/
        case MMIOM_GETFORMATINFO:
            {
            /***********************************************************
             * Declare local variables.
             ***********************************************************/
            PMMFORMATINFO       pmmformatinfo;

            /************************************************************
             * Set pointer to MMFORMATINFO structure.
             ************************************************************/
            pmmformatinfo = (PMMFORMATINFO) lParam1;

            /************************************************************
             * Fill in the values for the MMFORMATINFO structure.
             ************************************************************/
            pmmformatinfo->ulStructLen  = sizeof (MMFORMATINFO);
            pmmformatinfo->fccIOProc    = FOURCC_MMOT;
            pmmformatinfo->ulIOProcType = MMIO_IOPROC_FILEFORMAT;
            pmmformatinfo->ulMediaType  = MMIO_MEDIATYPE_IMAGE;

            pmmformatinfo->ulFlags      = MMIO_CANREADTRANSLATED        |
                                          MMIO_CANREADUNTRANSLATED      |
                                          MMIO_CANWRITETRANSLATED       |
                                          MMIO_CANWRITEUNTRANSLATED     |
                                          MMIO_CANREADWRITEUNTRANSLATED |
                                          MMIO_CANSEEKTRANSLATED        |
                                          MMIO_CANSEEKUNTRANSLATED;

            strcpy ((PSZ) pmmformatinfo->szDefaultFormatExt, pszMotionExt);
            if( IOPRNLS_GetNLSData(&pmmformatinfo->ulCodePage,
                                   &pmmformatinfo->ulLanguage ) )
               return ( MMIO_ERROR );

            if ( GetFormatStringLength( IMMO_FORMATNAMEKEY,
                                       &(pmmformatinfo->lNameLength) ) )
               return ( MMIO_ERROR );

////////////if (GetNLSData( &pmmformatinfo->ulCodePage,
////////////                &pmmformatinfo->ulLanguage ))
////////////   {
////////////   return( -1L );
////////////   }
////////////
////////////if (GetFormatStringLength( FOURCC_MMOT,
////////////                           &(pmmformatinfo->lNameLength) ))
////////////   {
////////////   return( -1L );
////////////   }


            /************************************************************
             * Return success back to the application.
             ************************************************************/
            return (MMIO_SUCCESS);
            } /* end case of MMIOM_GETFORMATINFO */

        /*#############################################################*
         * Get the NLS format name.
         *#############################################################*/
        case MMIOM_GETFORMATNAME:
            {
            LONG lBytesCopied;

            /************************************************************
             * Copy the M-Motion format string into buffer supplied by
             * lParam1.  Only put in the amount of my string up to the
             * allocated amount which is in lParam2.  Leave enough room
             * for the NULL termination.
             ************************************************************/
            lBytesCopied = IOPRNLS_GetFormatString(IOPRNLS_IMMO_BASE,
                                                   (char *)lParam1,
                                                   lParam2 );
////////////lBytesCopied = GetFormatString( FOURCC_MMOT,
////////////                                (char *)lParam1,
////////////                                lParam2 );

            return (lBytesCopied);
            } /* end case of MMIOM_GETFORMATNAME */

        /*#############################################################*
         * Get the file header.
         *#############################################################*/
        case MMIOM_GETHEADER:
            {
            /************************************************************
             * Declare local variables.
             ************************************************************/
            PMMFILESTATUS       pVidInfo;

            /************************************************************
             * Check for valid MMIOINFO block.
             ************************************************************/
            if (!pmmioinfo)
                return (0);

            /************************************************************
             * Set up our working file status variable.
             ************************************************************/
            pVidInfo = (PMMFILESTATUS)pmmioinfo->pExtraInfoStruct;

            /**************************************************
             * Getheader only valid in READ or READ/WRITE mode.
             * There is no header to get in WRITE mode.  We
             * must also have a valid file handle to read from
             **************************************************/
            if ((pmmioinfo->ulFlags & MMIO_WRITE) ||
                (!(pVidInfo->hmmioSS)))
               return (0);

            /************************************************************
             * Check for Translation mode.
             ************************************************************/
            if (!(pmmioinfo->ulTranslate & MMIO_TRANSLATEHEADER))
                {
                /********************************************************
                 * Translation is off.
                 ********************************************************/
                if (lParam2 < sizeof (MMOTIONHEADER))
                    {
                    pmmioinfo->ulErrorRet = MMIOERR_INVALID_BUFFER_LENGTH;
                    return (0);
                    }

                if (!lParam1)
                    {
                    pmmioinfo->ulErrorRet = MMIOERR_INVALID_STRUCTURE;
                    return (0);
                    }

                /********************************************************
                 * Read in first 16 bytes to fill up MMotion header.
                 ********************************************************/
                memcpy ((PVOID) lParam1,
                        (PVOID)&pVidInfo->mmotHeader,
                        sizeof (MMOTIONHEADER));

                /* Indicate that the header has been set, which    */
                /* is meaningless in read mode, but allows the     */
                /* application to do writes in read/write mode     */
                pVidInfo->bSetHeader = TRUE;

                return (sizeof (MMOTIONHEADER));
                }   /* end IF NOT TRANSLATED block */

            /******************
             * TRANSLATION IS ON
             ******************/
            if (lParam2 < sizeof (MMIMAGEHEADER))
                {
                pmmioinfo->ulErrorRet = MMIOERR_INVALID_BUFFER_LENGTH;
                return (0);
                }

            if (!lParam1)
                {
                pmmioinfo->ulErrorRet = MMIOERR_INVALID_STRUCTURE;
                return (0);
                }

            memcpy ((PVOID)lParam1,
                    (PVOID)&pVidInfo->mmImgHdr,
                    sizeof (MMIMAGEHEADER));

            return (sizeof (MMIMAGEHEADER));
            } /* end case of MMIOM_GETHEADER */

        /*#############################################################*
         * Identify whether this file can be processed.
         *#############################################################*/
        case MMIOM_IDENTIFYFILE:
            {
            /************************************************************
             * Declare local variables.
             ************************************************************/
            MMOTIONHEADER   mmotHeader;    /* MMotion structure variable*/
            HMMIO           hmmioTemp;     /* MMIO File Handle          */
            ULONG           ulWidth;
            ULONG           ulHeight;
            ULONG           ulRequiredFileLength;
            ULONG           ulActualFileLength;
            BOOL             fValidMMotionFile = FALSE;

            ULONG           ulTempFlags = MMIO_READ | MMIO_DENYWRITE |
                                          MMIO_NOIDENTIFY;
                                           /* flags used for temp open  */
                                           /* and close                 */

            /************************************************************
             * We need either a file name (lParam1) or file handle (lParam2)
             ************************************************************/
            if (!lParam1 && !lParam2)
                return (MMIO_ERROR);

            /* Copy the file handle, assuming one was provided... */
            hmmioTemp = (HMMIO)lParam2;

            /************************************************************
             * If no handle, then open the file using the string name
             ************************************************************/
            if (!hmmioTemp)
               {
               if (!(hmmioTemp = mmioOpen ((PSZ) lParam1,
                                          NULL,
                                          ulTempFlags)))
                   {
                   return (MMIO_ERROR);
                   }
               }

            /************************************************************
             * Read in enough bytes to check out file.
             ************************************************************/
            if (sizeof (MMOTIONHEADER) !=
                            mmioRead (hmmioTemp,
                                      (PVOID) &mmotHeader,
                                      (ULONG) sizeof (MMOTIONHEADER)))
                {
                /********************************************************
                 * Fail so close file and then return.
                 ********************************************************/
                if (!lParam2) /* Don't close handle if provided to us  */
                   mmioClose (hmmioTemp, 0);
                return (MMIO_ERROR);
                }

            /************************************************************
             * Close file before returning.
             ************************************************************/
            if (!lParam2) /* Don't close handle if provided to us  */
               mmioClose (hmmioTemp, 0);

            /************************************************************
             * Check validity of file and return result.
             ************************************************************/
            if (memcmp (mmotHeader.mmID, "YUV12C", 6) == 0)
                {
                ulWidth = mmotHeader.mmXlen;
                ulHeight = mmotHeader.mmYlen;

                /* Calculate what the length of the file SHOULD be based on the */
                /*   header contents                                            */
                ulRequiredFileLength = (((ulWidth >> 2) * 6) * ulHeight) +
                                                       sizeof (MMOTIONHEADER);

                /* Query what the ACTUAL length of the file is                  */
                ulActualFileLength = (ULONG)mmioSeek (hmmioTemp, 0, SEEK_END);

                /* If these don't match, then it isn't a VALID M-Motion file    */
                /*     - regardless of what the header says.                    */
                if (ulRequiredFileLength == ulActualFileLength)
                   fValidMMotionFile = TRUE;
                else
                   fValidMMotionFile = FALSE;
                }  /* end header check block */

            /************************************************************
             * Close file before returning.
             ************************************************************/
            if (!lParam2)  /* Don't close handle if provided to us      */
               mmioClose (hmmioTemp, 0);

            if (fValidMMotionFile)
               return (MMIO_SUCCESS);
            else
               return (MMIO_ERROR);
            } /* end case of MMIOM_IDENTIFYFILE */

        /*#############################################################*/
        /*#############################################################*/
        case MMIOM_OPEN:
            {
            /************************************************************
             * Declare local variables
             ************************************************************/
            PMMFILESTATUS   pVidInfo;   /* pointer to a MMOTION file    */
                                        /* status structure that we will*/
                                        /* use for this file instance   */
            MMIMAGEHEADER   MMImgHdr;
            ULONG           ulRequiredFileLength;
            ULONG           ulActualFileLength;
            ULONG           ulWidth;
            ULONG           ulHeight;
            PBYTE           lpYUVBuf;
            ULONG           ulRowCount;
            ULONG           ulRGBBytesPerLine;
            ULONG           ulYUVBytesPerLine;
            LONG            rc;
            HMMIO           hmmioSS;
            PBYTE           lpRGBBufPtr;
            FOURCC          fccStorageSystem;   /* SS I/O Proc FOURCC       */
            MMIOINFO        mmioinfoSS;         /* I/O info block for SS ref*/
            PSZ pszFileName = (CHAR *)lParam1;  /* get the filename from    */
                                                /* parameter                */

            /************************************************************
             * Check for valid MMIOINFO block.
             ************************************************************/
            if (!pmmioinfo)
                return (MMIO_ERROR);

            /************************************************************
             * If flags show read and write then send back an error.  We
             * only support reading or writing but not both at the same
             * time on the same file.
             ************************************************************/
            if ((pmmioinfo->ulFlags & MMIO_READWRITE) &&
                ((pmmioinfo->ulTranslate & MMIO_TRANSLATEDATA) ||
                 (pmmioinfo->ulTranslate & MMIO_TRANSLATEHEADER)))
                {
                return (MMIO_ERROR);
                }

            /*********************************************************
             * Determine the storage system/child IOProc that actually
             *   obtains the data for us.   The M-Motion data may be contained
             *   in a memory (RAM) file, as a component in a database or
             *   library (a Compound file), or as a stand-alone disk file.
             *
             * While the Application uses this M-Motion IOProc to obtain
             *   untranslated (MMotion) or translated (Bitmap) data,
             *   the IOProc must obtain it's data from something that
             *   reads and writes to a storage media.  The exact storage
             *   media is immaterial - so long as the read and write
             *   operations generate data that LOOKS like it is part
             *   of a standard file.
             *********************************************************/
            if (!pmmioinfo->fccChildIOProc)
               {
               /* Need to determine SS if create from pmmioinfo and filename. */
               if (pmmioinfo->ulFlags & MMIO_CREATE)
                  {
                  if (mmioDetermineSSIOProc( pszFileName,
                                             pmmioinfo,
                                             &fccStorageSystem,
                                             NULL ))
                     {
                     fccStorageSystem = FOURCC_DOS;
                     }
                  }
               else
                  {
                  if (mmioIdentifyStorageSystem( pszFileName,
                                                 pmmioinfo,
                                                 &fccStorageSystem ))
                     {
                     return (MMIO_ERROR);
                     }

                  }

               if (!fccStorageSystem)
                  {
                  return (MMIO_ERROR);
                  }
               else
                  {
                  pmmioinfo->fccChildIOProc = fccStorageSystem;
                  }
               } /* end storage system identification block */

            /**************************************************************
             * Direct the open to the specific storage system necessary
             **************************************************************/
            memset( &mmioinfoSS, '\0', sizeof(MMIOINFO));
            memmove( &mmioinfoSS, pmmioinfo, sizeof(MMIOINFO));
            mmioinfoSS.pIOProc = NULL;
            mmioinfoSS.fccIOProc = pmmioinfo->fccChildIOProc;
            mmioinfoSS.ulFlags |= MMIO_NOIDENTIFY;

            /************************************************************
             * Try to open the file.  Add the NO IDENTIFY flag to
             *    ENSURE THAT WE DON'T LOOP RECURSIVELY!!!
             ************************************************************/
            hmmioSS = mmioOpen (pszFileName,
                                &mmioinfoSS,
                                mmioinfoSS.ulFlags);

            /************************************************************
             * Check if a DELETE was requested - mmioOpen returns a 1,
             *    so we much check this separately
             ************************************************************/
            if (pmmioinfo->ulFlags & MMIO_DELETE)
               {
               /* was the delete successful?         */
               if (!hmmioSS)
                  {
                  pmmioinfo->ulErrorRet = MMIOERR_DELETE_FAILED;
                  return (MMIO_ERROR);
                  }
               else
                  {
                  return (MMIO_SUCCESS);
                  }
               }

            /************************************************************
             * Check the return code from the open call for an error.
             *    If not delete, then the open should have worked.
             ************************************************************/
            if (!hmmioSS)
                return (MMIO_ERROR);

            /************************************************************
             * Allocate memory for one MMotion FileStatus structures
             ************************************************************/
            DosAllocMem ((PPVOID) &pVidInfo,
                                  sizeof (MMFILESTATUS),
                                  fALLOC);

            /************************************************************
             * Ensure the allocate was successful.  If not, then
             *   close the file and return open as unsucessful...
             ************************************************************/
            if (!pVidInfo)
               {
               mmioClose (hmmioSS, 0);
               return (MMIO_ERROR);
               }

            pVidInfo->hmmioSS = hmmioSS;

            /************************************************************
             * Store pointer to our MMFILESTATUS structure in
             * pExtraInfoStruct field that is provided for our use.
             ************************************************************/
            pmmioinfo->pExtraInfoStruct = (PVOID)pVidInfo;

            /************************************************************
             * Set the fields of the FileStatus structure that the
             * IOProc is responsible for.
             ************************************************************/
            InitFileStruct (pVidInfo);

            /*********************************************************
             * If this is a read, we need to check that is a M-Motion
             *    file and perhaps get the data.
             *********************************************************/
            if (pmmioinfo->ulFlags & MMIO_READ)
               {
               /*********************************************************
                * First we must get some basic information from the file
                * Read in data to fill up the MMOTIONHEADER structure.
                *
                * If the read is unsuccessful, this is not a M-Motion file
                *    and we should return a failure on the open
                *********************************************************/
               if (sizeof (MMOTIONHEADER) !=
                                   mmioRead (pVidInfo->hmmioSS,
                                             (PVOID) &pVidInfo->mmotHeader,
                                             (ULONG) sizeof (MMOTIONHEADER)))
                   {
                   mmioClose (pVidInfo->hmmioSS, 0);
                   DosFreeMem ((PVOID) pVidInfo);
                   return (MMIO_ERROR);
                   }

               /* Ensure this IS an MMotion file header before we continue  */
               if (strcmp (pVidInfo->mmotHeader.mmID, "YUV12C"))
                   {
                   mmioClose (pVidInfo->hmmioSS, 0);
                   DosFreeMem ((PVOID) pVidInfo);
                   return (MMIO_ERROR);
                   }

               /********************************************************
                * Set up width and height of image.
                ********************************************************/
               ulWidth  = (ULONG)pVidInfo->mmotHeader.mmXlen;
               ulHeight = (ULONG)pVidInfo->mmotHeader.mmYlen;

               /* Calculate what the length of the file SHOULD be based on the */
               /*   header contents                                            */
               ulRequiredFileLength = (((ulWidth >> 2) * 6) * ulHeight) +
                                                      sizeof (MMOTIONHEADER);

               /* Query what the ACTUAL length of the file is,                 */
               /*   then move back to just after the header.                   */
               ulActualFileLength = (ULONG)mmioSeek (pVidInfo->hmmioSS,
                                                    0, SEEK_END);

               mmioSeek (pVidInfo->hmmioSS, sizeof (MMOTIONHEADER), SEEK_SET);

               /* If these don't match, then it isn't a VALID M-Motion file    */
               /*    - regardless of what the header says.                     */
               if (ulRequiredFileLength != ulActualFileLength)
                   {
                   mmioClose (pVidInfo->hmmioSS, 0);
                   DosFreeMem ((PVOID) pVidInfo);
                   return (MMIO_ERROR);
                   }

               /************************************************************
                * If the app intends to read in translation mode, we must
                * allocate and set-up the buffer that will contain the RGB data
                *
                * We must also read in the data to insure that the first
                * read, seek, or get-header operation will have data
                * to use.  This is ONLY NECESSARY FOR TRANSLATED MODE
                * operations, since we must process reads/writes pretending
                * the image is stored from the Bottom-up.
                *
                ************************************************************
                ************************************************************
                * Fill out the MMIMAGEHEADER structure.
                ************************************************************/
               MMImgHdr.ulHeaderLength = sizeof (MMIMAGEHEADER);
               MMImgHdr.ulContentType  = MMIO_IMAGE_PHOTO;
               MMImgHdr.ulMediaType    = MMIO_MEDIATYPE_IMAGE;
               MMImgHdr.mmXDIBHeader.BMPInfoHeader2.cbFix =
                           sizeof (BITMAPINFOHEADER2);
               MMImgHdr.mmXDIBHeader.BMPInfoHeader2.cx              = ulWidth;
               MMImgHdr.mmXDIBHeader.BMPInfoHeader2.cy              = ulHeight;
               MMImgHdr.mmXDIBHeader.BMPInfoHeader2.cPlanes         = 1;
               MMImgHdr.mmXDIBHeader.BMPInfoHeader2.cBitCount       = 24;
               MMImgHdr.mmXDIBHeader.BMPInfoHeader2.ulCompression   =
                   BCA_UNCOMP;
               MMImgHdr.mmXDIBHeader.BMPInfoHeader2.cbImage         =
                   ulWidth * ulHeight * 3;
               MMImgHdr.mmXDIBHeader.BMPInfoHeader2.cxResolution    = 0L;
               MMImgHdr.mmXDIBHeader.BMPInfoHeader2.cyResolution    = 0L;
               MMImgHdr.mmXDIBHeader.BMPInfoHeader2.cclrUsed        = 0L;
               MMImgHdr.mmXDIBHeader.BMPInfoHeader2.cclrImportant   = 0L;
               MMImgHdr.mmXDIBHeader.BMPInfoHeader2.usUnits         = 0L;
               MMImgHdr.mmXDIBHeader.BMPInfoHeader2.usReserved      = 0L;
               MMImgHdr.mmXDIBHeader.BMPInfoHeader2.usRecording     =
                   BRA_BOTTOMUP;
               MMImgHdr.mmXDIBHeader.BMPInfoHeader2.usRendering     =
                   BRH_NOTHALFTONED;
               MMImgHdr.mmXDIBHeader.BMPInfoHeader2.cSize1          = 0L;
               MMImgHdr.mmXDIBHeader.BMPInfoHeader2.cSize2          = 0L;
               MMImgHdr.mmXDIBHeader.BMPInfoHeader2.ulColorEncoding = 0L;
               MMImgHdr.mmXDIBHeader.BMPInfoHeader2.ulIdentifier    = 0L;

               /************************************************************
                * Copy the image header into private area for later use.
                * This will be returned on a mmioGetHeader () call
                ************************************************************/
               pVidInfo->mmImgHdr = MMImgHdr;

               /********************************************************
                * YUV Bytes/Line are = 1 1/2 times the number of pels
                ********************************************************/
               ulYUVBytesPerLine = ulWidth + (ulWidth >> 1);

               /********************************************************
                * RGB Bytes/Line are = 2* YUV bytes/line
                ********************************************************/
               ulRGBBytesPerLine = (ulYUVBytesPerLine << 1);

               /********************************************************
                * Determine total bytes in image
                ********************************************************/
               pVidInfo->ulRGBTotalBytes = ulWidth * ulHeight * 3;

               /********************************************************
                * MMotion Images are ALWAYS on 4-pel boundaries, which also
                *   makes them on 4-byte/LONG boundaries which is used for
                *   Bitmaps.  Therefore, there are no extra pad bytes necessary
                ********************************************************/
               pVidInfo->ulImgPaddedBytesPerLine = ulWidth * 3;
               pVidInfo->ulImgTotalBytes = pVidInfo->ulRGBTotalBytes;

               /*********************************************************
                * For translated data READ mode, we must allocate a buffer,
                *    then get the YUV data from the file and load the RGB
                *    buffer.
                * PLACE FORMAT SPECIFIC CODE HERE TO LOAD IMAGE INTO BUFFER
                * The code below is M-Motion format specific.
                *********************************************************/
               if (pmmioinfo->ulTranslate & MMIO_TRANSLATEDATA)
                  {
                  /********************************************************
                   * Get space for full image buffer.
                   * This will be retained until the file is closed.
                   ********************************************************/
                  if (DosAllocMem ((PPVOID) &(pVidInfo->lpRGBBuf),
                                    pVidInfo->ulRGBTotalBytes,
                                    fALLOC))
                      {
                      mmioClose (pVidInfo->hmmioSS, 0);
                      DosFreeMem ((PVOID) pVidInfo);
                      return (MMIO_ERROR);
                      }

                  /********************************************************
                   * Get temporary space for one line YUV Buffer.
                   ********************************************************/
                  if (DosAllocMem ((PPVOID) &lpYUVBuf,
                                   ulYUVBytesPerLine,
                                   fALLOC))
                      {
                      mmioClose (pVidInfo->hmmioSS, 0);
                      DosFreeMem ((PVOID) pVidInfo);
                      return (MMIO_ERROR);
                      }

                  /********************************************************
                   * Initialize beginning buffer position
                   ********************************************************/
                  lpRGBBufPtr = pVidInfo->lpRGBBuf;

                  /********************************************************
                   * Read in YUV data one line at a time, converting
                   * from YUV to RGB, then placing in the image buffer
                   ********************************************************/
                  for (ulRowCount = 0;
                       ulRowCount < ulHeight;
                       ulRowCount++)

                      {
                      /****************************************************
                       * Read in one line.
                       ****************************************************/
                      rc = mmioRead (pVidInfo->hmmioSS,
                                     (PVOID) lpYUVBuf,
                                     (ULONG) ulYUVBytesPerLine);

                      /****************************************************
                       * Convert one line at a time.
                       ****************************************************/
                      ConvertOneLineYUVtoRGB (lpYUVBuf,
                                              lpRGBBufPtr,
                                              ulYUVBytesPerLine);

                      /****************************************************
                       * Make sure buffer ptr is correct for next convert.
                       ****************************************************/
                      lpRGBBufPtr += (LONG)ulRGBBytesPerLine;
                      }   /* end of FOR loop to read YUV data */

                  DosFreeMem (lpYUVBuf);

                  /********************************************************
                   * This changes from M-Motion's top-down form to OS/2
                   * PM's bottom-up bitmap form.
                   ********************************************************/
                  ImgBufferFlip (pVidInfo->lpRGBBuf,
                                 pVidInfo->ulImgPaddedBytesPerLine,
                                 ulHeight);

                  /********************************************************
                   * RGB Buffer now full, set position pointers to the
                   * beginning of the buffer.
                   ********************************************************/
                  pVidInfo->lImgBytePos =  0;
                  }   /*  end IF TRANSLATED block */
               } /* end IF READ block */

            return (MMIO_SUCCESS);
            } /* end case of MMIOM_OPEN */

        /*#############################################################*/
        /*#############################################################*/
        case MMIOM_QUERYHEADERLENGTH:
            {
            /************************************************************
             * If there is no MMIOINFO block then return an error.
             ************************************************************/
            if (!pmmioinfo)
                return (0);

            /************************************************************
             * If header is in translated mode then return the media
             * type specific structure size.
             ************************************************************/
            if (pmmioinfo->ulTranslate & MMIO_TRANSLATEHEADER)
                return (sizeof (MMIMAGEHEADER));

            else
                /********************************************************
                 * Header is not in translated mode so return the size
                 * of the MMotion header.
                 ********************************************************/
                return (sizeof (MMOTIONHEADER));

            break;
            } /* end case of MMIOM_QUERYHEADERLENGTH */

        /*#############################################################*/
        /*#############################################################*/
        case MMIOM_READ:
            {
            /************************************************************
             * Declare Local Variables
             ************************************************************/
            PMMFILESTATUS   pVidInfo;
            LONG            rc;
            LONG            lBytesToRead;

            /************************************************************
             * Check for valid MMIOINFO block.
             ************************************************************/
            if (!pmmioinfo)
                return (MMIO_ERROR);

            /************************************************************
             * Set up our working file status variable.
             ************************************************************/
            pVidInfo = (PMMFILESTATUS)pmmioinfo->pExtraInfoStruct;

            /************************************************************
             * Is Translate Data off?
             ************************************************************/
            if (!(pmmioinfo->ulTranslate & MMIO_TRANSLATEDATA))
                {
                /********************************************************
                 * Since no translation, provide exact number of bytes req
                 ********************************************************/
                if (!lParam1)
                    return (MMIO_ERROR);

                rc = mmioRead (pVidInfo->hmmioSS,
                               (PVOID) lParam1,
                               (ULONG) lParam2);

                return (rc);
                }

            /************************************************************
             * Otherwise, Translate Data is on...
             ************************************************************/

            /************************************************************
             * Ensure we do NOT write more data out than is remaining
             *    in the buffer.  The length of read was requested in
             *    image bytes, so confirm that there are that many of
             *    virtual bytes remaining.
             ************************************************************/
            if ((ULONG)(pVidInfo->lImgBytePos + lParam2) >
                 pVidInfo->ulImgTotalBytes)
               lBytesToRead =
                   pVidInfo->ulImgTotalBytes - pVidInfo->lImgBytePos;
            else
               lBytesToRead = (ULONG)lParam2;

            /************************************************************
             * Perform this block on ALL reads.  The image data should
             * be in the RGB buffer at this point, and can be handed
             * to the application.
             *
             * Conveniently, the virtual image position is the same
             *    as the RGB buffer position, since both are 24 bit-RGB
             ************************************************************/
            memcpy ((PVOID)lParam1,
                    &(pVidInfo->lpRGBBuf[pVidInfo->lImgBytePos]),
                    lBytesToRead);

            /************************************************************
             * Move RGB Buffer pointer forward by number of bytes read.
             * The Img buffer pos is identical, since both are 24 bits
             ************************************************************/
            pVidInfo->lImgBytePos += lBytesToRead;

            return (lBytesToRead);
            }   /* end case  of MMIOM_READ */

        /*#############################################################*/
        /*#############################################################*/
        case MMIOM_SEEK:
            {
            /************************************************************
             * Set up locals.
             ************************************************************/
            PMMFILESTATUS   pVidInfo;
            LONG            lNewFilePosition;
            LONG            lPosDesired;
            SHORT           sSeekMode;

            /************************************************************
             * Check to make sure MMIOINFO block is valid.
             ************************************************************/
            if (!pmmioinfo)
                return (MMIO_ERROR);

            /************************************************************
             * Set up our working file status variable.
             ************************************************************/
            pVidInfo = (PMMFILESTATUS)pmmioinfo->pExtraInfoStruct;

            lPosDesired = lParam1;
            sSeekMode = (SHORT)lParam2;

            /************************************************************
             * Is Translate Data on?
             ************************************************************/
            if (pmmioinfo->ulTranslate & MMIO_TRANSLATEDATA)
                {
                /********************************************************
                 * Attempt to move the Image buffer pointer to the
                 *    desired location.  App sends SEEK requests in
                 *    positions relative to the image planes & bits/pel
                 * We must also convert this to RGB positions
                 ********************************************************/
                switch (sSeekMode)
                    {
                    case SEEK_SET:
                        {
                        lNewFilePosition = lPosDesired;
                        break;
                        }
                    case SEEK_CUR:
                        {
                        lNewFilePosition = pVidInfo->lImgBytePos + lPosDesired;
                        break;
                        }
                    case SEEK_END:
                        {

                        lNewFilePosition =
                                     pVidInfo->ulImgTotalBytes += lPosDesired;
                        break;
                        }

                    default :
                       return (MMIO_ERROR);
                    }

                /********************************************************
                 * Make sure seek did not go before start of file.
                 * If so, then don't change anything, just return an error
                 ********************************************************/
                if (lNewFilePosition < 0)
                   {
                   return (MMIO_ERROR);
                   }

                /********************************************************
                 * Make sure seek did not go past the end of file.
                 ********************************************************/
                if (lNewFilePosition > (LONG)pVidInfo->ulImgTotalBytes)
                    lNewFilePosition = pVidInfo->ulImgTotalBytes;

                pVidInfo->lImgBytePos = lNewFilePosition;

                return (pVidInfo->lImgBytePos);
                } /* end  IF DATA TRANSLATED */

            /************************************************************
             * Translate Data is OFF...
             ************************************************************
             * if this is a seek from the beginning of the file,
             *    we must account for and pass the header
             ************************************************************/
            if (lParam2==SEEK_SET)
               lPosDesired += MMOTION_HEADER_SIZE;

            lNewFilePosition = mmioSeek (pVidInfo->hmmioSS,
                                         lPosDesired,
                                         sSeekMode);

            /********************************************
             * Ensure we did not move to within the header
             ********************************************/
            if ((lNewFilePosition != MMIO_ERROR) &&
                (lNewFilePosition < MMOTION_HEADER_SIZE))
               {
               lNewFilePosition = mmioSeek (pVidInfo->hmmioSS,
                                             (LONG)MMOTION_HEADER_SIZE,
                                             SEEK_SET);
               }

            /************************************************************
             * Return new position.  Always remove the length of the
             *    header from the this position value
             ************************************************************/
            if (lNewFilePosition != MMIO_ERROR)
               lNewFilePosition -= MMOTION_HEADER_SIZE;

            return (lNewFilePosition);
            }  /* end case of MMIOM_SEEK */

        /*#############################################################*/
        /*#############################################################*/
        case MMIOM_SETHEADER:
            {
            /************************************************************
             * Declare local variables.
             ************************************************************/
            PMMIMAGEHEADER          pMMImgHdr;
            PMMFILESTATUS           pVidInfo;
            USHORT                  usNumColors;
            ULONG                   ulImgBitsPerLine;
            ULONG                   ulImgBytesPerLine;
            ULONG                   ulBytesWritten;
            ULONG                   ulWidth;
            ULONG                   ul4PelWidth;
            ULONG                   ulHeight;
            USHORT                  usPlanes;
            USHORT                  usBitCount;
            USHORT                  usPadBytes;

            /************************************************************
             * Check for valid MMIOINFO block.
             ************************************************************/
            if (!pmmioinfo)
                return (MMIO_ERROR);

            /************************************************************
             * Set up our working variable MMFILESTATUS.
             ************************************************************/
            pVidInfo = (PMMFILESTATUS) pmmioinfo->pExtraInfoStruct;

            /************************************************************
             * Only allow this function if we are in WRITE mode
             * And only if we have not already set the header
             ************************************************************/
            if ((!(pmmioinfo->ulFlags & MMIO_WRITE)) ||
                (!(pVidInfo->hmmioSS)) ||
                (pVidInfo->bSetHeader))
                return (0);

            /********************************************************
             * Make sure lParam1 is a valid pointer
             ********************************************************/
            if (!lParam1)
                {
                pmmioinfo->ulErrorRet = MMIOERR_INVALID_STRUCTURE;
                return (0);
                }

            /************************************************************
             * Header is not in translated mode.
             ************************************************************/
            if (!(pmmioinfo->ulTranslate & MMIO_TRANSLATEHEADER))
                {
                /********************************************************
                 * Make sure lParam2 is correct size
                 ********************************************************/
                if (lParam2 != MMOTION_HEADER_SIZE)
                    {
                    pmmioinfo->ulErrorRet = MMIOERR_INVALID_BUFFER_LENGTH;
                    return (0);
                    }

                /********************************************************
                 * Ensure that the header at least begins with "YUV12C"
                 ********************************************************/
                if (strncmp ((char *)lParam1, "YUV12C", 6))
                   {
                   pmmioinfo->ulErrorRet = MMIOERR_INVALID_STRUCTURE;
                   return (0);
                   }

                /********************************************************
                 * Take 16 byte buffer (lParam1), write to file and .
                 *    copy to internal structure.
                 ********************************************************/
                memcpy ((PVOID)&pVidInfo->mmotHeader,
                        (PVOID)lParam1, (ULONG) MMOTION_HEADER_SIZE);
                ulBytesWritten = mmioWrite (pVidInfo->hmmioSS,
                                            (PVOID) lParam1,
                                            (ULONG) MMOTION_HEADER_SIZE);

                /*********************************
                 * Check for an error on the write..
                 *********************************/
                if (ulBytesWritten != MMOTION_HEADER_SIZE)
                    return (0);   /* 0 indicates error */

                /*********************************
                 * Success...
                 *********************************/
                pVidInfo->bSetHeader = TRUE;
                return (sizeof (MMOTIONHEADER));
                }  /* end IF NOT TRANSLATED block */

            /************************************************************
             * Header is translated.
             ************************************************************/

            /************************************************************
             * Create local pointer media specific structure.
             ************************************************************/
            pMMImgHdr = (PMMIMAGEHEADER) lParam1;


            /************************************************************
             * Check for validity of header contents supplied
             ************************************************************
             *  --  Length must be that of the standard header
             *  --  NO Compression
             *      1 plane
             *      24, 8, 4 or 1 bpp
             ************************************************************/
            usBitCount = pMMImgHdr->mmXDIBHeader.BMPInfoHeader2.cBitCount;
            if ((pMMImgHdr->mmXDIBHeader.BMPInfoHeader2.ulCompression !=
                 BCA_UNCOMP) ||
                (pMMImgHdr->mmXDIBHeader.BMPInfoHeader2.cPlanes != 1) ||
                (! ((usBitCount == 24) || (usBitCount == 8) ||
                    (usBitCount == 4) || (usBitCount == 1)))
               )
                {
                pmmioinfo->ulErrorRet = MMIOERR_INVALID_STRUCTURE;
                return (0);
                }

            if (lParam2 != sizeof (MMIMAGEHEADER))
               {
               pmmioinfo->ulErrorRet = MMIOERR_INVALID_BUFFER_LENGTH;
               return (0);
               }

            /************************************************************
             * Complete MMOTIONHEADER.
             ************************************************************/
            memcpy ((PVOID)&pVidInfo->mmotHeader.mmID, "YUV12C", 6);
            pVidInfo->mmotHeader.mmXorg  = 0;
            pVidInfo->mmotHeader.mmYorg  = 0;

            /********************************************************
             * Ensure we will save stuff on 4-pel boundaries when
             *     we actually convert to YUV and pack the bits.
             * We DON'T change what the user is actually going to
             *     give us.  HE thinks he is on 1-pel boundaries,
             *     and that is how we buffer the RGB data.
             ********************************************************/
            ulWidth  = pMMImgHdr->mmXDIBHeader.BMPInfoHeader2.cx;
            ulHeight = pMMImgHdr->mmXDIBHeader.BMPInfoHeader2.cy;
            if (ulWidth % 4)
                ul4PelWidth = (((ulWidth >> 2) + 1) << 2);
            else
                ul4PelWidth = ulWidth;
            pVidInfo->mmotHeader.mmXlen  = (USHORT) ul4PelWidth;
            pVidInfo->mmotHeader.mmYlen  = (USHORT) ulHeight;

            /************************************************************
             * Write the MMotion Header.
             ************************************************************/
            ulBytesWritten = mmioWrite (pVidInfo->hmmioSS,
                                        (PVOID) &pVidInfo->mmotHeader,
                                        (ULONG) MMOTION_HEADER_SIZE);

            /***************************************
             * Check for an error on the write...
             ***************************************/
            if (ulBytesWritten != MMOTION_HEADER_SIZE)
                return (0);

            /************************************************************
             * Flag that MMIOM_SETHEADER has been done.  It can only
             *    be done ONCE for a file.  All future attempts will
             *    be flagged as errors.
             ************************************************************/
            pVidInfo->bSetHeader = TRUE;

            /************************************************************
             * Create copy of MMIMAGEHEADER for future use.
             ************************************************************/
            pVidInfo->mmImgHdr = *pMMImgHdr;

            /************************************************************
             * Check bitcount, set palette if less than 24.
             ************************************************************/
            if (usBitCount < 24)
               {
               /*********************************************************
                * Find out how many colors are in the palette.
                *********************************************************/
               usNumColors = (USHORT)(1 << usBitCount);

               /*********************************************************
                * Take the RGB2 Palette and convert it to an RGB palette
                *    Place the converted palette in MMFILESTATUS struct
                *********************************************************/
               RGB2_To_RGB (pVidInfo->mmImgHdr.bmiColors,
                            (PRGB) &(pVidInfo->rgbPalette),
                            usNumColors);
               }

            /*********************************************************
             * We must allocate the buffer.  The app will load the
             * buffer on subsequent write calls
             *********************************************************/
            usPlanes = pVidInfo->mmImgHdr.mmXDIBHeader.BMPInfoHeader2.cPlanes;

            /********************************************************
             * Determine total Image size
             ********************************************************
             * Find bits-per-line BEFORE padding and 1bpp or 4bpp pel overflow
             ********************************************************/
            ulImgBitsPerLine = ulWidth * usPlanes * usBitCount;
            ulImgBytesPerLine = ulImgBitsPerLine >> 3;

            /********************************************************
             * Account for extra pels not on an even byte boundary
             *   for 1bpp and 4bpp
             ********************************************************/
            if (ulImgBitsPerLine % 8)
               ulImgBytesPerLine ++;

            pVidInfo->ulImgPelBytesPerLine = ulImgBytesPerLine;

            /********************************************************
             * Ensure the row length in bytes accounts for byte padding.
             *   All bitmap data rows are aligned on LONG/4-BYTE boundaries.
             *   The data FROM an application should always appear in this form
             ********************************************************/
            usPadBytes = (USHORT)(ulImgBytesPerLine % 4);
            if (usPadBytes)
               ulImgBytesPerLine += 4 - usPadBytes;

            pVidInfo->ulImgPaddedBytesPerLine  = ulImgBytesPerLine;
            pVidInfo->ulImgTotalBytes = ulImgBytesPerLine * ulHeight;

            /********************************************************
             * Get space for full image buffer.
             ********************************************************/
            if (DosAllocMem ((PPVOID) &(pVidInfo->lpImgBuf),
                              pVidInfo->ulImgTotalBytes,
                              fALLOC))
                return (MMIO_ERROR);

            /********************************************************
             * Set up initial pointer value within RGB buffer & image
             ********************************************************/
            pVidInfo->lImgBytePos = 0;

            return (sizeof (MMIMAGEHEADER));
            }  /* end case of MMIOM_SETHEADER */

        /*#############################################################*/
        /*#############################################################*/
        case MMIOM_WRITE:
            {
            /************************************************************
             * Declare Local Variables.
             ************************************************************/
            PMMFILESTATUS       pVidInfo;
            USHORT              usBitCount;
            LONG                lBytesWritten;
            ULONG               ulImgBytesToWrite;

            /************************************************************
             * Check for valid MMIOINFO block.
             ************************************************************/
            if (!pmmioinfo)
                return (MMIO_ERROR);

            /************************************************************
             * Set up our working variable MMFILESTATUS.
             ************************************************************/
            pVidInfo = (PMMFILESTATUS) pmmioinfo->pExtraInfoStruct;

            /************************************************************
             * See if a SetHeader has been done on this file.
             ************************************************************/
            if ((!pVidInfo) || (!pVidInfo->bSetHeader))
                {
                return (MMIO_ERROR);
                }

            if (!(pmmioinfo->ulTranslate & MMIO_TRANSLATEDATA))
                {
                /********************************************************
                 * Translation is off, take amount of bytes sent and
                 * write to the file.
                 ********************************************************
                 * Ensure that there is a data buffer to write from
                 ********************************************************/
                if (!lParam1)
                    return (MMIO_ERROR);

                lBytesWritten = mmioWrite (pVidInfo->hmmioSS,
                                           (PVOID) lParam1,
                                           (ULONG) lParam2);

                return (lBytesWritten);
                }

            /************************************************************
             ************************************************************
             * Translation is on.
             ************************************************************
             ************************************************************
             ************************************************************
             * Set up local variables
             ************************************************************/
            usBitCount =
               pVidInfo->mmImgHdr.mmXDIBHeader.BMPInfoHeader2.cBitCount;


            /************************************************************
             * Ensure we do not attempt to write past the end of the
             *    buffer...
             ************************************************************/
            if ((ULONG)(pVidInfo->lImgBytePos + lParam2) >
               pVidInfo->ulImgTotalBytes)
               ulImgBytesToWrite =
                   pVidInfo->ulImgTotalBytes - pVidInfo->lImgBytePos;
            else
               ulImgBytesToWrite = (ULONG)lParam2;

            /************************************************************
             * Write the data into the image buffer.  It will be converted to
             *   RGB, then YUV  when the file is closed.  This allows the
             *   application to seek to arbitrary  positions within the
             *   image in terms of the bits/pel, etc they are writing.
             ************************************************************/
            memcpy (&(pVidInfo->lpImgBuf[pVidInfo->lImgBytePos]),
                    (PVOID)lParam1,
                    ulImgBytesToWrite);

            /* Update current position in the image buffer */
            pVidInfo->lImgBytePos += ulImgBytesToWrite;

            return (ulImgBytesToWrite);
            }   /* end case of MMIOM_WRITE */

        /*
         * If the IOProc has a child IOProc, then pass the message on to the Child, otherwise
         * return Unsupported Message
         */
        default:
            {
             /*
              * Declare Local Variables.
              */
             PMMFILESTATUS       pVidInfo;
             LONG                lRC;

             /************************************************************
              * Check for valid MMIOINFO block.
              ************************************************************/
             if (!pmmioinfo)
                 return (MMIO_ERROR);

             /************************************************************
              * Set up our working variable MMFILESTATUS.
              ************************************************************/
             pVidInfo = (PMMFILESTATUS) pmmioinfo->pExtraInfoStruct;

             if (pVidInfo != NULL && pVidInfo->hmmioSS)
                {
                 lRC = mmioSendMessage (pVidInfo->hmmioSS,
                                        usMsg,
                                        lParam1,
                                        lParam2);
                 if (!lRC)
                    pmmioinfo->ulErrorRet = mmioGetLastError (pVidInfo->hmmioSS);
                 return (lRC);
                }
            else
               {
                if (pmmioinfo != NULL)
                   pmmioinfo->ulErrorRet = MMIOERR_UNSUPPORTED_MESSAGE;
                return (MMIOERR_UNSUPPORTED_MESSAGE);
               }

            }   /* end case of Default */

        } /* end SWITCH statement for MMIO messages */

    return (0);
    }      /* end of window procedure */
