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