1/*
2Open Asset Import Library (assimp)
3----------------------------------------------------------------------
4
5Copyright (c) 2006-2017, 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 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
54using namespace Assimp;
55using namespace Assimp::Blender;
56using namespace Assimp::Formatter;
57
58static 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
69struct Type {
70 size_t size;
71 std::string name;
72};
73
74// ------------------------------------------------------------------------------------------------
75void 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// ------------------------------------------------------------------------------------------------
229void 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 :: 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// ------------------------------------------------------------------------------------------------
276std::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// ------------------------------------------------------------------------------------------------
293DNA::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// ------------------------------------------------------------------------------------------------
304void 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// ------------------------------------------------------------------------------------------------
346void 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