LCOV - code coverage report
Current view: top level - src/presets - preset_manager_dirs.cpp (source / functions) Coverage Total Hit
Test: merged.info Lines: 90.8 % 153 139
Test Date: 2026-06-03 09:13:19 Functions: 100.0 % 8 8

            Line data    Source code
       1              : #include "preset_manager.h"
       2              : #include "preset_manager_impl.h"
       3              : #include <iostream>
       4              : #include <cstdlib>
       5              : #include <filesystem>
       6              : #include <sys/stat.h>
       7              : 
       8              : #ifdef _WIN32
       9              : #include <direct.h>
      10              : #include <io.h>
      11              : #include <windows.h>
      12              : #define MKDIR(path) _mkdir(path)
      13              : #elif defined(__APPLE__)
      14              : #include <dirent.h>
      15              : #include <mach-o/dyld.h>
      16              : #define MKDIR(path) mkdir(path, 0755)
      17              : #else
      18              : #include <dirent.h>
      19              : #define MKDIR(path) mkdir(path, 0755)
      20              : #endif
      21              : 
      22              : #ifdef _EMSCRIPTEN_
      23              : #include <emscripten.h>
      24              : 
      25              : namespace Amplitron {
      26              :     std::string PresetManager::get_user_presets_dir() {
      27              :         char* result = (char*)EM_ASM_PTR({
      28              :             return stringToNewUTF8(
      29              :                 window._amplitronPresetDir || 'preset'
      30              :             );
      31              :         });
      32              :         std::string dir(result);
      33              :         free(result);
      34              :         return dir;
      35              :     }
      36              : }
      37              : #endif
      38              : 
      39              : namespace Amplitron {
      40              : 
      41          514 : void append_json_files(const std::string& dir,
      42              :                        std::vector<std::string>& result) {
      43          176 :     try {
      44         3957 :         for (const auto& entry : std::filesystem::directory_iterator(dir)) {
      45         2926 :             if (!entry.is_regular_file()) continue;
      46         2911 :             const auto& path = entry.path();
      47         5908 :             if (path.extension() == ".json") {
      48         3906 :                 result.push_back(path.string());
      49          955 :             }
      50          504 :         }
      51          175 :     } catch (...) {
      52              :         // Ignore invalid or non-existent directories
      53            9 :     }
      54          517 : }
      55              : 
      56           21 : std::string get_bundled_presets_dir() {
      57              : #ifdef _WIN32
      58           14 :     auto has_json_presets = [](const std::string& dir) -> bool {
      59            7 :         std::vector<std::string> files;
      60            7 :         append_json_files(dir, files);
      61            7 :         return !files.empty();
      62            7 :     };
      63              : 
      64            7 :     char path[MAX_PATH];
      65            7 :     if (GetModuleFileNameA(nullptr, path, sizeof(path))) {
      66            7 :         std::filesystem::path exe_path(path);
      67            7 :         std::filesystem::path exe_dir = exe_path.parent_path();
      68              : 
      69              :         // Installed/bundled layout: presets next to the executable.
      70           14 :         std::string bundled = (exe_dir / "presets").string();
      71            7 :         if (dir_exists(bundled) && has_json_presets(bundled)) return bundled;
      72              : 
      73              :         // Dev/CMake layout: executable under build dir, presets at repo root.
      74            0 :         std::string repo_root_presets = (exe_dir / ".." / "presets").string();
      75            0 :         if (dir_exists(repo_root_presets) && has_json_presets(repo_root_presets)) return repo_root_presets;
      76           21 :     }
      77              : 
      78              :     // Fallback: relative to current working directory (useful for local runs).
      79            0 :     if (dir_exists("presets") && has_json_presets("presets")) return "presets";
      80            0 :     return "presets";
      81              : #elif defined(__APPLE__)
      82              :     char exe_path[4096];
      83            7 :     uint32_t size = sizeof(exe_path);
      84            7 :     if (_NSGetExecutablePath(exe_path, &size) == 0) {
      85            7 :         std::string exe_str = exe_path;
      86            7 :         size_t last_slash = exe_str.find_last_of("/");
      87            7 :         if (last_slash != std::string::npos) {
      88            7 :             std::string bundle_presets = exe_str.substr(0, last_slash) + "/../Resources/presets";
      89            7 :             if (dir_exists(bundle_presets)) {
      90            0 :                 return bundle_presets;
      91              :             }
      92           14 :         }
      93            7 :     }
      94            7 :     return "presets";
      95              : #else
      96           14 :     if (dir_exists("presets")) {
      97           14 :         return "presets";
      98              :     }
      99            0 :     return "/usr/share/amplitron/presets";
     100              : #endif
     101            7 : }
     102          462 : void PresetManager::set_presets_dir(const std::string& dir) {
     103          462 :     if (dir.empty()) {
     104           30 :         custom_presets_dir_ = "";
     105           40 :         return;
     106              :     }
     107          432 :     std::string normalized = dir;
     108              : #ifdef _WIN32
     109              :     // Allow callers/tests to pass forward slashes; normalize for _findfirst/_mkdir.
     110         7444 :     for (char& c : normalized) {
     111         7300 :         if (c == '/') c = '\\';
     112              :     }
     113              : #endif
     114          144 :     try {
     115          432 :         std::filesystem::create_directories(normalized);
     116          144 :     } catch (...) {
     117            0 :         return;
     118            0 :     }
     119          432 :     if (dir_exists(normalized)) {
     120          432 :         custom_presets_dir_ = normalized;
     121          432 :         std::vector<std::string> dir_presets;
     122          432 :         append_json_files(normalized, dir_presets);
     123          432 :         if (dir_presets.empty()) {
     124           21 :             save_factory_presets(normalized);
     125            7 :         }
     126          432 :     }
     127          442 : }
     128              : 
     129           15 : void PresetManager::save_config() {
     130           15 :     std::string path = get_config_path();
     131           15 :     std::ofstream f(path);
     132           15 :     if (!f.is_open()) return;
     133            9 :     f << "{\n";
     134            9 :     f << "  \"presets_dir\": \"";
     135          325 :     for (char c : custom_presets_dir_) {
     136          316 :         if (c == '\\') f << "\\\\";
     137          309 :         else if (c == '"') f << "\\\"";
     138          309 :         else f << c;
     139              :     }
     140            9 :     f << "\"\n}\n";
     141           17 : }
     142              : 
     143           45 : void PresetManager::load_config() {
     144           45 :     std::string path = get_config_path();
     145           45 :     std::ifstream f(path);
     146           45 :     if (!f.is_open()) return;
     147              : 
     148           32 :     std::string content((std::istreambuf_iterator<char>(f)),
     149           40 :                         std::istreambuf_iterator<char>());
     150              : 
     151           32 :     const std::string key = "\"presets_dir\"";
     152           32 :     size_t key_pos = content.find(key);
     153           32 :     if (key_pos == std::string::npos) return;
     154              : 
     155           29 :     size_t colon = content.find(':', key_pos + key.size());
     156           29 :     if (colon == std::string::npos) return;
     157              : 
     158           24 :     size_t quote_open = content.find('"', colon + 1);
     159           24 :     if (quote_open == std::string::npos) return;
     160              : 
     161           19 :     std::string value;
     162           19 :     size_t i = quote_open + 1;
     163          687 :     while (i < content.size() && content[i] != '"') {
     164          668 :         if (content[i] == '\\' && i + 1 < content.size()) {
     165           17 :             ++i;
     166           17 :             if (content[i] == '\\') value += '\\';
     167            8 :             else if (content[i] == '"') value += '"';
     168            6 :             else if (content[i] == 'n') value += '\n';
     169            6 :             else { value += '\\'; value += content[i]; }
     170            4 :         } else {
     171          651 :             value += content[i];
     172              :         }
     173          668 :         ++i;
     174              :     }
     175              : 
     176           19 :     if (!value.empty() && dir_exists(value)) {
     177           17 :         custom_presets_dir_ = value;
     178            4 :     }
     179           63 : }
     180              : 
     181          504 : std::string PresetManager::get_presets_dir() {
     182          504 :     if (!custom_presets_dir_.empty()) {
     183          491 :         MKDIR(custom_presets_dir_.c_str());
     184          491 :         if (dir_exists(custom_presets_dir_)) {
     185          659 :             return custom_presets_dir_;
     186              :         }
     187            0 :     }
     188              : 
     189           13 :     std::string user_dir = get_user_presets_dir();
     190           13 :     if (!user_dir.empty()) {
     191            2 :         try {
     192           12 :             std::filesystem::create_directories(user_dir);
     193            5 :         } catch (...) {
     194              :             // Fall through to local fallback if creation fails
     195            1 :         }
     196           10 :         if (dir_exists(user_dir)) {
     197            9 :             return user_dir;
     198              :         }
     199            0 :     }
     200              : 
     201            4 :     std::string dir = "presets";
     202            4 :     MKDIR(dir.c_str());
     203            4 :     return dir;
     204          179 : }
     205              : 
     206           21 : void PresetManager::save_factory_presets(const std::string& dir) {
     207           21 :     std::string src_dir = get_bundled_presets_dir();
     208           21 :     if (!dir_exists(src_dir)) {
     209            0 :         std::cerr << "Bundled presets directory not found: " << src_dir << std::endl;
     210            0 :         return;
     211              :     }
     212              : 
     213           21 :     std::vector<std::string> preset_files;
     214           21 :     append_json_files(src_dir, preset_files);
     215              : 
     216          150 :     for (const auto& src_path : preset_files) {
     217          129 :         size_t last_slash = src_path.find_last_of("/\\");
     218          172 :         std::string filename = (last_slash != std::string::npos) ?
     219          129 :                                 src_path.substr(last_slash + 1) : src_path;
     220              : 
     221              : #ifdef _WIN32
     222           43 :         std::string dest_path = dir + "\\" + filename;
     223              : #else
     224           86 :         std::string dest_path = dir + "/" + filename;
     225              : #endif
     226              : 
     227          129 :         std::ifstream src_file(src_path);
     228          129 :         if (!src_file.is_open()) {
     229            0 :             std::cerr << "Could not open source preset: " << src_path << std::endl;
     230            0 :             continue;
     231              :         }
     232              : 
     233          129 :         std::string content((std::istreambuf_iterator<char>(src_file)),
     234          172 :                            std::istreambuf_iterator<char>());
     235          129 :         src_file.close();
     236              : 
     237          129 :         std::ofstream dest_file(dest_path);
     238          129 :         if (!dest_file.is_open()) {
     239           14 :             std::cerr << "Could not write preset: " << dest_path << std::endl;
     240           14 :             continue;
     241              :         }
     242              : 
     243          115 :         dest_file << content;
     244          115 :         dest_file.close();
     245          157 :     }
     246           21 : }
     247              : 
     248              : } // namespace Amplitron
        

Generated by: LCOV version 2.0-1