/** * zlib_inflate_data_to_file: * @path : filename path of archive. * @valid_exts : Valid extensions of archive to be parsed. * If NULL, allow all. * @cdata : input data. * @csize : size of input data. * @size : output file size * @checksum : CRC32 checksum from input data. * * Decompress data to file. * * Returns: true (1) on success, otherwise false (0). **/ int zlib_inflate_data_to_file(zlib_file_handle_t *handle, int ret, const char *path, const char *valid_exts, const uint8_t *cdata, uint32_t csize, uint32_t size, uint32_t checksum) { if (handle) { zlib_stream_free(handle->stream); free(handle->stream); } if (!handle || ret == -1) { ret = 0; goto end; } handle->real_checksum = zlib_crc32_calculate(handle->data, size); #if 0 if (handle->real_checksum != checksum) { /* File CRC difers from ZIP CRC. */ printf("File CRC differs from ZIP CRC. File: 0x%x, ZIP: 0x%x.\n", (unsigned)handle->real_checksum, (unsigned)checksum); } #endif if (!zlib_write_file(path, handle->data, size)) GOTO_END_ERROR(); end: if (handle->data) free(handle->data); return ret; }
/** * zlib_extract_first_content_file: * @zip_path : filename path to ZIP archive. * @zip_path_size : size of ZIP archive. * @valid_exts : valid extensions for a content file. * @extraction_directory : the directory to extract temporary * unzipped content to. * * Extract first content file from archive. * * Returns : true (1) on success, otherwise false (0). **/ bool zlib_extract_first_content_file(char *zip_path, size_t zip_path_size, const char *valid_exts, const char *extraction_directory) { struct string_list *list; bool ret = true; struct zip_extract_userdata userdata = {0}; if (!valid_exts) { /* Libretro implementation does not have any valid extensions. * Cannot unzip without knowing this. */ return false; } list = string_split(valid_exts, "|"); if (!list) GOTO_END_ERROR(); userdata.zip_path = zip_path; userdata.zip_path_size = zip_path_size; userdata.extraction_directory = extraction_directory; userdata.ext = list; if (!zlib_parse_file(zip_path, valid_exts, zip_extract_cb, &userdata)) { /* Parsing ZIP failed. */ GOTO_END_ERROR(); } if (!userdata.found_content) { /* Didn't find any content that matched valid extensions * for libretro implementation. */ GOTO_END_ERROR(); } end: if (list) string_list_free(list); return ret; }
bool zlib_extract_first_rom(char *zip_path, size_t zip_path_size, const char *valid_exts) { bool ret; struct zip_extract_userdata userdata = {0}; struct string_list *list; if (!valid_exts) { RARCH_ERR("Libretro implementation does not have any valid extensions. Cannot unzip without knowing this.\n"); return false; } ret = true; list = string_split(valid_exts, "|"); if (!list) GOTO_END_ERROR(); userdata.zip_path = zip_path; userdata.zip_path_size = zip_path_size; userdata.ext = list; if (!zlib_parse_file(zip_path, zip_extract_cb, &userdata)) { RARCH_ERR("Parsing ZIP failed.\n"); GOTO_END_ERROR(); } if (!userdata.found_rom) { RARCH_ERR("Didn't find any ROMS that matched valid extensions for libretro implementation.\n"); GOTO_END_ERROR(); } end: if (list) string_list_free(list); return ret; }
bool zlib_inflate_data_to_file(const char *path, const uint8_t *cdata, uint32_t csize, uint32_t size, uint32_t crc32) { bool ret = true; uint8_t *out_data = (uint8_t*)malloc(size); if (!out_data) return false; uint32_t real_crc32 = 0; z_stream stream = {0}; if (inflateInit2(&stream, -MAX_WBITS) != Z_OK) GOTO_END_ERROR(); stream.next_in = (uint8_t*)cdata; stream.avail_in = csize; stream.next_out = out_data; stream.avail_out = size; if (inflate(&stream, Z_FINISH) != Z_STREAM_END) { inflateEnd(&stream); GOTO_END_ERROR(); } inflateEnd(&stream); real_crc32 = crc32_calculate(out_data, size); if (real_crc32 != crc32) RARCH_WARN("File CRC differs from ZIP CRC. File: 0x%x, ZIP: 0x%x.\n", (unsigned)real_crc32, (unsigned)crc32); if (!write_file(path, out_data, size)) GOTO_END_ERROR(); end: free(out_data); return ret; }
bool png_process_ihdr(struct png_ihdr *ihdr) { unsigned i; bool ret = true; switch (ihdr->color_type) { case PNG_IHDR_COLOR_RGB: case PNG_IHDR_COLOR_GRAY_ALPHA: case PNG_IHDR_COLOR_RGBA: if (ihdr->depth != 8 && ihdr->depth != 16) GOTO_END_ERROR(); break; case PNG_IHDR_COLOR_GRAY: { static const unsigned valid_bpp[] = { 1, 2, 4, 8, 16 }; bool correct_bpp = false; for (i = 0; i < ARRAY_SIZE(valid_bpp); i++) { if (valid_bpp[i] == ihdr->depth) { correct_bpp = true; break; } } if (!correct_bpp) GOTO_END_ERROR(); } break; case PNG_IHDR_COLOR_PLT: { static const unsigned valid_bpp[] = { 1, 2, 4, 8 }; bool correct_bpp = false; for (i = 0; i < ARRAY_SIZE(valid_bpp); i++) { if (valid_bpp[i] == ihdr->depth) { correct_bpp = true; break; } } if (!correct_bpp) GOTO_END_ERROR(); } break; default: GOTO_END_ERROR(); } #ifdef RPNG_TEST fprintf(stderr, "IHDR: (%u x %u), bpc = %u, palette = %s, color = %s, alpha = %s, adam7 = %s.\n", ihdr->width, ihdr->height, ihdr->depth, ihdr->color_type == PNG_IHDR_COLOR_PLT ? "yes" : "no", ihdr->color_type & PNG_IHDR_COLOR_RGB ? "yes" : "no", ihdr->color_type & PNG_IHDR_COLOR_GRAY_ALPHA ? "yes" : "no", ihdr->interlace == 1 ? "yes" : "no"); #endif if (ihdr->compression != 0) GOTO_END_ERROR(); end: return ret; }
bool zlib_parse_file(const char *file, zlib_file_cb file_cb, void *userdata) { const uint8_t *footer = NULL; const uint8_t *directory = NULL; bool ret = true; const uint8_t *data = NULL; const struct zlib_file_backend *backend = zlib_get_default_file_backend(); if (!backend) return NULL; ssize_t zip_size = 0; void *handle = backend->open(file); if (!handle) GOTO_END_ERROR(); zip_size = backend->size(handle); if (zip_size < 22) GOTO_END_ERROR(); data = backend->data(handle); footer = data + zip_size - 22; for (;; footer--) { if (footer <= data + 22) GOTO_END_ERROR(); if (read_le(footer, 4) == 0x06054b50) { unsigned comment_len = read_le(footer + 20, 2); if (footer + 22 + comment_len == data + zip_size) break; } } directory = data + read_le(footer + 16, 4); for (;;) { uint32_t signature = read_le(directory + 0, 4); if (signature != 0x02014b50) break; unsigned cmode = read_le(directory + 10, 2); uint32_t crc32 = read_le(directory + 16, 4); uint32_t csize = read_le(directory + 20, 4); uint32_t size = read_le(directory + 24, 4); unsigned namelength = read_le(directory + 28, 2); unsigned extralength = read_le(directory + 30, 2); unsigned commentlength = read_le(directory + 32, 2); char filename[PATH_MAX] = {0}; if (namelength >= PATH_MAX) GOTO_END_ERROR(); memcpy(filename, directory + 46, namelength); uint32_t offset = read_le(directory + 42, 4); unsigned offsetNL = read_le(data + offset + 26, 2); unsigned offsetEL = read_le(data + offset + 28, 2); const uint8_t *cdata = data + offset + 30 + offsetNL + offsetEL; //RARCH_LOG("OFFSET: %u, CSIZE: %u, SIZE: %u.\n", offset + 30 + offsetNL + offsetEL, csize, size); if (!file_cb(filename, cdata, cmode, csize, size, crc32, userdata)) break; directory += 46 + namelength + extralength + commentlength; } end: if (handle) backend->free(handle); return ret; }
bool rpng_load_image_argb(const char *path, uint32_t **data, unsigned *width, unsigned *height) { long pos; *data = NULL; *width = 0; *height = 0; bool ret = true; FILE *file = fopen(path, "rb"); if (!file) return false; fseek(file, 0, SEEK_END); long file_len = ftell(file); rewind(file); bool has_ihdr = false; bool has_idat = false; bool has_iend = false; bool has_plte = false; uint8_t *inflate_buf = NULL; size_t inflate_buf_size = 0; z_stream stream = {0}; struct idat_buffer idat_buf = {0}; struct png_ihdr ihdr = {0}; uint32_t palette[256] = {0}; char header[8]; if (fread(header, 1, sizeof(header), file) != sizeof(header)) GOTO_END_ERROR(); if (memcmp(header, png_magic, sizeof(png_magic)) != 0) GOTO_END_ERROR(); // feof() apparently isn't triggered after a seek (IEND). for (pos = ftell(file); pos < file_len && pos >= 0; pos = ftell(file)) { struct png_chunk chunk = {0}; if (!read_chunk_header(file, &chunk)) GOTO_END_ERROR(); switch (png_chunk_type(&chunk)) { case PNG_CHUNK_NOOP: default: if (fseek(file, chunk.size + sizeof(uint32_t), SEEK_CUR) < 0) GOTO_END_ERROR(); break; case PNG_CHUNK_ERROR: GOTO_END_ERROR(); case PNG_CHUNK_IHDR: if (has_ihdr || has_idat || has_iend) GOTO_END_ERROR(); if (!png_parse_ihdr(file, &chunk, &ihdr)) GOTO_END_ERROR(); has_ihdr = true; break; case PNG_CHUNK_PLTE: if (!has_ihdr || has_plte || has_iend || has_idat) GOTO_END_ERROR(); if (chunk.size % 3) GOTO_END_ERROR(); if (!png_read_plte(file, palette, chunk.size / 3)) GOTO_END_ERROR(); has_plte = true; break; case PNG_CHUNK_IDAT: if (!has_ihdr || has_iend || (ihdr.color_type == 3 && !has_plte)) GOTO_END_ERROR(); if (!png_append_idat(file, &chunk, &idat_buf)) GOTO_END_ERROR(); has_idat = true; break; case PNG_CHUNK_IEND: if (!has_ihdr || !has_idat) GOTO_END_ERROR(); if (fseek(file, sizeof(uint32_t), SEEK_CUR) < 0) GOTO_END_ERROR(); has_iend = true; break; } } if (!has_ihdr || !has_idat || !has_iend) GOTO_END_ERROR(); if (inflateInit(&stream) != Z_OK) GOTO_END_ERROR(); png_pass_geom(&ihdr, ihdr.width, ihdr.height, NULL, NULL, &inflate_buf_size); if (ihdr.interlace == 1) // To be sure. inflate_buf_size *= 2; inflate_buf = (uint8_t*)malloc(inflate_buf_size); if (!inflate_buf) GOTO_END_ERROR(); stream.next_in = idat_buf.data; stream.avail_in = idat_buf.size; stream.avail_out = inflate_buf_size; stream.next_out = inflate_buf; if (inflate(&stream, Z_FINISH) != Z_STREAM_END) { inflateEnd(&stream); GOTO_END_ERROR(); } inflateEnd(&stream); *width = ihdr.width; *height = ihdr.height; *data = (uint32_t*)malloc(ihdr.width * ihdr.height * sizeof(uint32_t)); if (!*data) GOTO_END_ERROR(); if (ihdr.interlace == 1) { if (!png_reverse_filter_adam7(*data, &ihdr, inflate_buf, stream.total_out, palette)) GOTO_END_ERROR(); } else if (!png_reverse_filter(*data, &ihdr, inflate_buf, stream.total_out, palette)) GOTO_END_ERROR(); end: if (file) fclose(file); if (!ret) free(*data); free(idat_buf.data); free(inflate_buf); return ret; }
static bool png_reverse_filter(uint32_t *data, const struct png_ihdr *ihdr, const uint8_t *inflate_buf, size_t inflate_buf_size, const uint32_t *palette) { unsigned i, h; bool ret = true; unsigned bpp; unsigned pitch; size_t pass_size; png_pass_geom(ihdr, ihdr->width, ihdr->height, &bpp, &pitch, &pass_size); if (inflate_buf_size < pass_size) return false; uint8_t *prev_scanline = (uint8_t*)calloc(1, pitch); uint8_t *decoded_scanline = (uint8_t*)calloc(1, pitch); if (!prev_scanline || !decoded_scanline) GOTO_END_ERROR(); for (h = 0; h < ihdr->height; h++, inflate_buf += pitch, data += ihdr->width) { unsigned filter = *inflate_buf++; switch (filter) { case 0: // None memcpy(decoded_scanline, inflate_buf, pitch); break; case 1: // Sub for (i = 0; i < bpp; i++) decoded_scanline[i] = inflate_buf[i]; for (i = bpp; i < pitch; i++) decoded_scanline[i] = decoded_scanline[i - bpp] + inflate_buf[i]; break; case 2: // Up for (i = 0; i < pitch; i++) decoded_scanline[i] = prev_scanline[i] + inflate_buf[i]; break; case 3: // Average for (i = 0; i < bpp; i++) { uint8_t avg = prev_scanline[i] >> 1; decoded_scanline[i] = avg + inflate_buf[i]; } for (i = bpp; i < pitch; i++) { uint8_t avg = (decoded_scanline[i - bpp] + prev_scanline[i]) >> 1; decoded_scanline[i] = avg + inflate_buf[i]; } break; case 4: // Paeth for (i = 0; i < bpp; i++) decoded_scanline[i] = paeth(0, prev_scanline[i], 0) + inflate_buf[i]; for (i = bpp; i < pitch; i++) decoded_scanline[i] = paeth(decoded_scanline[i - bpp], prev_scanline[i], prev_scanline[i - bpp]) + inflate_buf[i]; break; default: GOTO_END_ERROR(); } if (ihdr->color_type == 0) copy_line_bw(data, decoded_scanline, ihdr->width, ihdr->depth); else if (ihdr->color_type == 2) copy_line_rgb(data, decoded_scanline, ihdr->width, ihdr->depth); else if (ihdr->color_type == 3) copy_line_plt(data, decoded_scanline, ihdr->width, ihdr->depth, palette); else if (ihdr->color_type == 4) copy_line_gray_alpha(data, decoded_scanline, ihdr->width, ihdr->depth); else if (ihdr->color_type == 6) copy_line_rgba(data, decoded_scanline, ihdr->width, ihdr->depth); memcpy(prev_scanline, decoded_scanline, pitch); } end: free(decoded_scanline); free(prev_scanline); return ret; }
static bool png_parse_ihdr(FILE *file, struct png_chunk *chunk, struct png_ihdr *ihdr) { unsigned i; bool ret = true; if (!png_read_chunk(file, chunk)) return false; if (chunk->size != 13) GOTO_END_ERROR(); ihdr->width = dword_be(chunk->data + 0); ihdr->height = dword_be(chunk->data + 4); ihdr->depth = chunk->data[8]; ihdr->color_type = chunk->data[9]; ihdr->compression = chunk->data[10]; ihdr->filter = chunk->data[11]; ihdr->interlace = chunk->data[12]; if (ihdr->width == 0 || ihdr->height == 0) GOTO_END_ERROR(); if (ihdr->color_type == 2 || ihdr->color_type == 4 || ihdr->color_type == 6) { if (ihdr->depth != 8 && ihdr->depth != 16) GOTO_END_ERROR(); } else if (ihdr->color_type == 0) { static const unsigned valid_bpp[] = { 1, 2, 4, 8, 16 }; bool correct_bpp = false; for (i = 0; i < ARRAY_SIZE(valid_bpp); i++) { if (valid_bpp[i] == ihdr->depth) { correct_bpp = true; break; } } if (!correct_bpp) GOTO_END_ERROR(); } else if (ihdr->color_type == 3) { static const unsigned valid_bpp[] = { 1, 2, 4, 8 }; bool correct_bpp = false; for (i = 0; i < ARRAY_SIZE(valid_bpp); i++) { if (valid_bpp[i] == ihdr->depth) { correct_bpp = true; break; } } if (!correct_bpp) GOTO_END_ERROR(); } else GOTO_END_ERROR(); #ifdef RPNG_TEST fprintf(stderr, "IHDR: (%u x %u), bpc = %u, palette = %s, color = %s, alpha = %s, adam7 = %s.\n", ihdr->width, ihdr->height, ihdr->depth, ihdr->color_type == 3 ? "yes" : "no", ihdr->color_type & 2 ? "yes" : "no", ihdr->color_type & 4 ? "yes" : "no", ihdr->interlace == 1 ? "yes" : "no"); #endif if (ihdr->compression != 0) GOTO_END_ERROR(); //if (ihdr->interlace != 0) // No Adam7 supported. // GOTO_END_ERROR(); end: png_free_chunk(chunk); return ret; }
bool rpng_load_image_argb(const char *path, uint32_t **data, unsigned *width, unsigned *height) { long pos, file_len; FILE *file; char header[8] = {0}; rpng_t rpng = {{0}}; bool ret = true; int retval = 0; *data = NULL; *width = 0; *height = 0; file = fopen(path, "rb"); if (!file) return false; fseek(file, 0, SEEK_END); file_len = ftell(file); rewind(file); if (fread(header, 1, sizeof(header), file) != sizeof(header)) GOTO_END_ERROR(); if (memcmp(header, png_magic, sizeof(png_magic)) != 0) GOTO_END_ERROR(); /* feof() apparently isn't triggered after a seek (IEND). */ for (pos = ftell(file); pos < file_len && pos >= 0; pos = ftell(file)) { if (!rpng_load_image_argb_iterate(&file, &rpng)) GOTO_END_ERROR(); } if (!rpng.has_ihdr || !rpng.has_idat || !rpng.has_iend) GOTO_END_ERROR(); if (!rpng_load_image_argb_process_init(&rpng, data, width, height)) GOTO_END_ERROR(); do{ retval = rpng_load_image_argb_process_inflate_init(&rpng, data, width, height); }while(retval == 0); if (retval == -1) GOTO_END_ERROR(); do{ retval = png_reverse_filter_iterate(&rpng, data); }while(retval == PNG_PROCESS_NEXT); if (retval == PNG_PROCESS_ERROR || retval == PNG_PROCESS_ERROR_END) GOTO_END_ERROR(); end: if (file) fclose(file); if (!ret) free(*data); free(rpng.idat_buf.data); free(rpng.process.inflate_buf); if (rpng.process.stream) { zlib_stream_free(rpng.process.stream); free(rpng.process.stream); } return ret; }
static bool png_parse_ihdr(uint8_t *buf, struct png_ihdr *ihdr) { unsigned i; bool ret = true; buf += 4 + 4; ihdr->width = dword_be(buf + 0); ihdr->height = dword_be(buf + 4); ihdr->depth = buf[8]; ihdr->color_type = buf[9]; ihdr->compression = buf[10]; ihdr->filter = buf[11]; ihdr->interlace = buf[12]; if (ihdr->width == 0 || ihdr->height == 0) GOTO_END_ERROR(); if (ihdr->color_type == 2 || ihdr->color_type == 4 || ihdr->color_type == 6) { if (ihdr->depth != 8 && ihdr->depth != 16) GOTO_END_ERROR(); } else if (ihdr->color_type == 0) { static const unsigned valid_bpp[] = { 1, 2, 4, 8, 16 }; bool correct_bpp = false; for (i = 0; i < ARRAY_SIZE(valid_bpp); i++) { if (valid_bpp[i] == ihdr->depth) { correct_bpp = true; break; } } if (!correct_bpp) GOTO_END_ERROR(); } else if (ihdr->color_type == 3) { static const unsigned valid_bpp[] = { 1, 2, 4, 8 }; bool correct_bpp = false; for (i = 0; i < ARRAY_SIZE(valid_bpp); i++) { if (valid_bpp[i] == ihdr->depth) { correct_bpp = true; break; } } if (!correct_bpp) GOTO_END_ERROR(); } else GOTO_END_ERROR(); #ifdef RPNG_TEST fprintf(stderr, "IHDR: (%u x %u), bpc = %u, palette = %s, color = %s, alpha = %s, adam7 = %s.\n", ihdr->width, ihdr->height, ihdr->depth, ihdr->color_type == 3 ? "yes" : "no", ihdr->color_type & 2 ? "yes" : "no", ihdr->color_type & 4 ? "yes" : "no", ihdr->interlace == 1 ? "yes" : "no"); #endif if (ihdr->compression != 0) GOTO_END_ERROR(); #if 0 if (ihdr->interlace != 0) /* No Adam7 supported. */ GOTO_END_ERROR(); #endif end: return ret; }