/*
//
// jpeg.c
//
// JPEG FIF (File Interchange Format) support functions
//
// Copyright (c) 1995-96 Jim Nelson.  Permission to distribute
// granted by the author.  No warranties are made on the fitness of this
// source code.
//
*/

#include "htp.h"

/*
// a great deal of this code was ripped off wholesale from the Independent
// JPEG Group's sample source code, obtainable from any SimTel mirror under
// msdos/graphics/jpegsrc6.zip
*/

/*
// JPEG markers
*/
#define M_SOF0  0xC0        /* Start Of Frame N */
#define M_SOF1  0xC1		/* N indicates which compression process */
#define M_SOF2  0xC2		/* Only SOF0-SOF2 are now in common use */
#define M_SOF3  0xC3
#define M_SOF5  0xC5		/* NB: codes C4 and CC are NOT SOF markers */
#define M_SOF6  0xC6
#define M_SOF7  0xC7
#define M_SOF9  0xC9
#define M_SOF10 0xCA
#define M_SOF11 0xCB
#define M_SOF13 0xCD
#define M_SOF14 0xCE
#define M_SOF15 0xCF
#define M_SOI   0xD8		/* Start Of Image (beginning of datastream) */
#define M_EOI   0xD9		/* End Of Image (end of datastream) */
#define M_SOS   0xDA		/* Start Of Scan (begins compressed data) */
#define M_APP0  0xE0
#define M_COM   0xFE		/* COMment */

BOOL JpegReadByte(FILE *file, BYTE *b)
{
    int i;

    if((i = fgetc(file)) == EOF)
        return FALSE;

    *b = (BYTE) i;

    return TRUE;
}

BOOL JpegFirstMarker(FILE *file)
{
    BYTE flag;
    BYTE marker;

    /* move to the beginning of the file */
    if(fseek(file, 0, SEEK_SET) != 0)
    {
        HtpMsg(MSG_WARNING, NULL, "unable to seek to start of JFIF file");
        return FALSE;
    }

    /* look for the start of image marker */
    if(JpegReadByte(file, &flag) == FALSE)
    {
        return FALSE;
    }

    if(JpegReadByte(file, &marker) == FALSE)
    {
        return FALSE;
    }

    /* start of image? */
    if((flag != 0xFF) || (marker != M_SOI))
    {
        return FALSE;
    }

    return TRUE;
}

BOOL JpegNextMarker(FILE *file, BYTE *marker, WORD *size)
{
    BYTE flag;
    BYTE buff[2];
    int bytesread = 0;

    /* move file pointer to next 0xFF flag */
    do {

	if (JpegReadByte(file, &flag) == FALSE)
	    return FALSE;
	bytesread++;
    } while (flag != 0xFF);

    /* extra 0xFF flags are legal as padding, so move past them */
    while (flag == 0xFF) {
	if (JpegReadByte(file, &flag) == FALSE)
	    return FALSE;
	bytesread++;
    }
    *marker = flag;

    if (bytesread > 2) {
	HtpMsg(MSG_WARNING, NULL, 
	       "Skipped %d bytes of garbage in JFIF file", bytesread - 2);
    }

    if(fread(buff, 1, 2, file) != 2)
    {
	return FALSE;
    }

    *size = MAKE_WORD(buff[0], buff[1]);

    /* exit condition really depends if a good marker was found */
    return TRUE;
}

BOOL JpegFormatFound(FILE *file)
{
    BYTE marker;
    WORD size;
    char signature[8];

    if(JpegFirstMarker(file) == FALSE)
    {
        return FALSE;
    }

    while(JpegNextMarker(file, &marker, &size) == TRUE)
    {
        /* should see an APP0 marker */
        if(marker == M_APP0)
        {
            /* file format is now pointing to JFIF header */
            /* look for the signature */
            
            if(fread(signature, 1, 5, file) != 5)
            {
                HtpMsg(MSG_WARNING, NULL, "unable to read JFIF signature from file");
                return FALSE;
            }

            /* it all comes down to the signature being present */
            return (strcmp(signature, "JFIF") == 0) ? TRUE : FALSE;
        } else if(marker == 0xe1)
        {
            /* file format is now pointing to JFIF header */
            /* look for the signature */
            
            if(fread(signature, 1, 5, file) != 5)
            {
                HtpMsg(MSG_WARNING, NULL, "unable to read JFIF signature from file");
                return FALSE;
            }

            /* it all comes down to the signature being present */
            return (strcmp(signature, "Exif") == 0) ? TRUE : FALSE;
        } else {
            if (fseek (file, size-2, SEEK_CUR) != 0)
            {
                HtpMsg(MSG_WARNING, NULL, "unable to seek past JFIF block");
                return FALSE;
            }
        }
    }
    
    return FALSE;
}

BOOL JpegReadDimensions(FILE *file, DWORD *height, DWORD *width)
{
    BYTE marker, buff[5];
    WORD size;

    /* make sure we can find the first marker */
    if(JpegFirstMarker(file) == FALSE)
    {
        return FALSE;
    }

    /* read file looking for SOF (start of frame) ... when it or */
    /* or SOS (start of scan, the compressed data) is reached, stop */
    while(JpegNextMarker(file, &marker, &size) == TRUE)
    {
        /* if SOS, stop */
        if(marker == M_SOS)
        {
            HtpMsg(MSG_WARNING, NULL, "JFIF SOS marker found before SOF marker");
            break;
        }

        /* if not SOF, continue */
	switch (marker) {
	case M_SOF0:                /* Baseline */
	case M_SOF1:                /* Extended sequential, Huffman */
	case M_SOF2:                /* Progressive, Huffman */
	case M_SOF3:                /* Lossless, Huffman */
	case M_SOF5:                /* Differential sequential, Huffman */
	case M_SOF6:                /* Differential progressive, Huffman */
	case M_SOF7:                /* Differential lossless, Huffman */
	case M_SOF9:                /* Extended sequential, arithmetic */
	case M_SOF10:               /* Progressive, arithmetic */
	case M_SOF11:               /* Lossless, arithmetic */
	case M_SOF13:               /* Differential sequential, arithmetic */
	case M_SOF14:               /* Differential progressive, arithmetic */
	case M_SOF15:               /* Differential lossless, arithmetic */

	    /* start of frame found ... process the dimension information */
	    /* read the height and width and get outta here */
	    if(fread(buff, 1, 5, file) != 5)
	    {
		HtpMsg(MSG_WARNING, NULL,
		       "unable to read dimensions from JFIF file");
		return FALSE;
	    }

	    /* words are kept in MSB format */
	    *height = MAKE_WORD(buff[1], buff[2]);
	    *width  = MAKE_WORD(buff[3], buff[4]);
	    return TRUE;

	default:
	    if (fseek (file, size-2, SEEK_CUR) != 0)
	    {
		HtpMsg(MSG_WARNING, NULL, "unable to seek past JFIF block");
		return FALSE;
	    }
        }
    }

    HtpMsg(MSG_WARNING, NULL, ("JFIF SOF marker not found"));

    /* didn't find the SOF or found the SOS */
    return FALSE;
}

