Vanilla/Turtle WoW support: M2 loading, bone parsing, textures, auth

- Vanilla M2 bone struct (108 bytes) with 28-byte animation tracks
- Version-aware bone parsing (vanilla vs WotLK format detection)
- Fix CharSections.dbc field layout for vanilla (variation/color at 4-5)
- Remove broken CharSections.csv files (all fields marked as strings)
- Expansion data reload on profile switch (DBC cache clear, layout reload)
- Vanilla packet encryption (VanillaCrypt XOR-based header crypt)
- Extended character preview geoset range (0-99) for vanilla models
- DBC cache clear support in AssetManager
This commit is contained in:
Kelsi 2026-02-13 16:53:28 -08:00
parent 6729f66a37
commit 430c2bdcfa
34 changed files with 1066 additions and 24795 deletions

View file

@ -19,8 +19,9 @@ struct ExpansionProfile {
uint8_t majorVersion = 0;
uint8_t minorVersion = 0;
uint8_t patchVersion = 0;
uint16_t build = 0;
uint8_t protocolVersion = 0; // SRP auth protocol version byte
uint16_t build = 0; // Realm build (sent in LOGON_CHALLENGE)
uint16_t worldBuild = 0; // World build (sent in CMSG_AUTH_SESSION, defaults to build)
uint8_t protocolVersion = 0; // SRP auth protocol version byte
// Client header fields used in LOGON_CHALLENGE.
// Defaults match a typical Windows x86 client.
std::string game = "WoW";

View file

@ -1214,6 +1214,19 @@ private:
std::unique_ptr<WardenCrypto> wardenCrypto_;
std::unique_ptr<WardenModuleManager> wardenModuleManager_;
// Warden module download state
enum class WardenState {
WAIT_MODULE_USE, // Waiting for first SMSG (MODULE_USE)
WAIT_MODULE_CACHE, // Sent MODULE_MISSING, receiving module chunks
WAIT_HASH_REQUEST, // Module received, waiting for HASH_REQUEST
WAIT_CHECKS, // Hash sent, waiting for check requests
};
WardenState wardenState_ = WardenState::WAIT_MODULE_USE;
std::vector<uint8_t> wardenModuleHash_; // 16 bytes MD5
std::vector<uint8_t> wardenModuleKey_; // 16 bytes RC4
uint32_t wardenModuleSize_ = 0;
std::vector<uint8_t> wardenModuleData_; // Downloaded module chunks
// ---- XP tracking ----
uint32_t playerXp_ = 0;
uint32_t playerNextLevelXp_ = 0;

View file

@ -8,8 +8,9 @@ namespace wowee {
namespace game {
/**
* Warden anti-cheat crypto handler for WoW 3.3.5a
* Handles RC4 encryption/decryption of Warden packets
* Warden anti-cheat crypto handler.
* Derives RC4 keys from the 40-byte SRP session key using SHA1Randx,
* then encrypts/decrypts Warden packet payloads.
*/
class WardenCrypto {
public:
@ -17,45 +18,40 @@ public:
~WardenCrypto();
/**
* Initialize Warden crypto with module seed
* @param moduleData The SMSG_WARDEN_DATA payload containing seed
* @return true if initialization succeeded
* Initialize Warden crypto from the 40-byte SRP session key.
* Derives encrypt (client->server) and decrypt (server->client) RC4 keys
* using the SHA1Randx / WardenKeyGenerator algorithm.
*/
bool initialize(const std::vector<uint8_t>& moduleData);
bool initFromSessionKey(const std::vector<uint8_t>& sessionKey);
/**
* Decrypt an incoming Warden packet
* @param data Encrypted data from server
* @return Decrypted data
* Replace RC4 keys (called after module hash challenge succeeds).
* @param newEncryptKey 16-byte key for encrypting outgoing packets
* @param newDecryptKey 16-byte key for decrypting incoming packets
*/
void replaceKeys(const std::vector<uint8_t>& newEncryptKey,
const std::vector<uint8_t>& newDecryptKey);
/** Decrypt an incoming SMSG_WARDEN_DATA payload. */
std::vector<uint8_t> decrypt(const std::vector<uint8_t>& data);
/**
* Encrypt an outgoing Warden response
* @param data Plaintext response data
* @return Encrypted data
*/
/** Encrypt an outgoing CMSG_WARDEN_DATA payload. */
std::vector<uint8_t> encrypt(const std::vector<uint8_t>& data);
/**
* Check if crypto has been initialized
*/
bool isInitialized() const { return initialized_; }
private:
bool initialized_;
std::vector<uint8_t> inputKey_;
std::vector<uint8_t> outputKey_;
// RC4 state for incoming packets
std::vector<uint8_t> inputRC4State_;
uint8_t inputRC4_i_;
uint8_t inputRC4_j_;
// RC4 state for decrypting incoming packets (server->client)
std::vector<uint8_t> decryptRC4State_;
uint8_t decryptRC4_i_;
uint8_t decryptRC4_j_;
// RC4 state for outgoing packets
std::vector<uint8_t> outputRC4State_;
uint8_t outputRC4_i_;
uint8_t outputRC4_j_;
// RC4 state for encrypting outgoing packets (client->server)
std::vector<uint8_t> encryptRC4State_;
uint8_t encryptRC4_i_;
uint8_t encryptRC4_j_;
void initRC4(const std::vector<uint8_t>& key,
std::vector<uint8_t>& state,
@ -63,6 +59,14 @@ private:
void processRC4(const uint8_t* input, uint8_t* output, size_t length,
std::vector<uint8_t>& state, uint8_t& i, uint8_t& j);
/**
* SHA1Randx / WardenKeyGenerator: generates pseudo-random bytes from a seed.
* Used to derive the 16-byte encrypt and decrypt keys from the session key.
*/
static void sha1RandxGenerate(const std::vector<uint8_t>& seed,
uint8_t* outputEncryptKey,
uint8_t* outputDecryptKey);
};
} // namespace game

View file

@ -26,7 +26,7 @@ struct AuthChallengeData {
uint32_t serverSeed; // Random seed from server
// Note: 3.3.5a has additional data after this
bool isValid() const { return unknown1 != 0; }
bool isValid() const { return true; }
};
/**