mirror of https://github.com/WerWolv/ImHex.git
tools: Added Anonfiles uploader
This commit is contained in:
parent
c56159da89
commit
3138d2c4a2
|
@ -1,6 +1,9 @@
|
|||
#include <hex/plugin.hpp>
|
||||
|
||||
#include <hex/helpers/net.hpp>
|
||||
|
||||
#include <regex>
|
||||
#include <chrono>
|
||||
|
||||
#include <llvm/Demangle/Demangle.h>
|
||||
#include "math_evaluator.hpp"
|
||||
|
@ -9,6 +12,8 @@ namespace hex::plugin::builtin {
|
|||
|
||||
namespace {
|
||||
|
||||
using namespace std::literals::chrono_literals;
|
||||
|
||||
void drawDemangler() {
|
||||
static std::vector<char> mangledBuffer(0xF'FFFF, 0x00);
|
||||
static std::string demangledName;
|
||||
|
@ -399,6 +404,98 @@ namespace hex::plugin::builtin {
|
|||
|
||||
}
|
||||
|
||||
void drawFileUploader() {
|
||||
struct UploadedFile {
|
||||
std::string fileName, link, size;
|
||||
};
|
||||
|
||||
static hex::Net net;
|
||||
static std::future<Response<std::string>> uploadProcess;
|
||||
static std::filesystem::path currFile;
|
||||
static std::vector<UploadedFile> links;
|
||||
|
||||
bool uploading = uploadProcess.valid() && uploadProcess.wait_for(0s) != std::future_status::ready;
|
||||
|
||||
ImGui::Header("hex.builtin.tools.file_uploader.control"_lang, true);
|
||||
if (!uploading) {
|
||||
if (ImGui::Button("hex.builtin.tools.file_uploader.upload"_lang)) {
|
||||
hex::openFileBrowser("hex.builtin.tools.file_uploader.done"_lang, DialogMode::Open, { }, [&](auto path){
|
||||
uploadProcess = net.uploadFile("https://api.anonfiles.com/upload", path);
|
||||
currFile = path;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (ImGui::Button("hex.common.cancel"_lang)) {
|
||||
net.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::ProgressBar(net.getProgress(), ImVec2(0, 0), uploading ? nullptr : "Done!");
|
||||
|
||||
ImGui::Header("hex.builtin.tools.file_uploader.recent"_lang);
|
||||
|
||||
if (ImGui::BeginTable("##links", 3, ImGuiTableFlags_ScrollY | ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg, ImVec2(0, 400))) {
|
||||
ImGui::TableSetupScrollFreeze(0, 1);
|
||||
ImGui::TableSetupColumn("hex.common.file"_lang);
|
||||
ImGui::TableSetupColumn("hex.common.link"_lang);
|
||||
ImGui::TableSetupColumn("hex.common.size"_lang);
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
ImGuiListClipper clipper;
|
||||
clipper.Begin(links.size());
|
||||
|
||||
while (clipper.Step()) {
|
||||
for (s32 i = clipper.DisplayEnd - 1; i >= clipper.DisplayStart; i--) {
|
||||
auto &[fileName, link, size] = links[i];
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(fileName.c_str());
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Hyperlink(link.c_str())) {
|
||||
if (ImGui::GetMergedKeyModFlags() == ImGuiKeyModFlags_Ctrl)
|
||||
hex::openWebpage(link);
|
||||
else
|
||||
ImGui::SetClipboardText(link.c_str());
|
||||
}
|
||||
|
||||
ImGui::InfoTooltip("hex.builtin.tools.file_uploader.tooltip"_lang);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(size.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
clipper.End();
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
if (uploadProcess.valid() && uploadProcess.wait_for(0s) == std::future_status::ready) {
|
||||
auto response = uploadProcess.get();
|
||||
if (response.code == 200) {
|
||||
try {
|
||||
auto json = nlohmann::json::parse(response.body);
|
||||
links.push_back({
|
||||
currFile.filename().string(),
|
||||
json["data"]["file"]["url"]["short"],
|
||||
json["data"]["file"]["metadata"]["size"]["readable"]
|
||||
});
|
||||
} catch (...) {
|
||||
View::showErrorPopup("hex.builtin.tools.file_uploader.invalid_response"_lang);
|
||||
}
|
||||
} else if (response.code == 0) {
|
||||
// Canceled by user, no action needed
|
||||
} else View::showErrorPopup(hex::format("hex.builtin.tools.file_uploader.error"_lang, response.code));
|
||||
|
||||
uploadProcess = { };
|
||||
currFile.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void registerToolEntries() {
|
||||
ContentRegistry::Tools::add("hex.builtin.tools.demangler", drawDemangler);
|
||||
ContentRegistry::Tools::add("hex.builtin.tools.ascii_table", drawASCIITable);
|
||||
|
@ -407,6 +504,7 @@ namespace hex::plugin::builtin {
|
|||
ContentRegistry::Tools::add("hex.builtin.tools.calc", drawMathEvaluator);
|
||||
ContentRegistry::Tools::add("hex.builtin.tools.base_converter", drawBaseConverter);
|
||||
ContentRegistry::Tools::add("hex.builtin.tools.permissions", drawPermissionsCalculator);
|
||||
ContentRegistry::Tools::add("hex.builtin.tools.file_uploader", drawFileUploader);
|
||||
}
|
||||
|
||||
}
|
|
@ -60,7 +60,7 @@ namespace hex::plugin::builtin {
|
|||
// Save file as
|
||||
ImGui::Disabled([&provider] {
|
||||
if (ImGui::ToolBarButton(ICON_VS_SAVE_AS, ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarBlue), buttonSize))
|
||||
View::openFileBrowser("hex.view.hexeditor.save_as"_lang, View::DialogMode::Save, { }, [&provider](auto path) {
|
||||
hex::openFileBrowser("hex.view.hexeditor.save_as"_lang, DialogMode::Save, { }, [&provider](auto path) {
|
||||
provider->saveAs(path);
|
||||
});
|
||||
}, provider == nullptr || !provider->isSavable());
|
||||
|
|
|
@ -76,6 +76,8 @@ namespace hex::plugin::builtin {
|
|||
{ "hex.common.set", "Setzen" },
|
||||
{ "hex.common.close", "Schliessen" },
|
||||
{ "hex.common.dont_show_again", "Nicht mehr anzeigen" },
|
||||
{ "hex.common.link", "Link" },
|
||||
{ "hex.common.file", "Datei" },
|
||||
|
||||
{ "hex.view.bookmarks.name", "Lesezeichen" },
|
||||
{ "hex.view.bookmarks.default_title", "Lesezeichen [0x{0:X} - 0x{1:X}]" },
|
||||
|
@ -538,6 +540,14 @@ namespace hex::plugin::builtin {
|
|||
{ "hex.builtin.tools.permissions.setuid_error", "User benötigt execute Rechte, damit setuid bit gilt!" },
|
||||
{ "hex.builtin.tools.permissions.setgid_error", "Group benötigt execute Rechte, damit setgid bit gilt!" },
|
||||
{ "hex.builtin.tools.permissions.sticky_error", "Other benötigt execute Rechte, damit sticky bit gilt!" },
|
||||
{ "hex.builtin.tools.file_uploader", "File Uploader" },
|
||||
{ "hex.builtin.tools.file_uploader.control", "Einstellungen" },
|
||||
{ "hex.builtin.tools.file_uploader.upload", "Upload" },
|
||||
{ "hex.builtin.tools.file_uploader.done", "Fertig!" },
|
||||
{ "hex.builtin.tools.file_uploader.recent", "Letzte Uploads" },
|
||||
{ "hex.builtin.tools.file_uploader.tooltip", "Klicken zum Kopieren\nCTRL + Klicken zum öffnen" },
|
||||
{ "hex.builtin.tools.file_uploader.invalid_response", "Ungültige Antwort von Anonfiles!"_lang },
|
||||
{ "hex.builtin.tools.file_uploader.error", "Dateiupload fehlgeschlagen\n\nError Code: {0}"_lang },
|
||||
|
||||
{ "hex.builtin.setting.imhex", "ImHex" },
|
||||
{ "hex.builtin.setting.imhex.recent_files", "Kürzlich geöffnete Dateien" },
|
||||
|
|
|
@ -76,6 +76,8 @@ namespace hex::plugin::builtin {
|
|||
{ "hex.common.set", "Set" },
|
||||
{ "hex.common.close", "Close" },
|
||||
{ "hex.common.dont_show_again", "Don't show again" },
|
||||
{ "hex.common.link", "Link" },
|
||||
{ "hex.common.file", "File" },
|
||||
|
||||
{ "hex.view.bookmarks.name", "Bookmarks" },
|
||||
{ "hex.view.bookmarks.default_title", "Bookmark [0x{0:X} - 0x{1:X}]" },
|
||||
|
@ -538,6 +540,15 @@ namespace hex::plugin::builtin {
|
|||
{ "hex.builtin.tools.permissions.setuid_error", "User must have execute rights for setuid bit to apply!" },
|
||||
{ "hex.builtin.tools.permissions.setgid_error", "Group must have execute rights for setgid bit to apply!" },
|
||||
{ "hex.builtin.tools.permissions.sticky_error", "Other must have execute rights for sticky bit to apply!" },
|
||||
{ "hex.builtin.tools.file_uploader", "File Uploader" },
|
||||
{ "hex.builtin.tools.file_uploader.control", "Control" },
|
||||
{ "hex.builtin.tools.file_uploader.upload", "Upload" },
|
||||
{ "hex.builtin.tools.file_uploader.done", "Done!" },
|
||||
{ "hex.builtin.tools.file_uploader.recent", "Recent Uploads" },
|
||||
{ "hex.builtin.tools.file_uploader.tooltip", "Click to copy\nCTRL + Click to open" },
|
||||
{ "hex.builtin.tools.file_uploader.invalid_response", "Invalid response from Anonfiles!" },
|
||||
{ "hex.builtin.tools.file_uploader.error", "Failed to upload file!\n\nError Code: {0}" },
|
||||
|
||||
|
||||
{ "hex.builtin.setting.imhex", "ImHex" },
|
||||
{ "hex.builtin.setting.imhex.recent_files", "Recent Files" },
|
||||
|
|
|
@ -75,6 +75,8 @@ namespace hex::plugin::builtin {
|
|||
{ "hex.common.set", "Imposta" },
|
||||
//{ "hex.common.close", "Close" },
|
||||
//{ "hex.common.dont_show_again", "Don't show again" },
|
||||
//{ "hex.common.link", "Link" },
|
||||
//{ "hex.common.file", "File" },
|
||||
|
||||
{ "hex.view.bookmarks.name", "Segnalibri" },
|
||||
{ "hex.view.bookmarks.default_title", "Segnalibro [0x{0:X} - 0x{1:X}]" },
|
||||
|
@ -537,6 +539,14 @@ namespace hex::plugin::builtin {
|
|||
//{ "hex.builtin.tools.permissions.setuid_error", "User must have execute rights for setuid bit to apply!" },
|
||||
//{ "hex.builtin.tools.permissions.setgid_error", "Group must have execute rights for setgid bit to apply!" },
|
||||
//{ "hex.builtin.tools.permissions.sticky_error", "Other must have execute rights for sticky bit to apply!" },
|
||||
//{ "hex.builtin.tools.file_uploader", "File Uploader" },
|
||||
//{ "hex.builtin.tools.file_uploader.control", "Control" },
|
||||
//{ "hex.builtin.tools.file_uploader.upload", "Upload" },
|
||||
//{ "hex.builtin.tools.file_uploader.done", "Done!" },
|
||||
//{ "hex.builtin.tools.file_uploader.recent", "Recent Uploads" },
|
||||
//{ "hex.builtin.tools.file_uploader.tooltip", "Click to copy\nCTRL + Click to open" },
|
||||
//{ "hex.builtin.tools.file_uploader.invalid_response", "Invalid response from Anonfiles!" },
|
||||
//{ "hex.builtin.tools.file_uploader.error", "Failed to upload file!\n\nError Code: {0}" },
|
||||
|
||||
{ "hex.builtin.setting.imhex", "ImHex" },
|
||||
{ "hex.builtin.setting.imhex.recent_files", "File recenti" },
|
||||
|
|
|
@ -2,17 +2,20 @@
|
|||
|
||||
#include <hex.hpp>
|
||||
#include <future>
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
#include <atomic>
|
||||
|
||||
#include <curl/curl.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include <curl/curl.h>
|
||||
|
||||
namespace hex {
|
||||
|
||||
template<typename T>
|
||||
struct Response {
|
||||
u32 code;
|
||||
T response;
|
||||
s32 code;
|
||||
T body;
|
||||
};
|
||||
|
||||
class Net {
|
||||
|
@ -20,11 +23,28 @@ namespace hex {
|
|||
Net();
|
||||
~Net();
|
||||
|
||||
Response<std::string> getString(std::string_view url);
|
||||
Response<nlohmann::json> getJson(std::string_view url);
|
||||
std::future<Response<std::string>> getString(const std::string &url);
|
||||
std::future<Response<nlohmann::json>> getJson(const std::string &url);
|
||||
|
||||
std::future<Response<std::string>> uploadFile(const std::string &url, const std::filesystem::path &filePath);
|
||||
|
||||
[[nodiscard]]
|
||||
float getProgress() const { return this->m_progress; }
|
||||
|
||||
void cancel() { this->m_shouldCancel = true; }
|
||||
|
||||
private:
|
||||
void setCommonSettings(std::string &response, const std::string &url, const std::map<std::string, std::string> &extraHeaders = { }, const std::string &body = { });
|
||||
std::optional<s32> execute();
|
||||
|
||||
friend int progressCallback(void *contents, curl_off_t dlTotal, curl_off_t dlNow, curl_off_t ulTotal, curl_off_t ulNow);
|
||||
private:
|
||||
CURL *m_ctx;
|
||||
struct curl_slist *m_headers = nullptr;
|
||||
|
||||
std::mutex m_transmissionActive;
|
||||
float m_progress = 0.0F;
|
||||
bool m_shouldCancel = false;
|
||||
};
|
||||
|
||||
}
|
|
@ -15,11 +15,13 @@
|
|||
#include <fmt/chrono.h>
|
||||
|
||||
#ifdef __MINGW32__
|
||||
#include <winsock.h>
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
#include <nfd.hpp>
|
||||
|
||||
#if defined(__APPLE__) || defined(__FreeBSD__)
|
||||
#define off64_t off_t
|
||||
#define fopen64 fopen
|
||||
|
@ -331,30 +333,87 @@ namespace hex {
|
|||
|
||||
std::vector<std::string> getPath(ImHexPath path);
|
||||
|
||||
#define SCOPE_GUARD ::hex::ScopeGuardOnExit() + [&]()
|
||||
#define ON_SCOPE_EXIT auto ANONYMOUS_VARIABLE(SCOPE_EXIT_) = SCOPE_GUARD
|
||||
template<class F>
|
||||
class ScopeGuard {
|
||||
private:
|
||||
F m_func;
|
||||
bool m_active;
|
||||
public:
|
||||
constexpr ScopeGuard(F func) : m_func(std::move(func)), m_active(true) { }
|
||||
~ScopeGuard() { if (this->m_active) { this->m_func(); } }
|
||||
void release() { this->m_active = false; }
|
||||
|
||||
ScopeGuard(ScopeGuard &&other) noexcept : m_func(std::move(other.m_func)), m_active(other.m_active) {
|
||||
other.cancel();
|
||||
}
|
||||
|
||||
ScopeGuard& operator=(ScopeGuard &&) = delete;
|
||||
enum class DialogMode {
|
||||
Open,
|
||||
Save,
|
||||
Folder
|
||||
};
|
||||
|
||||
enum class ScopeGuardOnExit { };
|
||||
void openFileBrowser(std::string_view title, DialogMode mode, const std::vector<nfdfilteritem_t> &validExtensions, const std::function<void(std::string)> &callback);
|
||||
|
||||
namespace scope_guard {
|
||||
|
||||
#define SCOPE_GUARD ::hex::scope_guard::ScopeGuardOnExit() + [&]()
|
||||
#define ON_SCOPE_EXIT auto ANONYMOUS_VARIABLE(SCOPE_EXIT_) = SCOPE_GUARD
|
||||
|
||||
template<class F>
|
||||
class ScopeGuard {
|
||||
private:
|
||||
F m_func;
|
||||
bool m_active;
|
||||
public:
|
||||
constexpr ScopeGuard(F func) : m_func(std::move(func)), m_active(true) { }
|
||||
~ScopeGuard() { if (this->m_active) { this->m_func(); } }
|
||||
void release() { this->m_active = false; }
|
||||
|
||||
ScopeGuard(ScopeGuard &&other) noexcept : m_func(std::move(other.m_func)), m_active(other.m_active) {
|
||||
other.cancel();
|
||||
}
|
||||
|
||||
ScopeGuard& operator=(ScopeGuard &&) = delete;
|
||||
};
|
||||
|
||||
enum class ScopeGuardOnExit { };
|
||||
|
||||
template <typename F>
|
||||
constexpr ScopeGuard<F> operator+(ScopeGuardOnExit, F&& f) {
|
||||
return ScopeGuard<F>(std::forward<F>(f));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace first_time_exec {
|
||||
|
||||
#define FIRST_TIME static auto ANONYMOUS_VARIABLE(FIRST_TIME_) = ::hex::first_time_exec::FirstTimeExecutor() + [&]()
|
||||
|
||||
template<class F>
|
||||
class FirstTimeExecute {
|
||||
public:
|
||||
constexpr FirstTimeExecute(F func) { func(); }
|
||||
|
||||
FirstTimeExecute& operator=(FirstTimeExecute &&) = delete;
|
||||
};
|
||||
|
||||
enum class FirstTimeExecutor { };
|
||||
|
||||
template <typename F>
|
||||
constexpr FirstTimeExecute<F> operator+(FirstTimeExecutor, F&& f) {
|
||||
return FirstTimeExecute<F>(std::forward<F>(f));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace final_cleanup {
|
||||
|
||||
#define FINAL_CLEANUP static auto ANONYMOUS_VARIABLE(ON_EXIT_) = ::hex::final_cleanup::FinalCleanupExecutor() + [&]()
|
||||
|
||||
template<class F>
|
||||
class FinalCleanupExecute {
|
||||
F m_func;
|
||||
public:
|
||||
constexpr FinalCleanupExecute(F func) : m_func(func) { }
|
||||
constexpr ~FinalCleanupExecute() { this->m_func(); }
|
||||
|
||||
FinalCleanupExecute& operator=(FinalCleanupExecute &&) = delete;
|
||||
};
|
||||
|
||||
enum class FinalCleanupExecutor { };
|
||||
|
||||
template <typename F>
|
||||
constexpr FinalCleanupExecute<F> operator+(FinalCleanupExecutor, F&& f) {
|
||||
return FinalCleanupExecute<F>(std::forward<F>(f));
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
constexpr ScopeGuard<F> operator+(ScopeGuardOnExit, F&& f) {
|
||||
return ScopeGuard<F>(std::forward<F>(f));
|
||||
}
|
||||
|
||||
struct Region {
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
#include <fontawesome_font.h>
|
||||
#include <codicons_font.h>
|
||||
#include <nfd.hpp>
|
||||
|
||||
#include <hex/api/imhex_api.hpp>
|
||||
#include <hex/api/event.hpp>
|
||||
|
@ -32,13 +31,6 @@ namespace hex {
|
|||
virtual bool isAvailable();
|
||||
virtual bool shouldProcess() { return this->isAvailable() && this->getWindowOpenState(); }
|
||||
|
||||
enum class DialogMode {
|
||||
Open,
|
||||
Save,
|
||||
Folder
|
||||
};
|
||||
|
||||
static void openFileBrowser(std::string_view title, DialogMode mode, const std::vector<nfdfilteritem_t> &validExtensions, const std::function<void(std::string)> &callback);
|
||||
static void doLater(std::function<void()> &&function);
|
||||
static std::vector<std::function<void()>>& getDeferedCalls();
|
||||
|
||||
|
|
|
@ -3,10 +3,9 @@
|
|||
#include <hex/helpers/utils.hpp>
|
||||
|
||||
#include <filesystem>
|
||||
#include <cstdio>
|
||||
|
||||
#include <mbedtls/ssl.h>
|
||||
#include <mbedtls/x509.h>
|
||||
#include <mbedtls/x509_crt.h>
|
||||
#include <mbedtls/error.h>
|
||||
|
||||
#include <hex/resources.hpp>
|
||||
|
@ -14,21 +13,33 @@
|
|||
namespace hex {
|
||||
|
||||
Net::Net() {
|
||||
curl_global_sslset(CURLSSLBACKEND_MBEDTLS, nullptr, nullptr);
|
||||
curl_global_init(CURL_GLOBAL_ALL);
|
||||
FIRST_TIME {
|
||||
curl_global_sslset(CURLSSLBACKEND_MBEDTLS, nullptr, nullptr);
|
||||
curl_global_init(CURL_GLOBAL_ALL);
|
||||
};
|
||||
|
||||
FINAL_CLEANUP {
|
||||
curl_global_cleanup();
|
||||
};
|
||||
|
||||
this->m_ctx = curl_easy_init();
|
||||
}
|
||||
|
||||
Net::~Net() {
|
||||
curl_easy_cleanup(this->m_ctx);
|
||||
curl_global_cleanup();
|
||||
}
|
||||
|
||||
static size_t writeToString(void *contents, size_t size, size_t nmemb, void *userp){
|
||||
static_cast<std::string*>(userp)->append((char*)contents, size * nmemb);
|
||||
static size_t writeToString(void *contents, size_t size, size_t nmemb, void *userdata){
|
||||
static_cast<std::string*>(userdata)->append((char*)contents, size * nmemb);
|
||||
return size * nmemb;
|
||||
}
|
||||
|
||||
static size_t readFromFile(void *contents, size_t size, size_t nmemb, void *userdata) {
|
||||
FILE *file = static_cast<FILE*>(userdata);
|
||||
|
||||
return fread(contents, size, nmemb, file);
|
||||
}
|
||||
|
||||
static CURLcode sslCtxFunction(CURL *ctx, void *sslctx, void *userdata) {
|
||||
auto* cfg = static_cast<mbedtls_ssl_config*>(sslctx);
|
||||
|
||||
|
@ -41,9 +52,21 @@ namespace hex {
|
|||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static void setCommonSettings(CURL *ctx, std::string &response, std::string_view path, const std::map<std::string, std::string> &extraHeaders, const std::string &body) {
|
||||
struct curl_slist *headers = nullptr;
|
||||
headers = curl_slist_append(headers, "Cache-Control: no-cache");
|
||||
int progressCallback(void *contents, curl_off_t dlTotal, curl_off_t dlNow, curl_off_t ulTotal, curl_off_t ulNow) {
|
||||
auto &net = *static_cast<Net*>(contents);
|
||||
|
||||
if (dlTotal > 0)
|
||||
net.m_progress = float(dlNow) / dlTotal;
|
||||
else if (ulTotal > 0)
|
||||
net.m_progress = float(ulNow) / ulTotal;
|
||||
else
|
||||
net.m_progress = 0.0F;
|
||||
|
||||
return net.m_shouldCancel ? CURLE_ABORTED_BY_CALLBACK : CURLE_OK;
|
||||
}
|
||||
|
||||
void Net::setCommonSettings(std::string &response, const std::string &url, const std::map<std::string, std::string> &extraHeaders, const std::string &body) {
|
||||
this->m_headers = curl_slist_append(this->m_headers, "Cache-Control: no-cache");
|
||||
|
||||
if (!extraHeaders.empty())
|
||||
for (const auto &[key, value] : extraHeaders) {
|
||||
|
@ -51,65 +74,131 @@ namespace hex {
|
|||
entry += ": ";
|
||||
entry += value;
|
||||
|
||||
headers = curl_slist_append(headers, entry.c_str());
|
||||
this->m_headers = curl_slist_append(this->m_headers, entry.c_str());
|
||||
}
|
||||
|
||||
if (!body.empty())
|
||||
curl_easy_setopt(ctx, CURLOPT_POSTFIELDS, body.c_str());
|
||||
curl_easy_setopt(this->m_ctx, CURLOPT_POSTFIELDS, body.c_str());
|
||||
|
||||
curl_easy_setopt(ctx, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS);
|
||||
curl_easy_setopt(ctx, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2 | CURL_SSLVERSION_MAX_TLSv1_2);
|
||||
curl_easy_setopt(ctx, CURLOPT_URL, path.data());
|
||||
curl_easy_setopt(ctx, CURLOPT_FOLLOWLOCATION, 1L);
|
||||
curl_easy_setopt(ctx, CURLOPT_HTTPHEADER, headers);
|
||||
curl_easy_setopt(ctx, CURLOPT_USERAGENT, "ImHex/1.0");
|
||||
curl_easy_setopt(ctx, CURLOPT_DEFAULT_PROTOCOL, "https");
|
||||
curl_easy_setopt(ctx, CURLOPT_WRITEFUNCTION, writeToString);
|
||||
curl_easy_setopt(ctx, CURLOPT_SSL_VERIFYPEER, 1L);
|
||||
curl_easy_setopt(ctx, CURLOPT_SSL_VERIFYHOST, 2L);
|
||||
curl_easy_setopt(ctx, CURLOPT_CAINFO, nullptr);
|
||||
curl_easy_setopt(ctx, CURLOPT_CAPATH, nullptr);
|
||||
curl_easy_setopt(ctx, CURLOPT_SSLCERTTYPE, "PEM");
|
||||
curl_easy_setopt(ctx, CURLOPT_SSL_CTX_FUNCTION, sslCtxFunction);
|
||||
curl_easy_setopt(ctx, CURLOPT_WRITEDATA, &response);
|
||||
curl_easy_setopt(ctx, CURLOPT_TIMEOUT_MS, 2000L);
|
||||
curl_easy_setopt(ctx, CURLOPT_CONNECTTIMEOUT_MS, 2000L);
|
||||
curl_easy_setopt(ctx, CURLOPT_NOPROGRESS, 1L);
|
||||
curl_easy_setopt(ctx, CURLOPT_NOSIGNAL, 1L);
|
||||
curl_easy_setopt(this->m_ctx, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS);
|
||||
curl_easy_setopt(this->m_ctx, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
|
||||
curl_easy_setopt(this->m_ctx, CURLOPT_URL, url.c_str());
|
||||
curl_easy_setopt(this->m_ctx, CURLOPT_FOLLOWLOCATION, 1L);
|
||||
curl_easy_setopt(this->m_ctx, CURLOPT_HTTPHEADER, this->m_headers);
|
||||
curl_easy_setopt(this->m_ctx, CURLOPT_USERAGENT, "ImHex/1.0");
|
||||
curl_easy_setopt(this->m_ctx, CURLOPT_DEFAULT_PROTOCOL, "https");
|
||||
curl_easy_setopt(this->m_ctx, CURLOPT_WRITEFUNCTION, writeToString);
|
||||
curl_easy_setopt(this->m_ctx, CURLOPT_SSL_VERIFYPEER, 1L);
|
||||
curl_easy_setopt(this->m_ctx, CURLOPT_SSL_VERIFYHOST, 2L);
|
||||
curl_easy_setopt(this->m_ctx, CURLOPT_CAINFO, nullptr);
|
||||
curl_easy_setopt(this->m_ctx, CURLOPT_CAPATH, nullptr);
|
||||
curl_easy_setopt(this->m_ctx, CURLOPT_SSLCERTTYPE, "PEM");
|
||||
curl_easy_setopt(this->m_ctx, CURLOPT_SSL_CTX_FUNCTION, sslCtxFunction);
|
||||
curl_easy_setopt(this->m_ctx, CURLOPT_WRITEDATA, &response);
|
||||
curl_easy_setopt(this->m_ctx, CURLOPT_TIMEOUT_MS, 0L);
|
||||
curl_easy_setopt(this->m_ctx, CURLOPT_CONNECTTIMEOUT_MS, 2000L);
|
||||
curl_easy_setopt(this->m_ctx, CURLOPT_XFERINFODATA, this);
|
||||
curl_easy_setopt(this->m_ctx, CURLOPT_XFERINFOFUNCTION, progressCallback);
|
||||
curl_easy_setopt(this->m_ctx, CURLOPT_NOSIGNAL, 1L);
|
||||
curl_easy_setopt(this->m_ctx, CURLOPT_NOPROGRESS, 0L);
|
||||
}
|
||||
|
||||
Response<std::string> Net::getString(std::string_view url) {
|
||||
std::string response;
|
||||
|
||||
curl_easy_setopt(this->m_ctx, CURLOPT_CUSTOMREQUEST, "GET");
|
||||
setCommonSettings(this->m_ctx, response, url, {}, "");
|
||||
|
||||
std::optional<s32> Net::execute() {
|
||||
CURLcode result = curl_easy_perform(this->m_ctx);
|
||||
|
||||
u32 responseCode = 0;
|
||||
s32 responseCode = 0;
|
||||
curl_easy_getinfo(this->m_ctx, CURLINFO_RESPONSE_CODE, &responseCode);
|
||||
|
||||
curl_slist_free_all(this->m_headers);
|
||||
this->m_headers = nullptr;
|
||||
this->m_progress = 0.0F;
|
||||
this->m_shouldCancel = false;
|
||||
|
||||
if (result != CURLE_OK)
|
||||
return Response<std::string>{ responseCode, "" };
|
||||
return { };
|
||||
else
|
||||
return Response<std::string>{ responseCode, response };
|
||||
return responseCode;
|
||||
}
|
||||
|
||||
Response<nlohmann::json> Net::getJson(std::string_view url) {
|
||||
std::string response;
|
||||
std::future<Response<std::string>> Net::getString(const std::string &url) {
|
||||
this->m_transmissionActive.lock();
|
||||
|
||||
curl_easy_setopt(this->m_ctx, CURLOPT_CUSTOMREQUEST, "GET");
|
||||
setCommonSettings(this->m_ctx, response, url, {}, "");
|
||||
return std::async(std::launch::async, [=, this]{
|
||||
std::string response;
|
||||
|
||||
CURLcode result = curl_easy_perform(this->m_ctx);
|
||||
ON_SCOPE_EXIT { this->m_transmissionActive.unlock(); };
|
||||
|
||||
u32 responseCode = 0;
|
||||
curl_easy_getinfo(this->m_ctx, CURLINFO_RESPONSE_CODE, &responseCode);
|
||||
curl_easy_setopt(this->m_ctx, CURLOPT_CUSTOMREQUEST, "GET");
|
||||
setCommonSettings(response, url);
|
||||
|
||||
if (result != CURLE_OK)
|
||||
return Response<nlohmann::json>{ responseCode, { } };
|
||||
else
|
||||
return Response<nlohmann::json>{ responseCode, nlohmann::json::parse(response) };
|
||||
auto responseCode = execute();
|
||||
|
||||
return Response<std::string> { responseCode.value_or(0), response };
|
||||
});
|
||||
}
|
||||
|
||||
std::future<Response<nlohmann::json>> Net::getJson(const std::string &url) {
|
||||
this->m_transmissionActive.lock();
|
||||
|
||||
return std::async(std::launch::async, [=, this]{
|
||||
std::string response;
|
||||
|
||||
ON_SCOPE_EXIT { this->m_transmissionActive.unlock(); };
|
||||
|
||||
curl_easy_setopt(this->m_ctx, CURLOPT_CUSTOMREQUEST, "GET");
|
||||
setCommonSettings(response, url, {});
|
||||
|
||||
auto responseCode = execute();
|
||||
|
||||
return Response<nlohmann::json> { responseCode.value_or(0), nlohmann::json::parse(response) };
|
||||
});
|
||||
}
|
||||
|
||||
std::future<Response<std::string>> Net::uploadFile(const std::string &url, const std::filesystem::path &filePath) {
|
||||
this->m_transmissionActive.lock();
|
||||
|
||||
return std::async(std::launch::async, [=, this] {
|
||||
std::string response;
|
||||
|
||||
ON_SCOPE_EXIT { this->m_transmissionActive.unlock(); };
|
||||
|
||||
FILE *file = fopen(filePath.string().c_str(), "rb");
|
||||
if (file == nullptr)
|
||||
return Response<std::string> { 400, { } };
|
||||
|
||||
fseek(file, 0, SEEK_END);
|
||||
size_t fileSize = ftell(file);
|
||||
rewind(file);
|
||||
|
||||
curl_mime *mime = curl_mime_init(this->m_ctx);
|
||||
curl_mimepart *part = curl_mime_addpart(mime);
|
||||
|
||||
auto fileName = filePath.filename().string();
|
||||
curl_mime_data_cb(part, fileSize,
|
||||
[](char *buffer, size_t size, size_t nitems, void *arg) -> size_t {
|
||||
auto file = static_cast<FILE*>(arg);
|
||||
return fread(buffer, size, nitems, file);
|
||||
},
|
||||
[](void *arg, curl_off_t offset, int origin) -> int {
|
||||
auto file = static_cast<FILE*>(arg);
|
||||
fseek(file, offset, origin);
|
||||
return CURL_SEEKFUNC_OK;
|
||||
},
|
||||
[](void *arg) {
|
||||
auto file = static_cast<FILE*>(arg);
|
||||
fclose(file);
|
||||
}, file);
|
||||
curl_mime_filename(part, fileName.c_str());
|
||||
curl_mime_name(part, "file");
|
||||
|
||||
setCommonSettings(response, url);
|
||||
curl_easy_setopt(this->m_ctx, CURLOPT_MIMEPOST, mime);
|
||||
curl_easy_setopt(this->m_ctx, CURLOPT_CUSTOMREQUEST, "POST");
|
||||
|
||||
auto responseCode = execute();
|
||||
|
||||
return Response<std::string> { responseCode.value_or(0), response };
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -296,4 +296,30 @@ namespace hex {
|
|||
#endif
|
||||
}
|
||||
|
||||
void openFileBrowser(std::string_view title, DialogMode mode, const std::vector<nfdfilteritem_t> &validExtensions, const std::function<void(std::string)> &callback) {
|
||||
NFD::Init();
|
||||
|
||||
nfdchar_t *outPath;
|
||||
nfdresult_t result;
|
||||
switch (mode) {
|
||||
case DialogMode::Open:
|
||||
result = NFD::OpenDialog(outPath, validExtensions.data(), validExtensions.size(), nullptr);
|
||||
break;
|
||||
case DialogMode::Save:
|
||||
result = NFD::SaveDialog(outPath, validExtensions.data(), validExtensions.size(), nullptr);
|
||||
break;
|
||||
case DialogMode::Folder:
|
||||
result = NFD::PickFolder(outPath, nullptr);
|
||||
break;
|
||||
default: __builtin_unreachable();
|
||||
}
|
||||
|
||||
if (result == NFD_OKAY) {
|
||||
callback(outPath);
|
||||
NFD::FreePath(outPath);
|
||||
}
|
||||
|
||||
NFD::Quit();
|
||||
}
|
||||
|
||||
}
|
|
@ -24,32 +24,6 @@ namespace hex {
|
|||
return SharedData::deferredCalls;
|
||||
}
|
||||
|
||||
void View::openFileBrowser(std::string_view title, DialogMode mode, const std::vector<nfdfilteritem_t> &validExtensions, const std::function<void(std::string)> &callback) {
|
||||
NFD::Init();
|
||||
|
||||
nfdchar_t *outPath;
|
||||
nfdresult_t result;
|
||||
switch (mode) {
|
||||
case DialogMode::Open:
|
||||
result = NFD::OpenDialog(outPath, validExtensions.data(), validExtensions.size(), nullptr);
|
||||
break;
|
||||
case DialogMode::Save:
|
||||
result = NFD::SaveDialog(outPath, validExtensions.data(), validExtensions.size(), nullptr);
|
||||
break;
|
||||
case DialogMode::Folder:
|
||||
result = NFD::PickFolder(outPath, nullptr);
|
||||
break;
|
||||
default: __builtin_unreachable();
|
||||
}
|
||||
|
||||
if (result == NFD_OKAY) {
|
||||
callback(outPath);
|
||||
NFD::FreePath(outPath);
|
||||
}
|
||||
|
||||
NFD::Quit();
|
||||
}
|
||||
|
||||
void View::drawCommonInterfaces() {
|
||||
if (ImGui::BeginPopupModal("hex.common.error"_lang, nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::Text("%s", SharedData::errorPopupMessage.c_str());
|
||||
|
|
|
@ -31,15 +31,15 @@ namespace hex::init {
|
|||
static bool checkForUpdates() {
|
||||
hex::Net net;
|
||||
|
||||
auto releases = net.getJson("https://api.github.com/repos/WerWolv/ImHex/releases/latest");
|
||||
auto releases = net.getJson("https://api.github.com/repos/WerWolv/ImHex/releases/latest").get();
|
||||
if (releases.code != 200)
|
||||
return false;
|
||||
|
||||
if (!releases.response.contains("tag_name") || !releases.response["tag_name"].is_string())
|
||||
if (!releases.body.contains("tag_name") || !releases.body["tag_name"].is_string())
|
||||
return false;
|
||||
|
||||
auto currVersion = "v" + std::string(IMHEX_VERSION).substr(0, 5);
|
||||
auto latestVersion = releases.response["tag_name"].get<std::string_view>();
|
||||
auto latestVersion = releases.body["tag_name"].get<std::string_view>();
|
||||
|
||||
if (latestVersion != currVersion)
|
||||
getInitArguments().push_back({ "update-available", latestVersion.data() });
|
||||
|
@ -50,11 +50,11 @@ namespace hex::init {
|
|||
static bool downloadInformation() {
|
||||
hex::Net net;
|
||||
|
||||
auto tip = net.getString("https://api.werwolv.net/imhex/tip");
|
||||
auto tip = net.getString("https://api.werwolv.net/imhex/tip").get();
|
||||
if (tip.code != 200)
|
||||
return false;
|
||||
|
||||
getInitArguments().push_back({ "tip-of-the-day", tip.response });
|
||||
getInitArguments().push_back({ "tip-of-the-day", tip.body });
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -176,7 +176,7 @@ namespace hex {
|
|||
|
||||
EventManager::subscribe<RequestOpenWindow>(this, [this](std::string name) {
|
||||
if (name == "Create File") {
|
||||
View::openFileBrowser("hex.view.hexeditor.create_file"_lang, DialogMode::Save, { }, [this](auto path) {
|
||||
hex::openFileBrowser("hex.view.hexeditor.create_file"_lang, DialogMode::Save, { }, [this](auto path) {
|
||||
if (!this->createFile(path)) {
|
||||
View::showErrorPopup("hex.view.hexeditor.error.create"_lang);
|
||||
return;
|
||||
|
@ -185,12 +185,12 @@ namespace hex {
|
|||
this->getWindowOpenState() = true;
|
||||
});
|
||||
} else if (name == "Open File") {
|
||||
View::openFileBrowser("hex.view.hexeditor.open_file"_lang, DialogMode::Open, { }, [this](auto path) {
|
||||
hex::openFileBrowser("hex.view.hexeditor.open_file"_lang, DialogMode::Open, { }, [this](auto path) {
|
||||
this->openFile(path);
|
||||
this->getWindowOpenState() = true;
|
||||
});
|
||||
} else if (name == "Open Project") {
|
||||
View::openFileBrowser("hex.view.hexeditor.open_project"_lang, DialogMode::Open, { { "Project File", "hexproj" } }, [this](auto path) {
|
||||
hex::openFileBrowser("hex.view.hexeditor.open_project"_lang, DialogMode::Open, { { "Project File", "hexproj" } }, [this](auto path) {
|
||||
ProjectFile::load(path);
|
||||
EventManager::post<EventProjectFileLoad>();
|
||||
this->getWindowOpenState() = true;
|
||||
|
@ -271,7 +271,7 @@ namespace hex {
|
|||
}
|
||||
|
||||
static void saveAs() {
|
||||
View::openFileBrowser("hex.view.hexeditor.save_as"_lang, View::DialogMode::Save, { }, [](auto path) {
|
||||
hex::openFileBrowser("hex.view.hexeditor.save_as"_lang, DialogMode::Save, { }, [](auto path) {
|
||||
auto provider = SharedData::currentProvider;
|
||||
provider->saveAs(path);
|
||||
});
|
||||
|
@ -306,14 +306,14 @@ namespace hex {
|
|||
ImGui::InputText("##nolabel", this->m_loaderScriptScriptPath.data(), this->m_loaderScriptScriptPath.length(), ImGuiInputTextFlags_ReadOnly);
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("hex.view.hexeditor.script.script"_lang)) {
|
||||
View::openFileBrowser("hex.view.hexeditor.script.script.title"_lang, DialogMode::Open, { { "Python Script", "py" } }, [this](auto path) {
|
||||
hex::openFileBrowser("hex.view.hexeditor.script.script.title"_lang, DialogMode::Open, { { "Python Script", "py" } }, [this](auto path) {
|
||||
this->m_loaderScriptScriptPath = path;
|
||||
});
|
||||
}
|
||||
ImGui::InputText("##nolabel", this->m_loaderScriptFilePath.data(), this->m_loaderScriptFilePath.length(), ImGuiInputTextFlags_ReadOnly);
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("hex.view.hexeditor.script.file"_lang)) {
|
||||
View::openFileBrowser("hex.view.hexeditor.script.file.title"_lang, DialogMode::Open, { }, [this](auto path) {
|
||||
hex::openFileBrowser("hex.view.hexeditor.script.file.title"_lang, DialogMode::Open, { }, [this](auto path) {
|
||||
this->m_loaderScriptFilePath = path;
|
||||
});
|
||||
}
|
||||
|
@ -384,7 +384,7 @@ namespace hex {
|
|||
if (ImGui::BeginMenu("hex.menu.file"_lang)) {
|
||||
if (ImGui::MenuItem("hex.view.hexeditor.menu.file.open_file"_lang, "CTRL + O")) {
|
||||
|
||||
View::openFileBrowser("hex.view.hexeditor.open_file"_lang, DialogMode::Open, { }, [this](auto path) {
|
||||
hex::openFileBrowser("hex.view.hexeditor.open_file"_lang, DialogMode::Open, { }, [this](auto path) {
|
||||
EventManager::post<EventFileDropped>(path);
|
||||
});
|
||||
}
|
||||
|
@ -416,7 +416,7 @@ namespace hex {
|
|||
ImGui::Separator();
|
||||
|
||||
if (ImGui::MenuItem("hex.view.hexeditor.menu.file.open_project"_lang, "")) {
|
||||
View::openFileBrowser("hex.view.hexeditor.menu.file.open_project"_lang, DialogMode::Open, { { "Project File", "hexproj" } }, [this](auto path) {
|
||||
hex::openFileBrowser("hex.view.hexeditor.menu.file.open_project"_lang, DialogMode::Open, { { "Project File", "hexproj" } }, [this](auto path) {
|
||||
ProjectFile::load(path);
|
||||
EventManager::post<EventProjectFileLoad>();
|
||||
});
|
||||
|
@ -424,7 +424,7 @@ namespace hex {
|
|||
|
||||
if (ImGui::MenuItem("hex.view.hexeditor.menu.file.save_project"_lang, "", false, provider != nullptr && provider->isWritable())) {
|
||||
if (ProjectFile::getProjectFilePath() == "") {
|
||||
View::openFileBrowser("hex.view.hexeditor.save_project"_lang, DialogMode::Save, { { "Project File", "hexproj" } }, [](auto path) {
|
||||
hex::openFileBrowser("hex.view.hexeditor.save_project"_lang, DialogMode::Save, { { "Project File", "hexproj" } }, [](auto path) {
|
||||
if (path.ends_with(".hexproj")) {
|
||||
ProjectFile::store(path);
|
||||
}
|
||||
|
@ -438,7 +438,7 @@ namespace hex {
|
|||
}
|
||||
|
||||
if (ImGui::MenuItem("hex.view.hexeditor.menu.file.load_encoding_file"_lang)) {
|
||||
View::openFileBrowser("hex.view.hexeditor.load_enconding_file"_lang, DialogMode::Open, { }, [this](auto path) {
|
||||
hex::openFileBrowser("hex.view.hexeditor.load_enconding_file"_lang, DialogMode::Open, { }, [this](auto path) {
|
||||
this->m_currEncodingFile = EncodingFile(EncodingFile::Type::Thingy, path);
|
||||
});
|
||||
}
|
||||
|
@ -448,7 +448,7 @@ namespace hex {
|
|||
if (ImGui::BeginMenu("hex.view.hexeditor.menu.file.import"_lang)) {
|
||||
if (ImGui::MenuItem("hex.view.hexeditor.menu.file.import.base64"_lang)) {
|
||||
|
||||
View::openFileBrowser("hex.view.hexeditor.menu.file.import.base64"_lang, DialogMode::Open, { }, [this](auto path) {
|
||||
hex::openFileBrowser("hex.view.hexeditor.menu.file.import.base64"_lang, DialogMode::Open, { }, [this](auto path) {
|
||||
std::vector<u8> base64;
|
||||
this->loadFromFile(path, base64);
|
||||
|
||||
|
@ -468,7 +468,7 @@ namespace hex {
|
|||
|
||||
if (ImGui::MenuItem("hex.view.hexeditor.menu.file.import.ips"_lang)) {
|
||||
|
||||
View::openFileBrowser("hex.view.hexeditor.open_file"_lang, DialogMode::Open, { }, [this](auto path) {
|
||||
hex::openFileBrowser("hex.view.hexeditor.open_file"_lang, DialogMode::Open, { }, [this](auto path) {
|
||||
auto patchData = hex::readFile(path);
|
||||
auto patch = hex::loadIPSPatch(patchData);
|
||||
|
||||
|
@ -482,7 +482,7 @@ namespace hex {
|
|||
}
|
||||
|
||||
if (ImGui::MenuItem("hex.view.hexeditor.menu.file.import.ips32"_lang)) {
|
||||
View::openFileBrowser("hex.view.hexeditor.open_file"_lang, DialogMode::Open, { }, [this](auto path) {
|
||||
hex::openFileBrowser("hex.view.hexeditor.open_file"_lang, DialogMode::Open, { }, [this](auto path) {
|
||||
auto patchData = hex::readFile(path);
|
||||
auto patch = hex::loadIPS32Patch(patchData);
|
||||
|
||||
|
@ -512,7 +512,7 @@ namespace hex {
|
|||
}
|
||||
|
||||
this->m_dataToSave = generateIPSPatch(patches);
|
||||
View::openFileBrowser("hex.view.hexeditor.menu.file.export.title"_lang, DialogMode::Save, { }, [this](auto path) {
|
||||
hex::openFileBrowser("hex.view.hexeditor.menu.file.export.title"_lang, DialogMode::Save, { }, [this](auto path) {
|
||||
this->saveToFile(path, this->m_dataToSave);
|
||||
});
|
||||
}
|
||||
|
@ -525,7 +525,7 @@ namespace hex {
|
|||
}
|
||||
|
||||
this->m_dataToSave = generateIPS32Patch(patches);
|
||||
View::openFileBrowser("hex.view.hexeditor.menu.file.export.title"_lang, DialogMode::Save, { }, [this](auto path) {
|
||||
hex::openFileBrowser("hex.view.hexeditor.menu.file.export.title"_lang, DialogMode::Save, { }, [this](auto path) {
|
||||
this->saveToFile(path, this->m_dataToSave);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -213,7 +213,7 @@ namespace hex {
|
|||
void ViewPattern::drawMenu() {
|
||||
if (ImGui::BeginMenu("hex.menu.file"_lang)) {
|
||||
if (ImGui::MenuItem("hex.view.pattern.menu.file.load_pattern"_lang)) {
|
||||
View::openFileBrowser("hex.view.pattern.open_pattern"_lang, DialogMode::Open, { { "Pattern File", "hexpat" } }, [this](auto path) {
|
||||
hex::openFileBrowser("hex.view.pattern.open_pattern"_lang, DialogMode::Open, { { "Pattern File", "hexpat" } }, [this](auto path) {
|
||||
this->loadPatternFile(path);
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue