1 // Copyright (C) 2002-2012 Nikolaus Gebhardt
2 // This file is part of the "Irrlicht Engine".
3 // For conditions of distribution and use, see copyright notice in irrlicht.h
4 
5 #include "IrrCompileConfig.h"
6 
7 #include "CFileSystem.h"
8 #include "IReadFile.h"
9 #include "IWriteFile.h"
10 #include "CZipReader.h"
11 #include "CMountPointReader.h"
12 #include "CPakReader.h"
13 #include "CNPKReader.h"
14 #include "CTarReader.h"
15 #include "CWADReader.h"
16 #include "CFileList.h"
17 #include "CXMLReader.h"
18 #include "CXMLWriter.h"
19 #include "stdio.h"
20 #include "os.h"
21 #include "CAttributes.h"
22 #include "CMemoryFile.h"
23 #include "CLimitReadFile.h"
24 #include "irrList.h"
25 
26 #if defined (__STRICT_ANSI__)
27     #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.
28 #endif
29 
30 #if defined (_IRR_WINDOWS_API_)
31 	#if !defined ( _WIN32_WCE )
32 		#include <direct.h> // for _chdir
33 		#include <io.h> // for _access
34 		#include <tchar.h>
35 	#endif
36 #else
37 	#if (defined(_IRR_POSIX_API_) || defined(_IRR_OSX_PLATFORM_))
38 		#include <stdio.h>
39 		#include <stdlib.h>
40 		#include <string.h>
41 		#include <limits.h>
42 		#include <sys/types.h>
43 		#include <dirent.h>
44 		#include <sys/stat.h>
45 		#include <unistd.h>
46 	#endif
47 #endif
48 
49 namespace irr
50 {
51 namespace io
52 {
53 
54 //! constructor
CFileSystem()55 CFileSystem::CFileSystem()
56 {
57 	#ifdef _DEBUG
58 	setDebugName("CFileSystem");
59 	#endif
60 
61 	setFileListSystem(FILESYSTEM_NATIVE);
62 	//! reset current working directory
63 	getWorkingDirectory();
64 
65 #ifdef __IRR_COMPILE_WITH_PAK_ARCHIVE_LOADER_
66 	ArchiveLoader.push_back(new CArchiveLoaderPAK(this));
67 #endif
68 
69 #ifdef __IRR_COMPILE_WITH_NPK_ARCHIVE_LOADER_
70 	ArchiveLoader.push_back(new CArchiveLoaderNPK(this));
71 #endif
72 
73 #ifdef __IRR_COMPILE_WITH_TAR_ARCHIVE_LOADER_
74 	ArchiveLoader.push_back(new CArchiveLoaderTAR(this));
75 #endif
76 
77 #ifdef __IRR_COMPILE_WITH_WAD_ARCHIVE_LOADER_
78 	ArchiveLoader.push_back(new CArchiveLoaderWAD(this));
79 #endif
80 
81 #ifdef __IRR_COMPILE_WITH_MOUNT_ARCHIVE_LOADER_
82 	ArchiveLoader.push_back(new CArchiveLoaderMount(this));
83 #endif
84 
85 #ifdef __IRR_COMPILE_WITH_ZIP_ARCHIVE_LOADER_
86 	ArchiveLoader.push_back(new CArchiveLoaderZIP(this));
87 #endif
88 
89 }
90 
91 
92 //! destructor
~CFileSystem()93 CFileSystem::~CFileSystem()
94 {
95 	u32 i;
96 
97 	for ( i=0; i < FileArchives.size(); ++i)
98 	{
99 		FileArchives[i]->drop();
100 	}
101 
102 	for ( i=0; i < ArchiveLoader.size(); ++i)
103 	{
104 		ArchiveLoader[i]->drop();
105 	}
106 }
107 
108 
109 //! opens a file for read access
createAndOpenFile(const io::path & filename)110 IReadFile* CFileSystem::createAndOpenFile(const io::path& filename)
111 {
112 	if ( filename.empty() )
113 		return 0;
114 
115 	IReadFile* file = 0;
116 	u32 i;
117 
118 	for (i=0; i< FileArchives.size(); ++i)
119 	{
120 		file = FileArchives[i]->createAndOpenFile(filename);
121 		if (file)
122 			return file;
123 	}
124 
125 	// Create the file using an absolute path so that it matches
126 	// the scheme used by CNullDriver::getTexture().
127 	return createReadFile(getAbsolutePath(filename));
128 }
129 
130 
131 //! Creates an IReadFile interface for treating memory like a file.
createMemoryReadFile(void * memory,s32 len,const io::path & fileName,bool deleteMemoryWhenDropped)132 IReadFile* CFileSystem::createMemoryReadFile(void* memory, s32 len,
133 		const io::path& fileName, bool deleteMemoryWhenDropped)
134 {
135 	if (!memory)
136 		return 0;
137 	else
138 		return new CMemoryFile(memory, len, fileName, deleteMemoryWhenDropped);
139 			}
140 
141 
142 //! Creates an IReadFile interface for reading files inside files
createLimitReadFile(const io::path & fileName,IReadFile * alreadyOpenedFile,long pos,long areaSize)143 IReadFile* CFileSystem::createLimitReadFile(const io::path& fileName,
144 		IReadFile* alreadyOpenedFile, long pos, long areaSize)
145 {
146 	if (!alreadyOpenedFile)
147 		return 0;
148 	else
149 		return new CLimitReadFile(alreadyOpenedFile, pos, areaSize, fileName);
150 }
151 
152 
153 //! Creates an IReadFile interface for treating memory like a file.
createMemoryWriteFile(void * memory,s32 len,const io::path & fileName,bool deleteMemoryWhenDropped)154 IWriteFile* CFileSystem::createMemoryWriteFile(void* memory, s32 len,
155 		const io::path& fileName, bool deleteMemoryWhenDropped)
156 {
157 	if (!memory)
158 		return 0;
159 	else
160 		return new CMemoryFile(memory, len, fileName, deleteMemoryWhenDropped);
161 }
162 
163 
164 //! Opens a file for write access.
createAndWriteFile(const io::path & filename,bool append)165 IWriteFile* CFileSystem::createAndWriteFile(const io::path& filename, bool append)
166 {
167 	return createWriteFile(filename, append);
168 }
169 
170 
171 //! Adds an external archive loader to the engine.
addArchiveLoader(IArchiveLoader * loader)172 void CFileSystem::addArchiveLoader(IArchiveLoader* loader)
173 {
174 	if (!loader)
175 		return;
176 
177 	loader->grab();
178 	ArchiveLoader.push_back(loader);
179 }
180 
181 //! Returns the total number of archive loaders added.
getArchiveLoaderCount() const182 u32 CFileSystem::getArchiveLoaderCount() const
183 {
184 	return ArchiveLoader.size();
185 }
186 
187 //! Gets the archive loader by index.
getArchiveLoader(u32 index) const188 IArchiveLoader* CFileSystem::getArchiveLoader(u32 index) const
189 {
190 	if (index < ArchiveLoader.size())
191 		return ArchiveLoader[index];
192 	else
193 		return 0;
194 }
195 
196 //! move the hirarchy of the filesystem. moves sourceIndex relative up or down
moveFileArchive(u32 sourceIndex,s32 relative)197 bool CFileSystem::moveFileArchive(u32 sourceIndex, s32 relative)
198 {
199 	bool r = false;
200 	const s32 dest = (s32) sourceIndex + relative;
201 	const s32 dir = relative < 0 ? -1 : 1;
202 	const s32 sourceEnd = ((s32) FileArchives.size() ) - 1;
203 	IFileArchive *t;
204 
205 	for (s32 s = (s32) sourceIndex;s != dest; s += dir)
206 	{
207 		if (s < 0 || s > sourceEnd || s + dir < 0 || s + dir > sourceEnd)
208 			continue;
209 
210 		t = FileArchives[s + dir];
211 		FileArchives[s + dir] = FileArchives[s];
212 		FileArchives[s] = t;
213 		r = true;
214 	}
215 	return r;
216 }
217 
218 
219 //! Adds an archive to the file system.
addFileArchive(const io::path & filename,bool ignoreCase,bool ignorePaths,E_FILE_ARCHIVE_TYPE archiveType,const core::stringc & password,IFileArchive ** retArchive)220 bool CFileSystem::addFileArchive(const io::path& filename, bool ignoreCase,
221 			  bool ignorePaths, E_FILE_ARCHIVE_TYPE archiveType,
222 			  const core::stringc& password,
223 			  IFileArchive** retArchive)
224 {
225 	IFileArchive* archive = 0;
226 	bool ret = false;
227 
228 	// see if archive is already added
229 	if (changeArchivePassword(filename, password, retArchive))
230 		return true;
231 
232 	s32 i;
233 
234 	// do we know what type it should be?
235 	if (archiveType == EFAT_UNKNOWN || archiveType == EFAT_FOLDER)
236 	{
237 		// try to load archive based on file name
238 		for (i = ArchiveLoader.size()-1; i >=0 ; --i)
239 		{
240 			if (ArchiveLoader[i]->isALoadableFileFormat(filename))
241 			{
242 				archive = ArchiveLoader[i]->createArchive(filename, ignoreCase, ignorePaths);
243 				if (archive)
244 					break;
245 			}
246 		}
247 
248 		// try to load archive based on content
249 		if (!archive)
250 		{
251 			io::IReadFile* file = createAndOpenFile(filename);
252 			if (file)
253 			{
254 				for (i = ArchiveLoader.size()-1; i >= 0; --i)
255 				{
256 					file->seek(0);
257 					if (ArchiveLoader[i]->isALoadableFileFormat(file))
258 					{
259 						file->seek(0);
260 						archive = ArchiveLoader[i]->createArchive(file, ignoreCase, ignorePaths);
261 						if (archive)
262 							break;
263 					}
264 				}
265 				file->drop();
266 			}
267 		}
268 	}
269 	else
270 	{
271 		// try to open archive based on archive loader type
272 
273 		io::IReadFile* file = 0;
274 
275 		for (i = ArchiveLoader.size()-1; i >= 0; --i)
276 		{
277 			if (ArchiveLoader[i]->isALoadableFileFormat(archiveType))
278 			{
279 				// attempt to open file
280 				if (!file)
281 					file = createAndOpenFile(filename);
282 
283 				// is the file open?
284 				if (file)
285 				{
286 					// attempt to open archive
287 					file->seek(0);
288 					if (ArchiveLoader[i]->isALoadableFileFormat(file))
289 					{
290 						file->seek(0);
291 						archive = ArchiveLoader[i]->createArchive(file, ignoreCase, ignorePaths);
292 						if (archive)
293 							break;
294 					}
295 				}
296 				else
297 				{
298 					// couldn't open file
299 					break;
300 				}
301 			}
302 		}
303 
304 		// if open, close the file
305 		if (file)
306 			file->drop();
307 	}
308 
309 	if (archive)
310 	{
311 		FileArchives.push_back(archive);
312 		if (password.size())
313 			archive->Password=password;
314 		if (retArchive)
315 			*retArchive = archive;
316 		ret = true;
317 	}
318 	else
319 	{
320 		os::Printer::log("Could not create archive for", filename, ELL_ERROR);
321 	}
322 
323 	_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
324 	return ret;
325 }
326 
327 // don't expose!
changeArchivePassword(const path & filename,const core::stringc & password,IFileArchive ** archive)328 bool CFileSystem::changeArchivePassword(const path& filename,
329 		const core::stringc& password,
330 		IFileArchive** archive)
331 {
332 	for (s32 idx = 0; idx < (s32)FileArchives.size(); ++idx)
333 	{
334 		// TODO: This should go into a path normalization method
335 		// We need to check for directory names with trailing slash and without
336 		const path absPath = getAbsolutePath(filename);
337 		const path arcPath = FileArchives[idx]->getFileList()->getPath();
338 		if ((absPath == arcPath) || ((absPath+_IRR_TEXT("/")) == arcPath))
339 		{
340 			if (password.size())
341 				FileArchives[idx]->Password=password;
342 			if (archive)
343 				*archive = FileArchives[idx];
344 			return true;
345 		}
346 	}
347 
348 	return false;
349 }
350 
addFileArchive(IReadFile * file,bool ignoreCase,bool ignorePaths,E_FILE_ARCHIVE_TYPE archiveType,const core::stringc & password,IFileArchive ** retArchive)351 bool CFileSystem::addFileArchive(IReadFile* file, bool ignoreCase,
352 		bool ignorePaths, E_FILE_ARCHIVE_TYPE archiveType,
353 		const core::stringc& password, IFileArchive** retArchive)
354 {
355 	if (!file || archiveType == EFAT_FOLDER)
356 		return false;
357 
358 	if (file)
359 	{
360 		if (changeArchivePassword(file->getFileName(), password, retArchive))
361 			return true;
362 
363 		IFileArchive* archive = 0;
364 		s32 i;
365 
366 		if (archiveType == EFAT_UNKNOWN)
367 		{
368 			// try to load archive based on file name
369 			for (i = ArchiveLoader.size()-1; i >=0 ; --i)
370 			{
371 				if (ArchiveLoader[i]->isALoadableFileFormat(file->getFileName()))
372 				{
373 					archive = ArchiveLoader[i]->createArchive(file, ignoreCase, ignorePaths);
374 					if (archive)
375 						break;
376 				}
377 			}
378 
379 			// try to load archive based on content
380 			if (!archive)
381 			{
382 				for (i = ArchiveLoader.size()-1; i >= 0; --i)
383 				{
384 					file->seek(0);
385 					if (ArchiveLoader[i]->isALoadableFileFormat(file))
386 					{
387 						file->seek(0);
388 						archive = ArchiveLoader[i]->createArchive(file, ignoreCase, ignorePaths);
389 						if (archive)
390 							break;
391 					}
392 				}
393 			}
394 		}
395 		else
396 		{
397 			// try to open archive based on archive loader type
398 			for (i = ArchiveLoader.size()-1; i >= 0; --i)
399 			{
400 				if (ArchiveLoader[i]->isALoadableFileFormat(archiveType))
401 				{
402 					// attempt to open archive
403 					file->seek(0);
404 					if (ArchiveLoader[i]->isALoadableFileFormat(file))
405 					{
406 						file->seek(0);
407 						archive = ArchiveLoader[i]->createArchive(file, ignoreCase, ignorePaths);
408 						if (archive)
409 							break;
410 					}
411 				}
412 			}
413 		}
414 
415 		if (archive)
416 		{
417 			FileArchives.push_back(archive);
418 			if (password.size())
419 				archive->Password=password;
420 			if (retArchive)
421 				*retArchive = archive;
422 			return true;
423 		}
424 		else
425 		{
426 			os::Printer::log("Could not create archive for", file->getFileName(), ELL_ERROR);
427 		}
428 	}
429 
430 	return false;
431 }
432 
433 
434 //! Adds an archive to the file system.
addFileArchive(IFileArchive * archive)435 bool CFileSystem::addFileArchive(IFileArchive* archive)
436 {
437 	for (u32 i=0; i < FileArchives.size(); ++i)
438 	{
439 		if (archive == FileArchives[i])
440 		{
441 			_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
442 			return false;
443 		}
444 	}
445 	FileArchives.push_back(archive);
446 	return true;
447 }
448 
449 
450 //! removes an archive from the file system.
removeFileArchive(u32 index)451 bool CFileSystem::removeFileArchive(u32 index)
452 {
453 	bool ret = false;
454 	if (index < FileArchives.size())
455 	{
456 		FileArchives[index]->drop();
457 		FileArchives.erase(index);
458 		ret = true;
459 	}
460 	_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
461 	return ret;
462 }
463 
464 
465 //! removes an archive from the file system.
removeFileArchive(const io::path & filename)466 bool CFileSystem::removeFileArchive(const io::path& filename)
467 {
468 	const path absPath = getAbsolutePath(filename);
469 	for (u32 i=0; i < FileArchives.size(); ++i)
470 	{
471 		if (absPath == FileArchives[i]->getFileList()->getPath())
472 			return removeFileArchive(i);
473 	}
474 	_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
475 	return false;
476 }
477 
478 
479 //! Removes an archive from the file system.
removeFileArchive(const IFileArchive * archive)480 bool CFileSystem::removeFileArchive(const IFileArchive* archive)
481 {
482 	for (u32 i=0; i < FileArchives.size(); ++i)
483 	{
484 		if (archive == FileArchives[i])
485 		{
486 			_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
487 			return removeFileArchive(i);
488 		}
489 	}
490 	_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
491 	return false;
492 }
493 
494 
495 //! gets an archive
getFileArchiveCount() const496 u32 CFileSystem::getFileArchiveCount() const
497 {
498 	return FileArchives.size();
499 }
500 
501 
getFileArchive(u32 index)502 IFileArchive* CFileSystem::getFileArchive(u32 index)
503 {
504 	return index < getFileArchiveCount() ? FileArchives[index] : 0;
505 }
506 
507 
508 //! Returns the string of the current working directory
getWorkingDirectory()509 const io::path& CFileSystem::getWorkingDirectory()
510 {
511 	EFileSystemType type = FileSystemType;
512 
513 	if (type != FILESYSTEM_NATIVE)
514 	{
515 		type = FILESYSTEM_VIRTUAL;
516 	}
517 	else
518 	{
519 		#if defined(_IRR_WINDOWS_CE_PLATFORM_)
520 		// does not need this
521 		#elif defined(_IRR_WINDOWS_API_)
522 			fschar_t tmp[_MAX_PATH];
523 			#if defined(_IRR_WCHAR_FILESYSTEM )
524 				_wgetcwd(tmp, _MAX_PATH);
525 				WorkingDirectory[FILESYSTEM_NATIVE] = tmp;
526 				WorkingDirectory[FILESYSTEM_NATIVE].replace(L'\\', L'/');
527 			#else
528 				_getcwd(tmp, _MAX_PATH);
529 				WorkingDirectory[FILESYSTEM_NATIVE] = tmp;
530 				WorkingDirectory[FILESYSTEM_NATIVE].replace('\\', '/');
531 			#endif
532 		#endif
533 
534 		#if (defined(_IRR_POSIX_API_) || defined(_IRR_OSX_PLATFORM_))
535 
536 			// getting the CWD is rather complex as we do not know the size
537 			// so try it until the call was successful
538 			// Note that neither the first nor the second parameter may be 0 according to POSIX
539 
540 			#if defined(_IRR_WCHAR_FILESYSTEM )
541 				u32 pathSize=256;
542 				wchar_t *tmpPath = new wchar_t[pathSize];
543 				while ((pathSize < (1<<16)) && !(wgetcwd(tmpPath,pathSize)))
544 				{
545 					delete [] tmpPath;
546 					pathSize *= 2;
547 					tmpPath = new char[pathSize];
548 				}
549 				if (tmpPath)
550 				{
551 					WorkingDirectory[FILESYSTEM_NATIVE] = tmpPath;
552 					delete [] tmpPath;
553 				}
554 			#else
555 				u32 pathSize=256;
556 				char *tmpPath = new char[pathSize];
557 				while ((pathSize < (1<<16)) && !(getcwd(tmpPath,pathSize)))
558 				{
559 					delete [] tmpPath;
560 					pathSize *= 2;
561 					tmpPath = new char[pathSize];
562 				}
563 				if (tmpPath)
564 				{
565 					WorkingDirectory[FILESYSTEM_NATIVE] = tmpPath;
566 					delete [] tmpPath;
567 				}
568 			#endif
569 		#endif
570 
571 		WorkingDirectory[type].validate();
572 	}
573 
574 	return WorkingDirectory[type];
575 }
576 
577 
578 //! Changes the current Working Directory to the given string.
changeWorkingDirectoryTo(const io::path & newDirectory)579 bool CFileSystem::changeWorkingDirectoryTo(const io::path& newDirectory)
580 {
581 	bool success=false;
582 
583 	if (FileSystemType != FILESYSTEM_NATIVE)
584 	{
585 		WorkingDirectory[FILESYSTEM_VIRTUAL] = newDirectory;
586 		// is this empty string constant really intended?
587 		flattenFilename(WorkingDirectory[FILESYSTEM_VIRTUAL], _IRR_TEXT(""));
588 		success = true;
589 	}
590 	else
591 	{
592 		WorkingDirectory[FILESYSTEM_NATIVE] = newDirectory;
593 
594 #if defined(_IRR_WINDOWS_CE_PLATFORM_)
595 		success = true;
596 #elif defined(_MSC_VER)
597 	#if defined(_IRR_WCHAR_FILESYSTEM)
598 		success = (_wchdir(newDirectory.c_str()) == 0);
599 	#else
600 		success = (_chdir(newDirectory.c_str()) == 0);
601 	#endif
602 #else
603     #if defined(_IRR_WCHAR_FILESYSTEM)
604 		success = (_wchdir(newDirectory.c_str()) == 0);
605     #else
606         success = (chdir(newDirectory.c_str()) == 0);
607     #endif
608 #endif
609 	}
610 
611 	return success;
612 }
613 
614 
getAbsolutePath(const io::path & filename) const615 io::path CFileSystem::getAbsolutePath(const io::path& filename) const
616 {
617 	if ( filename.empty() )
618 		return filename;
619 #if defined(_IRR_WINDOWS_CE_PLATFORM_)
620 	return filename;
621 #elif defined(_IRR_WINDOWS_API_)
622 	fschar_t *p=0;
623 	fschar_t fpath[_MAX_PATH];
624 	#if defined(_IRR_WCHAR_FILESYSTEM )
625 		p = _wfullpath(fpath, filename.c_str(), _MAX_PATH);
626 		core::stringw tmp(p);
627 		tmp.replace(L'\\', L'/');
628 	#else
629 		p = _fullpath(fpath, filename.c_str(), _MAX_PATH);
630 		core::stringc tmp(p);
631 		tmp.replace('\\', '/');
632 	#endif
633 	return tmp;
634 #elif (defined(_IRR_POSIX_API_) || defined(_IRR_OSX_PLATFORM_))
635 	c8* p=0;
636 	c8 fpath[4096];
637 	fpath[0]=0;
638 	p = realpath(filename.c_str(), fpath);
639 	if (!p)
640 	{
641 		// content in fpath is unclear at this point
642 		if (!fpath[0]) // seems like fpath wasn't altered, use our best guess
643 		{
644 			io::path tmp(filename);
645 			return flattenFilename(tmp);
646 		}
647 		else
648 			return io::path(fpath);
649 	}
650 	if (filename[filename.size()-1]=='/')
651 		return io::path(p)+_IRR_TEXT("/");
652 	else
653 		return io::path(p);
654 #else
655 	return io::path(filename);
656 #endif
657 }
658 
659 
660 //! returns the directory part of a filename, i.e. all until the first
661 //! slash or backslash, excluding it. If no directory path is prefixed, a '.'
662 //! is returned.
getFileDir(const io::path & filename) const663 io::path CFileSystem::getFileDir(const io::path& filename) const
664 {
665 	// find last forward or backslash
666 	s32 lastSlash = filename.findLast('/');
667 	const s32 lastBackSlash = filename.findLast('\\');
668 	lastSlash = lastSlash > lastBackSlash ? lastSlash : lastBackSlash;
669 
670 	if ((u32)lastSlash < filename.size())
671 		return filename.subString(0, lastSlash);
672 	else
673 		return _IRR_TEXT(".");
674 }
675 
676 
677 //! returns the base part of a filename, i.e. all except for the directory
678 //! part. If no directory path is prefixed, the full name is returned.
getFileBasename(const io::path & filename,bool keepExtension) const679 io::path CFileSystem::getFileBasename(const io::path& filename, bool keepExtension) const
680 {
681 	// find last forward or backslash
682 	s32 lastSlash = filename.findLast('/');
683 	const s32 lastBackSlash = filename.findLast('\\');
684 	lastSlash = core::max_(lastSlash, lastBackSlash);
685 
686 	// get number of chars after last dot
687 	s32 end = 0;
688 	if (!keepExtension)
689 	{
690 		// take care to search only after last slash to check only for
691 		// dots in the filename
692 		end = filename.findLast('.');
693 		if (end == -1 || end < lastSlash)
694 			end=0;
695 		else
696 			end = filename.size()-end;
697 	}
698 
699 	if ((u32)lastSlash < filename.size())
700 		return filename.subString(lastSlash+1, filename.size()-lastSlash-1-end);
701 	else if (end != 0)
702 		return filename.subString(0, filename.size()-end);
703 	else
704 		return filename;
705 }
706 
707 
708 //! flatten a path and file name for example: "/you/me/../." becomes "/you"
flattenFilename(io::path & directory,const io::path & root) const709 io::path& CFileSystem::flattenFilename(io::path& directory, const io::path& root) const
710 {
711 	directory.replace('\\', '/');
712 	if (directory.lastChar() != '/')
713 		directory.append('/');
714 
715 	io::path dir;
716 	io::path subdir;
717 
718 	s32 lastpos = 0;
719 	s32 pos = 0;
720 	bool lastWasRealDir=false;
721 
722 	while ((pos = directory.findNext('/', lastpos)) >= 0)
723 	{
724 		subdir = directory.subString(lastpos, pos - lastpos + 1);
725 
726 		if (subdir == _IRR_TEXT("../"))
727 		{
728 			if (lastWasRealDir)
729 			{
730 				deletePathFromPath(dir, 2);
731 				lastWasRealDir=(dir.size()!=0);
732 			}
733 			else
734 			{
735 				dir.append(subdir);
736 				lastWasRealDir=false;
737 			}
738 		}
739 		else if (subdir == _IRR_TEXT("/"))
740 		{
741 			dir = root;
742 		}
743 		else if (subdir != _IRR_TEXT("./"))
744 		{
745 			dir.append(subdir);
746 			lastWasRealDir=true;
747 		}
748 
749 		lastpos = pos + 1;
750 	}
751 	directory = dir;
752 	return directory;
753 }
754 
755 
756 //! Get the relative filename, relative to the given directory
getRelativeFilename(const path & filename,const path & directory) const757 path CFileSystem::getRelativeFilename(const path& filename, const path& directory) const
758 {
759 	if ( filename.empty() || directory.empty() )
760 		return filename;
761 
762 	io::path path1, file, ext;
763 	core::splitFilename(getAbsolutePath(filename), &path1, &file, &ext);
764 	io::path path2(getAbsolutePath(directory));
765 	core::list<io::path> list1, list2;
766 	path1.split(list1, _IRR_TEXT("/\\"), 2);
767 	path2.split(list2, _IRR_TEXT("/\\"), 2);
768 	u32 i=0;
769 	core::list<io::path>::ConstIterator it1,it2;
770 	it1=list1.begin();
771 	it2=list2.begin();
772 
773 	#if defined (_IRR_WINDOWS_API_)
774 	fschar_t partition1 = 0, partition2 = 0;
775 	io::path prefix1, prefix2;
776 	if ( it1 != list1.end() )
777 		prefix1 = *it1;
778 	if ( it2 != list2.end() )
779 		prefix2 = *it2;
780 	if ( prefix1.size() > 1 && prefix1[1] == _IRR_TEXT(':') )
781 		partition1 = core::locale_lower(prefix1[0]);
782 	if ( prefix2.size() > 1 && prefix2[1] == _IRR_TEXT(':') )
783 		partition2 = core::locale_lower(prefix2[0]);
784 
785 	// must have the same prefix or we can't resolve it to a relative filename
786 	if ( partition1 != partition2 )
787 	{
788 		return filename;
789 	}
790 	#endif
791 
792 
793 	for (; i<list1.size() && i<list2.size()
794 #if defined (_IRR_WINDOWS_API_)
795 		&& (io::path(*it1).make_lower()==io::path(*it2).make_lower())
796 #else
797 		&& (*it1==*it2)
798 #endif
799 		; ++i)
800 	{
801 		++it1;
802 		++it2;
803 	}
804 	path1=_IRR_TEXT("");
805 	for (; i<list2.size(); ++i)
806 		path1 += _IRR_TEXT("../");
807 	while (it1 != list1.end())
808 	{
809 		path1 += *it1++;
810 		path1 += _IRR_TEXT('/');
811 	}
812 	path1 += file;
813 	if (ext.size())
814 	{
815 		path1 += _IRR_TEXT('.');
816 		path1 += ext;
817 	}
818 	return path1;
819 }
820 
821 
822 //! Sets the current file systen type
setFileListSystem(EFileSystemType listType)823 EFileSystemType CFileSystem::setFileListSystem(EFileSystemType listType)
824 {
825 	EFileSystemType current = FileSystemType;
826 	FileSystemType = listType;
827 	return current;
828 }
829 
830 
831 //! Creates a list of files and directories in the current working directory
createFileList()832 IFileList* CFileSystem::createFileList()
833 {
834 	CFileList* r = 0;
835 	io::path Path = getWorkingDirectory();
836 	Path.replace('\\', '/');
837 	if (Path.lastChar() != '/')
838 		Path.append('/');
839 
840 	//! Construct from native filesystem
841 	if (FileSystemType == FILESYSTEM_NATIVE)
842 	{
843 		// --------------------------------------------
844 		//! Windows version
845 		#ifdef _IRR_WINDOWS_API_
846 		#if !defined ( _WIN32_WCE )
847 
848 		r = new CFileList(Path, true, false);
849 
850 		// TODO: Should be unified once mingw adapts the proper types
851 #if defined(__GNUC__)
852 		long hFile; //mingw return type declaration
853 #else
854 		intptr_t hFile;
855 #endif
856 
857 		struct _tfinddata_t c_file;
858 		if( (hFile = _tfindfirst( _T("*"), &c_file )) != -1L )
859 		{
860 			do
861 			{
862 				r->addItem(Path + c_file.name, 0, c_file.size, (_A_SUBDIR & c_file.attrib) != 0, 0);
863 			}
864 			while( _tfindnext( hFile, &c_file ) == 0 );
865 
866 			_findclose( hFile );
867 		}
868 		#endif
869 
870 		//TODO add drives
871 		//entry.Name = "E:\\";
872 		//entry.isDirectory = true;
873 		//Files.push_back(entry);
874 		#endif
875 
876 		// --------------------------------------------
877 		//! Linux version
878 		#if (defined(_IRR_POSIX_API_) || defined(_IRR_OSX_PLATFORM_))
879 
880 
881 		r = new CFileList(Path, false, false);
882 
883 		r->addItem(Path + _IRR_TEXT(".."), 0, 0, true, 0);
884 
885 		//! We use the POSIX compliant methods instead of scandir
886 		DIR* dirHandle=opendir(Path.c_str());
887 		if (dirHandle)
888 		{
889 			struct dirent *dirEntry;
890 			while ((dirEntry=readdir(dirHandle)))
891 			{
892 				u32 size = 0;
893 				bool isDirectory = false;
894 
895 				if((strcmp(dirEntry->d_name, ".")==0) ||
896 				   (strcmp(dirEntry->d_name, "..")==0))
897 				{
898 					continue;
899 				}
900 				struct stat buf;
901 				if (stat(dirEntry->d_name, &buf)==0)
902 				{
903 					size = buf.st_size;
904 					isDirectory = S_ISDIR(buf.st_mode);
905 				}
906 				#if !defined(_IRR_SOLARIS_PLATFORM_) && !defined(__CYGWIN__)
907 				// only available on some systems
908 				else
909 				{
910 					isDirectory = dirEntry->d_type == DT_DIR;
911 				}
912 				#endif
913 
914 				r->addItem(Path + dirEntry->d_name, 0, size, isDirectory, 0);
915 			}
916 			closedir(dirHandle);
917 		}
918 		#endif
919 	}
920 	else
921 	{
922 		//! create file list for the virtual filesystem
923 		r = new CFileList(Path, false, false);
924 
925 		//! add relative navigation
926 		SFileListEntry e2;
927 		SFileListEntry e3;
928 
929 		//! PWD
930 		r->addItem(Path + _IRR_TEXT("."), 0, 0, true, 0);
931 
932 		//! parent
933 		r->addItem(Path + _IRR_TEXT(".."), 0, 0, true, 0);
934 
935 		//! merge archives
936 		for (u32 i=0; i < FileArchives.size(); ++i)
937 		{
938 			const IFileList *merge = FileArchives[i]->getFileList();
939 
940 			for (u32 j=0; j < merge->getFileCount(); ++j)
941 			{
942 				if (core::isInSameDirectory(Path, merge->getFullFileName(j)) == 0)
943 				{
944 					r->addItem(merge->getFullFileName(j), merge->getFileOffset(j), merge->getFileSize(j), merge->isDirectory(j), 0);
945 				}
946 			}
947 		}
948 	}
949 
950 	if (r)
951 		r->sort();
952 	return r;
953 }
954 
955 //! Creates an empty filelist
createEmptyFileList(const io::path & path,bool ignoreCase,bool ignorePaths)956 IFileList* CFileSystem::createEmptyFileList(const io::path& path, bool ignoreCase, bool ignorePaths)
957 {
958 	return new CFileList(path, ignoreCase, ignorePaths);
959 }
960 
961 
962 //! determines if a file exists and would be able to be opened.
existFile(const io::path & filename) const963 bool CFileSystem::existFile(const io::path& filename) const
964 {
965 	for (u32 i=0; i < FileArchives.size(); ++i)
966 		if (FileArchives[i]->getFileList()->findFile(filename)!=-1)
967 			return true;
968 
969 #if defined(_IRR_WINDOWS_CE_PLATFORM_)
970 #if defined(_IRR_WCHAR_FILESYSTEM)
971 	HANDLE hFile = CreateFileW(filename.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
972 #else
973 	HANDLE hFile = CreateFileW(core::stringw(filename).c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
974 #endif
975 	if (hFile == INVALID_HANDLE_VALUE)
976 		return false;
977 	else
978 	{
979 		CloseHandle(hFile);
980 		return true;
981 	}
982 #else
983 	_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
984 #if defined(_MSC_VER)
985     #if defined(_IRR_WCHAR_FILESYSTEM)
986         return (_waccess(filename.c_str(), 0) != -1);
987     #else
988         return (_access(filename.c_str(), 0) != -1);
989     #endif
990 #elif defined(F_OK)
991     #if defined(_IRR_WCHAR_FILESYSTEM)
992         return (_waccess(filename.c_str(), F_OK) != -1);
993     #else
994         return (access(filename.c_str(), F_OK) != -1);
995 	#endif
996 #else
997     return (access(filename.c_str(), 0) != -1);
998 #endif
999 #endif
1000 }
1001 
1002 
1003 //! Creates a XML Reader from a file.
createXMLReader(const io::path & filename)1004 IXMLReader* CFileSystem::createXMLReader(const io::path& filename)
1005 {
1006 	IReadFile* file = createAndOpenFile(filename);
1007 	if (!file)
1008 		return 0;
1009 
1010 	IXMLReader* reader = createXMLReader(file);
1011 	file->drop();
1012 	return reader;
1013 }
1014 
1015 
1016 //! Creates a XML Reader from a file.
createXMLReader(IReadFile * file)1017 IXMLReader* CFileSystem::createXMLReader(IReadFile* file)
1018 {
1019 	if (!file)
1020 		return 0;
1021 
1022 	return createIXMLReader(file);
1023 }
1024 
1025 
1026 //! Creates a XML Reader from a file.
createXMLReaderUTF8(const io::path & filename)1027 IXMLReaderUTF8* CFileSystem::createXMLReaderUTF8(const io::path& filename)
1028 {
1029 	IReadFile* file = createAndOpenFile(filename);
1030 	if (!file)
1031 		return 0;
1032 
1033 	IXMLReaderUTF8* reader = createIXMLReaderUTF8(file);
1034 	file->drop();
1035 	return reader;
1036 }
1037 
1038 
1039 //! Creates a XML Reader from a file.
createXMLReaderUTF8(IReadFile * file)1040 IXMLReaderUTF8* CFileSystem::createXMLReaderUTF8(IReadFile* file)
1041 {
1042 	if (!file)
1043 		return 0;
1044 
1045 	return createIXMLReaderUTF8(file);
1046 }
1047 
1048 
1049 //! Creates a XML Writer from a file.
createXMLWriter(const io::path & filename)1050 IXMLWriter* CFileSystem::createXMLWriter(const io::path& filename)
1051 {
1052 	IWriteFile* file = createAndWriteFile(filename);
1053 	IXMLWriter* writer = 0;
1054 	if (file)
1055 	{
1056 		writer = createXMLWriter(file);
1057 		file->drop();
1058 	}
1059 	return writer;
1060 }
1061 
1062 
1063 //! Creates a XML Writer from a file.
createXMLWriter(IWriteFile * file)1064 IXMLWriter* CFileSystem::createXMLWriter(IWriteFile* file)
1065 {
1066 	return new CXMLWriter(file);
1067 }
1068 
1069 
1070 //! creates a filesystem which is able to open files from the ordinary file system,
1071 //! and out of zipfiles, which are able to be added to the filesystem.
createFileSystem()1072 IFileSystem* createFileSystem()
1073 {
1074 	return new CFileSystem();
1075 }
1076 
1077 
1078 //! Creates a new empty collection of attributes, usable for serialization and more.
createEmptyAttributes(video::IVideoDriver * driver)1079 IAttributes* CFileSystem::createEmptyAttributes(video::IVideoDriver* driver)
1080 {
1081 	return new CAttributes(driver);
1082 }
1083 
1084 
1085 } // end namespace irr
1086 } // end namespace io
1087