Files
slimbootloader/BootloaderCorePkg/Tools/ConfigEditor.py
T
Maurice Ma 2cd3739bde Minor fixes for ConfigEditor tool
ConfigEditor can load binary into Table widget. It has a boundary
check issue which results in the last byte left out. This patch
fixed this and also added code to roll back invalid inputs to its
last known good value for Table widget cell.

Signed-off-by: Maurice Ma <maurice.ma@intel.com>
2019-10-10 09:42:03 -07:00

1101 lines
38 KiB
Python

## @ ConfigEditor.py
#
# Copyright (c) 2018 - 2019, Intel Corporation. All rights reserved.<BR>
# SPDX-License-Identifier: BSD-2-Clause-Patent
#
##
import os
import re
import sys
import marshal
sys.dont_write_bytecode = True
if sys.hexversion >= 0x3000000:
# for Python3
from tkinter import * ## notice lowercase 't' in tkinter here
import tkinter.ttk as ttk
import tkinter.messagebox as messagebox
import tkinter.filedialog as filedialog
else:
# for Python2
from Tkinter import * ## notice capitalized T in Tkinter
import ttk
import tkMessageBox as messagebox
import tkFileDialog as filedialog
from GenCfgData import CGenCfgData, Bytes2Str, Bytes2Val, Val2Bytes, Array2Val
class CreateToolTip(object):
'''
create a tooltip for a given widget
'''
InProgress = False
def __init__(self, Widget, Text=''):
self.TopWin = None
self.Widget = Widget
self.Text = Text
self.Widget.bind("<Enter>", self.Enter)
self.Widget.bind("<Leave>", self.Leave)
def Enter(self, event=None):
if self.InProgress:
return
if self.Widget.winfo_class() == 'Treeview':
# Only show help when cursor is on row header.
rowid = self.Widget.identify_row(event.y)
if rowid != '':
return
else:
x, y, cx, cy = self.Widget.bbox("insert")
Cursor = self.Widget.winfo_pointerxy()
x = self.Widget.winfo_rootx() + 35
y = self.Widget.winfo_rooty() + 20
if Cursor[1] > y and Cursor[1] < y + 20:
y += 20
# creates a toplevel window
self.TopWin = Toplevel(self.Widget)
# Leaves only the label and removes the app window
self.TopWin.wm_overrideredirect(True)
self.TopWin.wm_geometry("+%d+%d" % (x, y))
label = Message(self.TopWin,
text=self.Text,
justify='left',
background='bisque',
relief='solid',
borderwidth=1,
font=("times", "10", "normal"))
label.pack(ipadx=1)
self.InProgress = True
def Leave(self, event=None):
if self.TopWin:
self.TopWin.destroy()
self.InProgress = False
class ValidatingEntry(Entry):
def __init__(self, master, **kw):
Entry.__init__(*(self, master), **kw)
self.Parent = master
self.OldValue = ''
self.LastValue = ''
self.Variable = StringVar()
self.Variable.trace("w", self.Callback)
self.config(textvariable=self.Variable)
self.config({"background": "#c0c0c0"})
self.bind("<Return>", self.MoveNext)
self.bind("<Tab>", self.MoveNext)
self.bind("<Escape>", self.Cancel)
for each in ['BackSpace', 'Delete']:
self.bind("<%s>" % each, self.Ignore)
self.Display (None)
def Ignore (self, even):
return "break"
def MoveNext (self, event):
if self.Row < 0:
return
Row, Col = self.Row, self.Col
Txt, RowId, ColId = self.Parent.GetNextCell (Row, Col)
self.Display (Txt, RowId, ColId)
return "break"
def Cancel (self, event):
self.Variable.set(self.OldValue)
self.Display (None)
def Display (self, Txt, RowId = '', ColId = ''):
if Txt is None:
self.Row = -1
self.Col = -1
self.place_forget()
else:
Row = int('0x' + RowId[1:], 0) - 1
Col = int(ColId[1:]) - 1
self.Row = Row
self.Col = Col
self.OldValue = Txt
self.LastValue = Txt
x, y, width, height = self.Parent.bbox(RowId, Col)
self.place(x=x, y=y, w=width)
self.Variable.set(Txt)
self.focus_set()
self.icursor(0)
def Callback(self, *Args):
CurVal = self.Variable.get()
NewVal = self.Validate(CurVal)
if NewVal is not None and self.Row >= 0:
self.LastValue = NewVal
self.Parent.SetCell (self.Row , self.Col, NewVal)
self.Variable.set(self.LastValue)
def Validate(self, Value):
if len(Value) > 0:
try:
int(Value, 16)
except:
return None
# Normalize the cell format
self.update()
CellWidth = self.winfo_width ()
MaxLen = CustomTable.ToByteLength(CellWidth) * 2
CurPos = self.index("insert")
if CurPos == MaxLen + 1:
Value = Value[-MaxLen:]
else:
Value = Value[:MaxLen]
if Value == '':
Value = '0'
Fmt = '%%0%dX' % MaxLen
return Fmt % int(Value, 16)
class CustomTable(ttk.Treeview):
_Padding = 20
_CharWidth = 6
def __init__(self, Parent, ColHdr, Bins):
Cols = len(ColHdr)
ColByteLen = []
for Col in range(Cols): #Columns
ColByteLen.append(int(ColHdr[Col].split(':')[1]))
ByteLen = sum(ColByteLen)
Rows = (len(Bins) + ByteLen - 1) // ByteLen
self.Rows = Rows
self.Cols = Cols
self.ColByteLen = ColByteLen
self.ColHdr = ColHdr
self.Size = len(Bins)
self.LastDir = ''
style = ttk.Style()
style.configure("Custom.Treeview.Heading", font=('Calibri', 10, 'bold'), foreground="blue")
ttk.Treeview.__init__(self, Parent, height=Rows, columns=[''] + ColHdr, show='headings', style="Custom.Treeview", selectmode='none')
self.bind("<Button-1>", self.Click)
self.bind("<FocusOut>", self.FocusOut)
self.entry = ValidatingEntry(self, width=4, justify=CENTER)
self.heading(0, text='LOAD')
self.column (0, width=60, stretch=0, anchor=CENTER)
for Col in range(Cols): #Columns
Text = ColHdr[Col].split(':')[0]
ByteLen = int(ColHdr[Col].split(':')[1])
self.heading(Col+1, text=Text)
self.column(Col+1, width=self.ToCellWidth(ByteLen), stretch=0, anchor=CENTER)
Idx = 0
for Row in range(Rows): #Rows
Text = '%04X' % (Row * len(ColHdr))
Vals = ['%04X:' % (Cols * Row)]
for Col in range(Cols): #Columns
if Idx >= len(Bins):
break
ByteLen = int(ColHdr[Col].split(':')[1])
Value = Bytes2Val (Bins[Idx:Idx+ByteLen])
Hex = ("%%0%dX" % (ByteLen * 2) ) % Value
Vals.append (Hex)
Idx += ByteLen
self.insert('', 'end', values=tuple(Vals))
if Idx >= len(Bins):
break
@staticmethod
def ToCellWidth(ByteLen):
return ByteLen * 2 * CustomTable._CharWidth + CustomTable._Padding
@staticmethod
def ToByteLength(CellWidth):
return (CellWidth - CustomTable._Padding) // (2 * CustomTable._CharWidth)
def FocusOut (self, event):
self.entry.Display (None)
def RefreshBin (self, Bins):
if not Bins:
return
# Reload binary into widget
BinLen = len(Bins)
for Row in range(self.Rows):
Iid = self.get_children()[Row]
for Col in range(self.Cols):
Idx = Row * sum(self.ColByteLen) + sum(self.ColByteLen[:Col])
ByteLen = self.ColByteLen[Col]
if Idx + ByteLen <= self.Size:
ByteLen = int(self.ColHdr[Col].split(':')[1])
if Idx + ByteLen > BinLen:
Val = 0
else:
Val = Bytes2Val (Bins[Idx:Idx+ByteLen])
HexVal = ("%%0%dX" % (ByteLen * 2) ) % Val
self.set (Iid, Col + 1, HexVal)
def GetCell (self, Row, Col):
Iid = self.get_children()[Row]
Txt = self.item(Iid, 'values')[Col]
return Txt
def GetNextCell (self, Row, Col):
Rows = self.get_children()
Col += 1
if Col > self.Cols:
Col = 1
Row +=1
Cnt = Row * sum(self.ColByteLen) + sum(self.ColByteLen[:Col])
if Cnt > self.Size:
# Reached the last cell, so roll back to beginning
Row = 0
Col = 1
Txt = self.GetCell(Row, Col)
RowId = Rows[Row]
ColId = '#%d' % (Col + 1)
return (Txt, RowId, ColId)
def SetCell (self, Row, Col, Val):
Iid = self.get_children()[Row]
self.set (Iid, Col, Val)
def LoadBin (self):
# Load binary from file
Path = filedialog.askopenfilename(
initialdir=self.LastDir,
title="Load binary file",
filetypes=(("Binary files", "*.bin"), (
"binary files", "*.bin")))
if Path:
self.LastDir = os.path.dirname(Path)
Fd = open(Path, 'rb')
Bins = bytearray(Fd.read())[:self.Size]
Fd.close()
Bins.extend (b'\x00' * (self.Size - len(Bins)))
return Bins
return None
def Click (self, event):
RowId = self.identify_row(event.y)
ColId = self.identify_column(event.x)
if RowId == '' and ColId == '#1':
# Clicked on "LOAD" cell
Bins = self.LoadBin ()
self.RefreshBin (Bins)
return
if ColId == '#1':
# Clicked on column 1 (Offset column)
return
Item = self.identify('item', event.x, event.y)
if not Item or not ColId:
# Not clicked on valid cell
return
# Clicked cell
Row = int('0x' + RowId[1:], 0) - 1
Col = int(ColId[1:]) - 1
if Row * self.Cols + Col > self.Size:
return
Vals = self.item(Item, 'values')
if Col < len(Vals):
Txt = self.item(Item, 'values')[Col]
self.entry.Display (Txt, RowId, ColId)
def get(self):
Bins = bytearray()
RowIds = self.get_children()
for RowId in RowIds:
Row = int('0x' + RowId[1:], 0) - 1
for Col in range(self.Cols):
Idx = Row * sum(self.ColByteLen) + sum(self.ColByteLen[:Col])
ByteLen = self.ColByteLen[Col]
if Idx + ByteLen > self.Size:
break
Hex = self.item(RowId, 'values')[Col + 1]
Values = Val2Bytes (int(Hex, 16) & ((1 << ByteLen * 8) - 1), ByteLen)
Bins.extend(Values)
return Bins
class State:
def __init__(self):
self.state = False
def set(self, value):
self.state = value
def get(self):
return self.state
class Application(Frame):
def __init__(self, master=None):
Root = master
self.Debug = True
self.LastDir = '.'
self.PageId = ''
self.PageList = {}
self.ConfList = {}
self.CfgDataObj = None
self.OrgCfgDataBin = None
self.InLeft = State()
self.InRight = State()
Frame.__init__(self, master, borderwidth=2)
self.MenuString = [
'Save Config Data to Binary', 'Load Config Data from Binary',
'Load Config Changes from Delta File',
'Save Config Changes to Delta File',
'Save Full Config Data to Delta File'
]
Root.geometry("1200x800")
Paned = ttk.Panedwindow(Root, orient=HORIZONTAL)
Paned.pack(fill=BOTH, expand=True, padx=(4, 4))
Status = Label(master, text="", bd=1, relief=SUNKEN, anchor=W)
Status.pack(side=BOTTOM, fill=X)
FrameLeft = ttk.Frame(Paned, height=800, relief="groove")
self.Left = ttk.Treeview(FrameLeft, show="tree")
# Set up tree HScroller
Pady = (10, 10)
self.TreeScroll = ttk.Scrollbar(FrameLeft,
orient="vertical",
command=self.Left.yview)
self.Left.configure(yscrollcommand=self.TreeScroll.set)
self.Left.bind('<<TreeviewSelect>>', self.OnConfigPageSelectChange)
self.Left.bind('<Enter>', lambda e: self.InLeft.set(True))
self.Left.bind('<Leave>', lambda e: self.InLeft.set(False))
self.Left.bind('<MouseWheel>', self.OnTreeScroll)
self.Left.pack(side='left',
fill=BOTH,
expand=True,
padx=(5, 0),
pady=Pady)
self.TreeScroll.pack(side='right', fill=Y, pady=Pady, padx=(0, 5))
FrameRight = ttk.Frame(Paned, relief="groove")
self.FrameRight = FrameRight
self.ConfCanvas = Canvas(FrameRight, highlightthickness=0)
self.PageScroll = ttk.Scrollbar(FrameRight,
orient="vertical",
command=self.ConfCanvas.yview)
self.RightGrid = ttk.Frame(self.ConfCanvas)
self.ConfCanvas.configure(yscrollcommand=self.PageScroll.set)
self.ConfCanvas.pack(side='left',
fill=BOTH,
expand=True,
pady=Pady,
padx=(5, 0))
self.PageScroll.pack(side='right', fill=Y, pady=Pady, padx=(0, 5))
self.ConfCanvas.create_window(0, 0, window=self.RightGrid, anchor='nw')
self.ConfCanvas.bind('<Enter>', lambda e: self.InRight.set(True))
self.ConfCanvas.bind('<Leave>', lambda e: self.InRight.set(False))
self.ConfCanvas.bind("<Configure>", self.OnCanvasConfigure)
self.ConfCanvas.bind_all("<MouseWheel>", self.OnPageScroll)
Paned.add(FrameLeft, weight=2)
Paned.add(FrameRight, weight=10)
Style = ttk.Style()
Style.layout("Treeview", [('Treeview.treearea', {'sticky': 'nswe'})])
Menubar = Menu(Root)
FileMenu = Menu(Menubar, tearoff=0)
FileMenu.add_command(label="Open Config DSC file...",
command=self.LoadFromDsc)
FileMenu.add_command(label=self.MenuString[0],
command=self.SaveToBin,
state='disabled')
FileMenu.add_command(label=self.MenuString[1],
command=self.LoadFromBin,
state='disabled')
FileMenu.add_command(label=self.MenuString[2],
command=self.LoadFromDelta,
state='disabled')
FileMenu.add_command(label=self.MenuString[3],
command=self.SaveToDelta,
state='disabled')
FileMenu.add_command(label=self.MenuString[4],
command=self.SaveFullToDelta,
state='disabled')
FileMenu.add_command(label="About", command=self.About)
Menubar.add_cascade(label="File", menu=FileMenu)
self.FileMenu = FileMenu
Root.config(menu=Menubar)
if len(sys.argv) > 1:
Path = sys.argv[1]
if Path.endswith('.dlt'):
DscPath = os.path.join (os.path.dirname(Path), 'CfgDataDef.dsc')
else:
DscPath = Path
if DscPath.endswith('.dsc'):
if self.LoadDscFile (DscPath) == 0:
if DscPath != Path:
self.LoadDeltaFile (Path)
def SetObjectName(self, Widget, Name):
self.ConfList[id(Widget)] = Name
def GetObjectName(self, Widget):
if id(Widget) in self.ConfList:
return self.ConfList[id(Widget)]
else:
return None
def LimitEntrySize(self, Variable, Limit):
Value = Variable.get()
if len(Value) > Limit:
Variable.set(Value[:Limit])
def OnCanvasConfigure(self, Event):
self.RightGrid.grid_columnconfigure(0, minsize=Event.width)
def OnTreeScroll(self, Event):
if not self.InLeft.get() and self.InRight.get():
# This prevents scroll event from being handled by both left and
# right frame at the same time.
self.OnPageScroll (Event)
return 'break'
def OnPageScroll(self, Event):
if self.InRight.get():
# Only scroll when it is in active area
min, max = self.PageScroll.get()
if not ((min == 0.0) and (max == 1.0)):
self.ConfCanvas.yview_scroll(-1 * (Event.delta / 120), 'units')
def UpdateVisibilityForWidget(self, Widget, Args):
Visible = True
if isinstance(Widget, Label):
Item = self.GetConfigDataItemFromWidget(Widget, True)
else:
Item = self.GetConfigDataItemFromWidget(Widget, True)
if Item is None:
return Visible
elif not Item:
return Visible
if Item['condition']:
Visible = self.EvaluateCondition(Item['condition'])
if Visible:
Widget.grid()
else:
Widget.grid_remove()
return Visible
def UpdateWidgetsVisibilityOnPage(self):
self.WalkWidgetsInLayout(self.RightGrid,
self.UpdateVisibilityForWidget)
def ComboSelectChanged(self, Event):
self.UpdateConfigDataFromWidget(Event.widget, None)
self.UpdateWidgetsVisibilityOnPage()
def EditNumFinished(self, Event):
Widget = Event.widget
Item = self.GetConfigDataItemFromWidget(Widget)
if not Item:
return
Parts = Item['type'].split(',')
if len(Parts) > 3:
Min = Parts[2].lstrip()[1:]
Max = Parts[3].rstrip()[:-1]
MinVal = Array2Val(Min)
MaxVal = Array2Val(Max)
Text = Widget.get()
if ',' in Text:
Text = '{ %s }' % Text
try:
Value = Array2Val(Text)
if Value < MinVal or Value > MaxVal:
raise Exception('Invalid input!')
self.SetConfigItemValue(Item, Text)
except Exception as e:
pass
Text = Item['value'].strip('{').strip('}').strip()
Widget.delete(0, END)
Widget.insert(0, Text)
self.UpdateWidgetsVisibilityOnPage()
def UpdatePageScrollBar(self):
# Update scrollbar
self.FrameRight.update()
self.ConfCanvas.config(scrollregion=self.ConfCanvas.bbox("all"))
def OnConfigPageSelectChange(self, Event):
self.UpdateConfigDataOnPage()
Sel = self.Left.selection()
if len(Sel) > 0:
PageId = Sel[0]
self.BuildConfigDataPage(PageId)
self.UpdateWidgetsVisibilityOnPage()
self.UpdatePageScrollBar()
def WalkWidgetsInLayout(self, Parent, CallbackFunction, Args=None):
for Widget in Parent.winfo_children():
CallbackFunction(Widget, Args)
def ClearWidgetsInLayout(self, Parent=None):
if Parent is None:
Parent = self.RightGrid
for Widget in Parent.winfo_children():
Widget.destroy()
Parent.grid_forget()
self.ConfList.clear()
def BuildConfigPageTree(self, CfgPage, Parent):
for Page in CfgPage:
PageId = next(iter(Page))
# Put CFG items into related page list
self.PageList[PageId] = [
Item
for Item in self.CfgDataObj._CfgItemList
if Item['name'] and (Item['page'] == PageId)
]
self.PageList[PageId].sort (key=lambda x: x['order'])
PageName = self.CfgDataObj._CfgPageDict[PageId]
Child = self.Left.insert(
Parent, 'end',
iid=PageId, text=PageName,
value=0)
if len(Page[PageId]) > 0:
self.BuildConfigPageTree(Page[PageId], Child)
def IsConfigDataLoaded(self):
return True if len(self.PageList) else False
def SetCurrentConfigPage(self, PageId):
self.PageId = PageId
def GetCurrentConfigPage(self):
return self.PageId
def GetCurrentConfigData(self):
PageId = self.GetCurrentConfigPage()
if PageId in self.PageList:
return self.PageList[PageId]
else:
return []
def BuildConfigDataPage(self, PageId):
self.ClearWidgetsInLayout()
self.SetCurrentConfigPage(PageId)
DispList = []
for Item in self.GetCurrentConfigData():
if Item['subreg']:
for SubItem in Item['subreg']:
DispList.append(SubItem)
else:
DispList.append(Item)
Row = 0
DispList.sort(key=lambda x:x['order'])
for Item in DispList:
self.AddConfigItem (Item, Row)
Row += 2
def LoadConfigData(self, FileName):
GenCfgData = CGenCfgData()
if FileName.endswith('.pkl'):
with open(FileName, "rb") as PklFile:
GenCfgData.__dict__ = marshal.load(PklFile)
elif FileName.endswith('.dsc'):
if GenCfgData.ParseDscFile(FileName) != 0:
raise Exception(GenCfgData.Error)
if GenCfgData.CreateVarDict() != 0:
raise Exception(GenCfgData.Error)
else:
raise Exception('Unsupported file "%s" !' % FileName)
GenCfgData.UpdateDefaultValue()
return GenCfgData
def About(self):
Msg = 'Configuration Editor\n--------------------------------\nVersion 0.5\n2018'
Lines = Msg.split('\n')
Width = 30
Text = []
for Line in Lines:
Text.append(Line.center(Width, ' '))
messagebox.showinfo('Config Editor', '\n'.join(Text))
def UpdateLastDir (self, Path):
self.LastDir = os.path.dirname(Path)
def GetOpenFileName(self, Type):
if self.IsConfigDataLoaded():
if Type == 'dlt':
Question = ''
elif Type == 'bin':
Question = 'All configuration will be reloaded from BIN file, continue ?'
elif Type == 'dsc':
Question = ''
else:
raise Exception('Unsupported file type !')
if Question:
Reply = messagebox.askquestion('', Question, icon='warning')
if Reply == 'no':
return None
if Type == 'dsc':
FileType = 'DSC or PKL'
FileExt = 'pkl *Def.dsc'
else:
FileType = Type.upper()
FileExt = Type
Path = filedialog.askopenfilename(
initialdir=self.LastDir,
title="Load file",
filetypes=(("%s files" % FileType, "*.%s" % FileExt), (
"all files", "*.*")))
if Path:
self.UpdateLastDir (Path)
return Path
else:
return None
def LoadFromDelta(self):
Path = self.GetOpenFileName('dlt')
if not Path:
return
self.LoadDeltaFile (Path)
def LoadDeltaFile (self, Path):
self.ReloadConfigDataFromBin(self.OrgCfgDataBin)
try:
self.CfgDataObj.OverrideDefaultValue(Path)
self.CfgDataObj.UpdateDefaultValue()
except Exception as e:
messagebox.showerror('LOADING ERROR', str(e))
return
self.UpdateLastDir (Path)
self.RefreshConfigDataPage()
def LoadFromBin(self):
Path = self.GetOpenFileName('bin')
if not Path:
return
with open(Path, 'rb') as Fd:
BinData = bytearray(Fd.read())
self.ReloadConfigDataFromBin(BinData)
try:
self.ReloadConfigDataFromBin(BinData)
except Exception as e:
messagebox.showerror('LOADING ERROR', str(e))
return
def LoadDscFile(self, Path):
# Save current values in widget and clear database
self.ClearWidgetsInLayout()
self.Left.delete(*self.Left.get_children())
try:
self.CfgDataObj = self.LoadConfigData(Path)
except Exception as e:
messagebox.showerror('LOADING ERROR', str(e))
return -1
self.UpdateLastDir (Path)
self.OrgCfgDataBin = self.CfgDataObj.GenerateBinaryArray()
self.BuildConfigPageTree(self.CfgDataObj._CfgPageTree['root'], '')
for Menu in self.MenuString:
self.FileMenu.entryconfig(Menu, state="normal")
return 0
def LoadFromDsc(self):
Path = self.GetOpenFileName('dsc')
if not Path:
return
self.LoadDscFile(Path)
def GetSaveFileName (self, Extension):
Path = filedialog.asksaveasfilename(
initialdir=self.LastDir,
title="Save file",
defaultextension=Extension)
if Path:
self.LastDir = os.path.dirname(Path)
return Path
else:
return None
def SaveDeltaFile(self, Full=False):
Path = self.GetSaveFileName (".dlt")
if not Path:
return
self.UpdateConfigDataOnPage()
self.GenerateDeltaFile(Path, Full)
def SaveToDelta(self):
self.SaveDeltaFile()
def SaveFullToDelta(self):
self.SaveDeltaFile(True)
def SaveToBin(self):
Path = self.GetSaveFileName (".bin")
if not Path:
return
self.UpdateConfigDataOnPage()
with open(Path, 'wb') as Fd:
Bins = self.CfgDataObj.GenerateBinaryArray()
Fd.write(Bins)
def GenerateDeltaFile(self, DeltaFile, Full=False):
NewData = self.CfgDataObj.GenerateBinaryArray()
Lines = []
TagName = ''
Level = 0
PlatformId = None
DefPlatformId = 0
for Item in self.CfgDataObj._CfgItemList:
if Level == 0 and Item['embed'].endswith(':START'):
TagName = Item['embed'].split(':')[0]
Level += 1
Start = Item['offset']
End = Start + Item['length']
FullName = '%s.%s' % (TagName, Item['cname'])
if 'PLATFORMID_CFG_DATA.PlatformId' == FullName:
DefPlatformId = Bytes2Val(self.OrgCfgDataBin[Start:End])
if NewData[Start:End] != self.OrgCfgDataBin[Start:End] or (
Full and Item['name'] and (Item['cname'] != 'Dummy')):
if not Item['subreg']:
ValStr = self.CfgDataObj.FormatDeltaValue (Item)
Text = '%-40s | %s' % (FullName, ValStr)
if 'PLATFORMID_CFG_DATA.PlatformId' == FullName:
PlatformId = Array2Val(Item['value'])
else:
Lines.append(Text)
else:
OldArray = self.OrgCfgDataBin[Start:End]
NewArray = NewData[Start:End]
for SubItem in Item['subreg']:
NewBitValue = self.CfgDataObj.GetBsfBitFields(SubItem,
NewArray)
OldBitValue = self.CfgDataObj.GetBsfBitFields(SubItem,
OldArray)
if OldBitValue != NewBitValue or (
Full and Item['name'] and
(Item['cname'] != 'Dummy')):
if SubItem['cname'].startswith(Item['cname']):
Offset = len(Item['cname']) + 1
FieldName = '%s.%s' % (
FullName, SubItem['cname'][Offset:])
ValStr = self.CfgDataObj.FormatDeltaValue (SubItem)
Text = '%-40s | %s' % (FieldName, ValStr)
Lines.append(Text)
if Item['embed'].endswith(':END'):
EndTagName = Item['embed'].split(':')[0]
if EndTagName == TagName:
Level -= 1
if PlatformId is None or DefPlatformId == PlatformId:
PlatformId = DefPlatformId
print("WARNING: 'PlatformId' configuration is same as default %d!"
% PlatformId)
Lines.insert(0, '%-40s | %s\n\n' %
('PLATFORMID_CFG_DATA.PlatformId', '0x%04X' % PlatformId))
self.CfgDataObj.WriteDeltaFile(DeltaFile, PlatformId, Lines)
def RefreshConfigDataPage(self):
self.ClearWidgetsInLayout()
self.OnConfigPageSelectChange(None)
def ReloadConfigDataFromBin(self, BinDat):
self.CfgDataObj.LoadDefaultFromBinaryArray(BinDat)
self.RefreshConfigDataPage()
def NormalizeValueStr(self, Item, ValueStr):
IsArray = True if Item['value'].startswith('{') else False
if IsArray and not ValueStr.startswith('{'):
ValueStr = "{ %s }" % ValueStr
try:
OldBytes = self.CfgDataObj.ValueToByteArray(Item['value'],
Item['length'])
NewBytes = self.CfgDataObj.ValueToByteArray(ValueStr,
Item['length'])
if OldBytes == NewBytes:
NewValue = Item['value']
else:
if IsArray:
NewValue = Bytes2Str(NewBytes)
else:
if Item['value'].startswith('0x'):
HexLen = Item['length'] * 2
if len(Item['value']) == HexLen + 2:
Fmt = '0x%%0%dX' % HexLen
else:
Fmt = '0x%X'
else:
Fmt = '%d'
NewValue = Fmt % Bytes2Val(NewBytes)
except:
NewValue = Item['value']
return NewValue
def SetConfigItemValue(self, Item, ValueStr):
Type = Item['type'].split(',')[0]
if Type == "Table":
NewValue = ValueStr
elif Type == "EditText":
NewValue = ValueStr[:Item['length']]
if Item['value'].startswith("'"):
NewValue = "'%s'" % NewValue
else:
NewValue = self.NormalizeValueStr(Item, ValueStr)
if Item['value'] != NewValue:
if self.Debug:
print('Update %s from %s to %s !' % (Item['cname'],
Item['value'], NewValue))
Item['value'] = NewValue
def SyncConfigDataFromSubRegion(self):
if not len(self.PageList) or not self.PageId:
return
for Item in self.GetCurrentConfigData():
if not Item['subreg']:
continue
ValArray = self.CfgDataObj.ValueToByteArray(Item['value'],
Item['length'])
for SubItem in Item['subreg']:
Value = Array2Val(SubItem['value'])
self.CfgDataObj.UpdateBsfBitFields(SubItem, Value, ValArray)
if Item['value'].startswith('{'):
NewValue = Bytes2Str(ValArray)
else:
BitsValue = ''.join('{0:08b}'.format(i)
for i in ValArray[::-1])
NewValue = '0x%X' % (int(BitsValue, 2))
NewValue = self.NormalizeValueStr(Item, NewValue)
if NewValue != Item['value']:
if self.Debug:
print('Update Subregion %s from %s to %s' %
(Item['cname'], Item['value'], NewValue))
Item['value'] = NewValue
def GetConfigDataItemFromWidget(self, Widget, Label=False):
Name = self.GetObjectName(Widget)
if not Name or not len(self.PageList):
return None
if Label and Name.startswith('LABEL_'):
Name = Name[6:]
Item = 0
for Conf in self.GetCurrentConfigData():
if Conf['subreg']:
for SubConf in Conf['subreg']:
if SubConf['cname'] == Name:
Item = SubConf
break
else:
if Conf['cname'] == Name:
Item = Conf
break
if Item:
break
return Item
def UpdateConfigDataFromWidget(self, Widget, Args):
Item = self.GetConfigDataItemFromWidget(Widget)
if Item is None:
return
elif not Item:
if isinstance(Widget, Label):
return
raise Exception('Failed to find "%s" !' %
self.GetObjectName(Widget))
Type = Item['type'].split(',')[0]
if Type == "Combo":
if Item['option'] in self.CfgDataObj._BuidinOption:
OptList = [('0', 'Disable'), ('1', 'Enable')]
else:
OptList = self.CfgDataObj.GetItemOptionList(Item)
TmpList = [Opt[0] for Opt in OptList]
Idx = Widget.current()
self.SetConfigItemValue(Item, TmpList[Idx])
elif Type in ["EditNum", "EditText"]:
self.SetConfigItemValue(Item, Widget.get())
elif Type in ["Table"]:
NewValue = Bytes2Str(Widget.get())
self.SetConfigItemValue(Item, NewValue)
def GetConfigDataItemFromName(self, Name, InPage=True):
CfgItemList = self.GetCurrentConfigData(
) if InPage else self.CfgDataObj._CfgItemList
for Item in CfgItemList:
if not Item['length'] or not Item['name']:
continue
if Item['subreg']:
for SubItem in Item['subreg']:
if not SubItem['name']:
continue
if SubItem['cname'] == Name:
return SubItem
else:
if Item['cname'] == Name:
return Item
return None
def EvaluateCondition(self, Cond):
Item = None
ReplaceList = []
Parts = re.findall("\$(\w+)(\.\w+)?", Cond)
for Part in Parts:
if len(Part) > 1 and len(Part[1]) > 0:
Name = Part[0] + '_' + Part[1][1:]
else:
Name = Part[0]
Value = None
Item = self.GetConfigDataItemFromName(Name, False)
if Item:
try:
Value = '0x%x' % Bytes2Val(
self.CfgDataObj.ValueToByteArray(Item['value'], Item[
'length']))
except:
Value = None
ReplaceList.append(('$' + ''.join(Part), Value))
OrgCond = Cond
Result = True
ReplaceList.sort(key=lambda x: len(x[0]), reverse=True)
for Each in ReplaceList:
if Each[1] is None:
break
Cond = Cond.replace(Each[0], Each[1])
try:
Result = True if eval(Cond) else False
except:
print("WARNING: Condition '%s' is invalid!" % OrgCond)
return Result
def AddConfigItem(self, Item, Row):
Parent = self.RightGrid
Name = Label(Parent, text=Item['name'], anchor="w")
Parts = Item['type'].split(',')
Type = Parts[0]
Widget = None
if Type == "Combo":
# Build
if Item['option'] in self.CfgDataObj._BuidinOption:
OptList = [('0', 'Disable'), ('1', 'Enable')]
else:
OptList = self.CfgDataObj.GetItemOptionList(Item)
CurrentValue = self.CfgDataObj.ValueToByteArray(Item['value'],
Item['length'])
OptionList = []
Current = None
for Idx, Option in enumerate(OptList):
OptionValue = self.CfgDataObj.ValueToByteArray(Option[0],
Item['length'])
if OptionValue == CurrentValue:
Current = Idx
OptionList.append(Option[1])
Widget = ttk.Combobox(Parent, value=OptionList, state="readonly")
Widget.bind("<<ComboboxSelected>>", self.ComboSelectChanged)
if Current is None:
print('WARNING: Value "%s" is an invalid option for "%s" !' %
(Bytes2Val(CurrentValue), Item['cname']))
else:
Widget.current(Current)
elif Type in ["EditNum", "EditText"]:
TxtVal = StringVar()
Widget = Entry(Parent, textvariable=TxtVal)
Value = Item['value'].strip("'")
if Type in ["EditText"]:
TxtVal.trace(
'w',
lambda *args: self.LimitEntrySize(TxtVal, Item['length']))
elif Type in ["EditNum"]:
Value = Item['value'].strip("{").strip("}").strip()
Widget.bind("<FocusOut>", self.EditNumFinished)
TxtVal.set(Value)
elif Type in ["Table"]:
Bins = self.CfgDataObj.ValueToByteArray(Item['value'],
Item['length'])
ColHdr = Item['option'].split(',')
Widget = CustomTable(Parent, ColHdr, Bins)
if Widget:
Ttp = CreateToolTip(Widget, Item['help'])
self.SetObjectName(Name, 'LABEL_' + Item['cname'])
self.SetObjectName(Widget, Item['cname'])
Name.grid(row=Row, column=0, padx=10, pady=5, sticky="nsew")
Widget.grid(row=Row + 1, rowspan=1, column=0, padx=10, pady=5, sticky="nsew")
def UpdateConfigDataOnPage(self):
self.WalkWidgetsInLayout(self.RightGrid,
self.UpdateConfigDataFromWidget)
self.SyncConfigDataFromSubRegion()
if __name__ == '__main__':
Root = Tk()
App = Application(master=Root)
Root.title("Config Editor")
Root.mainloop()