diff options
Diffstat (limited to 'libs/assimp/code/AssetLib/Blender/BlenderDNA.cpp')
-rw-r--r-- | libs/assimp/code/AssetLib/Blender/BlenderDNA.cpp | 351 |
1 files changed, 351 insertions, 0 deletions
diff --git a/libs/assimp/code/AssetLib/Blender/BlenderDNA.cpp b/libs/assimp/code/AssetLib/Blender/BlenderDNA.cpp new file mode 100644 index 0000000..2910904 --- /dev/null +++ b/libs/assimp/code/AssetLib/Blender/BlenderDNA.cpp @@ -0,0 +1,351 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2022, 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 <assimp/StreamReader.h> +#include <assimp/TinyFormatter.h> +#include <assimp/fast_atof.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[4]; + tmp[0] = (stream).GetI1(); + tmp[1] = (stream).GetI1(); + tmp[2] = (stream).GetI1(); + tmp[3] = (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("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; + + 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("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("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("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; + } + + ASSIMP_LOG_DEBUG("BlenderDNA: Got ", dna.structures.size(), " structures with totally ", fields, " fields"); + +#if ASSIMP_BUILD_BLENDER_DEBUG_DNA + dna.DumpToFile(); +#endif + + dna.AddPrimitiveStructures(); + dna.RegisterConverters(); +} + +#if ASSIMP_BUILD_BLENDER_DEBUG_DNA + +#include <fstream> +// ------------------------------------------------------------------------------------------------ +void DNA ::DumpToFile() { + // we don'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()) { + ASSIMP_LOG_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; + + ASSIMP_LOG_INFO("BlenderDNA: Dumped dna to dna.txt"); +} +#endif // ASSIMP_BUILD_BLENDER_DEBUG_DNA + +// ------------------------------------------------------------------------------------------------ +/*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 + ASSIMP_LOG_VERBOSE_DEBUG(current.id); +#endif +} + +#endif |