11#!/usr/bin/env python
2- import sys , os , binascii , base64
3- import queue , json , ctypes # Fix for pyinstaller failing to bundle dependencies
2+ import sys , os , binascii , base64 , json , re , queue , ctypes
3+ from collections import OrderedDict
44try :
55 import Tkinter as tk
66 import ttk
@@ -19,51 +19,59 @@ class ProperTree:
1919 self .tk = tk .Tk ()
2020 self .tk .title ("Convert Values" )
2121 self .tk .minsize (width = 640 ,height = 130 )
22- self .tk .resizable (False , False )
23- # self.tk.columnconfigure(2,weight=1)
22+ self .tk .resizable (True , False )
23+ self .tk .columnconfigure (2 ,weight = 1 )
24+ self .tk .columnconfigure (3 ,weight = 1 )
2425 # Build the Hex <--> Base64 converter
2526 f_label = tk .Label (self .tk , text = "From:" )
2627 f_label .grid (row = 0 ,column = 0 )
2728 t_label = tk .Label (self .tk , text = "To:" )
2829 t_label .grid (row = 1 ,column = 0 )
30+
2931 # Setup the from/to option menus
3032 f_title = tk .StringVar (self .tk )
3133 t_title = tk .StringVar (self .tk )
3234 f_title .set ("Base64" )
3335 t_title .set ("Hex" )
34- f_option = tk .OptionMenu (self .tk , f_title , "Ascii" , "Base64" , "Hex" , command = self .change_from_type )
35- t_option = tk .OptionMenu (self .tk , t_title , "Ascii" , "Base64" , "Hex" , command = self .change_to_type )
36+ f_option = tk .OptionMenu (self .tk , f_title , "Ascii" , "Base64" , "Decimal" , " Hex" , command = self .change_from_type )
37+ t_option = tk .OptionMenu (self .tk , t_title , "Ascii" , "Base64" , "Decimal" , " Hex" , command = self .change_to_type )
3638 self .from_type = "Base64"
3739 self .to_type = "Hex"
3840 f_option .grid (row = 0 ,column = 1 ,sticky = "we" )
3941 t_option .grid (row = 1 ,column = 1 ,sticky = "we" )
4042
41- self .f_text = tk .Entry (self .tk , width = 80 )
43+ self .f_text = tk .Entry (self .tk )
4244 self .f_text .delete (0 ,tk .END )
4345 self .f_text .insert (0 ,"" )
44- self .f_text .grid (row = 0 ,column = 2 ,sticky = "we" ,padx = 10 ,pady = 10 )
46+ self .f_text .grid (row = 0 ,column = 2 ,columnspan = 2 , sticky = "we" ,padx = 10 ,pady = 10 )
4547
46- self .t_text = tk .Entry (self .tk , width = 80 )
48+ self .t_text = tk .Entry (self .tk )
4749 self .t_text .configure (state = 'normal' )
4850 self .t_text .delete (0 ,tk .END )
4951 self .t_text .insert (0 ,"" )
5052 self .t_text .configure (state = 'readonly' )
51- self .t_text .grid (row = 1 ,column = 2 ,sticky = "we" ,padx = 10 ,pady = 10 )
53+ self .t_text .grid (row = 1 ,column = 2 ,columnspan = 2 , sticky = "we" ,padx = 10 ,pady = 10 )
5254
5355 self .c_button = tk .Button (self .tk , text = "Convert" , command = self .convert_values )
54- self .c_button .grid (row = 2 ,column = 2 ,sticky = "e" ,padx = 10 ,pady = 10 )
56+ self .c_button .grid (row = 2 ,column = 3 ,sticky = "e" ,padx = 10 ,pady = 10 )
57+
58+ self .f_text .bind ("<Return>" , self .convert_values )
59+ self .f_text .bind ("<KP_Enter>" , self .convert_values )
5560
56- self .tk .bind ("<Return>" , self .convert_values )
57- self .tk .bind ("<KP_Enter>" , self .convert_values )
61+ self .start_window = None
5862
59- self .clipboard = None
63+ # Regex to find the processor serial numbers when
64+ # opened from the Finder
65+ self .regexp = re .compile (r"^-psn_[0-9]+_[0-9]+$" )
6066
6167 # Setup the menu-related keybinds - and change the app name if needed
6268 key = "Control"
6369 sign = "Ctr+"
6470 if str (sys .platform ) == "darwin" :
6571 # Remap the quit function to our own
6672 self .tk .createcommand ('::tk::mac::Quit' , self .quit )
73+ self .tk .createcommand ("::tk::mac::OpenDocument" , self .open_plist_from_app )
74+ self .tk .createcommand ("::tk::mac::ReopenApplication" , self .open_plist_from_app )
6775 # Import the needed modules to change the bundle name and force focus
6876 try :
6977 from Foundation import NSBundle
@@ -93,6 +101,7 @@ class ProperTree:
93101 file_menu .add_command (label = "Save ({}S)" .format (sign ), command = self .save_plist )
94102 file_menu .add_command (label = "Save As ({}Shift+S)" .format (sign ), command = self .save_plist_as )
95103 file_menu .add_command (label = "Duplicate ({}D)" .format (sign ), command = self .duplicate_plist )
104+ file_menu .add_command (label = "Reload From Disk ({}L)" .format (sign ), command = self .reload_from_disk )
96105 file_menu .add_separator ()
97106 file_menu .add_command (label = "OC Snapshot ({}R)" .format (sign ), command = self .oc_snapshot )
98107 file_menu .add_separator ()
@@ -107,38 +116,83 @@ class ProperTree:
107116 self .tk .config (menu = main_menu )
108117
109118 # Set bindings
119+ self .tk .bind ("<{}-w>" .format (key ), self .close_window )
110120 self .tk .bind_all ("<{}-n>" .format (key ), self .new_plist )
111121 self .tk .bind_all ("<{}-o>" .format (key ), self .open_plist )
112122 self .tk .bind_all ("<{}-s>" .format (key ), self .save_plist )
113123 self .tk .bind_all ("<{}-S>" .format (key ), self .save_plist_as )
114124 self .tk .bind_all ("<{}-d>" .format (key ), self .duplicate_plist )
115- self .tk .bind_all ("<{}-c>" .format (key ), self .copy_selection )
116- self .tk .bind_all ("<{}-v>" .format (key ), self .paste_selection )
117125 self .tk .bind_all ("<{}-t>" .format (key ), self .show_convert )
118126 self .tk .bind_all ("<{}-z>" .format (key ), self .undo )
119127 self .tk .bind_all ("<{}-Z>" .format (key ), self .redo )
120128 self .tk .bind_all ("<{}-m>" .format (key ), self .strip_comments )
121129 self .tk .bind_all ("<{}-r>" .format (key ), self .oc_snapshot )
130+ self .tk .bind_all ("<{}-l>" .format (key ), self .reload_from_disk )
122131 if not str (sys .platform ) == "darwin" :
123132 # Rewrite the default Command-Q command
124133 self .tk .bind_all ("<{}-q>" .format (key ), self .quit )
125134
135+ cwd = os .getcwd ()
136+ os .chdir (os .path .dirname (os .path .realpath (__file__ )))
137+ settings = {}
138+ try :
139+ if os .path .exists ("Scripts/settings.json" ):
140+ settings = json .load (open ("Scripts/settings.json" ))
141+ except :
142+ pass
143+ self .xcode_data = settings .get ("xcode_data" ,True ) # keep <data>xxxx</data> in one line when true
144+ self .sort_dict = settings .get ("sort_dict" ,False ) # Preserve key ordering in dictionaries when loading/saving
145+ os .chdir (cwd )
146+
147+ # Wait before opening a new document to see if we need to.
148+ # This was annoying to debug, but seems to work.
149+ self .tk .after (100 , lambda :self .check_open (plists ))
150+
151+ # Start our run loop
152+ tk .mainloop ()
153+
154+ def check_open (self , plists = []):
155+ plists = [x for x in plists if not self .regexp .search (x )]
126156 if isinstance (plists , list ) and len (plists ):
127- self .start_window = None
128157 # Iterate the passed plists and open them
129158 for p in set (plists ):
130- self .open_plist_with_path (None ,p ,None )
131- else :
159+ window = self .open_plist_with_path (None ,p ,None )
160+ if self .start_window == None :
161+ self .start_window = window
162+ elif not len (self .stackorder (self .tk )):
132163 # create a fresh plist to start
133164 self .start_window = self .new_plist ()
134165
135- # Start our run loop
136- tk .mainloop ()
166+ def open_plist_from_app (self , * args ):
167+ if isinstance (args , str ):
168+ args = [args ]
169+ args = [x for x in args if not self .regexp .search (x )]
170+ for arg in args :
171+ # Let's load the plist
172+ if self .start_window == None :
173+ self .start_window = self .open_plist_with_path (None ,arg ,None )
174+ elif self .start_window .current_plist == None :
175+ self .open_plist_with_path (None ,arg ,self .start_window )
176+ else :
177+ self .open_plist_with_path (None ,arg ,None )
178+
179+ def change_hd_type (self , value ):
180+ self .hd_type = value
181+
182+ def reload_from_disk (self , event = None ):
183+ windows = self .stackorder (self .tk )
184+ if not len (windows ):
185+ # Nothing to do
186+ return
187+ window = windows [- 1 ] # Get the last item (most recent)
188+ if window == self .tk :
189+ return
190+ window .reload_from_disk (event )
137191
138192 def change_data_display (self , new_data = None ):
139193 windows = self .stackorder (self .tk )
140194 if not len (windows ):
141- # Nothing to save
195+ # Nothing to do
142196 return
143197 window = windows [- 1 ] # Get the last item (most recent)
144198 if window == self .tk :
@@ -148,7 +202,7 @@ class ProperTree:
148202 def oc_snapshot (self , event = None ):
149203 windows = self .stackorder (self .tk )
150204 if not len (windows ):
151- # Nothing to save
205+ # Nothing to do
152206 return
153207 window = windows [- 1 ] # Get the last item (most recent)
154208 if window == self .tk :
@@ -157,10 +211,10 @@ class ProperTree:
157211
158212 def close_window (self , event = None , check_close = True ):
159213 # Remove the default window that comes from it
160- if str (sys .platform ) == "darwin" :
161- self .tk .iconify ()
162- else :
163- self .tk .withdraw ()
214+ # if str(sys.platform) == "darwin":
215+ # self.tk.iconify()
216+ # else:
217+ self .tk .withdraw ()
164218 if check_close :
165219 windows = self .stackorder (self .tk )
166220 if not len (windows ):
@@ -170,7 +224,7 @@ class ProperTree:
170224 def strip_comments (self , event = None ):
171225 windows = self .stackorder (self .tk )
172226 if not len (windows ):
173- # Nothing to save
227+ # Nothing to do
174228 return
175229 window = windows [- 1 ] # Get the last item (most recent)
176230 if window == self .tk :
@@ -196,27 +250,34 @@ class ProperTree:
196250 if self .from_type .lower () == "hex" :
197251 if from_value .lower ().startswith ("0x" ):
198252 from_value = from_value [2 :]
199- from_value = from_value .replace (" " ,"" )
253+ from_value = from_value .replace (" " ,"" ). replace ( "<" , "" ). replace ( ">" , "" )
200254 if [x for x in from_value if x .lower () not in "0123456789abcdef" ]:
201255 self .tk .bell ()
202256 mb .showerror ("Invalid Hex Data" ,"Invalid character in passed hex data." ,parent = self .tk )
203257 return
204258 try :
259+ if self .from_type .lower () == "decimal" :
260+ # Convert to hex bytes
261+ from_value = "{:x}" .format (int (from_value ))
262+ if len (from_value ) % 2 :
263+ from_value = "0" + from_value
205264 # Handle the from data
206265 if sys .version_info >= (3 ,0 ):
207266 # Convert to bytes
208267 from_value = from_value .encode ("utf-8" )
209268 if self .from_type .lower () == "base64" :
210269 from_value = base64 .b64decode (from_value )
211- elif self .from_type .lower () == "hex" :
270+ elif self .from_type .lower () in [ "hex" , "decimal" ] :
212271 from_value = binascii .unhexlify (from_value )
213272 # Let's get the data converted
214273 to_value = from_value
215274 if self .to_type .lower () == "base64" :
216275 to_value = base64 .b64encode (from_value )
217276 elif self .to_type .lower () == "hex" :
218277 to_value = binascii .hexlify (from_value )
219- if sys .version_info >= (3 ,0 ):
278+ elif self .to_type .lower () == "decimal" :
279+ to_value = str (int (binascii .hexlify (from_value ),16 ))
280+ if sys .version_info >= (3 ,0 ) and not self .to_type .lower () == "decimal" :
220281 # Convert to bytes
221282 to_value = to_value .decode ("utf-8" )
222283 if self .to_type .lower () == "hex" :
@@ -235,36 +296,10 @@ class ProperTree:
235296 # Save/Load Plist Functions #
236297 ### ###
237298
238- def copy_selection (self , event = None ):
239- windows = self .stackorder (self .tk )
240- if not len (windows ):
241- # Nothing to save
242- return
243- window = windows [- 1 ] # Get the last item (most recent)
244- if window == self .tk :
245- return
246- node = window ._tree .focus ()
247- if node == "" :
248- # Nothing to copy
249- return
250- self .clipboard = window .nodes_to_values (node ,{})
251-
252- def paste_selection (self , event = None ):
253- if self .clipboard == None :
254- return
255- windows = self .stackorder (self .tk )
256- if not len (windows ):
257- # Nothing to save
258- return
259- window = windows [- 1 ] # Get the last item (most recent)
260- if window == self .tk :
261- return
262- window .paste_selection (self .clipboard )
263-
264299 def duplicate_plist (self , event = None ):
265300 windows = self .stackorder (self .tk )
266301 if not len (windows ):
267- # Nothing to save
302+ # Nothing to do
268303 return
269304 window = windows [- 1 ] # Get the last item (most recent)
270305 if window == self .tk :
@@ -275,7 +310,7 @@ class ProperTree:
275310 def save_plist (self , event = None ):
276311 windows = self .stackorder (self .tk )
277312 if not len (windows ):
278- # Nothing to save
313+ # Nothing to do
279314 return
280315 window = windows [- 1 ] # Get the last item (most recent)
281316 if window == self .tk :
@@ -285,7 +320,7 @@ class ProperTree:
285320 def save_plist_as (self , event = None ):
286321 windows = self .stackorder (self .tk )
287322 if not len (windows ):
288- # Nothing to save
323+ # Nothing to do
289324 return
290325 window = windows [- 1 ] # Get the last item (most recent)
291326 if window == self .tk :
@@ -295,7 +330,7 @@ class ProperTree:
295330 def undo (self , event = None ):
296331 windows = self .stackorder (self .tk )
297332 if not len (windows ):
298- # Nothing to save
333+ # Nothing to do
299334 return
300335 window = windows [- 1 ] # Get the last item (most recent)
301336 if window == self .tk :
@@ -305,7 +340,7 @@ class ProperTree:
305340 def redo (self , event = None ):
306341 windows = self .stackorder (self .tk )
307342 if not len (windows ):
308- # Nothing to save
343+ # Nothing to do
309344 return
310345 window = windows [- 1 ] # Get the last item (most recent)
311346 if window == self .tk :
@@ -348,10 +383,11 @@ class ProperTree:
348383 if path == None :
349384 # Uh... wut?
350385 return
386+ path = os .path .realpath (os .path .expanduser (path ))
351387 # Let's try to load the plist
352388 try :
353389 with open (path ,"rb" ) as f :
354- plist_data = plist .load (f )
390+ plist_data = plist .load (f , dict_type = dict if self . sort_dict else OrderedDict )
355391 except Exception as e :
356392 # Had an issue, throw up a display box
357393 # print("{}\a".format(str(e)))
0 commit comments