def __init__(self, *args, **kwargs): super(EmulatorWindow, self).__init__() self.arch = DEFAULT_ARCHITECTURE self.recentFileActions = [] self.current_file = None self.setAttribute(Qt.WA_DeleteOnClose) self.shortcuts = Shortcut() self.emulator = Emulator(self) self.reil = Reil(self.arch) self.canvas = CanvasWidget(self) self.setMainWindowProperty() self.setMainWindowMenuBar() self.setCentralWidget(self.canvas) self.show() return
def __init__(self, *args, **kwargs): super(CEmuWindow, self).__init__() self.arch = DEFAULT_ARCHITECTURE self.recentFileActions = [] self.archActions = {} self.current_file = None self.setAttribute(Qt.WA_DeleteOnClose) self.shortcuts = Shortcut() self.emulator = Emulator(self) self.canvas = CanvasWidget(self) self.setMainWindowProperty() self.setMainWindowMenuBar() self.setCentralWidget(self.canvas) self.show() return
class EmulatorWindow(QMainWindow): MaxRecentFiles = 5 def __init__(self, *args, **kwargs): super(EmulatorWindow, self).__init__() self.arch = DEFAULT_ARCHITECTURE self.recentFileActions = [] self.current_file = None self.setAttribute(Qt.WA_DeleteOnClose) self.shortcuts = Shortcut() self.emulator = Emulator(self) self.reil = Reil(self.arch) self.canvas = CanvasWidget(self) self.setMainWindowProperty() self.setMainWindowMenuBar() self.setCentralWidget(self.canvas) self.show() return def setMainWindowProperty(self): self.resize(*WINDOW_SIZE) self.updateTitle() self.centerMainWindow() qApp.setStyle("Cleanlooks") return def centerMainWindow(self): frameGm = self.frameGeometry() screen = QApplication.desktop().screenNumber( QApplication.desktop().cursor().pos()) centerPoint = QApplication.desktop().screenGeometry(screen).center() frameGm.moveCenter(centerPoint) self.move(frameGm.topLeft()) return def add_menu_item(self, title, callback, description=None, shortcut=None): action = QAction(QIcon(), title, self) action.triggered.connect(callback) if description: action.setStatusTip(description) if shortcut: action.setShortcut(shortcut) return action def setMainWindowMenuBar(self): self.statusBar() menubar = self.menuBar() # Add File menu bar fileMenu = menubar.addMenu("&File") loadAsmAction = self.add_menu_item( "Load Assembly", self.loadCodeText, self.shortcuts.description("load_assembly"), self.shortcuts.shortcut("load_assembly")) loadBinAction = self.add_menu_item( "Load Binary", self.loadCodeBin, self.shortcuts.description("load_binary"), self.shortcuts.shortcut("load_binary")) for i in range(EmulatorWindow.MaxRecentFiles): self.recentFileActions.append( QAction(self, visible=False, triggered=self.openRecentFile)) clearRecentFilesAction = self.add_menu_item("Clear Recent Files", self.clearRecentFiles, "Clear Recent Files", "") saveAsmAction = self.add_menu_item( "Save Assembly", self.saveCodeText, self.shortcuts.description("save_as_asm"), self.shortcuts.shortcut("save_as_asm")) saveBinAction = self.add_menu_item( "Save Binary", self.saveCodeBin, self.shortcuts.description("save_as_binary"), self.shortcuts.shortcut("save_as_binary")) saveCAction = self.add_menu_item( "Generate C code", self.saveAsCFile, self.shortcuts.description("generate_c_file"), self.shortcuts.shortcut("generate_c_file")) saveAsAsmAction = self.add_menu_item( "Generate Assembly code", self.saveAsAsmFile, self.shortcuts.description("generate_asm_file"), self.shortcuts.shortcut("generate_asm_file")) quitAction = self.add_menu_item( "Quit", QApplication.quit, self.shortcuts.shortcut("exit_application"), self.shortcuts.description("exit_application")) fileMenu.addAction(loadAsmAction) fileMenu.addAction(loadBinAction) fileMenu.addSeparator() for i in range(EmulatorWindow.MaxRecentFiles): fileMenu.addAction(self.recentFileActions[i]) self.updateRecentFileActions() fileMenu.addSeparator() fileMenu.addAction(clearRecentFilesAction) fileMenu.addSeparator() fileMenu.addAction(saveAsmAction) fileMenu.addAction(saveBinAction) fileMenu.addAction(saveCAction) fileMenu.addAction(saveAsAsmAction) fileMenu.addSeparator() fileMenu.addAction(quitAction) # Add Architecture menu bar archMenu = menubar.addMenu("&Architecture") for abi in sorted(Architectures.keys()): archSubMenu = archMenu.addMenu(abi) for arch in Architectures[abi]: archAction = QAction(QIcon(), str(arch), self) if arch.__class__.__name__ == self.arch.__class__.__name__ and self.arch.endianness == arch.endianness and self.arch.syntax == arch.syntax: archAction.setEnabled(False) self.currentAction = archAction archAction.setStatusTip( "Switch context to architecture: '%s'" % arch) archAction.triggered.connect( functools.partial(self.updateMode, arch, archAction)) archSubMenu.addAction(archAction) # Add Help menu bar helpMenu = menubar.addMenu("&Help") shortcutAction = self.add_menu_item( "Shortcuts", self.showShortcutPopup, self.shortcuts.description("shortcut_popup"), self.shortcuts.shortcut("shortcut_popup")) aboutAction = self.add_menu_item( "About", self.showAboutPopup, self.shortcuts.description("about_popup")) helpMenu.addAction(shortcutAction) helpMenu.addAction(aboutAction) return def loadFile(self, fname, data=None): if data is None: data = open(fname, 'r').read() self.canvas.codeWidget.editor.setPlainText(data) self.canvas.logWidget.editor.append("Loaded '%s'" % fname) self.updateRecentFileActions(fname) self.current_file = fname self.updateTitle(self.current_file) return def openRecentFile(self): action = self.sender() if action: self.loadFile(action.data()) return def loadCode(self, title, filter, run_disassembler): qFile, qFilter = QFileDialog.getOpenFileName(self, title, EXAMPLES_PATH, filter) if not os.access(qFile, os.R_OK): return if run_disassembler or qFile.endswith(".raw"): body = disassemble_file(qFile, self.arch) self.loadFile(qFile, data=body) else: self.loadFile(qFile) return def loadCodeText(self): return self.loadCode("Open Assembly file", "Assembly files (*.asm)", False) def loadCodeBin(self): return self.loadCode("Open Raw file", "Raw binary files (*.raw)", True) def saveCode(self, title, filter, run_assembler): qFile, qFilter = QFileDialog().getSaveFileName(self, title, HOME, filter=filter) if qFile is None or len(qFile) == 0 or qFile == "": return if run_assembler: asm = self.canvas.codeWidget.parser.getCleanCodeAsByte( as_string=True) txt, cnt = assemble(asm, self.arch) if cnt < 0: self.canvas.logWidget.editor.append("Failed to compile code") return else: txt = self.canvas.codeWidget.parser.getCleanCodeAsByte( as_string=True) with open(qFile, "wb") as f: f.write(txt) self.canvas.logWidget.editor.append("Saved as '%s'" % qFile) return def saveCodeText(self): return self.saveCode("Save Assembly Pane As", "*.asm", False) def saveCodeBin(self): return self.saveCode("Save Raw Binary Pane As", "*.raw", True) def saveAsCFile(self): template = open(TEMPLATES_PATH + "/template.c", "rb").read() insns = self.canvas.codeWidget.parser.getCleanCodeAsByte( as_string=False) if sys.version_info.major == 2: title = bytes(self.arch.name) else: title = bytes(self.arch.name, encoding="utf-8") sc = b'""\n' i = 0 for insn in insns: txt, cnt = assemble(insn, self.arch) if cnt < 0: self.canvas.logWidget.editor.append("Failed to compile code") return c = b'"' + b''.join([b'\\x%.2x' % txt[i] for i in range(len(txt))]) + b'"' c = c.ljust(60, b' ') c += b'// ' + insn + b'\n' sc += b'\t' + c i += len(txt) sc += b'\t""' body = template % (title, i, sc) fd, fpath = tempfile.mkstemp(suffix=".c") os.write(fd, body) os.close(fd) self.canvas.logWidget.editor.append("Saved as '%s'" % fpath) return def saveAsAsmFile(self): asm_fmt = open(TEMPLATES_PATH + "/template.asm", "rb").read() txt = self.canvas.codeWidget.parser.getCleanCodeAsByte(as_string=True) if sys.version_info.major == 2: title = bytes(self.arch.name) else: title = bytes(self.arch.name, encoding="utf-8") asm = asm_fmt % (title, b'\n'.join( [b"\t%s" % x for x in txt.split(b'\n')])) fd, fpath = tempfile.mkstemp(suffix=".asm") os.write(fd, asm) os.close(fd) self.canvas.logWidget.editor.append("Saved as '%s'" % fpath) def updateMode(self, arch, newAction): self.currentAction.setEnabled(True) self.arch = arch print("Switching to '%s'" % self.arch) self.canvas.logWidget.editor.append("Switching to '%s'" % self.arch) self.canvas.regWidget.updateGrid() newAction.setEnabled(False) self.currentAction = newAction self.updateTitle() return def updateTitle(self, msg=None): title = "{} ({})".format(TITLE, self.arch) if msg: title += ": {}".format(msg) self.setWindowTitle(title) return def showShortcutPopup(self): msgbox = QMessageBox(self) msgbox.setWindowTitle("CEMU Shortcuts") wid = QWidget() grid = QGridLayout() for j, title in enumerate(["Shortcut", "Description"]): lbl = QLabel() lbl.setTextFormat(Qt.RichText) lbl.setText("<b>{}</b>".format(title)) grid.addWidget(lbl, 0, j) for i, config_item in enumerate(self.shortcuts._config): sc, desc = self.shortcuts._config[config_item] if not sc: continue grid.addWidget(QLabel(sc), i + 1, 0) grid.addWidget(QLabel(desc), i + 1, 1) wid.setMinimumWidth(800) wid.setLayout(grid) msgbox.layout().addWidget(wid) msgbox.exec_() return def showAboutPopup(self): templ = open(TEMPLATES_PATH + "/about.html", "r").read() desc = templ.format(author=cemu.AUTHOR, version=cemu.VERSION, project_link=cemu.LINK, issues_link=cemu.ISSUES) msgbox = QMessageBox(self) msgbox.setIcon(QMessageBox.Information) msgbox.setWindowTitle("About CEMU") msgbox.setTextFormat(Qt.RichText) msgbox.setText(desc) msgbox.setStandardButtons(QMessageBox.Ok) msgbox.exec_() return def updateRecentFileActions(self, insert_file=None): settings = QSettings('Cemu', 'Recent Files') files = settings.value('recentFileList') if files is None: # if setting doesn't exist, create it settings.setValue('recentFileList', []) files = settings.value('recentFileList') maxRecentFiles = EmulatorWindow.MaxRecentFiles if insert_file: # insert new file to list if insert_file not in files: files.insert(0, insert_file) # ensure list size if len(files) > maxRecentFiles: files = files[0:maxRecentFiles] # save the setting settings.setValue('recentFileList', files) numRecentFiles = min(len(files), maxRecentFiles) for i in range(numRecentFiles): text = "&%d %s" % (i + 1, self.strippedName(files[i])) self.recentFileActions[i].setText(text) self.recentFileActions[i].setData(files[i]) self.recentFileActions[i].setVisible(True) for j in range(numRecentFiles, maxRecentFiles): self.recentFileActions[j].setVisible(False) return def strippedName(self, fullFileName): return QFileInfo(fullFileName).fileName() def clearRecentFiles(self): settings = QSettings('Cemu', 'Recent Files') settings.setValue('recentFileList', []) self.updateRecentFileActions() return
class CEmuWindow(QMainWindow): MaxRecentFiles = 5 def __init__(self, *args, **kwargs): super(CEmuWindow, self).__init__() self.arch = DEFAULT_ARCHITECTURE self.recentFileActions = [] self.archActions = {} self.current_file = None self.setAttribute(Qt.WA_DeleteOnClose) self.shortcuts = Shortcut() self.emulator = Emulator(self) self.canvas = CanvasWidget(self) self.setMainWindowProperty() self.setMainWindowMenuBar() self.setCentralWidget(self.canvas) self.show() return def setMainWindowProperty(self): self.resize(*WINDOW_SIZE) self.updateTitle() self.centerMainWindow() qApp.setStyle("Cleanlooks") return def centerMainWindow(self): frameGm = self.frameGeometry() screen = QApplication.desktop().screenNumber(QApplication.desktop().cursor().pos()) centerPoint = QApplication.desktop().screenGeometry(screen).center() frameGm.moveCenter(centerPoint) self.move(frameGm.topLeft()) return def add_menu_item(self, title, callback, description=None, shortcut=None): action = QAction(QIcon(), title, self) action.triggered.connect( callback ) if description: action.setStatusTip(description) if shortcut: action.setShortcut(shortcut) return action def setMainWindowMenuBar(self): self.statusBar() menubar = self.menuBar() # Add File menu bar fileMenu = menubar.addMenu("&File") loadAsmAction = self.add_menu_item("Load Assembly", self.loadCodeText, self.shortcuts.description("load_assembly"), self.shortcuts.shortcut("load_assembly")) loadBinAction = self.add_menu_item("Load Binary", self.loadCodeBin, self.shortcuts.description("load_binary"), self.shortcuts.shortcut("load_binary")) for i in range(CEmuWindow.MaxRecentFiles): self.recentFileActions.append(QAction(self, visible=False, triggered=self.openRecentFile)) clearRecentFilesAction = self.add_menu_item("Clear Recent Files", self.clearRecentFiles, "Clear Recent Files", "") saveAsmAction = self.add_menu_item("Save Assembly", self.saveCodeText, self.shortcuts.description("save_as_asm"), self.shortcuts.shortcut("save_as_asm")) saveBinAction = self.add_menu_item ("Save Binary", self.saveCodeBin, self.shortcuts.description("save_as_binary"), self.shortcuts.shortcut("save_as_binary")) saveCAction = self.add_menu_item("Generate C code", self.saveAsCFile, self.shortcuts.description("generate_c_file"), self.shortcuts.shortcut("generate_c_file")) saveAsAsmAction = self.add_menu_item("Generate Assembly code", self.saveAsAsmFile, self.shortcuts.description("generate_asm_file"), self.shortcuts.shortcut("generate_asm_file")) quitAction = self.add_menu_item("Quit", QApplication.quit, self.shortcuts.shortcut("exit_application"), self.shortcuts.description("exit_application")) fileMenu.addAction(loadAsmAction) fileMenu.addAction(loadBinAction) fileMenu.addSeparator() for i in range(CEmuWindow.MaxRecentFiles): fileMenu.addAction(self.recentFileActions[i]) self.updateRecentFileActions() fileMenu.addSeparator() fileMenu.addAction(clearRecentFilesAction) fileMenu.addSeparator() fileMenu.addAction(saveAsmAction) fileMenu.addAction(saveBinAction) fileMenu.addAction(saveCAction) fileMenu.addAction(saveAsAsmAction) fileMenu.addSeparator() fileMenu.addAction(quitAction) # Add Architecture menu bar archMenu = menubar.addMenu("&Architecture") for abi in sorted(Architectures.keys()): archSubMenu = archMenu.addMenu(abi) for arch in Architectures[abi]: self.archActions[arch.name] = QAction(QIcon(), str(arch), self) if arch == self.arch: self.archActions[arch.name].setEnabled(False) self.currentAction = self.archActions[arch.name] self.archActions[arch.name].setStatusTip("Switch context to architecture: '%s'" % arch) self.archActions[arch.name].triggered.connect( functools.partial(self.updateMode, arch) ) archSubMenu.addAction(self.archActions[arch.name]) # Add Help menu bar helpMenu = menubar.addMenu("&Help") shortcutAction = self.add_menu_item("Shortcuts", self.showShortcutPopup, self.shortcuts.description("shortcut_popup"), self.shortcuts.shortcut("shortcut_popup")) aboutAction = self.add_menu_item("About", self.showAboutPopup, self.shortcuts.description("about_popup")) helpMenu.addAction(shortcutAction) helpMenu.addAction(aboutAction) return def loadFile(self, fname, data=None): if data is None: data = open(fname, 'r').read() for line in data.splitlines(): part = line.strip().split() if len(part) < 3: continue if not (part[0] == COMMENT_MARKER and part[1] == PROPERTY_MARKER): continue if part[2].startswith("arch:"): try: arch_from_file = part[2][5:] arch = get_architecture_by_name(arch_from_file) self.updateMode(arch) except KeyError: self.canvas.logWidget.editor.append("Unknown architecture '{:s}', discarding...".format(arch_from_file)) continue if part[2].startswith("endian:"): endian_from_file = part[2][7:].lower() if endian_from_file not in ("little", "big"): self.canvas.logWidget.editor.append("Incorrect endianness '{:s}', discarding...".format(endian_from_file)) continue self.arch.endianness = Endianness.LITTLE if endian_from_file == "little" else Endianness.BIG self.canvas.logWidget.editor.append("Changed endianness to '{:s}'".format(endian_from_file)) if part[2].startswith("syntax:"): syntax_from_file = part[2][7:].lower() if syntax_from_file not in ("att", "intel"): self.canvas.logWidget.editor.append("Incorrect syntax '{:s}', discarding...".format(syntax_from_file)) continue self.arch.syntax = Syntax.ATT if syntax_from_file=="att" else Syntax.INTEL self.canvas.logWidget.editor.append("Changed syntax to '{:s}'".format(syntax_from_file)) self.canvas.codeWidget.editor.setPlainText(data) self.canvas.logWidget.editor.append("Loaded '%s'" % fname) self.updateRecentFileActions(fname) self.current_file = fname self.updateTitle(self.current_file) return def openRecentFile(self): action = self.sender() if action: self.loadFile(action.data()) return def loadCode(self, title, filter, run_disassembler): qFile, _ = QFileDialog.getOpenFileName(self, title, EXAMPLE_PATH, filter + ";;All files (*.*)") if not os.access(qFile, os.R_OK): return if run_disassembler or qFile.endswith(".raw"): body = disassemble_file(qFile, self.arch) self.loadFile(qFile, data=body) else: self.loadFile(qFile) return def loadCodeText(self): return self.loadCode("Open Assembly file", "Assembly files (*.asm *.s)", False) def loadCodeBin(self): return self.loadCode("Open Raw file", "Raw binary files (*.raw)", True) def saveCode(self, title, filter, run_assembler): qFile, _ = QFileDialog().getSaveFileName(self, title, HOME, filter=filter + ";;All files (*.*)") if qFile is None or len(qFile)==0 or qFile=="": return if run_assembler: asm = self.canvas.codeWidget.parser.getCleanCodeAsByte(as_string=True) txt, cnt = assemble(asm, self.arch) if cnt < 0: self.canvas.logWidget.editor.append("Failed to compile: error at line {:d}".format(-cnt)) return else: txt = self.canvas.codeWidget.parser.getCleanCodeAsByte(as_string=True) with open(qFile, "wb") as f: f.write(txt) self.canvas.logWidget.editor.append("Saved as '%s'" % qFile) return def saveCodeText(self): return self.saveCode("Save Assembly Pane As", "Assembly files (*.asm *.s)", False) def saveCodeBin(self): return self.saveCode("Save Raw Binary Pane As", "Raw binary files (*.raw)", True) def saveAsCFile(self): template = open(TEMPLATE_PATH+"/template.c", "rb").read() insns = self.canvas.codeWidget.parser.getCleanCodeAsByte(as_string=False) if sys.version_info.major == 2: title = bytes(self.arch.name) else: title = bytes(self.arch.name, encoding="utf-8") sc = b'""\n' i = 0 for insn in insns: txt, cnt = assemble(insn, self.arch) if cnt < 0: self.canvas.logWidget.editor.append("Failed to compile: error at line {:d}".format(-cnt)) return c = b'"' + b''.join([ b'\\x%.2x'%txt[i] for i in range(len(txt)) ]) + b'"' c = c.ljust(60, b' ') c+= b'// ' + insn + b'\n' sc += b'\t' + c i += len(txt) sc += b'\t""' body = template % (title, i, sc) fd, fpath = tempfile.mkstemp(suffix=".c") os.write(fd, body) os.close(fd) self.canvas.logWidget.editor.append("Saved as '%s'" % fpath) return def saveAsAsmFile(self): asm_fmt = open( os.sep.join([TEMPLATE_PATH, "template.asm"]), "rb").read() txt = self.canvas.codeWidget.parser.getCleanCodeAsByte(as_string=True) title = bytes(self.arch.name, encoding="utf-8") asm = asm_fmt % (title, b'\n'.join([b"\t%s"%x for x in txt.split(b'\n')])) fd, fpath = tempfile.mkstemp(suffix=".asm") os.write(fd, asm) os.close(fd) self.canvas.logWidget.editor.append("Saved as '%s'" % fpath) def updateMode(self, arch): self.currentAction.setEnabled(True) self.arch = arch print("Switching to '%s'" % self.arch) self.canvas.logWidget.editor.append("Switching to '%s'" % self.arch) self.canvas.registerWidget.updateGrid() self.archActions[arch.name].setEnabled(False) self.currentAction = self.archActions[arch.name] self.updateTitle() return def updateTitle(self, msg=None): title = "{} ({})".format(TITLE, self.arch) if msg: title+=": {}".format(msg) self.setWindowTitle(title) return def showShortcutPopup(self): msgbox = QMessageBox(self) msgbox.setWindowTitle("CEMU Shortcuts") wid = QWidget() grid = QGridLayout() for j, title in enumerate(["Shortcut", "Description"]): lbl = QLabel() lbl.setTextFormat(Qt.RichText) lbl.setText("<b>{}</b>".format(title)) grid.addWidget(lbl, 0, j) for i, config_item in enumerate(self.shortcuts._config): sc, desc = self.shortcuts._config[config_item] if not sc: continue grid.addWidget(QLabel(sc), i+1, 0) grid.addWidget(QLabel(desc), i+1, 1) wid.setMinimumWidth(800) wid.setLayout(grid) msgbox.layout().addWidget(wid) msgbox.exec_() return def showAboutPopup(self): templ = open(TEMPLATE_PATH + "/about.html", "r").read() desc = templ.format(author=cemu.const.AUTHOR, version=cemu.const.VERSION, project_link=cemu.const.URL, issues_link=cemu.const.ISSUES) msgbox = QMessageBox(self) msgbox.setIcon(QMessageBox.Information) msgbox.setWindowTitle("About CEMU") msgbox.setTextFormat(Qt.RichText) msgbox.setText(desc) msgbox.setStandardButtons(QMessageBox.Ok) msgbox.exec_() return def updateRecentFileActions(self, insert_file=None): settings = QSettings('Cemu', 'Recent Files') files = settings.value('recentFileList') if files is None: # if setting doesn't exist, create it settings.setValue('recentFileList', []) files = settings.value('recentFileList') maxRecentFiles = CEmuWindow.MaxRecentFiles if insert_file: # insert new file to list if insert_file not in files: files.insert(0, insert_file) # ensure list size if len(files) > maxRecentFiles: files = files[0:maxRecentFiles] # save the setting settings.setValue('recentFileList', files) numRecentFiles = min(len(files), maxRecentFiles) for i in range(numRecentFiles): text = "&%d %s" % (i + 1, self.strippedName(files[i])) self.recentFileActions[i].setText(text) self.recentFileActions[i].setData(files[i]) self.recentFileActions[i].setVisible(True) for j in range(numRecentFiles, maxRecentFiles): self.recentFileActions[j].setVisible(False) return def strippedName(self, fullFileName): return QFileInfo(fullFileName).fileName() def clearRecentFiles(self): settings = QSettings('Cemu', 'Recent Files') settings.setValue('recentFileList', []) self.updateRecentFileActions() return