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 | /** @file BlenderDNA.cpp |
43 | * @brief Implementation of the Blender `DNA`, that is its own |
44 | * serialized set of data structures. |
45 | */ |
46 | |
47 | |
48 | #ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER |
49 | #include "BlenderDNA.h" |
50 | #include "StreamReader.h" |
51 | #include "fast_atof.h" |
52 | #include "TinyFormatter.h" |
53 | |
54 | using namespace Assimp; |
55 | using namespace Assimp::Blender; |
56 | using namespace Assimp::Formatter; |
57 | |
58 | static bool match4(StreamReaderAny& stream, const char* string) { |
59 | ai_assert( nullptr != string ); |
60 | char tmp[] = { |
61 | (const char)(stream).GetI1(), |
62 | (const char)(stream).GetI1(), |
63 | (const char)(stream).GetI1(), |
64 | (const char)(stream).GetI1() |
65 | }; |
66 | return (tmp[0]==string[0] && tmp[1]==string[1] && tmp[2]==string[2] && tmp[3]==string[3]); |
67 | } |
68 | |
69 | struct Type { |
70 | size_t size; |
71 | std::string name; |
72 | }; |
73 | |
74 | // ------------------------------------------------------------------------------------------------ |
75 | void DNAParser::Parse () |
76 | { |
77 | StreamReaderAny& stream = *db.reader.get(); |
78 | DNA& dna = db.dna; |
79 | |
80 | if(!match4(stream,"SDNA" )) { |
81 | throw DeadlyImportError("BlenderDNA: Expected SDNA chunk" ); |
82 | } |
83 | |
84 | // name dictionary |
85 | if(!match4(stream,"NAME" )) { |
86 | throw DeadlyImportError("BlenderDNA: Expected NAME field" ); |
87 | } |
88 | |
89 | std::vector<std::string> names (stream.GetI4()); |
90 | for(std::string& s : names) { |
91 | while (char c = stream.GetI1()) { |
92 | s += c; |
93 | } |
94 | } |
95 | |
96 | // type dictionary |
97 | for (;stream.GetCurrentPos() & 0x3; stream.GetI1()); |
98 | if(!match4(stream,"TYPE" )) { |
99 | throw DeadlyImportError("BlenderDNA: Expected TYPE field" ); |
100 | } |
101 | |
102 | std::vector<Type> types (stream.GetI4()); |
103 | for(Type& s : types) { |
104 | while (char c = stream.GetI1()) { |
105 | s.name += c; |
106 | } |
107 | } |
108 | |
109 | // type length dictionary |
110 | for (;stream.GetCurrentPos() & 0x3; stream.GetI1()); |
111 | if(!match4(stream,"TLEN" )) { |
112 | throw DeadlyImportError("BlenderDNA: Expected TLEN field" ); |
113 | } |
114 | |
115 | for(Type& s : types) { |
116 | s.size = stream.GetI2(); |
117 | } |
118 | |
119 | // structures dictionary |
120 | for (;stream.GetCurrentPos() & 0x3; stream.GetI1()); |
121 | if(!match4(stream,"STRC" )) { |
122 | throw DeadlyImportError("BlenderDNA: Expected STRC field" ); |
123 | } |
124 | |
125 | size_t end = stream.GetI4(), fields = 0; |
126 | |
127 | dna.structures.reserve(end); |
128 | for(size_t i = 0; i != end; ++i) { |
129 | |
130 | uint16_t n = stream.GetI2(); |
131 | if (n >= types.size()) { |
132 | throw DeadlyImportError((format(), |
133 | "BlenderDNA: Invalid type index in structure name" ,n, |
134 | " (there are only " , types.size(), " entries)" |
135 | )); |
136 | } |
137 | |
138 | // maintain separate indexes |
139 | dna.indices[types[n].name] = dna.structures.size(); |
140 | |
141 | dna.structures.push_back(Structure()); |
142 | Structure& s = dna.structures.back(); |
143 | s.name = types[n].name; |
144 | //s.index = dna.structures.size()-1; |
145 | |
146 | n = stream.GetI2(); |
147 | s.fields.reserve(n); |
148 | |
149 | size_t offset = 0; |
150 | for (size_t m = 0; m < n; ++m, ++fields) { |
151 | |
152 | uint16_t j = stream.GetI2(); |
153 | if (j >= types.size()) { |
154 | throw DeadlyImportError((format(), |
155 | "BlenderDNA: Invalid type index in structure field " , j, |
156 | " (there are only " , types.size(), " entries)" |
157 | )); |
158 | } |
159 | s.fields.push_back(Field()); |
160 | Field& f = s.fields.back(); |
161 | f.offset = offset; |
162 | |
163 | f.type = types[j].name; |
164 | f.size = types[j].size; |
165 | |
166 | j = stream.GetI2(); |
167 | if (j >= names.size()) { |
168 | throw DeadlyImportError((format(), |
169 | "BlenderDNA: Invalid name index in structure field " , j, |
170 | " (there are only " , names.size(), " entries)" |
171 | )); |
172 | } |
173 | |
174 | f.name = names[j]; |
175 | f.flags = 0u; |
176 | |
177 | // pointers always specify the size of the pointee instead of their own. |
178 | // The pointer asterisk remains a property of the lookup name. |
179 | if (f.name[0] == '*') { |
180 | f.size = db.i64bit ? 8 : 4; |
181 | f.flags |= FieldFlag_Pointer; |
182 | } |
183 | |
184 | // arrays, however, specify the size of a single element so we |
185 | // need to parse the (possibly multi-dimensional) array declaration |
186 | // in order to obtain the actual size of the array in the file. |
187 | // Also we need to alter the lookup name to include no array |
188 | // brackets anymore or size fixup won't work (if our size does |
189 | // not match the size read from the DNA). |
190 | if (*f.name.rbegin() == ']') { |
191 | const std::string::size_type rb = f.name.find('['); |
192 | if (rb == std::string::npos) { |
193 | throw DeadlyImportError((format(), |
194 | "BlenderDNA: Encountered invalid array declaration " , |
195 | f.name |
196 | )); |
197 | } |
198 | |
199 | f.flags |= FieldFlag_Array; |
200 | DNA::ExtractArraySize(f.name,f.array_sizes); |
201 | f.name = f.name.substr(0,rb); |
202 | |
203 | f.size *= f.array_sizes[0] * f.array_sizes[1]; |
204 | } |
205 | |
206 | // maintain separate indexes |
207 | s.indices[f.name] = s.fields.size()-1; |
208 | offset += f.size; |
209 | } |
210 | s.size = offset; |
211 | } |
212 | |
213 | DefaultLogger::get()->debug((format(),"BlenderDNA: Got " ,dna.structures.size(), |
214 | " structures with totally " ,fields," fields" )); |
215 | |
216 | #ifdef ASSIMP_BUILD_BLENDER_DEBUG |
217 | dna.DumpToFile(); |
218 | #endif |
219 | |
220 | dna.AddPrimitiveStructures(); |
221 | dna.RegisterConverters(); |
222 | } |
223 | |
224 | |
225 | #ifdef ASSIMP_BUILD_BLENDER_DEBUG |
226 | |
227 | #include <fstream> |
228 | // ------------------------------------------------------------------------------------------------ |
229 | void DNA :: DumpToFile() |
230 | { |
231 | // we dont't bother using the VFS here for this is only for debugging. |
232 | // (and all your bases are belong to us). |
233 | |
234 | std::ofstream f("dna.txt" ); |
235 | if (f.fail()) { |
236 | DefaultLogger::get()->error("Could not dump dna to dna.txt" ); |
237 | return; |
238 | } |
239 | f << "Field format: type name offset size" << "\n" ; |
240 | f << "Structure format: name size" << "\n" ; |
241 | |
242 | for(const Structure& s : structures) { |
243 | f << s.name << " " << s.size << "\n\n" ; |
244 | for(const Field& ff : s.fields) { |
245 | f << "\t" << ff.type << " " << ff.name << " " << ff.offset << " " << ff.size << "\n" ; |
246 | } |
247 | f << "\n" ; |
248 | } |
249 | f << std::flush; |
250 | |
251 | DefaultLogger::get()->info("BlenderDNA: Dumped dna to dna.txt" ); |
252 | } |
253 | #endif |
254 | |
255 | // ------------------------------------------------------------------------------------------------ |
256 | /*static*/ void DNA :: ( |
257 | const std::string& out, |
258 | size_t array_sizes[2] |
259 | ) |
260 | { |
261 | array_sizes[0] = array_sizes[1] = 1; |
262 | std::string::size_type pos = out.find('['); |
263 | if (pos++ == std::string::npos) { |
264 | return; |
265 | } |
266 | array_sizes[0] = strtoul10(&out[pos]); |
267 | |
268 | pos = out.find('[',pos); |
269 | if (pos++ == std::string::npos) { |
270 | return; |
271 | } |
272 | array_sizes[1] = strtoul10(&out[pos]); |
273 | } |
274 | |
275 | // ------------------------------------------------------------------------------------------------ |
276 | std::shared_ptr< ElemBase > DNA :: ConvertBlobToStructure( |
277 | const Structure& structure, |
278 | const FileDatabase& db |
279 | ) const |
280 | { |
281 | std::map<std::string, FactoryPair >::const_iterator it = converters.find(structure.name); |
282 | if (it == converters.end()) { |
283 | return std::shared_ptr< ElemBase >(); |
284 | } |
285 | |
286 | std::shared_ptr< ElemBase > ret = (structure.*((*it).second.first))(); |
287 | (structure.*((*it).second.second))(ret,db); |
288 | |
289 | return ret; |
290 | } |
291 | |
292 | // ------------------------------------------------------------------------------------------------ |
293 | DNA::FactoryPair DNA :: GetBlobToStructureConverter( |
294 | const Structure& structure, |
295 | const FileDatabase& /*db*/ |
296 | ) const |
297 | { |
298 | std::map<std::string, FactoryPair>::const_iterator it = converters.find(structure.name); |
299 | return it == converters.end() ? FactoryPair() : (*it).second; |
300 | } |
301 | |
302 | // basing on http://www.blender.org/development/architecture/notes-on-sdna/ |
303 | // ------------------------------------------------------------------------------------------------ |
304 | void DNA :: AddPrimitiveStructures() |
305 | { |
306 | // NOTE: these are just dummies. Their presence enforces |
307 | // Structure::Convert<target_type> to be called on these |
308 | // empty structures. These converters are special |
309 | // overloads which scan the name of the structure and |
310 | // perform the required data type conversion if one |
311 | // of these special names is found in the structure |
312 | // in question. |
313 | |
314 | indices["int" ] = structures.size(); |
315 | structures.push_back( Structure() ); |
316 | structures.back().name = "int" ; |
317 | structures.back().size = 4; |
318 | |
319 | indices["short" ] = structures.size(); |
320 | structures.push_back( Structure() ); |
321 | structures.back().name = "short" ; |
322 | structures.back().size = 2; |
323 | |
324 | |
325 | indices["char" ] = structures.size(); |
326 | structures.push_back( Structure() ); |
327 | structures.back().name = "char" ; |
328 | structures.back().size = 1; |
329 | |
330 | |
331 | indices["float" ] = structures.size(); |
332 | structures.push_back( Structure() ); |
333 | structures.back().name = "float" ; |
334 | structures.back().size = 4; |
335 | |
336 | |
337 | indices["double" ] = structures.size(); |
338 | structures.push_back( Structure() ); |
339 | structures.back().name = "double" ; |
340 | structures.back().size = 8; |
341 | |
342 | // no long, seemingly. |
343 | } |
344 | |
345 | // ------------------------------------------------------------------------------------------------ |
346 | void SectionParser :: Next() |
347 | { |
348 | stream.SetCurrentPos(current.start + current.size); |
349 | |
350 | const char tmp[] = { |
351 | (const char)stream.GetI1(), |
352 | (const char)stream.GetI1(), |
353 | (const char)stream.GetI1(), |
354 | (const char)stream.GetI1() |
355 | }; |
356 | current.id = std::string(tmp,tmp[3]?4:tmp[2]?3:tmp[1]?2:1); |
357 | |
358 | current.size = stream.GetI4(); |
359 | current.address.val = ptr64 ? stream.GetU8() : stream.GetU4(); |
360 | |
361 | current.dna_index = stream.GetI4(); |
362 | current.num = stream.GetI4(); |
363 | |
364 | current.start = stream.GetCurrentPos(); |
365 | if (stream.GetRemainingSizeToLimit() < current.size) { |
366 | throw DeadlyImportError("BLEND: invalid size of file block" ); |
367 | } |
368 | |
369 | #ifdef ASSIMP_BUILD_BLENDER_DEBUG |
370 | DefaultLogger::get()->debug(current.id); |
371 | #endif |
372 | } |
373 | |
374 | |
375 | |
376 | #endif |
377 | |