614 lines
14 KiB
C++
614 lines
14 KiB
C++
// Copyright (C) 2002-2009 Nikolaus Gebhardt
|
|
// This file is part of the "Irrlicht Engine".
|
|
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
|
|
|
#include "IrrCompileConfig.h"
|
|
|
|
#include "CFileSystem.h"
|
|
#include "IReadFile.h"
|
|
#include "IWriteFile.h"
|
|
#include "CZipReader.h"
|
|
#include "CPakReader.h"
|
|
#include "CFileList.h"
|
|
#include "CXMLReader.h"
|
|
#include "CXMLWriter.h"
|
|
#include "stdio.h"
|
|
#include "os.h"
|
|
#include "CAttributes.h"
|
|
#include "CMemoryFile.h"
|
|
|
|
#if defined (_IRR_WINDOWS_API_)
|
|
#if !defined ( _WIN32_WCE )
|
|
#include <direct.h> // for _chdir
|
|
#endif
|
|
#else
|
|
#include <unistd.h>
|
|
#include <limits.h>
|
|
#include <stdlib.h>
|
|
#endif
|
|
|
|
namespace irr
|
|
{
|
|
namespace io
|
|
{
|
|
|
|
|
|
|
|
|
|
//! constructor
|
|
CFileSystem::CFileSystem()
|
|
{
|
|
#ifdef _DEBUG
|
|
setDebugName("CFileSystem");
|
|
#endif
|
|
|
|
setFileListSystem ( FILESYSTEM_NATIVE );
|
|
|
|
addArchiveLoader ( new CArchiveLoaderZIP ( this ) );
|
|
addArchiveLoader ( new CArchiveLoaderMount ( this ) );
|
|
addArchiveLoader ( new CArchiveLoaderPAK ( this ) );
|
|
|
|
}
|
|
|
|
|
|
//! destructor
|
|
CFileSystem::~CFileSystem()
|
|
{
|
|
u32 i;
|
|
|
|
for ( i=0; i < FileArchive.size(); ++i)
|
|
{
|
|
FileArchive[i]->drop ();
|
|
}
|
|
|
|
for ( i=0; i < ArchiveLoader.size(); ++i)
|
|
{
|
|
ArchiveLoader[i]->drop ();
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//! opens a file for read access
|
|
IReadFile* CFileSystem::createAndOpenFile( const core::string<c16>& filename)
|
|
{
|
|
IReadFile* file = 0;
|
|
u32 i;
|
|
|
|
for ( i=0; i< FileArchive.size(); ++i)
|
|
{
|
|
file = FileArchive[i]->openFile(filename);
|
|
if (file)
|
|
return file;
|
|
}
|
|
|
|
// Create the file using an absolute path so that it matches
|
|
// the scheme used by CNullDriver::getTexture().
|
|
return createReadFile(getAbsolutePath(filename));
|
|
}
|
|
|
|
|
|
//! Creates an IReadFile interface for treating memory like a file.
|
|
IReadFile* CFileSystem::createMemoryReadFile(void* memory, s32 len,
|
|
const core::string<c16>& fileName, bool deleteMemoryWhenDropped)
|
|
{
|
|
if (!memory)
|
|
return 0;
|
|
else
|
|
return new CMemoryFile(memory, len, fileName, deleteMemoryWhenDropped);
|
|
}
|
|
|
|
//! Creates an IReadFile interface for treating memory like a file.
|
|
IWriteFile* CFileSystem::createMemoryWriteFile(void* memory, s32 len,
|
|
const core::string<c16>& fileName, bool deleteMemoryWhenDropped)
|
|
{
|
|
if (!memory)
|
|
return 0;
|
|
else
|
|
return new CMemoryFile(memory, len, fileName, deleteMemoryWhenDropped);
|
|
}
|
|
|
|
|
|
//! Opens a file for write access.
|
|
IWriteFile* CFileSystem::createAndWriteFile(const core::string<c16>& filename, bool append)
|
|
{
|
|
return createWriteFile(filename, append);
|
|
}
|
|
|
|
|
|
|
|
//! Adds an external archive loader to the engine.
|
|
void CFileSystem::addArchiveLoader(IArchiveLoader* loader)
|
|
{
|
|
if (!loader)
|
|
return;
|
|
|
|
//loader->grab();
|
|
ArchiveLoader.push_back ( loader );
|
|
}
|
|
|
|
|
|
//! Adds an archive to the file system.
|
|
bool CFileSystem::registerFileArchive( const core::string<c16>& filename, bool ignoreCase, bool ignorePaths, s32 index )
|
|
{
|
|
IFileArchive* archive = 0;
|
|
bool ret = false;
|
|
u32 i;
|
|
|
|
// try to load arhive based on file name
|
|
for ( i = 0; i < ArchiveLoader.size(); ++i)
|
|
{
|
|
if ( ArchiveLoader[i]->isALoadableFileFormat( filename ) )
|
|
{
|
|
archive = ArchiveLoader[i]->createArchive( filename, ignoreCase, ignorePaths );
|
|
if (archive)
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( archive )
|
|
{
|
|
if ( index < 0 )
|
|
FileArchive.push_back ( archive );
|
|
else
|
|
FileArchive.insert ( archive, index );
|
|
|
|
ret = true;
|
|
}
|
|
else
|
|
{
|
|
char buf[256];
|
|
|
|
core::stringw showName ( filename );
|
|
snprintf ( buf, sizeof (buf), "Could not create archive for: %ls\n", showName.c_str() );
|
|
os::Printer::log( buf ,ELL_ERROR);
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
//! removes an archive to the file system.
|
|
bool CFileSystem::unregisterFileArchive( u32 index )
|
|
{
|
|
bool ret = false;
|
|
if ( index < FileArchive.size () )
|
|
{
|
|
FileArchive[index]->drop();
|
|
FileArchive.erase ( index );
|
|
ret = true;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
//! gets an archive
|
|
u32 CFileSystem::getFileArchiveCount()
|
|
{
|
|
return FileArchive.size();
|
|
}
|
|
|
|
|
|
IFileArchive* CFileSystem::getFileArchive( u32 index )
|
|
{
|
|
return index < getFileArchiveCount () ? FileArchive [ index ] : 0;
|
|
}
|
|
|
|
|
|
//! Returns the string of the current working directory
|
|
const core::string<c16>& CFileSystem::getWorkingDirectory()
|
|
{
|
|
eFileSystemType type = FileSystemType;
|
|
|
|
if ( type != FILESYSTEM_NATIVE )
|
|
{
|
|
type = FILESYSTEM_VIRTUAL;
|
|
}
|
|
else
|
|
{
|
|
const s32 FILE_SYSTEM_MAX_PATH = 1024;
|
|
WorkingDirectory [ type ].reserve ( FILE_SYSTEM_MAX_PATH );
|
|
c16* r = (c16*) WorkingDirectory [ type ].c_str();
|
|
|
|
#if defined( _IRR_USE_WINDOWS_CE_DEVICE_)
|
|
#elif defined( _IRR_WINDOWS_API_)
|
|
#if defined ( _IRR_WCHAR_FILESYSTEM )
|
|
_wgetcwd ( r, FILE_SYSTEM_MAX_PATH );
|
|
#else
|
|
_getcwd(r, FILE_SYSTEM_MAX_PATH);
|
|
#endif
|
|
#endif
|
|
|
|
#if (defined(_IRR_POSIX_API_) || defined(_IRR_OSX_PLATFORM_))
|
|
|
|
#if defined ( _IRR_WCHAR_FILESYSTEM )
|
|
wgetcwd ( r, FILE_SYSTEM_MAX_PATH );
|
|
#else
|
|
getcwd(r, (size_t)FILE_SYSTEM_MAX_PATH);
|
|
#endif
|
|
#endif
|
|
|
|
WorkingDirectory [ type ].verify();
|
|
}
|
|
|
|
return WorkingDirectory [ type ];
|
|
}
|
|
|
|
|
|
//! Changes the current Working Directory to the given string.
|
|
bool CFileSystem::changeWorkingDirectoryTo(const core::string<c16>& newDirectory)
|
|
{
|
|
bool success=false;
|
|
|
|
if ( FileSystemType != FILESYSTEM_NATIVE )
|
|
{
|
|
WorkingDirectory [ FILESYSTEM_VIRTUAL ].append ( newDirectory );
|
|
flattenFilename ( WorkingDirectory [ FILESYSTEM_VIRTUAL ], "" );
|
|
success = 1;
|
|
}
|
|
else
|
|
{
|
|
WorkingDirectory [ FILESYSTEM_NATIVE ] = newDirectory;
|
|
|
|
#if defined( _IRR_USE_WINDOWS_CE_DEVICE_)
|
|
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;
|
|
}
|
|
|
|
|
|
core::string<c16> CFileSystem::getAbsolutePath(const core::string<c16>& filename) const
|
|
{
|
|
c16 *p=0;
|
|
|
|
#if defined( _IRR_USE_WINDOWS_CE_DEVICE_)
|
|
return filename;
|
|
#elif defined( _IRR_WINDOWS_API_)
|
|
|
|
#if defined ( _IRR_WCHAR_FILESYSTEM )
|
|
c16 fpath[_MAX_PATH];
|
|
p = _wfullpath( fpath, filename.c_str(), _MAX_PATH);
|
|
#else
|
|
c8 fpath[_MAX_PATH];
|
|
p = _fullpath( fpath, filename.c_str(), _MAX_PATH);
|
|
#endif
|
|
|
|
|
|
#elif (defined(_IRR_POSIX_API_) || defined(_IRR_OSX_PLATFORM_))
|
|
c8 fpath[4096];
|
|
fpath[0]=0;
|
|
p = realpath(filename.c_str(), fpath);
|
|
if (!p)
|
|
{
|
|
// content in fpath is undefined at this point
|
|
if ('0'==fpath[0]) // seems like fpath wasn't altered
|
|
{
|
|
// at least remove a ./ prefix
|
|
if ('.'==filename[0] && '/'==filename[1])
|
|
return filename.subString(2, filename.size()-2);
|
|
else
|
|
return filename;
|
|
}
|
|
else
|
|
return core::string<c16>(fpath);
|
|
}
|
|
|
|
#endif
|
|
|
|
return core::string<c16>(p);
|
|
}
|
|
|
|
|
|
//! returns the directory part of a filename, i.e. all until the first
|
|
//! slash or backslash, excluding it. If no directory path is prefixed, a '.'
|
|
//! is returned.
|
|
core::string<c16> CFileSystem::getFileDir(const core::string<c16>& filename) const
|
|
{
|
|
// find last forward or backslash
|
|
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 ".";
|
|
}
|
|
|
|
|
|
//! returns the base part of a filename, i.e. all except for the directory
|
|
//! part. If no directory path is prefixed, the full name is returned.
|
|
core::string<c16> CFileSystem::getFileBasename(const core::string<c16>& filename, bool keepExtension) const
|
|
{
|
|
// find last forward or backslash
|
|
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=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;
|
|
}
|
|
|
|
|
|
//! flaten a path and file name for example: "/you/me/../." becomes "/you"
|
|
core::string<c16>& CFileSystem::flattenFilename( core::string<c16>& directory, const core::string<c16>& root ) const
|
|
{
|
|
directory.replace ( '\\', '/' );
|
|
if ( lastChar ( directory ) != '/' )
|
|
directory.append ( '/' );
|
|
|
|
core::string<c16> dir;
|
|
core::string<c16> subdir;
|
|
|
|
s32 lastpos = 0;
|
|
s32 pos = 0;
|
|
|
|
while ( ( pos = directory.findNext ( '/', lastpos ) ) >= 0 )
|
|
{
|
|
subdir = directory.subString ( lastpos, pos - lastpos + 1 );
|
|
|
|
if ( subdir == "../" )
|
|
{
|
|
deletePathFromPath ( dir, 2 );
|
|
}
|
|
else
|
|
if ( subdir == "/" )
|
|
{
|
|
dir = root;
|
|
}
|
|
else
|
|
if ( subdir != "./" )
|
|
{
|
|
dir.append ( subdir );
|
|
}
|
|
|
|
lastpos = pos + 1;
|
|
}
|
|
directory = dir;
|
|
return directory;
|
|
}
|
|
|
|
//! Creates a list of files and directories in the current working directory
|
|
eFileSystemType CFileSystem::setFileListSystem( eFileSystemType listType)
|
|
{
|
|
eFileSystemType current = FileSystemType;
|
|
FileSystemType = listType;
|
|
return current;
|
|
}
|
|
|
|
|
|
//! Creates a list of files and directories in the current working directory
|
|
IFileList* CFileSystem::createFileList()
|
|
{
|
|
FileEntry e2;
|
|
FileEntry e3;
|
|
|
|
if ( FileSystemType == FILESYSTEM_NATIVE )
|
|
return new CFileList();
|
|
|
|
CFileList* r = new CFileList( "virtual" );
|
|
r->Path = WorkingDirectory [ FILESYSTEM_VIRTUAL ];
|
|
|
|
for ( u32 i = 0; i != FileArchive.size(); ++i)
|
|
{
|
|
CFileList* flist[2] = { 0, 0 };
|
|
|
|
//! merge relative folder file archives
|
|
if ( FileArchive[i]->getArchiveType() == "mount" )
|
|
{
|
|
eFileSystemType currentType = setFileListSystem ( FILESYSTEM_NATIVE );
|
|
|
|
core::string<c16> save ( getWorkingDirectory () );
|
|
core::string<c16> current;
|
|
|
|
current = FileArchive[i]->getArchiveName() + WorkingDirectory [ FILESYSTEM_VIRTUAL ];
|
|
flattenFilename ( current );
|
|
|
|
if ( changeWorkingDirectoryTo ( current ) )
|
|
{
|
|
flist[0] = new CFileList( "mountpoint" );
|
|
flist[0]->constructNative ();
|
|
changeWorkingDirectoryTo ( save );
|
|
}
|
|
|
|
setFileListSystem ( currentType );
|
|
}
|
|
else
|
|
if ( FileArchive[i]->getArchiveType() == "zip" )
|
|
{
|
|
flist[0] = new CFileList( "zip" );
|
|
flist[1] = new CFileList( "zip directory" );
|
|
|
|
// add relative navigation
|
|
e2.isDirectory = 1;
|
|
e2.Name = ".";
|
|
e2.FullName = r->Path + e2.Name;
|
|
e2.Size = 0;
|
|
flist[1]->Files.push_back ( e2 );
|
|
|
|
e2.Name = "..";
|
|
e2.FullName = r->Path + e2.Name;
|
|
flist[1]->Files.push_back ( e2 );
|
|
|
|
for ( u32 g = 0; g < FileArchive[i]->getFileCount(); ++g)
|
|
{
|
|
const SZipFileEntry *e = (SZipFileEntry*) FileArchive[i]->getFileInfo ( g );
|
|
s32 test = isInSameDirectory ( r->Path, e->zipFileName );
|
|
if ( test < 0 || test > 1 )
|
|
continue;
|
|
|
|
e2.Size = e->header.DataDescriptor.UncompressedSize;
|
|
e2.isDirectory = e2.Size == 0;
|
|
|
|
// check missing directories
|
|
if ( !e2.isDirectory && test == 1 )
|
|
{
|
|
e3.Size = 0;
|
|
e3.isDirectory = 1;
|
|
e3.FullName = e->path;
|
|
e3.Name = e->path.subString ( r->Path.size(), e->path.size() - r->Path.size() - 1 );
|
|
|
|
if ( flist[1]->Files.binary_search ( e3 ) < 0 )
|
|
flist[1]->Files.push_back ( e3 );
|
|
}
|
|
else
|
|
{
|
|
e2.FullName = e->zipFileName;
|
|
e2.Name = e->simpleFileName;
|
|
|
|
if ( !e2.isDirectory )
|
|
core::deletePathFromFilename ( e2.Name );
|
|
flist[0]->Files.push_back ( e2 );
|
|
}
|
|
}
|
|
}
|
|
|
|
// add file to virtual directory
|
|
for ( u32 g = 0; g < 2; ++g )
|
|
{
|
|
if ( !flist[g] )
|
|
continue;
|
|
for ( u32 j = 0; j != flist[g]->Files.size(); ++j )
|
|
r->Files.push_back ( flist[g]->Files[j] );
|
|
|
|
flist[g]->drop();
|
|
}
|
|
}
|
|
|
|
r->Files.sort();
|
|
return r;
|
|
}
|
|
|
|
|
|
//! determines if a file exists and would be able to be opened.
|
|
bool CFileSystem::existFile(const core::string<c16>& filename) const
|
|
{
|
|
u32 i;
|
|
|
|
for (i=0; i < FileArchive.size(); ++i)
|
|
if ( FileArchive[i]->findFile(filename)!=-1)
|
|
return true;
|
|
|
|
#if defined ( _IRR_WCHAR_FILESYSTEM )
|
|
FILE* f = _wfopen(filename.c_str(), L"rb");
|
|
#else
|
|
FILE* f = fopen(filename.c_str(), "rb");
|
|
#endif
|
|
|
|
if (f)
|
|
{
|
|
fclose(f);
|
|
return true;
|
|
}
|
|
|
|
_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
|
|
return false;
|
|
}
|
|
|
|
|
|
//! Creates a XML Reader from a file.
|
|
IXMLReader* CFileSystem::createXMLReader(const core::string<c16>& filename)
|
|
{
|
|
IReadFile* file = createAndOpenFile(filename);
|
|
if (!file)
|
|
return 0;
|
|
|
|
IXMLReader* reader = createXMLReader(file);
|
|
file->drop();
|
|
return reader;
|
|
}
|
|
|
|
|
|
//! Creates a XML Reader from a file.
|
|
IXMLReader* CFileSystem::createXMLReader(IReadFile* file)
|
|
{
|
|
if (!file)
|
|
return 0;
|
|
|
|
return createIXMLReader(file);
|
|
}
|
|
|
|
|
|
//! Creates a XML Reader from a file.
|
|
IXMLReaderUTF8* CFileSystem::createXMLReaderUTF8(const core::string<c16>& filename)
|
|
{
|
|
IReadFile* file = createAndOpenFile(filename);
|
|
if (!file)
|
|
return 0;
|
|
|
|
IXMLReaderUTF8* reader = createIXMLReaderUTF8(file);
|
|
file->drop();
|
|
return reader;
|
|
}
|
|
|
|
|
|
//! Creates a XML Reader from a file.
|
|
IXMLReaderUTF8* CFileSystem::createXMLReaderUTF8(IReadFile* file)
|
|
{
|
|
if (!file)
|
|
return 0;
|
|
|
|
return createIXMLReaderUTF8(file);
|
|
}
|
|
|
|
|
|
//! Creates a XML Writer from a file.
|
|
IXMLWriter* CFileSystem::createXMLWriter(const core::string<c16>& filename)
|
|
{
|
|
IWriteFile* file = createAndWriteFile(filename);
|
|
IXMLWriter* writer = createXMLWriter(file);
|
|
file->drop();
|
|
return writer;
|
|
}
|
|
|
|
|
|
//! Creates a XML Writer from a file.
|
|
IXMLWriter* CFileSystem::createXMLWriter(IWriteFile* file)
|
|
{
|
|
return new CXMLWriter(file);
|
|
}
|
|
|
|
|
|
//! creates a filesystem which is able to open files from the ordinary file system,
|
|
//! and out of zipfiles, which are able to be added to the filesystem.
|
|
IFileSystem* createFileSystem()
|
|
{
|
|
return new CFileSystem();
|
|
}
|
|
|
|
|
|
//! Creates a new empty collection of attributes, usable for serialization and more.
|
|
IAttributes* CFileSystem::createEmptyAttributes(video::IVideoDriver* driver)
|
|
{
|
|
return new CAttributes(driver);
|
|
}
|
|
|
|
|
|
} // end namespace irr
|
|
} // end namespace io
|
|
|