/* Copyright (C) 1999-2006 Id Software, Inc. and contributors. For a list of contributors, see the accompanying CONTRIBUTORS file. This file is part of GtkRadiant. GtkRadiant 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. GtkRadiant 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 GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #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 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 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>8)&255; *out_p++ = (in.count>>16)&255; *out_p++ = (in.count>>24)&255; // write head index for (i=0 ; i>8)&255; *out_p++ = (i>>16)&255; *out_p++ = (i>>24)&255; // write the L column for (i=0 ; i 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 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>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 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) max = in.count - i; start = lzss_head[val]; while (start != -1 && start >= i-BACK_WINDOW) { // count match length for (j=0 ; 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 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<>3] |= 1<<(outbits&7); } else { // output a phrase outbits++; // leave a 0 bit to mark phrase for (j=0 ; j>3] |= 1<<(outbits&7); for (j=0 ; j>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 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 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 max) max = hnodes1[i][j].count; } if (max == 0) max = 1; total = 0; for (j=0 ; j 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>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<>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 =============== */ 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); }