LCOV - code coverage report
Current view: top level - src/presets - preset_manager_dirs.cpp (source / functions) Coverage Total Hit
Test: merged.info Lines: 89.9 % 159 143
Test Date: 2026-06-07 15:51:50 Functions: 100.0 % 8 8

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

Generated by: LCOV version 2.0-1