diff options
author | Neo2003 <none@none> | 2008-10-02 16:23:55 -0500 |
---|---|---|
committer | Neo2003 <none@none> | 2008-10-02 16:23:55 -0500 |
commit | 9b1c0e006f20091f28f3f468cfcab1feb51286bd (patch) | |
tree | b5d1ba94a656e6679f8737f9ea6bed1239b73b14 /src/shared |
[svn] * Proper SVN structureinit
--HG--
branch : trunk
Diffstat (limited to 'src/shared')
106 files changed, 18413 insertions, 0 deletions
diff --git a/src/shared/Auth/AuthCrypt.cpp b/src/shared/Auth/AuthCrypt.cpp new file mode 100644 index 00000000000..56143c3417c --- /dev/null +++ b/src/shared/Auth/AuthCrypt.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "AuthCrypt.h" +#include "Hmac.h" + +AuthCrypt::AuthCrypt() +{ + _initialized = false; +} + +void AuthCrypt::Init() +{ + _send_i = _send_j = _recv_i = _recv_j = 0; + _initialized = true; +} + +void AuthCrypt::DecryptRecv(uint8 *data, size_t len) +{ + if (!_initialized) return; + if (len < CRYPTED_RECV_LEN) return; + + for (size_t t = 0; t < CRYPTED_RECV_LEN; t++) + { + _recv_i %= _key.size(); + uint8 x = (data[t] - _recv_j) ^ _key[_recv_i]; + ++_recv_i; + _recv_j = data[t]; + data[t] = x; + } +} + +void AuthCrypt::EncryptSend(uint8 *data, size_t len) +{ + if (!_initialized) return; + if (len < CRYPTED_SEND_LEN) return; + + for (size_t t = 0; t < CRYPTED_SEND_LEN; t++) + { + _send_i %= _key.size(); + uint8 x = (data[t] ^ _key[_send_i]) + _send_j; + ++_send_i; + data[t] = _send_j = x; + } +} + +void AuthCrypt::SetKey(BigNumber *bn) +{ + uint8 *key = new uint8[SHA_DIGEST_LENGTH]; + GenerateKey(key, bn); + _key.resize(SHA_DIGEST_LENGTH); + std::copy(key, key + SHA_DIGEST_LENGTH, _key.begin()); + delete key; +} + +AuthCrypt::~AuthCrypt() +{ +} + +void AuthCrypt::GenerateKey(uint8 *key, BigNumber *bn) +{ + HmacHash hash; + hash.UpdateBigNumber(bn); + hash.Finalize(); + memcpy(key, hash.GetDigest(), SHA_DIGEST_LENGTH); +} diff --git a/src/shared/Auth/AuthCrypt.h b/src/shared/Auth/AuthCrypt.h new file mode 100644 index 00000000000..94e075e584e --- /dev/null +++ b/src/shared/Auth/AuthCrypt.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _AUTHCRYPT_H +#define _AUTHCRYPT_H + +#include <Common.h> +#include <vector> + +class BigNumber; + +class AuthCrypt +{ + public: + AuthCrypt(); + ~AuthCrypt(); + + const static size_t CRYPTED_SEND_LEN = 4; + const static size_t CRYPTED_RECV_LEN = 6; + + void Init(); + + void SetKey(BigNumber *); + + void DecryptRecv(uint8 *, size_t); + void EncryptSend(uint8 *, size_t); + + bool IsInitialized() { return _initialized; } + + static void GenerateKey(uint8 *, BigNumber *); + + private: + std::vector<uint8> _key; + uint8 _send_i, _send_j, _recv_i, _recv_j; + bool _initialized; +}; +#endif diff --git a/src/shared/Auth/BigNumber.cpp b/src/shared/Auth/BigNumber.cpp new file mode 100644 index 00000000000..752e82f171e --- /dev/null +++ b/src/shared/Auth/BigNumber.cpp @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Auth/BigNumber.h" +#include <openssl/bn.h> +#include <algorithm> + +BigNumber::BigNumber() +{ + _bn = BN_new(); + _array = NULL; +} + +BigNumber::BigNumber(const BigNumber &bn) +{ + _bn = BN_dup(bn._bn); + _array = NULL; +} + +BigNumber::BigNumber(uint32 val) +{ + _bn = BN_new(); + BN_set_word(_bn, val); + _array = NULL; +} + +BigNumber::~BigNumber() +{ + BN_free(_bn); + if(_array) delete[] _array; +} + +void BigNumber::SetDword(uint32 val) +{ + BN_set_word(_bn, val); +} + +void BigNumber::SetQword(uint64 val) +{ + BN_add_word(_bn, (uint32)(val >> 32)); + BN_lshift(_bn, _bn, 32); + BN_add_word(_bn, (uint32)(val & 0xFFFFFFFF)); +} + +void BigNumber::SetBinary(const uint8 *bytes, int len) +{ + uint8 t[1000]; + for (int i = 0; i < len; i++) t[i] = bytes[len - 1 - i]; + BN_bin2bn(t, len, _bn); +} + +void BigNumber::SetHexStr(const char *str) +{ + BN_hex2bn(&_bn, str); +} + +void BigNumber::SetRand(int numbits) +{ + BN_rand(_bn, numbits, 0, 1); +} + +BigNumber BigNumber::operator=(const BigNumber &bn) +{ + BN_copy(_bn, bn._bn); + return *this; +} + +BigNumber BigNumber::operator+=(const BigNumber &bn) +{ + BN_add(_bn, _bn, bn._bn); + return *this; +} + +BigNumber BigNumber::operator-=(const BigNumber &bn) +{ + BN_sub(_bn, _bn, bn._bn); + return *this; +} + +BigNumber BigNumber::operator*=(const BigNumber &bn) +{ + BN_CTX *bnctx; + + bnctx = BN_CTX_new(); + BN_mul(_bn, _bn, bn._bn, bnctx); + BN_CTX_free(bnctx); + + return *this; +} + +BigNumber BigNumber::operator/=(const BigNumber &bn) +{ + BN_CTX *bnctx; + + bnctx = BN_CTX_new(); + BN_div(_bn, NULL, _bn, bn._bn, bnctx); + BN_CTX_free(bnctx); + + return *this; +} + +BigNumber BigNumber::operator%=(const BigNumber &bn) +{ + BN_CTX *bnctx; + + bnctx = BN_CTX_new(); + BN_mod(_bn, _bn, bn._bn, bnctx); + BN_CTX_free(bnctx); + + return *this; +} + +BigNumber BigNumber::Exp(const BigNumber &bn) +{ + BigNumber ret; + BN_CTX *bnctx; + + bnctx = BN_CTX_new(); + BN_exp(ret._bn, _bn, bn._bn, bnctx); + BN_CTX_free(bnctx); + + return ret; +} + +BigNumber BigNumber::ModExp(const BigNumber &bn1, const BigNumber &bn2) +{ + BigNumber ret; + BN_CTX *bnctx; + + bnctx = BN_CTX_new(); + BN_mod_exp(ret._bn, _bn, bn1._bn, bn2._bn, bnctx); + BN_CTX_free(bnctx); + + return ret; +} + +int BigNumber::GetNumBytes(void) +{ + return BN_num_bytes(_bn); +} + +uint32 BigNumber::AsDword() +{ + return (uint32)BN_get_word(_bn); +} + +uint8 *BigNumber::AsByteArray(int minSize) +{ + int length = (minSize >= GetNumBytes()) ? minSize : GetNumBytes(); + + if (_array) + { + delete[] _array; + _array = NULL; + } + _array = new uint8[length]; + + // If we need more bytes than length of BigNumber set the rest to 0 + if (length > GetNumBytes()) + memset((void*)_array, 0, length); + + BN_bn2bin(_bn, (unsigned char *)_array); + + std::reverse(_array, _array + length); + + return _array; +} + +ByteBuffer BigNumber::AsByteBuffer() +{ + ByteBuffer ret(GetNumBytes()); + ret.append(AsByteArray(), GetNumBytes()); + return ret; +} + +std::vector<uint8> BigNumber::AsByteVector() +{ + std::vector<uint8> ret; + ret.resize(GetNumBytes()); + memcpy(&ret[0], AsByteArray(), GetNumBytes()); + return ret; +} + +const char *BigNumber::AsHexStr() +{ + return BN_bn2hex(_bn); +} + +const char *BigNumber::AsDecStr() +{ + return BN_bn2dec(_bn); +} diff --git a/src/shared/Auth/BigNumber.h b/src/shared/Auth/BigNumber.h new file mode 100644 index 00000000000..c66798afd85 --- /dev/null +++ b/src/shared/Auth/BigNumber.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _AUTH_BIGNUMBER_H +#define _AUTH_BIGNUMBER_H + +#include "Common.h" +#include "ByteBuffer.h" + +struct bignum_st; + +class BigNumber +{ + public: + BigNumber(); + BigNumber(const BigNumber &bn); + BigNumber(uint32); + ~BigNumber(); + + void SetDword(uint32); + void SetQword(uint64); + void SetBinary(const uint8 *bytes, int len); + void SetHexStr(const char *str); + + void SetRand(int numbits); + + BigNumber operator=(const BigNumber &bn); + + BigNumber operator+=(const BigNumber &bn); + BigNumber operator+(const BigNumber &bn) + { + BigNumber t(*this); + return t += bn; + } + BigNumber operator-=(const BigNumber &bn); + BigNumber operator-(const BigNumber &bn) + { + BigNumber t(*this); + return t -= bn; + } + BigNumber operator*=(const BigNumber &bn); + BigNumber operator*(const BigNumber &bn) + { + BigNumber t(*this); + return t *= bn; + } + BigNumber operator/=(const BigNumber &bn); + BigNumber operator/(const BigNumber &bn) + { + BigNumber t(*this); + return t /= bn; + } + BigNumber operator%=(const BigNumber &bn); + BigNumber operator%(const BigNumber &bn) + { + BigNumber t(*this); + return t %= bn; + } + + BigNumber ModExp(const BigNumber &bn1, const BigNumber &bn2); + BigNumber Exp(const BigNumber &); + + int GetNumBytes(void); + + struct bignum_st *BN() { return _bn; } + + uint32 AsDword(); + uint8* AsByteArray(int minSize = 0); + ByteBuffer AsByteBuffer(); + std::vector<uint8> AsByteVector(); + + const char *AsHexStr(); + const char *AsDecStr(); + + private: + struct bignum_st *_bn; + uint8 *_array; +}; +#endif diff --git a/src/shared/Auth/Hmac.cpp b/src/shared/Auth/Hmac.cpp new file mode 100644 index 00000000000..d2054a0a777 --- /dev/null +++ b/src/shared/Auth/Hmac.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Auth/Hmac.h" +#include "BigNumber.h" + +HmacHash::HmacHash() +{ + uint8 temp[SEED_KEY_SIZE] = { 0x38, 0xA7, 0x83, 0x15, 0xF8, 0x92, 0x25, 0x30, 0x71, 0x98, 0x67, 0xB1, 0x8C, 0x4, 0xE2, 0xAA }; + memcpy(&m_key, &temp, SEED_KEY_SIZE); + HMAC_CTX_init(&m_ctx); + HMAC_Init_ex(&m_ctx, &m_key, SEED_KEY_SIZE, EVP_sha1(), NULL); +} + +HmacHash::~HmacHash() +{ + memset(&m_key, 0x00, SEED_KEY_SIZE); + HMAC_CTX_cleanup(&m_ctx); +} + +void HmacHash::UpdateBigNumber(BigNumber *bn) +{ + UpdateData(bn->AsByteArray(), bn->GetNumBytes()); +} + +void HmacHash::UpdateData(const uint8 *data, int length) +{ + HMAC_Update(&m_ctx, data, length); +} + +void HmacHash::Initialize() +{ + HMAC_Init_ex(&m_ctx, &m_key, SEED_KEY_SIZE, EVP_sha1(), NULL); +} + +void HmacHash::Finalize() +{ + uint32 length = 0; + HMAC_Final(&m_ctx, m_digest, &length); + ASSERT(length == SHA_DIGEST_LENGTH) +} diff --git a/src/shared/Auth/Hmac.h b/src/shared/Auth/Hmac.h new file mode 100644 index 00000000000..083c84fe871 --- /dev/null +++ b/src/shared/Auth/Hmac.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _AUTH_HMAC_H +#define _AUTH_HMAC_H + +#include "Common.h" +#include <openssl/hmac.h> +#include <openssl/sha.h> + +class BigNumber; + +#define SEED_KEY_SIZE 16 + +class HmacHash +{ + public: + HmacHash(); + ~HmacHash(); + void UpdateBigNumber(BigNumber *bn); + void UpdateData(const uint8 *data, int length); + void Initialize(); + void Finalize(); + uint8 *GetDigest() { return m_digest; }; + int GetLength() { return SHA_DIGEST_LENGTH; }; + private: + HMAC_CTX m_ctx; + uint8 m_key[SEED_KEY_SIZE]; + uint8 m_digest[SHA_DIGEST_LENGTH]; +}; +#endif diff --git a/src/shared/Auth/Makefile.am b/src/shared/Auth/Makefile.am new file mode 100644 index 00000000000..6646568ee35 --- /dev/null +++ b/src/shared/Auth/Makefile.am @@ -0,0 +1,39 @@ +# Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +## Process this file with automake to produce Makefile.in + +## Sub-directories to parse + +## CPP flags for includes, defines, etc. +AM_CPPFLAGS = $(MYSQL_INCLUDES) $(POSTGRE_INCLUDES) -I$(top_builddir)/src/shared -I$(srcdir) -I$(srcdir)/../../../dep/include -I$(srcdir)/../../framework -I$(srcdir)/../../shared -I$(srcdir)/../../../dep/include/g3dlite + +## Build MaNGOS shared library and its parts as convenience library. +# All libraries will be convenience libraries. Might be changed to shared +# later. +noinst_LIBRARIES = libmangosauth.a + +libmangosauth_a_SOURCES = \ + AuthCrypt.cpp \ + AuthCrypt.h \ + BigNumber.cpp \ + BigNumber.h \ + Hmac.cpp \ + Hmac.h \ + Sha1.cpp \ + Sha1.h \ + md5.c \ + md5.h diff --git a/src/shared/Auth/Sha1.cpp b/src/shared/Auth/Sha1.cpp new file mode 100644 index 00000000000..6a4a3967b26 --- /dev/null +++ b/src/shared/Auth/Sha1.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Auth/Sha1.h" +#include <stdarg.h> + +Sha1Hash::Sha1Hash() +{ + SHA1_Init(&mC); +} + +Sha1Hash::~Sha1Hash() +{ + SHA1_Init(&mC); +} + +void Sha1Hash::UpdateData(const uint8 *dta, int len) +{ + SHA1_Update(&mC, dta, len); +} + +void Sha1Hash::UpdateData(const std::string &str) +{ + UpdateData((uint8 const*)str.c_str(), str.length()); +} + +void Sha1Hash::UpdateBigNumbers(BigNumber *bn0, ...) +{ + va_list v; + BigNumber *bn; + + va_start(v, bn0); + bn = bn0; + while (bn) + { + UpdateData(bn->AsByteArray(), bn->GetNumBytes()); + bn = va_arg(v, BigNumber *); + } + va_end(v); +} + +void Sha1Hash::Initialize() +{ + SHA1_Init(&mC); +} + +void Sha1Hash::Finalize(void) +{ + SHA1_Final(mDigest, &mC); +} diff --git a/src/shared/Auth/Sha1.h b/src/shared/Auth/Sha1.h new file mode 100644 index 00000000000..3be4bcb8159 --- /dev/null +++ b/src/shared/Auth/Sha1.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _AUTH_SHA1_H +#define _AUTH_SHA1_H + +#include "Common.h" +#include <openssl/sha.h> +#include <openssl/crypto.h> +#include "Auth/BigNumber.h" + +class Sha1Hash +{ + public: + Sha1Hash(); + ~Sha1Hash(); + + void UpdateFinalizeBigNumbers(BigNumber *bn0, ...); + void UpdateBigNumbers(BigNumber *bn0, ...); + + void UpdateData(const uint8 *dta, int len); + void UpdateData(const std::string &str); + + void Initialize(); + void Finalize(); + + uint8 *GetDigest(void) { return mDigest; }; + int GetLength(void) { return SHA_DIGEST_LENGTH; }; + + BigNumber GetBigNumber(); + + private: + SHA_CTX mC; + uint8 mDigest[SHA_DIGEST_LENGTH]; +}; +#endif diff --git a/src/shared/Auth/md5.c b/src/shared/Auth/md5.c new file mode 100644 index 00000000000..3e9735e2dbb --- /dev/null +++ b/src/shared/Auth/md5.c @@ -0,0 +1,385 @@ +/* + Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not +claim that you wrote the original software. If you use this software +in a product, an acknowledgment in the product documentation would be +appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be +misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. + +L. Peter Deutsch +ghost@aladdin.com + +*/ +/* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321, whose + text is available at + http://www.ietf.org/rfc/rfc1321.txt + The code is derived from the text of the RFC, including the test suite + (section A.5) but excluding the rest of Appendix A. It does not include + any code or documentation that is identified in the RFC as being + copyrighted. + +The original and principal author of md5.c is L. Peter Deutsch +<ghost@aladdin.com>. Other authors are noted in the change history +that follows (in reverse chronological order): + +2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order +either statically or dynamically; added missing #include <string.h> +in library. +2002-03-11 lpd Corrected argument list for main(), and added int return +type, in test program and T value program. +2002-02-21 lpd Added missing #include <stdio.h> in test program. +2000-07-03 lpd Patched to eliminate warnings about "constant is +unsigned in ANSI C, signed in traditional"; made test program +self-checking. +1999-11-04 lpd Edited comments slightly for automatic TOC extraction. +1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). +1999-05-03 lpd Original version. +*/ + +#include "md5.h" +#include <string.h> + +#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */ +#ifdef ARCH_IS_BIG_ENDIAN +# define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1) +#else +# define BYTE_ORDER 0 +#endif + +#define T_MASK ((md5_word_t)~0) +#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87) +#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9) +#define T3 0x242070db +#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111) +#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050) +#define T6 0x4787c62a +#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec) +#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe) +#define T9 0x698098d8 +#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850) +#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e) +#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841) +#define T13 0x6b901122 +#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c) +#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71) +#define T16 0x49b40821 +#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d) +#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf) +#define T19 0x265e5a51 +#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855) +#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2) +#define T22 0x02441453 +#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e) +#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437) +#define T25 0x21e1cde6 +#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829) +#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278) +#define T28 0x455a14ed +#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa) +#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07) +#define T31 0x676f02d9 +#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375) +#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd) +#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e) +#define T35 0x6d9d6122 +#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3) +#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb) +#define T38 0x4bdecfa9 +#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f) +#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f) +#define T41 0x289b7ec6 +#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805) +#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a) +#define T44 0x04881d05 +#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6) +#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a) +#define T47 0x1fa27cf8 +#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a) +#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb) +#define T50 0x432aff97 +#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58) +#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6) +#define T53 0x655b59c3 +#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d) +#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82) +#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e) +#define T57 0x6fa87e4f +#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f) +#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb) +#define T60 0x4e0811a1 +#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d) +#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca) +#define T63 0x2ad7d2bb +#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e) + +static void +md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) +{ + md5_word_t + a = pms->abcd[0], b = pms->abcd[1], + c = pms->abcd[2], d = pms->abcd[3]; + md5_word_t t; + #if BYTE_ORDER > 0 + /* Define storage only for big-endian CPUs. */ + md5_word_t X[16]; + #else + /* Define storage for little-endian or both types of CPUs. */ + md5_word_t xbuf[16]; + const md5_word_t *X; + #endif + + { + #if BYTE_ORDER == 0 + /* + * Determine dynamically whether this is a big-endian or + * little-endian machine, since we can use a more efficient + * algorithm on the latter. + */ + static const int w = 1; + + if (*((const md5_byte_t *)&w)) /* dynamic little-endian */ + #endif + #if BYTE_ORDER <= 0 /* little-endian */ + { + /* + * On little-endian machines, we can process properly aligned + * data without copying it. + */ + if (!((data - (const md5_byte_t *)0) & 3)) + { + /* data are properly aligned */ + X = (const md5_word_t *)data; + } + else + { + /* not aligned */ + memcpy(xbuf, data, 64); + X = xbuf; + } + } + #endif + #if BYTE_ORDER == 0 + else /* dynamic big-endian */ + #endif + #if BYTE_ORDER >= 0 /* big-endian */ + { + /* + * On big-endian machines, we must arrange the bytes in the + * right order. + */ + const md5_byte_t *xp = data; + int i; + + # if BYTE_ORDER == 0 + X = xbuf; /* (dynamic only) */ + # else + # define xbuf X /* (static only) */ + # endif + for (i = 0; i < 16; ++i, xp += 4) + xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); + } + #endif + } + + #define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) + + /* Round 1. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ + #define F(x, y, z) (((x) & (y)) | (~(x) & (z))) + #define SET(a, b, c, d, k, s, Ti)\ + t = a + F(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 7, T1); + SET(d, a, b, c, 1, 12, T2); + SET(c, d, a, b, 2, 17, T3); + SET(b, c, d, a, 3, 22, T4); + SET(a, b, c, d, 4, 7, T5); + SET(d, a, b, c, 5, 12, T6); + SET(c, d, a, b, 6, 17, T7); + SET(b, c, d, a, 7, 22, T8); + SET(a, b, c, d, 8, 7, T9); + SET(d, a, b, c, 9, 12, T10); + SET(c, d, a, b, 10, 17, T11); + SET(b, c, d, a, 11, 22, T12); + SET(a, b, c, d, 12, 7, T13); + SET(d, a, b, c, 13, 12, T14); + SET(c, d, a, b, 14, 17, T15); + SET(b, c, d, a, 15, 22, T16); + #undef SET + + /* Round 2. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ + #define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) + #define SET(a, b, c, d, k, s, Ti)\ + t = a + G(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 1, 5, T17); + SET(d, a, b, c, 6, 9, T18); + SET(c, d, a, b, 11, 14, T19); + SET(b, c, d, a, 0, 20, T20); + SET(a, b, c, d, 5, 5, T21); + SET(d, a, b, c, 10, 9, T22); + SET(c, d, a, b, 15, 14, T23); + SET(b, c, d, a, 4, 20, T24); + SET(a, b, c, d, 9, 5, T25); + SET(d, a, b, c, 14, 9, T26); + SET(c, d, a, b, 3, 14, T27); + SET(b, c, d, a, 8, 20, T28); + SET(a, b, c, d, 13, 5, T29); + SET(d, a, b, c, 2, 9, T30); + SET(c, d, a, b, 7, 14, T31); + SET(b, c, d, a, 12, 20, T32); + #undef SET + + /* Round 3. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ + #define H(x, y, z) ((x) ^ (y) ^ (z)) + #define SET(a, b, c, d, k, s, Ti)\ + t = a + H(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 5, 4, T33); + SET(d, a, b, c, 8, 11, T34); + SET(c, d, a, b, 11, 16, T35); + SET(b, c, d, a, 14, 23, T36); + SET(a, b, c, d, 1, 4, T37); + SET(d, a, b, c, 4, 11, T38); + SET(c, d, a, b, 7, 16, T39); + SET(b, c, d, a, 10, 23, T40); + SET(a, b, c, d, 13, 4, T41); + SET(d, a, b, c, 0, 11, T42); + SET(c, d, a, b, 3, 16, T43); + SET(b, c, d, a, 6, 23, T44); + SET(a, b, c, d, 9, 4, T45); + SET(d, a, b, c, 12, 11, T46); + SET(c, d, a, b, 15, 16, T47); + SET(b, c, d, a, 2, 23, T48); + #undef SET + + /* Round 4. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ + #define I(x, y, z) ((y) ^ ((x) | ~(z))) + #define SET(a, b, c, d, k, s, Ti)\ + t = a + I(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 6, T49); + SET(d, a, b, c, 7, 10, T50); + SET(c, d, a, b, 14, 15, T51); + SET(b, c, d, a, 5, 21, T52); + SET(a, b, c, d, 12, 6, T53); + SET(d, a, b, c, 3, 10, T54); + SET(c, d, a, b, 10, 15, T55); + SET(b, c, d, a, 1, 21, T56); + SET(a, b, c, d, 8, 6, T57); + SET(d, a, b, c, 15, 10, T58); + SET(c, d, a, b, 6, 15, T59); + SET(b, c, d, a, 13, 21, T60); + SET(a, b, c, d, 4, 6, T61); + SET(d, a, b, c, 11, 10, T62); + SET(c, d, a, b, 2, 15, T63); + SET(b, c, d, a, 9, 21, T64); + #undef SET + + /* Then perform the following additions. (That is increment each + of the four registers by the value it had before this block + was started.) */ + pms->abcd[0] += a; + pms->abcd[1] += b; + pms->abcd[2] += c; + pms->abcd[3] += d; +} + +void +md5_init(md5_state_t *pms) +{ + pms->count[0] = pms->count[1] = 0; + pms->abcd[0] = 0x67452301; + pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476; + pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301; + pms->abcd[3] = 0x10325476; +} + +void +md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes) +{ + const md5_byte_t *p = data; + int left = nbytes; + int offset = (pms->count[0] >> 3) & 63; + md5_word_t nbits = (md5_word_t)(nbytes << 3); + + if (nbytes <= 0) + return; + + /* Update the message length. */ + pms->count[1] += nbytes >> 29; + pms->count[0] += nbits; + if (pms->count[0] < nbits) + ++pms->count[1]; + + /* Process an initial partial block. */ + if (offset) + { + int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); + + memcpy(pms->buf + offset, p, copy); + if (offset + copy < 64) + return; + p += copy; + left -= copy; + md5_process(pms, pms->buf); + } + + /* Process full blocks. */ + for (; left >= 64; p += 64, left -= 64) + md5_process(pms, p); + + /* Process a final partial block. */ + if (left) + memcpy(pms->buf, p, left); +} + +void +md5_finish(md5_state_t *pms, md5_byte_t digest[16]) +{ + static const md5_byte_t pad[64] = + { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + md5_byte_t data[8]; + int i; + + /* Save the length before padding. */ + for (i = 0; i < 8; ++i) + data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); + /* Pad to 56 bytes mod 64. */ + md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); + /* Append the length. */ + md5_append(pms, data, 8); + for (i = 0; i < 16; ++i) + digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); +} diff --git a/src/shared/Auth/md5.h b/src/shared/Auth/md5.h new file mode 100644 index 00000000000..fa2937e13e4 --- /dev/null +++ b/src/shared/Auth/md5.h @@ -0,0 +1,91 @@ +/* + Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not +claim that you wrote the original software. If you use this software +in a product, an acknowledgment in the product documentation would be +appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be +misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. + +L. Peter Deutsch +ghost@aladdin.com + +*/ +/* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321, whose + text is available at + http://www.ietf.org/rfc/rfc1321.txt + The code is derived from the text of the RFC, including the test suite + (section A.5) but excluding the rest of Appendix A. It does not include + any code or documentation that is identified in the RFC as being + copyrighted. + +The original and principal author of md5.h is L. Peter Deutsch +<ghost@aladdin.com>. Other authors are noted in the change history +that follows (in reverse chronological order): + +2002-04-13 lpd Removed support for non-ANSI compilers; removed +references to Ghostscript; clarified derivation from RFC 1321; +now handles byte order either statically or dynamically. +1999-11-04 lpd Edited comments slightly for automatic TOC extraction. +1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); +added conditionalization for C++ compilation from Martin +Purschke <purschke@bnl.gov>. +1999-05-03 lpd Original version. +*/ + +#ifndef md5_INCLUDED +# define md5_INCLUDED + +/* + * This package supports both compile-time and run-time determination of CPU + * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be + * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is + * defined as non-zero, the code will be compiled to run only on big-endian + * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to + * run on either big- or little-endian CPUs, but will run slightly less + * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined. + */ + +typedef unsigned char md5_byte_t; /* 8-bit byte */ +typedef unsigned int md5_word_t; /* 32-bit word */ + +/* Define the state of the MD5 Algorithm. */ +typedef struct md5_state_s +{ + md5_word_t count[2]; /* message length in bits, lsw first */ + md5_word_t abcd[4]; /* digest buffer */ + md5_byte_t buf[64]; /* accumulate block */ +} md5_state_t; + +#ifdef __cplusplus +extern "C" +{ + #endif + + /* Initialize the algorithm. */ + void md5_init(md5_state_t *pms); + + /* Append a string to the message. */ + void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); + + /* Finish the message and return the digest. */ + void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); + + #ifdef __cplusplus +} /* end extern "C" */ +#endif +#endif /* md5_INCLUDED */ diff --git a/src/shared/Base.cpp b/src/shared/Base.cpp new file mode 100644 index 00000000000..9929cd41fdf --- /dev/null +++ b/src/shared/Base.cpp @@ -0,0 +1,67 @@ +/* + Base class interface + Copyright (C) 1998,1999 by Andrew Zabolotny + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "Base.h" + +Base::~Base () +{ +} + +/** + * Decrement object's reference count; as soon as the last reference + * to the object is removed, it is destroyed. + */ + +void Base::DecRef () +{ + if (!--RefCount) + delete this; +} + +/** + * Object initialization. The initial reference count is set to one; + * this means if you call DecRef() immediately after creating the object, + * it will be destroyed. + */ +Base::Base () +{ + RefCount = 1; +} + +/** + * Increment reference count. + * Every time when you copy a pointer to a object and store it for + * later use you MUST call IncRef() on it; this will allow to keep + * objects as long as they are referenced by some entity. + */ +void Base::IncRef () +{ + ++RefCount; + +} + +/** + * Query number of references to this object. + * I would rather prefer to have the reference counter strictly private, + * but sometimes, mostly for debugging, such a function can help. + */ +int Base::GetRefCount () +{ + return RefCount; +} diff --git a/src/shared/Base.h b/src/shared/Base.h new file mode 100644 index 00000000000..d5907fdf473 --- /dev/null +++ b/src/shared/Base.h @@ -0,0 +1,54 @@ +/* + Base class interface + Copyright (C) 1998,1999 by Andrew Zabolotny + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __BASE_H__ +#define __BASE_H__ + +#include "Common.h" + +/** + * This class is intended to be a base class for every other class. + * It defines the basic interface available for any object. + */ +class Base +{ + private: + /// Object reference count + int RefCount; + + protected: + /** + * Destroy this object. Destructor is virtual, because class contains + * virtual methods; also it is private because it is never intended + * to be called directly; use DecRef() instead: when reference counter + * reaches zero, the object will be destroyed. + */ + virtual ~Base (); + + public: + + Base (); + + void IncRef (); + + void DecRef (); + int GetRefCount (); + +}; +#endif // __BASE_H__ diff --git a/src/shared/ByteBuffer.h b/src/shared/ByteBuffer.h new file mode 100644 index 00000000000..d89466c2b1f --- /dev/null +++ b/src/shared/ByteBuffer.h @@ -0,0 +1,479 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _BYTEBUFFER_H +#define _BYTEBUFFER_H + +#include "Common.h" +#include "Errors.h" +#include "Log.h" +#include "Utilities/ByteConverter.h" + +class ByteBuffer +{ + public: + const static size_t DEFAULT_SIZE = 0x1000; + + // constructor + ByteBuffer(): _rpos(0), _wpos(0) + { + _storage.reserve(DEFAULT_SIZE); + } + // constructor + ByteBuffer(size_t res): _rpos(0), _wpos(0) + { + _storage.reserve(res); + } + // copy constructor + ByteBuffer(const ByteBuffer &buf): _rpos(buf._rpos), _wpos(buf._wpos), _storage(buf._storage) { } + + void clear() + { + _storage.clear(); + _rpos = _wpos = 0; + } + + template <typename T> void append(T value) + { + EndianConvert(value); + append((uint8 *)&value, sizeof(value)); + } + + template <typename T> void put(size_t pos,T value) + { + EndianConvert(value); + put(pos,(uint8 *)&value,sizeof(value)); + } + + ByteBuffer &operator<<(uint8 value) + { + append<uint8>(value); + return *this; + } + ByteBuffer &operator<<(uint16 value) + { + append<uint16>(value); + return *this; + } + ByteBuffer &operator<<(uint32 value) + { + append<uint32>(value); + return *this; + } + ByteBuffer &operator<<(uint64 value) + { + append<uint64>(value); + return *this; + } + + // signed as in 2e complement + ByteBuffer &operator<<(int8 value) + { + append<int8>(value); + return *this; + } + ByteBuffer &operator<<(int16 value) + { + append<int16>(value); + return *this; + } + ByteBuffer &operator<<(int32 value) + { + append<int32>(value); + return *this; + } + ByteBuffer &operator<<(int64 value) + { + append<int64>(value); + return *this; + } + + // floating points + ByteBuffer &operator<<(float value) + { + append<float>(value); + return *this; + } + ByteBuffer &operator<<(double value) + { + append<double>(value); + return *this; + } + ByteBuffer &operator<<(const std::string &value) + { + append((uint8 const *)value.c_str(), value.length()); + append((uint8)0); + return *this; + } + ByteBuffer &operator<<(const char *str) + { + append((uint8 const *)str, str ? strlen(str) : 0); + append((uint8)0); + return *this; + } + + ByteBuffer &operator>>(bool &value) + { + value = read<char>() > 0 ? true : false; + return *this; + } + + ByteBuffer &operator>>(uint8 &value) + { + value = read<uint8>(); + return *this; + } + ByteBuffer &operator>>(uint16 &value) + { + value = read<uint16>(); + return *this; + } + ByteBuffer &operator>>(uint32 &value) + { + value = read<uint32>(); + return *this; + } + ByteBuffer &operator>>(uint64 &value) + { + value = read<uint64>(); + return *this; + } + + //signed as in 2e complement + ByteBuffer &operator>>(int8 &value) + { + value = read<int8>(); + return *this; + } + ByteBuffer &operator>>(int16 &value) + { + value = read<int16>(); + return *this; + } + ByteBuffer &operator>>(int32 &value) + { + value = read<int32>(); + return *this; + } + ByteBuffer &operator>>(int64 &value) + { + value = read<int64>(); + return *this; + } + + ByteBuffer &operator>>(float &value) + { + value = read<float>(); + return *this; + } + ByteBuffer &operator>>(double &value) + { + value = read<double>(); + return *this; + } + ByteBuffer &operator>>(std::string& value) + { + value.clear(); + while (rpos() < size()) // prevent crash at wrong string format in packet + { + char c=read<char>(); + if (c==0) + break; + value+=c; + } + return *this; + } + + uint8 operator[](size_t pos) + { + return read<uint8>(pos); + } + + size_t rpos() const { return _rpos; } + + size_t rpos(size_t rpos_) + { + _rpos = rpos_; + return _rpos; + }; + + size_t wpos() const { return _wpos; } + + size_t wpos(size_t wpos_) + { + _wpos = wpos_; + return _wpos; + } + + template <typename T> T read() + { + T r=read<T>(_rpos); + _rpos += sizeof(T); + return r; + }; + template <typename T> T read(size_t pos) const + { + ASSERT(pos + sizeof(T) <= size() || PrintPosError(false,pos,sizeof(T))); + T val = *((T const*)&_storage[pos]); + EndianConvert(val); + return val; + } + + void read(uint8 *dest, size_t len) + { + ASSERT(_rpos + len <= size() || PrintPosError(false,_rpos,len)); + memcpy(dest, &_storage[_rpos], len); + _rpos += len; + } + + const uint8 *contents() const { return &_storage[0]; } + + size_t size() const { return _storage.size(); } + bool empty() const { return _storage.empty(); } + + void resize(size_t newsize) + { + _storage.resize(newsize); + _rpos = 0; + _wpos = size(); + }; + void reserve(size_t ressize) + { + if (ressize > size()) _storage.reserve(ressize); + }; + + void append(const std::string& str) + { + append((uint8 const*)str.c_str(),str.size() + 1); + } + void append(const char *src, size_t cnt) + { + return append((const uint8 *)src, cnt); + } + template<class T> + void append(const T *src, size_t cnt) + { + return append((const uint8 *)src, cnt*sizeof(T)); + } + void append(const uint8 *src, size_t cnt) + { + if (!cnt) return; + + ASSERT(size() < 10000000); + + if (_storage.size() < _wpos + cnt) + _storage.resize(_wpos + cnt); + memcpy(&_storage[_wpos], src, cnt); + _wpos += cnt; + } + void append(const ByteBuffer& buffer) + { + if(buffer.size()) append(buffer.contents(),buffer.size()); + } + + void appendPackGUID(uint64 guid) + { + size_t mask_position = wpos(); + *this << uint8(0); + for(uint8 i = 0; i < 8; i++) + { + if(guid & 0xFF) + { + _storage[mask_position] |= uint8(1<<i); + *this << uint8(guid & 0xFF); + } + + guid >>= 8; + } + } + + void put(size_t pos, const uint8 *src, size_t cnt) + { + ASSERT(pos + cnt <= size() || PrintPosError(true,pos,cnt)); + memcpy(&_storage[pos], src, cnt); + } + void print_storage() + { + if(!sLog.IsOutDebug()) // optimize disabled debug output + return; + + sLog.outDebug("STORAGE_SIZE: %u", size() ); + for(uint32 i = 0; i < size(); i++) + sLog.outDebugInLine("%u - ", read<uint8>(i) ); + sLog.outDebug(" "); + } + + void textlike() + { + if(!sLog.IsOutDebug()) // optimize disabled debug output + return; + + sLog.outDebug("STORAGE_SIZE: %u", size() ); + for(uint32 i = 0; i < size(); i++) + sLog.outDebugInLine("%c", read<uint8>(i) ); + sLog.outDebug(" "); + } + + void hexlike() + { + if(!sLog.IsOutDebug()) // optimize disabled debug output + return; + + uint32 j = 1, k = 1; + sLog.outDebug("STORAGE_SIZE: %u", size() ); + + if(sLog.IsIncludeTime()) + sLog.outDebugInLine(" "); + + for(uint32 i = 0; i < size(); i++) + { + if ((i == (j*8)) && ((i != (k*16)))) + { + if (read<uint8>(i) < 0x0F) + { + sLog.outDebugInLine("| 0%X ", read<uint8>(i) ); + } + else + { + sLog.outDebugInLine("| %X ", read<uint8>(i) ); + } + ++j; + } + else if (i == (k*16)) + { + if (read<uint8>(i) < 0x0F) + { + sLog.outDebugInLine("\n"); + if(sLog.IsIncludeTime()) + sLog.outDebugInLine(" "); + + sLog.outDebugInLine("0%X ", read<uint8>(i) ); + } + else + { + sLog.outDebugInLine("\n"); + if(sLog.IsIncludeTime()) + sLog.outDebugInLine(" "); + + sLog.outDebugInLine("%X ", read<uint8>(i) ); + } + + ++k; + ++j; + } + else + { + if (read<uint8>(i) < 0x0F) + { + sLog.outDebugInLine("0%X ", read<uint8>(i) ); + } + else + { + sLog.outDebugInLine("%X ", read<uint8>(i) ); + } + } + } + sLog.outDebugInLine("\n"); + } + + protected: + bool PrintPosError(bool add, size_t pos, size_t esize) const + { + sLog.outError("ERROR: Attempt %s in ByteBuffer (pos: %u size: %u) value with size: %u",(add ? "put" : "get"),pos, size(), esize); + + // assert must fail after function call + return false; + } + + size_t _rpos, _wpos; + std::vector<uint8> _storage; +}; + +template <typename T> ByteBuffer &operator<<(ByteBuffer &b, std::vector<T> v) +{ + b << (uint32)v.size(); + for (typename std::vector<T>::iterator i = v.begin(); i != v.end(); i++) + { + b << *i; + } + return b; +} + +template <typename T> ByteBuffer &operator>>(ByteBuffer &b, std::vector<T> &v) +{ + uint32 vsize; + b >> vsize; + v.clear(); + while(vsize--) + { + T t; + b >> t; + v.push_back(t); + } + return b; +} + +template <typename T> ByteBuffer &operator<<(ByteBuffer &b, std::list<T> v) +{ + b << (uint32)v.size(); + for (typename std::list<T>::iterator i = v.begin(); i != v.end(); i++) + { + b << *i; + } + return b; +} + +template <typename T> ByteBuffer &operator>>(ByteBuffer &b, std::list<T> &v) +{ + uint32 vsize; + b >> vsize; + v.clear(); + while(vsize--) + { + T t; + b >> t; + v.push_back(t); + } + return b; +} + +template <typename K, typename V> ByteBuffer &operator<<(ByteBuffer &b, std::map<K, V> &m) +{ + b << (uint32)m.size(); + for (typename std::map<K, V>::iterator i = m.begin(); i != m.end(); i++) + { + b << i->first << i->second; + } + return b; +} + +template <typename K, typename V> ByteBuffer &operator>>(ByteBuffer &b, std::map<K, V> &m) +{ + uint32 msize; + b >> msize; + m.clear(); + while(msize--) + { + K k; + V v; + b >> k >> v; + m.insert(make_pair(k, v)); + } + return b; +} +#endif diff --git a/src/shared/Common.cpp b/src/shared/Common.cpp new file mode 100644 index 00000000000..d64404a6e91 --- /dev/null +++ b/src/shared/Common.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.h" + +char const* localeNames[MAX_LOCALE] = { + "enUS", + "koKR", + "frFR", + "deDE", + "zhCN", + "zhTW", + "esES", + "esMX", + "ruRU" +}; + +LocaleConstant GetLocaleByName(std::string name) +{ + for(uint32 i = 0; i < MAX_LOCALE; ++i) + if(name==localeNames[i]) + return LocaleConstant(i); + + return LOCALE_enUS; // including enGB case +} diff --git a/src/shared/Common.h b/src/shared/Common.h new file mode 100644 index 00000000000..4e93ad71eb2 --- /dev/null +++ b/src/shared/Common.h @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOSSERVER_COMMON_H +#define MANGOSSERVER_COMMON_H + +#include "Platform/Define.h" + +#if COMPILER == COMPILER_MICROSOFT + +#pragma warning(disable:4996) + +#ifndef __SHOW_STUPID_WARNINGS__ + +#pragma warning(disable:4244) + +#pragma warning(disable:4267) + +#pragma warning(disable:4800) + +#pragma warning(disable:4018) + +#pragma warning(disable:4311) + +#pragma warning(disable:4305) + +#pragma warning(disable:4005) +#endif // __SHOW_STUPID_WARNINGS__ +#endif // __GNUC__ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "Utilities/HashMap.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <math.h> +#include <errno.h> +#include <signal.h> + +#if PLATFORM == PLATFORM_WINDOWS +#define STRCASECMP stricmp +#else +#define STRCASECMP strcasecmp +#endif + +#include <set> +#include <list> +#include <string> +#include <map> +#include <queue> +#include <sstream> +#include <algorithm> + +#include <zthread/FastMutex.h> +#include <zthread/LockedQueue.h> +#include <zthread/Runnable.h> +#include <zthread/Thread.h> + +#if PLATFORM == PLATFORM_WINDOWS +# define FD_SETSIZE 1024 +# include <winsock2.h> +// XP winver - needed to compile with standard leak check in MemoryLeaks.h +// uncomment later if needed +//#define _WIN32_WINNT 0x0501 +# include <ws2tcpip.h> +//#undef WIN32_WINNT +#else +# include <sys/types.h> +# include <sys/ioctl.h> +# include <sys/socket.h> +# include <netinet/in.h> +# include <unistd.h> +# include <netdb.h> +#endif + +#if COMPILER == COMPILER_MICROSOFT + +#include <float.h> + +#define I64FMT "%016I64X" +#define I64FMTD "%I64u" +#define SI64FMTD "%I64d" +#define snprintf _snprintf +#define atoll __atoi64 +#define vsnprintf _vsnprintf +#define strdup _strdup +#define finite(X) _finite(X) + +#else + +#define stricmp strcasecmp +#define strnicmp strncasecmp +#define I64FMT "%016llX" +#define I64FMTD "%llu" +#define SI64FMTD "%lld" +#endif + +inline float finiteAlways(float f) { return finite(f) ? f : 0.0f; } + +#define atol(a) strtoul( a, NULL, 10) + +#define STRINGIZE(a) #a + +enum TimeConstants +{ + MINUTE = 60, + HOUR = MINUTE*60, + DAY = HOUR*24, + MONTH = DAY*30 +}; + +enum AccountTypes +{ + SEC_PLAYER = 0, + SEC_MODERATOR = 1, + SEC_GAMEMASTER = 2, + SEC_ADMINISTRATOR = 3 +}; + +enum LocaleConstant +{ + LOCALE_enUS = 0, + LOCALE_koKR = 1, + LOCALE_frFR = 2, + LOCALE_deDE = 3, + LOCALE_zhCN = 4, + LOCALE_zhTW = 5, + LOCALE_esES = 6, + LOCALE_esMX = 7, + LOCALE_ruRU = 8 +}; + +#define MAX_LOCALE 9 + +extern char const* localeNames[MAX_LOCALE]; + +LocaleConstant GetLocaleByName(std::string name); + +// we always use stdlibc++ std::max/std::min, undefine some not C++ standard defines (Win API and some pother platforms) +#ifdef max +#undef max +#endif + +#ifdef min +#undef min +#endif + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +#endif diff --git a/src/shared/Config/Config.cpp b/src/shared/Config/Config.cpp new file mode 100644 index 00000000000..292cf390276 --- /dev/null +++ b/src/shared/Config/Config.cpp @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ConfigEnv.h" +#include "Policies/SingletonImp.h" + +INSTANTIATE_SINGLETON_1(Config); + +Config::Config() : mIgnoreCase(true), mConf(NULL) +{ +} + + +Config::~Config() +{ + delete mConf; +} + + +bool Config::SetSource(const char *file, bool ignorecase) +{ + mIgnoreCase = ignorecase; + mFilename = file; + + return Reload(); +} + +bool Config::Reload() +{ + delete mConf; + + mConf = new DOTCONFDocument(mIgnoreCase ? + DOTCONFDocument::CASEINSENSETIVE : + DOTCONFDocument::CASESENSETIVE); + + if (mConf->setContent(mFilename.c_str()) == -1) + { + delete mConf; + mConf = NULL; + return false; + } + + return true; +} + +bool Config::GetString(const char* name, std::string *value) +{ + if(!mConf) + return false; + + DOTCONFDocumentNode const *node = mConf->findNode(name); + if(!node || !node->getValue()) + return false; + + *value = node->getValue(); + + return true; +} + +bool Config::GetString(const char* name, char const **value) +{ + if(!mConf) + return false; + + DOTCONFDocumentNode const *node = mConf->findNode(name); + if(!node || !node->getValue()) + return false; + + *value = node->getValue(); + + return true; +} + + +std::string Config::GetStringDefault(const char* name, const char* def) +{ + if(!mConf) + return std::string(def); + + DOTCONFDocumentNode const *node = mConf->findNode(name); + if(!node || !node->getValue()) + return std::string(def); + + return std::string(node->getValue()); +} + + +bool Config::GetBool(const char* name, bool *value) +{ + if(!mConf) + return false; + + DOTCONFDocumentNode const *node = mConf->findNode(name); + if(!node || !node->getValue()) + return false; + + const char* str = node->getValue(); + if(strcmp(str, "true") == 0 || strcmp(str, "TRUE") == 0 || + strcmp(str, "yes") == 0 || strcmp(str, "YES") == 0 || + strcmp(str, "1") == 0) + { + *value = true; + } + else + *value = false; + + return true; +} + + +bool Config::GetBoolDefault(const char* name, const bool def) +{ + bool val; + return GetBool(name, &val) ? val : def; +} + + +bool Config::GetInt(const char* name, int *value) +{ + if(!mConf) + return false; + + DOTCONFDocumentNode const *node = mConf->findNode(name); + if(!node || !node->getValue()) + return false; + + *value = atoi(node->getValue()); + + return true; +} + + +bool Config::GetFloat(const char* name, float *value) +{ + if(!mConf) + return false; + + DOTCONFDocumentNode const *node = mConf->findNode(name); + if(!node || !node->getValue()) + return false; + + *value = atof(node->getValue()); + + return true; +} + + +int Config::GetIntDefault(const char* name, const int def) +{ + int val; + return GetInt(name, &val) ? val : def; +} + + +float Config::GetFloatDefault(const char* name, const float def) +{ + float val; + return (GetFloat(name, &val) ? val : def); +} diff --git a/src/shared/Config/Config.h b/src/shared/Config/Config.h new file mode 100644 index 00000000000..bc753f6d33a --- /dev/null +++ b/src/shared/Config/Config.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef CONFIG_H +#define CONFIG_H + +#include <Policies/Singleton.h> +#include "Platform/Define.h" + +class DOTCONFDocument; + +class MANGOS_DLL_SPEC Config +{ + public: + Config(); + ~Config(); + + bool SetSource(const char *file, bool ignorecase = true); + bool Reload(); + + bool GetString(const char* name, std::string *value); + bool GetString(const char* name, char const **value); + std::string GetStringDefault(const char* name, const char* def); + + bool GetBool(const char* name, bool *value); + bool GetBoolDefault(const char* name, const bool def = false); + + bool GetInt(const char* name, int *value); + int GetIntDefault(const char* name, const int def); + + bool GetFloat(const char* name, float *value); + float GetFloatDefault(const char* name, const float def); + + std::string GetFilename() const { return mFilename; } + private: + std::string mFilename; + bool mIgnoreCase; + DOTCONFDocument *mConf; +}; + +#define sConfig MaNGOS::Singleton<Config>::Instance() + +#endif diff --git a/src/shared/Config/ConfigEnv.h b/src/shared/Config/ConfigEnv.h new file mode 100644 index 00000000000..7ef06f015a2 --- /dev/null +++ b/src/shared/Config/ConfigEnv.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined(CONFIGENVIRONMENT_H) + +#define CONFIGENVIRONMENT_H + +#include "Common.h" +#include "dotconfpp/dotconfpp.h" +#include "Config.h" +#include "Log.h" + +#endif diff --git a/src/shared/Config/ConfigLibrary.vcproj b/src/shared/Config/ConfigLibrary.vcproj new file mode 100644 index 00000000000..a8f1fc322e7 --- /dev/null +++ b/src/shared/Config/ConfigLibrary.vcproj @@ -0,0 +1,137 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="7.10" + Name="ConfigLibrary" + ProjectGUID="{C849D54F-32A6-4025-95BE-E64D1CF0686E}" + Keyword="Win32Proj"> + <Platforms> + <Platform + Name="Win32"/> + </Platforms> + <Configurations> + <Configuration + Name="Debug|Win32" + OutputDirectory="Debug" + IntermediateDirectory="Debug" + ConfigurationType="4" + CharacterSet="2"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="" + PreprocessorDefinitions="WIN32;_DEBUG;_LIB" + MinimalRebuild="TRUE" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + UsePrecompiledHeader="0" + WarningLevel="3" + Detect64BitPortabilityProblems="TRUE" + DebugInformationFormat="4"/> + <Tool + Name="VCCustomBuildTool"/> + <Tool + Name="VCLibrarianTool" + OutputFile="$(OutDir)/ConfigLibrary.lib"/> + <Tool + Name="VCMIDLTool"/> + <Tool + Name="VCPostBuildEventTool"/> + <Tool + Name="VCPreBuildEventTool"/> + <Tool + Name="VCPreLinkEventTool"/> + <Tool + Name="VCResourceCompilerTool"/> + <Tool + Name="VCWebServiceProxyGeneratorTool"/> + <Tool + Name="VCXMLDataGeneratorTool"/> + <Tool + Name="VCManagedWrapperGeneratorTool"/> + <Tool + Name="VCAuxiliaryManagedWrapperGeneratorTool"/> + </Configuration> + <Configuration + Name="Release|Win32" + OutputDirectory="Release" + IntermediateDirectory="Release" + ConfigurationType="4" + CharacterSet="2"> + <Tool + Name="VCCLCompilerTool" + AdditionalIncludeDirectories="" + PreprocessorDefinitions="WIN32;NDEBUG;_LIB" + RuntimeLibrary="0" + UsePrecompiledHeader="0" + WarningLevel="3" + Detect64BitPortabilityProblems="TRUE" + DebugInformationFormat="3"/> + <Tool + Name="VCCustomBuildTool"/> + <Tool + Name="VCLibrarianTool" + OutputFile="$(OutDir)/ConfigLibrary.lib"/> + <Tool + Name="VCMIDLTool"/> + <Tool + Name="VCPostBuildEventTool"/> + <Tool + Name="VCPreBuildEventTool"/> + <Tool + Name="VCPreLinkEventTool"/> + <Tool + Name="VCResourceCompilerTool"/> + <Tool + Name="VCWebServiceProxyGeneratorTool"/> + <Tool + Name="VCXMLDataGeneratorTool"/> + <Tool + Name="VCManagedWrapperGeneratorTool"/> + <Tool + Name="VCAuxiliaryManagedWrapperGeneratorTool"/> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="Source Files" + Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx" + UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"> + <File + RelativePath=".\Config.cpp"> + </File> + <File + RelativePath=".\dotconfpp\dotconfpp.cpp"> + </File> + <File + RelativePath=".\dotconfpp\mempool.cpp"> + </File> + </Filter> + <Filter + Name="Header Files" + Filter="h;hpp;hxx;hm;inl;inc;xsd" + UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"> + <File + RelativePath=".\Config.h"> + </File> + <File + RelativePath=".\ConfigEnv.h"> + </File> + <File + RelativePath=".\dotconfpp\dotconfpp.h"> + </File> + <File + RelativePath=".\dotconfpp\mempool.h"> + </File> + </Filter> + <Filter + Name="Resource Files" + Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx" + UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"> + </Filter> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/src/shared/Config/Makefile.am b/src/shared/Config/Makefile.am new file mode 100644 index 00000000000..e36f80a2fc4 --- /dev/null +++ b/src/shared/Config/Makefile.am @@ -0,0 +1,40 @@ +# Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +## Process this file with automake to produce Makefile.in + +## Sub-directories to parse + +## CPP flags for includes, defines, etc. +AM_CPPFLAGS = $(MYSQL_INCLUDES) $(POSTGRE_INCLUDES) -I$(top_builddir)/src/shared -I$(srcdir) -I$(srcdir)/../../../dep/include -I$(srcdir)/../../framework -I$(srcdir)/../../shared -I$(srcdir)/../../../dep/include/g3dlite + +## Build MaNGOS shared library and its parts as convenience library. +# All libraries will be convenience libraries. Might be changed to shared +# later. +noinst_LIBRARIES = libmangosconfig.a + +libmangosconfig_a_SOURCES = \ + dotconfpp/dotconfpp.cpp \ + dotconfpp/dotconfpp.h \ + dotconfpp/mempool.cpp \ + dotconfpp/mempool.h \ + Config.cpp \ + Config.h \ + ConfigEnv.h + +# VC++ project workspace for dotconfpp +EXTRA_DIST = \ + ConfigLibrary.vcproj diff --git a/src/shared/Config/dotconfpp/dotconfpp.cpp b/src/shared/Config/dotconfpp/dotconfpp.cpp new file mode 100644 index 00000000000..60008747ad1 --- /dev/null +++ b/src/shared/Config/dotconfpp/dotconfpp.cpp @@ -0,0 +1,583 @@ + +#include "Common.h" + +#include "dotconfpp.h" + +#if !defined(R_OK) +#define R_OK 04 +#endif + +DOTCONFDocumentNode::DOTCONFDocumentNode():previousNode(NULL), nextNode(NULL), parentNode(NULL), childNode(NULL), + values(NULL), valuesCount(0), + name(NULL), lineNum(0), fileName(NULL), closed(true) +{ +} + +DOTCONFDocumentNode::~DOTCONFDocumentNode() +{ + free(name); + if(values != NULL){ + for(int i = 0 ; i < valuesCount; i++){ + free(values[i]); + } + free(values); + } +} + +void DOTCONFDocumentNode::pushValue(char * _value) +{ + ++valuesCount; + values = (char**)realloc(values, valuesCount*sizeof(char*)); + values[valuesCount-1] = strdup(_value); +} + +const char* DOTCONFDocumentNode::getValue(int index) const +{ + if(index >= valuesCount){ + return NULL; + } + return values[index]; +} + +DOTCONFDocument::DOTCONFDocument(DOTCONFDocument::CaseSensitive caseSensitivity): + mempool(NULL), + curParent(NULL), curPrev(NULL), curLine(0), file(NULL), fileName(NULL) +{ + if(caseSensitivity == CASESENSETIVE){ + cmp_func = strcmp; + } else { + cmp_func = strcasecmp; + } + + mempool = new AsyncDNSMemPool(1024); + mempool->initialize(); +} + +DOTCONFDocument::~DOTCONFDocument() +{ + for(std::list<DOTCONFDocumentNode*>::iterator i = nodeTree.begin(); i != nodeTree.end(); i++){ + delete(*i); + } + for(std::list<char*>::iterator i = requiredOptions.begin(); i != requiredOptions.end(); i++){ + free(*i); + } + for(std::list<char*>::iterator i = processedFiles.begin(); i != processedFiles.end(); i++){ + free(*i); + } + free(fileName); + delete mempool; +} + +int DOTCONFDocument::cleanupLine(char * line) +{ + char * start = line; + char * bg = line; + bool multiline = false; + bool concat = false; + char * word = NULL; + + if(!words.empty() && quoted) + concat = true; + + while(*line){ + if((*line == '#' || *line == ';') && !quoted){ + *bg = 0; + if(strlen(start)){ + + if(concat){ + word = (char*)mempool->alloc(strlen(words.back())+strlen(start)+1); + strcpy(word, words.back()); + strcat(word, start); + words.pop_back(); + concat = false; + } else { + word = mempool->strdup(start); + } + words.push_back(word); + } + break; + } + if(*line == '=' && !quoted){ + *line = ' ';continue; + } + + // Allowing \" in there causes problems with directory paths + // like "C:\MaNGOS\" + //if(*line == '\\' && (*(line+1) == '"' || *(line+1) == '\'')){ + if(*line == '\\' && (*(line+1) == '\'')) { + *bg++ = *(line+1); + line+=2; continue; + } + if(*line == '\\' && *(line+1) == 'n'){ + *bg++ = '\n'; + line+=2; continue; + } + if(*line == '\\' && *(line+1) == 'r'){ + *bg++ = '\r'; + line+=2; continue; + } + if(*line == '\\' && (*(line+1) == '\n' || *(line+1) == '\r')){ + *bg = 0; + if(strlen(start)){ + + if(concat){ + word = (char*)mempool->alloc(strlen(words.back())+strlen(start)+1); + strcpy(word, words.back()); + strcat(word, start); + words.pop_back(); + concat = false; + } else { + word = mempool->strdup(start); + } + words.push_back(word); + } + multiline = true; + break; + } + if(*line == '"' || *line == '\''){ + quoted = !quoted; + ++line; continue; + } + if(isspace(*line) && !quoted){ + *bg++ = 0; + if(strlen(start)){ + + if(concat){ + word = (char*)mempool->alloc(strlen(words.back())+strlen(start)+1); + strcpy(word, words.back()); + strcat(word, start); + words.pop_back(); + concat = false; + } else { + word = mempool->strdup(start); + } + words.push_back(word); + } + start = bg; + while(isspace(*++line)) {} + + continue; + } + *bg++ = *line++; + } + + if(quoted && !multiline){ + error(curLine, fileName, "unterminated quote"); + return -1; + } + + return multiline?1:0; +} + +int DOTCONFDocument::parseLine() +{ + char * word = NULL; + char * nodeName = NULL; + char * nodeValue = NULL; + DOTCONFDocumentNode * tagNode = NULL; + bool newNode = false; + + for(std::list<char*>::iterator i = words.begin(); i != words.end(); i++) { + word = *i; + + if(*word == '<'){ + newNode = true; + } + + if(newNode){ + nodeValue = NULL; + nodeName = NULL; + newNode = false; + } + + size_t wordLen = strlen(word); + if(word[wordLen-1] == '>'){ + word[wordLen-1] = 0; + newNode = true; + } + + if(nodeName == NULL){ + nodeName = word; + bool closed = true; + if(*nodeName == '<'){ + if(*(nodeName+1) != '/'){ + ++nodeName; + closed = false; + } else { + nodeName+=2; + std::list<DOTCONFDocumentNode*>::reverse_iterator itr=nodeTree.rbegin(); + for(; itr!=nodeTree.rend(); ++itr){ + if(!cmp_func(nodeName, (*itr)->name) && !(*itr)->closed){ + (*itr)->closed = true; + curParent = (*itr)->parentNode; + curPrev = *itr; + break; + } + } + if(itr==nodeTree.rend()){ + error(curLine, fileName, "not matched closing tag </%s>", nodeName); + return -1; + } + continue; + } + } + tagNode = new DOTCONFDocumentNode; + tagNode->name = strdup(nodeName); + tagNode->document = this; + tagNode->fileName = processedFiles.back(); + tagNode->lineNum = curLine; + tagNode->closed = closed; + if(!nodeTree.empty()){ + DOTCONFDocumentNode * prev = nodeTree.back(); + if(prev->closed){ + + curPrev->nextNode = tagNode; + tagNode->previousNode = curPrev; + tagNode->parentNode = curParent; + + } else { + prev->childNode = tagNode; + tagNode->parentNode = prev; + curParent = prev; + } + } + nodeTree.push_back(tagNode); + curPrev = tagNode; + } else { + nodeValue = word; + tagNode->pushValue(nodeValue); + } + } + + return 0; +} +int DOTCONFDocument::parseFile(DOTCONFDocumentNode * _parent) +{ + char str[512]; + int ret = 0; + curLine = 0; + curParent = _parent; + + quoted = false; + size_t slen = 0; + + while(fgets(str, 511, file)){ + ++curLine; + slen = strlen(str); + if( slen >= 510 ){ + error(curLine, fileName, "warning: line too long"); + } + if(str[slen-1] != '\n'){ + str[slen] = '\n'; + str[slen+1] = 0; + } + if((ret = cleanupLine(str)) == -1){ + break; + } + if(ret == 0){ + if(!words.empty()){ + ret = parseLine(); + mempool->free(); + words.clear(); + if(ret == -1){ + break; + } + } + } + } + + return ret; +} + +int DOTCONFDocument::checkConfig(const std::list<DOTCONFDocumentNode*>::iterator & from) +{ + int ret = 0; + + DOTCONFDocumentNode * tagNode = NULL; + int vi = 0; + for(std::list<DOTCONFDocumentNode*>::iterator i = from; i != nodeTree.end(); i++){ + tagNode = *i; + if(!tagNode->closed){ + error(tagNode->lineNum, tagNode->fileName, "unclosed tag %s", tagNode->name); + ret = -1; + break; + } + vi = 0; + while( vi < tagNode->valuesCount ){ + + if(strstr(tagNode->values[vi], "${") && strchr(tagNode->values[vi], '}') ){ + ret = macroSubstitute(tagNode, vi ); + mempool->free(); + if(ret == -1){ + break; + } + } + ++vi; + } + if(ret == -1){ + break; + } + } + + return ret; +} + +int DOTCONFDocument::setContent(const char * _fileName) +{ + int ret = 0; + char realpathBuf[PATH_MAX]; + + if(realpath(_fileName, realpathBuf) == NULL){ + error(0, NULL, "realpath(%s) failed: %s", _fileName, strerror(errno)); + return -1; + } + + fileName = strdup(realpathBuf); + + processedFiles.push_back(strdup(realpathBuf)); + + if(( file = fopen(fileName, "r")) == NULL){ + error(0, NULL, "failed to open file '%s': %s", fileName, strerror(errno)); + return -1; + } + + ret = parseFile(); + + (void) fclose(file); + + if(!ret){ + + if( (ret = checkConfig(nodeTree.begin())) == -1){ + return -1; + } + + std::list<DOTCONFDocumentNode*>::iterator from; + DOTCONFDocumentNode * tagNode = NULL; + int vi = 0; + for(std::list<DOTCONFDocumentNode*>::iterator i = nodeTree.begin(); i!=nodeTree.end(); i++){ + tagNode = *i; + if(!cmp_func("DOTCONFPPIncludeFile", tagNode->name)){ + vi = 0; + while( vi < tagNode->valuesCount ){ + if(access(tagNode->values[vi], R_OK) == -1){ + error(tagNode->lineNum, tagNode->fileName, "%s: %s", tagNode->values[vi], strerror(errno)); + return -1; + } + if(realpath(tagNode->values[vi], realpathBuf) == NULL){ + error(tagNode->lineNum, tagNode->fileName, "realpath(%s) failed: %s", tagNode->values[vi], strerror(errno)); + return -1; + } + + bool processed = false; + for(std::list<char*>::const_iterator itInode = processedFiles.begin(); itInode != processedFiles.end(); itInode++){ + if(!strcmp(*itInode, realpathBuf)){ + processed = true; + break; + } + } + if(processed){ + break; + } + + processedFiles.push_back(strdup(realpathBuf)); + + file = fopen(tagNode->values[vi], "r"); + if(file == NULL){ + error(tagNode->lineNum, fileName, "failed to open file '%s': %s", tagNode->values[vi], strerror(errno)); + return -1; + } + + fileName = strdup(realpathBuf); + from = nodeTree.end(); --from; + + ret = parseFile(); + (void) fclose(file); + if(ret == -1) + return -1; + if(checkConfig(++from) == -1){ + return -1; + } + ++vi; + } + } + } + + + if(!requiredOptions.empty()) + ret = checkRequiredOptions(); + } + + return ret; +} + +int DOTCONFDocument::checkRequiredOptions() +{ + for(std::list<char*>::const_iterator ci = requiredOptions.begin(); ci != requiredOptions.end(); ci++){ + bool matched = false; + for(std::list<DOTCONFDocumentNode*>::iterator i = nodeTree.begin(); i!=nodeTree.end(); i++){ + if(!cmp_func((*i)->name, *ci)){ + matched = true; + break; + } + } + if(!matched){ + error(0, NULL, "required option '%s' not specified", *ci); + return -1; + } + } + return 0; +} + +void DOTCONFDocument::error(int lineNum, const char * fileName_, const char * fmt, ...) +{ + va_list args; + va_start(args, fmt); + + size_t len = (lineNum!=0?strlen(fileName_):0) + strlen(fmt) + 50; + char * buf = (char*)mempool->alloc(len); + + if(lineNum) + (void) snprintf(buf, len, "DOTCONF++: file '%s', line %d: %s\n", fileName_, lineNum, fmt); + else + (void) snprintf(buf, len, "DOTCONF++: %s\n", fmt); + + (void) vfprintf(stderr, buf, args); + + va_end(args); +} + +char * DOTCONFDocument::getSubstitution(char * macro, int lineNum) +{ + char * buf = NULL; + char * variable = macro+2; + + char * endBr = strchr(macro, '}'); + + if(!endBr){ + error(lineNum, fileName, "unterminated '{'"); + return NULL; + } + *endBr = 0; + + char * defaultValue = strchr(variable, ':'); + + if(defaultValue){ + *defaultValue++ = 0; + if(*defaultValue != '-'){ + error(lineNum, fileName, "incorrect macro substitution syntax"); + return NULL; + } + ++defaultValue; + if(*defaultValue == '"' || *defaultValue == '\''){ + ++defaultValue; + defaultValue[strlen(defaultValue)-1] = 0; + } + } else { + defaultValue = NULL; + } + + char * subs = getenv(variable); + if( subs ){ + buf = mempool->strdup(subs); + } else { + std::list<DOTCONFDocumentNode*>::iterator i = nodeTree.begin(); + DOTCONFDocumentNode * tagNode = NULL; + for(; i!=nodeTree.end(); i++){ + tagNode = *i; + if(!cmp_func(tagNode->name, variable)){ + if(tagNode->valuesCount != 0){ + buf = mempool->strdup(tagNode->values[0]); + break; + } + } + } + if( i == nodeTree.end() ){ + if( defaultValue ){ + buf = mempool->strdup(defaultValue); + } else { + error(lineNum, fileName, "substitution not found and default value not given"); + return NULL; + } + } + } + return buf; +} + +int DOTCONFDocument::macroSubstitute(DOTCONFDocumentNode * tagNode, int valueIndex) +{ + int ret = 0; + char * macro = tagNode->values[valueIndex]; + size_t valueLen = strlen(tagNode->values[valueIndex])+1; + char * value = (char*)mempool->alloc(valueLen); + char * v = value; + char * subs = NULL; + + while(*macro){ + if(*macro == '$' && *(macro+1) == '{'){ + char * m = strchr(macro, '}'); + subs = getSubstitution(macro, tagNode->lineNum); + if(subs == NULL){ + ret = -1; + break; + } + macro = m + 1; + *v = 0; + v = (char*)mempool->alloc(strlen(value)+strlen(subs)+valueLen); + strcpy(v, value); + value = strcat(v, subs); + v = value + strlen(value); + continue; + } + *v++ = *macro++; + } + *v = 0; + + free(tagNode->values[valueIndex]); + tagNode->values[valueIndex] = strdup(value); + return ret; +} + +const DOTCONFDocumentNode * DOTCONFDocument::getFirstNode() const +{ + if ( !nodeTree.empty() ) { + return *nodeTree.begin(); + } else { + return NULL; + } +} + +const DOTCONFDocumentNode * DOTCONFDocument::findNode(const char * nodeName, const DOTCONFDocumentNode * parentNode, const DOTCONFDocumentNode * startNode) const +{ + + + std::list<DOTCONFDocumentNode*>::const_iterator i = nodeTree.begin(); + + if(startNode == NULL) + startNode = parentNode; + + if(startNode != NULL){ + while( i != nodeTree.end() && (*i) != startNode ){ + ++i; + } + if( i != nodeTree.end() ) ++i; + } + + for(; i!=nodeTree.end(); i++){ + + if((*i)->parentNode != parentNode){ + continue; + } + if(!cmp_func(nodeName, (*i)->name)){ + return *i; + } + } + return NULL; +} + +void DOTCONFDocument::setRequiredOptionNames(const char ** requiredOptionNames) +{ + while(*requiredOptionNames){ + requiredOptions.push_back(strdup( *requiredOptionNames )); + ++requiredOptionNames; + } +} diff --git a/src/shared/Config/dotconfpp/dotconfpp.h b/src/shared/Config/dotconfpp/dotconfpp.h new file mode 100644 index 00000000000..15c4f7fcd78 --- /dev/null +++ b/src/shared/Config/dotconfpp/dotconfpp.h @@ -0,0 +1,110 @@ + + + +#ifndef DOTCONFPP_H +#define DOTCONFPP_H + +#include <list> + +#include <stdarg.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#ifdef WIN32 +#define PATH_MAX _MAX_PATH +#define snprintf _snprintf +#define strcasecmp stricmp +#define realpath(path,resolved_path) _fullpath(resolved_path, path, _MAX_PATH) +#include <io.h> +#else +#include <unistd.h> +#include <limits.h> +#include <stdint.h> +#include <strings.h> +#endif + +#include "mempool.h" + +class DOTCONFDocument; + +class DOTCONFDocumentNode +{ +friend class DOTCONFDocument; +private: + DOTCONFDocumentNode * previousNode; + DOTCONFDocumentNode * nextNode; + DOTCONFDocumentNode * parentNode; + DOTCONFDocumentNode * childNode; + char ** values; + int valuesCount; + char * name; + const DOTCONFDocument * document; + int lineNum; + char * fileName; + bool closed; + + void pushValue(char * _value); + +public: + DOTCONFDocumentNode(); + ~DOTCONFDocumentNode(); + + const char * getConfigurationFileName()const { return fileName; } + int getConfigurationLineNumber() const { return lineNum; } + + const DOTCONFDocumentNode * getNextNode() const { return nextNode; } + const DOTCONFDocumentNode * getPreviuosNode() const { return previousNode; } + const DOTCONFDocumentNode * getParentNode() const { return parentNode; } + const DOTCONFDocumentNode * getChildNode() const { return childNode; } + const char* getValue(int index = 0) const; + const char * getName() const { return name; } + const DOTCONFDocument * getDocument() const { return document; } +}; + +class DOTCONFDocument +{ +public: + enum CaseSensitive { CASESENSETIVE, CASEINSENSETIVE }; +protected: + AsyncDNSMemPool * mempool; +private: + DOTCONFDocumentNode * curParent; + DOTCONFDocumentNode * curPrev; + int curLine; + bool quoted; + std::list<DOTCONFDocumentNode*> nodeTree; + std::list<char*> requiredOptions; + std::list<char*> processedFiles; + FILE * file; + char * fileName; + std::list<char*> words; + int (*cmp_func)(const char *, const char *); + + int checkRequiredOptions(); + int parseLine(); + int parseFile(DOTCONFDocumentNode * _parent = NULL); + int checkConfig(const std::list<DOTCONFDocumentNode*>::iterator & from); + int cleanupLine(char * line); + char * getSubstitution(char * macro, int lineNum); + int macroSubstitute(DOTCONFDocumentNode * tagNode, int valueIndex); + +protected: + virtual void error(int lineNum, const char * fileName, const char * fmt, ...) ATTR_PRINTF(4,5); + +public: + DOTCONFDocument(CaseSensitive caseSensitivity = CASESENSETIVE); + virtual ~DOTCONFDocument(); + + int setContent(const char * _fileName); + + void setRequiredOptionNames(const char ** requiredOptionNames); + const DOTCONFDocumentNode * getFirstNode() const; + const DOTCONFDocumentNode * findNode(const char * nodeName, const DOTCONFDocumentNode * parentNode = NULL, const DOTCONFDocumentNode * startNode = NULL) const; +}; + +#endif diff --git a/src/shared/Config/dotconfpp/mempool.cpp b/src/shared/Config/dotconfpp/mempool.cpp new file mode 100644 index 00000000000..cf589ffb2bc --- /dev/null +++ b/src/shared/Config/dotconfpp/mempool.cpp @@ -0,0 +1,100 @@ + + + +#include "mempool.h" + +AsyncDNSMemPool::PoolChunk::PoolChunk(size_t _size): + pool(NULL), pos(0), size(_size) +{ + pool = ::malloc(size); +} + +AsyncDNSMemPool::PoolChunk::~PoolChunk() +{ + ::free(pool); +} + +AsyncDNSMemPool::AsyncDNSMemPool(size_t _defaultSize): + chunks(NULL), chunksCount(0), defaultSize(_defaultSize), + poolUsage(0), poolUsageCounter(0) +{ +} + +AsyncDNSMemPool::~AsyncDNSMemPool() +{ + for(size_t i = 0; i<chunksCount; i++){ + delete chunks[i]; + } + ::free(chunks); +} + +int AsyncDNSMemPool::initialize() +{ + chunksCount = 1; + chunks = (PoolChunk**)::malloc(sizeof(PoolChunk*)); + if(chunks == NULL) + return -1; + + chunks[chunksCount-1] = new PoolChunk(defaultSize); + + return 0; +} + +void AsyncDNSMemPool::addNewChunk(size_t size) +{ + ++chunksCount; + chunks = (PoolChunk**)::realloc(chunks, chunksCount*sizeof(PoolChunk*)); + if(size <= defaultSize) + chunks[chunksCount-1] = new PoolChunk(defaultSize); + else + chunks[chunksCount-1] = new PoolChunk(size); +} + +void * AsyncDNSMemPool::alloc(size_t size) +{ + PoolChunk * chunk = NULL; + for(size_t i = 0; i<chunksCount; i++){ + chunk = chunks[i]; + if((chunk->size - chunk->pos) >= size){ + chunk->pos += size; + return ((char*)chunk->pool) + chunk->pos - size; + } + } + addNewChunk(size); + chunks[chunksCount-1]->pos = size; + return chunks[chunksCount-1]->pool; +} + +void AsyncDNSMemPool::free() +{ + size_t pu = 0; + size_t psz = 0; + ++poolUsageCounter; + + for(size_t i = 0; i<chunksCount; i++){ + pu += chunks[i]->pos; + psz += chunks[i]->size; + chunks[i]->pos = 0; + } + poolUsage=(poolUsage>pu)?poolUsage:pu; + + if(poolUsageCounter >= 10 && chunksCount > 1){ + psz -= chunks[chunksCount-1]->size; + if(poolUsage < psz){ + --chunksCount; + delete chunks[chunksCount]; + } + poolUsage = 0; + poolUsageCounter = 0; + } +} + +void * AsyncDNSMemPool::calloc(size_t size) +{ + return ::memset(this->alloc(size), 0, size); +} + +char * AsyncDNSMemPool::strdup(const char *str) +{ + return ::strcpy((char*)this->alloc(strlen(str)+1), str); +} diff --git a/src/shared/Config/dotconfpp/mempool.h b/src/shared/Config/dotconfpp/mempool.h new file mode 100644 index 00000000000..04bd1e006ad --- /dev/null +++ b/src/shared/Config/dotconfpp/mempool.h @@ -0,0 +1,46 @@ + + + +#ifndef ASYNC_DNS_MEMPOOL_H +#define ASYNC_DNS_MEMPOOL_H + +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> + +#undef free +#undef calloc +#undef strdup + +class AsyncDNSMemPool +{ +private: + struct PoolChunk { + void * pool; + size_t pos; + size_t size; + + PoolChunk(size_t _size); + ~PoolChunk(); + }; + PoolChunk ** chunks; + size_t chunksCount; + size_t defaultSize; + + size_t poolUsage; + size_t poolUsageCounter; + + void addNewChunk(size_t size); + +public: + AsyncDNSMemPool(size_t _defaultSize = 4096); + virtual ~AsyncDNSMemPool(); + + int initialize(); + void free(); + void * alloc(size_t size); + void * calloc(size_t size); + char * strdup(const char *str); +}; + +#endif diff --git a/src/shared/Database/DBCStores.cpp b/src/shared/Database/DBCStores.cpp new file mode 100644 index 00000000000..623b9652fb3 --- /dev/null +++ b/src/shared/Database/DBCStores.cpp @@ -0,0 +1,642 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "DBCStores.h" +//#include "DataStore.h" +#include "Policies/SingletonImp.h" +#include "Log.h" +#include "ProgressBar.h" + +#include "DBCfmt.cpp" + +#include <map> + +typedef std::map<uint16,uint32> AreaFlagByAreaID; +typedef std::map<uint32,uint32> AreaFlagByMapID; + +DBCStorage <AreaTableEntry> sAreaStore(AreaTableEntryfmt); +static AreaFlagByAreaID sAreaFlagByAreaID; +static AreaFlagByMapID sAreaFlagByMapID; // for instances without generated *.map files + +DBCStorage <AreaTriggerEntry> sAreaTriggerStore(AreaTriggerEntryfmt); +DBCStorage <BankBagSlotPricesEntry> sBankBagSlotPricesStore(BankBagSlotPricesEntryfmt); +DBCStorage <BattlemasterListEntry> sBattlemasterListStore(BattlemasterListEntryfmt); +DBCStorage <CharTitlesEntry> sCharTitlesStore(CharTitlesEntryfmt); +DBCStorage <ChatChannelsEntry> sChatChannelsStore(ChatChannelsEntryfmt); +DBCStorage <ChrClassesEntry> sChrClassesStore(ChrClassesEntryfmt); +DBCStorage <ChrRacesEntry> sChrRacesStore(ChrRacesEntryfmt); +DBCStorage <CreatureDisplayInfoEntry> sCreatureDisplayInfoStore(CreatureDisplayInfofmt); +DBCStorage <CreatureFamilyEntry> sCreatureFamilyStore(CreatureFamilyfmt); +DBCStorage <CreatureSpellDataEntry> sCreatureSpellDataStore(CreatureSpellDatafmt); + +DBCStorage <DurabilityQualityEntry> sDurabilityQualityStore(DurabilityQualityfmt); +DBCStorage <DurabilityCostsEntry> sDurabilityCostsStore(DurabilityCostsfmt); + +DBCStorage <EmotesTextEntry> sEmotesTextStore(EmoteEntryfmt); + +typedef std::map<uint32,SimpleFactionsList> FactionTeamMap; +static FactionTeamMap sFactionTeamMap; +DBCStorage <FactionEntry> sFactionStore(FactionEntryfmt); +DBCStorage <FactionTemplateEntry> sFactionTemplateStore(FactionTemplateEntryfmt); + +DBCStorage <GemPropertiesEntry> sGemPropertiesStore(GemPropertiesEntryfmt); + +DBCStorage <GtCombatRatingsEntry> sGtCombatRatingsStore(GtCombatRatingsfmt); +DBCStorage <GtChanceToMeleeCritBaseEntry> sGtChanceToMeleeCritBaseStore(GtChanceToMeleeCritBasefmt); +DBCStorage <GtChanceToMeleeCritEntry> sGtChanceToMeleeCritStore(GtChanceToMeleeCritfmt); +DBCStorage <GtChanceToSpellCritBaseEntry> sGtChanceToSpellCritBaseStore(GtChanceToSpellCritBasefmt); +DBCStorage <GtChanceToSpellCritEntry> sGtChanceToSpellCritStore(GtChanceToSpellCritfmt); +DBCStorage <GtOCTRegenHPEntry> sGtOCTRegenHPStore(GtOCTRegenHPfmt); +//DBCStorage <GtOCTRegenMPEntry> sGtOCTRegenMPStore(GtOCTRegenMPfmt); -- not used currently +DBCStorage <GtRegenHPPerSptEntry> sGtRegenHPPerSptStore(GtRegenHPPerSptfmt); +DBCStorage <GtRegenMPPerSptEntry> sGtRegenMPPerSptStore(GtRegenMPPerSptfmt); +DBCStorage <ItemEntry> sItemStore(Itemfmt); +//DBCStorage <ItemCondExtCostsEntry> sItemCondExtCostsStore(ItemCondExtCostsEntryfmt); +//DBCStorage <ItemDisplayInfoEntry> sItemDisplayInfoStore(ItemDisplayTemplateEntryfmt); -- not used currently +DBCStorage <ItemExtendedCostEntry> sItemExtendedCostStore(ItemExtendedCostEntryfmt); +DBCStorage <ItemRandomPropertiesEntry> sItemRandomPropertiesStore(ItemRandomPropertiesfmt); +DBCStorage <ItemRandomSuffixEntry> sItemRandomSuffixStore(ItemRandomSuffixfmt); +DBCStorage <ItemSetEntry> sItemSetStore(ItemSetEntryfmt); + +DBCStorage <LockEntry> sLockStore(LockEntryfmt); + +DBCStorage <MailTemplateEntry> sMailTemplateStore(MailTemplateEntryfmt); +DBCStorage <MapEntry> sMapStore(MapEntryfmt); + +DBCStorage <QuestSortEntry> sQuestSortStore(QuestSortEntryfmt); + +DBCStorage <RandomPropertiesPointsEntry> sRandomPropertiesPointsStore(RandomPropertiesPointsfmt); + +DBCStorage <SkillLineEntry> sSkillLineStore(SkillLinefmt); +DBCStorage <SkillLineAbilityEntry> sSkillLineAbilityStore(SkillLineAbilityfmt); + +DBCStorage <SoundEntriesEntry> sSoundEntriesStore(SoundEntriesfmt); + +DBCStorage <SpellItemEnchantmentEntry> sSpellItemEnchantmentStore(SpellItemEnchantmentfmt); +DBCStorage <SpellItemEnchantmentConditionEntry> sSpellItemEnchantmentConditionStore(SpellItemEnchantmentConditionfmt); +DBCStorage <SpellEntry> sSpellStore(SpellEntryfmt); +SpellCategoryStore sSpellCategoryStore; +PetFamilySpellsStore sPetFamilySpellsStore; + +DBCStorage <SpellCastTimesEntry> sSpellCastTimesStore(SpellCastTimefmt); +DBCStorage <SpellDurationEntry> sSpellDurationStore(SpellDurationfmt); +DBCStorage <SpellFocusObjectEntry> sSpellFocusObjectStore(SpellFocusObjectfmt); +DBCStorage <SpellRadiusEntry> sSpellRadiusStore(SpellRadiusfmt); +DBCStorage <SpellRangeEntry> sSpellRangeStore(SpellRangefmt); +DBCStorage <SpellShapeshiftEntry> sSpellShapeshiftStore(SpellShapeshiftfmt); +DBCStorage <StableSlotPricesEntry> sStableSlotPricesStore(StableSlotPricesfmt); +DBCStorage <TalentEntry> sTalentStore(TalentEntryfmt); +TalentSpellPosMap sTalentSpellPosMap; +DBCStorage <TalentTabEntry> sTalentTabStore(TalentTabEntryfmt); + +// store absolute bit position for first rank for talent inspect +typedef std::map<uint32,uint32> TalentInspectMap; +static TalentInspectMap sTalentPosInInspect; +static TalentInspectMap sTalentTabSizeInInspect; +static uint32 sTalentTabPages[12/*MAX_CLASSES*/][3]; + +DBCStorage <TaxiNodesEntry> sTaxiNodesStore(TaxiNodesEntryfmt); +TaxiMask sTaxiNodesMask; + +// DBC used only for initialization sTaxiPathSetBySource at startup. +TaxiPathSetBySource sTaxiPathSetBySource; +DBCStorage <TaxiPathEntry> sTaxiPathStore(TaxiPathEntryfmt); + +// DBC used only for initialization sTaxiPathSetBySource at startup. +TaxiPathNodesByPath sTaxiPathNodesByPath; +struct TaxiPathNodeEntry +{ + uint32 path; + uint32 index; + uint32 mapid; + float x; + float y; + float z; + uint32 actionFlag; + uint32 delay; +}; +static DBCStorage <TaxiPathNodeEntry> sTaxiPathNodeStore(TaxiPathNodeEntryfmt); + +DBCStorage <TotemCategoryEntry> sTotemCategoryStore(TotemCategoryEntryfmt); + +DBCStorage <WorldMapAreaEntry> sWorldMapAreaStore(WorldMapAreaEntryfmt); +DBCStorage <WorldSafeLocsEntry> sWorldSafeLocsStore(WorldSafeLocsEntryfmt); + +typedef std::list<std::string> StoreProblemList; + +static bool LoadDBC_assert_print(uint32 fsize,uint32 rsize, std::string filename) +{ + sLog.outError("ERROR: Size of '%s' setted by format string (%u) not equal size of C++ structure (%u).",filename.c_str(),fsize,rsize); + + // assert must fail after function call + return false; +} + +template<class T> +inline void LoadDBC(uint32& availableDbcLocales,barGoLink& bar, StoreProblemList& errlist, DBCStorage<T>& storage, std::string dbc_path, std::string filename) +{ + // compatibility format and C++ structure sizes + assert(DBCFile::GetFormatRecordSize(storage.GetFormat()) == sizeof(T) || LoadDBC_assert_print(DBCFile::GetFormatRecordSize(storage.GetFormat()),sizeof(T),filename)); + + std::string dbc_filename = dbc_path + filename; + if(storage.Load(dbc_filename.c_str())) + { + bar.step(); + for(uint8 i = 0; i < MAX_LOCALE; ++i) + { + if(!(availableDbcLocales & (1 << i))) + continue; + + std::string dbc_filename_loc = dbc_path + localeNames[i] + "/" + filename; + if(!storage.LoadStringsFrom(dbc_filename_loc.c_str())) + availableDbcLocales &= ~(1<<i); // mark as not available for speedup next checks + } + } + else + { + // sort problematic dbc to (1) non compatible and (2) non-existed + FILE * f=fopen(dbc_filename.c_str(),"rb"); + if(f) + { + char buf[100]; + snprintf(buf,100," (exist, but have %d fields instead %d) Wrong client version DBC file?",storage.GetFieldCount(),strlen(storage.GetFormat())); + errlist.push_back(dbc_filename + buf); + fclose(f); + } + else + errlist.push_back(dbc_filename); + } +} + +void LoadDBCStores(std::string dataPath) +{ + std::string dbcPath = dataPath+"dbc/"; + + const uint32 DBCFilesCount = 56; + + barGoLink bar( DBCFilesCount ); + + StoreProblemList bad_dbc_files; + uint32 availableDbcLocales = 0xFFFFFFFF; + + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sAreaStore, dbcPath,"AreaTable.dbc"); + + // must be after sAreaStore loading + for(uint32 i = 0; i < sAreaStore.GetNumRows(); ++i) // areaflag numbered from 0 + { + if(AreaTableEntry const* area = sAreaStore.LookupEntry(i)) + { + // fill AreaId->DBC records + sAreaFlagByAreaID.insert(AreaFlagByAreaID::value_type(uint16(area->ID),area->exploreFlag)); + + // fill MapId->DBC records ( skip sub zones and continents ) + if(area->zone==0 && area->mapid != 0 && area->mapid != 1 && area->mapid != 530 ) + sAreaFlagByMapID.insert(AreaFlagByMapID::value_type(area->mapid,area->exploreFlag)); + } + } + + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sAreaTriggerStore, dbcPath,"AreaTrigger.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sBankBagSlotPricesStore, dbcPath,"BankBagSlotPrices.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sBattlemasterListStore, dbcPath,"BattlemasterList.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sCharTitlesStore, dbcPath,"CharTitles.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sChatChannelsStore, dbcPath,"ChatChannels.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sChrClassesStore, dbcPath,"ChrClasses.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sChrRacesStore, dbcPath,"ChrRaces.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sCreatureDisplayInfoStore, dbcPath,"CreatureDisplayInfo.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sCreatureFamilyStore, dbcPath,"CreatureFamily.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sCreatureSpellDataStore, dbcPath,"CreatureSpellData.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sDurabilityCostsStore, dbcPath,"DurabilityCosts.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sDurabilityQualityStore, dbcPath,"DurabilityQuality.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sEmotesTextStore, dbcPath,"EmotesText.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sFactionStore, dbcPath,"Faction.dbc"); + for (uint32 i=0;i<sFactionStore.GetNumRows(); ++i) + { + FactionEntry const * faction = sFactionStore.LookupEntry(i); + if (faction && faction->team) + { + SimpleFactionsList &flist = sFactionTeamMap[faction->team]; + flist.push_back(i); + } + } + + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sFactionTemplateStore, dbcPath,"FactionTemplate.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGemPropertiesStore, dbcPath,"GemProperties.dbc"); + + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGtCombatRatingsStore, dbcPath,"gtCombatRatings.dbc"); + + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGtChanceToMeleeCritBaseStore, dbcPath,"gtChanceToMeleeCritBase.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGtChanceToMeleeCritStore, dbcPath,"gtChanceToMeleeCrit.dbc"); + + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGtChanceToSpellCritBaseStore, dbcPath,"gtChanceToSpellCritBase.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGtChanceToSpellCritStore, dbcPath,"gtChanceToSpellCrit.dbc"); + + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGtOCTRegenHPStore, dbcPath,"gtOCTRegenHP.dbc"); + //LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGtOCTRegenMPStore, dbcPath,"gtOCTRegenMP.dbc"); -- not used currently + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGtRegenHPPerSptStore, dbcPath,"gtRegenHPPerSpt.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGtRegenMPPerSptStore, dbcPath,"gtRegenMPPerSpt.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sItemStore, dbcPath,"Item.dbc"); + //LoadDBC(availableDbcLocales,bar,bad_dbc_files,sItemDisplayInfoStore, dbcPath,"ItemDisplayInfo.dbc"); -- not used currently + //LoadDBC(availableDbcLocales,bar,bad_dbc_files,sItemCondExtCostsStore, dbcPath,"ItemCondExtCosts.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sItemExtendedCostStore, dbcPath,"ItemExtendedCost.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sItemRandomPropertiesStore,dbcPath,"ItemRandomProperties.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sItemRandomSuffixStore, dbcPath,"ItemRandomSuffix.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sItemSetStore, dbcPath,"ItemSet.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sLockStore, dbcPath,"Lock.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sMailTemplateStore, dbcPath,"MailTemplate.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sMapStore, dbcPath,"Map.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sQuestSortStore, dbcPath,"QuestSort.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sRandomPropertiesPointsStore, dbcPath,"RandPropPoints.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSkillLineStore, dbcPath,"SkillLine.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSkillLineAbilityStore, dbcPath,"SkillLineAbility.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSoundEntriesStore, dbcPath,"SoundEntries.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSpellStore, dbcPath,"Spell.dbc"); + for(uint32 i = 1; i < sSpellStore.GetNumRows(); ++i) + { + SpellEntry const * spell = sSpellStore.LookupEntry(i); + if(spell && spell->Category) + sSpellCategoryStore[spell->Category].insert(i); + + // DBC not support uint64 fields but SpellEntry have SpellFamilyFlags mapped at 2 uint32 fields + // uint32 field already converted to bigendian if need, but must be swapped for correct uint64 bigendian view + #if MANGOS_ENDIAN == MANGOS_BIGENDIAN + std::swap(*((uint32*)(&spell->SpellFamilyFlags)),*(((uint32*)(&spell->SpellFamilyFlags))+1)); + #endif + } + + for (uint32 j = 0; j < sSkillLineAbilityStore.GetNumRows(); ++j) + { + SkillLineAbilityEntry const *skillLine = sSkillLineAbilityStore.LookupEntry(j); + + if(!skillLine) + continue; + + SpellEntry const* spellInfo = sSpellStore.LookupEntry(skillLine->spellId); + + if(spellInfo && (spellInfo->Attributes & 0x1D0) == 0x1D0) + { + for (unsigned int i = 1; i < sCreatureFamilyStore.GetNumRows(); ++i) + { + CreatureFamilyEntry const* cFamily = sCreatureFamilyStore.LookupEntry(i); + if(!cFamily) + continue; + + if(skillLine->skillId != cFamily->skillLine && skillLine->skillId != cFamily->skillLine2) + continue; + + sPetFamilySpellsStore[i].insert(spellInfo->Id); + } + } + } + + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSpellCastTimesStore, dbcPath,"SpellCastTimes.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSpellDurationStore, dbcPath,"SpellDuration.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSpellFocusObjectStore, dbcPath,"SpellFocusObject.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSpellItemEnchantmentStore,dbcPath,"SpellItemEnchantment.dbc"); + + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSpellItemEnchantmentConditionStore,dbcPath,"SpellItemEnchantmentCondition.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSpellRadiusStore, dbcPath,"SpellRadius.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSpellRangeStore, dbcPath,"SpellRange.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSpellShapeshiftStore, dbcPath,"SpellShapeshiftForm.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sStableSlotPricesStore, dbcPath,"StableSlotPrices.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sTalentStore, dbcPath,"Talent.dbc"); + + // create talent spells set + for (unsigned int i = 0; i < sTalentStore.GetNumRows(); ++i) + { + TalentEntry const *talentInfo = sTalentStore.LookupEntry(i); + if (!talentInfo) continue; + for (int j = 0; j < 5; j++) + if(talentInfo->RankID[j]) + sTalentSpellPosMap[talentInfo->RankID[j]] = TalentSpellPos(i,j); + } + + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sTalentTabStore, dbcPath,"TalentTab.dbc"); + + // preper fast data access to bit pos of talent ranks for use at inspecting + { + // fill table by amount of talent ranks and fill sTalentTabBitSizeInInspect + // store in with (row,col,talent)->size key for correct sorting by (row,col) + typedef std::map<uint32,uint32> TalentBitSize; + TalentBitSize sTalentBitSize; + for(uint32 i = 1; i < sTalentStore.GetNumRows(); ++i) + { + TalentEntry const *talentInfo = sTalentStore.LookupEntry(i); + if (!talentInfo) continue; + + TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry( talentInfo->TalentTab ); + if(!talentTabInfo) + continue; + + // find talent rank + uint32 curtalent_maxrank = 0; + for(uint32 k = 5; k > 0; --k) + { + if(talentInfo->RankID[k-1]) + { + curtalent_maxrank = k; + break; + } + } + + sTalentBitSize[(talentInfo->Row<<24) + (talentInfo->Col<<16)+talentInfo->TalentID] = curtalent_maxrank; + sTalentTabSizeInInspect[talentInfo->TalentTab] += curtalent_maxrank; + } + + // now have all max ranks (and then bit amount used for store talent ranks in inspect) + for(uint32 talentTabId = 1; talentTabId < sTalentTabStore.GetNumRows(); ++talentTabId) + { + TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry( talentTabId ); + if(!talentTabInfo) + continue; + + // store class talent tab pages + uint32 cls = 1; + for(uint32 m=1;!(m & talentTabInfo->ClassMask) && cls < 12 /*MAX_CLASSES*/;m <<=1, ++cls) {} + + sTalentTabPages[cls][talentTabInfo->tabpage]=talentTabId; + + // add total amount bits for first rank starting from talent tab first talent rank pos. + uint32 pos = 0; + for(TalentBitSize::iterator itr = sTalentBitSize.begin(); itr != sTalentBitSize.end(); ++itr) + { + uint32 talentId = itr->first & 0xFFFF; + TalentEntry const *talentInfo = sTalentStore.LookupEntry( talentId ); + if(!talentInfo) + continue; + + if(talentInfo->TalentTab != talentTabId) + continue; + + sTalentPosInInspect[talentId] = pos; + pos+= itr->second; + } + } + } + + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sTaxiNodesStore, dbcPath,"TaxiNodes.dbc"); + + // Initialize global taxinodes mask + memset(sTaxiNodesMask,0,sizeof(sTaxiNodesMask)); + for(uint32 i = 1; i < sTaxiNodesStore.GetNumRows(); ++i) + { + if(sTaxiNodesStore.LookupEntry(i)) + { + uint8 field = (uint8)((i - 1) / 32); + uint32 submask = 1<<((i-1)%32); + sTaxiNodesMask[field] |= submask; + } + } + + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sTaxiPathStore, dbcPath,"TaxiPath.dbc"); + for(uint32 i = 1; i < sTaxiPathStore.GetNumRows(); ++i) + if(TaxiPathEntry const* entry = sTaxiPathStore.LookupEntry(i)) + sTaxiPathSetBySource[entry->from][entry->to] = TaxiPathBySourceAndDestination(entry->ID,entry->price); + uint32 pathCount = sTaxiPathStore.GetNumRows(); + + //## TaxiPathNode.dbc ## Loaded only for initialization different structures + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sTaxiPathNodeStore, dbcPath,"TaxiPathNode.dbc"); + // Calculate path nodes count + std::vector<uint32> pathLength; + pathLength.resize(pathCount); // 0 and some other indexes not used + for(uint32 i = 1; i < sTaxiPathNodeStore.GetNumRows(); ++i) + if(TaxiPathNodeEntry const* entry = sTaxiPathNodeStore.LookupEntry(i)) + ++pathLength[entry->path]; + // Set path length + sTaxiPathNodesByPath.resize(pathCount); // 0 and some other indexes not used + for(uint32 i = 1; i < sTaxiPathNodesByPath.size(); ++i) + sTaxiPathNodesByPath[i].resize(pathLength[i]); + // fill data + for(uint32 i = 1; i < sTaxiPathNodeStore.GetNumRows(); ++i) + if(TaxiPathNodeEntry const* entry = sTaxiPathNodeStore.LookupEntry(i)) + sTaxiPathNodesByPath[entry->path][entry->index] = TaxiPathNode(entry->mapid,entry->x,entry->y,entry->z,entry->actionFlag,entry->delay); + sTaxiPathNodeStore.Clear(); + + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sTotemCategoryStore, dbcPath,"TotemCategory.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sWorldMapAreaStore, dbcPath,"WorldMapArea.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sWorldSafeLocsStore, dbcPath,"WorldSafeLocs.dbc"); + + // error checks + if(bad_dbc_files.size() >= DBCFilesCount ) + { + sLog.outError("\nIncorrect DataDir value in mangosd.conf or ALL required *.dbc files (%d) not found by path: %sdbc",DBCFilesCount,dataPath.c_str()); + exit(1); + } + else if(!bad_dbc_files.empty() ) + { + std::string str; + for(std::list<std::string>::iterator i = bad_dbc_files.begin(); i != bad_dbc_files.end(); ++i) + str += *i + "\n"; + + sLog.outError("\nSome required *.dbc files (%u from %d) not found or not compatible:\n%s",bad_dbc_files.size(),DBCFilesCount,str.c_str()); + exit(1); + } + + // check at up-to-date DBC files (53085 is last added spell in 2.4.3) + // check at up-to-date DBC files (17514 is last ID in SkillLineAbilities in 2.4.3) + // check at up-to-date DBC files (598 is last map added in 2.4.3) + // check at up-to-date DBC files (1127 is last gem property added in 2.4.3) + // check at up-to-date DBC files (2425 is last item extended cost added in 2.4.3) + // check at up-to-date DBC files (71 is last char title added in 2.4.3) + // check at up-to-date DBC files (1768 is last area added in 2.4.3) + if( !sSpellStore.LookupEntry(53085) || + !sSkillLineAbilityStore.LookupEntry(17514) || + !sMapStore.LookupEntry(598) || + !sGemPropertiesStore.LookupEntry(1127) || + !sItemExtendedCostStore.LookupEntry(2425) || + !sCharTitlesStore.LookupEntry(71) || + !sAreaStore.LookupEntry(1768) ) + { + sLog.outError("\nYou have _outdated_ DBC files. Please extract correct versions from current using client."); + exit(1); + } + + sLog.outString(); + sLog.outString( ">> Loaded %d data stores", DBCFilesCount ); + sLog.outString(); +} + +SimpleFactionsList const* GetFactionTeamList(uint32 faction) +{ + FactionTeamMap::const_iterator itr = sFactionTeamMap.find(faction); + if(itr==sFactionTeamMap.end()) + return NULL; + return &itr->second; +} + +char* GetPetName(uint32 petfamily, uint32 dbclang) +{ + if(!petfamily) + return NULL; + CreatureFamilyEntry const *pet_family = sCreatureFamilyStore.LookupEntry(petfamily); + if(!pet_family) + return NULL; + return pet_family->Name[dbclang]?pet_family->Name[dbclang]:NULL; +} + +TalentSpellPos const* GetTalentSpellPos(uint32 spellId) +{ + TalentSpellPosMap::const_iterator itr = sTalentSpellPosMap.find(spellId); + if(itr==sTalentSpellPosMap.end()) + return NULL; + + return &itr->second; +} + +uint32 GetTalentSpellCost(uint32 spellId) +{ + if(TalentSpellPos const* pos = GetTalentSpellPos(spellId)) + return pos->rank+1; + + return 0; +} + +AreaTableEntry const* GetAreaEntryByAreaID(uint32 area_id) +{ + AreaFlagByAreaID::iterator i = sAreaFlagByAreaID.find(area_id); + if(i == sAreaFlagByAreaID.end()) + return NULL; + + return sAreaStore.LookupEntry(i->second); +} + +AreaTableEntry const* GetAreaEntryByAreaFlagAndMap(uint32 area_flag,uint32 map_id) +{ + if(area_flag) + return sAreaStore.LookupEntry(area_flag); + + if(MapEntry const* mapEntry = sMapStore.LookupEntry(map_id)) + return GetAreaEntryByAreaID(mapEntry->linked_zone); + + return NULL; +} + +uint32 GetAreaFlagByMapId(uint32 mapid) +{ + AreaFlagByMapID::iterator i = sAreaFlagByMapID.find(mapid); + if(i == sAreaFlagByMapID.end()) + return 0; + else + return i->second; +} + +uint32 GetVirtualMapForMapAndZone(uint32 mapid, uint32 zoneId) +{ + if(mapid != 530) // speed for most cases + return mapid; + + if(WorldMapAreaEntry const* wma = sWorldMapAreaStore.LookupEntry(zoneId)) + return wma->virtual_map_id >= 0 ? wma->virtual_map_id : wma->map_id; + + return mapid; +} + +ContentLevels GetContentLevelsForMapAndZone(uint32 mapid, uint32 zoneId) +{ + mapid = GetVirtualMapForMapAndZone(mapid,zoneId); + if(mapid < 2) + return CONTENT_1_60; + + MapEntry const* mapEntry = sMapStore.LookupEntry(mapid); + return (!mapEntry || !mapEntry->IsExpansionMap()) ? CONTENT_1_60 : CONTENT_61_70; +} + +ChatChannelsEntry const* GetChannelEntryFor(uint32 channel_id) +{ + // not sorted, numbering index from 0 + for(uint32 i = 0; i < sChatChannelsStore.GetNumRows(); ++i) + { + ChatChannelsEntry const* ch = sChatChannelsStore.LookupEntry(i); + if(ch && ch->ChannelID == channel_id) + return ch; + } + return NULL; +} + +bool IsTotemCategoryCompatiableWith(uint32 itemTotemCategoryId, uint32 requiredTotemCategoryId) +{ + if(requiredTotemCategoryId==0) + return true; + if(itemTotemCategoryId==0) + return false; + + TotemCategoryEntry const* itemEntry = sTotemCategoryStore.LookupEntry(itemTotemCategoryId); + if(!itemEntry) + return false; + TotemCategoryEntry const* reqEntry = sTotemCategoryStore.LookupEntry(requiredTotemCategoryId); + if(!reqEntry) + return false; + + if(itemEntry->categoryType!=reqEntry->categoryType) + return false; + + return (itemEntry->categoryMask & reqEntry->categoryMask)==reqEntry->categoryMask; +} + +void Zone2MapCoordinates(float& x,float& y,uint32 zone) +{ + WorldMapAreaEntry const* maEntry = sWorldMapAreaStore.LookupEntry(zone); + + // if not listed then map coordinates (instance) + if(!maEntry) + return; + + std::swap(x,y); // at client map coords swapped + x = x*((maEntry->x2-maEntry->x1)/100)+maEntry->x1; + y = y*((maEntry->y2-maEntry->y1)/100)+maEntry->y1; // client y coord from top to down +} + +void Map2ZoneCoordinates(float& x,float& y,uint32 zone) +{ + WorldMapAreaEntry const* maEntry = sWorldMapAreaStore.LookupEntry(zone); + + // if not listed then map coordinates (instance) + if(!maEntry) + return; + + x = (x-maEntry->x1)/((maEntry->x2-maEntry->x1)/100); + y = (y-maEntry->y1)/((maEntry->y2-maEntry->y1)/100); // client y coord from top to down + std::swap(x,y); // client have map coords swapped +} + +uint32 GetTalentInspectBitPosInTab(uint32 talentId) +{ + TalentInspectMap::const_iterator itr = sTalentPosInInspect.find(talentId); + if(itr == sTalentPosInInspect.end()) + return 0; + + return itr->second; +} + +uint32 GetTalentTabInspectBitSize(uint32 talentTabId) +{ + TalentInspectMap::const_iterator itr = sTalentTabSizeInInspect.find(talentTabId); + if(itr == sTalentTabSizeInInspect.end()) + return 0; + + return itr->second; +} + +uint32 const* GetTalentTabPages(uint32 cls) +{ + return sTalentTabPages[cls]; +} + +// script support functions +MANGOS_DLL_SPEC DBCStorage <SoundEntriesEntry> const* GetSoundEntriesStore() { return &sSoundEntriesStore; } +MANGOS_DLL_SPEC DBCStorage <SpellEntry> const* GetSpellStore() { return &sSpellStore; } +MANGOS_DLL_SPEC DBCStorage <SpellRangeEntry> const* GetSpellRangeStore() { return &sSpellRangeStore; } diff --git a/src/shared/Database/DBCStores.h b/src/shared/Database/DBCStores.h new file mode 100644 index 00000000000..e1d05298695 --- /dev/null +++ b/src/shared/Database/DBCStores.h @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef DBCSTORES_H +#define DBCSTORES_H + +#include "Common.h" +//#include "DataStore.h" +#include "dbcfile.h" +#include "DBCStructure.h" + +#include <list> + +typedef std::list<uint32> SimpleFactionsList; + +SimpleFactionsList const* GetFactionTeamList(uint32 faction); +char* GetPetName(uint32 petfamily, uint32 dbclang); +uint32 GetTalentSpellCost(uint32 spellId); +TalentSpellPos const* GetTalentSpellPos(uint32 spellId); + +AreaTableEntry const* GetAreaEntryByAreaID(uint32 area_id); +AreaTableEntry const* GetAreaEntryByAreaFlagAndMap(uint32 area_flag,uint32 map_id); +uint32 GetAreaFlagByMapId(uint32 mapid); + +uint32 GetVirtualMapForMapAndZone(uint32 mapid, uint32 zoneId); + +enum ContentLevels +{ + CONTENT_1_60 = 0, + CONTENT_61_70 +}; +ContentLevels GetContentLevelsForMapAndZone(uint32 mapid, uint32 zoneId); + +ChatChannelsEntry const* GetChannelEntryFor(uint32 channel_id); + +bool IsTotemCategoryCompatiableWith(uint32 itemTotemCategoryId, uint32 requiredTotemCategoryId); + +void Zone2MapCoordinates(float& x,float& y,uint32 zone); +void Map2ZoneCoordinates(float& x,float& y,uint32 zone); + +uint32 GetTalentInspectBitPosInTab(uint32 talentId); +uint32 GetTalentTabInspectBitSize(uint32 talentTabId); +uint32 const* /*[3]*/ GetTalentTabPages(uint32 cls); + +template<class T> +class DBCStorage +{ + typedef std::list<char*> StringPoolList; + public: + explicit DBCStorage(const char *f) : nCount(0), fieldCount(0), fmt(f), indexTable(NULL), m_dataTable(NULL) { } + ~DBCStorage() { Clear(); } + + T const* LookupEntry(uint32 id) const { return (id>=nCount)?NULL:indexTable[id]; } + uint32 GetNumRows() const { return nCount; } + char const* GetFormat() const { return fmt; } + uint32 GetFieldCount() const { return fieldCount; } + + bool Load(char const* fn) + { + + DBCFile dbc; + // Check if load was sucessful, only then continue + if(!dbc.Load(fn, fmt)) + return false; + + fieldCount = dbc.GetCols(); + m_dataTable = (T*)dbc.AutoProduceData(fmt,nCount,(char**&)indexTable); + m_stringPoolList.push_back(dbc.AutoProduceStrings(fmt,(char*)m_dataTable)); + + // error in dbc file at loading if NULL + return indexTable!=NULL; + } + + bool LoadStringsFrom(char const* fn) + { + // DBC must be already loaded using Load + if(!indexTable) + return false; + + DBCFile dbc; + // Check if load was successful, only then continue + if(!dbc.Load(fn, fmt)) + return false; + + m_stringPoolList.push_back(dbc.AutoProduceStrings(fmt,(char*)m_dataTable)); + + return true; + } + + void Clear() + { + if (!indexTable) + return; + + delete[] ((char*)indexTable); + indexTable = NULL; + delete[] ((char*)m_dataTable); + m_dataTable = NULL; + + while(!m_stringPoolList.empty()) + { + delete[] m_stringPoolList.front(); + m_stringPoolList.pop_front(); + } + nCount = 0; + } + + private: + uint32 nCount; + uint32 fieldCount; + char const* fmt; + T** indexTable; + T* m_dataTable; + StringPoolList m_stringPoolList; +}; + +extern DBCStorage <AreaTableEntry> sAreaStore;// recommend access using functions +extern DBCStorage <AreaTriggerEntry> sAreaTriggerStore; +extern DBCStorage <BankBagSlotPricesEntry> sBankBagSlotPricesStore; +extern DBCStorage <BattlemasterListEntry> sBattlemasterListStore; +//extern DBCStorage <ChatChannelsEntry> sChatChannelsStore; -- accessed using function, no usable index +extern DBCStorage <CharTitlesEntry> sCharTitlesStore; +extern DBCStorage <ChrClassesEntry> sChrClassesStore; +extern DBCStorage <ChrRacesEntry> sChrRacesStore; +extern DBCStorage <CreatureDisplayInfoEntry> sCreatureDisplayInfoStore; +extern DBCStorage <CreatureFamilyEntry> sCreatureFamilyStore; +extern DBCStorage <CreatureSpellDataEntry> sCreatureSpellDataStore; +extern DBCStorage <DurabilityCostsEntry> sDurabilityCostsStore; +extern DBCStorage <DurabilityQualityEntry> sDurabilityQualityStore; +extern DBCStorage <EmotesTextEntry> sEmotesTextStore; +extern DBCStorage <FactionEntry> sFactionStore; +extern DBCStorage <FactionTemplateEntry> sFactionTemplateStore; +extern DBCStorage <GemPropertiesEntry> sGemPropertiesStore; + +extern DBCStorage <GtCombatRatingsEntry> sGtCombatRatingsStore; +extern DBCStorage <GtChanceToMeleeCritBaseEntry> sGtChanceToMeleeCritBaseStore; +extern DBCStorage <GtChanceToMeleeCritEntry> sGtChanceToMeleeCritStore; +extern DBCStorage <GtChanceToSpellCritBaseEntry> sGtChanceToSpellCritBaseStore; +extern DBCStorage <GtChanceToSpellCritEntry> sGtChanceToSpellCritStore; +extern DBCStorage <GtOCTRegenHPEntry> sGtOCTRegenHPStore; +//extern DBCStorage <GtOCTRegenMPEntry> sGtOCTRegenMPStore; -- not used currently +extern DBCStorage <GtRegenHPPerSptEntry> sGtRegenHPPerSptStore; +extern DBCStorage <GtRegenMPPerSptEntry> sGtRegenMPPerSptStore; +extern DBCStorage <ItemEntry> sItemStore; +//extern DBCStorage <ItemDisplayInfoEntry> sItemDisplayInfoStore; -- not used currently +extern DBCStorage <ItemExtendedCostEntry> sItemExtendedCostStore; +extern DBCStorage <ItemRandomPropertiesEntry> sItemRandomPropertiesStore; +extern DBCStorage <ItemRandomSuffixEntry> sItemRandomSuffixStore; +extern DBCStorage <ItemSetEntry> sItemSetStore; +extern DBCStorage <LockEntry> sLockStore; +extern DBCStorage <MailTemplateEntry> sMailTemplateStore; +extern DBCStorage <MapEntry> sMapStore; +extern DBCStorage <QuestSortEntry> sQuestSortStore; +extern DBCStorage <RandomPropertiesPointsEntry> sRandomPropertiesPointsStore; +extern DBCStorage <SkillLineEntry> sSkillLineStore; +extern DBCStorage <SkillLineAbilityEntry> sSkillLineAbilityStore; +extern DBCStorage <SoundEntriesEntry> sSoundEntriesStore; +extern DBCStorage <SpellCastTimesEntry> sSpellCastTimesStore; +extern DBCStorage <SpellDurationEntry> sSpellDurationStore; +extern DBCStorage <SpellFocusObjectEntry> sSpellFocusObjectStore; +extern DBCStorage <SpellItemEnchantmentEntry> sSpellItemEnchantmentStore; +extern DBCStorage <SpellItemEnchantmentConditionEntry> sSpellItemEnchantmentConditionStore; +extern SpellCategoryStore sSpellCategoryStore; +extern PetFamilySpellsStore sPetFamilySpellsStore; +extern DBCStorage <SpellRadiusEntry> sSpellRadiusStore; +extern DBCStorage <SpellRangeEntry> sSpellRangeStore; +extern DBCStorage <SpellShapeshiftEntry> sSpellShapeshiftStore; +extern DBCStorage <SpellEntry> sSpellStore; +extern DBCStorage <StableSlotPricesEntry> sStableSlotPricesStore; +extern DBCStorage <TalentEntry> sTalentStore; +extern DBCStorage <TalentTabEntry> sTalentTabStore; +extern DBCStorage <TaxiNodesEntry> sTaxiNodesStore; +extern DBCStorage <TaxiPathEntry> sTaxiPathStore; +extern TaxiMask sTaxiNodesMask; +extern TaxiPathSetBySource sTaxiPathSetBySource; +extern TaxiPathNodesByPath sTaxiPathNodesByPath; +extern DBCStorage <TotemCategoryEntry> sTotemCategoryStore; +//extern DBCStorage <WorldMapAreaEntry> sWorldMapAreaStore; -- use Zone2MapCoordinates and Map2ZoneCoordinates +extern DBCStorage <WorldSafeLocsEntry> sWorldSafeLocsStore; + +void LoadDBCStores(std::string dataPath); + +// script support functions +MANGOS_DLL_SPEC DBCStorage <SoundEntriesEntry> const* GetSoundEntriesStore(); +MANGOS_DLL_SPEC DBCStorage <SpellEntry> const* GetSpellStore(); +MANGOS_DLL_SPEC DBCStorage <SpellRangeEntry> const* GetSpellRangeStore(); +#endif diff --git a/src/shared/Database/DBCStructure.h b/src/shared/Database/DBCStructure.h new file mode 100644 index 00000000000..c0370180511 --- /dev/null +++ b/src/shared/Database/DBCStructure.h @@ -0,0 +1,937 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef DBCSTRUCTURE_H +#define DBCSTRUCTURE_H + +#include "Platform/Define.h" + +#include <map> +#include <set> +#include <vector> + +// Structures using to access raw DBC data and required packing to portability + +// GCC have alternative #pragma pack(N) syntax and old gcc version not support pack(push,N), also any gcc version not support it at some platform +#if defined( __GNUC__ ) +#pragma pack(1) +#else +#pragma pack(push,1) +#endif + +enum AreaTeams +{ + AREATEAM_NONE = 0, + AREATEAM_ALLY = 2, + AREATEAM_HORDE = 4 +}; + +enum AreaFlags +{ + AREA_FLAG_SNOW = 0x00000001, // snow (only Dun Morogh, Naxxramas, Razorfen Downs and Winterspring) + AREA_FLAG_UNK1 = 0x00000002, // unknown, (only Naxxramas and Razorfen Downs) + AREA_FLAG_UNK2 = 0x00000004, // Only used on development map + AREA_FLAG_SLAVE_CAPITAL = 0x00000008, // slave capital city flag? + AREA_FLAG_UNK3 = 0x00000010, // unknown + AREA_FLAG_SLAVE_CAPITAL2 = 0x00000020, // slave capital city flag? + AREA_FLAG_UNK4 = 0x00000040, // many zones have this flag + AREA_FLAG_ARENA = 0x00000080, // arena, both instanced and world arenas + AREA_FLAG_CAPITAL = 0x00000100, // main capital city flag + AREA_FLAG_CITY = 0x00000200, // only for one zone named "City" (where it located?) + AREA_FLAG_OUTLAND = 0x00000400, // outland zones? (only Eye of the Storm not have this flag, but have 0x00004000 flag) + AREA_FLAG_SANCTUARY = 0x00000800, // sanctuary area (PvP disabled) + AREA_FLAG_NEED_FLY = 0x00001000, // only Netherwing Ledge, Socrethar's Seat, Tempest Keep, The Arcatraz, The Botanica, The Mechanar, Sorrow Wing Point, Dragonspine Ridge, Netherwing Mines, Dragonmaw Base Camp, Dragonmaw Skyway + AREA_FLAG_UNUSED1 = 0x00002000, // not used now (no area/zones with this flag set in 2.4.2) + AREA_FLAG_OUTLAND2 = 0x00004000, // outland zones? (only Circle of Blood Arena not have this flag, but have 0x00000400 flag) + AREA_FLAG_PVP = 0x00008000, // pvp objective area? (Death's Door also has this flag although it's no pvp object area) + AREA_FLAG_ARENA_INSTANCE = 0x00010000, // used by instanced arenas only + AREA_FLAG_UNUSED2 = 0x00020000, // not used now (no area/zones with this flag set in 2.4.2) + AREA_FLAG_UNK5 = 0x00040000, // just used for Amani Pass, Hatchet Hills + AREA_FLAG_LOWLEVEL = 0x00100000 // used for some starting areas with area_level <=15 +}; + +enum FactionTemplateFlags +{ + FACTION_TEMPLATE_FLAG_CONTESTED_GUARD = 0x00001000, // faction will attack players that were involved in PvP combats +}; + +struct AreaTableEntry +{ + uint32 ID; // 0 + uint32 mapid; // 1 + uint32 zone; // 2 if 0 then it's zone, else it's zone id of this area + uint32 exploreFlag; // 3, main index + uint32 flags; // 4, unknown value but 312 for all cities + // 5-9 unused + int32 area_level; // 10 + char* area_name[16]; // 11-26 + // 27, string flags, unused + uint32 team; // 28 +}; + +struct AreaTriggerEntry +{ + uint32 id; // 0 + uint32 mapid; // 1 + float x; // 2 + float y; // 3 + float z; // 4 + float radius; // 5 + float box_x; // 6 extent x edge + float box_y; // 7 extent y edge + float box_z; // 8 extent z edge + float box_orientation; // 9 extent rotation by about z axis +}; + +struct BankBagSlotPricesEntry +{ + uint32 ID; + uint32 price; +}; + +struct BattlemasterListEntry +{ + uint32 id; // 0 + uint32 mapid[3]; // 1-3 mapid + // 4-8 unused + uint32 type; // 9 (3 - BG, 4 - arena) + uint32 minlvl; // 10 + uint32 maxlvl; // 11 + uint32 maxplayersperteam; // 12 + // 13-14 unused + char* name[16]; // 15-30 + // 31 string flag, unused + // 32 unused +}; + +struct CharTitlesEntry +{ + uint32 ID; // 0, title ids, for example in Quest::GetCharTitleId() + //uint32 unk1; // 1 flags? + //char* name[16]; // 2-17, unused + // 18 string flag, unused + //char* name2[16]; // 19-34, unused + // 35 string flag, unused + uint32 bit_index; // 36 used in PLAYER_CHOSEN_TITLE and 1<<index in PLAYER__FIELD_KNOWN_TITLES +}; + +struct ChatChannelsEntry +{ + uint32 ChannelID; // 0 + uint32 flags; // 1 + char* pattern[16]; // 3-18 + // 19 string flags, unused + //char* name[16]; // 20-35 unused + // 36 string flag, unused +}; + +struct ChrClassesEntry +{ + uint32 ClassID; // 0 + // 1-2, unused + uint32 powerType; // 3 + // 4, unused + //char* name[16]; // 5-20 unused + // 21 string flag, unused + //char* string1[16]; // 21-36 unused + // 37 string flag, unused + //char* string2[16]; // 38-53 unused + // 54 string flag, unused + // 55, unused + uint32 spellfamily; // 56 + // 57, unused +}; + +struct ChrRacesEntry +{ + uint32 RaceID; // 0 + // 1 unused + uint32 FactionID; // 2 facton template id + // 3 unused + uint32 model_m; // 4 + uint32 model_f; // 5 + // 6-7 unused + uint32 TeamID; // 8 (7-Alliance 1-Horde) + // 9-12 unused + uint32 startmovie; // 13 id from CinematicCamera.dbc + char* name[16]; // 14-29 used for DBC language detection/selection + // 30 string flags, unused + //char* string1[16]; // 31-46 used for DBC language detection/selection + // 47 string flags, unused + //char* string2[16]; // 48-63 used for DBC language detection/selection + // 64 string flags, unused + // 65-67 unused + //uint32 addon // 68 (0 - original race, 1 - tbc addon, ...) unused +}; + +struct CreatureDisplayInfoEntry +{ + uint32 Displayid; // 0 + // 1-3,unused + float scale; // 4 + // 5-13,unused +}; + +struct CreatureFamilyEntry +{ + uint32 ID; // 0 + float minScale; // 1 + uint32 minScaleLevel; // 2 0/1 + float maxScale; // 3 + uint32 maxScaleLevel; // 4 0/60 + uint32 skillLine; // 5 + uint32 skillLine2; // 6 + uint32 petFoodMask; // 7 + char* Name[16]; // 8-23 + // 24 string flags, unused + // 25 icon, unused +}; + +struct CreatureSpellDataEntry +{ + uint32 ID; // 0 + //uint32 spellId[4]; // 1-4 hunter pet learned spell (for later use) +}; + +struct DurabilityCostsEntry +{ + uint32 Itemlvl; // 0 + uint32 multiplier[29]; // 1-29 +}; + +struct DurabilityQualityEntry +{ + uint32 Id; // 0 + float quality_mod; // 1 +}; + +struct EmotesTextEntry +{ + uint32 Id; + uint32 textid; +}; + +struct FactionEntry +{ + uint32 ID; // 0 + int32 reputationListID; // 1 + uint32 BaseRepRaceMask[4]; // 2-5 Base reputation race masks (see enum Races) + uint32 BaseRepClassMask[4]; // 6-9 Base reputation class masks (see enum Classes) + int32 BaseRepValue[4]; // 10-13 Base reputation values + uint32 ReputationFlags[4]; // 14-17 Default flags to apply + uint32 team; // 18 enum Team + char* name[16]; // 19-34 + // 35 string flags, unused + //char* description[16]; // 36-51 unused + // 52 string flags, unused +}; + +enum FactionMasks +{ + FACTION_MASK_PLAYER = 1, // any player + FACTION_MASK_ALLIANCE = 2, // player or creature from alliance team + FACTION_MASK_HORDE = 4, // player or creature from horde team + FACTION_MASK_MONSTER = 8 // aggressive creature from monster team + // if none flags set then non-aggressive creature +}; + +struct FactionTemplateEntry +{ + uint32 ID; // 0 + uint32 faction; // 1 + uint32 factionFlags; // 2 specific flags for that faction + uint32 ourMask; // 3 if mask set (see FactionMasks) then faction included in masked team + uint32 friendlyMask; // 4 if mask set (see FactionMasks) then faction friendly to masked team + uint32 hostileMask; // 5 if mask set (see FactionMasks) then faction hostile to masked team + uint32 enemyFaction1; // 6 + uint32 enemyFaction2; // 7 + uint32 enemyFaction3; // 8 + uint32 enemyFaction4; // 9 + uint32 friendFaction1; // 10 + uint32 friendFaction2; // 11 + uint32 friendFaction3; // 12 + uint32 friendFaction4; // 13 + //------------------------------------------------------- end structure + + // helpers + bool IsFriendlyTo(FactionTemplateEntry const& entry) const + { + if(enemyFaction1 == entry.faction || enemyFaction2 == entry.faction || enemyFaction3 == entry.faction || enemyFaction4 == entry.faction ) + return false; + if(friendFaction1 == entry.faction || friendFaction2 == entry.faction || friendFaction3 == entry.faction || friendFaction4 == entry.faction ) + return true; + return (friendlyMask & entry.ourMask) || (ourMask & entry.friendlyMask); + } + bool IsHostileTo(FactionTemplateEntry const& entry) const + { + if(enemyFaction1 == entry.faction || enemyFaction2 == entry.faction || enemyFaction3 == entry.faction || enemyFaction4 == entry.faction ) + return true; + if(friendFaction1 == entry.faction || friendFaction2 == entry.faction || friendFaction3 == entry.faction || friendFaction4 == entry.faction ) + return false; + return (hostileMask & entry.ourMask) != 0; + } + bool IsHostileToPlayers() const { return (hostileMask & FACTION_MASK_PLAYER) !=0; } + bool IsNeutralToAll() const { return hostileMask == 0 && friendlyMask == 0 && enemyFaction1==0 && enemyFaction2==0 && enemyFaction3==0 && enemyFaction4==0; } + bool IsContestedGuardFaction() const { return (factionFlags & FACTION_TEMPLATE_FLAG_CONTESTED_GUARD)!=0; } +}; + +struct GemPropertiesEntry +{ + uint32 ID; + uint32 spellitemenchantement; + uint32 color; +}; + +#define GT_MAX_LEVEL 100 +struct GtCombatRatingsEntry +{ + float ratio; +}; + +struct GtChanceToMeleeCritBaseEntry +{ + float base; +}; + +struct GtChanceToMeleeCritEntry +{ + float ratio; +}; + +struct GtChanceToSpellCritBaseEntry +{ + float base; +}; + +struct GtChanceToSpellCritEntry +{ + float ratio; +}; + +struct GtOCTRegenHPEntry +{ + float ratio; +}; + +//struct GtOCTRegenMPEntry +//{ +// float ratio; +//}; + +struct GtRegenHPPerSptEntry +{ + float ratio; +}; + +struct GtRegenMPPerSptEntry +{ + float ratio; +}; + +struct ItemEntry +{ + uint32 ID; + uint32 DisplayId; + uint32 InventoryType; + uint32 Sheath; +}; + +struct ItemDisplayInfoEntry +{ + uint32 ID; + uint32 randomPropertyChance; +}; + +//struct ItemCondExtCostsEntry +//{ +// uint32 ID; +// uint32 condExtendedCost; // ItemPrototype::CondExtendedCost +// uint32 itemextendedcostentry; // ItemPrototype::ExtendedCost +// uint32 arenaseason; // arena season number(1-4) +//}; + +struct ItemExtendedCostEntry +{ + uint32 ID; // 0 extended-cost entry id + uint32 reqhonorpoints; // 1 required honor points + uint32 reqarenapoints; // 2 required arena points + uint32 reqitem[5]; // 3-7 required item id + uint32 reqitemcount[5]; // 8-12 required count of 1st item + uint32 reqpersonalarenarating; // 13 required personal arena rating +}; + +struct ItemRandomPropertiesEntry +{ + uint32 ID; // 0 + //char* internalName // 1 unused + uint32 enchant_id[3]; // 2-4 + // 5-6 unused, 0 only values, reserved for additional enchantments? + //char* nameSuffix[16] // 7-22, unused + // 23 nameSufix flags, unused +}; + +struct ItemRandomSuffixEntry +{ + uint32 ID; // 0 + //char* name[16] // 1-16 unused + // 17, name flags, unused + // 18 unused + uint32 enchant_id[3]; // 19-21 + uint32 prefix[3]; // 22-24 +}; + +struct ItemSetEntry +{ + //uint32 id // 0 item set ID + char* name[16]; // 1-16 + // 17 string flags, unused + // 18-28 items from set, but not have all items listed, use ItemPrototype::ItemSet instead + // 29-34 unused + uint32 spells[8]; // 35-42 + uint32 items_to_triggerspell[8]; // 43-50 + uint32 required_skill_id; // 51 + uint32 required_skill_value; // 52 +}; + +struct LockEntry +{ + uint32 ID; // 0 + uint32 keytype[5]; // 1-5 + // 6-8, not used + uint32 key[5]; // 9-13 + // 14-16, not used + uint32 requiredminingskill; // 17 + uint32 requiredlockskill; // 18 + // 19-32, not used +}; + +struct MailTemplateEntry +{ + uint32 ID; // 0 + //char* subject[16]; // 1-16 + // 17 name flags, unused + //char* content[16]; // 18-33 +}; + +enum MapTypes +{ + MAP_COMMON = 0, + MAP_INSTANCE = 1, + MAP_RAID = 2, + MAP_BATTLEGROUND = 3, + MAP_ARENA = 4 +}; + +struct MapEntry +{ + uint32 MapID; // 0 + //char* internalname; // 1 unused + uint32 map_type; // 2 + // 3 unused + char* name[16]; // 4-19 + // 20 name flags, unused + // 21-23 unused (something PvPZone related - levels?) + // 24-26 + uint32 linked_zone; // 27 common zone for instance and continent map + //char* hordeIntro // 28-43 text for PvP Zones + // 44 intro text flags + //char* allianceIntro // 45-60 text for PvP Zones + // 46 intro text flags + // 47-61 not used + uint32 multimap_id; // 62 + // 63-65 not used + //chat* unknownText1 // 66-81 unknown empty text fields, possible normal Intro text. + // 82 text flags + //chat* heroicIntroText // 83-98 heroic mode requirement text + // 99 text flags + //chat* unknownText2 // 100-115 unknown empty text fields + // 116 text flags + int32 parent_map; // 117 map_id of parent map + //float start_x // 118 enter x coordinate (if exist single entry) + //float start_y // 119 enter y coordinate (if exist single entry) + uint32 resetTimeRaid; // 120 + uint32 resetTimeHeroic; // 121 + // 122-123 + uint32 addon; // 124 (0-original maps,1-tbc addon) + + // Helpers + bool IsExpansionMap() const { return addon != 0; } + bool Instanceable() const { return map_type == MAP_INSTANCE || map_type == MAP_RAID; } + // NOTE: this duplicate of Instanceable(), but Instanceable() can be changed when BG also will be instanceable + bool IsDungeon() const { return map_type == MAP_INSTANCE || map_type == MAP_RAID; } + bool IsRaid() const { return map_type == MAP_RAID; } + bool IsBattleGround() const { return map_type == MAP_BATTLEGROUND; } + bool IsBattleArena() const { return map_type == MAP_ARENA; } + bool IsBattleGroundOrArena() const { return map_type == MAP_BATTLEGROUND || map_type == MAP_ARENA; } + bool SupportsHeroicMode() const { return resetTimeHeroic && !resetTimeRaid; } + bool HasResetTime() const { return resetTimeHeroic || resetTimeRaid; } + + bool IsMountAllowed() const + { + return !IsDungeon() || + MapID==568 || MapID==309 || MapID==209 || MapID==534 || + MapID==560 || MapID==509 || MapID==269; + } +}; + +struct QuestSortEntry +{ + uint32 id; // 0, sort id + //char* name[16]; // 1-16, unused + // 17 name flags, unused +}; + +struct RandomPropertiesPointsEntry +{ + //uint32 Id; // 0 hidden key + uint32 itemLevel; // 1 + uint32 EpicPropertiesPoints[5]; // 2-6 + uint32 RarePropertiesPoints[5]; // 7-11 + uint32 UncommonPropertiesPoints[5]; // 12-16 +}; + +//struct SkillLineCategoryEntry{ +// uint32 id; // 0 hidden key +// char* name[16]; // 1 - 17 Category name +// // 18 string flag +// uint32 displayOrder; // Display order in character tab +//}; + +//struct SkillRaceClassInfoEntry{ +// uint32 id; // 0 +// uint32 skillId; // 1 present some refrences to unknown skill +// uint32 raceMask; // 2 +// uint32 classMask; // 3 +// uint32 flags; // 4 mask for some thing +// uint32 reqLevel; // 5 +// uint32 skillTierId; // 6 +// uint32 skillCostID; // 7 +//}; + +//struct SkillTiersEntry{ +// uint32 id; // 0 +// uint32 skillValue[16]; // 1-17 unknown possibly add value on learn? +// uint32 maxSkillValue[16]; // Max value for rank +//}; + +struct SkillLineEntry +{ + uint32 id; // 0 + uint32 categoryId; // 1 (index from SkillLineCategory.dbc) + //uint32 skillCostID; // 2 not used + char* name[16]; // 3-18 + // 19 string flags, not used + //char* description[16]; // 20-35, not used + // 36 string flags, not used + uint32 spellIcon; // 37 +}; + +enum AbilytyLearnType +{ + ABILITY_LEARNED_ON_GET_PROFESSION_SKILL = 1, + ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL = 2 +}; + +struct SkillLineAbilityEntry +{ + uint32 id; // 0, INDEX + uint32 skillId; // 1 + uint32 spellId; // 2 + uint32 racemask; // 3 + uint32 classmask; // 4 + //uint32 racemaskNot; // 5 always 0 in 2.4.2 + //uint32 classmaskNot; // 6 always 0 in 2.4.2 + uint32 req_skill_value; // 7 for trade skill.not for training. + uint32 forward_spellid; // 8 + uint32 learnOnGetSkill; // 9 can be 1 or 2 for spells learned on get skill + uint32 max_value; // 10 + uint32 min_value; // 11 + // 12-13, unknown, always 0 + uint32 reqtrainpoints; // 14 +}; + +struct SoundEntriesEntry +{ + uint32 Id; // 0, sound id + //uint32 Type; // 1, sound type (10 generally for creature, etc) + //char* InternalName; // 2, internal name, for use in lookup command for example + //char* FileName[10]; // 3-12, file names + //uint32 Unk13[10]; // 13-22, linked with file names? + //char* Path; // 23 + // 24-28, unknown +}; + +struct SpellEntry +{ + uint32 Id; // 0 normally counted from 0 field (but some tools start counting from 1, check this before tool use for data view!) + uint32 Category; // 1 + //uint32 castUI // 2 not used + uint32 Dispel; // 3 + uint32 Mechanic; // 4 + uint32 Attributes; // 5 + uint32 AttributesEx; // 6 + uint32 AttributesEx2; // 7 + uint32 AttributesEx3; // 8 + uint32 AttributesEx4; // 9 + uint32 AttributesEx5; // 10 + //uint32 AttributesEx6; // 11 not used + uint32 Stances; // 12 + uint32 StancesNot; // 13 + uint32 Targets; // 14 + uint32 TargetCreatureType; // 15 + uint32 RequiresSpellFocus; // 16 + //uint32 FacingCasterFlags; // 17 not used + uint32 CasterAuraState; // 18 + uint32 TargetAuraState; // 19 + uint32 CasterAuraStateNot; // 20 + uint32 TargetAuraStateNot; // 21 + uint32 CastingTimeIndex; // 22 + uint32 RecoveryTime; // 23 + uint32 CategoryRecoveryTime; // 24 + uint32 InterruptFlags; // 25 + uint32 AuraInterruptFlags; // 26 + uint32 ChannelInterruptFlags; // 27 + uint32 procFlags; // 28 + uint32 procChance; // 29 + uint32 procCharges; // 30 + uint32 maxLevel; // 31 + uint32 baseLevel; // 32 + uint32 spellLevel; // 33 + uint32 DurationIndex; // 34 + uint32 powerType; // 35 + uint32 manaCost; // 36 + uint32 manaCostPerlevel; // 37 + uint32 manaPerSecond; // 38 + uint32 manaPerSecondPerLevel; // 39 + uint32 rangeIndex; // 40 + float speed; // 41 + //uint32 modalNextSpell; // 42 + uint32 StackAmount; // 43 + uint32 Totem[2]; // 44-45 + int32 Reagent[8]; // 46-53 + uint32 ReagentCount[8]; // 54-61 + int32 EquippedItemClass; // 62 (value) + int32 EquippedItemSubClassMask; // 63 (mask) + int32 EquippedItemInventoryTypeMask; // 64 (mask) + uint32 Effect[3]; // 65-67 + int32 EffectDieSides[3]; // 68-70 + uint32 EffectBaseDice[3]; // 71-73 + float EffectDicePerLevel[3]; // 74-76 + float EffectRealPointsPerLevel[3]; // 77-79 + int32 EffectBasePoints[3]; // 80-82 (don't must be used in spell/auras explicitly, must be used cached Spell::m_currentBasePoints) + uint32 EffectMechanic[3]; // 83-85 + uint32 EffectImplicitTargetA[3]; // 86-88 + uint32 EffectImplicitTargetB[3]; // 89-91 + uint32 EffectRadiusIndex[3]; // 92-94 - spellradius.dbc + uint32 EffectApplyAuraName[3]; // 95-97 + uint32 EffectAmplitude[3]; // 98-100 + float EffectMultipleValue[3]; // 101-103 + uint32 EffectChainTarget[3]; // 104-106 + uint32 EffectItemType[3]; // 107-109 + int32 EffectMiscValue[3]; // 110-112 + int32 EffectMiscValueB[3]; // 113-115 + uint32 EffectTriggerSpell[3]; // 116-118 + float EffectPointsPerComboPoint[3]; // 119-121 + uint32 SpellVisual; // 122 + // 123 not used + uint32 SpellIconID; // 124 + uint32 activeIconID; // 125 + //uint32 spellPriority; // 126 + char* SpellName[16]; // 127-142 + //uint32 SpellNameFlag; // 143 + char* Rank[16]; // 144-159 + //uint32 RankFlags; // 160 + //char* Description[16]; // 161-176 not used + //uint32 DescriptionFlags; // 177 not used + //char* ToolTip[16]; // 178-193 not used + //uint32 ToolTipFlags; // 194 not used + uint32 ManaCostPercentage; // 195 + uint32 StartRecoveryCategory; // 196 + uint32 StartRecoveryTime; // 197 + uint32 MaxTargetLevel; // 198 + uint32 SpellFamilyName; // 199 + uint64 SpellFamilyFlags; // 200+201 + uint32 MaxAffectedTargets; // 202 + uint32 DmgClass; // 203 defenseType + uint32 PreventionType; // 204 + //uint32 StanceBarOrder; // 205 not used + float DmgMultiplier[3]; // 206-208 + //uint32 MinFactionId; // 209 not used, and 0 in 2.4.2 + //uint32 MinReputation; // 210 not used, and 0 in 2.4.2 + //uint32 RequiredAuraVision; // 211 not used + uint32 TotemCategory[2]; // 212-213 + uint32 AreaId; // 214 + uint32 SchoolMask; // 215 school mask + + private: + // prevent creating custom entries (copy data from original in fact) + SpellEntry(SpellEntry const&); // DON'T must have implementation +}; + +typedef std::set<uint32> SpellCategorySet; +typedef std::map<uint32,SpellCategorySet > SpellCategoryStore; +typedef std::set<uint32> PetFamilySpellsSet; +typedef std::map<uint32,PetFamilySpellsSet > PetFamilySpellsStore; + +struct SpellCastTimesEntry +{ + uint32 ID; // 0 + int32 CastTime; // 1 + //float CastTimePerLevel; // 2 unsure / per skill? + //int32 MinCastTime; // 3 unsure +}; + +struct SpellFocusObjectEntry +{ + uint32 ID; // 0 + //char* Name[16]; // 1-15 unused + // 16 string flags, unused +}; + +// stored in SQL table +struct SpellThreatEntry +{ + uint32 spellId; + int32 threat; +}; + +struct SpellRadiusEntry +{ + uint32 ID; + float Radius; + float Radius2; +}; + +struct SpellRangeEntry +{ + uint32 ID; + float minRange; + float maxRange; +}; + +struct SpellShapeshiftEntry +{ + uint32 ID; // 0 + //uint32 buttonPosition; // 1 unused + //char* Name[16]; // 2-17 unused + //uint32 NameFlags; // 18 unused + uint32 flags1; // 19 + int32 creatureType; // 20 <=0 humanoid, other normal creature types + //uint32 unk1; // 21 unused + uint32 attackSpeed; // 22 + //uint32 modelID; // 23 unused, alliance modelid (where horde case?) + //uint32 unk2; // 24 unused + //uint32 unk3; // 25 unused + //uint32 unk4; // 26 unused + //uint32 unk5; // 27 unused + //uint32 unk6; // 28 unused + //uint32 unk7; // 29 unused + //uint32 unk8; // 30 unused + //uint32 unk9; // 31 unused + //uint32 unk10; // 32 unused + //uint32 unk11; // 33 unused + //uint32 unk12; // 34 unused +}; + +struct SpellDurationEntry +{ + uint32 ID; + int32 Duration[3]; +}; + +enum ItemEnchantmentType +{ + ITEM_ENCHANTMENT_TYPE_NONE = 0, + ITEM_ENCHANTMENT_TYPE_COMBAT_SPELL = 1, + ITEM_ENCHANTMENT_TYPE_DAMAGE = 2, + ITEM_ENCHANTMENT_TYPE_EQUIP_SPELL = 3, + ITEM_ENCHANTMENT_TYPE_RESISTANCE = 4, + ITEM_ENCHANTMENT_TYPE_STAT = 5, + ITEM_ENCHANTMENT_TYPE_TOTEM = 6 +}; + +struct SpellItemEnchantmentEntry +{ + uint32 ID; // 0 + uint32 type[3]; // 1-3 + uint32 amount[3]; // 4-6 + //uint32 amount2[3] // 7-9 always same as similar `amount` value + uint32 spellid[3]; // 10-12 + char* description[16]; // 13-29 + // 30 description flags + uint32 aura_id; // 31 + uint32 slot; // 32 + uint32 GemID; // 33 + uint32 EnchantmentCondition; // 34 +}; + +struct SpellItemEnchantmentConditionEntry +{ + uint32 ID; + uint8 Color[5]; + uint8 Comparator[5]; + uint8 CompareColor[5]; + uint32 Value[5]; +}; + +struct StableSlotPricesEntry +{ + uint32 Slot; + uint32 Price; +}; + +struct TalentEntry +{ + uint32 TalentID; // 0 + uint32 TalentTab; // 1 index in TalentTab.dbc (TalentTabEntry) + uint32 Row; // 2 + uint32 Col; // 3 + uint32 RankID[5]; // 4-8 + // 9-12 not used, always 0, maybe not used high ranks + uint32 DependsOn; // 13 index in Talent.dbc (TalentEntry) + // 14-15 not used + uint32 DependsOnRank; // 16 + // 17-19 not used + uint32 DependsOnSpell; // 20 req.spell +}; + +struct TalentTabEntry +{ + uint32 TalentTabID; // 0 + //char* name[16]; // 1-16, unused + //uint32 nameFlags; // 17, unused + //unit32 spellicon; // 18 + // 19 not used + uint32 ClassMask; // 20 + uint32 tabpage; // 21 + //char* internalname; // 22 +}; + +struct TaxiPathEntry +{ + uint32 ID; + uint32 from; + uint32 to; + uint32 price; +}; + +struct TaxiNodesEntry +{ + uint32 ID; // 0 + uint32 map_id; // 1 + float x; // 2 + float y; // 3 + float z; // 4 + //char* name[16]; // 5-21 + // 22 string flags, unused + uint32 horde_mount_type; // 23 + uint32 alliance_mount_type; // 24 +}; + +enum TotemCategoryType +{ + TOTEM_CATEGORY_TYPE_KNIFE = 1, + TOTEM_CATEGORY_TYPE_TOTEM = 2, + TOTEM_CATEGORY_TYPE_ROD = 3, + TOTEM_CATEGORY_TYPE_PICK = 21, + TOTEM_CATEGORY_TYPE_STONE = 22, + TOTEM_CATEGORY_TYPE_HAMMER = 23, + TOTEM_CATEGORY_TYPE_SPANNER = 24 +}; + +struct TotemCategoryEntry +{ + uint32 ID; // 0 + //char* name[16]; // 1-16 + // 17 string flags, unused + uint32 categoryType; // 18 (one for specialization) + uint32 categoryMask; // 19 (compatibility mask for same type: different for totems, compatible from high to low for rods) +}; + +struct WorldMapAreaEntry +{ + //uint32 ID; // 0 + uint32 map_id; // 1 + uint32 area_id; // 2 index (continent 0 areas ignored) + //char* internal_name // 3 + float y1; // 4 + float y2; // 5 + float x1; // 6 + float x2; // 7 + int32 virtual_map_id; // 8 -1 (map_id have correct map) other: virtual map where zone show (map_id - where zone in fact internally) +}; + +struct WorldSafeLocsEntry +{ + uint32 ID; // 0 + uint32 map_id; // 1 + float x; // 2 + float y; // 3 + float z; // 4 + //char* name[16] // 5-20 name, unused + // 21 name flags, unused +}; + +// GCC have alternative #pragma pack() syntax and old gcc version not support pack(pop), also any gcc version not support it at some platform +#if defined( __GNUC__ ) +#pragma pack() +#else +#pragma pack(pop) +#endif + +// Structures not used for casting to loaded DBC data and not required then packing +struct TalentSpellPos +{ + TalentSpellPos() : talent_id(0), rank(0) {} + TalentSpellPos(uint16 _talent_id, uint8 _rank) : talent_id(_talent_id), rank(_rank) {} + + uint16 talent_id; + uint8 rank; +}; + +typedef std::map<uint32,TalentSpellPos> TalentSpellPosMap; + +struct TaxiPathBySourceAndDestination +{ + TaxiPathBySourceAndDestination() : ID(0),price(0) {} + TaxiPathBySourceAndDestination(uint32 _id,uint32 _price) : ID(_id),price(_price) {} + + uint32 ID; + uint32 price; +}; +typedef std::map<uint32,TaxiPathBySourceAndDestination> TaxiPathSetForSource; +typedef std::map<uint32,TaxiPathSetForSource> TaxiPathSetBySource; + +struct TaxiPathNode +{ + TaxiPathNode() : mapid(0), x(0),y(0),z(0),actionFlag(0),delay(0) {} + TaxiPathNode(uint32 _mapid, float _x, float _y, float _z, uint32 _actionFlag, uint32 _delay) : mapid(_mapid), x(_x),y(_y),z(_z),actionFlag(_actionFlag),delay(_delay) {} + + uint32 mapid; + float x; + float y; + float z; + uint32 actionFlag; + uint32 delay; +}; +typedef std::vector<TaxiPathNode> TaxiPathNodeList; +typedef std::vector<TaxiPathNodeList> TaxiPathNodesByPath; + +#define TaxiMaskSize 16 +typedef uint32 TaxiMask[TaxiMaskSize]; +#endif diff --git a/src/shared/Database/DBCfmt.cpp b/src/shared/Database/DBCfmt.cpp new file mode 100644 index 00000000000..651a0b12944 --- /dev/null +++ b/src/shared/Database/DBCfmt.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +const char AreaTableEntryfmt[]="iiinixxxxxissssssssssssssssxixxxxxx"; +const char AreaTriggerEntryfmt[]="niffffffff"; +const char BankBagSlotPricesEntryfmt[]="ni"; +const char BattlemasterListEntryfmt[]="niiixxxxxiiiixxssssssssssssssssxx"; +const char CharTitlesEntryfmt[]="nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxi"; +const char ChatChannelsEntryfmt[]="iixssssssssssssssssxxxxxxxxxxxxxxxxxx"; + // ChatChannelsEntryfmt, index not used (more compact store) +const char ChrClassesEntryfmt[]="nxixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxix"; +const char ChrRacesEntryfmt[]="nxixiixxixxxxissssssssssssssssxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; +const char CreatureDisplayInfofmt[]="nxxxfxxxxxxxxx"; +const char CreatureFamilyfmt[]="nfifiiiissssssssssssssssxx"; +const char CreatureSpellDatafmt[]="nxxxxxxxx"; +const char DurabilityCostsfmt[]="niiiiiiiiiiiiiiiiiiiiiiiiiiiii"; +const char DurabilityQualityfmt[]="nf"; +const char EmoteEntryfmt[]="nxixxxxxxxxxxxxxxxx"; +const char FactionEntryfmt[]="niiiiiiiiiiiiiiiiiissssssssssssssssxxxxxxxxxxxxxxxxxx"; +const char FactionTemplateEntryfmt[]="niiiiiiiiiiiii"; +const char GemPropertiesEntryfmt[]="nixxi"; +const char GtCombatRatingsfmt[]="f"; +const char GtChanceToMeleeCritBasefmt[]="f"; +const char GtChanceToMeleeCritfmt[]="f"; +const char GtChanceToSpellCritBasefmt[]="f"; +const char GtChanceToSpellCritfmt[]="f"; +const char GtOCTRegenHPfmt[]="f"; +//const char GtOCTRegenMPfmt[]="f"; +const char GtRegenHPPerSptfmt[]="f"; +const char GtRegenMPPerSptfmt[]="f"; +const char Itemfmt[]="niii"; +//const char ItemDisplayTemplateEntryfmt[]="nxxxxxxxxxxixxxxxxxxxxx"; +//const char ItemCondExtCostsEntryfmt[]="xiii"; +const char ItemExtendedCostEntryfmt[]="niiiiiiiiiiiii"; +const char ItemRandomPropertiesfmt[]="nxiiixxxxxxxxxxxxxxxxxxx"; +const char ItemRandomSuffixfmt[]="nxxxxxxxxxxxxxxxxxxiiiiii"; +const char ItemSetEntryfmt[]="dssssssssssssssssxxxxxxxxxxxxxxxxxxiiiiiiiiiiiiiiiiii"; +const char LockEntryfmt[]="niiiiixxxiiiiixxxiixxxxxxxxxxxxxx"; +const char MailTemplateEntryfmt[]="nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; +const char MapEntryfmt[]="nxixssssssssssssssssxxxxxxxixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxixxiixxi"; +const char QuestSortEntryfmt[]="nxxxxxxxxxxxxxxxxx"; +const char RandomPropertiesPointsfmt[]="niiiiiiiiiiiiiii"; +const char SkillLinefmt[]="nixssssssssssssssssxxxxxxxxxxxxxxxxxxi"; +const char SkillLineAbilityfmt[]="niiiixxiiiiixxi"; +const char SoundEntriesfmt[]="nxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; +const char SpellCastTimefmt[]="nixx"; +const char SpellDurationfmt[]="niii"; +const char SpellEntryfmt[]="nixiiiiiiiixiiiiixiiiiiiiiiiiiiiiiiiiiiiifxiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiffffffiiiiiiiiiiiiiiiiiiiiifffiiiiiiiiiiiiiiifffixiixssssssssssssssssxssssssssssssssssxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxiiiiiiiiiixfffxxxiiii"; +const char SpellFocusObjectfmt[]="nxxxxxxxxxxxxxxxxx"; +const char SpellItemEnchantmentfmt[]="niiiiiixxxiiissssssssssssssssxiiii"; +const char SpellItemEnchantmentConditionfmt[]="nbbbbbxxxxxbbbbbbbbbbiiiiiXXXXX"; +const char SpellRadiusfmt[]="nfxf"; +const char SpellRangefmt[]="nffxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; +const char SpellShapeshiftfmt[]="nxxxxxxxxxxxxxxxxxxiixixxxxxxxxxxxx"; +const char StableSlotPricesfmt[] = "ni"; +const char TalentEntryfmt[]="niiiiiiiixxxxixxixxxi"; +const char TalentTabEntryfmt[]="nxxxxxxxxxxxxxxxxxxxiix"; +const char TaxiNodesEntryfmt[]="nifffxxxxxxxxxxxxxxxxxii"; +const char TaxiPathEntryfmt[]="niii"; +const char TaxiPathNodeEntryfmt[]="diiifffiixx"; +const char TotemCategoryEntryfmt[]="nxxxxxxxxxxxxxxxxxii"; +const char WorldMapAreaEntryfmt[]="xinxffffi"; +const char WorldSafeLocsEntryfmt[]="nifffxxxxxxxxxxxxxxxxx"; diff --git a/src/shared/Database/Database.cpp b/src/shared/Database/Database.cpp new file mode 100644 index 00000000000..5726aeaa340 --- /dev/null +++ b/src/shared/Database/Database.cpp @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "DatabaseEnv.h" +#include "Config/ConfigEnv.h" + +#include <ctime> +#include <iostream> +#include <fstream> + +Database::~Database() +{ + /*Delete objects*/ +} + +bool Database::Initialize(const char *) +{ + // Enable logging of SQL commands (usally only GM commands) + // (See method: PExecuteLog) + m_logSQL = sConfig.GetBoolDefault("LogSQL", false); + m_logsDir = sConfig.GetStringDefault("LogsDir",""); + if(!m_logsDir.empty()) + { + if((m_logsDir.at(m_logsDir.length()-1)!='/') && (m_logsDir.at(m_logsDir.length()-1)!='\\')) + m_logsDir.append("/"); + } + + return true; +} + +void Database::ThreadStart() +{ +} + +void Database::ThreadEnd() +{ +} + +void Database::escape_string(std::string& str) +{ + if(str.empty()) + return; + + char* buf = new char[str.size()*2+1]; + escape_string(buf,str.c_str(),str.size()); + str = buf; + delete[] buf; +} + +bool Database::PExecuteLog(const char * format,...) +{ + if (!format) + return false; + + va_list ap; + char szQuery [MAX_QUERY_LEN]; + va_start(ap, format); + int res = vsnprintf( szQuery, MAX_QUERY_LEN, format, ap ); + va_end(ap); + + if(res==-1) + { + sLog.outError("SQL Query truncated (and not execute) for format: %s",format); + return false; + } + + if( m_logSQL ) + { + time_t curr; + tm local; + time(&curr); // get current time_t value + local=*(localtime(&curr)); // dereference and assign + char fName[128]; + sprintf( fName, "%04d-%02d-%02d_logSQL.sql", local.tm_year+1900, local.tm_mon+1, local.tm_mday ); + + FILE* log_file; + std::string logsDir_fname = m_logsDir+fName; + log_file = fopen(logsDir_fname.c_str(), "a"); + if (log_file) + { + fprintf(log_file, "%s;\n", szQuery); + fclose(log_file); + } + else + { + // The file could not be opened + sLog.outError("SQL-Logging is disabled - Log file for the SQL commands could not be openend: %s",fName); + } + } + + return Execute(szQuery); +} + +void Database::SetResultQueue(SqlResultQueue * queue) +{ + m_queryQueues[ZThread::ThreadImpl::current()] = queue; +} + +QueryResult* Database::PQuery(const char *format,...) +{ + if(!format) return NULL; + + va_list ap; + char szQuery [MAX_QUERY_LEN]; + va_start(ap, format); + int res = vsnprintf( szQuery, MAX_QUERY_LEN, format, ap ); + va_end(ap); + + if(res==-1) + { + sLog.outError("SQL Query truncated (and not execute) for format: %s",format); + return false; + } + + return Query(szQuery); +} + +bool Database::PExecute(const char * format,...) +{ + if (!format) + return false; + + va_list ap; + char szQuery [MAX_QUERY_LEN]; + va_start(ap, format); + int res = vsnprintf( szQuery, MAX_QUERY_LEN, format, ap ); + va_end(ap); + + if(res==-1) + { + sLog.outError("SQL Query truncated (and not execute) for format: %s",format); + return false; + } + + return Execute(szQuery); +} + +bool Database::DirectPExecute(const char * format,...) +{ + if (!format) + return false; + + va_list ap; + char szQuery [MAX_QUERY_LEN]; + va_start(ap, format); + int res = vsnprintf( szQuery, MAX_QUERY_LEN, format, ap ); + va_end(ap); + + if(res==-1) + { + sLog.outError("SQL Query truncated (and not execute) for format: %s",format); + return false; + } + + return DirectExecute(szQuery); +} diff --git a/src/shared/Database/Database.h b/src/shared/Database/Database.h new file mode 100644 index 00000000000..40097773430 --- /dev/null +++ b/src/shared/Database/Database.h @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef DATABASE_H +#define DATABASE_H + +#include "zthread/Thread.h" +#include "../src/zthread/ThreadImpl.h" +#include "Utilities/HashMap.h" +#include "Database/SqlDelayThread.h" + +class SqlTransaction; +class SqlResultQueue; +class SqlQueryHolder; + +typedef HM_NAMESPACE::hash_map<ZThread::ThreadImpl*, SqlTransaction*> TransactionQueues; +typedef HM_NAMESPACE::hash_map<ZThread::ThreadImpl*, SqlResultQueue*> QueryQueues; + +#define MAX_QUERY_LEN 1024 + +class MANGOS_DLL_SPEC Database +{ + protected: + Database() : m_threadBody(NULL), m_delayThread(NULL) {}; + + TransactionQueues m_tranQueues; ///< Transaction queues from diff. threads + QueryQueues m_queryQueues; ///< Query queues from diff threads + SqlDelayThread* m_threadBody; ///< Pointer to delay sql executer + ZThread::Thread* m_delayThread; ///< Pointer to executer thread + + public: + + virtual ~Database(); + + virtual bool Initialize(const char *infoString); + virtual void InitDelayThread() = 0; + virtual void HaltDelayThread() = 0; + + virtual QueryResult* Query(const char *sql) = 0; + QueryResult* PQuery(const char *format,...) ATTR_PRINTF(2,3); + + /// Async queries and query holders, implemented in DatabaseImpl.h + template<class Class> + bool AsyncQuery(Class *object, void (Class::*method)(QueryResult*), const char *sql); + template<class Class, typename ParamType1> + bool AsyncQuery(Class *object, void (Class::*method)(QueryResult*, ParamType1), ParamType1 param1, const char *sql); + template<typename ParamType1> + bool AsyncQuery(void (*method)(QueryResult*, ParamType1), ParamType1 param1, const char *sql); + template<class Class> + bool AsyncPQuery(Class *object, void (Class::*method)(QueryResult*), const char *format,...) ATTR_PRINTF(4,5); + template<class Class, typename ParamType1> + bool AsyncPQuery(Class *object, void (Class::*method)(QueryResult*, ParamType1), ParamType1 param1, const char *format,...) ATTR_PRINTF(5,6); + template<typename ParamType1> + bool AsyncPQuery(void (*method)(QueryResult*, ParamType1), ParamType1 param1, const char *format,...) ATTR_PRINTF(5,6); + template<class Class> + bool DelayQueryHolder(Class *object, void (Class::*method)(QueryResult*, SqlQueryHolder*), SqlQueryHolder *holder); + template<class Class, typename ParamType1> + bool DelayQueryHolder(Class *object, void (Class::*method)(QueryResult*, SqlQueryHolder*, ParamType1), SqlQueryHolder *holder, ParamType1 param1); + + virtual bool Execute(const char *sql) = 0; + bool PExecute(const char *format,...) ATTR_PRINTF(2,3); + virtual bool DirectExecute(const char* sql) = 0; + bool DirectPExecute(const char *format,...) ATTR_PRINTF(2,3); + + // Writes SQL commands to a LOG file (see mangosd.conf "LogSQL") + bool PExecuteLog(const char *format,...) ATTR_PRINTF(2,3); + + virtual bool BeginTransaction() // nothing do if DB not support transactions + { + return true; + } + virtual bool CommitTransaction() // nothing do if DB not support transactions + { + return true; + } + virtual bool RollbackTransaction() // can't rollback without transaction support + { + return false; + } + + virtual operator bool () const = 0; + + virtual unsigned long escape_string(char *to, const char *from, unsigned long length) { strncpy(to,from,length); return length; } + void escape_string(std::string& str); + + // must be called before first query in thread (one time for thread using one from existed Database objects) + virtual void ThreadStart(); + // must be called before finish thread run (one time for thread using one from existed Database objects) + virtual void ThreadEnd(); + + // sets the result queue of the current thread, be careful what thread you call this from + void SetResultQueue(SqlResultQueue * queue); + + private: + bool m_logSQL; + std::string m_logsDir; +}; +#endif diff --git a/src/shared/Database/DatabaseEnv.h b/src/shared/Database/DatabaseEnv.h new file mode 100644 index 00000000000..e393b4d6de5 --- /dev/null +++ b/src/shared/Database/DatabaseEnv.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined(DATABASEENV_H) +#define DATABASEENV_H + +#include "Common.h" +#include "Log.h" +#include "Errors.h" + +#include "Database/DBCStores.h" +#include "Database/Field.h" +#include "Database/QueryResult.h" + +#ifdef DO_POSTGRESQL +#include "Database/QueryResultPostgre.h" +#include "Database/Database.h" +#include "Database/DatabasePostgre.h" +typedef DatabasePostgre DatabaseType; +#define _LIKE_ "ILIKE" +#define _TABLE_SIM_ "\"" +#else +#include "Database/QueryResultMysql.h" +#include "Database/QueryResultSqlite.h" +#include "Database/Database.h" +#include "Database/DatabaseMysql.h" +#include "Database/DatabaseSqlite.h" +typedef DatabaseMysql DatabaseType; +#define _LIKE_ "LIKE" +#define _TABLE_SIM_ "`" +#endif + +extern DatabaseType WorldDatabase; +extern DatabaseType CharacterDatabase; +extern DatabaseType loginDatabase; + +#endif diff --git a/src/shared/Database/DatabaseImpl.h b/src/shared/Database/DatabaseImpl.h new file mode 100644 index 00000000000..682f7b25ef7 --- /dev/null +++ b/src/shared/Database/DatabaseImpl.h @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Database/Database.h" +#include "Database/SqlOperations.h" + +/// Function body definitions for the template function members of the Database class + +template<class Class> +bool +Database::AsyncQuery(Class *object, void (Class::*method)(QueryResult*), const char *sql) +{ + if (!sql) return false; + ZThread::ThreadImpl * queryThread = ZThread::ThreadImpl::current(); + QueryQueues::iterator itr = m_queryQueues.find(queryThread); + if (itr == m_queryQueues.end()) return false; + m_threadBody->Delay(new SqlQuery(sql, new MaNGOS::QueryCallback<Class>(object, method), itr->second)); + return true; +} + +template<class Class, typename ParamType1> +bool +Database::AsyncQuery(Class *object, void (Class::*method)(QueryResult*, ParamType1), ParamType1 param1, const char *sql) +{ + if (!sql) return false; + ZThread::ThreadImpl * queryThread = ZThread::ThreadImpl::current(); + QueryQueues::iterator itr = m_queryQueues.find(queryThread); + if (itr == m_queryQueues.end()) return false; + m_threadBody->Delay(new SqlQuery(sql, new MaNGOS::QueryCallback<Class, ParamType1>(object, method, (QueryResult*)NULL, param1), itr->second)); + return true; +} + +template<typename ParamType1> +bool +Database::AsyncQuery(void (*method)(QueryResult*, ParamType1), ParamType1 param1, const char *sql) +{ + if (!sql) return false; + ZThread::ThreadImpl * queryThread = ZThread::ThreadImpl::current(); + QueryQueues::iterator itr = m_queryQueues.find(queryThread); + if (itr == m_queryQueues.end()) return false; + m_threadBody->Delay(new SqlQuery(sql, new MaNGOS::SQueryCallback<ParamType1>(method, (QueryResult*)NULL, param1), itr->second)); + return true; +} + +template<class Class> +bool +Database::AsyncPQuery(Class *object, void (Class::*method)(QueryResult*), const char *format,...) +{ + if(!format) return false; + + va_list ap; + char szQuery [MAX_QUERY_LEN]; + va_start(ap, format); + int res = vsnprintf( szQuery, MAX_QUERY_LEN, format, ap ); + va_end(ap); + + if(res==-1) + { + sLog.outError("SQL Query truncated (and not execute) for format: %s",format); + return false; + } + + return AsyncQuery(object, method, szQuery); +} + +template<class Class, typename ParamType1> +bool +Database::AsyncPQuery(Class *object, void (Class::*method)(QueryResult*, ParamType1), ParamType1 param1, const char *format,...) +{ + if(!format) return false; + + va_list ap; + char szQuery [MAX_QUERY_LEN]; + va_start(ap, format); + int res = vsnprintf( szQuery, MAX_QUERY_LEN, format, ap ); + va_end(ap); + + if(res==-1) + { + sLog.outError("SQL Query truncated (and not execute) for format: %s",format); + return false; + } + + return AsyncQuery(object, method, param1, szQuery); +} + +template<typename ParamType1> +bool +Database::AsyncPQuery(void (*method)(QueryResult*, ParamType1), ParamType1 param1, const char *format,...) +{ + if(!format) return false; + + va_list ap; + char szQuery [MAX_QUERY_LEN]; + va_start(ap, format); + int res = vsnprintf( szQuery, MAX_QUERY_LEN, format, ap ); + va_end(ap); + + if(res==-1) + { + sLog.outError("SQL Query truncated (and not execute) for format: %s",format); + return false; + } + + return AsyncQuery(method, param1, szQuery); +} + + +template<class Class> +bool +Database::DelayQueryHolder(Class *object, void (Class::*method)(QueryResult*, SqlQueryHolder*), SqlQueryHolder *holder) +{ + if (!holder) return false; + ZThread::ThreadImpl * queryThread = ZThread::ThreadImpl::current(); + QueryQueues::iterator itr = m_queryQueues.find(queryThread); + if (itr == m_queryQueues.end()) return false; + holder->Execute(new MaNGOS::QueryCallback<Class, SqlQueryHolder*>(object, method, (QueryResult*)NULL, holder), m_threadBody, itr->second); + return true; +} + +template<class Class, typename ParamType1> +bool +Database::DelayQueryHolder(Class *object, void (Class::*method)(QueryResult*, SqlQueryHolder*, ParamType1), SqlQueryHolder *holder, ParamType1 param1) +{ + if (!holder) return false; + ZThread::ThreadImpl * queryThread = ZThread::ThreadImpl::current(); + QueryQueues::iterator itr = m_queryQueues.find(queryThread); + if (itr == m_queryQueues.end()) return false; + holder->Execute(new MaNGOS::QueryCallback<Class, SqlQueryHolder*, ParamType1>(object, method, (QueryResult*)NULL, holder, param1), m_threadBody, itr->second); + return true; +} diff --git a/src/shared/Database/DatabaseMysql.cpp b/src/shared/Database/DatabaseMysql.cpp new file mode 100644 index 00000000000..b736a60aa1b --- /dev/null +++ b/src/shared/Database/DatabaseMysql.cpp @@ -0,0 +1,408 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef DO_POSTGRESQL + +#include "Util.h" +#include "Policies/SingletonImp.h" +#include "Platform/Define.h" +#include "../src/zthread/ThreadImpl.h" +#include "DatabaseEnv.h" +#include "Database/MySQLDelayThread.h" +#include "Database/SqlOperations.h" +#include "Timer.h" + +void DatabaseMysql::ThreadStart() +{ + mysql_thread_init(); +} + +void DatabaseMysql::ThreadEnd() +{ + mysql_thread_end(); +} + +size_t DatabaseMysql::db_count = 0; + +DatabaseMysql::DatabaseMysql() : Database(), mMysql(0) +{ + // before first connection + if( db_count++ == 0 ) + { + // Mysql Library Init + mysql_library_init(-1, NULL, NULL); + + if (!mysql_thread_safe()) + { + sLog.outError("FATAL ERROR: Used MySQL library isn't thread-safe."); + exit(1); + } + } +} + +DatabaseMysql::~DatabaseMysql() +{ + if (m_delayThread) + HaltDelayThread(); + + if (mMysql) + mysql_close(mMysql); + + //Free Mysql library pointers for last ~DB + if(--db_count == 0) + mysql_library_end(); +} + +bool DatabaseMysql::Initialize(const char *infoString) +{ + + if(!Database::Initialize(infoString)) + return false; + + tranThread = NULL; + MYSQL *mysqlInit; + mysqlInit = mysql_init(NULL); + if (!mysqlInit) + { + sLog.outError( "Could not initialize Mysql connection" ); + return false; + } + + InitDelayThread(); + + Tokens tokens = StrSplit(infoString, ";"); + + Tokens::iterator iter; + + std::string host, port_or_socket, user, password, database; + int port; + char const* unix_socket; + + iter = tokens.begin(); + + if(iter != tokens.end()) + host = *iter++; + if(iter != tokens.end()) + port_or_socket = *iter++; + if(iter != tokens.end()) + user = *iter++; + if(iter != tokens.end()) + password = *iter++; + if(iter != tokens.end()) + database = *iter++; + + mysql_options(mysqlInit,MYSQL_SET_CHARSET_NAME,"utf8"); + #ifdef WIN32 + if(host==".") // named pipe use option (Windows) + { + unsigned int opt = MYSQL_PROTOCOL_PIPE; + mysql_options(mysqlInit,MYSQL_OPT_PROTOCOL,(char const*)&opt); + port = 0; + unix_socket = 0; + } + else // generic case + { + port = atoi(port_or_socket.c_str()); + unix_socket = 0; + } + #else + if(host==".") // socket use option (Unix/Linux) + { + unsigned int opt = MYSQL_PROTOCOL_SOCKET; + mysql_options(mysqlInit,MYSQL_OPT_PROTOCOL,(char const*)&opt); + host = "localhost"; + port = 0; + unix_socket = port_or_socket.c_str(); + } + else // generic case + { + port = atoi(port_or_socket.c_str()); + unix_socket = 0; + } + #endif + + mMysql = mysql_real_connect(mysqlInit, host.c_str(), user.c_str(), + password.c_str(), database.c_str(), port, unix_socket, 0); + + if (mMysql) + { + sLog.outDetail( "Connected to MySQL database at %s", + host.c_str()); + sLog.outString( "MySQL client library: %s", mysql_get_client_info()); + sLog.outString( "MySQL server ver: %s ", mysql_get_server_info( mMysql)); + + /*----------SET AUTOCOMMIT ON---------*/ + // It seems mysql 5.0.x have enabled this feature + // by default. In crash case you can lose data!!! + // So better to turn this off + // --- + // This is wrong since mangos use transactions, + // autocommit is turned of during it. + // Setting it to on makes atomic updates work + if (!mysql_autocommit(mMysql, 1)) + sLog.outDetail("AUTOCOMMIT SUCCESSFULLY SET TO 1"); + else + sLog.outDetail("AUTOCOMMIT NOT SET TO 1"); + /*-------------------------------------*/ + + // set connection properties to UTF8 to properly handle locales for different + // server configs - core sends data in UTF8, so MySQL must expect UTF8 too + PExecute("SET NAMES `utf8`"); + PExecute("SET CHARACTER SET `utf8`"); + + return true; + } + else + { + sLog.outError( "Could not connect to MySQL database at %s: %s\n", + host.c_str(),mysql_error(mysqlInit)); + mysql_close(mysqlInit); + return false; + } +} + +QueryResult* DatabaseMysql::Query(const char *sql) +{ + if (!mMysql) + return 0; + + MYSQL_RES *result = 0; + uint64 rowCount = 0; + uint32 fieldCount = 0; + + { + // guarded block for thread-safe mySQL request + ZThread::Guard<ZThread::FastMutex> query_connection_guard(mMutex); + #ifdef MANGOS_DEBUG + uint32 _s = getMSTime(); + #endif + if(mysql_query(mMysql, sql)) + { + sLog.outErrorDb( "SQL: %s", sql ); + sLog.outErrorDb("query ERROR: %s", mysql_error(mMysql)); + return NULL; + } + else + { + #ifdef MANGOS_DEBUG + sLog.outDebug("[%u ms] SQL: %s", getMSTimeDiff(_s,getMSTime()), sql ); + #endif + } + + result = mysql_store_result(mMysql); + + rowCount = mysql_affected_rows(mMysql); + fieldCount = mysql_field_count(mMysql); + // end guarded block + } + + if (!result ) + return NULL; + + if (!rowCount) + { + mysql_free_result(result); + return NULL; + } + + QueryResultMysql *queryResult = new QueryResultMysql(result, rowCount, fieldCount); + + queryResult->NextRow(); + + return queryResult; +} + +bool DatabaseMysql::Execute(const char *sql) +{ + if (!mMysql) + return false; + + // don't use queued execution if it has not been initialized + if (!m_threadBody) return DirectExecute(sql); + + tranThread = ZThread::ThreadImpl::current(); // owner of this transaction + TransactionQueues::iterator i = m_tranQueues.find(tranThread); + if (i != m_tranQueues.end() && i->second != NULL) + { // Statement for transaction + i->second->DelayExecute(sql); + } + else + { + // Simple sql statement + m_threadBody->Delay(new SqlStatement(sql)); + } + + return true; +} + +bool DatabaseMysql::DirectExecute(const char* sql) +{ + if (!mMysql) + return false; + + { + // guarded block for thread-safe mySQL request + ZThread::Guard<ZThread::FastMutex> query_connection_guard(mMutex); + #ifdef MANGOS_DEBUG + uint32 _s = getMSTime(); + #endif + if(mysql_query(mMysql, sql)) + { + sLog.outErrorDb("SQL: %s", sql); + sLog.outErrorDb("SQL ERROR: %s", mysql_error(mMysql)); + return false; + } + else + { + #ifdef MANGOS_DEBUG + sLog.outDebug("[%u ms] SQL: %s", getMSTimeDiff(_s,getMSTime()), sql ); + #endif + } + // end guarded block + } + + return true; +} + +bool DatabaseMysql::_TransactionCmd(const char *sql) +{ + if (mysql_query(mMysql, sql)) + { + sLog.outError("SQL: %s", sql); + sLog.outError("SQL ERROR: %s", mysql_error(mMysql)); + return false; + } + else + { + DEBUG_LOG("SQL: %s", sql); + } + return true; +} + +bool DatabaseMysql::BeginTransaction() +{ + if (!mMysql) + return false; + + // don't use queued execution if it has not been initialized + if (!m_threadBody) + { + if (tranThread==ZThread::ThreadImpl::current()) + return false; // huh? this thread already started transaction + mMutex.acquire(); + if (!_TransactionCmd("START TRANSACTION")) + { + mMutex.release(); // can't start transaction + return false; + } + return true; // transaction started + } + + tranThread = ZThread::ThreadImpl::current(); // owner of this transaction + TransactionQueues::iterator i = m_tranQueues.find(tranThread); + if (i != m_tranQueues.end() && i->second != NULL) + // If for thread exists queue and also contains transaction + // delete that transaction (not allow trans in trans) + delete i->second; + + m_tranQueues[tranThread] = new SqlTransaction(); + + return true; +} + +bool DatabaseMysql::CommitTransaction() +{ + if (!mMysql) + return false; + + // don't use queued execution if it has not been initialized + if (!m_threadBody) + { + if (tranThread!=ZThread::ThreadImpl::current()) + return false; + bool _res = _TransactionCmd("COMMIT"); + tranThread = NULL; + mMutex.release(); + return _res; + } + + tranThread = ZThread::ThreadImpl::current(); + TransactionQueues::iterator i = m_tranQueues.find(tranThread); + if (i != m_tranQueues.end() && i->second != NULL) + { + m_threadBody->Delay(i->second); + i->second = NULL; + return true; + } + else + return false; +} + +bool DatabaseMysql::RollbackTransaction() +{ + if (!mMysql) + return false; + + // don't use queued execution if it has not been initialized + if (!m_threadBody) + { + if (tranThread!=ZThread::ThreadImpl::current()) + return false; + bool _res = _TransactionCmd("ROLLBACK"); + tranThread = NULL; + mMutex.release(); + return _res; + } + + tranThread = ZThread::ThreadImpl::current(); + TransactionQueues::iterator i = m_tranQueues.find(tranThread); + if (i != m_tranQueues.end() && i->second != NULL) + { + delete i->second; + i->second = NULL; + } + return true; +} + +unsigned long DatabaseMysql::escape_string(char *to, const char *from, unsigned long length) +{ + if (!mMysql || !to || !from || !length) + return 0; + + return(mysql_real_escape_string(mMysql, to, from, length)); +} + +void DatabaseMysql::InitDelayThread() +{ + assert(!m_delayThread); + + //New delay thread for delay execute + m_delayThread = new ZThread::Thread(m_threadBody = new MySQLDelayThread(this)); +} + +void DatabaseMysql::HaltDelayThread() +{ + if (!m_threadBody || !m_delayThread) return; + + m_threadBody->Stop(); //Stop event + m_delayThread->wait(); //Wait for flush to DB + delete m_delayThread; //This also deletes m_threadBody + m_delayThread = NULL; + m_threadBody = NULL; +} +#endif diff --git a/src/shared/Database/DatabaseMysql.h b/src/shared/Database/DatabaseMysql.h new file mode 100644 index 00000000000..2608212d52a --- /dev/null +++ b/src/shared/Database/DatabaseMysql.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef DO_POSTGRESQL + +#ifndef _DATABASEMYSQL_H +#define _DATABASEMYSQL_H + +#include "Database.h" +#include "Policies/Singleton.h" +#include "zthread/FastMutex.h" + +#ifdef WIN32 +#define FD_SETSIZE 1024 +#include <winsock2.h> +#include <mysql/mysql.h> +#else +#include <mysql.h> +#endif + +class MANGOS_DLL_SPEC DatabaseMysql : public Database +{ + friend class MaNGOS::OperatorNew<DatabaseMysql>; + + public: + DatabaseMysql(); + ~DatabaseMysql(); + + //! Initializes Mysql and connects to a server. + /*! infoString should be formated like hostname;username;password;database. */ + bool Initialize(const char *infoString); + void InitDelayThread(); + void HaltDelayThread(); + QueryResult* Query(const char *sql); + bool Execute(const char *sql); + bool DirectExecute(const char* sql); + bool BeginTransaction(); + bool CommitTransaction(); + bool RollbackTransaction(); + + operator bool () const { return mMysql != NULL; } + + unsigned long escape_string(char *to, const char *from, unsigned long length); + using Database::escape_string; + + // must be call before first query in thread + void ThreadStart(); + // must be call before finish thread run + void ThreadEnd(); + private: + ZThread::FastMutex mMutex; + + ZThread::ThreadImpl* tranThread; + + MYSQL *mMysql; + + static size_t db_count; + + bool _TransactionCmd(const char *sql); +}; +#endif +#endif diff --git a/src/shared/Database/DatabasePostgre.cpp b/src/shared/Database/DatabasePostgre.cpp new file mode 100644 index 00000000000..637c2ac9522 --- /dev/null +++ b/src/shared/Database/DatabasePostgre.cpp @@ -0,0 +1,345 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef DO_POSTGRESQL + +#include "Util.h" +#include "Policies/SingletonImp.h" +#include "Platform/Define.h" +#include "../src/zthread/ThreadImpl.h" +#include "DatabaseEnv.h" +#include "Database/PGSQLDelayThread.h" +#include "Database/SqlOperations.h" +#include "Timer.h" + +void DatabasePostgre::ThreadStart() +{ +} + +void DatabasePostgre::ThreadEnd() +{ +} + +size_t DatabasePostgre::db_count = 0; + +DatabasePostgre::DatabasePostgre() : Database(), mPGconn(NULL) +{ + // before first connection + if( db_count++ == 0 ) + { + + if (!PQisthreadsafe()) + { + sLog.outError("FATAL ERROR: PostgreSQL libpq isn't thread-safe."); + exit(1); + } + } +} + +DatabasePostgre::~DatabasePostgre() +{ + + if (m_delayThread) + HaltDelayThread(); + + if( mPGconn ) + { + PQfinish(mPGconn); + mPGconn = NULL; + } +} + +bool DatabasePostgre::Initialize(const char *infoString) +{ + if(!Database::Initialize(infoString)) + return false; + + tranThread = NULL; + + InitDelayThread(); + + Tokens tokens = StrSplit(infoString, ";"); + + Tokens::iterator iter; + + std::string host, port_or_socket, user, password, database; + + iter = tokens.begin(); + + if(iter != tokens.end()) + host = *iter++; + if(iter != tokens.end()) + port_or_socket = *iter++; + if(iter != tokens.end()) + user = *iter++; + if(iter != tokens.end()) + password = *iter++; + if(iter != tokens.end()) + database = *iter++; + + mPGconn = PQsetdbLogin(host.c_str(), port_or_socket.c_str(), NULL, NULL, database.c_str(), user.c_str(), password.c_str()); + + /* check to see that the backend connection was successfully made */ + if (PQstatus(mPGconn) != CONNECTION_OK) + { + sLog.outError( "Could not connect to Postgre database at %s: %s", + host.c_str(), PQerrorMessage(mPGconn)); + PQfinish(mPGconn); + return false; + } + else + { + sLog.outDetail( "Connected to Postgre database at %s", + host.c_str()); + sLog.outString( "PostgreSQL server ver: %d",PQserverVersion(mPGconn)); + return true; + } + +} + +QueryResult* DatabasePostgre::Query(const char *sql) +{ + if (!mPGconn) + return 0; + + uint64 rowCount = 0; + uint32 fieldCount = 0; + + // guarded block for thread-safe request + ZThread::Guard<ZThread::FastMutex> query_connection_guard(mMutex); + #ifdef MANGOS_DEBUG + uint32 _s = getMSTime(); + #endif + // Send the query + PGresult * result = PQexec(mPGconn, sql); + if (!result ) + { + return NULL; + } + + if (PQresultStatus(result) != PGRES_TUPLES_OK) + { + sLog.outErrorDb( "SQL : %s", sql ); + sLog.outErrorDb( "SQL %s", PQerrorMessage(mPGconn)); + PQclear(result); + return NULL; + } + else + { + #ifdef MANGOS_DEBUG + sLog.outDebug("[%u ms] SQL: %s", getMSTime() - _s, sql ); + #endif + } + + rowCount = PQntuples(result); + fieldCount = PQnfields(result); + // end guarded block + + if (!rowCount) + { + PQclear(result); + return NULL; + } + + QueryResultPostgre * queryResult = new QueryResultPostgre(result, rowCount, fieldCount); + queryResult->NextRow(); + + return queryResult; +} + +bool DatabasePostgre::Execute(const char *sql) +{ + + if (!mPGconn) + return false; + + // don't use queued execution if it has not been initialized + if (!m_threadBody) return DirectExecute(sql); + + tranThread = ZThread::ThreadImpl::current(); // owner of this transaction + TransactionQueues::iterator i = m_tranQueues.find(tranThread); + if (i != m_tranQueues.end() && i->second != NULL) + { // Statement for transaction + i->second->DelayExecute(sql); + } + else + { + // Simple sql statement + m_threadBody->Delay(new SqlStatement(sql)); + } + + return true; +} + +bool DatabasePostgre::DirectExecute(const char* sql) +{ + if (!mPGconn) + return false; + { + // guarded block for thread-safe request + ZThread::Guard<ZThread::FastMutex> query_connection_guard(mMutex); + #ifdef MANGOS_DEBUG + uint32 _s = getMSTime(); + #endif + PGresult *res = PQexec(mPGconn, sql); + if (PQresultStatus(res) != PGRES_COMMAND_OK) + { + sLog.outErrorDb( "SQL: %s", sql ); + sLog.outErrorDb( "SQL %s", PQerrorMessage(mPGconn) ); + return false; + } + else + { + #ifdef MANGOS_DEBUG + sLog.outDebug("[%u ms] SQL: %s", getMSTime() - _s, sql ); + #endif + } + PQclear(res); + + // end guarded block + } + return true; +} + +bool DatabasePostgre::_TransactionCmd(const char *sql) +{ + if (!mPGconn) + return false; + + PGresult *res = PQexec(mPGconn, sql); + if (PQresultStatus(res) != PGRES_COMMAND_OK) + { + sLog.outError("SQL: %s", sql); + sLog.outError("SQL ERROR: %s", PQerrorMessage(mPGconn)); + return false; + } + else + { + DEBUG_LOG("SQL: %s", sql); + } + return true; +} + +bool DatabasePostgre::BeginTransaction() +{ + if (!mPGconn) + return false; + // don't use queued execution if it has not been initialized + if (!m_threadBody) + { + if (tranThread==ZThread::ThreadImpl::current()) + return false; // huh? this thread already started transaction + mMutex.acquire(); + if (!_TransactionCmd("START TRANSACTION")) + { + mMutex.release(); // can't start transaction + return false; + } + return true; + } + // transaction started + tranThread = ZThread::ThreadImpl::current(); // owner of this transaction + TransactionQueues::iterator i = m_tranQueues.find(tranThread); + if (i != m_tranQueues.end() && i->second != NULL) + // If for thread exists queue and also contains transaction + // delete that transaction (not allow trans in trans) + delete i->second; + + m_tranQueues[tranThread] = new SqlTransaction(); + + return true; +} + +bool DatabasePostgre::CommitTransaction() +{ + if (!mPGconn) + return false; + + // don't use queued execution if it has not been initialized + if (!m_threadBody) + { + if (tranThread!=ZThread::ThreadImpl::current()) + return false; + bool _res = _TransactionCmd("COMMIT"); + tranThread = NULL; + mMutex.release(); + return _res; + } + tranThread = ZThread::ThreadImpl::current(); + TransactionQueues::iterator i = m_tranQueues.find(tranThread); + if (i != m_tranQueues.end() && i->second != NULL) + { + m_threadBody->Delay(i->second); + i->second = NULL; + return true; + } + else + return false; +} + +bool DatabasePostgre::RollbackTransaction() +{ + if (!mPGconn) + return false; + // don't use queued execution if it has not been initialized + if (!m_threadBody) + { + if (tranThread!=ZThread::ThreadImpl::current()) + return false; + bool _res = _TransactionCmd("ROLLBACK"); + tranThread = NULL; + mMutex.release(); + return _res; + } + tranThread = ZThread::ThreadImpl::current(); + TransactionQueues::iterator i = m_tranQueues.find(tranThread); + if (i != m_tranQueues.end() && i->second != NULL) + { + delete i->second; + i->second = NULL; + } + return true; +} + +unsigned long DatabasePostgre::escape_string(char *to, const char *from, unsigned long length) +{ + if (!mPGconn || !to || !from || !length) + return 0; + + return PQescapeString(to, from, length); +} + +void DatabasePostgre::InitDelayThread() +{ + assert(!m_delayThread); + + //New delay thread for delay execute + m_delayThread = new ZThread::Thread(m_threadBody = new PGSQLDelayThread(this)); +} + +void DatabasePostgre::HaltDelayThread() +{ + if (!m_threadBody || !m_delayThread) return; + + m_threadBody->Stop(); //Stop event + m_delayThread->wait(); //Wait for flush to DB + delete m_delayThread; //This also deletes m_threadBody + m_delayThread = NULL; + m_threadBody = NULL; +} +#endif diff --git a/src/shared/Database/DatabasePostgre.h b/src/shared/Database/DatabasePostgre.h new file mode 100644 index 00000000000..b1929c46360 --- /dev/null +++ b/src/shared/Database/DatabasePostgre.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _DatabasePostgre_H +#define _DatabasePostgre_H + +#include "Policies/Singleton.h" +#include "zthread/FastMutex.h" +#include <stdarg.h> + +#ifdef WIN32 +#define FD_SETSIZE 1024 +#include <winsock2.h> +#include <postgre/libpq-fe.h> +#else +#include <libpq-fe.h> +#endif + +class DatabasePostgre : public Database +{ + friend class MaNGOS::OperatorNew<DatabasePostgre>; + + public: + DatabasePostgre(); + ~DatabasePostgre(); + + //! Initializes Postgres and connects to a server. + /*! infoString should be formated like hostname;username;password;database. */ + bool Initialize(const char *infoString); + void InitDelayThread(); + void HaltDelayThread(); + QueryResult* Query(const char *sql); + bool Execute(const char *sql); + bool DirectExecute(const char* sql); + bool BeginTransaction(); + bool CommitTransaction(); + bool RollbackTransaction(); + + operator bool () const { return mPGconn != NULL; } + + unsigned long escape_string(char *to, const char *from, unsigned long length); + using Database::escape_string; + + // must be call before first query in thread + void ThreadStart(); + // must be call before finish thread run + void ThreadEnd(); + private: + ZThread::FastMutex mMutex; + ZThread::FastMutex tranMutex; + + ZThread::ThreadImpl* tranThread; + + PGconn *mPGconn; + + static size_t db_count; + + bool _TransactionCmd(const char *sql); +}; +#endif diff --git a/src/shared/Database/DatabaseSqlite.cpp b/src/shared/Database/DatabaseSqlite.cpp new file mode 100644 index 00000000000..75307f9d430 --- /dev/null +++ b/src/shared/Database/DatabaseSqlite.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef DO_POSTGRESQL + +#include "DatabaseEnv.h" + +DatabaseSqlite::DatabaseSqlite() : Database(), mSqlite(0) +{ +} + +DatabaseSqlite::~DatabaseSqlite() +{ + if (mSqlite) + sqlite_close(mSqlite); +} + +bool DatabaseSqlite::Initialize(const char *infoString) +{ + if(!Database::Initialize(infoString)) + return false; + + char *errmsg; + + mSqlite = sqlite_open(infoString, 0, &errmsg); + + if (!mSqlite) + { + + if (errmsg) + sqlite_freemem(errmsg); + return false; + } + + return true; +} + +QueryResult* DatabaseSqlite::Query(const char *sql) +{ + char *errmsg; + + if (!mSqlite) + return 0; + + char **tableData; + int rowCount; + int fieldCount; + + sqlite_get_table(mSqlite, sql, &tableData, &rowCount, &fieldCount, &errmsg); + + if (!rowCount) + return 0; + + if (!tableData) + { + + if (errmsg) + sqlite_freemem(errmsg); + return 0; + } + + QueryResultSqlite *queryResult = new QueryResultSqlite(tableData, rowCount, fieldCount); + if(!queryResult) + { + + return 0; + } + + queryResult->NextRow(); + + return queryResult; +} + +bool DatabaseSqlite::Execute(const char *sql) +{ + char *errmsg; + + if (!mSqlite) + return false; + + if(sqlite_exec(mSqlite, sql, NULL, NULL, &errmsg) != SQLITE_OK) + return false; + + return true; +} +#endif diff --git a/src/shared/Database/DatabaseSqlite.h b/src/shared/Database/DatabaseSqlite.h new file mode 100644 index 00000000000..37190a6b562 --- /dev/null +++ b/src/shared/Database/DatabaseSqlite.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef DO_POSTGRESQL + +#ifndef _DATABASESQLITE_H +#define _DATABASESQLITE_H + +#include <sqlite/sqlite.h> + +class DatabaseSqlite : public Database +{ + public: + DatabaseSqlite(); + ~DatabaseSqlite(); + + bool Initialize(const char *infoString); + + QueryResult* Query(const char *sql); + bool Execute(const char *sql); + + operator bool () const { return mSqlite != NULL; } + + private: + sqlite *mSqlite; +}; +#endif +#endif diff --git a/src/shared/Database/Field.cpp b/src/shared/Database/Field.cpp new file mode 100644 index 00000000000..5c693403c06 --- /dev/null +++ b/src/shared/Database/Field.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "DatabaseEnv.h" + +Field::Field() : +mValue(NULL), mType(DB_TYPE_UNKNOWN) +{ +} + +Field::Field(Field &f) +{ + const char *value; + + value = f.GetString(); + + if (value && (mValue = new char[strlen(value) + 1])) + strcpy(mValue, value); + else + mValue = NULL; + + mType = f.GetType(); +} + +Field::Field(const char *value, enum Field::DataTypes type) : +mType(type) +{ + if (value && (mValue = new char[strlen(value) + 1])) + strcpy(mValue, value); + else + mValue = NULL; +} + +Field::~Field() +{ + if(mValue) delete [] mValue; +} + +void Field::SetValue(const char *value) +{ + if(mValue) delete [] mValue; + + if (value) + { + mValue = new char[strlen(value) + 1]; + strcpy(mValue, value); + } + else + mValue = NULL; +} diff --git a/src/shared/Database/Field.h b/src/shared/Database/Field.h new file mode 100644 index 00000000000..f829ec18424 --- /dev/null +++ b/src/shared/Database/Field.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined(FIELD_H) +#define FIELD_H + +class Field +{ + public: + + enum DataTypes + { + DB_TYPE_UNKNOWN = 0x00, + DB_TYPE_STRING = 0x01, + DB_TYPE_INTEGER = 0x02, + DB_TYPE_FLOAT = 0x03, + DB_TYPE_BOOL = 0x04 + }; + + Field(); + Field(Field &f); + Field(const char *value, enum DataTypes type); + + ~Field(); + + enum DataTypes GetType() const { return mType; } + + const char *GetString() const { return mValue; } + std::string GetCppString() const + { + return mValue ? mValue : ""; // std::string s = 0 have undefine result in C++ + } + float GetFloat() const { return mValue ? static_cast<float>(atof(mValue)) : 0.0f; } + bool GetBool() const { return mValue ? atoi(mValue) > 0 : false; } + int32 GetInt32() const { return mValue ? static_cast<int32>(atol(mValue)) : int32(0); } + uint8 GetUInt8() const { return mValue ? static_cast<uint8>(atol(mValue)) : uint8(0); } + uint16 GetUInt16() const { return mValue ? static_cast<uint16>(atol(mValue)) : uint16(0); } + int16 GetInt16() const { return mValue ? static_cast<int16>(atol(mValue)) : int16(0); } + uint32 GetUInt32() const { return mValue ? static_cast<uint32>(atol(mValue)) : uint32(0); } + uint64 GetUInt64() const + { + if(mValue) + { + uint64 value; + sscanf(mValue,I64FMTD,&value); + return value; + } + else + return 0; + } + + void SetType(enum DataTypes type) { mType = type; } + + void SetValue(const char *value); + + private: + char *mValue; + enum DataTypes mType; +}; +#endif diff --git a/src/shared/Database/Makefile.am b/src/shared/Database/Makefile.am new file mode 100644 index 00000000000..2d3a9fb5d5c --- /dev/null +++ b/src/shared/Database/Makefile.am @@ -0,0 +1,62 @@ +# Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +## Process this file with automake to produce Makefile.in + +## Sub-directories to parse + +## CPP flags for includes, defines, etc. +AM_CPPFLAGS = $(MYSQL_INCLUDES) $(POSTGRE_INCLUDES) -I$(top_builddir)/src/shared -I$(srcdir) -I$(srcdir)/../../../dep/include -I$(srcdir)/../../framework -I$(srcdir)/../../shared -I$(srcdir)/../../../dep/include/g3dlite + +## Build MaNGOS shared library and its parts as convenience library. +# All libraries will be convenience libraries. Might be changed to shared +# later. +noinst_LIBRARIES = libmangosdatabase.a + +libmangosdatabase_a_SOURCES = \ + DBCStores.cpp \ + DBCStores.h \ + DBCStructure.h \ + DBCfmt.cpp \ + Database.cpp \ + Database.h \ + DatabaseEnv.h \ + DatabaseImpl.h \ + DatabaseMysql.cpp \ + DatabasePostgre.cpp \ + DatabaseMysql.h \ + DatabasePostgre.h \ + DatabaseSqlite.cpp \ + DatabaseSqlite.h \ + Field.cpp \ + Field.h \ + MySQLDelayThread.h \ + PGSQLDelayThread.h \ + QueryResult.h \ + QueryResultMysql.cpp \ + QueryResultMysql.h \ + QueryResultPostgre.cpp \ + QueryResultPostgre.h \ + QueryResultSqlite.cpp \ + QueryResultSqlite.h \ + SQLStorage.cpp \ + SQLStorage.h \ + SqlDelayThread.cpp \ + SqlDelayThread.h \ + SqlOperations.cpp \ + SqlOperations.h \ + dbcfile.cpp \ + dbcfile.h diff --git a/src/shared/Database/MySQLDelayThread.h b/src/shared/Database/MySQLDelayThread.h new file mode 100644 index 00000000000..dcc979d6d53 --- /dev/null +++ b/src/shared/Database/MySQLDelayThread.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __MYSQLDELAYTHREAD_H +#define __MYSQLDELAYTHREAD_H + +#include "Database/SqlDelayThread.h" + +class MySQLDelayThread : public SqlDelayThread +{ + public: + MySQLDelayThread(Database* db) : SqlDelayThread(db) {} + void Stop() { SqlDelayThread::Stop(); } +}; +#endif //__MYSQLDELAYTHREAD_H diff --git a/src/shared/Database/PGSQLDelayThread.h b/src/shared/Database/PGSQLDelayThread.h new file mode 100644 index 00000000000..03907026899 --- /dev/null +++ b/src/shared/Database/PGSQLDelayThread.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PGSQLDELAYTHREAD_H +#define __PGSQLDELAYTHREAD_H + +#include "Database/SqlDelayThread.h" + +class PGSQLDelayThread : public SqlDelayThread +{ + public: + PGSQLDelayThread(Database* db) : SqlDelayThread(db) {} + void Stop() { SqlDelayThread::Stop(); } +}; +#endif //__PGSQLDELAYTHREAD_H diff --git a/src/shared/Database/QueryResult.h b/src/shared/Database/QueryResult.h new file mode 100644 index 00000000000..50ee98a9a22 --- /dev/null +++ b/src/shared/Database/QueryResult.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined(QUERYRESULT_H) +#define QUERYRESULT_H + +class MANGOS_DLL_SPEC QueryResult +{ + public: + QueryResult(uint64 rowCount, uint32 fieldCount) + : mFieldCount(fieldCount), mRowCount(rowCount) {} + + virtual ~QueryResult() {} + + virtual bool NextRow() = 0; + + typedef std::map<uint32, std::string> FieldNames; + + uint32 GetField_idx(const std::string &name) const + { + for(FieldNames::const_iterator iter = GetFiedNames().begin(); iter != GetFiedNames().end(); ++iter) + { + if(iter->second == name) + return iter->first; + } + assert(false && "unknown field name"); + return uint32(-1); + } + + Field *Fetch() const { return mCurrentRow; } + + const Field & operator [] (int index) const { return mCurrentRow[index]; } + + const Field & operator [] (const std::string &name) const + { + return mCurrentRow[GetField_idx(name)]; + } + + uint32 GetFieldCount() const { return mFieldCount; } + uint64 GetRowCount() const { return mRowCount; } + FieldNames const& GetFiedNames() const {return mFieldNames; } + + protected: + Field *mCurrentRow; + uint32 mFieldCount; + uint64 mRowCount; + FieldNames mFieldNames; +}; +#endif diff --git a/src/shared/Database/QueryResultMysql.cpp b/src/shared/Database/QueryResultMysql.cpp new file mode 100644 index 00000000000..b24e51c2352 --- /dev/null +++ b/src/shared/Database/QueryResultMysql.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef DO_POSTGRESQL + +#include "DatabaseEnv.h" + +QueryResultMysql::QueryResultMysql(MYSQL_RES *result, uint64 rowCount, uint32 fieldCount) : +QueryResult(rowCount, fieldCount), mResult(result) +{ + + mCurrentRow = new Field[mFieldCount]; + ASSERT(mCurrentRow); + + MYSQL_FIELD *fields = mysql_fetch_fields(mResult); + + for (uint32 i = 0; i < mFieldCount; i++) + { + mFieldNames[i] = fields[i].name; + mCurrentRow[i].SetType(ConvertNativeType(fields[i].type)); + } +} + +QueryResultMysql::~QueryResultMysql() +{ + EndQuery(); +} + +bool QueryResultMysql::NextRow() +{ + MYSQL_ROW row; + + if (!mResult) + return false; + + row = mysql_fetch_row(mResult); + if (!row) + { + EndQuery(); + return false; + } + + for (uint32 i = 0; i < mFieldCount; i++) + mCurrentRow[i].SetValue(row[i]); + + return true; +} + +void QueryResultMysql::EndQuery() +{ + if (mCurrentRow) + { + delete [] mCurrentRow; + mCurrentRow = 0; + } + + if (mResult) + { + mysql_free_result(mResult); + mResult = 0; + } +} + +enum Field::DataTypes QueryResultMysql::ConvertNativeType(enum_field_types mysqlType) const +{ + switch (mysqlType) + { + case FIELD_TYPE_TIMESTAMP: + case FIELD_TYPE_DATE: + case FIELD_TYPE_TIME: + case FIELD_TYPE_DATETIME: + case FIELD_TYPE_YEAR: + case FIELD_TYPE_STRING: + case FIELD_TYPE_VAR_STRING: + case FIELD_TYPE_BLOB: + case FIELD_TYPE_SET: + case FIELD_TYPE_NULL: + return Field::DB_TYPE_STRING; + case FIELD_TYPE_TINY: + + case FIELD_TYPE_SHORT: + case FIELD_TYPE_LONG: + case FIELD_TYPE_INT24: + case FIELD_TYPE_LONGLONG: + case FIELD_TYPE_ENUM: + return Field::DB_TYPE_INTEGER; + case FIELD_TYPE_DECIMAL: + case FIELD_TYPE_FLOAT: + case FIELD_TYPE_DOUBLE: + return Field::DB_TYPE_FLOAT; + default: + return Field::DB_TYPE_UNKNOWN; + } +} +#endif diff --git a/src/shared/Database/QueryResultMysql.h b/src/shared/Database/QueryResultMysql.h new file mode 100644 index 00000000000..e62a1a4c120 --- /dev/null +++ b/src/shared/Database/QueryResultMysql.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef DO_POSTGRESQL + +#if !defined(QUERYRESULTMYSQL_H) +#define QUERYRESULTMYSQL_H + +#ifdef WIN32 +#define FD_SETSIZE 1024 +#include <winsock2.h> +#include <mysql/mysql.h> +#else +#include <mysql.h> +#endif + +class QueryResultMysql : public QueryResult +{ + public: + QueryResultMysql(MYSQL_RES *result, uint64 rowCount, uint32 fieldCount); + + ~QueryResultMysql(); + + bool NextRow(); + + private: + enum Field::DataTypes ConvertNativeType(enum_field_types mysqlType) const; + void EndQuery(); + + MYSQL_RES *mResult; +}; +#endif +#endif diff --git a/src/shared/Database/QueryResultPostgre.cpp b/src/shared/Database/QueryResultPostgre.cpp new file mode 100644 index 00000000000..2cb6447b170 --- /dev/null +++ b/src/shared/Database/QueryResultPostgre.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef DO_POSTGRESQL + +#include "DatabaseEnv.h" + +QueryResultPostgre::QueryResultPostgre(PGresult *result, uint64 rowCount, uint32 fieldCount) : +QueryResult(rowCount, fieldCount), mResult(result), mTableIndex(0) +{ + + mCurrentRow = new Field[mFieldCount]; + ASSERT(mCurrentRow); + + for (uint32 i = 0; i < mFieldCount; i++) + { + mFieldNames[i] = PQfname(result, i); + mCurrentRow[i].SetType(ConvertNativeType(PQftype( result, i ))); + } +} + +QueryResultPostgre::~QueryResultPostgre() +{ + EndQuery(); +} + +bool QueryResultPostgre::NextRow() +{ + if (!mResult) + return false; + + if (mTableIndex >= mRowCount) + { + EndQuery(); + return false; + } + + char* pPQgetvalue; + for (int j = 0; j < mFieldCount; j++) + { + pPQgetvalue = PQgetvalue(mResult, mTableIndex, j); + if(pPQgetvalue && !(*pPQgetvalue)) + pPQgetvalue = NULL; + + mCurrentRow[j].SetValue(pPQgetvalue); + } + ++mTableIndex; + + return true; +} + +void QueryResultPostgre::EndQuery() +{ + if (mCurrentRow) + { + delete [] mCurrentRow; + mCurrentRow = 0; + } + + if (mResult) + { + PQclear(mResult); + mResult = 0; + } +} + +// see types in #include <postgre/pg_type.h> +enum Field::DataTypes QueryResultPostgre::ConvertNativeType(Oid pOid ) const +{ + switch (pOid) + { + case BPCHAROID: + case CIDOID: + case CIDROID: + case CIRCLEOID: + case INETOID: + case NAMEOID: + case TEXTOID: + case VARCHAROID: + return Field::DB_TYPE_STRING; + case CASHOID: + case FLOAT4OID: + case FLOAT8OID: + case NUMERICOID: + return Field::DB_TYPE_FLOAT; + case DATEOID: // Date + case RELTIMEOID: // Date + case TIMEOID: // Time + case TIMETZOID: // Time + case ABSTIMEOID: // DateTime + case INTERVALOID: // DateTime + case TIMESTAMPOID: // DateTime + case TIMESTAMPTZOID: // DateTime + case INT2OID: // Int + case INT2VECTOROID: // Int + case INT4OID: // Int + case OIDOID: // Int + case CHAROID: // UInt + case INT8OID: // LongLong + return Field::DB_TYPE_INTEGER; + case BOOLOID: + return Field::DB_TYPE_BOOL; // Bool +/* + case BOXOID: Rect; + case LINEOID: Rect; + case VARBITOID: BitArray; + case BYTEAOID: ByteArray; +*/ + case LSEGOID: + case OIDVECTOROID: + case PATHOID: + case POINTOID: + case POLYGONOID: + case REGPROCOID: + case TIDOID: + case TINTERVALOID: + case UNKNOWNOID: + case XIDOID: + default: + return Field::DB_TYPE_UNKNOWN; + } + return Field::DB_TYPE_UNKNOWN; +} +#endif diff --git a/src/shared/Database/QueryResultPostgre.h b/src/shared/Database/QueryResultPostgre.h new file mode 100644 index 00000000000..f6726d20123 --- /dev/null +++ b/src/shared/Database/QueryResultPostgre.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined(QUERYRESULTPOSTGRE_H) +#define QUERYRESULTPOSTGRE_H + +#ifdef WIN32 +#define FD_SETSIZE 1024 +#include <winsock2.h> +#include <postgre/libpq-fe.h> +#include <postgre/pg_type.h> +#else +#include <libpq-fe.h> +//#include <pg_type.h> +#endif + +class QueryResultPostgre : public QueryResult +{ + public: + QueryResultPostgre(PGresult *result, uint64 rowCount, uint32 fieldCount); + + ~QueryResultPostgre(); + + bool NextRow(); + + private: + enum Field::DataTypes ConvertNativeType(Oid pOid) const; + void EndQuery(); + + PGresult *mResult; + uint32 mTableIndex; +}; +#endif diff --git a/src/shared/Database/QueryResultSqlite.cpp b/src/shared/Database/QueryResultSqlite.cpp new file mode 100644 index 00000000000..3eca2f08d46 --- /dev/null +++ b/src/shared/Database/QueryResultSqlite.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef DO_POSTGRESQL + +#include "DatabaseEnv.h" + +QueryResultSqlite::QueryResultSqlite(char **tableData, uint32 rowCount, uint32 fieldCount) : +QueryResult(rowCount, fieldCount), mTableData(tableData), mTableIndex(0) +{ + mCurrentRow = new Field[mFieldCount]; + + for (uint32 i = 0; i < mFieldCount; i++) + { + mFieldNames[i] = mTableData[i]; + mCurrentRow[i].SetType(Field::DB_TYPE_UNKNOWN); + } +} + +QueryResultSqlite::~QueryResultSqlite() +{ + EndQuery(); +} + +bool QueryResultSqlite::NextRow() +{ + int startIndex; + uint32 i; + + if (!mTableData) + return false; + + if (mTableIndex >= mRowCount) + { + EndQuery(); + return false; + } + + startIndex = (mTableIndex + 1) * mFieldCount; + for (i = 0; i < mFieldCount; i++) + { + mCurrentRow[i].SetValue(mTableData[startIndex + i]); + } + + ++mTableIndex; + return true; +} + +void QueryResultSqlite::EndQuery() +{ + if (mCurrentRow) + { + delete [] mCurrentRow; + mCurrentRow = NULL; + } + if (mTableData) + { + sqlite_free_table(mTableData); + mTableData = NULL; + } +} + +enum Field::DataTypes QueryResultSqlite::ConvertNativeType(const char* sqliteType) const +{ + if (sqliteType) + { + switch (*sqliteType) + { + case 'S': + return Field::DB_TYPE_STRING; + case 'I': + return Field::DB_TYPE_INTEGER; + case 'F': + return Field::DB_TYPE_FLOAT; + default: + return Field::DB_TYPE_UNKNOWN; + }; + } + return Field::DB_TYPE_UNKNOWN; +} +#endif diff --git a/src/shared/Database/QueryResultSqlite.h b/src/shared/Database/QueryResultSqlite.h new file mode 100644 index 00000000000..c72e5727999 --- /dev/null +++ b/src/shared/Database/QueryResultSqlite.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef DO_POSTGRESQL + +#if !defined(QUERYRESULTSQLITE_H) +#define QUERYRESULTSQLITE_H + +#include <sqlite/sqlite.h> + +class QueryResultSqlite : public QueryResult +{ + public: + QueryResultSqlite(char **tableData, uint32 rowCount, uint32 fieldCount); + + ~QueryResultSqlite(); + + bool NextRow(); + + private: + enum Field::DataTypes ConvertNativeType(const char* sqliteType) const; + void EndQuery(); + + char **mTableData; + uint32 mTableIndex; +}; +#endif +#endif diff --git a/src/shared/Database/SQLStorage.cpp b/src/shared/Database/SQLStorage.cpp new file mode 100644 index 00000000000..6041eaf282e --- /dev/null +++ b/src/shared/Database/SQLStorage.cpp @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "SQLStorage.h" +#include "ProgressBar.h" +#include "Log.h" +#include "dbcfile.h" + +#ifdef DO_POSTGRESQL +extern DatabasePostgre WorldDatabase; +#else +extern DatabaseMysql WorldDatabase; +#endif + +const char CreatureInfofmt[]="iiiiiisssiiiiiiiiiiffiffiiiiiiiiiiiffiiiiiiiiiiiiiiiiiiisiilliiis"; +const char CreatureDataAddonInfofmt[]="iiiiiiis"; +const char CreatureModelfmt[]="iffbi"; +const char CreatureInfoAddonInfofmt[]="iiiiiiis"; +const char EquipmentInfofmt[]="iiiiiiiiii"; +const char GameObjectInfofmt[]="iiissiifiiiiiiiiiiiiiiiiiiiiiiiis"; +const char ItemPrototypefmt[]="iiiisiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiffiffiffiffiffiiiiiiiiiifiiifiiiiiifiiiiiifiiiiiifiiiiiifiiiisiiiiiiiiiiiiiiiiiiiiiiiiifsiiiii"; +const char PageTextfmt[]="isi"; +const char SpellThreatfmt[]="ii"; +const char InstanceTemplatefmt[]="iiiiiiffffs"; + +SQLStorage sCreatureStorage(CreatureInfofmt,"entry","creature_template"); +SQLStorage sCreatureDataAddonStorage(CreatureDataAddonInfofmt,"guid","creature_addon"); +SQLStorage sCreatureModelStorage(CreatureModelfmt,"modelid","creature_model_info"); +SQLStorage sCreatureInfoAddonStorage(CreatureInfoAddonInfofmt,"entry","creature_template_addon"); +SQLStorage sEquipmentStorage(EquipmentInfofmt,"entry","creature_equip_template"); +SQLStorage sGOStorage(GameObjectInfofmt,"entry","gameobject_template"); +SQLStorage sItemStorage(ItemPrototypefmt,"entry","item_template"); +SQLStorage sPageTextStore(PageTextfmt,"entry","page_text"); +SQLStorage sSpellThreatStore(SpellThreatfmt,"entry","spell_threat"); +SQLStorage sInstanceTemplate(InstanceTemplatefmt,"map","instance_template"); + +void SQLStorage::Free () +{ + uint32 offset=0; + for(uint32 x=0;x<iNumFields;x++) + if (format[x]==FT_STRING) + { + for(uint32 y=0;y<MaxEntry;y++) + if(pIndex[y]) + delete [] *(char**)((char*)(pIndex[y])+offset); + + offset+=sizeof(char*); + } + else if (format[x]==FT_LOGIC) + offset+=sizeof(bool); + else if (format[x]==FT_BYTE) + offset+=sizeof(char); + else + offset+=4; + + delete [] pIndex; + delete [] data; +} + +void SQLStorage::Load () +{ + uint32 maxi; + Field *fields; + QueryResult *result = WorldDatabase.PQuery("SELECT MAX(%s) FROM %s",entry_field,table); + if(!result) + { + sLog.outError("Error loading %s table (not exist?)\n",table); + exit(1); // Stop server at loading non exited table or not accessable table + } + + maxi= (*result)[0].GetUInt32()+1; + delete result; + + result = WorldDatabase.PQuery("SELECT COUNT(*) FROM %s",table); + if(result) + { + fields = result->Fetch(); + RecordCount=fields[0].GetUInt32(); + delete result; + } + else + RecordCount = 0; + + result = WorldDatabase.PQuery("SELECT * FROM %s",table); + + if(!result) + { + sLog.outError("%s table is empty!\n",table); + RecordCount = 0; + return; + } + + uint32 recordsize=0; + uint32 offset=0; + + if(iNumFields!=result->GetFieldCount()) + { + RecordCount = 0; + sLog.outError("Error in %s table, probably sql file format was updated (there should be %d fields in sql).\n",table,iNumFields); + delete result; + exit(1); // Stop server at loading broken or non-compatiable table. + } + + //get struct size + uint32 sc=0; + uint32 bo=0; + uint32 bb=0; + for(uint32 x=0;x<iNumFields;x++) + if(format[x]==FT_STRING) + ++sc; + else if (format[x]==FT_LOGIC) + ++bo; + else if (format[x]==FT_BYTE) + ++bb; + recordsize=(iNumFields-sc-bo-bb)*4+sc*sizeof(char*)+bo*sizeof(bool)+bb*sizeof(char); + + char** newIndex=new char*[maxi]; + memset(newIndex,0,maxi*sizeof(char*)); + + char * _data= new char[RecordCount *recordsize]; + uint32 count=0; + barGoLink bar( RecordCount ); + do + { + fields = result->Fetch(); + bar.step(); + char *p=(char*)&_data[recordsize*count]; + newIndex[fields[0].GetUInt32()]=p; + + offset=0; + for(uint32 x=0;x<iNumFields;x++) + switch(format[x]) + { + case FT_LOGIC: + *((bool*)(&p[offset]))=(fields[x].GetUInt32()>0); + offset+=sizeof(bool); + break; + case FT_BYTE: + *((char*)(&p[offset]))=(fields[x].GetUInt8()); + offset+=sizeof(char); + break; + case FT_INT: + *((uint32*)(&p[offset]))=fields[x].GetUInt32(); + offset+=sizeof(uint32); + break; + case FT_FLOAT: + *((float*)(&p[offset]))=fields[x].GetFloat(); + offset+=sizeof(float); + break; + case FT_STRING: + char const* tmp = fields[x].GetString(); + char* st; + if(!tmp) + { + st=new char[1]; + *st=0; + } + else + { + uint32 l=strlen(tmp)+1; + st=new char[l]; + memcpy(st,tmp,l); + } + *((char**)(&p[offset]))=st; + offset+=sizeof(char*); + break; + } + ++count; + }while( result->NextRow() ); + + delete result; + + pIndex =newIndex; + MaxEntry=maxi; + data=_data; +} diff --git a/src/shared/Database/SQLStorage.h b/src/shared/Database/SQLStorage.h new file mode 100644 index 00000000000..4ddd44f593e --- /dev/null +++ b/src/shared/Database/SQLStorage.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef SQLSTORAGE_H +#define SQLSTORAGE_H + +#include "Common.h" +#include "Database/DatabaseEnv.h" + +class SQLStorage +{ + public: + + SQLStorage(const char*fmt,const char * _entry_field,const char * sqlname) + { + format=fmt; + entry_field = _entry_field; + table=sqlname; + data=NULL; + pIndex=NULL; + iNumFields =strlen(fmt); + MaxEntry = 0; + } + ~SQLStorage() + { + Free(); + } + + template<class T> + T const* LookupEntry(uint32 id) const + { + if( id == 0 ) + return NULL; + if(id >= MaxEntry) + return NULL; + return reinterpret_cast<T const*>(pIndex[id]); + } + + uint32 RecordCount; + uint32 MaxEntry; + uint32 iNumFields; + void Load(); + void Free(); + private: + char** pIndex; + + char *data; + const char *format; + const char *table; + const char *entry_field; + //bool HasString; +}; +#endif diff --git a/src/shared/Database/SqlDelayThread.cpp b/src/shared/Database/SqlDelayThread.cpp new file mode 100644 index 00000000000..257a3d686e8 --- /dev/null +++ b/src/shared/Database/SqlDelayThread.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Database/SqlDelayThread.h" +#include "Database/SqlOperations.h" +#include "DatabaseEnv.h" + +SqlDelayThread::SqlDelayThread(Database* db) : m_dbEngine(db), m_running(true) +{ +} + +void SqlDelayThread::run() +{ + SqlOperation* s; + #ifndef DO_POSTGRESQL + mysql_thread_init(); + #endif + + while (m_running) + { + // if the running state gets turned off while sleeping + // empty the queue before exiting + ZThread::Thread::sleep(10); + while (!m_sqlQueue.empty()) + { + s = m_sqlQueue.next(); + s->Execute(m_dbEngine); + delete s; + } + } + + #ifndef DO_POSTGRESQL + mysql_thread_end(); + #endif +} + +void SqlDelayThread::Stop() +{ + m_running = false; +} diff --git a/src/shared/Database/SqlDelayThread.h b/src/shared/Database/SqlDelayThread.h new file mode 100644 index 00000000000..dd729660dba --- /dev/null +++ b/src/shared/Database/SqlDelayThread.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __SQLDELAYTHREAD_H +#define __SQLDELAYTHREAD_H + +#include "zthread/Thread.h" +#include "zthread/Runnable.h" +#include "zthread/FastMutex.h" +#include "zthread/LockedQueue.h" + +class Database; +class SqlOperation; + +class SqlDelayThread : public ZThread::Runnable +{ + typedef ZThread::LockedQueue<SqlOperation*, ZThread::FastMutex> SqlQueue; + private: + SqlQueue m_sqlQueue; ///< Queue of SQL statements + Database* m_dbEngine; ///< Pointer to used Database engine + bool m_running; + + SqlDelayThread(); + public: + SqlDelayThread(Database* db); + + ///< Put sql statement to delay queue + inline void Delay(SqlOperation* sql) { m_sqlQueue.add(sql); } + + virtual void Stop(); ///< Stop event + virtual void run(); ///< Main Thread loop +}; +#endif //__SQLDELAYTHREAD_H diff --git a/src/shared/Database/SqlOperations.cpp b/src/shared/Database/SqlOperations.cpp new file mode 100644 index 00000000000..38febadbc5c --- /dev/null +++ b/src/shared/Database/SqlOperations.cpp @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "SqlOperations.h" +#include "SqlDelayThread.h" +#include "DatabaseEnv.h" +#include "DatabaseImpl.h" + +/// ---- ASYNC STATEMENTS / TRANSACTIONS ---- + +void SqlStatement::Execute(Database *db) +{ + /// just do it + db->DirectExecute(m_sql); +} + +void SqlTransaction::Execute(Database *db) +{ + if(m_queue.empty()) + return; + db->DirectExecute("START TRANSACTION"); + while(!m_queue.empty()) + { + char const *sql = m_queue.front(); + m_queue.pop(); + + if(!db->DirectExecute(sql)) + { + free((void*)const_cast<char*>(sql)); + db->DirectExecute("ROLLBACK"); + while(!m_queue.empty()) + { + free((void*)const_cast<char*>(m_queue.front())); + m_queue.pop(); + } + return; + } + + free((void*)const_cast<char*>(sql)); + } + db->DirectExecute("COMMIT"); +} + +/// ---- ASYNC QUERIES ---- + +void SqlQuery::Execute(Database *db) +{ + if(!m_callback || !m_queue) + return; + /// execute the query and store the result in the callback + m_callback->SetResult(db->Query(m_sql)); + /// add the callback to the sql result queue of the thread it originated from + m_queue->add(m_callback); +} + +void SqlResultQueue::Update() +{ + /// execute the callbacks waiting in the synchronization queue + while(!empty()) + { + MaNGOS::IQueryCallback * callback = next(); + callback->Execute(); + delete callback; + } +} + +void SqlQueryHolder::Execute(MaNGOS::IQueryCallback * callback, SqlDelayThread *thread, SqlResultQueue *queue) +{ + if(!callback || !thread || !queue) + return; + + /// delay the execution of the queries, sync them with the delay thread + /// which will in turn resync on execution (via the queue) and call back + SqlQueryHolderEx *holderEx = new SqlQueryHolderEx(this, callback, queue); + thread->Delay(holderEx); +} + +bool SqlQueryHolder::SetQuery(size_t index, const char *sql) +{ + if(m_queries.size() <= index) + { + sLog.outError("Query index (%u) out of range (size: %u) for query: %s",index,m_queries.size(),sql); + return false; + } + + if(m_queries[index].first != NULL) + { + sLog.outError("Attempt assign query to holder index (%u) where other query stored (Old: [%s] New: [%s])",index,m_queries.size(),m_queries[index].first,sql); + return false; + } + + /// not executed yet, just stored (it's not called a holder for nothing) + m_queries[index] = SqlResultPair(strdup(sql), NULL); + return true; +} + +bool SqlQueryHolder::SetPQuery(size_t index, const char *format, ...) +{ + if(!format) + { + sLog.outError("Query (index: %u) is empty.",index); + return false; + } + + va_list ap; + char szQuery [MAX_QUERY_LEN]; + va_start(ap, format); + int res = vsnprintf( szQuery, MAX_QUERY_LEN, format, ap ); + va_end(ap); + + if(res==-1) + { + sLog.outError("SQL Query truncated (and not execute) for format: %s",format); + return false; + } + + return SetQuery(index,szQuery); +} + +QueryResult* SqlQueryHolder::GetResult(size_t index) +{ + if(index < m_queries.size()) + { + /// the query strings are freed on the first GetResult or in the destructor + if(m_queries[index].first != NULL) + { + free((void*)(const_cast<char*>(m_queries[index].first))); + m_queries[index].first = NULL; + } + /// when you get a result aways remember to delete it! + return m_queries[index].second; + } + else + return NULL; +} + +void SqlQueryHolder::SetResult(size_t index, QueryResult *result) +{ + /// store the result in the holder + if(index < m_queries.size()) + m_queries[index].second = result; +} + +SqlQueryHolder::~SqlQueryHolder() +{ + for(size_t i = 0; i < m_queries.size(); i++) + { + /// if the result was never used, free the resources + /// results used already (getresult called) are expected to be deleted + if(m_queries[i].first != NULL) + { + free((void*)(const_cast<char*>(m_queries[i].first))); + if(m_queries[i].second) + delete m_queries[i].second; + } + } +} + +void SqlQueryHolder::SetSize(size_t size) +{ + /// to optimize push_back, reserve the number of queries about to be executed + m_queries.resize(size); +} + +void SqlQueryHolderEx::Execute(Database *db) +{ + if(!m_holder || !m_callback || !m_queue) + return; + + /// we can do this, we are friends + std::vector<SqlQueryHolder::SqlResultPair> &queries = m_holder->m_queries; + + for(size_t i = 0; i < queries.size(); i++) + { + /// execute all queries in the holder and pass the results + char const *sql = queries[i].first; + if(sql) m_holder->SetResult(i, db->Query(sql)); + } + + /// sync with the caller thread + m_queue->add(m_callback); +} diff --git a/src/shared/Database/SqlOperations.h b/src/shared/Database/SqlOperations.h new file mode 100644 index 00000000000..0018a7ed08e --- /dev/null +++ b/src/shared/Database/SqlOperations.h @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __SQLOPERATIONS_H +#define __SQLOPERATIONS_H + +#include "Common.h" + +#include "zthread/LockedQueue.h" +#include "zthread/FastMutex.h" +#include "zthread/Thread.h" +#include <queue> +#include "Utilities/Callback.h" + +/// ---- BASE --- + +class Database; +class SqlDelayThread; + +class SqlOperation +{ + public: + virtual void OnRemove() { delete this; } + virtual void Execute(Database *db) = 0; + virtual ~SqlOperation() {} +}; + +/// ---- ASYNC STATEMENTS / TRANSACTIONS ---- + +class SqlStatement : public SqlOperation +{ + private: + const char *m_sql; + public: + SqlStatement(const char *sql) : m_sql(strdup(sql)){} + ~SqlStatement() { void* tofree = const_cast<char*>(m_sql); free(tofree); } + void Execute(Database *db); +}; + +class SqlTransaction : public SqlOperation +{ + private: + std::queue<const char *> m_queue; + public: + SqlTransaction() {} + void DelayExecute(const char *sql) { m_queue.push(strdup(sql)); } + void Execute(Database *db); +}; + +/// ---- ASYNC QUERIES ---- + +class SqlQuery; /// contains a single async query +class QueryResult; /// the result of one +class SqlResultQueue; /// queue for thread sync +class SqlQueryHolder; /// groups several async quries +class SqlQueryHolderEx; /// points to a holder, added to the delay thread + +class SqlResultQueue : public ZThread::LockedQueue<MaNGOS::IQueryCallback*, ZThread::FastMutex> +{ + public: + SqlResultQueue() {} + void Update(); +}; + +class SqlQuery : public SqlOperation +{ + private: + const char *m_sql; + MaNGOS::IQueryCallback * m_callback; + SqlResultQueue * m_queue; + public: + SqlQuery(const char *sql, MaNGOS::IQueryCallback * callback, SqlResultQueue * queue) + : m_sql(strdup(sql)), m_callback(callback), m_queue(queue) {} + ~SqlQuery() { void* tofree = const_cast<char*>(m_sql); free(tofree); } + void Execute(Database *db); +}; + +class SqlQueryHolder +{ + friend class SqlQueryHolderEx; + private: + typedef std::pair<const char*, QueryResult*> SqlResultPair; + std::vector<SqlResultPair> m_queries; + public: + SqlQueryHolder() {} + ~SqlQueryHolder(); + bool SetQuery(size_t index, const char *sql); + bool SetPQuery(size_t index, const char *format, ...) ATTR_PRINTF(3,4); + void SetSize(size_t size); + QueryResult* GetResult(size_t index); + void SetResult(size_t index, QueryResult *result); + void Execute(MaNGOS::IQueryCallback * callback, SqlDelayThread *thread, SqlResultQueue *queue); +}; + +class SqlQueryHolderEx : public SqlOperation +{ + private: + SqlQueryHolder * m_holder; + MaNGOS::IQueryCallback * m_callback; + SqlResultQueue * m_queue; + public: + SqlQueryHolderEx(SqlQueryHolder *holder, MaNGOS::IQueryCallback * callback, SqlResultQueue * queue) + : m_holder(holder), m_callback(callback), m_queue(queue) {} + void Execute(Database *db); +}; +#endif //__SQLOPERATIONS_H diff --git a/src/shared/Database/dbcfile.cpp b/src/shared/Database/dbcfile.cpp new file mode 100644 index 00000000000..2521ab22614 --- /dev/null +++ b/src/shared/Database/dbcfile.cpp @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "dbcfile.h" + +DBCFile::DBCFile() +{ + data = NULL; + fieldsOffset = NULL; +} + +bool DBCFile::Load(const char *filename, const char *fmt) +{ + + uint32 header; + if(data) + { + delete [] data; + data=NULL; + } + FILE * f=fopen(filename,"rb"); + if(!f)return false; + + fread(&header,4,1,f); // Number of records + EndianConvert(header); + if(header!=0x43424457) + { + //printf("not dbc file"); + return false; //'WDBC' + } + fread(&recordCount,4,1,f); // Number of records + EndianConvert(recordCount); + fread(&fieldCount,4,1,f); // Number of fields + EndianConvert(fieldCount); + fread(&recordSize,4,1,f); // Size of a record + EndianConvert(recordSize); + fread(&stringSize,4,1,f); // String size + EndianConvert(stringSize); + + fieldsOffset = new uint32[fieldCount]; + fieldsOffset[0] = 0; + for(uint32 i = 1; i < fieldCount; i++) + { + fieldsOffset[i] = fieldsOffset[i - 1]; + if (fmt[i - 1] == 'b' || fmt[i - 1] == 'X') // byte fields + fieldsOffset[i] += 1; + else // 4 byte fields (int32/float/strings) + fieldsOffset[i] += 4; + } + + data = new unsigned char[recordSize*recordCount+stringSize]; + stringTable = data + recordSize*recordCount; + fread(data,recordSize*recordCount+stringSize,1,f); + fclose(f); + return true; +} + +DBCFile::~DBCFile() +{ + if(data) + delete [] data; + if(fieldsOffset) + delete [] fieldsOffset; +} + +DBCFile::Record DBCFile::getRecord(size_t id) +{ + assert(data); + return Record(*this, data + id*recordSize); +} + +uint32 DBCFile::GetFormatRecordSize(const char * format,int32* index_pos) +{ + uint32 recordsize = 0; + int32 i = -1; + for(uint32 x=0; format[x];++x) + switch(format[x]) + { + case FT_FLOAT: + case FT_INT: + recordsize+=4; + break; + case FT_STRING: + recordsize+=sizeof(char*); + break; + case FT_SORT: + i=x; + break; + case FT_IND: + i=x; + recordsize+=4; + break; + case FT_BYTE: + recordsize += 1; + break; + } + + if(index_pos) + *index_pos = i; + + return recordsize; +} + +char* DBCFile::AutoProduceData(const char* format, uint32& records, char**& indexTable) +{ + /* + format STRING, NA, FLOAT,NA,INT <=> + struct{ + char* field0, + float field1, + int field2 + }entry; + + this func will generate entry[rows] data; + */ + + typedef char * ptr; + if(strlen(format)!=fieldCount) + return NULL; + + //get struct size and index pos + int32 i; + uint32 recordsize=GetFormatRecordSize(format,&i); + + if(i>=0) + { + uint32 maxi=0; + //find max index + for(uint32 y=0;y<recordCount;y++) + { + uint32 ind=getRecord(y).getUInt (i); + if(ind>maxi)maxi=ind; + } + + ++maxi; + records=maxi; + indexTable=new ptr[maxi]; + memset(indexTable,0,maxi*sizeof(ptr)); + } + else + { + records = recordCount; + indexTable = new ptr[recordCount]; + } + + char* dataTable= new char[recordCount*recordsize]; + + uint32 offset=0; + + for(uint32 y =0;y<recordCount;y++) + { + if(i>=0) + { + indexTable[getRecord(y).getUInt(i)]=&dataTable[offset]; + } + else + indexTable[y]=&dataTable[offset]; + + for(uint32 x=0;x<fieldCount;x++) + { + switch(format[x]) + { + case FT_FLOAT: + *((float*)(&dataTable[offset]))=getRecord(y).getFloat(x); + offset+=4; + break; + case FT_IND: + case FT_INT: + *((uint32*)(&dataTable[offset]))=getRecord(y).getUInt(x); + offset+=4; + break; + case FT_BYTE: + *((uint8*)(&dataTable[offset]))=getRecord(y).getUInt8(x); + offset+=1; + break; + case FT_STRING: + *((char**)(&dataTable[offset]))=NULL; // will be replaces non-empty or "" strings in AutoProduceStrings + offset+=sizeof(char*); + break; + } + } + } + + return dataTable; +} + +char* DBCFile::AutoProduceStrings(const char* format, char* dataTable) +{ + if(strlen(format)!=fieldCount) + return NULL; + + char* stringPool= new char[stringSize]; + memcpy(stringPool,stringTable,stringSize); + + uint32 offset=0; + + for(uint32 y =0;y<recordCount;y++) + { + for(uint32 x=0;x<fieldCount;x++) + switch(format[x]) + { + case FT_FLOAT: + case FT_IND: + case FT_INT: + offset+=4; + break; + case FT_BYTE: + offset+=1; + break; + case FT_STRING: + // fill only not filled entries + char** slot = (char**)(&dataTable[offset]); + if(!*slot || !**slot) + { + const char * st = getRecord(y).getString(x); + *slot=stringPool+(st-(const char*)stringTable); + } + offset+=sizeof(char*); + break; + } + } + + return stringPool; +} diff --git a/src/shared/Database/dbcfile.h b/src/shared/Database/dbcfile.h new file mode 100644 index 00000000000..bc59914ce53 --- /dev/null +++ b/src/shared/Database/dbcfile.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef DBCFILE_H +#define DBCFILE_H +#include "Platform/Define.h" +#include "Utilities/ByteConverter.h" +#include <cassert> + +enum +{ + FT_NA='x', //not used or unknown, 4 byte size + FT_NA_BYTE='X', //not used or unknown, byte + FT_STRING='s', //char* + FT_FLOAT='f', //float + FT_INT='i', //uint32 + FT_BYTE='b', //uint8 + FT_SORT='d', //sorted by this field, field is not included + FT_IND='n', //the same,but parsed to data + FT_LOGIC='l' //Logical (boolean) +}; + +class DBCFile +{ + public: + DBCFile(); + ~DBCFile(); + + bool Load(const char *filename, const char *fmt); + + class Record + { + public: + float getFloat(size_t field) const + { + assert(field < file.fieldCount); + float val = *reinterpret_cast<float*>(offset+file.GetOffset(field)); + EndianConvert(val); + return val; + } + uint32 getUInt(size_t field) const + { + assert(field < file.fieldCount); + uint32 val = *reinterpret_cast<uint32*>(offset+file.GetOffset(field)); + EndianConvert(val); + return val; + } + uint8 getUInt8(size_t field) const + { + assert(field < file.fieldCount); + return *reinterpret_cast<uint8*>(offset+file.GetOffset(field)); + } + + const char *getString(size_t field) const + { + assert(field < file.fieldCount); + size_t stringOffset = getUInt(field); + assert(stringOffset < file.stringSize); + return reinterpret_cast<char*>(file.stringTable + stringOffset); + } + + private: + Record(DBCFile &file_, unsigned char *offset_): offset(offset_), file(file_) {} + unsigned char *offset; + DBCFile &file; + + friend class DBCFile; + + }; + + // Get record by id + Record getRecord(size_t id); + /// Get begin iterator over records + + uint32 GetNumRows() const { return recordCount;} + uint32 GetCols() const { return fieldCount; } + uint32 GetOffset(size_t id) const { return (fieldsOffset != NULL && id < fieldCount) ? fieldsOffset[id] : 0; } + bool IsLoaded() {return (data!=NULL);} + char* AutoProduceData(const char* fmt, uint32& count, char**& indexTable); + char* AutoProduceStrings(const char* fmt, char* dataTable); + static uint32 GetFormatRecordSize(const char * format, int32 * index_pos = NULL); + private: + + uint32 recordSize; + uint32 recordCount; + uint32 fieldCount; + uint32 stringSize; + uint32 *fieldsOffset; + unsigned char *data; + unsigned char *stringTable; +}; +#endif diff --git a/src/shared/Errors.h b/src/shared/Errors.h new file mode 100644 index 00000000000..a038b777916 --- /dev/null +++ b/src/shared/Errors.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOSSERVER_ERRORS_H +#define MANGOSSERVER_ERRORS_H + +#define WPAssert( assertion ) { if( !(assertion) ) { fprintf( stderr, "\n%s:%i ASSERTION FAILED:\n %s\n", __FILE__, __LINE__, #assertion ); assert( #assertion &&0 ); } } +#define WPError( assertion, errmsg ) if( ! (assertion) ) { sLog.outError( "%s:%i ERROR:\n %s\n", __FILE__, __LINE__, (char *)errmsg ); assert( false ); } +#define WPWarning( assertion, errmsg ) if( ! (assertion) ) { sLog.outError( "%s:%i WARNING:\n %s\n", __FILE__, __LINE__, (char *)errmsg ); } + +#define WPFatal( assertion, errmsg ) if( ! (assertion) ) { sLog.outError( "%s:%i FATAL ERROR:\n %s\n", __FILE__, __LINE__, (char *)errmsg ); assert( #assertion &&0 ); abort(); } + +#define ASSERT WPAssert +#endif diff --git a/src/shared/Log.cpp b/src/shared/Log.cpp new file mode 100644 index 00000000000..b6283bc8437 --- /dev/null +++ b/src/shared/Log.cpp @@ -0,0 +1,762 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.h" +#include "Log.h" +#include "Policies/SingletonImp.h" +#include "Config/ConfigEnv.h" +#include "Util.h" + +#include <stdarg.h> + +INSTANTIATE_SINGLETON_1( Log ); + +enum LogType +{ + LogNormal = 0, + LogDetails, + LogDebug, + LogError +}; + +const int LogType_count = int(LogError) +1; + +void Log::InitColors(std::string str) +{ + if(str.empty()) + { + m_colored = false; + return; + } + + int color[4]; + + std::istringstream ss(str); + + for(int i = 0; i < LogType_count; ++i) + { + ss >> color[i]; + + if(!ss) + return; + + if(color[i] < 0 || color[i] >= Color_count) + return; + } + + for(int i = 0; i < LogType_count; ++i) + m_colors[i] = Color(color[i]); + + m_colored = true; +} + +void Log::SetColor(bool stdout_stream, Color color) +{ + #if PLATFORM == PLATFORM_WINDOWS + + static WORD WinColorFG[Color_count] = + { + 0, // BLACK + FOREGROUND_RED, // RED + FOREGROUND_GREEN, // GREEN + FOREGROUND_RED | FOREGROUND_GREEN, // BROWN + FOREGROUND_BLUE, // BLUE + FOREGROUND_RED | FOREGROUND_BLUE,// MAGENTA + FOREGROUND_GREEN | FOREGROUND_BLUE, // CYAN + FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE,// WHITE + // YELLOW + FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY, + // RED_BOLD + FOREGROUND_RED | FOREGROUND_INTENSITY, + // GREEN_BOLD + FOREGROUND_GREEN | FOREGROUND_INTENSITY, + FOREGROUND_BLUE | FOREGROUND_INTENSITY, // BLUE_BOLD + // MAGENTA_BOLD + FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY, + // CYAN_BOLD + FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY, + // WHITE_BOLD + FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY + }; + + HANDLE hConsole = GetStdHandle(stdout_stream ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE ); + SetConsoleTextAttribute(hConsole, WinColorFG[color]); + #else + + enum ANSITextAttr + { + TA_NORMAL=0, + TA_BOLD=1, + TA_BLINK=5, + TA_REVERSE=7 + }; + + enum ANSIFgTextAttr + { + FG_BLACK=30, FG_RED, FG_GREEN, FG_BROWN, FG_BLUE, + FG_MAGENTA, FG_CYAN, FG_WHITE, FG_YELLOW + }; + + enum ANSIBgTextAttr + { + BG_BLACK=40, BG_RED, BG_GREEN, BG_BROWN, BG_BLUE, + BG_MAGENTA, BG_CYAN, BG_WHITE + }; + + static uint8 UnixColorFG[Color_count] = + { + FG_BLACK, // BLACK + FG_RED, // RED + FG_GREEN, // GREEN + FG_BROWN, // BROWN + FG_BLUE, // BLUE + FG_MAGENTA, // MAGENTA + FG_CYAN, // CYAN + FG_WHITE, // WHITE + FG_YELLOW, // YELLOW + FG_RED, // LRED + FG_GREEN, // LGREEN + FG_BLUE, // LBLUE + FG_MAGENTA, // LMAGENTA + FG_CYAN, // LCYAN + FG_WHITE // LWHITE + }; + + fprintf((stdout_stream? stdout : stderr), "\x1b[%d%sm",UnixColorFG[color],(color>=YELLOW&&color<Color_count ?";1":"")); + #endif +} + +void Log::ResetColor(bool stdout_stream) +{ + #if PLATFORM == PLATFORM_WINDOWS + HANDLE hConsole = GetStdHandle(stdout_stream ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE ); + SetConsoleTextAttribute(hConsole, FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED ); + #else + fprintf(( stdout_stream ? stdout : stderr ), "\x1b[0m"); + #endif +} + +void Log::SetLogLevel(char *Level) +{ + int32 NewLevel =atoi((char*)Level); + if ( NewLevel <0 ) + NewLevel = 0; + m_logLevel = NewLevel; + + printf( "LogLevel is %u\n",m_logLevel ); +} + +void Log::SetLogFileLevel(char *Level) +{ + int32 NewLevel =atoi((char*)Level); + if ( NewLevel <0 ) + NewLevel = 0; + m_logFileLevel = NewLevel; + + printf( "LogFileLevel is %u\n",m_logFileLevel ); +} + +void Log::Initialize() +{ + std::string logsDir = sConfig.GetStringDefault("LogsDir",""); + + if(!logsDir.empty()) + { + if((logsDir.at(logsDir.length()-1)!='/') && (logsDir.at(logsDir.length()-1)!='\\')) + logsDir.append("/"); + } + + std::string logfn=sConfig.GetStringDefault("LogFile", ""); + if(!logfn.empty()) + { + if(sConfig.GetBoolDefault("LogTimestamp",false)) + { + std::string logTimestamp = GetTimestampStr(); + logTimestamp.insert(0,"_"); + size_t dot_pos = logfn.find_last_of("."); + if(dot_pos!=logfn.npos) + logfn.insert(dot_pos,logTimestamp); + else + logfn += logTimestamp; + } + + logfile = fopen((logsDir+logfn).c_str(), "w"); + } + + std::string gmlogname = sConfig.GetStringDefault("GMLogFile", ""); + if(!gmlogname.empty()) + { + if(sConfig.GetBoolDefault("GmLogTimestamp",false)) + { + std::string gmLogTimestamp = GetTimestampStr(); + gmLogTimestamp.insert(0,"_"); + size_t dot_pos = gmlogname.find_last_of("."); + if(dot_pos!=gmlogname.npos) + gmlogname.insert(dot_pos,gmLogTimestamp); + else + gmlogname += gmLogTimestamp; + } + gmLogfile = fopen((logsDir+gmlogname).c_str(), "a"); + } + + std::string charlogname = sConfig.GetStringDefault("CharLogFile", ""); + if(!charlogname.empty()) + { + if(sConfig.GetBoolDefault("CharLogTimestamp",false)) + { + std::string charLogTimestamp = GetTimestampStr(); + charLogTimestamp.insert(0,"_"); + size_t dot_pos = charlogname.find_last_of("."); + if(dot_pos!=charlogname.npos) + charlogname.insert(dot_pos,charLogTimestamp); + else + charlogname += charLogTimestamp; + } + charLogfile = fopen((logsDir+charlogname).c_str(), "a"); + } + + std::string dberlogname = sConfig.GetStringDefault("DBErrorLogFile", ""); + if(!dberlogname.empty()) + { + dberLogfile = fopen((logsDir+dberlogname).c_str(), "a"); + } + std::string ralogname = sConfig.GetStringDefault("RaLogFile", ""); + if(!ralogname.empty()) + { + raLogfile = fopen((logsDir+ralogname).c_str(), "a"); + } + m_includeTime = sConfig.GetBoolDefault("LogTime", false); + m_logLevel = sConfig.GetIntDefault("LogLevel", 0); + m_logFileLevel = sConfig.GetIntDefault("LogFileLevel", 0); + InitColors(sConfig.GetStringDefault("LogColors", "")); + + m_logFilter = 0; + + if(sConfig.GetBoolDefault("LogFilter_TransportMoves", true)) + m_logFilter |= LOG_FILTER_TRANSPORT_MOVES; + if(sConfig.GetBoolDefault("LogFilter_CreatureMoves", true)) + m_logFilter |= LOG_FILTER_CREATURE_MOVES; + if(sConfig.GetBoolDefault("LogFilter_VisibilityChanges", true)) + m_logFilter |= LOG_FILTER_VISIBILITY_CHANGES; + + m_charLog_Dump = sConfig.GetBoolDefault("CharLogDump", false); +} + +void Log::outTimestamp(FILE* file) +{ + time_t t = time(NULL); + tm* aTm = localtime(&t); + // YYYY year + // MM month (2 digits 01-12) + // DD day (2 digits 01-31) + // HH hour (2 digits 00-23) + // MM minutes (2 digits 00-59) + // SS seconds (2 digits 00-59) + fprintf(file,"%-4d-%02d-%02d %02d:%02d:%02d ",aTm->tm_year+1900,aTm->tm_mon+1,aTm->tm_mday,aTm->tm_hour,aTm->tm_min,aTm->tm_sec); +} + +void Log::outTime() +{ + time_t t = time(NULL); + tm* aTm = localtime(&t); + // YYYY year + // MM month (2 digits 01-12) + // DD day (2 digits 01-31) + // HH hour (2 digits 00-23) + // MM minutes (2 digits 00-59) + // SS seconds (2 digits 00-59) + printf("%02d:%02d:%02d ",aTm->tm_hour,aTm->tm_min,aTm->tm_sec); +} + +std::string Log::GetTimestampStr() +{ + time_t t = time(NULL); + tm* aTm = localtime(&t); + // YYYY year + // MM month (2 digits 01-12) + // DD day (2 digits 01-31) + // HH hour (2 digits 00-23) + // MM minutes (2 digits 00-59) + // SS seconds (2 digits 00-59) + char buf[20]; + snprintf(buf,20,"%04d-%02d-%02d_%02d-%02d-%02d",aTm->tm_year+1900,aTm->tm_mon+1,aTm->tm_mday,aTm->tm_hour,aTm->tm_min,aTm->tm_sec); + return std::string(buf); +} + +void Log::outTitle( const char * str) +{ + if( !str ) + return; + + if(m_colored) + SetColor(true,WHITE); + + // not expected utf8 and then send as-is + printf( str ); + + if(m_colored) + ResetColor(true); + + printf( "\n" ); + if(logfile) + { + fprintf(logfile, str); + fprintf(logfile, "\n" ); + fflush(logfile); + } + + fflush(stdout); +} + +void Log::outString() +{ + if(m_includeTime) + outTime(); + printf( "\n" ); + if(logfile) + { + outTimestamp(logfile); + fprintf(logfile, "\n" ); + fflush(logfile); + } + fflush(stdout); +} + +void Log::outString( const char * str, ... ) +{ + if( !str ) + return; + + if(m_colored) + SetColor(true,m_colors[LogNormal]); + + if(m_includeTime) + outTime(); + + UTF8PRINTF(stdout,str,); + + if(m_colored) + ResetColor(true); + + printf( "\n" ); + if(logfile) + { + outTimestamp(logfile); + + va_list ap; + va_start(ap, str); + vfprintf(logfile, str, ap); + fprintf(logfile, "\n" ); + va_end(ap); + + fflush(logfile); + } + fflush(stdout); +} + +void Log::outError( const char * err, ... ) +{ + if( !err ) + return; + + if(m_colored) + SetColor(false,m_colors[LogError]); + + if(m_includeTime) + outTime(); + + UTF8PRINTF(stderr,err,); + + if(m_colored) + ResetColor(false); + + fprintf( stderr, "\n" ); + if(logfile) + { + outTimestamp(logfile); + fprintf(logfile, "ERROR:" ); + + va_list ap; + va_start(ap, err); + vfprintf(logfile, err, ap); + va_end(ap); + + fprintf(logfile, "\n" ); + fflush(logfile); + } + fflush(stderr); +} + +void Log::outErrorDb( const char * err, ... ) +{ + if( !err ) + return; + + if(m_colored) + SetColor(false,m_colors[LogError]); + + if(m_includeTime) + outTime(); + + UTF8PRINTF(stderr,err,); + + if(m_colored) + ResetColor(false); + + fprintf( stderr, "\n" ); + + if(logfile) + { + outTimestamp(logfile); + fprintf(logfile, "ERROR:" ); + + va_list ap; + va_start(ap, err); + vfprintf(logfile, err, ap); + va_end(ap); + + fprintf(logfile, "\n" ); + fflush(logfile); + } + + if(dberLogfile) + { + outTimestamp(dberLogfile); + + va_list ap; + va_start(ap, err); + vfprintf(dberLogfile, err, ap); + va_end(ap); + + fprintf(dberLogfile, "\n" ); + fflush(dberLogfile); + } + fflush(stderr); +} + +void Log::outBasic( const char * str, ... ) +{ + if( !str ) + return; + + if( m_logLevel > 0 ) + { + if(m_colored) + SetColor(true,m_colors[LogDetails]); + + if(m_includeTime) + outTime(); + + UTF8PRINTF(stdout,str,); + + if(m_colored) + ResetColor(true); + + printf( "\n" ); + } + + if(logfile && m_logFileLevel > 0) + { + va_list ap; + outTimestamp(logfile); + va_start(ap, str); + vfprintf(logfile, str, ap); + fprintf(logfile, "\n" ); + va_end(ap); + fflush(logfile); + } + fflush(stdout); +} + +void Log::outDetail( const char * str, ... ) +{ + if( !str ) + return; + + if( m_logLevel > 1 ) + { + + if(m_colored) + SetColor(true,m_colors[LogDetails]); + + if(m_includeTime) + outTime(); + + UTF8PRINTF(stdout,str,); + + if(m_colored) + ResetColor(true); + + printf( "\n" ); + } + if(logfile && m_logFileLevel > 1) + { + va_list ap; + outTimestamp(logfile); + va_start(ap, str); + vfprintf(logfile, str, ap); + fprintf(logfile, "\n" ); + va_end(ap); + fflush(logfile); + } + + fflush(stdout); +} + +void Log::outDebugInLine( const char * str, ... ) +{ + if( !str ) + return; + if( m_logLevel > 2 ) + { + if(m_colored) + SetColor(true,m_colors[LogDebug]); + + UTF8PRINTF(stdout,str,); + + if(m_colored) + ResetColor(true); + } + if(logfile && m_logFileLevel > 2) + { + va_list ap; + va_start(ap, str); + vfprintf(logfile, str, ap); + va_end(ap); + } +} + +void Log::outDebug( const char * str, ... ) +{ + if( !str ) + return; + if( m_logLevel > 2 ) + { + if(m_colored) + SetColor(true,m_colors[LogDebug]); + + if(m_includeTime) + outTime(); + + UTF8PRINTF(stdout,str,); + + if(m_colored) + ResetColor(true); + + printf( "\n" ); + } + if(logfile && m_logFileLevel > 2) + { + outTimestamp(logfile); + + va_list ap; + va_start(ap, str); + vfprintf(logfile, str, ap); + va_end(ap); + + fprintf(logfile, "\n" ); + fflush(logfile); + } + fflush(stdout); +} + +void Log::outCommand( const char * str, ... ) +{ + if( !str ) + return; + + if( m_logLevel > 1 ) + { + if(m_colored) + SetColor(true,m_colors[LogDetails]); + + if(m_includeTime) + outTime(); + + UTF8PRINTF(stdout,str,); + + if(m_colored) + ResetColor(true); + + printf( "\n" ); + } + if(logfile && m_logFileLevel > 1) + { + va_list ap; + outTimestamp(logfile); + va_start(ap, str); + vfprintf(logfile, str, ap); + fprintf(logfile, "\n" ); + va_end(ap); + fflush(logfile); + } + if(gmLogfile) + { + va_list ap; + outTimestamp(gmLogfile); + va_start(ap, str); + vfprintf(gmLogfile, str, ap); + fprintf(gmLogfile, "\n" ); + va_end(ap); + fflush(gmLogfile); + } + fflush(stdout); +} + +void Log::outChar(const char * str, ... ) +{ + + if (!str) + return; + + if(charLogfile) + { + va_list ap; + outTimestamp(charLogfile); + va_start(ap, str); + vfprintf(charLogfile, str, ap); + fprintf(charLogfile, "\n" ); + va_end(ap); + fflush(charLogfile); + } +} + +void Log::outCharDump( const char * str, uint32 account_id, uint32 guid, const char * name ) +{ + if(charLogfile) + { + fprintf(charLogfile, "== START DUMP == (account: %u guid: %u name: %s )\n%s\n== END DUMP ==\n",account_id,guid,name,str ); + fflush(charLogfile); + } +} + +void Log::outMenu( const char * str, ... ) +{ + if( !str ) + return; + + SetColor(true,m_colors[LogNormal]); + + if(m_includeTime) + outTime(); + + UTF8PRINTF(stdout,str,); + + ResetColor(true); + + if(logfile) + { + outTimestamp(logfile); + + va_list ap; + va_start(ap, str); + vfprintf(logfile, str, ap); + va_end(ap); + + fprintf(logfile, "\n" ); + fflush(logfile); + } + fflush(stdout); +} + +void Log::outRALog( const char * str, ... ) +{ + if( !str ) + return; + va_list ap; + if (raLogfile) + { + outTimestamp(raLogfile); + va_start(ap, str); + vfprintf(raLogfile, str, ap); + fprintf(raLogfile, "\n" ); + va_end(ap); + fflush(raLogfile); + } + fflush(stdout); +} + +void outstring_log(const char * str, ...) +{ + if( !str ) + return; + + char buf[256]; + va_list ap; + va_start(ap, str); + vsnprintf(buf,256, str, ap); + va_end(ap); + + MaNGOS::Singleton<Log>::Instance().outString(buf); +} + +void detail_log(const char * str, ...) +{ + if( !str ) + return; + + char buf[256]; + va_list ap; + va_start(ap, str); + vsnprintf(buf,256, str, ap); + va_end(ap); + + MaNGOS::Singleton<Log>::Instance().outDetail(buf); +} + +void debug_log(const char * str, ...) +{ + if( !str ) + return; + + char buf[256]; + va_list ap; + va_start(ap, str); + vsnprintf(buf,256, str, ap); + va_end(ap); + + MaNGOS::Singleton<Log>::Instance().outDebug(buf); +} + +void error_log(const char * str, ...) +{ + if( !str ) + return; + + char buf[256]; + va_list ap; + va_start(ap, str); + vsnprintf(buf,256, str, ap); + va_end(ap); + + MaNGOS::Singleton<Log>::Instance().outError(buf); +} + +void error_db_log(const char * str, ...) +{ + if( !str ) + return; + + char buf[256]; + va_list ap; + va_start(ap, str); + vsnprintf(buf,256, str, ap); + va_end(ap); + + MaNGOS::Singleton<Log>::Instance().outErrorDb(buf); +} diff --git a/src/shared/Log.h b/src/shared/Log.h new file mode 100644 index 00000000000..e6e51afadd6 --- /dev/null +++ b/src/shared/Log.h @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOSSERVER_LOG_H +#define MANGOSSERVER_LOG_H + +#include "Common.h" +#include "Policies/Singleton.h" + +class Config; + +// bitmask +enum LogFilters +{ + LOG_FILTER_TRANSPORT_MOVES = 1, + LOG_FILTER_CREATURE_MOVES = 2, + LOG_FILTER_VISIBILITY_CHANGES = 4 +}; + +enum Color +{ + BLACK, + RED, + GREEN, + BROWN, + BLUE, + MAGENTA, + CYAN, + GREY, + YELLOW, + LRED, + LGREEN, + LBLUE, + LMAGENTA, + LCYAN, + WHITE +}; + +const int Color_count = int(WHITE)+1; + +class Log : public MaNGOS::Singleton<Log, MaNGOS::ClassLevelLockable<Log, ZThread::FastMutex> > +{ + friend class MaNGOS::OperatorNew<Log>; + Log() : raLogfile(NULL), logfile(NULL), gmLogfile(NULL), charLogfile(NULL), dberLogfile(NULL), m_colored(false) { Initialize(); } + ~Log() + { + if( logfile != NULL ) + fclose(logfile); + logfile = NULL; + + if( gmLogfile != NULL ) + fclose(gmLogfile); + gmLogfile = NULL; + + if (charLogfile != NULL) + fclose(charLogfile); + charLogfile = NULL; + + if( dberLogfile != NULL ) + fclose(dberLogfile); + dberLogfile = NULL; + + if (raLogfile != NULL) + fclose(raLogfile); + raLogfile = NULL; + } + public: + void Initialize(); + void InitColors(std::string init_str); + void outTitle( const char * str); + void outCommand( const char * str, ...) ATTR_PRINTF(2,3); + void outString(); // any log level + // any log level + void outString( const char * str, ... ) ATTR_PRINTF(2,3); + // any log level + void outError( const char * err, ... ) ATTR_PRINTF(2,3); + // log level >= 1 + void outBasic( const char * str, ... ) ATTR_PRINTF(2,3); + // log level >= 2 + void outDetail( const char * str, ... ) ATTR_PRINTF(2,3); + // log level >= 3 + void outDebugInLine( const char * str, ... ) ATTR_PRINTF(2,3); + // log level >= 3 + void outDebug( const char * str, ... ) ATTR_PRINTF(2,3); + // any log level + void outMenu( const char * str, ... ) ATTR_PRINTF(2,3); + // any log level + void outErrorDb( const char * str, ... ) ATTR_PRINTF(2,3); + // any log level + void outChar( const char * str, ... ) ATTR_PRINTF(2,3); + // any log level + void outCharDump( const char * str, uint32 account_id, uint32 guid, const char * name ); + void outRALog( const char * str, ... ) ATTR_PRINTF(2,3); + void SetLogLevel(char * Level); + void SetLogFileLevel(char * Level); + void SetColor(bool stdout_stream, Color color); + void ResetColor(bool stdout_stream); + void outTime(); + static void outTimestamp(FILE* file); + static std::string GetTimestampStr(); + uint32 getLogFilter() const { return m_logFilter; } + bool IsOutDebug() const { return m_logLevel > 2 || (m_logFileLevel > 2 && logfile); } + bool IsOutCharDump() const { return m_charLog_Dump; } + bool IsIncludeTime() const { return m_includeTime; } + private: + FILE* raLogfile; + FILE* logfile; + FILE* gmLogfile; + FILE* charLogfile; + FILE* dberLogfile; + + // log/console control + uint32 m_logLevel; + uint32 m_logFileLevel; + bool m_colored; + bool m_includeTime; + Color m_colors[4]; + uint32 m_logFilter; + + // char log control + bool m_charLog_Dump; + +}; + +#define sLog MaNGOS::Singleton<Log>::Instance() + +#ifdef MANGOS_DEBUG +#define DEBUG_LOG MaNGOS::Singleton<Log>::Instance().outDebug +#else +#define DEBUG_LOG +#endif + +// primary for script library +void MANGOS_DLL_SPEC outstring_log(const char * str, ...) ATTR_PRINTF(1,2); +void MANGOS_DLL_SPEC detail_log(const char * str, ...) ATTR_PRINTF(1,2); +void MANGOS_DLL_SPEC debug_log(const char * str, ...) ATTR_PRINTF(1,2); +void MANGOS_DLL_SPEC error_log(const char * str, ...) ATTR_PRINTF(1,2); +void MANGOS_DLL_SPEC error_db_log(const char * str, ...) ATTR_PRINTF(1,2); +#endif diff --git a/src/shared/Makefile.am b/src/shared/Makefile.am new file mode 100644 index 00000000000..4df10557532 --- /dev/null +++ b/src/shared/Makefile.am @@ -0,0 +1,117 @@ +# Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +## Process this file with automake to produce Makefile.in + +## TODO move vmaps in src dir instead of src/shared + +## Sub-directories to parse +SUBDIRS = vmap + +## Build MaNGOS shared library and its parts as convenience library. +# All libraries will be convenience libraries. Might be changed to shared +# later. +noinst_LIBRARIES = libshared.a + +libshared_a_CPPFLAGS = \ +$(MYSQL_INCLUDES) \ +$(POSTGRE_INCLUDES) \ +-I$(top_srcdir)/dep/include \ +-I$(top_srcdir)/src/framework + +# libmangosshared library will later be reused by ... +libshared_a_SOURCES = \ +$(srcdir)/Base.cpp \ +$(srcdir)/Base.h \ +$(srcdir)/ByteBuffer.h \ +$(srcdir)/Common.cpp \ +$(srcdir)/Common.h \ +$(srcdir)/Errors.h \ +$(srcdir)/Log.cpp \ +$(srcdir)/Log.h \ +$(srcdir)/Mthread.cpp \ +$(srcdir)/Mthread.h \ +$(srcdir)/ProgressBar.cpp \ +$(srcdir)/ProgressBar.h \ +$(srcdir)/Timer.h \ +$(srcdir)/Util.cpp \ +$(srcdir)/Util.h \ +$(srcdir)/WorldPacket.h \ +$(srcdir)/SystemConfig.h \ +$(srcdir)/Auth/AuthCrypt.cpp \ +$(srcdir)/Auth/AuthCrypt.h \ +$(srcdir)/Auth/BigNumber.cpp \ +$(srcdir)/Auth/BigNumber.h \ +$(srcdir)/Auth/Hmac.cpp \ +$(srcdir)/Auth/Hmac.h \ +$(srcdir)/Auth/Sha1.cpp \ +$(srcdir)/Auth/Sha1.h \ +$(srcdir)/Auth/md5.c \ +$(srcdir)/Auth/md5.h \ +$(srcdir)/Config/dotconfpp/dotconfpp.cpp \ +$(srcdir)/Config/dotconfpp/dotconfpp.h \ +$(srcdir)/Config/dotconfpp/mempool.cpp \ +$(srcdir)/Config/dotconfpp/mempool.h \ +$(srcdir)/Config/Config.cpp \ +$(srcdir)/Config/Config.h \ +$(srcdir)/Config/ConfigEnv.h \ +$(srcdir)/Database/DBCStores.cpp \ +$(srcdir)/Database/DBCStores.h \ +$(srcdir)/Database/DBCStructure.h \ +$(srcdir)/Database/DBCfmt.cpp \ +$(srcdir)/Database/Database.cpp \ +$(srcdir)/Database/Database.h \ +$(srcdir)/Database/DatabaseEnv.h \ +$(srcdir)/Database/DatabaseImpl.h \ +$(srcdir)/Database/DatabaseMysql.cpp \ +$(srcdir)/Database/DatabasePostgre.cpp \ +$(srcdir)/Database/DatabaseMysql.h \ +$(srcdir)/Database/DatabasePostgre.h \ +$(srcdir)/Database/DatabaseSqlite.cpp \ +$(srcdir)/Database/DatabaseSqlite.h \ +$(srcdir)/Database/Field.cpp \ +$(srcdir)/Database/Field.h \ +$(srcdir)/Database/MySQLDelayThread.h \ +$(srcdir)/Database/PGSQLDelayThread.h \ +$(srcdir)/Database/QueryResult.h \ +$(srcdir)/Database/QueryResultMysql.cpp \ +$(srcdir)/Database/QueryResultMysql.h \ +$(srcdir)/Database/QueryResultPostgre.cpp \ +$(srcdir)/Database/QueryResultPostgre.h \ +$(srcdir)/Database/QueryResultSqlite.cpp \ +$(srcdir)/Database/QueryResultSqlite.h \ +$(srcdir)/Database/SQLStorage.cpp \ +$(srcdir)/Database/SQLStorage.h \ +$(srcdir)/Database/SqlDelayThread.cpp \ +$(srcdir)/Database/SqlDelayThread.h \ +$(srcdir)/Database/SqlOperations.cpp \ +$(srcdir)/Database/SqlOperations.h \ +$(srcdir)/Database/dbcfile.cpp \ +$(srcdir)/Database/dbcfile.h + +## Additional files to include when running 'make dist' +# Disabled packet logger +EXTRA_DIST = \ + PacketLog.cpp \ + PacketLog.h + +# System Win32 files +EXTRA_DIST += \ + ServiceWin32.cpp \ + ServiceWin32.h \ + WheatyExceptionReport.cpp \ + WheatyExceptionReport.h + diff --git a/src/shared/Mthread.cpp b/src/shared/Mthread.cpp new file mode 100644 index 00000000000..0dfaac4fcf7 --- /dev/null +++ b/src/shared/Mthread.cpp @@ -0,0 +1,205 @@ +/* + Cross-platform thread handling + Copyright (C) 2005 Andrew Zabolotny + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "Mthread.h" + +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE_CC__) +# define MANGOS_PTHREAD_MUTEX_RECURSIVE PTHREAD_MUTEX_RECURSIVE +#else +# define MANGOS_PTHREAD_MUTEX_RECURSIVE PTHREAD_MUTEX_RECURSIVE_NP +#endif + +#if PLATFORM != PLATFORM_WINDOWS + +MThread::MThread () +{ + tid = 0; +} + +MThread::~MThread () +{ + /* Kill thread if this is not the current thread */ + if (tid && (pthread_self () != tid)) + { + pthread_cancel (tid); + pthread_join (tid, NULL); + } +} + +static void *thread_start_routine (void *arg) +{ + MThread *newthr = (MThread *)arg; + pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL); + newthr->routine (newthr->arg); + return NULL; +} + +MThread *MThread::Start (void (*routine) (void *arg), void *arg) +{ + MThread *newthr = new MThread (); + newthr->routine = routine; + newthr->arg = arg; + int rc = pthread_create (&newthr->tid, NULL, thread_start_routine, newthr); + if (rc) + { + newthr->DecRef (); + return NULL; + } + + return newthr; +} + +pthread_mutexattr_t MMutex::attr; +int MMutex::attr_refcount = 0; + +MMutex::MMutex () +{ + if (!attr_refcount++) + { + pthread_mutexattr_init (&attr); + pthread_mutexattr_settype (&attr, MANGOS_PTHREAD_MUTEX_RECURSIVE); + } + + pthread_mutex_init (&mutex, &attr); +} + +MMutex::~MMutex () +{ + pthread_mutex_destroy (&mutex); + if (!--attr_refcount) + pthread_mutexattr_destroy (&attr); +} + +bool MMutex::Lock () +{ + return (pthread_mutex_lock (&mutex) == 0); +} + +bool MMutex::TryLock () +{ + return (pthread_mutex_trylock (&mutex) == 0); +} + +void MMutex::Unlock () +{ + pthread_mutex_unlock (&mutex); +} + +MMutex *MMutex::Create () +{ + return new MMutex (); +} + +#else //windows + +MThread::MThread() +{ + th = NULL; +} + +MThread::~MThread () +{ + /* Kill thread if this is not current thread */ + if (th && (GetCurrentThreadId () != id)) + { + TerminateThread (th, 0); + WaitForSingleObject (th, INFINITE); + CloseHandle (th); + } +} + +bool MThread::SetPriority (ThreadPriority prio) +{ + int p; + switch (prio) + { + case IDLE: p = THREAD_PRIORITY_IDLE; break; + case LOWER: p = THREAD_PRIORITY_LOWEST; break; + case LOW: p = THREAD_PRIORITY_BELOW_NORMAL; break; + case NORMAL: p = THREAD_PRIORITY_NORMAL; break; + case HIGH: p = THREAD_PRIORITY_ABOVE_NORMAL; break; + case HIGHER: p = THREAD_PRIORITY_HIGHEST; break; + case REALTIME: p = THREAD_PRIORITY_TIME_CRITICAL; break; + default: p = THREAD_PRIORITY_NORMAL; break; + } + return SetThreadPriority (th, p); +} + +static DWORD WINAPI thread_start_routine (void *arg) +//static void thread_start_routine (void *arg) +{ + MThread *newthr = (MThread *)arg; + newthr->id = GetCurrentThreadId (); + newthr->routine (newthr->arg); + return 0; +} + +MThread *MThread::Start (void (*routine) (void *arg), void *arg) +{ + DWORD dwtid; + MThread *newthr = new MThread (); + newthr->routine = routine; + newthr->arg = arg; + newthr->th = CreateThread (NULL, WIN32_THREAD_STACK_SIZE, thread_start_routine, newthr, 0, &dwtid); + //newthr->th = (HANDLE)_beginthread(thread_start_routine, 0, newthr); + if (!newthr->th) + { + newthr->DecRef (); + return NULL; + } + return newthr; +} + +MMutex::MMutex () +{ + sem = CreateMutex (NULL, FALSE, NULL); +} + +MMutex::~MMutex () +{ + CloseHandle (sem); +} + +bool MMutex::Lock () +{ + return (WaitForSingleObject (sem, INFINITE) != WAIT_FAILED); +} + +bool MMutex::TryLock () +{ + DWORD state = WaitForSingleObject (sem, 0); + return (state == WAIT_OBJECT_0) && (state != WAIT_ABANDONED); +} + +void MMutex::Unlock () +{ + ReleaseMutex (sem); +} + +MMutex *MMutex::Create () +{ + MMutex *mutex = new MMutex (); + if (!mutex->sem) + { + mutex->DecRef (); + return NULL; + } + return mutex; +} +#endif diff --git a/src/shared/Mthread.h b/src/shared/Mthread.h new file mode 100644 index 00000000000..1afee4acd1b --- /dev/null +++ b/src/shared/Mthread.h @@ -0,0 +1,62 @@ +#ifndef MTHREAD_H +#define MTHREAD_H + +#include "Base.h" +#ifndef WIN32 +#include <pthread.h> +#else +#include <windows.h> +//#include "Process.h" +#define WIN32_THREAD_STACK_SIZE 0x10000 +#endif + +enum ThreadPriority +{ + IDLE, + LOWER, + LOW, + NORMAL, + HIGH, + HIGHER, + REALTIME +}; + +class MThread: public Base +{ + public: + static MThread *Start (void (*routine) (void *arg), void *arg); + MThread (); + ~MThread (); + bool SetPriority (ThreadPriority prio); + + void (*routine) (void *arg); + void *arg; + + #ifdef WIN32 + HANDLE th; + ULONG id; + #else + pthread_t tid; + #endif + +}; + +class MMutex : public Base +{ + public: + + #ifdef WIN32 + HANDLE sem; + #else + pthread_mutex_t mutex; + static pthread_mutexattr_t attr; + static int attr_refcount; + #endif + static MMutex *Create (); + MMutex (); + virtual ~MMutex (); + virtual bool Lock (); + virtual bool TryLock (); + virtual void Unlock (); +}; +#endif // MTHREAD_H diff --git a/src/shared/PacketLog.cpp b/src/shared/PacketLog.cpp new file mode 100644 index 00000000000..835e174eb54 --- /dev/null +++ b/src/shared/PacketLog.cpp @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.h" +#include "PacketLog.h" +#include "Config/ConfigEnv.h" +#include "Policies/SingletonImpl.h" + +#include <ctype.h> + +INSTANTIATE_SINGLETON_1( PacketLog ); + +PacketLog::PacketLog() +{ + + if (sConfig.GetBoolDefault("LogRealm", false)) + { + FILE *pFile = fopen("realm.log", "w+"); + fclose(pFile); + } + + if (sConfig.GetBoolDefault("LogWorld", false)) + { + FILE *pFile = fopen("world.log", "w+"); + fclose(pFile); + } +} + +PacketLog::~PacketLog() +{ +} + +char PacketLog::makehexchar(int i) +{ + return (i<=9) ? '0'+i : 'A'+(i-10); +} + +int PacketLog::hextoint(char c) +{ + c = toupper(c); + return (c > '9' ? c - 'A' + 10 : c - '0'); +} + +void PacketLog::HexDump(const unsigned char* data, size_t length, const char* file) +{ + FILE *pFile; + pFile = fopen(file, "a"); + + const int char_offset = 16*3 + 2; + const int line_size = 16*3 + 16 + 3; + char line[line_size+1]; + + fprintf(pFile,"OFFSET 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F | 0123456789ABCDEF\n"); + fprintf(pFile,"--------------------------------------------------------------------------\n"); + + line[char_offset - 1] = ' '; + line[char_offset - 2] = ' '; + + for (size_t i=0; i<length; ) + { + int bi=0; + int ci=0; + + int start_i = i; + + for (int line_i=0; i < length && line_i < 16; i++, line_i++) + { + line[bi++] = makehexchar(*data>>4); + line[bi++] = makehexchar(*data & 0x0f); + line[bi++] = ' '; + line[char_offset+(ci++)]=(isprint(*data) ? *data : '.'); + ++data; + } + + while (bi<16*3) + { + line[bi++]=' '; + } + + line[char_offset+(ci++)]='\n'; + line[char_offset+ci]=0; + + fprintf(pFile,"%06X %s", start_i, line); + } + fprintf(pFile, "\n\n"); + fclose(pFile); +} + +void PacketLog::HexDump(const char *data, size_t length, const char* file) +{ + HexDump((unsigned char *)data, length, file); +} + +void PacketLog::HexDumpStr(const char *msg, const char *data, size_t len, const char* file) +{ + FILE *pFile; + pFile = fopen(file, "a"); + fprintf(pFile,"%s\n", msg); + fclose(pFile); + + HexDump(data, len, file); +} + +void PacketLog::RealmHexDump(RealmPacket* data, uint32 socket, bool direction) +{ + if (!sConfig.GetBoolDefault("LogRealm", false)) + return; + + FILE *pFile; + pFile = fopen("realm.log", "a"); + + uint16 len = data->size() + 2; + uint8 opcode = data->GetOpcode(); + if (direction) + fprintf(pFile, "SERVER:\nSOCKET: %d\nLENGTH: %d\nOPCODE: %.2X\nDATA:\n", socket, len, opcode); + else + fprintf(pFile, "CLIENT:\nSOCKET: %d\nLENGTH: %d\nOPCODE: %.2X\nDATA:\n", socket, len, opcode); + + fclose(pFile); + HexDump((char *)data->contents(), data->size(), "realm.log"); + +} + +void PacketLog::WorldHexDump(WorldPacket* data, uint32 socket, bool direction) +{ + if (!sConfig.GetBoolDefault("LogWorld", false)) + return; + + FILE *pFile; + pFile = fopen("world.log", "a"); + + uint16 len = data->size(); + uint16 opcode = data->GetOpcode(); + if (direction) + fprintf(pFile, "SERVER:\nSOCKET: %d\nLENGTH: %d\nOPCODE: %.4X\nDATA:\n", socket, len, opcode); + else + fprintf(pFile, "CLIENT:\nSOCKET: %d\nLENGTH: %d\nOPCODE: %.4X\nDATA:\n", socket, len, opcode); + + fclose(pFile); + HexDump((char *)data->contents(), data->size(), "world.log"); + +} diff --git a/src/shared/PacketLog.h b/src/shared/PacketLog.h new file mode 100644 index 00000000000..e2f4d2423d5 --- /dev/null +++ b/src/shared/PacketLog.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOSSERVER_PACKETLOG_H +#define MANGOSSERVER_PACKETLOG_H + +#include "Common.h" +#include "Policies/Singleton.h" +#include "RealmPacket.h" +#include "WorldPacket.h" + +class PacketLog +{ + public: + PacketLog(); + ~PacketLog(); + + int hextoint(char c); + char makehexchar(int i); + + void HexDump(const unsigned char* data, size_t length, const char* file); + void HexDump(const char *data, size_t length, const char* file); + void HexDumpStr(const char *msg, const char *data, size_t len, const char* file); + + void RealmHexDump(RealmPacket * data, uint32 socket, bool direction); + + void WorldHexDump(WorldPacket * data, uint32 socket, bool direction); +}; + +#define sPacketLog MaNGOS::Singleton<PacketLog>::Instance() +#endif diff --git a/src/shared/ProgressBar.cpp b/src/shared/ProgressBar.cpp new file mode 100644 index 00000000000..b23add06262 --- /dev/null +++ b/src/shared/ProgressBar.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ProgressBar.h" + +char const* const barGoLink::empty = " "; +#ifdef _WIN32 +char const* const barGoLink::full = "\x3D"; +#else +char const* const barGoLink::full = "*"; +#endif + +barGoLink::~barGoLink() +{ + printf( "\n" ); + fflush(stdout); +} + +barGoLink::barGoLink( int row_count ) +{ + rec_no = 0; + rec_pos = 0; + indic_len = 50; + num_rec = row_count; + #ifdef _WIN32 + printf( "\x3D" ); + #else + printf( "[" ); + #endif + for ( int i = 0; i < indic_len; i++ ) printf( empty ); + #ifdef _WIN32 + printf( "\x3D 0%%\r\x3D" ); + #else + printf( "] 0%%\r[" ); + #endif + fflush(stdout); +} + +void barGoLink::step( void ) +{ + int i, n; + + if ( num_rec == 0 ) return; + ++rec_no; + n = rec_no * indic_len / num_rec; + if ( n != rec_pos ) + { + #ifdef _WIN32 + printf( "\r\x3D" ); + #else + printf( "\r[" ); + #endif + for ( i = 0; i < n; i++ ) printf( full ); + for ( ; i < indic_len; i++ ) printf( empty ); + float percent = (((float)n/(float)indic_len)*100); + #ifdef _WIN32 + printf( "\x3D %i%% \r\x3D", (int)percent); + #else + printf( "] %i%% \r[", (int)percent); + #endif + fflush(stdout); + + rec_pos = n; + } +} diff --git a/src/shared/ProgressBar.h b/src/shared/ProgressBar.h new file mode 100644 index 00000000000..3ee5963070a --- /dev/null +++ b/src/shared/ProgressBar.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef MANGOSSERVER_PROGRESSBAR_H +#define MANGOSSERVER_PROGRESSBAR_H + +#include <stdio.h> +#include "Platform/Define.h" + +class MANGOS_DLL_SPEC barGoLink +{ + static char const * const empty; + static char const * const full; + + int rec_no; + int rec_pos; + int num_rec; + int indic_len; + + public: + + void step( void ); + barGoLink( int ); + ~barGoLink(); +}; +#endif diff --git a/src/shared/ServiceWin32.cpp b/src/shared/ServiceWin32.cpp new file mode 100644 index 00000000000..d95cfe90291 --- /dev/null +++ b/src/shared/ServiceWin32.cpp @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef WIN32 + +#include "Common.h" +#include "Log.h" +#include <cstring> +#include <windows.h> +#include <winsvc.h> + +#if !defined(WINADVAPI) +#if !defined(_ADVAPI32_) +#define WINADVAPI DECLSPEC_IMPORT +#else +#define WINADVAPI +#endif +#endif + +extern int main(int argc, char ** argv); +extern char serviceLongName[]; +extern char serviceName[]; +extern char serviceDescription[]; + +extern int m_ServiceStatus; + +SERVICE_STATUS serviceStatus; + +SERVICE_STATUS_HANDLE serviceStatusHandle = 0; + +typedef WINADVAPI BOOL (WINAPI *CSD_T)(SC_HANDLE, DWORD, LPCVOID); + +bool WinServiceInstall() +{ + CSD_T ChangeService_Config2; + HMODULE advapi32; + SC_HANDLE serviceControlManager = OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE); + + if (serviceControlManager) + { + char path[_MAX_PATH + 10]; + if (GetModuleFileName( 0, path, sizeof(path)/sizeof(path[0]) ) > 0) + { + SC_HANDLE service; + std::strcat(path, " --service"); + service = CreateService(serviceControlManager, + serviceName, // name of service + serviceLongName, // service name to display + SERVICE_ALL_ACCESS, // desired access + // service type + SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS, + SERVICE_AUTO_START, // start type + SERVICE_ERROR_IGNORE, // error control type + path, // service's binary + 0, // no load ordering group + 0, // no tag identifier + 0, // no dependencies + 0, // LocalSystem account + 0); // no password + if (service) + { + advapi32 = GetModuleHandle("ADVAPI32.DLL"); + if(!advapi32) + { + CloseServiceHandle(service); + CloseServiceHandle(serviceControlManager); + return false; + } + + ChangeService_Config2 = (CSD_T) GetProcAddress(advapi32, "ChangeServiceConfig2A"); + if (!ChangeService_Config2) + { + CloseServiceHandle(service); + CloseServiceHandle(serviceControlManager); + return false; + } + + SERVICE_DESCRIPTION sdBuf; + sdBuf.lpDescription = serviceDescription; + ChangeService_Config2( + service, // handle to service + SERVICE_CONFIG_DESCRIPTION, // change: description + &sdBuf); // new data + + SC_ACTION _action[1]; + _action[0].Type = SC_ACTION_RESTART; + _action[0].Delay = 10000; + SERVICE_FAILURE_ACTIONS sfa; + ZeroMemory(&sfa, sizeof(SERVICE_FAILURE_ACTIONS)); + sfa.lpsaActions = _action; + sfa.cActions = 1; + sfa.dwResetPeriod =INFINITE; + ChangeService_Config2( + service, // handle to service + SERVICE_CONFIG_FAILURE_ACTIONS, // information level + &sfa); // new data + + CloseServiceHandle(service); + + } + } + CloseServiceHandle(serviceControlManager); + } + return true; +} + +bool WinServiceUninstall() +{ + SC_HANDLE serviceControlManager = OpenSCManager(0, 0, SC_MANAGER_CONNECT); + + if (serviceControlManager) + { + SC_HANDLE service = OpenService(serviceControlManager, + serviceName, SERVICE_QUERY_STATUS | DELETE); + if (service) + { + SERVICE_STATUS serviceStatus; + if (QueryServiceStatus(service, &serviceStatus)) + { + if (serviceStatus.dwCurrentState == SERVICE_STOPPED) + DeleteService(service); + } + CloseServiceHandle(service); + } + + CloseServiceHandle(serviceControlManager); + } + return true; +} + +void WINAPI ServiceControlHandler(DWORD controlCode) +{ + switch (controlCode) + { + case SERVICE_CONTROL_INTERROGATE: + break; + + case SERVICE_CONTROL_SHUTDOWN: + case SERVICE_CONTROL_STOP: + serviceStatus.dwCurrentState = SERVICE_STOP_PENDING; + SetServiceStatus(serviceStatusHandle, &serviceStatus); + + m_ServiceStatus = 0; + return; + + case SERVICE_CONTROL_PAUSE: + m_ServiceStatus = 2; + serviceStatus.dwCurrentState = SERVICE_PAUSED; + SetServiceStatus(serviceStatusHandle, &serviceStatus); + break; + + case SERVICE_CONTROL_CONTINUE: + serviceStatus.dwCurrentState = SERVICE_RUNNING; + SetServiceStatus(serviceStatusHandle, &serviceStatus); + m_ServiceStatus = 1; + break; + + default: + if ( controlCode >= 128 && controlCode <= 255 ) + // user defined control code + break; + else + // unrecognized control code + break; + } + + SetServiceStatus(serviceStatusHandle, &serviceStatus); +} + +void WINAPI ServiceMain(DWORD argc, char *argv[]) +{ + // initialise service status + serviceStatus.dwServiceType = SERVICE_WIN32; + serviceStatus.dwCurrentState = SERVICE_START_PENDING; + serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE; + serviceStatus.dwWin32ExitCode = NO_ERROR; + serviceStatus.dwServiceSpecificExitCode = NO_ERROR; + serviceStatus.dwCheckPoint = 0; + serviceStatus.dwWaitHint = 0; + + serviceStatusHandle = RegisterServiceCtrlHandler(serviceName, ServiceControlHandler); + + if ( serviceStatusHandle ) + { + char path[_MAX_PATH + 1]; + unsigned int i, last_slash = 0; + + GetModuleFileName(0, path, sizeof(path)/sizeof(path[0])); + + for (i = 0; i < std::strlen(path); i++) + { + if (path[i] == '\\') last_slash = i; + } + + path[last_slash] = 0; + + // service is starting + serviceStatus.dwCurrentState = SERVICE_START_PENDING; + SetServiceStatus(serviceStatusHandle, &serviceStatus); + + // do initialisation here + SetCurrentDirectory(path); + + // running + serviceStatus.dwControlsAccepted |= (SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN); + serviceStatus.dwCurrentState = SERVICE_RUNNING; + SetServiceStatus( serviceStatusHandle, &serviceStatus ); + + //////////////////////// + // service main cycle // + //////////////////////// + + m_ServiceStatus = 1; + argc = 1; + main(argc , argv); + + // service was stopped + serviceStatus.dwCurrentState = SERVICE_STOP_PENDING; + SetServiceStatus(serviceStatusHandle, &serviceStatus); + + // do cleanup here + + // service is now stopped + serviceStatus.dwControlsAccepted &= ~(SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN); + serviceStatus.dwCurrentState = SERVICE_STOPPED; + SetServiceStatus(serviceStatusHandle, &serviceStatus); + } +} + +bool WinServiceRun() +{ + SERVICE_TABLE_ENTRY serviceTable[] = + { + { serviceName, ServiceMain }, + { 0, 0 } + }; + + if (!StartServiceCtrlDispatcher(serviceTable)) + { + sLog.outError("StartService Failed. Error [%u]", ::GetLastError()); + return false; + } + return true; +} +#endif diff --git a/src/shared/ServiceWin32.h b/src/shared/ServiceWin32.h new file mode 100644 index 00000000000..351e733d051 --- /dev/null +++ b/src/shared/ServiceWin32.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef WIN32 +#ifndef _WIN32_SERVICE_ +#define _WIN32_SERVICE_ + +bool WinServiceInstall(); +bool WinServiceUninstall(); +bool WinServiceRun(); + +#endif // _WIN32_SERVICE_ +#endif // WIN32 diff --git a/src/shared/SystemConfig.h b/src/shared/SystemConfig.h new file mode 100644 index 00000000000..c1fd6e66a99 --- /dev/null +++ b/src/shared/SystemConfig.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2005,2006 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +// THIS FILE IS DEPRECATED + +#ifndef MANGOS_SYSTEMCONFIG_H +#define MANGOS_SYSTEMCONFIG_H + +#include "Platform/Define.h" + +// THIS IS TEMP :) +#define _FULLVERSION "Trinity" + +#define DEFAULT_PLAYER_LIMIT 100 +#define DEFAULT_WORLDSERVER_PORT 8085 //8129 +#define DEFAULT_REALMSERVER_PORT 3724 +#define DEFAULT_SOCKET_SELECT_TIME 10000 +#endif diff --git a/src/shared/Timer.h b/src/shared/Timer.h new file mode 100644 index 00000000000..f24d6f02ce0 --- /dev/null +++ b/src/shared/Timer.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_TIMER_H +#define MANGOS_TIMER_H + +#include "Platform/CompilerDefs.h" + +#if PLATFORM == PLATFORM_WINDOWS +# include <windows.h> +# include <mmsystem.h> +# include <time.h> +#else +# if defined(__APPLE_CC__) +# include <time.h> +# endif +# include <sys/time.h> +# include <sys/timeb.h> +#endif + +#if PLATFORM == PLATFORM_WINDOWS +inline uint32 getMSTime() { return GetTickCount(); } +#else +inline uint32 getMSTime() +{ + struct timeval tv; + struct timezone tz; + gettimeofday( &tv, &tz ); + return (tv.tv_sec * 1000) + (tv.tv_usec / 1000); +} +#endif + +inline uint32 getMSTimeDiff(uint32 oldMSTime, uint32 newMSTime) +{ + // getMSTime() have limited data range and this is case when it overflow in this tick + if (oldMSTime > newMSTime) + return (0xFFFFFFFF - oldMSTime) + newMSTime; + else + return newMSTime - oldMSTime; +} + +class IntervalTimer +{ + public: + IntervalTimer() : _interval(0), _current(0) {} + + void Update(time_t diff) { _current += diff; if(_current<0) _current=0;} + bool Passed() { return _current >= _interval; } + void Reset() { if(_current >= _interval) _current -= _interval; } + + void SetCurrent(time_t current) { _current = current; } + void SetInterval(time_t interval) { _interval = interval; } + time_t GetInterval() const { return _interval; } + time_t GetCurrent() const { return _current; } + + private: + time_t _interval; + time_t _current; +}; + +struct TimeTracker +{ + TimeTracker(time_t expiry) : i_expiryTime(expiry) {} + void Update(time_t diff) { i_expiryTime -= diff; } + bool Passed(void) const { return (i_expiryTime <= 0); } + void Reset(time_t interval) { i_expiryTime = interval; } + time_t GetExpiry(void) const { return i_expiryTime; } + time_t i_expiryTime; +}; + +struct TimeTrackerSmall +{ + TimeTrackerSmall(int32 expiry) : i_expiryTime(expiry) {} + void Update(int32 diff) { i_expiryTime -= diff; } + bool Passed(void) const { return (i_expiryTime <= 0); } + void Reset(int32 interval) { i_expiryTime = interval; } + int32 GetExpiry(void) const { return i_expiryTime; } + int32 i_expiryTime; +}; + +#endif diff --git a/src/shared/Util.cpp b/src/shared/Util.cpp new file mode 100644 index 00000000000..c5e8284534d --- /dev/null +++ b/src/shared/Util.cpp @@ -0,0 +1,425 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Util.h" + +#include "sockets/socket_include.h" +#include "utf8cpp/utf8.h" +#include "mersennetwister/MersenneTwister.h" +#include "zthread/ThreadLocal.h" + +typedef ZThread::ThreadLocal<MTRand> MTRandTSS; + +/* NOTE: Not sure if static initialization is ok for TSS objects , + * as I see zthread uses custom implementation of the TSS + * ,and in the consturctor there is no code ,so I suppose its ok + * If its not ok ,change it to use singleton. + */ +static MTRandTSS mtRand; + +int32 irand (int32 min, int32 max) +{ + return int32 (mtRand.get ().randInt (max - min)) + min; +} + +uint32 urand (uint32 min, uint32 max) +{ + return mtRand.get ().randInt (max - min) + min; +} + +int32 rand32 () +{ + return mtRand.get ().randInt (); +} + +double rand_norm(void) +{ + return mtRand.get ().randExc (); +} + +double rand_chance (void) +{ + return mtRand.get ().randExc (100.0); +} + +Tokens StrSplit(const std::string &src, const std::string &sep) +{ + Tokens r; + std::string s; + for (std::string::const_iterator i = src.begin(); i != src.end(); i++) + { + if (sep.find(*i) != std::string::npos) + { + if (s.length()) r.push_back(s); + s = ""; + } + else + { + s += *i; + } + } + if (s.length()) r.push_back(s); + return r; +} + +void stripLineInvisibleChars(std::string &str) +{ + static std::string invChars = " \t\7"; + + size_t wpos = 0; + + bool space = false; + for(size_t pos = 0; pos < str.size(); ++pos) + { + if(invChars.find(str[pos])!=std::string::npos) + { + if(!space) + { + str[wpos++] = ' '; + space = true; + } + } + else + { + if(wpos!=pos) + str[wpos++] = str[pos]; + else + ++wpos; + space = false; + } + } + + if(wpos < str.size()) + str.erase(wpos,str.size()); +} + +std::string secsToTimeString(uint32 timeInSecs, bool shortText, bool hoursOnly) +{ + uint32 secs = timeInSecs % MINUTE; + uint32 minutes = timeInSecs % HOUR / MINUTE; + uint32 hours = timeInSecs % DAY / HOUR; + uint32 days = timeInSecs / DAY; + + std::ostringstream ss; + if(days) + ss << days << (shortText ? "d" : " Day(s) "); + if(hours || hoursOnly) + ss << hours << (shortText ? "h" : " Hour(s) "); + if(!hoursOnly) + { + if(minutes) + ss << minutes << (shortText ? "m" : " Minute(s) "); + if(secs || (!days && !hours && !minutes) ) + ss << secs << (shortText ? "s" : " Second(s)."); + } + + return ss.str(); +} + +uint32 TimeStringToSecs(std::string timestring) +{ + uint32 secs = 0; + uint32 buffer = 0; + uint32 multiplier = 0; + + for(std::string::iterator itr = timestring.begin(); itr != timestring.end(); itr++ ) + { + if(isdigit(*itr)) + { + std::string str; //very complicated typecast char->const char*; is there no better way? + str += *itr; + const char* tmp = str.c_str(); + + buffer*=10; + buffer+=atoi(tmp); + } + else + { + switch(*itr) + { + case 'd': multiplier = DAY; break; + case 'h': multiplier = HOUR; break; + case 'm': multiplier = MINUTE; break; + case 's': multiplier = 1; break; + default : return 0; //bad format + } + buffer*=multiplier; + secs+=buffer; + buffer=0; + } + } + + return secs; +} + +std::string TimeToTimestampStr(time_t t) +{ + tm* aTm = localtime(&t); + // YYYY year + // MM month (2 digits 01-12) + // DD day (2 digits 01-31) + // HH hour (2 digits 00-23) + // MM minutes (2 digits 00-59) + // SS seconds (2 digits 00-59) + char buf[20]; + snprintf(buf,20,"%04d-%02d-%02d_%02d-%02d-%02d",aTm->tm_year+1900,aTm->tm_mon+1,aTm->tm_mday,aTm->tm_hour,aTm->tm_min,aTm->tm_sec); + return std::string(buf); +} + +/// Check if the string is a valid ip address representation +bool IsIPAddress(char const* ipaddress) +{ + if(!ipaddress) + return false; + + // Let the big boys do it. + // Drawback: all valid ip address formats are recognized e.g.: 12.23,121234,0xABCD) + return inet_addr(ipaddress) != INADDR_NONE; +} + +/// create PID file +uint32 CreatePIDFile(std::string filename) +{ + FILE * pid_file = fopen (filename.c_str(), "w" ); + if (pid_file == NULL) + return 0; + +#ifdef WIN32 + DWORD pid = GetCurrentProcessId(); +#else + pid_t pid = getpid(); +#endif + + fprintf(pid_file, "%d", pid ); + fclose(pid_file); + + return (uint32)pid; +} + +size_t utf8length(std::string& utf8str) +{ + try + { + return utf8::distance(utf8str.c_str(),utf8str.c_str()+utf8str.size()); + } + catch(std::exception) + { + utf8str = ""; + return 0; + } +} + +void utf8truncate(std::string& utf8str,size_t len) +{ + try + { + size_t wlen = utf8::distance(utf8str.c_str(),utf8str.c_str()+utf8str.size()); + if(wlen <= len) + return; + + std::wstring wstr; + wstr.resize(wlen); + utf8::utf8to16(utf8str.c_str(),utf8str.c_str()+utf8str.size(),&wstr[0]); + wstr.resize(len); + char* oend = utf8::utf16to8(wstr.c_str(),wstr.c_str()+wstr.size(),&utf8str[0]); + utf8str.resize(oend-(&utf8str[0])); // remove unused tail + } + catch(std::exception) + { + utf8str = ""; + } +} + +bool Utf8toWStr(char const* utf8str, size_t csize, wchar_t* wstr, size_t& wsize) +{ + try + { + size_t len = utf8::distance(utf8str,utf8str+csize); + if(len > wsize) + { + wsize = 0; + wstr = L""; + return false; + } + + wsize = len; + utf8::utf8to16(utf8str,utf8str+csize,wstr); + wstr[len] = L'\0'; + } + catch(std::exception) + { + wsize = 0; + wstr = L""; + return false; + } + + return true; +} + +bool Utf8toWStr(std::string utf8str, std::wstring& wstr) +{ + try + { + size_t len = utf8::distance(utf8str.c_str(),utf8str.c_str()+utf8str.size()); + wstr.resize(len); + + utf8::utf8to16(utf8str.c_str(),utf8str.c_str()+utf8str.size(),&wstr[0]); + } + catch(std::exception) + { + wstr = L""; + return false; + } + + return true; +} + +bool WStrToUtf8(wchar_t* wstr, size_t size, std::string& utf8str) +{ + try + { + std::string utf8str2; + utf8str2.resize(size*4); // allocate for most long case + + char* oend = utf8::utf16to8(wstr,wstr+size,&utf8str2[0]); + utf8str2.resize(oend-(&utf8str2[0])); // remove unused tail + utf8str = utf8str2; + } + catch(std::exception) + { + utf8str = ""; + return false; + } + + return true; +} + +bool WStrToUtf8(std::wstring wstr, std::string& utf8str) +{ + try + { + std::string utf8str2; + utf8str2.resize(wstr.size()*4); // allocate for most long case + + char* oend = utf8::utf16to8(wstr.c_str(),wstr.c_str()+wstr.size(),&utf8str2[0]); + utf8str2.resize(oend-(&utf8str2[0])); // remove unused tail + utf8str = utf8str2; + } + catch(std::exception) + { + utf8str = ""; + return false; + } + + return true; +} + +typedef wchar_t const* const* wstrlist; + +std::wstring GetMainPartOfName(std::wstring wname, uint32 declension) +{ + // supported only Cyrillic cases + if(wname.size() < 1 || !isCyrillicCharacter(wname[0]) || declension > 5) + return wname; + + // Important: end length must be <= MAX_INTERNAL_PLAYER_NAME-MAX_PLAYER_NAME (3 currently) + + static wchar_t const a_End[] = { wchar_t(1), wchar_t(0x0430),wchar_t(0x0000)}; + static wchar_t const o_End[] = { wchar_t(1), wchar_t(0x043E),wchar_t(0x0000)}; + static wchar_t const ya_End[] = { wchar_t(1), wchar_t(0x044F),wchar_t(0x0000)}; + static wchar_t const ie_End[] = { wchar_t(1), wchar_t(0x0435),wchar_t(0x0000)}; + static wchar_t const i_End[] = { wchar_t(1), wchar_t(0x0438),wchar_t(0x0000)}; + static wchar_t const yeru_End[] = { wchar_t(1), wchar_t(0x044B),wchar_t(0x0000)}; + static wchar_t const u_End[] = { wchar_t(1), wchar_t(0x0443),wchar_t(0x0000)}; + static wchar_t const yu_End[] = { wchar_t(1), wchar_t(0x044E),wchar_t(0x0000)}; + static wchar_t const oj_End[] = { wchar_t(2), wchar_t(0x043E),wchar_t(0x0439),wchar_t(0x0000)}; + static wchar_t const ie_j_End[] = { wchar_t(2), wchar_t(0x0435),wchar_t(0x0439),wchar_t(0x0000)}; + static wchar_t const io_j_End[] = { wchar_t(2), wchar_t(0x0451),wchar_t(0x0439),wchar_t(0x0000)}; + static wchar_t const o_m_End[] = { wchar_t(2), wchar_t(0x043E),wchar_t(0x043C),wchar_t(0x0000)}; + static wchar_t const io_m_End[] = { wchar_t(2), wchar_t(0x0451),wchar_t(0x043C),wchar_t(0x0000)}; + static wchar_t const ie_m_End[] = { wchar_t(2), wchar_t(0x0435),wchar_t(0x043C),wchar_t(0x0000)}; + static wchar_t const soft_End[] = { wchar_t(1), wchar_t(0x044C),wchar_t(0x0000)}; + static wchar_t const j_End[] = { wchar_t(1), wchar_t(0x0439),wchar_t(0x0000)}; + + static wchar_t const* const dropEnds[6][8] = { + { &a_End[1], &o_End[1], &ya_End[1], &ie_End[1], &soft_End[1], &j_End[1], NULL, NULL }, + { &a_End[1], &ya_End[1], &yeru_End[1], &i_End[1], NULL, NULL, NULL, NULL }, + { &ie_End[1], &u_End[1], &yu_End[1], &i_End[1], NULL, NULL, NULL, NULL }, + { &u_End[1], &yu_End[1], &o_End[1], &ie_End[1], &soft_End[1], &ya_End[1], &a_End[1], NULL }, + { &oj_End[1], &io_j_End[1], &ie_j_End[1], &o_m_End[1], &io_m_End[1], &ie_m_End[1], &yu_End[1], NULL }, + { &ie_End[1], &i_End[1], NULL, NULL, NULL, NULL, NULL, NULL } + }; + + for(wchar_t const * const* itr = &dropEnds[declension][0]; *itr; ++itr) + { + size_t len = size_t((*itr)[-1]); // get length from string size field + + if(wname.substr(wname.size()-len,len)==*itr) + return wname.substr(0,wname.size()-len); + } + + return wname; +} + +bool utf8ToConsole(std::string utf8str, std::string& conStr) +{ +#if PLATFORM == PLATFORM_WINDOWS + std::wstring wstr; + if(!Utf8toWStr(utf8str,wstr)) + return false; + + conStr.resize(wstr.size()); + CharToOemBuffW(&wstr[0],&conStr[0],wstr.size()); +#else + // not implemented yet + conStr = utf8str; +#endif + + return true; +} + +bool consoleToUtf8(std::string conStr,std::string& utf8str) +{ +#if PLATFORM == PLATFORM_WINDOWS + std::wstring wstr; + wstr.resize(conStr.size()); + OemToCharBuffW(&conStr[0],&wstr[0],conStr.size()); + + return WStrToUtf8(wstr,utf8str); +#else + // not implemented yet + utf8str = conStr; + return true; +#endif +} + +bool Utf8FitTo(std::string str, std::wstring search) +{ + std::wstring temp; + + if(!Utf8toWStr(str,temp)) + return false; + + // converting to lower case + wstrToLower( temp ); + + if(temp.find(search) == std::wstring::npos) + return false; + + return true; +} diff --git a/src/shared/Util.h b/src/shared/Util.h new file mode 100644 index 00000000000..1119414dd1e --- /dev/null +++ b/src/shared/Util.h @@ -0,0 +1,306 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _UTIL_H +#define _UTIL_H + +#include "Common.h" + +#include <string> +#include <vector> + +typedef std::vector<std::string> Tokens; + +Tokens StrSplit(const std::string &src, const std::string &sep); + +void stripLineInvisibleChars(std::string &src); + +std::string secsToTimeString(uint32 timeInSecs, bool shortText = false, bool hoursOnly = false); +uint32 TimeStringToSecs(std::string timestring); +std::string TimeToTimestampStr(time_t t); + +inline uint32 secsToTimeBitFields(time_t secs) +{ + tm* lt = localtime(&secs); + return (lt->tm_year - 100) << 24 | lt->tm_mon << 20 | (lt->tm_mday - 1) << 14 | lt->tm_wday << 11 | lt->tm_hour << 6 | lt->tm_min; +} + +/* Return a random number in the range min..max; (max-min) must be smaller than 32768. */ +MANGOS_DLL_SPEC int32 irand(int32 min, int32 max); + +/* Return a random number in the range min..max (inclusive). For reliable results, the difference +* between max and min should be less than RAND32_MAX. */ +MANGOS_DLL_SPEC uint32 urand(uint32 min, uint32 max); + +/* Return a random number in the range 0 .. RAND32_MAX. */ +MANGOS_DLL_SPEC int32 rand32(); + +/* Return a random double from 0.0 to 1.0 (exclusive). Floats support only 7 valid decimal digits. + * A double supports up to 15 valid decimal digits and is used internally (RAND32_MAX has 10 digits). + * With an FPU, there is usually no difference in performance between float and double. */ +MANGOS_DLL_SPEC double rand_norm(void); + +/* Return a random double from 0.0 to 99.9999999999999. Floats support only 7 valid decimal digits. + * A double supports up to 15 valid decimal digits and is used internaly (RAND32_MAX has 10 digits). + * With an FPU, there is usually no difference in performance between float and double. */ +MANGOS_DLL_SPEC double rand_chance(void); + +/* Return true if a random roll fits in the specified chance (range 0-100). */ +inline bool roll_chance_f(float chance) +{ + return chance > rand_chance(); +} + +/* Return true if a random roll fits in the specified chance (range 0-100). */ +inline bool roll_chance_i(int chance) +{ + return chance > irand(0, 99); +} + +inline void ApplyModUInt32Var(uint32& var, int32 val, bool apply) +{ + int32 cur = var; + cur += (apply ? val : -val); + if(cur < 0) + cur = 0; + var = cur; +} + +inline void ApplyModFloatVar(float& var, float val, bool apply) +{ + var += (apply ? val : -val); + if(var < 0) + var = 0; +} + +inline void ApplyPercentModFloatVar(float& var, float val, bool apply) +{ + if (!apply && val == -100.0f) + val = -99.99f; + var *= (apply?(100.0f+val)/100.0f : 100.0f / (100.0f+val)); +} + +bool Utf8toWStr(std::string utf8str, std::wstring& wstr); +// in wsize==max size of buffer, out wsize==real string size +bool Utf8toWStr(char const* utf8str, size_t csize, wchar_t* wstr, size_t& wsize); +inline bool Utf8toWStr(std::string utf8str, wchar_t* wstr, size_t& wsize) +{ + return Utf8toWStr(utf8str.c_str(), utf8str.size(), wstr, wsize); +} + +bool WStrToUtf8(std::wstring wstr, std::string& utf8str); +// size==real string size +bool WStrToUtf8(wchar_t* wstr, size_t size, std::string& utf8str); + +size_t utf8length(std::string& utf8str); // set string to "" if invalid utf8 sequence +void utf8truncate(std::string& utf8str,size_t len); + +inline bool isBasicLatinCharacter(wchar_t wchar) +{ + if(wchar >= L'a' && wchar <= L'z') // LATIN SMALL LETTER A - LATIN SMALL LETTER Z + return true; + if(wchar >= L'A' && wchar <= L'Z') // LATIN CAPITAL LETTER A - LATIN CAPITAL LETTER Z + return true; + return false; +} + +inline bool isExtendedLatinCharacter(wchar_t wchar) +{ + if(isBasicLatinCharacter(wchar)) + return true; + if(wchar >= 0x00C0 && wchar <= 0x00D6) // LATIN CAPITAL LETTER A WITH GRAVE - LATIN CAPITAL LETTER O WITH DIAERESIS + return true; + if(wchar >= 0x00D8 && wchar <= 0x00DF) // LATIN CAPITAL LETTER O WITH STROKE - LATIN CAPITAL LETTER THORN + return true; + if(wchar == 0x00DF) // LATIN SMALL LETTER SHARP S + return true; + if(wchar >= 0x00E0 && wchar <= 0x00F6) // LATIN SMALL LETTER A WITH GRAVE - LATIN SMALL LETTER O WITH DIAERESIS + return true; + if(wchar >= 0x00F8 && wchar <= 0x00FE) // LATIN SMALL LETTER O WITH STROKE - LATIN SMALL LETTER THORN + return true; + if(wchar >= 0x0100 && wchar <= 0x012F) // LATIN CAPITAL LETTER A WITH MACRON - LATIN SMALL LETTER I WITH OGONEK + return true; + if(wchar == 0x1E9E) // LATIN CAPITAL LETTER SHARP S + return true; + return false; +} + +inline bool isCyrillicCharacter(wchar_t wchar) +{ + if(wchar >= 0x0410 && wchar <= 0x044F) // CYRILLIC CAPITAL LETTER A - CYRILLIC SMALL LETTER YA + return true; + if(wchar == 0x0401 || wchar == 0x0451) // CYRILLIC CAPITAL LETTER IO, CYRILLIC SMALL LETTER IO + return true; + return false; +} + +inline bool isEastAsianCharacter(wchar_t wchar) +{ + if(wchar >= 0x1100 && wchar <= 0x11F9) // Hangul Jamo + return true; + if(wchar >= 0x3041 && wchar <= 0x30FF) // Hiragana + Katakana + return true; + if(wchar >= 0x3131 && wchar <= 0x318E) // Hangul Compatibility Jamo + return true; + if(wchar >= 0x31F0 && wchar <= 0x31FF) // Katakana Phonetic Ext. + return true; + if(wchar >= 0x3400 && wchar <= 0x4DB5) // CJK Ideographs Ext. A + return true; + if(wchar >= 0x4E00 && wchar <= 0x9FC3) // Unified CJK Ideographs + return true; + if(wchar >= 0xAC00 && wchar <= 0xD7A3) // Hangul Syllables + return true; + if(wchar >= 0xFF01 && wchar <= 0xFFEE) // Halfwidth forms + return true; + return false; +} + +inline bool isNumericOrSpace(wchar_t wchar) +{ + return (wchar >= L'0' && wchar <=L'9') || wchar == L' '; +} + +inline bool isBasicLatinString(std::wstring wstr, bool numericOrSpace) +{ + for(size_t i = 0; i < wstr.size(); ++i) + if(!isBasicLatinCharacter(wstr[i]) && (!numericOrSpace || !isNumericOrSpace(wstr[i]))) + return false; + return true; +} + +inline bool isExtendedLatinString(std::wstring wstr, bool numericOrSpace) +{ + for(size_t i = 0; i < wstr.size(); ++i) + if(!isExtendedLatinCharacter(wstr[i]) && (!numericOrSpace || !isNumericOrSpace(wstr[i]))) + return false; + return true; +} + +inline bool isCyrillicString(std::wstring wstr, bool numericOrSpace) +{ + for(size_t i = 0; i < wstr.size(); ++i) + if(!isCyrillicCharacter(wstr[i]) && (!numericOrSpace || !isNumericOrSpace(wstr[i]))) + return false; + return true; +} + +inline bool isEastAsianString(std::wstring wstr, bool numericOrSpace) +{ + for(size_t i = 0; i < wstr.size(); ++i) + if(!isEastAsianCharacter(wstr[i]) && (!numericOrSpace || !isNumericOrSpace(wstr[i]))) + return false; + return true; +} + +inline wchar_t wcharToUpper(wchar_t wchar) +{ + if(wchar >= L'a' && wchar <= L'z') // LATIN SMALL LETTER A - LATIN SMALL LETTER Z + return wchar_t(uint16(wchar)-0x0020); + if(wchar == 0x00DF) // LATIN SMALL LETTER SHARP S + return wchar_t(0x1E9E); + if(wchar >= 0x00E0 && wchar <= 0x00F6) // LATIN SMALL LETTER A WITH GRAVE - LATIN SMALL LETTER O WITH DIAERESIS + return wchar_t(uint16(wchar)-0x0020); + if(wchar >= 0x00F8 && wchar <= 0x00FE) // LATIN SMALL LETTER O WITH STROKE - LATIN SMALL LETTER THORN + return wchar_t(uint16(wchar)-0x0020); + if(wchar >= 0x0101 && wchar <= 0x012F) // LATIN SMALL LETTER A WITH MACRON - LATIN SMALL LETTER I WITH OGONEK (only %2=1) + { + if(wchar % 2 == 1) + return wchar_t(uint16(wchar)-0x0001); + } + if(wchar >= 0x0430 && wchar <= 0x044F) // CYRILLIC SMALL LETTER A - CYRILLIC SMALL LETTER YA + return wchar_t(uint16(wchar)-0x0020); + if(wchar == 0x0451) // CYRILLIC SMALL LETTER IO + return wchar_t(0x0401); + + return wchar; +} + +inline wchar_t wcharToUpperOnlyLatin(wchar_t wchar) +{ + return isBasicLatinCharacter(wchar) ? wcharToUpper(wchar) : wchar; +} + +inline wchar_t wcharToLower(wchar_t wchar) +{ + if(wchar >= L'A' && wchar <= L'Z') // LATIN CAPITAL LETTER A - LATIN CAPITAL LETTER Z + return wchar_t(uint16(wchar)+0x0020); + if(wchar >= 0x00C0 && wchar <= 0x00D6) // LATIN CAPITAL LETTER A WITH GRAVE - LATIN CAPITAL LETTER O WITH DIAERESIS + return wchar_t(uint16(wchar)+0x0020); + if(wchar >= 0x00D8 && wchar <= 0x00DE) // LATIN CAPITAL LETTER O WITH STROKE - LATIN CAPITAL LETTER THORN + return wchar_t(uint16(wchar)+0x0020); + if(wchar >= 0x0100 && wchar <= 0x012E) // LATIN CAPITAL LETTER A WITH MACRON - LATIN CAPITAL LETTER I WITH OGONEK (only %2=0) + { + if(wchar % 2 == 0) + return wchar_t(uint16(wchar)+0x0001); + } + if(wchar == 0x1E9E) // LATIN CAPITAL LETTER SHARP S + return wchar_t(0x00DF); + if(wchar == 0x0401) // CYRILLIC CAPITAL LETTER IO + return wchar_t(0x0451); + if(wchar >= 0x0410 && wchar <= 0x042F) // CYRILLIC CAPITAL LETTER A - CYRILLIC CAPITAL LETTER YA + return wchar_t(uint16(wchar)+0x0020); + + return wchar; +} + +inline void wstrToUpper(std::wstring& str) +{ + std::transform( str.begin(), str.end(), str.begin(), wcharToUpper ); +} + +inline void wstrToLower(std::wstring& str) +{ + std::transform( str.begin(), str.end(), str.begin(), wcharToLower ); +} + +std::wstring GetMainPartOfName(std::wstring wname, uint32 declension); + +bool utf8ToConsole(std::string utf8str, std::string& conStr); +bool consoleToUtf8(std::string conStr,std::string& utf8str); +bool Utf8FitTo(std::string str, std::wstring search); + +#if PLATFORM == PLATFORM_WINDOWS +#define UTF8PRINTF(OUT,FRM,RESERR) \ +{ \ + char temp_buf[6000]; \ + va_list ap; \ + va_start(ap, FRM); \ + size_t temp_len = vsnprintf(temp_buf,6000,FRM,ap); \ + va_end(ap); \ + \ + wchar_t wtemp_buf[6000]; \ + size_t wtemp_len = 6000-1; \ + if(!Utf8toWStr(temp_buf,temp_len,wtemp_buf,wtemp_len)) \ + return RESERR; \ + CharToOemBuffW(&wtemp_buf[0],&temp_buf[0],wtemp_len+1);\ + fprintf(OUT,temp_buf); \ +} +#else +#define UTF8PRINTF(OUT,FRM,RESERR) \ +{ \ + va_list ap; \ + va_start(ap, FRM); \ + vfprintf(OUT, FRM, ap ); \ + va_end(ap); \ +} +#endif + +bool IsIPAddress(char const* ipaddress); +uint32 CreatePIDFile(std::string filename); + +#endif diff --git a/src/shared/WheatyExceptionReport.cpp b/src/shared/WheatyExceptionReport.cpp new file mode 100644 index 00000000000..ced12f96d9c --- /dev/null +++ b/src/shared/WheatyExceptionReport.cpp @@ -0,0 +1,964 @@ +//========================================== +// Matt Pietrek +// MSDN Magazine, 2002 +// FILE: WheatyExceptionReport.CPP +//========================================== +#define WIN32_LEAN_AND_MEAN +#pragma warning(disable:4996) +#pragma warning(disable:4312) +#pragma warning(disable:4311) +#include <windows.h> +#include <stdio.h> +#include <tchar.h> +#define _NO_CVCONST_H +#include <dbghelp.h> +#include "WheatyExceptionReport.h" +#include "svn_revision.h" +#define CrashFolder _T("Crashs") +//#pragma comment(linker, "/defaultlib:dbghelp.lib") + +inline LPTSTR ErrorMessage(DWORD dw) +{ + LPVOID lpMsgBuf; + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + dw, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &lpMsgBuf, + 0, NULL ); + return (LPTSTR)lpMsgBuf; +} + +//============================== Global Variables ============================= + +// +// Declare the static variables of the WheatyExceptionReport class +// +TCHAR WheatyExceptionReport::m_szLogFileName[MAX_PATH]; +LPTOP_LEVEL_EXCEPTION_FILTER WheatyExceptionReport::m_previousFilter; +HANDLE WheatyExceptionReport::m_hReportFile; +HANDLE WheatyExceptionReport::m_hProcess; + +// Declare global instance of class +WheatyExceptionReport g_WheatyExceptionReport; + +//============================== Class Methods ============================= + +WheatyExceptionReport::WheatyExceptionReport( ) // Constructor +{ + // Install the unhandled exception filter function + m_previousFilter = SetUnhandledExceptionFilter(WheatyUnhandledExceptionFilter); + m_hProcess = GetCurrentProcess(); +} + +//============ +// Destructor +//============ +WheatyExceptionReport::~WheatyExceptionReport( ) +{ + if(m_previousFilter) + SetUnhandledExceptionFilter( m_previousFilter ); +} + +//=========================================================== +// Entry point where control comes on an unhandled exception +//=========================================================== +LONG WINAPI WheatyExceptionReport::WheatyUnhandledExceptionFilter( +PEXCEPTION_POINTERS pExceptionInfo ) +{ + TCHAR module_folder_name[MAX_PATH]; + GetModuleFileName( 0, module_folder_name, MAX_PATH ); + TCHAR* pos = _tcsrchr(module_folder_name, '\\'); + if(!pos) + return 0; + pos[0] = '\0'; + ++pos; + + TCHAR crash_folder_path[MAX_PATH]; + sprintf(crash_folder_path, "%s\\%s", module_folder_name, CrashFolder); + if(!CreateDirectory(crash_folder_path, NULL)) + { + if(GetLastError() != ERROR_ALREADY_EXISTS) + return 0; + } + + SYSTEMTIME systime; + GetLocalTime(&systime); + sprintf(m_szLogFileName, "%s\\%s_[%u-%u_%u-%u-%u].txt", + crash_folder_path, pos, systime.wDay, systime.wMonth, systime.wHour, systime.wMinute, systime.wSecond + ); + + m_hReportFile = CreateFile( m_szLogFileName, + GENERIC_WRITE, + 0, + 0, + OPEN_ALWAYS, + FILE_FLAG_WRITE_THROUGH, + 0 ); + + if ( m_hReportFile ) + { + SetFilePointer( m_hReportFile, 0, 0, FILE_END ); + + GenerateExceptionReport( pExceptionInfo ); + + CloseHandle( m_hReportFile ); + m_hReportFile = 0; + } + + if ( m_previousFilter ) + return m_previousFilter( pExceptionInfo ); + else + return EXCEPTION_EXECUTE_HANDLER/*EXCEPTION_CONTINUE_SEARCH*/; +} + +BOOL WheatyExceptionReport::_GetProcessorName(TCHAR* sProcessorName, DWORD maxcount) +{ + if(!sProcessorName) + return FALSE; + + HKEY hKey; + LONG lRet; + lRet = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0"), + 0, KEY_QUERY_VALUE, &hKey); + if (lRet != ERROR_SUCCESS) + return FALSE; + TCHAR szTmp[2048]; + DWORD cntBytes = sizeof(szTmp); + lRet = ::RegQueryValueEx(hKey, _T("ProcessorNameString"), NULL, NULL, + (LPBYTE)szTmp, &cntBytes); + if (lRet != ERROR_SUCCESS) + return FALSE; + ::RegCloseKey(hKey); + sProcessorName[0] = '\0'; + // Skip spaces + TCHAR* psz = szTmp; + while (iswspace(*psz)) + ++psz; + _tcsncpy(sProcessorName, psz, maxcount); + return TRUE; +} + +BOOL WheatyExceptionReport::_GetWindowsVersion(TCHAR* szVersion, DWORD cntMax) +{ + // Try calling GetVersionEx using the OSVERSIONINFOEX structure. + // If that fails, try using the OSVERSIONINFO structure. + OSVERSIONINFOEX osvi = { 0 }; + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + BOOL bOsVersionInfoEx; + bOsVersionInfoEx = ::GetVersionEx((LPOSVERSIONINFO)(&osvi)); + if (!bOsVersionInfoEx) + { + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + if (!::GetVersionEx((OSVERSIONINFO*)&osvi)) + return FALSE; + } + *szVersion = _T('\0'); + TCHAR wszTmp[128]; + switch (osvi.dwPlatformId) + { + // Windows NT product family. + case VER_PLATFORM_WIN32_NT: + // Test for the specific product family. + if (osvi.dwMajorVersion == 6) + _tcsncat(szVersion, _T("Windows Vista or Windows Server 2008 "), cntMax); + if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2) + _tcsncat(szVersion, _T("Microsoft Windows Server 2003 "), cntMax); + if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1) + _tcsncat(szVersion, _T("Microsoft Windows XP "), cntMax); + if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0) + _tcsncat(szVersion, _T("Microsoft Windows 2000 "), cntMax); + if (osvi.dwMajorVersion <= 4 ) + _tcsncat(szVersion, _T("Microsoft Windows NT "), cntMax); + + // Test for specific product on Windows NT 4.0 SP6 and later. + if (bOsVersionInfoEx) + { + // Test for the workstation type. + #if WINVER < 0x0500 + if (osvi.wReserved[1] == VER_NT_WORKSTATION) + #else + if (osvi.wProductType == VER_NT_WORKSTATION) + #endif // WINVER < 0x0500 + { + if (osvi.dwMajorVersion == 4) + _tcsncat(szVersion, _T("Workstation 4.0 "), cntMax); + #if WINVER < 0x0500 + else if (osvi.wReserved[0] & VER_SUITE_PERSONAL) + #else + else if (osvi.wSuiteMask & VER_SUITE_PERSONAL) + #endif // WINVER < 0x0500 + _tcsncat(szVersion, _T("Home Edition "), cntMax); + #if WINVER < 0x0500 + else if (osvi.wReserved[0] & VER_SUITE_EMBEDDEDNT) + #else + else if (osvi.wSuiteMask & VER_SUITE_EMBEDDEDNT) + #endif // WINVER < 0x0500 + _tcsncat(szVersion, _T("Embedded "), cntMax); + else + _tcsncat(szVersion, _T("Professional "), cntMax); + } + // Test for the server type. + #if WINVER < 0x0500 + else if (osvi.wReserved[1] == VER_NT_SERVER) + #else + else if (osvi.wProductType == VER_NT_SERVER) + #endif // WINVER < 0x0500 + { + if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2) + { + #if WINVER < 0x0500 + if (osvi.wReserved[0] & VER_SUITE_DATACENTER) + #else + if (osvi.wSuiteMask & VER_SUITE_DATACENTER) + #endif // WINVER < 0x0500 + _tcsncat(szVersion, _T("Datacenter Edition "), cntMax); + #if WINVER < 0x0500 + else if (osvi.wReserved[0] & VER_SUITE_ENTERPRISE) + #else + else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) + #endif // WINVER < 0x0500 + _tcsncat(szVersion, _T("Enterprise Edition "), cntMax); + #if WINVER < 0x0500 + else if (osvi.wReserved[0] == VER_SUITE_BLADE) + #else + else if (osvi.wSuiteMask == VER_SUITE_BLADE) + #endif // WINVER < 0x0500 + _tcsncat(szVersion, _T("Web Edition "), cntMax); + else + _tcsncat(szVersion, _T("Standard Edition "), cntMax); + } + else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0) + { + #if WINVER < 0x0500 + if (osvi.wReserved[0] & VER_SUITE_DATACENTER) + #else + if (osvi.wSuiteMask & VER_SUITE_DATACENTER) + #endif // WINVER < 0x0500 + _tcsncat(szVersion, _T("Datacenter Server "), cntMax); + #if WINVER < 0x0500 + else if (osvi.wReserved[0] & VER_SUITE_ENTERPRISE ) + #else + else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE ) + #endif // WINVER < 0x0500 + _tcsncat(szVersion, _T("Advanced Server "), cntMax); + else + _tcsncat(szVersion, _T("Server "), cntMax); + } + else // Windows NT 4.0 + { + #if WINVER < 0x0500 + if (osvi.wReserved[0] & VER_SUITE_ENTERPRISE) + #else + if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) + #endif // WINVER < 0x0500 + _tcsncat(szVersion, _T("Server 4.0, Enterprise Edition "), cntMax); + else + _tcsncat(szVersion, _T("Server 4.0 "), cntMax); + } + } + } + // Display service pack (if any) and build number. + if (osvi.dwMajorVersion == 4 && _tcsicmp(osvi.szCSDVersion, _T("Service Pack 6")) == 0) + { + HKEY hKey; + LONG lRet; + + // Test for SP6 versus SP6a. + lRet = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Hotfix\\Q246009"), 0, KEY_QUERY_VALUE, &hKey); + if (lRet == ERROR_SUCCESS) + { + _stprintf(wszTmp, _T("Service Pack 6a (Version %d.%d, Build %d)"), + osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF); + _tcsncat(szVersion, wszTmp, cntMax); + } + else // Windows NT 4.0 prior to SP6a + { + _stprintf(wszTmp, _T("%s (Version %d.%d, Build %d)"), + osvi.szCSDVersion, osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF); + _tcsncat(szVersion, wszTmp, cntMax); + } + ::RegCloseKey(hKey); + } + else // Windows NT 3.51 and earlier or Windows 2000 and later + { + if (!_tcslen(osvi.szCSDVersion)) + _stprintf(wszTmp, _T("(Version %d.%d, Build %d)"), + osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF); + else + _stprintf(wszTmp, _T("%s (Version %d.%d, Build %d)"), + osvi.szCSDVersion, osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF); + _tcsncat(szVersion, wszTmp, cntMax); + } + break; + default: + _stprintf(wszTmp, _T("%s (Version %d.%d, Build %d)"), + osvi.szCSDVersion, osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF); + _tcsncat(szVersion, wszTmp, cntMax); + break; + } + + return TRUE; +} + +void WheatyExceptionReport::PrintSystemInfo() +{ + SYSTEM_INFO SystemInfo; + ::GetSystemInfo(&SystemInfo); + + MEMORYSTATUS MemoryStatus; + MemoryStatus.dwLength = sizeof (MEMORYSTATUS); + ::GlobalMemoryStatus(&MemoryStatus); + TCHAR sString[1024]; + _tprintf(_T("//=====================================================\r\n")); + if (_GetProcessorName(sString, countof(sString))) + _tprintf(_T("*** Hardware ***\r\nProcessor: %s\r\nNumber Of Processors: %d\r\nPhysical Memory: %d KB (Available: %d KB)\r\nCommit Charge Limit: %d KB\r\n"), + sString, SystemInfo.dwNumberOfProcessors, MemoryStatus.dwTotalPhys/0x400, MemoryStatus.dwAvailPhys/0x400, MemoryStatus.dwTotalPageFile/0x400); + else + _tprintf(_T("*** Hardware ***\r\nProcessor: <unknown>\r\nNumber Of Processors: %d\r\nPhysical Memory: %d KB (Available: %d KB)\r\nCommit Charge Limit: %d KB\r\n"), + SystemInfo.dwNumberOfProcessors, MemoryStatus.dwTotalPhys/0x400, MemoryStatus.dwAvailPhys/0x400, MemoryStatus.dwTotalPageFile/0x400); + + if(_GetWindowsVersion(sString, countof(sString))) + _tprintf(_T("\r\n*** Operation System ***\r\n%s\r\n"), sString); + else + _tprintf(_T("\r\n*** Operation System:\r\n<unknown>\r\n")); +} + +//=========================================================================== +// Open the report file, and write the desired information to it. Called by +// WheatyUnhandledExceptionFilter +//=========================================================================== +void WheatyExceptionReport::GenerateExceptionReport( +PEXCEPTION_POINTERS pExceptionInfo ) +{ + SYSTEMTIME systime; + GetLocalTime(&systime); + + // Start out with a banner + _tprintf(_T("Revision: %s\r\n"), SVN_REVISION); + _tprintf(_T("Date %u:%u:%u. Time %u:%u \r\n"), systime.wDay, systime.wMonth, systime.wYear, systime.wHour, systime.wMinute); + PEXCEPTION_RECORD pExceptionRecord = pExceptionInfo->ExceptionRecord; + + PrintSystemInfo(); + // First print information about the type of fault + _tprintf(_T("\r\n//=====================================================\r\n")); + _tprintf( _T("Exception code: %08X %s\r\n"), + pExceptionRecord->ExceptionCode, + GetExceptionString(pExceptionRecord->ExceptionCode) ); + + // Now print information about where the fault occured + TCHAR szFaultingModule[MAX_PATH]; + DWORD section; + DWORD_PTR offset; + GetLogicalAddress( pExceptionRecord->ExceptionAddress, + szFaultingModule, + sizeof( szFaultingModule ), + section, offset ); + +#ifdef _M_IX86 + _tprintf( _T("Fault address: %08X %02X:%08X %s\r\n"), + pExceptionRecord->ExceptionAddress, + section, offset, szFaultingModule ); +#endif +#ifdef _M_X64 + _tprintf( _T("Fault address: %016I64X %02X:%016I64X %s\r\n"), + pExceptionRecord->ExceptionAddress, + section, offset, szFaultingModule ); +#endif + + PCONTEXT pCtx = pExceptionInfo->ContextRecord; + + // Show the registers + #ifdef _M_IX86 // X86 Only! + _tprintf( _T("\r\nRegisters:\r\n") ); + + _tprintf(_T("EAX:%08X\r\nEBX:%08X\r\nECX:%08X\r\nEDX:%08X\r\nESI:%08X\r\nEDI:%08X\r\n") + ,pCtx->Eax, pCtx->Ebx, pCtx->Ecx, pCtx->Edx, + pCtx->Esi, pCtx->Edi ); + + _tprintf( _T("CS:EIP:%04X:%08X\r\n"), pCtx->SegCs, pCtx->Eip ); + _tprintf( _T("SS:ESP:%04X:%08X EBP:%08X\r\n"), + pCtx->SegSs, pCtx->Esp, pCtx->Ebp ); + _tprintf( _T("DS:%04X ES:%04X FS:%04X GS:%04X\r\n"), + pCtx->SegDs, pCtx->SegEs, pCtx->SegFs, pCtx->SegGs ); + _tprintf( _T("Flags:%08X\r\n"), pCtx->EFlags ); + #endif + + #ifdef _M_X64 + _tprintf( _T("\r\nRegisters:\r\n") ); + _tprintf(_T("RAX:%016I64X\r\nRBX:%016I64X\r\nRCX:%016I64X\r\nRDX:%016I64X\r\nRSI:%016I64X\r\nRDI:%016I64X\r\n") + _T("R8: %016I64X\r\nR9: %016I64X\r\nR10:%016I64X\r\nR11:%016I64X\r\nR12:%016I64X\r\nR13:%016I64X\r\nR14:%016I64X\r\nR15:%016I64X\r\n") + ,pCtx->Rax, pCtx->Rbx, pCtx->Rcx, pCtx->Rdx, + pCtx->Rsi, pCtx->Rdi ,pCtx->R9,pCtx->R10,pCtx->R11,pCtx->R12,pCtx->R13,pCtx->R14,pCtx->R15); + _tprintf( _T("CS:RIP:%04X:%016I64X\r\n"), pCtx->SegCs, pCtx->Rip ); + _tprintf( _T("SS:RSP:%04X:%016X RBP:%08X\r\n"), + pCtx->SegSs, pCtx->Rsp, pCtx->Rbp ); + _tprintf( _T("DS:%04X ES:%04X FS:%04X GS:%04X\r\n"), + pCtx->SegDs, pCtx->SegEs, pCtx->SegFs, pCtx->SegGs ); + _tprintf( _T("Flags:%08X\r\n"), pCtx->EFlags ); + #endif + + SymSetOptions( SYMOPT_DEFERRED_LOADS ); + + // Initialize DbgHelp + if ( !SymInitialize( GetCurrentProcess(), 0, TRUE ) ) + { + _tprintf(_T("\n\rCRITICAL ERROR.\n\r Couldn't initialize the symbol handler for process.\n\rError [%s].\n\r\n\r"), + ErrorMessage(GetLastError())); + } + + CONTEXT trashableContext = *pCtx; + + WriteStackDetails( &trashableContext, false ); + +// #ifdef _M_IX86 // X86 Only! + + _tprintf( _T("========================\r\n") ); + _tprintf( _T("Local Variables And Parameters\r\n") ); + + trashableContext = *pCtx; + WriteStackDetails( &trashableContext, true ); + + _tprintf( _T("========================\r\n") ); + _tprintf( _T("Global Variables\r\n") ); + + SymEnumSymbols( GetCurrentProcess(), + (DWORD64)GetModuleHandle(szFaultingModule), + 0, EnumerateSymbolsCallback, 0 ); + // #endif // X86 Only! + + SymCleanup( GetCurrentProcess() ); + + _tprintf( _T("\r\n") ); +} + +//====================================================================== +// Given an exception code, returns a pointer to a static string with a +// description of the exception +//====================================================================== +LPTSTR WheatyExceptionReport::GetExceptionString( DWORD dwCode ) +{ + #define EXCEPTION( x ) case EXCEPTION_##x: return _T(#x); + + switch ( dwCode ) + { + EXCEPTION( ACCESS_VIOLATION ) + EXCEPTION( DATATYPE_MISALIGNMENT ) + EXCEPTION( BREAKPOINT ) + EXCEPTION( SINGLE_STEP ) + EXCEPTION( ARRAY_BOUNDS_EXCEEDED ) + EXCEPTION( FLT_DENORMAL_OPERAND ) + EXCEPTION( FLT_DIVIDE_BY_ZERO ) + EXCEPTION( FLT_INEXACT_RESULT ) + EXCEPTION( FLT_INVALID_OPERATION ) + EXCEPTION( FLT_OVERFLOW ) + EXCEPTION( FLT_STACK_CHECK ) + EXCEPTION( FLT_UNDERFLOW ) + EXCEPTION( INT_DIVIDE_BY_ZERO ) + EXCEPTION( INT_OVERFLOW ) + EXCEPTION( PRIV_INSTRUCTION ) + EXCEPTION( IN_PAGE_ERROR ) + EXCEPTION( ILLEGAL_INSTRUCTION ) + EXCEPTION( NONCONTINUABLE_EXCEPTION ) + EXCEPTION( STACK_OVERFLOW ) + EXCEPTION( INVALID_DISPOSITION ) + EXCEPTION( GUARD_PAGE ) + EXCEPTION( INVALID_HANDLE ) + } + + // If not one of the "known" exceptions, try to get the string + // from NTDLL.DLL's message table. + + static TCHAR szBuffer[512] = { 0 }; + + FormatMessage( FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE, + GetModuleHandle( _T("NTDLL.DLL") ), + dwCode, 0, szBuffer, sizeof( szBuffer ), 0 ); + + return szBuffer; +} + +//============================================================================= +// Given a linear address, locates the module, section, and offset containing +// that address. +// +// Note: the szModule paramater buffer is an output buffer of length specified +// by the len parameter (in characters!) +//============================================================================= +BOOL WheatyExceptionReport::GetLogicalAddress( +PVOID addr, PTSTR szModule, DWORD len, DWORD& section, DWORD_PTR& offset ) +{ + MEMORY_BASIC_INFORMATION mbi; + + if ( !VirtualQuery( addr, &mbi, sizeof(mbi) ) ) + return FALSE; + + DWORD_PTR hMod = (DWORD_PTR)mbi.AllocationBase; + + if ( !GetModuleFileName( (HMODULE)hMod, szModule, len ) ) + return FALSE; + + // Point to the DOS header in memory + PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)hMod; + + // From the DOS header, find the NT (PE) header + PIMAGE_NT_HEADERS pNtHdr = (PIMAGE_NT_HEADERS)(hMod + DWORD_PTR(pDosHdr->e_lfanew)); + + PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION( pNtHdr ); + + DWORD_PTR rva = (DWORD_PTR)addr - hMod; // RVA is offset from module load address + + // Iterate through the section table, looking for the one that encompasses + // the linear address. + for ( unsigned i = 0; + i < pNtHdr->FileHeader.NumberOfSections; + i++, pSection++ ) + { + DWORD_PTR sectionStart = pSection->VirtualAddress; + DWORD_PTR sectionEnd = sectionStart + + DWORD_PTR(max(pSection->SizeOfRawData, pSection->Misc.VirtualSize)); + + // Is the address in this section??? + if ( (rva >= sectionStart) && (rva <= sectionEnd) ) + { + // Yes, address is in the section. Calculate section and offset, + // and store in the "section" & "offset" params, which were + // passed by reference. + section = i+1; + offset = rva - sectionStart; + return TRUE; + } + } + + return FALSE; // Should never get here! +} + +// It contains SYMBOL_INFO structure plus additional +// space for the name of the symbol +struct CSymbolInfoPackage : public SYMBOL_INFO_PACKAGE +{ + CSymbolInfoPackage() + { + si.SizeOfStruct = sizeof(SYMBOL_INFO); + si.MaxNameLen = sizeof(name); + } +}; + +//============================================================ +// Walks the stack, and writes the results to the report file +//============================================================ +void WheatyExceptionReport::WriteStackDetails( +PCONTEXT pContext, +bool bWriteVariables ) // true if local/params should be output +{ + _tprintf( _T("\r\nCall stack:\r\n") ); + + _tprintf( _T("Address Frame Function SourceFile\r\n") ); + + DWORD dwMachineType = 0; + // Could use SymSetOptions here to add the SYMOPT_DEFERRED_LOADS flag + + STACKFRAME64 sf; + memset( &sf, 0, sizeof(sf) ); + + #ifdef _M_IX86 + // Initialize the STACKFRAME structure for the first call. This is only + // necessary for Intel CPUs, and isn't mentioned in the documentation. + sf.AddrPC.Offset = pContext->Eip; + sf.AddrPC.Mode = AddrModeFlat; + sf.AddrStack.Offset = pContext->Esp; + sf.AddrStack.Mode = AddrModeFlat; + sf.AddrFrame.Offset = pContext->Ebp; + sf.AddrFrame.Mode = AddrModeFlat; + + dwMachineType = IMAGE_FILE_MACHINE_I386; + #endif + +#ifdef _M_X64 + sf.AddrPC.Offset = pContext->Rip; + sf.AddrPC.Mode = AddrModeFlat; + sf.AddrStack.Offset = pContext->Rsp; + sf.AddrStack.Mode = AddrModeFlat; + sf.AddrFrame.Offset = pContext->Rbp; + sf.AddrFrame.Mode = AddrModeFlat; + dwMachineType = IMAGE_FILE_MACHINE_AMD64; +#endif + + while ( 1 ) + { + // Get the next stack frame + if ( ! StackWalk64( dwMachineType, + m_hProcess, + GetCurrentThread(), + &sf, + pContext, + 0, + SymFunctionTableAccess64, + SymGetModuleBase64, + 0 ) ) + break; + if ( 0 == sf.AddrFrame.Offset ) // Basic sanity check to make sure + break; // the frame is OK. Bail if not. +#ifdef _M_IX86 + _tprintf( _T("%08X %08X "), sf.AddrPC.Offset, sf.AddrFrame.Offset ); +#endif +#ifdef _M_X64 + _tprintf( _T("%016I64X %016I64X "), sf.AddrPC.Offset, sf.AddrFrame.Offset ); +#endif + + DWORD64 symDisplacement = 0; // Displacement of the input address, + // relative to the start of the symbol + + // Get the name of the function for this stack frame entry + CSymbolInfoPackage sip; + if ( SymFromAddr( + m_hProcess, // Process handle of the current process + sf.AddrPC.Offset, // Symbol address + &symDisplacement, // Address of the variable that will receive the displacement + &sip.si // Address of the SYMBOL_INFO structure (inside "sip" object) + )) + { + _tprintf( _T("%hs+%I64X"), sip.si.Name, symDisplacement ); + + } + else // No symbol found. Print out the logical address instead. + { + TCHAR szModule[MAX_PATH] = _T(""); + DWORD section = 0; + DWORD_PTR offset = 0; + + GetLogicalAddress( (PVOID)sf.AddrPC.Offset, + szModule, sizeof(szModule), section, offset ); +#ifdef _M_IX86 + _tprintf( _T("%04X:%08X %s"), section, offset, szModule ); +#endif +#ifdef _M_X64 + _tprintf( _T("%04X:%016I64X %s"), section, offset, szModule ); +#endif + } + + // Get the source line for this stack frame entry + IMAGEHLP_LINE64 lineInfo = { sizeof(IMAGEHLP_LINE) }; + DWORD dwLineDisplacement; + if ( SymGetLineFromAddr64( m_hProcess, sf.AddrPC.Offset, + &dwLineDisplacement, &lineInfo ) ) + { + _tprintf(_T(" %s line %u"),lineInfo.FileName,lineInfo.LineNumber); + } + + _tprintf( _T("\r\n") ); + + // Write out the variables, if desired + if ( bWriteVariables ) + { + // Use SymSetContext to get just the locals/params for this frame + IMAGEHLP_STACK_FRAME imagehlpStackFrame; + imagehlpStackFrame.InstructionOffset = sf.AddrPC.Offset; + SymSetContext( m_hProcess, &imagehlpStackFrame, 0 ); + + // Enumerate the locals/parameters + SymEnumSymbols( m_hProcess, 0, 0, EnumerateSymbolsCallback, &sf ); + + _tprintf( _T("\r\n") ); + } + } + +} + +////////////////////////////////////////////////////////////////////////////// +// The function invoked by SymEnumSymbols +////////////////////////////////////////////////////////////////////////////// + +BOOL CALLBACK +WheatyExceptionReport::EnumerateSymbolsCallback( +PSYMBOL_INFO pSymInfo, +ULONG SymbolSize, +PVOID UserContext ) +{ + + char szBuffer[2048]; + + __try + { + if ( FormatSymbolValue( pSymInfo, (STACKFRAME*)UserContext, + szBuffer, sizeof(szBuffer) ) ) + _tprintf( _T("\t%s\r\n"), szBuffer ); + } + __except( 1 ) + { + _tprintf( _T("punting on symbol %s\r\n"), pSymInfo->Name ); + } + + return TRUE; +} + +////////////////////////////////////////////////////////////////////////////// +// Given a SYMBOL_INFO representing a particular variable, displays its +// contents. If it's a user defined type, display the members and their +// values. +////////////////////////////////////////////////////////////////////////////// +bool WheatyExceptionReport::FormatSymbolValue( +PSYMBOL_INFO pSym, +STACKFRAME * sf, +char * pszBuffer, +unsigned cbBuffer ) +{ + char * pszCurrBuffer = pszBuffer; + + // Indicate if the variable is a local or parameter + if ( pSym->Flags & IMAGEHLP_SYMBOL_INFO_PARAMETER ) + pszCurrBuffer += sprintf( pszCurrBuffer, "Parameter " ); + else if ( pSym->Flags & IMAGEHLP_SYMBOL_INFO_LOCAL ) + pszCurrBuffer += sprintf( pszCurrBuffer, "Local " ); + + // If it's a function, don't do anything. + if ( pSym->Tag == 5 ) // SymTagFunction from CVCONST.H from the DIA SDK + return false; + + DWORD_PTR pVariable = 0; // Will point to the variable's data in memory + + if ( pSym->Flags & IMAGEHLP_SYMBOL_INFO_REGRELATIVE ) + { + // if ( pSym->Register == 8 ) // EBP is the value 8 (in DBGHELP 5.1) + { // This may change!!! + pVariable = sf->AddrFrame.Offset; + pVariable += (DWORD_PTR)pSym->Address; + } + // else + // return false; + } + else if ( pSym->Flags & IMAGEHLP_SYMBOL_INFO_REGISTER ) + { + return false; // Don't try to report register variable + } + else + { + pVariable = (DWORD_PTR)pSym->Address; // It must be a global variable + } + + // Determine if the variable is a user defined type (UDT). IF so, bHandled + // will return true. + bool bHandled; + pszCurrBuffer = DumpTypeIndex(pszCurrBuffer,pSym->ModBase, pSym->TypeIndex, + 0, pVariable, bHandled, pSym->Name ); + + if ( !bHandled ) + { + // The symbol wasn't a UDT, so do basic, stupid formatting of the + // variable. Based on the size, we're assuming it's a char, WORD, or + // DWORD. + BasicType basicType = GetBasicType( pSym->TypeIndex, pSym->ModBase ); + pszCurrBuffer += sprintf( pszCurrBuffer, rgBaseType[basicType]); + + // Emit the variable name + pszCurrBuffer += sprintf( pszCurrBuffer, "\'%s\'", pSym->Name ); + + pszCurrBuffer = FormatOutputValue(pszCurrBuffer, basicType, pSym->Size, + (PVOID)pVariable ); + } + + return true; +} + +////////////////////////////////////////////////////////////////////////////// +// If it's a user defined type (UDT), recurse through its members until we're +// at fundamental types. When he hit fundamental types, return +// bHandled = false, so that FormatSymbolValue() will format them. +////////////////////////////////////////////////////////////////////////////// +char * WheatyExceptionReport::DumpTypeIndex( +char * pszCurrBuffer, +DWORD64 modBase, +DWORD dwTypeIndex, +unsigned nestingLevel, +DWORD_PTR offset, +bool & bHandled, +char* Name) +{ + bHandled = false; + + // Get the name of the symbol. This will either be a Type name (if a UDT), + // or the structure member name. + WCHAR * pwszTypeName; + if ( SymGetTypeInfo( m_hProcess, modBase, dwTypeIndex, TI_GET_SYMNAME, + &pwszTypeName ) ) + { + pszCurrBuffer += sprintf( pszCurrBuffer, " %ls", pwszTypeName ); + LocalFree( pwszTypeName ); + } + + // Determine how many children this type has. + DWORD dwChildrenCount = 0; + SymGetTypeInfo( m_hProcess, modBase, dwTypeIndex, TI_GET_CHILDRENCOUNT, + &dwChildrenCount ); + + if ( !dwChildrenCount ) // If no children, we're done + return pszCurrBuffer; + + // Prepare to get an array of "TypeIds", representing each of the children. + // SymGetTypeInfo(TI_FINDCHILDREN) expects more memory than just a + // TI_FINDCHILDREN_PARAMS struct has. Use derivation to accomplish this. + struct FINDCHILDREN : TI_FINDCHILDREN_PARAMS + { + ULONG MoreChildIds[1024]; + FINDCHILDREN(){Count = sizeof(MoreChildIds) / sizeof(MoreChildIds[0]);} + } children; + + children.Count = dwChildrenCount; + children.Start= 0; + + // Get the array of TypeIds, one for each child type + if ( !SymGetTypeInfo( m_hProcess, modBase, dwTypeIndex, TI_FINDCHILDREN, + &children ) ) + { + return pszCurrBuffer; + } + + // Append a line feed + pszCurrBuffer += sprintf( pszCurrBuffer, "\r\n" ); + + // Iterate through each of the children + for ( unsigned i = 0; i < dwChildrenCount; i++ ) + { + // Add appropriate indentation level (since this routine is recursive) + for ( unsigned j = 0; j <= nestingLevel+1; j++ ) + pszCurrBuffer += sprintf( pszCurrBuffer, "\t" ); + + // Recurse for each of the child types + bool bHandled2; + BasicType basicType = GetBasicType(children.ChildId[i], modBase ); + pszCurrBuffer += sprintf( pszCurrBuffer, rgBaseType[basicType]); + + pszCurrBuffer = DumpTypeIndex( pszCurrBuffer, modBase, + children.ChildId[i], nestingLevel+1, + offset, bHandled2, ""/*Name */); + + // If the child wasn't a UDT, format it appropriately + if ( !bHandled2 ) + { + // Get the offset of the child member, relative to its parent + DWORD dwMemberOffset; + SymGetTypeInfo( m_hProcess, modBase, children.ChildId[i], + TI_GET_OFFSET, &dwMemberOffset ); + + // Get the real "TypeId" of the child. We need this for the + // SymGetTypeInfo( TI_GET_TYPEID ) call below. + DWORD typeId; + SymGetTypeInfo( m_hProcess, modBase, children.ChildId[i], + TI_GET_TYPEID, &typeId ); + + // Get the size of the child member + ULONG64 length; + SymGetTypeInfo(m_hProcess, modBase, typeId, TI_GET_LENGTH,&length); + + // Calculate the address of the member + DWORD_PTR dwFinalOffset = offset + dwMemberOffset; + + // BasicType basicType = GetBasicType(children.ChildId[i], modBase ); + // + // pszCurrBuffer += sprintf( pszCurrBuffer, rgBaseType[basicType]); + // + // Emit the variable name + // pszCurrBuffer += sprintf( pszCurrBuffer, "\'%s\'", Name ); + + pszCurrBuffer = FormatOutputValue( pszCurrBuffer, basicType, + length, (PVOID)dwFinalOffset ); + + pszCurrBuffer += sprintf( pszCurrBuffer, "\r\n" ); + } + } + + bHandled = true; + return pszCurrBuffer; +} + +char * WheatyExceptionReport::FormatOutputValue( char * pszCurrBuffer, +BasicType basicType, +DWORD64 length, +PVOID pAddress ) +{ + // Format appropriately (assuming it's a 1, 2, or 4 bytes (!!!) + if ( length == 1 ) + pszCurrBuffer += sprintf( pszCurrBuffer, " = %X", *(PBYTE)pAddress ); + else if ( length == 2 ) + pszCurrBuffer += sprintf( pszCurrBuffer, " = %X", *(PWORD)pAddress ); + else if ( length == 4 ) + { + if ( basicType == btFloat ) + { + pszCurrBuffer += sprintf(pszCurrBuffer," = %f", *(PFLOAT)pAddress); + } + else if ( basicType == btChar ) + { + if ( !IsBadStringPtr( *(PSTR*)pAddress, 32) ) + { + pszCurrBuffer += sprintf( pszCurrBuffer, " = \"%.31s\"", + *(PDWORD)pAddress ); + } + else + pszCurrBuffer += sprintf( pszCurrBuffer, " = %X", + *(PDWORD)pAddress ); + } + else + pszCurrBuffer += sprintf(pszCurrBuffer," = %X", *(PDWORD)pAddress); + } + else if ( length == 8 ) + { + if ( basicType == btFloat ) + { + pszCurrBuffer += sprintf( pszCurrBuffer, " = %lf", + *(double *)pAddress ); + } + else + pszCurrBuffer += sprintf( pszCurrBuffer, " = %I64X", + *(DWORD64*)pAddress ); + } + + return pszCurrBuffer; +} + +BasicType +WheatyExceptionReport::GetBasicType( DWORD typeIndex, DWORD64 modBase ) +{ + BasicType basicType; + if ( SymGetTypeInfo( m_hProcess, modBase, typeIndex, + TI_GET_BASETYPE, &basicType ) ) + { + return basicType; + } + + // Get the real "TypeId" of the child. We need this for the + // SymGetTypeInfo( TI_GET_TYPEID ) call below. + DWORD typeId; + if (SymGetTypeInfo(m_hProcess,modBase, typeIndex, TI_GET_TYPEID, &typeId)) + { + if ( SymGetTypeInfo( m_hProcess, modBase, typeId, TI_GET_BASETYPE, + &basicType ) ) + { + return basicType; + } + } + + return btNoType; +} + +//============================================================================ +// Helper function that writes to the report file, and allows the user to use +// printf style formating +//============================================================================ +int __cdecl WheatyExceptionReport::_tprintf(const TCHAR * format, ...) +{ + TCHAR szBuff[1024]; + int retValue; + DWORD cbWritten; + va_list argptr; + + va_start( argptr, format ); + retValue = vsprintf( szBuff, format, argptr ); + va_end( argptr ); + + WriteFile(m_hReportFile, szBuff, retValue * sizeof(TCHAR), &cbWritten, 0 ); + + return retValue; +} diff --git a/src/shared/WheatyExceptionReport.h b/src/shared/WheatyExceptionReport.h new file mode 100644 index 00000000000..aa77c951612 --- /dev/null +++ b/src/shared/WheatyExceptionReport.h @@ -0,0 +1,117 @@ +#ifndef _WHEATYEXCEPTIONREPORT_ +#define _WHEATYEXCEPTIONREPORT_ + +#include <dbghelp.h> + +#if _MSC_VER < 1400 +# define countof(array) (sizeof(array) / sizeof(array[0])) +#else +# include <stdlib.h> +# define countof _countof +#endif // _MSC_VER < 1400 + +enum BasicType // Stolen from CVCONST.H in the DIA 2.0 SDK +{ + btNoType = 0, + btVoid = 1, + btChar = 2, + btWChar = 3, + btInt = 6, + btUInt = 7, + btFloat = 8, + btBCD = 9, + btBool = 10, + btLong = 13, + btULong = 14, + btCurrency = 25, + btDate = 26, + btVariant = 27, + btComplex = 28, + btBit = 29, + btBSTR = 30, + btHresult = 31 +}; + +const char* const rgBaseType[] = +{ + " <user defined> ", // btNoType = 0, + " void ", // btVoid = 1, + " char* ", // btChar = 2, + " wchar_t* ", // btWChar = 3, + " signed char ", + " unsigned char ", + " int ", // btInt = 6, + " unsigned int ", // btUInt = 7, + " float ", // btFloat = 8, + " <BCD> ", // btBCD = 9, + " bool ", // btBool = 10, + " short ", + " unsigned short ", + " long ", // btLong = 13, + " unsigned long ", // btULong = 14, + " __int8 ", + " __int16 ", + " __int32 ", + " __int64 ", + " __int128 ", + " unsigned __int8 ", + " unsigned __int16 ", + " unsigned __int32 ", + " unsigned __int64 ", + " unsigned __int128 ", + " <currency> ", // btCurrency = 25, + " <date> ", // btDate = 26, + " VARIANT ", // btVariant = 27, + " <complex> ", // btComplex = 28, + " <bit> ", // btBit = 29, + " BSTR ", // btBSTR = 30, + " HRESULT " // btHresult = 31 +}; + +class WheatyExceptionReport +{ + public: + + WheatyExceptionReport( ); + ~WheatyExceptionReport( ); + + // entry point where control comes on an unhandled exception + static LONG WINAPI WheatyUnhandledExceptionFilter( + PEXCEPTION_POINTERS pExceptionInfo ); + + private: + + // where report info is extracted and generated + static void GenerateExceptionReport( PEXCEPTION_POINTERS pExceptionInfo ); + static void PrintSystemInfo(); + static BOOL _GetWindowsVersion(TCHAR* szVersion, DWORD cntMax); + static BOOL _GetProcessorName(TCHAR* sProcessorName, DWORD maxcount); + + // Helper functions + static LPTSTR GetExceptionString( DWORD dwCode ); + static BOOL GetLogicalAddress( PVOID addr, PTSTR szModule, DWORD len, + DWORD& section, DWORD_PTR& offset ); + + static void WriteStackDetails( PCONTEXT pContext, bool bWriteVariables ); + + static BOOL CALLBACK EnumerateSymbolsCallback(PSYMBOL_INFO,ULONG, PVOID); + + static bool FormatSymbolValue( PSYMBOL_INFO, STACKFRAME *, char * pszBuffer, unsigned cbBuffer ); + + static char * DumpTypeIndex( char *, DWORD64, DWORD, unsigned, DWORD_PTR, bool & , char*); + + static char * FormatOutputValue( char * pszCurrBuffer, BasicType basicType, DWORD64 length, PVOID pAddress ); + + static BasicType GetBasicType( DWORD typeIndex, DWORD64 modBase ); + + static int __cdecl _tprintf(const TCHAR * format, ...); + + // Variables used by the class + static TCHAR m_szLogFileName[MAX_PATH]; + static LPTOP_LEVEL_EXCEPTION_FILTER m_previousFilter; + static HANDLE m_hReportFile; + static HANDLE m_hProcess; +}; + +extern WheatyExceptionReport g_WheatyExceptionReport; // global instance of class +#endif //WheatyExceptionReport diff --git a/src/shared/WorldPacket.h b/src/shared/WorldPacket.h new file mode 100644 index 00000000000..aea9140045c --- /dev/null +++ b/src/shared/WorldPacket.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOSSERVER_WORLDPACKET_H +#define MANGOSSERVER_WORLDPACKET_H + +#include "Common.h" +#include "ByteBuffer.h" + +class WorldPacket : public ByteBuffer +{ + public: + // just container for later use + WorldPacket() : ByteBuffer(0), m_opcode(0) + { + } + explicit WorldPacket(uint16 opcode, size_t res=200) : ByteBuffer(res), m_opcode(opcode) { } + // copy constructor + WorldPacket(const WorldPacket &packet) : ByteBuffer(packet), m_opcode(packet.m_opcode) + { + } + + void Initialize(uint16 opcode, size_t newres=200) + { + clear(); + _storage.reserve(newres); + m_opcode = opcode; + } + + uint16 GetOpcode() const { return m_opcode; } + void SetOpcode(uint16 opcode) { m_opcode = opcode; } + + protected: + uint16 m_opcode; +}; +#endif diff --git a/src/shared/vmap/AABSPTree.h b/src/shared/vmap/AABSPTree.h new file mode 100644 index 00000000000..9b44470e7d9 --- /dev/null +++ b/src/shared/vmap/AABSPTree.h @@ -0,0 +1,1620 @@ +/** + @file AABSPTree.h + + @maintainer Morgan McGuire, matrix@graphics3d.com + + @created 2004-01-11 + @edited 2007-02-16 + + Copyright 2000-2007, Morgan McGuire. + All rights reserved. + + */ + +#ifndef G3D_AABSPTREE_H +#define G3D_AABSPTREE_H + +#include "VMapTools.h" + +#include "G3D/platform.h" +#include "G3D/Array.h" +#include "G3D/Table.h" +#include "G3D/Vector3.h" +#include "G3D/AABox.h" +#include "G3D/Sphere.h" +#include "G3D/Box.h" +#include "G3D/Triangle.h" +#include "G3D/Ray.h" +#include "G3D/GCamera.h" +#if 0 +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" +#endif +#include "G3D/CollisionDetection.h" +#include "G3D/GCamera.h" +#include <algorithm> + +// If defined, in debug mode the tree is checked for consistency +// as a way of detecting corruption due to implementation bugs +// #define VERIFY_TREE + +inline void getBounds(const G3D::Vector3& v, G3D::AABox& out) { + out = G3D::AABox(v); +} + + +inline void getBounds(const G3D::AABox& a, G3D::AABox& out) { + out = a; +} + +inline void getBounds(const G3D::Sphere& s, G3D::AABox& out) { + s.getBounds(out); +} + + +inline void getBounds(const G3D::Box& b, G3D::AABox& out) { + b.getBounds(out); +} + + +inline void getBounds(const G3D::Triangle& t, G3D::AABox& out) { + t.getBounds(out); +} + + + +inline void getBounds(const G3D::Vector3* v, G3D::AABox& out) { + out = G3D::AABox(*v); +} + + +inline void getBounds(const G3D::AABox* a, G3D::AABox& out) { + getBounds(*a, out); +} + +inline void getBounds(const G3D::Sphere* s, G3D::AABox& out) { + s->getBounds(out); +} + + +inline void getBounds(const G3D::Box* b, G3D::AABox& out) { + b->getBounds(out); +} + +inline void getBounds(const G3D::Triangle* t, G3D::AABox& out) { + t->getBounds(out); +} +namespace G3D { + namespace _internal { + + /** + Wraps a pointer value so that it can be treated as the instance itself; + convenient for inserting pointers into a Table but using the + object equality instead of pointer equality. + */ + template<class Type> + class Indirector { + public: + Type* handle; + + inline Indirector(Type* h) : handle(h) {} + + inline Indirector() : handle(NULL) {} + + /** Returns true iff the values referenced by the handles are equivalent. */ + inline bool operator==(const Indirector& m) { + return *handle == *(m.handle); + } + + inline bool operator==(const Type& m) { + return *handle == m; + } + + inline size_t hashCode() const { + return handle->hashCode(); + } + }; + } // namespace internal +} // namespace G3D + +template <class Handle> +struct GHashCode< G3D::_internal::Indirector<Handle> > +{ + size_t operator()(const G3D::_internal::Indirector<Handle>& key) const { return key.hashCode(); } +}; + +namespace G3D { + +/** + A set that supports spatial queries using an axis-aligned + BSP tree for speed. + + AABSPTree allows you to quickly find objects in 3D that lie within + a box or along a ray. For large sets of objects it is much faster + than testing each object for a collision. + + AABSPTree is as powerful as but more general than a Quad Tree, Oct + Tree, or KD Tree, but less general than an unconstrained BSP tree + (which is much slower to create). + + Internally, objects + are arranged into an axis-aligned BSP-tree according to their + axis-aligned bounds. This increases the cost of insertion to + O(log n) but allows fast overlap queries. + + <B>Template Parameters</B> + <DT>The template parameter <I>T</I> must be one for which + the following functions are all overloaded: + + <P><CODE>void ::getBounds(const T&, G3D::AABox&);</CODE> + <DT><CODE>bool ::operator==(const T&, const T&);</CODE> + <DT><CODE>unsigned int ::hashCode(const T&);</CODE> + <DT><CODE>T::T();</CODE> <I>(public constructor of no arguments)</I> + + G3D provides these for common classes like G3D::Vector3 and G3D::Sphere. + If you use a custom class, or a pointer to a custom class, you will need + to define those functions. + + <B>Moving %Set Members</B> + <DT>It is important that objects do not move without updating the + AABSPTree. If the axis-aligned bounds of an object are about + to change, AABSPTree::remove it before they change and + AABSPTree::insert it again afterward. For objects + where the hashCode and == operator are invariant with respect + to the 3D position, + you can use the AABSPTree::update method as a shortcut to + insert/remove an object in one step after it has moved. + + + Note: Do not mutate any value once it has been inserted into AABSPTree. Values + are copied interally. All AABSPTree iterators convert to pointers to constant + values to reinforce this. + + If you want to mutate the objects you intend to store in a AABSPTree + simply insert <I>pointers</I> to your objects instead of the objects + themselves, and ensure that the above operations are defined. (And + actually, because values are copied, if your values are large you may + want to insert pointers anyway, to save space and make the balance + operation faster.) + + <B>Dimensions</B> + Although designed as a 3D-data structure, you can use the AABSPTree + for data distributed along 2 or 1 axes by simply returning bounds + that are always zero along one or more dimensions. + +*/ +namespace _AABSPTree { + + /** Wrapper for a value that includes a cache of its bounds. + Except for the test value used in a set-query operation, there + is only ever one instance of the handle associated with any + value and the memberTable and Nodes maintain pointers to that + heap-allocated value. + */ + template<class TValue> + class Handle { + public: + /** The bounds of each object are constrained to AABox::maxFinite */ + AABox bounds; + + /** Center of bounds. We cache this value to avoid recomputing it + during the median sort, and because MSVC 6 std::sort goes into + an infinite loop if we compute the midpoint on the fly (possibly + a floating point roundoff issue, where B<A and A<B both are true).*/ + Vector3 center; + + TValue value; + + Handle<TValue>() {} + + inline Handle<TValue>(const TValue& v) : value(v) { + getBounds(v, bounds); + bounds = bounds.intersect(AABox::maxFinite()); + center = bounds.center(); + } + + inline bool operator==(const Handle<TValue>& other) const { + return (*value).operator==(*other.value); + } + + inline size_t hashCode() const { + return value->hashCode(); + } + }; + + template<> + class Handle<Triangle> { + public: + /** The bounds of each object are constrained to AABox::maxFinite */ + AABox bounds; + + /** Center of bounds. We cache this value to avoid recomputing it + during the median sort, and because MSVC 6 std::sort goes into + an infinite loop if we compute the midpoint on the fly (possibly + a floating point roundoff issue, where B<A and A<B both are true).*/ + Vector3 center; + + Triangle value; + + Handle<Triangle>() {} + + inline Handle<Triangle>(const Triangle& v) : value(v) { + getBounds(v, bounds); + bounds = bounds.intersect(AABox::maxFinite()); + center = bounds.center(); + } + + inline bool operator==(const Handle<Triangle>& other) const { + return value.operator==(other.value); + } + + inline size_t hashCode() const { + return value.hashCode(); + } + }; +} + +template<class T> class AABSPTree { +protected: +public: + + + /** Returns the bounds of the sub array. Used by makeNode. */ + static AABox computeBounds( + const Array<_AABSPTree::Handle<T>*>& point, + int beginIndex, + int endIndex) { + + Vector3 lo = Vector3::inf(); + Vector3 hi = -lo; + + for (int p = beginIndex; p <= endIndex; ++p) { + lo = lo.min(point[p]->bounds.low()); + hi = hi.max(point[p]->bounds.high()); + } + + return AABox(lo, hi); + } + + /** Compares centers */ + class CenterComparator { + public: + Vector3::Axis sortAxis; + + CenterComparator(Vector3::Axis a) : sortAxis(a) {} + + inline int operator()(_AABSPTree::Handle<T>* A, const _AABSPTree::Handle<T>* B) const { + float a = A->center[sortAxis]; + float b = B->center[sortAxis]; + + if (a < b) { + return 1; + } else if (a > b) { + return -1; + } else { + return 0; + } + } + }; + + + /** Compares bounds for strict >, <, or overlap*/ + class BoundsComparator { + public: + Vector3::Axis sortAxis; + + BoundsComparator(Vector3::Axis a) : sortAxis(a) {} + + inline int operator()(_AABSPTree::Handle<T>* A, const _AABSPTree::Handle<T>* B) const { + const AABox& a = A->bounds; + const AABox& b = B->bounds; + + if (a.high()[sortAxis] < b.low()[sortAxis]) { + return 1; + } else if (a.low()[sortAxis] > b.high()[sortAxis]) { + return -1; + } else { + return 0; + } + } + }; + + + /** Compares bounds to the sort location */ + class Comparator { + public: + Vector3::Axis sortAxis; + float sortLocation; + + Comparator(Vector3::Axis a, float l) : sortAxis(a), sortLocation(l) {} + + inline int operator()(_AABSPTree::Handle<T>* /*ignore*/, const _AABSPTree::Handle<T>* handle) const { + const AABox& box = handle->bounds; + debugAssert(ignore == NULL); + + if (box.high()[sortAxis] < sortLocation) { + // Box is strictly below the sort location + return -1; + } else if (box.low()[sortAxis] > sortLocation) { + // Box is strictly above the sort location + return 1; + } else { + // Box overlaps the sort location + return 0; + } + } + }; + + // Using System::malloc with this class provided no speed improvement. + class Node { + public: + + /** Spatial bounds on all values at this node and its children, based purely on + the parent's splitting planes. May be infinite. */ + AABox splitBounds; + + Vector3::Axis splitAxis; + + /** Location along the specified axis */ + float splitLocation; + + /** child[0] contains all values strictly + smaller than splitLocation along splitAxis. + + child[1] contains all values strictly + larger. + + Both may be NULL if there are not enough + values to bother recursing. + */ + Node* child[2]; + + /** Array of values at this node (i.e., values + straddling the split plane + all values if + this is a leaf node). + + This is an array of pointers because that minimizes + data movement during tree building, which accounts + for about 15% of the time cost of tree building. + */ + Array<_AABSPTree::Handle<T> * > valueArray; + + /** For each object in the value array, a copy of its bounds. + Packing these into an array at the node level + instead putting them in the valueArray improves + cache coherence, which is about a 3x performance + increase when performing intersection computations. + */ + Array<AABox> boundsArray; + + /** Creates node with NULL children */ + Node() { + splitAxis = Vector3::X_AXIS; + splitLocation = 0; + splitBounds = AABox(-Vector3::inf(), Vector3::inf()); + for (int i = 0; i < 2; ++i) { + child[i] = NULL; + } + } + + /** + Doesn't clone children. + */ + Node(const Node& other) : valueArray(other.valueArray), boundsArray(other.boundsArray) { + splitAxis = other.splitAxis; + splitLocation = other.splitLocation; + splitBounds = other.splitBounds; + for (int i = 0; i < 2; ++i) { + child[i] = NULL; + } + } + + /** Copies the specified subarray of pt into point, NULLs the children. + Assumes a second pass will set splitBounds. */ + Node(const Array<_AABSPTree::Handle<T> * >& pt) : valueArray(pt) { + splitAxis = Vector3::X_AXIS; + splitLocation = 0; + for (int i = 0; i < 2; ++i) { + child[i] = NULL; + } + + boundsArray.resize(valueArray.size()); + for (int i = 0; i < valueArray.size(); ++i) { + boundsArray[i] = valueArray[i]->bounds; + } + } + + /** Deletes the children (but not the values) */ + ~Node() { + for (int i = 0; i < 2; ++i) { + delete child[i]; + } + } + + /** Returns true if this node is a leaf (no children) */ + inline bool isLeaf() const { + return (child[0] == NULL) && (child[1] == NULL); + } + + + /** + Recursively appends all handles and children's handles + to the array. + */ + void getHandles(Array<_AABSPTree::Handle<T> * >& handleArray) const { + handleArray.append(valueArray); + for (int i = 0; i < 2; ++i) { + if (child[i] != NULL) { + child[i]->getHandles(handleArray); + } + } + } + + void verifyNode(const Vector3& lo, const Vector3& hi) { + // debugPrintf("Verifying: split %d @ %f [%f, %f, %f], [%f, %f, %f]\n", + // splitAxis, splitLocation, lo.x, lo.y, lo.z, hi.x, hi.y, hi.z); + + debugAssert(lo == splitBounds.low()); + debugAssert(hi == splitBounds.high()); + + for (int i = 0; i < valueArray.length(); ++i) { + const AABox& b = valueArray[i]->bounds; + debugAssert(b == boundsArray[i]); + + for(int axis = 0; axis < 3; ++axis) { + debugAssert(b.low()[axis] <= b.high()[axis]); + debugAssert(b.low()[axis] >= lo[axis]); + debugAssert(b.high()[axis] <= hi[axis]); + } + } + + if (child[0] || child[1]) { + debugAssert(lo[splitAxis] < splitLocation); + debugAssert(hi[splitAxis] > splitLocation); + } + + Vector3 newLo = lo; + newLo[splitAxis] = splitLocation; + Vector3 newHi = hi; + newHi[splitAxis] = splitLocation; + + if (child[0] != NULL) { + child[0]->verifyNode(lo, newHi); + } + + if (child[1] != NULL) { + child[1]->verifyNode(newLo, hi); + } + } + +#if 0 + /** + Stores the locations of the splitting planes (the structure but not the content) + so that the tree can be quickly rebuilt from a previous configuration without + calling balance. + */ + static void serializeStructure(const Node* n, BinaryOutput& bo) { + if (n == NULL) { + bo.writeUInt8(0); + } else { + bo.writeUInt8(1); + n->splitBounds.serialize(bo); + serialize(n->splitAxis, bo); + bo.writeFloat32(n->splitLocation); + for (int c = 0; c < 2; ++c) { + serializeStructure(n->child[c], bo); + } + } + } + + /** Clears the member table */ + static Node* deserializeStructure(BinaryInput& bi) { + if (bi.readUInt8() == 0) { + return NULL; + } else { + Node* n = new Node(); + n->splitBounds.deserialize(bi); + deserialize(n->splitAxis, bi); + n->splitLocation = bi.readFloat32(); + for (int c = 0; c < 2; ++c) { + n->child[c] = deserializeStructure(bi); + } + } + } +#endif + /** Returns the deepest node that completely contains bounds. */ + Node* findDeepestContainingNode(const AABox& bounds) { + + // See which side of the splitting plane the bounds are on + if (bounds.high()[splitAxis] < splitLocation) { + // Bounds are on the low side. Recurse into the child + // if it exists. + if (child[0] != NULL) { + return child[0]->findDeepestContainingNode(bounds); + } + } else if (bounds.low()[splitAxis] > splitLocation) { + // Bounds are on the high side, recurse into the child + // if it exists. + if (child[1] != NULL) { + return child[1]->findDeepestContainingNode(bounds); + } + } + + // There was no containing child, so this node is the + // deepest containing node. + return this; + } + + + /** Appends all members that intersect the box. + If useSphere is true, members that pass the box test + face a second test against the sphere. */ + void getIntersectingMembers( + const AABox& box, + const Sphere& sphere, + Array<T>& members, + bool useSphere) const { + + // Test all values at this node + for (int v = 0; v < boundsArray.size(); ++v) { + const AABox& bounds = boundsArray[v]; + if (bounds.intersects(box) && + (! useSphere || bounds.intersects(sphere))) { + members.append(valueArray[v]->value); + } + } + + // If the left child overlaps the box, recurse into it + if ((child[0] != NULL) && (box.low()[splitAxis] < splitLocation)) { + child[0]->getIntersectingMembers(box, sphere, members, useSphere); + } + + // If the right child overlaps the box, recurse into it + if ((child[1] != NULL) && (box.high()[splitAxis] > splitLocation)) { + child[1]->getIntersectingMembers(box, sphere, members, useSphere); + } + } + + /** + Recurse through the tree, assigning splitBounds fields. + */ + void assignSplitBounds(const AABox& myBounds) { + splitBounds = myBounds; + + AABox childBounds[2]; + myBounds.split(splitAxis, splitLocation, childBounds[0], childBounds[1]); + +# if defined(G3D_DEBUG) && defined(VERIFY_TREE) + // Verify the split + for (int v = 0; v < boundsArray.size(); ++v) { + const AABox& bounds = boundsArray[v]; + debugAssert(myBounds.contains(bounds)); + } +# endif + + for (int c = 0; c < 2; ++c) { + if (child[c]) { + child[c]->assignSplitBounds(childBounds[c]); + } + } + } + + /** Returns true if the ray intersects this node */ + bool intersects(const Ray& ray, float distance) const { + // See if the ray will ever hit this node or its children + Vector3 location; + bool alreadyInsideBounds = false; + bool rayWillHitBounds = + VMAP::MyCollisionDetection::collisionLocationForMovingPointFixedAABox( + ray.origin, ray.direction, splitBounds, location, alreadyInsideBounds); + + bool canHitThisNode = (alreadyInsideBounds || + (rayWillHitBounds && ((location - ray.origin).squaredLength() < square(distance)))); + + return canHitThisNode; + } + + template<typename RayCallback> + void intersectRay( + const Ray& ray, + RayCallback& intersectCallback, + float& distance, + bool pStopAtFirstHit, + bool intersectCallbackIsFast) const { + float enterDistance = distance; + + if (! intersects(ray, distance)) { + // The ray doesn't hit this node, so it can't hit the children of the node. + return; + } + + // Test for intersection against every object at this node. + for (int v = 0; v < valueArray.size(); ++v) { + bool canHitThisObject = true; + + if (! intersectCallbackIsFast) { + // See if + Vector3 location; + const AABox& bounds = boundsArray[v]; + bool alreadyInsideBounds = false; + bool rayWillHitBounds = + VMAP::MyCollisionDetection::collisionLocationForMovingPointFixedAABox( + ray.origin, ray.direction, bounds, location, alreadyInsideBounds); + + canHitThisObject = (alreadyInsideBounds || + (rayWillHitBounds && ((location - ray.origin).squaredLength() < square(distance)))); + } + + if (canHitThisObject) { + // It is possible that this ray hits this object. Look for the intersection using the + // callback. + const T& value = valueArray[v]->value; + intersectCallback(ray, value, pStopAtFirstHit, distance); + } + if(pStopAtFirstHit && distance < enterDistance) + return; + } + + // There are three cases to consider next: + // + // 1. the ray can start on one side of the splitting plane and never enter the other, + // 2. the ray can start on one side and enter the other, and + // 3. the ray can travel exactly down the splitting plane + + enum {NONE = -1}; + int firstChild = NONE; + int secondChild = NONE; + + if (ray.origin[splitAxis] < splitLocation) { + + // The ray starts on the small side + firstChild = 0; + + if (ray.direction[splitAxis] > 0) { + // The ray will eventually reach the other side + secondChild = 1; + } + + } else if (ray.origin[splitAxis] > splitLocation) { + + // The ray starts on the large side + firstChild = 1; + + if (ray.direction[splitAxis] < 0) { + secondChild = 0; + } + } else { + // The ray starts on the splitting plane + if (ray.direction[splitAxis] < 0) { + // ...and goes to the small side + firstChild = 0; + } else if (ray.direction[splitAxis] > 0) { + // ...and goes to the large side + firstChild = 1; + } + } + + // Test on the side closer to the ray origin. + if ((firstChild != NONE) && child[firstChild]) { + child[firstChild]->intersectRay(ray, intersectCallback, distance, pStopAtFirstHit, intersectCallbackIsFast); + if(pStopAtFirstHit && distance < enterDistance) + return; + } + + if (ray.direction[splitAxis] != 0) { + // See if there was an intersection before hitting the splitting plane. + // If so, there is no need to look on the far side and recursion terminates. + float distanceToSplittingPlane = (splitLocation - ray.origin[splitAxis]) / ray.direction[splitAxis]; + if (distanceToSplittingPlane > distance) { + // We aren't going to hit anything else before hitting the splitting plane, + // so don't bother looking on the far side of the splitting plane at the other + // child. + return; + } + } + + // Test on the side farther from the ray origin. + if ((secondChild != NONE) && child[secondChild]) { + child[secondChild]->intersectRay(ray, intersectCallback, distance, pStopAtFirstHit, intersectCallbackIsFast); + } + + } + }; + + + /** + Recursively subdivides the subarray. + + Clears the source array as soon as it is no longer needed. + + Call assignSplitBounds() on the root node after making a tree. + */ + Node* makeNode( + Array<_AABSPTree::Handle<T> * >& source, + int valuesPerNode, + int numMeanSplits, + Array<_AABSPTree::Handle<T> * >& temp) { + + Node* node = NULL; + + if (source.size() <= valuesPerNode) { + // Make a new leaf node + node = new Node(source); + + // Set the pointers in the memberTable + for (int i = 0; i < source.size(); ++i) { + memberTable.set(Member(source[i]), node); + } + source.clear(); + + } else { + // Make a new internal node + node = new Node(); + + const AABox bounds = computeBounds(source, 0, source.size() - 1); + const Vector3 extent = bounds.high() - bounds.low(); + + Vector3::Axis splitAxis = extent.primaryAxis(); + + float splitLocation; + + // Arrays for holding the children + Array<_AABSPTree::Handle<T> * > lt, gt; + + if (numMeanSplits <= 0) { + + source.medianPartition(lt, node->valueArray, gt, temp, CenterComparator(splitAxis)); + + // Choose the split location to be the center of whatever fell in the center + splitLocation = node->valueArray[0]->center[splitAxis]; + + // Some of the elements in the lt or gt array might really overlap the split location. + // Move them as needed. + for (int i = 0; i < lt.size(); ++i) { + const AABox& bounds = lt[i]->bounds; + if ((bounds.low()[splitAxis] <= splitLocation) && (bounds.high()[splitAxis] >= splitLocation)) { + node->valueArray.append(lt[i]); + // Remove this element and process the new one that + // is swapped in in its place. + lt.fastRemove(i); --i; + } + } + + for (int i = 0; i < gt.size(); ++i) { + const AABox& bounds = gt[i]->bounds; + if ((bounds.low()[splitAxis] <= splitLocation) && (bounds.high()[splitAxis] >= splitLocation)) { + node->valueArray.append(gt[i]); + // Remove this element and process the new one that + // is swapped in in its place. + gt.fastRemove(i); --i; + } + } + + if ((node->valueArray.size() > (source.size() / 2)) && + (source.size() > 6)) { + // This was a bad partition; we ended up putting the splitting plane right in the middle of most of the + // objects. We could try to split on a different axis, or use a different partition (e.g., the extents mean, + // or geometric mean). This implementation falls back on the extents mean, since that case is already handled + // below. + numMeanSplits = 1; + } + } + + // Note: numMeanSplits may have been increased by the code in the previous case above in order to + // force a re-partition. + + if (numMeanSplits > 0) { + // Split along the mean + splitLocation = (bounds.high()[splitAxis] + + bounds.low()[splitAxis]) / 2.0; + + source.partition(NULL, lt, node->valueArray, gt, Comparator(splitAxis, splitLocation)); + + // The Comparator ensures that elements are strictly on the correct side of the split + } + + +# if defined(G3D_DEBUG) && defined(VERIFY_TREE) + debugAssert(lt.size() + node->valueArray.size() + gt.size() == source.size()); + // Verify that all objects ended up on the correct side of the split. + // (i.e., make sure that the Array partition was correct) + for (int i = 0; i < lt.size(); ++i) { + const AABox& bounds = lt[i]->bounds; + debugAssert(bounds.high()[splitAxis] < splitLocation); + } + + for (int i = 0; i < gt.size(); ++i) { + const AABox& bounds = gt[i]->bounds; + debugAssert(bounds.low()[splitAxis] > splitLocation); + } + + for (int i = 0; i < node->valueArray.size(); ++i) { + const AABox& bounds = node->valueArray[i]->bounds; + debugAssert(bounds.high()[splitAxis] >= splitLocation); + debugAssert(bounds.low()[splitAxis] <= splitLocation); + } +# endif + + // The source array is no longer needed + source.clear(); + + node->splitAxis = splitAxis; + node->splitLocation = splitLocation; + + // Update the bounds array and member table + node->boundsArray.resize(node->valueArray.size()); + for (int i = 0; i < node->valueArray.size(); ++i) { + _AABSPTree::Handle<T> * v = node->valueArray[i]; + node->boundsArray[i] = v->bounds; + memberTable.set(Member(v), node); + } + + if (lt.size() > 0) { + node->child[0] = makeNode(lt, valuesPerNode, numMeanSplits - 1, temp); + } + + if (gt.size() > 0) { + node->child[1] = makeNode(gt, valuesPerNode, numMeanSplits - 1, temp); + } + + } + + return node; + } + + /** + Recursively clone the passed in node tree, setting + pointers for members in the memberTable as appropriate. + called by the assignment operator. + */ + Node* cloneTree(Node* src) { + Node* dst = new Node(*src); + + // Make back pointers + for (int i = 0; i < dst->valueArray.size(); ++i) { + memberTable.set(Member(dst->valueArray[i]), dst); + } + + // Clone children + for (int i = 0; i < 2; ++i) { + if (src->child[i] != NULL) { + dst->child[i] = cloneTree(src->child[i]); + } + } + + return dst; + } + + /** + Wrapper for a Handle; used to create a memberTable that acts like Table<Handle, Node*> but + stores only Handle* internally to avoid memory copies. + */ + typedef _internal::Indirector<_AABSPTree::Handle<T> > Member; + + typedef Table<Member, Node*> MemberTable; + + /** Maps members to the node containing them */ + MemberTable memberTable; + + Node* root; + +public: + + /** To construct a balanced tree, insert the elements and then call + AABSPTree::balance(). */ + AABSPTree() : root(NULL) {} + + + AABSPTree(const AABSPTree& src) : root(NULL) { + *this = src; + } + + + AABSPTree& operator=(const AABSPTree& src) { + delete root; + // Clone tree takes care of filling out the memberTable. + root = cloneTree(src.root); + return *this; + } + + + ~AABSPTree() { + clear(); + } + + /** + Throws out all elements of the set. + */ + void clear() { + typedef typename Table<_internal::Indirector<_AABSPTree::Handle<T> >, Node* >::Iterator It; + + // Delete all handles stored in the member table + It cur = memberTable.begin(); + It end = memberTable.end(); + while (cur != end) { + delete cur->key.handle; + cur->key.handle = NULL; + ++cur; + } + memberTable.clear(); + + // Delete the tree structure itself + delete root; + root = NULL; + } + + size_t size() const { + return memberTable.size(); + } + + /** + Inserts an object into the set if it is not + already present. O(log n) time. Does not + cause the tree to be balanced. + */ + void insert(const T& value) { + if (contains(value)) { + // Already in the set + return; + } + + _AABSPTree::Handle<T>* h = new _AABSPTree::Handle<T>(value); + + if (root == NULL) { + // This is the first node; create a root node + root = new Node(); + } + + Node* node = root->findDeepestContainingNode(h->bounds); + + // Insert into the node + node->valueArray.append(h); + node->boundsArray.append(h->bounds); + + // Insert into the node table + Member m(h); + memberTable.set(m, node); + } + + /** Inserts each elements in the array in turn. If the tree + begins empty (no structure and no elements), this is faster + than inserting each element in turn. You still need to balance + the tree at the end.*/ + void insert(const Array<T>& valueArray) { + if (root == NULL) { + // Optimized case for an empty tree; don't bother + // searching or reallocating the root node's valueArray + // as we incrementally insert. + root = new Node(); + root->valueArray.resize(valueArray.size()); + root->boundsArray.resize(root->valueArray.size()); + for (int i = 0; i < valueArray.size(); ++i) { + // Insert in opposite order so that we have the exact same + // data structure as if we inserted each (i.e., order is reversed + // from array). + _AABSPTree::Handle<T>* h = new _AABSPTree::Handle<T>(valueArray[i]); + int j = valueArray.size() - i - 1; + root->valueArray[j] = h; + root->boundsArray[j] = h->bounds; + memberTable.set(Member(h), root); + } + + } else { + // Insert at appropriate tree depth. + for (int i = 0; i < valueArray.size(); ++i) { + insert(valueArray[i]); + } + } + } + + + /** + Returns true if this object is in the set, otherwise + returns false. O(1) time. + */ + bool contains(const T& value) { + // Temporarily create a handle and member + _AABSPTree::Handle<T> h(value); + return memberTable.containsKey(Member(&h)); + } + + + /** + Removes an object from the set in O(1) time. + It is an error to remove members that are not already + present. May unbalance the tree. + + Removing an element never causes a node (split plane) to be removed... + nodes are only changed when the tree is rebalanced. This behavior + is desirable because it allows the split planes to be serialized, + and then deserialized into an empty tree which can be repopulated. + */ + void remove(const T& value) { + debugAssertM(contains(value), + "Tried to remove an element from a " + "AABSPTree that was not present"); + + // Get the list of elements at the node + _AABSPTree::Handle<T> h(value); + Member m(&h); + + Array<_AABSPTree::Handle<T> * >& list = memberTable[m]->valueArray; + + _AABSPTree::Handle<T>* ptr = NULL; + + // Find the element and remove it + for (int i = list.length() - 1; i >= 0; --i) { + if (list[i]->value == value) { + // This was the element. Grab the pointer so that + // we can delete it below + ptr = list[i]; + + // Remove the handle from the node + list.fastRemove(i); + + // Remove the corresponding bounds + memberTable[m]->boundsArray.fastRemove(i); + break; + } + } + + // Remove the member + memberTable.remove(m); + + // Delete the handle data structure + delete ptr; + ptr = NULL; + } + + + /** + If the element is in the set, it is removed. + The element is then inserted. + + This is useful when the == and hashCode methods + on <I>T</I> are independent of the bounds. In + that case, you may call update(v) to insert an + element for the first time and call update(v) + again every time it moves to keep the tree + up to date. + */ + void update(const T& value) { + if (contains(value)) { + remove(value); + } + insert(value); + } + + + /** + Rebalances the tree (slow). Call when objects + have moved substantially from their original positions + (which unbalances the tree and causes the spatial + queries to be slow). + + @param valuesPerNode Maximum number of elements to put at + a node. + + @param numMeanSplits numMeanSplits = 0 gives a + fully axis aligned BSP-tree, where the balance operation attempts to balance + the tree so that every splitting plane has an equal number of left + and right children (i.e. it is a <B>median</B> split along that axis). + This tends to maximize average performance. + + You can override this behavior by + setting a number of <B>mean</B> (average) splits. numMeanSplits = MAX_INT + creates a full oct-tree, which tends to optimize peak performance at the expense of + average performance. It tends to have better clustering behavior when + members are not uniformly distributed. + */ + void balance(int valuesPerNode = 5, int numMeanSplits = 3) { + if (root == NULL) { + // Tree is empty + return; + } + + // Get all handles and delete the old tree structure + Node* oldRoot = root; + for (int c = 0; c < 2; ++c) { + if (root->child[c] != NULL) { + root->child[c]->getHandles(root->valueArray); + + // Delete the child; this will delete all structure below it + delete root->child[c]; + root->child[c] = NULL; + } + } + + Array<_AABSPTree::Handle<T> * > temp; + // Make a new root. Work with a copy of the value array because + // makeNode clears the source array as it progresses + Array<_AABSPTree::Handle<T> * > copy(oldRoot->valueArray); + root = makeNode(copy, valuesPerNode, numMeanSplits, temp); + + // Throw away the old root node + delete oldRoot; + oldRoot = NULL; + + // Walk the tree, assigning splitBounds. We start with unbounded + // space. This will override the current member table. + root->assignSplitBounds(AABox::maxFinite()); + +# ifdef _DEBUG + // Ensure that the balanced tree is till correct + root->verifyNode(Vector3::minFinite(), Vector3::maxFinite()); +# endif + } + +protected: + + /** + @param parentMask The mask that this node returned from culledBy. + */ + static void getIntersectingMembers( + const Array<Plane>& plane, + Array<T>& members, + Node* node, + uint32 parentMask) { + + int dummy; + + if (parentMask == 0) { + // None of these planes can cull anything + for (int v = node->valueArray.size() - 1; v >= 0; --v) { + members.append(node->valueArray[v]->value); + } + + // Iterate through child nodes + for (int c = 0; c < 2; ++c) { + if (node->child[c]) { + getIntersectingMembers(plane, members, node->child[c], 0); + } + } + } else { + + // Test values at this node against remaining planes + for (int v = node->boundsArray.size() - 1; v >= 0; --v) { + if (! node->boundsArray[v].culledBy(plane, dummy, parentMask)) { + members.append(node->valueArray[v]->value); + } + } + + uint32 childMask = 0xFFFFFF; + + // Iterate through child nodes + for (int c = 0; c < 2; ++c) { + if (node->child[c] && + ! node->child[c]->splitBounds.culledBy(plane, dummy, parentMask, childMask)) { + // This node was not culled + getIntersectingMembers(plane, members, node->child[c], childMask); + } + } + } + } + +public: + + /** + Returns all members inside the set of planes. + @param members The results are appended to this array. + */ + void getIntersectingMembers(const Array<Plane>& plane, Array<T>& members) const { + if (root == NULL) { + return; + } + + getIntersectingMembers(plane, members, root, 0xFFFFFF); + } + + /** + Typically used to find all visible + objects inside the view frustum (see also GCamera::getClipPlanes)... i.e. all objects + <B>not<B> culled by frustum. + + Example: + <PRE> + Array<Object*> visible; + tree.getIntersectingMembers(camera.frustum(), visible); + // ... Draw all objects in the visible array. + </PRE> + @param members The results are appended to this array. + */ + void getIntersectingMembers(const GCamera::Frustum& frustum, Array<T>& members) const { + Array<Plane> plane; + + for (int i = 0; i < frustum.faceArray.size(); ++i) { + plane.append(frustum.faceArray[i].plane); + } + + getIntersectingMembers(plane, members); + } + + /** + C++ STL style iterator variable. See beginBoxIntersection(). + The iterator overloads the -> (dereference) operator, so this + acts like a pointer to the current member. + */ + // This iterator turns Node::getIntersectingMembers into a + // coroutine. It first translates that method from recursive to + // stack based, then captures the system state (analogous to a Scheme + // continuation) after each element is appended to the member array, + // and allowing the computation to be restarted. + class BoxIntersectionIterator { + private: + friend class AABSPTree<T>; + + /** True if this is the "end" iterator instance */ + bool isEnd; + + /** The box that we're testing against. */ + AABox box; + + /** Node that we're currently looking at. Undefined if isEnd + is true. */ + Node* node; + + /** Nodes waiting to be processed */ + // We could use backpointers within the tree and careful + // state management to avoid ever storing the stack-- but + // it is much easier this way and only inefficient if the + // caller uses post increment (which they shouldn't!). + Array<Node*> stack; + + /** The next index of current->valueArray to return. + Undefined when isEnd is true.*/ + int nextValueArrayIndex; + + BoxIntersectionIterator() : isEnd(true) {} + + BoxIntersectionIterator(const AABox& b, const Node* root) : + isEnd(root == NULL), box(b), + node(const_cast<Node*>(root)), nextValueArrayIndex(-1) { + + // We intentionally start at the "-1" index of the current + // node so we can use the preincrement operator to move + // ourselves to element 0 instead of repeating all of the + // code from the preincrement method. Note that this might + // cause us to become the "end" instance. + ++(*this); + } + + public: + + inline bool operator!=(const BoxIntersectionIterator& other) const { + return ! (*this == other); + } + + bool operator==(const BoxIntersectionIterator& other) const { + if (isEnd) { + return other.isEnd; + } else if (other.isEnd) { + return false; + } else { + // Two non-end iterators; see if they match. This is kind of + // silly; users shouldn't call == on iterators in general unless + // one of them is the end iterator. + if ((box != other.box) || (node != other.node) || + (nextValueArrayIndex != other.nextValueArrayIndex) || + (stack.length() != other.stack.length())) { + return false; + } + + // See if the stacks are the same + for (int i = 0; i < stack.length(); ++i) { + if (stack[i] != other.stack[i]) { + return false; + } + } + + // We failed to find a difference; they must be the same + return true; + } + } + + /** + Pre increment. + */ + BoxIntersectionIterator& operator++() { + ++nextValueArrayIndex; + + bool foundIntersection = false; + while (! isEnd && ! foundIntersection) { + + // Search for the next node if we've exhausted this one + while ((! isEnd) && (nextValueArrayIndex >= node->valueArray.length())) { + // If we entered this loop, then the iterator has exhausted the elements at + // node (possibly because it just switched to a child node with no members). + // This loop continues until it finds a node with members or reaches + // the end of the whole intersection search. + + // If the right child overlaps the box, push it onto the stack for + // processing. + if ((node->child[1] != NULL) && + (box.high()[node->splitAxis] > node->splitLocation)) { + stack.push(node->child[1]); + } + + // If the left child overlaps the box, push it onto the stack for + // processing. + if ((node->child[0] != NULL) && + (box.low()[node->splitAxis] < node->splitLocation)) { + stack.push(node->child[0]); + } + + if (stack.length() > 0) { + // Go on to the next node (which may be either one of the ones we + // just pushed, or one from farther back the tree). + node = stack.pop(); + nextValueArrayIndex = 0; + } else { + // That was the last node; we're done iterating + isEnd = true; + } + } + + // Search for the next intersection at this node until we run out of children + while (! isEnd && ! foundIntersection && (nextValueArrayIndex < node->valueArray.length())) { + if (box.intersects(node->boundsArray[nextValueArrayIndex])) { + foundIntersection = true; + } else { + ++nextValueArrayIndex; + // If we exhaust this node, we'll loop around the master loop + // to find a new node. + } + } + } + + return *this; + } + + private: + /** + Post increment (much slower than preincrement!). Intentionally overloaded to preclude accidentally slow code. + */ + BoxIntersectionIterator operator++(int); + /*{ + BoxIntersectionIterator old = *this; + ++this; + return old; + }*/ + + public: + + /** Overloaded dereference operator so the iterator can masquerade as a pointer + to a member */ + const T& operator*() const { + alwaysAssertM(! isEnd, "Can't dereference the end element of an iterator"); + return node->valueArray[nextValueArrayIndex]->value; + } + + /** Overloaded dereference operator so the iterator can masquerade as a pointer + to a member */ + T const * operator->() const { + alwaysAssertM(! isEnd, "Can't dereference the end element of an iterator"); + return &(stack.last()->valueArray[nextValueArrayIndex]->value); + } + + /** Overloaded cast operator so the iterator can masquerade as a pointer + to a member */ + operator T*() const { + alwaysAssertM(! isEnd, "Can't dereference the end element of an iterator"); + return &(stack.last()->valueArray[nextValueArrayIndex]->value); + } + }; + + + /** + Iterates through the members that intersect the box + */ + BoxIntersectionIterator beginBoxIntersection(const AABox& box) const { + return BoxIntersectionIterator(box, root); + } + + BoxIntersectionIterator endBoxIntersection() const { + // The "end" iterator instance + return BoxIntersectionIterator(); + } + + /** + Appends all members whose bounds intersect the box. + See also AABSPTree::beginBoxIntersection. + */ + void getIntersectingMembers(const AABox& box, Array<T>& members) const { + if (root == NULL) { + return; + } + root->getIntersectingMembers(box, Sphere(Vector3::zero(), 0), members, false); + } + + + /** + Invoke a callback for every member along a ray until the closest intersection is found. + + @param callback either a function or an instance of a class with an overloaded operator() of the form: + + <code>void callback(const Ray& ray, const T& object, float& distance)</code>. If the ray hits the object + before travelling distance <code>distance</code>, updates <code>distance</code> with the new distance to + the intersection, otherwise leaves it unmodified. A common example is: + + <pre> + class Entity { + public: + + void intersect(const Ray& ray, float& maxDist, Vector3& outLocation, Vector3& outNormal) { + float d = maxDist; + + // ... search for intersection distance d + + if ((d > 0) && (d < maxDist)) { + // Intersection occured + maxDist = d; + outLocation = ...; + outNormal = ...; + } + } + }; + + // Finds the surface normal and location of the first intersection with the scene + class Intersection { + public: + Entity* closestEntity; + Vector3 hitLocation; + Vector3 hitNormal; + + void operator()(const Ray& ray, const Entity* entity, float& distance) { + entity->intersect(ray, distance, hitLocation, hitNormal); + } + }; + + AABSPTree<Entity*> scene; + + Intersection intersection; + float distance = inf(); + scene.intersectRay(camera.worldRay(x, y), intersection, distance); + </pre> + + + @param distance When the method is invoked, this is the maximum distance that the tree should search for an intersection. + On return, this is set to the distance to the first intersection encountered. + + @param intersectCallbackIsFast If false, each object's bounds are tested before the intersectCallback is invoked. + If the intersect callback runs at the same speed or faster than AABox-ray intersection, set this to true. + */ + template<typename RayCallback> + void intersectRay( + const Ray& ray, + RayCallback& intersectCallback, + float& distance, + bool pStopAtFirstHit, + bool intersectCallbackIsFast = false) const { + + root->intersectRay(ray, intersectCallback, distance, pStopAtFirstHit, intersectCallbackIsFast); + + } + + + /** + @param members The results are appended to this array. + */ + void getIntersectingMembers(const Sphere& sphere, Array<T>& members) const { + if (root == NULL) { + return; + } + + AABox box; + sphere.getBounds(box); + root->getIntersectingMembers(box, sphere, members, true); + + } +#if 0 + /** + Stores the locations of the splitting planes (the structure but not the content) + so that the tree can be quickly rebuilt from a previous configuration without + calling balance. + */ + void serializeStructure(BinaryOutput& bo) const { + Node::serializeStructure(root, bo); + } + + /** Clears the member table */ + void deserializeStructure(BinaryInput& bi) { + clear(); + root = Node::deserializeStructure(bi); + } +#endif + /** + Returns an array of all members of the set. See also AABSPTree::begin. + */ + void getMembers(Array<T>& members) const { + Array<Member> temp; + memberTable.getKeys(temp); + for (int i = 0; i < temp.size(); ++i) { + members.append(temp[i].handle->value); + } + } + + + /** + C++ STL style iterator variable. See begin(). + Overloads the -> (dereference) operator, so this acts like a pointer + to the current member. + */ + class Iterator { + private: + friend class AABSPTree<T>; + + // Note: this is a Table iterator, we are currently defining + // Set iterator + typename Table<Member, Node*>::Iterator it; + + Iterator(const typename Table<Member, Node*>::Iterator& it) : it(it) {} + + public: + + inline bool operator!=(const Iterator& other) const { + return !(*this == other); + } + + bool operator==(const Iterator& other) const { + return it == other.it; + } + + /** + Pre increment. + */ + Iterator& operator++() { + ++it; + return *this; + } + + private: + /** + Post increment (slower than preincrement). Intentionally unimplemented to prevent slow code. + */ + Iterator operator++(int);/* { + Iterator old = *this; + ++(*this); + return old; + }*/ + public: + + const T& operator*() const { + return it->key.handle->value; + } + + T* operator->() const { + return &(it->key.handle->value); + } + + operator T*() const { + return &(it->key.handle->value); + } + }; + + + /** + C++ STL style iterator method. Returns the first member. + Use preincrement (++entry) to get to the next element (iteration + order is arbitrary). + Do not modify the set while iterating. + */ + Iterator begin() const { + return Iterator(memberTable.begin()); + } + + + /** + C++ STL style iterator method. Returns one after the last iterator + element. + */ + Iterator end() const { + return Iterator(memberTable.end()); + } +}; + +} + +#endif + + + diff --git a/src/shared/vmap/BaseModel.cpp b/src/shared/vmap/BaseModel.cpp new file mode 100644 index 00000000000..9a1e9fd1002 --- /dev/null +++ b/src/shared/vmap/BaseModel.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "BaseModel.h" +#include "VMapTools.h" + +using namespace G3D; + +namespace VMAP +{ + //========================================================== + + void BaseModel::getMember(Array<TriangleBox>& pMembers) + { + for(unsigned int i=0; i<iNTriangles; i++) + { + pMembers.append(iTriangles[i]); + } + } + + //========================================================== + BaseModel::BaseModel(unsigned int pNNodes, unsigned int pNTriangles) + { + init(pNNodes, pNTriangles); + }; + + //========================================================== + + void BaseModel::init(unsigned int pNNodes, unsigned int pNTriangles) + { + iNNodes = pNNodes; + iNTriangles = pNTriangles; + iTriangles = 0; + iTreeNodes = 0; + if(iNNodes >0) iTreeNodes = new TreeNode[iNNodes]; + if(iNTriangles >0) iTriangles = new TriangleBox[iNTriangles]; + } + + //========================================================== + + void BaseModel::free() + { + if(getTriangles() != 0) delete [] getTriangles(); setNTriangles(0); + if(getTreeNodes() != 0) delete [] getTreeNodes(); setNNodes(0); + } + + //========================================================== + + void BaseModel::intersect(const G3D::AABox& pBox, const G3D::Ray& pRay, float& pMaxDist, G3D::Vector3& pOutLocation, G3D::Vector3& /*pOutNormal*/) const + { + bool isInside = false; + + float d = MyCollisionDetection::collisionLocationForMovingPointFixedAABox( + pRay.origin, pRay.direction, + pBox, + pOutLocation, isInside); + if (!isInside && ((d > 0) && (d < pMaxDist))) + { + pMaxDist = d; + } + } + + //========================================================== + + bool BaseModel::intersect(const G3D::AABox& pBox, const G3D::Ray& pRay, float& pMaxDist) const + { + // See if the ray will ever hit this node or its children + Vector3 location; + bool alreadyInsideBounds = false; + bool rayWillHitBounds = + MyCollisionDetection::collisionLocationForMovingPointFixedAABox( + pRay.origin, pRay.direction, pBox, location, alreadyInsideBounds); + + bool canHitThisNode = (alreadyInsideBounds || + (rayWillHitBounds && ((location - pRay.origin).squaredLength() < (pMaxDist * pMaxDist)))); + + return canHitThisNode; + } + +} // VMAP diff --git a/src/shared/vmap/BaseModel.h b/src/shared/vmap/BaseModel.h new file mode 100644 index 00000000000..78ded7811b6 --- /dev/null +++ b/src/shared/vmap/BaseModel.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _BASEMODEL_H_ +#define _BASEMODEL_H_ + +#include <G3D/AABox.h> +#include <G3D/Vector3.h> + +#include "ShortVector.h" +#include "ShortBox.h" +#include "TreeNode.h" + +/** +A model is based on triangles. To be able to check intersection we need a BSP-Tree. +This Class holds the array of triangles as well as the management information for the BSP-Tree. +Both are stored in static array and index information is used instead of pointers. +Therefore we can load the whole object as a binary block. + +The vectors are relative to a base position. +*/ + +namespace VMAP +{ + + class BaseModel + { + protected: + TriangleBox *iTriangles; + TreeNode *iTreeNodes; + unsigned int iNTriangles; + unsigned int iNNodes; + G3D::Vector3 iBasePosition; + public: + BaseModel() { iNTriangles = 0; iNNodes = 0; iTriangles = 0; iTreeNodes = 0;}; + BaseModel(unsigned int pNNodes , TreeNode* pTreeNode, unsigned int pNTriangles, TriangleBox* pTriangleBox) + { + iNNodes = pNNodes; iNTriangles = pNTriangles; iTriangles = pTriangleBox; iTreeNodes = pTreeNode; + }; + BaseModel(unsigned int pNNodes, unsigned int pNTriangles); + + // destructor does nothing ! The subclass controles the array memory and knows when to free it + ~BaseModel() {} + + void free(); + void init(unsigned int pNNodes, unsigned int pNTriangles); + + void getMember(G3D::Array<TriangleBox>& pMembers); + + inline const TriangleBox& getTriangle(int pPos) const { return(iTriangles[pPos]); } + inline TriangleBox& getTriangle(int pPos) { return(iTriangles[pPos]); } + + inline void setTriangle(const TriangleBox& pTriangleBox, int pPos) { iTriangles[pPos] = pTriangleBox; } + + inline const TreeNode& getTreeNode(int pPos) const { return(getTreeNodes()[pPos]); } + inline TreeNode& getTreeNode(int pPos) { return(getTreeNodes()[pPos]); } + + inline void setTreeNode(const TreeNode& pTreeNode, int pPos) { getTreeNodes()[pPos] = pTreeNode; } + + inline void setBasePosition(const G3D::Vector3& pBasePosition) { iBasePosition = pBasePosition; } + + inline const G3D::Vector3& getBasePosition() const { return(iBasePosition); } + + inline unsigned int getNNodes() const { return(iNNodes); } + inline unsigned int getNTriangles() const { return(iNTriangles); } + + inline void setNNodes(unsigned int pNNodes) { iNNodes = pNNodes; } + inline void setNTriangles(unsigned int pNTriangles) { iNTriangles = pNTriangles; } + + inline void setTriangleArray(TriangleBox *pGlobalTriangleArray ) { iTriangles = pGlobalTriangleArray ; } + inline void setTreeNodeArray(TreeNode *pGlobalTreeNodeArray ) { iTreeNodes = pGlobalTreeNodeArray ; } + + inline TriangleBox* getTriangles() const { return(iTriangles); } + + inline TreeNode* getTreeNodes() const{ return(iTreeNodes); } + + inline size_t getMemUsage() { return(iNTriangles * sizeof(TriangleBox) + iNNodes * sizeof(TreeNode) + sizeof(BaseModel)); } + + void intersect(const G3D::AABox& pBox, const G3D::Ray& pRay, float& pMaxDist, G3D::Vector3& pOutLocation, G3D::Vector3& pOutNormal) const; + bool intersect(const G3D::AABox& pBox, const G3D::Ray& pRay, float& pMaxDist) const; + }; + +} +#endif /*BASEMODEL_H_*/ diff --git a/src/shared/vmap/CoordModelMapping.cpp b/src/shared/vmap/CoordModelMapping.cpp new file mode 100644 index 00000000000..0ea0261c894 --- /dev/null +++ b/src/shared/vmap/CoordModelMapping.cpp @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "CoordModelMapping.h" + +#include <string.h> + +using namespace G3D; + +namespace VMAP +{ + + //============================================================ + //============================================================ + + void CMappingEntry::addFilename(char *pName) + { + std::string name = std::string(pName); + if(!iFilenames.contains(name)) + iFilenames.append(std::string(pName)); + } + + //============================================================ + + const std::string CMappingEntry::getKeyString() const + { + return(CMappingEntry::getKeyString(iMapId,xPos, yPos)); + } + + //============================================================ + //============================================================ + //============================================================ + + CoordModelMapping::~CoordModelMapping() + { + Array<std::string> keys = iMapObjectFiles.getKeys(); + for (int k = 0; k < keys.length(); k++) + { + CMappingEntry *value = getCMappingEntry(keys[k]); + if(value != 0) + { + iMapObjectFiles.remove(keys[k]); + delete value; + } + } + } + + //============================================================ + + int findPosChar(const char *namebuffer, char pSearch, int pCount) + { + int result = -1; + int pos=0; + while(namebuffer[pos] != 0) + { + if(namebuffer[pos] == pSearch) + { + --pCount; + } + if(pCount == 0) + { + result = pos; + break; + } + ++pos; + } + return result; + } + //============================================================ + bool CoordModelMapping::readCoordinateMapping(const std::string& pDirectoryFileName) + { + FILE *f = fopen(pDirectoryFileName.c_str(), "rb"); + bool result = false; + char buffer[500+1]; + + if(f) + { + result = true; + CMappingEntry* cMappingEntry; + while(fgets(buffer, 500, f)) + { + //char namebuffer[500]; + char positionbuffer[500]; + int xpos, ypos, noVec; + float scale; + xpos = ypos = noVec = 0; + + //sscanf(buffer, "%d %d %s %s %f %d", &xpos, &ypos, namebuffer,positionbuffer, &scale, &noVec); + + // this is ugly, but the format has no read delimiter and a space could be in the first part of the name + int nameStart = findPosChar(buffer, ' ', 2);// find the 2. space + if(nameStart > -1 && (iFilterMethod == NULL || (*iFilterMethod)(buffer))) + { + ++nameStart; + // find the 1. / (now a space only can be found at the end of the name) + int nameEnd = nameStart + findPosChar(&buffer[nameStart], '/', 1); + // find the 1. space (after the name) + nameEnd += findPosChar(&buffer[nameEnd], ' ', 1); + buffer[nameEnd] = 0; // terminate the name + + sscanf(buffer, "%d %d", &xpos, &ypos); + sscanf(&buffer[nameEnd+1], "%s %f %d", positionbuffer, &scale, &noVec); + unsigned int mapId = getMapIdFromFilename(std::string(&buffer[nameStart])); + if(!iMapIds.contains(mapId)) + { + iMapIds.append(mapId); + } + if(!isWorldAreaMap(mapId)) + { + xpos = 0; // store all files under the groupKey + ypos = 0; + } + + std::string key = CMappingEntry::getKeyString(mapId, xpos, ypos); + cMappingEntry = getCMappingEntry(key); + if(cMappingEntry == 0) + { + cMappingEntry = new CMappingEntry(mapId, xpos, ypos); + addCMappingEntry(cMappingEntry); + } + char namebuffer2[500]; + sprintf(namebuffer2, "%d %s#%s_%f", noVec, &buffer[nameStart], positionbuffer, scale); + cMappingEntry->addFilename(namebuffer2); + //break; + } + } + fclose(f); + } + return result; + } + + //============================================================ + + const NameCollection CoordModelMapping::getFilenamesForCoordinate(unsigned int pMapId, int xPos, int yPos) + { + NameCollection result; + Array<std::string> rawNames; + + CMappingEntry *entry = getCMappingEntry(CMappingEntry::getKeyString(pMapId, xPos, yPos)); + if(entry != 0) + { + rawNames = entry->getFilenames(); + + int pos = 0; + while(pos < rawNames.size()) + { + char namebuffer[500]; + int noVerc; + int startName = findPosChar(rawNames[pos].c_str(), ' ', 1) + 1; + int endName = (int) rawNames[pos].length(); + sscanf(rawNames[pos].c_str(), "%d", &noVerc); + memcpy(namebuffer, &rawNames[pos].c_str()[startName], endName-startName); + namebuffer[endName-startName] = 0; + sscanf(rawNames[pos].c_str(), "%d", &noVerc); + std::string modelPosFileName = std::string(namebuffer); + if(noVerc > MIN_VERTICES_FOR_OWN_CONTAINER_FILE) + { + result.appendToSingle(modelPosFileName); + } + else + { + result.appendToMain(modelPosFileName); + } + ++pos; + } + } + return result; + } + + //================================================================= + +} diff --git a/src/shared/vmap/CoordModelMapping.h b/src/shared/vmap/CoordModelMapping.h new file mode 100644 index 00000000000..908c56e66dd --- /dev/null +++ b/src/shared/vmap/CoordModelMapping.h @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _COORDMODELMAPPING_H_ +#define _COORDMODELMAPPING_H_ + +#include <G3D/Table.h> +#include <G3D/Array.h> + +/** +This Class is a helper Class to convert the raw vector data into BSP-Trees. +We read the directory file of the raw data output and build logical groups. +Models with a lot of vectors are not merged into a resulting model, but separated into an additional file. +*/ + +namespace VMAP +{ + + #define MIN_VERTICES_FOR_OWN_CONTAINER_FILE 65000 + + // if we are in an instance + #define MIN_INST_VERTICES_FOR_OWN_CONTAINER_FILE 40000 + + //===================================================== + class NameCollection + { + public: + G3D::Array<std::string> iMainFiles; + G3D::Array<std::string> iSingeFiles; + + void appendToMain(std::string pStr) { iMainFiles.append(pStr); } + void appendToSingle(std::string pStr) { iSingeFiles.append(pStr); } + + size_t size() { return (iMainFiles.size() + iSingeFiles.size()); } + }; + + //===================================================== + + class CMappingEntry + { + private: + int xPos; + int yPos; + unsigned int iMapId; + G3D::Array<std::string> iFilenames; + + public: + CMappingEntry() { }; + CMappingEntry(unsigned int pMapId, const int pXPos, const int pYPos) + { + iMapId = pMapId; + xPos = pXPos; yPos = pYPos; + }; + ~CMappingEntry() {}; + + void addFilename(char *pName); + const std::string getKeyString() const; + inline const G3D::Array<std::string>& getFilenames() const { return(iFilenames); } + + static const std::string getKeyString(unsigned int pMapId, int pXPos, int pYPos) + { + char b[100]; + sprintf(b,"%03u_%d_%d", pMapId, pXPos, pYPos); + return(std::string(b)); + } + + }; + + //===================================================== + + class CoordModelMapping + { + private: + G3D::Table<std::string, CMappingEntry *> iMapObjectFiles; + G3D::Table<std::string, std::string> iProcesseSingleFiles; + G3D::Array<unsigned int> iMapIds; + G3D::Array<unsigned int> iWorldAreaGroups; + bool (*iFilterMethod)(char *pName); + + inline void addCMappingEntry(CMappingEntry* pCMappingEntry) + { + iMapObjectFiles.set(pCMappingEntry->getKeyString(), pCMappingEntry); + } + + inline CMappingEntry* getCMappingEntry(const std::string& pKey) + { + if(iMapObjectFiles.containsKey(pKey)) + return(iMapObjectFiles.get(pKey)); + else + return 0; + } + + public: + CoordModelMapping() { iFilterMethod = NULL; } + virtual ~CoordModelMapping(); + + bool readCoordinateMapping(const std::string& pDirectoryFileName); + + const NameCollection getFilenamesForCoordinate(unsigned int pMapId, int xPos, int yPos); + + static unsigned int getMapIdFromFilename(std::string pName) + { + size_t spos; + + spos = pName.find_last_of('/'); + std::string basename = pName.substr(0, spos); + spos = basename.find_last_of('/'); + std::string groupname = basename.substr(spos+1, basename.length()); + unsigned int mapId = atoi(groupname.c_str()); + return(mapId); + } + + const G3D::Array<unsigned int>& getMaps() const { return iMapIds; } + inline bool isAlreadyProcessedSingleFile(std::string pName) { return(iProcesseSingleFiles.containsKey(pName)); } + inline void addAlreadyProcessedSingleFile(std::string pName) { iProcesseSingleFiles.set(pName,pName); } + + inline void addWorldAreaMap(unsigned int pMapId) + { + if(!iWorldAreaGroups.contains(pMapId)) + { + iWorldAreaGroups.append(pMapId); + } + } + inline bool isWorldAreaMap(unsigned int pMapId) { return(iWorldAreaGroups.contains(pMapId)); } + void setModelNameFilterMethod(bool (*pFilterMethod)(char *pName)) { iFilterMethod = pFilterMethod; } + + }; +} +#endif /*_COORDMODELMAPPING_H_*/ diff --git a/src/shared/vmap/DebugCmdLogger.cpp b/src/shared/vmap/DebugCmdLogger.cpp new file mode 100644 index 00000000000..56a5d3ffd2a --- /dev/null +++ b/src/shared/vmap/DebugCmdLogger.cpp @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "DebugCmdLogger.h" + +using namespace G3D; + +namespace VMAP +{ + + bool CommandFileRW::appendCmd(const Command& +#ifdef _DEBUG + pCommand +#endif + ) + { + #ifdef _DEBUG + bool result = false; + if(iWritingEnabled || pCommand.isCoreCmd()) + { + FILE* f = fopen(iFileName.c_str(), "ab"); + if(f) + { + result = true; + if(fwrite(&pCommand, sizeof(Command), 1, f) != 1) { result = false; } + fclose(f); + } + } + else + { + result = true; + } + return result; + #else + return true; + #endif + } + + //========================================================= + + bool CommandFileRW::appendCmds(const Array<Command>& +#ifdef _DEBUG + pCmdArray +#endif + ) + { + #ifdef _DEBUG + bool result = false; + if(iWritingEnabled) + { + FILE* f; + if(resetfile) + f = fopen(iFileName.c_str(), "wb"); + else + f = fopen(iFileName.c_str(), "ab"); + resetfile = false; + + if(f) + { + result = true; + for(int i=0; i<pCmdArray.size(); ++i) + { + if(fwrite(&pCmdArray[i], sizeof(Command), 1, f) != 1) { result = false; break; } + } + fclose(f); + } + } + else + { + result = true; + } + return result; + #else + return true; + #endif + } + + //========================================================= + + bool CommandFileRW::getNewCommands(Array<Command>& pCmdArray) + { + bool result = false; + FILE* f = fopen(iFileName.c_str(), "rb"); + if(f) + { + Command cmd; + if(fseek(f, iLastPos, SEEK_SET) == 0) { result = true; } + while(result) + { + if(fread(&cmd, sizeof(Command), 1, f) != 1) + { + result = false; + } + iLastPos = ftell(f); + if(cmd.getType() == STOP) + { + break; + } + pCmdArray.append(cmd); + } + fclose(f); + } + if(result) + { + iCommandArray.append(pCmdArray); + } + return(result); + } + //======================================================== +} diff --git a/src/shared/vmap/DebugCmdLogger.h b/src/shared/vmap/DebugCmdLogger.h new file mode 100644 index 00000000000..8440707d75f --- /dev/null +++ b/src/shared/vmap/DebugCmdLogger.h @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _DEBUGCMDLOGGER_H +#define _DEBUGCMDLOGGER_H + +#include <G3D/Vector3.h> +#include <G3D/Array.h> + +/** +Class is used for debugging. We log activities into a file. +With an external Class we read that log and display the activity in a graphical view. +*/ + +namespace VMAP +{ + + //========================================== + enum C_TYPES + { + STOP, + START, + LOAD_TILE, + UNLOAD_TILE, + SET_POS, + TEST_VIS, + LOAD_INSTANCE, + UNLOAD_INSTANCE, + TEST_HEIGHT, + TEST_OBJECT_HIT, + }; + + class Command + { + int iType; + float floats[9]; + int ints[4]; + char buffer[100]; + public: + + Command() { iType = STOP; } + + inline int getType() { return iType; } + inline G3D::Vector3 getVector(int pos) { return(G3D::Vector3(floats[pos*3+0], floats[pos*3+1], floats[pos*3+2])); } + inline int getInt(int pos) { return(ints[pos]); } + inline char* getBuffer() { return(buffer); } + + void fillStopCmd() { iType = STOP; } + void fillStartCmd() { iType = START; } + void fillLoadTileCmd(int x, int y, G3D::uint32 pMapId) { iType = LOAD_TILE; ints[0] = x; ints[1] = y; ints[2] = pMapId; } + //void fillLoadTileCmd(int x,int y) { iType = LOAD_TILE; ints[0] = x; ints[1] = y; } + void fillUnloadTileCmd(G3D::uint32 pMapId) { iType = UNLOAD_INSTANCE; ints[0] = pMapId; } + void fillUnloadTileCmd(unsigned int pMapId, int x,int y) { iType = UNLOAD_TILE; ints[0] = x; ints[1] = y; ints[0]=pMapId; } + void fillSetPosCmd(G3D::Vector3 pPos) { iType = SET_POS; floats[0] = pPos.x; floats[1]=pPos.y; floats[2]=pPos.z; } + void fillTestVisCmd(int pMapId, G3D::Vector3 pPos1, G3D::Vector3 pPos2, bool result) + { + iType = TEST_VIS; floats[0] = pPos1.x; floats[1]=pPos1.y; floats[2]=pPos1.z; + floats[3] = pPos2.x; floats[4]=pPos2.y; floats[5]=pPos2.z; + ints[0] = result; ints[1] = pMapId; + } + void fillTestHeightCmd(int pMapId, G3D::Vector3 pPos, float result) + { + iType = TEST_HEIGHT; floats[0] = pPos.x; floats[1]=pPos.y; floats[2]=pPos.z; + floats[3] = result; ints[0] = pMapId; + } + void fillTestObjectHitCmd(int pMapId, G3D::Vector3 pPos1, G3D::Vector3 pPos2, G3D::Vector3 pResultPos, bool result) + { + iType = TEST_OBJECT_HIT; floats[0] = pPos1.x; floats[1]=pPos1.y; floats[2]=pPos1.z; + floats[3] = pPos2.x; floats[4]=pPos2.y; floats[5]=pPos2.z; + floats[6] = pResultPos.x; floats[7]=pResultPos.y; floats[8]=pResultPos.z; + ints[0] = result; ints[1] = pMapId; + } + + bool isCoreCmd() const { return(iType != TEST_VIS); } + }; + + //========================================== + + class CommandFileRW + { + private: + std::string iFileName; + long iLastPos; + G3D::Array<G3D::Array<Command> > iCommandArray; + bool resetfile; + bool iWritingEnabled; + public: + CommandFileRW() { iLastPos=0; iWritingEnabled = true; resetfile = true;} + CommandFileRW(const std::string& pFileName) { iLastPos = 0; iFileName = pFileName; iWritingEnabled = true; resetfile = true; } + void setResetFile() { resetfile = true; } + void enableWriting(bool pValue) { iWritingEnabled = pValue; } + void setFileName(const std::string& pName) { iFileName = pName; } + bool getNewCommands(G3D::Array<Command>& commandArray); + const G3D::Array<G3D::Array<Command> >& getFullCommandArray() { return iCommandArray; } + + bool appendCmd(const Command& pCommand); + bool appendCmds(const G3D::Array<Command>& pCmdArray); + }; + +} +#endif diff --git a/src/shared/vmap/IVMapManager.h b/src/shared/vmap/IVMapManager.h new file mode 100644 index 00000000000..67321f29e3d --- /dev/null +++ b/src/shared/vmap/IVMapManager.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _IVMAPMANAGER_H +#define _IVMAPMANAGER_H + +#include<string> + +//=========================================================== + +/** +This is the minimum interface to the VMapMamager. +*/ + +namespace VMAP +{ + + enum VMAP_LOAD_RESULT + { + VMAP_LOAD_RESULT_ERROR, + VMAP_LOAD_RESULT_OK, + VMAP_LOAD_RESULT_IGNORED, + }; + + #define VMAP_INVALID_HEIGHT -100000.0f // for check + #define VMAP_INVALID_HEIGHT_VALUE -200000.0f // real assigned value in unknown height case + + //=========================================================== + class IVMapManager + { + private: + bool iEnableLineOfSightCalc; + bool iEnableHeightCalc; + + public: + IVMapManager() : iEnableLineOfSightCalc(true), iEnableHeightCalc(true) {} + + virtual ~IVMapManager(void) {} + + virtual int loadMap(const char* pBasePath, unsigned int pMapId, int x, int y) = 0; + + virtual bool existsMap(const char* pBasePath, unsigned int pMapId, int x, int y) = 0; + + virtual void unloadMap(unsigned int pMapId, int x, int y) = 0; + virtual void unloadMap(unsigned int pMapId) = 0; + + virtual bool isInLineOfSight(unsigned int pMapId, float x1, float y1, float z1, float x2, float y2, float z2) = 0; + virtual float getHeight(unsigned int pMapId, float x, float y, float z) = 0; + /** + test if we hit an object. return true if we hit one. rx,ry,rz will hold the hit position or the dest position, if no intersection was found + return a position, that is pReduceDist closer to the origin + */ + virtual bool getObjectHitPos(unsigned int pMapId, float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float &ry, float& rz, float pModifyDist) = 0; + /** + send debug commands + */ + virtual bool processCommand(char *pCommand)= 0; + + /** + Enable/disable LOS calculation + It is enabled by default. If it is enabled in mid game the maps have to loaded manualy + */ + void setEnableLineOfSightCalc(bool pVal) { iEnableLineOfSightCalc = pVal; } + /** + Enable/disable model height calculation + It is enabled by default. If it is enabled in mid game the maps have to loaded manualy + */ + void setEnableHeightCalc(bool pVal) { iEnableHeightCalc = pVal; } + + bool isLineOfSightCalcEnabled() const { return(iEnableLineOfSightCalc); } + bool isHeightCalcEnabled() const { return(iEnableHeightCalc); } + bool isMapLoadingEnabled() const { return(iEnableLineOfSightCalc || iEnableHeightCalc ); } + + virtual std::string getDirFileName(unsigned int pMapId, int x, int y) const =0; + /** + Block maps from being used. + parameter: String of map ids. Delimiter = "," + e.g.: "0,1,530" + */ + virtual void preventMapsFromBeingUsed(const char* pMapIdString) =0; + }; + +} +#endif diff --git a/src/shared/vmap/Makefile.am b/src/shared/vmap/Makefile.am new file mode 100644 index 00000000000..322c94bfad1 --- /dev/null +++ b/src/shared/vmap/Makefile.am @@ -0,0 +1,54 @@ +# Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +## Process this file with automake to produce Makefile.in + +noinst_LIBRARIES = libvmaps.a + +## Preprocessor flags +libvmaps_a_CPPFLAGS = \ +-I$(top_srcdir)/dep/include \ +-I$(top_srcdir)/dep/include/g3dlite + +libvmaps_a_SOURCES = \ +$(srcdir)/AABSPTree.h \ +$(srcdir)/BaseModel.cpp \ +$(srcdir)/BaseModel.h \ +$(srcdir)/CoordModelMapping.cpp \ +$(srcdir)/CoordModelMapping.h \ +$(srcdir)/DebugCmdLogger.cpp \ +$(srcdir)/DebugCmdLogger.h \ +$(srcdir)/IVMapManager.h \ +$(srcdir)/ManagedModelContainer.cpp \ +$(srcdir)/ManagedModelContainer.h \ +$(srcdir)/ModelContainer.cpp \ +$(srcdir)/ModelContainer.h \ +$(srcdir)/NodeValueAccess.h \ +$(srcdir)/ShortBox.h \ +$(srcdir)/ShortVector.h \ +$(srcdir)/SubModel.cpp \ +$(srcdir)/SubModel.h \ +$(srcdir)/TileAssembler.cpp \ +$(srcdir)/TileAssembler.h \ +$(srcdir)/TreeNode.cpp \ +$(srcdir)/TreeNode.h \ +$(srcdir)/VMapDefinitions.h \ +$(srcdir)/VMapFactory.cpp \ +$(srcdir)/VMapFactory.h \ +$(srcdir)/VMapManager.cpp \ +$(srcdir)/VMapManager.h \ +$(srcdir)/VMapTools.h + diff --git a/src/shared/vmap/ManagedModelContainer.cpp b/src/shared/vmap/ManagedModelContainer.cpp new file mode 100644 index 00000000000..bc3ba5ba3ec --- /dev/null +++ b/src/shared/vmap/ManagedModelContainer.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ManagedModelContainer.h" + +using namespace G3D; + +namespace VMAP +{ + + ManagedModelContainer::ManagedModelContainer(void) : ModelContainer() + { + refCount = 0; + } + + ManagedModelContainer::~ManagedModelContainer(void) + { + } + +} diff --git a/src/shared/vmap/ManagedModelContainer.h b/src/shared/vmap/ManagedModelContainer.h new file mode 100644 index 00000000000..9766813d66c --- /dev/null +++ b/src/shared/vmap/ManagedModelContainer.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _MANAGEDMODELCONTAINER_H +#define _MANAGEDMODELCONTAINER_H + +#include "ModelContainer.h" + +//======================================================= +/** +This is a ModelContainer with reference count information. +*/ + +namespace VMAP +{ + //======================================================= + + class ManagedModelContainer : + public ModelContainer + { + private: + int refCount; + public: + ManagedModelContainer(void) ; + ~ManagedModelContainer(void); + + void incRefCount() { ++refCount; } + void decRefCount() { --refCount; if(refCount < 0) refCount = 0; } + int getRefCount() { return refCount; } + }; + + //======================================================= +} +#endif diff --git a/src/shared/vmap/ModelContainer.cpp b/src/shared/vmap/ModelContainer.cpp new file mode 100644 index 00000000000..43a947b12dd --- /dev/null +++ b/src/shared/vmap/ModelContainer.cpp @@ -0,0 +1,375 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <iostream> +#include <fstream> + +#include <string.h> + +#include "ModelContainer.h" +#include "VMapDefinitions.h" + +using namespace G3D; + +namespace VMAP +{ + //========================================================== + /** + Functions to use ModelContainer with a AABSPTree + */ + size_t hashCode(const ModelContainer& pMc) + { + return (pMc.getBasePosition() * pMc.getNTriangles()).hashCode(); + } + //========================================================== + + ModelContainer::ModelContainer(unsigned int pNTriangles, unsigned int pNNodes, unsigned int pNSubModel) : + BaseModel(pNNodes, pNTriangles) + { + + iNSubModel = pNSubModel; + iSubModel = 0; + if(pNSubModel > 0) iSubModel = new SubModel[iNSubModel]; + } + + //========================================================== + + bool ModelContainer::operator==(const ModelContainer& pMc2) const + { + if (this->iNSubModel == 0 && pMc2.iNSubModel == 0 && this->iSubModel == 0 && pMc2.iSubModel == 0) + return true; + return this == &pMc2; + } + + //========================================================== + + void ModelContainer::countSubModelsAndNodesAndTriangles(AABSPTree<SubModel *>::Node& pNode, int& nSubModels, int& nNodes, int& nTriangles) + { + // For this node we will need a TreeNode as well as for the internal nodes + ++nNodes; + + nSubModels += pNode.valueArray.size(); + for(int i=0;i<pNode.valueArray.size(); i++) + { + G3D::_AABSPTree::Handle<SubModel*>* h= pNode.valueArray[i]; + SubModel *m = h->value; + // add the internal nodes as well + nNodes += m->getNNodes(); + nTriangles += m->getNTriangles(); + } + + if(pNode.child[0] != 0) + { + countSubModelsAndNodesAndTriangles(*pNode.child[0], nSubModels, nNodes, nTriangles); + } + if(pNode.child[1] != 0) + { + countSubModelsAndNodesAndTriangles(*pNode.child[1], nSubModels, nNodes, nTriangles); + } + } + //========================================================== + + void ModelContainer::fillContainer(const AABSPTree<SubModel *>::Node& pNode, int &pSubModelPos, int &pTreeNodePos, int &pTrianglePos, Vector3& pLo, Vector3& pHi, Vector3& pFinalLo, Vector3& pFinalHi) + { + // TreeNode for the SubModel + TreeNode treeNode = TreeNode(pNode.valueArray.size(), pSubModelPos); + treeNode.setSplitAxis(pNode.splitAxis); + treeNode.setSplitLocation(pNode.splitLocation); + int currentTreeNodePos = pTreeNodePos++; + + Vector3 lo = Vector3(inf(),inf(),inf()); + Vector3 hi = Vector3(-inf(),-inf(),-inf()); + + for(int i=0;i<pNode.valueArray.size(); i++) + { + G3D::_AABSPTree::Handle<SubModel*>* h= pNode.valueArray[i]; + SubModel *m = h->value; + + memcpy(&getTreeNodes()[pTreeNodePos], &m->getTreeNode(0), sizeof(TreeNode) * m->getNNodes()); + memcpy(&getTriangles()[pTrianglePos], &m->getTriangle(0), sizeof(TriangleBox) * m->getNTriangles()); + + SubModel newModel = SubModel(m->getNTriangles(), getTriangles(), pTrianglePos, m->getNNodes(), getTreeNodes(), pTreeNodePos); + newModel.setReletiveBounds(m->getReletiveBounds().getLo(), m->getReletiveBounds().getHi()); + newModel.setBasePosition(m->getBasePosition()); + iSubModel[pSubModelPos++] = newModel; + + pTreeNodePos += m->getNNodes(); + pTrianglePos += m->getNTriangles(); + + AABox box = m->getAABoxBounds(); + lo = lo.min(box.low()); + hi = hi.max(box.high()); + pFinalLo = pFinalLo.min(lo); + pFinalHi = pFinalHi.max(hi); + } + /* + if(pNode.valueArray.size() == 0) { + int xxx = 0; // just for the breakpoint + } + */ + // get absolute bounds + + if(pNode.child[0] != 0) + { + treeNode.setChildPos(0, pTreeNodePos); + fillContainer(*pNode.child[0], pSubModelPos, pTreeNodePos, pTrianglePos, lo, hi,pFinalLo,pFinalHi); + } + if(pNode.child[1] != 0) + { + treeNode.setChildPos(1, pTreeNodePos); + fillContainer(*pNode.child[1], pSubModelPos, pTreeNodePos, pTrianglePos, lo, hi,pFinalLo,pFinalHi); + } + + pLo = pLo.min(lo); + pHi = pHi.max(hi); + + treeNode.setBounds(lo,hi); + + setTreeNode(treeNode, currentTreeNodePos); + + } + + //========================================================== + /** + Create the structure out of a AABSPTree + */ + + ModelContainer::ModelContainer(AABSPTree<SubModel *> *pTree) + { + + int nSubModels, nNodes, nTriangles; + nSubModels = nNodes = nTriangles = 0; + countSubModelsAndNodesAndTriangles(*pTree->root, nSubModels, nNodes, nTriangles); + + init(nNodes, nTriangles); + + iNSubModel = nSubModels; + + iSubModel = new SubModel[iNSubModel]; + + int subModelPos,treeNodePos, trianglePos; + subModelPos = treeNodePos = trianglePos = 0; + + Vector3 lo = Vector3(inf(),inf(),inf()); + Vector3 hi = Vector3(-inf(),-inf(),-inf()); + Vector3 finalLo, finalHi; + finalLo = lo; + finalHi = hi; + + fillContainer(*pTree->root, subModelPos, treeNodePos, trianglePos, lo, hi, finalLo, finalHi); + setBounds(finalLo, finalHi); + } + + //========================================================== + + ModelContainer::~ModelContainer(void) + { + free(); + if(iSubModel != 0) delete [] iSubModel; + } + //========================================================== + + bool ModelContainer::writeFile(const char *filename) + { + bool result = false; + unsigned int flags=0; + unsigned int size; + + FILE *wf =fopen(filename,"wb"); + if(wf) + { + fwrite(VMAP_MAGIC,1,8,wf); + result = true; + if(result && fwrite("CTREE01",8,1,wf) != 1) result = false; + if(result && fwrite(&flags,sizeof(unsigned int),1,wf) != 1) result = false; + + if(result && fwrite("POS ",4,1,wf) != 1) result = false; + size = sizeof(float)*3; + if(result && fwrite(&size,4,1,wf) != 1) result = false; + Vector3 basePos = getBasePosition(); + if(result && fwrite(&basePos,sizeof(float),3,wf) != 3) result = false; + + if(result && fwrite("BOX ",4,1,wf) != 1) result = false; + size = sizeof(float)*6; + if(result && fwrite(&size,4,1,wf) != 1) result = false; + Vector3 low = iBox.low(); + if(result && fwrite(&low,sizeof(float),3,wf) != 3) result = false; + Vector3 high = iBox.high(); + if(result && fwrite(&high,sizeof(float),3,wf) != 3) result = false; + + if(result && fwrite("NODE",4,1,wf) != 1) result = false; + size = sizeof(unsigned int)+ sizeof(TreeNode)*getNNodes(); + if(result && fwrite(&size,4,1,wf) != 1) result = false; + unsigned int val = getNNodes(); + if(result && fwrite(&val,sizeof(unsigned int),1,wf) != 1) result = false; + if(result && fwrite(getTreeNodes(),sizeof(TreeNode),getNNodes(),wf) != getNNodes()) result = false; + + if(result && fwrite("TRIB",4,1,wf) != 1) result = false; + size = sizeof(unsigned int)+ sizeof(TriangleBox)*getNTriangles(); + if(result && fwrite(&size,4,1,wf) != 1) result = false; + val = getNTriangles(); + if(result && fwrite(&val,sizeof(unsigned int),1,wf) != 1) result = false; + if(result && fwrite(getTriangles(),sizeof(TriangleBox),getNTriangles(),wf) != getNTriangles()) result = false; + + if(result && fwrite("SUBM",4,1,wf) != 1) result = false; + size = sizeof(unsigned int)+ sizeof(SubModel)*iNSubModel; + if(result && fwrite(&size,4,1,wf) != 1) result = false; + if(result && fwrite(&iNSubModel,sizeof(unsigned int),1,wf) != 1) result = false; + if(result && fwrite(iSubModel,sizeof(SubModel),iNSubModel,wf) != iNSubModel) result = false; + + fclose(wf); + } + + return(result); + } + + //=============================================================== + + bool ModelContainer::readFile(const char *filename) + { + bool result = false; + unsigned int flags; + unsigned int size; + char ident[8]; + char chunk[4]; + unsigned int ival; + FILE *rf = fopen(filename, "rb"); + if(rf) + { + free(); + + result = true; + char magic[8]; // Ignore the added magic header + fread(magic,1,8,rf); + if(strncmp(VMAP_MAGIC,magic,8)) result = false; + if(result && fread(ident,8,1,rf) != 1) result = false; + if(result && fread(&flags,sizeof(unsigned int),1,rf) != 1) result = false; + //POS + if(result && fread(chunk,4,1,rf) != 1) result = false; + if(result && fread(&size,4,1,rf) != 1) result = false; + Vector3 basePos; + if(result && fread(&basePos,sizeof(float),3,rf) != 3) result = false; + setBasePosition(basePos); + + //---- Box + if(result && fread(chunk,4,1,rf) != 1) result = false; + if(result && fread(&size,4,1,rf) != 1) result = false; + Vector3 low,high; + if(result && fread(&low,sizeof(float),3,rf) != 3) result = false; + if(result && fread(&high,sizeof(float),3,rf) != 3) result = false; + setBounds(low, high); + + //---- TreeNodes + if(result && fread(chunk,4,1,rf) != 1) result = false; + if(result && fread(&size,4,1,rf) != 1) result = false; + + if(result && fread(&ival,sizeof(unsigned int),1,rf) != 1) result = false; + if(result) setNNodes(ival); + if(result) setTreeNodeArray(new TreeNode[getNNodes()]); + if(result && fread(getTreeNodes(),sizeof(TreeNode),getNNodes(),rf) != getNNodes()) result = false; + + //---- TriangleBoxes + if(result && fread(chunk,4,1,rf) != 1) result = false; + if(result && fread(&size,4,1,rf) != 1) result = false; + + if(result && fread(&ival,sizeof(unsigned int),1,rf) != 1) result = false; + setNTriangles(ival); + if(result) setTriangleArray(new TriangleBox[getNTriangles()]); + if(result && fread(getTriangles(),sizeof(TriangleBox),getNTriangles(),rf) != getNTriangles()) result = false; + + //---- SubModel + if(result && fread(chunk,4,1,rf) != 1) result = false; + if(result && fread(&size,4,1,rf) != 1) result = false; + + if(result && fread(&iNSubModel,sizeof(unsigned int),1,rf) != 1) result = false; + if(result) iSubModel = new SubModel[iNSubModel]; + + if(result) + { + for(unsigned int i=0;i<iNSubModel && result; ++i) + { + unsigned char readBuffer[52]; // this is the size of SubModel on 32 bit systems + if(fread(readBuffer,sizeof(readBuffer),1,rf) != 1) result = false; + iSubModel[i].initFromBinBlock(readBuffer); + iSubModel[i].setTriangleArray(getTriangles()); + iSubModel[i].setTreeNodeArray(getTreeNodes()); + } + } + fclose(rf); + } + return result; + } + + //================================================================= + + size_t ModelContainer::getMemUsage() + { + // BaseModel is included in ModelContainer + return(iNSubModel * sizeof(SubModel) + BaseModel::getMemUsage() + sizeof(ModelContainer) - sizeof(BaseModel)); + } + + //================================================================= +#ifdef _DEBUG_VMAPS +#ifndef gBoxArray + extern Vector3 p1,p2,p3,p4,p5,p6,p7; + extern Array<AABox>gBoxArray; + extern int gCount1, gCount2, gCount3, gCount4; + extern bool myfound; +#endif +#endif + + void ModelContainer::intersect(const G3D::Ray& pRay, float& pMaxDist, bool pStopAtFirstHit, G3D::Vector3& /*pOutLocation*/, G3D::Vector3& /*pOutNormal*/) const + { + IntersectionCallBack<SubModel> intersectCallback; + NodeValueAccess<TreeNode, SubModel> vna = NodeValueAccess<TreeNode, SubModel>(getTreeNodes(), iSubModel); + Ray relativeRay = Ray::fromOriginAndDirection(pRay.origin - getBasePosition(), pRay.direction); + iTreeNodes[0].intersectRay(pRay, intersectCallback, pMaxDist, vna, pStopAtFirstHit, false); + } + + //========================================================== + + bool ModelContainer::intersect(const G3D::Ray& pRay, float& pMaxDist) const + { + return BaseModel::intersect(getAABoxBounds(), pRay, pMaxDist); + } + + //================================================================= + + template<typename RayCallback> + void ModelContainer::intersectRay(const G3D::Ray& pRay, RayCallback& intersectCallback, float& pMaxDist, bool pStopAtFirstHit, bool intersectCallbackIsFast) + { + if(intersect(pRay, pMaxDist)) + { + NodeValueAccess<TreeNode, SubModel> vna = NodeValueAccess<TreeNode, SubModel>(getTreeNodes(), iSubModel); + iTreeNodes[0].intersectRay(pRay, intersectCallback, distance, vna, pStopAtFirstHit, true); + } + } + //================================================================= + void getBounds(const ModelContainer& pMc, G3D::AABox& pAABox) + { + pAABox = pMc.getAABoxBounds(); + } + + //================================================================= + + void getBounds(const ModelContainer* pMc, G3D::AABox& pAABox) + { + pAABox = pMc->getAABoxBounds(); + } + //================================================================= +} // VMAP diff --git a/src/shared/vmap/ModelContainer.h b/src/shared/vmap/ModelContainer.h new file mode 100644 index 00000000000..636be1713b4 --- /dev/null +++ b/src/shared/vmap/ModelContainer.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _MODELCONTAINER_H +#define _MODELCONTAINER_H + +// load our modified version first !! +#include "AABSPTree.h" + +#include <G3D/AABox.h> +#include <G3D/Vector3.h> +#include <G3D/Ray.h> + +#include "ShortBox.h" +#include "TreeNode.h" +#include "VMapTools.h" +#include "SubModel.h" +#include "BaseModel.h" + +namespace VMAP +{ + /** + The ModelContainer is a balanced BSP-Tree of SubModels. + We store a map tile or an instance in one ModelContainer. + The ModelContainer manages the memory used for the tree nodes, the SubModels and its triangles in static arrays. + The tree nodes are used for the BSP-Tree of SubModels as well as for the BSP-Tree of triangles within one SubModel. + The references are done by indexes within these static arrays. + Therefore we are able to just load a binary block and do not need to mess around with memory allocation and pointers. + */ + + //===================================================== + + class ModelContainer : public BaseModel + { + private: + unsigned int iNSubModel; + SubModel *iSubModel; + G3D::AABox iBox; + + ModelContainer (const ModelContainer& c): BaseModel(c) {} + ModelContainer& operator=(const ModelContainer& ) {} + + public: + ModelContainer() : BaseModel() { iNSubModel =0; iSubModel = 0; }; + + // for the mainnode + ModelContainer(unsigned int pNTriangles, unsigned int pNNodes, unsigned int pNSubModel); + + ModelContainer(G3D::AABSPTree<SubModel *> *pTree); + + ~ModelContainer(void); + + inline const void setSubModel(const SubModel& pSubModel, int pPos) { iSubModel[pPos] = pSubModel; } + + inline const SubModel& getSubModel(int pPos) const { return iSubModel[pPos]; } + + inline unsigned int getNSubModel() const { return(iNSubModel); } + + void countSubModelsAndNodesAndTriangles(G3D::AABSPTree<SubModel *>::Node& pNode, int& nSubModels, int& nNodes, int& nTriangles); + + void fillContainer(const G3D::AABSPTree<SubModel *>::Node& pNode, int &pSubModelPos, int &pTreeNodePos, int &pTrianglePos, G3D::Vector3& pLo, G3D::Vector3& pHi, G3D::Vector3& pFinalLo, G3D::Vector3& pFinalHi); + + bool readRawFile(const char *name); + + inline const G3D::AABox& getAABoxBounds() const { return(iBox); } + + inline void setBounds(const G3D::Vector3& lo, const G3D::Vector3& hi) { iBox.set(lo,hi); } + + bool writeFile(const char *filename); + + bool readFile(const char *filename); + + size_t getMemUsage(); + size_t hashCode() { return (getBasePosition() * getNTriangles()).hashCode(); } + + void intersect(const G3D::Ray& pRay, float& pMaxDist, bool pStopAtFirstHit, G3D::Vector3& pOutLocation, G3D::Vector3& pOutNormal) const; + bool intersect(const G3D::Ray& pRay, float& pMaxDist) const; + + template<typename RayCallback> + void intersectRay(const G3D::Ray& ray, RayCallback& intersectCallback, float& distance, bool pStopAtFirstHit, bool intersectCallbackIsFast = false); + + bool operator==(const ModelContainer& pMc2) const; + }; + + //===================================================== + + //===================================================== + + size_t hashCode(const ModelContainer& pMc); + void getBounds(const ModelContainer& pMc, G3D::AABox& pAABox); + void getBounds(const ModelContainer* pMc, G3D::AABox& pAABox); +} +#endif diff --git a/src/shared/vmap/NodeValueAccess.h b/src/shared/vmap/NodeValueAccess.h new file mode 100644 index 00000000000..fb7a771baf0 --- /dev/null +++ b/src/shared/vmap/NodeValueAccess.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NODEVALUEACCESS_H +#define _NODEVALUEACCESS_H + +namespace VMAP +{ + /** + This is a helper Class to get access to SubModels or triangles when analyzing the BSP-Tree. + */ + + template<class TNode, class TValue> class NodeValueAccess + { + private: + TNode const* iNodeArray; + TValue const* iValueArray; + + public: + inline NodeValueAccess() : iNodeArray(NULL), iValueArray(NULL) {} + + inline NodeValueAccess(TNode const* pNodeArray, TValue const* pValueArray) : iNodeArray(pNodeArray), iValueArray(pValueArray) {} + inline TNode const* getNodePtr() const { return(iNodeArray); } + inline TValue const* getValuePtr() const { return(iValueArray); } + + inline TNode const& getNode(unsigned int pPos) const { return(iNodeArray[pPos]); } + inline void setNode(const TNode& pNode, unsigned int pPos) { iNodeArray[pPos] = pNode; } + + inline TValue const& getValue(unsigned int pPos) const { return(iValueArray[pPos]); } + inline void setValue(const TValue& pValue, unsigned int pPos) { iValueArray[pPos] = pValue; } + }; +} +#endif diff --git a/src/shared/vmap/ShortBox.h b/src/shared/vmap/ShortBox.h new file mode 100644 index 00000000000..d43bc61caa7 --- /dev/null +++ b/src/shared/vmap/ShortBox.h @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _SHORTBOX_H +#define _SHORTBOX_H + +#include <G3D/Vector3.h> +#include <G3D/AABox.h> +#include <G3D/Triangle.h> +#include <G3D/Ray.h> + +#include "ShortVector.h" + +/** +This is a box and a triangle Class using ShortVectors. Each vector has 16 bit an a fixed point 12.4 representation. +*/ + +namespace VMAP +{ + + class ShortBox + { + private: + ShortVector iV1; + ShortVector iV2; + public: + ShortBox() {} + inline const ShortVector& getLo() const { return(iV1); } + inline const ShortVector& getHi() const { return(iV2); } + inline void setLo(const ShortVector& pV){ iV1 = pV; } + inline void setHi(const ShortVector& pV){ iV2 = pV; } + inline void setLo(const G3D::Vector3& pV){ iV1 = ShortVector(pV); } + inline void setHi(const G3D::Vector3& pV){ iV2 = ShortVector(pV); } + + inline bool operator==(const ShortBox& b) const + { + return ((iV1 == b.iV1) && (iV2 == b.iV2)); + } + + inline bool operator!=(const ShortBox& b) const + { + return !((iV1 == b.iV1) && (iV2 == b.iV2)); + } + }; + + //===================================================================== +#ifdef _DEBUG_VMAPS +#ifndef gBoxArray + extern G3D::Vector3 p1,p2,p3,p4,p5,p6,p7; + extern G3D::Array<G3D::AABox>gBoxArray; + extern G3D::Array<G3D::Triangle>gTriArray; + extern int gCount1, gCount2, gCount3, gCount4; + extern bool myfound; +#endif +#endif + + static const G3D::Vector3 dummyZeroPosition = G3D::Vector3(0,0,0); + + class TriangleBox + { + private: + ShortVector _vertex[3]; + //ShortBox iBox; + public: + inline TriangleBox() { } + inline TriangleBox(const ShortVector& pV1, const ShortVector& pV2, const ShortVector& pV3) + { + _vertex[0] = pV1; + _vertex[1] = pV2; + _vertex[2] = pV3; + + } + inline const ShortVector& vertex (int n) const + { + return(_vertex[n]); + } + + inline const ShortBox getBounds()const + { + ShortBox box; + + ShortVector lo = _vertex[0]; + ShortVector hi = lo; + + for (int i = 1; i < 3; ++i) + { + lo = lo.min(_vertex[i]); + hi = hi.max(_vertex[i]); + } + box.setLo(lo); + box.setHi(hi); + return(box); + } + inline const G3D::Vector3& getBasePosition() { return(dummyZeroPosition); } + + inline const G3D::AABox getAABoxBounds() const { ShortBox box = getBounds(); return(G3D::AABox(box.getLo().getVector3(), box.getHi().getVector3())); } + + inline bool operator==(const TriangleBox& t) const + { + return ((_vertex[0] == t._vertex[0]) && (_vertex[1] == t._vertex[1]) &&(_vertex[2] == t._vertex[2])); + } + + inline bool operator!=(const TriangleBox& t) const + { + return !((_vertex[0] == t._vertex[0]) && (_vertex[1] == t._vertex[1]) &&(_vertex[2] == t._vertex[2])); + } + + inline void intersect(const G3D::Ray& pRay, float& pMaxDist, bool /*pStopAtFirstHitDummy*/, G3D::Vector3& /*pOutLocationDummy*/, G3D::Vector3& /*pOutNormalDummy*/) const + { + static const double epsilon = 0.00001; + G3D::Triangle testT(vertex(0).getVector3(),vertex(1).getVector3(),vertex(2).getVector3()); + float t = pRay.intersectionTime(testT); + if ((t < pMaxDist) || t < (pMaxDist + epsilon)) + pMaxDist = t; + else + { + testT = G3D::Triangle(vertex(2).getVector3(),vertex(1).getVector3(),vertex(0).getVector3()); + +#ifdef _DEBUG_VMAPS + { + G3D::Triangle myt(testT.vertex(0)+p6, testT.vertex(1)+p6,testT.vertex(2)+p6); + gTriArray.push_back(myt); + } +#endif + t = pRay.intersectionTime(testT); + if ((t < pMaxDist) || t < (pMaxDist + epsilon)) + pMaxDist = t; + } + } + }; + +} +#endif diff --git a/src/shared/vmap/ShortVector.h b/src/shared/vmap/ShortVector.h new file mode 100644 index 00000000000..642f5b53c1b --- /dev/null +++ b/src/shared/vmap/ShortVector.h @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _SHORTVECTOR_H +#define _SHORTVECTOR_H + +#include <G3D/Vector3.h> + +namespace VMAP +{ + /** + Vector with 16 bit fix point values 12.4 bit. + */ + + class ShortVector + { + private: + short iX; + short iY; + short iZ; + + const static short maxvalue = 0x7fff; + const static short minvalue = -0x7fff; + const static int fixpointdiv = 16; + const static short fixpoint_maxvalue = maxvalue / fixpointdiv; + const static short fixpoint_minvalue = minvalue / fixpointdiv; + + inline short float2Short(float fv) const + { + short sv; + debugAssert((fv <= fixpoint_maxvalue || fv >= 1000000) && (fv >= fixpoint_minvalue || fv <= -1000000)); + if(fv >= fixpoint_maxvalue) + sv=maxvalue; + else if(fv <= fixpoint_minvalue) + sv=minvalue; + else + sv = (short) (fv * fixpointdiv + 0.5); + return(sv); + } + inline float short2Float(short sv) const + { + float fv; + if(sv >= maxvalue) + fv=G3D::inf(); + else if(sv <= minvalue) + fv=-G3D::inf(); + else + fv = ((float)sv) / fixpointdiv; + return fv; + } + + inline float getFX() const { return(short2Float(iX)); } + inline float getFY() const { return(short2Float(iY)); } + inline float getFZ() const { return(short2Float(iZ)); } + public: + inline ShortVector() {} + inline ShortVector(const G3D::Vector3& pVector) + { + iX = float2Short(pVector.x); + iY = float2Short(pVector.y); + iZ = float2Short(pVector.z); + } + + inline ShortVector(float pX, float pY, float pZ) + { + iX = float2Short(pX); + iY = float2Short(pY); + iZ = float2Short(pZ); + } + inline ShortVector(short pX, short pY, short pZ) + { + iX = pX; + iY = pY; + iZ = pZ; + } + inline ShortVector(const ShortVector& pShortVector) + { + iX = pShortVector.iX; + iY = pShortVector.iY; + iZ = pShortVector.iZ; + } + + inline float getX() const { return(iX); } + inline float getY() const { return(iY); } + inline float getZ() const { return(iZ); } + + inline G3D::Vector3 getVector3() const { return(G3D::Vector3(getFX(), getFY(), getFZ())); } + + inline ShortVector min(const ShortVector pShortVector) + { + ShortVector result = pShortVector; + if(pShortVector.iX > iX) { result.iX = iX; } + if(pShortVector.iY > iY) { result.iY = iY; } + if(pShortVector.iZ > iZ) { result.iZ = iZ; } + return(result); + } + + inline ShortVector max(const ShortVector pShortVector) + { + ShortVector result = pShortVector; + if(pShortVector.iX < iX) { result.iX = iX; } + if(pShortVector.iY < iY) { result.iY = iY; } + if(pShortVector.iZ < iZ) { result.iZ = iZ; } + return(result); + } + + inline bool operator==(const ShortVector& v) const + { + return (iX == v.iX && iY == v.iY && iZ == v.iZ); + } + + inline bool operator!=(const ShortVector& v) const + { + return !(iX == v.iX && iY == v.iY && iZ == v.iZ); + } + + }; +} +#endif diff --git a/src/shared/vmap/SubModel.cpp b/src/shared/vmap/SubModel.cpp new file mode 100644 index 00000000000..2a46b3c6a1f --- /dev/null +++ b/src/shared/vmap/SubModel.cpp @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "SubModel.h" + +#ifdef _ASSEMBLER_DEBUG +extern FILE *::g_df; +#endif + +using namespace G3D; + +namespace VMAP +{ + + //========================================================== + /** + Functions to use ModelContainer with a AABSPTree + */ + unsigned int hashCode(const SubModel& pSm) + { + return pSm.getNTriangles(); + } + + void getBounds(const SubModel& pSm, G3D::AABox& pAABox) + { + ShortBox box = pSm.getReletiveBounds(); + pAABox.set(box.getLo().getVector3()+pSm.getBasePosition(), box.getHi().getVector3()+pSm.getBasePosition()); + } + + void getBounds(const SubModel* pSm, G3D::AABox& pAABox) + { + ShortBox box = pSm->getReletiveBounds(); + pAABox.set(box.getLo().getVector3()+pSm->getBasePosition(), box.getHi().getVector3()+pSm->getBasePosition()); + } + + //========================================================== + //========================================================== + //========================================================== + //========================================================== + SubModel::SubModel(unsigned int pNTriangles, TriangleBox *pTriangles, unsigned int pTrianglesPos, unsigned int pNNodes, TreeNode *pTreeNodes, unsigned int pNodesPos) : + BaseModel(pNNodes, pTreeNodes, pNTriangles, pTriangles) + { + iTrianglesPos = pTrianglesPos; + iNodesPos = pNodesPos; + iHasInternalMemAlloc = false; + } + + //========================================================== + + SubModel::~SubModel(void) + { + if(iHasInternalMemAlloc) + { + free(); + } + } + + //========================================================== + + bool SubModel::operator==(const SubModel& pSm2) const + { + bool result = false; + + if(getNNodes() == pSm2.getNNodes() && + getNTriangles() == pSm2.getNTriangles() && + getBasePosition() == pSm2.getBasePosition() && + getNodesPos() == pSm2.getNodesPos() && + getTrianglesPos() == pSm2.getTrianglesPos()) + { + result = true; + } + return result; + } + //========================================================== + + enum BIN_POSITIONS + { + BP_iNTriangles=8, + BP_iNNodes=12, + BP_iBasePosition=16, + BP_iNodesPos=28, + BP_iTrianglesPos=32, + BP_iHasInternalMemAlloc=36, + BP_iBox=38, + }; + /** + This is ugly, but due to compatibility and 64 bit support we have to do that ... sorry + */ + void SubModel::initFromBinBlock(void *pBinBlock) + { + iNTriangles = *((unsigned int *)(((char *) pBinBlock) + BP_iNTriangles)); + iNNodes = *((unsigned int *) (((char *) pBinBlock) + BP_iNNodes)); + iBasePosition = *((Vector3 *) (((char *) pBinBlock) + BP_iBasePosition)); + iNodesPos = *((unsigned int *) (((char *) pBinBlock) + BP_iNodesPos)); + iTrianglesPos = *((unsigned int *) (((char *) pBinBlock) + BP_iTrianglesPos)); + iHasInternalMemAlloc = *((bool *) (((char *) pBinBlock) + BP_iHasInternalMemAlloc)); + iBox = *((ShortBox *) (((char *) pBinBlock) + BP_iBox)); + } + + //========================================================== + + void SubModel::countNodesAndTriangles(AABSPTree<Triangle>::Node& pNode, int &pNNodes, int &pNTriabgles) + { + ++pNNodes; + pNTriabgles += pNode.valueArray.size(); + + #ifdef _ASSEMBLER_DEBUG + fprintf(::g_df, "Nodes: %d, Tris: %d\n",pNNodes, pNTriabgles); + #endif + + if(pNode.child[0] != 0) + { + countNodesAndTriangles(*pNode.child[0], pNNodes, pNTriabgles); + } + if(pNode.child[1] != 0) + { + countNodesAndTriangles(*pNode.child[1], pNNodes, pNTriabgles); + } + } + + //========================================================== + + void SubModel::fillContainer(const AABSPTree<Triangle>::Node& pNode, int &pTreeNodePos, int &pTrianglePos, Vector3& pLo, Vector3& pHi) + { + TreeNode treeNode = TreeNode(pNode.valueArray.size(), pTrianglePos); + treeNode.setSplitAxis(pNode.splitAxis); + treeNode.setSplitLocation(pNode.splitLocation); + + int currentTreeNodePos = pTreeNodePos++; + + Vector3 lo = Vector3(inf(),inf(),inf()); + Vector3 hi = Vector3(-inf(),-inf(),-inf()); + + for(int i=0;i<pNode.valueArray.size(); i++) + { + G3D::_AABSPTree::Handle<Triangle>* h= pNode.valueArray[i]; + Triangle t = h->value; + TriangleBox triangleBox = TriangleBox(t.vertex(0),t.vertex(1), t.vertex(2)); + lo = lo.min(triangleBox.getBounds().getLo().getVector3()); + hi = hi.max(triangleBox.getBounds().getHi().getVector3()); + + getTriangles()[pTrianglePos++] = triangleBox; + } + + if(pNode.child[0] != 0) + { + treeNode.setChildPos(0, pTreeNodePos); + fillContainer(*pNode.child[0], pTreeNodePos, pTrianglePos, lo, hi); + } + if(pNode.child[1] != 0) + { + treeNode.setChildPos(1, pTreeNodePos); + fillContainer(*pNode.child[1], pTreeNodePos, pTrianglePos, lo, hi); + } + + treeNode.setBounds(lo,hi); + + // get absolute bounds + pLo = pLo.min(lo); + pHi = pHi.max(hi); + + getTreeNodes()[currentTreeNodePos] = treeNode; + } + + //========================================================== + + SubModel::SubModel(AABSPTree<Triangle> *pTree) + { + int nNodes, nTriangles; + nNodes = nTriangles = 0; + countNodesAndTriangles(*pTree->root, nNodes, nTriangles); + + init(nNodes, nTriangles); + + iTrianglesPos = 0; // this is the global array + iNodesPos = 0; // this is the global array + iHasInternalMemAlloc = true; + int treeNodePos, trianglePos; + treeNodePos = trianglePos = 0; + + Vector3 lo = Vector3(inf(),inf(),inf()); + Vector3 hi = Vector3(-inf(),-inf(),-inf()); + + fillContainer(*pTree->root, treeNodePos, trianglePos, lo, hi); + setReletiveBounds(lo, hi); + } + + //========================================================== +#ifdef _DEBUG_VMAPS +#ifndef gBoxArray + extern Vector3 p1,p2,p3,p4,p5,p6,p7; + extern Array<AABox>gBoxArray; + extern Array<G3D::Triangle>gTriArray; + extern int gCount1, gCount2, gCount3, gCount4; + extern bool myfound; +#endif +#endif + + //========================================================== + void SubModel::intersect(const G3D::Ray& pRay, float& pMaxDist, bool pStopAtFirstHit, G3D::Vector3& /*pOutLocation*/, G3D::Vector3& /*pOutNormal*/) const + { + NodeValueAccess<TreeNode, TriangleBox> vna = NodeValueAccess<TreeNode, TriangleBox>(getTreeNodes(), getTriangles()); + IntersectionCallBack<TriangleBox> intersectCallback; + Ray relativeRay = Ray::fromOriginAndDirection(pRay.origin - getBasePosition(), pRay.direction); +#ifdef _DEBUG_VMAPS + //p6=getBasePosition(); + //gBoxArray.push_back(getAABoxBounds()); +#endif + getTreeNode(0).intersectRay(relativeRay, intersectCallback, pMaxDist, vna, pStopAtFirstHit, false); + } + + //========================================================== + + bool SubModel::intersect(const G3D::Ray& pRay, float& pMaxDist) const + { + return BaseModel::intersect(getAABoxBounds(), pRay, pMaxDist); + } + + //========================================================== + + template<typename RayCallback> + void SubModel::intersectRay(const Ray& pRay, RayCallback& pIntersectCallback, float& pMaxDist, bool pStopAtFirstHit, bool intersectCallbackIsFast) + { + if(intersect(pRay, pMaxDist)) + { + NodeValueAccess<TreeNode, TriangleBox> vna = NodeValueAccess<TreeNode, TriangleBox>(getTreeNodes(), getTriangles()); + IntersectionCallBack<TriangleBox> intersectCallback; + getTreeNode(0).intersectRay(pRay, intersectCallback, pMaxDist, vna, pStopAtFirstHit, false); + } + } + //========================================================== + +} diff --git a/src/shared/vmap/SubModel.h b/src/shared/vmap/SubModel.h new file mode 100644 index 00000000000..d72b603e298 --- /dev/null +++ b/src/shared/vmap/SubModel.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _SUBMODEL_H +#define _SUBMODEL_H + +// load our modified version first !! +#include "AABSPTree.h" + +#include "ShortVector.h" +#include "ShortBox.h" +#include "TreeNode.h" +#include "VMapTools.h" +#include "BaseModel.h" + +namespace VMAP +{ + /** + This is a balanced static BSP-Tree of triangles. + The memory for the tree nodes and the triangles are managed by the ModelContainer. + The exception to this is during the conversion of raw data info balanced BSP-Trees. + During this conversion the memory management is done internally. + */ + class SubModel : public BaseModel + { + private: + unsigned int iNodesPos; + unsigned int iTrianglesPos; + bool iHasInternalMemAlloc; + ShortBox iBox; + #ifdef _DEBUG_VIEW + G3D::Array<TriangleBox *> iDrawBox; + #endif + public: + SubModel() : BaseModel(){ }; + + SubModel(unsigned int pNTriangles, TriangleBox *pTriangles, unsigned int pTrianglesPos, unsigned int pNNodes, TreeNode *pTreeNodes, unsigned int pNodesPos); + SubModel(G3D::AABSPTree<G3D::Triangle> *pTree); + ~SubModel(void); + //Gets a 50 byte binary block + void initFromBinBlock(void *pBinBlock); + + void fillRenderArray(G3D::Array<TriangleBox> &pArray, const TreeNode* pTreeNode); + + void countNodesAndTriangles(G3D::AABSPTree<G3D::Triangle>::Node& pNode, int &pNNodes, int &pNTriabgles); + + void fillContainer(const G3D::AABSPTree<G3D::Triangle>::Node& pNode, int &pTreeNodePos, int &pTrianglePos, G3D::Vector3& pLo, G3D::Vector3& pHi); + + inline const ShortBox& getReletiveBounds() const { return(iBox); } + + inline void setReletiveBounds(const ShortVector& lo, const ShortVector& hi) { iBox.setLo(lo); iBox.setHi(hi); } + + inline const G3D::AABox getAABoxBounds() const { return(G3D::AABox(iBox.getLo().getVector3() + getBasePosition(), iBox.getHi().getVector3()+ getBasePosition())); } + + // get start pos bases on the global array + inline TriangleBox const* getTriangles() const { return &BaseModel::getTriangle(iTrianglesPos); } + inline TriangleBox * getTriangles() { return &BaseModel::getTriangle(iTrianglesPos); } + + // get start pos bases on the global array + inline TreeNode const* getTreeNodes() const { return &BaseModel::getTreeNode(iNodesPos); } + inline TreeNode * getTreeNodes() { return &BaseModel::getTreeNode(iNodesPos); } + + // internal method usign internal offset + inline const TreeNode& getTreeNode(int pPos) const { return(SubModel::getTreeNodes()[pPos]); } + + // internal method usign internal offset + inline const TriangleBox& getTriangle(int pPos) const { return(SubModel::getTriangles()[pPos]); } + + inline unsigned int getNodesPos() const { return(iNodesPos); } + inline unsigned int getTrianglesPos() const { return(iTrianglesPos); } + + //unsigned int hashCode() { return (getBasePosition() * getNTriangles()).hashCode(); } + + void intersect(const G3D::Ray& pRay, float& pMaxDist, bool pStopAtFirstHit, G3D::Vector3& pOutLocation, G3D::Vector3& pOutNormal) const; + bool intersect(const G3D::Ray& pRay, float& pMaxDist) const; + template<typename RayCallback> + void intersectRay(const G3D::Ray& ray, RayCallback& intersectCallback, float& distance, bool pStopAtFirstHit, bool intersectCallbackIsFast = false); + bool operator==(const SubModel& pSm2) const; + unsigned int hashCode() const { return BaseModel::getNTriangles(); } + }; + + unsigned int hashCode(const SubModel& pSm); + void getBounds(const SubModel& pSm, G3D::AABox& pAABox); + void getBounds(const SubModel* pSm, G3D::AABox& pAABox); + //==================================== +} // VMAP +#endif diff --git a/src/shared/vmap/TileAssembler.cpp b/src/shared/vmap/TileAssembler.cpp new file mode 100644 index 00000000000..a51e2c86d63 --- /dev/null +++ b/src/shared/vmap/TileAssembler.cpp @@ -0,0 +1,571 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <G3D/Vector3.h> +#include <G3D/Triangle.h> + +#include "TileAssembler.h" +#include "CoordModelMapping.h" +#include "ModelContainer.h" + +#include <limits.h> +#include <string.h> + +#ifdef _ASSEMBLER_DEBUG +FILE *g_df = NULL; +#endif + +using namespace G3D; + +namespace VMAP +{ + //================================================================= + + Vector3 ModelPosition::transform(const Vector3& pIn) const + { + //return(pIn); + Vector3 out = pIn * iScale; + out = izMatrix * out; + out = ixMatrix * out; + out = iyMatrix * out; + return(out); + + } + //================================================================= + + TileAssembler::TileAssembler(const std::string& pSrcDirName, const std::string& pDestDirName) + { + iCurrentUniqueNameId = 0; + iFilterMethod = NULL; + iSrcDir = pSrcDirName; + iDestDir = pDestDirName; + //mkdir(iDestDir); + init(); + } + + //================================================================= + + TileAssembler::~TileAssembler() + { + delete iCoordModelMapping; + } + + //================================================================= + + void TileAssembler::init() + { + iCoordModelMapping = new CoordModelMapping(); + addWorldAreaMapId(0); //Azeroth + addWorldAreaMapId(1); //Kalimdor + addWorldAreaMapId(530); //Expansion01 + } + //================================================================= + + std::string getModNameFromModPosName(const std::string& pModPosName) + { + + size_t spos = pModPosName.find_first_of('#'); + std::string modelFileName = pModPosName.substr(0,spos); + return(modelFileName); + } + + //================================================================= + + unsigned int TileAssembler::getUniqueNameId(const std::string pName) + { + unsigned int result; + + if(!iUniqueNameIds.containsKey(pName)) + { + ++iCurrentUniqueNameId; + iUniqueNameIds.set(pName, iCurrentUniqueNameId); + } + result = iUniqueNameIds.get(pName); + return result; + } + + //================================================================= + + std::string TileAssembler::getDirEntryNameFromModName(unsigned int pMapId, const std::string& pModPosName) + { + size_t spos; + char buffer[20]; + + std::string modelFileName = getModNameFromModPosName(pModPosName); + //std::string fext = pModPosName.substr(modelFileName.length(),pModPosName.length()); + unsigned int fextId = getUniqueNameId(pModPosName); + sprintf(buffer, "_%07d",fextId); + std::string fext(buffer); + spos = modelFileName.find_last_of('/'); + std::string fname = modelFileName.substr(spos+1, modelFileName.length()); + spos = fname.find_last_of('.'); + fname = fname.substr(0,spos); + sprintf(buffer, "%03u", pMapId); + std::string dirEntry(buffer); + dirEntry.append("_"); + dirEntry.append(fname); + dirEntry.append(fext); + dirEntry.append(".vmap"); + return(dirEntry); + } + + //================================================================= + + void emptyArray(Array<ModelContainer*>& mc) + { + int no=mc.size(); + while(no > 0) + { + --no; + delete mc[no]; + mc.remove(no); + } + } + + //================================================================= + bool TileAssembler::convertWorld() + { + + #ifdef _ASSEMBLER_DEBUG + # ifdef _DEBUG + ::g_df = fopen("../TileAssembler_debug.txt", "wb"); + # else + ::g_df = fopen("../TileAssembler_release.txt", "wb"); + # endif + #endif + + bool result = true; + std::string fname = iSrcDir; + fname.append("/"); + fname.append("dir"); + iCoordModelMapping->setModelNameFilterMethod(iFilterMethod); + iCoordModelMapping->readCoordinateMapping(fname); + + Array<unsigned int> mapIds = iCoordModelMapping->getMaps(); + if(mapIds.size() == 0) + { + result = false; + } + for(int i=0; i<mapIds.size() && result; ++i) + { + unsigned int mapId = mapIds[i]; + + #ifdef _ASSEMBLER_DEBUG + if(mapId == 0) // "Azeroth" just for debug + { + for(int x=28; x<29 && result; ++x) //debug + { + for(int y=28; y<29 && result; ++y) + { + #else + // ignore DeeprunTram (369) it is too large for short vector and not important + // ignore test (13), Test (29) , development (451) + if(mapId != 369 && mapId != 13 && mapId != 29 && mapId != 451) + { + for(int x=0; x<66 && result; ++x) + { + for(int y=0; y<66 && result; ++y) + { + #endif + Array<ModelContainer*> mc; + std::string dirname; + char buffer[100]; + if(iCoordModelMapping->isWorldAreaMap(mapId) && x<65 && y<65) + { + sprintf(buffer, "%03u_%d_%d",mapId,y,x); // Let's flip x and y here + dirname = std::string(buffer); + } + else + { + sprintf(buffer, "%03u",mapId); + dirname = std::string(buffer); + } + result = fillModelContainerArray(dirname, mapId, x, y, mc); + emptyArray(mc); + } + } + } + } + #ifdef _ASSEMBLER_DEBUG + if(::g_df) fclose(::g_df); + #endif + + return result; + } + + //================================================================= + + bool TileAssembler::fillModelContainerArray(const std::string& pDirFileName, unsigned int pMapId, int pXPos, int pYPos, Array<ModelContainer*>& pMC) + { + bool result = true; + ModelContainer* modelContainer; + + NameCollection nameCollection = iCoordModelMapping->getFilenamesForCoordinate(pMapId, pXPos, pYPos); + if(nameCollection.size() > 0) + { + result = false; + char dirfilename[500]; + sprintf(dirfilename,"%s/%s.vmdir",iDestDir.c_str(),pDirFileName.c_str()); + FILE *dirfile = fopen(dirfilename, "ab"); + if(dirfile) + { + result = true; + char destnamebuffer[500]; + char fullnamedestnamebuffer[500]; + if(nameCollection.iMainFiles.size() >0) + { + sprintf(destnamebuffer,"%03u_%i_%i.vmap",pMapId, pYPos, pXPos); // flip it here too + std::string checkDoubleStr = std::string(dirfilename); + checkDoubleStr.append("##"); + checkDoubleStr.append(std::string(destnamebuffer)); + // Check, if same file already is in the same dir file + if(!iCoordModelMapping->isAlreadyProcessedSingleFile(checkDoubleStr)) + { + iCoordModelMapping->addAlreadyProcessedSingleFile(checkDoubleStr); + fprintf(dirfile, "%s\n",destnamebuffer); + sprintf(fullnamedestnamebuffer,"%s/%s",iDestDir.c_str(),destnamebuffer); + modelContainer = processNames(nameCollection.iMainFiles, fullnamedestnamebuffer); + if(modelContainer) + { + pMC.append(modelContainer); + } + else + { + result = false; + } + } + } + // process the large singe files + int pos = 0; + while(result && (pos < nameCollection.iSingeFiles.size())) + { + std::string destFileName = iDestDir; + destFileName.append("/"); + std::string dirEntryName = getDirEntryNameFromModName(pMapId,nameCollection.iSingeFiles[pos]); + std::string checkDoubleStr = std::string(dirfilename); + checkDoubleStr.append("##"); + checkDoubleStr.append(nameCollection.iSingeFiles[pos]); + // Check, if same file already is in the same dir file + if(!iCoordModelMapping->isAlreadyProcessedSingleFile(checkDoubleStr)) + { + iCoordModelMapping->addAlreadyProcessedSingleFile(checkDoubleStr); + fprintf(dirfile, "%s\n",dirEntryName.c_str()); + destFileName.append(dirEntryName); + + Array<std::string> positionarray; + positionarray.append(nameCollection.iSingeFiles[pos]); + + if(!iCoordModelMapping->isAlreadyProcessedSingleFile(nameCollection.iSingeFiles[pos])) + { + modelContainer = processNames(positionarray, destFileName.c_str()); + iCoordModelMapping->addAlreadyProcessedSingleFile(nameCollection.iSingeFiles[pos]); + if(modelContainer) + { + pMC.append(modelContainer); + } + else + { + result = false; + } + } + } + ++pos; + } + fclose(dirfile); + } + } + return(result); + } + + //================================================================= + + void removeEntriesFromTree(AABSPTree<SubModel *>* pTree) + { + Array<SubModel *> submodelArray; + pTree->getMembers(submodelArray); + int no = submodelArray.size(); + while(no > 0) + { + --no; + delete submodelArray[no]; + } + } + + //================================================================= + + ModelContainer* TileAssembler::processNames(const Array<std::string>& pPositions, const char* pDestFileName) + { + ModelContainer *modelContainer = 0; + + Vector3 basepos = Vector3(0,0,0); + AABSPTree<SubModel *>* mainTree = new AABSPTree<SubModel *>(); + + int pos = 0; + + bool result = true; + while(result && (pos < pPositions.size())) + { + std::string modelPosString = pPositions[pos]; + std::string modelFileName = getModNameFromModPosName(modelPosString); + + if(!fillModelIntoTree(mainTree, basepos, modelPosString, modelFileName)) + { + result = false; + break; + } + ++pos; + } + if(result && mainTree->size() > 0) + { + mainTree->balance(); + modelContainer = new ModelContainer(mainTree); + modelContainer->writeFile(pDestFileName); + } + removeEntriesFromTree(mainTree); + + delete mainTree; + + return(modelContainer); + } + + //================================================================= + bool TileAssembler::readRawFile(std::string& pModelFilename, ModelPosition& pModelPosition, AABSPTree<SubModel *> *pMainTree) + { + bool result = false; + + std::string filename = iSrcDir; + if(filename.length() >0) + filename.append("/"); + filename.append(pModelFilename); + FILE *rf = fopen(filename.c_str(), "rb"); + if(!rf) + { + // depending on the extractor version, the data could be located in the root dir + std::string baseModelFilename = pModelFilename.substr((pModelFilename.find_first_of("/")+1),pModelFilename.length()); + filename = iSrcDir; + if(filename.length() >0) + filename.append("/"); + filename.append(baseModelFilename); + rf = fopen(filename.c_str(), "rb"); + } + char ident[8]; + + int trianglecount =0; + + #ifdef _ASSEMBLER_DEBUG + int startgroup = 0; //2; + int endgroup = INT_MAX; //2; + fprintf(::g_df,"-------------------------------------------------\n"); + fprintf(::g_df,"%s\n", pModelFilename.c_str()); + fprintf(::g_df,"-------------------------------------------------\n"); + #else + int startgroup = 0; + int endgroup = INT_MAX; + #endif + + if(rf) + { + if(fread(&ident, 8, 1, rf) != 1) { fclose(rf); return(false); } + if(strcmp(ident, "VMAP001") == 0) + { + // OK, do nothing + } + else if(strcmp(ident, "VMAP002") == 0) + { + // we have to read one int. This is needed during the export and we have to skip it here + int tempNVectors; + if(fread(&tempNVectors, sizeof(int), 1, rf) != 1) { fclose(rf); return(false); } + + } + else + { + // wrong version + fclose(rf); + return(false); + } + G3D::uint32 groups; + char blockId[5]; + blockId[4] = 0; + int blocksize; + + if(fread(&groups, sizeof(G3D::uint32), 1, rf) != 1) { fclose(rf); return(false); } + + for(int g=0;g<(int)groups;g++) + { + // group MUST NOT have more then 65536 indexes !! Array will have a problem with that !! (strange ...) + Array<int> tempIndexArray; + Array<Vector3> tempVertexArray; + + AABSPTree<Triangle> *gtree = new AABSPTree<Triangle>(); + + G3D::uint32 flags; + if(fread(&flags, sizeof(G3D::uint32), 1, rf) != 1) { fclose(rf); return(false); } + + G3D::uint32 branches; + if(fread(&blockId, 4, 1, rf) != 1) { fclose(rf); return(false); } + if(strcmp(blockId, "GRP ") != 0) { fclose(rf); return(false); } + if(fread(&blocksize, sizeof(int), 1, rf) != 1) { fclose(rf); return(false); } + if(fread(&branches, sizeof(G3D::uint32), 1, rf) != 1) { fclose(rf); return(false); } + for(int b=0;b<(int)branches; b++) + { + G3D::uint32 indexes; + // indexes for each branch (not used jet) + if(fread(&indexes, sizeof(G3D::uint32), 1, rf) != 1) { fclose(rf); return(false); } + } + + // ---- indexes + if(fread(&blockId, 4, 1, rf) != 1) { fclose(rf); return(false); } + if(strcmp(blockId, "INDX") != 0) { fclose(rf); return(false); } + if(fread(&blocksize, sizeof(int), 1, rf) != 1) { fclose(rf); return(false); } + unsigned int nindexes; + if(fread(&nindexes, sizeof(G3D::uint32), 1, rf) != 1) { fclose(rf); return(false); } + if(nindexes >0) + { + unsigned short *indexarray = new unsigned short[nindexes*sizeof(unsigned short)]; + if(fread(indexarray, sizeof(unsigned short), nindexes, rf) != nindexes) { fclose(rf); return(false); } + for(int i=0;i<(int)nindexes; i++) + { + unsigned short val = indexarray[i]; + tempIndexArray.append(val); + } + delete indexarray; + } + + // ---- vectors + if(fread(&blockId, 4, 1, rf) != 1) {fclose(rf); return(false); } + if(strcmp(blockId, "VERT") != 0) { fclose(rf); return(false); } + if(fread(&blocksize, sizeof(int), 1, rf) != 1) { fclose(rf); return(false); } + unsigned int nvectors; + if(fread(&nvectors, sizeof(int), 1, rf) != 1) { fclose(rf); return(false); } + float *vectorarray = 0; + if(nvectors >0) + { + vectorarray = new float[nvectors*sizeof(float)*3]; + if(fread(vectorarray, sizeof(float)*3, nvectors, rf) != nvectors) { fclose(rf); return(false); } + } + // ----- liquit + if(flags & 1) + { + // we have liquit -> not handled yet ... skip + if(fread(&blockId, 4, 1, rf) != 1) { fclose(rf); return(false); } + if(strcmp(blockId, "LIQU") != 0) { fclose(rf); return(false); } + if(fread(&blocksize, sizeof(int), 1, rf) != 1) { fclose(rf); return(false); } + fseek(rf, blocksize, SEEK_CUR); + } + + for(unsigned int i=0, indexNo=0; indexNo<nvectors; indexNo++) + { + Vector3 v = Vector3(vectorarray[i+2], vectorarray[i+1], vectorarray[i+0]); + i+=3; + v = pModelPosition.transform(v); + + float swapy = v.y; + v.y = v.x; + v.x = swapy; + + tempVertexArray.append(v); + } + + // ---- calculate triangles + int rest = nindexes%3; + if(rest != 0) + { + nindexes -= rest; + } + + for(unsigned int i=0;i<(nindexes);) + { + Triangle t = Triangle(tempVertexArray[tempIndexArray[i+2]], tempVertexArray[tempIndexArray[i+1]], tempVertexArray[tempIndexArray[i+0]] ); + i+=3; + ++trianglecount; + if(g>= startgroup && g <= endgroup) + { + gtree->insert(t); + } + } + + if(vectorarray != 0) + { + delete vectorarray; + } + + if(gtree->size() >0) + { + gtree->balance(); + SubModel *sm = new SubModel(gtree); + #ifdef _ASSEMBLER_DEBUG + if(::g_df) fprintf(::g_df,"group trianglies: %d, Tris: %d, Nodes: %d, gtree.triangles: %d\n", g, sm->getNTriangles(), sm->getNNodes(), gtree->memberTable.size()); + if(sm->getNTriangles() != gtree->memberTable.size()) + { + if(::g_df) fprintf(::g_df,"ERROR !!!! group trianglies: %d, Tris: %d, Nodes: %d, gtree.triangles: %d\n", g, sm->getNTriangles(), sm->getNNodes(), gtree->memberTable.size()); + } + #endif + sm->setBasePosition(pModelPosition.iPos); + pMainTree->insert(sm); + } + delete gtree; + } + fclose(rf); + result = true; + } + return(result); + } + + //================================================================= + + bool TileAssembler::fillModelIntoTree(AABSPTree<SubModel *> *pMainTree, const Vector3& pBasePos, std::string& pPos, std::string& pModelFilename) + { + bool result = false; + ModelPosition modelPosition; + getModelPosition(pPos, modelPosition); + // all should be relative to object base position + modelPosition.moveToBasePos(pBasePos); + + modelPosition.init(); + + if(readRawFile(pModelFilename, modelPosition, pMainTree)) + { + result = true; + } + + return result; + } + + //================================================================= + void TileAssembler::getModelPosition(std::string& pPosString, ModelPosition& pModelPosition) + { + float vposarray[3]; + float vdirarray[3]; + float scale; + + size_t spos = pPosString.find_first_of('#'); + std::string stripedPosString = pPosString.substr(spos+1,pPosString.length()); + + sscanf(stripedPosString.c_str(), "%f,%f,%f_%f,%f,%f_%f", + &vposarray[0],&vposarray[1],&vposarray[2], + &vdirarray[0],&vdirarray[1],&vdirarray[2], + &scale); + + pModelPosition.iPos = Vector3(vposarray[0], vposarray[1], vposarray[2]); + pModelPosition.iDir = Vector3(vdirarray[0], vdirarray[1], vdirarray[2]); + pModelPosition.iScale = scale; + + } + //========================================== + + } // VMAP diff --git a/src/shared/vmap/TileAssembler.h b/src/shared/vmap/TileAssembler.h new file mode 100644 index 00000000000..afcce24c664 --- /dev/null +++ b/src/shared/vmap/TileAssembler.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _TILEASSEMBLER_H_ +#define _TILEASSEMBLER_H_ + +// load our modified version first !! +#include "AABSPTree.h" + +#include <G3D/Vector3.h> + +#include "CoordModelMapping.h" +#include "SubModel.h" +#include "ModelContainer.h" + +namespace VMAP +{ + /** + This Class is used to convert raw vector data into balanced BSP-Trees. + To start the conversion call convertWorld(). + */ + //=============================================== + + class ModelPosition + { + private: + G3D::Matrix3 ixMatrix; + G3D::Matrix3 iyMatrix; + G3D::Matrix3 izMatrix; + public: + G3D::Vector3 iPos; + G3D::Vector3 iDir; + float iScale; + void init() + { + + // Swap x and y the raw data uses the axis differently + ixMatrix = G3D::Matrix3::fromAxisAngle(G3D::Vector3::unitY(),-(G3D::pi()*iDir.x/180.0)); + iyMatrix = G3D::Matrix3::fromAxisAngle(G3D::Vector3::unitX(),-(G3D::pi()*iDir.y/180.0)); + izMatrix = G3D::Matrix3::fromAxisAngle(G3D::Vector3::unitZ(),-(G3D::pi()*iDir.z/180.0)); + + } + G3D::Vector3 transform(const G3D::Vector3& pIn) const; + void moveToBasePos(const G3D::Vector3& pBasePos) { iPos -= pBasePos; } + }; + //=============================================== + + class TileAssembler + { + private: + CoordModelMapping *iCoordModelMapping; + std::string iDestDir; + std::string iSrcDir; + bool (*iFilterMethod)(char *pName); + G3D::Table<std::string, unsigned int > iUniqueNameIds; + unsigned int iCurrentUniqueNameId; + + public: + TileAssembler(const std::string& pSrcDirName, const std::string& pDestDirName); + virtual ~TileAssembler(); + + bool fillModelContainerArray(const std::string& pDirFileName, unsigned int pMapId, int pXPos, int pYPos, G3D::Array<ModelContainer*>& pMC); + ModelContainer* processNames(const G3D::Array<std::string>& pPosFileNames, const char* pDestFileName); + + void init(); + bool convertWorld(); + + bool fillModelIntoTree(G3D::AABSPTree<SubModel *> *pMainTree, const G3D::Vector3& pBasePos, std::string& pPosFilename, std::string& pModelFilename); + void getModelPosition(std::string& pPosString, ModelPosition& pModelPosition); + bool readRawFile(std::string& pModelFilename, ModelPosition& pModelPosition, G3D::AABSPTree<SubModel *> *pMainTree); + void addWorldAreaMapId(unsigned int pMapId) { iCoordModelMapping->addWorldAreaMap(pMapId); } + void setModelNameFilterMethod(bool (*pFilterMethod)(char *pName)) { iFilterMethod = pFilterMethod; } + std::string getDirEntryNameFromModName(unsigned int pMapId, const std::string& pModPosName); + unsigned int getUniqueNameId(const std::string pName); + }; + //=============================================== +} // VMAP +#endif /*_TILEASSEMBLER_H_*/ diff --git a/src/shared/vmap/TreeNode.cpp b/src/shared/vmap/TreeNode.cpp new file mode 100644 index 00000000000..97846d6c992 --- /dev/null +++ b/src/shared/vmap/TreeNode.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "TreeNode.h" + +using namespace G3D; + +namespace VMAP +{ + + TreeNode const* TreeNode::getChild(TreeNode const* pValueArray,int pNo) const + { + if(iChilds[pNo] != -1) + return(&pValueArray[iChilds[pNo]]); + else + return(NULL); + } + + //================================================================= + //================================================================= + //================================================================= +} diff --git a/src/shared/vmap/TreeNode.h b/src/shared/vmap/TreeNode.h new file mode 100644 index 00000000000..c034b3c88f3 --- /dev/null +++ b/src/shared/vmap/TreeNode.h @@ -0,0 +1,223 @@ +/* +* Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef _TREENODE_H +#define _TREENODE_H + +#include "ShortVector.h" +#include "ShortBox.h" +#include "NodeValueAccess.h" +#include "VMapTools.h" + +#include <G3D/Vector3.h> +#include <G3D/AABox.h> + +namespace VMAP +{ + /** + This Class is mainly taken from G3D/AABSPTree.h and modified to match our data structure. + It is the node within our static BSP-Trees. + It does not use pointers but indexes to access the values and other nodes. + */ + + //===================================================== + + class TreeNode + { + private: + /** Location along the specified axis */ + float iSplitLocation; + // Offest or the clients + int iChilds[2]; + //Position within the TriangleBox array + unsigned int iStartPosition; + G3D::Vector3::Axis iSplitAxis; + G3D::AABox iBounds; + unsigned short iNumberOfValues; + public: + TreeNode() {} + TreeNode(unsigned short pNValues, unsigned int pStartPosition) + { + iChilds[0] = -1; + iChilds[1] = -1; + iStartPosition = pStartPosition; + iNumberOfValues = pNValues; + } + + bool hasChilds() const { return(iChilds[0] >= 0 || iChilds[1] >= 0); } + + TreeNode const* getChild(TreeNode const* pValueArray, int pNo) const; + // pChildNo = 0 or 1 + inline void setChildPos(int pChildNo, int pChildPosInTreeNodeArray) { iChilds[pChildNo] = pChildPosInTreeNodeArray; } + + inline G3D::Vector3::Axis getSplitAxis() const { return(iSplitAxis); } + + inline void setSplitAxis(G3D::Vector3::Axis a) { iSplitAxis = a; } + inline void setSplitLocation(float l) { iSplitLocation = l; } + + inline void setBounds(const G3D::AABox& pBox) { iBounds = pBox; } + + inline void setBounds(const G3D::Vector3& lo, const G3D::Vector3& hi) { iBounds.set(lo,hi); } + + inline void getBounds(G3D::AABox& pBox) const { pBox.set(iBounds.low(),iBounds.high()); } + + inline float getSplitLocation() const { return(iSplitLocation); } + + inline unsigned short getNValues() const { return (iNumberOfValues); } + + inline unsigned int getStartPosition() const { return(iStartPosition); } + + inline bool operator==(const TreeNode& n) const + { + return ((iSplitLocation == n.iSplitLocation) && + (iChilds[0] == n.iChilds[0]) && (iChilds[1] == n.iChilds[1]) && + (iStartPosition == n.iStartPosition) && + (iSplitAxis == n.iSplitAxis) && + (iBounds == n.iBounds) && + (iNumberOfValues == n.iNumberOfValues)); + } + + inline bool operator!=(const TreeNode& n) const + { + return !((iSplitLocation == n.iSplitLocation) && + (iChilds[0] == n.iChilds[0]) && (iChilds[1] == n.iChilds[1]) && + (iStartPosition == n.iStartPosition) && + (iSplitAxis == n.iSplitAxis) && + (iBounds == n.iBounds) && + (iNumberOfValues == n.iNumberOfValues)); + } + + /** Returns true if the ray intersects this node */ + bool intersects(const G3D::Ray& ray, float distance) const { + // See if the ray will ever hit this node or its children + G3D::Vector3 location; + bool alreadyInsideBounds = false; + bool rayWillHitBounds = + MyCollisionDetection::collisionLocationForMovingPointFixedAABox( + ray.origin, ray.direction, iBounds, location, alreadyInsideBounds); + + bool canHitThisNode = (alreadyInsideBounds || + (rayWillHitBounds && ((location - ray.origin).squaredLength() < (distance*distance)))); + + return canHitThisNode; + } + + template<typename RayCallback, typename TNode, typename TValue> + void intersectRay( + const G3D::Ray& ray, + RayCallback& intersectCallback, + float& distance, + const NodeValueAccess<TNode, TValue>& pNodeValueAccess, + bool pStopAtFirstHit, + bool intersectCallbackIsFast) const { + float enterDistance = distance; + if (! intersects(ray, distance)) { + // The ray doesn't hit this node, so it can't hit the children of the node. + return; + } + + // Test for intersection against every object at this node. + for (unsigned int v = iStartPosition; v < (iNumberOfValues+iStartPosition); ++v) { + const TValue& nodeValue = pNodeValueAccess.getValue(v); + bool canHitThisObject = true; + if (! intersectCallbackIsFast) { + // See if + G3D::Vector3 location; + const G3D::AABox& bounds = nodeValue.getAABoxBounds(); + bool alreadyInsideBounds = false; + bool rayWillHitBounds = + MyCollisionDetection::collisionLocationForMovingPointFixedAABox( + ray.origin, ray.direction, bounds, location, alreadyInsideBounds); + + canHitThisObject = (alreadyInsideBounds || + (rayWillHitBounds && ((location - ray.origin).squaredLength() < (distance*distance)))); + } + + if (canHitThisObject) { + // It is possible that this ray hits this object. Look for the intersection using the + // callback. + intersectCallback(ray, &nodeValue, pStopAtFirstHit, distance); + } + if(pStopAtFirstHit && distance < enterDistance) + return; + } + + // There are three cases to consider next: + // + // 1. the ray can start on one side of the splitting plane and never enter the other, + // 2. the ray can start on one side and enter the other, and + // 3. the ray can travel exactly down the splitting plane + + enum {NONE = -1}; + int firstChild = NONE; + int secondChild = NONE; + + if (ray.origin[iSplitAxis] < iSplitLocation) { + + // The ray starts on the small side + firstChild = 0; + + if (ray.direction[iSplitAxis] > 0) { + // The ray will eventually reach the other side + secondChild = 1; + } + + } else if (ray.origin[iSplitAxis] > iSplitLocation) { + + // The ray starts on the large side + firstChild = 1; + + if (ray.direction[iSplitAxis] < 0) { + secondChild = 0; + } + } else { + // The ray starts on the splitting plane + if (ray.direction[iSplitAxis] < 0) { + // ...and goes to the small side + firstChild = 0; + } else if (ray.direction[iSplitAxis] > 0) { + // ...and goes to the large side + firstChild = 1; + } + } + + // Test on the side closer to the ray origin. + if ((firstChild != NONE) && iChilds[firstChild]>0) { + getChild(pNodeValueAccess.getNodePtr() , firstChild)->intersectRay(ray, intersectCallback, distance, pNodeValueAccess, pStopAtFirstHit,intersectCallbackIsFast); + if(pStopAtFirstHit && distance < enterDistance) + return; + } + if (ray.direction[iSplitAxis] != 0) { + // See if there was an intersection before hitting the splitting plane. + // If so, there is no need to look on the far side and recursion terminates. + float distanceToSplittingPlane = (iSplitLocation - ray.origin[iSplitAxis]) / ray.direction[iSplitAxis]; + if (distanceToSplittingPlane > distance) { + // We aren't going to hit anything else before hitting the splitting plane, + // so don't bother looking on the far side of the splitting plane at the other + // child. + return; + } + } + // Test on the side farther from the ray origin. + if ((secondChild != NONE) && iChilds[secondChild]>0) { + getChild(pNodeValueAccess.getNodePtr() , secondChild)->intersectRay(ray, intersectCallback, distance, pNodeValueAccess, pStopAtFirstHit,intersectCallbackIsFast); + } + } + }; +} +#endif diff --git a/src/shared/vmap/VMapDefinitions.h b/src/shared/vmap/VMapDefinitions.h new file mode 100644 index 00000000000..ec7858b0cc1 --- /dev/null +++ b/src/shared/vmap/VMapDefinitions.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _VMAPDEFINITIONS_H +#define _VMAPDEFINITIONS_H +#include <cstring> + +namespace VMAP +{ + //===================================== + #define MAX_CAN_FALL_DISTANCE 10.0 + const char VMAP_MAGIC[] = "VMAP_2.0"; + + class VMapDefinitions + { + public: + static const double getMaxCanFallDistance() { return(MAX_CAN_FALL_DISTANCE); } + }; + + //====================================== +} +#endif diff --git a/src/shared/vmap/VMapFactory.cpp b/src/shared/vmap/VMapFactory.cpp new file mode 100644 index 00000000000..15454533c8b --- /dev/null +++ b/src/shared/vmap/VMapFactory.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <sys/types.h> +#include "VMapFactory.h" +#include "VMapManager.h" + +using namespace G3D; + +namespace VMAP +{ + extern void chompAndTrim(std::string& str); + + VMapManager *gVMapManager = 0; + Table<unsigned int , bool>* iIgnoreSpellIds=0; + + //=============================================== + // result false, if no more id are found + + bool getNextId(const std::string& pString, unsigned int& pStartPos, unsigned int& pId) + { + bool result = false; + unsigned int i; + for(i=pStartPos;i<pString.size(); ++i) + { + if(pString[i] == ',') + { + break; + } + } + if(i>pStartPos) + { + std::string idString = pString.substr(pStartPos, i-pStartPos); + pStartPos = i+1; + chompAndTrim(idString); + pId = atoi(idString.c_str()); + result = true; + } + return(result); + } + + //=============================================== + /** + parameter: String of map ids. Delimiter = "," + */ + + void VMapFactory::preventSpellsFromBeingTestedForLoS(const char* pSpellIdString) + { + if(!iIgnoreSpellIds) + iIgnoreSpellIds = new Table<unsigned int , bool>(); + if(pSpellIdString != NULL) + { + unsigned int pos =0; + unsigned int id; + std::string confString(pSpellIdString); + chompAndTrim(confString); + while(getNextId(confString, pos, id)) + { + iIgnoreSpellIds->set(id, true); + } + } + } + + //=============================================== + + bool VMapFactory::checkSpellForLoS(unsigned int pSpellId) + { + return(!iIgnoreSpellIds->containsKey(pSpellId)); + } + + //=============================================== + // just return the instance + IVMapManager* VMapFactory::createOrGetVMapManager() + { + if(gVMapManager == 0) + gVMapManager= new VMapManager(); // should be taken from config ... Please change if you like :-) + return gVMapManager; + } + + //=============================================== + // delete all internal data structures + void VMapFactory::clear() + { + if(iIgnoreSpellIds) + delete iIgnoreSpellIds; + if(gVMapManager) + delete gVMapManager; + } +} diff --git a/src/shared/vmap/VMapFactory.h b/src/shared/vmap/VMapFactory.h new file mode 100644 index 00000000000..d63f10d94b4 --- /dev/null +++ b/src/shared/vmap/VMapFactory.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _VMAPFACTORY_H +#define _VMAPFACTORY_H + +#include "IVMapManager.h" + +/** +This is the access point to the VMapManager. +*/ + +namespace VMAP +{ + //=========================================================== + + class VMapFactory + { + public: + static IVMapManager* createOrGetVMapManager(); + static void clear(); + + static void preventSpellsFromBeingTestedForLoS(const char* pSpellIdString); + static bool checkSpellForLoS(unsigned int pSpellId); + }; + +} +#endif diff --git a/src/shared/vmap/VMapManager.cpp b/src/shared/vmap/VMapManager.cpp new file mode 100644 index 00000000000..a4427060184 --- /dev/null +++ b/src/shared/vmap/VMapManager.cpp @@ -0,0 +1,780 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "VMapManager.h" +#include "VMapDefinitions.h" + +using namespace G3D; + +namespace VMAP +{ + + //========================================================= + + VMapManager::VMapManager() + { +#ifdef _VMAP_LOG_DEBUG + iCommandLogger.setFileName("vmapcmd.log"); + iCommandLogger.setResetFile(); +#endif + } + + //========================================================= + + VMapManager::~VMapManager(void) + { + Array<unsigned int > keyArray = iInstanceMapTrees.getKeys(); + for(int i=0;i<keyArray.size(); ++i) + { + delete iInstanceMapTrees.get(keyArray[i]); + iInstanceMapTrees.remove(keyArray[i]); + } + } + + //========================================================= + + Vector3 VMapManager::convertPositionToInternalRep(float x, float y, float z) const + { + float pos[3]; + pos[0] = y; + pos[1] = z; + pos[2] = x; + double full = 64.0*533.33333333; + double mid = full/2.0; + pos[0] = full- (pos[0] + mid); + pos[2] = full- (pos[2] + mid); + + return(Vector3(pos)); + } + + //========================================================= + + Vector3 VMapManager::convertPositionToMangosRep(float x, float y, float z) const + { + float pos[3]; + pos[0] = z; + pos[1] = x; + pos[2] = y; + double full = 64.0*533.33333333; + double mid = full/2.0; + pos[0] = -((mid+pos[0])-full); + pos[1] = -((mid+pos[1])-full); + + return(Vector3(pos)); + } + //========================================================= + + std::string VMapManager::getDirFileName(unsigned int pMapId, int x, int y) const + { + char name[FILENAMEBUFFER_SIZE]; + + sprintf(name, "%03u_%d_%d%s",pMapId, x, y, DIR_FILENAME_EXTENSION); + return(std::string(name)); + } + + //========================================================= + std::string VMapManager::getDirFileName(unsigned int pMapId) const + { + char name[FILENAMEBUFFER_SIZE]; + + sprintf(name, "%03d%s",pMapId, DIR_FILENAME_EXTENSION); + return(std::string(name)); + } + //========================================================= + // remote last return or LF + void chomp(std::string& str) + { + while(str.length() >0) + { + char lc = str[str.length()-1]; + if(lc == '\r' || lc == '\n') + { + str = str.substr(0,str.length()-1); + } + else + { + break; + } + } + } + //========================================================= + + void chompAndTrim(std::string& str) + { + while(str.length() >0) + { + char lc = str[str.length()-1]; + if(lc == '\r' || lc == '\n' || lc == ' ' || lc == '"' || lc == '\'') + { + str = str.substr(0,str.length()-1); + } + else + { + break; + } + } + while(str.length() >0) + { + char lc = str[0]; + if(lc == ' ' || lc == '"' || lc == '\'') + { + str = str.substr(1,str.length()-1); + } + else + { + break; + } + } + } + + //========================================================= + // result false, if no more id are found + + bool getNextMapId(const std::string& pString, unsigned int& pStartPos, unsigned int& pId) + { + bool result = false; + unsigned int i; + for(i=pStartPos;i<pString.size(); ++i) + { + if(pString[i] == ',') + { + break; + } + } + if(i>pStartPos) + { + std::string idString = pString.substr(pStartPos, i-pStartPos); + pStartPos = i+1; + chompAndTrim(idString); + pId = atoi(idString.c_str()); + result = true; + } + return(result); + } + + //========================================================= + /** + Block maps from being used. + parameter: String of map ids. Delimiter = "," + e.g.: "0,1,590" + */ + + void VMapManager::preventMapsFromBeingUsed(const char* pMapIdString) + { + if(pMapIdString != NULL) + { + unsigned int pos =0; + unsigned int id; + std::string confString(pMapIdString); + chompAndTrim(confString); + while(getNextMapId(confString, pos, id)) + { + iIgnoreMapIds.set(id, true); + } + } + } + + //========================================================= + + int VMapManager::loadMap(const char* pBasePath, unsigned int pMapId, int x, int y) + { + int result = VMAP_LOAD_RESULT_IGNORED; + if(isMapLoadingEnabled() && !iIgnoreMapIds.containsKey(pMapId)) + { + bool loaded = _loadMap(pBasePath, pMapId, x, y, false); + if(!loaded) + { + // if we can't load the map it might be splitted into tiles. Try that one and store the result + loaded = _loadMap(pBasePath, pMapId, x, y, true); + if(loaded) + { + iMapsSplitIntoTiles.set(pMapId, true); + } + } + if(loaded) + { + result = VMAP_LOAD_RESULT_OK; + // just for debugging +#ifdef _VMAP_LOG_DEBUG + Command c = Command(); + c.fillLoadTileCmd(x, y, pMapId); + iCommandLogger.appendCmd(c); +#endif + } + else + { + result = VMAP_LOAD_RESULT_ERROR; + } + } + return result; + } + + //========================================================= + // load one tile (internal use only) + + bool VMapManager::_loadMap(const char* pBasePath, unsigned int pMapId, int x, int y, bool pForceTileLoad) + { + bool result = false; + std::string dirFileName; + if(pForceTileLoad || iMapsSplitIntoTiles.containsKey(pMapId)) + { + dirFileName = getDirFileName(pMapId,x,y); + } + else + { + dirFileName = getDirFileName(pMapId); + } + MapTree* instanceTree; + if(!iInstanceMapTrees.containsKey(pMapId)) + { + instanceTree = new MapTree(pBasePath); + iInstanceMapTrees.set(pMapId, instanceTree); + } + else + instanceTree = iInstanceMapTrees.get(pMapId); + + unsigned int mapTileIdent = MAP_TILE_IDENT(x,y); + result = instanceTree->loadMap(dirFileName, mapTileIdent); + if(!result) // remove on fail + { + if(instanceTree->size() == 0) + { + iInstanceMapTrees.remove(pMapId); + delete instanceTree; + } + } + return(result); + } + + //========================================================= + + bool VMapManager::_existsMap(const std::string& pBasePath, unsigned int pMapId, int x, int y, bool pForceTileLoad) + { + bool result = false; + std::string dirFileName; + if(pForceTileLoad || iMapsSplitIntoTiles.containsKey(pMapId)) + { + dirFileName = getDirFileName(pMapId,x,y); + } + else + { + dirFileName = getDirFileName(pMapId); + } + size_t len = pBasePath.length() + dirFileName.length(); + char *filenameBuffer = new char[len+1]; + sprintf(filenameBuffer, "%s%s", pBasePath.c_str(), dirFileName.c_str()); + FILE* df = fopen(filenameBuffer, "rb"); + if(df) + { + char lineBuffer[FILENAMEBUFFER_SIZE]; + if (fgets(lineBuffer, FILENAMEBUFFER_SIZE-1, df) != 0) + { + std::string name = std::string(lineBuffer); + chomp(name); + if(name.length() >1) + { + sprintf(filenameBuffer, "%s%s", pBasePath.c_str(), name.c_str()); + FILE* df2 = fopen(filenameBuffer, "rb"); + if(df2) + { + char magic[8]; + fread(magic,1,8,df2); + if(!strncmp(VMAP_MAGIC,magic,8)) + result = true; + fclose(df2); + } + } + } + fclose(df); + } + delete[] filenameBuffer; + return result; + } + + //========================================================= + + bool VMapManager::existsMap(const char* pBasePath, unsigned int pMapId, int x, int y) + { + std::string basePath = std::string(pBasePath); + if(basePath.length() > 0 && (basePath[basePath.length()-1] != '/' || basePath[basePath.length()-1] != '\\')) + { + basePath.append("/"); + } + bool found = _existsMap(basePath, pMapId, x, y, false); + if(!found) + { + // if we can't load the map it might be splitted into tiles. Try that one and store the result + found = _existsMap(basePath, pMapId, x, y, true); + if(found) + { + iMapsSplitIntoTiles.set(pMapId, true); + } + } + return found; + } + + //========================================================= + + void VMapManager::unloadMap(unsigned int pMapId, int x, int y) + { + _unloadMap(pMapId, x, y); + +#ifdef _VMAP_LOG_DEBUG + Command c = Command(); + c.fillUnloadTileCmd(pMapId, x,y); + iCommandLogger.appendCmd(c); +#endif + } + + //========================================================= + + void VMapManager::_unloadMap(unsigned int pMapId, int x, int y) + { + if(iInstanceMapTrees.containsKey(pMapId)) + { + MapTree* instanceTree = iInstanceMapTrees.get(pMapId); + std::string dirFileName; + if(iMapsSplitIntoTiles.containsKey(pMapId)) + { + dirFileName = getDirFileName(pMapId,x,y); + } + else + { + dirFileName = getDirFileName(pMapId); + } + unsigned int mapTileIdent = MAP_TILE_IDENT(x,y); + instanceTree->unloadMap(dirFileName, mapTileIdent); + if(instanceTree->size() == 0) + { + iInstanceMapTrees.remove(pMapId); + delete instanceTree; + } + } + } + + //========================================================= + + void VMapManager::unloadMap(unsigned int pMapId) + { + if(iInstanceMapTrees.containsKey(pMapId)) + { + MapTree* instanceTree = iInstanceMapTrees.get(pMapId); + std::string dirFileName = getDirFileName(pMapId); + instanceTree->unloadMap(dirFileName, 0, true); + if(instanceTree->size() == 0) + { + iInstanceMapTrees.remove(pMapId); + delete instanceTree; + } +#ifdef _VMAP_LOG_DEBUG + Command c = Command(); + c.fillUnloadTileCmd(pMapId); + iCommandLogger.appendCmd(c); +#endif + } + } + //========================================================== + + bool VMapManager::isInLineOfSight(unsigned int pMapId, float x1, float y1, float z1, float x2, float y2, float z2) + { + bool result = true; + if(isLineOfSightCalcEnabled() && iInstanceMapTrees.containsKey(pMapId)) + { + Vector3 pos1 = convertPositionToInternalRep(x1,y1,z1); + Vector3 pos2 = convertPositionToInternalRep(x2,y2,z2); + if(pos1 != pos2) + { + MapTree* mapTree = iInstanceMapTrees.get(pMapId); + result = mapTree->isInLineOfSight(pos1, pos2); +#ifdef _VMAP_LOG_DEBUG + Command c = Command(); + // save the orig vectors + c.fillTestVisCmd(pMapId,Vector3(x1,y1,z1),Vector3(x2,y2,z2),result); + iCommandLogger.appendCmd(c); +#endif + } + } + return(result); + } + //========================================================= + /** + get the hit position and return true if we hit something + otherwise the result pos will be the dest pos + */ + bool VMapManager::getObjectHitPos(unsigned int pMapId, float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float &ry, float& rz, float pModifyDist) + { + bool result = false; + rx=x2; + ry=y2; + rz=z2; + if(isLineOfSightCalcEnabled()) + { + if(iInstanceMapTrees.containsKey(pMapId)) + { + Vector3 pos1 = convertPositionToInternalRep(x1,y1,z1); + Vector3 pos2 = convertPositionToInternalRep(x2,y2,z2); + Vector3 resultPos; + MapTree* mapTree = iInstanceMapTrees.get(pMapId); + result = mapTree->getObjectHitPos(pos1, pos2, resultPos, pModifyDist); + resultPos = convertPositionToMangosRep(resultPos.x,resultPos.y,resultPos.z); + rx = resultPos.x; + ry = resultPos.y; + rz = resultPos.z; +#ifdef _VMAP_LOG_DEBUG + Command c = Command(); + c.fillTestObjectHitCmd(pMapId, pos1, pos2, resultPos, result); + iCommandLogger.appendCmd(c); +#endif + } + } + return result; + } + + //========================================================= + /** + get height or INVALID_HEIGHT if to hight was calculated + */ + + //int gGetHeightCounter = 0; + float VMapManager::getHeight(unsigned int pMapId, float x, float y, float z) + { + float height = VMAP_INVALID_HEIGHT_VALUE; //no height + if(isHeightCalcEnabled() && iInstanceMapTrees.containsKey(pMapId)) + { + Vector3 pos = convertPositionToInternalRep(x,y,z); + MapTree* mapTree = iInstanceMapTrees.get(pMapId); + height = mapTree->getHeight(pos); + if(!(height < inf())) + { + height = VMAP_INVALID_HEIGHT_VALUE; //no height + } +#ifdef _VMAP_LOG_DEBUG + Command c = Command(); + c.fillTestHeightCmd(pMapId,Vector3(x,y,z),height); + iCommandLogger.appendCmd(c); +#endif + } + return(height); + } + + //========================================================= + /** + used for debugging + */ + bool VMapManager::processCommand(char *pCommand) + { + bool result = false; + std::string cmd = std::string(pCommand); + if(cmd == "startlog") + { +#ifdef _VMAP_LOG_DEBUG + + iCommandLogger.enableWriting(true); +#endif + result = true; + } + else if(cmd == "stoplog") + { +#ifdef _VMAP_LOG_DEBUG + iCommandLogger.appendCmd(Command()); // Write stop command + iCommandLogger.enableWriting(false); +#endif + result = true; + } + else if(cmd.find_first_of("pos ") == 0) + { + float x,y,z; + sscanf(pCommand, "pos %f,%f,%f",&x,&y,&z); +#ifdef _VMAP_LOG_DEBUG + Command c = Command(); + c.fillSetPosCmd(convertPositionToInternalRep(x,y,z)); + iCommandLogger.appendCmd(c); + iCommandLogger.enableWriting(false); +#endif + result = true; + } + return result; + } + + //========================================================= + //========================================================= + //========================================================= + + MapTree::MapTree(const char* pBaseDir) + { + iBasePath = std::string(pBaseDir); + if(iBasePath.length() > 0 && (iBasePath[iBasePath.length()-1] != '/' || iBasePath[iBasePath.length()-1] != '\\')) + { + iBasePath.append("/"); + } + iTree = new AABSPTree<ModelContainer *>(); + } + + //========================================================= + MapTree::~MapTree() + { + Array<ModelContainer *> mcArray; + iTree->getMembers(mcArray); + int no = mcArray.size(); + while(no >0) + { + --no; + delete mcArray[no]; + } + delete iTree; + } + //========================================================= + + // just for visual debugging with an external debug class + #ifdef _DEBUG_VMAPS + #ifndef gBoxArray + extern Vector3 p1,p2,p3,p4,p5,p6,p7; + extern Array<AABox>gBoxArray; + extern int gCount1, gCount2, gCount3, gCount4; + extern bool myfound; + #endif + #endif + + //========================================================= + /** + return dist to hit or inf() if no hit + */ + + float MapTree::getIntersectionTime(const Ray& pRay, float pMaxDist, bool pStopAtFirstHit) + { + float firstDistance = inf(); + IntersectionCallBack<ModelContainer> intersectionCallBack; + float t = pMaxDist; + iTree->intersectRay(pRay, intersectionCallBack, t, pStopAtFirstHit, false); +#ifdef _DEBUG_VMAPS + { + if(t < pMaxDist) + { + myfound = true; + p4 = pRay.origin + pRay.direction*t; + } + } +#endif + if(t > 0 && t < inf() && pMaxDist > t) + { + firstDistance = t; + } + return firstDistance; + } + //========================================================= + + bool MapTree::isInLineOfSight(const Vector3& pos1, const Vector3& pos2) + { + bool result = true; + float maxDist = abs((pos2 - pos1).magnitude()); + // direction with length of 1 + Ray ray = Ray::fromOriginAndDirection(pos1, (pos2 - pos1)/maxDist); + float resultDist = getIntersectionTime(ray, maxDist, true); + if(resultDist < maxDist) + { + result = false; + } + return result; + } + //========================================================= + /** + When moving from pos1 to pos2 check if we hit an object. Return true and the position if we hit one + Return the hit pos or the original dest pos + */ + + bool MapTree::getObjectHitPos(const Vector3& pPos1, const Vector3& pPos2, Vector3& pResultHitPos, float pModifyDist) + { + bool result; + float maxDist = abs((pPos2 - pPos1).magnitude()); + Vector3 dir = (pPos2 - pPos1)/maxDist; // direction with length of 1 + Ray ray = Ray::fromOriginAndDirection(pPos1, dir); + float dist = getIntersectionTime(ray, maxDist, false); + if(dist < maxDist) + { + pResultHitPos = pPos1 + dir * dist; + if(pModifyDist < 0) + { + if(abs((pResultHitPos - pPos1).magnitude()) > -pModifyDist) + { + pResultHitPos = pResultHitPos + dir*pModifyDist; + } + else + { + pResultHitPos = pPos1; + } + } + else + { + pResultHitPos = pResultHitPos + dir*pModifyDist; + } + result = true; + } + else + { + pResultHitPos = pPos2; + result = false; + } + return result; + } + + //========================================================= + + float MapTree::getHeight(const Vector3& pPos) + { + float height = inf(); + Vector3 dir = Vector3(0,-1,0); + Ray ray = Ray::fromOriginAndDirection(pPos, dir); // direction with length of 1 + float maxDist = VMapDefinitions::getMaxCanFallDistance(); + float dist = getIntersectionTime(ray, maxDist, false); + if(dist < inf()) + { + height = (pPos + dir * dist).y; + } + return(height); + } + + //========================================================= + + bool MapTree::PrepareTree() + { + iTree->balance(); + return true; + } + + bool MapTree::loadMap(const std::string& pDirFileName, unsigned int pMapTileIdent) + { + bool result = true; + size_t len = iBasePath.length() + pDirFileName.length(); + char *filenameBuffer = new char[len+1]; + if(!hasDirFile(pDirFileName)) + { + FilesInDir filesInDir; + result = false; + sprintf(filenameBuffer, "%s%s", iBasePath.c_str(), pDirFileName.c_str()); + FILE* df = fopen(filenameBuffer, "rb"); + if(df) + { + char lineBuffer[FILENAMEBUFFER_SIZE]; + result = true; + bool newModelLoaded = false; + while(result && (fgets(lineBuffer, FILENAMEBUFFER_SIZE-1, df) != 0)) + { + std::string name = std::string(lineBuffer); + chomp(name); + if(name.length() >1) + { + filesInDir.append(name); + ManagedModelContainer *mc; + if(!isAlreadyLoaded(name)) + { + std::string fname = iBasePath; + fname.append(name); + mc = new ManagedModelContainer(); + result = mc->readFile(fname.c_str()); + if(result) + { + addModelContainer(name, mc); + newModelLoaded = true; + } + } + else + { + mc = getModelContainer(name); + } + mc->incRefCount(); + } + } + if(result && newModelLoaded) + { + iTree->balance(); + } + if(result && ferror(df) != 0) + { + result = false; + } + fclose(df); + if(result) + { + filesInDir.incRefCount(); + addDirFile(pDirFileName, filesInDir); + setLoadedMapTile(pMapTileIdent); + } + } + } + else + { + // Already loaded, so just inc. the ref count if mapTileIdent is new + if(!containsLoadedMapTile(pMapTileIdent)) + { + setLoadedMapTile(pMapTileIdent); + FilesInDir& filesInDir = getDirFiles(pDirFileName); + filesInDir.incRefCount(); + } + } + delete [] filenameBuffer; + return (result); + } + + //========================================================= + + void MapTree::unloadMap(const std::string& dirFileName, unsigned int pMapTileIdent, bool pForce) + { + if(hasDirFile(dirFileName) && (pForce || containsLoadedMapTile(pMapTileIdent))) + { + if(containsLoadedMapTile(pMapTileIdent)) + removeLoadedMapTile(pMapTileIdent); + FilesInDir& filesInDir = getDirFiles(dirFileName); + filesInDir.decRefCount(); + if(filesInDir.getRefCount() <= 0) + { + Array<std::string> fileNames = filesInDir.getFiles(); + bool treeChanged = false; + for(int i=0; i<fileNames.size(); ++i) + { + std::string name = fileNames[i]; + ManagedModelContainer* mc = getModelContainer(name); + mc->decRefCount(); + if(mc->getRefCount() <= 0) + { + iLoadedModelContainer.remove(name); + iTree->remove(mc); + delete mc; + treeChanged = true; + } + } + iLoadedDirFiles.remove(dirFileName); + if(treeChanged) + { + iTree->balance(); + } + } + } + } + + //========================================================= + //========================================================= + + void MapTree::addModelContainer(const std::string& pName, ManagedModelContainer *pMc) + { + iLoadedModelContainer.set(pName, pMc); + iTree->insert(pMc); + } + //========================================================= + //========================================================= + //========================================================= +} diff --git a/src/shared/vmap/VMapManager.h b/src/shared/vmap/VMapManager.h new file mode 100644 index 00000000000..aef9e0fa8f1 --- /dev/null +++ b/src/shared/vmap/VMapManager.h @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _VMAPMANAGER_H +#define _VMAPMANAGER_H + +// load our modified version first !! +#include "AABSPTree.h" +#include "ManagedModelContainer.h" +#include "IVMapManager.h" +#ifdef _VMAP_LOG_DEBUG +#include "DebugCmdLogger.h" +#endif +#include <G3D/Table.h> + +//=========================================================== + +#define DIR_FILENAME_EXTENSION ".vmdir" + +#define FILENAMEBUFFER_SIZE 500 + +/** +This is the main Class to manage loading and unloading of maps, line of sight, height calculation and so on. +For each map or map tile to load it reads a directory file that contains the ModelContainer files used by this map or map tile. +Each global map or instance has its own dynamic BSP-Tree. +The loaded ModelContainers are included in one of these BSP-Trees. +Additionally a table to match map ids and map names is used. +*/ + +// Create a value describing the map tile +#define MAP_TILE_IDENT(x,y) ((x<<8) + y) +//=========================================================== + +namespace VMAP +{ + //=========================================================== + + class FilesInDir + { + private: + int iRefCount; + G3D::Array<std::string> iFiles; + public: + + FilesInDir() { iRefCount = 0; } + void append(const std::string& pName) { iFiles.append(pName); } + void incRefCount() { ++iRefCount; } + void decRefCount() { if(iRefCount > 0) --iRefCount; } + int getRefCount() { return iRefCount; } + const G3D::Array<std::string>& getFiles() const { return iFiles; } + }; + + //=========================================================== + //=========================================================== + //=========================================================== + //=========================================================== + + class MapTree + { + private: + G3D::AABSPTree<ModelContainer *> *iTree; + + // Key: filename, value ModelContainer + G3D::Table<std::string, ManagedModelContainer *> iLoadedModelContainer; + + // Key: dir file name, value FilesInDir + G3D::Table<std::string, FilesInDir> iLoadedDirFiles; + + // Store all the map tile idents that are loaded for that map + // some maps are not splitted into tiles and we have to make sure, not removing the map before all tiles are removed + G3D::Table<unsigned int, bool> iLoadedMapTiles; + std::string iBasePath; + + private: + float getIntersectionTime(const G3D::Ray& pRay, float pMaxDist, bool pStopAtFirstHit); + bool isAlreadyLoaded(const std::string& pName) { return(iLoadedModelContainer.containsKey(pName)); } + void setLoadedMapTile(unsigned int pTileIdent) { iLoadedMapTiles.set(pTileIdent, true); } + void removeLoadedMapTile(unsigned int pTileIdent) { iLoadedMapTiles.remove(pTileIdent); } + bool hasLoadedMapTiles() { return(iLoadedMapTiles.size() > 0); } + bool containsLoadedMapTile(unsigned int pTileIdent) { return(iLoadedMapTiles.containsKey(pTileIdent)); } + public: + ManagedModelContainer *getModelContainer(const std::string& pName) { return(iLoadedModelContainer.get(pName)); } + const bool hasDirFile(const std::string& pDirName) const { return(iLoadedDirFiles.containsKey(pDirName)); } + FilesInDir& getDirFiles(const std::string& pDirName) const { return(iLoadedDirFiles.get(pDirName)); } + public: + MapTree(const char *pBasePath); + ~MapTree(); + + bool isInLineOfSight(const G3D::Vector3& pos1, const G3D::Vector3& pos2); + bool getObjectHitPos(const G3D::Vector3& pos1, const G3D::Vector3& pos2, G3D::Vector3& pResultHitPos, float pModifyDist); + float getHeight(const G3D::Vector3& pPos); + + bool PrepareTree(); + bool loadMap(const std::string& pDirFileName, unsigned int pMapTileIdent); + void addModelContainer(const std::string& pName, ManagedModelContainer *pMc); + void unloadMap(const std::string& dirFileName, unsigned int pMapTileIdent, bool pForce=false); + + void getModelContainer(G3D::Array<ModelContainer *>& pArray ) { iTree->getMembers(pArray); } + const void addDirFile(const std::string& pDirName, const FilesInDir& pFilesInDir) { iLoadedDirFiles.set(pDirName, pFilesInDir); } + size_t size() { return(iTree->size()); } + }; + + //=========================================================== + class MapIdNames + { + public: + std::string iDirName; + std::string iMapGroupName; + }; + + //=========================================================== + class VMapManager : public IVMapManager + { + private: + // Tree to check collision + G3D::Table<unsigned int , MapTree *> iInstanceMapTrees; + G3D::Table<unsigned int , bool> iMapsSplitIntoTiles; + G3D::Table<unsigned int , bool> iIgnoreMapIds; + +#ifdef _VMAP_LOG_DEBUG + CommandFileRW iCommandLogger; +#endif + private: + bool _loadMap(const char* pBasePath, unsigned int pMapId, int x, int y, bool pForceTileLoad=false); + void _unloadMap(unsigned int pMapId, int x, int y); + bool _existsMap(const std::string& pBasePath, unsigned int pMapId, int x, int y, bool pForceTileLoad); + + public: + // public for debug + G3D::Vector3 convertPositionToInternalRep(float x, float y, float z) const; + G3D::Vector3 convertPositionToMangosRep(float x, float y, float z) const; + std::string getDirFileName(unsigned int pMapId) const; + std::string getDirFileName(unsigned int pMapId, int x, int y) const; + MapTree* getInstanceMapTree(int pMapId) { return(iInstanceMapTrees.get(pMapId)); } + public: + VMapManager(); + ~VMapManager(void); + + int loadMap(const char* pBasePath, unsigned int pMapId, int x, int y); + + bool existsMap(const char* pBasePath, unsigned int pMapId, int x, int y); + + void unloadMap(unsigned int pMapId, int x, int y); + void unloadMap(unsigned int pMapId); + + bool isInLineOfSight(unsigned int pMapId, float x1, float y1, float z1, float x2, float y2, float z2) ; + /** + fill the hit pos and return true, if an object was hit + */ + bool getObjectHitPos(unsigned int pMapId, float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float &ry, float& rz, float pModifyDist); + float getHeight(unsigned int pMapId, float x, float y, float z); + + bool processCommand(char *pCommand); // for debug and extensions + + void preventMapsFromBeingUsed(const char* pMapIdString); + }; +} +#endif diff --git a/src/shared/vmap/VMapTools.h b/src/shared/vmap/VMapTools.h new file mode 100644 index 00000000000..b368b18ee86 --- /dev/null +++ b/src/shared/vmap/VMapTools.h @@ -0,0 +1,150 @@ +/* +* Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef _VMAPTOOLS_H +#define _VMAPTOOLS_H + +#include <G3D/CollisionDetection.h> +#include <G3D/AABox.h> + +#include "NodeValueAccess.h" + +/** +The Class is mainly taken from G3D/AABSPTree.h but modified to be able to use our internal data structure. +This is an iterator that helps us analysing the BSP-Trees. +The collision detection is modified to return true, if we are inside an object. +*/ + +namespace VMAP +{ + template<class TValue> + class IntersectionCallBack { + public: + TValue* closestEntity; + G3D::Vector3 hitLocation; + G3D::Vector3 hitNormal; + + void operator()(const G3D::Ray& ray, const TValue* entity, bool pStopAtFirstHit, float& distance) { + entity->intersect(ray, distance, pStopAtFirstHit, hitLocation, hitNormal); + } + }; + + //============================================================== + //============================================================== + //============================================================== + + class MyCollisionDetection + { + private: + public: + + static bool collisionLocationForMovingPointFixedAABox( + const G3D::Vector3& origin, + const G3D::Vector3& dir, + const G3D::AABox& box, + G3D::Vector3& location, + bool& Inside) + { + + // Integer representation of a floating-point value. +#define IR(x) ((G3D::uint32&)x) + + Inside = true; + const G3D::Vector3& MinB = box.low(); + const G3D::Vector3& MaxB = box.high(); + G3D::Vector3 MaxT(-1.0f, -1.0f, -1.0f); + + // Find candidate planes. + for (int i = 0; i < 3; ++i) + { + if (origin[i] < MinB[i]) + { + location[i] = MinB[i]; + Inside = false; + + // Calculate T distances to candidate planes + if (IR(dir[i])) + { + MaxT[i] = (MinB[i] - origin[i]) / dir[i]; + } + } + else if (origin[i] > MaxB[i]) + { + location[i] = MaxB[i]; + Inside = false; + + // Calculate T distances to candidate planes + if (IR(dir[i])) + { + MaxT[i] = (MaxB[i] - origin[i]) / dir[i]; + } + } + } + + if (Inside) + { + // definite hit + location = origin; + return true; + } + + // Get largest of the maxT's for final choice of intersection + int WhichPlane = 0; + if (MaxT[1] > MaxT[WhichPlane]) + { + WhichPlane = 1; + } + + if (MaxT[2] > MaxT[WhichPlane]) + { + WhichPlane = 2; + } + + // Check final candidate actually inside box + if (IR(MaxT[WhichPlane]) & 0x80000000) + { + // Miss the box + return false; + } + + for (int i = 0; i < 3; ++i) + { + if (i != WhichPlane) + { + location[i] = origin[i] + MaxT[WhichPlane] * dir[i]; + if ((location[i] < MinB[i]) || + (location[i] > MaxB[i])) + { + // On this plane we're outside the box extents, so + // we miss the box + return false; + } + } + } + /* + // Choose the normal to be the plane normal facing into the ray + normal = G3D::Vector3::zero(); + normal[WhichPlane] = (dir[WhichPlane] > 0) ? -1.0 : 1.0; + */ + return true; + +#undef IR + } + }; +} +#endif |