1/*
2Open Asset Import Library (assimp)
3----------------------------------------------------------------------
4
5Copyright (c) 2006-2022, 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/** @file ZipArchiveIOSystem.cpp
43 * @brief Zip File I/O implementation for #Importer
44 */
45
46#include <assimp/BaseImporter.h>
47#include <assimp/ZipArchiveIOSystem.h>
48
49#include <assimp/ai_assert.h>
50
51#include <map>
52#include <memory>
53
54#ifdef ASSIMP_USE_HUNTER
55# include <minizip/unzip.h>
56#else
57# include <unzip.h>
58#endif
59
60namespace Assimp {
61
62// ----------------------------------------------------------------
63// A read-only file inside a ZIP
64
65class ZipFile : public IOStream {
66 friend class ZipFileInfo;
67 explicit ZipFile(std::string &filename, size_t size);
68
69public:
70 std::string m_Filename;
71 virtual ~ZipFile();
72
73 // IOStream interface
74 size_t Read(void *pvBuffer, size_t pSize, size_t pCount) override;
75 size_t Write(const void * /*pvBuffer*/, size_t /*pSize*/, size_t /*pCount*/) override { return 0; }
76 size_t FileSize() const override;
77 aiReturn Seek(size_t pOffset, aiOrigin pOrigin) override;
78 size_t Tell() const override;
79 void Flush() override {}
80
81private:
82 size_t m_Size = 0;
83 size_t m_SeekPtr = 0;
84 std::unique_ptr<uint8_t[]> m_Buffer;
85};
86
87
88// ----------------------------------------------------------------
89// Wraps an existing Assimp::IOSystem for unzip
90class IOSystem2Unzip {
91public:
92 static voidpf open(voidpf opaque, const char *filename, int mode);
93 static voidpf opendisk(voidpf opaque, voidpf stream, uint32_t number_disk, int mode);
94 static uLong read(voidpf opaque, voidpf stream, void *buf, uLong size);
95 static uLong write(voidpf opaque, voidpf stream, const void *buf, uLong size);
96 static long tell(voidpf opaque, voidpf stream);
97 static long seek(voidpf opaque, voidpf stream, uLong offset, int origin);
98 static int close(voidpf opaque, voidpf stream);
99 static int testerror(voidpf opaque, voidpf stream);
100 static zlib_filefunc_def get(IOSystem *pIOHandler);
101};
102
103voidpf IOSystem2Unzip::open(voidpf opaque, const char *filename, int mode) {
104 IOSystem *io_system = reinterpret_cast<IOSystem *>(opaque);
105
106 const char *mode_fopen = nullptr;
107 if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) == ZLIB_FILEFUNC_MODE_READ) {
108 mode_fopen = "rb";
109 } else {
110 if (mode & ZLIB_FILEFUNC_MODE_EXISTING) {
111 mode_fopen = "r+b";
112 } else {
113 if (mode & ZLIB_FILEFUNC_MODE_CREATE) {
114 mode_fopen = "wb";
115 }
116 }
117 }
118
119 return (voidpf)io_system->Open(pFile: filename, pMode: mode_fopen);
120}
121
122voidpf IOSystem2Unzip::opendisk(voidpf opaque, voidpf stream, uint32_t number_disk, int mode) {
123 ZipFile *io_stream = (ZipFile *)stream;
124 voidpf ret = NULL;
125 int i;
126
127 char *disk_filename = (char*)malloc(size: io_stream->m_Filename.length() + 1);
128 strncpy(dest: disk_filename, src: io_stream->m_Filename.c_str(), n: io_stream->m_Filename.length() + 1);
129 for (i = (int)io_stream->m_Filename.length() - 1; i >= 0; i -= 1)
130 {
131 if (disk_filename[i] != '.')
132 continue;
133 snprintf(s: &disk_filename[i], maxlen: io_stream->m_Filename.length() - size_t(i), format: ".z%02u", number_disk + 1);
134 break;
135 }
136
137 if (i >= 0)
138 ret = open(opaque, filename: disk_filename, mode);
139
140 free(ptr: disk_filename);
141 return ret;
142}
143
144uLong IOSystem2Unzip::read(voidpf /*opaque*/, voidpf stream, void *buf, uLong size) {
145 IOStream *io_stream = (IOStream *)stream;
146
147 return static_cast<uLong>(io_stream->Read(pvBuffer: buf, pSize: 1, pCount: size));
148}
149
150uLong IOSystem2Unzip::write(voidpf /*opaque*/, voidpf stream, const void *buf, uLong size) {
151 IOStream *io_stream = (IOStream *)stream;
152
153 return static_cast<uLong>(io_stream->Write(pvBuffer: buf, pSize: 1, pCount: size));
154}
155
156long IOSystem2Unzip::tell(voidpf /*opaque*/, voidpf stream) {
157 IOStream *io_stream = (IOStream *)stream;
158
159 return static_cast<long>(io_stream->Tell());
160}
161
162long IOSystem2Unzip::seek(voidpf /*opaque*/, voidpf stream, uLong offset, int origin) {
163 IOStream *io_stream = (IOStream *)stream;
164
165 aiOrigin assimp_origin;
166 switch (origin) {
167 default:
168 case ZLIB_FILEFUNC_SEEK_CUR:
169 assimp_origin = aiOrigin_CUR;
170 break;
171 case ZLIB_FILEFUNC_SEEK_END:
172 assimp_origin = aiOrigin_END;
173 break;
174 case ZLIB_FILEFUNC_SEEK_SET:
175 assimp_origin = aiOrigin_SET;
176 break;
177 }
178
179 return (io_stream->Seek(pOffset: offset, pOrigin: assimp_origin) == aiReturn_SUCCESS ? 0 : -1);
180}
181
182int IOSystem2Unzip::close(voidpf opaque, voidpf stream) {
183 IOSystem *io_system = (IOSystem *)opaque;
184 IOStream *io_stream = (IOStream *)stream;
185
186 io_system->Close(pFile: io_stream);
187
188 return 0;
189}
190
191int IOSystem2Unzip::testerror(voidpf /*opaque*/, voidpf /*stream*/) {
192 return 0;
193}
194
195zlib_filefunc_def IOSystem2Unzip::get(IOSystem *pIOHandler) {
196 zlib_filefunc_def mapping;
197
198 mapping.zopen_file = (open_file_func)open;
199#ifdef _UNZ_H
200 mapping.zopendisk_file = (opendisk_file_func)opendisk;
201#endif
202 mapping.zread_file = (read_file_func)read;
203 mapping.zwrite_file = (write_file_func)write;
204 mapping.ztell_file = (tell_file_func)tell;
205 mapping.zseek_file = (seek_file_func)seek;
206 mapping.zclose_file = (close_file_func)close;
207 mapping.zerror_file = testerror;
208
209 mapping.opaque = reinterpret_cast<voidpf>(pIOHandler);
210
211 return mapping;
212}
213
214// ----------------------------------------------------------------
215// Info about a read-only file inside a ZIP
216class ZipFileInfo {
217public:
218 explicit ZipFileInfo(unzFile zip_handle, size_t size);
219
220 // Allocate and Extract data from the ZIP
221 ZipFile *Extract(std::string &filename, unzFile zip_handle) const;
222
223private:
224 size_t m_Size = 0;
225 unz_file_pos_s m_ZipFilePos;
226};
227
228ZipFileInfo::ZipFileInfo(unzFile zip_handle, size_t size) :
229 m_Size(size) {
230 ai_assert(m_Size != 0);
231 // Workaround for MSVC 2013 - C2797
232 m_ZipFilePos.num_of_file = 0;
233 m_ZipFilePos.pos_in_zip_directory = 0;
234 unzGetFilePos(file: zip_handle, file_pos: &(m_ZipFilePos));
235}
236
237ZipFile *ZipFileInfo::Extract(std::string &filename, unzFile zip_handle) const {
238 // Find in the ZIP. This cannot fail
239 unz_file_pos_s *filepos = const_cast<unz_file_pos_s *>(&(m_ZipFilePos));
240 if (unzGoToFilePos(file: zip_handle, file_pos: filepos) != UNZ_OK)
241 return nullptr;
242
243 if (unzOpenCurrentFile(file: zip_handle) != UNZ_OK)
244 return nullptr;
245
246 ZipFile *zip_file = new ZipFile(filename, m_Size);
247
248 // Unzip has a limit of UINT16_MAX bytes buffer
249 uint16_t unzipBufferSize = zip_file->m_Size <= UINT16_MAX ? static_cast<uint16_t>(zip_file->m_Size) : UINT16_MAX;
250 std::unique_ptr<uint8_t[]> unzipBuffer = std::unique_ptr<uint8_t[]>(new uint8_t[unzipBufferSize]);
251 size_t readCount = 0;
252 while (readCount < zip_file->m_Size)
253 {
254 size_t bufferSize = zip_file->m_Size - readCount;
255 if (bufferSize > UINT16_MAX) {
256 bufferSize = UINT16_MAX;
257 }
258
259 int ret = unzReadCurrentFile(file: zip_handle, buf: unzipBuffer.get(), len: static_cast<unsigned int>(bufferSize));
260 if (ret != static_cast<int>(bufferSize))
261 {
262 // Failed, release the memory
263 delete zip_file;
264 zip_file = nullptr;
265 break;
266 }
267
268 std::memcpy(dest: zip_file->m_Buffer.get() + readCount, src: unzipBuffer.get(), n: ret);
269 readCount += ret;
270 }
271
272 ai_assert(unzCloseCurrentFile(zip_handle) == UNZ_OK);
273 return zip_file;
274}
275
276ZipFile::ZipFile(std::string &filename, size_t size) :
277 m_Filename(filename), m_Size(size) {
278 ai_assert(m_Size != 0);
279 m_Buffer = std::unique_ptr<uint8_t[]>(new uint8_t[m_Size]);
280}
281
282ZipFile::~ZipFile() = default;
283
284size_t ZipFile::Read(void *pvBuffer, size_t pSize, size_t pCount) {
285 // Should be impossible
286 ai_assert(m_Buffer != nullptr);
287 ai_assert(nullptr != pvBuffer);
288 ai_assert(0 != pSize);
289 ai_assert(0 != pCount);
290
291 // Clip down to file size
292 size_t byteSize = pSize * pCount;
293 if ((byteSize + m_SeekPtr) > m_Size) {
294 pCount = (m_Size - m_SeekPtr) / pSize;
295 byteSize = pSize * pCount;
296 if (byteSize == 0) {
297 return 0;
298 }
299 }
300
301 std::memcpy(dest: pvBuffer, src: m_Buffer.get() + m_SeekPtr, n: byteSize);
302
303 m_SeekPtr += byteSize;
304
305 return pCount;
306}
307
308size_t ZipFile::FileSize() const {
309 return m_Size;
310}
311
312aiReturn ZipFile::Seek(size_t pOffset, aiOrigin pOrigin) {
313 switch (pOrigin) {
314 case aiOrigin_SET: {
315 if (pOffset > m_Size) return aiReturn_FAILURE;
316 m_SeekPtr = pOffset;
317 return aiReturn_SUCCESS;
318 }
319
320 case aiOrigin_CUR: {
321 if ((pOffset + m_SeekPtr) > m_Size) return aiReturn_FAILURE;
322 m_SeekPtr += pOffset;
323 return aiReturn_SUCCESS;
324 }
325
326 case aiOrigin_END: {
327 if (pOffset > m_Size) return aiReturn_FAILURE;
328 m_SeekPtr = m_Size - pOffset;
329 return aiReturn_SUCCESS;
330 }
331 default:;
332 }
333
334 return aiReturn_FAILURE;
335}
336
337size_t ZipFile::Tell() const {
338 return m_SeekPtr;
339}
340
341// ----------------------------------------------------------------
342// pImpl of the Zip Archive IO
343class ZipArchiveIOSystem::Implement {
344public:
345 static const unsigned int FileNameSize = 256;
346
347 Implement(IOSystem *pIOHandler, const char *pFilename, const char *pMode);
348 ~Implement();
349
350 bool isOpen() const;
351 void getFileList(std::vector<std::string> &rFileList);
352 void getFileListExtension(std::vector<std::string> &rFileList, const std::string &extension);
353 bool Exists(std::string &filename);
354 IOStream *OpenFile(std::string &filename);
355
356 static void SimplifyFilename(std::string &filename);
357
358private:
359 void MapArchive();
360
361private:
362 typedef std::map<std::string, ZipFileInfo> ZipFileInfoMap;
363
364 unzFile m_ZipFileHandle = nullptr;
365 ZipFileInfoMap m_ArchiveMap;
366};
367
368ZipArchiveIOSystem::Implement::Implement(IOSystem *pIOHandler, const char *pFilename, const char *pMode) {
369 ai_assert(strcmp(pMode, "r") == 0);
370 ai_assert(pFilename != nullptr);
371 if (pFilename[0] == 0 || nullptr == pMode) {
372 return;
373 }
374
375 zlib_filefunc_def mapping = IOSystem2Unzip::get(pIOHandler);
376 m_ZipFileHandle = unzOpen2(path: pFilename, pzlib_filefunc_def: &mapping);
377}
378
379ZipArchiveIOSystem::Implement::~Implement() {
380 if (m_ZipFileHandle != nullptr) {
381 unzClose(file: m_ZipFileHandle);
382 }
383}
384
385void ZipArchiveIOSystem::Implement::MapArchive() {
386 if (m_ZipFileHandle == nullptr)
387 return;
388
389 if (!m_ArchiveMap.empty())
390 return;
391
392 // At first ensure file is already open
393 if (unzGoToFirstFile(file: m_ZipFileHandle) != UNZ_OK)
394 return;
395
396 // Loop over all files
397 do {
398 char filename[FileNameSize];
399 unz_file_info fileInfo;
400
401 if (unzGetCurrentFileInfo(file: m_ZipFileHandle, pfile_info: &fileInfo, szFileName: filename, fileNameBufferSize: FileNameSize, extraField: nullptr, extraFieldBufferSize: 0, szComment: nullptr, commentBufferSize: 0) == UNZ_OK) {
402 if (fileInfo.uncompressed_size != 0 && fileInfo.size_filename <= FileNameSize) {
403 std::string filename_string(filename, fileInfo.size_filename);
404 SimplifyFilename(filename&: filename_string);
405 m_ArchiveMap.emplace(args&: filename_string, args: ZipFileInfo(m_ZipFileHandle, fileInfo.uncompressed_size));
406 }
407 }
408 } while (unzGoToNextFile(file: m_ZipFileHandle) != UNZ_END_OF_LIST_OF_FILE);
409}
410
411bool ZipArchiveIOSystem::Implement::isOpen() const {
412 return (m_ZipFileHandle != nullptr);
413}
414
415void ZipArchiveIOSystem::Implement::getFileList(std::vector<std::string> &rFileList) {
416 MapArchive();
417 rFileList.clear();
418
419 for (const auto &file : m_ArchiveMap) {
420 rFileList.push_back(x: file.first);
421 }
422}
423
424void ZipArchiveIOSystem::Implement::getFileListExtension(std::vector<std::string> &rFileList, const std::string &extension) {
425 MapArchive();
426 rFileList.clear();
427
428 for (const auto &file : m_ArchiveMap) {
429 if (extension == BaseImporter::GetExtension(pFile: file.first))
430 rFileList.push_back(x: file.first);
431 }
432}
433
434bool ZipArchiveIOSystem::Implement::Exists(std::string &filename) {
435 MapArchive();
436
437 ZipFileInfoMap::const_iterator it = m_ArchiveMap.find(x: filename);
438 return (it != m_ArchiveMap.end());
439}
440
441IOStream *ZipArchiveIOSystem::Implement::OpenFile(std::string &filename) {
442 MapArchive();
443
444 SimplifyFilename(filename);
445
446 // Find in the map
447 ZipFileInfoMap::const_iterator zip_it = m_ArchiveMap.find(x: filename);
448 if (zip_it == m_ArchiveMap.cend())
449 return nullptr;
450
451 const ZipFileInfo &zip_file = (*zip_it).second;
452 return zip_file.Extract(filename, zip_handle: m_ZipFileHandle);
453}
454
455inline void ReplaceAll(std::string &data, const std::string &before, const std::string &after) {
456 size_t pos = data.find(str: before);
457 while (pos != std::string::npos) {
458 data.replace(pos: pos, n: before.size(), str: after);
459 pos = data.find(str: before, pos: pos + after.size());
460 }
461}
462
463inline void ReplaceAllChar(std::string &data, const char before, const char after) {
464 size_t pos = data.find(c: before);
465 while (pos != std::string::npos) {
466 data[pos] = after;
467 pos = data.find(c: before, pos: pos + 1);
468 }
469}
470
471void ZipArchiveIOSystem::Implement::SimplifyFilename(std::string &filename) {
472 ReplaceAllChar(data&: filename, before: '\\', after: '/');
473
474 // Remove all . and / from the beginning of the path
475 size_t pos = filename.find_first_not_of(s: "./");
476 if (pos != 0)
477 filename.erase(pos: 0, n: pos);
478
479 // Simplify "my/folder/../file.png" constructions, if any
480 static const std::string relative("/../");
481 const size_t relsize = relative.size() - 1;
482 pos = filename.find(str: relative);
483 while (pos != std::string::npos) {
484 // Previous slash
485 size_t prevpos = filename.rfind(c: '/', pos: pos - 1);
486 if (prevpos == pos)
487 filename.erase(pos: 0, n: pos + relative.size());
488 else
489 filename.erase(pos: prevpos, n: pos + relsize - prevpos);
490
491 pos = filename.find(str: relative);
492 }
493}
494
495ZipArchiveIOSystem::ZipArchiveIOSystem(IOSystem *pIOHandler, const char *pFilename, const char *pMode) :
496 pImpl(new Implement(pIOHandler, pFilename, pMode)) {
497}
498
499// ----------------------------------------------------------------
500// The ZipArchiveIO
501ZipArchiveIOSystem::ZipArchiveIOSystem(IOSystem *pIOHandler, const std::string &rFilename, const char *pMode) :
502 pImpl(new Implement(pIOHandler, rFilename.c_str(), pMode)) {
503}
504
505ZipArchiveIOSystem::~ZipArchiveIOSystem() {
506 delete pImpl;
507}
508
509bool ZipArchiveIOSystem::Exists(const char *pFilename) const {
510 ai_assert(pFilename != nullptr);
511
512 if (pFilename == nullptr) {
513 return false;
514 }
515
516 std::string filename(pFilename);
517 return pImpl->Exists(filename);
518}
519
520// This is always '/' in a ZIP
521char ZipArchiveIOSystem::getOsSeparator() const {
522 return '/';
523}
524
525// Only supports Reading
526IOStream *ZipArchiveIOSystem::Open(const char *pFilename, const char *pMode) {
527 ai_assert(pFilename != nullptr);
528
529 for (size_t i = 0; pMode[i] != 0; ++i) {
530 ai_assert(pMode[i] != 'w');
531 if (pMode[i] == 'w')
532 return nullptr;
533 }
534
535 std::string filename(pFilename);
536 return pImpl->OpenFile(filename);
537}
538
539void ZipArchiveIOSystem::Close(IOStream *pFile) {
540 delete pFile;
541}
542
543bool ZipArchiveIOSystem::isOpen() const {
544 return (pImpl->isOpen());
545}
546
547void ZipArchiveIOSystem::getFileList(std::vector<std::string> &rFileList) const {
548 return pImpl->getFileList(rFileList);
549}
550
551void ZipArchiveIOSystem::getFileListExtension(std::vector<std::string> &rFileList, const std::string &extension) const {
552 return pImpl->getFileListExtension(rFileList, extension);
553}
554
555bool ZipArchiveIOSystem::isZipArchive(IOSystem *pIOHandler, const char *pFilename) {
556 Implement tmp(pIOHandler, pFilename, "r");
557 return tmp.isOpen();
558}
559
560bool ZipArchiveIOSystem::isZipArchive(IOSystem *pIOHandler, const std::string &rFilename) {
561 return isZipArchive(pIOHandler, pFilename: rFilename.c_str());
562}
563
564} // namespace Assimp
565

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