
// Copyright 2007 Juhana Sadeharju
// GNU GPL

// espparser: extracts the data from Oblivion ESP file
// espcompiler: compiles the data to Oblivion ESP file

// Usage: espparser <esp file> <target directory>
// Usage: espcompiler <source directory> <esp file>

// The ESP file is converted to directory and file system.
// This is only the first step in parsing and using the
// data in the ESP file.

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <zlib.h>

#include "mylib/filesdirs.h"
#include "mylib/mmapfile.h"
#include "mylib/buffer.h"


// Oblivion mod file structures.
typedef struct {
  int size;
  unsigned char *data;
} ty_oblivion_chunk;


// For recursive parsing we windowize the original buffer.
void *bufferstack[128];



void printflags(unsigned int n)
{
  unsigned int k;
  int i;

  k = 0x80000000;
  for (i = 0; i < 32; i++) {
    if ((n & k) != 0) {
      printf("1");
    } else {
      printf("0");
    }
    k = k >> 1;
    if (((i % 4) == 3) && (i != 31)) {
      printf(",");
    }
  }
  printf("\n");
}

void printmodt(void *b, int n)
{
  int i,loc,m;
  float f;

  for (i = 0; i < n/4; i++) {
    loc = buffer_getloc(b);
    m = buffer_read_int(b);
    buffer_setloc(b,loc);
    f = buffer_read_float(b);
    printf("%i -- %f\n",m,f);
  }

}


#include "parsebuffer_achr.c"

#include "parsebuffer_acre.c"

#include "parsebuffer_acti.c"

#include "parsebuffer_alch.c"

#include "parsebuffer_ammo.c"

// Not in multi.esp.
// Not in multi2.esp.
int parsebuffer_anio(void *b)
{
  int n;
  int loc;

  printf("Skipping\n");
  loc = buffer_getloc(b);
  buffer_skip_bytes(b,4);
  n = buffer_read_int(b);
  buffer_setloc(b,loc);
  buffer_skip_bytes(b,20);
  buffer_skip_bytes(b,n);

  return -1;
}


#include "parsebuffer_appa.c"

#include "parsebuffer_armo.c"

#include "parsebuffer_book.c"

#include "parsebuffer_bsgn.c"

#include "parsebuffer_cell.c"

#include "parsebuffer_clas.c"


// Not in multi.esp.
// Not in multi2.esp.
int parsebuffer_clmt(void *b)
{
  int n;
  int loc;

  printf("Skipping\n");
  loc = buffer_getloc(b);
  buffer_skip_bytes(b,4);
  n = buffer_read_int(b);
  buffer_setloc(b,loc);
  buffer_skip_bytes(b,20);
  buffer_skip_bytes(b,n);

  return -1;
}


#include "parsebuffer_clot.c"

#include "parsebuffer_cont.c"

#include "parsebuffer_crea.c"

#include "parsebuffer_csty.c"

#include "parsebuffer_dial.c"

#include "parsebuffer_door.c"

#include "parsebuffer_efsh.c"

#include "parsebuffer_ench.c"

#include "parsebuffer_eyes.c"

#include "parsebuffer_fact.c"

#include "parsebuffer_flor.c"

#include "parsebuffer_furn.c"

#include "parsebuffer_glob.c"

#include "parsebuffer_gmst.c"

#include "parsebuffer_gras.c"

#include "parsebuffer_hair.c"

#include "parsebuffer_idle.c"

#include "parsebuffer_info.c"

#include "parsebuffer_ingr.c"

#include "parsebuffer_keym.c"

#include "parsebuffer_land.c"

#include "parsebuffer_ligh.c"

#include "parsebuffer_lscr.c"

#include "parsebuffer_ltex.c"

#include "parsebuffer_lvlc.c"

#include "parsebuffer_lvli.c"


// Not in multi.esp.
// Not in multi2.esp.
int parsebuffer_lvsp(void *b)
{
  int n;
  int loc;

  printf("Skipping\n");
  loc = buffer_getloc(b);
  buffer_skip_bytes(b,4);
  n = buffer_read_int(b);
  buffer_setloc(b,loc);
  buffer_skip_bytes(b,20);
  buffer_skip_bytes(b,n);

  return -1;
}


#include "parsebuffer_mgef.c"

#include "parsebuffer_misc.c"

#include "parsebuffer_npc.c"

#include "parsebuffer_pack.c"

#include "parsebuffer_pgrd.c"

#include "parsebuffer_qust.c"

#include "parsebuffer_race.c"

#include "parsebuffer_refr.c"

#include "parsebuffer_regn.c"


// Not in multi.esp.
// Not in multi2.esp.
int parsebuffer_road(void *b)
{
  int n;
  int loc;

  printf("Skipping\n");
  loc = buffer_getloc(b);
  buffer_skip_bytes(b,4);
  n = buffer_read_int(b);
  buffer_setloc(b,loc);
  buffer_skip_bytes(b,20);
  buffer_skip_bytes(b,n);

  return -1;
}


// Not in multi.esp.
// Not in multi2.esp.
int parsebuffer_sbsp(void *b)
{
  int n;
  int loc;

  printf("Skipping\n");
  loc = buffer_getloc(b);
  buffer_skip_bytes(b,4);
  n = buffer_read_int(b);
  buffer_setloc(b,loc);
  buffer_skip_bytes(b,20);
  buffer_skip_bytes(b,n);

  return -1;
}


#include "parsebuffer_scpt.c"

#include "parsebuffer_sgst.c"


// Not in multi.esp.
// Not in multi2.esp.
int parsebuffer_skil(void *b)
{
  int n;
  int loc;

  printf("Skipping\n");
  loc = buffer_getloc(b);
  buffer_skip_bytes(b,4);
  n = buffer_read_int(b);
  buffer_setloc(b,loc);
  buffer_skip_bytes(b,20);
  buffer_skip_bytes(b,n);

  return -1;
}


#include "parsebuffer_slgm.c"

#include "parsebuffer_soun.c"

#include "parsebuffer_spel.c"

#include "parsebuffer_stat.c"

#include "parsebuffer_tes4.c"

#include "parsebuffer_tree.c"

#include "parsebuffer_watr.c"

#include "parsebuffer_weap.c"

#include "parsebuffer_wrld.c"

#include "parsebuffer_wthr.c"

int global_grouplevel = -1;
int global_locn = 0;
int global_loc[20];

void parsebuffer(void *b)
{
  int loc,loc2,n,m;
  char *s,*s2;
  void *b2;
  unsigned char *buffer;
  float f;
  unsigned int flags1;
  unsigned int flags2;
  int r;

  global_grouplevel++;

  while (buffer_eof(b) != 1) {
    loc = buffer_getloc(b);
    s = buffer_read_string4(b);
    printf("Chunk name 0: %s %i %i",s,global_grouplevel,
	   global_loc[global_locn] + loc);
    if (strcmp(s,"GRUP") == 0) {
      n = buffer_read_int(b); // uint groupsize;
      printf(" %i\n",global_loc[global_locn] + loc + n);
      printf("Groupsize = %i\n",n);
      // buffer_setloc(b,loc);
      // buffer_skip_bytes(b,20);
      // loc2 = buffer_getloc(b);
      s2 = buffer_read_string4(b); // label
      m = buffer_read_int(b); // int grouptype;
      switch (m) {
      case 0:
	printf("%s\n",s2);
	printf("Grouptype = 0 Top\n");
	break;
      case 1:
	printf("%i\n",((int *)s2)[0]);
	printf("Grouptype = 1 World Children\n");
	break;
      case 2:
	// printf("loc2 = %i\n",global_loc[global_locn] + loc2);
	printf("%i\n",((int *)s2)[0]);
	printf("Grouptype = 2 Interior Cell Block\n");
	break;
      case 3:
	printf("%i\n",((int *)s2)[0]);
	printf("Grouptype = 3 Interior Cell Sub-Block\n");
	break;
      case 4:
	// grid y,x -- reverse order
	printf("%i %i\n",
	       (int)(((unsigned short *)s2)[0]),
	       (int)(((unsigned short *)s2)[1]));
	printf("Grouptype = 4 Exterior Cell Block\n");
	break;
      case 5:
	// grid y,x -- reverse order
	printf("%i %i\n",
	       (int)(((unsigned short *)s2)[0]),
	       (int)(((unsigned short *)s2)[1]));
	printf("Grouptype = 5 Exterior Cell Sub-Block\n");
	break;
      case 6:
	printf("%i\n",((int *)s2)[0]);
	printf("Grouptype = 6 Cell Children\n");
	break;
      case 7:
	printf("%i\n",((int *)s2)[0]);
	printf("Grouptype = 7 Topic Children\n");
	break;
      case 8:
	printf("%i\n",((int *)s2)[0]);
	printf("Grouptype = 8 Cell Persistent Children\n");
	break;
      case 9:
	printf("%i\n",((int *)s2)[0]);
	printf("Grouptype = 9 Cell Temporary Children\n");
	break;
      case 10:
	printf("%i\n",((int *)s2)[0]);
	printf("Grouptype = 10 Cell Visible Distant Children\n");
	break;
      default:
	printf("Unknown group type\n");
	break;
      }
      free(s2);
      m = buffer_read_int(b); // int stamp;
      printf("Stamp = %i\n",m);
      // buffer_skip_bytes(b,n);
      buffer = buffer_getbuf(b);
      b2 = buffer_new(&(buffer[buffer_getloc(b)]),n-20);
      global_locn++;
      global_loc[global_locn] = global_loc[global_locn - 1] + buffer_getloc(b);
      parsebuffer(b2);
      buffer_free(b2);
      global_locn--;
      buffer_setloc(b,loc);
      buffer_skip_bytes(b,n);

      printf("Chunk extra: GRUP END\n");
    } else {
      printf("\n");
      buffer_setloc(b,loc);
      if (strcmp(s,"ACHR") == 0) {
	r = parsebuffer_achr(b);
      } else if (strcmp(s,"ACRE") == 0) {
	r = parsebuffer_acre(b);
      } else if (strcmp(s,"ACTI") == 0) {
	r = parsebuffer_acti(b);
      } else if (strcmp(s,"ALCH") == 0) {
	r = parsebuffer_alch(b);
      } else if (strcmp(s,"AMMO") == 0) {
	r = parsebuffer_ammo(b);
      } else if (strcmp(s,"ANIO") == 0) {
	r = parsebuffer_anio(b);
      } else if (strcmp(s,"APPA") == 0) {
	r = parsebuffer_appa(b);
      } else if (strcmp(s,"ARMO") == 0) {
	r = parsebuffer_armo(b);
      } else if (strcmp(s,"BOOK") == 0) {
	r = parsebuffer_book(b);
      } else if (strcmp(s,"BSGN") == 0) {
	r = parsebuffer_bsgn(b);
      } else if (strcmp(s,"CELL") == 0) {
	r = parsebuffer_cell(b);
      } else if (strcmp(s,"CLAS") == 0) {
	r = parsebuffer_clas(b);
      } else if (strcmp(s,"CLMT") == 0) {
	r = parsebuffer_clmt(b);
      } else if (strcmp(s,"CLOT") == 0) {
	r = parsebuffer_clot(b);
      } else if (strcmp(s,"CONT") == 0) {
	r = parsebuffer_cont(b);
      } else if (strcmp(s,"CREA") == 0) {
	r = parsebuffer_crea(b);
      } else if (strcmp(s,"CSTY") == 0) {
	r = parsebuffer_csty(b);
      } else if (strcmp(s,"DIAL") == 0) {
	r = parsebuffer_dial(b);
      } else if (strcmp(s,"DOOR") == 0) {
	r = parsebuffer_door(b);
      } else if (strcmp(s,"EFSH") == 0) {
	r = parsebuffer_efsh(b);
      } else if (strcmp(s,"ENCH") == 0) {
	r = parsebuffer_ench(b);
      } else if (strcmp(s,"EYES") == 0) {
	r = parsebuffer_eyes(b);
      } else if (strcmp(s,"FACT") == 0) {
	r = parsebuffer_fact(b);
      } else if (strcmp(s,"FLOR") == 0) {
	r = parsebuffer_flor(b);
      } else if (strcmp(s,"FURN") == 0) {
	r = parsebuffer_furn(b);
      } else if (strcmp(s,"GLOB") == 0) {
	r = parsebuffer_glob(b);
      } else if (strcmp(s,"GMST") == 0) {
	r = parsebuffer_gmst(b);
      } else if (strcmp(s,"GRAS") == 0) {
	r = parsebuffer_gras(b);
      } else if (strcmp(s,"HAIR") == 0) {
	r = parsebuffer_hair(b);
      } else if (strcmp(s,"IDLE") == 0) {
	r = parsebuffer_idle(b);
      } else if (strcmp(s,"INFO") == 0) {
	r = parsebuffer_info(b);
      } else if (strcmp(s,"INGR") == 0) {
	r = parsebuffer_ingr(b);
      } else if (strcmp(s,"KEYM") == 0) {
	r = parsebuffer_keym(b);
      } else if (strcmp(s,"LAND") == 0) {
	r = parsebuffer_land(b);
      } else if (strcmp(s,"LIGH") == 0) {
	r = parsebuffer_ligh(b);
      } else if (strcmp(s,"LSCR") == 0) {
	r = parsebuffer_lscr(b);
      } else if (strcmp(s,"LTEX") == 0) {
	r = parsebuffer_ltex(b);
      } else if (strcmp(s,"LVLC") == 0) {
	r = parsebuffer_lvlc(b);
      } else if (strcmp(s,"LVLI") == 0) {
	r = parsebuffer_lvli(b);
      } else if (strcmp(s,"LVSP") == 0) {
	r = parsebuffer_lvsp(b);
      } else if (strcmp(s,"MGEF") == 0) {
	r = parsebuffer_mgef(b);
      } else if (strcmp(s,"MISC") == 0) {
	r = parsebuffer_misc(b);
      } else if (strcmp(s,"NPC_") == 0) {
	r = parsebuffer_npc(b);
      } else if (strcmp(s,"PACK") == 0) {
	r = parsebuffer_pack(b);
      } else if (strcmp(s,"PGRD") == 0) {
	r = parsebuffer_pgrd(b);
      } else if (strcmp(s,"QUST") == 0) {
	r = parsebuffer_qust(b);
      } else if (strcmp(s,"RACE") == 0) {
	r = parsebuffer_race(b);
      } else if (strcmp(s,"REFR") == 0) {
	r = parsebuffer_refr(b);
      } else if (strcmp(s,"REGN") == 0) {
	r = parsebuffer_regn(b);
      } else if (strcmp(s,"ROAD") == 0) {
	r = parsebuffer_road(b);
      } else if (strcmp(s,"SBSP") == 0) {
	r = parsebuffer_sbsp(b);
      } else if (strcmp(s,"SCPT") == 0) {
	r = parsebuffer_scpt(b);
      } else if (strcmp(s,"SGST") == 0) {
	r = parsebuffer_sgst(b);
      } else if (strcmp(s,"SKIL") == 0) {
	r = parsebuffer_skil(b);
      } else if (strcmp(s,"SLGM") == 0) {
	r = parsebuffer_slgm(b);
      } else if (strcmp(s,"SOUN") == 0) {
	r = parsebuffer_soun(b);
      } else if (strcmp(s,"SPEL") == 0) {
	r = parsebuffer_spel(b);
      } else if (strcmp(s,"STAT") == 0) {
	r = parsebuffer_stat(b);
      } else if (strcmp(s,"TES4") == 0) {
	r = parsebuffer_tes4(b);
      } else if (strcmp(s,"TREE") == 0) {
	r = parsebuffer_tree(b);
      } else if (strcmp(s,"WATR") == 0) {
	r = parsebuffer_watr(b);
      } else if (strcmp(s,"WEAP") == 0) {
	r = parsebuffer_weap(b);
      } else if (strcmp(s,"WRLD") == 0) {
	r = parsebuffer_wrld(b);
      } else if (strcmp(s,"WTHR") == 0) {
	r = parsebuffer_wthr(b);
      } else {
	printf("Record %s unimplemented\n",s);
	exit(-1);
	// tessnipsource/Records.cs lists additional records which are
	// not listed at UESP Wiki / Tes4Mod:Mod_File_Format.
	// NONE
	// SNDG
	// TLOD
	// TOFT
      }
      if (r == 0) {
	printf("parsebuffer_ error\n");
	exit(-1);
      }
    }
    free(s);
  }
  global_grouplevel--;
}


int main(int ac, char **av)
{
  void *m;
  unsigned char *buffer;
  int buflen,n,i;
  void *b;
  char *s;
  float f;
  int hierarchy;
  int loc;

#if 0
  if (ac < 2) {
    fprintf(stderr,"Usage: espparser <file> <targetdir>\n");
    exit(-1);
  }
#else
  if (ac < 1) {
    fprintf(stderr,"Usage: espparser <file>\n");
    exit(-1);
  }
#endif

  m = mmapfile_open_r(av[1]);
  buffer = mmapfile_getbuf(m);
  buflen = mmapfile_getsize(m);

  b = buffer_new(buffer,buflen);

  global_locn = 0;
  global_loc[0] = 0;

  parsebuffer(b);

  buffer_free(b);

  return 1;
}
