Merge branch 'master' into issue_2393

pull/2400/head
Matias Lavik 2019-04-03 12:46:11 +02:00 committed by GitHub
commit 0251633ea2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 6590 additions and 3794 deletions

View File

@ -555,8 +555,6 @@ void WriteDump(const aiScene* scene, IOStream* io, bool shortened) {
mesh->mNormals[n].z); mesh->mNormals[n].z);
} }
} }
else {
}
ioprintf(io,"\t\t</Normals>\n"); ioprintf(io,"\t\t</Normals>\n");
} }

View File

@ -416,7 +416,7 @@ template <> struct Structure :: _defaultInitializer<ErrorPolicy_Fail> {
void operator ()(T& /*out*/,const char* = "") { void operator ()(T& /*out*/,const char* = "") {
// obviously, it is crucial that _DefaultInitializer is used // obviously, it is crucial that _DefaultInitializer is used
// only from within a catch clause. // only from within a catch clause.
throw; throw DeadlyImportError("Constructing BlenderDNA Structure encountered an error");
} }
}; };

View File

@ -866,6 +866,13 @@ SET( ziplib_SRCS
../contrib/zip/src/zip.h ../contrib/zip/src/zip.h
) )
# TODO if cmake required version has been updated to >3.12.0, collapse this to the second case only
if(${CMAKE_VERSION} VERSION_LESS "3.12.0")
add_definitions(-DMINIZ_USE_UNALIGNED_LOADS_AND_STORES=0)
else()
add_compile_definitions(MINIZ_USE_UNALIGNED_LOADS_AND_STORES=0)
endif()
SOURCE_GROUP( ziplib FILES ${ziplib_SRCS} ) SOURCE_GROUP( ziplib FILES ${ziplib_SRCS} )
SET ( openddl_parser_SRCS SET ( openddl_parser_SRCS

View File

@ -144,7 +144,7 @@ void COBImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS
// check header // check header
char head[32]; char head[32];
stream->CopyAndAdvance(head,32); stream->CopyAndAdvance(head,32);
if (strncmp(head,"Caligari ",9)) { if (strncmp(head,"Caligari ",9) != 0) {
ThrowException("Could not found magic id: `Caligari`"); ThrowException("Could not found magic id: `Caligari`");
} }
@ -656,14 +656,14 @@ void COBImporter::ReadLght_Ascii(Scene& out, LineSplitter& splitter, const Chunk
ReadFloat3Tuple_Ascii(msh.color ,&rgb); ReadFloat3Tuple_Ascii(msh.color ,&rgb);
SkipSpaces(&rgb); SkipSpaces(&rgb);
if (strncmp(rgb,"cone angle",10)) { if (strncmp(rgb,"cone angle",10) != 0) {
ASSIMP_LOG_WARN_F( "Expected `cone angle` entity in `color` line in `Lght` chunk ", nfo.id ); ASSIMP_LOG_WARN_F( "Expected `cone angle` entity in `color` line in `Lght` chunk ", nfo.id );
} }
SkipSpaces(rgb+10,&rgb); SkipSpaces(rgb+10,&rgb);
msh.angle = fast_atof(&rgb); msh.angle = fast_atof(&rgb);
SkipSpaces(&rgb); SkipSpaces(&rgb);
if (strncmp(rgb,"inner angle",11)) { if (strncmp(rgb,"inner angle",11) != 0) {
ASSIMP_LOG_WARN_F( "Expected `inner angle` entity in `color` line in `Lght` chunk ", nfo.id); ASSIMP_LOG_WARN_F( "Expected `inner angle` entity in `color` line in `Lght` chunk ", nfo.id);
} }
SkipSpaces(rgb+11,&rgb); SkipSpaces(rgb+11,&rgb);
@ -903,7 +903,7 @@ public:
if(nfo.size != static_cast<unsigned int>(-1)) { if(nfo.size != static_cast<unsigned int>(-1)) {
try { try {
reader.IncPtr( static_cast< int >( nfo.size ) - reader.GetCurrentPos() + cur ); reader.IncPtr( static_cast< int >( nfo.size ) - reader.GetCurrentPos() + cur );
} catch ( DeadlyImportError e ) { } catch (const DeadlyImportError& e ) {
// out of limit so correct the value // out of limit so correct the value
reader.IncPtr( reader.GetReadLimit() ); reader.IncPtr( reader.GetReadLimit() );
} }
@ -1214,7 +1214,7 @@ void COBImporter::ReadGrou_Binary(COB::Scene& out, StreamReaderLE& reader, const
const chunk_guard cn(nfo,reader); const chunk_guard cn(nfo,reader);
out.nodes.push_back(std::shared_ptr<Group>(new Group())); out.nodes.push_back(std::make_shared<Group>());
Group& msh = (Group&)(*out.nodes.back().get()); Group& msh = (Group&)(*out.nodes.back().get());
msh = nfo; msh = nfo;

View File

@ -476,8 +476,11 @@ D3MFOpcPackage::D3MFOpcPackage(IOSystem* pIOHandler, const std::string& rFile)
mZipArchive->Close( fileStream ); mZipArchive->Close( fileStream );
} else if( file == D3MF::XmlTag::CONTENT_TYPES_ARCHIVE) { } else if( file == D3MF::XmlTag::CONTENT_TYPES_ARCHIVE) {
ASSIMP_LOG_WARN_F("Ignored file of unsupported type CONTENT_TYPES_ARCHIVES",file);
} else {
ASSIMP_LOG_WARN_F("Ignored file of unknown type: ",file);
} }
} }
} }

View File

@ -1712,22 +1712,22 @@ namespace Assimp {
if (!mesh) if (!mesh)
{ {
for (const MeshMap::value_type& v : meshes_converted) { for (const MeshMap::value_type& v : meshes_converted) {
const MeshGeometry* const mesh = dynamic_cast<const MeshGeometry*> (v.first); const MeshGeometry* const meshGeom = dynamic_cast<const MeshGeometry*> (v.first);
if (!mesh) { if (!meshGeom) {
continue; continue;
} }
const MatIndexArray& mats = mesh->GetMaterialIndices(); const MatIndexArray& mats = meshGeom->GetMaterialIndices();
if (std::find(mats.begin(), mats.end(), matIndex) == mats.end()) { if (std::find(mats.begin(), mats.end(), matIndex) == mats.end()) {
continue; continue;
} }
int index = -1; int index = -1;
for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) { for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
if (mesh->GetTextureCoords(i).empty()) { if (meshGeom->GetTextureCoords(i).empty()) {
break; break;
} }
const std::string& name = mesh->GetTextureCoordChannelName(i); const std::string& name = meshGeom->GetTextureCoordChannelName(i);
if (name == uvSet) { if (name == uvSet) {
index = static_cast<int>(i); index = static_cast<int>(i);
break; break;
@ -1835,22 +1835,22 @@ namespace Assimp {
if (!mesh) if (!mesh)
{ {
for (const MeshMap::value_type& v : meshes_converted) { for (const MeshMap::value_type& v : meshes_converted) {
const MeshGeometry* const mesh = dynamic_cast<const MeshGeometry*> (v.first); const MeshGeometry* const meshGeom = dynamic_cast<const MeshGeometry*> (v.first);
if (!mesh) { if (!meshGeom) {
continue; continue;
} }
const MatIndexArray& mats = mesh->GetMaterialIndices(); const MatIndexArray& mats = meshGeom->GetMaterialIndices();
if (std::find(mats.begin(), mats.end(), matIndex) == mats.end()) { if (std::find(mats.begin(), mats.end(), matIndex) == mats.end()) {
continue; continue;
} }
int index = -1; int index = -1;
for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) { for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
if (mesh->GetTextureCoords(i).empty()) { if (meshGeom->GetTextureCoords(i).empty()) {
break; break;
} }
const std::string& name = mesh->GetTextureCoordChannelName(i); const std::string& name = meshGeom->GetTextureCoordChannelName(i);
if (name == uvSet) { if (name == uvSet) {
index = static_cast<int>(i); index = static_cast<int>(i);
break; break;
@ -2197,22 +2197,22 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTa
if (!mesh) if (!mesh)
{ {
for (const MeshMap::value_type& v : meshes_converted) { for (const MeshMap::value_type& v : meshes_converted) {
const MeshGeometry* const mesh = dynamic_cast<const MeshGeometry*>(v.first); const MeshGeometry* const meshGeom = dynamic_cast<const MeshGeometry*>(v.first);
if (!mesh) { if (!meshGeom) {
continue; continue;
} }
const MatIndexArray& mats = mesh->GetMaterialIndices(); const MatIndexArray& mats = meshGeom->GetMaterialIndices();
if (std::find(mats.begin(), mats.end(), matIndex) == mats.end()) { if (std::find(mats.begin(), mats.end(), matIndex) == mats.end()) {
continue; continue;
} }
int index = -1; int index = -1;
for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) { for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
if (mesh->GetTextureCoords(i).empty()) { if (meshGeom->GetTextureCoords(i).empty()) {
break; break;
} }
const std::string& name = mesh->GetTextureCoordChannelName(i); const std::string& name = meshGeom->GetTextureCoordChannelName(i);
if (name == uvSet) { if (name == uvSet) {
index = static_cast<int>(i); index = static_cast<int>(i);
break; break;

View File

@ -432,7 +432,7 @@ void FBX::Node::WritePropertyNodeAscii(
char buffer[32]; char buffer[32];
FBX::Node node(name); FBX::Node node(name);
node.Begin(s, false, indent); node.Begin(s, false, indent);
std::string vsize = std::to_string(v.size()); std::string vsize = to_string(v.size());
// *<size> { // *<size> {
s.PutChar('*'); s.PutString(vsize); s.PutString(" {\n"); s.PutChar('*'); s.PutString(vsize); s.PutString(" {\n");
// indent + 1 // indent + 1
@ -468,7 +468,7 @@ void FBX::Node::WritePropertyNodeAscii(
char buffer[32]; char buffer[32];
FBX::Node node(name); FBX::Node node(name);
node.Begin(s, false, indent); node.Begin(s, false, indent);
std::string vsize = std::to_string(v.size()); std::string vsize = to_string(v.size());
// *<size> { // *<size> {
s.PutChar('*'); s.PutString(vsize); s.PutString(" {\n"); s.PutChar('*'); s.PutString(vsize); s.PutString(" {\n");
// indent + 1 // indent + 1

View File

@ -326,8 +326,11 @@ Video::Video(uint64_t id, const Element& element, const Document& doc, const std
content = new uint8_t[len]; content = new uint8_t[len];
::memcpy(content, data + 5, len); ::memcpy(content, data + 5, len);
} }
} catch (runtime_error runtimeError) { } catch (const runtime_error& runtimeError)
{
//we don't need the content data for contents that has already been loaded //we don't need the content data for contents that has already been loaded
ASSIMP_LOG_DEBUG_F("Caught exception in FBXMaterial (likely because content was already loaded): ",
runtimeError.what());
} }
} }

View File

@ -304,9 +304,6 @@ inline void FindSuitableMultiple(int& angle)
else if (angle < 10) angle = 10; else if (angle < 10) angle = 10;
else if (angle < 20) angle = 20; else if (angle < 20) angle = 20;
else if (angle < 30) angle = 30; else if (angle < 30) angle = 30;
else
{
}
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -317,6 +314,8 @@ void IRRImporter::ComputeAnimations(Node* root, aiNode* real, std::vector<aiNode
// XXX totally WIP - doesn't produce proper results, need to evaluate // XXX totally WIP - doesn't produce proper results, need to evaluate
// whether there's any use for Irrlicht's proprietary scene format // whether there's any use for Irrlicht's proprietary scene format
// outside Irrlicht ... // outside Irrlicht ...
// This also applies to the above function of FindSuitableMultiple and ClampSpline which are
// solely used in this function
if (root->animators.empty()) { if (root->animators.empty()) {
return; return;
@ -674,38 +673,38 @@ void IRRImporter::GenerateGraph(Node* root,aiNode* rootOut ,aiScene* scene,
// Get the loaded mesh from the scene and add it to // Get the loaded mesh from the scene and add it to
// the list of all scenes to be attached to the // the list of all scenes to be attached to the
// graph we're currently building // graph we're currently building
aiScene* scene = batch.GetImport(root->id); aiScene* localScene = batch.GetImport(root->id);
if (!scene) { if (!localScene) {
ASSIMP_LOG_ERROR("IRR: Unable to load external file: " + root->meshPath); ASSIMP_LOG_ERROR("IRR: Unable to load external file: " + root->meshPath);
break; break;
} }
attach.push_back(AttachmentInfo(scene,rootOut)); attach.push_back(AttachmentInfo(localScene,rootOut));
// Now combine the material we've loaded for this mesh // Now combine the material we've loaded for this mesh
// with the real materials we got from the file. As we // with the real materials we got from the file. As we
// don't execute any pp-steps on the file, the numbers // don't execute any pp-steps on the file, the numbers
// should be equal. If they are not, we can impossibly // should be equal. If they are not, we can impossibly
// do this ... // do this ...
if (root->materials.size() != (unsigned int)scene->mNumMaterials) { if (root->materials.size() != (unsigned int)localScene->mNumMaterials) {
ASSIMP_LOG_WARN("IRR: Failed to match imported materials " ASSIMP_LOG_WARN("IRR: Failed to match imported materials "
"with the materials found in the IRR scene file"); "with the materials found in the IRR scene file");
break; break;
} }
for (unsigned int i = 0; i < scene->mNumMaterials;++i) { for (unsigned int i = 0; i < localScene->mNumMaterials;++i) {
// Delete the old material, we don't need it anymore // Delete the old material, we don't need it anymore
delete scene->mMaterials[i]; delete localScene->mMaterials[i];
std::pair<aiMaterial*, unsigned int>& src = root->materials[i]; std::pair<aiMaterial*, unsigned int>& src = root->materials[i];
scene->mMaterials[i] = src.first; localScene->mMaterials[i] = src.first;
} }
// NOTE: Each mesh should have exactly one material assigned, // NOTE: Each mesh should have exactly one material assigned,
// but we do it in a separate loop if this behaviour changes // but we do it in a separate loop if this behaviour changes
// in future. // in future.
for (unsigned int i = 0; i < scene->mNumMeshes;++i) { for (unsigned int i = 0; i < localScene->mNumMeshes;++i) {
// Process material flags // Process material flags
aiMesh* mesh = scene->mMeshes[i]; aiMesh* mesh = localScene->mMeshes[i];
// If "trans_vertex_alpha" mode is enabled, search all vertex colors // If "trans_vertex_alpha" mode is enabled, search all vertex colors

View File

@ -278,10 +278,10 @@ void STEP::ReadFile(DB& db,const EXPRESS::ConversionSchema& scheme,
std::transform( type.begin(), type.end(), type.begin(), &Assimp::ToLower<char> ); std::transform( type.begin(), type.end(), type.begin(), &Assimp::ToLower<char> );
const char* sz = scheme.GetStaticStringForToken(type); const char* sz = scheme.GetStaticStringForToken(type);
if(sz) { if(sz) {
const std::string::size_type len = n2-n1+1; const std::string::size_type szLen = n2-n1+1;
char* const copysz = new char[len+1]; char* const copysz = new char[szLen+1];
std::copy(s.c_str()+n1,s.c_str()+n2+1,copysz); std::copy(s.c_str()+n1,s.c_str()+n2+1,copysz);
copysz[len] = '\0'; copysz[szLen] = '\0';
db.InternInsert(new LazyObject(db,id,line,sz,copysz)); db.InternInsert(new LazyObject(db,id,line,sz,copysz));
} }
if(!has_next) { if(!has_next) {

View File

@ -443,10 +443,10 @@ void MD5Importer::LoadMD5MeshFile ()
for (MD5::VertexList::const_iterator iter = meshSrc.mVertices.begin();iter != meshSrc.mVertices.end();++iter,++pv) { for (MD5::VertexList::const_iterator iter = meshSrc.mVertices.begin();iter != meshSrc.mVertices.end();++iter,++pv) {
for (unsigned int jub = (*iter).mFirstWeight, w = jub; w < jub + (*iter).mNumWeights;++w) for (unsigned int jub = (*iter).mFirstWeight, w = jub; w < jub + (*iter).mNumWeights;++w)
{ {
MD5::WeightDesc& desc = meshSrc.mWeights[w]; MD5::WeightDesc& weightDesc = meshSrc.mWeights[w];
/* FIX for some invalid exporters */ /* FIX for some invalid exporters */
if (!(desc.mWeight < AI_MD5_WEIGHT_EPSILON && desc.mWeight >= -AI_MD5_WEIGHT_EPSILON )) if (!(weightDesc.mWeight < AI_MD5_WEIGHT_EPSILON && weightDesc.mWeight >= -AI_MD5_WEIGHT_EPSILON ))
++piCount[desc.mBone]; ++piCount[weightDesc.mBone];
} }
} }
@ -493,20 +493,20 @@ void MD5Importer::LoadMD5MeshFile ()
if (w >= meshSrc.mWeights.size()) if (w >= meshSrc.mWeights.size())
throw DeadlyImportError("MD5MESH: Invalid weight index"); throw DeadlyImportError("MD5MESH: Invalid weight index");
MD5::WeightDesc& desc = meshSrc.mWeights[w]; MD5::WeightDesc& weightDesc = meshSrc.mWeights[w];
if ( desc.mWeight < AI_MD5_WEIGHT_EPSILON && desc.mWeight >= -AI_MD5_WEIGHT_EPSILON) { if ( weightDesc.mWeight < AI_MD5_WEIGHT_EPSILON && weightDesc.mWeight >= -AI_MD5_WEIGHT_EPSILON) {
continue; continue;
} }
const ai_real fNewWeight = desc.mWeight / fSum; const ai_real fNewWeight = weightDesc.mWeight / fSum;
// transform the local position into worldspace // transform the local position into worldspace
MD5::BoneDesc& boneSrc = meshParser.mJoints[desc.mBone]; MD5::BoneDesc& boneSrc = meshParser.mJoints[weightDesc.mBone];
const aiVector3D v = boneSrc.mRotationQuatConverted.Rotate (desc.vOffsetPosition); const aiVector3D v = boneSrc.mRotationQuatConverted.Rotate (weightDesc.vOffsetPosition);
// use the original weight to compute the vertex position // use the original weight to compute the vertex position
// (some MD5s seem to depend on the invalid weight values ...) // (some MD5s seem to depend on the invalid weight values ...)
*pv += ((boneSrc.mPositionXYZ+v)* (ai_real)desc.mWeight); *pv += ((boneSrc.mPositionXYZ+v)* (ai_real)weightDesc.mWeight);
aiBone* bone = mesh->mBones[boneSrc.mMap]; aiBone* bone = mesh->mBones[boneSrc.mMap];
*bone->mWeights++ = aiVertexWeight((unsigned int)(pv-mesh->mVertices),fNewWeight); *bone->mWeights++ = aiVertexWeight((unsigned int)(pv-mesh->mVertices),fNewWeight);

View File

@ -127,7 +127,7 @@ STLExporter::STLExporter(const char* _filename, const aiScene* pScene, bool expo
mOutput.write((char *)&meshnum, 4); mOutput.write((char *)&meshnum, 4);
if (exportPointClouds) { if (exportPointClouds) {
throw DeadlyExportError("This functionality is not yet implemented for binary output.");
} }
for(unsigned int i = 0; i < pScene->mNumMeshes; ++i) { for(unsigned int i = 0; i < pScene->mNumMeshes; ++i) {

View File

@ -294,17 +294,17 @@ namespace glTF {
// filling object "compressedData" // filling object "compressedData"
json_comp_data.SetObject(); json_comp_data.SetObject();
json_comp_data.AddMember("buffer", ptr_ext_comp->Buffer, w.mAl); json_comp_data.AddMember("buffer", ptr_ext_comp->Buffer, w.mAl);
json_comp_data.AddMember("byteOffset", ptr_ext_comp->Offset, w.mAl); json_comp_data.AddMember("byteOffset", static_cast<uint64_t>(ptr_ext_comp->Offset), w.mAl);
json_comp_data.AddMember("componentType", 5121, w.mAl); json_comp_data.AddMember("componentType", 5121, w.mAl);
json_comp_data.AddMember("type", "SCALAR", w.mAl); json_comp_data.AddMember("type", "SCALAR", w.mAl);
json_comp_data.AddMember("count", ptr_ext_comp->Count, w.mAl); json_comp_data.AddMember("count", static_cast<uint64_t>(ptr_ext_comp->Count), w.mAl);
if(ptr_ext_comp->Binary) if(ptr_ext_comp->Binary)
json_comp_data.AddMember("mode", "binary", w.mAl); json_comp_data.AddMember("mode", "binary", w.mAl);
else else
json_comp_data.AddMember("mode", "ascii", w.mAl); json_comp_data.AddMember("mode", "ascii", w.mAl);
json_comp_data.AddMember("indicesCount", ptr_ext_comp->IndicesCount, w.mAl); json_comp_data.AddMember("indicesCount", static_cast<uint64_t>(ptr_ext_comp->IndicesCount), w.mAl);
json_comp_data.AddMember("verticesCount", ptr_ext_comp->VerticesCount, w.mAl); json_comp_data.AddMember("verticesCount", static_cast<uint64_t>(ptr_ext_comp->VerticesCount), w.mAl);
// filling object "Open3DGC-compression" // filling object "Open3DGC-compression"
Value json_o3dgc; Value json_o3dgc;

View File

@ -245,7 +245,7 @@ inline Ref<Accessor> ExportData(Asset& a, std::string& meshName, Ref<Buffer>& bu
namespace { namespace {
void GetMatScalar(const aiMaterial* mat, float& val, const char* propName, int type, int idx) { void GetMatScalar(const aiMaterial* mat, float& val, const char* propName, int type, int idx) {
if (mat->Get(propName, type, idx, val) == AI_SUCCESS) {} ai_assert(mat->Get(propName, type, idx, val) == AI_SUCCESS);
} }
} }

View File

@ -36,3 +36,21 @@
# Temporary # Temporary
*.swp *.swp
.DS_Store .DS_Store
# CMake
CMakeScripts
*.cmake
# Xcode
*.build
*.xcodeproj
zip.sln
zip.vcxproj.filters
zip.vcxproj
ALL_BUILD.vcxproj.filters
ALL_BUILD.vcxproj
CMakeFiles/
zip.dir/
test/test.exe.vcxproj.filters
test/test.exe.vcxproj
test/test.exe.dir/

View File

@ -0,0 +1,18 @@
#!/bin/bash
#
# Build script for travis-ci.org builds.
#
if [ $ANALYZE = "true" ] && [ "$CC" = "clang" ]; then
# scan-build -h
scan-build cmake -G "Unix Makefiles"
scan-build -enable-checker security.FloatLoopCounter \
-enable-checker security.insecureAPI.UncheckedReturn \
--status-bugs -v \
make -j 8 \
make -j 8 test
else
cmake -DCMAKE_BUILD_TYPE=Debug -DSANITIZE_ADDRESS=On -DCMAKE_INSTALL_PREFIX=_install
make -j 8
make install
ASAN_OPTIONS=detect_leaks=0 LSAN_OPTIONS=verbosity=1:log_threads=1 ctest -V
fi

View File

@ -1,10 +1,22 @@
language: c language: c
addons:
apt:
packages: &1
- lcov
# Compiler selection # Compiler selection
compiler: compiler:
- clang - clang
- gcc - gcc
env:
- ANALYZE=false
- ANALYZE=true
# Build steps # Build steps
script: script:
- mkdir build - ./.travis.sh
- cd build after_success:
- cmake -DCMAKE_BUILD_TYPE=Debug .. && make && make test # Creating report
- cmake -DENABLE_COVERAGE=ON
- make
- make test
# Uploading report to CodeCov
- bash <(curl -s https://codecov.io/bash)

View File

@ -1,18 +1,47 @@
cmake_minimum_required(VERSION 2.8) cmake_minimum_required(VERSION 2.8)
project(zip) project(zip)
enable_language(C)
set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
if (MSVC) if (MSVC)
# Use secure functions by defaualt and suppress warnings about "deprecated" functions # 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=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_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") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_NONSTDC_NO_WARNINGS=1 /D _CRT_SECURE_NO_WARNINGS=1")
elseif ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" OR
"${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR
"${CMAKE_C_COMPILER_ID}" STREQUAL "AppleClang")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -Wall -Wextra -Werror -pedantic")
endif (MSVC) endif (MSVC)
# zip # zip
set(SRC src/miniz.h src/zip.h src/zip.c) set(SRC src/miniz.h src/zip.h src/zip.c)
add_library(${CMAKE_PROJECT_NAME} ${SRC}) add_library(${PROJECT_NAME} ${SRC})
target_include_directories(${PROJECT_NAME} INTERFACE src)
# test # test
if (NOT CMAKE_DISABLE_TESTING)
enable_testing() enable_testing()
add_subdirectory(test) add_subdirectory(test)
find_package(Sanitizers)
add_sanitizers(${PROJECT_NAME} test.exe)
add_sanitizers(${PROJECT_NAME} test_miniz.exe)
endif()
install(TARGETS ${PROJECT_NAME}
RUNTIME DESTINATION bin
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
COMPONENT library)
install(FILES ${PROJECT_SOURCE_DIR}/src/zip.h DESTINATION include)
# uninstall target (https://gitlab.kitware.com/cmake/community/wikis/FAQ#can-i-do-make-uninstall-with-cmake)
if(NOT TARGET uninstall)
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/cmake/cmake_uninstall.cmake"
IMMEDIATE @ONLY)
add_custom_target(uninstall
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake/cmake_uninstall.cmake)
endif()

View File

@ -1,12 +1,11 @@
### A portable (OSX/Linux/Windows), simple zip library written in C ### 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. 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] [![Windows](https://ci.appveyor.com/api/projects/status/bph8dr3jacgmjv32/branch/master?svg=true&label=windows)](https://ci.appveyor.com/project/kuba--/zip)
[![Linux](https://travis-ci.org/kuba--/zip.svg?branch=master&label=linux%2fosx)](https://travis-ci.org/kuba--/zip)
[![Version](https://badge.fury.io/gh/kuba--%2Fzip.svg)](https://github.com/kuba--/zip/releases)
[![Codecov](https://codecov.io/gh/kuba--/zip/branch/master/graph/badge.svg)](https://codecov.io/gh/kuba--/zip)
[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 # The Idea
<img src="zip.png" name="zip" /> <img src="zip.png" name="zip" />
@ -27,7 +26,7 @@ It was the reason, why I decided to write zip module on top of the miniz. It req
{ {
zip_entry_open(zip, "foo-1.txt"); zip_entry_open(zip, "foo-1.txt");
{ {
char *buf = "Some data here..."; const char *buf = "Some data here...\0";
zip_entry_write(zip, buf, strlen(buf)); zip_entry_write(zip, buf, strlen(buf));
} }
zip_entry_close(zip); zip_entry_close(zip);
@ -50,7 +49,7 @@ It was the reason, why I decided to write zip module on top of the miniz. It req
{ {
zip_entry_open(zip, "foo-3.txt"); zip_entry_open(zip, "foo-3.txt");
{ {
char *buf = "Append some data here..."; const char *buf = "Append some data here...\0";
zip_entry_write(zip, buf, strlen(buf)); zip_entry_write(zip, buf, strlen(buf));
} }
zip_entry_close(zip); zip_entry_close(zip);
@ -90,6 +89,27 @@ It was the reason, why I decided to write zip module on top of the miniz. It req
free(buf); free(buf);
``` ```
* Extract a zip entry into memory (no internal allocation).
```c
unsigned char *buf;
size_t bufsize;
struct zip_t *zip = zip_open("foo.zip", 0, 'r');
{
zip_entry_open(zip, "foo-1.txt");
{
bufsize = zip_entry_size(zip);
buf = calloc(sizeof(unsigned char), bufsize);
zip_entry_noallocread(zip, (void *)buf, bufsize);
}
zip_entry_close(zip);
}
zip_close(zip);
free(buf);
```
* Extract a zip entry into memory using callback. * Extract a zip entry into memory using callback.
```c ```c
struct buffer_t { struct buffer_t {
@ -137,3 +157,153 @@ It was the reason, why I decided to write zip module on top of the miniz. It req
zip_close(zip); zip_close(zip);
``` ```
* List of all zip entries
```c
struct zip_t *zip = zip_open("foo.zip", 0, 'r');
int i, n = zip_total_entries(zip);
for (i = 0; i < n; ++i) {
zip_entry_openbyindex(zip, i);
{
const char *name = zip_entry_name(zip);
int isdir = zip_entry_isdir(zip);
unsigned long long size = zip_entry_size(zip);
unsigned int crc32 = zip_entry_crc32(zip);
}
zip_entry_close(zip);
}
zip_close(zip);
```
## Bindings
Compile zip library as a dynamic library.
```shell
$ mkdir build
$ cd build
$ cmake -DBUILD_SHARED_LIBS=true ..
$ make
```
### Go (cgo)
```go
package main
/*
#cgo CFLAGS: -I../src
#cgo LDFLAGS: -L. -lzip
#include <zip.h>
*/
import "C"
import "unsafe"
func main() {
path := C.CString("/tmp/go.zip")
zip := C.zip_open(path, 6, 'w')
entryname := C.CString("test")
C.zip_entry_open(zip, entryname)
content := "test content"
buf := unsafe.Pointer(C.CString(content))
bufsize := C.size_t(len(content))
C.zip_entry_write(zip, buf, bufsize)
C.zip_entry_close(zip)
C.zip_close(zip)
}
```
### Ruby (ffi)
Install _ffi_ gem.
```shell
$ gem install ffi
```
Bind in your module.
```ruby
require 'ffi'
module Zip
extend FFI::Library
ffi_lib "./libzip.#{::FFI::Platform::LIBSUFFIX}"
attach_function :zip_open, [:string, :int, :char], :pointer
attach_function :zip_close, [:pointer], :void
attach_function :zip_entry_open, [:pointer, :string], :int
attach_function :zip_entry_close, [:pointer], :void
attach_function :zip_entry_write, [:pointer, :string, :int], :int
end
ptr = Zip.zip_open("/tmp/ruby.zip", 6, "w".bytes()[0])
status = Zip.zip_entry_open(ptr, "test")
content = "test content"
status = Zip.zip_entry_write(ptr, content, content.size())
Zip.zip_entry_close(ptr)
Zip.zip_close(ptr)
```
### Python (cffi)
Install _cffi_ package
```shell
$ pip install cffi
```
Bind in your package.
```python
import ctypes.util
from cffi import FFI
ffi = FFI()
ffi.cdef("""
struct zip_t *zip_open(const char *zipname, int level, char mode);
void zip_close(struct zip_t *zip);
int zip_entry_open(struct zip_t *zip, const char *entryname);
int zip_entry_close(struct zip_t *zip);
int zip_entry_write(struct zip_t *zip, const void *buf, size_t bufsize);
""")
Zip = ffi.dlopen(ctypes.util.find_library("zip"))
ptr = Zip.zip_open("/tmp/python.zip", 6, 'w')
status = Zip.zip_entry_open(ptr, "test")
content = "test content"
status = Zip.zip_entry_write(ptr, content, len(content))
Zip.zip_entry_close(ptr)
Zip.zip_close(ptr)
```
### Ring
The language comes with RingZip based on this library
```ring
load "ziplib.ring"
new Zip {
setFileName("myfile.zip")
open("w")
newEntry() {
open("test.c")
writefile("test.c")
close()
}
close()
}
```
# Contribution Rules/Coding Standards
No need to throw away your coding style, just do your best to follow default clang-format style.
Apply `clang-format` to the source files before commit:
```sh
for file in $(git ls-files | \grep -E '\.(c|h)$' | \grep -v -- '#')
do
clang-format -i $file
done
```

View File

@ -1,4 +1,4 @@
version: 1.0.{build} version: zip-0.1.9.{build}
build_script: build_script:
- cmd: >- - cmd: >-
cd c:\projects\zip cd c:\projects\zip

View File

@ -0,0 +1,55 @@
#!/bin/sh
# The MIT License (MIT)
#
# Copyright (c)
# 2013 Matthew Arsenault
# 2015-2016 RWTH Aachen University, Federal Republic of Germany
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# 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 OR COPYRIGHT HOLDERS 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.
# This script is a wrapper for AddressSanitizer. In some special cases you need
# to preload AddressSanitizer to avoid error messages - e.g. if you're
# preloading another library to your application. At the moment this script will
# only do something, if we're running on a Linux platform. OSX might not be
# affected.
# Exit immediately, if platform is not Linux.
if [ "$(uname)" != "Linux" ]
then
exec $@
fi
# Get the used libasan of the application ($1). If a libasan was found, it will
# be prepended to LD_PRELOAD.
libasan=$(ldd $1 | grep libasan | sed "s/^[[:space:]]//" | cut -d' ' -f1)
if [ -n "$libasan" ]
then
if [ -n "$LD_PRELOAD" ]
then
export LD_PRELOAD="$libasan:$LD_PRELOAD"
else
export LD_PRELOAD="$libasan"
fi
fi
# Execute the application.
exec $@

View File

@ -0,0 +1,23 @@
# copied from https://gitlab.kitware.com/cmake/community/wikis/FAQ#can-i-do-make-uninstall-with-cmake
if(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt")
message(FATAL_ERROR "Cannot find install manifest: @CMAKE_BINARY_DIR@/install_manifest.txt")
endif(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt")
file(READ "@CMAKE_BINARY_DIR@/install_manifest.txt" files)
string(REGEX REPLACE "\n" ";" files "${files}")
foreach(file ${files})
message(STATUS "Uninstalling $ENV{DESTDIR}${file}")
if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
exec_program(
"@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
OUTPUT_VARIABLE rm_out
RETURN_VALUE rm_retval
)
if(NOT "${rm_retval}" STREQUAL 0)
message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}")
endif(NOT "${rm_retval}" STREQUAL 0)
else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
message(STATUS "File $ENV{DESTDIR}${file} does not exist.")
endif(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
endforeach(file)

File diff suppressed because it is too large Load Diff

View File

@ -7,16 +7,15 @@
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE. * OTHER DEALINGS IN THE SOFTWARE.
*/ */
#define __STDC_WANT_LIB_EXT1__ 1
#include "zip.h"
#include "miniz.h"
#include <errno.h> #include <errno.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <time.h> #include <time.h>
#if defined _WIN32 || defined __WIN32__ #if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER) || \
/* Win32, DOS */ defined(__MINGW32__)
/* Win32, DOS, MSVC, MSVS */
#include <direct.h> #include <direct.h>
#define MKDIR(DIRNAME) _mkdir(DIRNAME) #define MKDIR(DIRNAME) _mkdir(DIRNAME)
@ -28,8 +27,20 @@
#define ISSLASH(C) ((C) == '/' || (C) == '\\') #define ISSLASH(C) ((C) == '/' || (C) == '\\')
#else #else
#include <unistd.h> // needed for symlink() on BSD
int symlink(const char *target, const char *linkpath); // needed on Linux
#define MKDIR(DIRNAME) mkdir(DIRNAME, 0755) #define MKDIR(DIRNAME) mkdir(DIRNAME, 0755)
#define STRCLONE(STR) ((STR) ? strdup(STR) : NULL) #define STRCLONE(STR) ((STR) ? strdup(STR) : NULL)
#endif
#include "miniz.h"
#include "zip.h"
#ifndef HAS_DEVICE
#define HAS_DEVICE(P) 0
#endif #endif
#ifndef FILESYSTEM_PREFIX_LEN #ifndef FILESYSTEM_PREFIX_LEN
@ -48,7 +59,7 @@
} \ } \
} while (0) } while (0)
static char *basename(const char *name) { static const char *base_name(const char *name) {
char const *p; char const *p;
char const *base = name += FILESYSTEM_PREFIX_LEN(name); char const *base = name += FILESYSTEM_PREFIX_LEN(name);
int all_slashes = 1; int all_slashes = 1;
@ -61,20 +72,32 @@ static char *basename(const char *name) {
} }
/* If NAME is all slashes, arrange to return `/'. */ /* If NAME is all slashes, arrange to return `/'. */
if (*base == '\0' && ISSLASH(*name) && all_slashes) --base; if (*base == '\0' && ISSLASH(*name) && all_slashes)
--base;
return (char *)base; return base;
} }
static int mkpath(const char *path) { static int mkpath(const char *path) {
char const *p; char const *p;
char npath[MAX_PATH + 1] = {0}; char npath[MAX_PATH + 1];
int len = 0; int len = 0;
int has_device = HAS_DEVICE(path);
for (p = path; *p && len < MAX_PATH; p++) { memset(npath, 0, MAX_PATH + 1);
if (ISSLASH(*p) && len > 0) {
#ifdef _WIN32
// only on windows fix the path
npath[0] = path[0];
npath[1] = path[1];
len = 2;
#endif // _WIN32
for (p = path + len; *p && len < MAX_PATH; p++) {
if (ISSLASH(*p) && ((!has_device && len > 0) || (has_device && len > 2))) {
if (MKDIR(npath) == -1) if (MKDIR(npath) == -1)
if (errno != EEXIST) return -1; if (errno != EEXIST)
return -1;
} }
npath[len++] = *p; npath[len++] = *p;
} }
@ -82,24 +105,28 @@ static int mkpath(const char *path) {
return 0; return 0;
} }
static char *strrpl(const char *str, char oldchar, char newchar) { static char *strrpl(const char *str, size_t n, char oldchar, char newchar) {
char *rpl = (char *)malloc(sizeof(char) * (1 + strlen(str)));
char *begin = rpl;
char c; char c;
while((c = *str++)) { size_t i;
char *rpl = (char *)calloc((1 + n), sizeof(char));
char *begin = rpl;
if (!rpl) {
return NULL;
}
for (i = 0; (i < n) && (c = *str++); ++i) {
if (c == oldchar) { if (c == oldchar) {
c = newchar; c = newchar;
} }
*rpl++ = c; *rpl++ = c;
} }
*rpl = '\0';
return begin; return begin;
} }
struct zip_entry_t { struct zip_entry_t {
int index; int index;
const char *name; char *name;
mz_uint64 uncomp_size; mz_uint64 uncomp_size;
mz_uint64 comp_size; mz_uint64 comp_size;
mz_uint32 uncomp_crc32; mz_uint32 uncomp_crc32;
@ -109,13 +136,14 @@ struct zip_entry_t {
mz_uint16 method; mz_uint16 method;
mz_zip_writer_add_state state; mz_zip_writer_add_state state;
tdefl_compressor comp; tdefl_compressor comp;
mz_uint32 external_attr;
time_t m_time;
}; };
struct zip_t { struct zip_t {
mz_zip_archive archive; mz_zip_archive archive;
mz_uint level; mz_uint level;
struct zip_entry_t entry; struct zip_entry_t entry;
char mode;
}; };
struct zip_t *zip_open(const char *zipname, int level, char mode) { struct zip_t *zip_open(const char *zipname, int level, char mode) {
@ -126,17 +154,18 @@ struct zip_t *zip_open(const char *zipname, int level, char mode) {
goto cleanup; goto cleanup;
} }
if (level < 0) level = MZ_DEFAULT_LEVEL; if (level < 0)
level = MZ_DEFAULT_LEVEL;
if ((level & 0xF) > MZ_UBER_COMPRESSION) { if ((level & 0xF) > MZ_UBER_COMPRESSION) {
// Wrong compression level // Wrong compression level
goto cleanup; goto cleanup;
} }
zip = (struct zip_t *)calloc((size_t)1, sizeof(struct zip_t)); zip = (struct zip_t *)calloc((size_t)1, sizeof(struct zip_t));
if (!zip) goto cleanup; if (!zip)
goto cleanup;
zip->level = level; zip->level = (mz_uint)level;
zip->mode = mode;
switch (mode) { switch (mode) {
case 'w': case 'w':
// Create a new archive. // Create a new archive.
@ -150,18 +179,16 @@ struct zip_t *zip_open(const char *zipname, int level, char mode) {
case 'a': case 'a':
if (!mz_zip_reader_init_file( if (!mz_zip_reader_init_file(
&(zip->archive), zipname, &(zip->archive), zipname,
level | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) { zip->level | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) {
// An archive file does not exist or cannot initialize // An archive file does not exist or cannot initialize
// zip_archive reader // zip_archive reader
goto cleanup; goto cleanup;
} }
if (mode == 'a' && if (mode == 'a' &&
!mz_zip_writer_init_from_reader(&(zip->archive), zipname)) { !mz_zip_writer_init_from_reader(&(zip->archive), zipname)) {
mz_zip_reader_end(&(zip->archive)); mz_zip_reader_end(&(zip->archive));
goto cleanup; goto cleanup;
} }
break; break;
default: default:
@ -189,10 +216,10 @@ void zip_close(struct zip_t *zip) {
} }
int zip_entry_open(struct zip_t *zip, const char *entryname) { int zip_entry_open(struct zip_t *zip, const char *entryname) {
char *locname = NULL;
size_t entrylen = 0; size_t entrylen = 0;
mz_zip_archive *pzip = NULL; mz_zip_archive *pzip = NULL;
mz_uint num_alignment_padding_bytes, level; mz_uint num_alignment_padding_bytes, level;
mz_zip_archive_file_stat stats;
if (!zip || !entryname) { if (!zip || !entryname) {
return -1; return -1;
@ -203,7 +230,6 @@ int zip_entry_open(struct zip_t *zip, const char *entryname) {
return -1; return -1;
} }
pzip = &(zip->archive);
/* /*
.ZIP File Format Specification Version: 6.3.3 .ZIP File Format Specification Version: 6.3.3
@ -215,40 +241,56 @@ int zip_entry_open(struct zip_t *zip, const char *entryname) {
and UNIX file systems etc. If input came from standard and UNIX file systems etc. If input came from standard
input, there is no file name field. input, there is no file name field.
*/ */
locname = strrpl(entryname, '\\', '/'); zip->entry.name = strrpl(entryname, entrylen, '\\', '/');
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) { if (!zip->entry.name) {
// Cannot parse zip entry name // Cannot parse zip entry name
return -1; return -1;
} }
pzip = &(zip->archive);
if (pzip->m_zip_mode == MZ_ZIP_MODE_READING) {
zip->entry.index =
mz_zip_reader_locate_file(pzip, zip->entry.name, NULL, 0);
if (zip->entry.index < 0) {
goto cleanup;
}
if (!mz_zip_reader_file_stat(pzip, (mz_uint)zip->entry.index, &stats)) {
goto cleanup;
}
zip->entry.comp_size = stats.m_comp_size;
zip->entry.uncomp_size = stats.m_uncomp_size;
zip->entry.uncomp_crc32 = stats.m_crc32;
zip->entry.offset = stats.m_central_dir_ofs;
zip->entry.header_offset = stats.m_local_header_ofs;
zip->entry.method = stats.m_method;
zip->entry.external_attr = stats.m_external_attr;
zip->entry.m_time = stats.m_time;
return 0;
}
zip->entry.index = (int)zip->archive.m_total_files;
zip->entry.comp_size = 0; zip->entry.comp_size = 0;
zip->entry.uncomp_size = 0; zip->entry.uncomp_size = 0;
zip->entry.uncomp_crc32 = MZ_CRC32_INIT; zip->entry.uncomp_crc32 = MZ_CRC32_INIT;
zip->entry.offset = zip->archive.m_archive_size; zip->entry.offset = zip->archive.m_archive_size;
zip->entry.header_offset = zip->archive.m_archive_size; zip->entry.header_offset = zip->archive.m_archive_size;
memset(zip->entry.header, 0, memset(zip->entry.header, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE * sizeof(mz_uint8));
MZ_ZIP_LOCAL_DIR_HEADER_SIZE * sizeof(mz_uint8));
zip->entry.method = 0; zip->entry.method = 0;
zip->entry.external_attr = 0;
num_alignment_padding_bytes = num_alignment_padding_bytes =
mz_zip_writer_compute_padding_needed_for_file_alignment(pzip); mz_zip_writer_compute_padding_needed_for_file_alignment(pzip);
if (!pzip->m_pState || (pzip->m_zip_mode != MZ_ZIP_MODE_WRITING)) { if (!pzip->m_pState || (pzip->m_zip_mode != MZ_ZIP_MODE_WRITING)) {
// Wrong zip mode // Wrong zip mode
return -1; goto cleanup;
} }
if (zip->level & MZ_ZIP_FLAG_COMPRESSED_DATA) { if (zip->level & MZ_ZIP_FLAG_COMPRESSED_DATA) {
// Wrong zip compression level // Wrong zip compression level
return -1; goto cleanup;
} }
// no zip64 support yet // no zip64 support yet
if ((pzip->m_total_files == 0xFFFF) || if ((pzip->m_total_files == 0xFFFF) ||
@ -256,27 +298,26 @@ int zip_entry_open(struct zip_t *zip, const char *entryname) {
MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE +
entrylen) > 0xFFFFFFFF)) { entrylen) > 0xFFFFFFFF)) {
// No zip64 support yet // No zip64 support yet
return -1; goto cleanup;
} }
if (!mz_zip_writer_write_zeros( if (!mz_zip_writer_write_zeros(pzip, zip->entry.offset,
pzip, zip->entry.offset, num_alignment_padding_bytes +
num_alignment_padding_bytes + sizeof(zip->entry.header))) { sizeof(zip->entry.header))) {
// Cannot memset zip entry header // Cannot memset zip entry header
return -1; goto cleanup;
} }
zip->entry.header_offset += num_alignment_padding_bytes; zip->entry.header_offset += num_alignment_padding_bytes;
if (pzip->m_file_offset_alignment) { if (pzip->m_file_offset_alignment) {
MZ_ASSERT((zip->entry.header_offset & MZ_ASSERT(
(pzip->m_file_offset_alignment - 1)) == 0); (zip->entry.header_offset & (pzip->m_file_offset_alignment - 1)) == 0);
} }
zip->entry.offset += zip->entry.offset += num_alignment_padding_bytes + sizeof(zip->entry.header);
num_alignment_padding_bytes + sizeof(zip->entry.header);
if (pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.offset, zip->entry.name, if (pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.offset, zip->entry.name,
entrylen) != entrylen) { entrylen) != entrylen) {
// Cannot write data to zip entry // Cannot write data to zip entry
return -1; goto cleanup;
} }
zip->entry.offset += entrylen; zip->entry.offset += entrylen;
@ -288,14 +329,88 @@ int zip_entry_open(struct zip_t *zip, const char *entryname) {
if (tdefl_init(&(zip->entry.comp), mz_zip_writer_add_put_buf_callback, if (tdefl_init(&(zip->entry.comp), mz_zip_writer_add_put_buf_callback,
&(zip->entry.state), &(zip->entry.state),
tdefl_create_comp_flags_from_zip_params( (int)tdefl_create_comp_flags_from_zip_params(
level, -15, MZ_DEFAULT_STRATEGY)) != (int)level, -15, MZ_DEFAULT_STRATEGY)) !=
TDEFL_STATUS_OKAY) { TDEFL_STATUS_OKAY) {
// Cannot initialize the zip compressor // Cannot initialize the zip compressor
goto cleanup;
}
}
zip->entry.m_time = time(NULL);
return 0;
cleanup:
CLEANUP(zip->entry.name);
return -1; return -1;
} }
int zip_entry_openbyindex(struct zip_t *zip, int index) {
mz_zip_archive *pZip = NULL;
mz_zip_archive_file_stat stats;
mz_uint namelen;
const mz_uint8 *pHeader;
const char *pFilename;
if (!zip) {
// zip_t handler is not initialized
return -1;
} }
pZip = &(zip->archive);
if (pZip->m_zip_mode != MZ_ZIP_MODE_READING) {
// open by index requires readonly mode
return -1;
}
if (index < 0 || (mz_uint)index >= pZip->m_total_files) {
// index out of range
return -1;
}
if (!(pHeader = &MZ_ZIP_ARRAY_ELEMENT(
&pZip->m_pState->m_central_dir, mz_uint8,
MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets,
mz_uint32, index)))) {
// cannot find header in central directory
return -1;
}
namelen = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS);
pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE;
/*
.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.
*/
zip->entry.name = strrpl(pFilename, namelen, '\\', '/');
if (!zip->entry.name) {
// local entry name is NULL
return -1;
}
if (!mz_zip_reader_file_stat(pZip, (mz_uint)index, &stats)) {
return -1;
}
zip->entry.index = index;
zip->entry.comp_size = stats.m_comp_size;
zip->entry.uncomp_size = stats.m_uncomp_size;
zip->entry.uncomp_crc32 = stats.m_crc32;
zip->entry.offset = stats.m_central_dir_ofs;
zip->entry.header_offset = stats.m_local_header_ofs;
zip->entry.method = stats.m_method;
zip->entry.external_attr = stats.m_external_attr;
zip->entry.m_time = stats.m_time;
return 0; return 0;
} }
@ -304,21 +419,20 @@ int zip_entry_close(struct zip_t *zip) {
mz_uint level; mz_uint level;
tdefl_status done; tdefl_status done;
mz_uint16 entrylen; mz_uint16 entrylen;
time_t t;
struct tm *tm;
mz_uint16 dos_time, dos_date; mz_uint16 dos_time, dos_date;
int status = -1; int status = -1;
if (!zip) { if (!zip) {
// zip_t handler is not initialized // zip_t handler is not initialized
return -1; goto cleanup;
}
if (zip->mode == 'r') {
return 0;
} }
pzip = &(zip->archive); pzip = &(zip->archive);
if (pzip->m_zip_mode == MZ_ZIP_MODE_READING) {
status = 0;
goto cleanup;
}
level = zip->level & 0xF; level = zip->level & 0xF;
if (level) { if (level) {
done = tdefl_compress_buffer(&(zip->entry.comp), "", 0, TDEFL_FINISH); done = tdefl_compress_buffer(&(zip->entry.comp), "", 0, TDEFL_FINISH);
@ -332,20 +446,13 @@ int zip_entry_close(struct zip_t *zip) {
} }
entrylen = (mz_uint16)strlen(zip->entry.name); 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 // no zip64 support yet
if ((zip->entry.comp_size > 0xFFFFFFFF) || if ((zip->entry.comp_size > 0xFFFFFFFF) || (zip->entry.offset > 0xFFFFFFFF)) {
(zip->entry.offset > 0xFFFFFFFF)) {
// No zip64 support, yet // No zip64 support, yet
goto cleanup; goto cleanup;
} }
mz_zip_time_t_to_dos_time(zip->entry.m_time, &dos_time, &dos_date);
if (!mz_zip_writer_create_local_dir_header( if (!mz_zip_writer_create_local_dir_header(
pzip, zip->entry.header, entrylen, 0, zip->entry.uncomp_size, pzip, zip->entry.header, entrylen, 0, zip->entry.uncomp_size,
zip->entry.comp_size, zip->entry.uncomp_crc32, zip->entry.method, 0, zip->entry.comp_size, zip->entry.uncomp_crc32, zip->entry.method, 0,
@ -355,17 +462,17 @@ int zip_entry_close(struct zip_t *zip) {
} }
if (pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.header_offset, if (pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.header_offset,
zip->entry.header, sizeof(zip->entry.header)) != zip->entry.header,
sizeof(zip->entry.header)) { sizeof(zip->entry.header)) != sizeof(zip->entry.header)) {
// Cannot write zip entry header // Cannot write zip entry header
goto cleanup; goto cleanup;
} }
if (!mz_zip_writer_add_to_central_dir( if (!mz_zip_writer_add_to_central_dir(
pzip, zip->entry.name, entrylen, NULL, 0, "", 0, pzip, zip->entry.name, entrylen, NULL, 0, "", 0,
zip->entry.uncomp_size, zip->entry.comp_size, zip->entry.uncomp_size, zip->entry.comp_size, zip->entry.uncomp_crc32,
zip->entry.uncomp_crc32, zip->entry.method, 0, dos_time, dos_date, zip->entry.method, 0, dos_time, dos_date, zip->entry.header_offset,
zip->entry.header_offset, 0)) { zip->entry.external_attr)) {
// Cannot write to zip central dir // Cannot write to zip central dir
goto cleanup; goto cleanup;
} }
@ -375,10 +482,54 @@ int zip_entry_close(struct zip_t *zip) {
status = 0; status = 0;
cleanup: cleanup:
if (zip) {
zip->entry.m_time = 0;
CLEANUP(zip->entry.name); CLEANUP(zip->entry.name);
}
return status; return status;
} }
const char *zip_entry_name(struct zip_t *zip) {
if (!zip) {
// zip_t handler is not initialized
return NULL;
}
return zip->entry.name;
}
int zip_entry_index(struct zip_t *zip) {
if (!zip) {
// zip_t handler is not initialized
return -1;
}
return zip->entry.index;
}
int zip_entry_isdir(struct zip_t *zip) {
if (!zip) {
// zip_t handler is not initialized
return -1;
}
if (zip->entry.index < 0) {
// zip entry is not opened
return -1;
}
return (int)mz_zip_reader_is_file_a_directory(&zip->archive,
(mz_uint)zip->entry.index);
}
unsigned long long zip_entry_size(struct zip_t *zip) {
return zip ? zip->entry.uncomp_size : 0;
}
unsigned int zip_entry_crc32(struct zip_t *zip) {
return zip ? zip->entry.uncomp_crc32 : 0;
}
int zip_entry_write(struct zip_t *zip, const void *buf, size_t bufsize) { int zip_entry_write(struct zip_t *zip, const void *buf, size_t bufsize) {
mz_uint level; mz_uint level;
mz_zip_archive *pzip = NULL; mz_zip_archive *pzip = NULL;
@ -421,15 +572,34 @@ int zip_entry_fwrite(struct zip_t *zip, const char *filename) {
int status = 0; int status = 0;
size_t n = 0; size_t n = 0;
FILE *stream = NULL; FILE *stream = NULL;
mz_uint8 buf[MZ_ZIP_MAX_IO_BUF_SIZE] = {0}; mz_uint8 buf[MZ_ZIP_MAX_IO_BUF_SIZE];
struct MZ_FILE_STAT_STRUCT file_stat;
if (!zip) { if (!zip) {
// zip_t handler is not initialized // zip_t handler is not initialized
return -1; return -1;
} }
stream = fopen(filename, "rb"); memset(buf, 0, MZ_ZIP_MAX_IO_BUF_SIZE);
if (!stream) { memset((void *)&file_stat, 0, sizeof(struct MZ_FILE_STAT_STRUCT));
if (MZ_FILE_STAT(filename, &file_stat) != 0) {
// problem getting information - check errno
return -1;
}
if ((file_stat.st_mode & 0200) == 0) {
// MS-DOS read-only attribute
zip->entry.external_attr |= 0x01;
}
zip->entry.external_attr |= (mz_uint32)((file_stat.st_mode & 0xFFFF) << 16);
zip->entry.m_time = file_stat.st_mtime;
#if defined(_MSC_VER)
if (fopen_s(&stream, filename, "rb"))
#else
if (!(stream = fopen(filename, "rb")))
#endif
{
// Cannot open filename // Cannot open filename
return -1; return -1;
} }
@ -446,53 +616,104 @@ int zip_entry_fwrite(struct zip_t *zip, const char *filename) {
return status; return status;
} }
int zip_entry_read(struct zip_t *zip, void **buf, size_t *bufsize) { ssize_t zip_entry_read(struct zip_t *zip, void **buf, size_t *bufsize) {
mz_zip_archive *pzip = NULL; mz_zip_archive *pzip = NULL;
mz_uint idx; mz_uint idx;
size_t size = 0;
if (!zip) { if (!zip) {
// zip_t handler is not initialized // zip_t handler is not initialized
return -1; return -1;
} }
if (zip->mode != 'r' || zip->entry.index < 0) { pzip = &(zip->archive);
if (pzip->m_zip_mode != MZ_ZIP_MODE_READING || zip->entry.index < 0) {
// the entry is not found or we do not have read access // the entry is not found or we do not have read access
return -1; return -1;
} }
pzip = &(zip->archive);
idx = (mz_uint)zip->entry.index; idx = (mz_uint)zip->entry.index;
if (mz_zip_reader_is_file_a_directory(pzip, idx)) { if (mz_zip_reader_is_file_a_directory(pzip, idx)) {
// the entry is a directory // the entry is a directory
return -1; return -1;
} }
*buf = mz_zip_reader_extract_to_heap(pzip, idx, bufsize, 0); *buf = mz_zip_reader_extract_to_heap(pzip, idx, &size, 0);
return (*buf) ? 0 : -1; if (*buf && bufsize) {
*bufsize = size;
}
return size;
}
ssize_t zip_entry_noallocread(struct zip_t *zip, void *buf, size_t bufsize) {
mz_zip_archive *pzip = NULL;
if (!zip) {
// zip_t handler is not initialized
return -1;
}
pzip = &(zip->archive);
if (pzip->m_zip_mode != MZ_ZIP_MODE_READING || zip->entry.index < 0) {
// the entry is not found or we do not have read access
return -1;
}
if (!mz_zip_reader_extract_to_mem_no_alloc(pzip, (mz_uint)zip->entry.index,
buf, bufsize, 0, NULL, 0)) {
return -1;
}
return (ssize_t)zip->entry.uncomp_size;
} }
int zip_entry_fread(struct zip_t *zip, const char *filename) { int zip_entry_fread(struct zip_t *zip, const char *filename) {
mz_zip_archive *pzip = NULL; mz_zip_archive *pzip = NULL;
mz_uint idx; mz_uint idx;
#if defined(_MSC_VER)
#else
mz_uint32 xattr = 0;
#endif
mz_zip_archive_file_stat info;
if (!zip) { if (!zip) {
// zip_t handler is not initialized // zip_t handler is not initialized
return -1; return -1;
} }
if (zip->mode != 'r' || zip->entry.index < 0) { memset((void *)&info, 0, sizeof(mz_zip_archive_file_stat));
pzip = &(zip->archive);
if (pzip->m_zip_mode != MZ_ZIP_MODE_READING || zip->entry.index < 0) {
// the entry is not found or we do not have read access // the entry is not found or we do not have read access
return -1; return -1;
} }
pzip = &(zip->archive);
idx = (mz_uint)zip->entry.index; idx = (mz_uint)zip->entry.index;
if (mz_zip_reader_is_file_a_directory(pzip, idx)) { if (mz_zip_reader_is_file_a_directory(pzip, idx)) {
// the entry is a directory // the entry is a directory
return -1; return -1;
} }
return (mz_zip_reader_extract_to_file(pzip, idx, filename, 0)) ? 0 : -1; if (!mz_zip_reader_extract_to_file(pzip, idx, filename, 0)) {
return -1;
}
#if defined(_MSC_VER)
#else
if (!mz_zip_reader_file_stat(pzip, idx, &info)) {
// Cannot get information about zip archive;
return -1;
}
xattr = (info.m_external_attr >> 16) & 0xFFFF;
if (xattr > 0) {
if (chmod(filename, (mode_t)xattr) < 0) {
return -1;
}
}
#endif
return 0;
} }
int zip_entry_extract(struct zip_t *zip, int zip_entry_extract(struct zip_t *zip,
@ -507,23 +728,33 @@ int zip_entry_extract(struct zip_t *zip,
return -1; return -1;
} }
if (zip->mode != 'r' || zip->entry.index < 0) { pzip = &(zip->archive);
if (pzip->m_zip_mode != MZ_ZIP_MODE_READING || zip->entry.index < 0) {
// the entry is not found or we do not have read access // the entry is not found or we do not have read access
return -1; return -1;
} }
pzip = &(zip->archive);
idx = (mz_uint)zip->entry.index; idx = (mz_uint)zip->entry.index;
return (mz_zip_reader_extract_to_callback(pzip, idx, on_extract, arg, 0)) return (mz_zip_reader_extract_to_callback(pzip, idx, on_extract, arg, 0))
? 0 ? 0
: -1; : -1;
} }
int zip_total_entries(struct zip_t *zip) {
if (!zip) {
// zip_t handler is not initialized
return -1;
}
return (int)zip->archive.m_total_files;
}
int zip_create(const char *zipname, const char *filenames[], size_t len) { int zip_create(const char *zipname, const char *filenames[], size_t len) {
int status = 0; int status = 0;
size_t i; size_t i;
mz_zip_archive zip_archive; mz_zip_archive zip_archive;
struct MZ_FILE_STAT_STRUCT file_stat;
mz_uint32 ext_attributes = 0;
if (!zipname || strlen(zipname) < 1) { if (!zipname || strlen(zipname) < 1) {
// zip_t archive name is empty or NULL // zip_t archive name is empty or NULL
@ -541,6 +772,8 @@ int zip_create(const char *zipname, const char *filenames[], size_t len) {
return -1; return -1;
} }
memset((void *)&file_stat, 0, sizeof(struct MZ_FILE_STAT_STRUCT));
for (i = 0; i < len; ++i) { for (i = 0; i < len; ++i) {
const char *name = filenames[i]; const char *name = filenames[i];
if (!name) { if (!name) {
@ -548,8 +781,20 @@ int zip_create(const char *zipname, const char *filenames[], size_t len) {
break; break;
} }
if (!mz_zip_writer_add_file(&zip_archive, basename(name), name, "", 0, if (MZ_FILE_STAT(name, &file_stat) != 0) {
ZIP_DEFAULT_COMPRESSION_LEVEL)) { // problem getting information - check errno
return -1;
}
if ((file_stat.st_mode & 0200) == 0) {
// MS-DOS read-only attribute
ext_attributes |= 0x01;
}
ext_attributes |= (mz_uint32)((file_stat.st_mode & 0xFFFF) << 16);
if (!mz_zip_writer_add_file(&zip_archive, base_name(name), name, "", 0,
ZIP_DEFAULT_COMPRESSION_LEVEL,
ext_attributes)) {
// Cannot add file to zip_archive // Cannot add file to zip_archive
status = -1; status = -1;
break; break;
@ -565,11 +810,15 @@ int zip_extract(const char *zipname, const char *dir,
int (*on_extract)(const char *filename, void *arg), void *arg) { int (*on_extract)(const char *filename, void *arg), void *arg) {
int status = -1; int status = -1;
mz_uint i, n; mz_uint i, n;
char path[MAX_PATH + 1] = {0}; char path[MAX_PATH + 1];
char symlink_to[MAX_PATH + 1];
mz_zip_archive zip_archive; mz_zip_archive zip_archive;
mz_zip_archive_file_stat info; mz_zip_archive_file_stat info;
size_t dirlen = 0; size_t dirlen = 0;
mz_uint32 xattr = 0;
memset(path, 0, sizeof(path));
memset(symlink_to, 0, sizeof(symlink_to));
if (!memset(&(zip_archive), 0, sizeof(zip_archive))) { if (!memset(&(zip_archive), 0, sizeof(zip_archive))) {
// Cannot memset zip archive // Cannot memset zip archive
return -1; return -1;
@ -591,9 +840,16 @@ int zip_extract(const char *zipname, const char *dir,
return -1; return -1;
} }
memset((void *)&info, 0, sizeof(mz_zip_archive_file_stat));
#if defined(_MSC_VER)
strcpy_s(path, MAX_PATH, dir);
#else
strcpy(path, dir); strcpy(path, dir);
#endif
if (!ISSLASH(path[dirlen - 1])) { if (!ISSLASH(path[dirlen - 1])) {
#if defined _WIN32 || defined __WIN32__ #if defined(_WIN32) || defined(__WIN32__)
path[dirlen] = '\\'; path[dirlen] = '\\';
#else #else
path[dirlen] = '/'; path[dirlen] = '/';
@ -608,12 +864,31 @@ int zip_extract(const char *zipname, const char *dir,
// Cannot get information about zip archive; // Cannot get information about zip archive;
goto out; goto out;
} }
#if defined(_MSC_VER)
strncpy_s(&path[dirlen], MAX_PATH - dirlen, info.m_filename,
MAX_PATH - dirlen);
#else
strncpy(&path[dirlen], info.m_filename, MAX_PATH - dirlen); strncpy(&path[dirlen], info.m_filename, MAX_PATH - dirlen);
#endif
if (mkpath(path) < 0) { if (mkpath(path) < 0) {
// Cannot make a path // Cannot make a path
goto out; goto out;
} }
if ((((info.m_version_made_by >> 8) == 3) || ((info.m_version_made_by >> 8) == 19)) // if zip is produced on Unix or macOS (3 and 19 from section 4.4.2.2 of zip standard)
&& info.m_external_attr & (0x20 << 24)) { // and has sym link attribute (0x80 is file, 0x40 is directory)
#if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER) || \
defined(__MINGW32__)
#else
if (info.m_uncomp_size > MAX_PATH || !mz_zip_reader_extract_to_mem_no_alloc(&zip_archive, i, symlink_to, MAX_PATH, 0, NULL, 0)) {
goto out;
}
symlink_to[info.m_uncomp_size] = '\0';
if (symlink(symlink_to, path) != 0) {
goto out;
}
#endif
} else {
if (!mz_zip_reader_is_file_a_directory(&zip_archive, i)) { if (!mz_zip_reader_is_file_a_directory(&zip_archive, i)) {
if (!mz_zip_reader_extract_to_file(&zip_archive, i, path, 0)) { if (!mz_zip_reader_extract_to_file(&zip_archive, i, path, 0)) {
// Cannot extract zip archive to file // Cannot extract zip archive to file
@ -621,6 +896,17 @@ int zip_extract(const char *zipname, const char *dir,
} }
} }
#if defined(_MSC_VER)
#else
xattr = (info.m_external_attr >> 16) & 0xFFFF;
if (xattr > 0) {
if (chmod(path, (mode_t)xattr) < 0) {
goto out;
}
}
#endif
}
if (on_extract) { if (on_extract) {
if (on_extract(path, arg) < 0) { if (on_extract(path, arg) < 0) {
goto out; goto out;

View File

@ -13,11 +13,24 @@
#define ZIP_H #define ZIP_H
#include <string.h> #include <string.h>
#include <sys/types.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
#if !defined(_SSIZE_T_DEFINED) && !defined(_SSIZE_T_DEFINED_) && \
!defined(_SSIZE_T) && !defined(_SSIZE_T_) && !defined(__ssize_t_defined)
#define _SSIZE_T
// 64-bit Windows is the only mainstream platform
// where sizeof(long) != sizeof(void*)
#ifdef _WIN64
typedef long long ssize_t; /* byte count or error */
#else
typedef long ssize_t; /* byte count or error */
#endif
#endif
#ifndef MAX_PATH #ifndef MAX_PATH
#define MAX_PATH 32767 /* # chars in a path name including NULL */ #define MAX_PATH 32767 /* # chars in a path name including NULL */
#endif #endif
@ -47,7 +60,7 @@ struct zip_t;
extern struct zip_t *zip_open(const char *zipname, int level, char mode); extern struct zip_t *zip_open(const char *zipname, int level, char mode);
/* /*
Closes zip archive, releases resources - always finalize. Closes the zip archive, releases resources - always finalize.
Args: Args:
zip: zip archive handler. zip: zip archive handler.
@ -55,7 +68,10 @@ extern struct zip_t *zip_open(const char *zipname, int level, char mode);
extern void zip_close(struct zip_t *zip); extern void zip_close(struct zip_t *zip);
/* /*
Opens a new entry for writing in a zip archive. Opens an entry by name in the zip archive.
For zip archive opened in 'w' or 'a' mode the function will append
a new entry. In readonly mode the function tries to locate the entry
in global dictionary.
Args: Args:
zip: zip archive handler. zip: zip archive handler.
@ -66,6 +82,19 @@ extern void zip_close(struct zip_t *zip);
*/ */
extern int zip_entry_open(struct zip_t *zip, const char *entryname); extern int zip_entry_open(struct zip_t *zip, const char *entryname);
/*
Opens a new entry by index in the zip archive.
This function is only valid if zip archive was opened in 'r' (readonly) mode.
Args:
zip: zip archive handler.
index: index in local dictionary.
Returns:
The return code - 0 on success, negative number (< 0) on error.
*/
extern int zip_entry_openbyindex(struct zip_t *zip, int index);
/* /*
Closes a zip entry, flushes buffer and releases resources. Closes a zip entry, flushes buffer and releases resources.
@ -77,6 +106,67 @@ extern int zip_entry_open(struct zip_t *zip, const char *entryname);
*/ */
extern int zip_entry_close(struct zip_t *zip); extern int zip_entry_close(struct zip_t *zip);
/*
Returns a local name of the current zip entry.
The main difference between user's entry name and local entry name
is optional relative path.
Following .ZIP File Format Specification - 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.
Args:
zip: zip archive handler.
Returns:
The pointer to the current zip entry name, or NULL on error.
*/
extern const char *zip_entry_name(struct zip_t *zip);
/*
Returns an index of the current zip entry.
Args:
zip: zip archive handler.
Returns:
The index on success, negative number (< 0) on error.
*/
extern int zip_entry_index(struct zip_t *zip);
/*
Determines if the current zip entry is a directory entry.
Args:
zip: zip archive handler.
Returns:
The return code - 1 (true), 0 (false), negative number (< 0) on error.
*/
extern int zip_entry_isdir(struct zip_t *zip);
/*
Returns an uncompressed size of the current zip entry.
Args:
zip: zip archive handler.
Returns:
The uncompressed size in bytes.
*/
extern unsigned long long zip_entry_size(struct zip_t *zip);
/*
Returns CRC-32 checksum of the current zip entry.
Args:
zip: zip archive handler.
Returns:
The CRC-32 checksum.
*/
extern unsigned int zip_entry_crc32(struct zip_t *zip);
/* /*
Compresses an input buffer for the current zip entry. Compresses an input buffer for the current zip entry.
@ -116,9 +206,31 @@ extern int zip_entry_fwrite(struct zip_t *zip, const char *filename);
- for large entries, please take a look at zip_entry_extract function. - for large entries, please take a look at zip_entry_extract function.
Returns: Returns:
The return code - 0 on success, negative number (< 0) on error. The return code - the number of bytes actually read on success.
Otherwise a -1 on error.
*/ */
extern int zip_entry_read(struct zip_t *zip, void **buf, size_t *bufsize); extern ssize_t zip_entry_read(struct zip_t *zip, void **buf, size_t *bufsize);
/*
Extracts the current zip entry into a memory buffer using no memory
allocation.
Args:
zip: zip archive handler.
buf: preallocated output buffer.
bufsize: output buffer size (in bytes).
Note:
- ensure supplied output buffer is large enough.
- zip_entry_size function (returns uncompressed size for the current entry)
can be handy to estimate how big buffer is needed.
- for large entries, please take a look at zip_entry_extract function.
Returns:
The return code - the number of bytes actually read on success.
Otherwise a -1 on error (e.g. bufsize is not large enough).
*/
extern ssize_t zip_entry_noallocread(struct zip_t *zip, void *buf, size_t bufsize);
/* /*
Extracts the current zip entry into output file. Extracts the current zip entry into output file.
@ -133,7 +245,7 @@ extern int zip_entry_read(struct zip_t *zip, void **buf, size_t *bufsize);
extern int zip_entry_fread(struct zip_t *zip, const char *filename); extern int zip_entry_fread(struct zip_t *zip, const char *filename);
/* /*
Extract the current zip entry using a callback function (on_extract). Extracts the current zip entry using a callback function (on_extract).
Args: Args:
zip: zip archive handler. zip: zip archive handler.
@ -144,13 +256,24 @@ extern int zip_entry_fread(struct zip_t *zip, const char *filename);
Returns: Returns:
The return code - 0 on success, negative number (< 0) on error. The return code - 0 on success, negative number (< 0) on error.
*/ */
extern int zip_entry_extract(struct zip_t *zip, extern int
size_t (*on_extract)(void *arg, zip_entry_extract(struct zip_t *zip,
unsigned long long offset, size_t (*on_extract)(void *arg, unsigned long long offset,
const void *data, const void *data, size_t size),
size_t size),
void *arg); void *arg);
/*
Returns the number of all entries (files and directories) in the zip archive.
Args:
zip: zip archive handler.
Returns:
The return code - the number of entries on success,
negative number (< 0) on error.
*/
extern int zip_total_entries(struct zip_t *zip);
/* /*
Creates a new archive and puts files into a single zip archive. Creates a new archive and puts files into a single zip archive.

View File

@ -1,7 +1,19 @@
cmake_minimum_required(VERSION 2.8) cmake_minimum_required(VERSION 2.8)
if ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "AppleClang")
if(ENABLE_COVERAGE)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g ")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ftest-coverage")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage")
endif()
endif ()
# test # test
include_directories(../src) include_directories(../src)
add_executable(test.exe test.c ../src/zip.c) add_executable(test.exe test.c ../src/zip.c)
add_executable(test_miniz.exe test_miniz.c)
add_test(NAME test COMMAND test.exe) add_test(NAME test COMMAND test.exe)
add_test(NAME test_miniz COMMAND test_miniz.exe)

View File

@ -4,10 +4,34 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/stat.h>
#define ZIPNAME "test.zip" #if defined(_MSC_VER) || defined(__MINGW64__) || defined(__MINGW32__)
#define MZ_FILE_STAT_STRUCT _stat
#define MZ_FILE_STAT _stat
#else
#define MZ_FILE_STAT_STRUCT stat
#define MZ_FILE_STAT stat
#endif
#define ZIPNAME "test.zip\0"
#define TESTDATA1 "Some test data 1...\0" #define TESTDATA1 "Some test data 1...\0"
#define CRC32DATA1 2220805626
#define TESTDATA2 "Some test data 2...\0" #define TESTDATA2 "Some test data 2...\0"
#define CRC32DATA2 2532008468
#define RFILE "4.txt\0"
#define RMODE 0100444
#define WFILE "6.txt\0"
#define WMODE 0100666
#define XFILE "7.txt\0"
#define XMODE 0100777
#define UNUSED(x) (void)x
static int total_entries = 0;
static void test_write(void) { static void test_write(void) {
struct zip_t *zip = zip_open(ZIPNAME, ZIP_DEFAULT_COMPRESSION_LEVEL, 'w'); struct zip_t *zip = zip_open(ZIPNAME, ZIP_DEFAULT_COMPRESSION_LEVEL, 'w');
@ -15,6 +39,11 @@ static void test_write(void) {
assert(0 == zip_entry_open(zip, "test/test-1.txt")); assert(0 == zip_entry_open(zip, "test/test-1.txt"));
assert(0 == zip_entry_write(zip, TESTDATA1, strlen(TESTDATA1))); assert(0 == zip_entry_write(zip, TESTDATA1, strlen(TESTDATA1)));
assert(0 == strcmp(zip_entry_name(zip), "test/test-1.txt"));
assert(total_entries == zip_entry_index(zip));
assert(strlen(TESTDATA1) == zip_entry_size(zip));
assert(CRC32DATA1 == zip_entry_crc32(zip));
++total_entries;
assert(0 == zip_entry_close(zip)); assert(0 == zip_entry_close(zip));
zip_close(zip); zip_close(zip);
@ -25,7 +54,31 @@ static void test_append(void) {
assert(zip != NULL); assert(zip != NULL);
assert(0 == zip_entry_open(zip, "test\\test-2.txt")); assert(0 == zip_entry_open(zip, "test\\test-2.txt"));
assert(0 == strcmp(zip_entry_name(zip), "test/test-2.txt"));
assert(total_entries == zip_entry_index(zip));
assert(0 == zip_entry_write(zip, TESTDATA2, strlen(TESTDATA2))); assert(0 == zip_entry_write(zip, TESTDATA2, strlen(TESTDATA2)));
assert(strlen(TESTDATA2) == zip_entry_size(zip));
assert(CRC32DATA2 == zip_entry_crc32(zip));
++total_entries;
assert(0 == zip_entry_close(zip));
assert(0 == zip_entry_open(zip, "test\\empty/"));
assert(0 == strcmp(zip_entry_name(zip), "test/empty/"));
assert(0 == zip_entry_size(zip));
assert(0 == zip_entry_crc32(zip));
assert(total_entries == zip_entry_index(zip));
++total_entries;
assert(0 == zip_entry_close(zip));
assert(0 == zip_entry_open(zip, "empty/"));
assert(0 == strcmp(zip_entry_name(zip), "empty/"));
assert(0 == zip_entry_size(zip));
assert(0 == zip_entry_crc32(zip));
assert(total_entries == zip_entry_index(zip));
++total_entries;
assert(0 == zip_entry_close(zip)); assert(0 == zip_entry_close(zip));
zip_close(zip); zip_close(zip);
@ -33,23 +86,59 @@ static void test_append(void) {
static void test_read(void) { static void test_read(void) {
char *buf = NULL; char *buf = NULL;
size_t bufsize; ssize_t bufsize;
size_t buftmp;
struct zip_t *zip = zip_open(ZIPNAME, 0, 'r'); struct zip_t *zip = zip_open(ZIPNAME, 0, 'r');
assert(zip != NULL); assert(zip != NULL);
assert(0 == zip_entry_open(zip, "test\\test-1.txt")); assert(0 == zip_entry_open(zip, "test\\test-1.txt"));
assert(0 == zip_entry_read(zip, (void **)&buf, &bufsize)); assert(strlen(TESTDATA1) == zip_entry_size(zip));
assert(CRC32DATA1 == zip_entry_crc32(zip));
bufsize = zip_entry_read(zip, (void **)&buf, &buftmp);
assert(bufsize == strlen(TESTDATA1)); assert(bufsize == strlen(TESTDATA1));
assert((size_t)bufsize == buftmp);
assert(0 == strncmp(buf, TESTDATA1, bufsize)); assert(0 == strncmp(buf, TESTDATA1, bufsize));
assert(0 == zip_entry_close(zip)); assert(0 == zip_entry_close(zip));
free(buf); free(buf);
buf = NULL; buf = NULL;
assert(0 == zip_entry_open(zip, "test/test-2.txt"));
assert(strlen(TESTDATA2) == zip_entry_size(zip));
assert(CRC32DATA2 == zip_entry_crc32(zip));
bufsize = zip_entry_read(zip, (void **)&buf, NULL);
assert((size_t)bufsize == strlen(TESTDATA2));
assert(0 == strncmp(buf, TESTDATA2, (size_t)bufsize));
assert(0 == zip_entry_close(zip));
free(buf);
buf = NULL;
bufsize = 0; bufsize = 0;
assert(0 == zip_entry_open(zip, "test\\empty/"));
assert(0 == strcmp(zip_entry_name(zip), "test/empty/"));
assert(0 == zip_entry_size(zip));
assert(0 == zip_entry_crc32(zip));
assert(0 == zip_entry_close(zip));
buftmp = strlen(TESTDATA2);
buf = calloc(buftmp, sizeof(char));
assert(0 == zip_entry_open(zip, "test/test-2.txt")); assert(0 == zip_entry_open(zip, "test/test-2.txt"));
assert(0 == zip_entry_read(zip, (void **)&buf, &bufsize));
assert(bufsize == strlen(TESTDATA2)); bufsize = zip_entry_noallocread(zip, (void *)buf, buftmp);
assert(0 == strncmp(buf, TESTDATA2, bufsize)); assert(buftmp == (size_t)bufsize);
assert(0 == strncmp(buf, TESTDATA2, buftmp));
assert(0 == zip_entry_close(zip));
free(buf);
buf = NULL;
buftmp = strlen(TESTDATA1);
buf = calloc(buftmp, sizeof(char));
assert(0 == zip_entry_open(zip, "test/test-1.txt"));
bufsize = zip_entry_noallocread(zip, (void *)buf, buftmp);
assert(buftmp == (size_t)bufsize);
assert(0 == strncmp(buf, TESTDATA1, buftmp));
assert(0 == zip_entry_close(zip)); assert(0 == zip_entry_close(zip));
free(buf); free(buf);
buf = NULL; buf = NULL;
@ -65,6 +154,8 @@ struct buffer_t {
static size_t on_extract(void *arg, unsigned long long offset, const void *data, static size_t on_extract(void *arg, unsigned long long offset, const void *data,
size_t size) { size_t size) {
UNUSED(offset);
struct buffer_t *buf = (struct buffer_t *)arg; struct buffer_t *buf = (struct buffer_t *)arg;
buf->data = realloc(buf->data, buf->size + size + 1); buf->data = realloc(buf->data, buf->size + size + 1);
assert(NULL != buf->data); assert(NULL != buf->data);
@ -77,10 +168,11 @@ static size_t on_extract(void *arg, unsigned long long offset, const void *data,
} }
static void test_extract(void) { static void test_extract(void) {
struct buffer_t buf = {0}; struct buffer_t buf;
struct zip_t *zip = zip_open(ZIPNAME, 0, 'r'); struct zip_t *zip = zip_open(ZIPNAME, 0, 'r');
assert(zip != NULL); assert(zip != NULL);
memset((void *)&buf, 0, sizeof(struct buffer_t));
assert(0 == zip_entry_open(zip, "test/test-1.txt")); assert(0 == zip_entry_open(zip, "test/test-1.txt"));
assert(0 == zip_entry_extract(zip, on_extract, &buf)); assert(0 == zip_entry_extract(zip, on_extract, &buf));
@ -95,11 +187,273 @@ static void test_extract(void) {
zip_close(zip); zip_close(zip);
} }
static void test_total_entries(void) {
struct zip_t *zip = zip_open(ZIPNAME, 0, 'r');
assert(zip != NULL);
int n = zip_total_entries(zip);
zip_close(zip);
assert(n == total_entries);
}
static void test_entry_name(void) {
struct zip_t *zip = zip_open(ZIPNAME, 0, 'r');
assert(zip != NULL);
assert(zip_entry_name(zip) == NULL);
assert(0 == zip_entry_open(zip, "test\\test-1.txt"));
assert(NULL != zip_entry_name(zip));
assert(0 == strcmp(zip_entry_name(zip), "test/test-1.txt"));
assert(strlen(TESTDATA1) == zip_entry_size(zip));
assert(CRC32DATA1 == zip_entry_crc32(zip));
assert(0 == zip_entry_index(zip));
assert(0 == zip_entry_close(zip));
assert(0 == zip_entry_open(zip, "test/test-2.txt"));
assert(NULL != zip_entry_name(zip));
assert(0 == strcmp(zip_entry_name(zip), "test/test-2.txt"));
assert(strlen(TESTDATA2) == zip_entry_size(zip));
assert(CRC32DATA2 == zip_entry_crc32(zip));
assert(1 == zip_entry_index(zip));
assert(0 == zip_entry_close(zip));
zip_close(zip);
}
static void test_entry_index(void) {
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_index(zip));
assert(0 == strcmp(zip_entry_name(zip), "test/test-1.txt"));
assert(strlen(TESTDATA1) == zip_entry_size(zip));
assert(CRC32DATA1 == zip_entry_crc32(zip));
assert(0 == zip_entry_close(zip));
assert(0 == zip_entry_open(zip, "test/test-2.txt"));
assert(1 == zip_entry_index(zip));
assert(0 == strcmp(zip_entry_name(zip), "test/test-2.txt"));
assert(strlen(TESTDATA2) == zip_entry_size(zip));
assert(CRC32DATA2 == zip_entry_crc32(zip));
assert(0 == zip_entry_close(zip));
zip_close(zip);
}
static void test_entry_openbyindex(void) {
struct zip_t *zip = zip_open(ZIPNAME, 0, 'r');
assert(zip != NULL);
assert(0 == zip_entry_openbyindex(zip, 1));
assert(1 == zip_entry_index(zip));
assert(strlen(TESTDATA2) == zip_entry_size(zip));
assert(CRC32DATA2 == zip_entry_crc32(zip));
assert(0 == strcmp(zip_entry_name(zip), "test/test-2.txt"));
assert(0 == zip_entry_close(zip));
assert(0 == zip_entry_openbyindex(zip, 0));
assert(0 == zip_entry_index(zip));
assert(strlen(TESTDATA1) == zip_entry_size(zip));
assert(CRC32DATA1 == zip_entry_crc32(zip));
assert(0 == strcmp(zip_entry_name(zip), "test/test-1.txt"));
assert(0 == zip_entry_close(zip));
zip_close(zip);
}
static void test_list_entries(void) {
struct zip_t *zip = zip_open(ZIPNAME, 0, 'r');
assert(zip != NULL);
int i = 0, n = zip_total_entries(zip);
for (; i < n; ++i) {
assert(0 == zip_entry_openbyindex(zip, i));
fprintf(stdout, "[%d]: %s", i, zip_entry_name(zip));
if (zip_entry_isdir(zip)) {
fprintf(stdout, " (DIR)");
}
fprintf(stdout, "\n");
assert(0 == zip_entry_close(zip));
}
zip_close(zip);
}
static void test_fwrite(void) {
const char *filename = WFILE;
FILE *stream = NULL;
struct zip_t *zip = NULL;
#if defined(_MSC_VER)
if (0 != fopen_s(&stream, filename, "w+"))
#else
if (!(stream = fopen(filename, "w+")))
#endif
{
// Cannot open filename
fprintf(stdout, "Cannot open filename\n");
assert(0 == -1);
}
fwrite(TESTDATA1, sizeof(char), strlen(TESTDATA1), stream);
assert(0 == fclose(stream));
zip = zip_open(ZIPNAME, 9, 'w');
assert(zip != NULL);
assert(0 == zip_entry_open(zip, WFILE));
assert(0 == zip_entry_fwrite(zip, WFILE));
assert(0 == zip_entry_close(zip));
zip_close(zip);
remove(WFILE);
remove(ZIPNAME);
}
static void test_exe_permissions(void) {
#if defined(_WIN32) || defined(__WIN32__)
#else
struct MZ_FILE_STAT_STRUCT file_stats;
const char *filenames[] = {XFILE};
FILE *f = fopen(XFILE, "w");
fclose(f);
chmod(XFILE, XMODE);
remove(ZIPNAME);
assert(0 == zip_create(ZIPNAME, filenames, 1));
remove(XFILE);
assert(0 == zip_extract(ZIPNAME, ".", NULL, NULL));
assert(0 == MZ_FILE_STAT(XFILE, &file_stats));
assert(XMODE == file_stats.st_mode);
remove(XFILE);
remove(ZIPNAME);
#endif
}
static void test_read_permissions(void) {
#if defined(_MSC_VER)
#else
struct MZ_FILE_STAT_STRUCT file_stats;
const char *filenames[] = {RFILE};
FILE *f = fopen(RFILE, "w");
fclose(f);
chmod(RFILE, RMODE);
remove(ZIPNAME);
assert(0 == zip_create(ZIPNAME, filenames, 1));
// chmod from 444 to 666 to be able delete the file on windows
chmod(RFILE, WMODE);
remove(RFILE);
assert(0 == zip_extract(ZIPNAME, ".", NULL, NULL));
assert(0 == MZ_FILE_STAT(RFILE, &file_stats));
assert(RMODE == file_stats.st_mode);
chmod(RFILE, WMODE);
remove(RFILE);
remove(ZIPNAME);
#endif
}
static void test_write_permissions(void) {
#if defined(_MSC_VER)
#else
struct MZ_FILE_STAT_STRUCT file_stats;
const char *filenames[] = {WFILE};
FILE *f = fopen(WFILE, "w");
fclose(f);
chmod(WFILE, WMODE);
remove(ZIPNAME);
assert(0 == zip_create(ZIPNAME, filenames, 1));
remove(WFILE);
assert(0 == zip_extract(ZIPNAME, ".", NULL, NULL));
assert(0 == MZ_FILE_STAT(WFILE, &file_stats));
assert(WMODE == file_stats.st_mode);
remove(WFILE);
remove(ZIPNAME);
#endif
}
static void test_mtime(void) {
struct MZ_FILE_STAT_STRUCT file_stat1, file_stat2;
const char *filename = WFILE;
FILE *stream = NULL;
struct zip_t *zip = NULL;
#if defined(_MSC_VER)
if (0 != fopen_s(&stream, filename, "w+"))
#else
if (!(stream = fopen(filename, "w+")))
#endif
{
// Cannot open filename
fprintf(stdout, "Cannot open filename\n");
assert(0 == -1);
}
fwrite(TESTDATA1, sizeof(char), strlen(TESTDATA1), stream);
assert(0 == fclose(stream));
memset(&file_stat1, 0, sizeof(file_stat1));
memset(&file_stat2, 0, sizeof(file_stat2));
zip = zip_open(ZIPNAME, ZIP_DEFAULT_COMPRESSION_LEVEL, 'w');
assert(zip != NULL);
assert(0 == zip_entry_open(zip, filename));
assert(0 == zip_entry_fwrite(zip, filename));
assert(0 == zip_entry_close(zip));
zip_close(zip);
assert(0 == MZ_FILE_STAT(filename, &file_stat1));
remove(filename);
assert(0 == zip_extract(ZIPNAME, ".", NULL, NULL));
assert(0 == MZ_FILE_STAT(filename, &file_stat2));
fprintf(stdout, "file_stat1.st_mtime: %lu\n", file_stat1.st_mtime);
fprintf(stdout, "file_stat2.st_mtime: %lu\n", file_stat2.st_mtime);
assert(labs(file_stat1.st_mtime - file_stat2.st_mtime) <= 1);
remove(filename);
remove(ZIPNAME);
}
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
UNUSED(argc);
UNUSED(argv);
remove(ZIPNAME);
test_write(); test_write();
test_append(); test_append();
test_read(); test_read();
test_extract(); test_extract();
test_total_entries();
test_entry_name();
test_entry_index();
test_entry_openbyindex();
test_list_entries();
test_fwrite();
test_read_permissions();
test_write_permissions();
test_exe_permissions();
test_mtime();
return remove(ZIPNAME); remove(ZIPNAME);
return 0;
} }

View File

@ -0,0 +1,104 @@
// Demonstrates miniz.c's compress() and uncompress() functions
// (same as zlib's). Public domain, May 15 2011, Rich Geldreich,
// richgel99@gmail.com. See "unlicense" statement at the end of tinfl.c.
#include <miniz.h>
#include <stdio.h>
typedef unsigned char uint8;
typedef unsigned short uint16;
typedef unsigned int uint;
// The string to compress.
static const char *s_pStr =
"Good morning Dr. Chandra. This is Hal. I am ready for my first lesson."
"Good morning Dr. Chandra. This is Hal. I am ready for my first lesson."
"Good morning Dr. Chandra. This is Hal. I am ready for my first lesson."
"Good morning Dr. Chandra. This is Hal. I am ready for my first lesson."
"Good morning Dr. Chandra. This is Hal. I am ready for my first lesson."
"Good morning Dr. Chandra. This is Hal. I am ready for my first lesson."
"Good morning Dr. Chandra. This is Hal. I am ready for my first lesson.";
int main(int argc, char *argv[]) {
uint step = 0;
int cmp_status;
uLong src_len = (uLong)strlen(s_pStr);
uLong cmp_len = compressBound(src_len);
uLong uncomp_len = src_len;
uint8 *pCmp, *pUncomp;
uint total_succeeded = 0;
(void)argc, (void)argv;
printf("miniz.c version: %s\n", MZ_VERSION);
do {
// Allocate buffers to hold compressed and uncompressed data.
pCmp = (mz_uint8 *)malloc((size_t)cmp_len);
pUncomp = (mz_uint8 *)malloc((size_t)src_len);
if ((!pCmp) || (!pUncomp)) {
printf("Out of memory!\n");
return EXIT_FAILURE;
}
// Compress the string.
cmp_status =
compress(pCmp, &cmp_len, (const unsigned char *)s_pStr, src_len);
if (cmp_status != Z_OK) {
printf("compress() failed!\n");
free(pCmp);
free(pUncomp);
return EXIT_FAILURE;
}
printf("Compressed from %u to %u bytes\n", (mz_uint32)src_len,
(mz_uint32)cmp_len);
if (step) {
// Purposely corrupt the compressed data if fuzzy testing (this is a
// very crude fuzzy test).
uint n = 1 + (rand() % 3);
while (n--) {
uint i = rand() % cmp_len;
pCmp[i] ^= (rand() & 0xFF);
}
}
// Decompress.
cmp_status = uncompress(pUncomp, &uncomp_len, pCmp, cmp_len);
total_succeeded += (cmp_status == Z_OK);
if (step) {
printf("Simple fuzzy test: step %u total_succeeded: %u\n", step,
total_succeeded);
} else {
if (cmp_status != Z_OK) {
printf("uncompress failed!\n");
free(pCmp);
free(pUncomp);
return EXIT_FAILURE;
}
printf("Decompressed from %u to %u bytes\n", (mz_uint32)cmp_len,
(mz_uint32)uncomp_len);
// Ensure uncompress() returned the expected data.
if ((uncomp_len != src_len) ||
(memcmp(pUncomp, s_pStr, (size_t)src_len))) {
printf("Decompression failed!\n");
free(pCmp);
free(pUncomp);
return EXIT_FAILURE;
}
}
free(pCmp);
free(pUncomp);
step++;
// Keep on fuzzy testing if there's a non-empty command line.
} while (argc >= 2);
printf("Success.\n");
return EXIT_SUCCESS;
}

View File

@ -6,20 +6,18 @@ This is the main-module of PyAssimp.
import sys import sys
if sys.version_info < (2,6): if sys.version_info < (2,6):
raise 'pyassimp: need python 2.6 or newer' raise RuntimeError('pyassimp: need python 2.6 or newer')
# xrange was renamed range in Python 3 and the original range from Python 2 was removed. # xrange was renamed range in Python 3 and the original range from Python 2 was removed.
# To keep compatibility with both Python 2 and 3, xrange is set to range for version 3.0 and up. # To keep compatibility with both Python 2 and 3, xrange is set to range for version 3.0 and up.
if sys.version_info >= (3,0): if sys.version_info >= (3,0):
xrange = range xrange = range
import ctypes
import os
try: import numpy try: import numpy
except: numpy = None except ImportError: numpy = None
import logging import logging
import ctypes
logger = logging.getLogger("pyassimp") logger = logging.getLogger("pyassimp")
# attach default null handler to logger so it doesn't complain # attach default null handler to logger so it doesn't complain
# even if you don't attach another handler to logger # even if you don't attach another handler to logger
@ -67,10 +65,10 @@ def make_tuple(ai_obj, type = None):
# Returns unicode object for Python 2, and str object for Python 3. # Returns unicode object for Python 2, and str object for Python 3.
def _convert_assimp_string(assimp_string): def _convert_assimp_string(assimp_string):
try: if sys.version_info >= (3, 0):
return unicode(assimp_string.data, errors='ignore')
except:
return str(assimp_string.data, errors='ignore') return str(assimp_string.data, errors='ignore')
else:
return unicode(assimp_string.data, errors='ignore')
# It is faster and more correct to have an init function for each assimp class # It is faster and more correct to have an init function for each assimp class
def _init_face(aiFace): def _init_face(aiFace):
@ -305,7 +303,7 @@ def load(filename,
# unsigned int pLength, # unsigned int pLength,
# unsigned int pFlags, # unsigned int pFlags,
# const char* pHint) # const char* pHint)
if file_type == None: if file_type is None:
raise AssimpError('File type must be specified when passing file objects!') raise AssimpError('File type must be specified when passing file objects!')
data = filename.read() data = filename.read()
model = _assimp_lib.load_mem(data, model = _assimp_lib.load_mem(data,
@ -343,8 +341,7 @@ def export(scene,
''' '''
from ctypes import pointer exportStatus = _assimp_lib.export(ctypes.pointer(scene), file_type.encode("ascii"), filename.encode(sys.getfilesystemencoding()), processing)
exportStatus = _assimp_lib.export(pointer(scene), file_type.encode("ascii"), filename.encode(sys.getfilesystemencoding()), processing)
if exportStatus != 0: if exportStatus != 0:
raise AssimpError('Could not export scene!') raise AssimpError('Could not export scene!')
@ -369,16 +366,14 @@ def export_blob(scene,
--------- ---------
Pointer to structs.ExportDataBlob Pointer to structs.ExportDataBlob
''' '''
from ctypes import pointer exportBlobPtr = _assimp_lib.export_blob(ctypes.pointer(scene), file_type.encode("ascii"), processing)
exportBlobPtr = _assimp_lib.export_blob(pointer(scene), file_type.encode("ascii"), processing)
if exportBlobPtr == 0: if exportBlobPtr == 0:
raise AssimpError('Could not export scene to blob!') raise AssimpError('Could not export scene to blob!')
return exportBlobPtr return exportBlobPtr
def release(scene): def release(scene):
from ctypes import pointer _assimp_lib.release(ctypes.pointer(scene))
_assimp_lib.release(pointer(scene))
def _finalize_texture(tex, target): def _finalize_texture(tex, target):
setattr(target, "achformathint", tex.achFormatHint) setattr(target, "achformathint", tex.achFormatHint)
@ -442,24 +437,22 @@ def _finalize_mesh(mesh, target):
setattr(target, 'faces', faces) setattr(target, 'faces', faces)
def _init_metadata_entry(entry): def _init_metadata_entry(entry):
from ctypes import POINTER, c_bool, c_int32, c_uint64, c_float, c_double, cast
entry.type = entry.mType entry.type = entry.mType
if entry.type == structs.MetadataEntry.AI_BOOL: if entry.type == structs.MetadataEntry.AI_BOOL:
entry.data = cast(entry.mData, POINTER(c_bool)).contents.value entry.data = ctypes.cast(entry.mData, ctypes.POINTER(ctypes.c_bool)).contents.value
elif entry.type == structs.MetadataEntry.AI_INT32: elif entry.type == structs.MetadataEntry.AI_INT32:
entry.data = cast(entry.mData, POINTER(c_int32)).contents.value entry.data = ctypes.cast(entry.mData, ctypes.POINTER(ctypes.c_int32)).contents.value
elif entry.type == structs.MetadataEntry.AI_UINT64: elif entry.type == structs.MetadataEntry.AI_UINT64:
entry.data = cast(entry.mData, POINTER(c_uint64)).contents.value entry.data = ctypes.cast(entry.mData, ctypes.POINTER(ctypes.c_uint64)).contents.value
elif entry.type == structs.MetadataEntry.AI_FLOAT: elif entry.type == structs.MetadataEntry.AI_FLOAT:
entry.data = cast(entry.mData, POINTER(c_float)).contents.value entry.data = ctypes.cast(entry.mData, ctypes.POINTER(ctypes.c_float)).contents.value
elif entry.type == structs.MetadataEntry.AI_DOUBLE: elif entry.type == structs.MetadataEntry.AI_DOUBLE:
entry.data = cast(entry.mData, POINTER(c_double)).contents.value entry.data = ctypes.cast(entry.mData, ctypes.POINTER(ctypes.c_double)).contents.value
elif entry.type == structs.MetadataEntry.AI_AISTRING: elif entry.type == structs.MetadataEntry.AI_AISTRING:
assimp_string = cast(entry.mData, POINTER(structs.String)).contents assimp_string = ctypes.cast(entry.mData, ctypes.POINTER(structs.String)).contents
entry.data = _convert_assimp_string(assimp_string) entry.data = _convert_assimp_string(assimp_string)
elif entry.type == structs.MetadataEntry.AI_AIVECTOR3D: elif entry.type == structs.MetadataEntry.AI_AIVECTOR3D:
assimp_vector = cast(entry.mData, POINTER(structs.Vector3D)).contents assimp_vector = ctypes.cast(entry.mData, ctypes.POINTER(structs.Vector3D)).contents
entry.data = make_tuple(assimp_vector) entry.data = make_tuple(assimp_vector)
return entry return entry
@ -513,15 +506,18 @@ def _get_properties(properties, length):
key = (key.split('.')[1], p.mSemantic) key = (key.split('.')[1], p.mSemantic)
#the data #the data
from ctypes import POINTER, cast, c_int, c_float, sizeof
if p.mType == 1: if p.mType == 1:
arr = cast(p.mData, POINTER(c_float * int(p.mDataLength/sizeof(c_float)) )).contents arr = ctypes.cast(p.mData,
ctypes.POINTER(ctypes.c_float * int(p.mDataLength/ctypes.sizeof(ctypes.c_float)))
).contents
value = [x for x in arr] value = [x for x in arr]
elif p.mType == 3: #string can't be an array elif p.mType == 3: #string can't be an array
value = _convert_assimp_string(cast(p.mData, POINTER(structs.MaterialPropertyString)).contents) value = _convert_assimp_string(ctypes.cast(p.mData, ctypes.POINTER(structs.MaterialPropertyString)).contents)
elif p.mType == 4: elif p.mType == 4:
arr = cast(p.mData, POINTER(c_int * int(p.mDataLength/sizeof(c_int)) )).contents arr = ctypes.cast(p.mData,
ctypes.POINTER(ctypes.c_int * int(p.mDataLength/ctypes.sizeof(ctypes.c_int)))
).contents
value = [x for x in arr] value = [x for x in arr]
else: else:
value = p.mData[:p.mDataLength] value = p.mData[:p.mDataLength]
@ -541,7 +537,9 @@ def decompose_matrix(matrix):
rotation = structs.Quaternion() rotation = structs.Quaternion()
position = structs.Vector3D() position = structs.Vector3D()
from ctypes import byref, pointer _assimp_lib.dll.aiDecomposeMatrix(ctypes.pointer(matrix),
_assimp_lib.dll.aiDecomposeMatrix(pointer(matrix), byref(scaling), byref(rotation), byref(position)) ctypes.byref(scaling),
ctypes.byref(rotation),
ctypes.byref(position))
return scaling._init(), rotation._init(), position._init() return scaling._init(), rotation._init(), position._init()

View File

@ -21,7 +21,7 @@ FORMATS = ["CSM",
"STL", "STL",
"IRR", "IRR",
"Q3O", "Q3O",
"Q3D" "Q3D",
"MS3D", "MS3D",
"Q3S", "Q3S",
"ZGL", "ZGL",

View File

@ -6,7 +6,6 @@ Some fancy helper functions.
import os import os
import ctypes import ctypes
from ctypes import POINTER
import operator import operator
from distutils.sysconfig import get_python_lib from distutils.sysconfig import get_python_lib
@ -14,7 +13,7 @@ import re
import sys import sys
try: import numpy try: import numpy
except: numpy = None except ImportError: numpy = None
import logging;logger = logging.getLogger("pyassimp") import logging;logger = logging.getLogger("pyassimp")
@ -193,9 +192,9 @@ def try_load_functions(library_path, dll):
# library found! # library found!
from .structs import Scene, ExportDataBlob from .structs import Scene, ExportDataBlob
load.restype = POINTER(Scene) load.restype = ctype.POINTER(Scene)
load_mem.restype = POINTER(Scene) load_mem.restype = ctype.POINTER(Scene)
export2blob.restype = POINTER(ExportDataBlob) export2blob.restype = ctype.POINTER(ExportDataBlob)
return (library_path, load, load_mem, export, export2blob, release, dll) return (library_path, load, load_mem, export, export2blob, release, dll)
def search_library(): def search_library():
@ -276,5 +275,5 @@ def hasattr_silent(object, name):
try: try:
return hasattr(object, name) return hasattr(object, name)
except: except AttributeError:
return False return False

View File

@ -435,6 +435,7 @@ aiProcess_Debone = 0x4000000
aiProcess_GenEntityMeshes = 0x100000 aiProcess_GenEntityMeshes = 0x100000
aiProcess_OptimizeAnimations = 0x200000 aiProcess_OptimizeAnimations = 0x200000
aiProcess_FixTexturePaths = 0x200000 aiProcess_FixTexturePaths = 0x200000
aiProcess_EmbedTextures = 0x10000000,
## @def aiProcess_ConvertToLeftHanded ## @def aiProcess_ConvertToLeftHanded
# @brief Shortcut flag for Direct3D-based applications. # @brief Shortcut flag for Direct3D-based applications.

View File

@ -1,6 +1,6 @@
#-*- coding: UTF-8 -*- #-*- coding: UTF-8 -*-
from ctypes import POINTER, c_void_p, c_int, c_uint, c_char, c_float, Structure, c_char_p, c_double, c_ubyte, c_size_t, c_uint32 from ctypes import POINTER, c_void_p, c_uint, c_char, c_float, Structure, c_char_p, c_double, c_ubyte, c_size_t, c_uint32
class Vector2D(Structure): class Vector2D(Structure):

View File

@ -24,12 +24,13 @@ This sample is based on several sources, including:
- ASSIMP's C++ SimpleOpenGL viewer - ASSIMP's C++ SimpleOpenGL viewer
""" """
import os, sys import sys
from OpenGL.GLUT import * from OpenGL.GLUT import *
from OpenGL.GLU import * from OpenGL.GLU import *
from OpenGL.GL import * from OpenGL.GL import *
import logging;logger = logging.getLogger("pyassimp_opengl") import logging
logger = logging.getLogger("pyassimp_opengl")
logging.basicConfig(level=logging.INFO) logging.basicConfig(level=logging.INFO)
import math import math

View File

@ -5,7 +5,7 @@
This module demonstrates the functionality of PyAssimp. This module demonstrates the functionality of PyAssimp.
""" """
import os, sys import sys
import logging import logging
logging.basicConfig(level=logging.INFO) logging.basicConfig(level=logging.INFO)
@ -50,8 +50,8 @@ def main(filename=None):
print(" colors:" + str(len(mesh.colors))) print(" colors:" + str(len(mesh.colors)))
tcs = mesh.texturecoords tcs = mesh.texturecoords
if tcs.any(): if tcs.any():
for index, tc in enumerate(tcs): for tc_index, tc in enumerate(tcs):
print(" texture-coords "+ str(index) + ":" + str(len(tcs[index])) + "first3:" + str(tcs[index][:3])) print(" texture-coords "+ str(tc_index) + ":" + str(len(tcs[tc_index])) + "first3:" + str(tcs[tc_index][:3]))
else: else:
print(" no texture coordinates") print(" no texture coordinates")

View File

@ -292,6 +292,8 @@ def main():
output.write(templt.replace("<HERE>",s)) output.write(templt.replace("<HERE>",s))
# we got here, so no error
return 0
if __name__ == "__main__": if __name__ == "__main__":
sys.exit(main()) sys.exit(main())

View File

@ -87,7 +87,7 @@ template_type = r"""
template_stub_decl = '\tDECL_CONV_STUB({type});\n' template_stub_decl = '\tDECL_CONV_STUB({type});\n'
template_schema = '\t\tSchemaEntry("{normalized_name}",&STEP::ObjectHelper<{type},{argcnt}>::Construct )\n' template_schema = '\t\tSchemaEntry("{normalized_name}",&STEP::ObjectHelper<{type},{argcnt}>::Construct )\n'
template_schema_type = '\t\tSchemaEntry("{normalized_name}",NULL )\n' template_schema_type = '\t\tSchemaEntry("{normalized_name}",nullptr )\n'
template_converter = r""" template_converter = r"""
// ----------------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------------
template <> size_t GenericFill<{type}>(const DB& db, const LIST& params, {type}* in) template <> size_t GenericFill<{type}>(const DB& db, const LIST& params, {type}* in)
@ -99,7 +99,7 @@ template_converter_prologue_a = '\tsize_t base = GenericFill(db,params,static_ca
template_converter_prologue_b = '\tsize_t base = 0;\n' template_converter_prologue_b = '\tsize_t base = 0;\n'
template_converter_check_argcnt = '\tif (params.GetSize() < {max_arg}) {{ throw STEP::TypeError("expected {max_arg} arguments to {name}"); }}' template_converter_check_argcnt = '\tif (params.GetSize() < {max_arg}) {{ throw STEP::TypeError("expected {max_arg} arguments to {name}"); }}'
template_converter_code_per_field = r""" do {{ // convert the '{fieldname}' argument template_converter_code_per_field = r""" do {{ // convert the '{fieldname}' argument
boost::shared_ptr<const DataType> arg = params[base++];{handle_unset}{convert} std::shared_ptr<const DataType> arg = params[base++];{handle_unset}{convert}
}} while(0); }} while(0);
""" """
template_allow_optional = r""" template_allow_optional = r"""
@ -151,11 +151,8 @@ def handle_unset_args(field,entity,schema,argnum):
return n+template_allow_optional.format() return n+template_allow_optional.format()
def get_single_conversion(field,schema,argnum=0,classname='?'): def get_single_conversion(field,schema,argnum=0,classname='?'):
typen = field.type
name = field.name name = field.name
if field.collection: return template_convert_single.format(name=name,argnum=argnum,classname=classname,full_type=field.fullspec)
typen = 'LIST'
return template_convert_single.format(type=typen,name=name,argnum=argnum,classname=classname,full_type=field.fullspec)
def count_args_up(entity,schema): def count_args_up(entity,schema):
return len(entity.members) + (count_args_up(schema.entities[entity.parent],schema) if entity.parent else 0) return len(entity.members) + (count_args_up(schema.entities[entity.parent],schema) if entity.parent else 0)
@ -218,7 +215,7 @@ def get_derived(e,schema):
return res return res
def get_hierarchy(e,schema): def get_hierarchy(e,schema):
return get_derived(e.schema)+[e.name]+get_base_classes(e,schema) return get_derived(e, schema)+[e.name]+get_base_classes(e,schema)
def sort_entity_list(schema): def sort_entity_list(schema):
deps = [] deps = []
@ -300,5 +297,8 @@ def work(filename):
with open(output_file_cpp,'wt') as outp: with open(output_file_cpp,'wt') as outp:
outp.write(inp.read().replace('{schema-static-table}',schema_table).replace('{converter-impl}',converters)) outp.write(inp.read().replace('{schema-static-table}',schema_table).replace('{converter-impl}',converters))
# Finished without error, so return 0
return 0
if __name__ == "__main__": if __name__ == "__main__":
sys.exit(work(sys.argv[1] if len(sys.argv)>1 else 'schema.exp')) sys.exit(work(sys.argv[1] if len(sys.argv)>1 else 'schema.exp'))

View File

@ -43,7 +43,8 @@
"""Parse an EXPRESS file and extract basic information on all """Parse an EXPRESS file and extract basic information on all
entities and data types contained""" entities and data types contained"""
import sys, os, re import sys
import re
from collections import OrderedDict from collections import OrderedDict
re_match_entity = re.compile(r""" re_match_entity = re.compile(r"""

View File

@ -2,7 +2,7 @@
Open Asset Import Library (ASSIMP) Open Asset Import Library (ASSIMP)
---------------------------------------------------------------------- ----------------------------------------------------------------------
Copyright (c) 2006-2018, ASSIMP Development Team Copyright (c) 2006-2019, ASSIMP Development Team
All rights reserved. All rights reserved.
Redistribution and use of this software in source and binary forms, Redistribution and use of this software in source and binary forms,
@ -66,7 +66,7 @@ namespace STEP {
// ----------------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------------
template <> size_t GenericFill<NotImplemented>(const STEP::DB& db, const LIST& params, NotImplemented* in) template <> size_t GenericFill<NotImplemented>(const STEP::DB& db, const LIST& params, NotImplemented* in)
{ {
return 0; return 0u;
} }

View File

@ -2,7 +2,7 @@
Open Asset Import Library (ASSIMP) Open Asset Import Library (ASSIMP)
---------------------------------------------------------------------- ----------------------------------------------------------------------
Copyright (c) 2006-2018, ASSIMP Development Team Copyright (c) 2006-2019, ASSIMP Development Team
All rights reserved. All rights reserved.
Redistribution and use of this software in source and binary forms, Redistribution and use of this software in source and binary forms,
@ -47,22 +47,20 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
namespace Assimp { namespace Assimp {
namespace StepFile { namespace StepFile {
using namespace STEP; using namespace STEP;
using namespace STEP::EXPRESS; using namespace STEP::EXPRESS;
struct NotImplemented : public ObjectHelper<NotImplemented,0> { struct NotImplemented : public ObjectHelper<NotImplemented,0> {
}; };
// ****************************************************************************** // ******************************************************************************
// StepFile Custom data types // StepFile Custom data types
// ****************************************************************************** // ******************************************************************************
{types} {types}
// ****************************************************************************** // ******************************************************************************
// StepFile Entities // StepFile Entities
// ****************************************************************************** // ******************************************************************************
@ -73,6 +71,7 @@ namespace StepFile {
void GetSchema(EXPRESS::ConversionSchema& out); void GetSchema(EXPRESS::ConversionSchema& out);
} //! StepFile } //! StepFile
namespace STEP { namespace STEP {
// ****************************************************************************** // ******************************************************************************

View File

@ -229,6 +229,7 @@ int DoExport(const aiTexture* tx, FILE* p, const std::string& extension,
int Assimp_Extract (const char* const* params, unsigned int num) int Assimp_Extract (const char* const* params, unsigned int num)
{ {
const char* const invalid = "assimp extract: Invalid number of arguments. See \'assimp extract --help\'\n"; const char* const invalid = "assimp extract: Invalid number of arguments. See \'assimp extract --help\'\n";
// assimp extract in out [options]
if (num < 1) { if (num < 1) {
printf(invalid); printf(invalid);
return 1; return 1;
@ -240,11 +241,7 @@ int Assimp_Extract (const char* const* params, unsigned int num)
return 0; return 0;
} }
// asssimp extract in out [options]
if (num < 1) {
printf(invalid);
return 1;
}
std::string in = std::string(params[0]); std::string in = std::string(params[0]);
std::string out = (num > 1 ? std::string(params[1]) : "-"); std::string out = (num > 1 ? std::string(params[1]) : "-");