/**************************************************************************
*  file: SYNTAX_CHECKER.c , Check syntax of all files     Part of DIKUMUD *
*  Usage: QUICK AND DIRTY!!                                               *
*  Copyright (C) 1990, 1991 - see 'license.doc' for complete information. *
***************************************************************************/

#include "os.h"

#include "structs.h"
#include "utils.h"
#include "db.h"
#include "comm.h"
#include "handler.h"
#include "limits.h"

/**************************************************************************
*  declarations of most of the 'global' variables                         *
************************************************************************ */

struct room_data *world;        /* dyn alloc'ed array of rooms     */
int top_of_world = 0;           /* ref to the top element of world */
struct obj_data *object_list = 0;       /* the global linked list of obj's */
struct char_data *character_list = 0;   /* global l-list of chars          */

struct zone_data *zone_table;   /* table of reset data             */
int top_of_zone_table = 0;
struct message_list fight_messages[MAX_MESSAGES];       /* fighting messages   */
struct player_index_element *player_table = 0;  /* index to player file   */
int top_of_p_table = 0;         /* ref to top of table             */
int top_of_p_file = 0;

FILE *mob_f,                    /* file containing mob prototypes  */
 *obj_f,                        /* obj prototypes                  */
 *wld_f,                        /* World file                      */
 *zon_f;

struct index_data *mob_index;   /* index table for mobile file     */
struct index_data *obj_index;   /* index table for object file     */
struct index_data *wld_index;

struct help_index_element *help_index = 0;

int top_of_mobt = 0;            /* top of mobile index table       */
int top_of_objt = 0;            /* top of object index table       */
int top_of_wldt = 0;

struct time_data time_info;     /* the infomation about the time   */
struct weather_data weather_info;       /* the infomation about the weather */




/* local procedures */
void boot_zones (void);
void setup_dir (FILE * fl, int room, int dir);
void allocate_room (int new_top);
void boot_world (void);
struct index_data *generate_indices (FILE * fl, int *top);
void build_player_index (void);
void char_to_store (struct char_data *ch, struct char_file_u *st);
void store_to_char (struct char_file_u *st, struct char_data *ch);
int is_empty (int zone_nr);
void reset_zone (int zone);
int file_to_string (char *name, char *buf);
void renum_world (void);
void renum_zone_table (void);
void reset_time (void);
void clear_char (struct char_data *ch);


/*************************************************************************
*  routines for booting the system                                       *
*********************************************************************** */


void assume (int faktisk, int antal, int place, char *errmsg)
{
  if (antal != faktisk) {
    printf ("Error has occured at #%d.\n\r", place);
    printf ("Message is : %s\n\r", errmsg);
    printf ("Actual number read is %d\n\r", faktisk);
    exit (1);
  }
}


/* generate index table for object, monster or world file*/
struct index_data *generate_indices (FILE * fl, int *top)
{
  int i = 0, antal;
  struct index_data *index = NULL;
  long pos;
  char buf[82];

  rewind (fl);

  for (;;) {
    if (FGETS (buf, 81, fl)) {
      if (*buf == '#') {
        /* allocate new cell */

        if (!i)                 /* first cell */
          CREATE (index, struct index_data, 1);
        else
          if (!(index =
            (struct index_data *) realloc (index,
              (i + 1) * sizeof (struct index_data)))) {
          printf ("load indices");
          exit (1);
        }
        antal = sscanf (buf, "#%d", &index[i].virtual);
        assume (antal, 1, index[i].virtual, "Next string with E/A/$");

        index[i].pos = ftell (fl);
        index[i].number = index[i].virtual;
        index[i].func = 0;
        i++;
      } else if (*buf == '$')   /* EOF */
        break;
    } else {
      printf ("Error when generating index, based upon #xxxx numbers.\n\r");
      printf ("   Probably error at end of file.\n\r");

      exit (1);
    }
  }
  index[i - 1].number = -1;
  *top = i - 1;
  return (index);
}

int exist_index (struct index_data *index_list, int top, int num)
{
  int i, found;

  found = FALSE;

  for (i = 0; (i <= top) && !(found); i++)
    if (index_list[i].number == num)
      found = TRUE;

  if (!found) {
    printf ("Reference to non-existent number #%d\n\r", num);
  }

  return (found);
}


/* check the rooms */
void check_world (FILE * fl)
{
  int room_nr = 0, zone = 0, dir_nr, virtual_nr, flag, tmp, old_virtual;
  char *temp, chk[50];
  struct extra_descr_data *new_descr;

  int antal;
  char *temp2;

  world = 0;
  character_list = 0;
  object_list = 0;

  rewind (fl);

  old_virtual = -1;


  do {
    antal = fscanf (fl, " #%d\n", &virtual_nr);
    assume (antal, 1, virtual_nr, "Reading #xxx");

    if (old_virtual > virtual_nr)
      assume (0, 1, virtual_nr, "Error - #'s not in order.");
    old_virtual = virtual_nr;

    temp = fread_string (fl);
    if (flag = (*temp != '$')) {        /* a new record to be read */
      temp2 = fread_string (fl);

      antal = fscanf (fl, " %d ", &tmp);
      assume (antal, 1, virtual_nr, "In room basic 3 numbers");

      antal = fscanf (fl, " %d ", &tmp);
      assume (antal, 1, virtual_nr, "In room basic 3 numbers");

      antal = fscanf (fl, " %d ", &tmp);
      assume (antal, 1, virtual_nr, "In room basic 3 numbers");

      for (;;) {
        antal = fscanf (fl, " %s \n", chk);
        assume (antal, 1, virtual_nr, "Reading D/E/S string");

        if (*chk == 'D')        /* direction field */
          setup_dir (fl, virtual_nr, atoi (chk + 1));
        else if (*chk == 'E') { /* extra description field */
          temp2 = fread_string (fl);    /* Description */
          temp2 = fread_string (fl);    /* Keywords    */
        } else if (*chk == 'S') /* end of current room */
          break;
        else
          assume (FALSE, 0, virtual_nr, "MISSING D/E or S");
      }

    }
  }
  while (flag);
}




/* read direction data */
void setup_dir (FILE * fl, int room, int dir)
{
  int tmp, antal;
  char *temp;

  temp = fread_string (fl);
  temp = fread_string (fl);

  antal = fscanf (fl, " %d ", &tmp);
  assume (antal, 1, room, "One of three Direction data");
  antal = fscanf (fl, " %d ", &tmp);
  assume (antal, 1, room, "One of three Direction data");
  antal = fscanf (fl, " %d ", &tmp);
  assume (antal, 1, room, "One of three Direction data");
  exist_index (wld_index, top_of_wldt, tmp);

}


#define NEW_ZONE_SYSTEM xxx

/* load the zone table and command tables */
void check_zones (FILE * fl)
{
  int line_no;
  int antal, tmp1, tmp2, tmp3, tmp4;
  int zon = 0, cmd_no = 0, ch, expand;
  char *check, buf[81];
  char cmd_type;

  rewind (fl);
  line_no = 1;

  for (;;) {
    antal = fscanf (fl, " #%*d\n");
    assume (antal, 0, line_no++, "Zone number not found");

    check = fread_string (fl);
    line_no++;

    if (*check == '$')
      break;                    /* end of file */

    /* alloc a new zone */

#ifdef NEW_ZONE_SYSTEM
    antal = fscanf (fl, " %d ", &zon);
    assume (antal, 1, line_no, "Zone Room < number not found");
#endif

    antal = fscanf (fl, " %d ", &zon);
    assume (antal, 1, line_no, "Life Span");

    antal = fscanf (fl, " %d ", &zon);
    assume (antal, 1, line_no++, "Reset Mode");


    /* read the command table */

    cmd_no = 0;

    for (expand = 1;;) {

      fscanf (fl, " ");         /* skip blanks */
      antal = fscanf (fl, "%c", &cmd_type);
      assume (antal, 1, line_no, "Command type M/*/O/G/E/S missing");

      if (cmd_type == 'S')
        break;

      if (cmd_type == '*') {
        expand = 0;
        FGETS (buf, 80, fl);    /* skip command */
        line_no++;
        continue;
      }

      antal = fscanf (fl, " %d %d %d", &tmp1, &tmp2, &tmp3);
      assume (antal, 3, line_no, "Three values after command missing");


      if (cmd_type == 'M' || cmd_type == 'O' ||
        cmd_type == 'D' || cmd_type == 'P') {
        antal = fscanf (fl, " %d", &tmp4);
        assume (antal, 1, line_no, "Fourth value after command missing");
      }

      switch (cmd_type) {
      case 'M':
        exist_index (mob_index, top_of_mobt, tmp2);
        exist_index (wld_index, top_of_wldt, tmp4);
        break;
      case 'O':
        exist_index (obj_index, top_of_objt, tmp2);
        exist_index (wld_index, top_of_wldt, tmp4);
        break;
      case 'G':
        exist_index (obj_index, top_of_objt, tmp2);
        break;
      case 'E':
        exist_index (obj_index, top_of_objt, tmp2);
        break;
      case 'P':
        exist_index (obj_index, top_of_objt, tmp2);
        exist_index (obj_index, top_of_objt, tmp4);
        break;
      case 'D':
        exist_index (wld_index, top_of_wldt, tmp2);
        break;
      case 'R':
        exist_index (wld_index, top_of_wldt, tmp2);
        exist_index (obj_index, top_of_objt, tmp3);
        break;
      case '*':
        break;
      deafult:
        printf ("Illegal command type");
        exit (1);
        break;
      }


      FGETS (buf, 80, fl);      /* read comment */
      line_no++;
    }
  }
}





/*************************************************************************
*  procedures for resetting, both play-time and boot-time    *
*********************************************************************** */


/* read a mobile from MOB_FILE */
void check_mobile (FILE * fl)
{
  int virtual_nr, old_virtual, antal, flag;
  char *temp;
  char bogst;

  int i, skill_nr;
  long tmp, tmp2, tmp3;
  struct char_data *mob;
  char chk[10];


  old_virtual = -1;

  rewind (fl);

  do {
    antal = fscanf (fl, " #%d\n", &virtual_nr);
    assume (antal, 1, virtual_nr, "Reading #xxx");

    if (old_virtual > virtual_nr)
      assume (0, 1, virtual_nr, "Error - #'s not in order.");

    old_virtual = virtual_nr;

    temp = fread_string (fl);   /* Namelist */
    if (flag = (*temp != '$')) {        /* a new record to be read */

      /***** String data *** */
      /* Name already read mob->player.name = fread_string(fl); */
      temp = fread_string (fl); /* short description  */
      temp = fread_string (fl); /*long_description    */
      temp = fread_string (fl); /* player.description */

      /* *** Numeric data *** */

      antal = fscanf (fl, "%ld ", &tmp);
      assume (antal, 1, virtual_nr, "ACT error");

      antal = fscanf (fl, " %ld ", &tmp);
      assume (antal, 1, virtual_nr, "affected_by error");

      antal = fscanf (fl, " %ld ", &tmp);
      assume (antal, 1, virtual_nr, "Monster Alignment Error");

      antal = fscanf (fl, " %c \n", &bogst);
      assume (antal, 1, virtual_nr, "Simple/Detailed error");

      if (bogst != 'S')
        printf ("%c %d\n", bogst, bogst);

      if (bogst == 'S') {
        /* The new easy monsters */

        antal = fscanf (fl, " %ld ", &tmp);
        assume (antal, 1, virtual_nr, "Level error");

        antal = fscanf (fl, " %ld ", &tmp);
        assume (antal, 1, virtual_nr, "THAC0 error");

        antal = fscanf (fl, " %ld ", &tmp);
        assume (antal, 1, virtual_nr, "AC error");

        antal = fscanf (fl, " %ldd%ld+%ld ", &tmp, &tmp2, &tmp3);
        assume (antal, 3, virtual_nr, "Hitpoints");

        antal = fscanf (fl, " %ldd%ld+%ld \n", &tmp, &tmp2, &tmp3);
        assume (antal, 3, virtual_nr, "Damage error");

        antal = fscanf (fl, " %ld ", &tmp);
        assume (antal, 1, virtual_nr, "GOLD error");

        antal = fscanf (fl, " %ld \n", &tmp);
        assume (antal, 1, virtual_nr, "XP error");

        antal = fscanf (fl, " %ld ", &tmp);
        assume (antal, 1, virtual_nr, "POSITION error");

        antal = fscanf (fl, " %ld ", &tmp);
        assume (antal, 1, virtual_nr, "DEFAULT POS error");

        antal = fscanf (fl, " %ld \n", &tmp);
        assume (antal, 1, virtual_nr, "SEXY error");

      } else {                  /* The old monsters are down below here */

        printf ("Detailed monsters can't be syntax-checked (yet).\n\r");
        assume (0, 1, virtual_nr, "DETAIL ERROR");

        exit (1);
        /*   ***************************
           fscanf(fl, " %ld ", &tmp);
           mob->abilities.str = tmp;

           fscanf(fl, " %ld ", &tmp);
           mob->abilities.intel = tmp;

           fscanf(fl, " %ld ", &tmp);
           mob->abilities.wis = tmp;

           fscanf(fl, " %ld ", &tmp);
           mob->abilities.dex = tmp;

           fscanf(fl, " %ld \n", &tmp);
           mob->abilities.con = tmp;

           fscanf(fl, " %ld ", &tmp);
           fscanf(fl, " %ld ", &tmp2);

           mob->points.max_hit = 0;
           mob->points.hit = mob->points.max_hit;

           fscanf(fl, " %ld ", &tmp);
           mob->points.armor = tmp;

           fscanf(fl, " %ld ", &tmp);
           mob->points.mana = tmp;
           mob->points.max_mana = tmp;

           fscanf(fl, " %ld ", &tmp);
           mob->points.move = tmp;
           mob->points.max_move = tmp;

           fscanf(fl, " %ld ", &tmp);
           mob->points.gold = tmp;

           fscanf(fl, " %ld \n", &tmp);
           GET_EXP(mob) = tmp;

           fscanf(fl, " %ld ", &tmp);
           mob->specials.position = tmp;

           fscanf(fl, " %ld ", &tmp);
           mob->specials.default_pos = tmp;

           fscanf(fl, " %ld ", &tmp);
           mob->player.sex = tmp;

           fscanf(fl, " %ld ", &tmp);
           mob->player.class = tmp;

           fscanf(fl, " %ld ", &tmp);
           GET_LEVEL(mob) = tmp;

           fscanf(fl, " %ld ", &tmp);
           mob->player.birth.hours = time_info.hours;
           mob->player.birth.day = time_info.day;
           mob->player.birth.month = time_info.month;
           mob->player.birth.year  = time_info.year - tmp;

           fscanf(fl, " %ld ", &tmp);
           mob->player.weight = tmp;

           fscanf(fl, " %ld \n", &tmp);
           mob->player.height = tmp;

           for (i = 0; i < 3; i++)
           {
           fscanf(fl, " %ld ", &tmp);
           GET_COND(mob, i) = tmp;
           }
           fscanf(fl, " \n ");

           for (i = 0; i < 5; i++)
           {
           fscanf(fl, " %ld ", &tmp);
           mob->specials.apply_saving_throw[i] = tmp;
           }

           fscanf(fl, " \n ");
           mob->points.damroll = 0;
           mob->specials.damnodice = 1;
           mob->specials.damsizedice = 6;

           mob->points.hitroll = 0;
           ************************************* */
      }

    }
  }
  while (flag);

}


/* read an object from OBJ_FILE */
void check_objects (FILE * fl)
{
  int virtual_nr, old_virtual, antal, flag;
  char *temp;

  struct obj_data *obj;
  int tmp, i;
  char chk[256];
  struct extra_descr_data *new_descr;

  old_virtual = -1;
  virtual_nr = 0;

  rewind (fl);

  antal = fscanf (fl, " %s \n", chk);
  assume (antal, 1, virtual_nr, "First #xxx number");

  do {
    antal = sscanf (chk, " #%d\n", &virtual_nr);
    assume (antal, 1, virtual_nr, "Reading #xxx");

    if (old_virtual > virtual_nr)
      assume (0, 1, virtual_nr, "Error - #'s not in order.");

    old_virtual = virtual_nr;

    temp = fread_string (fl);   /* Namelist */
    if (flag = (*temp != '$')) {        /* a new record to be read */

      /* *** string data *** */

      /* temp = fread_string(fl);  name has been read above */
      temp = fread_string (fl); /* short */
      temp = fread_string (fl); /* descr */
      temp = fread_string (fl); /* action */

      /* *** numeric data *** */

      antal = fscanf (fl, " %d ", &tmp);
      assume (antal, 1, virtual_nr, "Error reading type flag");

      antal = fscanf (fl, " %d ", &tmp);
      assume (antal, 1, virtual_nr, "Extra Flag");

      antal = fscanf (fl, " %d ", &tmp);
      assume (antal, 1, virtual_nr, "wear_flags");

      antal = fscanf (fl, " %d ", &tmp);
      assume (antal, 1, virtual_nr, "value[0]");

      antal = fscanf (fl, " %d ", &tmp);
      assume (antal, 1, virtual_nr, "value[1]");

      antal = fscanf (fl, " %d ", &tmp);
      assume (antal, 1, virtual_nr, "value[2]");

      antal = fscanf (fl, " %d ", &tmp);
      assume (antal, 1, virtual_nr, "value[3]");

      antal = fscanf (fl, " %d ", &tmp);
      assume (antal, 1, virtual_nr, "Weight");

      antal = fscanf (fl, " %d \n", &tmp);
      assume (antal, 1, virtual_nr, "Cost");

      antal = fscanf (fl, " %d \n", &tmp);
      assume (antal, 1, virtual_nr, "Cost Per Day");

      /* *** extra descriptions *** */

      while (fscanf (fl, " %s \n", chk), *chk == 'E') {

        temp = fread_string (fl);
        temp = fread_string (fl);
      }

      for (i = 0; (i < MAX_OBJ_AFFECT) && (*chk == 'A'); i++) {
        antal = fscanf (fl, " %d ", &tmp);
        assume (antal, 1, virtual_nr, "affected location");

        antal = fscanf (fl, " %d \n", &tmp);
        assume (antal, 1, virtual_nr, "Modifier");

        antal = fscanf (fl, " %s \n", chk);
        assume (antal, 1, virtual_nr, "Next string with E/A/$");

      }
    }
  }
  while (flag);
}




/************************************************************************
*  procs of a (more or less) general utility nature     *
********************************************************************** */


/* read and allocate space for a '~'-terminated string from a given file */
char *fread_string (FILE * fl)
{
  static char buf[MAX_STRING_LENGTH], tmp[100];
  char *rslt;
  register char *point;
  int flag;

  bzero (buf, MAX_STRING_LENGTH);

  do {
    if (!FGETS (tmp, MAX_STRING_LENGTH, fl)) {
      printf ("fread_str");
      exit (1);
    }

    if (strlen (tmp) + strlen (buf) > MAX_STRING_LENGTH) {
      printf ("fread_string: string too large (db.c, fread_string)");
      exit (1);
    } else
      strcat (buf, tmp);

    for (point = buf + strlen (buf) - 2; point >= buf && isspace ((int)*point);
      point--);
    if (flag = (*point == '~'))
      if (*(buf + strlen (buf) - 3) == '\n') {
        *(buf + strlen (buf) - 2) = '\r';
        *(buf + strlen (buf) - 1) = '\0';
      } else
        *(buf + strlen (buf) - 2) = '\0';
    else {
      *(buf + strlen (buf) + 1) = '\0';
      *(buf + strlen (buf)) = '\r';
    }
  }
  while (!flag);

  return (buf);
}


int main (int argc, char *argv[])
{

  char name[256];

  if (argc != 2) {
    printf ("Usage : syntax_check <BaseFileName>\n\r");
    exit (0);
  }

  strcpy (name, argv[1]);
  strcat (name, ".wld");

  if (!(wld_f = fopen (name, "rb"))) {
    printf ("Could not open world file.\n\r");
    exit (1);
  }
  strcpy (name, argv[1]);
  strcat (name, ".mob");
  if (!(mob_f = fopen (name, "rb"))) {
    printf ("Could not open mobile file.\n\r");
    exit (1);
  }
  strcpy (name, argv[1]);
  strcat (name, ".obj");
  if (!(obj_f = fopen (name, "rb"))) {
    printf ("Could not open object file.\n\r");
    exit (1);
  }
  strcpy (name, argv[1]);
  strcat (name, ".zon");
  if (!(zon_f = fopen (name, "rb"))) {
    printf ("Could not open zone file.\n\r");
    exit (1);
  }


  printf ("Generating world file indexes.\n\r");
  wld_index = generate_indices (wld_f, &top_of_wldt);

  printf ("Generating mobile file indexes.\n\r");
  mob_index = generate_indices (mob_f, &top_of_mobt);

  printf ("Generating object file indexes.\n\r");
  obj_index = generate_indices (obj_f, &top_of_objt);

  printf ("Checking World File\n\r");
  check_world (wld_f);

  printf ("Checking Mobile File (only simple mobiles).\n\r");
  check_mobile (mob_f);

  printf ("Checking Object File.\n\r");
  check_objects (obj_f);

  printf ("Checking Zone File .\n\r");
  check_zones (zon_f);

  printf
    ("\n\r\nCheck successfully completed without any obvious errors.\n\r");

  fclose (zon_f);
  fclose (wld_f);
  fclose (mob_f);
  fclose (obj_f);
  return 0;
}