diff --git a/src/gmio_amf/amf_io.c b/src/gmio_amf/amf_io.c index d3f746b..034f414 100644 --- a/src/gmio_amf/amf_io.c +++ b/src/gmio_amf/amf_io.c @@ -632,7 +632,8 @@ static size_t gmio_amf_ostringstream_write_zlib( int z_retcode = Z_OK; context->z_uncompressed_size += len; - context->z_crc32 = crc32(context->z_crc32, (const Bytef*)ptr, len); + context->z_crc32 = + gmio_zlib_crc32_update(context->z_crc32, (const uint8_t*)ptr, len); z_stream->next_in = (z_const Bytef*)ptr; z_stream->avail_in = len; @@ -866,7 +867,7 @@ int gmio_amf_write( (uint8_t*)memblock->ptr + mblock_halfsize, mblock_halfsize, NULL); - context.z_crc32 = crc32(0, NULL, 0); + context.z_crc32 = gmio_zlib_crc32_initial(); context.error = gmio_zlib_compress_init( &context.z_stream, &opts->z_compress_options); diff --git a/src/gmio_core/internal/zip_utils.c b/src/gmio_core/internal/zip_utils.c index cd4c4bf..fa5f209 100644 --- a/src/gmio_core/internal/zip_utils.c +++ b/src/gmio_core/internal/zip_utils.c @@ -75,14 +75,21 @@ static const struct tm* gmio_nonnull_datetime(const struct tm* datetime) return datetime; } -#ifdef GMIO_HAVE_INT64_TYPE -/* Encodes little-endian 64b val in buffer and returns advanced buffer pos */ -static uint8_t* gmio_adv_encode_uint64_le(uint64_t val, uint8_t* bytes) +/* Reads 16b uint from memory (little-endian) and advances buffer pos */ +static uint16_t gmio_adv_decode_uint16_le(const uint8_t** bytes) { - gmio_encode_uint64_le(val, bytes); - return bytes + 8; + const uint16_t val = gmio_decode_uint16_le(*bytes); + *bytes += 2; + return val; +} + +/* Reads 32b uint from memory (little-endian) and advances buffer pos */ +static uint32_t gmio_adv_decode_uint32_le(const uint8_t** bytes) +{ + const uint32_t val = gmio_decode_uint32_le(*bytes); + *bytes += 4; + return val; } -#endif /* Encodes little-endian 32b val in buffer and returns advanced buffer pos */ static uint8_t* gmio_adv_encode_uint32_le(uint32_t val, uint8_t* bytes) @@ -98,25 +105,251 @@ static uint8_t* gmio_adv_encode_uint16_le(uint16_t val, uint8_t* bytes) return bytes + 2; } +#ifdef GMIO_HAVE_INT64_TYPE +/* Reads 64b uint from memory (little-endian) and advances buffer pos */ +static uint64_t gmio_adv_decode_uint64_le(const uint8_t** bytes) +{ + const uint64_t val = gmio_decode_uint64_le(*bytes); + *bytes += 8; + return val; +} + +/* Encodes little-endian 64b val in buffer and returns advanced buffer pos */ +static uint8_t* gmio_adv_encode_uint64_le(uint64_t val, uint8_t* bytes) +{ + gmio_encode_uint64_le(val, bytes); + return bytes + 8; +} +#endif + /* Helper to facilitate return from gmio_zip_write_xxx() API functions */ static size_t gmio_zip_write_returnhelper( - struct gmio_stream* stream, size_t written, size_t expected, int* error) + struct gmio_stream* stream, + size_t written, + size_t expected, + int* ptr_error) { - if (error != NULL) { + if (ptr_error != NULL) { const bool no_error = written == expected && !gmio_stream_error(stream); - *error = no_error ? GMIO_ERROR_OK : GMIO_ERROR_STREAM; + *ptr_error = no_error ? GMIO_ERROR_OK : GMIO_ERROR_STREAM; } return written; } +static bool gmio_zip_read_checkhelper( + struct gmio_stream* stream, size_t read, size_t expected) +{ + return read == expected && !gmio_stream_error(stream); +} + +/* Helper to facilitate return from gmio_zip_read_xxx() and gmio_zip_write_xxx() + * API functions */ +static size_t gmio_zip_io_returnerr(size_t io_len, int error, int* ptr_error) +{ + if (ptr_error != NULL) + *ptr_error = error; + return io_len; +} + /* ---------- * API functions * ---------- */ +size_t gmio_zip_read_local_file_header( + struct gmio_stream *stream, + struct gmio_zip_local_file_header *info, + int *ptr_error) +{ + uint8_t buff[GMIO_ZIP_SIZE_LOCAL_FILE_HEADER]; + uint8_t* buffit = buff; + + const size_t read_len = gmio_stream_read_bytes(stream, buff, sizeof(buff)); + if (!gmio_zip_read_checkhelper(stream, read_len, sizeof(buff))) + return gmio_zip_io_returnerr(read_len, GMIO_ERROR_STREAM, ptr_error); + + /* 4-bytes magic */ + if (gmio_adv_decode_uint32_le(&buffit) != 0x04034b50) { + return gmio_zip_io_returnerr( + read_len, GMIO_ZIP_UTILS_ERROR_BAD_MAGIC, ptr_error); + } + /* 2-bytes version needed to extract */ + info->version_needed_to_extract = gmio_adv_decode_uint16_le(&buffit); + /* 2-bytes general purpose bit flag */ + info->general_purpose_flags = gmio_adv_decode_uint16_le(&buffit); + /* 2-bytes compression method */ + info->compress_method = gmio_adv_decode_uint16_le(&buffit); + /* 2-bytes last mod file time */ + /* 2-bytes last mod file date */ + /* TODO: convert DOS datetime to struct tm */ + const uint32_t dos_datetime = gmio_adv_decode_uint32_le(&buffit); + GMIO_UNUSED(dos_datetime); + info->lastmod_datetime = NULL; + /* 4-bytes crc-32 */ + info->crc32 = gmio_adv_decode_uint32_le(&buffit); + /* 4-bytes compressed size */ + info->compressed_size = gmio_adv_decode_uint32_le(&buffit); + /* 4-bytes uncompressed size */ + info->uncompressed_size = gmio_adv_decode_uint32_le(&buffit); + /* 2-bytes file name length */ + info->filename_len = gmio_adv_decode_uint16_le(&buffit); + /* 2-bytes extra field length */ + info->extrafield_len = gmio_adv_decode_uint16_le(&buffit); + + info->filename = NULL; + info->extrafield = NULL; + + return gmio_zip_io_returnerr(read_len, GMIO_ERROR_OK, ptr_error); +} + +size_t gmio_zip_write_data_descriptor( + struct gmio_stream *stream, + const struct gmio_zip_data_descriptor *info, + int* ptr_error) +{ + const size_t fixed_data_len = + info->use_zip64 ? + GMIO_ZIP64_SIZE_DATA_DESCRIPTOR : + GMIO_ZIP_SIZE_DATA_DESCRIPTOR; + uint8_t fixed_data[GMIO_ZIP64_SIZE_DATA_DESCRIPTOR]; + uint8_t* buff = fixed_data; + + /* 4-bytes crc-32 */ + buff = gmio_adv_encode_uint32_le(info->crc32, buff); + /* Compressed size and uncompressed size (4 or 8 bytes) */ + if (info->use_zip64) { +#ifdef GMIO_HAVE_INT64_TYPE + buff = gmio_adv_encode_uint64_le(info->compressed_size, buff); + buff = gmio_adv_encode_uint64_le(info->uncompressed_size, buff); +#else + /* TODO: error code */ + return gmio_zip_io_returnerr(0, GMIO_ERROR_UNKNOWN, ptr_error); +#endif + } + else { + if (info->compressed_size <= UINT32_MAX + && info->uncompressed_size <= UINT32_MAX) + { + buff = gmio_adv_encode_uint32_le((uint32_t)info->compressed_size, buff); + buff = gmio_adv_encode_uint32_le((uint32_t)info->uncompressed_size, buff); + } + else { + /* TODO: error code */ + return gmio_zip_io_returnerr(0, GMIO_ERROR_UNKNOWN, ptr_error); + } + } + + /* Write to stream */ + const size_t written_len = + gmio_stream_write_bytes(stream, fixed_data, fixed_data_len); + return gmio_zip_write_returnhelper( + stream, written_len, fixed_data_len, ptr_error); +} + +size_t gmio_zip_read_central_directory_header( + struct gmio_stream *stream, + struct gmio_zip_central_directory_header *info, + int *ptr_error) +{ + uint8_t buff[GMIO_ZIP_SIZE_CENTRAL_DIRECTORY_HEADER]; + uint8_t* buffit = buff; + + const size_t read_len = gmio_stream_read_bytes(stream, buff, sizeof(buff)); + if (!gmio_zip_read_checkhelper(stream, read_len, sizeof(buff))) + return gmio_zip_io_returnerr(read_len, GMIO_ERROR_STREAM, ptr_error); + + /* 4-bytes magic */ + if (gmio_adv_decode_uint32_le(&buffit) != 0x02014b50) { + return gmio_zip_io_returnerr( + read_len, GMIO_ZIP_UTILS_ERROR_BAD_MAGIC, ptr_error); + } + + /* 2-bytes version made by */ + info->version_made_by = gmio_adv_decode_uint16_le(&buffit); + /* 2-bytes version needed to extract */ + info->version_needed_to_extract = gmio_adv_decode_uint16_le(&buffit); + /* 2-bytes general purpose bit flag */ + info->general_purpose_flags = gmio_adv_decode_uint16_le(&buffit); + /* 2-bytes compression method */ + info->compress_method = gmio_adv_decode_uint16_le(&buffit); + /* 2-bytes last mod file time */ + /* 2-bytes last mod file date */ + /* TODO: convert DOS datetime to struct tm */ + const uint32_t dos_datetime = gmio_adv_decode_uint32_le(&buffit); + GMIO_UNUSED(dos_datetime); + info->lastmod_datetime = NULL; + /* 4-bytes crc-32 */ + info->crc32 = gmio_adv_decode_uint32_le(&buffit); + /* 4-bytes compressed size */ + info->compressed_size = gmio_adv_decode_uint32_le(&buffit); + /* 4-bytes uncompressed size */ + info->uncompressed_size = gmio_adv_decode_uint32_le(&buffit); + /* 2-bytes file name length */ + info->filename_len = gmio_adv_decode_uint16_le(&buffit); + /* 2-bytes extra field length */ + info->extrafield_len = gmio_adv_decode_uint16_le(&buffit); + /* 2-bytes file comment length */ + info->filecomment_len = gmio_adv_decode_uint16_le(&buffit); + /* 2-bytes disk number start */ + info->disk_nb_start = gmio_adv_decode_uint16_le(&buffit); + /* 2-bytes internal file attributes */ + info->internal_file_attrs = gmio_adv_decode_uint16_le(&buffit); + /* 4-bytes external file attributes */ + info->external_file_attrs = gmio_adv_decode_uint32_le(&buffit); + /* 4-bytes relative offset of local header */ + info->relative_offset_local_header = gmio_adv_decode_uint32_le(&buffit); + + info->filename = NULL; + info->extrafield = NULL; + info->filecomment = NULL; + + return gmio_zip_io_returnerr(read_len, GMIO_ERROR_OK, ptr_error); +} + +size_t gmio_zip_read_end_of_central_directory_record( + struct gmio_stream *stream, + struct gmio_zip_end_of_central_directory_record *info, + int *ptr_error) +{ + uint8_t buff[GMIO_ZIP_SIZE_END_OF_CENTRAL_DIRECTORY_RECORD]; + uint8_t* buffit = buff; + + const size_t read_len = gmio_stream_read_bytes(stream, buff, sizeof(buff)); + if (!gmio_zip_read_checkhelper(stream, read_len, sizeof(buff))) + return gmio_zip_io_returnerr(read_len, GMIO_ERROR_STREAM, ptr_error); + + /* 4-bytes magic */ + if (gmio_adv_decode_uint32_le(&buffit) != 0x06054b50) { + return gmio_zip_io_returnerr( + read_len, GMIO_ZIP_UTILS_ERROR_BAD_MAGIC, ptr_error); + } + + /* 2-bytes number of this disk */ + info->disk_nb = gmio_adv_decode_uint16_le(&buffit); + /* 2-bytes number of the disk with the start of the central directory */ + info->disk_nb_with_start_of_central_dir = gmio_adv_decode_uint16_le(&buffit); + /* 2-bytes total number of entries in the central directory on this disk */ + info->total_entry_count_in_central_dir_on_disk = + gmio_adv_decode_uint16_le(&buffit); + /* 2-bytes total number of entries in the central directory */ + info->total_entry_count_in_central_dir = gmio_adv_decode_uint16_le(&buffit); + /* 4-bytes size of the central directory */ + info->central_dir_size = gmio_adv_decode_uint32_le(&buffit); + /* 4-bytes offset of start of central directory with respect to the starting + * disk number */ + info->start_offset_central_dir_from_disk_start_nb = + gmio_adv_decode_uint32_le(&buffit); + /* 2-bytes .ZIP file comment length */ + info->filecomment_len = gmio_adv_decode_uint16_le(&buffit); + + info->filecomment = NULL; + + return gmio_zip_io_returnerr(read_len, GMIO_ERROR_OK, ptr_error); +} + size_t gmio_zip_write_local_file_header( struct gmio_stream* stream, const struct gmio_zip_local_file_header* info, - int* error) + int* ptr_error) { uint8_t fixed_data[GMIO_ZIP_SIZE_LOCAL_FILE_HEADER]; uint8_t* buff = fixed_data; @@ -162,55 +395,62 @@ size_t gmio_zip_write_local_file_header( written_len += gmio_stream_write_bytes(stream, info->extrafield, info->extrafield_len); return gmio_zip_write_returnhelper( - stream, written_len, expected_written_len, error); + stream, written_len, expected_written_len, ptr_error); } -size_t gmio_zip_write_data_descriptor( +size_t gmio_zip_read_data_descriptor( struct gmio_stream *stream, - const struct gmio_zip_data_descriptor *info, - int* error) + struct gmio_zip_data_descriptor *info, + int* ptr_error) { - const size_t fixed_data_len = - info->use_zip64 ? - GMIO_ZIP64_SIZE_DATA_DESCRIPTOR : - GMIO_ZIP_SIZE_DATA_DESCRIPTOR; - uint8_t fixed_data[GMIO_ZIP64_SIZE_DATA_DESCRIPTOR]; - uint8_t* buff = fixed_data; + uint8_t buff[GMIO_ZIP_SIZE_DATA_DESCRIPTOR]; + uint8_t* buffit = buff; + + const size_t read_len = gmio_stream_read_bytes(stream, buff, sizeof(buff)); + if (!gmio_zip_read_checkhelper(stream, read_len, sizeof(buff))) + return gmio_zip_io_returnerr(read_len, GMIO_ERROR_STREAM, ptr_error); /* 4-bytes crc-32 */ - buff = gmio_adv_encode_uint32_le(info->crc32, buff); - /* Compressed size and uncompressed size (4 or 8 bytes) */ - if (info->use_zip64) { -#ifdef GMIO_HAVE_INT64_TYPE - buff = gmio_adv_encode_uint64_le(info->compressed_size, buff); - buff = gmio_adv_encode_uint64_le(info->uncompressed_size, buff); -#else - return GMIO_ERROR_UNKNOWN; /* TODO: error code */ -#endif - } - else { - if (info->compressed_size <= UINT32_MAX - && info->uncompressed_size <= UINT32_MAX) - { - buff = gmio_adv_encode_uint32_le((uint32_t)info->compressed_size, buff); - buff = gmio_adv_encode_uint32_le((uint32_t)info->uncompressed_size, buff); - } - else { - return GMIO_ERROR_UNKNOWN; /* TODO: error code */ - } - } + info->crc32 = gmio_adv_decode_uint32_le(&buffit); + /* 4-bytes compressed size */ + info->compressed_size = gmio_adv_decode_uint32_le(&buffit); + /* 4-bytes uncompressed size */ + info->uncompressed_size = gmio_adv_decode_uint32_le(&buffit); - /* Write to stream */ - const size_t written_len = - gmio_stream_write_bytes(stream, fixed_data, fixed_data_len); - return gmio_zip_write_returnhelper( - stream, written_len, fixed_data_len, error); + return gmio_zip_io_returnerr(read_len, GMIO_ERROR_OK, ptr_error); +} + +size_t gmio_zip64_read_data_descriptor( + struct gmio_stream* stream, + struct gmio_zip_data_descriptor* info, + int* ptr_error) +{ +#ifdef GMIO_HAVE_INT64_TYPE + uint8_t buff[GMIO_ZIP64_SIZE_DATA_DESCRIPTOR]; + uint8_t* buffit = buff; + + const size_t read_len = gmio_stream_read_bytes(stream, buff, sizeof(buff)); + if (!gmio_zip_read_checkhelper(stream, read_len, sizeof(buff))) + return gmio_zip_io_returnerr(read_len, GMIO_ERROR_STREAM, ptr_error); + + /* 4-bytes crc-32 */ + info->crc32 = gmio_adv_decode_uint32_le(&buffit); + /* 8-bytes compressed size */ + info->compressed_size = gmio_adv_decode_uint64_le(&buffit); + /* 8-bytes uncompressed size */ + info->uncompressed_size = gmio_adv_decode_uint64_le(&buffit); + + return gmio_zip_io_returnerr(read_len, GMIO_ERROR_OK, ptr_error); +#else + /* TODO: error code */ + return gmio_zip_io_returnerr(0, GMIO_ERROR_UNKNOWN, ptr_error); +#endif } size_t gmio_zip_write_central_directory_header( struct gmio_stream *stream, const struct gmio_zip_central_directory_header *info, - int* error) + int* ptr_error) { uint8_t fixed_data[GMIO_ZIP_SIZE_CENTRAL_DIRECTORY_HEADER]; uint8_t* buff = fixed_data; @@ -270,18 +510,18 @@ size_t gmio_zip_write_central_directory_header( written_len += gmio_stream_write_bytes(stream, info->filecomment, info->filecomment_len); return gmio_zip_write_returnhelper( - stream, written_len, expected_written_len, error); + stream, written_len, expected_written_len, ptr_error); } size_t gmio_zip64_write_extrafield_extended_info( uint8_t *buff, size_t buff_capacity, const struct gmio_zip64_extrablock_extended_info *info, - int* error) + int* ptr_error) { if (buff_capacity < GMIO_ZIP64_SIZE_EXTRAFIELD_EXTENDED_INFO) { - *error = GMIO_ERROR_INVALID_MEMBLOCK_SIZE; - return 0; + return gmio_zip_io_returnerr( + 0, GMIO_ERROR_INVALID_MEMBLOCK_SIZE, ptr_error); } #ifdef GMIO_HAVE_INT64_TYPE /* Tag */ @@ -297,18 +537,20 @@ size_t gmio_zip64_write_extrafield_extended_info( buff = gmio_adv_encode_uint64_le(info->relative_offset_local_header, buff); /* Number of the disk on which this file starts */ buff = gmio_adv_encode_uint32_le(info->disk_nb_start, buff); - *error = GMIO_ERROR_OK; - return GMIO_ZIP64_SIZE_EXTRAFIELD_EXTENDED_INFO; + return gmio_zip_io_returnerr( + GMIO_ZIP64_SIZE_EXTRAFIELD_EXTENDED_INFO, + GMIO_ERROR_OK, + ptr_error); #else - *error = GMIO_ERROR_UNKNOWN; /* TODO: error code */ - return 0; + /* TODO: error code */ + return gmio_zip_io_returnerr(0, GMIO_ERROR_UNKNOWN, error); #endif } size_t gmio_zip_write_end_of_central_directory_record( struct gmio_stream *stream, const struct gmio_zip_end_of_central_directory_record *info, - int* error) + int* ptr_error) { uint8_t fixed_data[GMIO_ZIP_SIZE_END_OF_CENTRAL_DIRECTORY_RECORD]; uint8_t* buff = fixed_data; @@ -322,17 +564,24 @@ size_t gmio_zip_write_end_of_central_directory_record( /* 2-bytes number of the disk with the start of the central directory */ const uint16_t disk_nb_with_start_of_central_dir = - info->use_zip64 ? UINT16_MAX : info->disk_nb_with_start_of_central_dir; + info->use_zip64 ? + UINT16_MAX : + info->disk_nb_with_start_of_central_dir; buff = gmio_adv_encode_uint16_le(disk_nb_with_start_of_central_dir, buff); /* 2-bytes total number of entries in the central directory on this disk */ const uint16_t total_entry_count_in_central_dir_on_disk = - info->use_zip64 ? UINT16_MAX : info->total_entry_count_in_central_dir_on_disk; - buff = gmio_adv_encode_uint16_le(total_entry_count_in_central_dir_on_disk, buff); + info->use_zip64 ? + UINT16_MAX : + info->total_entry_count_in_central_dir_on_disk; + buff = gmio_adv_encode_uint16_le( + total_entry_count_in_central_dir_on_disk, buff); /* 2-bytes total number of entries in the central directory */ const uint16_t total_entry_count_in_central_dir = - info->use_zip64 ? UINT16_MAX : info->total_entry_count_in_central_dir; + info->use_zip64 ? + UINT16_MAX : + info->total_entry_count_in_central_dir; buff = gmio_adv_encode_uint16_le(total_entry_count_in_central_dir, buff); /* 4-bytes size of the central directory */ @@ -343,8 +592,11 @@ size_t gmio_zip_write_end_of_central_directory_record( /* 4-bytes offset of start of central directory with respect to the starting * disk number */ const uint32_t start_offset_central_dir_from_disk_start_nb = - info->use_zip64 ? UINT32_MAX : info->start_offset_central_dir_from_disk_start_nb; - buff = gmio_adv_encode_uint32_le(start_offset_central_dir_from_disk_start_nb, buff); + info->use_zip64 ? + UINT32_MAX : + info->start_offset_central_dir_from_disk_start_nb; + buff = gmio_adv_encode_uint32_le( + start_offset_central_dir_from_disk_start_nb, buff); /* 2-bytes .ZIP file comment length */ buff = gmio_adv_encode_uint16_le(info->filecomment_len, buff); @@ -358,5 +610,5 @@ size_t gmio_zip_write_end_of_central_directory_record( written_len += gmio_stream_write_bytes(stream, info->filecomment, info->filecomment_len); return gmio_zip_write_returnhelper( - stream, written_len, expected_written_len, error); + stream, written_len, expected_written_len, ptr_error); } diff --git a/src/gmio_core/internal/zip_utils.h b/src/gmio_core/internal/zip_utils.h index 85eccf9..1adbb2d 100644 --- a/src/gmio_core/internal/zip_utils.h +++ b/src/gmio_core/internal/zip_utils.h @@ -165,10 +165,10 @@ struct gmio_zip_local_file_header { uint32_t crc32; uint32_t compressed_size; uint32_t uncompressed_size; - const char* filename; - const uint8_t* extrafield; uint16_t filename_len; uint16_t extrafield_len; + const char* filename; + const uint8_t* extrafield; }; /*! Zip data descriptor */ @@ -246,23 +246,58 @@ struct gmio_zip_write_result { size_t written_len; }; +enum { GMIO_ZIP_UTILS_ERROR_TAG = 0xAA000000 }; +enum gmio_zip_utils_error { + GMIO_ZIP_UTILS_ERROR_BAD_MAGIC = GMIO_ZIP_UTILS_ERROR_TAG + 0x01 +}; + +/*! Reads ZIP local file header from \p stream */ +size_t gmio_zip_read_local_file_header( + struct gmio_stream* stream, + struct gmio_zip_local_file_header* info, + int* ptr_error); + +/*! Reads ZIP data descriptor from \p stream */ +size_t gmio_zip_read_data_descriptor( + struct gmio_stream* stream, + struct gmio_zip_data_descriptor* info, + int* ptr_error); + +/*! Reads Zip64 data descriptor from \p stream */ +size_t gmio_zip64_read_data_descriptor( + struct gmio_stream* stream, + struct gmio_zip_data_descriptor* info, + int* ptr_error); + +/*! Reads ZIP central directory header from \p stream */ +size_t gmio_zip_read_central_directory_header( + struct gmio_stream* stream, + struct gmio_zip_central_directory_header* info, + int* ptr_error); + +/*! Reads ZIP end of central directory record from \p stream */ +size_t gmio_zip_read_end_of_central_directory_record( + struct gmio_stream* stream, + struct gmio_zip_end_of_central_directory_record* info, + int* ptr_error); + /*! Writes ZIP local file header to \p stream */ size_t gmio_zip_write_local_file_header( struct gmio_stream* stream, const struct gmio_zip_local_file_header* info, - int* error); + int* ptr_error); /*! Writes ZIP data descriptor to \p stream */ size_t gmio_zip_write_data_descriptor( struct gmio_stream* stream, const struct gmio_zip_data_descriptor* info, - int* error); + int* ptr_error); /*! Writes ZIP central directory header to \p stream */ size_t gmio_zip_write_central_directory_header( struct gmio_stream* stream, const struct gmio_zip_central_directory_header* info, - int* error); + int* ptr_error); /*! Writes ZIP local file header to \p buff * @@ -273,12 +308,12 @@ size_t gmio_zip64_write_extrafield_extended_info( uint8_t* buff, size_t buff_capacity, const struct gmio_zip64_extrablock_extended_info* info, - int* error); + int* ptr_error); -/*! Writes ZIP end of central directory record \p stream */ +/*! Writes ZIP end of central directory record to \p stream */ size_t gmio_zip_write_end_of_central_directory_record( struct gmio_stream* stream, const struct gmio_zip_end_of_central_directory_record* info, - int* error); + int* ptr_error); #endif /* GMIO_INTERNAL_ZIP_UTILS_H */ diff --git a/src/gmio_core/internal/zlib_utils.c b/src/gmio_core/internal/zlib_utils.c index 1799545..b7c24e7 100644 --- a/src/gmio_core/internal/zlib_utils.c +++ b/src/gmio_core/internal/zlib_utils.c @@ -30,6 +30,18 @@ #include "zlib_utils.h" #include "../error.h" +/* zlib doc: + * the windowBits parameter is the base two logarithm of the window size + * (the size of the history buffer). It should be in the range 8..15 for + * this version of the library. Larger values of this parameter result in + * better compression at the expense of memory usage. The default value is + * 15 if deflateInit is used instead. + * windowBits can also be –8..–15 for raw deflate. In this case, -windowBits + * determines the window size. deflate() will then generate raw deflate + * data with no zlib header or trailer, and will not compute an adler32 + * check value. */ +static const int z_window_bits_for_no_zlib_wrapper = -15; + static int gmio_to_zlib_compress_level(int gmio_compress_level) { if (gmio_compress_level == GMIO_ZLIB_COMPRESS_LEVEL_DEFAULT) @@ -69,23 +81,12 @@ int gmio_zlib_compress_init( gmio_to_zlib_compress_level(z_opts->level); const int zlib_compress_memusage = gmio_to_zlib_compress_memusage(z_opts->memory_usage); - /* zlib doc: - * the windowBits parameter is the base two logarithm of the window size - * (the size of the history buffer). It should be in the range 8..15 for - * this version of the library. Larger values of this parameter result in - * better compression at the expense of memory usage. The default value is - * 15 if deflateInit is used instead. - * windowBits can also be –8..–15 for raw deflate. In this case, -windowBits - * determines the window size. deflate() will then generate raw deflate - * data with no zlib header or trailer, and will not compute an adler32 - * check value. */ - static const int z_window_bits = -15; const int z_init_error = deflateInit2_( z_stream, zlib_compress_level, Z_DEFLATED, /* Method */ - z_window_bits, + z_window_bits_for_no_zlib_wrapper, zlib_compress_memusage, z_opts->strategy, ZLIB_VERSION, @@ -104,3 +105,60 @@ bool gmio_check_zlib_compress_options( } return gmio_no_error(*error); } + +int gmio_zlib_uncompress_buffer( + uint8_t* dest, size_t* dest_len, const uint8_t* src, size_t src_len) +{ + z_stream stream; + int err; + + stream.next_in = (z_const Bytef *)src; + stream.avail_in = (uInt)src_len; + /* Check for source > 64K on 16-bit machine: */ + if ((uLong)stream.avail_in != src_len) + return GMIO_ERROR_ZLIB_BUF; + + stream.next_out = dest; + stream.avail_out = (uInt)*dest_len; + if ((uLong)stream.avail_out != *dest_len) + return GMIO_ERROR_ZLIB_BUF; + + stream.zalloc = (alloc_func)NULL; + stream.zfree = (free_func)NULL; + + err = inflateInit2_( + &stream, + z_window_bits_for_no_zlib_wrapper, + ZLIB_VERSION, + sizeof(z_stream)); + if (err != Z_OK) + return err; + + err = inflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + inflateEnd(&stream); + if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0)) + return GMIO_ERROR_ZLIB_DATA; + return err; + } + *dest_len = stream.total_out; + + err = inflateEnd(&stream); + return zlib_error_to_gmio_error(err); +} + +uint32_t gmio_zlib_crc32(const uint8_t *buff, size_t buff_len) +{ + return gmio_zlib_crc32_update(gmio_zlib_crc32_initial(), buff, buff_len); +} + +uint32_t gmio_zlib_crc32_update( + uint32_t crc, const uint8_t *buff, size_t buff_len) +{ + return crc32(crc, (const Bytef*)buff, (uInt)buff_len); +} + +uint32_t gmio_zlib_crc32_initial() +{ + return crc32(0, NULL, 0); +} diff --git a/src/gmio_core/internal/zlib_utils.h b/src/gmio_core/internal/zlib_utils.h index 6957ede..bb4c805 100644 --- a/src/gmio_core/internal/zlib_utils.h +++ b/src/gmio_core/internal/zlib_utils.h @@ -46,4 +46,24 @@ int gmio_zlib_compress_init( bool gmio_check_zlib_compress_options( int* error, const struct gmio_zlib_compress_options* z_opts); +/* Decompresses the source buffer into the destination buffer. + * + * \p sourceLen is the byte length of the source buffer. Upon entry, \p destLen + * is the total size of the destination buffer, which must be large enough to + * hold the entire uncompressed data. + * Upon exit, destLen is the actual size of the compressed buffer. + */ +int gmio_zlib_uncompress_buffer( + uint8_t* dest, size_t* dest_len, const uint8_t* src, size_t src_len); + +/* Computes the CRC-32 value with the bytes buff[0..buff_len-1] */ +uint32_t gmio_zlib_crc32(const uint8_t* buff, size_t buff_len); + +/* Returns the required initial value for the gmio_zlib_crc32_update() */ +uint32_t gmio_zlib_crc32_initial(); + +/* Updates a running CRC-32 with the bytes buff[0..buff_len-1] */ +uint32_t gmio_zlib_crc32_update( + uint32_t crc, const uint8_t* buff, size_t buff_len); + #endif /* GMIO_INTERNAL_ZLIB_UTILS_H */ diff --git a/tests/main_test_amf.c b/tests/main_test_amf.c index 71eae47..be30e46 100644 --- a/tests/main_test_amf.c +++ b/tests/main_test_amf.c @@ -48,7 +48,8 @@ const char* all_tests() gmio_memblock_set_default_constructor(gmio_memblock_for_tests); - UTEST_RUN(test_amf_write); + UTEST_RUN(test_amf_write_doc_null); + UTEST_RUN(test_amf_write_doc_1); return NULL; } diff --git a/tests/test_amf_io.c b/tests/test_amf_io.c index c2e2c4f..7b34f88 100644 --- a/tests/test_amf_io.c +++ b/tests/test_amf_io.c @@ -36,6 +36,7 @@ #include "../src/gmio_core/internal/byte_codec.h" #include "../src/gmio_core/internal/helper_stream.h" #include "../src/gmio_core/internal/zip_utils.h" +#include "../src/gmio_core/internal/zlib_utils.h" #include "../src/gmio_amf/amf_error.h" #include "../src/gmio_amf/amf_io.h" @@ -178,97 +179,105 @@ static void __tamf__skip_zip_local_file_header() } -static const char* test_amf_write() +static const char* test_amf_write_doc_null() { + struct gmio_stream stream = {0}; + struct gmio_amf_document doc = {0}; + const int error = gmio_amf_write(&stream, &doc, NULL); + UTEST_ASSERT(gmio_error(error)); + return NULL; +} + +static const char* test_amf_write_doc_1() +{ + static const size_t wbuffsize = 8192; + struct gmio_rw_buffer wbuff = {0}; + struct gmio_amf_document doc = {0}; + + const struct __tamf__material testmaterials[] = { + { { 1., 0., 0. }, "red" }, + { { 0., 1., 0. }, "green" }, + { { 0., 0., 1. }, "blue" }, + { { 1., 1., 1. }, "white" } + }; + + const struct gmio_vec3d testvertices[] = { + { 0., 0., 0.}, + { 1., 0., 0.}, + { 1., -1., 0.}, + { 0., -1., 0.}, + { 1., 0., 1.}, + { 1., -1., 1.}, + { 0., 0., 1.}, + { 0., -1., 1.} + }; + const struct __tamf__triangle testtriangles[] = { + { 0, 1, 2}, + { 0, 2, 3}, + { 1, 5, 2}, + { 1, 4, 5}, + { 6, 5, 7}, + { 6, 4, 5}, + { 0, 6, 7}, + { 0, 7, 3}, + { 0, 6, 4}, + { 0, 4, 1}, + { 3, 7, 5}, + { 3, 5, 2} + }; + struct __tamf__document testdoc = {0}; + + wbuff.ptr = calloc(wbuffsize, 1); + wbuff.len = wbuffsize; + + testdoc.vec_material = testmaterials; + testdoc.mesh.vec_vertex = testvertices; + testdoc.mesh.vertex_count = GMIO_ARRAY_SIZE(testvertices); + testdoc.mesh.vec_triangle = testtriangles; + testdoc.mesh.triangle_count = GMIO_ARRAY_SIZE(testtriangles); + + doc.cookie = &testdoc; + doc.unit = GMIO_AMF_UNIT_MILLIMETER; + doc.func_get_document_element = &__tamf__get_document_element; + doc.func_get_object_mesh = &__tamf__get_object_mesh; + doc.func_get_object_mesh_element = &__tamf__get_object_mesh_element; + doc.func_get_object_mesh_volume_triangle = + &__tamf__get_object_mesh_volume_triangle; + doc.func_get_document_element_metadata = + &__tamf__get_document_element_metadata; + doc.object_count = 1; + doc.material_count = GMIO_ARRAY_SIZE(testmaterials); + + /* Write as raw contents(uncompressed) */ { - struct gmio_stream stream = {0}; - struct gmio_amf_document doc = {0}; - const int error = gmio_amf_write(&stream, &doc, NULL); - UTEST_ASSERT(gmio_error(error)); + struct gmio_stream stream = gmio_stream_buffer(&wbuff); + struct gmio_amf_write_options options = {0}; + options.float64_prec = 9; + const int error = gmio_amf_write(&stream, &doc, &options); + if (gmio_error(error)) + printf("\n0x%x\n", error); + UTEST_COMPARE_INT(error, GMIO_ERROR_OK); + /* printf("%s\n", wbuff.ptr); */ } + /* Write compressed ZIP */ { - static const size_t wbuffsize = 8192; - struct gmio_rw_buffer wbuff = {0}; - struct gmio_stream stream = gmio_stream_buffer(&wbuff); - struct gmio_amf_document doc = {0}; - struct gmio_amf_write_options options = {0}; + const size_t source_len = wbuff.pos; + const uint32_t crc32_amf_data = gmio_zlib_crc32(wbuff.ptr, source_len); + uint8_t* source = calloc(source_len, 1); + memcpy(source, wbuff.ptr, source_len); - const struct __tamf__material testmaterials[] = { - { { 1., 0., 0. }, "red" }, - { { 0., 1., 0. }, "green" }, - { { 0., 0., 1. }, "blue" }, - { { 1., 1., 1. }, "white" } - }; - const struct gmio_vec3d testvertices[] = { - { 0., 0., 0.}, - { 1., 0., 0.}, - { 1., -1., 0.}, - { 0., -1., 0.}, - { 1., 0., 1.}, - { 1., -1., 1.}, - { 0., 0., 1.}, - { 0., -1., 1.} - }; - const struct __tamf__triangle testtriangles[] = { - { 0, 1, 2}, - { 0, 2, 3}, - { 1, 5, 2}, - { 1, 4, 5}, - { 6, 5, 7}, - { 6, 4, 5}, - { 0, 6, 7}, - { 0, 7, 3}, - { 0, 6, 4}, - { 0, 4, 1}, - { 3, 7, 5}, - { 3, 5, 2} - }; - struct __tamf__document testdoc = {0}; - - wbuff.ptr = calloc(wbuffsize, 1); - wbuff.len = wbuffsize; - - testdoc.vec_material = testmaterials; - testdoc.mesh.vec_vertex = testvertices; - testdoc.mesh.vertex_count = GMIO_ARRAY_SIZE(testvertices); - testdoc.mesh.vec_triangle = testtriangles; - testdoc.mesh.triangle_count = GMIO_ARRAY_SIZE(testtriangles); - - doc.cookie = &testdoc; - doc.unit = GMIO_AMF_UNIT_MILLIMETER; - doc.func_get_document_element = &__tamf__get_document_element; - doc.func_get_object_mesh = &__tamf__get_object_mesh; - doc.func_get_object_mesh_element = &__tamf__get_object_mesh_element; - doc.func_get_object_mesh_volume_triangle = - &__tamf__get_object_mesh_volume_triangle; - doc.func_get_document_element_metadata = - &__tamf__get_document_element_metadata; - doc.object_count = 1; - doc.material_count = GMIO_ARRAY_SIZE(testmaterials); - - options.float64_prec = 9; - - /* Write as raw contents(uncompressed) */ + static const char zip_entry_filename[] = "test.amf"; + static const uint16_t zip_entry_filename_len = + sizeof(zip_entry_filename) - 1; { - const int error = gmio_amf_write(&stream, &doc, &options); - if (gmio_error(error)) - printf("\n0x%x\n", error); - UTEST_COMPARE_INT(error, GMIO_ERROR_OK); - /* printf("%s\n", wbuff.ptr); */ - } - - /* Write compressed ZIP */ - { - const size_t source_len = wbuff.pos; - uint8_t* source = calloc(source_len, 1); - - memcpy(source, wbuff.ptr, source_len); wbuff.pos = 0; + struct gmio_stream stream = gmio_stream_buffer(&wbuff); + struct gmio_amf_write_options options = {0}; + options.float64_prec = 9; options.compress = true; - static const char zip_entry_filename[] = "test.amf"; options.zip_entry_filename = zip_entry_filename; - options.zip_entry_filename_len = sizeof(zip_entry_filename) - 1; + options.zip_entry_filename_len = zip_entry_filename_len; const int error = gmio_amf_write(&stream, &doc, &options); UTEST_COMPARE_INT(error, GMIO_ERROR_OK); #if 0 @@ -276,52 +285,108 @@ static const char* test_amf_write() fwrite(wbuff.ptr, 1, wbuff.pos, file); fclose(file); #endif - - /* Unzip and compare with source data */ - uint8_t* rbuff = wbuff.ptr; - /* -- Read local file header */ - UTEST_COMPARE_UINT(gmio_decode_uint32_le(rbuff), 0x04034b50); - rbuff += 8; - /* -- Read compression method */ - UTEST_COMPARE_UINT( - gmio_decode_uint16_le(rbuff), - GMIO_ZIP_COMPRESS_METHOD_DEFLATE); - rbuff += 18; - /* -- Read filename length */ - UTEST_COMPARE_UINT( - gmio_decode_uint16_le(rbuff), - options.zip_entry_filename_len); - rbuff += 2; - /* -- Read extrafield length */ - const uint16_t zip_extrafield_len = gmio_decode_uint16_le(rbuff); - rbuff += 2; - /* -- Read filename */ - UTEST_ASSERT(strncmp( - (const char*)rbuff, - options.zip_entry_filename, - options.zip_entry_filename_len) - == 0); - rbuff += options.zip_entry_filename_len; - /* -- Skip extrafield */ - rbuff += zip_extrafield_len; - -#if 0 /* TODO: check other ZIP records, and uncompress file */ - uint8_t* dest = calloc(wbuffsize, 1); - unsigned long dest_len = (unsigned long)wbuffsize; - const unsigned long z_len = wbuff.pos; - const int zerr = uncompress(dest, &dest_len, wbuff.ptr, z_len); - printf("\n-- Info: z_len=%i src_len=%i\n", z_len, source_len); - UTEST_COMPARE_INT(zerr, Z_OK); - UTEST_COMPARE_UINT(source_len, dest_len); - UTEST_COMPARE_INT(memcmp(dest, source, source_len), 0); - free(dest); -#endif - - free(source); } - free(wbuff.ptr); + /* Unzip and compare with source data */ + { + /* Total size in bytes of the ZIP archive */ + const uintmax_t zip_archive_len = wbuff.pos; + wbuff.pos = 0; + struct gmio_stream stream = gmio_stream_buffer(&wbuff); + int error = GMIO_ERROR_OK; + /* -- Read ZIP local file header */ + struct gmio_zip_local_file_header zip_lfh = {0}; + const size_t lfh_read_len = + gmio_zip_read_local_file_header(&stream, &zip_lfh, &error); + UTEST_COMPARE_INT(error, GMIO_ERROR_OK); + UTEST_COMPARE_UINT( + zip_lfh.compress_method, + GMIO_ZIP_COMPRESS_METHOD_DEFLATE); + UTEST_ASSERT( + (zip_lfh.general_purpose_flags & + GMIO_ZIP_GENERAL_PURPOSE_FLAG_USE_DATA_DESCRIPTOR) + != 0); + UTEST_COMPARE_UINT( + zip_lfh.filename_len, + sizeof(zip_entry_filename) - 1); + UTEST_ASSERT(strncmp( + (const char*)wbuff.ptr + wbuff.pos, + zip_entry_filename, + zip_entry_filename_len) + == 0); + /* -- Read ZIP end of central directory record */ + static const size_t end_of_central_dir_record_len = 22; + wbuff.pos = zip_archive_len - end_of_central_dir_record_len;; + struct gmio_zip_end_of_central_directory_record zip_eocdr = {0}; + gmio_zip_read_end_of_central_directory_record( + &stream, &zip_eocdr, &error); + UTEST_COMPARE_INT(error, GMIO_ERROR_OK); + UTEST_COMPARE_UINT(zip_eocdr.disk_nb, 0); + UTEST_COMPARE_UINT( + zip_eocdr.disk_nb_with_start_of_central_dir, 0); + UTEST_COMPARE_UINT( + zip_eocdr.total_entry_count_in_central_dir_on_disk, 1); + UTEST_COMPARE_UINT( + zip_eocdr.total_entry_count_in_central_dir, 1); + /* -- Read ZIP central directory */ + wbuff.pos = zip_eocdr.start_offset_central_dir_from_disk_start_nb; + struct gmio_zip_central_directory_header zip_cdh = {0}; + gmio_zip_read_central_directory_header(&stream, &zip_cdh, &error); + UTEST_COMPARE_INT(error, GMIO_ERROR_OK); + UTEST_COMPARE_UINT( + zip_cdh.compress_method, + GMIO_ZIP_COMPRESS_METHOD_DEFLATE); + UTEST_ASSERT( + (zip_cdh.general_purpose_flags & + GMIO_ZIP_GENERAL_PURPOSE_FLAG_USE_DATA_DESCRIPTOR) + != 0); + UTEST_COMPARE_UINT(crc32_amf_data, zip_cdh.crc32); + UTEST_COMPARE_UINT( + zip_cdh.filename_len, zip_entry_filename_len); + UTEST_ASSERT(strncmp( + (const char*)wbuff.ptr + wbuff.pos, + zip_entry_filename, + zip_entry_filename_len) + == 0); + /* -- Read compressed AMF data */ + const size_t pos_start_amf_data = + lfh_read_len + zip_lfh.filename_len + zip_lfh.extrafield_len; + wbuff.pos = pos_start_amf_data; + { + uint8_t* dest = calloc(zip_cdh.uncompressed_size, 1); + size_t dest_len = zip_cdh.uncompressed_size; + const int error = + gmio_zlib_uncompress_buffer( + dest, + &dest_len, + (const uint8_t*)wbuff.ptr + wbuff.pos, + zip_cdh.compressed_size); + printf("\n-- Info: z_len=%i src_len=%i\n", + zip_cdh.compressed_size, source_len); + UTEST_COMPARE_INT(error, GMIO_ERROR_OK); + UTEST_COMPARE_UINT(source_len, dest_len); + UTEST_COMPARE_UINT(dest_len, zip_cdh.uncompressed_size); + UTEST_COMPARE_INT(memcmp(dest, source, source_len), 0); + const uint32_t crc32_uncomp = gmio_zlib_crc32(dest, dest_len); + UTEST_COMPARE_UINT(crc32_amf_data, crc32_uncomp); + free(dest); + } + /* -- Read ZIP data descriptor */ + wbuff.pos = pos_start_amf_data + zip_cdh.compressed_size; + struct gmio_zip_data_descriptor zip_dd = {0}; + gmio_zip_read_data_descriptor(&stream, &zip_dd, &error); + UTEST_COMPARE_INT(error, GMIO_ERROR_OK); + UTEST_COMPARE_UINT(zip_dd.crc32, crc32_amf_data); + UTEST_COMPARE_UINT( + zip_dd.compressed_size, zip_cdh.compressed_size); + UTEST_COMPARE_UINT( + zip_dd.uncompressed_size, zip_cdh.uncompressed_size); + } + + free(source); } + free(wbuff.ptr); + return NULL; }