/*
** 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