1 /* libSoX Macintosh HCOM format.
2  * These are really FSSD type files with Huffman compression,
3  * in MacBinary format.
4  * TODO: make the MacBinary format optional (so that .data files
5  * are also acceptable).  (How to do this on output?)
6  *
7  * September 25, 1991
8  * Copyright 1991 Guido van Rossum And Sundry Contributors
9  * This source code is freely redistributable and may be used for
10  * any purpose.  This copyright notice must be maintained.
11  * Guido van Rossum And Sundry Contributors are not responsible for
12  * the consequences of using this software.
13  *
14  * April 28, 1998 - Chris Bagwell (cbagwell@sprynet.com)
15  *
16  *  Rearranged some functions so that they are declared before they are
17  *  used, clearing up some compiler warnings.  Because these functions
18  *  passed floats, it helped some dumb compilers pass stuff on the
19  *  stack correctly.
20  *
21  */
22 
23 #include "sox_i.h"
24 #include <assert.h>
25 #include <string.h>
26 #include <stdlib.h>
27 #include <errno.h>
28 
29 /* FIXME: eliminate these 2 functions */
30 
put32_be(unsigned char ** p,int32_t val)31 static void put32_be(unsigned char **p, int32_t val)
32 {
33   *(*p)++ = (val >> 24) & 0xff;
34   *(*p)++ = (val >> 16) & 0xff;
35   *(*p)++ = (val >> 8) & 0xff;
36   *(*p)++ = val & 0xff;
37 }
38 
put16_be(unsigned char ** p,int val)39 static void put16_be(unsigned char **p, int val)
40 {
41   *(*p)++ = (val >> 8) & 0xff;
42   *(*p)++ = val & 0xff;
43 }
44 
45 /* Dictionary entry for Huffman (de)compression */
46 typedef struct {
47         long frequ;
48         short dict_leftson;
49         short dict_rightson;
50 } dictent;
51 
52 typedef struct {
53   /* Static data from the header */
54   dictent *dictionary;
55   int32_t checksum;
56   int deltacompression;
57   /* Engine state */
58   long huffcount;
59   long cksum;
60   int dictentry;
61   int nrbits;
62   uint32_t current;
63   short sample;
64   /* Dictionary */
65   dictent *de;
66   int32_t new_checksum;
67   int nbits;
68   int32_t curword;
69 
70   /* Private data used by writer */
71   unsigned char *data;          /* Buffer allocated with lsx_malloc */
72   size_t size;               /* Size of allocated buffer */
73   size_t pos;                /* Where next byte goes */
74 } priv_t;
75 
dictvalid(int n,int size,int left,int right)76 static int dictvalid(int n, int size, int left, int right)
77 {
78         if (n > 0 && left < 0)
79                 return 1;
80 
81         return (unsigned)left < size && (unsigned)right < size;
82 }
83 
startread(sox_format_t * ft)84 static int startread(sox_format_t * ft)
85 {
86         priv_t *p = (priv_t *) ft->priv;
87         int i;
88         char buf[5];
89         uint32_t datasize, rsrcsize;
90         uint32_t huffcount, checksum, compresstype, divisor;
91         unsigned short dictsize;
92         int rc;
93 
94 
95         /* Skip first 65 bytes of header */
96         rc = lsx_skipbytes(ft, (size_t) 65);
97         if (rc)
98             return rc;
99 
100         /* Check the file type (bytes 65-68) */
101         if (lsx_reads(ft, buf, (size_t)4) == SOX_EOF || strncmp(buf, "FSSD", (size_t)4) != 0)
102         {
103                 lsx_fail_errno(ft,SOX_EHDR,"Mac header type is not FSSD");
104                 return (SOX_EOF);
105         }
106 
107         /* Skip to byte 83 */
108         rc = lsx_skipbytes(ft, (size_t) 83-69);
109         if (rc)
110             return rc;
111 
112         /* Get essential numbers from the header */
113         lsx_readdw(ft, &datasize); /* bytes 83-86 */
114         lsx_readdw(ft, &rsrcsize); /* bytes 87-90 */
115 
116         /* Skip the rest of the header (total 128 bytes) */
117         rc = lsx_skipbytes(ft, (size_t) 128-91);
118         if (rc != 0)
119             return rc;
120 
121         /* The data fork must contain a "HCOM" header */
122         if (lsx_reads(ft, buf, (size_t)4) == SOX_EOF || strncmp(buf, "HCOM", (size_t)4) != 0)
123         {
124                 lsx_fail_errno(ft,SOX_EHDR,"Mac data fork is not HCOM");
125                 return (SOX_EOF);
126         }
127 
128         /* Then follow various parameters */
129         lsx_readdw(ft, &huffcount);
130         lsx_readdw(ft, &checksum);
131         lsx_readdw(ft, &compresstype);
132         if (compresstype > 1)
133         {
134                 lsx_fail_errno(ft,SOX_EHDR,"Bad compression type in HCOM header");
135                 return (SOX_EOF);
136         }
137         lsx_readdw(ft, &divisor);
138         if (divisor == 0 || divisor > 4)
139         {
140                 lsx_fail_errno(ft,SOX_EHDR,"Bad sampling rate divisor in HCOM header");
141                 return (SOX_EOF);
142         }
143         lsx_readw(ft, &dictsize);
144 
145         /* Translate to sox parameters */
146         ft->encoding.encoding = SOX_ENCODING_HCOM;
147         ft->encoding.bits_per_sample = 8;
148         ft->signal.rate = 22050 / divisor;
149         ft->signal.channels = 1;
150         ft->signal.length = huffcount;
151 
152         /* Allocate memory for the dictionary */
153         p->dictionary = lsx_malloc(511 * sizeof(dictent));
154 
155         /* Read dictionary */
156         for(i = 0; i < dictsize; i++) {
157                 lsx_readsw(ft, &(p->dictionary[i].dict_leftson));
158                 lsx_readsw(ft, &(p->dictionary[i].dict_rightson));
159                 lsx_debug("%d %d",
160                        p->dictionary[i].dict_leftson,
161                        p->dictionary[i].dict_rightson);
162                 if (!dictvalid(i, dictsize, p->dictionary[i].dict_leftson,
163                                p->dictionary[i].dict_rightson)) {
164                         lsx_fail_errno(ft, SOX_EHDR, "Invalid dictionary");
165                         return SOX_EOF;
166                 }
167         }
168         rc = lsx_skipbytes(ft, (size_t) 1); /* skip pad byte */
169         if (rc)
170             return rc;
171 
172         /* Initialized the decompression engine */
173         p->checksum = checksum;
174         p->deltacompression = compresstype;
175         if (!p->deltacompression)
176                 lsx_debug("HCOM data using value compression");
177         p->huffcount = huffcount;
178         p->cksum = 0;
179         p->dictentry = 0;
180         p->nrbits = -1; /* Special case to get first byte */
181 
182         return (SOX_SUCCESS);
183 }
184 
read_samples(sox_format_t * ft,sox_sample_t * buf,size_t len)185 static size_t read_samples(sox_format_t * ft, sox_sample_t *buf, size_t len)
186 {
187         register priv_t *p = (priv_t *) ft->priv;
188         int done = 0;
189         unsigned char sample_rate;
190 
191         if (p->nrbits < 0) {
192                 /* The first byte is special */
193                 if (p->huffcount == 0)
194                         return 0; /* Don't know if this can happen... */
195                 if (lsx_readb(ft, &sample_rate) == SOX_EOF)
196                 {
197                         return (0);
198                 }
199                 p->sample = sample_rate;
200                 *buf++ = SOX_UNSIGNED_8BIT_TO_SAMPLE(p->sample,);
201                 p->huffcount--;
202                 p->nrbits = 0;
203                 done++;
204                 len--;
205                 if (len == 0)
206                         return done;
207         }
208 
209         while (p->huffcount > 0) {
210                 if(p->nrbits == 0) {
211                         lsx_readdw(ft, &(p->current));
212                         if (lsx_eof(ft))
213                         {
214                                 lsx_fail_errno(ft,SOX_EOF,"unexpected EOF in HCOM data");
215                                 return (0);
216                         }
217                         p->cksum += p->current;
218                         p->nrbits = 32;
219                 }
220                 if(p->current & 0x80000000) {
221                         p->dictentry =
222                                 p->dictionary[p->dictentry].dict_rightson;
223                 } else {
224                         p->dictentry =
225                                 p->dictionary[p->dictentry].dict_leftson;
226                 }
227                 p->current = p->current << 1;
228                 p->nrbits--;
229                 if(p->dictionary[p->dictentry].dict_leftson < 0) {
230                         short datum;
231                         datum = p->dictionary[p->dictentry].dict_rightson;
232                         if (!p->deltacompression)
233                                 p->sample = 0;
234                         p->sample = (p->sample + datum) & 0xff;
235                         p->huffcount--;
236                         *buf++ = SOX_UNSIGNED_8BIT_TO_SAMPLE(p->sample,);
237                         p->dictentry = 0;
238                         done++;
239                         len--;
240                         if (len == 0)
241                                 break;
242                 }
243         }
244 
245         return done;
246 }
247 
stopread(sox_format_t * ft)248 static int stopread(sox_format_t * ft)
249 {
250         register priv_t *p = (priv_t *) ft->priv;
251 
252         if (p->huffcount != 0)
253         {
254                 lsx_fail_errno(ft,SOX_EFMT,"not all HCOM data read");
255                 return (SOX_EOF);
256         }
257         if(p->cksum != p->checksum)
258         {
259                 lsx_fail_errno(ft,SOX_EFMT,"checksum error in HCOM data");
260                 return (SOX_EOF);
261         }
262         free(p->dictionary);
263         p->dictionary = NULL;
264         return (SOX_SUCCESS);
265 }
266 
267 #define BUFINCR (10*BUFSIZ)
268 
startwrite(sox_format_t * ft)269 static int startwrite(sox_format_t * ft)
270 {
271   priv_t * p = (priv_t *) ft->priv;
272 
273   p->size = BUFINCR;
274   p->pos = 0;
275   p->data = lsx_malloc(p->size);
276   return SOX_SUCCESS;
277 }
278 
write_samples(sox_format_t * ft,const sox_sample_t * buf,size_t len)279 static size_t write_samples(sox_format_t * ft, const sox_sample_t *buf, size_t len)
280 {
281   priv_t *p = (priv_t *) ft->priv;
282   sox_sample_t datum;
283   size_t i;
284 
285   if (len == 0)
286     return 0;
287 
288   if (p->pos == INT32_MAX)
289     return SOX_EOF;
290 
291   if (p->pos + len > INT32_MAX) {
292     lsx_warn("maximum file size exceeded");
293     len = INT32_MAX - p->pos;
294   }
295 
296   if (p->pos + len > p->size) {
297     p->size = ((p->pos + len) / BUFINCR + 1) * BUFINCR;
298     p->data = lsx_realloc(p->data, p->size);
299   }
300 
301   for (i = 0; i < len; i++) {
302     SOX_SAMPLE_LOCALS;
303     datum = *buf++;
304     p->data[p->pos++] = SOX_SAMPLE_TO_UNSIGNED_8BIT(datum, ft->clips);
305   }
306 
307   return len;
308 }
309 
makecodes(int e,int c,int s,int b,dictent newdict[511],long codes[256],long codesize[256])310 static void makecodes(int e, int c, int s, int b, dictent newdict[511], long codes[256], long codesize[256])
311 {
312   assert(b);                    /* Prevent stack overflow */
313   if (newdict[e].dict_leftson < 0) {
314     codes[newdict[e].dict_rightson] = c;
315     codesize[newdict[e].dict_rightson] = s;
316   } else {
317     makecodes(newdict[e].dict_leftson, c, s + 1, b << 1, newdict, codes, codesize);
318     makecodes(newdict[e].dict_rightson, c + b, s + 1, b << 1, newdict, codes, codesize);
319   }
320 }
321 
putcode(sox_format_t * ft,long codes[256],long codesize[256],unsigned c,unsigned char ** df)322 static void putcode(sox_format_t * ft, long codes[256], long codesize[256], unsigned c, unsigned char **df)
323 {
324   priv_t *p = (priv_t *) ft->priv;
325   long code, size;
326   int i;
327 
328   code = codes[c];
329   size = codesize[c];
330   for(i = 0; i < size; i++) {
331     p->curword <<= 1;
332     if (code & 1)
333       p->curword += 1;
334     p->nbits++;
335     if (p->nbits == 32) {
336       put32_be(df, p->curword);
337       p->new_checksum += p->curword;
338       p->nbits = 0;
339       p->curword = 0;
340     }
341     code >>= 1;
342   }
343 }
344 
compress(sox_format_t * ft,unsigned char ** df,int32_t * dl)345 static void compress(sox_format_t * ft, unsigned char **df, int32_t *dl)
346 {
347   priv_t *p = (priv_t *) ft->priv;
348   int samplerate;
349   unsigned char *datafork = *df;
350   unsigned char *ddf, *dfp;
351   short dictsize;
352   int frequtable[256];
353   long codes[256], codesize[256];
354   dictent newdict[511];
355   int i, sample, j, k, d, l, frequcount;
356   int64_t csize;
357 
358   sample = *datafork;
359   memset(frequtable, 0, sizeof(frequtable));
360   memset(codes, 0, sizeof(codes));
361   memset(codesize, 0, sizeof(codesize));
362   memset(newdict, 0, sizeof(newdict));
363 
364   for (i = 1; i < *dl; i++) {
365     d = (datafork[i] - (sample & 0xff)) & 0xff; /* creates absolute entries LMS */
366     sample = datafork[i];
367     datafork[i] = d;
368     assert(d >= 0 && d <= 255); /* check our table is accessed correctly */
369     frequtable[d]++;
370   }
371   p->de = newdict;
372   for (i = 0; i < 256; i++)
373     if (frequtable[i] != 0) {
374       p->de->frequ = -frequtable[i];
375       p->de->dict_leftson = -1;
376       p->de->dict_rightson = i;
377       p->de++;
378     }
379   frequcount = p->de - newdict;
380   for (i = 0; i < frequcount; i++) {
381     for (j = i + 1; j < frequcount; j++) {
382       if (newdict[i].frequ > newdict[j].frequ) {
383         k = newdict[i].frequ;
384         newdict[i].frequ = newdict[j].frequ;
385         newdict[j].frequ = k;
386         k = newdict[i].dict_leftson;
387         newdict[i].dict_leftson = newdict[j].dict_leftson;
388         newdict[j].dict_leftson = k;
389         k = newdict[i].dict_rightson;
390         newdict[i].dict_rightson = newdict[j].dict_rightson;
391         newdict[j].dict_rightson = k;
392       }
393     }
394   }
395   while (frequcount > 1) {
396     j = frequcount - 1;
397     p->de->frequ = newdict[j - 1].frequ;
398     p->de->dict_leftson = newdict[j - 1].dict_leftson;
399     p->de->dict_rightson = newdict[j - 1].dict_rightson;
400     l = newdict[j - 1].frequ + newdict[j].frequ;
401     for (i = j - 2; i >= 0 && l < newdict[i].frequ; i--)
402       newdict[i + 1] = newdict[i];
403     i = i + 1;
404     newdict[i].frequ = l;
405     newdict[i].dict_leftson = j;
406     newdict[i].dict_rightson = p->de - newdict;
407     p->de++;
408     frequcount--;
409   }
410   dictsize = p->de - newdict;
411   makecodes(0, 0, 0, 1, newdict, codes, codesize);
412   csize = 0;
413   for (i = 0; i < 256; i++)
414     csize += frequtable[i] * codesize[i];
415   l = (((csize + 31) >> 5) << 2) + 24 + dictsize * 4;
416   lsx_debug("  Original size: %6d bytes", *dl);
417   lsx_debug("Compressed size: %6d bytes", l);
418   datafork = lsx_malloc((size_t)l);
419   ddf = datafork + 22;
420   for(i = 0; i < dictsize; i++) {
421     put16_be(&ddf, newdict[i].dict_leftson);
422     put16_be(&ddf, newdict[i].dict_rightson);
423   }
424   *ddf++ = 0;
425   *ddf++ = *(*df)++;
426   p->new_checksum = 0;
427   p->nbits = 0;
428   p->curword = 0;
429   for (i = 1; i < *dl; i++)
430     putcode(ft, codes, codesize, *(*df)++, &ddf);
431   if (p->nbits != 0) {
432     codes[0] = 0;
433     codesize[0] = 32 - p->nbits;
434     putcode(ft, codes, codesize, 0, &ddf);
435   }
436   memcpy(datafork, "HCOM", (size_t)4);
437   dfp = datafork + 4;
438   put32_be(&dfp, *dl);
439   put32_be(&dfp, p->new_checksum);
440   put32_be(&dfp, 1);
441   samplerate = 22050 / ft->signal.rate + .5;
442   put32_be(&dfp, samplerate);
443   put16_be(&dfp, dictsize);
444   *df = datafork;               /* reassign passed pointer to new datafork */
445   *dl = l;                      /* and its compressed length */
446 }
447 
448 /* End of hcom utility routines */
449 
stopwrite(sox_format_t * ft)450 static int stopwrite(sox_format_t * ft)
451 {
452   priv_t *p = (priv_t *) ft->priv;
453   unsigned char *compressed_data = p->data;
454   int32_t compressed_len = p->pos;
455   int rc = SOX_SUCCESS;
456 
457   /* Compress it all at once */
458   if (compressed_len) {
459     compress(ft, &compressed_data, &compressed_len);
460     free(p->data);
461   }
462 
463   /* Write the header */
464   lsx_writebuf(ft, "\000\001A", (size_t) 3); /* Dummy file name "A" */
465   lsx_padbytes(ft, (size_t) 65-3);
466   lsx_writes(ft, "FSSD");
467   lsx_padbytes(ft, (size_t) 83-69);
468   lsx_writedw(ft, (unsigned) compressed_len); /* compressed_data size */
469   lsx_writedw(ft, 0); /* rsrc size */
470   lsx_padbytes(ft, (size_t) 128 - 91);
471   if (lsx_error(ft)) {
472     lsx_fail_errno(ft, errno, "write error in HCOM header");
473     rc = SOX_EOF;
474   } else if (lsx_writebuf(ft, compressed_data, compressed_len) != compressed_len) {
475     /* Write the compressed_data fork */
476     lsx_fail_errno(ft, errno, "can't write compressed HCOM data");
477     rc = SOX_EOF;
478   }
479   free(compressed_data);
480 
481   if (rc == SOX_SUCCESS)
482     /* Pad the compressed_data fork to a multiple of 128 bytes */
483     lsx_padbytes(ft, 128u - (compressed_len % 128));
484 
485   return rc;
486 }
487 
LSX_FORMAT_HANDLER(hcom)488 LSX_FORMAT_HANDLER(hcom)
489 {
490   static char const * const names[]       = {"hcom", NULL};
491   static sox_rate_t   const write_rates[] = {22050,22050/2,22050/3,22050/4.,0};
492   static unsigned     const write_encodings[] = {
493     SOX_ENCODING_HCOM, 8, 0, 0};
494   static sox_format_handler_t handler = {SOX_LIB_VERSION_CODE,
495     "Mac FSSD files with Huffman compression",
496     names, SOX_FILE_BIG_END|SOX_FILE_MONO,
497     startread, read_samples, stopread,
498     startwrite, write_samples, stopwrite,
499     NULL, write_encodings, write_rates, sizeof(priv_t)
500   };
501   return &handler;
502 }
503