/*
 * Very simple program to list the names of the PNG/JNG/MNG chunks.
 * This program does NOT use libpng or libmng.
 * Author: Cosmin Truta, Jan 2001
 * Sorry, ASCII comparisons are used, and the program is not too commented...
 */


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


/** The necessary types: bool, byte, uint32. **/
typedef int           bool;
typedef unsigned char byte;
typedef unsigned long uint32;


/** The standard chunks **/
char *std_chunks[] =
{
    /* PNG standard chunks */
    "IHDR", "PLTE", "IDAT", "IEND",
    "cHRM", "gAMA", "iCCP", "sBIT", "sRGB", "bKGD", "hIST", "tRNS",
    "pHYs", "sPLT", "tIME", "iTXt", "tEXt", "zTXt",

    /* JNG standard chunks */
    "JHDR", "JDAT", "JDAA", "JSEP",

    /* MNG standard chunks */
    "MHDR", "MEND", "TERM", "SAVE", "SEEK", "BASI", "LOOP", "ENDL",
    "DEFI", "CLON", "PAST", "MAGN", "DISC",
    "BACK", "FRAM", "MOVE", "CLIP", "SHOW",
    "DHDR", "PROM", "DROP", "DBYK", "ORDR", "IPNG", "IJNG", "PPLT",
    "eXPI", "fPRI", "nEED", "pHYg",

    /* the end of the list */
    ""
};


/** Opens a PNG, MNG or JNG file. Returns the file pointer. **/
FILE *mng_open(const char *filename, char **errmsg)
{
    static char png_signature[] =
        {'\211', 'P', 'N', 'G', '\r', '\n', '\32', '\n'};
    static char mng_signature[] =
        {'\212', 'M', 'N', 'G', '\r', '\n', '\32', '\n'};
    static char jng_signature[] =
        {'\213', 'J', 'N', 'G', '\r', '\n', '\32', '\n'};

    static char sig_buf[sizeof(mng_signature)];
    FILE *fp;
    int error;

    if ((fp = fopen(filename, "rb")) == NULL)
    {
        *errmsg = "Error opening";
        return NULL;
    }

    error = 0;
    *errmsg = "Success opening";

    if (fread(sig_buf, sizeof(sig_buf), 1, fp) != 1)
    {
        error = 1;
        *errmsg = "Error reading";
    }

    if (!error)
    {
        if (memcmp(sig_buf, png_signature, sizeof(sig_buf)) != 0 &&
            memcmp(sig_buf, mng_signature, sizeof(sig_buf)) != 0 &&
            memcmp(sig_buf, jng_signature, sizeof(sig_buf)) != 0)
        {
            error = 1;
            *errmsg = "Not a PNG/MNG/JNG";
        }
    }

    if (error)
    {
        fclose(fp);
        return NULL;
    }
    else
        return fp;
}


/** Reads an uint32. **/
bool mng_read_uint32(uint32 *result, FILE *fp)
{
    byte buffer[4];

    if (fread(buffer, 4, 1, fp) != 1)
        return 0;
    *result = ((uint32)buffer[0] << 24) | ((uint32)buffer[1] << 16) |
        ((uint32)buffer[2] << 8) | ((uint32)buffer[3]);

    return 1;
}


/** Returns TRUE if the chunk is standard. **/
bool mng_is_standard_chunk(char *signature)
{
    char **p;

    for (p = std_chunks; **p; p++)
    {
        if (memcmp(*p, signature, 4) == 0)
            return 1;
    }

    return 0;
}


/** List the chunks found in the file. Simplistic. **/
bool mng_list_chunks(FILE *fp, bool concat, bool std)
{
    uint32 chunk_length;
    char   chunk_signature[5];
    uint32 chunk_crc32;
    int i;

    chunk_signature[4] = '\0';
    for ( ;; )
    {
        if (!mng_read_uint32(&chunk_length, fp))
            break;
        if (fread(chunk_signature, 4, 1, fp) != 1)
            return 0;
        if (fseek(fp, (long)chunk_length, SEEK_CUR) != 0)
            return 0;
        if (!mng_read_uint32(&chunk_crc32, fp))
            return 0;
        if (!std || mng_is_standard_chunk(chunk_signature))
            printf((concat ? "%s" : "%s\n"), chunk_signature);
    }

    return (memcmp(chunk_signature, "IEND", 4) == 0 ||
            memcmp(chunk_signature, "MEND", 4) == 0) ? 1 : 0;
}


/** main **/
int main(int argc, char *argv[])
{
    FILE *fp;
    char *errmsg;
    bool concat_chunks = 0;
    bool std_chunks = 0;
    bool show_help = 0;

    while (argc > 1 && argv[1][0] == '-')
    {
        switch (argv[1][1])
        {
            case 'c':
                concat_chunks = 1;
                break;
            case 's':
                std_chunks = 1;
                break;
            case 'h':
            case '?':
                show_help = 1;
                break;
        }
        --argc;
        ++argv;
    }

    if (argc <= 1)
        show_help = 1;

    if (show_help)
        fprintf(stderr,
            "Usage: mnglist [-c] [-s] <file.?ng>\n"
            "  -c  Concatenate the chunk names\n"
            "  -s  List the standard chunks only\n"
            "  -h  Show this help\n");

    if (argc <= 1)
        return 1;

    if ((fp = mng_open(argv[1], &errmsg)) == NULL)
    {
        fprintf(stderr, "%s %s\n", errmsg, argv[1]);
        return 2;
    }

    if (!mng_list_chunks(fp, concat_chunks, std_chunks))
        fprintf(stderr, "Error while listing the chunks.\n");

    fclose(fp);

    return 0;
}