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