aboutsummaryrefslogtreecommitdiff
path: root/src/shared
diff options
context:
space:
mode:
authorNeo2003 <none@none>2008-10-02 16:23:55 -0500
committerNeo2003 <none@none>2008-10-02 16:23:55 -0500
commit9b1c0e006f20091f28f3f468cfcab1feb51286bd (patch)
treeb5d1ba94a656e6679f8737f9ea6bed1239b73b14 /src/shared
[svn] * Proper SVN structureinit
--HG-- branch : trunk
Diffstat (limited to 'src/shared')
-rw-r--r--src/shared/Auth/AuthCrypt.cpp81
-rw-r--r--src/shared/Auth/AuthCrypt.h52
-rw-r--r--src/shared/Auth/BigNumber.cpp207
-rw-r--r--src/shared/Auth/BigNumber.h94
-rw-r--r--src/shared/Auth/Hmac.cpp56
-rw-r--r--src/shared/Auth/Hmac.h46
-rw-r--r--src/shared/Auth/Makefile.am39
-rw-r--r--src/shared/Auth/Sha1.cpp65
-rw-r--r--src/shared/Auth/Sha1.h51
-rw-r--r--src/shared/Auth/md5.c385
-rw-r--r--src/shared/Auth/md5.h91
-rw-r--r--src/shared/Base.cpp67
-rw-r--r--src/shared/Base.h54
-rw-r--r--src/shared/ByteBuffer.h479
-rw-r--r--src/shared/Common.cpp40
-rw-r--r--src/shared/Common.h171
-rw-r--r--src/shared/Config/Config.cpp174
-rw-r--r--src/shared/Config/Config.h58
-rw-r--r--src/shared/Config/ConfigEnv.h28
-rw-r--r--src/shared/Config/ConfigLibrary.vcproj137
-rw-r--r--src/shared/Config/Makefile.am40
-rw-r--r--src/shared/Config/dotconfpp/dotconfpp.cpp583
-rw-r--r--src/shared/Config/dotconfpp/dotconfpp.h110
-rw-r--r--src/shared/Config/dotconfpp/mempool.cpp100
-rw-r--r--src/shared/Config/dotconfpp/mempool.h46
-rw-r--r--src/shared/Database/DBCStores.cpp642
-rw-r--r--src/shared/Database/DBCStores.h202
-rw-r--r--src/shared/Database/DBCStructure.h937
-rw-r--r--src/shared/Database/DBCfmt.cpp78
-rw-r--r--src/shared/Database/Database.cpp171
-rw-r--r--src/shared/Database/Database.h113
-rw-r--r--src/shared/Database/DatabaseEnv.h52
-rw-r--r--src/shared/Database/DatabaseImpl.h146
-rw-r--r--src/shared/Database/DatabaseMysql.cpp408
-rw-r--r--src/shared/Database/DatabaseMysql.h77
-rw-r--r--src/shared/Database/DatabasePostgre.cpp345
-rw-r--r--src/shared/Database/DatabasePostgre.h75
-rw-r--r--src/shared/Database/DatabaseSqlite.cpp101
-rw-r--r--src/shared/Database/DatabaseSqlite.h43
-rw-r--r--src/shared/Database/Field.cpp65
-rw-r--r--src/shared/Database/Field.h75
-rw-r--r--src/shared/Database/Makefile.am62
-rw-r--r--src/shared/Database/MySQLDelayThread.h30
-rw-r--r--src/shared/Database/PGSQLDelayThread.h30
-rw-r--r--src/shared/Database/QueryResult.h64
-rw-r--r--src/shared/Database/QueryResultMysql.cpp110
-rw-r--r--src/shared/Database/QueryResultMysql.h48
-rw-r--r--src/shared/Database/QueryResultPostgre.cpp139
-rw-r--r--src/shared/Database/QueryResultPostgre.h48
-rw-r--r--src/shared/Database/QueryResultSqlite.cpp96
-rw-r--r--src/shared/Database/QueryResultSqlite.h43
-rw-r--r--src/shared/Database/SQLStorage.cpp191
-rw-r--r--src/shared/Database/SQLStorage.h68
-rw-r--r--src/shared/Database/SqlDelayThread.cpp55
-rw-r--r--src/shared/Database/SqlDelayThread.h48
-rw-r--r--src/shared/Database/SqlOperations.cpp197
-rw-r--r--src/shared/Database/SqlOperations.h121
-rw-r--r--src/shared/Database/dbcfile.cpp243
-rw-r--r--src/shared/Database/dbcfile.h107
-rw-r--r--src/shared/Errors.h29
-rw-r--r--src/shared/Log.cpp762
-rw-r--r--src/shared/Log.h154
-rw-r--r--src/shared/Makefile.am117
-rw-r--r--src/shared/Mthread.cpp205
-rw-r--r--src/shared/Mthread.h62
-rw-r--r--src/shared/PacketLog.cpp157
-rw-r--r--src/shared/PacketLog.h46
-rw-r--r--src/shared/ProgressBar.cpp80
-rw-r--r--src/shared/ProgressBar.h40
-rw-r--r--src/shared/ServiceWin32.cpp260
-rw-r--r--src/shared/ServiceWin32.h28
-rw-r--r--src/shared/SystemConfig.h33
-rw-r--r--src/shared/Timer.h96
-rw-r--r--src/shared/Util.cpp425
-rw-r--r--src/shared/Util.h306
-rw-r--r--src/shared/WheatyExceptionReport.cpp964
-rw-r--r--src/shared/WheatyExceptionReport.h117
-rw-r--r--src/shared/WorldPacket.h51
-rw-r--r--src/shared/vmap/AABSPTree.h1620
-rw-r--r--src/shared/vmap/BaseModel.cpp95
-rw-r--r--src/shared/vmap/BaseModel.h99
-rw-r--r--src/shared/vmap/CoordModelMapping.cpp187
-rw-r--r--src/shared/vmap/CoordModelMapping.h144
-rw-r--r--src/shared/vmap/DebugCmdLogger.cpp125
-rw-r--r--src/shared/vmap/DebugCmdLogger.h116
-rw-r--r--src/shared/vmap/IVMapManager.h99
-rw-r--r--src/shared/vmap/Makefile.am54
-rw-r--r--src/shared/vmap/ManagedModelContainer.cpp35
-rw-r--r--src/shared/vmap/ManagedModelContainer.h49
-rw-r--r--src/shared/vmap/ModelContainer.cpp375
-rw-r--r--src/shared/vmap/ModelContainer.h108
-rw-r--r--src/shared/vmap/NodeValueAccess.h48
-rw-r--r--src/shared/vmap/ShortBox.h148
-rw-r--r--src/shared/vmap/ShortVector.h134
-rw-r--r--src/shared/vmap/SubModel.cpp248
-rw-r--r--src/shared/vmap/SubModel.h102
-rw-r--r--src/shared/vmap/TileAssembler.cpp571
-rw-r--r--src/shared/vmap/TileAssembler.h93
-rw-r--r--src/shared/vmap/TreeNode.cpp37
-rw-r--r--src/shared/vmap/TreeNode.h223
-rw-r--r--src/shared/vmap/VMapDefinitions.h37
-rw-r--r--src/shared/vmap/VMapFactory.cpp104
-rw-r--r--src/shared/vmap/VMapFactory.h43
-rw-r--r--src/shared/vmap/VMapManager.cpp780
-rw-r--r--src/shared/vmap/VMapManager.h173
-rw-r--r--src/shared/vmap/VMapTools.h150
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