Base de Datos WE6FE Database Editor
#8
(10-28-2026, 08:22 AM)Ermolli91 escribió: Gran avance, se esta dando bastante rápido. Bien ahí.

Si parce, le estoy dando duro en las noches que tengo tiempo libre.

Ya practicamente esta el editor, se ve muy rudimentario, muy retro jaja Big Grin pero bueno, la idea era aprender y eso lo estoy haciendo dia a dia con este juego, por ejemplo, me he llevado una grata sorpresa, este juego solo cuenta con 1 molde de player Big Grin

Los archivos estan mejor distribuidos, y no se que tiene ese Motor Grafico, pero es mas NITIDO, Mas Claro, como con mas pixeles jaj

Pienso que una vez terminado bien el editor, en lo proximo que debo hacer es importar los datos de SoFIFA para que quede mas facil el desarrollo, se que a dia de hoy, hay tools que facilitan mucho eso, pero primero es terminar el editor, despues le agregamos o quitamos detalles de la tool

Dejo el codigo por si lo desean estudiar:

Toda este desarrollo por: Pino, Pato_Lukas18 & Gemini, aprendiendo tambien de eso de la IA, aunque ya voy de salida, algo se aprende!

Codigo Python:
 
Código PHP:
import struct
import tkinter 
as tk
from tkinter import colorchooser
, messagebox, ttk

# ==========================================
# RUTAS Y OFFSETS (WE6FE SLPM_622.68 / parameter.ovl)
# ==========================================
SLPM_PATH = "SLPM_622.68"
OVL_PATH = "parameter.ovl"

# Offsets de Jugadores
SLPM_BASE_OFFSET = 0x1F24B0
PLAYER_SIZE 
= 44
NAMES_BASE_OFFSET 
= 0x668
NAME_SIZE 
= 32
CLUB_NAMES_BASE_OFFSET 
= 0x17A90
NUMBERS_BASE_OFFSET 
= 0x338D0
NUMBERS_RECORD_SIZE 
= 23
CLUB_NUMBERS_OFFSET 
= 0x33F80
CLUB_RECORD_SIZE 
= 23
SHIRT_NAMES_OFFSET 
= 0x116C0
SHIRT_NAME_SIZE 
= 16
NUM_PLAYERS 
= 500
MAX_DATABASE_PLAYERS 
= 3000

# Offsets de Equipos y Plantillas (Separación Nacionales vs Clubes)
TEAM_NAMES_OFFSET = 0x23F8C8
TEAM_NAME_LIMIT 
= 16
NUM_TEAMS 
= 64
NUM_NAT_TEAMS 
= 32  # Los primeros 32 equipos son Selecciones Nacionales

# Offsets y Tamaños de Vínculos exactos
NAT_LINKS_OFFSET = 0x20C230     # En SLPM_622.68
CLUB_LINKS_OFFSET = 0x353B0    # En parameter.ovl
TEAM_RECORD_SIZE = 80          # 80 bytes por registro de equipo

# Lista de atributos mapeados en el SLPM
ATTRIBUTES_DEF = [
    (
"Ataque", 2), ("Defensa", 3), ("Balance", 4), ("Stamina", 5),
    (
"Top Speed", 6), ("Aceleración", 7), ("Respuesta", 8), ("Agilidad", 9),
    (
"Dribble Acc", 10), ("Dribble Vel", 11), ("P. Corto Acc", 12), ("P. Corto Vel", 13),
    (
"P. Largo Acc", 14), ("P. Largo Vel", 15), ("Tiro Acc", 16), ("Tiro Potencia", 17),
    (
"Tiro Téc", 18), ("Falta / T.Libre", 19), ("Curva", 20), ("Cabezazo", 21),
    (
"Salto", 22), ("Técnica", 23), ("Agresividad", 24), ("Mentalidad", 25),
    (
"Habil. Arquero", 26), ("Trabajo Equipo", 27), ("Condición", 28), 
    (
"Pie Bueno Frec", 29), ("Pie Bueno", 30)
]

class 
WE6FE_EditorApp:
    
def __init__(self, root):
        
self.root = root
        self
.root.title("WE6FE Database Editor & Engine Analyzer - By WE11world, Pato_Lucas18 & Gemini")
        
self.root.geometry("1220x880")
        
self.root.config(bg="#1e1e1e")

        
self.current_player_index = 0
        self
.current_team_index = 0
        self
.current_roster_team_index = 0
        self
.current_source_team_index = 0
        self
.entries = {}
        
self.team_name_entry = None
        self
.team_listbox = None

        self
.lbl_power_att = None
        self
.lbl_power_def = None
        self
.lbl_power_ovr = None

        self
.kit_team_combobox = None
        self
.kit_canvases = {}

        
# --- ESTILO GENERAL Y DE PESTAÑAS ---
        
style = ttk.Style()
        
style.theme_use('default')
        
style.configure('TNotebook', background='#1e1e1e', borderwidth=0)
        
style.configure('TNotebook.Tab', background='#2d2d2d', foreground='#ffffff', font=('Arial', 10, 'bold'), padding=[12, 6])
        
style.map('TNotebook.Tab', background=[('selected', '#007acc')], foreground=[('selected', '#ffffff')])

        
style.configure("Treeview", background="#2a2a2a", foreground="#ffffff", fieldbackground="#2a2a2a", font=("Consolas", 9), rowheight=22)
        
style.configure("Treeview.Heading", background="#3c3c3c", foreground="#00ffcc", font=("Arial", 9, "bold"))
        
style.map("Treeview", background=[('selected', '#007acc')], foreground=[('selected', '#ffffff')])

        
self.notebook = ttk.Notebook(root)
        
self.notebook.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)

        
self.tab_players = tk.Frame(self.notebook, bg="#1e1e1e")
        
self.tab_teams = tk.Frame(self.notebook, bg="#1e1e1e")
        
self.tab_rosters = tk.Frame(self.notebook, bg="#1e1e1e")
        
self.tab_kits = tk.Frame(self.notebook, bg="#1e1e1e")

        
self.notebook.add(self.tab_players, text=" ⚽ Edición de Jugadores ")
        
self.notebook.add(self.tab_teams, text=" ?️ Edición de Equipos ")
        
self.notebook.add(self.tab_rosters, text=" ? Plantillas y Traspasos ")
        
self.notebook.add(self.tab_kits, text=" ? Kits & Colores (C Engine) ")

        
self.build_players_tab()
        
self.build_teams_tab()
        
self.build_rosters_tab()
        
self.build_kits_tab()

    
def is_valid_name(self, text):
        if 
not text or len(text) < 1:
            return 
False
        valid_chars 
= sum(1 for c in text if c.isalnum() or c.isspace() or c in ".-'áéíóúÁÉÍÓÚñÑ")
        if (
valid_chars / len(text)) < 0.75:
            return 
False
        
return True

    def get_clean_player_info
(self, f_ovl, pid):
        
p_name = ""
        
shirt_name = ""
        
try:
            if 
pid < 150:
                
pos = NAMES_BASE_OFFSET + (pid * NAME_SIZE)
                
shirt_pos = SHIRT_NAMES_OFFSET + (pid * SHIRT_NAME_SIZE)
            else:
                
base_club_id = 1451
                index_offset 
= pid - base_club_id if pid >= 1451 else (pid - 150)
                
pos = CLUB_NAMES_BASE_OFFSET + (index_offset * NAME_SIZE)
                
            
f_ovl.seek(pos)
            
raw_name = f_ovl.read(NAME_SIZE)
            
extracted_name = raw_name.split(b'\x00')[0].decode('latin-1', errors='ignore').strip()
            
            if 
self.is_valid_name(extracted_name):
                
p_name = extracted_name
            
            
if pid < 150:
                
f_ovl.seek(shirt_pos)
                
raw_shirt = f_ovl.read(SHIRT_NAME_SIZE)
                
extracted_shirt = raw_shirt.split(b'\x00')[0].decode('latin-1', errors='ignore').strip()
                if 
self.is_valid_name(extracted_shirt):
                    
shirt_name = extracted_shirt
                elif p_name
:
                    
shirt_name = p_name.upper()[:16]
            else:
                if 
p_name:
                    
shirt_name = p_name.upper()[:16]

            if 
not p_name:
                if 
shirt_name:
                    
p_name = shirt_name
                
else:
                    
p_name = f"Jugador #{pid}"

        
except Exception:
            
p_name = f"Jugador #{pid}"
            
        
return p_name, shirt_name

    def get_team_file_and_info
(self, team_index):
        if 
team_index < NUM_NAT_TEAMS:
            return 
SLPM_PATH, NAT_LINKS_OFFSET, 23
        
else:
            return 
OVL_PATH, CLUB_LINKS_OFFSET, 23

    
# ==========================================
    # PESTAÑA 1: JUGADORES
    # ==========================================
    
def build_players_tab(self):
        
main_frame = tk.Frame(self.tab_players, bg="#1e1e1e")
        
main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)

        
left_frame = tk.Frame(main_frame, bg="#2d2d2d", width=280)
        
left_frame.pack(side=tk.LEFT, fill=tk.Y, padx=(0, 10))

        
tk.Label(left_frame, text="Plantilla de Jugadores", bg="#2d2d2d", fg="#ffffff", font=("Arial", 11, "bold")).pack(pady=10)

        
list_scroll = tk.Scrollbar(left_frame)
        
list_scroll.pack(side=tk.RIGHT, fill=tk.Y)

        
self.player_listbox = tk.Listbox(left_frame, yscrollcommand=list_scroll.set, bg="#1e1e1e", fg="#00ffcc", selectbackground="#007acc", font=("Consolas", 10), height=36)
        
self.player_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        
self.player_listbox.bind('<<ListboxSelect>>', self.on_player_select)
        
list_scroll.config(command=self.player_listbox.yview)

        
right_frame = tk.Frame(main_frame, bg="#2d2d2d")
        
right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True)

        
top_edit_frame1 = tk.Frame(right_frame, bg="#2d2d2d")
        
top_edit_frame1.pack(fill=tk.X, padx=15, pady=(10, 5))

        
tk.Label(top_edit_frame1, text="Nombre:", bg="#2d2d2d", fg="#00ffcc", font=("Arial", 10, "bold")).pack(side=tk.LEFT, padx=(0, 5))
        
self.name_entry = tk.Entry(top_edit_frame1, bg="#3c3c3c", fg="#ffffff", insertbackground="white", font=("Consolas", 10, "bold"), width=18)
        
self.name_entry.pack(side=tk.LEFT, padx=(0, 15))

        
tk.Label(top_edit_frame1, text="Nom. Camiseta:", bg="#2d2d2d", fg="#00ffcc", font=("Arial", 10, "bold")).pack(side=tk.LEFT, padx=(0, 5))
        
self.shirt_name_entry = tk.Entry(top_edit_frame1, bg="#3c3c3c", fg="#ffffff", insertbackground="white", font=("Consolas", 10, "bold"), width=16)
        
self.shirt_name_entry.pack(side=tk.LEFT)

        
top_edit_frame2 = tk.Frame(right_frame, bg="#2d2d2d")
        
top_edit_frame2.pack(fill=tk.X, padx=15, pady=(0, 10))

        
tk.Label(top_edit_frame2, text="D.Nac:", bg="#2d2d2d", fg="#00ffcc", font=("Arial", 10, "bold")).pack(side=tk.LEFT, padx=(0, 3))
        
self.dorsal_entry = tk.Entry(top_edit_frame2, bg="#3c3c3c", fg="#ffffff", insertbackground="white", font=("Consolas", 10, "bold"), width=4)
        
self.dorsal_entry.pack(side=tk.LEFT, padx=(0, 15))

        
tk.Label(top_edit_frame2, text="D.Club:", bg="#2d2d2d", fg="#00ffcc", font=("Arial", 10, "bold")).pack(side=tk.LEFT, padx=(0, 3))
        
self.club_dorsal_entry = tk.Entry(top_edit_frame2, bg="#3c3c3c", fg="#ffffff", insertbackground="white", font=("Consolas", 10, "bold"), width=4)
        
self.club_dorsal_entry.pack(side=tk.LEFT)

        
canvas = tk.Canvas(right_frame, bg="#2d2d2d", highlightthickness=0)
        
scrollbar = tk.Scrollbar(right_frame, orient="vertical", command=canvas.yview)
        
self.form_frame = tk.Frame(canvas, bg="#2d2d2d")

        for 
col_idx in range(4):
            
self.form_frame.grid_columnconfigure(col_idx, weight=1)

        
self.form_frame.bind("<Configure>", lambda e: canvas.configure(scrollregion=canvas.bbox("all")))
        
canvas.create_window((0, 0), window=self.form_frame, anchor="nw")
        
canvas.configure(yscrollcommand=scrollbar.set)

        
canvas.pack(side=tk.TOP, fill=tk.BOTH, expand=True, padx=5, pady=5)
        
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

        
row = 0
        col 
= 0
        
for name, byte_idx in ATTRIBUTES_DEF:
            
lbl = tk.Label(self.form_frame, text=name + ":", bg="#2d2d2d", fg="#d4d4d4", font=("Arial", 9, "bold"), anchor="e")
            
lbl.grid(row=row, column=col*2, sticky="e", padx=5, pady=4)

            
entry = tk.Entry(self.form_frame, width=8, bg="#3c3c3c", fg="#ffffff", insertbackground="white", font=("Consolas", 10))
            
entry.grid(row=row, column=col*2+1, sticky="w", padx=5, pady=4)
            
self.entries[byte_idx] = entry

            col 
+= 1
            
if col > 1:
                
col = 0
                row 
+= 1

        btn_save 
= tk.Button(right_frame, text="Guardar Cambios de Jugador", bg="#007acc", fg="#ffffff", font=("Arial", 10, "bold"), command=self.save_player_data)
        
btn_save.pack(pady=15)

        
self.load_players_list()

    
# ==========================================
    # PESTAÑA 2: EQUIPOS
    # ==========================================
    
def build_teams_tab(self):
        
main_frame = tk.Frame(self.tab_teams, bg="#1e1e1e")
        
main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)

        
left_frame = tk.Frame(main_frame, bg="#2d2d2d", width=320)
        
left_frame.pack(side=tk.LEFT, fill=tk.Y, padx=(0, 10))

        
tk.Label(left_frame, text="Listado de Equipos", bg="#2d2d2d", fg="#ffffff", font=("Arial", 11, "bold")).pack(pady=10)

        
list_scroll = tk.Scrollbar(left_frame)
        
list_scroll.pack(side=tk.RIGHT, fill=tk.Y)

        
self.team_listbox = tk.Listbox(left_frame, yscrollcommand=list_scroll.set, bg="#1e1e1e", fg="#ffcc00", selectbackground="#007acc", font=("Consolas", 10), height=36)
        
self.team_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        
self.team_listbox.bind('<<ListboxSelect>>', self.on_team_select)
        
list_scroll.config(command=self.team_listbox.yview)

        
right_frame = tk.Frame(main_frame, bg="#2d2d2d")
        
right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True)

        
edit_container = tk.Frame(right_frame, bg="#2d2d2d")
        
edit_container.pack(pady=40, padx=20, fill=tk.X)

        
tk.Label(edit_container, text="Nombre del Equipo:", bg="#2d2d2d", fg="#ffcc00", font=("Arial", 11, "bold")).pack(anchor="w", pady=(0, 8))
        
        
self.team_name_entry = tk.Entry(edit_container, bg="#3c3c3c", fg="#ffffff", insertbackground="white", font=("Consolas", 12, "bold"), width=25)
        
self.team_name_entry.pack(anchor="w", pady=(0, 20))

        
btn_save_team = tk.Button(edit_container, text="Guardar Nombre de Equipo en SLPM", bg="#007acc", fg="#ffffff", font=("Arial", 10, "bold"), command=self.save_team_data)
        
btn_save_team.pack(anchor="w")

        
self.load_teams_list()

    
# ==========================================
    # PESTAÑA 3: PLANTILLAS Y TRASPASOS (Doble Panel Inteligente - 23 Slots)
    # ==========================================
    
def build_rosters_tab(self):
        
main_frame = tk.Frame(self.tab_rosters, bg="#1e1e1e")
        
main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)

        
top_control_frame = tk.Frame(main_frame, bg="#2d2d2d", padx=10, pady=10)
        
top_control_frame.pack(fill=tk.X, pady=(0, 10))

        
team_names_list = [f"Equipo #{i} {'(Selección)' if i < NUM_NAT_TEAMS else '(Club)'}" for i in range(NUM_TEAMS)]

        
tk.Label(top_control_frame, text="Origen:", bg="#2d2d2d", fg="#00ffcc", font=("Arial", 9, "bold")).pack(side=tk.LEFT, padx=(0, 5))
        
self.source_team_combobox = ttk.Combobox(top_control_frame, values=team_names_list, width=26, state="readonly")
        
self.source_team_combobox.pack(side=tk.LEFT, padx=(0, 15))
        
self.source_team_combobox.current(1 if NUM_TEAMS > 1 else 0)
        
self.source_team_combobox.bind("<<ComboboxSelected>>", self.on_source_team_combo_select)

        
tk.Label(top_control_frame, text="Destino:", bg="#2d2d2d", fg="#ffcc00", font=("Arial", 9, "bold")).pack(side=tk.LEFT, padx=(0, 5))
        
self.roster_team_combobox = ttk.Combobox(top_control_frame, values=team_names_list, width=26, state="readonly")
        
self.roster_team_combobox.pack(side=tk.LEFT, padx=(0, 15))
        
self.roster_team_combobox.current(0)
        
self.roster_team_combobox.bind("<<ComboboxSelected>>", self.on_roster_team_combo_select)

        
self.lbl_power_att = tk.Label(top_control_frame, text="ATK: --", bg="#2d2d2d", fg="#00ffcc", font=("Consolas", 9, "bold"))
        
self.lbl_power_att.pack(side=tk.LEFT, padx=4)
        
self.lbl_power_def = tk.Label(top_control_frame, text="DEF: --", bg="#2d2d2d", fg="#00ffcc", font=("Consolas", 9, "bold"))
        
self.lbl_power_def.pack(side=tk.LEFT, padx=4)
        
self.lbl_power_ovr = tk.Label(top_control_frame, text="OVR: --", bg="#2d2d2d", fg="#ff3366", font=("Consolas", 9, "bold"))
        
self.lbl_power_ovr.pack(side=tk.LEFT, padx=4)

        
tables_frame = tk.Frame(main_frame, bg="#1e1e1e")
        
tables_frame.pack(fill=tk.BOTH, expand=True)

        
columns_def = ("id", "slot", "name", "shirt")

        
# --- TABLA IZQUIERDA: Plantilla Origen ---
        
left_container = tk.LabelFrame(tables_frame, text=" Plantilla Origen (23 Slots) ", bg="#2d2d2d", fg="#00ffcc", font=("Arial", 10, "bold"), padx=5, pady=5)
        
left_container.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(0, 5))

        
self.source_tree = ttk.Treeview(left_container, columns=columns_def, show="headings", height=22)
        
self.source_tree.heading("id", text="ID")
        
self.source_tree.heading("slot", text="Slot")
        
self.source_tree.heading("name", text="Name")
        
self.source_tree.heading("shirt", text="Shirt Name")
        
        
self.source_tree.column("id", width=50, anchor="center")
        
self.source_tree.column("slot", width=45, anchor="center")
        
self.source_tree.column("name", width=140, anchor="w")
        
self.source_tree.column("shirt", width=120, anchor="w")
        
        
source_scroll = ttk.Scrollbar(left_container, orient="vertical", command=self.source_tree.yview)
        
self.source_tree.configure(yscrollcommand=source_scroll.set)
        
        
self.source_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        
source_scroll.pack(side=tk.RIGHT, fill=tk.Y)

        
# --- BOTON DE TRASPASO ---
        
middle_frame = tk.Frame(tables_frame, bg="#1e1e1e", width=70)
        
middle_frame.pack(side=tk.LEFT, fill=tk.Y, padx=5)

        
btn_transfer_to_team = tk.Button(middle_frame, text=" ➔ \nFichar", bg="#007acc", fg="#ffffff", font=("Arial", 9, "bold"), command=self.transfer_player_to_slot)
        
btn_transfer_to_team.pack(expand=True, fill=tk.X, pady=5)

        
# --- TABLA DERECHA: Plantilla Destino ---
        
right_container = tk.LabelFrame(tables_frame, text=" Plantilla Destino (23 Slots) ", bg="#2d2d2d", fg="#ffcc00", font=("Arial", 10, "bold"), padx=5, pady=5)
        
right_container.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=(5, 0))

        
self.dest_tree = ttk.Treeview(right_container, columns=columns_def, show="headings", height=22)
        
self.dest_tree.heading("id", text="ID")
        
self.dest_tree.heading("slot", text="Slot")
        
self.dest_tree.heading("name", text="Name")
        
self.dest_tree.heading("shirt", text="Shirt Name")
        
        
self.dest_tree.column("id", width=50, anchor="center")
        
self.dest_tree.column("slot", width=45, anchor="center")
        
self.dest_tree.column("name", width=140, anchor="w")
        
self.dest_tree.column("shirt", width=120, anchor="w")
        
        
dest_scroll = ttk.Scrollbar(right_container, orient="vertical", command=self.dest_tree.yview)
        
self.dest_tree.configure(yscrollcommand=dest_scroll.set)
        
        
self.dest_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        
dest_scroll.pack(side=tk.RIGHT, fill=tk.Y)

        
self.load_roster_teams_list_v2()

    
def load_roster_teams_list_v2(self):
        try:
            
team_names = []
            
with open(SLPM_PATH, "rb") as f_slpm:
                for 
i in range(NUM_TEAMS):
                    
f_slpm.seek(TEAM_NAMES_OFFSET + (i * TEAM_RECORD_SIZE))
                    
name_data = f_slpm.read(TEAM_NAME_LIMIT)
                    
t_name = name_data.decode('latin-1', errors='ignore').rstrip('\x00').strip()
                    if 
not t_name:
                        
t_name = f"Equipo #{i}"
                    
t_type = "Selección" if i < NUM_NAT_TEAMS else "Club"
                    
team_names.append(f"#{i} - {t_name} ({t_type})")

            if 
team_names:
                if 
hasattr(self, 'source_team_combobox'):
                    
self.source_team_combobox['values'] = team_names
                    self
.source_team_combobox.current(1 if len(team_names) > 1 else 0)
                if 
hasattr(self, 'roster_team_combobox'):
                    
self.roster_team_combobox['values'] = team_names
                    self
.roster_team_combobox.current(0)
                
                
self.on_source_team_combo_select(None)
                
self.on_roster_team_combo_select(None)
        
except Exception:
            
pass

    def on_source_team_combo_select
(self, event):
        
self.current_source_team_index = self.source_team_combobox.current()
        
self.source_tree.delete(*self.source_tree.get_children())

        
file_path, base_offset, num_slots = self.get_team_file_and_info(self.current_source_team_index)

        try:
            
with open(file_path, "rb") as f:
                
record_offset = base_offset + (self.current_source_team_index * TEAM_RECORD_SIZE)
                
f.seek(record_offset)
                
data = f.read(TEAM_RECORD_SIZE)
                
                if 
len(data) >= (num_slots * 2):
                    
player_ids = struct.unpack(f"<{num_slots}H", data[:num_slots * 2])
                    for 
idx, pid in enumerate(player_ids):
                        
with open(OVL_PATH, "rb") as f_ovl:
                            if 
0 < pid < MAX_DATABASE_PLAYERS:
                                
p_name, shirt_name = self.get_clean_player_info(f_ovl, pid)
                                
self.source_tree.insert("", tk.END, values=(pid, idx, p_name, shirt_name))
                            else:
                                
self.source_tree.insert("", tk.END, values=(pid, idx, "[ Slot Vacío / Nulo ]", ""))
        
except Exception as e:
            
messagebox.showerror("Error", f"Error al cargar plantilla origen: {e}")

    
def on_roster_team_combo_select(self, event):
        
self.current_roster_team_index = self.roster_team_combobox.current()
        
self.dest_tree.delete(*self.dest_tree.get_children())
        
current_team_pids = []

        
file_path, base_offset, num_slots = self.get_team_file_and_info(self.current_roster_team_index)

        try:
            
with open(file_path, "rb") as f:
                
record_offset = base_offset + (self.current_roster_team_index * TEAM_RECORD_SIZE)
                
f.seek(record_offset)
                
data = f.read(TEAM_RECORD_SIZE)
                
                if 
len(data) >= (num_slots * 2):
                    
player_ids = struct.unpack(f"<{num_slots}H", data[:num_slots * 2])
                    
current_team_pids = list(player_ids)
                    for 
idx, pid in enumerate(player_ids):
                        
with open(OVL_PATH, "rb") as f_ovl:
                            if 
0 < pid < MAX_DATABASE_PLAYERS:
                                
p_name, shirt_name = self.get_clean_player_info(f_ovl, pid)
                                
self.dest_tree.insert("", tk.END, values=(pid, idx, p_name, shirt_name))
                            else:
                                
self.dest_tree.insert("", tk.END, values=(pid, idx, "[ Slot Vacío / Nulo ]", ""))

            
p_att, p_def, p_ovr = self.calculate_team_power(current_team_pids)
            
self.lbl_power_att.config(text=f"ATK: {p_att}")
            
self.lbl_power_def.config(text=f"DEF: {p_def}")
            
self.lbl_power_ovr.config(text=f"OVR: {p_ovr}")
        
except Exception as e:
            
messagebox.showerror("Error", f"Error al cargar plantilla destino: {e}")

    
def transfer_player_to_slot(self):
        
selected_source = self.source_tree.selection()
        
selected_dest = self.dest_tree.selection()

        if 
not selected_source or not selected_dest:
            
messagebox.showwarning("Selección Incompleta", "Por favor selecciona un jugador origen y un slot destino.")
            return

        try:
            
# CORRECCIÓN DIRECTA: Extraemos el primer valor (ID) de la tupla de manera segura
            
new_id = int(self.source_tree.item(selected_source, "values")[0])
            
dest_index = self.dest_tree.index(selected_dest)

            
# Escribir el cambio en el archivo SLPM_622.68
            
SLPM_PATH = "SLPM_622.68"
            
TEAM_RECORD_SIZE = 46  # 23 slots * 2 bytes
            
            # Dirección base en hexadecimal para la alineación del primer equipo (Irlanda)
            
SLPM_BASE_ROSTER_OFFSET = 0x0023BF00  

            slot_offset 
= SLPM_BASE_ROSTER_OFFSET + (self.current_roster_team_index * TEAM_RECORD_SIZE) + (dest_index * 2)

            
with open(SLPM_PATH, "r+b") as f:
                
f.seek(slot_offset)
                
f.write(struct.pack("<H", new_id))
                
f.flush()
                
import os
                os
.fsync(f.fileno())

            
messagebox.showinfo("Éxito", f"¡Traspaso grabado directamente en el ejecutable {SLPM_PATH}!\nSlot {dest_index} actualizado.")
            
            
self.on_roster_team_combo_select(None)
            
self.on_source_team_combo_select(None)

        
except Exception as e:
            
# Este es el bloque que Python estaba reclamando que faltaba abajo
            
messagebox.showerror("Error de Escritura", f"No se pudo sobreescribir el ejecutable: {e}")

    
# ==========================================
    # PESTAÑA 4: KITS Y COLORES
    # ==========================================

    
def build_kits_tab(self):
        
main_frame = tk.Frame(self.tab_kits, bg="#1e1e1e")
        
main_frame.pack(fill=tk.BOTH, expand=True, padx=15, pady=15)

        
tk.Label(main_frame, text="? Gestión de Uniformes y Paleta de Colores (Estilo Waterloo Tool)", bg="#1e1e1e", fg="#00ffcc", font=("Arial", 12, "bold")).pack(anchor="w", pady=(0, 10))

        
select_frame = tk.Frame(main_frame, bg="#2d2d2d", padx=10, pady=10)
        
select_frame.pack(fill=tk.X, pady=(0, 15))

        
tk.Label(select_frame, text="Seleccionar Equipo:", bg="#2d2d2d", fg="#ffcc00", font=("Arial", 10, "bold")).pack(side=tk.LEFT, padx=(0, 10))

        
team_names_list = [f"Equipo #{i}" for i in range(NUM_TEAMS)]
        
self.kit_team_combobox = ttk.Combobox(select_frame, values=team_names_list, width=35, state="readonly")
        
self.kit_team_combobox.pack(side=tk.LEFT, padx=(0, 10))
        
self.kit_team_combobox.current(0)
        
self.kit_team_combobox.bind("<<ComboboxSelected>>", self.on_kit_team_select)

        
colors_container = tk.LabelFrame(main_frame, text=" Configuración de Kits (Player & Goalkeeper - Home / Away) ", bg="#2d2d2d", fg="#ffcc00", font=("Arial", 10, "bold"), padx=10, pady=10)
        
colors_container.pack(fill=tk.BOTH, expand=True)

        
headers = ["Kit Type", "Edit Back Number Color", "Edit Back Name Color", "Edit Short Number Color", "Base Color"]
        for 
col_idx, h_text in enumerate(headers):
            
tk.Label(colors_container, text=h_text, bg="#2d2d2d", fg="#00ffcc", font=("Arial", 9, "bold"), anchor="center").grid(row=0, column=col_idx, padx=5, pady=5, sticky="nsew")

        
kit_rows = [
            (
"Player (Home)", "p_home"),
            (
"Player (Away)", "p_away"),
            (
"Goalkeeper (Home)", "gk_home"),
            (
"Goalkeeper (Away)", "gk_away")
        ]

        
self.kit_canvases = {}

        for 
row_idx, (row_label, row_key) in enumerate(kit_rows, start=1):
            
tk.Label(colors_container, text=row_label, bg="#2d2d2d", fg="#ffffff", font=("Arial", 9, "bold"), anchor="w").grid(row=row_idx, column=0, padx=10, pady=8, sticky="w")

            
self.kit_canvases[row_key] = {}
            
props = [
                (
"back_num", "5"),
                (
"back_name", "AB"),
                (
"short_num", "5"),
                (
"base_col", "")
            ]

            for 
col_idx, (prop_key, display_text) in enumerate(props, start=1):
                
c = tk.Canvas(colors_container, width=60, height=35, bg="#1a3b5c", relief="sunken", bd=2)
                
c.grid(row=row_idx, column=col_idx, padx=15, pady=5)

                if 
display_text:
                    
c.create_text(30, 17, text=display_text, fill="#ffcc00", font=("Consolas", 10, "bold"))

                
c.bind("<Button-1>", lambda e, r=row_key, p=prop_key: self.pick_kit_color(r, p))
                
self.kit_canvases[row_key][prop_key] = c

        btn_save_kits 
= tk.Button(colors_container, text="? Guardar Cambios de Kits en SLPM", bg="#007acc", fg="#ffffff", font=("Arial", 10, "bold"), command=self.save_kit_colors)
        
btn_save_kits.grid(row=5, column=0, columnspan=5, pady=15, sticky="ew")

        
self.update_kit_team_names()

    
def update_kit_team_names(self):
        try:
            
team_names = []
            
with open(SLPM_PATH, "rb") as f_slpm:
                for 
i in range(NUM_TEAMS):
                    
f_slpm.seek(TEAM_NAMES_OFFSET + (i * TEAM_RECORD_SIZE))
                    
name_data = f_slpm.read(TEAM_NAME_LIMIT)
                    
t_name = name_data.decode('latin-1', errors='ignore').rstrip('\x00').strip()
                    if 
not t_name:
                        
t_name = f"Equipo #{i}"
                    
team_names.append(f"#{i} - {t_name}")
            if 
team_names and self.kit_team_combobox:
                
self.kit_team_combobox['values'] = team_names
        except Exception
:
            
pass

    def on_kit_team_select
(self, event):
        
selected_idx = self.kit_team_combobox.current()
        if 
selected_idx < 0:
            return

        try:
            
base_offset = 0x240000 + (selected_idx * 0x40)
            
with open(SLPM_PATH, "rb") as f:
                
f.seek(base_offset)
                
kit_keys = ["p_home", "p_away", "gk_home", "gk_away"]
                
props = ["back_num", "back_name", "short_num", "base_col"]
                
                for 
row_key in kit_keys:
                    for 
prop_key in props:
                        
raw_color_bytes = f.read(2)
                        if 
len(raw_color_bytes) == 2:
                            
raw_color = struct.unpack('<H', raw_color_bytes)[0]
                            
r = ((raw_color >> 0) & 0x1F) << 3
                            g 
= ((raw_color >> 5) & 0x1F) << 3
                            b 
= ((raw_color >> 10) & 0x1F) << 3
                            hex_color 
= f"#{r:02x}{g:02x}{b:02x}"
                            
                            
if prop_key in self.kit_canvases[row_key]:
                                
self.kit_canvases[row_key][prop_key].config(bg=hex_color)
        
except Exception:
            
pass

    def pick_kit_color
(self, kit_type, prop_type):
        
color_code = colorchooser.askcolor(title=f"Seleccionar Color ({kit_type} - {prop_type})")[1]
        if 
color_code:
            if 
prop_type in self.kit_canvases[kit_type]:
                
self.kit_canvases[kit_type][prop_type].config(bg=color_code)

    
def save_kit_colors(self):
        
selected_idx = self.kit_team_combobox.current()
        if 
selected_idx < 0:
            
messagebox.showerror("Error", "Por favor, selecciona un equipo primero.")
            return

        try:
            
base_offset = 0x240000 + (selected_idx * 0x40)
            
kit_keys = ["p_home", "p_away", "gk_home", "gk_away"]
            
props = ["back_num", "back_name", "short_num", "base_col"]

            
with open(SLPM_PATH, "r+b") as f:
                
f.seek(base_offset)
                for 
row_key in kit_keys:
                    for 
prop_key in props:
                        
hex_color = self.kit_canvases[row_key][prop_key].cget("bg")
                        
hex_color = hex_color.lstrip("#")

                        
r = int(hex_color[0:2], 16)
                        
g = int(hex_color[2:4], 16)
                        
b = int(hex_color[4:6], 16)

                        
r_5 = r >> 3
                        g_5 
= g >> 3
                        b_5 
= b >> 3
                        raw_color 
= (b_5 << 10) | (g_5 << 5) | r_5

                        f
.write(struct.pack('<H', raw_color))

            
messagebox.showinfo("Éxito", "¡Los colores de los kits se han guardado correctamente en el archivo!")
        
except Exception as e:
            
messagebox.showerror("Error", f"No se pudo guardar la configuración de kits: {str(e)}")

    
def calculate_team_power(self, player_ids):
        if 
not player_ids:
            return 
0, 0, 0

        att_scores 
= []
        
def_scores = []

        try:
            
with open(SLPM_PATH, "rb") as f_slpm:
                for 
pid in player_ids:
                    if 
0 < pid < MAX_DATABASE_PLAYERS:
                        
f_slpm.seek(SLPM_BASE_OFFSET + (pid * PLAYER_SIZE))
                        
p_data = f_slpm.read(PLAYER_SIZE)
                        if 
len(p_data) >= PLAYER_SIZE:
                            
unpacked = struct.unpack("<31B 13s", p_data)
                            
att = unpacked[2]
                            
deff = unpacked[3]
                            
att_scores.append(att)
                            
def_scores.append(deff)

            if 
not att_scores:
                return 
0, 0, 0

            att_scores
.sort(reverse=True)
            
def_scores.sort(reverse=True)

            
top_att = sum(att_scores[:11]) / max(len(att_scores[:11]), 1)
            
top_def = sum(def_scores[:11]) / max(len(def_scores[:11]), 1)

            
power_att = min(int(top_att * 1.02), 99)
            
power_def = min(int(top_def * 1.02), 99)
            
power_ovr = min(int((power_att + power_def) / 2), 99)

            return 
power_att, power_def, power_ovr
        except Exception
:
            return 
0, 0, 0

    def load_players_list
(self):
        try:
            
with open(SLPM_PATH, "rb") as f_slpm, open(OVL_PATH, "rb") as f_ovl:
                for 
i in range(NUM_PLAYERS):
                    
p_name, _ = self.get_clean_player_info(f_ovl, i)
                    
self.player_listbox.insert(tk.END, f"#{i} - {p_name}")

            if 
NUM_PLAYERS > 0:
                
self.player_listbox.selection_set(0)
                
self.on_player_select(None)
        
except FileNotFoundError:
            
pass

    def on_player_select
(self, event):
        
selection = self.player_listbox.curselection()
        if 
not selection:
            return
        
self.current_player_index = selection[0]
        
        try:
            
with open(OVL_PATH, "rb") as f_ovl:
                
p_name, shirt_name = self.get_clean_player_info(f_ovl, self.current_player_index)
                
                
self.name_entry.delete(0, tk.END)
                
self.name_entry.insert(0, p_name)

                
self.shirt_name_entry.delete(0, tk.END)
                
self.shirt_name_entry.insert(0, shirt_name)

                
f_ovl.seek(NUMBERS_BASE_OFFSET + (self.current_player_index * NUMBERS_RECORD_SIZE))
                
record_bytes = f_ovl.read(NUMBERS_RECORD_SIZE)
                
self.dorsal_entry.delete(0, tk.END)
                
self.dorsal_entry.insert(0, str(record_bytes[1] if len(record_bytes) > 1 else 0))

                
f_ovl.seek(CLUB_NUMBERS_OFFSET + (self.current_player_index * CLUB_RECORD_SIZE))
                
club_bytes = f_ovl.read(CLUB_RECORD_SIZE)
                
self.club_dorsal_entry.delete(0, tk.END)
                
self.club_dorsal_entry.insert(0, str(club_bytes[0] if len(club_bytes) > 0 else 255))

            
with open(SLPM_PATH, "rb") as f_slpm:
                
f_slpm.seek(SLPM_BASE_OFFSET + (self.current_player_index * PLAYER_SIZE))
                
unpacked = struct.unpack("<31B 13s", f_slpm.read(PLAYER_SIZE))
                for 
byte_idx, entry in self.entries.items():
                    
entry.delete(0, tk.END)
                    
entry.insert(0, str(unpacked[byte_idx]))
        
except Exception as e:
            
messagebox.showerror("Error", f"Error al leer jugador: {e}")

    
def save_player_data(self):
        try:
            
with open(OVL_PATH, "r+b") as f_ovl:
                
new_name = self.name_entry.get().strip()
                
f_ovl.seek(NAMES_BASE_OFFSET + (self.current_player_index * NAME_SIZE))
                
f_ovl.write(new_name.encode('latin-1', errors='ignore')[:NAME_SIZE].ljust(NAME_SIZE, b'\x00'))

                
new_shirt = self.shirt_name_entry.get().strip()
                
f_ovl.seek(SHIRT_NAMES_OFFSET + (self.current_player_index * SHIRT_NAME_SIZE))
                
f_ovl.write(new_shirt.encode('latin-1', errors='ignore')[:SHIRT_NAME_SIZE].ljust(SHIRT_NAME_SIZE, b'\x00'))

                
dorsal_val = int(self.dorsal_entry.get())
                
f_ovl.seek(NUMBERS_BASE_OFFSET + (self.current_player_index * NUMBERS_RECORD_SIZE))
                
rec = bytearray(f_ovl.read(NUMBERS_RECORD_SIZE))
                
rec[1] = dorsal_val
                f_ovl
.seek(NUMBERS_BASE_OFFSET + (self.current_player_index * NUMBERS_RECORD_SIZE))
                
f_ovl.write(rec)

                
club_val = int(self.club_dorsal_entry.get())
                
f_ovl.seek(CLUB_NUMBERS_OFFSET + (self.current_player_index * CLUB_RECORD_SIZE))
                
crec = bytearray(f_ovl.read(CLUB_RECORD_SIZE))
                
crec[0] = club_val
                f_ovl
.seek(CLUB_NUMBERS_OFFSET + (self.current_player_index * CLUB_RECORD_SIZE))
                
f_ovl.write(crec)

            
with open(SLPM_PATH, "rb") as f_slpm:
                
f_slpm.seek(SLPM_BASE_OFFSET + (self.current_player_index * PLAYER_SIZE))
                
p_bytes = bytearray(f_slpm.read(PLAYER_SIZE))

            for 
byte_idx, entry in self.entries.items():
                
p_bytes[byte_idx] = int(entry.get())

            
with open(SLPM_PATH, "r+b") as f_slpm:
                
f_slpm.seek(SLPM_BASE_OFFSET + (self.current_player_index * PLAYER_SIZE))
                
f_slpm.write(p_bytes)

            
self.player_listbox.delete(self.current_player_index)
            
self.player_listbox.insert(self.current_player_index, f"#{self.current_player_index} - {new_name}")
            
self.player_listbox.selection_set(self.current_player_index)

            
messagebox.showinfo("Éxito", "¡Jugador actualizado correctamente!")
        
except Exception as e:
            
messagebox.showerror("Error", f"No se pudo guardar: {e}")

    
def load_teams_list(self):
        try:
            
with open(SLPM_PATH, "rb") as f_slpm:
                for 
i in range(NUM_TEAMS):
                    
f_slpm.seek(TEAM_NAMES_OFFSET + (i * TEAM_RECORD_SIZE))
                    
data = f_slpm.read(TEAM_NAME_LIMIT)
                    
team_name = data.decode('latin-1', errors='ignore').rstrip('\x00').strip()
                    if 
not team_name:
                        
team_name = f"Equipo #{i}"
                    
self.team_listbox.insert(tk.END, f"#{i} - {team_name}")

            if 
NUM_TEAMS > 0:
                
self.team_listbox.selection_set(0)
                
self.on_team_select(None)
        
except FileNotFoundError:
            
pass

    def on_team_select
(self, event):
        
selection = self.team_listbox.curselection()
        if 
not selection:
            return
        
self.current_team_index = selection[0]

        try:
            
with open(SLPM_PATH, "rb") as f_slpm:
                
f_slpm.seek(TEAM_NAMES_OFFSET + (self.current_team_index * TEAM_RECORD_SIZE))
                
data = f_slpm.read(TEAM_NAME_LIMIT)
                
team_name = data.decode('latin-1', errors='ignore').rstrip('\x00').strip()

                
self.team_name_entry.delete(0, tk.END)
                
self.team_name_entry.insert(0, team_name)
        
except Exception as e:
            
messagebox.showerror("Error", f"Error al leer equipo: {e}")

    
def save_team_data(self):
        try:
            
new_team_name = self.team_name_entry.get().strip()
            
name_bytes = new_team_name.encode('latin-1', errors='ignore')[:TEAM_NAME_LIMIT].ljust(TEAM_NAME_LIMIT, b'\x00')

            
with open(SLPM_PATH, "r+b") as f_slpm:
                
record_offset = TEAM_NAMES_OFFSET + (self.current_team_index * TEAM_RECORD_SIZE)
                
f_slpm.seek(record_offset)
                
full_record = bytearray(f_slpm.read(TEAM_RECORD_SIZE))

                
full_record[:TEAM_NAME_LIMIT] = name_bytes

                f_slpm
.seek(record_offset)
                
f_slpm.write(full_record)

            
self.team_listbox.delete(self.current_team_index)
            
self.team_listbox.insert(self.current_team_index, f"#{self.current_team_index} - {new_team_name}")
            
self.team_listbox.selection_set(self.current_team_index)

            
messagebox.showinfo("Éxito", f"¡Equipo #{self.current_team_index} actualizado a '{new_team_name}' en el SLPM!")
        
except Exception as e:
            
messagebox.showerror("Error", f"No se pudo guardar el equipo: {e}")

if 
__name__ == "__main__":
    
root = tk.Tk()
    
app = WE6FE_EditorApp(root)
    
root.mainloop() 


Enjoy Cool
Responder


Mensajes en este tema
WE6FE Database Editor - por we11world - 10-25-2026, 03:18 PM
RE: WE6FE Database Editor - por Pato_Lucas18 - 10-26-2026, 04:18 AM
RE: WE6FE Database Editor - por Ermolli91 - 10-26-2026, 01:55 PM
RE: WE6FE Database Editor - por we11world - 10-26-2026, 08:07 PM
RE: WE6FE Database Editor - por we11world - 10-28-2026, 02:01 AM
RE: WE6FE Database Editor - por Pato_Lucas18 - 10-28-2026, 02:36 AM
RE: WE6FE Database Editor - por Ermolli91 - 10-28-2026, 08:22 AM
RE: WE6FE Database Editor - por we11world - Hoy, 02:20 AM

Posibles temas similares…
Tema Autor Respuestas Vistas Último mensaje
Bug WE6FE AFS MAP List we11world 3 71 Hace 32 minutos
Último mensaje: Pato_Lucas18
  Base de Datos Pes Editor Libertadores y Selecciones ocultas Chentrolldroiid 0 616 03-14-2026, 05:19 PM
Último mensaje: Chentrolldroiid
  [PSX]  Como editar a database do PES de PS2? Maurício Silva 2 1,178 03-12-2026, 04:07 AM
Último mensaje: we11world
  [PS2] Pes Editor 7.0 PSD (Pes Stats Database) Fabvitor 15 21,566 01-04-2024, 07:41 PM
Último mensaje: Albatros
  [Duda] Edición de database de PES6 Ps2 padshance 0 1,830 05-01-2022, 05:39 PM
Último mensaje: padshance

Salto de foro:


Usuarios navegando en este tema: 1 invitado(s)