nuclear@0: /*
nuclear@0: Open Asset Import Library (assimp)
nuclear@0: ----------------------------------------------------------------------
nuclear@0:
nuclear@0: Copyright (c) 2006-2008, assimp team
nuclear@0: All rights reserved.
nuclear@0:
nuclear@0: Redistribution and use of this software in source and binary forms,
nuclear@0: with or without modification, are permitted provided that the
nuclear@0: following conditions are met:
nuclear@0:
nuclear@0: * Redistributions of source code must retain the above
nuclear@0: copyright notice, this list of conditions and the
nuclear@0: following disclaimer.
nuclear@0:
nuclear@0: * Redistributions in binary form must reproduce the above
nuclear@0: copyright notice, this list of conditions and the
nuclear@0: following disclaimer in the documentation and/or other
nuclear@0: materials provided with the distribution.
nuclear@0:
nuclear@0: * Neither the name of the assimp team, nor the names of its
nuclear@0: contributors may be used to endorse or promote products
nuclear@0: derived from this software without specific prior
nuclear@0: written permission of the assimp team.
nuclear@0:
nuclear@0: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
nuclear@0: "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
nuclear@0: LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
nuclear@0: A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
nuclear@0: OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
nuclear@0: SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
nuclear@0: LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
nuclear@0: DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
nuclear@0: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
nuclear@0: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
nuclear@0: OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
nuclear@0:
nuclear@0: ----------------------------------------------------------------------
nuclear@0: */
nuclear@0:
nuclear@0: /** @file FileSystemFilter.h
nuclear@0: * Implements a filter system to filter calls to Exists() and Open()
nuclear@0: * in order to improve the sucess rate of file opening ...
nuclear@0: */
nuclear@0: #ifndef AI_FILESYSTEMFILTER_H_INC
nuclear@0: #define AI_FILESYSTEMFILTER_H_INC
nuclear@0:
nuclear@0: #include "assimp/IOSystem.hpp"
nuclear@0: #include "fast_atof.h"
nuclear@0: #include "ParsingUtils.h"
nuclear@0: namespace Assimp {
nuclear@0:
nuclear@0: inline bool IsHex(char s) {
nuclear@0: return (s>='0' && s<='9') || (s>='a' && s<='f') || (s>='A' && s<='F');
nuclear@0: }
nuclear@0:
nuclear@0: // ---------------------------------------------------------------------------
nuclear@0: /** File system filter
nuclear@0: */
nuclear@0: class FileSystemFilter : public IOSystem
nuclear@0: {
nuclear@0: public:
nuclear@0: /** Constructor. */
nuclear@0: FileSystemFilter(const std::string& file, IOSystem* old)
nuclear@0: : wrapped (old)
nuclear@0: , src_file (file)
nuclear@0: , sep(wrapped->getOsSeparator())
nuclear@0: {
nuclear@0: ai_assert(NULL != wrapped);
nuclear@0:
nuclear@0: // Determine base directory
nuclear@0: base = src_file;
nuclear@0: std::string::size_type ss2;
nuclear@0: if (std::string::npos != (ss2 = base.find_last_of("\\/"))) {
nuclear@0: base.erase(ss2,base.length()-ss2);
nuclear@0: }
nuclear@0: else {
nuclear@0: base = "";
nuclear@0: // return;
nuclear@0: }
nuclear@0:
nuclear@0: // make sure the directory is terminated properly
nuclear@0: char s;
nuclear@0:
nuclear@0: if (base.length() == 0) {
nuclear@0: base = ".";
nuclear@0: base += getOsSeparator();
nuclear@0: }
nuclear@0: else if ((s = *(base.end()-1)) != '\\' && s != '/') {
nuclear@0: base += getOsSeparator();
nuclear@0: }
nuclear@0:
nuclear@0: DefaultLogger::get()->info("Import root directory is \'" + base + "\'");
nuclear@0: }
nuclear@0:
nuclear@0: /** Destructor. */
nuclear@0: ~FileSystemFilter()
nuclear@0: {
nuclear@0: // haha
nuclear@0: }
nuclear@0:
nuclear@0: // -------------------------------------------------------------------
nuclear@0: /** Tests for the existence of a file at the given path. */
nuclear@0: bool Exists( const char* pFile) const
nuclear@0: {
nuclear@0: std::string tmp = pFile;
nuclear@0:
nuclear@0: // Currently this IOSystem is also used to open THE ONE FILE.
nuclear@0: if (tmp != src_file) {
nuclear@0: BuildPath(tmp);
nuclear@0: Cleanup(tmp);
nuclear@0: }
nuclear@0:
nuclear@0: return wrapped->Exists(tmp);
nuclear@0: }
nuclear@0:
nuclear@0: // -------------------------------------------------------------------
nuclear@0: /** Returns the directory separator. */
nuclear@0: char getOsSeparator() const
nuclear@0: {
nuclear@0: return sep;
nuclear@0: }
nuclear@0:
nuclear@0: // -------------------------------------------------------------------
nuclear@0: /** Open a new file with a given path. */
nuclear@0: IOStream* Open( const char* pFile, const char* pMode = "rb")
nuclear@0: {
nuclear@0: ai_assert(pFile);
nuclear@0: ai_assert(pMode);
nuclear@0:
nuclear@0: // First try the unchanged path
nuclear@0: IOStream* s = wrapped->Open(pFile,pMode);
nuclear@0:
nuclear@0: if (!s) {
nuclear@0: std::string tmp = pFile;
nuclear@0:
nuclear@0: // Try to convert between absolute and relative paths
nuclear@0: BuildPath(tmp);
nuclear@0: s = wrapped->Open(tmp,pMode);
nuclear@0:
nuclear@0: if (!s) {
nuclear@0: // Finally, look for typical issues with paths
nuclear@0: // and try to correct them. This is our last
nuclear@0: // resort.
nuclear@0: tmp = pFile;
nuclear@0: Cleanup(tmp);
nuclear@0: BuildPath(tmp);
nuclear@0: s = wrapped->Open(tmp,pMode);
nuclear@0: }
nuclear@0: }
nuclear@0:
nuclear@0: return s;
nuclear@0: }
nuclear@0:
nuclear@0: // -------------------------------------------------------------------
nuclear@0: /** Closes the given file and releases all resources associated with it. */
nuclear@0: void Close( IOStream* pFile)
nuclear@0: {
nuclear@0: return wrapped->Close(pFile);
nuclear@0: }
nuclear@0:
nuclear@0: // -------------------------------------------------------------------
nuclear@0: /** Compare two paths */
nuclear@0: bool ComparePaths (const char* one, const char* second) const
nuclear@0: {
nuclear@0: return wrapped->ComparePaths (one,second);
nuclear@0: }
nuclear@0:
nuclear@0: private:
nuclear@0:
nuclear@0: // -------------------------------------------------------------------
nuclear@0: /** Build a valid path from a given relative or absolute path.
nuclear@0: */
nuclear@0: void BuildPath (std::string& in) const
nuclear@0: {
nuclear@0: // if we can already access the file, great.
nuclear@0: if (in.length() < 3 || wrapped->Exists(in)) {
nuclear@0: return;
nuclear@0: }
nuclear@0:
nuclear@0: // Determine whether this is a relative path (Windows-specific - most assets are packaged on Windows).
nuclear@0: if (in[1] != ':') {
nuclear@0:
nuclear@0: // append base path and try
nuclear@0: const std::string tmp = base + in;
nuclear@0: if (wrapped->Exists(tmp)) {
nuclear@0: in = tmp;
nuclear@0: return;
nuclear@0: }
nuclear@0: }
nuclear@0:
nuclear@0: // Chop of the file name and look in the model directory, if
nuclear@0: // this fails try all sub paths of the given path, i.e.
nuclear@0: // if the given path is foo/bar/something.lwo, try
nuclear@0: // /something.lwo
nuclear@0: // /bar/something.lwo
nuclear@0: // /foo/bar/something.lwo
nuclear@0: std::string::size_type pos = in.rfind('/');
nuclear@0: if (std::string::npos == pos) {
nuclear@0: pos = in.rfind('\\');
nuclear@0: }
nuclear@0:
nuclear@0: if (std::string::npos != pos) {
nuclear@0: std::string tmp;
nuclear@0: std::string::size_type last_dirsep = std::string::npos;
nuclear@0:
nuclear@0: while(true) {
nuclear@0: tmp = base;
nuclear@0: tmp += sep;
nuclear@0:
nuclear@0: std::string::size_type dirsep = in.rfind('/', last_dirsep);
nuclear@0: if (std::string::npos == dirsep) {
nuclear@0: dirsep = in.rfind('\\', last_dirsep);
nuclear@0: }
nuclear@0:
nuclear@0: if (std::string::npos == dirsep || dirsep == 0) {
nuclear@0: // we did try this already.
nuclear@0: break;
nuclear@0: }
nuclear@0:
nuclear@0: last_dirsep = dirsep-1;
nuclear@0:
nuclear@0: tmp += in.substr(dirsep+1, in.length()-pos);
nuclear@0: if (wrapped->Exists(tmp)) {
nuclear@0: in = tmp;
nuclear@0: return;
nuclear@0: }
nuclear@0: }
nuclear@0: }
nuclear@0:
nuclear@0: // hopefully the underlying file system has another few tricks to access this file ...
nuclear@0: }
nuclear@0:
nuclear@0: // -------------------------------------------------------------------
nuclear@0: /** Cleanup the given path
nuclear@0: */
nuclear@0: void Cleanup (std::string& in) const
nuclear@0: {
nuclear@0: char last = 0;
nuclear@0: if(in.empty()) {
nuclear@0: return;
nuclear@0: }
nuclear@0:
nuclear@0: // Remove a very common issue when we're parsing file names: spaces at the
nuclear@0: // beginning of the path.
nuclear@0: std::string::iterator it = in.begin();
nuclear@0: while (IsSpaceOrNewLine( *it ))++it;
nuclear@0: if (it != in.begin()) {
nuclear@0: in.erase(in.begin(),it+1);
nuclear@0: }
nuclear@0:
nuclear@0: const char sep = getOsSeparator();
nuclear@0: for (it = in.begin(); it != in.end(); ++it) {
nuclear@0: // Exclude :// and \\, which remain untouched.
nuclear@0: // https://sourceforge.net/tracker/?func=detail&aid=3031725&group_id=226462&atid=1067632
nuclear@0: if ( !strncmp(&*it, "://", 3 )) {
nuclear@0: it += 3;
nuclear@0: continue;
nuclear@0: }
nuclear@0: if (it == in.begin() && !strncmp(&*it, "\\\\", 2)) {
nuclear@0: it += 2;
nuclear@0: continue;
nuclear@0: }
nuclear@0:
nuclear@0: // Cleanup path delimiters
nuclear@0: if (*it == '/' || (*it) == '\\') {
nuclear@0: *it = sep;
nuclear@0:
nuclear@0: // And we're removing double delimiters, frequent issue with
nuclear@0: // incorrectly composited paths ...
nuclear@0: if (last == *it) {
nuclear@0: it = in.erase(it);
nuclear@0: --it;
nuclear@0: }
nuclear@0: }
nuclear@0: else if (*it == '%' && in.end() - it > 2) {
nuclear@0:
nuclear@0: // Hex sequence in URIs
nuclear@0: if( IsHex((&*it)[0]) && IsHex((&*it)[1]) ) {
nuclear@0: *it = HexOctetToDecimal(&*it);
nuclear@0: it = in.erase(it+1,it+2);
nuclear@0: --it;
nuclear@0: }
nuclear@0: }
nuclear@0:
nuclear@0: last = *it;
nuclear@0: }
nuclear@0: }
nuclear@0:
nuclear@0: private:
nuclear@0: IOSystem* wrapped;
nuclear@0: std::string src_file, base;
nuclear@0: char sep;
nuclear@0: };
nuclear@0:
nuclear@0: } //!ns Assimp
nuclear@0:
nuclear@0: #endif //AI_DEFAULTIOSYSTEM_H_INC