fteqw/engine/common/huff.c
Spoike 49ae9573b8 reworked clipboard handling to avoid stalls when pasting in linux.
made a load of functions static (just code style stuff).
downloads menu now deselects any autoselected items, to avoid confusion.
csqc traces can now hit networked ents. I probably need to make some more tweaks to this.
arg completion for flocate+dir+modelviewer commands.
fix autosave not cycling saves when using the vanilla save format.
fix patch collisions with q3bsp submodels.
md3 now supports framegroups too.
added some more buttons to make xonotic happy.
gl_polyblend 2 shows the screen flashes at the edge of the screen instead of the middle.
colormod no longer applies to fullbrights (matching DP). this fixes an issue with xonotic.
fix uninitialised local that was causing issues with bones used as tags.
rewrote qc search_* to use a dynamic array instead of a linked list. this should make it faster to read when there's many handles open at a time.
fte's saved games can now save buffers properly.
fix issue with raster freetype fonts (read: coloured emoji).
initial support for struct-based classes (instead of entity-based classes). still has issues.
execing configs in untrusted packages will no longer run out of sync (fixing a number of afterquake issues).
fix stupid bug with the te_gunshot/etc builtins.


git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5317 fc73d0e0-1445-4013-8a0c-d673dee63da5
2018-10-11 10:31:23 +00:00

666 lines
17 KiB
C

/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code 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.
Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
/* This is based on the Adaptive Huffman algorithm described in Sayood's Data
* Compression book. The ranks are not actually stored, but implicitly defined
* by the location of a node within a doubly-linked list */
#include "quakedef.h"
#ifdef HUFFNETWORK
#define NYT HMAX /* NYT = Not Yet Transmitted */
#define INTERNAL_NODE (HMAX+1)
typedef struct nodetype {
struct nodetype *left, *right, *parent; /* tree structure */
struct nodetype *next, *prev; /* doubly-linked list */
struct nodetype **head; /* highest ranked node in block */
int weight;
int symbol;
} node_t;
#define HMAX 256 /* Maximum symbol */
typedef struct {
int blocNode;
int blocPtrs;
node_t* tree;
node_t* lhead;
node_t* ltail;
node_t* loc[HMAX+1];
node_t** freelist;
node_t nodeList[768];
node_t* nodePtrs[768];
} huff_t;
struct huffman_s{
int counts[256];
unsigned int crc;
qboolean built;
huff_t compressor;
huff_t decompressor;
};
extern cvar_t net_compress;
static int bloc = 0;
/*
static void Huff_putBit( int bit, qbyte *fout, int *offset) {
bloc = *offset;
if ((bloc&7) == 0) {
fout[(bloc>>3)] = 0;
}
fout[(bloc>>3)] |= bit << (bloc&7);
bloc++;
*offset = bloc;
}
static int Huff_getBloc(void)
{
return bloc;
}
static void Huff_setBloc(int _bloc)
{
bloc = _bloc;
}
static int Huff_getBit( qbyte *fin, int *offset) {
int t;
bloc = *offset;
t = (fin[(bloc>>3)] >> (bloc&7)) & 0x1;
bloc++;
*offset = bloc;
return t;
}*/
/* Add a bit to the output file (buffered) */
static void huff_add_bit (char bit, qbyte *fout) {
if ((bloc&7) == 0) {
fout[(bloc>>3)] = 0;
}
fout[(bloc>>3)] |= bit << (bloc&7);
bloc++;
}
/* Receive one bit from the input file (buffered) */
static int huff_get_bit (qbyte *fin) {
int t;
t = (fin[(bloc>>3)] >> (bloc&7)) & 0x1;
bloc++;
return t;
}
static node_t **huff_get_ppnode(huff_t* huff) {
node_t **tppnode;
if (!huff->freelist) {
return &(huff->nodePtrs[huff->blocPtrs++]);
} else {
tppnode = huff->freelist;
huff->freelist = (node_t **)*tppnode;
return tppnode;
}
}
static void huff_free_ppnode(huff_t* huff, node_t **ppnode) {
*ppnode = (node_t *)huff->freelist;
huff->freelist = ppnode;
}
/* Swap the location of these two nodes in the tree */
static void huff_swap (huff_t* huff, node_t *node1, node_t *node2) {
node_t *par1, *par2;
par1 = node1->parent;
par2 = node2->parent;
if (par1) {
if (par1->left == node1) {
par1->left = node2;
} else {
par1->right = node2;
}
} else {
huff->tree = node2;
}
if (par2) {
if (par2->left == node2) {
par2->left = node1;
} else {
par2->right = node1;
}
} else {
huff->tree = node1;
}
node1->parent = par2;
node2->parent = par1;
}
/* Swap these two nodes in the linked list (update ranks) */
static void huff_swaplist(node_t *node1, node_t *node2) {
node_t *par1;
par1 = node1->next;
node1->next = node2->next;
node2->next = par1;
par1 = node1->prev;
node1->prev = node2->prev;
node2->prev = par1;
if (node1->next == node1) {
node1->next = node2;
}
if (node2->next == node2) {
node2->next = node1;
}
if (node1->next) {
node1->next->prev = node1;
}
if (node2->next) {
node2->next->prev = node2;
}
if (node1->prev) {
node1->prev->next = node1;
}
if (node2->prev) {
node2->prev->next = node2;
}
}
/* Do the increments */
static void huff_increment(huff_t* huff, node_t *node) {
node_t *lnode;
if (!node) {
return;
}
if (node->next != NULL && node->next->weight == node->weight) {
lnode = *node->head;
if (lnode != node->parent) {
huff_swap(huff, lnode, node);
}
huff_swaplist(lnode, node);
}
if (node->prev && node->prev->weight == node->weight) {
*node->head = node->prev;
} else {
*node->head = NULL;
huff_free_ppnode(huff, node->head);
}
node->weight++;
if (node->next && node->next->weight == node->weight) {
node->head = node->next->head;
} else {
node->head = huff_get_ppnode(huff);
*node->head = node;
}
if (node->parent) {
huff_increment(huff, node->parent);
if (node->prev == node->parent) {
huff_swaplist(node, node->parent);
if (*node->head == node) {
*node->head = node->parent;
}
}
}
}
static void Huff_addRef(huff_t* huff, qbyte ch) {
node_t *tnode, *tnode2;
if (huff->loc[ch] == NULL) { /* if this is the first transmission of this node */
tnode = &(huff->nodeList[huff->blocNode++]);
tnode2 = &(huff->nodeList[huff->blocNode++]);
tnode2->symbol = INTERNAL_NODE;
tnode2->weight = 1;
tnode2->next = huff->lhead->next;
if (huff->lhead->next) {
huff->lhead->next->prev = tnode2;
if (huff->lhead->next->weight == 1) {
tnode2->head = huff->lhead->next->head;
} else {
tnode2->head = huff_get_ppnode(huff);
*tnode2->head = tnode2;
}
} else {
tnode2->head = huff_get_ppnode(huff);
*tnode2->head = tnode2;
}
huff->lhead->next = tnode2;
tnode2->prev = huff->lhead;
tnode->symbol = ch;
tnode->weight = 1;
tnode->next = huff->lhead->next;
if (huff->lhead->next) {
huff->lhead->next->prev = tnode;
if (huff->lhead->next->weight == 1) {
tnode->head = huff->lhead->next->head;
} else {
/* this should never happen */
tnode->head = huff_get_ppnode(huff);
*tnode->head = tnode2;
}
} else {
/* this should never happen */
tnode->head = huff_get_ppnode(huff);
*tnode->head = tnode;
}
huff->lhead->next = tnode;
tnode->prev = huff->lhead;
tnode->left = tnode->right = NULL;
if (huff->lhead->parent) {
if (huff->lhead->parent->left == huff->lhead) { /* lhead is guaranteed to by the NYT */
huff->lhead->parent->left = tnode2;
} else {
huff->lhead->parent->right = tnode2;
}
} else {
huff->tree = tnode2;
}
tnode2->right = tnode;
tnode2->left = huff->lhead;
tnode2->parent = huff->lhead->parent;
huff->lhead->parent = tnode->parent = tnode2;
huff->loc[ch] = tnode;
huff_increment(huff, tnode2->parent);
} else {
huff_increment(huff, huff->loc[ch]);
}
}
/* Get a symbol */
static int Huff_Receive (node_t *node, int *ch, qbyte *fin) {
while (node && node->symbol == INTERNAL_NODE) {
if (huff_get_bit(fin)) {
node = node->right;
} else {
node = node->left;
}
}
if (!node) {
return 0;
// Com_Error(ERR_DROP, "Illegal tree!");
}
return (*ch = node->symbol);
}
/* Get a symbol */
static void Huff_offsetReceive (node_t *node, int *ch, qbyte *fin, int *offset) {
bloc = *offset;
while (node && node->symbol == INTERNAL_NODE) {
if (huff_get_bit(fin)) {
node = node->right;
} else {
node = node->left;
}
}
if (!node) {
*ch = 0;
return;
// Com_Error(ERR_DROP, "Illegal tree!");
}
*ch = node->symbol;
*offset = bloc;
}
/* Send the prefix code for this node */
static void huff_send(node_t *node, node_t *child, qbyte *fout) {
if (node->parent) {
huff_send(node->parent, node, fout);
}
if (child) {
if (node->right == child) {
huff_add_bit(1, fout);
} else {
huff_add_bit(0, fout);
}
}
}
/* Send a symbol */
static void Huff_transmit (huff_t *huff, int ch, qbyte *fout) {
int i;
if (huff->loc[ch] == NULL) {
/* node_t hasn't been transmitted, send a NYT, then the symbol */
Huff_transmit(huff, NYT, fout);
for (i = 7; i >= 0; i--) {
huff_add_bit((char)((ch >> i) & 0x1), fout);
}
} else {
huff_send(huff->loc[ch], NULL, fout);
}
}
static void Huff_offsetTransmit (huff_t *huff, int ch, qbyte *fout, int *offset) {
bloc = *offset;
huff_send(huff->loc[ch], NULL, fout);
*offset = bloc;
}
static void Huff_Decompress(sizebuf_t *mbuf, int offset) {
int ch, cch, i, j, size;
qbyte seq[65536];
qbyte* buffer;
huff_t huff;
size = mbuf->cursize - offset;
buffer = mbuf->data + offset;
if ( size <= 0 ) {
return;
}
memset(&huff, 0, sizeof(huff_t));
// Initialize the tree & list with the NYT node
huff.tree = huff.lhead = huff.ltail = huff.loc[NYT] = &(huff.nodeList[huff.blocNode++]);
huff.tree->symbol = NYT;
huff.tree->weight = 0;
huff.lhead->next = huff.lhead->prev = NULL;
huff.tree->parent = huff.tree->left = huff.tree->right = NULL;
cch = buffer[0]*256 + buffer[1];
// don't overflow with bad messages
if ( cch > mbuf->maxsize - offset ) {
cch = mbuf->maxsize - offset;
}
bloc = 16;
for ( j = 0; j < cch; j++ ) {
ch = 0;
// don't overflow reading from the messages
// FIXME: would it be better to have an overflow check in get_bit ?
if ( (bloc >> 3) > size ) {
seq[j] = 0;
break;
}
Huff_Receive(huff.tree, &ch, buffer); /* Get a character */
if ( ch == NYT ) { /* We got a NYT, get the symbol associated with it */
ch = 0;
for ( i = 0; i < 8; i++ ) {
ch = (ch<<1) + huff_get_bit(buffer);
}
}
seq[j] = ch; /* Write symbol */
Huff_addRef(&huff, (qbyte)ch); /* Increment node */
}
mbuf->cursize = cch + offset;
memcpy(mbuf->data + offset, seq, cch);
}
static void Huff_Compress(sizebuf_t *mbuf, int offset) {
int i, ch, size;
qbyte seq[65536];
qbyte* buffer;
huff_t huff;
size = mbuf->cursize - offset;
buffer = mbuf->data+ + offset;
if (size<=0) {
return;
}
memset(&huff, 0, sizeof(huff_t));
// Add the NYT (not yet transmitted) node into the tree/list */
huff.tree = huff.lhead = huff.loc[NYT] = &(huff.nodeList[huff.blocNode++]);
huff.tree->symbol = NYT;
huff.tree->weight = 0;
huff.lhead->next = huff.lhead->prev = NULL;
huff.tree->parent = huff.tree->left = huff.tree->right = NULL;
seq[0] = (size>>8);
seq[1] = size&0xff;
bloc = 16;
for (i=0; i<size; i++ ) {
ch = buffer[i];
Huff_transmit(&huff, ch, seq); /* Transmit symbol */
Huff_addRef(&huff, (qbyte)ch); /* Do update */
}
bloc += 8; // next byte
mbuf->cursize = (bloc>>3) + offset;
memcpy(mbuf->data+offset, seq, (bloc>>3));
}
static void Huff_Init(huffman_t *huff)
{
int i, j;
memset(&huff->compressor, 0, sizeof(huff_t));
memset(&huff->decompressor, 0, sizeof(huff_t));
// Initialize the tree & list with the NYT node
huff->decompressor.tree = huff->decompressor.lhead = huff->decompressor.ltail = huff->decompressor.loc[NYT] = &(huff->decompressor.nodeList[huff->decompressor.blocNode++]);
huff->decompressor.tree->symbol = NYT;
huff->decompressor.tree->weight = 0;
huff->decompressor.lhead->next = huff->decompressor.lhead->prev = NULL;
huff->decompressor.tree->parent = huff->decompressor.tree->left = huff->decompressor.tree->right = NULL;
// Add the NYT (not yet transmitted) node into the tree/list */
huff->compressor.tree = huff->compressor.lhead = huff->compressor.loc[NYT] = &(huff->compressor.nodeList[huff->compressor.blocNode++]);
huff->compressor.tree->symbol = NYT;
huff->compressor.tree->weight = 0;
huff->compressor.lhead->next = huff->compressor.lhead->prev = NULL;
huff->compressor.tree->parent = huff->compressor.tree->left = huff->compressor.tree->right = NULL;
for(i=0;i<256;i++)
{
for (j=0;j<huff->counts[i];j++)
{
Huff_addRef(&huff->compressor, (qbyte)i);
Huff_addRef(&huff->decompressor, (qbyte)i);
}
}
}
static huffman_t q3huff =
{
{
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
},
0x286f2e8d
};
int Huff_PreferedCompressionCRC (void)
{
if (!net_compress.ival)
return 0;
return q3huff.crc;
}
huffman_t *Huff_CompressionCRC(int crc)
{
huffman_t *huff = NULL;
if (crc == q3huff.crc)
huff = &q3huff;
else
huff = NULL;
if (huff && !huff->built)
Huff_Init(huff);
return huff;
}
void Huff_DecryptPacket(sizebuf_t *msg, int offset)
{
//decompress using a dynamic from-nil tree
Huff_Decompress(msg, offset);
}
void Huff_EncryptPacket(sizebuf_t *msg, int offset)
{
//decompress using a dynamic from-nil tree
Huff_Compress(msg, offset);
}
int Huff_GetByte(qbyte *buffer, int *count)
{
int ch;
Huff_offsetReceive (q3huff.decompressor.tree, &ch, buffer, count);
return ch;
}
void Huff_EmitByte(int ch, qbyte *buffer, int *count)
{
Huff_offsetTransmit(&q3huff.compressor, ch, buffer, count);
}
void Huff_CompressPacket(huffman_t *huff, sizebuf_t *msg, int offset)
{
qbyte buffer[MAX_OVERALLMSGLEN];
qbyte *data;
int outLen;
int inLen;
int i;
data = msg->data + offset;
inLen = msg->cursize - offset;
if (inLen <= 0 || inLen >= MAX_OVERALLMSGLEN)
{
//panic!
return;
}
outLen = 0;
for (i=0; i < inLen; i++)
{
Huff_offsetTransmit(&huff->compressor, data[i], buffer, &outLen);
if (outLen > inLen)
break;
if (outLen > MAX_OVERALLMSGLEN-64)
{
Con_Printf("Huffman overflow\n");
//panic
data[0] = 0x80;
msg->cursize = offset+1;
return;
}
}
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) - outLen;
data+=1;
msg->cursize+=1;
}
if (msg->cursize > msg->maxsize)
Sys_Error("Compression became too large\n");
memcpy(data, buffer, outLen);
}
void Huff_DecompressPacket(huffman_t *huff, sizebuf_t *msg, int offset)
{
qbyte buffer[MAX_OVERALLMSGLEN];
qbyte *data;
int outLen;
int inLen;
int i, ch;
data = msg->data + offset;
inLen = msg->cursize - offset;
if (inLen <= 0 || inLen >= MAX_OVERALLMSGLEN)
{
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_OVERALLMSGLEN)
Sys_Error("Decompression became too large\n");
Huff_offsetReceive (huff->decompressor.tree, &ch, data, &outLen);
buffer[i] = ch;
}
msg->cursize = offset + i;
if (msg->cursize > msg->maxsize)
Sys_Error("Decompression became too large\n");
memcpy(msg->data + offset, buffer, i);
}
#endif