mtkclient/mtk_gui

347 lines
14 KiB
Text
Raw Normal View History

2021-12-25 17:01:25 -05:00
#!/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
2021-12-31 07:00:23 -05:00
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
2021-12-25 17:01:25 -05:00
from mtkclient.Library.mtk import Mtk
from mtkclient.Library.mtk_da_cmd import DA_handler
2021-12-25 17:01:25 -05:00
from mtkclient.Library.gpt import gpt_settings
from mtkclient.Library.mtk_main import Main, Mtk_Config
from mtkclient.gui.readFlashPartitions import ReadFlashWindow
2021-12-30 14:41:25 -05:00
from mtkclient.gui.writeFlashPartitions import WriteFlashWindow
from mtkclient.gui.eraseFlashPartitions import EraseFlashWindow
2021-12-28 04:51:36 -05:00
from mtkclient.gui.readFull import ReadFullFlashWindow
2021-12-30 14:41:25 -05:00
from mtkclient.gui.writeFull import WriteFullFlashWindow
from mtkclient.gui.toolsMenu import generateKeysMenu
from mtkclient.gui.toolkit import asyncThread, trap_exc_during_debug
2021-12-25 17:01:25 -05:00
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
2021-12-25 17:01:25 -05:00
# 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)
2021-12-30 14:41:25 -05:00
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)
2021-12-30 14:41:25 -05:00
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)
2021-12-25 17:01:25 -05:00
def getDevInfo(self, parameters):
loglevel = parameters[0]
phoneInfo = parameters[1]
devhandler = parameters[2]
mtkClass = devhandler.mtkClass
da_handler = devhandler.da_handler
try:
2021-12-29 18:17:55 -05:00
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()
2021-12-25 17:01:25 -05:00
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()
2021-12-25 17:01:25 -05:00
# try:
# print(mtkClass.daloader.get_partition_data(parttype="user"))
# except Exception:
# print(traceback.format_exc())
# MtkTool.cmd_stage(mtkClass, None, None, None, False)
2021-12-29 09:31:56 -05:00
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()
2021-12-29 10:08:38 -05:00
#lang = 'mtkclient/gui/i18n/fr_FR'
#lang = 'mtkclient/gui/i18n/de_DE'
2021-12-30 14:41:25 -05:00
#lang = 'mtkclient/gui/i18n/en_GB'
#lang = 'mtkclient/gui/i18n/es_ES'
2021-12-29 09:31:56 -05:00
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)
2021-12-29 10:08:38 -05:00
2021-12-29 18:17:55 -05:00
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
w = QWidget(self)
self.ui=Ui_MainWindow()
self.ui.setupUi(self)
# Menubar
2021-12-30 04:47:08 -05:00
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)
2021-12-30 14:41:25 -05:00
self.ui.actionWrite_boot2.triggered.connect(self.on_writeboot2)
self.ui.actionWrite_preloader.triggered.connect(self.on_writepreloader)
2021-12-30 04:47:08 -05:00
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)
2021-12-30 08:12:45 -05:00
self.ui.actionRead_boot2.triggered.connect(self.on_readboot2)
2021-12-30 04:47:08 -05:00
#self.ui.toolsFlashMenu.triggered[QAction].connect(self.openToolsMenu)
2021-12-29 18:17:55 -05:00
self.initpixmap()
self.ui.debugBtn.clicked.connect(self.showDebugInfo)
self.ui.logBox.setWordWrapMode(QTextOption.NoWrap)
self.ui.menubar.setEnabled(False)
2021-12-30 04:47:08 -05:00
def on_readflashoffset(self):
return
def on_writefullflash(self):
2021-12-30 14:41:25 -05:00
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")
2021-12-30 04:47:08 -05:00
return
def on_writepartflash(self):
2021-12-30 14:41:25 -05:00
writeFlashWindowVal = WriteFlashWindow(self, devhandler, devhandler.da_handler, self.sendToLog)
2021-12-30 04:47:08 -05:00
return
def on_writeflashoffset(self):
return
def on_readrpmb(self):
2021-12-30 14:41:25 -05:00
readFlashWindowVal = ReadFullFlashWindow(self, devhandler, devhandler.da_handler, self.sendToLog,
2021-12-30 08:12:45 -05:00
parttype="rpmb")
2021-12-30 04:47:08 -05:00
return
def on_writerpmb(self):
2021-12-30 14:41:25 -05:00
readFlashWindowVal = WriteFullFlashWindow(self, devhandler, devhandler.da_handler, self.sendToLog,
parttype="rpmb")
2021-12-30 04:47:08 -05:00
return
2021-12-30 14:41:25 -05:00
2021-12-30 04:47:08 -05:00
def on_eraseoffset(self):
return
def on_erasepartflash(self):
2021-12-30 14:41:25 -05:00
eraseFlashWindowVal = EraseFlashWindow(self, devhandler, devhandler.da_handler, self.sendToLog)
2021-12-30 04:47:08 -05:00
return
def on_generatekeys(self):
2021-12-30 14:41:25 -05:00
readFlashWindowVal = generateKeysMenu(self, devhandler, devhandler.da_handler, self.sendToLog)
2021-12-30 04:47:08 -05:00
return
def on_readpreloader(self):
2021-12-30 14:41:25 -05:00
readFlashWindowVal = ReadFullFlashWindow(self, devhandler, devhandler.da_handler, self.sendToLog,
2021-12-30 08:12:45 -05:00
parttype="boot1")
return
def on_readboot2(self):
2021-12-30 14:41:25 -05:00
readFlashWindowVal = ReadFullFlashWindow(self, devhandler, devhandler.da_handler, self.sendToLog,
2021-12-30 08:12:45 -05:00
parttype="boot2")
2021-12-30 04:47:08 -05:00
return
def on_readpartflash(self):
2021-12-30 14:41:25 -05:00
readFlashWindowVal = ReadFlashWindow(self, devhandler, devhandler.da_handler, self.sendToLog)
2021-12-30 04:47:08 -05:00
def on_readfullflash(self):
2021-12-30 14:41:25 -05:00
readFlashWindowVal = ReadFullFlashWindow(self, devhandler, devhandler.da_handler, self.sendToLog,
2021-12-30 08:12:45 -05:00
parttype="user")
2021-12-30 04:47:08 -05:00
2021-12-29 18:17:55 -05:00
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())
2021-12-30 14:41:25 -05:00
def sendToProgress(self, progress):
return
2021-12-29 18:17:55 -05:00
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")
2021-12-25 17:01:25 -05:00
if __name__ == '__main__':
2021-12-29 09:31:56 -05:00
# Enable nice 4K Scaling
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True)
os.environ["QT_AUTO_SCREEN_SCALE_FACTOR"] = "1"
2021-12-25 17:01:25 -05:00
# Init the app window
app = QApplication(sys.argv)
2021-12-29 09:31:56 -05:00
load_translations(app)
2021-12-29 18:17:55 -05:00
win = MainWindow()
2021-12-25 17:01:25 -05:00
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()
2021-12-29 10:24:38 -05:00
#win.setFixedSize(746, 400 + addTopMargin)
2021-12-25 17:01:25 -05:00
# Device setup
loglevel = logging.INFO
devhandler = DeviceHandler(parent=app,preloader=None,loglevel=loglevel)
2021-12-29 18:17:55 -05:00
devhandler.sendToLogSignal.connect(win.sendToLog)
2021-12-25 17:01:25 -05:00
# Get the device info
thread = asyncThread(parent=app, n=0, function=getDevInfo, parameters=[loglevel,phoneInfo,devhandler])
2021-12-29 18:17:55 -05:00
thread.sendToLogSignal.connect(win.sendToLog)
thread.sendUpdateSignal.connect(win.updateGui)
2021-12-30 14:41:25 -05:00
thread.sendToProgressSignal.connect(win.sendToProgress)
2021-12-25 17:01:25 -05:00
thread.start()
# Run loop the app
app.exec_()
# Prevent thread from not being closed and call error end codes
thread.terminate()
thread.wait()