/**************************************************************************
*  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 <stdio.h>
#include <string.h>
#include <ctype.h>
#include <time.h>

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


/* structure for the reset commands */
struct reset_com {
   char	command;   /* current command                      */

   bool if_flag;   /* if TRUE: exe only if preceding exe'd */
   int	arg1;       /*                                      */
   int	arg2;       /* Arguments to the command             */
   int	arg3;       /*                                      */

   /* 
	*  Commands:              *
	*  'M': Read a mobile     *
	*  'O': Read an object    *
	*  'G': Give obj to mob   *
	*  'P': Put obj in obj    *
	*  'G': Obj to char       *
	*  'E': Obj to char equip *
	*  'D': Set state of door *
	*/
};



/* zone definition structure. for the 'zone-table'   */
struct zone_data {
   char	*name;             /* name of this zone                  */
   int	lifespan;           /* how long between resets (minutes)  */
   int	age;                /* current age of this zone (minutes) */
   int	top;                /* upper limit for rooms in this zone */

   int	reset_mode;         /* conditions for reset (see below)   */
   int	number;		    /* virtual number of this zone	  */
   struct reset_com *cmd;  /* command table for reset	           */

   /*
	*  Reset mode:                              *
	*  0: Don't reset, and don't update age.    *
	*  1: Reset if no PC's are located in zone. *
	*  2: Just reset.                           *
   */
};




/* element in monster and object index-tables   */
struct index_data {
   int	virtual;    /* virtual number of this mob/obj           */
   int	number;     /* number of existing units of this mob/obj	*/
   long	pos;	    /* pos in file				*/
   int	(*func)();  /* special procedure for this mob/obj       */
};




/* for queueing zones for update   */
struct reset_q_element {
   int	zone_to_reset;            /* ref to zone_data */
   struct reset_q_element *next;
};



/* structure for the update queue     */
struct reset_q_type {
   struct reset_q_element *head;
   struct reset_q_element *tail;
} reset_q;



struct player_index_element {
   char	*name;
   int	nr;
};


struct help_index_element {
   char	*keyword;
   long	pos;
};

char	*fread_string(FILE *fl);



/**************************************************************************
*  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;

extern struct time_data time_info;	  /* the infomation about the time   */




/* 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();
   }
}


/* 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;
   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();
	    }
	    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();
      }
   }
   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();
	    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, "%d ", &tmp);
	 assume(antal, 1, virtual_nr, "ACT error");

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

	 antal = fscanf(fl, " %d ", &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, " %D ", &tmp);
	    assume(antal, 1, virtual_nr, "Level error");

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

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

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

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

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

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

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

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

	    antal = fscanf(fl, " %D \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();
	    /*   ***************************
			fscanf(fl, " %D ", &tmp);
			mob->abilities.str = tmp;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

			fscanf(fl, " %D ", &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, " %D ", &tmp);
			mob->player.weight = tmp;

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

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

			for (i = 0; i < 5; i++)
			{
				fscanf(fl, " %D ", &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;

   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();
      }

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

      for (point = buf + strlen(buf) - 2; point >= buf && isspace(*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: (from /lib/world dir) %s world-number\n\r", argv[0]);
      exit(0);
   }

   sprintf(name, "wld/%s.wld", argv[1]);
   if (!(wld_f = fopen(name, "r"))) {
      printf("Could not open world file.\n\r");
      exit();
   }

   sprintf(name, "mob/%s.mob", argv[1]);
   if (!(mob_f = fopen(name, "r"))) {
      printf("Could not open mobile file.\n\r");
      exit();
   }

   sprintf(name, "obj/%s.obj", argv[1]);
   if (!(obj_f = fopen(name, "r"))) {
      printf("Could not open object file.\n\r");
      exit();
   }

   sprintf(name, "zon/%s.zon", argv[1]);
   if (!(zon_f = fopen(name, "r"))) {
      printf("Could not open zone file.\n\r");
      exit();
   }


   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);
}


