Skip to content

Commit

Permalink
Update colors without clobbering user settings
Browse files Browse the repository at this point in the history
  • Loading branch information
zackelia committed Nov 11, 2020
1 parent f03ed27 commit 0c22814
Show file tree
Hide file tree
Showing 3 changed files with 203 additions and 2 deletions.
9 changes: 7 additions & 2 deletions install.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
import subprocess
import urllib.request

from tcd_browser import TCDBrowser
from preferences import preferences

parser = argparse.ArgumentParser(description="Install Ghidra dark theme")
parser.add_argument("--path", dest="install_path", type=str, default=None,
help="The installation path for Ghidra")
Expand Down Expand Up @@ -100,6 +103,8 @@
with open(perferences_path, "a") as fp:
fp.write("LastLookAndFeel=System\n")

# Backup the current tcd and copy the new one
# Backup the current tcd
shutil.copy(code_browser_path, code_browser_bak_path)
shutil.copy("_code_browser.tcd", code_browser_path)

browser = TCDBrowser(code_browser_path)
browser.update(preferences)
115 changes: 115 additions & 0 deletions preferences.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
from typing import Tuple

class State():
def __init__(self, value, name=""):
self.tag = "STATE"
self.name = name
self.value = str(value)
if type(value) == str:
self.type = "string"
elif type(value) == bool:
self.type = "boolean"
elif type(value) == int:
self.type = "int"
else:
self.type = "unknown"

class Wrapped():
def __init__(self, *states: Tuple[State]):
self.tag = "WRAPPED_OPTION"
self.states = states
self.classname = "ghidra.framework.options.Wrapped{}".format(self.__class__.__name__)

class Color(Wrapped):
def __init__(self, color: str):
super().__init__(
State(color, "color")
)

class Font(Wrapped):
def __init__(self, size: int, style: int, family: str):
super().__init__(
State(size, "size"),
State(style, "style"),
State(family, "family")
)

class KeyStroke(Wrapped):
def __init__(self, keyCode: int, modifiers: int):
super().__init__(
State(keyCode, "KeyCode"),
State(modifiers, "Modifiers")
)

preferences = {
"Listing Fields": {
"Cursor Text Highlight.Highlight Color": Color(-13157567),
"Cursor Text Highlight.Scoped Write Highlight Color": Color(-13157567),
"Cursor Text Highlight.Scoped Read Highlight Color": Color(-13157567),
"Selection Colors.Selection Color": Color(-11118501),
"Selection Colors.Difference Color": Color(-11118501),
"Selection Colors.Highlight Color": Color(-11118501),
"Cursor.Cursor Color - Focused": Color(-3815226),
"Cursor.Cursor Color - Unfocused": Color(-13157567),
"Cursor.Highlight Cursor Line Color": Color(-13157567),
},
"Decompiler": {
"Display.Color for Keywords": Color(-2190497),
"Display.Background Color": Color(-14144978),
"Display.Color for Parameters": Color(-8034417),
"Display.Color for Constants": Color(-5946814),
"Display.Color for Current Variable Highlight": Color(-13157567),
"Display.Color Default": Color(-3815226),
"Display.Color for Types": Color(-7564224),
"Display.Color for Variables": Color(-3815226),
"Display.Color for Comments": Color(-10518115),
"Display.Color for Function names": Color(-10580601),
"Display.Font": Font(14, 0, "Monospaced")
},
"Search": {
"Highlight Color for Current Match": Color(-11974594),
"Highlight Color": Color(-11974594),
},
"Listing Display": {
"Background Color": Color(-14144978),
"Mnemonic Color": Color(-3815226),
"Bad Reference Address Color": Color(-5946814),
"XRef Write Color": Color(-2190497),
"Address Color": Color(-10066330),
"Function Parameters Color": Color(-3815226),
"Function Return Type Color": Color(-3815226),
"Comment, Referenced Repeatable Color": Color(-10518115),
"Constant Color": Color(-5946814),
"XRef Other Color": Color(-3815226),
"EOL Comment Color": Color(-10518115),
"Labels, Primary Color": Color(-10518115),
"Function Tag Color": Color(-8034417),
"Bytes Color": Color(-8281410),
"Post-Comment Color": Color(-10518115),
"Function Call-Fixup Color": Color(-5073733),
"Plate Comment Color": Color(-10518115),
"Labels, Unreferenced Color": Color(-3815226),
"Entry Point Color": Color(-3815226),
"Pre-Comment Color": Color(-10518115),
"Mnemonic, Override Color": Color(-3815226),
"External Reference, Resolved Color": Color(-10580601),
"Parameter, Dynamic Storage Color": Color(-10580601),
"Parameter, Custom Storage Color": Color(-8034417),
"Underline Color": Color(-5073733),
"Field Name Color": Color(-3815226),
"XRef Read Color": Color(-10518115),
"Separator Color": Color(-3815226),
"Version Track Color": Color(-5073733),
"Comment, Automatic Color": Color(-10518115),
"XRef Color": Color(-7564224),
"Variable Color": Color(-8034417),
"Flow Arrow, Active Color": Color(-3815226),
"Labels, Local Color": Color(-7564224),
"Function Name Color": Color(-10580601),
"Comment, Repeatable Color": Color(-10518115),
"BASE FONT": Font(14, 0, "Monospaced"),
},
"Comments": {
"Enter accepts comment": State(True)
},
}
81 changes: 81 additions & 0 deletions tcd_browser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
"""Represents _code_browser.tcd"""
import sys
import xml.etree.ElementTree as ET
import xml.dom.minidom as MD
from preferences import Wrapped

class TCDBrowser:
def __init__(self, path: str) -> None:
self.path = path
tree = ET.parse(path)
self.root = tree.getroot()

def _indent(self, elem, level=0):
"""Spacing fixup after adding new things"""
i = "\n" + level*" "
if len(elem):
if not elem.text or not elem.text.strip():
elem.text = i + " "
if not elem.tail or not elem.tail.strip():
elem.tail = i
for elem in elem:
self._indent(elem, level+1)
if not elem.tail or not elem.tail.strip():
elem.tail = i
else:
if level and (not elem.tail or not elem.tail.strip()):
elem.tail = i

def update(self, preferences):
# List comprehensions instead of a ton of nested statements
tool = [_ for _ in self.root.iter() if _.tag == "TOOL"][0]
tool_options = [_ for _ in tool.iter() if _.tag == "OPTIONS"][0]
categories = [_ for _ in tool_options.iter() if _.tag == "CATEGORY" and "NAME" in _.attrib]

# Add missing category tags
names = [_.get("NAME") for _ in categories]
for category in preferences.keys():
if category not in names:
c = ET.Element("CATEGORY")
c.set("NAME", category)
tool_options.insert(sys.maxsize, c)

# Remake the categories list in case we added new ones
categories = [_ for _ in tool_options.iter() if _.tag == "CATEGORY" and "NAME" in _.attrib]

for category in categories:
for preference, option in preferences[category.get("NAME")].items():
element = [_ for _ in category.iter() if _.get("NAME") == preference]

# Check if the preference exists or not
if len(element) == 0:
e = ET.Element(option.tag)
e.set("NAME", preference)

if isinstance(option, Wrapped):
e.set("CLASS", option.classname)
for state in option.states:
s = ET.SubElement(e, state.tag)
s.set("NAME", state.name)
s.set("TYPE", state.type)
s.set("VALUE", state.value)
else:
e.set("TYPE", option.type)
e.set("VALUE", option.value)

category.insert(sys.maxsize, e)

else:
element = element[0]
if isinstance(option, Wrapped):
for state in option.states:
e = [_ for _ in category.iter() if _.get("NAME") == state.name][0]
e.set("VALUE", state.value)
else:
element.set("VALUE", option.value)

self._indent(self.root)

xmlstr = MD.parseString(ET.tostring(self.root)).toprettyxml(indent="", newl="", encoding="UTF-8")
with open(self.path, "wb") as f:
f.write(xmlstr)

0 comments on commit 0c22814

Please sign in to comment.