Base de Datos WE6FE Database Editor
#1
Lightbulb 
WE6FE Database Editor

Inicio esta Documentacion de Offsets Database para el Juego Winning Eleven 6 Final Evolution (WE6FE).
La idea con este proyecto es ir desglosando la Database del juego, aprender un poco mas de el, y crear un Database Editor en Python!

Asi, podre avanzar en los otros juegos a los cuales deseo crear editores modernos como:

- PES 4 - WE8
- PES 3 - WE7

... y todos esos juegos Retro antes de PES 6 que estan Olvidados.  Cool

Vamos a ver en que termina esta aventura.... 

[Documentación Técnica]
Mapeo de Atributos de Jugadores en WE6FE (PS2)

Juego: Winning Eleven 6 Final Evolution (PS2)

Archivo Objetivo: SLPM_622.68

Herramientas Utilitarias:
- Hex Workshop v4.23 (32-bit),
- Python 3.x

1. Mapeo del Offset y Tamaño de Estructura

Offset Base: 0x1F24B0

Tamaño por Jugador: 44 bytes (0x2C bytes)

Byte Order: Little Endian (Intel)

--------------------------------

Estructura Interna del Bloque (44 bytes):

Bytes 00 - 01: ID Desconocido / Control interno

Byte 02: Ataque (Attack)

Byte 03: Defensa (Defence)

Byte 04: Balance (Body Balance)

Byte 05: Resistencia (Stamina)

Byte 06: Velocidad Máxima (Top Speed)

Byte 07: Aceleración (Acceleration)

Byte 08: Respuesta (Response)

Byte 09: Agilidad (Agility)

Byte 10: Precisión de Drible (Dribble Accuracy)

Byte 11: Velocidad de Drible (Dribble Speed)

Byte 12: Precisión Pase Corto (Short Pass Accuracy)

Byte 13: Velocidad Pase Corto (Short Pass Speed)

Byte 14: Precisión Pase Largo (Long Pass Accuracy)

Byte 15: Velocidad Pase Largo (Long Pass Speed)

Byte 16: Precisión de Tiro (Shot Accuracy)

Byte 17: Potencia de Tiro (Shot Power)

Byte 18: Técnica de Tiro (Shot Technique)

Byte 19: Saque de Falta / Tiro Libre (Free Kick Accuracy)

Byte 20: Efecto / Curva (Curving)

Byte 21: Cabezazo (Heading)

Byte 22: Salto (Jump)

Byte 23: Técnica (Technique)

Byte 24: Agresividad (Aggression)

Byte 25: Mentalidad (Mentality)

Byte 26: Habilidad de Arquero (GK Skills)

Byte 27: Trabajo en Equipo (Teamwork)

Byte 28: Condición / Regularidad (Condition / Stability)

Byte 29: Frecuencia Pie Malo (Weak Foot Frequency)

Byte 30: Precisión Pie Malo (Weak Foot Accuracy)

Bytes 30 - 43: Espacio reservado / Atributos especiales (14 bytes)



2. Script Lector en Python (we6fe_reader.py)
Script funcional en Python para lectura binaria automática directamente desde el ejecutable SLPM_622.68.

Codigo:
 
Código:
import struct FILE_PATH = "SLPM_622.68" BASE_OFFSET = 0x1F24B0 PLAYER_SIZE = 44 NUM_PLAYERS = 10 def leer_base_datos_jugadores(): print(f"Leyendo base de datos de Winning Eleven 6 FE...") print(f"Archivo: {FILE_PATH} | Offset Base: {hex(BASE_OFFSET)}\n") try: with open(FILE_PATH, "rb") as f: f.seek(BASE_OFFSET) for i in range(NUM_PLAYERS): player_bytes = f.read(PLAYER_SIZE) if len(player_bytes) < PLAYER_SIZE: break unpacked = struct.unpack("<30B 14s", player_bytes) jugador = { "ataque": unpacked[2], "defensa": unpacked[3], "balance": unpacked[4], "stamina": unpacked[5], "top_speed": unpacked[6], "aceleracion": unpacked[7] } current_offset = BASE_OFFSET + (i * PLAYER_SIZE) print(f"--- Jugador #{i} [Offset: {hex(current_offset)}] ---") print(f" Ataque: {jugador['ataque']} | Defensa: {jugador['defensa']} | Balance: {jugador['balance']}") print(f" Stamina: {jugador['stamina']} | Velocidad: {jugador['top_speed']} | Aceleración: {jugador['aceleracion']}") print("-" * 55) except FileNotFoundError: print(f"Error: No se encontró el archivo '{FILE_PATH}'.") if __name__ == "__main__": leer_base_datos_jugadores()



Resumen de estudio de la Database WE6FE:

Atributo de Jugadores:
[Imagen: WE6FE-Database-TEST.png]


Database Completa:
[Imagen: WE6FE-Database-TEST3.png]

Atributo de Jugadores:
[Imagen: WE6FE-Database-TEST4.png]

Programa: Lector de Python
[Imagen: WE6FE-Database-TEST5-Programa-lector.png]



Espero continuar con este desarrollo...

Enjoy!
Responder
#2
Buenisimo amigo
Responder
#3
Muy bien con esos avances. Suerte con el proyecto, especialmente porque PES 6 es el más popular de esa época pero los que mencionaste también fueron grandes juegos. Ánimo.
Responder
#4
(10-26-2026, 04:18 AM)Pato_Lucas18 escribió: Buenisimo amigo
 
Excelente Pato, muchas gracias por toda tu ayuda! Esta semana voy a mirar esos codigos que me envio, lo pude ojear por encima, es ORO puro, me va servir mucho! Muchas Gracias Master!
(10-26-2026, 01:55 PM)Ermolli91 escribió: Muy bien con esos avances. Suerte con el proyecto, especialmente porque PES 6 es el más popular de esa época pero los que mencionaste también fueron grandes juegos. Ánimo.

Excelente amigo, muchas gracias por la motivacion y apoyo! pienso que si dejamos la informacion en este foro, en el futuro servira para desarrollo de nuevos parches. yo estoy avanzando lo que mas puedo en la noche, que es donde tengo un poco de tiempo libre, estudio el SLPM. haciendo Ingeniería Inversa y Estructuras de Datos!

Mapeo de archivos ejecutables y de configuración de PlayStation 2, como el SLPM_622.68 y parameter.ovl

Adjunto video preview: del Mapeo


Tambien se actualzo el Codigo en Python, por si lo desean testear:
 
Código PHP:
import struct
import tkinter 
as tk
from tkinter import messagebox
, ttk

# --- RUTAS Y OFFSETS ---
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
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 
= 50

# Offsets de Equipos y Plantillas (Player Links)
TEAM_NAMES_OFFSET = 0x23F8C8
TEAM_RECORD_SIZE 
= 32
TEAM_NAME_LIMIT 
= 16
NUM_TEAMS 
= 64

LINKS_OFFSET 
= 0x353B0  # Offset base de Player Links de Clubes

# Lista de los 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 - By WE11WORLD & Gemini")
        
self.root.geometry("1100x820")
        
self.root.config(bg="#1e1e1e")

        
self.current_player_index = 0
        self
.current_team_index = 0
        self
.current_roster_team_index = 0
        self
.current_selected_slot = 0
        self
.entries = {}
        
self.team_name_entry = None
        self
.team_listbox = None
        self
.roster_team_listbox = None
        self
.roster_slots_listbox = None
        self
.transfer_id_entry = None

        
# --- ESTILO DE PESTAÑAS (NOTEBOOK) ---
        
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')])

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

        
# Crear Pestañas
        
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.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 ")

        
# Construir Interfaces de las Pestañas
        
self.build_players_tab()
        
self.build_teams_tab()
        
self.build_rosters_tab()

    
# ==========================================
    # 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 (PLAYER LINKS)
    # ==========================================
    
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)

        
# Panel Izquierdo: Selección de Equipo
        
left_frame = tk.Frame(main_frame, bg="#2d2d2d", width=300)
        
left_frame.pack(side=tk.LEFT, fill=tk.Y, padx=(0, 10))

        
tk.Label(left_frame, text="Seleccionar Equipo", 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.roster_team_listbox = tk.Listbox(left_frame, yscrollcommand=list_scroll.set, bg="#1e1e1e", fg="#00ffcc", selectbackground="#007acc", font=("Consolas", 10), height=36)
        
self.roster_team_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        
self.roster_team_listbox.bind('<<ListboxSelect>>', self.on_roster_team_select)
        
list_scroll.config(command=self.roster_team_listbox.yview)

        
# Panel Derecho: Slots de Plantilla y Controles de Traspaso
        
right_frame = tk.Frame(main_frame, bg="#2d2d2d")
        
right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True)

        
tk.Label(right_frame, text="Plantilla de 16 Slots (Selecciona un Slot para Traspasar)", bg="#2d2d2d", fg="#ffcc00", font=("Arial", 11, "bold")).pack(anchor="w", padx=15, pady=10)

        
roster_scroll = tk.Scrollbar(right_frame)
        
roster_scroll.pack(side=tk.RIGHT, fill=tk.Y)

        
self.roster_slots_listbox = tk.Listbox(right_frame, yscrollcommand=roster_scroll.set, bg="#1e1e1e", fg="#ffffff", selectbackground="#007acc", font=("Consolas", 10), height=25)
        
self.roster_slots_listbox.pack(side=tk.TOP, fill=tk.BOTH, expand=True, padx=15, pady=(0, 10))
        
self.roster_slots_listbox.bind('<<ListboxSelect>>', self.on_slot_select)
        
roster_scroll.config(command=self.roster_slots_listbox.yview)

        
# Panel de Edición / Traspaso inferior
        
transfer_frame = tk.Frame(right_frame, bg="#363636", relief=tk.GROOVE, bd=2)
        
transfer_frame.pack(fill=tk.X, padx=15, pady=(0, 15), ipady=10)

        
tk.Label(transfer_frame, text="Asignar / Traspasar Jugador al Slot Seleccionado:", bg="#363636", fg="#00ffcc", font=("Arial", 10, "bold")).pack(anchor="w", padx=15, pady=(5, 5))

        
inputs_subframe = tk.Frame(transfer_frame, bg="#363636")
        
inputs_subframe.pack(anchor="w", padx=15, pady=5)

        
tk.Label(inputs_subframe, text="Nuevo ID de Jugador:", bg="#363636", fg="#ffffff", font=("Arial", 9)).pack(side=tk.LEFT, padx=(0, 5))
        
self.transfer_id_entry = tk.Entry(inputs_subframe, bg="#2d2d2d", fg="#ffffff", insertbackground="white", font=("Consolas", 10, "bold"), width=8)
        
self.transfer_id_entry.pack(side=tk.LEFT, padx=(0, 15))

        
btn_transfer = tk.Button(inputs_subframe, text="? Guardar Traspaso en OVL", bg="#007acc", fg="#ffffff", font=("Arial", 9, "bold"), command=self.save_player_transfer)
        
btn_transfer.pack(side=tk.LEFT)

        
self.load_roster_teams_list()

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

            if 
NUM_TEAMS > 0:
                
self.roster_team_listbox.selection_set(0)
                
self.on_roster_team_select(None)
        
except FileNotFoundError:
            
pass

    def on_roster_team_select
(self, event):
        
selection = self.roster_team_listbox.curselection()
        if 
not selection:
            return
        
self.current_roster_team_index = selection[0]
        
self.roster_slots_listbox.delete(0, tk.END)

        try:
            
with open(OVL_PATH, "rb") as f_ovl:
                
record_offset = LINKS_OFFSET + (self.current_roster_team_index * TEAM_RECORD_SIZE)
                
f_ovl.seek(record_offset)
                
data = f_ovl.read(TEAM_RECORD_SIZE)
                
                if 
len(data) >= 32:
                    
player_ids = struct.unpack("<16H", data[:32])
                    for 
idx, pid in enumerate(player_ids):
                        if 
0 < pid < 2500:
                            
pos = NAMES_BASE_OFFSET + (pid * NAME_SIZE)
                            
f_ovl.seek(pos)
                            
raw_name = f_ovl.read(NAME_SIZE)
                            
nombre = raw_name.decode('latin-1', errors='ignore').rstrip('\x00').strip()
                            
                            if 
nombre and any(c.isalnum() for c in nombre):
                                
self.roster_slots_listbox.insert(tk.END, f"Slot {idx:2d}  -->  ID: {pid:4d}  ({nombre})")
                            else:
                                
self.roster_slots_listbox.insert(tk.END, f"Slot {idx:2d}  -->  ID: {pid:4d}  [ Oculto / Sin Mapear ]")
                        else:
                            
self.roster_slots_listbox.insert(tk.END, f"Slot {idx:2d}  -->  ID:    0  [ Slot Vacío / Sin Asignar ]")
            
            if 
self.roster_slots_listbox.size() > 0:
                
self.roster_slots_listbox.selection_set(0)
                
self.on_slot_select(None)
        
except Exception as e:
            
messagebox.showerror("Error", f"Error al leer plantilla: {e}")

    
def on_slot_select(self, event):
        
selection = self.roster_slots_listbox.curselection()
        if 
not selection:
            return
        
self.current_selected_slot = selection[0]
        
        
text_line = self.roster_slots_listbox.get(self.current_selected_slot)
        try:
            
id_part = text_line.split("ID:")[1].strip().split()[0]
            
self.transfer_id_entry.delete(0, tk.END)
            
self.transfer_id_entry.insert(0, id_part)
        
except Exception:
            
pass

    def save_player_transfer
(self):
        try:
            
new_id = int(self.transfer_id_entry.get().strip())
            if 
new_id < 0 or new_id > 5000:
                
messagebox.showerror("Error", "El ID de jugador debe estar entre 0 y 5000.")
                return

            
slot_offset = LINKS_OFFSET + (self.current_roster_team_index * TEAM_RECORD_SIZE) + (self.current_selected_slot * 2)

            
with open(OVL_PATH, "r+b") as f_ovl:
                
f_ovl.seek(slot_offset)
                
f_ovl.write(struct.pack("<H", new_id))

            
messagebox.showinfo("Éxito", f"¡Traspaso guardado! Slot {self.current_selected_slot} actualizado con ID {new_id}.")
            
self.on_roster_team_select(None)
            
self.roster_slots_listbox.selection_set(self.current_selected_slot)
        
except Exception as e:
            
messagebox.showerror("Error", f"No se pudo guardar el traspaso: {e}")

    
# ==========================================
    # LOGICA DE DATOS: JUGADORES
    # ==========================================
    
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):
                    
f_slpm.seek(SLPM_BASE_OFFSET + (i * PLAYER_SIZE))
                    
data_slpm = f_slpm.read(PLAYER_SIZE)
                    if 
len(data_slpm) < PLAYER_SIZE:
                        break

                    
f_ovl.seek(NAMES_BASE_OFFSET + (i * NAME_SIZE))
                    
raw_name = f_ovl.read(NAME_SIZE)
                    
player_name = raw_name.decode('latin-1', errors='ignore').rstrip('\x00').strip()
                    if 
not player_name:
                        
player_name = f"Jugador #{i}"

                    
self.player_listbox.insert(tk.END, f"#{i} - {player_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:
                
f_ovl.seek(NAMES_BASE_OFFSET + (self.current_player_index * NAME_SIZE))
                
raw_name = f_ovl.read(NAME_SIZE)
                
self.name_entry.delete(0, tk.END)
                
self.name_entry.insert(0, raw_name.decode('latin-1', errors='ignore').rstrip('\x00').strip())

                
f_ovl.seek(SHIRT_NAMES_OFFSET + (self.current_player_index * SHIRT_NAME_SIZE))
                
raw_shirt = f_ovl.read(SHIRT_NAME_SIZE)
                
self.shirt_name_entry.delete(0, tk.END)
                
self.shirt_name_entry.insert(0, raw_shirt.decode('latin-1', errors='ignore').rstrip('\x00').strip())

                
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}")

    
# ==========================================
    # LOGICA DE DATOS: EQUIPOS
    # ==========================================
    
def load_teams_list(self):
        try:
            
with open(SLPM_PATH, "rb") as f_slpm:
                
f_slpm.seek(TEAM_NAMES_OFFSET)
                for 
i in range(NUM_TEAMS):
                    
data = f_slpm.read(TEAM_RECORD_SIZE)
                    if 
len(data) < TEAM_RECORD_SIZE:
                        break
                    
team_name = data[:TEAM_NAME_LIMIT].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_RECORD_SIZE)
                
team_name = data[:TEAM_NAME_LIMIT].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
#5
Actualizo la información:
Gracias a la Data que me paso Mr.Pato Heart  me ha servido mucho, ahora además de la Database se podrán implementar ediciones como la edición de Kits: sus colores, y se podrán implementar mas estructuras de edición, es una data muy completa la que me paso Mr. Pato, Muchas Gracias Parcero, ha sido una data increíble!

Resumen de lo estudiado hoy:

A. Estructuras de Colores y Paletas (seban_color_data / master_uni_color_table)
Funciones como mdsl_Playerset_model toman los datos directamente de bloques de memoria como seban_color_data y aplican filtros (mdsl_MS_Filter).

Editor Python: Necesitamos definir estructuras equivalentes (con struct o lectura directa por offsets binarios) que nos permitan leer los valores RGB/indexados de los colores de las camisetas, pantalonetas y medias para mostrarlos en la interfaz gráfica.

B. Definición y Asignación de Uniformes (tmsl_UniformDefine)
Esta función maneja el estado de carga de los kits (cGpffffa1a4), configurando los equipos de casa y fuera (home / away), reseteando los modelos 3D temporales y aplicando los colores mediante EditUniformNumberColorSet.

Editor Python: Podemos replicar esta máquina de estados o un flujo simplificado que permita al usuario seleccionar un equipo, ver los datos del kit cargado en la ISO/archivo y modificar los parámetros de los uniformes en tiempo real.

C. Validación de Uniformes (mdsl_uniform_present)
El motor ejecuta comprobaciones cruzadas (mdsl_uniform_color_check y mdsl_uniform_recheck) para evitar que dos equipos salgan al campo con colores que se crucen o confundan.

Implementar una función de validación visual y lógica que advierta al modder si los colores configurados para local y visitante entran en conflicto.

Hasta el momento los test con la data de Mr. Pato ha sido una ayuda de ORO toda esa data, me ha servido para mappear mejor los datos, además de encontrar Estructuras de Colores que se asignan a los uniformes, excelente!

Asi va quedando:
1. Ahora las Plantillas y Traspasos funcionan correctamente
2. Listado de Teams con la lectura correcta y Organizada
3. Gestión de Kits y Colores ahora podemos configurar los kits, Home, Away, los Arqueros y quedan con el Color que uno les asigne sin generar errores.

Adjunto imagenes preview:
[Imagen: WE6FE-DATABASE-EDITOR-1.png]

Este es el resultado de los Kits y su configuracion, con la data nueva de Mr. Pato se creo todo esto

[Imagen: WE6FE-DATABASE-EDITOR-2.png]

Bueno... por ahora debo descansar un poco.
Voy a estudiar mas el codigo esta semana y ver que mas desarrollos pueda estudiar, tambien voy a compartir todo el codigo, por si lo desean estudiar.

Video Preview:
Yo no soy diseñador, por eso la tool se ve toda RETRO... hasta mejor asi! jaj



deseo dejar el registro de desarrollo, por si alguien en el futuro necesita toda esa data!

Eso es todo por ahora!   Shy
Responder
#6
Buenismo amigo
Responder
#7
Gran avance, se esta dando bastante rápido. Bien ahí.
Responder
#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


Posibles temas similares…
Tema Autor Respuestas Vistas Último mensaje
Bug WE6FE AFS MAP List we11world 3 82 Hace 2 horas
Ú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,568 01-04-2024, 07:41 PM
Último mensaje: Albatros
  [Duda] Edición de database de PES6 Ps2 padshance 0 1,831 05-01-2022, 05:39 PM
Último mensaje: padshance

Salto de foro:


Usuarios navegando en este tema: 1 invitado(s)