2020-11-10 20:31:04 +00:00
|
|
|
#include "views/view_pattern.hpp"
|
|
|
|
|
2020-11-29 23:03:12 +00:00
|
|
|
#include "helpers/project_file_handler.hpp"
|
2021-01-13 16:28:27 +00:00
|
|
|
#include <hex/helpers/utils.hpp>
|
2021-01-22 17:01:39 +00:00
|
|
|
#include <hex/lang/preprocessor.hpp>
|
2020-11-10 20:31:04 +00:00
|
|
|
|
2020-11-21 13:39:16 +00:00
|
|
|
#include <magic.h>
|
|
|
|
|
2020-11-10 20:31:04 +00:00
|
|
|
namespace hex {
|
|
|
|
|
2020-11-20 17:24:59 +00:00
|
|
|
static const TextEditor::LanguageDefinition& PatternLanguage() {
|
|
|
|
static bool initialized = false;
|
|
|
|
static TextEditor::LanguageDefinition langDef;
|
|
|
|
if (!initialized) {
|
|
|
|
static const char* const keywords[] = {
|
2021-01-07 16:34:50 +00:00
|
|
|
"using", "struct", "union", "enum", "bitfield", "be", "le", "if", "else", "false", "true"
|
2020-11-20 17:24:59 +00:00
|
|
|
};
|
|
|
|
for (auto& k : keywords)
|
|
|
|
langDef.mKeywords.insert(k);
|
|
|
|
|
2020-11-20 21:21:59 +00:00
|
|
|
static std::pair<const char* const, size_t> builtInTypes[] = {
|
|
|
|
{ "u8", 1 }, { "u16", 2 }, { "u32", 4 }, { "u64", 8 }, { "u128", 16 },
|
|
|
|
{ "s8", 1 }, { "s16", 2 }, { "s32", 4 }, { "s64", 8 }, { "s128", 16 },
|
2021-01-07 16:34:50 +00:00
|
|
|
{ "float", 4 }, { "double", 8 }, { "char", 1 }, { "bool", 1 }, { "padding", 1 }
|
2020-11-20 17:24:59 +00:00
|
|
|
};
|
2021-01-09 22:48:42 +00:00
|
|
|
|
2020-11-20 21:21:59 +00:00
|
|
|
for (const auto &[name, size] : builtInTypes) {
|
2020-11-20 17:24:59 +00:00
|
|
|
TextEditor::Identifier id;
|
2020-11-20 21:21:59 +00:00
|
|
|
id.mDeclaration = std::to_string(size);
|
|
|
|
id.mDeclaration += size == 1 ? " byte" : " bytes";
|
|
|
|
langDef.mIdentifiers.insert(std::make_pair(std::string(name), id));
|
2020-11-20 17:24:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
langDef.mTokenize = [](const char * inBegin, const char * inEnd, const char *& outBegin, const char *& outEnd, TextEditor::PaletteIndex & paletteIndex) -> bool {
|
|
|
|
paletteIndex = TextEditor::PaletteIndex::Max;
|
|
|
|
|
|
|
|
while (inBegin < inEnd && isascii(*inBegin) && isblank(*inBegin))
|
|
|
|
inBegin++;
|
|
|
|
|
|
|
|
if (inBegin == inEnd) {
|
|
|
|
outBegin = inEnd;
|
|
|
|
outEnd = inEnd;
|
|
|
|
paletteIndex = TextEditor::PaletteIndex::Default;
|
|
|
|
}
|
2021-01-22 17:01:39 +00:00
|
|
|
else if (TokenizeCStyleIdentifier(inBegin, inEnd, outBegin, outEnd)) {
|
|
|
|
if (SharedData::patternLanguageFunctions.contains(std::string(outBegin, outEnd - outBegin)))
|
|
|
|
paletteIndex = TextEditor::PaletteIndex::LineNumber;
|
|
|
|
else
|
|
|
|
paletteIndex = TextEditor::PaletteIndex::Identifier;
|
|
|
|
}
|
2020-11-20 17:24:59 +00:00
|
|
|
else if (TokenizeCStyleNumber(inBegin, inEnd, outBegin, outEnd))
|
|
|
|
paletteIndex = TextEditor::PaletteIndex::Number;
|
2020-11-27 13:18:28 +00:00
|
|
|
else if (TokenizeCStyleCharacterLiteral(inBegin, inEnd, outBegin, outEnd))
|
|
|
|
paletteIndex = TextEditor::PaletteIndex::CharLiteral;
|
2021-01-09 22:48:42 +00:00
|
|
|
else if (TokenizeCStyleString(inBegin, inEnd, outBegin, outEnd))
|
|
|
|
paletteIndex = TextEditor::PaletteIndex::String;
|
2020-11-20 17:24:59 +00:00
|
|
|
|
|
|
|
return paletteIndex != TextEditor::PaletteIndex::Max;
|
|
|
|
};
|
|
|
|
|
|
|
|
langDef.mCommentStart = "/*";
|
|
|
|
langDef.mCommentEnd = "*/";
|
|
|
|
langDef.mSingleLineComment = "//";
|
|
|
|
|
|
|
|
langDef.mCaseSensitive = true;
|
|
|
|
langDef.mAutoIndentation = true;
|
|
|
|
langDef.mPreprocChar = '#';
|
|
|
|
|
|
|
|
langDef.mName = "Pattern Language";
|
|
|
|
|
|
|
|
initialized = true;
|
|
|
|
}
|
|
|
|
return langDef;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-12-27 14:39:06 +00:00
|
|
|
ViewPattern::ViewPattern(std::vector<lang::PatternData*> &patternData) : View("Pattern"), m_patternData(patternData) {
|
2021-01-23 13:00:09 +00:00
|
|
|
this->m_patternLanguageRuntime = new lang::PatternLanguage();
|
2020-11-13 13:35:52 +00:00
|
|
|
|
2020-11-20 17:24:59 +00:00
|
|
|
this->m_textEditor.SetLanguageDefinition(PatternLanguage());
|
|
|
|
this->m_textEditor.SetShowWhitespaces(false);
|
2020-11-21 13:39:16 +00:00
|
|
|
|
2021-01-21 09:53:12 +00:00
|
|
|
View::subscribeEvent(Events::ProjectFileStore, [this](auto) {
|
2020-11-29 23:03:12 +00:00
|
|
|
ProjectFile::setPattern(this->m_textEditor.GetText());
|
|
|
|
});
|
|
|
|
|
2021-01-21 09:53:12 +00:00
|
|
|
View::subscribeEvent(Events::ProjectFileLoad, [this](auto) {
|
2020-11-29 23:03:12 +00:00
|
|
|
this->m_textEditor.SetText(ProjectFile::getPattern());
|
|
|
|
this->parsePattern(this->m_textEditor.GetText().data());
|
|
|
|
});
|
|
|
|
|
2021-01-21 09:53:12 +00:00
|
|
|
View::subscribeEvent(Events::AppendPatternLanguageCode, [this](auto userData) {
|
|
|
|
auto code = std::any_cast<const char*>(userData);
|
2020-12-01 15:41:38 +00:00
|
|
|
|
|
|
|
this->m_textEditor.InsertText("\n");
|
|
|
|
this->m_textEditor.InsertText(code);
|
|
|
|
});
|
|
|
|
|
2021-01-21 09:53:12 +00:00
|
|
|
View::subscribeEvent(Events::FileLoaded, [this](auto) {
|
2020-12-05 21:30:09 +00:00
|
|
|
if (this->m_textEditor.GetText().find_first_not_of(" \f\n\r\t\v") != std::string::npos)
|
2020-11-29 23:03:12 +00:00
|
|
|
return;
|
2020-11-21 13:39:16 +00:00
|
|
|
|
2020-11-29 23:03:12 +00:00
|
|
|
lang::Preprocessor preprocessor;
|
2020-11-21 13:39:16 +00:00
|
|
|
std::string magicFiles;
|
|
|
|
|
|
|
|
std::error_code error;
|
|
|
|
for (const auto &entry : std::filesystem::directory_iterator("magic", error)) {
|
|
|
|
if (entry.is_regular_file() && entry.path().extension() == ".mgc")
|
|
|
|
magicFiles += entry.path().string() + MAGIC_PATH_SEPARATOR;
|
|
|
|
}
|
|
|
|
|
2020-12-05 21:30:09 +00:00
|
|
|
if (error)
|
|
|
|
return;
|
|
|
|
|
2021-01-12 22:28:41 +00:00
|
|
|
auto provider = SharedData::currentProvider;
|
2020-12-27 14:39:06 +00:00
|
|
|
|
|
|
|
if (provider == nullptr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
std::vector<u8> buffer(std::min(provider->getSize(), size_t(0xFFFF)), 0x00);
|
|
|
|
provider->read(0, buffer.data(), buffer.size());
|
2020-11-21 13:39:16 +00:00
|
|
|
|
|
|
|
std::string mimeType;
|
|
|
|
|
|
|
|
magic_t cookie = magic_open(MAGIC_MIME_TYPE);
|
|
|
|
if (magic_load(cookie, magicFiles.c_str()) != -1)
|
|
|
|
mimeType = magic_buffer(cookie, buffer.data(), buffer.size());
|
|
|
|
|
|
|
|
magic_close(cookie);
|
|
|
|
|
|
|
|
bool foundCorrectType = false;
|
|
|
|
preprocessor.addPragmaHandler("MIME", [&mimeType, &foundCorrectType](std::string value) {
|
|
|
|
if (value == mimeType) {
|
|
|
|
foundCorrectType = true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return !std::all_of(value.begin(), value.end(), isspace) && !value.ends_with('\n') && !value.ends_with('\r');
|
|
|
|
});
|
2020-11-27 20:20:23 +00:00
|
|
|
preprocessor.addDefaultPragmaHandlers();
|
2020-11-21 13:39:16 +00:00
|
|
|
|
|
|
|
|
2020-11-22 23:12:33 +00:00
|
|
|
std::error_code errorCode;
|
|
|
|
for (auto &entry : std::filesystem::directory_iterator("patterns", errorCode)) {
|
2020-11-21 13:39:16 +00:00
|
|
|
if (!entry.is_regular_file())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
FILE *file = fopen(entry.path().string().c_str(), "r");
|
|
|
|
|
|
|
|
if (file == nullptr)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
fseek(file, 0, SEEK_END);
|
|
|
|
size_t size = ftell(file);
|
|
|
|
rewind(file);
|
|
|
|
|
|
|
|
std::vector<char> buffer( size + 1, 0x00);
|
|
|
|
fread(buffer.data(), 1, size, file);
|
|
|
|
fclose(file);
|
|
|
|
|
|
|
|
preprocessor.preprocess(buffer.data());
|
|
|
|
|
|
|
|
if (foundCorrectType) {
|
|
|
|
this->m_possiblePatternFile = entry.path();
|
2020-12-05 21:30:09 +00:00
|
|
|
View::doLater([] { ImGui::OpenPopup("Accept Pattern"); });
|
2020-11-21 13:39:16 +00:00
|
|
|
ImGui::SetNextWindowSize(ImVec2(200, 100));
|
2020-11-22 22:06:17 +00:00
|
|
|
break;
|
2020-11-21 13:39:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2021-01-11 19:32:12 +00:00
|
|
|
|
|
|
|
/* Settings */
|
|
|
|
{
|
|
|
|
|
|
|
|
constexpr auto SettingsCategoryInterface = "Interface";
|
|
|
|
|
|
|
|
constexpr auto SettingColorTheme = "Color theme";
|
|
|
|
|
|
|
|
ContentRegistry::Settings::add(SettingsCategoryInterface, SettingColorTheme, 0, [](nlohmann::json &setting) {
|
|
|
|
static int selection = setting;
|
|
|
|
if (ImGui::Combo("##nolabel", &selection, "Dark\0Light\0Classic\0")) {
|
|
|
|
setting = selection;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
});
|
|
|
|
|
2021-01-21 09:53:12 +00:00
|
|
|
View::subscribeEvent(Events::SettingsChanged, [this](auto) {
|
2021-01-11 19:32:12 +00:00
|
|
|
int theme = ContentRegistry::Settings::getSettingsData()[SettingsCategoryInterface][SettingColorTheme];
|
|
|
|
|
|
|
|
switch (theme) {
|
|
|
|
default:
|
|
|
|
case 0: /* Dark theme */
|
|
|
|
ImGui::StyleColorsDark();
|
|
|
|
this->m_textEditor.SetPalette(TextEditor::GetDarkPalette());
|
|
|
|
break;
|
|
|
|
case 1: /* Light theme */
|
|
|
|
ImGui::StyleColorsLight();
|
|
|
|
this->m_textEditor.SetPalette(TextEditor::GetLightPalette());
|
|
|
|
break;
|
|
|
|
case 2: /* Classic theme */
|
|
|
|
ImGui::StyleColorsClassic();
|
|
|
|
this->m_textEditor.SetPalette(TextEditor::GetRetroBluePalette());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
2020-11-10 20:31:04 +00:00
|
|
|
}
|
2020-11-20 17:24:59 +00:00
|
|
|
|
2020-11-10 20:31:04 +00:00
|
|
|
ViewPattern::~ViewPattern() {
|
2020-11-29 23:03:12 +00:00
|
|
|
View::unsubscribeEvent(Events::ProjectFileStore);
|
|
|
|
View::unsubscribeEvent(Events::ProjectFileLoad);
|
2020-11-10 20:31:04 +00:00
|
|
|
}
|
|
|
|
|
2020-12-22 17:10:01 +00:00
|
|
|
void ViewPattern::drawMenu() {
|
2020-11-10 20:31:04 +00:00
|
|
|
if (ImGui::BeginMenu("File")) {
|
|
|
|
if (ImGui::MenuItem("Load pattern...")) {
|
2020-11-17 14:38:24 +00:00
|
|
|
View::doLater([]{ ImGui::OpenPopup("Open Hex Pattern"); });
|
2020-11-10 20:31:04 +00:00
|
|
|
}
|
|
|
|
ImGui::EndMenu();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-22 17:10:01 +00:00
|
|
|
void ViewPattern::drawContent() {
|
2020-11-23 22:57:19 +00:00
|
|
|
if (ImGui::Begin("Pattern", &this->getWindowOpenState(), ImGuiWindowFlags_None | ImGuiWindowFlags_NoCollapse)) {
|
2021-01-12 22:28:41 +00:00
|
|
|
auto provider = SharedData::currentProvider;
|
2020-12-27 14:39:06 +00:00
|
|
|
|
|
|
|
if (provider != nullptr && provider->isAvailable()) {
|
2021-01-09 20:45:21 +00:00
|
|
|
auto textEditorSize = ImGui::GetContentRegionAvail();
|
|
|
|
textEditorSize.y *= 4.0/5.0;
|
|
|
|
this->m_textEditor.Render("Pattern", textEditorSize, true);
|
|
|
|
|
|
|
|
auto consoleSize = ImGui::GetContentRegionAvail();
|
2021-01-11 19:32:12 +00:00
|
|
|
ImGui::PushStyleColor(ImGuiCol_ChildBg, this->m_textEditor.GetPalette()[u32(TextEditor::PaletteIndex::Background)]);
|
2021-01-09 20:45:21 +00:00
|
|
|
|
|
|
|
if (ImGui::BeginChild("##console", consoleSize, true, ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
|
2021-01-09 22:48:42 +00:00
|
|
|
for (auto &[level, message] : this->m_console) {
|
|
|
|
switch (level) {
|
2021-01-21 10:36:58 +00:00
|
|
|
case lang::LogConsole::Level::Debug:
|
2021-01-11 19:32:12 +00:00
|
|
|
ImGui::PushStyleColor(ImGuiCol_Text, this->m_textEditor.GetPalette()[u32(TextEditor::PaletteIndex::Comment)]);
|
2021-01-09 22:48:42 +00:00
|
|
|
break;
|
2021-01-21 10:36:58 +00:00
|
|
|
case lang::LogConsole::Level::Info:
|
2021-01-11 19:32:12 +00:00
|
|
|
ImGui::PushStyleColor(ImGuiCol_Text, this->m_textEditor.GetPalette()[u32(TextEditor::PaletteIndex::Default)]);
|
2021-01-09 22:48:42 +00:00
|
|
|
break;
|
2021-01-21 10:36:58 +00:00
|
|
|
case lang::LogConsole::Level::Warning:
|
2021-01-11 19:32:12 +00:00
|
|
|
ImGui::PushStyleColor(ImGuiCol_Text, this->m_textEditor.GetPalette()[u32(TextEditor::PaletteIndex::Preprocessor)]);
|
2021-01-09 22:48:42 +00:00
|
|
|
break;
|
2021-01-21 10:36:58 +00:00
|
|
|
case lang::LogConsole::Level::Error:
|
2021-01-11 19:32:12 +00:00
|
|
|
ImGui::PushStyleColor(ImGuiCol_Text, this->m_textEditor.GetPalette()[u32(TextEditor::PaletteIndex::ErrorMarker)]);
|
2021-01-09 22:48:42 +00:00
|
|
|
break;
|
|
|
|
default: continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
ImGui::TextUnformatted(message.c_str());
|
|
|
|
|
|
|
|
ImGui::PopStyleColor();
|
|
|
|
}
|
2021-01-09 20:45:21 +00:00
|
|
|
}
|
|
|
|
ImGui::EndChild();
|
|
|
|
|
2021-01-09 22:48:42 +00:00
|
|
|
ImGui::PopStyleColor(1);
|
2020-11-20 19:26:19 +00:00
|
|
|
|
2020-11-20 23:12:58 +00:00
|
|
|
if (this->m_textEditor.IsTextChanged()) {
|
|
|
|
this->parsePattern(this->m_textEditor.GetText().data());
|
|
|
|
}
|
2020-11-20 19:26:19 +00:00
|
|
|
}
|
2021-01-21 16:48:24 +00:00
|
|
|
|
|
|
|
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows))
|
|
|
|
ImGui::GetIO().ConfigFlags &= ~ImGuiConfigFlags_NavEnableKeyboard;
|
2020-11-11 09:47:02 +00:00
|
|
|
}
|
2020-11-10 20:31:04 +00:00
|
|
|
ImGui::End();
|
2020-11-12 20:20:51 +00:00
|
|
|
|
2020-11-17 14:38:24 +00:00
|
|
|
if (this->m_fileBrowser.showFileDialog("Open Hex Pattern", imgui_addons::ImGuiFileBrowser::DialogMode::OPEN, ImVec2(0, 0), ".hexpat")) {
|
2020-11-21 13:39:16 +00:00
|
|
|
this->loadPatternFile(this->m_fileBrowser.selected_path);
|
|
|
|
}
|
2020-11-12 20:20:51 +00:00
|
|
|
|
2020-11-21 19:19:33 +00:00
|
|
|
if (ImGui::BeginPopupModal("Accept Pattern", nullptr, ImGuiWindowFlags_NoResize)) {
|
2020-11-21 13:39:16 +00:00
|
|
|
ImGui::TextUnformatted("A pattern compatible with this data type has been found:");
|
|
|
|
ImGui::Text("%ls", this->m_possiblePatternFile.filename().c_str());
|
|
|
|
ImGui::NewLine();
|
|
|
|
ImGui::Text("Do you want to load it?");
|
|
|
|
ImGui::NewLine();
|
2020-11-12 20:20:51 +00:00
|
|
|
|
2021-01-14 16:01:44 +00:00
|
|
|
confirmButtons("Yes", "No", [this]{
|
2020-11-21 13:39:16 +00:00
|
|
|
this->loadPatternFile(this->m_possiblePatternFile.string());
|
|
|
|
ImGui::CloseCurrentPopup();
|
2021-01-14 16:01:44 +00:00
|
|
|
}, []{
|
|
|
|
ImGui::CloseCurrentPopup();
|
|
|
|
});
|
|
|
|
|
|
|
|
if (ImGui::IsKeyDown(ImGui::GetKeyIndex(ImGuiKey_Escape)))
|
2020-11-21 13:39:16 +00:00
|
|
|
ImGui::CloseCurrentPopup();
|
2020-11-12 20:20:51 +00:00
|
|
|
|
2020-11-21 13:39:16 +00:00
|
|
|
ImGui::EndPopup();
|
|
|
|
}
|
|
|
|
}
|
2020-11-20 17:24:59 +00:00
|
|
|
|
2020-11-12 20:20:51 +00:00
|
|
|
|
2020-11-21 13:39:16 +00:00
|
|
|
void ViewPattern::loadPatternFile(std::string path) {
|
|
|
|
FILE *file = fopen(path.c_str(), "rb");
|
2020-11-12 20:20:51 +00:00
|
|
|
|
2020-11-21 13:39:16 +00:00
|
|
|
if (file != nullptr) {
|
|
|
|
char *buffer;
|
|
|
|
fseek(file, 0, SEEK_END);
|
|
|
|
size_t size = ftell(file);
|
|
|
|
rewind(file);
|
2020-11-12 20:20:51 +00:00
|
|
|
|
2020-11-21 13:39:16 +00:00
|
|
|
buffer = new char[size + 1];
|
2020-11-20 17:24:59 +00:00
|
|
|
|
2020-11-21 13:39:16 +00:00
|
|
|
fread(buffer, size, 1, file);
|
|
|
|
buffer[size] = 0x00;
|
|
|
|
|
|
|
|
|
|
|
|
fclose(file);
|
|
|
|
|
|
|
|
this->parsePattern(buffer);
|
|
|
|
this->m_textEditor.SetText(buffer);
|
|
|
|
|
|
|
|
delete[] buffer;
|
2020-11-12 20:20:51 +00:00
|
|
|
}
|
2020-11-10 20:31:04 +00:00
|
|
|
}
|
|
|
|
|
2020-11-14 13:42:21 +00:00
|
|
|
void ViewPattern::clearPatternData() {
|
|
|
|
for (auto &data : this->m_patternData)
|
|
|
|
delete data;
|
2020-11-13 11:07:05 +00:00
|
|
|
|
2020-11-14 13:42:21 +00:00
|
|
|
this->m_patternData.clear();
|
2020-11-19 10:36:52 +00:00
|
|
|
lang::PatternData::resetPalette();
|
2020-11-10 20:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ViewPattern::parsePattern(char *buffer) {
|
2020-11-14 13:42:21 +00:00
|
|
|
this->clearPatternData();
|
2020-11-27 20:20:23 +00:00
|
|
|
this->m_textEditor.SetErrorMarkers({ });
|
2021-01-09 20:45:21 +00:00
|
|
|
this->m_console.clear();
|
2020-11-14 23:46:18 +00:00
|
|
|
this->postEvent(Events::PatternChanged);
|
2020-11-10 20:31:04 +00:00
|
|
|
|
2021-01-23 13:00:09 +00:00
|
|
|
auto result = this->m_patternLanguageRuntime->executeString(SharedData::currentProvider, buffer);
|
2020-11-10 20:31:04 +00:00
|
|
|
|
2021-01-23 13:00:09 +00:00
|
|
|
auto error = this->m_patternLanguageRuntime->getError();
|
|
|
|
if (error.has_value()) {
|
|
|
|
this->m_textEditor.SetErrorMarkers({ error.value() });
|
2020-11-10 20:31:04 +00:00
|
|
|
}
|
|
|
|
|
2021-01-22 17:01:39 +00:00
|
|
|
this->m_console = this->m_patternLanguageRuntime->getConsoleLog();
|
2020-11-10 20:31:04 +00:00
|
|
|
|
2021-01-23 13:00:09 +00:00
|
|
|
if (result.has_value()) {
|
|
|
|
this->m_patternData = std::move(result.value());
|
|
|
|
View::postEvent(Events::PatternChanged);
|
|
|
|
}
|
2020-11-10 20:31:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|