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 */ /* Writing(output) context */
struct gmio_amf_wcontext struct gmio_amf_wcontext
{ {
struct gmio_ostringstream sstream;
const struct gmio_amf_write_options* options; const struct gmio_amf_write_options* options;
struct gmio_ostringstream* sstream;
const struct gmio_amf_document* document; const struct gmio_amf_document* document;
const struct gmio_task_iface* task_iface; const struct gmio_task_iface* task_iface;
intmax_t task_progress_current; intmax_t task_progress_current;
@ -85,7 +85,7 @@ static void gmio_amf_write_double(
double value, double value,
const char* value_formula) const char* value_formula)
{ {
struct gmio_ostringstream* sstream = context->sstream; struct gmio_ostringstream* sstream = &context->sstream;
if (value_formula == NULL || *value_formula == '\0') if (value_formula == NULL || *value_formula == '\0')
gmio_ostringstream_write_f64(sstream, value, &context->f64_format); gmio_ostringstream_write_f64(sstream, value, &context->f64_format);
else else
@ -98,7 +98,7 @@ static void gmio_amf_write_color_component(
double value, double value,
const char* value_formula) const char* value_formula)
{ {
struct gmio_ostringstream* sstream = context->sstream; struct gmio_ostringstream* sstream = &context->sstream;
if (value_formula == NULL || *value_formula == '\0') { if (value_formula == NULL || *value_formula == '\0') {
gmio_ostringstream_write_f64(sstream, value, &context->f64_format); gmio_ostringstream_write_f64(sstream, value, &context->f64_format);
} }
@ -114,7 +114,7 @@ static void gmio_amf_write_color(
struct gmio_amf_wcontext* context, struct gmio_amf_wcontext* context,
const struct gmio_amf_color* color) 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_ostringstream_write_chararray(sstream, "<color><r>");
gmio_amf_write_color_component(context, color->r, color->r_formula); gmio_amf_write_color_component(context, color->r, color->r_formula);
gmio_ostringstream_write_chararray(sstream, "</r><g>"); 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->func_get_document_element(
doc->cookie, doc->cookie,
GMIO_AMF_DOCUMENT_ELEMENT_METADATA, imeta, &metadata); 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); gmio_amf_wcontext_incr_task_progress(context);
} }
return gmio_no_error(context->error); 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) static bool gmio_amf_write_root_materials(struct gmio_amf_wcontext* context)
{ {
const struct gmio_amf_document* doc = context->document; 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}; struct gmio_amf_material material = {0};
for (uint32_t imat = 0; imat < doc->material_count; ++imat) { for (uint32_t imat = 0; imat < doc->material_count; ++imat) {
doc->func_get_document_element( 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_object_mesh_element_index* base_mesh_element_index)
{ {
const struct gmio_amf_document* doc = context->document; 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 = struct gmio_amf_object_mesh_element_index mesh_elt_index =
*base_mesh_element_index; *base_mesh_element_index;
const struct gmio_ostringstream_format_float* f64_format = 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) static bool gmio_amf_write_root_objects(struct gmio_amf_wcontext* context)
{ {
const struct gmio_amf_document* doc = context->document; 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}; struct gmio_amf_object object = {0};
for (uint32_t iobj = 0; iobj < doc->object_count; ++iobj) { for (uint32_t iobj = 0; iobj < doc->object_count; ++iobj) {
doc->func_get_document_element( 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) static bool gmio_amf_write_root_textures(struct gmio_amf_wcontext* context)
{ {
const struct gmio_amf_document* doc = context->document; 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}; struct gmio_amf_texture texture = {0};
for (uint32_t itex = 0; itex < doc->texture_count; ++itex) { for (uint32_t itex = 0; itex < doc->texture_count; ++itex) {
doc->func_get_document_element( 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) static bool gmio_amf_write_root_constellations(struct gmio_amf_wcontext* context)
{ {
const struct gmio_amf_document* doc = context->document; 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}; struct gmio_amf_constellation constellation = {0};
for (uint32_t icons = 0; icons < doc->constellation_count; ++icons) { for (uint32_t icons = 0; icons < doc->constellation_count; ++icons) {
doc->func_get_document_element( doc->func_get_document_element(
@ -814,6 +814,39 @@ static struct gmio_zip_entry_filename gmio_amf_zip_entry_filename(
return zip_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( int gmio_amf_write(
struct gmio_stream* stream, struct gmio_stream* stream,
const struct gmio_amf_document* doc, 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}; static const struct gmio_amf_write_options default_write_opts = {0};
opts = opts != NULL ? opts : &default_write_opts; opts = opts != NULL ? opts : &default_write_opts;
/* Constants */ struct gmio_amf_wcontext context = {0};
struct gmio_memblock_helper mblock_helper = struct gmio_memblock_helper mblock_helper =
gmio_memblock_helper(opts != NULL ? &opts->stream_memblock : NULL); gmio_memblock_helper(opts != NULL ? &opts->stream_memblock : NULL);
const struct gmio_memblock* memblock = &mblock_helper.memblock; 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 */ /* Check validity of input parameters */
context.error = GMIO_ERROR_OK; context.error = GMIO_ERROR_OK;
@ -847,8 +871,12 @@ int gmio_amf_write(
/* Initialize writing context */ /* Initialize writing context */
const struct gmio_string_16 f64_stdio_format = const struct gmio_string_16 f64_stdio_format =
gmio_to_stdio_float_format(opts->float64_format, opts->float64_prec); 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.options = opts;
context.sstream = &sstream;
context.document = doc; context.document = doc;
context.task_iface = &opts->task_iface; context.task_iface = &opts->task_iface;
context.task_progress_current = 0; context.task_progress_current = 0;
@ -858,10 +886,11 @@ int gmio_amf_write(
context.f64_format.text_format = opts->float64_format; context.f64_format.text_format = opts->float64_format;
context.f64_format.precision = context.f64_format.precision =
opts->float64_prec != 0 ? opts->float64_prec : 16; opts->float64_prec != 0 ? opts->float64_prec : 16;
if (opts->compress) { if (opts->compress) {
/* Initialize internal zlib stream for compression */ /* Initialize internal zlib stream for compression */
const size_t mblock_halfsize = memblock->size / 2; const size_t mblock_halfsize = memblock->size / 2;
context.sstream->strbuff.capacity = mblock_halfsize; context.sstream.strbuff.capacity = mblock_halfsize;
context.z_memblock = context.z_memblock =
gmio_memblock( gmio_memblock(
(uint8_t*)memblock->ptr + mblock_halfsize, (uint8_t*)memblock->ptr + mblock_halfsize,
@ -874,85 +903,23 @@ int gmio_amf_write(
if (gmio_error(context.error)) if (gmio_error(context.error))
goto label_end; goto label_end;
context.z_flush = Z_NO_FLUSH; 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);
} }
else {
sstream.cookie = &context; context.error = gmio_amf_write_file_data(&context, NULL);
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);
} }
label_end: label_end:

View File

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

View File

@ -39,9 +39,15 @@
#include "global.h" #include "global.h"
/*! \c GMIO_ZLIB_ERROR_TAG enum {
* Byte-mask to tag(identify) zlib-specific error codes */ /*! \c GMIO_ZLIB_ERROR_TAG
enum { GMIO_ZLIB_ERROR_TAG = 0x1000000 }; * 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 */ /*! Common errors */
enum gmio_error enum gmio_error
@ -97,7 +103,14 @@ enum gmio_error
/*! Invalid compression memory usage, see /*! Invalid compression memory usage, see
* gmio_zlib_compress_options::memory_usage */ * 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> */ /*! Returns true if <tt>code == GMIO_NO_ERROR</tt> */

View File

@ -34,8 +34,7 @@
#include <stddef.h> #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 * For faster lookups, it knowns the length of its contents. Length must not
* exceeds the maximum size(capacity). * exceeds the maximum size(capacity).
*/ */

View File

@ -41,8 +41,10 @@
enum { enum {
GMIO_ZIP_SIZE_LOCAL_FILE_HEADER = 4 + 5*2 + 3*4 + 2*2, 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_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 + 4 + 4 + 4,
GMIO_ZIP_SIZE_DATA_DESCRIPTOR = 4 + 2*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 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 bytes[GMIO_ZIP64_SIZE_DATA_DESCRIPTOR];
uint8_t* buff = bytes; uint8_t* buff = bytes;
/* 4-bytes crc-32 */ gmio_adv_encode_uint32_le(0x08074b50, &buff);
gmio_adv_encode_uint32_le(info->crc32, &buff); gmio_adv_encode_uint32_le(info->crc32, &buff);
/* Compressed size and uncompressed size (4 or 8 bytes) */ /* Compressed size and uncompressed size (4 or 8 bytes) */
if (info->use_zip64) { 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->compressed_size, &buff);
gmio_adv_encode_uint64_le(info->uncompressed_size, &buff); gmio_adv_encode_uint64_le(info->uncompressed_size, &buff);
#else #else
/* TODO: error code */ return gmio_zip_io_returnerr(
return gmio_zip_io_returnerr(0, GMIO_ERROR_UNKNOWN, ptr_error); 0, GMIO_ERROR_ZIP_INT64_TYPE_REQUIRED, ptr_error);
#endif #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 { else {
if (info->compressed_size <= UINT32_MAX return gmio_zip_io_returnerr(
&& info->uncompressed_size <= UINT32_MAX) 0, GMIO_ERROR_ZIP64_FORMAT_REQUIRED, ptr_error);
{
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);
}
} }
/* Write to stream */ /* 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); 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( size_t gmio_zip_read_end_of_central_directory_record(
struct gmio_stream *stream, struct gmio_stream *stream,
struct gmio_zip_end_of_central_directory_record *info, 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); gmio_adv_decode_uint32_le(&buff);
info->filecomment_len = gmio_adv_decode_uint16_le(&buff); info->filecomment_len = gmio_adv_decode_uint16_le(&buff);
info->filecomment = NULL; info->filecomment = NULL;
return gmio_zip_io_returnerr(read_len, GMIO_ERROR_OK, ptr_error); 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; size_t read_len = 0;
if (!gmio_zip_readcheckbytes(stream, bytes, sizeof(bytes), &read_len, ptr_error)) if (!gmio_zip_readcheckbytes(stream, bytes, sizeof(bytes), &read_len, ptr_error))
return read_len; return read_len;
if (!gmio_zip_readcheckmagic(&buff, 0x08074b50, ptr_error))
return read_len;
info->crc32 = gmio_adv_decode_uint32_le(&buff); info->crc32 = gmio_adv_decode_uint32_le(&buff);
info->compressed_size = gmio_adv_decode_uint32_le(&buff); info->compressed_size = gmio_adv_decode_uint32_le(&buff);
info->uncompressed_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; size_t read_len = 0;
if (!gmio_zip_readcheckbytes(stream, bytes, sizeof(bytes), &read_len, ptr_error)) if (!gmio_zip_readcheckbytes(stream, bytes, sizeof(bytes), &read_len, ptr_error))
return read_len; return read_len;
if (!gmio_zip_readcheckmagic(&buff, 0x08074b50, ptr_error))
return read_len;
info->crc32 = gmio_adv_decode_uint32_le(&buff); info->crc32 = gmio_adv_decode_uint32_le(&buff);
info->compressed_size = gmio_adv_decode_uint64_le(&buff); info->compressed_size = gmio_adv_decode_uint64_le(&buff);
info->uncompressed_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); return gmio_zip_io_returnerr(read_len, GMIO_ERROR_OK, ptr_error);
#else #else
/* TODO: error code */ return gmio_zip_io_returnerr(
return gmio_zip_io_returnerr(0, GMIO_ERROR_UNKNOWN, ptr_error); 0, GMIO_ERROR_ZIP_INT64_TYPE_REQUIRED, ptr_error);
#endif #endif
} }
@ -401,32 +457,86 @@ size_t gmio_zip_write_central_directory_header(
stream, written_len, expected_written_len, ptr_error); 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, uint8_t *buff,
size_t buff_capacity, size_t buff_capacity,
const struct gmio_zip64_extrablock_extended_info *info, const struct gmio_zip64_extrafield *info,
int* ptr_error) int* ptr_error)
{ {
if (buff_capacity < GMIO_ZIP64_SIZE_EXTRAFIELD_EXTENDED_INFO) { if (buff_capacity < GMIO_ZIP64_SIZE_EXTRAFIELD) {
return gmio_zip_io_returnerr( return gmio_zip_io_returnerr(
0, GMIO_ERROR_INVALID_MEMBLOCK_SIZE, ptr_error); 0, GMIO_ERROR_INVALID_MEMBLOCK_SIZE, ptr_error);
} }
#ifdef GMIO_HAVE_INT64_TYPE #ifdef GMIO_HAVE_INT64_TYPE
/* Tag */ static const uint16_t extrablock_tag = 0x0001;
gmio_adv_encode_uint16_le(0x0001, &buff); static const uint16_t extrablock_size = 3*8 + 4;
/* Size of the "extra" block */ gmio_adv_encode_uint16_le(extrablock_tag, &buff);
gmio_adv_encode_uint16_le(GMIO_ZIP64_SIZE_EXTRAFIELD_EXTENDED_INFO - 2, &buff); gmio_adv_encode_uint16_le(extrablock_size, &buff);
gmio_adv_encode_uint64_le(info->uncompressed_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->compressed_size, &buff);
gmio_adv_encode_uint64_le(info->relative_offset_local_header, &buff); gmio_adv_encode_uint64_le(info->relative_offset_local_header, &buff);
gmio_adv_encode_uint32_le(info->disk_nb_start, &buff); gmio_adv_encode_uint32_le(info->disk_nb_start, &buff);
return gmio_zip_io_returnerr( return gmio_zip_io_returnerr(
GMIO_ZIP64_SIZE_EXTRAFIELD_EXTENDED_INFO, GMIO_ZIP64_SIZE_EXTRAFIELD, GMIO_ERROR_OK, ptr_error);
GMIO_ERROR_OK,
ptr_error);
#else #else
/* TODO: error code */ return gmio_zip_io_returnerr(
return gmio_zip_io_returnerr(0, GMIO_ERROR_UNKNOWN, error); 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 #endif
} }
@ -493,3 +603,140 @@ size_t gmio_zip_write_end_of_central_directory_record(
return gmio_zip_write_returnhelper( return gmio_zip_write_returnhelper(
stream, written_len, expected_written_len, ptr_error); 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 { 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) */ /*! Zip64 extended info (extra field) */
struct gmio_zip64_extrablock_extended_info { struct gmio_zip64_extrafield {
uintmax_t compressed_size; uintmax_t compressed_size;
uintmax_t uncompressed_size; uintmax_t uncompressed_size;
uintmax_t relative_offset_local_header; uintmax_t relative_offset_local_header;
@ -229,7 +229,6 @@ struct gmio_zip_end_of_central_directory_record {
/*! Zip64 end of central directory record */ /*! Zip64 end of central directory record */
struct gmio_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; uint16_t version_made_by;
enum gmio_zip_feature_version version_needed_to_extract; enum gmio_zip_feature_version version_needed_to_extract;
uint32_t disk_nb; 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 */ 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 { struct gmio_zip_write_result {
int error; int error;
size_t written_len; size_t written_len;
@ -275,6 +281,18 @@ size_t gmio_zip_read_central_directory_header(
struct gmio_zip_central_directory_header* info, struct gmio_zip_central_directory_header* info,
int* ptr_error); 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 */ /*! Reads ZIP end of central directory record from \p stream */
size_t gmio_zip_read_end_of_central_directory_record( size_t gmio_zip_read_end_of_central_directory_record(
struct gmio_stream* stream, struct gmio_stream* stream,
@ -299,15 +317,25 @@ size_t gmio_zip_write_central_directory_header(
const struct gmio_zip_central_directory_header* info, const struct gmio_zip_central_directory_header* info,
int* ptr_error); 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 * Returns \c GMIO_ERROR_INVALID_MEMBLOCK_SIZE if \p buff_capacity is less than
* \c GMIO_ZIP64_SIZE_EXTRAFIELD_EXTENDED_INFO * \c GMIO_ZIP64_SIZE_EXTRAFIELD */
*/ size_t gmio_zip64_write_extrafield(
size_t gmio_zip64_write_extrafield_extended_info(
uint8_t* buff, uint8_t* buff,
size_t buff_capacity, 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); int* ptr_error);
/*! Writes ZIP end of central directory record to \p stream */ /*! 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, const struct gmio_zip_end_of_central_directory_record* info,
int* ptr_error); 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 */ #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); gmio_memblock_set_default_constructor(gmio_memblock_for_tests);
UTEST_RUN(test_amf_write_doc_null); 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; return NULL;
} }

View File

@ -67,6 +67,7 @@ struct __tamf__mesh
struct __tamf__document struct __tamf__document
{ {
const struct __tamf__material* vec_material; const struct __tamf__material* vec_material;
uint32_t material_count;
struct __tamf__mesh mesh; 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() static const char* test_amf_write_doc_null()
{ {
struct gmio_stream stream = {0}; struct gmio_stream stream = {0};
@ -188,205 +184,382 @@ static const char* test_amf_write_doc_null()
return 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; static const size_t wbuffsize = 8192;
struct gmio_rw_buffer wbuff = {0}; 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.ptr = calloc(wbuffsize, 1);
wbuff.len = wbuffsize; wbuff.len = wbuffsize;
testdoc.vec_material = testmaterials; const struct __tamf__document testdoc = __tamf__create_doc_1();
testdoc.mesh.vec_vertex = testvertices; const struct gmio_amf_document doc = __tamf_create_doc(&testdoc);
testdoc.mesh.vertex_count = GMIO_ARRAY_SIZE(testvertices); struct gmio_amf_write_options options = {0};
testdoc.mesh.vec_triangle = testtriangles; options.float64_prec = 9;
testdoc.mesh.triangle_count = GMIO_ARRAY_SIZE(testtriangles); const int error = __tamf__write_amf(&wbuff, &doc, &options);
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);
#if 0 #if 0
FILE* file = fopen("output.zip", "wb"); if (gmio_error(error))
fwrite(wbuff.ptr, 1, wbuff.pos, file); printf("\n0x%x\n", error);
fclose(file);
#endif #endif
} UTEST_COMPARE_INT(error, GMIO_ERROR_OK);
/* printf("%s\n", wbuff.ptr); */
/* Unzip and compare with source data */ free(wbuff.ptr);
{ return NULL;
/* Total size in bytes of the ZIP archive */ }
const uintmax_t zip_archive_len = wbuff.pos;
wbuff.pos = 0; static const char zip_entry_filename[] = "test.amf";
struct gmio_stream stream = gmio_stream_buffer(&wbuff); static const uint16_t zip_entry_filename_len = sizeof(zip_entry_filename) - 1;
int error = GMIO_ERROR_OK;
/* -- Read ZIP local file header */ static const char* __tamf__check_zip_local_file_header(
struct gmio_zip_local_file_header zip_lfh = {0}; const struct gmio_zip_local_file_header* zip_lfh)
const size_t lfh_read_len = {
gmio_zip_read_local_file_header(&stream, &zip_lfh, &error); UTEST_COMPARE_UINT(
UTEST_COMPARE_INT(error, GMIO_ERROR_OK); zip_lfh->compress_method,
UTEST_COMPARE_UINT( GMIO_ZIP_COMPRESS_METHOD_DEFLATE);
zip_lfh.compress_method, UTEST_ASSERT(
GMIO_ZIP_COMPRESS_METHOD_DEFLATE); (zip_lfh->general_purpose_flags &
UTEST_ASSERT( GMIO_ZIP_GENERAL_PURPOSE_FLAG_USE_DATA_DESCRIPTOR)
(zip_lfh.general_purpose_flags & != 0);
GMIO_ZIP_GENERAL_PURPOSE_FLAG_USE_DATA_DESCRIPTOR) UTEST_COMPARE_UINT(
!= 0); zip_lfh->filename_len,
UTEST_COMPARE_UINT( zip_entry_filename_len);
zip_lfh.filename_len, UTEST_ASSERT(strncmp(
sizeof(zip_entry_filename) - 1); zip_lfh->filename,
UTEST_ASSERT(strncmp( zip_entry_filename,
(const char*)wbuff.ptr + wbuff.pos, zip_entry_filename_len)
zip_entry_filename, == 0);
zip_entry_filename_len) return NULL;
== 0); }
/* -- Read ZIP end of central directory record */
static const size_t end_of_central_dir_record_len = 22; static const char* test_amf_write_doc_1_zip()
wbuff.pos = zip_archive_len - end_of_central_dir_record_len;; {
struct gmio_zip_end_of_central_directory_record zip_eocdr = {0}; static const size_t wbuffsize = 8192;
gmio_zip_read_end_of_central_directory_record( struct gmio_rw_buffer wbuff = {0};
&stream, &zip_eocdr, &error); wbuff.ptr = calloc(wbuffsize, 1);
UTEST_COMPARE_INT(error, GMIO_ERROR_OK); wbuff.len = wbuffsize;
UTEST_COMPARE_UINT(zip_eocdr.disk_nb, 0);
UTEST_COMPARE_UINT( const struct __tamf__document testdoc = __tamf__create_doc_1();
zip_eocdr.disk_nb_with_start_of_central_dir, 0); const struct gmio_amf_document doc = __tamf_create_doc(&testdoc);
UTEST_COMPARE_UINT( { /* Write uncompressed */
zip_eocdr.total_entry_count_in_central_dir_on_disk, 1); struct gmio_amf_write_options options = {0};
UTEST_COMPARE_UINT( options.float64_prec = 9;
zip_eocdr.total_entry_count_in_central_dir, 1); const int error = __tamf__write_amf(&wbuff, &doc, &options);
/* -- Read ZIP central directory */ UTEST_COMPARE_INT(error, GMIO_ERROR_OK);
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); const size_t source_len = wbuff.pos;
UTEST_COMPARE_INT(error, GMIO_ERROR_OK); const uint32_t crc32_amf_data = gmio_zlib_crc32(wbuff.ptr, source_len);
UTEST_COMPARE_UINT( uint8_t* source = calloc(source_len, 1);
zip_cdh.compress_method, memcpy(source, wbuff.ptr, source_len);
GMIO_ZIP_COMPRESS_METHOD_DEFLATE);
UTEST_ASSERT( { /* Write compressed(ZIP) */
(zip_cdh.general_purpose_flags & wbuff.pos = 0;
GMIO_ZIP_GENERAL_PURPOSE_FLAG_USE_DATA_DESCRIPTOR) struct gmio_amf_write_options options = {0};
!= 0); options.float64_prec = 9;
UTEST_COMPARE_UINT(crc32_amf_data, zip_cdh.crc32); options.compress = true;
UTEST_COMPARE_UINT( options.zip_entry_filename = zip_entry_filename;
zip_cdh.filename_len, zip_entry_filename_len); options.zip_entry_filename_len = zip_entry_filename_len;
UTEST_ASSERT(strncmp( const int error = __tamf__write_amf(&wbuff, &doc, &options);
(const char*)wbuff.ptr + wbuff.pos, UTEST_COMPARE_INT(error, GMIO_ERROR_OK);
zip_entry_filename, #if 1
zip_entry_filename_len) FILE* file = fopen("output.zip", "wb");
== 0); fwrite(wbuff.ptr, 1, wbuff.pos, file);
/* -- Read compressed AMF data */ fclose(file);
const size_t pos_start_amf_data = #endif
lfh_read_len + zip_lfh.filename_len + zip_lfh.extrafield_len; }
wbuff.pos = pos_start_amf_data;
{ /* Unzip and compare with source data */
uint8_t* dest = calloc(zip_cdh.uncompressed_size, 1); {
size_t dest_len = zip_cdh.uncompressed_size; /* Total size in bytes of the ZIP archive */
const int error = const uintmax_t zip_archive_len = wbuff.pos;
gmio_zlib_uncompress_buffer( wbuff.pos = 0;
dest, struct gmio_stream stream = gmio_stream_buffer(&wbuff);
&dest_len, int error = GMIO_ERROR_OK;
(const uint8_t*)wbuff.ptr + wbuff.pos, /* -- Read ZIP local file header */
zip_cdh.compressed_size); struct gmio_zip_local_file_header zip_lfh = {0};
printf("\n-- Info: z_len=%i src_len=%i\n", const size_t lfh_read_len =
zip_cdh.compressed_size, source_len); gmio_zip_read_local_file_header(&stream, &zip_lfh, &error);
UTEST_COMPARE_INT(error, GMIO_ERROR_OK); UTEST_COMPARE_INT(error, GMIO_ERROR_OK);
UTEST_COMPARE_UINT(source_len, dest_len); zip_lfh.filename = (const char*)wbuff.ptr + wbuff.pos;
UTEST_COMPARE_UINT(dest_len, zip_cdh.uncompressed_size); const char* check_str = __tamf__check_zip_local_file_header(&zip_lfh);
UTEST_COMPARE_INT(memcmp(dest, source, source_len), 0); if (check_str != NULL)
const uint32_t crc32_uncomp = gmio_zlib_crc32(dest, dest_len); return check_str;
UTEST_COMPARE_UINT(crc32_amf_data, crc32_uncomp); /* -- Read ZIP end of central directory record */
free(dest); static const size_t end_of_central_dir_record_len = 22;
} wbuff.pos = zip_archive_len - end_of_central_dir_record_len;;
/* -- Read ZIP data descriptor */ struct gmio_zip_end_of_central_directory_record zip_eocdr = {0};
wbuff.pos = pos_start_amf_data + zip_cdh.compressed_size; gmio_zip_read_end_of_central_directory_record(
struct gmio_zip_data_descriptor zip_dd = {0}; &stream, &zip_eocdr, &error);
gmio_zip_read_data_descriptor(&stream, &zip_dd, &error); UTEST_COMPARE_INT(error, GMIO_ERROR_OK);
UTEST_COMPARE_INT(error, GMIO_ERROR_OK); UTEST_COMPARE_UINT(zip_eocdr.disk_nb, 0);
UTEST_COMPARE_UINT(zip_dd.crc32, crc32_amf_data); UTEST_COMPARE_UINT(
UTEST_COMPARE_UINT( zip_eocdr.disk_nb_with_start_of_central_dir, 0);
zip_dd.compressed_size, zip_cdh.compressed_size); UTEST_COMPARE_UINT(
UTEST_COMPARE_UINT( zip_eocdr.total_entry_count_in_central_dir_on_disk, 1);
zip_dd.uncompressed_size, zip_cdh.uncompressed_size); UTEST_COMPARE_UINT(
} zip_eocdr.total_entry_count_in_central_dir, 1);
/* -- Read ZIP central directory */
free(source); 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); free(wbuff.ptr);
return NULL; return NULL;
} }