1 | /* |
2 | Open Asset Import Library (assimp) |
3 | ---------------------------------------------------------------------- |
4 | |
5 | Copyright (c) 2006-2022, 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 | #include "Compression.h" |
43 | #include <assimp/ai_assert.h> |
44 | #include <assimp/Exceptional.h> |
45 | |
46 | namespace Assimp { |
47 | |
48 | struct Compression::impl { |
49 | bool mOpen; |
50 | z_stream mZSstream; |
51 | FlushMode mFlushMode; |
52 | |
53 | impl() : |
54 | mOpen(false), |
55 | mZSstream(), |
56 | mFlushMode(Compression::FlushMode::NoFlush) { |
57 | // empty |
58 | } |
59 | }; |
60 | |
61 | Compression::Compression() : |
62 | mImpl(new impl) { |
63 | // empty |
64 | } |
65 | |
66 | Compression::~Compression() { |
67 | ai_assert(mImpl != nullptr); |
68 | |
69 | delete mImpl; |
70 | } |
71 | |
72 | bool Compression::open(Format format, FlushMode flush, int windowBits) { |
73 | ai_assert(mImpl != nullptr); |
74 | |
75 | if (mImpl->mOpen) { |
76 | return false; |
77 | } |
78 | |
79 | // build a zlib stream |
80 | mImpl->mZSstream.opaque = Z_NULL; |
81 | mImpl->mZSstream.zalloc = Z_NULL; |
82 | mImpl->mZSstream.zfree = Z_NULL; |
83 | mImpl->mFlushMode = flush; |
84 | if (format == Format::Binary) { |
85 | mImpl->mZSstream.data_type = Z_BINARY; |
86 | } else { |
87 | mImpl->mZSstream.data_type = Z_ASCII; |
88 | } |
89 | |
90 | // raw decompression without a zlib or gzip header |
91 | if (windowBits == 0) { |
92 | inflateInit(&mImpl->mZSstream); |
93 | } else { |
94 | inflateInit2(&mImpl->mZSstream, windowBits); |
95 | } |
96 | mImpl->mOpen = true; |
97 | |
98 | return mImpl->mOpen; |
99 | } |
100 | |
101 | static int getFlushMode(Compression::FlushMode flush) { |
102 | int z_flush = 0; |
103 | switch (flush) { |
104 | case Compression::FlushMode::NoFlush: |
105 | z_flush = Z_NO_FLUSH; |
106 | break; |
107 | case Compression::FlushMode::Block: |
108 | z_flush = Z_BLOCK; |
109 | break; |
110 | case Compression::FlushMode::Tree: |
111 | z_flush = Z_TREES; |
112 | break; |
113 | case Compression::FlushMode::SyncFlush: |
114 | z_flush = Z_SYNC_FLUSH; |
115 | break; |
116 | case Compression::FlushMode::Finish: |
117 | z_flush = Z_FINISH; |
118 | break; |
119 | default: |
120 | ai_assert(false); |
121 | break; |
122 | } |
123 | |
124 | return z_flush; |
125 | } |
126 | |
127 | constexpr size_t MYBLOCK = 32786; |
128 | |
129 | size_t Compression::decompress(const void *data, size_t in, std::vector<char> &uncompressed) { |
130 | ai_assert(mImpl != nullptr); |
131 | if (data == nullptr || in == 0) { |
132 | return 0l; |
133 | } |
134 | |
135 | mImpl->mZSstream.next_in = (Bytef*)(data); |
136 | mImpl->mZSstream.avail_in = (uInt)in; |
137 | |
138 | int ret = 0; |
139 | size_t total = 0l; |
140 | const int flushMode = getFlushMode(flush: mImpl->mFlushMode); |
141 | if (flushMode == Z_FINISH) { |
142 | mImpl->mZSstream.avail_out = static_cast<uInt>(uncompressed.size()); |
143 | mImpl->mZSstream.next_out = reinterpret_cast<Bytef *>(&*uncompressed.begin()); |
144 | ret = inflate(strm: &mImpl->mZSstream, Z_FINISH); |
145 | |
146 | if (ret != Z_STREAM_END && ret != Z_OK) { |
147 | throw DeadlyImportError("Compression" , "Failure decompressing this file using gzip." ); |
148 | } |
149 | total = mImpl->mZSstream.avail_out; |
150 | } else { |
151 | do { |
152 | Bytef block[MYBLOCK] = {}; |
153 | mImpl->mZSstream.avail_out = MYBLOCK; |
154 | mImpl->mZSstream.next_out = block; |
155 | |
156 | ret = inflate(strm: &mImpl->mZSstream, flush: flushMode); |
157 | |
158 | if (ret != Z_STREAM_END && ret != Z_OK) { |
159 | throw DeadlyImportError("Compression" , "Failure decompressing this file using gzip." ); |
160 | } |
161 | const size_t have = MYBLOCK - mImpl->mZSstream.avail_out; |
162 | total += have; |
163 | uncompressed.resize(new_size: total); |
164 | ::memcpy(dest: uncompressed.data() + total - have, src: block, n: have); |
165 | } while (ret != Z_STREAM_END); |
166 | } |
167 | |
168 | return total; |
169 | } |
170 | |
171 | size_t Compression::decompressBlock(const void *data, size_t in, char *out, size_t availableOut) { |
172 | ai_assert(mImpl != nullptr); |
173 | if (data == nullptr || in == 0 || out == nullptr || availableOut == 0) { |
174 | return 0l; |
175 | } |
176 | |
177 | // push data to the stream |
178 | mImpl->mZSstream.next_in = (Bytef *)data; |
179 | mImpl->mZSstream.avail_in = (uInt)in; |
180 | mImpl->mZSstream.next_out = (Bytef *)out; |
181 | mImpl->mZSstream.avail_out = (uInt)availableOut; |
182 | |
183 | // and decompress the data .... |
184 | int ret = ::inflate(strm: &mImpl->mZSstream, Z_SYNC_FLUSH); |
185 | if (ret != Z_OK && ret != Z_STREAM_END) { |
186 | throw DeadlyImportError("X: Failed to decompress MSZIP-compressed data" ); |
187 | } |
188 | |
189 | ::inflateReset(strm: &mImpl->mZSstream); |
190 | ::inflateSetDictionary(strm: &mImpl->mZSstream, dictionary: (const Bytef *)out, dictLength: (uInt)availableOut - mImpl->mZSstream.avail_out); |
191 | |
192 | return availableOut - (size_t)mImpl->mZSstream.avail_out; |
193 | } |
194 | |
195 | bool Compression::isOpen() const { |
196 | ai_assert(mImpl != nullptr); |
197 | |
198 | return mImpl->mOpen; |
199 | } |
200 | |
201 | bool Compression::close() { |
202 | ai_assert(mImpl != nullptr); |
203 | |
204 | if (!mImpl->mOpen) { |
205 | return false; |
206 | } |
207 | |
208 | inflateEnd(strm: &mImpl->mZSstream); |
209 | mImpl->mOpen = false; |
210 | |
211 | return true; |
212 | } |
213 | |
214 | } // namespace Assimp |
215 | |