Kelsidavis-WoWee/include/pipeline/dbc_layout.hpp
Kelsi 503f9ed650 fix: auto-detect CharSections.dbc layout and add Blood Elf/Draenei NPC voices
CharSections.dbc has different field layouts between stock WotLK (textures
at field 4-6) and Classic/TBC/Turtle/HD-textured WotLK (VariationIndex at
field 4). Add detectCharSectionsFields() that probes field-4 values at
runtime to determine the correct layout, so both stock and modded clients
work without JSON changes.

Also add BLOODELF_MALE/FEMALE and DRAENEI_MALE/FEMALE voice types to the
NPC voice system — previously all Blood Elf and Draenei NPCs fell through
to GENERIC (random dwarf/gnome/night elf/orc mix).
2026-03-23 11:00:49 -07:00

96 lines
3 KiB
C++

#pragma once
#include <cstdint>
#include <string>
#include <unordered_map>
namespace wowee {
namespace pipeline {
/**
* Maps DBC field names to column indices for a single DBC file.
* Column indices vary between WoW expansions.
*/
struct DBCFieldMap {
std::unordered_map<std::string, uint32_t> fields;
/** Get column index by field name. Returns 0xFFFFFFFF if unknown. */
uint32_t field(const std::string& name) const {
auto it = fields.find(name);
return (it != fields.end()) ? it->second : 0xFFFFFFFF;
}
/** Convenience operator for shorter syntax: layout["Name"] */
uint32_t operator[](const std::string& name) const { return field(name); }
};
/**
* Maps DBC file names to their field layouts.
* Loaded from JSON (e.g. Data/expansions/wotlk/dbc_layouts.json).
*/
class DBCLayout {
public:
/** Load from JSON file. Returns true if successful. */
bool loadFromJson(const std::string& path);
/** Get the field map for a DBC file. Returns nullptr if unknown. */
const DBCFieldMap* getLayout(const std::string& dbcName) const;
/** Number of DBC layouts loaded. */
size_t size() const { return layouts_.size(); }
private:
std::unordered_map<std::string, DBCFieldMap> layouts_;
};
/**
* Global active DBC layout (set by Application at startup).
*/
void setActiveDBCLayout(const DBCLayout* layout);
const DBCLayout* getActiveDBCLayout();
/** Convenience: get field index for a DBC field. */
inline uint32_t dbcField(const std::string& dbcName, const std::string& fieldName) {
const auto* l = getActiveDBCLayout();
if (!l) return 0xFFFFFFFF;
const auto* fm = l->getLayout(dbcName);
return fm ? fm->field(fieldName) : 0xFFFFFFFF;
}
// Forward declaration
class DBCFile;
/**
* Resolved CharSections.dbc field indices.
*
* Stock WotLK 3.3.5a uses: Texture1=4, Texture2=5, Texture3=6, Flags=7,
* VariationIndex=8, ColorIndex=9 (textures first).
* Classic/TBC/Turtle and HD-texture WotLK use: VariationIndex=4, ColorIndex=5,
* Texture1=6, Texture2=7, Texture3=8, Flags=9 (variation first).
*
* detectCharSectionsFields() auto-detects which layout the actual DBC uses
* by sampling field-4 values: small integers (0-15) => variation-first,
* large values (string offsets) => texture-first.
*/
struct CharSectionsFields {
uint32_t raceId = 1;
uint32_t sexId = 2;
uint32_t baseSection = 3;
uint32_t variationIndex = 4;
uint32_t colorIndex = 5;
uint32_t texture1 = 6;
uint32_t texture2 = 7;
uint32_t texture3 = 8;
uint32_t flags = 9;
};
/**
* Detect the actual CharSections.dbc field layout by probing record data.
* @param dbc Loaded CharSections.dbc file (must not be null).
* @param csL JSON-derived field map (may be null — defaults used).
* @return Resolved field indices for this particular DBC binary.
*/
CharSectionsFields detectCharSectionsFields(const DBCFile* dbc, const DBCFieldMap* csL);
} // namespace pipeline
} // namespace wowee