#include "MainWindow.hpp" #include "ui_MainWindow.h" #include #include #include #include #include #include "FileDirDialog.hpp" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), m_ui(new Ui::MainWindow) , m_heclProc(this) { m_ui->setupUi(this); m_ui->heclTabs->setCurrentIndex(0); m_ui->splitter->setSizes({0,-1}); initSlots(); } MainWindow::~MainWindow() { m_heclProc.close(); m_heclProc.terminate(); delete m_ui; } /* TODO: more complete Vt102 emulation */ // based on information: http://en.m.wikipedia.org/wiki/ANSI_escape_code http://misc.flogisoft.com/bash/tip_colors_and_formatting http://invisible-island.net/xterm/ctlseqs/ctlseqs.html void MainWindow::parseEscapeSequence(int attribute, QListIterator< QString > & i, QTextCharFormat & textCharFormat, QTextCharFormat const & defaultTextCharFormat) { switch (attribute) { case 0 : { // Normal/Default (reset all attributes) textCharFormat = defaultTextCharFormat; break; } case 1 : { // Bold/Bright (bold or increased intensity) textCharFormat.setFontWeight(QFont::Bold); break; } case 2 : { // Dim/Faint (decreased intensity) textCharFormat.setFontWeight(QFont::Light); break; } case 3 : { // Italicized (italic on) textCharFormat.setFontItalic(true); break; } case 4 : { // Underscore (single underlined) textCharFormat.setUnderlineStyle(QTextCharFormat::SingleUnderline); textCharFormat.setFontUnderline(true); break; } case 5 : { // Blink (slow, appears as Bold) textCharFormat.setFontWeight(QFont::Bold); break; } case 6 : { // Blink (rapid, appears as very Bold) textCharFormat.setFontWeight(QFont::Black); break; } case 7 : { // Reverse/Inverse (swap foreground and background) QBrush foregroundBrush = textCharFormat.foreground(); textCharFormat.setForeground(textCharFormat.background()); textCharFormat.setBackground(foregroundBrush); break; } case 8 : { // Concealed/Hidden/Invisible (usefull for passwords) textCharFormat.setForeground(textCharFormat.background()); break; } case 9 : { // Crossed-out characters textCharFormat.setFontStrikeOut(true); break; } case 10 : { // Primary (default) font textCharFormat.setFont(defaultTextCharFormat.font()); break; } case 11: case 12: case 13: case 14: case 15: case 16: case 17: case 18: case 19: { QFontDatabase fontDatabase; QString fontFamily = textCharFormat.fontFamily(); QStringList fontStyles = fontDatabase.styles(fontFamily); int fontStyleIndex = attribute - 11; if (fontStyleIndex < fontStyles.length()) { textCharFormat.setFont(fontDatabase.font(fontFamily, fontStyles.at(fontStyleIndex), textCharFormat.font().pointSize())); } break; } case 20 : { // Fraktur (unsupported) break; } case 21 : { // Set Bold off textCharFormat.setFontWeight(QFont::Normal); break; } case 22 : { // Set Dim off textCharFormat.setFontWeight(QFont::Normal); break; } case 23 : { // Unset italic and unset fraktur textCharFormat.setFontItalic(false); break; } case 24 : { // Unset underlining textCharFormat.setUnderlineStyle(QTextCharFormat::NoUnderline); textCharFormat.setFontUnderline(false); break; } case 25 : { // Unset Blink/Bold textCharFormat.setFontWeight(QFont::Normal); break; } case 26 : { // Reserved break; } case 27 : { // Positive (non-inverted) QBrush backgroundBrush = textCharFormat.background(); textCharFormat.setBackground(textCharFormat.foreground()); textCharFormat.setForeground(backgroundBrush); break; } case 28 : { textCharFormat.setForeground(defaultTextCharFormat.foreground()); textCharFormat.setBackground(defaultTextCharFormat.background()); break; } case 29 : { textCharFormat.setUnderlineStyle(QTextCharFormat::NoUnderline); textCharFormat.setFontUnderline(false); break; } case 30: case 31: case 32: case 33: case 34: case 35: case 36: case 37: { int colorIndex = attribute - 30; QColor color; if (QFont::Normal < textCharFormat.fontWeight()) { switch (colorIndex) { case 0 : { color = Qt::darkGray; break; } case 1 : { color = Qt::red; break; } case 2 : { color = Qt::green; break; } case 3 : { color = Qt::yellow; break; } case 4 : { color = Qt::blue; break; } case 5 : { color = Qt::magenta; break; } case 6 : { color = Qt::cyan; break; } case 7 : { color = Qt::white; break; } default : { Q_ASSERT(false); } } } else { switch (colorIndex) { case 0 : { color = Qt::black; break; } case 1 : { color = Qt::darkRed; break; } case 2 : { color = Qt::darkGreen; break; } case 3 : { color = Qt::darkYellow; break; } case 4 : { color = Qt::darkBlue; break; } case 5 : { color = Qt::darkMagenta; break; } case 6 : { color = Qt::darkCyan; break; } case 7 : { color = Qt::lightGray; break; } default : { Q_ASSERT(false); } } } textCharFormat.setForeground(color); break; } case 38 : { if (i.hasNext()) { bool ok = false; int selector = i.next().toInt(&ok); Q_ASSERT(ok); QColor color; switch (selector) { case 2 : { if (!i.hasNext()) { break; } int red = i.next().toInt(&ok); Q_ASSERT(ok); if (!i.hasNext()) { break; } int green = i.next().toInt(&ok); Q_ASSERT(ok); if (!i.hasNext()) { break; } int blue = i.next().toInt(&ok); Q_ASSERT(ok); color.setRgb(red, green, blue); break; } case 5 : { if (!i.hasNext()) { break; } int index = i.next().toInt(&ok); Q_ASSERT(ok); if (index >= 0 && index <= 0x07) { // 0x00-0x07: standard colors (as in ESC [ 30..37 m) return parseEscapeSequence(index - 0x00 + 30, i, textCharFormat, defaultTextCharFormat); } else if (index >= 0x08 && index <= 0x0F) { // 0x08-0x0F: high intensity colors (as in ESC [ 90..97 m) return parseEscapeSequence(index - 0x08 + 90, i, textCharFormat, defaultTextCharFormat); } else if (index >= 0x10 && index <= 0xE7) { // 0x10-0xE7: 6*6*6=216 colors: 16 + 36*r + 6*g + b (0≤r,g,b≤5) index -= 0x10; int red = index % 6; index /= 6; int green = index % 6; index /= 6; int blue = index % 6; index /= 6; Q_ASSERT(index == 0); color.setRgb(red, green, blue); break; } else if (index >= 0xE8 && index <= 0xFF) { // 0xE8-0xFF: grayscale from black to white in 24 steps qreal intensity = qreal(index - 0xE8) / (0xFF - 0xE8); color.setRgbF(intensity, intensity, intensity); break; } textCharFormat.setForeground(color); break; } default : { break; } } } break; } case 39 : { textCharFormat.setForeground(defaultTextCharFormat.foreground()); break; } case 40: case 41: case 42: case 43: case 44: case 45: case 46: case 47: { int colorIndex = attribute - 40; QColor color; switch (colorIndex) { case 0 : { color = Qt::darkGray; break; } case 1 : { color = Qt::red; break; } case 2 : { color = Qt::green; break; } case 3 : { color = Qt::yellow; break; } case 4 : { color = Qt::blue; break; } case 5 : { color = Qt::magenta; break; } case 6 : { color = Qt::cyan; break; } case 7 : { color = Qt::white; break; } default : { Q_ASSERT(false); } } textCharFormat.setBackground(color); break; } case 48 : { if (i.hasNext()) { bool ok = false; int selector = i.next().toInt(&ok); Q_ASSERT(ok); QColor color; switch (selector) { case 2 : { if (!i.hasNext()) { break; } int red = i.next().toInt(&ok); Q_ASSERT(ok); if (!i.hasNext()) { break; } int green = i.next().toInt(&ok); Q_ASSERT(ok); if (!i.hasNext()) { break; } int blue = i.next().toInt(&ok); Q_ASSERT(ok); color.setRgb(red, green, blue); break; } case 5 : { if (!i.hasNext()) { break; } int index = i.next().toInt(&ok); Q_ASSERT(ok); if (index >= 0x00 && index <= 0x07) { // 0x00-0x07: standard colors (as in ESC [ 40..47 m) return parseEscapeSequence(index - 0x00 + 40, i, textCharFormat, defaultTextCharFormat); } else if (index >= 0x08 && index <= 0x0F) { // 0x08-0x0F: high intensity colors (as in ESC [ 100..107 m) return parseEscapeSequence(index - 0x08 + 100, i, textCharFormat, defaultTextCharFormat); } else if (index >= 0x10 && index <= 0xE7) { // 0x10-0xE7: 6*6*6=216 colors: 16 + 36*r + 6*g + b (0≤r,g,b≤5) index -= 0x10; int red = index % 6; index /= 6; int green = index % 6; index /= 6; int blue = index % 6; index /= 6; Q_ASSERT(index == 0); color.setRgb(red, green, blue); break; } else if (index >= 0xE8 && index <= 0xFF) { // 0xE8-0xFF: grayscale from black to white in 24 steps qreal intensity = qreal(index - 0xE8) / (0xFF - 0xE8); color.setRgbF(intensity, intensity, intensity); } } textCharFormat.setBackground(color); break; } } break; } case 49: { textCharFormat.setBackground(defaultTextCharFormat.background()); break; } case 90: case 91: case 92: case 93: case 94: case 95: case 96: case 97: { int colorIndex = attribute - 90; QColor color; switch (colorIndex) { case 0 : { color = Qt::darkGray; break; } case 1 : { color = Qt::red; break; } case 2 : { color = Qt::green; break; } case 3 : { color = Qt::yellow; break; } case 4 : { color = Qt::blue; break; } case 5 : { color = Qt::magenta; break; } case 6 : { color = Qt::cyan; break; } case 7 : { color = Qt::white; break; } default : { Q_ASSERT(false); } } color.setRedF(color.redF() * 0.8); color.setGreenF(color.greenF() * 0.8); color.setBlueF(color.blueF() * 0.8); textCharFormat.setForeground(color); break; } case 100: case 101: case 102: case 103: case 104: case 105: case 106: case 107: { int colorIndex = attribute - 100; QColor color; switch (colorIndex) { case 0 : { color = Qt::darkGray; break; } case 1 : { color = Qt::red; break; } case 2 : { color = Qt::green; break; } case 3 : { color = Qt::yellow; break; } case 4 : { color = Qt::blue; break; } case 5 : { color = Qt::magenta; break; } case 6 : { color = Qt::cyan; break; } case 7 : { color = Qt::white; break; } default : { Q_ASSERT(false); } } color.setRedF(color.redF() * 0.8); color.setGreenF(color.greenF() * 0.8); color.setBlueF(color.blueF() * 0.8); textCharFormat.setBackground(color); break; } default : { break; } } } void MainWindow::onExtract() { } void MainWindow::onReturnPressed() { if (sender() == m_ui->pathEdit && !m_ui->pathEdit->text().isEmpty()) m_path = m_ui->pathEdit->text(); } void MainWindow::initSlots() { #ifdef Q_OS_WIN m_heclProc.setEnvironment(QProcessEnvironment::systemEnvironment().toStringList() + QStringList("ConEmuANSI=ON")); #endif connect(&m_heclProc, &QProcess::readyRead, [=](){ m_ansiString = m_heclProc.readAll(); m_ui->processOutput->clear(); setTextTermFormatting(m_ui->processOutput, m_ansiString); m_ui->processOutput->ensureCursorVisible(); }); connect(m_ui->extractBtn, &QPushButton::clicked, [=](){ if (m_path.isEmpty()) return; QString path = QFileInfo(m_path).absolutePath(); m_ansiString.clear(); m_heclProc.close(); m_heclProc.terminate(); m_heclProc.setProcessChannelMode(QProcess::ProcessChannelMode::MergedChannels); m_heclProc.setWorkingDirectory(path); m_heclProc.start("../hecl/driver/hecl.exe", {"extract", m_path, "-y"}); }); connect(m_ui->browseBtn, &QPushButton::clicked, [=]() { FileDirDialog dialog(this); dialog.setWindowTitle("Select ISO or Directory"); int res = dialog.exec();//QFileDialog::getOpenFileName(this, "Select ISO or Directory"); if (res == QFileDialog::Rejected) return; if (dialog.selectedFiles().size() <= 0) return; QString path = dialog.selectedFiles().at(0); /* TODO: Add beacon detection */ if (!path.isEmpty()) { m_path = path; m_ui->pathEdit->setText(m_path); m_ui->extractBtn->setEnabled(true); m_ui->packageBtn->setEnabled(true); } }); connect(m_ui->packageBtn, &QPushButton::clicked, [=](){ if (m_path.isEmpty()) return; QString projectDir = m_path; if (projectDir.endsWith(".iso", Qt::CaseInsensitive)) projectDir.remove(m_path.length() - 4, 4); m_ansiString.clear(); m_heclProc.close(); m_heclProc.terminate(); m_heclProc.setWorkingDirectory(projectDir); m_heclProc.setProcessChannelMode(QProcess::ProcessChannelMode::MergedChannels); m_heclProc.start("../hecl/driver/hecl.exe", {"package", "-y"}); }); } void MainWindow::setTextTermFormatting(QTextEdit* textEdit, const QString& text) { QTextDocument * document = textEdit->document(); QRegExp const escapeSequenceExpression(R"(\x1B\[([\d;]+)m)"); QTextCursor cursor(document); QTextCharFormat defaultTextCharFormat = cursor.charFormat(); cursor.beginEditBlock(); int offset = escapeSequenceExpression.indexIn(text); cursor.insertText(text.mid(0, offset)); QTextCharFormat textCharFormat = defaultTextCharFormat; while (!(offset < 0)) { int previousOffset = offset + escapeSequenceExpression.matchedLength(); QStringList capturedTexts = escapeSequenceExpression.capturedTexts().back().split(';'); QListIterator< QString > i(capturedTexts); while (i.hasNext()) { bool ok = false; int attribute = i.next().toInt(&ok); Q_ASSERT(ok); parseEscapeSequence(attribute, i, textCharFormat, defaultTextCharFormat); } offset = escapeSequenceExpression.indexIn(text, previousOffset); if (offset < 0) { cursor.insertText(text.mid(previousOffset), textCharFormat); } else { cursor.insertText(text.mid(previousOffset, offset - previousOffset), textCharFormat); } } cursor.setCharFormat(defaultTextCharFormat); cursor.endEditBlock(); cursor.movePosition(QTextCursor::End); textEdit->setTextCursor(cursor); }