/*
    dictutil.c

    UltraRogue: The Ultimate Adventure in the Dungeons of Doom
    Copyright (C) 1995 Herb Chong
    All rights reserved.

    See the file LICENSE.TXT for full copyright and licensing information.
*/

/*************************************************************************
**   Utilities for Dictionary Maintenence Functions
*************************************************************************/

static char sccsid[] = "%W% %G%";

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#if !defined(OS2) && !defined(_WIN32)
   #include <unistd.h>
#else
   #include <io.h>
   #include <fcntl.h>
#endif

#include "dict.h"
#include "dictutil.h"
#include "rogue.h"

int   dict_trace;
FILE  *ft;


/***********
**  Read 'count' characters into 'buffer' at 'offset' in a binary file
**  Return 0 on success; -1 on failure;
***********/

int block_read( FILE *fi , char *buffer , size_t count , long offset )
{
   if ( fseek(fi,offset,SEEK_SET) == -1 )
      return( -1 );

   if ( fread(buffer,1,count,fi) != count )
      return( -1 );
   return( 0 );
}

/***********
**  Write 'count' characters from 'buffer' to a binary file.
**  Return -1 on failure; 0 on success.
***********/

int block_write( FILE *fo , char *buffer , size_t count )
{
   if ( fwrite(buffer,1,count,fo) != count )
      return( -1 );
   return( 0 );
}

/***********
**  Load a dictionary table entry with id TOC_id into memory pointed to by block.
**  Update the dictionary TOC.
**  If *block=NULL, allocate the block of memory.
**  Return 0 on success; -1 on failure.
**  Set dt_entry->ptr to where the block is stored.
***********/

void *dict_load_block( DICTIONARY *dict , char *toc_id ,
                          FILE *fi , void *block )
{  DICT_TOC_ENTRY  *dt_entry;
   static void     *ptr;
   int             index, ret_code;

   index = dict_toc_index( dict , toc_id );
   if ( index != -1 ) {  /* Found the id */
      dt_entry = &(dict->toc[index]);
   } else {
      signal_error( "dict_load_block: could not find TOC_id" , toc_id , 1 );
      return( NULL );
   } /* endif */

   if ( block == NULL ) {
      ptr = malloc( dt_entry->size );
      if ( dict_trace > 3 ) {
         fprintf( ft , "\ndict_load_block allocates %lx bytes at location %p\n" ,
                   dt_entry->size , ptr );
      } /* endif */
   } else {
      ptr = block;
      if ( dict_trace > 3 ) {
         fprintf( ft , "\ndict_load_block uses memory at location %p\n" , ptr );
      } /* endif */
   } /* endif */
   if ( ptr == NULL ) {
      signal_error( "dict_load_block: alloc failed " , toc_id , 1 );
      return( NULL );
   } /* endif */

   ret_code = block_read( fi ,
                          (char*)ptr ,
                          dt_entry->size ,
                          dt_entry->offset );
   if ( ret_code == -1 )
      return( NULL );

   if ( dt_entry->checksum !=
        compute_checksum( dt_entry->size , (char*)ptr ) ) {
      signal_error( "dict_load_block: invalid checksum ", toc_id, 1);
      return( NULL );
   } /* endif */

   dt_entry->ptr = ptr;

   if ( dict_trace > 3 ) {
      fprintf( ft , "\nLoaded block\nTOC entry:    id:%s  offset:%lx  size:%lx  ptr:%p  checksum:%lx  type:%d\n" ,
               dict->toc[index].id , dict->toc[index].offset ,
               dict->toc[index].size , dict->toc[index].ptr ,
               dict->toc[index].checksum , dict->toc[index].type );
   } /* endif */

   return( ptr );
}

/***********
**  Save a dictionary table entry.
**  Update the dictionary TOC entry offset and checksum fields.
**  Return 0 on success, -1 on failure.
**  Note: It is assumed that the size and pointer fields in TOC entry are
**        already up to date; i.e., that they are consistent with the current
**        location and size of the block being written. This is essential
**        because the table of contents must have already been written
**        into the file.
***********/

BOOLEANC dict_save_block( DICTIONARY *dict , char *toc_id , FILE *fo )
{  DICT_TOC_ENTRY   *dt_entry;
   int              index, ret_code;
   char             *block;

   index = dict_toc_index( dict , toc_id );
   if ( index == -1 ) {
      signal_error( "dict_save_block: id not found " , toc_id , 1 );
      return( FALSE );
   } /* endif */
   dt_entry = &(dict->toc[index]);
   block = (char*)(dt_entry->ptr);

   if ( block == NULL ) {
      signal_error( "dict_save_block: NULL block " , toc_id , 1 );
      return( FALSE );
   } /* endif */

   /* dt_entry->offset = fseek( fo , 0 , SEEK_END ); */
   dt_entry->checksum = compute_checksum( dt_entry->size , block );
   ret_code = block_write( fo , dt_entry->ptr , dt_entry->size );
   if ( ret_code == -1 ) {
      signal_error( "dict_save_block: block_write failed " , toc_id , 1 );
      return( FALSE );
   } /* endif */

   if ( dict_trace > 3 ) {
      fprintf( ft , "\nStored block\nTOC entry:           id:%s  offset:%lx  size:%lx  ptr:%p  checksum:%lx  type:%d\n" ,
               dict->toc[index].id , dict->toc[index].offset ,
               dict->toc[index].size , dict->toc[index].ptr ,
               dict->toc[index].checksum , dict->toc[index].type );
   } /* endif */

   return( TRUE );
}

/***********
**  Look up and id in the table of contents.
**  Return its index (-1 on failure).
***********/

int dict_toc_index( DICTIONARY *dict , char *toc_id )
{  int index;

   for ( index = 0 ; index < dict->sig->toc_size ; index++ ) {
           if ( strcmp(dict->toc[index].id,toc_id) == 0 )
                   return( index );
   } /* endfor */

   return( -1 );
}

/***********
**  Compute a block checksum.
**  (Currently just returns 0.)
***********/

unsigned long compute_checksum( size_t size , char *block )
{
    NOOP(size);
    NOOP(block);
    return( 0 );
}

/***********
**  Create a dictionary paramter entry.
***********/

DICT_PARM_ENTRY *dict_make_parm_entry( char *id , unsigned long value )
{  static DICT_PARM_ENTRY  *entry;

   entry = (DICT_PARM_ENTRY *) malloc( sizeof(DICT_PARM_ENTRY) );
   if ( entry == NULL )
      return(NULL);

   strncpy( entry->id , id , 13 );
   entry->value = value;

   return( entry );
}

/***********
**  Look up and id in the parameter array.
**  Return its index (-1 on failure).
***********/

int dict_parm_index( DICTIONARY *dict , char *parm_id )
{  long index;

   for ( index = 0 ; index < dict->sig->nparms ; index++ ) {
           if ( strcmp( dict->parm[index].id , parm_id ) == 0 )
                   return( (int) index );
   } /* endfor */

   return( -1 );
}

/***********
**  Reset table of contents offsets and checksums
**  in preparation for dict_save().
***********/

BOOLEANC dict_reset_toc_offsets( DICTIONARY *dict )
{  int  i;
   long offset;

   offset = sizeof(DICT_SIG)
          + dict->sig->toc_size * sizeof(DICT_TOC_ENTRY);
   for ( i = 0 ; i < dict->sig->toc_size ; i++ ) {
      dict->toc[i].offset = offset;
      offset += dict->toc[i].size;
      dict->toc[i].checksum =
         compute_checksum( dict->toc[i].size , dict->toc[i].ptr );
   } /* endfor */

   return( TRUE );
}

/***********
**  Load the names of the dictionary parameters.
**  14 parms
***********/

BOOLEANC dict_set_parm_ids( DICTIONARY *dict )
{
   if ( dict==NULL || dict->sig == NULL ) {
      signal_error( "dict_set_parm_ids: Allocate dict and signature first." , "" , 0 );
      return( FALSE );
   }
   dict->sig->nparms = 14;
   strcpy( dict->parm[0].id , "FLAGS_______" );
   strcpy( dict->parm[1].id , "ENTRY_COUNT_" );
   strcpy( dict->parm[2].id , "ARRAY_SIZE__" );
   strcpy( dict->parm[3].id , "ARRAY_USED__" );
   strcpy( dict->parm[4].id , "ARR_GROW_CT_" );
   strcpy( dict->parm[5].id , "STRING_MAX__" );
   strcpy( dict->parm[6].id , "STR_GROW_CT_" );
   strcpy( dict->parm[7].id , "LONG_CHAIN__" );
   strcpy( dict->parm[8].id , "ALLOW_CHAIN_" );
   strcpy( dict->parm[9].id , "HASH_TAB_SIZ" );
   strcpy( dict->parm[10].id , "HASH_MASK___" );
   strcpy( dict->parm[11].id , "HASH_GROW_CT" );
   strcpy( dict->parm[12].id , "CHECK_VALUE_" );
   strcpy( dict->parm[13].id , "SCAN_STR_IX_" );

   return( TRUE );
}

/***********
**  Set the dictionary parm structure from the values in the dict structure.
**  14 parms
***********/

BOOLEANC dict_set_parm_values( DICTIONARY *dict )
{  int  index;

   if ( (index=dict_parm_index(dict,"FLAGS_______")) == -1 )
      return( FALSE );
   dict->parm[index].value = (unsigned long)dict->flags;

   if ( (index=dict_parm_index(dict,"ENTRY_COUNT_")) == -1 )
      return( FALSE );
   dict->parm[index].value = (unsigned long)dict->entry_count;

   if ( (index=dict_parm_index(dict,"ARRAY_SIZE__")) == -1 )
      return( FALSE );
   dict->parm[index].value = (unsigned long)dict->array_size;

   if ( (index=dict_parm_index(dict,"ARRAY_USED__")) == -1 )
      return( FALSE );
   dict->parm[index].value = (unsigned long)dict->array_used;

   if ( (index=dict_parm_index(dict,"ARR_GROW_CT_")) == -1 )
      return( FALSE );
   dict->parm[index].value = (unsigned long)dict->array_growth_count;

   if ( (index=dict_parm_index(dict,"STRING_MAX__")) == -1 )
      return( FALSE );
   dict->parm[index].value = (unsigned long)dict->string_max;

   if ( (index=dict_parm_index(dict,"STR_GROW_CT_")) == -1 )
      return( FALSE );
   dict->parm[index].value = (unsigned long)dict->string_growth_count;

   if ( (index=dict_parm_index(dict,"LONG_CHAIN__")) == -1 )
      return( FALSE );
   dict->parm[index].value = (unsigned long)dict->longest_chain_length;

   if ( (index=dict_parm_index(dict,"ALLOW_CHAIN_")) == -1 )
      return( FALSE );
   dict->parm[index].value = (unsigned long)dict->allowable_chain_length;

   if ( (index=dict_parm_index(dict,"HASH_TAB_SIZ")) == -1 )
      return( FALSE );
   dict->parm[index].value = (unsigned long)dict->table_size;

   if ( (index=dict_parm_index(dict,"HASH_MASK___")) == -1 )
      return( FALSE );
   dict->parm[index].value = (unsigned long)dict->hash_mask;

   if ( (index=dict_parm_index(dict,"HASH_GROW_CT")) == -1 )
      return( FALSE );
   dict->parm[index].value = (unsigned long)dict->hash_growth_count;

   if ( (index=dict_parm_index(dict,"CHECK_VALUE_")) == -1 )
      return( FALSE );
   dict->parm[index].value = (unsigned long)dict->check_value;

   if ( (index=dict_parm_index(dict,"SCAN_STR_IX_")) == -1 )
      return( FALSE );
   dict->parm[index].value = (unsigned long)dict->scan_string_index;

   return( TRUE );
}


/***********
**  Set the values in the dict structure from the dictionary parm structure.
**  14 parms
***********/

BOOLEANC dict_set_parm_variables( DICTIONARY *dict )
{  int  index;

   if ( (index=dict_parm_index(dict,"FLAGS_______")) == -1 )
      return( FALSE );
   dict->flags = (unsigned long)dict->parm[index].value;

   if ( (index=dict_parm_index(dict,"ENTRY_COUNT_")) == -1 )
      return( FALSE );
   dict->entry_count = (long)dict->parm[index].value;

   if ( (index=dict_parm_index(dict,"ARRAY_SIZE__")) == -1 )
      return( FALSE );
   dict->array_size = (long)dict->parm[index].value;

   if ( (index=dict_parm_index(dict,"ARRAY_USED__")) == -1 )
      return( FALSE );
   dict->array_used = (long)dict->parm[index].value;

   if ( (index=dict_parm_index(dict,"ARR_GROW_CT_")) == -1 )
      return( FALSE );
   dict->array_growth_count = (int)dict->parm[index].value;

   if ( (index=dict_parm_index(dict,"STRING_MAX__")) == -1 )
      return( FALSE );
   dict->string_max = (long)dict->parm[index].value ;

   if ( (index=dict_parm_index(dict,"STR_GROW_CT_")) == -1 )
      return( FALSE );
   dict->string_growth_count = (int)dict->parm[index].value;

   if ( (index=dict_parm_index(dict,"LONG_CHAIN__")) == -1 )
      return( FALSE );
   dict->longest_chain_length = (int)dict->parm[index].value;

   if ( (index=dict_parm_index(dict,"ALLOW_CHAIN_")) == -1 )
      return( FALSE );
   dict->allowable_chain_length = (int)dict->parm[index].value;

   if ( (index=dict_parm_index(dict,"HASH_TAB_SIZ")) == -1 )
      return( FALSE );
   dict->table_size = (long)dict->parm[index].value;

   if ( (index=dict_parm_index(dict,"HASH_MASK___")) == -1 )
      return( FALSE );
   dict->hash_mask = (unsigned long)dict->parm[index].value;

   if ( (index=dict_parm_index(dict,"HASH_GROW_CT")) == -1 )
      return( FALSE );
   dict->hash_growth_count = (int)dict->parm[index].value;

   if ( (index=dict_parm_index(dict,"CHECK_VALUE_")) == -1 )
      return( FALSE );
   dict->check_value = (unsigned long)dict->parm[index].value;

   if ( (index=dict_parm_index(dict,"SCAN_STR_IX_")) == -1 )
      return( FALSE );
   dict->scan_string_index = (long)dict->parm[index].value;

   return( TRUE );
}

/***********
**  If dict_trace (global) > 0 , signal an error
**  If severity > 0 , abort
***********/

void signal_error( char *header , char *message , int severity )
{
  FILE *fpe;

  if ( dict_trace > 0 ) {
     printf( "%s: %s\n" , header , message );
     fpe = fopen( "ERROR.FIL" , "a" );
     fprintf( fpe , "\n%s: %s\n" , header , message );
     fclose( fpe );
  } /* endif */

  if ( severity > 0 )
     abort();
}
