350 lines
11 KiB
C++
350 lines
11 KiB
C++
/*
|
|
Open Asset Import Library (assimp)
|
|
----------------------------------------------------------------------
|
|
|
|
Copyright (c) 2006-2008, 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 FileSystemFilter.h
|
|
* Implements a filter system to filter calls to Exists() and Open()
|
|
* in order to improve the success rate of file opening ...
|
|
*/
|
|
#ifndef AI_FILESYSTEMFILTER_H_INC
|
|
#define AI_FILESYSTEMFILTER_H_INC
|
|
|
|
#include "../include/assimp/IOSystem.hpp"
|
|
#include "../include/assimp/DefaultLogger.hpp"
|
|
#include "../include/assimp/fast_atof.h"
|
|
#include "../include/assimp/ParsingUtils.h"
|
|
|
|
namespace Assimp {
|
|
|
|
inline bool IsHex(char s) {
|
|
return (s>='0' && s<='9') || (s>='a' && s<='f') || (s>='A' && s<='F');
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
/** File system filter
|
|
*/
|
|
class FileSystemFilter : public IOSystem
|
|
{
|
|
public:
|
|
/** Constructor. */
|
|
FileSystemFilter(const std::string& file, IOSystem* old)
|
|
: wrapped (old)
|
|
, src_file (file)
|
|
, sep(wrapped->getOsSeparator())
|
|
{
|
|
ai_assert(NULL != wrapped);
|
|
|
|
// Determine base directory
|
|
base = src_file;
|
|
std::string::size_type ss2;
|
|
if (std::string::npos != (ss2 = base.find_last_of("\\/"))) {
|
|
base.erase(ss2,base.length()-ss2);
|
|
}
|
|
else {
|
|
base = "";
|
|
// return;
|
|
}
|
|
|
|
// make sure the directory is terminated properly
|
|
char s;
|
|
|
|
if (base.length() == 0) {
|
|
base = ".";
|
|
base += getOsSeparator();
|
|
}
|
|
else if ((s = *(base.end()-1)) != '\\' && s != '/') {
|
|
base += getOsSeparator();
|
|
}
|
|
|
|
DefaultLogger::get()->info("Import root directory is \'" + base + "\'");
|
|
}
|
|
|
|
/** Destructor. */
|
|
~FileSystemFilter()
|
|
{
|
|
// haha
|
|
}
|
|
|
|
// -------------------------------------------------------------------
|
|
/** Tests for the existence of a file at the given path. */
|
|
bool Exists( const char* pFile) const
|
|
{
|
|
std::string tmp = pFile;
|
|
|
|
// Currently this IOSystem is also used to open THE ONE FILE.
|
|
if (tmp != src_file) {
|
|
BuildPath(tmp);
|
|
Cleanup(tmp);
|
|
}
|
|
|
|
return wrapped->Exists(tmp);
|
|
}
|
|
|
|
// -------------------------------------------------------------------
|
|
/** Returns the directory separator. */
|
|
char getOsSeparator() const
|
|
{
|
|
return sep;
|
|
}
|
|
|
|
// -------------------------------------------------------------------
|
|
/** Open a new file with a given path. */
|
|
IOStream* Open( const char* pFile, const char* pMode = "rb")
|
|
{
|
|
ai_assert(pFile);
|
|
ai_assert(pMode);
|
|
|
|
// First try the unchanged path
|
|
IOStream* s = wrapped->Open(pFile,pMode);
|
|
|
|
if (!s) {
|
|
std::string tmp = pFile;
|
|
|
|
// Try to convert between absolute and relative paths
|
|
BuildPath(tmp);
|
|
s = wrapped->Open(tmp,pMode);
|
|
|
|
if (!s) {
|
|
// Finally, look for typical issues with paths
|
|
// and try to correct them. This is our last
|
|
// resort.
|
|
tmp = pFile;
|
|
Cleanup(tmp);
|
|
BuildPath(tmp);
|
|
s = wrapped->Open(tmp,pMode);
|
|
}
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
// -------------------------------------------------------------------
|
|
/** Closes the given file and releases all resources associated with it. */
|
|
void Close( IOStream* pFile)
|
|
{
|
|
return wrapped->Close(pFile);
|
|
}
|
|
|
|
// -------------------------------------------------------------------
|
|
/** Compare two paths */
|
|
bool ComparePaths (const char* one, const char* second) const
|
|
{
|
|
return wrapped->ComparePaths (one,second);
|
|
}
|
|
|
|
// -------------------------------------------------------------------
|
|
/** Pushes a new directory onto the directory stack. */
|
|
bool PushDirectory(const std::string &path)
|
|
{
|
|
return wrapped->PushDirectory(path);
|
|
}
|
|
|
|
// -------------------------------------------------------------------
|
|
/** Returns the top directory from the stack. */
|
|
const std::string &CurrentDirectory() const
|
|
{
|
|
return wrapped->CurrentDirectory();
|
|
}
|
|
|
|
// -------------------------------------------------------------------
|
|
/** Returns the number of directories stored on the stack. */
|
|
size_t StackSize() const
|
|
{
|
|
return wrapped->StackSize();
|
|
}
|
|
|
|
// -------------------------------------------------------------------
|
|
/** Pops the top directory from the stack. */
|
|
bool PopDirectory()
|
|
{
|
|
return wrapped->PopDirectory();
|
|
}
|
|
|
|
// -------------------------------------------------------------------
|
|
/** Creates an new directory at the given path. */
|
|
bool CreateDirectory(const std::string &path)
|
|
{
|
|
return wrapped->CreateDirectory(path);
|
|
}
|
|
|
|
// -------------------------------------------------------------------
|
|
/** Will change the current directory to the given path. */
|
|
bool ChangeDirectory(const std::string &path)
|
|
{
|
|
return wrapped->ChangeDirectory(path);
|
|
}
|
|
|
|
// -------------------------------------------------------------------
|
|
/** Delete file. */
|
|
bool DeleteFile(const std::string &file)
|
|
{
|
|
return wrapped->DeleteFile(file);
|
|
}
|
|
|
|
private:
|
|
|
|
// -------------------------------------------------------------------
|
|
/** Build a valid path from a given relative or absolute path.
|
|
*/
|
|
void BuildPath (std::string& in) const
|
|
{
|
|
// if we can already access the file, great.
|
|
if (in.length() < 3 || wrapped->Exists(in)) {
|
|
return;
|
|
}
|
|
|
|
// Determine whether this is a relative path (Windows-specific - most assets are packaged on Windows).
|
|
if (in[1] != ':') {
|
|
|
|
// append base path and try
|
|
const std::string tmp = base + in;
|
|
if (wrapped->Exists(tmp)) {
|
|
in = tmp;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Chop of the file name and look in the model directory, if
|
|
// this fails try all sub paths of the given path, i.e.
|
|
// if the given path is foo/bar/something.lwo, try
|
|
// <base>/something.lwo
|
|
// <base>/bar/something.lwo
|
|
// <base>/foo/bar/something.lwo
|
|
std::string::size_type pos = in.rfind('/');
|
|
if (std::string::npos == pos) {
|
|
pos = in.rfind('\\');
|
|
}
|
|
|
|
if (std::string::npos != pos) {
|
|
std::string tmp;
|
|
std::string::size_type last_dirsep = std::string::npos;
|
|
|
|
while(true) {
|
|
tmp = base;
|
|
tmp += sep;
|
|
|
|
std::string::size_type dirsep = in.rfind('/', last_dirsep);
|
|
if (std::string::npos == dirsep) {
|
|
dirsep = in.rfind('\\', last_dirsep);
|
|
}
|
|
|
|
if (std::string::npos == dirsep || dirsep == 0) {
|
|
// we did try this already.
|
|
break;
|
|
}
|
|
|
|
last_dirsep = dirsep-1;
|
|
|
|
tmp += in.substr(dirsep+1, in.length()-pos);
|
|
if (wrapped->Exists(tmp)) {
|
|
in = tmp;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// hopefully the underlying file system has another few tricks to access this file ...
|
|
}
|
|
|
|
// -------------------------------------------------------------------
|
|
/** Cleanup the given path
|
|
*/
|
|
void Cleanup (std::string& in) const
|
|
{
|
|
char last = 0;
|
|
if(in.empty()) {
|
|
return;
|
|
}
|
|
|
|
// Remove a very common issue when we're parsing file names: spaces at the
|
|
// beginning of the path.
|
|
std::string::iterator it = in.begin();
|
|
while (IsSpaceOrNewLine( *it ))++it;
|
|
if (it != in.begin()) {
|
|
in.erase(in.begin(),it+1);
|
|
}
|
|
|
|
const char sep = getOsSeparator();
|
|
for (it = in.begin(); it != in.end(); ++it) {
|
|
// Exclude :// and \\, which remain untouched.
|
|
// https://sourceforge.net/tracker/?func=detail&aid=3031725&group_id=226462&atid=1067632
|
|
if ( !strncmp(&*it, "://", 3 )) {
|
|
it += 3;
|
|
continue;
|
|
}
|
|
if (it == in.begin() && !strncmp(&*it, "\\\\", 2)) {
|
|
it += 2;
|
|
continue;
|
|
}
|
|
|
|
// Cleanup path delimiters
|
|
if (*it == '/' || (*it) == '\\') {
|
|
*it = sep;
|
|
|
|
// And we're removing double delimiters, frequent issue with
|
|
// incorrectly composited paths ...
|
|
if (last == *it) {
|
|
it = in.erase(it);
|
|
--it;
|
|
}
|
|
}
|
|
else if (*it == '%' && in.end() - it > 2) {
|
|
|
|
// Hex sequence in URIs
|
|
if( IsHex((&*it)[0]) && IsHex((&*it)[1]) ) {
|
|
*it = HexOctetToDecimal(&*it);
|
|
it = in.erase(it+1,it+2);
|
|
--it;
|
|
}
|
|
}
|
|
|
|
last = *it;
|
|
}
|
|
}
|
|
|
|
private:
|
|
IOSystem* wrapped;
|
|
std::string src_file, base;
|
|
char sep;
|
|
};
|
|
|
|
} //!ns Assimp
|
|
|
|
#endif //AI_DEFAULTIOSYSTEM_H_INC
|