Merge remote-tracking branch 'origin/master' into bensewell-fixifcopening4343

pull/4344/head
Ben Sewell 2022-01-20 09:43:45 +00:00
commit 3e6fc3a64b
62 changed files with 888 additions and 662 deletions

View File

@ -111,20 +111,9 @@ Discreet3DSImporter::~Discreet3DSImporter() {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file. // Returns whether the class can handle the format of the given file.
bool Discreet3DSImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const { bool Discreet3DSImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
std::string extension = GetExtension(pFile); static const uint16_t token[] = { 0x4d4d, 0x3dc2 /*, 0x3daa */ };
if (extension == "3ds" || extension == "prj") { return CheckMagicToken(pIOHandler, pFile, token, AI_COUNT_OF(token), 0, sizeof token[0]);
return true;
}
if (!extension.length() || checkSig) {
uint16_t token[3];
token[0] = 0x4d4d;
token[1] = 0x3dc2;
//token[2] = 0x3daa;
return CheckMagicToken(pIOHandler, pFile, token, 2, 0, 2);
}
return false;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------

View File

@ -90,24 +90,12 @@ D3MFImporter::~D3MFImporter() {
// empty // empty
} }
bool D3MFImporter::CanRead(const std::string &filename, IOSystem *pIOHandler, bool checkSig) const { bool D3MFImporter::CanRead(const std::string &filename, IOSystem *pIOHandler, bool /*checkSig*/) const {
const std::string extension(GetExtension(filename)); if (!ZipArchiveIOSystem::isZipArchive(pIOHandler, filename)) {
if (extension == desc.mFileExtensions) { return false;
return true;
}
if (!extension.length() || checkSig) {
if (nullptr == pIOHandler) {
return false;
}
if (!ZipArchiveIOSystem::isZipArchive(pIOHandler, filename)) {
return false;
}
D3MFOpcPackage opcPackage(pIOHandler, filename);
return opcPackage.validate();
} }
D3MF::D3MFOpcPackage opcPackage(pIOHandler, filename);
return false; return opcPackage.validate();
} }
void D3MFImporter::SetupProperties(const Importer*) { void D3MFImporter::SetupProperties(const Importer*) {

View File

@ -152,18 +152,9 @@ AC3DImporter::~AC3DImporter() {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file. // Returns whether the class can handle the format of the given file.
bool AC3DImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const { bool AC3DImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
std::string extension = GetExtension(pFile); static const uint32_t tokens[] = { AI_MAKE_MAGIC("AC3D") };
return CheckMagicToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
// fixme: are acc and ac3d *really* used? Some sources say they are
if (extension == "ac" || extension == "ac3d" || extension == "acc") {
return true;
}
if (!extension.length() || checkSig) {
uint32_t token = AI_MAKE_MAGIC("AC3D");
return CheckMagicToken(pIOHandler, pFile, &token, 1, 0);
}
return false;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------

View File

@ -503,19 +503,9 @@ void AMFImporter::ParseNode_Metadata(XmlNode &node) {
mNodeElement_List.push_back(ne); // and to node element list because its a new object in graph. mNodeElement_List.push_back(ne); // and to node element list because its a new object in graph.
} }
bool AMFImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool pCheckSig) const { bool AMFImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*pCheckSig*/) const {
const std::string extension = GetExtension(pFile); static const char *tokens[] = { "<amf" };
return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
if (extension == "amf") {
return true;
}
if (extension.empty() || pCheckSig) {
static const char * const tokens[] = { "<amf" };
return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1);
}
return false;
} }
const aiImporterDesc *AMFImporter::GetInfo() const { const aiImporterDesc *AMFImporter::GetInfo() const {

View File

@ -95,19 +95,9 @@ ASEImporter::~ASEImporter() {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file. // Returns whether the class can handle the format of the given file.
bool ASEImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool cs) const { bool ASEImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
// check file extension static const char *tokens[] = { "*3dsmax_asciiexport" };
const std::string extension = GetExtension(pFile); return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
if (extension == "ase" || extension == "ask") {
return true;
}
if ((!extension.length() || cs) && pIOHandler) {
static const char * const tokens[] = { "*3dsmax_asciiexport" };
return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1);
}
return false;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------

View File

@ -486,8 +486,9 @@ void Parser::ParseLV1MaterialListBlock() {
ParseLV4MeshLong(iIndex); ParseLV4MeshLong(iIndex);
if (iIndex >= iMaterialCount) { if (iIndex >= iMaterialCount) {
LogWarning("Out of range: material index is too large"); LogError("Out of range: material index is too large");
iIndex = iMaterialCount - 1; iIndex = iMaterialCount - 1;
return;
} }
// get a reference to the material // get a reference to the material

View File

@ -81,7 +81,7 @@ static const aiImporterDesc desc = {
//#define DEBUG_B3D //#define DEBUG_B3D
template <typename T> template<typename T>
void DeleteAllBarePointers(std::vector<T> &x) { void DeleteAllBarePointers(std::vector<T> &x) {
for (auto p : x) { for (auto p : x) {
delete p; delete p;
@ -89,11 +89,11 @@ void DeleteAllBarePointers(std::vector<T> &x) {
} }
B3DImporter::~B3DImporter() { B3DImporter::~B3DImporter() {
// empty
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
bool B3DImporter::CanRead(const std::string &pFile, IOSystem * /*pIOHandler*/, bool /*checkSig*/) const { bool B3DImporter::CanRead(const std::string &pFile, IOSystem * /*pIOHandler*/, bool /*checkSig*/) const {
size_t pos = pFile.find_last_of('.'); size_t pos = pFile.find_last_of('.');
if (pos == string::npos) { if (pos == string::npos) {
return false; return false;

View File

@ -92,18 +92,9 @@ BVHLoader::~BVHLoader() {}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file. // Returns whether the class can handle the format of the given file.
bool BVHLoader::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool cs) const { bool BVHLoader::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
// check file extension static const char *tokens[] = { "HIERARCHY" };
const std::string extension = GetExtension(pFile); return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
if (extension == "bvh")
return true;
if ((!extension.length() || cs) && pIOHandler) {
static const char * const tokens[] = { "HIERARCHY" };
return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1);
}
return false;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------

View File

@ -114,22 +114,14 @@ BlenderImporter::~BlenderImporter() {
} }
static const char * const Tokens[] = { "BLENDER" }; static const char * const Tokens[] = { "BLENDER" };
static const char * const TokensForSearch[] = { "blender" };
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file. // Returns whether the class can handle the format of the given file.
bool BlenderImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const { bool BlenderImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
const std::string &extension = GetExtension(pFile); // note: this won't catch compressed files
if (extension == "blend") { static const char *tokens[] = { "<BLENDER", "blender" };
return true;
}
if ((!extension.length() || checkSig) && pIOHandler) { return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
// note: this won't catch compressed files
return SearchFileHeaderForToken(pIOHandler, pFile, TokensForSearch, 1);
}
return false;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------

View File

@ -106,14 +106,25 @@ static const aiImporterDesc desc = {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
bool C4DImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const { C4DImporter::C4DImporter()
: BaseImporter() {
// empty
}
// ------------------------------------------------------------------------------------------------
C4DImporter::~C4DImporter() {
// empty
}
// ------------------------------------------------------------------------------------------------
bool C4DImporter::CanRead( const std::string& pFile, IOSystem* /*pIOHandler*/, bool /*checkSig*/) const {
const std::string& extension = GetExtension(pFile); const std::string& extension = GetExtension(pFile);
if (extension == "c4d") { if (extension == "c4d") {
return true; return true;
} else if ((!extension.length() || checkSig) && pIOHandler) { } else if ((!extension.length() || checkSig) && pIOHandler) {
// TODO // TODO
} }
return false; return false;
} }

View File

@ -103,17 +103,9 @@ COBImporter::~COBImporter() {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file. // Returns whether the class can handle the format of the given file.
bool COBImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const { bool COBImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
const std::string &extension = GetExtension(pFile); static const char *tokens[] = { "Caligary" };
if (extension == "cob" || extension == "scn" || extension == "COB" || extension == "SCN") { return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
return true;
}
else if ((!extension.length() || checkSig) && pIOHandler) {
static const char * const tokens[] = { "Caligary" };
return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1);
}
return false;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------

View File

@ -90,19 +90,10 @@ CSMImporter::~CSMImporter()
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file. // Returns whether the class can handle the format of the given file.
bool CSMImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const bool CSMImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool /*checkSig*/) const
{ {
// check file extension static const char* tokens[] = {"$Filename"};
const std::string extension = GetExtension(pFile); return SearchFileHeaderForToken(pIOHandler,pFile,tokens,AI_COUNT_OF(tokens));
if( extension == "csm")
return true;
if ((checkSig || !extension.length()) && pIOHandler) {
static const char * const tokens[] = {"$Filename"};
return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
}
return false;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------

View File

@ -116,37 +116,15 @@ ColladaLoader::~ColladaLoader() {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file. // Returns whether the class can handle the format of the given file.
bool ColladaLoader::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const { bool ColladaLoader::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
// check file extension // Look for a DAE file inside, but don't extract it
const std::string extension = GetExtension(pFile); ZipArchiveIOSystem zip_archive(pIOHandler, pFile);
const bool readSig = checkSig && (pIOHandler != nullptr); if (zip_archive.isOpen()) {
if (!readSig) { return !ColladaParser::ReadZaeManifest(zip_archive).empty();
if (extension == "dae" || extension == "zae") {
return true;
}
} else {
// Look for a DAE file inside, but don't extract it
ZipArchiveIOSystem zip_archive(pIOHandler, pFile);
if (zip_archive.isOpen()) {
return !ColladaParser::ReadZaeManifest(zip_archive).empty();
}
} }
// XML - too generic, we need to open the file and search for typical keywords static const char *tokens[] = { "<collada" };
if (extension == "xml" || !extension.length() || checkSig) { return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
// If CanRead() is called in order to check whether we
// support a specific file extension in general pIOHandler
// might be nullptr and it's our duty to return true here.
if (nullptr == pIOHandler) {
return true;
}
static const char * const tokens[] = {
"<collada"
};
return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1);
}
return false;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------

View File

@ -331,7 +331,16 @@ void ColladaParser::ReadAssetInfo(XmlNode &node) {
const std::string &currentName = currentNode.name(); const std::string &currentName = currentNode.name();
if (currentName == "unit") { if (currentName == "unit") {
mUnitSize = 1.f; mUnitSize = 1.f;
XmlParser::getRealAttribute(currentNode, "meter", mUnitSize); std::string tUnitSizeString;
if (XmlParser::getStdStrAttribute(currentNode, "meter", tUnitSizeString)) {
try {
fast_atoreal_move<ai_real>(tUnitSizeString.data(), mUnitSize);
} catch (const DeadlyImportError& die) {
std::string warning("Collada: Failed to parse meter parameter to real number. Exception:\n");
warning.append(die.what());
ASSIMP_LOG_WARN(warning.data());
}
}
} else if (currentName == "up_axis") { } else if (currentName == "up_axis") {
std::string v; std::string v;
if (!XmlParser::getValueAsString(currentNode, v)) { if (!XmlParser::getValueAsString(currentNode, v)) {
@ -2242,20 +2251,26 @@ void ColladaParser::ReadNodeGeometry(XmlNode &node, Node *pNode) {
if (currentName == "bind_material") { if (currentName == "bind_material") {
XmlNode techNode = currentNode.child("technique_common"); XmlNode techNode = currentNode.child("technique_common");
if (techNode) { if (techNode) {
XmlNode instanceMatNode = techNode.child("instance_material"); for (XmlNode instanceMatNode = techNode.child("instance_material"); instanceMatNode; instanceMatNode = instanceMatNode.next_sibling())
// read ID of the geometry subgroup and the target material {
std::string group; const std::string &instance_name = instanceMatNode.name();
XmlParser::getStdStrAttribute(instanceMatNode, "symbol", group); if (instance_name == "instance_material")
XmlParser::getStdStrAttribute(instanceMatNode, "target", url); {
const char *urlMat = url.c_str(); // read ID of the geometry subgroup and the target material
Collada::SemanticMappingTable s; std::string group;
if (urlMat[0] == '#') XmlParser::getStdStrAttribute(instanceMatNode, "symbol", group);
urlMat++; XmlParser::getStdStrAttribute(instanceMatNode, "target", url);
const char *urlMat = url.c_str();
Collada::SemanticMappingTable s;
if (urlMat[0] == '#')
urlMat++;
s.mMatName = urlMat; s.mMatName = urlMat;
// store the association // store the association
instance.mMaterials[group] = s; instance.mMaterials[group] = s;
ReadMaterialVertexInputBinding(instanceMatNode, s); ReadMaterialVertexInputBinding(instanceMatNode, s);
}
}
} }
} }
} }

View File

@ -123,18 +123,9 @@ DXFImporter::~DXFImporter() {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file. // Returns whether the class can handle the format of the given file.
bool DXFImporter::CanRead( const std::string& filename, IOSystem* pIOHandler, bool checkSig ) const { bool DXFImporter::CanRead( const std::string& filename, IOSystem* pIOHandler, bool /*checkSig*/ ) const {
const std::string& extension = GetExtension( filename ); static const char *tokens[] = { "SECTION", "HEADER", "ENDSEC", "BLOCKS" };
if ( extension == desc.mFileExtensions ) { return SearchFileHeaderForToken(pIOHandler, filename, tokens, AI_COUNT_OF(tokens), 32);
return true;
}
if ( extension.empty() || checkSig ) {
static const char * const pTokens[] = { "SECTION", "HEADER", "ENDSEC", "BLOCKS" };
return SearchFileHeaderForToken(pIOHandler, filename, pTokens, 4, 32 );
}
return false;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------

View File

@ -100,18 +100,10 @@ FBXImporter::~FBXImporter() {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file. // Returns whether the class can handle the format of the given file.
bool FBXImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const { bool FBXImporter::CanRead(const std::string & pFile, IOSystem * pIOHandler, bool /*checkSig*/) const {
const std::string &extension = GetExtension(pFile); // at least ASCII-FBX files usually have a 'FBX' somewhere in their head
if (extension == std::string(desc.mFileExtensions)) { static const char *tokens[] = { "fbx" };
return true; return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
}
else if ((!extension.length() || checkSig) && pIOHandler) {
// at least ASCII-FBX files usually have a 'FBX' somewhere in their head
static const char * const tokens[] = { "fbx" };
return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1);
}
return false;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------

View File

@ -84,20 +84,13 @@ HMPImporter::~HMPImporter() {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file. // Returns whether the class can handle the format of the given file.
bool HMPImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool cs) const { bool HMPImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
const std::string extension = GetExtension(pFile); static const uint32_t tokens[] = {
if (extension == "hmp") AI_HMP_MAGIC_NUMBER_LE_4,
return true; AI_HMP_MAGIC_NUMBER_LE_5,
AI_HMP_MAGIC_NUMBER_LE_7
// if check for extension is not enough, check for the magic tokens };
if (!extension.length() || cs) { return CheckMagicToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
uint32_t tokens[3];
tokens[0] = AI_HMP_MAGIC_NUMBER_LE_4;
tokens[1] = AI_HMP_MAGIC_NUMBER_LE_5;
tokens[2] = AI_HMP_MAGIC_NUMBER_LE_7;
return CheckMagicToken(pIOHandler, pFile, tokens, 3, 0);
}
return false;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------

View File

@ -129,18 +129,12 @@ IFCImporter::~IFCImporter() {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file. // Returns whether the class can handle the format of the given file.
bool IFCImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const { bool IFCImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
const std::string &extension = GetExtension(pFile); // note: this is the common identification for STEP-encoded files, so
if (extension == "ifc" || extension == "ifczip") { // it is only unambiguous as long as we don't support any further
return true; // file formats with STEP as their encoding.
} else if ((!extension.length() || checkSig) && pIOHandler) { static const char *tokens[] = { "ISO-10303-21" };
// note: this is the common identification for STEP-encoded files, so return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
// it is only unambiguous as long as we don't support any further
// file formats with STEP as their encoding.
static const char * const tokens[] = { "ISO-10303-21" };
return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1);
}
return false;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------

View File

@ -0,0 +1,322 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2021, 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.
----------------------------------------------------------------------
*/
#ifndef ASSIMP_BUILD_NO_IQM_IMPORTER
#include <assimp/DefaultIOSystem.h>
#include <assimp/IOStreamBuffer.h>
#include <assimp/ai_assert.h>
#include <assimp/importerdesc.h>
#include <assimp/scene.h>
#include <assimp/DefaultLogger.hpp>
#include <assimp/Importer.hpp>
#include <assimp/ByteSwapper.h>
#include <memory>
#include <numeric>
#include "IQMImporter.h"
#include "iqm.h"
// RESOURCES:
// http://sauerbraten.org/iqm/
// https://github.com/lsalzman/iqm
inline void swap_block( uint32_t *block, size_t size ){
(void)block; // suppress 'unreferenced formal parameter' MSVC warning
size >>= 2;
for ( size_t i = 0; i < size; ++i )
AI_SWAP4( block[ i ] );
}
static const aiImporterDesc desc = {
"Inter-Quake Model Importer",
"",
"",
"",
aiImporterFlags_SupportBinaryFlavour,
0,
0,
0,
0,
"iqm"
};
namespace Assimp {
// ------------------------------------------------------------------------------------------------
// Default constructor
IQMImporter::IQMImporter() :
mScene(nullptr) {
// empty
}
// ------------------------------------------------------------------------------------------------
// Returns true, if file is a binary Inter-Quake Model file.
bool IQMImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const {
const std::string extension = GetExtension(pFile);
if (extension == "iqm")
return true;
else if (!extension.length() || checkSig) {
if (!pIOHandler) {
return true;
}
/*
* don't use CheckMagicToken because that checks with swapped bytes too, leading to false
* positives. This magic is not uint32_t, but char[4], so memcmp is the best way
const char* tokens[] = {"3DMO", "3dmo"};
return CheckMagicToken(pIOHandler,pFile,tokens,2,0,4);
*/
std::unique_ptr<IOStream> pStream(pIOHandler->Open(pFile, "rb"));
unsigned char data[15];
if (!pStream || 15 != pStream->Read(data, 1, 15)) {
return false;
}
return !memcmp(data, "INTERQUAKEMODEL", 15);
}
return false;
}
// ------------------------------------------------------------------------------------------------
const aiImporterDesc *IQMImporter::GetInfo() const {
return &desc;
}
// ------------------------------------------------------------------------------------------------
// Model 3D import implementation
void IQMImporter::InternReadFile(const std::string &file, aiScene *pScene, IOSystem *pIOHandler) {
// Read file into memory
std::unique_ptr<IOStream> pStream(pIOHandler->Open(file, "rb"));
if (!pStream.get()) {
throw DeadlyImportError("Failed to open file ", file, ".");
}
// Get the file-size and validate it, throwing an exception when fails
const size_t fileSize = pStream->FileSize();
if (fileSize < sizeof( iqmheader )) {
throw DeadlyImportError("IQM-file ", file, " is too small.");
}
std::vector<unsigned char> buffer(fileSize);
unsigned char *data = buffer.data();
if (fileSize != pStream->Read(data, 1, fileSize)) {
throw DeadlyImportError("Failed to read the file ", file, ".");
}
// get header
iqmheader &hdr = reinterpret_cast<iqmheader&>( *data );
swap_block( &hdr.version, sizeof( iqmheader ) - sizeof( iqmheader::magic ) );
// extra check for header
if (memcmp(data, IQM_MAGIC, sizeof( IQM_MAGIC ) )
|| hdr.version != IQM_VERSION
|| hdr.filesize != fileSize) {
throw DeadlyImportError("Bad binary header in file ", file, ".");
}
ASSIMP_LOG_DEBUG("IQM: loading ", file);
// create the root node
pScene->mRootNode = new aiNode( "<IQMRoot>" );
// Now rotate the whole scene 90 degrees around the x axis to convert to internal coordinate system
pScene->mRootNode->mTransformation = aiMatrix4x4(
1.f, 0.f, 0.f, 0.f,
0.f, 0.f, 1.f, 0.f,
0.f, -1.f, 0.f, 0.f,
0.f, 0.f, 0.f, 1.f);
pScene->mRootNode->mNumMeshes = hdr.num_meshes;
pScene->mRootNode->mMeshes = new unsigned int[hdr.num_meshes];
std::iota( pScene->mRootNode->mMeshes, pScene->mRootNode->mMeshes + pScene->mRootNode->mNumMeshes, 0 );
mScene = pScene;
// Allocate output storage
pScene->mNumMeshes = 0;
pScene->mMeshes = new aiMesh *[hdr.num_meshes](); // Set arrays to zero to ensue proper destruction if an exception is raised
pScene->mNumMaterials = 0;
pScene->mMaterials = new aiMaterial *[hdr.num_meshes]();
// swap vertex arrays beforehand...
for( auto array = reinterpret_cast<iqmvertexarray*>( data + hdr.ofs_vertexarrays ), end = array + hdr.num_vertexarrays; array != end; ++array )
{
swap_block( &array->type, sizeof( iqmvertexarray ) );
}
// Read all surfaces from the file
for( auto imesh = reinterpret_cast<iqmmesh*>( data + hdr.ofs_meshes ), end_ = imesh + hdr.num_meshes; imesh != end_; ++imesh )
{
swap_block( &imesh->name, sizeof( iqmmesh ) );
// Allocate output mesh & material
auto mesh = pScene->mMeshes[pScene->mNumMeshes++] = new aiMesh();
mesh->mMaterialIndex = pScene->mNumMaterials;
auto mat = pScene->mMaterials[pScene->mNumMaterials++] = new aiMaterial();
{
auto text = reinterpret_cast<char*>( data + hdr.ofs_text );
aiString name( text + imesh->material );
mat->AddProperty( &name, AI_MATKEY_NAME );
mat->AddProperty( &name, AI_MATKEY_TEXTURE_DIFFUSE(0) );
}
// Fill mesh information
mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
mesh->mNumFaces = 0;
mesh->mFaces = new aiFace[imesh->num_triangles];
// Fill in all triangles
for( auto tri = reinterpret_cast<iqmtriangle*>( data + hdr.ofs_triangles ) + imesh->first_triangle, end = tri + imesh->num_triangles; tri != end; ++tri )
{
swap_block( tri->vertex, sizeof( tri->vertex ) );
auto& face = mesh->mFaces[mesh->mNumFaces++];
face.mNumIndices = 3;
face.mIndices = new unsigned int[3]{ tri->vertex[0] - imesh->first_vertex,
tri->vertex[2] - imesh->first_vertex,
tri->vertex[1] - imesh->first_vertex };
}
// Fill in all vertices
for( auto array = reinterpret_cast<const iqmvertexarray*>( data + hdr.ofs_vertexarrays ), end__ = array + hdr.num_vertexarrays; array != end__; ++array )
{
const unsigned int nVerts = imesh->num_vertexes;
const unsigned int step = array->size;
switch ( array->type )
{
case IQM_POSITION:
if( array->format == IQM_FLOAT && step >= 3 ){
mesh->mNumVertices = nVerts;
auto v = mesh->mVertices = new aiVector3D[nVerts];
for( auto f = reinterpret_cast<const float*>( data + array->offset ) + imesh->first_vertex * step,
end = f + nVerts * step; f != end; f += step, ++v )
{
*v = { AI_BE( f[0] ),
AI_BE( f[1] ),
AI_BE( f[2] ) };
}
}
break;
case IQM_TEXCOORD:
if( array->format == IQM_FLOAT && step >= 2)
{
auto v = mesh->mTextureCoords[0] = new aiVector3D[nVerts];
mesh->mNumUVComponents[0] = 2;
for( auto f = reinterpret_cast<const float*>( data + array->offset ) + imesh->first_vertex * step,
end = f + nVerts * step; f != end; f += step, ++v )
{
*v = { AI_BE( f[0] ),
1 - AI_BE( f[1] ), 0 };
}
}
break;
case IQM_NORMAL:
if (array->format == IQM_FLOAT && step >= 3)
{
auto v = mesh->mNormals = new aiVector3D[nVerts];
for( auto f = reinterpret_cast<const float*>( data + array->offset ) + imesh->first_vertex * step,
end = f + nVerts * step; f != end; f += step, ++v )
{
*v = { AI_BE( f[0] ),
AI_BE( f[1] ),
AI_BE( f[2] ) };
}
}
break;
case IQM_COLOR:
if (array->format == IQM_UBYTE && step >= 3)
{
auto v = mesh->mColors[0] = new aiColor4D[nVerts];
for( auto f = ( data + array->offset ) + imesh->first_vertex * step,
end = f + nVerts * step; f != end; f += step, ++v )
{
*v = { ( f[0] ) / 255.f,
( f[1] ) / 255.f,
( f[2] ) / 255.f,
step == 3? 1 : ( f[3] ) / 255.f };
}
}
else if (array->format == IQM_FLOAT && step >= 3)
{
auto v = mesh->mColors[0] = new aiColor4D[nVerts];
for( auto f = reinterpret_cast<const float*>( data + array->offset ) + imesh->first_vertex * step,
end = f + nVerts * step; f != end; f += step, ++v )
{
*v = { AI_BE( f[0] ),
AI_BE( f[1] ),
AI_BE( f[2] ),
step == 3? 1 : AI_BE( f[3] ) };
}
}
break;
case IQM_TANGENT:
#if 0
if (array->format == IQM_FLOAT && step >= 3)
{
auto v = mesh->mTangents = new aiVector3D[nVerts];
for( auto f = reinterpret_cast<const float*>( data + array->offset ) + imesh->first_vertex * step,
end = f + nVerts * step; f != end; f += step, ++v )
{
*v = { AI_BE( f[0] ),
AI_BE( f[1] ),
AI_BE( f[2] ) };
}
}
#endif
break;
case IQM_BLENDINDEXES:
case IQM_BLENDWEIGHTS:
case IQM_CUSTOM:
break; // these attributes are not relevant.
default:
break;
}
}
}
}
// ------------------------------------------------------------------------------------------------
} // Namespace Assimp
#endif // !! ASSIMP_BUILD_NO_IQM_IMPORTER

View File

@ -0,0 +1,78 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2021, 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 IQMImporter.h
* @brief Declares the importer class to read a scene from an Inter-Quake Model file
*/
#pragma once
#ifndef ASSIMP_BUILD_NO_IQM_IMPORTER
#include <assimp/BaseImporter.h>
#include <assimp/material.h>
namespace Assimp {
class IQMImporter : public BaseImporter {
public:
/// \brief Default constructor
IQMImporter();
~IQMImporter() override {}
/// \brief Returns whether the class can handle the format of the given file.
/// \remark See BaseImporter::CanRead() for details.
bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const override;
protected:
//! \brief Appends the supported extension.
const aiImporterDesc *GetInfo() const override;
//! \brief File import implementation.
void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) override;
private:
aiScene *mScene = nullptr; // the scene to import to
};
} // Namespace Assimp
#endif // ASSIMP_BUILD_NO_IQM_IMPORTER

View File

@ -0,0 +1,134 @@
#ifndef __IQM_H__
#define __IQM_H__
#define IQM_MAGIC "INTERQUAKEMODEL"
#define IQM_VERSION 2
struct iqmheader
{
char magic[16];
unsigned int version;
unsigned int filesize;
unsigned int flags;
unsigned int num_text, ofs_text;
unsigned int num_meshes, ofs_meshes;
unsigned int num_vertexarrays, num_vertexes, ofs_vertexarrays;
unsigned int num_triangles, ofs_triangles, ofs_adjacency;
unsigned int num_joints, ofs_joints;
unsigned int num_poses, ofs_poses;
unsigned int num_anims, ofs_anims;
unsigned int num_frames, num_framechannels, ofs_frames, ofs_bounds;
unsigned int num_comment, ofs_comment;
unsigned int num_extensions, ofs_extensions;
};
struct iqmmesh
{
unsigned int name;
unsigned int material;
unsigned int first_vertex, num_vertexes;
unsigned int first_triangle, num_triangles;
};
enum
{
IQM_POSITION = 0,
IQM_TEXCOORD = 1,
IQM_NORMAL = 2,
IQM_TANGENT = 3,
IQM_BLENDINDEXES = 4,
IQM_BLENDWEIGHTS = 5,
IQM_COLOR = 6,
IQM_CUSTOM = 0x10
};
enum
{
IQM_BYTE = 0,
IQM_UBYTE = 1,
IQM_SHORT = 2,
IQM_USHORT = 3,
IQM_INT = 4,
IQM_UINT = 5,
IQM_HALF = 6,
IQM_FLOAT = 7,
IQM_DOUBLE = 8
};
struct iqmtriangle
{
unsigned int vertex[3];
};
struct iqmadjacency
{
unsigned int triangle[3];
};
struct iqmjointv1
{
unsigned int name;
int parent;
float translate[3], rotate[3], scale[3];
};
struct iqmjoint
{
unsigned int name;
int parent;
float translate[3], rotate[4], scale[3];
};
struct iqmposev1
{
int parent;
unsigned int mask;
float channeloffset[9];
float channelscale[9];
};
struct iqmpose
{
int parent;
unsigned int mask;
float channeloffset[10];
float channelscale[10];
};
struct iqmanim
{
unsigned int name;
unsigned int first_frame, num_frames;
float framerate;
unsigned int flags;
};
enum
{
IQM_LOOP = 1<<0
};
struct iqmvertexarray
{
unsigned int type;
unsigned int flags;
unsigned int format;
unsigned int size;
unsigned int offset;
};
struct iqmbounds
{
float bbmin[3], bbmax[3];
float xyradius, radius;
};
struct iqmextension
{
unsigned int name;
unsigned int num_data, ofs_data;
unsigned int ofs_extensions; // pointer to next extension
};
#endif

View File

@ -94,23 +94,9 @@ IRRImporter::~IRRImporter() {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file. // Returns whether the class can handle the format of the given file.
bool IRRImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const { bool IRRImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
const std::string extension = GetExtension(pFile); static const char *tokens[] = { "irr_scene" };
if (extension == "irr") { return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
return true;
} else if (extension == "xml" || checkSig) {
/* If CanRead() is called in order to check whether we
* support a specific file extension in general pIOHandler
* might be nullptr and it's our duty to return true here.
*/
if (nullptr == pIOHandler) {
return true;
}
static const char * const tokens[] = { "irr_scene" };
return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1);
}
return false;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------

View File

@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
Copyright (c) 2006-2022, assimp team Copyright (c) 2006-2022, assimp team
All rights reserved. All rights reserved.
Redistribution and use of this software in source and binary forms, Redistribution and use of this software in source and binary forms,
@ -85,26 +83,14 @@ IRRMeshImporter::~IRRMeshImporter() {}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file. // Returns whether the class can handle the format of the given file.
bool IRRMeshImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const { bool IRRMeshImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
/* NOTE: A simple check for the file extension is not enough /* NOTE: A simple check for the file extension is not enough
* here. Irrmesh and irr are easy, but xml is too generic * here. Irrmesh and irr are easy, but xml is too generic
* and could be collada, too. So we need to open the file and * and could be collada, too. So we need to open the file and
* search for typical tokens. * search for typical tokens.
*/ */
const std::string extension = GetExtension(pFile); static const char *tokens[] = { "irrmesh" };
return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
if (extension == "irrmesh")
return true;
else if (extension == "xml" || checkSig) {
/* If CanRead() is called to check whether the loader
* supports a specific file extension in general we
* must return true here.
*/
if (!pIOHandler) return true;
static const char * const tokens[] = { "irrmesh" };
return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1);
}
return false;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------

View File

@ -105,21 +105,13 @@ LWOImporter::~LWOImporter() {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file. // Returns whether the class can handle the format of the given file.
bool LWOImporter::CanRead(const std::string &file, IOSystem *pIOHandler, bool checkSig) const { bool LWOImporter::CanRead(const std::string &file, IOSystem *pIOHandler, bool /*checkSig*/) const {
const std::string extension = GetExtension(file); static const uint32_t tokens[] = {
if (extension == "lwo" || extension == "lxo") { AI_LWO_FOURCC_LWOB,
return true; AI_LWO_FOURCC_LWO2,
} AI_LWO_FOURCC_LXOB
};
// if check for extension is not enough, check for the magic tokens return CheckMagicToken(pIOHandler, file, tokens, AI_COUNT_OF(tokens), 8);
if (!extension.length() || checkSig) {
uint32_t tokens[3];
tokens[0] = AI_LWO_FOURCC_LWOB;
tokens[1] = AI_LWO_FOURCC_LWO2;
tokens[2] = AI_LWO_FOURCC_LXOB;
return CheckMagicToken(pIOHandler, file, tokens, 3, 8);
}
return false;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------

View File

@ -147,20 +147,12 @@ LWSImporter::~LWSImporter() {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file. // Returns whether the class can handle the format of the given file.
bool LWSImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const { bool LWSImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
const std::string extension = GetExtension(pFile); static const uint32_t tokens[] = {
if (extension == "lws" || extension == "mot") { AI_MAKE_MAGIC("LWSC"),
return true; AI_MAKE_MAGIC("LWMO")
} };
return CheckMagicToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
// if check for extension is not enough, check for the magic tokens LWSC and LWMO
if (!extension.length() || checkSig) {
uint32_t tokens[2];
tokens[0] = AI_MAKE_MAGIC("LWSC");
tokens[1] = AI_MAKE_MAGIC("LWMO");
return CheckMagicToken(pIOHandler, pFile, tokens, 2);
}
return false;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------

View File

@ -111,34 +111,19 @@ M3DImporter::M3DImporter() :
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns true, if file is a binary or ASCII Model 3D file. // Returns true, if file is a binary or ASCII Model 3D file.
bool M3DImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const { bool M3DImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
const std::string extension = GetExtension(pFile); // don't use CheckMagicToken because that checks with swapped bytes too, leading to false
// positives. This magic is not uint32_t, but char[4], so memcmp is the best way
if (extension == "m3d" std::unique_ptr<IOStream> pStream(pIOHandler->Open(pFile, "rb"));
|| extension == "a3d" unsigned char data[4];
) if (4 != pStream->Read(data, 1, 4)) {
return true; return false;
else if (!extension.length() || checkSig) {
if (!pIOHandler) {
return true;
}
/*
* don't use CheckMagicToken because that checks with swapped bytes too, leading to false
* positives. This magic is not uint32_t, but char[4], so memcmp is the best way
const char* tokens[] = {"3DMO", "3dmo"};
return CheckMagicToken(pIOHandler,pFile,tokens,2,0,4);
*/
std::unique_ptr<IOStream> pStream(pIOHandler->Open(pFile, "rb"));
unsigned char data[4];
if (!pStream || 4 != pStream->Read(data, 1, 4)) {
return false;
}
return !memcmp(data, "3DMO", 4) /* bin */
|| !memcmp(data, "3dmo", 4) /* ASCII */
;
} }
return false; return !memcmp(data, "3DMO", 4) /* bin */
#ifdef M3D_ASCII
|| !memcmp(data, "3dmo", 4) /* ASCII */
#endif
;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------

View File

@ -107,19 +107,10 @@ MD2Importer::~MD2Importer()
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file. // Returns whether the class can handle the format of the given file.
bool MD2Importer::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const bool MD2Importer::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool /*checkSig*/) const
{ {
const std::string extension = GetExtension(pFile); static const uint32_t tokens[] = { AI_MD2_MAGIC_NUMBER_LE };
if (extension == "md2") return CheckMagicToken(pIOHandler,pFile,tokens,AI_COUNT_OF(tokens));
return true;
// if check for extension is not enough, check for the magic tokens
if (!extension.length() || checkSig) {
uint32_t tokens[1];
tokens[0] = AI_MD2_MAGIC_NUMBER_LE;
return CheckMagicToken(pIOHandler,pFile,tokens,1);
}
return false;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------

View File

@ -349,18 +349,9 @@ MD3Importer::~MD3Importer() {}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file. // Returns whether the class can handle the format of the given file.
bool MD3Importer::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const { bool MD3Importer::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
const std::string extension = GetExtension(pFile); static const uint32_t tokens[] = { AI_MD3_MAGIC_NUMBER_LE };
if (extension == "md3") return CheckMagicToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
return true;
// if check for extension is not enough, check for the magic tokens
if (!extension.length() || checkSig) {
uint32_t tokens[1];
tokens[0] = AI_MD3_MAGIC_NUMBER_LE;
return CheckMagicToken(pIOHandler, pFile, tokens, 1);
}
return false;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------

View File

@ -100,20 +100,9 @@ MD5Importer::~MD5Importer() {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file. // Returns whether the class can handle the format of the given file.
bool MD5Importer::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const { bool MD5Importer::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
const std::string extension = GetExtension(pFile); static const char *tokens[] = { "MD5Version" };
return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
if (extension == "md5anim" || extension == "md5mesh" || extension == "md5camera")
return true;
else if (!extension.length() || checkSig) {
if (!pIOHandler) {
return true;
}
static const char * const tokens[] = { "MD5Version" };
return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1);
}
return false;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------

View File

@ -111,19 +111,9 @@ MDCImporter::~MDCImporter() {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file. // Returns whether the class can handle the format of the given file.
bool MDCImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const { bool MDCImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
const std::string extension = GetExtension(pFile); static const uint32_t tokens[] = { AI_MDC_MAGIC_NUMBER_LE };
if (extension == "mdc") { return CheckMagicToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
return true;
}
// if check for extension is not enough, check for the magic tokens
if (!extension.length() || checkSig) {
uint32_t tokens[1];
tokens[0] = AI_MDC_MAGIC_NUMBER_LE;
return CheckMagicToken(pIOHandler, pFile, tokens, 1);
}
return false;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------

View File

@ -104,23 +104,18 @@ MDLImporter::~MDLImporter() {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file. // Returns whether the class can handle the format of the given file.
bool MDLImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const { bool MDLImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
const std::string extension = GetExtension(pFile); static const uint32_t tokens[] = {
AI_MDL_MAGIC_NUMBER_LE_HL2a,
// if check for extension is not enough, check for the magic tokens AI_MDL_MAGIC_NUMBER_LE_HL2b,
if (extension == "mdl" || !extension.length() || checkSig) { AI_MDL_MAGIC_NUMBER_LE_GS7,
uint32_t tokens[8]; AI_MDL_MAGIC_NUMBER_LE_GS5b,
tokens[0] = AI_MDL_MAGIC_NUMBER_LE_HL2a; AI_MDL_MAGIC_NUMBER_LE_GS5a,
tokens[1] = AI_MDL_MAGIC_NUMBER_LE_HL2b; AI_MDL_MAGIC_NUMBER_LE_GS4,
tokens[2] = AI_MDL_MAGIC_NUMBER_LE_GS7; AI_MDL_MAGIC_NUMBER_LE_GS3,
tokens[3] = AI_MDL_MAGIC_NUMBER_LE_GS5b; AI_MDL_MAGIC_NUMBER_LE
tokens[4] = AI_MDL_MAGIC_NUMBER_LE_GS5a; };
tokens[5] = AI_MDL_MAGIC_NUMBER_LE_GS4; return CheckMagicToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
tokens[6] = AI_MDL_MAGIC_NUMBER_LE_GS3;
tokens[7] = AI_MDL_MAGIC_NUMBER_LE;
return CheckMagicToken(pIOHandler, pFile, tokens, 8, 0);
}
return false;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------

View File

@ -89,14 +89,9 @@ MMDImporter::~MMDImporter() {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns true, if file is an pmx file. // Returns true, if file is an pmx file.
bool MMDImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool MMDImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler,
bool checkSig) const { bool /*checkSig*/) const {
if (!checkSig) { static const char *tokens[] = { "PMX " };
return SimpleExtensionCheck(pFile, "pmx"); return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
} else {
// Check file Header
static const char * const pTokens[] = { "PMX " };
return SearchFileHeaderForToken(pIOHandler, pFile, pTokens, 1);
}
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------

View File

@ -88,26 +88,12 @@ MS3DImporter::MS3DImporter()
// Destructor, private as well // Destructor, private as well
MS3DImporter::~MS3DImporter() MS3DImporter::~MS3DImporter()
{} {}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file. // Returns whether the class can handle the format of the given file.
bool MS3DImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const bool MS3DImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool /*checkSig*/) const
{ {
// first call - simple extension check static const char* tokens[] = { "MS3D000000" };
const std::string extension = GetExtension(pFile); return SearchFileHeaderForToken(pIOHandler,pFile,tokens,AI_COUNT_OF(tokens));
if (extension == "ms3d") {
return true;
}
// second call - check for magic identifiers
else if (!extension.length() || checkSig) {
if (!pIOHandler) {
return true;
}
static const char * const tokens[] = {"MS3D000000"};
return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
}
return false;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------

View File

@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
Copyright (c) 2006-2022, assimp team Copyright (c) 2006-2022, assimp team
All rights reserved. All rights reserved.
Redistribution and use of this software in source and binary forms, Redistribution and use of this software in source and binary forms,
@ -82,19 +80,10 @@ NDOImporter::~NDOImporter()
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file. // Returns whether the class can handle the format of the given file.
bool NDOImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const bool NDOImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool /*checkSig*/) const
{ {
// check file extension static const char* tokens[] = {"nendo"};
const std::string extension = GetExtension(pFile); return SearchFileHeaderForToken(pIOHandler,pFile,tokens,AI_COUNT_OF(tokens),5);
if( extension == "ndo")
return true;
if ((checkSig || !extension.length()) && pIOHandler) {
static const char * const tokens[] = {"nendo"};
return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1,5);
}
return false;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------

View File

@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
Copyright (c) 2006-2022, assimp team Copyright (c) 2006-2022, assimp team
All rights reserved. All rights reserved.
Redistribution and use of this software in source and binary forms, Redistribution and use of this software in source and binary forms,
@ -83,7 +81,7 @@ NFFImporter::~NFFImporter() {}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file. // Returns whether the class can handle the format of the given file.
bool NFFImporter::CanRead(const std::string &pFile, IOSystem * /*pIOHandler*/, bool /*checkSig*/) const { bool NFFImporter::CanRead(const std::string & pFile, IOSystem * /*pIOHandler*/, bool /*checkSig*/) const {
return SimpleExtensionCheck(pFile, "nff", "enff"); return SimpleExtensionCheck(pFile, "nff", "enff");
} }

View File

@ -83,19 +83,10 @@ OFFImporter::~OFFImporter()
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file. // Returns whether the class can handle the format of the given file.
bool OFFImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const bool OFFImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool /*checkSig*/) const
{ {
const std::string extension = GetExtension(pFile); static const char* tokens[] = { "off" };
return SearchFileHeaderForToken(pIOHandler,pFile,tokens,AI_COUNT_OF(tokens),3);
if (extension == "off")
return true;
else if (!extension.length() || checkSig)
{
if (!pIOHandler)return true;
static const char * const tokens[] = {"off"};
return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1,3);
}
return false;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------

View File

@ -87,16 +87,10 @@ ObjFileImporter::~ObjFileImporter() {
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns true, if file is an obj file. // Returns true if file is an obj file.
bool ObjFileImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const { bool ObjFileImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
if (!checkSig) { static const char *tokens[] = { "mtllib", "usemtl", "v ", "vt ", "vn ", "o ", "g ", "s ", "f " };
//Check File Extension return BaseImporter::SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens), 200, false, true);
return SimpleExtensionCheck(pFile, "obj");
} else {
// Check file Header
static const char *pTokens[] = { "mtllib", "usemtl", "v ", "vt ", "vn ", "o ", "g ", "s ", "f " };
return BaseImporter::SearchFileHeaderForToken(pIOHandler, pFile, pTokens, 9, 200, false, true);
}
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------

View File

@ -73,14 +73,10 @@ void OgreImporter::SetupProperties(const Importer *pImp) {
m_detectTextureTypeFromFilename = pImp->GetPropertyBool(AI_CONFIG_IMPORT_OGRE_TEXTURETYPE_FROM_FILENAME, false); m_detectTextureTypeFromFilename = pImp->GetPropertyBool(AI_CONFIG_IMPORT_OGRE_TEXTURETYPE_FROM_FILENAME, false);
} }
bool OgreImporter::CanRead(const std::string &pFile, Assimp::IOSystem *pIOHandler, bool checkSig) const { bool OgreImporter::CanRead(const std::string &pFile, Assimp::IOSystem *pIOHandler, bool /*checkSig*/) const {
if (!checkSig) {
return EndsWith(pFile, ".mesh.xml", false) || EndsWith(pFile, ".mesh", false);
}
if (EndsWith(pFile, ".mesh.xml", false)) { if (EndsWith(pFile, ".mesh.xml", false)) {
static const char * const tokens[] = { "<mesh>" }; static const char *tokens[] = { "<mesh>" };
return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1); return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
} }
/// @todo Read and validate first header chunk? /// @todo Read and validate first header chunk?

View File

@ -290,16 +290,9 @@ OpenGEXImporter::~OpenGEXImporter() {
} }
//------------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------------
bool OpenGEXImporter::CanRead(const std::string &file, IOSystem *pIOHandler, bool checkSig) const { bool OpenGEXImporter::CanRead(const std::string &file, IOSystem *pIOHandler, bool /*checkSig*/) const {
bool canRead(false); static const char *tokens[] = { "Metric", "GeometryNode", "VertexArray (attrib", "IndexArray" };
if (!checkSig) { return SearchFileHeaderForToken(pIOHandler, file, tokens, AI_COUNT_OF(tokens));
canRead = SimpleExtensionCheck(file, "ogex");
} else {
static const char * const token[] = { "Metric", "GeometryNode", "VertexArray (attrib", "IndexArray" };
canRead = SearchFileHeaderForToken(pIOHandler, file, token, 4);
}
return canRead;
} }
//------------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------------

View File

@ -100,24 +100,9 @@ PLYImporter::~PLYImporter() {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file. // Returns whether the class can handle the format of the given file.
bool PLYImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const { bool PLYImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
const std::string extension = GetExtension(pFile); static const char *tokens[] = { "ply" };
return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
if (extension == "ply") {
return true;
}
if (!extension.length() || checkSig) {
if (!pIOHandler) {
return true;
}
static const char * const tokens[] = {
"ply"
};
return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1);
}
return false;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------

View File

@ -156,12 +156,11 @@ Q3BSPFileImporter::~Q3BSPFileImporter() {
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns true, if the loader can read this. // Returns true if the loader can read this.
bool Q3BSPFileImporter::CanRead(const std::string &rFile, IOSystem * /*pIOHandler*/, bool checkSig) const { bool Q3BSPFileImporter::CanRead(const std::string &filename, IOSystem * /*pIOHandler*/, bool checkSig) const {
if (!checkSig) { if (!checkSig) {
return SimpleExtensionCheck(rFile, "pk3", "bsp"); return SimpleExtensionCheck(filename, "pk3", "bsp");
} }
return false; return false;
} }

View File

@ -84,18 +84,9 @@ Q3DImporter::~Q3DImporter() {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file. // Returns whether the class can handle the format of the given file.
bool Q3DImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const { bool Q3DImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
const std::string extension = GetExtension(pFile); static const char *tokens[] = { "quick3Do", "quick3Ds" };
return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
if (extension == "q3s" || extension == "q3o")
return true;
else if (!extension.length() || checkSig) {
if (!pIOHandler)
return true;
static const char * const tokens[] = { "quick3Do", "quick3Ds" };
return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 2);
}
return false;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------

View File

@ -84,8 +84,8 @@ RAWImporter::~RAWImporter() {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file. // Returns whether the class can handle the format of the given file.
bool RAWImporter::CanRead(const std::string &pFile, IOSystem * /*pIOHandler*/, bool /*checkSig*/) const { bool RAWImporter::CanRead(const std::string &filename, IOSystem * /*pIOHandler*/, bool /*checkSig*/) const {
return SimpleExtensionCheck(pFile, "raw"); return SimpleExtensionCheck(filename, "raw");
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------

View File

@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
Copyright (c) 2006-2022, assimp team Copyright (c) 2006-2022, assimp team
All rights reserved. All rights reserved.
Redistribution and use of this software in source and binary forms, Redistribution and use of this software in source and binary forms,
@ -61,7 +59,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifdef ASSIMP_USE_HUNTER #ifdef ASSIMP_USE_HUNTER
#include <utf8.h> #include <utf8.h>
#else #else
//# include "../contrib/ConvertUTF/ConvertUTF.h"
#include "../contrib/utf8cpp/source/utf8.h" #include "../contrib/utf8cpp/source/utf8.h"
#endif #endif
#include <assimp/importerdesc.h> #include <assimp/importerdesc.h>
@ -217,8 +214,8 @@ SIBImporter::~SIBImporter() {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file. // Returns whether the class can handle the format of the given file.
bool SIBImporter::CanRead(const std::string &pFile, IOSystem * /*pIOHandler*/, bool /*checkSig*/) const { bool SIBImporter::CanRead(const std::string &filename, IOSystem * /*pIOHandler*/, bool /*checkSig*/) const {
return SimpleExtensionCheck(pFile, "sib"); return SimpleExtensionCheck(filename, "sib");
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------

View File

@ -81,15 +81,15 @@ static const aiImporterDesc desc = {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer // Constructor to be privately used by Importer
SMDImporter::SMDImporter() SMDImporter::SMDImporter() :
: configFrameID() configFrameID(),
, mBuffer() mBuffer(),
, pScene( nullptr ) pScene( nullptr ),
, iFileSize( 0 ) iFileSize( 0 ),
, iSmallestFrame( INT_MAX ) iSmallestFrame( INT_MAX ),
, dLengthOfAnim( 0.0 ) dLengthOfAnim( 0.0 ),
, bHasUVs(false ) bHasUVs(false ),
, iLineNumber((unsigned int)-1) { iLineNumber((unsigned int)-1) {
// empty // empty
} }
@ -101,9 +101,8 @@ SMDImporter::~SMDImporter() {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file. // Returns whether the class can handle the format of the given file.
bool SMDImporter::CanRead( const std::string& pFile, IOSystem* /*pIOHandler*/, bool) const { bool SMDImporter::CanRead( const std::string& filename, IOSystem* /*pIOHandler*/, bool) const {
// fixme: auto format detection return SimpleExtensionCheck(filename, "smd", "vta");
return SimpleExtensionCheck(pFile,"smd","vta");
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------

View File

@ -140,22 +140,9 @@ STLImporter::~STLImporter() {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file. // Returns whether the class can handle the format of the given file.
bool STLImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const { bool STLImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
const std::string extension = GetExtension(pFile); static const char *tokens[] = { "STL", "solid" };
return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
if (extension == "stl") {
return true;
}
if (!extension.length() || checkSig) {
if (!pIOHandler) {
return true;
}
static const char * const tokens[] = { "STL", "solid" };
return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 2);
}
return false;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------

View File

@ -81,27 +81,9 @@ TerragenImporter::~TerragenImporter() {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file. // Returns whether the class can handle the format of the given file.
bool TerragenImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const { bool TerragenImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
// check file extension static const char *tokens[] = { "terragen" };
std::string extension = GetExtension(pFile); return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
if (extension == "ter")
return true;
if (!extension.length() || checkSig) {
/* If CanRead() is called in order to check whether we
* support a specific file extension in general pIOHandler
* might be nullptr and it's our duty to return true here.
*/
if (!pIOHandler) {
return true;
}
static const char * const tokens[] = { "terragen" };
return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1);
}
return false;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------

View File

@ -74,7 +74,7 @@ namespace Unreal {
3 = Masked two-sided 3 = Masked two-sided
4 = Modulation blended two-sided 4 = Modulation blended two-sided
8 = Placeholder triangle for weapon positioning (invisible) 8 = Placeholder triangle for weapon positioning (invisible)
*/ */
enum MeshFlags { enum MeshFlags {
MF_NORMAL_OS = 0, MF_NORMAL_OS = 0,
MF_NORMAL_TS = 1, MF_NORMAL_TS = 1,
@ -168,16 +168,20 @@ static const aiImporterDesc desc = {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer // Constructor to be privately used by Importer
UnrealImporter::UnrealImporter() : UnrealImporter::UnrealImporter() :
mConfigFrameID(0), mConfigHandleFlags(true) {} mConfigFrameID(0), mConfigHandleFlags(true) {
// empty
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Destructor, private as well // Destructor, private as well
UnrealImporter::~UnrealImporter() {} UnrealImporter::~UnrealImporter() {
// empty
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file. // Returns whether the class can handle the format of the given file.
bool UnrealImporter::CanRead(const std::string &pFile, IOSystem * /*pIOHandler*/, bool /*checkSig*/) const { bool UnrealImporter::CanRead(const std::string & filename, IOSystem * /*pIOHandler*/, bool /*checkSig*/) const {
return SimpleExtensionCheck(pFile, "3d", "uc"); return SimpleExtensionCheck(filename, "3d", "uc");
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------

View File

@ -88,17 +88,9 @@ XFileImporter::~XFileImporter() {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file. // Returns whether the class can handle the format of the given file.
bool XFileImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const { bool XFileImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool /*checkSig*/) const {
std::string extension = GetExtension(pFile); static const uint32_t token[] = { AI_MAKE_MAGIC("xof ") };
if(extension == "x") { return CheckMagicToken(pIOHandler,pFile,token,AI_COUNT_OF(token));
return true;
}
if (!extension.length() || checkSig) {
uint32_t token[1];
token[0] = AI_MAKE_MAGIC("xof ");
return CheckMagicToken(pIOHandler,pFile,token,1,0);
}
return false;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------

View File

@ -104,25 +104,9 @@ XGLImporter::~XGLImporter() {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file. // Returns whether the class can handle the format of the given file.
bool XGLImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const { bool XGLImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
/* NOTE: A simple check for the file extension is not enough static const char *tokens[] = { "<world>", "<World>", "<WORLD>" };
* here. XGL and ZGL are ok, but xml is too generic return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
* and might be collada as well. So open the file and
* look for typical signal tokens.
*/
const std::string extension = GetExtension(pFile);
if (extension == "xgl" || extension == "zgl") {
return true;
}
if (extension == "xml" || checkSig) {
ai_assert(pIOHandler != nullptr);
static const char * const tokens[] = { "<world>", "<World>", "<WORLD>" };
return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 3);
}
return false;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------

View File

@ -93,24 +93,14 @@ const aiImporterDesc *glTFImporter::GetInfo() const {
} }
bool glTFImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /* checkSig */) const { bool glTFImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /* checkSig */) const {
const std::string &extension = GetExtension(pFile); glTF::Asset asset(pIOHandler);
try {
if (extension != "gltf" && extension != "glb") { asset.Load(pFile, GetExtension(pFile) == "glb");
std::string version = asset.asset.version;
return !version.empty() && version[0] == '1';
} catch (...) {
return false; return false;
} }
if (pIOHandler) {
glTF::Asset asset(pIOHandler);
try {
asset.Load(pFile, extension == "glb");
std::string version = asset.asset.version;
return !version.empty() && version[0] == '1';
} catch (...) {
return false;
}
}
return false;
} }
inline void SetMaterialColorProperty(std::vector<int> &embeddedTexIdxs, Asset & /*r*/, glTF::TexProperty prop, aiMaterial *mat, inline void SetMaterialColorProperty(std::vector<int> &embeddedTexIdxs, Asset & /*r*/, glTF::TexProperty prop, aiMaterial *mat,

View File

@ -98,7 +98,7 @@ static const aiImporterDesc desc = {
glTF2Importer::glTF2Importer() : glTF2Importer::glTF2Importer() :
BaseImporter(), BaseImporter(),
meshOffsets(), meshOffsets(),
embeddedTexIdxs(), mEmbeddedTexIdxs(),
mScene(nullptr) { mScene(nullptr) {
// empty // empty
} }
@ -111,21 +111,21 @@ const aiImporterDesc *glTF2Importer::GetInfo() const {
return &desc; return &desc;
} }
bool glTF2Importer::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig ) const { bool glTF2Importer::CanRead(const std::string &filename, IOSystem *pIOHandler, bool checkSig ) const {
const std::string &extension = GetExtension(pFile); const std::string extension = GetExtension(filename);
if (!checkSig && (extension != "gltf") && (extension != "glb")) {
return false;
}
if (!checkSig && (extension != "gltf") && (extension != "glb")) if (pIOHandler) {
return false; glTF2::Asset asset(pIOHandler);
return asset.CanRead(filename, extension == "glb");
if (pIOHandler) { }
glTF2::Asset asset(pIOHandler);
return asset.CanRead(pFile, extension == "glb");
}
return false; return false;
} }
static aiTextureMapMode ConvertWrappingMode(SamplerWrap gltfWrapMode) { static inline aiTextureMapMode ConvertWrappingMode(SamplerWrap gltfWrapMode) {
switch (gltfWrapMode) { switch (gltfWrapMode) {
case SamplerWrap::Mirrored_Repeat: case SamplerWrap::Mirrored_Repeat:
return aiTextureMapMode_Mirror; return aiTextureMapMode_Mirror;
@ -140,21 +140,21 @@ static aiTextureMapMode ConvertWrappingMode(SamplerWrap gltfWrapMode) {
} }
} }
inline void SetMaterialColorProperty(Asset & /*r*/, vec4 &prop, aiMaterial *mat, static inline void SetMaterialColorProperty(Asset & /*r*/, vec4 &prop, aiMaterial *mat,
const char *pKey, unsigned int type, unsigned int idx) { const char *pKey, unsigned int type, unsigned int idx) {
aiColor4D col; aiColor4D col;
CopyValue(prop, col); CopyValue(prop, col);
mat->AddProperty(&col, 1, pKey, type, idx); mat->AddProperty(&col, 1, pKey, type, idx);
} }
inline void SetMaterialColorProperty(Asset & /*r*/, vec3 &prop, aiMaterial *mat, static inline void SetMaterialColorProperty(Asset & /*r*/, vec3 &prop, aiMaterial *mat,
const char *pKey, unsigned int type, unsigned int idx) { const char *pKey, unsigned int type, unsigned int idx) {
aiColor4D col; aiColor4D col;
glTFCommon::CopyValue(prop, col); glTFCommon::CopyValue(prop, col);
mat->AddProperty(&col, 1, pKey, type, idx); mat->AddProperty(&col, 1, pKey, type, idx);
} }
inline void SetMaterialTextureProperty(std::vector<int> &embeddedTexIdxs, Asset & /*r*/, static void SetMaterialTextureProperty(std::vector<int> &embeddedTexIdxs, Asset & /*r*/,
glTF2::TextureInfo prop, aiMaterial *mat, aiTextureType texType, glTF2::TextureInfo prop, aiMaterial *mat, aiTextureType texType,
unsigned int texSlot = 0) { unsigned int texSlot = 0) {
if (prop.texture && prop.texture->source) { if (prop.texture && prop.texture->source) {
@ -371,10 +371,10 @@ void glTF2Importer::ImportMaterials(Asset &r) {
mScene->mNumMaterials = numImportedMaterials + 1; mScene->mNumMaterials = numImportedMaterials + 1;
mScene->mMaterials = new aiMaterial *[mScene->mNumMaterials]; mScene->mMaterials = new aiMaterial *[mScene->mNumMaterials];
std::fill(mScene->mMaterials, mScene->mMaterials + mScene->mNumMaterials, nullptr); std::fill(mScene->mMaterials, mScene->mMaterials + mScene->mNumMaterials, nullptr);
mScene->mMaterials[numImportedMaterials] = ImportMaterial(embeddedTexIdxs, r, defaultMaterial); mScene->mMaterials[numImportedMaterials] = ImportMaterial(mEmbeddedTexIdxs, r, defaultMaterial);
for (unsigned int i = 0; i < numImportedMaterials; ++i) { for (unsigned int i = 0; i < numImportedMaterials; ++i) {
mScene->mMaterials[i] = ImportMaterial(embeddedTexIdxs, r, r.materials[i]); mScene->mMaterials[i] = ImportMaterial(mEmbeddedTexIdxs, r, r.materials[i]);
} }
} }
@ -802,8 +802,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
if (actualNumFaces < nFaces) { if (actualNumFaces < nFaces) {
ASSIMP_LOG_WARN("Some faces had out-of-range indices. Those faces were dropped."); ASSIMP_LOG_WARN("Some faces had out-of-range indices. Those faces were dropped.");
} }
if (actualNumFaces == 0) if (actualNumFaces == 0) {
{
throw DeadlyImportError("Mesh \"", aim->mName.C_Str(), "\" has no faces"); throw DeadlyImportError("Mesh \"", aim->mName.C_Str(), "\" has no faces");
} }
aim->mNumFaces = actualNumFaces; aim->mNumFaces = actualNumFaces;
@ -843,7 +842,6 @@ void glTF2Importer::ImportCameras(glTF2::Asset &r) {
aicam->mLookAt = aiVector3D(0.f, 0.f, -1.f); aicam->mLookAt = aiVector3D(0.f, 0.f, -1.f);
if (cam.type == Camera::Perspective) { if (cam.type == Camera::Perspective) {
aicam->mAspect = cam.cameraProperties.perspective.aspectRatio; aicam->mAspect = cam.cameraProperties.perspective.aspectRatio;
aicam->mHorizontalFOV = cam.cameraProperties.perspective.yfov * ((aicam->mAspect == 0.f) ? 1.f : aicam->mAspect); aicam->mHorizontalFOV = cam.cameraProperties.perspective.yfov * ((aicam->mAspect == 0.f) ? 1.f : aicam->mAspect);
aicam->mClipPlaneFar = cam.cameraProperties.perspective.zfar; aicam->mClipPlaneFar = cam.cameraProperties.perspective.zfar;
@ -862,8 +860,9 @@ void glTF2Importer::ImportCameras(glTF2::Asset &r) {
} }
void glTF2Importer::ImportLights(glTF2::Asset &r) { void glTF2Importer::ImportLights(glTF2::Asset &r) {
if (!r.lights.Size()) if (!r.lights.Size()) {
return; return;
}
const unsigned int numLights = r.lights.Size(); const unsigned int numLights = r.lights.Size();
ASSIMP_LOG_DEBUG("Importing ", numLights, " lights"); ASSIMP_LOG_DEBUG("Importing ", numLights, " lights");
@ -1125,8 +1124,8 @@ aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector<unsigned int> &
bone->mNumWeights = static_cast<uint32_t>(weights.size()); bone->mNumWeights = static_cast<uint32_t>(weights.size());
if (bone->mNumWeights > 0) { if (bone->mNumWeights > 0) {
bone->mWeights = new aiVertexWeight[bone->mNumWeights]; bone->mWeights = new aiVertexWeight[bone->mNumWeights];
memcpy(bone->mWeights, weights.data(), bone->mNumWeights * sizeof(aiVertexWeight)); memcpy(bone->mWeights, weights.data(), bone->mNumWeights * sizeof(aiVertexWeight));
} else { } else {
// Assimp expects all bones to have at least 1 weight. // Assimp expects all bones to have at least 1 weight.
bone->mWeights = new aiVertexWeight[1]; bone->mWeights = new aiVertexWeight[1];
@ -1167,8 +1166,7 @@ aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector<unsigned int> &
if (!ainode->mMetaData) { if (!ainode->mMetaData) {
ainode->mMetaData = aiMetadata::Alloc(1); ainode->mMetaData = aiMetadata::Alloc(1);
ainode->mMetaData->Set(0, "PBR_LightRange", node.light->range.value); ainode->mMetaData->Set(0, "PBR_LightRange", node.light->range.value);
} } else {
else {
ainode->mMetaData->Add("PBR_LightRange", node.light->range.value); ainode->mMetaData->Add("PBR_LightRange", node.light->range.value);
} }
} }
@ -1509,16 +1507,20 @@ void glTF2Importer::ImportAnimations(glTF2::Asset &r) {
} }
} }
void glTF2Importer::ImportEmbeddedTextures(glTF2::Asset &r) { static unsigned int countEmbeddedTextures(glTF2::Asset &r) {
embeddedTexIdxs.resize(r.images.Size(), -1); unsigned int numEmbeddedTexs = 0;
int numEmbeddedTexs = 0;
for (size_t i = 0; i < r.images.Size(); ++i) { for (size_t i = 0; i < r.images.Size(); ++i) {
if (r.images[i].HasData()) { if (r.images[i].HasData()) {
numEmbeddedTexs += 1; numEmbeddedTexs += 1;
} }
} }
return numEmbeddedTexs;
}
void glTF2Importer::ImportEmbeddedTextures(glTF2::Asset &r) {
mEmbeddedTexIdxs.resize(r.images.Size(), -1);
const unsigned int numEmbeddedTexs = countEmbeddedTextures(r);
if (numEmbeddedTexs == 0) { if (numEmbeddedTexs == 0) {
return; return;
} }
@ -1536,7 +1538,7 @@ void glTF2Importer::ImportEmbeddedTextures(glTF2::Asset &r) {
} }
int idx = mScene->mNumTextures++; int idx = mScene->mNumTextures++;
embeddedTexIdxs[i] = idx; mEmbeddedTexIdxs[i] = idx;
aiTexture *tex = mScene->mTextures[idx] = new aiTexture(); aiTexture *tex = mScene->mTextures[idx] = new aiTexture();
@ -1597,7 +1599,7 @@ void glTF2Importer::InternReadFile(const std::string &pFile, aiScene *pScene, IO
// clean all member arrays // clean all member arrays
meshOffsets.clear(); meshOffsets.clear();
embeddedTexIdxs.clear(); mEmbeddedTexIdxs.clear();
this->mScene = pScene; this->mScene = pScene;

View File

@ -79,7 +79,7 @@ private:
private: private:
std::vector<unsigned int> meshOffsets; std::vector<unsigned int> meshOffsets;
std::vector<int> embeddedTexIdxs; std::vector<int> mEmbeddedTexIdxs;
aiScene *mScene; aiScene *mScene;
/// An instance of rapidjson::IRemoteSchemaDocumentProvider /// An instance of rapidjson::IRemoteSchemaDocumentProvider

View File

@ -376,6 +376,12 @@ ADD_ASSIMP_IMPORTER( IRRMESH
AssetLib/Irr/IRRShared.h AssetLib/Irr/IRRShared.h
) )
ADD_ASSIMP_IMPORTER( IQM
AssetLib/IQM/IQMImporter.cpp
AssetLib/IQM/iqm.h
AssetLib/IQM/IQMImporter.h
)
ADD_ASSIMP_IMPORTER( IRR ADD_ASSIMP_IMPORTER( IRR
AssetLib/Irr/IRRLoader.cpp AssetLib/Irr/IRRLoader.cpp
AssetLib/Irr/IRRLoader.h AssetLib/Irr/IRRLoader.h

View File

@ -156,8 +156,8 @@ void BaseImporter::GetExtensionList(std::set<std::string> &extensions) {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
/*static*/ bool BaseImporter::SearchFileHeaderForToken(IOSystem *pIOHandler, /*static*/ bool BaseImporter::SearchFileHeaderForToken(IOSystem *pIOHandler,
const std::string &pFile, const std::string &pFile,
const char * const *tokens, const char **tokens,
unsigned int numTokens, std::size_t numTokens,
unsigned int searchBytes /* = 200 */, unsigned int searchBytes /* = 200 */,
bool tokensSol /* false */, bool tokensSol /* false */,
bool noAlphaBeforeTokens /* false */) { bool noAlphaBeforeTokens /* false */) {
@ -268,10 +268,11 @@ std::string BaseImporter::GetExtension(const std::string &file) {
return ret; return ret;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Check for magic bytes at the beginning of the file. // Check for magic bytes at the beginning of the file.
/* static */ bool BaseImporter::CheckMagicToken(IOSystem *pIOHandler, const std::string &pFile, /* static */ bool BaseImporter::CheckMagicToken(IOSystem *pIOHandler, const std::string &pFile,
const void *_magic, unsigned int num, unsigned int offset, unsigned int size) { const void *_magic, std::size_t num, unsigned int offset, unsigned int size) {
ai_assert(size <= 16); ai_assert(size <= 16);
ai_assert(_magic); ai_assert(_magic);

View File

@ -617,29 +617,71 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags) {
profiler->BeginRegion("total"); profiler->BeginRegion("total");
} }
// Find an worker class which can handle the file // Find an worker class which can handle the file extension.
BaseImporter* imp = nullptr; // Multiple importers may be able to handle the same extension (.xml!); gather them all.
SetPropertyInteger("importerIndex", -1); SetPropertyInteger("importerIndex", -1);
for( unsigned int a = 0; a < pimpl->mImporter.size(); a++) { struct ImporterAndIndex {
BaseImporter * importer;
unsigned int index;
};
std::vector<ImporterAndIndex> possibleImporters;
for (unsigned int a = 0; a < pimpl->mImporter.size(); a++) {
// Every importer has a list of supported extensions.
std::set<std::string> extensions;
pimpl->mImporter[a]->GetExtensionList(extensions);
// CAUTION: Do not just search for the extension!
// GetExtension() returns the part after the *last* dot, but some extensions have dots
// inside them, e.g. ogre.mesh.xml. Compare the entire end of the string.
for (std::set<std::string>::const_iterator it = extensions.cbegin(); it != extensions.cend(); ++it) {
// Yay for C++<20 not having std::string::ends_with()
std::string extension = "." + *it;
if (extension.length() <= pFile.length()) {
// Possible optimization: Fetch the lowercase filename!
if (0 == ASSIMP_stricmp(pFile.c_str() + pFile.length() - extension.length(), extension.c_str())) {
ImporterAndIndex candidate = { pimpl->mImporter[a], a };
possibleImporters.push_back(candidate);
break;
}
}
if( pimpl->mImporter[a]->CanRead( pFile, pimpl->mIOHandler, false)) {
imp = pimpl->mImporter[a];
SetPropertyInteger("importerIndex", a);
break;
} }
}
// If just one importer supports this extension, pick it and close the case.
BaseImporter* imp = nullptr;
if (1 == possibleImporters.size()) {
imp = possibleImporters[0].importer;
SetPropertyInteger("importerIndex", possibleImporters[0].index);
}
// If multiple importers claim this file extension, ask them to look at the actual file data to decide.
// This can happen e.g. with XML (COLLADA vs. Irrlicht).
else {
for (std::vector<ImporterAndIndex>::const_iterator it = possibleImporters.begin(); it < possibleImporters.end(); ++it) {
BaseImporter & importer = *it->importer;
ASSIMP_LOG_INFO("Found a possible importer: " + std::string(importer.GetInfo()->mName) + "; trying signature-based detection");
if (importer.CanRead( pFile, pimpl->mIOHandler, true)) {
imp = &importer;
SetPropertyInteger("importerIndex", it->index);
break;
}
}
} }
if (!imp) { if (!imp) {
// not so bad yet ... try format auto detection. // not so bad yet ... try format auto detection.
const std::string::size_type s = pFile.find_last_of('.'); ASSIMP_LOG_INFO("File extension not known, trying signature-based detection");
if (s != std::string::npos) { for( unsigned int a = 0; a < pimpl->mImporter.size(); a++) {
ASSIMP_LOG_INFO("File extension not known, trying signature-based detection"); if( pimpl->mImporter[a]->CanRead( pFile, pimpl->mIOHandler, true)) {
for( unsigned int a = 0; a < pimpl->mImporter.size(); a++) { imp = pimpl->mImporter[a];
if( pimpl->mImporter[a]->CanRead( pFile, pimpl->mIOHandler, true)) { SetPropertyInteger("importerIndex", a);
imp = pimpl->mImporter[a]; break;
SetPropertyInteger("importerIndex", a);
break;
}
} }
} }
// Put a proper error message if no suitable importer was found // Put a proper error message if no suitable importer was found

View File

@ -202,6 +202,9 @@ corresponding preprocessor flag to selectively disable formats.
#ifndef ASSIMP_BUILD_NO_M3D_IMPORTER #ifndef ASSIMP_BUILD_NO_M3D_IMPORTER
#include "AssetLib/M3D/M3DImporter.h" #include "AssetLib/M3D/M3DImporter.h"
#endif #endif
#ifndef ASSIMP_BUILD_NO_IQM_IMPORTER
#include "AssetLib/IQM/IQMImporter.h"
#endif
namespace Assimp { namespace Assimp {
@ -370,6 +373,9 @@ void GetImporterInstanceList(std::vector<BaseImporter *> &out) {
#endif #endif
#ifndef ASSIMP_BUILD_NO_MMD_IMPORTER #ifndef ASSIMP_BUILD_NO_MMD_IMPORTER
out.push_back(new MMDImporter()); out.push_back(new MMDImporter());
#endif
#ifndef ASSIMP_BUILD_NO_IQM_IMPORTER
out.push_back(new IQMImporter());
#endif #endif
//#ifndef ASSIMP_BUILD_NO_STEP_IMPORTER //#ifndef ASSIMP_BUILD_NO_STEP_IMPORTER
// out.push_back(new StepFile::StepFileImporter()); // out.push_back(new StepFile::StepFileImporter());

View File

@ -95,20 +95,15 @@ public:
// ------------------------------------------------------------------- // -------------------------------------------------------------------
/** Returns whether the class can handle the format of the given file. /** Returns whether the class can handle the format of the given file.
* *
* The implementation should be as quick as possible. A check for * The implementation is expected to perform a full check of the file
* the file extension is enough. If no suitable loader is found with * structure, possibly searching the first bytes of the file for magic
* this strategy, CanRead() is called again, the 'checkSig' parameter * identifiers or keywords.
* set to true this time. Now the implementation is expected to
* perform a full check of the file structure, possibly searching the
* first bytes of the file for magic identifiers or keywords.
* *
* @param pFile Path and file name of the file to be examined. * @param pFile Path and file name of the file to be examined.
* @param pIOHandler The IO handler to use for accessing any file. * @param pIOHandler The IO handler to use for accessing any file.
* @param checkSig Set to true if this method is called a second time. * @param checkSig Legacy; do not use.
* This time, the implementation may take more time to examine the * @return true if the class can read this file, false if not or if
* contents of the file to be loaded for magic bytes, keywords, etc * unsure.
* to be able to load files with unknown/not existent file extensions.
* @return true if the class can read this file, false if not.
*/ */
virtual bool CanRead( virtual bool CanRead(
const std::string &pFile, const std::string &pFile,
@ -259,8 +254,8 @@ public: // static utilities
static bool SearchFileHeaderForToken( static bool SearchFileHeaderForToken(
IOSystem *pIOSystem, IOSystem *pIOSystem,
const std::string &file, const std::string &file,
const char * const *tokens, const char **tokens,
unsigned int numTokens, std::size_t numTokens,
unsigned int searchBytes = 200, unsigned int searchBytes = 200,
bool tokensSol = false, bool tokensSol = false,
bool noAlphaBeforeTokens = false); bool noAlphaBeforeTokens = false);
@ -305,7 +300,7 @@ public: // static utilities
IOSystem *pIOHandler, IOSystem *pIOHandler,
const std::string &pFile, const std::string &pFile,
const void *magic, const void *magic,
unsigned int num, std::size_t num,
unsigned int offset = 0, unsigned int offset = 0,
unsigned int size = 4); unsigned int size = 4);

View File

@ -331,4 +331,6 @@ static const ai_real ai_epsilon = (ai_real)0.00001;
#define AI_DEBUG_INVALIDATE_PTR(x) #define AI_DEBUG_INVALIDATE_PTR(x)
#endif #endif
#define AI_COUNT_OF(X) (sizeof(X) / sizeof((X)[0]))
#endif // !! AI_DEFINES_H_INC #endif // !! AI_DEFINES_H_INC

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.