/*----------------------------------------------------------------------+
| msvidc.c - Microsoft Video 1 Compressor                               |
|                                                                       |
| Copyright (c) 1990-1995 Microsoft Corporation.                        |
| Portions Copyright Media Vision Inc.                                  |
| All Rights Reserved.                                                  |
|                                                                       |
| You have a non-exclusive, worldwide, royalty-free, and perpetual      |
| license to use this source code in developing hardware, software      |
| (limited to drivers and other software required for hardware          |
| functionality), and firmware for video display and/or processing      |
| boards.   Microsoft makes no warranties, express or implied, with     |
| respect to the Video 1 codec, including without limitation warranties |
| of merchantability or fitness for a particular purpose.  Microsoft    |
| shall not be liable for any damages whatsoever, including without     |
| limitation consequential damages arising from your use of the Video 1 |
| codec.                                                                |
|                                                                       |
|                                                                       |
+----------------------------------------------------------------------*/
#include <win32.h>
#include <ole2.h>
#include <mmsystem.h>

#ifndef _INC_COMPDDK
#define _INC_COMPDDK    50      /* version number */
#endif

#include <vfw.h>

#ifdef _WIN32
#define abs(x)  ((x) < 0 ? -(x) : (x))
#endif

#ifdef _WIN32
#include <memory.h>     /* for memcpy */
#endif


#ifdef _WIN32
#define _FPInit()       0
#define _FPTerm(x)
#else
void _acrtused2(void) {}
extern LPVOID WINAPI _FPInit(void);
extern void   WINAPI _FPTerm(LPVOID);
#endif

#include "msvidc.h"
#ifdef _WIN32
#include "profile.h"
#endif

#ifndef _WIN32
static BOOL gf286 = FALSE;
#endif

#define FOURCC_MSVC     mmioFOURCC('M','S','V','C')
#define FOURCC_CRAM     mmioFOURCC('C','R','A','M')
#define FOURCC_Cram     mmioFOURCC('C','r','a','m')
#define TWOCC_XX        aviTWOCC('d', 'c')

#define QUALITY_DEFAULT 2500

#define VERSION         0x00010000      // 1.0

ICSTATE   DefaultState = {75};

INT_PTR FAR PASCAL _LOADDS ConfigureDlgProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam);

#ifndef _WIN32
//
// put the compress stuff in the "rare" segment
//
#pragma alloc_text(_TEXT, ConfigureDlgProc)
#pragma alloc_text(_TEXT, CompressBegin)
#pragma alloc_text(_TEXT, CompressQuery)
#pragma alloc_text(_TEXT, CompressGetFormat)
#pragma alloc_text(_TEXT, Compress)
#pragma alloc_text(_TEXT, CompressGetSize)
#pragma alloc_text(_TEXT, CompressEnd)
#endif

/*****************************************************************************
 * dither stuff..
 ****************************************************************************/

#include <dith775.h>

LPVOID lpDitherTable;

//////////////////////////////////////////////////////////////////////////////
//
//  Dither16InitScale()
//
//////////////////////////////////////////////////////////////////////////////

#pragma optimize("", off)
STATICFN LPVOID Dither16InitScale()
{
    LPVOID p;
    LPBYTE pbLookup;
    LPWORD pwScale;
    UINT   r,g,b;

    p = GlobalAllocPtr(GMEM_MOVEABLE|GMEM_SHARE, 32768l*2+64000);

    if (p == NULL)
        return NULL;

    pwScale  = (LPWORD)p;

    for (r=0; r<32; r++)
        for (g=0; g<32; g++)
            for (b=0; b<32; b++)
                *pwScale++ = 1600 * r + 40 * g + b;

    pbLookup = (LPBYTE)(((WORD _huge *)p) + 32768l);

    for (r=0; r<40; r++)
        for (g=0; g<40; g++)
            for (b=0; b<40; b++)
                *pbLookup++ = lookup775[35*rlevel[r] + 5*glevel[g] + blevel[b]];

    return p;
}
#pragma optimize("", on)

/*****************************************************************************
 ****************************************************************************/
#ifndef _WIN32
BOOL NEAR PASCAL VideoLoad(void)
{
    gf286 = (BOOL)(GetWinFlags() & WF_CPU286);

#ifdef DEBUG
    gf286 = GetProfileIntA("Debug", "cpu", gf286 ? 286 : 386) == 286;
#endif

    return TRUE;
}
#endif

/*****************************************************************************
 ****************************************************************************/
void NEAR PASCAL VideoFree()
{
    // CompressFrameFree();        // let compression stuff clean up...

    if (lpDitherTable != NULL) {
        GlobalFreePtr(lpDitherTable);
        lpDitherTable = NULL;
    }
}

/*****************************************************************************
 ****************************************************************************/
INSTINFO * NEAR PASCAL VideoOpen(ICOPEN FAR * icinfo)
{
    INSTINFO *  pinst;

    //
    // refuse to open if we are not being opened as a Video compressor
    //
    if (icinfo->fccType != ICTYPE_VIDEO)
        return NULL;

    pinst = (INSTINFO *)LocalAlloc(LPTR, sizeof(INSTINFO));

    if (!pinst) {
        icinfo->dwError = (DWORD)ICERR_MEMORY;
        return NULL;
    }

    //
    // init structure
    //
    pinst->dwFlags = icinfo->dwFlags;
    pinst->nCompress = 0;
    pinst->nDecompress = 0;
    pinst->nDraw = 0;

    //
    // set the default state.
    //
    SetState(pinst, NULL, 0);

    //
    // return success.
    //
    icinfo->dwError = ICERR_OK;

    return pinst;
}

/*****************************************************************************
 ****************************************************************************/
LONG NEAR PASCAL VideoClose(INSTINFO * pinst)
{
    while (pinst->nCompress > 0)
        CompressEnd(pinst);

    while (pinst->nDecompress > 0)
        DecompressEnd(pinst);

    while (pinst->nDraw > 0)
        DrawEnd(pinst);

    LocalFree((HLOCAL)pinst);

    return 1;
}

/*****************************************************************************
 ****************************************************************************/

#ifndef QueryAbout
BOOL NEAR PASCAL QueryAbout(INSTINFO * pinst)
{
    return TRUE;
}
#endif

LONG NEAR PASCAL About(INSTINFO * pinst, HWND hwnd)
{
    char achDescription[128];
    char achAbout[64];

    LoadStringA(ghModule, IDS_DESCRIPTION, achDescription, sizeof(achDescription));
    LoadStringA(ghModule, IDS_ABOUT, achAbout, sizeof(achAbout));

    MessageBoxA(hwnd,achDescription,achAbout,
    MB_OK|MB_ICONINFORMATION);
    return ICERR_OK;
}

/*****************************************************************************
 ****************************************************************************/
#ifndef QueryConfigure
BOOL NEAR PASCAL QueryConfigure(INSTINFO * pinst)
{
    return TRUE;
}
#endif

LONG NEAR PASCAL Configure(INSTINFO * pinst, HWND hwnd)
{
    return (LONG) DialogBoxParam(ghModule,TEXT("Configure"),hwnd,ConfigureDlgProc, (LONG_PTR)(UINT_PTR)pinst);
}

/*****************************************************************************
 ****************************************************************************/
LONG NEAR PASCAL GetState(INSTINFO * pinst, LPVOID pv, DWORD dwSize)
{
    if (pv == NULL || dwSize == 0)
        return sizeof(ICSTATE);

    if (dwSize < sizeof(ICSTATE))
        return 0;

    *((ICSTATE FAR *)pv) = pinst->CurrentState;

    // return number of bytes copied
    return sizeof(ICSTATE);
}

/*****************************************************************************
 ****************************************************************************/
LONG NEAR PASCAL SetState(INSTINFO * pinst, LPVOID pv, DWORD dwSize)
{
    if (pv == NULL)
        pinst->CurrentState = DefaultState;
    else if (dwSize == sizeof(ICSTATE))
        pinst->CurrentState = *((ICSTATE FAR *)pv);
    else
        return 0;

    // return number of bytes copied
    return sizeof(ICSTATE);
}

#if !defined NUMELMS
 #define NUMELMS(aa) (sizeof(aa)/sizeof((aa)[0]))
#endif

#if defined _WIN32 && !defined UNICODE

int LoadUnicodeString(HINSTANCE hinst, UINT wID, LPWSTR lpBuffer, int cchBuffer)
{
    char    ach[128];
    int     i;

    i = LoadString(hinst, wID, ach, sizeof(ach));

    if (i > 0)
        MultiByteToWideChar(CP_ACP, 0, ach, -1, lpBuffer, cchBuffer);

    return i;
}

#else
#define LoadUnicodeString   LoadString
#endif

/*****************************************************************************
 ****************************************************************************/
LONG NEAR PASCAL GetInfo(INSTINFO * pinst, ICINFO FAR *icinfo, DWORD dwSize)
{
    if (icinfo == NULL)
        return sizeof(ICINFO);

    if (dwSize < sizeof(ICINFO))
        return 0;

    icinfo->dwSize            = sizeof(ICINFO);
    icinfo->fccType           = ICTYPE_VIDEO;
    icinfo->fccHandler        = FOURCC_MSVC;
    icinfo->dwFlags           = VIDCF_QUALITY   |  // supports quality
                                VIDCF_TEMPORAL;    // supports inter-frame
    icinfo->dwVersion         = VERSION;
    icinfo->dwVersionICM      = ICVERSION;

    LoadUnicodeString(ghModule, IDS_DESCRIPTION, icinfo->szDescription, NUMELMS(icinfo->szDescription));
    LoadUnicodeString(ghModule, IDS_NAME, icinfo->szName, NUMELMS(icinfo->szName));

    return sizeof(ICINFO);
}

/*****************************************************************************
 ****************************************************************************/
LONG FAR PASCAL CompressQuery(INSTINFO * pinst, LPBITMAPINFOHEADER lpbiIn, LPBITMAPINFOHEADER lpbiOut)
{
    if (lpbiOut)
         DPF(("CompressQuery %dx%dx%d --> %dx%dx%d'%4.4hs'", (int)lpbiIn->biWidth, (int)lpbiIn->biHeight, (int)lpbiIn->biBitCount, (int)lpbiOut->biWidth, (int)lpbiOut->biHeight, (int)lpbiOut->biBitCount, (LPSTR)&lpbiOut->biCompression));
    else
        DPF(("CompressQuery %dx%dx%d", (int)lpbiIn->biWidth, (int)lpbiIn->biHeight, (int)lpbiIn->biBitCount));

    //
    // determine if the input DIB data is in a format we like.
    //
    if (lpbiIn == NULL ||
        !(lpbiIn->biBitCount == 8  ||
          lpbiIn->biBitCount == 16 ||
          lpbiIn->biBitCount == 24 ||
          lpbiIn->biBitCount == 32) ||
        lpbiIn->biPlanes != 1 ||
        lpbiIn->biWidth < 4 ||
        lpbiIn->biHeight < 4 ||
        lpbiIn->biCompression != BI_RGB)
        return ICERR_BADFORMAT;

    //
    //  are we being asked to query just the input format?
    //
    if (lpbiOut == NULL)
        return ICERR_OK;

    //
    // make sure we can handle the format to compress to also.
    //
    if (!(lpbiOut->biCompression == FOURCC_MSVC ||  // must be 'MSVC' or 'CRAM'
          lpbiOut->biCompression == FOURCC_CRAM) ||
        !(lpbiOut->biBitCount == 16 ||              // must be 8 or 16
          lpbiOut->biBitCount == 8) ||
        (lpbiOut->biPlanes != 1) ||
        (lpbiOut->biWidth & ~3)  != (lpbiIn->biWidth & ~3)   || // must be 1:1 (no stretch)
        (lpbiOut->biHeight & ~3) != (lpbiIn->biHeight & ~3))
        return ICERR_BADFORMAT;

    return ICERR_OK;
}

/*****************************************************************************
 ****************************************************************************/
LONG FAR PASCAL CompressGetFormat(INSTINFO * pinst, LPBITMAPINFOHEADER lpbiIn, LPBITMAPINFOHEADER lpbiOut)
{
    LONG l;

    if (l = CompressQuery(pinst, lpbiIn, NULL))
        return l;

    if (lpbiIn->biBitCount == 8)
    {
        //
        // if lpbiOut == NULL then, return the size required to hold a output
        // format
        //

        DWORD dwClrUsed = lpbiIn->biClrUsed;
        if (dwClrUsed == 0) {
            dwClrUsed = 256;
        }
        l = lpbiIn->biSize + (int)dwClrUsed * sizeof(RGBQUAD);

        if (lpbiOut == NULL)
            return l;

        hmemcpy(lpbiOut, lpbiIn, (int)l);

        lpbiOut->biBitCount    = 8;
    }
    else
    {
        //
        // if lpbiOut == NULL then, return the size required to hold a output
        // format
        //
        if (lpbiOut == NULL)
            return (int)lpbiIn->biSize;

        *lpbiOut = *lpbiIn;

        lpbiOut->biClrUsed     = 0;
        lpbiOut->biBitCount    = 16;
    }

    lpbiOut->biWidth       = lpbiIn->biWidth  & ~3;
    lpbiOut->biHeight      = lpbiIn->biHeight & ~3;
    lpbiOut->biCompression = FOURCC_CRAM;
    lpbiOut->biSizeImage   = CompressGetSize(pinst, lpbiIn, lpbiOut);

    return ICERR_OK;
}

/*****************************************************************************
 ****************************************************************************/

LONG FAR PASCAL CompressBegin(INSTINFO * pinst, LPBITMAPINFOHEADER lpbiIn, LPBITMAPINFOHEADER lpbiOut)
{
    LONG l;

    if (l = CompressQuery(pinst, lpbiIn, lpbiOut))
        return l;

    DPF(("CompressBegin %dx%dx%d --> %dx%dx%d'%4.4ls'", (int)lpbiIn->biWidth, (int)lpbiIn->biHeight, (int)lpbiIn->biBitCount, (int)lpbiOut->biWidth, (int)lpbiOut->biHeight, (int)lpbiOut->biBitCount,(LPSTR)&lpbiOut->biCompression));

    //
    // initialize for compression, for real....
    //
    pinst->nCompress = 1;

    return CompressFrameBegin(lpbiIn, lpbiOut, &pinst->lpITable, pinst->rgbqOut);
}

/*****************************************************************************
 ****************************************************************************/
LONG FAR PASCAL CompressGetSize(INSTINFO * pinst, LPBITMAPINFOHEADER lpbiIn, LPBITMAPINFOHEADER lpbiOut)
{
    int dx,dy;

    dx = (int)lpbiIn->biWidth;
    dy = (int)lpbiIn->biHeight;

    /* maximum compressed size *** your code here *** */

    if (lpbiOut->biBitCount == 8)
        // worst case size of data 10 bytes per 16 pixels (8 colors + mask)
        // remember the EOF code!
        return ((DWORD)dx * (DWORD)dy * 10l) / 16l + 2l;
    else
        // worst case size of data 18 bytes per 16 pixels (8 colors + mask)
        // remember the EOF code!
        return ((DWORD)dx * (DWORD)dy * 10l) / 8l + 2l; // 10/8 ~= 18/16
////////return ((DWORD)dx * (DWORD)dy * 18l) / 16l + 2l;
}

/*****************************************************************************
 ****************************************************************************/
LONG FAR PASCAL Compress(INSTINFO * pinst, ICCOMPRESS FAR *icinfo, DWORD dwSize)
{
    LONG l;
    LPBITMAPINFOHEADER lpbiIn  = icinfo->lpbiInput;
    LPBITMAPINFOHEADER lpbiOut = icinfo->lpbiOutput;
    DWORD threshold;
    DWORD thresholdPrev;
    DWORD dwQualityPrev;
    DWORD dwQuality;
    BOOL  fBegin;
    LPVOID smag;
    PCELLS compressTemp;

    if (l = CompressQuery(pinst, icinfo->lpbiInput, icinfo->lpbiOutput))
        return l;

    //
    // check for being called without a BEGIN message, and do the begin for
    // the caller
    //
    if (fBegin = (pinst->nCompress == 0))
    {
        if (l = CompressBegin(pinst, icinfo->lpbiInput, icinfo->lpbiOutput))
            return l;
    }

    smag = _FPInit();

    DPF(("Compress %dx%dx%d --> %dx%dx%d'%4.4ls'", (int)lpbiIn->biWidth, (int)lpbiIn->biHeight, (int)lpbiIn->biBitCount, (int)lpbiOut->biWidth, (int)lpbiOut->biHeight, (int)lpbiOut->biBitCount, (LPSTR)&lpbiOut->biCompression));

    if (icinfo->dwQuality == ICQUALITY_DEFAULT)
        dwQuality = QUALITY_DEFAULT;
    else
        dwQuality = ICQUALITY_HIGH - icinfo->dwQuality;

    dwQualityPrev = MulDiv((UINT)dwQuality,100,pinst->CurrentState.wTemporalRatio);

    threshold     = QualityToThreshold(dwQuality);
    thresholdPrev = QualityToThreshold(dwQualityPrev);

    if (pinst->Status)
        pinst->Status(pinst->lParam, ICSTATUS_START, 0);

    // For Win16, this needs to be in the data segment so we
    // can use a near pointer to it.
    compressTemp = (PCELLS) LocalAlloc(LPTR, sizeof(CELLS));

    if (!compressTemp)
        return ICERR_MEMORY;

    if (lpbiOut->biBitCount == 8)
        l = CompressFrame8(
                  icinfo->lpbiInput,        // DIB header to compress
                  icinfo->lpInput,          // DIB bits to compress
                  icinfo->lpOutput,         // put compressed data here
                  threshold,                // edge threshold
                  thresholdPrev,            // inter-frame threshold
                  icinfo->lpbiPrev,         // previous frame
                  icinfo->lpPrev,           // previous frame
                  pinst->Status,            // status callback
                  pinst->lParam,
                  compressTemp,
                  pinst->lpITable,
                  pinst->rgbqOut);
    else
        l = CompressFrame16(
                  icinfo->lpbiInput,        // DIB header to compress
                  icinfo->lpInput,          // DIB bits to compress
                  icinfo->lpOutput,         // put compressed data here
                  threshold,                // edge threshold
                  thresholdPrev,            // inter-frame threshold
                  icinfo->lpbiPrev,         // previous frame
                  icinfo->lpPrev,           // previous frame
                  pinst->Status,            // status callback
                  pinst->lParam,
                  compressTemp);

    LocalFree((HLOCAL) compressTemp);

    if (pinst->Status)
        pinst->Status(pinst->lParam, ICSTATUS_END, 0);

    _FPTerm(smag);

    if (l == -1)
        return ICERR_ERROR;

    lpbiOut->biWidth       = lpbiIn->biWidth  & ~3;
    lpbiOut->biHeight      = lpbiIn->biHeight & ~3;
    lpbiOut->biCompression = FOURCC_CRAM;
    lpbiOut->biSizeImage   = l;
////lpbiOut->biBitCount    = 16;

    //
    // return the chunk id
    //
    if (icinfo->lpckid)
        *icinfo->lpckid = TWOCC_XX;

    //
    // set the AVI index flags,
    //
    //    make it a keyframe?
    //
    if (icinfo->lpdwFlags) {
        *icinfo->lpdwFlags = AVIIF_TWOCC;

        if (icinfo->lpbiPrev == NULL || numberOfSkips == 0)
            *icinfo->lpdwFlags |= AVIIF_KEYFRAME;
    }

    if (fBegin)
        CompressEnd(pinst);

    return ICERR_OK;
}

/*****************************************************************************
 ****************************************************************************/
LONG FAR PASCAL CompressEnd(INSTINFO * pinst)
{
    if (pinst->nCompress == 0)
        return ICERR_ERROR;

    pinst->nCompress = 0;

    return CompressFrameEnd(&pinst->lpITable);
}

/*****************************************************************************
 *
 *  decompress tables
 *
 *      indexed by:
 *          SRC:     0=8 bit Cram 1=16 bit Cram
 *          STRETCH: 0=1:1, 1=1:2
 *          DST:     0=8, 1=16, 2=24, 3=32
 *
 ****************************************************************************/

#ifdef _WIN32

DECOMPPROC  DecompressWin32[2][2][5] = {
        DecompressFrame8,               // Cram8  1:1 to 8
        NULL,                           // Cram8  1:1 to 16 (555)
        NULL,                           // Cram8  1:1 to 24
        NULL,                           // Cram8  1:1 to 32
        NULL,                           // Cram8  1:1 to 565

        DecompressFrame8X2C,            // Cram8  1:2 to 8
        NULL,                           // Cram8  1:2 to 16 (555)
        NULL,                           // Cram8  1:2 to 24
        NULL,                           // Cram8  1:2 to 32
        NULL,                           // Cram8  1:2 to 565

        DecompressFrame16To8C,          // Cram16 1:1 to 8
        DecompressFrame16To555C,        // Cram16 1:1 to 16 (555)
        DecompressFrame24,              // Cram16 1:1 to 24
        NULL,                           // Cram16 1:1 to 32
        DecompressFrame16To565C,        // Cram16 1:1 to 565

        DecompressFrame16To8X2C,        // Cram16 1:2 to 8
        NULL,                           // Cram16 1:2 to 16 (555)
        NULL,                           // Cram16 1:2 to 24
        NULL,                           // Cram16 1:2 to 32
        NULL};                          // Cram16 1:2 to 565

#else

DECOMPPROC  Decompress386[2][2][5] = {
        DecompressCram8,                // Cram8  1:1 to 8
        NULL,                           // Cram8  1:1 to 16 (555)
        NULL,                           // Cram8  1:1 to 24
        NULL,                           // Cram8  1:1 to 32
        NULL,                           // Cram8  1:1 to 565

        DecompressCram8x2,              // Cram8  1:2 to 8
        NULL,                           // Cram8  1:2 to 16 (555)
        NULL,                           // Cram8  1:2 to 24
        NULL,                           // Cram8  1:2 to 32
        NULL,                           // Cram8  1:2 to 565

        DecompressCram168,              // Cram16 1:1 to 8
        DecompressCram16,               // Cram16 1:1 to 16 (555)
        NULL,                           // Cram16 1:1 to 24
        NULL /* DecompressCram32 */,    // Cram16 1:1 to 32
        NULL /* DecompressFrame16To565C */,     // Cram16 1:1 to 565

        NULL,                           // Cram16 1:2 to 8
        DecompressCram16x2,             // Cram16 1:2 to 16 (555)
        NULL,                           // Cram16 1:2 to 24
        NULL,                           // Cram16 1:2 to 32
        NULL};                          // Cram16 1:2 to 565


DECOMPPROC  Decompress286[2][2][5] = {
        DecompressCram8_286,            // Cram8  1:1 to 8
        NULL,                           // Cram8  1:1 to 16 (555)
        NULL,                           // Cram8  1:1 to 24
        NULL,                           // Cram8  1:1 to 32
        NULL,                           // Cram8  1:1 to 565

        NULL,                           // Cram8  1:2 to 8
        NULL,                           // Cram8  1:2 to 16 (555)
        NULL,                           // Cram8  1:2 to 24
        NULL,                           // Cram8  1:2 to 32
        NULL,                           // Cram8  1:2 to 565

        NULL,                           // Cram16 1:1 to 8
        DecompressCram16_286,           // Cram16 1:1 to 16 (555)
        NULL,                           // Cram16 1:1 to 24
        NULL,                           // Cram16 1:1 to 32
        NULL,                           // Cram16 1:1 to 565

        NULL,                           // Cram16 1:2 to 8
        NULL,                           // Cram16 1:2 to 16 (555)
        NULL,                           // Cram16 1:2 to 24
        NULL,                           // Cram16 1:2 to 32
        NULL};                          // Cram16 1:2 to 565
#endif

/*****************************************************************************
 ****************************************************************************/
LONG NEAR PASCAL DecompressQueryFmt(
    INSTINFO * pinst,
    LPBITMAPINFOHEADER lpbiSrc)
{
    //
    // determine if the input DIB data is in a format we like.
    //
    if (lpbiSrc == NULL ||
        !(lpbiSrc->biBitCount == 16 || lpbiSrc->biBitCount == 8) ||
        (lpbiSrc->biPlanes != 1) ||
        !(lpbiSrc->biCompression == FOURCC_MSVC ||
          lpbiSrc->biCompression == FOURCC_CRAM))
        return ICERR_BADFORMAT;

    return ICERR_OK;
}

/*****************************************************************************
 ****************************************************************************/
LONG NEAR PASCAL DecompressQuery(
    INSTINFO * pinst,
    DWORD dwFlags,
    LPBITMAPINFOHEADER lpbiSrc,
    LPVOID pSrc,
    int xSrc,
    int ySrc,
    int dxSrc,
    int dySrc,
    LPBITMAPINFOHEADER lpbiDst,
    LPVOID pDst,
    int xDst,
    int yDst,
    int dxDst,
    int dyDst)
{
#ifndef _WIN32
    DWORD biSizeImage;
#endif
    DECOMPPROC fn;
    int s,d,n;

    //
    // determine if the input DIB data is in a format we like.
    //
    if (DecompressQueryFmt(pinst, lpbiSrc))
        return ICERR_BADFORMAT;

    //
    // allow (-1) as a default width/height
    //
    if (dxSrc == -1)
        dxSrc = (int)lpbiSrc->biWidth;

    if (dySrc == -1)
        dySrc = (int)lpbiSrc->biHeight;

    //
    //  we cant clip the source.
    //
    if (xSrc != 0 || ySrc != 0)
        return ICERR_BADPARAM;

    if ((dxSrc != (int)lpbiSrc->biWidth) || (dySrc != (int)lpbiSrc->biHeight))
        return ICERR_BADPARAM;

    //
    //  are we being asked to query just the input format?
    //
    if (lpbiDst == NULL)
        return ICERR_OK;

    //
    // allow (-1) as a default width/height
    //
    if (dxDst == -1)
        dxDst = (int)lpbiDst->biWidth;

    if (dyDst == -1)
        dyDst = abs((int)lpbiDst->biHeight);

#ifndef _WIN32
    if (gf286)
        biSizeImage = (DWORD)(UINT)abs((int)lpbiDst->biHeight)*(DWORD)(WORD)DIBWIDTHBYTES(*lpbiDst);
#endif

    s = lpbiSrc->biBitCount/8-1;    //  s = 0,1

#ifdef _WIN32
    // Can't support 16:32 access in our C version, of course....
    if (lpbiDst->biCompression == BI_1632) {
        return ICERR_BADFORMAT;
    }
#endif

    if (lpbiDst->biBitCount != 8 && lpbiDst->biBitCount != 16 && lpbiDst->biBitCount != 24 && lpbiDst->biBitCount != 32)
    {
        return ICERR_BADFORMAT;
    }

    // must be full dib or a '1632' DIB
    if (lpbiDst->biCompression != BI_RGB &&
        lpbiDst->biCompression != BI_1632) {
	    if (lpbiDst->biCompression != BI_BITFIELDS) {
		DPF(("MSVIDC asked to decompress to '%.4hs'!", &lpbiDst->biCompression));
		return ICERR_BADFORMAT;
	    }
	
            // allow 565 dibs
            if ((lpbiDst->biBitCount == 16) &&
                (((LPDWORD)(lpbiDst+1))[0] == 0x00f800) &&
                (((LPDWORD)(lpbiDst+1))[1] == 0x0007e0) &&
                (((LPDWORD)(lpbiDst+1))[2] == 0x00001f) ) {

                    // ok - its 565 format
                    d = 4;
            } else {
                DPF(("Bad bitmask (%lX %lX %lX) in %d-bit BI_BITMAP case!",
                     lpbiDst->biBitCount,
                     ((LPDWORD)(lpbiDst+1))[0],
                     ((LPDWORD)(lpbiDst+1))[1],
                     ((LPDWORD)(lpbiDst+1))[2]));
                return ICERR_BADFORMAT;
            }
    } else {
        d = lpbiDst->biBitCount/8-1;    //  d = 0,1,2,3

        if (lpbiDst->biCompression == BI_1632 && lpbiDst->biBitCount == 16) {

            if ((((LPDWORD)(lpbiDst+1))[0] == 0x007400) &&
                (((LPDWORD)(lpbiDst+1))[1] == 0x0003f0) &&
                (((LPDWORD)(lpbiDst+1))[2] == 0x00000f) ) {
                    // ok - it's 555 format
            } else if ((((LPDWORD)(lpbiDst+1))[0] == 0x00f800) &&
                (((LPDWORD)(lpbiDst+1))[1] == 0x0007e0) &&
                (((LPDWORD)(lpbiDst+1))[2] == 0x00001f) ) {

                    // ok - it's 565 format
                    d = 4;
            } else {
                DPF(("Bad bitmask (%lX %lX %lX) in 16-bit BI_1632 case!",
                     ((LPDWORD)(lpbiDst+1))[0],
                     ((LPDWORD)(lpbiDst+1))[1],
                     ((LPDWORD)(lpbiDst+1))[2]));
                return ICERR_BADFORMAT;
            }
        }

        // What about 24-bit BI_1632?  Should we check the masks?
    }

    //
    //  n = 0 for 1:1, 1 for 1:2
    //
    if (dxDst  == dxSrc && dyDst == dySrc)
        n = 0;
    else if (dxDst == dxSrc*2 && dyDst == dySrc*2)
        n = 1;
    else
        return ICERR_BADSIZE;

#ifdef DEBUG
    DPF(("DecompressQuery %dx%dx%d [%d,%d,%d,%d] --> %dx%dx%d (565) [%d,%d,%d,%d]",
        (int)lpbiSrc->biWidth, (int)lpbiSrc->biHeight, (int)lpbiSrc->biBitCount,
        xSrc, ySrc, dxSrc, dySrc,
        (int)lpbiDst->biWidth, (int)lpbiDst->biHeight, (int)lpbiDst->biBitCount,
        d == 4 ? "(565)" : "",
        xDst, yDst, dxDst, dyDst));

#endif

#ifdef _WIN32
    fn = DecompressWin32[s][n][d];
#else
    if (gf286)
    {
        fn = Decompress286[s][n][d];

        if (fn && biSizeImage > 64l*1024)
            fn = fn==DecompressCram8_286 ? DecompressFrame8 : NULL;
    }
    else
    {
        fn = Decompress386[s][n][d];
    }
#endif

    if (fn == NULL)
        return ICERR_BADFORMAT;

    pinst->DecompressTest = fn;     // return this to DecompressBegin.

    return ICERR_OK;
}

/*****************************************************************************
 ****************************************************************************/
LONG NEAR PASCAL DecompressGetFormat(INSTINFO * pinst, LPBITMAPINFOHEADER lpbiIn, LPBITMAPINFOHEADER lpbiOut)
{
    LONG l;
    int dx,dy;

    if (l = DecompressQueryFmt(pinst, lpbiIn))
        return l;

    //
    // if lpbiOut == NULL then, return the size required to hold a output
    // format
    //
    if (lpbiOut == NULL)
        return (int)lpbiIn->biSize + (int)lpbiIn->biClrUsed * sizeof(RGBQUAD);

    hmemcpy(lpbiOut, lpbiIn,
        (int)lpbiIn->biSize + (int)lpbiIn->biClrUsed * sizeof(RGBQUAD));

    dx = (int)lpbiIn->biWidth & ~3;
    dy = (int)lpbiIn->biHeight & ~3;

    lpbiOut->biWidth       = dx;
    lpbiOut->biHeight      = dy;
    lpbiOut->biBitCount    = lpbiIn->biBitCount;    // convert 8->8 16->16
    lpbiOut->biPlanes      = 1;

    lpbiOut->biCompression = BI_RGB;
    lpbiOut->biSizeImage   = (DWORD)(WORD)abs(dy)*(DWORD)(WORD)DIBWIDTHBYTES(*lpbiOut);

    return ICERR_OK;
}

/*****************************************************************************
 ****************************************************************************/
LONG NEAR PASCAL DecompressBegin(
    INSTINFO * pinst,
    DWORD dwFlags,
    LPBITMAPINFOHEADER lpbiSrc,
    LPVOID pSrc,
    int xSrc,
    int ySrc,
    int dxSrc,
    int dySrc,
    LPBITMAPINFOHEADER lpbiDst,
    LPVOID pDst,
    int xDst,
    int yDst,
    int dxDst,
    int dyDst)
{
    LONG l;

    if (l = DecompressQuery(pinst, dwFlags, lpbiSrc, pSrc, xSrc, ySrc, dxSrc, dySrc, lpbiDst, pDst, xDst, yDst, dxDst, dyDst))
        return l;

    pinst->DecompressProc = pinst->DecompressTest;

    //
    //  make sure biSizeImage is set, the decompress code needs it to be
    //
    if (lpbiDst->biSizeImage == 0)
        lpbiDst->biSizeImage = (DWORD)(WORD)abs((int)lpbiDst->biHeight)*(DWORD)(WORD)DIBWIDTHBYTES(*lpbiDst);

    //
    // init the dither tables !!! call MSVIDEO, dont have code here!!!
    //
    if (lpbiSrc->biBitCount == 16 &&
        lpbiDst->biBitCount == 8)
    {
        if (lpDitherTable == NULL)
            lpDitherTable = Dither16InitScale();

        if (lpDitherTable == NULL)
            return ICERR_MEMORY;
    }

    pinst->nDecompress = 1;

    return ICERR_OK;
}

/*****************************************************************************
 *
 *  Decompress
 *
 *  we can assume certain things here because DecompressQuery() only lets
 *  valid stuff in.
 *
 *      the source rect is always the entire source.
 *      the dest rect is either 1:1 or 1:2
 *
 ****************************************************************************/
LONG NEAR PASCAL Decompress(
    INSTINFO * pinst,
    DWORD dwFlags,
    LPBITMAPINFOHEADER lpbiSrc,
    LPVOID pSrc,
    int xSrc,
    int ySrc,
    int dxSrc,
    int dySrc,
    LPBITMAPINFOHEADER lpbiDst,
    LPVOID pDst,
    int xDst,
    int yDst,
    int dxDst,
    int dyDst)
{
    //
    //  if we are called without a begin do the begin now, but dont make
    //  the begin "stick"
    //
    if (pinst->nDecompress == 0)
    {
        LONG err;

        if (err = DecompressBegin(pinst, dwFlags, lpbiSrc, pSrc, xSrc, ySrc, dxSrc, dySrc, lpbiDst, pDst, xDst, yDst, dxDst, dyDst))
            return err;

        pinst->nDecompress = 0;
    }

#ifdef DEBUG
    if (lpbiDst->biSizeImage == 0)
        DebugBreak();

    if (pinst->DecompressProc == NULL)
        DebugBreak();
#endif

    (*pinst->DecompressProc)(lpbiSrc,pSrc,lpbiDst,pDst,xDst,yDst);

    return ICERR_OK;
}

/*****************************************************************************
 *
 * DecompressGetPalette() implements ICM_GET_PALETTE
 *
 * This function has no Compress...() equivalent
 *
 * It is used to pull the palette from a frame in order to possibly do
 * a palette change.
 *
 ****************************************************************************/
LONG NEAR PASCAL DecompressGetPalette(INSTINFO * pinst, LPBITMAPINFOHEADER lpbiIn, LPBITMAPINFOHEADER lpbiOut)
{
    LONG l;
    int i;
    RGBQUAD FAR * prgb;

    DPF(("DecompressGetPalette()"));

    if (l = DecompressQueryFmt(pinst, lpbiIn))
        return l;

    if (lpbiOut->biBitCount != 8)
        return ICERR_BADFORMAT;

    //
    // if you decompress full-color to 8 bit you need to put the "dither"
    // palette in lpbiOut
    //
    if (lpbiIn->biBitCount != 8)
    {
        lpbiOut->biClrUsed = 256;

        prgb = (LPVOID)(lpbiOut + 1);

        for (i=0; i<256; i++)
        {
            prgb[i].rgbRed      = dpal775[i][0];
            prgb[i].rgbGreen    = dpal775[i][1];
            prgb[i].rgbBlue     = dpal775[i][2];
            prgb[i].rgbReserved = 0;
        }

        return ICERR_OK;
    }

    if (lpbiIn->biClrUsed == 0)
        lpbiIn->biClrUsed = 256;

    //
    // return the 8bit palette used for decompression.
    //
    hmemcpy(
        (LPBYTE)lpbiOut + (int)lpbiOut->biSize,
        (LPBYTE)lpbiIn + (int)lpbiIn->biSize,
        (int)lpbiIn->biClrUsed * sizeof(RGBQUAD));

    lpbiOut->biClrUsed = lpbiIn->biClrUsed;

    return ICERR_OK;
}

/*****************************************************************************
 ****************************************************************************/
LONG NEAR PASCAL DecompressEnd(INSTINFO * pinst)
{
    if (pinst->nDecompress == 0)
        return ICERR_ERROR;

    pinst->nDecompress = 0;
    return ICERR_OK;
}

/*****************************************************************************
 ****************************************************************************/
LONG NEAR PASCAL DrawBegin(INSTINFO * pinst,ICDRAWBEGIN FAR *icinfo, DWORD dwSize)
{
    return ICERR_UNSUPPORTED;
}

/*****************************************************************************
 ****************************************************************************/
LONG NEAR PASCAL Draw(INSTINFO * pinst, ICDRAW FAR *icinfo, DWORD dwSize)
{
    return ICERR_UNSUPPORTED;
}

/*****************************************************************************
 ****************************************************************************/
LONG NEAR PASCAL DrawEnd(INSTINFO * pinst)
{
    return ICERR_UNSUPPORTED;
}

/*****************************************************************************
 ****************************************************************************/

INT_PTR FAR PASCAL _LOADDS ConfigureDlgProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
    int     i;
    HWND    hsb;
    TCHAR   ach[10];
    static  TCHAR chDecimal = TEXT('.');

    static  INSTINFO *pinst;

    #define SCROLL_MIN  1       // 0.00
    #define SCROLL_MAX  100     // 1.00

    switch (msg)
    {
        case WM_COMMAND:
            switch (wParam)
            {
                case IDOK:
                    hsb = GetDlgItem(hdlg,ID_SCROLL);
                    pinst->CurrentState.wTemporalRatio = GetScrollPos(hsb,SB_CTL);
                    EndDialog(hdlg,TRUE);
                    break;

                case IDCANCEL:
                    EndDialog(hdlg,FALSE);
                    break;
            }
            break;

        case WM_HSCROLL:
            hsb = GET_WM_HSCROLL_HWND(wParam, lParam);

            i = GetScrollPos(hsb,SB_CTL);

            switch (GET_WM_HSCROLL_CODE(wParam, lParam))
            {
                case SB_LINEDOWN:      i += 1; break;
                case SB_LINEUP:        i -= 1; break;
                case SB_PAGEDOWN:      i += 10; break;
                case SB_PAGEUP:        i -= 10; break;

                case SB_THUMBTRACK:
                case SB_THUMBPOSITION:
                    i = (int)GET_WM_HSCROLL_POS(wParam, lParam);
                    break;

                default:
                    return TRUE;
            }

            i = max(SCROLL_MIN,min(SCROLL_MAX,i));
            SetScrollPos(hsb,SB_CTL,i,TRUE);
            wsprintf(ach, TEXT("%d%c%02d"), i/100, chDecimal, i%100);
            SetDlgItemText(hdlg,ID_TEXT,ach);
            return TRUE;

        case WM_INITDIALOG:
            pinst = (INSTINFO *)lParam;

            ach[0] = chDecimal;
            ach[1] = 0;
            GetProfileString(TEXT("intl"), TEXT("sDecimal"), ach, ach,
                             sizeof(ach) / sizeof(ach[0]));
            chDecimal = ach[0];

            hsb = GetDlgItem(hdlg,ID_SCROLL);
            i = pinst->CurrentState.wTemporalRatio;

            SetScrollRange(hsb,SB_CTL,SCROLL_MIN, SCROLL_MAX, TRUE);
            SetScrollPos(hsb,SB_CTL,i,TRUE);
            wsprintf(ach, TEXT("%d%c%02d"), i/100, chDecimal, i%100);
            SetDlgItemText(hdlg,ID_TEXT,ach);
            return TRUE;
    }
    return FALSE;
}

/*****************************************************************************
 ****************************************************************************/

#ifdef DEBUG

#define _WINDLL
#include <stdarg.h>
#ifdef _WIN32
#define GetProfileIntA  mmGetProfileIntA
#endif

void FAR CDECL dprintf(LPSTR szFormat, ...)
{
    char ach[128];
    static BOOL fDebug = -1;

    va_list va;
    va_start(va, szFormat);

    if (fDebug == -1)
        fDebug = GetProfileIntA("Debug", "MSVIDC", FALSE);

    if (!fDebug)
        return;

#ifdef _WIN32
    wsprintfA(ach, "MSVIDC32: (tid %x) ", GetCurrentThreadId());
    wvsprintfA(ach+strlen(ach),szFormat,va);
#else
    lstrcpyA(ach, "MSVIDC: ");
    wvsprintfA(ach+8,szFormat,va);
#endif
    lstrcatA(ach, "\r\n");

    OutputDebugStringA(ach);
    va_end(va);
}

#endif
