factored out the EFFv2 saving into EFFImporter
[gemrb.git] / gemrb / core / System / VFS.cpp
blob4e6d34894893477dadcb195d972f76ada1bcbf4b
1 /* GemRB - Infinity Engine Emulator
2 * Copyright (C) 2003 The GemRB Project
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 // VFS.cpp : functions to access filesystem in os-independent way
22 // and POSIX-like compatibility layer for win
24 #include "System/VFS.h"
26 #include "globals.h"
28 #include "Interface.h"
30 #if defined(__HAIKU__)
31 #include <unistd.h>
32 #endif
34 #include <cstdarg>
35 #include <cstring>
37 #ifndef WIN32
38 #include <dirent.h>
39 #endif
41 #ifndef S_ISDIR
42 #define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
43 #endif
45 #ifndef S_ISREG
46 #define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
47 #endif
49 #ifdef WIN32
51 struct DIR {
52 char path[_MAX_PATH];
53 bool is_first;
54 struct _finddata_t c_file;
55 long hFile;
58 struct dirent {
59 char d_name[_MAX_PATH];
62 // buffer which readdir returns
63 static dirent de;
65 DIR* opendir(const char* filename)
67 DIR* dirp = ( DIR* ) malloc( sizeof( DIR ) );
68 dirp->is_first = 1;
70 sprintf( dirp->path, "%s%s*.*", filename, SPathDelimiter );
71 //if((hFile = (long)_findfirst(Path, &c_file)) == -1L) //If there is no file matching our search
73 return dirp;
76 dirent* readdir(DIR* dirp)
78 struct _finddata_t c_file;
80 if (dirp->is_first) {
81 dirp->is_first = 0;
82 dirp->hFile = ( long ) _findfirst( dirp->path, &c_file );
83 if (dirp->hFile == -1L)
84 return NULL;
85 } else {
86 if (( long ) _findnext( dirp->hFile, &c_file ) != 0) {
87 return NULL;
91 strcpy( de.d_name, c_file.name );
93 return &de;
96 void closedir(DIR* dirp)
98 _findclose( dirp->hFile );
99 free( dirp );
103 _FILE* _fopen(const char* filename, const char* mode)
105 DWORD OpenFlags = 0;
106 DWORD AccessFlags = 0;
107 DWORD ShareFlags = 0;
109 while (*mode) {
110 if (( *mode == 'w' ) || ( *mode == 'W' )) {
111 OpenFlags |= OPEN_ALWAYS;
112 AccessFlags |= GENERIC_WRITE;
113 ShareFlags |= FILE_SHARE_READ;
114 } else if (( *mode == 'r' ) || ( *mode == 'R' )) {
115 OpenFlags |= OPEN_EXISTING;
116 AccessFlags |= GENERIC_READ;
117 ShareFlags |= FILE_SHARE_READ|FILE_SHARE_WRITE;
118 } else if (( *mode == 'a' ) || ( *mode == 'A' )) {
119 OpenFlags |= OPEN_ALWAYS;
120 AccessFlags |= GENERIC_READ|GENERIC_WRITE;
121 ShareFlags |= FILE_SHARE_READ|FILE_SHARE_WRITE;
122 } else if (*mode == '+') {
123 AccessFlags |= GENERIC_READ|GENERIC_WRITE;
124 ShareFlags |= FILE_SHARE_READ|FILE_SHARE_WRITE;
126 mode++;
128 HANDLE hFile = CreateFile( filename, AccessFlags, ShareFlags, NULL,
129 OpenFlags, FILE_ATTRIBUTE_NORMAL, NULL );
130 if (hFile == INVALID_HANDLE_VALUE) {
131 return NULL;
133 _FILE* ret = ( _FILE* ) malloc( sizeof( _FILE ) );
134 ret->hFile = hFile;
135 return ret;
138 size_t _fread(void* ptr, size_t size, size_t n, _FILE* stream)
140 if (!stream) {
141 return ( size_t ) 0;
143 unsigned long read;
144 if (!ReadFile( stream->hFile, ptr, ( unsigned long ) ( size * n ), &read,
145 NULL )) {
146 return ( size_t ) 0;
148 return ( size_t ) read;
151 size_t _fwrite(const void* ptr, size_t size, size_t n, _FILE* stream)
153 if (!stream) {
154 return ( size_t ) 0;
156 unsigned long wrote;
157 if (!WriteFile( stream->hFile, ptr, ( unsigned long ) ( size * n ),
158 &wrote, NULL )) {
159 return ( size_t ) 0;
161 return ( size_t ) wrote;
164 size_t _fseek(_FILE* stream, long offset, int whence)
166 if (!stream) {
167 return ( size_t ) 1;
169 unsigned long method;
170 switch (whence) {
171 case SEEK_SET:
172 method = FILE_BEGIN;
173 break;
174 case SEEK_CUR:
175 method = FILE_CURRENT;
176 break;
177 case SEEK_END:
178 method = FILE_END;
179 break;
180 default:
181 return ( size_t ) 1;
183 if (SetFilePointer( stream->hFile, offset, NULL, method ) == 0xffffffff) {
184 return ( size_t ) 1;
186 return ( size_t ) 0;
189 int _fgetc(_FILE* stream)
191 if (!stream) {
192 return 0;
194 unsigned char tmp;
195 unsigned long read;
196 BOOL bResult = ReadFile( stream->hFile, &tmp, ( unsigned long ) 1, &read,
197 NULL );
198 if (bResult && read) {
199 return ( int ) tmp;
201 return EOF;
204 long int _ftell(_FILE* stream)
206 if (!stream) {
207 return EOF;
209 unsigned long pos = SetFilePointer( stream->hFile, 0, NULL, FILE_CURRENT );
210 if (pos == 0xffffffff) {
211 return -1L;
213 return ( long int ) pos;
216 int _feof(_FILE* stream)
218 if (!stream) {
219 return 0;
221 unsigned char tmp;
222 unsigned long read;
223 BOOL bResult = ReadFile( stream->hFile, &tmp, ( unsigned long ) 1, &read,
224 NULL );
225 if (bResult && ( read == 0 )) {
226 return 1;
227 } //EOF
228 bResult = SetFilePointer( stream->hFile, -1, NULL, FILE_CURRENT );
229 return 0;
232 int _fclose(_FILE* stream)
234 if (!stream) {
235 return EOF;
237 if (!CloseHandle( stream->hFile )) {
238 return EOF;
240 free( stream );
241 return 0;
244 #endif // WIN32
247 /** Returns true if path is an existing directory */
248 bool dir_exists(const char* path)
250 struct stat buf;
251 buf.st_mode = 0;
253 if (stat(path, &buf) < 0) {
254 return false;
256 if (!S_ISDIR(buf.st_mode)) {
257 return false;
260 return true;
263 /** Returns true if path is an existing directory */
264 bool file_exists(const char* path)
266 struct stat buf;
267 buf.st_mode = 0;
269 if (stat(path, &buf) < 0) {
270 return false;
272 if (!S_ISREG(buf.st_mode)) {
273 return false;
276 return true;
281 * Appends 'name' to path 'target' and returns 'target'.
282 * It takes care of inserting PathDelimiter ('/' or '\\') if needed
284 char* PathAppend (char* target, const char* name)
286 size_t len = strlen(target);
288 if (target[0] != 0 && target[len-1] != PathDelimiter && len+1 < _MAX_PATH) {
289 target[len++] = PathDelimiter;
290 target[len] = 0;
292 strncat( target+len, name, _MAX_PATH - len - 1 );
294 return target;
298 bool FindInDir(const char* Dir, char *Filename)
300 // First test if there's a Filename with exactly same name
301 // and if yes, return it and do not search in the Dir
302 char TempFilePath[_MAX_PATH];
303 strcpy(TempFilePath, Dir);
304 PathAppend( TempFilePath, Filename );
306 if (!access( TempFilePath, R_OK )) {
307 return true;
310 if (!core->CaseSensitive) {
311 return false;
314 DirectoryIterator dir(Dir);
315 if (!dir) {
316 return false;
319 // Exact match not found, so try to search for Filename
320 // with different case
321 do {
322 const char *name = dir.GetName();
323 if (stricmp( name, Filename ) == 0) {
324 strcpy( Filename, name );
325 return true;
327 } while (++dir);
328 return false;
331 bool PathJoin (char *target, const char *base, ...)
333 va_list ap;
334 va_start(ap, base);
336 if (base == NULL) {
337 target[0] = '\0';
338 return false;
341 strcpy(target, base);
343 while (char *source = va_arg(ap, char*)) {
344 char *slash;
345 do {
346 char filename[_MAX_PATH] = { '\0' };
347 slash = strchr(source, PathDelimiter);
348 if (slash == source) {
349 ++source;
350 continue;
351 } else if (slash) {
352 strncat(filename, source, slash-source);
353 } else {
354 strcpy(filename, source);
356 if (!FindInDir(target, filename)) {
357 PathAppend(target, source);
358 goto finish;
360 PathAppend(target, filename);
361 source = slash + 1;
362 } while (slash);
364 va_end( ap );
365 return true;
366 finish:
367 while (char *source = va_arg(ap, char*)) {
368 PathAppend(target, source);
370 va_end( ap );
371 return false;
374 bool PathJoinExt (char* target, const char* dir, const char* base, const char* ext)
376 char file[_MAX_PATH];
377 strcpy(file, base);
378 strcat(file, ".");
379 strcat(file, ext);
380 return PathJoin(target, dir, file, NULL);
383 /** Fixes path delimiter character (slash).
384 * needslash = true : we add a slash
385 * needslash = false: we remove the slash
387 void FixPath (char *path, bool needslash)
389 size_t i = strlen( path ) - 1;
391 if (needslash) {
392 if (path[i] == PathDelimiter) return;
394 // if path is already too long, don't do anything
395 if (i >= _MAX_PATH - 2) return;
396 i++;
397 path[i++] = PathDelimiter;
399 else {
400 if (path[i] != PathDelimiter) return;
402 path[i] = 0;
405 int strmatch(const char *string, const char *mask)
407 while(*mask) {
408 if (*mask!='?') {
409 if (tolower(*mask)!=tolower(*string)) {
410 return 1;
413 mask++;
414 string++;
416 return 0;
419 bool FileGlob(char* target, const char* Dir, const char *glob)
421 DirectoryIterator dir(Dir);
422 if (!dir) {
423 return false;
426 do {
427 const char *name = dir.GetName();
428 if (strmatch( name, glob ) == 0) {
429 strcpy( target, name );
430 return true;
432 } while (++dir);
433 return false;
437 #ifndef WIN32
439 void ResolveFilePath(char* FilePath)
441 char TempFilePath[_MAX_PATH];
443 if (FilePath[0]=='~') {
444 const char *home = getenv("HOME");
445 if (home) {
446 strcpy(TempFilePath, FilePath+1);
447 PathJoin(FilePath, home, TempFilePath, NULL);
448 return;
452 if (core && !core->CaseSensitive) {
453 return;
455 strcpy(TempFilePath, FilePath);
456 PathJoin(FilePath, TempFilePath[0]==PathDelimiter?SPathDelimiter:"", TempFilePath, NULL);
459 void ResolveFilePath(std::string& FilePath)
461 char TempFilePath[_MAX_PATH];
463 if (FilePath[0]=='~') {
464 const char *home = getenv("HOME");
465 if (home) {
466 PathJoin(TempFilePath, home, FilePath.c_str()+1, NULL);
467 FilePath = TempFilePath;
468 return;
472 if (core && !core->CaseSensitive) {
473 return;
475 PathJoin(TempFilePath, FilePath[0]==PathDelimiter?SPathDelimiter:"", FilePath.c_str(), NULL);
476 FilePath = TempFilePath;
479 #endif
481 void ExtractFileFromPath(char *file, const char *full_path)
483 const char *p;
484 if ((p = strrchr (full_path, PathDelimiter)))
485 strcpy(file, p+1);
486 else if ((p = strchr (full_path, ':')))
487 strcpy(file, p+1);
488 else
489 strcpy(file, full_path);
492 DirectoryIterator::DirectoryIterator(const char *path)
493 : Directory(NULL), Entry(NULL), Path(path)
495 Rewind();
498 DirectoryIterator::~DirectoryIterator()
500 if (Directory)
501 closedir(static_cast<DIR*>(Directory));
504 bool DirectoryIterator::IsDirectory()
506 char dtmp[_MAX_PATH];
507 struct stat fst;
508 GetFullPath(dtmp);
509 stat( dtmp, &fst );
510 return S_ISDIR( fst.st_mode );
513 char* DirectoryIterator::GetName()
515 return static_cast<dirent*>(Entry)->d_name;
518 void DirectoryIterator::GetFullPath(char *name)
520 snprintf(name, _MAX_PATH, "%s%s%s", Path, SPathDelimiter, static_cast<dirent*>(Entry)->d_name);
523 DirectoryIterator& DirectoryIterator::operator++()
525 Entry = readdir(static_cast<DIR*>(Directory));
526 return *this;
529 void DirectoryIterator::Rewind()
531 if (Directory)
532 closedir(static_cast<DIR*>(Directory));
533 Directory = opendir(Path);
534 if (Directory == NULL)
535 Entry = NULL;
536 else
537 Entry = readdir(static_cast<DIR*>(Directory));