From bef72f5cd0e0ff95cd7f5dbe6c0cf995b82e57a7 Mon Sep 17 00:00:00 2001 From: eliboa Date: Wed, 24 Jun 2020 20:31:54 +0200 Subject: [PATCH] libs & dark theme --- TegraRcmGUI.pro | 37 +- kourou/kourou.cpp | 195 +++++++++ kourou/kourou.h | 50 ++- kourou/libs/rcm_device.cpp | 255 ++++++++---- kourou/libs/rcm_device.h | 84 +++- kourou/libs/tegrarcm/aes-cmac.cpp | 28 +- kourou/libs/tegrarcm/nv3p.h | 2 +- kourou/libs/tegrarcm/rcm.h | 8 + kourou/libs/tegrarcm/rsa-pss.cpp | 44 +- kourou/libs/tegrarcm/usb.c | 3 +- kourou/libs/tegrarcm/usb.h | 7 + kourou/usb_command.h | 60 ++- main.cpp | 9 + qkourou.cpp | 202 +++++++++- qkourou.h | 58 ++- qpayload.cpp | 382 +++++++++++++++++- qpayload.h | 87 +++- qpayload.ui | 287 ++++++++++++- qresources.qrc | 19 +- qtools.ui | 21 +- qutils.cpp | 134 +++++++ qutils.h | 88 ++++ rcm_device.h | 11 - res/QFrame_box01.qss | 2 +- res/QFrame_box02.qss | 12 +- res/QLabel_title01.qss | 14 + res/QMainWindow.qss | 15 +- res/QPushButton.qss | 17 + res/QTabWidget.qss | 53 ++- res/QTableView.qss | 7 + tegrarcmgui.cpp | 346 ++++++++++++++++ tegrarcmgui.h | 67 +++- tegrarcmgui.ui | 642 +++++++++++++++++++++++++++++- types.h | 35 ++ 34 files changed, 3058 insertions(+), 223 deletions(-) delete mode 100644 rcm_device.h diff --git a/TegraRcmGUI.pro b/TegraRcmGUI.pro index 036374c..00a02c7 100644 --- a/TegraRcmGUI.pro +++ b/TegraRcmGUI.pro @@ -1,9 +1,10 @@ QT += core gui - greaterThan(QT_MAJOR_VERSION, 4): QT += widgets CONFIG += c++11 +RC_ICONS = res/TegraRcmGUI.ico + # The following define makes your compiler emit warnings if you use # any Qt feature that has been marked deprecated (the exact warnings # depend on your compiler). Please consult the documentation of the @@ -16,16 +17,40 @@ DEFINES += QT_DEPRECATED_WARNINGS #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 SOURCES += \ + kourou/kourou.cpp \ + kourou/libs/rcm_device.cpp \ main.cpp \ + qkourou.cpp \ + qpayload.cpp \ + qtools.cpp \ + qutils.cpp \ tegrarcmgui.cpp HEADERS += \ - tegrarcmgui.h + kourou/kourou.h \ + kourou/libs/libusbk_int.h \ + kourou/libs/rcm_device.h \ + kourou/usb_command.h \ + qkourou.h \ + qpayload.h \ + qtools.h \ + qutils.h \ + rcm_device.h \ + tegrarcmgui.h \ + types.h FORMS += \ + qpayload.ui \ + qtools.ui \ tegrarcmgui.ui -# Default rules for deployment. -qnx: target.path = /tmp/$${TARGET}/bin -else: unix:!android: target.path = /opt/$${TARGET}/bin -!isEmpty(target.path): INSTALLS += target +TRANSLATIONS = languages/tegrarcmgui_fr.ts + +LIBS += -L$$PWD/../../../../../../../libusbK-dev-kit/bin/lib/amd64/ -llibusbK +INCLUDEPATH += $$PWD/../../../../../../../libusbK-dev-kit/includes +DEPENDPATH += $$PWD/../../../../../../../libusbK-dev-kit/includes + +RESOURCES += \ + qresources.qrc + +DISTFILES += diff --git a/kourou/kourou.cpp b/kourou/kourou.cpp index a227a8e..21f09b4 100644 --- a/kourou/kourou.cpp +++ b/kourou/kourou.cpp @@ -1,6 +1,201 @@ +/* + * Copyright (c) 2020 eliboa + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + #include "kourou.h" +#include +#include + +using namespace std; Kourou::Kourou() { } + +bool Kourou::initDevice(KLST_DEVINFO_HANDLE deviceInfo) +{ + m_rcm_ready = RcmDevice::initDevice(deviceInfo); + m_ariane_ready = false; + + // If RCM device is not ready, check if Ariane is already loaded + if (!m_rcm_ready && getStatus() == CONNECTED) + { + m_ariane_ready = arianeIsReady_sync(); + return m_ariane_ready; + } + return m_rcm_ready; +} + +void Kourou::disconnect() +{ + RcmDevice::disconnect(); + m_rcm_ready = false; + m_ariane_ready = false; +} + +int Kourou::sdmmc_writeFile(const char* in_path, const char* out_path, bool create_always) +{ + char* fileMemBlock = nullptr; + ifstream file(in_path, ios::in | ios::binary | ios::ate); + UC_SDIO uc; + auto end = [&] (int rc) { + if(file.is_open()) file.close(); + if (fileMemBlock != nullptr) delete[] fileMemBlock; + return rc; + }; + + if (strlen(out_path) > array_countof(uc.path)-1) + return PATH_TOO_LONG; + + if (!file.is_open()) + return OPEN_FILE_FAILED; + + // Check file size + if (file.tellg() > MAX_FILE_SIZE) // 100MB max + return end(FILE_TOO_LARGE); + + // Create command + memset(uc.path, 0, array_countof(uc.path)); + uc.command = WRITE_SD_FILE; + strcpy_s(uc.path, array_countof(uc.path), out_path); + uc.file_size = file.tellg(); + file.seekg(0, ios::beg); + + // Send command to device + int bytesSent = write((const u8*)&uc, sizeof(uc)); + if (bytesSent != sizeof(uc)) + return end(SEND_COMMAND_FAILED); + + // Allocate memory for file + fileMemBlock = new char[uc.file_size]; + file.read(fileMemBlock, uc.file_size); + + return end(sendBinPackets(fileMemBlock, uc.file_size)); +} + +int Kourou::sendBinPackets(char* buffer, u32 len) +{ + u32 curOffset = 0, bytesRemaining = len; + u8 buf[USB_BUFFER_LENGTH]; + u32 dataBufSize = USB_BUFFER_LENGTH - 32; + while(bytesRemaining > 0) + { + memset(buf, 0, USB_BUFFER_LENGTH); + UC_BlockHeader bh; + bh.block_size = bytesRemaining > dataBufSize ? dataBufSize : bytesRemaining; + bh.block_full_size = 0; // no compression + memcpy_s(&buf[0], sizeof(UC_BlockHeader), &bh, sizeof(UC_BlockHeader)); + memcpy_s(&buf[32], bh.block_size, &buffer[curOffset], bh.block_size); + u32 fbs = bh.block_size + 32; + + if (write((const u8*)&buf[0], fbs) != int(fbs)) + return USB_WRITE_FAILED; + + // Get confirmation + u32 bytesReceived = 0; + if (!readResponse(&bytesReceived, sizeof(u32))) + return USB_WRITE_FAILED; + if (bytesReceived != bh.block_size) + return USB_WRITE_FAILED; + + bytesRemaining -= bh.block_size; + } + return int(len); +} + +bool Kourou::readResponse(void* buffer, u32 size) +{ + u32 res_size = RESPONSE_MAX_SIZE - sizeof(u16); + u8 tmp_buffer[RESPONSE_MAX_SIZE - sizeof(u16)]; + if (size > res_size) + return false; + else res_size = size + sizeof(u16); + + if (read(tmp_buffer, res_size) != int(res_size)) + return false; + + u16* signature = (u16*)tmp_buffer; + if (*signature != RESPONSE) + return false; + + memcpy(buffer, &tmp_buffer[sizeof(u16)], size); + return true; +} + +bool Kourou::arianeIsReady_sync() +{ + auto end = [&](bool ready) { + m_ariane_ready = ready; + return ready; + }; + + if (RcmDevice::deviceIsReady()) + { + m_rcm_ready = true; + return end(false); + } + else m_rcm_ready = false; + + u8 buff[0x10]; + static const char READY_INDICATOR[] = "READY.\n"; + int bytesRead = 0; + + flushPipe(READ_PIPE_ID); + + UC_Header uc; + uc.command = GET_STATUS; + if (write((const u8*)&uc, sizeof(uc)) != sizeof(uc)) + return end(false); + + if ((bytesRead = read(&buff[0], 0x10)) > 0 && memcmp(&buff[0], READY_INDICATOR, array_countof(READY_INDICATOR) - 1) == 0) + return end(true); + + return end(false); +} + +int Kourou::getDeviceInfo(UC_DeviceInfo* di) +{ + UC_Header uc; + uc.command = GET_DEVICE_INFO; + if(write((const u8*)&uc, sizeof(uc)) != sizeof(uc)) + return SEND_COMMAND_FAILED; + + if(read((u8*)di, sizeof(UC_DeviceInfo)) != sizeof(UC_DeviceInfo)) + return RECEIVE_COMMAND_FAILED; + + if (di->signature != DEVINFO) + return RECEIVE_COMMAND_FAILED; + + return 0; +} + +bool Kourou::rebootToRcm() +{ + if (!arianeIsReady_sync()) + return false; + + UC_Header uc; + uc.command = REBOOT_RCM; + // Send command + write((const u8*)&uc, sizeof(uc)); + + // Get response + bool response = false; + if (!readResponse(&response, sizeof(bool))) + return false; + + return response; +} diff --git a/kourou/kourou.h b/kourou/kourou.h index ea615d0..44f4963 100644 --- a/kourou/kourou.h +++ b/kourou/kourou.h @@ -1,11 +1,59 @@ +/* + * Copyright (c) 2020 eliboa + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * Kourou is a C++ child class derived from base class "RcmDevice" + * This class provides additionnal functions for communicating with Ariane (backend payload based program) + * + */ + #ifndef KOUROU_H #define KOUROU_H +#include "libs/rcm_device.h" +#include "usb_command.h" +typedef enum _KRESULT : DWORD +{ + RCM_REBOOT_FAILED = 0x00F, + ARIANE_NOT_READY = 0x010, + WRONG_PARAM_GENERIC = 0x011 -class Kourou +} KRESULT; + +class Kourou : public RcmDevice { public: Kourou(); + bool initDevice(KLST_DEVINFO_HANDLE deviceInfo = nullptr); + void disconnect(); + + int sdmmc_writeFile(const char* in_path, const char* out_path, bool create_always = false); + int getDeviceInfo(UC_DeviceInfo* di); + bool arianeIsReady() { return m_ariane_ready; } + bool rcmIsReady() { return m_rcm_ready; } + void setRcmReady(bool b) { m_rcm_ready = b; } + bool arianeIsReady_sync(); + void setArianeReady(bool b) { m_ariane_ready = b; } + bool rebootToRcm(); + +private: + int sendBinPackets(char* buffer, u32 len); + bool readResponse(void* buffer, u32 size); + bool m_ariane_ready = false; + bool m_rcm_ready = false; }; #endif // KOUROU_H diff --git a/kourou/libs/rcm_device.cpp b/kourou/libs/rcm_device.cpp index 5f1e504..21c6517 100644 --- a/kourou/libs/rcm_device.cpp +++ b/kourou/libs/rcm_device.cpp @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2020 eliboa + * Copyright (c) 2018 ktemkin + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + #include "rcm_device.h" #include #include @@ -8,80 +25,107 @@ using namespace std; RcmDevice::RcmDevice() { m_currentBuffer = 0; - m_b_RcmReady = false; + m_usbAPI_loaded = false; + m_devIsInitialized = false; + m_devStatus = DISCONNECTED; } -bool RcmDevice::initDevice(KLST_DEVINFO_HANDLE deviceInfo) -{ - m_b_RcmReady = false; - - if (deviceInfo != nullptr) - m_devInfo = deviceInfo; - else - getPluggedDevice(); - - if (m_devInfo != nullptr && loadDevice() == SUCCESS) - m_b_RcmReady = true; - - return m_b_RcmReady; -} - -bool RcmDevice::getPluggedDevice() +RcmDevice::~RcmDevice() { if (m_devList != nullptr) LstK_Free(m_devList); - u32 devCount = 0; - - if (!LstK_Init(&m_devList, KLST_FLAG_NONE)) - return false; - - LstK_Count(m_devList, &devCount); - - if (devCount == 0 || !LstK_FindByVidPid(m_devList, RCM_VID, RCM_PID, &m_devInfo)) - return false; - - if (m_devInfo == nullptr) - return false; - - return true; + if (m_usbAPI_loaded) + m_usbApi.Free(m_usbHandle); } -RRESULT RcmDevice::loadDevice() -{ - if (m_devInfo == nullptr) - return DEVICE_NOT_FOUND; +bool RcmDevice::initDevice(KLST_DEVINFO_HANDLE deviceInfo) +{ + auto error = [&](DWORD err_code){ + SetLastError(err_code); + return false; + }; + + if (deviceInfo != nullptr && (deviceInfo->Common.Vid != RCM_VID && deviceInfo->Common.Pid != RCM_PID)) + return error(WRONG_DEVICE_VID_PID); + + KLST_DEVINFO_HANDLE tmp_devInfo = deviceInfo != nullptr ? deviceInfo : m_devInfo; + if(tmp_devInfo == nullptr && !getPluggedDevice(&tmp_devInfo)) + return error(DEVICE_NOT_FOUND); + + // New device already initialized & connected, return ready state (no need to load anything else) + if (m_devInfo != nullptr && m_devStatus == CONNECTED && m_devInfo->DeviceID == tmp_devInfo->DeviceID) + return deviceIsReady() ? true : error(DEVICE_NOT_READY); + + // Init device + m_devInfo = tmp_devInfo; + m_devIsInitialized = false; if (m_devInfo->DriverID != KUSB_DRVID_LIBUSBK) - return MISSING_LIBUSBK_DRIVER; + return error(MISSING_LIBUSBK_DRIVER); - LibK_LoadDriverAPI(&m_usbApi, m_devInfo->DriverID); + // Load driver API + if (!m_usbAPI_loaded) + { + LibK_LoadDriverAPI(&m_usbApi, KUSB_DRVID_LIBUSBK); + m_usbAPI_loaded = true; + } + // Init USB handle + m_usbApi.Free(m_usbHandle); // Free previous usb handle if (!m_usbApi.Init(&m_usbHandle, m_devInfo)) - return DEVICE_HANDLE_FAILED; + return error(DEVICE_HANDLE_FAILED); + // Verify libusbk version libusbk::libusb_request req; memset(&req, 0, sizeof(req)); int res = Ioctl(libusbk::LIBUSB_IOCTL_GET_VERSION, &req, sizeof(req), &req, sizeof(req)); - if (res <= 0) - return DEVICE_HANDLE_FAILED; + return error(DEVICE_HANDLE_FAILED); libusbk::version_t usbkVersion = req.version; if (usbkVersion.major != 3 || usbkVersion.minor != 0 || usbkVersion.micro != 7) - return LIBUSBK_WRONG_DRIVER; + return error(LIBUSBK_WRONG_DRIVER); - vector deviceId(0x10, 0); - if(read(&deviceId[0], 0x10) != int(deviceId.size())) - return DEVICEID_READ_FAILED; + m_devIsInitialized = true; + m_devStatus = CONNECTED; - return SUCCESS; + // Set pipes timeout policy + u32 pipe_timeout = 2000; //ms + m_usbApi.SetPipePolicy(m_usbHandle, READ_PIPE_ID, PIPE_TRANSFER_TIMEOUT, sizeof(u32), &pipe_timeout); + m_usbApi.SetPipePolicy(m_usbHandle, WRITE_PIPE_ID, PIPE_TRANSFER_TIMEOUT, sizeof(u32), &pipe_timeout); + + // Device is initialized, return ready state + return deviceIsReady() ? true : error(DEVICE_NOT_READY); +} + +// This function returns true if device is ready to receive RCM commands. +// It doesn't mean the device is eXploitable ! +bool RcmDevice::deviceIsReady() +{ + if (!m_devIsInitialized || m_devStatus != CONNECTED) + return false; + + // Generate a GET_STATUS request + u16 trash = 0; // GET_STATUS returns 2 bytes + libusbk::libusb_request req; + memset(&req, 0, sizeof(req)); + req.timeout= 500; + req.status.index = 0; + req.status.recipient = 0x02; + + int res = Ioctl(libusbk::LIBUSB_IOCTL_GET_STATUS, &req, sizeof(req), &trash, sizeof(u16)); + + if (res < 0) // If device's stack is already smashed, res = -141 (timeout) + return false; + else + return true; } int RcmDevice::read(u8* buffer, size_t bufferSize) { - size_t bytesRead; - if (!m_usbApi.ReadPipe(m_usbHandle, 0x81, buffer, bufferSize, &bytesRead, nullptr)) + u32 bytesRead; + if (!m_usbApi.ReadPipe(m_usbHandle, READ_PIPE_ID, buffer, bufferSize, &bytesRead, nullptr)) return -int(GetLastError()); return int(bytesRead); @@ -94,10 +138,10 @@ int RcmDevice::write(const u8* buffer, size_t bufferSize) while (bytesRemaining > 0) { - const size_t bytesToWrite = (bytesRemaining < PACKET_SIZE) ? bytesRemaining : PACKET_SIZE; + const u32 bytesToWrite = (bytesRemaining < PACKET_SIZE) ? bytesRemaining : PACKET_SIZE; m_currentBuffer = (m_currentBuffer == 0) ? 1u : 0u; - size_t bytesTransfered = 0; - if(!m_usbApi.WritePipe(m_usbHandle, 0x01, (u8*)&buffer[bytesWritten], bytesToWrite, &bytesTransfered, nullptr)) + u32 bytesTransfered = 0; + if(!m_usbApi.WritePipe(m_usbHandle, WRITE_PIPE_ID, (u8*)&buffer[bytesWritten], bytesToWrite, &bytesTransfered, nullptr)) return -int(GetLastError()); bytesWritten += bytesTransfered; @@ -107,6 +151,19 @@ int RcmDevice::write(const u8* buffer, size_t bufferSize) return int(bytesWritten); } + +/* + * Fusée Gelée eXploit + * ------------------- + * Vulnerability : CVE-2018-6242 + * Author / Reporter : Katherine Temkin (@ktemkin) + * Affiliation : ReSwitched + * Disclosure : public disclosure planned for June 15th, 2018 ^^ + * Credits : @Qyriad for fusée launcher + * @rajkosto for the Windows reimplementation (TegraRcmSmash) + * + */ + const byte BUILTIN_INTERMEZZO[] = { 0x44, 0x00, 0x9F, 0xE5, 0x01, 0x11, 0xA0, 0xE3, 0x40, 0x20, 0x9F, 0xE5, 0x00, 0x20, 0x42, 0xE0, @@ -117,29 +174,18 @@ const byte BUILTIN_INTERMEZZO[] = 0x5C, 0xF0, 0x01, 0x40, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x01, 0x40 }; -/* - * Fusée Gelée eXploit - * ------------------- - * Author / Reporter : Katherine Temkin (@ktemkin) - * Affiliation : ReSwitched - * Disclosure : public disclosure planned for June 15th, 2018 ^^ - * Credits : @rajkosto for windows reimplementation (TegraRcmSmash) - * - */ RRESULT RcmDevice::hack(const char* user_payload_path) { - // Control user payload ifstream userPayload(user_payload_path, ios::in | ios::binary | ios::ate); + if (!userPayload.is_open()) return OPEN_FILE_FAILED; const auto userPayloadSize = int(userPayload.tellg()); - if (userPayloadSize > 0x1ED58) + if (userPayloadSize > PAYLOAD_MAX_SIZE) return PAYLOAD_TOO_LARGE; - // Read the user payload file into memory & release file userPayload.seekg(0, ios::beg); - char *userPayloadBuffer = new char[userPayloadSize]; userPayload.read(&userPayloadBuffer[0], userPayloadSize); bool error = !(userPayload) || int(userPayload.tellg()) != userPayloadSize; @@ -149,6 +195,32 @@ RRESULT RcmDevice::hack(const char* user_payload_path) delete[] userPayloadBuffer; return OPEN_FILE_FAILED; } + + RRESULT res = hack((u8*)userPayloadBuffer, (u32)userPayloadSize); + + delete[] userPayloadBuffer; + return res; +} + +RRESULT RcmDevice::hack(u8 *payload_buff, u32 buff_size) +{ + if (!m_devIsInitialized) + return DEVICE_NOT_SET; + else if (m_devStatus != CONNECTED) + return DEVICE_DISCONNECTED; + else if (!deviceIsReady()) + return DEVICE_NOT_READY; + + if (buff_size > PAYLOAD_MAX_SIZE) + return PAYLOAD_TOO_LARGE; + + m_currentBuffer = 0; + + // Read device id first. + vector deviceId(0x10, 0); + if (read(&deviceId[0], 0x10) != int(deviceId.size())) + return DEVICE_NOT_READY; + // Inits std::vector payload; // Full payload (relocator + user payload) size_t currOffset = 0; // Payload current offset @@ -189,10 +261,9 @@ RRESULT RcmDevice::hack(const char* user_payload_path) currOffset += paddingSize; // Include the user payload in the command stream - payload.resize(payload.size() + userPayloadSize); - memcpy(&payload[currOffset], &userPayloadBuffer[0], userPayloadSize); - currOffset += userPayloadSize; - delete[] userPayloadBuffer; + payload.resize(payload.size() + buff_size); + memcpy(&payload[currOffset], &payload_buff[0], buff_size); + currOffset += buff_size; // Pad the payload to fill a USB request exactly, so we don't send a short // packet and break out of the RCM loop @@ -211,27 +282,58 @@ RRESULT RcmDevice::hack(const char* user_payload_path) // The RCM backend alternates between two different DMA buffers. Ensure we're about to DMA // into the higher one, so we have less to copy during our attack. - if (switchToHighBuffer() != 0) + // Warning! If device reboot to RCM, resetCurrentBuffer() must be called otherwise we'll swith to lower buffer instead + u32 sres = switchToHighBuffer(); + if (sres != 0 && (sres < 0 || sres != PACKET_SIZE)) return SW_HIGHBUFF_FAILED; - // Smash the stack + // Finally, to trigger the memcpy vulnerability itself, we need to send a + // long length "GET_STATUS" request to the endpoint int stLength = RCM_PAYLOAD_ADDR - getCurrentBufferAddress(); - std::vector threshBuf(stLength, 0); - + std::vector threshBuf(stLength, 0); libusbk::libusb_request req; memset(&req, 0, sizeof(req)); req.timeout= 1000; req.status.index = 0; req.status.recipient = 0x02; + // Request device int res = Ioctl(libusbk::LIBUSB_IOCTL_GET_STATUS, &req, sizeof(req), &threshBuf[0], threshBuf.size()); - + // If stack is smashed, the request will timeout + // If the device is an ipatched unit or Mariko+, the request will likely return 0 (or < 0) if (res <= 0 && -res != ERROR_SEM_TIMEOUT) return STACK_SMASH_FAILED; else return SUCCESS; } +//////////////////////////////////////////// +///// PRIVATE METHODS ///// +//////////////////////////////////////////// + +// Find a plugged USB device matching Tegra RCM pID and vID. +bool RcmDevice::getPluggedDevice(KLST_DEVINFO_HANDLE *devinfo) +{ + if (m_devList != nullptr) + LstK_Free(m_devList); + + u32 devCount = 0; + + if (!LstK_Init(&m_devList, KLST_FLAG_NONE)) + return false; + + LstK_Count(m_devList, &devCount); + + if (devCount == 0 || !LstK_FindByVidPid(m_devList, RCM_VID, RCM_PID, devinfo)) + return false; + + if (devinfo == nullptr) + return false; + + return true; +} + +// Send an IOCTL request to USB endpoint int RcmDevice::Ioctl(DWORD ioctlCode, const void* inputBytes, size_t numInputBytes, void* outputBytes, size_t numOutputBytes) { HANDLE driverHandle = INVALID_HANDLE_VALUE; @@ -255,9 +357,9 @@ int RcmDevice::Ioctl(DWORD ioctlCode, const void* inputBytes, size_t numInputByt if (!GetOverlappedResult(driverHandle, &overlapped, &bytesReceived, true)) return -int(GetLastError()); - return bytesReceived; + return int(bytesReceived); } - +// Switch to higher DMA buffer int RcmDevice::switchToHighBuffer() { if (m_currentBuffer == 0) @@ -271,9 +373,6 @@ int RcmDevice::switchToHighBuffer() return writeRes; } - else - return 0; + else return 0; } - - diff --git a/kourou/libs/rcm_device.h b/kourou/libs/rcm_device.h index 061b7e4..ac4a102 100644 --- a/kourou/libs/rcm_device.h +++ b/kourou/libs/rcm_device.h @@ -1,17 +1,36 @@ +/* + * Copyright (c) 2020 eliboa + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + #ifndef RCMDEVICE_H #define RCMDEVICE_H #include #include "libusbk_int.h" -#include "../types.h" #include +#include "../../types.h" #define PACKET_SIZE 0x1000 +#define PAYLOAD_MAX_SIZE 0x1ED58 static int RCM_VID = 0x0955; static int RCM_PID = 0x7321; +static UCHAR READ_PIPE_ID = 0x81; +static UCHAR WRITE_PIPE_ID = 0x01; -typedef enum _RESULT +typedef enum _RESULT : DWORD { SUCCESS = 0x000, LIBUSBK_WRONG_DRIVER = 0x001, @@ -23,19 +42,28 @@ typedef enum _RESULT USB_WRITE_FAILED = 0x007, SW_HIGHBUFF_FAILED = 0x008, STACK_SMASH_FAILED = 0x009, - DEVICEID_READ_FAILED = 0x00A + DEVICEID_READ_FAILED = 0x00A, + DEVICE_NOT_READY = 0x00B, + DEVICE_NOT_SET = 0x00C, + WRONG_DEVICE_VID_PID = 0x00D, + DEVICE_DISCONNECTED = 0x00E } RRESULT; +typedef enum _DEVICE_STATUS { + DISCONNECTED, + CONNECTED +} DEVICE_STATUS; + class RcmDevice { - -//////////////////////////////////////////// -///// PUBLIC METHODS ///// -//////////////////////////////////////////// public: -//! Rcm device constructor + //! Rcm device constructor RcmDevice(); + ~RcmDevice(); + //////////////////////////////////////////// + ///// PUBLIC METHODS ///// + //////////////////////////////////////////// /*! * \brief Initialize a RCM device. This function must be called at least on time before any other method call @@ -44,7 +72,7 @@ public: * * \returns * - on success, true. - * - on failure, false. + * - on failure, false (use GetLastError() to get error code) */ bool initDevice(KLST_DEVINFO_HANDLE deviceInfo = nullptr); @@ -57,6 +85,17 @@ public: * - on failure, see enum RRESULT */ RRESULT hack(const char* payload_path); + /*! + * \brief Constructs a RCM payload and smash the device stack (fusée gelée exploit) + * \param payload_buff + * Pointer to the payload data + * \param buff_size + * Payload size + * \returns + * - on success, SUCCESS (0) + * - on failure, see enum RRESULT + */ + RRESULT hack(u8 *payload_buff, u32 buff_size); /*! * \brief Read a buffer from USB pipe @@ -79,12 +118,24 @@ public: int write(const u8* buffer, size_t bufferSize); /*! - * \brief Checks whether the device is ready or not + * \brief Check whether the device is ready to receive RCM commands * \return true if device is ready */ - bool isDeviceReady() { return m_b_RcmReady; }; + bool deviceIsReady(); + + //! Get the RCM device status (CONNECTED / DISCONNECTED) + DEVICE_STATUS getStatus() { return m_devStatus; } + //! Set the RCM device status to DISCONNECTED + void disconnect() { m_devStatus = DISCONNECTED; } + + // API Getters + bool flushPipe(UCHAR pipeId) { return m_usbApi.FlushPipe(m_usbHandle, pipeId); } + + // Reset the current buffer to lower one. Use this with caution. + // Current buffer should not be reset manually, except after a controlled RCM reboot er disconnection. + // disconnect() method should be preferred + void resetCurrentBuffer() { m_currentBuffer = 0; } -// member variables private: bool m_is_devicePlugged; KLST_DEVINFO_HANDLE m_devInfo = nullptr; @@ -92,12 +143,12 @@ private: KUSB_DRIVER_API m_usbApi; KLST_HANDLE m_devList = nullptr; u32 m_currentBuffer = 0; - bool m_b_RcmReady; + bool m_devIsInitialized = false; + bool m_usbAPI_loaded = false; + DEVICE_STATUS m_devStatus; -// private methods private: - RRESULT loadDevice(); - bool getPluggedDevice(); + bool getPluggedDevice(KLST_DEVINFO_HANDLE *devinfo); int switchToHighBuffer(); u32 getCurrentBufferAddress() const { return m_currentBuffer == 0 ? 0x40005000u : 0x40009000u; } int Ioctl(DWORD ioctlCode, const void* inputBytes, size_t numInputBytes, void* outputBytes, size_t numOutputBytes); @@ -115,7 +166,6 @@ public: _handle = INVALID_HANDLE_VALUE; } } - void swap(WinHandle&& other) noexcept { std::swap(_handle, other._handle); } WinHandle(WinHandle&& moved) noexcept : _handle(INVALID_HANDLE_VALUE) { swap(std::move(moved)); } diff --git a/kourou/libs/tegrarcm/aes-cmac.cpp b/kourou/libs/tegrarcm/aes-cmac.cpp index da8be5a..24c89f8 100644 --- a/kourou/libs/tegrarcm/aes-cmac.cpp +++ b/kourou/libs/tegrarcm/aes-cmac.cpp @@ -26,9 +26,6 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - -#include "config.h" - #include using std::cout; using std::cerr; @@ -43,29 +40,26 @@ using std::string; #include using std::exit; -#define CRYPTOPP_INCLUDE_CRYPTLIB -#define CRYPTOPP_INCLUDE_CMAC -#define CRYPTOPP_INCLUDE_AES -#define CRYPTOPP_INCLUDE_HEX -#define CRYPTOPP_INCLUDE_FILTERS -#define CRYPTOPP_INCLUDE_SECBLOCK - -#include CRYPTOPP_INCLUDE_CRYPTLIB -#include CRYPTOPP_INCLUDE_CMAC -#include CRYPTOPP_INCLUDE_AES -#include CRYPTOPP_INCLUDE_HEX -#include CRYPTOPP_INCLUDE_FILTERS -#include CRYPTOPP_INCLUDE_SECBLOCK - +#include "cryptlib.h" using CryptoPP::Exception; + +#include "cmac.h" using CryptoPP::CMAC; + +#include "aes.h" using CryptoPP::AES; + +#include "hex.h" using CryptoPP::HexEncoder; using CryptoPP::HexDecoder; + +#include "filters.h" using CryptoPP::StringSink; using CryptoPP::StringSource; using CryptoPP::HashFilter; using CryptoPP::HashVerificationFilter; + +#include "secblock.h" using CryptoPP::SecByteBlock; extern "C" int cmac_hash(const unsigned char *msg, int len, unsigned char *cmac_buf) diff --git a/kourou/libs/tegrarcm/nv3p.h b/kourou/libs/tegrarcm/nv3p.h index d1ce78e..e3498bc 100644 --- a/kourou/libs/tegrarcm/nv3p.h +++ b/kourou/libs/tegrarcm/nv3p.h @@ -137,7 +137,7 @@ typedef struct { } nv3p_cmd_status_t; /* - * nv3p_platform_info_t: retrieves the system information. All parameters + * nv3p_platform_info_t: retrieves the system information. All paramters * are output parameters. */ typedef struct { diff --git a/kourou/libs/tegrarcm/rcm.h b/kourou/libs/tegrarcm/rcm.h index d5fef30..7c07d4c 100644 --- a/kourou/libs/tegrarcm/rcm.h +++ b/kourou/libs/tegrarcm/rcm.h @@ -113,6 +113,10 @@ typedef struct { #define RCM_OP_MODE_ODM_OPEN 0x5 #define RCM_OP_MODE_ODM_SECURE_PKC 0x6 +#ifdef __cplusplus +extern "C" { +#endif + int rcm_init(uint32_t version, const char *keyfile); uint32_t rcm_get_msg_len(uint8_t *msg); int rcm_create_msg( @@ -123,4 +127,8 @@ int rcm_create_msg( uint32_t payload_len, uint8_t **msg); +#ifdef __cplusplus +} +#endif + #endif // _RCM_H diff --git a/kourou/libs/tegrarcm/rsa-pss.cpp b/kourou/libs/tegrarcm/rsa-pss.cpp index 7e6f066..ab0a680 100644 --- a/kourou/libs/tegrarcm/rsa-pss.cpp +++ b/kourou/libs/tegrarcm/rsa-pss.cpp @@ -26,9 +26,6 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - -#include "config.h" - #include using std::cout; using std::cerr; @@ -43,39 +40,36 @@ using std::string; #include using std::exit; -#define CRYPTOPP_INCLUDE_CRYPTLIB -#define CRYPTOPP_INCLUDE_FILES -#define CRYPTOPP_INCLUDE_FILTERS -#define CRYPTOPP_INCLUDE_INTEGER -#define CRYPTOPP_INCLUDE_OSRNG -#define CRYPTOPP_INCLUDE_QUEUE -#define CRYPTOPP_INCLUDE_RSA -#define CRYPTOPP_INCLUDE_PSSR -#define CRYPTOPP_INCLUDE_SHA -#define CRYPTOPP_INCLUDE_SECBLOCK - -#include CRYPTOPP_INCLUDE_CRYPTLIB -#include CRYPTOPP_INCLUDE_FILES -#include CRYPTOPP_INCLUDE_FILTERS -#include CRYPTOPP_INCLUDE_INTEGER -#include CRYPTOPP_INCLUDE_OSRNG -#include CRYPTOPP_INCLUDE_QUEUE -#include CRYPTOPP_INCLUDE_RSA -#include CRYPTOPP_INCLUDE_PSSR -#include CRYPTOPP_INCLUDE_SHA -#include CRYPTOPP_INCLUDE_SECBLOCK - +#include "cryptlib.h" using CryptoPP::Exception; + +#include "integer.h" using CryptoPP::Integer; + +#include "files.h" using CryptoPP::FileSource; + +#include "filters.h" using CryptoPP::StringSink; using CryptoPP::SignerFilter; + +#include "queue.h" using CryptoPP::ByteQueue; + +#include "rsa.h" using CryptoPP::RSA; using CryptoPP::RSASS; + +#include "pssr.h" using CryptoPP::PSS; + +#include "sha.h" using CryptoPP::SHA256; + +#include "secblock.h" using CryptoPP::SecByteBlock; + +#include "osrng.h" using CryptoPP::AutoSeededRandomPool; #include "rsa-pss.h" diff --git a/kourou/libs/tegrarcm/usb.c b/kourou/libs/tegrarcm/usb.c index 5bed1ef..68070a3 100644 --- a/kourou/libs/tegrarcm/usb.c +++ b/kourou/libs/tegrarcm/usb.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2016, NVIDIA CORPORATION +< * Copyright (c) 2011-2016, NVIDIA CORPORATION * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -76,6 +76,7 @@ static int usb_match(libusb_device *dev, uint16_t venid, uint16_t *devid } switch (desc.idProduct & 0xff) { case USB_DEVID_NVIDIA_TEGRA20: + case USB_DEVID_NINTENDO_SWITCH: case USB_DEVID_NVIDIA_TEGRA30: case USB_DEVID_NVIDIA_TEGRA114: case USB_DEVID_NVIDIA_TEGRA124: diff --git a/kourou/libs/tegrarcm/usb.h b/kourou/libs/tegrarcm/usb.h index e8fcd67..ceca124 100644 --- a/kourou/libs/tegrarcm/usb.h +++ b/kourou/libs/tegrarcm/usb.h @@ -32,6 +32,9 @@ #include #include +#define MIN(a,b) (((a)<(b))?(a):(b)) +#define MAX(a,b) (((a)>(b))?(a):(b)) + #if defined(LIBUSBX_API_VERSION) && (LIBUSBX_API_VERSION >= 0x01000102) #define HAVE_USB_PORT_MATCH #define PORT_MATCH_MAX_PORTS 7 @@ -39,10 +42,14 @@ #define USB_VENID_NVIDIA 0x955 #define USB_DEVID_NVIDIA_TEGRA20 0x20 +#define USB_DEVID_NINTENDO_SWITCH 0x21 #define USB_DEVID_NVIDIA_TEGRA30 0x30 #define USB_DEVID_NVIDIA_TEGRA114 0x35 #define USB_DEVID_NVIDIA_TEGRA124 0x40 +#define MIN(a,b) (((a)<(b))?(a):(b)) +#define MAX(a,b) (((a)>(b))?(a):(b)) + typedef struct { libusb_device_handle *handle; uint8_t iface_num; diff --git a/kourou/usb_command.h b/kourou/usb_command.h index ee05a6e..1236b9a 100644 --- a/kourou/usb_command.h +++ b/kourou/usb_command.h @@ -1,13 +1,24 @@ -#pragma once -#include "windows.h" +#ifndef UCOMMAND_H +#define UCOMMAND_H typedef unsigned char u8; typedef unsigned short u16; typedef unsigned int u32; typedef unsigned long long int u64; +typedef unsigned long DWORD; -#define COMMAND 0x5543 -#define BIN_PACKET 0x4249 -#define EXEC_COMMAND 0x4558 +#define COMMAND 0x5543 +#define BIN_PACKET 0x4249 +#define EXEC_COMMAND 0x4558 +#define RESPONSE 0x5245 +#define DEVINFO 0x4445 + +#define MAX_FILE_SIZE 0x6400000 //100MB +#define SUCCESS 0 +#define USB_BUFFER_LENGTH 0x1000 +#define RESPONSE_MAX_SIZE 0x20 + +#define FS_FAT32 3 +#define FS_EXFAT 4 typedef enum _UC_CommandType : u8 { @@ -16,7 +27,9 @@ typedef enum _UC_CommandType : u8 READ_SD_FILE, WRITE_SD_FILE, PUSH_PAYLOAD, - REBOOT_RCM + REBOOT_RCM, + GET_DEVICE_INFO, + GET_STATUS } UC_CommandType; @@ -50,7 +63,38 @@ typedef struct _UC_EXEC typedef struct _UC_BlockHeader { u16 signature = BIN_PACKET; - int block_size; - int block_full_size; // Full size if LZ4 compressed + u32 block_size; + u32 block_full_size; // Full size if LZ4 compressed } UC_BlockHeader; + +typedef struct _UC_DeviceInfo +{ + u16 signature = DEVINFO; // UC signature + u32 battery_capacity; // Fuel gauge + bool autoRCM; // autoRCM state + u32 burnt_fuses; // Number of burnt fuses + bool sdmmc_initialized; // MMC FS initialized + u8 emmc_fs_type; // 3 is FAT32, 4 is exFAT + u16 emmc_fs_cl_size; // Cluster size in sectors (always 512B per sectors) + DWORD emmc_fs_last_cl; // Last allocated cluster + DWORD emmc_fs_free_cl; // Number of free cluster + bool cfw_sxos; // SX OS bootloader + bool cfw_ams; // AMS fusee + bool cbl_hekate; // Hekate + +} UC_DeviceInfo; + +/*--------------*/ +/* ERROR CODES */ +/*--------------*/ +// FS +#define OPEN_FILE_FAILED -0x0001 +#define FILE_TOO_LARGE -0x0002 +#define PATH_TOO_LONG -0x0003 +// COM +#define SEND_COMMAND_FAILED -0x1001 +#define RECEIVE_COMMAND_FAILED -0x1002 +#define USB_WRITE_FAILED -0x1003 + +#endif // UCOMMAND_H diff --git a/main.cpp b/main.cpp index 2853cf7..df6157c 100644 --- a/main.cpp +++ b/main.cpp @@ -1,11 +1,20 @@ #include "tegrarcmgui.h" #include +#include + int main(int argc, char *argv[]) { + QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QApplication a(argc, argv); + a.setStyleSheet("QMainWindow{ border-radius: 20px; }"); + QTranslator appTranslator; + //appTranslator.load(QLocale(), QLatin1String("tegrarcmgui"), QLatin1String("_"), QLatin1String(":/i18n")); + appTranslator.load("tegrarcmgui_fr", "languages"); + a.installTranslator(&appTranslator); TegraRcmGUI w; + w.setWindowFlags(Qt::Window | Qt::FramelessWindowHint); w.show(); return a.exec(); } diff --git a/qkourou.cpp b/qkourou.cpp index 6dc40c9..302aa9e 100644 --- a/qkourou.cpp +++ b/qkourou.cpp @@ -1,6 +1,206 @@ #include "qkourou.h" +#include +#include +#include +#include -QKourou::QKourou() +QKourou::QKourou(QWidget *parent, Kourou* device, TegraRcmGUI* gui) : QWidget(parent), m_device(device), parent(parent), m_gui(gui) { + qRegisterMetaType("UC_DeviceInfo"); + connect(this, SIGNAL(clb_deviceInfo(UC_DeviceInfo)), parent, SLOT(on_deviceInfo_received(UC_DeviceInfo))); + connect(this, SIGNAL(clb_error(int)), parent, SLOT(error(int))); + connect(this, SIGNAL(clb_deviceStateChange()), parent, SLOT(on_deviceStateChange())); + connect(this, SIGNAL(clb_finished(int)), parent, SLOT(on_Kourou_finished(int))); +} + +void QKourou::initDevice(bool silent, KLST_DEVINFO_HANDLE deviceInfo) +{ + if (!waitUntilUnlock()) + return; + + DWORD error = 0; + bool devInfoSync = false; + UC_DeviceInfo di; + + setLockEnabled(true); + + if (!m_device->initDevice(deviceInfo)) + error = GetLastError(); + + emit clb_deviceStateChange(); + + bool b_autoInject = false; + if (!error) + { + // Do auto inject if needed + DWORD autoErr = autoInject(); + b_autoInject = !autoErr ? true : false; + error = autoErr != WRONG_PARAM_GENERIC ? autoErr : error; + + // Auto boot Ariane + if (!error && !b_autoInject && !m_device->arianeIsReady() && autoLaunchAriane && ariane_bin.size()) + { + arianeIsLoading = true; + error = m_device->hack((u8*)ariane_bin.data(), (u32)ariane_bin.size()); + if (!error) + m_device->arianeIsReady_sync(); + + arianeIsLoading = false; + } + } + + setLockEnabled(false); + + if (error && !silent) + emit clb_error(int(error)); + + if (!error && b_autoInject) + emit clb_finished(AUTO_INJECT); + + // Get device info if ariane is ready + if (m_device->arianeIsReady()) + devInfoSync = !(m_device->getDeviceInfo(&di)); + + setLockEnabled(false); + emit clb_deviceStateChange(); + + if (devInfoSync) + emit clb_deviceInfo(di); +} + +DWORD QKourou::autoInject() +{ + if (!autoInjectPayload) + return WRONG_PARAM_GENERIC; + + QString path = m_gui->userSettings->value("autoInjectPath").toString(); + if (!path.length()) + return WRONG_PARAM_GENERIC; + + if (!m_device->rcmIsReady() && !m_device->arianeIsReady()) + return DEVICE_NOT_READY; + + tmp_string.clear(); + tmp_string.append(path.toStdString()); + DWORD err = 0; + + // Reboot if ariane is loaded + if (m_device->arianeIsReady() && !rebootToRcm()) + err = GetLastError(); + + // Push payload + if (!err) err = m_device->hack(tmp_string.c_str()); + + if (!err && !m_device->deviceIsReady()) + m_device->setRcmReady(false); + + return err; +} + +void QKourou::getDeviceInfo() +{ + qDebug() << "QKourou::getDeviceInfo() execute in " << QThread::currentThreadId(); + + if (!waitUntilUnlock()) + return; + + UC_DeviceInfo di; + + setLockEnabled(true); + int res = m_device->getDeviceInfo(&di); + setLockEnabled(false); + + if (res) + emit clb_error(res); + else + emit clb_deviceInfo(di); } + +void QKourou::hack(const char* payload_path, u8 *payload_buff, u32 buff_size) +{ + if (!waitUntilUnlock()) + return; + + DWORD res = 0; + setLockEnabled(true); + + // Reboot if ariane is loaded + if (m_device->arianeIsReady() && !rebootToRcm()) + res = GetLastError(); + + // Push payload + if(!res) res = payload_path != nullptr ? m_device->hack(payload_path) : m_device->hack(payload_buff, buff_size); + + if (!res && !m_device->deviceIsReady()) + m_device->setRcmReady(false); + + setLockEnabled(false); + + if (res) + emit clb_error(int(res)); + else + emit clb_finished(PAYLOAD_INJECT); + + emit clb_deviceStateChange(); +} + +void QKourou::hack(const char* payload_path) +{ + hack(payload_path, nullptr, 0); +} +void QKourou::hack(u8 *payload_buff, u32 buff_size) +{ + hack(nullptr, payload_buff, buff_size); +} + +bool QKourou::rebootToRcm() +{ + DWORD err = !m_device->rebootToRcm() ? RCM_REBOOT_FAILED : 0; + + if (!err) + { + m_device->disconnect(); + if(!waitUntilInit(3)) + err = RCM_REBOOT_FAILED; + } + + if (err) + SetLastError(err); + + return !err ? true : false; +} + +bool QKourou::waitUntilUnlock(uint timeout_s) +{ + qint64 begin_timestamp = QDateTime::currentSecsSinceEpoch(); + while(isLocked()) + { + if (QDateTime::currentSecsSinceEpoch() > begin_timestamp + timeout_s) + return false; + } + return true; +} + +bool QKourou::waitUntilRcmReady(uint timeout_s) +{ + qint64 begin_timestamp = QDateTime::currentSecsSinceEpoch(); + while(!m_device->deviceIsReady()) + { + if (QDateTime::currentSecsSinceEpoch() > begin_timestamp + timeout_s) + return false; + } + return true; +} + + +bool QKourou::waitUntilInit(uint timeout_s) +{ + qint64 begin_timestamp = QDateTime::currentSecsSinceEpoch(); + while(!m_device->initDevice()) + { + if (QDateTime::currentSecsSinceEpoch() > begin_timestamp + timeout_s) + return false; + } + return true; +} diff --git a/qkourou.h b/qkourou.h index 601d7a4..a29cba3 100644 --- a/qkourou.h +++ b/qkourou.h @@ -3,11 +3,65 @@ #include #include +#include +#include "kourou/kourou.h" +#include "tegrarcmgui.h" -class QKourou +typedef enum _qKourouAction : int { + INIT_DEVICE, + AUTO_INJECT, + PAYLOAD_INJECT + +} qKourouAction; + + +class TegraRcmGUI; + +/// +/// \brief The QKourou class is a Qt thread safe reimplemented class of Kourou class, for TegraRcmGUI +/// + +class QKourou : public QWidget { + Q_OBJECT + public: - QKourou(); + QKourou(QWidget *parent, Kourou* device, TegraRcmGUI* gui); + bool isLocked() { return m_locked; } + QByteArray ariane_bin; + bool autoLaunchAriane = true; + bool autoInjectPayload = false; + bool arianeIsLoading = false; + +private: + Kourou *m_device; + TegraRcmGUI *m_gui; + bool m_locked = false; + bool m_force_lock = false; + QWidget *parent; + std::string tmp_string; + void hack(const char* payload_path, u8 *payload_buff, u32 buff_size); + void setLockEnabled(bool enable) { m_locked = enable; } + bool waitUntilUnlock(uint timeout_s = 10); + bool waitUntilRcmReady(uint timeout_s = 10); + bool waitUntilInit(uint timeout_s = 10); + bool rebootToRcm(); + DWORD autoInject(); + + +public slots: + void initDevice(bool silent, KLST_DEVINFO_HANDLE deviceInfo = nullptr); + void getDeviceInfo(); + void hack(const char* payload_path); + void hack(u8 *payload_buff, u32 buff_size); + + +signals: + void clb_deviceInfo(UC_DeviceInfo di); + void clb_error(int error); + void clb_deviceStateChange(); + void clb_finished(int res); + }; #endif // QKOUROU_H diff --git a/qpayload.cpp b/qpayload.cpp index 4cbe802..c147102 100644 --- a/qpayload.cpp +++ b/qpayload.cpp @@ -1,6 +1,386 @@ +#include #include "qpayload.h" +#include "ui_qpayload.h" +#include "qutils.h" -QPayloadWidget::QPayloadWidget(QWidget *parent) : QWidget(parent) +///////////////////////////////////////////////////////////// +/// \brief QPayloadWidget::QPayloadWidget +/// \param parent +///////////////////////////////////////////////////////////// + +QPayloadWidget::QPayloadWidget(TegraRcmGUI *parent) : QWidget(parent) + , ui(new Ui::QPayloadWidget), parent(parent) +{ + ui->setupUi(this); + m_kourou = parent->m_kourou; + m_device = &parent->m_device; + connect(this, SIGNAL(error(int)), parent, SLOT(error(int))); + + // Payload model + m_payloadModel = new PayloadModel(this); + /// Populate model + readFavoritesFromFile(); + /// Connect model to table view + ui->payload_tableView->setModel(m_payloadModel); + + // Payload view + const auto view = ui->payload_tableView; + view->verticalHeader()->hide(); + view->horizontalHeader()->hide(); + view->hideColumn(1); + view->setColumnWidth(0, view->width()); + view->setAlternatingRowColors(true); + view->show(); + + auto item = ui->payload_tableView->findChildren(); + for (int i = 0; i < item.count(); i++) + { + item.at(i)->setStatusTip("test"); + } + + + // Timers + QTimer *payloadBtnStatus_timer = new QTimer(this); + connect(payloadBtnStatus_timer, SIGNAL(timeout()), this, SLOT(payloadBtnStatusTimer())); + payloadBtnStatus_timer->start(1000); // Every second + + + + /// Stylesheets + // Apply stylesheet to all buttons + QString btnSs = GetStyleSheetFromResFile(":/res/QPushButton.qss"); + auto buttons = this->findChildren(); + for (int i = 0; i < buttons.count(); i++) + { + buttons.at(i)->setStyleSheet(btnSs); + buttons.at(i)->setCursor(Qt::PointingHandCursor); + + } + ui->payload_tableView->setStyleSheet(GetStyleSheetFromResFile(":/res/QTableView.qss")); + ui->payloadFrame->setStyleSheet(GetStyleSheetFromResFile(":/res/QFrame_box02.qss")); + + // Buttons + Switch *_switch = new Switch(parent->m_kourou->autoInjectPayload ? true : false, 70); + ui->horizontalLayout->addWidget(_switch); + connect(_switch, SIGNAL(clicked()), this, SLOT(on_autoInject_toggled())); + //ui->injectPayloadBtn->setCursor(Qt::PointingHandCursor); + //ui->browsePayloadBtn->setCursor(Qt::PointingHandCursor); + + // ToolTip + ui->addFavoriteBtn->setStatusTip(tr("Add current payload selection to favorites")); + ui->deleteFavoriteBtn->setStatusTip(tr("Remove selected item from favorites (payload file will not be deleted)")); + + QString ppath = parent->userSettings->value("autoInjectPath").toString(); + ui->payload_path->setText(ppath); + +} +void QPayloadWidget::payloadBtnStatusTimer() +{ + qint64 time = QDateTime::currentSecsSinceEpoch(); + if (payloadBtnStatusLbl_time && (time + 5 > payloadBtnStatusLbl_time)) + { + ui->payloadBtnStatusLbl->setText(""); + payloadBtnStatusLbl_time = 0; + } +} +void QPayloadWidget::on_browsePayloadBtn_clicked() +{ + QString path = FileDialog(this, open_file).toLocal8Bit(); + if (path.length()) + ui->payload_path->setText(path); +} + +void QPayloadWidget::on_injectPayloadBtn_clicked() +{ + if (!m_device->rcmIsReady() && !m_device->arianeIsReady()) + return; + + if (payloadBtnLock) + { + ui->payloadBtnStatusLbl->setText(tr("Work in progress")); + payloadBtnStatusLbl_time = QDateTime::currentSecsSinceEpoch(); + return; + } + + QFile file(ui->payload_path->text().toLocal8Bit().constData()); + if (!file.open(QIODevice::ReadOnly)) + return parent->error(0x005); + if (file.size() > PAYLOAD_MAX_SIZE) + { + file.close(); + return parent->error(PAYLOAD_TOO_LARGE); + } + file.close(); + + tmp_string.clear(); + tmp_string.append(ui->payload_path->text().toStdString()); + payloadBtnLock = true; + QtConcurrent::run(m_kourou, &QKourou::hack, tmp_string.c_str()); +} + +void QPayloadWidget::on_deviceStateChange() +{ + if ((m_device->rcmIsReady() && !m_kourou->autoLaunchAriane) || m_device->arianeIsReady()) + { + payloadBtnLock = false; + ui->injectPayloadBtn->setCursor(Qt::PointingHandCursor); + ui->injectPayloadBtn->setStatusTip("Inject current payload to device"); + if (m_payloadModel->rowCount()) + ui->payload_tableView->setStatusTip(tr("Double-click to inject payload")); + } + else + { + payloadBtnLock = true; + QString tip = m_device->getStatus() == CONNECTED ? tr("RCM device is not ready. Reboot to RCM") : tr("RCM device undetected!"); + if (m_kourou->arianeIsLoading) + tip = tr("Wait for Ariane to be fully loaded"); + //ui->injectPayloadBtn->setToolTip(tip); + ui->injectPayloadBtn->setStatusTip(tip); + ui->injectPayloadBtn->setCursor(Qt::ForbiddenCursor); + if (m_payloadModel->rowCount()) + ui->payload_tableView->setStatusTip(tr("Double-click to push favorite into current selection")); + } +} + +bool QPayloadWidget::addFavorite(QString name, QString path) +{ + QFile file(path); + + if (!file.exists()) + return false; + + if (!m_payloadModel->getPayloads().contains({ name, path })) + { + m_payloadModel->insertRows(0, 1, QModelIndex()); + QModelIndex index = m_payloadModel->index(0, 0, QModelIndex()); + m_payloadModel->setData(index, name, Qt::EditRole); + index = m_payloadModel->index(0, 1, QModelIndex()); + m_payloadModel->setData(index, path, Qt::EditRole); + return true; + } + return false; +} + +void QPayloadWidget::on_addFavoriteBtn_clicked() +{ + QString path = ui->payload_path->text(); + if (!path.size()) + { + QMessageBox::information(this, tr("Argument missing"), tr("Payload path is missing")); + return; + } + QFile file(path); + if (!file.exists()) + { + QMessageBox::information(this, tr("File missing"), tr("Payload path does not point to a file")); + return; + } + + QFileInfo info(path); + QString name = info.fileName(); + + if (m_payloadModel->getPayloads().contains({ path, name })) + { + QMessageBox::information(this, tr("Duplicate Path"), tr("Payload already in favorites").arg(name)); + return; + } + + if(addFavorite(name, path)) + writeFavoritesToFile(); +} + +void QPayloadWidget::on_deleteFavoriteBtn_clicked() +{ + int row = ui->payload_tableView->currentIndex().row(); + if (row >= 0 && !m_payloadModel->removeRows(row, 1, QModelIndex())) + emit error(-1); + + writeFavoritesToFile(); +} + +void QPayloadWidget::on_autoInject_toggled() +{ + m_kourou->autoInjectPayload = !m_kourou->autoInjectPayload; + parent->userSettings->setValue("autoInject", m_kourou->autoInjectPayload); + + if (m_kourou->autoInjectPayload && (m_device->arianeIsReady() || m_device->rcmIsReady())) + QtConcurrent::run(m_kourou, &QKourou::initDevice, false, nullptr); +} + +void QPayloadWidget::on_payload_path_textChanged(const QString &arg1) +{ + if (!ui->payload_path->text().length()) + return; + + QString curPath = parent->userSettings->value("autoInjectPath").toString(); + + if (curPath == ui->payload_path->text()) + return; + + parent->userSettings->setValue("autoInjectPath", ui->payload_path->text()); + + if (m_kourou->autoInjectPayload && (m_device->arianeIsReady() || m_device->rcmIsReady())) + QtConcurrent::run(m_kourou, &QKourou::initDevice, false, nullptr); +} + + +bool QPayloadWidget::readFavoritesFromFile() +{ + QFile pFile(QStringLiteral("payloads.json")); + + if (!pFile.open(QIODevice::ReadOnly)) + return false; + + QByteArray pData = pFile.readAll(); + + pFile.close(); + + QJsonDocument loadDoc(QJsonDocument::fromJson(pData)); + + QJsonObject json = loadDoc.object(); + + QJsonArray pArray = json["favorites"].toArray(); + int i(0); + for (i = 0; i < pArray.size(); i++) + { + QJsonObject payloadEntry = pArray[i].toObject(); + QString payload_name = payloadEntry["name"].toString(); + QString payload_path = payloadEntry["path"].toString(); + addFavorite(payload_name, payload_path); + } + + return i ? true : false; + +} +bool QPayloadWidget::writeFavoritesToFile() +{ + + QFile pFile(QStringLiteral("payloads.json")); + + if (!pFile.open(QIODevice::WriteOnly)) + return false; + + QJsonArray pArray; + QVector payloads = m_payloadModel->getPayloads(); + for (payload_t paylaod : payloads) + { + QJsonObject payloadEntry; + payloadEntry["name"] = paylaod.name; + payloadEntry["path"] = paylaod.path; + pArray.append(payloadEntry); + } + QJsonObject json; + json["favorites"] = pArray; + QJsonDocument saveDoc(json); + pFile.write(saveDoc.toJson()); + + pFile.close(); + + return true; +} + + +///////////////////////////////////////////////////////////// +/// \brief PayloadModel::PayloadModel +/// \param parent +//////////////////////////////////////////////////////////// + +PayloadModel::PayloadModel(QObject *parent) + : QAbstractTableModel(parent) { } + +int PayloadModel::rowCount(const QModelIndex &parent) const +{ + return parent.isValid() ? 0 : int(payloads.size()); +} + +int PayloadModel::columnCount(const QModelIndex &parent) const +{ + return parent.isValid() ? 0 : 2; +} + +QVariant PayloadModel::data(const QModelIndex &index, int role) const +{ + + if (!index.isValid()) + return QVariant(); + + if (index.row() >= payloads.size() || index.row() < 0) + return QVariant(); + + if (role == Qt::DisplayRole) + { + const auto &payload = payloads.at(index.row()); + return !index.column() ? payload.name : payload.path; + } + + return QVariant(); +} + +bool PayloadModel::insertRows(int position, int rows, const QModelIndex &index) +{ + Q_UNUSED(index) + beginInsertRows(QModelIndex(), position, position + rows - 1); + + for (int row = 0; row < rows; ++row) + payloads.insert(position, { QString(), QString() }); + + endInsertRows(); + return true; +} + +bool PayloadModel::removeRows(int position, int rows, const QModelIndex &index) +{ + Q_UNUSED(index) + beginRemoveRows(QModelIndex(), position, position + rows - 1); + + for (int row = 0; row < rows; ++row) + payloads.removeAt(position); + + endRemoveRows(); + return true; +} + +bool PayloadModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (index.isValid() && role == Qt::EditRole) + { + const int row = index.row(); + auto payload = payloads.value(row); + + if (!index.column()) + payload.name = value.toString(); + else + payload.path = value.toString(); + + payloads.replace(row, payload); + emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole}); + + return true; + } + + return false; +} + +Qt::ItemFlags PayloadModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return Qt::ItemIsEnabled; + + return QAbstractTableModel::flags(index) | Qt::ItemIsEditable; +} + +const QVector &PayloadModel::getPayloads() const +{ + return payloads; +} + + +void QPayloadWidget::on_payload_tableView_doubleClicked(const QModelIndex &index) +{ + QString path = m_payloadModel->getPayloads().at(index.row()).path; + ui->payload_path->setText(path); + on_injectPayloadBtn_clicked(); +} diff --git a/qpayload.h b/qpayload.h index 5f9f5d8..8776892 100644 --- a/qpayload.h +++ b/qpayload.h @@ -1,18 +1,101 @@ #ifndef QPAYLOADWIDGET_H #define QPAYLOADWIDGET_H - #include #include +#include +#include +#include +#include "tegrarcmgui.h" + +struct payload_t +{ + QString path; + QString name; + + bool operator==(const payload_t &other) const + { + return path == other.path; + } +}; + +inline QDataStream &operator<<(QDataStream &stream, const payload_t &payload) +{ + return stream << payload.name << payload.path; +} + +inline QDataStream &operator>>(QDataStream &stream, payload_t &payload) +{ + return stream >> payload.name >> payload.path; +} + + +class TegraRcmGUI; +class Kourou; +class QKourou; + +class PayloadModel : public QAbstractTableModel +{ + Q_OBJECT +public: + PayloadModel(QObject *parent = nullptr); + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; + bool insertRows(int position, int rows, const QModelIndex &index) override; + bool removeRows(int position, int rows, const QModelIndex &index) override; + Qt::ItemFlags flags(const QModelIndex &index) const override; + const QVector &getPayloads() const; + +private: + QVector payloads; +}; + +QT_BEGIN_NAMESPACE +namespace Ui { class QPayloadWidget; } +QT_END_NAMESPACE class QPayloadWidget : public QWidget { Q_OBJECT public: - explicit QPayloadWidget(QWidget *parent = nullptr); + explicit QPayloadWidget(TegraRcmGUI *parent = nullptr); + std::string tmp_string; + const QVector &getPayloads() const { return m_payloadModel->getPayloads(); } + +private: + Ui::QPayloadWidget *ui; + PayloadModel *m_payloadModel; + QFileSystemModel *m_fsModel; + TegraRcmGUI *parent; + QKourou *m_kourou; + Kourou *m_device; + qint64 payloadBtnStatusLbl_time = 0; + bool payloadBtnLock = false; + + bool addFavorite(QString name, QString path); + bool readFavoritesFromFile(); + bool writeFavoritesToFile(); signals: public slots: + void on_deviceStateChange(); + +private slots: + void on_browsePayloadBtn_clicked(); + void on_injectPayloadBtn_clicked(); + void on_addFavoriteBtn_clicked(); + void on_deleteFavoriteBtn_clicked(); + void on_autoInject_toggled(); + void payloadBtnStatusTimer(); + + void on_payload_path_textChanged(const QString &arg1); + + void on_payload_tableView_doubleClicked(const QModelIndex &index); + +signals: + void error(int); }; #endif // QPAYLOADWIDGET_H diff --git a/qpayload.ui b/qpayload.ui index 9580b0b..3bc2504 100644 --- a/qpayload.ui +++ b/qpayload.ui @@ -1,22 +1,285 @@ - - - - - Form - - + + + QPayloadWidget + + 0 0 - 400 - 300 + 496 + 380 - + Form + + + + + + + 20 + 150 + 360 + 145 + + + + + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::DropOnly + + + Qt::MoveAction + + + QAbstractItemView::SingleSelection + + + false + + + false + + + false + + + + + + 20 + 35 + 421 + 81 + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + 340 + 10 + 71 + 20 + + + + + + + Browse + + + + + + 0 + 10 + 50 + 20 + + + + font: 10pt "Calibri"; +color: rgb(255, 255, 255); + + + Path: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + 250 + 50 + 81 + 16 + + + + font: 10pt "Calibri"; +color: rgb(255, 255, 255); + + + Auto inject: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + 340 + 40 + 71 + 31 + + + + + + + + 10 + 40 + 101 + 30 + + + + + + + INJECT PAYLOAD + + + + + + 55 + 10 + 281 + 20 + + + + + + + + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + 120 + 45 + 131 + 21 + + + + font: 10pt "Calibri"; + + + + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + 20 + 15 + 131 + 20 + + + + font: 11pt "Calibri"; + + + + Payload selection: + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + 390 + 150 + 31 + 31 + + + + + + + + + + + :/res/add.ico:/res/add.ico + + + + + + 390 + 190 + 31 + 31 + + + + + + + + + + + :/res/delete.ico:/res/delete.ico + + + + + + 20 + 130 + 120 + 20 + + + + font: 11pt "Calibri"; + + + + Favorites: + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + payload_tableView + payloadFrame + addFavoriteBtn + deleteFavoriteBtn + favoritesLbl + payloadPathLbl - + + + - diff --git a/qresources.qrc b/qresources.qrc index 90f4a83..a2ac1b2 100644 --- a/qresources.qrc +++ b/qresources.qrc @@ -1,2 +1,17 @@ - - + + + ariane/out/ariane.bin + res/switch_logo_off.png + res/switch_logo_on.png + res/QTabWidget.qss + res/QPushButton.qss + res/QLabel_title01.qss + res/QTableView.qss + res/QFrame_box01.qss + res/QMainWindow.qss + res/QFrame_titleBar.qss + res/QFrame_box02.qss + res/delete.ico + res/add.ico + + diff --git a/qtools.ui b/qtools.ui index 32f5b5c..29fa67f 100644 --- a/qtools.ui +++ b/qtools.ui @@ -1,9 +1,7 @@ + - - - qTools - + 0 @@ -15,7 +13,20 @@ Form + + + + 50 + 30 + 47 + 14 + + + + TextLabel + + - + diff --git a/qutils.cpp b/qutils.cpp index 1d45db1..b88000e 100644 --- a/qutils.cpp +++ b/qutils.cpp @@ -1,5 +1,6 @@ #include "qutils.h" + QString FileDialog(QWidget *parent, fdMode mode, const QString& defaultName) { QFileDialog fd(parent); @@ -21,3 +22,136 @@ QString FileDialog(QWidget *parent, fdMode mode, const QString& defaultName) } return filePath; } + +QString GetReadableSize(qint64 bytes) +{ + return QLocale().formattedDataSize(bytes); +} + +Switch::Switch(bool initial_state, int width, QBrush brush, QWidget *parent) : QAbstractButton(parent), +_height(16), +_opacity(0.000), +_switch(initial_state), +_margin(3), +_thumb("#d5d5d5"), +_width(width), +_anim(new QPropertyAnimation(this, "offset", this)) +{ + if (!initial_state) + { + setOffset(_height / 2); + } + else + { + setOffset(_width - _height); + _thumb = brush; + } + _y = _height / 2; + setBrush(brush); +} + +Switch::Switch(const QBrush &brush, QWidget *parent) : QAbstractButton(parent), +_height(16), +_switch(false), +_opacity(0.000), +_margin(3), +_thumb("#d5d5d5"), +_anim(new QPropertyAnimation(this, "offset", this)) +{ + setOffset(_height / 2); + _y = _height / 2; + setBrush(brush); +} + +void Switch::paintEvent(QPaintEvent *e) { + QPainter p(this); + p.setPen(Qt::NoPen); + if (isEnabled()) { + p.setBrush(_switch ? brush() : Qt::black); + p.setOpacity(_switch ? 0.5 : 0.38); + p.setRenderHint(QPainter::Antialiasing, true); + p.drawRoundedRect(QRect(_margin, _margin, _width - 2 * _margin, height() - 2 * _margin), 8.0, 8.0); + p.setBrush(_thumb); + p.setOpacity(1.0); + p.drawEllipse(QRectF(offset() - (_height / 2), _y - (_height / 2), height(), height())); + + } else { + p.setBrush(Qt::black); + p.setOpacity(0.12); + p.drawRoundedRect(QRect(_margin, _margin, _width - 2 * _margin, height() - 2 * _margin), 8.0, 8.0); + p.setOpacity(1.0); + p.setBrush(QColor("#BDBDBD")); + p.drawEllipse(QRectF(offset() - (_height / 2), _y - (_height / 2), height(), height())); + } +} + +void Switch::toggle(bool state) +{ + int toffset = offset(); + _switch = _switch ? false : true; + _thumb = _switch ? _brush : QBrush("#d5d5d5"); + if (_switch) { + _anim->setStartValue(_height / 2); + _anim->setEndValue(_width - _height); + _anim->setDuration(120); + _anim->start(); + } else { + _anim->setStartValue(offset()); + _anim->setEndValue(_height / 2); + _anim->setDuration(120); + _anim->start(); + } +} + +void Switch::mouseReleaseEvent(QMouseEvent *e) { + if (e->button() & Qt::LeftButton) { + toggle(_switch); + } + QAbstractButton::mouseReleaseEvent(e); +} + +void Switch::enterEvent(QEvent *e) { + setCursor(Qt::PointingHandCursor); + QAbstractButton::enterEvent(e); +} + +QSize Switch::sizeHint() const { + return QSize(2 * (_height + _margin), _height + 2 * _margin); +} + +QString GetStyleSheetFromResFile(QString qss_file) +{ + QFile File(qss_file); + File.open(QFile::ReadOnly); + QString StyleSheet = QLatin1String(File.readAll()); + File.close(); + return StyleSheet; +} + + +MoveWindowWidget::MoveWindowWidget(QWidget *parent) : QWidget(parent) +{ +} + +void MoveWindowWidget::paintEvent(QPaintEvent *) +{ + QStyleOption opt; + opt.init(this); + QPainter painter(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this); +} + +void MoveWindowWidget::mousePressEvent(QMouseEvent *event) +{ + startPos = event->pos(); + QWidget::mousePressEvent(event); +} + +void MoveWindowWidget::mouseMoveEvent(QMouseEvent *event) +{ + QPoint delta = event->pos() - startPos; + QWidget * w = window(); + if(w) + w->move(w->pos() + delta); + QWidget::mouseMoveEvent(event); +} diff --git a/qutils.h b/qutils.h index 032b096..6988470 100644 --- a/qutils.h +++ b/qutils.h @@ -5,9 +5,97 @@ #include #include #include +#include +#include + enum fdMode { open_file, save_as }; QString FileDialog(QWidget *parent, fdMode mode, const QString& defaultName = ""); +QString GetReadableSize(qint64 bytes); +QString GetStyleSheetFromResFile(QString qss_file); + +class Switch : public QAbstractButton { + Q_OBJECT + Q_PROPERTY(int offset READ offset WRITE setOffset) + Q_PROPERTY(QBrush brush READ brush WRITE setBrush) + +public: + Switch(bool initial_state, int width, QBrush brush = QBrush("#009688"), QWidget* parent = nullptr); + Switch(const QBrush& brush, QWidget* parent = nullptr); + + QSize sizeHint() const override; + + QBrush brush() const { + return _brush; + } + void setBrush(const QBrush &brsh) { + _brush = brsh; + } + + int offset() const { + return _x; + } + void setOffset(int o) { + _x = o; + update(); + } + + bool isActive() { return _switch; } + void setState(bool value) { _switch = value; } + void toggle(bool state); + +protected: + void paintEvent(QPaintEvent*) override; + void mouseReleaseEvent(QMouseEvent*) override; + void enterEvent(QEvent*) override; + +private: + bool _switch; + qreal _opacity; + int _x, _y, _height, _margin, _width; + QBrush _thumb, _track, _brush; + QPropertyAnimation *_anim = nullptr; +}; + + +class AnimatedLabel : public QLabel +{ + + Q_OBJECT + Q_PROPERTY(QColor color READ color WRITE setColor) + +public: + AnimatedLabel(QWidget *parent = 0) + { + } + void setColor (QColor color){ + setStyleSheet(QString( + "qproperty-alignment: 'AlignVCenter | AlignRight';" + "background-color: rgba(%1, %2, %3, 200);" + "color: rgb(0, 0, 0);" + "border-top-left-radius: 20px; " + "border-bottom-left-radius: 20px; " + "padding: 10px;").arg(color.red()).arg(color.green()).arg(color.blue())); + } + QColor color(){ + return Qt::black; // getter is not really needed for now + } +}; + + + +class MoveWindowWidget : public QWidget +{ + Q_OBJECT +public: + explicit MoveWindowWidget(QWidget *parent = nullptr); +protected: + void paintEvent(QPaintEvent *); + void mousePressEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); +private: + QPoint startPos; +}; #endif // QUTILS_H diff --git a/rcm_device.h b/rcm_device.h deleted file mode 100644 index bb99962..0000000 --- a/rcm_device.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef RCMDEVICE_H -#define RCMDEVICE_H - - -class RcmDevice -{ -public: - RcmDevice(); -}; - -#endif // RCMDEVICE_H diff --git a/res/QFrame_box01.qss b/res/QFrame_box01.qss index fba8238..f1aec7a 100644 --- a/res/QFrame_box01.qss +++ b/res/QFrame_box01.qss @@ -9,4 +9,4 @@ QLabel border-radius: 0px; font: 10pt "Calibri"; color: rgb(255, 255, 255); -} \ No newline at end of file +} diff --git a/res/QFrame_box02.qss b/res/QFrame_box02.qss index 534a0a5..ca71113 100644 --- a/res/QFrame_box02.qss +++ b/res/QFrame_box02.qss @@ -8,8 +8,9 @@ QFrame QLabel { border-radius: 0px; - font: 10pt "Calibri"; - color: rgb(255, 255, 255); + font: 10pt "Calibri"; + background-color: rgb(41, 41, 41); + color: rgb(255, 255, 255); } QLineEdit { @@ -18,4 +19,9 @@ QLineEdit font: 9pt "Calibri"; border-radius: 2px; color: rgb(255, 255, 255); -} \ No newline at end of file +} +QToolTip +{ + background-color: rgb(30, 30, 30); + color: rgb(255, 255, 255); +} diff --git a/res/QLabel_title01.qss b/res/QLabel_title01.qss index e69de29..b492dad 100644 --- a/res/QLabel_title01.qss +++ b/res/QLabel_title01.qss @@ -0,0 +1,14 @@ +QFrame +{ + border-radius: 10px; + background-color: rgb(0, 150, 136); + border-color: rgb(0, 0, 0); +} +QLabel +{ + font: 75 9pt "Calibri"; + color: rgb(255, 255, 255); + border-radius: 10px; + background-color: rgb(0, 150, 136); + border-color: rgb(0, 0, 0); +} diff --git a/res/QMainWindow.qss b/res/QMainWindow.qss index e39d887..78392a0 100644 --- a/res/QMainWindow.qss +++ b/res/QMainWindow.qss @@ -2,4 +2,17 @@ QMainWindow { background-color: rgb(30, 30, 30); border-radius: 10px; -} \ No newline at end of file + color: rgb(255, 255, 255); +} +QStatusBar +{ + color: rgb(255, 255, 255); +} +QToolTip +{ + background-color: rgb(30, 30, 30); + color: rgb(255, 255, 255); +} +QMessageBox QLabel { + color: rgb(0, 0, 0); +} diff --git a/res/QPushButton.qss b/res/QPushButton.qss index e69de29..7acba29 100644 --- a/res/QPushButton.qss +++ b/res/QPushButton.qss @@ -0,0 +1,17 @@ +QPushButton +{ + background-color: rgb(0, 150, 136); + color: rgb(255, 255, 255); + border-radius: 10px; +} +QPushButton:pressed +{ + color: rgb(0, 0, 0); + background-color: rgb(255, 255, 255); +} +QPushButton:hover +{ + color: rgb(255, 255, 255); + background-color: rgb(150, 35, 0); +} + diff --git a/res/QTabWidget.qss b/res/QTabWidget.qss index f048feb..39a064e 100644 --- a/res/QTabWidget.qss +++ b/res/QTabWidget.qss @@ -1,26 +1,37 @@ -QTabWidget::pane { - border-radius:10px; - background-color: rgb(255, 255, 255); -} - QTabWidget::tab-bar +QTabWidget::pane { - left: 10px; - } -QTabBar::tab { + border-radius:10px; + background-color: rgb(60, 60, 60); + color: rgb(255, 255, 255); +} +QTabWidget::tab-bar +{ + left: 10px; +} +QTabBar::tab +{ + color: rgb(255, 255, 255); + border: 0px; - border-top-left-radius: 6px; /* For rounded corners */ - border-top-right-radius: 6px; - min-width: 8ex; + min-width: 10ex; padding: 6px; /* Padding inside each tab */ - margin-left: 4px; /* Margins among the tabs */ - margin-right: 4px; - background-color: rgb(213, 213, 213); - font: 75 9pt "Calibri"; - color: rgb(0, 0, 0); + font: 75 10pt "MS Shell Dlg 2"; + color: rgb(255, 255, 255); + border-bottom: 2px solid grey; +} +QTabBar::tab:selected { + border-bottom: 2px solid white; +} +QLabel +{ + color: rgb(255, 255, 255); +} +QToolTip +{ + background-color: rgb(30, 30, 30); + color: rgb(255, 255, 255); +} +QMessageBox QLabel { + color: rgb(0, 0, 0); } -QTabBar::tab:selected { - background-color: rgb(0, 150, 136); - border-color: grey; - color: rgb(255, 255, 255); -} \ No newline at end of file diff --git a/res/QTableView.qss b/res/QTableView.qss index e69de29..fa9f9fd 100644 --- a/res/QTableView.qss +++ b/res/QTableView.qss @@ -0,0 +1,7 @@ +QTableView +{ + border-radius: 10px; + alternate-background-color: rgb(188, 188, 188); + background: rgb(240, 240, 240); + selection-background-color: rgb(150, 0, 83); +} diff --git a/tegrarcmgui.cpp b/tegrarcmgui.cpp index 215f523..aa48d4f 100644 --- a/tegrarcmgui.cpp +++ b/tegrarcmgui.cpp @@ -1,15 +1,361 @@ #include "tegrarcmgui.h" #include "ui_tegrarcmgui.h" +#include "qutils.h" +#include + +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 acces to builtin resources + Q_INIT_RESOURCE(qresources); + + // Tray icon init + trayIcon = new QSystemTrayIcon; + trayIcon->setIcon(switchOffIcon); + trayIcon->setVisible(true); + connect(trayIcon, &QSystemTrayIcon::activated, this, &TegraRcmGUI::trayIconActivated); + trayIconMenu = trayIcon->contextMenu(); + + // Load settings + userSettings = new QSettings("nx", "TegraRcmGUI"); + + // 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(); + + // 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"); + 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 minute + + + /// 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")); + + // Init 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")); + toolsTab = new qTools(this); + ui->tabWidget->addTab(toolsTab, tr("TOOLS")); + + ui->closeAppBtn->setCursor(Qt::PointingHandCursor); + connect(ui->closeAppBtn, SIGNAL(clicked()), this, SLOT(close())); + + clearDeviceInfo(); + 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(680, 400); + this->adjustSize(); } TegraRcmGUI::~TegraRcmGUI() { + if (m_hotHandle != nullptr) + HotK_Free(m_hotHandle); + + delete trayIcon; delete ui; } +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() +{ + ui->devStatusLbl_2->setText(m_device.getStatus() == CONNECTED ? tr("CONNECTED") : tr("DISCONNECTED")); + ui->devStatusFrame->setStyleSheet(m_device.getStatus() == CONNECTED ? statusOnStyleSht : statusOffStyleSht); + ui->rcmStatusLbl_2->setText(m_device.rcmIsReady() ? tr("READY") : tr("OFF")); + QString arianeStatus; + if (m_kourou->arianeIsLoading) + arianeStatus.append(tr("LOADING")); + else if (m_device.arianeIsReady()) + arianeStatus.append(tr("READY")); + else + arianeStatus.append(tr("OFF")); + ui->arianeStatusLbl_2->setText(arianeStatus); + ui->rcmStatusFrame->setStyleSheet(m_device.rcmIsReady() ? statusOnStyleSht : statusOffStyleSht); + ui->arianeStatusFrame->setStyleSheet(m_device.arianeIsReady() ? statusOnStyleSht : statusOffStyleSht); + if (!m_device.arianeIsReady()) + clearDeviceInfo(); + + if (m_device.rcmIsReady() || m_device.arianeIsReady()) + trayIcon->setIcon(switchOnIcon); + else + trayIcon->setIcon(switchOffIcon); + + payloadTab->on_deviceStateChange(); + +} + +void TegraRcmGUI::on_autoLaunchAriane_toggled(bool value) +{ + m_kourou->autoLaunchAriane = !m_kourou->autoLaunchAriane; + userSettings->setValue("autoAriane", m_kourou->autoLaunchAriane); + + if (m_device.rcmIsReady() && m_kourou->autoLaunchAriane) + QtConcurrent::run(m_kourou, &QKourou::initDevice, true, nullptr); +} + +bool TegraRcmGUI::enableWidget(QWidget *widget, bool enable) +{ + widget->setEnabled(enable); +} + +void TegraRcmGUI::clearDeviceInfo() +{ + ui->deviceInfoFrame->setStyleSheet(statusOffStyleSht); + ui->deviceInfoLbl->setStyleSheet(statusOffStyleSht); + ui->batteryLevel->hide(); + ui->batteryLevel->setValue(0); + ui->burntFusesLbl2->setText(""); + ui->sdfsLbl2->setText(""); + ui->sdfsLbl2->setText(""); + ui->fsTotSizeLbl2->setText(""); + ui->fsFreeSpaceLbl2->setText(""); + if (!m_kourou->autoLaunchAriane) + { + ui->batteryLbl->hide(); + ui->burntFusesLbl1->hide(); + ui->sdfsLbl1->hide(); + ui->fsTotSizeLbl1->hide(); + ui->fsFreeSpaceLbl1->hide(); + ui->devInfoDisableLbl->show(); + } + else ui->devInfoDisableLbl->hide(); +} + +void TegraRcmGUI::on_deviceInfo_received(UC_DeviceInfo di) +{ + ui->batteryLbl->show(); + ui->burntFusesLbl1->show(); + ui->sdfsLbl1->show(); + ui->fsTotSizeLbl1->show(); + ui->fsFreeSpaceLbl1->show(); + ui->devInfoDisableLbl->hide(); + ui->deviceInfoLbl->setStyleSheet(GetStyleSheetFromResFile(":/res/QLabel_title01.qss")); + ui->batteryLevel->show(); + ui->batteryLevel->setValue(int(di.battery_capacity)); + ui->burntFusesLbl2->setText(QString().asprintf("%u", di.burnt_fuses)); + ui->sdfsLbl2->setText(QString().asprintf("%s", di.sdmmc_initialized ? "Yes" : "No")); + if (di.sdmmc_initialized) + { + QString fs; + if (di.emmc_fs_type == FS_FAT32) fs.append("FAT32"); + else if (di.emmc_fs_type == FS_EXFAT) fs.append("exFAT"); + else fs.append("UNKNOWN"); + ui->sdfsLbl2->setText(fs); + + qint64 fs_size = 0x200 * (qint64)di.emmc_fs_cl_size * ((qint64)di.emmc_fs_last_cl + 1); + qint64 fs_free_space = 0x200 * (qint64)di.emmc_fs_cl_size * (qint64)di.emmc_fs_free_cl; + ui->fsTotSizeLbl2->setText(GetReadableSize(fs_size)); + ui->fsFreeSpaceLbl2->setText(GetReadableSize(fs_free_space)); + } + else + { + ui->sdfsLbl2->setText("N/A"); + ui->fsTotSizeLbl2->setText("N/A"); + ui->fsFreeSpaceLbl2->setText("N/A"); + } +} + +void TegraRcmGUI::error(int error) +{ + QMessageBox::critical(this, "Error", QString().asprintf("Error %d", error)); +} + +void TegraRcmGUI::pushMessage(QString 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().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().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: + t = 0; + break; + case QSystemTrayIcon::DoubleClick: + t = 0; + break; + case QSystemTrayIcon::MiddleClick: + t = 0; + 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);" + "}"; + + + QMenu *menu = new QMenu; + menu->setStyleSheet(menu_ss); + menu->setContextMenuPolicy(Qt::CustomContextMenu); + menu->addAction("Exit",this,SLOT(close())); + + QMenu *fav_menu = new QMenu; + fav_menu->setStyleSheet(menu_ss); + fav_menu->setContextMenuPolicy(Qt::CustomContextMenu); + fav_menu->setTitle("Favorites"); + + for (payload_t payload : payloadTab->getPayloads()) + { + QMenu *p_menu = new QMenu; + p_menu->setStyleSheet(menu_ss); + p_menu->setContextMenuPolicy(Qt::CustomContextMenu); + p_menu->setTitle(payload.name); + fav_menu->addMenu(p_menu); + } + + menu->addMenu(fav_menu); + + trayIcon->setContextMenu(menu); + trayIcon->contextMenu()->popup(QCursor::pos()); +} diff --git a/tegrarcmgui.h b/tegrarcmgui.h index c5954ea..abd85f4 100644 --- a/tegrarcmgui.h +++ b/tegrarcmgui.h @@ -1,7 +1,17 @@ #ifndef TEGRARCMGUI_H #define TEGRARCMGUI_H - #include +#include +#include +#include +#include "qpayload.h" +#include "qtools.h" +#include "kourou/kourou.h" +#include "kourou/usb_command.h" +#include "qkourou.h" + +class QPayloadWidget; +class QKourou; QT_BEGIN_NAMESPACE namespace Ui { class TegraRcmGUI; } @@ -9,13 +19,66 @@ QT_END_NAMESPACE class TegraRcmGUI : public QMainWindow { - Q_OBJECT + Q_OBJECT + static TegraRcmGUI* m_instance; public: TegraRcmGUI(QWidget *parent = nullptr); ~TegraRcmGUI(); + static bool hasInstance() { return m_instance; } + static TegraRcmGUI * instance() { + if (!m_instance) m_instance = new TegraRcmGUI; + return m_instance; + } + QSettings *userSettings; + QSettings *userPayloads; + Kourou m_device; + QKourou *m_kourou; + QPayloadWidget *payloadTab; + qTools *toolsTab; + bool enableWidget(QWidget *widget, bool enable); + +private slots: + void on_deviceInfo_received(UC_DeviceInfo di); + void on_autoLaunchAriane_toggled(bool value); + void pushTimer(); + void on_Kourou_finished(int res); + void trayIconActivated(QSystemTrayIcon::ActivationReason reason); + +public slots: + void hotPlugEvent(bool added, KLST_DEVINFO_HANDLE deviceInfo); + void deviceInfoTimer(); + void error(int error); + void on_deviceStateChange(); + void pushMessage(QString message); + +signals: + void sign_hotPlugEvent(bool added, KLST_DEVINFO_HANDLE); private: Ui::TegraRcmGUI *ui; + KHOT_HANDLE m_hotHandle = nullptr; + + bool m_ready = false; + std::string tmp_string; + QVector push_ts; + int tsToDeleteCount = 0; + qint64 lastTsToDelete = 0; + + const QIcon switchOnIcon = QIcon(":/res/switch_logo_on.png"); + const QIcon switchOffIcon = QIcon(":/res/switch_logo_off.png"); + QSystemTrayIcon *trayIcon; + QMenu *trayIconMenu; + void drawTrayContextMenu(); + + void clearDeviceInfo(); }; + +const QString statusOnStyleSht( "QFrame{border-radius: 10px; background-color: rgb(0, 150, 136); border-color: rgb(0, 0, 0);}" + "QLabel{font: 75 9pt \"Calibri\"; color: rgb(255, 255, 255);}"); +const QString statusOffStyleSht("QFrame{border-radius: 10px; background-color: rgb(213, 213, 213); border-color: rgb(0, 0, 0);}" + "QLabel{font: 75 9pt \"Calibri\"; color: rgb(0, 0, 0);}"); +const QString statusOffRedStyleSht("QFrame{border-radius: 10px; background-color: rgb(150, 35, 0); border-color: rgb(0, 0, 0);}" + "QLabel{font: 75 9pt \"Calibri\"; color: rgb(255, 255, 255);}"); + #endif // TEGRARCMGUI_H diff --git a/tegrarcmgui.ui b/tegrarcmgui.ui index f4f6526..c0ff3ed 100644 --- a/tegrarcmgui.ui +++ b/tegrarcmgui.ui @@ -6,17 +6,649 @@ 0 0 - 800 - 600 + 677 + 435 TegraRcmGUI - - - + + + + + + + + + + + 190 + 30 + 481 + 371 + + + + + + + -1 + + + + + + 20 + 30 + 100 + 20 + + + + border-radius: 10px; +background-color: rgb(0, 150, 136); +border-color: rgb(0, 0, 0); +QLabel{font: 75 11pt "Calibri"; color: rgb(255, 255, 255);} + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + -1 + 0 + 101 + 20 + + + + + + + Qt::AlignCenter + + + + + + + 120 + 55 + 50 + 20 + + + + border-radius: 10px; +background-color: rgb(0, 150, 136); +border-color: rgb(0, 0, 0); +QLabel{font: 75 11pt "Calibri"; color: rgb(255, 255, 255);} + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + 0 + 0 + 50 + 20 + + + + + + + Qt::AlignCenter + + + + + + + 120 + 105 + 50 + 20 + + + + border-radius: 10px; +background-color: rgb(0, 150, 136); +border-color: rgb(0, 0, 0); +QLabel{font: 75 11pt "Calibri"; color: rgb(255, 255, 255);} + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + 0 + 0 + 50 + 20 + + + + + + + Qt::AlignCenter + + + + + + + 120 + 80 + 51 + 21 + + + + + + + + 10 + 150 + 171 + 251 + + + + font: 10pt "Calibri"; +border:0px; +QLabel{ +background-color: rgb(255, 255, 255); +color: rgb(37, 37, 38); +} + + + + + + true + + + + + 0 + 10 + 171 + 241 + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + 10 + 20 + 81 + 20 + + + + font: 10pt "Calibri"; +QLabel{ +background-color: rgb(255, 255, 255); +color: rgb(37, 37, 38); +} + + + Battery charge: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + 10 + 40 + 81 + 20 + + + + font: 10pt "Calibri"; + + + + Burnt fuses: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + 10 + 60 + 81 + 20 + + + + font: 10pt "Calibri"; + + + + SD Filesystem: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + 10 + 80 + 81 + 20 + + + + font: 10pt "Calibri"; + + + FS Total size: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + 10 + 100 + 81 + 20 + + + + font: 10pt "Calibri"; + + + FS Free space: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + 100 + 100 + 71 + 20 + + + + font: 10pt "Calibri"; + + + + + + + + + 100 + 80 + 71 + 20 + + + + font: 10pt "Calibri"; + + + + + + + + + 100 + 60 + 71 + 20 + + + + font: 10pt "Calibri"; + + + + + + + + + 100 + 40 + 71 + 20 + + + + font: 10pt "Calibri"; + + + + + + + + + 100 + 20 + 51 + 20 + + + + QProgressBar { + border: 2px solid grey; + border-radius: 5px; + text-align: center; +} + +QProgressBar::chunk { + background-color: #009688; + width: 1px; +} + + + 24 + + + %p% + + + + + + 10 + 10 + 151 + 221 + + + + font: 10pt "Calibri"; + + + Enable Ariane autoboot + to display device info + + + Qt::AlignCenter + + + + + + + + 20 + 150 + 100 + 20 + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + 0 + 0 + 100 + 20 + + + + color: rgb(255, 255, 255); +font: 9pt "Calibri"; + + + DEVICE INFO + + + Qt::AlignCenter + + + + + + + 10 + 40 + 171 + 101 + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + 0 + 15 + 101 + 20 + + + + font: 10pt "Calibri"; + + + RCM status: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + 0 + 40 + 101 + 20 + + + + font: 10pt "Calibri"; + + + + Ariane autoboot: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + 0 + 65 + 101 + 20 + + + + font: 10pt "Calibri"; + + + + Ariane status: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + 390 + 20 + 291 + 381 + + + + background-color: rgba(0,0,0,0) + + + + + + + + + + + -1 + 0 + 685 + 21 + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + 10 + 0 + 151 + 20 + + + + + + + TegraRcmGUI v3.0 + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + 655 + 0 + 21 + 20 + + + + + + + X + + + + + + -1 + 0 + 651 + 20 + + + + + statusBoxFrame + tabWidget + devStatusFrame + rcmStatusFrame + arianeStatusFrame + verticalLayoutWidget + devInfoBox + deviceInfoFrame + pushLayoutWidget + titleBarFrame + + + + + + + + + MoveWindowWidget + QWidget +
qutils.h
+ 1 +
+
diff --git a/types.h b/types.h index 87c3e3c..aded598 100644 --- a/types.h +++ b/types.h @@ -1,4 +1,39 @@ #ifndef TYPES_H #define TYPES_H +#include +#include +#include +#include + +using u64 = std::uint64_t; +using u32 = std::uint32_t; +using u16 = std::uint16_t; +using u8 = std::uint8_t; +using s64 = std::int64_t; +using s32 = std::int32_t; +using s16 = std::int16_t; +using s8 = std::int8_t; + +using std::vector; +using std::string; +using std::array; + +template +size_t array_countof(T(&)[ARR_SIZE]) { return ARR_SIZE; } + +template +T align_up(const T n, const Y align) +{ + const T alignm1 = align - 1; + return (n + alignm1) & (~alignm1); +} + +template +T align_down(const T n, const Y align) +{ + const T alignm1 = align - 1; + return n & (~alignm1); +} + #endif // TYPES_H