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