Minor refactoring of the IFF and box-format parsers
[deark.git] / modules / mp3.c
blob2b35a01ceb8a8397e7841e2f82b6b479bc741b8f
1 // This file is part of Deark.
2 // Copyright (C) 2017 Jason Summers
3 // See the file COPYING for terms of use.
5 // MP3, and other MPEG audio
6 // APE tag
7 // Monkey's Audio (.ape)
9 #include <deark-config.h>
10 #include <deark-private.h>
11 #include <deark-fmtutil.h>
12 DE_DECLARE_MODULE(de_module_mpegaudio);
13 DE_DECLARE_MODULE(de_module_apetag);
14 DE_DECLARE_MODULE(de_module_monkeys_audio);
16 typedef struct mp3ctx_struct {
17 int has_id3v2;
18 // Settings are for the current frame.
19 unsigned int version_id, layer_desc, has_crc;
20 unsigned int bitrate_idx, samprate_idx;
21 unsigned int has_padding, channel_mode;
22 unsigned int mode_extension;
23 unsigned int copyright_flag, orig_media_flag;
24 unsigned int emphasis;
25 int frame_count;
26 } mp3ctx;
28 struct ape_tag_header_footer {
29 u32 ape_ver, ape_flags;
30 i64 tag_size_raw, item_count;
31 i64 tag_startpos;
32 i64 tag_size_total;
33 i64 items_startpos;
34 i64 items_size;
35 int has_header;
38 static int is_apetag_sig_at(dbuf *f, i64 pos)
40 return !dbuf_memcmp(f, pos, "APETAGEX", 8);
43 static const char *get_ape_item_type_name(unsigned int t)
45 const char *name;
47 switch(t) {
48 case 0: name = "UTF-8 text"; break;
49 case 1: name = "binary"; break;
50 case 2: name = "locator"; break;
51 default: name = "?";
53 return name;
56 static void do_ape_text_item(deark *c, struct ape_tag_header_footer *ah,
57 i64 pos, i64 len)
59 de_encoding encoding;
60 de_ucstring *s = NULL;
62 encoding = (ah->ape_ver>=2000)?DE_ENCODING_UTF8:DE_ENCODING_ASCII;
63 s = ucstring_create(c);
64 dbuf_read_to_ucstring_n(c->infile, pos, len, DE_DBG_MAX_STRLEN,
65 s, 0, encoding);
66 de_dbg(c, "value: \"%s\"", ucstring_getpsz(s));
67 ucstring_destroy(s);
70 static int do_ape_binary_item(deark *c, struct ape_tag_header_footer *ah,
71 i64 pos, i64 len, struct de_stringreaderdata *key)
73 struct de_stringreaderdata *name = NULL;
74 i64 nbytes_to_scan;
75 i64 img_pos, img_len;
76 de_finfo *fi = NULL;
77 char *ext = NULL;
78 int retval = 0;
80 if(de_strncasecmp(key->sz, "cover art", 9)) {
81 goto done;
84 nbytes_to_scan = len;
85 if(nbytes_to_scan>256) nbytes_to_scan=256;
86 name = dbuf_read_string(c->infile, pos, nbytes_to_scan, nbytes_to_scan,
87 DE_CONVFLAG_STOP_AT_NUL, DE_ENCODING_ASCII);
88 if(!name->found_nul) goto done;
90 img_pos = pos + name->bytes_consumed;
91 img_len = len - name->bytes_consumed;
92 if(len < 16) goto done;
94 fi = de_finfo_create(c);
95 if(c->filenames_from_file) {
96 de_finfo_set_name_from_ucstring(c, fi, name->str, 0);
98 else {
99 u8 sig[2];
101 de_finfo_set_name_from_sz(c, fi, "cover_art", 0, DE_ENCODING_LATIN1);
103 de_read(sig, img_pos, 2);
104 if(sig[0]==0x89 && sig[1]==0x50) ext="png";
105 else if(sig[0]==0xff && sig[1]==0xd8) ext="jpg";
106 else ext="bin";
108 dbuf_create_file_from_slice(c->infile, img_pos, img_len, ext,
109 fi, DE_CREATEFLAG_IS_AUX);
110 retval = 1;
112 done:
113 de_finfo_destroy(c, fi);
114 de_destroy_stringreaderdata(c, name);
115 return retval;
118 static int do_ape_item(deark *c, struct ape_tag_header_footer *ah,
119 i64 pos1, i64 bytes_avail, i64 *bytes_consumed)
121 i64 item_value_len;
122 i64 pos = pos1;
123 u32 flags;
124 unsigned int item_type;
125 struct de_stringreaderdata *key = NULL;
126 int handled = 0;
127 int retval = 0;
129 de_dbg(c, "APE item at %"I64_FMT, pos1);
130 de_dbg_indent(c, 1);
132 item_value_len = de_getu32le(pos);
133 pos += 4;
135 flags = (u32)de_getu32le(pos);
136 de_dbg(c, "flags: 0x%08x", (unsigned int)flags);
137 if(ah->ape_ver>=2000) {
138 de_dbg_indent(c, 1);
139 item_type = (flags&0x00000006)>>1;
140 de_dbg(c, "type: %u (%s)", item_type, get_ape_item_type_name(item_type));
141 de_dbg_indent(c, -1);
143 else {
144 item_type = 0;
146 pos += 4;
148 key = dbuf_read_string(c->infile, pos, 256, 256, DE_CONVFLAG_STOP_AT_NUL,
149 DE_ENCODING_ASCII);
150 if(!key->found_nul) goto done;
151 de_dbg(c, "key: \"%s\"", ucstring_getpsz(key->str));
152 pos += key->bytes_consumed;
154 de_dbg(c, "item data at %"I64_FMT", len=%"I64_FMT, pos, item_value_len);
155 de_dbg_indent(c, 1);
156 if(item_type==0 || item_type==2) {
157 do_ape_text_item(c, ah, pos, item_value_len);
158 handled = 1;
160 else if(item_type==1) { // binary
161 handled = do_ape_binary_item(c, ah, pos, item_value_len, key);
164 if(!handled && c->debug_level>=2) {
165 de_dbg_hexdump(c, c->infile, pos, item_value_len, 256, NULL, 0x1);
167 de_dbg_indent(c, -1);
169 pos += item_value_len;
170 *bytes_consumed = pos - pos1;
171 retval = 1;
173 done:
174 de_dbg_indent(c, -1);
175 de_destroy_stringreaderdata(c, key);
176 return retval;
178 static void do_ape_item_list(deark *c, struct ape_tag_header_footer *ah,
179 i64 pos1, i64 len)
181 i64 pos = pos1;
183 de_dbg(c, "APE items at %"I64_FMT", len=%"I64_FMT, pos1, len);
184 de_dbg_indent(c, 1);
185 while(1) {
186 i64 bytes_consumed = 0;
188 if(pos >= pos1+len) break;
189 if(!do_ape_item(c, ah, pos, pos1+len-pos, &bytes_consumed)) {
190 goto done;
192 if(bytes_consumed<1) goto done;
194 pos += bytes_consumed;
196 done:
197 de_dbg_indent(c, -1);
200 static int do_ape_tag_header_or_footer(deark *c, struct ape_tag_header_footer *ah,
201 i64 pos1, int is_footer)
203 int retval = 0;
204 int saved_indent_level;
206 de_dbg_indent_save(c, &saved_indent_level);
207 de_dbg(c, "APE tag %s at %"I64_FMT, (is_footer?"footer":"header"), pos1);
208 de_dbg_indent(c, 1);
210 ah->ape_ver = (u32)de_getu32le(pos1+8);
211 de_dbg(c, "version: %u", (unsigned int)ah->ape_ver);
212 ah->tag_size_raw = de_getu32le(pos1+12);
213 de_dbg(c, "tag size: %d", (int)ah->tag_size_raw);
214 if(is_footer) {
215 ah->items_startpos = pos1 + 32 - ah->tag_size_raw;
216 ah->items_size = pos1 - ah->items_startpos;
218 ah->item_count = de_getu32le(pos1+16);
219 de_dbg(c, "item count: %d", (int)ah->item_count);
220 ah->ape_flags = (u32)de_getu32le(pos1+20);
221 de_dbg(c, "flags: 0x%08x", (unsigned int)ah->ape_flags);
222 if(ah->ape_ver>=2000) {
223 ah->has_header = (ah->ape_flags&0x80000000U) ? 1 : 0;
226 ah->tag_size_total = ah->tag_size_raw;
227 if(ah->has_header)
228 ah->tag_size_total += 32;
230 if(ah->ape_ver<1000 || ah->ape_ver>=3000) {
231 de_warn(c, "Unrecognized APE tag version: %u", (unsigned int)ah->ape_ver);
232 goto done;
235 if(is_footer) {
236 ah->tag_startpos = pos1 + 32 - ah->tag_size_total;
237 de_dbg(c, "calculated start of APE tag: %"I64_FMT, ah->tag_startpos);
239 retval = 1;
240 done:
241 de_dbg_indent_restore(c, saved_indent_level);
242 return retval;
245 static int do_ape_tag_internal(deark *c, i64 endpos, i64 *ape_tag_bytes_consumed)
247 struct ape_tag_header_footer *af = NULL;
248 i64 footer_startpos = endpos - 32;
249 int retval = 0;
251 if(!is_apetag_sig_at(c->infile, footer_startpos)) {
252 de_warn(c, "Expected APE tag footer not found at %"I64_FMT, footer_startpos);
253 goto done;
255 af = de_malloc(c, sizeof(struct ape_tag_header_footer));
256 if(!do_ape_tag_header_or_footer(c, af, footer_startpos, 1)) goto done;
257 *ape_tag_bytes_consumed = af->tag_size_total;
259 do_ape_item_list(c, af, af->items_startpos, af->tag_size_raw - 32);
261 retval = 1;
262 done:
263 de_free(c, af);
264 return retval;
267 static void de_run_apetag(deark *c, de_module_params *mparams)
269 i64 endpos;
270 i64 bytes_consumed = 0;
272 // The calling module should provide a slice that starts at the beginning
273 // of the file, and ends at the end of the APE tag (which is often, but
274 // not always, the end of the file).
275 // This does not very flexible, but it can be improved if need be.
277 // If we successfully process an APE tag, we set the
278 // 0x1 bit of mparams->out_params.flags, and set
279 // mparams->out_params.int64_1 to the total size of the APE tag.
281 endpos = c->infile->len;
283 if(!do_ape_tag_internal(c, endpos, &bytes_consumed)) goto done;
284 if(mparams) {
285 mparams->out_params.flags = 0x1;
286 mparams->out_params.int64_1 = bytes_consumed;
288 done:
292 void de_module_apetag(deark *c, struct deark_module_info *mi)
294 mi->id = "apetag";
295 mi->desc = "APE tag";
296 mi->run_fn = de_run_apetag;
297 mi->identify_fn = NULL;
298 mi->flags |= DE_MODFLAG_HIDDEN;
301 static int do_ape_tag_if_exists(deark *c, i64 endpos, i64 *ape_tag_bytes_consumed)
303 i64 footer_startpos;
304 de_module_params *mparams = NULL;
305 int saved_indent_level;
306 int retval = 0;
308 de_dbg_indent_save(c, &saved_indent_level);
309 *ape_tag_bytes_consumed = 0;
311 footer_startpos = endpos-32;
312 if(!is_apetag_sig_at(c->infile, footer_startpos)) {
313 goto done;
316 de_dbg(c, "APE tag found, ending at %"I64_FMT, endpos);
317 de_dbg_indent(c, 1);
318 mparams = de_malloc(c, sizeof(de_module_params));
319 de_run_module_by_id_on_slice(c, "apetag", mparams, c->infile, 0, endpos);
320 if(mparams->out_params.flags & 0x1) {
321 // apetag module told us the size of the APE tag data.
322 *ape_tag_bytes_consumed = mparams->out_params.int64_1;
324 else {
325 goto done;
327 de_dbg_indent(c, -1);
329 retval = 1;
330 done:
331 de_free(c, mparams);
332 de_dbg_indent_restore(c, saved_indent_level);
333 return retval;
336 static const char *get_mp3_ver_id_name(unsigned int n)
338 const char *name;
339 switch(n) {
340 case 0: name = "MPEG v2.5"; break;
341 case 2: name = "MPEG v2"; break;
342 case 3: name = "MPEG v1"; break;
343 default: name = "?";
345 return name;
348 static const char *get_mp3_layer_desc_name(unsigned int n)
350 const char *name;
351 switch(n) {
352 case 1: name = "Layer III"; break;
353 case 2: name = "Layer II"; break;
354 case 3: name = "Layer I"; break;
355 default: name = "?";
357 return name;
360 static const char *get_mp3_channel_mode_name(unsigned int n)
362 const char *name;
363 switch(n) {
364 case 0: name = "Stereo"; break;
365 case 1: name = "Joint stereo"; break;
366 case 2: name = "Dual channel"; break;
367 case 3: name = "Single channel"; break;
368 default: name = "?";
370 return name;
373 // Returns a copy of the buf ptr
374 static char *get_bitrate_name(char *buf, size_t buflen,
375 unsigned int bitrate_idx, unsigned int version_id, unsigned int layer_desc)
377 static const u16 tbl[5][16] = {
378 {0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0},
379 {0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0},
380 {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0},
381 {0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0},
382 {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0}};
383 unsigned int tbl_to_use = 0;
384 unsigned int br = 0;
386 if(version_id==0x03) { // v1
387 if(layer_desc==0x03) tbl_to_use=0; // Layer 1
388 else if(layer_desc==0x02) tbl_to_use=1; // Layer 2
389 else if(layer_desc==0x01) tbl_to_use=2; // Layer 3
390 else goto done;
392 else if(version_id==0x02 || version_id==0x00) { // v2, v2.5
393 if(layer_desc==0x03) tbl_to_use=3; // Layer 1
394 else if(layer_desc==0x02 || layer_desc==0x01) tbl_to_use=4; // Layer 2,3
395 else goto done;
397 else {
398 goto done;
401 if(bitrate_idx>15) goto done;
402 br = (unsigned int)tbl[tbl_to_use][bitrate_idx];
404 done:
405 if(br>0)
406 de_snprintf(buf, buflen, "%u kbps", br);
407 else
408 de_strlcpy(buf, "?", buflen);
409 return buf;
412 static char *get_sampling_rate_name(char *buf, size_t buflen,
413 unsigned int sr_idx, unsigned int version_id, unsigned int layer_desc)
415 static const u32 tbl[3][4] = {
416 {44100, 48000, 32000, 0},
417 {22050, 24000, 16000, 0},
418 {11025, 12000, 8000, 0}};
419 unsigned int tbl_to_use = 0;
420 unsigned int sr = 0;
422 if(layer_desc<1 || layer_desc>3) goto done;
424 if(version_id==0x03) { // v1
425 tbl_to_use = 0;
427 else if(version_id==0x02) { // v2
428 tbl_to_use = 1;
430 else if(version_id==0x00) { // v2.5
431 tbl_to_use = 2;
433 else {
434 goto done;
437 if(sr_idx>3) goto done;
438 sr = (unsigned int)tbl[tbl_to_use][sr_idx];
440 done:
441 if(sr>0)
442 de_snprintf(buf, buflen, "%u Hz", sr);
443 else
444 de_strlcpy(buf, "?", buflen);
445 return buf;
448 static int find_mp3_frame_header(deark *c, mp3ctx *d, i64 pos1, i64 nbytes_avail,
449 i64 *skip_this_many_bytes)
451 u8 *buf = NULL;
452 i64 nbytes_in_buf;
453 i64 bpos = 0;
454 int retval = 0;
456 *skip_this_many_bytes = 0;
457 nbytes_in_buf = 65536;
458 if(nbytes_avail < nbytes_in_buf) nbytes_in_buf = nbytes_avail;
459 buf = de_malloc(c, nbytes_in_buf);
460 de_read(buf, pos1, nbytes_in_buf);
461 for(bpos=0; bpos<nbytes_in_buf-1; bpos++) {
462 if(buf[bpos]==0xff) {
463 if((buf[bpos+1]&0xe0) == 0xe0) {
464 *skip_this_many_bytes = bpos;
465 retval = 1;
466 goto done;
471 done:
472 de_free(c, buf);
473 return retval;
476 static void do_mp3_frame(deark *c, mp3ctx *d, i64 pos1, i64 len)
478 u32 x;
479 i64 pos = pos1;
480 int saved_indent_level;
481 char buf[32];
483 de_dbg_indent_save(c, &saved_indent_level);
484 x = (u32)de_getu32be(pos);
485 if((x & 0xffe00000U) != 0xffe00000U) {
486 int ret;
487 i64 num_bytes_to_skip = 0;
488 de_info(c, "Note: MP3/MPA frame header not found at %"I64_FMT". Scanning for frame header.", pos);
489 if(d->frame_count==0 && c->module_disposition==DE_MODDISP_AUTODETECT &&
490 d->has_id3v2)
492 // Format was presumably autodetected solely based on the existence
493 // of ID3v2 data.
494 de_warn(c, "This might not be an MPEG audio file. It might be an unrecognized "
495 "audio format.");
497 ret = find_mp3_frame_header(c, d, pos1, len, &num_bytes_to_skip);
498 if(!ret) {
499 de_err(c, "MP3/MPA frame header not found");
500 goto done;
502 pos += num_bytes_to_skip;
503 de_info(c, "Note: Possible MP3 frame header found at %"I64_FMT".", pos);
504 x = (u32)de_getu32be(pos);
507 de_dbg(c, "frame at %"I64_FMT, pos);
508 de_dbg_indent(c, 1);
509 de_dbg(c, "frame header: 0x%08x", (unsigned int)x);
510 de_dbg_indent(c, 1);
511 d->version_id = (x&0x00180000U)>>19;
512 de_dbg(c, "audio version id: %u (%s)", d->version_id, get_mp3_ver_id_name(d->version_id));
513 d->layer_desc = (x&0x00060000U)>>17;
514 de_dbg(c, "layer description: %u (%s)", d->layer_desc, get_mp3_layer_desc_name(d->layer_desc));
515 if(d->frame_count==0) {
516 if(d->layer_desc==1) {
517 de_declare_fmt(c, "MP3");
519 else if(d->layer_desc==2) {
520 de_declare_fmt(c, "MP2 audio");
522 else if(d->layer_desc==3) {
523 de_declare_fmt(c, "MP1 audio");
526 d->has_crc = (x&0x00010000U)>>16;
527 de_dbg(c, "has crc: %u", d->has_crc);
528 d->bitrate_idx = (x&0x0000f000U)>>12;
529 de_dbg(c, "bitrate id: %u (%s)", d->bitrate_idx,
530 get_bitrate_name(buf, sizeof(buf), d->bitrate_idx, d->version_id, d->layer_desc));
531 d->samprate_idx = (x&0x00000c00U)>>10;
532 de_dbg(c, "sampling rate frequency id: %u (%s)", d->samprate_idx,
533 get_sampling_rate_name(buf, sizeof(buf), d->samprate_idx, d->version_id, d->layer_desc));
534 d->has_padding = (x&0x00000200U)>>9;
535 de_dbg(c, "has padding: %u", d->has_padding);
536 d->channel_mode = (x&0x000000c0U)>>6;
537 de_dbg(c, "channel mode: %u (%s)", d->channel_mode, get_mp3_channel_mode_name(d->channel_mode));
538 if(d->channel_mode==1) {
539 d->mode_extension = (x&0x00000030U)>>4;
540 de_dbg(c, "mode extension: %u", d->mode_extension);
542 d->copyright_flag = (x&0x00000008U)>>3;
543 de_dbg(c, "copyright flag: %u", d->has_padding);
544 d->orig_media_flag = (x&0x00000004U)>>2;
545 de_dbg(c, "original media flag: %u", d->has_padding);
546 d->emphasis = (x&0x00000003U);
547 de_dbg(c, "emphasis: %u", d->emphasis);
548 //pos += 4;
549 d->frame_count++;
551 done:
552 de_dbg_indent_restore(c, saved_indent_level);
555 static void do_mp3_data(deark *c, mp3ctx *d, i64 pos1, i64 len)
557 de_dbg(c, "MP3/MPA data at %"I64_FMT", len=%"I64_FMT, pos1, len);
558 de_dbg_indent(c, 1);
559 do_mp3_frame(c, d, pos1, len);
560 // TODO: There are probably many frames. Should we look for more frames
561 // (in some cases?)?
562 de_dbg_indent(c, -1);
565 static void de_run_mpegaudio(deark *c, de_module_params *mparams)
567 mp3ctx *d = NULL;
568 i64 pos;
569 i64 endpos;
570 i64 ape_tag_len;
571 struct de_id3info id3i;
573 d = de_malloc(c, sizeof(mp3ctx));
575 fmtutil_handle_id3(c, c->infile, &id3i, 0);
576 d->has_id3v2 = id3i.has_id3v2;
577 pos = id3i.main_start;
578 endpos = id3i.main_end;
580 if(!id3i.has_id3v2) {
581 if(!dbuf_memcmp(c->infile, endpos-10, "3DI", 3)) {
582 de_warn(c, "Possible ID3v2 tag found at end of file (footer at %"I64_FMT"). "
583 "This is not supported.", endpos-10);
587 do_ape_tag_if_exists(c, endpos, &ape_tag_len);
588 endpos -= ape_tag_len;
590 do_mp3_data(c, d, pos, endpos-pos);
592 de_free(c, d);
595 static int de_identify_mpegaudio(deark *c)
597 unsigned int x;
598 unsigned int ver_id, lyr_id;
599 int has_mp1_ext = 0;
600 int has_mp2_ext = 0;
601 int has_mp3_ext = 0;
602 int has_any_ext;
603 int looks_valid = 0;
604 u8 has_id3v2;
605 i64 pos;
607 if(!c->detection_data->id3.detection_attempted) {
608 de_err(c, "mpegaudio detection requires id3 module");
609 return 0;
612 if(de_input_file_has_ext(c, "mp3")) {
613 has_mp3_ext = 1;
615 if(de_input_file_has_ext(c, "mp2")) {
616 has_mp2_ext = 1;
618 if(de_input_file_has_ext(c, "mp1")) {
619 has_mp1_ext = 1;
621 else if(de_input_file_has_ext(c, "mpa")) {
622 has_mp1_ext = 1;
623 has_mp2_ext = 1;
624 has_mp3_ext = 1;
626 has_any_ext = has_mp3_ext || has_mp2_ext || has_mp1_ext;
628 has_id3v2 = c->detection_data->id3.has_id3v2;
630 if(!has_id3v2 && !has_any_ext) {
631 // TODO: We could try harder to identify MP3.
632 return 0;
635 if(has_id3v2) {
636 pos = (i64)c->detection_data->id3.bytes_at_start;
638 else {
639 pos = 0;
642 x = (unsigned int)de_getu16be(pos);
643 if((x&0xffe0) == 0xffe0) {
644 ver_id = (x&0x0018)>>3;
645 lyr_id = (x&0x0006)>>1;
647 if(has_mp3_ext) {
648 if((lyr_id==1) && (ver_id!=1)) looks_valid = 1;
650 if(has_mp2_ext) {
651 if((lyr_id==2) && (ver_id==2 || ver_id==3)) looks_valid = 1;
653 if(has_mp1_ext) {
654 if((lyr_id==3) && (ver_id==2 || ver_id==3)) looks_valid = 1;
658 if(has_id3v2 && looks_valid) return 100;
659 if(has_id3v2 && !looks_valid) {
660 // This must be lower than the corresponding confidence for other
661 // audio formats that might start with ID3v2, like Ogg.
662 return 80;
664 if(looks_valid) {
665 return 100;
668 return 0;
671 void de_module_mpegaudio(deark *c, struct deark_module_info *mi)
673 mi->id = "mpegaudio";
674 mi->id_alias[0] = "mp3";
675 mi->desc = "MP3 / MPEG audio";
676 mi->run_fn = de_run_mpegaudio;
677 mi->identify_fn = de_identify_mpegaudio;
680 //// Monkey's Audio
682 static void de_run_monkeys_audio(deark *c, de_module_params *mparams)
684 i64 endpos = c->infile->len;
686 if(is_apetag_sig_at(c->infile, endpos-32)) {
687 de_dbg(c, "APE tag found, ending at %"I64_FMT, endpos);
688 de_dbg_indent(c, 1);
689 de_run_module_by_id_on_slice2(c, "apetag", NULL, c->infile, 0, endpos);
690 de_dbg_indent(c, -1);
694 static int ma_is_known_cmpr(unsigned int n)
696 if(n<1000 || n>5000) return 0;
697 if(n%1000) return 0;
698 return 1;
701 static int de_identify_monkeys_audio(deark *c)
703 unsigned int n;
705 if(dbuf_memcmp(c->infile, 0, "MAC ", 4)) return 0;
706 n = (unsigned int)de_getu16le(6);
707 if(ma_is_known_cmpr(n)) return 100;
708 n = (unsigned int)de_getu16le(52);
709 if(ma_is_known_cmpr(n)) return 100;
710 return 0;
713 void de_module_monkeys_audio(deark *c, struct deark_module_info *mi)
715 mi->id = "monkeys_audio";
716 mi->desc = "Monkey's Audio (.ape)";
717 mi->run_fn = de_run_monkeys_audio;
718 mi->identify_fn = de_identify_monkeys_audio;