/* ** Command & Conquer Red Alert(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ /*********************************************************************************************** ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** *********************************************************************************************** * * * Project Name : Command & Conquer * * * * File Name : AUDIOMAK.CPP * * * * Programmer : Joe L. Bostic * * * * Start Date : August 8, 1994 * * * * Last Update : August 8, 1994 [JLB] * * * *---------------------------------------------------------------------------------------------* * Functions: * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include #include #include #include #include #include #include #define FALSE 0 #define TRUE 1 #define MIN(a,b) (((a)<(b)) ? (a) : (b)) #define MAX(a,b) (((a)>(b)) ? (a) : (b)) char * SourceFile = 0; int SourceWild = FALSE; char * DestFile = 0; int DestDir = FALSE; int DoSpecial = TRUE; // Compression starts "on". long MagicNumber = 0xDEAF; int DoVerbose = FALSE; // Verbose report starts "off". char Path[MAXPATH]; char Drive[MAXDRIVE]; char Dir[MAXDIR]; char File[MAXFILE]; char Ext[MAXEXT]; // The size of these buffers must be evenly divisble by 2048. char far StageBuffer[1024*16]; char far padding[1024]; char far AltBuffer[1024*16]; char far padding2[1024]; // General header to the RIFF file itself. typedef struct { char szRIFF[4]; // "RIFF" long dwFormatLength; char szWAVE[4]; // "WAVE" } RIFFHeaderType; // Header to each sub-block of a RIFF file. typedef struct { char szDATA[4]; // ASCII identifier for block. long dwDataLength; // Size of block (not counting this header). } RIFFBlockType; // First part of the "fmt" data block. typedef struct { short wFormatTag; // 1=raw, 33=sonarc short wChannels; // 1=mono, 2=stereo long dwSamplesPerSec; // Playback "rate". long dwAvgBytesPerSec; // =(samples/sec)*(bytes/sample)*wChannels short wBlockAlign; short wBitsPerSample; // 8 or 16 // Even more bytes can go here... examine dwDataLength to verify. // Optional for Sonarc // LONG cbSize; // Size of the extra portion of fmt (always 2) // WORD wCompType; // Compression algorithm index } RIFFfmtType; typedef struct { char Description[0x14]; short Offset; short Version; short IDCode; } VocHeaderType; // Compression types. typedef enum { SCOMP_NONE=0, // No compression -- raw data. SCOMP_WESTWOOD, // Special sliding window delta compression. SCOMP_SONARC=33 // Sonarc frame compression. } SCompressType; // Header to output file. typedef struct { unsigned short Rate; // Playback rate (hertz). long Size; // Size of data (bytes). unsigned Stereo:1; // Is the sample stereo? unsigned Bits:1; // Is the sample 16 bits? unsigned pad:6; // Padding bits. unsigned char Compression; // What kind of compression for this sample? } RawHeaderType; typedef struct { short frameSize; // Size of the frame in bytes (including header). short nSamples; // # of samples in the frame. short chkSum; // A checksum -- Words XORed with 0xACED. char tableIndex; // Bit 7 set if frame was RLE'd char order; // The order of the predictor. } SonarcFrameType; int main(int argc, char **argv); void Process(char const *src, char const *name, char const *dest); void Convert(char const *src, char const *dst); int Is_WAV(FILE *src); void Convert_WAV(FILE * src, FILE * dst); int Is_VOC(FILE *src); void Convert_VOC(FILE *src, FILE *dst); long Compress_Frame(void * source, void * dest, long size); extern "C" { short Decompress_Frame(void * source, void * dest, short size); } int main(int argc, char **argv) { /* ** Process the command line parameters. */ try { if (argc < 2) throw 1; for (int index = 1; index < argc; index++) { char *string = argv[index]; switch (string[0]) { case '-': case '/': switch (toupper(string[1])) { case 'X': if (string[2] == '-') { DoSpecial = FALSE; } break; case 'V': DoVerbose = TRUE; break; default: printf("\nERROR: Invalid command line argument.\n"); return (EXIT_FAILURE); } break; default: if (!SourceFile) { SourceFile = string; } else { if (!DestFile) { DestFile = string; } else { printf("\nERROR: Invalid command line argument.\n"); return (EXIT_FAILURE); } } } } /* ** Verify that the source filespec can locate at least one file. */ { struct ffblk block; int result = fnsplit(SourceFile, Drive, Dir, File, Ext); if (result & WILDCARDS) SourceWild = TRUE; result = findfirst(SourceFile, &block, FA_ARCH|FA_RDONLY); if (result) { printf("\nERROR: Cannot find source file '%s'.\n", SourceFile); exit(EXIT_FAILURE); } } /* ** No output file is necessary if only a single file is specified for ** the source. In such a case, use the source file as the output file ** name but change the extension to .AUD. */ if (!DestFile) { if (!SourceWild) { fnsplit(SourceFile, Drive, Dir, File, Ext); fnmerge(Path, Drive, Dir, File, ".AUD"); DestFile = strdup(Path); } else { DestFile = strdup("."); //printf("\nERROR: No destination directory specified.\n"); //exit(EXIT_FAILURE); } } /* ** The source and destination files cannot be the same. */ if (stricmp(SourceFile, DestFile) == 0) { printf("\nERROR: Source and destination files must not be the same.\n"); exit(EXIT_FAILURE); } /* ** Make sure that the destination filespec does not contain any ** wildcard specifications. */ if (fnsplit(DestFile, Drive, Dir, File, Ext) & WILDCARDS) { printf("\nERROR: Wildcard characters not allowed in destination file '%s'.\n", DestFile); exit(EXIT_FAILURE); } /* ** Determine if the destination is a directory or not. If it is ** a directory, then it must be mutated into proper directory format. */ { struct ffblk block; strcpy(Path, DestFile); if (Path[strlen(Path)-1] == '\\') { Path[strlen(Path)-1] = '\0'; } int result = findfirst(Path, &block, FA_DIREC); if (!result && (block.ff_attrib & FA_DIREC)) { DestDir = TRUE; /* ** Make sure that the output directory ends with a backslash or a colon. */ strcpy(Path, DestFile); switch (Path[strlen(Path)-1]) { default: strcat(Path, "\\"); DestFile = strdup(Path); break; case '\\': case ':': break; } } } /* ** Verify that the destination is a directory if wildcards were specified ** in the source filespec. */ if (SourceWild && !DestDir) { printf("\nERROR: Destination '%s' was not a directory.\n", DestFile); exit(EXIT_FAILURE); } /* ** Scan through all specified source files and process them into the ** appropriate audio format. */ struct ffblk block; findfirst(SourceFile, &block, FA_ARCH|FA_RDONLY); do { Process(SourceFile, block.ff_name, DestFile); } while (!findnext(&block)); } catch(int) { printf("\n" "AUDIOMAK V2.3 (c) 1994, by Westwood Studios\n" "USAGE: AUDIOMAK [option] \n" "\n" " Options:\n" " -X- : Turn compression off.\n" " -V : Verbose compression report.\n" "\n" " : File or filespec of VOC or WAV files to process\n" " Can be SONARC compressed.\n" " : Destination file or destination directory. If\n" " wildcards were used in the source file then this\n" " can only be a destination directory.\n" "\n"); return(EXIT_FAILURE); } return(EXIT_SUCCESS); } long rawframes; // Number of non-compressed frames. long rate; // Sample rate. long raw; // Number of raw bytes output. long bit5; // Number of 5bit codes output. long bit4; // Number of 4bit codes output (@2 samples). long bit2; // Number of 2bit codes output (@4 samples). long bit0; // Number of 0 delta runs. long overhead; // Overhead (wrapper) bytes. long original; // Original data size. long compressed; // Compressed data output. inline void Log_RawFrame(long val) {rawframes += val;} inline void Log_Raw(long val) {raw += val;} inline void Log_5Bit(long val) {bit5 += val;} inline void Log_4Bit(long val) {bit4 += val;} inline void Log_2Bit(long val) {bit2 += val;} inline void Log_0Bit(long val) {bit0 += val;} inline void Log_Overhead(long val) {overhead += val;} inline void Log_Compressed(long val) {compressed += val;} inline void Log_Original(long val) {original += val;} inline void Log_Rate(long val) {rate = val;} void Stat_Reset(void) { if (DoVerbose) { rawframes = 0; rate = 0; raw = 0; bit5 = 0; bit4 = 0; bit2 = 0; bit0 = 0; overhead = 0; original = 0; compressed = 0; } } void Stat_Dump(void) { if (DoVerbose) { printf("Compression efficiency = %ld%% (%ld bytes)\n", ((original - compressed) * 100) / original, original - compressed); printf(" Sample Rate = %ld\n", rate); printf(" Uncompressed data..:%7ld %ld%%\n", raw, (raw * 100) / original); printf(" Uncompressed frames:%7ld\n", rawframes); printf(" 5 bit codes........:%7ld %ld%%\n", bit5, (bit5 * 100) / original); printf(" 4 bit codes........:%7ld %ld%%\n", bit4, (bit4 * 100) / original); printf(" 2 bit codes........:%7ld %ld%%\n", bit2, (bit2 * 100) / original); printf(" 0 bit codes........:%7ld %ld%%\n", bit0, (bit0 * 100) / original); printf(" overhead bytes.....:%7ld %ld%%\n", overhead, (overhead * 100) / original); } else { printf("%ld%%\n", ((original - compressed) * 100) / original); } } void Process(char const *src, char const *name, char const *dest) { char sourcename[MAXPATH]; /* ** Open the input file. */ fnsplit(src, Drive, Dir, File, Ext); fnmerge(sourcename, Drive, Dir, NULL, NULL); strcat(sourcename, name); /* ** Open the output filename based on the source filename and the destination ** directory or filename. */ fnsplit(sourcename, NULL, NULL, File, Ext); if (DestDir) { fnsplit(dest, Drive, Dir, NULL, NULL); fnmerge(Path, Drive, Dir, File, ".AUD"); } else { fnsplit(dest, Drive, Dir, NULL, Ext); fnmerge(Path, Drive, Dir, File, Ext); } strupr(sourcename); strupr(Path); printf("%s ", sourcename, Path); Convert(sourcename, Path); } void Convert(char const *src, char const *dst) { FILE *srcfile = 0; FILE *dstfile = 0; srcfile = fopen(src, "rb"); if (!srcfile) { perror(src); exit(EXIT_FAILURE); } if (Is_WAV(srcfile)) { dstfile = fopen(dst, "wb"); if (!dstfile) { perror(dst); exit(EXIT_FAILURE); } Stat_Reset(); Convert_WAV(srcfile, dstfile); Stat_Dump(); } else { if (Is_VOC(srcfile)) { dstfile = fopen(dst, "wb"); if (!dstfile) { perror(dst); exit(EXIT_FAILURE); } Stat_Reset(); Convert_VOC(srcfile, dstfile); Stat_Dump(); } else { printf("ERROR: Unrecognized source file format for '%s'.\n", src); exit(EXIT_FAILURE); } } if (dstfile) fclose(dstfile); fclose(srcfile); } int Is_WAV(FILE *src) { long size; RIFFHeaderType header; fseek(src, 0, SEEK_SET); /* ** Determine if this is a WAV file. Examining the file extension is ** not foolproof. The actual data must be looked at. */ size = fread(&header, 1, sizeof(header), src); if (size == sizeof(header)) { if (memcmp(header.szRIFF, "RIFF", 4) == 0) { if (memcmp(header.szWAVE, "WAVE", 4) == 0) { return(TRUE); } } } return(FALSE); } void Convert_WAV(FILE * src, FILE * dst) { RIFFBlockType block; RIFFfmtType fmt; RawHeaderType raw; int nodo = FALSE; long fcount = 0; raw.pad = 0; raw.Size = 0; fwrite(&raw, 1, sizeof(raw), dst); Log_Overhead(sizeof(raw)); /* ** At this point, we already know that it is a RIFF (wav) file, so ** skip the identifier bytes at the beginning. */ fseek(src, sizeof(RIFFHeaderType), SEEK_SET); /* ** Process all the blocks in the RIFF file. */ for (;;) { long s; s = fread(&block, sizeof(block), 1, src); if (!s) break; /* ** If this is the format control block, the process it. */ if (memcmp(block.szDATA, "fmt ", sizeof("fmt ")-1) == 0) { s = fread(&fmt, 1, sizeof(fmt), src); if (s != sizeof(fmt)) break; raw.Rate = (short)fmt.dwSamplesPerSec; Log_Rate(raw.Rate); raw.Stereo = (fmt.wChannels == 2); raw.Bits = (fmt.wBitsPerSample == 16); switch (fmt.wFormatTag) { default: case 1: raw.Compression = SCOMP_NONE; if (DoSpecial) { raw.Compression = SCOMP_WESTWOOD; } break; case 33: raw.Compression = SCOMP_SONARC; if (DoSpecial) { printf("ERROR: Cannot compress an already compressed WAV file.\n"); nodo = TRUE; } break; } /* ** Skip any extra format bytes. For a Sonarc file, there are ** extra bytes to skip. */ fseek(src, block.dwDataLength - sizeof(fmt), SEEK_CUR); } else if (memcmp(block.szDATA, "data", sizeof("data")-1) == 0) { /* ** Add in the size of this data block to the running total of data ** count. For SONARC compressed blocks, there is an extra four bytes ** at the start of the data that records the uncompressed size -- who ** cares, so skip it. */ if (raw.Compression == SCOMP_SONARC) { fseek(src, 4, SEEK_CUR); block.dwDataLength -= sizeof(raw.Size); } /* ** Copy the bulk of the data to the output file. */ while (block.dwDataLength) { long desired; long actual; desired = sizeof(StageBuffer); if (block.dwDataLength < desired) { desired = block.dwDataLength; } actual = fread(StageBuffer, 1, (unsigned)desired, src); Log_Original(actual); block.dwDataLength -= actual; /* ** Special process to convert audio data into deltas. */ if (DoSpecial && !nodo) { void *sptr = &StageBuffer[0]; long a = actual; /* ** When compressing the data, it must be processed in small ** chunks. Loop through the source and process the chunks ** until there is no more source data. Careful choice of the ** size of the buffers ensures that only full chunks are ** output. */ while (a) { long tocomp; long comped; tocomp = MIN(a, 2048); comped = Compress_Frame(sptr, AltBuffer, tocomp); a -= tocomp; Log_Compressed(comped); #if(FALSE) if (comped < tocomp) { Decompress_Frame(AltBuffer, sptr, (short)tocomp); } raw.Size += fwrite(sptr, 1, (size_t)tocomp, dst); (char*)sptr += (unsigned)tocomp; #else (char*)sptr += (unsigned)tocomp; raw.Size += fwrite(&comped, 1, sizeof(short), dst); raw.Size += fwrite(&tocomp, 1, sizeof(short), dst); raw.Size += fwrite(&MagicNumber, 1, sizeof(MagicNumber), dst); raw.Size += fwrite(&AltBuffer[0], 1, (size_t)comped, dst); #endif fcount++; } } else { raw.Size += actual; fwrite(StageBuffer, 1, (unsigned)actual, dst); Log_Compressed(actual); fcount++; } if (!actual) break; } } else { /* ** Unrecognized blocks are skipped. */ fseek(src, block.dwDataLength, SEEK_CUR); } } /* ** Output the finalized raw header structure. */ fseek(dst, 0, SEEK_SET); fwrite(&raw, 1, sizeof(raw), dst); } int Is_VOC(FILE *src) { long size; VocHeaderType voc; fseek(src, 0, SEEK_SET); size = fread(&voc, 1, sizeof(voc), src); if (size != sizeof(voc)) { return(FALSE); } if (memicmp(voc.Description, "Creative Voice File", strlen("Creative Voice File")) != 0) { return(FALSE); } return (TRUE); } void Convert_VOC(FILE *src, FILE *dst) { // long size = 0; VocHeaderType voc; RawHeaderType raw; raw.pad = 0; raw.Size = 0; raw.Compression = SCOMP_NONE; if (DoSpecial) { raw.Compression = SCOMP_WESTWOOD; } fseek(src, 0, SEEK_SET); fread(&voc, 1, sizeof(voc), src); fseek(src, voc.Offset, SEEK_SET); /* ** Output a dummy raw header (will be updated later). */ raw.Size = 0; raw.Rate = 11025; raw.Stereo = FALSE; raw.Bits = FALSE; fwrite(&raw, sizeof(raw), 1, dst); /* ** Loop through all the VOC chunks in order to build the correct ** rawheader structure. */ char code = -1; while (code != 0) { char c; long blocksize=0; unsigned rate=0; unsigned char pack=0; ldiv_t result; fread(&code, 1, sizeof(code), src); switch (code) { /* ** General data block. Packed data is not supported. ** If an extended control block has been processed, then ** ignore the control bytes in this file. */ case 1: fread(&blocksize, 3, 1, src); /* ** Determine the sample rate from the special code provided ** for this block. The formula is very inaccurate, so check ** for some special codes and fixe the playback rate accordingly. ** When dealing with odd sample rates, then just figure out ** the rate based on the formula. */ fread(&rate, 1, 1, src); switch (256-rate) { case 22: case 23: raw.Rate = 44100U; break; case 45: raw.Rate = 22050; break; case 90: case 91: raw.Rate = 11025; break; case 181: raw.Rate = 5512; break; default: result = ldiv(1000000L, (long)256-(long)rate); raw.Rate = (int)result.quot; break; } Log_Rate(raw.Rate); fread(&pack, 1, 1, src); /* ** Copy over the raw data. If the data is packed, then ** it is skipped since packed data is not supported by this ** utility (yet). */ if (pack == 0) { long desired; long actual; blocksize -= 2; //raw.Size += blocksize; while (blocksize) { desired = MIN(blocksize, sizeof(StageBuffer)); actual = fread(StageBuffer, 1, (unsigned)desired, src); Log_Original(actual); /* ** Special process to convert audio data into deltas. */ if (DoSpecial) { void *sptr = &StageBuffer[0]; long a = actual; /* ** When compressing the data, it must be processed in small ** chunks. Loop through the source and process the chunks ** until there is no more source data. Careful choice of the ** size of the buffers ensures that only full chunks are ** output. */ while (a) { short tocomp; short comped; tocomp = (short)MIN(a, 2048); comped = (short)Compress_Frame(sptr, AltBuffer, tocomp); a -= tocomp; Log_Compressed(comped); (char*)sptr += (unsigned)tocomp; raw.Size += fwrite(&comped, 1, sizeof(short), dst); raw.Size += fwrite(&tocomp, 1, sizeof(short), dst); raw.Size += fwrite(&MagicNumber, 1, sizeof(MagicNumber), dst); raw.Size += fwrite(&AltBuffer[0], 1, (size_t)comped, dst); } } else { raw.Size += actual; fwrite(StageBuffer, 1, (unsigned)actual, dst); Log_Compressed(actual); } //fwrite(StageBuffer, 1, (unsigned)actual, dst); blocksize -= actual; if (actual != desired) break; } } else { fseek(src, blocksize-2, SEEK_CUR); } break; /* ** Continuation block. Sample rate and pack type is ** inherited from the previous data block. */ case 2: fread(&blocksize, 1, 3, src); if (pack == 0) { long desired; long actual; blocksize -= 2; raw.Size += blocksize; while (blocksize) { desired = MIN(blocksize, sizeof(StageBuffer)); actual = fread(StageBuffer, 1, (unsigned)desired, src); fwrite(StageBuffer, 1, (unsigned)actual, dst); Log_Original(actual); Log_Compressed(actual); blocksize -= actual; if (actual != desired) break; } } else { fseek(src, blocksize-2, SEEK_CUR); } break; /* ** Extended block is used for highspeed or ** stereo samples. */ case 8: fseek(src, 3, SEEK_CUR); fread(&rate, 2, 1, src); fread(&pack, 1, 1, src); fread(&c, 1, 1, src); raw.Stereo = (c == 1); if (raw.Stereo) { result = ldiv(128000000L, 65536L-rate); } else { result = ldiv(256000000L, 65532L-rate); } raw.Rate = (int)result.quot; Log_Rate(raw.Rate); break; /* ** Unprocessed blocks are skipped. */ case 3: // Silence block. case 4: // Marker block. case 5: // Embedded string. case 6: // Loop start block. case 7: // End of loop block. fread(&blocksize, 1, 3, src); fseek(src, blocksize, SEEK_CUR); break; case 0: // End of file block. break; default: code = 0; break; } } fseek(dst, 0, SEEK_SET); fwrite(&raw, sizeof(raw), 1, dst); } typedef enum { CODE_2BIT, // Bit packed 2 bit delta. CODE_4BIT, // Nibble packed 4 bit delta. CODE_RAW, // Raw sample. CODE_SILENCE // Run of silence. } SCodeType; signed char _2bitencode[5] = {0, 1, 2, 3}; //signed char _2bitencode[5] = {0, 1, (2), 2, 3}; signed int _2bitdecode[4] = {-2, -1, 0, 1}; //signed int _2bitdecode[4] = {-2, -1, 1, 2}; #if(TRUE) signed char _4bitencode[19] = { 0, 1, 2, 2, 3, 4, 5, 6, 7, (8), 8, 9, 10, 11, 12, 13, 13, 14, 15 }; signed int _4bitdecode[16] = {-9, -8, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 8}; #else signed char _4bitencode[33] = { 0, 0, 0, 1, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 7, 8, (8), 9, 10, 11, 12, 12, 13, 13, 13, 14, 14, 14, 15, 15, 15, 15 }; signed int _4bitdecode[16] = {-16, -13, -10, -8, -6, -4, -2, -1, 1, 2, 4, 6, 8, 10, 13, 16}; #endif long Compress_Frame(void * source, void * dest, long size) { unsigned char *s = (unsigned char *)source; unsigned char *d = (unsigned char *)dest; int delta; unsigned int previous = 0x80; long outcount = 0; unsigned char *s4; // Scratch pointer into source data. unsigned int p4; // Scratch "previous" sample value. long max4; unsigned char *lastraw = 0; // Pointer to last raw sequence code. long osize = size; // Copy of original compression data length. /* ** Reduce very small amplitudes to silence. Usually a rather large ** portion of a sample is hovering around the silence value. This is ** due, in part, to the artifacting of the sample process. These low ** amplitudes are not audible. */ max4 = size; #if(FALSE) while (max4) { int delta = (int)s[1] - (int)s[0]; /* ** For steep sample transitions that just border on the range of 4 bit ** deltas, adjust them to fit. This results in small savings but at ** no loss to audio quality. */ if (delta == -18) s[1] += 2; if (delta == -17) s[1]++; if (delta == 18) s[1] -= 2; if (delta == 17) s[1]--; if ((*s > 0x7F && *s < 0x81) && s[1] > 0x7F && s[1] < 0x81) *s = 0x80; s++; max4--; } #endif s = (unsigned char *)source; while (size > 0 && outcount < osize) { /* ** First check for runs of zero deltas. If a run of at least ** any can be found, then output it. */ s4 = s; max4 = MIN(63+1, size); for (int i = 0; i < max4; i++) { if (previous != *s4++) break; } if (i > 2) { /* ** When there is a code transition, terminate any run of raw ** samples. */ lastraw = 0; *d++ = (i-1) | (CODE_SILENCE<<6); outcount++; s += i; size -= i; Log_0Bit(i); Log_Overhead(1); continue; } /* ** If there are fewer than 4 samples remaining, then using delta ** compression is inefficient. Just drop into the raw routine */ if (size > 4) { s4 = s; p4 = previous; /* ** Find out the number of lossless 2 bit deltas available. These ** deltas are always present in quads. The compressed code is ** the delta quad count followed by the deltas in bit packed ** bytes. */ max4 = MIN(64L*4L + 4L + 4L, size); for (unsigned i = 0; i < max4; i++) { delta = ((int)*s4++) - p4; if (delta < -2 || delta > 1 /*|| !delta*/) break; //if (delta < -2 || delta > 2 || !delta) break; p4 += _2bitdecode[_2bitencode[delta+2]]; if (((signed)p4) < 0) p4 = 0; if (((signed)p4) > 255) p4 = 255; } i >>= 2; // Delta 2 always occur in quads -- force this. /* ** If there is the minimum benificial number of delta 2s available, ** then compress them. */ if (i) { Log_2Bit(i*4); Log_Overhead(1); /* ** When there is a code transition, terminate any run of raw ** samples. */ lastraw = 0; /* ** Output the delta 4 pair count. This is the number of pairs ** minus the 'free' two pairs already assumed to be there. */ i = MIN(i, 63+1); *d++ = (i-1) | (CODE_2BIT<<6); outcount++; for (int dd = 0; dd < i; dd++) { int delta1, delta2, delta3, delta4; delta1 = _2bitencode[(((int)*s++) - previous)+2]; previous += _2bitdecode[delta1]; if (((signed)previous) < 0) previous = 0; if (((signed)previous) > 255) previous = 255; size--; delta2 = _2bitencode[(((int)*s++) - previous)+2]; previous += _2bitdecode[delta2]; if (((signed)previous) < 0) previous = 0; if (((signed)previous) > 255) previous = 255; size--; delta3 = _2bitencode[(((int)*s++) - previous)+2]; previous += _2bitdecode[delta3]; if (((signed)previous) < 0) previous = 0; if (((signed)previous) > 255) previous = 255; size--; delta4 = _2bitencode[(((int)*s++) - previous)+2]; previous += _2bitdecode[delta4]; if (((signed)previous) < 0) previous = 0; if (((signed)previous) > 255) previous = 255; size--; *d++ = (delta4<<6) | (delta3<<4) | (delta2<<2) | delta1; outcount++; } continue; } else { s4 = s; p4 = previous; /* ** Find out the number of lossless 4 bit deltas follow. These ** deltas are always present in pairs. The compressed code is ** the delta pair count followed by the deltas in nibble packed ** bytes. */ max4 = MIN(64L*2L+4L+4L, size); for (unsigned i = 0; i < max4; i++) { delta = ((int)*s4++) - p4; if (delta < -9 || delta >= 9 /*|| !delta*/) break; //if (delta < -16 || delta > 16 || !delta) break; p4 += _4bitdecode[_4bitencode[delta+9]]; //p4 += _4bitdecode[_4bitencode[delta+16]]; if (((signed)p4) < 0) p4 = 0; if (((signed)p4) > 255) p4 = 255; } i >>= 1; // Delta 4 always occur in pairs -- force this. /* ** If there is the minimum benificial number of delta 4s available, ** then compress them. */ if (i) { Log_4Bit(2*i); Log_Overhead(1); /* ** When there is a code transition, terminate any run of raw ** samples. */ lastraw = 0; /* ** Output the delta 4 pair count. This is the number of pairs ** minus the 'free' two pairs already assumed to be there. */ i = MIN(i, 63+1); *d++ = (i-1) | (CODE_4BIT<<6); outcount++; for (int dd = 0; dd < i; dd++) { int delta1, delta2; delta1 = _4bitencode[(((int)*s++) - previous) + 9]; //delta1 = _4bitencode[(((int)*s++) - previous) + 16]; previous += _4bitdecode[delta1]; if (((signed)previous) < 0) previous = 0; if (((signed)previous) > 255) previous = 255; size--; delta2 = _4bitencode[(((int)*s++) - previous) + 9]; //delta2 = _4bitencode[(((int)*s++) - previous) + 16]; previous += _4bitdecode[delta2]; if (((signed)previous) < 0) previous = 0; if (((signed)previous) > 255) previous = 255; size--; *d++ = (delta2 << 4) | (delta1 & 0x0F); outcount++; } continue; } } } /* ** Raw output since deltas were unsuccessful. If this is a run ** of raw output, then merely tack it onto the run rather than ** create a new code sequence. */ if (lastraw) { *lastraw = (*lastraw)+1; /* ** There is only so much a run code can accomodate. If the limit ** has been reached, then terminate this code. A new one will be ** created if necessary. */ if ((*lastraw & 0x1F) == 0x1F) { lastraw = 0; } } else { /* ** If there is no current raw dump of samples, then check to see if ** this sample can fit into a 5 bit delta. If it can, then store ** it as such as a parasite to the "raw" code. This will save a byte ** for any stray 5 bit deltas that happen along. It is expected that ** this is more frequent than 6 or more bit deltas that would necessitate ** the use of the RAW code. */ delta = ((int)*s) - previous; if (delta >= -16 && delta <= 15) { lastraw = 0; *d++ = (CODE_RAW<<6) | 0x20 | (delta & 0x1F); outcount++; previous = *s++; size--; Log_5Bit(1); continue; } else { lastraw = d; *d++ = (CODE_RAW<<6); Log_Overhead(1); outcount++; } } Log_Raw(1); *d++ = previous = *s++; size--; outcount++; } /* ** Check to see if the compression process actually resulted in smaller ** data size. In some cases, the 'compressed' data is actually larger. In ** this case, just output the raw frame. If the compressed and actual frame ** size match, then it is presumed that no compression occurs. */ if (outcount >= osize) { memcpy(dest, source, (size_t)osize); outcount = osize; Log_RawFrame(1); } return(outcount); } #if(FALSE) long Decompress_Frame(void * source, void * dest, long size) { unsigned int previous = 0x0080; signed char *s = (signed char *)source; unsigned char *d = (unsigned char *)dest; long incount = 0; /* ** Uncompress the source data until the buffer is filled. */ while (size > 0) { signed char code; // Compression code. int counter; code = *s++; counter = (code & 0x3F) + 1; incount++; switch ((code >> 6) & 0x03) { case CODE_RAW: /* ** The "raw" code could actually contain an embedded 5 bit delta. ** If this is the case then this is a self contained code. Extract ** and process the delta. */ if ((counter-1) & 0x20) { counter = (counter-1) & 0x1F; if (counter & 0x10) counter |= 0xFFE0; previous = *d++ = previous + counter; size--; } else { /* ** Normal run of raw samples. */ memcpy(d, s, counter); incount += counter; size -= counter; d += counter-1; s += counter; previous = *d++; } break; case CODE_4BIT: while (counter) { int delta; delta = *s++; incount--; previous += (signed)_4bitdecode[delta & 0x0F]; if (((signed)previous) < 0) previous = 0; if (((signed)previous) > 255) previous = 255; *d++ = previous; size--; previous += (signed)_4bitdecode[(delta >> 4) & 0x0F]; if (((signed)previous) < 0) previous = 0; if (((signed)previous) > 255) previous = 255; *d++ = previous; size--; counter--; } break; case CODE_2BIT: while (counter) { int delta; delta = *s++; incount--; previous += (signed)_2bitdecode[delta & 0x03]; if (((signed)previous) < 0) previous = 0; if (((signed)previous) > 255) previous = 255; *d++ = previous; size--; previous += (signed)_2bitdecode[(delta >> 2) & 0x03]; if (((signed)previous) < 0) previous = 0; if (((signed)previous) > 255) previous = 255; *d++ = previous; size--; previous += (signed)_2bitdecode[(delta >> 4) & 0x03]; if (((signed)previous) < 0) previous = 0; if (((signed)previous) > 255) previous = 255; *d++ = previous; size--; previous += (signed)_2bitdecode[(delta >> 6) & 0x03]; if (((signed)previous) < 0) previous = 0; if (((signed)previous) > 255) previous = 255; *d++ = previous; size--; counter--; } break; default: case CODE_SILENCE: memset(d, previous, counter); d += counter; size -= counter; break; } } return (incount); } #endif