Add multi-expansion support with data-driven protocol layer

Replace hardcoded WotLK protocol constants with a data-driven architecture
supporting Classic 1.12.1, TBC 2.4.3, and WotLK 3.3.5a. Each expansion
has JSON profiles for opcodes, update fields, and DBC layouts, plus C++
polymorphic packet parsers for binary format differences (movement flags,
speed fields, transport data, spline format, char enum layout).

Key components:
- ExpansionRegistry: scans Data/expansions/*/expansion.json at startup
- OpcodeTable: logical enum <-> wire values loaded from JSON
- UpdateFieldTable: field indices loaded from JSON per expansion
- DBCLayout: schema-driven DBC field lookups replacing magic numbers
- PacketParsers: WotLK/TBC/Classic parsers with correct flag positions
- Multi-manifest AssetManager: layered manifests with priority ordering
- HDPackManager: overlay texture packs with expansion compatibility
- Auth screen expansion picker replacing hardcoded version dropdown
This commit is contained in:
Kelsi 2026-02-12 22:56:36 -08:00
parent aa16a687c2
commit 7092844b5e
51 changed files with 5258 additions and 887 deletions

View file

@ -2,6 +2,7 @@
#include "rendering/character_preview.hpp"
#include "game/game_handler.hpp"
#include "pipeline/asset_manager.hpp"
#include "pipeline/dbc_layout.hpp"
#include <imgui.h>
#include <cstring>
@ -169,16 +170,17 @@ void CharacterCreateScreen::updateAppearanceRanges() {
uint32_t targetRaceId = static_cast<uint32_t>(allRaces[raceIndex]);
uint32_t targetSexId = (genderIndex == 1) ? 1u : 0u;
const auto* csL = pipeline::getActiveDBCLayout() ? pipeline::getActiveDBCLayout()->getLayout("CharSections") : nullptr;
int skinMax = -1;
int hairStyleMax = -1;
for (uint32_t r = 0; r < dbc->getRecordCount(); r++) {
uint32_t raceId = dbc->getUInt32(r, 1);
uint32_t sexId = dbc->getUInt32(r, 2);
uint32_t raceId = dbc->getUInt32(r, csL ? (*csL)["RaceID"] : 1);
uint32_t sexId = dbc->getUInt32(r, csL ? (*csL)["SexID"] : 2);
if (raceId != targetRaceId || sexId != targetSexId) continue;
uint32_t baseSection = dbc->getUInt32(r, 3);
uint32_t variationIndex = dbc->getUInt32(r, 8);
uint32_t colorIndex = dbc->getUInt32(r, 9);
uint32_t baseSection = dbc->getUInt32(r, csL ? (*csL)["BaseSection"] : 3);
uint32_t variationIndex = dbc->getUInt32(r, csL ? (*csL)["VariationIndex"] : 8);
uint32_t colorIndex = dbc->getUInt32(r, csL ? (*csL)["ColorIndex"] : 9);
if (baseSection == 0 && variationIndex == 0) {
skinMax = std::max(skinMax, static_cast<int>(colorIndex));
@ -199,13 +201,13 @@ void CharacterCreateScreen::updateAppearanceRanges() {
int faceMax = -1;
std::vector<uint8_t> hairColorIds;
for (uint32_t r = 0; r < dbc->getRecordCount(); r++) {
uint32_t raceId = dbc->getUInt32(r, 1);
uint32_t sexId = dbc->getUInt32(r, 2);
uint32_t raceId = dbc->getUInt32(r, csL ? (*csL)["RaceID"] : 1);
uint32_t sexId = dbc->getUInt32(r, csL ? (*csL)["SexID"] : 2);
if (raceId != targetRaceId || sexId != targetSexId) continue;
uint32_t baseSection = dbc->getUInt32(r, 3);
uint32_t variationIndex = dbc->getUInt32(r, 8);
uint32_t colorIndex = dbc->getUInt32(r, 9);
uint32_t baseSection = dbc->getUInt32(r, csL ? (*csL)["BaseSection"] : 3);
uint32_t variationIndex = dbc->getUInt32(r, csL ? (*csL)["VariationIndex"] : 8);
uint32_t colorIndex = dbc->getUInt32(r, csL ? (*csL)["ColorIndex"] : 9);
if (baseSection == 1 && colorIndex == static_cast<uint32_t>(skin)) {
faceMax = std::max(faceMax, static_cast<int>(variationIndex));
@ -232,12 +234,13 @@ void CharacterCreateScreen::updateAppearanceRanges() {
}
int facialMax = -1;
auto facialDbc = assetManager_->loadDBC("CharacterFacialHairStyles.dbc");
const auto* fhL = pipeline::getActiveDBCLayout() ? pipeline::getActiveDBCLayout()->getLayout("CharacterFacialHairStyles") : nullptr;
if (facialDbc) {
for (uint32_t r = 0; r < facialDbc->getRecordCount(); r++) {
uint32_t raceId = facialDbc->getUInt32(r, 0);
uint32_t sexId = facialDbc->getUInt32(r, 1);
uint32_t raceId = facialDbc->getUInt32(r, fhL ? (*fhL)["RaceID"] : 0);
uint32_t sexId = facialDbc->getUInt32(r, fhL ? (*fhL)["SexID"] : 1);
if (raceId != targetRaceId || sexId != targetSexId) continue;
uint32_t variation = facialDbc->getUInt32(r, 2);
uint32_t variation = facialDbc->getUInt32(r, fhL ? (*fhL)["Variation"] : 2);
facialMax = std::max(facialMax, static_cast<int>(variation));
}
}