306 lines
8.6 KiB
C++
306 lines
8.6 KiB
C++
/*
|
|
Copyright (C) 2005-2006 Feeling Software Inc.
|
|
MIT License: http://www.opensource.org/licenses/mit-license.php
|
|
*/
|
|
/*
|
|
Based on the FS Import classes:
|
|
Copyright (C) 2005-2006 Feeling Software Inc
|
|
Copyright (C) 2005-2006 Autodesk Media Entertainment
|
|
MIT License: http://www.opensource.org/licenses/mit-license.php
|
|
*/
|
|
|
|
#include "StdAfx.h"
|
|
#include "FUtils/FUFile.h"
|
|
#include "FUtils/FUFileManager.h"
|
|
#include "FUtils/FUStringConversion.h"
|
|
|
|
#if defined(WIN32)
|
|
#include <direct.h>
|
|
#endif
|
|
|
|
FUFileManager::FUFileManager()
|
|
{
|
|
// Push on the stack the original root path
|
|
char fullPath[MAX_PATH];
|
|
getcwd(fullPath, MAX_PATH);
|
|
pathStack.push_back(TO_FSTRING(fullPath));
|
|
}
|
|
|
|
FUFileManager::~FUFileManager()
|
|
{
|
|
}
|
|
|
|
// Set a new root path
|
|
void FUFileManager::PushRootPath(const fstring& path)
|
|
{
|
|
// Ensure that the new root path is an absolute root path.
|
|
fstring absolutePath = MakeFilePathAbsolute(path);
|
|
pathStack.push_back(absolutePath);
|
|
fchdir(path.c_str());
|
|
}
|
|
|
|
// Go back to the previous root path
|
|
void FUFileManager::PopRootPath()
|
|
{
|
|
if (pathStack.size() > 1)
|
|
{
|
|
pathStack.pop_back();
|
|
fchdir(pathStack.back().c_str());
|
|
}
|
|
}
|
|
|
|
// Set the current path root, using a known filename
|
|
void FUFileManager::PushRootFile(const fstring& filename)
|
|
{
|
|
// Strip the filename of the actual file's name
|
|
fstring path = StripFileFromPath(filename);
|
|
PushRootPath(path);
|
|
}
|
|
|
|
void FUFileManager::PopRootFile()
|
|
{
|
|
PopRootPath();
|
|
}
|
|
|
|
// Open a file to read
|
|
FUFile* FUFileManager::OpenFile(const fstring& filename, bool write)
|
|
{
|
|
fstring absoluteFilename = MakeFilePathAbsolute(filename);
|
|
string dumpFilename = FUStringConversion::ToString(absoluteFilename);
|
|
return new FUFile(dumpFilename.c_str(), write ? FUFile::WRITE : FUFile::READ);
|
|
}
|
|
|
|
// Massage a given filename to be absolute
|
|
fstring FUFileManager::GetFilePath(const fstring& fileURL)
|
|
{
|
|
// Strip any prefix
|
|
fstring filename = fileURL;
|
|
std::replace(filename.begin(), filename.end(), '\\', '/');
|
|
if (filename.size() > 7 && filename.substr(0, 7) == FC("file://"))
|
|
{
|
|
filename = filename.c_str() + 7;
|
|
|
|
if (filename.size() > 3 && filename[0] == '/' && (filename[2] == ':' || filename[2] == '|'))
|
|
{
|
|
filename = filename.c_str() + 1;
|
|
}
|
|
|
|
if (filename[1] == '|') filename[1] = ':';
|
|
}
|
|
|
|
// Replace any '%' character string into the wanted characters: %20 is common.
|
|
for (size_t pos = filename.find('%'); pos != fstring::npos; pos = filename.find('%'))
|
|
{
|
|
const fchar* pc = filename.c_str() + pos + 1; // +1 to skip/erase the '%' character
|
|
uint32 value = FUStringConversion::HexToUInt32(&pc, 2);
|
|
size_t count = (pc - filename.c_str()) - pos;
|
|
filename.erase(pos, count);
|
|
filename.insert(pos, 1, (fchar) value);
|
|
}
|
|
|
|
return MakeFilePathAbsolute(filename);
|
|
}
|
|
|
|
// Transform a file path into a file URL
|
|
fstring FUFileManager::GetFileURL(const fstring& filepath, bool relative)
|
|
{
|
|
fstring url;
|
|
if (relative)
|
|
{
|
|
url = MakeFilePathRelative(filepath);
|
|
if (!url.empty() && url[0] != '.')
|
|
{
|
|
// Unable to make the path relative, so return an absolute path
|
|
relative = false;
|
|
}
|
|
}
|
|
|
|
if (!relative)
|
|
{
|
|
// Transform into an absolute file path
|
|
fstring url = MakeFilePathAbsolute(filepath);
|
|
std::replace(url.begin(), url.end(), ':', '|');
|
|
std::replace(url.begin(), url.end(), '\\', '/');
|
|
url = fstring(FC("file://")) + url;
|
|
}
|
|
|
|
// Remove any invalid character(s) using the %X guideline
|
|
/*for (size_t p = 0; p < url.size(); ++p)
|
|
{
|
|
fchar c = url[p];
|
|
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') ||
|
|
c == '.' || c == '|' || c == '/' || c == '-' || c == '_')
|
|
{
|
|
// Valid characters
|
|
}
|
|
else
|
|
{
|
|
url.erase(p, 1);
|
|
globalBuilder.set('%'); <---- NEED TO BE IN HEX
|
|
globalBuilder.append(c);
|
|
url.append(globalBuilder.ToCharPtr());
|
|
}
|
|
}*/
|
|
|
|
return url;
|
|
}
|
|
|
|
// Strip a full filename of its filename, returning the path
|
|
fstring FUFileManager::StripFileFromPath(const fstring& filename)
|
|
{
|
|
fchar fullPath[MAX_PATH + 1];
|
|
fstrncpy(fullPath, filename.c_str(), MAX_PATH);
|
|
fullPath[MAX_PATH] = 0;
|
|
fchar* lastSlash = fstrrchr(fullPath, FC('/'));
|
|
fchar* lastBackslash = fstrrchr(fullPath, FC('\\'));
|
|
lastSlash = max(lastSlash, lastBackslash);
|
|
if (lastSlash != NULL) *lastSlash = 0;
|
|
return fstring(fullPath);
|
|
}
|
|
|
|
// Extract the file extension out of a filename
|
|
fstring FUFileManager::GetFileExtension(const fstring& _filename)
|
|
{
|
|
fchar filename[MAX_PATH];
|
|
fstrncpy(filename, _filename.c_str(), MAX_PATH);
|
|
filename[MAX_PATH - 1] = 0;
|
|
|
|
fchar* lastPeriod = fstrrchr(filename, '.');
|
|
if (lastPeriod == NULL) return fstring();
|
|
|
|
fchar* lastSlash = fstrrchr(filename, '/');
|
|
fchar* lastBackslash = fstrrchr(filename, '\\');
|
|
lastSlash = max(lastSlash, lastBackslash);
|
|
if (lastSlash > lastPeriod) return fstring();
|
|
|
|
fstrlower(lastPeriod + 1);
|
|
return fstring(lastPeriod + 1);
|
|
}
|
|
|
|
// For a relative path, extract the list of the individual paths that must be traversed to get to the file.
|
|
void FUFileManager::ExtractPathStack(const fstring& name, FStringList& list, bool includeFilename)
|
|
{
|
|
list.clear();
|
|
list.reserve(6);
|
|
|
|
fstring split = name;
|
|
while (split.length() > 0)
|
|
{
|
|
// Extract out the next path
|
|
size_t slashIndex = split.find_first_of('/');
|
|
size_t bslashIndex = split.find_first_of('\\');
|
|
size_t pathSeparator;
|
|
if (slashIndex != fstring::npos && bslashIndex != fstring::npos) pathSeparator = min(slashIndex, bslashIndex);
|
|
else if (bslashIndex != fstring::npos) pathSeparator = bslashIndex;
|
|
else if (slashIndex != fstring::npos) pathSeparator = slashIndex;
|
|
else
|
|
{
|
|
if (includeFilename) list.push_back(split);
|
|
break;
|
|
}
|
|
|
|
list.push_back(split.substr(0, pathSeparator));
|
|
split = split.substr(pathSeparator + 1, split.length() - pathSeparator - 1);
|
|
}
|
|
}
|
|
|
|
// Make a file path relative/absolute
|
|
fstring FUFileManager::MakeFilePathAbsolute(const fstring& _filePath)
|
|
{
|
|
fstring filePath = _filePath;
|
|
if ((filePath.size() > 1 && (filePath[0] == '/' || filePath[0] == '\\')) ||
|
|
(filePath.size() > 2 && (filePath[1] == ':' || filePath[1] == '|')))
|
|
{
|
|
// Already an absolute filepath
|
|
}
|
|
else
|
|
{
|
|
// Relative file path.
|
|
FStringList documentPaths, localPaths;
|
|
ExtractPathStack(pathStack.back(), documentPaths, true);
|
|
ExtractPathStack(filePath, localPaths, true);
|
|
for (FStringList::iterator it = localPaths.begin(); it != localPaths.end(); ++it)
|
|
{
|
|
// Look for special relative path tokens: '.' and '..'
|
|
if ((*it) == FC(".")) {} // do nothing
|
|
else if ((*it) == FC("..")) { documentPaths.pop_back(); } // pop one path out
|
|
else { documentPaths.push_back(*it); } // traverse this path
|
|
}
|
|
|
|
// Recreate the absolute filename
|
|
filePath.clear();
|
|
for (FStringList::iterator it = documentPaths.begin(); it != documentPaths.end(); ++it)
|
|
{
|
|
if (!filePath.empty()) filePath.push_back('/');
|
|
filePath += (*it);
|
|
}
|
|
|
|
if (filePath.size() < 2 || (filePath[1] != ':' && filePath[1] != '|'))
|
|
{
|
|
filePath.insert(0, '/');
|
|
}
|
|
}
|
|
|
|
#ifdef WIN32
|
|
std::replace(filePath.begin(), filePath.end(), '/', '\\');
|
|
#endif // WIN32
|
|
|
|
return filePath;
|
|
}
|
|
|
|
fstring FUFileManager::MakeFilePathRelative(const fstring& _filePath)
|
|
{
|
|
fstring filePath = _filePath;
|
|
if (!filePath.empty() && filePath[0] != '.')
|
|
{
|
|
// First, ensure we have an absolute file path
|
|
filePath = MakeFilePathAbsolute(_filePath);
|
|
|
|
// Relative file path.
|
|
FStringList documentPaths, localPaths;
|
|
ExtractPathStack(pathStack.back(), documentPaths, true);
|
|
ExtractPathStack(filePath, localPaths, true);
|
|
|
|
// Extract the filename from the path stack
|
|
fstring filename = localPaths.back();
|
|
localPaths.pop_back();
|
|
|
|
// Look for commonality in the path stacks
|
|
size_t documentPathCount = documentPaths.size();
|
|
size_t filePathCount = localPaths.size();
|
|
size_t matchIndex = 0;
|
|
for (; matchIndex < filePathCount && matchIndex < documentPathCount; ++matchIndex)
|
|
{
|
|
if (fstricmp(documentPaths[matchIndex].c_str(), localPaths[matchIndex].c_str()) != 0) break;
|
|
}
|
|
|
|
if (matchIndex != 0)
|
|
{
|
|
// There is some similar part, so generate the relative filename
|
|
fstring relativePath;
|
|
|
|
if (documentPathCount > matchIndex)
|
|
{
|
|
// Backtrack the document's path
|
|
for (size_t i = matchIndex; i < documentPathCount; ++i)
|
|
{
|
|
relativePath += fstring(FC("../"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Start at the document's root folder
|
|
relativePath = FC("./");
|
|
}
|
|
|
|
// Add the file's relative path
|
|
for (size_t i = matchIndex; i < filePathCount; ++i)
|
|
{
|
|
relativePath += filePath[i] + fstring(FC("/"));
|
|
}
|
|
filePath = relativePath + filename;
|
|
}
|
|
}
|
|
return filePath;
|
|
}
|