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 
match4(StreamReaderAny & stream,const char * string)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 // ------------------------------------------------------------------------------------------------
Parse()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 // ------------------------------------------------------------------------------------------------
DumpToFile()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 // ------------------------------------------------------------------------------------------------
ExtractArraySize(const std::string & out,size_t array_sizes[2])256 /*static*/ void  DNA :: ExtractArraySize(
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 // ------------------------------------------------------------------------------------------------
ConvertBlobToStructure(const Structure & structure,const FileDatabase & db) const276 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 // ------------------------------------------------------------------------------------------------
GetBlobToStructureConverter(const Structure & structure,const FileDatabase &) const293 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 // ------------------------------------------------------------------------------------------------
AddPrimitiveStructures()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 // ------------------------------------------------------------------------------------------------
Next()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