From 33d4c79f01343aa31bf2ed7b4170f841c7833285 Mon Sep 17 00:00:00 2001 From: albertzeyer Date: Fri, 24 Jul 2009 11:29:11 +0000 Subject: [PATCH] added FindFile.cpp (for searchpath system) git-svn-id: https://clonekeenplus.svn.sourceforge.net/svnroot/clonekeenplus/cgenius/trunk@133 4df4b0f3-56ce-47cb-b001-ed939b7d65a6 --- src/FindFile.cpp | 1022 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1022 insertions(+) create mode 100644 src/FindFile.cpp diff --git a/src/FindFile.cpp b/src/FindFile.cpp new file mode 100644 index 000000000..f6c29a2ae --- /dev/null +++ b/src/FindFile.cpp @@ -0,0 +1,1022 @@ +///////////////////////////////////////// +// +// OpenLieroX +// +// Auxiliary Software class library +// +// based on the work of JasonB +// enhanced by Dark Charlie and Albert Zeyer +// +// code under LGPL +// +///////////////////////////////////////// + + +// File finding routines +// Created 30/9/01 +// By Jason Boettcher + + + + +#ifdef _MSC_VER +#pragma warning(disable: 4786) // WARNING: identifier XXX was truncated to 255 characters in the debug info +#pragma warning(disable: 4503) // WARNING: decorated name length exceeded, name was truncated +#endif + +#include "FindFile.h" +#include "StringUtils.h" +#include "Debug.h" + + +#ifdef WIN32 +# ifndef _WIN32_IE +// Some functions that are required are unavailable if this is not defined: +// TODO: which functions? +# define _WIN32_IE 0x0400 // Because of Dev-cpp +# endif + +# include + +#else // WIN32 + +// not defined for older GCC versions; we check only for >=4.3 anyway +# ifndef __GNUC_PREREQ +# define __GNUC_PREREQ(maj, min) (0) +# endif + +// include hash_set support +# if !defined(STLPORT) +# if defined(__GNUC__) && __GNUC_PREREQ(4,3) +# include +# define hash_set std::tr1::unordered_set +# else +# include + using __gnu_cxx::hash_set; +# endif +# else // STLPORT +# include + using std::hash_set; +# endif + +// for getpwduid +# include + +// for realpath +# include +# include + +#endif + + +searchpathlist tSearchPaths; + + + +bool IsFileAvailable(const std::string& f, bool absolute) { + std::string abs_f; + if(absolute) { + abs_f = f; + } else + if((abs_f = GetFullFileName(f)) == "") return false; + + // remove trailing slashes + // don't remove them on WIN, if it is a drive-letter + while(abs_f.size() > 0 && (abs_f[abs_f.size()-1] == '\\' || abs_f[abs_f.size()-1] == '/')) { +#ifdef WIN32 + if(abs_f.size() > 2 && abs_f[abs_f.size()-2] == ':') break; +#endif + abs_f.erase(abs_f.size()-1); + } + + abs_f = Utf8ToSystemNative(abs_f); + + // HINT: this should also work on WIN32, as we have _stat here + struct stat s; + if(stat(abs_f.c_str(), &s) != 0 || !S_ISREG(s.st_mode)) { + // it's not stat-able or not a reg file + return false; + } + + // it's stat-able and a file + return true; +} + + + +////////////////////// +// Replaces backward slashes with forward slashes (windows only) +// Used when comparing two paths +static void ReplaceSlashes(std::string& path) { +#ifdef WIN32 + for (std::string::iterator it = path.begin(); it != path.end(); it++) + if (*it == '\\') *it = '/'; +#endif +} + +bool EqualPaths(const std::string& path1, const std::string& path2) { + std::string p1 = path1; + std::string p2 = path2; + + ReplaceSlashes(p1); + ReplaceSlashes(p2); + + if (*p1.rbegin() != '/') + p1 += '/'; + if (*p2.rbegin() != '/') + p2 += '/'; + + return stringcaseequal(p1, p2); +} + +/* + + Drives + +*/ + +//////////////////// +// +drive_list GetDrives() +{ +static drive_list list; +list.clear(); +#ifdef WIN32 + static char drives[34]; + int len = GetLogicalDriveStrings(sizeof(drives),drives); // Get the list of drives + drive_t tmp; + if (len) { + for (register int i=0; i 0 && (abs_f[abs_f.size()-1] == '\\' || abs_f[abs_f.size()-1] == '/')) { +#ifdef WIN32 + if(abs_f.size() > 2 && abs_f[abs_f.size()-2] == ':') break; +#endif + abs_f.erase(abs_f.size()-1); + } + + // HINT: this should also work on WIN32, as we have _stat here + struct stat s; +#ifdef WIN32 // uses UTF16 + return (wstat(Utf8ToUtf16(abs_f).c_str(), &s) == 0); // ...==0, if successfull +#else // other systems + return (stat(abs_f.c_str(), &s) == 0); // ...==0, if successfull +#endif +} + + +// used by unix-GetExactFileName +// HINT: it only reads the first char of the seperators +// it returns the start of the subdir (the pos _after_ the sep.) +// HINT: it returns position in bytes, not in characters +size_t GetNextName(const std::string& fullname, const char** seperators, std::string& nextname) +{ + std::string::const_iterator pos; + size_t p = 0; + unsigned short i; + + for(pos = fullname.begin(); pos != fullname.end(); pos++, p++) { + for(i = 0; seperators[i] != NULL; i++) + if(*pos == seperators[i][0]) { + nextname = fullname.substr(0, p); + return p + 1; + } + } + + nextname = fullname; + return 0; +} + +// get ending filename of a path +size_t GetLastName(const std::string& fullname, const char** seperators) +{ + std::string::const_reverse_iterator pos; + size_t p = fullname.size()-1; + unsigned short i; + + for(pos = fullname.rbegin(); pos != fullname.rend(); pos++, p--) { + for(i = 0; seperators[i] != NULL; i++) + if(*pos == seperators[i][0]) { + return p; + } + } + + // indicates that there is no more sep + return (size_t)(-1); +} + + + +struct strcasecomparer { + bool operator()(const std::string& str1, const std::string& str2) const { + return stringcaseequal(str1, str2); + } +}; + +typedef hash_set exactfilenamecache_t; +struct ExactFilenameCache { + exactfilenamecache_t cache; + Mutex mutex; +} +exactfilenamecache; + +bool is_searchname_in_exactfilenamecache( + const std::string& searchname, + std::string& exactname +) { + Mutex::ScopedLock lock(exactfilenamecache.mutex); + exactfilenamecache_t::iterator it = exactfilenamecache.cache.find(searchname); + if(it != exactfilenamecache.cache.end()) { + exactname = *it; + return true; + } else + return false; +} + +void add_searchname_to_exactfilenamecache(const std::string& exactname) { + Mutex::ScopedLock lock(exactfilenamecache.mutex); + exactfilenamecache.cache.insert(exactname); +} + + +// used by unix-GetExactFileName +// does a case insensitive search for searchname in dir +// sets filename to the first search result +// returns true, if any file found +bool CaseInsFindFile(const std::string& dir, const std::string& searchname, std::string& filename) { + if(searchname == "") { + filename = ""; + return true; + } + + // Check first if searchname perhaps exists with exactly this name. + // This check is also needed in the case if we cannot read dir (-r) but we can access files (+x) in it. + if(IsPathStatable((dir == "") ? searchname : (dir + "/" + searchname))) { + filename = searchname; + return true; + } + + DIR* dirhandle = opendir((dir == "") ? "." : dir.c_str()); + if(dirhandle == NULL) return false; + + dirent* direntry; + while((direntry = readdir(dirhandle))) { + if(strcasecmp(direntry->d_name, searchname.c_str()) == 0) { + filename = direntry->d_name; + closedir(dirhandle); +#ifdef DEBUG + // HINT: activate this warning temporarly when you want to fix some filenames + //if(filename != searchname) + // cerr << "filename case mismatch: " << searchname << " <-> " << filename << endl; +#endif + return true; + } + add_searchname_to_exactfilenamecache((dir == "") ? direntry->d_name : (dir + "/" + direntry->d_name)); + } + + closedir(dirhandle); + return false; +} + + + +// does case insensitive search for file +bool GetExactFileName(const std::string& abs_searchname, std::string& filename) { + const char* seps[] = {"\\", "/", (char*)NULL}; + if(abs_searchname.size() == 0) { + filename = ""; + return false; + } + + std::string sname = abs_searchname; + ReplaceFileVariables(sname); + + std::string nextname = ""; + std::string nextexactname = ""; + size_t pos; + + bool first_iter = true; // this is used in the bottom loop + + // search in cache + + // sname[0..pos-1] is left rest, excluding the / + pos = sname.size(); + std::string rest; + while(true) { + rest = sname.substr(0,pos); + if(is_searchname_in_exactfilenamecache(rest, filename)) { + if(IsPathStatable(filename)) { + if(pos == sname.size()) // do we got the whole filename? + return true; + + // filename is the correct one here + sname.erase(0,pos+1); + first_iter = false; // prevents the following loop from not adding a "/" to filename + break; + } + } + pos = GetLastName(rest, seps); + if(pos == (size_t)(-1)) { + first_iter = false; + if(rest == "." || rest == "..") { + filename = rest; + sname.erase(0,rest.size()+1); + break; + } + filename = "."; + break; + } + if(pos == 0) { + filename = "/"; + sname.erase(0,1); + break; + } + } + + + + // search the filesystem for the name + + // sname contains the rest of the path + // filename contains the start (including a "/" if necces.) + // if first_iter is set to true, don't add leading "/" + while(true) { + pos = GetNextName(sname, seps, nextname); + // pos>0 => found a sep (pos is right behind the sep) + // pos==0 => none found + if(pos > 0) sname.erase(0,pos); + + if(nextname == "") { + // simply ignore this case + // (we accept sth like /usr///share/) + if(pos == 0) break; + continue; + } else if(!CaseInsFindFile( + filename, // dir + nextname, // ~name + nextexactname // resulted name + )) { + // we doesn't get any result + // just add rest to it + if(!first_iter) filename += "/"; + filename += nextname; + if(pos > 0) filename += "/" + sname; + return false; // error (not found) + } + + if(!first_iter) filename += "/"; + filename += nextexactname; + if(nextexactname != "") + add_searchname_to_exactfilenamecache(filename); + + if(pos == 0) break; + first_iter = false; + } + + // we got here after the full path was resolved successfully + return true; +} + +#endif // not WIN32 + + +searchpathlist basesearchpaths; +void InitBaseSearchPaths() { + basesearchpaths.clear(); +#if defined(MACOSX) + AddToFileList(&basesearchpaths, "${HOME}/Library/Application Support/OpenLieroX"); + AddToFileList(&basesearchpaths, "."); + AddToFileList(&basesearchpaths, "${BIN}/../Resources/gamedir"); + AddToFileList(&basesearchpaths, SYSTEM_DATA_DIR"/OpenLieroX"); +#elif defined(WIN32) + AddToFileList(&basesearchpaths, "${HOME}/OpenLieroX"); + AddToFileList(&basesearchpaths, "."); + AddToFileList(&basesearchpaths, "${BIN}"); +#else // all other systems (Linux, *BSD, OS/2, ...) + AddToFileList(&basesearchpaths, "${HOME}/.OpenLieroX"); + AddToFileList(&basesearchpaths, "."); + AddToFileList(&basesearchpaths, SYSTEM_DATA_DIR"/OpenLieroX"); // no use of ${SYSTEM_DATA}, because it is uncommon and could cause confusion to the user +#endif +} + +void CreateRecDir(const std::string& abs_filename, bool last_is_dir) { + std::string tmp; + std::string::const_iterator f = abs_filename.begin(); + for(tmp = ""; f != abs_filename.end(); f++) { + if(*f == '\\' || *f == '/') + mkdir(tmp.c_str(), 0777); + tmp += *f; + } + if(last_is_dir) + mkdir(tmp.c_str(), 0777); +} + +std::string GetFirstSearchPath() { + if(tSearchPaths.size() > 0) + return tSearchPaths.front(); + else if(basesearchpaths.size() > 0) + return basesearchpaths.front(); + else + return GetHomeDir(); +} + +size_t FileSize(const std::string& path) +{ + FILE *fp = fopen(path.c_str(), "rb"); + if (!fp) { + fp = OpenGameFile(path, "rb"); + if (!fp) + return 0; + } + fseek(fp, 0, SEEK_END); + size_t size = ftell(fp); + fclose(fp); + return size; +} + +// Checks if the given path is absolute +bool IsAbsolutePath(const std::string& path) +{ +#ifdef WIN32 + // The path must start with a drive letter + if (path.size() < 2) + return false; + + return (isalpha((uchar)path[0]) && path[1] == ':'); +#else + // Must start with a slash + if (!path.size()) + return false; + + return path[0] == '/'; +#endif +} + + +static std::string specialSearchPathForTheme = ""; + +void initSpecialSearchPathForTheme() { + /*if(tLXOptions->sTheme != "") { + specialSearchPathForTheme = GetFullFileName("themes/" + tLXOptions->sTheme); + } else */ + specialSearchPathForTheme = ""; +} + +const std::string* getSpecialSearchPathForTheme() { + if(specialSearchPathForTheme == "") + return NULL; + else + return &specialSearchPathForTheme; +} + + + class CheckSearchpathForFile { public: + const std::string& filename; + std::string* result; + std::string* searchpath; + CheckSearchpathForFile(const std::string& f, std::string* r, std::string* s) : filename(f), result(r), searchpath(s) {} + + bool operator() (const std::string& spath) { + std::string tmp = spath + filename; + if(GetExactFileName(tmp, *result)) { + // we got here, if the file exists + if(searchpath) *searchpath = spath; + return false; // stop checking next searchpaths + } + + // go to the next searchpath + return true; + } + }; + +std::string GetFullFileName(const std::string& path, std::string* searchpath) { + if(searchpath) *searchpath = ""; + if(path == "") return GetFirstSearchPath(); + + // Check if we have an absolute path + if(IsAbsolutePath(path)) { + std::string tmp; + GetExactFileName(path, tmp); + return tmp; + } + + std::string fname; + CheckSearchpathForFile checker(path, &fname, searchpath); + ForEachSearchpath(checker); + + return fname; +} + +std::string GetWriteFullFileName(const std::string& path, bool create_nes_dirs) { + std::string tmp; + std::string fname; + + // get the dir, where we should write into + if(tSearchPaths.size() == 0 && basesearchpaths.size() == 0) { + errors << "we want to write somewhere, but don't know where => we are writing to your temp-dir now..." << endl; + tmp = GetTempDir() + "/" + path; + } else { + GetExactFileName(GetFirstSearchPath(), tmp); + + CreateRecDir(tmp); + if(!CanWriteToDir(tmp)) { + errors << "we cannot write to " << tmp << " => we are writing to your temp-dir now..." << endl; + tmp = GetTempDir(); + } + + tmp += "/"; + tmp += path; + } + + GetExactFileName(tmp, fname); + if(create_nes_dirs) CreateRecDir(fname, false); + return tmp; +} + +FILE* OpenAbsFile(const std::string& path, const char *mode) { + std::string exactfn; + if(!GetExactFileName(path, exactfn)) + return NULL; + return fopen(exactfn.c_str(), mode); +} + +FILE *OpenGameFile(const std::string& path, const char *mode) { + if(path.size() == 0) + return NULL; + + std::string fullfn = GetFullFileName(path); + + bool write_mode = strchr(mode, 'w') != 0; + bool append_mode = strchr(mode, 'a') != 0; + if(write_mode || append_mode) { + std::string writefullname = GetWriteFullFileName(path, true); + if(append_mode && fullfn != "") { // check, if we should copy the file + if(IsFileAvailable(fullfn, true)) { // we found the file + // GetWriteFullFileName ensures an exact filename, + // so no case insensitive check is needed here + if(fullfn != writefullname) { + // it is not the file, we would write to, so copy it to the wanted destination + if(!FileCopy(fullfn, writefullname)) { + errors << "problems while copying, so I cannot open this file in append-mode somewhere else" << endl; + return NULL; + } + } + } + } + //errors << "opening file for writing (mode %s): %s\n", mode, writefullname); + return fopen(Utf8ToSystemNative(writefullname).c_str(), mode); + } + + if(fullfn.size() != 0) { + return fopen(Utf8ToSystemNative(fullfn).c_str(), mode); + } + + return NULL; +} + + + +std::ifstream* OpenGameFileR(const std::string& path) { + // TODO: unicode support! (?) + + if(path.size() == 0) + return NULL; + + std::string fullfn = GetFullFileName(path); + if(fullfn.size() != 0) { + try { + std::ifstream* f = new std::ifstream(fullfn.c_str(), std::ios::in | std::ios::binary); + if (f->is_open()) + return f; + else { + delete f; + return NULL; + } + } catch(...) {} + return NULL; + } + + return NULL; +} + + + +void AddToFileList(searchpathlist* l, const std::string& f) { + if(!FileListIncludesExact(l, f)) l->push_back(f); +} + +void removeEndingSlashes(std::string& s) +{ + while(s.size() > 0 && (*s.rbegin() == '\\' || *s.rbegin() == '/')) + s.erase(s.size() - 1); +} + +///////////////// +// Returns true, if the list contains the path +bool FileListIncludesExact(const searchpathlist* l, const std::string& f) { + std::string tmp1 = f; + removeEndingSlashes(tmp1); + ReplaceFileVariables(tmp1); + replace(tmp1,"\\","/"); + + // Go through the list, checking each item + for(searchpathlist::const_iterator i = l->begin(); i != l->end(); i++) { + std::string tmp2 = *i; + removeEndingSlashes(tmp2); + ReplaceFileVariables(tmp2); + replace(tmp2,"\\","/"); + if(stringcaseequal(tmp1, tmp2)) + return true; + } + + return false; +} + +std::string GetHomeDir() { +#ifndef WIN32 + char* home = getenv("HOME"); + if(home == NULL || home[0] == '\0') { + passwd* userinfo = getpwuid(getuid()); + if(userinfo) + return userinfo->pw_dir; + return ""; // both failed, very strange system... + } + return home; +#else + static std::string result = ""; + if (result.size() == 0) { // Only do this once + char tmp[1024]; + if (!SHGetSpecialFolderPath(NULL, tmp, CSIDL_PERSONAL,FALSE)) { + // TODO: get dynamicaly another possible path + // the following is only a workaround! + return "C:\\OpenLieroX"; + } + fix_markend(tmp); + + result = SystemNativeToUtf8(tmp); + } + return result; +#endif +} + + +std::string GetSystemDataDir() { +#ifndef WIN32 + return SYSTEM_DATA_DIR; +#else + // windows don't have such dir, don't it? + // or should we return windows/system32 (which is not exactly intended here)? + return ""; +#endif +} + + +std::string binary_dir; // given by argv[0], set by main() + +std::string GetBinaryDir() { + return binary_dir; +} + +std::string GetTempDir() { +#ifndef WIN32 + return "/tmp"; // year, it's so simple :) +#else + static char buf[1024] = ""; + if(buf[0] == '\0') { // only do this once + GetTempPath(sizeof(buf), buf); + fix_markend(buf); + } + return SystemNativeToUtf8(buf); +#endif +} + +void ReplaceFileVariables(std::string& filename) { + if(filename.compare(0,2,"~/")==0 + || filename.compare(0,2,"~\\")==0 + || filename == "~") { + filename.erase(0,1); + filename.insert(0,GetHomeDir()); + } + replace(filename, "${HOME}", GetHomeDir()); + replace(filename, "${SYSTEM_DATA}", GetSystemDataDir()); + replace(filename, "${BIN}", GetBinaryDir()); +} + +// WARNING: not multithreading aware +// HINT: uses absolute paths +// returns true, if successfull +bool FileCopy(const std::string& src, const std::string& dest) { + static char tmp[2048]; + + notes << "FileCopy: " << src << " -> " << dest << endl; + + FILE* src_f = fopen(Utf8ToSystemNative(src).c_str(), "rb"); + + if(!src_f) { + errors << "FileCopy: cannot open source" << endl; + return false; + } + + FILE* dest_f = fopen(Utf8ToSystemNative(dest).c_str(), "wb"); + + if(!dest_f) { + fclose(src_f); + errors << "FileCopy: cannot open destination" << endl; + return false; + } + + bool success = true; + unsigned short count = 0; + notes << "FileCopy: |" << flush; + size_t len = 0; + while((len = fread(tmp, 1, sizeof(tmp), src_f)) > 0) { + if(count == 0) notes << "." << flush; count++; count %= 20; + if(len != fwrite(tmp, 1, len, dest_f)) { + errors << "FileCopy: problem while writing" << endl; + success = false; + break; + } + if(len != sizeof(tmp)) break; + } + notes << endl; + if(success) { + success = feof(src_f) != 0; + if(!success) errors << "FileCopy: problem while reading" << endl; + } + + fclose(src_f); + fclose(dest_f); + if(success) notes << "FileCopy: success :)" << endl; + return success; +} + +bool CanWriteToDir(const std::string& dir) { + // TODO: we have to make this a lot better! + std::string fname = dir + "/.some_stupid_temp_file"; + + FILE* fp = fopen(Utf8ToSystemNative(fname).c_str(), "w"); + + if(fp) { + fclose(fp); + remove(Utf8ToSystemNative(fname).c_str()); + + return true; + } + return false; +} + + +std::string GetAbsolutePath(const std::string& path) { +#ifdef WIN32 + std::string exactpath; + if (!GetExactFileName(path, exactpath)) + exactpath = path; + + char buf[2048]; + int len = GetFullPathName(Utf8ToSystemNative(exactpath).c_str(), sizeof(buf), buf, NULL); + fix_markend(buf); + if (len) + return SystemNativeToUtf8(buf); + else // Failed + return path; +#else + std::string exactpath; + if(GetExactFileName(path, exactpath)) { + char buf[PATH_MAX]; + if(realpath(exactpath.c_str(), buf) != NULL) { + fix_markend(buf); + return buf; + } else + return exactpath; + } else + return path; +#endif +} + +bool PathListIncludes(const std::list& pathlist, const std::string& path) { + std::string abs_path; + abs_path = GetAbsolutePath(path); + + // Go through the list, checking each item + for(std::list::const_iterator i = pathlist.begin(); i != pathlist.end(); i++) { + if(EqualPaths(abs_path, GetAbsolutePath(*i))) { + return true; + } + } + + return false; +} + +/////////////////////// +// Returns the file contents as a string +std::string GetFileContents(const std::string& path, bool absolute) +{ + FILE *fp = NULL; + if (absolute) + fp = fopen(/*Utf8ToSystemNative(path)*/path.c_str(), "rb"); + else + fp = OpenGameFile(path, "rb"); + + if (!fp) + return ""; + + fseek(fp, 0, SEEK_END); + size_t size = ftell(fp); + fseek(fp, 0, SEEK_SET); + + if (!size) { + fclose(fp); + return ""; + } + + char *buf = new char[size]; + size = fread(buf, 1, size, fp); + if (!size) { + delete[] buf; + fclose(fp); + return ""; + } + + std::string result; + result.append(buf, size); + delete[] buf; + fclose(fp); + + return result; +} + +//////////////// +// Extract the directory part from a path +std::string ExtractDirectory(const std::string& path) +{ + if (path.size() == 0) + return ""; + + size_t pos = findLastPathSep(path); + if (pos == std::string::npos) + return path; + else + return path.substr(0, pos); +} + + +std::string GetScriptInterpreterCommandForFile(const std::string& filename) { + FILE* f = OpenGameFile(filename, "r"); + if(f) { + std::string line = ReadUntil(f); + if(line.size() > 2 && line[0] == '#' && line[1] == '!') { + std::string cmd = line.substr(2); + TrimSpaces(cmd); + fclose(f); + return cmd; + } + fclose(f); + return ""; + } + return ""; +} + + +/////////////////// +// Merges two parts of a path into one, for example JoinPath("./test/", "/file.fil") gives "./test/file.fil" +std::string JoinPaths(const std::string& path1, const std::string& path2) +{ + if (path1.size() == 0) + return path2; + if (path2.size() == 0) + return path1; + + std::string result = path1; + if (*path1.rbegin() == '/' || *path1.rbegin() == '\\') { + if (*path2.begin() == '/' || *path2.begin() == '\\') { + result.erase(result.size() - 1); + result += path2; + return result; + } else { + result += path2; + return result; + } + } else { + if (*path2.begin() == '/' || *path2.begin() == '\\') { + result += path2; + return result; + } else { + result += '/'; + result += path2; + return result; + } + } +} + +////////////////////////////// +// Creating SDL_RWops structure from a file pointer +// We cannot use SDL's function for this under WIN32 because it doesn't allow +// passing file pointers to a dll + +#include + +// These are taken from SDL_rwops.c +#ifdef WIN32 +static int stdio_seek(SDL_RWops *context, int offset, int whence) +{ + if ( fseek(context->hidden.stdio.fp, offset, whence) == 0 ) { + return(ftell(context->hidden.stdio.fp)); + } else { + SDL_Error(SDL_EFSEEK); + return(-1); + } +} +static int stdio_read(SDL_RWops *context, void *ptr, int size, int maxnum) +{ + size_t nread; + + nread = fread(ptr, size, maxnum, context->hidden.stdio.fp); + if ( nread == 0 && ferror(context->hidden.stdio.fp) ) { + SDL_Error(SDL_EFREAD); + } + return (int)(nread); +} +static int stdio_write(SDL_RWops *context, const void *ptr, int size, int num) +{ + size_t nwrote; + + nwrote = fwrite(ptr, size, num, context->hidden.stdio.fp); + if ( nwrote == 0 && ferror(context->hidden.stdio.fp) ) { + SDL_Error(SDL_EFWRITE); + } + return (int)(nwrote); +} +static int stdio_close(SDL_RWops *context) +{ + if ( context ) { + if ( context->hidden.stdio.autoclose ) { + /* WARNING: Check the return value here! */ + fclose(context->hidden.stdio.fp); + } + free(context); + } + return(0); +} +#endif + +//////////////// +// Creates SDL_RWops from a file pointer +SDL_RWops *RWopsFromFP(FILE *fp, bool autoclose) { +#ifdef WIN32 + // Taken from SDL code + SDL_RWops *rwops = SDL_AllocRW(); + if ( rwops != NULL ) { + rwops->seek = stdio_seek; + rwops->read = stdio_read; + rwops->write = stdio_write; + rwops->close = stdio_close; + rwops->hidden.stdio.fp = fp; + rwops->hidden.stdio.autoclose = (int)autoclose; + } + return(rwops); + +#else + return SDL_RWFromFP(fp, (int)autoclose); +#endif +} +