Crear Mapeo AFS MAP Simple con Python

 Crear Mapeo AFS MAP Simple con Python


Para crear el mapa del 0_sound.afs, el proceso generalmente se divide en estas etapas principales:


1. Delimitación de rangos de IDs: Separar el contenedor por bloques según la función del audio (BGM/Música de fondo, Efectos de sonido/SE, Cánticos de hinchada, Megafonía y Comentarios).


2. Análisis de cabeceras ADX: Identificar sample rate (24 kHz, 32 kHz, 44.1 kHz), canales (mono/estéreo) y puntos de loop (Loop Start / Loop End) para evitar distorsiones o ruidos al reinsertar.


3. Audición y catalogación: Probar la extracción/reproducción mediante DKZ Studio o MFAudio para asignar un nombre descriptivo a cada ID sin asignar (unk_XXXXX.adx).


4. Estructuración de la tabla del mapa: Registrar ID, dirección/offset dentro del AFS, tamaño en bytes, frecuencia de muestreo y descripción del contenido.

Archivo por archivo usando métricas genéricas (canales, sample rate y tamaño de archivo), por lo que asigna la misma etiqueta "Cánticos / Ambiente Estadio" a todo el bloque sin entender la estructura interna de los equipos.

Código:


import os
import struct

# Mapeo opcional de asignación de slots conocidos (puedes ampliarlo según vayas testeando los IDs en el juego)
# Formato: SLOT_INDEX: "Nombre del Equipo / Selección"
SLOT_NAMES = {
    0: "Genérico / Ligas",
    1: "Japón",
    # Agrega más slots conocidos según tu base de datos:
    # 2: "Corea del Sur",
    # 3: "Argentina",
}

def analyze_adx(file_path):
    """Analiza la cabecera del archivo Criware ADX."""
    try:
        with open(file_path, "rb") as f:
            header = f.read(32)
            if len(header) < 18 or header[:2] != b'\x80\x00':
                return "Desconocido / No ADX", 0, 0
           
            channels = header[7]
            sample_rate = struct.unpack(">I", header[8:12])[0]
            file_size = os.path.getsize(file_path)
           
            if channels == 2 and sample_rate >= 32000 and file_size > 800000:
                tipo = "BGM - Música / Fondo"
            elif channels == 2 and file_size > 150000:
                tipo = "Cánticos / Ambiente Estadio"
            elif channels == 1:
                tipo = "Megafonía / Callname / FX Mono"
            else:
                tipo = "Efecto / SFX General"
               
            return tipo, channels, sample_rate
    except Exception:
        return "Error al analizar", 0, 0

def get_chant_info(file_id):
    """
    Agrupa el rango 00000 a 00702 en slots fijos de 5 cánticos por equipo.
    """
    if 0 <= file_id <= 702:
        slot_index = file_id // 5
        chant_num = (file_id % 5) + 1
        team_name = SLOT_NAMES.get(slot_index, f"Equipo/Slot {slot_index}")
        return f"Cánticos - {team_name} (Audio {chant_num}/5)"
    return None

def main():
    print("==================================================")
    print("   Generador de AFS MAP Estructurado (WE10 JL)   ")
    print("==================================================")
   
    folder_path = input("\nIngresa la ruta de la carpeta con los .adx: ").strip('"')
   
    if not os.path.exists(folder_path):
        print("\n[ERROR] La carpeta especificada no existe.")
        input("\nPresiona Enter para salir...")
        return

    output_map = "WE10JL_0_SOUND.map"
    files = [f for f in os.listdir(folder_path) if f.lower().endswith(".adx")]
   
    def get_id(fname):
        name = os.path.splitext(fname)[0]
        parts = name.split('_')
        if len(parts) > 1 and parts[-1].isdigit():
            return int(parts[-1])
        return 999999

    files.sort(key=get_id)

    print(f"\nProcesando {len(files)} archivos con asignación de slots...")

    with open(output_map, "w", encoding="utf-8") as map_file:
        for filename in files:
            file_id = get_id(filename)
            full_path = os.path.join(folder_path, filename)
           
            tipo, channels, rate = analyze_adx(full_path)
           
            # Verificamos si cae dentro del rango de cánticos estructurados (0 a 702)
            chant_detail = get_chant_info(file_id)
            if chant_detail:
                descripcion = f"[{chant_detail}] ({channels}Ch, {rate}Hz)"
            else:
                descripcion = f"[{tipo}] ({channels}Ch, {rate}Hz)"
           
            linea = f"{file_id:05d};{filename};{descripcion}\n"
            map_file.write(linea)

    print(f"\n[¡ÉXITO!] Se ha generado '{output_map}' organizado por slots de 5 archivos.")
    input("\nPresiona Enter para cerrar...")

if __name__ == "__main__":
    main()


El folder de trabajo debe ser algo asi:



 


Crear Mapeo AFS MAP por Bloques de Selección / Club
Es la recomendada porque da mas detalles del AFS MAP que se genera


En WE10JL, los cánticos de estadio no están sueltos; están organizados en slots fijos de 5 archivos ADX por equipo (o bloques de 4/8 según la sección).

el script necesita agrupar los archivos en slots numéricos. En el bloque de cánticos de WE10 / WE10JL, el estándar es usar slots fijos de 5 ADX por equipo (del 00000 al 00004 es el Slot 0, del 00005 al 00009 el Slot 1, y así sucesivamente).

Codigo:


import os
import struct

# Mapeo opcional de asignación de slots conocidos (puedes ampliarlo según vayas testeando los IDs en el juego)
# Formato: SLOT_INDEX: "Nombre del Equipo / Selección"
SLOT_NAMES = {
    0: "Genérico / Ligas",
    1: "Japón",
    # Agrega más slots conocidos según tu base de datos:
    # 2: "Corea del Sur",
    # 3: "Argentina",
}

def analyze_adx(file_path):
    """Analiza la cabecera del archivo Criware ADX."""
    try:
        with open(file_path, "rb") as f:
            header = f.read(32)
            if len(header) < 18 or header[:2] != b'\x80\x00':
                return "Desconocido / No ADX", 0, 0
           
            channels = header[7]
            sample_rate = struct.unpack(">I", header[8:12])[0]
            file_size = os.path.getsize(file_path)
           
            if channels == 2 and sample_rate >= 32000 and file_size > 800000:
                tipo = "BGM - Música / Fondo"
            elif channels == 2 and file_size > 150000:
                tipo = "Cánticos / Ambiente Estadio"
            elif channels == 1:
                tipo = "Megafonía / Callname / FX Mono"
            else:
                tipo = "Efecto / SFX General"
               
            return tipo, channels, sample_rate
    except Exception:
        return "Error al analizar", 0, 0

def get_chant_info(file_id):
    """
    Agrupa el rango 00000 a 00702 en slots fijos de 5 cánticos por equipo.
    """
    if 0 <= file_id <= 702:
        slot_index = file_id // 5
        chant_num = (file_id % 5) + 1
        team_name = SLOT_NAMES.get(slot_index, f"Equipo/Slot {slot_index}")
        return f"Cánticos - {team_name} (Audio {chant_num}/5)"
    return None

def main():
    print("==================================================")
    print("   Generador de AFS MAP Estructurado (WE10 JL)   ")
    print("==================================================")
   
    folder_path = input("\nIngresa la ruta de la carpeta con los .adx: ").strip('"')
   
    if not os.path.exists(folder_path):
        print("\n[ERROR] La carpeta especificada no existe.")
        input("\nPresiona Enter para salir...")
        return

    output_map = "WE10JL_0_SOUND.map"
    files = [f for f in os.listdir(folder_path) if f.lower().endswith(".adx")]
   
    def get_id(fname):
        name = os.path.splitext(fname)[0]
        parts = name.split('_')
        if len(parts) > 1 and parts[-1].isdigit():
            return int(parts[-1])
        return 999999

    files.sort(key=get_id)

    print(f"\nProcesando {len(files)} archivos con asignación de slots...")

    with open(output_map, "w", encoding="utf-8") as map_file:
        for filename in files:
            file_id = get_id(filename)
            full_path = os.path.join(folder_path, filename)
           
            tipo, channels, rate = analyze_adx(full_path)
           
            # Verificamos si cae dentro del rango de cánticos estructurados (0 a 702)
            chant_detail = get_chant_info(file_id)
            if chant_detail:
                descripcion = f"[{chant_detail}] ({channels}Ch, {rate}Hz)"
            else:
                descripcion = f"[{tipo}] ({channels}Ch, {rate}Hz)"
           
            linea = f"{file_id:05d};{filename};{descripcion}\n"
            map_file.write(linea)

    print(f"\n[¡ÉXITO!] Se ha generado '{output_map}' organizado por slots de 5 archivos.")
    input("\nPresiona Enter para cerrar...")

if __name__ == "__main__":
    main()





Para ejecutar el Codigo:
python generar_map.py


O Si deseas puedes crear un .bat para llamar el script de forma simple:

@echo off
title Generador AFS MAP WE10JL
color 0A
echo Buscando instalacion de Python...
python --version >nul 2>&1
if %errorlevel% neq 0 (
    echo.
    echo [!] Python no esta instalado en tu sistema.
    echo Por favor instala Python 3 desde la Microsoft Store o python.org
    echo para ejecutar este script con un doble clic.
    echo.
    pause
    exit
)

python generar_map.py
pause



Cuando se inicia el Script, debe de dar la ubicacion donde estan los archivos


Después le saldrá una confirmación:

El Resultado seria un AFS MAP como este:















Comentarios

Entradas populares de este blog

Tutorial: Cómo editar Scoreboards en WE10JL (SLPM_665.95)

Tutorial: WE10JL (SLPM_665.95) - Relación de KITS

Tutorial: WE10JL Estilo de Camara (SLPM_665.95)