From a38c2aad1f6d5a43e88e8a9ee6c485e21eed1285 Mon Sep 17 00:00:00 2001 From: Seth Hillbrand Date: Mon, 17 Aug 2020 15:48:20 -0700 Subject: [PATCH] ADDED: Support compressed STEP and VRML files This adds support for opening .stpZ, step.gz and .wrz files where the files have been compressed using ZIP or gzip according to the "standard" published by the MBx volunteer forum at https://www.cax-if.org/documents/rec_prac_file_compression_v12.pdf Fixes https://gitlab.com/kicad/code/kicad/issues/2479 --- CMakeLists.txt | 9 ++- plugins/3d/oce/CMakeLists.txt | 6 +- plugins/3d/oce/loadmodel.cpp | 67 +++++++++++++++-- plugins/3d/oce/oce.cpp | 70 ++++++------------ plugins/3d/vrml/CMakeLists.txt | 6 +- plugins/3d/vrml/vrml.cpp | 98 +++++++++++++++---------- thirdparty/CMakeLists.txt | 1 + thirdparty/gzip-hpp/CMakeLists.txt | 7 ++ thirdparty/gzip-hpp/LICENSE.BSD2 | 11 +++ thirdparty/gzip-hpp/decompress.hpp | 113 +++++++++++++++++++++++++++++ 10 files changed, 290 insertions(+), 98 deletions(-) create mode 100644 thirdparty/gzip-hpp/CMakeLists.txt create mode 100644 thirdparty/gzip-hpp/LICENSE.BSD2 create mode 100644 thirdparty/gzip-hpp/decompress.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 2c569269e6..300eb58100 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ # # This program source code file is part of KICAD, a free EDA CAD application. # -# Copyright (C) 2007-2018 Kicad Developers, see AUTHORS.txt for contributors. +# Copyright (C) 2007-2020 Kicad Developers, see AUTHORS.txt for contributors. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -595,6 +595,13 @@ find_package( GLM 0.9.8 REQUIRED ) add_definitions( -DGLM_FORCE_CTOR_INIT ) include_directories( SYSTEM ${GLM_INCLUDE_DIR} ) +# +# Find zlib library, required +# +find_package(ZLIB REQUIRED) +check_find_package_result( ZLIB_FOUND "ZLIB" ) +include_directories( SYSTEM ${ZLIB_INCLUDE_DIRS} ) + # # Find CURL library, required # diff --git a/plugins/3d/oce/CMakeLists.txt b/plugins/3d/oce/CMakeLists.txt index 37ca0a6a6e..73a7244aaa 100644 --- a/plugins/3d/oce/CMakeLists.txt +++ b/plugins/3d/oce/CMakeLists.txt @@ -17,8 +17,12 @@ add_library( s3d_plugin_oce MODULE loadmodel.cpp ) -target_link_libraries( s3d_plugin_oce kicad_3dsg ${OCC_LIBRARIES} ${wxWidgets_LIBRARIES} ) +target_link_libraries( s3d_plugin_oce kicad_3dsg ${OCC_LIBRARIES} ${wxWidgets_LIBRARIES} ${ZLIB_LIBRARIES} ) +target_include_directories( s3d_plugin_oce PRIVATE + $ + ) + if( APPLE ) # puts library into the main kicad.app bundle in build tree set_target_properties( s3d_plugin_oce PROPERTIES diff --git a/plugins/3d/oce/loadmodel.cpp b/plugins/3d/oce/loadmodel.cpp index 6bd7d8b529..eb4f953c00 100644 --- a/plugins/3d/oce/loadmodel.cpp +++ b/plugins/3d/oce/loadmodel.cpp @@ -2,6 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2016 Cirilo Bernardo + * Copyright (C) 2020 KiCad Developers, see CHANGELOG.TXT for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -28,12 +29,12 @@ #include #include #include +#include +#include #include #include -#if ( defined( DEBUG_OCE ) && DEBUG_OCE > 3 ) -#include -#endif +#include #include #include @@ -261,19 +262,24 @@ struct DATA enum FormatType { FMT_NONE = 0, - FMT_STEP = 1, - FMT_IGES = 2 + FMT_STEP, + FMT_STPZ, + FMT_IGES }; FormatType fileType( const char* aFileName ) { - wxString fname( wxString::FromUTF8Unchecked( aFileName ) ); - wxFileInputStream ifile( fname ); + wxFileName fname( wxString::FromUTF8Unchecked( aFileName ) ); + wxFileInputStream ifile( fname.GetFullPath() ); if( !ifile.IsOk() ) return FMT_NONE; + if( fname.GetExt().MakeUpper().EndsWith( "STPZ" ) || + fname.GetExt().MakeUpper().EndsWith( "GZ" ) ) + return FMT_STPZ; + char iline[82]; memset( iline, 0, 82 ); ifile.Read( iline, 82 ); @@ -451,6 +457,47 @@ bool readSTEP( Handle(TDocStd_Document)& m_doc, const char* fname ) } +bool readSTEPZ( Handle(TDocStd_Document)& m_doc, const char* aFileName ) +{ + wxFileName fname( wxString::FromUTF8Unchecked( aFileName ) ); + wxFileInputStream ifile( fname.GetFullPath() ); + + wxFileName outFile( fname ); + + outFile.SetPath( wxStandardPaths::Get().GetTempDir() ); + outFile.SetExt( "STEP" ); + + wxFileOffset size = ifile.GetLength(); + + if( size == wxInvalidOffset ) + return false; + + { + wxFileOutputStream ofile( outFile.GetFullPath() ); + + if( !ofile.IsOk() ) + return false; + + char *buffer = new char[size]; + + ifile.Read( buffer, size); + std::string expanded = gzip::decompress( buffer, size ); + + delete buffer; + + ofile.Write( expanded.data(), expanded.size() ); + ofile.Close(); + } + + bool retval = readSTEP( m_doc, outFile.GetFullPath() ); + + // Cleanup our temporary file + wxRemoveFile( outFile.GetFullPath() ); + + return retval; +} + + SCENEGRAPH* LoadModel( char const* filename ) { DATA data; @@ -473,6 +520,12 @@ SCENEGRAPH* LoadModel( char const* filename ) return NULL; break; + case FMT_STPZ: + if( !readSTEPZ( data.m_doc, filename ) ) + return NULL; + break; + + default: return NULL; break; diff --git a/plugins/3d/oce/oce.cpp b/plugins/3d/oce/oce.cpp index 62f6e82a6c..272220f768 100644 --- a/plugins/3d/oce/oce.cpp +++ b/plugins/3d/oce/oce.cpp @@ -2,6 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2016 Cirilo Bernardo + * Copyright (C) 2020 KiCad Developers, see CHANGELOG.TXT for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -30,6 +31,9 @@ #include "plugins/3d/3d_plugin.h" #include "plugins/3dapi/ifsg_all.h" +#include +#include + SCENEGRAPH* LoadModel( char const* filename ); #define PLUGIN_OCE_MAJOR 1 @@ -62,55 +66,23 @@ void GetPluginVersion( unsigned char* Major, return; } -// number of extensions supported -#ifdef _WIN32 -#define NEXTS 4 -#else -#define NEXTS 8 -#endif - -// number of filter sets supported -#define NFILS 2 - -static char ext0[] = "stp"; -static char ext1[] = "step"; -static char ext2[] = "igs"; -static char ext3[] = "iges"; - -#ifdef _WIN32 -static char fil0[] = "STEP (*.stp;*.step)|*.stp;*.step"; -static char fil1[] = "IGES (*.igs;*.iges)|*.igs;*.iges"; -#else -static char ext4[] = "STP"; -static char ext5[] = "STEP"; -static char ext6[] = "IGS"; -static char ext7[] = "IGES"; -static char fil0[] = "STEP (*.stp;*.STP;*.step;*.STEP)|*.stp;*.STP;*.step;*.STEP"; -static char fil1[] = "IGES (*.igs;*.IGS;*.iges;*.IGES)|*.igs;*.IGS;*.iges;*.IGES"; -#endif - static struct FILE_DATA { - char const* extensions[NEXTS]; - char const* filters[NFILS]; + std::vector extensions; + std::vector filters; FILE_DATA() { - extensions[0] = ext0; - extensions[1] = ext1; - extensions[2] = ext2; - extensions[3] = ext3; - filters[0] = fil0; - filters[1] = fil1; - -#ifndef _WIN32 - extensions[4] = ext4; - extensions[5] = ext5; - extensions[6] = ext6; - extensions[7] = ext7; +#ifdef _WIN32 + extensions = { "stp","step","stpZ","step.gz","igs","iges" }; + filters = { "STEP (*.stp;*.step;*.stpZ;*.step.gz)|*.stp;*.step;*.stpZ;*.step.gz", + "IGES (*.igs;*.iges)|*.igs;*.iges" }; +#else + extensions = { "stp","STP","stpZ","STPZ","step","STEP","step.gz","STEP.GZ","igs","IGS","iges","IGES" }; + filters = { "STEP (*.stp;*.STP;*.stpZ;*.STPZ;*.step;*.STEP;*.step.gz;*.STEP.GZ)" + "|*.stp;*.STP;*.stpZ;*.STPZ;*.step;*.STEP;*.step.gz;*.STEP.GZ", + "IGES (*.igs;*.IGS;*.iges;*.IGES)|*.igs;*.IGS;*.iges;*.IGES" }; #endif - - return; } } file_data; @@ -118,31 +90,31 @@ static struct FILE_DATA int GetNExtensions( void ) { - return NEXTS; + return file_data.extensions.size(); } char const* GetModelExtension( int aIndex ) { - if( aIndex < 0 || aIndex >= NEXTS ) + if( aIndex < 0 || aIndex >= int( file_data.extensions.size() ) ) return NULL; - return file_data.extensions[aIndex]; + return file_data.extensions[aIndex].c_str(); } int GetNFilters( void ) { - return NFILS; + return file_data.filters.size(); } char const* GetFileFilter( int aIndex ) { - if( aIndex < 0 || aIndex >= NFILS ) + if( aIndex < 0 || aIndex >= int( file_data.filters.size() ) ) return NULL; - return file_data.filters[aIndex]; + return file_data.filters[aIndex].c_str(); } diff --git a/plugins/3d/vrml/CMakeLists.txt b/plugins/3d/vrml/CMakeLists.txt index 52f57846dc..b8bd160730 100644 --- a/plugins/3d/vrml/CMakeLists.txt +++ b/plugins/3d/vrml/CMakeLists.txt @@ -65,7 +65,11 @@ add_library( s3d_plugin_vrml MODULE x3d/x3d_transform.cpp ) -target_link_libraries( s3d_plugin_vrml kicad_3dsg ${OPENGL_LIBRARIES} ${wxWidgets_LIBRARIES} ) +target_link_libraries( s3d_plugin_vrml kicad_3dsg ${OPENGL_LIBRARIES} ${wxWidgets_LIBRARIES} ${ZLIB_LIBRARIES} ) + +target_include_directories( s3d_plugin_vrml PRIVATE + $ + ) if( APPLE ) # puts library into the main kicad.app bundle in build tree diff --git a/plugins/3d/vrml/vrml.cpp b/plugins/3d/vrml/vrml.cpp index 8c227f8255..643c0179dc 100644 --- a/plugins/3d/vrml/vrml.cpp +++ b/plugins/3d/vrml/vrml.cpp @@ -2,6 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2015-2016 Cirilo Bernardo + * Copyright (C) 2020 KiCad Developers, see CHANGELOG.TXT for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -40,8 +41,13 @@ #include "x3d.h" #include #include +#include +#include +#include #include +#include + #define PLUGIN_VRML_MAJOR 1 #define PLUGIN_VRML_MINOR 3 @@ -73,47 +79,24 @@ void GetPluginVersion( unsigned char* Major, return; } -// number of extensions supported -#ifdef _WIN32 -#define NEXTS 2 -#else -#define NEXTS 4 -#endif - -// number of filter sets supported -#define NFILS 2 - -static char ext0[] = "wrl"; -static char ext1[] = "x3d"; - -#ifdef _WIN32 -static char fil0[] = "VRML 1.0/2.0 (*.wrl)|*.wrl"; -static char fil1[] = "X3D (*.x3d)|*.x3d"; -#else -static char ext2[] = "WRL"; -static char ext3[] = "X3D"; -static char fil0[] = "VRML 1.0/2.0 (*.wrl;*.WRL)|*.wrl;*.WRL"; -static char fil1[] = "X3D (*.x3d;*.X3D)|*.x3d;*.X3D"; -#endif static struct FILE_DATA { - char const* extensions[NEXTS]; - char const* filters[NFILS]; + std::vector extensions; + std::vector filters; FILE_DATA() { - extensions[0] = ext0; - extensions[1] = ext1; - filters[0] = fil0; - filters[1] = fil1; -#ifndef _WIN32 - extensions[2] = ext2; - extensions[3] = ext3; +#ifdef _WIN32 + extensions = { "wrl", "wrz", "x3d" }; + filters = { "VRML 1.0/2.0 (*.wrl;*.wrz)|*.wrl;*.wrz", + "X3D (*.x3d)|*.x3d" }; +#else + extensions = { "wrl", "WRL", "wrz", "WRZ", "x3d", "X3D" }; + filters = { "VRML 1.0/2.0 (*.wrl;*.WRL;*.wrz;*.WRZ)|*.wrl;*.WRL;*.wrz;*.WRZ", + "X3D (*.x3d;*.X3D)|*.x3d;*.X3D" }; #endif - - return; } } file_data; @@ -121,31 +104,31 @@ static struct FILE_DATA int GetNExtensions( void ) { - return NEXTS; + return file_data.extensions.size(); } char const* GetModelExtension( int aIndex ) { - if( aIndex < 0 || aIndex >= NEXTS ) + if( aIndex < 0 || aIndex >= int( file_data.extensions.size() ) ) return NULL; - return file_data.extensions[aIndex]; + return file_data.extensions[aIndex].c_str(); } int GetNFilters( void ) { - return NFILS; + return file_data.filters.size(); } char const* GetFileFilter( int aIndex ) { - if( aIndex < 0 || aIndex >= NFILS ) + if( aIndex < 0 || aIndex >= int( file_data.filters.size() ) ) return NULL; - return file_data.filters[aIndex]; + return file_data.filters[aIndex].c_str(); } @@ -179,6 +162,39 @@ SCENEGRAPH* LoadVRML( const wxString& aFileName, bool useInline ) { FILE_LINE_READER* modelFile = NULL; SCENEGRAPH* scene = NULL; + wxString filename = aFileName; + wxFileName tmpfilename; + + if( aFileName.Upper().EndsWith( "WRZ" ) ) + { + wxFileInputStream ifile( aFileName ); + tmpfilename = wxFileName( aFileName ); + tmpfilename.SetExt( "WRL" ); + + wxFileOffset size = ifile.GetLength(); + + if( size == wxInvalidOffset ) + return nullptr; + + { + wxFileOutputStream ofile( tmpfilename.GetFullPath() ); + + if( !ofile.IsOk() ) + return nullptr; + + char *buffer = new char[size]; + + ifile.Read( buffer, size); + std::string expanded = gzip::decompress( buffer, size ); + + delete buffer; + + ofile.Write( expanded.data(), expanded.size() ); + ofile.Close(); + } + + filename = tmpfilename.GetFullPath(); + } try { @@ -196,6 +212,10 @@ SCENEGRAPH* LoadVRML( const wxString& aFileName, bool useInline ) // VRML file processor WRLPROC proc( modelFile ); + // Cleanup our temporary file + if( tmpfilename.IsOk() ) + wxRemoveFile( tmpfilename.GetFullPath() ); + if( proc.GetVRMLType() == VRML_V1 ) { wxLogTrace( MASK_VRML, " * [INFO] Processing VRML 1.0 file\n" ); diff --git a/thirdparty/CMakeLists.txt b/thirdparty/CMakeLists.txt index 74c2df1a08..a5b044a2ec 100644 --- a/thirdparty/CMakeLists.txt +++ b/thirdparty/CMakeLists.txt @@ -25,6 +25,7 @@ add_subdirectory( clipper ) add_subdirectory( compoundfilereader ) add_subdirectory( delaunator ) add_subdirectory( dxflib_qcad ) +add_subdirectory( gzip-hpp ) add_subdirectory( lemon ) add_subdirectory( libcontext ) add_subdirectory( markdown2html ) diff --git a/thirdparty/gzip-hpp/CMakeLists.txt b/thirdparty/gzip-hpp/CMakeLists.txt new file mode 100644 index 0000000000..674736bfef --- /dev/null +++ b/thirdparty/gzip-hpp/CMakeLists.txt @@ -0,0 +1,7 @@ +add_library( gzip-hpp INTERFACE ) + +target_include_directories( gzip-hpp INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} ) + +target_sources( gzip-hpp INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}/decompress.hpp + ) \ No newline at end of file diff --git a/thirdparty/gzip-hpp/LICENSE.BSD2 b/thirdparty/gzip-hpp/LICENSE.BSD2 new file mode 100644 index 0000000000..74b5c97332 --- /dev/null +++ b/thirdparty/gzip-hpp/LICENSE.BSD2 @@ -0,0 +1,11 @@ + + +Copyright (c) 2017, Mapbox Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/thirdparty/gzip-hpp/decompress.hpp b/thirdparty/gzip-hpp/decompress.hpp new file mode 100644 index 0000000000..ca0b589116 --- /dev/null +++ b/thirdparty/gzip-hpp/decompress.hpp @@ -0,0 +1,113 @@ +#ifndef ZLIB_CONST +#define ZLIB_CONST +#endif + +// zlib +#include + +// std +#include +#include +#include + +namespace gzip { + +inline void decompress(const char* data, + std::size_t size, + std::string& output, + std::size_t max_uncompressed_size = 0, + std::size_t buffering_size = 0) +{ + if (buffering_size == 0) + { + buffering_size = (size * 2) - (size / 2) + 16; + } + z_stream inflate_s; + inflate_s.zalloc = Z_NULL; + inflate_s.zfree = Z_NULL; + inflate_s.opaque = Z_NULL; + inflate_s.avail_in = 0; + inflate_s.next_in = Z_NULL; + + // The windowBits parameter is the base two logarithm of the window size (the size of the history buffer). + // It should be in the range 8..15 for this version of the library. + // Larger values of this parameter result in better compression at the expense of memory usage. + // This range of values also changes the decoding type: + // -8 to -15 for raw deflate + // 8 to 15 for zlib + // (8 to 15) + 16 for gzip + // (8 to 15) + 32 to automatically detect gzip/zlib header + constexpr int window_bits = 15 + 32; // auto with windowbits of 15 + + constexpr unsigned int max_uint = std::numeric_limits::max(); + const unsigned int size_step = buffering_size > max_uint ? max_uint : static_cast(buffering_size); + if( max_uncompressed_size != 0 && size_step > max_uncompressed_size ) + { + throw std::runtime_error( + "buffer size used during decompression of gzip will use more memory then allowed, " + "either increase the limit or reduce the buffer size" ); + } + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wold-style-cast" + if (inflateInit2(&inflate_s, window_bits) != Z_OK) + { + throw std::runtime_error("inflate init failed"); + } +#pragma GCC diagnostic pop + inflate_s.next_in = reinterpret_cast(data); + inflate_s.avail_in = static_cast(size); + std::string buffer(static_cast(size_step), char()); + do + { + inflate_s.avail_out = size_step; + inflate_s.next_out = reinterpret_cast(&buffer[0]); + const int ret = inflate(&inflate_s, Z_FINISH); + if (ret != Z_STREAM_END && ret != Z_OK && ret != Z_BUF_ERROR) + { + std::string error_msg = inflate_s.msg; + inflateEnd(&inflate_s); + throw std::runtime_error(error_msg); + } + if (max_uncompressed_size != 0 && (output.size() + size_step - inflate_s.avail_out) > max_uncompressed_size) + { + inflateEnd(&inflate_s); + throw std::runtime_error("size of output string will use more memory then intended when decompressing"); + } + output.append(buffer, 0, size_step - inflate_s.avail_out); + } while (inflate_s.avail_out == 0); + const int ret2 = inflateEnd(&inflate_s); + if (ret2 != Z_OK) + { + throw std::runtime_error("Unexpected gzip decompression error, state of stream was inconsistent"); + } +} + +inline void decompress(std::string const& input, + std::string& output, + std::size_t max_uncompressed_size = 0, + std::size_t buffering_size = 0) +{ + return decompress(input.data(), input.size(), output, max_uncompressed_size, buffering_size); +} + +inline std::string decompress(const char* data, + std::size_t size, + std::size_t max_uncompressed_size = 0, + std::size_t buffering_size = 0) +{ + std::string output; + decompress(data, size, output, max_uncompressed_size, buffering_size); + return output; +} + +inline std::string decompress(std::string const& input, + std::size_t max_uncompressed_size = 0, + std::size_t buffering_size = 0) +{ + std::string output; + decompress(input.data(), input.size(), output, max_uncompressed_size, buffering_size); + return output; +} + +} // namespace gzip