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