mirror of
https://github.com/ioquake/ioq3.git
synced 2024-11-10 07:11:46 +00:00
8ab3f7af8d
Previously, a legacy client wouldn't get a VoIP packet, but if they did, they'd panic and disconnect. Now they ignore them and continue on. This also gives us the framework to add other features legacy clients can ignore. Oh, this also has the benefit of allowing us to store incoming VoIP for playback in recorded demos. They'll play the chatter on VoIP clients, and be ignored on legacy ones. Huge win.
447 lines
11 KiB
C
447 lines
11 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 "q_shared.h"
|
|
#include "qcommon.h"
|
|
|
|
static int bloc = 0;
|
|
|
|
void Huff_putBit( int bit, byte *fout, int *offset) {
|
|
bloc = *offset;
|
|
if ((bloc&7) == 0) {
|
|
fout[(bloc>>3)] = 0;
|
|
}
|
|
fout[(bloc>>3)] |= bit << (bloc&7);
|
|
bloc++;
|
|
*offset = bloc;
|
|
}
|
|
|
|
int Huff_getBloc(void)
|
|
{
|
|
return bloc;
|
|
}
|
|
|
|
void Huff_setBloc(int _bloc)
|
|
{
|
|
bloc = _bloc;
|
|
}
|
|
|
|
int Huff_getBit( byte *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 add_bit (char bit, byte *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 get_bit (byte *fin) {
|
|
int t;
|
|
t = (fin[(bloc>>3)] >> (bloc&7)) & 0x1;
|
|
bloc++;
|
|
return t;
|
|
}
|
|
|
|
static node_t **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 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 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 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 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) {
|
|
swap(huff, lnode, node);
|
|
}
|
|
swaplist(lnode, node);
|
|
}
|
|
if (node->prev && node->prev->weight == node->weight) {
|
|
*node->head = node->prev;
|
|
} else {
|
|
*node->head = NULL;
|
|
free_ppnode(huff, node->head);
|
|
}
|
|
node->weight++;
|
|
if (node->next && node->next->weight == node->weight) {
|
|
node->head = node->next->head;
|
|
} else {
|
|
node->head = get_ppnode(huff);
|
|
*node->head = node;
|
|
}
|
|
if (node->parent) {
|
|
increment(huff, node->parent);
|
|
if (node->prev == node->parent) {
|
|
swaplist(node, node->parent);
|
|
if (*node->head == node) {
|
|
*node->head = node->parent;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Huff_addRef(huff_t* huff, byte 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 = get_ppnode(huff);
|
|
*tnode2->head = tnode2;
|
|
}
|
|
} else {
|
|
tnode2->head = 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 = get_ppnode(huff);
|
|
*tnode->head = tnode2;
|
|
}
|
|
} else {
|
|
/* this should never happen */
|
|
tnode->head = 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;
|
|
|
|
increment(huff, tnode2->parent);
|
|
} else {
|
|
increment(huff, huff->loc[ch]);
|
|
}
|
|
}
|
|
|
|
/* Get a symbol */
|
|
int Huff_Receive (node_t *node, int *ch, byte *fin) {
|
|
while (node && node->symbol == INTERNAL_NODE) {
|
|
if (get_bit(fin)) {
|
|
node = node->right;
|
|
} else {
|
|
node = node->left;
|
|
}
|
|
}
|
|
if (!node) {
|
|
return 0;
|
|
// Com_Error(ERR_DROP, "Illegal tree!\n");
|
|
}
|
|
return (*ch = node->symbol);
|
|
}
|
|
|
|
/* Get a symbol */
|
|
void Huff_offsetReceive (node_t *node, int *ch, byte *fin, int *offset) {
|
|
bloc = *offset;
|
|
while (node && node->symbol == INTERNAL_NODE) {
|
|
if (get_bit(fin)) {
|
|
node = node->right;
|
|
} else {
|
|
node = node->left;
|
|
}
|
|
}
|
|
if (!node) {
|
|
*ch = 0;
|
|
return;
|
|
// Com_Error(ERR_DROP, "Illegal tree!\n");
|
|
}
|
|
*ch = node->symbol;
|
|
*offset = bloc;
|
|
}
|
|
|
|
/* Send the prefix code for this node */
|
|
static void send(node_t *node, node_t *child, byte *fout) {
|
|
if (node->parent) {
|
|
send(node->parent, node, fout);
|
|
}
|
|
if (child) {
|
|
if (node->right == child) {
|
|
add_bit(1, fout);
|
|
} else {
|
|
add_bit(0, fout);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Send a symbol */
|
|
void Huff_transmit (huff_t *huff, int ch, byte *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--) {
|
|
add_bit((char)((ch >> i) & 0x1), fout);
|
|
}
|
|
} else {
|
|
send(huff->loc[ch], NULL, fout);
|
|
}
|
|
}
|
|
|
|
void Huff_offsetTransmit (huff_t *huff, int ch, byte *fout, int *offset) {
|
|
bloc = *offset;
|
|
send(huff->loc[ch], NULL, fout);
|
|
*offset = bloc;
|
|
}
|
|
|
|
void Huff_Decompress(msg_t *mbuf, int offset) {
|
|
int ch, cch, i, j, size;
|
|
byte seq[65536];
|
|
byte* buffer;
|
|
huff_t huff;
|
|
|
|
size = mbuf->cursize - offset;
|
|
buffer = mbuf->data + offset;
|
|
|
|
if ( size <= 0 ) {
|
|
return;
|
|
}
|
|
|
|
Com_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 a 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) + get_bit(buffer);
|
|
}
|
|
}
|
|
|
|
seq[j] = ch; /* Write symbol */
|
|
|
|
Huff_addRef(&huff, (byte)ch); /* Increment node */
|
|
}
|
|
mbuf->cursize = cch + offset;
|
|
Com_Memcpy(mbuf->data + offset, seq, cch);
|
|
}
|
|
|
|
extern int oldsize;
|
|
|
|
void Huff_Compress(msg_t *mbuf, int offset) {
|
|
int i, ch, size;
|
|
byte seq[65536];
|
|
byte* buffer;
|
|
huff_t huff;
|
|
|
|
size = mbuf->cursize - offset;
|
|
buffer = mbuf->data+ + offset;
|
|
|
|
if (size<=0) {
|
|
return;
|
|
}
|
|
|
|
Com_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;
|
|
huff.loc[NYT] = huff.tree;
|
|
|
|
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, (byte)ch); /* Do update */
|
|
}
|
|
|
|
bloc += 8; // next byte
|
|
|
|
mbuf->cursize = (bloc>>3) + offset;
|
|
Com_Memcpy(mbuf->data+offset, seq, (bloc>>3));
|
|
}
|
|
|
|
void Huff_Init(huffman_t *huff) {
|
|
|
|
Com_Memset(&huff->compressor, 0, sizeof(huff_t));
|
|
Com_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;
|
|
huff->compressor.loc[NYT] = huff->compressor.tree;
|
|
}
|
|
|