1
0
Fork 0
mirror of https://github.com/eliboa/TegraRcmGUI.git synced 2024-11-08 11:51:45 +00:00
TegraRcmGUI/tegrarcmgui.cpp
2020-12-11 12:46:53 +01:00

537 lines
17 KiB
C++

#include "tegrarcmgui.h"
#include "ui_tegrarcmgui.h"
#include "qutils.h"
#include <QPoint>
#include <Windows.h>
#include <JlCompress.h>
QNetworkAccessManager *nMgr();
QMouseEvent MouseLeftButtonEvent(QEvent::MouseButtonPress, QPoint(0,0), Qt::LeftButton, nullptr, nullptr);
TegraRcmGUI * TegraRcmGUI::m_instance;
static void KUSB_API HotPlugEventCallback(KHOT_HANDLE Handle, KLST_DEVINFO_HANDLE DeviceInfo, KLST_SYNC_FLAG flag)
{
Q_ASSERT(TegraRcmGUI::hasInstance());
if (DeviceInfo == nullptr || (DeviceInfo->Common.Vid != RCM_VID && DeviceInfo->Common.Pid != RCM_PID))
return;
if (flag == KLST_SYNC_FLAG_ADDED)
TegraRcmGUI::instance()->emit sign_hotPlugEvent(true, DeviceInfo);
else if (flag == KLST_SYNC_FLAG_REMOVED)
TegraRcmGUI::instance()->emit sign_hotPlugEvent(false, DeviceInfo);
}
TegraRcmGUI::TegraRcmGUI(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::TegraRcmGUI)
{
ui->setupUi(this);
// Set a static instance to handle hotplug callback
Q_ASSERT(!hasInstance());
m_instance = this;
// Init access to builtin resources
Q_INIT_RESOURCE(qresources);
// Load settings
userSettings = new QSettings("nx", "TegraRcmGUI");
// Start update manager
/*
qRegisterMetaType<download_t>("download_t");
m_um = new UpdateManager(this);
connect(m_um, &UpdateManager::new_update_available, this, &TegraRcmGUI::updateAvailable);
//m_um->start();
*/
// Tray icon init
trayIcon = new QSystemTrayIcon;
trayIcon->setIcon(switchOffIcon);
trayIcon->setVisible(true);
connect(trayIcon, &QSystemTrayIcon::activated, this, &TegraRcmGUI::trayIconActivated);
trayIconMenu = trayIcon->contextMenu();
m_progressWidget = new qProgressWidget("", this);
// Create a qKourou instance to invoke Kourou methods (asynchronously) using signals and slots
m_kourou = new QKourou(this, &m_device, this);
m_kourou->autoLaunchAriane = userSettings->value("autoAriane").toBool();
m_kourou->autoInjectPayload = userSettings->value("autoInject").toBool();
// Create tabs
ui->tabWidget->tabBar()->setCursor(Qt::PointingHandCursor);
ui->push_layout->setAlignment(Qt::AlignTop);
ui->pushLayoutWidget->setAttribute(Qt::WA_TransparentForMouseEvents);
payloadTab = new QPayloadWidget(this);
ui->tabWidget->addTab(payloadTab, tr("PAYLOAD"));
hekateTab = new qHekate(this);
ui->tabWidget->addTab(hekateTab, tr("HEKATE / AMS"));
toolsTab = new qTools(this);
ui->tabWidget->addTab(toolsTab, tr("TOOLS"));
settingsTab = new qSettings(this);
ui->tabWidget->addTab(settingsTab, tr("SETTINGS"));
connect(ui->tabWidget, &QTabWidget::currentChanged, [=](int idx) {
if (idx == 1) hekateTab->on_tabActivated();
});
// Load builtin Ariane payload
QFile file(":/ariane_bin");
if (file.open(QIODevice::ReadOnly))
{
m_kourou->ariane_bin = file.readAll();
file.close();
}
// Init device at startup (in a concurrent thread)
QtConcurrent::run(m_kourou, &QKourou::initDevice, true, nullptr);
// Set the USB hotplug event
qRegisterMetaType<KLST_DEVINFO_HANDLE>("KLST_DEVINFO_HANDLE");
QObject::connect(this, SIGNAL(sign_hotPlugEvent(bool, KLST_DEVINFO_HANDLE)), this, SLOT(hotPlugEvent(bool, KLST_DEVINFO_HANDLE)));
KHOT_PARAMS hotParams;
memset(&hotParams, 0, sizeof(hotParams));
hotParams.OnHotPlug = HotPlugEventCallback;
hotParams.Flags = KHOT_FLAG_NONE;
HotK_Init(&m_hotHandle, &hotParams);
// Set timers
QTimer *devInfoTimer = new QTimer(this);
connect(devInfoTimer, SIGNAL(timeout()), this, SLOT(deviceInfoTimer()));
devInfoTimer->start(60000*5); // Every 5 minutes
QTimer *pushTimer = new QTimer(this);
connect(pushTimer, SIGNAL(timeout()), this, SLOT(pushTimer()));
pushTimer->start(1000); // Every second
m_kourou->initNoDriverDeviceLookUpLoop();
m_pkgs.setParent(this);
/// GUI inits
// Set stylesheets
this->setStyleSheet(GetStyleSheetFromResFile(":/res/QMainWindow.qss"));
ui->statusbar->setStyleSheet(GetStyleSheetFromResFile(":/res/QMainWindow.qss"));
ui->tabWidget->setStyleSheet(GetStyleSheetFromResFile(":/res/QTabWidget.qss"));
ui->titleBarFrame->setStyleSheet(GetStyleSheetFromResFile(":/res/QFrame_titleBar.qss"));
ui->statusBoxFrame->setStyleSheet(GetStyleSheetFromResFile(":/res/QFrame_box01.qss"));
ui->deviceInfoBoxFrame->setStyleSheet(GetStyleSheetFromResFile(":/res/QFrame_box01.qss"));
ui->closeAppBtn->setCursor(Qt::PointingHandCursor);
connect(ui->closeAppBtn, SIGNAL(clicked()), this, SLOT(on_appClose()));
ui->minimizeAppBtn->setCursor(Qt::PointingHandCursor);
connect(ui->minimizeAppBtn, SIGNAL(clicked()), this, SLOT(showMinimized()));
clearDeviceInfo();
ui->di_tableWidget->setColumnCount(2);
ui->di_tableWidget->setColumnWidth(0, 95);
ui->di_tableWidget->setColumnWidth(1, 75);
ui->di_tableWidget->horizontalScrollBar()->setStyleSheet("QScrollBar {height:0px;}");
ui->di_tableWidget->setStyleSheet(GetStyleSheetFromResFile(":/res/QTableWidget.qss"));
Switch *_switch = new Switch(m_kourou->autoLaunchAriane ? true : false, 52);
ui->verticalLayout->addWidget(_switch);
connect(_switch, SIGNAL(clicked(bool)), this, SLOT(on_autoLaunchAriane_toggled(bool)));
ui->centralwidget->setFixedSize(690, 400);
this->adjustSize();
}
TegraRcmGUI::~TegraRcmGUI()
{
if (m_hotHandle != nullptr)
HotK_Free(m_hotHandle);
delete trayIcon;
delete ui;
//QMainWindow::~QMainWindow();
}
void TegraRcmGUI::updateAvailable()
{
/*
auto queue = m_um->getDownloadQueue();
if (!queue.size())
return;
QString message;
QTextStream messageStr(&message);
messageStr << tr("New updates available :\n\n");
QString buff;
for (auto item : queue)
{
if (item.dest_path != buff)
messageStr << item.dest_path << "\n";
messageStr << "- " << QFileInfo(item.url.path()).fileName() << "\n";
buff = item.dest_path;
}
if(QMessageBox::question(this, "Update", message, QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes)
{
//QtConcurrent::run(m_um, &UpdateManager::download_all);
//m_um->download_all();
auto dialog = new UM_Dialog(m_um, this);
dialog->exec();
}
*/
}
void TegraRcmGUI::on_appClose()
{
if (userSettings->value("minToTray").toBool())
{
showMinimized();
trayIcon->showMessage("", tr("TegraRcmGUI is still running in system tray"), m_device.isReadyToReceivePayload() ? switchOnIcon : switchOffIcon, 3000);
}
else close();
}
void TegraRcmGUI::changeEvent(QEvent* e)
{
int t;
QEvent::Type typ = e->type();
switch (e->type())
{
case QEvent::LanguageChange:
this->ui->retranslateUi(this);
break;
case QEvent::WindowStateChange:
{
if (this->windowState() & Qt::WindowMinimized)
{
if (userSettings->value("minToTray").toBool())
QTimer::singleShot(250, this, SLOT(hide()));
}
this->adjustSize();
break;
}
default:
break;
}
QMainWindow::changeEvent(e);
}
void TegraRcmGUI::hotPlugEvent(bool added, KLST_DEVINFO_HANDLE deviceInfo)
{
if (added)
QtConcurrent::run(m_kourou, &QKourou::initDevice, true, deviceInfo);
else
{
m_device.disconnect();
on_deviceStateChange();
}
}
void TegraRcmGUI::deviceInfoTimer()
{
if (m_device.arianeIsReady())
QtConcurrent::run(m_kourou, &QKourou::getDeviceInfo);
}
void TegraRcmGUI::on_deviceStateChange()
{
// Device status box
ui->devStatusLbl_2->setText(tr("DEVICE STATUS"));
ui->devStatusFrame->setStyleSheet(m_device.getStatus() == CONNECTED ? statusOnStyleSht : statusOffStyleSht);
ui->rcmStatusLbl_2->setText(m_device.rcmIsReady() ? tr("READY") : tr("OFF"));
QString arianeStatus = "", arianeStyle = "";
if (m_kourou->arianeIsLoading)
{
arianeStatus.append(tr("LOADING"));
arianeStyle = statusOffStyleSht;
}
else if (m_device.arianeIsReady())
{
arianeStatus.append(tr("READY"));
arianeStyle = statusOnStyleSht;
}
else
{
arianeStatus.append(tr("OFF"));
arianeStyle = m_kourou->autoLaunchAriane ? statusOffRedStyleSht : statusOffStyleSht;
}
ui->arianeStatusLbl_2->setText(arianeStatus);
QString style = statusOffRedStyleSht;
if (m_device.arianeIsReady())
style = statusOffStyleSht;
else if (m_device.rcmIsReady())
style = statusOnStyleSht;
ui->rcmStatusFrame->setStyleSheet(style);
ui->arianeStatusFrame->setStyleSheet(arianeStyle);
if (!m_device.arianeIsReady())
clearDeviceInfo();
// Icons
if (m_device.isReadyToReceivePayload())
{
trayIcon->setIcon(switchOnIcon);
setWindowIcon(switchOnIcon);
}
else
{
trayIcon->setIcon(switchOffIcon);
setWindowIcon(switchOffIcon);
}
if (m_device.arianeIsReady() && m_pkgs.hasPendingDevInstalls())
m_pkgs.devInstall();
payloadTab->on_deviceStateChange();
toolsTab->on_deviceStateChange();
hekateTab->on_deviceStateChange();
}
void TegraRcmGUI::on_autoLaunchAriane_toggled(bool value)
{
m_kourou->autoLaunchAriane = !m_kourou->autoLaunchAriane;
userSettings->setValue("autoAriane", m_kourou->autoLaunchAriane);
on_deviceStateChange();
if (!m_kourou->autoLaunchAriane)
return;
if (m_device.rcmIsReady())
QtConcurrent::run(m_kourou, &QKourou::initDevice, true, nullptr);
else if (!m_device.arianeIsReady())
pushMessage((m_device.getStatus() == CONNECTED ? tr("Reboot") : tr("Boot")) + tr(" device to RCM to launch Ariane"));
}
bool TegraRcmGUI::enableWidget(QWidget *widget, bool enable)
{
widget->setEnabled(enable);
return true;
}
void TegraRcmGUI::clearDeviceInfo()
{
ui->deviceInfoFrame->setStyleSheet(statusOffStyleSht);
ui->deviceInfoLbl->setStyleSheet(statusOffStyleSht);
ui->di_tableWidget->setRowCount(0);
if (!m_kourou->autoLaunchAriane)
{
ui->devInfoDisableLbl->show();
}
else ui->devInfoDisableLbl->hide();
}
void TegraRcmGUI::on_deviceInfo_received(UC_DeviceInfo di)
{
ui->devInfoDisableLbl->hide();
ui->deviceInfoLbl->setStyleSheet(GetStyleSheetFromResFile(":/res/QLabel_title01.qss"));
ui->di_tableWidget->setRowCount(0);
auto addTableWidgetRow = [&](const QString &label, const QString &value, bool tooltip = false) {
auto di_t = ui->di_tableWidget;
di_t->insertRow(di_t->rowCount());
auto label_i = new QTableWidgetItem(label + ":");
label_i->setTextAlignment(Qt::AlignRight |Qt::AlignVCenter);
label_i->setFlags(label_i->flags() ^ Qt::ItemIsEditable);
auto value_i = new QTableWidgetItem(value);
value_i->setTextAlignment(Qt::AlignLeft |Qt::AlignVCenter);
value_i->setFlags(value_i->flags() ^ Qt::ItemIsEditable);
if (tooltip) value_i->setToolTip(value);
di_t->setItem(di_t->rowCount()-1, 0, label_i);
di_t->setItem(di_t->rowCount()-1, 1, value_i);
};
if (strlen(di.deviceId))
addTableWidgetRow(tr("NX ID"), QString(di.deviceId), true);
addTableWidgetRow(tr("Battery charge"), QString().asprintf("%u%%", di.battery_capacity));
addTableWidgetRow(tr("Burnt fuses"), QString().asprintf("%u", di.burnt_fuses));
if (strlen(di.fw_version))
addTableWidgetRow(tr("Firmware (SYS)"), QString(di.fw_version) + (di.exFat_driver ? tr(" exFAT") : ""), true);
if (di.emunand_type)
addTableWidgetRow(tr("Emunand"), di.emunand_type == FILEBASED ? tr("File based") : tr("RAW based"));
if (strlen(di.emu_fw_version))
addTableWidgetRow(tr("Firmware (EMU)"), QString(di.emu_fw_version) + (di.emu_exFat_driver ? tr(" exFAT") : ""), true);
if (di.sdmmc_initialized)
{
QString fs;
if (di.mmc_fs_type == FS_FAT32) fs.append("FAT32");
else if (di.mmc_fs_type == FS_EXFAT) fs.append("exFAT");
else fs.append("UNKNOWN");
addTableWidgetRow(tr("SD Filesystem"), fs);
qint64 fs_size = 0x200 * (qint64)di.mmc_fs_cl_size * ((qint64)di.mmc_fs_last_cl + 1);
qint64 fs_free_space = 0x200 * (qint64)di.mmc_fs_cl_size * (qint64)di.mmc_fs_free_cl;
addTableWidgetRow(tr("FS Total size"), GetReadableSize(fs_size));
if (fs_free_space)
addTableWidgetRow(tr("FS Free space"), GetReadableSize(fs_free_space));
}
if (di.autoRCM != toolsTab->autoRCM_switch->isActive())
toolsTab->autoRCM_switch->toggle();
toolsTab->on_deviceStateChange();
hekateTab->on_deviceInfo_received();
}
void TegraRcmGUI::error(int error)
{
if (error == FAILED_TO_SET_AUTORCM)
toolsTab->autoRCM_switch->toggle();
QString err_label;
for (ErrorLabel item : ErrorLabelArr)
{
if (item.error == error)
err_label = item.label;
}
if (!err_label.size())
err_label.append(QString().asprintf("Error %d", error));
if (!isVisible())
trayIcon->showMessage("", err_label, QSystemTrayIcon::Critical, 3000);
else
QMessageBox::critical(this, "Error", err_label);
}
void TegraRcmGUI::pushMessage(const QString message)
{
// Send message from tray if app is not visible
if (!isVisible())
{
trayIcon->showMessage("", message, QSystemTrayIcon::Information, 3000);
return;
}
// Otherwise, push in-app message
AnimatedLabel *push = new AnimatedLabel;
QPropertyAnimation *animation = new QPropertyAnimation(push, "color");
animation->setDuration(800);
animation->setStartValue(QColor(0, 150, 136));
animation->setEndValue(QColor(202, 202, 202));
push->setText(message);
push->setMinimumHeight(40);
push->setMaximumHeight(80);
ui->push_layout->addWidget(push);
animation->start();
push_ts.push_back(QDateTime::currentSecsSinceEpoch());
}
void TegraRcmGUI::pushTimer()
{
qint64 now = QDateTime::currentSecsSinceEpoch();
if (tsToDeleteCount && push_ts.count() == 0)
{
for (int i(0); i < tsToDeleteCount; i++)
ui->pushLayoutWidget->findChildren<QLabel*>().at(i)->deleteLater();
tsToDeleteCount = 0;
}
if (!push_ts.size())
return;
int i(0);
int j(0);
for (qint64 ts : push_ts)
{
if (now > ts + 5)
{
auto item = ui->pushLayoutWidget->findChildren<QLabel*>().at(j + tsToDeleteCount);
QPropertyAnimation *animation = new QPropertyAnimation(item, "geometry");
//QPropertyAnimation *animation = new QPropertyAnimation(item, "offset");
animation->setDuration(500);
int r = item->pos().y() + item->height() - 10;
if (lastTsToDelete)
r -= lastTsToDelete * item->pos().y();
animation->setStartValue(QRect(item->pos().x(), item->pos().y(), ui->pushLayoutWidget->width(), r));
animation->setEndValue(QRect(ui->pushLayoutWidget->width(), item->pos().y(), ui->pushLayoutWidget->width(), r));
animation->start();
lastTsToDelete = ts;
i++;
break;
}
j++;
}
push_ts.erase(push_ts.begin(), push_ts.begin() + i);
tsToDeleteCount += i;
}
void TegraRcmGUI::on_Kourou_finished(int res)
{
if (res == AUTO_INJECT || res == PAYLOAD_INJECT)
pushMessage(tr("Payload successfully injected"));
}
void TegraRcmGUI::trayIconActivated(QSystemTrayIcon::ActivationReason reason)
{
int t;
switch (reason) {
case QSystemTrayIcon::Trigger:
showNormal();
activateWindow();
break;
case QSystemTrayIcon::DoubleClick:
break;
case QSystemTrayIcon::MiddleClick:
break;
case QSystemTrayIcon::Context:
drawTrayContextMenu();
break;
default:
;
}
}
void TegraRcmGUI::drawTrayContextMenu()
{
//trayIconMenu->addAction("Exit",this,SLOT(close()));
QString menu_ss = "QMenu::item {"
"padding: 4px 20px 4px 4px;"
"}"
"QMenu::item:selected {"
"background-color: rgb(0, 85, 127);"
"color: rgb(255, 255, 255);"
"}";
if (nullptr != trayIconMenu)
delete trayIconMenu;
trayIconMenu = new QMenu;
trayIconMenu->setStyleSheet(menu_ss);
trayIconMenu->setContextMenuPolicy(Qt::CustomContextMenu);
if (!isVisible())
trayIconMenu->addAction("Show",this,SLOT(showNormal()));
trayIconMenu->addAction("Close",this,SLOT(close()));
trayIconMenu->addSeparator();
if (m_device.isReadyToReceivePayload())
{
QMenu *fav_menu = new QMenu;
fav_menu->setStyleSheet(menu_ss);
fav_menu->setContextMenuPolicy(Qt::CustomContextMenu);
fav_menu->setTitle("Favorites");
QSignalMapper* signalmapper = new QSignalMapper();
for (payload_t payload : payloadTab->getPayloads())
{
QAction *action = new QAction(payload.name);
connect (action, SIGNAL(triggered()), signalmapper, SLOT(map()));
signalmapper ->setMapping (action, payload.path);
connect (signalmapper , SIGNAL(mapped(QString)), payloadTab, SLOT(on_injectPayload(QString)));
fav_menu->addAction(action);
}
trayIconMenu->addMenu(fav_menu);
}
trayIcon->setContextMenu(trayIconMenu);
trayIcon->contextMenu()->popup(QCursor::pos());
}