1// Aseprite
2// Copyright (C) 2018-2021 Igara Studio S.A.
3// Copyright (C) 2001-2016 David Capello
4//
5// This program is distributed under the terms of
6// the End-User License Agreement for Aseprite.
7
8#ifdef HAVE_CONFIG_H
9#include "config.h"
10#endif
11
12#include "app/ini_file.h"
13
14#include "app/resource_finder.h"
15#include "base/fs.h"
16#include "base/split_string.h"
17#include "base/string.h"
18#include "cfg/cfg.h"
19
20#ifdef __APPLE__
21#include "os/logger.h"
22#include "os/system.h"
23#endif
24
25#ifndef _WIN32
26 #include "base/fs.h"
27#endif
28
29#include <cstdlib>
30#include <vector>
31
32namespace app {
33
34using namespace gfx;
35
36static std::string g_configFilename;
37static std::vector<cfg::CfgFile*> g_configs;
38
39ConfigModule::ConfigModule()
40{
41 ResourceFinder rf;
42 rf.includeUserDir("aseprite.ini");
43
44 // getFirstOrCreateDefault() will create the Aseprite directory
45 // inside the OS configuration folder (~/.config/aseprite/, etc.).
46 std::string fn = rf.getFirstOrCreateDefault();
47
48#ifdef __APPLE__
49
50 // On OS X we migrate from ~/.config/aseprite/* -> "~/Library/Application Support/Aseprite/*"
51 if (!base::is_file(fn)) {
52 try {
53 std::string new_dir = base::get_file_path(fn);
54
55 // Now we try to move all old configuration files into the new
56 // directory.
57 ResourceFinder old_rf;
58 old_rf.includeHomeDir(".config/aseprite/aseprite.ini");
59 std::string old_config_fn = old_rf.defaultFilename();
60 if (base::is_file(old_config_fn)) {
61 std::string old_dir = base::get_file_path(old_config_fn);
62 for (std::string old_fn : base::list_files(old_dir)) {
63 std::string from = base::join_path(old_dir, old_fn);
64 std::string to = base::join_path(new_dir, old_fn);
65 base::move_file(from, to);
66 }
67 base::remove_directory(old_dir);
68 }
69 }
70 // Something failed
71 catch (const std::exception& ex) {
72 std::string err = "Error in configuration migration: ";
73 err += ex.what();
74
75 auto system = os::instance();
76 if (system && system->logger())
77 system->logger()->logError(err.c_str());
78 }
79 }
80
81#elif !defined(_WIN32)
82
83 // On Linux we migrate the old configuration file name
84 // (~/.asepriterc -> ~/.config/aseprite/aseprite.ini)
85 {
86 ResourceFinder old_rf;
87 old_rf.includeHomeDir(".asepriterc");
88 std::string old_fn = old_rf.defaultFilename();
89 if (base::is_file(old_fn))
90 base::move_file(old_fn, fn);
91 }
92
93#endif
94
95 set_config_file(fn.c_str());
96 g_configFilename = fn;
97}
98
99ConfigModule::~ConfigModule()
100{
101 flush_config_file();
102
103 for (auto cfg : g_configs)
104 delete cfg;
105 g_configs.clear();
106}
107
108//////////////////////////////////////////////////////////////////////
109// Allegro-like API to handle .ini configuration files
110
111void push_config_state()
112{
113 g_configs.push_back(new cfg::CfgFile());
114}
115
116void pop_config_state()
117{
118 ASSERT(!g_configs.empty());
119
120 delete g_configs.back();
121 g_configs.erase(--g_configs.end());
122}
123
124void flush_config_file()
125{
126 ASSERT(!g_configs.empty());
127
128 g_configs.back()->save();
129}
130
131void set_config_file(const char* filename)
132{
133 if (g_configs.empty())
134 g_configs.push_back(new cfg::CfgFile());
135
136 g_configs.back()->load(filename);
137}
138
139std::string main_config_filename()
140{
141 return g_configFilename;
142}
143
144const char* get_config_string(const char* section, const char* name, const char* value)
145{
146 return g_configs.back()->getValue(section, name, value);
147}
148
149void set_config_string(const char* section, const char* name, const char* value)
150{
151 g_configs.back()->setValue(section, name, value);
152}
153
154int get_config_int(const char* section, const char* name, int value)
155{
156 return g_configs.back()->getIntValue(section, name, value);
157}
158
159void set_config_int(const char* section, const char* name, int value)
160{
161 g_configs.back()->setIntValue(section, name, value);
162}
163
164float get_config_float(const char* section, const char* name, float value)
165{
166 return (float)g_configs.back()->getDoubleValue(section, name, (float)value);
167}
168
169void set_config_float(const char* section, const char* name, float value)
170{
171 g_configs.back()->setDoubleValue(section, name, (float)value);
172}
173
174double get_config_double(const char* section, const char* name, double value)
175{
176 return g_configs.back()->getDoubleValue(section, name, value);
177}
178
179void set_config_double(const char* section, const char* name, double value)
180{
181 g_configs.back()->setDoubleValue(section, name, value);
182}
183
184bool get_config_bool(const char* section, const char* name, bool value)
185{
186 return g_configs.back()->getBoolValue(section, name, value);
187}
188
189void set_config_bool(const char* section, const char* name, bool value)
190{
191 g_configs.back()->setBoolValue(section, name, value);
192}
193
194Point get_config_point(const char* section, const char* name, const Point& point)
195{
196 Point point2(point);
197 const char* value = get_config_string(section, name, "");
198 if (value) {
199 std::vector<std::string> parts;
200 base::split_string(value, parts, " ");
201 if (parts.size() == 2) {
202 point2.x = strtol(parts[0].c_str(), NULL, 10);
203 point2.y = strtol(parts[1].c_str(), NULL, 10);
204 }
205 }
206 return point2;
207}
208
209void set_config_point(const char* section, const char* name, const Point& point)
210{
211 char buf[128];
212 sprintf(buf, "%d %d", point.x, point.y);
213 set_config_string(section, name, buf);
214}
215
216Size get_config_size(const char* section, const char* name, const Size& size)
217{
218 Size size2(size);
219 const char* value = get_config_string(section, name, "");
220 if (value) {
221 std::vector<std::string> parts;
222 base::split_string(value, parts, " ");
223 if (parts.size() == 2) {
224 size2.w = strtol(parts[0].c_str(), NULL, 10);
225 size2.h = strtol(parts[1].c_str(), NULL, 10);
226 }
227 }
228 return size2;
229}
230
231void set_config_size(const char* section, const char* name, const Size& size)
232{
233 char buf[128];
234 sprintf(buf, "%d %d", size.w, size.h);
235 set_config_string(section, name, buf);
236}
237
238Rect get_config_rect(const char* section, const char* name, const Rect& rect)
239{
240 Rect rect2(rect);
241 const char* value = get_config_string(section, name, "");
242 if (value) {
243 std::vector<std::string> parts;
244 base::split_string(value, parts, " ");
245 if (parts.size() == 4) {
246 rect2.x = strtol(parts[0].c_str(), NULL, 10);
247 rect2.y = strtol(parts[1].c_str(), NULL, 10);
248 rect2.w = strtol(parts[2].c_str(), NULL, 10);
249 rect2.h = strtol(parts[3].c_str(), NULL, 10);
250 }
251 }
252 return rect2;
253}
254
255void set_config_rect(const char* section, const char* name, const Rect& rect)
256{
257 char buf[128];
258 sprintf(buf, "%d %d %d %d", rect.x, rect.y, rect.w, rect.h);
259 set_config_string(section, name, buf);
260}
261
262app::Color get_config_color(const char* section, const char* name, const app::Color& value)
263{
264 return app::Color::fromString(get_config_string(section, name, value.toString().c_str()));
265}
266
267void set_config_color(const char* section, const char* name, const app::Color& value)
268{
269 set_config_string(section, name, value.toString().c_str());
270}
271
272void del_config_value(const char* section, const char* name)
273{
274 g_configs.back()->deleteValue(section, name);
275}
276
277void del_config_section(const char* section)
278{
279 g_configs.back()->deleteSection(section);
280}
281
282base::paths enum_config_keys(const char* section)
283{
284 base::paths keys;
285 g_configs.back()->getAllKeys(section, keys);
286 return keys;
287}
288
289} // namespace app
290