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

#include <pr.h>

#define SHOW_FACES 1

/*

       /=========\ /========\ ||        || /========  /=======\
       ||       || ||      || ||        || ||         ||      ||
       ||       || ||      || ||        || ||         ||      ||
       ||       || ||      || ||        || ||         ||      ||
       ||       || ||      || ||   ||   || ||         ||      //
       |=========/ ||      || ||   ||   || |=======   |=====<<
       ||          ||      || ||   ||   || ||         ||      \\
       ||          ||      || ||   ||   || ||         ||       ||
       ||          ||      || ||   ||   || ||         ||       ||
       ||          \========/ \==========/ \========= ||       ||

--------------------------------<===>----------------------------------

    /=======\   /========= |\     || |======\  /========= /=======\
    ||      ||  ||         ||\    || ||    \ \ ||         ||      ||
    ||      ||  ||         || \   || ||     || ||         ||      ||
    ||      ||  ||         ||  \  || ||     || ||         ||      ||
    ||      //  ||         ||   \ || ||     || ||         ||      //
    |======<<   |======    ||\   \|| ||     || |======    |======<<
    ||      \\  ||         || \   || ||     || ||         ||      \\
    ||       || ||         ||  \  || ||     || ||         ||       ||
    ||       || ||         ||   \ || ||    / / ||         ||       ||
    ||       || |========= ||    \|| |======/  |========= ||       ||

PRO file text dump

*/

typedef struct
 {
  PR_DWORD   start;
  PR_DWORD   end;
  PR_DWORD   length;
  PR_WORD    tag;
} PROChunk;


FILE *LPROFile;

#define PROCHUNK_SIGNATURE          0x0303
#define PROCHUNK_VERSION            0x0000
#define PROCHUNK_OBJECTNAME         0x0100
#define PROCHUNK_OBJECTFLAGS        0x0101
#define PROCHUNK_SEGMENTS           0x1000
#define PROCHUNK_SEGMENTNAME        0x1010
#define PROCHUNK_SEGMENTFLAGS       0x1020
#define PROCHUNK_VERTICES           0x1030
#define PROCHUNK_FACES              0x1040
#define PROCHUNK_KEYFRAME_PIVOT     0x1F00
#define PROCHUNK_KEYFRAME_MATRIX    0x1F10
#define PROCHUNK_KEYFRAME_ROTKEYS   0x1F20
#define PROCHUNK_KEYFRAME_POSKEYS   0x1F30
#define PROCHUNK_KEYFRAME_SCLKEYS   0x1F40
#define PROCHUNK_KEYFRAME_LINKS     0x1F50
#define PROCHUNK_SHADETABLELIST     0x2000
#define PROCHUNK_TEXTURELIST        0x2010
#define PROCHUNK_MATERIALLIST       0x2100
#define PROCHUNK_MATERIAL_NAME      0x2101
#define PROCHUNK_MATERIAL_METHOD    0x2102
#define PROCHUNK_MATERIAL_TEXNUM    0x2103
#define PROCHUNK_MATERIAL_BASECOLOR 0x2104
#define PROCHUNK_MATERIAL_SHADES    0x2105
#define PROCHUNK_MATERIAL_TABLE     0x2106
#define PROCHUNK_MATERIAL_ENVMAP    0x2107
#define PROCHUNK_MATERIAL_MIPMAP    0x2108
#define PROCHUNK_MATERIAL_COLOR     0x2109
#define PROCHUNK_MATERIAL_END       0x2199

char *tagnames[] = {
"UNKNOWN",
"PROCHUNK_SIGNATURE",
"PROCHUNK_VERSION",
"PROCHUNK_OBJECTNAME",
"PROCHUNK_OBJECTFLAGS",
"PROCHUNK_SEGMENTS",
"PROCHUNK_SEGMENTNAME",
"PROCHUNK_SEGMENTFLAGS",
"PROCHUNK_VERTICES",
"PROCHUNK_FACES",
"PROCHUNK_SHADETABLELIST",
"PROCHUNK_TEXTURELIST",
"PROCHUNK_MATERIALLIST",
"PROCHUNK_MATERIAL_NAME",
"PROCHUNK_MATERIAL_METHOD",
"PROCHUNK_MATERIAL_TEXNUM",
"PROCHUNK_MATERIAL_BASECOLOR",
"PROCHUNK_MATERIAL_SHADES",
"PROCHUNK_MATERIAL_TABLE",
"PROCHUNK_MATERIAL_ENVMAP",
"PROCHUNK_MATERIAL_MIPMAP",
"PROCHUNK_MATERIAL_COLOR",
"PROCHUNK_MATERIAL_END",
"PROCHUNK_KEYFRAME_PIVOT",
"PROCHUNK_KEYFRAME_MATRIX",
"PROCHUNK_KEYFRAME_ROTKEYS",
"PROCHUNK_KEYFRAME_POSKEYS",
"PROCHUNK_KEYFRAME_SCLKEYS",
"PROCHUNK_KEYFRAME_LINKS"};

char *get_tagname (unsigned short tag)
{
  switch (tag)
    {
     case PROCHUNK_SIGNATURE:          return (tagnames[1]); break;
     case PROCHUNK_VERSION:            return (tagnames[2]); break;
     case PROCHUNK_OBJECTNAME:         return (tagnames[3]); break;
     case PROCHUNK_OBJECTFLAGS:        return (tagnames[4]); break;
     case PROCHUNK_SEGMENTS:           return (tagnames[5]); break;
     case PROCHUNK_SEGMENTNAME:        return (tagnames[6]); break;
     case PROCHUNK_SEGMENTFLAGS:       return (tagnames[7]); break;
     case PROCHUNK_VERTICES:           return (tagnames[8]); break;
     case PROCHUNK_FACES:              return (tagnames[9]); break;
     case PROCHUNK_SHADETABLELIST:     return (tagnames[10]); break;
     case PROCHUNK_TEXTURELIST:        return (tagnames[11]); break;
     case PROCHUNK_MATERIALLIST:       return (tagnames[12]); break;
     case PROCHUNK_MATERIAL_NAME:      return (tagnames[13]); break;
     case PROCHUNK_MATERIAL_METHOD:    return (tagnames[14]); break;
     case PROCHUNK_MATERIAL_TEXNUM:    return (tagnames[15]); break;
     case PROCHUNK_MATERIAL_BASECOLOR: return (tagnames[16]); break;
     case PROCHUNK_MATERIAL_SHADES:    return (tagnames[17]); break;
     case PROCHUNK_MATERIAL_TABLE:     return (tagnames[18]); break;
     case PROCHUNK_MATERIAL_ENVMAP:    return (tagnames[19]); break;
     case PROCHUNK_MATERIAL_MIPMAP:    return (tagnames[20]); break;
     case PROCHUNK_MATERIAL_COLOR:     return (tagnames[21]); break;
     case PROCHUNK_MATERIAL_END:       return (tagnames[22]); break;
     case PROCHUNK_KEYFRAME_PIVOT:     return (tagnames[23]); break; 
     case PROCHUNK_KEYFRAME_MATRIX:    return (tagnames[24]); break;
     case PROCHUNK_KEYFRAME_ROTKEYS:   return (tagnames[25]); break;
     case PROCHUNK_KEYFRAME_POSKEYS:   return (tagnames[26]); break;
     case PROCHUNK_KEYFRAME_SCLKEYS:   return (tagnames[27]); break;
     case PROCHUNK_KEYFRAME_LINKS:     return (tagnames[28]); break;
     default:               return (tagnames[0]); break;
    }
}

PR_WORD LPRO_Read_word (void)
/* Reads a word from the PRO file */
{
PR_WORD data;

  fread (&data, sizeof (PR_WORD), 1, LPROFile);
  return data;
}


PR_DWORD LPRO_Read_dword (void)
/* Reads a dword from the PRO file */
{
PR_DWORD data;

  fread (&data, sizeof (PR_DWORD), 1, LPROFile);
  return data;
}


PR_REAL LPRO_Read_float (void)
/* Reads a floating point number from the PRO file */
{
PR_REAL data;

  fread (&data, sizeof(PR_REAL), 1, LPROFile);
  return data;
}


void LPRO_BeginChunk (PROChunk *chunk)
/* Reads in the chunk header */
{
  chunk->start  = ftell (LPROFile);
  chunk->tag    = LPRO_Read_word ();
  chunk->length = LPRO_Read_dword ();
  chunk->end    = chunk->start + chunk->length;

  printf ("\nChunk found! Tag:%4x   Length:%i\n", chunk->tag, chunk->length);
  printf ("Tag Type: %s\n", get_tagname(chunk->tag));
}


void LPRO_EndChunk (PROChunk *chunk)
/* Moves to the end of a chunk */
{
  fseek (LPROFile, chunk->end, 0);
}


char *LPRO_Read_string (void)
{
static char LPROstring[80];
PR_WORD i;

  for (i = 0; i < 80; i++)
    {
     LPROstring[i] = fgetc (LPROFile);
     if (LPROstring[i] == '\0')
       break;
    }

  return LPROstring;
}


void LPRO_Parse_ObjectName (void)
/* Reads the object title into the new object structure */
{
  printf ("Object Name: %s\n", LPRO_Read_string ());
}


void LPRO_Parse_Version (void)
/* Reads the file format version number */
{
  printf ("Version Number: %f\n", LPRO_Read_float ());
}


void LPRO_Parse_ShadeTableList (void)
{
PR_DWORD i;
char *tablename;
PR_DWORD tablecount;

  tablecount = LPRO_Read_word ();
  /* Read how many tables are used by this object */

  printf ("There are %i tables used by this object\n", tablecount);

  for (i = 0; i < tablecount; i++)
    {
     tablename = LPRO_Read_string ();

     printf ("Table %i: %s\n", i, tablename);
    }
}


void LPRO_Parse_TextureList (void)
{
PR_DWORD i;
char *texturename;
PR_DWORD texturecount;

  texturecount = LPRO_Read_word ();
  /* Read how many textures are used by this object */

  printf ("There are %i textures used by this object\n", texturecount);

  for (i = 0; i < texturecount; i++)
    {
     texturename = LPRO_Read_string ();
     printf ("Texture %i: %s\n", i, texturename);
    }
}



void LPRO_Parse_MaterialList (PROChunk *mainchunk)
/* Load in the materials used by this file */
{
PROChunk chunk;
PR_MATERIAL newmat;

  do {
     LPRO_BeginChunk (&chunk);

     if (chunk.end <= mainchunk->end)
       {
        switch (chunk.tag)
          {
           case PROCHUNK_MATERIAL_NAME:
                    strcpy (newmat.name, LPRO_Read_string ());
                    printf ("Material Name: %s\n", newmat.name);
                    break;
           case PROCHUNK_MATERIAL_METHOD:
                    newmat.render_method = LPRO_Read_dword ();
                    printf ("Render Method: %i\n", newmat.render_method);
                    break;
           case PROCHUNK_MATERIAL_TEXNUM:
                    newmat.texture_number = LPRO_Read_dword ();
                    printf ("Texture: %i\n", newmat.texture_number);
                    break;
           case PROCHUNK_MATERIAL_BASECOLOR:
                    newmat.base_color = fgetc (LPROFile);
                    printf ("Base Color: %i\n", newmat.base_color);
                    break;
           case PROCHUNK_MATERIAL_COLOR:
                    newmat.r = LPRO_Read_float ();
                    newmat.g = LPRO_Read_float ();
                    newmat.b = LPRO_Read_float ();
                    newmat.a = LPRO_Read_float ();
                    printf ("Color: R:%f G:%f B:%f\nAlpha: %f\n", newmat.r,
                    newmat.g, newmat.b, newmat.a);
                    break;
           case PROCHUNK_MATERIAL_SHADES:
                    newmat.render_shades = LPRO_Read_dword ();
                    printf ("Shades: %i\n", newmat.render_shades);
                    break;
           case PROCHUNK_MATERIAL_TABLE:
                    newmat.shadetable = fgetc (LPROFile);
                    printf ("Shadetable: %i\n", newmat.shadetable);
                    break;
           case PROCHUNK_MATERIAL_ENVMAP:
                    newmat.environment_map = fgetc (LPROFile);
                    newmat.environment_axis = fgetc (LPROFile);
                    printf ("Environment Map: %i\n", newmat.environment_map);
                    printf ("Environment Axis: %i\n", newmat.environment_axis);
                    break;
           case PROCHUNK_MATERIAL_MIPMAP:
                    newmat.MipMap = fgetc (LPROFile);
                    newmat.MipMaterial1 = LPRO_Read_word ();
                    newmat.MipMaterial2 = LPRO_Read_word ();
                    newmat.MipMaterial3 = LPRO_Read_word ();
                    newmat.MipMaterial4 = LPRO_Read_word ();
                    newmat.MipShift1 = fgetc (LPROFile);
                    newmat.MipShift2 = fgetc (LPROFile);
                    newmat.MipShift3 = fgetc (LPROFile);
                    newmat.MipShift4 = fgetc (LPROFile);

                    printf ("MipMap: %i\n", newmat.MipMap);
                    printf ("Mip Material 1: %i\n", newmat.MipMaterial1);
                    printf ("Mip Material 2: %i\n", newmat.MipMaterial2);
                    printf ("Mip Material 3: %i\n", newmat.MipMaterial3);
                    printf ("Mip Material 4: %i\n", newmat.MipMaterial4);

                    printf ("Mip Map Shift 1: %i\n", newmat.MipShift1);
                    printf ("Mip Map Shift 2: %i\n", newmat.MipShift2);
                    printf ("Mip Map Shift 3: %i\n", newmat.MipShift3);
                    printf ("Mip Map Shift 4: %i\n", newmat.MipShift4);
                    break;
           case PROCHUNK_MATERIAL_END:
                    printf ("End of material %s\n\n", newmat.name);
                    break;
          }
       }

     LPRO_EndChunk (&chunk);
   } while (chunk.end < mainchunk->end);
}





void LPRO_Parse_ObjectFlags (void)
/* Reads the object flags */
{
  printf ("Object Flags: %i\n", LPRO_Read_dword ());
}


void LPRO_Parse_SegmentName (void)
/* Reads the segment title into the new segment structure */
{
  printf ("Segment Name: %s\n", LPRO_Read_string ());
}


void LPRO_Parse_SegmentFlags (void)
/* Reads the segment flags */
{
  printf ("Object Flags: %i\n", LPRO_Read_dword ());
}


void LPRO_Parse_Vertices (void)
/* Reads the segment's vertices */
{
PR_DWORD numvert;

  numvert = LPRO_Read_dword ();
  printf ("Number of vertices: %i\n", numvert);
}


void LPRO_Parse_Faces (void)
/* Reads the segment's vertices */
{
#ifdef SHOW_FACES
PR_DWORD i;
PR_FACE *face;
#endif

PR_DWORD numface;

  numface = LPRO_Read_dword ();
  printf ("Number of faces: %i\n", numface);

#ifdef SHOW_FACES
  face = calloc (numface, sizeof (PR_FACE));
  for (i = 0; i < numface; i++)
    {
     fread (&face[i], sizeof (PR_FACE), 1, LPROFile);

     printf ("Front Material: %i\n", face[i].material);
     printf ("Back Material: %i\n", face[i].backmaterial);
     printf ("Vert 1: %i\n", face[i].vertex1);
     printf ("Vert 2: %i\n", face[i].vertex2);
     printf ("Vert 3: %i\n", face[i].vertex3);
    }
  free (face);
#endif
}


void LPRO_Parse_Pivot (void)
{
PR_REAL x,y,z;

  x = LPRO_Read_float ();
  y = LPRO_Read_float ();
  z = LPRO_Read_float ();

  printf ("Pivot Location: %f %f %f\n", x, y, z);
}


void LPRO_Parse_Matrix (void)
{
 /* Reads 16 floating point numbers (4x4 matrix) */
}


void LPRO_Parse_rot_keys (void)
{
PR_DWORD numkeys;

  numkeys = LPRO_Read_dword ();
  printf ("Number of rotation keys: %i\n", numkeys);
}


void LPRO_Parse_pos_keys (void)
{
PR_DWORD numkeys;

  numkeys = LPRO_Read_dword ();          
  printf ("Number of position keys: %i\n", numkeys);

}



void LPRO_Parse_scl_keys (void)
{
PR_DWORD numkeys;

  numkeys = LPRO_Read_dword ();
  printf ("Number of scale keys: %i\n", numkeys);
}

void LPRO_Parse_Links (void)
{
PR_DWORD temp;

  temp = LPRO_Read_dword ();
  printf ("Child number: %i\n", temp);

  temp = LPRO_Read_dword ();
  printf ("Next segment number: %i\n", temp);

  temp = LPRO_Read_dword ();
  printf ("Parent number: %i\n", temp);
}



void LPRO_Parse_Segments (PROChunk *mainchunk)
/* Load in the segments */
{
PROChunk chunk;
PR_DWORD segments;

  segments = LPRO_Read_dword ();
  printf ("Number of segments: %i\n", segments);

  do {
     LPRO_BeginChunk (&chunk);

     if (chunk.end <= mainchunk->end)
       {
        switch (chunk.tag)
          {
           case PROCHUNK_SEGMENTNAME:
                    LPRO_Parse_SegmentName ();
                    break;
           case PROCHUNK_SEGMENTFLAGS:
                    LPRO_Parse_SegmentFlags ();
                    break;
           case PROCHUNK_VERTICES:
                    LPRO_Parse_Vertices ();
                    break;
           case PROCHUNK_FACES:
                    LPRO_Parse_Faces ();
                    break;
           case PROCHUNK_KEYFRAME_PIVOT:
                    LPRO_Parse_Pivot ();
                    break;
           case PROCHUNK_KEYFRAME_MATRIX:
                    LPRO_Parse_Matrix ();
                    break;
           case PROCHUNK_KEYFRAME_ROTKEYS:
                    LPRO_Parse_rot_keys ();
                    break;
           case PROCHUNK_KEYFRAME_POSKEYS:
                    LPRO_Parse_pos_keys ();
                    break;
           case PROCHUNK_KEYFRAME_SCLKEYS:
                    LPRO_Parse_scl_keys ();
                    break;
           case PROCHUNK_KEYFRAME_LINKS:
                    LPRO_Parse_Links ();
                    break;
          }
       }

     LPRO_EndChunk (&chunk);
   } while (chunk.end < mainchunk->end);
}


void LPRO_Parse (PROChunk *mainchunk)
/* Start parsing the file */
{
PROChunk chunk;

  do {
    LPRO_BeginChunk (&chunk);

    if (chunk.end <= mainchunk->end)
      {
       switch (chunk.tag)
         {
          case PROCHUNK_VERSION:
                   LPRO_Parse_Version ();
                   break;
          case PROCHUNK_SHADETABLELIST:
                   LPRO_Parse_ShadeTableList ();
                   break;
          case PROCHUNK_TEXTURELIST:
                   LPRO_Parse_TextureList ();
                   break;
          case PROCHUNK_MATERIALLIST:
                   LPRO_Parse_MaterialList (&chunk);
                   break;
          case PROCHUNK_OBJECTNAME:
                   LPRO_Parse_ObjectName ();
                   break;
          case PROCHUNK_OBJECTFLAGS:
                   LPRO_Parse_ObjectFlags ();
                   break;
          case PROCHUNK_SEGMENTS:
                   LPRO_Parse_Segments (&chunk);
                   break;
         }
      }

    LPRO_EndChunk (&chunk);
   } while (chunk.end < mainchunk->end);
}




void LPRO_File_Open (char *filename)
/* Opens a PRO file for reading */
{
  LPROFile = fopen (filename, "rb");
  return;
}





void PR_DumpPRO (char *filename)
/* Attempts to dump a PRO file to the screen
   (Used for debugging) */
{
PROChunk chunk;

  LPROFile = NULL;
  /* Attempt to open the file */
  LPRO_File_Open (filename);

  if (LPROFile == NULL)
    {
     printf ("Could not open %s\n", filename);
     return;
    }


  LPRO_BeginChunk (&chunk);

  if (chunk.tag == PROCHUNK_SIGNATURE)
    LPRO_Parse (&chunk);
  else /* Not a valid PRO file */
    {
     fclose (LPROFile);
     return;
    }

  fclose (LPROFile);
}




void main (int argc, char *argv[])
{
  printf ("Dump PRO - Copyright 1997 Chris Egerter\n"
          "Version "
          PR_VERSION_NUMBER
          "\n");

  if (argc < 1)
    {
     printf ("\nUsage: dumppro filename.pro\n");
     exit (1);
    }

  printf ("Attempting to Load PRO file (%s)\n", argv[1]);
  PR_DumpPRO (argv[1]);
  printf ("Closed PRO file (%s)\n", argv[1]);
}





