init
This commit is contained in:
@@ -0,0 +1,152 @@
|
|||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import json
|
||||||
|
import customtkinter as ctk
|
||||||
|
from tkinter import filedialog, messagebox
|
||||||
|
|
||||||
|
ctk.set_appearance_mode("System")
|
||||||
|
ctk.set_default_color_theme("blue")
|
||||||
|
# Initialize global variables
|
||||||
|
game_folder = None
|
||||||
|
|
||||||
|
# Create the customtkinter window
|
||||||
|
app = ctk.CTk()
|
||||||
|
app.title("Firefly HDiff Tool")
|
||||||
|
app.geometry("335x470")
|
||||||
|
app.resizable(False, False)
|
||||||
|
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
app.iconbitmap(os.path.join(current_dir, 'image.ico'))
|
||||||
|
app.grid_columnconfigure((0), weight=1)
|
||||||
|
|
||||||
|
# Set appearance mode and theme
|
||||||
|
|
||||||
|
|
||||||
|
# Main frame for better organization
|
||||||
|
main_frame = ctk.CTkFrame(app)
|
||||||
|
main_frame.pack(fill=ctk.BOTH, expand=True)
|
||||||
|
|
||||||
|
# Folder selection button with modern style
|
||||||
|
folder_button = ctk.CTkButton(main_frame, text="Select Game Folder", command=lambda: select_folder(), width=300, anchor='center')
|
||||||
|
folder_button.grid(row=0, column=0, padx=20, pady=20, sticky="ew", columnspan=10)
|
||||||
|
|
||||||
|
# Folder label to display selected folder
|
||||||
|
folder_label = ctk.CTkLabel(main_frame, text="No folder selected", width=200, anchor="center", text_color="gray")
|
||||||
|
folder_label.grid(row=1, column=0, padx=20, pady=20, sticky="ew", columnspan=2)
|
||||||
|
|
||||||
|
# Apply Patch button with modern style
|
||||||
|
apply_button = ctk.CTkButton(main_frame, text="Apply Patch", command=lambda: apply_patch_action(), state=ctk.DISABLED, width=300, anchor='center')
|
||||||
|
apply_button.grid(row=2, column=0, padx=10, pady=10)
|
||||||
|
|
||||||
|
# Output text area with scroll functionality
|
||||||
|
output_frame = ctk.CTkFrame(main_frame, width=300)
|
||||||
|
output_frame.grid(row=3, column=0, padx=10, pady=5)
|
||||||
|
|
||||||
|
output_text = ctk.CTkTextbox(output_frame, height=8, width=300)
|
||||||
|
output_text.grid(row=0, column=0, sticky="nsew")
|
||||||
|
|
||||||
|
# Scrollbar for the text area
|
||||||
|
scrollbar = ctk.CTkScrollbar(output_frame, command=output_text.yview)
|
||||||
|
scrollbar.grid(row=0, column=1, sticky="ns")
|
||||||
|
|
||||||
|
output_text.configure(yscrollcommand=scrollbar.set, state=ctk.DISABLED)
|
||||||
|
|
||||||
|
# Progress bar
|
||||||
|
progress_label = ctk.CTkLabel(main_frame, text="Progress", width=300, anchor='center')
|
||||||
|
progress_label.grid(row=4, column=0, padx=10)
|
||||||
|
|
||||||
|
progress_bar = ctk.CTkProgressBar(main_frame, orientation="horizontal")
|
||||||
|
progress_bar.grid(row=5, column=0, pady=10)
|
||||||
|
progress_bar.set(0)
|
||||||
|
|
||||||
|
def select_folder():
|
||||||
|
"""Select a folder to set as the game folder."""
|
||||||
|
global game_folder
|
||||||
|
folder_path = filedialog.askdirectory()
|
||||||
|
if folder_path:
|
||||||
|
game_folder = folder_path
|
||||||
|
folder_label.configure(text=f"Selected Folder: {folder_path}")
|
||||||
|
apply_button.configure(state=ctk.NORMAL)
|
||||||
|
else:
|
||||||
|
messagebox.showwarning("Warning", "No folder selected.")
|
||||||
|
apply_button.configure(state=ctk.DISABLED)
|
||||||
|
|
||||||
|
def apply_patch_action():
|
||||||
|
"""Apply patch to the selected game folder."""
|
||||||
|
if not game_folder:
|
||||||
|
messagebox.showwarning("Warning", "Please select a game folder!")
|
||||||
|
return
|
||||||
|
apply_patch(game_folder)
|
||||||
|
|
||||||
|
def apply_patch(game_folder):
|
||||||
|
"""Main function to apply patch based on selected folder."""
|
||||||
|
folder_button.configure(state=ctk.DISABLED)
|
||||||
|
delete_files_path = os.path.join(game_folder, 'deletefiles.txt')
|
||||||
|
if not os.path.exists(delete_files_path):
|
||||||
|
folder_button.configure(state=ctk.NORMAL)
|
||||||
|
log_output('deletefiles.txt does not exist in the game directory!')
|
||||||
|
return
|
||||||
|
|
||||||
|
hdiff_map_path = os.path.join(game_folder, 'hdiffmap.json')
|
||||||
|
if not os.path.exists(hdiff_map_path):
|
||||||
|
folder_button.configure(state=ctk.NORMAL)
|
||||||
|
log_output('hdiffmap.json does not exist in the game directory!')
|
||||||
|
return
|
||||||
|
|
||||||
|
# Read deletefiles.txt and delete the specified files
|
||||||
|
with open(delete_files_path, 'r') as delete_files:
|
||||||
|
for file in delete_files.read().split('\n'):
|
||||||
|
if len(file) < 1:
|
||||||
|
continue
|
||||||
|
file_path = os.path.join(game_folder, file)
|
||||||
|
if os.path.exists(file_path):
|
||||||
|
log_output(f'Deleting {file_path}')
|
||||||
|
os.remove(file_path)
|
||||||
|
|
||||||
|
# Check if hpatchz is available in the system path
|
||||||
|
hzpatchz = os.path.join(current_dir, 'hpatchz.exe')
|
||||||
|
if not os.path.exists(hzpatchz):
|
||||||
|
folder_button.configure(state=ctk.NORMAL)
|
||||||
|
log_output(f"HDiffPatch not found in the current directory ({current_dir})!\nPlease make sure hpatchz.exe is in the same folder as the script.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Read hdiffmap.json
|
||||||
|
with open(hdiff_map_path, 'r') as hdiff_json:
|
||||||
|
data = json.load(hdiff_json)
|
||||||
|
|
||||||
|
total_patches = len(data['diff_map'])
|
||||||
|
progress_bar.set(0)
|
||||||
|
|
||||||
|
# Apply patches
|
||||||
|
for i, entry in enumerate(data['diff_map']):
|
||||||
|
source_file_name = os.path.join(game_folder, entry['source_file_name'])
|
||||||
|
patch_file_name = os.path.join(game_folder, entry['patch_file_name'])
|
||||||
|
target_file_name = os.path.join(game_folder, entry['target_file_name'])
|
||||||
|
|
||||||
|
# Check if files exist
|
||||||
|
if not os.path.exists(source_file_name):
|
||||||
|
log_output(f"Source file missing: {source_file_name}")
|
||||||
|
continue
|
||||||
|
if not os.path.exists(patch_file_name):
|
||||||
|
log_output(f"Patch file missing: {patch_file_name}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
log_output(f"Applying patch: {patch_file_name} -> {target_file_name} using {source_file_name}")
|
||||||
|
subprocess.run([hzpatchz, source_file_name, patch_file_name, target_file_name])
|
||||||
|
|
||||||
|
# Update progress bar
|
||||||
|
progress_percentage = float((i + 1) / total_patches)
|
||||||
|
progress_bar.set(progress_percentage)
|
||||||
|
app.update_idletasks()
|
||||||
|
|
||||||
|
log_output("Patch application complete.")
|
||||||
|
|
||||||
|
folder_button.configure(state=ctk.NORMAL)
|
||||||
|
|
||||||
|
def log_output(message):
|
||||||
|
"""Log the output in the Text widget."""
|
||||||
|
output_text.configure(state=ctk.NORMAL)
|
||||||
|
output_text.insert(ctk.END, message + "\n\n")
|
||||||
|
output_text.yview(ctk.END)
|
||||||
|
output_text.configure(state=ctk.DISABLED)
|
||||||
|
|
||||||
|
app.mainloop()
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import json
|
||||||
|
import customtkinter as ctk
|
||||||
|
from tkinter import filedialog, messagebox
|
||||||
|
|
||||||
|
ctk.set_appearance_mode("System")
|
||||||
|
ctk.set_default_color_theme("blue")
|
||||||
|
# Initialize global variables
|
||||||
|
game_folder = None
|
||||||
|
|
||||||
|
# Create the customtkinter window
|
||||||
|
app = ctk.CTk()
|
||||||
|
app.title("Firefly HDiff Tool")
|
||||||
|
app.geometry("335x470")
|
||||||
|
app.resizable(False, False)
|
||||||
|
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
app.iconbitmap(os.path.join(current_dir, 'image.ico'))
|
||||||
|
app.grid_columnconfigure((0), weight=1)
|
||||||
|
|
||||||
|
# Set appearance mode and theme
|
||||||
|
|
||||||
|
|
||||||
|
# Main frame for better organization
|
||||||
|
main_frame = ctk.CTkFrame(app)
|
||||||
|
main_frame.pack(fill=ctk.BOTH, expand=True)
|
||||||
|
|
||||||
|
# Folder selection button with modern style
|
||||||
|
folder_button = ctk.CTkButton(main_frame, text="Select Game Folder", command=lambda: select_folder(), width=300, anchor='center')
|
||||||
|
folder_button.grid(row=0, column=0, padx=20, pady=20, sticky="ew", columnspan=10)
|
||||||
|
|
||||||
|
# Folder label to display selected folder
|
||||||
|
folder_label = ctk.CTkLabel(main_frame, text="No folder selected", width=200, anchor="center", text_color="gray")
|
||||||
|
folder_label.grid(row=1, column=0, padx=20, pady=20, sticky="ew", columnspan=2)
|
||||||
|
|
||||||
|
# Apply Patch button with modern style
|
||||||
|
apply_button = ctk.CTkButton(main_frame, text="Apply Patch", command=lambda: apply_patch_action(), state=ctk.DISABLED, width=300, anchor='center')
|
||||||
|
apply_button.grid(row=2, column=0, padx=10, pady=10)
|
||||||
|
|
||||||
|
# Output text area with scroll functionality
|
||||||
|
output_frame = ctk.CTkFrame(main_frame, width=300)
|
||||||
|
output_frame.grid(row=3, column=0, padx=10, pady=5)
|
||||||
|
|
||||||
|
output_text = ctk.CTkTextbox(output_frame, height=8, width=300)
|
||||||
|
output_text.grid(row=0, column=0, sticky="nsew")
|
||||||
|
|
||||||
|
# Scrollbar for the text area
|
||||||
|
scrollbar = ctk.CTkScrollbar(output_frame, command=output_text.yview)
|
||||||
|
scrollbar.grid(row=0, column=1, sticky="ns")
|
||||||
|
|
||||||
|
output_text.configure(yscrollcommand=scrollbar.set, state=ctk.DISABLED)
|
||||||
|
|
||||||
|
# Progress bar
|
||||||
|
progress_label = ctk.CTkLabel(main_frame, text="Progress", width=300, anchor='center')
|
||||||
|
progress_label.grid(row=4, column=0, padx=10)
|
||||||
|
|
||||||
|
progress_bar = ctk.CTkProgressBar(main_frame, orientation="horizontal")
|
||||||
|
progress_bar.grid(row=5, column=0, pady=10)
|
||||||
|
progress_bar.set(0)
|
||||||
|
|
||||||
|
def select_folder():
|
||||||
|
"""Select a folder to set as the game folder."""
|
||||||
|
global game_folder
|
||||||
|
folder_path = filedialog.askdirectory()
|
||||||
|
if folder_path:
|
||||||
|
game_folder = folder_path
|
||||||
|
folder_label.configure(text=f"Selected Folder: {folder_path}")
|
||||||
|
apply_button.configure(state=ctk.NORMAL)
|
||||||
|
else:
|
||||||
|
messagebox.showwarning("Warning", "No folder selected.")
|
||||||
|
apply_button.configure(state=ctk.DISABLED)
|
||||||
|
|
||||||
|
def apply_patch_action():
|
||||||
|
"""Apply patch to the selected game folder."""
|
||||||
|
if not game_folder:
|
||||||
|
messagebox.showwarning("Warning", "Please select a game folder!")
|
||||||
|
return
|
||||||
|
apply_patch(game_folder)
|
||||||
|
|
||||||
|
def apply_patch(game_folder):
|
||||||
|
"""Main function to apply patch based on selected folder."""
|
||||||
|
folder_button.configure(state=ctk.DISABLED)
|
||||||
|
delete_files_path = os.path.join(game_folder, 'deletefiles.txt')
|
||||||
|
if not os.path.exists(delete_files_path):
|
||||||
|
folder_button.configure(state=ctk.NORMAL)
|
||||||
|
log_output('deletefiles.txt does not exist in the game directory!')
|
||||||
|
return
|
||||||
|
|
||||||
|
hdiff_map_path = os.path.join(game_folder, 'hdiffmap.json')
|
||||||
|
if not os.path.exists(hdiff_map_path):
|
||||||
|
folder_button.configure(state=ctk.NORMAL)
|
||||||
|
log_output('hdiffmap.json does not exist in the game directory!')
|
||||||
|
return
|
||||||
|
|
||||||
|
# Read deletefiles.txt and delete the specified files
|
||||||
|
with open(delete_files_path, 'r') as delete_files:
|
||||||
|
for file in delete_files.read().split('\n'):
|
||||||
|
if len(file) < 1:
|
||||||
|
continue
|
||||||
|
file_path = os.path.join(game_folder, file)
|
||||||
|
if os.path.exists(file_path):
|
||||||
|
log_output(f'Deleting {file_path}')
|
||||||
|
os.remove(file_path)
|
||||||
|
|
||||||
|
# Check if hpatchz is available in the system path
|
||||||
|
hzpatchz = os.path.join(current_dir, 'hpatchz.exe')
|
||||||
|
if not os.path.exists(hzpatchz):
|
||||||
|
folder_button.configure(state=ctk.NORMAL)
|
||||||
|
log_output(f"HDiffPatch not found in the current directory ({current_dir})!\nPlease make sure hpatchz.exe is in the same folder as the script.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Read hdiffmap.json
|
||||||
|
with open(hdiff_map_path, 'r') as hdiff_json:
|
||||||
|
data = json.load(hdiff_json)
|
||||||
|
|
||||||
|
total_patches = len(data['diff_map'])
|
||||||
|
progress_bar.set(0)
|
||||||
|
|
||||||
|
# Apply patches
|
||||||
|
for i, entry in enumerate(data['diff_map']):
|
||||||
|
source_file_name = os.path.join(game_folder, entry['source_file_name'])
|
||||||
|
patch_file_name = os.path.join(game_folder, entry['patch_file_name'])
|
||||||
|
target_file_name = os.path.join(game_folder, entry['target_file_name'])
|
||||||
|
|
||||||
|
# Check if files exist
|
||||||
|
if not os.path.exists(source_file_name):
|
||||||
|
log_output(f"Source file missing: {source_file_name}")
|
||||||
|
continue
|
||||||
|
if not os.path.exists(patch_file_name):
|
||||||
|
log_output(f"Patch file missing: {patch_file_name}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
log_output(f"Applying patch: {patch_file_name} -> {target_file_name} using {source_file_name}")
|
||||||
|
subprocess.run([hzpatchz, source_file_name, patch_file_name, target_file_name])
|
||||||
|
|
||||||
|
# Update progress bar
|
||||||
|
progress_percentage = float((i + 1) / total_patches)
|
||||||
|
progress_bar.set(progress_percentage)
|
||||||
|
app.update_idletasks()
|
||||||
|
|
||||||
|
log_output("Patch application complete.")
|
||||||
|
|
||||||
|
folder_button.configure(state=ctk.NORMAL)
|
||||||
|
|
||||||
|
def log_output(message):
|
||||||
|
"""Log the output in the Text widget."""
|
||||||
|
output_text.configure(state=ctk.NORMAL)
|
||||||
|
output_text.insert(ctk.END, message + "\n\n")
|
||||||
|
output_text.yview(ctk.END)
|
||||||
|
output_text.configure(state=ctk.DISABLED)
|
||||||
|
|
||||||
|
app.mainloop()
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
customtkinter
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
customtkinter
|
||||||
Generated
+3
@@ -0,0 +1,3 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
Generated
+10
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="PYTHON_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/venv" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="jdk" jdkName="Python 3.10" jdkType="Python SDK" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
Generated
+7
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="DiscordProjectSettings">
|
||||||
|
<option name="show" value="ASK" />
|
||||||
|
<option name="description" value="" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
Generated
+6
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="GitToolBoxBlameSettings">
|
||||||
|
<option name="version" value="2" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<settings>
|
||||||
|
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||||
|
<version value="1.0" />
|
||||||
|
</settings>
|
||||||
|
</component>
|
||||||
Generated
+12
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="MaterialThemeProjectNewConfig">
|
||||||
|
<option name="metadata">
|
||||||
|
<MTProjectMetadataState>
|
||||||
|
<option name="migrated" value="true" />
|
||||||
|
<option name="pristineConfig" value="false" />
|
||||||
|
<option name="userId" value="496a2beb:192c2fc7145:-7ffe" />
|
||||||
|
</MTProjectMetadataState>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
Generated
+7
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Black">
|
||||||
|
<option name="sdkName" value="Python 3.12" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10" project-jdk-type="Python SDK" />
|
||||||
|
</project>
|
||||||
Generated
+8
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/FireflyHdiff.iml" filepath="$PROJECT_DIR$/.idea/FireflyHdiff.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
Generated
+6
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,152 @@
|
|||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import json
|
||||||
|
import customtkinter as ctk
|
||||||
|
from tkinter import filedialog, messagebox
|
||||||
|
|
||||||
|
ctk.set_appearance_mode("System")
|
||||||
|
ctk.set_default_color_theme("blue")
|
||||||
|
# Initialize global variables
|
||||||
|
game_folder = None
|
||||||
|
|
||||||
|
# Create the customtkinter window
|
||||||
|
app = ctk.CTk()
|
||||||
|
app.title("Firefly HDiff Tool")
|
||||||
|
app.geometry("335x470")
|
||||||
|
app.resizable(False, False)
|
||||||
|
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
app.iconbitmap(os.path.join(current_dir, 'image.ico'))
|
||||||
|
app.grid_columnconfigure((0), weight=1)
|
||||||
|
|
||||||
|
# Set appearance mode and theme
|
||||||
|
|
||||||
|
|
||||||
|
# Main frame for better organization
|
||||||
|
main_frame = ctk.CTkFrame(app)
|
||||||
|
main_frame.pack(fill=ctk.BOTH, expand=True)
|
||||||
|
|
||||||
|
# Folder selection button with modern style
|
||||||
|
folder_button = ctk.CTkButton(main_frame, text="Select Game Folder", command=lambda: select_folder(), width=300, anchor='center')
|
||||||
|
folder_button.grid(row=0, column=0, padx=20, pady=20, sticky="ew", columnspan=10)
|
||||||
|
|
||||||
|
# Folder label to display selected folder
|
||||||
|
folder_label = ctk.CTkLabel(main_frame, text="No folder selected", width=200, anchor="center", text_color="gray")
|
||||||
|
folder_label.grid(row=1, column=0, padx=20, pady=20, sticky="ew", columnspan=2)
|
||||||
|
|
||||||
|
# Apply Patch button with modern style
|
||||||
|
apply_button = ctk.CTkButton(main_frame, text="Apply Patch", command=lambda: apply_patch_action(), state=ctk.DISABLED, width=300, anchor='center')
|
||||||
|
apply_button.grid(row=2, column=0, padx=10, pady=10)
|
||||||
|
|
||||||
|
# Output text area with scroll functionality
|
||||||
|
output_frame = ctk.CTkFrame(main_frame, width=300)
|
||||||
|
output_frame.grid(row=3, column=0, padx=10, pady=5)
|
||||||
|
|
||||||
|
output_text = ctk.CTkTextbox(output_frame, height=8, width=300)
|
||||||
|
output_text.grid(row=0, column=0, sticky="nsew")
|
||||||
|
|
||||||
|
# Scrollbar for the text area
|
||||||
|
scrollbar = ctk.CTkScrollbar(output_frame, command=output_text.yview)
|
||||||
|
scrollbar.grid(row=0, column=1, sticky="ns")
|
||||||
|
|
||||||
|
output_text.configure(yscrollcommand=scrollbar.set, state=ctk.DISABLED)
|
||||||
|
|
||||||
|
# Progress bar
|
||||||
|
progress_label = ctk.CTkLabel(main_frame, text="Progress", width=300, anchor='center')
|
||||||
|
progress_label.grid(row=4, column=0, padx=10)
|
||||||
|
|
||||||
|
progress_bar = ctk.CTkProgressBar(main_frame, orientation="horizontal")
|
||||||
|
progress_bar.grid(row=5, column=0, pady=10)
|
||||||
|
progress_bar.set(0)
|
||||||
|
|
||||||
|
def select_folder():
|
||||||
|
"""Select a folder to set as the game folder."""
|
||||||
|
global game_folder
|
||||||
|
folder_path = filedialog.askdirectory()
|
||||||
|
if folder_path:
|
||||||
|
game_folder = folder_path
|
||||||
|
folder_label.configure(text=f"Selected Folder: {folder_path}")
|
||||||
|
apply_button.configure(state=ctk.NORMAL)
|
||||||
|
else:
|
||||||
|
messagebox.showwarning("Warning", "No folder selected.")
|
||||||
|
apply_button.configure(state=ctk.DISABLED)
|
||||||
|
|
||||||
|
def apply_patch_action():
|
||||||
|
"""Apply patch to the selected game folder."""
|
||||||
|
if not game_folder:
|
||||||
|
messagebox.showwarning("Warning", "Please select a game folder!")
|
||||||
|
return
|
||||||
|
apply_patch(game_folder)
|
||||||
|
|
||||||
|
def apply_patch(game_folder):
|
||||||
|
"""Main function to apply patch based on selected folder."""
|
||||||
|
folder_button.configure(state=ctk.DISABLED)
|
||||||
|
delete_files_path = os.path.join(game_folder, 'deletefiles.txt')
|
||||||
|
if not os.path.exists(delete_files_path):
|
||||||
|
folder_button.configure(state=ctk.NORMAL)
|
||||||
|
log_output('deletefiles.txt does not exist in the game directory!')
|
||||||
|
return
|
||||||
|
|
||||||
|
hdiff_map_path = os.path.join(game_folder, 'hdiffmap.json')
|
||||||
|
if not os.path.exists(hdiff_map_path):
|
||||||
|
folder_button.configure(state=ctk.NORMAL)
|
||||||
|
log_output('hdiffmap.json does not exist in the game directory!')
|
||||||
|
return
|
||||||
|
|
||||||
|
# Read deletefiles.txt and delete the specified files
|
||||||
|
with open(delete_files_path, 'r') as delete_files:
|
||||||
|
for file in delete_files.read().split('\n'):
|
||||||
|
if len(file) < 1:
|
||||||
|
continue
|
||||||
|
file_path = os.path.join(game_folder, file)
|
||||||
|
if os.path.exists(file_path):
|
||||||
|
log_output(f'Deleting {file_path}')
|
||||||
|
os.remove(file_path)
|
||||||
|
|
||||||
|
# Check if hpatchz is available in the system path
|
||||||
|
hzpatchz = os.path.join(current_dir, 'hpatchz.exe')
|
||||||
|
if not os.path.exists(hzpatchz):
|
||||||
|
folder_button.configure(state=ctk.NORMAL)
|
||||||
|
log_output(f"HDiffPatch not found in the current directory ({current_dir})!\nPlease make sure hpatchz.exe is in the same folder as the script.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Read hdiffmap.json
|
||||||
|
with open(hdiff_map_path, 'r') as hdiff_json:
|
||||||
|
data = json.load(hdiff_json)
|
||||||
|
|
||||||
|
total_patches = len(data['diff_map'])
|
||||||
|
progress_bar.set(0)
|
||||||
|
|
||||||
|
# Apply patches
|
||||||
|
for i, entry in enumerate(data['diff_map']):
|
||||||
|
source_file_name = os.path.join(game_folder, entry['source_file_name'])
|
||||||
|
patch_file_name = os.path.join(game_folder, entry['patch_file_name'])
|
||||||
|
target_file_name = os.path.join(game_folder, entry['target_file_name'])
|
||||||
|
|
||||||
|
# Check if files exist
|
||||||
|
if not os.path.exists(source_file_name):
|
||||||
|
log_output(f"Source file missing: {source_file_name}")
|
||||||
|
continue
|
||||||
|
if not os.path.exists(patch_file_name):
|
||||||
|
log_output(f"Patch file missing: {patch_file_name}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
log_output(f"Applying patch: {patch_file_name} -> {target_file_name} using {source_file_name}")
|
||||||
|
subprocess.run([hzpatchz, source_file_name, patch_file_name, target_file_name])
|
||||||
|
|
||||||
|
# Update progress bar
|
||||||
|
progress_percentage = float((i + 1) / total_patches)
|
||||||
|
progress_bar.set(progress_percentage)
|
||||||
|
app.update_idletasks()
|
||||||
|
|
||||||
|
log_output("Patch application complete.")
|
||||||
|
|
||||||
|
folder_button.configure(state=ctk.NORMAL)
|
||||||
|
|
||||||
|
def log_output(message):
|
||||||
|
"""Log the output in the Text widget."""
|
||||||
|
output_text.configure(state=ctk.NORMAL)
|
||||||
|
output_text.insert(ctk.END, message + "\n\n")
|
||||||
|
output_text.yview(ctk.END)
|
||||||
|
output_text.configure(state=ctk.DISABLED)
|
||||||
|
|
||||||
|
app.mainloop()
|
||||||
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
customtkinter
|
||||||
Reference in New Issue
Block a user