2023-10-03 20:37:00 +02:00
// Copyright (C) 2002-2012 Nikolaus Gebhardt
// This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in irrlicht.h
# include "CFileSystem.h"
# include "IReadFile.h"
# include "IWriteFile.h"
# include "CZipReader.h"
# include "CFileList.h"
# include "stdio.h"
# include "os.h"
# include "CReadFile.h"
# include "CMemoryFile.h"
# include "CLimitReadFile.h"
# include "CWriteFile.h"
# include <list>
2024-03-20 19:35:52 +01:00
# if defined(__STRICT_ANSI__)
# error Compiling with __STRICT_ANSI__ not supported. g++ does set this when compiling with -std=c++11 or -std=c++0x. Use instead -std=gnu++11 or -std=gnu++0x. Or use -U__STRICT_ANSI__ to disable strict ansi.
2023-10-03 20:37:00 +02:00
# endif
2024-03-20 19:35:52 +01:00
# if defined(_IRR_WINDOWS_API_)
# include <direct.h> // for _chdir
2024-03-21 17:30:28 +01:00
# include <io.h> // for _access
2024-03-20 19:35:52 +01:00
# include <tchar.h>
2023-10-03 20:37:00 +02:00
# elif (defined(_IRR_POSIX_API_) || defined(_IRR_OSX_PLATFORM_) || defined(_IRR_ANDROID_PLATFORM_))
2024-03-20 19:35:52 +01:00
# include <cstdio>
# include <cstdlib>
# include <cstring>
# include <climits>
# include <sys/types.h>
# include <dirent.h>
# include <sys/stat.h>
# include <unistd.h>
2023-10-03 20:37:00 +02:00
# elif defined(_IRR_EMSCRIPTEN_PLATFORM_)
2024-03-20 19:35:52 +01:00
# include <unistd.h>
2023-10-03 20:37:00 +02:00
# endif
namespace irr
{
namespace io
{
//! constructor
CFileSystem : : CFileSystem ( )
{
2024-03-20 19:35:52 +01:00
# ifdef _DEBUG
2023-10-03 20:37:00 +02:00
setDebugName ( " CFileSystem " ) ;
2024-03-20 19:35:52 +01:00
# endif
2023-10-03 20:37:00 +02:00
setFileListSystem ( FILESYSTEM_NATIVE ) ;
//! reset current working directory
getWorkingDirectory ( ) ;
ArchiveLoader . push_back ( new CArchiveLoaderZIP ( this ) ) ;
}
//! destructor
CFileSystem : : ~ CFileSystem ( )
{
u32 i ;
2024-03-20 19:35:52 +01:00
for ( i = 0 ; i < FileArchives . size ( ) ; + + i ) {
2023-10-03 20:37:00 +02:00
FileArchives [ i ] - > drop ( ) ;
}
2024-03-20 19:35:52 +01:00
for ( i = 0 ; i < ArchiveLoader . size ( ) ; + + i ) {
2023-10-03 20:37:00 +02:00
ArchiveLoader [ i ] - > drop ( ) ;
}
}
//! opens a file for read access
2024-03-20 19:35:52 +01:00
IReadFile * CFileSystem : : createAndOpenFile ( const io : : path & filename )
2023-10-03 20:37:00 +02:00
{
2024-03-20 19:35:52 +01:00
if ( filename . empty ( ) )
2023-10-03 20:37:00 +02:00
return 0 ;
2024-03-20 19:35:52 +01:00
IReadFile * file = 0 ;
2023-10-03 20:37:00 +02:00
u32 i ;
2024-03-20 19:35:52 +01:00
for ( i = 0 ; i < FileArchives . size ( ) ; + + i ) {
2023-10-03 20:37:00 +02:00
file = FileArchives [ i ] - > createAndOpenFile ( filename ) ;
if ( file )
return file ;
}
// Create the file using an absolute path so that it matches
// the scheme used by CNullDriver::getTexture().
return CReadFile : : createReadFile ( getAbsolutePath ( filename ) ) ;
}
//! Creates an IReadFile interface for treating memory like a file.
2024-03-20 19:35:52 +01:00
IReadFile * CFileSystem : : createMemoryReadFile ( const void * memory , s32 len ,
const io : : path & fileName , bool deleteMemoryWhenDropped )
2023-10-03 20:37:00 +02:00
{
if ( ! memory )
return 0 ;
else
return new CMemoryReadFile ( memory , len , fileName , deleteMemoryWhenDropped ) ;
}
//! Creates an IReadFile interface for reading files inside files
2024-03-20 19:35:52 +01:00
IReadFile * CFileSystem : : createLimitReadFile ( const io : : path & fileName ,
IReadFile * alreadyOpenedFile , long pos , long areaSize )
2023-10-03 20:37:00 +02:00
{
if ( ! alreadyOpenedFile )
return 0 ;
else
return new CLimitReadFile ( alreadyOpenedFile , pos , areaSize , fileName ) ;
}
//! Creates an IReadFile interface for treating memory like a file.
2024-03-20 19:35:52 +01:00
IWriteFile * CFileSystem : : createMemoryWriteFile ( void * memory , s32 len ,
const io : : path & fileName , bool deleteMemoryWhenDropped )
2023-10-03 20:37:00 +02:00
{
if ( ! memory )
return 0 ;
else
return new CMemoryWriteFile ( memory , len , fileName , deleteMemoryWhenDropped ) ;
}
//! Opens a file for write access.
2024-03-20 19:35:52 +01:00
IWriteFile * CFileSystem : : createAndWriteFile ( const io : : path & filename , bool append )
2023-10-03 20:37:00 +02:00
{
return CWriteFile : : createWriteFile ( filename , append ) ;
}
//! Adds an external archive loader to the engine.
2024-03-20 19:35:52 +01:00
void CFileSystem : : addArchiveLoader ( IArchiveLoader * loader )
2023-10-03 20:37:00 +02:00
{
if ( ! loader )
return ;
loader - > grab ( ) ;
ArchiveLoader . push_back ( loader ) ;
}
//! Returns the total number of archive loaders added.
u32 CFileSystem : : getArchiveLoaderCount ( ) const
{
return ArchiveLoader . size ( ) ;
}
//! Gets the archive loader by index.
2024-03-20 19:35:52 +01:00
IArchiveLoader * CFileSystem : : getArchiveLoader ( u32 index ) const
2023-10-03 20:37:00 +02:00
{
if ( index < ArchiveLoader . size ( ) )
return ArchiveLoader [ index ] ;
else
return 0 ;
}
//! move the hirarchy of the filesystem. moves sourceIndex relative up or down
bool CFileSystem : : moveFileArchive ( u32 sourceIndex , s32 relative )
{
bool r = false ;
2024-03-20 19:35:52 +01:00
const s32 dest = ( s32 ) sourceIndex + relative ;
2023-10-03 20:37:00 +02:00
const s32 dir = relative < 0 ? - 1 : 1 ;
2024-03-20 19:35:52 +01:00
const s32 sourceEnd = ( ( s32 ) FileArchives . size ( ) ) - 1 ;
2023-10-03 20:37:00 +02:00
IFileArchive * t ;
2024-03-20 19:35:52 +01:00
for ( s32 s = ( s32 ) sourceIndex ; s ! = dest ; s + = dir ) {
2023-10-03 20:37:00 +02:00
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 ;
}
//! Adds an archive to the file system.
2024-03-20 19:35:52 +01:00
bool CFileSystem : : addFileArchive ( const io : : path & filename , bool ignoreCase ,
bool ignorePaths , E_FILE_ARCHIVE_TYPE archiveType ,
const core : : stringc & password ,
IFileArchive * * retArchive )
2023-10-03 20:37:00 +02:00
{
2024-03-20 19:35:52 +01:00
IFileArchive * archive = 0 ;
2023-10-03 20:37:00 +02:00
bool ret = false ;
// see if archive is already added
s32 i ;
// do we know what type it should be?
2024-03-20 19:35:52 +01:00
if ( archiveType = = EFAT_UNKNOWN ) {
2023-10-03 20:37:00 +02:00
// try to load archive based on file name
2024-03-20 19:35:52 +01:00
for ( i = ArchiveLoader . size ( ) - 1 ; i > = 0 ; - - i ) {
if ( ArchiveLoader [ i ] - > isALoadableFileFormat ( filename ) ) {
2023-10-03 20:37:00 +02:00
archive = ArchiveLoader [ i ] - > createArchive ( filename , ignoreCase , ignorePaths ) ;
if ( archive )
break ;
}
}
// try to load archive based on content
2024-03-20 19:35:52 +01:00
if ( ! archive ) {
io : : IReadFile * file = createAndOpenFile ( filename ) ;
if ( file ) {
for ( i = ArchiveLoader . size ( ) - 1 ; i > = 0 ; - - i ) {
2023-10-03 20:37:00 +02:00
file - > seek ( 0 ) ;
2024-03-20 19:35:52 +01:00
if ( ArchiveLoader [ i ] - > isALoadableFileFormat ( file ) ) {
2023-10-03 20:37:00 +02:00
file - > seek ( 0 ) ;
archive = ArchiveLoader [ i ] - > createArchive ( file , ignoreCase , ignorePaths ) ;
if ( archive )
break ;
}
}
file - > drop ( ) ;
}
}
2024-03-20 19:35:52 +01:00
} else {
2023-10-03 20:37:00 +02:00
// try to open archive based on archive loader type
2024-03-20 19:35:52 +01:00
io : : IReadFile * file = 0 ;
2023-10-03 20:37:00 +02:00
2024-03-20 19:35:52 +01:00
for ( i = ArchiveLoader . size ( ) - 1 ; i > = 0 ; - - i ) {
if ( ArchiveLoader [ i ] - > isALoadableFileFormat ( archiveType ) ) {
2023-10-03 20:37:00 +02:00
// attempt to open file
if ( ! file )
file = createAndOpenFile ( filename ) ;
// is the file open?
2024-03-20 19:35:52 +01:00
if ( file ) {
2023-10-03 20:37:00 +02:00
// attempt to open archive
file - > seek ( 0 ) ;
2024-03-20 19:35:52 +01:00
if ( ArchiveLoader [ i ] - > isALoadableFileFormat ( file ) ) {
2023-10-03 20:37:00 +02:00
file - > seek ( 0 ) ;
archive = ArchiveLoader [ i ] - > createArchive ( file , ignoreCase , ignorePaths ) ;
if ( archive )
break ;
}
2024-03-20 19:35:52 +01:00
} else {
2023-10-03 20:37:00 +02:00
// couldn't open file
break ;
}
}
}
// if open, close the file
if ( file )
file - > drop ( ) ;
}
2024-03-20 19:35:52 +01:00
if ( archive ) {
2023-10-03 20:37:00 +02:00
FileArchives . push_back ( archive ) ;
if ( password . size ( ) )
2024-03-20 19:35:52 +01:00
archive - > Password = password ;
2023-10-03 20:37:00 +02:00
if ( retArchive )
* retArchive = archive ;
ret = true ;
2024-03-20 19:35:52 +01:00
} else {
2023-10-03 20:37:00 +02:00
os : : Printer : : log ( " Could not create archive for " , filename , ELL_ERROR ) ;
}
return ret ;
}
2024-03-20 19:35:52 +01:00
bool CFileSystem : : addFileArchive ( IReadFile * file , bool ignoreCase ,
2023-10-03 20:37:00 +02:00
bool ignorePaths , E_FILE_ARCHIVE_TYPE archiveType ,
2024-03-20 19:35:52 +01:00
const core : : stringc & password , IFileArchive * * retArchive )
2023-10-03 20:37:00 +02:00
{
2024-03-09 22:34:05 +01:00
if ( ! file )
2023-10-03 20:37:00 +02:00
return false ;
2024-03-20 19:35:52 +01:00
if ( file ) {
IFileArchive * archive = 0 ;
2023-10-03 20:37:00 +02:00
s32 i ;
2024-03-20 19:35:52 +01:00
if ( archiveType = = EFAT_UNKNOWN ) {
2023-10-03 20:37:00 +02:00
// try to load archive based on file name
2024-03-20 19:35:52 +01:00
for ( i = ArchiveLoader . size ( ) - 1 ; i > = 0 ; - - i ) {
if ( ArchiveLoader [ i ] - > isALoadableFileFormat ( file - > getFileName ( ) ) ) {
2023-10-03 20:37:00 +02:00
archive = ArchiveLoader [ i ] - > createArchive ( file , ignoreCase , ignorePaths ) ;
if ( archive )
break ;
}
}
// try to load archive based on content
2024-03-20 19:35:52 +01:00
if ( ! archive ) {
for ( i = ArchiveLoader . size ( ) - 1 ; i > = 0 ; - - i ) {
2023-10-03 20:37:00 +02:00
file - > seek ( 0 ) ;
2024-03-20 19:35:52 +01:00
if ( ArchiveLoader [ i ] - > isALoadableFileFormat ( file ) ) {
2023-10-03 20:37:00 +02:00
file - > seek ( 0 ) ;
archive = ArchiveLoader [ i ] - > createArchive ( file , ignoreCase , ignorePaths ) ;
if ( archive )
break ;
}
}
}
2024-03-20 19:35:52 +01:00
} else {
2023-10-03 20:37:00 +02:00
// try to open archive based on archive loader type
2024-03-20 19:35:52 +01:00
for ( i = ArchiveLoader . size ( ) - 1 ; i > = 0 ; - - i ) {
if ( ArchiveLoader [ i ] - > isALoadableFileFormat ( archiveType ) ) {
2023-10-03 20:37:00 +02:00
// attempt to open archive
file - > seek ( 0 ) ;
2024-03-20 19:35:52 +01:00
if ( ArchiveLoader [ i ] - > isALoadableFileFormat ( file ) ) {
2023-10-03 20:37:00 +02:00
file - > seek ( 0 ) ;
archive = ArchiveLoader [ i ] - > createArchive ( file , ignoreCase , ignorePaths ) ;
if ( archive )
break ;
}
}
}
}
2024-03-20 19:35:52 +01:00
if ( archive ) {
2023-10-03 20:37:00 +02:00
FileArchives . push_back ( archive ) ;
if ( password . size ( ) )
2024-03-20 19:35:52 +01:00
archive - > Password = password ;
2023-10-03 20:37:00 +02:00
if ( retArchive )
* retArchive = archive ;
return true ;
2024-03-20 19:35:52 +01:00
} else {
2023-10-03 20:37:00 +02:00
os : : Printer : : log ( " Could not create archive for " , file - > getFileName ( ) , ELL_ERROR ) ;
}
}
return false ;
}
//! Adds an archive to the file system.
2024-03-20 19:35:52 +01:00
bool CFileSystem : : addFileArchive ( IFileArchive * archive )
2023-10-03 20:37:00 +02:00
{
2024-03-20 19:35:52 +01:00
if ( archive ) {
for ( u32 i = 0 ; i < FileArchives . size ( ) ; + + i ) {
if ( archive = = FileArchives [ i ] ) {
2023-10-03 20:37:00 +02:00
return false ;
}
}
FileArchives . push_back ( archive ) ;
archive - > grab ( ) ;
return true ;
}
return false ;
}
//! removes an archive from the file system.
bool CFileSystem : : removeFileArchive ( u32 index )
{
bool ret = false ;
2024-03-20 19:35:52 +01:00
if ( index < FileArchives . size ( ) ) {
2023-10-03 20:37:00 +02:00
FileArchives [ index ] - > drop ( ) ;
FileArchives . erase ( index ) ;
ret = true ;
}
return ret ;
}
//! removes an archive from the file system.
2024-03-20 19:35:52 +01:00
bool CFileSystem : : removeFileArchive ( const io : : path & filename )
2023-10-03 20:37:00 +02:00
{
const path absPath = getAbsolutePath ( filename ) ;
2024-03-20 19:35:52 +01:00
for ( u32 i = 0 ; i < FileArchives . size ( ) ; + + i ) {
2023-10-03 20:37:00 +02:00
if ( absPath = = FileArchives [ i ] - > getFileList ( ) - > getPath ( ) )
return removeFileArchive ( i ) ;
}
return false ;
}
//! Removes an archive from the file system.
2024-03-20 19:35:52 +01:00
bool CFileSystem : : removeFileArchive ( const IFileArchive * archive )
2023-10-03 20:37:00 +02:00
{
2024-03-20 19:35:52 +01:00
for ( u32 i = 0 ; i < FileArchives . size ( ) ; + + i ) {
if ( archive = = FileArchives [ i ] ) {
2023-10-03 20:37:00 +02:00
return removeFileArchive ( i ) ;
}
}
return false ;
}
//! gets an archive
u32 CFileSystem : : getFileArchiveCount ( ) const
{
return FileArchives . size ( ) ;
}
2024-03-20 19:35:52 +01:00
IFileArchive * CFileSystem : : getFileArchive ( u32 index )
2023-10-03 20:37:00 +02:00
{
return index < getFileArchiveCount ( ) ? FileArchives [ index ] : 0 ;
}
//! Returns the string of the current working directory
2024-03-20 19:35:52 +01:00
const io : : path & CFileSystem : : getWorkingDirectory ( )
2023-10-03 20:37:00 +02:00
{
EFileSystemType type = FileSystemType ;
2024-03-20 19:35:52 +01:00
if ( type ! = FILESYSTEM_NATIVE ) {
2023-10-03 20:37:00 +02:00
type = FILESYSTEM_VIRTUAL ;
2024-03-20 19:35:52 +01:00
} else {
# if defined(_IRR_WINDOWS_API_)
fschar_t tmp [ _MAX_PATH ] ;
_getcwd ( tmp , _MAX_PATH ) ;
WorkingDirectory [ FILESYSTEM_NATIVE ] = tmp ;
WorkingDirectory [ FILESYSTEM_NATIVE ] . replace ( ' \\ ' , ' / ' ) ;
# endif
# if (defined(_IRR_POSIX_API_) || defined(_IRR_OSX_PLATFORM_))
// getting the CWD is rather complex as we do not know the size
// so try it until the call was successful
// Note that neither the first nor the second parameter may be 0 according to POSIX
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
2023-10-03 20:37:00 +02:00
WorkingDirectory [ type ] . validate ( ) ;
}
return WorkingDirectory [ type ] ;
}
//! Changes the current Working Directory to the given string.
2024-03-20 19:35:52 +01:00
bool CFileSystem : : changeWorkingDirectoryTo ( const io : : path & newDirectory )
2023-10-03 20:37:00 +02:00
{
2024-03-20 19:35:52 +01:00
bool success = false ;
2023-10-03 20:37:00 +02:00
2024-03-20 19:35:52 +01:00
if ( FileSystemType ! = FILESYSTEM_NATIVE ) {
2023-10-03 20:37:00 +02:00
WorkingDirectory [ FILESYSTEM_VIRTUAL ] = newDirectory ;
// is this empty string constant really intended?
flattenFilename ( WorkingDirectory [ FILESYSTEM_VIRTUAL ] , _IRR_TEXT ( " " ) ) ;
success = true ;
2024-03-20 19:35:52 +01:00
} else {
2023-10-03 20:37:00 +02:00
WorkingDirectory [ FILESYSTEM_NATIVE ] = newDirectory ;
# if defined(_MSC_VER)
success = ( _chdir ( newDirectory . c_str ( ) ) = = 0 ) ;
# else
success = ( chdir ( newDirectory . c_str ( ) ) = = 0 ) ;
# endif
}
return success ;
}
2024-03-20 19:35:52 +01:00
io : : path CFileSystem : : getAbsolutePath ( const io : : path & filename ) const
2023-10-03 20:37:00 +02:00
{
2024-03-20 19:35:52 +01:00
if ( filename . empty ( ) )
2023-10-03 20:37:00 +02:00
return filename ;
# if defined(_IRR_WINDOWS_API_)
2024-03-20 19:35:52 +01:00
fschar_t * p = 0 ;
2023-10-03 20:37:00 +02:00
fschar_t fpath [ _MAX_PATH ] ;
2024-03-20 19:35:52 +01:00
p = _fullpath ( fpath , filename . c_str ( ) , _MAX_PATH ) ;
core : : stringc tmp ( p ) ;
tmp . replace ( ' \\ ' , ' / ' ) ;
2023-10-03 20:37:00 +02:00
return tmp ;
# elif (defined(_IRR_POSIX_API_) || defined(_IRR_OSX_PLATFORM_))
2024-03-20 19:35:52 +01:00
c8 * p = 0 ;
2023-10-03 20:37:00 +02:00
c8 fpath [ 4096 ] ;
2024-03-20 19:35:52 +01:00
fpath [ 0 ] = 0 ;
2023-10-03 20:37:00 +02:00
p = realpath ( filename . c_str ( ) , fpath ) ;
2024-03-20 19:35:52 +01:00
if ( ! p ) {
2023-10-03 20:37:00 +02:00
// content in fpath is unclear at this point
2023-10-04 20:10:58 +02:00
if ( ! fpath [ 0 ] ) { // seems like fpath wasn't altered, use our best guess
2023-10-03 20:37:00 +02:00
io : : path tmp ( filename ) ;
return flattenFilename ( tmp ) ;
2024-03-20 19:35:52 +01:00
} else
2023-10-03 20:37:00 +02:00
return io : : path ( fpath ) ;
}
2024-03-20 19:35:52 +01:00
if ( filename [ filename . size ( ) - 1 ] = = ' / ' )
return io : : path ( p ) + _IRR_TEXT ( " / " ) ;
2023-10-03 20:37:00 +02:00
else
return io : : path ( p ) ;
# else
return io : : path ( filename ) ;
# endif
}
//! 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.
2024-03-20 19:35:52 +01:00
io : : path CFileSystem : : getFileDir ( const io : : path & filename ) const
2023-10-03 20:37:00 +02:00
{
// 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 _IRR_TEXT ( " . " ) ;
}
//! 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.
2024-03-20 19:35:52 +01:00
io : : path CFileSystem : : getFileBasename ( const io : : path & filename , bool keepExtension ) const
2023-10-03 20:37:00 +02:00
{
// find last forward or backslash
s32 lastSlash = filename . findLast ( ' / ' ) ;
const s32 lastBackSlash = filename . findLast ( ' \\ ' ) ;
lastSlash = core : : max_ ( lastSlash , lastBackSlash ) ;
// get number of chars after last dot
s32 end = 0 ;
2024-03-20 19:35:52 +01:00
if ( ! keepExtension ) {
2023-10-03 20:37:00 +02:00
// take care to search only after last slash to check only for
// dots in the filename
end = filename . findLast ( ' . ' ) ;
if ( end = = - 1 | | end < lastSlash )
2024-03-20 19:35:52 +01:00
end = 0 ;
2023-10-03 20:37:00 +02:00
else
2024-03-20 19:35:52 +01:00
end = filename . size ( ) - end ;
2023-10-03 20:37:00 +02:00
}
if ( ( u32 ) lastSlash < filename . size ( ) )
2024-03-20 19:35:52 +01:00
return filename . subString ( lastSlash + 1 , filename . size ( ) - lastSlash - 1 - end ) ;
2023-10-03 20:37:00 +02:00
else if ( end ! = 0 )
2024-03-20 19:35:52 +01:00
return filename . subString ( 0 , filename . size ( ) - end ) ;
2023-10-03 20:37:00 +02:00
else
return filename ;
}
//! flatten a path and file name for example: "/you/me/../." becomes "/you"
2024-03-20 19:35:52 +01:00
io : : path & CFileSystem : : flattenFilename ( io : : path & directory , const io : : path & root ) const
2023-10-03 20:37:00 +02:00
{
directory . replace ( ' \\ ' , ' / ' ) ;
if ( directory . lastChar ( ) ! = ' / ' )
directory . append ( ' / ' ) ;
io : : path dir ;
io : : path subdir ;
s32 lastpos = 0 ;
s32 pos = 0 ;
2024-03-20 19:35:52 +01:00
bool lastWasRealDir = false ;
2023-10-03 20:37:00 +02:00
2024-03-20 19:35:52 +01:00
while ( ( pos = directory . findNext ( ' / ' , lastpos ) ) > = 0 ) {
2023-10-03 20:37:00 +02:00
subdir = directory . subString ( lastpos , pos - lastpos + 1 ) ;
2024-03-20 19:35:52 +01:00
if ( subdir = = _IRR_TEXT ( " ../ " ) ) {
if ( lastWasRealDir ) {
2023-10-03 20:37:00 +02:00
deletePathFromPath ( dir , 2 ) ;
2024-03-20 19:35:52 +01:00
lastWasRealDir = ( dir . size ( ) ! = 0 ) ;
} else {
2023-10-03 20:37:00 +02:00
dir . append ( subdir ) ;
2024-03-20 19:35:52 +01:00
lastWasRealDir = false ;
2023-10-03 20:37:00 +02:00
}
2024-03-20 19:35:52 +01:00
} else if ( subdir = = _IRR_TEXT ( " / " ) ) {
2023-10-03 20:37:00 +02:00
dir = root ;
2024-03-20 19:35:52 +01:00
} else if ( subdir ! = _IRR_TEXT ( " ./ " ) ) {
2023-10-03 20:37:00 +02:00
dir . append ( subdir ) ;
2024-03-20 19:35:52 +01:00
lastWasRealDir = true ;
2023-10-03 20:37:00 +02:00
}
lastpos = pos + 1 ;
}
directory = dir ;
return directory ;
}
//! Get the relative filename, relative to the given directory
2024-03-20 19:35:52 +01:00
path CFileSystem : : getRelativeFilename ( const path & filename , const path & directory ) const
2023-10-03 20:37:00 +02:00
{
2024-03-20 19:35:52 +01:00
if ( filename . empty ( ) | | directory . empty ( ) )
2023-10-03 20:37:00 +02:00
return filename ;
io : : path path1 , file , ext ;
core : : splitFilename ( getAbsolutePath ( filename ) , & path1 , & file , & ext ) ;
io : : path path2 ( getAbsolutePath ( directory ) ) ;
std : : list < io : : path > list1 , list2 ;
path1 . split ( list1 , _IRR_TEXT ( " / \\ " ) , 2 ) ;
path2 . split ( list2 , _IRR_TEXT ( " / \\ " ) , 2 ) ;
2024-03-20 19:35:52 +01:00
std : : list < io : : path > : : const_iterator it1 , it2 ;
it1 = list1 . begin ( ) ;
it2 = list2 . begin ( ) ;
2023-10-03 20:37:00 +02:00
2024-03-20 19:35:52 +01:00
# if defined(_IRR_WINDOWS_API_)
2023-10-03 20:37:00 +02:00
fschar_t partition1 = 0 , partition2 = 0 ;
io : : path prefix1 , prefix2 ;
2024-03-20 19:35:52 +01:00
if ( it1 ! = list1 . end ( ) )
2023-10-03 20:37:00 +02:00
prefix1 = * it1 ;
2024-03-20 19:35:52 +01:00
if ( it2 ! = list2 . end ( ) )
2023-10-03 20:37:00 +02:00
prefix2 = * it2 ;
2024-03-20 19:35:52 +01:00
if ( prefix1 . size ( ) > 1 & & prefix1 [ 1 ] = = _IRR_TEXT ( ' : ' ) )
2023-10-03 20:37:00 +02:00
partition1 = core : : locale_lower ( prefix1 [ 0 ] ) ;
2024-03-20 19:35:52 +01:00
if ( prefix2 . size ( ) > 1 & & prefix2 [ 1 ] = = _IRR_TEXT ( ' : ' ) )
2023-10-03 20:37:00 +02:00
partition2 = core : : locale_lower ( prefix2 [ 0 ] ) ;
// must have the same prefix or we can't resolve it to a relative filename
2024-03-20 19:35:52 +01:00
if ( partition1 ! = partition2 ) {
2023-10-03 20:37:00 +02:00
return filename ;
}
2024-03-20 19:35:52 +01:00
# endif
2023-10-03 20:37:00 +02:00
for ( ; it1 ! = list1 . end ( ) & & it2 ! = list2 . end ( )
2024-03-20 19:35:52 +01:00
# if defined(_IRR_WINDOWS_API_)
& & ( io : : path ( * it1 ) . make_lower ( ) = = io : : path ( * it2 ) . make_lower ( ) )
2023-10-03 20:37:00 +02:00
# else
2024-03-20 19:35:52 +01:00
& & ( * it1 = = * it2 )
2023-10-03 20:37:00 +02:00
# endif
2024-03-20 19:35:52 +01:00
; ) {
2023-10-03 20:37:00 +02:00
+ + it1 ;
+ + it2 ;
}
2024-03-20 19:35:52 +01:00
path1 = _IRR_TEXT ( " " ) ;
2023-10-03 20:37:00 +02:00
for ( ; it2 ! = list2 . end ( ) ; + + it2 )
path1 + = _IRR_TEXT ( " ../ " ) ;
2024-03-20 19:35:52 +01:00
while ( it1 ! = list1 . end ( ) ) {
2023-10-03 20:37:00 +02:00
path1 + = * it1 + + ;
path1 + = _IRR_TEXT ( ' / ' ) ;
}
path1 + = file ;
2024-03-20 19:35:52 +01:00
if ( ext . size ( ) ) {
2023-10-03 20:37:00 +02:00
path1 + = _IRR_TEXT ( ' . ' ) ;
path1 + = ext ;
}
return path1 ;
}
//! Sets the current file systen type
EFileSystemType CFileSystem : : setFileListSystem ( EFileSystemType listType )
{
EFileSystemType current = FileSystemType ;
FileSystemType = listType ;
return current ;
}
//! Creates a list of files and directories in the current working directory
2024-03-20 19:35:52 +01:00
IFileList * CFileSystem : : createFileList ( )
2023-10-03 20:37:00 +02:00
{
2024-03-20 19:35:52 +01:00
CFileList * r = 0 ;
2023-10-03 20:37:00 +02:00
io : : path Path = getWorkingDirectory ( ) ;
Path . replace ( ' \\ ' , ' / ' ) ;
if ( ! Path . empty ( ) & & Path . lastChar ( ) ! = ' / ' )
Path . append ( ' / ' ) ;
//! Construct from native filesystem
2024-03-20 19:35:52 +01:00
if ( FileSystemType = = FILESYSTEM_NATIVE ) {
// --------------------------------------------
//! Windows version
# ifdef _IRR_WINDOWS_API_
2023-10-03 20:37:00 +02:00
r = new CFileList ( Path , true , false ) ;
2024-03-20 19:35:52 +01:00
// intptr_t is optional but supported by MinGW since 2007 or earlier.
2023-10-03 20:37:00 +02:00
intptr_t hFile ;
struct _tfinddata_t c_file ;
2024-03-20 19:35:52 +01:00
if ( ( hFile = _tfindfirst ( _T ( " * " ) , & c_file ) ) ! = ( intptr_t ) ( - 1L ) ) {
do {
2023-10-03 20:37:00 +02:00
r - > addItem ( Path + c_file . name , 0 , c_file . size , ( _A_SUBDIR & c_file . attrib ) ! = 0 , 0 ) ;
2024-03-20 19:35:52 +01:00
} while ( _tfindnext ( hFile , & c_file ) = = 0 ) ;
2023-10-03 20:37:00 +02:00
2024-03-20 19:35:52 +01:00
_findclose ( hFile ) ;
2023-10-03 20:37:00 +02:00
}
2024-03-20 19:35:52 +01:00
# endif
2023-10-03 20:37:00 +02:00
2024-03-20 19:35:52 +01:00
// --------------------------------------------
//! Linux version
# if (defined(_IRR_POSIX_API_) || defined(_IRR_OSX_PLATFORM_))
2023-10-03 20:37:00 +02:00
r = new CFileList ( Path , false , false ) ;
r - > addItem ( Path + _IRR_TEXT ( " .. " ) , 0 , 0 , true , 0 ) ;
//! We use the POSIX compliant methods instead of scandir
2024-03-20 19:35:52 +01:00
DIR * dirHandle = opendir ( Path . c_str ( ) ) ;
if ( dirHandle ) {
2023-10-03 20:37:00 +02:00
struct dirent * dirEntry ;
2024-03-20 19:35:52 +01:00
while ( ( dirEntry = readdir ( dirHandle ) ) ) {
2023-10-03 20:37:00 +02:00
u32 size = 0 ;
bool isDirectory = false ;
2024-03-20 19:35:52 +01:00
if ( ( strcmp ( dirEntry - > d_name , " . " ) = = 0 ) | |
( strcmp ( dirEntry - > d_name , " .. " ) = = 0 ) ) {
2023-10-03 20:37:00 +02:00
continue ;
}
struct stat buf ;
2024-03-20 19:35:52 +01:00
if ( stat ( dirEntry - > d_name , & buf ) = = 0 ) {
2023-10-03 20:37:00 +02:00
size = buf . st_size ;
isDirectory = S_ISDIR ( buf . st_mode ) ;
}
2024-03-20 19:35:52 +01:00
# if !defined(_IRR_SOLARIS_PLATFORM_) && !defined(__CYGWIN__) && !defined(__HAIKU__)
2023-10-03 20:37:00 +02:00
// only available on some systems
2024-03-20 19:35:52 +01:00
else {
2023-10-03 20:37:00 +02:00
isDirectory = dirEntry - > d_type = = DT_DIR ;
}
2024-03-20 19:35:52 +01:00
# endif
2023-10-03 20:37:00 +02:00
r - > addItem ( Path + dirEntry - > d_name , 0 , size , isDirectory , 0 ) ;
}
closedir ( dirHandle ) ;
}
2024-03-20 19:35:52 +01:00
# endif
} else {
2023-10-03 20:37:00 +02:00
//! create file list for the virtual filesystem
r = new CFileList ( Path , false , false ) ;
//! add relative navigation
SFileListEntry e2 ;
SFileListEntry e3 ;
//! PWD
r - > addItem ( Path + _IRR_TEXT ( " . " ) , 0 , 0 , true , 0 ) ;
//! parent
r - > addItem ( Path + _IRR_TEXT ( " .. " ) , 0 , 0 , true , 0 ) ;
//! merge archives
2024-03-20 19:35:52 +01:00
for ( u32 i = 0 ; i < FileArchives . size ( ) ; + + i ) {
2023-10-03 20:37:00 +02:00
const IFileList * merge = FileArchives [ i ] - > getFileList ( ) ;
2024-03-20 19:35:52 +01:00
for ( u32 j = 0 ; j < merge - > getFileCount ( ) ; + + j ) {
if ( core : : isInSameDirectory ( Path , merge - > getFullFileName ( j ) ) = = 0 ) {
2023-10-03 20:37:00 +02:00
r - > addItem ( merge - > getFullFileName ( j ) , merge - > getFileOffset ( j ) , merge - > getFileSize ( j ) , merge - > isDirectory ( j ) , 0 ) ;
}
}
}
}
if ( r )
r - > sort ( ) ;
return r ;
}
//! Creates an empty filelist
2024-03-20 19:35:52 +01:00
IFileList * CFileSystem : : createEmptyFileList ( const io : : path & path , bool ignoreCase , bool ignorePaths )
2023-10-03 20:37:00 +02:00
{
return new CFileList ( path , ignoreCase , ignorePaths ) ;
}
//! determines if a file exists and would be able to be opened.
2024-03-20 19:35:52 +01:00
bool CFileSystem : : existFile ( const io : : path & filename ) const
2023-10-03 20:37:00 +02:00
{
2024-03-20 19:35:52 +01:00
for ( u32 i = 0 ; i < FileArchives . size ( ) ; + + i )
if ( FileArchives [ i ] - > getFileList ( ) - > findFile ( filename ) ! = - 1 )
2023-10-03 20:37:00 +02:00
return true ;
# if defined(_MSC_VER)
2024-03-20 19:35:52 +01:00
return ( _access ( filename . c_str ( ) , 0 ) ! = - 1 ) ;
2023-10-03 20:37:00 +02:00
# elif defined(F_OK)
2024-03-20 19:35:52 +01:00
return ( access ( filename . c_str ( ) , F_OK ) ! = - 1 ) ;
2023-10-03 20:37:00 +02:00
# else
return ( access ( filename . c_str ( ) , 0 ) ! = - 1 ) ;
# endif
}
//! 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.
2024-03-20 19:35:52 +01:00
IFileSystem * createFileSystem ( )
2023-10-03 20:37:00 +02:00
{
return new CFileSystem ( ) ;
}
} // end namespace irr
} // end namespace io