Skip to content

Commit ac4e504

Browse files
Integrate KNEAUX EDU Patch Suite into Awesome Code Editor
- Added main.py with AwesomeCodeEditor class providing basic text editing. - Created patch_suite_integration.py with KNEAUXPatchSuiteIntegration class. - Integrated the patch suite into the editor via a 'Tools' menu. - The patch suite can handle ZIP/CRX archives, folders, and single text files. - Patching logic uses regular expressions to modify code. - UI for the patch suite is presented as a Toplevel dialog within the editor.
1 parent 0e85f2e commit ac4e504

File tree

2 files changed

+264
-0
lines changed

2 files changed

+264
-0
lines changed

main.py

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import tkinter as tk
2+
from tkinter import scrolledtext, Menu, filedialog, messagebox
3+
from patch_suite_integration import KNEAUXPatchSuiteIntegration # Import the integration class
4+
5+
class AwesomeCodeEditor:
6+
def __init__(self, root):
7+
self.root = root
8+
self.root.title("Awesome Code Editor")
9+
self.root.geometry("800x600")
10+
11+
self.text_area = scrolledtext.ScrolledText(self.root, wrap=tk.WORD, undo=True)
12+
self.text_area.pack(fill=tk.BOTH, expand=True)
13+
self.current_file_path = None # To keep track of the currently open file
14+
15+
self.menu_bar = Menu(self.root)
16+
self.root.config(menu=self.menu_bar)
17+
18+
self.file_menu = Menu(self.menu_bar, tearoff=0)
19+
self.menu_bar.add_cascade(label="File", menu=self.file_menu)
20+
self.file_menu.add_command(label="Open", command=self.open_file)
21+
self.file_menu.add_command(label="Save", command=self.save_file)
22+
self.file_menu.add_command(label="Save As...", command=self.save_file_as)
23+
self.file_menu.add_separator()
24+
self.file_menu.add_command(label="Exit", command=self.root.quit)
25+
26+
self.tools_menu = Menu(self.menu_bar, tearoff=0)
27+
self.menu_bar.add_cascade(label="Tools", menu=self.tools_menu)
28+
self.tools_menu.add_command(label="Patch with KNEAUX EDU", command=self.open_patch_suite_dialog)
29+
30+
# Initialize patch suite integration object - created on demand
31+
self.patch_suite_instance = None
32+
33+
34+
def open_file(self):
35+
filepath = filedialog.askopenfilename(
36+
filetypes=[("Text Files", "*.txt"), ("All Files", "*.*")]
37+
)
38+
if not filepath:
39+
return
40+
try:
41+
with open(filepath, "r", encoding="utf-8") as f:
42+
content = f.read()
43+
self.text_area.delete(1.0, tk.END)
44+
self.text_area.insert(tk.END, content)
45+
self.current_file_path = filepath # Store current file path
46+
self.root.title(f"Awesome Code Editor - {filepath}")
47+
except Exception as e:
48+
messagebox.showerror("Error", f"Failed to open file: {e}")
49+
self.current_file_path = None
50+
51+
def save_file_as(self):
52+
filepath = filedialog.asksaveasfilename(
53+
defaultextension=".txt",
54+
filetypes=[("Text Files", "*.txt"), ("All Files", "*.*")]
55+
)
56+
if not filepath:
57+
return False # Indicate cancellation
58+
try:
59+
with open(filepath, "w", encoding="utf-8") as f:
60+
content = self.text_area.get(1.0, tk.END)
61+
f.write(content)
62+
self.current_file_path = filepath # Update current file path
63+
self.root.title(f"Awesome Code Editor - {filepath}")
64+
return True # Indicate success
65+
except Exception as e:
66+
messagebox.showerror("Error", f"Failed to save file: {e}")
67+
return False # Indicate failure
68+
69+
def save_file(self):
70+
if self.current_file_path:
71+
try:
72+
with open(self.current_file_path, "w", encoding="utf-8") as f:
73+
content = self.text_area.get(1.0, tk.END)
74+
f.write(content)
75+
self.root.title(f"Awesome Code Editor - {self.current_file_path}")
76+
except Exception as e:
77+
messagebox.showerror("Error", f"Failed to save file: {e}")
78+
else:
79+
self.save_file_as() # If no current file, use Save As logic
80+
81+
def open_patch_suite_dialog(self):
82+
if self.patch_suite_instance is None or not self.patch_suite_instance.patch_window or not self.patch_suite_instance.patch_window.winfo_exists():
83+
self.patch_suite_instance = KNEAUXPatchSuiteIntegration(self.root)
84+
self.patch_suite_instance.show_patch_dialog()
85+
86+
# Optionally, if a file is open, you could pass its path or directory to the patch suite
87+
# For example:
88+
# current_dir = None
89+
# if self.current_file_path:
90+
# current_dir = Path(self.current_file_path).parent
91+
# self.patch_suite_instance.show_patch_dialog(initial_dir=current_dir)
92+
# This would require KNEAUXPatchSuiteIntegration to accept an initial_dir parameter.
93+
94+
def main():
95+
root = tk.Tk()
96+
editor = AwesomeCodeEditor(root)
97+
root.mainloop()
98+
99+
if __name__ == "__main__":
100+
main()

patch_suite_integration.py

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
import tkinter as tk
2+
from tkinter import filedialog, messagebox, Toplevel
3+
import zipfile
4+
import shutil # Added for removing temp directory
5+
import re
6+
from pathlib import Path
7+
8+
PREMIUM_PATTERNS = [
9+
(re.compile(r"\b(isPremium|licenseValid|proVersion|hasProAccess)\s*=\s*false\b"), r"\1 = True"),
10+
(re.compile(r"if\s*\(!?\s*(isPremium|licenseValid|proVersion|hasProAccess)\)"), r"if True"), # Simplified for editor context
11+
(re.compile(r"checkLicense\s*\([^)]*\)\s*{[^}]*}"), r"checkLicense() { return True }"), # JS/TS like
12+
]
13+
14+
TEXT_FILE_EXTENSIONS = {'.js', '.ts', '.json', '.html', '.py', '.txt', '.cfg', '.xml', '.md', '.java', '.cs', '.cpp', '.c', '.php'} # Expanded list
15+
16+
class KNEAUXPatchSuiteIntegration:
17+
def __init__(self, editor_root):
18+
self.editor_root = editor_root # Main editor window to anchor dialogs
19+
self.patch_window = None
20+
21+
def show_patch_dialog(self):
22+
if self.patch_window and self.patch_window.winfo_exists():
23+
self.patch_window.lift()
24+
return
25+
26+
self.patch_window = Toplevel(self.editor_root)
27+
self.patch_window.title("KNEAUX-COD3 EDU Patch Suite")
28+
self.patch_window.geometry("450x200")
29+
self.patch_window.transient(self.editor_root) # Make it a child of the main window
30+
31+
tk.Label(self.patch_window, text="🔧 Select an extension (ZIP/CRX) or folder to patch:").pack(pady=15)
32+
tk.Button(self.patch_window, text="📂 Select File or Folder", command=self.select_and_process).pack(pady=5)
33+
tk.Button(self.patch_window, text="✖️ Close", command=self.patch_window.destroy).pack(pady=10)
34+
35+
36+
def select_and_process(self):
37+
# Determine if it's a file or folder dialog needed
38+
# For simplicity, starting with askopenfilename and then checking type
39+
path_str = filedialog.askopenfilename(
40+
parent=self.patch_window,
41+
title="Select ZIP/CRX file or any file in a target folder",
42+
filetypes=[("Supported Archives", "*.zip *.crx"), ("All files", "*.*")]
43+
)
44+
45+
if not path_str:
46+
# User cancelled
47+
# Try asking for a directory if no file was selected, or provide a separate button for directory
48+
path_str = filedialog.askdirectory(
49+
parent=self.patch_window,
50+
title="Select Folder to Patch"
51+
)
52+
if not path_str:
53+
messagebox.showinfo("Info", "No file or folder selected.", parent=self.patch_window)
54+
return
55+
56+
file_path = Path(path_str)
57+
58+
if file_path.is_dir():
59+
self.scan_and_patch_folder(file_path)
60+
elif file_path.is_file():
61+
if file_path.suffix in ['.zip', '.crx']:
62+
temp_dir = file_path.parent / (file_path.stem + "_unzipped_temp")
63+
if temp_dir.exists():
64+
shutil.rmtree(temp_dir) # Clean up previous attempt
65+
temp_dir.mkdir(parents=True, exist_ok=True)
66+
67+
try:
68+
with zipfile.ZipFile(file_path, 'r') as zip_ref:
69+
zip_ref.extractall(temp_dir)
70+
71+
patched_files_count = self.scan_and_patch_folder(temp_dir, is_temp=True)
72+
73+
if patched_files_count > 0:
74+
self.rezip_folder(temp_dir, file_path.parent / (file_path.stem + "_patched" + file_path.suffix))
75+
elif patched_files_count == 0:
76+
messagebox.showinfo("ℹ️ No Changes", "No patchable code found in the archive.", parent=self.patch_window)
77+
# If patched_files_count is None, an error occurred during patching
78+
79+
except zipfile.BadZipFile:
80+
messagebox.showerror("Error", "Invalid or corrupted ZIP/CRX file.", parent=self.patch_window)
81+
except Exception as e:
82+
messagebox.showerror("Error", f"Failed to process archive: {e}", parent=self.patch_window)
83+
finally:
84+
if temp_dir.exists():
85+
shutil.rmtree(temp_dir) # Clean up
86+
elif file_path.suffix in TEXT_FILE_EXTENSIONS:
87+
# Allow patching a single, currently open, or selected text file
88+
if self.patch_single_file(file_path):
89+
messagebox.showinfo("✅ Patch Complete", f"File '{file_path.name}' patched.", parent=self.patch_window)
90+
else:
91+
messagebox.showinfo("ℹ️ No Changes", f"No patchable patterns found in '{file_path.name}'.", parent=self.patch_window)
92+
93+
elif file_path.suffix in ['.exe', '.bin']:
94+
messagebox.showinfo("Note", "Binary patching is not supported. Please select source files, folders, or archives (ZIP/CRX).", parent=self.patch_window)
95+
else:
96+
messagebox.showwarning("Unsupported File", f"File type '{file_path.suffix}' is not directly patchable. Select a folder, ZIP, or CRX.", parent=self.patch_window)
97+
else:
98+
messagebox.showerror("Error", "Selected path is not a valid file or folder.", parent=self.patch_window)
99+
100+
101+
def patch_code_content(self, content):
102+
original_content = content
103+
for pattern, repl in PREMIUM_PATTERNS:
104+
content = pattern.sub(repl, content)
105+
return content, content != original_content
106+
107+
def patch_single_file(self, file_path: Path):
108+
try:
109+
content = file_path.read_text(encoding='utf-8')
110+
patched_content, changed = self.patch_code_content(content)
111+
if changed:
112+
file_path.write_text(patched_content, encoding='utf-8')
113+
print(f"[+] Patched: {file_path}")
114+
return True
115+
except UnicodeDecodeError:
116+
print(f"[!] Skipping binary or non-UTF-8 file: {file_path.name}")
117+
except Exception as e:
118+
messagebox.showerror("File Patch Error", f"Error patching file {file_path.name}:\n{e}", parent=self.patch_window or self.editor_root)
119+
print(f"[!] Error processing {file_path.name}: {e}")
120+
return False
121+
122+
def scan_and_patch_folder(self, folder_path: Path, is_temp: bool = False):
123+
patched_files_count = 0
124+
error_occurred = False
125+
for path_object in folder_path.rglob('*'):
126+
if path_object.is_file() and path_object.suffix in TEXT_FILE_EXTENSIONS:
127+
if self.patch_single_file(path_object):
128+
patched_files_count += 1
129+
# Check if patch_single_file returned None due to an error, if so, set error_occurred flag
130+
# This check is a bit tricky as False means no patches applied, not necessarily an error.
131+
# We rely on messagebox shown in patch_single_file for error reporting.
132+
133+
if not is_temp: # Only show message for direct folder patching here. Archive patching has its own summary.
134+
if patched_files_count > 0:
135+
messagebox.showinfo("✅ Patch Complete", f"{patched_files_count} file(s) patched in folder '{folder_path.name}'.", parent=self.patch_window)
136+
else:
137+
messagebox.showinfo("ℹ️ No Changes", f"No patchable code found in folder '{folder_path.name}'.", parent=self.patch_window)
138+
139+
return patched_files_count # Return count for archive processing logic
140+
141+
142+
def rezip_folder(self, folder_to_zip: Path, output_zip_path: Path):
143+
try:
144+
with zipfile.ZipFile(output_zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
145+
for file in folder_to_zip.rglob('*'):
146+
if file.is_file():
147+
zipf.write(file, file.relative_to(folder_to_zip))
148+
messagebox.showinfo("📦 Repack Complete", f"Patched archive saved to:\n{output_zip_path}", parent=self.patch_window)
149+
except Exception as e:
150+
messagebox.showerror("Repack Error", f"Failed to repack archive: {e}", parent=self.patch_window)
151+
print(f"[!] Error repacking {folder_to_zip} to {output_zip_path}: {e}")
152+
153+
# Example of how it might be instantiated from the main editor (for testing purposes)
154+
if __name__ == '__main__':
155+
root = tk.Tk()
156+
root.title("Main Editor Window (Test)")
157+
root.geometry("600x400")
158+
159+
def open_patcher():
160+
patch_suite_instance = KNEAUXPatchSuiteIntegration(root)
161+
patch_suite_instance.show_patch_dialog()
162+
163+
tk.Button(root, text="Open KNEAUX Patcher", command=open_patcher).pack(pady=20)
164+
root.mainloop()

0 commit comments

Comments
 (0)