11import customtkinter
22from tkinter import filedialog
33from CTkMenuBar import *
4- import github
5- from github import *
6-
4+ import os
5+ import subprocess
6+ import logging
7+
8+ logging .basicConfig (level = logging .DEBUG , format = '%(asctime)s - %(levelname)s - %(message)s' )
9+
10+ def run_executable (source , patch , output ):
11+ try :
12+ result = subprocess .run (
13+ ["xdelta3.exe" , "-d" , "-s" , source , patch , output ],
14+ capture_output = True ,
15+ text = True
16+ )
17+ logging .info (result .stdout )
18+ logging .error (result .stderr )
19+ return result .returncode == 0
20+ except Exception as e :
21+ logging .error (f"Error running xdelta3: { e } " )
22+ return False
723
824
925class ToplevelWindow (customtkinter .CTkToplevel ):
1026 def __init__ (self , * args , ** kwargs ):
1127 super ().__init__ (* args , ** kwargs )
1228 self .geometry ("255x250" )
13- self .label = customtkinter .CTkLabel (self , text = "About Xdelta Py UI" )
14- self .label .pack (padx = 20 , pady = 20 )
15- self .label2 = customtkinter .CTkLabel (self , text = "This is a simple UI for Xdelta patching." )
16- self .label2 .pack (padx = 20 , pady = 20 )
17- self .label3 = customtkinter .CTkLabel (self , text = "version 1.0" , font = ("Arial" , 10 , "italic" ))
18- self .label3 .pack (padx = 20 , pady = 20 )
29+ self .title ("About" )
30+ customtkinter .CTkLabel (self , text = "About Xdelta Py UI" ).pack (padx = 20 , pady = 10 )
31+ customtkinter .CTkLabel (self , text = "This is a simple UI for Xdelta patching." ).pack (padx = 20 , pady = 10 )
32+ customtkinter .CTkLabel (self , text = "Version 1.0" , font = ("Arial" , 10 , "italic" )).pack (padx = 20 , pady = 10 )
33+
1934
2035class App (customtkinter .CTk ):
2136 def __init__ (self , * args , ** kwargs ):
@@ -25,38 +40,79 @@ def __init__(self, *args, **kwargs):
2540 self .title ("Xdelta Py UI" )
2641 self ._set_appearance_mode ("System" )
2742
28-
43+ self .sourcefile = None
44+ self .patchfile = None
45+
2946 menu = CTkTitleMenu (master = self )
30- button_1 = menu .add_cascade ("File" )
31- button_2 = menu .add_cascade ("About" )
47+ file_button = menu .add_cascade ("File" )
48+ about_button = menu .add_cascade ("About" )
3249
33-
34-
50+ file_menu = CustomDropdownMenu (widget = file_button )
51+ file_menu .add_option ("Open Source File" , command = self .open_source_file )
52+ file_menu .add_option ("Open Patch File" , command = self .open_patch_file )
53+ file_menu .add_separator ()
54+ file_menu .add_option ("Exit" , command = self .quit )
3555
56+ about_menu = CustomDropdownMenu (widget = about_button )
57+ about_menu .add_option ("Open About Window" , command = self .open_toplevel )
3658
37- dropdown1 = CustomDropdownMenu (widget = button_1 )
38- dropdown1 .add_option (option = "Open Source File" , command = lambda : filedialog .askopenfilename (filetypes = [("GM Data Files" , "*data.win" )]))
39- dropdown1 .add_option (option = "Open Patch File" , command = lambda : filedialog .askopenfilename (filetypes = [("Xdelta Patch Files" , "*.xdelta" )]))
40- dropdown1 .add_separator ()
59+ self .toplevel_window = None
4160
42- dropdown1 .add_option (option = "Exit" , command = self .quit )
61+ self .file_entry = customtkinter .CTkEntry (self , placeholder_text = "No source file selected" )
62+ self .file_entry .pack (pady = 10 , padx = 20 , fill = "x" )
63+ self .file_entry .configure (state = "disabled" )
4364
65+ self .source_button = customtkinter .CTkButton (self , text = "Change Source File" , command = self .open_source_file )
66+ self .source_button .pack (pady = 10 , padx = 20 , fill = "x" )
4467
45- dropdown2 = CustomDropdownMenu (widget = button_2 )
46- dropdown2 .add_option ("Open About Window" , command = self .open_toplevel )
68+ self .patch_entry = customtkinter .CTkEntry (self , placeholder_text = "No patch file selected" )
69+ self .patch_entry .pack (pady = 10 , padx = 20 , fill = "x" )
70+ self .patch_entry .configure (state = "disabled" )
4771
48- self .toplevel_window = None
72+ self .patch_button = customtkinter .CTkButton (self , text = "Change Patch File" , command = self .open_patch_file )
73+ self .patch_button .pack (pady = 10 , padx = 20 , fill = "x" )
74+
75+ self .output_entry = customtkinter .CTkEntry (self , placeholder_text = "Output file name (e.g., patched.win)" )
76+ self .output_entry .pack (pady = 10 , padx = 20 , fill = "x" )
77+
78+ self .run_button = customtkinter .CTkButton (self , text = "Apply Patch" , command = self .run_patch )
79+ self .run_button .pack (pady = 20 , padx = 20 , fill = "x" )
80+
81+ def open_source_file (self ):
82+ self .sourcefile = filedialog .askopenfilename (filetypes = [("GM Data Files" , "*data.win" )])
83+ if self .sourcefile :
84+ self .file_entry .configure (state = "normal" )
85+ self .file_entry .delete (0 , 'end' )
86+ self .file_entry .insert (0 , self .sourcefile )
87+ self .file_entry .configure (state = "disabled" )
88+
89+ def open_patch_file (self ):
90+ self .patchfile = filedialog .askopenfilename (filetypes = [("Xdelta Patch Files" , "*.xdelta" )])
91+ if self .patchfile :
92+ self .patch_entry .configure (state = "normal" )
93+ self .patch_entry .delete (0 , 'end' )
94+ self .patch_entry .insert (0 , self .patchfile )
95+ self .patch_entry .configure (state = "disabled" )
4996
5097 def open_toplevel (self ):
5198 if self .toplevel_window is None or not self .toplevel_window .winfo_exists ():
5299 self .toplevel_window = ToplevelWindow (self )
53100 else :
54101 self .toplevel_window .focus ()
55102
103+ def run_patch (self ):
104+ output_path = self .output_entry .get ()
105+ if not self .sourcefile or not self .patchfile or not output_path :
106+ logging .error ("Missing source, patch, or output path." )
107+ return
56108
109+ success = run_executable (self .sourcefile , self .patchfile , output_path )
110+ if success :
111+ logging .info ("Patch applied successfully." )
112+ else :
113+ logging .error ("Failed to apply patch." )
57114
58115
59-
60-
61- app = App ()
62- app .mainloop ()
116+ if __name__ == "__main__" :
117+ app = App ()
118+ app .mainloop ()
0 commit comments