1/*
2Open Asset Import Library (assimp)
3----------------------------------------------------------------------
4
5Copyright (c) 2006-2019, assimp team
6
7
8All rights reserved.
9
10Redistribution and use of this software in source and binary forms,
11with or without modification, are permitted provided that the
12following conditions are met:
13
14* Redistributions of source code must retain the above
15 copyright notice, this list of conditions and the
16 following disclaimer.
17
18* Redistributions in binary form must reproduce the above
19 copyright notice, this list of conditions and the
20 following disclaimer in the documentation and/or other
21 materials provided with the distribution.
22
23* Neither the name of the assimp team, nor the names of its
24 contributors may be used to endorse or promote products
25 derived from this software without specific prior
26 written permission of the assimp team.
27
28THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39
40----------------------------------------------------------------------
41*/
42
43/** @file ZipArchiveIOSystem.cpp
44 * @brief Zip File I/O implementation for #Importer
45 */
46
47#include <assimp/ZipArchiveIOSystem.h>
48#include <assimp/BaseImporter.h>
49
50#include <assimp/ai_assert.h>
51
52#include <map>
53#include <memory>
54
55#ifdef ASSIMP_USE_HUNTER
56# include <minizip/unzip.h>
57#else
58# include <unzip.h>
59#endif
60
61namespace Assimp {
62 // ----------------------------------------------------------------
63 // Wraps an existing Assimp::IOSystem for unzip
64 class IOSystem2Unzip {
65 public:
66 static voidpf open(voidpf opaque, const char* filename, int mode);
67 static uLong read(voidpf opaque, voidpf stream, void* buf, uLong size);
68 static uLong write(voidpf opaque, voidpf stream, const void* buf, uLong size);
69 static long tell(voidpf opaque, voidpf stream);
70 static long seek(voidpf opaque, voidpf stream, uLong offset, int origin);
71 static int close(voidpf opaque, voidpf stream);
72 static int testerror(voidpf opaque, voidpf stream);
73 static zlib_filefunc_def get(IOSystem* pIOHandler);
74 };
75
76 voidpf IOSystem2Unzip::open(voidpf opaque, const char* filename, int mode) {
77 IOSystem* io_system = reinterpret_cast<IOSystem*>(opaque);
78
79 const char* mode_fopen = nullptr;
80 if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) == ZLIB_FILEFUNC_MODE_READ) {
81 mode_fopen = "rb";
82 }
83 else {
84 if (mode & ZLIB_FILEFUNC_MODE_EXISTING) {
85 mode_fopen = "r+b";
86 }
87 else {
88 if (mode & ZLIB_FILEFUNC_MODE_CREATE) {
89 mode_fopen = "wb";
90 }
91 }
92 }
93
94 return (voidpf)io_system->Open(pFile: filename, pMode: mode_fopen);
95 }
96
97 uLong IOSystem2Unzip::read(voidpf /*opaque*/, voidpf stream, void* buf, uLong size) {
98 IOStream* io_stream = (IOStream*)stream;
99
100 return static_cast<uLong>(io_stream->Read(pvBuffer: buf, pSize: 1, pCount: size));
101 }
102
103 uLong IOSystem2Unzip::write(voidpf /*opaque*/, voidpf stream, const void* buf, uLong size) {
104 IOStream* io_stream = (IOStream*)stream;
105
106 return static_cast<uLong>(io_stream->Write(pvBuffer: buf, pSize: 1, pCount: size));
107 }
108
109 long IOSystem2Unzip::tell(voidpf /*opaque*/, voidpf stream) {
110 IOStream* io_stream = (IOStream*)stream;
111
112 return static_cast<long>(io_stream->Tell());
113 }
114
115 long IOSystem2Unzip::seek(voidpf /*opaque*/, voidpf stream, uLong offset, int origin) {
116 IOStream* io_stream = (IOStream*)stream;
117
118 aiOrigin assimp_origin;
119 switch (origin) {
120 default:
121 case ZLIB_FILEFUNC_SEEK_CUR:
122 assimp_origin = aiOrigin_CUR;
123 break;
124 case ZLIB_FILEFUNC_SEEK_END:
125 assimp_origin = aiOrigin_END;
126 break;
127 case ZLIB_FILEFUNC_SEEK_SET:
128 assimp_origin = aiOrigin_SET;
129 break;
130 }
131
132 return (io_stream->Seek(pOffset: offset, pOrigin: assimp_origin) == aiReturn_SUCCESS ? 0 : -1);
133 }
134
135 int IOSystem2Unzip::close(voidpf opaque, voidpf stream) {
136 IOSystem* io_system = (IOSystem*)opaque;
137 IOStream* io_stream = (IOStream*)stream;
138
139 io_system->Close(pFile: io_stream);
140
141 return 0;
142 }
143
144 int IOSystem2Unzip::testerror(voidpf /*opaque*/, voidpf /*stream*/) {
145 return 0;
146 }
147
148 zlib_filefunc_def IOSystem2Unzip::get(IOSystem* pIOHandler) {
149 zlib_filefunc_def mapping;
150
151#ifdef ASSIMP_USE_HUNTER
152 mapping.zopen_file = (open_file_func)open;
153 mapping.zread_file = (read_file_func)read;
154 mapping.zwrite_file = (write_file_func)write;
155 mapping.ztell_file = (tell_file_func)tell;
156 mapping.zseek_file = (seek_file_func)seek;
157 mapping.zclose_file = (close_file_func)close;
158 mapping.zerror_file = (error_file_func)testerror;
159#else
160 mapping.zopen_file = open;
161 mapping.zread_file = read;
162 mapping.zwrite_file = write;
163 mapping.ztell_file = tell;
164 mapping.zseek_file = seek;
165 mapping.zclose_file = close;
166 mapping.zerror_file = testerror;
167#endif
168 mapping.opaque = reinterpret_cast<voidpf>(pIOHandler);
169
170 return mapping;
171 }
172
173 // ----------------------------------------------------------------
174 // A read-only file inside a ZIP
175
176 class ZipFile : public IOStream {
177 friend class ZipFileInfo;
178 explicit ZipFile(size_t size);
179 public:
180 virtual ~ZipFile();
181
182 // IOStream interface
183 size_t Read(void* pvBuffer, size_t pSize, size_t pCount) override;
184 size_t Write(const void* /*pvBuffer*/, size_t /*pSize*/, size_t /*pCount*/) override { return 0; }
185 size_t FileSize() const override;
186 aiReturn Seek(size_t pOffset, aiOrigin pOrigin) override;
187 size_t Tell() const override;
188 void Flush() override {}
189
190 private:
191 size_t m_Size = 0;
192 size_t m_SeekPtr = 0;
193 std::unique_ptr<uint8_t[]> m_Buffer;
194 };
195
196
197 // ----------------------------------------------------------------
198 // Info about a read-only file inside a ZIP
199 class ZipFileInfo
200 {
201 public:
202 explicit ZipFileInfo(unzFile zip_handle, size_t size);
203
204 // Allocate and Extract data from the ZIP
205 ZipFile * Extract(unzFile zip_handle) const;
206
207 private:
208 size_t m_Size = 0;
209 unz_file_pos_s m_ZipFilePos;
210 };
211
212 ZipFileInfo::ZipFileInfo(unzFile zip_handle, size_t size)
213 : m_Size(size) {
214 ai_assert(m_Size != 0);
215 // Workaround for MSVC 2013 - C2797
216 m_ZipFilePos.num_of_file = 0;
217 m_ZipFilePos.pos_in_zip_directory = 0;
218 unzGetFilePos(file: zip_handle, file_pos: &(m_ZipFilePos));
219 }
220
221 ZipFile * ZipFileInfo::Extract(unzFile zip_handle) const {
222 // Find in the ZIP. This cannot fail
223 unz_file_pos_s *filepos = const_cast<unz_file_pos_s*>(&(m_ZipFilePos));
224 if (unzGoToFilePos(file: zip_handle, file_pos: filepos) != UNZ_OK)
225 return nullptr;
226
227 if (unzOpenCurrentFile(file: zip_handle) != UNZ_OK)
228 return nullptr;
229
230 ZipFile *zip_file = new ZipFile(m_Size);
231
232 if (unzReadCurrentFile(file: zip_handle, buf: zip_file->m_Buffer.get(), len: static_cast<unsigned int>(m_Size)) != static_cast<int>(m_Size))
233 {
234 // Failed, release the memory
235 delete zip_file;
236 zip_file = nullptr;
237 }
238
239 ai_assert(unzCloseCurrentFile(zip_handle) == UNZ_OK);
240 return zip_file;
241 }
242
243 ZipFile::ZipFile(size_t size)
244 : m_Size(size) {
245 ai_assert(m_Size != 0);
246 m_Buffer = std::unique_ptr<uint8_t[]>(new uint8_t[m_Size]);
247 }
248
249 ZipFile::~ZipFile() {
250 }
251
252 size_t ZipFile::Read(void* pvBuffer, size_t pSize, size_t pCount) {
253 // Should be impossible
254 ai_assert(m_Buffer != nullptr);
255 ai_assert(NULL != pvBuffer && 0 != pSize && 0 != pCount);
256
257 // Clip down to file size
258 size_t byteSize = pSize * pCount;
259 if ((byteSize + m_SeekPtr) > m_Size)
260 {
261 pCount = (m_Size - m_SeekPtr) / pSize;
262 byteSize = pSize * pCount;
263 if (byteSize == 0)
264 return 0;
265 }
266
267 std::memcpy(dest: pvBuffer, src: m_Buffer.get() + m_SeekPtr, n: byteSize);
268
269 m_SeekPtr += byteSize;
270
271 return pCount;
272 }
273
274 size_t ZipFile::FileSize() const {
275 return m_Size;
276 }
277
278 aiReturn ZipFile::Seek(size_t pOffset, aiOrigin pOrigin) {
279 switch (pOrigin)
280 {
281 case aiOrigin_SET: {
282 if (pOffset > m_Size) return aiReturn_FAILURE;
283 m_SeekPtr = pOffset;
284 return aiReturn_SUCCESS;
285 }
286
287 case aiOrigin_CUR: {
288 if ((pOffset + m_SeekPtr) > m_Size) return aiReturn_FAILURE;
289 m_SeekPtr += pOffset;
290 return aiReturn_SUCCESS;
291 }
292
293 case aiOrigin_END: {
294 if (pOffset > m_Size) return aiReturn_FAILURE;
295 m_SeekPtr = m_Size - pOffset;
296 return aiReturn_SUCCESS;
297 }
298 default:;
299 }
300
301 return aiReturn_FAILURE;
302 }
303
304 size_t ZipFile::Tell() const {
305 return m_SeekPtr;
306 }
307
308 // ----------------------------------------------------------------
309 // pImpl of the Zip Archive IO
310 class ZipArchiveIOSystem::Implement {
311 public:
312 static const unsigned int FileNameSize = 256;
313
314 Implement(IOSystem* pIOHandler, const char* pFilename, const char* pMode);
315 ~Implement();
316
317 bool isOpen() const;
318 void getFileList(std::vector<std::string>& rFileList);
319 void getFileListExtension(std::vector<std::string>& rFileList, const std::string& extension);
320 bool Exists(std::string& filename);
321 IOStream* OpenFile(std::string& filename);
322
323 static void SimplifyFilename(std::string& filename);
324
325 private:
326 void MapArchive();
327
328 private:
329 typedef std::map<std::string, ZipFileInfo> ZipFileInfoMap;
330
331 unzFile m_ZipFileHandle = nullptr;
332 ZipFileInfoMap m_ArchiveMap;
333 };
334
335 ZipArchiveIOSystem::Implement::Implement(IOSystem* pIOHandler, const char* pFilename, const char* pMode) {
336 ai_assert(strcmp(pMode, "r") == 0);
337 ai_assert(pFilename != nullptr);
338 if (pFilename[0] == 0)
339 return;
340
341 zlib_filefunc_def mapping = IOSystem2Unzip::get(pIOHandler);
342 m_ZipFileHandle = unzOpen2(path: pFilename, pzlib_filefunc_def: &mapping);
343 }
344
345 ZipArchiveIOSystem::Implement::~Implement() {
346 m_ArchiveMap.clear();
347
348 if (m_ZipFileHandle != nullptr) {
349 unzClose(file: m_ZipFileHandle);
350 m_ZipFileHandle = nullptr;
351 }
352 }
353
354 void ZipArchiveIOSystem::Implement::MapArchive() {
355 if (m_ZipFileHandle == nullptr)
356 return;
357
358 if (!m_ArchiveMap.empty())
359 return;
360
361 // At first ensure file is already open
362 if (unzGoToFirstFile(file: m_ZipFileHandle) != UNZ_OK)
363 return;
364
365 // Loop over all files
366 do {
367 char filename[FileNameSize];
368 unz_file_info fileInfo;
369
370 if (unzGetCurrentFileInfo(file: m_ZipFileHandle, pfile_info: &fileInfo, szFileName: filename, fileNameBufferSize: FileNameSize, extraField: nullptr, extraFieldBufferSize: 0, szComment: nullptr, commentBufferSize: 0) == UNZ_OK) {
371 if (fileInfo.uncompressed_size != 0) {
372 std::string filename_string(filename, fileInfo.size_filename);
373 SimplifyFilename(filename&: filename_string);
374 m_ArchiveMap.emplace(args&: filename_string, args: ZipFileInfo(m_ZipFileHandle, fileInfo.uncompressed_size));
375 }
376 }
377 } while (unzGoToNextFile(file: m_ZipFileHandle) != UNZ_END_OF_LIST_OF_FILE);
378 }
379
380 bool ZipArchiveIOSystem::Implement::isOpen() const {
381 return (m_ZipFileHandle != nullptr);
382 }
383
384 void ZipArchiveIOSystem::Implement::getFileList(std::vector<std::string>& rFileList) {
385 MapArchive();
386 rFileList.clear();
387
388 for (const auto &file : m_ArchiveMap) {
389 rFileList.push_back(x: file.first);
390 }
391 }
392
393 void ZipArchiveIOSystem::Implement::getFileListExtension(std::vector<std::string>& rFileList, const std::string& extension) {
394 MapArchive();
395 rFileList.clear();
396
397 for (const auto &file : m_ArchiveMap) {
398 if (extension == BaseImporter::GetExtension(pFile: file.first))
399 rFileList.push_back(x: file.first);
400 }
401 }
402
403 bool ZipArchiveIOSystem::Implement::Exists(std::string& filename) {
404 MapArchive();
405
406 ZipFileInfoMap::const_iterator it = m_ArchiveMap.find(x: filename);
407 return (it != m_ArchiveMap.end());
408 }
409
410 IOStream * ZipArchiveIOSystem::Implement::OpenFile(std::string& filename) {
411 MapArchive();
412
413 SimplifyFilename(filename);
414
415 // Find in the map
416 ZipFileInfoMap::const_iterator zip_it = m_ArchiveMap.find(x: filename);
417 if (zip_it == m_ArchiveMap.cend())
418 return nullptr;
419
420 const ZipFileInfo &zip_file = (*zip_it).second;
421 return zip_file.Extract(zip_handle: m_ZipFileHandle);
422 }
423
424 inline void ReplaceAll(std::string& data, const std::string& before, const std::string& after) {
425 size_t pos = data.find(str: before);
426 while (pos != std::string::npos)
427 {
428 data.replace(pos: pos, n: before.size(), str: after);
429 pos = data.find(str: before, pos: pos + after.size());
430 }
431 }
432
433 inline void ReplaceAllChar(std::string& data, const char before, const char after) {
434 size_t pos = data.find(c: before);
435 while (pos != std::string::npos)
436 {
437 data[pos] = after;
438 pos = data.find(c: before, pos: pos + 1);
439 }
440 }
441
442 void ZipArchiveIOSystem::Implement::SimplifyFilename(std::string& filename)
443 {
444 ReplaceAllChar(data&: filename, before: '\\', after: '/');
445
446 // Remove all . and / from the beginning of the path
447 size_t pos = filename.find_first_not_of(s: "./");
448 if (pos != 0)
449 filename.erase(pos: 0, n: pos);
450
451 // Simplify "my/folder/../file.png" constructions, if any
452 static const std::string relative("/../");
453 const size_t relsize = relative.size() - 1;
454 pos = filename.find(str: relative);
455 while (pos != std::string::npos)
456 {
457 // Previous slash
458 size_t prevpos = filename.rfind(c: '/', pos: pos - 1);
459 if (prevpos == pos)
460 filename.erase(pos: 0, n: pos + relative.size());
461 else
462 filename.erase(pos: prevpos, n: pos + relsize - prevpos);
463
464 pos = filename.find(str: relative);
465 }
466 }
467
468 ZipArchiveIOSystem::ZipArchiveIOSystem(IOSystem* pIOHandler, const char* pFilename, const char* pMode)
469 : pImpl(new Implement(pIOHandler, pFilename, pMode)) {
470 }
471
472 // ----------------------------------------------------------------
473 // The ZipArchiveIO
474 ZipArchiveIOSystem::ZipArchiveIOSystem(IOSystem* pIOHandler, const std::string& rFilename, const char* pMode)
475 : pImpl(new Implement(pIOHandler, rFilename.c_str(), pMode))
476 {
477 }
478
479 ZipArchiveIOSystem::~ZipArchiveIOSystem() {
480 delete pImpl;
481 }
482
483 bool ZipArchiveIOSystem::Exists(const char* pFilename) const {
484 ai_assert(pFilename != nullptr);
485
486 if (pFilename == nullptr) {
487 return false;
488 }
489
490 std::string filename(pFilename);
491 return pImpl->Exists(filename);
492 }
493
494 // This is always '/' in a ZIP
495 char ZipArchiveIOSystem::getOsSeparator() const {
496 return '/';
497 }
498
499 // Only supports Reading
500 IOStream * ZipArchiveIOSystem::Open(const char* pFilename, const char* pMode) {
501 ai_assert(pFilename != nullptr);
502
503 for (size_t i = 0; pMode[i] != 0; ++i)
504 {
505 ai_assert(pMode[i] != 'w');
506 if (pMode[i] == 'w')
507 return nullptr;
508 }
509
510 std::string filename(pFilename);
511 return pImpl->OpenFile(filename);
512 }
513
514 void ZipArchiveIOSystem::Close(IOStream* pFile) {
515 delete pFile;
516 }
517
518 bool ZipArchiveIOSystem::isOpen() const {
519 return (pImpl->isOpen());
520 }
521
522 void ZipArchiveIOSystem::getFileList(std::vector<std::string>& rFileList) const {
523 return pImpl->getFileList(rFileList);
524 }
525
526 void ZipArchiveIOSystem::getFileListExtension(std::vector<std::string>& rFileList, const std::string& extension) const {
527 return pImpl->getFileListExtension(rFileList, extension);
528 }
529
530 bool ZipArchiveIOSystem::isZipArchive(IOSystem* pIOHandler, const char* pFilename) {
531 Implement tmp(pIOHandler, pFilename, "r");
532 return tmp.isOpen();
533 }
534
535 bool ZipArchiveIOSystem::isZipArchive(IOSystem* pIOHandler, const std::string& rFilename) {
536 return isZipArchive(pIOHandler, pFilename: rFilename.c_str());
537 }
538
539}
540

source code of qt3d/src/3rdparty/assimp/src/code/Common/ZipArchiveIOSystem.cpp