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