2022-03-04 11:44:11 +01:00
|
|
|
#include <hex/helpers/fs.hpp>
|
|
|
|
|
2022-02-18 22:34:54 +01:00
|
|
|
#include <hex/api/content_registry.hpp>
|
2022-06-25 12:19:59 +02:00
|
|
|
#include <hex/helpers/fs_macos.hpp>
|
2022-03-04 11:36:37 +01:00
|
|
|
#include <hex/helpers/file.hpp>
|
2022-03-22 09:08:34 +01:00
|
|
|
#include <hex/helpers/intrinsics.hpp>
|
2022-07-06 11:29:24 +02:00
|
|
|
#include <hex/helpers/net.hpp>
|
2021-09-03 02:33:15 +02:00
|
|
|
|
2021-12-03 00:00:25 +01:00
|
|
|
#include <xdg.hpp>
|
|
|
|
|
2021-09-03 02:33:15 +02:00
|
|
|
#if defined(OS_WINDOWS)
|
|
|
|
#include <windows.h>
|
|
|
|
#include <shlobj.h>
|
|
|
|
#elif defined(OS_LINUX)
|
|
|
|
#include <xdg.hpp>
|
2021-09-10 15:26:19 +02:00
|
|
|
#include <linux/limits.h>
|
2021-09-03 02:33:15 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
#include <filesystem>
|
|
|
|
|
2022-03-04 11:36:37 +01:00
|
|
|
namespace hex::fs {
|
2021-09-03 02:33:15 +02:00
|
|
|
|
2022-03-22 09:34:26 +01:00
|
|
|
std::optional<std::fs::path> getExecutablePath() {
|
2022-01-24 20:53:17 +01:00
|
|
|
#if defined(OS_WINDOWS)
|
2022-06-29 21:34:17 +02:00
|
|
|
std::wstring exePath(MAX_PATH, '\0');
|
|
|
|
if (GetModuleFileNameW(nullptr, exePath.data(), exePath.length()) == 0)
|
2022-03-22 09:34:26 +01:00
|
|
|
return std::nullopt;
|
2022-01-24 20:53:17 +01:00
|
|
|
|
|
|
|
return exePath;
|
|
|
|
#elif defined(OS_LINUX)
|
|
|
|
std::string exePath(PATH_MAX, '\0');
|
2022-03-22 09:34:26 +01:00
|
|
|
if (readlink("/proc/self/exe", exePath.data(), PATH_MAX) < 0)
|
|
|
|
return std::nullopt;
|
2022-01-24 20:53:17 +01:00
|
|
|
|
|
|
|
return exePath;
|
|
|
|
#elif defined(OS_MACOS)
|
2022-06-25 12:19:59 +02:00
|
|
|
std::string result;
|
|
|
|
|
|
|
|
{
|
|
|
|
auto string = getMacExecutableDirectoryPath();
|
|
|
|
result = string;
|
|
|
|
macFree(string);
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
2022-01-24 20:53:17 +01:00
|
|
|
#else
|
2022-03-22 09:34:26 +01:00
|
|
|
return std::nullopt;
|
2022-01-24 20:53:17 +01:00
|
|
|
#endif
|
2021-10-31 16:28:10 +01:00
|
|
|
}
|
|
|
|
|
2022-03-04 11:36:37 +01:00
|
|
|
|
2022-03-04 20:52:39 +01:00
|
|
|
bool isPathWritable(const std::fs::path &path) {
|
2022-03-04 11:36:37 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2022-03-27 00:01:28 +01:00
|
|
|
bool openFileBrowser(DialogMode mode, const std::vector<nfdfilteritem_t> &validExtensions, const std::function<void(std::fs::path)> &callback, const std::string &defaultPath) {
|
2022-03-04 11:36:37 +01:00
|
|
|
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:
|
2022-03-22 09:08:34 +01:00
|
|
|
hex::unreachable();
|
2022-03-04 11:36:37 +01:00
|
|
|
}
|
|
|
|
|
2022-07-06 11:29:24 +02:00
|
|
|
std::fs::path path;
|
|
|
|
#if defined(OS_LINUX)
|
|
|
|
// xdg-desktop-portal, which is the file picker backend used on Linux, returns all paths with URI encoding.
|
|
|
|
// This is a bit ugly and will most likely be fixed sometime in the future but until then, we'll just use
|
|
|
|
// curl to decode the URI string into a valid file path string
|
|
|
|
path = Net().decode(outPath);
|
|
|
|
#else
|
|
|
|
path = reinterpret_cast<char8_t*>(outPath);
|
|
|
|
#endif
|
|
|
|
|
2022-03-04 11:36:37 +01:00
|
|
|
if (result == NFD_OKAY) {
|
2022-07-06 11:29:24 +02:00
|
|
|
callback(path);
|
2022-03-04 11:36:37 +01:00
|
|
|
NFD::FreePath(outPath);
|
|
|
|
}
|
|
|
|
|
|
|
|
NFD::Quit();
|
|
|
|
|
|
|
|
return result == NFD_OKAY;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
std::vector<std::fs::path> getDefaultPaths(ImHexPath path, bool listNonExisting) {
|
|
|
|
std::vector<std::fs::path> result;
|
2022-02-18 22:34:54 +01:00
|
|
|
const auto exePath = getExecutablePath();
|
2022-07-02 17:53:13 +02:00
|
|
|
auto userDirs = ImHexApi::System::getAdditionalFolderPaths();
|
2022-02-18 22:34:54 +01:00
|
|
|
|
2022-03-27 00:01:28 +01:00
|
|
|
[[maybe_unused]]
|
2022-02-18 22:34:54 +01:00
|
|
|
auto addUserDirs = [&userDirs](auto &paths) {
|
|
|
|
std::transform(userDirs.begin(), userDirs.end(), std::back_inserter(paths), [](auto &item) {
|
|
|
|
return std::move(item);
|
|
|
|
});
|
|
|
|
};
|
2021-12-15 00:21:34 +01:00
|
|
|
|
2022-01-24 20:53:17 +01:00
|
|
|
#if defined(OS_WINDOWS)
|
2022-03-04 11:36:37 +01:00
|
|
|
std::fs::path appDataDir;
|
2022-01-24 20:53:17 +01:00
|
|
|
{
|
2022-06-29 21:34:17 +02:00
|
|
|
PWSTR wAppDataPath = nullptr;
|
2022-01-24 20:53:17 +01:00
|
|
|
if (!SUCCEEDED(SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, nullptr, &wAppDataPath)))
|
|
|
|
throw std::runtime_error("Failed to get APPDATA folder path");
|
|
|
|
|
2022-06-29 21:34:17 +02:00
|
|
|
appDataDir = std::wstring(wAppDataPath);
|
2022-01-24 20:53:17 +01:00
|
|
|
CoTaskMemFree(wAppDataPath);
|
|
|
|
}
|
|
|
|
|
2022-03-22 09:34:26 +01:00
|
|
|
std::vector<std::fs::path> paths = { appDataDir / "imhex" };
|
|
|
|
|
|
|
|
if (exePath)
|
|
|
|
paths.push_back(exePath->parent_path());
|
2022-01-24 20:53:17 +01:00
|
|
|
|
|
|
|
switch (path) {
|
2022-02-01 22:09:44 +01:00
|
|
|
case ImHexPath::Patterns:
|
2022-02-18 22:34:54 +01:00
|
|
|
addUserDirs(paths);
|
2022-02-01 22:09:44 +01:00
|
|
|
std::transform(paths.begin(), paths.end(), std::back_inserter(result), [](auto &path) {
|
2022-06-29 21:34:17 +02:00
|
|
|
return path / "patterns";
|
2022-02-01 22:09:44 +01:00
|
|
|
});
|
|
|
|
break;
|
|
|
|
case ImHexPath::PatternsInclude:
|
2022-02-18 22:34:54 +01:00
|
|
|
addUserDirs(paths);
|
2022-02-01 22:09:44 +01:00
|
|
|
std::transform(paths.begin(), paths.end(), std::back_inserter(result), [](auto &path) {
|
2022-06-29 21:34:17 +02:00
|
|
|
return path / "includes";
|
2022-02-01 22:09:44 +01:00
|
|
|
});
|
|
|
|
break;
|
|
|
|
case ImHexPath::Magic:
|
2022-02-18 22:34:54 +01:00
|
|
|
addUserDirs(paths);
|
2022-02-01 22:09:44 +01:00
|
|
|
std::transform(paths.begin(), paths.end(), std::back_inserter(result), [](auto &path) {
|
2022-06-29 21:34:17 +02:00
|
|
|
return path / "magic";
|
2022-02-01 22:09:44 +01:00
|
|
|
});
|
|
|
|
break;
|
|
|
|
case ImHexPath::Python:
|
2022-02-18 22:34:54 +01:00
|
|
|
addUserDirs(paths);
|
2022-02-01 22:09:44 +01:00
|
|
|
std::transform(paths.begin(), paths.end(), std::back_inserter(result), [](auto &path) {
|
2022-06-29 21:34:17 +02:00
|
|
|
return path / "python";
|
2022-02-01 22:09:44 +01:00
|
|
|
});
|
|
|
|
break;
|
|
|
|
case ImHexPath::Plugins:
|
|
|
|
std::transform(paths.begin(), paths.end(), std::back_inserter(result), [](auto &path) {
|
2022-06-29 21:34:17 +02:00
|
|
|
return path / "plugins";
|
2022-02-01 22:09:44 +01:00
|
|
|
});
|
|
|
|
break;
|
|
|
|
case ImHexPath::Yara:
|
2022-02-18 22:34:54 +01:00
|
|
|
addUserDirs(paths);
|
2022-02-01 22:09:44 +01:00
|
|
|
std::transform(paths.begin(), paths.end(), std::back_inserter(result), [](auto &path) {
|
2022-06-29 21:34:17 +02:00
|
|
|
return path / "yara";
|
2022-02-01 22:09:44 +01:00
|
|
|
});
|
|
|
|
break;
|
|
|
|
case ImHexPath::Config:
|
2022-06-29 21:34:17 +02:00
|
|
|
return { appDataDir / "imhex" / "config" };
|
2022-02-01 22:09:44 +01:00
|
|
|
case ImHexPath::Resources:
|
|
|
|
std::transform(paths.begin(), paths.end(), std::back_inserter(result), [](auto &path) {
|
2022-06-29 21:34:17 +02:00
|
|
|
return path / "resources";
|
2022-02-01 22:09:44 +01:00
|
|
|
});
|
|
|
|
break;
|
|
|
|
case ImHexPath::Constants:
|
2022-02-18 22:34:54 +01:00
|
|
|
addUserDirs(paths);
|
2022-02-01 22:09:44 +01:00
|
|
|
std::transform(paths.begin(), paths.end(), std::back_inserter(result), [](auto &path) {
|
2022-06-29 21:34:17 +02:00
|
|
|
return path / "constants";
|
2022-02-01 22:09:44 +01:00
|
|
|
});
|
|
|
|
break;
|
|
|
|
case ImHexPath::Encodings:
|
2022-02-18 22:34:54 +01:00
|
|
|
addUserDirs(paths);
|
2022-02-01 22:09:44 +01:00
|
|
|
std::transform(paths.begin(), paths.end(), std::back_inserter(result), [](auto &path) {
|
2022-06-29 21:34:17 +02:00
|
|
|
return path / "encodings";
|
2022-02-01 22:09:44 +01:00
|
|
|
});
|
|
|
|
break;
|
|
|
|
case ImHexPath::Logs:
|
|
|
|
std::transform(paths.begin(), paths.end(), std::back_inserter(result), [](auto &path) {
|
2022-06-29 21:34:17 +02:00
|
|
|
return path / "logs";
|
2022-02-01 22:09:44 +01:00
|
|
|
});
|
|
|
|
break;
|
|
|
|
default:
|
2022-03-22 09:08:34 +01:00
|
|
|
hex::unreachable();
|
2022-01-24 20:53:17 +01:00
|
|
|
}
|
|
|
|
#elif defined(OS_MACOS)
|
|
|
|
// Get path to special directories
|
2022-06-25 12:19:59 +02:00
|
|
|
std::string applicationSupportDir;
|
|
|
|
{
|
|
|
|
auto string = getMacApplicationSupportDirectoryPath();
|
|
|
|
applicationSupportDir = string;
|
|
|
|
macFree(string);
|
|
|
|
}
|
|
|
|
const std::fs::path applicationSupportDirPath(applicationSupportDir);
|
2022-01-24 20:53:17 +01:00
|
|
|
|
2022-06-25 12:19:59 +02:00
|
|
|
std::vector<std::fs::path> paths = { applicationSupportDirPath };
|
2022-03-22 09:34:26 +01:00
|
|
|
|
2022-06-17 10:42:54 +02:00
|
|
|
if (exePath.has_value())
|
2022-06-17 10:31:28 +02:00
|
|
|
paths.push_back(exePath.value());
|
2022-01-24 20:53:17 +01:00
|
|
|
|
|
|
|
switch (path) {
|
2022-02-01 22:09:44 +01:00
|
|
|
case ImHexPath::Patterns:
|
2022-07-05 00:00:00 +02:00
|
|
|
result.push_back(applicationSupportDirPath / "patterns");
|
2022-02-01 22:09:44 +01:00
|
|
|
break;
|
|
|
|
case ImHexPath::PatternsInclude:
|
2022-07-05 00:00:00 +02:00
|
|
|
result.push_back(applicationSupportDirPath / "includes");
|
2022-02-01 22:09:44 +01:00
|
|
|
break;
|
|
|
|
case ImHexPath::Magic:
|
2022-07-05 00:00:00 +02:00
|
|
|
result.push_back(applicationSupportDirPath / "magic");
|
2022-02-01 22:09:44 +01:00
|
|
|
break;
|
|
|
|
case ImHexPath::Python:
|
2022-07-05 00:00:00 +02:00
|
|
|
result.push_back(applicationSupportDirPath / "python");
|
2022-02-01 22:09:44 +01:00
|
|
|
break;
|
|
|
|
case ImHexPath::Plugins:
|
|
|
|
std::transform(paths.begin(), paths.end(), std::back_inserter(result), [](auto &path) {
|
2022-07-05 00:00:00 +02:00
|
|
|
return path / "plugins";
|
2022-02-01 22:09:44 +01:00
|
|
|
});
|
|
|
|
break;
|
|
|
|
case ImHexPath::Yara:
|
2022-07-05 00:00:00 +02:00
|
|
|
result.push_back(applicationSupportDirPath / "yara");
|
2022-02-01 22:09:44 +01:00
|
|
|
break;
|
|
|
|
case ImHexPath::Config:
|
2022-07-05 00:00:00 +02:00
|
|
|
result.push_back(applicationSupportDirPath / "config");
|
2022-02-01 22:09:44 +01:00
|
|
|
break;
|
|
|
|
case ImHexPath::Resources:
|
2022-07-05 00:00:00 +02:00
|
|
|
result.push_back(applicationSupportDirPath / "resources");
|
2022-02-01 22:09:44 +01:00
|
|
|
break;
|
|
|
|
case ImHexPath::Constants:
|
2022-07-05 00:00:00 +02:00
|
|
|
result.push_back(applicationSupportDirPath / "constants");
|
2022-02-01 22:09:44 +01:00
|
|
|
break;
|
|
|
|
case ImHexPath::Encodings:
|
2022-07-05 00:00:00 +02:00
|
|
|
result.push_back(applicationSupportDirPath / "encodings");
|
2022-02-01 22:09:44 +01:00
|
|
|
break;
|
|
|
|
case ImHexPath::Logs:
|
2022-07-05 00:00:00 +02:00
|
|
|
result.push_back(applicationSupportDirPath / "logs");
|
2022-02-01 22:09:44 +01:00
|
|
|
break;
|
|
|
|
default:
|
2022-03-22 09:08:34 +01:00
|
|
|
hex::unreachable();
|
2022-01-24 20:53:17 +01:00
|
|
|
}
|
|
|
|
#else
|
2022-03-04 11:36:37 +01:00
|
|
|
std::vector<std::fs::path> configDirs = xdg::ConfigDirs();
|
|
|
|
std::vector<std::fs::path> dataDirs = xdg::DataDirs();
|
2022-01-24 20:53:17 +01:00
|
|
|
|
2022-02-26 16:25:15 +01:00
|
|
|
configDirs.push_back(xdg::ConfigHomeDir());
|
|
|
|
dataDirs.push_back(xdg::DataHomeDir());
|
2022-01-24 20:53:17 +01:00
|
|
|
|
|
|
|
for (auto &dir : dataDirs)
|
|
|
|
dir = dir / "imhex";
|
|
|
|
|
2022-03-22 09:34:26 +01:00
|
|
|
if (exePath && !exePath->empty())
|
|
|
|
dataDirs.push_back(exePath->parent_path());
|
2022-02-26 16:25:15 +01:00
|
|
|
|
2022-01-24 20:53:17 +01:00
|
|
|
switch (path) {
|
2022-02-01 22:09:44 +01:00
|
|
|
case ImHexPath::Patterns:
|
2022-02-18 22:34:54 +01:00
|
|
|
addUserDirs(dataDirs);
|
2022-07-05 00:00:00 +02:00
|
|
|
std::transform(dataDirs.begin(), dataDirs.end(), std::back_inserter(result), [](auto p) { return p / "patterns"; });
|
2022-02-01 22:09:44 +01:00
|
|
|
break;
|
|
|
|
case ImHexPath::PatternsInclude:
|
2022-02-18 22:34:54 +01:00
|
|
|
addUserDirs(dataDirs);
|
2022-07-05 00:00:00 +02:00
|
|
|
std::transform(dataDirs.begin(), dataDirs.end(), std::back_inserter(result), [](auto p) { return p / "includes"; });
|
2022-02-01 22:09:44 +01:00
|
|
|
break;
|
|
|
|
case ImHexPath::Magic:
|
2022-02-18 22:34:54 +01:00
|
|
|
addUserDirs(dataDirs);
|
2022-07-05 00:00:00 +02:00
|
|
|
std::transform(dataDirs.begin(), dataDirs.end(), std::back_inserter(result), [](auto p) { return p / "magic"; });
|
2022-02-01 22:09:44 +01:00
|
|
|
break;
|
|
|
|
case ImHexPath::Python:
|
2022-02-18 22:34:54 +01:00
|
|
|
addUserDirs(dataDirs);
|
2022-07-05 00:00:00 +02:00
|
|
|
std::transform(dataDirs.begin(), dataDirs.end(), std::back_inserter(result), [](auto p) { return p; });
|
2022-02-01 22:09:44 +01:00
|
|
|
break;
|
|
|
|
case ImHexPath::Plugins:
|
2022-07-05 00:00:00 +02:00
|
|
|
std::transform(dataDirs.begin(), dataDirs.end(), std::back_inserter(result), [](auto p) { return p / "plugins"; });
|
2022-02-01 22:09:44 +01:00
|
|
|
break;
|
|
|
|
case ImHexPath::Yara:
|
2022-02-18 22:34:54 +01:00
|
|
|
addUserDirs(dataDirs);
|
2022-07-05 00:00:00 +02:00
|
|
|
std::transform(dataDirs.begin(), dataDirs.end(), std::back_inserter(result), [](auto p) { return p / "yara"; });
|
2022-02-01 22:09:44 +01:00
|
|
|
break;
|
|
|
|
case ImHexPath::Config:
|
2022-07-05 00:00:00 +02:00
|
|
|
std::transform(configDirs.begin(), configDirs.end(), std::back_inserter(result), [](auto p) { return p / "imhex"; });
|
2022-02-01 22:09:44 +01:00
|
|
|
break;
|
|
|
|
case ImHexPath::Resources:
|
2022-07-05 00:00:00 +02:00
|
|
|
std::transform(dataDirs.begin(), dataDirs.end(), std::back_inserter(result), [](auto p) { return p / "resources"; });
|
2022-02-01 22:09:44 +01:00
|
|
|
break;
|
|
|
|
case ImHexPath::Constants:
|
2022-02-18 22:34:54 +01:00
|
|
|
addUserDirs(dataDirs);
|
2022-07-05 00:00:00 +02:00
|
|
|
std::transform(dataDirs.begin(), dataDirs.end(), std::back_inserter(result), [](auto p) { return p / "constants"; });
|
2022-02-01 22:09:44 +01:00
|
|
|
break;
|
|
|
|
case ImHexPath::Encodings:
|
2022-02-18 22:34:54 +01:00
|
|
|
addUserDirs(dataDirs);
|
2022-07-05 00:00:00 +02:00
|
|
|
std::transform(dataDirs.begin(), dataDirs.end(), std::back_inserter(result), [](auto p) { return p / "encodings"; });
|
2022-02-01 22:09:44 +01:00
|
|
|
break;
|
|
|
|
case ImHexPath::Logs:
|
2022-07-05 00:00:00 +02:00
|
|
|
std::transform(dataDirs.begin(), dataDirs.end(), std::back_inserter(result), [](auto p) { return p / "logs"; });
|
2022-02-01 22:09:44 +01:00
|
|
|
break;
|
|
|
|
default:
|
2022-03-22 09:08:34 +01:00
|
|
|
hex::unreachable();
|
2022-01-24 20:53:17 +01:00
|
|
|
}
|
2022-02-21 22:47:56 +01:00
|
|
|
|
2022-01-24 20:53:17 +01:00
|
|
|
#endif
|
2021-12-15 00:21:34 +01:00
|
|
|
|
2022-01-12 09:02:03 +01:00
|
|
|
if (!listNonExisting) {
|
2022-01-24 20:53:17 +01:00
|
|
|
result.erase(std::remove_if(result.begin(), result.end(), [](const auto &path) {
|
2022-03-04 11:36:37 +01:00
|
|
|
return !fs::isDirectory(path);
|
2022-02-01 22:09:44 +01:00
|
|
|
}),
|
|
|
|
result.end());
|
2022-01-12 09:02:03 +01:00
|
|
|
}
|
2021-12-15 00:21:34 +01:00
|
|
|
|
|
|
|
return result;
|
2021-09-03 02:33:15 +02:00
|
|
|
}
|
|
|
|
|
2022-06-30 19:39:06 +02:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-02-18 22:34:54 +01:00
|
|
|
}
|