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