daleken/
daleken/data/notes/
daleken/data/player/
daleken/data/system/poses/
daleken/doc/Homepage/images/
daleken/log/
/*___________________________________________________________________________*
   )()(			  DalekenMUD 1.12 (C) 2000			)()(
   `]['		       by Martin Thomson, Lee Brooks,			`]['
    ||		       Ken Herbert and David Jacques			 ||
    || ----------------------------------------------------------------- ||
    || Envy Diku Mud improvements copyright (C) 1994 by Michael Quan,	 ||
    || David Love, Guilherme 'Willie' Arnold, and Mitchell Tse.		 ||
    || Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael	 ||
    || Chastain, Michael Quan, and Mitchell Tse.			 ||
    || Original Diku Mud copyright (C) 1990, 1991			 ||
    || by Sebastian Hammer, Michael Seifert, Hans Henrik St{rfeldt,	 ||
    || Tom Madsen, and Katja Nyboe.					 ||
    || ----------------------------------------------------------------- ||
    || Any use of this software must follow the licenses of the		 ||
    || creators.  Much time and thought has gone into this software and	 ||
    || you are benefitting. We hope that you share your changes too.	 ||
    || What goes around, comes around.					 ||
    || ----------------------------------------------------------------- ||
    ||				    mccp.c				 ||
    || The Mud Client Compression Protocol implementation.		 ||
 *_/<>\_________________________________________________________________/<>\_*/

/*
 * mccp.c - support functions for mccp (the Mud Client Compression Protocol)
 *
 * see http://homepages.ihug.co.nz/~icecube/compress/ and README.mccp
 *
 * Copyright (c) 1999, Oliver Jowett <icecube@ihug.co.nz>.
 *
 * This code may be freely distributed and used if this copyright notice is
 * retained intact.
 */

/*
 * At the moment I have no way of having this compile with zlib on windows,
 * but the #ifdef's can't hurt much.
 */
#if defined( WIN32 )
# if defined( BCB )
#  include <windows.h>
# else
#  include <winsock.h>
# endif
# include <sys/timeb.h>         /* for _ftime( ), uses _timeb struct */
#else
# include <sys/time.h>
# include <unistd.h>
#endif
#include <errno.h>
#include <arpa/telnet.h>

#include "mud.h"

/*
 * This argument determines how much to compress data.  It should be between
 * 1 and 9, 9 being the most compression, 1 is fastest.  If you are concerned
 * more with processor usage, 6 is the default value used by zlib, this may
 * give some bonus to speed without a massive drop in compression.
 */
#define MCCP_LEVEL	9

const char compress_start_v1[] = { IAC, SB, TELOPT_COMPRESS, WILL, SE, '\0' };
const char compress_start_v2[] = { IAC, SB, TELOPT_COMPRESS2, IAC, SE, '\0' };

/*
 * Memory management - zlib uses these hooks to allocate and free memory
 * it needs
 */

void *zlib_alloc( void *opaque, unsigned int items, unsigned int size )
{
    return calloc( items, size );
}

void zlib_free( void *opaque, void *address )
{
    free( address );
}

/*
 * Begin compressing data on `desc'
 */
bool compress_start( DESCRIPTOR_DATA *desc, int ver )
{
    z_stream *s;

    if( desc->out_compress ) /* already compressing */
	return TRUE;

    /* allocate and init stream, buffer */
    s = (z_stream *)alloc_mem( sizeof( *s ) );
    desc->out_compress_buf = (unsigned char *)alloc_mem( COMPRESS_BUF_SIZE );

    s->next_in = NULL;
    s->avail_in = 0;

    s->next_out = desc->out_compress_buf;
    s->avail_out = COMPRESS_BUF_SIZE;

    s->zalloc = zlib_alloc;
    s->zfree  = zlib_free;
    s->opaque = NULL;

    if( deflateInit( s, MCCP_LEVEL ) != Z_OK )
    {
	/* problems with zlib, try to clean up */
	free_mem( desc->out_compress_buf, COMPRESS_BUF_SIZE );
	free_mem( s, sizeof( z_stream ) );
	return FALSE;
    }
    if( ver != 2 )
	write_to_descriptor( desc->descriptor, compress_start_v1,
			     strlen( compress_start_v1 ) );
    else
	write_to_descriptor( desc->descriptor, compress_start_v2,
			     strlen( compress_start_v2 ) );

    /* now we're compressing */
    desc->out_compress = s;
    desc->mccp_version = ver;
    return TRUE;
}

/* Cleanly shut down compression on `desc' */
bool compress_end( DESCRIPTOR_DATA *desc, int ver )
{
    unsigned char dummy[1];

    if( !desc->out_compress )
	return TRUE;

    if( desc->mccp_version != ver )
	return FALSE;

    desc->out_compress->avail_in = 0;
    desc->out_compress->next_in = dummy;

    /* No terminating signature is needed - receiver will get Z_STREAM_END */

    if( deflate( desc->out_compress, Z_FINISH ) != Z_STREAM_END )
	return FALSE;

    if( !write_to_descriptor( desc->descriptor, desc->out_compress_buf,
			      desc->out_compress->next_out - desc->out_compress_buf ) )
	return FALSE;

    deflateEnd( desc->out_compress );
    free_mem( desc->out_compress_buf, COMPRESS_BUF_SIZE );
    free_mem( desc->out_compress, sizeof( z_stream ) );
    desc->out_compress = NULL;
    desc->out_compress_buf = NULL;

    return TRUE;
}


/*
 * This code's purpose has changed to be that of converting the uncompressed
 * data in outbuf into compressed data that is stored in out_compress_buf.
 */
bool process_compressed( DESCRIPTOR_DATA *desc )
{
    z_stream *s = desc->out_compress;

    /* compress the data */
    if( desc->large_buffer )
	s->next_in = (unsigned char *)desc->large_buffer->str;
    else
	s->next_in = (unsigned char *)&desc->outbuf[0];
    s->avail_in = desc->outtop;
    s->avail_out = COMPRESS_BUF_SIZE - ( s->next_out - desc->out_compress_buf );

    while( s->avail_in > 0 && s->avail_out > 0 )
    {
	if( deflate( s, Z_SYNC_FLUSH ) != Z_OK )
	{
	    bug( "Bad compression on desc %d.", desc->descriptor );
	    return FALSE;
	}
	s->avail_out = COMPRESS_BUF_SIZE - ( s->next_out - desc->out_compress_buf );
    }

/*  if( s->next_out - desc->out_compress_buf > record )
    {
	record = s->next_out - desc->out_compress_buf;
	fprintf( stderr, "Compress used %d bytes of the buffer.\n",
	record );
    }
*/
    /* copy any uncompressed data back */
    if( desc->large_buffer )
    {
	if( s->avail_in >= SMALL_OUTBUF_SIZE - 1 )
	{
	    memcpy( desc->large_buffer->str,
		    desc->large_buffer->str + desc->outtop - s->avail_in,
		    s->avail_in + 1 );
	}
	else
	{
	    if( s->avail_in > 0 )
		memcpy( desc->outbuf,
			desc->large_buffer->str + desc->outtop - s->avail_in,
			s->avail_in + 1 );

	    free_text_block( desc->large_buffer );
	    desc->large_buffer = NULL;
	}
    }
    else if( s->avail_in > 0 )
	memcpy( desc->outbuf, desc->outbuf + desc->outtop - s->avail_in,
		s->avail_in + 1 );

    desc->outtop = s->avail_in;
    return TRUE;
}


/* User-level compression toggle */
void do_compress( CHAR_DATA *ch, const char *argument )
{
    if( !ch->desc )
	return;

    if( argument[0] == '\0' )
    {
	if( ch->desc->out_compress )
	    charprintf( ch, "Version %d compression is enabled.\n\r",
			ch->desc->mccp_version );
	else
	    send_to_char( "Compression is not currently enabled.\n\r", ch );
	return;
    }

    if( !str_cmp( argument, "on" ) )
    {
	if( !compress_start( ch->desc, ch->desc->mccp_version ) )
	{
	    send_to_char( "Failed, see help MCCP for details..\n\r", ch );
	    return;
	}

	charprintf( ch, "Ok, version %d compression enabled.\n\r",
		    ch->desc->mccp_version );
    }
    else if( !str_cmp( argument, "off" ) )
    {
	if( !compress_end( ch->desc, ch->desc->mccp_version ) )
	{
	    send_to_char( "Failed.\n\r", ch );
	    return;
	}

	send_to_char( "Ok, compression disabled.\n\r", ch );
    }
    else
	send_to_char( "Usage: compress [on|off]\n\r", ch );
}