/* Open Asset Import Library (assimp) ---------------------------------------------------------------------- Copyright (c) 2006-2024, 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.inl * @brief Blender `DNA` (file format specification embedded in * blend file itself) loader. */ #ifndef INCLUDED_AI_BLEND_DNA_INL #define INCLUDED_AI_BLEND_DNA_INL #include #include namespace Assimp { namespace Blender { //-------------------------------------------------------------------------------- const Field& Structure :: operator [] (const std::string& ss) const { std::map::const_iterator it = indices.find(ss); if (it == indices.end()) { throw Error("BlendDNA: Did not find a field named `",ss,"` in structure `",name,"`"); } return fields[(*it).second]; } //-------------------------------------------------------------------------------- const Field* Structure :: Get (const std::string& ss) const { std::map::const_iterator it = indices.find(ss); return it == indices.end() ? nullptr : &fields[(*it).second]; } //-------------------------------------------------------------------------------- const Field& Structure :: operator [] (const size_t i) const { if (i >= fields.size()) { throw Error("BlendDNA: There is no field with index `",i,"` in structure `",name,"`"); } return fields[i]; } //-------------------------------------------------------------------------------- template std::shared_ptr Structure :: Allocate() const { return std::shared_ptr(new T()); } //-------------------------------------------------------------------------------- template void Structure :: Convert( std::shared_ptr in, const FileDatabase& db) const { Convert (*static_cast ( in.get() ),db); } //-------------------------------------------------------------------------------- template void Structure :: ReadFieldArray(T (& out)[M], const char* name, const FileDatabase& db) const { const StreamReaderAny::pos old = db.reader->GetCurrentPos(); try { const Field& f = (*this)[name]; const Structure& s = db.dna[f.type]; // is the input actually an array? if (!(f.flags & FieldFlag_Array)) { throw Error("Field `",name,"` of structure `",this->name,"` ought to be an array of size ",M); } db.reader->IncPtr(f.offset); // size conversions are always allowed, regardless of error_policy unsigned int i = 0; for(; i < std::min(f.array_sizes[0],M); ++i) { s.Convert(out[i],db); } for(; i < M; ++i) { _defaultInitializer()(out[i]); } } catch (const Error& e) { _defaultInitializer()(out,e.what()); } // and recover the previous stream position db.reader->SetCurrentPos(old); #ifndef ASSIMP_BUILD_BLENDER_NO_STATS ++db.stats().fields_read; #endif } //-------------------------------------------------------------------------------- template void Structure :: ReadFieldArray2(T (& out)[M][N], const char* name, const FileDatabase& db) const { const StreamReaderAny::pos old = db.reader->GetCurrentPos(); try { const Field& f = (*this)[name]; const Structure& s = db.dna[f.type]; // is the input actually an array? if (!(f.flags & FieldFlag_Array)) { throw Error("Field `",name,"` of structure `", this->name,"` ought to be an array of size ",M,"*",N ); } db.reader->IncPtr(f.offset); // size conversions are always allowed, regardless of error_policy unsigned int i = 0; for(; i < std::min(f.array_sizes[0],M); ++i) { unsigned int j = 0; for(; j < std::min(f.array_sizes[1],N); ++j) { s.Convert(out[i][j],db); } for(; j < N; ++j) { _defaultInitializer()(out[i][j]); } } for(; i < M; ++i) { _defaultInitializer()(out[i]); } } catch (const Error& e) { _defaultInitializer()(out,e.what()); } // and recover the previous stream position db.reader->SetCurrentPos(old); #ifndef ASSIMP_BUILD_BLENDER_NO_STATS ++db.stats().fields_read; #endif } //-------------------------------------------------------------------------------- template class TOUT, typename T> bool Structure :: ReadFieldPtr(TOUT& out, const char* name, const FileDatabase& db, bool non_recursive /*= false*/) const { const StreamReaderAny::pos old = db.reader->GetCurrentPos(); Pointer ptrval; const Field* f; try { f = &(*this)[name]; // sanity check, should never happen if the genblenddna script is right if (!(f->flags & FieldFlag_Pointer)) { throw Error("Field `",name,"` of structure `", this->name,"` ought to be a pointer"); } db.reader->IncPtr(f->offset); Convert(ptrval,db); // actually it is meaningless on which Structure the Convert is called // because the `Pointer` argument triggers a special implementation. } catch (const Error& e) { _defaultInitializer()(out,e.what()); out.reset(); return false; } // resolve the pointer and load the corresponding structure const bool res = ResolvePointer(out,ptrval,db,*f, non_recursive); if(!non_recursive) { // and recover the previous stream position db.reader->SetCurrentPos(old); } #ifndef ASSIMP_BUILD_BLENDER_NO_STATS ++db.stats().fields_read; #endif return res; } //-------------------------------------------------------------------------------- template class TOUT, typename T, size_t N> bool Structure :: ReadFieldPtr(TOUT (&out)[N], const char* name, const FileDatabase& db) const { // XXX see if we can reduce this to call to the 'normal' ReadFieldPtr const StreamReaderAny::pos old = db.reader->GetCurrentPos(); Pointer ptrval[N]; const Field* f; try { f = &(*this)[name]; #ifdef _DEBUG // sanity check, should never happen if the genblenddna script is right if ((FieldFlag_Pointer|FieldFlag_Pointer) != (f->flags & (FieldFlag_Pointer|FieldFlag_Pointer))) { throw Error("Field `",name,"` of structure `", this->name,"` ought to be a pointer AND an array"); } #endif // _DEBUG db.reader->IncPtr(f->offset); size_t i = 0; for(; i < std::min(f->array_sizes[0],N); ++i) { Convert(ptrval[i],db); } for(; i < N; ++i) { _defaultInitializer()(ptrval[i]); } // actually it is meaningless on which Structure the Convert is called // because the `Pointer` argument triggers a special implementation. } catch (const Error& e) { _defaultInitializer()(out,e.what()); for(size_t i = 0; i < N; ++i) { out[i].reset(); } return false; } bool res = true; for(size_t i = 0; i < N; ++i) { // resolve the pointer and load the corresponding structure res = ResolvePointer(out[i],ptrval[i],db,*f) && res; } // and recover the previous stream position db.reader->SetCurrentPos(old); #ifndef ASSIMP_BUILD_BLENDER_NO_STATS ++db.stats().fields_read; #endif return res; } //-------------------------------------------------------------------------------- template void Structure :: ReadField(T& out, const char* name, const FileDatabase& db) const { const StreamReaderAny::pos old = db.reader->GetCurrentPos(); try { const Field& f = (*this)[name]; // find the structure definition pertaining to this field const Structure& s = db.dna[f.type]; db.reader->IncPtr(f.offset); s.Convert(out,db); } catch (const Error& e) { _defaultInitializer()(out,e.what()); } // and recover the previous stream position db.reader->SetCurrentPos(old); #ifndef ASSIMP_BUILD_BLENDER_NO_STATS ++db.stats().fields_read; #endif } //-------------------------------------------------------------------------------- // field parsing for raw untyped data (like CustomDataLayer.data) template bool Structure::ReadCustomDataPtr(std::shared_ptr&out, int cdtype, const char* name, const FileDatabase& db) const { const StreamReaderAny::pos old = db.reader->GetCurrentPos(); Pointer ptrval; const Field* f; try { f = &(*this)[name]; // sanity check, should never happen if the genblenddna script is right if (!(f->flags & FieldFlag_Pointer)) { throw Error("Field `", name, "` of structure `", this->name, "` ought to be a pointer"); } db.reader->IncPtr(f->offset); Convert(ptrval, db); // actually it is meaningless on which Structure the Convert is called // because the `Pointer` argument triggers a special implementation. } catch (const Error& e) { _defaultInitializer()(out, e.what()); out.reset(); } bool readOk = true; if (ptrval.val) { // get block for ptr const FileBlockHead* block = LocateFileBlockForAddress(ptrval, db); db.reader->SetCurrentPos(block->start + static_cast((ptrval.val - block->address.val))); // read block->num instances of given type to out readOk = readCustomData(out, cdtype, block->num, db); } // and recover the previous stream position db.reader->SetCurrentPos(old); #ifndef ASSIMP_BUILD_BLENDER_NO_STATS ++db.stats().fields_read; #endif return readOk; } //-------------------------------------------------------------------------------- template class TOUT, typename T> bool Structure::ReadFieldPtrVector(vector>&out, const char* name, const FileDatabase& db) const { out.clear(); const StreamReaderAny::pos old = db.reader->GetCurrentPos(); Pointer ptrval; const Field* f; try { f = &(*this)[name]; // sanity check, should never happen if the genblenddna script is right if (!(f->flags & FieldFlag_Pointer)) { throw Error("Field `", name, "` of structure `", this->name, "` ought to be a pointer"); } db.reader->IncPtr(f->offset); Convert(ptrval, db); // actually it is meaningless on which Structure the Convert is called // because the `Pointer` argument triggers a special implementation. } catch (const Error& e) { _defaultInitializer()(out, e.what()); out.clear(); return false; } if (ptrval.val) { // find the file block the pointer is pointing to const FileBlockHead* block = LocateFileBlockForAddress(ptrval, db); db.reader->SetCurrentPos(block->start + static_cast((ptrval.val - block->address.val))); // FIXME: basically, this could cause problems with 64 bit pointers on 32 bit systems. // I really ought to improve StreamReader to work with 64 bit indices exclusively. const Structure& s = db.dna[f->type]; for (size_t i = 0; i < block->num; ++i) { TOUT p(new T); s.Convert(*p, db); out.push_back(p); } } db.reader->SetCurrentPos(old); #ifndef ASSIMP_BUILD_BLENDER_NO_STATS ++db.stats().fields_read; #endif return true; } //-------------------------------------------------------------------------------- template