예제 #1
0
// load in an image from the filename, and store it in IPAGE. 
// if there were any errors, return a NULL
IPAGE *
iff_file_load_page(
	char * filename
)
{
    int x,y;
    int plane;
    int handle_x = 0;
    int handle_y = 1;
    BLOCKHEADER bh;
    IFF_BMHD bmhd;
    FILE * fp;
    COLEL * palette = NULL;
    IPAGE * iff_page = NULL;

    fp = fopen(filename, "r");

    // skip the container header...
    (void)endian_little_read_32(fp);
    (void)endian_little_read_32(fp);
    (void)endian_little_read_32(fp);

    bmhd.width=0;
    bmhd.height=0;
    // parse the file...
    iff_read_header(fp, &bh);
    while (bh.size)
    {
	/*
	printf( "%c%c%c%c\n",
		(bh.name>>24)&0xff,
		(bh.name>>16)&0xff,
		(bh.name>>8)&0xff,
		(bh.name)&0xff );
	*/
	switch (bh.name)
	{
	case(IFF_BLOCK_BMHD):
	    //printf("Bitmap header\n");

	    bmhd.width   = endian_little_read_16(fp) & 0x00ffff;
	    bmhd.height  = endian_little_read_16(fp) & 0x00ffff;
	    bmhd.x       = endian_little_read_16(fp) & 0x00ffff;
	    bmhd.y       = endian_little_read_16(fp) & 0x00ffff;
	    bmhd.planes  = endian_little_read_8(fp)  & 0x0000ff;
	    bmhd.masking = endian_little_read_8(fp)  & 0x0000ff;
	    bmhd.comp    = endian_little_read_8(fp)  & 0x0000ff;
	    (void)endian_little_read_8(fp);  // pad.  not used.  -- store 0
	    bmhd.xparent = endian_little_read_16(fp) & 0x00ffff;
	    bmhd.xaspect = endian_little_read_8(fp)  & 0x0000ff;
	    bmhd.yaspect = endian_little_read_8(fp)  & 0x0000ff;
	    bmhd.pwidth  = endian_little_read_16(fp) & 0x00ffff;
	    bmhd.pheight = endian_little_read_16(fp) & 0x00ffff;

	    /*
	    printf("image is %d by %d  (%d %d)  (%d %d)\n",
			bmhd.width, bmhd.height,
			bmhd.pwidth, bmhd.pheight,
			bmhd.xaspect, bmhd.yaspect);
	    printf("%d planes %d %s\n",
			bmhd.planes, bmhd.masking, 
			(bmhd.comp==1)?"RLE":"?");
	    */
	    break;

	case(IFF_BLOCK_GRAB):
	    handle_x = endian_little_read_16(fp);
	    handle_y = endian_little_read_16(fp);
	    break;

	case(IFF_BLOCK_CMAP):
	    palette = (COLEL *) malloc(bh.size*sizeof(COLEL));
	    for (x=0 ; x<bh.size/3 ; x++)
	    {
		palette[x].r = endian_little_read_8(fp)&0x00ff;
		palette[x].g = endian_little_read_8(fp)&0x00ff;
		palette[x].b = endian_little_read_8(fp)&0x00ff;
	    }
	    break;

	case(IFF_BLOCK_CRNG):
	    // Electronic Arts Deluxe Paint color range
	    (void)endian_little_read_16(fp); // pad -- store 0
	    (void)endian_little_read_16(fp); // rate
	    (void)endian_little_read_16(fp); // active -- 1 means cycle them
	    (void)endian_little_read_8(fp);  // start range
	    (void)endian_little_read_8(fp);  // end range
	    break;

	case(IFF_BLOCK_CCRT):
	    // Commodore Graphicraft color range
	    (void)endian_little_read_16(fp); // direction  0=stop 1=forw -1=back
	    (void)endian_little_read_8(fp);  // start range
	    (void)endian_little_read_8(fp);  // end range
	    (void)endian_little_read_32(fp); // seconds
	    (void)endian_little_read_32(fp); // microseconds
	    (void)endian_little_read_16(fp); // pad -- store 0
	    break;

	case(IFF_BLOCK_BODY):
	    //printf("Image Body %d (%d colors)\n", bh.size, 1<<bmhd.planes);

	    iff_page = Page_Create(bmhd.width, bmhd.height, 1<<bmhd.planes);

	    iff_page->handle_x = handle_x;
	    iff_page->handle_y = handle_y;

	    // at this point, we should have a palette, but just in case,
	    // we'll put an if around this anyway...
	    if (palette)
	    {
		free(iff_page->palette);
		iff_page->palette = palette;
	    }

	    // now load in the bitmap data...

	    for (y=0 ; y < iff_page->h ; y++)
	    {
		// decode image data

		for ( plane = 0 ; plane < bmhd.planes ; plane++)
		{
		    if (1) //bmhd.comp==1)
		    {
			iff_decode_plane(fp, iff_page->pimage, y, 
						iff_page->w, plane);
		    } else {
			iff_load_in_plane(fp, iff_page->pimage, y, 
						iff_page->w, plane);
		    }
		}

		// and the mask...
		if (bmhd.masking == 1)
		{
		    iff_decode_plane(fp, iff_page->alpha, y, iff_page->w, 1);
		}
	    }

	    return iff_page;
	    break;

	default:
	    iff_skip_block(fp, &bh);
	}


	iff_read_header(fp, &bh);
    }

    fclose(fp);
    return iff_page;
}
예제 #2
0
파일: iff2html.c 프로젝트: pepster98/Niotso
int main(int argc, char *argv[]){
    unsigned c;
    int slash;
    FILE * hFile;
    int overwrite = 0;
    char *InFile, *OutFile = NULL, *FileName, *OutDir = NULL;
    size_t FileSize;
    struct MD5Context md5c;
    unsigned char digest[16];
    uint8_t * IFFData;
    IFFFile * IFFFileInfo;
    IFFChunk * ChunkData;

    if(argc == 1 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")){
        printf("Usage: iff2html [-f] infile (outfile)\n"
        "Produce an HTML webpage describing an EA IFF file.\n"
        "Use -f to force overwriting without confirmation.\n"
        "If outfile is unspecified, file.iff will output to file.html.\n"
        "\n"
        "Report bugs to <*****@*****.**>.\n"
        "iff2html is maintained by the Niotso project.\n"
        "Home page: <http://www.niotso.org/>\n");
        return 0;
    }

    if(argc >= 4 && !strcmp(argv[1], "-f")){
        overwrite++;
        InFile = argv[2];
        OutFile = argv[3];
    }else if(argc == 3){
        if(!strcmp(argv[1], "-f")){
            overwrite++;
            InFile = argv[2];
        }else{
            InFile = argv[1];
            OutFile = argv[2];
        }
    }else InFile = argv[1];
    if(OutFile == NULL){
        unsigned length = strlen(InFile);
        OutFile = malloc(max(length+2, 6));
        strcpy(OutFile, InFile);
        strcpy(max(OutFile+length-4, OutFile), ".html");
    }

    for(c=0, slash=-1; OutFile[c]; c++)
        if(OutFile[c] == '/' || OutFile[c] == '\\') slash = c;
    if(slash >= 0){
        OutDir = malloc(slash+2);
        memcpy(OutDir, OutFile, slash+1);
        OutDir[slash+1] = 0x00;
    }else OutDir = "";

    for(c=0, slash=-1; InFile[c]; c++)
        if(InFile[c] == '/' || InFile[c] == '\\') slash = c;
    FileName = InFile + slash + 1;

    /****
    ** Open the file and read in entire contents to memory
    */

    hFile = fopen(InFile, "rb");
    if(hFile == NULL){
        printf("%sThe specified input file does not exist or could not be opened for reading.", "iff2html: error: ");
        return -1;
    }
    fseek(hFile, 0, SEEK_END);
    FileSize = ftell(hFile);
    if(FileSize < 64){
        printf("%sNot a valid IFF file.", "iff2html: error: ");
        return -1;
    }
    fseek(hFile, 0, SEEK_SET);

    IFFData = malloc(FileSize);
    if(IFFData == NULL){
        printf("%sMemory for this file could not be allocated.", "iff2html: error: ");
        return -1;
    }
    if(!fread(IFFData, FileSize, 1, hFile)){
        printf("%sThe input file could not be read.", "iff2html: error: ");
        return -1;
    }
    fclose(hFile);

    /****
    ** Load header information
    */

    IFFFileInfo = iff_create();
    if(IFFFileInfo == NULL){
        printf("%sMemory for this file could not be allocated.", "iff2html: error: ");
        return -1;
    }
    if(!iff_read_header(IFFFileInfo, IFFData, FileSize)){
        printf("%sNot a valid IFF file.", "iff2html: error: ");
        return -1;
    }

    /****
    ** Load entry information
    */

    if(!iff_enumerate_chunks(IFFFileInfo, IFFData+64, FileSize-64)){
        printf("%sChunk data is corrupt.", "iff2html: error: ");
        return -1;
    }

    /* Calculate the MD5, and then we can free the IFF data because we're done with it */
    MD5Init(&md5c);
    MD5Update(&md5c, IFFData, FileSize);
    MD5Final(digest, &md5c);
    free(IFFData);

    for(c = 0, ChunkData = IFFFileInfo->Chunks; c < IFFFileInfo->ChunkCount; c++, ChunkData++)
        iff_parse_chunk(ChunkData, ChunkData->Data);

    /****
    ** Open the output file and write the header
    */
    if(!overwrite){
        hFile = fopen(OutFile, "rb");
        if(hFile != NULL){
            /* File exists */
            char c;
            fclose(hFile);
            printf("File \"%s\" exists.\nContinue anyway? (y/n) ", OutFile);
            c = getchar();
            if(c != 'y' && c != 'Y'){
                printf("\nAborted.");
                return -1;
            }
        }
    }
    hFile = fopen(OutFile, "wb");
    if(hFile == NULL){
        printf("%sThe output file could not be opened for writing.", "iff2html: error: ");
        return -1;
    }

    /****
    ** We're splitting fprintf by line to guarantee compatibility;
    ** even C99 compilers are only required to support 4096 byte strings in printf()-related functions
    */
    fprintf(hFile,
        "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n");
    fprintf(hFile, "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\" dir=\"ltr\">\n");
    fprintf(hFile, "<head>\n");
    fprintf(hFile, "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\" />\n");
    fprintf(hFile, "<meta http-equiv=\"Content-Style-Type\" content=\"text/css; charset=iso-8859-1\" />\n");
    fprintf(hFile, "<meta http-equiv=\"Content-Language\" content=\"en\" />\n");
    fprintf(hFile, "<meta name=\"description\" content=\"%s (iff2html)\" />\n", FileName);
    fprintf(hFile, "<meta name=\"generator\" content=\"iff2html\" />\n");
    fprintf(hFile, "<title>%s (iff2html)</title>\n", FileName);
    fprintf(hFile, "<style type=\"text/css\" media=\"all\">\n");
    fprintf(hFile, "html, body {\n");
    fprintf(hFile, "    background: #fff;\n");
    fprintf(hFile, "    color: #000;\n");
    fprintf(hFile, "    font-family: sans-serif;\n");
    fprintf(hFile, "}\n");
    fprintf(hFile, "\n");
    fprintf(hFile, "a:link, a:visited, a:hover, a:active { color: #00f; }\n");
    fprintf(hFile, "a:link, a:visited { text-decoration: none; }\n");
    fprintf(hFile, "a:hover, a:active { text-decoration: underline; }\n");
    fprintf(hFile, "\n");
    fprintf(hFile, "#attributes {\n");
    fprintf(hFile, "    border-left: 2px solid #888; padding-left: 4px; margin-bottom: 1em;\n");
    fprintf(hFile, "}\n");
    fprintf(hFile, "\n");
    fprintf(hFile, "#toc {\n");
    fprintf(hFile, "    display: table-cell;\n");
    fprintf(hFile, "    margin-top: 1em;\n");
    fprintf(hFile, "    background: #eee; border: 1px solid #bbb;\n");
    fprintf(hFile, "    padding: .25em;\n");
    fprintf(hFile, "}\n");
    fprintf(hFile, "#toc div {\n");
    fprintf(hFile, "    border-bottom: 1px solid #aaa;\n");
    fprintf(hFile, "}\n");
    fprintf(hFile, "#toc ul {\n");
    fprintf(hFile, "    list-style-type: none;\n");
    fprintf(hFile, "    padding: 0;\n");
    fprintf(hFile, "}\n");
    fprintf(hFile, "ul ul {\n");
    fprintf(hFile, "    padding: 2em;\n");
    fprintf(hFile, "}\n");
    fprintf(hFile, "\n");
    fprintf(hFile, "h2 {\n");
    fprintf(hFile, "    border-bottom: 1px solid #888;\n");
    fprintf(hFile, "    margin: 2em 0 0.25em 0;\n");
    fprintf(hFile, "}\n");
    fprintf(hFile, "h2 a {\n");
    fprintf(hFile, "    font-size: 9pt;\n");
    fprintf(hFile, "}\n");
    fprintf(hFile, "\n");
    fprintf(hFile, "table {\n");
    fprintf(hFile, "    border: 1px #aaa solid;\n");
    fprintf(hFile, "    border-collapse: collapse;\n");
    fprintf(hFile, "}\n");
    fprintf(hFile, "th, td {\n");
    fprintf(hFile, "    border: 1px #aaa solid;\n");
    fprintf(hFile, "    padding: 0.2em;\n");
    fprintf(hFile, "    white-space: pre-wrap;\n");
    fprintf(hFile, "}\n");
    fprintf(hFile, "\n");
    fprintf(hFile, ".center {\n");
    fprintf(hFile, "    margin: auto auto;\n");
    fprintf(hFile, "}\n");
    fprintf(hFile, ".centerall * {\n");
    fprintf(hFile, "    text-align: center;\n");
    fprintf(hFile, "    vertical-align: middle;\n");
    fprintf(hFile, "}\n");
    fprintf(hFile, "\n");
    fprintf(hFile, ".palette td, .palette th {\n");
    fprintf(hFile, "    border: none;\n");
    fprintf(hFile, "    width: 16px;\n");
    fprintf(hFile, "    height: 16px;\n");
    fprintf(hFile, "    font-size: 12px;\n");
    fprintf(hFile, "    line-height: 16px;\n");
    fprintf(hFile, "}\n");
    fprintf(hFile, ".palette td[title] {\n");
    fprintf(hFile, "    border: 1px solid #000;\n");
    fprintf(hFile, "    cursor: help;\n");
    fprintf(hFile, "}\n");
    fprintf(hFile, "\n");
    fprintf(hFile, "#footer {\n");
    fprintf(hFile, "    margin-top: 2em;\n");
    fprintf(hFile, "    padding-bottom: 0.5em;\n");
    fprintf(hFile, "    text-align: center;\n");
    fprintf(hFile, "}\n");
    fprintf(hFile, "</style>\n");
    fprintf(hFile, "</head>\n");
    fprintf(hFile, "<body>\n");
    fprintf(hFile, "<h1>%s</h1>\n", FileName);
    fprintf(hFile, "<div id=\"attributes\">\n");
    fprintf(hFile, "<div>");
    for(c=0; c<16; c++)
        fprintf(hFile, "%.2x", digest[c]);
    fprintf(hFile, " (md5), ");
    printsize(hFile, FileSize);
    fprintf(hFile, "</div>\n");
    fprintf(hFile, "<div>Dumped by iff2html.</div></div>\n");
    fprintf(hFile, "\n");
    fprintf(hFile, "<div id=\"toc\"><div><b>Contents</b> &ndash; %u chunks</div>\n", IFFFileInfo->ChunkCount);
    fprintf(hFile, "<ul>\n");
    for(c=1, ChunkData = IFFFileInfo->Chunks; c <= IFFFileInfo->ChunkCount; c++, ChunkData++)
        fprintf(hFile, "<li><a href=\"#chunk%u_%.4x\">%u [%s] (%.4X)%s%s</a></li>\n",
            c, ChunkData->ChunkID, c, ChunkData->Type, ChunkData->ChunkID,
            (ChunkData->Label[0] != 0x00) ? " &ndash; " : "", ChunkData->Label);
    fprintf(hFile, "</ul>\n");
    fprintf(hFile, "</div>\n");
    fprintf(hFile, "\n");

    for(c=0, ChunkData = IFFFileInfo->Chunks; c < IFFFileInfo->ChunkCount; c++, ChunkData++){
        fprintf(hFile, "<h2 id=\"chunk%u_%.4x\">%u [%s] (%.4X)%s%s <a href=\"#chunk%u_%.4x\">(Jump)</a></h2>\n",
            c+1, ChunkData->ChunkID, c+1, ChunkData->Type, ChunkData->ChunkID,
            (ChunkData->Label[0] != 0x00) ? " &ndash; " : "", ChunkData->Label,
            c+1, ChunkData->ChunkID);
        fprintf(hFile, "<div>\n");

        if(ChunkData->FormattedData == NULL){
            int success = 0;
            /* The iff library does not parse BMP_ or FBMP chunks */
            if(!strcmp(ChunkData->Type, "BMP_") || !strcmp(ChunkData->Type, "FBMP")){
                int bmp = !strcmp(ChunkData->Type, "BMP_");
                size_t Width, Height;
                char filename[32];
                sprintf(filename, "%s%s_%u_%.4x.png", OutDir, bmp ? "bmp" : "fbmp", c+1, ChunkData->ChunkID);

                if(WritePNG(filename, ChunkData, 0, NULL, &Width, &Height)){
                    fprintf(hFile, "<table class=\"center centerall\">\n");
                    fprintf(hFile, "<tr><th>Image</th></tr>\n");
                    fprintf(hFile, "<tr><td><img src=\"%s_%u_%.4x.png\" width=\"%u\" height=\"%u\" alt=\"\" /></td></tr>\n",
                        bmp ? "bmp" : "fbmp", c+1, ChunkData->ChunkID, (unsigned) Width, (unsigned) Height);
                    fprintf(hFile, "</table>\n");
                    success++;
                }
            }
            if(!success)
                fprintf(hFile, "The contents of this chunk could not be parsed.\n");
        }else if(!strcmp(ChunkData->Type, "STR#")  ||
            !strcmp(ChunkData->Type, "CTSS") ||
            !strcmp(ChunkData->Type, "FAMs") ||
            !strcmp(ChunkData->Type, "TTAs") ||
            !strcmp(ChunkData->Type, "CST")  ){
            /****
            ** STR# parsing
            */

            IFFString * StringData = ChunkData->FormattedData;
            fprintf(hFile, "<table>\n");
            fprintf(hFile, "<tr><td>Format:</td><td>");
            switch(StringData->Format){
                case 0:  fprintf(hFile, "<tt>00 00</tt> (0)"); break;
                case -1: fprintf(hFile, "<tt>FF FF</tt> (&minus;1)"); break;
                case -2: fprintf(hFile, "<tt>FE FF</tt> (&minus;2)"); break;
                case -3: fprintf(hFile, "<tt>FD FF</tt> (&minus;3)"); break;
                case -4: fprintf(hFile, "<tt>FC FF</tt> (&minus;4)"); break;
                default: fprintf(hFile, "Unrecognized"); break;
            }
            fprintf(hFile, "</td></tr>\n");
            fprintf(hFile, "</table>\n");
            if(StringData->Format >= -4 && StringData->Format <= 0){
                unsigned LanguageSet;
                const char * LanguageStrings[] = {
                    "English (US)",
                    "English (International)",
                    "French",
                    "German",
                    "Italian",
                    "Spanish",
                    "Dutch",
                    "Danish",
                    "Swedish",
                    "Norwegian",
                    "Finnish",
                    "Hebrew",
                    "Russian",
                    "Portuguese",
                    "Japanese",
                    "Polish",
                    "Simplified Chinese",
                    "Traditional Chinese",
                    "Thai",
                    "Korean"
                };
                fprintf(hFile, "<br />\n");
                fprintf(hFile, "<table class=\"center\">\n");
                fprintf(hFile, "<tr><th>Language</th><th colspan=\"3\">String pairs</th></tr>\n");

                for(LanguageSet=0; LanguageSet<20; LanguageSet++){
                    IFFStringPair * Pair;
                    unsigned PairIndex;
                    if(StringData->LanguageSets[LanguageSet].PairCount == 0)
                        continue;

                    fprintf(hFile, "<tr><td rowspan=\"%u\">%s</td>\n", StringData->LanguageSets[LanguageSet].PairCount,
                        LanguageStrings[LanguageSet]);
                    for(PairIndex=1, Pair = StringData->LanguageSets[LanguageSet].Pairs;
                        PairIndex <= StringData->LanguageSets[LanguageSet].PairCount; PairIndex++, Pair++){
                        if(PairIndex != 1)
                            fprintf(hFile, "<tr>");
                        fprintf(hFile, "<td>%u</td><td>%s</td><td>%s</td></tr>\n", PairIndex,
                            (Pair->Key)   != NULL ? Pair->Key   : "",
                            (Pair->Value) != NULL ? Pair->Value : "");
                    }
                }

                fprintf(hFile, "</table>\n");
            }
        }else if(!strcmp(ChunkData->Type, "CATS")){
            /****
            ** Regular string pair
            */

            IFFStringPair * Pair = ChunkData->FormattedData;

            fprintf(hFile, "<table class=\"center\">\n");
            fprintf(hFile, "<tr><th>Key</th><th>Value</th></tr>\n");
            fprintf(hFile, "<tr><td>%s</td><td>%s</td></tr>\n",
                (Pair->Key)   != NULL ? Pair->Key   : "",
                (Pair->Value) != NULL ? Pair->Value : "");
            fprintf(hFile, "</table>\n");
        }else if(!strcmp(ChunkData->Type, "FWAV") || !strcmp(ChunkData->Type, "GLOB")){
            /****
            ** Regular string
            */

            fprintf(hFile, "<table class=\"center\">\n");
            fprintf(hFile, "<tr><th>String</th></tr>\n");
            fprintf(hFile, "<tr><td>%s</td></tr>\n", ChunkData->FormattedData ? (char*) ChunkData->FormattedData : "");
            fprintf(hFile, "</table>\n");
        }else if(!strcmp(ChunkData->Type, "BCON")){
            /****
            ** BCON parsing
            */

            IFF_BCON * BCONData = ChunkData->FormattedData;
            fprintf(hFile, "<table>\n");
            fprintf(hFile, "<tr><td>Flags:</td><td><tt>%02X</tt> (%u)</td></tr>\n", BCONData->Flags, BCONData->Flags);
            fprintf(hFile, "</table>\n");
            if(BCONData->ConstantCount > 0){
                unsigned i;

                fprintf(hFile, "<br />\n");
                fprintf(hFile, "<table class=\"center\">\n");
                fprintf(hFile, "<tr><th colspan=\"2\">Constant Value</th></tr>\n");
                for(i=0; i<BCONData->ConstantCount; i++)
                    fprintf(hFile, "<tr><td>%u</td><td>%u</td></tr>\n", i+1, BCONData->Constants[i]);
                fprintf(hFile, "</table>\n");
            }
        }else if(!strcmp(ChunkData->Type, "FCNS")){
            /****
            ** FCNS parsing
            */

            IFFConstantList * List = ChunkData->FormattedData;
            fprintf(hFile, "<table>\n");
            fprintf(hFile, "<tr><td>Version:</td><td>%u</td></tr>\n", List->Version);
            fprintf(hFile, "</table>\n");
            if(List->ConstantCount > 0){
                IFFConstant * Constant;
                unsigned i;

                fprintf(hFile, "<br />\n");
                fprintf(hFile, "<table class=\"center\">\n");
                fprintf(hFile, "<tr><th colspan=\"2\">Name</th><th>Value</th><th>Description</th></tr>\n");
                for(i=0, Constant=List->Constants; i<List->ConstantCount; i++, Constant++)
                    fprintf(hFile, "<tr><td>%u</td><td>%s</td><td>%g</td><td>%s</td></tr>\n",
                        i+1,
                        Constant->Name ? Constant->Name : "",
                        Constant->Value,
                        Constant->Description ? Constant->Description : "");
                fprintf(hFile, "</table>\n");
            }
        }else if(!strcmp(ChunkData->Type, "TMPL")){
            /****
            ** TMPL parsing
            */

            IFFTemplate * Template = ChunkData->FormattedData;
            IFFTemplateField * Field;
            unsigned i;
            fprintf(hFile, "<table class=\"center\">\n");
            fprintf(hFile, "<tr><th colspan=\"2\">Name</th><th>Type</th>\n");
            for(i=0, Field=Template->Fields; i<Template->FieldCount; i++, Field++)
                fprintf(hFile, "<tr><td>%u</td><td>%s</td><td>%s</td></tr>\n",
                    i+1,
                    Field->Name ? Field->Name : "",
                    Field->Type ? Field->Type : "");
            fprintf(hFile, "</table>\n");
        }else if(!strcmp(ChunkData->Type, "TRCN")){
            /****
            ** TRCN parsing
            */

            IFFRangeSet * RangeSet = ChunkData->FormattedData;
            fprintf(hFile, "<table>\n");
            fprintf(hFile, "<tr><td>Version:</td><td>%u</td></tr>\n", RangeSet->Version);
            fprintf(hFile, "</table>\n");
            if(RangeSet->RangeCount > 0){
                unsigned i;
                IFFRangeEntry * Range;

                fprintf(hFile, "<br />\n");
                fprintf(hFile, "<table class=\"center\">\n");
                fprintf(hFile, "<tr><th colspan=\"2\">In use</th><th>Default value</th><th>Name</th>"
                    "<th>Comment</th><th>Range is enforced</th><th>Minimum</th><th>Maximum</th></tr>\n");
                for(i=0, Range=RangeSet->Ranges; i<RangeSet->RangeCount; i++, Range++)
                    fprintf(hFile,
                        "<tr><td>%u</td><td>%s</td><td>%u</td><td>%s</td><td>%s</td><td>%s</td><td>%u</td><td>%u</td></tr>\n",
                        i+1,
                        Range->IsUsed ? "Yes" : "No", Range->DefaultValue,
                        Range->Name ? Range->Name : "",
                        Range->Comment ? Range->Comment : "",
                        Range->Enforced ? "Yes" : "No",
                        Range->RangeMin, Range->RangeMax);
                fprintf(hFile, "</table>\n");
            }
        }else if(!strcmp(ChunkData->Type, "PALT")){
            /****
            ** PALT parsing
            */

            IFFPalette * Palette = ChunkData->FormattedData;
            uint8_t * Data = Palette->Data;
            unsigned i, j;

            fprintf(hFile, "<table class=\"center palette\" border=\"0\">\n");
            fprintf(hFile, "<tr><th></th>");
            for(i=0; i<16; i++) fprintf(hFile, "<th>%X</th>", i);
            fprintf(hFile, "</tr>\n");
            for(i=0; i<16; i++){
                fprintf(hFile, "<tr><th>%X</th>", i);
                for(j=0; j<16; j++){
                    if(i*16 + j < Palette->ColorCount){
                        unsigned red = *(Data++);
                        unsigned green = *(Data++);
                        unsigned blue = *(Data++);

                        fprintf(hFile, "\n<td style=\"background:#%.2x%.2x%.2x\" title=\"%u: #%.2x%.2x%.2x\"></td>",
                            red, green, blue, i*16 + j, red, green, blue);
                    }else
                        fprintf(hFile, "\n<td></td>");
                }
                fprintf(hFile, "</tr>\n");
            }
            fprintf(hFile, "</table>\n");
        }else if(!strcmp(ChunkData->Type, "SPR#") || !strcmp(ChunkData->Type, "SPR2")){
            /****
            ** SPR# and SPR2 parsing
            */

            int spr1 = !strcmp(ChunkData->Type, "SPR#");
            IFFSpriteList * SpriteList = ChunkData->FormattedData;
            IFFChunk * Palette = NULL;
            IFFPalette BlankPalette;
            IFFPalette * PaletteData;
            unsigned i;

            fprintf(hFile, "<table>\n");
            fprintf(hFile, "<tr><td>Version:</td><td>%u</td></tr>\n", SpriteList->Version);
            fprintf(hFile, "<tr><td>Palette ID:</td><td>%.4X</td></tr>\n", SpriteList->PaletteID);
            fprintf(hFile, "</table>\n");

            if(SpriteList->PaletteID < 0xFFFF){
                Palette = iff_find_chunk(IFFFileInfo, "PALT", SpriteList->PaletteID);
                if(!Palette || !Palette->FormattedData) Palette = iff_find_chunk(IFFFileInfo, "PALT", ChunkData->ChunkID);
                if(!Palette || !Palette->FormattedData) Palette = iff_find_chunk(IFFFileInfo, "PALT", -1);
            }
            if(!Palette || !Palette->FormattedData){
                memset(&BlankPalette, 0, sizeof(IFFPalette));
                BlankPalette.Version = 1;
                BlankPalette.ColorCount = 256;
                PaletteData = &BlankPalette;
            }else PaletteData = Palette->FormattedData;

            fprintf(hFile, "<table class=\"center centerall\">\n");
            fprintf(hFile, "<tr><th colspan=\"2\">Sprite</th>");
            if(!spr1) fprintf(hFile, "<th>Z-Buffer</th>");
            fprintf(hFile, "</tr>\n");
            for(i=0; i<SpriteList->SpriteCount; i++){
                IFFSprite * Sprite = &SpriteList->Sprites[i];
                char filename[32];
                sprintf(filename, "%s%s_%u_%.4x_%u.png", OutDir, spr1 ? "spr1" : "spr2", c+1, ChunkData->ChunkID, i+1);

                fprintf(hFile, "<tr><td>%u</td><td", i+1);
                if(Sprite->IndexData && iff_depalette(Sprite, PaletteData)){
                    WritePNG(filename, NULL, 0, Sprite, NULL, NULL);
                    fprintf(hFile, "><img src=\"%s_%u_%.4x_%u.png\" width=\"%u\" height=\"%u\" alt=\"\" />",
                        spr1 ? "spr1" : "spr2", c+1, ChunkData->ChunkID, i+1, Sprite->Width, Sprite->Height);
                    if(!spr1){
                        sprintf(filename, "%sspr2_%u_%.4x_%u_z.png", OutDir, c+1, ChunkData->ChunkID, i+1);
                        if(Sprite->ZBuffer){
                            WritePNG(filename, NULL, 1, Sprite, NULL, NULL);
                            fprintf(hFile, "</td><td><img src=\"spr2_%u_%.4x_%u_z.png\" width=\"%u\" height=\"%u\" alt=\"\" />",
                                c+1, ChunkData->ChunkID, i+1, Sprite->Width, Sprite->Height);
                        }else
                            fprintf(hFile, "None provided");
                    }
                }else
                    fprintf(hFile, Sprite->InvalidDimensions ? "%sBlank sprite" : "%sThis sprite cannot be displayed.",
                        !spr1 ? " colspan=\"2\">" : ">");
                fprintf(hFile, "</td></tr>\n");
            }
            fprintf(hFile, "</table>\n");
        }else if(!strcmp(ChunkData->Type, "DGRP")){
            /****
            ** DGRP parsing
            */

            IFFDrawGroup * Group = ChunkData->FormattedData;
            IFFDrawAngle * Angle;
            IFFSpriteInfo * Sprite;
            unsigned i,j;
            const char * Zooms[] = {"Far", "Middle", "Close"};

            fprintf(hFile, "<table>\n");
            fprintf(hFile, "<tr><td>Version:</td><td>%u</td></tr>\n", Group->Version);
            fprintf(hFile, "</table>\n");
            fprintf(hFile, "<br />\n");

            fprintf(hFile, "<table class=\"center\">\n");
            fprintf(hFile, "<tr><th>Direction</th><th>Zoom</th><th colspan=\"6\">Sprite</th></tr>\n");
            for(i=0, Angle=Group->DrawAngles; i<12; i++, Angle++){
                const char * Direction =
                    (Angle->Direction == IFFDIRECTION_NORTHEAST)  ? "North east" :
                    (Angle->Direction == IFFDIRECTION_SOUTHEAST)  ? "South east" :
                    (Angle->Direction == IFFDIRECTION_NORTHWEST)  ? "North west" :
                                                                    "South west";

                if(Angle->SpriteCount){
                    fprintf(hFile,
                        "<tr><td rowspan=\"%u\">%s</td><td rowspan=\"%u\">%s</td>"
                        "<th>#</th><th>Type</th><th>Chunk ID</th><th>Sprite index</th>"
                        "<th>Flags</th><th>Sprite offset</th><th>Object offset</th></tr>\n",
                        1+Angle->SpriteCount, Direction, 1+Angle->SpriteCount, Zooms[Angle->Zoom-1]);
                    for(j=0, Sprite = Angle->SpriteInfo; j<Angle->SpriteCount; j++, Sprite++)
                        fprintf(hFile, "<tr><td>%u</td><td>%u</td><td>%.4X</td><td>%u</td><td>%u</td>"
                            "<td>(%+d,%+d)</td><td>(%+g,%+g,%+g)</td></tr>",
                            j+1, Sprite->Type, Sprite->ChunkID, Sprite->SpriteIndex, Sprite->Flags,
                            Sprite->SpriteX, Sprite->SpriteY, Sprite->ObjectX, Sprite->ObjectY, Sprite->ObjectZ);

                }else{
                    fprintf(hFile, "<tr><td>%s</td><td>%s</td><td>None specified</td></tr>", Direction, Zooms[Angle->Zoom-1]);
                }
            }
            fprintf(hFile, "</table>\n");
        }else if(!strcmp(ChunkData->Type, "BHAV")){
            /****
            ** BHAV parsing
            */

            IFFBehavior * Behavior = ChunkData->FormattedData;
            IFFInstruction * Instruction;

            fprintf(hFile, "<table>\n");
            fprintf(hFile, "<tr><td>Version:</td><td>%u</td></tr>\n", Behavior->Version);
            fprintf(hFile, "<tr><td>Type:</td><td>%u</td></tr>\n", Behavior->Type);
            fprintf(hFile, "<tr><td>Arguments:</td><td>%u</td></tr>\n", Behavior->ArgumentCount);
            fprintf(hFile, "<tr><td>Locals:</td><td>%u</td></tr>\n", Behavior->LocalCount);
            fprintf(hFile, "<tr><td>Flags:</td><td>%.4X</td></tr>\n", Behavior->Flags);
            fprintf(hFile, "</table>\n");

            if(Behavior->InstructionCount > 0){
                unsigned i;

                fprintf(hFile, "<br />\n");
                fprintf(hFile, "<table class=\"center\">\n");
                fprintf(hFile, "<tr><th colspan=\"2\">Opcode</th><th>T-Dest</th><th>F-Dest</th><th>Operand data</th></tr>\n");
                for(i=0, Instruction = Behavior->Instructions; i<Behavior->InstructionCount; i++, Instruction++)
                    fprintf(hFile, "<tr><td>%u</td><td><tt>%.4X</tt></td><td>%u</td><td>%u</td>"
                        "<td><tt>%.2X %.2X %.2X %.2X %.2X %.2X %.2X %.2X</tt></td></tr>\n",
                        i, Instruction->Opcode, Instruction->TDest, Instruction->FDest,
                        Instruction->Operands[0], Instruction->Operands[1],
                        Instruction->Operands[2], Instruction->Operands[3],
                        Instruction->Operands[4], Instruction->Operands[5],
                        Instruction->Operands[6], Instruction->Operands[7]);
                fprintf(hFile, "</table>\n");
            }
        }else if(!strcmp(ChunkData->Type, "OBJf")){
            /****
            ** OBJf parsing
            */

            IFFFunctionTable * Table = ChunkData->FormattedData;
            fprintf(hFile, "<table>\n");
            fprintf(hFile, "<tr><td>Version:</td><td>%u</td></tr>\n", Table->Version);
            fprintf(hFile, "</table>\n");

            if(Table->FunctionCount > 0){
                unsigned i;

                fprintf(hFile, "<br />\n");
                fprintf(hFile, "<table class=\"center\">\n");
                fprintf(hFile, "<tr><th colspan=\"2\">Condition function</th><th>Action function</th></tr>\n");
                for(i=0; i<Table->FunctionCount; i++)
                    fprintf(hFile,
                        "<tr><td>%u</td><td>%.4X</td><td>%.4X</td></tr>\n",
                        i+1, Table->Functions[i].ConditionID, Table->Functions[i].ActionID);
                fprintf(hFile, "</table>\n");
            }
        }else{
            fprintf(hFile, "The contents of this chunk cannot be shown on this page.\n");
        }

        fprintf(hFile, "</div>\n\n");
    }
    iff_delete(IFFFileInfo);

    fprintf(hFile,
        "<div id=\"footer\">This page was generated by the use of <a href=\"http://www.niotso.org/\">iff2html</a>.\n");
    fprintf(hFile, "The content of this page may be subject to copyright by the author(s) of the original iff file.</div>\n");
    fprintf(hFile, "</body>\n");
    fprintf(hFile, "</html>");
    fclose(hFile);

    printf("Wrote contents to '%s'.\n", OutFile);
    return 0;
}