1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010-2020 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2012-2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
6 // Copyright (C) 2014-2015 Laszlo KIS-ADAM (dfighter) <dfighter1985@gmail.com>
8 // This program is free software: you can redistribute it and/or modify
9 // it under the terms of the GNU Affero General Public License as
10 // published by the Free Software Foundation, either version 3 of the
11 // License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU Affero General Public License for more details.
18 // You should have received a copy of the GNU Affero General Public License
19 // along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "nel/misc/path.h"
25 #include "nel/misc/big_file.h"
26 #include "nel/misc/hierarchical_timer.h"
27 #include "nel/misc/progress_callback.h"
28 #include "nel/misc/file.h"
29 #include "nel/misc/xml_pack.h"
30 #include "nel/misc/streamed_package_manager.h"
33 # include <sys/types.h>
34 # include <sys/stat.h>
38 # include <sys/types.h>
39 # include <sys/stat.h>
42 # include <sys/types.h>
43 # include <sys/stat.h>
48 # include <sys/types.h>
50 #endif // NL_OS_WINDOWS
64 // Use this define if you want to display info about the CPath.
65 //#define NL_DEBUG_PATH
68 # define NL_DISPLAY_PATH nlinfo
71 # define NL_DISPLAY_PATH(format, args...)
73 # define NL_DISPLAY_PATH if(false)
82 NLMISC_SAFE_SINGLETON_IMPL(CPath
);
89 CFileContainer::~CFileContainer()
93 delete[] _AllFileNames
;
98 void CPath::releaseInstance()
102 NLMISC::INelContext::getInstance().releaseSingletonPointer("CPath", _Instance
);
109 void CPath::getFileList(const std::string
&extension
, std::vector
<std::string
> &filenames
)
111 getInstance()->_FileContainer
.getFileList(extension
, filenames
);
114 void CFileContainer::getFileList(const std::string
&extension
, std::vector
<std::string
> &filenames
)
116 if (!_MemoryCompressed
)
118 TFiles::iterator
first(_Files
.begin()), last(_Files
.end());
120 if( !extension
.empty() )
122 for (; first
!= last
; ++ first
)
124 string ext
= SSMext
.get(first
->second
.idExt
);
125 if (ext
== extension
)
127 filenames
.push_back(first
->first
);
131 // if extension is empty we keep all files
134 for (; first
!= last
; ++ first
)
136 filenames
.push_back(first
->first
);
142 // compressed memory version
143 std::vector
<CFileContainer::CMCFileEntry
>::iterator
first(_MCFiles
.begin()), last(_MCFiles
.end());
145 if( !extension
.empty() )
147 for (; first
!= last
; ++ first
)
149 string ext
= SSMext
.get(first
->idExt
);
150 if (ext
== extension
)
152 filenames
.push_back(first
->Name
);
156 // if extension is empty we keep all files
159 for (; first
!= last
; ++ first
)
161 filenames
.push_back(first
->Name
);
167 void CPath::getFileListByName(const std::string
&extension
, const std::string
&name
, std::vector
<std::string
> &filenames
)
169 getInstance()->_FileContainer
.getFileListByName(extension
, name
, filenames
);
172 void CFileContainer::getFileListByName(const std::string
&extension
, const std::string
&name
, std::vector
<std::string
> &filenames
)
174 if (!_MemoryCompressed
)
176 TFiles::iterator
first(_Files
.begin()), last(_Files
.end());
180 for (; first
!= last
; ++ first
)
182 string ext
= SSMext
.get(first
->second
.idExt
);
183 if (first
->first
.find(name
) != string::npos
&& (ext
== extension
|| extension
.empty()))
185 filenames
.push_back(first
->first
);
189 // if extension is empty we keep all files
192 for (; first
!= last
; ++ first
)
194 filenames
.push_back(first
->first
);
200 // compressed memory version
201 std::vector
<CFileContainer::CMCFileEntry
>::iterator
first(_MCFiles
.begin()), last(_MCFiles
.end());
205 for (; first
!= last
; ++ first
)
207 string ext
= SSMext
.get(first
->idExt
);
208 if (strstr(first
->Name
, name
.c_str()) != NULL
&& (ext
== extension
|| extension
.empty()))
210 filenames
.push_back(first
->Name
);
214 // if extension is empty we keep all files
217 for (; first
!= last
; ++ first
)
219 filenames
.push_back(first
->Name
);
225 void CPath::getFileListByPath(const std::string
&extension
, const std::string
&path
, std::vector
<std::string
> &filenames
)
227 getInstance()->_FileContainer
.getFileListByPath(extension
, path
, filenames
);
230 void CFileContainer::getFileListByPath(const std::string
&extension
, const std::string
&path
, std::vector
<std::string
> &filenames
)
232 if (!_MemoryCompressed
)
234 TFiles::iterator
first(_Files
.begin()), last(_Files
.end());
238 for (; first
!= last
; ++ first
)
240 string ext
= SSMext
.get(first
->second
.idExt
);
241 string p
= SSMpath
.get(first
->second
.idPath
);
242 if (p
.find(path
) != string::npos
&& (ext
== extension
|| extension
.empty()))
244 filenames
.push_back(first
->first
);
248 // if extension is empty we keep all files
251 for (; first
!= last
; ++ first
)
253 filenames
.push_back(first
->first
);
259 // compressed memory version
260 std::vector
<CFileContainer::CMCFileEntry
>::iterator
first(_MCFiles
.begin()), last(_MCFiles
.end());
264 for (; first
!= last
; ++ first
)
266 string ext
= SSMext
.get(first
->idExt
);
267 string p
= SSMpath
.get(first
->idPath
);
269 if (strstr(p
.c_str(), path
.c_str()) != NULL
&& (ext
== extension
|| extension
.empty()))
271 filenames
.push_back(first
->Name
);
275 // if extension is empty we keep all files
278 for (; first
!= last
; ++ first
)
280 filenames
.push_back(first
->Name
);
286 void CPath::clearMap ()
288 getInstance()->_FileContainer
.clearMap();
291 void CFileContainer::clearMap ()
293 nlassert(!_MemoryCompressed
);
295 CBigFile::getInstance().removeAll ();
296 CStreamedPackageManager::getInstance().unloadAll ();
297 NL_DISPLAY_PATH("PATH: CPath::clearMap(): map directory cleared");
300 CFileContainer::CMCFileEntry
*CFileContainer::MCfind (const std::string
&filename
)
302 nlassert(_MemoryCompressed
);
303 vector
<CMCFileEntry
>::iterator it
;
304 CMCFileEntry temp_cmc_file
;
305 temp_cmc_file
.Name
= (char*)filename
.c_str();
306 it
= lower_bound(_MCFiles
.begin(), _MCFiles
.end(), temp_cmc_file
, CMCFileComp());
307 if (it
!= _MCFiles
.end())
309 CMCFileComp FileComp
;
310 if (FileComp
.specialCompare(*it
, filename
.c_str()) == 0)
316 sint
CFileContainer::findExtension (const string
&ext1
, const string
&ext2
)
318 for (uint i
= 0; i
< _Extensions
.size (); i
++)
320 if (_Extensions
[i
].first
== ext1
&& _Extensions
[i
].second
== ext2
)
328 void CPath::remapExtension (const string
&ext1
, const string
&ext2
, bool substitute
)
330 getInstance()->_FileContainer
.remapExtension(ext1
, ext2
, substitute
);
333 void CFileContainer::remapExtension (const string
&ext1
, const string
&ext2
, bool substitute
)
335 nlassert(!_MemoryCompressed
);
337 string ext1lwr
= toLowerAscii(ext1
);
338 string ext2lwr
= toLowerAscii(ext2
);
340 if (ext1lwr
.empty() || ext2lwr
.empty())
342 nlwarning ("PATH: CPath::remapExtension(%s, %s, %d): can't remap empty extension", ext1lwr
.c_str(), ext2lwr
.c_str(), substitute
);
345 if (ext1lwr
== "bnp" || ext2lwr
== "bnp" || ext1lwr
== "snp" || ext2lwr
== "snp")
347 nlwarning ("PATH: CPath::remapExtension(%s, %s, %d): you can't remap a big file", ext1lwr
.c_str(), ext2lwr
.c_str(), substitute
);
352 // remove the mapping from the mapping list
353 sint n
= findExtension (ext1lwr
, ext2lwr
);
355 _Extensions
.erase (_Extensions
.begin() + n
);
357 // remove mapping in the map
358 TFiles::iterator it
= _Files
.begin();
359 TFiles::iterator nit
= it
;
360 while (it
!= _Files
.end ())
363 string ext
= SSMext
.get((*it
).second
.idExt
);
364 if ((*it
).second
.Remapped
&& ext
== ext2lwr
)
370 NL_DISPLAY_PATH("PATH: CPath::remapExtension(%s, %s, %d): extension removed", ext1lwr
.c_str(), ext2lwr
.c_str(), substitute
);
374 sint n
= findExtension (ext1lwr
, ext2lwr
);
377 nlwarning ("PATH: CPath::remapExtension(%s, %s, %d): remapping already set", ext1lwr
.c_str(), ext2lwr
.c_str(), substitute
);
381 // adding mapping into the mapping list
382 _Extensions
.push_back (make_pair (ext1lwr
, ext2lwr
));
384 // adding mapping into the map
385 vector
<string
> newFiles
;
386 TFiles::iterator it
= _Files
.begin();
387 while (it
!= _Files
.end ())
389 string ext
= SSMext
.get((*it
).second
.idExt
);
390 if (!(*it
).second
.Remapped
&& ext
== ext1lwr
)
392 // find if already exist
393 string::size_type pos
= (*it
).first
.find_last_of (".");
394 if (pos
!= string::npos
)
396 string file
= (*it
).first
.substr (0, pos
+ 1);
399 // TODO perhaps a problem because I insert in the current map that I process
400 string path
= SSMpath
.get((*it
).second
.idPath
);
401 insertFileInMap (file
, path
+file
, true, ext1lwr
);
406 NL_DISPLAY_PATH("PATH: CPath::remapExtension(%s, %s, %d): extension added", ext1lwr
.c_str(), ext2lwr
.c_str(), substitute
);
410 // ***************************************************************************
411 void CPath::remapFile (const std::string
&file1
, const std::string
&file2
)
413 getInstance()->_FileContainer
.remapFile(file1
, file2
);
416 void CFileContainer::remapFile (const std::string
&file1
, const std::string
&file2
)
418 if (file1
.empty()) return;
419 if (file2
.empty()) return;
420 _RemappedFiles
[toLowerAscii(file1
)] = toLowerAscii(file2
);
423 // ***************************************************************************
424 static void removeAllUnusedChar(string
&str
)
427 while (!str
.empty() && (i
!= str
.size()))
429 if ((str
[i
] == ' ' || str
[i
] == '\t' || str
[i
] == '\r' || str
[i
] == '\n'))
430 str
.erase(str
.begin()+i
);
436 // ***************************************************************************
437 void CPath::loadRemappedFiles (const std::string
&file
)
439 getInstance()->_FileContainer
.loadRemappedFiles(file
);
442 void CFileContainer::loadRemappedFiles (const std::string
&file
)
444 string fullName
= lookup(file
, false, true, true);
446 f
.setCacheFileOnOpen (true);
448 if (!f
.open (fullName
))
456 f
.getline(sTmp
, 512);
458 std::string::size_type pos
= str
.find(',');
459 if (pos
!= string::npos
)
461 removeAllUnusedChar(str
);
465 remapFile( str
.substr(0,pos
), str
.substr(pos
+1, str
.size()) );
472 // ***************************************************************************
473 string
CPath::lookup (const string
&filename
, bool throwException
, bool displayWarning
, bool lookupInLocalDirectory
)
475 return getInstance()->_FileContainer
.lookup(filename
, throwException
, displayWarning
, lookupInLocalDirectory
);
478 string
CFileContainer::lookup (const string
&filename
, bool throwException
, bool displayWarning
, bool lookupInLocalDirectory
)
480 /* ***********************************************
481 * WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance
482 * It can be loaded/called through CAsyncFileManager for instance
483 * ***********************************************/
485 NB: CPath access static instance getInstance() of course, so user must ensure
486 that no mutator is called while async loading
490 // If the file already contains a @, it means that a lookup already proceed and returning a big file, do nothing
491 if (filename
.find ("@") != string::npos
)
493 NL_DISPLAY_PATH("PATH: CPath::lookup(%s): already found", filename
.c_str());
497 // Try to find in the map directories
499 // If filename contains a path, we get only the filename to look inside paths
500 string str
= CFile::getFilename(toLowerAscii(filename
));
503 while ((!str
.empty()) && (str
[str
.size()-1] == ' '))
505 str
.resize (str
.size()-1);
508 map
<string
, string
>::iterator itss
= _RemappedFiles
.find(str
);
509 if (itss
!= _RemappedFiles
.end())
512 if (_MemoryCompressed
)
514 CMCFileEntry
*pMCFE
= MCfind(str
);
515 // If found in the map, returns it
518 string fname
, path
= SSMpath
.get(pMCFE
->idPath
);
520 fname
= CFile::getFilenameWithoutExtension(pMCFE
->Name
) + "." + SSMext
.get(pMCFE
->idExt
);
524 NL_DISPLAY_PATH("PATH: CPath::lookup(%s): found in the map directory: '%s'", fname
.c_str(), path
.c_str());
528 else // NOT memory compressed
531 TFiles::iterator it
= _Files
.find (str
);
532 // If found in the map, returns it
533 if (it
!= _Files
.end())
535 string fname
, path
= SSMpath
.get((*it
).second
.idPath
);
536 if (it
->second
.Remapped
)
537 fname
= CFile::getFilenameWithoutExtension((*it
).second
.Name
) + "." + SSMext
.get((*it
).second
.idExt
);
539 fname
= (*it
).second
.Name
;
541 NL_DISPLAY_PATH("PATH: CPath::lookup(%s): found in the map directory: '%s'", fname
.c_str(), path
.c_str());
547 // Try to find in the alternative directories
548 for (uint i
= 0; i
< _AlternativePaths
.size(); i
++)
550 string s
= _AlternativePaths
[i
] + str
;
551 if ( CFile::fileExists(s
) )
553 NL_DISPLAY_PATH("PATH: CPath::lookup(%s): found in the alternative directory: '%s'", str
.c_str(), s
.c_str());
557 // try with the remapping
558 for (uint j
= 0; j
< _Extensions
.size(); j
++)
560 if (toLowerAscii(CFile::getExtension (str
)) == _Extensions
[j
].second
)
562 string rs
= _AlternativePaths
[i
] + CFile::getFilenameWithoutExtension (filename
) + "." + _Extensions
[j
].first
;
563 if ( CFile::fileExists(rs
) )
565 NL_DISPLAY_PATH("PATH: CPath::lookup(%s): found in the alternative directory: '%s'", str
.c_str(), rs
.c_str());
572 // Try to find in the current directory
573 if ( lookupInLocalDirectory
&& CFile::fileExists(filename
) )
575 NL_DISPLAY_PATH("PATH: CPath::lookup(%s): found in the current directory: '%s'", str
.c_str(), filename
.c_str());
583 nlwarning ("PATH: Try to lookup for an empty filename. TODO: check why.");
585 nlwarning ("PATH: File (%s) not found (%s)", str
.c_str(), filename
.c_str());
589 throw EPathNotFound (filename
);
594 bool CPath::exists (const std::string
&filename
)
596 return getInstance()->_FileContainer
.exists(filename
);
599 bool CFileContainer::exists (const std::string
&filename
)
601 // Try to find in the map directories
602 string str
= toLowerAscii(filename
);
605 while ((!str
.empty()) && (str
[str
.size()-1] == ' '))
607 str
.resize (str
.size()-1);
610 if (_MemoryCompressed
)
612 CMCFileEntry
*pMCFE
= MCfind(str
);
613 // If found in the vector, returns it
619 TFiles::iterator it
= _Files
.find (str
);
620 // If found in the map, returns it
621 if (it
!= _Files
.end())
628 string
CPath::standardizePath (const string
&path
, bool addFinalSlash
)
630 return getInstance()->_FileContainer
.standardizePath(path
, addFinalSlash
);
633 string
CFileContainer::standardizePath (const string
&path
, bool addFinalSlash
)
639 string
newPath(path
);
641 for (uint i
= 0; i
< path
.size(); i
++)
643 // don't transform the first \\ for windows network path
648 // add terminal slash
649 if (addFinalSlash
&& newPath
[path
.size()-1] != '/')
655 // replace / with backslash
656 std::string
CPath::standardizeDosPath (const std::string
&path
)
658 return getInstance()->_FileContainer
.standardizeDosPath(path
);
661 std::string
CFileContainer::standardizeDosPath (const std::string
&path
)
665 for (uint i
= 0; i
< path
.size(); i
++)
669 // Yoyo: supress toLower. Not useful!?!
670 /*else if (isupper(path[i]))
671 newPath += tolower(path[i]);*/
676 if (CFile::isExists(path
) && CFile::isDirectory(path
) && newPath
[newPath
.size()-1] != '\\')
683 std::string
CPath::getCurrentPath ()
685 return getInstance()->_FileContainer
.getCurrentPath();
688 std::string
CFileContainer::getCurrentPath ()
691 wchar_t buffer
[1024];
692 return standardizePath(wideToUtf8(_wgetcwd(buffer
, 1024)), false);
695 return standardizePath(getcwd(buffer
, 1024), false);
699 bool CPath::setCurrentPath (const std::string
&path
)
701 return getInstance()->_FileContainer
.setCurrentPath(path
);
704 bool CFileContainer::setCurrentPath (const std::string
&path
)
707 //nldebug("Change current path to '%s' (current path is '%s')", path.c_str(), getCurrentPath().c_str());
709 res
= _wchdir(nlUtf8ToWide(path
));
711 res
= chdir(path
.c_str());
713 if(res
!= 0) nlwarning("Cannot change current path to '%s' (current path is '%s') res: %d errno: %d (%s)", path
.c_str(), getCurrentPath().c_str(), res
, errno
, strerror(errno
));
717 std::string
CPath::getFullPath (const std::string
&path
, bool addFinalSlash
)
719 return getInstance()->_FileContainer
.getFullPath(path
, addFinalSlash
);
722 std::string
CFileContainer::getFullPath (const std::string
&path
, bool addFinalSlash
)
724 string currentPath
= standardizePath (getCurrentPath ());
725 string sPath
= standardizePath (path
, addFinalSlash
);
728 if (path
.empty() || sPath
== "." || sPath
== "./")
734 if (path
.size() >= 2 && path
[1] == ':')
739 if (path
.size() >= 2 && (path
[0] == '/' || path
[0] == '\\') && (path
[1] == '/' || path
[1] == '\\'))
746 if (path
[0] == '/' || path
[0] == '\\')
748 if (currentPath
.size() > 2 && currentPath
[1] == ':')
750 return currentPath
.substr(0,3) + sPath
.substr(1);
759 return currentPath
+ sPath
;
765 # define dirent WIN32_FIND_DATAW
769 static WIN32_FIND_DATAW findData
;
772 DIR *opendir (const char *path
)
774 nlassert (path
!= NULL
);
775 nlassert (path
[0] != '\0');
777 if (!CFile::isDirectory(path
))
787 int closedir (DIR *dir
)
793 dirent
*readdir (DIR *dir
)
795 // FIX : call to SetCurrentDirectory() and SetCurrentDirectory() removed to improve speed
796 nlassert (!sDir
.empty());
798 // first visit in this directory : FindFirstFile()
801 hFind
= FindFirstFileW(nlUtf8ToWide(CPath::standardizePath(sDir
) + "*"), &findData
);
803 // directory already visited : FindNextFile()
806 if (!FindNextFileW (hFind
, &findData
))
814 #endif // NL_OS_WINDOWS
816 #ifndef NL_OS_WINDOWS
817 string BasePathgetPathContent
;
820 bool isdirectory (dirent
*de
)
822 nlassert (de
!= NULL
);
824 return ((de
->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) != 0) && ((de
->dwFileAttributes
& FILE_ATTRIBUTE_SYSTEM
) == 0);
826 //nlinfo ("isdirectory filename %s -> 0x%08x", de->d_name, de->d_type);
827 // we can't use "de->d_type & DT_DIR" because it s always NULL on libc2.1
828 //return (de->d_type & DT_DIR) != 0;
830 return CFile::isDirectory (BasePathgetPathContent
+ de
->d_name
);
832 #endif // NL_OS_WINDOWS
835 bool isfile (dirent
*de
)
837 nlassert (de
!= NULL
);
839 return ((de
->dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) == 0) && ((de
->dwFileAttributes
& FILE_ATTRIBUTE_SYSTEM
) == 0);
841 // we can't use "de->d_type & DT_DIR" because it s always NULL on libc2.1
842 //return (de->d_type & DT_DIR) == 0;
844 return !CFile::isDirectory (BasePathgetPathContent
+ de
->d_name
);
846 #endif // NL_OS_WINDOWS
849 string
getname (dirent
*de
)
851 nlassert (de
!= NULL
);
853 return wideToUtf8(de
->cFileName
);
856 #endif // NL_OS_WINDOWS
859 void CPath::getPathContent (const string
&path
, bool recurse
, bool wantDir
, bool wantFile
, vector
<string
> &result
, class IProgressCallback
*progressCallBack
, bool showEverything
)
861 getInstance()->_FileContainer
.getPathContent(path
, recurse
, wantDir
, wantFile
, result
, progressCallBack
, showEverything
);
863 sort(result
.begin(), result
.end());
866 void CFileContainer::getPathContent (const string
&path
, bool recurse
, bool wantDir
, bool wantFile
, vector
<string
> &result
, class IProgressCallback
*progressCallBack
, bool showEverything
)
870 NL_DISPLAY_PATH("PATH: CPath::getPathContent(): Empty input Path");
874 #ifndef NL_OS_WINDOWS
875 BasePathgetPathContent
= CPath::standardizePath (path
);
878 DIR *dir
= opendir (path
.c_str());
882 NL_DISPLAY_PATH("PATH: CPath::getPathContent(%s, %d, %d, %d): could not open the directory", path
.c_str(), recurse
, wantDir
, wantFile
);
886 // contains path that we have to recurs into
887 vector
<string
> recursPath
;
891 dirent
*de
= readdir(dir
);
894 NL_DISPLAY_PATH("PATH: CPath::getPathContent(%s, %d, %d, %d): end of directory", path
.c_str(), recurse
, wantDir
, wantFile
);
898 string fn
= getname (de
);
901 if (fn
== "." || fn
== "..")
906 // skip CVS, .svn and .hg directory
907 if ((!showEverything
) && (fn
== "CVS" || fn
== ".svn" || fn
== ".hg" || fn
== ".git"))
909 NL_DISPLAY_PATH("PATH: CPath::getPathContent(%s, %d, %d, %d): skip '%s' directory", path
.c_str(), recurse
, wantDir
, wantFile
, fn
.c_str());
913 string stdName
= standardizePath(standardizePath(path
) + fn
);
916 NL_DISPLAY_PATH("PATH: CPath::getPathContent(%s, %d, %d, %d): need to recurse into '%s'", path
.c_str(), recurse
, wantDir
, wantFile
, stdName
.c_str());
917 recursPath
.push_back (stdName
);
922 NL_DISPLAY_PATH("PATH: CPath::getPathContent(%s, %d, %d, %d): adding path '%s'", path
.c_str(), recurse
, wantDir
, wantFile
, stdName
.c_str());
923 result
.push_back (stdName
);
927 if (wantFile
&& isfile(de
))
929 if ( (!showEverything
) && (fn
.size() >= 4 && fn
.substr (fn
.size()-4) == ".log"))
931 NL_DISPLAY_PATH("PATH: CPath::getPathContent(%s, %d, %d, %d): skip *.log files (%s)", path
.c_str(), recurse
, wantDir
, wantFile
, fn
.c_str());
935 string stdName
= standardizePath(path
) + getname(de
);
937 NL_DISPLAY_PATH("PATH: CPath::getPathContent(%s, %d, %d, %d): adding file '%s'", path
.c_str(), recurse
, wantDir
, wantFile
, stdName
.c_str());
938 result
.push_back (stdName
);
944 #ifndef NL_OS_WINDOWS
945 BasePathgetPathContent
.clear();
949 for (uint i
= 0; i
< recursPath
.size (); i
++)
952 if (progressCallBack
)
954 progressCallBack
->progress ((float)i
/(float)recursPath
.size ());
955 progressCallBack
->pushCropedValues ((float)i
/(float)recursPath
.size (), (float)(i
+1)/(float)recursPath
.size ());
958 getPathContent (recursPath
[i
], recurse
, wantDir
, wantFile
, result
, progressCallBack
, showEverything
);
961 if (progressCallBack
)
963 progressCallBack
->popCropedValues ();
968 void CPath::removeAllAlternativeSearchPath ()
970 getInstance()->_FileContainer
.removeAllAlternativeSearchPath();
973 void CFileContainer::removeAllAlternativeSearchPath ()
975 _AlternativePaths
.clear ();
976 NL_DISPLAY_PATH("PATH: CPath::RemoveAllAternativeSearchPath(): removed");
980 void CPath::addSearchPath (const string
&path
, bool recurse
, bool alternative
, class IProgressCallback
*progressCallBack
)
982 getInstance()->_FileContainer
.addSearchPath(path
, recurse
, alternative
, progressCallBack
);
985 void CFileContainer::addSearchPath (const string
&path
, bool recurse
, bool alternative
, class IProgressCallback
*progressCallBack
)
987 //H_AUTO_INST(addSearchPath);
989 nlassert(!_MemoryCompressed
);
991 // check empty directory
994 nlwarning ("PATH: CPath::addSearchPath(%s, %s, %s): can't add empty directory, skip it",
996 recurse
? "recursive" : "not recursive",
997 alternative
? "alternative" : "not alternative");
1001 // check if it s a directory
1002 if (!CFile::isDirectory (path
))
1004 nlinfo ("PATH: CPath::addSearchPath(%s, %s, %s): '%s' is not a directory, I'll call addSearchFile()",
1006 recurse
? "recursive" : "not recursive",
1007 alternative
? "alternative" : "not alternative",
1009 addSearchFile (path
, false, "", progressCallBack
);
1013 string newPath
= standardizePath(path
);
1015 // check if it s a directory
1016 if (!CFile::isExists (newPath
))
1018 nlwarning ("PATH: CPath::addSearchPath(%s, %s, %s): '%s' is not found, skip it",
1020 recurse
? "recursive" : "not recursive",
1021 alternative
? "alternative" : "not alternative",
1026 nlinfo ("PATH: CPath::addSearchPath(%s, %d, %d): adding the path '%s'", path
.c_str(), recurse
, alternative
, newPath
.c_str());
1028 NL_DISPLAY_PATH("PATH: CPath::addSearchPath(%s, %d, %d): try to add '%s'", path
.c_str(), recurse
, alternative
, newPath
.c_str());
1032 vector
<string
> pathsToProcess
;
1034 // add the current path
1035 pathsToProcess
.push_back (newPath
);
1039 // find all path and subpath
1040 getPathContent (newPath
, recurse
, true, false, pathsToProcess
, progressCallBack
);
1043 sort(pathsToProcess
.begin(), pathsToProcess
.end());
1046 for (uint p
= 0; p
< pathsToProcess
.size(); p
++)
1048 // check if the path not already in the vector
1050 for (i
= 0; i
< _AlternativePaths
.size(); i
++)
1052 if (_AlternativePaths
[i
] == pathsToProcess
[p
])
1055 if (i
== _AlternativePaths
.size())
1057 // add them in the alternative directory
1058 _AlternativePaths
.push_back (pathsToProcess
[p
]);
1059 NL_DISPLAY_PATH("PATH: CPath::addSearchPath(%s, %s, %s): path '%s' added",
1061 recurse
? "recursive" : "not recursive",
1062 alternative
? "alternative" : "not alternative",
1063 pathsToProcess
[p
].c_str());
1067 nlwarning ("PATH: CPath::addSearchPath(%s, %s, %s): path '%s' already added",
1069 recurse
? "recursive" : "not recursive",
1070 alternative
? "alternative" : "not alternative",
1071 pathsToProcess
[p
].c_str());
1077 vector
<string
> filesToProcess
;
1080 if (progressCallBack
)
1082 progressCallBack
->progress (0);
1083 progressCallBack
->pushCropedValues (0, 0.5f
);
1086 // find all files in the path and subpaths
1087 getPathContent (newPath
, recurse
, false, true, filesToProcess
, progressCallBack
);
1090 sort(filesToProcess
.begin(), filesToProcess
.end());
1093 if (progressCallBack
)
1095 progressCallBack
->popCropedValues ();
1096 progressCallBack
->progress (0.5);
1097 progressCallBack
->pushCropedValues (0.5f
, 1);
1100 // add them in the map
1101 for (uint f
= 0; f
< filesToProcess
.size(); f
++)
1104 if (progressCallBack
)
1106 progressCallBack
->progress ((float)f
/(float)filesToProcess
.size());
1107 progressCallBack
->pushCropedValues ((float)f
/(float)filesToProcess
.size(), (float)(f
+1)/(float)filesToProcess
.size());
1110 string filename
= CFile::getFilename (filesToProcess
[f
]);
1111 string filepath
= CFile::getPath (filesToProcess
[f
]);
1112 // insertFileInMap (filename, filepath, false, CFile::getExtension(filename));
1113 addSearchFile (filesToProcess
[f
], false, "", progressCallBack
);
1116 if (progressCallBack
)
1118 progressCallBack
->popCropedValues ();
1123 if (progressCallBack
)
1125 progressCallBack
->popCropedValues ();
1130 void CPath::addSearchFile (const string
&file
, bool remap
, const string
&virtual_ext
, NLMISC::IProgressCallback
*progressCallBack
)
1132 getInstance()->_FileContainer
.addSearchFile(file
, remap
, virtual_ext
, progressCallBack
);
1135 void CFileContainer::addSearchFile (const string
&file
, bool remap
, const string
&virtual_ext
, NLMISC::IProgressCallback
*progressCallBack
)
1137 nlassert(!_MemoryCompressed
);
1139 string newFile
= standardizePath(file
, false);
1142 if (newFile
.empty())
1144 nlwarning ("PATH: CPath::addSearchFile(%s, %d, '%s'): can't add empty file, skip it", file
.c_str(), remap
, virtual_ext
.c_str());
1148 // check if the file exists
1149 if (!CFile::isExists (newFile
))
1151 nlwarning ("PATH: CPath::addSearchFile(%s, %d, '%s'): '%s' is not found, skip it (current dir is '%s'",
1154 virtual_ext
.c_str(),
1156 CPath::getCurrentPath().c_str());
1160 // check if it s a file
1161 if (CFile::isDirectory (newFile
))
1163 nlwarning ("PATH: CPath::addSearchFile(%s, %d, '%s'): '%s' is not a file, skip it",
1166 virtual_ext
.c_str(),
1171 std::string fileExtension
= CFile::getExtension(newFile
);
1173 // check if it s a big file
1174 if (fileExtension
== "bnp")
1176 NL_DISPLAY_PATH ("PATH: CPath::addSearchFile(%s, %d, '%s'): '%s' is a big file, add it", file
.c_str(), remap
, virtual_ext
.c_str(), newFile
.c_str());
1177 addSearchBigFile(file
, false, false, progressCallBack
);
1181 // check if it s a streamed package
1182 if (fileExtension
== "snp")
1184 NL_DISPLAY_PATH ("PATH: CPath::addSearchStreamedPackage(%s, %d, '%s'): '%s' is a streamed package, add it", file
.c_str(), remap
, virtual_ext
.c_str(), newFile
.c_str());
1185 addSearchStreamedPackage(file
, false, false, progressCallBack
);
1189 // check if it s an xml pack file
1190 if (fileExtension
== "xml_pack")
1192 NL_DISPLAY_PATH ("PATH: CPath::addSearchFile(%s, %d, '%s'): '%s' is an xml pack file, add it", file
.c_str(), remap
, virtual_ext
.c_str(), newFile
.c_str());
1193 addSearchXmlpackFile(file
, false, false, progressCallBack
);
1197 string filenamewoext
= CFile::getFilenameWithoutExtension (newFile
);
1198 string filename
, ext
;
1200 if (virtual_ext
.empty())
1202 filename
= CFile::getFilename (newFile
);
1203 ext
= CFile::getExtension (filename
);
1207 filename
= filenamewoext
+ "." + virtual_ext
;
1208 ext
= CFile::getExtension (newFile
);
1211 insertFileInMap (filename
, newFile
, remap
, ext
);
1213 if (!remap
&& !ext
.empty())
1215 // now, we have to see extension and insert in the map the remapped files
1216 for (uint i
= 0; i
< _Extensions
.size (); i
++)
1218 if (_Extensions
[i
].first
== toLowerAscii(ext
))
1221 addSearchFile (newFile
, true, _Extensions
[i
].second
, progressCallBack
);
1227 void CPath::addSearchListFile (const string
&filename
, bool recurse
, bool alternative
)
1229 getInstance()->_FileContainer
.addSearchListFile(filename
, recurse
, alternative
);
1232 void CFileContainer::addSearchListFile (const string
&filename
, bool recurse
, bool alternative
)
1235 if (filename
.empty())
1237 nlwarning ("PATH: CPath::addSearchListFile(%s, %d, %d): can't add empty file, skip it", filename
.c_str(), recurse
, alternative
);
1241 // check if the file exists
1242 if (!CFile::isExists (filename
))
1244 nlwarning ("PATH: CPath::addSearchListFile(%s, %d, %d): '%s' is not found, skip it", filename
.c_str(), recurse
, alternative
, filename
.c_str());
1248 // check if it s a file
1249 if (CFile::isDirectory (filename
))
1251 nlwarning ("PATH: CPath::addSearchListFile(%s, %d, %d): '%s' is not a file, skip it", filename
.c_str(), recurse
, alternative
, filename
.c_str());
1255 // TODO read the file and add files that are inside
1258 // WARNING : recurse is not used
1259 void CPath::addSearchBigFile (const string
&sBigFilename
, bool recurse
, bool alternative
, NLMISC::IProgressCallback
*progressCallBack
)
1261 getInstance()->_FileContainer
.addSearchBigFile(sBigFilename
, recurse
, alternative
, progressCallBack
);
1264 void CFileContainer::addSearchBigFile (const string
&sBigFilename
, bool recurse
, bool alternative
, NLMISC::IProgressCallback
*progressCallBack
)
1266 // Check if filename is not empty
1267 if (sBigFilename
.empty())
1269 nlwarning ("PATH: CPath::addSearchBigFile(%s, %d, %d): can't add empty file, skip it", sBigFilename
.c_str(), recurse
, alternative
);
1272 // Check if the file exists
1273 if (!CFile::isExists (sBigFilename
))
1275 nlwarning ("PATH: CPath::addSearchBigFile(%s, %d, %d): '%s' is not found, skip it", sBigFilename
.c_str(), recurse
, alternative
, sBigFilename
.c_str());
1278 // Check if it s a file
1279 if (CFile::isDirectory (sBigFilename
))
1281 nlwarning ("PATH: CPath::addSearchBigFile(%s, %d, %d): '%s' is not a file, skip it", sBigFilename
.c_str(), recurse
, alternative
, sBigFilename
.c_str());
1284 // Open and read the big file header
1285 nlassert(!_MemoryCompressed
);
1287 FILE *Handle
= nlfopen (sBigFilename
, "rb");
1290 nlwarning ("PATH: CPath::addSearchBigFile(%s, %d, %d): can't open file, skip it", sBigFilename
.c_str(), recurse
, alternative
);
1294 // add the link with the CBigFile singleton
1295 if (CBigFile::getInstance().add (sBigFilename
, BF_ALWAYS_OPENED
| BF_CACHE_FILE_ON_OPEN
))
1297 // also add the bigfile name in the map to retrieve the full path of a .bnp when we want modification date of the bnp for example
1298 insertFileInMap (CFile::getFilename (sBigFilename
), sBigFilename
, false, CFile::getExtension(sBigFilename
));
1300 // parse the big file to add file in the map
1301 uint32 nFileSize
=CFile::getFileSize (Handle
);
1302 //nlfseek64 (Handle, 0, SEEK_END);
1303 //uint32 nFileSize = ftell (Handle);
1304 nlfseek64 (Handle
, nFileSize
-4, SEEK_SET
);
1305 uint32 nOffsetFromBeginning
;
1306 if (fread (&nOffsetFromBeginning
, sizeof(uint32
), 1, Handle
) != 1)
1312 #ifdef NL_BIG_ENDIAN
1313 NLMISC_BSWAP32(nOffsetFromBeginning
);
1316 nlfseek64 (Handle
, nOffsetFromBeginning
, SEEK_SET
);
1318 if (fread (&nNbFile
, sizeof(uint32
), 1, Handle
) != 1)
1324 #ifdef NL_BIG_ENDIAN
1325 NLMISC_BSWAP32(nNbFile
);
1328 for (uint32 i
= 0; i
< nNbFile
; ++i
)
1331 if (progressCallBack
)
1333 progressCallBack
->progress ((float)i
/(float)nNbFile
);
1334 progressCallBack
->pushCropedValues ((float)i
/(float)nNbFile
, (float)(i
+1)/(float)nNbFile
);
1339 if (fread (&nStringSize
, 1, 1, Handle
) != 1)
1346 if (fread(FileName
, 1, nStringSize
, Handle
) != nStringSize
)
1352 FileName
[nStringSize
] = 0;
1354 if (fread (&nFileSize2
, sizeof(uint32
), 1, Handle
) != 1)
1360 #ifdef NL_BIG_ENDIAN
1361 NLMISC_BSWAP32(nFileSize2
);
1365 if (fread (&nFilePos
, sizeof(uint32
), 1, Handle
) != 1)
1371 #ifdef NL_BIG_ENDIAN
1372 NLMISC_BSWAP32(nFilePos
);
1375 string sTmp
= toLowerAscii(string(FileName
));
1378 nlwarning ("PATH: CPath::addSearchBigFile(%s, %d, %d): can't add empty file, skip it", sBigFilename
.c_str(), recurse
, alternative
);
1381 string bigfilenamealone
= CFile::getFilename (sBigFilename
);
1382 string filenamewoext
= CFile::getFilenameWithoutExtension (sTmp
);
1383 string ext
= toLowerAscii(CFile::getExtension(sTmp
));
1385 insertFileInMap (sTmp
, bigfilenamealone
+ "@" + sTmp
, false, ext
);
1387 for (uint j
= 0; j
< _Extensions
.size (); j
++)
1389 if (_Extensions
[j
].first
== ext
)
1392 insertFileInMap (filenamewoext
+"."+_Extensions
[j
].second
,
1393 bigfilenamealone
+ "@" + sTmp
,
1395 _Extensions
[j
].first
);
1400 if (progressCallBack
)
1402 progressCallBack
->popCropedValues ();
1408 nlwarning ("PATH: CPath::addSearchBigFile(%s, %d, %d): can't add the big file", sBigFilename
.c_str(), recurse
, alternative
);
1414 void CPath::addSearchStreamedPackage (const string
&filename
, bool recurse
, bool alternative
, NLMISC::IProgressCallback
*progressCallBack
)
1416 getInstance()->_FileContainer
.addSearchBigFile(filename
, recurse
, alternative
, progressCallBack
);
1419 void CFileContainer::addSearchStreamedPackage (const string
&filename
, bool recurse
, bool alternative
, NLMISC::IProgressCallback
*progressCallBack
)
1421 // Check if filename is not empty
1422 if (filename
.empty())
1424 nlwarning ("PATH: CPath::addSearchStreamedPackage(%s, %d, %d): can't add empty file, skip it", filename
.c_str(), recurse
, alternative
);
1427 // Check if the file exists
1428 if (!CFile::isExists(filename
))
1430 nlwarning ("PATH: CPath::addSearchStreamedPackage(%s, %d, %d): '%s' is not found, skip it", filename
.c_str(), recurse
, alternative
, filename
.c_str());
1433 // Check if it s a file
1434 if (CFile::isDirectory(filename
))
1436 nlwarning ("PATH: CPath::addSearchStreamedPackage(%s, %d, %d): '%s' is not a file, skip it", filename
.c_str(), recurse
, alternative
, filename
.c_str());
1440 // Add the file itself
1441 std::string packname
= NLMISC::toLowerAscii(CFile::getFilename(filename
));
1442 insertFileInMap(packname
, filename
, false, CFile::getExtension(filename
));
1444 // Add the package to the package manager
1445 if (CStreamedPackageManager::getInstance().loadPackage(filename
))
1447 std::vector
<std::string
> fileNames
;
1448 CStreamedPackageManager::getInstance().list(fileNames
, packname
);
1450 for (std::vector
<std::string
>::iterator
it(fileNames
.begin()), end(fileNames
.end()); it
!= end
; ++it
)
1452 // Add the file to the lookup
1453 std::string filePackageName
= packname
+ "@" + (*it
);
1454 // nldebug("Insert '%s'", filePackageName.c_str());
1455 insertFileInMap((*it
), filePackageName
, false, CFile::getExtension(*it
));
1457 // Remapped extensions
1458 std::string ext
= CFile::getExtension(*it
);
1459 for (uint j
= 0; j
< _Extensions
.size (); j
++)
1461 if (_Extensions
[j
].first
== ext
)
1464 insertFileInMap(CFile::getFilenameWithoutExtension(*it
) + "." + _Extensions
[j
].second
,
1465 filePackageName
, true, _Extensions
[j
].first
);
1472 // WARNING : recurse is not used
1473 void CPath::addSearchXmlpackFile (const string
&sXmlpackFilename
, bool recurse
, bool alternative
, NLMISC::IProgressCallback
*progressCallBack
)
1475 getInstance()->_FileContainer
.addSearchXmlpackFile(sXmlpackFilename
, recurse
, alternative
, progressCallBack
);
1478 void CFileContainer::addSearchXmlpackFile (const string
&sXmlpackFilename
, bool recurse
, bool alternative
, NLMISC::IProgressCallback
*progressCallBack
)
1480 // Check if filename is not empty
1481 if (sXmlpackFilename
.empty())
1483 nlwarning ("PATH: CPath::addSearchXmlpackFile(%s, %d, %d): can't add empty file, skip it", sXmlpackFilename
.c_str(), recurse
, alternative
);
1486 // Check if the file exists
1487 if (!CFile::isExists (sXmlpackFilename
))
1489 nlwarning ("PATH: CPath::addSearchXmlpackFile(%s, %d, %d): '%s' is not found, skip it", sXmlpackFilename
.c_str(), recurse
, alternative
, sXmlpackFilename
.c_str());
1492 // Check if it s a file
1493 if (CFile::isDirectory (sXmlpackFilename
))
1495 nlwarning ("PATH: CPath::addSearchXmlpackFile(%s, %d, %d): '%s' is not a file, skip it", sXmlpackFilename
.c_str(), recurse
, alternative
, sXmlpackFilename
.c_str());
1498 // Open and read the xmlpack file header
1500 FILE *Handle
= nlfopen (sXmlpackFilename
, "rb");
1503 nlwarning ("PATH: CPath::addSearchXmlpackFile(%s, %d, %d): can't open file, skip it", sXmlpackFilename
.c_str(), recurse
, alternative
);
1507 // add the link with the CXMLPack singleton
1508 if (CXMLPack::getInstance().add (sXmlpackFilename
))
1510 // also add the xmlpack file name in the map to retrieve the full path of a .xml_pack when we want modification date of the xml_pack for example
1511 insertFileInMap (sXmlpackFilename
, sXmlpackFilename
, false, CFile::getExtension(sXmlpackFilename
));
1514 vector
<string
> filenames
;
1515 CXMLPack::getInstance().list(sXmlpackFilename
, filenames
);
1517 for (uint i
=0; i
<filenames
.size(); ++i
)
1520 if (progressCallBack
)
1522 progressCallBack
->progress ((float)i
/(float)filenames
.size());
1523 progressCallBack
->pushCropedValues ((float)i
/(float)filenames
.size(), (float)(i
+1)/(float)filenames
.size());
1526 string packfilenamealone
= sXmlpackFilename
;
1527 string filenamewoext
= CFile::getFilenameWithoutExtension (filenames
[i
]);
1528 string ext
= toLowerAscii(CFile::getExtension(filenames
[i
]));
1530 insertFileInMap (filenames
[i
], packfilenamealone
+ "@@" + filenames
[i
], false, ext
);
1532 for (uint j
= 0; j
< _Extensions
.size (); j
++)
1534 if (_Extensions
[j
].first
== ext
)
1537 insertFileInMap (filenamewoext
+"."+_Extensions
[j
].second
,
1538 packfilenamealone
+ "@@" + filenames
[i
],
1540 _Extensions
[j
].first
);
1545 if (progressCallBack
)
1547 progressCallBack
->popCropedValues ();
1553 nlwarning ("PATH: CPath::addSearchXmlpackFile(%s, %d, %d): can't add the xml pack file", sXmlpackFilename
.c_str(), recurse
, alternative
);
1559 void CPath::addIgnoredDoubleFile(const std::string
&ignoredFile
)
1561 getInstance()->_FileContainer
.addIgnoredDoubleFile(ignoredFile
);
1564 void CFileContainer::addIgnoredDoubleFile(const std::string
&ignoredFile
)
1566 IgnoredFiles
.push_back(ignoredFile
);
1569 void CFileContainer::insertFileInMap (const string
&filename
, const string
&filepath
, bool remap
, const string
&extension
)
1571 nlassert(!_MemoryCompressed
);
1572 // find if the file already exist
1573 TFiles::iterator it
= _Files
.find (toLowerAscii(filename
));
1574 if (it
!= _Files
.end ())
1576 string path
= SSMpath
.get((*it
).second
.idPath
);
1577 if (path
.find("@") != string::npos
&& filepath
.find("@") == string::npos
)
1579 // if there's a file in a big file and a file in a path, the file in path wins
1580 // replace with the new one
1581 nlinfo ("PATH: CPath::insertFileInMap(%s, %s, %d, %s): already inserted from '%s' but special case so override it", filename
.c_str(), filepath
.c_str(), remap
, extension
.c_str(), path
.c_str());
1582 string sTmp
= filepath
.substr(0,filepath
.rfind('/')+1);
1583 (*it
).second
.idPath
= SSMpath
.add(sTmp
);
1584 (*it
).second
.Remapped
= remap
;
1585 (*it
).second
.idExt
= SSMext
.add(extension
);
1586 (*it
).second
.Name
= filename
;
1590 for(uint i
= 0; i
< IgnoredFiles
.size(); i
++)
1592 // if we don't want to display a warning, skip it
1593 if(filename
== IgnoredFiles
[i
])
1596 // if the path is the same, don't warn
1597 string path2
= SSMpath
.get((*it
).second
.idPath
);
1599 if(filepath
.rfind("@@") != string::npos
)
1600 sPathOnly
= filepath
.substr(0,filepath
.rfind("@@")+2);
1601 else if(filepath
.rfind('@') != string::npos
)
1602 sPathOnly
= filepath
.substr(0,filepath
.rfind('@')+1);
1604 sPathOnly
= CFile::getPath(filepath
);
1606 if (path2
== sPathOnly
)
1608 nlwarning ("PATH: CPath::insertFileInMap(%s, %s, %d, %s): already inserted from '%s', skip it",
1619 fe
.idExt
= SSMext
.add(extension
);
1620 fe
.Remapped
= remap
;
1622 if (filepath
.find("@") == string::npos
)
1623 sTmp
= filepath
.substr(0,filepath
.rfind('/')+1);
1624 else if (filepath
.find("@@") != string::npos
)
1625 sTmp
= filepath
.substr(0,filepath
.rfind("@@")+2);
1627 sTmp
= filepath
.substr(0,filepath
.rfind('@')+1);
1629 fe
.idPath
= SSMpath
.add(sTmp
);
1632 _Files
.insert (make_pair(toLowerAscii(filename
), fe
));
1633 NL_DISPLAY_PATH("PATH: CPath::insertFileInMap(%s, %s, %d, %s): added", toLowerAscii(filename
).c_str(), filepath
.c_str(), remap
, toLowerAscii(extension
).c_str());
1637 void CPath::display ()
1639 getInstance()->_FileContainer
.display();
1642 void CFileContainer::display ()
1644 nlinfo ("PATH: Contents of the map:");
1645 nlinfo ("PATH: %-25s %-5s %-5s %s", "filename", "ext", "remap", "full path");
1646 nlinfo ("PATH: ----------------------------------------------------");
1647 if (_MemoryCompressed
)
1649 for (uint i
= 0; i
< _MCFiles
.size(); ++i
)
1651 const CMCFileEntry
&fe
= _MCFiles
[i
];
1652 string ext
= SSMext
.get(fe
.idExt
);
1653 string path
= SSMpath
.get(fe
.idPath
);
1654 nlinfo ("PATH: %-25s %-5s %-5d %s", fe
.Name
, ext
.c_str(), fe
.Remapped
, path
.c_str());
1659 for (TFiles::iterator it
= _Files
.begin(); it
!= _Files
.end (); it
++)
1661 string ext
= SSMext
.get((*it
).second
.idExt
);
1662 string path
= SSMpath
.get((*it
).second
.idPath
);
1663 nlinfo ("PATH: %-25s %-5s %-5d %s", (*it
).first
.c_str(), ext
.c_str(), (*it
).second
.Remapped
, path
.c_str());
1667 nlinfo ("PATH: Contents of the alternative directory:");
1668 for (uint i
= 0; i
< _AlternativePaths
.size(); i
++)
1670 nlinfo ("PATH: '%s'", _AlternativePaths
[i
].c_str ());
1673 nlinfo ("PATH: Contents of the remapped entension table:");
1674 for (uint j
= 0; j
< _Extensions
.size(); j
++)
1676 nlinfo ("PATH: '%s' -> '%s'", _Extensions
[j
].first
.c_str (), _Extensions
[j
].second
.c_str ());
1678 nlinfo ("PATH: End of display");
1681 void CPath::removeBigFiles(const std::vector
<std::string
> &bnpFilenames
)
1683 getInstance()->_FileContainer
.removeBigFiles(bnpFilenames
);
1686 void CFileContainer::removeBigFiles(const std::vector
<std::string
> &bnpFilenames
)
1688 nlassert(!isMemoryCompressed());
1689 CHashSet
<TSStringId
> bnpStrIds
;
1690 TFiles::iterator fileIt
, fileCurrIt
;
1691 for (uint k
= 0; k
< bnpFilenames
.size(); ++k
)
1693 std::string completeBNPName
= toLowerAscii(bnpFilenames
[k
]) + "@";
1694 if (SSMpath
.isAdded(completeBNPName
))
1696 bnpStrIds
.insert(SSMpath
.add(completeBNPName
));
1698 CBigFile::getInstance().remove(bnpFilenames
[k
]);
1699 fileIt
= _Files
.find(toLowerAscii(bnpFilenames
[k
]));
1700 if (fileIt
!= _Files
.end())
1702 _Files
.erase(fileIt
);
1705 if (bnpStrIds
.empty()) return;
1706 // remove remapped files
1707 std::map
<std::string
, std::string
>::iterator remapIt
, remapCurrIt
;
1708 for(remapIt
= _RemappedFiles
.begin(); remapIt
!= _RemappedFiles
.end();)
1710 remapCurrIt
= remapIt
;
1712 const std::string
&filename
= remapCurrIt
->second
;
1713 fileIt
= _Files
.find(filename
);
1714 if (fileIt
!= _Files
.end())
1716 if (bnpStrIds
.count(fileIt
->second
.idPath
))
1718 _Files
.erase(fileIt
);
1719 _RemappedFiles
.erase(remapCurrIt
);
1723 // remove file entries
1724 for(fileIt
= _Files
.begin(); fileIt
!= _Files
.end();)
1726 fileCurrIt
= fileIt
;
1728 if (bnpStrIds
.count(fileCurrIt
->second
.idPath
))
1730 _Files
.erase(fileCurrIt
);
1738 void CPath::memoryCompress()
1740 getInstance()->_FileContainer
.memoryCompress();
1743 void CFileContainer::memoryCompress()
1746 SSMext
.memoryCompress();
1747 SSMpath
.memoryCompress();
1748 uint nDbg
= (uint
)_Files
.size();
1749 uint nDbg2
= SSMext
.getCount();
1750 uint nDbg3
= SSMpath
.getCount();
1751 nlinfo ("PATH: Number of file: %d, extension: %d, path: %d", nDbg
, nDbg2
, nDbg3
);
1753 // Convert from _Files to _MCFiles
1754 uint nSize
= 0, nNb
= 0;
1755 TFiles::iterator it
= _Files
.begin();
1756 while (it
!= _Files
.end())
1758 string sTmp
= SSMpath
.get(it
->second
.idPath
);
1759 if ((sTmp
.find("@@") == string::npos
) && (sTmp
.find('@') != string::npos
) && (sTmp
.find("snp@") == string::npos
) && !it
->second
.Remapped
)
1761 // This is a file included in a bigfile (so the name is in the bigfile manager)
1765 nSize
+= (uint
)it
->second
.Name
.size() + 1;
1771 _AllFileNames
= new char[nSize
];
1772 memset(_AllFileNames
, 0, nSize
);
1773 _MCFiles
.resize(nNb
);
1775 it
= _Files
.begin();
1778 while (it
!= _Files
.end())
1780 CFileEntry
&rFE
= it
->second
;
1781 string sTmp
= SSMpath
.get(rFE
.idPath
);
1782 if ((sTmp
.find("@") == string::npos
) || (sTmp
.find("@@") != string::npos
) || (sTmp
.find("snp@") != string::npos
) || rFE
.Remapped
)
1784 strcpy(_AllFileNames
+nSize
, rFE
.Name
.c_str());
1785 _MCFiles
[nNb
].Name
= _AllFileNames
+nSize
;
1786 nSize
+= (uint
)rFE
.Name
.size() + 1;
1790 // This is a file included in a bigfile (so the name is in the bigfile manager)
1791 sTmp
= sTmp
.substr(0, sTmp
.size()-1);
1792 _MCFiles
[nNb
].Name
= CBigFile::getInstance().getFileNamePtr(rFE
.Name
, sTmp
);
1793 if (_MCFiles
[nNb
].Name
== NULL
)
1795 nlerror("memoryCompress: failed to find named file in big file: %s",SSMpath
.get(rFE
.idPath
));
1799 _MCFiles
[nNb
].idExt
= rFE
.idExt
;
1800 _MCFiles
[nNb
].idPath
= rFE
.idPath
;
1801 _MCFiles
[nNb
].Remapped
= rFE
.Remapped
;
1808 _MemoryCompressed
= true;
1811 void CPath::memoryUncompress()
1813 getInstance()->_FileContainer
.memoryUncompress();
1816 void CFileContainer::memoryUncompress()
1818 SSMext
.memoryUncompress();
1819 SSMpath
.memoryUncompress();
1820 for(std::vector
<CMCFileEntry
>::iterator it
= _MCFiles
.begin(); it
!= _MCFiles
.end(); ++it
)
1824 fe
.idExt
= it
->idExt
;
1825 fe
.idPath
= it
->idPath
;
1826 fe
.Remapped
= it
->Remapped
;
1828 _Files
[toLowerAscii(CFile::getFilename(fe
.Name
))] = fe
;
1830 contReset(_MCFiles
);
1831 _MemoryCompressed
= false;
1834 std::string
CPath::getWindowsDirectory()
1836 return getInstance()->_FileContainer
.getWindowsDirectory();
1839 std::string
CFileContainer::getWindowsDirectory()
1841 #ifndef NL_OS_WINDOWS
1842 nlwarning("not a ms windows platform");
1845 wchar_t winDir
[MAX_PATH
];
1846 UINT numChar
= GetWindowsDirectoryW(winDir
, MAX_PATH
);
1847 if (numChar
> MAX_PATH
|| numChar
== 0)
1849 nlwarning("Couldn't retrieve windows directory");
1852 return CPath::standardizePath(wideToUtf8(winDir
));
1856 std::string
CPath::getApplicationDirectory(const std::string
&appName
, bool local
)
1858 return getInstance()->_FileContainer
.getApplicationDirectory(appName
, local
);
1861 std::string
CFileContainer::getApplicationDirectory(const std::string
&appName
, bool local
)
1863 static std::string appPaths
[2];
1865 std::string
&appPath
= appPaths
[local
? 1 : 0];
1867 if (appPath
.empty())
1869 #ifdef NL_OS_WINDOWS
1870 wchar_t buffer
[MAX_PATH
];
1871 #ifdef CSIDL_LOCAL_APPDATA
1874 SHGetSpecialFolderPathW(NULL
, buffer
, CSIDL_LOCAL_APPDATA
, TRUE
);
1879 SHGetSpecialFolderPathW(NULL
, buffer
, CSIDL_APPDATA
, TRUE
);
1881 appPath
= CPath::standardizePath(wideToUtf8(buffer
));
1883 // get user home directory from HOME environment variable
1884 const char* homePath
= getenv("HOME");
1885 appPath
= CPath::standardizePath(homePath
? homePath
: ".");
1887 #if defined(NL_OS_MAC)
1888 appPath
+= "Library/Application Support/";
1890 // recommended for applications data that are owned by user
1891 appPath
+= ".local/share/";
1896 return CPath::standardizePath(appPath
+ appName
);
1899 std::string
CPath::getTemporaryDirectory()
1901 return getInstance()->_FileContainer
.getTemporaryDirectory();
1904 std::string
CFileContainer::getTemporaryDirectory()
1906 static std::string path
;
1909 const char *temp
= getenv("TEMP");
1910 const char *tmp
= getenv("TMP");
1912 std::string tempDir
;
1917 if (tempDir
.empty() && tmp
)
1921 if (tempDir
.empty())
1924 if (tempDir
.empty())
1928 path
= CPath::standardizePath(tempDir
);
1934 //////////////////////////////////////////////////////////////////////////////////////////////////////
1935 //////////////////////////////////////////////////////////////////////////////////////////////////////
1936 //////////////////////////////////////////////////////////////////////////////////////////////////////
1937 //////////////////////////////////////////////////////////////////////////////////////////////////////
1938 //////////////////////////////////////////////////////////////////////////////////////////////////////
1940 std::string::size_type
CFile::getLastSeparator (const string
&filename
)
1942 string::size_type pos
= filename
.find_last_of ('/');
1943 if (pos
== string::npos
)
1945 pos
= filename
.find_last_of ('\\');
1946 if (pos
== string::npos
)
1948 pos
= filename
.find_last_of ('@');
1954 string
CFile::getFilename (const string
&filename
)
1956 string::size_type pos
= CFile::getLastSeparator(filename
);
1957 if (pos
!= string::npos
)
1958 return filename
.substr (pos
+ 1);
1963 string
CFile::getFilenameWithoutExtension (const string
&filename
)
1965 string filename2
= getFilename (filename
);
1966 string::size_type pos
= filename2
.find_last_of ('.');
1967 if (pos
== string::npos
)
1970 return filename2
.substr (0, pos
);
1973 string
CFile::getExtension (const string
&filename
)
1975 string::size_type pos
= filename
.find_last_of ('.');
1976 if (pos
== string::npos
)
1979 return filename
.substr (pos
+ 1);
1982 string
CFile::getPath (const string
&filename
)
1984 string::size_type pos
= CFile::getLastSeparator(filename
);
1985 if (pos
!= string::npos
)
1986 return filename
.substr (0, pos
+ 1);
1991 bool CFile::isDirectory (const string
&filename
)
1993 #ifdef NL_OS_WINDOWS
1994 DWORD res
= GetFileAttributesW(nlUtf8ToWide(filename
));
1995 if (res
== INVALID_FILE_ATTRIBUTES
)
1997 // nlwarning ("PATH: '%s' is not a valid file or directory name", filename.c_str ());
2000 return (res
& FILE_ATTRIBUTE_DIRECTORY
) != 0;
2001 #else // NL_OS_WINDOWS
2003 int res
= stat (filename
.c_str (), &buf
);
2006 // There was previously a warning message here but that was incorrect as it is defined that isDirectory returns false if the directory doesn't exist
2007 // nlwarning ("PATH: can't stat '%s' error %d '%s'", filename.c_str(), errno, strerror(errno));
2010 return (buf
.st_mode
& S_IFDIR
) != 0;
2011 #endif // NL_OS_WINDOWS
2014 bool CFile::isExists (const string
&filename
)
2016 #ifdef NL_OS_WINDOWS
2017 return GetFileAttributesW(nlUtf8ToWide(filename
)) != INVALID_FILE_ATTRIBUTES
;
2018 #else // NL_OS_WINDOWS
2020 return stat (filename
.c_str (), &buf
) == 0;
2021 #endif // NL_OS_WINDOWS
2024 bool CFile::createEmptyFile (const std::string
& filename
)
2026 FILE *file
= nlfopen (filename
, "wb");
2037 bool CFile::fileExists (const string
& filename
)
2039 //H_AUTO(FileExists);
2040 FILE *file
= nlfopen(filename
, "rb");
2052 string
CFile::findNewFile (const string
&filename
)
2054 string::size_type pos
= filename
.find_last_of ('.');
2055 if (pos
== string::npos
)
2058 string start
= filename
.substr (0, pos
);
2059 string end
= filename
.substr (pos
);
2067 smprintf(numchar
,4,"%03d",num
++);
2070 if (!CFile::fileExists(npath
)) break;
2076 // \warning doesn't work with big file
2077 uint32
CFile::getFileSize (const std::string
&filename
)
2079 std::string::size_type pos
;
2080 if (filename
.find("@@") != string::npos
)
2084 CXMLPack::getInstance().getFile (filename
, fs
, bfo
, c
, d
);
2087 else if ((pos
= filename
.find('@')) != string::npos
)
2089 if (pos
> 3 && filename
[pos
-3] == 's' && filename
[pos
-2] == 'n' && filename
[pos
-1] == 'p')
2092 CStreamedPackageManager::getInstance().getFileSize (fs
, filename
.substr(pos
+1));
2099 CBigFile::getInstance().getFile (filename
, fs
, bfo
, c
, d
);
2105 #if defined (NL_OS_WINDOWS)
2107 int result
= _wstat(nlUtf8ToWide(filename
), &buf
);
2108 #elif defined (NL_OS_UNIX)
2110 int result
= stat (filename
.c_str (), &buf
);
2112 if (result
!= 0) return 0;
2113 else return buf
.st_size
;
2117 uint32
CFile::getFileSize (FILE *f
)
2119 #if defined (NL_OS_WINDOWS)
2121 int result
= _fstat (fileno(f
), &buf
);
2122 #elif defined (NL_OS_UNIX)
2124 int result
= fstat (fileno(f
), &buf
);
2126 if (result
!= 0) return 0;
2127 else return buf
.st_size
;
2130 uint32
CFile::getFileModificationDate(const std::string
&filename
)
2132 string::size_type pos
;
2134 if ((pos
=filename
.find("@@")) != string::npos
)
2136 fn
= filename
.substr (0, pos
);
2138 else if ((pos
=filename
.find('@')) != string::npos
)
2140 fn
= CPath::lookup(filename
.substr (0, pos
));
2147 #if defined (NL_OS_WINDOWS)
2148 // struct _stat buf;
2149 // int result = _stat (fn.c_str (), &buf);
2150 // Changed 06-06-2007 : boris : _stat have an incoherent and hard to reproduce
2151 // on windows : if the system clock is adjusted according to daylight saving
2152 // time, the file date reported by _stat may (not always!) be adjusted by 3600s
2153 // This is a bad behavior because file time should always be reported as UTC time value
2155 // Use the WIN32 API to read the file times in UTC
2157 // create a file handle (this does not open the file)
2158 HANDLE h
= CreateFileW(nlUtf8ToWide(fn
), 0, 0, NULL
, OPEN_EXISTING
, 0, 0);
2159 if (h
== INVALID_HANDLE_VALUE
)
2161 nlwarning("Can't get modification date on file '%s' : %s", fn
.c_str(), NLMISC::formatErrorMessage(NLMISC::getLastError()).c_str());
2164 FILETIME creationTime
;
2165 FILETIME accesstime
;
2168 // get the files times
2169 BOOL res
= GetFileTime(h
, &creationTime
, &accesstime
, &modTime
);
2172 nlwarning("Can't get modification date on file '%s' : %s", fn
.c_str(), NLMISC::formatErrorMessage(NLMISC::getLastError()).c_str());
2179 // win32 file times are in 10th of micro sec (100ns resolution), starting at jan 1, 1601
2180 // hey Mr Gates, why 1601 ?
2182 // first, convert it into second since jan1, 1601
2183 uint64 t
= modTime
.dwLowDateTime
| (uint64(modTime
.dwHighDateTime
)<<32);
2185 // adjust time base to unix epoch base
2186 t
-= CTime::getWindowsToUnixBaseTimeOffset();
2188 // convert the resulting time into seconds
2189 t
/= 10; // microsec
2190 t
/= 1000; // millisec
2193 // return the resulting time
2196 #elif defined (NL_OS_UNIX)
2198 int result
= stat (fn
.c_str (), &buf
);
2201 nlwarning("Can't get modification date on file '%s' : %s", fn
.c_str(), NLMISC::formatErrorMessage(NLMISC::getLastError()).c_str());
2205 return (uint32
)buf
.st_mtime
;
2210 bool CFile::setFileModificationDate(const std::string
&filename
, uint32 modTime
)
2212 string::size_type pos
;
2214 if ((pos
=filename
.find('@')) != string::npos
)
2216 fn
= CPath::lookup(filename
.substr (0, pos
));
2223 #if defined (NL_OS_WINDOWS)
2225 // Use the WIN32 API to set the file times in UTC
2227 // create a file handle (this does not open the file)
2228 HANDLE h
= CreateFileW(nlUtf8ToWide(fn
), GENERIC_WRITE
| GENERIC_READ
, FILE_SHARE_WRITE
| FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, 0);
2229 if (h
== INVALID_HANDLE_VALUE
)
2231 nlwarning("Can't set modification date on file '%s' (error accessing file) : %s", fn
.c_str(), NLMISC::formatErrorMessage(NLMISC::getLastError()).c_str());
2234 FILETIME creationFileTime
;
2235 FILETIME accessFileTime
;
2236 FILETIME modFileTime
;
2238 // read the current file time
2239 if (GetFileTime(h
, &creationFileTime
, &accessFileTime
, &modFileTime
) == 0)
2241 nlwarning("Can't set modification date on file '%s' : %s", fn
.c_str(), formatErrorMessage(getLastError()).c_str());
2246 // win32 file times are in 10th of micro sec (100ns resolution), starting at jan 1, 1601
2247 // hey Mr Gates, why 1601 ?
2249 // convert the unix time into a windows file time
2251 // convert to 10th of microsec
2252 t
*= 1000; // millisec
2253 t
*= 1000; // microsec
2254 t
*= 10; // 10th of micro sec (rez of windows file time is 100ns <=> 1/10 us
2256 // apply the windows to unix base time offset
2257 t
+= CTime::getWindowsToUnixBaseTimeOffset();
2259 // update the windows modTime structure
2260 modFileTime
.dwLowDateTime
= uint32(t
& 0xffffffff);
2261 modFileTime
.dwHighDateTime
= uint32(t
>> 32);
2263 // update the file time on disk
2264 BOOL rez
= SetFileTime(h
, &creationFileTime
, &accessFileTime
, &modFileTime
);
2267 nlwarning("Can't set modification date on file '%s': %s", fn
.c_str(), formatErrorMessage(getLastError()).c_str());
2278 #elif defined (NL_OS_UNIX)
2279 // first, read the current time of the file
2281 int result
= stat (fn
.c_str (), &buf
);
2285 // prepare the new time to apply
2287 tb
.actime
= buf
.st_atime
;
2288 tb
.modtime
= modTime
;
2290 int res
= utime(fn
.c_str(), &tb
);
2292 nlwarning("Can't set modification date on file '%s': %s", fn
.c_str(), formatErrorMessage(getLastError()).c_str());
2298 uint32
CFile::getFileCreationDate(const std::string
&filename
)
2300 string::size_type pos
;
2302 if ((pos
=filename
.find('@')) != string::npos
)
2304 fn
= CPath::lookup(filename
.substr (0, pos
));
2311 #if defined (NL_OS_WINDOWS)
2313 int result
= _wstat(nlUtf8ToWide(fn
), &buf
);
2314 #elif defined (NL_OS_UNIX)
2316 int result
= stat(fn
.c_str (), &buf
);
2319 if (result
!= 0) return 0;
2320 else return (uint32
)buf
.st_ctime
;
2325 CFileEntry (const string
&filename
, void (*callback
)(const string
&filename
)) : FileName (filename
), Callback (callback
)
2327 LastModified
= CFile::getFileModificationDate(filename
);
2330 void (*Callback
)(const string
&filename
);
2331 uint32 LastModified
;
2334 static vector
<CFileEntry
> FileToCheck
;
2336 void CFile::removeFileChangeCallback (const std::string
&filename
)
2338 string fn
= CPath::lookup(filename
, false, false);
2343 for (uint i
= 0; i
< FileToCheck
.size(); i
++)
2345 if(FileToCheck
[i
].FileName
== fn
)
2347 nlinfo ("PATH: CFile::removeFileChangeCallback: '%s' is removed from checked files modification", fn
.c_str());
2348 FileToCheck
.erase(FileToCheck
.begin()+i
);
2354 void CFile::addFileChangeCallback (const std::string
&filename
, void (*cb
)(const string
&filename
))
2356 string fn
= CPath::lookup(filename
, false, false);
2361 nlinfo ("PATH: CFile::addFileChangeCallback: I'll check the modification date for this file '%s'", fn
.c_str());
2362 FileToCheck
.push_back(CFileEntry(fn
, cb
));
2365 void CFile::checkFileChange (TTime frequency
)
2367 static TTime lastChecked
= CTime::getLocalTime();
2369 if (CTime::getLocalTime() > lastChecked
+ frequency
)
2371 for (uint i
= 0; i
< FileToCheck
.size(); i
++)
2373 if(CFile::getFileModificationDate(FileToCheck
[i
].FileName
) != FileToCheck
[i
].LastModified
)
2375 // need to reload it
2376 if(FileToCheck
[i
].Callback
!= NULL
)
2377 FileToCheck
[i
].Callback(FileToCheck
[i
].FileName
);
2379 FileToCheck
[i
].LastModified
= CFile::getFileModificationDate(FileToCheck
[i
].FileName
);
2383 lastChecked
= CTime::getLocalTime();
2387 static bool CopyMoveFile(const std::string
&dest
, const std::string
&src
, bool copyFile
, bool failIfExists
= false, IProgressCallback
*progress
= NULL
)
2389 if (dest
.empty() || src
.empty()) return false;
2390 std::string sdest
= CPath::standardizePath(dest
,false);
2391 std::string ssrc
= CPath::standardizePath(src
,false);
2393 if (progress
) progress
->progress(0.f
);
2396 uint32 totalSize
= 0;
2397 uint32 readSize
= 0;
2400 totalSize
= CFile::getFileSize(ssrc
);
2402 FILE *fp1
= nlfopen(ssrc
, "rb");
2405 nlwarning ("PATH: CopyMoveFile error: can't fopen in read mode '%s'", ssrc
.c_str());
2408 FILE *fp2
= nlfopen(sdest
, "wb");
2411 nlwarning ("PATH: CopyMoveFile error: can't fopen in read write mode '%s'", sdest
.c_str());
2414 static char buffer
[1000];
2417 s
= fread(buffer
, 1, sizeof(buffer
), fp1
);
2422 readSize
+= (uint32
)s
;
2423 progress
->progress((float) readSize
/ totalSize
);
2425 size_t ws
= fwrite(buffer
, s
, 1, fp2
);
2428 nlwarning("Error copying '%s' to '%s', trying to write %u bytes failed.",
2434 nlwarning("Errno = %d", errno
);
2437 s
= fread(buffer
, 1, sizeof(buffer
), fp1
);
2442 if (progress
) progress
->progress(1.f
);
2446 #ifdef NL_OS_WINDOWS
2447 if (MoveFileW(nlUtf8ToWide(ssrc
), nlUtf8ToWide(sdest
)) == 0)
2449 sint lastError
= NLMISC::getLastError();
2450 nlwarning ("PATH: CopyMoveFile error: can't link/move '%s' into '%s', error %u (%s)",
2454 NLMISC::formatErrorMessage(lastError
).c_str());
2459 if (rename (ssrc
.c_str(), sdest
.c_str()) == -1)
2461 // unable to move because file systems are different
2464 // different file system, we need to copy and delete file manually
2465 if (!CopyMoveFile(dest
, src
, true, failIfExists
, progress
)) return false;
2467 // get modification time
2468 uint32 modificationTime
= CFile::getFileModificationDate(src
);
2470 // delete original file
2471 if (!CFile::deleteFile(src
)) return false;
2473 // set same modification time
2474 if (!CFile::setFileModificationDate(dest
, modificationTime
))
2476 nlwarning("Unable to set modification time %s (%u) for %s", timestampToHumanReadable(modificationTime
).c_str(), modificationTime
, dest
.c_str());
2481 nlwarning("PATH: CopyMoveFile error: can't rename '%s' into '%s', error %u",
2491 if (progress
) progress
->progress(1.f
);
2495 bool CFile::copyFile(const std::string
&dest
, const std::string
&src
, bool failIfExists
/*=false*/, IProgressCallback
*progress
)
2497 return CopyMoveFile(dest
, src
, true, failIfExists
, progress
);
2500 bool CFile::quickFileCompare(const std::string
&fileName0
, const std::string
&fileName1
)
2502 // make sure the files both exist
2503 if (!fileExists(fileName0
) || !fileExists(fileName1
))
2506 // compare time stamps
2507 if (getFileModificationDate(fileName0
) != getFileModificationDate(fileName1
))
2510 // compare file sizes
2511 if (getFileSize(fileName0
) != getFileSize(fileName1
))
2514 // everything matched so return true
2518 bool CFile::thoroughFileCompare(const std::string
&fileName0
, const std::string
&fileName1
,uint32 maxBufSize
)
2520 // make sure the files both exist
2521 if (!fileExists(fileName0
) || !fileExists(fileName1
))
2524 // setup the size variable from file length of first file
2525 uint32 fileSize
=getFileSize(fileName0
);
2527 // compare file sizes
2528 if (fileSize
!= getFileSize(fileName1
))
2531 // allocate a couple of data buffers for our 2 files
2532 uint32 bufSize
= maxBufSize
/2;
2533 nlassert(sint32(bufSize
)>0);
2534 std::vector
<uint8
> buf0(bufSize
);
2535 std::vector
<uint8
> buf1(bufSize
);
2537 // open the two files for input
2538 CIFile
file0(fileName0
);
2539 CIFile
file1(fileName1
);
2541 for (uint32 i
=0;i
<fileSize
;i
+=bufSize
)
2543 // for the last block in the file reduce buf size to represent the amount of data left in file
2544 if (i
+bufSize
>fileSize
)
2546 bufSize
= fileSize
-i
;
2547 buf0
.resize(bufSize
);
2548 buf1
.resize(bufSize
);
2551 // read in the next data block from disk
2552 file0
.serialBuffer(&buf0
[0], bufSize
);
2553 file1
.serialBuffer(&buf1
[0], bufSize
);
2555 // compare the contents of the 2 data buffers
2560 // everything matched so return true
2564 bool CFile::moveFile(const std::string
&dest
, const std::string
&src
)
2566 return CopyMoveFile(dest
, src
, false);
2569 bool CFile::createDirectory(const std::string
&filename
)
2571 #ifdef NL_OS_WINDOWS
2572 return _wmkdir(nlUtf8ToWide(filename
)) == 0;
2574 // set rwxrwxr-x permissions
2575 return mkdir(filename
.c_str(), S_IRUSR
|S_IWUSR
|S_IXUSR
|S_IRGRP
|S_IWGRP
|S_IXGRP
|S_IROTH
|S_IXOTH
)==0;
2579 bool CFile::createDirectoryTree(const std::string
&filename
)
2581 bool lastResult
=true;
2584 // skip dos drive name eg "a:"
2585 if (filename
.size()>1 && filename
[1]==':')
2588 // iterate over the set of directories in the routine's argument
2589 while (i
<filename
.size())
2591 // skip passed leading slashes
2592 for (;i
<filename
.size();++i
)
2593 if (filename
[i
]!='\\' && filename
[i
]!='/')
2596 // if the file name ended with a '/' then there's no extra directory to create
2597 if (i
==filename
.size())
2600 // skip forwards to next slash
2601 for (;i
<filename
.size();++i
)
2602 if (filename
[i
]=='\\' || filename
[i
]=='/')
2605 // try to create directory
2606 std::string s
= filename
.substr(0,i
);
2607 lastResult
= createDirectory(s
);
2613 bool CPath::makePathRelative (const std::string
&basePath
, std::string
&relativePath
)
2615 // Standard path with final slash
2616 string tmp
= standardizePath (basePath
, true);
2617 string src
= standardizePath (relativePath
, true);
2622 // Compare with relativePath
2623 if (strncmp (tmp
.c_str (), src
.c_str (), tmp
.length ()) == 0)
2626 uint size
= (uint
)tmp
.length ();
2629 if (size
== src
.length ())
2635 relativePath
= prefix
+relativePath
.substr (size
, relativePath
.length () - size
);
2640 if (tmp
.length ()<2)
2643 // Remove last directory
2644 string::size_type lastPos
= tmp
.rfind ('/', tmp
.length ()-2);
2645 string::size_type previousPos
= tmp
.find ('/');
2646 if ((lastPos
== previousPos
) || (lastPos
== string::npos
))
2650 tmp
= tmp
.substr (0, lastPos
+1);
2659 std::string
CPath::makePathAbsolute( const std::string
&relativePath
, const std::string
&directory
, bool simplify
)
2661 if( relativePath
.empty() )
2663 if( directory
.empty() )
2666 std::string absolutePath
;
2668 #ifdef NL_OS_WINDOWS
2669 // Windows network address. Eg.: \\someshare\path
2670 if ((relativePath
[0] == '\\') && (relativePath
[1] == '\\'))
2672 absolutePath
= relativePath
;
2675 // Normal Windows absolute path. Eg.: C:\something
2677 else if (isalpha(relativePath
[0]) && (relativePath
[1] == ':') && ((relativePath
[2] == '\\') || (relativePath
[2] == '/')))
2679 absolutePath
= relativePath
;
2683 // Unix filesystem absolute path (works also under Windows)
2684 if (relativePath
[0] == '/')
2686 absolutePath
= relativePath
;
2690 // Add a slash to the directory if necessary.
2691 // If the relative path starts with dots we need a slash.
2692 // If the relative path starts with a slash we don't.
2693 // If it starts with neither, we need a slash.
2694 bool needSlash
= true;
2695 char c
= relativePath
[0];
2696 if ((c
== '\\') || (c
== '/'))
2699 bool hasSlash
= false;
2700 absolutePath
= directory
;
2701 c
= absolutePath
[absolutePath
.size() - 1];
2702 if ((c
== '\\') || (c
== '/'))
2705 if (needSlash
&& !hasSlash
)
2706 absolutePath
+= '/';
2708 if (hasSlash
&& !needSlash
)
2709 absolutePath
.resize(absolutePath
.size() - 1);
2711 // Now build the new absolute path
2712 absolutePath
+= relativePath
;
2715 absolutePath
= standardizePath(absolutePath
, true);
2719 // split all components path to manage parent directories
2720 std::vector
<std::string
> tokens
;
2721 explode(absolutePath
, std::string("/"), tokens
, false);
2723 std::vector
<std::string
> directoryParts
;
2725 // process all components
2726 for(uint i
= 0, len
= tokens
.size(); i
< len
; ++i
)
2728 std::string token
= tokens
[i
];
2730 // current directory
2736 // remove last directory
2737 directoryParts
.pop_back();
2742 directoryParts
.push_back(token
);
2747 if (!directoryParts
.empty())
2749 absolutePath
= directoryParts
[0];
2751 // rebuild the whole absolute path
2752 for(uint i
= 1, len
= directoryParts
.size(); i
< len
; ++i
)
2754 if (!directoryParts
[i
].empty())
2755 absolutePath
+= "/" + directoryParts
[i
];
2758 // add trailing slash
2759 absolutePath
+= "/";
2764 absolutePath
.clear();
2768 return absolutePath
;
2771 bool CPath::isAbsolutePath(const std::string
&path
)
2773 if (path
.empty()) return false;
2775 #ifdef NL_OS_WINDOWS
2776 // Windows root of current disk. Eg.: "\" or
2777 // Windows network address. Eg.: \\someshare\path
2778 if (path
[0] == '\\') return true;
2780 // Normal Windows absolute path. Eg.: C:\something
2781 if (path
.length() > 2 && isalpha(path
[0]) && (path
[1] == ':' ) && ((path
[2] == '\\') || (path
[2] == '/' ))) return true;
2784 // Unix filesystem absolute path (works also under Windows)
2785 if (path
[0] == '/') return true;
2790 bool CFile::setRWAccess(const std::string
&filename
)
2792 #ifdef NL_OS_WINDOWS
2793 std::wstring wideFile
= NLMISC::utf8ToWide(filename
);
2795 // if the file exists and there's no write access
2796 if (_waccess (wideFile
.c_str(), 00) == 0 && _waccess (wideFile
.c_str(), 06) == -1)
2798 // try to set the read/write access
2799 if (_wchmod (wideFile
.c_str(), _S_IREAD
| _S_IWRITE
) == -1)
2801 if (INelContext::getInstance().getAlreadyCreateSharedAmongThreads())
2803 nlwarning ("PATH: Can't set RW access to file '%s': %d %s", filename
.c_str(), errno
, strerror(errno
));
2809 // if the file exists and there's no write access
2810 if (access (filename
.c_str(), F_OK
) == 0)
2812 // try to set the read/write access
2814 mode_t mode
= S_IRUSR
|S_IWUSR
|S_IRGRP
|S_IWGRP
|S_IROTH
;
2816 // set +x only for directory
2818 if (CFile::isDirectory(filename
))
2820 mode
|= S_IXUSR
|S_IXGRP
|S_IXOTH
;
2823 if (chmod (filename
.c_str(), mode
) == -1)
2825 if (INelContext::getInstance().getAlreadyCreateSharedAmongThreads())
2827 nlwarning ("PATH: Can't set RW access to file '%s': %d %s", filename
.c_str(), errno
, strerror(errno
));
2835 if (INelContext::getInstance().getAlreadyCreateSharedAmongThreads())
2837 nlwarning("PATH: Can't access to file '%s'", filename
.c_str());
2844 bool CFile::setExecutable(const std::string
&filename
)
2846 #ifndef NL_OS_WINDOWS
2848 if (stat(filename
.c_str (), &buf
) == 0)
2850 mode_t mode
= buf
.st_mode
| S_IXUSR
| S_IXGRP
| S_IXOTH
;
2851 if (chmod(filename
.c_str(), mode
) == -1)
2853 if (INelContext::getInstance().getAlreadyCreateSharedAmongThreads())
2855 nlwarning ("PATH: Can't set +x flag on file '%s': %d %s", filename
.c_str(), errno
, strerror(errno
));
2862 if (INelContext::getInstance().getAlreadyCreateSharedAmongThreads())
2864 nlwarning("PATH: Can't access file '%s': %d %s", filename
.c_str(), errno
, strerror(errno
));
2872 bool CFile::deleteFile(const std::string
&filename
)
2874 setRWAccess(filename
);
2875 #ifdef NL_OS_WINDOWS
2876 sint res
= _wunlink(nlUtf8ToWide(filename
));
2878 sint res
= unlink(filename
.c_str());
2882 if (INelContext::getInstance().getAlreadyCreateSharedAmongThreads())
2884 nlwarning ("PATH: Can't delete file '%s': (errno %d) %s", filename
.c_str(), errno
, strerror(errno
));
2891 bool CFile::deleteDirectory(const std::string
&filename
)
2893 setRWAccess(filename
);
2894 #ifdef NL_OS_WINDOWS
2895 sint res
= _wrmdir(nlUtf8ToWide(filename
));
2897 sint res
= rmdir(filename
.c_str());
2901 nlwarning ("PATH: Can't delete directory '%s': (errno %d) %s", filename
.c_str(), errno
, strerror(errno
));
2907 void CFile::getTemporaryOutputFilename (const std::string
&originalFilename
, std::string
&tempFilename
)
2911 tempFilename
= originalFilename
+".tmp"+toString (i
++);
2912 while (CFile::isExists(tempFilename
));