gmio_amf: add Zip64 support

This commit is contained in:
Hugues Delorme 2017-01-10 10:47:46 +01:00
parent cad0804c17
commit 0481df6e61
8 changed files with 793 additions and 342 deletions

View File

@ -46,8 +46,8 @@
/* Writing(output) context */
struct gmio_amf_wcontext
{
struct gmio_ostringstream sstream;
const struct gmio_amf_write_options* options;
struct gmio_ostringstream* sstream;
const struct gmio_amf_document* document;
const struct gmio_task_iface* task_iface;
intmax_t task_progress_current;
@ -85,7 +85,7 @@ static void gmio_amf_write_double(
double value,
const char* value_formula)
{
struct gmio_ostringstream* sstream = context->sstream;
struct gmio_ostringstream* sstream = &context->sstream;
if (value_formula == NULL || *value_formula == '\0')
gmio_ostringstream_write_f64(sstream, value, &context->f64_format);
else
@ -98,7 +98,7 @@ static void gmio_amf_write_color_component(
double value,
const char* value_formula)
{
struct gmio_ostringstream* sstream = context->sstream;
struct gmio_ostringstream* sstream = &context->sstream;
if (value_formula == NULL || *value_formula == '\0') {
gmio_ostringstream_write_f64(sstream, value, &context->f64_format);
}
@ -114,7 +114,7 @@ static void gmio_amf_write_color(
struct gmio_amf_wcontext* context,
const struct gmio_amf_color* color)
{
struct gmio_ostringstream* sstream = context->sstream;
struct gmio_ostringstream* sstream = &context->sstream;
gmio_ostringstream_write_chararray(sstream, "<color><r>");
gmio_amf_write_color_component(context, color->r, color->r_formula);
gmio_ostringstream_write_chararray(sstream, "</r><g>");
@ -177,7 +177,7 @@ static bool gmio_amf_write_root_metadata(struct gmio_amf_wcontext* context)
doc->func_get_document_element(
doc->cookie,
GMIO_AMF_DOCUMENT_ELEMENT_METADATA, imeta, &metadata);
gmio_amf_write_metadata(context->sstream, &metadata);
gmio_amf_write_metadata(&context->sstream, &metadata);
gmio_amf_wcontext_incr_task_progress(context);
}
return gmio_no_error(context->error);
@ -187,7 +187,7 @@ static bool gmio_amf_write_root_metadata(struct gmio_amf_wcontext* context)
static bool gmio_amf_write_root_materials(struct gmio_amf_wcontext* context)
{
const struct gmio_amf_document* doc = context->document;
struct gmio_ostringstream* sstream = context->sstream;
struct gmio_ostringstream* sstream = &context->sstream;
struct gmio_amf_material material = {0};
for (uint32_t imat = 0; imat < doc->material_count; ++imat) {
doc->func_get_document_element(
@ -280,7 +280,7 @@ static bool gmio_amf_write_mesh(
const struct gmio_amf_object_mesh_element_index* base_mesh_element_index)
{
const struct gmio_amf_document* doc = context->document;
struct gmio_ostringstream* sstream = context->sstream;
struct gmio_ostringstream* sstream = &context->sstream;
struct gmio_amf_object_mesh_element_index mesh_elt_index =
*base_mesh_element_index;
const struct gmio_ostringstream_format_float* f64_format =
@ -441,7 +441,7 @@ static bool gmio_amf_write_mesh(
static bool gmio_amf_write_root_objects(struct gmio_amf_wcontext* context)
{
const struct gmio_amf_document* doc = context->document;
struct gmio_ostringstream* sstream = context->sstream;
struct gmio_ostringstream* sstream = &context->sstream;
struct gmio_amf_object object = {0};
for (uint32_t iobj = 0; iobj < doc->object_count; ++iobj) {
doc->func_get_document_element(
@ -496,7 +496,7 @@ static bool gmio_amf_write_root_objects(struct gmio_amf_wcontext* context)
static bool gmio_amf_write_root_textures(struct gmio_amf_wcontext* context)
{
const struct gmio_amf_document* doc = context->document;
struct gmio_ostringstream* sstream = context->sstream;
struct gmio_ostringstream* sstream = &context->sstream;
struct gmio_amf_texture texture = {0};
for (uint32_t itex = 0; itex < doc->texture_count; ++itex) {
doc->func_get_document_element(
@ -532,7 +532,7 @@ static bool gmio_amf_write_root_textures(struct gmio_amf_wcontext* context)
static bool gmio_amf_write_root_constellations(struct gmio_amf_wcontext* context)
{
const struct gmio_amf_document* doc = context->document;
struct gmio_ostringstream* sstream = context->sstream;
struct gmio_ostringstream* sstream = &context->sstream;
struct gmio_amf_constellation constellation = {0};
for (uint32_t icons = 0; icons < doc->constellation_count; ++icons) {
doc->func_get_document_element(
@ -814,6 +814,39 @@ static struct gmio_zip_entry_filename gmio_amf_zip_entry_filename(
return zip_filename;
}
/* Writes AMF file data, plain text or compressed(ZIP)
* This function satisfies the signature required by gmio_zip_write_single_file()
*/
static int gmio_amf_write_file_data(
void* cookie, struct gmio_zip_data_descriptor* dd)
{
struct gmio_amf_wcontext* context = (struct gmio_amf_wcontext*)cookie;
struct gmio_ostringstream* sstream = &context->sstream;
gmio_amf_write_amf_begin(sstream, context->document);
if (!gmio_amf_write_root_metadata(context))
return context->error;
if (!gmio_amf_write_root_materials(context))
return context->error;
if (!gmio_amf_write_root_objects(context))
return context->error;
if (!gmio_amf_write_root_textures(context))
return context->error;
if (!gmio_amf_write_root_constellations(context))
return context->error;
if (context->options->compress) {
gmio_ostringstream_flush(sstream);
context->z_flush = Z_FINISH;
}
gmio_ostringstream_write_chararray(sstream, "</amf>\n");
gmio_ostringstream_flush(sstream);
if (context->options->compress && dd != NULL) {
dd->crc32 = context->z_crc32;
dd->uncompressed_size = context->z_uncompressed_size;
dd->compressed_size = context->z_compressed_size;
}
return context->error;
}
int gmio_amf_write(
struct gmio_stream* stream,
const struct gmio_amf_document* doc,
@ -822,19 +855,10 @@ int gmio_amf_write(
static const struct gmio_amf_write_options default_write_opts = {0};
opts = opts != NULL ? opts : &default_write_opts;
/* Constants */
struct gmio_amf_wcontext context = {0};
struct gmio_memblock_helper mblock_helper =
gmio_memblock_helper(opts != NULL ? &opts->stream_memblock : NULL);
const struct gmio_memblock* memblock = &mblock_helper.memblock;
const struct gmio_zip_entry_filename zip_entry_filename =
gmio_amf_zip_entry_filename(opts);
/* Variables */
struct gmio_amf_wcontext context = {0};
struct gmio_ostringstream sstream =
gmio_ostringstream(
*stream, gmio_string(memblock->ptr, 0, memblock->size));
uintmax_t zip_write_pos = 0;
/* Check validity of input parameters */
context.error = GMIO_ERROR_OK;
@ -847,8 +871,12 @@ int gmio_amf_write(
/* Initialize writing context */
const struct gmio_string_16 f64_stdio_format =
gmio_to_stdio_float_format(opts->float64_format, opts->float64_prec);
context.sstream =
gmio_ostringstream(
*stream, gmio_string(memblock->ptr, 0, memblock->size));
context.sstream.cookie = &context;
context.sstream.func_stream_write = &gmio_amf_ostringstream_write;
context.options = opts;
context.sstream = &sstream;
context.document = doc;
context.task_iface = &opts->task_iface;
context.task_progress_current = 0;
@ -858,10 +886,11 @@ int gmio_amf_write(
context.f64_format.text_format = opts->float64_format;
context.f64_format.precision =
opts->float64_prec != 0 ? opts->float64_prec : 16;
if (opts->compress) {
/* Initialize internal zlib stream for compression */
const size_t mblock_halfsize = memblock->size / 2;
context.sstream->strbuff.capacity = mblock_halfsize;
context.sstream.strbuff.capacity = mblock_halfsize;
context.z_memblock =
gmio_memblock(
(uint8_t*)memblock->ptr + mblock_halfsize,
@ -874,85 +903,23 @@ int gmio_amf_write(
if (gmio_error(context.error))
goto label_end;
context.z_flush = Z_NO_FLUSH;
/* Write ZIP file */
struct gmio_zip_file_entry file_entry = {0};
file_entry.compress_method = GMIO_ZIP_COMPRESS_METHOD_DEFLATE;
file_entry.feature_version =
opts->force_zip64_format ?
GMIO_ZIP_FEATURE_VERSION_FILE_ZIP64_FORMAT_EXTENSIONS :
GMIO_ZIP_FEATURE_VERSION_FILE_COMPRESSED_DEFLATE;
const struct gmio_zip_entry_filename zip_entry_filename =
gmio_amf_zip_entry_filename(opts);
file_entry.filename = zip_entry_filename.ptr;
file_entry.filename_len = zip_entry_filename.len;
file_entry.cookie_func_write_file_data = &context;
file_entry.func_write_file_data = gmio_amf_write_file_data;
gmio_zip_write_single_file(stream, &file_entry, &context.error);
}
sstream.cookie = &context;
sstream.func_stream_write = &gmio_amf_ostringstream_write;
/* If compression enabled, write ZIP local file header */
if (opts->compress) {
struct gmio_zip_local_file_header info = {0};
info.general_purpose_flags =
GMIO_ZIP_GENERAL_PURPOSE_FLAG_USE_DATA_DESCRIPTOR;
info.compress_method = GMIO_ZIP_COMPRESS_METHOD_DEFLATE;
info.filename = zip_entry_filename.ptr;
info.filename_len = zip_entry_filename.len;
zip_write_pos +=
gmio_zip_write_local_file_header(stream, &info, &context.error);
if (gmio_error(context.error))
goto label_end;
}
gmio_amf_write_amf_begin(&sstream, doc);
if (!gmio_amf_write_root_metadata(&context))
goto label_end;
if (!gmio_amf_write_root_materials(&context))
goto label_end;
if (!gmio_amf_write_root_objects(&context))
goto label_end;
if (!gmio_amf_write_root_textures(&context))
goto label_end;
if (!gmio_amf_write_root_constellations(&context))
goto label_end;
if (opts->compress) {
gmio_ostringstream_flush(&sstream);
context.z_flush = Z_FINISH;
}
gmio_ostringstream_write_chararray(&sstream, "</amf>\n");
gmio_ostringstream_flush(&sstream);
/* Write ending ZIP archive data */
if (opts->compress && gmio_no_error(context.error)) {
zip_write_pos += context.z_compressed_size;
/* Write data descriptor */
struct gmio_zip_data_descriptor dd = {0};
dd.crc32 = context.z_crc32;
dd.compressed_size = context.z_compressed_size;
dd.uncompressed_size = context.z_uncompressed_size;
zip_write_pos +=
gmio_zip_write_data_descriptor(stream, &dd, &context.error);
if (gmio_error(context.error))
goto label_end;
/* Write central directory header */
struct gmio_zip_central_directory_header cdh = {0};
cdh.version_needed_to_extract =
GMIO_ZIP_FEATURE_VERSION_FILE_COMPRESSED_DEFLATE;
cdh.general_purpose_flags =
GMIO_ZIP_GENERAL_PURPOSE_FLAG_USE_DATA_DESCRIPTOR;
cdh.compress_method = GMIO_ZIP_COMPRESS_METHOD_DEFLATE;
cdh.crc32 = context.z_crc32;
cdh.compressed_size = (uint32_t)context.z_compressed_size;
cdh.uncompressed_size = (uint32_t)context.z_uncompressed_size;
cdh.filename = zip_entry_filename.ptr;
cdh.filename_len = zip_entry_filename.len;
const uintmax_t central_dir_startpos = zip_write_pos;
const size_t central_dir_size =
gmio_zip_write_central_directory_header(
stream, &cdh, &context.error);
zip_write_pos += central_dir_size;
if (gmio_error(context.error))
goto label_end;
/* Write end of central directory record */
struct gmio_zip_end_of_central_directory_record eocdr = {0};
eocdr.total_entry_count_in_central_dir_on_disk = 1;
eocdr.total_entry_count_in_central_dir = 1;
eocdr.central_dir_size = (uint32_t)central_dir_size;
eocdr.start_offset_central_dir_from_disk_start_nb =
(uint32_t)central_dir_startpos;
zip_write_pos +=
gmio_zip_write_end_of_central_directory_record(
stream, &eocdr, &context.error);
else {
context.error = gmio_amf_write_file_data(&context, NULL);
}
label_end:

View File

@ -82,6 +82,7 @@ struct gmio_amf_write_options
struct gmio_zlib_compress_options z_compress_options;
const char* zip_entry_filename;
uint16_t zip_entry_filename_len;
bool force_zip64_format;
};
#endif /* GMIO_AMF_IO_OPTIONS_H */

View File

@ -39,9 +39,15 @@
#include "global.h"
/*! \c GMIO_ZLIB_ERROR_TAG
* Byte-mask to tag(identify) zlib-specific error codes */
enum { GMIO_ZLIB_ERROR_TAG = 0x1000000 };
enum {
/*! \c GMIO_ZLIB_ERROR_TAG
* Byte-mask to tag(identify) zlib-specific error codes */
GMIO_ZLIB_ERROR_TAG = 0x01000000,
/*! \c GMIO_ZIP_ERROR_TAG
* Byte-mask to tag(identify) ZIP-specific error codes */
GMIO_ZIP_ERROR_TAG = 0x02000000
};
/*! Common errors */
enum gmio_error
@ -97,7 +103,14 @@ enum gmio_error
/*! Invalid compression memory usage, see
* gmio_zlib_compress_options::memory_usage */
GMIO_ERROR_ZLIB_INVALID_COMPRESS_MEMORY_USAGE
GMIO_ERROR_ZLIB_INVALID_COMPRESS_MEMORY_USAGE,
/*! Zip64 format requires the compiler to provide a 64b integer type */
GMIO_ERROR_ZIP_INT64_TYPE_REQUIRED = GMIO_ZIP_ERROR_TAG + 0x01,
/*! The size of some ZIP file entry exceeds 32b limit and so requires Zip64
* format */
GMIO_ERROR_ZIP64_FORMAT_REQUIRED = GMIO_ZIP_ERROR_TAG + 0x02
};
/*! Returns true if <tt>code == GMIO_NO_ERROR</tt> */

View File

@ -34,8 +34,7 @@
#include <stddef.h>
/*! Stores a mutable string of 8-bit chars
*
/*! Stores a mutable string of 8-bit chars.
* For faster lookups, it knowns the length of its contents. Length must not
* exceeds the maximum size(capacity).
*/

View File

@ -41,8 +41,10 @@
enum {
GMIO_ZIP_SIZE_LOCAL_FILE_HEADER = 4 + 5*2 + 3*4 + 2*2,
GMIO_ZIP_SIZE_CENTRAL_DIRECTORY_HEADER = 4 + 6*2 + 3*4 + 5*2 +2*4,
GMIO_ZIP64_SIZE_DATA_DESCRIPTOR = 4 + 2*8,
GMIO_ZIP_SIZE_DATA_DESCRIPTOR = 4 + 2*4,
GMIO_ZIP_SIZE_DATA_DESCRIPTOR = 4 + 4 + 4 + 4,
GMIO_ZIP64_SIZE_DATA_DESCRIPTOR = 4 + 4 + 2*8,
GMIO_ZIP64_SIZE_END_OF_CENTRAL_DIRECTORY_RECORD = 4 + 8 + 2*2 + 2*4 + 4*8,
GMIO_ZIP64_SIZE_END_OF_CENTRAL_DIRECTORY_LOCATOR = 4 + 4 + 8 + 4,
GMIO_ZIP_SIZE_END_OF_CENTRAL_DIRECTORY_RECORD = 4 + 4*2 + 2*4 + 2
};
@ -176,7 +178,7 @@ size_t gmio_zip_write_data_descriptor(
uint8_t bytes[GMIO_ZIP64_SIZE_DATA_DESCRIPTOR];
uint8_t* buff = bytes;
/* 4-bytes crc-32 */
gmio_adv_encode_uint32_le(0x08074b50, &buff);
gmio_adv_encode_uint32_le(info->crc32, &buff);
/* Compressed size and uncompressed size (4 or 8 bytes) */
if (info->use_zip64) {
@ -184,21 +186,19 @@ size_t gmio_zip_write_data_descriptor(
gmio_adv_encode_uint64_le(info->compressed_size, &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);
return gmio_zip_io_returnerr(
0, GMIO_ERROR_ZIP_INT64_TYPE_REQUIRED, ptr_error);
#endif
}
else if (!gmio_zip64_required(
info->uncompressed_size, info->compressed_size))
{
gmio_adv_encode_uint32_le((uint32_t)info->compressed_size, &buff);
gmio_adv_encode_uint32_le((uint32_t)info->uncompressed_size, &buff);
}
else {
if (info->compressed_size <= UINT32_MAX
&& info->uncompressed_size <= UINT32_MAX)
{
gmio_adv_encode_uint32_le((uint32_t)info->compressed_size, &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);
}
return gmio_zip_io_returnerr(
0, GMIO_ERROR_ZIP64_FORMAT_REQUIRED, ptr_error);
}
/* Write to stream */
@ -247,6 +247,59 @@ size_t gmio_zip_read_central_directory_header(
return gmio_zip_io_returnerr(read_len, GMIO_ERROR_OK, ptr_error);
}
size_t gmio_zip64_read_end_of_central_directory_record(
struct gmio_stream *stream,
struct gmio_zip64_end_of_central_directory_record *info,
int *ptr_error)
{
#ifdef GMIO_HAVE_INT64_TYPE
uint8_t bytes[GMIO_ZIP64_SIZE_END_OF_CENTRAL_DIRECTORY_RECORD];
const uint8_t* buff = bytes;
size_t read_len = 0;
if (!gmio_zip_readcheckbytes(stream, bytes, sizeof(bytes), &read_len, ptr_error))
return read_len;
if (!gmio_zip_readcheckmagic(&buff, 0x06064b50, ptr_error))
return read_len;
gmio_adv_decode_uint64_le(&buff);
info->version_made_by = gmio_adv_decode_uint16_le(&buff);
info->version_needed_to_extract = gmio_adv_decode_uint16_le(&buff);
info->disk_nb = gmio_adv_decode_uint32_le(&buff);
info->disk_nb_with_start_of_central_dir = gmio_adv_decode_uint32_le(&buff);
info->total_entry_count_in_central_dir_on_disk =
gmio_adv_decode_uint64_le(&buff);
info->central_dir_size = gmio_adv_decode_uint64_le(&buff);
info->start_offset_central_dir_from_disk_start_nb =
gmio_adv_decode_uint64_le(&buff);
return gmio_zip_io_returnerr(read_len, GMIO_ERROR_OK, ptr_error);
#else
return gmio_zip_io_returnerr(
0, GMIO_ERROR_ZIP_INT64_TYPE_REQUIRED, ptr_error);
#endif
}
size_t gmio_zip64_read_end_of_central_directory_locator(
struct gmio_stream *stream,
struct gmio_zip64_end_of_central_directory_locator *info,
int *ptr_error)
{
#ifdef GMIO_HAVE_INT64_TYPE
uint8_t bytes[GMIO_ZIP64_SIZE_END_OF_CENTRAL_DIRECTORY_LOCATOR];
const uint8_t* buff = bytes;
size_t read_len = 0;
if (!gmio_zip_readcheckbytes(stream, bytes, sizeof(bytes), &read_len, ptr_error))
return read_len;
if (!gmio_zip_readcheckmagic(&buff, 0x07064b50, ptr_error))
return read_len;
info->disk_nb_with_start_of_central_dir = gmio_adv_decode_uint32_le(&buff);
info->relative_offset = gmio_adv_decode_uint64_le(&buff);
info->total_disk_count = gmio_adv_decode_uint32_le(&buff);
return gmio_zip_io_returnerr(read_len, GMIO_ERROR_OK, ptr_error);
#else
return gmio_zip_io_returnerr(
0, GMIO_ERROR_ZIP_INT64_TYPE_REQUIRED, ptr_error);
#endif
}
size_t gmio_zip_read_end_of_central_directory_record(
struct gmio_stream *stream,
struct gmio_zip_end_of_central_directory_record *info,
@ -269,7 +322,6 @@ size_t gmio_zip_read_end_of_central_directory_record(
gmio_adv_decode_uint32_le(&buff);
info->filecomment_len = gmio_adv_decode_uint16_le(&buff);
info->filecomment = NULL;
return gmio_zip_io_returnerr(read_len, GMIO_ERROR_OK, ptr_error);
}
@ -324,6 +376,8 @@ size_t gmio_zip_read_data_descriptor(
size_t read_len = 0;
if (!gmio_zip_readcheckbytes(stream, bytes, sizeof(bytes), &read_len, ptr_error))
return read_len;
if (!gmio_zip_readcheckmagic(&buff, 0x08074b50, ptr_error))
return read_len;
info->crc32 = gmio_adv_decode_uint32_le(&buff);
info->compressed_size = gmio_adv_decode_uint32_le(&buff);
info->uncompressed_size = gmio_adv_decode_uint32_le(&buff);
@ -341,13 +395,15 @@ size_t gmio_zip64_read_data_descriptor(
size_t read_len = 0;
if (!gmio_zip_readcheckbytes(stream, bytes, sizeof(bytes), &read_len, ptr_error))
return read_len;
if (!gmio_zip_readcheckmagic(&buff, 0x08074b50, ptr_error))
return read_len;
info->crc32 = gmio_adv_decode_uint32_le(&buff);
info->compressed_size = gmio_adv_decode_uint64_le(&buff);
info->uncompressed_size = gmio_adv_decode_uint64_le(&buff);
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);
return gmio_zip_io_returnerr(
0, GMIO_ERROR_ZIP_INT64_TYPE_REQUIRED, ptr_error);
#endif
}
@ -401,32 +457,86 @@ size_t gmio_zip_write_central_directory_header(
stream, written_len, expected_written_len, ptr_error);
}
size_t gmio_zip64_write_extrafield_extended_info(
size_t gmio_zip64_write_extrafield(
uint8_t *buff,
size_t buff_capacity,
const struct gmio_zip64_extrablock_extended_info *info,
const struct gmio_zip64_extrafield *info,
int* ptr_error)
{
if (buff_capacity < GMIO_ZIP64_SIZE_EXTRAFIELD_EXTENDED_INFO) {
if (buff_capacity < GMIO_ZIP64_SIZE_EXTRAFIELD) {
return gmio_zip_io_returnerr(
0, GMIO_ERROR_INVALID_MEMBLOCK_SIZE, ptr_error);
}
#ifdef GMIO_HAVE_INT64_TYPE
/* Tag */
gmio_adv_encode_uint16_le(0x0001, &buff);
/* Size of the "extra" block */
gmio_adv_encode_uint16_le(GMIO_ZIP64_SIZE_EXTRAFIELD_EXTENDED_INFO - 2, &buff);
static const uint16_t extrablock_tag = 0x0001;
static const uint16_t extrablock_size = 3*8 + 4;
gmio_adv_encode_uint16_le(extrablock_tag, &buff);
gmio_adv_encode_uint16_le(extrablock_size, &buff);
gmio_adv_encode_uint64_le(info->uncompressed_size, &buff);
gmio_adv_encode_uint64_le(info->compressed_size, &buff);
gmio_adv_encode_uint64_le(info->relative_offset_local_header, &buff);
gmio_adv_encode_uint32_le(info->disk_nb_start, &buff);
return gmio_zip_io_returnerr(
GMIO_ZIP64_SIZE_EXTRAFIELD_EXTENDED_INFO,
GMIO_ERROR_OK,
ptr_error);
GMIO_ZIP64_SIZE_EXTRAFIELD, GMIO_ERROR_OK, ptr_error);
#else
/* TODO: error code */
return gmio_zip_io_returnerr(0, GMIO_ERROR_UNKNOWN, error);
return gmio_zip_io_returnerr(
0, GMIO_ERROR_ZIP_INT64_TYPE_REQUIRED, ptr_error);
#endif
}
size_t gmio_zip64_write_end_of_central_directory_record(
struct gmio_stream *stream,
const struct gmio_zip64_end_of_central_directory_record *info,
int *ptr_error)
{
#ifdef GMIO_HAVE_INT64_TYPE
uint8_t bytes[GMIO_ZIP64_SIZE_END_OF_CENTRAL_DIRECTORY_RECORD];
uint8_t* buff = bytes;
gmio_adv_encode_uint32_le(0x06064b50, &buff);
gmio_adv_encode_uint64_le(sizeof(bytes) - 12, &buff);
gmio_adv_encode_uint16_le(info->version_made_by, &buff);
gmio_adv_encode_uint16_le(info->version_needed_to_extract, &buff);
gmio_adv_encode_uint32_le(info->disk_nb, &buff);
gmio_adv_encode_uint32_le(info->disk_nb_with_start_of_central_dir, &buff);
gmio_adv_encode_uint64_le(
info->total_entry_count_in_central_dir_on_disk, &buff);
gmio_adv_encode_uint64_le(info->total_entry_count_in_central_dir, &buff);
gmio_adv_encode_uint64_le(info->central_dir_size, &buff);
gmio_adv_encode_uint64_le(
info->start_offset_central_dir_from_disk_start_nb, &buff);
/* Write to stream */
const size_t expected_written_len = sizeof(bytes);
const size_t written_len =
gmio_stream_write_bytes(stream, bytes, sizeof(bytes));
return gmio_zip_write_returnhelper(
stream, written_len, expected_written_len, ptr_error);
#else
return gmio_zip_io_returnerr(
0, GMIO_ERROR_ZIP_INT64_TYPE_REQUIRED, ptr_error);
#endif
}
size_t gmio_zip64_write_end_of_central_directory_locator(
struct gmio_stream *stream,
const struct gmio_zip64_end_of_central_directory_locator *info,
int *ptr_error)
{
#ifdef GMIO_HAVE_INT64_TYPE
uint8_t bytes[GMIO_ZIP64_SIZE_END_OF_CENTRAL_DIRECTORY_LOCATOR];
uint8_t* buff = bytes;
gmio_adv_encode_uint32_le(0x07064b50, &buff);
gmio_adv_encode_uint32_le(info->disk_nb_with_start_of_central_dir, &buff);
gmio_adv_encode_uint64_le(info->relative_offset, &buff);
gmio_adv_encode_uint32_le(info->total_disk_count, &buff);
/* Write to stream */
const size_t expected_written_len = sizeof(bytes);
const size_t written_len =
gmio_stream_write_bytes(stream, bytes, sizeof(bytes));
return gmio_zip_write_returnhelper(
stream, written_len, expected_written_len, ptr_error);
#else
return gmio_zip_io_returnerr(
0, GMIO_ERROR_ZIP_INT64_TYPE_REQUIRED, ptr_error);
#endif
}
@ -493,3 +603,140 @@ size_t gmio_zip_write_end_of_central_directory_record(
return gmio_zip_write_returnhelper(
stream, written_len, expected_written_len, ptr_error);
}
bool gmio_zip64_required(
uintmax_t uncompressed_size, uintmax_t compressed_size)
{
return uncompressed_size > UINT32_MAX || compressed_size > UINT32_MAX;
}
bool gmio_zip_write_single_file(
struct gmio_stream *stream,
const struct gmio_zip_file_entry *file_entry,
int *ptr_error)
{
const bool use_zip64_format_extensions =
file_entry->feature_version
>= GMIO_ZIP_FEATURE_VERSION_FILE_ZIP64_FORMAT_EXTENSIONS;
uint8_t extrafield[GMIO_ZIP64_SIZE_EXTRAFIELD];
size_t zip_write_pos = 0;
/* Write local file header */
struct gmio_zip_local_file_header lfh = {0};
lfh.general_purpose_flags = GMIO_ZIP_GENERAL_PURPOSE_FLAG_USE_DATA_DESCRIPTOR;
lfh.compress_method = file_entry->compress_method;
lfh.filename = file_entry->filename;
lfh.filename_len = file_entry->filename_len;
if (use_zip64_format_extensions) {
const struct gmio_zip64_extrafield zip64extra = {0};
zip_write_pos +=
gmio_zip64_write_extrafield(
extrafield, sizeof(extrafield), &zip64extra, ptr_error);
if (gmio_error(*ptr_error))
return false;
lfh.extrafield = extrafield;
lfh.extrafield_len = sizeof(extrafield);
}
zip_write_pos += gmio_zip_write_local_file_header(stream, &lfh, ptr_error);
if (gmio_error(*ptr_error))
return false;
/* Write file data */
struct gmio_zip_data_descriptor dd = {0};
*ptr_error =
file_entry->func_write_file_data(
file_entry->cookie_func_write_file_data, &dd);
zip_write_pos += dd.compressed_size;
if (gmio_error(*ptr_error))
return false;
/* Guess version needed */
const bool needs_zip64 =
use_zip64_format_extensions
|| gmio_zip64_required(dd.uncompressed_size, dd.compressed_size);
const enum gmio_zip_feature_version version_needed =
needs_zip64 && !use_zip64_format_extensions ?
GMIO_ZIP_FEATURE_VERSION_FILE_ZIP64_FORMAT_EXTENSIONS :
file_entry->feature_version;
/* Write data descriptor */
dd.use_zip64 = needs_zip64;
zip_write_pos += gmio_zip_write_data_descriptor(stream, &dd, ptr_error);
if (gmio_error(*ptr_error))
return false;
/* Write central directory header */
const uintmax_t pos_start_central_dir = zip_write_pos;
struct gmio_zip_central_directory_header cdh = {0};
cdh.use_zip64 = needs_zip64;
cdh.version_needed_to_extract = version_needed;
cdh.general_purpose_flags =
GMIO_ZIP_GENERAL_PURPOSE_FLAG_USE_DATA_DESCRIPTOR;
cdh.compress_method = file_entry->compress_method;
cdh.crc32 = dd.crc32;
cdh.compressed_size = (uint32_t)dd.compressed_size;
cdh.uncompressed_size = (uint32_t)dd.uncompressed_size;
cdh.filename = file_entry->filename;
cdh.filename_len = file_entry->filename_len;
if (needs_zip64) {
struct gmio_zip64_extrafield zip64extra = {0};
zip64extra.compressed_size = dd.compressed_size;
zip64extra.uncompressed_size = dd.uncompressed_size;
zip64extra.relative_offset_local_header = 0;
zip_write_pos +=
gmio_zip64_write_extrafield(
extrafield, sizeof(extrafield), &zip64extra, ptr_error);
if (gmio_error(*ptr_error))
return false;
cdh.extrafield = extrafield;
cdh.extrafield_len = sizeof(extrafield);
}
const uintmax_t central_dir_startpos = zip_write_pos;
const size_t central_dir_size =
gmio_zip_write_central_directory_header(stream, &cdh, ptr_error);
zip_write_pos += central_dir_size;
if (gmio_error(*ptr_error))
return false;
const uintmax_t pos_end_central_dir = zip_write_pos;
if (needs_zip64) {
/* Write Zip64 end of central directory record */
struct gmio_zip64_end_of_central_directory_record eocdr64 = {0};
eocdr64.version_needed_to_extract = version_needed;
eocdr64.total_entry_count_in_central_dir_on_disk = 1;
eocdr64.total_entry_count_in_central_dir = 1;
eocdr64.central_dir_size =
pos_end_central_dir - pos_start_central_dir;
eocdr64.start_offset_central_dir_from_disk_start_nb =
pos_start_central_dir;
zip_write_pos +=
gmio_zip64_write_end_of_central_directory_record(
stream, &eocdr64, ptr_error);
if (gmio_error(*ptr_error))
return false;
/* Write Zip64 end of central directory locator */
struct gmio_zip64_end_of_central_directory_locator eocdl64 = {0};
eocdl64.relative_offset = pos_end_central_dir;
eocdl64.total_disk_count = 1;
zip_write_pos +=
gmio_zip64_write_end_of_central_directory_locator(
stream, &eocdl64, ptr_error);
if (gmio_error(*ptr_error))
return false;
}
/* Write end of central directory record */
struct gmio_zip_end_of_central_directory_record eocdr = {0};
eocdr.use_zip64 = needs_zip64;
eocdr.total_entry_count_in_central_dir_on_disk = 1;
eocdr.total_entry_count_in_central_dir = 1;
eocdr.central_dir_size = (uint32_t)central_dir_size;
eocdr.start_offset_central_dir_from_disk_start_nb =
(uint32_t)central_dir_startpos;
zip_write_pos +=
gmio_zip_write_end_of_central_directory_record(
stream, &eocdr, ptr_error);
return gmio_no_error(*ptr_error);
}

View File

@ -203,11 +203,11 @@ struct gmio_zip_central_directory_header {
};
enum {
GMIO_ZIP64_SIZE_EXTRAFIELD_EXTENDED_INFO = 2*2 + 3*8 + 4
GMIO_ZIP64_SIZE_EXTRAFIELD = 2*2 + 3*8 + 4
};
/*! Zip64 extended info (extra field) */
struct gmio_zip64_extrablock_extended_info {
struct gmio_zip64_extrafield {
uintmax_t compressed_size;
uintmax_t uncompressed_size;
uintmax_t relative_offset_local_header;
@ -229,7 +229,6 @@ struct gmio_zip_end_of_central_directory_record {
/*! Zip64 end of central directory record */
struct gmio_zip64_end_of_central_directory_record {
uintmax_t remaining_record_size; /* should be 64b */
uint16_t version_made_by;
enum gmio_zip_feature_version version_needed_to_extract;
uint32_t disk_nb;
@ -241,6 +240,13 @@ struct gmio_zip64_end_of_central_directory_record {
const uint8_t* extensible_data_sector; /* Reserved for use by PKWARE */
};
/*! Zip64 end of central directory locator */
struct gmio_zip64_end_of_central_directory_locator {
uint32_t disk_nb_with_start_of_central_dir;
uintmax_t relative_offset; /* should be 64b */
uint32_t total_disk_count;
};
struct gmio_zip_write_result {
int error;
size_t written_len;
@ -275,6 +281,18 @@ size_t gmio_zip_read_central_directory_header(
struct gmio_zip_central_directory_header* info,
int* ptr_error);
/*! Reads Zip64 end of central directory record from \p stream */
size_t gmio_zip64_read_end_of_central_directory_record(
struct gmio_stream* stream,
struct gmio_zip64_end_of_central_directory_record* info,
int* ptr_error);
/*! Reads Zip64 end of central directory locator from \p stream */
size_t gmio_zip64_read_end_of_central_directory_locator(
struct gmio_stream* stream,
struct gmio_zip64_end_of_central_directory_locator* 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,
@ -299,15 +317,25 @@ size_t gmio_zip_write_central_directory_header(
const struct gmio_zip_central_directory_header* info,
int* ptr_error);
/*! Writes ZIP local file header to \p buff
*
/*! Writes Zip64 extra field to \p buff.
* Returns \c GMIO_ERROR_INVALID_MEMBLOCK_SIZE if \p buff_capacity is less than
* \c GMIO_ZIP64_SIZE_EXTRAFIELD_EXTENDED_INFO
*/
size_t gmio_zip64_write_extrafield_extended_info(
* \c GMIO_ZIP64_SIZE_EXTRAFIELD */
size_t gmio_zip64_write_extrafield(
uint8_t* buff,
size_t buff_capacity,
const struct gmio_zip64_extrablock_extended_info* info,
const struct gmio_zip64_extrafield* info,
int* ptr_error);
/*! Writes Zip64 end of central directory record to \p stream */
size_t gmio_zip64_write_end_of_central_directory_record(
struct gmio_stream* stream,
const struct gmio_zip64_end_of_central_directory_record* info,
int* ptr_error);
/*! Writes Zip64 end of central directory locator to \p stream */
size_t gmio_zip64_write_end_of_central_directory_locator(
struct gmio_stream* stream,
const struct gmio_zip64_end_of_central_directory_locator* info,
int* ptr_error);
/*! Writes ZIP end of central directory record to \p stream */
@ -316,4 +344,25 @@ size_t gmio_zip_write_end_of_central_directory_record(
const struct gmio_zip_end_of_central_directory_record* info,
int* ptr_error);
/*! Is Zip64 format required for this file uncompress/compress sizes ? */
bool gmio_zip64_required(
uintmax_t uncompressed_size, uintmax_t compressed_size);
/*! Defines a file entry in a ZIP archive */
struct gmio_zip_file_entry {
enum gmio_zip_compress_method compress_method;
enum gmio_zip_feature_version feature_version;
const char* filename;
uint16_t filename_len;
void* cookie_func_write_file_data;
int (*func_write_file_data)(
void* cookie, struct gmio_zip_data_descriptor* dd);
};
/*! Writes a ZIP archive containing a single file */
bool gmio_zip_write_single_file(
struct gmio_stream* stream,
const struct gmio_zip_file_entry* file_entry,
int* ptr_error);
#endif /* GMIO_INTERNAL_ZIP_UTILS_H */

View File

@ -49,7 +49,9 @@ const char* all_tests()
gmio_memblock_set_default_constructor(gmio_memblock_for_tests);
UTEST_RUN(test_amf_write_doc_null);
UTEST_RUN(test_amf_write_doc_1);
UTEST_RUN(test_amf_write_doc_1_plaintext);
UTEST_RUN(test_amf_write_doc_1_zip);
UTEST_RUN(test_amf_write_doc_1_zip64);
return NULL;
}

View File

@ -67,6 +67,7 @@ struct __tamf__mesh
struct __tamf__document
{
const struct __tamf__material* vec_material;
uint32_t material_count;
struct __tamf__mesh mesh;
};
@ -174,11 +175,6 @@ static void __tamf__get_document_element_metadata(
}
}
static void __tamf__skip_zip_local_file_header()
{
}
static const char* test_amf_write_doc_null()
{
struct gmio_stream stream = {0};
@ -188,205 +184,382 @@ static const char* test_amf_write_doc_null()
return NULL;
}
static const char* test_amf_write_doc_1()
const struct __tamf__material __tamf__doc_1_materials[] = {
{ { 1., 0., 0. }, "red" },
{ { 0., 1., 0. }, "green" },
{ { 0., 0., 1. }, "blue" },
{ { 1., 1., 1. }, "white" }
};
const struct gmio_vec3d __tamf__doc_1_vertices[] = {
{ 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 __tamf__doc_1_triangles[] = {
{ 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 __tamf__create_doc_1()
{
struct __tamf__document doc = {0};
doc.vec_material = __tamf__doc_1_materials;
doc.material_count = GMIO_ARRAY_SIZE(__tamf__doc_1_materials);
doc.mesh.vec_vertex = __tamf__doc_1_vertices;
doc.mesh.vertex_count = GMIO_ARRAY_SIZE(__tamf__doc_1_vertices);
doc.mesh.vec_triangle = __tamf__doc_1_triangles;
doc.mesh.triangle_count = GMIO_ARRAY_SIZE(__tamf__doc_1_triangles);
return doc;
}
struct gmio_amf_document __tamf_create_doc(
const struct __tamf__document* testdoc)
{
struct gmio_amf_document doc = {0};
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 = testdoc->material_count;
return doc;
}
static int __tamf__write_amf(
struct gmio_rw_buffer* wbuff,
const struct gmio_amf_document* doc,
const struct gmio_amf_write_options* opts)
{
struct gmio_stream stream = gmio_stream_buffer(wbuff);
return gmio_amf_write(&stream, doc, opts);
}
static const char* test_amf_write_doc_1_plaintext()
{
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 = 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 */
{
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);
static const char zip_entry_filename[] = "test.amf";
static const uint16_t zip_entry_filename_len =
sizeof(zip_entry_filename) - 1;
{
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;
options.zip_entry_filename = zip_entry_filename;
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);
const struct __tamf__document testdoc = __tamf__create_doc_1();
const struct gmio_amf_document doc = __tamf_create_doc(&testdoc);
struct gmio_amf_write_options options = {0};
options.float64_prec = 9;
const int error = __tamf__write_amf(&wbuff, &doc, &options);
#if 0
FILE* file = fopen("output.zip", "wb");
fwrite(wbuff.ptr, 1, wbuff.pos, file);
fclose(file);
if (gmio_error(error))
printf("\n0x%x\n", error);
#endif
}
/* 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);
}
UTEST_COMPARE_INT(error, GMIO_ERROR_OK);
/* printf("%s\n", wbuff.ptr); */
free(wbuff.ptr);
return NULL;
}
static const char zip_entry_filename[] = "test.amf";
static const uint16_t zip_entry_filename_len = sizeof(zip_entry_filename) - 1;
static const char* __tamf__check_zip_local_file_header(
const struct gmio_zip_local_file_header* zip_lfh)
{
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,
zip_entry_filename_len);
UTEST_ASSERT(strncmp(
zip_lfh->filename,
zip_entry_filename,
zip_entry_filename_len)
== 0);
return NULL;
}
static const char* test_amf_write_doc_1_zip()
{
static const size_t wbuffsize = 8192;
struct gmio_rw_buffer wbuff = {0};
wbuff.ptr = calloc(wbuffsize, 1);
wbuff.len = wbuffsize;
const struct __tamf__document testdoc = __tamf__create_doc_1();
const struct gmio_amf_document doc = __tamf_create_doc(&testdoc);
{ /* Write uncompressed */
struct gmio_amf_write_options options = {0};
options.float64_prec = 9;
const int error = __tamf__write_amf(&wbuff, &doc, &options);
UTEST_COMPARE_INT(error, GMIO_ERROR_OK);
}
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);
{ /* Write compressed(ZIP) */
wbuff.pos = 0;
struct gmio_amf_write_options options = {0};
options.float64_prec = 9;
options.compress = true;
options.zip_entry_filename = zip_entry_filename;
options.zip_entry_filename_len = zip_entry_filename_len;
const int error = __tamf__write_amf(&wbuff, &doc, &options);
UTEST_COMPARE_INT(error, GMIO_ERROR_OK);
#if 1
FILE* file = fopen("output.zip", "wb");
fwrite(wbuff.ptr, 1, wbuff.pos, file);
fclose(file);
#endif
}
/* 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);
zip_lfh.filename = (const char*)wbuff.ptr + wbuff.pos;
const char* check_str = __tamf__check_zip_local_file_header(&zip_lfh);
if (check_str != NULL)
return check_str;
/* -- 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;
}
static const char* test_amf_write_doc_1_zip64()
{
static const size_t wbuffsize = 8192;
struct gmio_rw_buffer wbuff = {0};
wbuff.ptr = calloc(wbuffsize, 1);
wbuff.len = wbuffsize;
const struct __tamf__document testdoc = __tamf__create_doc_1();
const struct gmio_amf_document doc = __tamf_create_doc(&testdoc);
{ /* Write uncompressed */
struct gmio_amf_write_options options = {0};
options.float64_prec = 9;
const int error = __tamf__write_amf(&wbuff, &doc, &options);
UTEST_COMPARE_INT(error, GMIO_ERROR_OK);
}
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);
static const char zip_entry_filename[] = "test.amf";
static const uint16_t zip_entry_filename_len = sizeof(zip_entry_filename) - 1;
{ /* Write compressed(Zip64) */
wbuff.pos = 0;
struct gmio_amf_write_options options = {0};
options.float64_prec = 9;
options.compress = true;
options.zip_entry_filename = zip_entry_filename;
options.zip_entry_filename_len = zip_entry_filename_len;
options.force_zip64_format = true;
const int error = __tamf__write_amf(&wbuff, &doc, &options);
UTEST_COMPARE_INT(error, GMIO_ERROR_OK);
#if 1
FILE* file = fopen("output_64.zip", "wb");
fwrite(wbuff.ptr, 1, wbuff.pos, file);
fclose(file);
#endif
}
/* 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);
zip_lfh.filename = (const char*)wbuff.ptr + wbuff.pos;
const char* check_str = __tamf__check_zip_local_file_header(&zip_lfh);
if (check_str != NULL)
return check_str;
/* -- 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, 0xFFFF);
UTEST_COMPARE_UINT(zip_eocdr.disk_nb_with_start_of_central_dir, 0xFFFF);
UTEST_COMPARE_UINT(
zip_eocdr.total_entry_count_in_central_dir_on_disk, 0xFFFF);
UTEST_COMPARE_UINT(zip_eocdr.total_entry_count_in_central_dir, 0xFFFF);
UTEST_COMPARE_UINT(zip_eocdr.central_dir_size, 0xFFFFFFFF);
UTEST_COMPARE_UINT(
zip_eocdr.start_offset_central_dir_from_disk_start_nb,
0xFFFFFFFF);
#if 0
/* -- 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_zip64_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);
#endif
}
free(source);
free(wbuff.ptr);
return NULL;
}