talker/
talker/bin/
talker/files/whois/
talker/update/
talker/update/bin/
/*
 * Rooms.c
 */

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <memory.h>
#include <string.h>

#include "config.h"
#include "player.h"
#include "fix.h"
#include "dynamic.h"


/* Our Extern Functions */

extern void dynamic_free(dfile *df,int key);
extern dfile *dynamic_init(char *file,int granularity);
extern int dynamic_load(dfile *df,int key,char *data);
extern int dynamic_save(dfile *df,char *data,int l,int key);
extern char *end_string(char *str);
extern saved_player *find_saved_player(char *name);
extern char *get_int(int *dest, char *source);
extern char *get_string(char *dest, char *source);
extern file load_file(char *filename);
extern int load_player(player * p);
extern void log(char *file, char *string);
extern int restore_player(player * p, char *name);
extern void save_player(player * p);
extern char *store_int(char *dest, int source);
extern char *store_string(char *dest, char *source);
extern void init_your_rooms(void);

/* Local Function Prototypes */

void delete_room(player * p, char *str); 

/* Local Variables */

dfile *room_df;

void            delete_room(player * p, char *str)
{
   char           *oldstack;
   room           *scan, **previous, *r;
   oldstack = stack;
#ifdef ROOM_TRACK
   printf("delete_room(%s, %s)\n", p->lower_name, str);
#endif
   if (!current_room)
   {
      return;
   }
   if (!strcmp("system", current_room->owner->lower_name) &&
       !strcmp("void", current_room->id))
     return;
   r = current_room;
   while (r->players_top)
   {}
   previous = &(r->owner->rooms);
   scan = *previous;
   for (; scan && scan != r; scan = scan->next)
      previous = &(scan->next);
   if (scan)
   {
      *previous = scan->next;
      if (scan->text.where)
    FREE(scan->text.where);
      if (scan->exits.where)
    FREE(scan->exits.where);
      if (scan->automessage.where)
    FREE(scan->automessage.where);
      dynamic_free(room_df,scan->data_key);
      FREE(scan);
   } else
      *previous = 0;
   stack = oldstack;
}

/* creates a new standard room */

room *create_room(player * p)
{
  room *r, *scan;
  saved_player   *sp;
  int             number, home = 1, unique = 1;
  char            id[MAX_ID];
#ifdef ROOM_TRACK
  printf("create_room(%s)\n", p->lower_name);
#endif
  sp = p->saved;
  if (!sp)
  {
    return 0;
  }
  sprintf(id, "room.%d", unique);
  do
  {
    for (scan = sp->rooms, number = 1; scan; number++, scan = scan->next)
    {
      if (!strcmp(scan->id, id))
      {
	unique++;
	sprintf(id, "room.%d", unique);
	break;
      }
      if (scan->flags & HOME_ROOM)
	home = 0;
    }
  } while (scan);
  
  if (number > p->max_rooms)
    return 0;
  r = (room *) MALLOC(sizeof(room));
  memset(r, 0, sizeof(room));
  strcpy(r->id, id);
  sprintf(r->name, "in somewhere belonging to %s.", p->name);
  sprintf(r->enter_msg, "goes to a room belonging to %s.", p->name);
  r->flags = (home * HOME_ROOM)|ROOM_UPDATED;
  sprintf(stack, "\n A bare room, belonging to %s.\n"
	  " Isn't it time to write a description ?\n", p->name);
  number = strlen(stack) + 1;
  r->text.where = (char *) MALLOC(number);
  memcpy(r->text.where, stack, number);
  r->text.length = number;
  r->exits.length = 0;
  r->exits.where = 0;
  r->auto_base = 30;
  r->automessage.length = 0;
  r->automessage.where = 0;
  r->owner = sp;
  r->players_top = 0;
  r->next = sp->rooms;
  sp->rooms = r;
  r->flags |= ROOM_UPDATED;
  return r;
}


/* change decompress room to get from disk */

int decompress_room(room *r)
{
  int length;
  char *oldstack,*tmp;
#ifdef ROOM_TRACK
  printf("decompress_room(%s)\n", r->id);
#endif
  oldstack=stack;
  if (!(r->flags&COMPRESSED)) return 1;
  
  length=dynamic_load(room_df,r->data_key,stack);
  if (length<=0) {
    current_room=r;
    delete_room(current_player,0);
    return 0;
  }
  tmp=stack;
  stack+=length;
  
  tmp=get_string(stack,tmp);
  length=strlen(stack)+1;
  r->text.length=length;
  if (r->text.where) FREE(r->text.where);
  r->text.where=(char *)MALLOC(length);
  strcpy(r->text.where,stack);
  
  tmp=get_string(stack,tmp);
  length=strlen(stack)+1;
  if (length==1) {
    r->exits.length=0;
    r->exits.where=0;
  }
  else {
    r->exits.length=length;
    if (r->exits.where) FREE(r->exits.where);
    r->exits.where=(char *)MALLOC(length);
    strcpy(r->exits.where,stack);
  }
  tmp=get_string(stack,tmp);
  length=strlen(stack)+1;
  if (length==1) {
    r->automessage.length=0;
    r->automessage.where=0;
  }
  else {
    r->automessage.length=length;
    if (r->automessage.where) FREE(r->automessage.where);
    r->automessage.where=(char *)MALLOC(length);
    strcpy(r->automessage.where,stack);
  }
  r->flags &= ~(COMPRESSED|ROOM_UPDATED);
  stack=oldstack;
  return 1;
}


/* and the compress room chugs to the disk */

void compress_room(room *r)
{
  int length;
  char *oldstack;
  oldstack=stack;

#ifdef ROOM_TRACK
  printf("compress_room(%s)\n", r->id);
#endif
  if (r->flags&COMPRESSED) return;
  if (r->owner && r->owner->residency==STANDARD_ROOMS) return;
/*  printf("compress key = %d, id = %s\n",r->data_key,r->id); */
  if (r->flags&ROOM_UPDATED) {
    if (r->text.where) stack=store_string(stack,r->text.where);
    else stack=store_string(stack,"");
    if (r->exits.where) stack=store_string(stack,r->exits.where);
    else stack=store_string(stack,"");
    if (r->automessage.where) stack=store_string(stack,r->automessage.where);
    else stack=store_string(stack,"");
    length=(int)stack-(int)oldstack;
    r->data_key=dynamic_save(room_df,oldstack,length,r->data_key);
  }
  if (r->text.where) FREE(r->text.where);
  r->text.where=0;
  r->text.length=0;
  if (r->exits.where) FREE(r->exits.where);
  r->exits.where=0;
  r->exits.length=0;
  if (r->automessage.where) FREE(r->automessage.where);
  r->automessage.where=0;
  r->automessage.length=0;
  r->flags |= COMPRESSED;
  r->flags &= ~ROOM_UPDATED;
  stack=oldstack;
 }






 /* destroy all the room data */

void free_room_data(saved_player *sp)
{
  room *room_list,*next;
#ifdef ROOM_TRACK
  printf("free_room_data(%s)\n", sp->lower_name);
#endif
  if (!sp->rooms) return;
   room_list=sp->rooms;
   while(room_list) {
     next=room_list->next;
     if (room_list->text.where) FREE(room_list->text.where);
     if (room_list->exits.where)  FREE(room_list->exits.where);
     if (room_list->automessage.where)  FREE(room_list->automessage.where);
     dynamic_free(room_df,room_list->data_key);
     FREE(room_list);
     room_list=next;
   }
   sp->rooms=0;
}

/* collect all the room data ready for saving */

void construct_room_save(saved_player *sp)
{
   room *room_list;
   char *tmpstack;
   int length;
#ifdef ROOM_TRACK
   printf("construct_room_save(%s)\n", sp->lower_name);
#endif
   for(room_list=sp->rooms;room_list;room_list=room_list->next) {
     if (!room_list->players_top || (sys_flags&(SHUTDOWN|PANIC)))
       compress_room(room_list);
     tmpstack=stack;
     stack+=4;
     stack=store_string(stack,room_list->name);
     stack=store_string(stack,room_list->id);
     stack=store_string(stack,room_list->enter_msg);
     stack=store_int(stack,room_list->auto_base);
     stack=store_int(stack,room_list->flags);
     stack=store_int(stack,room_list->data_key);
     length=(int)stack-(int)tmpstack;
     (void) store_int(tmpstack,length);
   }
   stack=store_int(stack,0);
}

/* retrieve room data from a save file */

char *retrieve_room_data(saved_player *sp,char *where)
{
  int length;
  room *r,**last;
#ifdef ROOM_TRACK
  printf("retrieve_room_data(%s, %s)\n", sp->lower_name, where);
#endif
  free_room_data(sp);
  last=&sp->rooms;
  where=get_int(&length,where);
  for(;length;where=get_int(&length,where))
  {
    r=(room *)MALLOC(sizeof(room));
    memset(r,0,sizeof(room));
    where=get_string(r->name,where);
    where=get_string(r->id,where);
    where=get_string(r->enter_msg,where);
    where=get_int(&r->auto_base,where);
    where=get_int(&r->flags,where);
    where=get_int(&r->data_key,where);
    r->flags|=COMPRESSED;
    *last=r;
    last=&r->next;
    r->owner=sp;
    r->players_top=0;
    r->next=0;
  }
  *last=0;
  return where;
}
 
/* convert a room string into an actual room */

room *convert_room_verbose(player *p,char *str,int verbose)
{
  char *scan,*oldstack;
  saved_player *sp;
  room *r;
#ifdef ROOM_TRACK
  printf("convert_room_verbose(%s, %s, v:%d)\n",
	 p->lower_name, str, verbose);
#endif
  oldstack=stack;
  scan=str;
  while(*scan && *scan!='.')
    *stack++=*scan++;
  if (!*scan)
  {
    stack=oldstack;
    return 0;
  }
  *stack++=0;
  scan++;
  if (!*oldstack)
    strcpy(oldstack,p->name);
  lower_case(oldstack);
  sp=find_saved_player(oldstack);
  if (!sp)
  {
    stack=oldstack;
    return 0;
  }
  strcpy(oldstack,scan);
  lower_case(oldstack);
  stack=end_string(oldstack);
  r=sp->rooms;
  while(r)
  {
    strcpy(stack,r->id);
    lower_case(stack);
    if (!strcmp(stack,oldstack))
    {
      stack=oldstack;
      current_room=r;
      if (!decompress_room(r))
	return 0;
      return r;
    }
    if (!sp)
    {
      stack=oldstack;
      return 0;
    }
    strcpy(oldstack,scan);
    lower_case(oldstack);
    stack=end_string(oldstack);
    r=sp->rooms;
    while(r)
    {
      strcpy(stack,r->id);
      lower_case(stack);
      if (!strcmp(stack,oldstack))
      {
	stack=oldstack;
	current_room=r;
	if (!decompress_room(r))
	  return 0;
	return r;
      }
      r=r->next;
    }
    stack=oldstack;
    return 0;
  }
  return r;
}

room           *convert_room(player *p, char *str)
{
   return convert_room_verbose(p, str, 1);
}

/* load in the standard rooms and initialise */

char           *get_line(file * f)
{
   char           *start;
#ifdef ROOM_TRACK
   printf("get_line(%s)\n", f->where);
#endif
   while (*(f->where) == '#')
   {
      while ((f->length > 0) && *(f->where) != '\n')
      {
    f->where++;
    f->length--;
      }
      if (f->length == 0)
    return 0;
      f->where++;
      f->length--;
   }
   start = f->where;
   while (*(f->where) && *(f->where) != '\n' && *(f->where) != '\r')
   {
      f->where++;
      f->length--;
   }
   if (*(f->where))
   {
      *(f->where)++ = 0;
      f->length--;
   }
   if (*(f->where) == '\r' || *(f->where) == '\n')
   {
      *(f->where)++ = 0;
      f->length--;
   }
   return start;
}

void init_room(char *name, file rf)
{
   char *line, *oldstack, *file_start;
   saved_player *sp;
   room *r;
   player np;
#ifdef ROOM_TRACK
   printf("init_room(%s, %s)\n", name, rf.where);
#endif
   oldstack = stack;

   /* if (!malloc_verify()) printf("wrong HERE 1\n"); */

   file_start = rf.where;

   sp = find_saved_player(name);
   if (!sp)
   {
      memset(&np, 0, sizeof(player));
      np.fd = 0;
      np.location = (room *) - 1;
      restore_player(&np, name);
      np.residency = SYSTEM_ROOM;
      np.saved_residency = SYSTEM_ROOM;
      np.email[0] = -1;
      np.password[0] = -1;
      np.max_exits=30;
      np.max_rooms=30;
      np.max_autos=30;
      np.max_list=1;
      save_player(&np);
      sp = find_saved_player(name);
      sp->list_top = 0;
   }

   sp->residency=SYSTEM_ROOM;
   sp->rooms = 0;

   /* if (!malloc_verify()) printf("wrong HERE 2\n"); */

   while (rf.length > 0)
   {
      line = get_line(&rf);
      if (rf.length <= 0 || !(*line))
    break;
      r = (room *) MALLOC(sizeof(room));
      memset(r, 0, sizeof(room));
      r->owner = sp;
      r->players_top = 0;
      r->next = sp->rooms;
      sp->rooms = r;
      strncpy(r->id, line, MAX_ID - 2);
      line = get_line(&rf);
      strncpy(r->name, line, MAX_ROOM_NAME - 2);
      line = get_line(&rf);
      strncpy(r->enter_msg, line, MAX_ENTER_MSG - 2);
      r->flags = OPEN | ROOM_UPDATED;

      while (*(rf.where) != '#')
    if (*(rf.where) != '\r')
    {
       *stack++ = *rf.where++;
       rf.length--;
    } else
    {
       rf.where++;
       rf.length--;
    }
      *stack++ = 0;
      r->text.length = strlen(oldstack) + 1;
      r->text.where = (char *) MALLOC(r->text.length);
      strcpy(r->text.where, oldstack);
      stack = oldstack;
      line = get_line(&rf);
      while (strcasecmp(line, "end"))
      {
    while (*line)
       *stack++ = *line++;
    *stack++ = '\n';
    line = get_line(&rf);
      }
      *stack++ = 0;
      r->exits.length = strlen(oldstack) + 1;
      if (r->exits.length == 1)
      {
    r->exits.where = 0;
    r->exits.length = 0;
      } else
      {
    r->exits.where = (char *) MALLOC(r->exits.length);
    strcpy(r->exits.where, oldstack);
      }
      stack = oldstack;

      line = get_line(&rf);
      while (strcasecmp(line, "end"))
      {
    while (*line)
       *stack++ = *line++;
    *stack++ = '\n';
    line = get_line(&rf);
      }
      *stack++ = 0;
      r->automessage.length = strlen(oldstack) + 1;
      if (r->automessage.length == 1)
      {
    r->automessage.where = 0;
    r->automessage.length = 0;
      } else
      {
    r->automessage.where = (char *) MALLOC(r->automessage.length);
    strcpy(r->automessage.where, oldstack);
    r->flags |= AUTO_MESSAGE;
      }
      stack = oldstack;
   }
   
   /* if (!malloc_verify()) printf("wrong HERE 3\n"); */
   FREE(file_start);
   stack = oldstack;
}

void init_rooms(void)
{
  room_df=dynamic_init("rooms", 256);
  init_your_rooms();
}