#include #include #include #include #include #include #if defined(OS_WINDOWS) #include #include #elif defined(OS_LINUX) #include #include #endif #include #include namespace hex::fs { std::optional getExecutablePath() { #if defined(OS_WINDOWS) std::wstring exePath(MAX_PATH, '\0'); if (GetModuleFileNameW(nullptr, exePath.data(), exePath.length()) == 0) return std::nullopt; return exePath; #elif defined(OS_LINUX) std::string exePath(PATH_MAX, '\0'); if (readlink("/proc/self/exe", exePath.data(), PATH_MAX) < 0) return std::nullopt; return exePath; #elif defined(OS_MACOS) std::string result; { auto string = getMacExecutableDirectoryPath(); result = string; macFree(string); } return result; #else return std::nullopt; #endif } bool isPathWritable(const std::fs::path &path) { constexpr static auto TestFileName = "__imhex__tmp__"; { File file(path / TestFileName, File::Mode::Read); if (file.isValid()) { if (!file.remove()) return false; } } File file(path / TestFileName, File::Mode::Create); bool result = file.isValid(); if (!file.remove()) return false; return result; } bool openFileBrowser(DialogMode mode, const std::vector &validExtensions, const std::function &callback, const std::string &defaultPath) { NFD::Init(); nfdchar_t *outPath; nfdresult_t result; switch (mode) { case DialogMode::Open: result = NFD::OpenDialog(outPath, validExtensions.data(), validExtensions.size(), defaultPath.c_str()); break; case DialogMode::Save: result = NFD::SaveDialog(outPath, validExtensions.data(), validExtensions.size(), defaultPath.c_str()); break; case DialogMode::Folder: result = NFD::PickFolder(outPath, defaultPath.c_str()); break; default: hex::unreachable(); } if (result == NFD_OKAY) { callback(reinterpret_cast(outPath)); NFD::FreePath(outPath); } NFD::Quit(); return result == NFD_OKAY; } std::vector getDefaultPaths(ImHexPath path, bool listNonExisting) { std::vector result; const auto exePath = getExecutablePath(); const std::string settingName { "hex.builtin.setting.folders" }; auto userDirs = ContentRegistry::Settings::read(settingName, settingName, std::vector {}); [[maybe_unused]] auto addUserDirs = [&userDirs](auto &paths) { std::transform(userDirs.begin(), userDirs.end(), std::back_inserter(paths), [](auto &item) { return std::move(item); }); }; #if defined(OS_WINDOWS) std::fs::path appDataDir; { PWSTR wAppDataPath = nullptr; if (!SUCCEEDED(SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, nullptr, &wAppDataPath))) throw std::runtime_error("Failed to get APPDATA folder path"); appDataDir = std::wstring(wAppDataPath); CoTaskMemFree(wAppDataPath); } std::vector paths = { appDataDir / "imhex" }; if (exePath) paths.push_back(exePath->parent_path()); switch (path) { case ImHexPath::Patterns: addUserDirs(paths); std::transform(paths.begin(), paths.end(), std::back_inserter(result), [](auto &path) { return path / "patterns"; }); break; case ImHexPath::PatternsInclude: addUserDirs(paths); std::transform(paths.begin(), paths.end(), std::back_inserter(result), [](auto &path) { return path / "includes"; }); break; case ImHexPath::Magic: addUserDirs(paths); std::transform(paths.begin(), paths.end(), std::back_inserter(result), [](auto &path) { return path / "magic"; }); break; case ImHexPath::Python: addUserDirs(paths); std::transform(paths.begin(), paths.end(), std::back_inserter(result), [](auto &path) { return path / "python"; }); break; case ImHexPath::Plugins: std::transform(paths.begin(), paths.end(), std::back_inserter(result), [](auto &path) { return path / "plugins"; }); break; case ImHexPath::Yara: addUserDirs(paths); std::transform(paths.begin(), paths.end(), std::back_inserter(result), [](auto &path) { return path / "yara"; }); break; case ImHexPath::Config: return { appDataDir / "imhex" / "config" }; case ImHexPath::Resources: std::transform(paths.begin(), paths.end(), std::back_inserter(result), [](auto &path) { return path / "resources"; }); break; case ImHexPath::Constants: addUserDirs(paths); std::transform(paths.begin(), paths.end(), std::back_inserter(result), [](auto &path) { return path / "constants"; }); break; case ImHexPath::Encodings: addUserDirs(paths); std::transform(paths.begin(), paths.end(), std::back_inserter(result), [](auto &path) { return path / "encodings"; }); break; case ImHexPath::Logs: std::transform(paths.begin(), paths.end(), std::back_inserter(result), [](auto &path) { return path / "logs"; }); break; default: hex::unreachable(); } #elif defined(OS_MACOS) // Get path to special directories std::string applicationSupportDir; { auto string = getMacApplicationSupportDirectoryPath(); applicationSupportDir = string; macFree(string); } const std::fs::path applicationSupportDirPath(applicationSupportDir); std::vector paths = { applicationSupportDirPath }; if (exePath.has_value()) paths.push_back(exePath.value()); switch (path) { case ImHexPath::Patterns: result.push_back((applicationSupportDirPath / "patterns").string()); break; case ImHexPath::PatternsInclude: result.push_back((applicationSupportDirPath / "includes").string()); break; case ImHexPath::Magic: result.push_back((applicationSupportDirPath / "magic").string()); break; case ImHexPath::Python: result.push_back((applicationSupportDirPath / "python").string()); break; case ImHexPath::Plugins: std::transform(paths.begin(), paths.end(), std::back_inserter(result), [](auto &path) { return (path / "plugins").string(); }); break; case ImHexPath::Yara: result.push_back((applicationSupportDirPath / "yara").string()); break; case ImHexPath::Config: result.push_back((applicationSupportDirPath / "config").string()); break; case ImHexPath::Resources: result.push_back((applicationSupportDirPath / "resources").string()); break; case ImHexPath::Constants: result.push_back((applicationSupportDirPath / "constants").string()); break; case ImHexPath::Encodings: result.push_back((applicationSupportDirPath / "encodings").string()); break; case ImHexPath::Logs: result.push_back((applicationSupportDirPath / "logs").string()); break; default: hex::unreachable(); } #else std::vector configDirs = xdg::ConfigDirs(); std::vector dataDirs = xdg::DataDirs(); configDirs.push_back(xdg::ConfigHomeDir()); dataDirs.push_back(xdg::DataHomeDir()); for (auto &dir : dataDirs) dir = dir / "imhex"; if (exePath && !exePath->empty()) dataDirs.push_back(exePath->parent_path()); switch (path) { case ImHexPath::Patterns: addUserDirs(dataDirs); std::transform(dataDirs.begin(), dataDirs.end(), std::back_inserter(result), [](auto p) { return (p / "patterns").string(); }); break; case ImHexPath::PatternsInclude: addUserDirs(dataDirs); std::transform(dataDirs.begin(), dataDirs.end(), std::back_inserter(result), [](auto p) { return (p / "includes").string(); }); break; case ImHexPath::Magic: addUserDirs(dataDirs); std::transform(dataDirs.begin(), dataDirs.end(), std::back_inserter(result), [](auto p) { return (p / "magic").string(); }); break; case ImHexPath::Python: addUserDirs(dataDirs); std::transform(dataDirs.begin(), dataDirs.end(), std::back_inserter(result), [](auto p) { return (p).string(); }); break; case ImHexPath::Plugins: std::transform(dataDirs.begin(), dataDirs.end(), std::back_inserter(result), [](auto p) { return (p / "plugins").string(); }); break; case ImHexPath::Yara: addUserDirs(dataDirs); std::transform(dataDirs.begin(), dataDirs.end(), std::back_inserter(result), [](auto p) { return (p / "yara").string(); }); break; case ImHexPath::Config: std::transform(configDirs.begin(), configDirs.end(), std::back_inserter(result), [](auto p) { return (p / "imhex").string(); }); break; case ImHexPath::Resources: std::transform(dataDirs.begin(), dataDirs.end(), std::back_inserter(result), [](auto p) { return (p / "resources").string(); }); break; case ImHexPath::Constants: addUserDirs(dataDirs); std::transform(dataDirs.begin(), dataDirs.end(), std::back_inserter(result), [](auto p) { return (p / "constants").string(); }); break; case ImHexPath::Encodings: addUserDirs(dataDirs); std::transform(dataDirs.begin(), dataDirs.end(), std::back_inserter(result), [](auto p) { return (p / "encodings").string(); }); break; case ImHexPath::Logs: std::transform(dataDirs.begin(), dataDirs.end(), std::back_inserter(result), [](auto p) { return (p / "logs").string(); }); break; default: hex::unreachable(); } #endif if (!listNonExisting) { result.erase(std::remove_if(result.begin(), result.end(), [](const auto &path) { return !fs::isDirectory(path); }), result.end()); } return result; } std::fs::path toShortPath(const std::fs::path &path) { #if defined(OS_WINDOWS) size_t size = GetShortPathNameW(path.c_str(), nullptr, 0) * sizeof(TCHAR); if (size == 0) return path; std::wstring newPath(size, 0x00); GetShortPathNameW(path.c_str(), newPath.data(), newPath.size()); return newPath; #else return path; #endif } }