| /* |
| Open Asset Import Library (assimp) |
| ---------------------------------------------------------------------- |
| |
| Copyright (c) 2006-2017, assimp team |
| |
| All rights reserved. |
| |
| Redistribution and use of this software in source and binary forms, |
| with or without modification, are permitted provided that the |
| following conditions are met: |
| |
| * Redistributions of source code must retain the above |
| copyright notice, this list of conditions and the |
| following disclaimer. |
| |
| * Redistributions in binary form must reproduce the above |
| copyright notice, this list of conditions and the |
| following disclaimer in the documentation and/or other |
| materials provided with the distribution. |
| |
| * Neither the name of the assimp team, nor the names of its |
| contributors may be used to endorse or promote products |
| derived from this software without specific prior |
| written permission of the assimp team. |
| |
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| ---------------------------------------------------------------------- |
| */ |
| |
| /** @file BlenderDNA.cpp |
| * @brief Implementation of the Blender `DNA`, that is its own |
| * serialized set of data structures. |
| */ |
| |
| |
| #ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER |
| #include "BlenderDNA.h" |
| #include "StreamReader.h" |
| #include "fast_atof.h" |
| #include "TinyFormatter.h" |
| |
| using namespace Assimp; |
| using namespace Assimp::Blender; |
| using namespace Assimp::Formatter; |
| |
| static bool match4(StreamReaderAny& stream, const char* string) { |
| ai_assert( nullptr != string ); |
| char tmp[] = { |
| (const char)(stream).GetI1(), |
| (const char)(stream).GetI1(), |
| (const char)(stream).GetI1(), |
| (const char)(stream).GetI1() |
| }; |
| return (tmp[0]==string[0] && tmp[1]==string[1] && tmp[2]==string[2] && tmp[3]==string[3]); |
| } |
| |
| struct Type { |
| size_t size; |
| std::string name; |
| }; |
| |
| // ------------------------------------------------------------------------------------------------ |
| void DNAParser::Parse () |
| { |
| StreamReaderAny& stream = *db.reader.get(); |
| DNA& dna = db.dna; |
| |
| if(!match4(stream,"SDNA")) { |
| throw DeadlyImportError("BlenderDNA: Expected SDNA chunk"); |
| } |
| |
| // name dictionary |
| if(!match4(stream,"NAME")) { |
| throw DeadlyImportError("BlenderDNA: Expected NAME field"); |
| } |
| |
| std::vector<std::string> names (stream.GetI4()); |
| for(std::string& s : names) { |
| while (char c = stream.GetI1()) { |
| s += c; |
| } |
| } |
| |
| // type dictionary |
| for (;stream.GetCurrentPos() & 0x3; stream.GetI1()); |
| if(!match4(stream,"TYPE")) { |
| throw DeadlyImportError("BlenderDNA: Expected TYPE field"); |
| } |
| |
| std::vector<Type> types (stream.GetI4()); |
| for(Type& s : types) { |
| while (char c = stream.GetI1()) { |
| s.name += c; |
| } |
| } |
| |
| // type length dictionary |
| for (;stream.GetCurrentPos() & 0x3; stream.GetI1()); |
| if(!match4(stream,"TLEN")) { |
| throw DeadlyImportError("BlenderDNA: Expected TLEN field"); |
| } |
| |
| for(Type& s : types) { |
| s.size = stream.GetI2(); |
| } |
| |
| // structures dictionary |
| for (;stream.GetCurrentPos() & 0x3; stream.GetI1()); |
| if(!match4(stream,"STRC")) { |
| throw DeadlyImportError("BlenderDNA: Expected STRC field"); |
| } |
| |
| size_t end = stream.GetI4(), fields = 0; |
| |
| dna.structures.reserve(end); |
| for(size_t i = 0; i != end; ++i) { |
| |
| uint16_t n = stream.GetI2(); |
| if (n >= types.size()) { |
| throw DeadlyImportError((format(), |
| "BlenderDNA: Invalid type index in structure name" ,n, |
| " (there are only ", types.size(), " entries)" |
| )); |
| } |
| |
| // maintain separate indexes |
| dna.indices[types[n].name] = dna.structures.size(); |
| |
| dna.structures.push_back(Structure()); |
| Structure& s = dna.structures.back(); |
| s.name = types[n].name; |
| //s.index = dna.structures.size()-1; |
| |
| n = stream.GetI2(); |
| s.fields.reserve(n); |
| |
| size_t offset = 0; |
| for (size_t m = 0; m < n; ++m, ++fields) { |
| |
| uint16_t j = stream.GetI2(); |
| if (j >= types.size()) { |
| throw DeadlyImportError((format(), |
| "BlenderDNA: Invalid type index in structure field ", j, |
| " (there are only ", types.size(), " entries)" |
| )); |
| } |
| s.fields.push_back(Field()); |
| Field& f = s.fields.back(); |
| f.offset = offset; |
| |
| f.type = types[j].name; |
| f.size = types[j].size; |
| |
| j = stream.GetI2(); |
| if (j >= names.size()) { |
| throw DeadlyImportError((format(), |
| "BlenderDNA: Invalid name index in structure field ", j, |
| " (there are only ", names.size(), " entries)" |
| )); |
| } |
| |
| f.name = names[j]; |
| f.flags = 0u; |
| |
| // pointers always specify the size of the pointee instead of their own. |
| // The pointer asterisk remains a property of the lookup name. |
| if (f.name[0] == '*') { |
| f.size = db.i64bit ? 8 : 4; |
| f.flags |= FieldFlag_Pointer; |
| } |
| |
| // arrays, however, specify the size of a single element so we |
| // need to parse the (possibly multi-dimensional) array declaration |
| // in order to obtain the actual size of the array in the file. |
| // Also we need to alter the lookup name to include no array |
| // brackets anymore or size fixup won't work (if our size does |
| // not match the size read from the DNA). |
| if (*f.name.rbegin() == ']') { |
| const std::string::size_type rb = f.name.find('['); |
| if (rb == std::string::npos) { |
| throw DeadlyImportError((format(), |
| "BlenderDNA: Encountered invalid array declaration ", |
| f.name |
| )); |
| } |
| |
| f.flags |= FieldFlag_Array; |
| DNA::ExtractArraySize(f.name,f.array_sizes); |
| f.name = f.name.substr(0,rb); |
| |
| f.size *= f.array_sizes[0] * f.array_sizes[1]; |
| } |
| |
| // maintain separate indexes |
| s.indices[f.name] = s.fields.size()-1; |
| offset += f.size; |
| } |
| s.size = offset; |
| } |
| |
| DefaultLogger::get()->debug((format(),"BlenderDNA: Got ",dna.structures.size(), |
| " structures with totally ",fields," fields")); |
| |
| #ifdef ASSIMP_BUILD_BLENDER_DEBUG |
| dna.DumpToFile(); |
| #endif |
| |
| dna.AddPrimitiveStructures(); |
| dna.RegisterConverters(); |
| } |
| |
| |
| #ifdef ASSIMP_BUILD_BLENDER_DEBUG |
| |
| #include <fstream> |
| // ------------------------------------------------------------------------------------------------ |
| void DNA :: DumpToFile() |
| { |
| // we dont't bother using the VFS here for this is only for debugging. |
| // (and all your bases are belong to us). |
| |
| std::ofstream f("dna.txt"); |
| if (f.fail()) { |
| DefaultLogger::get()->error("Could not dump dna to dna.txt"); |
| return; |
| } |
| f << "Field format: type name offset size" << "\n"; |
| f << "Structure format: name size" << "\n"; |
| |
| for(const Structure& s : structures) { |
| f << s.name << " " << s.size << "\n\n"; |
| for(const Field& ff : s.fields) { |
| f << "\t" << ff.type << " " << ff.name << " " << ff.offset << " " << ff.size << "\n"; |
| } |
| f << "\n"; |
| } |
| f << std::flush; |
| |
| DefaultLogger::get()->info("BlenderDNA: Dumped dna to dna.txt"); |
| } |
| #endif |
| |
| // ------------------------------------------------------------------------------------------------ |
| /*static*/ void DNA :: ExtractArraySize( |
| const std::string& out, |
| size_t array_sizes[2] |
| ) |
| { |
| array_sizes[0] = array_sizes[1] = 1; |
| std::string::size_type pos = out.find('['); |
| if (pos++ == std::string::npos) { |
| return; |
| } |
| array_sizes[0] = strtoul10(&out[pos]); |
| |
| pos = out.find('[',pos); |
| if (pos++ == std::string::npos) { |
| return; |
| } |
| array_sizes[1] = strtoul10(&out[pos]); |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| std::shared_ptr< ElemBase > DNA :: ConvertBlobToStructure( |
| const Structure& structure, |
| const FileDatabase& db |
| ) const |
| { |
| std::map<std::string, FactoryPair >::const_iterator it = converters.find(structure.name); |
| if (it == converters.end()) { |
| return std::shared_ptr< ElemBase >(); |
| } |
| |
| std::shared_ptr< ElemBase > ret = (structure.*((*it).second.first))(); |
| (structure.*((*it).second.second))(ret,db); |
| |
| return ret; |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| DNA::FactoryPair DNA :: GetBlobToStructureConverter( |
| const Structure& structure, |
| const FileDatabase& /*db*/ |
| ) const |
| { |
| std::map<std::string, FactoryPair>::const_iterator it = converters.find(structure.name); |
| return it == converters.end() ? FactoryPair() : (*it).second; |
| } |
| |
| // basing on http://www.blender.org/development/architecture/notes-on-sdna/ |
| // ------------------------------------------------------------------------------------------------ |
| void DNA :: AddPrimitiveStructures() |
| { |
| // NOTE: these are just dummies. Their presence enforces |
| // Structure::Convert<target_type> to be called on these |
| // empty structures. These converters are special |
| // overloads which scan the name of the structure and |
| // perform the required data type conversion if one |
| // of these special names is found in the structure |
| // in question. |
| |
| indices["int"] = structures.size(); |
| structures.push_back( Structure() ); |
| structures.back().name = "int"; |
| structures.back().size = 4; |
| |
| indices["short"] = structures.size(); |
| structures.push_back( Structure() ); |
| structures.back().name = "short"; |
| structures.back().size = 2; |
| |
| |
| indices["char"] = structures.size(); |
| structures.push_back( Structure() ); |
| structures.back().name = "char"; |
| structures.back().size = 1; |
| |
| |
| indices["float"] = structures.size(); |
| structures.push_back( Structure() ); |
| structures.back().name = "float"; |
| structures.back().size = 4; |
| |
| |
| indices["double"] = structures.size(); |
| structures.push_back( Structure() ); |
| structures.back().name = "double"; |
| structures.back().size = 8; |
| |
| // no long, seemingly. |
| } |
| |
| // ------------------------------------------------------------------------------------------------ |
| void SectionParser :: Next() |
| { |
| stream.SetCurrentPos(current.start + current.size); |
| |
| const char tmp[] = { |
| (const char)stream.GetI1(), |
| (const char)stream.GetI1(), |
| (const char)stream.GetI1(), |
| (const char)stream.GetI1() |
| }; |
| current.id = std::string(tmp,tmp[3]?4:tmp[2]?3:tmp[1]?2:1); |
| |
| current.size = stream.GetI4(); |
| current.address.val = ptr64 ? stream.GetU8() : stream.GetU4(); |
| |
| current.dna_index = stream.GetI4(); |
| current.num = stream.GetI4(); |
| |
| current.start = stream.GetCurrentPos(); |
| if (stream.GetRemainingSizeToLimit() < current.size) { |
| throw DeadlyImportError("BLEND: invalid size of file block"); |
| } |
| |
| #ifdef ASSIMP_BUILD_BLENDER_DEBUG |
| DefaultLogger::get()->debug(current.id); |
| #endif |
| } |
| |
| |
| |
| #endif |