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