1 // Aseprite
2 // Copyright (C) 2001-2016  David Capello
3 //
4 // This program is distributed under the terms of
5 // the End-User License Agreement for Aseprite.
6 
7 #ifdef HAVE_CONFIG_H
8 #include "config.h"
9 #endif
10 
11 #include "app/resource_finder.h"
12 
13 #include "app/app.h"
14 #include "base/fs.h"
15 #include "base/string.h"
16 
17 #include <cstdio>
18 #include <cstdlib>
19 
20 #ifdef _WIN32
21   #include <windows.h>
22   #include <shlobj.h>
23 #endif
24 
25 namespace app {
26 
ResourceFinder(bool log)27 ResourceFinder::ResourceFinder(bool log)
28   : m_log(log)
29 {
30   m_current = -1;
31 }
32 
filename() const33 const std::string& ResourceFinder::filename() const
34 {
35   // Throw an exception if we are out of bounds
36   return m_paths.at(m_current);
37 }
38 
defaultFilename() const39 const std::string& ResourceFinder::defaultFilename() const
40 {
41   if (m_default.empty()) {
42     // The first path is the default one if nobody specified it.
43     if (!m_paths.empty())
44       return m_paths[0];
45   }
46   return m_default;
47 }
48 
next()49 bool ResourceFinder::next()
50 {
51   ++m_current;
52   return (m_current < (int)m_paths.size());
53 }
54 
findFirst()55 bool ResourceFinder::findFirst()
56 {
57   while (next()) {
58     if (m_log)
59       LOG("FIND: \"%s\"", filename().c_str());
60 
61     if (base::is_file(filename())) {
62       if (m_log)
63         LOG(" (found)\n");
64 
65       return true;
66     }
67     else if (m_log)
68       LOG(" (not found)\n");
69   }
70 
71   return false;
72 }
73 
addPath(const std::string & path)74 void ResourceFinder::addPath(const std::string& path)
75 {
76   m_paths.push_back(path);
77 }
78 
includeBinDir(const char * filename)79 void ResourceFinder::includeBinDir(const char* filename)
80 {
81   addPath(base::join_path(base::get_file_path(base::get_app_path()), filename));
82 }
83 
includeDataDir(const char * filename)84 void ResourceFinder::includeDataDir(const char* filename)
85 {
86   char buf[4096];
87 
88 #ifdef _WIN32
89 
90   sprintf(buf, "data/%s", filename);
91   includeHomeDir(buf); // %AppData%/Aseprite/data/filename
92   includeBinDir(buf);  // $BINDIR/data/filename
93 
94 #elif __APPLE__
95 
96   sprintf(buf, "data/%s", filename);
97   includeUserDir(buf); // $HOME/Library/Application Support/Aseprite/data/filename
98   includeBinDir(buf);  // $BINDIR/data/filename (outside the bundle)
99 
100   sprintf(buf, "../Resources/data/%s", filename);
101   includeBinDir(buf);  // $BINDIR/../Resources/data/filename (inside a bundle)
102 
103 #else
104 
105   // $HOME/.config/aseprite/filename
106   sprintf(buf, ".config/aseprite/data/%s", filename);
107   includeHomeDir(buf);
108 
109   // $BINDIR/data/filename
110   sprintf(buf, "data/%s", filename);
111   includeBinDir(buf);
112 
113   // $BINDIR/../share/aseprite/data/filename (installed in /usr/ or /usr/local/)
114   sprintf(buf, "../share/aseprite/data/%s", filename);
115   includeBinDir(buf);
116 
117 #endif
118 }
119 
includeHomeDir(const char * filename)120 void ResourceFinder::includeHomeDir(const char* filename)
121 {
122 #ifdef _WIN32
123 
124   // %AppData%/Aseprite/filename
125   wchar_t* env = _wgetenv(L"AppData");
126   if (env) {
127     std::string path = base::join_path(base::to_utf8(env), "Aseprite");
128     path = base::join_path(path, filename);
129     addPath(path);
130     m_default = path;
131   }
132 
133 #else
134 
135   char* env = std::getenv("HOME");
136   char buf[4096];
137 
138   if ((env) && (*env)) {
139     // $HOME/filename
140     sprintf(buf, "%s/%s", env, filename);
141     addPath(buf);
142   }
143   else {
144     LOG("FIND: You don't have set $HOME variable\n");
145     addPath(filename);
146   }
147 
148 #endif
149 }
150 
includeUserDir(const char * filename)151 void ResourceFinder::includeUserDir(const char* filename)
152 {
153 #ifdef _WIN32
154 
155   if (App::instance()->isPortable()) {
156     // $BINDIR/filename
157     includeBinDir(filename);
158   }
159   else {
160     // %AppData%/Aseprite/filename
161     includeHomeDir(filename);
162   }
163 
164 #elif __APPLE__
165 
166   // $HOME/Library/Application Support/Aseprite/filename
167   addPath(
168     base::join_path(
169       base::join_path(base::get_lib_app_support_path(), PACKAGE),
170       filename).c_str());
171 
172 #else
173 
174   // $HOME/.config/aseprite/filename
175   includeHomeDir((std::string(".config/aseprite/") + filename).c_str());
176 
177 #endif
178 }
179 
includeDesktopDir(const char * filename)180 void ResourceFinder::includeDesktopDir(const char* filename)
181 {
182 #ifdef _WIN32
183 
184   std::vector<wchar_t> buf(MAX_PATH);
185   HRESULT hr = SHGetFolderPath(NULL, CSIDL_DESKTOPDIRECTORY, NULL,
186                                SHGFP_TYPE_DEFAULT, &buf[0]);
187   if (hr == S_OK) {
188     addPath(base::join_path(base::to_utf8(&buf[0]), filename));
189   }
190   else {
191     includeHomeDir(filename);
192   }
193 
194 #elif defined(__APPLE__)
195 
196   // TODO get the desktop folder
197   // $HOME/Desktop/filename
198   includeHomeDir(base::join_path(std::string("Desktop"), filename).c_str());
199 
200 #else
201 
202   char* desktopDir = std::getenv("XDG_DESKTOP_DIR");
203   if (desktopDir) {
204     // $XDG_DESKTOP_DIR/filename
205     addPath(base::join_path(desktopDir, filename));
206   }
207   else {
208     // $HOME/Desktop/filename
209     includeHomeDir(base::join_path(std::string("Desktop"), filename).c_str());
210   }
211 
212 #endif
213 }
214 
getFirstOrCreateDefault()215 std::string ResourceFinder::getFirstOrCreateDefault()
216 {
217   std::string fn;
218 
219   // Search from first to last path
220   if (findFirst())
221     fn = filename();
222 
223   // If the file wasn't found, we will create the directories for the
224   // default file name.
225   if (fn.empty()) {
226     fn = defaultFilename();
227 
228     std::string dir = base::get_file_path(fn);
229     if (!base::is_directory(dir))
230       base::make_all_directories(dir);
231   }
232 
233   return fn;
234 }
235 
236 } // namespace app
237