]> de.git.xonotic.org Git - xonotic/netradiant.git/blobdiff - tools/quake2/qdata/video.c
set eol-style
[xonotic/netradiant.git] / tools / quake2 / qdata / video.c
index 1ed1b96cd328a81dad520ae0478d4fb9c3fe9cbc..370f5e9597d1b9ade8be41e9458b5f33ff410ea6 100644 (file)
-#include "qdata.h"\r
-#include "inout.h"\r
-\r
-byte   *soundtrack;\r
-char   base[32];\r
-\r
-/*\r
-===============================================================================\r
-\r
-WAV loading\r
-\r
-===============================================================================\r
-*/\r
-\r
-typedef struct\r
-{\r
-       int                     rate;\r
-       int                     width;\r
-       int                     channels;\r
-       int                     loopstart;\r
-       int                     samples;\r
-       int                     dataofs;                // chunk starts this many bytes from file start\r
-} wavinfo_t;\r
-\r
-\r
-byte   *data_p;\r
-byte   *iff_end;\r
-byte   *last_chunk;\r
-byte   *iff_data;\r
-int    iff_chunk_len;\r
-\r
-\r
-int            samplecounts[0x10000];\r
-\r
-wavinfo_t      wavinfo;\r
-\r
-short GetLittleShort(void)\r
-{\r
-       short val = 0;\r
-       val = *data_p;\r
-       val = val + (*(data_p+1)<<8);\r
-       data_p += 2;\r
-       return val;\r
-}\r
-\r
-int GetLittleLong(void)\r
-{\r
-       int val = 0;\r
-       val = *data_p;\r
-       val = val + (*(data_p+1)<<8);\r
-       val = val + (*(data_p+2)<<16);\r
-       val = val + (*(data_p+3)<<24);\r
-       data_p += 4;\r
-       return val;\r
-}\r
-\r
-void FindNextChunk(char *name)\r
-{\r
-       while (1)\r
-       {\r
-               data_p=last_chunk;\r
-\r
-               if (data_p >= iff_end)\r
-               {       // didn't find the chunk\r
-                       data_p = NULL;\r
-                       return;\r
-               }\r
-               \r
-               data_p += 4;\r
-               iff_chunk_len = GetLittleLong();\r
-               if (iff_chunk_len < 0)\r
-               {\r
-                       data_p = NULL;\r
-                       return;\r
-               }\r
-//             if (iff_chunk_len > 1024*1024)\r
-//                     Sys_Error ("FindNextChunk: %i length is past the 1 meg sanity limit", iff_chunk_len);\r
-               data_p -= 8;\r
-               last_chunk = data_p + 8 + ( (iff_chunk_len + 1) & ~1 );\r
-               if (!strncmp(data_p, name, 4))\r
-                       return;\r
-       }\r
-}\r
-\r
-void FindChunk(char *name)\r
-{\r
-       last_chunk = iff_data;\r
-       FindNextChunk (name);\r
-}\r
-\r
-\r
-void DumpChunks(void)\r
-{\r
-       char    str[5];\r
-       \r
-       str[4] = 0;\r
-       data_p=iff_data;\r
-       do\r
-       {\r
-               memcpy (str, data_p, 4);\r
-               data_p += 4;\r
-               iff_chunk_len = GetLittleLong();\r
-               printf ("0x%x : %s (%d)\n", (int)(data_p - 4), str, iff_chunk_len);\r
-               data_p += (iff_chunk_len + 1) & ~1;\r
-       } while (data_p < iff_end);\r
-}\r
-\r
-/*\r
-============\r
-GetWavinfo\r
-============\r
-*/\r
-wavinfo_t GetWavinfo (char *name, byte *wav, int wavlength)\r
-{\r
-       wavinfo_t       info;\r
-       int     i;\r
-       int     format;\r
-       int             samples;\r
-\r
-       memset (&info, 0, sizeof(info));\r
-\r
-       if (!wav)\r
-               return info;\r
-               \r
-       iff_data = wav;\r
-       iff_end = wav + wavlength;\r
-\r
-// find "RIFF" chunk\r
-       FindChunk("RIFF");\r
-       if (!(data_p && !strncmp(data_p+8, "WAVE", 4)))\r
-       {\r
-               printf("Missing RIFF/WAVE chunks\n");\r
-               return info;\r
-       }\r
-\r
-// get "fmt " chunk\r
-       iff_data = data_p + 12;\r
-// DumpChunks ();\r
-\r
-       FindChunk("fmt ");\r
-       if (!data_p)\r
-       {\r
-               printf("Missing fmt chunk\n");\r
-               return info;\r
-       }\r
-       data_p += 8;\r
-       format = GetLittleShort();\r
-       if (format != 1)\r
-       {\r
-               printf("Microsoft PCM format only\n");\r
-               return info;\r
-       }\r
-\r
-       info.channels = GetLittleShort();\r
-       info.rate = GetLittleLong();\r
-       data_p += 4+2;\r
-       info.width = GetLittleShort() / 8;\r
-\r
-// get cue chunk\r
-       FindChunk("cue ");\r
-       if (data_p)\r
-       {\r
-               data_p += 32;\r
-               info.loopstart = GetLittleLong();\r
-//             Com_Printf("loopstart=%d\n", sfx->loopstart);\r
-\r
-       // if the next chunk is a LIST chunk, look for a cue length marker\r
-               FindNextChunk ("LIST");\r
-               if (data_p)\r
-               {\r
-                       if (!strncmp (data_p + 28, "mark", 4))\r
-                       {       // this is not a proper parse, but it works with cooledit...\r
-                               data_p += 24;\r
-                               i = GetLittleLong ();   // samples in loop\r
-                               info.samples = info.loopstart + i;\r
-                       }\r
-               }\r
-       }\r
-       else\r
-               info.loopstart = -1;\r
-\r
-// find data chunk\r
-       FindChunk("data");\r
-       if (!data_p)\r
-       {\r
-               printf("Missing data chunk\n");\r
-               return info;\r
-       }\r
-\r
-       data_p += 4;\r
-       samples = GetLittleLong ();\r
-\r
-       if (info.samples)\r
-       {\r
-               if (samples < info.samples)\r
-                       Error ("Sound %s has a bad loop length", name);\r
-       }\r
-       else\r
-               info.samples = samples;\r
-\r
-       info.dataofs = data_p - wav;\r
-\r
-       return info;\r
-}\r
-\r
-//=====================================================================\r
-\r
-/*\r
-==============\r
-LoadSoundtrack\r
-==============\r
-*/\r
-void LoadSoundtrack (void)\r
-{\r
-       char    name[1024];\r
-       FILE    *f;\r
-       int             len;\r
-       int     i, val, j;\r
-\r
-       soundtrack = NULL;\r
-       sprintf (name, "%svideo/%s/%s.wav", gamedir, base, base);\r
-       printf ("%s\n", name);\r
-       f = fopen (name, "rb");\r
-       if (!f)\r
-       {\r
-               printf ("no soundtrack for %s\n", base);\r
-               return;\r
-       }\r
-       len = Q_filelength(f);\r
-       soundtrack = malloc(len);\r
-       fread (soundtrack, 1, len, f);\r
-       fclose (f);\r
-\r
-       wavinfo = GetWavinfo (name, soundtrack, len);\r
-\r
-       // count samples for compression\r
-       memset (samplecounts, 0, sizeof(samplecounts));\r
-\r
-       j = wavinfo.samples/2;\r
-       for (i=0 ; i<j ; i++)\r
-       {\r
-               val = ((unsigned short *)( soundtrack + wavinfo.dataofs))[i];\r
-               samplecounts[val]++;\r
-       }\r
-       val = 0;\r
-       for (i=0 ; i<0x10000 ; i++)\r
-               if (samplecounts[i])\r
-                       val++;\r
-\r
-       printf ("%i unique sample values\n", val);\r
-}\r
-\r
-/*\r
-==================\r
-WriteSound\r
-==================\r
-*/\r
-void WriteSound (FILE *output, int frame)\r
-{\r
-       int             start, end;\r
-       int             count;\r
-       int             empty = 0;\r
-       int             i;\r
-       int             sample;\r
-       int             width;\r
-\r
-       width = wavinfo.width * wavinfo.channels;\r
-\r
-       start = frame*wavinfo.rate/14;\r
-       end = (frame+1)*wavinfo.rate/14;\r
-       count = end - start;\r
-\r
-       for (i=0 ; i<count ; i++)\r
-       {\r
-               sample = start+i;\r
-               if (sample > wavinfo.samples || !soundtrack)\r
-                       fwrite (&empty, 1, width, output);\r
-               else\r
-                       fwrite (soundtrack + wavinfo.dataofs + sample*width, 1, width,output);\r
-       }\r
-}\r
-\r
-//==========================================================================\r
-\r
-/*\r
-==================\r
-MTF\r
-==================\r
-*/\r
-cblock_t MTF (cblock_t in)\r
-{\r
-       int                     i, j, b, code;\r
-       byte            *out_p;\r
-       int                     index[256];\r
-       cblock_t        out;\r
-\r
-       out_p = out.data = malloc(in.count + 4);\r
-\r
-       // write count\r
-       *out_p++ = in.count&255;\r
-       *out_p++ = (in.count>>8)&255;\r
-       *out_p++ = (in.count>>16)&255;\r
-       *out_p++ = (in.count>>24)&255;\r
-\r
-       for (i=0 ; i<256 ; i++)\r
-               index[i] = i;\r
-\r
-       for (i=0 ; i<in.count ; i++)\r
-       {\r
-               b = in.data[i];\r
-               code = index[b];\r
-               *out_p++ = code;\r
-               \r
-               // shuffle b indexes to 0\r
-               for (j=0 ; j<256 ; j++)\r
-                       if (index[j] < code)\r
-                               index[j]++;\r
-               index[b] = 0;\r
-       }\r
-\r
-       out.count = out_p - out.data;\r
-\r
-       return out;\r
-}\r
-\r
-\r
-//==========================================================================\r
-\r
-int            bwt_size;\r
-byte   *bwt_data;\r
-\r
-int bwtCompare (const void *elem1, const void *elem2)\r
-{\r
-       int             i;\r
-       int             i1, i2;\r
-       int             b1, b2;\r
-\r
-       i1 = *(int *)elem1;\r
-       i2 = *(int *)elem2;\r
-\r
-       for (i=0 ; i<bwt_size ; i++)\r
-       {\r
-               b1 = bwt_data[i1];\r
-               b2 = bwt_data[i2];\r
-               if (b1 < b2)\r
-                       return -1;\r
-               if (b1 > b2)\r
-                       return 1;\r
-               if (++i1 == bwt_size)\r
-                       i1 = 0;\r
-               if (++i2 == bwt_size)\r
-                       i2 = 0;\r
-       }\r
-\r
-       return 0;\r
-}\r
-\r
-/*\r
-==================\r
-BWT\r
-==================\r
-*/\r
-cblock_t BWT (cblock_t in)\r
-{\r
-       int             *sorted;\r
-       int             i;\r
-       byte    *out_p;\r
-       cblock_t        out;\r
-\r
-       bwt_size = in.count;\r
-       bwt_data = in.data;\r
-\r
-       sorted = malloc(in.count*sizeof(*sorted));\r
-       for (i=0 ; i<in.count ; i++)\r
-               sorted[i] = i;\r
-       qsort (sorted, in.count, sizeof(*sorted), bwtCompare);\r
-\r
-       out_p = out.data = malloc(in.count + 8);\r
-\r
-       // write count\r
-       *out_p++ = in.count&255;\r
-       *out_p++ = (in.count>>8)&255;\r
-       *out_p++ = (in.count>>16)&255;\r
-       *out_p++ = (in.count>>24)&255;\r
-\r
-       // write head index\r
-       for (i=0 ; i<in.count ; i++)\r
-               if (sorted[i] == 0)\r
-                       break;\r
-       *out_p++ = i&255;\r
-       *out_p++ = (i>>8)&255;\r
-       *out_p++ = (i>>16)&255;\r
-       *out_p++ = (i>>24)&255;\r
-\r
-       // write the L column\r
-       for (i=0 ; i<in.count ; i++)\r
-               *out_p++ = in.data[(sorted[i]+in.count-1)%in.count];\r
-\r
-       free (sorted);\r
-\r
-       out.count = out_p - out.data;\r
-\r
-       return out;\r
-}\r
-\r
-//==========================================================================\r
-\r
-typedef struct hnode_s\r
-{\r
-       int                     count;\r
-       qboolean        used;\r
-       int                     children[2];\r
-} hnode_t;\r
-\r
-int                    numhnodes;\r
-hnode_t                hnodes[512];\r
-unsigned       charbits[256];\r
-int                    charbitscount[256];\r
-\r
-int    SmallestNode (void)\r
-{\r
-       int             i;\r
-       int             best, bestnode;\r
-\r
-       best = 99999999;\r
-       bestnode = -1;\r
-       for (i=0 ; i<numhnodes ; i++)\r
-       {\r
-               if (hnodes[i].used)\r
-                       continue;\r
-               if (!hnodes[i].count)\r
-                       continue;\r
-               if (hnodes[i].count < best)\r
-               {\r
-                       best = hnodes[i].count;\r
-                       bestnode = i;\r
-               }\r
-       }\r
-\r
-       if (bestnode == -1)\r
-               return -1;\r
-\r
-       hnodes[bestnode].used = true;\r
-       return bestnode;\r
-}\r
-\r
-void BuildChars (int nodenum, unsigned bits, int bitcount)\r
-{\r
-       hnode_t *node;\r
-\r
-       if (nodenum < 256)\r
-       {\r
-               if (bitcount > 32)\r
-                       Error ("bitcount > 32");\r
-               charbits[nodenum] = bits;\r
-               charbitscount[nodenum] = bitcount;\r
-               return;\r
-       }\r
-\r
-       node = &hnodes[nodenum];\r
-       bits <<= 1;\r
-       BuildChars (node->children[0], bits, bitcount+1);\r
-       bits |= 1;\r
-       BuildChars (node->children[1], bits, bitcount+1);\r
-}\r
-\r
-\r
-/*\r
-==================\r
-Huffman\r
-==================\r
-*/\r
-cblock_t Huffman (cblock_t in)\r
-{\r
-       int                     i;\r
-       hnode_t         *node;\r
-       int                     outbits, c;\r
-       unsigned        bits;\r
-       byte            *out_p;\r
-       cblock_t        out;\r
-       int                     max, maxchar;\r
-\r
-       // count\r
-       memset (hnodes, 0, sizeof(hnodes));\r
-       for (i=0 ; i<in.count ; i++)\r
-               hnodes[in.data[i]].count++;\r
-\r
-       // normalize counts\r
-       max = 0;\r
-       maxchar = 0;\r
-       for (i=0 ; i<256 ; i++)\r
-       {\r
-               if (hnodes[i].count > max)\r
-               {\r
-                       max = hnodes[i].count;\r
-                       maxchar = i;\r
-               }\r
-       }\r
-       if (max == 0)\r
-               Error ("Huffman: max == 0");\r
-\r
-       for (i=0 ; i<256 ; i++)\r
-       {\r
-               hnodes[i].count = (hnodes[i].count*255+max-1) / max;\r
-       }\r
-\r
-       // build the nodes\r
-       numhnodes = 256;\r
-       while (numhnodes != 511)\r
-       {\r
-               node = &hnodes[numhnodes];\r
-\r
-               // pick two lowest counts\r
-               node->children[0] = SmallestNode ();\r
-               if (node->children[0] == -1)\r
-                       break;  // no more\r
-\r
-               node->children[1] = SmallestNode ();\r
-               if (node->children[1] == -1)\r
-               {\r
-                       if (node->children[0] != numhnodes-1)\r
-                               Error ("Bad smallestnode");\r
-                       break;\r
-               }\r
-               node->count = hnodes[node->children[0]].count + \r
-                       hnodes[node->children[1]].count;\r
-               numhnodes++;\r
-       }\r
-\r
-       BuildChars (numhnodes-1, 0, 0);\r
-\r
-       out_p = out.data = malloc(in.count*2 + 1024);\r
-       memset (out_p, 0, in.count*2+1024);\r
-\r
-       // write count\r
-       *out_p++ = in.count&255;\r
-       *out_p++ = (in.count>>8)&255;\r
-       *out_p++ = (in.count>>16)&255;\r
-       *out_p++ = (in.count>>24)&255;\r
-\r
-       // save out the 256 normalized counts so the tree can be recreated\r
-       for (i=0 ; i<256 ; i++)\r
-               *out_p++ = hnodes[i].count;\r
-\r
-       // write bits\r
-       outbits = 0;\r
-       for (i=0 ; i<in.count ; i++)\r
-       {\r
-               c = charbitscount[in.data[i]];\r
-               bits = charbits[in.data[i]];\r
-               while (c)\r
-               {\r
-                       c--;\r
-                       if (bits & (1<<c))\r
-                               out_p[outbits>>3] |= 1<<(outbits&7);\r
-                       outbits++;\r
-               }\r
-       }\r
-\r
-       out_p += (outbits+7)>>3;\r
-\r
-       out.count = out_p - out.data;\r
-\r
-       return out;\r
-}\r
-\r
-//==========================================================================\r
-\r
-/*\r
-==================\r
-RLE\r
-==================\r
-*/\r
-#define        RLE_CODE        0xe8\r
-#define        RLE_TRIPPLE     0xe9\r
-\r
-int    rle_counts[256];\r
-int    rle_bytes[256];\r
-\r
-cblock_t RLE (cblock_t in)\r
-{\r
-       int             i;\r
-       byte    *out_p;\r
-       int             val;\r
-       int             repeat;\r
-       cblock_t        out;\r
-\r
-       out_p = out.data = malloc (in.count*2);\r
-\r
-       // write count\r
-       *out_p++ = in.count&255;\r
-       *out_p++ = (in.count>>8)&255;\r
-       *out_p++ = (in.count>>16)&255;\r
-       *out_p++ = (in.count>>24)&255;\r
-\r
-       for (i=0 ; i<in.count ; )\r
-       {\r
-               val = in.data[i];\r
-               rle_bytes[val]++;\r
-               repeat = 1;\r
-               i++;\r
-               while (i<in.count && repeat < 255 && in.data[i] == val)\r
-               {\r
-                       repeat++;\r
-                       i++;\r
-               }\r
-if (repeat < 256)\r
-rle_counts[repeat]++;\r
-               if (repeat > 3 || val == RLE_CODE)\r
-               {\r
-                       *out_p++ = RLE_CODE;\r
-                       *out_p++ = val;\r
-                       *out_p++ = repeat;\r
-               }\r
-               else\r
-               {\r
-                       while (repeat--)\r
-                               *out_p++ = val;\r
-               }\r
-       }\r
-\r
-       out.count = out_p - out.data;\r
-       return out;\r
-}\r
-\r
-//==========================================================================\r
-\r
-unsigned       lzss_head[256];\r
-unsigned       lzss_next[0x20000];\r
-\r
-/*\r
-==================\r
-LZSS\r
-==================\r
-*/\r
-#define        BACK_WINDOW             0x10000\r
-#define        BACK_BITS               16\r
-#define        FRONT_WINDOW    16\r
-#define        FRONT_BITS              4\r
-cblock_t LZSS (cblock_t in)\r
-{\r
-       int             i;\r
-       byte    *out_p;\r
-       cblock_t        out;\r
-       int             val;\r
-       int             j, start, max;\r
-       int             bestlength, beststart;\r
-       int             outbits;\r
-\r
-if (in.count >= sizeof(lzss_next)/4)\r
-Error ("LZSS: too big");\r
-\r
-       memset (lzss_head, -1, sizeof(lzss_head));\r
-\r
-       out_p = out.data = malloc (in.count*2);\r
-       memset (out.data, 0, in.count*2);\r
-\r
-       // write count\r
-       *out_p++ = in.count&255;\r
-       *out_p++ = (in.count>>8)&255;\r
-       *out_p++ = (in.count>>16)&255;\r
-       *out_p++ = (in.count>>24)&255;\r
-\r
-       outbits = 0;\r
-       for (i=0 ; i<in.count ; )\r
-       {\r
-               val = in.data[i];\r
-#if 1\r
-// chained search\r
-               bestlength = 0;\r
-               beststart = 0;\r
-\r
-               max = FRONT_WINDOW;\r
-               if (i + max > in.count)\r
-                       max = in.count - i;\r
-\r
-               start = lzss_head[val];\r
-               while (start != -1 && start >= i-BACK_WINDOW)\r
-               {                       \r
-                       // count match length\r
-                       for (j=0 ; j<max ; j++)\r
-                               if (in.data[start+j] != in.data[i+j])\r
-                                       break;\r
-                       if (j > bestlength)\r
-                       {\r
-                               bestlength = j;\r
-                               beststart = start;\r
-                       }\r
-                       start = lzss_next[start];\r
-               }\r
-\r
-#else\r
-// slow simple search\r
-               // search for a match\r
-               max = FRONT_WINDOW;\r
-               if (i + max > in.count)\r
-                       max = in.count - i;\r
-\r
-               start = i - BACK_WINDOW;\r
-               if (start < 0)\r
-                       start = 0;\r
-               bestlength = 0;\r
-               beststart = 0;\r
-               for ( ; start < i ; start++)\r
-               {\r
-                       if (in.data[start] != val)\r
-                               continue;\r
-                       // count match length\r
-                       for (j=0 ; j<max ; j++)\r
-                               if (in.data[start+j] != in.data[i+j])\r
-                                       break;\r
-                       if (j > bestlength)\r
-                       {\r
-                               bestlength = j;\r
-                               beststart = start;\r
-                       }\r
-               }\r
-#endif\r
-               beststart = BACK_WINDOW - (i-beststart);\r
-\r
-               if (bestlength < 3)\r
-               {       // output a single char\r
-                       bestlength = 1;\r
-\r
-                       out_p[outbits>>3] |= 1<<(outbits&7);    // set bit to mark char\r
-                       outbits++;\r
-                       for (j=0 ; j<8 ; j++, outbits++)\r
-                               if (val & (1<<j) )\r
-                                       out_p[outbits>>3] |= 1<<(outbits&7);\r
-               }\r
-               else\r
-               {       // output a phrase\r
-                       outbits++;      // leave a 0 bit to mark phrase\r
-                       for (j=0 ; j<BACK_BITS ; j++, outbits++)\r
-                               if (beststart & (1<<j) )\r
-                                       out_p[outbits>>3] |= 1<<(outbits&7);\r
-                       for (j=0 ; j<FRONT_BITS ; j++, outbits++)\r
-                               if (bestlength & (1<<j) )\r
-                                       out_p[outbits>>3] |= 1<<(outbits&7);\r
-               }\r
-\r
-               while (bestlength--)\r
-               {\r
-                       val = in.data[i];\r
-                       lzss_next[i] = lzss_head[val];\r
-                       lzss_head[val] = i;\r
-                       i++;\r
-               }\r
-       }\r
-\r
-       out_p += (outbits+7)>>3;\r
-       out.count = out_p - out.data;\r
-       return out;\r
-}\r
-\r
-//==========================================================================\r
-\r
-#define        MIN_REPT        15\r
-#define        MAX_REPT        0\r
-#define        HUF_TOKENS      (256+MAX_REPT)\r
-\r
-unsigned       charbits1[256][HUF_TOKENS];\r
-int                    charbitscount1[256][HUF_TOKENS];\r
-\r
-hnode_t                hnodes1[256][HUF_TOKENS*2];\r
-int                    numhnodes1[256];\r
-\r
-int                    order0counts[256];\r
-\r
-/*\r
-==================\r
-SmallestNode1\r
-==================\r
-*/\r
-int    SmallestNode1 (hnode_t *hnodes, int numhnodes)\r
-{\r
-       int             i;\r
-       int             best, bestnode;\r
-\r
-       best = 99999999;\r
-       bestnode = -1;\r
-       for (i=0 ; i<numhnodes ; i++)\r
-       {\r
-               if (hnodes[i].used)\r
-                       continue;\r
-               if (!hnodes[i].count)\r
-                       continue;\r
-               if (hnodes[i].count < best)\r
-               {\r
-                       best = hnodes[i].count;\r
-                       bestnode = i;\r
-               }\r
-       }\r
-\r
-       if (bestnode == -1)\r
-               return -1;\r
-\r
-       hnodes[bestnode].used = true;\r
-       return bestnode;\r
-}\r
-\r
-\r
-/*\r
-==================\r
-BuildChars1\r
-==================\r
-*/\r
-void BuildChars1 (int prev, int nodenum, unsigned bits, int bitcount)\r
-{\r
-       hnode_t *node;\r
-\r
-       if (nodenum < HUF_TOKENS)\r
-       {\r
-               if (bitcount > 32)\r
-                       Error ("bitcount > 32");\r
-               charbits1[prev][nodenum] = bits;\r
-               charbitscount1[prev][nodenum] = bitcount;\r
-               return;\r
-       }\r
-\r
-       node = &hnodes1[prev][nodenum];\r
-       bits <<= 1;\r
-       BuildChars1 (prev, node->children[0], bits, bitcount+1);\r
-       bits |= 1;\r
-       BuildChars1 (prev, node->children[1], bits, bitcount+1);\r
-}\r
-\r
-\r
-/*\r
-==================\r
-BuildTree1\r
-==================\r
-*/\r
-void BuildTree1 (int prev)\r
-{\r
-       hnode_t         *node, *nodebase;\r
-       int                     numhnodes;\r
-\r
-       // build the nodes\r
-       numhnodes = HUF_TOKENS;\r
-       nodebase = hnodes1[prev];\r
-       while (1)\r
-       {\r
-               node = &nodebase[numhnodes];\r
-\r
-               // pick two lowest counts\r
-               node->children[0] = SmallestNode1 (nodebase, numhnodes);\r
-               if (node->children[0] == -1)\r
-                       break;  // no more\r
-\r
-               node->children[1] = SmallestNode1 (nodebase, numhnodes);\r
-               if (node->children[1] == -1)\r
-                       break;\r
-\r
-               node->count = nodebase[node->children[0]].count + \r
-                       nodebase[node->children[1]].count;\r
-               numhnodes++;\r
-       }\r
-       numhnodes1[prev] = numhnodes-1;\r
-       BuildChars1 (prev, numhnodes-1, 0, 0);\r
-}\r
-\r
-\r
-/*\r
-==================\r
-Huffman1_Count\r
-==================\r
-*/\r
-void Huffman1_Count (cblock_t in)\r
-{\r
-       int             i;\r
-       int             prev;\r
-       int             v;\r
-       int             rept;\r
-\r
-       prev = 0;\r
-       for (i=0 ; i<in.count ; i++)\r
-       {\r
-               v = in.data[i];\r
-               order0counts[v]++;\r
-               hnodes1[prev][v].count++;\r
-               prev = v;\r
-#if 1\r
-               for (rept=1 ; i+rept < in.count && rept < MAX_REPT ; rept++)\r
-                       if (in.data[i+rept] != v)\r
-                               break;\r
-               if (rept > MIN_REPT)\r
-               {\r
-                       hnodes1[prev][255+rept].count++;\r
-                       i += rept-1;\r
-               }\r
-#endif\r
-       }\r
-}\r
-\r
-\r
-/*\r
-==================\r
-Huffman1_Build\r
-==================\r
-*/\r
-byte   scaled[256][HUF_TOKENS];\r
-void Huffman1_Build (FILE *f)\r
-{\r
-       int             i, j, v;\r
-       int             max;\r
-       int             total;\r
-\r
-       for (i=0 ; i<256 ; i++)\r
-       {\r
-               // normalize and save the counts\r
-               max = 0;\r
-               for (j=0 ; j<HUF_TOKENS ; j++)\r
-               {\r
-                       if (hnodes1[i][j].count > max)\r
-                               max = hnodes1[i][j].count;\r
-               }\r
-               if (max == 0)\r
-                       max = 1;\r
-               total = 0;\r
-               for (j=0 ; j<HUF_TOKENS ; j++)\r
-               {       // easy to overflow 32 bits here!\r
-                       v = (hnodes1[i][j].count*(double)255+max-1)/max;\r
-                       if (v > 255)\r
-                               Error ("v > 255");\r
-                       scaled[i][j] = hnodes1[i][j].count = v;\r
-                       if (v)\r
-                               total++;\r
-               }\r
-               if (total == 1)\r
-               {       // must have two tokens\r
-                       if (!scaled[i][0])\r
-                               scaled[i][0] = hnodes1[i][0].count = 1;\r
-                       else\r
-                               scaled[i][1] = hnodes1[i][1].count = 1;\r
-               }\r
-\r
-               BuildTree1 (i);\r
-       }\r
-\r
-#if 0\r
-       // count up the total bits\r
-       total = 0;\r
-       for (i=0 ; i<256 ; i++)\r
-               for (j=0 ; j<256 ; j++)\r
-                       total += charbitscount1[i][j] * hnodes1[i][j].count;\r
-\r
-       total = (total+7)/8;\r
-       printf ("%i bytes huffman1 compressed\n", total);\r
-#endif\r
-\r
-       fwrite (scaled, 1, sizeof(scaled), f);\r
-}\r
-\r
-/*\r
-==================\r
-Huffman1\r
-\r
-Order 1 compression with pre-built table\r
-==================\r
-*/\r
-cblock_t Huffman1 (cblock_t in)\r
-{\r
-       int                     i;\r
-       int                     outbits, c;\r
-       unsigned        bits;\r
-       byte            *out_p;\r
-       cblock_t        out;\r
-       int                     prev;\r
-       int                     v;\r
-       int                     rept;\r
-\r
-       out_p = out.data = malloc(in.count*2 + 1024);\r
-       memset (out_p, 0, in.count*2+1024);\r
-\r
-       // write count\r
-       *out_p++ = in.count&255;\r
-       *out_p++ = (in.count>>8)&255;\r
-       *out_p++ = (in.count>>16)&255;\r
-       *out_p++ = (in.count>>24)&255;\r
-\r
-       // write bits\r
-       outbits = 0;\r
-       prev = 0;\r
-       for (i=0 ; i<in.count ; i++)\r
-       {\r
-               v = in.data[i];\r
-\r
-               c = charbitscount1[prev][v];\r
-               bits = charbits1[prev][v];\r
-               if (!c)\r
-                       Error ("!bits");\r
-               while (c)\r
-               {\r
-                       c--;\r
-                       if (bits & (1<<c))\r
-                               out_p[outbits>>3] |= 1<<(outbits&7);\r
-                       outbits++;\r
-               }\r
-\r
-               prev = v;\r
-#if 1\r
-               // check for repeat encodes\r
-               for (rept=1 ; i+rept < in.count && rept < MAX_REPT ; rept++)\r
-                       if (in.data[i+rept] != v)\r
-                               break;\r
-               if (rept > MIN_REPT)\r
-               {\r
-                       c = charbitscount1[prev][255+rept];\r
-                       bits = charbits1[prev][255+rept];\r
-                       if (!c)\r
-                               Error ("!bits");\r
-                       while (c)\r
-                       {\r
-                               c--;\r
-                               if (bits & (1<<c))\r
-                                       out_p[outbits>>3] |= 1<<(outbits&7);\r
-                               outbits++;\r
-                       }\r
-                       i += rept-1;\r
-               }\r
-#endif\r
-       }\r
-\r
-       out_p += (outbits+7)>>3;\r
-\r
-       out.count = out_p - out.data;\r
-\r
-       return out;\r
-}\r
-\r
-//==========================================================================\r
-\r
-\r
-/*\r
-===================\r
-LoadFrame\r
-===================\r
-*/\r
-cblock_t LoadFrame (char *base, int frame, int digits, byte **palette)\r
-{\r
-       int                     ten3, ten2, ten1, ten0;\r
-       cblock_t        in;\r
-       int                     width, height;\r
-       char            name[1024];\r
-       FILE            *f;\r
-\r
-       in.data = NULL;\r
-       in.count = -1;\r
-\r
-       ten3 = frame/1000;\r
-       ten2 = (frame-ten3*1000)/100;\r
-       ten1 = (frame-ten3*1000-ten2*100)/10;\r
-       ten0 = frame%10;\r
-\r
-       if (digits == 4)\r
-               sprintf (name, "%svideo/%s/%s%i%i%i%i.pcx", gamedir, base, base, ten3, ten2, ten1, ten0);\r
-       else\r
-               sprintf (name, "%svideo/%s/%s%i%i%i.pcx", gamedir, base, base, ten2, ten1, ten0);\r
-\r
-       f = fopen(name, "rb");\r
-       if (!f)\r
-       {\r
-               in.data = NULL;\r
-               return in;\r
-       }\r
-       fclose (f);\r
-\r
-       printf ("%s\n", name);\r
-       Load256Image (name, &in.data, palette, &width, &height);\r
-       in.count = width*height;\r
-// FIXME: map 0 and 255!\r
-\r
-#if 0\r
-       // rle compress\r
-       rle = RLE(in);\r
-       free (in.data);\r
-\r
-       return rle;\r
-#endif\r
-\r
-       return in;\r
-}\r
-\r
-/*\r
-===============\r
-Cmd_Video\r
-\r
-video <directory> <framedigits>\r
-===============\r
-*/\r
-void Cmd_Video (void)\r
-{\r
-       char    savename[1024];\r
-       char    name[1024];\r
-       FILE    *output;\r
-       int             startframe, frame;\r
-       byte    *palette;\r
-       int             width, height;\r
-       byte    current_palette[768];\r
-       int             command;\r
-       int             i;\r
-       int             digits;\r
-       cblock_t        in, huffman;\r
-       int             swap;\r
-\r
-\r
-       GetToken (false);\r
-       strcpy (base, token);\r
-       if (g_release)\r
-       {\r
-//             sprintf (savename, "video/%s.cin", token);\r
-//             ReleaseFile (savename);\r
-               return;\r
-       }\r
-\r
-       GetToken (false);\r
-       digits = atoi(token);\r
-\r
-       // optionally skip frames\r
-       if (TokenAvailable ())\r
-       {\r
-               GetToken (false);\r
-               startframe = atoi(token);\r
-       }\r
-       else\r
-               startframe=0;\r
-\r
-       sprintf (savename, "%svideo/%s.cin", gamedir, base);\r
-\r
-\r
-       // clear stuff\r
-       memset (charbits1, 0, sizeof(charbits1));\r
-       memset (charbitscount1, 0, sizeof(charbitscount1));\r
-       memset (hnodes1, 0, sizeof(hnodes1));\r
-       memset (numhnodes1, 0, sizeof(numhnodes1));\r
-       memset (order0counts, 0, sizeof(order0counts));\r
-\r
-\r
-       // load the entire sound wav file if present\r
-       LoadSoundtrack ();\r
-\r
-       if (digits == 4)\r
-               sprintf (name, "%svideo/%s/%s0000.pcx", gamedir, base, base);\r
-       else\r
-               sprintf (name, "%svideo/%s/%s000.pcx", gamedir, base, base);\r
-\r
-       printf ("%s\n", name);\r
-       Load256Image (name, NULL, &palette, &width, &height);\r
-\r
-       output = fopen (savename, "wb");\r
-       if (!output)\r
-               Error ("Can't open %s", savename);\r
-\r
-       // write header info\r
-       i = LittleLong (width);\r
-       fwrite (&i, 4, 1, output);\r
-       i = LittleLong (height);\r
-       fwrite (&i, 4, 1, output);\r
-       i = LittleLong (wavinfo.rate);\r
-       fwrite (&i, 4, 1, output);\r
-       i = LittleLong (wavinfo.width);\r
-       fwrite (&i, 4, 1, output);\r
-       i = LittleLong (wavinfo.channels);\r
-       fwrite (&i, 4, 1, output);\r
-\r
-       // build the dictionary\r
-       for ( frame=startframe ;  ; frame++)\r
-       {\r
-               printf ("counting ", frame);\r
-               in = LoadFrame (base, frame, digits, &palette);\r
-               if (!in.data)\r
-                       break;\r
-               Huffman1_Count (in);\r
-               free (in.data);\r
-       }\r
-       printf ("\n");\r
-\r
-       // build nodes and write counts\r
-       Huffman1_Build (output);\r
-\r
-\r
-       memset (current_palette, 0, sizeof(current_palette));\r
-\r
-       // compress it with the dictionary\r
-       for (frame=startframe ;  ; frame++)\r
-       {\r
-               printf ("packing ", frame);\r
-               in = LoadFrame (base, frame, digits, &palette);\r
-               if (!in.data)\r
-                       break;\r
-\r
-               // see if the palette has changed\r
-               for (i=0 ; i<768 ; i++)\r
-                       if (palette[i] != current_palette[i])\r
-                       {\r
-                               // write a palette change\r
-                               memcpy (current_palette, palette, sizeof(current_palette));\r
-                               command = LittleLong(1);\r
-                               fwrite (&command, 1, 4, output);\r
-                               fwrite (current_palette, 1, sizeof(current_palette), output);\r
-                               break;\r
-                       }\r
-               if (i == 768)\r
-               {\r
-                       command = 0;    // no palette change\r
-                       fwrite (&command, 1, 4, output);\r
-               }\r
-\r
-               // save the image\r
-               huffman = Huffman1 (in);\r
-               printf ("%5i bytes after huffman1\n", huffman.count);\r
-\r
-               swap = LittleLong (huffman.count);\r
-               fwrite (&swap, 1, sizeof(swap), output);\r
-\r
-               fwrite (huffman.data, 1, huffman.count, output);\r
-\r
-               // save some sound samples\r
-               WriteSound (output, frame);\r
-\r
-               free (palette);\r
-               free (in.data);\r
-               free (huffman.data);\r
-       }\r
-       printf ("\n");\r
-\r
-       // write end-of-file command\r
-       command = 2;\r
-       fwrite (&command, 1, 4, output);\r
-\r
-       printf ("Total size: %i\n", ftell (output));\r
-\r
-       fclose (output);\r
-\r
-       if (soundtrack)\r
-               free (soundtrack);\r
-}\r
+#include "qdata.h"
+#include "inout.h"
+
+byte   *soundtrack;
+char   base[32];
+
+/*
+===============================================================================
+
+WAV loading
+
+===============================================================================
+*/
+
+typedef struct
+{
+       int                     rate;
+       int                     width;
+       int                     channels;
+       int                     loopstart;
+       int                     samples;
+       int                     dataofs;                // chunk starts this many bytes from file start
+} wavinfo_t;
+
+
+byte   *data_p;
+byte   *iff_end;
+byte   *last_chunk;
+byte   *iff_data;
+int    iff_chunk_len;
+
+
+int            samplecounts[0x10000];
+
+wavinfo_t      wavinfo;
+
+short GetLittleShort(void)
+{
+       short val = 0;
+       val = *data_p;
+       val = val + (*(data_p+1)<<8);
+       data_p += 2;
+       return val;
+}
+
+int GetLittleLong(void)
+{
+       int val = 0;
+       val = *data_p;
+       val = val + (*(data_p+1)<<8);
+       val = val + (*(data_p+2)<<16);
+       val = val + (*(data_p+3)<<24);
+       data_p += 4;
+       return val;
+}
+
+void FindNextChunk(char *name)
+{
+       while (1)
+       {
+               data_p=last_chunk;
+
+               if (data_p >= iff_end)
+               {       // didn't find the chunk
+                       data_p = NULL;
+                       return;
+               }
+               
+               data_p += 4;
+               iff_chunk_len = GetLittleLong();
+               if (iff_chunk_len < 0)
+               {
+                       data_p = NULL;
+                       return;
+               }
+//             if (iff_chunk_len > 1024*1024)
+//                     Sys_Error ("FindNextChunk: %i length is past the 1 meg sanity limit", iff_chunk_len);
+               data_p -= 8;
+               last_chunk = data_p + 8 + ( (iff_chunk_len + 1) & ~1 );
+               if (!strncmp(data_p, name, 4))
+                       return;
+       }
+}
+
+void FindChunk(char *name)
+{
+       last_chunk = iff_data;
+       FindNextChunk (name);
+}
+
+
+void DumpChunks(void)
+{
+       char    str[5];
+       
+       str[4] = 0;
+       data_p=iff_data;
+       do
+       {
+               memcpy (str, data_p, 4);
+               data_p += 4;
+               iff_chunk_len = GetLittleLong();
+               printf ("0x%x : %s (%d)\n", (int)(data_p - 4), str, iff_chunk_len);
+               data_p += (iff_chunk_len + 1) & ~1;
+       } while (data_p < iff_end);
+}
+
+/*
+============
+GetWavinfo
+============
+*/
+wavinfo_t GetWavinfo (char *name, byte *wav, int wavlength)
+{
+       wavinfo_t       info;
+       int     i;
+       int     format;
+       int             samples;
+
+       memset (&info, 0, sizeof(info));
+
+       if (!wav)
+               return info;
+               
+       iff_data = wav;
+       iff_end = wav + wavlength;
+
+// find "RIFF" chunk
+       FindChunk("RIFF");
+       if (!(data_p && !strncmp(data_p+8, "WAVE", 4)))
+       {
+               printf("Missing RIFF/WAVE chunks\n");
+               return info;
+       }
+
+// get "fmt " chunk
+       iff_data = data_p + 12;
+// DumpChunks ();
+
+       FindChunk("fmt ");
+       if (!data_p)
+       {
+               printf("Missing fmt chunk\n");
+               return info;
+       }
+       data_p += 8;
+       format = GetLittleShort();
+       if (format != 1)
+       {
+               printf("Microsoft PCM format only\n");
+               return info;
+       }
+
+       info.channels = GetLittleShort();
+       info.rate = GetLittleLong();
+       data_p += 4+2;
+       info.width = GetLittleShort() / 8;
+
+// get cue chunk
+       FindChunk("cue ");
+       if (data_p)
+       {
+               data_p += 32;
+               info.loopstart = GetLittleLong();
+//             Com_Printf("loopstart=%d\n", sfx->loopstart);
+
+       // if the next chunk is a LIST chunk, look for a cue length marker
+               FindNextChunk ("LIST");
+               if (data_p)
+               {
+                       if (!strncmp (data_p + 28, "mark", 4))
+                       {       // this is not a proper parse, but it works with cooledit...
+                               data_p += 24;
+                               i = GetLittleLong ();   // samples in loop
+                               info.samples = info.loopstart + i;
+                       }
+               }
+       }
+       else
+               info.loopstart = -1;
+
+// find data chunk
+       FindChunk("data");
+       if (!data_p)
+       {
+               printf("Missing data chunk\n");
+               return info;
+       }
+
+       data_p += 4;
+       samples = GetLittleLong ();
+
+       if (info.samples)
+       {
+               if (samples < info.samples)
+                       Error ("Sound %s has a bad loop length", name);
+       }
+       else
+               info.samples = samples;
+
+       info.dataofs = data_p - wav;
+
+       return info;
+}
+
+//=====================================================================
+
+/*
+==============
+LoadSoundtrack
+==============
+*/
+void LoadSoundtrack (void)
+{
+       char    name[1024];
+       FILE    *f;
+       int             len;
+       int     i, val, j;
+
+       soundtrack = NULL;
+       sprintf (name, "%svideo/%s/%s.wav", gamedir, base, base);
+       printf ("%s\n", name);
+       f = fopen (name, "rb");
+       if (!f)
+       {
+               printf ("no soundtrack for %s\n", base);
+               return;
+       }
+       len = Q_filelength(f);
+       soundtrack = malloc(len);
+       fread (soundtrack, 1, len, f);
+       fclose (f);
+
+       wavinfo = GetWavinfo (name, soundtrack, len);
+
+       // count samples for compression
+       memset (samplecounts, 0, sizeof(samplecounts));
+
+       j = wavinfo.samples/2;
+       for (i=0 ; i<j ; i++)
+       {
+               val = ((unsigned short *)( soundtrack + wavinfo.dataofs))[i];
+               samplecounts[val]++;
+       }
+       val = 0;
+       for (i=0 ; i<0x10000 ; i++)
+               if (samplecounts[i])
+                       val++;
+
+       printf ("%i unique sample values\n", val);
+}
+
+/*
+==================
+WriteSound
+==================
+*/
+void WriteSound (FILE *output, int frame)
+{
+       int             start, end;
+       int             count;
+       int             empty = 0;
+       int             i;
+       int             sample;
+       int             width;
+
+       width = wavinfo.width * wavinfo.channels;
+
+       start = frame*wavinfo.rate/14;
+       end = (frame+1)*wavinfo.rate/14;
+       count = end - start;
+
+       for (i=0 ; i<count ; i++)
+       {
+               sample = start+i;
+               if (sample > wavinfo.samples || !soundtrack)
+                       fwrite (&empty, 1, width, output);
+               else
+                       fwrite (soundtrack + wavinfo.dataofs + sample*width, 1, width,output);
+       }
+}
+
+//==========================================================================
+
+/*
+==================
+MTF
+==================
+*/
+cblock_t MTF (cblock_t in)
+{
+       int                     i, j, b, code;
+       byte            *out_p;
+       int                     index[256];
+       cblock_t        out;
+
+       out_p = out.data = malloc(in.count + 4);
+
+       // write count
+       *out_p++ = in.count&255;
+       *out_p++ = (in.count>>8)&255;
+       *out_p++ = (in.count>>16)&255;
+       *out_p++ = (in.count>>24)&255;
+
+       for (i=0 ; i<256 ; i++)
+               index[i] = i;
+
+       for (i=0 ; i<in.count ; i++)
+       {
+               b = in.data[i];
+               code = index[b];
+               *out_p++ = code;
+               
+               // shuffle b indexes to 0
+               for (j=0 ; j<256 ; j++)
+                       if (index[j] < code)
+                               index[j]++;
+               index[b] = 0;
+       }
+
+       out.count = out_p - out.data;
+
+       return out;
+}
+
+
+//==========================================================================
+
+int            bwt_size;
+byte   *bwt_data;
+
+int bwtCompare (const void *elem1, const void *elem2)
+{
+       int             i;
+       int             i1, i2;
+       int             b1, b2;
+
+       i1 = *(int *)elem1;
+       i2 = *(int *)elem2;
+
+       for (i=0 ; i<bwt_size ; i++)
+       {
+               b1 = bwt_data[i1];
+               b2 = bwt_data[i2];
+               if (b1 < b2)
+                       return -1;
+               if (b1 > b2)
+                       return 1;
+               if (++i1 == bwt_size)
+                       i1 = 0;
+               if (++i2 == bwt_size)
+                       i2 = 0;
+       }
+
+       return 0;
+}
+
+/*
+==================
+BWT
+==================
+*/
+cblock_t BWT (cblock_t in)
+{
+       int             *sorted;
+       int             i;
+       byte    *out_p;
+       cblock_t        out;
+
+       bwt_size = in.count;
+       bwt_data = in.data;
+
+       sorted = malloc(in.count*sizeof(*sorted));
+       for (i=0 ; i<in.count ; i++)
+               sorted[i] = i;
+       qsort (sorted, in.count, sizeof(*sorted), bwtCompare);
+
+       out_p = out.data = malloc(in.count + 8);
+
+       // write count
+       *out_p++ = in.count&255;
+       *out_p++ = (in.count>>8)&255;
+       *out_p++ = (in.count>>16)&255;
+       *out_p++ = (in.count>>24)&255;
+
+       // write head index
+       for (i=0 ; i<in.count ; i++)
+               if (sorted[i] == 0)
+                       break;
+       *out_p++ = i&255;
+       *out_p++ = (i>>8)&255;
+       *out_p++ = (i>>16)&255;
+       *out_p++ = (i>>24)&255;
+
+       // write the L column
+       for (i=0 ; i<in.count ; i++)
+               *out_p++ = in.data[(sorted[i]+in.count-1)%in.count];
+
+       free (sorted);
+
+       out.count = out_p - out.data;
+
+       return out;
+}
+
+//==========================================================================
+
+typedef struct hnode_s
+{
+       int                     count;
+       qboolean        used;
+       int                     children[2];
+} hnode_t;
+
+int                    numhnodes;
+hnode_t                hnodes[512];
+unsigned       charbits[256];
+int                    charbitscount[256];
+
+int    SmallestNode (void)
+{
+       int             i;
+       int             best, bestnode;
+
+       best = 99999999;
+       bestnode = -1;
+       for (i=0 ; i<numhnodes ; i++)
+       {
+               if (hnodes[i].used)
+                       continue;
+               if (!hnodes[i].count)
+                       continue;
+               if (hnodes[i].count < best)
+               {
+                       best = hnodes[i].count;
+                       bestnode = i;
+               }
+       }
+
+       if (bestnode == -1)
+               return -1;
+
+       hnodes[bestnode].used = true;
+       return bestnode;
+}
+
+void BuildChars (int nodenum, unsigned bits, int bitcount)
+{
+       hnode_t *node;
+
+       if (nodenum < 256)
+       {
+               if (bitcount > 32)
+                       Error ("bitcount > 32");
+               charbits[nodenum] = bits;
+               charbitscount[nodenum] = bitcount;
+               return;
+       }
+
+       node = &hnodes[nodenum];
+       bits <<= 1;
+       BuildChars (node->children[0], bits, bitcount+1);
+       bits |= 1;
+       BuildChars (node->children[1], bits, bitcount+1);
+}
+
+
+/*
+==================
+Huffman
+==================
+*/
+cblock_t Huffman (cblock_t in)
+{
+       int                     i;
+       hnode_t         *node;
+       int                     outbits, c;
+       unsigned        bits;
+       byte            *out_p;
+       cblock_t        out;
+       int                     max, maxchar;
+
+       // count
+       memset (hnodes, 0, sizeof(hnodes));
+       for (i=0 ; i<in.count ; i++)
+               hnodes[in.data[i]].count++;
+
+       // normalize counts
+       max = 0;
+       maxchar = 0;
+       for (i=0 ; i<256 ; i++)
+       {
+               if (hnodes[i].count > max)
+               {
+                       max = hnodes[i].count;
+                       maxchar = i;
+               }
+       }
+       if (max == 0)
+               Error ("Huffman: max == 0");
+
+       for (i=0 ; i<256 ; i++)
+       {
+               hnodes[i].count = (hnodes[i].count*255+max-1) / max;
+       }
+
+       // build the nodes
+       numhnodes = 256;
+       while (numhnodes != 511)
+       {
+               node = &hnodes[numhnodes];
+
+               // pick two lowest counts
+               node->children[0] = SmallestNode ();
+               if (node->children[0] == -1)
+                       break;  // no more
+
+               node->children[1] = SmallestNode ();
+               if (node->children[1] == -1)
+               {
+                       if (node->children[0] != numhnodes-1)
+                               Error ("Bad smallestnode");
+                       break;
+               }
+               node->count = hnodes[node->children[0]].count + 
+                       hnodes[node->children[1]].count;
+               numhnodes++;
+       }
+
+       BuildChars (numhnodes-1, 0, 0);
+
+       out_p = out.data = malloc(in.count*2 + 1024);
+       memset (out_p, 0, in.count*2+1024);
+
+       // write count
+       *out_p++ = in.count&255;
+       *out_p++ = (in.count>>8)&255;
+       *out_p++ = (in.count>>16)&255;
+       *out_p++ = (in.count>>24)&255;
+
+       // save out the 256 normalized counts so the tree can be recreated
+       for (i=0 ; i<256 ; i++)
+               *out_p++ = hnodes[i].count;
+
+       // write bits
+       outbits = 0;
+       for (i=0 ; i<in.count ; i++)
+       {
+               c = charbitscount[in.data[i]];
+               bits = charbits[in.data[i]];
+               while (c)
+               {
+                       c--;
+                       if (bits & (1<<c))
+                               out_p[outbits>>3] |= 1<<(outbits&7);
+                       outbits++;
+               }
+       }
+
+       out_p += (outbits+7)>>3;
+
+       out.count = out_p - out.data;
+
+       return out;
+}
+
+//==========================================================================
+
+/*
+==================
+RLE
+==================
+*/
+#define        RLE_CODE        0xe8
+#define        RLE_TRIPPLE     0xe9
+
+int    rle_counts[256];
+int    rle_bytes[256];
+
+cblock_t RLE (cblock_t in)
+{
+       int             i;
+       byte    *out_p;
+       int             val;
+       int             repeat;
+       cblock_t        out;
+
+       out_p = out.data = malloc (in.count*2);
+
+       // write count
+       *out_p++ = in.count&255;
+       *out_p++ = (in.count>>8)&255;
+       *out_p++ = (in.count>>16)&255;
+       *out_p++ = (in.count>>24)&255;
+
+       for (i=0 ; i<in.count ; )
+       {
+               val = in.data[i];
+               rle_bytes[val]++;
+               repeat = 1;
+               i++;
+               while (i<in.count && repeat < 255 && in.data[i] == val)
+               {
+                       repeat++;
+                       i++;
+               }
+if (repeat < 256)
+rle_counts[repeat]++;
+               if (repeat > 3 || val == RLE_CODE)
+               {
+                       *out_p++ = RLE_CODE;
+                       *out_p++ = val;
+                       *out_p++ = repeat;
+               }
+               else
+               {
+                       while (repeat--)
+                               *out_p++ = val;
+               }
+       }
+
+       out.count = out_p - out.data;
+       return out;
+}
+
+//==========================================================================
+
+unsigned       lzss_head[256];
+unsigned       lzss_next[0x20000];
+
+/*
+==================
+LZSS
+==================
+*/
+#define        BACK_WINDOW             0x10000
+#define        BACK_BITS               16
+#define        FRONT_WINDOW    16
+#define        FRONT_BITS              4
+cblock_t LZSS (cblock_t in)
+{
+       int             i;
+       byte    *out_p;
+       cblock_t        out;
+       int             val;
+       int             j, start, max;
+       int             bestlength, beststart;
+       int             outbits;
+
+if (in.count >= sizeof(lzss_next)/4)
+Error ("LZSS: too big");
+
+       memset (lzss_head, -1, sizeof(lzss_head));
+
+       out_p = out.data = malloc (in.count*2);
+       memset (out.data, 0, in.count*2);
+
+       // write count
+       *out_p++ = in.count&255;
+       *out_p++ = (in.count>>8)&255;
+       *out_p++ = (in.count>>16)&255;
+       *out_p++ = (in.count>>24)&255;
+
+       outbits = 0;
+       for (i=0 ; i<in.count ; )
+       {
+               val = in.data[i];
+#if 1
+// chained search
+               bestlength = 0;
+               beststart = 0;
+
+               max = FRONT_WINDOW;
+               if (i + max > in.count)
+                       max = in.count - i;
+
+               start = lzss_head[val];
+               while (start != -1 && start >= i-BACK_WINDOW)
+               {                       
+                       // count match length
+                       for (j=0 ; j<max ; j++)
+                               if (in.data[start+j] != in.data[i+j])
+                                       break;
+                       if (j > bestlength)
+                       {
+                               bestlength = j;
+                               beststart = start;
+                       }
+                       start = lzss_next[start];
+               }
+
+#else
+// slow simple search
+               // search for a match
+               max = FRONT_WINDOW;
+               if (i + max > in.count)
+                       max = in.count - i;
+
+               start = i - BACK_WINDOW;
+               if (start < 0)
+                       start = 0;
+               bestlength = 0;
+               beststart = 0;
+               for ( ; start < i ; start++)
+               {
+                       if (in.data[start] != val)
+                               continue;
+                       // count match length
+                       for (j=0 ; j<max ; j++)
+                               if (in.data[start+j] != in.data[i+j])
+                                       break;
+                       if (j > bestlength)
+                       {
+                               bestlength = j;
+                               beststart = start;
+                       }
+               }
+#endif
+               beststart = BACK_WINDOW - (i-beststart);
+
+               if (bestlength < 3)
+               {       // output a single char
+                       bestlength = 1;
+
+                       out_p[outbits>>3] |= 1<<(outbits&7);    // set bit to mark char
+                       outbits++;
+                       for (j=0 ; j<8 ; j++, outbits++)
+                               if (val & (1<<j) )
+                                       out_p[outbits>>3] |= 1<<(outbits&7);
+               }
+               else
+               {       // output a phrase
+                       outbits++;      // leave a 0 bit to mark phrase
+                       for (j=0 ; j<BACK_BITS ; j++, outbits++)
+                               if (beststart & (1<<j) )
+                                       out_p[outbits>>3] |= 1<<(outbits&7);
+                       for (j=0 ; j<FRONT_BITS ; j++, outbits++)
+                               if (bestlength & (1<<j) )
+                                       out_p[outbits>>3] |= 1<<(outbits&7);
+               }
+
+               while (bestlength--)
+               {
+                       val = in.data[i];
+                       lzss_next[i] = lzss_head[val];
+                       lzss_head[val] = i;
+                       i++;
+               }
+       }
+
+       out_p += (outbits+7)>>3;
+       out.count = out_p - out.data;
+       return out;
+}
+
+//==========================================================================
+
+#define        MIN_REPT        15
+#define        MAX_REPT        0
+#define        HUF_TOKENS      (256+MAX_REPT)
+
+unsigned       charbits1[256][HUF_TOKENS];
+int                    charbitscount1[256][HUF_TOKENS];
+
+hnode_t                hnodes1[256][HUF_TOKENS*2];
+int                    numhnodes1[256];
+
+int                    order0counts[256];
+
+/*
+==================
+SmallestNode1
+==================
+*/
+int    SmallestNode1 (hnode_t *hnodes, int numhnodes)
+{
+       int             i;
+       int             best, bestnode;
+
+       best = 99999999;
+       bestnode = -1;
+       for (i=0 ; i<numhnodes ; i++)
+       {
+               if (hnodes[i].used)
+                       continue;
+               if (!hnodes[i].count)
+                       continue;
+               if (hnodes[i].count < best)
+               {
+                       best = hnodes[i].count;
+                       bestnode = i;
+               }
+       }
+
+       if (bestnode == -1)
+               return -1;
+
+       hnodes[bestnode].used = true;
+       return bestnode;
+}
+
+
+/*
+==================
+BuildChars1
+==================
+*/
+void BuildChars1 (int prev, int nodenum, unsigned bits, int bitcount)
+{
+       hnode_t *node;
+
+       if (nodenum < HUF_TOKENS)
+       {
+               if (bitcount > 32)
+                       Error ("bitcount > 32");
+               charbits1[prev][nodenum] = bits;
+               charbitscount1[prev][nodenum] = bitcount;
+               return;
+       }
+
+       node = &hnodes1[prev][nodenum];
+       bits <<= 1;
+       BuildChars1 (prev, node->children[0], bits, bitcount+1);
+       bits |= 1;
+       BuildChars1 (prev, node->children[1], bits, bitcount+1);
+}
+
+
+/*
+==================
+BuildTree1
+==================
+*/
+void BuildTree1 (int prev)
+{
+       hnode_t         *node, *nodebase;
+       int                     numhnodes;
+
+       // build the nodes
+       numhnodes = HUF_TOKENS;
+       nodebase = hnodes1[prev];
+       while (1)
+       {
+               node = &nodebase[numhnodes];
+
+               // pick two lowest counts
+               node->children[0] = SmallestNode1 (nodebase, numhnodes);
+               if (node->children[0] == -1)
+                       break;  // no more
+
+               node->children[1] = SmallestNode1 (nodebase, numhnodes);
+               if (node->children[1] == -1)
+                       break;
+
+               node->count = nodebase[node->children[0]].count + 
+                       nodebase[node->children[1]].count;
+               numhnodes++;
+       }
+       numhnodes1[prev] = numhnodes-1;
+       BuildChars1 (prev, numhnodes-1, 0, 0);
+}
+
+
+/*
+==================
+Huffman1_Count
+==================
+*/
+void Huffman1_Count (cblock_t in)
+{
+       int             i;
+       int             prev;
+       int             v;
+       int             rept;
+
+       prev = 0;
+       for (i=0 ; i<in.count ; i++)
+       {
+               v = in.data[i];
+               order0counts[v]++;
+               hnodes1[prev][v].count++;
+               prev = v;
+#if 1
+               for (rept=1 ; i+rept < in.count && rept < MAX_REPT ; rept++)
+                       if (in.data[i+rept] != v)
+                               break;
+               if (rept > MIN_REPT)
+               {
+                       hnodes1[prev][255+rept].count++;
+                       i += rept-1;
+               }
+#endif
+       }
+}
+
+
+/*
+==================
+Huffman1_Build
+==================
+*/
+byte   scaled[256][HUF_TOKENS];
+void Huffman1_Build (FILE *f)
+{
+       int             i, j, v;
+       int             max;
+       int             total;
+
+       for (i=0 ; i<256 ; i++)
+       {
+               // normalize and save the counts
+               max = 0;
+               for (j=0 ; j<HUF_TOKENS ; j++)
+               {
+                       if (hnodes1[i][j].count > max)
+                               max = hnodes1[i][j].count;
+               }
+               if (max == 0)
+                       max = 1;
+               total = 0;
+               for (j=0 ; j<HUF_TOKENS ; j++)
+               {       // easy to overflow 32 bits here!
+                       v = (hnodes1[i][j].count*(double)255+max-1)/max;
+                       if (v > 255)
+                               Error ("v > 255");
+                       scaled[i][j] = hnodes1[i][j].count = v;
+                       if (v)
+                               total++;
+               }
+               if (total == 1)
+               {       // must have two tokens
+                       if (!scaled[i][0])
+                               scaled[i][0] = hnodes1[i][0].count = 1;
+                       else
+                               scaled[i][1] = hnodes1[i][1].count = 1;
+               }
+
+               BuildTree1 (i);
+       }
+
+#if 0
+       // count up the total bits
+       total = 0;
+       for (i=0 ; i<256 ; i++)
+               for (j=0 ; j<256 ; j++)
+                       total += charbitscount1[i][j] * hnodes1[i][j].count;
+
+       total = (total+7)/8;
+       printf ("%i bytes huffman1 compressed\n", total);
+#endif
+
+       fwrite (scaled, 1, sizeof(scaled), f);
+}
+
+/*
+==================
+Huffman1
+
+Order 1 compression with pre-built table
+==================
+*/
+cblock_t Huffman1 (cblock_t in)
+{
+       int                     i;
+       int                     outbits, c;
+       unsigned        bits;
+       byte            *out_p;
+       cblock_t        out;
+       int                     prev;
+       int                     v;
+       int                     rept;
+
+       out_p = out.data = malloc(in.count*2 + 1024);
+       memset (out_p, 0, in.count*2+1024);
+
+       // write count
+       *out_p++ = in.count&255;
+       *out_p++ = (in.count>>8)&255;
+       *out_p++ = (in.count>>16)&255;
+       *out_p++ = (in.count>>24)&255;
+
+       // write bits
+       outbits = 0;
+       prev = 0;
+       for (i=0 ; i<in.count ; i++)
+       {
+               v = in.data[i];
+
+               c = charbitscount1[prev][v];
+               bits = charbits1[prev][v];
+               if (!c)
+                       Error ("!bits");
+               while (c)
+               {
+                       c--;
+                       if (bits & (1<<c))
+                               out_p[outbits>>3] |= 1<<(outbits&7);
+                       outbits++;
+               }
+
+               prev = v;
+#if 1
+               // check for repeat encodes
+               for (rept=1 ; i+rept < in.count && rept < MAX_REPT ; rept++)
+                       if (in.data[i+rept] != v)
+                               break;
+               if (rept > MIN_REPT)
+               {
+                       c = charbitscount1[prev][255+rept];
+                       bits = charbits1[prev][255+rept];
+                       if (!c)
+                               Error ("!bits");
+                       while (c)
+                       {
+                               c--;
+                               if (bits & (1<<c))
+                                       out_p[outbits>>3] |= 1<<(outbits&7);
+                               outbits++;
+                       }
+                       i += rept-1;
+               }
+#endif
+       }
+
+       out_p += (outbits+7)>>3;
+
+       out.count = out_p - out.data;
+
+       return out;
+}
+
+//==========================================================================
+
+
+/*
+===================
+LoadFrame
+===================
+*/
+cblock_t LoadFrame (char *base, int frame, int digits, byte **palette)
+{
+       int                     ten3, ten2, ten1, ten0;
+       cblock_t        in;
+       int                     width, height;
+       char            name[1024];
+       FILE            *f;
+
+       in.data = NULL;
+       in.count = -1;
+
+       ten3 = frame/1000;
+       ten2 = (frame-ten3*1000)/100;
+       ten1 = (frame-ten3*1000-ten2*100)/10;
+       ten0 = frame%10;
+
+       if (digits == 4)
+               sprintf (name, "%svideo/%s/%s%i%i%i%i.pcx", gamedir, base, base, ten3, ten2, ten1, ten0);
+       else
+               sprintf (name, "%svideo/%s/%s%i%i%i.pcx", gamedir, base, base, ten2, ten1, ten0);
+
+       f = fopen(name, "rb");
+       if (!f)
+       {
+               in.data = NULL;
+               return in;
+       }
+       fclose (f);
+
+       printf ("%s\n", name);
+       Load256Image (name, &in.data, palette, &width, &height);
+       in.count = width*height;
+// FIXME: map 0 and 255!
+
+#if 0
+       // rle compress
+       rle = RLE(in);
+       free (in.data);
+
+       return rle;
+#endif
+
+       return in;
+}
+
+/*
+===============
+Cmd_Video
+
+video <directory> <framedigits>
+===============
+*/
+void Cmd_Video (void)
+{
+       char    savename[1024];
+       char    name[1024];
+       FILE    *output;
+       int             startframe, frame;
+       byte    *palette;
+       int             width, height;
+       byte    current_palette[768];
+       int             command;
+       int             i;
+       int             digits;
+       cblock_t        in, huffman;
+       int             swap;
+
+
+       GetToken (false);
+       strcpy (base, token);
+       if (g_release)
+       {
+//             sprintf (savename, "video/%s.cin", token);
+//             ReleaseFile (savename);
+               return;
+       }
+
+       GetToken (false);
+       digits = atoi(token);
+
+       // optionally skip frames
+       if (TokenAvailable ())
+       {
+               GetToken (false);
+               startframe = atoi(token);
+       }
+       else
+               startframe=0;
+
+       sprintf (savename, "%svideo/%s.cin", gamedir, base);
+
+
+       // clear stuff
+       memset (charbits1, 0, sizeof(charbits1));
+       memset (charbitscount1, 0, sizeof(charbitscount1));
+       memset (hnodes1, 0, sizeof(hnodes1));
+       memset (numhnodes1, 0, sizeof(numhnodes1));
+       memset (order0counts, 0, sizeof(order0counts));
+
+
+       // load the entire sound wav file if present
+       LoadSoundtrack ();
+
+       if (digits == 4)
+               sprintf (name, "%svideo/%s/%s0000.pcx", gamedir, base, base);
+       else
+               sprintf (name, "%svideo/%s/%s000.pcx", gamedir, base, base);
+
+       printf ("%s\n", name);
+       Load256Image (name, NULL, &palette, &width, &height);
+
+       output = fopen (savename, "wb");
+       if (!output)
+               Error ("Can't open %s", savename);
+
+       // write header info
+       i = LittleLong (width);
+       fwrite (&i, 4, 1, output);
+       i = LittleLong (height);
+       fwrite (&i, 4, 1, output);
+       i = LittleLong (wavinfo.rate);
+       fwrite (&i, 4, 1, output);
+       i = LittleLong (wavinfo.width);
+       fwrite (&i, 4, 1, output);
+       i = LittleLong (wavinfo.channels);
+       fwrite (&i, 4, 1, output);
+
+       // build the dictionary
+       for ( frame=startframe ;  ; frame++)
+       {
+               printf ("counting ", frame);
+               in = LoadFrame (base, frame, digits, &palette);
+               if (!in.data)
+                       break;
+               Huffman1_Count (in);
+               free (in.data);
+       }
+       printf ("\n");
+
+       // build nodes and write counts
+       Huffman1_Build (output);
+
+
+       memset (current_palette, 0, sizeof(current_palette));
+
+       // compress it with the dictionary
+       for (frame=startframe ;  ; frame++)
+       {
+               printf ("packing ", frame);
+               in = LoadFrame (base, frame, digits, &palette);
+               if (!in.data)
+                       break;
+
+               // see if the palette has changed
+               for (i=0 ; i<768 ; i++)
+                       if (palette[i] != current_palette[i])
+                       {
+                               // write a palette change
+                               memcpy (current_palette, palette, sizeof(current_palette));
+                               command = LittleLong(1);
+                               fwrite (&command, 1, 4, output);
+                               fwrite (current_palette, 1, sizeof(current_palette), output);
+                               break;
+                       }
+               if (i == 768)
+               {
+                       command = 0;    // no palette change
+                       fwrite (&command, 1, 4, output);
+               }
+
+               // save the image
+               huffman = Huffman1 (in);
+               printf ("%5i bytes after huffman1\n", huffman.count);
+
+               swap = LittleLong (huffman.count);
+               fwrite (&swap, 1, sizeof(swap), output);
+
+               fwrite (huffman.data, 1, huffman.count, output);
+
+               // save some sound samples
+               WriteSound (output, frame);
+
+               free (palette);
+               free (in.data);
+               free (huffman.data);
+       }
+       printf ("\n");
+
+       // write end-of-file command
+       command = 2;
+       fwrite (&command, 1, 4, output);
+
+       printf ("Total size: %i\n", ftell (output));
+
+       fclose (output);
+
+       if (soundtrack)
+               free (soundtrack);
+}