@@ -354,6 +354,8 @@ def __init__(self, controller, root, **kw):
354354 file_menu .add_command (label = "Save As ({}Shift+S)" .format (sign ), command = self .controller .save_plist_as )
355355 file_menu .add_command (label = "Duplicate ({}D)" .format (sign ), command = self .controller .duplicate_plist )
356356 file_menu .add_separator ()
357+ file_menu .add_command (label = "OC Snapshot ({}R)" .format (sign ), command = self .oc_snapshot )
358+ file_menu .add_separator ()
357359 file_menu .add_command (label = "Convert Window ({}T)" .format (sign ), command = self .controller .show_convert )
358360 file_menu .add_command (label = "Strip Comments ({}M)" .format (sign ), command = self .strip_comments )
359361 file_menu .add_separator ()
@@ -384,6 +386,176 @@ def __init__(self, controller, root, **kw):
384386 self ._tree .pack (side = "bottom" , fill = "both" , expand = True )
385387 self .entry_popup = None
386388
389+ def walk_kexts (self ,path ,parent = "" ):
390+ kexts = []
391+ for x in os .listdir (path ):
392+ if x .startswith ("." ):
393+ continue
394+ if not x .lower ().endswith (".kext" ):
395+ continue
396+ kdir = os .path .join (path ,x )
397+ if not os .path .isdir (kdir ):
398+ continue
399+ kdict = {
400+ "Comment" :"" ,
401+ "Enabled" :True ,
402+ "MatchKernel" :"" ,
403+ "BundlePath" :parent + "/" + x if len (parent ) else x
404+ }
405+ # Should be valid-ish - let's check for a binary
406+ binpath = os .path .join (kdir ,"Contents" ,"MacOS" ,os .path .splitext (x )[0 ])
407+ if os .path .exists (binpath ):
408+ kdict ["ExecutablePath" ] = "Contents/MacOS/" + os .path .splitext (x )[0 ]
409+ # Get the Info.plist
410+ if os .path .exists (os .path .join (kdir ,"Contents" ,"Info.plist" )):
411+ kdict ["PlistPath" ] = "Contents/Info.plist"
412+ elif os .path .exists (os .path .join (kdir ,"Info.plist" )):
413+ kdict ["PlistPath" ] = "Info.plist"
414+ if not kdict .get ("PlistPath" ) and not kdict .get ("ExecutablePath" ):
415+ continue
416+ # Should have something here
417+ kexts .append (kdict )
418+ # Check if we have a PlugIns folder
419+ pdir = kdir + "/Contents/PlugIns"
420+ if os .path .exists (pdir ) and os .path .isdir (pdir ):
421+ kexts .extend (self .walk_kexts (pdir ,parent + "/Contents/PlugIns/" + x ))
422+ return kexts
423+
424+ def oc_snapshot (self , event = None ):
425+ oc_folder = fd .askdirectory (title = "Select OC Folder:" )
426+ if not len (oc_folder ):
427+ return
428+
429+ # Verify folder structure - should be as follows:
430+ # OC
431+ # +- ACPI
432+ # | +- Custom
433+ # | +- SSDT.aml
434+ # +- Drivers
435+ # | +- EfiDriver.efi
436+ # +- Kexts
437+ # | +- Something.kext
438+ # +- config.plist
439+
440+ oc_acpi = os .path .join (oc_folder ,"ACPI" ,"Custom" )
441+ oc_drivers = os .path .join (oc_folder ,"Drivers" )
442+ oc_kexts = os .path .join (oc_folder ,"Kexts" )
443+
444+ for x in [oc_acpi ,oc_drivers ,oc_kexts ]:
445+ if not os .path .exists (x ):
446+ self .bell ()
447+ mb .showerror ("Incorrect OC Folder Struction" , "{} does not exist." .format (x ), parent = self )
448+ return
449+ if not os .path .isdir (x ):
450+ self .bell ()
451+ mb .showerror ("Incorrect OC Folder Struction" , "{} exists, but is not a directory." .format (x ), parent = self )
452+ return
453+
454+ # Folders are valid - lets work through each section
455+
456+ # ACPI is first, we'll iterate the .aml files we have and add what is missing
457+ # while also removing what exists in the plist and not in the folder.
458+ # If something exists in the table already, we won't touch it. This leaves the
459+ # enabled and comment properties untouched.
460+ #
461+ # Let's make sure we have the ACPI -> Add sections in our config
462+
463+ tree_dict = self .nodes_to_values ()
464+ # We're going to replace the whole list
465+ if not "ACPI" in tree_dict or not isinstance (tree_dict ["ACPI" ],dict ):
466+ tree_dict ["ACPI" ] = {"Add" :[]}
467+ if not "Add" in tree_dict ["ACPI" ] or not isinstance (tree_dict ["ACPI" ]["Add" ],list ):
468+ tree_dict ["ACPI" ]["Add" ] = []
469+ # Now we walk the existing add values
470+ new_acpi = [x for x in os .listdir (oc_acpi ) if x .lower ().endswith (".aml" ) and not x .startswith ("." )]
471+ add = tree_dict ["ACPI" ]["Add" ]
472+ for aml in new_acpi :
473+ if aml .lower () in [x .get ("Path" ,"" ).lower () for x in add ]:
474+ # Found it - skip
475+ continue
476+ # Doesn't exist, add it
477+ add .append ({
478+ "Enabled" :True ,
479+ "Comment" :aml ,
480+ "Path" :aml
481+ })
482+ new_add = []
483+ for aml in add :
484+ if not aml .get ("Path" ,"" ).lower () in [x .lower () for x in new_acpi ]:
485+ # Not there, skip
486+ continue
487+ new_add .append (aml )
488+ tree_dict ["ACPI" ]["Add" ] = new_add
489+
490+ # Next we need to walk the .efi drivers - in basically the same exact manner
491+ if not "UEFI" in tree_dict or not isinstance (tree_dict ["UEFI" ],dict ):
492+ tree_dict ["UEFI" ] = {"Drivers" :[]}
493+ if not "Drivers" in tree_dict ["UEFI" ] or not isinstance (tree_dict ["UEFI" ]["Drivers" ],list ):
494+ tree_dict ["UEFI" ]["Drivers" ] = []
495+ # Now we walk the existing values
496+ new_efi = [x for x in os .listdir (oc_drivers ) if x .lower ().endswith (".efi" ) and not x .startswith ("." )]
497+ add = tree_dict ["UEFI" ]["Drivers" ]
498+ for efi in new_efi :
499+ if efi .lower () in [x .lower () for x in add ]:
500+ # Found it - skip
501+ continue
502+ # Doesn't exist, add it
503+ add .append (efi )
504+ new_add = []
505+ for efi in add :
506+ if not efi .lower () in [x .lower () for x in new_efi ]:
507+ # Not there, skip
508+ continue
509+ new_add .append (efi )
510+ tree_dict ["UEFI" ]["Drivers" ] = new_add
511+
512+ # Now we need to walk the kexts
513+ if not "Kernel" in tree_dict or not isinstance (tree_dict ["Kernel" ],dict ):
514+ tree_dict ["Kernel" ] = {"Add" :[]}
515+ if not "Add" in tree_dict ["Kernel" ] or not isinstance (tree_dict ["Kernel" ]["Add" ],list ):
516+ tree_dict ["Kernel" ]["Add" ] = []
517+ kext_list = self .walk_kexts (oc_kexts )
518+ kexts = tree_dict ["Kernel" ]["Add" ]
519+ for kext in kext_list :
520+ if kext ["BundlePath" ].lower () in [x .get ("BundlePath" ,"" ).lower () for x in kexts ]:
521+ # Already have it, skip
522+ continue
523+ # We need it, it seems
524+ kexts .append (kext )
525+ new_kexts = []
526+ for kext in kexts :
527+ if not kext .get ("BundlePath" ,"" ).lower () in [x ["BundlePath" ].lower () for x in kext_list ]:
528+ # Not there, skip it
529+ continue
530+ new_kexts .append (kext )
531+ tree_dict ["Kernel" ]["Add" ] = new_kexts
532+
533+ # Now we remove the original tree - then replace it
534+ undo_list = []
535+ for x in self ._tree .get_children ():
536+ undo_list .append ({
537+ "type" :"remove" ,
538+ "cell" :x ,
539+ "from" :self ._tree .parent (x ),
540+ "index" :self ._tree .index (x )
541+ })
542+ self ._tree .detach (x )
543+ # Finally, we add the nodes back
544+ self .add_node (tree_dict )
545+ # Add all the root items to our undo list
546+ for child in self ._tree .get_children ():
547+ undo_list .append ({
548+ "type" :"add" ,
549+ "cell" :child
550+ })
551+ self .add_undo (undo_list )
552+ # Ensure we're edited
553+ if not self .edited :
554+ self .edited = True
555+ self .title (self .title ()+ " - Edited" )
556+ self .update_all_children ()
557+ self .alternate_colors ()
558+
387559 def get_check_type (self , cell = None , string = None ):
388560 if not cell == None :
389561 t = self .get_padded_values (cell ,1 )[0 ]
@@ -1038,9 +1210,11 @@ def update_array_counts(self, target):
10381210 # Only updating the "text" field
10391211 self ._tree .item (child ,text = x )
10401212
1041- def change_type (self , value ):
1213+ def change_type (self , value , cell = None ):
10421214 # Need to walk the values and pad
1043- values = self .get_padded_values (self ._tree .focus (), 3 )
1215+ if cell == None :
1216+ cell = self ._tree .focus ()
1217+ values = self .get_padded_values (cell , 3 )
10441218 # Verify we actually changed type
10451219 if values [0 ] == value :
10461220 # No change, bail
@@ -1050,15 +1224,14 @@ def change_type(self, value):
10501224 values [0 ] = value
10511225 # Remove children if needed
10521226 changes = []
1053- for i in self ._tree .get_children (self . _tree . focus () ):
1227+ for i in self ._tree .get_children (cell ):
10541228 changes .append ({
10551229 "type" :"remove" ,
10561230 "cell" :i ,
10571231 "from" :self ._tree .parent (i ),
10581232 "index" :self ._tree .index (i )
10591233 })
10601234 self ._tree .detach (i )
1061- cell = self ._tree .focus ()
10621235 changes .append ({
10631236 "type" :"edit" ,
10641237 "cell" :cell ,
@@ -1074,10 +1247,10 @@ def change_type(self, value):
10741247 elif value .lower () == "boolean" :
10751248 values [1 ] = "True"
10761249 elif value .lower () == "array" :
1077- self ._tree .item (self . _tree . focus () ,open = True )
1250+ self ._tree .item (cell ,open = True )
10781251 values [1 ] = "0 children"
10791252 elif value .lower () == "dictionary" :
1080- self ._tree .item (self . _tree . focus () ,open = True )
1253+ self ._tree .item (cell ,open = True )
10811254 values [1 ] = "0 key/value pairs"
10821255 elif value .lower () == "date" :
10831256 values [1 ] = datetime .datetime .now ().strftime ("%b %d, %Y %I:%M:%S %p" )
@@ -1086,7 +1259,7 @@ def change_type(self, value):
10861259 else :
10871260 values [1 ] = ""
10881261 # Set the values
1089- self ._tree .item (self . _tree . focus () , values = values )
1262+ self ._tree .item (cell , values = values )
10901263 if not self .edited :
10911264 self .edited = True
10921265 self .title (self .title ()+ " - Edited" )
@@ -1173,6 +1346,7 @@ def merge_menu_preset(self, val = None):
11731346 # and ensuring the type
11741347 created = None
11751348 current_cell = ""
1349+ undo_list = []
11761350 for p ,t in izip (paths ,types ):
11771351 found = False
11781352 needed_type = {"d" :"Dictionary" ,"a" :"Array" }.get (t .lower (),"Dictionary" )
@@ -1191,29 +1365,36 @@ def merge_menu_preset(self, val = None):
11911365 if not found :
11921366 # Need to add it
11931367 current_cell = self ._tree .insert (current_cell ,"end" ,text = p ,values = (self .menu_code + " " + needed_type ,"" ,self .drag_code ,),open = True )
1194- if created == None :
1195- # Only get the top level item created
1196- created = current_cell
1197-
1368+ undo_list . append ({
1369+ "type" : "add" ,
1370+ "cell" : current_cell
1371+ })
11981372 # At this point - we should be able to add the final piece
11991373 # let's first make sure it doesn't already exist - if it does, we
12001374 # will overwrite it
1201- ''' current_type = self.get_check_type(current_cell).lower()
1375+ current_type = self .get_check_type (current_cell ).lower ()
12021376 if current_type == "dictionary" :
12031377 # Scan through and make sure we have all the keys needed
12041378 for x in self ._tree .get_children (current_cell ):
12051379 name = self ._tree .item (x ,"text" )
12061380 if name in value :
12071381 # Need to change this one
1208- if len(self._tree.get_children(x)):
1209- # Add some remove commands'''
1382+ for child in self ._tree .get_children (x ):
1383+ # Add some remove commands
1384+ undo_list .append ({
1385+ "type" :"remove" ,
1386+ "cell" :child ,
1387+ "from" :x ,
1388+ "index" :self ._tree .index (child )
1389+ })
12101390 last_cell = self .add_node (value ,current_cell ,"" )
12111391 if created == None :
12121392 created = last_cell
1213- self . add_undo ({
1393+ undo_list . append ({
12141394 "type" :"add" ,
12151395 "cell" :created
12161396 })
1397+ self .add_undo (undo_list )
12171398 if not self .edited :
12181399 self .edited = True
12191400 self .title (self .title ()+ " - Edited" )
@@ -1432,4 +1613,4 @@ def alternate_colors(self, event = None):
14321613 tags .append ("odd" if x % 2 else "even" )
14331614 self ._tree .item (item , tags = tags )
14341615 self ._tree .tag_configure ('odd' , background = '#E8E8E8' )
1435- self ._tree .tag_configure ('even' , background = '#DFDFDF' )
1616+ self ._tree .tag_configure ('even' , background = '#DFDFDF' )
0 commit comments