Exemplo n.º 1
0
static void SaveOutput(const WebPDecBuffer* const buffer,
                       OutputFileFormat format, const char* const out_file) {
  FILE* fout = NULL;
  int needs_open_file = 1;
  int ok = 1;
  Stopwatch stop_watch;

  if (verbose)
    StopwatchReadAndReset(&stop_watch);

#ifdef HAVE_WINCODEC_H
  needs_open_file = (format != PNG);
#endif
  if (needs_open_file) {
    fout = fopen(out_file, "wb");
    if (!fout) {
      fprintf(stderr, "Error opening output file %s\n", out_file);
      return;
    }
  }

  if (format == PNG) {
#ifdef HAVE_WINCODEC_H
    ok &= WritePNG(out_file, buffer);
#else
    ok &= WritePNG(fout, buffer);
#endif
  } else if (format == PAM) {
    ok &= WritePPM(fout, buffer, 1);
  } else if (format == PPM) {
    ok &= WritePPM(fout, buffer, 0);
  } else if (format == PGM || format == YUV) {
    ok &= WritePGMOrYUV(fout, buffer, format);
  } else if (format == ALPHA_PLANE_ONLY) {
    ok &= WriteAlphaPlane(fout, buffer);
  }
  if (fout) {
    fclose(fout);
  }
  if (ok) {
    printf("Saved file %s\n", out_file);
    if (verbose) {
      const double write_time = StopwatchReadAndReset(&stop_watch);
      printf("Time to write output: %.3fs\n", write_time);
    }
  } else {
    fprintf(stderr, "Error writing file %s !!\n", out_file);
  }
}
Exemplo n.º 2
0
	static void Screenshot( const ScreenshotCommand &cmd ) {
		//NOTE: glGetSynciv never returns GL_SIGNALED on Mac
		// see also: chromium src/ui/gl/gl_fence.cc revision 213908 -> 213907
		//	https://src.chromium.org/viewvc/chrome/trunk/src/ui/gl/gl_fence.cc?r1=213908&r2=213907
		// device details: Yosemite 10.10.3, Intel HD Graphics 5000
#if defined(XS_OS_MAC)
		GLint res = glClientWaitSync( cmd.sync, GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED );
		SDL_assert( res != GL_TIMEOUT_EXPIRED );
#else
		GLint signalled = GL_UNSIGNALED;
		do {
			glGetSynciv( cmd.sync, GL_SYNC_STATUS, 1, nullptr, &signalled );
		} while ( signalled != GL_SIGNALED );
#endif

		glDeleteSync( cmd.sync );
		glBindBuffer( GL_PIXEL_PACK_BUFFER, cmd.pbo );
		void *data = glMapBuffer( GL_PIXEL_PACK_BUFFER, GL_READ_ONLY );
			console.Print( PrintLevel::Normal, "Writing screenshot %s (%ix%i)...\n",
				cmd.name,
				cmd.width,
				cmd.height
			);

			//TODO: strip alpha?
			WritePNG(
				cmd.name,
				reinterpret_cast<uint8_t *>( data ),
				cmd.width,
				cmd.height,
				4
			);
		glUnmapBuffer( GL_PIXEL_PACK_BUFFER );
	}
Exemplo n.º 3
0
/**
 * WriteImage(): Write an image.
 * @param filename Filename to write to.
 * @param format Image format.
 * @param w Width of the image.
 * @param h Height of the image.
 * @param pitch Pitch of the image. (measured in pixels)
 * @param screen Pointer to screen buffer.
 * @param bpp Bits per pixel.
 * @param alpha Alpha channel specification. (32-bit color only.)
 * @return 0 on success; non-zero on error.
 */
int ImageUtil::WriteImage(const char* filename, const ImageFormat format,
			  const int w, const int h, const int pitch,
			  const void *screen, const int bpp, const AlphaChannel alpha)
{
	if (!filename)
		return 1;
	
	// Write an image file.
	FILE *fImg = fopen(filename, "wb");
	if (!fImg)
	{
		LOG_MSG(gens, LOG_MSG_LEVEL_CRITICAL,
			"Error opening %s.", filename);
		return 2;
	}
	
	int rval;
#ifdef GENS_PNG
	if (format == IMAGEFORMAT_PNG)
	{
		rval = WritePNG(fImg, w, h, pitch, screen, bpp, alpha);
	}
	else
#endif /* GENS_PNG */
	{
		rval = WriteBMP(fImg, w, h, pitch, screen, bpp);
	}
	
	fclose(fImg);
	return rval;
}
Exemplo n.º 4
0
bool ImageWriter::WritePNG(const std::string& sFilename, ImageObject& image)
{
   if (image.GetNumChannels() != 4)
   {
      return false;
   }

   return WritePNG(sFilename, image.GetRawData().get(), image.GetWidth(), image.GetHeight());
}
Exemplo n.º 5
0
bool WriteImage(ImageData* outImage, char* fileName)
{
    // Check that fileName ends in either jpg or png, and dispatch if so
    if (strcmp("png", fileName + strlen(fileName) - 3) == 0)
        return WritePNG(outImage, fileName);
    else if (strcmp("jpg", fileName + strlen(fileName) - 3) == 0)
        return WriteJPG(outImage, fileName);
    else {
        return false; 
    }
}
Exemplo n.º 6
0
static void
write_png (image_t *image)
{
	byte       *data, *in, *out, b;
	int         size = image->width * image->height;

	out = data = malloc (size * 3);
	for (in = image->image; in - image->image < size; in++) {
		b = *in;
		*out++ = b;
		*out++ = b;
		*out++ = b;
	}
	WritePNG (options.outf_name, data, image->width, image->height);
}
Exemplo n.º 7
0
/* saves the contents of an HBITMAP to a file.  returns 1 if successfull,
                // 0 otherwise */
                BMGError SaveBitmapToPNGFile( HBITMAP hBitmap,      /* bitmap to be saved */
                const char *filename) /* name of output file */
{
    struct BMGImageStruct img;
    char msg[256], ext[4], *period;
    BMGError out = BMG_OK;

    InitBMGImage( &img );

    /* determine the file type by using the extension */
    strcpy( msg, filename );
    period = strrchr( msg, '.' );
    if ( period != NULL )
    {
        period++;
        strcpy( ext, period );
        ext[0] = toupper( ext[0] );
        ext[1] = toupper( ext[1] );
        ext[2] = toupper( ext[2] );
        ext[3] = 0;
    }
    else
    {
        strcat( msg, ".PNG" );
        strcpy( ext, "PNG" );
    }

    if ( strcmp( ext, "PNG" ) == 0 )
    {
/* extract data from the bitmap.  We assume that 32 bit images have been
// blended with the background (unless this is a DDB - see GetDataFromBitmap
        // for more details) */
        out = GetDataFromBitmap( hBitmap, &img, 1 ); 
        if (  out == BMG_OK )
        {
            out = WritePNG( msg, img );
        }
        FreeBMGImage( &img );
    }
    else
    {
        out = errInvalidFileExtension;
    }

    SetLastBMGError( out );
    return out;
}
Exemplo n.º 8
0
Arquivo: azura.cpp Projeto: kyuu/azura
    //--------------------------------------------------------------
    bool WriteImage(Image* image, File* file, FileFormat::Enum ff)
    {
        if (!image || !file) {
            return false;
        }

        switch (ff) {
            case FileFormat::BMP:
                return WriteBMP(image, file);
            case FileFormat::PNG:
                return WritePNG(image, file);
            case FileFormat::JPEG:
                return WriteJPEG(image, file);
            default:
                return false;
        }
    }
int main (int argc , char ** argv) {
  if (argc==3) {
    FILE * fp = fopen (argv[1],"rb");
    if (fp) {
      unsigned int sizex, sizey;
      int img_depth,img_alpha;
      unsigned char ** row_pointers;
      unsigned char * data = ReadPNG(fp,sizex,sizey,img_depth,img_alpha,&row_pointers);
      fclose(fp);
      if (data&&(img_alpha&PNG_HAS_ALPHA)) {
        ModifyImage (sizex,sizey,img_depth,img_alpha&PNG_HAS_ALPHA,row_pointers);
        free(row_pointers);
        FILE * wr = fopen(argv[2],"wb");
        WritePNG(wr,data,sizex,sizey,img_depth,img_alpha&PNG_HAS_ALPHA);
        fclose(wr);
      }
    }
  }
}
Exemplo n.º 10
0
int main (int argc , char ** argv) {
  if (argc==3) {
    FILE * fp = fopen (argv[1],"rb");
    if (fp) {
      unsigned int sizex, sizey;
      int img_depth,img_alpha;
      unsigned char ** row_pointers;
      unsigned char * data = ReadPNG(fp,sizex,sizey,img_depth,img_alpha,&row_pointers);
      fclose(fp);
      if (data) {
        unsigned char outimage[HEI][WID][3];
        ModifyImage (sizex,sizey,img_depth,img_alpha&PNG_HAS_ALPHA,row_pointers,WID,HEI,outimage);
        free(row_pointers);
        FILE * wr = fopen(argv[2],"wb");
        WritePNG(wr,(unsigned char*)outimage,WID,HEI,8,0);
        fclose(wr);
      }
    }
  }
}
Exemplo n.º 11
0
static void SavePNG(uint8_t* buffer, int width, int height)
{
    wchar_t* fileName = xmalloc(32768 * sizeof(wchar_t));

    GenerateFileName(fileName, 32768);

    if (ShowSaveDialog(fileName, 32768))
    {
        FILE* file = OpenFileWrite(fileName);

        if (!file)
            Fatal(L"Couldn't write file.");

        if (!WritePNG(file, buffer, width, height))
            Fatal(L"Couldn't write PNG.");

        fclose(file);
    }

    free(fileName);
}
Exemplo n.º 12
0
int R2Image::
Write(const char *filename) const
{
  // Parse input filename extension
  char *input_extension;
  if (!(input_extension = (char*)strrchr(filename, '.'))) {
    fprintf(stderr, "Output file has no extension (e.g., .jpg).\n");
    return 0;
  }
  
  // Write file of appropriate type
  if (!strncmp(input_extension, ".bmp", 4)) return WriteBMP(filename);
  else if (!strncmp(input_extension, ".png", 4)) return WritePNG(filename);
  else if (!strncmp(input_extension, ".ppm", 4)) return WritePPM(filename, 1);
  else if (!strncmp(input_extension, ".pgm", 4)) return WritePGM(filename, 1);
  else if (!strncmp(input_extension, ".pfm", 4)) return WritePFM(filename);
  else if (!strncmp(input_extension, ".jpg", 5)) return WriteJPEG(filename);
  else if (!strncmp(input_extension, ".jpeg", 5)) return WriteJPEG(filename);

  // Should never get here
  fprintf(stderr, "Unrecognized image file extension");
  return 0;
}
Exemplo n.º 13
0
/* ********************************************************************* */
void WriteData (const Data *d, Output *output, Grid *grid)
/*!
 * Write data to disk using any of the available formats.
 *
 * \param [in] d      pointer to PLUTO Data structre 
 * \param [in] output the output structure corresponding to a given
 *                    format
 * \param [in] grid   pointer to an array of Grid structures
 *********************************************************************** */
{
  int    i, j, k, nv;
  int    single_file;
  size_t dsize;
  char   filename[512], sline[512];
  static int last_computed_var = -1;
  double units[MAX_OUTPUT_VARS]; 
  float ***Vpt3;
  void *Vpt;
  FILE *fout, *fbin;
  time_t tbeg, tend;
  long long offset;

/* -----------------------------------------------------------
            Increment the file number and initialize units
   ----------------------------------------------------------- */

  output->nfile++;

  print1 ("> Writing file #%d (%s) to disk...", output->nfile, output->ext);

  #ifdef PARALLEL
   MPI_Barrier (MPI_COMM_WORLD);
   if (prank == 0) time(&tbeg);
  #endif

  for (nv = 0; nv < MAX_OUTPUT_VARS; nv++) units[nv] = 1.0;
  if (output->cgs) GetCGSUnits(units);

/* --------------------------------------------------------
            Get user var if necessary 
   -------------------------------------------------------- */

  if (last_computed_var != g_stepNumber && d->Vuser != NULL) {
    ComputeUserVar (d, grid);
    last_computed_var = g_stepNumber;
  }

/* --------------------------------------------------------
            Select the output type 
   -------------------------------------------------------- */

  if (output->type == DBL_OUTPUT) {

  /* ------------------------------------------------------------------- */
  /*! - \b DBL output:
        Double-precision data files can be written using single or
        multiple file mode. 
        - for single file, serial: we open the file just once before
          the main variable loop, dump variables and then close.
        - for single file, parallel the distributed array descriptor sz is
          different for cell-centered or staggered data type and we
          thus have to open and close the file after each variable
          has been dumped.
        - when writing multiple files we open, write to and close the
          file one each loop cycle.
        \note In all cases, the pointer to the data array that has to be 
              written must be cast into (void *) and the starting index of 
              the array must be zero.
  */
  /* ------------------------------------------------------------------- */

    int sz;
    single_file = strcmp(output->mode,"single_file") == 0;
    dsize = sizeof(double);

    if (single_file){  /* -- single output file -- */

      sprintf (filename, "%s/data.%04d.%s", output->dir,output->nfile, 
                                            output->ext);
      offset = 0;
      #ifndef PARALLEL
       fbin = OpenBinaryFile (filename, 0, "w");
      #endif
      for (nv = 0; nv < output->nvar; nv++) {
        if (!output->dump_var[nv]) continue;

        if      (output->stag_var[nv] == -1) {  /* -- cell-centered data -- */
          sz = SZ;
          Vpt = (void *)output->V[nv][0][0];
        } else if (output->stag_var[nv] == 0) { /* -- x-staggered data -- */
          sz  = SZ_stagx;
          Vpt = (void *)(output->V[nv][0][0]-1);
        } else if (output->stag_var[nv] == 1) { /* -- y-staggered data -- */
          sz = SZ_stagy;
          Vpt = (void *)output->V[nv][0][-1];
        } else if (output->stag_var[nv] == 2) { /* -- z-staggered data -- */
           sz = SZ_stagz;
           Vpt = (void *)output->V[nv][-1][0];
        }
        #ifdef PARALLEL
         fbin = OpenBinaryFile (filename, sz, "w");
         AL_Set_offset(sz, offset);
        #endif
        WriteBinaryArray (Vpt, dsize, sz, fbin, output->stag_var[nv]);
        #ifdef PARALLEL
         offset = AL_Get_offset(sz);
         CloseBinaryFile(fbin, sz);
        #endif
      }
      #ifndef PARALLEL
       CloseBinaryFile(fbin, sz);
      #endif

    }else{              /* -- multiple files -- */

      for (nv = 0; nv < output->nvar; nv++) {
        if (!output->dump_var[nv]) continue;
        sprintf (filename, "%s/%s.%04d.%s", output->dir, output->var_name[nv], 
                                            output->nfile, output->ext);

        if      (output->stag_var[nv] == -1) {  /* -- cell-centered data -- */
          sz = SZ;
          Vpt = (void *)output->V[nv][0][0];
        } else if (output->stag_var[nv] == 0) { /* -- x-staggered data -- */
          sz  = SZ_stagx;
          Vpt = (void *)(output->V[nv][0][0]-1);
        } else if (output->stag_var[nv] == 1) { /* -- y-staggered data -- */
          sz = SZ_stagy;
          Vpt = (void *)output->V[nv][0][-1];
        } else if (output->stag_var[nv] == 2) { /* -- z-staggered data -- */
           sz = SZ_stagz;
           Vpt = (void *)output->V[nv][-1][0];
        }
        fbin = OpenBinaryFile (filename, sz, "w");
        WriteBinaryArray (Vpt, dsize, sz, fbin, output->stag_var[nv]);
        CloseBinaryFile (fbin, sz);
      }
    }

  } else if (output->type == FLT_OUTPUT) {

  /* ----------------------------------------------------------
                 FLT output for cell-centered data
     ---------------------------------------------------------- */

    single_file = strcmp(output->mode,"single_file") == 0;

    if (single_file){  /* -- single output file -- */
      sprintf (filename, "%s/data.%04d.%s", output->dir, output->nfile, 
                                            output->ext);
      fbin = OpenBinaryFile (filename, SZ_float, "w");
      for (nv = 0; nv < output->nvar; nv++) {
        if (!output->dump_var[nv]) continue;
/*        Vpt = (void *)(Convert_dbl2flt(output->V[nv],0))[0][0];  */
        Vpt3 = Convert_dbl2flt(output->V[nv], units[nv],0);
        Vpt = (void *)Vpt3[0][0];
        WriteBinaryArray (Vpt, sizeof(float), SZ_float, fbin, 
                          output->stag_var[nv]);
      }
      CloseBinaryFile(fbin, SZ_float);
/*
BOV_Header(output, filename);
*/
    }else{              /* -- multiple files -- */

      for (nv = 0; nv < output->nvar; nv++) {
        if (!output->dump_var[nv]) continue;
        sprintf (filename, "%s/%s.%04d.%s", output->dir, output->var_name[nv], 
                                            output->nfile, output->ext);

        fbin = OpenBinaryFile (filename, SZ_float, "w");
/*        Vpt = (void *)(Convert_dbl2flt(output->V[nv],0))[0][0];   */
        Vpt3 = Convert_dbl2flt(output->V[nv], units[nv],0);
        Vpt = (void *)Vpt3[0][0];
        WriteBinaryArray (Vpt, sizeof(float), SZ_float, fbin, 
                          output->stag_var[nv]);
        CloseBinaryFile (fbin, SZ_float);
      }
    }

  }else if (output->type == DBL_H5_OUTPUT || output->type == FLT_H5_OUTPUT){

  /* ------------------------------------------------------
       HDF5 (static grid) output (single/double precision)
     ------------------------------------------------------ */

    #ifdef USE_HDF5 
     single_file = YES;
     WriteHDF5 (output, grid);
    #else
     print1 ("! WriteData: HDF5 library not available\n");
     return;
    #endif

  }else if (output->type == VTK_OUTPUT) { 

  /* ------------------------------------------------------------------- */
  /*! - \b VTK output:  
      in order to enable parallel writing, files must be closed and
      opened again for scalars, since the distributed array descriptors 
      used by ArrayLib (Float_Vect) and (float) are different. This is
      done using the AL_Get_offset() and AL_Set_offset() functions.      */
  /* ------------------------------------------------------------------- */
    
    single_file = strcmp(output->mode,"single_file") == 0;
    sprintf (filename, "%s/data.%04d.%s", output->dir, output->nfile,
                                          output->ext);

    if (single_file){  /* -- single output file -- */

      fbin  = OpenBinaryFile(filename, SZ_Float_Vect, "w");
      WriteVTK_Header(fbin, grid);
      for (nv = 0; nv < output->nvar; nv++) {  /* -- write vectors -- */
        if (output->dump_var[nv] != VTK_VECTOR) continue;
        WriteVTK_Vector (fbin, output->V + nv, units[nv],
                         output->var_name[nv], grid);
      }

      #ifdef PARALLEL
       offset = AL_Get_offset(SZ_Float_Vect);
       CloseBinaryFile(fbin, SZ_Float_Vect);
       fbin  = OpenBinaryFile(filename, SZ_float, "w");
       AL_Set_offset(SZ_float, offset);
      #endif
      
      for (nv = 0; nv < output->nvar; nv++) { /* -- write scalars -- */
        if (output->dump_var[nv] != YES) continue;
        WriteVTK_Scalar (fbin, output->V[nv], units[nv],
                         output->var_name[nv], grid);
      }
      CloseBinaryFile(fbin, SZ_float);

    }else{          /* -- multiple output files -- */

      for (nv = 0; nv < output->nvar; nv++) { /* -- write vectors -- */
        if (output->dump_var[nv] != VTK_VECTOR) continue;
        if (strcmp(output->var_name[nv],"vx1") == 0) {
          sprintf (filename, "%s/vfield.%04d.%s", output->dir, output->nfile, 
                                                  output->ext);
        }else if (strcmp(output->var_name[nv],"bx1") == 0) {
          sprintf (filename, "%s/bfield.%04d.%s", output->dir, output->nfile, 
                                                  output->ext);
        }else{
          print1 ("! WriteData: unknown vector type in VTK output\n"); 
          QUIT_PLUTO(1);
        }

        fbin = OpenBinaryFile(filename, SZ_Float_Vect, "w");
        WriteVTK_Header(fbin, grid);
        WriteVTK_Vector(fbin, output->V + nv, units[nv],
                        output->var_name[nv], grid);
        CloseBinaryFile(fbin, SZ_Float_Vect);
      }

      for (nv = 0; nv < output->nvar; nv++) {  /* -- write scalars -- */
        if (output->dump_var[nv] != YES) continue;
        sprintf (filename, "%s/%s.%04d.%s", output->dir, output->var_name[nv], 
                                            output->nfile,  output->ext);
        fbin = OpenBinaryFile(filename, SZ_Float_Vect, "w");
        WriteVTK_Header(fbin, grid);
        #ifdef PARALLEL
         offset = AL_Get_offset(SZ_Float_Vect);
         CloseBinaryFile(fbin, SZ_Float_Vect);
         fbin  = OpenBinaryFile(filename, SZ_float, "w");
         AL_Set_offset(SZ_float, offset);
        #endif
        WriteVTK_Scalar(fbin, output->V[nv], units[nv],
                        output->var_name[nv], grid);
        CloseBinaryFile (fbin, SZ_float);
      }
    }

  }else if (output->type == TAB_OUTPUT) { 

  /* ------------------------------------------------------
               Tabulated (ASCII) output
     ------------------------------------------------------ */

    single_file = YES;
    sprintf (filename,"%s/data.%04d.%s", output->dir, output->nfile,
                                         output->ext);
    WriteTabArray (output, filename, grid);

  }else if (output->type == PPM_OUTPUT) { 

  /* ------------------------------------------------------
                   PPM output
     ------------------------------------------------------ */

    single_file = NO;
    for (nv = 0; nv < output->nvar; nv++) {
      if (!output->dump_var[nv]) continue;
      sprintf (filename, "%s/%s.%04d.%s", output->dir, output->var_name[nv], 
                                          output->nfile, output->ext);
      WritePPM (output->V[nv], output->var_name[nv], filename, grid);
    }

  }else if (output->type == PNG_OUTPUT) { 

  /* ------------------------------------------------------
                   PNG  output
     ------------------------------------------------------ */

    #ifdef USE_PNG
     single_file = NO;
     for (nv = 0; nv < output->nvar; nv++) {
       if (!output->dump_var[nv]) continue;
       sprintf (filename, "%s/%s.%04d.%s", output->dir, output->var_name[nv], 
                                           output->nfile, output->ext);
       WritePNG (output->V[nv], output->var_name[nv], filename, grid);
     }
    #else
     print1 ("! PNG library not available\n");
     return;
    #endif

  }

/* -------------------------------------------------------------
           Update corresponding ".out" file
   ------------------------------------------------------------- */

  sprintf (filename,"%s/%s.out",output->dir, output->ext);

  if (prank == 0) {
    if (output->nfile == 0) {
      fout = fopen (filename, "w");
    }else {
      fout = fopen (filename, "r+");
      for (nv = 0; nv < output->nfile; nv++) fgets (sline, 512, fout);
      fseek (fout, ftell(fout), SEEK_SET);
    }

  /* -- write a multi-column file -- */

    fprintf (fout, "%d %12.6e %12.6e %ld ",
             output->nfile, g_time, g_dt, g_stepNumber);

    if (single_file) fprintf (fout,"single_file ");
    else             fprintf (fout,"multiple_files ");

    if (IsLittleEndian()) fprintf (fout, "little ");
    else                  fprintf (fout, "big ");

    for (nv = 0; nv < output->nvar; nv++) { 
      if (output->dump_var[nv]) fprintf (fout, "%s ", output->var_name[nv]);
    }

    fprintf (fout,"\n");
    fclose (fout);
  }

  #ifdef PARALLEL
   MPI_Barrier (MPI_COMM_WORLD);
   if (prank == 0){
     time(&tend);
     print1 (" [%5.2f sec]",difftime(tend,tbeg));
   }
  #endif
  print1 ("\n");

}
Exemplo n.º 14
0
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;
}