int bmp_isfile(const uint8_t *data, size_t input_len, size_t *lengthptr) { if (input_len < (BMP_HEADER_SIZE + DIB_HEADER_SIZE) || !IS_BMP_MAGIC(data)) return 0; const struct bmp_header *header = (const struct bmp_header *)data; size_t filesize = le32toh(header->filesize); uint16_t reserved1 = le16toh(header->reserved1); uint16_t reserved2 = le16toh(header->reserved2); size_t dataoffset = le32toh(header->dataoffset); size_t headersize = le32toh(header->headersize); int32_t width = le32toh(header->width); int32_t height = le32toh(header->height); uint16_t planes = le16toh(header->planes); uint16_t bpp = le16toh(header->bpp); uint32_t compression = le32toh(header->compression); size_t datasize = le32toh(header->datasize); size_t palettecolors = le32toh(header->palettecolors); size_t colortblsize = 4 * palettecolors; // integer overflow? if (SIZE_MAX / 4 < palettecolors) return 0; // sanity of all sizes and offsets if (filesize < (BMP_HEADER_SIZE + DIB_HEADER_SIZE) || dataoffset >= filesize || datasize >= filesize || headersize >= filesize || headersize < DIB_HEADER_SIZE || colortblsize >= filesize || dataoffset < (BMP_HEADER_SIZE + headersize + colortblsize) || filesize - datasize < dataoffset) return 0; // legal range of values? if (width <= 0 || height == 0 || planes != 1 || bpp == 0 || reserved1 != 0 || reserved2 != 0 || compression > 6) return 0; if (filesize > input_len) return 0; if (lengthptr) *lengthptr = filesize; return 1; }
int do_extract(const uint8_t *filedata, size_t filesize, const struct extract_options *options, size_t *numfilesptr, size_t *sumsizeptr) { const uint8_t *ptr = NULL, *end = NULL; enum fileformat format = NONE; size_t sumsize = 0; size_t length = 0; int success = 1; int formats = options->formats; char *outfilename = NULL; size_t numfiles = 0; const char *filename = basename(options->filepath); // max. ext length is 16 characters size_t namelen = strlen(options->outdir) + strlen(filename) + 37; struct mpg123_info mpg123; struct ogg_info ogg; struct file_info info = {0, 0}; size_t count = 0; // e.g. for tracks count in midi const uint8_t *audio_start = NULL; size_t input_len = 0; outfilename = malloc(namelen); if (outfilename == NULL) { perror(options->filepath); goto error; } if (!options->quiet) { double slice_size = 0; const char *slice_unit = format_size(filesize, &slice_size); printf("Extracting 0x%08"PRIx64" ... 0x%08"PRIx64" (%g %s) from %s\n", options->offset, options->offset + filesize, slice_size, slice_unit, options->filepath); } #define WRITE_FILE(data, length, ext) \ if (write_file((data), length, options, filename, (size_t)((data) - filedata), (ext), outfilename, namelen)) \ { \ ++ numfiles; \ sumsize += length; \ } ptr = filedata; end = filedata + filesize; for (input_len = filesize; input_len >= 4; input_len = (size_t)(end - ptr)) { uint32_t magic = MAGIC(ptr); if (formats & OGG && magic == OGG_MAGIC && ogg_ispage(ptr, input_len, &ogg)) { uint32_t pageno = ogg.pageno; audio_start = ptr; for (;;) { ptr += ogg.length; if (!ogg_ispage(ptr, (size_t)(end - ptr), &ogg) || ogg.pageno <= pageno) break; pageno = ogg.pageno; } WRITE_FILE(audio_start, ptr - audio_start, "ogg"); continue; } if (formats & RIFF && magic == RIFF_MAGIC && riff_isfile(ptr, input_len, &info)) { WRITE_FILE(ptr, info.length, info.ext); ptr += info.length; continue; } if (formats & AIFF && magic == FORM_MAGIC && aiff_isfile(ptr, input_len, &info)) { WRITE_FILE(ptr, info.length, info.ext); ptr += info.length; continue; } if (formats & MIDI && magic == MIDI_MAGIC && midi_isheader(ptr, input_len, &length, &count)) { audio_start = ptr; do { ptr += length; } while (count-- > 0 && midi_istrack(ptr, (size_t)(end - ptr), &length)); if (count != 0 && !(options->quiet)) { fprintf(stderr, "warning: midi file misses %"PRIzu" tracks\n", count); } WRITE_FILE(audio_start, ptr - audio_start, "mid"); continue; } format = NONE; if (formats & ID3v2 && IS_ID3v2_MAGIC(ptr) && id3v2_istag(ptr, input_len, 0, &length)) { format = ID3v2; } if (formats & MPG123 && IS_MPG123_MAGIC(ptr)) { format = MPG123; length = 0; } if (format & (ID3v2 | MPG123) && mpg123_isframe(ptr + length, input_len - length, &mpg123)) { uint8_t version = mpg123.version; uint8_t layer = mpg123.layer; audio_start = ptr; ptr += length; do { ptr += mpg123.frame_size; } while (mpg123_isframe(ptr, (size_t)(end - ptr), &mpg123) && mpg123.version == version && mpg123.layer == layer); if (id3v1_istag(ptr, (size_t)(end - ptr), &length)) { ptr += length; } if (formats & ID3v2 && id3v2_istag(ptr, (size_t)(end - ptr), 1, &length)) { ptr += length; } WRITE_FILE(audio_start, ptr - audio_start, layer == 1 ? "mp1" : layer == 2 ? "mp2" : layer == 3 ? "mp3" : "mpg"); continue; } if (formats & IT && magic == IT_MAGIC && it_isfile(ptr, input_len, &length)) { WRITE_FILE(ptr, length, "it"); ptr += length; continue; } if (formats & XM && magic == XM_MAGIC && xm_isfile(ptr, input_len, &length)) { WRITE_FILE(ptr, length, "xm"); ptr += length; continue; } if (formats & ASF && magic == ASF_MAGIC && asf_isfile(ptr, input_len, &length)) { WRITE_FILE(ptr, length, "asf"); ptr += length; continue; } if (formats & AU && magic == AU_MAGIC && au_isfile(ptr, input_len, &length)) { WRITE_FILE(ptr, length, "au"); ptr += length; continue; } if (formats & PNG && magic == PNG_MAGIC && png_isfile(ptr, input_len, &length)) { WRITE_FILE(ptr, length, "png"); ptr += length; continue; } if (formats & GIF && magic == GIF_MAGIC && gif_isfile(ptr, input_len, &length)) { WRITE_FILE(ptr, length, "gif"); ptr += length; continue; } if (formats & (MPEG1 | MPEGPS | MPEGVS) && IS_MPEG_MAGIC(magic) && mpeg_isfile(ptr, input_len, formats, &length)) { WRITE_FILE(ptr, length, "mpg"); ptr += length; continue; } if (formats & MPEGTS && IS_MPEG_TS_MAGIC(ptr) && mpeg_isfile(ptr, input_len, formats, &length)) { WRITE_FILE(ptr, length, "mpg"); ptr += length; continue; } if (formats & JPEG && IS_JPG_MAGIC(magic) && jpg_isfile(ptr, input_len, &length)) { WRITE_FILE(ptr, length, "jpg"); ptr += length; continue; } if (formats & BINK && IS_BINK_MAGIC(magic) && bink_isfile(ptr, input_len, &length)) { WRITE_FILE(ptr, length, "bik"); ptr += length; continue; } if (formats & BMP && IS_BMP_MAGIC(ptr) && bmp_isfile(ptr, input_len, &length)) { WRITE_FILE(ptr, length, "bmp"); ptr += length; continue; } if (formats & SMK && IS_SMK_MAGIC(magic) && smk_isfile(ptr, input_len, &length)) { WRITE_FILE(ptr, length, "smk"); ptr += length; continue; } if (formats & MP4 && input_len > MP4_HEADER_SIZE && MAGIC(ptr + MP4_MAGIC_OFFSET) == MP4_MAGIC && mp4_isfile(ptr, input_len, &info)) { WRITE_FILE(ptr, info.length, info.ext); ptr += info.length; continue; } if (formats & S3M && input_len > S3M_MAGIC_OFFSET + 4 && MAGIC(ptr + S3M_MAGIC_OFFSET) == S3M_MAGIC && s3m_isfile(ptr, input_len, &length)) { WRITE_FILE(ptr, length, "s3m"); ptr += length; continue; } if (formats & MOD && input_len > MOD_MAGIC_OFFSET + 4) { const uint8_t *modmagic = ptr + MOD_MAGIC_OFFSET; if (IS_MOD_MAGIC(modmagic) && mod_isfile(ptr, input_len, &length)) { WRITE_FILE(ptr, length, "mod"); ptr += length; continue; } } if (formats & UTF_32LE && utf32le_isfile(ptr, input_len, &info)) { WRITE_FILE(ptr, info.length, info.ext); ptr += info.length; continue; } if (formats & UTF_32BE && utf32be_isfile(ptr, input_len, &info)) { WRITE_FILE(ptr, info.length, info.ext); ptr += info.length; continue; } if (formats & UTF_16LE && utf16le_isfile(ptr, input_len, &info)) { WRITE_FILE(ptr, info.length, info.ext); ptr += info.length; continue; } if (formats & UTF_16BE && utf16be_isfile(ptr, input_len, &info)) { WRITE_FILE(ptr, info.length, info.ext); ptr += info.length; continue; } if (formats & UTF_8 && utf8_isfile(ptr, input_len, &info)) { WRITE_FILE(ptr, info.length, info.ext); ptr += info.length; continue; } else if (formats & ASCII && ascii_isfile(ptr, input_len, &info)) { WRITE_FILE(ptr, info.length, info.ext); ptr += info.length; continue; } ++ ptr; } goto cleanup; error: success = 0; cleanup: if (outfilename) free(outfilename); if (numfilesptr) *numfilesptr = numfiles; if (sumsizeptr) *sumsizeptr = sumsize; return success; }