1 /*
2 Open Asset Import Library (assimp)
3 ----------------------------------------------------------------------
4 
5 Copyright (c) 2006-2021, assimp team
6 
7 All rights reserved.
8 
9 Redistribution and use of this software in source and binary forms,
10 with or without modification, are permitted provided that the
11 following conditions are met:
12 
13 * Redistributions of source code must retain the above
14   copyright notice, this list of conditions and the
15   following disclaimer.
16 
17 * Redistributions in binary form must reproduce the above
18   copyright notice, this list of conditions and the
19   following disclaimer in the documentation and/or other
20   materials provided with the distribution.
21 
22 * Neither the name of the assimp team, nor the names of its
23   contributors may be used to endorse or promote products
24   derived from this software without specific prior
25   written permission of the assimp team.
26 
27 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 
39 ----------------------------------------------------------------------
40 */
41 
42 /// @file Definition of the base class for all importer worker classes.
43 
44 #pragma once
45 #ifndef INCLUDED_AI_BASEIMPORTER_H
46 #define INCLUDED_AI_BASEIMPORTER_H
47 
48 #ifdef __GNUC__
49 #pragma GCC system_header
50 #endif
51 
52 #include "Exceptional.h"
53 
54 #include <assimp/types.h>
55 #include <assimp/ProgressHandler.hpp>
56 #include <set>
57 #include <vector>
58 #include <memory>
59 
60 struct aiScene;
61 struct aiImporterDesc;
62 
63 namespace Assimp {
64 
65 class Importer;
66 class IOSystem;
67 class BaseProcess;
68 class SharedPostProcessInfo;
69 class IOStream;
70 
71 // utility to do char4 to uint32 in a portable manner
72 #define AI_MAKE_MAGIC(string) ((uint32_t)((string[0] << 24) + \
73                                           (string[1] << 16) + (string[2] << 8) + string[3]))
74 
75 // ---------------------------------------------------------------------------
76 /** FOR IMPORTER PLUGINS ONLY: The BaseImporter defines a common interface
77  *  for all importer worker classes.
78  *
79  * The interface defines two functions: CanRead() is used to check if the
80  * importer can handle the format of the given file. If an implementation of
81  * this function returns true, the importer then calls ReadFile() which
82  * imports the given file. ReadFile is not overridable, it just calls
83  * InternReadFile() and catches any ImportErrorException that might occur.
84  */
85 class ASSIMP_API BaseImporter {
86     friend class Importer;
87 
88 public:
89     /** Constructor to be privately used by #Importer */
90     BaseImporter() AI_NO_EXCEPT;
91 
92     /** Destructor, private as well */
93     virtual ~BaseImporter();
94 
95     // -------------------------------------------------------------------
96     /** Returns whether the class can handle the format of the given file.
97      *
98      * The implementation should be as quick as possible. A check for
99      * the file extension is enough. If no suitable loader is found with
100      * this strategy, CanRead() is called again, the 'checkSig' parameter
101      * set to true this time. Now the implementation is expected to
102      * perform a full check of the file structure, possibly searching the
103      * first bytes of the file for magic identifiers or keywords.
104      *
105      * @param pFile Path and file name of the file to be examined.
106      * @param pIOHandler The IO handler to use for accessing any file.
107      * @param checkSig Set to true if this method is called a second time.
108      *   This time, the implementation may take more time to examine the
109      *   contents of the file to be loaded for magic bytes, keywords, etc
110      *   to be able to load files with unknown/not existent file extensions.
111      * @return true if the class can read this file, false if not.
112      */
113     virtual bool CanRead(
114             const std::string &pFile,
115             IOSystem *pIOHandler,
116             bool checkSig) const = 0;
117 
118     // -------------------------------------------------------------------
119     /** Imports the given file and returns the imported data.
120      * If the import succeeds, ownership of the data is transferred to
121      * the caller. If the import fails, nullptr is returned. The function
122      * takes care that any partially constructed data is destroyed
123      * beforehand.
124      *
125      * @param pImp #Importer object hosting this loader.
126      * @param pFile Path of the file to be imported.
127      * @param pIOHandler IO-Handler used to open this and possible other files.
128      * @return The imported data or nullptr if failed. If it failed a
129      * human-readable error description can be retrieved by calling
130      * GetErrorText()
131      *
132      * @note This function is not intended to be overridden. Implement
133      * InternReadFile() to do the import. If an exception is thrown somewhere
134      * in InternReadFile(), this function will catch it and transform it into
135      *  a suitable response to the caller.
136      */
137     aiScene *ReadFile(
138             Importer *pImp,
139             const std::string &pFile,
140             IOSystem *pIOHandler);
141 
142     // -------------------------------------------------------------------
143     /** Returns the error description of the last error that occurred.
144      * If the error is due to a std::exception, this will return the message.
145      * Exceptions can also be accessed with GetException().
146      * @return A description of the last error that occurred. An empty
147      * string if there was no error.
148      */
GetErrorText()149     const std::string &GetErrorText() const {
150         return m_ErrorText;
151     }
152 
153     // -------------------------------------------------------------------
154     /** Returns the exception of the last exception that occurred.
155      * Note: Exceptions are not the only source of error details, so GetErrorText
156      * should be consulted too.
157      * @return The last exception that occurred.
158      */
GetException()159     const std::exception_ptr& GetException() const {
160         return m_Exception;
161     }
162 
163     // -------------------------------------------------------------------
164     /** Called prior to ReadFile().
165      * The function is a request to the importer to update its configuration
166      * basing on the Importer's configuration property list.
167      * @param pImp Importer instance
168      */
169     virtual void SetupProperties(
170             const Importer *pImp);
171 
172     // -------------------------------------------------------------------
173     /** Called by #Importer::GetImporterInfo to get a description of
174      *  some loader features. Importers must provide this information. */
175     virtual const aiImporterDesc *GetInfo() const = 0;
176 
177     /**
178      * Will be called only by scale process when scaling is requested.
179      */
SetFileScale(double scale)180     void SetFileScale(double scale) {
181         fileScale = scale;
182     }
183 
184     // -------------------------------------------------------------------
185     /** Called by #Importer::GetExtensionList for each loaded importer.
186      *  Take the extension list contained in the structure returned by
187      *  #GetInfo and insert all file extensions into the given set.
188      *  @param extension set to collect file extensions in*/
189     void GetExtensionList(std::set<std::string> &extensions);
190 
191 protected:
192     double importerScale = 1.0;
193     double fileScale = 1.0;
194 
195     // -------------------------------------------------------------------
196     /** Imports the given file into the given scene structure. The
197      * function is expected to throw an ImportErrorException if there is
198      * an error. If it terminates normally, the data in aiScene is
199      * expected to be correct. Override this function to implement the
200      * actual importing.
201      * <br>
202      *  The output scene must meet the following requirements:<br>
203      * <ul>
204      * <li>At least a root node must be there, even if its only purpose
205      *     is to reference one mesh.</li>
206      * <li>aiMesh::mPrimitiveTypes may be 0. The types of primitives
207      *   in the mesh are determined automatically in this case.</li>
208      * <li>the vertex data is stored in a pseudo-indexed "verbose" format.
209      *   In fact this means that every vertex that is referenced by
210      *   a face is unique. Or the other way round: a vertex index may
211      *   not occur twice in a single aiMesh.</li>
212      * <li>aiAnimation::mDuration may be -1. Assimp determines the length
213      *   of the animation automatically in this case as the length of
214      *   the longest animation channel.</li>
215      * <li>aiMesh::mBitangents may be nullptr if tangents and normals are
216      *   given. In this case bitangents are computed as the cross product
217      *   between normal and tangent.</li>
218      * <li>There needn't be a material. If none is there a default material
219      *   is generated. However, it is recommended practice for loaders
220      *   to generate a default material for yourself that matches the
221      *   default material setting for the file format better than Assimp's
222      *   generic default material. Note that default materials *should*
223      *   be named AI_DEFAULT_MATERIAL_NAME if they're just color-shaded
224      *   or AI_DEFAULT_TEXTURED_MATERIAL_NAME if they define a (dummy)
225      *   texture. </li>
226      * </ul>
227      * If the AI_SCENE_FLAGS_INCOMPLETE-Flag is <b>not</b> set:<ul>
228      * <li> at least one mesh must be there</li>
229      * <li> there may be no meshes with 0 vertices or faces</li>
230      * </ul>
231      * This won't be checked (except by the validation step): Assimp will
232      * crash if one of the conditions is not met!
233      *
234      * @param pFile Path of the file to be imported.
235      * @param pScene The scene object to hold the imported data.
236      * nullptr is not a valid parameter.
237      * @param pIOHandler The IO handler to use for any file access.
238      * nullptr is not a valid parameter. */
239     virtual void InternReadFile(
240             const std::string &pFile,
241             aiScene *pScene,
242             IOSystem *pIOHandler) = 0;
243 
244 public: // static utilities
245     // -------------------------------------------------------------------
246     /** A utility for CanRead().
247      *
248      *  The function searches the header of a file for a specific token
249      *  and returns true if this token is found. This works for text
250      *  files only. There is a rudimentary handling of UNICODE files.
251      *  The comparison is case independent.
252      *
253      *  @param pIOSystem IO System to work with
254      *  @param file File name of the file
255      *  @param tokens List of tokens to search for
256      *  @param numTokens Size of the token array
257      *  @param searchBytes Number of bytes to be searched for the tokens.
258      */
259     static bool SearchFileHeaderForToken(
260             IOSystem *pIOSystem,
261             const std::string &file,
262             const char * const *tokens,
263             unsigned int numTokens,
264             unsigned int searchBytes = 200,
265             bool tokensSol = false,
266             bool noAlphaBeforeTokens = false);
267 
268     // -------------------------------------------------------------------
269     /** @brief Check whether a file has a specific file extension
270      *  @param pFile Input file
271      *  @param ext0 Extension to check for. Lowercase characters only, no dot!
272      *  @param ext1 Optional second extension
273      *  @param ext2 Optional third extension
274      *  @note Case-insensitive
275      */
276     static bool SimpleExtensionCheck(
277             const std::string &pFile,
278             const char *ext0,
279             const char *ext1 = nullptr,
280             const char *ext2 = nullptr);
281 
282     // -------------------------------------------------------------------
283     /** @brief Extract file extension from a string
284      *  @param pFile Input file
285      *  @return Extension without trailing dot, all lowercase
286      */
287     static std::string GetExtension(
288             const std::string &pFile);
289 
290     // -------------------------------------------------------------------
291     /** @brief Check whether a file starts with one or more magic tokens
292      *  @param pFile Input file
293      *  @param pIOHandler IO system to be used
294      *  @param magic n magic tokens
295      *  @params num Size of magic
296      *  @param offset Offset from file start where tokens are located
297      *  @param Size of one token, in bytes. Maximally 16 bytes.
298      *  @return true if one of the given tokens was found
299      *
300      *  @note For convenience, the check is also performed for the
301      *  byte-swapped variant of all tokens (big endian). Only for
302      *  tokens of size 2,4.
303      */
304     static bool CheckMagicToken(
305             IOSystem *pIOHandler,
306             const std::string &pFile,
307             const void *magic,
308             unsigned int num,
309             unsigned int offset = 0,
310             unsigned int size = 4);
311 
312     // -------------------------------------------------------------------
313     /** An utility for all text file loaders. It converts a file to our
314      *   UTF8 character set. Errors are reported, but ignored.
315      *
316      *  @param data File buffer to be converted to UTF8 data. The buffer
317      *  is resized as appropriate. */
318     static void ConvertToUTF8(
319             std::vector<char> &data);
320 
321     // -------------------------------------------------------------------
322     /** An utility for all text file loaders. It converts a file from our
323      *   UTF8 character set back to ISO-8859-1. Errors are reported, but ignored.
324      *
325      *  @param data File buffer to be converted from UTF8 to ISO-8859-1. The buffer
326      *  is resized as appropriate. */
327     static void ConvertUTF8toISO8859_1(
328             std::string &data);
329 
330     // -------------------------------------------------------------------
331     /// @brief  Enum to define, if empty files are ok or not.
332     enum TextFileMode {
333         ALLOW_EMPTY,
334         FORBID_EMPTY
335     };
336 
337     // -------------------------------------------------------------------
338     /** Utility for text file loaders which copies the contents of the
339      *  file into a memory buffer and converts it to our UTF8
340      *  representation.
341      *  @param stream Stream to read from.
342      *  @param data Output buffer to be resized and filled with the
343      *   converted text file data. The buffer is terminated with
344      *   a binary 0.
345      *  @param mode Whether it is OK to load empty text files. */
346     static void TextFileToBuffer(
347             IOStream *stream,
348             std::vector<char> &data,
349             TextFileMode mode = FORBID_EMPTY);
350 
351     // -------------------------------------------------------------------
352     /** Utility function to move a std::vector into a aiScene array
353     *  @param vec The vector to be moved
354     *  @param out The output pointer to the allocated array.
355     *  @param numOut The output count of elements copied. */
356     template <typename T>
CopyVector(std::vector<T> & vec,T * & out,unsigned int & outLength)357     AI_FORCE_INLINE static void CopyVector(
358             std::vector<T> &vec,
359             T *&out,
360             unsigned int &outLength) {
361         outLength = unsigned(vec.size());
362         if (outLength) {
363             out = new T[outLength];
364             std::swap_ranges(vec.begin(), vec.end(), out);
365         }
366     }
367 
368     // -------------------------------------------------------------------
369     /** Utility function to move a std::vector of unique_ptrs into a aiScene array
370     *  @param vec The vector of unique_ptrs to be moved
371     *  @param out The output pointer to the allocated array.
372     *  @param numOut The output count of elements copied. */
373     template <typename T>
CopyVector(std::vector<std::unique_ptr<T>> & vec,T ** & out,unsigned int & outLength)374     AI_FORCE_INLINE static void CopyVector(
375             std::vector<std::unique_ptr<T> > &vec,
376             T **&out,
377             unsigned int &outLength) {
378         outLength = unsigned(vec.size());
379         if (outLength) {
380             out = new T*[outLength];
381             T** outPtr = out;
382             std::for_each(vec.begin(), vec.end(), [&outPtr](std::unique_ptr<T>& uPtr){*outPtr = uPtr.release(); ++outPtr; });
383         }
384     }
385 
386 private:
387     /* Pushes state into importer for the importer scale */
388     void UpdateImporterScale(Importer *pImp);
389 
390 protected:
391     /// Error description in case there was one.
392     std::string m_ErrorText;
393     /// The exception, in case there was one.
394     std::exception_ptr m_Exception;
395     /// Currently set progress handler.
396     ProgressHandler *m_progress;
397 };
398 
399 } // end of namespace Assimp
400 
401 #endif // AI_BASEIMPORTER_H_INC
402