#include "IrrCompileConfig.h"
#include "CFileSystem.h"
#include "IReadFile.h"
#include "IWriteFile.h"
#include "CZipReader.h"
#include "CMountPointReader.h"
#include "CPakReader.h"
#include "CNPKReader.h"
#include "CTarReader.h"
#include "CWADReader.h"
#include "CFileList.h"
#include "CXMLReader.h"
#include "CXMLWriter.h"
#include "stdio.h"
#include "os.h"
#include "CAttributes.h"
#include "CMemoryFile.h"
#include "CLimitReadFile.h"
#include "irrList.h"
#if defined (_IRR_WINDOWS_API_)
#if !defined ( _WIN32_WCE )
#include <direct.h>
#include <io.h>
#include <tchar.h>
#endif
#else
#if (defined(_IRR_POSIX_API_) || defined(_IRR_OSX_PLATFORM_))
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
#endif
#endif
namespace irr
{
namespace io
{
CFileSystem::CFileSystem()
{
#ifdef _DEBUG
setDebugName("CFileSystem");
#endif
setFileListSystem(FILESYSTEM_NATIVE);
getWorkingDirectory();
#ifdef __IRR_COMPILE_WITH_PAK_ARCHIVE_LOADER_
ArchiveLoader.push_back(new CArchiveLoaderPAK(this));
#endif
#ifdef __IRR_COMPILE_WITH_NPK_ARCHIVE_LOADER_
ArchiveLoader.push_back(new CArchiveLoaderNPK(this));
#endif
#ifdef __IRR_COMPILE_WITH_TAR_ARCHIVE_LOADER_
ArchiveLoader.push_back(new CArchiveLoaderTAR(this));
#endif
#ifdef __IRR_COMPILE_WITH_WAD_ARCHIVE_LOADER_
ArchiveLoader.push_back(new CArchiveLoaderWAD(this));
#endif
#ifdef __IRR_COMPILE_WITH_MOUNT_ARCHIVE_LOADER_
ArchiveLoader.push_back(new CArchiveLoaderMount(this));
#endif
#ifdef __IRR_COMPILE_WITH_ZIP_ARCHIVE_LOADER_
ArchiveLoader.push_back(new CArchiveLoaderZIP(this));
#endif
}
CFileSystem::~CFileSystem()
{
u32 i;
for ( i=0; i < FileArchives.size(); ++i)
{
FileArchives[i]->drop();
}
for ( i=0; i < ArchiveLoader.size(); ++i)
{
ArchiveLoader[i]->drop();
}
}
IReadFile* CFileSystem::createAndOpenFile(const io::path& filename)
{
IReadFile* file = 0;
u32 i;
for (i=0; i< FileArchives.size(); ++i)
{
file = FileArchives[i]->createAndOpenFile(filename);
if (file)
return file;
}
return createReadFile(getAbsolutePath(filename));
}
IReadFile* CFileSystem::createMemoryReadFile(void* memory, s32 len,
const io::path& fileName, bool deleteMemoryWhenDropped)
{
if (!memory)
return 0;
else
return new CMemoryFile(memory, len, fileName, deleteMemoryWhenDropped);
}
IReadFile* CFileSystem::createLimitReadFile(const io::path& fileName,
IReadFile* alreadyOpenedFile, long pos, long areaSize)
{
if (!alreadyOpenedFile)
return 0;
else
return new CLimitReadFile(alreadyOpenedFile, pos, areaSize, fileName);
}
IWriteFile* CFileSystem::createMemoryWriteFile(void* memory, s32 len,
const io::path& fileName, bool deleteMemoryWhenDropped)
{
if (!memory)
return 0;
else
return new CMemoryFile(memory, len, fileName, deleteMemoryWhenDropped);
}
IWriteFile* CFileSystem::createAndWriteFile(const io::path& filename, bool append)
{
return createWriteFile(filename, append);
}
void CFileSystem::addArchiveLoader(IArchiveLoader* loader)
{
if (!loader)
return;
loader->grab();
ArchiveLoader.push_back(loader);
}
u32 CFileSystem::getArchiveLoaderCount() const
{
return ArchiveLoader.size();
}
IArchiveLoader* CFileSystem::getArchiveLoader(u32 index) const
{
if (index < ArchiveLoader.size())
return ArchiveLoader[index];
else
return 0;
}
bool CFileSystem::moveFileArchive(u32 sourceIndex, s32 relative)
{
bool r = false;
const s32 dest = (s32) sourceIndex + relative;
const s32 dir = relative < 0 ? -1 : 1;
const s32 sourceEnd = ((s32) FileArchives.size() ) - 1;
IFileArchive *t;
for (s32 s = (s32) sourceIndex;s != dest; s += dir)
{
if (s < 0 || s > sourceEnd || s + dir < 0 || s + dir > sourceEnd)
continue;
t = FileArchives[s + dir];
FileArchives[s + dir] = FileArchives[s];
FileArchives[s] = t;
r = true;
}
return r;
}
bool CFileSystem::addFileArchive(const io::path& filename, bool ignoreCase,
bool ignorePaths, E_FILE_ARCHIVE_TYPE archiveType,
const core::stringc& password)
{
IFileArchive* archive = 0;
bool ret = false;
for (u32 idx = 0; idx < FileArchives.size(); ++idx)
{
const core::stringc absPath = getAbsolutePath(filename);
const core::stringc arcPath = FileArchives[idx]->getFileList()->getPath();
if ((absPath == arcPath) || ((absPath+"/") == arcPath))
{
if (password.size())
FileArchives[idx]->Password=password;
return true;
}
}
s32 i;
if (archiveType == EFAT_UNKNOWN || archiveType == EFAT_FOLDER)
{
for (i = ArchiveLoader.size()-1; i >=0 ; --i)
{
if (ArchiveLoader[i]->isALoadableFileFormat(filename))
{
archive = ArchiveLoader[i]->createArchive(filename, ignoreCase, ignorePaths);
if (archive)
break;
}
}
if (!archive)
{
io::IReadFile* file = createAndOpenFile(filename);
if (file)
{
for (i = ArchiveLoader.size()-1; i >= 0; --i)
{
file->seek(0);
if (ArchiveLoader[i]->isALoadableFileFormat(file))
{
file->seek(0);
archive = ArchiveLoader[i]->createArchive(file, ignoreCase, ignorePaths);
if (archive)
break;
}
}
file->drop();
}
}
}
else
{
io::IReadFile* file = 0;
for (i = ArchiveLoader.size()-1; i >= 0; --i)
{
if (ArchiveLoader[i]->isALoadableFileFormat(archiveType))
{
if (!file)
file = createAndOpenFile(filename);
if (file)
{
file->seek(0);
if (ArchiveLoader[i]->isALoadableFileFormat(file))
{
file->seek(0);
archive = ArchiveLoader[i]->createArchive(file, ignoreCase, ignorePaths);
if (archive)
break;
}
}
else
{
break;
}
}
}
if (file)
file->drop();
}
if (archive)
{
FileArchives.push_back(archive);
if (password.size())
archive->Password=password;
ret = true;
}
else
{
os::Printer::log("Could not create archive for", filename, ELL_ERROR);
}
_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
return ret;
}
bool CFileSystem::removeFileArchive(u32 index)
{
bool ret = false;
if (index < FileArchives.size())
{
FileArchives[index]->drop();
FileArchives.erase(index);
ret = true;
}
_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
return ret;
}
bool CFileSystem::removeFileArchive(const io::path& filename)
{
for (u32 i=0; i < FileArchives.size(); ++i)
{
if (filename == FileArchives[i]->getFileList()->getPath())
return removeFileArchive(i);
}
_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
return false;
}
u32 CFileSystem::getFileArchiveCount() const
{
return FileArchives.size();
}
IFileArchive* CFileSystem::getFileArchive(u32 index)
{
return index < getFileArchiveCount() ? FileArchives[index] : 0;
}
const io::path& CFileSystem::getWorkingDirectory()
{
EFileSystemType type = FileSystemType;
if (type != FILESYSTEM_NATIVE)
{
type = FILESYSTEM_VIRTUAL;
}
else
{
#if defined(_IRR_WINDOWS_CE_PLATFORM_)
#elif defined(_IRR_WINDOWS_API_)
fschar_t tmp[_MAX_PATH];
#if defined(_IRR_WCHAR_FILESYSTEM )
_wgetcwd(tmp, _MAX_PATH);
WorkingDirectory[FILESYSTEM_NATIVE] = tmp;
WorkingDirectory[FILESYSTEM_NATIVE].replace(L'\\', L'/');
#else
_getcwd(tmp, _MAX_PATH);
WorkingDirectory[FILESYSTEM_NATIVE] = tmp;
WorkingDirectory[FILESYSTEM_NATIVE].replace('\\', '/');
#endif
#endif
#if (defined(_IRR_POSIX_API_) || defined(_IRR_OSX_PLATFORM_))
#if defined(_IRR_WCHAR_FILESYSTEM )
u32 pathSize=256;
wchar_t *tmpPath = new wchar_t[pathSize];
while ((pathSize < (1<<16)) && !(wgetcwd(tmpPath,pathSize)))
{
delete [] tmpPath;
pathSize *= 2;
tmpPath = new char[pathSize];
}
if (tmpPath)
{
WorkingDirectory[FILESYSTEM_NATIVE] = tmpPath;
delete [] tmpPath;
}
#else
u32 pathSize=256;
char *tmpPath = new char[pathSize];
while ((pathSize < (1<<16)) && !(getcwd(tmpPath,pathSize)))
{
delete [] tmpPath;
pathSize *= 2;
tmpPath = new char[pathSize];
}
if (tmpPath)
{
WorkingDirectory[FILESYSTEM_NATIVE] = tmpPath;
delete [] tmpPath;
}
#endif
#endif
WorkingDirectory[type].validate();
}
return WorkingDirectory[type];
}
bool CFileSystem::changeWorkingDirectoryTo(const io::path& newDirectory)
{
bool success=false;
if (FileSystemType != FILESYSTEM_NATIVE)
{
WorkingDirectory[FILESYSTEM_VIRTUAL] = newDirectory;
flattenFilename(WorkingDirectory[FILESYSTEM_VIRTUAL], "");
success = 1;
}
else
{
WorkingDirectory[FILESYSTEM_NATIVE] = newDirectory;
#if defined(_IRR_WINDOWS_CE_PLATFORM_)
success = true;
#elif defined(_MSC_VER)
#if defined(_IRR_WCHAR_FILESYSTEM)
success=(_wchdir(newDirectory.c_str()) == 0);
#else
success=(_chdir(newDirectory.c_str()) == 0);
#endif
#else
success=(chdir(newDirectory.c_str()) == 0);
#endif
}
return success;
}
io::path CFileSystem::getAbsolutePath(const io::path& filename) const
{
#if defined(_IRR_WINDOWS_CE_PLATFORM_)
return filename;
#elif defined(_IRR_WINDOWS_API_)
fschar_t *p=0;
fschar_t fpath[_MAX_PATH];
#if defined(_IRR_WCHAR_FILESYSTEM )
p = _wfullpath(fpath, filename.c_str(), _MAX_PATH);
core::stringw tmp(p);
tmp.replace(L'\\', L'/');
#else
p = _fullpath(fpath, filename.c_str(), _MAX_PATH);
core::stringc tmp(p);
tmp.replace('\\', '/');
#endif
return tmp;
#elif (defined(_IRR_POSIX_API_) || defined(_IRR_OSX_PLATFORM_))
c8* p=0;
c8 fpath[4096];
fpath[0]=0;
p = realpath(filename.c_str(), fpath);
if (!p)
{
if (!fpath[0])
{
io::path tmp(filename);
return flattenFilename(tmp);
}
else
return io::path(fpath);
}
if (filename[filename.size()-1]=='/')
return io::path(p)+"/";
else
return io::path(p);
#else
return io::path(filename);
#endif
}
io::path CFileSystem::getFileDir(const io::path& filename) const
{
s32 lastSlash = filename.findLast('/');
const s32 lastBackSlash = filename.findLast('\\');
lastSlash = lastSlash > lastBackSlash ? lastSlash : lastBackSlash;
if ((u32)lastSlash < filename.size())
return filename.subString(0, lastSlash);
else
return ".";
}
io::path CFileSystem::getFileBasename(const io::path& filename, bool keepExtension) const
{
s32 lastSlash = filename.findLast('/');
const s32 lastBackSlash = filename.findLast('\\');
lastSlash = core::max_(lastSlash, lastBackSlash);
s32 end = 0;
if (!keepExtension)
{
end = filename.findLast('.');
if (end == -1 || end < lastSlash)
end=0;
else
end = filename.size()-end;
}
if ((u32)lastSlash < filename.size())
return filename.subString(lastSlash+1, filename.size()-lastSlash-1-end);
else if (end != 0)
return filename.subString(0, filename.size()-end);
else
return filename;
}
io::path& CFileSystem::flattenFilename(io::path& directory, const io::path& root) const
{
directory.replace('\\', '/');
if (directory.lastChar() != '/')
directory.append('/');
io::path dir;
io::path subdir;
s32 lastpos = 0;
s32 pos = 0;
bool lastWasRealDir=false;
while ((pos = directory.findNext('/', lastpos)) >= 0)
{
subdir = directory.subString(lastpos, pos - lastpos + 1);
if (subdir == "../")
{
if (lastWasRealDir)
{
deletePathFromPath(dir, 2);
lastWasRealDir=(dir.size()!=0);
}
else
{
dir.append(subdir);
lastWasRealDir=false;
}
}
else if (subdir == "/")
{
dir = root;
}
else if (subdir != "./" )
{
dir.append(subdir);
lastWasRealDir=true;
}
lastpos = pos + 1;
}
directory = dir;
return directory;
}
path CFileSystem::getRelativeFilename(const path& filename, const path& directory) const
{
io::path path, file, ext;
core::splitFilename(getAbsolutePath(filename), &path, &file, &ext);
io::path path2(getAbsolutePath(directory));
core::list<io::path> list1, list2;
path.split(list1, "/\\", 2);
path2.split(list2, "/\\", 2);
u32 i=0;
core::list<io::path>::ConstIterator it1,it2;
it1=list1.begin();
it2=list2.begin();
for (; i<list1.size() && (*it1==*it2); ++i)
{
++it1;
++it2;
}
path="";
for (; i<list2.size(); ++i)
path += "../";
while (it1 != list1.end())
{
path += *it1++;
path += "/";
}
path += file;
if (ext.size())
{
path += ".";
path += ext;
}
return path;
}
EFileSystemType CFileSystem::setFileListSystem(EFileSystemType listType)
{
EFileSystemType current = FileSystemType;
FileSystemType = listType;
return current;
}
IFileList* CFileSystem::createFileList()
{
CFileList* r = 0;
io::path Path = getWorkingDirectory();
Path.replace('\\', '/');
if (Path.lastChar() != '/')
Path.append('/');
if (FileSystemType == FILESYSTEM_NATIVE)
{
#ifdef _IRR_WINDOWS_API_
#if !defined ( _WIN32_WCE )
r = new CFileList(Path, true, false);
struct _tfinddata_t c_file;
long hFile;
if( (hFile = _tfindfirst( _T("*"), &c_file )) != -1L )
{
do
{
r->addItem(Path + c_file.name, 0, c_file.size, (_A_SUBDIR & c_file.attrib) != 0, 0);
}
while( _tfindnext( hFile, &c_file ) == 0 );
_findclose( hFile );
}
#endif
#endif
#if (defined(_IRR_POSIX_API_) || defined(_IRR_OSX_PLATFORM_))
r = new CFileList(Path, false, false);
r->addItem(Path + "..", 0, 0, true, 0);
DIR* dirHandle=opendir(Path.c_str());
if (dirHandle)
{
struct dirent *dirEntry;
while ((dirEntry=readdir(dirHandle)))
{
u32 size = 0;
bool isDirectory = false;
if((strcmp(dirEntry->d_name, ".")==0) ||
(strcmp(dirEntry->d_name, "..")==0))
{
continue;
}
struct stat buf;
if (stat(dirEntry->d_name, &buf)==0)
{
size = buf.st_size;
isDirectory = S_ISDIR(buf.st_mode);
}
#if !defined(_IRR_SOLARIS_PLATFORM_) && !defined(__CYGWIN__)
else
{
isDirectory = dirEntry->d_type == DT_DIR;
}
#endif
r->addItem(Path + dirEntry->d_name, 0, size, isDirectory, 0);
}
closedir(dirHandle);
}
#endif
}
else
{
r = new CFileList(Path, false, false);
SFileListEntry e2;
SFileListEntry e3;
r->addItem(Path + ".", 0, 0, true, 0);
r->addItem(Path + "..", 0, 0, true, 0);
for (u32 i=0; i < FileArchives.size(); ++i)
{
const IFileList *merge = FileArchives[i]->getFileList();
for (u32 j=0; j < merge->getFileCount(); ++j)
{
if (core::isInSameDirectory(Path, merge->getFullFileName(j)) == 0)
{
r->addItem(merge->getFullFileName(j), merge->getFileOffset(j), merge->getFileSize(j), merge->isDirectory(j), 0);
}
}
}
}
if (r)
r->sort();
return r;
}
IFileList* CFileSystem::createEmptyFileList(const io::path& path, bool ignoreCase, bool ignorePaths)
{
return new CFileList(path, ignoreCase, ignorePaths);
}
bool CFileSystem::existFile(const io::path& filename) const
{
for (u32 i=0; i < FileArchives.size(); ++i)
if (FileArchives[i]->getFileList()->findFile(filename)!=-1)
return true;
#if defined(_IRR_WINDOWS_CE_PLATFORM_)
#if defined(_IRR_WCHAR_FILESYSTEM)
HANDLE hFile = CreateFileW(filename.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
#else
HANDLE hFile = CreateFileW(core::stringw(filename).c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
#endif
if (hFile == INVALID_HANDLE_VALUE)
return false;
else
{
CloseHandle(hFile);
return true;
}
#else
_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
#if defined(_MSC_VER)
#if defined(_IRR_WCHAR_FILESYSTEM)
return (_waccess(filename.c_str(), 0) != -1);
#else
return (_access(filename.c_str(), 0) != -1);
#endif
#elif defined(F_OK)
return (access(filename.c_str(), F_OK) != -1);
#else
return (access(filename.c_str(), 0) != -1);
#endif
#endif
}
IXMLReader* CFileSystem::createXMLReader(const io::path& filename)
{
IReadFile* file = createAndOpenFile(filename);
if (!file)
return 0;
IXMLReader* reader = createXMLReader(file);
file->drop();
return reader;
}
IXMLReader* CFileSystem::createXMLReader(IReadFile* file)
{
if (!file)
return 0;
return createIXMLReader(file);
}
IXMLReaderUTF8* CFileSystem::createXMLReaderUTF8(const io::path& filename)
{
IReadFile* file = createAndOpenFile(filename);
if (!file)
return 0;
IXMLReaderUTF8* reader = createIXMLReaderUTF8(file);
file->drop();
return reader;
}
IXMLReaderUTF8* CFileSystem::createXMLReaderUTF8(IReadFile* file)
{
if (!file)
return 0;
return createIXMLReaderUTF8(file);
}
IXMLWriter* CFileSystem::createXMLWriter(const io::path& filename)
{
IWriteFile* file = createAndWriteFile(filename);
IXMLWriter* writer = 0;
if (file)
{
writer = createXMLWriter(file);
file->drop();
}
return writer;
}
IXMLWriter* CFileSystem::createXMLWriter(IWriteFile* file)
{
return new CXMLWriter(file);
}
IFileSystem* createFileSystem()
{
return new CFileSystem();
}
IAttributes* CFileSystem::createEmptyAttributes(video::IVideoDriver* driver)
{
return new CAttributes(driver);
}
}
}