8 ===============================================================================
\r
12 ===============================================================================
\r
22 int dataofs; // chunk starts this many bytes from file start
\r
33 int samplecounts[0x10000];
\r
37 short GetLittleShort(void)
\r
41 val = val + (*(data_p+1)<<8);
\r
46 int GetLittleLong(void)
\r
50 val = val + (*(data_p+1)<<8);
\r
51 val = val + (*(data_p+2)<<16);
\r
52 val = val + (*(data_p+3)<<24);
\r
57 void FindNextChunk(char *name)
\r
63 if (data_p >= iff_end)
\r
64 { // didn't find the chunk
\r
70 iff_chunk_len = GetLittleLong();
\r
71 if (iff_chunk_len < 0)
\r
76 // if (iff_chunk_len > 1024*1024)
\r
77 // Sys_Error ("FindNextChunk: %i length is past the 1 meg sanity limit", iff_chunk_len);
\r
79 last_chunk = data_p + 8 + ( (iff_chunk_len + 1) & ~1 );
\r
80 if (!strncmp(data_p, name, 4))
\r
85 void FindChunk(char *name)
\r
87 last_chunk = iff_data;
\r
88 FindNextChunk (name);
\r
92 void DumpChunks(void)
\r
100 memcpy (str, data_p, 4);
\r
102 iff_chunk_len = GetLittleLong();
\r
103 printf ("0x%x : %s (%d)\n", (int)(data_p - 4), str, iff_chunk_len);
\r
104 data_p += (iff_chunk_len + 1) & ~1;
\r
105 } while (data_p < iff_end);
\r
113 wavinfo_t GetWavinfo (char *name, byte *wav, int wavlength)
\r
120 memset (&info, 0, sizeof(info));
\r
126 iff_end = wav + wavlength;
\r
128 // find "RIFF" chunk
\r
130 if (!(data_p && !strncmp(data_p+8, "WAVE", 4)))
\r
132 printf("Missing RIFF/WAVE chunks\n");
\r
136 // get "fmt " chunk
\r
137 iff_data = data_p + 12;
\r
143 printf("Missing fmt chunk\n");
\r
147 format = GetLittleShort();
\r
150 printf("Microsoft PCM format only\n");
\r
154 info.channels = GetLittleShort();
\r
155 info.rate = GetLittleLong();
\r
157 info.width = GetLittleShort() / 8;
\r
164 info.loopstart = GetLittleLong();
\r
165 // Com_Printf("loopstart=%d\n", sfx->loopstart);
\r
167 // if the next chunk is a LIST chunk, look for a cue length marker
\r
168 FindNextChunk ("LIST");
\r
171 if (!strncmp (data_p + 28, "mark", 4))
\r
172 { // this is not a proper parse, but it works with cooledit...
\r
174 i = GetLittleLong (); // samples in loop
\r
175 info.samples = info.loopstart + i;
\r
180 info.loopstart = -1;
\r
186 printf("Missing data chunk\n");
\r
191 samples = GetLittleLong ();
\r
195 if (samples < info.samples)
\r
196 Error ("Sound %s has a bad loop length", name);
\r
199 info.samples = samples;
\r
201 info.dataofs = data_p - wav;
\r
206 //=====================================================================
\r
213 void LoadSoundtrack (void)
\r
221 sprintf (name, "%svideo/%s/%s.wav", gamedir, base, base);
\r
222 printf ("%s\n", name);
\r
223 f = fopen (name, "rb");
\r
226 printf ("no soundtrack for %s\n", base);
\r
229 len = Q_filelength(f);
\r
230 soundtrack = malloc(len);
\r
231 fread (soundtrack, 1, len, f);
\r
234 wavinfo = GetWavinfo (name, soundtrack, len);
\r
236 // count samples for compression
\r
237 memset (samplecounts, 0, sizeof(samplecounts));
\r
239 j = wavinfo.samples/2;
\r
240 for (i=0 ; i<j ; i++)
\r
242 val = ((unsigned short *)( soundtrack + wavinfo.dataofs))[i];
\r
243 samplecounts[val]++;
\r
246 for (i=0 ; i<0x10000 ; i++)
\r
247 if (samplecounts[i])
\r
250 printf ("%i unique sample values\n", val);
\r
258 void WriteSound (FILE *output, int frame)
\r
267 width = wavinfo.width * wavinfo.channels;
\r
269 start = frame*wavinfo.rate/14;
\r
270 end = (frame+1)*wavinfo.rate/14;
\r
271 count = end - start;
\r
273 for (i=0 ; i<count ; i++)
\r
276 if (sample > wavinfo.samples || !soundtrack)
\r
277 fwrite (&empty, 1, width, output);
\r
279 fwrite (soundtrack + wavinfo.dataofs + sample*width, 1, width,output);
\r
283 //==========================================================================
\r
290 cblock_t MTF (cblock_t in)
\r
297 out_p = out.data = malloc(in.count + 4);
\r
300 *out_p++ = in.count&255;
\r
301 *out_p++ = (in.count>>8)&255;
\r
302 *out_p++ = (in.count>>16)&255;
\r
303 *out_p++ = (in.count>>24)&255;
\r
305 for (i=0 ; i<256 ; i++)
\r
308 for (i=0 ; i<in.count ; i++)
\r
314 // shuffle b indexes to 0
\r
315 for (j=0 ; j<256 ; j++)
\r
316 if (index[j] < code)
\r
321 out.count = out_p - out.data;
\r
327 //==========================================================================
\r
332 int bwtCompare (const void *elem1, const void *elem2)
\r
338 i1 = *(int *)elem1;
\r
339 i2 = *(int *)elem2;
\r
341 for (i=0 ; i<bwt_size ; i++)
\r
349 if (++i1 == bwt_size)
\r
351 if (++i2 == bwt_size)
\r
363 cblock_t BWT (cblock_t in)
\r
370 bwt_size = in.count;
\r
371 bwt_data = in.data;
\r
373 sorted = malloc(in.count*sizeof(*sorted));
\r
374 for (i=0 ; i<in.count ; i++)
\r
376 qsort (sorted, in.count, sizeof(*sorted), bwtCompare);
\r
378 out_p = out.data = malloc(in.count + 8);
\r
381 *out_p++ = in.count&255;
\r
382 *out_p++ = (in.count>>8)&255;
\r
383 *out_p++ = (in.count>>16)&255;
\r
384 *out_p++ = (in.count>>24)&255;
\r
386 // write head index
\r
387 for (i=0 ; i<in.count ; i++)
\r
388 if (sorted[i] == 0)
\r
391 *out_p++ = (i>>8)&255;
\r
392 *out_p++ = (i>>16)&255;
\r
393 *out_p++ = (i>>24)&255;
\r
395 // write the L column
\r
396 for (i=0 ; i<in.count ; i++)
\r
397 *out_p++ = in.data[(sorted[i]+in.count-1)%in.count];
\r
401 out.count = out_p - out.data;
\r
406 //==========================================================================
\r
408 typedef struct hnode_s
\r
416 hnode_t hnodes[512];
\r
417 unsigned charbits[256];
\r
418 int charbitscount[256];
\r
420 int SmallestNode (void)
\r
423 int best, bestnode;
\r
427 for (i=0 ; i<numhnodes ; i++)
\r
429 if (hnodes[i].used)
\r
431 if (!hnodes[i].count)
\r
433 if (hnodes[i].count < best)
\r
435 best = hnodes[i].count;
\r
440 if (bestnode == -1)
\r
443 hnodes[bestnode].used = true;
\r
447 void BuildChars (int nodenum, unsigned bits, int bitcount)
\r
454 Error ("bitcount > 32");
\r
455 charbits[nodenum] = bits;
\r
456 charbitscount[nodenum] = bitcount;
\r
460 node = &hnodes[nodenum];
\r
462 BuildChars (node->children[0], bits, bitcount+1);
\r
464 BuildChars (node->children[1], bits, bitcount+1);
\r
473 cblock_t Huffman (cblock_t in)
\r
484 memset (hnodes, 0, sizeof(hnodes));
\r
485 for (i=0 ; i<in.count ; i++)
\r
486 hnodes[in.data[i]].count++;
\r
488 // normalize counts
\r
491 for (i=0 ; i<256 ; i++)
\r
493 if (hnodes[i].count > max)
\r
495 max = hnodes[i].count;
\r
500 Error ("Huffman: max == 0");
\r
502 for (i=0 ; i<256 ; i++)
\r
504 hnodes[i].count = (hnodes[i].count*255+max-1) / max;
\r
509 while (numhnodes != 511)
\r
511 node = &hnodes[numhnodes];
\r
513 // pick two lowest counts
\r
514 node->children[0] = SmallestNode ();
\r
515 if (node->children[0] == -1)
\r
518 node->children[1] = SmallestNode ();
\r
519 if (node->children[1] == -1)
\r
521 if (node->children[0] != numhnodes-1)
\r
522 Error ("Bad smallestnode");
\r
525 node->count = hnodes[node->children[0]].count +
\r
526 hnodes[node->children[1]].count;
\r
530 BuildChars (numhnodes-1, 0, 0);
\r
532 out_p = out.data = malloc(in.count*2 + 1024);
\r
533 memset (out_p, 0, in.count*2+1024);
\r
536 *out_p++ = in.count&255;
\r
537 *out_p++ = (in.count>>8)&255;
\r
538 *out_p++ = (in.count>>16)&255;
\r
539 *out_p++ = (in.count>>24)&255;
\r
541 // save out the 256 normalized counts so the tree can be recreated
\r
542 for (i=0 ; i<256 ; i++)
\r
543 *out_p++ = hnodes[i].count;
\r
547 for (i=0 ; i<in.count ; i++)
\r
549 c = charbitscount[in.data[i]];
\r
550 bits = charbits[in.data[i]];
\r
555 out_p[outbits>>3] |= 1<<(outbits&7);
\r
560 out_p += (outbits+7)>>3;
\r
562 out.count = out_p - out.data;
\r
567 //==========================================================================
\r
574 #define RLE_CODE 0xe8
\r
575 #define RLE_TRIPPLE 0xe9
\r
577 int rle_counts[256];
\r
578 int rle_bytes[256];
\r
580 cblock_t RLE (cblock_t in)
\r
588 out_p = out.data = malloc (in.count*2);
\r
591 *out_p++ = in.count&255;
\r
592 *out_p++ = (in.count>>8)&255;
\r
593 *out_p++ = (in.count>>16)&255;
\r
594 *out_p++ = (in.count>>24)&255;
\r
596 for (i=0 ; i<in.count ; )
\r
602 while (i<in.count && repeat < 255 && in.data[i] == val)
\r
608 rle_counts[repeat]++;
\r
609 if (repeat > 3 || val == RLE_CODE)
\r
611 *out_p++ = RLE_CODE;
\r
622 out.count = out_p - out.data;
\r
626 //==========================================================================
\r
628 unsigned lzss_head[256];
\r
629 unsigned lzss_next[0x20000];
\r
636 #define BACK_WINDOW 0x10000
\r
637 #define BACK_BITS 16
\r
638 #define FRONT_WINDOW 16
\r
639 #define FRONT_BITS 4
\r
640 cblock_t LZSS (cblock_t in)
\r
647 int bestlength, beststart;
\r
650 if (in.count >= sizeof(lzss_next)/4)
\r
651 Error ("LZSS: too big");
\r
653 memset (lzss_head, -1, sizeof(lzss_head));
\r
655 out_p = out.data = malloc (in.count*2);
\r
656 memset (out.data, 0, in.count*2);
\r
659 *out_p++ = in.count&255;
\r
660 *out_p++ = (in.count>>8)&255;
\r
661 *out_p++ = (in.count>>16)&255;
\r
662 *out_p++ = (in.count>>24)&255;
\r
665 for (i=0 ; i<in.count ; )
\r
673 max = FRONT_WINDOW;
\r
674 if (i + max > in.count)
\r
675 max = in.count - i;
\r
677 start = lzss_head[val];
\r
678 while (start != -1 && start >= i-BACK_WINDOW)
\r
680 // count match length
\r
681 for (j=0 ; j<max ; j++)
\r
682 if (in.data[start+j] != in.data[i+j])
\r
684 if (j > bestlength)
\r
689 start = lzss_next[start];
\r
693 // slow simple search
\r
694 // search for a match
\r
695 max = FRONT_WINDOW;
\r
696 if (i + max > in.count)
\r
697 max = in.count - i;
\r
699 start = i - BACK_WINDOW;
\r
704 for ( ; start < i ; start++)
\r
706 if (in.data[start] != val)
\r
708 // count match length
\r
709 for (j=0 ; j<max ; j++)
\r
710 if (in.data[start+j] != in.data[i+j])
\r
712 if (j > bestlength)
\r
719 beststart = BACK_WINDOW - (i-beststart);
\r
721 if (bestlength < 3)
\r
722 { // output a single char
\r
725 out_p[outbits>>3] |= 1<<(outbits&7); // set bit to mark char
\r
727 for (j=0 ; j<8 ; j++, outbits++)
\r
729 out_p[outbits>>3] |= 1<<(outbits&7);
\r
732 { // output a phrase
\r
733 outbits++; // leave a 0 bit to mark phrase
\r
734 for (j=0 ; j<BACK_BITS ; j++, outbits++)
\r
735 if (beststart & (1<<j) )
\r
736 out_p[outbits>>3] |= 1<<(outbits&7);
\r
737 for (j=0 ; j<FRONT_BITS ; j++, outbits++)
\r
738 if (bestlength & (1<<j) )
\r
739 out_p[outbits>>3] |= 1<<(outbits&7);
\r
742 while (bestlength--)
\r
745 lzss_next[i] = lzss_head[val];
\r
746 lzss_head[val] = i;
\r
751 out_p += (outbits+7)>>3;
\r
752 out.count = out_p - out.data;
\r
756 //==========================================================================
\r
758 #define MIN_REPT 15
\r
760 #define HUF_TOKENS (256+MAX_REPT)
\r
762 unsigned charbits1[256][HUF_TOKENS];
\r
763 int charbitscount1[256][HUF_TOKENS];
\r
765 hnode_t hnodes1[256][HUF_TOKENS*2];
\r
766 int numhnodes1[256];
\r
768 int order0counts[256];
\r
775 int SmallestNode1 (hnode_t *hnodes, int numhnodes)
\r
778 int best, bestnode;
\r
782 for (i=0 ; i<numhnodes ; i++)
\r
784 if (hnodes[i].used)
\r
786 if (!hnodes[i].count)
\r
788 if (hnodes[i].count < best)
\r
790 best = hnodes[i].count;
\r
795 if (bestnode == -1)
\r
798 hnodes[bestnode].used = true;
\r
808 void BuildChars1 (int prev, int nodenum, unsigned bits, int bitcount)
\r
812 if (nodenum < HUF_TOKENS)
\r
815 Error ("bitcount > 32");
\r
816 charbits1[prev][nodenum] = bits;
\r
817 charbitscount1[prev][nodenum] = bitcount;
\r
821 node = &hnodes1[prev][nodenum];
\r
823 BuildChars1 (prev, node->children[0], bits, bitcount+1);
\r
825 BuildChars1 (prev, node->children[1], bits, bitcount+1);
\r
834 void BuildTree1 (int prev)
\r
836 hnode_t *node, *nodebase;
\r
840 numhnodes = HUF_TOKENS;
\r
841 nodebase = hnodes1[prev];
\r
844 node = &nodebase[numhnodes];
\r
846 // pick two lowest counts
\r
847 node->children[0] = SmallestNode1 (nodebase, numhnodes);
\r
848 if (node->children[0] == -1)
\r
851 node->children[1] = SmallestNode1 (nodebase, numhnodes);
\r
852 if (node->children[1] == -1)
\r
855 node->count = nodebase[node->children[0]].count +
\r
856 nodebase[node->children[1]].count;
\r
859 numhnodes1[prev] = numhnodes-1;
\r
860 BuildChars1 (prev, numhnodes-1, 0, 0);
\r
869 void Huffman1_Count (cblock_t in)
\r
877 for (i=0 ; i<in.count ; i++)
\r
881 hnodes1[prev][v].count++;
\r
884 for (rept=1 ; i+rept < in.count && rept < MAX_REPT ; rept++)
\r
885 if (in.data[i+rept] != v)
\r
887 if (rept > MIN_REPT)
\r
889 hnodes1[prev][255+rept].count++;
\r
902 byte scaled[256][HUF_TOKENS];
\r
903 void Huffman1_Build (FILE *f)
\r
909 for (i=0 ; i<256 ; i++)
\r
911 // normalize and save the counts
\r
913 for (j=0 ; j<HUF_TOKENS ; j++)
\r
915 if (hnodes1[i][j].count > max)
\r
916 max = hnodes1[i][j].count;
\r
921 for (j=0 ; j<HUF_TOKENS ; j++)
\r
922 { // easy to overflow 32 bits here!
\r
923 v = (hnodes1[i][j].count*(double)255+max-1)/max;
\r
926 scaled[i][j] = hnodes1[i][j].count = v;
\r
931 { // must have two tokens
\r
933 scaled[i][0] = hnodes1[i][0].count = 1;
\r
935 scaled[i][1] = hnodes1[i][1].count = 1;
\r
942 // count up the total bits
\r
944 for (i=0 ; i<256 ; i++)
\r
945 for (j=0 ; j<256 ; j++)
\r
946 total += charbitscount1[i][j] * hnodes1[i][j].count;
\r
948 total = (total+7)/8;
\r
949 printf ("%i bytes huffman1 compressed\n", total);
\r
952 fwrite (scaled, 1, sizeof(scaled), f);
\r
959 Order 1 compression with pre-built table
\r
962 cblock_t Huffman1 (cblock_t in)
\r
973 out_p = out.data = malloc(in.count*2 + 1024);
\r
974 memset (out_p, 0, in.count*2+1024);
\r
977 *out_p++ = in.count&255;
\r
978 *out_p++ = (in.count>>8)&255;
\r
979 *out_p++ = (in.count>>16)&255;
\r
980 *out_p++ = (in.count>>24)&255;
\r
985 for (i=0 ; i<in.count ; i++)
\r
989 c = charbitscount1[prev][v];
\r
990 bits = charbits1[prev][v];
\r
997 out_p[outbits>>3] |= 1<<(outbits&7);
\r
1003 // check for repeat encodes
\r
1004 for (rept=1 ; i+rept < in.count && rept < MAX_REPT ; rept++)
\r
1005 if (in.data[i+rept] != v)
\r
1007 if (rept > MIN_REPT)
\r
1009 c = charbitscount1[prev][255+rept];
\r
1010 bits = charbits1[prev][255+rept];
\r
1016 if (bits & (1<<c))
\r
1017 out_p[outbits>>3] |= 1<<(outbits&7);
\r
1025 out_p += (outbits+7)>>3;
\r
1027 out.count = out_p - out.data;
\r
1032 //==========================================================================
\r
1036 ===================
\r
1038 ===================
\r
1040 cblock_t LoadFrame (char *base, int frame, int digits, byte **palette)
\r
1042 int ten3, ten2, ten1, ten0;
\r
1044 int width, height;
\r
1051 ten3 = frame/1000;
\r
1052 ten2 = (frame-ten3*1000)/100;
\r
1053 ten1 = (frame-ten3*1000-ten2*100)/10;
\r
1057 sprintf (name, "%svideo/%s/%s%i%i%i%i.pcx", gamedir, base, base, ten3, ten2, ten1, ten0);
\r
1059 sprintf (name, "%svideo/%s/%s%i%i%i.pcx", gamedir, base, base, ten2, ten1, ten0);
\r
1061 f = fopen(name, "rb");
\r
1069 printf ("%s\n", name);
\r
1070 Load256Image (name, &in.data, palette, &width, &height);
\r
1071 in.count = width*height;
\r
1072 // FIXME: map 0 and 255!
\r
1089 video <directory> <framedigits>
\r
1092 void Cmd_Video (void)
\r
1094 char savename[1024];
\r
1097 int startframe, frame;
\r
1099 int width, height;
\r
1100 byte current_palette[768];
\r
1104 cblock_t in, huffman;
\r
1109 strcpy (base, token);
\r
1112 // sprintf (savename, "video/%s.cin", token);
\r
1113 // ReleaseFile (savename);
\r
1118 digits = atoi(token);
\r
1120 // optionally skip frames
\r
1121 if (TokenAvailable ())
\r
1124 startframe = atoi(token);
\r
1129 sprintf (savename, "%svideo/%s.cin", gamedir, base);
\r
1133 memset (charbits1, 0, sizeof(charbits1));
\r
1134 memset (charbitscount1, 0, sizeof(charbitscount1));
\r
1135 memset (hnodes1, 0, sizeof(hnodes1));
\r
1136 memset (numhnodes1, 0, sizeof(numhnodes1));
\r
1137 memset (order0counts, 0, sizeof(order0counts));
\r
1140 // load the entire sound wav file if present
\r
1141 LoadSoundtrack ();
\r
1144 sprintf (name, "%svideo/%s/%s0000.pcx", gamedir, base, base);
\r
1146 sprintf (name, "%svideo/%s/%s000.pcx", gamedir, base, base);
\r
1148 printf ("%s\n", name);
\r
1149 Load256Image (name, NULL, &palette, &width, &height);
\r
1151 output = fopen (savename, "wb");
\r
1153 Error ("Can't open %s", savename);
\r
1155 // write header info
\r
1156 i = LittleLong (width);
\r
1157 fwrite (&i, 4, 1, output);
\r
1158 i = LittleLong (height);
\r
1159 fwrite (&i, 4, 1, output);
\r
1160 i = LittleLong (wavinfo.rate);
\r
1161 fwrite (&i, 4, 1, output);
\r
1162 i = LittleLong (wavinfo.width);
\r
1163 fwrite (&i, 4, 1, output);
\r
1164 i = LittleLong (wavinfo.channels);
\r
1165 fwrite (&i, 4, 1, output);
\r
1167 // build the dictionary
\r
1168 for ( frame=startframe ; ; frame++)
\r
1170 printf ("counting ", frame);
\r
1171 in = LoadFrame (base, frame, digits, &palette);
\r
1174 Huffman1_Count (in);
\r
1179 // build nodes and write counts
\r
1180 Huffman1_Build (output);
\r
1183 memset (current_palette, 0, sizeof(current_palette));
\r
1185 // compress it with the dictionary
\r
1186 for (frame=startframe ; ; frame++)
\r
1188 printf ("packing ", frame);
\r
1189 in = LoadFrame (base, frame, digits, &palette);
\r
1193 // see if the palette has changed
\r
1194 for (i=0 ; i<768 ; i++)
\r
1195 if (palette[i] != current_palette[i])
\r
1197 // write a palette change
\r
1198 memcpy (current_palette, palette, sizeof(current_palette));
\r
1199 command = LittleLong(1);
\r
1200 fwrite (&command, 1, 4, output);
\r
1201 fwrite (current_palette, 1, sizeof(current_palette), output);
\r
1206 command = 0; // no palette change
\r
1207 fwrite (&command, 1, 4, output);
\r
1211 huffman = Huffman1 (in);
\r
1212 printf ("%5i bytes after huffman1\n", huffman.count);
\r
1214 swap = LittleLong (huffman.count);
\r
1215 fwrite (&swap, 1, sizeof(swap), output);
\r
1217 fwrite (huffman.data, 1, huffman.count, output);
\r
1219 // save some sound samples
\r
1220 WriteSound (output, frame);
\r
1224 free (huffman.data);
\r
1228 // write end-of-file command
\r
1230 fwrite (&command, 1, 4, output);
\r
1232 printf ("Total size: %i\n", ftell (output));
\r
1237 free (soundtrack);
\r