/* 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 = (void**)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 = (void**)tree2[2]; if (a) { if (a[0] == tree2) a[0] = tree3; else a[1] = tree3; } else tree1[2] = tree3; b = (void**)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 = (void**)tree1[3]; tree1[3] = tree2[3]; tree2[3] = temp; temp = (void**)tree1[4]; tree1[4] = tree2[4]; tree2[4] = temp; if (tree1[3] == tree1) tree1[3] = tree2; if (tree2[3] == tree2) tree2[3] = tree1; temp = (void**)tree1[3]; if (temp) temp[4] = tree1; temp = (void**)tree2[3]; if (temp) temp[4] = tree2; temp = (void**)tree1[4]; if (temp) temp[3] = tree1; temp = (void**)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 = (void**)tree2[3]; if (a) { a = (void**)a[6]; if (a == tree2[6]) { b = (void**)tree2[5]; if (b[0] != tree2[2]) { Huff_Swap(tree1, (void**)b[0], tree2); } Huff_SwapTrees((void**)b[0], tree2); } } a = (void**)tree2[4]; if (a && a[6] == tree2[6]) { b = (void**)tree2[5]; b[0] = a; } else { a = (void**)tree2[5]; a[0] = 0; Huff_DeleteNode(tree1, (void**)tree2[5]); } VALUE(tree2[6])++; a = (void**)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, (void**)tree2[2]); if (tree2[4] == tree2[2]) { Huff_SwapTrees(tree2, (void**)tree2[2]); a = (void**)tree2[5]; if (a[0] == tree2) { a[0] = (void**)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, (void**)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 = (void**)tree[3]; a[3] = d[3]; if (a[3]) { d = (void**)a[3]; d[4] = a; d = (void**)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 = (void**)tree[3]; d[3] = a; a[4] = (void**)tree[3]; b[7] = NODE(ch); b[6] = NODE_START; d = (void**)tree[3]; b[3] = d[3]; if (b[3]) { d = (void**)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 = (void**)tree[3]; d[3] = b; b[4] = (void**)tree[3]; b[1] = NULL; b[0] = NULL; d = (void**)tree[3]; c = (void**)d[2]; if (c) { if (c[0] == tree[3]) { c[0] = a; } else { c[1] = a; } } else { tree[2] = a; } a[1] = b; d = (void**)tree[3]; a[0] = d; a[2] = d[2]; b[2] = a; d = (void**)tree[3]; d[2] = a; tree[ch + 5] = b; Huff_IncrementFreq_r(tree, (void**)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((void**)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 = (void**)tree[0]; } else { tree = (void**)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 = (void**)tree[value + 5]; if (subtree) { if (subtree[2]) { Huff_EmitPathToByte((void**)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((void**)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((void**)huffTree[ch + 5], NULL, buffer); *count = huffBitPos; } /* ============ Huff_GetByte ============ */ int Huff_GetByte(qbyte *buffer, int *count) { int ch; huffBitPos = *count; ch = Huff_GetByteFromTree((void**)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); } huffCounts[i] = LittleLong(huffCounts[i]); } madetable=Com_BlockChecksum(huffCounts, sizeof(*huffCounts)*256); for(i=0;i<256;i++) huffCounts[i] = LittleLong(huffCounts[i]); } 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 != LittleLong(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