1 | /* |
2 | --------------------------------------------------------------------------- |
3 | Open Asset Import Library (assimp) |
4 | --------------------------------------------------------------------------- |
5 | |
6 | Copyright (c) 2006-2017, assimp team |
7 | |
8 | |
9 | All rights reserved. |
10 | |
11 | Redistribution and use of this software in source and binary forms, |
12 | with or without modification, are permitted provided that the following |
13 | conditions are met: |
14 | |
15 | * Redistributions of source code must retain the above |
16 | copyright notice, this list of conditions and the |
17 | following disclaimer. |
18 | |
19 | * Redistributions in binary form must reproduce the above |
20 | copyright notice, this list of conditions and the |
21 | following disclaimer in the documentation and/or other |
22 | materials provided with the distribution. |
23 | |
24 | * Neither the name of the assimp team, nor the names of its |
25 | contributors may be used to endorse or promote products |
26 | derived from this software without specific prior |
27 | written permission of the assimp team. |
28 | |
29 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
30 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
31 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
32 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
33 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
34 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
35 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
36 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
37 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
38 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
39 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
40 | --------------------------------------------------------------------------- |
41 | */ |
42 | |
43 | /** @file Provides cheat implementations for IOSystem and IOStream to |
44 | * redirect exporter output to a blob chain.*/ |
45 | |
46 | #ifndef AI_BLOBIOSYSTEM_H_INCLUDED |
47 | #define AI_BLOBIOSYSTEM_H_INCLUDED |
48 | |
49 | #include <assimp/IOStream.hpp> |
50 | #include <assimp/cexport.h> |
51 | #include <assimp/IOSystem.hpp> |
52 | #include <assimp/DefaultLogger.hpp> |
53 | #include <stdint.h> |
54 | #include <set> |
55 | #include <vector> |
56 | |
57 | namespace Assimp { |
58 | class BlobIOSystem; |
59 | |
60 | // -------------------------------------------------------------------------------------------- |
61 | /** Redirect IOStream to a blob */ |
62 | // -------------------------------------------------------------------------------------------- |
63 | class BlobIOStream : public IOStream |
64 | { |
65 | public: |
66 | |
67 | BlobIOStream(BlobIOSystem* creator, const std::string& file, size_t initial = 4096) |
68 | : buffer() |
69 | , cur_size() |
70 | , file_size() |
71 | , cursor() |
72 | , initial(initial) |
73 | , file(file) |
74 | , creator(creator) |
75 | { |
76 | } |
77 | |
78 | |
79 | virtual ~BlobIOStream(); |
80 | |
81 | public: |
82 | |
83 | // ------------------------------------------------------------------- |
84 | aiExportDataBlob* GetBlob() |
85 | { |
86 | aiExportDataBlob* blob = new aiExportDataBlob(); |
87 | blob->size = file_size; |
88 | blob->data = buffer; |
89 | |
90 | buffer = NULL; |
91 | |
92 | return blob; |
93 | } |
94 | |
95 | |
96 | public: |
97 | |
98 | |
99 | // ------------------------------------------------------------------- |
100 | virtual size_t Read( void *, |
101 | size_t, |
102 | size_t ) |
103 | { |
104 | return 0; |
105 | } |
106 | |
107 | // ------------------------------------------------------------------- |
108 | virtual size_t Write(const void* pvBuffer, |
109 | size_t pSize, |
110 | size_t pCount) |
111 | { |
112 | pSize *= pCount; |
113 | if (cursor + pSize > cur_size) { |
114 | Grow(cursor + pSize); |
115 | } |
116 | |
117 | memcpy(buffer+cursor, pvBuffer, pSize); |
118 | cursor += pSize; |
119 | |
120 | file_size = std::max(file_size,cursor); |
121 | return pCount; |
122 | } |
123 | |
124 | // ------------------------------------------------------------------- |
125 | virtual aiReturn Seek(size_t pOffset, |
126 | aiOrigin pOrigin) |
127 | { |
128 | switch(pOrigin) |
129 | { |
130 | case aiOrigin_CUR: |
131 | cursor += pOffset; |
132 | break; |
133 | |
134 | case aiOrigin_END: |
135 | cursor = file_size - pOffset; |
136 | break; |
137 | |
138 | case aiOrigin_SET: |
139 | cursor = pOffset; |
140 | break; |
141 | |
142 | default: |
143 | return AI_FAILURE; |
144 | } |
145 | |
146 | if (cursor > file_size) { |
147 | Grow(cursor); |
148 | } |
149 | |
150 | file_size = std::max(cursor,file_size); |
151 | return AI_SUCCESS; |
152 | } |
153 | |
154 | // ------------------------------------------------------------------- |
155 | virtual size_t Tell() const |
156 | { |
157 | return cursor; |
158 | } |
159 | |
160 | // ------------------------------------------------------------------- |
161 | virtual size_t FileSize() const |
162 | { |
163 | return file_size; |
164 | } |
165 | |
166 | // ------------------------------------------------------------------- |
167 | virtual void Flush() |
168 | { |
169 | // ignore |
170 | } |
171 | |
172 | |
173 | |
174 | private: |
175 | |
176 | // ------------------------------------------------------------------- |
177 | void Grow(size_t need = 0) |
178 | { |
179 | // 1.5 and phi are very heap-friendly growth factors (the first |
180 | // allows for frequent re-use of heap blocks, the second |
181 | // forms a fibonacci sequence with similar characteristics - |
182 | // since this heavily depends on the heap implementation |
183 | // and other factors as well, i'll just go with 1.5 since |
184 | // it is quicker to compute). |
185 | size_t new_size = std::max(initial, std::max( need, cur_size+(cur_size>>1) )); |
186 | |
187 | const uint8_t* const old = buffer; |
188 | buffer = new uint8_t[new_size]; |
189 | |
190 | if (old) { |
191 | memcpy(buffer,old,cur_size); |
192 | delete[] old; |
193 | } |
194 | |
195 | cur_size = new_size; |
196 | } |
197 | |
198 | private: |
199 | |
200 | uint8_t* buffer; |
201 | size_t cur_size,file_size, cursor, initial; |
202 | |
203 | const std::string file; |
204 | BlobIOSystem* const creator; |
205 | }; |
206 | |
207 | |
208 | #define AI_BLOBIO_MAGIC "$blobfile" |
209 | |
210 | // -------------------------------------------------------------------------------------------- |
211 | /** Redirect IOSystem to a blob */ |
212 | // -------------------------------------------------------------------------------------------- |
213 | class BlobIOSystem : public IOSystem |
214 | { |
215 | |
216 | friend class BlobIOStream; |
217 | typedef std::pair<std::string, aiExportDataBlob*> BlobEntry; |
218 | |
219 | public: |
220 | |
221 | BlobIOSystem() |
222 | { |
223 | } |
224 | |
225 | virtual ~BlobIOSystem() |
226 | { |
227 | for(BlobEntry& blobby : blobs) { |
228 | delete blobby.second; |
229 | } |
230 | } |
231 | |
232 | public: |
233 | |
234 | // ------------------------------------------------------------------- |
235 | const char* GetMagicFileName() const |
236 | { |
237 | return AI_BLOBIO_MAGIC; |
238 | } |
239 | |
240 | |
241 | // ------------------------------------------------------------------- |
242 | aiExportDataBlob* GetBlobChain() |
243 | { |
244 | // one must be the master |
245 | aiExportDataBlob* master = NULL, *cur; |
246 | for(const BlobEntry& blobby : blobs) { |
247 | if (blobby.first == AI_BLOBIO_MAGIC) { |
248 | master = blobby.second; |
249 | break; |
250 | } |
251 | } |
252 | if (!master) { |
253 | DefaultLogger::get()->error("BlobIOSystem: no data written or master file was not closed properly." ); |
254 | return NULL; |
255 | } |
256 | |
257 | master->name.Set("" ); |
258 | |
259 | cur = master; |
260 | for(const BlobEntry& blobby : blobs) { |
261 | if (blobby.second == master) { |
262 | continue; |
263 | } |
264 | |
265 | cur->next = blobby.second; |
266 | cur = cur->next; |
267 | |
268 | // extract the file extension from the file written |
269 | const std::string::size_type s = blobby.first.find_first_of('.'); |
270 | cur->name.Set(s == std::string::npos ? blobby.first : blobby.first.substr(s+1)); |
271 | } |
272 | |
273 | // give up blob ownership |
274 | blobs.clear(); |
275 | return master; |
276 | } |
277 | |
278 | public: |
279 | |
280 | // ------------------------------------------------------------------- |
281 | virtual bool Exists( const char* pFile) const { |
282 | return created.find(std::string(pFile)) != created.end(); |
283 | } |
284 | |
285 | |
286 | // ------------------------------------------------------------------- |
287 | virtual char getOsSeparator() const { |
288 | return '/'; |
289 | } |
290 | |
291 | |
292 | // ------------------------------------------------------------------- |
293 | virtual IOStream* Open(const char* pFile, |
294 | const char* pMode) |
295 | { |
296 | if (pMode[0] != 'w') { |
297 | return NULL; |
298 | } |
299 | |
300 | created.insert(std::string(pFile)); |
301 | return new BlobIOStream(this,std::string(pFile)); |
302 | } |
303 | |
304 | // ------------------------------------------------------------------- |
305 | virtual void Close( IOStream* pFile) |
306 | { |
307 | delete pFile; |
308 | } |
309 | |
310 | private: |
311 | |
312 | // ------------------------------------------------------------------- |
313 | void OnDestruct(const std::string& filename, BlobIOStream* child) |
314 | { |
315 | // we don't know in which the files are closed, so we |
316 | // can't reliably say that the first must be the master |
317 | // file ... |
318 | blobs.push_back( BlobEntry(filename,child->GetBlob()) ); |
319 | } |
320 | |
321 | private: |
322 | std::set<std::string> created; |
323 | std::vector< BlobEntry > blobs; |
324 | }; |
325 | |
326 | |
327 | // -------------------------------------------------------------------------------------------- |
328 | BlobIOStream :: ~BlobIOStream() |
329 | { |
330 | creator->OnDestruct(file,this); |
331 | delete[] buffer; |
332 | } |
333 | |
334 | |
335 | } // end Assimp |
336 | |
337 | #endif |
338 | |