Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/src/core/core.cpp
7197 views
1
// SPDX-FileCopyrightText: 2019-2026 Connor McLaughlin <[email protected]>
2
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
3
4
#include "core.h"
5
#include "core_private.h"
6
#include "settings.h"
7
#include "system.h"
8
9
#include "scmversion/scmversion.h"
10
11
#include "util/ini_settings_interface.h"
12
#include "util/input_manager.h"
13
14
#include "common/assert.h"
15
#include "common/crash_handler.h"
16
#include "common/error.h"
17
#include "common/file_system.h"
18
#include "common/layered_settings_interface.h"
19
#include "common/log.h"
20
#include "common/path.h"
21
#include "common/string_util.h"
22
23
#include "fmt/format.h"
24
25
#include <cstdarg>
26
#include <cstdlib>
27
#include <limits>
28
29
#ifdef _WIN32
30
#include "common/windows_headers.h"
31
#include <ShlObj.h>
32
#endif
33
34
LOG_CHANNEL(Core);
35
36
namespace Core {
37
38
static bool SetAppRootAndResources(const char* resources_subdir, Error* error);
39
static bool SetDataRoot(Error* error);
40
static void SetDefaultSettings(SettingsInterface& si, bool host, bool system, bool controller);
41
42
namespace {
43
struct CoreLocals
44
{
45
std::mutex settings_mutex;
46
LayeredSettingsInterface layered_settings_interface;
47
48
#ifndef __ANDROID__
49
INISettingsInterface base_settings_interface;
50
#endif
51
};
52
} // namespace
53
54
ALIGN_TO_CACHE_LINE static CoreLocals s_locals;
55
56
} // namespace Core
57
58
bool Core::SetCriticalFolders(const char* resources_subdir, Error* error)
59
{
60
if (!SetAppRootAndResources(resources_subdir, error))
61
return false;
62
63
if (!SetDataRoot(error))
64
return false;
65
66
// logging of directories in case something goes wrong super early
67
DEV_LOG("AppRoot Directory: {}", EmuFolders::AppRoot);
68
DEV_LOG("DataRoot Directory: {}", EmuFolders::DataRoot);
69
DEV_LOG("Resources Directory: {}", EmuFolders::Resources);
70
71
// Write crash dumps to the data directory, since that'll be accessible for certain.
72
CrashHandler::SetWriteDirectory(EmuFolders::DataRoot);
73
74
return true;
75
}
76
77
bool Core::SetAppRootAndResources(const char* resources_subdir, Error* error)
78
{
79
const std::string program_path = FileSystem::GetProgramPath(error);
80
if (program_path.empty())
81
return false;
82
83
INFO_LOG("Program Path: {}", program_path);
84
85
EmuFolders::AppRoot = Path::Canonicalize(Path::GetDirectory(program_path));
86
87
// MacOS resources are inside the app bundle, so canonicalize them.
88
EmuFolders::Resources = Path::Combine(EmuFolders::AppRoot, resources_subdir);
89
#ifdef __APPLE__
90
EmuFolders::Resources = Path::Canonicalize(EmuFolders::Resources);
91
#endif
92
93
if (!FileSystem::DirectoryExists(EmuFolders::Resources.c_str()))
94
{
95
Error::SetStringFmt(error,
96
"Resources directory does not exist at expected path:\n\n{}\n\nYour installation is not "
97
"complete. Please delete and re-download the application from https://www.duckstation.org/.",
98
EmuFolders::Resources);
99
return false;
100
}
101
102
return true;
103
}
104
105
bool Core::SetDataRoot(Error* error)
106
{
107
#ifndef __ANDROID__
108
// This bullshit here because AppImage mounts in /tmp, so we need to check the "real" appimage location.
109
std::string_view real_approot = EmuFolders::AppRoot;
110
111
#ifdef __linux__
112
if (const char* appimage_path = std::getenv("APPIMAGE"))
113
real_approot = Path::GetDirectory(appimage_path);
114
#endif
115
116
// Check whether portable.ini exists in the program directory.
117
if (FileSystem::FileExists(Path::Combine(real_approot, "portable.txt").c_str()) ||
118
FileSystem::FileExists(Path::Combine(real_approot, "settings.ini").c_str()))
119
{
120
// no need to check that it exists, if it's where the executable is it definitely will
121
EmuFolders::DataRoot = std::string(real_approot);
122
return true;
123
}
124
#endif // __ANDROID__
125
126
#if defined(_WIN32)
127
128
// On Windows, we want to use %APPDATA%\DuckStation for data, and %LOCALAPPDATA%\DuckStation for cache.
129
// Old installs use Documents\DuckStation for everything. Check this first.
130
PWSTR documents_directory;
131
if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_Documents, 0, NULL, &documents_directory)))
132
{
133
if (std::wcslen(documents_directory) > 0)
134
{
135
std::string path = Path::Combine(StringUtil::WideStringToUTF8String(documents_directory), "DuckStation");
136
if (FileSystem::DirectoryExists(path.c_str()))
137
{
138
WARNING_LOG("Using Documents directory for data root: {}", path);
139
EmuFolders::DataRoot = std::move(path);
140
}
141
}
142
143
CoTaskMemFree(documents_directory);
144
}
145
146
if (EmuFolders::DataRoot.empty())
147
{
148
PWSTR appdata_directory;
149
HRESULT hr;
150
if (SUCCEEDED((hr = SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr, &appdata_directory))))
151
{
152
if (std::wcslen(appdata_directory) > 0)
153
EmuFolders::DataRoot = Path::Combine(StringUtil::WideStringToUTF8String(appdata_directory), "DuckStation");
154
155
CoTaskMemFree(appdata_directory);
156
}
157
else
158
{
159
Error::SetHResult(error, "SHGetKnownFolderPath(FOLDERID_LocalAppData) failed: ", hr);
160
}
161
}
162
163
#elif (defined(__linux__) || defined(__FreeBSD__)) && !defined(__ANDROID__)
164
165
// Use $XDG_CONFIG_HOME/duckstation if it exists.
166
const char* xdg_config_home = getenv("XDG_CONFIG_HOME");
167
if (xdg_config_home && Path::IsAbsolute(xdg_config_home))
168
{
169
EmuFolders::DataRoot = Path::RealPath(Path::Combine(xdg_config_home, "duckstation"));
170
}
171
else
172
{
173
// Use ~/.local/share/duckstation otherwise.
174
const char* home_dir = getenv("HOME");
175
if (home_dir)
176
{
177
// ~/.local/share should exist, but just in case it doesn't and this is a fresh profile..
178
const std::string local_dir = Path::Combine(home_dir, ".local");
179
const std::string share_dir = Path::Combine(local_dir, "share");
180
FileSystem::EnsureDirectoryExists(local_dir.c_str(), false);
181
FileSystem::EnsureDirectoryExists(share_dir.c_str(), false);
182
EmuFolders::DataRoot = Path::RealPath(Path::Combine(share_dir, "duckstation"));
183
}
184
else
185
{
186
Error::SetStringView(error, "HOME environment variable is not set.");
187
}
188
}
189
190
#elif defined(__APPLE__)
191
192
static constexpr char MAC_DATA_DIR[] = "Library/Application Support/DuckStation";
193
const char* home_dir = getenv("HOME");
194
if (home_dir)
195
EmuFolders::DataRoot = Path::RealPath(Path::Combine(home_dir, MAC_DATA_DIR));
196
else
197
Error::SetStringView(error, "HOME environment variable is not set.");
198
199
#endif
200
201
// Couldn't find anything? Fall back to portable.
202
if (EmuFolders::DataRoot.empty())
203
{
204
Error::AddPrefix(
205
error,
206
"Failed to set data directory. Please ensure your system is configured correctly. You can also try portable mode "
207
"by creating portable.txt in the same directory you installed DuckStation into.\n\nThe error was:\n");
208
return false;
209
}
210
211
// Ensure the directories exist.
212
if (!FileSystem::EnsureDirectoryExists(EmuFolders::DataRoot.c_str(), false, error))
213
{
214
Error::AddPrefixFmt(error,
215
"Failed to create data directory at path:\n\n{}\n\n.Please ensure this directory is "
216
"writable. You can also try portable mode by creating portable.txt in the same directory you "
217
"installed DuckStation into.\n\nThe error was:\n",
218
EmuFolders::AppRoot);
219
return false;
220
}
221
222
return true;
223
}
224
225
#ifndef __ANDROID__
226
227
std::string Core::GetBaseSettingsPath()
228
{
229
return Path::Combine(EmuFolders::DataRoot, "settings.ini");
230
}
231
232
bool Core::InitializeBaseSettingsLayer(std::string settings_path, Error* error)
233
{
234
INISettingsInterface& si = s_locals.base_settings_interface;
235
s_locals.layered_settings_interface.SetLayer(LayeredSettingsInterface::LAYER_BASE, &si);
236
237
if (!settings_path.empty())
238
{
239
const bool settings_exists = FileSystem::FileExists(settings_path.c_str());
240
INFO_LOG("Loading config from {}.", settings_path);
241
si.SetPath(std::move(settings_path));
242
243
if (settings_exists)
244
{
245
if (!si.Load(error))
246
{
247
Error::AddPrefix(error, "Failed to load settings: ");
248
return false;
249
}
250
}
251
else
252
{
253
SetDefaultSettings(si, true, true, true);
254
if (!si.Save(error))
255
{
256
Error::AddPrefix(error, "Failed to save settings: ");
257
return false;
258
}
259
}
260
}
261
else
262
{
263
// Running settings-file-less, use defaults.
264
SetDefaultSettings(si, true, true, true);
265
}
266
267
EmuFolders::LoadConfig(si);
268
EmuFolders::EnsureFoldersExist();
269
270
// We need to create the console window early, otherwise it appears in front of the main window.
271
if (!Log::IsConsoleOutputEnabled() && si.GetBoolValue("Logging", "LogToConsole", false))
272
Log::SetConsoleOutputParams(true, si.GetBoolValue("Logging", "LogTimestamps", true));
273
274
return true;
275
}
276
277
bool Core::SaveBaseSettingsLayer(Error* error)
278
{
279
INISettingsInterface& si = s_locals.base_settings_interface;
280
if (si.IsDirty() && !si.Save(error))
281
return false;
282
283
return true;
284
}
285
286
void Core::SetDefaultSettings(bool host, bool system, bool controller)
287
{
288
{
289
const auto lock = GetSettingsLock();
290
SetDefaultSettings(s_locals.base_settings_interface, host, system, controller);
291
}
292
293
Host::OnSettingsResetToDefault(host, system, controller);
294
}
295
296
void Core::SetDefaultSettings(SettingsInterface& si, bool host, bool system, bool controller)
297
{
298
if (host)
299
Host::SetDefaultSettings(si);
300
301
if (system)
302
{
303
System::SetDefaultSettings(si);
304
EmuFolders::SetDefaults();
305
EmuFolders::Save(si);
306
}
307
308
if (controller)
309
{
310
InputManager::SetDefaultSourceConfig(si);
311
Settings::SetDefaultControllerConfig(si);
312
Settings::SetDefaultHotkeyConfig(si);
313
}
314
}
315
316
#endif // __ANDROID__
317
318
std::unique_lock<std::mutex> Core::GetSettingsLock()
319
{
320
return std::unique_lock(s_locals.settings_mutex);
321
}
322
323
SettingsInterface* Core::GetSettingsInterface()
324
{
325
return &s_locals.layered_settings_interface;
326
}
327
328
std::string Core::GetBaseStringSettingValue(const char* section, const char* key, const char* default_value /*= ""*/)
329
{
330
std::unique_lock lock(s_locals.settings_mutex);
331
return s_locals.layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)
332
->GetStringValue(section, key, default_value);
333
}
334
335
SmallString Core::GetBaseSmallStringSettingValue(const char* section, const char* key,
336
const char* default_value /*= ""*/)
337
{
338
std::unique_lock lock(s_locals.settings_mutex);
339
return s_locals.layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)
340
->GetSmallStringValue(section, key, default_value);
341
}
342
343
TinyString Core::GetBaseTinyStringSettingValue(const char* section, const char* key, const char* default_value /*= ""*/)
344
{
345
std::unique_lock lock(s_locals.settings_mutex);
346
return s_locals.layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)
347
->GetTinyStringValue(section, key, default_value);
348
}
349
350
bool Core::GetBaseBoolSettingValue(const char* section, const char* key, bool default_value /*= false*/)
351
{
352
std::unique_lock lock(s_locals.settings_mutex);
353
return s_locals.layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)
354
->GetBoolValue(section, key, default_value);
355
}
356
357
s32 Core::GetBaseIntSettingValue(const char* section, const char* key, s32 default_value /*= 0*/)
358
{
359
std::unique_lock lock(s_locals.settings_mutex);
360
return s_locals.layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)
361
->GetIntValue(section, key, default_value);
362
}
363
364
u32 Core::GetBaseUIntSettingValue(const char* section, const char* key, u32 default_value /*= 0*/)
365
{
366
std::unique_lock lock(s_locals.settings_mutex);
367
return s_locals.layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)
368
->GetUIntValue(section, key, default_value);
369
}
370
371
float Core::GetBaseFloatSettingValue(const char* section, const char* key, float default_value /*= 0.0f*/)
372
{
373
std::unique_lock lock(s_locals.settings_mutex);
374
return s_locals.layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)
375
->GetFloatValue(section, key, default_value);
376
}
377
378
double Core::GetBaseDoubleSettingValue(const char* section, const char* key, double default_value /* = 0.0f */)
379
{
380
std::unique_lock lock(s_locals.settings_mutex);
381
return s_locals.layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)
382
->GetDoubleValue(section, key, default_value);
383
}
384
385
std::vector<std::string> Core::GetBaseStringListSetting(const char* section, const char* key)
386
{
387
std::unique_lock lock(s_locals.settings_mutex);
388
return s_locals.layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)
389
->GetStringList(section, key);
390
}
391
392
std::string Core::GetStringSettingValue(const char* section, const char* key, const char* default_value /*= ""*/)
393
{
394
std::unique_lock lock(s_locals.settings_mutex);
395
return s_locals.layered_settings_interface.GetStringValue(section, key, default_value);
396
}
397
398
SmallString Core::GetSmallStringSettingValue(const char* section, const char* key, const char* default_value /*= ""*/)
399
{
400
std::unique_lock lock(s_locals.settings_mutex);
401
return s_locals.layered_settings_interface.GetSmallStringValue(section, key, default_value);
402
}
403
404
TinyString Core::GetTinyStringSettingValue(const char* section, const char* key, const char* default_value /*= ""*/)
405
{
406
std::unique_lock lock(s_locals.settings_mutex);
407
return s_locals.layered_settings_interface.GetTinyStringValue(section, key, default_value);
408
}
409
410
bool Core::GetBoolSettingValue(const char* section, const char* key, bool default_value /*= false*/)
411
{
412
std::unique_lock lock(s_locals.settings_mutex);
413
return s_locals.layered_settings_interface.GetBoolValue(section, key, default_value);
414
}
415
416
s32 Core::GetIntSettingValue(const char* section, const char* key, s32 default_value /*= 0*/)
417
{
418
std::unique_lock lock(s_locals.settings_mutex);
419
return s_locals.layered_settings_interface.GetIntValue(section, key, default_value);
420
}
421
422
u32 Core::GetUIntSettingValue(const char* section, const char* key, u32 default_value /*= 0*/)
423
{
424
std::unique_lock lock(s_locals.settings_mutex);
425
return s_locals.layered_settings_interface.GetUIntValue(section, key, default_value);
426
}
427
428
float Core::GetFloatSettingValue(const char* section, const char* key, float default_value /*= 0.0f*/)
429
{
430
std::unique_lock lock(s_locals.settings_mutex);
431
return s_locals.layered_settings_interface.GetFloatValue(section, key, default_value);
432
}
433
434
double Core::GetDoubleSettingValue(const char* section, const char* key, double default_value /*= 0.0f*/)
435
{
436
std::unique_lock lock(s_locals.settings_mutex);
437
return s_locals.layered_settings_interface.GetDoubleValue(section, key, default_value);
438
}
439
440
std::vector<std::string> Core::GetStringListSetting(const char* section, const char* key)
441
{
442
std::unique_lock lock(s_locals.settings_mutex);
443
return s_locals.layered_settings_interface.GetStringList(section, key);
444
}
445
446
void Core::SetBaseBoolSettingValue(const char* section, const char* key, bool value)
447
{
448
std::unique_lock lock(s_locals.settings_mutex);
449
s_locals.layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->SetBoolValue(section, key, value);
450
}
451
452
void Core::SetBaseIntSettingValue(const char* section, const char* key, s32 value)
453
{
454
std::unique_lock lock(s_locals.settings_mutex);
455
s_locals.layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->SetIntValue(section, key, value);
456
}
457
458
void Core::SetBaseUIntSettingValue(const char* section, const char* key, u32 value)
459
{
460
std::unique_lock lock(s_locals.settings_mutex);
461
s_locals.layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->SetUIntValue(section, key, value);
462
}
463
464
void Core::SetBaseFloatSettingValue(const char* section, const char* key, float value)
465
{
466
std::unique_lock lock(s_locals.settings_mutex);
467
s_locals.layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)
468
->SetFloatValue(section, key, value);
469
}
470
471
void Core::SetBaseStringSettingValue(const char* section, const char* key, const char* value)
472
{
473
std::unique_lock lock(s_locals.settings_mutex);
474
s_locals.layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)
475
->SetStringValue(section, key, value);
476
}
477
478
void Core::SetBaseStringListSettingValue(const char* section, const char* key, const std::vector<std::string>& values)
479
{
480
std::unique_lock lock(s_locals.settings_mutex);
481
s_locals.layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)
482
->SetStringList(section, key, values);
483
}
484
485
bool Core::AddValueToBaseStringListSetting(const char* section, const char* key, const char* value)
486
{
487
std::unique_lock lock(s_locals.settings_mutex);
488
return s_locals.layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)
489
->AddToStringList(section, key, value);
490
}
491
492
bool Core::RemoveValueFromBaseStringListSetting(const char* section, const char* key, const char* value)
493
{
494
std::unique_lock lock(s_locals.settings_mutex);
495
return s_locals.layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)
496
->RemoveFromStringList(section, key, value);
497
}
498
499
bool Core::ContainsBaseSettingValue(const char* section, const char* key)
500
{
501
std::unique_lock lock(s_locals.settings_mutex);
502
return s_locals.layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)
503
->ContainsValue(section, key);
504
}
505
506
void Core::DeleteBaseSettingValue(const char* section, const char* key)
507
{
508
std::unique_lock lock(s_locals.settings_mutex);
509
s_locals.layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE)->DeleteValue(section, key);
510
}
511
512
SettingsInterface* Core::GetBaseSettingsLayer()
513
{
514
return s_locals.layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE);
515
}
516
517
SettingsInterface* Core::GetGameSettingsLayer()
518
{
519
return s_locals.layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_GAME);
520
}
521
522
SettingsInterface* Core::GetInputSettingsLayer()
523
{
524
return s_locals.layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_INPUT);
525
}
526
527
#ifdef __ANDROID__
528
529
void Core::SetBaseSettingsLayer(SettingsInterface* sif)
530
{
531
AssertMsg(s_locals.layered_settings_interface.GetLayer(LayeredSettingsInterface::LAYER_BASE) == nullptr,
532
"Base layer has not been set");
533
s_locals.layered_settings_interface.SetLayer(LayeredSettingsInterface::LAYER_BASE, sif);
534
}
535
536
#endif // __ANDROID__
537
538
void Core::SetGameSettingsLayer(SettingsInterface* sif, std::unique_lock<std::mutex>& lock)
539
{
540
s_locals.layered_settings_interface.SetLayer(LayeredSettingsInterface::LAYER_GAME, sif);
541
}
542
543
void Core::SetInputSettingsLayer(SettingsInterface* sif, std::unique_lock<std::mutex>& lock)
544
{
545
s_locals.layered_settings_interface.SetLayer(LayeredSettingsInterface::LAYER_INPUT, sif);
546
}
547
548
std::string Core::GetHTTPUserAgent()
549
{
550
return fmt::format("DuckStation for {} ({}) {}", TARGET_OS_STR, CPU_ARCH_STR, g_scm_tag_str);
551
}
552
553