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