1 /////////////////////////////////////////
2 //
3 // OpenLieroX
4 //
5 // Auxiliary Software class library
6 //
7 // based on the work of JasonB
8 // enhanced by Dark Charlie and Albert Zeyer
9 //
10 // code under LGPL
11 //
12 /////////////////////////////////////////
13
14
15 // File finding routines
16 // Created 30/9/01
17 // By Jason Boettcher
18
19
20
21
22 #ifdef _MSC_VER
23 #pragma warning(disable: 4786) // WARNING: identifier XXX was truncated to 255 characters in the debug info
24 #pragma warning(disable: 4503) // WARNING: decorated name length exceeded, name was truncated
25 #endif
26
27 #include "FindFile.h"
28 #include "StringUtils.h"
29 #include "Options.h"
30 #include "Debug.h"
31
32
33 #ifdef WIN32
34 # ifndef _WIN32_IE
35 // Some functions that are required are unavailable if this is not defined:
36 // TODO: which functions?
37 # define _WIN32_IE 0x0400 // Because of Dev-cpp
38 # endif
39
40 # include <shlobj.h>
41
42 #else // WIN32
43
44 // not defined for older GCC versions; we check only for >=4.3 anyway
45 # ifndef __GNUC_PREREQ
46 # define __GNUC_PREREQ(maj, min) (0)
47 # endif
48
49 // include hash_set support
50 # if !defined(STLPORT)
51 # if defined(__GNUC__) && __GNUC_PREREQ(4,3)
52 # include <tr1/unordered_set>
53 # define hash_set std::tr1::unordered_set
54 # else
55 # include <ext/hash_set>
56 # ifdef _GLIBCXX_DEBUG
57 using namespace __gnu_debug_def;
58 # else
59 using namespace __gnu_cxx;
60 # endif
61 # endif
62 # else // STLPORT
63 # include <hash_set>
64 using std::hash_set;
65 # endif
66
67 // for getpwduid
68 # include <pwd.h>
69
70 // for realpath
71 # include <sys/param.h>
72 # include <unistd.h>
73
74 #endif
75
76
77 searchpathlist tSearchPaths;
78
79
80
IsFileAvailable(const std::string & f,bool absolute)81 bool IsFileAvailable(const std::string& f, bool absolute) {
82 std::string abs_f;
83 if(absolute) {
84 abs_f = f;
85 } else
86 if((abs_f = GetFullFileName(f)) == "") return false;
87
88 // remove trailing slashes
89 // don't remove them on WIN, if it is a drive-letter
90 while(abs_f.size() > 0 && (abs_f[abs_f.size()-1] == '\\' || abs_f[abs_f.size()-1] == '/')) {
91 #ifdef WIN32
92 if(abs_f.size() > 2 && abs_f[abs_f.size()-2] == ':') break;
93 #endif
94 abs_f.erase(abs_f.size()-1);
95 }
96
97 abs_f = Utf8ToSystemNative(abs_f);
98
99 // HINT: this should also work on WIN32, as we have _stat here
100 struct stat s;
101 if(stat(abs_f.c_str(), &s) != 0 || !S_ISREG(s.st_mode)) {
102 // it's not stat-able or not a reg file
103 return false;
104 }
105
106 // it's stat-able and a file
107 return true;
108 }
109
110
111
112 //////////////////////
113 // Replaces backward slashes with forward slashes (windows only)
114 // Used when comparing two paths
ReplaceSlashes(std::string & path)115 static void ReplaceSlashes(std::string& path) {
116 #ifdef WIN32
117 for (std::string::iterator it = path.begin(); it != path.end(); it++)
118 if (*it == '\\') *it = '/';
119 #endif
120 }
121
EqualPaths(const std::string & path1,const std::string & path2)122 bool EqualPaths(const std::string& path1, const std::string& path2) {
123 std::string p1 = path1;
124 std::string p2 = path2;
125
126 ReplaceSlashes(p1);
127 ReplaceSlashes(p2);
128
129 if (*p1.rbegin() != '/')
130 p1 += '/';
131 if (*p2.rbegin() != '/')
132 p2 += '/';
133
134 return stringcaseequal(p1, p2);
135 }
136
137 /*
138
139 Drives
140
141 */
142
143 ////////////////////
144 //
GetDrives()145 drive_list GetDrives()
146 {
147 static drive_list list;
148 list.clear();
149 #ifdef WIN32
150 static char drives[34];
151 int len = GetLogicalDriveStrings(sizeof(drives),drives); // Get the list of drives
152 drive_t tmp;
153 if (len) {
154 for (int i=0; i<len; i+=(int)strnlen(&drives[i],4)+1) {
155 // Create the name (for example: C:\)
156 tmp.name = &drives[i];
157 // Get the type
158 tmp.type = GetDriveType((LPCTSTR)tmp.name.c_str());
159 // Add to the list
160 list.push_back(tmp);
161 }
162 }
163
164
165 #else
166 // there are not any drives on Linux/Unix/MacOSX/...
167 // it's only windows which uses this crazy drive-letters
168
169 // perhaps not the best way
170 // home-dir of user is in other applications the default
171 // but it's always possible to read most other stuff
172 // and it's not uncommon that a user hase a shared dir like /mp3s
173 drive_t tmp;
174 tmp.name = "/";
175 tmp.type = 0;
176 list.push_back(tmp);
177
178 // we could communicate with dbus and ask it for all connected
179 // and mounted hardware-stuff
180 #endif
181
182 return list;
183 }
184
185
186 #ifndef WIN32
187
188 // checks, if path is statable (that means, it's existing)
189 // HINT: absolute path and there is not case fixing
190 // (used by GetExactFileName)
IsPathStatable(const std::string & f)191 bool IsPathStatable(const std::string& f) {
192 std::string abs_f = f;
193
194 // remove trailing slashes
195 // don't remove them on WIN, if it is a drive-letter
196 while(abs_f.size() > 0 && (abs_f[abs_f.size()-1] == '\\' || abs_f[abs_f.size()-1] == '/')) {
197 #ifdef WIN32
198 if(abs_f.size() > 2 && abs_f[abs_f.size()-2] == ':') break;
199 #endif
200 abs_f.erase(abs_f.size()-1);
201 }
202
203 // HINT: this should also work on WIN32, as we have _stat here
204 struct stat s;
205 #ifdef WIN32 // uses UTF16
206 return (wstat(Utf8ToUtf16(abs_f).c_str(), &s) == 0); // ...==0, if successfull
207 #else // other systems
208 return (stat(abs_f.c_str(), &s) == 0); // ...==0, if successfull
209 #endif
210 }
211
212
213 // used by unix-GetExactFileName
214 // HINT: it only reads the first char of the seperators
215 // it returns the start of the subdir (the pos _after_ the sep.)
216 // HINT: it returns position in bytes, not in characters
GetNextName(const std::string & fullname,const char ** seperators,std::string & nextname)217 size_t GetNextName(const std::string& fullname, const char** seperators, std::string& nextname)
218 {
219 std::string::const_iterator pos;
220 size_t p = 0;
221 unsigned short i;
222
223 for(pos = fullname.begin(); pos != fullname.end(); pos++, p++) {
224 for(i = 0; seperators[i] != NULL; i++)
225 if(*pos == seperators[i][0]) {
226 nextname = fullname.substr(0, p);
227 return p + 1;
228 }
229 }
230
231 nextname = fullname;
232 return 0;
233 }
234
235 // get ending filename of a path
GetLastName(const std::string & fullname,const char ** seperators)236 size_t GetLastName(const std::string& fullname, const char** seperators)
237 {
238 std::string::const_reverse_iterator pos;
239 size_t p = fullname.size()-1;
240 unsigned short i;
241
242 for(pos = fullname.rbegin(); pos != fullname.rend(); pos++, p--) {
243 for(i = 0; seperators[i] != NULL; i++)
244 if(*pos == seperators[i][0]) {
245 return p;
246 }
247 }
248
249 // indicates that there is no more sep
250 return (size_t)(-1);
251 }
252
253
254
255 struct strcasecomparer {
operator ()strcasecomparer256 bool operator()(const std::string& str1, const std::string& str2) const {
257 return stringcaseequal(str1, str2);
258 }
259 };
260
261 typedef hash_set<std::string, simple_reversestring_hasher, strcasecomparer> exactfilenamecache_t;
262 struct ExactFilenameCache {
263 exactfilenamecache_t cache;
264 Mutex mutex;
265 }
266 exactfilenamecache;
267
is_searchname_in_exactfilenamecache(const std::string & searchname,std::string & exactname)268 bool is_searchname_in_exactfilenamecache(
269 const std::string& searchname,
270 std::string& exactname
271 ) {
272 Mutex::ScopedLock lock(exactfilenamecache.mutex);
273 exactfilenamecache_t::iterator it = exactfilenamecache.cache.find(searchname);
274 if(it != exactfilenamecache.cache.end()) {
275 exactname = *it;
276 return true;
277 } else
278 return false;
279 }
280
add_searchname_to_exactfilenamecache(const std::string & exactname)281 void add_searchname_to_exactfilenamecache(const std::string& exactname) {
282 Mutex::ScopedLock lock(exactfilenamecache.mutex);
283 exactfilenamecache.cache.insert(exactname);
284 }
285
286
287 // used by unix-GetExactFileName
288 // does a case insensitive search for searchname in dir
289 // sets filename to the first search result
290 // returns true, if any file found
CaseInsFindFile(const std::string & dir,const std::string & searchname,std::string & filename)291 bool CaseInsFindFile(const std::string& dir, const std::string& searchname, std::string& filename) {
292 if(searchname == "") {
293 filename = "";
294 return true;
295 }
296
297 // Check first if searchname perhaps exists with exactly this name.
298 // This check is also needed in the case if we cannot read dir (-r) but we can access files (+x) in it.
299 if(IsPathStatable((dir == "") ? searchname : (dir + "/" + searchname))) {
300 filename = searchname;
301 return true;
302 }
303
304 DIR* dirhandle = opendir((dir == "") ? "." : dir.c_str());
305 if(dirhandle == NULL) return false;
306
307 dirent* direntry;
308 while((direntry = readdir(dirhandle))) {
309 if(strcasecmp(direntry->d_name, searchname.c_str()) == 0) {
310 filename = direntry->d_name;
311 closedir(dirhandle);
312 #ifdef DEBUG
313 // HINT: activate this warning temporarly when you want to fix some filenames
314 //if(filename != searchname)
315 // cerr << "filename case mismatch: " << searchname << " <-> " << filename << endl;
316 #endif
317 return true;
318 }
319 add_searchname_to_exactfilenamecache((dir == "") ? direntry->d_name : (dir + "/" + direntry->d_name));
320 }
321
322 closedir(dirhandle);
323 return false;
324 }
325
326
327
328 // does case insensitive search for file
GetExactFileName(const std::string & abs_searchname,std::string & filename)329 bool GetExactFileName(const std::string& abs_searchname, std::string& filename) {
330 const char* seps[] = {"\\", "/", (char*)NULL};
331 if(abs_searchname.size() == 0) {
332 filename = "";
333 return false;
334 }
335
336 std::string sname = abs_searchname;
337 ReplaceFileVariables(sname);
338
339 std::string nextname = "";
340 std::string nextexactname = "";
341 size_t pos;
342
343 bool first_iter = true; // this is used in the bottom loop
344
345 // search in cache
346
347 // sname[0..pos-1] is left rest, excluding the /
348 pos = sname.size();
349 std::string rest;
350 while(true) {
351 rest = sname.substr(0,pos);
352 if(is_searchname_in_exactfilenamecache(rest, filename)) {
353 if(IsPathStatable(filename)) {
354 if(pos == sname.size()) // do we got the whole filename?
355 return true;
356
357 // filename is the correct one here
358 sname.erase(0,pos+1);
359 first_iter = false; // prevents the following loop from not adding a "/" to filename
360 break;
361 }
362 }
363 pos = GetLastName(rest, seps);
364 if(pos == (size_t)(-1)) {
365 first_iter = false;
366 if(rest == "." || rest == "..") {
367 filename = rest;
368 sname.erase(0,rest.size()+1);
369 break;
370 }
371 filename = ".";
372 break;
373 }
374 if(pos == 0) {
375 filename = "/";
376 sname.erase(0,1);
377 break;
378 }
379 }
380
381
382
383 // search the filesystem for the name
384
385 // sname contains the rest of the path
386 // filename contains the start (including a "/" if necces.)
387 // if first_iter is set to true, don't add leading "/"
388 while(true) {
389 pos = GetNextName(sname, seps, nextname);
390 // pos>0 => found a sep (pos is right behind the sep)
391 // pos==0 => none found
392 if(pos > 0) sname.erase(0,pos);
393
394 if(nextname == "") {
395 // simply ignore this case
396 // (we accept sth like /usr///share/)
397 if(pos == 0) break;
398 continue;
399 } else if(!CaseInsFindFile(
400 filename, // dir
401 nextname, // ~name
402 nextexactname // resulted name
403 )) {
404 // we doesn't get any result
405 // just add rest to it
406 if(!first_iter) filename += "/";
407 filename += nextname;
408 if(pos > 0) filename += "/" + sname;
409 return false; // error (not found)
410 }
411
412 if(!first_iter) filename += "/";
413 filename += nextexactname;
414 if(nextexactname != "")
415 add_searchname_to_exactfilenamecache(filename);
416
417 if(pos == 0) break;
418 first_iter = false;
419 }
420
421 // we got here after the full path was resolved successfully
422 return true;
423 }
424
425 #endif // not WIN32
426
427
428 searchpathlist basesearchpaths;
InitBaseSearchPaths()429 void InitBaseSearchPaths() {
430 basesearchpaths.clear();
431 #if defined(__APPLE__)
432 AddToFileList(&basesearchpaths, "${HOME}/Library/Application Support/OpenLieroX");
433 AddToFileList(&basesearchpaths, "Contents/Resources/gamedir");
434 AddToFileList(&basesearchpaths, ".");
435 AddToFileList(&basesearchpaths, SYSTEM_DATA_DIR"/OpenLieroX");
436 #elif defined(WIN32)
437 AddToFileList(&basesearchpaths, "${HOME}/OpenLieroX");
438 AddToFileList(&basesearchpaths, ".");
439 AddToFileList(&basesearchpaths, "${BIN}");
440 #else // all other systems (Linux, *BSD, OS/2, ...)
441 AddToFileList(&basesearchpaths, "${HOME}/.OpenLieroX");
442 AddToFileList(&basesearchpaths, ".");
443 AddToFileList(&basesearchpaths, SYSTEM_DATA_DIR"/OpenLieroX"); // no use of ${SYSTEM_DATA}, because it is uncommon and could cause confusion to the user
444 #endif
445 }
446
CreateRecDir(const std::string & abs_filename,bool last_is_dir)447 void CreateRecDir(const std::string& abs_filename, bool last_is_dir) {
448 std::string tmp;
449 std::string::const_iterator f = abs_filename.begin();
450 for(tmp = ""; f != abs_filename.end(); f++) {
451 if(*f == '\\' || *f == '/')
452 mkdir(tmp.c_str(), 0777);
453 tmp += *f;
454 }
455 if(last_is_dir)
456 mkdir(tmp.c_str(), 0777);
457 }
458
GetFirstSearchPath()459 std::string GetFirstSearchPath() {
460 if(tSearchPaths.size() > 0)
461 return tSearchPaths.front();
462 else if(basesearchpaths.size() > 0)
463 return basesearchpaths.front();
464 else
465 return GetHomeDir();
466 }
467
FileSize(const std::string & path)468 size_t FileSize(const std::string& path)
469 {
470 FILE *fp = OpenGameFile(path, "rb");
471 if (fp == NULL)
472 return 0;
473 fseek(fp, 0, SEEK_END);
474 size_t size = ftell(fp);
475 fclose(fp);
476 return size;
477 }
478
479 // Checks if the given path is absolute
IsAbsolutePath(const std::string & path)480 bool IsAbsolutePath(const std::string& path)
481 {
482 #ifdef WIN32
483 // The path must start with a drive letter
484 if (path.size() < 2)
485 return false;
486
487 return (isalpha((uchar)path[0]) && path[1] == ':');
488 #else
489 // Must start with a slash
490 if (!path.size())
491 return false;
492
493 return path[0] == '/';
494 #endif
495 }
496
497
498 static std::string specialSearchPathForTheme = "";
499
initSpecialSearchPathForTheme()500 void initSpecialSearchPathForTheme() {
501 if(tLXOptions->sTheme != "") {
502 specialSearchPathForTheme = GetFullFileName("themes/" + tLXOptions->sTheme);
503 } else
504 specialSearchPathForTheme = "";
505 }
506
getSpecialSearchPathForTheme()507 const std::string* getSpecialSearchPathForTheme() {
508 if(specialSearchPathForTheme == "")
509 return NULL;
510 else
511 return &specialSearchPathForTheme;
512 }
513
514
515 class CheckSearchpathForFile { public:
516 const std::string& filename;
517 std::string* result;
518 std::string* searchpath;
CheckSearchpathForFile(const std::string & f,std::string * r,std::string * s)519 CheckSearchpathForFile(const std::string& f, std::string* r, std::string* s) : filename(f), result(r), searchpath(s) {}
520
operator ()(const std::string & spath)521 bool operator() (const std::string& spath) {
522 std::string tmp = spath + filename;
523 if(GetExactFileName(tmp, *result)) {
524 // we got here, if the file exists
525 if(searchpath) *searchpath = spath;
526 return false; // stop checking next searchpaths
527 }
528
529 // go to the next searchpath
530 return true;
531 }
532 };
533
GetFullFileName(const std::string & path,std::string * searchpath)534 std::string GetFullFileName(const std::string& path, std::string* searchpath) {
535 if(searchpath) *searchpath = "";
536 if(path == "") return GetFirstSearchPath();
537
538 // Check if we have an absolute path
539 if(IsAbsolutePath(path)) {
540 std::string tmp;
541 GetExactFileName(path, tmp);
542 return tmp;
543 }
544
545 std::string fname;
546 CheckSearchpathForFile checker(path, &fname, searchpath);
547 ForEachSearchpath(checker);
548 return fname;
549 }
550
GetWriteFullFileName(const std::string & path,bool create_nes_dirs)551 std::string GetWriteFullFileName(const std::string& path, bool create_nes_dirs) {
552 std::string tmp;
553 std::string fname;
554
555 // get the dir, where we should write into
556 if(tSearchPaths.size() == 0 && basesearchpaths.size() == 0) {
557 errors << "we want to write somewhere, but don't know where => we are writing to your temp-dir now..." << endl;
558 tmp = GetTempDir() + "/" + path;
559 } else {
560 GetExactFileName(GetFirstSearchPath(), tmp);
561
562 CreateRecDir(tmp);
563 if(!CanWriteToDir(tmp)) {
564 errors << "we cannot write to " << tmp << " => we are writing to your temp-dir now..." << endl;
565 tmp = GetTempDir();
566 }
567
568 tmp += "/";
569 tmp += path;
570 }
571
572 GetExactFileName(tmp, fname);
573 if(create_nes_dirs) CreateRecDir(fname, false);
574 return tmp;
575 }
576
OpenAbsFile(const std::string & path,const char * mode)577 FILE* OpenAbsFile(const std::string& path, const char *mode) {
578 std::string exactfn;
579 if(!GetExactFileName(path, exactfn))
580 return NULL;
581 return fopen(Utf8ToSystemNative(exactfn).c_str(), mode);
582 }
583
OpenGameFile(const std::string & path,const char * mode)584 FILE *OpenGameFile(const std::string& path, const char *mode) {
585 if(path.size() == 0)
586 return NULL;
587
588 std::string fullfn = GetFullFileName(path);
589
590 bool write_mode = strchr(mode, 'w') != 0;
591 bool append_mode = strchr(mode, 'a') != 0;
592 if(write_mode || append_mode) {
593 std::string writefullname = GetWriteFullFileName(path, true);
594 if(append_mode && fullfn != "") { // check, if we should copy the file
595 if(IsFileAvailable(fullfn, true)) { // we found the file
596 // GetWriteFullFileName ensures an exact filename,
597 // so no case insensitive check is needed here
598 if(fullfn != writefullname) {
599 // it is not the file, we would write to, so copy it to the wanted destination
600 if(!FileCopy(fullfn, writefullname)) {
601 errors << "problems while copying, so I cannot open this file in append-mode somewhere else" << endl;
602 return NULL;
603 }
604 }
605 }
606 }
607 //errors << "opening file for writing (mode %s): %s\n", mode, writefullname);
608 return fopen(Utf8ToSystemNative(writefullname).c_str(), mode);
609 }
610
611 if(fullfn.size() != 0) {
612 return fopen(Utf8ToSystemNative(fullfn).c_str(), mode);
613 }
614
615 return NULL;
616 }
617
618
619
OpenGameFileR(const std::string & path)620 std::ifstream* OpenGameFileR(const std::string& path) {
621 if(path.size() == 0)
622 return NULL;
623
624 std::string fullfn = GetFullFileName(path);
625 if(fullfn.size() != 0) {
626 try {
627 std::ifstream* f = new std::ifstream(Utf8ToSystemNative(fullfn).c_str(), std::ios::in | std::ios::binary);
628 if (f->is_open())
629 return f;
630 else {
631 delete f;
632 return NULL;
633 }
634 } catch(...) {}
635 return NULL;
636 }
637
638 return NULL;
639 }
640
641
642
AddToFileList(searchpathlist * l,const std::string & f)643 void AddToFileList(searchpathlist* l, const std::string& f) {
644 if(!FileListIncludesExact(l, f)) l->push_back(f);
645 }
646
removeEndingSlashes(std::string & s)647 void removeEndingSlashes(std::string& s)
648 {
649 while(s.size() > 0 && (*s.rbegin() == '\\' || *s.rbegin() == '/'))
650 s.erase(s.size() - 1);
651 }
652
653 /////////////////
654 // Returns true, if the list contains the path
FileListIncludesExact(const searchpathlist * l,const std::string & f)655 bool FileListIncludesExact(const searchpathlist* l, const std::string& f) {
656 std::string tmp1 = f;
657 removeEndingSlashes(tmp1);
658 ReplaceFileVariables(tmp1);
659 replace(tmp1,"\\","/");
660
661 // Go through the list, checking each item
662 for(searchpathlist::const_iterator i = l->begin(); i != l->end(); i++) {
663 std::string tmp2 = *i;
664 removeEndingSlashes(tmp2);
665 ReplaceFileVariables(tmp2);
666 replace(tmp2,"\\","/");
667 if(stringcaseequal(tmp1, tmp2))
668 return true;
669 }
670
671 return false;
672 }
673
GetHomeDir()674 std::string GetHomeDir() {
675 #ifndef WIN32
676 char* home = getenv("HOME");
677 if(home == NULL || home[0] == '\0') {
678 passwd* userinfo = getpwuid(getuid());
679 if(userinfo)
680 return userinfo->pw_dir;
681 return ""; // both failed, very strange system...
682 }
683 return home;
684 #else
685 static std::string result = "";
686 if (result.size() == 0) { // Only do this once
687 char tmp[1024];
688 if (!SHGetSpecialFolderPath(NULL, tmp, CSIDL_PERSONAL,FALSE)) {
689 // TODO: get dynamicaly another possible path
690 // the following is only a workaround!
691 return "C:\\OpenLieroX";
692 }
693 fix_markend(tmp);
694
695 result = SystemNativeToUtf8(tmp);
696 }
697 return result;
698 #endif
699 }
700
701
GetSystemDataDir()702 std::string GetSystemDataDir() {
703 #ifndef WIN32
704 return SYSTEM_DATA_DIR;
705 #else
706 // windows don't have such dir, don't it?
707 // or should we return windows/system32 (which is not exactly intended here)?
708 return "";
709 #endif
710 }
711
712
713 std::string binary_dir; // given by argv[0], set by main()
714
GetBinaryDir()715 std::string GetBinaryDir() {
716 return binary_dir;
717 }
718
GetTempDir()719 std::string GetTempDir() {
720 #ifndef WIN32
721 return "/tmp"; // year, it's so simple :)
722 #else
723 static char buf[1024] = "";
724 if(buf[0] == '\0') { // only do this once
725 GetTempPath(sizeof(buf), buf);
726 fix_markend(buf);
727 }
728 return SystemNativeToUtf8(buf);
729 #endif
730 }
731
ReplaceFileVariables(std::string & filename)732 void ReplaceFileVariables(std::string& filename) {
733 if(filename.compare(0,2,"~/")==0
734 || filename.compare(0,2,"~\\")==0
735 || filename == "~") {
736 filename.erase(0,1);
737 filename.insert(0,GetHomeDir());
738 }
739 replace(filename, "${HOME}", GetHomeDir());
740 replace(filename, "${SYSTEM_DATA}", GetSystemDataDir());
741 replace(filename, "${BIN}", GetBinaryDir());
742 }
743
744 // WARNING: not multithreading aware
745 // HINT: uses absolute paths
746 // returns true, if successfull
FileCopy(const std::string & src,const std::string & dest)747 bool FileCopy(const std::string& src, const std::string& dest) {
748 static char tmp[2048];
749
750 notes << "FileCopy: " << src << " -> " << dest << endl;
751
752 FILE* src_f = fopen(Utf8ToSystemNative(src).c_str(), "rb");
753
754 if(!src_f) {
755 errors << "FileCopy: cannot open source" << endl;
756 return false;
757 }
758
759 FILE* dest_f = fopen(Utf8ToSystemNative(dest).c_str(), "wb");
760
761 if(!dest_f) {
762 fclose(src_f);
763 errors << "FileCopy: cannot open destination" << endl;
764 return false;
765 }
766
767 bool success = true;
768 unsigned short count = 0;
769 notes << "FileCopy: |" << flush;
770 size_t len = 0;
771 while((len = fread(tmp, 1, sizeof(tmp), src_f)) > 0) {
772 if(count == 0) notes << "." << flush;
773 count++;
774 count %= 20;
775 if(len != fwrite(tmp, 1, len, dest_f)) {
776 errors << "FileCopy: problem while writing" << endl;
777 success = false;
778 break;
779 }
780 if(len != sizeof(tmp)) break;
781 }
782 notes << endl;
783 if(success) {
784 success = feof(src_f) != 0;
785 if(!success) errors << "FileCopy: problem while reading" << endl;
786 }
787
788 fclose(src_f);
789 fclose(dest_f);
790 if(success) notes << "FileCopy: success :)" << endl;
791 return success;
792 }
793
CanWriteToDir(const std::string & dir)794 bool CanWriteToDir(const std::string& dir) {
795 // TODO: we have to make this a lot better!
796 std::string fname = dir + "/.some_stupid_temp_file";
797
798 FILE* fp = fopen(Utf8ToSystemNative(fname).c_str(), "w");
799
800 if(fp) {
801 fclose(fp);
802 remove(Utf8ToSystemNative(fname).c_str());
803
804 return true;
805 }
806 return false;
807 }
808
809
GetAbsolutePath(const std::string & path)810 std::string GetAbsolutePath(const std::string& path) {
811 #ifdef WIN32
812 std::string exactpath;
813 if (!GetExactFileName(path, exactpath))
814 exactpath = path;
815
816 char buf[2048];
817 int len = GetFullPathName(Utf8ToSystemNative(exactpath).c_str(), sizeof(buf), buf, NULL);
818 fix_markend(buf);
819 if (len)
820 return SystemNativeToUtf8(buf);
821 else // Failed
822 return path;
823 #else
824 std::string exactpath;
825 if(GetExactFileName(path, exactpath)) {
826 char buf[PATH_MAX];
827 if(realpath(exactpath.c_str(), buf) != NULL) {
828 fix_markend(buf);
829 return buf;
830 } else
831 return exactpath;
832 } else
833 return path;
834 #endif
835 }
836
PathListIncludes(const std::list<std::string> & pathlist,const std::string & path)837 bool PathListIncludes(const std::list<std::string>& pathlist, const std::string& path) {
838 std::string abs_path;
839 abs_path = GetAbsolutePath(path);
840
841 // Go through the list, checking each item
842 for(std::list<std::string>::const_iterator i = pathlist.begin(); i != pathlist.end(); i++) {
843 if(EqualPaths(abs_path, GetAbsolutePath(*i))) {
844 return true;
845 }
846 }
847
848 return false;
849 }
850
851 ///////////////////////
852 // Returns the file contents as a string
GetFileContents(const std::string & path,bool absolute)853 std::string GetFileContents(const std::string& path, bool absolute)
854 {
855 FILE *fp = NULL;
856 if (absolute)
857 fp = fopen(Utf8ToSystemNative(path).c_str(), "rb");
858 else
859 fp = OpenGameFile(path, "rb");
860
861 if (!fp)
862 return "";
863
864 fseek(fp, 0, SEEK_END);
865 long size = ftell(fp);
866 fseek(fp, 0, SEEK_SET);
867
868 if (!size || size < 0 || size > 100000000) {
869 fclose(fp);
870 return "";
871 }
872
873 char *buf = new char[size];
874 size = fread(buf, 1, size, fp);
875 if (!size) {
876 delete[] buf;
877 fclose(fp);
878 return "";
879 }
880
881 std::string result;
882 result.append(buf, size);
883 delete[] buf;
884 fclose(fp);
885
886 return result;
887 }
888
889 ////////////////
890 // Extract the directory part from a path
ExtractDirectory(const std::string & path)891 std::string ExtractDirectory(const std::string& path)
892 {
893 if (path.size() == 0)
894 return "";
895
896 size_t pos = findLastPathSep(path);
897 if (pos == std::string::npos)
898 return path;
899 else
900 return path.substr(0, pos);
901 }
902
903
GetScriptInterpreterCommandForFile(const std::string & filename)904 std::string GetScriptInterpreterCommandForFile(const std::string& filename) {
905 FILE* f = OpenGameFile(filename, "r");
906 if(f) {
907 std::string line = ReadUntil(f);
908 if(line.size() > 2 && line[0] == '#' && line[1] == '!') {
909 std::string cmd = line.substr(2);
910 TrimSpaces(cmd);
911 fclose(f);
912 return cmd;
913 }
914 fclose(f);
915 return "";
916 }
917 return "";
918 }
919
920
921 ///////////////////
922 // Merges two parts of a path into one, for example JoinPath("./test/", "/file.fil") gives "./test/file.fil"
JoinPaths(const std::string & path1,const std::string & path2)923 std::string JoinPaths(const std::string& path1, const std::string& path2)
924 {
925 if (path1.size() == 0)
926 return path2;
927 if (path2.size() == 0)
928 return path1;
929
930 std::string result = path1;
931 if (*path1.rbegin() == '/' || *path1.rbegin() == '\\') {
932 if (*path2.begin() == '/' || *path2.begin() == '\\') {
933 result.erase(result.size() - 1);
934 result += path2;
935 return result;
936 } else {
937 result += path2;
938 return result;
939 }
940 } else {
941 if (*path2.begin() == '/' || *path2.begin() == '\\') {
942 result += path2;
943 return result;
944 } else {
945 result += '/';
946 result += path2;
947 return result;
948 }
949 }
950 }
951
952 //////////////////////////////
953 // Creating SDL_RWops structure from a file pointer
954 // We cannot use SDL's function for this under WIN32 because it doesn't allow
955 // passing file pointers to a dll
956
957 #include <SDL.h>
958
959 // These are taken from SDL_rwops.c
960 #ifdef WIN32
stdio_seek(SDL_RWops * context,int offset,int whence)961 static int stdio_seek(SDL_RWops *context, int offset, int whence)
962 {
963 if ( fseek(context->hidden.stdio.fp, offset, whence) == 0 ) {
964 return(ftell(context->hidden.stdio.fp));
965 } else {
966 SDL_Error(SDL_EFSEEK);
967 return(-1);
968 }
969 }
stdio_read(SDL_RWops * context,void * ptr,int size,int maxnum)970 static int stdio_read(SDL_RWops *context, void *ptr, int size, int maxnum)
971 {
972 size_t nread;
973
974 nread = fread(ptr, size, maxnum, context->hidden.stdio.fp);
975 if ( nread == 0 && ferror(context->hidden.stdio.fp) ) {
976 SDL_Error(SDL_EFREAD);
977 }
978 return (int)(nread);
979 }
stdio_write(SDL_RWops * context,const void * ptr,int size,int num)980 static int stdio_write(SDL_RWops *context, const void *ptr, int size, int num)
981 {
982 size_t nwrote;
983
984 nwrote = fwrite(ptr, size, num, context->hidden.stdio.fp);
985 if ( nwrote == 0 && ferror(context->hidden.stdio.fp) ) {
986 SDL_Error(SDL_EFWRITE);
987 }
988 return (int)(nwrote);
989 }
stdio_close(SDL_RWops * context)990 static int stdio_close(SDL_RWops *context)
991 {
992 if ( context ) {
993 if ( context->hidden.stdio.autoclose ) {
994 /* WARNING: Check the return value here! */
995 fclose(context->hidden.stdio.fp);
996 }
997 free(context);
998 }
999 return(0);
1000 }
1001 #endif
1002
1003 ////////////////
1004 // Creates SDL_RWops from a file pointer
RWopsFromFP(FILE * fp,bool autoclose)1005 SDL_RWops *RWopsFromFP(FILE *fp, bool autoclose) {
1006 #ifdef WIN32
1007 // Taken from SDL code
1008 SDL_RWops *rwops = SDL_AllocRW();
1009 if ( rwops != NULL ) {
1010 rwops->seek = stdio_seek;
1011 rwops->read = stdio_read;
1012 rwops->write = stdio_write;
1013 rwops->close = stdio_close;
1014 rwops->hidden.stdio.fp = fp;
1015 rwops->hidden.stdio.autoclose = (int)autoclose;
1016 }
1017 return(rwops);
1018
1019 #else
1020 return SDL_RWFromFP(fp, (int)autoclose);
1021 #endif
1022 }
1023
1024