fteqw/engine/common/huff.c
Spoike 9ae7e2621d SOFTWARE RENDERING IS BROKEN: DO NOT USE ASM VERSION.
Lots of changes.
CSQC should be functional, but is still tied to debug builds. It WILL have some bugs still, hopefully I'll be able to clean them up better if people test it a bit.
Precompiled headers are working properly now. Compile times are now much quicker in msvc. This takes most of the files this commit.
Restructured how client commands work. They're buffered outside the network message, some multithreaded code is in. It needs a bit of testing before it's active.


git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@885 fc73d0e0-1445-4013-8a0c-d673dee63da5
2005-02-28 07:16:19 +00:00

810 lines
15 KiB
C

/*
Q3Fusion - Quake III Clone Engine
Copyright (C) 2003 Andrey Nazarov
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
//
// huff.c - Huffman compression routines for data bitstream
//
#include "quakedef.h"
#ifdef HUFFNETWORK
#define ID_INLINE
#define VALUE(a) (*(int *)&(a))
#define NODE(a) ((void*)(a))
#define NODE_START NODE( 1)
#define NODE_NONE NODE(256)
#define NODE_NEXT NODE(257)
#define NOT_REFERENCED 256
#define HUFF_TREE_SIZE 7175
typedef void *tree_t[HUFF_TREE_SIZE];
//
// pre-defined frequency counts for all bytes [0..255]
//
static int q3huffCounts[256] = {
0x3D1CB, 0x0A0E9, 0x01894, 0x01BC2, 0x00E92, 0x00EA6, 0x017DE, 0x05AF3,
0x08225, 0x01B26, 0x01E9E, 0x025F2, 0x02429, 0x0436B, 0x00F6D, 0x006F2,
0x02060, 0x00644, 0x00636, 0x0067F, 0x0044C, 0x004BD, 0x004D6, 0x0046E,
0x006D5, 0x00423, 0x004DE, 0x0047D, 0x004F9, 0x01186, 0x00AF5, 0x00D90,
0x0553B, 0x00487, 0x00686, 0x0042A, 0x00413, 0x003F4, 0x0041D, 0x0042E,
0x006BE, 0x00378, 0x0049C, 0x00352, 0x003C0, 0x0030C, 0x006D8, 0x00CE0,
0x02986, 0x011A2, 0x016F9, 0x00A7D, 0x0122A, 0x00EFD, 0x0082D, 0x0074B,
0x00A18, 0x0079D, 0x007B4, 0x003AC, 0x0046E, 0x006FC, 0x00686, 0x004B6,
0x01657, 0x017F0, 0x01C36, 0x019FE, 0x00E7E, 0x00ED3, 0x005D4, 0x005F4,
0x008A7, 0x00474, 0x0054B, 0x003CB, 0x00884, 0x004E0, 0x00530, 0x004AB,
0x006EA, 0x00436, 0x004F0, 0x004F2, 0x00490, 0x003C5, 0x00483, 0x004A2,
0x00543, 0x004CC, 0x005F9, 0x00640, 0x00A39, 0x00800, 0x009F2, 0x00CCB,
0x0096A, 0x00E01, 0x009C8, 0x00AF0, 0x00A73, 0x01802, 0x00E4F, 0x00B18,
0x037AD, 0x00C5C, 0x008AD, 0x00697, 0x00C88, 0x00AB3, 0x00DB8, 0x012BC,
0x00FFB, 0x00DBB, 0x014A8, 0x00FB0, 0x01F01, 0x0178F, 0x014F0, 0x00F54,
0x0131C, 0x00E9F, 0x011D6, 0x012C7, 0x016DC, 0x01900, 0x01851, 0x02063,
0x05ACB, 0x01E9E, 0x01BA1, 0x022E7, 0x0153D, 0x01183, 0x00E39, 0x01488,
0x014C0, 0x014D0, 0x014FA, 0x00DA4, 0x0099A, 0x0069E, 0x0071D, 0x00849,
0x0077C, 0x0047D, 0x005EC, 0x00557, 0x004D4, 0x00405, 0x004EA, 0x00450,
0x004DD, 0x003EE, 0x0047D, 0x00401, 0x004D9, 0x003B8, 0x00507, 0x003E5,
0x006B1, 0x003F1, 0x004A3, 0x0036F, 0x0044B, 0x003A1, 0x00436, 0x003B7,
0x00678, 0x003A2, 0x00481, 0x00406, 0x004EE, 0x00426, 0x004BE, 0x00424,
0x00655, 0x003A2, 0x00452, 0x00390, 0x0040A, 0x0037C, 0x00486, 0x003DE,
0x00497, 0x00352, 0x00461, 0x00387, 0x0043F, 0x00398, 0x00478, 0x00420,
0x00D86, 0x008C0, 0x0112D, 0x02F68, 0x01E4E, 0x00541, 0x0051B, 0x00CCE,
0x0079E, 0x00376, 0x003FF, 0x00458, 0x00435, 0x00412, 0x00425, 0x0042F,
0x005CC, 0x003E9, 0x00448, 0x00393, 0x0041C, 0x003E3, 0x0042E, 0x0036C,
0x00457, 0x00353, 0x00423, 0x00325, 0x00458, 0x0039B, 0x0044F, 0x00331,
0x0076B, 0x00750, 0x003D0, 0x00349, 0x00467, 0x003BC, 0x00487, 0x003B6,
0x01E6F, 0x003BA, 0x00509, 0x003A5, 0x00467, 0x00C87, 0x003FC, 0x0039F,
0x0054B, 0x00300, 0x00410, 0x002E9, 0x003B8, 0x00325, 0x00431, 0x002E4,
0x003F5, 0x00325, 0x003F0, 0x0031C, 0x003E4, 0x00421, 0x02CC1, 0x034C0
};
static int countinghuffCounts[256];
//
// static Huffman tree
//
static tree_t huffTree;
//
// received from MSG_* code
//
static int huffBitPos;
/*
=======================================================================================
HUFFMAN TREE CONSTRUCTION
=======================================================================================
*/
/*
============
Huff_PrepareTree
============
*/
static ID_INLINE void Huff_PrepareTree( tree_t tree ) {
void **node;
memset( tree, 0, sizeof( tree_t ) );
// create first node
node = &tree[263];
tree[0] = (void*)(VALUE( tree[0] )+1);
node[7] = NODE_NONE;
tree[2] = node;
tree[3] = node;
tree[4] = node;
tree[261] = node;
}
/*
============
Huff_GetNode
============
*/
static ID_INLINE void **Huff_GetNode( void **tree ) {
void **node;
int value;
node = tree[262];
if( !node ) {
value = VALUE( tree[1] )++;
node = &tree[value + 6407];
return node;
}
tree[262] = node[0];
return node;
}
/*
============
Huff_Swap
============
*/
static ID_INLINE void Huff_Swap( void **tree1, void **tree2, void **tree3 ) {
void **a, **b;
a = tree2[2];
if( a ) {
if( a[0] == tree2 ) {
a[0] = tree3;
} else {
a[1] = tree3;
}
} else {
tree1[2] = tree3;
}
b = tree3[2];
if( b ) {
if( b[0] == tree3 ) {
b[0] = tree2;
tree2[2] = b;
tree3[2] = a;
return;
}
b[1] = tree2;
tree2[2] = b;
tree3[2] = a;
return;
}
tree1[2] = tree2;
tree2[2] = NULL;
tree3[2] = a;
}
/*
============
Huff_SwapTrees
============
*/
static ID_INLINE void Huff_SwapTrees( void **tree1, void **tree2 ) {
void **temp;
temp = tree1[3];
tree1[3] = tree2[3];
tree2[3] = temp;
temp = tree1[4];
tree1[4] = tree2[4];
tree2[4] = temp;
if( tree1[3] == tree1 ) {
tree1[3] = tree2;
}
if( tree2[3] == tree2 ) {
tree2[3] = tree1;
}
temp = tree1[3];
if( temp ) {
temp[4] = tree1;
}
temp = tree2[3];
if( temp ) {
temp[4] = tree2;
}
temp = tree1[4];
if( temp ) {
temp[3] = tree1;
}
temp = tree2[4];
if( temp ) {
temp[3] = tree2;
}
}
/*
============
Huff_DeleteNode
============
*/
static ID_INLINE void Huff_DeleteNode( void **tree1, void **tree2 ) {
tree2[0] = tree1[262];
tree1[262] = tree2;
}
/*
============
Huff_IncrementFreq_r
============
*/
static void Huff_IncrementFreq_r( void **tree1, void **tree2 ) {
void **a, **b;
if( !tree2 ) {
return;
}
a = tree2[3];
if( a ) {
a = a[6];
if( a == tree2[6] ) {
b = tree2[5];
if( b[0] != tree2[2] ) {
Huff_Swap( tree1, b[0], tree2 );
}
Huff_SwapTrees( b[0], tree2 );
}
}
a = tree2[4];
if( a && a[6] == tree2[6] ) {
b = tree2[5];
b[0] = a;
} else {
a = tree2[5];
a[0] = 0;
Huff_DeleteNode( tree1, tree2[5] );
}
VALUE( tree2[6] )++;
a = tree2[3];
if( a && a[6] == tree2[6] ) {
tree2[5] = a[5];
} else {
a = Huff_GetNode( tree1 );
tree2[5] = a;
a[0] = tree2;
}
if( tree2[2] ) {
Huff_IncrementFreq_r( tree1, tree2[2] );
if( tree2[4] == tree2[2] ) {
Huff_SwapTrees( tree2, tree2[2] );
a = tree2[5];
if( a[0] == tree2 ) {
a[0] = tree2[2];
}
}
}
}
/*
============
Huff_AddReference
Insert 'ch' into the tree or increment it's frequency
============
*/
static void Huff_AddReference( void **tree, int ch ) {
void **a, **b, **c, **d;
int value;
ch &= 255;
if( tree[ch + 5] ) {
Huff_IncrementFreq_r( tree, tree[ch + 5] );
return; // already added
}
value = VALUE( tree[0] )++;
b = &tree[value * 8 + 263];
value = VALUE( tree[0] )++;
a = &tree[value * 8 + 263];
a[7] = NODE_NEXT;
a[6] = NODE_START;
d = tree[3];
a[3] = d[3];
if( a[3] ) {
d = a[3];
d[4] = a;
d = a[3];
if( d[6] == NODE_START ) {
a[5] = d[5];
} else {
d = Huff_GetNode( tree );
a[5] = d;
d[0] = a;
}
} else {
d = Huff_GetNode( tree );
a[5] = d;
d[0] = a;
}
d = tree[3];
d[3] = a;
a[4] = tree[3];
b[7] = NODE( ch );
b[6] = NODE_START;
d = tree[3];
b[3] = d[3];
if( b[3] ) {
d = b[3];
d[4] = b;
if( d[6] == NODE_START ) {
b[5] = d[5];
} else {
d = Huff_GetNode( tree );
b[5] = d;
d[0] = a;
}
} else {
d = Huff_GetNode( tree );
b[5] = d;
d[0] = b;
}
d = tree[3];
d[3] = b;
b[4] = tree[3];
b[1] = NULL;
b[0] = NULL;
d = tree[3];
c = d[2];
if( c ) {
if( c[0] == tree[3] ) {
c[0] = a;
} else {
c[1] = a;
}
} else {
tree[2] = a;
}
a[1] = b;
d = tree[3];
a[0] = d;
a[2] = d[2];
b[2] = a;
d = tree[3];
d[2] = a;
tree[ch + 5] = b;
Huff_IncrementFreq_r( tree, a[2] );
}
/*
=======================================================================================
BITSTREAM I/O
=======================================================================================
*/
/*
============
Huff_EmitBit
Put one bit into buffer
============
*/
static ID_INLINE void Huff_EmitBit( int bit, qbyte *buffer ) {
if( !(huffBitPos & 7) ) {
buffer[huffBitPos >> 3] = 0;
}
buffer[huffBitPos >> 3] |= bit << (huffBitPos & 7);
huffBitPos++;
}
/*
============
Huff_GetBit
Read one bit from buffer
============
*/
static ID_INLINE int Huff_GetBit( qbyte *buffer ) {
int bit;
bit = buffer[huffBitPos >> 3] >> (huffBitPos & 7);
huffBitPos++;
return (bit & 1);
}
/*
============
Huff_EmitPathToByte
============
*/
static ID_INLINE void Huff_EmitPathToByte( void **tree, void **subtree, qbyte *buffer ) {
if( tree[2] ) {
Huff_EmitPathToByte( tree[2], tree, buffer );
}
if( !subtree ) {
return;
}
//
// emit tree walking control bits
//
if( tree[1] == subtree ) {
Huff_EmitBit( 1, buffer );
} else {
Huff_EmitBit( 0, buffer );
}
}
/*
============
Huff_GetByteFromTree
Get one qbyte using dynamic or static tree
============
*/
static ID_INLINE int Huff_GetByteFromTree( void **tree, qbyte *buffer ) {
if( !tree ) {
return 0;
}
//
// walk through the tree until we get a value
//
while( tree[7] == NODE_NEXT ) {
if( !Huff_GetBit( buffer ) ) {
tree = tree[0];
} else {
tree = tree[1];
}
if( !tree ) {
return 0;
}
}
return VALUE( tree[7] );
}
/*
============
Huff_EmitByteDynamic
Emit one qbyte using dynamic tree
============
*/
static void Huff_EmitByteDynamic( void **tree, int value, qbyte *buffer ) {
void **subtree;
int i;
//
// if qbyte was already referenced, emit path to it
//
subtree = tree[value + 5];
if( subtree ) {
if( subtree[2] ) {
Huff_EmitPathToByte( subtree[2], subtree, buffer );
}
return;
}
//
// qbyte was not referenced, just emit 8 bits
//
Huff_EmitByteDynamic( tree, NOT_REFERENCED, buffer );
for( i=7 ; i>=0 ; i-- ) {
Huff_EmitBit( (value >> i) & 1, buffer );
}
}
/*
=======================================================================================
PUBLIC INTERFACE
=======================================================================================
*/
/*
============
Huff_CompressPacket
Compress message using dynamic Huffman tree,
beginning from specified offset
============
*/
void Huff_EncryptPacket( sizebuf_t *msg, int offset ) {
tree_t tree;
qbyte buffer[MAX_NQMSGLEN];
qbyte *data;
int outLen;
int inLen;
int i;
data = msg->data + offset;
inLen = msg->cursize - offset;
if( inLen <= 0 || inLen >= MAX_NQMSGLEN ) {
return;
}
Huff_PrepareTree( tree );
buffer[0] = inLen >> 8;
buffer[1] = inLen & 0xFF;
huffBitPos = 16;
for( i=0 ; i<inLen ; i++ ) {
Huff_EmitByteDynamic( tree, data[i], buffer );
Huff_AddReference( tree, data[i] );
}
outLen = (huffBitPos >> 3) + 1;
msg->cursize = offset + outLen;
memcpy( data, buffer, outLen );
}
/*
============
Huff_DecompressPacket
Decompress message using dynamic Huffman tree,
beginning from specified offset
============
*/
void Huff_DecryptPacket( sizebuf_t *msg, int offset ) {
tree_t tree;
qbyte buffer[MAX_NQMSGLEN];
qbyte *data;
int outLen;
int inLen;
int i, j;
int ch;
data = msg->data + offset;
inLen = msg->cursize - offset;
if( inLen <= 0 ) {
return;
}
Huff_PrepareTree( tree );
outLen = (data[0] << 8) + data[1];
huffBitPos = 16;
if( outLen > msg->maxsize - offset ) {
outLen = msg->maxsize - offset;
}
for( i=0 ; i<outLen ; i++ ) {
if( (huffBitPos >> 3) > inLen ) {
buffer[i] = 0;
break;
}
ch = Huff_GetByteFromTree( tree[2], data );
if( ch == NOT_REFERENCED ) {
ch = 0; // just read 8 bits
for( j=0 ; j<8 ; j++ ) {
ch <<= 1;
ch |= Huff_GetBit( data );
}
}
buffer[i] = ch;
Huff_AddReference( tree, ch );
}
msg->cursize = offset + outLen;
memcpy( data, buffer, outLen );
}
/*
============
Huff_EmitByte
============
*/
void Huff_EmitByte( int ch, qbyte *buffer, int *count ) {
huffBitPos = *count;
Huff_EmitPathToByte( huffTree[ch + 5], NULL, buffer );
*count = huffBitPos;
}
/*
============
Huff_GetByte
============
*/
int Huff_GetByte( qbyte *buffer, int *count ) {
int ch;
huffBitPos = *count;
ch = Huff_GetByteFromTree( huffTree[2], buffer );
*count = huffBitPos;
return ch;
}
static int madetable;
/*
============
Huff_Init
============
*/
void Huff_Init( int *huffCounts ) {
int i, j;
if (!huffCounts)
huffCounts = q3huffCounts;
// build empty tree
Huff_PrepareTree( huffTree );
// add all pre-defined qbyte references
for( i=0 ; i<256 ; i++ ) {
for( j=0 ; j<huffCounts[i] ; j++ ) {
Huff_AddReference( huffTree, i );
}
}
madetable=Com_BlockChecksum(huffCounts, sizeof(huffCounts));
}
void Huff_LoadTable(char *filename)
{
}
int Huff_PreferedCompressionCRC (void)
{
if (!madetable)
Huff_Init(NULL);
return madetable;
}
qboolean Huff_CompressionCRC(int crc)
{
if (!madetable)
Huff_Init(NULL);
if (crc != madetable)
return false;
return true;
}
/*
============
Huff_CompressPacket
Compress message using loaded Huffman tree,
beginning from specified offset
============
*/
void Huff_CompressPacket( sizebuf_t *msg, int offset )
{
qbyte buffer[MAX_NQMSGLEN];
qbyte *data;
int outLen;
int inLen;
int i;
if (!madetable)
Huff_Init(NULL);
data = msg->data + offset;
inLen = msg->cursize - offset;
if( inLen <= 0 || inLen >= MAX_NQMSGLEN ) {
return;
}
outLen = 0;
for( i=0 ; i<inLen ; i++ )
{
if (i == MAX_NQMSGLEN)
Sys_Error("Compression became too large\n");
Huff_EmitByte(data[i], buffer, &outLen);
countinghuffCounts[data[i]]++;
}
outLen = (huffBitPos >> 3) + 1;
if (outLen > inLen)
{
memmove( data+1, data, inLen );
data[0] = 0x80; //this would have grown the packet.
msg->cursize+=1;
return; //cap it at only 1 qbyte growth.
}
msg->cursize = offset + outLen;
{ //add the bitcount
data[0] = (outLen<<3) - huffBitPos;
data+=1;
msg->cursize+=1;
}
if (msg->cursize > msg->maxsize)
Sys_Error("Compression became too large\n");
memcpy( data, buffer, outLen );
}
/*
============
Huff_DecompressPacket
Decompress message using loaded Huffman tree,
beginning from specified offset
============
*/
void Huff_DecompressPacket( sizebuf_t *msg, int offset )
{
qbyte buffer[MAX_NQMSGLEN];
qbyte *data;
int outLen;
int inLen;
int i;
if (!madetable)
Huff_Init(NULL);
data = msg->data + offset;
inLen = msg->cursize - offset;
if( inLen <= 0 || inLen >= MAX_NQMSGLEN ) {
return;
}
inLen<<=3;
{ //add the bitcount
inLen = inLen-8-data[0];
if (data[0]&0x80)
{ //packet would have grown.
msg->cursize -= 1;
memmove(data, data+1, msg->cursize);
return; //this never happened, okay?
}
data+=1;
}
outLen = 0;
for( i=0 ; outLen<inLen ; i++ )
{
if (i == MAX_NQMSGLEN)
Sys_Error("Decompression became too large\n");
buffer[i] = Huff_GetByte(data, &outLen);
}
msg->cursize = offset + i;
if (msg->cursize > msg->maxsize)
Sys_Error("Decompression became too large\n");
memcpy( msg->data + offset, buffer, i );
}
#endif