1+ import ctypes
2+
3+ try :
4+ import customtkinter
5+ import threading
6+ from tkinter .filedialog import askopenfilename
7+ import os
8+ import zipfile
9+ import shutil
10+ import patcher
11+ except ImportError :
12+ ctypes .windll .user32 .MessageBoxW (0 , "You need to install the required packages to run this program." , "Error" , 1 )
13+ exit (1 )
14+
15+ try :
16+ customtkinter .set_appearance_mode ("System" ) # Modes: system (default), light, dark
17+ customtkinter .set_default_color_theme ("green" ) # Themes: blue (default), dark-blue, green
18+ except Exception as e :
19+ ctypes .windll .user32 .MessageBoxW (0 , f"An error occurred while setting the appearance mode: { e } " , "Error" , 1 )
20+ exit (1 )
21+
22+ try :
23+ app = customtkinter .CTk () # create CTk window like you do with the Tk window
24+ app .geometry ("400x650" )
25+ app .title ("Project Earth Patcher" )
26+ except Exception as e :
27+ ctypes .windll .user32 .MessageBoxW (0 , f"An error occurred while creating the window: { e } " , "Error" , 1 )
28+ exit (1 )
29+
30+ def cleanup ():
31+ try :
32+ if os .path .exists ('data/ipa.zip' ):
33+ os .remove ('data/ipa.zip' )
34+ if os .path .exists ('data/ipa.ipa' ):
35+ os .remove ('data/ipa.ipa' )
36+ if shutil .os .path .exists ('data/ipa' ):
37+ shutil .rmtree ('data/ipa' )
38+ if shutil .os .path .exists ('data' ):
39+ shutil .rmtree ('data' )
40+ except Exception as e :
41+ ctypes .windll .user32 .MessageBoxW (0 , f"An error occurred while cleaning up: { e } " , "Error" , 1 )
42+ exit (1 )
43+
44+ try :
45+ os .makedirs ("data" , exist_ok = True )
46+ except Exception as e :
47+ cleanup ()
48+ ctypes .windll .user32 .MessageBoxW (0 , f"An error occurred while creating the data directory: { e } " , "Error" , 1 )
49+ exit (1 )
50+
51+
52+ def log (level , text , prefix ):
53+ print (f"[{ prefix } ] { level } : { text } " )
54+ textbox .configure (state = "normal" )
55+ textbox .insert ("end" , f"[{ prefix } ] { level } : { text } \n " )
56+ textbox .configure (state = "disabled" )
57+
58+
59+ def patch_sync ():
60+ button .configure (state = "disabled" )
61+ ip = ip_entry .get ()
62+ ip_entry .configure (state = "disabled" )
63+ if len (ip ) > 27 :
64+ log ("ERROR" , "IP too long!" , "PATCHER" )
65+ cleanup ()
66+ ctypes .windll .user32 .MessageBoxW (0 , f"Please enter a valid IP!" , "Error" , 1 )
67+ return
68+ log ("INFO" , f"Starting to patch with ip: { ip } " , "PATCHER" )
69+ filename = askopenfilename (title = "Select the .ipa file" , filetypes = [("IPA files" , "*.ipa" )])
70+ if filename == '' :
71+ log ("ERROR" , "No file selected" , "PATCHER" )
72+ cleanup ()
73+ ctypes .windll .user32 .MessageBoxW (0 , f"Please select a file!" , "Error" , 1 )
74+ return
75+ shutil .copyfile (filename , "./data/ipa.zip" )
76+ # extract the zip
77+ log ("INFO" , "Extracting the ipa" , "PATCHER" )
78+ with zipfile .ZipFile ("./data/ipa.zip" , 'r' ) as zip_ref :
79+ zip_ref .extractall ("./data/ipa" )
80+ # check payload
81+ if not patcher .hex_bytes_in_file ("68747470733A2F2F6C6F6361746F722E6D6365736572762E6E6574" , "./data/ipa/Payload/minecraftearthtf.app/minecraftearthtf" ):
82+ log ("INFO" , "This file is encrypted!" , "PATCHER" )
83+ ctypes .windll .user32 .MessageBoxW (0 , f"This file is encrypted!" , "Error" , 1 )
84+ cleanup ()
85+ return
86+ else :
87+ log ("INFO" , "This file is not encrypted and ready to patch!" , "PATCHER" )
88+ # patch the file
89+ log ("INFO" , "Patching the file" , "PATCHER" )
90+ log ("INFO" , "Patching App Name" , "PATCHER" )
91+ patcher .patch_app_name ()
92+ log ("INFO" , "Patched App Name" , "PATCHER" )
93+ log ("INFO" , "Removing DRM" , "PATCHER" )
94+ patcher .remove_drm ()
95+ log ("INFO" , "Removed DRM" , "PATCHER" )
96+ log ("INFO" , "Removing Useless Files" , "PATCHER" )
97+ patcher .remove_useless_files ()
98+ log ("INFO" , "Removed Useless Files" , "PATCHER" )
99+ log ("INFO" , "Patching IP" , "PATCHER" )
100+ patcher .patch_ip (ip )
101+ log ("INFO" , "Patched IP" , "PATCHER" )
102+ log ("INFO" , "Patching Sunset Time" , "PATCHER" )
103+ patcher .patch_sunset_time ()
104+ log ("INFO" , "Patched Sunset Time" , "PATCHER" )
105+ # zip the file
106+ log ("INFO" , "Zipping the file" , "PATCHER" )
107+ patcher .zip_folder_contents ("./data/ipa/" , "./ipa.ipa" )
108+ log ("INFO" , "Zipped the file" , "PATCHER" )
109+ log ("INFO" , "Patching done!" , "PATCHER" )
110+ cleanup ()
111+
112+ def patch ():
113+ # Patch method in a new thread
114+ threading .Thread (target = patch_sync ).start ()
115+
116+ patcher_label = customtkinter .CTkLabel (master = app , text = "Project Earth Patcher" , font = ("Arial" , 20 ))
117+ patcher_label .place (relx = 0.5 , rely = 0.3 , anchor = customtkinter .CENTER )
118+
119+ # IP Textbox
120+ ip_label = customtkinter .CTkLabel (master = app , text = "Server IP:" )
121+ ip_label .place (relx = 0.2 , rely = 0.4 , anchor = customtkinter .CENTER )
122+
123+ ip_entry = customtkinter .CTkEntry (master = app )
124+ ip_entry .place (relx = 0.5 , rely = 0.4 , anchor = customtkinter .CENTER )
125+
126+ button = customtkinter .CTkButton (master = app , text = "Patch!" , command = patch )
127+ button .place (relx = 0.5 , rely = 0.5 , anchor = customtkinter .CENTER )
128+
129+ textbox = customtkinter .CTkTextbox (app , state = "disabled" )
130+ textbox .place (relx = 0.5 , rely = 0.7 , anchor = customtkinter .CENTER )
131+
132+
133+ app .mainloop ()
0 commit comments