mirror of
https://github.com/smartcmd/MinecraftConsoles.git
synced 2026-08-20 09:57:09 +00:00
Modtools
This commit is contained in:
parent
95d4d0a703
commit
427aac1c32
222
Modtools/pck_tool.py
Normal file
222
Modtools/pck_tool.py
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
import os
|
||||
import re
|
||||
import tkinter as tk
|
||||
from tkinter import filedialog, messagebox
|
||||
import customtkinter as ctk
|
||||
from tkinterdnd2 import DND_FILES, TkinterDnD
|
||||
|
||||
PNG_HEADER = b"\x89PNG\r\n\x1a\n"
|
||||
|
||||
LANGUAGES = {
|
||||
"English": {
|
||||
"title": "PCK Skin Tool",
|
||||
"drop": "DRAG & DROP PCK FILES HERE",
|
||||
"extract_folder": "Extract Folder",
|
||||
"create_pck": "Create PCK",
|
||||
"language": "Language",
|
||||
"theme": "Theme:",
|
||||
"logs": "Activity Log",
|
||||
"success_extract": "Extraction completed",
|
||||
"success_create": "PCK created successfully",
|
||||
"error_no_png": "No PNG files found",
|
||||
"error_no_png_in_pck": "No PNG inside PCK"
|
||||
},
|
||||
"Español": {
|
||||
"title": "Herramienta PCK",
|
||||
"drop": "ARRASTRA ARCHIVOS .PCK AQUÍ",
|
||||
"extract_folder": "Extraer Carpeta",
|
||||
"create_pck": "Crear PCK",
|
||||
"language": "Idioma",
|
||||
"theme": "Tema:",
|
||||
"logs": "Registro de Actividad",
|
||||
"success_extract": "Extracción completada",
|
||||
"success_create": "PCK creado correctamente",
|
||||
"error_no_png": "No se encontraron PNG",
|
||||
"error_no_png_in_pck": "Sin PNG dentro del PCK"
|
||||
}
|
||||
}
|
||||
|
||||
ctk.set_appearance_mode("dark")
|
||||
ctk.set_default_color_theme("blue")
|
||||
|
||||
class PCKTool(TkinterDnD.Tk):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
|
||||
self.language = "English"
|
||||
self.translations = LANGUAGES[self.language]
|
||||
|
||||
current_mode = 1 if ctk.get_appearance_mode() == "Dark" else 0
|
||||
bg_color = ctk.ThemeManager.theme["CTk"]["fg_color"][current_mode]
|
||||
self.config(background=bg_color)
|
||||
|
||||
self.title(self.translations["title"])
|
||||
self.geometry("850x570")
|
||||
|
||||
# Grid layout
|
||||
self.grid_columnconfigure(1, weight=1)
|
||||
self.grid_rowconfigure(0, weight=1)
|
||||
|
||||
# --- SIDEBAR ---
|
||||
self.sidebar = ctk.CTkFrame(
|
||||
self,
|
||||
width=220,
|
||||
corner_radius=0,
|
||||
fg_color=("#EDEDED", "#1A1A1A")
|
||||
)
|
||||
self.sidebar.grid(row=0, column=0, sticky="nsew")
|
||||
self.sidebar.grid_rowconfigure(6, weight=1)
|
||||
|
||||
self.logo_label = ctk.CTkLabel(
|
||||
self.sidebar,
|
||||
text="PCK EXTRACTOR",
|
||||
font=ctk.CTkFont(size=20, weight="bold"),
|
||||
anchor="w"
|
||||
)
|
||||
self.logo_label.grid(row=0, column=0, padx=20, pady=(20, 30), sticky="w")
|
||||
|
||||
self.lang_label = ctk.CTkLabel(self.sidebar, text=self.translations["language"], anchor="w")
|
||||
self.lang_label.grid(row=1, column=0, padx=20, pady=(10, 0), sticky="w")
|
||||
|
||||
self.lang_menu = ctk.CTkOptionMenu(self.sidebar, values=list(LANGUAGES.keys()), command=self.change_language)
|
||||
self.lang_menu.set(self.language)
|
||||
self.lang_menu.grid(row=2, column=0, padx=20, pady=10, sticky="w")
|
||||
|
||||
# self.theme_label = ctk.CTkLabel(self.sidebar, text=self.translations["theme"], anchor="w")
|
||||
# self.theme_label.grid(row=3, column=0, padx=20, pady=(20, 0), sticky="w")
|
||||
|
||||
# self.appearance_mode_menu = ctk.CTkOptionMenu(self.sidebar, values=["Dark", "Light"], command=self.change_appearance_mode)
|
||||
# self.appearance_mode_menu.grid(row=4, column=0, padx=20, pady=10, sticky="w")
|
||||
|
||||
# --- MAIN CONTENT ---
|
||||
self.main_container = ctk.CTkFrame(self, fg_color="transparent")
|
||||
self.main_container.grid(row=0, column=1, padx=20, pady=20, sticky="nsew")
|
||||
self.main_container.grid_columnconfigure(0, weight=1)
|
||||
self.main_container.grid_rowconfigure(3, weight=1)
|
||||
|
||||
# Drop Zone
|
||||
self.drop_frame = ctk.CTkFrame(
|
||||
self.main_container,
|
||||
border_width=2,
|
||||
border_color="#3B8ED0",
|
||||
fg_color=("white", "#2B2B2B")
|
||||
)
|
||||
self.drop_frame.grid(row=0, column=0, sticky="nsew", pady=(0, 20), ipady=50)
|
||||
self.drop_frame.grid_columnconfigure(0, weight=1)
|
||||
self.drop_frame.grid_rowconfigure(0, weight=1)
|
||||
|
||||
self.drop_label = ctk.CTkLabel(self.drop_frame, text=self.translations["drop"],
|
||||
font=ctk.CTkFont(size=16, weight="bold"))
|
||||
self.drop_label.grid(row=0, column=0)
|
||||
|
||||
self.drop_frame.drop_target_register(DND_FILES)
|
||||
self.drop_frame.dnd_bind("<<Drop>>", self.drop_file)
|
||||
|
||||
# Action Buttons
|
||||
self.button_frame = ctk.CTkFrame(self.main_container, fg_color="transparent")
|
||||
self.button_frame.grid(row=1, column=0, sticky="ew")
|
||||
self.button_frame.grid_columnconfigure((0, 1), weight=1)
|
||||
|
||||
self.extract_button = ctk.CTkButton(self.button_frame, text=self.translations["extract_folder"],
|
||||
height=45, fg_color="#1f538d", command=self.extract_folder)
|
||||
self.extract_button.grid(row=0, column=0, padx=5, pady=10, sticky="ew")
|
||||
|
||||
self.create_button = ctk.CTkButton(self.button_frame, text=self.translations["create_pck"],
|
||||
height=45, fg_color="#2b8a3e", hover_color="#1e5f2b",
|
||||
command=self.create_pck)
|
||||
self.create_button.grid(row=0, column=1, padx=5, pady=10, sticky="ew")
|
||||
|
||||
# Logs
|
||||
self.log_label = ctk.CTkLabel(self.main_container, text=self.translations["logs"], font=ctk.CTkFont(weight="bold"))
|
||||
self.log_label.grid(row=2, column=0, sticky="w", pady=(10, 0))
|
||||
|
||||
self.textbox = ctk.CTkTextbox(self.main_container, corner_radius=10, border_width=1)
|
||||
self.textbox.grid(row=3, column=0, sticky="nsew", pady=(5, 0))
|
||||
|
||||
self.log("System initialized.")
|
||||
|
||||
def log(self, message):
|
||||
self.textbox.insert("end", f"[*] {message}\n")
|
||||
self.textbox.see("end")
|
||||
|
||||
def change_appearance_mode(self, new_mode: str):
|
||||
ctk.set_appearance_mode(new_mode)
|
||||
current_mode = 1 if new_mode == "Dark" else 0
|
||||
bg_color = ctk.ThemeManager.theme["CTk"]["fg_color"][current_mode]
|
||||
self.config(background=bg_color)
|
||||
|
||||
def change_language(self, choice):
|
||||
self.language = choice
|
||||
self.translations = LANGUAGES[self.language]
|
||||
self.refresh_ui()
|
||||
|
||||
def refresh_ui(self):
|
||||
self.title(self.translations["title"])
|
||||
self.lang_label.configure(text=self.translations["language"])
|
||||
self.theme_label.configure(text=self.translations["theme"])
|
||||
self.drop_label.configure(text=self.translations["drop"])
|
||||
self.extract_button.configure(text=self.translations["extract_folder"])
|
||||
self.create_button.configure(text=self.translations["create_pck"])
|
||||
self.log_label.configure(text=self.translations["logs"])
|
||||
|
||||
def extract_pck(self, filepath):
|
||||
try:
|
||||
filename = os.path.basename(filepath)
|
||||
with open(filepath, "rb") as f:
|
||||
data = f.read()
|
||||
positions = [m.start() for m in re.finditer(PNG_HEADER, data)]
|
||||
if not positions:
|
||||
self.log(f"Error: No PNGs in {filename}")
|
||||
return
|
||||
output_dir = os.path.splitext(filepath)[0] + "_extracted"
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
for i, pos in enumerate(positions):
|
||||
next_pos = positions[i+1] if i+1 < len(positions) else len(data)
|
||||
chunk = data[pos:next_pos]
|
||||
with open(os.path.join(output_dir, f"skin_{i+1:03d}.png"), "wb") as out:
|
||||
out.write(chunk)
|
||||
self.log(f"Success: {filename} -> {len(positions)} skins extracted.")
|
||||
except Exception as e:
|
||||
self.log(f"Critical Error: {str(e)}")
|
||||
|
||||
def drop_file(self, event):
|
||||
raw_data = event.data
|
||||
if raw_data.startswith('{') and raw_data.endswith('}'):
|
||||
paths = [raw_data[1:-1]]
|
||||
else:
|
||||
paths = self.tk.splitlist(raw_data)
|
||||
for path in paths:
|
||||
if path.lower().endswith(".pck"):
|
||||
self.extract_pck(path)
|
||||
messagebox.showinfo("Process", self.translations["success_extract"])
|
||||
|
||||
def extract_folder(self):
|
||||
folder = filedialog.askdirectory()
|
||||
if not folder: return
|
||||
pck_files = [f for f in os.listdir(folder) if f.lower().endswith(".pck")]
|
||||
for file in pck_files:
|
||||
self.extract_pck(os.path.join(folder, file))
|
||||
messagebox.showinfo("OK", self.translations["success_extract"])
|
||||
|
||||
def create_pck(self):
|
||||
folder = filedialog.askdirectory()
|
||||
if not folder: return
|
||||
pngs = sorted([f for f in os.listdir(folder) if f.lower().endswith(".png")])
|
||||
if not pngs:
|
||||
messagebox.showerror("Error", self.translations["error_no_png"])
|
||||
return
|
||||
output_path = os.path.join(folder, "custom_skins.pck")
|
||||
try:
|
||||
with open(output_path, "wb") as out:
|
||||
for png in pngs:
|
||||
with open(os.path.join(folder, png), "rb") as f:
|
||||
out.write(f.read())
|
||||
self.log(f"Created PCK: {output_path} ({len(pngs)} images)")
|
||||
messagebox.showinfo("OK", self.translations["success_create"])
|
||||
except Exception as e:
|
||||
self.log(f"Error: {str(e)}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = PCKTool()
|
||||
app.mainloop()
|
||||
112
Modtools/readme.md
Normal file
112
Modtools/readme.md
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
# PCK Decompiler
|
||||
|
||||
Technical Documentation and Project Specifications
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
**PCK Decompiler** is an open-source Python tool designed to extract and rebuild `.pck` files containing image assets (PNG) used in game UI systems.
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
The tool is composed of:
|
||||
|
||||
- `pck_tool.py` → Main application
|
||||
- Graphical Interface built with `CustomTkinter`
|
||||
- Drag & Drop support via `tkinterdnd2`
|
||||
- Image handling via `Pillow`
|
||||
|
||||
It supports:
|
||||
|
||||
- Drag & Drop `.pck` files
|
||||
- Folder-wide decompilation
|
||||
- PNG injection
|
||||
- Language switching (UI localization)
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
### 1️⃣ Requirements
|
||||
|
||||
- Python 3.9+
|
||||
- Visual Studio Code
|
||||
|
||||
---
|
||||
|
||||
### 2️⃣ Install Dependencies (Recommended via VS Code Terminal)
|
||||
|
||||
Open the project folder in **Visual Studio Code**.
|
||||
|
||||
Then open the integrated terminal:
|
||||
|
||||
Terminal → New Terminal
|
||||
|
||||
Run the following commands:
|
||||
|
||||
python -m venv venv
|
||||
|
||||
Activate the virtual environment:
|
||||
|
||||
Windows:
|
||||
|
||||
venv\\Scripts\\activate
|
||||
|
||||
Install required packages:
|
||||
|
||||
pip install --upgrade pip
|
||||
pip install customtkinter pillow tkinterdnd2
|
||||
|
||||
---
|
||||
|
||||
## Running the Tool (via VS Code Terminal)
|
||||
|
||||
With the virtual environment activated inside VS Code terminal:
|
||||
|
||||
python pck_tool.py
|
||||
|
||||
The GUI will launch automatically.
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
|
||||
### Drag & Drop
|
||||
|
||||
Drop `.pck` files directly into the application window to extract contents.
|
||||
|
||||
### Folder Decompilation
|
||||
|
||||
Select a folder and the tool will process all `.pck` files inside it.
|
||||
|
||||
### PNG Injection
|
||||
|
||||
Import custom `.png` files and rebuild the `.pck` structure.
|
||||
|
||||
### Language Selection
|
||||
|
||||
Switch UI language dynamically from the interface.
|
||||
|
||||
---
|
||||
|
||||
## Repository Integration
|
||||
|
||||
This tool is designed to:
|
||||
|
||||
- Live inside another repository
|
||||
- Be submitted as a Pull Request
|
||||
- Not require independent directory generation
|
||||
- Not auto-create standalone project folders
|
||||
|
||||
It behaves as an internal utility module.
|
||||
|
||||
---
|
||||
|
||||
## Known Limitations
|
||||
|
||||
- Supports PNG-based PCK structures
|
||||
- Does not reverse proprietary compression (if present)
|
||||
- Assumes consistent internal file structure
|
||||
Loading…
Reference in a new issue