mirror of https://github.com/WerWolv/ImHex
293 lines
9.0 KiB
C++
293 lines
9.0 KiB
C++
#if !defined(OS_WEB)
|
|
|
|
#include "content/providers/command_provider.hpp"
|
|
|
|
#include <chrono>
|
|
|
|
#include <imgui.h>
|
|
#include <hex/ui/imgui_imhex_extensions.h>
|
|
|
|
#include <hex/helpers/fmt.hpp>
|
|
#include <hex/api/localization_manager.hpp>
|
|
#include <hex/helpers/logger.hpp>
|
|
|
|
#include <nlohmann/json.hpp>
|
|
#include <wolv/utils/charconv.hpp>
|
|
|
|
#if defined(OS_WINDOWS)
|
|
#include <windows.h>
|
|
#else
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <fcntl.h>
|
|
#endif
|
|
|
|
namespace hex::plugin::builtin {
|
|
|
|
using namespace std::chrono_literals;
|
|
|
|
CommandProvider::CommandProvider() { }
|
|
|
|
bool CommandProvider::isAvailable() const {
|
|
return m_open;
|
|
}
|
|
|
|
bool CommandProvider::isReadable() const {
|
|
return true;
|
|
}
|
|
|
|
bool CommandProvider::isWritable() const {
|
|
return !m_writeCommand.empty();
|
|
}
|
|
|
|
bool CommandProvider::isResizable() const {
|
|
return !m_resizeCommand.empty();
|
|
}
|
|
|
|
bool CommandProvider::isSavable() const {
|
|
return !m_saveCommand.empty();
|
|
}
|
|
|
|
static std::vector<u8> executeCommand(const std::string &command, std::span<const u8> stdinData = {}) {
|
|
std::vector<u8> output;
|
|
|
|
#if defined(_WIN32)
|
|
|
|
HANDLE hStdinRead = nullptr, hStdinWrite = nullptr;
|
|
HANDLE hStdoutRead = nullptr, hStdoutWrite = nullptr;
|
|
|
|
SECURITY_ATTRIBUTES sa{};
|
|
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|
sa.bInheritHandle = TRUE;
|
|
|
|
if (!CreatePipe(&hStdoutRead, &hStdoutWrite, &sa, 0)) {
|
|
log::error("CreatePipe(stdout) failed");
|
|
return {};
|
|
}
|
|
|
|
if (!SetHandleInformation(hStdoutRead, HANDLE_FLAG_INHERIT, 0)) {
|
|
log::error("SetHandleInformation(stdout) failed");
|
|
CloseHandle(hStdoutRead); CloseHandle(hStdoutWrite);
|
|
return {};
|
|
}
|
|
|
|
if (!CreatePipe(&hStdinRead, &hStdinWrite, &sa, 0)) {
|
|
log::error("CreatePipe(stdin) failed");
|
|
CloseHandle(hStdoutRead); CloseHandle(hStdoutWrite);
|
|
return {};
|
|
}
|
|
|
|
if (!SetHandleInformation(hStdinWrite, HANDLE_FLAG_INHERIT, 0)) {
|
|
log::error("SetHandleInformation(stdin) failed");
|
|
CloseHandle(hStdoutRead); CloseHandle(hStdoutWrite);
|
|
CloseHandle(hStdinRead); CloseHandle(hStdinWrite);
|
|
return {};
|
|
}
|
|
|
|
STARTUPINFOW si{};
|
|
si.cb = sizeof(si);
|
|
si.hStdOutput = si.hStdError = hStdoutWrite;
|
|
si.hStdInput = hStdinRead;
|
|
si.dwFlags = STARTF_USESTDHANDLES;
|
|
|
|
PROCESS_INFORMATION pi{};
|
|
|
|
// UTF-16 conversion
|
|
auto wcmd = wolv::util::utf8ToWstring(command);
|
|
|
|
if (!CreateProcessW(
|
|
nullptr, wcmd->data(),
|
|
nullptr, nullptr,
|
|
TRUE,
|
|
0,
|
|
nullptr, nullptr,
|
|
&si, &pi
|
|
)) {
|
|
log::error("CreateProcessW failed");
|
|
CloseHandle(hStdoutRead); CloseHandle(hStdoutWrite);
|
|
CloseHandle(hStdinRead); CloseHandle(hStdinWrite);
|
|
return {};
|
|
}
|
|
|
|
CloseHandle(hStdoutWrite);
|
|
CloseHandle(hStdinRead);
|
|
|
|
// Write stdin
|
|
if (!stdinData.empty()) {
|
|
DWORD written = 0;
|
|
WriteFile(hStdinWrite, stdinData.data(),
|
|
(DWORD)stdinData.size(), &written, nullptr);
|
|
}
|
|
CloseHandle(hStdinWrite);
|
|
|
|
// Read stdout
|
|
u8 buffer[4096];
|
|
DWORD bytesRead = 0;
|
|
|
|
while (ReadFile(hStdoutRead, buffer, sizeof(buffer), &bytesRead, nullptr) && bytesRead > 0) {
|
|
output.insert(output.end(), buffer, buffer + bytesRead);
|
|
}
|
|
|
|
CloseHandle(hStdoutRead);
|
|
|
|
WaitForSingleObject(pi.hProcess, INFINITE);
|
|
CloseHandle(pi.hProcess);
|
|
CloseHandle(pi.hThread);
|
|
|
|
return output;
|
|
|
|
#else
|
|
|
|
int stdinPipe[2];
|
|
int stdoutPipe[2];
|
|
|
|
if (pipe(stdinPipe) != 0 || pipe(stdoutPipe) != 0) {
|
|
log::error("pipe() failed");
|
|
return {};
|
|
}
|
|
|
|
pid_t pid = fork();
|
|
if (pid < 0) {
|
|
log::error("fork() failed");
|
|
return {};
|
|
}
|
|
|
|
if (pid == 0) {
|
|
// Child
|
|
dup2(stdinPipe[0], STDIN_FILENO);
|
|
dup2(stdoutPipe[1], STDOUT_FILENO);
|
|
dup2(stdoutPipe[1], STDERR_FILENO);
|
|
|
|
close(stdinPipe[1]);
|
|
close(stdoutPipe[0]);
|
|
|
|
execl("/bin/sh", "sh", "-c", command.c_str(), (char*)nullptr);
|
|
|
|
_exit(127);
|
|
}
|
|
|
|
// Parent
|
|
close(stdinPipe[0]);
|
|
close(stdoutPipe[1]);
|
|
|
|
if (!stdinData.empty()) {
|
|
std::ignore = write(stdinPipe[1], stdinData.data(), stdinData.size());
|
|
}
|
|
close(stdinPipe[1]);
|
|
|
|
u8 buffer[4096];
|
|
while (true) {
|
|
ssize_t n = read(stdoutPipe[0], buffer, sizeof(buffer));
|
|
if (n <= 0) break;
|
|
output.insert(output.end(), buffer, buffer + n);
|
|
}
|
|
|
|
close(stdoutPipe[0]);
|
|
waitpid(pid, nullptr, 0);
|
|
|
|
return output;
|
|
|
|
#endif
|
|
}
|
|
|
|
static std::string executeCommandString(const std::string &command) {
|
|
auto output = executeCommand(command);
|
|
return std::string(output.begin(), output.end());
|
|
}
|
|
|
|
void CommandProvider::readFromSource(u64 offset, void *buffer, size_t size) {
|
|
auto output = executeCommand(
|
|
fmt::format(fmt::runtime(m_readCommand),
|
|
fmt::arg("address", offset),
|
|
fmt::arg("size", size)
|
|
)
|
|
);
|
|
|
|
if (output.size() < size) {
|
|
std::memcpy(buffer, output.data(), output.size());
|
|
std::memset(static_cast<u8*>(buffer) + output.size(), 0, size - output.size());
|
|
} else {
|
|
std::memcpy(buffer, output.data(), size);
|
|
}
|
|
}
|
|
|
|
void CommandProvider::writeToSource(u64 offset, const void *buffer, size_t size) {
|
|
if (m_writeCommand.empty())
|
|
return;
|
|
|
|
std::ignore = executeCommand(
|
|
fmt::format(fmt::runtime(m_writeCommand),
|
|
fmt::arg("address", offset),
|
|
fmt::arg("size", size)
|
|
),
|
|
{ static_cast<const u8*>(buffer), size }
|
|
);
|
|
}
|
|
|
|
void CommandProvider::save() {
|
|
Provider::save();
|
|
std::ignore = executeCommand(m_saveCommand);
|
|
}
|
|
|
|
u64 CommandProvider::getSourceSize() const {
|
|
if (m_sizeCommand.empty())
|
|
return std::numeric_limits<u32>::max();
|
|
|
|
const auto output = executeCommandString(m_sizeCommand);
|
|
return wolv::util::from_chars<u64>(output).value_or(0);
|
|
}
|
|
|
|
std::string CommandProvider::getName() const {
|
|
return fmt::format("hex.builtin.provider.command.name"_lang, m_name);
|
|
}
|
|
|
|
bool CommandProvider::open() {
|
|
m_open = true;
|
|
return true;
|
|
}
|
|
|
|
void CommandProvider::close() {
|
|
|
|
}
|
|
|
|
bool CommandProvider::drawLoadInterface() {
|
|
ImGui::InputText("hex.builtin.provider.command.load.name"_lang, m_name);
|
|
ImGui::Separator();
|
|
ImGui::NewLine();
|
|
|
|
ImGui::InputText("hex.builtin.provider.command.load.read_command"_lang, m_readCommand);
|
|
ImGui::InputTextWithHint("hex.builtin.provider.command.load.write_command"_lang, "hex.builtin.provider.command.optional"_lang, m_writeCommand);
|
|
ImGui::InputTextWithHint("hex.builtin.provider.command.load.size_command"_lang, "hex.builtin.provider.command.optional"_lang, m_sizeCommand);
|
|
ImGui::InputTextWithHint("hex.builtin.provider.command.load.resize_command"_lang, "hex.builtin.provider.command.optional"_lang, m_resizeCommand);
|
|
ImGui::InputTextWithHint("hex.builtin.provider.command.load.save_command"_lang, "hex.builtin.provider.command.optional"_lang, m_saveCommand);
|
|
ImGuiExt::HelpHover("hex.builtin.provider.command.load.hint"_lang, ICON_VS_INFO);
|
|
|
|
return !m_name.empty() && !m_readCommand.empty();
|
|
}
|
|
|
|
void CommandProvider::loadSettings(const nlohmann::json &settings) {
|
|
Provider::loadSettings(settings);
|
|
|
|
m_readCommand = settings.value("read", "");
|
|
m_writeCommand = settings.value("write", "");
|
|
m_resizeCommand = settings.value("resize", "");
|
|
m_sizeCommand = settings.value("size", "");
|
|
m_saveCommand = settings.value("save", "");
|
|
m_name = settings.value("name", "");
|
|
}
|
|
|
|
nlohmann::json CommandProvider::storeSettings(nlohmann::json settings) const {
|
|
settings["read"] = m_readCommand;
|
|
settings["write"] = m_writeCommand;
|
|
settings["resize"] = m_resizeCommand;
|
|
settings["size"] = m_sizeCommand;
|
|
settings["save"] = m_saveCommand;
|
|
settings["name"] = m_name;
|
|
|
|
return Provider::storeSettings(settings);
|
|
}
|
|
|
|
}
|
|
|
|
#endif |