Files
moon-well-ui/tools/foreverui/db2.py
T
Adrestie 1de02d13a0 Ecrans d'accueil a la DA de camelot : connexion, menu, selection, dialogues et creation de personnage
- GlueXML ForeverUI (data/glue) : cadre 2:1, polices, atlas, gabarits ;
  connexion, menu, selection des personnages, dialogues, creation en deux
  etapes (races et classes ; personnalisation numerotee, listes ouvrantes,
  echantillons de couleur, apercu differe, nom en bas de l'ecran)
- choix valides calcules sur CharSections aux regles du moteur
  (tools/choix_personnalisation.py) ; couleurs des echantillons lues dans
  les textures du client (tools/couleurs_personnalisation.py), peau teintee
  par la lumiere du decor
- decors de la creation et de la selection reduits a 0,912 pour compenser
  le cadre 2:1 (tools/decors_accueil.py) ; deployer.py suit aussi
  Interface\Glues\Models
- patcheur optionnel de Wow.exe (tools/patcheur, source et executable) :
  CycleCharCustomization rend l'indice du reglage
- art des ecrans d'accueil (data/art), outils de decoupe, d'atlas et de
  donnees
- barre du bas : boutons JcJ et Aide retires, bandeau a la largeur de ses
  boutons

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 23:44:01 +02:00

124 lines
5.0 KiB
Python

# -*- coding: utf-8 -*-
# This file is part of mod-forever-ui.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
"""Lecteur minimal des tables .db2 du client moderne (format WDC5).
Rend, pour chaque ligne, son identifiant et la liste brute de ses champs ;
un champ texte rend sa chaine. Formats de stockage geres : brut, compacte,
compacte signe, donnee commune, palette, palette en tableau. Les tables
chiffrees ou eparses (drapeau 1) ne sont pas gerees.
"""
import io
import struct
def _bits(donnees, debut_octets, decalage_bits, nombre_bits):
octet = debut_octets + decalage_bits // 8
n = (decalage_bits % 8 + nombre_bits + 7) // 8
brut = int.from_bytes(donnees[octet:octet + n], "little")
return (brut >> (decalage_bits % 8)) & ((1 << nombre_bits) - 1)
def lire(chemin, champs_texte=()):
d = io.open(chemin, "rb").read()
if d[:4] != b"WDC5":
raise ValueError("pas un WDC5 : %s" % chemin)
o = 4 + 4 + 128
(nb, nb_champs, taille_ligne, taille_textes, _, _, _, _, _) = struct.unpack_from("<9I", d, o)
o += 36
drapeaux, index_id = struct.unpack_from("<HH", d, o)
o += 4
(total_champs, _, _, taille_stockage, taille_communs, taille_palette, nb_sections) = struct.unpack_from("<7I", d, o)
o += 28
if drapeaux & 1:
raise ValueError("table eparse non geree")
sections = []
for _ in range(nb_sections):
sections.append(struct.unpack_from("<QIIIIIIII", d, o))
o += 40
o += 4 * nb_champs
stockage = [struct.unpack_from("<HHIIIII", d, o + 24 * i) for i in range(total_champs)]
o += taille_stockage
palette = d[o:o + taille_palette]
o += taille_palette
communs_brut = d[o:o + taille_communs]
o += taille_communs
# decoupe palette et donnees communes champ par champ
pal, com = [], []
p = c = 0
for (_, _, extra, genre, v1, v2, v3) in stockage:
if genre in (3, 4):
pal.append(p)
p += extra
else:
pal.append(None)
if genre == 2:
table = {}
for k in range(extra // 8):
ident, val = struct.unpack_from("<II", communs_brut, c + 8 * k)
table[ident] = val
com.append(table)
c += extra
else:
com.append(None)
lignes = []
for (_, debut, n, _, _, taille_ids, _, _, nb_copies) in sections:
ids = []
fin_lignes = debut + n * taille_ligne
fin_textes = fin_lignes + taille_textes
if taille_ids:
ids = list(struct.unpack_from("<%dI" % (taille_ids // 4), d, fin_textes))
copies_debut = fin_textes + taille_ids
for i in range(n):
base = debut + i * taille_ligne
valeurs = []
for k, (bits, nbits, extra, genre, v1, v2, v3) in enumerate(stockage):
if genre == 0:
octet = bits // 8
v = int.from_bytes(d[base + octet: base + octet + nbits // 8], "little")
if k in champs_texte:
pos = base + octet + v
v = d[pos:d.index(b"\x00", pos)].decode("utf-8", "replace")
elif genre in (1, 5):
v = _bits(d, base, bits, nbits)
if genre == 5 and v & (1 << (nbits - 1)):
v -= 1 << nbits
elif genre == 2:
v = None # rempli apres, par identifiant
elif genre == 3:
idx = _bits(d, base, bits, nbits)
v = struct.unpack_from("<I", palette, pal[k] + 4 * idx)[0]
elif genre == 4:
idx = _bits(d, base, bits, nbits)
v = list(struct.unpack_from("<%dI" % v3, palette, pal[k] + 4 * idx * v3))
else:
raise ValueError("stockage %d non gere" % genre)
valeurs.append(v)
ident = ids[i] if ids else valeurs[index_id]
for k, (bits, nbits, extra, genre, v1, v2, v3) in enumerate(stockage):
if genre == 2:
valeurs[k] = com[k].get(ident, v1)
lignes.append((ident, valeurs))
# copies : (nouvel id, id copie)
par_id = dict(lignes)
for j in range(nb_copies):
nouveau, ancien = struct.unpack_from("<II", d, copies_debut + 8 * j)
if ancien in par_id:
lignes.append((nouveau, list(par_id[ancien])))
return lignes