1 /*
2 Open Asset Import Library (assimp)
3 ----------------------------------------------------------------------
4 
5 Copyright (c) 2006-2017, assimp team
6 
7 All rights reserved.
8 
9 Redistribution and use of this software in source and binary forms,
10 with or without modification, are permitted provided that the
11 following conditions are met:
12 
13 * Redistributions of source code must retain the above
14   copyright notice, this list of conditions and the
15   following disclaimer.
16 
17 * Redistributions in binary form must reproduce the above
18   copyright notice, this list of conditions and the
19   following disclaimer in the documentation and/or other
20   materials provided with the distribution.
21 
22 * Neither the name of the assimp team, nor the names of its
23   contributors may be used to endorse or promote products
24   derived from this software without specific prior
25   written permission of the assimp team.
26 
27 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 
39 ----------------------------------------------------------------------
40 */
41 
42 #ifndef ASSIMP_BUILD_NO_3MF_IMPORTER
43 
44 #include "D3MFOpcPackage.h"
45 #include "Exceptional.h"
46 
47 #include <assimp/IOStream.hpp>
48 #include <assimp/IOSystem.hpp>
49 #include <assimp/DefaultLogger.hpp>
50 #include <assimp/ai_assert.h>
51 
52 #include <cstdlib>
53 #include <memory>
54 #include <vector>
55 #include <map>
56 #include <algorithm>
57 #include <cassert>
58 #include <contrib/unzip/unzip.h>
59 #include "3MFXmlTags.h"
60 
61 namespace Assimp {
62 
63 namespace D3MF {
64 
65 class IOSystem2Unzip {
66 public:
67     static voidpf open(voidpf opaque, const char* filename, int mode);
68     static uLong read(voidpf opaque, voidpf stream, void* buf, uLong size);
69     static uLong write(voidpf opaque, voidpf stream, const void* buf, uLong size);
70     static long tell(voidpf opaque, voidpf stream);
71     static long seek(voidpf opaque, voidpf stream, uLong offset, int origin);
72     static int close(voidpf opaque, voidpf stream);
73     static int testerror(voidpf opaque, voidpf stream);
74     static zlib_filefunc_def get(IOSystem* pIOHandler);
75 };
76 
open(voidpf opaque,const char * filename,int mode)77 voidpf IOSystem2Unzip::open(voidpf opaque, const char* filename, int mode) {
78     IOSystem* io_system = reinterpret_cast<IOSystem*>(opaque);
79 
80     const char* mode_fopen = NULL;
81     if((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) {
82         mode_fopen = "rb";
83     } else {
84         if(mode & ZLIB_FILEFUNC_MODE_EXISTING) {
85             mode_fopen = "r+b";
86         } else {
87             if(mode & ZLIB_FILEFUNC_MODE_CREATE) {
88                 mode_fopen = "wb";
89             }
90         }
91     }
92 
93     return (voidpf) io_system->Open(filename, mode_fopen);
94 }
95 
read(voidpf,voidpf stream,void * buf,uLong size)96 uLong IOSystem2Unzip::read(voidpf /*opaque*/, voidpf stream, void* buf, uLong size) {
97     IOStream* io_stream = (IOStream*) stream;
98 
99     return static_cast<uLong>(io_stream->Read(buf, 1, size));
100 }
101 
write(voidpf,voidpf stream,const void * buf,uLong size)102 uLong IOSystem2Unzip::write(voidpf /*opaque*/, voidpf stream, const void* buf, uLong size) {
103     IOStream* io_stream = (IOStream*) stream;
104 
105     return static_cast<uLong>(io_stream->Write(buf, 1, size));
106 }
107 
tell(voidpf,voidpf stream)108 long IOSystem2Unzip::tell(voidpf /*opaque*/, voidpf stream) {
109     IOStream* io_stream = (IOStream*) stream;
110 
111     return static_cast<long>(io_stream->Tell());
112 }
113 
seek(voidpf,voidpf stream,uLong offset,int origin)114 long IOSystem2Unzip::seek(voidpf /*opaque*/, voidpf stream, uLong offset, int origin) {
115     IOStream* io_stream = (IOStream*) stream;
116 
117     aiOrigin assimp_origin;
118     switch (origin) {
119         default:
120         case ZLIB_FILEFUNC_SEEK_CUR:
121             assimp_origin = aiOrigin_CUR;
122             break;
123         case ZLIB_FILEFUNC_SEEK_END:
124             assimp_origin = aiOrigin_END;
125             break;
126         case ZLIB_FILEFUNC_SEEK_SET:
127             assimp_origin = aiOrigin_SET;
128             break;
129     }
130 
131     return (io_stream->Seek(offset, assimp_origin) == aiReturn_SUCCESS ? 0 : -1);
132 }
133 
close(voidpf opaque,voidpf stream)134 int IOSystem2Unzip::close(voidpf opaque, voidpf stream) {
135     IOSystem* io_system = (IOSystem*) opaque;
136     IOStream* io_stream = (IOStream*) stream;
137 
138     io_system->Close(io_stream);
139 
140     return 0;
141 }
142 
testerror(voidpf,voidpf)143 int IOSystem2Unzip::testerror(voidpf /*opaque*/, voidpf /*stream*/) {
144     return 0;
145 }
146 
get(IOSystem * pIOHandler)147 zlib_filefunc_def IOSystem2Unzip::get(IOSystem* pIOHandler) {
148     zlib_filefunc_def mapping;
149 
150     mapping.zopen_file = open;
151     mapping.zread_file = read;
152     mapping.zwrite_file = write;
153     mapping.ztell_file = tell;
154     mapping.zseek_file = seek;
155     mapping.zclose_file = close;
156     mapping.zerror_file = testerror;
157     mapping.opaque = reinterpret_cast<voidpf>(pIOHandler);
158 
159     return mapping;
160 }
161 
162 class ZipFile : public IOStream {
163     friend class D3MFZipArchive;
164 
165 public:
166     explicit ZipFile(size_t size);
167     virtual ~ZipFile();
168     size_t Read(void* pvBuffer, size_t pSize, size_t pCount );
169     size_t Write(const void* /*pvBuffer*/, size_t /*pSize*/, size_t /*pCount*/);
170     size_t FileSize() const;
171     aiReturn Seek(size_t /*pOffset*/, aiOrigin /*pOrigin*/);
172     size_t Tell() const;
173     void Flush();
174 
175 private:
176     void *m_Buffer;
177     size_t m_Size;
178 };
179 
ZipFile(size_t size)180 ZipFile::ZipFile(size_t size)
181 : m_Buffer( nullptr )
182 , m_Size(size) {
183     ai_assert(m_Size != 0);
184     m_Buffer = ::malloc(m_Size);
185 }
186 
~ZipFile()187 ZipFile::~ZipFile() {
188     ::free(m_Buffer);
189     m_Buffer = NULL;
190 }
191 
Read(void * pvBuffer,size_t pSize,size_t pCount)192 size_t ZipFile::Read(void* pvBuffer, size_t pSize, size_t pCount) {
193     const size_t size = pSize * pCount;
194     ai_assert(size <= m_Size);
195 
196     std::memcpy(pvBuffer, m_Buffer, size);
197 
198     return size;
199 }
200 
Write(const void * pvBuffer,size_t size,size_t pCount)201 size_t ZipFile::Write(const void* pvBuffer, size_t size, size_t pCount ) {
202     const size_t size_to_write( size * pCount );
203     if ( 0 == size_to_write ) {
204         return 0U;
205     }
206     return 0U;
207 }
208 
FileSize() const209 size_t ZipFile::FileSize() const {
210     return m_Size;
211 }
212 
Seek(size_t,aiOrigin)213 aiReturn ZipFile::Seek(size_t /*pOffset*/, aiOrigin /*pOrigin*/) {
214     return aiReturn_FAILURE;
215 }
216 
Tell() const217 size_t ZipFile::Tell() const {
218     return 0;
219 }
220 
Flush()221 void ZipFile::Flush() {
222     // empty
223 }
224 
225 class D3MFZipArchive : public IOSystem {
226 public:
227     static const unsigned int FileNameSize = 256;
228 
229     D3MFZipArchive(IOSystem* pIOHandler, const std::string & rFile);
230     ~D3MFZipArchive();
231     bool Exists(const char* pFile) const;
232     char getOsSeparator() const;
233     IOStream* Open(const char* pFile, const char* pMode = "rb");
234     void Close(IOStream* pFile);
235     bool isOpen() const;
236     void getFileList(std::vector<std::string> &rFileList);
237 
238 private:
239     bool mapArchive();
240 
241 private:
242     unzFile m_ZipFileHandle;
243     std::map<std::string, ZipFile*> m_ArchiveMap;
244 };
245 
246 // ------------------------------------------------------------------------------------------------
247 //  Constructor.
D3MFZipArchive(IOSystem * pIOHandler,const std::string & rFile)248 D3MFZipArchive::D3MFZipArchive(IOSystem* pIOHandler, const std::string& rFile)
249 : m_ZipFileHandle(NULL)
250 , m_ArchiveMap() {
251     if (! rFile.empty()) {
252         zlib_filefunc_def mapping = IOSystem2Unzip::get(pIOHandler);
253 
254         m_ZipFileHandle = unzOpen2(rFile.c_str(), &mapping);
255         if(m_ZipFileHandle != NULL) {
256             mapArchive();
257         }
258     }
259 }
260 
261 // ------------------------------------------------------------------------------------------------
262 //  Destructor.
~D3MFZipArchive()263 D3MFZipArchive::~D3MFZipArchive() {
264     for(auto &file : m_ArchiveMap) {
265         delete file.second;
266     }
267     m_ArchiveMap.clear();
268 
269     if(m_ZipFileHandle != NULL) {
270         unzClose(m_ZipFileHandle);
271         m_ZipFileHandle = NULL;
272     }
273 }
274 
275 // ------------------------------------------------------------------------------------------------
276 //  Returns true, if the archive is already open.
isOpen() const277 bool D3MFZipArchive::isOpen() const {
278     return (m_ZipFileHandle != NULL);
279 }
280 
281 // ------------------------------------------------------------------------------------------------
282 //  Returns true, if the filename is part of the archive.
Exists(const char * pFile) const283 bool D3MFZipArchive::Exists(const char* pFile) const {
284     ai_assert(pFile != NULL);
285 
286     bool exist = false;
287 
288     if (pFile != NULL) {
289         std::string rFile(pFile);
290         std::map<std::string, ZipFile*>::const_iterator it = m_ArchiveMap.find(rFile);
291 
292         if(it != m_ArchiveMap.end()) {
293             exist = true;
294         }
295     }
296 
297     return exist;
298 }
299 
300 // ------------------------------------------------------------------------------------------------
301 //  Returns the separator delimiter.
getOsSeparator() const302 char D3MFZipArchive::getOsSeparator() const {
303 #ifndef _WIN32
304     return '/';
305 #else
306     return '\\';
307 #endif
308 }
309 
310 // ------------------------------------------------------------------------------------------------
311 //  Opens a file, which is part of the archive.
Open(const char * pFile,const char *)312 IOStream *D3MFZipArchive::Open(const char* pFile, const char* /*pMode*/) {
313     ai_assert(pFile != NULL);
314 
315     IOStream* result = NULL;
316 
317     std::map<std::string, ZipFile*>::iterator it = m_ArchiveMap.find(pFile);
318 
319     if(it != m_ArchiveMap.end()) {
320         result = static_cast<IOStream*>(it->second);
321     }
322 
323     return result;
324 }
325 
326 // ------------------------------------------------------------------------------------------------
327 //  Close a filestream.
Close(IOStream * pFile)328 void D3MFZipArchive::Close(IOStream *pFile) {
329     (void)(pFile);
330     ai_assert(pFile != NULL);
331 
332     // We don't do anything in case the file would be opened again in the future
333 }
334 // ------------------------------------------------------------------------------------------------
335 //  Returns the file-list of the archive.
getFileList(std::vector<std::string> & rFileList)336 void D3MFZipArchive::getFileList(std::vector<std::string> &rFileList) {
337     rFileList.clear();
338 
339     for(const auto &file : m_ArchiveMap) {
340         rFileList.push_back(file.first);
341     }
342 }
343 
344 // ------------------------------------------------------------------------------------------------
345 //  Maps the archive content.
mapArchive()346 bool D3MFZipArchive::mapArchive() {
347     bool success = false;
348 
349     if(m_ZipFileHandle != NULL) {
350         if(m_ArchiveMap.empty()) {
351             //  At first ensure file is already open
352             if(unzGoToFirstFile(m_ZipFileHandle) == UNZ_OK) {
353                 // Loop over all files
354                 do {
355                     char filename[FileNameSize];
356                     unz_file_info fileInfo;
357 
358                     if(unzGetCurrentFileInfo(m_ZipFileHandle, &fileInfo, filename, FileNameSize, NULL, 0, NULL, 0) == UNZ_OK) {
359                         // The file has EXACTLY the size of uncompressed_size. In C
360                         // you need to mark the last character with '\0', so add
361                         // another character
362                         if(fileInfo.uncompressed_size != 0 && unzOpenCurrentFile(m_ZipFileHandle) == UNZ_OK) {
363                             std::pair<std::map<std::string, ZipFile*>::iterator, bool> result = m_ArchiveMap.insert(std::make_pair(filename, new ZipFile(fileInfo.uncompressed_size)));
364 
365                             if(unzReadCurrentFile(m_ZipFileHandle, result.first->second->m_Buffer, fileInfo.uncompressed_size) == (long int) fileInfo.uncompressed_size) {
366                                 if(unzCloseCurrentFile(m_ZipFileHandle) == UNZ_OK) {
367                                     // Nothing to do anymore...
368                                 }
369                             }
370                         }
371                     }
372                 } while(unzGoToNextFile(m_ZipFileHandle) != UNZ_END_OF_LIST_OF_FILE);
373             }
374         }
375 
376         success = true;
377     }
378 
379     return success;
380 }
381 
382 // ------------------------------------------------------------------------------------------------
383 
384 typedef std::shared_ptr<OpcPackageRelationship> OpcPackageRelationshipPtr;
385 
386 class OpcPackageRelationshipReader {
387 public:
OpcPackageRelationshipReader(XmlReader * xmlReader)388     OpcPackageRelationshipReader(XmlReader* xmlReader) {
389         while(xmlReader->read()) {
390             if(xmlReader->getNodeType() == irr::io::EXN_ELEMENT &&
391                xmlReader->getNodeName() == XmlTag::RELS_RELATIONSHIP_CONTAINER)
392             {
393                 ParseRootNode(xmlReader);
394             }
395         }
396     }
397 
ParseRootNode(XmlReader * xmlReader)398     void ParseRootNode(XmlReader* xmlReader)
399     {
400         ParseAttributes(xmlReader);
401 
402         while(xmlReader->read())
403         {
404             if(xmlReader->getNodeType() == irr::io::EXN_ELEMENT &&
405                xmlReader->getNodeName() == XmlTag::RELS_RELATIONSHIP_NODE)
406             {
407                 ParseChildNode(xmlReader);
408             }
409         }
410     }
411 
ParseAttributes(XmlReader *)412     void ParseAttributes(XmlReader*) {
413         // empty
414     }
415 
validateRels(OpcPackageRelationshipPtr & relPtr)416     bool validateRels( OpcPackageRelationshipPtr &relPtr ) {
417         if ( relPtr->id.empty() || relPtr->type.empty() || relPtr->target.empty() ) {
418             return false;
419         }
420         return true;
421     }
422 
ParseChildNode(XmlReader * xmlReader)423     void ParseChildNode(XmlReader* xmlReader) {
424         OpcPackageRelationshipPtr relPtr(new OpcPackageRelationship());
425 
426         relPtr->id = xmlReader->getAttributeValueSafe(XmlTag::RELS_ATTRIB_ID.c_str());
427         relPtr->type = xmlReader->getAttributeValueSafe(XmlTag::RELS_ATTRIB_TYPE.c_str());
428         relPtr->target = xmlReader->getAttributeValueSafe(XmlTag::RELS_ATTRIB_TARGET.c_str());
429         if ( validateRels( relPtr ) ) {
430             m_relationShips.push_back( relPtr );
431         }
432     }
433 
434     std::vector<OpcPackageRelationshipPtr> m_relationShips;
435 };
436 // ------------------------------------------------------------------------------------------------
437 
D3MFOpcPackage(IOSystem * pIOHandler,const std::string & rFile)438 D3MFOpcPackage::D3MFOpcPackage(IOSystem* pIOHandler, const std::string& rFile)
439 : mRootStream(nullptr)
440 , mZipArchive() {
441     mZipArchive.reset( new D3MF::D3MFZipArchive( pIOHandler, rFile ) );
442     if(!mZipArchive->isOpen()) {
443         throw DeadlyImportError("Failed to open file " + rFile+ ".");
444     }
445 
446     std::vector<std::string> fileList;
447     mZipArchive->getFileList(fileList);
448 
449     for (auto& file: fileList) {
450         if(file == D3MF::XmlTag::ROOT_RELATIONSHIPS_ARCHIVE) {
451             //PkgRelationshipReader pkgRelReader(file, archive);
452             ai_assert(mZipArchive->Exists(file.c_str()));
453 
454             IOStream *fileStream = mZipArchive->Open(file.c_str());
455 
456             ai_assert(fileStream != nullptr);
457 
458             std::string rootFile = ReadPackageRootRelationship(fileStream);
459             if ( rootFile.size() > 0 && rootFile[ 0 ] == '/' ) {
460                 rootFile = rootFile.substr( 1 );
461                 if ( rootFile[ 0 ] == '/' ) {
462                     // deal with zipbug
463                     rootFile = rootFile.substr( 1 );
464                 }
465             }
466 
467             DefaultLogger::get()->debug(rootFile);
468 
469             mRootStream = mZipArchive->Open(rootFile.c_str());
470             ai_assert( mRootStream != nullptr );
471             if ( nullptr == mRootStream ) {
472                 throw DeadlyExportError( "Cannot open rootfile in archive : " + rootFile );
473             }
474 
475         //    const size_t size = zipArchive->FileSize();
476         //    m_Data.resize( size );
477 
478         //    const size_t readSize = pMapFile->Read( &m_Data[0], sizeof( char ), size );
479         //    if ( readSize != size )
480         //    {
481         //        m_Data.clear();
482         //        return false;
483         //    }
484             mZipArchive->Close( fileStream );
485 
486         } else if( file == D3MF::XmlTag::CONTENT_TYPES_ARCHIVE) {
487 
488         }
489     }
490 }
491 
~D3MFOpcPackage()492 D3MFOpcPackage::~D3MFOpcPackage() {
493     // empty
494 }
495 
RootStream() const496 IOStream* D3MFOpcPackage::RootStream() const {
497     return mRootStream;
498 }
499 
ReadPackageRootRelationship(IOStream * stream)500 std::string D3MFOpcPackage::ReadPackageRootRelationship(IOStream* stream) {
501     std::unique_ptr<CIrrXML_IOStreamReader> xmlStream(new CIrrXML_IOStreamReader(stream));
502     std::unique_ptr<XmlReader> xml(irr::io::createIrrXMLReader(xmlStream.get()));
503 
504     OpcPackageRelationshipReader reader(xml.get());
505 
506     auto itr = std::find_if(reader.m_relationShips.begin(), reader.m_relationShips.end(), [](const OpcPackageRelationshipPtr& rel){
507         return rel->type == XmlTag::PACKAGE_START_PART_RELATIONSHIP_TYPE;
508     });
509 
510     if(itr == reader.m_relationShips.end())
511         throw DeadlyImportError("Cannot find " + XmlTag::PACKAGE_START_PART_RELATIONSHIP_TYPE);
512 
513     return (*itr)->target;
514 }
515 
516 } // Namespace D3MF
517 
518 } // Namespace Assimp
519 
520 #endif //ASSIMP_BUILD_NO_3MF_IMPORTER
521