// Aseprite // Copyright (C) 2019-2021 Igara Studio S.A. // Copyright (C) 2001-2016 David Capello // // This program is distributed under the terms of // the End-User License Agreement for Aseprite. #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "app/resource_finder.h" #include "app/app.h" #include "base/fs.h" #include "base/string.h" #include "ver/info.h" #include #include #ifdef _WIN32 #include #include #endif namespace app { ResourceFinder::ResourceFinder(bool log) : m_log(log) , m_current(-1) { } const std::string& ResourceFinder::filename() const { // Throw an exception if we are out of bounds return m_paths.at(m_current); } const std::string& ResourceFinder::defaultFilename() const { if (m_default.empty()) { // The first path is the default one if nobody specified it. if (!m_paths.empty()) return m_paths[0]; } return m_default; } bool ResourceFinder::next() { ++m_current; return (m_current < (int)m_paths.size()); } bool ResourceFinder::findFirst() { while (next()) { if (m_log) LOG("FIND: \"%s\"", filename().c_str()); if (base::is_file(filename())) { if (m_log) LOG(" (found)\n"); return true; } else if (m_log) LOG(" (not found)\n"); } return false; } void ResourceFinder::addPath(const std::string& path) { m_paths.push_back(path); } void ResourceFinder::includeBinDir(const char* filename) { addPath(base::join_path(base::get_file_path(base::get_app_path()), filename)); } void ResourceFinder::includeDataDir(const char* filename) { char buf[4096]; #ifdef _WIN32 sprintf(buf, "data/%s", filename); includeHomeDir(buf); // %AppData%/Aseprite/data/filename includeBinDir(buf); // $BINDIR/data/filename #elif __APPLE__ sprintf(buf, "data/%s", filename); includeUserDir(buf); // $HOME/Library/Application Support/Aseprite/data/filename includeBinDir(buf); // $BINDIR/data/filename (outside the bundle) sprintf(buf, "../Resources/data/%s", filename); includeBinDir(buf); // $BINDIR/../Resources/data/filename (inside a bundle) #else // $HOME/.config/aseprite/filename sprintf(buf, "aseprite/data/%s", filename); includeHomeConfigDir(buf); // $BINDIR/data/filename sprintf(buf, "data/%s", filename); includeBinDir(buf); // $BINDIR/../share/aseprite/data/filename (installed in /usr/ or /usr/local/) sprintf(buf, "../share/aseprite/data/%s", filename); includeBinDir(buf); #endif } void ResourceFinder::includeHomeDir(const char* filename) { #ifdef _WIN32 // %AppData%/Aseprite/filename wchar_t* env = _wgetenv(L"AppData"); if (env) { std::string path = base::join_path(base::to_utf8(env), "Aseprite"); path = base::join_path(path, filename); addPath(path); m_default = path; } #else char* env = std::getenv("HOME"); char buf[4096]; if ((env) && (*env)) { // $HOME/filename sprintf(buf, "%s/%s", env, filename); addPath(buf); } else { LOG("FIND: You don't have set $HOME variable\n"); addPath(filename); } #endif } #if !defined(_WIN32) && !defined(__APPLE__) // For Linux: It's $XDG_CONFIG_HOME or $HOME/.config void ResourceFinder::includeHomeConfigDir(const char* filename) { char* configHome = std::getenv("XDG_CONFIG_HOME"); if (configHome && *configHome) { // $XDG_CONFIG_HOME/filename addPath(base::join_path(configHome, filename)); } else { // $HOME/.config/filename includeHomeDir(base::join_path(std::string(".config"), filename).c_str()); } } #endif // !defined(_WIN32) && !defined(__APPLE__) void ResourceFinder::includeUserDir(const char* filename) { #ifdef _WIN32 // $ASEPRITE_USER_FOLDER/filename if (const wchar_t* env = _wgetenv(L"ASEPRITE_USER_FOLDER")) { addPath(base::join_path(base::to_utf8(env), filename)); } else if (App::instance()->isPortable()) { // $BINDIR/filename includeBinDir(filename); } else { // %AppData%/Aseprite/filename includeHomeDir(filename); } #else // Unix-like // $ASEPRITE_USER_FOLDER/filename if (const char* env = std::getenv("ASEPRITE_USER_FOLDER")) { addPath(base::join_path(env, filename)); } else { #ifdef __APPLE__ // $HOME/Library/Application Support/Aseprite/filename addPath( base::join_path( base::join_path(base::get_lib_app_support_path(), get_app_name()), filename).c_str()); #else // !__APPLE__ // $HOME/.config/aseprite/filename includeHomeConfigDir((std::string("aseprite/") + filename).c_str()); #endif } #endif // end Unix-like } void ResourceFinder::includeDesktopDir(const char* filename) { #ifdef _WIN32 std::vector buf(MAX_PATH); HRESULT hr = SHGetFolderPath(NULL, CSIDL_DESKTOPDIRECTORY, NULL, SHGFP_TYPE_DEFAULT, &buf[0]); if (hr == S_OK) { addPath(base::join_path(base::to_utf8(&buf[0]), filename)); } else { includeHomeDir(filename); } #elif defined(__APPLE__) // TODO get the desktop folder // $HOME/Desktop/filename includeHomeDir(base::join_path(std::string("Desktop"), filename).c_str()); #else char* desktopDir = std::getenv("XDG_DESKTOP_DIR"); if (desktopDir) { // $XDG_DESKTOP_DIR/filename addPath(base::join_path(desktopDir, filename)); } else { // $HOME/Desktop/filename includeHomeDir(base::join_path(std::string("Desktop"), filename).c_str()); } #endif } std::string ResourceFinder::getFirstOrCreateDefault() { std::string fn; // Search from first to last path if (findFirst()) fn = filename(); // If the file wasn't found, we will create the directories for the // default file name. if (fn.empty()) { fn = defaultFilename(); std::string dir = base::get_file_path(fn); if (!base::is_directory(dir)) base::make_all_directories(dir); } return fn; } } // namespace app