Line data Source code
1 : #include "preset_manager.h"
2 :
3 : #include <sys/stat.h>
4 :
5 : #include <cstdlib>
6 : #include <cstring>
7 : #include <ctime>
8 : #include <filesystem>
9 : #include <iostream>
10 :
11 : #include "preset_manager_impl.h"
12 : #include "presets/preset_components.h"
13 :
14 : #ifdef _WIN32
15 : #include <direct.h>
16 : #include <io.h>
17 : #include <windows.h>
18 : #define MKDIR(path) _mkdir(path)
19 : #define STAT_STRUCT struct _stat
20 : #define STAT_FN _stat
21 : #elif defined(__APPLE__)
22 : #include <dirent.h>
23 : #include <mach-o/dyld.h>
24 : #define MKDIR(path) mkdir(path, 0755)
25 : #define STAT_STRUCT struct stat
26 : #define STAT_FN stat
27 : #else
28 : #include <dirent.h>
29 : #define MKDIR(path) mkdir(path, 0755)
30 : #define STAT_STRUCT struct stat
31 : #define STAT_FN stat
32 : #endif
33 :
34 : namespace Amplitron {
35 :
36 378 : PresetManager::PresetManager() : PresetManager(nullptr, nullptr, nullptr) {}
37 :
38 630 : PresetManager::PresetManager(std::unique_ptr<IPresetSerializer> serializer,
39 : std::unique_ptr<IPresetStorage> storage,
40 252 : std::unique_ptr<IPresetMigrator> migrator)
41 378 : : serializer_(std::move(serializer)),
42 378 : storage_(std::move(storage)),
43 756 : migrator_(std::move(migrator)) {
44 378 : if (!serializer_) serializer_ = std::make_unique<PresetSerializer>();
45 378 : if (!storage_) storage_ = std::make_unique<PresetStorage>();
46 378 : if (!migrator_) migrator_ = std::make_unique<PresetMigrator>();
47 504 : }
48 :
49 525 : PresetManager::~PresetManager() = default;
50 :
51 2 : std::string PresetManager::custom_presets_dir_;
52 :
53 931 : bool dir_exists(const std::string& path) {
54 309 : STAT_STRUCT st;
55 1514 : return STAT_FN(path.c_str(), &st) == 0 &&
56 : #ifdef _WIN32
57 294 : (st.st_mode & _S_IFDIR);
58 : #else
59 894 : S_ISDIR(st.st_mode);
60 : #endif
61 : }
62 :
63 39 : std::string PresetManager::get_system_presets_dir() {
64 : #ifdef _WIN32
65 13 : const char* pd = std::getenv("ProgramData");
66 26 : return pd ? std::string(pd) + "\\Amplitron\\presets" : "";
67 : #elif defined(__APPLE__)
68 13 : return "/Library/Application Support/Amplitron/presets";
69 : #else
70 26 : return "/usr/share/amplitron/presets";
71 : #endif
72 : }
73 :
74 60 : std::string PresetManager::get_config_path() {
75 : #ifdef _WIN32
76 20 : const char* appdata = std::getenv("APPDATA");
77 20 : if (!appdata) return "amplitron_config.json";
78 34 : std::string dir = std::string(appdata) + "\\Amplitron";
79 17 : MKDIR(dir.c_str());
80 17 : return dir + "\\config.json";
81 : #else
82 40 : const char* home = std::getenv("HOME");
83 46 : if (!home) return "amplitron_config.json";
84 34 : std::string config_dir = std::string(home) + "/.config/amplitron";
85 : try {
86 36 : std::filesystem::create_directories(config_dir);
87 19 : } catch (...) {
88 4 : }
89 34 : return config_dir + "/config.json";
90 : #endif
91 56 : }
92 :
93 16 : std::string get_user_presets_dir() {
94 : #ifdef _WIN32
95 4 : const char* appdata = std::getenv("APPDATA");
96 4 : if (!appdata) return "";
97 6 : return std::string(appdata) + "\\Amplitron\\presets";
98 : #elif defined(__APPLE__)
99 6 : const char* home = std::getenv("HOME");
100 6 : if (!home) return "";
101 5 : return std::string(home) + "/Library/Application Support/Amplitron/presets";
102 : #else
103 6 : const char* home = std::getenv("HOME");
104 8 : if (!home) return "";
105 10 : return std::string(home) + "/.config/amplitron/presets";
106 : #endif
107 6 : }
108 :
109 : // Clean, robust string migration implementation (No JSON header dependencies)
110 84 : std::string PresetManager::apply_migrations(const std::string& raw_json_string) {
111 : // Find the absolute root opening of the JSON payload
112 84 : size_t root_start = raw_json_string.find('{');
113 84 : if (root_start == std::string::npos) {
114 6 : return raw_json_string;
115 : }
116 :
117 : // Look for a version key strictly near the root area
118 78 : size_t version_pos = raw_json_string.find("\"format_version\"");
119 78 : if (version_pos == std::string::npos) {
120 21 : version_pos = raw_json_string.find("\"version\"");
121 7 : }
122 78 : bool is_root_version = (version_pos != std::string::npos && (version_pos - root_start) < 100);
123 :
124 57 : if (!is_root_version) {
125 15 : std::cout << "[Preset Migration] Upgrading legacy unversioned preset format to Version "
126 15 : << CURRENT_PRESET_VERSION << std::endl;
127 :
128 15 : std::string patched = raw_json_string;
129 15 : size_t last_bracket = patched.find_last_of('}');
130 :
131 15 : if (last_bracket != std::string::npos && last_bracket > root_start) {
132 : // Find out if there is any content between the root brackets to avoid trailing comma
133 : // bugs
134 12 : size_t content_check = patched.find_first_not_of(" \t\n\r", root_start + 1);
135 12 : bool is_empty_json = (content_check == last_bracket);
136 :
137 : // Construct the exact version upgrade block using our header constant dynamically
138 12 : std::string migration_patch;
139 12 : if (!is_empty_json) {
140 6 : migration_patch += ",\n";
141 2 : }
142 8 : migration_patch +=
143 20 : " \"format_version\": " + std::to_string(CURRENT_PRESET_VERSION) + ",\n";
144 20 : migration_patch += " \"version\": " + std::to_string(CURRENT_PRESET_VERSION) + ",\n";
145 12 : migration_patch += " \"input_gain\": 0.7,\n";
146 12 : migration_patch += " \"output_gain\": 0.8\n";
147 :
148 12 : patched.insert(last_bracket, migration_patch);
149 12 : return patched;
150 12 : }
151 16 : }
152 :
153 66 : return raw_json_string;
154 28 : }
155 :
156 : } // namespace Amplitron
|