1/*
2Open Asset Import Library (assimp)
3----------------------------------------------------------------------
4
5Copyright (c) 2006-2017, assimp team
6
7All rights reserved.
8
9Redistribution and use of this software in source and binary forms,
10with or without modification, are permitted provided that the
11following 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
27THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37OF 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
61namespace Assimp {
62
63namespace D3MF {
64
65class IOSystem2Unzip {
66public:
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
77voidpf 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
96uLong 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
102uLong 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
108long IOSystem2Unzip::tell(voidpf /*opaque*/, voidpf stream) {
109 IOStream* io_stream = (IOStream*) stream;
110
111 return static_cast<long>(io_stream->Tell());
112}
113
114long 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
134int 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
143int IOSystem2Unzip::testerror(voidpf /*opaque*/, voidpf /*stream*/) {
144 return 0;
145}
146
147zlib_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
162class ZipFile : public IOStream {
163 friend class D3MFZipArchive;
164
165public:
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
175private:
176 void *m_Buffer;
177 size_t m_Size;
178};
179
180ZipFile::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
187ZipFile::~ZipFile() {
188 ::free(m_Buffer);
189 m_Buffer = NULL;
190}
191
192size_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
201size_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
209size_t ZipFile::FileSize() const {
210 return m_Size;
211}
212
213aiReturn ZipFile::Seek(size_t /*pOffset*/, aiOrigin /*pOrigin*/) {
214 return aiReturn_FAILURE;
215}
216
217size_t ZipFile::Tell() const {
218 return 0;
219}
220
221void ZipFile::Flush() {
222 // empty
223}
224
225class D3MFZipArchive : public IOSystem {
226public:
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
238private:
239 bool mapArchive();
240
241private:
242 unzFile m_ZipFileHandle;
243 std::map<std::string, ZipFile*> m_ArchiveMap;
244};
245
246// ------------------------------------------------------------------------------------------------
247// Constructor.
248D3MFZipArchive::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.
263D3MFZipArchive::~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.
277bool D3MFZipArchive::isOpen() const {
278 return (m_ZipFileHandle != NULL);
279}
280
281// ------------------------------------------------------------------------------------------------
282// Returns true, if the filename is part of the archive.
283bool 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.
302char 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.
312IOStream *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.
328void 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.
336void 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.
346bool 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
384typedef std::shared_ptr<OpcPackageRelationship> OpcPackageRelationshipPtr;
385
386class OpcPackageRelationshipReader {
387public:
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
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
412 void ParseAttributes(XmlReader*) {
413 // empty
414 }
415
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
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
438D3MFOpcPackage::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
492D3MFOpcPackage::~D3MFOpcPackage() {
493 // empty
494}
495
496IOStream* D3MFOpcPackage::RootStream() const {
497 return mRootStream;
498}
499
500std::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