3MF: add export to a given archive.

pull/1598/head
Kim Kulling 2017-11-27 21:48:33 +01:00
parent 6c59c83e0f
commit 3dfca3bc84
15 changed files with 6152 additions and 60 deletions

View File

@ -743,6 +743,14 @@ SET( unzip_SRCS
)
SOURCE_GROUP( unzip FILES ${unzip_SRCS})
SET( ziplib_SRCS
../contrib/zip/src/miniz.h
../contrib/zip/src/zip.c
../contrib/zip/src/zip.h
)
SOURCE_GROUP( ziplib FILES ${ziplib_SRCS} )
SET ( openddl_parser_SRCS
../contrib/openddlparser/code/OpenDDLParser.cpp
../contrib/openddlparser/code/DDLNode.cpp
@ -854,6 +862,7 @@ SET( assimp_src
${Clipper_SRCS}
${openddl_parser_SRCS}
${open3dgc_SRCS}
${ziplib_SRCS}
# Necessary to show the headers in the project when using the VC++ generator:
${PUBLIC_HEADERS}

View File

@ -50,12 +50,17 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "3MFXmlTags.h"
#include "D3MFOpcPackage.h"
#include <contrib/zip/src/zip.h>
namespace Assimp {
void ExportScene3MF( const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/ ) {
D3MF::D3MFExporter myExporter( pFile, pIOSystem, pScene );
if ( myExporter.validate() ) {
bool ok = myExporter.exportArchive(pFile);
if ( !ok ) {
throw DeadlyExportError( "Could not export 3MP archive: " + std::string( pFile ) );
}
}
}
@ -66,6 +71,7 @@ namespace D3MF {
D3MFExporter::D3MFExporter( const char* pFile, IOSystem* pIOSystem, const aiScene* pScene )
: mIOSystem( pIOSystem )
, mArchiveName( pFile )
, m_zipArchive( nullptr )
, mScene( pScene )
, mBuildItems()
, mRelations() {
@ -79,26 +85,6 @@ D3MFExporter::~D3MFExporter() {
mRelations.clear();
}
bool D3MFExporter::createFileStructure( const char *file ) {
if ( !mIOSystem->CreateDirectory( file ) ) {
return false;
}
if ( !mIOSystem->ChangeDirectory( file ) ) {
return false;
}
if ( !mIOSystem->CreateDirectory( "3D" ) ) {
return false;
}
if ( !mIOSystem->CreateDirectory( "_rels" ) ) {
return false;
}
return true;
}
bool D3MFExporter::validate() {
if ( mArchiveName.empty() ) {
return false;
@ -113,13 +99,14 @@ bool D3MFExporter::validate() {
bool D3MFExporter::exportArchive( const char *file ) {
bool ok( true );
ok |= createFileStructure( file );
m_zipArchive = zip_open( file, ZIP_DEFAULT_COMPRESSION_LEVEL, 'w' );
if ( nullptr == m_zipArchive ) {
return false;
}
ok |= exportRelations();
ok |= export3DModel();
if ( ok ) {
createZipArchiveFromeFileStructure();
}
return ok;
}
@ -247,45 +234,21 @@ void D3MFExporter::writeBuild() {
}
void D3MFExporter::writeModelToArchive( const std::string &folder, const std::string &modelName ) {
const std::string &oldFolder( mIOSystem->CurrentDirectory() );
if ( folder != oldFolder ) {
mIOSystem->PushDirectory( oldFolder );
mIOSystem->ChangeDirectory( folder );
const std::string entry = folder + "/" + mArchiveName;
zip_entry_open( m_zipArchive, entry.c_str() );
const std::string &exportTxt( mOutput.str() );
std::shared_ptr<IOStream> outfile( mIOSystem->Open( modelName, "wb" ) );
if ( !outfile ) {
throw DeadlyExportError( "Could not open output model file: " + std::string( modelName ) );
}
zip_entry_write( m_zipArchive, exportTxt.c_str(), exportTxt.size() );
const size_t writtenBytes( outfile->Write( exportTxt.c_str(), sizeof( char ), exportTxt.size() ) );
mIOSystem->ChangeDirectory( ".." );
mIOSystem->PopDirectory();
}
zip_entry_close( m_zipArchive );
}
void D3MFExporter::writeRelInfoToFile( const std::string &folder, const std::string &relName ) {
const std::string &oldFolder( mIOSystem->CurrentDirectory() );
if ( folder != oldFolder ) {
mIOSystem->PushDirectory( oldFolder );
mIOSystem->ChangeDirectory( folder );
const std::string entry = folder + "/" + "_rels";
zip_entry_open( m_zipArchive, entry.c_str() );
const std::string &exportTxt( mOutput.str() );
std::shared_ptr<IOStream> outfile( mIOSystem->Open( relName, "wb" ) );
if ( !outfile ) {
throw DeadlyExportError( "Could not open output model file: " + std::string( relName ) );
}
const size_t writtenBytes( outfile->Write( exportTxt.c_str(), sizeof( char ), exportTxt.size() ) );
mIOSystem->ChangeDirectory( ".." );
mIOSystem->PopDirectory();
}
}
void D3MFExporter::createZipArchiveFromeFileStructure() {
zip_entry_write( m_zipArchive, exportTxt.c_str(), exportTxt.size() );
zip_entry_close( m_zipArchive );
}
#endif // ASSIMP_BUILD_NO3MF_EXPORTER

View File

@ -50,6 +50,8 @@ struct aiNode;
struct aiMaterial;
struct aiMesh;
struct zip_t;
namespace Assimp {
class IOStream;
@ -66,7 +68,6 @@ public:
D3MFExporter( const char* pFile, IOSystem* pIOSystem, const aiScene* pScene );
~D3MFExporter();
bool validate();
bool createFileStructure( const char *file );
bool exportArchive( const char *file );
bool exportRelations();
bool export3DModel();
@ -80,11 +81,12 @@ protected:
void writeBuild();
void writeModelToArchive( const std::string &folder, const std::string &modelName );
void writeRelInfoToFile( const std::string &folder, const std::string &relName );
void createZipArchiveFromeFileStructure();
void createZipArchiveFromeFileStructure( const char* pFile );
private:
IOSystem *mIOSystem;
std::string mArchiveName;
zip_t *m_zipArchive;
const aiScene *mScene;
std::ostringstream mOutput;
std::vector<unsigned int> mBuildItems;

38
contrib/zip/.gitignore vendored 100644
View File

@ -0,0 +1,38 @@
/build/
/test/build/
/xcodeproj/
# Object files
*.o
*.ko
*.obj
*.elf
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.a
*.la
*.lo
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
*.suo
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
# Temporary
*.swp
.DS_Store

View File

@ -0,0 +1,10 @@
language: c
# Compiler selection
compiler:
- clang
- gcc
# Build steps
script:
- mkdir build
- cd build
- cmake -DCMAKE_BUILD_TYPE=Debug .. && make && make test

View File

@ -0,0 +1,18 @@
cmake_minimum_required(VERSION 2.8)
project(zip)
if (MSVC)
# Use secure functions by defaualt and suppress warnings about "deprecated" functions
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_COUNT=1")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_NONSTDC_NO_WARNINGS=1 /D _CRT_SECURE_NO_WARNINGS=1")
endif (MSVC)
# zip
set(SRC src/miniz.h src/zip.h src/zip.c)
add_library(${CMAKE_PROJECT_NAME} ${SRC})
# test
enable_testing()
add_subdirectory(test)

View File

@ -0,0 +1,139 @@
### A portable (OSX/Linux/Windows), simple zip library written in C
This is done by hacking awesome [miniz](https://code.google.com/p/miniz) library and layering functions on top of the miniz v1.15 API.
[![Windows][win-badge]][win-link] [![OS X][osx-linux-badge]][osx-linux-link]
[win-badge]: https://img.shields.io/appveyor/ci/kuba--/zip/master.svg?label=windows "AppVeyor build status"
[win-link]: https://ci.appveyor.com/project/kuba--/zip "AppVeyor build status"
[osx-linux-badge]: https://img.shields.io/travis/kuba--/zip/master.svg?label=linux/osx "Travis CI build status"
[osx-linux-link]: https://travis-ci.org/kuba--/zip "Travis CI build status"
# The Idea
<img src="zip.png" name="zip" />
... Some day, I was looking for zip library written in C for my project, but I could not find anything simple enough and lightweight.
Everything what I tried required 'crazy mental gymnastics' to integrate or had some limitations or was too heavy.
I hate frameworks, factories and adding new dependencies. If I must to install all those dependencies and link new library, I'm getting almost sick.
I wanted something powerfull and small enough, so I could add just a few files and compile them into my project.
And finally I found miniz.
Miniz is a lossless, high performance data compression library in a single source file. I only needed simple interface to append buffers or files to the current zip-entry. Thanks to this feature I'm able to merge many files/buffers and compress them on-the-fly.
It was the reason, why I decided to write zip module on top of the miniz. It required a little bit hacking and wrapping some functions, but I kept simplicity. So, you can grab these 3 files and compile them into your project. I hope that interface is also extremely simple, so you will not have any problems to understand it.
# Examples
* Create a new zip archive with default compression level.
```c
struct zip_t *zip = zip_open("foo.zip", ZIP_DEFAULT_COMPRESSION_LEVEL, 'w');
{
zip_entry_open(zip, "foo-1.txt");
{
char *buf = "Some data here...";
zip_entry_write(zip, buf, strlen(buf));
}
zip_entry_close(zip);
zip_entry_open(zip, "foo-2.txt");
{
// merge 3 files into one entry and compress them on-the-fly.
zip_entry_fwrite(zip, "foo-2.1.txt");
zip_entry_fwrite(zip, "foo-2.2.txt");
zip_entry_fwrite(zip, "foo-2.3.txt");
}
zip_entry_close(zip);
}
zip_close(zip);
```
* Append to the existing zip archive.
```c
struct zip_t *zip = zip_open("foo.zip", ZIP_DEFAULT_COMPRESSION_LEVEL, 'a');
{
zip_entry_open(zip, "foo-3.txt");
{
char *buf = "Append some data here...";
zip_entry_write(zip, buf, strlen(buf));
}
zip_entry_close(zip);
}
zip_close(zip);
```
* Extract a zip archive into a folder.
```c
int on_extract_entry(const char *filename, void *arg) {
static int i = 0;
int n = *(int *)arg;
printf("Extracted: %s (%d of %d)\n", filename, ++i, n);
return 0;
}
int arg = 2;
zip_extract("foo.zip", "/tmp", on_extract_entry, &arg);
```
* Extract a zip entry into memory.
```c
void *buf = NULL;
size_t bufsize;
struct zip_t *zip = zip_open("foo.zip", 0, 'r');
{
zip_entry_open(zip, "foo-1.txt");
{
zip_entry_read(zip, &buf, &bufsize);
}
zip_entry_close(zip);
}
zip_close(zip);
free(buf);
```
* Extract a zip entry into memory using callback.
```c
struct buffer_t {
char *data;
size_t size;
};
static size_t on_extract(void *arg, unsigned long long offset, const void *data, size_t size) {
struct buffer_t *buf = (struct buffer_t *)arg;
buf->data = realloc(buf->data, buf->size + size + 1);
assert(NULL != buf->data);
memcpy(&(buf->data[buf->size]), data, size);
buf->size += size;
buf->data[buf->size] = 0;
return size;
}
struct buffer_t buf = {0};
struct zip_t *zip = zip_open("foo.zip", 0, 'r');
{
zip_entry_open(zip, "foo-1.txt");
{
zip_entry_extract(zip, on_extract, &buf);
}
zip_entry_close(zip);
}
zip_close(zip);
free(buf.data);
```
* Extract a zip entry into a file.
```c
struct zip_t *zip = zip_open("foo.zip", 0, 'r');
{
zip_entry_open(zip, "foo-2.txt");
{
zip_entry_fread(zip, "foo-2.txt");
}
zip_entry_close(zip);
}
zip_close(zip);
```

View File

@ -0,0 +1,26 @@
/*
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>
*/

View File

@ -0,0 +1,14 @@
version: 1.0.{build}
build_script:
- cmd: >-
cd c:\projects\zip
mkdir build
cd build
cmake -G"Visual Studio 14" -DCMAKE_BUILD_TYPE=Debug ..
cmake --build . --config %CMAKE_BUILD_TYPE%
ctest --verbose -C "Debug"

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,640 @@
/*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include "zip.h"
#include "miniz.h"
#include <errno.h>
#include <sys/stat.h>
#include <time.h>
#if defined _WIN32 || defined __WIN32__
/* Win32, DOS */
#include <direct.h>
#define MKDIR(DIRNAME) _mkdir(DIRNAME)
#define STRCLONE(STR) ((STR) ? _strdup(STR) : NULL)
#define HAS_DEVICE(P) \
((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) && \
(P)[1] == ':')
#define FILESYSTEM_PREFIX_LEN(P) (HAS_DEVICE(P) ? 2 : 0)
#define ISSLASH(C) ((C) == '/' || (C) == '\\')
#else
#define MKDIR(DIRNAME) mkdir(DIRNAME, 0755)
#define STRCLONE(STR) ((STR) ? strdup(STR) : NULL)
#endif
#ifndef FILESYSTEM_PREFIX_LEN
#define FILESYSTEM_PREFIX_LEN(P) 0
#endif
#ifndef ISSLASH
#define ISSLASH(C) ((C) == '/')
#endif
#define CLEANUP(ptr) \
do { \
if (ptr) { \
free((void *)ptr); \
ptr = NULL; \
} \
} while (0)
static char *basename(const char *name) {
char const *p;
char const *base = name += FILESYSTEM_PREFIX_LEN(name);
int all_slashes = 1;
for (p = name; *p; p++) {
if (ISSLASH(*p))
base = p + 1;
else
all_slashes = 0;
}
/* If NAME is all slashes, arrange to return `/'. */
if (*base == '\0' && ISSLASH(*name) && all_slashes) --base;
return (char *)base;
}
static int mkpath(const char *path) {
char const *p;
char npath[MAX_PATH + 1] = {0};
int len = 0;
for (p = path; *p && len < MAX_PATH; p++) {
if (ISSLASH(*p) && len > 0) {
if (MKDIR(npath) == -1)
if (errno != EEXIST) return -1;
}
npath[len++] = *p;
}
return 0;
}
static char *strrpl(const char *str, char oldchar, char newchar) {
char *rpl = (char *)malloc(sizeof(char) * (1 + strlen(str)));
char *begin = rpl;
char c;
while((c = *str++)) {
if (c == oldchar) {
c = newchar;
}
*rpl++ = c;
}
*rpl = '\0';
return begin;
}
struct zip_entry_t {
int index;
const char *name;
mz_uint64 uncomp_size;
mz_uint64 comp_size;
mz_uint32 uncomp_crc32;
mz_uint64 offset;
mz_uint8 header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE];
mz_uint64 header_offset;
mz_uint16 method;
mz_zip_writer_add_state state;
tdefl_compressor comp;
};
struct zip_t {
mz_zip_archive archive;
mz_uint level;
struct zip_entry_t entry;
char mode;
};
struct zip_t *zip_open(const char *zipname, int level, char mode) {
struct zip_t *zip = NULL;
if (!zipname || strlen(zipname) < 1) {
// zip_t archive name is empty or NULL
goto cleanup;
}
if (level < 0) level = MZ_DEFAULT_LEVEL;
if ((level & 0xF) > MZ_UBER_COMPRESSION) {
// Wrong compression level
goto cleanup;
}
zip = (struct zip_t *)calloc((size_t)1, sizeof(struct zip_t));
if (!zip) goto cleanup;
zip->level = level;
zip->mode = mode;
switch (mode) {
case 'w':
// Create a new archive.
if (!mz_zip_writer_init_file(&(zip->archive), zipname, 0)) {
// Cannot initialize zip_archive writer
goto cleanup;
}
break;
case 'r':
case 'a':
if (!mz_zip_reader_init_file(
&(zip->archive), zipname,
level | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) {
// An archive file does not exist or cannot initialize
// zip_archive reader
goto cleanup;
}
if (mode == 'a' &&
!mz_zip_writer_init_from_reader(&(zip->archive), zipname)) {
mz_zip_reader_end(&(zip->archive));
goto cleanup;
}
break;
default:
goto cleanup;
}
return zip;
cleanup:
CLEANUP(zip);
return NULL;
}
void zip_close(struct zip_t *zip) {
if (zip) {
// Always finalize, even if adding failed for some reason, so we have a
// valid central directory.
mz_zip_writer_finalize_archive(&(zip->archive));
mz_zip_writer_end(&(zip->archive));
mz_zip_reader_end(&(zip->archive));
CLEANUP(zip);
}
}
int zip_entry_open(struct zip_t *zip, const char *entryname) {
char *locname = NULL;
size_t entrylen = 0;
mz_zip_archive *pzip = NULL;
mz_uint num_alignment_padding_bytes, level;
if (!zip || !entryname) {
return -1;
}
entrylen = strlen(entryname);
if (entrylen < 1) {
return -1;
}
pzip = &(zip->archive);
/*
.ZIP File Format Specification Version: 6.3.3
4.4.17.1 The name of the file, with optional relative path.
The path stored MUST not contain a drive or
device letter, or a leading slash. All slashes
MUST be forward slashes '/' as opposed to
backwards slashes '\' for compatibility with Amiga
and UNIX file systems etc. If input came from standard
input, there is no file name field.
*/
locname = strrpl(entryname, '\\', '/');
if (zip->mode == 'r') {
zip->entry.index = mz_zip_reader_locate_file(pzip, locname, NULL, 0);
CLEANUP(locname);
return (zip->entry.index < 0) ? -1 : 0;
}
zip->entry.index = zip->archive.m_total_files;
zip->entry.name = locname;
if (!zip->entry.name) {
// Cannot parse zip entry name
return -1;
}
zip->entry.comp_size = 0;
zip->entry.uncomp_size = 0;
zip->entry.uncomp_crc32 = MZ_CRC32_INIT;
zip->entry.offset = zip->archive.m_archive_size;
zip->entry.header_offset = zip->archive.m_archive_size;
memset(zip->entry.header, 0,
MZ_ZIP_LOCAL_DIR_HEADER_SIZE * sizeof(mz_uint8));
zip->entry.method = 0;
num_alignment_padding_bytes =
mz_zip_writer_compute_padding_needed_for_file_alignment(pzip);
if (!pzip->m_pState || (pzip->m_zip_mode != MZ_ZIP_MODE_WRITING)) {
// Wrong zip mode
return -1;
}
if (zip->level & MZ_ZIP_FLAG_COMPRESSED_DATA) {
// Wrong zip compression level
return -1;
}
// no zip64 support yet
if ((pzip->m_total_files == 0xFFFF) ||
((pzip->m_archive_size + num_alignment_padding_bytes +
MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE +
entrylen) > 0xFFFFFFFF)) {
// No zip64 support yet
return -1;
}
if (!mz_zip_writer_write_zeros(
pzip, zip->entry.offset,
num_alignment_padding_bytes + sizeof(zip->entry.header))) {
// Cannot memset zip entry header
return -1;
}
zip->entry.header_offset += num_alignment_padding_bytes;
if (pzip->m_file_offset_alignment) {
MZ_ASSERT((zip->entry.header_offset &
(pzip->m_file_offset_alignment - 1)) == 0);
}
zip->entry.offset +=
num_alignment_padding_bytes + sizeof(zip->entry.header);
if (pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.offset, zip->entry.name,
entrylen) != entrylen) {
// Cannot write data to zip entry
return -1;
}
zip->entry.offset += entrylen;
level = zip->level & 0xF;
if (level) {
zip->entry.state.m_pZip = pzip;
zip->entry.state.m_cur_archive_file_ofs = zip->entry.offset;
zip->entry.state.m_comp_size = 0;
if (tdefl_init(&(zip->entry.comp), mz_zip_writer_add_put_buf_callback,
&(zip->entry.state),
tdefl_create_comp_flags_from_zip_params(
level, -15, MZ_DEFAULT_STRATEGY)) !=
TDEFL_STATUS_OKAY) {
// Cannot initialize the zip compressor
return -1;
}
}
return 0;
}
int zip_entry_close(struct zip_t *zip) {
mz_zip_archive *pzip = NULL;
mz_uint level;
tdefl_status done;
mz_uint16 entrylen;
time_t t;
struct tm *tm;
mz_uint16 dos_time, dos_date;
int status = -1;
if (!zip) {
// zip_t handler is not initialized
return -1;
}
if (zip->mode == 'r') {
return 0;
}
pzip = &(zip->archive);
level = zip->level & 0xF;
if (level) {
done = tdefl_compress_buffer(&(zip->entry.comp), "", 0, TDEFL_FINISH);
if (done != TDEFL_STATUS_DONE && done != TDEFL_STATUS_OKAY) {
// Cannot flush compressed buffer
goto cleanup;
}
zip->entry.comp_size = zip->entry.state.m_comp_size;
zip->entry.offset = zip->entry.state.m_cur_archive_file_ofs;
zip->entry.method = MZ_DEFLATED;
}
entrylen = (mz_uint16)strlen(zip->entry.name);
t = time(NULL);
tm = localtime(&t);
dos_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) +
((tm->tm_sec) >> 1));
dos_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) +
((tm->tm_mon + 1) << 5) + tm->tm_mday);
// no zip64 support yet
if ((zip->entry.comp_size > 0xFFFFFFFF) ||
(zip->entry.offset > 0xFFFFFFFF)) {
// No zip64 support, yet
goto cleanup;
}
if (!mz_zip_writer_create_local_dir_header(
pzip, zip->entry.header, entrylen, 0, zip->entry.uncomp_size,
zip->entry.comp_size, zip->entry.uncomp_crc32, zip->entry.method, 0,
dos_time, dos_date)) {
// Cannot create zip entry header
goto cleanup;
}
if (pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.header_offset,
zip->entry.header, sizeof(zip->entry.header)) !=
sizeof(zip->entry.header)) {
// Cannot write zip entry header
goto cleanup;
}
if (!mz_zip_writer_add_to_central_dir(
pzip, zip->entry.name, entrylen, NULL, 0, "", 0,
zip->entry.uncomp_size, zip->entry.comp_size,
zip->entry.uncomp_crc32, zip->entry.method, 0, dos_time, dos_date,
zip->entry.header_offset, 0)) {
// Cannot write to zip central dir
goto cleanup;
}
pzip->m_total_files++;
pzip->m_archive_size = zip->entry.offset;
status = 0;
cleanup:
CLEANUP(zip->entry.name);
return status;
}
int zip_entry_write(struct zip_t *zip, const void *buf, size_t bufsize) {
mz_uint level;
mz_zip_archive *pzip = NULL;
tdefl_status status;
if (!zip) {
// zip_t handler is not initialized
return -1;
}
pzip = &(zip->archive);
if (buf && bufsize > 0) {
zip->entry.uncomp_size += bufsize;
zip->entry.uncomp_crc32 = (mz_uint32)mz_crc32(
zip->entry.uncomp_crc32, (const mz_uint8 *)buf, bufsize);
level = zip->level & 0xF;
if (!level) {
if ((pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.offset, buf,
bufsize) != bufsize)) {
// Cannot write buffer
return -1;
}
zip->entry.offset += bufsize;
zip->entry.comp_size += bufsize;
} else {
status = tdefl_compress_buffer(&(zip->entry.comp), buf, bufsize,
TDEFL_NO_FLUSH);
if (status != TDEFL_STATUS_DONE && status != TDEFL_STATUS_OKAY) {
// Cannot compress buffer
return -1;
}
}
}
return 0;
}
int zip_entry_fwrite(struct zip_t *zip, const char *filename) {
int status = 0;
size_t n = 0;
FILE *stream = NULL;
mz_uint8 buf[MZ_ZIP_MAX_IO_BUF_SIZE] = {0};
if (!zip) {
// zip_t handler is not initialized
return -1;
}
stream = fopen(filename, "rb");
if (!stream) {
// Cannot open filename
return -1;
}
while ((n = fread(buf, sizeof(mz_uint8), MZ_ZIP_MAX_IO_BUF_SIZE, stream)) >
0) {
if (zip_entry_write(zip, buf, n) < 0) {
status = -1;
break;
}
}
fclose(stream);
return status;
}
int zip_entry_read(struct zip_t *zip, void **buf, size_t *bufsize) {
mz_zip_archive *pzip = NULL;
mz_uint idx;
if (!zip) {
// zip_t handler is not initialized
return -1;
}
if (zip->mode != 'r' || zip->entry.index < 0) {
// the entry is not found or we do not have read access
return -1;
}
pzip = &(zip->archive);
idx = (mz_uint)zip->entry.index;
if (mz_zip_reader_is_file_a_directory(pzip, idx)) {
// the entry is a directory
return -1;
}
*buf = mz_zip_reader_extract_to_heap(pzip, idx, bufsize, 0);
return (*buf) ? 0 : -1;
}
int zip_entry_fread(struct zip_t *zip, const char *filename) {
mz_zip_archive *pzip = NULL;
mz_uint idx;
if (!zip) {
// zip_t handler is not initialized
return -1;
}
if (zip->mode != 'r' || zip->entry.index < 0) {
// the entry is not found or we do not have read access
return -1;
}
pzip = &(zip->archive);
idx = (mz_uint)zip->entry.index;
if (mz_zip_reader_is_file_a_directory(pzip, idx)) {
// the entry is a directory
return -1;
}
return (mz_zip_reader_extract_to_file(pzip, idx, filename, 0)) ? 0 : -1;
}
int zip_entry_extract(struct zip_t *zip,
size_t (*on_extract)(void *arg, unsigned long long offset,
const void *buf, size_t bufsize),
void *arg) {
mz_zip_archive *pzip = NULL;
mz_uint idx;
if (!zip) {
// zip_t handler is not initialized
return -1;
}
if (zip->mode != 'r' || zip->entry.index < 0) {
// the entry is not found or we do not have read access
return -1;
}
pzip = &(zip->archive);
idx = (mz_uint)zip->entry.index;
return (mz_zip_reader_extract_to_callback(pzip, idx, on_extract, arg, 0))
? 0
: -1;
}
int zip_create(const char *zipname, const char *filenames[], size_t len) {
int status = 0;
size_t i;
mz_zip_archive zip_archive;
if (!zipname || strlen(zipname) < 1) {
// zip_t archive name is empty or NULL
return -1;
}
// Create a new archive.
if (!memset(&(zip_archive), 0, sizeof(zip_archive))) {
// Cannot memset zip archive
return -1;
}
if (!mz_zip_writer_init_file(&zip_archive, zipname, 0)) {
// Cannot initialize zip_archive writer
return -1;
}
for (i = 0; i < len; ++i) {
const char *name = filenames[i];
if (!name) {
status = -1;
break;
}
if (!mz_zip_writer_add_file(&zip_archive, basename(name), name, "", 0,
ZIP_DEFAULT_COMPRESSION_LEVEL)) {
// Cannot add file to zip_archive
status = -1;
break;
}
}
mz_zip_writer_finalize_archive(&zip_archive);
mz_zip_writer_end(&zip_archive);
return status;
}
int zip_extract(const char *zipname, const char *dir,
int (*on_extract)(const char *filename, void *arg), void *arg) {
int status = -1;
mz_uint i, n;
char path[MAX_PATH + 1] = {0};
mz_zip_archive zip_archive;
mz_zip_archive_file_stat info;
size_t dirlen = 0;
if (!memset(&(zip_archive), 0, sizeof(zip_archive))) {
// Cannot memset zip archive
return -1;
}
if (!zipname || !dir) {
// Cannot parse zip archive name
return -1;
}
dirlen = strlen(dir);
if (dirlen + 1 > MAX_PATH) {
return -1;
}
// Now try to open the archive.
if (!mz_zip_reader_init_file(&zip_archive, zipname, 0)) {
// Cannot initialize zip_archive reader
return -1;
}
strcpy(path, dir);
if (!ISSLASH(path[dirlen - 1])) {
#if defined _WIN32 || defined __WIN32__
path[dirlen] = '\\';
#else
path[dirlen] = '/';
#endif
++dirlen;
}
// Get and print information about each file in the archive.
n = mz_zip_reader_get_num_files(&zip_archive);
for (i = 0; i < n; ++i) {
if (!mz_zip_reader_file_stat(&zip_archive, i, &info)) {
// Cannot get information about zip archive;
goto out;
}
strncpy(&path[dirlen], info.m_filename, MAX_PATH - dirlen);
if (mkpath(path) < 0) {
// Cannot make a path
goto out;
}
if (!mz_zip_reader_is_file_a_directory(&zip_archive, i)) {
if (!mz_zip_reader_extract_to_file(&zip_archive, i, path, 0)) {
// Cannot extract zip archive to file
goto out;
}
}
if (on_extract) {
if (on_extract(path, arg) < 0) {
goto out;
}
}
}
status = 0;
out:
// Close the archive, freeing any resources it was using
if (!mz_zip_reader_end(&zip_archive)) {
// Cannot end zip reader
status = -1;
}
return status;
}

View File

@ -0,0 +1,193 @@
/*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#pragma once
#ifndef ZIP_H
#define ZIP_H
#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifndef MAX_PATH
#define MAX_PATH 32767 /* # chars in a path name including NULL */
#endif
#define ZIP_DEFAULT_COMPRESSION_LEVEL 6
/*
This data structure is used throughout the library to represent zip archive
- forward declaration.
*/
struct zip_t;
/*
Opens zip archive with compression level using the given mode.
Args:
zipname: zip archive file name.
level: compression level (0-9 are the standard zlib-style levels).
mode: file access mode.
'r': opens a file for reading/extracting (the file must exists).
'w': creates an empty file for writing.
'a': appends to an existing archive.
Returns:
The zip archive handler or NULL on error
*/
extern struct zip_t *zip_open(const char *zipname, int level, char mode);
/*
Closes zip archive, releases resources - always finalize.
Args:
zip: zip archive handler.
*/
extern void zip_close(struct zip_t *zip);
/*
Opens a new entry for writing in a zip archive.
Args:
zip: zip archive handler.
entryname: an entry name in local dictionary.
Returns:
The return code - 0 on success, negative number (< 0) on error.
*/
extern int zip_entry_open(struct zip_t *zip, const char *entryname);
/*
Closes a zip entry, flushes buffer and releases resources.
Args:
zip: zip archive handler.
Returns:
The return code - 0 on success, negative number (< 0) on error.
*/
extern int zip_entry_close(struct zip_t *zip);
/*
Compresses an input buffer for the current zip entry.
Args:
zip: zip archive handler.
buf: input buffer.
bufsize: input buffer size (in bytes).
Returns:
The return code - 0 on success, negative number (< 0) on error.
*/
extern int zip_entry_write(struct zip_t *zip, const void *buf, size_t bufsize);
/*
Compresses a file for the current zip entry.
Args:
zip: zip archive handler.
filename: input file.
Returns:
The return code - 0 on success, negative number (< 0) on error.
*/
extern int zip_entry_fwrite(struct zip_t *zip, const char *filename);
/*
Extracts the current zip entry into output buffer.
The function allocates sufficient memory for a output buffer.
Args:
zip: zip archive handler.
buf: output buffer.
bufsize: output buffer size (in bytes).
Note:
- remember to release memory allocated for a output buffer.
- for large entries, please take a look at zip_entry_extract function.
Returns:
The return code - 0 on success, negative number (< 0) on error.
*/
extern int zip_entry_read(struct zip_t *zip, void **buf, size_t *bufsize);
/*
Extracts the current zip entry into output file.
Args:
zip: zip archive handler.
filename: output file.
Returns:
The return code - 0 on success, negative number (< 0) on error.
*/
extern int zip_entry_fread(struct zip_t *zip, const char *filename);
/*
Extract the current zip entry using a callback function (on_extract).
Args:
zip: zip archive handler.
on_extract: callback function.
arg: opaque pointer (optional argument,
which you can pass to the on_extract callback)
Returns:
The return code - 0 on success, negative number (< 0) on error.
*/
extern int zip_entry_extract(struct zip_t *zip,
size_t (*on_extract)(void *arg,
unsigned long long offset,
const void *data,
size_t size),
void *arg);
/*
Creates a new archive and puts files into a single zip archive.
Args:
zipname: zip archive file.
filenames: input files.
len: number of input files.
Returns:
The return code - 0 on success, negative number (< 0) on error.
*/
extern int zip_create(const char *zipname, const char *filenames[], size_t len);
/*
Extracts a zip archive file into directory.
If on_extract_entry is not NULL, the callback will be called after
successfully extracted each zip entry.
Returning a negative value from the callback will cause abort and return an
error. The last argument (void *arg) is optional, which you can use to pass
data to the on_extract_entry callback.
Args:
zipname: zip archive file.
dir: output directory.
on_extract_entry: on extract callback.
arg: opaque pointer.
Returns:
The return code - 0 on success, negative number (< 0) on error.
*/
extern int zip_extract(const char *zipname, const char *dir,
int (*on_extract_entry)(const char *filename, void *arg),
void *arg);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,7 @@
cmake_minimum_required(VERSION 2.8)
# test
include_directories(../src)
add_executable(test.exe test.c ../src/zip.c)
add_test(NAME test COMMAND test.exe)

View File

@ -0,0 +1,105 @@
#include <zip.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define ZIPNAME "test.zip"
#define TESTDATA1 "Some test data 1...\0"
#define TESTDATA2 "Some test data 2...\0"
static void test_write(void) {
struct zip_t *zip = zip_open(ZIPNAME, ZIP_DEFAULT_COMPRESSION_LEVEL, 'w');
assert(zip != NULL);
assert(0 == zip_entry_open(zip, "test/test-1.txt"));
assert(0 == zip_entry_write(zip, TESTDATA1, strlen(TESTDATA1)));
assert(0 == zip_entry_close(zip));
zip_close(zip);
}
static void test_append(void) {
struct zip_t *zip = zip_open(ZIPNAME, ZIP_DEFAULT_COMPRESSION_LEVEL, 'a');
assert(zip != NULL);
assert(0 == zip_entry_open(zip, "test\\test-2.txt"));
assert(0 == zip_entry_write(zip, TESTDATA2, strlen(TESTDATA2)));
assert(0 == zip_entry_close(zip));
zip_close(zip);
}
static void test_read(void) {
char *buf = NULL;
size_t bufsize;
struct zip_t *zip = zip_open(ZIPNAME, 0, 'r');
assert(zip != NULL);
assert(0 == zip_entry_open(zip, "test\\test-1.txt"));
assert(0 == zip_entry_read(zip, (void **)&buf, &bufsize));
assert(bufsize == strlen(TESTDATA1));
assert(0 == strncmp(buf, TESTDATA1, bufsize));
assert(0 == zip_entry_close(zip));
free(buf);
buf = NULL;
bufsize = 0;
assert(0 == zip_entry_open(zip, "test/test-2.txt"));
assert(0 == zip_entry_read(zip, (void **)&buf, &bufsize));
assert(bufsize == strlen(TESTDATA2));
assert(0 == strncmp(buf, TESTDATA2, bufsize));
assert(0 == zip_entry_close(zip));
free(buf);
buf = NULL;
bufsize = 0;
zip_close(zip);
}
struct buffer_t {
char *data;
size_t size;
};
static size_t on_extract(void *arg, unsigned long long offset, const void *data,
size_t size) {
struct buffer_t *buf = (struct buffer_t *)arg;
buf->data = realloc(buf->data, buf->size + size + 1);
assert(NULL != buf->data);
memcpy(&(buf->data[buf->size]), data, size);
buf->size += size;
buf->data[buf->size] = 0;
return size;
}
static void test_extract(void) {
struct buffer_t buf = {0};
struct zip_t *zip = zip_open(ZIPNAME, 0, 'r');
assert(zip != NULL);
assert(0 == zip_entry_open(zip, "test/test-1.txt"));
assert(0 == zip_entry_extract(zip, on_extract, &buf));
assert(buf.size == strlen(TESTDATA1));
assert(0 == strncmp(buf.data, TESTDATA1, buf.size));
assert(0 == zip_entry_close(zip));
free(buf.data);
buf.data = NULL;
buf.size = 0;
zip_close(zip);
}
int main(int argc, char *argv[]) {
test_write();
test_append();
test_read();
test_extract();
return remove(ZIPNAME);
}

BIN
contrib/zip/zip.png 100644

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB