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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
143 | int IOSystem2Unzip::testerror(voidpf /*opaque*/, voidpf /*stream*/) { |
144 | return 0; |
145 | } |
146 | |
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 | |
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 | |
187 | ZipFile::~ZipFile() { |
188 | ::free(m_Buffer); |
189 | m_Buffer = NULL; |
190 | } |
191 | |
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 | |
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 | |
209 | size_t ZipFile::FileSize() const { |
210 | return m_Size; |
211 | } |
212 | |
213 | aiReturn ZipFile::Seek(size_t /*pOffset*/, aiOrigin /*pOrigin*/) { |
214 | return aiReturn_FAILURE; |
215 | } |
216 | |
217 | size_t ZipFile::Tell() const { |
218 | return 0; |
219 | } |
220 | |
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. |
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. |
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. |
277 | bool D3MFZipArchive::isOpen() const { |
278 | return (m_ZipFileHandle != NULL); |
279 | } |
280 | |
281 | // ------------------------------------------------------------------------------------------------ |
282 | // Returns true, if the filename is part of the archive. |
283 | 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. |
302 | 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. |
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. |
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. |
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. |
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: |
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 | |
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 | |
492 | D3MFOpcPackage::~D3MFOpcPackage() { |
493 | // empty |
494 | } |
495 | |
496 | IOStream* D3MFOpcPackage::RootStream() const { |
497 | return mRootStream; |
498 | } |
499 | |
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 | |