From e9ce0621120456af5ddc74f92659653e39374321 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Sun, 22 Mar 2026 15:22:25 -0700 Subject: [PATCH] fix: restore correct CharSections.dbc field indices for character textures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR #19 (572bb4ef) swapped CharSections.dbc field indices, placing Texture1-3 at fields 4-6 and VariationIndex/ColorIndex at 8-9. Binary analysis of the actual DBC files (Classic, TBC, Turtle — all identical layout, no WotLK-specific override) confirms the correct order is: Field 4 = VariationIndex Field 5 = ColorIndex Field 6 = Texture1 (string) Field 7 = Texture2 (string) Field 8 = Texture3 (string) Field 9 = Flags With the wrong indices, VariationIndex/ColorIndex reads returned string offsets (garbage values that never matched), so all CharSections lookups failed silently — producing white untextured character models at the login screen and in-world. Fixes all 4 expansion JSON layouts, hardcoded fallbacks in character_preview.cpp, application.cpp, and character_create_screen.cpp. Also handles the single-layer edge case (body skin only, no face/underwear) by loading the texture directly instead of skipping compositing. --- Data/expansions/classic/dbc_layouts.json | 12 ++++++------ Data/expansions/tbc/dbc_layouts.json | 12 ++++++------ Data/expansions/turtle/dbc_layouts.json | 12 ++++++------ Data/expansions/wotlk/dbc_layouts.json | 12 ++++++------ src/core/application.cpp | 6 +++--- src/rendering/character_preview.cpp | 25 +++++++++++++++++------- src/ui/character_create_screen.cpp | 8 ++++---- 7 files changed, 49 insertions(+), 38 deletions(-) diff --git a/Data/expansions/classic/dbc_layouts.json b/Data/expansions/classic/dbc_layouts.json index a3cba2f8..ae75e254 100644 --- a/Data/expansions/classic/dbc_layouts.json +++ b/Data/expansions/classic/dbc_layouts.json @@ -37,12 +37,12 @@ "RaceID": 1, "SexID": 2, "BaseSection": 3, - "Texture1": 4, - "Texture2": 5, - "Texture3": 6, - "Flags": 7, - "VariationIndex": 8, - "ColorIndex": 9 + "VariationIndex": 4, + "ColorIndex": 5, + "Texture1": 6, + "Texture2": 7, + "Texture3": 8, + "Flags": 9 }, "SpellIcon": { "ID": 0, diff --git a/Data/expansions/tbc/dbc_layouts.json b/Data/expansions/tbc/dbc_layouts.json index 8dc4bbe8..8142434e 100644 --- a/Data/expansions/tbc/dbc_layouts.json +++ b/Data/expansions/tbc/dbc_layouts.json @@ -37,12 +37,12 @@ "RaceID": 1, "SexID": 2, "BaseSection": 3, - "Texture1": 4, - "Texture2": 5, - "Texture3": 6, - "Flags": 7, - "VariationIndex": 8, - "ColorIndex": 9 + "VariationIndex": 4, + "ColorIndex": 5, + "Texture1": 6, + "Texture2": 7, + "Texture3": 8, + "Flags": 9 }, "SpellIcon": { "ID": 0, diff --git a/Data/expansions/turtle/dbc_layouts.json b/Data/expansions/turtle/dbc_layouts.json index b7650ef5..42839fc6 100644 --- a/Data/expansions/turtle/dbc_layouts.json +++ b/Data/expansions/turtle/dbc_layouts.json @@ -37,12 +37,12 @@ "RaceID": 1, "SexID": 2, "BaseSection": 3, - "Texture1": 4, - "Texture2": 5, - "Texture3": 6, - "Flags": 7, - "VariationIndex": 8, - "ColorIndex": 9 + "VariationIndex": 4, + "ColorIndex": 5, + "Texture1": 6, + "Texture2": 7, + "Texture3": 8, + "Flags": 9 }, "SpellIcon": { "ID": 0, diff --git a/Data/expansions/wotlk/dbc_layouts.json b/Data/expansions/wotlk/dbc_layouts.json index 505ae150..5a05a517 100644 --- a/Data/expansions/wotlk/dbc_layouts.json +++ b/Data/expansions/wotlk/dbc_layouts.json @@ -37,12 +37,12 @@ "RaceID": 1, "SexID": 2, "BaseSection": 3, - "Texture1": 4, - "Texture2": 5, - "Texture3": 6, - "Flags": 7, - "VariationIndex": 8, - "ColorIndex": 9 + "VariationIndex": 4, + "ColorIndex": 5, + "Texture1": 6, + "Texture2": 7, + "Texture3": 8, + "Flags": 9 }, "SpellIcon": { "ID": 0, diff --git a/src/core/application.cpp b/src/core/application.cpp index a4728379..49c40976 100644 --- a/src/core/application.cpp +++ b/src/core/application.cpp @@ -3671,13 +3671,13 @@ void Application::spawnPlayerCharacter() { uint32_t raceId = charSectionsDbc->getUInt32(r, csL ? (*csL)["RaceID"] : 1); uint32_t sexId = charSectionsDbc->getUInt32(r, csL ? (*csL)["SexID"] : 2); uint32_t baseSection = charSectionsDbc->getUInt32(r, csL ? (*csL)["BaseSection"] : 3); - uint32_t variationIndex = charSectionsDbc->getUInt32(r, csL ? (*csL)["VariationIndex"] : 8); - uint32_t colorIndex = charSectionsDbc->getUInt32(r, csL ? (*csL)["ColorIndex"] : 9); + uint32_t variationIndex = charSectionsDbc->getUInt32(r, csL ? (*csL)["VariationIndex"] : 4); + uint32_t colorIndex = charSectionsDbc->getUInt32(r, csL ? (*csL)["ColorIndex"] : 5); if (raceId != targetRaceId || sexId != targetSexId) continue; // Section 0 = skin: match by colorIndex = skin byte - const uint32_t csTex1 = csL ? (*csL)["Texture1"] : 4; + const uint32_t csTex1 = csL ? (*csL)["Texture1"] : 6; if (baseSection == 0 && !foundSkin && colorIndex == charSkinId) { std::string tex1 = charSectionsDbc->getString(r, csTex1); if (!tex1.empty()) { diff --git a/src/rendering/character_preview.cpp b/src/rendering/character_preview.cpp index 31546026..306509ed 100644 --- a/src/rendering/character_preview.cpp +++ b/src/rendering/character_preview.cpp @@ -336,8 +336,8 @@ bool CharacterPreview::loadCharacter(game::Race race, game::Gender gender, uint32_t fRace = csL ? (*csL)["RaceID"] : 1; uint32_t fSex = csL ? (*csL)["SexID"] : 2; uint32_t fBase = csL ? (*csL)["BaseSection"] : 3; - uint32_t fVar = csL ? (*csL)["VariationIndex"] : 8; - uint32_t fColor = csL ? (*csL)["ColorIndex"] : 9; + uint32_t fVar = csL ? (*csL)["VariationIndex"] : 4; + uint32_t fColor = csL ? (*csL)["ColorIndex"] : 5; for (uint32_t r = 0; r < charSectionsDbc->getRecordCount(); r++) { uint32_t raceId = charSectionsDbc->getUInt32(r, fRace); uint32_t sexId = charSectionsDbc->getUInt32(r, fSex); @@ -350,7 +350,7 @@ bool CharacterPreview::loadCharacter(game::Race race, game::Gender gender, // Section 0: Body skin (variation=0, colorIndex = skin color) if (baseSection == 0 && !foundSkin && variationIndex == 0 && colorIndex == static_cast(skin)) { - std::string tex1 = charSectionsDbc->getString(r, csL ? (*csL)["Texture1"] : 4); + std::string tex1 = charSectionsDbc->getString(r, csL ? (*csL)["Texture1"] : 6); if (!tex1.empty()) { bodySkinPath_ = tex1; foundSkin = true; @@ -360,8 +360,8 @@ bool CharacterPreview::loadCharacter(game::Race race, game::Gender gender, else if (baseSection == 1 && !foundFace && variationIndex == static_cast(face) && colorIndex == static_cast(skin)) { - std::string tex1 = charSectionsDbc->getString(r, csL ? (*csL)["Texture1"] : 4); - std::string tex2 = charSectionsDbc->getString(r, csL ? (*csL)["Texture2"] : 5); + std::string tex1 = charSectionsDbc->getString(r, csL ? (*csL)["Texture1"] : 6); + std::string tex2 = charSectionsDbc->getString(r, csL ? (*csL)["Texture2"] : 7); if (!tex1.empty()) faceLowerPath = tex1; if (!tex2.empty()) faceUpperPath = tex2; foundFace = true; @@ -370,7 +370,7 @@ bool CharacterPreview::loadCharacter(game::Race race, game::Gender gender, else if (baseSection == 3 && !foundHair && variationIndex == static_cast(hairStyle) && colorIndex == static_cast(hairColor)) { - std::string tex1 = charSectionsDbc->getString(r, csL ? (*csL)["Texture1"] : 4); + std::string tex1 = charSectionsDbc->getString(r, csL ? (*csL)["Texture1"] : 6); if (!tex1.empty()) { hairScalpPath = tex1; foundHair = true; @@ -379,7 +379,7 @@ bool CharacterPreview::loadCharacter(game::Race race, game::Gender gender, // Section 4: Underwear (variation=0, colorIndex = skin color) else if (baseSection == 4 && !foundUnderwear && variationIndex == 0 && colorIndex == static_cast(skin)) { - uint32_t texBase = csL ? (*csL)["Texture1"] : 4; + uint32_t texBase = csL ? (*csL)["Texture1"] : 6; for (uint32_t f = texBase; f <= texBase + 2; f++) { std::string tex = charSectionsDbc->getString(r, f); if (!tex.empty()) { @@ -462,6 +462,17 @@ bool CharacterPreview::loadCharacter(game::Race race, game::Gender gender, } } } + } else { + // Single layer (body skin only, no face/underwear overlays) — load directly + VkTexture* skinTex = charRenderer_->loadTexture(bodySkinPath_); + if (skinTex != nullptr) { + for (size_t ti = 0; ti < model.textures.size(); ti++) { + if (model.textures[ti].type == 1) { + charRenderer_->setModelTexture(PREVIEW_MODEL_ID, static_cast(ti), skinTex); + break; + } + } + } } } diff --git a/src/ui/character_create_screen.cpp b/src/ui/character_create_screen.cpp index 63933924..fa81756f 100644 --- a/src/ui/character_create_screen.cpp +++ b/src/ui/character_create_screen.cpp @@ -257,8 +257,8 @@ void CharacterCreateScreen::updateAppearanceRanges() { if (raceId != targetRaceId || sexId != targetSexId) continue; 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); + uint32_t variationIndex = dbc->getUInt32(r, csL ? (*csL)["VariationIndex"] : 4); + uint32_t colorIndex = dbc->getUInt32(r, csL ? (*csL)["ColorIndex"] : 5); if (baseSection == 0 && variationIndex == 0) { skinMax = std::max(skinMax, static_cast(colorIndex)); @@ -284,8 +284,8 @@ void CharacterCreateScreen::updateAppearanceRanges() { if (raceId != targetRaceId || sexId != targetSexId) continue; 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); + uint32_t variationIndex = dbc->getUInt32(r, csL ? (*csL)["VariationIndex"] : 4); + uint32_t colorIndex = dbc->getUInt32(r, csL ? (*csL)["ColorIndex"] : 5); if (baseSection == 1 && colorIndex == static_cast(skin)) { faceMax = std::max(faceMax, static_cast(variationIndex));