mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00:00
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:
parent
6729f66a37
commit
430c2bdcfa
34 changed files with 1066 additions and 24795 deletions
|
|
@ -246,15 +246,17 @@ size_t TCPSocket::getExpectedPacketSize(uint8_t opcode) {
|
|||
return 0; // Need more data to determine
|
||||
|
||||
case 0x01: // LOGON_PROOF response
|
||||
// Success: opcode(1) + status(1) + M2(20) + accountFlags(4) + surveyId(4) + loginFlags(2) = 32
|
||||
// Some vanilla-era servers send a shorter success response:
|
||||
// opcode(1) + status(1) + M2(20) = 22
|
||||
// Success response varies by server build (determined by client build sent in challenge):
|
||||
// Build >= 8089: cmd(1)+error(1)+M2(20)+accountFlags(4)+surveyId(4)+loginFlags(2) = 32
|
||||
// Build 6299-8088: cmd(1)+error(1)+M2(20)+surveyId(4)+loginFlags(2) = 28
|
||||
// Build < 6299: cmd(1)+error(1)+M2(20)+surveyId(4) = 26
|
||||
// Failure: varies by server — minimum 2 bytes (opcode + status), some send 4
|
||||
if (receiveBuffer.size() >= 2) {
|
||||
uint8_t status = receiveBuffer[1];
|
||||
if (status == 0x00) {
|
||||
if (receiveBuffer.size() >= 32) return 32; // WotLK-style
|
||||
if (receiveBuffer.size() >= 22) return 22; // minimal/vanilla-style
|
||||
if (receiveBuffer.size() >= 32) return 32;
|
||||
if (receiveBuffer.size() >= 28) return 28;
|
||||
if (receiveBuffer.size() >= 26) return 26;
|
||||
return 0;
|
||||
} else {
|
||||
// Consume up to 4 bytes if available, minimum 2
|
||||
|
|
|
|||
|
|
@ -115,6 +115,7 @@ void WorldSocket::disconnect() {
|
|||
}
|
||||
connected = false;
|
||||
encryptionEnabled = false;
|
||||
useVanillaCrypt = false;
|
||||
receiveBuffer.clear();
|
||||
headerBytesDecrypted = 0;
|
||||
LOG_INFO("Disconnected from world server");
|
||||
|
|
@ -214,7 +215,11 @@ void WorldSocket::send(const Packet& packet) {
|
|||
|
||||
// Encrypt header if encryption is enabled (all 6 bytes)
|
||||
if (encryptionEnabled) {
|
||||
encryptCipher.process(sendData.data(), 6);
|
||||
if (useVanillaCrypt) {
|
||||
vanillaCrypt.encrypt(sendData.data(), 6);
|
||||
} else {
|
||||
encryptCipher.process(sendData.data(), 6);
|
||||
}
|
||||
}
|
||||
|
||||
// Add payload (unencrypted)
|
||||
|
|
@ -291,17 +296,26 @@ void WorldSocket::update() {
|
|||
}
|
||||
|
||||
if (receivedAny) {
|
||||
LOG_DEBUG("World socket read ", bytesReadThisTick, " bytes in ", readOps,
|
||||
" recv call(s), buffered=", receiveBuffer.size());
|
||||
LOG_INFO("World socket read ", bytesReadThisTick, " bytes in ", readOps,
|
||||
" recv call(s), buffered=", receiveBuffer.size());
|
||||
// Hex dump received bytes for auth debugging
|
||||
if (bytesReadThisTick <= 128) {
|
||||
std::string hex;
|
||||
for (size_t i = 0; i < receiveBuffer.size(); ++i) {
|
||||
char buf[4]; snprintf(buf, sizeof(buf), "%02x ", receiveBuffer[i]); hex += buf;
|
||||
}
|
||||
LOG_INFO("World socket raw bytes: ", hex);
|
||||
}
|
||||
tryParsePackets();
|
||||
if (connected && !receiveBuffer.empty()) {
|
||||
LOG_DEBUG("World socket parse left ", receiveBuffer.size(),
|
||||
" bytes buffered (awaiting complete packet)");
|
||||
LOG_INFO("World socket parse left ", receiveBuffer.size(),
|
||||
" bytes buffered (awaiting complete packet)");
|
||||
}
|
||||
}
|
||||
|
||||
if (sawClose) {
|
||||
LOG_INFO("World server connection closed");
|
||||
LOG_INFO("World server connection closed (receivedAny=", receivedAny,
|
||||
" buffered=", receiveBuffer.size(), ")");
|
||||
disconnect();
|
||||
return;
|
||||
}
|
||||
|
|
@ -317,7 +331,11 @@ void WorldSocket::tryParsePackets() {
|
|||
// Only decrypt bytes we haven't already decrypted
|
||||
if (encryptionEnabled && headerBytesDecrypted < 4) {
|
||||
size_t toDecrypt = 4 - headerBytesDecrypted;
|
||||
decryptCipher.process(receiveBuffer.data() + headerBytesDecrypted, toDecrypt);
|
||||
if (useVanillaCrypt) {
|
||||
vanillaCrypt.decrypt(receiveBuffer.data() + headerBytesDecrypted, toDecrypt);
|
||||
} else {
|
||||
decryptCipher.process(receiveBuffer.data() + headerBytesDecrypted, toDecrypt);
|
||||
}
|
||||
headerBytesDecrypted = 4;
|
||||
}
|
||||
|
||||
|
|
@ -402,33 +420,36 @@ void WorldSocket::tryParsePackets() {
|
|||
}
|
||||
}
|
||||
|
||||
void WorldSocket::initEncryption(const std::vector<uint8_t>& sessionKey) {
|
||||
void WorldSocket::initEncryption(const std::vector<uint8_t>& sessionKey, uint32_t build) {
|
||||
if (sessionKey.size() != 40) {
|
||||
LOG_ERROR("Invalid session key size: ", sessionKey.size(), " (expected 40)");
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_INFO(">>> ENABLING ENCRYPTION - encryptionEnabled will become true <<<");
|
||||
// Vanilla/TBC (build <= 8606) uses XOR+addition cipher with raw session key
|
||||
// WotLK (build > 8606) uses HMAC-SHA1 derived RC4 with 1024-byte drop
|
||||
useVanillaCrypt = (build <= 8606);
|
||||
|
||||
// Convert hardcoded keys to vectors
|
||||
std::vector<uint8_t> encryptKey(ENCRYPT_KEY, ENCRYPT_KEY + 16);
|
||||
std::vector<uint8_t> decryptKey(DECRYPT_KEY, DECRYPT_KEY + 16);
|
||||
LOG_INFO(">>> ENABLING ENCRYPTION (", useVanillaCrypt ? "vanilla XOR" : "WotLK RC4",
|
||||
") build=", build, " <<<");
|
||||
|
||||
// Compute HMAC-SHA1(seed, sessionKey) for each cipher
|
||||
// The 16-byte seed is the HMAC key, session key is the message
|
||||
std::vector<uint8_t> encryptHash = auth::Crypto::hmacSHA1(encryptKey, sessionKey);
|
||||
std::vector<uint8_t> decryptHash = auth::Crypto::hmacSHA1(decryptKey, sessionKey);
|
||||
if (useVanillaCrypt) {
|
||||
vanillaCrypt.init(sessionKey);
|
||||
} else {
|
||||
// WotLK: HMAC-SHA1(hardcoded seed, sessionKey) → RC4 key
|
||||
std::vector<uint8_t> encryptKey(ENCRYPT_KEY, ENCRYPT_KEY + 16);
|
||||
std::vector<uint8_t> decryptKey(DECRYPT_KEY, DECRYPT_KEY + 16);
|
||||
|
||||
LOG_DEBUG("Encrypt hash: ", encryptHash.size(), " bytes");
|
||||
LOG_DEBUG("Decrypt hash: ", decryptHash.size(), " bytes");
|
||||
std::vector<uint8_t> encryptHash = auth::Crypto::hmacSHA1(encryptKey, sessionKey);
|
||||
std::vector<uint8_t> decryptHash = auth::Crypto::hmacSHA1(decryptKey, sessionKey);
|
||||
|
||||
// Initialize RC4 ciphers with HMAC results
|
||||
encryptCipher.init(encryptHash);
|
||||
decryptCipher.init(decryptHash);
|
||||
encryptCipher.init(encryptHash);
|
||||
decryptCipher.init(decryptHash);
|
||||
|
||||
// Drop first 1024 bytes of keystream (WoW protocol requirement)
|
||||
encryptCipher.drop(1024);
|
||||
decryptCipher.drop(1024);
|
||||
// Drop first 1024 bytes of keystream (WoW WotLK protocol requirement)
|
||||
encryptCipher.drop(1024);
|
||||
decryptCipher.drop(1024);
|
||||
}
|
||||
|
||||
encryptionEnabled = true;
|
||||
headerTracePacketsLeft = 24;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue