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