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