/*
 * Demonstracni priklad, ktery nacte a posleze vytiskne vsechny znacky,
 * ktere se nachazi v souboru typu JFIF/JPEG.
 *
 * Autor: Pavel Tisnovsky
 *
 * Pouziti:
 * jpeg_markers image.jpg
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>



// struktura pro uchovani zakladnich informaci o znackach
typedef struct Marker {
    unsigned char value;
    char * marker;
    char * text;
} Marker;

// pole znamych znacek
Marker markers[]={
    {0xd8, "SOI",   "Start Of Image"},
    {0xd9, "EOI",   "End Of Image"},
    {0xd0, "RST0",  "Restart Marker 0"},
    {0xd1, "RST1",  "Restart Marker 1"},
    {0xd2, "RST2",  "Restart Marker 2"},
    {0xd3, "RST3",  "Restart Marker 3"},
    {0xd4, "RST4",  "Restart Marker 4"},
    {0xd5, "RST5",  "Restart Marker 5"},
    {0xd6, "RST6",  "Restart Marker 6"},
    {0xd7, "RST7",  "Restart Marker 7"},
    {0xda, "SOS",   "Start Of Scan"},
    {0xe0, "APP0",  "Application Marker 0"},
    {0xe1, "APP1",  "Application Marker 1 (EXIF)"},
    {0xe2, "APP2",  "Application Marker 2 (EXIF)"},
    {0xc4, "DHT",   "Define Huffman Table"},
    {0xcc, "DAC",   "Define Arithmetic Table"},
    {0xdb, "DQT",   "Define Quantization Table"},
    {0xdc, "DNL",   "Define Number Of Lines"},
    {0xdd, "DRI",   "Define Restart Interval"},
    {0xfe, "COM",   "Comment"},
    {0x01, "TEM",   "usually causes a decoding error, may be ignored"},
    {0xc0, "SOF0",  "Start Of Frame (baseline JPEG)"},
    {0xc1, "SOF1",  "Start Of Frame (baseline JPEG)"},
    {0xc2, "SOF2",  "usually unsupported"},
    {0xc3, "SOF3",  "usually unsupported"},
    {0xc5, "SOF5",  "usually unsupported"},
    {0xc6, "SOF6",  "usually unsupported"},
    {0xc7, "SOF7",  "usually unsupported"},
    {0xc9, "SOF9",  "for arithmetic coding, usually unsupported"},
    {0xca, "SOF10", "usually unsupported"},
    {0xcb, "SOF11", "usually unsupported"},
    {0xcd, "SOF13", "usually unsupported"},
    {0xce, "SOF14", "usually unsupported"},
    {0xce, "SOF14", "usually unsupported"},
    {0xcf, "SOF15", "usually unsupported"},
    {0xc8, "JPG",   "undefined/reserved (causes decoding error)"},
    {0xde, "DHP",   "ignore (skip)"},
    {0xdf, "EXP",   "ignore (skip)"},
    {0xef, "APP15", "ignore"},
    {0xf0, "JPG0",  "ignore (skip)"},
    {0xfd, "JPG13", "ignore (skip)"},
    {0x00, "***",   "neznama znacka"},      // zarazka (musi byt na konci)
};


/*
 * Tisk hodnoty znacky
 */
void printMarker(unsigned char byte1, unsigned char byte2, unsigned long int offset)
{
    int i=0;
    // pokusit se najit znacku v poli markers (nebo alespon zarazku)
    while (markers[i].value!=0 && markers[i].value!=byte2)
        i++;
    printf("%10lu\t0x%08lx\t%02x%02x\t%s\t%s\n", offset, offset, byte1, byte2, markers[i].marker, markers[i].text);
}



/*
 * Funkce pro ziskani zakladnich udaju o souboru typu JFIF/JPEG
 */
void printJpegMarkers(FILE *fileJpeg) {
    int i;
    unsigned char byte1=0, byte2=0;
    printf("Offset dec\tOffset hex\tZnacka\tJmeno\tText\n");
    // projit celym souborem
    while ((i=fgetc(fileJpeg))!=EOF) {
        // pokud byl predchozi byte nastaveny na 0xff
        if (byte1==0xff) {
            byte2=i;
            // a druhy byte je v rozsahu 0x01-0xfe
            if (byte2>=0x01 && byte2<=0xfe) {
                // jedna se o znacku
                printMarker(byte1, byte2, ftell(fileJpeg)-2);
            }
            // zabranit chybne detekci znacky
            byte1=0;
        }
        // test, zda je nacteny byt roven zacatku znacky
        if (i==0xff) {
            byte1=0xff;
        }
    }
};



/*
 * Hlavni funkce konzolove aplikace
 */
int main(int argc, char *argv[])
{
    FILE *fileJpeg;

    // kontrola, jestli je z prikazoveho radku zadano jmeno souboru
    if (argc<=1) {
        printf("Pouziti jpeg_markers image.jpg\n");
        return 1;
    }

    // pokus o otevreni souboru pro cteni
    fileJpeg=fopen(argv[1], "rb");
    if (!fileJpeg) {
        printf("Chyba pri otevirani souboru %s pro cteni!\n", argv[1]);
        return 1;
    }

    // ziskani informaci o znackach ulozenych v souborech JFIF/JPEG
    printJpegMarkers(fileJpeg);

    if (fclose(fileJpeg)==EOF) {
        printf("Chyba pri zavirani souboru %s!\n", argv[1]);
        return 1;
    }
    return 0;
}



/*
 * finito
 */