diff --git a/core/cfg/cfg.cpp b/core/cfg/cfg.cpp index 9e8213cad..da22eb764 100644 --- a/core/cfg/cfg.cpp +++ b/core/cfg/cfg.cpp @@ -78,7 +78,7 @@ void cfgSaveStr(const wchar * Section, const wchar * Key, const wchar * String) bool cfgOpen() { - cfgPath=GetPath("/emu.cfg"); + cfgPath=get_config_path("/emu.cfg"); FILE* cfgfile = fopen(cfgPath.c_str(),"r"); if(cfgfile != NULL) { diff --git a/core/hw/maple/maple_devs.cpp b/core/hw/maple/maple_devs.cpp index e1874eefa..46115215e 100755 --- a/core/hw/maple/maple_devs.cpp +++ b/core/hw/maple/maple_devs.cpp @@ -277,7 +277,7 @@ struct maple_sega_vmu: maple_base memset(lcd_data,0,sizeof(lcd_data)); wchar tempy[512]; sprintf(tempy,"/vmu_save_%s.bin",logical_port); - string apath=GetPath(tempy); + string apath=get_data_path(tempy); file=fopen(apath.c_str(),"rb+"); if (!file) diff --git a/core/hw/mem/_vmem.cpp b/core/hw/mem/_vmem.cpp index 6d8f82855..8d33cbf48 100644 --- a/core/hw/mem/_vmem.cpp +++ b/core/hw/mem/_vmem.cpp @@ -580,7 +580,7 @@ error: { #if HOST_OS == OS_DARWIN - string path = GetPath("/dcnzorz_mem"); + string path = get_data_path("/dcnzorz_mem"); fd = open(path.c_str(),O_CREAT|O_RDWR|O_TRUNC,S_IRWXU|S_IRWXG|S_IRWXO); unlink(path.c_str()); verify(ftruncate(fd,RAM_SIZE + VRAM_SIZE +ARAM_SIZE)==0); diff --git a/core/hw/sh4/dyna/blockmanager.cpp b/core/hw/sh4/dyna/blockmanager.cpp index 9a5224118..efff520ff 100644 --- a/core/hw/sh4/dyna/blockmanager.cpp +++ b/core/hw/sh4/dyna/blockmanager.cpp @@ -607,7 +607,7 @@ void print_blocks() if (print_stats) { - f=fopen(GetPath("/blkmap.lst").c_str(),"w"); + f=fopen(get_data_path("/blkmap.lst").c_str(),"w"); print_stats=0; printf("Writing blocks to %p\n",f); diff --git a/core/hw/sh4/dyna/driver.cpp b/core/hw/sh4/dyna/driver.cpp index f708cd9a0..701c98c11 100644 --- a/core/hw/sh4/dyna/driver.cpp +++ b/core/hw/sh4/dyna/driver.cpp @@ -54,7 +54,7 @@ void emit_WriteCodeCache() { wchar path[512]; sprintf(path,"/code_cache_%8p.bin",CodeCache); - string pt2=GetPath(path); + string pt2=get_data_path(path); printf("Writing code cache to %s\n",pt2.c_str()); FILE*f=fopen(pt2.c_str(),"wb"); if (f) diff --git a/core/hw/sh4/sh4_mem.cpp b/core/hw/sh4/sh4_mem.cpp index 871b4e05f..fcd4aa058 100644 --- a/core/hw/sh4/sh4_mem.cpp +++ b/core/hw/sh4/sh4_mem.cpp @@ -223,7 +223,7 @@ void mem_Term() sh4_area0_Term(); //write back Flash/SRAM - SaveRomFiles(GetPath("/data/")); + SaveRomFiles(get_data_path("/data/")); //mem_b.Term(); // handled by vmem diff --git a/core/linux-dist/main.cpp b/core/linux-dist/main.cpp index 663ef5d27..0521d329b 100755 --- a/core/linux-dist/main.cpp +++ b/core/linux-dist/main.cpp @@ -239,7 +239,7 @@ void dc_run(); } #endif -string get_config_dir() +string find_user_config_dir() { #ifdef USES_HOMEDIR struct stat info; @@ -259,14 +259,13 @@ string get_config_dir() * http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html#variables */ home = (string)getenv("HOME") + "/.config/reicast"; - } if(getenv("XDG_CONFIG_HOME") != NULL) { // If XDG_CONFIG_HOME is set explicitly, we'll use that instead of $HOME/.config home = (string)getenv("XDG_CONFIG_HOME") + "/reicast"; } - + if(!home.empty()) { if((stat(home.c_str(), &info) != 0) || !(info.st_mode & S_IFDIR)) @@ -277,11 +276,105 @@ string get_config_dir() return home; } #endif - + // Unable to detect config dir, use the current folder return "."; } +string find_user_data_dir() +{ + #ifdef USES_HOMEDIR + struct stat info; + string data = ""; + if(getenv("HOME") != NULL) + { + // Support for the legacy config dir at "$HOME/.reicast" + string legacy_data = (string)getenv("HOME") + "/.reicast"; + if((stat(legacy_data.c_str(), &info) == 0) && (info.st_mode & S_IFDIR)) + { + // "$HOME/.reicast" already exists, let's use it! + return legacy_data; + } + + /* If $XDG_DATA_HOME is not set, we're supposed to use "$HOME/.local/share" instead. + * Consult the XDG Base Directory Specification for details: + * http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html#variables + */ + data = (string)getenv("HOME") + "/.local/share/reicast"; + } + if(getenv("XDG_DATA_HOME") != NULL) + { + // If XDG_DATA_HOME is set explicitly, we'll use that instead of $HOME/.config + data = (string)getenv("XDG_DATA_HOME") + "/reicast"; + } + + if(!data.empty()) + { + if((stat(data.c_str(), &info) != 0) || !(info.st_mode & S_IFDIR)) + { + // If the directory doesn't exist yet, create it! + mkdir(data.c_str(), 0755); + } + return data; + } + #endif + + // Unable to detect config dir, use the current folder + return "."; +} + +std::vector find_system_config_dirs() +{ + std::vector dirs; + if (getenv("XDG_DATA_DIRS") != NULL) + { + string s = (string)getenv("XDG_CONFIG_DIRS"); + + string::size_type pos = 0; + string::size_type n = s.find(":", pos); + while(n != std::string::npos) + { + dirs.push_back(s.substr(pos, n-pos) + "/reicast"); + pos = n + 1; + n = s.find(":", pos); + } + // Separator not found + dirs.push_back(s.substr(pos) + "/reicast"); + } + else + { + dirs.push_back("/etc/xdg/reicast"); + } + return dirs; +} + +std::vector find_system_data_dirs() +{ + std::vector dirs; + if (getenv("XDG_DATA_DIRS") != NULL) + { + string s = (string)getenv("XDG_DATA_DIRS"); + + string::size_type pos = 0; + string::size_type n = s.find(":", pos); + while(n != std::string::npos) + { + dirs.push_back(s.substr(pos, n-pos) + "/reicast"); + pos = n + 1; + n = s.find(":", pos); + } + // Separator not found + dirs.push_back(s.substr(pos) + "/reicast"); + } + else + { + dirs.push_back("/usr/local/share/reicast"); + dirs.push_back("/usr/share/reicast"); + } + return dirs; +} + + int main(int argc, wchar* argv[]) { #ifdef TARGET_PANDORA @@ -289,9 +382,22 @@ int main(int argc, wchar* argv[]) signal(SIGKILL, clean_exit); #endif - /* Set home dir */ - SetHomeDir(get_config_dir()); - printf("Home dir is: %s\n", GetPath("/").c_str()); + /* Set directories */ + set_user_config_dir(find_user_config_dir()); + set_user_data_dir(find_user_data_dir()); + std::vector dirs; + dirs = find_system_config_dirs(); + for(unsigned int i = 0; i < dirs.size(); i++) + { + add_system_data_dir(dirs[i]); + } + dirs = find_system_data_dirs(); + for(unsigned int i = 0; i < dirs.size(); i++) + { + add_system_data_dir(dirs[i]); + } + printf("Config dir is: %s\n", get_config_path("/").c_str()); + printf("Data dir is: %s\n", get_data_path("/").c_str()); common_linux_setup(); diff --git a/core/linux/nixprof/nixprof.cpp b/core/linux/nixprof/nixprof.cpp index 3bd110094..8a8015956 100644 --- a/core/linux/nixprof/nixprof.cpp +++ b/core/linux/nixprof/nixprof.cpp @@ -207,7 +207,7 @@ void* prof(void *ptr) sprintf(line, "/%d.reprof", tick_count); - string logfile=GetPath(line); + string logfile=get_data_path(line); printf("Profiler thread logging to -> %s\n", logfile.c_str()); diff --git a/core/nacl/nacl.cpp b/core/nacl/nacl.cpp index 8c552bb4a..b7fa39d1e 100644 --- a/core/nacl/nacl.cpp +++ b/core/nacl/nacl.cpp @@ -48,7 +48,8 @@ void* emuthread(void* ) { char *Args[3]; Args[0] = "dc"; - SetHomeDir("/http"); + set_user_config_dir("/http"); + set_user_data_dir("/http"); dc_init(1,Args); dc_run(); diff --git a/core/nullDC.cpp b/core/nullDC.cpp index 47a804a04..82439fc2e 100755 --- a/core/nullDC.cpp +++ b/core/nullDC.cpp @@ -165,9 +165,9 @@ int dc_init(int argc,wchar* argv[]) #define DATA_PATH "/" #endif - if (settings.bios.UseReios || !LoadRomFiles(GetPath(DATA_PATH))) + if (settings.bios.UseReios || !LoadRomFiles(get_data_path(DATA_PATH, false))) { - if (!LoadHle(GetPath(DATA_PATH))) + if (!LoadHle(get_data_path(DATA_PATH, false))) return -3; else printf("Did not load bios, using reios\n"); @@ -224,7 +224,7 @@ void dc_term() #ifndef _ANDROID SaveSettings(); #endif - SaveRomFiles(GetPath("/data/")); + SaveRomFiles(get_data_path("/data/")); } void LoadSettings() diff --git a/core/rend/gles/gles.cpp b/core/rend/gles/gles.cpp index 601235f45..84b045b9c 100755 --- a/core/rend/gles/gles.cpp +++ b/core/rend/gles/gles.cpp @@ -930,9 +930,9 @@ bool gl_create_resources() #endif int w, h; - osd_tex=loadPNG(GetPath("/data/buttons.png"),w,h); + osd_tex=loadPNG(get_data_path("/data/buttons.png", false),w,h); #ifdef TARGET_PANDORA - osd_font=loadPNG(GetPath("/font2.png"),w,h); + osd_font=loadPNG(get_data_path("/font2.png", false),w,h); #endif return true; diff --git a/core/sdl/main.cpp b/core/sdl/main.cpp index 640fc416b..7337b0c18 100755 --- a/core/sdl/main.cpp +++ b/core/sdl/main.cpp @@ -435,15 +435,19 @@ int main(int argc, wchar* argv[]) { home += "/.reicast"; mkdir(home.c_str(), 0755); // create the directory if missing - SetHomeDir(home); + set_user_config_dir(home); + set_user_data_dir(home); } else - SetHomeDir("."); + set_user_config_dir("."); + set_user_data_dir("."); #else - SetHomeDir("."); + set_user_config_dir("."); + set_user_data_dir("."); #endif - printf("Home dir is: %s\n",GetPath("/").c_str()); + printf("Config dir is: %s\n", get_config_path("/").c_str()); + printf("Data dir is: %s\n", get_data_path("/").c_str()); common_linux_setup(); diff --git a/core/stdclass.cpp b/core/stdclass.cpp index 0defdec6c..75b9dcb40 100644 --- a/core/stdclass.cpp +++ b/core/stdclass.cpp @@ -1,22 +1,88 @@ #include +#include +#include #include "types.h" #include "hw/mem/_vmem.h" #include "types.h" -string home_dir; +string user_config_dir; +string user_data_dir; +std::vector system_config_dirs; +std::vector system_data_dirs; -void SetHomeDir(const string& home) +bool file_exists(const string& filename) { - home_dir=home; + struct stat info; + return (stat (filename.c_str(), &info) == 0); } -//subpath format: /data/fsca-table.bit -string GetPath(const string& subpath) +void set_user_config_dir(const string& dir) { - return (home_dir+subpath); + user_config_dir = dir; +} + +void set_user_data_dir(const string& dir) +{ + user_data_dir = dir; +} + +void add_system_config_dir(const string& dir) +{ + system_config_dirs.push_back(dir); +} + +void add_system_data_dir(const string& dir) +{ + system_data_dirs.push_back(dir); +} + +string get_config_path(const string& filename, bool user_writable) +{ + if(user_writable) + { + /* Only stuff in the user_config_dir is supposed to be writable, + * so we always return that. + */ + return (user_config_dir + filename); + } + + string filepath; + for (unsigned int i = 0; i < system_config_dirs.size(); i++) { + filepath = system_config_dirs[i] + filename; + if (file_exists(filepath)) + { + return filepath; + } + } + + // Not found, so we return the user variant + return (user_config_dir + filename); +} + +string get_data_path(const string& filename, bool user_writable) +{ + if(user_writable) + { + /* Only stuff in the user_data_dir is supposed to be writable, + * so we always return that. + */ + return (user_data_dir + filename); + } + + string filepath; + for (unsigned int i = 0; i < system_data_dirs.size(); i++) { + filepath = system_data_dirs[i] + filename; + if (file_exists(filepath)) + { + return filepath; + } + } + + // Not found, so we return the user variant + return (user_data_dir + filename); } diff --git a/core/stdclass.h b/core/stdclass.h index 139e50acc..782748406 100644 --- a/core/stdclass.h +++ b/core/stdclass.h @@ -251,9 +251,14 @@ public : }; //Set the path ! -void SetHomeDir(const string& home); +void set_user_config_dir(const string& dir); +void set_user_data_dir(const string& dir); +void add_system_config_dir(const string& dir); +void add_system_data_dir(const string& dir); + //subpath format: /data/fsca-table.bit -string GetPath(const string& subpath); +string get_config_path(const string& filename, bool user_writable = true); +string get_data_path(const string& filename, bool user_writable = true); class VArray2 diff --git a/core/webui/server.cpp b/core/webui/server.cpp index cfb53c518..80cc13ea2 100644 --- a/core/webui/server.cpp +++ b/core/webui/server.cpp @@ -79,7 +79,7 @@ enum demo_protocols { DEMO_PROTOCOL_COUNT }; -#define WEBUI_PATH GetPath("/webui") +#define WEBUI_PATH get_data_path("/webui") /* * We take a strict whitelist approach to stop ../ attacks diff --git a/core/windows/winmain.cpp b/core/windows/winmain.cpp index 58140290a..7fac9f59d 100644 --- a/core/windows/winmain.cpp +++ b/core/windows/winmain.cpp @@ -157,7 +157,8 @@ void SetupPath() GetModuleFileName(0,fname,512); string fn=string(fname); fn=fn.substr(0,fn.find_last_of('\\')); - SetHomeDir(fn); + set_user_config_dir(fn); + set_user_data_dir(fn); } int msgboxf(const wchar* text,unsigned int type,...) diff --git a/shell/android/jni/src/Android.cpp b/shell/android/jni/src/Android.cpp index 72a5c86af..cd2086bcc 100644 --- a/shell/android/jni/src/Android.cpp +++ b/shell/android/jni/src/Android.cpp @@ -253,8 +253,10 @@ JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_config(JNIEnv *env,jo { // Set home directory based on User config const char* D = dirName? env->GetStringUTFChars(dirName,0):0; - SetHomeDir(D); - printf("Home dir is: '%s'\n",GetPath("/").c_str()); + set_user_config_dir(D); + set_user_data_dir(D); + printf("Config dir is: %s\n", get_config_path("/").c_str()); + printf("Data dir is: %s\n", get_data_path("/").c_str()); env->ReleaseStringUTFChars(dirName,D); } JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_init(JNIEnv *env,jobject obj,jstring fileName) diff --git a/shell/apple/emulator-ios/emulator/ios_main.mm b/shell/apple/emulator-ios/emulator/ios_main.mm index ddd4338e8..46d867e45 100644 --- a/shell/apple/emulator-ios/emulator/ios_main.mm +++ b/shell/apple/emulator-ios/emulator/ios_main.mm @@ -66,11 +66,13 @@ extern "C" int reicast_main(int argc, wchar* argv[]) //ndcid=atoi(argv[1]); string homedir = [ [[[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] objectAtIndex:0] path] UTF8String]; - SetHomeDir(homedir); + set_user_config_dir(homedir); + set_user_data_dir(homedir); freopen( (homedir + "/log.txt").c_str(), "wb", stdout); - printf("Home dir is: %s\n",GetPath("/").c_str()); + printf("Config dir is: %s\n", get_config_path("/").c_str()); + printf("Data dir is: %s\n", get_data_path("/").c_str()); common_linux_setup(); diff --git a/shell/apple/emulator-osx/emulator-osx/osx-main.mm b/shell/apple/emulator-osx/emulator-osx/osx-main.mm index 4d024dc02..db3586891 100644 --- a/shell/apple/emulator-osx/emulator-osx/osx-main.mm +++ b/shell/apple/emulator-osx/emulator-osx/osx-main.mm @@ -95,10 +95,14 @@ void* emuthread(void*) { { home += "/.reicast"; mkdir(home.c_str(), 0755); // create the directory if missing - SetHomeDir(home); + set_user_config_dir(home); + set_user_data_dir(home); } else - SetHomeDir("."); + { + set_user_config_dir("."); + set_user_data_dir("."); + } char* argv[] = { "reicast" }; dc_init(1,argv);