#!/usr/bin/env python3 # MTK Flash Client (c) G.Kreileman, B.Kerler 2021. # Licensed under GPLv3 License import sys import time import mock import traceback import math import logging import ctypes from PySide6.QtCore import Qt, QVariantAnimation, Signal, QObject, QSize, QTranslator, QLocale, QLibraryInfo from PySide6.QtGui import QTextOption, QPixmap, QTransform, QIcon, QAction from PySide6.QtWidgets import QMainWindow, QApplication, QWidget from mtkclient.Library.mtk import Mtk from mtkclient.Library.mtk_da_cmd import DA_handler from mtkclient.Library.gpt import gpt_settings from mtkclient.Library.mtk_main import Main, Mtk_Config from mtkclient.gui.readFlashPartitions import ReadFlashWindow from mtkclient.gui.writeFlashPartitions import WriteFlashWindow from mtkclient.gui.eraseFlashPartitions import EraseFlashWindow from mtkclient.gui.readFull import ReadFullFlashWindow from mtkclient.gui.writeFull import WriteFullFlashWindow from mtkclient.gui.toolsMenu import generateKeysMenu from mtkclient.gui.toolkit import asyncThread, trap_exc_during_debug from mtkclient.config.payloads import pathconfig from mtkclient.gui.main_gui import Ui_MainWindow import os os.environ['QT_MAC_WANTS_LAYER'] = '1' #This fixes a bug in pyside2 on MacOS Big Sur # TO do Move all GUI modifications to signals! # install exception hook: without this, uncaught exception would cause application to exit sys.excepthook = trap_exc_during_debug # Initiate MTK classes variables = mock.Mock() variables.cmd = "stage" variables.debugmode = True path=pathconfig() # if sys.platform.startswith('darwin'): # config.ptype = "kamakiri" #Temp for Mac testing MtkTool = Main(variables) guiState = "welcome" phoneInfo = {"chipset": "", "bootMode": "", "daInit": False, "cdcInit": False} class DeviceHandler(QObject): sendToLogSignal = Signal(str) sendToProgressSignal = Signal(int) mtkClass = None da_handler = None def __init__(self, parent, preloader: str = None, loglevel=logging.INFO, *args, **kwargs): super().__init__(parent, *args, **kwargs) config = Mtk_Config(loglevel=logging.INFO, gui=self.sendToLogSignal, guiprogress=self.sendToProgressSignal) config.gpt_settings = gpt_settings(gpt_num_part_entries='0', gpt_part_entry_size='0', gpt_part_entry_start_lba='0') # This actually sets the right GPT settings.. self.mtkClass = Mtk(config=config, loglevel=logging.INFO) self.loglevel = logging.DEBUG self.da_handler = DA_handler(self.mtkClass, loglevel) def getDevInfo(self, parameters): loglevel = parameters[0] phoneInfo = parameters[1] devhandler = parameters[2] mtkClass = devhandler.mtkClass da_handler = devhandler.da_handler try: if not mtkClass.port.cdc.connect(): mtkClass.preloader.init() else: phoneInfo['cdcInit'] = True except: phoneInfo['cantConnect'] = True phoneInfo['chipset'] = str(mtkClass.config.chipconfig.name) + \ " (" + str(mtkClass.config.chipconfig.description) + ")" self.sendUpdateSignal.emit() mtkClass = da_handler.configure_da(mtkClass, preloader=None) if mtkClass: phoneInfo['daInit'] = True phoneInfo['chipset'] = str(mtkClass.config.chipconfig.name) + \ " (" + str(mtkClass.config.chipconfig.description) + ")" if mtkClass.config.is_brom: phoneInfo['bootMode'] = "Bootrom mode" elif mtkClass.config.chipconfig.damode: phoneInfo['bootMode'] = "DA mode" else: phoneInfo['bootMode'] = "Preloader mode" self.sendUpdateSignal.emit() else: phoneInfo['cantConnect'] = True self.sendUpdateSignal.emit() # try: # print(mtkClass.daloader.get_partition_data(parttype="user")) # except Exception: # print(traceback.format_exc()) # MtkTool.cmd_stage(mtkClass, None, None, None, False) def load_translations(application): """ Load application translations and the QT base translations for the current locale :param application: QApplication object """ locale = QLocale.system() translator = QTranslator(application) directory = os.path.dirname(__file__) lang = 'mtkclient/gui/i18n/' + locale.name() #lang = 'mtkclient/gui/i18n/fr_FR' #lang = 'mtkclient/gui/i18n/de_DE' #lang = 'mtkclient/gui/i18n/en_GB' #lang = 'mtkclient/gui/i18n/es_ES' if translator.load(lang, directory): application.installTranslator(translator) translations_path = QLibraryInfo.location(QLibraryInfo.TranslationsPath) base_translator = QTranslator(application) if base_translator.load(locale, "qtbase", "_", translations_path): application.installTranslator(base_translator) class MainWindow(QMainWindow): def __init__(self): super(MainWindow, self).__init__() w = QWidget(self) self.ui=Ui_MainWindow() self.ui.setupUi(self) # Menubar self.ui.actionRead_full_flash.triggered.connect(self.on_readfullflash) self.ui.actionRead_partition_s.triggered.connect(self.on_readpartflash) self.ui.actionRead_offset.triggered.connect(self.on_readflashoffset) self.ui.actionWrite_full_flash.triggered.connect(self.on_writefullflash) self.ui.actionWrite_partition_s.triggered.connect(self.on_writepartflash) self.ui.actionWrite_at_offset.triggered.connect(self.on_writeflashoffset) self.ui.actionWrite_boot2.triggered.connect(self.on_writeboot2) self.ui.actionWrite_preloader.triggered.connect(self.on_writepreloader) self.ui.actionRead_RPMB.triggered.connect(self.on_readrpmb) self.ui.actionWrite_RPMB.triggered.connect(self.on_writerpmb) self.ui.actionErase_at_offset.triggered.connect(self.on_eraseoffset) self.ui.actionErase_partitions_s.triggered.connect(self.on_erasepartflash) self.ui.actionGenerate_RPMB_keys.triggered.connect(self.on_generatekeys) self.ui.actionRead_preloader.triggered.connect(self.on_readpreloader) self.ui.actionRead_boot2.triggered.connect(self.on_readboot2) #self.ui.toolsFlashMenu.triggered[QAction].connect(self.openToolsMenu) self.initpixmap() self.ui.debugBtn.clicked.connect(self.showDebugInfo) self.ui.logBox.setWordWrapMode(QTextOption.NoWrap) self.ui.menubar.setEnabled(False) def on_readflashoffset(self): return def on_writefullflash(self): readFlashWindowVal = WriteFullFlashWindow(self, devhandler, devhandler.da_handler, self.sendToLog, parttype="user") return def on_writepreloader(self): readFlashWindowVal = WriteFullFlashWindow(self, devhandler, devhandler.da_handler, self.sendToLog, parttype="boot1") return def on_writeboot2(self): readFlashWindowVal = WriteFullFlashWindow(self, devhandler, devhandler.da_handler, self.sendToLog, parttype="boot2") return def on_writepartflash(self): writeFlashWindowVal = WriteFlashWindow(self, devhandler, devhandler.da_handler, self.sendToLog) return def on_writeflashoffset(self): return def on_readrpmb(self): readFlashWindowVal = ReadFullFlashWindow(self, devhandler, devhandler.da_handler, self.sendToLog, parttype="rpmb") return def on_writerpmb(self): readFlashWindowVal = WriteFullFlashWindow(self, devhandler, devhandler.da_handler, self.sendToLog, parttype="rpmb") return def on_eraseoffset(self): return def on_erasepartflash(self): eraseFlashWindowVal = EraseFlashWindow(self, devhandler, devhandler.da_handler, self.sendToLog) return def on_generatekeys(self): readFlashWindowVal = generateKeysMenu(self, devhandler, devhandler.da_handler, self.sendToLog) return def on_readpreloader(self): readFlashWindowVal = ReadFullFlashWindow(self, devhandler, devhandler.da_handler, self.sendToLog, parttype="boot1") return def on_readboot2(self): readFlashWindowVal = ReadFullFlashWindow(self, devhandler, devhandler.da_handler, self.sendToLog, parttype="boot2") return def on_readpartflash(self): readFlashWindowVal = ReadFlashWindow(self, devhandler, devhandler.da_handler, self.sendToLog) def on_readfullflash(self): readFlashWindowVal = ReadFullFlashWindow(self, devhandler, devhandler.da_handler, self.sendToLog, parttype="user") def sendToLog(self, info): t = time.localtime() self.ui.logBox.appendPlainText(time.strftime("[%H:%M:%S", t) + "]: " + info) self.ui.logBox.verticalScrollBar().setValue(self.ui.logBox.verticalScrollBar().maximum()) def sendToProgress(self, progress): return def updateGui(self): global phoneInfo if phoneInfo['cdcInit'] and phoneInfo['bootMode'] == "": self.ui.phoneInfoTextbox.setText("Phone detected:\nReading model info...") else: self.ui.phoneInfoTextbox.setText( "Phone detected:\n" + phoneInfo['chipset'] + "\n" + phoneInfo['bootMode']) self.ui.status.setText("Device detected, please wait.\nThis can take a while...") if phoneInfo['daInit']: self.ui.status.setText("Device connected :)") self.ui.menubar.setEnabled(True) self.pixmap = QPixmap(path.get_images_path("phone_connected.png")) self.ui.pic.setPixmap(self.pixmap) self.spinnerAnim.stop() self.ui.spinner_pic.setHidden(True) else: if 'cantConnect' in phoneInfo: self.ui.status.setText("Error initialising. Did you install the drivers?") self.spinnerAnim.start() self.ui.spinner_pic.setHidden(False) def spinnerAnimRot(self, angle): trans = QTransform() dimension = self.pixmap.width() / math.sqrt(2) newPixmap = self.pixmap.transformed(QTransform().rotate(angle), Qt.SmoothTransformation) xoffset = (newPixmap.width() - self.pixmap.width())/2 yoffset = (newPixmap.height() - self.pixmap.height())/2 rotated = newPixmap.copy(xoffset, yoffset, self.pixmap.width(), self.pixmap.height()) self.ui.spinner_pic.setPixmap(rotated) def initpixmap(self): # phone spinner self.pixmap = QPixmap(path.get_images_path("phone_loading.png")).scaled(96, 96, Qt.KeepAspectRatio, Qt.SmoothTransformation) self.pixmap.setDevicePixelRatio(2) self.ui.spinner_pic.setPixmap(self.pixmap) self.ui.spinner_pic.show() nfpixmap = QPixmap(path.get_images_path("phone_notfound.png")) self.ui.pic.setPixmap(nfpixmap) logo = QPixmap(path.get_images_path("logo_256.png")) self.ui.logoPic.setPixmap(logo) self.ui.logBox.setHidden(True) self.spinnerAnim = QVariantAnimation() self.spinnerAnim.setDuration(3000) self.spinnerAnim.setStartValue(0) self.spinnerAnim.setEndValue(360) self.spinnerAnim.setLoopCount(-1) self.spinnerAnim.valueChanged.connect(self.spinnerAnimRot) self.ui.spinner_pic.setHidden(True) def showDebugInfo(self): if self.ui.logBox.isHidden(): self.ui.logBox.setHidden(False) #win.setFixedSize(746, 600 + addTopMargin) self.ui.debugBtn.setText(u"Hide debug info") else: self.ui.logBox.setHidden(True) #win.setFixedSize(746, 400 + addTopMargin) self.ui.debugBtn.setText(u"Show debug info") if __name__ == '__main__': # Enable nice 4K Scaling QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True) os.environ["QT_AUTO_SCREEN_SCALE_FACTOR"] = "1" # Init the app window app = QApplication(sys.argv) load_translations(app) win = MainWindow() icon = QIcon() icon.addFile(path.get_images_path('logo_32.png'), QSize(32, 32)) icon.addFile(path.get_images_path('logo_64.png'), QSize(64, 64)) icon.addFile(path.get_images_path('logo_256.png'), QSize(256, 256)) icon.addFile(path.get_images_path('logo_512.png'), QSize(512, 512)) app.setWindowIcon(icon) win.setWindowIcon(icon) if sys.platform.startswith('win'): ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID('MTKTools.Gui') dpiMultiplier = win.logicalDpiX() if dpiMultiplier == 72: dpiMultiplier = 2 else: dpiMultiplier = 1 addTopMargin = 20 if sys.platform.startswith('darwin'): # MacOS has the toolbar in the top bar insted of in the app... addTopMargin = 0 win.setWindowTitle("MTKClient - Version 2.0 beta") # lay = QVBoxLayout(self) win.show() #win.setFixedSize(746, 400 + addTopMargin) # Device setup loglevel = logging.INFO devhandler = DeviceHandler(parent=app,preloader=None,loglevel=loglevel) devhandler.sendToLogSignal.connect(win.sendToLog) # Get the device info thread = asyncThread(parent=app, n=0, function=getDevInfo, parameters=[loglevel,phoneInfo,devhandler]) thread.sendToLogSignal.connect(win.sendToLog) thread.sendUpdateSignal.connect(win.updateGui) thread.sendToProgressSignal.connect(win.sendToProgress) thread.start() # Run loop the app app.exec_() # Prevent thread from not being closed and call error end codes thread.terminate() thread.wait()